aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/completion/presentation.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src/completion/presentation.rs')
-rw-r--r--crates/ra_ide/src/completion/presentation.rs673
1 files changed, 673 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..97475fc0b
--- /dev/null
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -0,0 +1,673 @@
1//! This modules takes care of rendering various definitions as completion items.
2
3use hir::{db::HirDatabase, Docs, HasAttrs, HasSource, HirDisplay, ScopeDef, Type};
4use join_to_string::join;
5use ra_syntax::ast::NameOwner;
6use test_utils::tested_by;
7
8use crate::completion::{
9 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions,
10};
11
12use crate::display::{const_label, function_label, macro_label, type_label};
13
14impl 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(&macro_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(), &macro_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 = variant.name(ctx.db);
271 let detail_types = variant.fields(ctx.db).into_iter().map(|field| field.ty(ctx.db));
272 let detail = join(detail_types.map(|t| t.display(ctx.db).to_string()))
273 .separator(", ")
274 .surround_with("(", ")")
275 .to_string();
276 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.to_string())
277 .kind(CompletionItemKind::EnumVariant)
278 .set_documentation(variant.docs(ctx.db))
279 .set_deprecated(is_deprecated)
280 .detail(detail)
281 .add_to(self);
282 }
283}
284
285fn is_deprecated(node: impl HasAttrs, db: &impl HirDatabase) -> bool {
286 node.attrs(db).by_key("deprecated").exists()
287}
288
289#[cfg(test)]
290mod tests {
291 use insta::assert_debug_snapshot;
292 use test_utils::covers;
293
294 use crate::completion::{do_completion, CompletionItem, CompletionKind};
295
296 fn do_reference_completion(code: &str) -> Vec<CompletionItem> {
297 do_completion(code, CompletionKind::Reference)
298 }
299
300 #[test]
301 fn sets_deprecated_flag_in_completion_items() {
302 assert_debug_snapshot!(
303 do_reference_completion(
304 r#"
305 #[deprecated]
306 fn something_deprecated() {}
307
308 #[deprecated(since = "1.0.0")]
309 fn something_else_deprecated() {}
310
311 fn main() { som<|> }
312 "#,
313 ),
314 @r###"
315 [
316 CompletionItem {
317 label: "main()",
318 source_range: [203; 206),
319 delete: [203; 206),
320 insert: "main()$0",
321 kind: Function,
322 lookup: "main",
323 detail: "fn main()",
324 },
325 CompletionItem {
326 label: "something_deprecated()",
327 source_range: [203; 206),
328 delete: [203; 206),
329 insert: "something_deprecated()$0",
330 kind: Function,
331 lookup: "something_deprecated",
332 detail: "fn something_deprecated()",
333 deprecated: true,
334 },
335 CompletionItem {
336 label: "something_else_deprecated()",
337 source_range: [203; 206),
338 delete: [203; 206),
339 insert: "something_else_deprecated()$0",
340 kind: Function,
341 lookup: "something_else_deprecated",
342 detail: "fn something_else_deprecated()",
343 deprecated: true,
344 },
345 ]
346 "###
347 );
348 }
349
350 #[test]
351 fn inserts_parens_for_function_calls() {
352 covers!(inserts_parens_for_function_calls);
353 assert_debug_snapshot!(
354 do_reference_completion(
355 r"
356 fn no_args() {}
357 fn main() { no_<|> }
358 "
359 ),
360 @r###"
361 [
362 CompletionItem {
363 label: "main()",
364 source_range: [61; 64),
365 delete: [61; 64),
366 insert: "main()$0",
367 kind: Function,
368 lookup: "main",
369 detail: "fn main()",
370 },
371 CompletionItem {
372 label: "no_args()",
373 source_range: [61; 64),
374 delete: [61; 64),
375 insert: "no_args()$0",
376 kind: Function,
377 lookup: "no_args",
378 detail: "fn no_args()",
379 },
380 ]
381 "###
382 );
383 assert_debug_snapshot!(
384 do_reference_completion(
385 r"
386 fn with_args(x: i32, y: String) {}
387 fn main() { with_<|> }
388 "
389 ),
390 @r###"
391 [
392 CompletionItem {
393 label: "main()",
394 source_range: [80; 85),
395 delete: [80; 85),
396 insert: "main()$0",
397 kind: Function,
398 lookup: "main",
399 detail: "fn main()",
400 },
401 CompletionItem {
402 label: "with_args(…)",
403 source_range: [80; 85),
404 delete: [80; 85),
405 insert: "with_args($0)",
406 kind: Function,
407 lookup: "with_args",
408 detail: "fn with_args(x: i32, y: String)",
409 },
410 ]
411 "###
412 );
413 assert_debug_snapshot!(
414 do_reference_completion(
415 r"
416 struct S {}
417 impl S {
418 fn foo(&self) {}
419 }
420 fn bar(s: &S) {
421 s.f<|>
422 }
423 "
424 ),
425 @r###"
426 [
427 CompletionItem {
428 label: "foo()",
429 source_range: [163; 164),
430 delete: [163; 164),
431 insert: "foo()$0",
432 kind: Method,
433 lookup: "foo",
434 detail: "fn foo(&self)",
435 },
436 ]
437 "###
438 );
439 }
440
441 #[test]
442 fn dont_render_function_parens_in_use_item() {
443 assert_debug_snapshot!(
444 do_reference_completion(
445 "
446 //- /lib.rs
447 mod m { pub fn foo() {} }
448 use crate::m::f<|>;
449 "
450 ),
451 @r###"
452 [
453 CompletionItem {
454 label: "foo",
455 source_range: [40; 41),
456 delete: [40; 41),
457 insert: "foo",
458 kind: Function,
459 detail: "pub fn foo()",
460 },
461 ]
462 "###
463 );
464 }
465
466 #[test]
467 fn dont_render_function_parens_if_already_call() {
468 assert_debug_snapshot!(
469 do_reference_completion(
470 "
471 //- /lib.rs
472 fn frobnicate() {}
473 fn main() {
474 frob<|>();
475 }
476 "
477 ),
478 @r###"
479 [
480 CompletionItem {
481 label: "frobnicate",
482 source_range: [35; 39),
483 delete: [35; 39),
484 insert: "frobnicate",
485 kind: Function,
486 detail: "fn frobnicate()",
487 },
488 CompletionItem {
489 label: "main",
490 source_range: [35; 39),
491 delete: [35; 39),
492 insert: "main",
493 kind: Function,
494 detail: "fn main()",
495 },
496 ]
497 "###
498 );
499 assert_debug_snapshot!(
500 do_reference_completion(
501 "
502 //- /lib.rs
503 struct Foo {}
504 impl Foo { fn new() -> Foo {} }
505 fn main() {
506 Foo::ne<|>();
507 }
508 "
509 ),
510 @r###"
511 [
512 CompletionItem {
513 label: "new",
514 source_range: [67; 69),
515 delete: [67; 69),
516 insert: "new",
517 kind: Function,
518 detail: "fn new() -> Foo",
519 },
520 ]
521 "###
522 );
523 }
524
525 #[test]
526 fn inserts_angle_brackets_for_generics() {
527 covers!(inserts_angle_brackets_for_generics);
528 assert_debug_snapshot!(
529 do_reference_completion(
530 r"
531 struct Vec<T> {}
532 fn foo(xs: Ve<|>)
533 "
534 ),
535 @r###"
536 [
537 CompletionItem {
538 label: "Vec<…>",
539 source_range: [61; 63),
540 delete: [61; 63),
541 insert: "Vec<$0>",
542 kind: Struct,
543 lookup: "Vec",
544 },
545 CompletionItem {
546 label: "foo(…)",
547 source_range: [61; 63),
548 delete: [61; 63),
549 insert: "foo($0)",
550 kind: Function,
551 lookup: "foo",
552 detail: "fn foo(xs: Ve)",
553 },
554 ]
555 "###
556 );
557 assert_debug_snapshot!(
558 do_reference_completion(
559 r"
560 type Vec<T> = (T,);
561 fn foo(xs: Ve<|>)
562 "
563 ),
564 @r###"
565 [
566 CompletionItem {
567 label: "Vec<…>",
568 source_range: [64; 66),
569 delete: [64; 66),
570 insert: "Vec<$0>",
571 kind: TypeAlias,
572 lookup: "Vec",
573 },
574 CompletionItem {
575 label: "foo(…)",
576 source_range: [64; 66),
577 delete: [64; 66),
578 insert: "foo($0)",
579 kind: Function,
580 lookup: "foo",
581 detail: "fn foo(xs: Ve)",
582 },
583 ]
584 "###
585 );
586 assert_debug_snapshot!(
587 do_reference_completion(
588 r"
589 struct Vec<T = i128> {}
590 fn foo(xs: Ve<|>)
591 "
592 ),
593 @r###"
594 [
595 CompletionItem {
596 label: "Vec",
597 source_range: [68; 70),
598 delete: [68; 70),
599 insert: "Vec",
600 kind: Struct,
601 },
602 CompletionItem {
603 label: "foo(…)",
604 source_range: [68; 70),
605 delete: [68; 70),
606 insert: "foo($0)",
607 kind: Function,
608 lookup: "foo",
609 detail: "fn foo(xs: Ve)",
610 },
611 ]
612 "###
613 );
614 assert_debug_snapshot!(
615 do_reference_completion(
616 r"
617 struct Vec<T> {}
618 fn foo(xs: Ve<|><i128>)
619 "
620 ),
621 @r###"
622 [
623 CompletionItem {
624 label: "Vec",
625 source_range: [61; 63),
626 delete: [61; 63),
627 insert: "Vec",
628 kind: Struct,
629 },
630 CompletionItem {
631 label: "foo(…)",
632 source_range: [61; 63),
633 delete: [61; 63),
634 insert: "foo($0)",
635 kind: Function,
636 lookup: "foo",
637 detail: "fn foo(xs: Ve<i128>)",
638 },
639 ]
640 "###
641 );
642 }
643
644 #[test]
645 fn dont_insert_macro_call_braces_in_use() {
646 assert_debug_snapshot!(
647 do_reference_completion(
648 r"
649 //- /main.rs
650 use foo::<|>;
651
652 //- /foo/lib.rs
653 #[macro_export]
654 macro_rules frobnicate {
655 () => ()
656 }
657 "
658 ),
659 @r###"
660 [
661 CompletionItem {
662 label: "frobnicate!",
663 source_range: [9; 9),
664 delete: [9; 9),
665 insert: "frobnicate",
666 kind: Macro,
667 detail: "#[macro_export]\nmacro_rules! frobnicate",
668 },
669 ]
670 "###
671 )
672 }
673}