aboutsummaryrefslogtreecommitdiff
path: root/crates/assists/src/handlers/generate_new.rs
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2021-02-10 10:32:36 +0000
committerGitHub <[email protected]>2021-02-10 10:32:36 +0000
commit5e39d7a68032ba40d3ed76d41d0f01ad628f5c95 (patch)
tree2cee8cecf4214092f42dbee3432afa944e219671 /crates/assists/src/handlers/generate_new.rs
parentff5ef2830c4cc6bf4116b99b440885bf0c94b459 (diff)
parente8d7bcc35507425f384cff25feb564ac41a5c5a7 (diff)
Merge #7617
7617: Add getter/setter assists r=Veykril a=yoshuawuyts This patch makes progress towards the design outlined in https://github.com/rust-analyzer/rust-analyzer/issues/5943, and includes a small refactor which closes https://github.com/rust-analyzer/rust-analyzer/issues/7607. All together this patch does 4 things: - Adds a `generate_getter` assist. - Adds a `generate_getter_mut` assist. - Adds a `generate_setter` assist. - Moves the `generate_impl_text` function from `generate_new` into `utils` (which closes #7607). ## Design Notes I've chosen to follow the [Rust API guidelines on getters](https://rust-lang.github.io/api-guidelines/naming.html#getter-names-follow-rust-convention-c-getter) as closely as possible. This deliberately leaves "builder pattern"-style setters out of scope. Also, similar to https://github.com/rust-analyzer/rust-analyzer/pull/7570 this assist generates doc comments. I think this should work well in most cases, and for the few where it doesn't it's probably easily edited. This makes it slightly less correct than the #7570 implementation, but I think this is still useful enough to include for many of the same reasons. The reason why this PR contains 3 assists, rather than 1, is because each of them is so similar to the others that it felt more noisy to do them separately than all at once. The amount of code added does not necessarily reflect that, but hope that still makes sense. ## Examples **Input** ```rust struct Person { name: String, // <- cursor on "name" } ``` **generate getter** ```rust struct Person { name: String, } impl Person { /// Get a reference to the person's name. fn name(&self) -> &String { &self.name } } ``` **generate mut getter** ```rust struct Person { name: String, } impl Person { /// Get a mutable reference to the person's name. fn name_mut(&mut self) -> &mut String { &mut self.name } } ``` **generate setter** ```rust struct Person { name: String, } impl Person { /// Set the person's name. fn set_name(&mut self, name: String) { self.name = name; } } ``` Co-authored-by: Yoshua Wuyts <[email protected]>
Diffstat (limited to 'crates/assists/src/handlers/generate_new.rs')
-rw-r--r--crates/assists/src/handlers/generate_new.rs69
1 files changed, 15 insertions, 54 deletions
diff --git a/crates/assists/src/handlers/generate_new.rs b/crates/assists/src/handlers/generate_new.rs
index a9203d33f..c29077225 100644
--- a/crates/assists/src/handlers/generate_new.rs
+++ b/crates/assists/src/handlers/generate_new.rs
@@ -1,12 +1,10 @@
1use ast::Adt;
1use itertools::Itertools; 2use itertools::Itertools;
2use stdx::format_to; 3use stdx::format_to;
3use syntax::{ 4use syntax::ast::{self, AstNode, NameOwner, StructKind, VisibilityOwner};
4 ast::{self, AstNode, GenericParamsOwner, NameOwner, StructKind, VisibilityOwner},
5 SmolStr,
6};
7 5
8use crate::{ 6use crate::{
9 utils::{find_impl_block, find_struct_impl}, 7 utils::{find_impl_block, find_struct_impl, generate_impl_text},
10 AssistContext, AssistId, AssistKind, Assists, 8 AssistContext, AssistId, AssistKind, Assists,
11}; 9};
12 10
@@ -28,7 +26,6 @@ use crate::{
28// impl<T: Clone> Ctx<T> { 26// impl<T: Clone> Ctx<T> {
29// fn $0new(data: T) -> Self { Self { data } } 27// fn $0new(data: T) -> Self { Self { data } }
30// } 28// }
31//
32// ``` 29// ```
33pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 30pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
34 let strukt = ctx.find_node_at_offset::<ast::Struct>()?; 31 let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
@@ -40,7 +37,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
40 }; 37 };
41 38
42 // Return early if we've found an existing new fn 39 // Return early if we've found an existing new fn
43 let impl_def = find_struct_impl(&ctx, &ast::Adt::Struct(strukt.clone()), "new")?; 40 let impl_def = find_struct_impl(&ctx, &Adt::Struct(strukt.clone()), "new")?;
44 41
45 let target = strukt.syntax().text_range(); 42 let target = strukt.syntax().text_range();
46 acc.add(AssistId("generate_new", AssistKind::Generate), "Generate `new`", target, |builder| { 43 acc.add(AssistId("generate_new", AssistKind::Generate), "Generate `new`", target, |builder| {
@@ -63,7 +60,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
63 let start_offset = impl_def 60 let start_offset = impl_def
64 .and_then(|impl_def| find_impl_block(impl_def, &mut buf)) 61 .and_then(|impl_def| find_impl_block(impl_def, &mut buf))
65 .unwrap_or_else(|| { 62 .unwrap_or_else(|| {
66 buf = generate_impl_text(&strukt, &buf); 63 buf = generate_impl_text(&Adt::Struct(strukt.clone()), &buf);
67 strukt.syntax().text_range().end() 64 strukt.syntax().text_range().end()
68 }); 65 });
69 66
@@ -77,32 +74,6 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
77 }) 74 })
78} 75}
79 76
80// Generates the surrounding `impl Type { <code> }` including type and lifetime
81// parameters
82fn generate_impl_text(strukt: &ast::Struct, code: &str) -> String {
83 let type_params = strukt.generic_param_list();
84 let mut buf = String::with_capacity(code.len());
85 buf.push_str("\n\nimpl");
86 if let Some(type_params) = &type_params {
87 format_to!(buf, "{}", type_params.syntax());
88 }
89 buf.push(' ');
90 buf.push_str(strukt.name().unwrap().text());
91 if let Some(type_params) = type_params {
92 let lifetime_params = type_params
93 .lifetime_params()
94 .filter_map(|it| it.lifetime())
95 .map(|it| SmolStr::from(it.text()));
96 let type_params =
97 type_params.type_params().filter_map(|it| it.name()).map(|it| SmolStr::from(it.text()));
98 format_to!(buf, "<{}>", lifetime_params.chain(type_params).format(", "))
99 }
100
101 format_to!(buf, " {{\n{}\n}}\n", code);
102
103 buf
104}
105
106#[cfg(test)] 77#[cfg(test)]
107mod tests { 78mod tests {
108 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; 79 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
@@ -120,8 +91,7 @@ mod tests {
120 91
121impl Foo { 92impl Foo {
122 fn $0new() -> Self { Self { } } 93 fn $0new() -> Self { Self { } }
123} 94}",
124",
125 ); 95 );
126 check_assist( 96 check_assist(
127 generate_new, 97 generate_new,
@@ -130,8 +100,7 @@ impl Foo {
130 100
131impl<T: Clone> Foo<T> { 101impl<T: Clone> Foo<T> {
132 fn $0new() -> Self { Self { } } 102 fn $0new() -> Self { Self { } }
133} 103}",
134",
135 ); 104 );
136 check_assist( 105 check_assist(
137 generate_new, 106 generate_new,
@@ -140,8 +109,7 @@ impl<T: Clone> Foo<T> {
140 109
141impl<'a, T: Foo<'a>> Foo<'a, T> { 110impl<'a, T: Foo<'a>> Foo<'a, T> {
142 fn $0new() -> Self { Self { } } 111 fn $0new() -> Self { Self { } }
143} 112}",
144",
145 ); 113 );
146 check_assist( 114 check_assist(
147 generate_new, 115 generate_new,
@@ -150,8 +118,7 @@ impl<'a, T: Foo<'a>> Foo<'a, T> {
150 118
151impl Foo { 119impl Foo {
152 fn $0new(baz: String) -> Self { Self { baz } } 120 fn $0new(baz: String) -> Self { Self { baz } }
153} 121}",
154",
155 ); 122 );
156 check_assist( 123 check_assist(
157 generate_new, 124 generate_new,
@@ -160,8 +127,7 @@ impl Foo {
160 127
161impl Foo { 128impl Foo {
162 fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } } 129 fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }
163} 130}",
164",
165 ); 131 );
166 132
167 // Check that visibility modifiers don't get brought in for fields 133 // Check that visibility modifiers don't get brought in for fields
@@ -172,8 +138,7 @@ impl Foo {
172 138
173impl Foo { 139impl Foo {
174 fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } } 140 fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }
175} 141}",
176",
177 ); 142 );
178 143
179 // Check that it reuses existing impls 144 // Check that it reuses existing impls
@@ -240,8 +205,7 @@ impl Foo {
240 205
241impl Foo { 206impl Foo {
242 pub fn $0new() -> Self { Self { } } 207 pub fn $0new() -> Self { Self { } }
243} 208}",
244",
245 ); 209 );
246 check_assist( 210 check_assist(
247 generate_new, 211 generate_new,
@@ -250,8 +214,7 @@ impl Foo {
250 214
251impl Foo { 215impl Foo {
252 pub(crate) fn $0new() -> Self { Self { } } 216 pub(crate) fn $0new() -> Self { Self { } }
253} 217}",
254",
255 ); 218 );
256 } 219 }
257 220
@@ -322,8 +285,7 @@ impl<T> Source<T> {
322 pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> { 285 pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
323 Source { file_id: self.file_id, ast: f(self.ast) } 286 Source { file_id: self.file_id, ast: f(self.ast) }
324 } 287 }
325} 288}"##,
326"##,
327 r##" 289 r##"
328pub struct AstId<N: AstNode> { 290pub struct AstId<N: AstNode> {
329 file_id: HirFileId, 291 file_id: HirFileId,
@@ -347,8 +309,7 @@ impl<T> Source<T> {
347 pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> { 309 pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
348 Source { file_id: self.file_id, ast: f(self.ast) } 310 Source { file_id: self.file_id, ast: f(self.ast) }
349 } 311 }
350} 312}"##,
351"##,
352 ); 313 );
353 } 314 }
354} 315}