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