diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2021-02-10 10:32:36 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2021-02-10 10:32:36 +0000 |
commit | 5e39d7a68032ba40d3ed76d41d0f01ad628f5c95 (patch) | |
tree | 2cee8cecf4214092f42dbee3432afa944e219671 /crates/assists/src/handlers/generate_new.rs | |
parent | ff5ef2830c4cc6bf4116b99b440885bf0c94b459 (diff) | |
parent | e8d7bcc35507425f384cff25feb564ac41a5c5a7 (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.rs | 69 |
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 @@ | |||
1 | use ast::Adt; | ||
1 | use itertools::Itertools; | 2 | use itertools::Itertools; |
2 | use stdx::format_to; | 3 | use stdx::format_to; |
3 | use syntax::{ | 4 | use syntax::ast::{self, AstNode, NameOwner, StructKind, VisibilityOwner}; |
4 | ast::{self, AstNode, GenericParamsOwner, NameOwner, StructKind, VisibilityOwner}, | ||
5 | SmolStr, | ||
6 | }; | ||
7 | 5 | ||
8 | use crate::{ | 6 | use 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 | // ``` |
33 | pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 30 | pub(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 | ||
82 | fn 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)] |
107 | mod tests { | 78 | mod 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 | ||
121 | impl Foo { | 92 | impl 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 | ||
131 | impl<T: Clone> Foo<T> { | 101 | impl<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 | ||
141 | impl<'a, T: Foo<'a>> Foo<'a, T> { | 110 | impl<'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 | ||
151 | impl Foo { | 119 | impl 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 | ||
161 | impl Foo { | 128 | impl 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 | ||
173 | impl Foo { | 139 | impl 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 | ||
241 | impl Foo { | 206 | impl 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 | ||
251 | impl Foo { | 215 | impl 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##" |
328 | pub struct AstId<N: AstNode> { | 290 | pub 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 | } |