diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2021-02-22 20:28:17 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2021-02-22 20:28:17 +0000 |
commit | 27ed1ebf8997cea55fb446ce249b390607b84105 (patch) | |
tree | a49a763fee848041fd607f449ad13a0b1040636e /crates/ide_assists/src/handlers/generate_new.rs | |
parent | 8687053b118f47ce1a4962d0baa19b22d40d2758 (diff) | |
parent | eb6cfa7f157690480fca5d55c69dba3fae87ad4f (diff) |
Merge #7759
7759: 7526: Rename ide related crates r=Veykril a=chetankhilosiya
renamed assists -> ide_assists and ssr -> ide_ssr.
the completion crate is already renamed.
Co-authored-by: Chetan Khilosiya <[email protected]>
Diffstat (limited to 'crates/ide_assists/src/handlers/generate_new.rs')
-rw-r--r-- | crates/ide_assists/src/handlers/generate_new.rs | 315 |
1 files changed, 315 insertions, 0 deletions
diff --git a/crates/ide_assists/src/handlers/generate_new.rs b/crates/ide_assists/src/handlers/generate_new.rs new file mode 100644 index 000000000..8ce5930b7 --- /dev/null +++ b/crates/ide_assists/src/handlers/generate_new.rs | |||
@@ -0,0 +1,315 @@ | |||
1 | use ast::Adt; | ||
2 | use itertools::Itertools; | ||
3 | use stdx::format_to; | ||
4 | use syntax::ast::{self, AstNode, NameOwner, StructKind, VisibilityOwner}; | ||
5 | |||
6 | use crate::{ | ||
7 | utils::{find_impl_block_start, find_struct_impl, generate_impl_text}, | ||
8 | AssistContext, AssistId, AssistKind, Assists, | ||
9 | }; | ||
10 | |||
11 | // Assist: generate_new | ||
12 | // | ||
13 | // Adds a new inherent impl for a type. | ||
14 | // | ||
15 | // ``` | ||
16 | // struct Ctx<T: Clone> { | ||
17 | // data: T,$0 | ||
18 | // } | ||
19 | // ``` | ||
20 | // -> | ||
21 | // ``` | ||
22 | // struct Ctx<T: Clone> { | ||
23 | // data: T, | ||
24 | // } | ||
25 | // | ||
26 | // impl<T: Clone> Ctx<T> { | ||
27 | // fn $0new(data: T) -> Self { Self { data } } | ||
28 | // } | ||
29 | // ``` | ||
30 | pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
31 | let strukt = ctx.find_node_at_offset::<ast::Struct>()?; | ||
32 | |||
33 | // We want to only apply this to non-union structs with named fields | ||
34 | let field_list = match strukt.kind() { | ||
35 | StructKind::Record(named) => named, | ||
36 | _ => return None, | ||
37 | }; | ||
38 | |||
39 | // Return early if we've found an existing new fn | ||
40 | let impl_def = find_struct_impl(&ctx, &Adt::Struct(strukt.clone()), "new")?; | ||
41 | |||
42 | let target = strukt.syntax().text_range(); | ||
43 | acc.add(AssistId("generate_new", AssistKind::Generate), "Generate `new`", target, |builder| { | ||
44 | let mut buf = String::with_capacity(512); | ||
45 | |||
46 | if impl_def.is_some() { | ||
47 | buf.push('\n'); | ||
48 | } | ||
49 | |||
50 | let vis = strukt.visibility().map_or(String::new(), |v| format!("{} ", v)); | ||
51 | |||
52 | let params = field_list | ||
53 | .fields() | ||
54 | .filter_map(|f| Some(format!("{}: {}", f.name()?.syntax(), f.ty()?.syntax()))) | ||
55 | .format(", "); | ||
56 | let fields = field_list.fields().filter_map(|f| f.name()).format(", "); | ||
57 | |||
58 | format_to!(buf, " {}fn new({}) -> Self {{ Self {{ {} }} }}", vis, params, fields); | ||
59 | |||
60 | let start_offset = impl_def | ||
61 | .and_then(|impl_def| find_impl_block_start(impl_def, &mut buf)) | ||
62 | .unwrap_or_else(|| { | ||
63 | buf = generate_impl_text(&Adt::Struct(strukt.clone()), &buf); | ||
64 | strukt.syntax().text_range().end() | ||
65 | }); | ||
66 | |||
67 | match ctx.config.snippet_cap { | ||
68 | None => builder.insert(start_offset, buf), | ||
69 | Some(cap) => { | ||
70 | buf = buf.replace("fn new", "fn $0new"); | ||
71 | builder.insert_snippet(cap, start_offset, buf); | ||
72 | } | ||
73 | } | ||
74 | }) | ||
75 | } | ||
76 | |||
77 | #[cfg(test)] | ||
78 | mod tests { | ||
79 | use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; | ||
80 | |||
81 | use super::*; | ||
82 | |||
83 | #[test] | ||
84 | #[rustfmt::skip] | ||
85 | fn test_generate_new() { | ||
86 | // Check output of generation | ||
87 | check_assist( | ||
88 | generate_new, | ||
89 | "struct Foo {$0}", | ||
90 | "struct Foo {} | ||
91 | |||
92 | impl Foo { | ||
93 | fn $0new() -> Self { Self { } } | ||
94 | }", | ||
95 | ); | ||
96 | check_assist( | ||
97 | generate_new, | ||
98 | "struct Foo<T: Clone> {$0}", | ||
99 | "struct Foo<T: Clone> {} | ||
100 | |||
101 | impl<T: Clone> Foo<T> { | ||
102 | fn $0new() -> Self { Self { } } | ||
103 | }", | ||
104 | ); | ||
105 | check_assist( | ||
106 | generate_new, | ||
107 | "struct Foo<'a, T: Foo<'a>> {$0}", | ||
108 | "struct Foo<'a, T: Foo<'a>> {} | ||
109 | |||
110 | impl<'a, T: Foo<'a>> Foo<'a, T> { | ||
111 | fn $0new() -> Self { Self { } } | ||
112 | }", | ||
113 | ); | ||
114 | check_assist( | ||
115 | generate_new, | ||
116 | "struct Foo { baz: String $0}", | ||
117 | "struct Foo { baz: String } | ||
118 | |||
119 | impl Foo { | ||
120 | fn $0new(baz: String) -> Self { Self { baz } } | ||
121 | }", | ||
122 | ); | ||
123 | check_assist( | ||
124 | generate_new, | ||
125 | "struct Foo { baz: String, qux: Vec<i32> $0}", | ||
126 | "struct Foo { baz: String, qux: Vec<i32> } | ||
127 | |||
128 | impl Foo { | ||
129 | fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } } | ||
130 | }", | ||
131 | ); | ||
132 | |||
133 | // Check that visibility modifiers don't get brought in for fields | ||
134 | check_assist( | ||
135 | generate_new, | ||
136 | "struct Foo { pub baz: String, pub qux: Vec<i32> $0}", | ||
137 | "struct Foo { pub baz: String, pub qux: Vec<i32> } | ||
138 | |||
139 | impl Foo { | ||
140 | fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } } | ||
141 | }", | ||
142 | ); | ||
143 | |||
144 | // Check that it reuses existing impls | ||
145 | check_assist( | ||
146 | generate_new, | ||
147 | "struct Foo {$0} | ||
148 | |||
149 | impl Foo {} | ||
150 | ", | ||
151 | "struct Foo {} | ||
152 | |||
153 | impl Foo { | ||
154 | fn $0new() -> Self { Self { } } | ||
155 | } | ||
156 | ", | ||
157 | ); | ||
158 | check_assist( | ||
159 | generate_new, | ||
160 | "struct Foo {$0} | ||
161 | |||
162 | impl Foo { | ||
163 | fn qux(&self) {} | ||
164 | } | ||
165 | ", | ||
166 | "struct Foo {} | ||
167 | |||
168 | impl Foo { | ||
169 | fn $0new() -> Self { Self { } } | ||
170 | |||
171 | fn qux(&self) {} | ||
172 | } | ||
173 | ", | ||
174 | ); | ||
175 | |||
176 | check_assist( | ||
177 | generate_new, | ||
178 | "struct Foo {$0} | ||
179 | |||
180 | impl Foo { | ||
181 | fn qux(&self) {} | ||
182 | fn baz() -> i32 { | ||
183 | 5 | ||
184 | } | ||
185 | } | ||
186 | ", | ||
187 | "struct Foo {} | ||
188 | |||
189 | impl Foo { | ||
190 | fn $0new() -> Self { Self { } } | ||
191 | |||
192 | fn qux(&self) {} | ||
193 | fn baz() -> i32 { | ||
194 | 5 | ||
195 | } | ||
196 | } | ||
197 | ", | ||
198 | ); | ||
199 | |||
200 | // Check visibility of new fn based on struct | ||
201 | check_assist( | ||
202 | generate_new, | ||
203 | "pub struct Foo {$0}", | ||
204 | "pub struct Foo {} | ||
205 | |||
206 | impl Foo { | ||
207 | pub fn $0new() -> Self { Self { } } | ||
208 | }", | ||
209 | ); | ||
210 | check_assist( | ||
211 | generate_new, | ||
212 | "pub(crate) struct Foo {$0}", | ||
213 | "pub(crate) struct Foo {} | ||
214 | |||
215 | impl Foo { | ||
216 | pub(crate) fn $0new() -> Self { Self { } } | ||
217 | }", | ||
218 | ); | ||
219 | } | ||
220 | |||
221 | #[test] | ||
222 | fn generate_new_not_applicable_if_fn_exists() { | ||
223 | check_assist_not_applicable( | ||
224 | generate_new, | ||
225 | " | ||
226 | struct Foo {$0} | ||
227 | |||
228 | impl Foo { | ||
229 | fn new() -> Self { | ||
230 | Self | ||
231 | } | ||
232 | }", | ||
233 | ); | ||
234 | |||
235 | check_assist_not_applicable( | ||
236 | generate_new, | ||
237 | " | ||
238 | struct Foo {$0} | ||
239 | |||
240 | impl Foo { | ||
241 | fn New() -> Self { | ||
242 | Self | ||
243 | } | ||
244 | }", | ||
245 | ); | ||
246 | } | ||
247 | |||
248 | #[test] | ||
249 | fn generate_new_target() { | ||
250 | check_assist_target( | ||
251 | generate_new, | ||
252 | " | ||
253 | struct SomeThingIrrelevant; | ||
254 | /// Has a lifetime parameter | ||
255 | struct Foo<'a, T: Foo<'a>> {$0} | ||
256 | struct EvenMoreIrrelevant; | ||
257 | ", | ||
258 | "/// Has a lifetime parameter | ||
259 | struct Foo<'a, T: Foo<'a>> {}", | ||
260 | ); | ||
261 | } | ||
262 | |||
263 | #[test] | ||
264 | fn test_unrelated_new() { | ||
265 | check_assist( | ||
266 | generate_new, | ||
267 | r##" | ||
268 | pub struct AstId<N: AstNode> { | ||
269 | file_id: HirFileId, | ||
270 | file_ast_id: FileAstId<N>, | ||
271 | } | ||
272 | |||
273 | impl<N: AstNode> AstId<N> { | ||
274 | pub fn new(file_id: HirFileId, file_ast_id: FileAstId<N>) -> AstId<N> { | ||
275 | AstId { file_id, file_ast_id } | ||
276 | } | ||
277 | } | ||
278 | |||
279 | pub struct Source<T> { | ||
280 | pub file_id: HirFileId,$0 | ||
281 | pub ast: T, | ||
282 | } | ||
283 | |||
284 | impl<T> Source<T> { | ||
285 | pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> { | ||
286 | Source { file_id: self.file_id, ast: f(self.ast) } | ||
287 | } | ||
288 | }"##, | ||
289 | r##" | ||
290 | pub struct AstId<N: AstNode> { | ||
291 | file_id: HirFileId, | ||
292 | file_ast_id: FileAstId<N>, | ||
293 | } | ||
294 | |||
295 | impl<N: AstNode> AstId<N> { | ||
296 | pub fn new(file_id: HirFileId, file_ast_id: FileAstId<N>) -> AstId<N> { | ||
297 | AstId { file_id, file_ast_id } | ||
298 | } | ||
299 | } | ||
300 | |||
301 | pub struct Source<T> { | ||
302 | pub file_id: HirFileId, | ||
303 | pub ast: T, | ||
304 | } | ||
305 | |||
306 | impl<T> Source<T> { | ||
307 | pub fn $0new(file_id: HirFileId, ast: T) -> Self { Self { file_id, ast } } | ||
308 | |||
309 | pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> { | ||
310 | Source { file_id: self.file_id, ast: f(self.ast) } | ||
311 | } | ||
312 | }"##, | ||
313 | ); | ||
314 | } | ||
315 | } | ||