aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/completion
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src/completion')
-rw-r--r--crates/ra_ide/src/completion/complete_dot.rs173
-rw-r--r--crates/ra_ide/src/completion/complete_fn_param.rs2
-rw-r--r--crates/ra_ide/src/completion/complete_keyword.rs3
-rw-r--r--crates/ra_ide/src/completion/complete_macro_in_item_position.rs3
-rw-r--r--crates/ra_ide/src/completion/complete_path.rs144
-rw-r--r--crates/ra_ide/src/completion/complete_pattern.rs20
-rw-r--r--crates/ra_ide/src/completion/complete_postfix.rs69
-rw-r--r--crates/ra_ide/src/completion/complete_record_literal.rs27
-rw-r--r--crates/ra_ide/src/completion/complete_record_pattern.rs30
-rw-r--r--crates/ra_ide/src/completion/complete_scope.rs76
-rw-r--r--crates/ra_ide/src/completion/complete_snippet.rs2
-rw-r--r--crates/ra_ide/src/completion/complete_trait_impl.rs146
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs116
-rw-r--r--crates/ra_ide/src/completion/completion_item.rs34
-rw-r--r--crates/ra_ide/src/completion/presentation.rs76
-rw-r--r--crates/ra_ide/src/completion/test_utils.rs29
16 files changed, 844 insertions, 106 deletions
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs
index 9145aa183..81e5037aa 100644
--- a/crates/ra_ide/src/completion/complete_dot.rs
+++ b/crates/ra_ide/src/completion/complete_dot.rs
@@ -38,7 +38,7 @@ pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
38fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { 38fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) {
39 for receiver in receiver.autoderef(ctx.db) { 39 for receiver in receiver.autoderef(ctx.db) {
40 for (field, ty) in receiver.fields(ctx.db) { 40 for (field, ty) in receiver.fields(ctx.db) {
41 if ctx.module.map_or(false, |m| !field.is_visible_from(ctx.db, m)) { 41 if ctx.scope().module().map_or(false, |m| !field.is_visible_from(ctx.db, m)) {
42 // Skip private field. FIXME: If the definition location of the 42 // Skip private field. FIXME: If the definition location of the
43 // field is editable, we should show the completion 43 // field is editable, we should show the completion
44 continue; 44 continue;
@@ -53,11 +53,14 @@ fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Ty
53} 53}
54 54
55fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { 55fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) {
56 if let Some(krate) = ctx.module.map(|it| it.krate()) { 56 if let Some(krate) = ctx.krate {
57 let mut seen_methods = FxHashSet::default(); 57 let mut seen_methods = FxHashSet::default();
58 let traits_in_scope = ctx.scope().traits_in_scope(); 58 let traits_in_scope = ctx.scope().traits_in_scope();
59 receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| { 59 receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| {
60 if func.has_self_param(ctx.db) && seen_methods.insert(func.name(ctx.db)) { 60 if func.has_self_param(ctx.db)
61 && ctx.scope().module().map_or(true, |m| func.is_visible_from(ctx.db, m))
62 && seen_methods.insert(func.name(ctx.db))
63 {
61 acc.add_function(ctx, func); 64 acc.add_function(ctx, func);
62 } 65 }
63 None::<()> 66 None::<()>
@@ -67,7 +70,7 @@ fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &T
67 70
68#[cfg(test)] 71#[cfg(test)]
69mod tests { 72mod tests {
70 use crate::completion::{do_completion, CompletionItem, CompletionKind}; 73 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
71 use insta::assert_debug_snapshot; 74 use insta::assert_debug_snapshot;
72 75
73 fn do_ref_completion(code: &str) -> Vec<CompletionItem> { 76 fn do_ref_completion(code: &str) -> Vec<CompletionItem> {
@@ -308,6 +311,39 @@ mod tests {
308 } 311 }
309 312
310 #[test] 313 #[test]
314 fn test_method_completion_private() {
315 assert_debug_snapshot!(
316 do_ref_completion(
317 r"
318 struct A {}
319 mod m {
320 impl super::A {
321 fn private_method(&self) {}
322 pub(super) fn the_method(&self) {}
323 }
324 }
325 fn foo(a: A) {
326 a.<|>
327 }
328 ",
329 ),
330 @r###"
331 [
332 CompletionItem {
333 label: "the_method()",
334 source_range: [256; 256),
335 delete: [256; 256),
336 insert: "the_method()$0",
337 kind: Method,
338 lookup: "the_method",
339 detail: "pub(super) fn the_method(&self)",
340 },
341 ]
342 "###
343 );
344 }
345
346 #[test]
311 fn test_trait_method_completion() { 347 fn test_trait_method_completion() {
312 assert_debug_snapshot!( 348 assert_debug_snapshot!(
313 do_ref_completion( 349 do_ref_completion(
@@ -584,4 +620,133 @@ mod tests {
584 "### 620 "###
585 ); 621 );
586 } 622 }
623
624 #[test]
625 fn works_in_simple_macro_1() {
626 assert_debug_snapshot!(
627 do_ref_completion(
628 r"
629 macro_rules! m { ($e:expr) => { $e } }
630 struct A { the_field: u32 }
631 fn foo(a: A) {
632 m!(a.x<|>)
633 }
634 ",
635 ),
636 @r###"
637 [
638 CompletionItem {
639 label: "the_field",
640 source_range: [156; 157),
641 delete: [156; 157),
642 insert: "the_field",
643 kind: Field,
644 detail: "u32",
645 },
646 ]
647 "###
648 );
649 }
650
651 #[test]
652 fn works_in_simple_macro_recursive() {
653 assert_debug_snapshot!(
654 do_ref_completion(
655 r"
656 macro_rules! m { ($e:expr) => { $e } }
657 struct A { the_field: u32 }
658 fn foo(a: A) {
659 m!(a.x<|>)
660 }
661 ",
662 ),
663 @r###"
664 [
665 CompletionItem {
666 label: "the_field",
667 source_range: [156; 157),
668 delete: [156; 157),
669 insert: "the_field",
670 kind: Field,
671 detail: "u32",
672 },
673 ]
674 "###
675 );
676 }
677
678 #[test]
679 fn works_in_simple_macro_2() {
680 // this doesn't work yet because the macro doesn't expand without the token -- maybe it can be fixed with better recovery
681 assert_debug_snapshot!(
682 do_ref_completion(
683 r"
684 macro_rules! m { ($e:expr) => { $e } }
685 struct A { the_field: u32 }
686 fn foo(a: A) {
687 m!(a.<|>)
688 }
689 ",
690 ),
691 @r###"[]"###
692 );
693 }
694
695 #[test]
696 fn works_in_simple_macro_recursive_1() {
697 assert_debug_snapshot!(
698 do_ref_completion(
699 r"
700 macro_rules! m { ($e:expr) => { $e } }
701 struct A { the_field: u32 }
702 fn foo(a: A) {
703 m!(m!(m!(a.x<|>)))
704 }
705 ",
706 ),
707 @r###"
708 [
709 CompletionItem {
710 label: "the_field",
711 source_range: [162; 163),
712 delete: [162; 163),
713 insert: "the_field",
714 kind: Field,
715 detail: "u32",
716 },
717 ]
718 "###
719 );
720 }
721
722 #[test]
723 fn test_method_completion_3547() {
724 assert_debug_snapshot!(
725 do_ref_completion(
726 r"
727 struct HashSet<T> {}
728 impl<T> HashSet<T> {
729 pub fn the_method(&self) {}
730 }
731 fn foo() {
732 let s: HashSet<_>;
733 s.<|>
734 }
735 ",
736 ),
737 @r###"
738 [
739 CompletionItem {
740 label: "the_method()",
741 source_range: [201; 201),
742 delete: [201; 201),
743 insert: "the_method()$0",
744 kind: Method,
745 lookup: "the_method",
746 detail: "pub fn the_method(&self)",
747 },
748 ]
749 "###
750 );
751 }
587} 752}
diff --git a/crates/ra_ide/src/completion/complete_fn_param.rs b/crates/ra_ide/src/completion/complete_fn_param.rs
index 502458706..9226ac055 100644
--- a/crates/ra_ide/src/completion/complete_fn_param.rs
+++ b/crates/ra_ide/src/completion/complete_fn_param.rs
@@ -52,7 +52,7 @@ pub(super) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext)
52 52
53#[cfg(test)] 53#[cfg(test)]
54mod tests { 54mod tests {
55 use crate::completion::{do_completion, CompletionItem, CompletionKind}; 55 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
56 use insta::assert_debug_snapshot; 56 use insta::assert_debug_snapshot;
57 57
58 fn do_magic_completion(code: &str) -> Vec<CompletionItem> { 58 fn do_magic_completion(code: &str) -> Vec<CompletionItem> {
diff --git a/crates/ra_ide/src/completion/complete_keyword.rs b/crates/ra_ide/src/completion/complete_keyword.rs
index eb7cd9ac2..1e053ea4a 100644
--- a/crates/ra_ide/src/completion/complete_keyword.rs
+++ b/crates/ra_ide/src/completion/complete_keyword.rs
@@ -79,6 +79,7 @@ pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
79} 79}
80 80
81fn is_in_loop_body(leaf: &SyntaxToken) -> bool { 81fn is_in_loop_body(leaf: &SyntaxToken) -> bool {
82 // FIXME move this to CompletionContext and make it handle macros
82 for node in leaf.parent().ancestors() { 83 for node in leaf.parent().ancestors() {
83 if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR { 84 if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR {
84 break; 85 break;
@@ -116,7 +117,7 @@ fn complete_return(
116 117
117#[cfg(test)] 118#[cfg(test)]
118mod tests { 119mod tests {
119 use crate::completion::{do_completion, CompletionItem, CompletionKind}; 120 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
120 use insta::assert_debug_snapshot; 121 use insta::assert_debug_snapshot;
121 122
122 fn do_keyword_completion(code: &str) -> Vec<CompletionItem> { 123 fn do_keyword_completion(code: &str) -> Vec<CompletionItem> {
diff --git a/crates/ra_ide/src/completion/complete_macro_in_item_position.rs b/crates/ra_ide/src/completion/complete_macro_in_item_position.rs
index 1866d9e6c..270e96df0 100644
--- a/crates/ra_ide/src/completion/complete_macro_in_item_position.rs
+++ b/crates/ra_ide/src/completion/complete_macro_in_item_position.rs
@@ -15,9 +15,10 @@ pub(super) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &Compl
15 15
16#[cfg(test)] 16#[cfg(test)]
17mod tests { 17mod tests {
18 use crate::completion::{do_completion, CompletionItem, CompletionKind};
19 use insta::assert_debug_snapshot; 18 use insta::assert_debug_snapshot;
20 19
20 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
21
21 fn do_reference_completion(code: &str) -> Vec<CompletionItem> { 22 fn do_reference_completion(code: &str) -> Vec<CompletionItem> {
22 do_completion(code, CompletionKind::Reference) 23 do_completion(code, CompletionKind::Reference)
23 } 24 }
diff --git a/crates/ra_ide/src/completion/complete_path.rs b/crates/ra_ide/src/completion/complete_path.rs
index 1a9699466..d588ee364 100644
--- a/crates/ra_ide/src/completion/complete_path.rs
+++ b/crates/ra_ide/src/completion/complete_path.rs
@@ -1,6 +1,6 @@
1//! Completion of paths, including when writing a single name. 1//! Completion of paths, i.e. `some::prefix::<|>`.
2 2
3use hir::{Adt, PathResolution, ScopeDef}; 3use hir::{Adt, HasVisibility, PathResolution, ScopeDef};
4use ra_syntax::AstNode; 4use ra_syntax::AstNode;
5use test_utils::tested_by; 5use test_utils::tested_by;
6 6
@@ -15,9 +15,10 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) {
15 Some(PathResolution::Def(def)) => def, 15 Some(PathResolution::Def(def)) => def,
16 _ => return, 16 _ => return,
17 }; 17 };
18 let context_module = ctx.scope().module();
18 match def { 19 match def {
19 hir::ModuleDef::Module(module) => { 20 hir::ModuleDef::Module(module) => {
20 let module_scope = module.scope(ctx.db); 21 let module_scope = module.scope(ctx.db, context_module);
21 for (name, def) in module_scope { 22 for (name, def) in module_scope {
22 if ctx.use_item_syntax.is_some() { 23 if ctx.use_item_syntax.is_some() {
23 if let ScopeDef::Unknown = def { 24 if let ScopeDef::Unknown = def {
@@ -47,10 +48,13 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) {
47 }; 48 };
48 // Iterate assoc types separately 49 // Iterate assoc types separately
49 // FIXME: complete T::AssocType 50 // FIXME: complete T::AssocType
50 let krate = ctx.module.map(|m| m.krate()); 51 let krate = ctx.krate;
51 if let Some(krate) = krate { 52 if let Some(krate) = krate {
52 let traits_in_scope = ctx.scope().traits_in_scope(); 53 let traits_in_scope = ctx.scope().traits_in_scope();
53 ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| { 54 ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| {
55 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
56 return None;
57 }
54 match item { 58 match item {
55 hir::AssocItem::Function(func) => { 59 hir::AssocItem::Function(func) => {
56 if !func.has_self_param(ctx.db) { 60 if !func.has_self_param(ctx.db) {
@@ -64,6 +68,9 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) {
64 }); 68 });
65 69
66 ty.iterate_impl_items(ctx.db, krate, |item| { 70 ty.iterate_impl_items(ctx.db, krate, |item| {
71 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
72 return None;
73 }
67 match item { 74 match item {
68 hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => {} 75 hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => {}
69 hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty), 76 hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
@@ -74,6 +81,9 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) {
74 } 81 }
75 hir::ModuleDef::Trait(t) => { 82 hir::ModuleDef::Trait(t) => {
76 for item in t.items(ctx.db) { 83 for item in t.items(ctx.db) {
84 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
85 continue;
86 }
77 match item { 87 match item {
78 hir::AssocItem::Function(func) => { 88 hir::AssocItem::Function(func) => {
79 if !func.has_self_param(ctx.db) { 89 if !func.has_self_param(ctx.db) {
@@ -93,7 +103,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) {
93mod tests { 103mod tests {
94 use test_utils::covers; 104 use test_utils::covers;
95 105
96 use crate::completion::{do_completion, CompletionItem, CompletionKind}; 106 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
97 use insta::assert_debug_snapshot; 107 use insta::assert_debug_snapshot;
98 108
99 fn do_reference_completion(code: &str) -> Vec<CompletionItem> { 109 fn do_reference_completion(code: &str) -> Vec<CompletionItem> {
@@ -170,6 +180,41 @@ mod tests {
170 } 180 }
171 181
172 #[test] 182 #[test]
183 fn path_visibility() {
184 assert_debug_snapshot!(
185 do_reference_completion(
186 r"
187 use self::my::<|>;
188
189 mod my {
190 struct Bar;
191 pub struct Foo;
192 pub use Bar as PublicBar;
193 }
194 "
195 ),
196 @r###"
197 [
198 CompletionItem {
199 label: "Foo",
200 source_range: [31; 31),
201 delete: [31; 31),
202 insert: "Foo",
203 kind: Struct,
204 },
205 CompletionItem {
206 label: "PublicBar",
207 source_range: [31; 31),
208 delete: [31; 31),
209 insert: "PublicBar",
210 kind: Struct,
211 },
212 ]
213 "###
214 );
215 }
216
217 #[test]
173 fn completes_use_item_starting_with_self() { 218 fn completes_use_item_starting_with_self() {
174 assert_debug_snapshot!( 219 assert_debug_snapshot!(
175 do_reference_completion( 220 do_reference_completion(
@@ -177,7 +222,7 @@ mod tests {
177 use self::m::<|>; 222 use self::m::<|>;
178 223
179 mod m { 224 mod m {
180 struct Bar; 225 pub struct Bar;
181 } 226 }
182 " 227 "
183 ), 228 ),
@@ -502,6 +547,60 @@ mod tests {
502 } 547 }
503 548
504 #[test] 549 #[test]
550 fn associated_item_visibility() {
551 assert_debug_snapshot!(
552 do_reference_completion(
553 "
554 //- /lib.rs
555 struct S;
556
557 mod m {
558 impl super::S {
559 pub(super) fn public_method() { }
560 fn private_method() { }
561 pub(super) type PublicType = u32;
562 type PrivateType = u32;
563 pub(super) const PUBLIC_CONST: u32 = 1;
564 const PRIVATE_CONST: u32 = 1;
565 }
566 }
567
568 fn foo() { let _ = S::<|> }
569 "
570 ),
571 @r###"
572 [
573 CompletionItem {
574 label: "PUBLIC_CONST",
575 source_range: [302; 302),
576 delete: [302; 302),
577 insert: "PUBLIC_CONST",
578 kind: Const,
579 detail: "pub(super) const PUBLIC_CONST: u32 = 1;",
580 },
581 CompletionItem {
582 label: "PublicType",
583 source_range: [302; 302),
584 delete: [302; 302),
585 insert: "PublicType",
586 kind: TypeAlias,
587 detail: "pub(super) type PublicType = u32;",
588 },
589 CompletionItem {
590 label: "public_method()",
591 source_range: [302; 302),
592 delete: [302; 302),
593 insert: "public_method()$0",
594 kind: Function,
595 lookup: "public_method",
596 detail: "pub(super) fn public_method()",
597 },
598 ]
599 "###
600 );
601 }
602
603 #[test]
505 fn completes_enum_associated_method() { 604 fn completes_enum_associated_method() {
506 assert_debug_snapshot!( 605 assert_debug_snapshot!(
507 do_reference_completion( 606 do_reference_completion(
@@ -835,4 +934,37 @@ mod tests {
835 "### 934 "###
836 ); 935 );
837 } 936 }
937
938 #[test]
939 fn completes_in_simple_macro_call() {
940 let completions = do_reference_completion(
941 r#"
942 macro_rules! m { ($e:expr) => { $e } }
943 fn main() { m!(self::f<|>); }
944 fn foo() {}
945 "#,
946 );
947 assert_debug_snapshot!(completions, @r###"
948 [
949 CompletionItem {
950 label: "foo()",
951 source_range: [93; 94),
952 delete: [93; 94),
953 insert: "foo()$0",
954 kind: Function,
955 lookup: "foo",
956 detail: "fn foo()",
957 },
958 CompletionItem {
959 label: "main()",
960 source_range: [93; 94),
961 delete: [93; 94),
962 insert: "main()$0",
963 kind: Function,
964 lookup: "main",
965 detail: "fn main()",
966 },
967 ]
968 "###);
969 }
838} 970}
diff --git a/crates/ra_ide/src/completion/complete_pattern.rs b/crates/ra_ide/src/completion/complete_pattern.rs
index c2c6ca002..6a1a66ef1 100644
--- a/crates/ra_ide/src/completion/complete_pattern.rs
+++ b/crates/ra_ide/src/completion/complete_pattern.rs
@@ -27,7 +27,7 @@ pub(super) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
27 27
28#[cfg(test)] 28#[cfg(test)]
29mod tests { 29mod tests {
30 use crate::completion::{do_completion, CompletionItem, CompletionKind}; 30 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
31 use insta::assert_debug_snapshot; 31 use insta::assert_debug_snapshot;
32 32
33 fn complete(code: &str) -> Vec<CompletionItem> { 33 fn complete(code: &str) -> Vec<CompletionItem> {
@@ -86,4 +86,22 @@ mod tests {
86 ] 86 ]
87 "###); 87 "###);
88 } 88 }
89
90 #[test]
91 fn completes_in_simple_macro_call() {
92 // FIXME: doesn't work yet because of missing error recovery in macro expansion
93 let completions = complete(
94 r"
95 macro_rules! m { ($e:expr) => { $e } }
96 enum E { X }
97
98 fn foo() {
99 m!(match E::X {
100 <|>
101 })
102 }
103 ",
104 );
105 assert_debug_snapshot!(completions, @r###"[]"###);
106 }
89} 107}
diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs
index 8a74f993a..0ba382165 100644
--- a/crates/ra_ide/src/completion/complete_postfix.rs
+++ b/crates/ra_ide/src/completion/complete_postfix.rs
@@ -12,7 +12,7 @@ use crate::{
12}; 12};
13 13
14pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { 14pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
15 if !ctx.db.feature_flags.get("completion.enable-postfix") { 15 if !ctx.options.enable_postfix_completions {
16 return; 16 return;
17 } 17 }
18 18
@@ -67,8 +67,8 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
67 67
68fn postfix_snippet(ctx: &CompletionContext, label: &str, detail: &str, snippet: &str) -> Builder { 68fn postfix_snippet(ctx: &CompletionContext, label: &str, detail: &str, snippet: &str) -> Builder {
69 let edit = { 69 let edit = {
70 let receiver_range = 70 let receiver_syntax = ctx.dot_receiver.as_ref().expect("no receiver available").syntax();
71 ctx.dot_receiver.as_ref().expect("no receiver available").syntax().text_range(); 71 let receiver_range = ctx.sema.original_range(receiver_syntax).range;
72 let delete_range = TextRange::from_to(receiver_range.start(), ctx.source_range().end()); 72 let delete_range = TextRange::from_to(receiver_range.start(), ctx.source_range().end());
73 TextEdit::replace(delete_range, snippet.to_string()) 73 TextEdit::replace(delete_range, snippet.to_string())
74 }; 74 };
@@ -81,7 +81,7 @@ fn postfix_snippet(ctx: &CompletionContext, label: &str, detail: &str, snippet:
81mod tests { 81mod tests {
82 use insta::assert_debug_snapshot; 82 use insta::assert_debug_snapshot;
83 83
84 use crate::completion::{do_completion, CompletionItem, CompletionKind}; 84 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
85 85
86 fn do_postfix_completion(code: &str) -> Vec<CompletionItem> { 86 fn do_postfix_completion(code: &str) -> Vec<CompletionItem> {
87 do_completion(code, CompletionKind::Postfix) 87 do_completion(code, CompletionKind::Postfix)
@@ -279,4 +279,65 @@ mod tests {
279 "### 279 "###
280 ); 280 );
281 } 281 }
282
283 #[test]
284 fn works_in_simple_macro() {
285 assert_debug_snapshot!(
286 do_postfix_completion(
287 r#"
288 macro_rules! m { ($e:expr) => { $e } }
289 fn main() {
290 let bar: u8 = 12;
291 m!(bar.b<|>)
292 }
293 "#,
294 ),
295 @r###"
296 [
297 CompletionItem {
298 label: "box",
299 source_range: [149; 150),
300 delete: [145; 150),
301 insert: "Box::new(bar)",
302 detail: "Box::new(expr)",
303 },
304 CompletionItem {
305 label: "dbg",
306 source_range: [149; 150),
307 delete: [145; 150),
308 insert: "dbg!(bar)",
309 detail: "dbg!(expr)",
310 },
311 CompletionItem {
312 label: "match",
313 source_range: [149; 150),
314 delete: [145; 150),
315 insert: "match bar {\n ${1:_} => {$0\\},\n}",
316 detail: "match expr {}",
317 },
318 CompletionItem {
319 label: "not",
320 source_range: [149; 150),
321 delete: [145; 150),
322 insert: "!bar",
323 detail: "!expr",
324 },
325 CompletionItem {
326 label: "ref",
327 source_range: [149; 150),
328 delete: [145; 150),
329 insert: "&bar",
330 detail: "&expr",
331 },
332 CompletionItem {
333 label: "refm",
334 source_range: [149; 150),
335 delete: [145; 150),
336 insert: "&mut bar",
337 detail: "&mut expr",
338 },
339 ]
340 "###
341 );
342 }
282} 343}
diff --git a/crates/ra_ide/src/completion/complete_record_literal.rs b/crates/ra_ide/src/completion/complete_record_literal.rs
index f98353d76..83ed1d52c 100644
--- a/crates/ra_ide/src/completion/complete_record_literal.rs
+++ b/crates/ra_ide/src/completion/complete_record_literal.rs
@@ -18,7 +18,7 @@ pub(super) fn complete_record_literal(acc: &mut Completions, ctx: &CompletionCon
18 18
19#[cfg(test)] 19#[cfg(test)]
20mod tests { 20mod tests {
21 use crate::completion::{do_completion, CompletionItem, CompletionKind}; 21 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
22 use insta::assert_debug_snapshot; 22 use insta::assert_debug_snapshot;
23 23
24 fn complete(code: &str) -> Vec<CompletionItem> { 24 fn complete(code: &str) -> Vec<CompletionItem> {
@@ -153,4 +153,29 @@ mod tests {
153 ] 153 ]
154 "###); 154 "###);
155 } 155 }
156
157 #[test]
158 fn test_record_literal_field_in_simple_macro() {
159 let completions = complete(
160 r"
161 macro_rules! m { ($e:expr) => { $e } }
162 struct A { the_field: u32 }
163 fn foo() {
164 m!(A { the<|> })
165 }
166 ",
167 );
168 assert_debug_snapshot!(completions, @r###"
169 [
170 CompletionItem {
171 label: "the_field",
172 source_range: [137; 140),
173 delete: [137; 140),
174 insert: "the_field",
175 kind: Field,
176 detail: "u32",
177 },
178 ]
179 "###);
180 }
156} 181}
diff --git a/crates/ra_ide/src/completion/complete_record_pattern.rs b/crates/ra_ide/src/completion/complete_record_pattern.rs
index 9bdeae49f..962376428 100644
--- a/crates/ra_ide/src/completion/complete_record_pattern.rs
+++ b/crates/ra_ide/src/completion/complete_record_pattern.rs
@@ -17,7 +17,7 @@ pub(super) fn complete_record_pattern(acc: &mut Completions, ctx: &CompletionCon
17 17
18#[cfg(test)] 18#[cfg(test)]
19mod tests { 19mod tests {
20 use crate::completion::{do_completion, CompletionItem, CompletionKind}; 20 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
21 use insta::assert_debug_snapshot; 21 use insta::assert_debug_snapshot;
22 22
23 fn complete(code: &str) -> Vec<CompletionItem> { 23 fn complete(code: &str) -> Vec<CompletionItem> {
@@ -87,4 +87,32 @@ mod tests {
87 ] 87 ]
88 "###); 88 "###);
89 } 89 }
90
91 #[test]
92 fn test_record_pattern_field_in_simple_macro() {
93 let completions = complete(
94 r"
95 macro_rules! m { ($e:expr) => { $e } }
96 struct S { foo: u32 }
97
98 fn process(f: S) {
99 m!(match f {
100 S { f<|>: 92 } => (),
101 })
102 }
103 ",
104 );
105 assert_debug_snapshot!(completions, @r###"
106 [
107 CompletionItem {
108 label: "foo",
109 source_range: [171; 172),
110 delete: [171; 172),
111 insert: "foo",
112 kind: Field,
113 detail: "u32",
114 },
115 ]
116 "###);
117 }
90} 118}
diff --git a/crates/ra_ide/src/completion/complete_scope.rs b/crates/ra_ide/src/completion/complete_scope.rs
index 2b9a0e556..bd4adf23a 100644
--- a/crates/ra_ide/src/completion/complete_scope.rs
+++ b/crates/ra_ide/src/completion/complete_scope.rs
@@ -1,4 +1,4 @@
1//! FIXME: write short doc here 1//! Completion of names from the current scope, e.g. locals and imported items.
2 2
3use crate::completion::{CompletionContext, Completions}; 3use crate::completion::{CompletionContext, Completions};
4 4
@@ -14,10 +14,10 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) {
14mod tests { 14mod tests {
15 use insta::assert_debug_snapshot; 15 use insta::assert_debug_snapshot;
16 16
17 use crate::completion::{do_completion, CompletionItem, CompletionKind}; 17 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
18 18
19 fn do_reference_completion(code: &str) -> Vec<CompletionItem> { 19 fn do_reference_completion(ra_fixture: &str) -> Vec<CompletionItem> {
20 do_completion(code, CompletionKind::Reference) 20 do_completion(ra_fixture, CompletionKind::Reference)
21 } 21 }
22 22
23 #[test] 23 #[test]
@@ -797,4 +797,72 @@ mod tests {
797 "### 797 "###
798 ) 798 )
799 } 799 }
800
801 #[test]
802 fn completes_in_simple_macro_1() {
803 assert_debug_snapshot!(
804 do_reference_completion(
805 r"
806 macro_rules! m { ($e:expr) => { $e } }
807 fn quux(x: i32) {
808 let y = 92;
809 m!(<|>);
810 }
811 "
812 ),
813 @"[]"
814 );
815 }
816
817 #[test]
818 fn completes_in_simple_macro_2() {
819 assert_debug_snapshot!(
820 do_reference_completion(
821 r"
822 macro_rules! m { ($e:expr) => { $e } }
823 fn quux(x: i32) {
824 let y = 92;
825 m!(x<|>);
826 }
827 "
828 ),
829 @r###"
830 [
831 CompletionItem {
832 label: "m!",
833 source_range: [145; 146),
834 delete: [145; 146),
835 insert: "m!($0)",
836 kind: Macro,
837 detail: "macro_rules! m",
838 },
839 CompletionItem {
840 label: "quux(…)",
841 source_range: [145; 146),
842 delete: [145; 146),
843 insert: "quux(${1:x})$0",
844 kind: Function,
845 lookup: "quux",
846 detail: "fn quux(x: i32)",
847 },
848 CompletionItem {
849 label: "x",
850 source_range: [145; 146),
851 delete: [145; 146),
852 insert: "x",
853 kind: Binding,
854 detail: "i32",
855 },
856 CompletionItem {
857 label: "y",
858 source_range: [145; 146),
859 delete: [145; 146),
860 insert: "y",
861 kind: Binding,
862 detail: "i32",
863 },
864 ]
865 "###
866 );
867 }
800} 868}
diff --git a/crates/ra_ide/src/completion/complete_snippet.rs b/crates/ra_ide/src/completion/complete_snippet.rs
index 731b4fd82..f731e9b9a 100644
--- a/crates/ra_ide/src/completion/complete_snippet.rs
+++ b/crates/ra_ide/src/completion/complete_snippet.rs
@@ -42,7 +42,7 @@ fn ${1:feature}() {
42 42
43#[cfg(test)] 43#[cfg(test)]
44mod tests { 44mod tests {
45 use crate::completion::{do_completion, CompletionItem, CompletionKind}; 45 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
46 use insta::assert_debug_snapshot; 46 use insta::assert_debug_snapshot;
47 47
48 fn do_snippet_completion(code: &str) -> Vec<CompletionItem> { 48 fn do_snippet_completion(code: &str) -> Vec<CompletionItem> {
diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs
index 18a1d2995..7fefa2c7a 100644
--- a/crates/ra_ide/src/completion/complete_trait_impl.rs
+++ b/crates/ra_ide/src/completion/complete_trait_impl.rs
@@ -34,7 +34,7 @@
34use hir::{self, Docs, HasSource}; 34use hir::{self, Docs, HasSource};
35use ra_assists::utils::get_missing_impl_items; 35use ra_assists::utils::get_missing_impl_items;
36use ra_syntax::{ 36use ra_syntax::{
37 ast::{self, edit}, 37 ast::{self, edit, ImplDef},
38 AstNode, SyntaxKind, SyntaxNode, TextRange, 38 AstNode, SyntaxKind, SyntaxNode, TextRange,
39}; 39};
40use ra_text_edit::TextEdit; 40use ra_text_edit::TextEdit;
@@ -47,22 +47,22 @@ use crate::{
47}; 47};
48 48
49pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) { 49pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) {
50 let trigger = ctx.token.ancestors().find(|p| match p.kind() { 50 if let Some((trigger, impl_def)) = completion_match(ctx) {
51 SyntaxKind::FN_DEF
52 | SyntaxKind::TYPE_ALIAS_DEF
53 | SyntaxKind::CONST_DEF
54 | SyntaxKind::BLOCK_EXPR => true,
55 _ => false,
56 });
57
58 let impl_def = trigger
59 .as_ref()
60 .and_then(|node| node.parent())
61 .and_then(|node| node.parent())
62 .and_then(ast::ImplDef::cast);
63
64 if let (Some(trigger), Some(impl_def)) = (trigger, impl_def) {
65 match trigger.kind() { 51 match trigger.kind() {
52 SyntaxKind::NAME_REF => {
53 get_missing_impl_items(&ctx.sema, &impl_def).iter().for_each(|item| match item {
54 hir::AssocItem::Function(fn_item) => {
55 add_function_impl(&trigger, acc, ctx, &fn_item)
56 }
57 hir::AssocItem::TypeAlias(type_item) => {
58 add_type_alias_impl(&trigger, acc, ctx, &type_item)
59 }
60 hir::AssocItem::Const(const_item) => {
61 add_const_impl(&trigger, acc, ctx, &const_item)
62 }
63 })
64 }
65
66 SyntaxKind::FN_DEF => { 66 SyntaxKind::FN_DEF => {
67 for missing_fn in get_missing_impl_items(&ctx.sema, &impl_def).iter().filter_map( 67 for missing_fn in get_missing_impl_items(&ctx.sema, &impl_def).iter().filter_map(
68 |item| match item { 68 |item| match item {
@@ -101,6 +101,21 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext
101 } 101 }
102} 102}
103 103
104fn completion_match(ctx: &CompletionContext) -> Option<(SyntaxNode, ImplDef)> {
105 let (trigger, impl_def_offset) = ctx.token.ancestors().find_map(|p| match p.kind() {
106 SyntaxKind::FN_DEF
107 | SyntaxKind::TYPE_ALIAS_DEF
108 | SyntaxKind::CONST_DEF
109 | SyntaxKind::BLOCK_EXPR => Some((p, 2)),
110 SyntaxKind::NAME_REF => Some((p, 5)),
111 _ => None,
112 })?;
113 let impl_def = (0..impl_def_offset - 1)
114 .try_fold(trigger.parent()?, |t, _| t.parent())
115 .and_then(ast::ImplDef::cast)?;
116 Some((trigger, impl_def))
117}
118
104fn add_function_impl( 119fn add_function_impl(
105 fn_def_node: &SyntaxNode, 120 fn_def_node: &SyntaxNode,
106 acc: &mut Completions, 121 acc: &mut Completions,
@@ -202,7 +217,7 @@ fn make_const_compl_syntax(const_: &ast::ConstDef) -> String {
202 217
203#[cfg(test)] 218#[cfg(test)]
204mod tests { 219mod tests {
205 use crate::completion::{do_completion, CompletionItem, CompletionKind}; 220 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
206 use insta::assert_debug_snapshot; 221 use insta::assert_debug_snapshot;
207 222
208 fn complete(code: &str) -> Vec<CompletionItem> { 223 fn complete(code: &str) -> Vec<CompletionItem> {
@@ -210,6 +225,103 @@ mod tests {
210 } 225 }
211 226
212 #[test] 227 #[test]
228 fn name_ref_function_type_const() {
229 let completions = complete(
230 r"
231 trait Test {
232 type TestType;
233 const TEST_CONST: u16;
234 fn test();
235 }
236
237 struct T1;
238
239 impl Test for T1 {
240 t<|>
241 }
242 ",
243 );
244 assert_debug_snapshot!(completions, @r###"
245 [
246 CompletionItem {
247 label: "const TEST_CONST: u16 = ",
248 source_range: [209; 210),
249 delete: [209; 210),
250 insert: "const TEST_CONST: u16 = ",
251 kind: Const,
252 lookup: "TEST_CONST",
253 },
254 CompletionItem {
255 label: "fn test()",
256 source_range: [209; 210),
257 delete: [209; 210),
258 insert: "fn test() {}",
259 kind: Function,
260 lookup: "test",
261 },
262 CompletionItem {
263 label: "type TestType = ",
264 source_range: [209; 210),
265 delete: [209; 210),
266 insert: "type TestType = ",
267 kind: TypeAlias,
268 lookup: "TestType",
269 },
270 ]
271 "###);
272 }
273
274 #[test]
275 fn no_nested_fn_completions() {
276 let completions = complete(
277 r"
278 trait Test {
279 fn test();
280 fn test2();
281 }
282
283 struct T1;
284
285 impl Test for T1 {
286 fn test() {
287 t<|>
288 }
289 }
290 ",
291 );
292 assert_debug_snapshot!(completions, @r###"[]"###);
293 }
294
295 #[test]
296 fn name_ref_single_function() {
297 let completions = complete(
298 r"
299 trait Test {
300 fn test();
301 }
302
303 struct T1;
304
305 impl Test for T1 {
306 t<|>
307 }
308 ",
309 );
310 assert_debug_snapshot!(completions, @r###"
311 [
312 CompletionItem {
313 label: "fn test()",
314 source_range: [139; 140),
315 delete: [139; 140),
316 insert: "fn test() {}",
317 kind: Function,
318 lookup: "test",
319 },
320 ]
321 "###);
322 }
323
324 #[test]
213 fn single_function() { 325 fn single_function() {
214 let completions = complete( 326 let completions = complete(
215 r" 327 r"
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index 9aa5a705d..3646fb8dc 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -5,13 +5,13 @@ use ra_db::SourceDatabase;
5use ra_ide_db::RootDatabase; 5use ra_ide_db::RootDatabase;
6use ra_syntax::{ 6use ra_syntax::{
7 algo::{find_covering_element, find_node_at_offset}, 7 algo::{find_covering_element, find_node_at_offset},
8 ast, AstNode, SourceFile, 8 ast, AstNode,
9 SyntaxKind::*, 9 SyntaxKind::*,
10 SyntaxNode, SyntaxToken, TextRange, TextUnit, 10 SyntaxNode, SyntaxToken, TextRange, TextUnit,
11}; 11};
12use ra_text_edit::AtomTextEdit; 12use ra_text_edit::AtomTextEdit;
13 13
14use crate::FilePosition; 14use crate::{completion::CompletionOptions, FilePosition};
15 15
16/// `CompletionContext` is created early during completion to figure out, where 16/// `CompletionContext` is created early during completion to figure out, where
17/// exactly is the cursor, syntax-wise. 17/// exactly is the cursor, syntax-wise.
@@ -19,9 +19,13 @@ use crate::FilePosition;
19pub(crate) struct CompletionContext<'a> { 19pub(crate) struct CompletionContext<'a> {
20 pub(super) sema: Semantics<'a, RootDatabase>, 20 pub(super) sema: Semantics<'a, RootDatabase>,
21 pub(super) db: &'a RootDatabase, 21 pub(super) db: &'a RootDatabase,
22 pub(super) options: &'a CompletionOptions,
22 pub(super) offset: TextUnit, 23 pub(super) offset: TextUnit,
24 /// The token before the cursor, in the original file.
25 pub(super) original_token: SyntaxToken,
26 /// The token before the cursor, in the macro-expanded file.
23 pub(super) token: SyntaxToken, 27 pub(super) token: SyntaxToken,
24 pub(super) module: Option<hir::Module>, 28 pub(super) krate: Option<hir::Crate>,
25 pub(super) name_ref_syntax: Option<ast::NameRef>, 29 pub(super) name_ref_syntax: Option<ast::NameRef>,
26 pub(super) function_syntax: Option<ast::FnDef>, 30 pub(super) function_syntax: Option<ast::FnDef>,
27 pub(super) use_item_syntax: Option<ast::UseItem>, 31 pub(super) use_item_syntax: Option<ast::UseItem>,
@@ -54,6 +58,7 @@ impl<'a> CompletionContext<'a> {
54 pub(super) fn new( 58 pub(super) fn new(
55 db: &'a RootDatabase, 59 db: &'a RootDatabase,
56 position: FilePosition, 60 position: FilePosition,
61 options: &'a CompletionOptions,
57 ) -> Option<CompletionContext<'a>> { 62 ) -> Option<CompletionContext<'a>> {
58 let sema = Semantics::new(db); 63 let sema = Semantics::new(db);
59 64
@@ -67,15 +72,21 @@ impl<'a> CompletionContext<'a> {
67 let edit = AtomTextEdit::insert(position.offset, "intellijRulezz".to_string()); 72 let edit = AtomTextEdit::insert(position.offset, "intellijRulezz".to_string());
68 parse.reparse(&edit).tree() 73 parse.reparse(&edit).tree()
69 }; 74 };
75 let fake_ident_token =
76 file_with_fake_ident.syntax().token_at_offset(position.offset).right_biased().unwrap();
70 77
71 let module = sema.to_module_def(position.file_id); 78 let krate = sema.to_module_def(position.file_id).map(|m| m.krate());
72 let token = original_file.syntax().token_at_offset(position.offset).left_biased()?; 79 let original_token =
80 original_file.syntax().token_at_offset(position.offset).left_biased()?;
81 let token = sema.descend_into_macros(original_token.clone());
73 let mut ctx = CompletionContext { 82 let mut ctx = CompletionContext {
74 sema, 83 sema,
75 db, 84 db,
85 options,
86 original_token,
76 token, 87 token,
77 offset: position.offset, 88 offset: position.offset,
78 module, 89 krate,
79 name_ref_syntax: None, 90 name_ref_syntax: None,
80 function_syntax: None, 91 function_syntax: None,
81 use_item_syntax: None, 92 use_item_syntax: None,
@@ -95,15 +106,57 @@ impl<'a> CompletionContext<'a> {
95 has_type_args: false, 106 has_type_args: false,
96 dot_receiver_is_ambiguous_float_literal: false, 107 dot_receiver_is_ambiguous_float_literal: false,
97 }; 108 };
98 ctx.fill(&original_file, file_with_fake_ident, position.offset); 109
110 let mut original_file = original_file.syntax().clone();
111 let mut hypothetical_file = file_with_fake_ident.syntax().clone();
112 let mut offset = position.offset;
113 let mut fake_ident_token = fake_ident_token;
114
115 // Are we inside a macro call?
116 while let (Some(actual_macro_call), Some(macro_call_with_fake_ident)) = (
117 find_node_at_offset::<ast::MacroCall>(&original_file, offset),
118 find_node_at_offset::<ast::MacroCall>(&hypothetical_file, offset),
119 ) {
120 if actual_macro_call.path().as_ref().map(|s| s.syntax().text())
121 != macro_call_with_fake_ident.path().as_ref().map(|s| s.syntax().text())
122 {
123 break;
124 }
125 let hypothetical_args = match macro_call_with_fake_ident.token_tree() {
126 Some(tt) => tt,
127 None => break,
128 };
129 if let (Some(actual_expansion), Some(hypothetical_expansion)) = (
130 ctx.sema.expand(&actual_macro_call),
131 ctx.sema.expand_hypothetical(
132 &actual_macro_call,
133 &hypothetical_args,
134 fake_ident_token,
135 ),
136 ) {
137 let new_offset = hypothetical_expansion.1.text_range().start();
138 if new_offset >= actual_expansion.text_range().end() {
139 break;
140 }
141 original_file = actual_expansion;
142 hypothetical_file = hypothetical_expansion.0;
143 fake_ident_token = hypothetical_expansion.1;
144 offset = new_offset;
145 } else {
146 break;
147 }
148 }
149
150 ctx.fill(&original_file, hypothetical_file, offset);
99 Some(ctx) 151 Some(ctx)
100 } 152 }
101 153
102 // The range of the identifier that is being completed. 154 // The range of the identifier that is being completed.
103 pub(crate) fn source_range(&self) -> TextRange { 155 pub(crate) fn source_range(&self) -> TextRange {
156 // check kind of macro-expanded token, but use range of original token
104 match self.token.kind() { 157 match self.token.kind() {
105 // workaroud when completion is triggered by trigger characters. 158 // workaroud when completion is triggered by trigger characters.
106 IDENT => self.token.text_range(), 159 IDENT => self.original_token.text_range(),
107 _ => TextRange::offset_len(self.offset, 0.into()), 160 _ => TextRange::offset_len(self.offset, 0.into()),
108 } 161 }
109 } 162 }
@@ -114,27 +167,24 @@ impl<'a> CompletionContext<'a> {
114 167
115 fn fill( 168 fn fill(
116 &mut self, 169 &mut self,
117 original_file: &ast::SourceFile, 170 original_file: &SyntaxNode,
118 file_with_fake_ident: ast::SourceFile, 171 file_with_fake_ident: SyntaxNode,
119 offset: TextUnit, 172 offset: TextUnit,
120 ) { 173 ) {
121 // First, let's try to complete a reference to some declaration. 174 // First, let's try to complete a reference to some declaration.
122 if let Some(name_ref) = 175 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) {
123 find_node_at_offset::<ast::NameRef>(file_with_fake_ident.syntax(), offset)
124 {
125 // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`. 176 // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`.
126 // See RFC#1685. 177 // See RFC#1685.
127 if is_node::<ast::Param>(name_ref.syntax()) { 178 if is_node::<ast::Param>(name_ref.syntax()) {
128 self.is_param = true; 179 self.is_param = true;
129 return; 180 return;
130 } 181 }
131 self.classify_name_ref(original_file, name_ref); 182 self.classify_name_ref(original_file, name_ref, offset);
132 } 183 }
133 184
134 // Otherwise, see if this is a declaration. We can use heuristics to 185 // Otherwise, see if this is a declaration. We can use heuristics to
135 // suggest declaration names, see `CompletionKind::Magic`. 186 // suggest declaration names, see `CompletionKind::Magic`.
136 if let Some(name) = find_node_at_offset::<ast::Name>(file_with_fake_ident.syntax(), offset) 187 if let Some(name) = find_node_at_offset::<ast::Name>(&file_with_fake_ident, offset) {
137 {
138 if let Some(bind_pat) = name.syntax().ancestors().find_map(ast::BindPat::cast) { 188 if let Some(bind_pat) = name.syntax().ancestors().find_map(ast::BindPat::cast) {
139 let parent = bind_pat.syntax().parent(); 189 let parent = bind_pat.syntax().parent();
140 if parent.clone().and_then(ast::MatchArm::cast).is_some() 190 if parent.clone().and_then(ast::MatchArm::cast).is_some()
@@ -148,23 +198,29 @@ impl<'a> CompletionContext<'a> {
148 return; 198 return;
149 } 199 }
150 if name.syntax().ancestors().find_map(ast::RecordFieldPatList::cast).is_some() { 200 if name.syntax().ancestors().find_map(ast::RecordFieldPatList::cast).is_some() {
151 self.record_lit_pat = find_node_at_offset(original_file.syntax(), self.offset); 201 self.record_lit_pat =
202 self.sema.find_node_at_offset_with_macros(&original_file, offset);
152 } 203 }
153 } 204 }
154 } 205 }
155 206
156 fn classify_name_ref(&mut self, original_file: &SourceFile, name_ref: ast::NameRef) { 207 fn classify_name_ref(
208 &mut self,
209 original_file: &SyntaxNode,
210 name_ref: ast::NameRef,
211 offset: TextUnit,
212 ) {
157 self.name_ref_syntax = 213 self.name_ref_syntax =
158 find_node_at_offset(original_file.syntax(), name_ref.syntax().text_range().start()); 214 find_node_at_offset(&original_file, name_ref.syntax().text_range().start());
159 let name_range = name_ref.syntax().text_range(); 215 let name_range = name_ref.syntax().text_range();
160 if name_ref.syntax().parent().and_then(ast::RecordField::cast).is_some() { 216 if name_ref.syntax().parent().and_then(ast::RecordField::cast).is_some() {
161 self.record_lit_syntax = find_node_at_offset(original_file.syntax(), self.offset); 217 self.record_lit_syntax =
218 self.sema.find_node_at_offset_with_macros(&original_file, offset);
162 } 219 }
163 220
164 self.impl_def = self 221 self.impl_def = self
165 .token 222 .sema
166 .parent() 223 .ancestors_with_macros(self.token.parent())
167 .ancestors()
168 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) 224 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
169 .find_map(ast::ImplDef::cast); 225 .find_map(ast::ImplDef::cast);
170 226
@@ -183,12 +239,12 @@ impl<'a> CompletionContext<'a> {
183 _ => (), 239 _ => (),
184 } 240 }
185 241
186 self.use_item_syntax = self.token.parent().ancestors().find_map(ast::UseItem::cast); 242 self.use_item_syntax =
243 self.sema.ancestors_with_macros(self.token.parent()).find_map(ast::UseItem::cast);
187 244
188 self.function_syntax = self 245 self.function_syntax = self
189 .token 246 .sema
190 .parent() 247 .ancestors_with_macros(self.token.parent())
191 .ancestors()
192 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) 248 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
193 .find_map(ast::FnDef::cast); 249 .find_map(ast::FnDef::cast);
194 250
@@ -242,7 +298,7 @@ impl<'a> CompletionContext<'a> {
242 298
243 if let Some(off) = name_ref.syntax().text_range().start().checked_sub(2.into()) { 299 if let Some(off) = name_ref.syntax().text_range().start().checked_sub(2.into()) {
244 if let Some(if_expr) = 300 if let Some(if_expr) =
245 find_node_at_offset::<ast::IfExpr>(original_file.syntax(), off) 301 self.sema.find_node_at_offset_with_macros::<ast::IfExpr>(original_file, off)
246 { 302 {
247 if if_expr.syntax().text_range().end() 303 if if_expr.syntax().text_range().end()
248 < name_ref.syntax().text_range().start() 304 < name_ref.syntax().text_range().start()
@@ -259,7 +315,7 @@ impl<'a> CompletionContext<'a> {
259 self.dot_receiver = field_expr 315 self.dot_receiver = field_expr
260 .expr() 316 .expr()
261 .map(|e| e.syntax().text_range()) 317 .map(|e| e.syntax().text_range())
262 .and_then(|r| find_node_with_range(original_file.syntax(), r)); 318 .and_then(|r| find_node_with_range(original_file, r));
263 self.dot_receiver_is_ambiguous_float_literal = 319 self.dot_receiver_is_ambiguous_float_literal =
264 if let Some(ast::Expr::Literal(l)) = &self.dot_receiver { 320 if let Some(ast::Expr::Literal(l)) = &self.dot_receiver {
265 match l.kind() { 321 match l.kind() {
@@ -275,7 +331,7 @@ impl<'a> CompletionContext<'a> {
275 self.dot_receiver = method_call_expr 331 self.dot_receiver = method_call_expr
276 .expr() 332 .expr()
277 .map(|e| e.syntax().text_range()) 333 .map(|e| e.syntax().text_range())
278 .and_then(|r| find_node_with_range(original_file.syntax(), r)); 334 .and_then(|r| find_node_with_range(original_file, r));
279 self.is_call = true; 335 self.is_call = true;
280 } 336 }
281 } 337 }
diff --git a/crates/ra_ide/src/completion/completion_item.rs b/crates/ra_ide/src/completion/completion_item.rs
index 61867c0ff..ef0eb43b2 100644
--- a/crates/ra_ide/src/completion/completion_item.rs
+++ b/crates/ra_ide/src/completion/completion_item.rs
@@ -13,7 +13,7 @@ pub struct CompletionItem {
13 /// Used only internally in tests, to check only specific kind of 13 /// Used only internally in tests, to check only specific kind of
14 /// completion (postfix, keyword, reference, etc). 14 /// completion (postfix, keyword, reference, etc).
15 #[allow(unused)] 15 #[allow(unused)]
16 completion_kind: CompletionKind, 16 pub(crate) completion_kind: CompletionKind,
17 /// Label in the completion pop up which identifies completion. 17 /// Label in the completion pop up which identifies completion.
18 label: String, 18 label: String,
19 /// Range of identifier that is being completed. 19 /// Range of identifier that is being completed.
@@ -47,6 +47,10 @@ pub struct CompletionItem {
47 47
48 /// Whether this item is marked as deprecated 48 /// Whether this item is marked as deprecated
49 deprecated: bool, 49 deprecated: bool,
50
51 /// If completing a function call, ask the editor to show parameter popup
52 /// after completion.
53 trigger_call_info: bool,
50} 54}
51 55
52// We use custom debug for CompletionItem to make `insta`'s diffs more readable. 56// We use custom debug for CompletionItem to make `insta`'s diffs more readable.
@@ -139,6 +143,7 @@ impl CompletionItem {
139 kind: None, 143 kind: None,
140 text_edit: None, 144 text_edit: None,
141 deprecated: None, 145 deprecated: None,
146 trigger_call_info: None,
142 } 147 }
143 } 148 }
144 /// What user sees in pop-up in the UI. 149 /// What user sees in pop-up in the UI.
@@ -177,6 +182,10 @@ impl CompletionItem {
177 pub fn deprecated(&self) -> bool { 182 pub fn deprecated(&self) -> bool {
178 self.deprecated 183 self.deprecated
179 } 184 }
185
186 pub fn trigger_call_info(&self) -> bool {
187 self.trigger_call_info
188 }
180} 189}
181 190
182/// A helper to make `CompletionItem`s. 191/// A helper to make `CompletionItem`s.
@@ -193,6 +202,7 @@ pub(crate) struct Builder {
193 kind: Option<CompletionItemKind>, 202 kind: Option<CompletionItemKind>,
194 text_edit: Option<TextEdit>, 203 text_edit: Option<TextEdit>,
195 deprecated: Option<bool>, 204 deprecated: Option<bool>,
205 trigger_call_info: Option<bool>,
196} 206}
197 207
198impl Builder { 208impl Builder {
@@ -221,6 +231,7 @@ impl Builder {
221 kind: self.kind, 231 kind: self.kind,
222 completion_kind: self.completion_kind, 232 completion_kind: self.completion_kind,
223 deprecated: self.deprecated.unwrap_or(false), 233 deprecated: self.deprecated.unwrap_or(false),
234 trigger_call_info: self.trigger_call_info.unwrap_or(false),
224 } 235 }
225 } 236 }
226 pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder { 237 pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder {
@@ -271,6 +282,10 @@ impl Builder {
271 self.deprecated = Some(deprecated); 282 self.deprecated = Some(deprecated);
272 self 283 self
273 } 284 }
285 pub(crate) fn trigger_call_info(mut self) -> Builder {
286 self.trigger_call_info = Some(true);
287 self
288 }
274} 289}
275 290
276impl<'a> Into<CompletionItem> for Builder { 291impl<'a> Into<CompletionItem> for Builder {
@@ -303,20 +318,3 @@ impl Into<Vec<CompletionItem>> for Completions {
303 self.buf 318 self.buf
304 } 319 }
305} 320}
306
307#[cfg(test)]
308pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> {
309 use crate::completion::completions;
310 use crate::mock_analysis::{analysis_and_position, single_file_with_position};
311 let (analysis, position) = if code.contains("//-") {
312 analysis_and_position(code)
313 } else {
314 single_file_with_position(code)
315 };
316 let completions = completions(&analysis.db, position).unwrap();
317 let completion_items: Vec<CompletionItem> = completions.into();
318 let mut kind_completions: Vec<CompletionItem> =
319 completion_items.into_iter().filter(|c| c.completion_kind == kind).collect();
320 kind_completions.sort_by_key(|c| c.label.clone());
321 kind_completions
322}
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index dac232a85..5213def20 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -103,11 +103,8 @@ impl Completions {
103 } 103 }
104 }; 104 };
105 105
106 // If not an import, add parenthesis automatically. 106 // Add `<>` for generic types
107 if ctx.is_path_type 107 if ctx.is_path_type && !ctx.has_type_args && ctx.options.add_call_parenthesis {
108 && !ctx.has_type_args
109 && ctx.db.feature_flags.get("completion.insertion.add-call-parenthesis")
110 {
111 let has_non_default_type_params = match resolution { 108 let has_non_default_type_params = match resolution {
112 ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(ctx.db), 109 ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(ctx.db),
113 ScopeDef::ModuleDef(TypeAlias(it)) => it.has_non_default_type_params(ctx.db), 110 ScopeDef::ModuleDef(TypeAlias(it)) => it.has_non_default_type_params(ctx.db),
@@ -211,26 +208,29 @@ impl Completions {
211 .set_deprecated(is_deprecated(func, ctx.db)) 208 .set_deprecated(is_deprecated(func, ctx.db))
212 .detail(function_signature.to_string()); 209 .detail(function_signature.to_string());
213 210
214 // Add `<>` for generic types 211 // If not an import, add parenthesis automatically.
215 if ctx.use_item_syntax.is_none() 212 if ctx.use_item_syntax.is_none() && !ctx.is_call && ctx.options.add_call_parenthesis {
216 && !ctx.is_call
217 && ctx.db.feature_flags.get("completion.insertion.add-call-parenthesis")
218 {
219 tested_by!(inserts_parens_for_function_calls); 213 tested_by!(inserts_parens_for_function_calls);
220 214
221 let (snippet, label) = if params.is_empty() || has_self_param && params.len() == 1 { 215 let (snippet, label) = if params.is_empty() || has_self_param && params.len() == 1 {
222 (format!("{}()$0", name), format!("{}()", name)) 216 (format!("{}()$0", name), format!("{}()", name))
223 } else { 217 } else {
224 let to_skip = if has_self_param { 1 } else { 0 }; 218 builder = builder.trigger_call_info();
225 let function_params_snippet = 219 let snippet = if ctx.options.add_call_argument_snippets {
226 join( 220 let to_skip = if has_self_param { 1 } else { 0 };
221 let function_params_snippet = join(
227 function_signature.parameter_names.iter().skip(to_skip).enumerate().map( 222 function_signature.parameter_names.iter().skip(to_skip).enumerate().map(
228 |(index, param_name)| format!("${{{}:{}}}", index + 1, param_name), 223 |(index, param_name)| format!("${{{}:{}}}", index + 1, param_name),
229 ), 224 ),
230 ) 225 )
231 .separator(", ") 226 .separator(", ")
232 .to_string(); 227 .to_string();
233 (format!("{}({})$0", name, function_params_snippet), format!("{}(…)", name)) 228 format!("{}({})$0", name, function_params_snippet)
229 } else {
230 format!("{}($0)", name)
231 };
232
233 (snippet, format!("{}(…)", name))
234 }; 234 };
235 builder = builder.lookup_by(name).label(label).insert_snippet(snippet); 235 builder = builder.lookup_by(name).label(label).insert_snippet(snippet);
236 } 236 }
@@ -307,12 +307,22 @@ mod tests {
307 use insta::assert_debug_snapshot; 307 use insta::assert_debug_snapshot;
308 use test_utils::covers; 308 use test_utils::covers;
309 309
310 use crate::completion::{do_completion, CompletionItem, CompletionKind}; 310 use crate::completion::{
311 test_utils::{do_completion, do_completion_with_options},
312 CompletionItem, CompletionKind, CompletionOptions,
313 };
311 314
312 fn do_reference_completion(ra_fixture: &str) -> Vec<CompletionItem> { 315 fn do_reference_completion(ra_fixture: &str) -> Vec<CompletionItem> {
313 do_completion(ra_fixture, CompletionKind::Reference) 316 do_completion(ra_fixture, CompletionKind::Reference)
314 } 317 }
315 318
319 fn do_reference_completion_with_options(
320 ra_fixture: &str,
321 options: CompletionOptions,
322 ) -> Vec<CompletionItem> {
323 do_completion_with_options(ra_fixture, CompletionKind::Reference, &options)
324 }
325
316 #[test] 326 #[test]
317 fn enum_detail_includes_names_for_record() { 327 fn enum_detail_includes_names_for_record() {
318 assert_debug_snapshot!( 328 assert_debug_snapshot!(
@@ -533,7 +543,7 @@ mod tests {
533 } 543 }
534 544
535 #[test] 545 #[test]
536 fn parens_for_method_call() { 546 fn arg_snippets_for_method_call() {
537 assert_debug_snapshot!( 547 assert_debug_snapshot!(
538 do_reference_completion( 548 do_reference_completion(
539 r" 549 r"
@@ -563,6 +573,40 @@ mod tests {
563 } 573 }
564 574
565 #[test] 575 #[test]
576 fn no_arg_snippets_for_method_call() {
577 assert_debug_snapshot!(
578 do_reference_completion_with_options(
579 r"
580 struct S {}
581 impl S {
582 fn foo(&self, x: i32) {}
583 }
584 fn bar(s: &S) {
585 s.f<|>
586 }
587 ",
588 CompletionOptions {
589 add_call_argument_snippets: false,
590 .. Default::default()
591 }
592 ),
593 @r###"
594 [
595 CompletionItem {
596 label: "foo(…)",
597 source_range: [171; 172),
598 delete: [171; 172),
599 insert: "foo($0)",
600 kind: Method,
601 lookup: "foo",
602 detail: "fn foo(&self, x: i32)",
603 },
604 ]
605 "###
606 )
607 }
608
609 #[test]
566 fn dont_render_function_parens_in_use_item() { 610 fn dont_render_function_parens_in_use_item() {
567 assert_debug_snapshot!( 611 assert_debug_snapshot!(
568 do_reference_completion( 612 do_reference_completion(
diff --git a/crates/ra_ide/src/completion/test_utils.rs b/crates/ra_ide/src/completion/test_utils.rs
new file mode 100644
index 000000000..136857315
--- /dev/null
+++ b/crates/ra_ide/src/completion/test_utils.rs
@@ -0,0 +1,29 @@
1//! Runs completion for testing purposes.
2
3use crate::{
4 completion::{completion_item::CompletionKind, CompletionOptions},
5 mock_analysis::{analysis_and_position, single_file_with_position},
6 CompletionItem,
7};
8
9pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> {
10 do_completion_with_options(code, kind, &CompletionOptions::default())
11}
12
13pub(crate) fn do_completion_with_options(
14 code: &str,
15 kind: CompletionKind,
16 options: &CompletionOptions,
17) -> Vec<CompletionItem> {
18 let (analysis, position) = if code.contains("//-") {
19 analysis_and_position(code)
20 } else {
21 single_file_with_position(code)
22 };
23 let completions = analysis.completions(position, options).unwrap().unwrap();
24 let completion_items: Vec<CompletionItem> = completions.into();
25 let mut kind_completions: Vec<CompletionItem> =
26 completion_items.into_iter().filter(|c| c.completion_kind == kind).collect();
27 kind_completions.sort_by_key(|c| c.label().to_owned());
28 kind_completions
29}