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