diff options
author | Aleksey Kladov <[email protected]> | 2019-11-27 18:32:33 +0000 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2019-11-27 18:35:06 +0000 |
commit | 757e593b253b4df7e6fc8bf15a4d4f34c9d484c5 (patch) | |
tree | d972d3a7e6457efdb5e0c558a8350db1818d07ae /crates/ra_ide_api/src/completion | |
parent | d9a36a736bfb91578a36505e7237212959bb55fe (diff) |
rename ra_ide_api -> ra_ide
Diffstat (limited to 'crates/ra_ide_api/src/completion')
-rw-r--r-- | crates/ra_ide_api/src/completion/complete_dot.rs | 456 | ||||
-rw-r--r-- | crates/ra_ide_api/src/completion/complete_fn_param.rs | 136 | ||||
-rw-r--r-- | crates/ra_ide_api/src/completion/complete_keyword.rs | 781 | ||||
-rw-r--r-- | crates/ra_ide_api/src/completion/complete_macro_in_item_position.rs | 143 | ||||
-rw-r--r-- | crates/ra_ide_api/src/completion/complete_path.rs | 785 | ||||
-rw-r--r-- | crates/ra_ide_api/src/completion/complete_pattern.rs | 89 | ||||
-rw-r--r-- | crates/ra_ide_api/src/completion/complete_postfix.rs | 282 | ||||
-rw-r--r-- | crates/ra_ide_api/src/completion/complete_record_literal.rs | 159 | ||||
-rw-r--r-- | crates/ra_ide_api/src/completion/complete_record_pattern.rs | 93 | ||||
-rw-r--r-- | crates/ra_ide_api/src/completion/complete_scope.rs | 876 | ||||
-rw-r--r-- | crates/ra_ide_api/src/completion/complete_snippet.rs | 120 | ||||
-rw-r--r-- | crates/ra_ide_api/src/completion/completion_context.rs | 274 | ||||
-rw-r--r-- | crates/ra_ide_api/src/completion/completion_item.rs | 322 | ||||
-rw-r--r-- | crates/ra_ide_api/src/completion/presentation.rs | 676 |
14 files changed, 0 insertions, 5192 deletions
diff --git a/crates/ra_ide_api/src/completion/complete_dot.rs b/crates/ra_ide_api/src/completion/complete_dot.rs deleted file mode 100644 index b6fe48627..000000000 --- a/crates/ra_ide_api/src/completion/complete_dot.rs +++ /dev/null | |||
@@ -1,456 +0,0 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | use hir::Type; | ||
4 | |||
5 | use crate::completion::completion_item::CompletionKind; | ||
6 | use crate::{ | ||
7 | completion::{completion_context::CompletionContext, completion_item::Completions}, | ||
8 | CompletionItem, | ||
9 | }; | ||
10 | use rustc_hash::FxHashSet; | ||
11 | |||
12 | /// Complete dot accesses, i.e. fields or methods (and .await syntax). | ||
13 | pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { | ||
14 | let dot_receiver = match &ctx.dot_receiver { | ||
15 | Some(expr) => expr, | ||
16 | _ => return, | ||
17 | }; | ||
18 | |||
19 | let receiver_ty = match ctx.analyzer.type_of(ctx.db, &dot_receiver) { | ||
20 | Some(ty) => ty, | ||
21 | _ => return, | ||
22 | }; | ||
23 | |||
24 | if !ctx.is_call { | ||
25 | complete_fields(acc, ctx, &receiver_ty); | ||
26 | } | ||
27 | complete_methods(acc, ctx, &receiver_ty); | ||
28 | |||
29 | // Suggest .await syntax for types that implement Future trait | ||
30 | if ctx.analyzer.impls_future(ctx.db, receiver_ty.into_ty()) { | ||
31 | CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await") | ||
32 | .detail("expr.await") | ||
33 | .insert_text("await") | ||
34 | .add_to(acc); | ||
35 | } | ||
36 | } | ||
37 | |||
38 | fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { | ||
39 | for receiver in receiver.autoderef(ctx.db) { | ||
40 | for (field, ty) in receiver.fields(ctx.db) { | ||
41 | acc.add_field(ctx, field, &ty); | ||
42 | } | ||
43 | for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() { | ||
44 | acc.add_tuple_field(ctx, i, &ty); | ||
45 | } | ||
46 | } | ||
47 | } | ||
48 | |||
49 | fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { | ||
50 | let mut seen_methods = FxHashSet::default(); | ||
51 | ctx.analyzer.iterate_method_candidates(ctx.db, receiver, None, |_ty, func| { | ||
52 | if func.has_self_param(ctx.db) && seen_methods.insert(func.name(ctx.db)) { | ||
53 | acc.add_function(ctx, func); | ||
54 | } | ||
55 | None::<()> | ||
56 | }); | ||
57 | } | ||
58 | |||
59 | #[cfg(test)] | ||
60 | mod tests { | ||
61 | use crate::completion::{do_completion, CompletionItem, CompletionKind}; | ||
62 | use insta::assert_debug_snapshot; | ||
63 | |||
64 | fn do_ref_completion(code: &str) -> Vec<CompletionItem> { | ||
65 | do_completion(code, CompletionKind::Reference) | ||
66 | } | ||
67 | |||
68 | #[test] | ||
69 | fn test_struct_field_completion() { | ||
70 | assert_debug_snapshot!( | ||
71 | do_ref_completion( | ||
72 | r" | ||
73 | struct A { the_field: u32 } | ||
74 | fn foo(a: A) { | ||
75 | a.<|> | ||
76 | } | ||
77 | ", | ||
78 | ), | ||
79 | @r###" | ||
80 | [ | ||
81 | CompletionItem { | ||
82 | label: "the_field", | ||
83 | source_range: [94; 94), | ||
84 | delete: [94; 94), | ||
85 | insert: "the_field", | ||
86 | kind: Field, | ||
87 | detail: "u32", | ||
88 | }, | ||
89 | ] | ||
90 | "### | ||
91 | ); | ||
92 | } | ||
93 | |||
94 | #[test] | ||
95 | fn test_struct_field_completion_self() { | ||
96 | assert_debug_snapshot!( | ||
97 | do_ref_completion( | ||
98 | r" | ||
99 | struct A { | ||
100 | /// This is the_field | ||
101 | the_field: (u32,) | ||
102 | } | ||
103 | impl A { | ||
104 | fn foo(self) { | ||
105 | self.<|> | ||
106 | } | ||
107 | } | ||
108 | ", | ||
109 | ), | ||
110 | @r###" | ||
111 | [ | ||
112 | CompletionItem { | ||
113 | label: "foo()", | ||
114 | source_range: [187; 187), | ||
115 | delete: [187; 187), | ||
116 | insert: "foo()$0", | ||
117 | kind: Method, | ||
118 | lookup: "foo", | ||
119 | detail: "fn foo(self)", | ||
120 | }, | ||
121 | CompletionItem { | ||
122 | label: "the_field", | ||
123 | source_range: [187; 187), | ||
124 | delete: [187; 187), | ||
125 | insert: "the_field", | ||
126 | kind: Field, | ||
127 | detail: "(u32,)", | ||
128 | documentation: Documentation( | ||
129 | "This is the_field", | ||
130 | ), | ||
131 | }, | ||
132 | ] | ||
133 | "### | ||
134 | ); | ||
135 | } | ||
136 | |||
137 | #[test] | ||
138 | fn test_struct_field_completion_autoderef() { | ||
139 | assert_debug_snapshot!( | ||
140 | do_ref_completion( | ||
141 | r" | ||
142 | struct A { the_field: (u32, i32) } | ||
143 | impl A { | ||
144 | fn foo(&self) { | ||
145 | self.<|> | ||
146 | } | ||
147 | } | ||
148 | ", | ||
149 | ), | ||
150 | @r###" | ||
151 | [ | ||
152 | CompletionItem { | ||
153 | label: "foo()", | ||
154 | source_range: [126; 126), | ||
155 | delete: [126; 126), | ||
156 | insert: "foo()$0", | ||
157 | kind: Method, | ||
158 | lookup: "foo", | ||
159 | detail: "fn foo(&self)", | ||
160 | }, | ||
161 | CompletionItem { | ||
162 | label: "the_field", | ||
163 | source_range: [126; 126), | ||
164 | delete: [126; 126), | ||
165 | insert: "the_field", | ||
166 | kind: Field, | ||
167 | detail: "(u32, i32)", | ||
168 | }, | ||
169 | ] | ||
170 | "### | ||
171 | ); | ||
172 | } | ||
173 | |||
174 | #[test] | ||
175 | fn test_no_struct_field_completion_for_method_call() { | ||
176 | assert_debug_snapshot!( | ||
177 | do_ref_completion( | ||
178 | r" | ||
179 | struct A { the_field: u32 } | ||
180 | fn foo(a: A) { | ||
181 | a.<|>() | ||
182 | } | ||
183 | ", | ||
184 | ), | ||
185 | @"[]" | ||
186 | ); | ||
187 | } | ||
188 | |||
189 | #[test] | ||
190 | fn test_method_completion() { | ||
191 | assert_debug_snapshot!( | ||
192 | do_ref_completion( | ||
193 | r" | ||
194 | struct A {} | ||
195 | impl A { | ||
196 | fn the_method(&self) {} | ||
197 | } | ||
198 | fn foo(a: A) { | ||
199 | a.<|> | ||
200 | } | ||
201 | ", | ||
202 | ), | ||
203 | @r###" | ||
204 | [ | ||
205 | CompletionItem { | ||
206 | label: "the_method()", | ||
207 | source_range: [144; 144), | ||
208 | delete: [144; 144), | ||
209 | insert: "the_method()$0", | ||
210 | kind: Method, | ||
211 | lookup: "the_method", | ||
212 | detail: "fn the_method(&self)", | ||
213 | }, | ||
214 | ] | ||
215 | "### | ||
216 | ); | ||
217 | } | ||
218 | |||
219 | #[test] | ||
220 | fn test_trait_method_completion() { | ||
221 | assert_debug_snapshot!( | ||
222 | do_ref_completion( | ||
223 | r" | ||
224 | struct A {} | ||
225 | trait Trait { fn the_method(&self); } | ||
226 | impl Trait for A {} | ||
227 | fn foo(a: A) { | ||
228 | a.<|> | ||
229 | } | ||
230 | ", | ||
231 | ), | ||
232 | @r###" | ||
233 | [ | ||
234 | CompletionItem { | ||
235 | label: "the_method()", | ||
236 | source_range: [151; 151), | ||
237 | delete: [151; 151), | ||
238 | insert: "the_method()$0", | ||
239 | kind: Method, | ||
240 | lookup: "the_method", | ||
241 | detail: "fn the_method(&self)", | ||
242 | }, | ||
243 | ] | ||
244 | "### | ||
245 | ); | ||
246 | } | ||
247 | |||
248 | #[test] | ||
249 | fn test_trait_method_completion_deduplicated() { | ||
250 | assert_debug_snapshot!( | ||
251 | do_ref_completion( | ||
252 | r" | ||
253 | struct A {} | ||
254 | trait Trait { fn the_method(&self); } | ||
255 | impl<T> Trait for T {} | ||
256 | fn foo(a: &A) { | ||
257 | a.<|> | ||
258 | } | ||
259 | ", | ||
260 | ), | ||
261 | @r###" | ||
262 | [ | ||
263 | CompletionItem { | ||
264 | label: "the_method()", | ||
265 | source_range: [155; 155), | ||
266 | delete: [155; 155), | ||
267 | insert: "the_method()$0", | ||
268 | kind: Method, | ||
269 | lookup: "the_method", | ||
270 | detail: "fn the_method(&self)", | ||
271 | }, | ||
272 | ] | ||
273 | "### | ||
274 | ); | ||
275 | } | ||
276 | |||
277 | #[test] | ||
278 | fn test_no_non_self_method() { | ||
279 | assert_debug_snapshot!( | ||
280 | do_ref_completion( | ||
281 | r" | ||
282 | struct A {} | ||
283 | impl A { | ||
284 | fn the_method() {} | ||
285 | } | ||
286 | fn foo(a: A) { | ||
287 | a.<|> | ||
288 | } | ||
289 | ", | ||
290 | ), | ||
291 | @"[]" | ||
292 | ); | ||
293 | } | ||
294 | |||
295 | #[test] | ||
296 | fn test_method_attr_filtering() { | ||
297 | assert_debug_snapshot!( | ||
298 | do_ref_completion( | ||
299 | r" | ||
300 | struct A {} | ||
301 | impl A { | ||
302 | #[inline] | ||
303 | fn the_method(&self) { | ||
304 | let x = 1; | ||
305 | let y = 2; | ||
306 | } | ||
307 | } | ||
308 | fn foo(a: A) { | ||
309 | a.<|> | ||
310 | } | ||
311 | ", | ||
312 | ), | ||
313 | @r###" | ||
314 | [ | ||
315 | CompletionItem { | ||
316 | label: "the_method()", | ||
317 | source_range: [249; 249), | ||
318 | delete: [249; 249), | ||
319 | insert: "the_method()$0", | ||
320 | kind: Method, | ||
321 | lookup: "the_method", | ||
322 | detail: "fn the_method(&self)", | ||
323 | }, | ||
324 | ] | ||
325 | "### | ||
326 | ); | ||
327 | } | ||
328 | |||
329 | #[test] | ||
330 | fn test_tuple_field_completion() { | ||
331 | assert_debug_snapshot!( | ||
332 | do_ref_completion( | ||
333 | r" | ||
334 | fn foo() { | ||
335 | let b = (0, 3.14); | ||
336 | b.<|> | ||
337 | } | ||
338 | ", | ||
339 | ), | ||
340 | @r###" | ||
341 | [ | ||
342 | CompletionItem { | ||
343 | label: "0", | ||
344 | source_range: [75; 75), | ||
345 | delete: [75; 75), | ||
346 | insert: "0", | ||
347 | kind: Field, | ||
348 | detail: "i32", | ||
349 | }, | ||
350 | CompletionItem { | ||
351 | label: "1", | ||
352 | source_range: [75; 75), | ||
353 | delete: [75; 75), | ||
354 | insert: "1", | ||
355 | kind: Field, | ||
356 | detail: "f64", | ||
357 | }, | ||
358 | ] | ||
359 | "### | ||
360 | ); | ||
361 | } | ||
362 | |||
363 | #[test] | ||
364 | fn test_tuple_field_inference() { | ||
365 | assert_debug_snapshot!( | ||
366 | do_ref_completion( | ||
367 | r" | ||
368 | pub struct S; | ||
369 | impl S { | ||
370 | pub fn blah(&self) {} | ||
371 | } | ||
372 | |||
373 | struct T(S); | ||
374 | |||
375 | impl T { | ||
376 | fn foo(&self) { | ||
377 | // FIXME: This doesn't work without the trailing `a` as `0.` is a float | ||
378 | self.0.a<|> | ||
379 | } | ||
380 | } | ||
381 | ", | ||
382 | ), | ||
383 | @r###" | ||
384 | [ | ||
385 | CompletionItem { | ||
386 | label: "blah()", | ||
387 | source_range: [299; 300), | ||
388 | delete: [299; 300), | ||
389 | insert: "blah()$0", | ||
390 | kind: Method, | ||
391 | lookup: "blah", | ||
392 | detail: "pub fn blah(&self)", | ||
393 | }, | ||
394 | ] | ||
395 | "### | ||
396 | ); | ||
397 | } | ||
398 | |||
399 | #[test] | ||
400 | fn test_completion_works_in_consts() { | ||
401 | assert_debug_snapshot!( | ||
402 | do_ref_completion( | ||
403 | r" | ||
404 | struct A { the_field: u32 } | ||
405 | const X: u32 = { | ||
406 | A { the_field: 92 }.<|> | ||
407 | }; | ||
408 | ", | ||
409 | ), | ||
410 | @r###" | ||
411 | [ | ||
412 | CompletionItem { | ||
413 | label: "the_field", | ||
414 | source_range: [106; 106), | ||
415 | delete: [106; 106), | ||
416 | insert: "the_field", | ||
417 | kind: Field, | ||
418 | detail: "u32", | ||
419 | }, | ||
420 | ] | ||
421 | "### | ||
422 | ); | ||
423 | } | ||
424 | |||
425 | #[test] | ||
426 | fn test_completion_await_impls_future() { | ||
427 | assert_debug_snapshot!( | ||
428 | do_completion( | ||
429 | r###" | ||
430 | //- /main.rs | ||
431 | use std::future::*; | ||
432 | struct A {} | ||
433 | impl Future for A {} | ||
434 | fn foo(a: A) { | ||
435 | a.<|> | ||
436 | } | ||
437 | |||
438 | //- /std/lib.rs | ||
439 | pub mod future { | ||
440 | pub trait Future {} | ||
441 | } | ||
442 | "###, CompletionKind::Keyword), | ||
443 | @r###" | ||
444 | [ | ||
445 | CompletionItem { | ||
446 | label: "await", | ||
447 | source_range: [74; 74), | ||
448 | delete: [74; 74), | ||
449 | insert: "await", | ||
450 | detail: "expr.await", | ||
451 | }, | ||
452 | ] | ||
453 | "### | ||
454 | ) | ||
455 | } | ||
456 | } | ||
diff --git a/crates/ra_ide_api/src/completion/complete_fn_param.rs b/crates/ra_ide_api/src/completion/complete_fn_param.rs deleted file mode 100644 index 502458706..000000000 --- a/crates/ra_ide_api/src/completion/complete_fn_param.rs +++ /dev/null | |||
@@ -1,136 +0,0 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | use ra_syntax::{ast, match_ast, AstNode}; | ||
4 | use rustc_hash::FxHashMap; | ||
5 | |||
6 | use crate::completion::{CompletionContext, CompletionItem, CompletionKind, Completions}; | ||
7 | |||
8 | /// Complete repeated parameters, both name and type. For example, if all | ||
9 | /// functions in a file have a `spam: &mut Spam` parameter, a completion with | ||
10 | /// `spam: &mut Spam` insert text/label and `spam` lookup string will be | ||
11 | /// suggested. | ||
12 | pub(super) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext) { | ||
13 | if !ctx.is_param { | ||
14 | return; | ||
15 | } | ||
16 | |||
17 | let mut params = FxHashMap::default(); | ||
18 | for node in ctx.token.parent().ancestors() { | ||
19 | match_ast! { | ||
20 | match node { | ||
21 | ast::SourceFile(it) => { process(it, &mut params) }, | ||
22 | ast::ItemList(it) => { process(it, &mut params) }, | ||
23 | _ => (), | ||
24 | } | ||
25 | } | ||
26 | } | ||
27 | params | ||
28 | .into_iter() | ||
29 | .filter_map(|(label, (count, param))| { | ||
30 | let lookup = param.pat()?.syntax().text().to_string(); | ||
31 | if count < 2 { | ||
32 | None | ||
33 | } else { | ||
34 | Some((label, lookup)) | ||
35 | } | ||
36 | }) | ||
37 | .for_each(|(label, lookup)| { | ||
38 | CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label) | ||
39 | .lookup_by(lookup) | ||
40 | .add_to(acc) | ||
41 | }); | ||
42 | |||
43 | fn process<N: ast::FnDefOwner>(node: N, params: &mut FxHashMap<String, (u32, ast::Param)>) { | ||
44 | node.functions().filter_map(|it| it.param_list()).flat_map(|it| it.params()).for_each( | ||
45 | |param| { | ||
46 | let text = param.syntax().text().to_string(); | ||
47 | params.entry(text).or_insert((0, param)).0 += 1; | ||
48 | }, | ||
49 | ) | ||
50 | } | ||
51 | } | ||
52 | |||
53 | #[cfg(test)] | ||
54 | mod tests { | ||
55 | use crate::completion::{do_completion, CompletionItem, CompletionKind}; | ||
56 | use insta::assert_debug_snapshot; | ||
57 | |||
58 | fn do_magic_completion(code: &str) -> Vec<CompletionItem> { | ||
59 | do_completion(code, CompletionKind::Magic) | ||
60 | } | ||
61 | |||
62 | #[test] | ||
63 | fn test_param_completion_last_param() { | ||
64 | assert_debug_snapshot!( | ||
65 | do_magic_completion( | ||
66 | r" | ||
67 | fn foo(file_id: FileId) {} | ||
68 | fn bar(file_id: FileId) {} | ||
69 | fn baz(file<|>) {} | ||
70 | ", | ||
71 | ), | ||
72 | @r###" | ||
73 | [ | ||
74 | CompletionItem { | ||
75 | label: "file_id: FileId", | ||
76 | source_range: [110; 114), | ||
77 | delete: [110; 114), | ||
78 | insert: "file_id: FileId", | ||
79 | lookup: "file_id", | ||
80 | }, | ||
81 | ] | ||
82 | "### | ||
83 | ); | ||
84 | } | ||
85 | |||
86 | #[test] | ||
87 | fn test_param_completion_nth_param() { | ||
88 | assert_debug_snapshot!( | ||
89 | do_magic_completion( | ||
90 | r" | ||
91 | fn foo(file_id: FileId) {} | ||
92 | fn bar(file_id: FileId) {} | ||
93 | fn baz(file<|>, x: i32) {} | ||
94 | ", | ||
95 | ), | ||
96 | @r###" | ||
97 | [ | ||
98 | CompletionItem { | ||
99 | label: "file_id: FileId", | ||
100 | source_range: [110; 114), | ||
101 | delete: [110; 114), | ||
102 | insert: "file_id: FileId", | ||
103 | lookup: "file_id", | ||
104 | }, | ||
105 | ] | ||
106 | "### | ||
107 | ); | ||
108 | } | ||
109 | |||
110 | #[test] | ||
111 | fn test_param_completion_trait_param() { | ||
112 | assert_debug_snapshot!( | ||
113 | do_magic_completion( | ||
114 | r" | ||
115 | pub(crate) trait SourceRoot { | ||
116 | pub fn contains(&self, file_id: FileId) -> bool; | ||
117 | pub fn module_map(&self) -> &ModuleMap; | ||
118 | pub fn lines(&self, file_id: FileId) -> &LineIndex; | ||
119 | pub fn syntax(&self, file<|>) | ||
120 | } | ||
121 | ", | ||
122 | ), | ||
123 | @r###" | ||
124 | [ | ||
125 | CompletionItem { | ||
126 | label: "file_id: FileId", | ||
127 | source_range: [289; 293), | ||
128 | delete: [289; 293), | ||
129 | insert: "file_id: FileId", | ||
130 | lookup: "file_id", | ||
131 | }, | ||
132 | ] | ||
133 | "### | ||
134 | ); | ||
135 | } | ||
136 | } | ||
diff --git a/crates/ra_ide_api/src/completion/complete_keyword.rs b/crates/ra_ide_api/src/completion/complete_keyword.rs deleted file mode 100644 index eb7cd9ac2..000000000 --- a/crates/ra_ide_api/src/completion/complete_keyword.rs +++ /dev/null | |||
@@ -1,781 +0,0 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | use ra_syntax::{ | ||
4 | ast::{self, LoopBodyOwner}, | ||
5 | match_ast, AstNode, | ||
6 | SyntaxKind::*, | ||
7 | SyntaxToken, | ||
8 | }; | ||
9 | |||
10 | use crate::completion::{ | ||
11 | CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions, | ||
12 | }; | ||
13 | |||
14 | pub(super) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionContext) { | ||
15 | // complete keyword "crate" in use stmt | ||
16 | let source_range = ctx.source_range(); | ||
17 | match (ctx.use_item_syntax.as_ref(), ctx.path_prefix.as_ref()) { | ||
18 | (Some(_), None) => { | ||
19 | CompletionItem::new(CompletionKind::Keyword, source_range, "crate") | ||
20 | .kind(CompletionItemKind::Keyword) | ||
21 | .insert_text("crate::") | ||
22 | .add_to(acc); | ||
23 | CompletionItem::new(CompletionKind::Keyword, source_range, "self") | ||
24 | .kind(CompletionItemKind::Keyword) | ||
25 | .add_to(acc); | ||
26 | CompletionItem::new(CompletionKind::Keyword, source_range, "super") | ||
27 | .kind(CompletionItemKind::Keyword) | ||
28 | .insert_text("super::") | ||
29 | .add_to(acc); | ||
30 | } | ||
31 | (Some(_), Some(_)) => { | ||
32 | CompletionItem::new(CompletionKind::Keyword, source_range, "self") | ||
33 | .kind(CompletionItemKind::Keyword) | ||
34 | .add_to(acc); | ||
35 | CompletionItem::new(CompletionKind::Keyword, source_range, "super") | ||
36 | .kind(CompletionItemKind::Keyword) | ||
37 | .insert_text("super::") | ||
38 | .add_to(acc); | ||
39 | } | ||
40 | _ => {} | ||
41 | } | ||
42 | } | ||
43 | |||
44 | fn keyword(ctx: &CompletionContext, kw: &str, snippet: &str) -> CompletionItem { | ||
45 | CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw) | ||
46 | .kind(CompletionItemKind::Keyword) | ||
47 | .insert_snippet(snippet) | ||
48 | .build() | ||
49 | } | ||
50 | |||
51 | pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) { | ||
52 | if !ctx.is_trivial_path { | ||
53 | return; | ||
54 | } | ||
55 | |||
56 | let fn_def = match &ctx.function_syntax { | ||
57 | Some(it) => it, | ||
58 | None => return, | ||
59 | }; | ||
60 | acc.add(keyword(ctx, "if", "if $0 {}")); | ||
61 | acc.add(keyword(ctx, "match", "match $0 {}")); | ||
62 | acc.add(keyword(ctx, "while", "while $0 {}")); | ||
63 | acc.add(keyword(ctx, "loop", "loop {$0}")); | ||
64 | |||
65 | if ctx.after_if { | ||
66 | acc.add(keyword(ctx, "else", "else {$0}")); | ||
67 | acc.add(keyword(ctx, "else if", "else if $0 {}")); | ||
68 | } | ||
69 | if is_in_loop_body(&ctx.token) { | ||
70 | if ctx.can_be_stmt { | ||
71 | acc.add(keyword(ctx, "continue", "continue;")); | ||
72 | acc.add(keyword(ctx, "break", "break;")); | ||
73 | } else { | ||
74 | acc.add(keyword(ctx, "continue", "continue")); | ||
75 | acc.add(keyword(ctx, "break", "break")); | ||
76 | } | ||
77 | } | ||
78 | acc.add_all(complete_return(ctx, &fn_def, ctx.can_be_stmt)); | ||
79 | } | ||
80 | |||
81 | fn is_in_loop_body(leaf: &SyntaxToken) -> bool { | ||
82 | for node in leaf.parent().ancestors() { | ||
83 | if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR { | ||
84 | break; | ||
85 | } | ||
86 | let loop_body = match_ast! { | ||
87 | match node { | ||
88 | ast::ForExpr(it) => { it.loop_body() }, | ||
89 | ast::WhileExpr(it) => { it.loop_body() }, | ||
90 | ast::LoopExpr(it) => { it.loop_body() }, | ||
91 | _ => None, | ||
92 | } | ||
93 | }; | ||
94 | if let Some(body) = loop_body { | ||
95 | if leaf.text_range().is_subrange(&body.syntax().text_range()) { | ||
96 | return true; | ||
97 | } | ||
98 | } | ||
99 | } | ||
100 | false | ||
101 | } | ||
102 | |||
103 | fn complete_return( | ||
104 | ctx: &CompletionContext, | ||
105 | fn_def: &ast::FnDef, | ||
106 | can_be_stmt: bool, | ||
107 | ) -> Option<CompletionItem> { | ||
108 | let snip = match (can_be_stmt, fn_def.ret_type().is_some()) { | ||
109 | (true, true) => "return $0;", | ||
110 | (true, false) => "return;", | ||
111 | (false, true) => "return $0", | ||
112 | (false, false) => "return", | ||
113 | }; | ||
114 | Some(keyword(ctx, "return", snip)) | ||
115 | } | ||
116 | |||
117 | #[cfg(test)] | ||
118 | mod tests { | ||
119 | use crate::completion::{do_completion, CompletionItem, CompletionKind}; | ||
120 | use insta::assert_debug_snapshot; | ||
121 | |||
122 | fn do_keyword_completion(code: &str) -> Vec<CompletionItem> { | ||
123 | do_completion(code, CompletionKind::Keyword) | ||
124 | } | ||
125 | |||
126 | #[test] | ||
127 | fn completes_keywords_in_use_stmt() { | ||
128 | assert_debug_snapshot!( | ||
129 | do_keyword_completion( | ||
130 | r" | ||
131 | use <|> | ||
132 | ", | ||
133 | ), | ||
134 | @r###" | ||
135 | [ | ||
136 | CompletionItem { | ||
137 | label: "crate", | ||
138 | source_range: [21; 21), | ||
139 | delete: [21; 21), | ||
140 | insert: "crate::", | ||
141 | kind: Keyword, | ||
142 | }, | ||
143 | CompletionItem { | ||
144 | label: "self", | ||
145 | source_range: [21; 21), | ||
146 | delete: [21; 21), | ||
147 | insert: "self", | ||
148 | kind: Keyword, | ||
149 | }, | ||
150 | CompletionItem { | ||
151 | label: "super", | ||
152 | source_range: [21; 21), | ||
153 | delete: [21; 21), | ||
154 | insert: "super::", | ||
155 | kind: Keyword, | ||
156 | }, | ||
157 | ] | ||
158 | "### | ||
159 | ); | ||
160 | |||
161 | assert_debug_snapshot!( | ||
162 | do_keyword_completion( | ||
163 | r" | ||
164 | use a::<|> | ||
165 | ", | ||
166 | ), | ||
167 | @r###" | ||
168 | [ | ||
169 | CompletionItem { | ||
170 | label: "self", | ||
171 | source_range: [24; 24), | ||
172 | delete: [24; 24), | ||
173 | insert: "self", | ||
174 | kind: Keyword, | ||
175 | }, | ||
176 | CompletionItem { | ||
177 | label: "super", | ||
178 | source_range: [24; 24), | ||
179 | delete: [24; 24), | ||
180 | insert: "super::", | ||
181 | kind: Keyword, | ||
182 | }, | ||
183 | ] | ||
184 | "### | ||
185 | ); | ||
186 | |||
187 | assert_debug_snapshot!( | ||
188 | do_keyword_completion( | ||
189 | r" | ||
190 | use a::{b, <|>} | ||
191 | ", | ||
192 | ), | ||
193 | @r###" | ||
194 | [ | ||
195 | CompletionItem { | ||
196 | label: "self", | ||
197 | source_range: [28; 28), | ||
198 | delete: [28; 28), | ||
199 | insert: "self", | ||
200 | kind: Keyword, | ||
201 | }, | ||
202 | CompletionItem { | ||
203 | label: "super", | ||
204 | source_range: [28; 28), | ||
205 | delete: [28; 28), | ||
206 | insert: "super::", | ||
207 | kind: Keyword, | ||
208 | }, | ||
209 | ] | ||
210 | "### | ||
211 | ); | ||
212 | } | ||
213 | |||
214 | #[test] | ||
215 | fn completes_various_keywords_in_function() { | ||
216 | assert_debug_snapshot!( | ||
217 | do_keyword_completion( | ||
218 | r" | ||
219 | fn quux() { | ||
220 | <|> | ||
221 | } | ||
222 | ", | ||
223 | ), | ||
224 | @r###" | ||
225 | [ | ||
226 | CompletionItem { | ||
227 | label: "if", | ||
228 | source_range: [49; 49), | ||
229 | delete: [49; 49), | ||
230 | insert: "if $0 {}", | ||
231 | kind: Keyword, | ||
232 | }, | ||
233 | CompletionItem { | ||
234 | label: "loop", | ||
235 | source_range: [49; 49), | ||
236 | delete: [49; 49), | ||
237 | insert: "loop {$0}", | ||
238 | kind: Keyword, | ||
239 | }, | ||
240 | CompletionItem { | ||
241 | label: "match", | ||
242 | source_range: [49; 49), | ||
243 | delete: [49; 49), | ||
244 | insert: "match $0 {}", | ||
245 | kind: Keyword, | ||
246 | }, | ||
247 | CompletionItem { | ||
248 | label: "return", | ||
249 | source_range: [49; 49), | ||
250 | delete: [49; 49), | ||
251 | insert: "return;", | ||
252 | kind: Keyword, | ||
253 | }, | ||
254 | CompletionItem { | ||
255 | label: "while", | ||
256 | source_range: [49; 49), | ||
257 | delete: [49; 49), | ||
258 | insert: "while $0 {}", | ||
259 | kind: Keyword, | ||
260 | }, | ||
261 | ] | ||
262 | "### | ||
263 | ); | ||
264 | } | ||
265 | |||
266 | #[test] | ||
267 | fn completes_else_after_if() { | ||
268 | assert_debug_snapshot!( | ||
269 | do_keyword_completion( | ||
270 | r" | ||
271 | fn quux() { | ||
272 | if true { | ||
273 | () | ||
274 | } <|> | ||
275 | } | ||
276 | ", | ||
277 | ), | ||
278 | @r###" | ||
279 | [ | ||
280 | CompletionItem { | ||
281 | label: "else", | ||
282 | source_range: [108; 108), | ||
283 | delete: [108; 108), | ||
284 | insert: "else {$0}", | ||
285 | kind: Keyword, | ||
286 | }, | ||
287 | CompletionItem { | ||
288 | label: "else if", | ||
289 | source_range: [108; 108), | ||
290 | delete: [108; 108), | ||
291 | insert: "else if $0 {}", | ||
292 | kind: Keyword, | ||
293 | }, | ||
294 | CompletionItem { | ||
295 | label: "if", | ||
296 | source_range: [108; 108), | ||
297 | delete: [108; 108), | ||
298 | insert: "if $0 {}", | ||
299 | kind: Keyword, | ||
300 | }, | ||
301 | CompletionItem { | ||
302 | label: "loop", | ||
303 | source_range: [108; 108), | ||
304 | delete: [108; 108), | ||
305 | insert: "loop {$0}", | ||
306 | kind: Keyword, | ||
307 | }, | ||
308 | CompletionItem { | ||
309 | label: "match", | ||
310 | source_range: [108; 108), | ||
311 | delete: [108; 108), | ||
312 | insert: "match $0 {}", | ||
313 | kind: Keyword, | ||
314 | }, | ||
315 | CompletionItem { | ||
316 | label: "return", | ||
317 | source_range: [108; 108), | ||
318 | delete: [108; 108), | ||
319 | insert: "return;", | ||
320 | kind: Keyword, | ||
321 | }, | ||
322 | CompletionItem { | ||
323 | label: "while", | ||
324 | source_range: [108; 108), | ||
325 | delete: [108; 108), | ||
326 | insert: "while $0 {}", | ||
327 | kind: Keyword, | ||
328 | }, | ||
329 | ] | ||
330 | "### | ||
331 | ); | ||
332 | } | ||
333 | |||
334 | #[test] | ||
335 | fn test_completion_return_value() { | ||
336 | assert_debug_snapshot!( | ||
337 | do_keyword_completion( | ||
338 | r" | ||
339 | fn quux() -> i32 { | ||
340 | <|> | ||
341 | 92 | ||
342 | } | ||
343 | ", | ||
344 | ), | ||
345 | @r###" | ||
346 | [ | ||
347 | CompletionItem { | ||
348 | label: "if", | ||
349 | source_range: [56; 56), | ||
350 | delete: [56; 56), | ||
351 | insert: "if $0 {}", | ||
352 | kind: Keyword, | ||
353 | }, | ||
354 | CompletionItem { | ||
355 | label: "loop", | ||
356 | source_range: [56; 56), | ||
357 | delete: [56; 56), | ||
358 | insert: "loop {$0}", | ||
359 | kind: Keyword, | ||
360 | }, | ||
361 | CompletionItem { | ||
362 | label: "match", | ||
363 | source_range: [56; 56), | ||
364 | delete: [56; 56), | ||
365 | insert: "match $0 {}", | ||
366 | kind: Keyword, | ||
367 | }, | ||
368 | CompletionItem { | ||
369 | label: "return", | ||
370 | source_range: [56; 56), | ||
371 | delete: [56; 56), | ||
372 | insert: "return $0;", | ||
373 | kind: Keyword, | ||
374 | }, | ||
375 | CompletionItem { | ||
376 | label: "while", | ||
377 | source_range: [56; 56), | ||
378 | delete: [56; 56), | ||
379 | insert: "while $0 {}", | ||
380 | kind: Keyword, | ||
381 | }, | ||
382 | ] | ||
383 | "### | ||
384 | ); | ||
385 | assert_debug_snapshot!( | ||
386 | do_keyword_completion( | ||
387 | r" | ||
388 | fn quux() { | ||
389 | <|> | ||
390 | 92 | ||
391 | } | ||
392 | ", | ||
393 | ), | ||
394 | @r###" | ||
395 | [ | ||
396 | CompletionItem { | ||
397 | label: "if", | ||
398 | source_range: [49; 49), | ||
399 | delete: [49; 49), | ||
400 | insert: "if $0 {}", | ||
401 | kind: Keyword, | ||
402 | }, | ||
403 | CompletionItem { | ||
404 | label: "loop", | ||
405 | source_range: [49; 49), | ||
406 | delete: [49; 49), | ||
407 | insert: "loop {$0}", | ||
408 | kind: Keyword, | ||
409 | }, | ||
410 | CompletionItem { | ||
411 | label: "match", | ||
412 | source_range: [49; 49), | ||
413 | delete: [49; 49), | ||
414 | insert: "match $0 {}", | ||
415 | kind: Keyword, | ||
416 | }, | ||
417 | CompletionItem { | ||
418 | label: "return", | ||
419 | source_range: [49; 49), | ||
420 | delete: [49; 49), | ||
421 | insert: "return;", | ||
422 | kind: Keyword, | ||
423 | }, | ||
424 | CompletionItem { | ||
425 | label: "while", | ||
426 | source_range: [49; 49), | ||
427 | delete: [49; 49), | ||
428 | insert: "while $0 {}", | ||
429 | kind: Keyword, | ||
430 | }, | ||
431 | ] | ||
432 | "### | ||
433 | ); | ||
434 | } | ||
435 | |||
436 | #[test] | ||
437 | fn dont_add_semi_after_return_if_not_a_statement() { | ||
438 | assert_debug_snapshot!( | ||
439 | do_keyword_completion( | ||
440 | r" | ||
441 | fn quux() -> i32 { | ||
442 | match () { | ||
443 | () => <|> | ||
444 | } | ||
445 | } | ||
446 | ", | ||
447 | ), | ||
448 | @r###" | ||
449 | [ | ||
450 | CompletionItem { | ||
451 | label: "if", | ||
452 | source_range: [97; 97), | ||
453 | delete: [97; 97), | ||
454 | insert: "if $0 {}", | ||
455 | kind: Keyword, | ||
456 | }, | ||
457 | CompletionItem { | ||
458 | label: "loop", | ||
459 | source_range: [97; 97), | ||
460 | delete: [97; 97), | ||
461 | insert: "loop {$0}", | ||
462 | kind: Keyword, | ||
463 | }, | ||
464 | CompletionItem { | ||
465 | label: "match", | ||
466 | source_range: [97; 97), | ||
467 | delete: [97; 97), | ||
468 | insert: "match $0 {}", | ||
469 | kind: Keyword, | ||
470 | }, | ||
471 | CompletionItem { | ||
472 | label: "return", | ||
473 | source_range: [97; 97), | ||
474 | delete: [97; 97), | ||
475 | insert: "return $0", | ||
476 | kind: Keyword, | ||
477 | }, | ||
478 | CompletionItem { | ||
479 | label: "while", | ||
480 | source_range: [97; 97), | ||
481 | delete: [97; 97), | ||
482 | insert: "while $0 {}", | ||
483 | kind: Keyword, | ||
484 | }, | ||
485 | ] | ||
486 | "### | ||
487 | ); | ||
488 | } | ||
489 | |||
490 | #[test] | ||
491 | fn last_return_in_block_has_semi() { | ||
492 | assert_debug_snapshot!( | ||
493 | do_keyword_completion( | ||
494 | r" | ||
495 | fn quux() -> i32 { | ||
496 | if condition { | ||
497 | <|> | ||
498 | } | ||
499 | } | ||
500 | ", | ||
501 | ), | ||
502 | @r###" | ||
503 | [ | ||
504 | CompletionItem { | ||
505 | label: "if", | ||
506 | source_range: [95; 95), | ||
507 | delete: [95; 95), | ||
508 | insert: "if $0 {}", | ||
509 | kind: Keyword, | ||
510 | }, | ||
511 | CompletionItem { | ||
512 | label: "loop", | ||
513 | source_range: [95; 95), | ||
514 | delete: [95; 95), | ||
515 | insert: "loop {$0}", | ||
516 | kind: Keyword, | ||
517 | }, | ||
518 | CompletionItem { | ||
519 | label: "match", | ||
520 | source_range: [95; 95), | ||
521 | delete: [95; 95), | ||
522 | insert: "match $0 {}", | ||
523 | kind: Keyword, | ||
524 | }, | ||
525 | CompletionItem { | ||
526 | label: "return", | ||
527 | source_range: [95; 95), | ||
528 | delete: [95; 95), | ||
529 | insert: "return $0;", | ||
530 | kind: Keyword, | ||
531 | }, | ||
532 | CompletionItem { | ||
533 | label: "while", | ||
534 | source_range: [95; 95), | ||
535 | delete: [95; 95), | ||
536 | insert: "while $0 {}", | ||
537 | kind: Keyword, | ||
538 | }, | ||
539 | ] | ||
540 | "### | ||
541 | ); | ||
542 | assert_debug_snapshot!( | ||
543 | do_keyword_completion( | ||
544 | r" | ||
545 | fn quux() -> i32 { | ||
546 | if condition { | ||
547 | <|> | ||
548 | } | ||
549 | let x = 92; | ||
550 | x | ||
551 | } | ||
552 | ", | ||
553 | ), | ||
554 | @r###" | ||
555 | [ | ||
556 | CompletionItem { | ||
557 | label: "if", | ||
558 | source_range: [95; 95), | ||
559 | delete: [95; 95), | ||
560 | insert: "if $0 {}", | ||
561 | kind: Keyword, | ||
562 | }, | ||
563 | CompletionItem { | ||
564 | label: "loop", | ||
565 | source_range: [95; 95), | ||
566 | delete: [95; 95), | ||
567 | insert: "loop {$0}", | ||
568 | kind: Keyword, | ||
569 | }, | ||
570 | CompletionItem { | ||
571 | label: "match", | ||
572 | source_range: [95; 95), | ||
573 | delete: [95; 95), | ||
574 | insert: "match $0 {}", | ||
575 | kind: Keyword, | ||
576 | }, | ||
577 | CompletionItem { | ||
578 | label: "return", | ||
579 | source_range: [95; 95), | ||
580 | delete: [95; 95), | ||
581 | insert: "return $0;", | ||
582 | kind: Keyword, | ||
583 | }, | ||
584 | CompletionItem { | ||
585 | label: "while", | ||
586 | source_range: [95; 95), | ||
587 | delete: [95; 95), | ||
588 | insert: "while $0 {}", | ||
589 | kind: Keyword, | ||
590 | }, | ||
591 | ] | ||
592 | "### | ||
593 | ); | ||
594 | } | ||
595 | |||
596 | #[test] | ||
597 | fn completes_break_and_continue_in_loops() { | ||
598 | assert_debug_snapshot!( | ||
599 | do_keyword_completion( | ||
600 | r" | ||
601 | fn quux() -> i32 { | ||
602 | loop { <|> } | ||
603 | } | ||
604 | ", | ||
605 | ), | ||
606 | @r###" | ||
607 | [ | ||
608 | CompletionItem { | ||
609 | label: "break", | ||
610 | source_range: [63; 63), | ||
611 | delete: [63; 63), | ||
612 | insert: "break;", | ||
613 | kind: Keyword, | ||
614 | }, | ||
615 | CompletionItem { | ||
616 | label: "continue", | ||
617 | source_range: [63; 63), | ||
618 | delete: [63; 63), | ||
619 | insert: "continue;", | ||
620 | kind: Keyword, | ||
621 | }, | ||
622 | CompletionItem { | ||
623 | label: "if", | ||
624 | source_range: [63; 63), | ||
625 | delete: [63; 63), | ||
626 | insert: "if $0 {}", | ||
627 | kind: Keyword, | ||
628 | }, | ||
629 | CompletionItem { | ||
630 | label: "loop", | ||
631 | source_range: [63; 63), | ||
632 | delete: [63; 63), | ||
633 | insert: "loop {$0}", | ||
634 | kind: Keyword, | ||
635 | }, | ||
636 | CompletionItem { | ||
637 | label: "match", | ||
638 | source_range: [63; 63), | ||
639 | delete: [63; 63), | ||
640 | insert: "match $0 {}", | ||
641 | kind: Keyword, | ||
642 | }, | ||
643 | CompletionItem { | ||
644 | label: "return", | ||
645 | source_range: [63; 63), | ||
646 | delete: [63; 63), | ||
647 | insert: "return $0;", | ||
648 | kind: Keyword, | ||
649 | }, | ||
650 | CompletionItem { | ||
651 | label: "while", | ||
652 | source_range: [63; 63), | ||
653 | delete: [63; 63), | ||
654 | insert: "while $0 {}", | ||
655 | kind: Keyword, | ||
656 | }, | ||
657 | ] | ||
658 | "### | ||
659 | ); | ||
660 | |||
661 | // No completion: lambda isolates control flow | ||
662 | assert_debug_snapshot!( | ||
663 | do_keyword_completion( | ||
664 | r" | ||
665 | fn quux() -> i32 { | ||
666 | loop { || { <|> } } | ||
667 | } | ||
668 | ", | ||
669 | ), | ||
670 | @r###" | ||
671 | [ | ||
672 | CompletionItem { | ||
673 | label: "if", | ||
674 | source_range: [68; 68), | ||
675 | delete: [68; 68), | ||
676 | insert: "if $0 {}", | ||
677 | kind: Keyword, | ||
678 | }, | ||
679 | CompletionItem { | ||
680 | label: "loop", | ||
681 | source_range: [68; 68), | ||
682 | delete: [68; 68), | ||
683 | insert: "loop {$0}", | ||
684 | kind: Keyword, | ||
685 | }, | ||
686 | CompletionItem { | ||
687 | label: "match", | ||
688 | source_range: [68; 68), | ||
689 | delete: [68; 68), | ||
690 | insert: "match $0 {}", | ||
691 | kind: Keyword, | ||
692 | }, | ||
693 | CompletionItem { | ||
694 | label: "return", | ||
695 | source_range: [68; 68), | ||
696 | delete: [68; 68), | ||
697 | insert: "return $0;", | ||
698 | kind: Keyword, | ||
699 | }, | ||
700 | CompletionItem { | ||
701 | label: "while", | ||
702 | source_range: [68; 68), | ||
703 | delete: [68; 68), | ||
704 | insert: "while $0 {}", | ||
705 | kind: Keyword, | ||
706 | }, | ||
707 | ] | ||
708 | "### | ||
709 | ); | ||
710 | } | ||
711 | |||
712 | #[test] | ||
713 | fn no_semi_after_break_continue_in_expr() { | ||
714 | assert_debug_snapshot!( | ||
715 | do_keyword_completion( | ||
716 | r" | ||
717 | fn f() { | ||
718 | loop { | ||
719 | match () { | ||
720 | () => br<|> | ||
721 | } | ||
722 | } | ||
723 | } | ||
724 | ", | ||
725 | ), | ||
726 | @r###" | ||
727 | [ | ||
728 | CompletionItem { | ||
729 | label: "break", | ||
730 | source_range: [122; 124), | ||
731 | delete: [122; 124), | ||
732 | insert: "break", | ||
733 | kind: Keyword, | ||
734 | }, | ||
735 | CompletionItem { | ||
736 | label: "continue", | ||
737 | source_range: [122; 124), | ||
738 | delete: [122; 124), | ||
739 | insert: "continue", | ||
740 | kind: Keyword, | ||
741 | }, | ||
742 | CompletionItem { | ||
743 | label: "if", | ||
744 | source_range: [122; 124), | ||
745 | delete: [122; 124), | ||
746 | insert: "if $0 {}", | ||
747 | kind: Keyword, | ||
748 | }, | ||
749 | CompletionItem { | ||
750 | label: "loop", | ||
751 | source_range: [122; 124), | ||
752 | delete: [122; 124), | ||
753 | insert: "loop {$0}", | ||
754 | kind: Keyword, | ||
755 | }, | ||
756 | CompletionItem { | ||
757 | label: "match", | ||
758 | source_range: [122; 124), | ||
759 | delete: [122; 124), | ||
760 | insert: "match $0 {}", | ||
761 | kind: Keyword, | ||
762 | }, | ||
763 | CompletionItem { | ||
764 | label: "return", | ||
765 | source_range: [122; 124), | ||
766 | delete: [122; 124), | ||
767 | insert: "return", | ||
768 | kind: Keyword, | ||
769 | }, | ||
770 | CompletionItem { | ||
771 | label: "while", | ||
772 | source_range: [122; 124), | ||
773 | delete: [122; 124), | ||
774 | insert: "while $0 {}", | ||
775 | kind: Keyword, | ||
776 | }, | ||
777 | ] | ||
778 | "### | ||
779 | ) | ||
780 | } | ||
781 | } | ||
diff --git a/crates/ra_ide_api/src/completion/complete_macro_in_item_position.rs b/crates/ra_ide_api/src/completion/complete_macro_in_item_position.rs deleted file mode 100644 index faadd1e3f..000000000 --- a/crates/ra_ide_api/src/completion/complete_macro_in_item_position.rs +++ /dev/null | |||
@@ -1,143 +0,0 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | use crate::completion::{CompletionContext, Completions}; | ||
4 | |||
5 | pub(super) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &CompletionContext) { | ||
6 | // Show only macros in top level. | ||
7 | if ctx.is_new_item { | ||
8 | ctx.analyzer.process_all_names(ctx.db, &mut |name, res| { | ||
9 | if let hir::ScopeDef::MacroDef(mac) = res { | ||
10 | acc.add_macro(ctx, Some(name.to_string()), mac); | ||
11 | } | ||
12 | }) | ||
13 | } | ||
14 | } | ||
15 | |||
16 | #[cfg(test)] | ||
17 | mod tests { | ||
18 | use crate::completion::{do_completion, CompletionItem, CompletionKind}; | ||
19 | use insta::assert_debug_snapshot; | ||
20 | |||
21 | fn do_reference_completion(code: &str) -> Vec<CompletionItem> { | ||
22 | do_completion(code, CompletionKind::Reference) | ||
23 | } | ||
24 | |||
25 | #[test] | ||
26 | fn completes_macros_as_item() { | ||
27 | assert_debug_snapshot!( | ||
28 | do_reference_completion( | ||
29 | " | ||
30 | //- /main.rs | ||
31 | macro_rules! foo { | ||
32 | () => {} | ||
33 | } | ||
34 | |||
35 | fn foo() {} | ||
36 | |||
37 | <|> | ||
38 | " | ||
39 | ), | ||
40 | @r###" | ||
41 | [ | ||
42 | CompletionItem { | ||
43 | label: "foo!", | ||
44 | source_range: [46; 46), | ||
45 | delete: [46; 46), | ||
46 | insert: "foo!($0)", | ||
47 | kind: Macro, | ||
48 | detail: "macro_rules! foo", | ||
49 | }, | ||
50 | ] | ||
51 | "### | ||
52 | ); | ||
53 | } | ||
54 | |||
55 | #[test] | ||
56 | fn completes_vec_macros_with_square_brackets() { | ||
57 | assert_debug_snapshot!( | ||
58 | do_reference_completion( | ||
59 | " | ||
60 | //- /main.rs | ||
61 | /// Creates a [`Vec`] containing the arguments. | ||
62 | /// | ||
63 | /// - Create a [`Vec`] containing a given list of elements: | ||
64 | /// | ||
65 | /// ``` | ||
66 | /// let v = vec![1, 2, 3]; | ||
67 | /// assert_eq!(v[0], 1); | ||
68 | /// assert_eq!(v[1], 2); | ||
69 | /// assert_eq!(v[2], 3); | ||
70 | /// ``` | ||
71 | macro_rules! vec { | ||
72 | () => {} | ||
73 | } | ||
74 | |||
75 | fn foo() {} | ||
76 | |||
77 | <|> | ||
78 | " | ||
79 | ), | ||
80 | @r###" | ||
81 | [ | ||
82 | CompletionItem { | ||
83 | label: "vec!", | ||
84 | source_range: [280; 280), | ||
85 | delete: [280; 280), | ||
86 | insert: "vec![$0]", | ||
87 | kind: Macro, | ||
88 | detail: "macro_rules! vec", | ||
89 | documentation: Documentation( | ||
90 | "Creates a [`Vec`] containing the arguments.\n\n- Create a [`Vec`] containing a given list of elements:\n\n```\nlet v = vec![1, 2, 3];\nassert_eq!(v[0], 1);\nassert_eq!(v[1], 2);\nassert_eq!(v[2], 3);\n```", | ||
91 | ), | ||
92 | }, | ||
93 | ] | ||
94 | "### | ||
95 | ); | ||
96 | } | ||
97 | |||
98 | #[test] | ||
99 | fn completes_macros_braces_guessing() { | ||
100 | assert_debug_snapshot!( | ||
101 | do_reference_completion( | ||
102 | " | ||
103 | //- /main.rs | ||
104 | /// Foo | ||
105 | /// | ||
106 | /// Not call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`. | ||
107 | /// Call as `let _=foo! { hello world };` | ||
108 | macro_rules! foo { | ||
109 | () => {} | ||
110 | } | ||
111 | |||
112 | fn main() { | ||
113 | <|> | ||
114 | } | ||
115 | " | ||
116 | ), | ||
117 | @r###" | ||
118 | [ | ||
119 | CompletionItem { | ||
120 | label: "foo!", | ||
121 | source_range: [163; 163), | ||
122 | delete: [163; 163), | ||
123 | insert: "foo! {$0}", | ||
124 | kind: Macro, | ||
125 | detail: "macro_rules! foo", | ||
126 | documentation: Documentation( | ||
127 | "Foo\n\nNot call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`.\nCall as `let _=foo! { hello world };`", | ||
128 | ), | ||
129 | }, | ||
130 | CompletionItem { | ||
131 | label: "main()", | ||
132 | source_range: [163; 163), | ||
133 | delete: [163; 163), | ||
134 | insert: "main()$0", | ||
135 | kind: Function, | ||
136 | lookup: "main", | ||
137 | detail: "fn main()", | ||
138 | }, | ||
139 | ] | ||
140 | "### | ||
141 | ); | ||
142 | } | ||
143 | } | ||
diff --git a/crates/ra_ide_api/src/completion/complete_path.rs b/crates/ra_ide_api/src/completion/complete_path.rs deleted file mode 100644 index 89e0009a1..000000000 --- a/crates/ra_ide_api/src/completion/complete_path.rs +++ /dev/null | |||
@@ -1,785 +0,0 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | use hir::{Adt, Either, HasSource, PathResolution}; | ||
4 | use ra_syntax::AstNode; | ||
5 | use test_utils::tested_by; | ||
6 | |||
7 | use crate::completion::{CompletionContext, Completions}; | ||
8 | |||
9 | pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { | ||
10 | let path = match &ctx.path_prefix { | ||
11 | Some(path) => path.clone(), | ||
12 | _ => return, | ||
13 | }; | ||
14 | let def = match ctx.analyzer.resolve_hir_path(ctx.db, &path) { | ||
15 | Some(PathResolution::Def(def)) => def, | ||
16 | _ => return, | ||
17 | }; | ||
18 | match def { | ||
19 | hir::ModuleDef::Module(module) => { | ||
20 | let module_scope = module.scope(ctx.db); | ||
21 | for (name, def, import) in module_scope { | ||
22 | if let hir::ScopeDef::ModuleDef(hir::ModuleDef::BuiltinType(..)) = def { | ||
23 | if ctx.use_item_syntax.is_some() { | ||
24 | tested_by!(dont_complete_primitive_in_use); | ||
25 | continue; | ||
26 | } | ||
27 | } | ||
28 | if Some(module) == ctx.module { | ||
29 | if let Some(import) = import { | ||
30 | if let Either::A(use_tree) = import.source(ctx.db).value { | ||
31 | if use_tree.syntax().text_range().contains_inclusive(ctx.offset) { | ||
32 | // for `use self::foo<|>`, don't suggest `foo` as a completion | ||
33 | tested_by!(dont_complete_current_use); | ||
34 | continue; | ||
35 | } | ||
36 | } | ||
37 | } | ||
38 | } | ||
39 | acc.add_resolution(ctx, name.to_string(), &def); | ||
40 | } | ||
41 | } | ||
42 | hir::ModuleDef::Adt(_) | hir::ModuleDef::TypeAlias(_) => { | ||
43 | if let hir::ModuleDef::Adt(Adt::Enum(e)) = def { | ||
44 | for variant in e.variants(ctx.db) { | ||
45 | acc.add_enum_variant(ctx, variant); | ||
46 | } | ||
47 | } | ||
48 | let ty = match def { | ||
49 | hir::ModuleDef::Adt(adt) => adt.ty(ctx.db), | ||
50 | hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db), | ||
51 | _ => unreachable!(), | ||
52 | }; | ||
53 | ctx.analyzer.iterate_path_candidates(ctx.db, &ty, None, |_ty, item| { | ||
54 | match item { | ||
55 | hir::AssocItem::Function(func) => { | ||
56 | if !func.has_self_param(ctx.db) { | ||
57 | acc.add_function(ctx, func); | ||
58 | } | ||
59 | } | ||
60 | hir::AssocItem::Const(ct) => acc.add_const(ctx, ct), | ||
61 | hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty), | ||
62 | } | ||
63 | None::<()> | ||
64 | }); | ||
65 | // Iterate assoc types separately | ||
66 | // FIXME: complete T::AssocType | ||
67 | let krate = ctx.module.map(|m| m.krate()); | ||
68 | if let Some(krate) = krate { | ||
69 | ty.iterate_impl_items(ctx.db, krate, |item| { | ||
70 | match item { | ||
71 | hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => {} | ||
72 | hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty), | ||
73 | } | ||
74 | None::<()> | ||
75 | }); | ||
76 | } | ||
77 | } | ||
78 | hir::ModuleDef::Trait(t) => { | ||
79 | for item in t.items(ctx.db) { | ||
80 | match item { | ||
81 | hir::AssocItem::Function(func) => { | ||
82 | if !func.has_self_param(ctx.db) { | ||
83 | acc.add_function(ctx, func); | ||
84 | } | ||
85 | } | ||
86 | hir::AssocItem::Const(ct) => acc.add_const(ctx, ct), | ||
87 | hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty), | ||
88 | } | ||
89 | } | ||
90 | } | ||
91 | _ => {} | ||
92 | }; | ||
93 | } | ||
94 | |||
95 | #[cfg(test)] | ||
96 | mod tests { | ||
97 | use test_utils::covers; | ||
98 | |||
99 | use crate::completion::{do_completion, CompletionItem, CompletionKind}; | ||
100 | use insta::assert_debug_snapshot; | ||
101 | |||
102 | fn do_reference_completion(code: &str) -> Vec<CompletionItem> { | ||
103 | do_completion(code, CompletionKind::Reference) | ||
104 | } | ||
105 | |||
106 | #[test] | ||
107 | fn dont_complete_current_use() { | ||
108 | covers!(dont_complete_current_use); | ||
109 | let completions = do_completion(r"use self::foo<|>;", CompletionKind::Reference); | ||
110 | assert!(completions.is_empty()); | ||
111 | } | ||
112 | |||
113 | #[test] | ||
114 | fn dont_complete_current_use_in_braces_with_glob() { | ||
115 | let completions = do_completion( | ||
116 | r" | ||
117 | mod foo { pub struct S; } | ||
118 | use self::{foo::*, bar<|>}; | ||
119 | ", | ||
120 | CompletionKind::Reference, | ||
121 | ); | ||
122 | assert_eq!(completions.len(), 2); | ||
123 | } | ||
124 | |||
125 | #[test] | ||
126 | fn dont_complete_primitive_in_use() { | ||
127 | covers!(dont_complete_primitive_in_use); | ||
128 | let completions = do_completion(r"use self::<|>;", CompletionKind::BuiltinType); | ||
129 | assert!(completions.is_empty()); | ||
130 | } | ||
131 | |||
132 | #[test] | ||
133 | fn completes_primitives() { | ||
134 | let completions = | ||
135 | do_completion(r"fn main() { let _: <|> = 92; }", CompletionKind::BuiltinType); | ||
136 | assert_eq!(completions.len(), 17); | ||
137 | } | ||
138 | |||
139 | #[test] | ||
140 | fn completes_mod_with_docs() { | ||
141 | assert_debug_snapshot!( | ||
142 | do_reference_completion( | ||
143 | r" | ||
144 | use self::my<|>; | ||
145 | |||
146 | /// Some simple | ||
147 | /// docs describing `mod my`. | ||
148 | mod my { | ||
149 | struct Bar; | ||
150 | } | ||
151 | " | ||
152 | ), | ||
153 | @r###" | ||
154 | [ | ||
155 | CompletionItem { | ||
156 | label: "my", | ||
157 | source_range: [27; 29), | ||
158 | delete: [27; 29), | ||
159 | insert: "my", | ||
160 | kind: Module, | ||
161 | documentation: Documentation( | ||
162 | "Some simple\ndocs describing `mod my`.", | ||
163 | ), | ||
164 | }, | ||
165 | ] | ||
166 | "### | ||
167 | ); | ||
168 | } | ||
169 | |||
170 | #[test] | ||
171 | fn completes_use_item_starting_with_self() { | ||
172 | assert_debug_snapshot!( | ||
173 | do_reference_completion( | ||
174 | r" | ||
175 | use self::m::<|>; | ||
176 | |||
177 | mod m { | ||
178 | struct Bar; | ||
179 | } | ||
180 | " | ||
181 | ), | ||
182 | @r###" | ||
183 | [ | ||
184 | CompletionItem { | ||
185 | label: "Bar", | ||
186 | source_range: [30; 30), | ||
187 | delete: [30; 30), | ||
188 | insert: "Bar", | ||
189 | kind: Struct, | ||
190 | }, | ||
191 | ] | ||
192 | "### | ||
193 | ); | ||
194 | } | ||
195 | |||
196 | #[test] | ||
197 | fn completes_use_item_starting_with_crate() { | ||
198 | assert_debug_snapshot!( | ||
199 | do_reference_completion( | ||
200 | " | ||
201 | //- /lib.rs | ||
202 | mod foo; | ||
203 | struct Spam; | ||
204 | //- /foo.rs | ||
205 | use crate::Sp<|> | ||
206 | " | ||
207 | ), | ||
208 | @r###" | ||
209 | [ | ||
210 | CompletionItem { | ||
211 | label: "Spam", | ||
212 | source_range: [11; 13), | ||
213 | delete: [11; 13), | ||
214 | insert: "Spam", | ||
215 | kind: Struct, | ||
216 | }, | ||
217 | CompletionItem { | ||
218 | label: "foo", | ||
219 | source_range: [11; 13), | ||
220 | delete: [11; 13), | ||
221 | insert: "foo", | ||
222 | kind: Module, | ||
223 | }, | ||
224 | ] | ||
225 | "### | ||
226 | ); | ||
227 | } | ||
228 | |||
229 | #[test] | ||
230 | fn completes_nested_use_tree() { | ||
231 | assert_debug_snapshot!( | ||
232 | do_reference_completion( | ||
233 | " | ||
234 | //- /lib.rs | ||
235 | mod foo; | ||
236 | struct Spam; | ||
237 | //- /foo.rs | ||
238 | use crate::{Sp<|>}; | ||
239 | " | ||
240 | ), | ||
241 | @r###" | ||
242 | [ | ||
243 | CompletionItem { | ||
244 | label: "Spam", | ||
245 | source_range: [12; 14), | ||
246 | delete: [12; 14), | ||
247 | insert: "Spam", | ||
248 | kind: Struct, | ||
249 | }, | ||
250 | CompletionItem { | ||
251 | label: "foo", | ||
252 | source_range: [12; 14), | ||
253 | delete: [12; 14), | ||
254 | insert: "foo", | ||
255 | kind: Module, | ||
256 | }, | ||
257 | ] | ||
258 | "### | ||
259 | ); | ||
260 | } | ||
261 | |||
262 | #[test] | ||
263 | fn completes_deeply_nested_use_tree() { | ||
264 | assert_debug_snapshot!( | ||
265 | do_reference_completion( | ||
266 | " | ||
267 | //- /lib.rs | ||
268 | mod foo; | ||
269 | pub mod bar { | ||
270 | pub mod baz { | ||
271 | pub struct Spam; | ||
272 | } | ||
273 | } | ||
274 | //- /foo.rs | ||
275 | use crate::{bar::{baz::Sp<|>}}; | ||
276 | " | ||
277 | ), | ||
278 | @r###" | ||
279 | [ | ||
280 | CompletionItem { | ||
281 | label: "Spam", | ||
282 | source_range: [23; 25), | ||
283 | delete: [23; 25), | ||
284 | insert: "Spam", | ||
285 | kind: Struct, | ||
286 | }, | ||
287 | ] | ||
288 | "### | ||
289 | ); | ||
290 | } | ||
291 | |||
292 | #[test] | ||
293 | fn completes_enum_variant() { | ||
294 | assert_debug_snapshot!( | ||
295 | do_reference_completion( | ||
296 | " | ||
297 | //- /lib.rs | ||
298 | /// An enum | ||
299 | enum E { | ||
300 | /// Foo Variant | ||
301 | Foo, | ||
302 | /// Bar Variant with i32 | ||
303 | Bar(i32) | ||
304 | } | ||
305 | fn foo() { let _ = E::<|> } | ||
306 | " | ||
307 | ), | ||
308 | @r###" | ||
309 | [ | ||
310 | CompletionItem { | ||
311 | label: "Bar", | ||
312 | source_range: [116; 116), | ||
313 | delete: [116; 116), | ||
314 | insert: "Bar", | ||
315 | kind: EnumVariant, | ||
316 | detail: "(i32)", | ||
317 | documentation: Documentation( | ||
318 | "Bar Variant with i32", | ||
319 | ), | ||
320 | }, | ||
321 | CompletionItem { | ||
322 | label: "Foo", | ||
323 | source_range: [116; 116), | ||
324 | delete: [116; 116), | ||
325 | insert: "Foo", | ||
326 | kind: EnumVariant, | ||
327 | detail: "()", | ||
328 | documentation: Documentation( | ||
329 | "Foo Variant", | ||
330 | ), | ||
331 | }, | ||
332 | ] | ||
333 | "### | ||
334 | ); | ||
335 | } | ||
336 | |||
337 | #[test] | ||
338 | fn completes_enum_variant_with_details() { | ||
339 | assert_debug_snapshot!( | ||
340 | do_reference_completion( | ||
341 | " | ||
342 | //- /lib.rs | ||
343 | struct S { field: u32 } | ||
344 | /// An enum | ||
345 | enum E { | ||
346 | /// Foo Variant (empty) | ||
347 | Foo, | ||
348 | /// Bar Variant with i32 and u32 | ||
349 | Bar(i32, u32), | ||
350 | /// | ||
351 | S(S), | ||
352 | } | ||
353 | fn foo() { let _ = E::<|> } | ||
354 | " | ||
355 | ), | ||
356 | @r###" | ||
357 | [ | ||
358 | CompletionItem { | ||
359 | label: "Bar", | ||
360 | source_range: [180; 180), | ||
361 | delete: [180; 180), | ||
362 | insert: "Bar", | ||
363 | kind: EnumVariant, | ||
364 | detail: "(i32, u32)", | ||
365 | documentation: Documentation( | ||
366 | "Bar Variant with i32 and u32", | ||
367 | ), | ||
368 | }, | ||
369 | CompletionItem { | ||
370 | label: "Foo", | ||
371 | source_range: [180; 180), | ||
372 | delete: [180; 180), | ||
373 | insert: "Foo", | ||
374 | kind: EnumVariant, | ||
375 | detail: "()", | ||
376 | documentation: Documentation( | ||
377 | "Foo Variant (empty)", | ||
378 | ), | ||
379 | }, | ||
380 | CompletionItem { | ||
381 | label: "S", | ||
382 | source_range: [180; 180), | ||
383 | delete: [180; 180), | ||
384 | insert: "S", | ||
385 | kind: EnumVariant, | ||
386 | detail: "(S)", | ||
387 | documentation: Documentation( | ||
388 | "", | ||
389 | ), | ||
390 | }, | ||
391 | ] | ||
392 | "### | ||
393 | ); | ||
394 | } | ||
395 | |||
396 | #[test] | ||
397 | fn completes_struct_associated_method() { | ||
398 | assert_debug_snapshot!( | ||
399 | do_reference_completion( | ||
400 | " | ||
401 | //- /lib.rs | ||
402 | /// A Struct | ||
403 | struct S; | ||
404 | |||
405 | impl S { | ||
406 | /// An associated method | ||
407 | fn m() { } | ||
408 | } | ||
409 | |||
410 | fn foo() { let _ = S::<|> } | ||
411 | " | ||
412 | ), | ||
413 | @r###" | ||
414 | [ | ||
415 | CompletionItem { | ||
416 | label: "m()", | ||
417 | source_range: [100; 100), | ||
418 | delete: [100; 100), | ||
419 | insert: "m()$0", | ||
420 | kind: Function, | ||
421 | lookup: "m", | ||
422 | detail: "fn m()", | ||
423 | documentation: Documentation( | ||
424 | "An associated method", | ||
425 | ), | ||
426 | }, | ||
427 | ] | ||
428 | "### | ||
429 | ); | ||
430 | } | ||
431 | |||
432 | #[test] | ||
433 | fn completes_struct_associated_const() { | ||
434 | assert_debug_snapshot!( | ||
435 | do_reference_completion( | ||
436 | " | ||
437 | //- /lib.rs | ||
438 | /// A Struct | ||
439 | struct S; | ||
440 | |||
441 | impl S { | ||
442 | /// An associated const | ||
443 | const C: i32 = 42; | ||
444 | } | ||
445 | |||
446 | fn foo() { let _ = S::<|> } | ||
447 | " | ||
448 | ), | ||
449 | @r###" | ||
450 | [ | ||
451 | CompletionItem { | ||
452 | label: "C", | ||
453 | source_range: [107; 107), | ||
454 | delete: [107; 107), | ||
455 | insert: "C", | ||
456 | kind: Const, | ||
457 | detail: "const C: i32 = 42;", | ||
458 | documentation: Documentation( | ||
459 | "An associated const", | ||
460 | ), | ||
461 | }, | ||
462 | ] | ||
463 | "### | ||
464 | ); | ||
465 | } | ||
466 | |||
467 | #[test] | ||
468 | fn completes_struct_associated_type() { | ||
469 | assert_debug_snapshot!( | ||
470 | do_reference_completion( | ||
471 | " | ||
472 | //- /lib.rs | ||
473 | /// A Struct | ||
474 | struct S; | ||
475 | |||
476 | impl S { | ||
477 | /// An associated type | ||
478 | type T = i32; | ||
479 | } | ||
480 | |||
481 | fn foo() { let _ = S::<|> } | ||
482 | " | ||
483 | ), | ||
484 | @r###" | ||
485 | [ | ||
486 | CompletionItem { | ||
487 | label: "T", | ||
488 | source_range: [101; 101), | ||
489 | delete: [101; 101), | ||
490 | insert: "T", | ||
491 | kind: TypeAlias, | ||
492 | detail: "type T = i32;", | ||
493 | documentation: Documentation( | ||
494 | "An associated type", | ||
495 | ), | ||
496 | }, | ||
497 | ] | ||
498 | "### | ||
499 | ); | ||
500 | } | ||
501 | |||
502 | #[test] | ||
503 | fn completes_enum_associated_method() { | ||
504 | assert_debug_snapshot!( | ||
505 | do_reference_completion( | ||
506 | " | ||
507 | //- /lib.rs | ||
508 | /// An enum | ||
509 | enum S {}; | ||
510 | |||
511 | impl S { | ||
512 | /// An associated method | ||
513 | fn m() { } | ||
514 | } | ||
515 | |||
516 | fn foo() { let _ = S::<|> } | ||
517 | " | ||
518 | ), | ||
519 | @r###" | ||
520 | [ | ||
521 | CompletionItem { | ||
522 | label: "m()", | ||
523 | source_range: [100; 100), | ||
524 | delete: [100; 100), | ||
525 | insert: "m()$0", | ||
526 | kind: Function, | ||
527 | lookup: "m", | ||
528 | detail: "fn m()", | ||
529 | documentation: Documentation( | ||
530 | "An associated method", | ||
531 | ), | ||
532 | }, | ||
533 | ] | ||
534 | "### | ||
535 | ); | ||
536 | } | ||
537 | |||
538 | #[test] | ||
539 | fn completes_union_associated_method() { | ||
540 | assert_debug_snapshot!( | ||
541 | do_reference_completion( | ||
542 | " | ||
543 | //- /lib.rs | ||
544 | /// A union | ||
545 | union U {}; | ||
546 | |||
547 | impl U { | ||
548 | /// An associated method | ||
549 | fn m() { } | ||
550 | } | ||
551 | |||
552 | fn foo() { let _ = U::<|> } | ||
553 | " | ||
554 | ), | ||
555 | @r###" | ||
556 | [ | ||
557 | CompletionItem { | ||
558 | label: "m()", | ||
559 | source_range: [101; 101), | ||
560 | delete: [101; 101), | ||
561 | insert: "m()$0", | ||
562 | kind: Function, | ||
563 | lookup: "m", | ||
564 | detail: "fn m()", | ||
565 | documentation: Documentation( | ||
566 | "An associated method", | ||
567 | ), | ||
568 | }, | ||
569 | ] | ||
570 | "### | ||
571 | ); | ||
572 | } | ||
573 | |||
574 | #[test] | ||
575 | fn completes_use_paths_across_crates() { | ||
576 | assert_debug_snapshot!( | ||
577 | do_reference_completion( | ||
578 | " | ||
579 | //- /main.rs | ||
580 | use foo::<|>; | ||
581 | |||
582 | //- /foo/lib.rs | ||
583 | pub mod bar { | ||
584 | pub struct S; | ||
585 | } | ||
586 | " | ||
587 | ), | ||
588 | @r###" | ||
589 | [ | ||
590 | CompletionItem { | ||
591 | label: "bar", | ||
592 | source_range: [9; 9), | ||
593 | delete: [9; 9), | ||
594 | insert: "bar", | ||
595 | kind: Module, | ||
596 | }, | ||
597 | ] | ||
598 | "### | ||
599 | ); | ||
600 | } | ||
601 | |||
602 | #[test] | ||
603 | fn completes_trait_associated_method_1() { | ||
604 | assert_debug_snapshot!( | ||
605 | do_reference_completion( | ||
606 | " | ||
607 | //- /lib.rs | ||
608 | trait Trait { | ||
609 | /// A trait method | ||
610 | fn m(); | ||
611 | } | ||
612 | |||
613 | fn foo() { let _ = Trait::<|> } | ||
614 | " | ||
615 | ), | ||
616 | @r###" | ||
617 | [ | ||
618 | CompletionItem { | ||
619 | label: "m()", | ||
620 | source_range: [73; 73), | ||
621 | delete: [73; 73), | ||
622 | insert: "m()$0", | ||
623 | kind: Function, | ||
624 | lookup: "m", | ||
625 | detail: "fn m()", | ||
626 | documentation: Documentation( | ||
627 | "A trait method", | ||
628 | ), | ||
629 | }, | ||
630 | ] | ||
631 | "### | ||
632 | ); | ||
633 | } | ||
634 | |||
635 | #[test] | ||
636 | fn completes_trait_associated_method_2() { | ||
637 | assert_debug_snapshot!( | ||
638 | do_reference_completion( | ||
639 | " | ||
640 | //- /lib.rs | ||
641 | trait Trait { | ||
642 | /// A trait method | ||
643 | fn m(); | ||
644 | } | ||
645 | |||
646 | struct S; | ||
647 | impl Trait for S {} | ||
648 | |||
649 | fn foo() { let _ = S::<|> } | ||
650 | " | ||
651 | ), | ||
652 | @r###" | ||
653 | [ | ||
654 | CompletionItem { | ||
655 | label: "m()", | ||
656 | source_range: [99; 99), | ||
657 | delete: [99; 99), | ||
658 | insert: "m()$0", | ||
659 | kind: Function, | ||
660 | lookup: "m", | ||
661 | detail: "fn m()", | ||
662 | documentation: Documentation( | ||
663 | "A trait method", | ||
664 | ), | ||
665 | }, | ||
666 | ] | ||
667 | "### | ||
668 | ); | ||
669 | } | ||
670 | |||
671 | #[test] | ||
672 | fn completes_trait_associated_method_3() { | ||
673 | assert_debug_snapshot!( | ||
674 | do_reference_completion( | ||
675 | " | ||
676 | //- /lib.rs | ||
677 | trait Trait { | ||
678 | /// A trait method | ||
679 | fn m(); | ||
680 | } | ||
681 | |||
682 | struct S; | ||
683 | impl Trait for S {} | ||
684 | |||
685 | fn foo() { let _ = <S as Trait>::<|> } | ||
686 | " | ||
687 | ), | ||
688 | @r###" | ||
689 | [ | ||
690 | CompletionItem { | ||
691 | label: "m()", | ||
692 | source_range: [110; 110), | ||
693 | delete: [110; 110), | ||
694 | insert: "m()$0", | ||
695 | kind: Function, | ||
696 | lookup: "m", | ||
697 | detail: "fn m()", | ||
698 | documentation: Documentation( | ||
699 | "A trait method", | ||
700 | ), | ||
701 | }, | ||
702 | ] | ||
703 | "### | ||
704 | ); | ||
705 | } | ||
706 | |||
707 | #[test] | ||
708 | fn completes_type_alias() { | ||
709 | assert_debug_snapshot!( | ||
710 | do_reference_completion( | ||
711 | " | ||
712 | struct S; | ||
713 | impl S { fn foo() {} } | ||
714 | type T = S; | ||
715 | impl T { fn bar() {} } | ||
716 | |||
717 | fn main() { | ||
718 | T::<|>; | ||
719 | } | ||
720 | " | ||
721 | ), | ||
722 | @r###" | ||
723 | [ | ||
724 | CompletionItem { | ||
725 | label: "bar()", | ||
726 | source_range: [185; 185), | ||
727 | delete: [185; 185), | ||
728 | insert: "bar()$0", | ||
729 | kind: Function, | ||
730 | lookup: "bar", | ||
731 | detail: "fn bar()", | ||
732 | }, | ||
733 | CompletionItem { | ||
734 | label: "foo()", | ||
735 | source_range: [185; 185), | ||
736 | delete: [185; 185), | ||
737 | insert: "foo()$0", | ||
738 | kind: Function, | ||
739 | lookup: "foo", | ||
740 | detail: "fn foo()", | ||
741 | }, | ||
742 | ] | ||
743 | "### | ||
744 | ); | ||
745 | } | ||
746 | |||
747 | #[test] | ||
748 | fn completes_qualified_macros() { | ||
749 | assert_debug_snapshot!( | ||
750 | do_reference_completion( | ||
751 | " | ||
752 | #[macro_export] | ||
753 | macro_rules! foo { | ||
754 | () => {} | ||
755 | } | ||
756 | |||
757 | fn main() { | ||
758 | let _ = crate::<|> | ||
759 | } | ||
760 | " | ||
761 | ), | ||
762 | @r###" | ||
763 | [ | ||
764 | CompletionItem { | ||
765 | label: "foo!", | ||
766 | source_range: [179; 179), | ||
767 | delete: [179; 179), | ||
768 | insert: "foo!($0)", | ||
769 | kind: Macro, | ||
770 | detail: "#[macro_export]\nmacro_rules! foo", | ||
771 | }, | ||
772 | CompletionItem { | ||
773 | label: "main()", | ||
774 | source_range: [179; 179), | ||
775 | delete: [179; 179), | ||
776 | insert: "main()$0", | ||
777 | kind: Function, | ||
778 | lookup: "main", | ||
779 | detail: "fn main()", | ||
780 | }, | ||
781 | ] | ||
782 | "### | ||
783 | ); | ||
784 | } | ||
785 | } | ||
diff --git a/crates/ra_ide_api/src/completion/complete_pattern.rs b/crates/ra_ide_api/src/completion/complete_pattern.rs deleted file mode 100644 index fd03b1c40..000000000 --- a/crates/ra_ide_api/src/completion/complete_pattern.rs +++ /dev/null | |||
@@ -1,89 +0,0 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | use crate::completion::{CompletionContext, Completions}; | ||
4 | |||
5 | /// Completes constats and paths in patterns. | ||
6 | pub(super) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { | ||
7 | if !ctx.is_pat_binding { | ||
8 | return; | ||
9 | } | ||
10 | // FIXME: ideally, we should look at the type we are matching against and | ||
11 | // suggest variants + auto-imports | ||
12 | ctx.analyzer.process_all_names(ctx.db, &mut |name, res| { | ||
13 | let def = match &res { | ||
14 | hir::ScopeDef::ModuleDef(def) => def, | ||
15 | _ => return, | ||
16 | }; | ||
17 | match def { | ||
18 | hir::ModuleDef::Adt(hir::Adt::Enum(..)) | ||
19 | | hir::ModuleDef::EnumVariant(..) | ||
20 | | hir::ModuleDef::Const(..) | ||
21 | | hir::ModuleDef::Module(..) => (), | ||
22 | _ => return, | ||
23 | } | ||
24 | acc.add_resolution(ctx, name.to_string(), &res) | ||
25 | }); | ||
26 | } | ||
27 | |||
28 | #[cfg(test)] | ||
29 | mod tests { | ||
30 | use crate::completion::{do_completion, CompletionItem, CompletionKind}; | ||
31 | use insta::assert_debug_snapshot; | ||
32 | |||
33 | fn complete(code: &str) -> Vec<CompletionItem> { | ||
34 | do_completion(code, CompletionKind::Reference) | ||
35 | } | ||
36 | |||
37 | #[test] | ||
38 | fn completes_enum_variants_and_modules() { | ||
39 | let completions = complete( | ||
40 | r" | ||
41 | enum E { X } | ||
42 | use self::E::X; | ||
43 | const Z: E = E::X; | ||
44 | mod m {} | ||
45 | |||
46 | static FOO: E = E::X; | ||
47 | struct Bar { f: u32 } | ||
48 | |||
49 | fn foo() { | ||
50 | match E::X { | ||
51 | <|> | ||
52 | } | ||
53 | } | ||
54 | ", | ||
55 | ); | ||
56 | assert_debug_snapshot!(completions, @r###" | ||
57 | [ | ||
58 | CompletionItem { | ||
59 | label: "E", | ||
60 | source_range: [246; 246), | ||
61 | delete: [246; 246), | ||
62 | insert: "E", | ||
63 | kind: Enum, | ||
64 | }, | ||
65 | CompletionItem { | ||
66 | label: "X", | ||
67 | source_range: [246; 246), | ||
68 | delete: [246; 246), | ||
69 | insert: "X", | ||
70 | kind: EnumVariant, | ||
71 | }, | ||
72 | CompletionItem { | ||
73 | label: "Z", | ||
74 | source_range: [246; 246), | ||
75 | delete: [246; 246), | ||
76 | insert: "Z", | ||
77 | kind: Const, | ||
78 | }, | ||
79 | CompletionItem { | ||
80 | label: "m", | ||
81 | source_range: [246; 246), | ||
82 | delete: [246; 246), | ||
83 | insert: "m", | ||
84 | kind: Module, | ||
85 | }, | ||
86 | ] | ||
87 | "###); | ||
88 | } | ||
89 | } | ||
diff --git a/crates/ra_ide_api/src/completion/complete_postfix.rs b/crates/ra_ide_api/src/completion/complete_postfix.rs deleted file mode 100644 index 646a30c76..000000000 --- a/crates/ra_ide_api/src/completion/complete_postfix.rs +++ /dev/null | |||
@@ -1,282 +0,0 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | use ra_syntax::{ast::AstNode, TextRange, TextUnit}; | ||
4 | use ra_text_edit::TextEdit; | ||
5 | |||
6 | use crate::{ | ||
7 | completion::{ | ||
8 | completion_context::CompletionContext, | ||
9 | completion_item::{Builder, CompletionKind, Completions}, | ||
10 | }, | ||
11 | CompletionItem, | ||
12 | }; | ||
13 | |||
14 | pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | ||
15 | if ctx.db.feature_flags.get("completion.enable-postfix") == false { | ||
16 | return; | ||
17 | } | ||
18 | |||
19 | let dot_receiver = match &ctx.dot_receiver { | ||
20 | Some(it) => it, | ||
21 | None => return, | ||
22 | }; | ||
23 | |||
24 | let receiver_text = if ctx.dot_receiver_is_ambiguous_float_literal { | ||
25 | let text = dot_receiver.syntax().text(); | ||
26 | let without_dot = ..text.len() - TextUnit::of_char('.'); | ||
27 | text.slice(without_dot).to_string() | ||
28 | } else { | ||
29 | dot_receiver.syntax().text().to_string() | ||
30 | }; | ||
31 | |||
32 | let receiver_ty = match ctx.analyzer.type_of(ctx.db, &dot_receiver) { | ||
33 | Some(it) => it, | ||
34 | None => return, | ||
35 | }; | ||
36 | |||
37 | if receiver_ty.is_bool() || receiver_ty.is_unknown() { | ||
38 | postfix_snippet(ctx, "if", "if expr {}", &format!("if {} {{$0}}", receiver_text)) | ||
39 | .add_to(acc); | ||
40 | postfix_snippet( | ||
41 | ctx, | ||
42 | "while", | ||
43 | "while expr {}", | ||
44 | &format!("while {} {{\n$0\n}}", receiver_text), | ||
45 | ) | ||
46 | .add_to(acc); | ||
47 | } | ||
48 | |||
49 | postfix_snippet(ctx, "not", "!expr", &format!("!{}", receiver_text)).add_to(acc); | ||
50 | |||
51 | postfix_snippet(ctx, "ref", "&expr", &format!("&{}", receiver_text)).add_to(acc); | ||
52 | postfix_snippet(ctx, "refm", "&mut expr", &format!("&mut {}", receiver_text)).add_to(acc); | ||
53 | |||
54 | postfix_snippet( | ||
55 | ctx, | ||
56 | "match", | ||
57 | "match expr {}", | ||
58 | &format!("match {} {{\n ${{1:_}} => {{$0\\}},\n}}", receiver_text), | ||
59 | ) | ||
60 | .add_to(acc); | ||
61 | |||
62 | postfix_snippet(ctx, "dbg", "dbg!(expr)", &format!("dbg!({})", receiver_text)).add_to(acc); | ||
63 | |||
64 | postfix_snippet(ctx, "box", "Box::new(expr)", &format!("Box::new({})", receiver_text)) | ||
65 | .add_to(acc); | ||
66 | } | ||
67 | |||
68 | fn postfix_snippet(ctx: &CompletionContext, label: &str, detail: &str, snippet: &str) -> Builder { | ||
69 | let edit = { | ||
70 | let receiver_range = | ||
71 | ctx.dot_receiver.as_ref().expect("no receiver available").syntax().text_range(); | ||
72 | let delete_range = TextRange::from_to(receiver_range.start(), ctx.source_range().end()); | ||
73 | TextEdit::replace(delete_range, snippet.to_string()) | ||
74 | }; | ||
75 | CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label) | ||
76 | .detail(detail) | ||
77 | .snippet_edit(edit) | ||
78 | } | ||
79 | |||
80 | #[cfg(test)] | ||
81 | mod tests { | ||
82 | use insta::assert_debug_snapshot; | ||
83 | |||
84 | use crate::completion::{do_completion, CompletionItem, CompletionKind}; | ||
85 | |||
86 | fn do_postfix_completion(code: &str) -> Vec<CompletionItem> { | ||
87 | do_completion(code, CompletionKind::Postfix) | ||
88 | } | ||
89 | |||
90 | #[test] | ||
91 | fn postfix_completion_works_for_trivial_path_expression() { | ||
92 | assert_debug_snapshot!( | ||
93 | do_postfix_completion( | ||
94 | r#" | ||
95 | fn main() { | ||
96 | let bar = true; | ||
97 | bar.<|> | ||
98 | } | ||
99 | "#, | ||
100 | ), | ||
101 | @r###" | ||
102 | [ | ||
103 | CompletionItem { | ||
104 | label: "box", | ||
105 | source_range: [89; 89), | ||
106 | delete: [85; 89), | ||
107 | insert: "Box::new(bar)", | ||
108 | detail: "Box::new(expr)", | ||
109 | }, | ||
110 | CompletionItem { | ||
111 | label: "dbg", | ||
112 | source_range: [89; 89), | ||
113 | delete: [85; 89), | ||
114 | insert: "dbg!(bar)", | ||
115 | detail: "dbg!(expr)", | ||
116 | }, | ||
117 | CompletionItem { | ||
118 | label: "if", | ||
119 | source_range: [89; 89), | ||
120 | delete: [85; 89), | ||
121 | insert: "if bar {$0}", | ||
122 | detail: "if expr {}", | ||
123 | }, | ||
124 | CompletionItem { | ||
125 | label: "match", | ||
126 | source_range: [89; 89), | ||
127 | delete: [85; 89), | ||
128 | insert: "match bar {\n ${1:_} => {$0\\},\n}", | ||
129 | detail: "match expr {}", | ||
130 | }, | ||
131 | CompletionItem { | ||
132 | label: "not", | ||
133 | source_range: [89; 89), | ||
134 | delete: [85; 89), | ||
135 | insert: "!bar", | ||
136 | detail: "!expr", | ||
137 | }, | ||
138 | CompletionItem { | ||
139 | label: "ref", | ||
140 | source_range: [89; 89), | ||
141 | delete: [85; 89), | ||
142 | insert: "&bar", | ||
143 | detail: "&expr", | ||
144 | }, | ||
145 | CompletionItem { | ||
146 | label: "refm", | ||
147 | source_range: [89; 89), | ||
148 | delete: [85; 89), | ||
149 | insert: "&mut bar", | ||
150 | detail: "&mut expr", | ||
151 | }, | ||
152 | CompletionItem { | ||
153 | label: "while", | ||
154 | source_range: [89; 89), | ||
155 | delete: [85; 89), | ||
156 | insert: "while bar {\n$0\n}", | ||
157 | detail: "while expr {}", | ||
158 | }, | ||
159 | ] | ||
160 | "### | ||
161 | ); | ||
162 | } | ||
163 | |||
164 | #[test] | ||
165 | fn some_postfix_completions_ignored() { | ||
166 | assert_debug_snapshot!( | ||
167 | do_postfix_completion( | ||
168 | r#" | ||
169 | fn main() { | ||
170 | let bar: u8 = 12; | ||
171 | bar.<|> | ||
172 | } | ||
173 | "#, | ||
174 | ), | ||
175 | @r###" | ||
176 | [ | ||
177 | CompletionItem { | ||
178 | label: "box", | ||
179 | source_range: [91; 91), | ||
180 | delete: [87; 91), | ||
181 | insert: "Box::new(bar)", | ||
182 | detail: "Box::new(expr)", | ||
183 | }, | ||
184 | CompletionItem { | ||
185 | label: "dbg", | ||
186 | source_range: [91; 91), | ||
187 | delete: [87; 91), | ||
188 | insert: "dbg!(bar)", | ||
189 | detail: "dbg!(expr)", | ||
190 | }, | ||
191 | CompletionItem { | ||
192 | label: "match", | ||
193 | source_range: [91; 91), | ||
194 | delete: [87; 91), | ||
195 | insert: "match bar {\n ${1:_} => {$0\\},\n}", | ||
196 | detail: "match expr {}", | ||
197 | }, | ||
198 | CompletionItem { | ||
199 | label: "not", | ||
200 | source_range: [91; 91), | ||
201 | delete: [87; 91), | ||
202 | insert: "!bar", | ||
203 | detail: "!expr", | ||
204 | }, | ||
205 | CompletionItem { | ||
206 | label: "ref", | ||
207 | source_range: [91; 91), | ||
208 | delete: [87; 91), | ||
209 | insert: "&bar", | ||
210 | detail: "&expr", | ||
211 | }, | ||
212 | CompletionItem { | ||
213 | label: "refm", | ||
214 | source_range: [91; 91), | ||
215 | delete: [87; 91), | ||
216 | insert: "&mut bar", | ||
217 | detail: "&mut expr", | ||
218 | }, | ||
219 | ] | ||
220 | "### | ||
221 | ); | ||
222 | } | ||
223 | |||
224 | #[test] | ||
225 | fn postfix_completion_works_for_ambiguous_float_literal() { | ||
226 | assert_debug_snapshot!( | ||
227 | do_postfix_completion( | ||
228 | r#" | ||
229 | fn main() { | ||
230 | 42.<|> | ||
231 | } | ||
232 | "#, | ||
233 | ), | ||
234 | @r###" | ||
235 | [ | ||
236 | CompletionItem { | ||
237 | label: "box", | ||
238 | source_range: [52; 52), | ||
239 | delete: [49; 52), | ||
240 | insert: "Box::new(42)", | ||
241 | detail: "Box::new(expr)", | ||
242 | }, | ||
243 | CompletionItem { | ||
244 | label: "dbg", | ||
245 | source_range: [52; 52), | ||
246 | delete: [49; 52), | ||
247 | insert: "dbg!(42)", | ||
248 | detail: "dbg!(expr)", | ||
249 | }, | ||
250 | CompletionItem { | ||
251 | label: "match", | ||
252 | source_range: [52; 52), | ||
253 | delete: [49; 52), | ||
254 | insert: "match 42 {\n ${1:_} => {$0\\},\n}", | ||
255 | detail: "match expr {}", | ||
256 | }, | ||
257 | CompletionItem { | ||
258 | label: "not", | ||
259 | source_range: [52; 52), | ||
260 | delete: [49; 52), | ||
261 | insert: "!42", | ||
262 | detail: "!expr", | ||
263 | }, | ||
264 | CompletionItem { | ||
265 | label: "ref", | ||
266 | source_range: [52; 52), | ||
267 | delete: [49; 52), | ||
268 | insert: "&42", | ||
269 | detail: "&expr", | ||
270 | }, | ||
271 | CompletionItem { | ||
272 | label: "refm", | ||
273 | source_range: [52; 52), | ||
274 | delete: [49; 52), | ||
275 | insert: "&mut 42", | ||
276 | detail: "&mut expr", | ||
277 | }, | ||
278 | ] | ||
279 | "### | ||
280 | ); | ||
281 | } | ||
282 | } | ||
diff --git a/crates/ra_ide_api/src/completion/complete_record_literal.rs b/crates/ra_ide_api/src/completion/complete_record_literal.rs deleted file mode 100644 index 577c394d2..000000000 --- a/crates/ra_ide_api/src/completion/complete_record_literal.rs +++ /dev/null | |||
@@ -1,159 +0,0 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | use crate::completion::{CompletionContext, Completions}; | ||
4 | |||
5 | /// Complete fields in fields literals. | ||
6 | pub(super) fn complete_record_literal(acc: &mut Completions, ctx: &CompletionContext) { | ||
7 | let (ty, variant) = match ctx.record_lit_syntax.as_ref().and_then(|it| { | ||
8 | Some(( | ||
9 | ctx.analyzer.type_of(ctx.db, &it.clone().into())?, | ||
10 | ctx.analyzer.resolve_record_literal(it)?, | ||
11 | )) | ||
12 | }) { | ||
13 | Some(it) => it, | ||
14 | _ => return, | ||
15 | }; | ||
16 | |||
17 | for (field, field_ty) in ty.variant_fields(ctx.db, variant) { | ||
18 | acc.add_field(ctx, field, &field_ty); | ||
19 | } | ||
20 | } | ||
21 | |||
22 | #[cfg(test)] | ||
23 | mod tests { | ||
24 | use crate::completion::{do_completion, CompletionItem, CompletionKind}; | ||
25 | use insta::assert_debug_snapshot; | ||
26 | |||
27 | fn complete(code: &str) -> Vec<CompletionItem> { | ||
28 | do_completion(code, CompletionKind::Reference) | ||
29 | } | ||
30 | |||
31 | #[test] | ||
32 | fn test_record_literal_deprecated_field() { | ||
33 | let completions = complete( | ||
34 | r" | ||
35 | struct A { | ||
36 | #[deprecated] | ||
37 | the_field: u32, | ||
38 | } | ||
39 | fn foo() { | ||
40 | A { the<|> } | ||
41 | } | ||
42 | ", | ||
43 | ); | ||
44 | assert_debug_snapshot!(completions, @r###" | ||
45 | [ | ||
46 | CompletionItem { | ||
47 | label: "the_field", | ||
48 | source_range: [142; 145), | ||
49 | delete: [142; 145), | ||
50 | insert: "the_field", | ||
51 | kind: Field, | ||
52 | detail: "u32", | ||
53 | deprecated: true, | ||
54 | }, | ||
55 | ] | ||
56 | "###); | ||
57 | } | ||
58 | |||
59 | #[test] | ||
60 | fn test_record_literal_field() { | ||
61 | let completions = complete( | ||
62 | r" | ||
63 | struct A { the_field: u32 } | ||
64 | fn foo() { | ||
65 | A { the<|> } | ||
66 | } | ||
67 | ", | ||
68 | ); | ||
69 | assert_debug_snapshot!(completions, @r###" | ||
70 | [ | ||
71 | CompletionItem { | ||
72 | label: "the_field", | ||
73 | source_range: [83; 86), | ||
74 | delete: [83; 86), | ||
75 | insert: "the_field", | ||
76 | kind: Field, | ||
77 | detail: "u32", | ||
78 | }, | ||
79 | ] | ||
80 | "###); | ||
81 | } | ||
82 | |||
83 | #[test] | ||
84 | fn test_record_literal_enum_variant() { | ||
85 | let completions = complete( | ||
86 | r" | ||
87 | enum E { | ||
88 | A { a: u32 } | ||
89 | } | ||
90 | fn foo() { | ||
91 | let _ = E::A { <|> } | ||
92 | } | ||
93 | ", | ||
94 | ); | ||
95 | assert_debug_snapshot!(completions, @r###" | ||
96 | [ | ||
97 | CompletionItem { | ||
98 | label: "a", | ||
99 | source_range: [119; 119), | ||
100 | delete: [119; 119), | ||
101 | insert: "a", | ||
102 | kind: Field, | ||
103 | detail: "u32", | ||
104 | }, | ||
105 | ] | ||
106 | "###); | ||
107 | } | ||
108 | |||
109 | #[test] | ||
110 | fn test_record_literal_two_structs() { | ||
111 | let completions = complete( | ||
112 | r" | ||
113 | struct A { a: u32 } | ||
114 | struct B { b: u32 } | ||
115 | |||
116 | fn foo() { | ||
117 | let _: A = B { <|> } | ||
118 | } | ||
119 | ", | ||
120 | ); | ||
121 | assert_debug_snapshot!(completions, @r###" | ||
122 | [ | ||
123 | CompletionItem { | ||
124 | label: "b", | ||
125 | source_range: [119; 119), | ||
126 | delete: [119; 119), | ||
127 | insert: "b", | ||
128 | kind: Field, | ||
129 | detail: "u32", | ||
130 | }, | ||
131 | ] | ||
132 | "###); | ||
133 | } | ||
134 | |||
135 | #[test] | ||
136 | fn test_record_literal_generic_struct() { | ||
137 | let completions = complete( | ||
138 | r" | ||
139 | struct A<T> { a: T } | ||
140 | |||
141 | fn foo() { | ||
142 | let _: A<u32> = A { <|> } | ||
143 | } | ||
144 | ", | ||
145 | ); | ||
146 | assert_debug_snapshot!(completions, @r###" | ||
147 | [ | ||
148 | CompletionItem { | ||
149 | label: "a", | ||
150 | source_range: [93; 93), | ||
151 | delete: [93; 93), | ||
152 | insert: "a", | ||
153 | kind: Field, | ||
154 | detail: "u32", | ||
155 | }, | ||
156 | ] | ||
157 | "###); | ||
158 | } | ||
159 | } | ||
diff --git a/crates/ra_ide_api/src/completion/complete_record_pattern.rs b/crates/ra_ide_api/src/completion/complete_record_pattern.rs deleted file mode 100644 index a56c7e3a1..000000000 --- a/crates/ra_ide_api/src/completion/complete_record_pattern.rs +++ /dev/null | |||
@@ -1,93 +0,0 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | use crate::completion::{CompletionContext, Completions}; | ||
4 | |||
5 | pub(super) fn complete_record_pattern(acc: &mut Completions, ctx: &CompletionContext) { | ||
6 | let (ty, variant) = match ctx.record_lit_pat.as_ref().and_then(|it| { | ||
7 | Some(( | ||
8 | ctx.analyzer.type_of_pat(ctx.db, &it.clone().into())?, | ||
9 | ctx.analyzer.resolve_record_pattern(it)?, | ||
10 | )) | ||
11 | }) { | ||
12 | Some(it) => it, | ||
13 | _ => return, | ||
14 | }; | ||
15 | |||
16 | for (field, field_ty) in ty.variant_fields(ctx.db, variant) { | ||
17 | acc.add_field(ctx, field, &field_ty); | ||
18 | } | ||
19 | } | ||
20 | |||
21 | #[cfg(test)] | ||
22 | mod tests { | ||
23 | use crate::completion::{do_completion, CompletionItem, CompletionKind}; | ||
24 | use insta::assert_debug_snapshot; | ||
25 | |||
26 | fn complete(code: &str) -> Vec<CompletionItem> { | ||
27 | do_completion(code, CompletionKind::Reference) | ||
28 | } | ||
29 | |||
30 | #[test] | ||
31 | fn test_record_pattern_field() { | ||
32 | let completions = complete( | ||
33 | r" | ||
34 | struct S { foo: u32 } | ||
35 | |||
36 | fn process(f: S) { | ||
37 | match f { | ||
38 | S { f<|>: 92 } => (), | ||
39 | } | ||
40 | } | ||
41 | ", | ||
42 | ); | ||
43 | assert_debug_snapshot!(completions, @r###" | ||
44 | [ | ||
45 | CompletionItem { | ||
46 | label: "foo", | ||
47 | source_range: [117; 118), | ||
48 | delete: [117; 118), | ||
49 | insert: "foo", | ||
50 | kind: Field, | ||
51 | detail: "u32", | ||
52 | }, | ||
53 | ] | ||
54 | "###); | ||
55 | } | ||
56 | |||
57 | #[test] | ||
58 | fn test_record_pattern_enum_variant() { | ||
59 | let completions = complete( | ||
60 | r" | ||
61 | enum E { | ||
62 | S { foo: u32, bar: () } | ||
63 | } | ||
64 | |||
65 | fn process(e: E) { | ||
66 | match e { | ||
67 | E::S { <|> } => (), | ||
68 | } | ||
69 | } | ||
70 | ", | ||
71 | ); | ||
72 | assert_debug_snapshot!(completions, @r###" | ||
73 | [ | ||
74 | CompletionItem { | ||
75 | label: "bar", | ||
76 | source_range: [161; 161), | ||
77 | delete: [161; 161), | ||
78 | insert: "bar", | ||
79 | kind: Field, | ||
80 | detail: "()", | ||
81 | }, | ||
82 | CompletionItem { | ||
83 | label: "foo", | ||
84 | source_range: [161; 161), | ||
85 | delete: [161; 161), | ||
86 | insert: "foo", | ||
87 | kind: Field, | ||
88 | detail: "u32", | ||
89 | }, | ||
90 | ] | ||
91 | "###); | ||
92 | } | ||
93 | } | ||
diff --git a/crates/ra_ide_api/src/completion/complete_scope.rs b/crates/ra_ide_api/src/completion/complete_scope.rs deleted file mode 100644 index d5739b58a..000000000 --- a/crates/ra_ide_api/src/completion/complete_scope.rs +++ /dev/null | |||
@@ -1,876 +0,0 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | use ra_assists::auto_import_text_edit; | ||
4 | use ra_syntax::{ast, AstNode, SmolStr}; | ||
5 | use ra_text_edit::TextEditBuilder; | ||
6 | use rustc_hash::FxHashMap; | ||
7 | |||
8 | use crate::completion::{CompletionContext, CompletionItem, CompletionKind, Completions}; | ||
9 | |||
10 | pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { | ||
11 | if !ctx.is_trivial_path { | ||
12 | return; | ||
13 | } | ||
14 | |||
15 | ctx.analyzer.process_all_names(ctx.db, &mut |name, res| { | ||
16 | acc.add_resolution(ctx, name.to_string(), &res) | ||
17 | }); | ||
18 | |||
19 | // auto-import | ||
20 | // We fetch ident from the original file, because we need to pre-filter auto-imports | ||
21 | if ast::NameRef::cast(ctx.token.parent()).is_some() { | ||
22 | let import_resolver = ImportResolver::new(); | ||
23 | let import_names = import_resolver.all_names(ctx.token.text()); | ||
24 | import_names.into_iter().for_each(|(name, path)| { | ||
25 | let edit = { | ||
26 | let mut builder = TextEditBuilder::default(); | ||
27 | builder.replace(ctx.source_range(), name.to_string()); | ||
28 | auto_import_text_edit( | ||
29 | &ctx.token.parent(), | ||
30 | &ctx.token.parent(), | ||
31 | &path, | ||
32 | &mut builder, | ||
33 | ); | ||
34 | builder.finish() | ||
35 | }; | ||
36 | |||
37 | // Hack: copied this check form conv.rs beacause auto import can produce edits | ||
38 | // that invalidate assert in conv_with. | ||
39 | if edit | ||
40 | .as_atoms() | ||
41 | .iter() | ||
42 | .filter(|atom| !ctx.source_range().is_subrange(&atom.delete)) | ||
43 | .all(|atom| ctx.source_range().intersection(&atom.delete).is_none()) | ||
44 | { | ||
45 | CompletionItem::new( | ||
46 | CompletionKind::Reference, | ||
47 | ctx.source_range(), | ||
48 | build_import_label(&name, &path), | ||
49 | ) | ||
50 | .text_edit(edit) | ||
51 | .add_to(acc); | ||
52 | } | ||
53 | }); | ||
54 | } | ||
55 | } | ||
56 | |||
57 | fn build_import_label(name: &str, path: &[SmolStr]) -> String { | ||
58 | let mut buf = String::with_capacity(64); | ||
59 | buf.push_str(name); | ||
60 | buf.push_str(" ("); | ||
61 | fmt_import_path(path, &mut buf); | ||
62 | buf.push_str(")"); | ||
63 | buf | ||
64 | } | ||
65 | |||
66 | fn fmt_import_path(path: &[SmolStr], buf: &mut String) { | ||
67 | let mut segments = path.iter(); | ||
68 | if let Some(s) = segments.next() { | ||
69 | buf.push_str(&s); | ||
70 | } | ||
71 | for s in segments { | ||
72 | buf.push_str("::"); | ||
73 | buf.push_str(&s); | ||
74 | } | ||
75 | } | ||
76 | |||
77 | #[derive(Debug, Clone, Default)] | ||
78 | pub(crate) struct ImportResolver { | ||
79 | // todo: use fst crate or something like that | ||
80 | dummy_names: Vec<(SmolStr, Vec<SmolStr>)>, | ||
81 | } | ||
82 | |||
83 | impl ImportResolver { | ||
84 | pub(crate) fn new() -> Self { | ||
85 | let dummy_names = vec![ | ||
86 | (SmolStr::new("fmt"), vec![SmolStr::new("std"), SmolStr::new("fmt")]), | ||
87 | (SmolStr::new("io"), vec![SmolStr::new("std"), SmolStr::new("io")]), | ||
88 | (SmolStr::new("iter"), vec![SmolStr::new("std"), SmolStr::new("iter")]), | ||
89 | (SmolStr::new("hash"), vec![SmolStr::new("std"), SmolStr::new("hash")]), | ||
90 | ( | ||
91 | SmolStr::new("Debug"), | ||
92 | vec![SmolStr::new("std"), SmolStr::new("fmt"), SmolStr::new("Debug")], | ||
93 | ), | ||
94 | ( | ||
95 | SmolStr::new("Display"), | ||
96 | vec![SmolStr::new("std"), SmolStr::new("fmt"), SmolStr::new("Display")], | ||
97 | ), | ||
98 | ( | ||
99 | SmolStr::new("Hash"), | ||
100 | vec![SmolStr::new("std"), SmolStr::new("hash"), SmolStr::new("Hash")], | ||
101 | ), | ||
102 | ( | ||
103 | SmolStr::new("Hasher"), | ||
104 | vec![SmolStr::new("std"), SmolStr::new("hash"), SmolStr::new("Hasher")], | ||
105 | ), | ||
106 | ( | ||
107 | SmolStr::new("Iterator"), | ||
108 | vec![SmolStr::new("std"), SmolStr::new("iter"), SmolStr::new("Iterator")], | ||
109 | ), | ||
110 | ]; | ||
111 | |||
112 | ImportResolver { dummy_names } | ||
113 | } | ||
114 | |||
115 | // Returns a map of importable items filtered by name. | ||
116 | // The map associates item name with its full path. | ||
117 | // todo: should return Resolutions | ||
118 | pub(crate) fn all_names(&self, name: &str) -> FxHashMap<SmolStr, Vec<SmolStr>> { | ||
119 | if name.len() > 1 { | ||
120 | self.dummy_names.iter().filter(|(n, _)| n.contains(name)).cloned().collect() | ||
121 | } else { | ||
122 | FxHashMap::default() | ||
123 | } | ||
124 | } | ||
125 | } | ||
126 | |||
127 | #[cfg(test)] | ||
128 | mod tests { | ||
129 | use crate::completion::{do_completion, CompletionItem, CompletionKind}; | ||
130 | use insta::assert_debug_snapshot; | ||
131 | |||
132 | fn do_reference_completion(code: &str) -> Vec<CompletionItem> { | ||
133 | do_completion(code, CompletionKind::Reference) | ||
134 | } | ||
135 | |||
136 | #[test] | ||
137 | fn completes_bindings_from_let() { | ||
138 | assert_debug_snapshot!( | ||
139 | do_reference_completion( | ||
140 | r" | ||
141 | fn quux(x: i32) { | ||
142 | let y = 92; | ||
143 | 1 + <|>; | ||
144 | let z = (); | ||
145 | } | ||
146 | " | ||
147 | ), | ||
148 | @r###" | ||
149 | [ | ||
150 | CompletionItem { | ||
151 | label: "quux(…)", | ||
152 | source_range: [91; 91), | ||
153 | delete: [91; 91), | ||
154 | insert: "quux($0)", | ||
155 | kind: Function, | ||
156 | lookup: "quux", | ||
157 | detail: "fn quux(x: i32)", | ||
158 | }, | ||
159 | CompletionItem { | ||
160 | label: "x", | ||
161 | source_range: [91; 91), | ||
162 | delete: [91; 91), | ||
163 | insert: "x", | ||
164 | kind: Binding, | ||
165 | detail: "i32", | ||
166 | }, | ||
167 | CompletionItem { | ||
168 | label: "y", | ||
169 | source_range: [91; 91), | ||
170 | delete: [91; 91), | ||
171 | insert: "y", | ||
172 | kind: Binding, | ||
173 | detail: "i32", | ||
174 | }, | ||
175 | ] | ||
176 | "### | ||
177 | ); | ||
178 | } | ||
179 | |||
180 | #[test] | ||
181 | fn completes_bindings_from_if_let() { | ||
182 | assert_debug_snapshot!( | ||
183 | do_reference_completion( | ||
184 | r" | ||
185 | fn quux() { | ||
186 | if let Some(x) = foo() { | ||
187 | let y = 92; | ||
188 | }; | ||
189 | if let Some(a) = bar() { | ||
190 | let b = 62; | ||
191 | 1 + <|> | ||
192 | } | ||
193 | } | ||
194 | " | ||
195 | ), | ||
196 | @r###" | ||
197 | [ | ||
198 | CompletionItem { | ||
199 | label: "a", | ||
200 | source_range: [242; 242), | ||
201 | delete: [242; 242), | ||
202 | insert: "a", | ||
203 | kind: Binding, | ||
204 | }, | ||
205 | CompletionItem { | ||
206 | label: "b", | ||
207 | source_range: [242; 242), | ||
208 | delete: [242; 242), | ||
209 | insert: "b", | ||
210 | kind: Binding, | ||
211 | detail: "i32", | ||
212 | }, | ||
213 | CompletionItem { | ||
214 | label: "quux()", | ||
215 | source_range: [242; 242), | ||
216 | delete: [242; 242), | ||
217 | insert: "quux()$0", | ||
218 | kind: Function, | ||
219 | lookup: "quux", | ||
220 | detail: "fn quux()", | ||
221 | }, | ||
222 | ] | ||
223 | "### | ||
224 | ); | ||
225 | } | ||
226 | |||
227 | #[test] | ||
228 | fn completes_bindings_from_for() { | ||
229 | assert_debug_snapshot!( | ||
230 | do_reference_completion( | ||
231 | r" | ||
232 | fn quux() { | ||
233 | for x in &[1, 2, 3] { | ||
234 | <|> | ||
235 | } | ||
236 | } | ||
237 | " | ||
238 | ), | ||
239 | @r###" | ||
240 | [ | ||
241 | CompletionItem { | ||
242 | label: "quux()", | ||
243 | source_range: [95; 95), | ||
244 | delete: [95; 95), | ||
245 | insert: "quux()$0", | ||
246 | kind: Function, | ||
247 | lookup: "quux", | ||
248 | detail: "fn quux()", | ||
249 | }, | ||
250 | CompletionItem { | ||
251 | label: "x", | ||
252 | source_range: [95; 95), | ||
253 | delete: [95; 95), | ||
254 | insert: "x", | ||
255 | kind: Binding, | ||
256 | }, | ||
257 | ] | ||
258 | "### | ||
259 | ); | ||
260 | } | ||
261 | |||
262 | #[test] | ||
263 | fn completes_generic_params() { | ||
264 | assert_debug_snapshot!( | ||
265 | do_reference_completion( | ||
266 | r" | ||
267 | fn quux<T>() { | ||
268 | <|> | ||
269 | } | ||
270 | " | ||
271 | ), | ||
272 | @r###" | ||
273 | [ | ||
274 | CompletionItem { | ||
275 | label: "T", | ||
276 | source_range: [52; 52), | ||
277 | delete: [52; 52), | ||
278 | insert: "T", | ||
279 | kind: TypeParam, | ||
280 | }, | ||
281 | CompletionItem { | ||
282 | label: "quux()", | ||
283 | source_range: [52; 52), | ||
284 | delete: [52; 52), | ||
285 | insert: "quux()$0", | ||
286 | kind: Function, | ||
287 | lookup: "quux", | ||
288 | detail: "fn quux<T>()", | ||
289 | }, | ||
290 | ] | ||
291 | "### | ||
292 | ); | ||
293 | } | ||
294 | |||
295 | #[test] | ||
296 | fn completes_generic_params_in_struct() { | ||
297 | assert_debug_snapshot!( | ||
298 | do_reference_completion( | ||
299 | r" | ||
300 | struct X<T> { | ||
301 | x: <|> | ||
302 | } | ||
303 | " | ||
304 | ), | ||
305 | @r###" | ||
306 | [ | ||
307 | CompletionItem { | ||
308 | label: "Self", | ||
309 | source_range: [54; 54), | ||
310 | delete: [54; 54), | ||
311 | insert: "Self", | ||
312 | kind: TypeParam, | ||
313 | }, | ||
314 | CompletionItem { | ||
315 | label: "T", | ||
316 | source_range: [54; 54), | ||
317 | delete: [54; 54), | ||
318 | insert: "T", | ||
319 | kind: TypeParam, | ||
320 | }, | ||
321 | CompletionItem { | ||
322 | label: "X<…>", | ||
323 | source_range: [54; 54), | ||
324 | delete: [54; 54), | ||
325 | insert: "X<$0>", | ||
326 | kind: Struct, | ||
327 | lookup: "X", | ||
328 | }, | ||
329 | ] | ||
330 | "### | ||
331 | ); | ||
332 | } | ||
333 | |||
334 | #[test] | ||
335 | fn completes_self_in_enum() { | ||
336 | assert_debug_snapshot!( | ||
337 | do_reference_completion( | ||
338 | r" | ||
339 | enum X { | ||
340 | Y(<|>) | ||
341 | } | ||
342 | " | ||
343 | ), | ||
344 | @r###" | ||
345 | [ | ||
346 | CompletionItem { | ||
347 | label: "Self", | ||
348 | source_range: [48; 48), | ||
349 | delete: [48; 48), | ||
350 | insert: "Self", | ||
351 | kind: TypeParam, | ||
352 | }, | ||
353 | CompletionItem { | ||
354 | label: "X", | ||
355 | source_range: [48; 48), | ||
356 | delete: [48; 48), | ||
357 | insert: "X", | ||
358 | kind: Enum, | ||
359 | }, | ||
360 | ] | ||
361 | "### | ||
362 | ); | ||
363 | } | ||
364 | |||
365 | #[test] | ||
366 | fn completes_module_items() { | ||
367 | assert_debug_snapshot!( | ||
368 | do_reference_completion( | ||
369 | r" | ||
370 | struct Foo; | ||
371 | enum Baz {} | ||
372 | fn quux() { | ||
373 | <|> | ||
374 | } | ||
375 | " | ||
376 | ), | ||
377 | @r###" | ||
378 | [ | ||
379 | CompletionItem { | ||
380 | label: "Baz", | ||
381 | source_range: [105; 105), | ||
382 | delete: [105; 105), | ||
383 | insert: "Baz", | ||
384 | kind: Enum, | ||
385 | }, | ||
386 | CompletionItem { | ||
387 | label: "Foo", | ||
388 | source_range: [105; 105), | ||
389 | delete: [105; 105), | ||
390 | insert: "Foo", | ||
391 | kind: Struct, | ||
392 | }, | ||
393 | CompletionItem { | ||
394 | label: "quux()", | ||
395 | source_range: [105; 105), | ||
396 | delete: [105; 105), | ||
397 | insert: "quux()$0", | ||
398 | kind: Function, | ||
399 | lookup: "quux", | ||
400 | detail: "fn quux()", | ||
401 | }, | ||
402 | ] | ||
403 | "### | ||
404 | ); | ||
405 | } | ||
406 | |||
407 | #[test] | ||
408 | fn completes_extern_prelude() { | ||
409 | assert_debug_snapshot!( | ||
410 | do_reference_completion( | ||
411 | r" | ||
412 | //- /lib.rs | ||
413 | use <|>; | ||
414 | |||
415 | //- /other_crate/lib.rs | ||
416 | // nothing here | ||
417 | " | ||
418 | ), | ||
419 | @r###" | ||
420 | [ | ||
421 | CompletionItem { | ||
422 | label: "other_crate", | ||
423 | source_range: [4; 4), | ||
424 | delete: [4; 4), | ||
425 | insert: "other_crate", | ||
426 | kind: Module, | ||
427 | }, | ||
428 | ] | ||
429 | "### | ||
430 | ); | ||
431 | } | ||
432 | |||
433 | #[test] | ||
434 | fn completes_module_items_in_nested_modules() { | ||
435 | assert_debug_snapshot!( | ||
436 | do_reference_completion( | ||
437 | r" | ||
438 | struct Foo; | ||
439 | mod m { | ||
440 | struct Bar; | ||
441 | fn quux() { <|> } | ||
442 | } | ||
443 | " | ||
444 | ), | ||
445 | @r###" | ||
446 | [ | ||
447 | CompletionItem { | ||
448 | label: "Bar", | ||
449 | source_range: [117; 117), | ||
450 | delete: [117; 117), | ||
451 | insert: "Bar", | ||
452 | kind: Struct, | ||
453 | }, | ||
454 | CompletionItem { | ||
455 | label: "quux()", | ||
456 | source_range: [117; 117), | ||
457 | delete: [117; 117), | ||
458 | insert: "quux()$0", | ||
459 | kind: Function, | ||
460 | lookup: "quux", | ||
461 | detail: "fn quux()", | ||
462 | }, | ||
463 | ] | ||
464 | "### | ||
465 | ); | ||
466 | } | ||
467 | |||
468 | #[test] | ||
469 | fn completes_return_type() { | ||
470 | assert_debug_snapshot!( | ||
471 | do_reference_completion( | ||
472 | r" | ||
473 | struct Foo; | ||
474 | fn x() -> <|> | ||
475 | " | ||
476 | ), | ||
477 | @r###" | ||
478 | [ | ||
479 | CompletionItem { | ||
480 | label: "Foo", | ||
481 | source_range: [55; 55), | ||
482 | delete: [55; 55), | ||
483 | insert: "Foo", | ||
484 | kind: Struct, | ||
485 | }, | ||
486 | CompletionItem { | ||
487 | label: "x()", | ||
488 | source_range: [55; 55), | ||
489 | delete: [55; 55), | ||
490 | insert: "x()$0", | ||
491 | kind: Function, | ||
492 | lookup: "x", | ||
493 | detail: "fn x()", | ||
494 | }, | ||
495 | ] | ||
496 | "### | ||
497 | ); | ||
498 | } | ||
499 | |||
500 | #[test] | ||
501 | fn dont_show_both_completions_for_shadowing() { | ||
502 | assert_debug_snapshot!( | ||
503 | do_reference_completion( | ||
504 | r" | ||
505 | fn foo() { | ||
506 | let bar = 92; | ||
507 | { | ||
508 | let bar = 62; | ||
509 | <|> | ||
510 | } | ||
511 | } | ||
512 | " | ||
513 | ), | ||
514 | @r###" | ||
515 | [ | ||
516 | CompletionItem { | ||
517 | label: "bar", | ||
518 | source_range: [146; 146), | ||
519 | delete: [146; 146), | ||
520 | insert: "bar", | ||
521 | kind: Binding, | ||
522 | detail: "i32", | ||
523 | }, | ||
524 | CompletionItem { | ||
525 | label: "foo()", | ||
526 | source_range: [146; 146), | ||
527 | delete: [146; 146), | ||
528 | insert: "foo()$0", | ||
529 | kind: Function, | ||
530 | lookup: "foo", | ||
531 | detail: "fn foo()", | ||
532 | }, | ||
533 | ] | ||
534 | "### | ||
535 | ); | ||
536 | } | ||
537 | |||
538 | #[test] | ||
539 | fn completes_self_in_methods() { | ||
540 | assert_debug_snapshot!( | ||
541 | do_reference_completion(r"impl S { fn foo(&self) { <|> } }"), | ||
542 | @r###" | ||
543 | [ | ||
544 | CompletionItem { | ||
545 | label: "Self", | ||
546 | source_range: [25; 25), | ||
547 | delete: [25; 25), | ||
548 | insert: "Self", | ||
549 | kind: TypeParam, | ||
550 | }, | ||
551 | CompletionItem { | ||
552 | label: "self", | ||
553 | source_range: [25; 25), | ||
554 | delete: [25; 25), | ||
555 | insert: "self", | ||
556 | kind: Binding, | ||
557 | detail: "&{unknown}", | ||
558 | }, | ||
559 | ] | ||
560 | "### | ||
561 | ); | ||
562 | } | ||
563 | |||
564 | #[test] | ||
565 | fn completes_prelude() { | ||
566 | assert_debug_snapshot!( | ||
567 | do_reference_completion( | ||
568 | " | ||
569 | //- /main.rs | ||
570 | fn foo() { let x: <|> } | ||
571 | |||
572 | //- /std/lib.rs | ||
573 | #[prelude_import] | ||
574 | use prelude::*; | ||
575 | |||
576 | mod prelude { | ||
577 | struct Option; | ||
578 | } | ||
579 | " | ||
580 | ), | ||
581 | @r###" | ||
582 | [ | ||
583 | CompletionItem { | ||
584 | label: "Option", | ||
585 | source_range: [18; 18), | ||
586 | delete: [18; 18), | ||
587 | insert: "Option", | ||
588 | kind: Struct, | ||
589 | }, | ||
590 | CompletionItem { | ||
591 | label: "foo()", | ||
592 | source_range: [18; 18), | ||
593 | delete: [18; 18), | ||
594 | insert: "foo()$0", | ||
595 | kind: Function, | ||
596 | lookup: "foo", | ||
597 | detail: "fn foo()", | ||
598 | }, | ||
599 | CompletionItem { | ||
600 | label: "std", | ||
601 | source_range: [18; 18), | ||
602 | delete: [18; 18), | ||
603 | insert: "std", | ||
604 | kind: Module, | ||
605 | }, | ||
606 | ] | ||
607 | "### | ||
608 | ); | ||
609 | } | ||
610 | |||
611 | #[test] | ||
612 | fn completes_std_prelude_if_core_is_defined() { | ||
613 | assert_debug_snapshot!( | ||
614 | do_reference_completion( | ||
615 | " | ||
616 | //- /main.rs | ||
617 | fn foo() { let x: <|> } | ||
618 | |||
619 | //- /core/lib.rs | ||
620 | #[prelude_import] | ||
621 | use prelude::*; | ||
622 | |||
623 | mod prelude { | ||
624 | struct Option; | ||
625 | } | ||
626 | |||
627 | //- /std/lib.rs | ||
628 | #[prelude_import] | ||
629 | use prelude::*; | ||
630 | |||
631 | mod prelude { | ||
632 | struct String; | ||
633 | } | ||
634 | " | ||
635 | ), | ||
636 | @r###" | ||
637 | [ | ||
638 | CompletionItem { | ||
639 | label: "String", | ||
640 | source_range: [18; 18), | ||
641 | delete: [18; 18), | ||
642 | insert: "String", | ||
643 | kind: Struct, | ||
644 | }, | ||
645 | CompletionItem { | ||
646 | label: "core", | ||
647 | source_range: [18; 18), | ||
648 | delete: [18; 18), | ||
649 | insert: "core", | ||
650 | kind: Module, | ||
651 | }, | ||
652 | CompletionItem { | ||
653 | label: "foo()", | ||
654 | source_range: [18; 18), | ||
655 | delete: [18; 18), | ||
656 | insert: "foo()$0", | ||
657 | kind: Function, | ||
658 | lookup: "foo", | ||
659 | detail: "fn foo()", | ||
660 | }, | ||
661 | CompletionItem { | ||
662 | label: "std", | ||
663 | source_range: [18; 18), | ||
664 | delete: [18; 18), | ||
665 | insert: "std", | ||
666 | kind: Module, | ||
667 | }, | ||
668 | ] | ||
669 | "### | ||
670 | ); | ||
671 | } | ||
672 | |||
673 | #[test] | ||
674 | fn completes_macros_as_value() { | ||
675 | assert_debug_snapshot!( | ||
676 | do_reference_completion( | ||
677 | " | ||
678 | //- /main.rs | ||
679 | macro_rules! foo { | ||
680 | () => {} | ||
681 | } | ||
682 | |||
683 | #[macro_use] | ||
684 | mod m1 { | ||
685 | macro_rules! bar { | ||
686 | () => {} | ||
687 | } | ||
688 | } | ||
689 | |||
690 | mod m2 { | ||
691 | macro_rules! nope { | ||
692 | () => {} | ||
693 | } | ||
694 | |||
695 | #[macro_export] | ||
696 | macro_rules! baz { | ||
697 | () => {} | ||
698 | } | ||
699 | } | ||
700 | |||
701 | fn main() { | ||
702 | let v = <|> | ||
703 | } | ||
704 | " | ||
705 | ), | ||
706 | @r###" | ||
707 | [ | ||
708 | CompletionItem { | ||
709 | label: "bar!", | ||
710 | source_range: [252; 252), | ||
711 | delete: [252; 252), | ||
712 | insert: "bar!($0)", | ||
713 | kind: Macro, | ||
714 | detail: "macro_rules! bar", | ||
715 | }, | ||
716 | CompletionItem { | ||
717 | label: "baz!", | ||
718 | source_range: [252; 252), | ||
719 | delete: [252; 252), | ||
720 | insert: "baz!($0)", | ||
721 | kind: Macro, | ||
722 | detail: "#[macro_export]\nmacro_rules! baz", | ||
723 | }, | ||
724 | CompletionItem { | ||
725 | label: "foo!", | ||
726 | source_range: [252; 252), | ||
727 | delete: [252; 252), | ||
728 | insert: "foo!($0)", | ||
729 | kind: Macro, | ||
730 | detail: "macro_rules! foo", | ||
731 | }, | ||
732 | CompletionItem { | ||
733 | label: "m1", | ||
734 | source_range: [252; 252), | ||
735 | delete: [252; 252), | ||
736 | insert: "m1", | ||
737 | kind: Module, | ||
738 | }, | ||
739 | CompletionItem { | ||
740 | label: "m2", | ||
741 | source_range: [252; 252), | ||
742 | delete: [252; 252), | ||
743 | insert: "m2", | ||
744 | kind: Module, | ||
745 | }, | ||
746 | CompletionItem { | ||
747 | label: "main()", | ||
748 | source_range: [252; 252), | ||
749 | delete: [252; 252), | ||
750 | insert: "main()$0", | ||
751 | kind: Function, | ||
752 | lookup: "main", | ||
753 | detail: "fn main()", | ||
754 | }, | ||
755 | ] | ||
756 | "### | ||
757 | ); | ||
758 | } | ||
759 | |||
760 | #[test] | ||
761 | fn completes_both_macro_and_value() { | ||
762 | assert_debug_snapshot!( | ||
763 | do_reference_completion( | ||
764 | " | ||
765 | //- /main.rs | ||
766 | macro_rules! foo { | ||
767 | () => {} | ||
768 | } | ||
769 | |||
770 | fn foo() { | ||
771 | <|> | ||
772 | } | ||
773 | " | ||
774 | ), | ||
775 | @r###" | ||
776 | [ | ||
777 | CompletionItem { | ||
778 | label: "foo!", | ||
779 | source_range: [49; 49), | ||
780 | delete: [49; 49), | ||
781 | insert: "foo!($0)", | ||
782 | kind: Macro, | ||
783 | detail: "macro_rules! foo", | ||
784 | }, | ||
785 | CompletionItem { | ||
786 | label: "foo()", | ||
787 | source_range: [49; 49), | ||
788 | delete: [49; 49), | ||
789 | insert: "foo()$0", | ||
790 | kind: Function, | ||
791 | lookup: "foo", | ||
792 | detail: "fn foo()", | ||
793 | }, | ||
794 | ] | ||
795 | "### | ||
796 | ); | ||
797 | } | ||
798 | |||
799 | #[test] | ||
800 | fn completes_macros_as_type() { | ||
801 | assert_debug_snapshot!( | ||
802 | do_reference_completion( | ||
803 | " | ||
804 | //- /main.rs | ||
805 | macro_rules! foo { | ||
806 | () => {} | ||
807 | } | ||
808 | |||
809 | fn main() { | ||
810 | let x: <|> | ||
811 | } | ||
812 | " | ||
813 | ), | ||
814 | @r###" | ||
815 | [ | ||
816 | CompletionItem { | ||
817 | label: "foo!", | ||
818 | source_range: [57; 57), | ||
819 | delete: [57; 57), | ||
820 | insert: "foo!($0)", | ||
821 | kind: Macro, | ||
822 | detail: "macro_rules! foo", | ||
823 | }, | ||
824 | CompletionItem { | ||
825 | label: "main()", | ||
826 | source_range: [57; 57), | ||
827 | delete: [57; 57), | ||
828 | insert: "main()$0", | ||
829 | kind: Function, | ||
830 | lookup: "main", | ||
831 | detail: "fn main()", | ||
832 | }, | ||
833 | ] | ||
834 | "### | ||
835 | ); | ||
836 | } | ||
837 | |||
838 | #[test] | ||
839 | fn completes_macros_as_stmt() { | ||
840 | assert_debug_snapshot!( | ||
841 | do_reference_completion( | ||
842 | " | ||
843 | //- /main.rs | ||
844 | macro_rules! foo { | ||
845 | () => {} | ||
846 | } | ||
847 | |||
848 | fn main() { | ||
849 | <|> | ||
850 | } | ||
851 | " | ||
852 | ), | ||
853 | @r###" | ||
854 | [ | ||
855 | CompletionItem { | ||
856 | label: "foo!", | ||
857 | source_range: [50; 50), | ||
858 | delete: [50; 50), | ||
859 | insert: "foo!($0)", | ||
860 | kind: Macro, | ||
861 | detail: "macro_rules! foo", | ||
862 | }, | ||
863 | CompletionItem { | ||
864 | label: "main()", | ||
865 | source_range: [50; 50), | ||
866 | delete: [50; 50), | ||
867 | insert: "main()$0", | ||
868 | kind: Function, | ||
869 | lookup: "main", | ||
870 | detail: "fn main()", | ||
871 | }, | ||
872 | ] | ||
873 | "### | ||
874 | ); | ||
875 | } | ||
876 | } | ||
diff --git a/crates/ra_ide_api/src/completion/complete_snippet.rs b/crates/ra_ide_api/src/completion/complete_snippet.rs deleted file mode 100644 index 1f2988b36..000000000 --- a/crates/ra_ide_api/src/completion/complete_snippet.rs +++ /dev/null | |||
@@ -1,120 +0,0 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | use crate::completion::{ | ||
4 | completion_item::Builder, CompletionContext, CompletionItem, CompletionItemKind, | ||
5 | CompletionKind, Completions, | ||
6 | }; | ||
7 | |||
8 | fn snippet(ctx: &CompletionContext, label: &str, snippet: &str) -> Builder { | ||
9 | CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), label) | ||
10 | .insert_snippet(snippet) | ||
11 | .kind(CompletionItemKind::Snippet) | ||
12 | } | ||
13 | |||
14 | pub(super) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) { | ||
15 | if !(ctx.is_trivial_path && ctx.function_syntax.is_some()) { | ||
16 | return; | ||
17 | } | ||
18 | |||
19 | snippet(ctx, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc); | ||
20 | snippet(ctx, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc); | ||
21 | } | ||
22 | |||
23 | pub(super) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionContext) { | ||
24 | if !ctx.is_new_item { | ||
25 | return; | ||
26 | } | ||
27 | snippet( | ||
28 | ctx, | ||
29 | "Test function", | ||
30 | "\ | ||
31 | #[test] | ||
32 | fn ${1:feature}() { | ||
33 | $0 | ||
34 | }", | ||
35 | ) | ||
36 | .lookup_by("tfn") | ||
37 | .add_to(acc); | ||
38 | |||
39 | snippet(ctx, "pub(crate)", "pub(crate) $0").add_to(acc); | ||
40 | } | ||
41 | |||
42 | #[cfg(test)] | ||
43 | mod tests { | ||
44 | use crate::completion::{do_completion, CompletionItem, CompletionKind}; | ||
45 | use insta::assert_debug_snapshot; | ||
46 | |||
47 | fn do_snippet_completion(code: &str) -> Vec<CompletionItem> { | ||
48 | do_completion(code, CompletionKind::Snippet) | ||
49 | } | ||
50 | |||
51 | #[test] | ||
52 | fn completes_snippets_in_expressions() { | ||
53 | assert_debug_snapshot!( | ||
54 | do_snippet_completion(r"fn foo(x: i32) { <|> }"), | ||
55 | @r###" | ||
56 | [ | ||
57 | CompletionItem { | ||
58 | label: "pd", | ||
59 | source_range: [17; 17), | ||
60 | delete: [17; 17), | ||
61 | insert: "eprintln!(\"$0 = {:?}\", $0);", | ||
62 | kind: Snippet, | ||
63 | }, | ||
64 | CompletionItem { | ||
65 | label: "ppd", | ||
66 | source_range: [17; 17), | ||
67 | delete: [17; 17), | ||
68 | insert: "eprintln!(\"$0 = {:#?}\", $0);", | ||
69 | kind: Snippet, | ||
70 | }, | ||
71 | ] | ||
72 | "### | ||
73 | ); | ||
74 | } | ||
75 | |||
76 | #[test] | ||
77 | fn should_not_complete_snippets_in_path() { | ||
78 | assert_debug_snapshot!( | ||
79 | do_snippet_completion(r"fn foo(x: i32) { ::foo<|> }"), | ||
80 | @"[]" | ||
81 | ); | ||
82 | assert_debug_snapshot!( | ||
83 | do_snippet_completion(r"fn foo(x: i32) { ::<|> }"), | ||
84 | @"[]" | ||
85 | ); | ||
86 | } | ||
87 | |||
88 | #[test] | ||
89 | fn completes_snippets_in_items() { | ||
90 | assert_debug_snapshot!( | ||
91 | do_snippet_completion( | ||
92 | r" | ||
93 | #[cfg(test)] | ||
94 | mod tests { | ||
95 | <|> | ||
96 | } | ||
97 | " | ||
98 | ), | ||
99 | @r###" | ||
100 | [ | ||
101 | CompletionItem { | ||
102 | label: "Test function", | ||
103 | source_range: [78; 78), | ||
104 | delete: [78; 78), | ||
105 | insert: "#[test]\nfn ${1:feature}() {\n $0\n}", | ||
106 | kind: Snippet, | ||
107 | lookup: "tfn", | ||
108 | }, | ||
109 | CompletionItem { | ||
110 | label: "pub(crate)", | ||
111 | source_range: [78; 78), | ||
112 | delete: [78; 78), | ||
113 | insert: "pub(crate) $0", | ||
114 | kind: Snippet, | ||
115 | }, | ||
116 | ] | ||
117 | "### | ||
118 | ); | ||
119 | } | ||
120 | } | ||
diff --git a/crates/ra_ide_api/src/completion/completion_context.rs b/crates/ra_ide_api/src/completion/completion_context.rs deleted file mode 100644 index b8345c91d..000000000 --- a/crates/ra_ide_api/src/completion/completion_context.rs +++ /dev/null | |||
@@ -1,274 +0,0 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | use ra_syntax::{ | ||
4 | algo::{find_covering_element, find_node_at_offset}, | ||
5 | ast, AstNode, Parse, SourceFile, | ||
6 | SyntaxKind::*, | ||
7 | SyntaxNode, SyntaxToken, TextRange, TextUnit, | ||
8 | }; | ||
9 | use ra_text_edit::AtomTextEdit; | ||
10 | |||
11 | use crate::{db, FilePosition}; | ||
12 | |||
13 | /// `CompletionContext` is created early during completion to figure out, where | ||
14 | /// exactly is the cursor, syntax-wise. | ||
15 | #[derive(Debug)] | ||
16 | pub(crate) struct CompletionContext<'a> { | ||
17 | pub(super) db: &'a db::RootDatabase, | ||
18 | pub(super) analyzer: hir::SourceAnalyzer, | ||
19 | pub(super) offset: TextUnit, | ||
20 | pub(super) token: SyntaxToken, | ||
21 | pub(super) module: Option<hir::Module>, | ||
22 | pub(super) function_syntax: Option<ast::FnDef>, | ||
23 | pub(super) use_item_syntax: Option<ast::UseItem>, | ||
24 | pub(super) record_lit_syntax: Option<ast::RecordLit>, | ||
25 | pub(super) record_lit_pat: Option<ast::RecordPat>, | ||
26 | pub(super) is_param: bool, | ||
27 | /// If a name-binding or reference to a const in a pattern. | ||
28 | /// Irrefutable patterns (like let) are excluded. | ||
29 | pub(super) is_pat_binding: bool, | ||
30 | /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path. | ||
31 | pub(super) is_trivial_path: bool, | ||
32 | /// If not a trivial path, the prefix (qualifier). | ||
33 | pub(super) path_prefix: Option<hir::Path>, | ||
34 | pub(super) after_if: bool, | ||
35 | /// `true` if we are a statement or a last expr in the block. | ||
36 | pub(super) can_be_stmt: bool, | ||
37 | /// Something is typed at the "top" level, in module or impl/trait. | ||
38 | pub(super) is_new_item: bool, | ||
39 | /// The receiver if this is a field or method access, i.e. writing something.<|> | ||
40 | pub(super) dot_receiver: Option<ast::Expr>, | ||
41 | pub(super) dot_receiver_is_ambiguous_float_literal: bool, | ||
42 | /// If this is a call (method or function) in particular, i.e. the () are already there. | ||
43 | pub(super) is_call: bool, | ||
44 | pub(super) is_path_type: bool, | ||
45 | pub(super) has_type_args: bool, | ||
46 | } | ||
47 | |||
48 | impl<'a> CompletionContext<'a> { | ||
49 | pub(super) fn new( | ||
50 | db: &'a db::RootDatabase, | ||
51 | original_parse: &'a Parse<ast::SourceFile>, | ||
52 | position: FilePosition, | ||
53 | ) -> Option<CompletionContext<'a>> { | ||
54 | let src = hir::ModuleSource::from_position(db, position); | ||
55 | let module = hir::Module::from_definition( | ||
56 | db, | ||
57 | hir::Source { file_id: position.file_id.into(), value: src }, | ||
58 | ); | ||
59 | let token = | ||
60 | original_parse.tree().syntax().token_at_offset(position.offset).left_biased()?; | ||
61 | let analyzer = hir::SourceAnalyzer::new( | ||
62 | db, | ||
63 | hir::Source::new(position.file_id.into(), &token.parent()), | ||
64 | Some(position.offset), | ||
65 | ); | ||
66 | let mut ctx = CompletionContext { | ||
67 | db, | ||
68 | analyzer, | ||
69 | token, | ||
70 | offset: position.offset, | ||
71 | module, | ||
72 | function_syntax: None, | ||
73 | use_item_syntax: None, | ||
74 | record_lit_syntax: None, | ||
75 | record_lit_pat: None, | ||
76 | is_param: false, | ||
77 | is_pat_binding: false, | ||
78 | is_trivial_path: false, | ||
79 | path_prefix: None, | ||
80 | after_if: false, | ||
81 | can_be_stmt: false, | ||
82 | is_new_item: false, | ||
83 | dot_receiver: None, | ||
84 | is_call: false, | ||
85 | is_path_type: false, | ||
86 | has_type_args: false, | ||
87 | dot_receiver_is_ambiguous_float_literal: false, | ||
88 | }; | ||
89 | ctx.fill(&original_parse, position.offset); | ||
90 | Some(ctx) | ||
91 | } | ||
92 | |||
93 | // The range of the identifier that is being completed. | ||
94 | pub(crate) fn source_range(&self) -> TextRange { | ||
95 | match self.token.kind() { | ||
96 | // workaroud when completion is triggered by trigger characters. | ||
97 | IDENT => self.token.text_range(), | ||
98 | _ => TextRange::offset_len(self.offset, 0.into()), | ||
99 | } | ||
100 | } | ||
101 | |||
102 | fn fill(&mut self, original_parse: &'a Parse<ast::SourceFile>, offset: TextUnit) { | ||
103 | // Insert a fake ident to get a valid parse tree. We will use this file | ||
104 | // to determine context, though the original_file will be used for | ||
105 | // actual completion. | ||
106 | let file = { | ||
107 | let edit = AtomTextEdit::insert(offset, "intellijRulezz".to_string()); | ||
108 | original_parse.reparse(&edit).tree() | ||
109 | }; | ||
110 | |||
111 | // First, let's try to complete a reference to some declaration. | ||
112 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(file.syntax(), offset) { | ||
113 | // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`. | ||
114 | // See RFC#1685. | ||
115 | if is_node::<ast::Param>(name_ref.syntax()) { | ||
116 | self.is_param = true; | ||
117 | return; | ||
118 | } | ||
119 | self.classify_name_ref(original_parse.tree(), name_ref); | ||
120 | } | ||
121 | |||
122 | // Otherwise, see if this is a declaration. We can use heuristics to | ||
123 | // suggest declaration names, see `CompletionKind::Magic`. | ||
124 | if let Some(name) = find_node_at_offset::<ast::Name>(file.syntax(), offset) { | ||
125 | if let Some(bind_pat) = name.syntax().ancestors().find_map(ast::BindPat::cast) { | ||
126 | let parent = bind_pat.syntax().parent(); | ||
127 | if parent.clone().and_then(ast::MatchArm::cast).is_some() | ||
128 | || parent.and_then(ast::Condition::cast).is_some() | ||
129 | { | ||
130 | self.is_pat_binding = true; | ||
131 | } | ||
132 | } | ||
133 | if is_node::<ast::Param>(name.syntax()) { | ||
134 | self.is_param = true; | ||
135 | return; | ||
136 | } | ||
137 | if name.syntax().ancestors().find_map(ast::RecordFieldPatList::cast).is_some() { | ||
138 | self.record_lit_pat = | ||
139 | find_node_at_offset(original_parse.tree().syntax(), self.offset); | ||
140 | } | ||
141 | } | ||
142 | } | ||
143 | |||
144 | fn classify_name_ref(&mut self, original_file: SourceFile, name_ref: ast::NameRef) { | ||
145 | let name_range = name_ref.syntax().text_range(); | ||
146 | if name_ref.syntax().parent().and_then(ast::RecordField::cast).is_some() { | ||
147 | self.record_lit_syntax = find_node_at_offset(original_file.syntax(), self.offset); | ||
148 | } | ||
149 | |||
150 | let top_node = name_ref | ||
151 | .syntax() | ||
152 | .ancestors() | ||
153 | .take_while(|it| it.text_range() == name_range) | ||
154 | .last() | ||
155 | .unwrap(); | ||
156 | |||
157 | match top_node.parent().map(|it| it.kind()) { | ||
158 | Some(SOURCE_FILE) | Some(ITEM_LIST) => { | ||
159 | self.is_new_item = true; | ||
160 | return; | ||
161 | } | ||
162 | _ => (), | ||
163 | } | ||
164 | |||
165 | self.use_item_syntax = self.token.parent().ancestors().find_map(ast::UseItem::cast); | ||
166 | |||
167 | self.function_syntax = self | ||
168 | .token | ||
169 | .parent() | ||
170 | .ancestors() | ||
171 | .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) | ||
172 | .find_map(ast::FnDef::cast); | ||
173 | |||
174 | let parent = match name_ref.syntax().parent() { | ||
175 | Some(it) => it, | ||
176 | None => return, | ||
177 | }; | ||
178 | |||
179 | if let Some(segment) = ast::PathSegment::cast(parent.clone()) { | ||
180 | let path = segment.parent_path(); | ||
181 | self.is_call = path | ||
182 | .syntax() | ||
183 | .parent() | ||
184 | .and_then(ast::PathExpr::cast) | ||
185 | .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast)) | ||
186 | .is_some(); | ||
187 | |||
188 | self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); | ||
189 | self.has_type_args = segment.type_arg_list().is_some(); | ||
190 | |||
191 | if let Some(mut path) = hir::Path::from_ast(path.clone()) { | ||
192 | if !path.is_ident() { | ||
193 | path.segments.pop().unwrap(); | ||
194 | self.path_prefix = Some(path); | ||
195 | return; | ||
196 | } | ||
197 | } | ||
198 | |||
199 | if path.qualifier().is_none() { | ||
200 | self.is_trivial_path = true; | ||
201 | |||
202 | // Find either enclosing expr statement (thing with `;`) or a | ||
203 | // block. If block, check that we are the last expr. | ||
204 | self.can_be_stmt = name_ref | ||
205 | .syntax() | ||
206 | .ancestors() | ||
207 | .find_map(|node| { | ||
208 | if let Some(stmt) = ast::ExprStmt::cast(node.clone()) { | ||
209 | return Some( | ||
210 | stmt.syntax().text_range() == name_ref.syntax().text_range(), | ||
211 | ); | ||
212 | } | ||
213 | if let Some(block) = ast::Block::cast(node) { | ||
214 | return Some( | ||
215 | block.expr().map(|e| e.syntax().text_range()) | ||
216 | == Some(name_ref.syntax().text_range()), | ||
217 | ); | ||
218 | } | ||
219 | None | ||
220 | }) | ||
221 | .unwrap_or(false); | ||
222 | |||
223 | if let Some(off) = name_ref.syntax().text_range().start().checked_sub(2.into()) { | ||
224 | if let Some(if_expr) = | ||
225 | find_node_at_offset::<ast::IfExpr>(original_file.syntax(), off) | ||
226 | { | ||
227 | if if_expr.syntax().text_range().end() | ||
228 | < name_ref.syntax().text_range().start() | ||
229 | { | ||
230 | self.after_if = true; | ||
231 | } | ||
232 | } | ||
233 | } | ||
234 | } | ||
235 | } | ||
236 | if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) { | ||
237 | // The receiver comes before the point of insertion of the fake | ||
238 | // ident, so it should have the same range in the non-modified file | ||
239 | self.dot_receiver = field_expr | ||
240 | .expr() | ||
241 | .map(|e| e.syntax().text_range()) | ||
242 | .and_then(|r| find_node_with_range(original_file.syntax(), r)); | ||
243 | self.dot_receiver_is_ambiguous_float_literal = if let Some(ast::Expr::Literal(l)) = | ||
244 | &self.dot_receiver | ||
245 | { | ||
246 | match l.kind() { | ||
247 | ast::LiteralKind::FloatNumber { suffix: _ } => l.token().text().ends_with('.'), | ||
248 | _ => false, | ||
249 | } | ||
250 | } else { | ||
251 | false | ||
252 | } | ||
253 | } | ||
254 | if let Some(method_call_expr) = ast::MethodCallExpr::cast(parent) { | ||
255 | // As above | ||
256 | self.dot_receiver = method_call_expr | ||
257 | .expr() | ||
258 | .map(|e| e.syntax().text_range()) | ||
259 | .and_then(|r| find_node_with_range(original_file.syntax(), r)); | ||
260 | self.is_call = true; | ||
261 | } | ||
262 | } | ||
263 | } | ||
264 | |||
265 | fn find_node_with_range<N: AstNode>(syntax: &SyntaxNode, range: TextRange) -> Option<N> { | ||
266 | find_covering_element(syntax, range).ancestors().find_map(N::cast) | ||
267 | } | ||
268 | |||
269 | fn is_node<N: AstNode>(node: &SyntaxNode) -> bool { | ||
270 | match node.ancestors().find_map(N::cast) { | ||
271 | None => false, | ||
272 | Some(n) => n.syntax().text_range() == node.text_range(), | ||
273 | } | ||
274 | } | ||
diff --git a/crates/ra_ide_api/src/completion/completion_item.rs b/crates/ra_ide_api/src/completion/completion_item.rs deleted file mode 100644 index 93f336370..000000000 --- a/crates/ra_ide_api/src/completion/completion_item.rs +++ /dev/null | |||
@@ -1,322 +0,0 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | use std::fmt; | ||
4 | |||
5 | use hir::Documentation; | ||
6 | use ra_syntax::TextRange; | ||
7 | use ra_text_edit::TextEdit; | ||
8 | |||
9 | /// `CompletionItem` describes a single completion variant in the editor pop-up. | ||
10 | /// It is basically a POD with various properties. To construct a | ||
11 | /// `CompletionItem`, use `new` method and the `Builder` struct. | ||
12 | pub struct CompletionItem { | ||
13 | /// Used only internally in tests, to check only specific kind of | ||
14 | /// completion (postfix, keyword, reference, etc). | ||
15 | #[allow(unused)] | ||
16 | completion_kind: CompletionKind, | ||
17 | /// Label in the completion pop up which identifies completion. | ||
18 | label: String, | ||
19 | /// Range of identifier that is being completed. | ||
20 | /// | ||
21 | /// It should be used primarily for UI, but we also use this to convert | ||
22 | /// genetic TextEdit into LSP's completion edit (see conv.rs). | ||
23 | /// | ||
24 | /// `source_range` must contain the completion offset. `insert_text` should | ||
25 | /// start with what `source_range` points to, or VSCode will filter out the | ||
26 | /// completion silently. | ||
27 | source_range: TextRange, | ||
28 | /// What happens when user selects this item. | ||
29 | /// | ||
30 | /// Typically, replaces `source_range` with new identifier. | ||
31 | text_edit: TextEdit, | ||
32 | insert_text_format: InsertTextFormat, | ||
33 | |||
34 | /// What item (struct, function, etc) are we completing. | ||
35 | kind: Option<CompletionItemKind>, | ||
36 | |||
37 | /// Lookup is used to check if completion item indeed can complete current | ||
38 | /// ident. | ||
39 | /// | ||
40 | /// That is, in `foo.bar<|>` lookup of `abracadabra` will be accepted (it | ||
41 | /// contains `bar` sub sequence), and `quux` will rejected. | ||
42 | lookup: Option<String>, | ||
43 | |||
44 | /// Additional info to show in the UI pop up. | ||
45 | detail: Option<String>, | ||
46 | documentation: Option<Documentation>, | ||
47 | |||
48 | /// Whether this item is marked as deprecated | ||
49 | deprecated: bool, | ||
50 | } | ||
51 | |||
52 | // We use custom debug for CompletionItem to make `insta`'s diffs more readable. | ||
53 | impl fmt::Debug for CompletionItem { | ||
54 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
55 | let mut s = f.debug_struct("CompletionItem"); | ||
56 | s.field("label", &self.label()).field("source_range", &self.source_range()); | ||
57 | if self.text_edit().as_atoms().len() == 1 { | ||
58 | let atom = &self.text_edit().as_atoms()[0]; | ||
59 | s.field("delete", &atom.delete); | ||
60 | s.field("insert", &atom.insert); | ||
61 | } else { | ||
62 | s.field("text_edit", &self.text_edit); | ||
63 | } | ||
64 | if let Some(kind) = self.kind().as_ref() { | ||
65 | s.field("kind", kind); | ||
66 | } | ||
67 | if self.lookup() != self.label() { | ||
68 | s.field("lookup", &self.lookup()); | ||
69 | } | ||
70 | if let Some(detail) = self.detail() { | ||
71 | s.field("detail", &detail); | ||
72 | } | ||
73 | if let Some(documentation) = self.documentation() { | ||
74 | s.field("documentation", &documentation); | ||
75 | } | ||
76 | if self.deprecated { | ||
77 | s.field("deprecated", &true); | ||
78 | } | ||
79 | s.finish() | ||
80 | } | ||
81 | } | ||
82 | |||
83 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
84 | pub enum CompletionItemKind { | ||
85 | Snippet, | ||
86 | Keyword, | ||
87 | Module, | ||
88 | Function, | ||
89 | BuiltinType, | ||
90 | Struct, | ||
91 | Enum, | ||
92 | EnumVariant, | ||
93 | Binding, | ||
94 | Field, | ||
95 | Static, | ||
96 | Const, | ||
97 | Trait, | ||
98 | TypeAlias, | ||
99 | Method, | ||
100 | TypeParam, | ||
101 | Macro, | ||
102 | } | ||
103 | |||
104 | #[derive(Debug, PartialEq, Eq, Copy, Clone)] | ||
105 | pub(crate) enum CompletionKind { | ||
106 | /// Parser-based keyword completion. | ||
107 | Keyword, | ||
108 | /// Your usual "complete all valid identifiers". | ||
109 | Reference, | ||
110 | /// "Secret sauce" completions. | ||
111 | Magic, | ||
112 | Snippet, | ||
113 | Postfix, | ||
114 | BuiltinType, | ||
115 | } | ||
116 | |||
117 | #[derive(Debug, PartialEq, Eq, Copy, Clone)] | ||
118 | pub enum InsertTextFormat { | ||
119 | PlainText, | ||
120 | Snippet, | ||
121 | } | ||
122 | |||
123 | impl CompletionItem { | ||
124 | pub(crate) fn new( | ||
125 | completion_kind: CompletionKind, | ||
126 | source_range: TextRange, | ||
127 | label: impl Into<String>, | ||
128 | ) -> Builder { | ||
129 | let label = label.into(); | ||
130 | Builder { | ||
131 | source_range, | ||
132 | completion_kind, | ||
133 | label, | ||
134 | insert_text: None, | ||
135 | insert_text_format: InsertTextFormat::PlainText, | ||
136 | detail: None, | ||
137 | documentation: None, | ||
138 | lookup: None, | ||
139 | kind: None, | ||
140 | text_edit: None, | ||
141 | deprecated: None, | ||
142 | } | ||
143 | } | ||
144 | /// What user sees in pop-up in the UI. | ||
145 | pub fn label(&self) -> &str { | ||
146 | &self.label | ||
147 | } | ||
148 | pub fn source_range(&self) -> TextRange { | ||
149 | self.source_range | ||
150 | } | ||
151 | |||
152 | pub fn insert_text_format(&self) -> InsertTextFormat { | ||
153 | self.insert_text_format | ||
154 | } | ||
155 | |||
156 | pub fn text_edit(&self) -> &TextEdit { | ||
157 | &self.text_edit | ||
158 | } | ||
159 | |||
160 | /// Short one-line additional information, like a type | ||
161 | pub fn detail(&self) -> Option<&str> { | ||
162 | self.detail.as_ref().map(|it| it.as_str()) | ||
163 | } | ||
164 | /// A doc-comment | ||
165 | pub fn documentation(&self) -> Option<Documentation> { | ||
166 | self.documentation.clone() | ||
167 | } | ||
168 | /// What string is used for filtering. | ||
169 | pub fn lookup(&self) -> &str { | ||
170 | self.lookup.as_ref().map(|it| it.as_str()).unwrap_or_else(|| self.label()) | ||
171 | } | ||
172 | |||
173 | pub fn kind(&self) -> Option<CompletionItemKind> { | ||
174 | self.kind | ||
175 | } | ||
176 | |||
177 | pub fn deprecated(&self) -> bool { | ||
178 | self.deprecated | ||
179 | } | ||
180 | } | ||
181 | |||
182 | /// A helper to make `CompletionItem`s. | ||
183 | #[must_use] | ||
184 | pub(crate) struct Builder { | ||
185 | source_range: TextRange, | ||
186 | completion_kind: CompletionKind, | ||
187 | label: String, | ||
188 | insert_text: Option<String>, | ||
189 | insert_text_format: InsertTextFormat, | ||
190 | detail: Option<String>, | ||
191 | documentation: Option<Documentation>, | ||
192 | lookup: Option<String>, | ||
193 | kind: Option<CompletionItemKind>, | ||
194 | text_edit: Option<TextEdit>, | ||
195 | deprecated: Option<bool>, | ||
196 | } | ||
197 | |||
198 | impl Builder { | ||
199 | pub(crate) fn add_to(self, acc: &mut Completions) { | ||
200 | acc.add(self.build()) | ||
201 | } | ||
202 | |||
203 | pub(crate) fn build(self) -> CompletionItem { | ||
204 | let label = self.label; | ||
205 | let text_edit = match self.text_edit { | ||
206 | Some(it) => it, | ||
207 | None => TextEdit::replace( | ||
208 | self.source_range, | ||
209 | self.insert_text.unwrap_or_else(|| label.clone()), | ||
210 | ), | ||
211 | }; | ||
212 | |||
213 | CompletionItem { | ||
214 | source_range: self.source_range, | ||
215 | label, | ||
216 | insert_text_format: self.insert_text_format, | ||
217 | text_edit, | ||
218 | detail: self.detail, | ||
219 | documentation: self.documentation, | ||
220 | lookup: self.lookup, | ||
221 | kind: self.kind, | ||
222 | completion_kind: self.completion_kind, | ||
223 | deprecated: self.deprecated.unwrap_or(false), | ||
224 | } | ||
225 | } | ||
226 | pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder { | ||
227 | self.lookup = Some(lookup.into()); | ||
228 | self | ||
229 | } | ||
230 | pub(crate) fn label(mut self, label: impl Into<String>) -> Builder { | ||
231 | self.label = label.into(); | ||
232 | self | ||
233 | } | ||
234 | pub(crate) fn insert_text(mut self, insert_text: impl Into<String>) -> Builder { | ||
235 | self.insert_text = Some(insert_text.into()); | ||
236 | self | ||
237 | } | ||
238 | pub(crate) fn insert_snippet(mut self, snippet: impl Into<String>) -> Builder { | ||
239 | self.insert_text_format = InsertTextFormat::Snippet; | ||
240 | self.insert_text(snippet) | ||
241 | } | ||
242 | pub(crate) fn kind(mut self, kind: CompletionItemKind) -> Builder { | ||
243 | self.kind = Some(kind); | ||
244 | self | ||
245 | } | ||
246 | pub(crate) fn text_edit(mut self, edit: TextEdit) -> Builder { | ||
247 | self.text_edit = Some(edit); | ||
248 | self | ||
249 | } | ||
250 | pub(crate) fn snippet_edit(mut self, edit: TextEdit) -> Builder { | ||
251 | self.insert_text_format = InsertTextFormat::Snippet; | ||
252 | self.text_edit(edit) | ||
253 | } | ||
254 | #[allow(unused)] | ||
255 | pub(crate) fn detail(self, detail: impl Into<String>) -> Builder { | ||
256 | self.set_detail(Some(detail)) | ||
257 | } | ||
258 | pub(crate) fn set_detail(mut self, detail: Option<impl Into<String>>) -> Builder { | ||
259 | self.detail = detail.map(Into::into); | ||
260 | self | ||
261 | } | ||
262 | #[allow(unused)] | ||
263 | pub(crate) fn documentation(self, docs: Documentation) -> Builder { | ||
264 | self.set_documentation(Some(docs)) | ||
265 | } | ||
266 | pub(crate) fn set_documentation(mut self, docs: Option<Documentation>) -> Builder { | ||
267 | self.documentation = docs.map(Into::into); | ||
268 | self | ||
269 | } | ||
270 | pub(crate) fn set_deprecated(mut self, deprecated: bool) -> Builder { | ||
271 | self.deprecated = Some(deprecated); | ||
272 | self | ||
273 | } | ||
274 | } | ||
275 | |||
276 | impl<'a> Into<CompletionItem> for Builder { | ||
277 | fn into(self) -> CompletionItem { | ||
278 | self.build() | ||
279 | } | ||
280 | } | ||
281 | |||
282 | /// Represents an in-progress set of completions being built. | ||
283 | #[derive(Debug, Default)] | ||
284 | pub(crate) struct Completions { | ||
285 | buf: Vec<CompletionItem>, | ||
286 | } | ||
287 | |||
288 | impl Completions { | ||
289 | pub(crate) fn add(&mut self, item: impl Into<CompletionItem>) { | ||
290 | self.buf.push(item.into()) | ||
291 | } | ||
292 | pub(crate) fn add_all<I>(&mut self, items: I) | ||
293 | where | ||
294 | I: IntoIterator, | ||
295 | I::Item: Into<CompletionItem>, | ||
296 | { | ||
297 | items.into_iter().for_each(|item| self.add(item.into())) | ||
298 | } | ||
299 | } | ||
300 | |||
301 | impl Into<Vec<CompletionItem>> for Completions { | ||
302 | fn into(self) -> Vec<CompletionItem> { | ||
303 | self.buf | ||
304 | } | ||
305 | } | ||
306 | |||
307 | #[cfg(test)] | ||
308 | pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> { | ||
309 | use crate::completion::completions; | ||
310 | use crate::mock_analysis::{analysis_and_position, single_file_with_position}; | ||
311 | let (analysis, position) = if code.contains("//-") { | ||
312 | analysis_and_position(code) | ||
313 | } else { | ||
314 | single_file_with_position(code) | ||
315 | }; | ||
316 | let completions = completions(&analysis.db, position).unwrap(); | ||
317 | let completion_items: Vec<CompletionItem> = completions.into(); | ||
318 | let mut kind_completions: Vec<CompletionItem> = | ||
319 | completion_items.into_iter().filter(|c| c.completion_kind == kind).collect(); | ||
320 | kind_completions.sort_by_key(|c| c.label.clone()); | ||
321 | kind_completions | ||
322 | } | ||
diff --git a/crates/ra_ide_api/src/completion/presentation.rs b/crates/ra_ide_api/src/completion/presentation.rs deleted file mode 100644 index 5f056730a..000000000 --- a/crates/ra_ide_api/src/completion/presentation.rs +++ /dev/null | |||
@@ -1,676 +0,0 @@ | |||
1 | //! This modules takes care of rendering various definitions as completion items. | ||
2 | |||
3 | use hir::{db::HirDatabase, Docs, HasAttrs, HasSource, HirDisplay, ScopeDef, Type}; | ||
4 | use join_to_string::join; | ||
5 | use ra_syntax::ast::NameOwner; | ||
6 | use test_utils::tested_by; | ||
7 | |||
8 | use crate::completion::{ | ||
9 | CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions, | ||
10 | }; | ||
11 | |||
12 | use crate::display::{const_label, function_label, macro_label, type_label}; | ||
13 | |||
14 | impl Completions { | ||
15 | pub(crate) fn add_field( | ||
16 | &mut self, | ||
17 | ctx: &CompletionContext, | ||
18 | field: hir::StructField, | ||
19 | ty: &Type, | ||
20 | ) { | ||
21 | let is_deprecated = is_deprecated(field, ctx.db); | ||
22 | CompletionItem::new( | ||
23 | CompletionKind::Reference, | ||
24 | ctx.source_range(), | ||
25 | field.name(ctx.db).to_string(), | ||
26 | ) | ||
27 | .kind(CompletionItemKind::Field) | ||
28 | .detail(ty.display(ctx.db).to_string()) | ||
29 | .set_documentation(field.docs(ctx.db)) | ||
30 | .set_deprecated(is_deprecated) | ||
31 | .add_to(self); | ||
32 | } | ||
33 | |||
34 | pub(crate) fn add_tuple_field(&mut self, ctx: &CompletionContext, field: usize, ty: &Type) { | ||
35 | CompletionItem::new(CompletionKind::Reference, ctx.source_range(), field.to_string()) | ||
36 | .kind(CompletionItemKind::Field) | ||
37 | .detail(ty.display(ctx.db).to_string()) | ||
38 | .add_to(self); | ||
39 | } | ||
40 | |||
41 | pub(crate) fn add_resolution( | ||
42 | &mut self, | ||
43 | ctx: &CompletionContext, | ||
44 | local_name: String, | ||
45 | resolution: &ScopeDef, | ||
46 | ) { | ||
47 | use hir::ModuleDef::*; | ||
48 | |||
49 | let completion_kind = match resolution { | ||
50 | ScopeDef::ModuleDef(BuiltinType(..)) => CompletionKind::BuiltinType, | ||
51 | _ => CompletionKind::Reference, | ||
52 | }; | ||
53 | |||
54 | let kind = match resolution { | ||
55 | ScopeDef::ModuleDef(Module(..)) => CompletionItemKind::Module, | ||
56 | ScopeDef::ModuleDef(Function(func)) => { | ||
57 | return self.add_function_with_name(ctx, Some(local_name), *func); | ||
58 | } | ||
59 | ScopeDef::ModuleDef(Adt(hir::Adt::Struct(_))) => CompletionItemKind::Struct, | ||
60 | // FIXME: add CompletionItemKind::Union | ||
61 | ScopeDef::ModuleDef(Adt(hir::Adt::Union(_))) => CompletionItemKind::Struct, | ||
62 | ScopeDef::ModuleDef(Adt(hir::Adt::Enum(_))) => CompletionItemKind::Enum, | ||
63 | |||
64 | ScopeDef::ModuleDef(EnumVariant(..)) => CompletionItemKind::EnumVariant, | ||
65 | ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::Const, | ||
66 | ScopeDef::ModuleDef(Static(..)) => CompletionItemKind::Static, | ||
67 | ScopeDef::ModuleDef(Trait(..)) => CompletionItemKind::Trait, | ||
68 | ScopeDef::ModuleDef(TypeAlias(..)) => CompletionItemKind::TypeAlias, | ||
69 | ScopeDef::ModuleDef(BuiltinType(..)) => CompletionItemKind::BuiltinType, | ||
70 | ScopeDef::GenericParam(..) => CompletionItemKind::TypeParam, | ||
71 | ScopeDef::Local(..) => CompletionItemKind::Binding, | ||
72 | // (does this need its own kind?) | ||
73 | ScopeDef::AdtSelfType(..) | ScopeDef::ImplSelfType(..) => CompletionItemKind::TypeParam, | ||
74 | ScopeDef::MacroDef(mac) => { | ||
75 | return self.add_macro(ctx, Some(local_name), *mac); | ||
76 | } | ||
77 | ScopeDef::Unknown => { | ||
78 | return self.add(CompletionItem::new( | ||
79 | CompletionKind::Reference, | ||
80 | ctx.source_range(), | ||
81 | local_name, | ||
82 | )); | ||
83 | } | ||
84 | }; | ||
85 | |||
86 | let docs = match resolution { | ||
87 | ScopeDef::ModuleDef(Module(it)) => it.docs(ctx.db), | ||
88 | ScopeDef::ModuleDef(Adt(it)) => it.docs(ctx.db), | ||
89 | ScopeDef::ModuleDef(EnumVariant(it)) => it.docs(ctx.db), | ||
90 | ScopeDef::ModuleDef(Const(it)) => it.docs(ctx.db), | ||
91 | ScopeDef::ModuleDef(Static(it)) => it.docs(ctx.db), | ||
92 | ScopeDef::ModuleDef(Trait(it)) => it.docs(ctx.db), | ||
93 | ScopeDef::ModuleDef(TypeAlias(it)) => it.docs(ctx.db), | ||
94 | _ => None, | ||
95 | }; | ||
96 | |||
97 | let mut completion_item = | ||
98 | CompletionItem::new(completion_kind, ctx.source_range(), local_name.clone()); | ||
99 | if let ScopeDef::Local(local) = resolution { | ||
100 | let ty = local.ty(ctx.db); | ||
101 | if !ty.is_unknown() { | ||
102 | completion_item = completion_item.detail(ty.display(ctx.db).to_string()); | ||
103 | } | ||
104 | }; | ||
105 | |||
106 | // If not an import, add parenthesis automatically. | ||
107 | if ctx.is_path_type | ||
108 | && !ctx.has_type_args | ||
109 | && ctx.db.feature_flags.get("completion.insertion.add-call-parenthesis") | ||
110 | { | ||
111 | let has_non_default_type_params = match resolution { | ||
112 | ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(ctx.db), | ||
113 | ScopeDef::ModuleDef(TypeAlias(it)) => it.has_non_default_type_params(ctx.db), | ||
114 | _ => false, | ||
115 | }; | ||
116 | if has_non_default_type_params { | ||
117 | tested_by!(inserts_angle_brackets_for_generics); | ||
118 | completion_item = completion_item | ||
119 | .lookup_by(local_name.clone()) | ||
120 | .label(format!("{}<…>", local_name)) | ||
121 | .insert_snippet(format!("{}<$0>", local_name)); | ||
122 | } | ||
123 | } | ||
124 | |||
125 | completion_item.kind(kind).set_documentation(docs).add_to(self) | ||
126 | } | ||
127 | |||
128 | pub(crate) fn add_function(&mut self, ctx: &CompletionContext, func: hir::Function) { | ||
129 | self.add_function_with_name(ctx, None, func) | ||
130 | } | ||
131 | |||
132 | fn guess_macro_braces(&self, macro_name: &str, docs: &str) -> &'static str { | ||
133 | let mut votes = [0, 0, 0]; | ||
134 | for (idx, s) in docs.match_indices(¯o_name) { | ||
135 | let (before, after) = (&docs[..idx], &docs[idx + s.len()..]); | ||
136 | // Ensure to match the full word | ||
137 | if after.starts_with('!') | ||
138 | && before | ||
139 | .chars() | ||
140 | .rev() | ||
141 | .next() | ||
142 | .map_or(true, |c| c != '_' && !c.is_ascii_alphanumeric()) | ||
143 | { | ||
144 | // It may have spaces before the braces like `foo! {}` | ||
145 | match after[1..].chars().find(|&c| !c.is_whitespace()) { | ||
146 | Some('{') => votes[0] += 1, | ||
147 | Some('[') => votes[1] += 1, | ||
148 | Some('(') => votes[2] += 1, | ||
149 | _ => {} | ||
150 | } | ||
151 | } | ||
152 | } | ||
153 | |||
154 | // Insert a space before `{}`. | ||
155 | // We prefer the last one when some votes equal. | ||
156 | *votes.iter().zip(&[" {$0}", "[$0]", "($0)"]).max_by_key(|&(&vote, _)| vote).unwrap().1 | ||
157 | } | ||
158 | |||
159 | pub(crate) fn add_macro( | ||
160 | &mut self, | ||
161 | ctx: &CompletionContext, | ||
162 | name: Option<String>, | ||
163 | macro_: hir::MacroDef, | ||
164 | ) { | ||
165 | let name = match name { | ||
166 | Some(it) => it, | ||
167 | None => return, | ||
168 | }; | ||
169 | |||
170 | let ast_node = macro_.source(ctx.db).value; | ||
171 | let detail = macro_label(&ast_node); | ||
172 | |||
173 | let docs = macro_.docs(ctx.db); | ||
174 | let macro_declaration = format!("{}!", name); | ||
175 | |||
176 | let mut builder = | ||
177 | CompletionItem::new(CompletionKind::Reference, ctx.source_range(), ¯o_declaration) | ||
178 | .kind(CompletionItemKind::Macro) | ||
179 | .set_documentation(docs.clone()) | ||
180 | .set_deprecated(is_deprecated(macro_, ctx.db)) | ||
181 | .detail(detail); | ||
182 | |||
183 | builder = if ctx.use_item_syntax.is_some() { | ||
184 | builder.insert_text(name) | ||
185 | } else { | ||
186 | let macro_braces_to_insert = | ||
187 | self.guess_macro_braces(&name, docs.as_ref().map_or("", |s| s.as_str())); | ||
188 | builder.insert_snippet(macro_declaration + macro_braces_to_insert) | ||
189 | }; | ||
190 | |||
191 | self.add(builder); | ||
192 | } | ||
193 | |||
194 | fn add_function_with_name( | ||
195 | &mut self, | ||
196 | ctx: &CompletionContext, | ||
197 | name: Option<String>, | ||
198 | func: hir::Function, | ||
199 | ) { | ||
200 | let func_name = func.name(ctx.db); | ||
201 | let has_self_param = func.has_self_param(ctx.db); | ||
202 | let params = func.params(ctx.db); | ||
203 | |||
204 | let name = name.unwrap_or_else(|| func_name.to_string()); | ||
205 | let ast_node = func.source(ctx.db).value; | ||
206 | let detail = function_label(&ast_node); | ||
207 | |||
208 | let mut builder = | ||
209 | CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.clone()) | ||
210 | .kind(if has_self_param { | ||
211 | CompletionItemKind::Method | ||
212 | } else { | ||
213 | CompletionItemKind::Function | ||
214 | }) | ||
215 | .set_documentation(func.docs(ctx.db)) | ||
216 | .set_deprecated(is_deprecated(func, ctx.db)) | ||
217 | .detail(detail); | ||
218 | |||
219 | // Add `<>` for generic types | ||
220 | if ctx.use_item_syntax.is_none() | ||
221 | && !ctx.is_call | ||
222 | && ctx.db.feature_flags.get("completion.insertion.add-call-parenthesis") | ||
223 | { | ||
224 | tested_by!(inserts_parens_for_function_calls); | ||
225 | let (snippet, label) = if params.is_empty() || has_self_param && params.len() == 1 { | ||
226 | (format!("{}()$0", func_name), format!("{}()", name)) | ||
227 | } else { | ||
228 | (format!("{}($0)", func_name), format!("{}(…)", name)) | ||
229 | }; | ||
230 | builder = builder.lookup_by(name).label(label).insert_snippet(snippet); | ||
231 | } | ||
232 | |||
233 | self.add(builder) | ||
234 | } | ||
235 | |||
236 | pub(crate) fn add_const(&mut self, ctx: &CompletionContext, constant: hir::Const) { | ||
237 | let ast_node = constant.source(ctx.db).value; | ||
238 | let name = match ast_node.name() { | ||
239 | Some(name) => name, | ||
240 | _ => return, | ||
241 | }; | ||
242 | let detail = const_label(&ast_node); | ||
243 | |||
244 | CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.text().to_string()) | ||
245 | .kind(CompletionItemKind::Const) | ||
246 | .set_documentation(constant.docs(ctx.db)) | ||
247 | .set_deprecated(is_deprecated(constant, ctx.db)) | ||
248 | .detail(detail) | ||
249 | .add_to(self); | ||
250 | } | ||
251 | |||
252 | pub(crate) fn add_type_alias(&mut self, ctx: &CompletionContext, type_alias: hir::TypeAlias) { | ||
253 | let type_def = type_alias.source(ctx.db).value; | ||
254 | let name = match type_def.name() { | ||
255 | Some(name) => name, | ||
256 | _ => return, | ||
257 | }; | ||
258 | let detail = type_label(&type_def); | ||
259 | |||
260 | CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.text().to_string()) | ||
261 | .kind(CompletionItemKind::TypeAlias) | ||
262 | .set_documentation(type_alias.docs(ctx.db)) | ||
263 | .set_deprecated(is_deprecated(type_alias, ctx.db)) | ||
264 | .detail(detail) | ||
265 | .add_to(self); | ||
266 | } | ||
267 | |||
268 | pub(crate) fn add_enum_variant(&mut self, ctx: &CompletionContext, variant: hir::EnumVariant) { | ||
269 | let is_deprecated = is_deprecated(variant, ctx.db); | ||
270 | let name = match variant.name(ctx.db) { | ||
271 | Some(it) => it, | ||
272 | None => return, | ||
273 | }; | ||
274 | let detail_types = variant.fields(ctx.db).into_iter().map(|field| field.ty(ctx.db)); | ||
275 | let detail = join(detail_types.map(|t| t.display(ctx.db).to_string())) | ||
276 | .separator(", ") | ||
277 | .surround_with("(", ")") | ||
278 | .to_string(); | ||
279 | CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.to_string()) | ||
280 | .kind(CompletionItemKind::EnumVariant) | ||
281 | .set_documentation(variant.docs(ctx.db)) | ||
282 | .set_deprecated(is_deprecated) | ||
283 | .detail(detail) | ||
284 | .add_to(self); | ||
285 | } | ||
286 | } | ||
287 | |||
288 | fn is_deprecated(node: impl HasAttrs, db: &impl HirDatabase) -> bool { | ||
289 | node.attrs(db).by_key("deprecated").exists() | ||
290 | } | ||
291 | |||
292 | #[cfg(test)] | ||
293 | mod tests { | ||
294 | use insta::assert_debug_snapshot; | ||
295 | use test_utils::covers; | ||
296 | |||
297 | use crate::completion::{do_completion, CompletionItem, CompletionKind}; | ||
298 | |||
299 | fn do_reference_completion(code: &str) -> Vec<CompletionItem> { | ||
300 | do_completion(code, CompletionKind::Reference) | ||
301 | } | ||
302 | |||
303 | #[test] | ||
304 | fn sets_deprecated_flag_in_completion_items() { | ||
305 | assert_debug_snapshot!( | ||
306 | do_reference_completion( | ||
307 | r#" | ||
308 | #[deprecated] | ||
309 | fn something_deprecated() {} | ||
310 | |||
311 | #[deprecated(since = "1.0.0")] | ||
312 | fn something_else_deprecated() {} | ||
313 | |||
314 | fn main() { som<|> } | ||
315 | "#, | ||
316 | ), | ||
317 | @r###" | ||
318 | [ | ||
319 | CompletionItem { | ||
320 | label: "main()", | ||
321 | source_range: [203; 206), | ||
322 | delete: [203; 206), | ||
323 | insert: "main()$0", | ||
324 | kind: Function, | ||
325 | lookup: "main", | ||
326 | detail: "fn main()", | ||
327 | }, | ||
328 | CompletionItem { | ||
329 | label: "something_deprecated()", | ||
330 | source_range: [203; 206), | ||
331 | delete: [203; 206), | ||
332 | insert: "something_deprecated()$0", | ||
333 | kind: Function, | ||
334 | lookup: "something_deprecated", | ||
335 | detail: "fn something_deprecated()", | ||
336 | deprecated: true, | ||
337 | }, | ||
338 | CompletionItem { | ||
339 | label: "something_else_deprecated()", | ||
340 | source_range: [203; 206), | ||
341 | delete: [203; 206), | ||
342 | insert: "something_else_deprecated()$0", | ||
343 | kind: Function, | ||
344 | lookup: "something_else_deprecated", | ||
345 | detail: "fn something_else_deprecated()", | ||
346 | deprecated: true, | ||
347 | }, | ||
348 | ] | ||
349 | "### | ||
350 | ); | ||
351 | } | ||
352 | |||
353 | #[test] | ||
354 | fn inserts_parens_for_function_calls() { | ||
355 | covers!(inserts_parens_for_function_calls); | ||
356 | assert_debug_snapshot!( | ||
357 | do_reference_completion( | ||
358 | r" | ||
359 | fn no_args() {} | ||
360 | fn main() { no_<|> } | ||
361 | " | ||
362 | ), | ||
363 | @r###" | ||
364 | [ | ||
365 | CompletionItem { | ||
366 | label: "main()", | ||
367 | source_range: [61; 64), | ||
368 | delete: [61; 64), | ||
369 | insert: "main()$0", | ||
370 | kind: Function, | ||
371 | lookup: "main", | ||
372 | detail: "fn main()", | ||
373 | }, | ||
374 | CompletionItem { | ||
375 | label: "no_args()", | ||
376 | source_range: [61; 64), | ||
377 | delete: [61; 64), | ||
378 | insert: "no_args()$0", | ||
379 | kind: Function, | ||
380 | lookup: "no_args", | ||
381 | detail: "fn no_args()", | ||
382 | }, | ||
383 | ] | ||
384 | "### | ||
385 | ); | ||
386 | assert_debug_snapshot!( | ||
387 | do_reference_completion( | ||
388 | r" | ||
389 | fn with_args(x: i32, y: String) {} | ||
390 | fn main() { with_<|> } | ||
391 | " | ||
392 | ), | ||
393 | @r###" | ||
394 | [ | ||
395 | CompletionItem { | ||
396 | label: "main()", | ||
397 | source_range: [80; 85), | ||
398 | delete: [80; 85), | ||
399 | insert: "main()$0", | ||
400 | kind: Function, | ||
401 | lookup: "main", | ||
402 | detail: "fn main()", | ||
403 | }, | ||
404 | CompletionItem { | ||
405 | label: "with_args(…)", | ||
406 | source_range: [80; 85), | ||
407 | delete: [80; 85), | ||
408 | insert: "with_args($0)", | ||
409 | kind: Function, | ||
410 | lookup: "with_args", | ||
411 | detail: "fn with_args(x: i32, y: String)", | ||
412 | }, | ||
413 | ] | ||
414 | "### | ||
415 | ); | ||
416 | assert_debug_snapshot!( | ||
417 | do_reference_completion( | ||
418 | r" | ||
419 | struct S {} | ||
420 | impl S { | ||
421 | fn foo(&self) {} | ||
422 | } | ||
423 | fn bar(s: &S) { | ||
424 | s.f<|> | ||
425 | } | ||
426 | " | ||
427 | ), | ||
428 | @r###" | ||
429 | [ | ||
430 | CompletionItem { | ||
431 | label: "foo()", | ||
432 | source_range: [163; 164), | ||
433 | delete: [163; 164), | ||
434 | insert: "foo()$0", | ||
435 | kind: Method, | ||
436 | lookup: "foo", | ||
437 | detail: "fn foo(&self)", | ||
438 | }, | ||
439 | ] | ||
440 | "### | ||
441 | ); | ||
442 | } | ||
443 | |||
444 | #[test] | ||
445 | fn dont_render_function_parens_in_use_item() { | ||
446 | assert_debug_snapshot!( | ||
447 | do_reference_completion( | ||
448 | " | ||
449 | //- /lib.rs | ||
450 | mod m { pub fn foo() {} } | ||
451 | use crate::m::f<|>; | ||
452 | " | ||
453 | ), | ||
454 | @r###" | ||
455 | [ | ||
456 | CompletionItem { | ||
457 | label: "foo", | ||
458 | source_range: [40; 41), | ||
459 | delete: [40; 41), | ||
460 | insert: "foo", | ||
461 | kind: Function, | ||
462 | detail: "pub fn foo()", | ||
463 | }, | ||
464 | ] | ||
465 | "### | ||
466 | ); | ||
467 | } | ||
468 | |||
469 | #[test] | ||
470 | fn dont_render_function_parens_if_already_call() { | ||
471 | assert_debug_snapshot!( | ||
472 | do_reference_completion( | ||
473 | " | ||
474 | //- /lib.rs | ||
475 | fn frobnicate() {} | ||
476 | fn main() { | ||
477 | frob<|>(); | ||
478 | } | ||
479 | " | ||
480 | ), | ||
481 | @r###" | ||
482 | [ | ||
483 | CompletionItem { | ||
484 | label: "frobnicate", | ||
485 | source_range: [35; 39), | ||
486 | delete: [35; 39), | ||
487 | insert: "frobnicate", | ||
488 | kind: Function, | ||
489 | detail: "fn frobnicate()", | ||
490 | }, | ||
491 | CompletionItem { | ||
492 | label: "main", | ||
493 | source_range: [35; 39), | ||
494 | delete: [35; 39), | ||
495 | insert: "main", | ||
496 | kind: Function, | ||
497 | detail: "fn main()", | ||
498 | }, | ||
499 | ] | ||
500 | "### | ||
501 | ); | ||
502 | assert_debug_snapshot!( | ||
503 | do_reference_completion( | ||
504 | " | ||
505 | //- /lib.rs | ||
506 | struct Foo {} | ||
507 | impl Foo { fn new() -> Foo {} } | ||
508 | fn main() { | ||
509 | Foo::ne<|>(); | ||
510 | } | ||
511 | " | ||
512 | ), | ||
513 | @r###" | ||
514 | [ | ||
515 | CompletionItem { | ||
516 | label: "new", | ||
517 | source_range: [67; 69), | ||
518 | delete: [67; 69), | ||
519 | insert: "new", | ||
520 | kind: Function, | ||
521 | detail: "fn new() -> Foo", | ||
522 | }, | ||
523 | ] | ||
524 | "### | ||
525 | ); | ||
526 | } | ||
527 | |||
528 | #[test] | ||
529 | fn inserts_angle_brackets_for_generics() { | ||
530 | covers!(inserts_angle_brackets_for_generics); | ||
531 | assert_debug_snapshot!( | ||
532 | do_reference_completion( | ||
533 | r" | ||
534 | struct Vec<T> {} | ||
535 | fn foo(xs: Ve<|>) | ||
536 | " | ||
537 | ), | ||
538 | @r###" | ||
539 | [ | ||
540 | CompletionItem { | ||
541 | label: "Vec<…>", | ||
542 | source_range: [61; 63), | ||
543 | delete: [61; 63), | ||
544 | insert: "Vec<$0>", | ||
545 | kind: Struct, | ||
546 | lookup: "Vec", | ||
547 | }, | ||
548 | CompletionItem { | ||
549 | label: "foo(…)", | ||
550 | source_range: [61; 63), | ||
551 | delete: [61; 63), | ||
552 | insert: "foo($0)", | ||
553 | kind: Function, | ||
554 | lookup: "foo", | ||
555 | detail: "fn foo(xs: Ve)", | ||
556 | }, | ||
557 | ] | ||
558 | "### | ||
559 | ); | ||
560 | assert_debug_snapshot!( | ||
561 | do_reference_completion( | ||
562 | r" | ||
563 | type Vec<T> = (T,); | ||
564 | fn foo(xs: Ve<|>) | ||
565 | " | ||
566 | ), | ||
567 | @r###" | ||
568 | [ | ||
569 | CompletionItem { | ||
570 | label: "Vec<…>", | ||
571 | source_range: [64; 66), | ||
572 | delete: [64; 66), | ||
573 | insert: "Vec<$0>", | ||
574 | kind: TypeAlias, | ||
575 | lookup: "Vec", | ||
576 | }, | ||
577 | CompletionItem { | ||
578 | label: "foo(…)", | ||
579 | source_range: [64; 66), | ||
580 | delete: [64; 66), | ||
581 | insert: "foo($0)", | ||
582 | kind: Function, | ||
583 | lookup: "foo", | ||
584 | detail: "fn foo(xs: Ve)", | ||
585 | }, | ||
586 | ] | ||
587 | "### | ||
588 | ); | ||
589 | assert_debug_snapshot!( | ||
590 | do_reference_completion( | ||
591 | r" | ||
592 | struct Vec<T = i128> {} | ||
593 | fn foo(xs: Ve<|>) | ||
594 | " | ||
595 | ), | ||
596 | @r###" | ||
597 | [ | ||
598 | CompletionItem { | ||
599 | label: "Vec", | ||
600 | source_range: [68; 70), | ||
601 | delete: [68; 70), | ||
602 | insert: "Vec", | ||
603 | kind: Struct, | ||
604 | }, | ||
605 | CompletionItem { | ||
606 | label: "foo(…)", | ||
607 | source_range: [68; 70), | ||
608 | delete: [68; 70), | ||
609 | insert: "foo($0)", | ||
610 | kind: Function, | ||
611 | lookup: "foo", | ||
612 | detail: "fn foo(xs: Ve)", | ||
613 | }, | ||
614 | ] | ||
615 | "### | ||
616 | ); | ||
617 | assert_debug_snapshot!( | ||
618 | do_reference_completion( | ||
619 | r" | ||
620 | struct Vec<T> {} | ||
621 | fn foo(xs: Ve<|><i128>) | ||
622 | " | ||
623 | ), | ||
624 | @r###" | ||
625 | [ | ||
626 | CompletionItem { | ||
627 | label: "Vec", | ||
628 | source_range: [61; 63), | ||
629 | delete: [61; 63), | ||
630 | insert: "Vec", | ||
631 | kind: Struct, | ||
632 | }, | ||
633 | CompletionItem { | ||
634 | label: "foo(…)", | ||
635 | source_range: [61; 63), | ||
636 | delete: [61; 63), | ||
637 | insert: "foo($0)", | ||
638 | kind: Function, | ||
639 | lookup: "foo", | ||
640 | detail: "fn foo(xs: Ve<i128>)", | ||
641 | }, | ||
642 | ] | ||
643 | "### | ||
644 | ); | ||
645 | } | ||
646 | |||
647 | #[test] | ||
648 | fn dont_insert_macro_call_braces_in_use() { | ||
649 | assert_debug_snapshot!( | ||
650 | do_reference_completion( | ||
651 | r" | ||
652 | //- /main.rs | ||
653 | use foo::<|>; | ||
654 | |||
655 | //- /foo/lib.rs | ||
656 | #[macro_export] | ||
657 | macro_rules frobnicate { | ||
658 | () => () | ||
659 | } | ||
660 | " | ||
661 | ), | ||
662 | @r###" | ||
663 | [ | ||
664 | CompletionItem { | ||
665 | label: "frobnicate!", | ||
666 | source_range: [9; 9), | ||
667 | delete: [9; 9), | ||
668 | insert: "frobnicate", | ||
669 | kind: Macro, | ||
670 | detail: "#[macro_export]\nmacro_rules! frobnicate", | ||
671 | }, | ||
672 | ] | ||
673 | "### | ||
674 | ) | ||
675 | } | ||
676 | } | ||