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.rs676
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
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 = 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
288fn is_deprecated(node: impl HasAttrs, db: &impl HirDatabase) -> bool {
289 node.attrs(db).by_key("deprecated").exists()
290}
291
292#[cfg(test)]
293mod 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}