]> git.lizzy.rs Git - rust.git/blob - crates/ra_ide_api/src/completion/presentation.rs
20242d293c0e89fad00e9881f889fe440180d71b
[rust.git] / crates / ra_ide_api / src / completion / presentation.rs
1 //! This modules takes care of rendering various definitions as completion items.
2
3 use hir::{db::HirDatabase, Docs, HasSource, HirDisplay, ScopeDef, Ty, TypeWalk};
4 use join_to_string::join;
5 use ra_syntax::ast::NameOwner;
6 use test_utils::tested_by;
7
8 use crate::completion::{
9     db, 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         substs: &hir::Substs,
20     ) {
21         CompletionItem::new(
22             CompletionKind::Reference,
23             ctx.source_range(),
24             field.name(ctx.db).to_string(),
25         )
26         .kind(CompletionItemKind::Field)
27         .detail(field.ty(ctx.db).subst(substs).display(ctx.db).to_string())
28         .set_documentation(field.docs(ctx.db))
29         .add_to(self);
30     }
31
32     pub(crate) fn add_tuple_field(&mut self, ctx: &CompletionContext, field: usize, ty: &hir::Ty) {
33         CompletionItem::new(CompletionKind::Reference, ctx.source_range(), field.to_string())
34             .kind(CompletionItemKind::Field)
35             .detail(ty.display(ctx.db).to_string())
36             .add_to(self);
37     }
38
39     pub(crate) fn add_resolution(
40         &mut self,
41         ctx: &CompletionContext,
42         local_name: String,
43         resolution: &ScopeDef,
44     ) {
45         use hir::ModuleDef::*;
46
47         let completion_kind = match resolution {
48             ScopeDef::ModuleDef(BuiltinType(..)) => CompletionKind::BuiltinType,
49             _ => CompletionKind::Reference,
50         };
51
52         let kind = match resolution {
53             ScopeDef::ModuleDef(Module(..)) => CompletionItemKind::Module,
54             ScopeDef::ModuleDef(Function(func)) => {
55                 return self.add_function_with_name(ctx, Some(local_name), *func);
56             }
57             ScopeDef::ModuleDef(Adt(hir::Adt::Struct(_))) => CompletionItemKind::Struct,
58             // FIXME: add CompletionItemKind::Union
59             ScopeDef::ModuleDef(Adt(hir::Adt::Union(_))) => CompletionItemKind::Struct,
60             ScopeDef::ModuleDef(Adt(hir::Adt::Enum(_))) => CompletionItemKind::Enum,
61
62             ScopeDef::ModuleDef(EnumVariant(..)) => CompletionItemKind::EnumVariant,
63             ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::Const,
64             ScopeDef::ModuleDef(Static(..)) => CompletionItemKind::Static,
65             ScopeDef::ModuleDef(Trait(..)) => CompletionItemKind::Trait,
66             ScopeDef::ModuleDef(TypeAlias(..)) => CompletionItemKind::TypeAlias,
67             ScopeDef::ModuleDef(BuiltinType(..)) => CompletionItemKind::BuiltinType,
68             ScopeDef::GenericParam(..) => CompletionItemKind::TypeParam,
69             ScopeDef::LocalBinding(..) => CompletionItemKind::Binding,
70             // (does this need its own kind?)
71             ScopeDef::AdtSelfType(..) | ScopeDef::ImplSelfType(..) => CompletionItemKind::TypeParam,
72             ScopeDef::MacroDef(mac) => {
73                 return self.add_macro(ctx, Some(local_name), *mac);
74             }
75             ScopeDef::Unknown => {
76                 return self.add(CompletionItem::new(
77                     CompletionKind::Reference,
78                     ctx.source_range(),
79                     local_name,
80                 ));
81             }
82         };
83
84         let docs = match resolution {
85             ScopeDef::ModuleDef(Module(it)) => it.docs(ctx.db),
86             ScopeDef::ModuleDef(Adt(it)) => it.docs(ctx.db),
87             ScopeDef::ModuleDef(EnumVariant(it)) => it.docs(ctx.db),
88             ScopeDef::ModuleDef(Const(it)) => it.docs(ctx.db),
89             ScopeDef::ModuleDef(Static(it)) => it.docs(ctx.db),
90             ScopeDef::ModuleDef(Trait(it)) => it.docs(ctx.db),
91             ScopeDef::ModuleDef(TypeAlias(it)) => it.docs(ctx.db),
92             _ => None,
93         };
94
95         let mut completion_item =
96             CompletionItem::new(completion_kind, ctx.source_range(), local_name.clone());
97         if let ScopeDef::LocalBinding(pat_id) = resolution {
98             let ty = ctx
99                 .analyzer
100                 .type_of_pat_by_id(ctx.db, pat_id.clone())
101                 .filter(|t| t != &Ty::Unknown)
102                 .map(|t| t.display(ctx.db).to_string());
103             completion_item = completion_item.set_detail(ty);
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 generic_def: Option<hir::GenericDef> = match resolution {
112                 ScopeDef::ModuleDef(Adt(it)) => Some((*it).into()),
113                 ScopeDef::ModuleDef(TypeAlias(it)) => Some((*it).into()),
114                 _ => None,
115             };
116             if let Some(def) = generic_def {
117                 if has_non_default_type_params(def, ctx.db) {
118                     tested_by!(inserts_angle_brackets_for_generics);
119                     completion_item = completion_item
120                         .lookup_by(local_name.clone())
121                         .label(format!("{}<…>", local_name))
122                         .insert_snippet(format!("{}<$0>", local_name));
123                 }
124             }
125         }
126
127         completion_item.kind(kind).set_documentation(docs).add_to(self)
128     }
129
130     pub(crate) fn add_function(&mut self, ctx: &CompletionContext, func: hir::Function) {
131         self.add_function_with_name(ctx, None, func)
132     }
133
134     pub(crate) fn add_macro(
135         &mut self,
136         ctx: &CompletionContext,
137         name: Option<String>,
138         macro_: hir::MacroDef,
139     ) {
140         let ast_node = macro_.source(ctx.db).ast;
141         if let Some(name) = name {
142             let detail = macro_label(&ast_node);
143
144             let macro_braces_to_insert = match name.as_str() {
145                 "vec" => "[$0]",
146                 _ => "($0)",
147             };
148             let macro_declaration = name + "!";
149
150             let builder = CompletionItem::new(
151                 CompletionKind::Reference,
152                 ctx.source_range(),
153                 &macro_declaration,
154             )
155             .kind(CompletionItemKind::Macro)
156             .set_documentation(macro_.docs(ctx.db))
157             .detail(detail)
158             .insert_snippet(macro_declaration + macro_braces_to_insert);
159
160             self.add(builder);
161         }
162     }
163
164     fn add_function_with_name(
165         &mut self,
166         ctx: &CompletionContext,
167         name: Option<String>,
168         func: hir::Function,
169     ) {
170         let data = func.data(ctx.db);
171         let name = name.unwrap_or_else(|| data.name().to_string());
172         let ast_node = func.source(ctx.db).ast;
173         let detail = function_label(&ast_node);
174
175         let mut builder =
176             CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.clone())
177                 .kind(if data.has_self_param() {
178                     CompletionItemKind::Method
179                 } else {
180                     CompletionItemKind::Function
181                 })
182                 .set_documentation(func.docs(ctx.db))
183                 .detail(detail);
184
185         // Add `<>` for generic types
186         if ctx.use_item_syntax.is_none()
187             && !ctx.is_call
188             && ctx.db.feature_flags.get("completion.insertion.add-call-parenthesis")
189         {
190             tested_by!(inserts_parens_for_function_calls);
191             let (snippet, label) =
192                 if data.params().is_empty() || data.has_self_param() && data.params().len() == 1 {
193                     (format!("{}()$0", data.name()), format!("{}()", name))
194                 } else {
195                     (format!("{}($0)", data.name()), format!("{}(…)", name))
196                 };
197             builder = builder.lookup_by(name.clone()).label(label).insert_snippet(snippet);
198         }
199
200         self.add(builder)
201     }
202
203     pub(crate) fn add_const(&mut self, ctx: &CompletionContext, constant: hir::Const) {
204         let ast_node = constant.source(ctx.db).ast;
205         let name = match ast_node.name() {
206             Some(name) => name,
207             _ => return,
208         };
209         let detail = const_label(&ast_node);
210
211         CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.text().to_string())
212             .kind(CompletionItemKind::Const)
213             .set_documentation(constant.docs(ctx.db))
214             .detail(detail)
215             .add_to(self);
216     }
217
218     pub(crate) fn add_type_alias(&mut self, ctx: &CompletionContext, type_alias: hir::TypeAlias) {
219         let type_def = type_alias.source(ctx.db).ast;
220         let name = match type_def.name() {
221             Some(name) => name,
222             _ => return,
223         };
224         let detail = type_label(&type_def);
225
226         CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.text().to_string())
227             .kind(CompletionItemKind::TypeAlias)
228             .set_documentation(type_alias.docs(ctx.db))
229             .detail(detail)
230             .add_to(self);
231     }
232
233     pub(crate) fn add_enum_variant(&mut self, ctx: &CompletionContext, variant: hir::EnumVariant) {
234         let name = match variant.name(ctx.db) {
235             Some(it) => it,
236             None => return,
237         };
238         let detail_types = variant.fields(ctx.db).into_iter().map(|field| field.ty(ctx.db));
239         let detail = join(detail_types.map(|t| t.display(ctx.db).to_string()))
240             .separator(", ")
241             .surround_with("(", ")")
242             .to_string();
243         CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.to_string())
244             .kind(CompletionItemKind::EnumVariant)
245             .set_documentation(variant.docs(ctx.db))
246             .detail(detail)
247             .add_to(self);
248     }
249 }
250
251 fn has_non_default_type_params(def: hir::GenericDef, db: &db::RootDatabase) -> bool {
252     let subst = db.generic_defaults(def);
253     subst.iter().any(|ty| ty == &Ty::Unknown)
254 }
255
256 #[cfg(test)]
257 mod tests {
258     use crate::completion::{do_completion, CompletionItem, CompletionKind};
259     use insta::assert_debug_snapshot;
260     use test_utils::covers;
261
262     fn do_reference_completion(code: &str) -> Vec<CompletionItem> {
263         do_completion(code, CompletionKind::Reference)
264     }
265
266     #[test]
267     fn inserts_parens_for_function_calls() {
268         covers!(inserts_parens_for_function_calls);
269         assert_debug_snapshot!(
270             do_reference_completion(
271                 r"
272                 fn no_args() {}
273                 fn main() { no_<|> }
274                 "
275             ),
276             @r###"
277         [
278             CompletionItem {
279                 label: "main()",
280                 source_range: [61; 64),
281                 delete: [61; 64),
282                 insert: "main()$0",
283                 kind: Function,
284                 lookup: "main",
285                 detail: "fn main()",
286             },
287             CompletionItem {
288                 label: "no_args()",
289                 source_range: [61; 64),
290                 delete: [61; 64),
291                 insert: "no_args()$0",
292                 kind: Function,
293                 lookup: "no_args",
294                 detail: "fn no_args()",
295             },
296         ]
297         "###
298         );
299         assert_debug_snapshot!(
300             do_reference_completion(
301                 r"
302                 fn with_args(x: i32, y: String) {}
303                 fn main() { with_<|> }
304                 "
305             ),
306             @r###"
307         [
308             CompletionItem {
309                 label: "main()",
310                 source_range: [80; 85),
311                 delete: [80; 85),
312                 insert: "main()$0",
313                 kind: Function,
314                 lookup: "main",
315                 detail: "fn main()",
316             },
317             CompletionItem {
318                 label: "with_args(…)",
319                 source_range: [80; 85),
320                 delete: [80; 85),
321                 insert: "with_args($0)",
322                 kind: Function,
323                 lookup: "with_args",
324                 detail: "fn with_args(x: i32, y: String)",
325             },
326         ]
327         "###
328         );
329         assert_debug_snapshot!(
330             do_reference_completion(
331                 r"
332                 struct S {}
333                 impl S {
334                     fn foo(&self) {}
335                 }
336                 fn bar(s: &S) {
337                     s.f<|>
338                 }
339                 "
340             ),
341             @r###"
342         [
343             CompletionItem {
344                 label: "foo()",
345                 source_range: [163; 164),
346                 delete: [163; 164),
347                 insert: "foo()$0",
348                 kind: Method,
349                 lookup: "foo",
350                 detail: "fn foo(&self)",
351             },
352         ]
353         "###
354         );
355     }
356
357     #[test]
358     fn dont_render_function_parens_in_use_item() {
359         assert_debug_snapshot!(
360             do_reference_completion(
361                 "
362                 //- /lib.rs
363                 mod m { pub fn foo() {} }
364                 use crate::m::f<|>;
365                 "
366             ),
367             @r#"[
368     CompletionItem {
369         label: "foo",
370         source_range: [40; 41),
371         delete: [40; 41),
372         insert: "foo",
373         kind: Function,
374         detail: "pub fn foo()",
375     },
376 ]"#
377         );
378     }
379
380     #[test]
381     fn dont_render_function_parens_if_already_call() {
382         assert_debug_snapshot!(
383             do_reference_completion(
384                 "
385                 //- /lib.rs
386                 fn frobnicate() {}
387                 fn main() {
388                     frob<|>();
389                 }
390                 "
391             ),
392             @r#"[
393     CompletionItem {
394         label: "frobnicate",
395         source_range: [35; 39),
396         delete: [35; 39),
397         insert: "frobnicate",
398         kind: Function,
399         detail: "fn frobnicate()",
400     },
401     CompletionItem {
402         label: "main",
403         source_range: [35; 39),
404         delete: [35; 39),
405         insert: "main",
406         kind: Function,
407         detail: "fn main()",
408     },
409 ]"#
410         );
411         assert_debug_snapshot!(
412             do_reference_completion(
413                 "
414                 //- /lib.rs
415                 struct Foo {}
416                 impl Foo { fn new() -> Foo {} }
417                 fn main() {
418                     Foo::ne<|>();
419                 }
420                 "
421             ),
422             @r#"[
423     CompletionItem {
424         label: "new",
425         source_range: [67; 69),
426         delete: [67; 69),
427         insert: "new",
428         kind: Function,
429         detail: "fn new() -> Foo",
430     },
431 ]"#
432         );
433     }
434
435     #[test]
436     fn inserts_angle_brackets_for_generics() {
437         covers!(inserts_angle_brackets_for_generics);
438         assert_debug_snapshot!(
439             do_reference_completion(
440                 r"
441                 struct Vec<T> {}
442                 fn foo(xs: Ve<|>)
443                 "
444             ),
445             @r###"
446         [
447             CompletionItem {
448                 label: "Vec<…>",
449                 source_range: [61; 63),
450                 delete: [61; 63),
451                 insert: "Vec<$0>",
452                 kind: Struct,
453                 lookup: "Vec",
454             },
455             CompletionItem {
456                 label: "foo(…)",
457                 source_range: [61; 63),
458                 delete: [61; 63),
459                 insert: "foo($0)",
460                 kind: Function,
461                 lookup: "foo",
462                 detail: "fn foo(xs: Ve)",
463             },
464         ]
465         "###
466         );
467         assert_debug_snapshot!(
468             do_reference_completion(
469                 r"
470                 type Vec<T> = (T,);
471                 fn foo(xs: Ve<|>)
472                 "
473             ),
474             @r###"
475         [
476             CompletionItem {
477                 label: "Vec<…>",
478                 source_range: [64; 66),
479                 delete: [64; 66),
480                 insert: "Vec<$0>",
481                 kind: TypeAlias,
482                 lookup: "Vec",
483             },
484             CompletionItem {
485                 label: "foo(…)",
486                 source_range: [64; 66),
487                 delete: [64; 66),
488                 insert: "foo($0)",
489                 kind: Function,
490                 lookup: "foo",
491                 detail: "fn foo(xs: Ve)",
492             },
493         ]
494         "###
495         );
496         assert_debug_snapshot!(
497             do_reference_completion(
498                 r"
499                 struct Vec<T = i128> {}
500                 fn foo(xs: Ve<|>)
501                 "
502             ),
503             @r###"
504         [
505             CompletionItem {
506                 label: "Vec",
507                 source_range: [68; 70),
508                 delete: [68; 70),
509                 insert: "Vec",
510                 kind: Struct,
511             },
512             CompletionItem {
513                 label: "foo(…)",
514                 source_range: [68; 70),
515                 delete: [68; 70),
516                 insert: "foo($0)",
517                 kind: Function,
518                 lookup: "foo",
519                 detail: "fn foo(xs: Ve)",
520             },
521         ]
522         "###
523         );
524         assert_debug_snapshot!(
525             do_reference_completion(
526                 r"
527                 struct Vec<T> {}
528                 fn foo(xs: Ve<|><i128>)
529                 "
530             ),
531             @r###"
532         [
533             CompletionItem {
534                 label: "Vec",
535                 source_range: [61; 63),
536                 delete: [61; 63),
537                 insert: "Vec",
538                 kind: Struct,
539             },
540             CompletionItem {
541                 label: "foo(…)",
542                 source_range: [61; 63),
543                 delete: [61; 63),
544                 insert: "foo($0)",
545                 kind: Function,
546                 lookup: "foo",
547                 detail: "fn foo(xs: Ve<i128>)",
548             },
549         ]
550         "###
551         );
552     }
553 }