aboutsummaryrefslogtreecommitdiff
path: root/crates/completion/src/presentation.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/completion/src/presentation.rs')
-rw-r--r--crates/completion/src/presentation.rs1341
1 files changed, 1341 insertions, 0 deletions
diff --git a/crates/completion/src/presentation.rs b/crates/completion/src/presentation.rs
new file mode 100644
index 000000000..0a0dc1ce5
--- /dev/null
+++ b/crates/completion/src/presentation.rs
@@ -0,0 +1,1341 @@
1//! This modules takes care of rendering various definitions as completion items.
2//! It also handles scoring (sorting) completions.
3
4use hir::{HasAttrs, HasSource, HirDisplay, ModPath, ScopeDef, StructKind, Type};
5use itertools::Itertools;
6use syntax::{ast::NameOwner, display::*};
7use test_utils::mark;
8
9use crate::{
10 // display::{const_label, function_declaration, macro_label, type_label},
11 CompletionScore,
12 RootDatabase,
13 {
14 completion_item::Builder, CompletionContext, CompletionItem, CompletionItemKind,
15 CompletionKind, Completions,
16 },
17};
18
19impl Completions {
20 pub(crate) fn add_field(&mut self, ctx: &CompletionContext, field: hir::Field, ty: &Type) {
21 let is_deprecated = is_deprecated(field, ctx.db);
22 let name = field.name(ctx.db);
23 let mut completion_item =
24 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.to_string())
25 .kind(CompletionItemKind::Field)
26 .detail(ty.display(ctx.db).to_string())
27 .set_documentation(field.docs(ctx.db))
28 .set_deprecated(is_deprecated);
29
30 if let Some(score) = compute_score(ctx, &ty, &name.to_string()) {
31 completion_item = completion_item.set_score(score);
32 }
33
34 completion_item.add_to(self);
35 }
36
37 pub(crate) fn add_tuple_field(&mut self, ctx: &CompletionContext, field: usize, ty: &Type) {
38 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), field.to_string())
39 .kind(CompletionItemKind::Field)
40 .detail(ty.display(ctx.db).to_string())
41 .add_to(self);
42 }
43
44 pub(crate) fn add_resolution(
45 &mut self,
46 ctx: &CompletionContext,
47 local_name: String,
48 resolution: &ScopeDef,
49 ) {
50 use hir::ModuleDef::*;
51
52 let completion_kind = match resolution {
53 ScopeDef::ModuleDef(BuiltinType(..)) => CompletionKind::BuiltinType,
54 _ => CompletionKind::Reference,
55 };
56
57 let kind = match resolution {
58 ScopeDef::ModuleDef(Module(..)) => CompletionItemKind::Module,
59 ScopeDef::ModuleDef(Function(func)) => {
60 return self.add_function(ctx, *func, Some(local_name));
61 }
62 ScopeDef::ModuleDef(Adt(hir::Adt::Struct(_))) => CompletionItemKind::Struct,
63 // FIXME: add CompletionItemKind::Union
64 ScopeDef::ModuleDef(Adt(hir::Adt::Union(_))) => CompletionItemKind::Struct,
65 ScopeDef::ModuleDef(Adt(hir::Adt::Enum(_))) => CompletionItemKind::Enum,
66
67 ScopeDef::ModuleDef(EnumVariant(var)) => {
68 return self.add_enum_variant(ctx, *var, Some(local_name));
69 }
70 ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::Const,
71 ScopeDef::ModuleDef(Static(..)) => CompletionItemKind::Static,
72 ScopeDef::ModuleDef(Trait(..)) => CompletionItemKind::Trait,
73 ScopeDef::ModuleDef(TypeAlias(..)) => CompletionItemKind::TypeAlias,
74 ScopeDef::ModuleDef(BuiltinType(..)) => CompletionItemKind::BuiltinType,
75 ScopeDef::GenericParam(..) => CompletionItemKind::TypeParam,
76 ScopeDef::Local(..) => CompletionItemKind::Binding,
77 // (does this need its own kind?)
78 ScopeDef::AdtSelfType(..) | ScopeDef::ImplSelfType(..) => CompletionItemKind::TypeParam,
79 ScopeDef::MacroDef(mac) => {
80 return self.add_macro(ctx, Some(local_name), *mac);
81 }
82 ScopeDef::Unknown => {
83 return self.add(
84 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), local_name)
85 .kind(CompletionItemKind::UnresolvedReference),
86 );
87 }
88 };
89
90 let docs = match resolution {
91 ScopeDef::ModuleDef(Module(it)) => it.docs(ctx.db),
92 ScopeDef::ModuleDef(Adt(it)) => it.docs(ctx.db),
93 ScopeDef::ModuleDef(EnumVariant(it)) => it.docs(ctx.db),
94 ScopeDef::ModuleDef(Const(it)) => it.docs(ctx.db),
95 ScopeDef::ModuleDef(Static(it)) => it.docs(ctx.db),
96 ScopeDef::ModuleDef(Trait(it)) => it.docs(ctx.db),
97 ScopeDef::ModuleDef(TypeAlias(it)) => it.docs(ctx.db),
98 _ => None,
99 };
100
101 let mut completion_item =
102 CompletionItem::new(completion_kind, ctx.source_range(), local_name.clone());
103 if let ScopeDef::Local(local) = resolution {
104 let ty = local.ty(ctx.db);
105 if !ty.is_unknown() {
106 completion_item = completion_item.detail(ty.display(ctx.db).to_string());
107 }
108 };
109
110 if let ScopeDef::Local(local) = resolution {
111 if let Some(score) = compute_score(ctx, &local.ty(ctx.db), &local_name) {
112 completion_item = completion_item.set_score(score);
113 }
114 }
115
116 // Add `<>` for generic types
117 if ctx.is_path_type && !ctx.has_type_args && ctx.config.add_call_parenthesis {
118 if let Some(cap) = ctx.config.snippet_cap {
119 let has_non_default_type_params = match resolution {
120 ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(ctx.db),
121 ScopeDef::ModuleDef(TypeAlias(it)) => it.has_non_default_type_params(ctx.db),
122 _ => false,
123 };
124 if has_non_default_type_params {
125 mark::hit!(inserts_angle_brackets_for_generics);
126 completion_item = completion_item
127 .lookup_by(local_name.clone())
128 .label(format!("{}<…>", local_name))
129 .insert_snippet(cap, format!("{}<$0>", local_name));
130 }
131 }
132 }
133
134 completion_item.kind(kind).set_documentation(docs).add_to(self)
135 }
136
137 pub(crate) fn add_macro(
138 &mut self,
139 ctx: &CompletionContext,
140 name: Option<String>,
141 macro_: hir::MacroDef,
142 ) {
143 // FIXME: Currently proc-macro do not have ast-node,
144 // such that it does not have source
145 if macro_.is_proc_macro() {
146 return;
147 }
148
149 let name = match name {
150 Some(it) => it,
151 None => return,
152 };
153
154 let ast_node = macro_.source(ctx.db).value;
155 let detail = macro_label(&ast_node);
156
157 let docs = macro_.docs(ctx.db);
158
159 let mut builder = CompletionItem::new(
160 CompletionKind::Reference,
161 ctx.source_range(),
162 &format!("{}!", name),
163 )
164 .kind(CompletionItemKind::Macro)
165 .set_documentation(docs.clone())
166 .set_deprecated(is_deprecated(macro_, ctx.db))
167 .detail(detail);
168
169 let needs_bang = ctx.use_item_syntax.is_none() && !ctx.is_macro_call;
170 builder = match ctx.config.snippet_cap {
171 Some(cap) if needs_bang => {
172 let docs = docs.as_ref().map_or("", |s| s.as_str());
173 let (bra, ket) = guess_macro_braces(&name, docs);
174 builder
175 .insert_snippet(cap, format!("{}!{}$0{}", name, bra, ket))
176 .label(format!("{}!{}…{}", name, bra, ket))
177 .lookup_by(format!("{}!", name))
178 }
179 None if needs_bang => builder.insert_text(format!("{}!", name)),
180 _ => {
181 mark::hit!(dont_insert_macro_call_parens_unncessary);
182 builder.insert_text(name)
183 }
184 };
185
186 self.add(builder);
187 }
188
189 pub(crate) fn add_function(
190 &mut self,
191 ctx: &CompletionContext,
192 func: hir::Function,
193 local_name: Option<String>,
194 ) {
195 fn add_arg(arg: &str, ty: &Type, ctx: &CompletionContext) -> String {
196 if let Some(derefed_ty) = ty.remove_ref() {
197 for (name, local) in ctx.locals.iter() {
198 if name == arg && local.ty(ctx.db) == derefed_ty {
199 return (if ty.is_mutable_reference() { "&mut " } else { "&" }).to_string()
200 + &arg.to_string();
201 }
202 }
203 }
204 arg.to_string()
205 };
206 let name = local_name.unwrap_or_else(|| func.name(ctx.db).to_string());
207 let ast_node = func.source(ctx.db).value;
208
209 let mut builder =
210 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.clone())
211 .kind(if func.self_param(ctx.db).is_some() {
212 CompletionItemKind::Method
213 } else {
214 CompletionItemKind::Function
215 })
216 .set_documentation(func.docs(ctx.db))
217 .set_deprecated(is_deprecated(func, ctx.db))
218 .detail(function_declaration(&ast_node));
219
220 let params_ty = func.params(ctx.db);
221 let params = ast_node
222 .param_list()
223 .into_iter()
224 .flat_map(|it| it.params())
225 .zip(params_ty)
226 .flat_map(|(it, param_ty)| {
227 if let Some(pat) = it.pat() {
228 let name = pat.to_string();
229 let arg = name.trim_start_matches("mut ").trim_start_matches('_');
230 return Some(add_arg(arg, param_ty.ty(), ctx));
231 }
232 None
233 })
234 .collect();
235
236 builder = builder.add_call_parens(ctx, name, Params::Named(params));
237
238 self.add(builder)
239 }
240
241 pub(crate) fn add_const(&mut self, ctx: &CompletionContext, constant: hir::Const) {
242 let ast_node = constant.source(ctx.db).value;
243 let name = match ast_node.name() {
244 Some(name) => name,
245 _ => return,
246 };
247 let detail = const_label(&ast_node);
248
249 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.text().to_string())
250 .kind(CompletionItemKind::Const)
251 .set_documentation(constant.docs(ctx.db))
252 .set_deprecated(is_deprecated(constant, ctx.db))
253 .detail(detail)
254 .add_to(self);
255 }
256
257 pub(crate) fn add_type_alias(&mut self, ctx: &CompletionContext, type_alias: hir::TypeAlias) {
258 let type_def = type_alias.source(ctx.db).value;
259 let name = match type_def.name() {
260 Some(name) => name,
261 _ => return,
262 };
263 let detail = type_label(&type_def);
264
265 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.text().to_string())
266 .kind(CompletionItemKind::TypeAlias)
267 .set_documentation(type_alias.docs(ctx.db))
268 .set_deprecated(is_deprecated(type_alias, ctx.db))
269 .detail(detail)
270 .add_to(self);
271 }
272
273 pub(crate) fn add_qualified_enum_variant(
274 &mut self,
275 ctx: &CompletionContext,
276 variant: hir::EnumVariant,
277 path: ModPath,
278 ) {
279 self.add_enum_variant_impl(ctx, variant, None, Some(path))
280 }
281
282 pub(crate) fn add_enum_variant(
283 &mut self,
284 ctx: &CompletionContext,
285 variant: hir::EnumVariant,
286 local_name: Option<String>,
287 ) {
288 self.add_enum_variant_impl(ctx, variant, local_name, None)
289 }
290
291 fn add_enum_variant_impl(
292 &mut self,
293 ctx: &CompletionContext,
294 variant: hir::EnumVariant,
295 local_name: Option<String>,
296 path: Option<ModPath>,
297 ) {
298 let is_deprecated = is_deprecated(variant, ctx.db);
299 let name = local_name.unwrap_or_else(|| variant.name(ctx.db).to_string());
300 let qualified_name = match &path {
301 Some(it) => it.to_string(),
302 None => name.to_string(),
303 };
304 let detail_types = variant
305 .fields(ctx.db)
306 .into_iter()
307 .map(|field| (field.name(ctx.db), field.signature_ty(ctx.db)));
308 let variant_kind = variant.kind(ctx.db);
309 let detail = match variant_kind {
310 StructKind::Tuple | StructKind::Unit => format!(
311 "({})",
312 detail_types.map(|(_, t)| t.display(ctx.db).to_string()).format(", ")
313 ),
314 StructKind::Record => format!(
315 "{{ {} }}",
316 detail_types
317 .map(|(n, t)| format!("{}: {}", n, t.display(ctx.db).to_string()))
318 .format(", ")
319 ),
320 };
321 let mut res = CompletionItem::new(
322 CompletionKind::Reference,
323 ctx.source_range(),
324 qualified_name.clone(),
325 )
326 .kind(CompletionItemKind::EnumVariant)
327 .set_documentation(variant.docs(ctx.db))
328 .set_deprecated(is_deprecated)
329 .detail(detail);
330
331 if path.is_some() {
332 res = res.lookup_by(name);
333 }
334
335 if variant_kind == StructKind::Tuple {
336 mark::hit!(inserts_parens_for_tuple_enums);
337 let params = Params::Anonymous(variant.fields(ctx.db).len());
338 res = res.add_call_parens(ctx, qualified_name, params)
339 }
340
341 res.add_to(self);
342 }
343}
344
345pub(crate) fn compute_score(
346 ctx: &CompletionContext,
347 ty: &Type,
348 name: &str,
349) -> Option<CompletionScore> {
350 let (active_name, active_type) = if let Some(record_field) = &ctx.record_field_syntax {
351 mark::hit!(record_field_type_match);
352 let (struct_field, _local) = ctx.sema.resolve_record_field(record_field)?;
353 (struct_field.name(ctx.db).to_string(), struct_field.signature_ty(ctx.db))
354 } else if let Some(active_parameter) = &ctx.active_parameter {
355 mark::hit!(active_param_type_match);
356 (active_parameter.name.clone(), active_parameter.ty.clone())
357 } else {
358 return None;
359 };
360
361 // Compute score
362 // For the same type
363 if &active_type != ty {
364 return None;
365 }
366
367 let mut res = CompletionScore::TypeMatch;
368
369 // If same type + same name then go top position
370 if active_name == name {
371 res = CompletionScore::TypeAndNameMatch
372 }
373
374 Some(res)
375}
376
377enum Params {
378 Named(Vec<String>),
379 Anonymous(usize),
380}
381
382impl Params {
383 fn len(&self) -> usize {
384 match self {
385 Params::Named(xs) => xs.len(),
386 Params::Anonymous(len) => *len,
387 }
388 }
389
390 fn is_empty(&self) -> bool {
391 self.len() == 0
392 }
393}
394
395impl Builder {
396 fn add_call_parens(mut self, ctx: &CompletionContext, name: String, params: Params) -> Builder {
397 if !ctx.config.add_call_parenthesis {
398 return self;
399 }
400 if ctx.use_item_syntax.is_some() {
401 mark::hit!(no_parens_in_use_item);
402 return self;
403 }
404 if ctx.is_pattern_call {
405 mark::hit!(dont_duplicate_pattern_parens);
406 return self;
407 }
408 if ctx.is_call {
409 return self;
410 }
411
412 // Don't add parentheses if the expected type is some function reference.
413 if let Some(ty) = &ctx.expected_type {
414 if ty.is_fn() {
415 mark::hit!(no_call_parens_if_fn_ptr_needed);
416 return self;
417 }
418 }
419
420 let cap = match ctx.config.snippet_cap {
421 Some(it) => it,
422 None => return self,
423 };
424 // If not an import, add parenthesis automatically.
425 mark::hit!(inserts_parens_for_function_calls);
426
427 let (snippet, label) = if params.is_empty() {
428 (format!("{}()$0", name), format!("{}()", name))
429 } else {
430 self = self.trigger_call_info();
431 let snippet = match (ctx.config.add_call_argument_snippets, params) {
432 (true, Params::Named(params)) => {
433 let function_params_snippet =
434 params.iter().enumerate().format_with(", ", |(index, param_name), f| {
435 f(&format_args!("${{{}:{}}}", index + 1, param_name))
436 });
437 format!("{}({})$0", name, function_params_snippet)
438 }
439 _ => {
440 mark::hit!(suppress_arg_snippets);
441 format!("{}($0)", name)
442 }
443 };
444
445 (snippet, format!("{}(…)", name))
446 };
447 self.lookup_by(name).label(label).insert_snippet(cap, snippet)
448 }
449}
450
451fn is_deprecated(node: impl HasAttrs, db: &RootDatabase) -> bool {
452 node.attrs(db).by_key("deprecated").exists()
453}
454
455fn guess_macro_braces(macro_name: &str, docs: &str) -> (&'static str, &'static str) {
456 let mut votes = [0, 0, 0];
457 for (idx, s) in docs.match_indices(&macro_name) {
458 let (before, after) = (&docs[..idx], &docs[idx + s.len()..]);
459 // Ensure to match the full word
460 if after.starts_with('!')
461 && !before.ends_with(|c: char| c == '_' || c.is_ascii_alphanumeric())
462 {
463 // It may have spaces before the braces like `foo! {}`
464 match after[1..].chars().find(|&c| !c.is_whitespace()) {
465 Some('{') => votes[0] += 1,
466 Some('[') => votes[1] += 1,
467 Some('(') => votes[2] += 1,
468 _ => {}
469 }
470 }
471 }
472
473 // Insert a space before `{}`.
474 // We prefer the last one when some votes equal.
475 let (_vote, (bra, ket)) = votes
476 .iter()
477 .zip(&[(" {", "}"), ("[", "]"), ("(", ")")])
478 .max_by_key(|&(&vote, _)| vote)
479 .unwrap();
480 (*bra, *ket)
481}
482
483#[cfg(test)]
484mod tests {
485 use std::cmp::Reverse;
486
487 use expect_test::{expect, Expect};
488 use test_utils::mark;
489
490 use crate::{
491 test_utils::{check_edit, check_edit_with_config, do_completion, get_all_completion_items},
492 CompletionConfig, CompletionKind, CompletionScore,
493 };
494
495 fn check(ra_fixture: &str, expect: Expect) {
496 let actual = do_completion(ra_fixture, CompletionKind::Reference);
497 expect.assert_debug_eq(&actual);
498 }
499
500 fn check_scores(ra_fixture: &str, expect: Expect) {
501 fn display_score(score: Option<CompletionScore>) -> &'static str {
502 match score {
503 Some(CompletionScore::TypeMatch) => "[type]",
504 Some(CompletionScore::TypeAndNameMatch) => "[type+name]",
505 None => "[]".into(),
506 }
507 }
508
509 let mut completions = get_all_completion_items(CompletionConfig::default(), ra_fixture);
510 completions.sort_by_key(|it| (Reverse(it.score()), it.label().to_string()));
511 let actual = completions
512 .into_iter()
513 .filter(|it| it.completion_kind == CompletionKind::Reference)
514 .map(|it| {
515 let tag = it.kind().unwrap().tag();
516 let score = display_score(it.score());
517 format!("{} {} {}\n", tag, it.label(), score)
518 })
519 .collect::<String>();
520 expect.assert_eq(&actual);
521 }
522
523 #[test]
524 fn enum_detail_includes_record_fields() {
525 check(
526 r#"
527enum Foo { Foo { x: i32, y: i32 } }
528
529fn main() { Foo::Fo<|> }
530"#,
531 expect![[r#"
532 [
533 CompletionItem {
534 label: "Foo",
535 source_range: 54..56,
536 delete: 54..56,
537 insert: "Foo",
538 kind: EnumVariant,
539 detail: "{ x: i32, y: i32 }",
540 },
541 ]
542 "#]],
543 );
544 }
545
546 #[test]
547 fn enum_detail_doesnt_include_tuple_fields() {
548 check(
549 r#"
550enum Foo { Foo (i32, i32) }
551
552fn main() { Foo::Fo<|> }
553"#,
554 expect![[r#"
555 [
556 CompletionItem {
557 label: "Foo(…)",
558 source_range: 46..48,
559 delete: 46..48,
560 insert: "Foo($0)",
561 kind: EnumVariant,
562 lookup: "Foo",
563 detail: "(i32, i32)",
564 trigger_call_info: true,
565 },
566 ]
567 "#]],
568 );
569 }
570
571 #[test]
572 fn enum_detail_just_parentheses_for_unit() {
573 check(
574 r#"
575enum Foo { Foo }
576
577fn main() { Foo::Fo<|> }
578"#,
579 expect![[r#"
580 [
581 CompletionItem {
582 label: "Foo",
583 source_range: 35..37,
584 delete: 35..37,
585 insert: "Foo",
586 kind: EnumVariant,
587 detail: "()",
588 },
589 ]
590 "#]],
591 );
592 }
593
594 #[test]
595 fn sets_deprecated_flag_in_completion_items() {
596 check(
597 r#"
598#[deprecated]
599fn something_deprecated() {}
600#[deprecated(since = "1.0.0")]
601fn something_else_deprecated() {}
602
603fn main() { som<|> }
604"#,
605 expect![[r#"
606 [
607 CompletionItem {
608 label: "main()",
609 source_range: 121..124,
610 delete: 121..124,
611 insert: "main()$0",
612 kind: Function,
613 lookup: "main",
614 detail: "fn main()",
615 },
616 CompletionItem {
617 label: "something_deprecated()",
618 source_range: 121..124,
619 delete: 121..124,
620 insert: "something_deprecated()$0",
621 kind: Function,
622 lookup: "something_deprecated",
623 detail: "fn something_deprecated()",
624 deprecated: true,
625 },
626 CompletionItem {
627 label: "something_else_deprecated()",
628 source_range: 121..124,
629 delete: 121..124,
630 insert: "something_else_deprecated()$0",
631 kind: Function,
632 lookup: "something_else_deprecated",
633 detail: "fn something_else_deprecated()",
634 deprecated: true,
635 },
636 ]
637 "#]],
638 );
639
640 check(
641 r#"
642struct A { #[deprecated] the_field: u32 }
643fn foo() { A { the<|> } }
644"#,
645 expect![[r#"
646 [
647 CompletionItem {
648 label: "the_field",
649 source_range: 57..60,
650 delete: 57..60,
651 insert: "the_field",
652 kind: Field,
653 detail: "u32",
654 deprecated: true,
655 },
656 ]
657 "#]],
658 );
659 }
660
661 #[test]
662 fn renders_docs() {
663 check(
664 r#"
665struct S {
666 /// Field docs
667 foo:
668}
669impl S {
670 /// Method docs
671 fn bar(self) { self.<|> }
672}"#,
673 expect![[r#"
674 [
675 CompletionItem {
676 label: "bar()",
677 source_range: 94..94,
678 delete: 94..94,
679 insert: "bar()$0",
680 kind: Method,
681 lookup: "bar",
682 detail: "fn bar(self)",
683 documentation: Documentation(
684 "Method docs",
685 ),
686 },
687 CompletionItem {
688 label: "foo",
689 source_range: 94..94,
690 delete: 94..94,
691 insert: "foo",
692 kind: Field,
693 detail: "{unknown}",
694 documentation: Documentation(
695 "Field docs",
696 ),
697 },
698 ]
699 "#]],
700 );
701
702 check(
703 r#"
704use self::my<|>;
705
706/// mod docs
707mod my { }
708
709/// enum docs
710enum E {
711 /// variant docs
712 V
713}
714use self::E::*;
715"#,
716 expect![[r#"
717 [
718 CompletionItem {
719 label: "E",
720 source_range: 10..12,
721 delete: 10..12,
722 insert: "E",
723 kind: Enum,
724 documentation: Documentation(
725 "enum docs",
726 ),
727 },
728 CompletionItem {
729 label: "V",
730 source_range: 10..12,
731 delete: 10..12,
732 insert: "V",
733 kind: EnumVariant,
734 detail: "()",
735 documentation: Documentation(
736 "variant docs",
737 ),
738 },
739 CompletionItem {
740 label: "my",
741 source_range: 10..12,
742 delete: 10..12,
743 insert: "my",
744 kind: Module,
745 documentation: Documentation(
746 "mod docs",
747 ),
748 },
749 ]
750 "#]],
751 )
752 }
753
754 #[test]
755 fn dont_render_attrs() {
756 check(
757 r#"
758struct S;
759impl S {
760 #[inline]
761 fn the_method(&self) { }
762}
763fn foo(s: S) { s.<|> }
764"#,
765 expect![[r#"
766 [
767 CompletionItem {
768 label: "the_method()",
769 source_range: 81..81,
770 delete: 81..81,
771 insert: "the_method()$0",
772 kind: Method,
773 lookup: "the_method",
774 detail: "fn the_method(&self)",
775 },
776 ]
777 "#]],
778 )
779 }
780
781 #[test]
782 fn inserts_parens_for_function_calls() {
783 mark::check!(inserts_parens_for_function_calls);
784 check_edit(
785 "no_args",
786 r#"
787fn no_args() {}
788fn main() { no_<|> }
789"#,
790 r#"
791fn no_args() {}
792fn main() { no_args()$0 }
793"#,
794 );
795
796 check_edit(
797 "with_args",
798 r#"
799fn with_args(x: i32, y: String) {}
800fn main() { with_<|> }
801"#,
802 r#"
803fn with_args(x: i32, y: String) {}
804fn main() { with_args(${1:x}, ${2:y})$0 }
805"#,
806 );
807
808 check_edit(
809 "foo",
810 r#"
811struct S;
812impl S {
813 fn foo(&self) {}
814}
815fn bar(s: &S) { s.f<|> }
816"#,
817 r#"
818struct S;
819impl S {
820 fn foo(&self) {}
821}
822fn bar(s: &S) { s.foo()$0 }
823"#,
824 );
825
826 check_edit(
827 "foo",
828 r#"
829struct S {}
830impl S {
831 fn foo(&self, x: i32) {}
832}
833fn bar(s: &S) {
834 s.f<|>
835}
836"#,
837 r#"
838struct S {}
839impl S {
840 fn foo(&self, x: i32) {}
841}
842fn bar(s: &S) {
843 s.foo(${1:x})$0
844}
845"#,
846 );
847 }
848
849 #[test]
850 fn suppress_arg_snippets() {
851 mark::check!(suppress_arg_snippets);
852 check_edit_with_config(
853 CompletionConfig { add_call_argument_snippets: false, ..CompletionConfig::default() },
854 "with_args",
855 r#"
856fn with_args(x: i32, y: String) {}
857fn main() { with_<|> }
858"#,
859 r#"
860fn with_args(x: i32, y: String) {}
861fn main() { with_args($0) }
862"#,
863 );
864 }
865
866 #[test]
867 fn strips_underscores_from_args() {
868 check_edit(
869 "foo",
870 r#"
871fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {}
872fn main() { f<|> }
873"#,
874 r#"
875fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {}
876fn main() { foo(${1:foo}, ${2:bar}, ${3:ho_ge_})$0 }
877"#,
878 );
879 }
880
881 #[test]
882 fn insert_ref_when_matching_local_in_scope() {
883 check_edit(
884 "ref_arg",
885 r#"
886struct Foo {}
887fn ref_arg(x: &Foo) {}
888fn main() {
889 let x = Foo {};
890 ref_ar<|>
891}
892"#,
893 r#"
894struct Foo {}
895fn ref_arg(x: &Foo) {}
896fn main() {
897 let x = Foo {};
898 ref_arg(${1:&x})$0
899}
900"#,
901 );
902 }
903
904 #[test]
905 fn insert_mut_ref_when_matching_local_in_scope() {
906 check_edit(
907 "ref_arg",
908 r#"
909struct Foo {}
910fn ref_arg(x: &mut Foo) {}
911fn main() {
912 let x = Foo {};
913 ref_ar<|>
914}
915"#,
916 r#"
917struct Foo {}
918fn ref_arg(x: &mut Foo) {}
919fn main() {
920 let x = Foo {};
921 ref_arg(${1:&mut x})$0
922}
923"#,
924 );
925 }
926
927 #[test]
928 fn insert_ref_when_matching_local_in_scope_for_method() {
929 check_edit(
930 "apply_foo",
931 r#"
932struct Foo {}
933struct Bar {}
934impl Bar {
935 fn apply_foo(&self, x: &Foo) {}
936}
937
938fn main() {
939 let x = Foo {};
940 let y = Bar {};
941 y.<|>
942}
943"#,
944 r#"
945struct Foo {}
946struct Bar {}
947impl Bar {
948 fn apply_foo(&self, x: &Foo) {}
949}
950
951fn main() {
952 let x = Foo {};
953 let y = Bar {};
954 y.apply_foo(${1:&x})$0
955}
956"#,
957 );
958 }
959
960 #[test]
961 fn trim_mut_keyword_in_func_completion() {
962 check_edit(
963 "take_mutably",
964 r#"
965fn take_mutably(mut x: &i32) {}
966
967fn main() {
968 take_m<|>
969}
970"#,
971 r#"
972fn take_mutably(mut x: &i32) {}
973
974fn main() {
975 take_mutably(${1:x})$0
976}
977"#,
978 );
979 }
980
981 #[test]
982 fn inserts_parens_for_tuple_enums() {
983 mark::check!(inserts_parens_for_tuple_enums);
984 check_edit(
985 "Some",
986 r#"
987enum Option<T> { Some(T), None }
988use Option::*;
989fn main() -> Option<i32> {
990 Som<|>
991}
992"#,
993 r#"
994enum Option<T> { Some(T), None }
995use Option::*;
996fn main() -> Option<i32> {
997 Some($0)
998}
999"#,
1000 );
1001 check_edit(
1002 "Some",
1003 r#"
1004enum Option<T> { Some(T), None }
1005use Option::*;
1006fn main(value: Option<i32>) {
1007 match value {
1008 Som<|>
1009 }
1010}
1011"#,
1012 r#"
1013enum Option<T> { Some(T), None }
1014use Option::*;
1015fn main(value: Option<i32>) {
1016 match value {
1017 Some($0)
1018 }
1019}
1020"#,
1021 );
1022 }
1023
1024 #[test]
1025 fn dont_duplicate_pattern_parens() {
1026 mark::check!(dont_duplicate_pattern_parens);
1027 check_edit(
1028 "Var",
1029 r#"
1030enum E { Var(i32) }
1031fn main() {
1032 match E::Var(92) {
1033 E::<|>(92) => (),
1034 }
1035}
1036"#,
1037 r#"
1038enum E { Var(i32) }
1039fn main() {
1040 match E::Var(92) {
1041 E::Var(92) => (),
1042 }
1043}
1044"#,
1045 );
1046 }
1047
1048 #[test]
1049 fn no_call_parens_if_fn_ptr_needed() {
1050 mark::check!(no_call_parens_if_fn_ptr_needed);
1051 check_edit(
1052 "foo",
1053 r#"
1054fn foo(foo: u8, bar: u8) {}
1055struct ManualVtable { f: fn(u8, u8) }
1056
1057fn main() -> ManualVtable {
1058 ManualVtable { f: f<|> }
1059}
1060"#,
1061 r#"
1062fn foo(foo: u8, bar: u8) {}
1063struct ManualVtable { f: fn(u8, u8) }
1064
1065fn main() -> ManualVtable {
1066 ManualVtable { f: foo }
1067}
1068"#,
1069 );
1070 }
1071
1072 #[test]
1073 fn no_parens_in_use_item() {
1074 mark::check!(no_parens_in_use_item);
1075 check_edit(
1076 "foo",
1077 r#"
1078mod m { pub fn foo() {} }
1079use crate::m::f<|>;
1080"#,
1081 r#"
1082mod m { pub fn foo() {} }
1083use crate::m::foo;
1084"#,
1085 );
1086 }
1087
1088 #[test]
1089 fn no_parens_in_call() {
1090 check_edit(
1091 "foo",
1092 r#"
1093fn foo(x: i32) {}
1094fn main() { f<|>(); }
1095"#,
1096 r#"
1097fn foo(x: i32) {}
1098fn main() { foo(); }
1099"#,
1100 );
1101 check_edit(
1102 "foo",
1103 r#"
1104struct Foo;
1105impl Foo { fn foo(&self){} }
1106fn f(foo: &Foo) { foo.f<|>(); }
1107"#,
1108 r#"
1109struct Foo;
1110impl Foo { fn foo(&self){} }
1111fn f(foo: &Foo) { foo.foo(); }
1112"#,
1113 );
1114 }
1115
1116 #[test]
1117 fn inserts_angle_brackets_for_generics() {
1118 mark::check!(inserts_angle_brackets_for_generics);
1119 check_edit(
1120 "Vec",
1121 r#"
1122struct Vec<T> {}
1123fn foo(xs: Ve<|>)
1124"#,
1125 r#"
1126struct Vec<T> {}
1127fn foo(xs: Vec<$0>)
1128"#,
1129 );
1130 check_edit(
1131 "Vec",
1132 r#"
1133type Vec<T> = (T,);
1134fn foo(xs: Ve<|>)
1135"#,
1136 r#"
1137type Vec<T> = (T,);
1138fn foo(xs: Vec<$0>)
1139"#,
1140 );
1141 check_edit(
1142 "Vec",
1143 r#"
1144struct Vec<T = i128> {}
1145fn foo(xs: Ve<|>)
1146"#,
1147 r#"
1148struct Vec<T = i128> {}
1149fn foo(xs: Vec)
1150"#,
1151 );
1152 check_edit(
1153 "Vec",
1154 r#"
1155struct Vec<T> {}
1156fn foo(xs: Ve<|><i128>)
1157"#,
1158 r#"
1159struct Vec<T> {}
1160fn foo(xs: Vec<i128>)
1161"#,
1162 );
1163 }
1164
1165 #[test]
1166 fn dont_insert_macro_call_parens_unncessary() {
1167 mark::check!(dont_insert_macro_call_parens_unncessary);
1168 check_edit(
1169 "frobnicate!",
1170 r#"
1171//- /main.rs crate:main deps:foo
1172use foo::<|>;
1173//- /foo/lib.rs crate:foo
1174#[macro_export]
1175macro_rules frobnicate { () => () }
1176"#,
1177 r#"
1178use foo::frobnicate;
1179"#,
1180 );
1181
1182 check_edit(
1183 "frobnicate!",
1184 r#"
1185macro_rules frobnicate { () => () }
1186fn main() { frob<|>!(); }
1187"#,
1188 r#"
1189macro_rules frobnicate { () => () }
1190fn main() { frobnicate!(); }
1191"#,
1192 );
1193 }
1194
1195 #[test]
1196 fn active_param_score() {
1197 mark::check!(active_param_type_match);
1198 check_scores(
1199 r#"
1200struct S { foo: i64, bar: u32, baz: u32 }
1201fn test(bar: u32) { }
1202fn foo(s: S) { test(s.<|>) }
1203"#,
1204 expect![[r#"
1205 fd bar [type+name]
1206 fd baz [type]
1207 fd foo []
1208 "#]],
1209 );
1210 }
1211
1212 #[test]
1213 fn record_field_scores() {
1214 mark::check!(record_field_type_match);
1215 check_scores(
1216 r#"
1217struct A { foo: i64, bar: u32, baz: u32 }
1218struct B { x: (), y: f32, bar: u32 }
1219fn foo(a: A) { B { bar: a.<|> }; }
1220"#,
1221 expect![[r#"
1222 fd bar [type+name]
1223 fd baz [type]
1224 fd foo []
1225 "#]],
1226 )
1227 }
1228
1229 #[test]
1230 fn record_field_and_call_scores() {
1231 check_scores(
1232 r#"
1233struct A { foo: i64, bar: u32, baz: u32 }
1234struct B { x: (), y: f32, bar: u32 }
1235fn f(foo: i64) { }
1236fn foo(a: A) { B { bar: f(a.<|>) }; }
1237"#,
1238 expect![[r#"
1239 fd foo [type+name]
1240 fd bar []
1241 fd baz []
1242 "#]],
1243 );
1244 check_scores(
1245 r#"
1246struct A { foo: i64, bar: u32, baz: u32 }
1247struct B { x: (), y: f32, bar: u32 }
1248fn f(foo: i64) { }
1249fn foo(a: A) { f(B { bar: a.<|> }); }
1250"#,
1251 expect![[r#"
1252 fd bar [type+name]
1253 fd baz [type]
1254 fd foo []
1255 "#]],
1256 );
1257 }
1258
1259 #[test]
1260 fn prioritize_exact_ref_match() {
1261 check_scores(
1262 r#"
1263struct WorldSnapshot { _f: () };
1264fn go(world: &WorldSnapshot) { go(w<|>) }
1265"#,
1266 expect![[r#"
1267 bn world [type+name]
1268 st WorldSnapshot []
1269 fn go(…) []
1270 "#]],
1271 );
1272 }
1273
1274 #[test]
1275 fn too_many_arguments() {
1276 check_scores(
1277 r#"
1278struct Foo;
1279fn f(foo: &Foo) { f(foo, w<|>) }
1280"#,
1281 expect![[r#"
1282 st Foo []
1283 fn f(…) []
1284 bn foo []
1285 "#]],
1286 );
1287 }
1288
1289 #[test]
1290 fn guesses_macro_braces() {
1291 check_edit(
1292 "vec!",
1293 r#"
1294/// Creates a [`Vec`] containing the arguments.
1295///
1296/// ```
1297/// let v = vec![1, 2, 3];
1298/// assert_eq!(v[0], 1);
1299/// assert_eq!(v[1], 2);
1300/// assert_eq!(v[2], 3);
1301/// ```
1302macro_rules! vec { () => {} }
1303
1304fn fn main() { v<|> }
1305"#,
1306 r#"
1307/// Creates a [`Vec`] containing the arguments.
1308///
1309/// ```
1310/// let v = vec![1, 2, 3];
1311/// assert_eq!(v[0], 1);
1312/// assert_eq!(v[1], 2);
1313/// assert_eq!(v[2], 3);
1314/// ```
1315macro_rules! vec { () => {} }
1316
1317fn fn main() { vec![$0] }
1318"#,
1319 );
1320
1321 check_edit(
1322 "foo!",
1323 r#"
1324/// Foo
1325///
1326/// Don't call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`,
1327/// call as `let _=foo! { hello world };`
1328macro_rules! foo { () => {} }
1329fn main() { <|> }
1330"#,
1331 r#"
1332/// Foo
1333///
1334/// Don't call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`,
1335/// call as `let _=foo! { hello world };`
1336macro_rules! foo { () => {} }
1337fn main() { foo! {$0} }
1338"#,
1339 )
1340 }
1341}