aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_completion/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_completion/src')
-rw-r--r--crates/ide_completion/src/completions.rs1
-rw-r--r--crates/ide_completion/src/completions/dot.rs6
-rw-r--r--crates/ide_completion/src/completions/flyimport.rs6
-rw-r--r--crates/ide_completion/src/completions/keyword.rs25
-rw-r--r--crates/ide_completion/src/completions/macro_in_item_position.rs48
-rw-r--r--crates/ide_completion/src/completions/postfix.rs2
-rw-r--r--crates/ide_completion/src/completions/qualified_path.rs15
-rw-r--r--crates/ide_completion/src/completions/snippet.rs14
-rw-r--r--crates/ide_completion/src/completions/trait_impl.rs18
-rw-r--r--crates/ide_completion/src/completions/unqualified_path.rs22
-rw-r--r--crates/ide_completion/src/context.rs152
-rw-r--r--crates/ide_completion/src/lib.rs1
-rw-r--r--crates/ide_completion/src/patterns.rs11
-rw-r--r--crates/ide_completion/src/render.rs8
-rw-r--r--crates/ide_completion/src/render/builder_ext.rs12
-rw-r--r--crates/ide_completion/src/render/macro_.rs4
16 files changed, 159 insertions, 186 deletions
diff --git a/crates/ide_completion/src/completions.rs b/crates/ide_completion/src/completions.rs
index ffdcdc930..7a4d71e91 100644
--- a/crates/ide_completion/src/completions.rs
+++ b/crates/ide_completion/src/completions.rs
@@ -6,7 +6,6 @@ pub(crate) mod flyimport;
6pub(crate) mod fn_param; 6pub(crate) mod fn_param;
7pub(crate) mod keyword; 7pub(crate) mod keyword;
8pub(crate) mod lifetime; 8pub(crate) mod lifetime;
9pub(crate) mod macro_in_item_position;
10pub(crate) mod mod_; 9pub(crate) mod mod_;
11pub(crate) mod pattern; 10pub(crate) mod pattern;
12pub(crate) mod postfix; 11pub(crate) mod postfix;
diff --git a/crates/ide_completion/src/completions/dot.rs b/crates/ide_completion/src/completions/dot.rs
index e0a7021fd..8ad57a069 100644
--- a/crates/ide_completion/src/completions/dot.rs
+++ b/crates/ide_completion/src/completions/dot.rs
@@ -4,7 +4,7 @@ use either::Either;
4use hir::{HasVisibility, ScopeDef}; 4use hir::{HasVisibility, ScopeDef};
5use rustc_hash::FxHashSet; 5use rustc_hash::FxHashSet;
6 6
7use crate::{context::CompletionContext, Completions}; 7use crate::{context::CompletionContext, patterns::ImmediateLocation, Completions};
8 8
9/// Complete dot accesses, i.e. fields or methods. 9/// Complete dot accesses, i.e. fields or methods.
10pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { 10pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
@@ -18,7 +18,7 @@ pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
18 _ => return, 18 _ => return,
19 }; 19 };
20 20
21 if ctx.is_call { 21 if matches!(ctx.completion_location, Some(ImmediateLocation::MethodCall { .. })) {
22 cov_mark::hit!(test_no_struct_field_completion_for_method_call); 22 cov_mark::hit!(test_no_struct_field_completion_for_method_call);
23 } else { 23 } else {
24 complete_fields(ctx, &receiver_ty, |field, ty| match field { 24 complete_fields(ctx, &receiver_ty, |field, ty| match field {
@@ -33,7 +33,7 @@ fn complete_undotted_self(acc: &mut Completions, ctx: &CompletionContext) {
33 if !ctx.config.enable_self_on_the_fly { 33 if !ctx.config.enable_self_on_the_fly {
34 return; 34 return;
35 } 35 }
36 if !ctx.is_trivial_path || ctx.is_path_disallowed() { 36 if !ctx.is_trivial_path() || ctx.is_path_disallowed() {
37 return; 37 return;
38 } 38 }
39 ctx.scope.process_all_names(&mut |name, def| { 39 ctx.scope.process_all_names(&mut |name, def| {
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs
index d72bf13d3..7bf47bf75 100644
--- a/crates/ide_completion/src/completions/flyimport.rs
+++ b/crates/ide_completion/src/completions/flyimport.rs
@@ -161,13 +161,13 @@ pub(crate) fn position_for_import<'a>(
161) -> Option<&'a SyntaxNode> { 161) -> Option<&'a SyntaxNode> {
162 Some(match import_candidate { 162 Some(match import_candidate {
163 Some(ImportCandidate::Path(_)) => ctx.name_ref_syntax.as_ref()?.syntax(), 163 Some(ImportCandidate::Path(_)) => ctx.name_ref_syntax.as_ref()?.syntax(),
164 Some(ImportCandidate::TraitAssocItem(_)) => ctx.path_qual.as_ref()?.syntax(), 164 Some(ImportCandidate::TraitAssocItem(_)) => ctx.path_qual()?.syntax(),
165 Some(ImportCandidate::TraitMethod(_)) => ctx.dot_receiver()?.syntax(), 165 Some(ImportCandidate::TraitMethod(_)) => ctx.dot_receiver()?.syntax(),
166 None => ctx 166 None => ctx
167 .name_ref_syntax 167 .name_ref_syntax
168 .as_ref() 168 .as_ref()
169 .map(|name_ref| name_ref.syntax()) 169 .map(|name_ref| name_ref.syntax())
170 .or_else(|| ctx.path_qual.as_ref().map(|path| path.syntax())) 170 .or_else(|| ctx.path_qual().map(|path| path.syntax()))
171 .or_else(|| ctx.dot_receiver().map(|expr| expr.syntax()))?, 171 .or_else(|| ctx.dot_receiver().map(|expr| expr.syntax()))?,
172 }) 172 })
173} 173}
@@ -190,7 +190,7 @@ fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAs
190 }; 190 };
191 let assets_for_path = ImportAssets::for_fuzzy_path( 191 let assets_for_path = ImportAssets::for_fuzzy_path(
192 current_module, 192 current_module,
193 ctx.path_qual.clone(), 193 ctx.path_qual().cloned(),
194 fuzzy_name, 194 fuzzy_name,
195 &ctx.sema, 195 &ctx.sema,
196 approximate_node, 196 approximate_node,
diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs
index 1a7a484a4..ba13d3707 100644
--- a/crates/ide_completion/src/completions/keyword.rs
+++ b/crates/ide_completion/src/completions/keyword.rs
@@ -5,8 +5,8 @@ use std::iter;
5use syntax::{SyntaxKind, T}; 5use syntax::{SyntaxKind, T};
6 6
7use crate::{ 7use crate::{
8 patterns::ImmediateLocation, CompletionContext, CompletionItem, CompletionItemKind, 8 context::PathCompletionContext, patterns::ImmediateLocation, CompletionContext, CompletionItem,
9 CompletionKind, Completions, 9 CompletionItemKind, CompletionKind, Completions,
10}; 10};
11 11
12pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionContext) { 12pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionContext) {
@@ -19,11 +19,12 @@ pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC
19 }; 19 };
20 20
21 if ctx.use_item_syntax.is_some() { 21 if ctx.use_item_syntax.is_some() {
22 if ctx.path_qual.is_none() { 22 let qual = ctx.path_qual();
23 if qual.is_none() {
23 kw_completion("crate::").add_to(acc); 24 kw_completion("crate::").add_to(acc);
24 } 25 }
25 kw_completion("self").add_to(acc); 26 kw_completion("self").add_to(acc);
26 if iter::successors(ctx.path_qual.clone(), |p| p.qualifier()) 27 if iter::successors(qual.cloned(), |p| p.qualifier())
27 .all(|p| p.segment().and_then(|s| s.super_token()).is_some()) 28 .all(|p| p.segment().and_then(|s| s.super_token()).is_some())
28 { 29 {
29 kw_completion("super::").add_to(acc); 30 kw_completion("super::").add_to(acc);
@@ -127,8 +128,15 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
127 add_keyword("mut", "mut "); 128 add_keyword("mut", "mut ");
128 } 129 }
129 130
130 if ctx.in_loop_body { 131 let (can_be_stmt, in_loop_body) = match ctx.path_context {
131 if ctx.can_be_stmt { 132 Some(PathCompletionContext {
133 is_trivial_path: true, can_be_stmt, in_loop_body, ..
134 }) => (can_be_stmt, in_loop_body),
135 _ => return,
136 };
137
138 if in_loop_body {
139 if can_be_stmt {
132 add_keyword("continue", "continue;"); 140 add_keyword("continue", "continue;");
133 add_keyword("break", "break;"); 141 add_keyword("break", "break;");
134 } else { 142 } else {
@@ -137,9 +145,6 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
137 } 145 }
138 } 146 }
139 147
140 if !ctx.is_trivial_path {
141 return;
142 }
143 let fn_def = match &ctx.function_def { 148 let fn_def = match &ctx.function_def {
144 Some(it) => it, 149 Some(it) => it,
145 None => return, 150 None => return,
@@ -147,7 +152,7 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
147 152
148 add_keyword( 153 add_keyword(
149 "return", 154 "return",
150 match (ctx.can_be_stmt, fn_def.ret_type().is_some()) { 155 match (can_be_stmt, fn_def.ret_type().is_some()) {
151 (true, true) => "return $0;", 156 (true, true) => "return $0;",
152 (true, false) => "return;", 157 (true, false) => "return;",
153 (false, true) => "return $0", 158 (false, true) => "return $0",
diff --git a/crates/ide_completion/src/completions/macro_in_item_position.rs b/crates/ide_completion/src/completions/macro_in_item_position.rs
deleted file mode 100644
index 781b96ff1..000000000
--- a/crates/ide_completion/src/completions/macro_in_item_position.rs
+++ /dev/null
@@ -1,48 +0,0 @@
1//! Completes macro invocations used in item position.
2
3use crate::{CompletionContext, Completions};
4
5// Ideally this should be removed and moved into `(un)qualified_path` respectively
6pub(crate) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &CompletionContext) {
7 // Show only macros in top level.
8 if !ctx.expects_item() {
9 return;
10 }
11
12 ctx.scope.process_all_names(&mut |name, res| {
13 if let hir::ScopeDef::MacroDef(mac) = res {
14 acc.add_macro(ctx, Some(name.clone()), mac);
15 }
16 // FIXME: This should be done in qualified_path/unqualified_path instead?
17 if let hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res {
18 acc.add_resolution(ctx, name, &res);
19 }
20 })
21}
22
23#[cfg(test)]
24mod tests {
25 use expect_test::{expect, Expect};
26
27 use crate::{test_utils::completion_list, CompletionKind};
28
29 fn check(ra_fixture: &str, expect: Expect) {
30 let actual = completion_list(ra_fixture, CompletionKind::Reference);
31 expect.assert_eq(&actual)
32 }
33
34 #[test]
35 fn completes_macros_as_item() {
36 check(
37 r#"
38macro_rules! foo { () => {} }
39fn foo() {}
40
41$0
42"#,
43 expect![[r#"
44 ma foo!(…) macro_rules! foo
45 "#]],
46 )
47 }
48}
diff --git a/crates/ide_completion/src/completions/postfix.rs b/crates/ide_completion/src/completions/postfix.rs
index 86bbb58e2..86eb21714 100644
--- a/crates/ide_completion/src/completions/postfix.rs
+++ b/crates/ide_completion/src/completions/postfix.rs
@@ -24,7 +24,7 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
24 } 24 }
25 25
26 let (dot_receiver, receiver_is_ambiguous_float_literal) = match &ctx.completion_location { 26 let (dot_receiver, receiver_is_ambiguous_float_literal) = match &ctx.completion_location {
27 Some(ImmediateLocation::MethodCall { receiver: Some(it) }) => (it, false), 27 Some(ImmediateLocation::MethodCall { receiver: Some(it), .. }) => (it, false),
28 Some(ImmediateLocation::FieldAccess { 28 Some(ImmediateLocation::FieldAccess {
29 receiver: Some(it), 29 receiver: Some(it),
30 receiver_is_ambiguous_float_literal, 30 receiver_is_ambiguous_float_literal,
diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs
index de58ce1cd..d58745fb4 100644
--- a/crates/ide_completion/src/completions/qualified_path.rs
+++ b/crates/ide_completion/src/completions/qualified_path.rs
@@ -7,11 +7,11 @@ use syntax::AstNode;
7use crate::{CompletionContext, Completions}; 7use crate::{CompletionContext, Completions};
8 8
9pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) { 9pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) {
10 if ctx.is_path_disallowed() || ctx.expects_item() { 10 if ctx.is_path_disallowed() {
11 return; 11 return;
12 } 12 }
13 let path = match &ctx.path_qual { 13 let path = match ctx.path_qual() {
14 Some(path) => path.clone(), 14 Some(path) => path,
15 None => return, 15 None => return,
16 }; 16 };
17 17
@@ -20,7 +20,8 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
20 None => return, 20 None => return,
21 }; 21 };
22 let context_module = ctx.scope.module(); 22 let context_module = ctx.scope.module();
23 if ctx.expects_assoc_item() { 23
24 if ctx.expects_item() || ctx.expects_assoc_item() {
24 if let hir::PathResolution::Def(hir::ModuleDef::Module(module)) = resolution { 25 if let hir::PathResolution::Def(hir::ModuleDef::Module(module)) = resolution {
25 let module_scope = module.scope(ctx.db, context_module); 26 let module_scope = module.scope(ctx.db, context_module);
26 for (name, def) in module_scope { 27 for (name, def) in module_scope {
@@ -631,17 +632,17 @@ impl MyStruct {
631"#, 632"#,
632 expect![[r##" 633 expect![[r##"
633 md bar 634 md bar
634 ma foo! #[macro_export] macro_rules! foo 635 ma foo!(…) #[macro_export] macro_rules! foo
635 "##]], 636 "##]],
636 ); 637 );
637 } 638 }
638 639
639 #[test] 640 #[test]
640 #[ignore] // FIXME doesn't complete anything atm
641 fn completes_in_item_list() { 641 fn completes_in_item_list() {
642 check( 642 check(
643 r#" 643 r#"
644struct MyStruct {} 644struct MyStruct {}
645#[macro_export]
645macro_rules! foo {} 646macro_rules! foo {}
646mod bar {} 647mod bar {}
647 648
@@ -649,7 +650,7 @@ crate::$0
649"#, 650"#,
650 expect![[r#" 651 expect![[r#"
651 md bar 652 md bar
652 ma foo! macro_rules! foo 653 ma foo!(…) #[macro_export] macro_rules! foo
653 "#]], 654 "#]],
654 ) 655 )
655 } 656 }
diff --git a/crates/ide_completion/src/completions/snippet.rs b/crates/ide_completion/src/completions/snippet.rs
index 6e6a6eb92..b9862de67 100644
--- a/crates/ide_completion/src/completions/snippet.rs
+++ b/crates/ide_completion/src/completions/snippet.rs
@@ -3,8 +3,8 @@
3use ide_db::helpers::SnippetCap; 3use ide_db::helpers::SnippetCap;
4 4
5use crate::{ 5use crate::{
6 item::Builder, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, 6 context::PathCompletionContext, item::Builder, CompletionContext, CompletionItem,
7 Completions, 7 CompletionItemKind, CompletionKind, Completions,
8}; 8};
9 9
10fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) -> Builder { 10fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) -> Builder {
@@ -14,15 +14,21 @@ fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str)
14} 14}
15 15
16pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) { 16pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) {
17 if !(ctx.is_trivial_path && ctx.function_def.is_some()) { 17 if ctx.function_def.is_none() {
18 return; 18 return;
19 } 19 }
20
21 let can_be_stmt = match ctx.path_context {
22 Some(PathCompletionContext { is_trivial_path: true, can_be_stmt, .. }) => can_be_stmt,
23 _ => return,
24 };
25
20 let cap = match ctx.config.snippet_cap { 26 let cap = match ctx.config.snippet_cap {
21 Some(it) => it, 27 Some(it) => it,
22 None => return, 28 None => return,
23 }; 29 };
24 30
25 if ctx.can_be_stmt { 31 if can_be_stmt {
26 snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc); 32 snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc);
27 snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc); 33 snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc);
28 } 34 }
diff --git a/crates/ide_completion/src/completions/trait_impl.rs b/crates/ide_completion/src/completions/trait_impl.rs
index 968c0254d..a60e5f43c 100644
--- a/crates/ide_completion/src/completions/trait_impl.rs
+++ b/crates/ide_completion/src/completions/trait_impl.rs
@@ -34,20 +34,13 @@
34use hir::{self, HasAttrs, HasSource}; 34use hir::{self, HasAttrs, HasSource};
35use ide_db::{traits::get_missing_assoc_items, SymbolKind}; 35use ide_db::{traits::get_missing_assoc_items, SymbolKind};
36use syntax::{ 36use syntax::{
37 ast::{self, edit, Impl}, 37 ast::{self, edit},
38 display::function_declaration, 38 display::function_declaration,
39 AstNode, SyntaxElement, SyntaxKind, SyntaxNode, TextRange, T, 39 AstNode, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, T,
40}; 40};
41use text_edit::TextEdit; 41use text_edit::TextEdit;
42 42
43use crate::{ 43use crate::{CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions};
44 CompletionContext,
45 CompletionItem,
46 CompletionItemKind,
47 CompletionKind,
48 Completions,
49 // display::function_declaration,
50};
51 44
52#[derive(Debug, PartialEq, Eq)] 45#[derive(Debug, PartialEq, Eq)]
53enum ImplCompletionKind { 46enum ImplCompletionKind {
@@ -58,7 +51,7 @@ enum ImplCompletionKind {
58} 51}
59 52
60pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) { 53pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) {
61 if let Some((kind, trigger, impl_def)) = completion_match(ctx) { 54 if let Some((kind, trigger, impl_def)) = completion_match(ctx.token.clone()) {
62 get_missing_assoc_items(&ctx.sema, &impl_def).into_iter().for_each(|item| match item { 55 get_missing_assoc_items(&ctx.sema, &impl_def).into_iter().for_each(|item| match item {
63 hir::AssocItem::Function(fn_item) 56 hir::AssocItem::Function(fn_item)
64 if kind == ImplCompletionKind::All || kind == ImplCompletionKind::Fn => 57 if kind == ImplCompletionKind::All || kind == ImplCompletionKind::Fn =>
@@ -80,8 +73,7 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext
80 } 73 }
81} 74}
82 75
83fn completion_match(ctx: &CompletionContext) -> Option<(ImplCompletionKind, SyntaxNode, Impl)> { 76fn completion_match(mut token: SyntaxToken) -> Option<(ImplCompletionKind, SyntaxNode, ast::Impl)> {
84 let mut token = ctx.token.clone();
85 // For keyword without name like `impl .. { fn $0 }`, the current position is inside 77 // For keyword without name like `impl .. { fn $0 }`, the current position is inside
86 // the whitespace token, which is outside `FN` syntax node. 78 // the whitespace token, which is outside `FN` syntax node.
87 // We need to follow the previous token in this case. 79 // We need to follow the previous token in this case.
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs
index bd955aa85..8b22933e0 100644
--- a/crates/ide_completion/src/completions/unqualified_path.rs
+++ b/crates/ide_completion/src/completions/unqualified_path.rs
@@ -5,26 +5,25 @@ use hir::ScopeDef;
5use crate::{CompletionContext, Completions}; 5use crate::{CompletionContext, Completions};
6 6
7pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { 7pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
8 if !ctx.is_trivial_path { 8 if ctx.is_path_disallowed() || !ctx.is_trivial_path() {
9 return;
10 }
11 if ctx.is_path_disallowed() || ctx.expects_item() {
12 return; 9 return;
13 } 10 }
14 11
15 if ctx.expects_assoc_item() { 12 if ctx.expects_item() || ctx.expects_assoc_item() {
16 ctx.scope.process_all_names(&mut |name, def| { 13 // only show macros in {Assoc}ItemList
17 if let ScopeDef::MacroDef(macro_def) = def { 14 ctx.scope.process_all_names(&mut |name, res| {
18 acc.add_macro(ctx, Some(name.clone()), macro_def); 15 if let hir::ScopeDef::MacroDef(mac) = res {
16 acc.add_macro(ctx, Some(name.clone()), mac);
19 } 17 }
20 if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = def { 18 if let hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res {
21 acc.add_resolution(ctx, name, &def); 19 acc.add_resolution(ctx, name, &res);
22 } 20 }
23 }); 21 });
24 return; 22 return;
25 } 23 }
26 24
27 if ctx.expects_use_tree() { 25 if ctx.expects_use_tree() {
26 // only show modules in a fresh UseTree
28 cov_mark::hit!(only_completes_modules_in_import); 27 cov_mark::hit!(only_completes_modules_in_import);
29 ctx.scope.process_all_names(&mut |name, res| { 28 ctx.scope.process_all_names(&mut |name, res| {
30 if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res { 29 if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res {
@@ -693,12 +692,11 @@ impl MyStruct {
693"#, 692"#,
694 expect![[r#" 693 expect![[r#"
695 md bar 694 md bar
696 ma foo! macro_rules! foo 695 ma foo!(…) macro_rules! foo
697 "#]], 696 "#]],
698 ) 697 )
699 } 698 }
700 699
701 // FIXME: The completions here currently come from `macro_in_item_position`, but they shouldn't
702 #[test] 700 #[test]
703 fn completes_in_item_list() { 701 fn completes_in_item_list() {
704 check( 702 check(
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index cb4f08e53..6177caa12 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -29,6 +29,29 @@ pub(crate) enum PatternRefutability {
29 Irrefutable, 29 Irrefutable,
30} 30}
31 31
32#[derive(Debug)]
33pub(crate) struct PathCompletionContext {
34 /// If this is a call with () already there
35 call_kind: Option<CallKind>,
36 /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path.
37 pub(super) is_trivial_path: bool,
38 /// If not a trivial path, the prefix (qualifier).
39 pub(super) path_qual: Option<ast::Path>,
40 pub(super) is_path_type: bool,
41 pub(super) has_type_args: bool,
42 /// `true` if we are a statement or a last expr in the block.
43 pub(super) can_be_stmt: bool,
44 /// `true` if we expect an expression at the cursor position.
45 pub(super) is_expr: bool,
46 pub(super) in_loop_body: bool,
47}
48
49#[derive(Copy, Clone, Debug, PartialEq, Eq)]
50pub(crate) enum CallKind {
51 Pat,
52 Mac,
53 Expr,
54}
32/// `CompletionContext` is created early during completion to figure out, where 55/// `CompletionContext` is created early during completion to figure out, where
33/// exactly is the cursor, syntax-wise. 56/// exactly is the cursor, syntax-wise.
34#[derive(Debug)] 57#[derive(Debug)]
@@ -45,14 +68,13 @@ pub(crate) struct CompletionContext<'a> {
45 pub(super) krate: Option<hir::Crate>, 68 pub(super) krate: Option<hir::Crate>,
46 pub(super) expected_name: Option<NameOrNameRef>, 69 pub(super) expected_name: Option<NameOrNameRef>,
47 pub(super) expected_type: Option<Type>, 70 pub(super) expected_type: Option<Type>,
48 pub(super) name_ref_syntax: Option<ast::NameRef>,
49
50 pub(super) use_item_syntax: Option<ast::Use>,
51 71
52 /// The parent function of the cursor position if it exists. 72 /// The parent function of the cursor position if it exists.
53 pub(super) function_def: Option<ast::Fn>, 73 pub(super) function_def: Option<ast::Fn>,
54 /// The parent impl of the cursor position if it exists. 74 /// The parent impl of the cursor position if it exists.
55 pub(super) impl_def: Option<ast::Impl>, 75 pub(super) impl_def: Option<ast::Impl>,
76 pub(super) name_ref_syntax: Option<ast::NameRef>,
77 pub(super) use_item_syntax: Option<ast::Use>,
56 78
57 // potentially set if we are completing a lifetime 79 // potentially set if we are completing a lifetime
58 pub(super) lifetime_syntax: Option<ast::Lifetime>, 80 pub(super) lifetime_syntax: Option<ast::Lifetime>,
@@ -67,29 +89,12 @@ pub(crate) struct CompletionContext<'a> {
67 pub(super) completion_location: Option<ImmediateLocation>, 89 pub(super) completion_location: Option<ImmediateLocation>,
68 pub(super) prev_sibling: Option<ImmediatePrevSibling>, 90 pub(super) prev_sibling: Option<ImmediatePrevSibling>,
69 pub(super) attribute_under_caret: Option<ast::Attr>, 91 pub(super) attribute_under_caret: Option<ast::Attr>,
92 pub(super) previous_token: Option<SyntaxToken>,
70 93
71 /// FIXME: `ActiveParameter` is string-based, which is very very wrong 94 pub(super) path_context: Option<PathCompletionContext>,
72 pub(super) active_parameter: Option<ActiveParameter>, 95 pub(super) active_parameter: Option<ActiveParameter>,
73 /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path.
74 pub(super) is_trivial_path: bool,
75 /// If not a trivial path, the prefix (qualifier).
76 pub(super) path_qual: Option<ast::Path>,
77 /// `true` if we are a statement or a last expr in the block.
78 pub(super) can_be_stmt: bool,
79 /// `true` if we expect an expression at the cursor position.
80 pub(super) is_expr: bool,
81 /// If this is a call (method or function) in particular, i.e. the () are already there.
82 pub(super) is_call: bool,
83 /// Like `is_call`, but for tuple patterns.
84 pub(super) is_pattern_call: bool,
85 /// If this is a macro call, i.e. the () are already there.
86 pub(super) is_macro_call: bool,
87 pub(super) is_path_type: bool,
88 pub(super) has_type_args: bool,
89 pub(super) locals: Vec<(String, Local)>, 96 pub(super) locals: Vec<(String, Local)>,
90 97
91 pub(super) previous_token: Option<SyntaxToken>,
92 pub(super) in_loop_body: bool,
93 pub(super) incomplete_let: bool, 98 pub(super) incomplete_let: bool,
94 99
95 no_completion_required: bool, 100 no_completion_required: bool,
@@ -136,36 +141,27 @@ impl<'a> CompletionContext<'a> {
136 original_token, 141 original_token,
137 token, 142 token,
138 krate, 143 krate,
139 lifetime_allowed: false,
140 expected_name: None, 144 expected_name: None,
141 expected_type: None, 145 expected_type: None,
146 function_def: None,
147 impl_def: None,
142 name_ref_syntax: None, 148 name_ref_syntax: None,
149 use_item_syntax: None,
143 lifetime_syntax: None, 150 lifetime_syntax: None,
144 lifetime_param_syntax: None, 151 lifetime_param_syntax: None,
145 function_def: None, 152 lifetime_allowed: false,
146 use_item_syntax: None,
147 impl_def: None,
148 active_parameter: ActiveParameter::at(db, position),
149 is_label_ref: false, 153 is_label_ref: false,
150 is_param: false,
151 is_pat_or_const: None, 154 is_pat_or_const: None,
152 is_trivial_path: false, 155 is_param: false,
153 path_qual: None,
154 can_be_stmt: false,
155 is_expr: false,
156 is_call: false,
157 is_pattern_call: false,
158 is_macro_call: false,
159 is_path_type: false,
160 has_type_args: false,
161 previous_token: None,
162 in_loop_body: false,
163 completion_location: None, 156 completion_location: None,
164 prev_sibling: None, 157 prev_sibling: None,
165 no_completion_required: false,
166 incomplete_let: false,
167 attribute_under_caret: None, 158 attribute_under_caret: None,
159 previous_token: None,
160 path_context: None,
161 active_parameter: ActiveParameter::at(db, position),
168 locals, 162 locals,
163 incomplete_let: false,
164 no_completion_required: false,
169 }; 165 };
170 166
171 let mut original_file = original_file.syntax().clone(); 167 let mut original_file = original_file.syntax().clone();
@@ -250,14 +246,14 @@ impl<'a> CompletionContext<'a> {
250 pub(crate) fn has_dot_receiver(&self) -> bool { 246 pub(crate) fn has_dot_receiver(&self) -> bool {
251 matches!( 247 matches!(
252 &self.completion_location, 248 &self.completion_location,
253 Some(ImmediateLocation::FieldAccess { receiver, .. }) | Some(ImmediateLocation::MethodCall { receiver }) 249 Some(ImmediateLocation::FieldAccess { receiver, .. }) | Some(ImmediateLocation::MethodCall { receiver,.. })
254 if receiver.is_some() 250 if receiver.is_some()
255 ) 251 )
256 } 252 }
257 253
258 pub(crate) fn dot_receiver(&self) -> Option<&ast::Expr> { 254 pub(crate) fn dot_receiver(&self) -> Option<&ast::Expr> {
259 match &self.completion_location { 255 match &self.completion_location {
260 Some(ImmediateLocation::MethodCall { receiver }) 256 Some(ImmediateLocation::MethodCall { receiver, .. })
261 | Some(ImmediateLocation::FieldAccess { receiver, .. }) => receiver.as_ref(), 257 | Some(ImmediateLocation::FieldAccess { receiver, .. }) => receiver.as_ref(),
262 _ => None, 258 _ => None,
263 } 259 }
@@ -275,11 +271,6 @@ impl<'a> CompletionContext<'a> {
275 matches!(self.completion_location, Some(ImmediateLocation::ItemList)) 271 matches!(self.completion_location, Some(ImmediateLocation::ItemList))
276 } 272 }
277 273
278 // fn expects_value(&self) -> bool {
279 pub(crate) fn expects_expression(&self) -> bool {
280 self.is_expr
281 }
282
283 pub(crate) fn has_block_expr_parent(&self) -> bool { 274 pub(crate) fn has_block_expr_parent(&self) -> bool {
284 matches!(self.completion_location, Some(ImmediateLocation::BlockExpr)) 275 matches!(self.completion_location, Some(ImmediateLocation::BlockExpr))
285 } 276 }
@@ -316,6 +307,22 @@ impl<'a> CompletionContext<'a> {
316 ) || self.attribute_under_caret.is_some() 307 ) || self.attribute_under_caret.is_some()
317 } 308 }
318 309
310 pub(crate) fn expects_expression(&self) -> bool {
311 self.path_context.as_ref().map_or(false, |it| it.is_expr)
312 }
313
314 pub(crate) fn path_call_kind(&self) -> Option<CallKind> {
315 self.path_context.as_ref().and_then(|it| it.call_kind)
316 }
317
318 pub(crate) fn is_trivial_path(&self) -> bool {
319 self.path_context.as_ref().map_or(false, |it| it.is_trivial_path)
320 }
321
322 pub(crate) fn path_qual(&self) -> Option<&ast::Path> {
323 self.path_context.as_ref().and_then(|it| it.path_qual.as_ref())
324 }
325
319 fn fill_impl_def(&mut self) { 326 fn fill_impl_def(&mut self) {
320 self.impl_def = self 327 self.impl_def = self
321 .sema 328 .sema
@@ -441,7 +448,6 @@ impl<'a> CompletionContext<'a> {
441 let for_is_prev2 = for_is_prev2(syntax_element.clone()); 448 let for_is_prev2 = for_is_prev2(syntax_element.clone());
442 (fn_is_prev && !inside_impl_trait_block) || for_is_prev2 449 (fn_is_prev && !inside_impl_trait_block) || for_is_prev2
443 }; 450 };
444 self.in_loop_body = is_in_loop_body(syntax_element.clone());
445 451
446 self.incomplete_let = 452 self.incomplete_let =
447 syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| { 453 syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| {
@@ -549,10 +555,6 @@ impl<'a> CompletionContext<'a> {
549 self.name_ref_syntax = 555 self.name_ref_syntax =
550 find_node_at_offset(original_file, name_ref.syntax().text_range().start()); 556 find_node_at_offset(original_file, name_ref.syntax().text_range().start());
551 557
552 if matches!(self.completion_location, Some(ImmediateLocation::ItemList)) {
553 return;
554 }
555
556 self.use_item_syntax = 558 self.use_item_syntax =
557 self.sema.token_ancestors_with_macros(self.token.clone()).find_map(ast::Use::cast); 559 self.sema.token_ancestors_with_macros(self.token.clone()).find_map(ast::Use::cast);
558 560
@@ -568,22 +570,34 @@ impl<'a> CompletionContext<'a> {
568 }; 570 };
569 571
570 if let Some(segment) = ast::PathSegment::cast(parent) { 572 if let Some(segment) = ast::PathSegment::cast(parent) {
573 let path_ctx = self.path_context.get_or_insert(PathCompletionContext {
574 call_kind: None,
575 is_trivial_path: false,
576 path_qual: None,
577 has_type_args: false,
578 is_path_type: false,
579 can_be_stmt: false,
580 is_expr: false,
581 in_loop_body: false,
582 });
583 path_ctx.in_loop_body = is_in_loop_body(name_ref.syntax());
571 let path = segment.parent_path(); 584 let path = segment.parent_path();
572 self.is_call = path
573 .syntax()
574 .parent()
575 .and_then(ast::PathExpr::cast)
576 .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast))
577 .is_some();
578 self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some();
579 self.is_pattern_call =
580 path.syntax().parent().and_then(ast::TupleStructPat::cast).is_some();
581 585
582 self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); 586 if let Some(p) = path.syntax().parent() {
583 self.has_type_args = segment.generic_arg_list().is_some(); 587 path_ctx.call_kind = match_ast! {
588 match p {
589 ast::PathExpr(it) => it.syntax().parent().and_then(ast::CallExpr::cast).map(|_| CallKind::Expr),
590 ast::MacroCall(it) => it.excl_token().and(Some(CallKind::Mac)),
591 ast::TupleStructPat(_it) => Some(CallKind::Pat),
592 _ => None
593 }
594 };
595 }
596 path_ctx.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some();
597 path_ctx.has_type_args = segment.generic_arg_list().is_some();
584 598
585 if let Some(path) = path_or_use_tree_qualifier(&path) { 599 if let Some(path) = path_or_use_tree_qualifier(&path) {
586 self.path_qual = path 600 path_ctx.path_qual = path
587 .segment() 601 .segment()
588 .and_then(|it| { 602 .and_then(|it| {
589 find_node_with_range::<ast::PathSegment>( 603 find_node_with_range::<ast::PathSegment>(
@@ -601,11 +615,11 @@ impl<'a> CompletionContext<'a> {
601 } 615 }
602 } 616 }
603 617
604 self.is_trivial_path = true; 618 path_ctx.is_trivial_path = true;
605 619
606 // Find either enclosing expr statement (thing with `;`) or a 620 // Find either enclosing expr statement (thing with `;`) or a
607 // block. If block, check that we are the last expr. 621 // block. If block, check that we are the last expr.
608 self.can_be_stmt = name_ref 622 path_ctx.can_be_stmt = name_ref
609 .syntax() 623 .syntax()
610 .ancestors() 624 .ancestors()
611 .find_map(|node| { 625 .find_map(|node| {
@@ -621,10 +635,8 @@ impl<'a> CompletionContext<'a> {
621 None 635 None
622 }) 636 })
623 .unwrap_or(false); 637 .unwrap_or(false);
624 self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some(); 638 path_ctx.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some();
625 } 639 }
626 self.is_call |=
627 matches!(self.completion_location, Some(ImmediateLocation::MethodCall { .. }));
628 } 640 }
629} 641}
630 642
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs
index 6fb38f50d..18983aa01 100644
--- a/crates/ide_completion/src/lib.rs
+++ b/crates/ide_completion/src/lib.rs
@@ -158,7 +158,6 @@ pub fn completions(
158 completions::record::complete_record(&mut acc, &ctx); 158 completions::record::complete_record(&mut acc, &ctx);
159 completions::pattern::complete_pattern(&mut acc, &ctx); 159 completions::pattern::complete_pattern(&mut acc, &ctx);
160 completions::postfix::complete_postfix(&mut acc, &ctx); 160 completions::postfix::complete_postfix(&mut acc, &ctx);
161 completions::macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx);
162 completions::trait_impl::complete_trait_impl(&mut acc, &ctx); 161 completions::trait_impl::complete_trait_impl(&mut acc, &ctx);
163 completions::mod_::complete_mod(&mut acc, &ctx); 162 completions::mod_::complete_mod(&mut acc, &ctx);
164 completions::flyimport::import_on_the_fly(&mut acc, &ctx); 163 completions::flyimport::import_on_the_fly(&mut acc, &ctx);
diff --git a/crates/ide_completion/src/patterns.rs b/crates/ide_completion/src/patterns.rs
index 080898aef..ee87bf461 100644
--- a/crates/ide_completion/src/patterns.rs
+++ b/crates/ide_completion/src/patterns.rs
@@ -4,7 +4,7 @@ use hir::Semantics;
4use ide_db::RootDatabase; 4use ide_db::RootDatabase;
5use syntax::{ 5use syntax::{
6 algo::non_trivia_sibling, 6 algo::non_trivia_sibling,
7 ast::{self, LoopBodyOwner}, 7 ast::{self, ArgListOwner, LoopBodyOwner},
8 match_ast, AstNode, Direction, SyntaxElement, 8 match_ast, AstNode, Direction, SyntaxElement,
9 SyntaxKind::*, 9 SyntaxKind::*,
10 SyntaxNode, SyntaxToken, TextRange, TextSize, T, 10 SyntaxNode, SyntaxToken, TextRange, TextSize, T,
@@ -39,6 +39,7 @@ pub(crate) enum ImmediateLocation {
39 // Original file ast node 39 // Original file ast node
40 MethodCall { 40 MethodCall {
41 receiver: Option<ast::Expr>, 41 receiver: Option<ast::Expr>,
42 has_parens: bool,
42 }, 43 },
43 // Original file ast node 44 // Original file ast node
44 FieldAccess { 45 FieldAccess {
@@ -204,6 +205,7 @@ pub(crate) fn determine_location(
204 .receiver() 205 .receiver()
205 .map(|e| e.syntax().text_range()) 206 .map(|e| e.syntax().text_range())
206 .and_then(|r| find_node_with_range(original_file, r)), 207 .and_then(|r| find_node_with_range(original_file, r)),
208 has_parens: it.arg_list().map_or(false, |it| it.l_paren_token().is_some())
207 }, 209 },
208 _ => return None, 210 _ => return None,
209 } 211 }
@@ -270,9 +272,8 @@ fn test_for_is_prev2() {
270 check_pattern_is_applicable(r"for i i$0", for_is_prev2); 272 check_pattern_is_applicable(r"for i i$0", for_is_prev2);
271} 273}
272 274
273pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool { 275pub(crate) fn is_in_loop_body(node: &SyntaxNode) -> bool {
274 element 276 node.ancestors()
275 .ancestors()
276 .take_while(|it| it.kind() != FN && it.kind() != CLOSURE_EXPR) 277 .take_while(|it| it.kind() != FN && it.kind() != CLOSURE_EXPR)
277 .find_map(|it| { 278 .find_map(|it| {
278 let loop_body = match_ast! { 279 let loop_body = match_ast! {
@@ -283,7 +284,7 @@ pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool {
283 _ => None, 284 _ => None,
284 } 285 }
285 }; 286 };
286 loop_body.filter(|it| it.syntax().text_range().contains_range(element.text_range())) 287 loop_body.filter(|it| it.syntax().text_range().contains_range(node.text_range()))
287 }) 288 })
288 .is_some() 289 .is_some()
289} 290}
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs
index a49a60711..750694432 100644
--- a/crates/ide_completion/src/render.rs
+++ b/crates/ide_completion/src/render.rs
@@ -275,8 +275,12 @@ impl<'a> Render<'a> {
275 }; 275 };
276 276
277 // Add `<>` for generic types 277 // Add `<>` for generic types
278 if self.ctx.completion.is_path_type 278 if self
279 && !self.ctx.completion.has_type_args 279 .ctx
280 .completion
281 .path_context
282 .as_ref()
283 .map_or(false, |it| it.is_path_type && !it.has_type_args)
280 && self.ctx.completion.config.add_call_parenthesis 284 && self.ctx.completion.config.add_call_parenthesis
281 { 285 {
282 if let Some(cap) = self.ctx.snippet_cap() { 286 if let Some(cap) = self.ctx.snippet_cap() {
diff --git a/crates/ide_completion/src/render/builder_ext.rs b/crates/ide_completion/src/render/builder_ext.rs
index 6d062b3b9..c54752d30 100644
--- a/crates/ide_completion/src/render/builder_ext.rs
+++ b/crates/ide_completion/src/render/builder_ext.rs
@@ -2,7 +2,7 @@
2 2
3use itertools::Itertools; 3use itertools::Itertools;
4 4
5use crate::{item::Builder, CompletionContext}; 5use crate::{context::CallKind, item::Builder, patterns::ImmediateLocation, CompletionContext};
6 6
7#[derive(Debug)] 7#[derive(Debug)]
8pub(super) enum Params { 8pub(super) enum Params {
@@ -32,10 +32,12 @@ impl Builder {
32 cov_mark::hit!(no_parens_in_use_item); 32 cov_mark::hit!(no_parens_in_use_item);
33 return false; 33 return false;
34 } 34 }
35 if ctx.is_pattern_call { 35 if matches!(ctx.path_call_kind(), Some(CallKind::Expr) | Some(CallKind::Pat))
36 return false; 36 | matches!(
37 } 37 ctx.completion_location,
38 if ctx.is_call { 38 Some(ImmediateLocation::MethodCall { has_parens: true, .. })
39 )
40 {
39 return false; 41 return false;
40 } 42 }
41 43
diff --git a/crates/ide_completion/src/render/macro_.rs b/crates/ide_completion/src/render/macro_.rs
index 0dfba8acc..429d937c8 100644
--- a/crates/ide_completion/src/render/macro_.rs
+++ b/crates/ide_completion/src/render/macro_.rs
@@ -5,6 +5,7 @@ use ide_db::SymbolKind;
5use syntax::display::macro_label; 5use syntax::display::macro_label;
6 6
7use crate::{ 7use crate::{
8 context::CallKind,
8 item::{CompletionItem, CompletionKind, ImportEdit}, 9 item::{CompletionItem, CompletionKind, ImportEdit},
9 render::RenderContext, 10 render::RenderContext,
10}; 11};
@@ -68,7 +69,8 @@ impl<'a> MacroRender<'a> {
68 } 69 }
69 70
70 fn needs_bang(&self) -> bool { 71 fn needs_bang(&self) -> bool {
71 self.ctx.completion.use_item_syntax.is_none() && !self.ctx.completion.is_macro_call 72 self.ctx.completion.use_item_syntax.is_none()
73 && !matches!(self.ctx.completion.path_call_kind(), Some(CallKind::Mac))
72 } 74 }
73 75
74 fn label(&self) -> String { 76 fn label(&self) -> String {