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