diff options
Diffstat (limited to 'crates/ra_ide/src/completion/presentation.rs')
-rw-r--r-- | crates/ra_ide/src/completion/presentation.rs | 676 |
1 files changed, 676 insertions, 0 deletions
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs new file mode 100644 index 000000000..5f056730a --- /dev/null +++ b/crates/ra_ide/src/completion/presentation.rs | |||
@@ -0,0 +1,676 @@ | |||
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 | } | ||