diff options
author | Aleksey Kladov <[email protected]> | 2021-02-17 14:53:31 +0000 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2021-02-17 14:53:31 +0000 |
commit | 3db64a400c78bbd2708e67ddc07df1001fff3f29 (patch) | |
tree | 5386aab9c452981be09bc3e4362643a34e6e3617 /crates/ide_completion/src/completions/record.rs | |
parent | 6334ce866ab095215381c4b72692b20a84d26e96 (diff) |
rename completion -> ide_completion
We don't have completion-related PRs in flight, so lets do it
Diffstat (limited to 'crates/ide_completion/src/completions/record.rs')
-rw-r--r-- | crates/ide_completion/src/completions/record.rs | 390 |
1 files changed, 390 insertions, 0 deletions
diff --git a/crates/ide_completion/src/completions/record.rs b/crates/ide_completion/src/completions/record.rs new file mode 100644 index 000000000..0a7927eb8 --- /dev/null +++ b/crates/ide_completion/src/completions/record.rs | |||
@@ -0,0 +1,390 @@ | |||
1 | //! Complete fields in record literals and patterns. | ||
2 | use ide_db::{helpers::FamousDefs, SymbolKind}; | ||
3 | use syntax::ast::Expr; | ||
4 | |||
5 | use crate::{item::CompletionKind, CompletionContext, CompletionItem, Completions}; | ||
6 | |||
7 | pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { | ||
8 | let missing_fields = match (ctx.record_pat_syntax.as_ref(), ctx.record_lit_syntax.as_ref()) { | ||
9 | (None, None) => return None, | ||
10 | (Some(_), Some(_)) => unreachable!("A record cannot be both a literal and a pattern"), | ||
11 | (Some(record_pat), _) => ctx.sema.record_pattern_missing_fields(record_pat), | ||
12 | (_, Some(record_lit)) => { | ||
13 | let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_lit.clone())); | ||
14 | let default_trait = FamousDefs(&ctx.sema, ctx.krate).core_default_Default(); | ||
15 | let impl_default_trait = default_trait | ||
16 | .and_then(|default_trait| ty.map(|ty| ty.impls_trait(ctx.db, default_trait, &[]))) | ||
17 | .unwrap_or(false); | ||
18 | |||
19 | let missing_fields = ctx.sema.record_literal_missing_fields(record_lit); | ||
20 | if impl_default_trait && !missing_fields.is_empty() { | ||
21 | let completion_text = "..Default::default()"; | ||
22 | let completion_text = completion_text | ||
23 | .strip_prefix(ctx.token.to_string().as_str()) | ||
24 | .unwrap_or(completion_text); | ||
25 | acc.add( | ||
26 | CompletionItem::new( | ||
27 | CompletionKind::Snippet, | ||
28 | ctx.source_range(), | ||
29 | "..Default::default()", | ||
30 | ) | ||
31 | .insert_text(completion_text) | ||
32 | .kind(SymbolKind::Field) | ||
33 | .build(), | ||
34 | ); | ||
35 | } | ||
36 | |||
37 | missing_fields | ||
38 | } | ||
39 | }; | ||
40 | |||
41 | for (field, ty) in missing_fields { | ||
42 | acc.add_field(ctx, field, &ty); | ||
43 | } | ||
44 | |||
45 | Some(()) | ||
46 | } | ||
47 | |||
48 | #[cfg(test)] | ||
49 | mod tests { | ||
50 | use expect_test::{expect, Expect}; | ||
51 | use ide_db::helpers::FamousDefs; | ||
52 | |||
53 | use crate::{ | ||
54 | test_utils::{self, completion_list}, | ||
55 | CompletionKind, | ||
56 | }; | ||
57 | |||
58 | fn check(ra_fixture: &str, expect: Expect) { | ||
59 | let actual = completion_list(ra_fixture, CompletionKind::Reference); | ||
60 | expect.assert_eq(&actual); | ||
61 | } | ||
62 | |||
63 | fn check_snippet(ra_fixture: &str, expect: Expect) { | ||
64 | let actual = completion_list( | ||
65 | &format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE), | ||
66 | CompletionKind::Snippet, | ||
67 | ); | ||
68 | expect.assert_eq(&actual); | ||
69 | } | ||
70 | |||
71 | fn check_edit(what: &str, ra_fixture_before: &str, ra_fixture_after: &str) { | ||
72 | test_utils::check_edit( | ||
73 | what, | ||
74 | &format!( | ||
75 | "//- /main.rs crate:main deps:core{}\n{}", | ||
76 | ra_fixture_before, | ||
77 | FamousDefs::FIXTURE, | ||
78 | ), | ||
79 | &(ra_fixture_after.to_owned() + "\n"), | ||
80 | ); | ||
81 | } | ||
82 | |||
83 | #[test] | ||
84 | fn test_record_literal_field_default() { | ||
85 | let test_code = r#" | ||
86 | struct S { foo: u32, bar: usize } | ||
87 | |||
88 | impl core::default::Default for S { | ||
89 | fn default() -> Self { | ||
90 | S { | ||
91 | foo: 0, | ||
92 | bar: 0, | ||
93 | } | ||
94 | } | ||
95 | } | ||
96 | |||
97 | fn process(f: S) { | ||
98 | let other = S { | ||
99 | foo: 5, | ||
100 | .$0 | ||
101 | }; | ||
102 | } | ||
103 | "#; | ||
104 | check( | ||
105 | test_code, | ||
106 | expect![[r#" | ||
107 | fd bar usize | ||
108 | "#]], | ||
109 | ); | ||
110 | |||
111 | check_snippet( | ||
112 | test_code, | ||
113 | expect![[r#" | ||
114 | sn pd | ||
115 | sn ppd | ||
116 | fd ..Default::default() | ||
117 | "#]], | ||
118 | ); | ||
119 | } | ||
120 | |||
121 | #[test] | ||
122 | fn test_record_literal_field_default_completion() { | ||
123 | check_edit( | ||
124 | "..Default::default()", | ||
125 | r#" | ||
126 | struct S { foo: u32, bar: usize } | ||
127 | |||
128 | impl core::default::Default for S { | ||
129 | fn default() -> Self { | ||
130 | S { | ||
131 | foo: 0, | ||
132 | bar: 0, | ||
133 | } | ||
134 | } | ||
135 | } | ||
136 | |||
137 | fn process(f: S) { | ||
138 | let other = S { | ||
139 | foo: 5, | ||
140 | .$0 | ||
141 | }; | ||
142 | } | ||
143 | "#, | ||
144 | r#" | ||
145 | struct S { foo: u32, bar: usize } | ||
146 | |||
147 | impl core::default::Default for S { | ||
148 | fn default() -> Self { | ||
149 | S { | ||
150 | foo: 0, | ||
151 | bar: 0, | ||
152 | } | ||
153 | } | ||
154 | } | ||
155 | |||
156 | fn process(f: S) { | ||
157 | let other = S { | ||
158 | foo: 5, | ||
159 | ..Default::default() | ||
160 | }; | ||
161 | } | ||
162 | "#, | ||
163 | ); | ||
164 | } | ||
165 | |||
166 | #[test] | ||
167 | fn test_record_literal_field_without_default() { | ||
168 | let test_code = r#" | ||
169 | struct S { foo: u32, bar: usize } | ||
170 | |||
171 | fn process(f: S) { | ||
172 | let other = S { | ||
173 | foo: 5, | ||
174 | .$0 | ||
175 | }; | ||
176 | } | ||
177 | "#; | ||
178 | check( | ||
179 | test_code, | ||
180 | expect![[r#" | ||
181 | fd bar usize | ||
182 | "#]], | ||
183 | ); | ||
184 | |||
185 | check_snippet( | ||
186 | test_code, | ||
187 | expect![[r#" | ||
188 | sn pd | ||
189 | sn ppd | ||
190 | "#]], | ||
191 | ); | ||
192 | } | ||
193 | |||
194 | #[test] | ||
195 | fn test_record_pattern_field() { | ||
196 | check( | ||
197 | r#" | ||
198 | struct S { foo: u32 } | ||
199 | |||
200 | fn process(f: S) { | ||
201 | match f { | ||
202 | S { f$0: 92 } => (), | ||
203 | } | ||
204 | } | ||
205 | "#, | ||
206 | expect![[r#" | ||
207 | fd foo u32 | ||
208 | "#]], | ||
209 | ); | ||
210 | } | ||
211 | |||
212 | #[test] | ||
213 | fn test_record_pattern_enum_variant() { | ||
214 | check( | ||
215 | r#" | ||
216 | enum E { S { foo: u32, bar: () } } | ||
217 | |||
218 | fn process(e: E) { | ||
219 | match e { | ||
220 | E::S { $0 } => (), | ||
221 | } | ||
222 | } | ||
223 | "#, | ||
224 | expect![[r#" | ||
225 | fd foo u32 | ||
226 | fd bar () | ||
227 | "#]], | ||
228 | ); | ||
229 | } | ||
230 | |||
231 | #[test] | ||
232 | fn test_record_pattern_field_in_simple_macro() { | ||
233 | check( | ||
234 | r" | ||
235 | macro_rules! m { ($e:expr) => { $e } } | ||
236 | struct S { foo: u32 } | ||
237 | |||
238 | fn process(f: S) { | ||
239 | m!(match f { | ||
240 | S { f$0: 92 } => (), | ||
241 | }) | ||
242 | } | ||
243 | ", | ||
244 | expect![[r#" | ||
245 | fd foo u32 | ||
246 | "#]], | ||
247 | ); | ||
248 | } | ||
249 | |||
250 | #[test] | ||
251 | fn only_missing_fields_are_completed_in_destruct_pats() { | ||
252 | check( | ||
253 | r#" | ||
254 | struct S { | ||
255 | foo1: u32, foo2: u32, | ||
256 | bar: u32, baz: u32, | ||
257 | } | ||
258 | |||
259 | fn main() { | ||
260 | let s = S { | ||
261 | foo1: 1, foo2: 2, | ||
262 | bar: 3, baz: 4, | ||
263 | }; | ||
264 | if let S { foo1, foo2: a, $0 } = s {} | ||
265 | } | ||
266 | "#, | ||
267 | expect![[r#" | ||
268 | fd bar u32 | ||
269 | fd baz u32 | ||
270 | "#]], | ||
271 | ); | ||
272 | } | ||
273 | |||
274 | #[test] | ||
275 | fn test_record_literal_field() { | ||
276 | check( | ||
277 | r#" | ||
278 | struct A { the_field: u32 } | ||
279 | fn foo() { | ||
280 | A { the$0 } | ||
281 | } | ||
282 | "#, | ||
283 | expect![[r#" | ||
284 | fd the_field u32 | ||
285 | "#]], | ||
286 | ); | ||
287 | } | ||
288 | |||
289 | #[test] | ||
290 | fn test_record_literal_enum_variant() { | ||
291 | check( | ||
292 | r#" | ||
293 | enum E { A { a: u32 } } | ||
294 | fn foo() { | ||
295 | let _ = E::A { $0 } | ||
296 | } | ||
297 | "#, | ||
298 | expect![[r#" | ||
299 | fd a u32 | ||
300 | "#]], | ||
301 | ); | ||
302 | } | ||
303 | |||
304 | #[test] | ||
305 | fn test_record_literal_two_structs() { | ||
306 | check( | ||
307 | r#" | ||
308 | struct A { a: u32 } | ||
309 | struct B { b: u32 } | ||
310 | |||
311 | fn foo() { | ||
312 | let _: A = B { $0 } | ||
313 | } | ||
314 | "#, | ||
315 | expect![[r#" | ||
316 | fd b u32 | ||
317 | "#]], | ||
318 | ); | ||
319 | } | ||
320 | |||
321 | #[test] | ||
322 | fn test_record_literal_generic_struct() { | ||
323 | check( | ||
324 | r#" | ||
325 | struct A<T> { a: T } | ||
326 | |||
327 | fn foo() { | ||
328 | let _: A<u32> = A { $0 } | ||
329 | } | ||
330 | "#, | ||
331 | expect![[r#" | ||
332 | fd a u32 | ||
333 | "#]], | ||
334 | ); | ||
335 | } | ||
336 | |||
337 | #[test] | ||
338 | fn test_record_literal_field_in_simple_macro() { | ||
339 | check( | ||
340 | r#" | ||
341 | macro_rules! m { ($e:expr) => { $e } } | ||
342 | struct A { the_field: u32 } | ||
343 | fn foo() { | ||
344 | m!(A { the$0 }) | ||
345 | } | ||
346 | "#, | ||
347 | expect![[r#" | ||
348 | fd the_field u32 | ||
349 | "#]], | ||
350 | ); | ||
351 | } | ||
352 | |||
353 | #[test] | ||
354 | fn only_missing_fields_are_completed() { | ||
355 | check( | ||
356 | r#" | ||
357 | struct S { | ||
358 | foo1: u32, foo2: u32, | ||
359 | bar: u32, baz: u32, | ||
360 | } | ||
361 | |||
362 | fn main() { | ||
363 | let foo1 = 1; | ||
364 | let s = S { foo1, foo2: 5, $0 } | ||
365 | } | ||
366 | "#, | ||
367 | expect![[r#" | ||
368 | fd bar u32 | ||
369 | fd baz u32 | ||
370 | "#]], | ||
371 | ); | ||
372 | } | ||
373 | |||
374 | #[test] | ||
375 | fn completes_functional_update() { | ||
376 | check( | ||
377 | r#" | ||
378 | struct S { foo1: u32, foo2: u32 } | ||
379 | |||
380 | fn main() { | ||
381 | let foo1 = 1; | ||
382 | let s = S { foo1, $0 .. loop {} } | ||
383 | } | ||
384 | "#, | ||
385 | expect![[r#" | ||
386 | fd foo2 u32 | ||
387 | "#]], | ||
388 | ); | ||
389 | } | ||
390 | } | ||