aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ide_completion/src/completions/dot.rs7
-rw-r--r--crates/ide_completion/src/completions/flyimport.rs6
-rw-r--r--crates/ide_completion/src/completions/keyword.rs2
-rw-r--r--crates/ide_completion/src/completions/macro_in_item_position.rs2
-rw-r--r--crates/ide_completion/src/completions/postfix.rs17
-rw-r--r--crates/ide_completion/src/completions/snippet.rs2
-rw-r--r--crates/ide_completion/src/context.rs65
-rw-r--r--crates/ide_completion/src/patterns.rs49
-rw-r--r--crates/ide_completion/src/render/function.rs2
9 files changed, 86 insertions, 66 deletions
diff --git a/crates/ide_completion/src/completions/dot.rs b/crates/ide_completion/src/completions/dot.rs
index 302c9ccbd..e0a7021fd 100644
--- a/crates/ide_completion/src/completions/dot.rs
+++ b/crates/ide_completion/src/completions/dot.rs
@@ -8,7 +8,7 @@ use crate::{context::CompletionContext, 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) {
11 let dot_receiver = match &ctx.dot_receiver { 11 let dot_receiver = match ctx.dot_receiver() {
12 Some(expr) => expr, 12 Some(expr) => expr,
13 _ => return complete_undotted_self(acc, ctx), 13 _ => return complete_undotted_self(acc, ctx),
14 }; 14 };
@@ -30,7 +30,10 @@ pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
30} 30}
31 31
32fn complete_undotted_self(acc: &mut Completions, ctx: &CompletionContext) { 32fn complete_undotted_self(acc: &mut Completions, ctx: &CompletionContext) {
33 if !ctx.is_trivial_path || !ctx.config.enable_self_on_the_fly { 33 if !ctx.config.enable_self_on_the_fly {
34 return;
35 }
36 if !ctx.is_trivial_path || ctx.is_path_disallowed() {
34 return; 37 return;
35 } 38 }
36 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 df27e7a84..d72bf13d3 100644
--- a/crates/ide_completion/src/completions/flyimport.rs
+++ b/crates/ide_completion/src/completions/flyimport.rs
@@ -162,19 +162,19 @@ pub(crate) fn position_for_import<'a>(
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.as_ref()?.syntax(),
165 Some(ImportCandidate::TraitMethod(_)) => ctx.dot_receiver.as_ref()?.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.as_ref().map(|path| path.syntax()))
171 .or_else(|| ctx.dot_receiver.as_ref().map(|expr| expr.syntax()))?, 171 .or_else(|| ctx.dot_receiver().map(|expr| expr.syntax()))?,
172 }) 172 })
173} 173}
174 174
175fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAssets> { 175fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAssets> {
176 let current_module = ctx.scope.module()?; 176 let current_module = ctx.scope.module()?;
177 if let Some(dot_receiver) = &ctx.dot_receiver { 177 if let Some(dot_receiver) = ctx.dot_receiver() {
178 ImportAssets::for_fuzzy_method_call( 178 ImportAssets::for_fuzzy_method_call(
179 current_module, 179 current_module,
180 ctx.sema.type_of_expr(dot_receiver)?, 180 ctx.sema.type_of_expr(dot_receiver)?,
diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs
index 0d035c611..1a7a484a4 100644
--- a/crates/ide_completion/src/completions/keyword.rs
+++ b/crates/ide_completion/src/completions/keyword.rs
@@ -31,7 +31,7 @@ pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC
31 } 31 }
32 32
33 // Suggest .await syntax for types that implement Future trait 33 // Suggest .await syntax for types that implement Future trait
34 if let Some(receiver) = &ctx.dot_receiver { 34 if let Some(receiver) = ctx.dot_receiver() {
35 if let Some(ty) = ctx.sema.type_of_expr(receiver) { 35 if let Some(ty) = ctx.sema.type_of_expr(receiver) {
36 if ty.impls_future(ctx.db) { 36 if ty.impls_future(ctx.db) {
37 let mut item = kw_completion("await"); 37 let mut item = kw_completion("await");
diff --git a/crates/ide_completion/src/completions/macro_in_item_position.rs b/crates/ide_completion/src/completions/macro_in_item_position.rs
index 202e71215..781b96ff1 100644
--- a/crates/ide_completion/src/completions/macro_in_item_position.rs
+++ b/crates/ide_completion/src/completions/macro_in_item_position.rs
@@ -5,7 +5,7 @@ use crate::{CompletionContext, Completions};
5// Ideally this should be removed and moved into `(un)qualified_path` respectively 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) { 6pub(crate) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &CompletionContext) {
7 // Show only macros in top level. 7 // Show only macros in top level.
8 if !ctx.is_new_item { 8 if !ctx.expects_item() {
9 return; 9 return;
10 } 10 }
11 11
diff --git a/crates/ide_completion/src/completions/postfix.rs b/crates/ide_completion/src/completions/postfix.rs
index 962aaf0df..86bbb58e2 100644
--- a/crates/ide_completion/src/completions/postfix.rs
+++ b/crates/ide_completion/src/completions/postfix.rs
@@ -14,6 +14,7 @@ use crate::{
14 completions::postfix::format_like::add_format_like_completions, 14 completions::postfix::format_like::add_format_like_completions,
15 context::CompletionContext, 15 context::CompletionContext,
16 item::{Builder, CompletionKind}, 16 item::{Builder, CompletionKind},
17 patterns::ImmediateLocation,
17 CompletionItem, CompletionItemKind, Completions, 18 CompletionItem, CompletionItemKind, Completions,
18}; 19};
19 20
@@ -22,13 +23,16 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
22 return; 23 return;
23 } 24 }
24 25
25 let dot_receiver = match &ctx.dot_receiver { 26 let (dot_receiver, receiver_is_ambiguous_float_literal) = match &ctx.completion_location {
26 Some(it) => it, 27 Some(ImmediateLocation::MethodCall { receiver: Some(it) }) => (it, false),
27 None => return, 28 Some(ImmediateLocation::FieldAccess {
29 receiver: Some(it),
30 receiver_is_ambiguous_float_literal,
31 }) => (it, *receiver_is_ambiguous_float_literal),
32 _ => return,
28 }; 33 };
29 34
30 let receiver_text = 35 let receiver_text = get_receiver_text(dot_receiver, receiver_is_ambiguous_float_literal);
31 get_receiver_text(dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal);
32 36
33 let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) { 37 let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) {
34 Some(it) => it, 38 Some(it) => it,
@@ -123,8 +127,7 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
123 // The rest of the postfix completions create an expression that moves an argument, 127 // The rest of the postfix completions create an expression that moves an argument,
124 // so it's better to consider references now to avoid breaking the compilation 128 // so it's better to consider references now to avoid breaking the compilation
125 let dot_receiver = include_references(dot_receiver); 129 let dot_receiver = include_references(dot_receiver);
126 let receiver_text = 130 let receiver_text = get_receiver_text(&dot_receiver, receiver_is_ambiguous_float_literal);
127 get_receiver_text(&dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal);
128 131
129 match try_enum { 132 match try_enum {
130 Some(try_enum) => match try_enum { 133 Some(try_enum) => match try_enum {
diff --git a/crates/ide_completion/src/completions/snippet.rs b/crates/ide_completion/src/completions/snippet.rs
index defc25b00..6e6a6eb92 100644
--- a/crates/ide_completion/src/completions/snippet.rs
+++ b/crates/ide_completion/src/completions/snippet.rs
@@ -29,7 +29,7 @@ pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionConte
29} 29}
30 30
31pub(crate) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionContext) { 31pub(crate) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionContext) {
32 if !ctx.is_new_item { 32 if !ctx.expects_item() {
33 return; 33 return;
34 } 34 }
35 let cap = match ctx.config.snippet_cap { 35 let cap = match ctx.config.snippet_cap {
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index 7c46c815d..6f685c02f 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -78,11 +78,6 @@ pub(crate) struct CompletionContext<'a> {
78 pub(super) can_be_stmt: bool, 78 pub(super) can_be_stmt: bool,
79 /// `true` if we expect an expression at the cursor position. 79 /// `true` if we expect an expression at the cursor position.
80 pub(super) is_expr: bool, 80 pub(super) is_expr: bool,
81 /// Something is typed at the "top" level, in module or impl/trait.
82 pub(super) is_new_item: bool,
83 /// The receiver if this is a field or method access, i.e. writing something.$0
84 pub(super) dot_receiver: Option<ast::Expr>,
85 pub(super) dot_receiver_is_ambiguous_float_literal: bool,
86 /// If this is a call (method or function) in particular, i.e. the () are already there. 81 /// If this is a call (method or function) in particular, i.e. the () are already there.
87 pub(super) is_call: bool, 82 pub(super) is_call: bool,
88 /// Like `is_call`, but for tuple patterns. 83 /// Like `is_call`, but for tuple patterns.
@@ -158,9 +153,6 @@ impl<'a> CompletionContext<'a> {
158 path_qual: None, 153 path_qual: None,
159 can_be_stmt: false, 154 can_be_stmt: false,
160 is_expr: false, 155 is_expr: false,
161 is_new_item: false,
162 dot_receiver: None,
163 dot_receiver_is_ambiguous_float_literal: false,
164 is_call: false, 156 is_call: false,
165 is_pattern_call: false, 157 is_pattern_call: false,
166 is_macro_call: false, 158 is_macro_call: false,
@@ -255,6 +247,22 @@ impl<'a> CompletionContext<'a> {
255 ) 247 )
256 } 248 }
257 249
250 pub(crate) fn has_dot_receiver(&self) -> bool {
251 matches!(
252 &self.completion_location,
253 Some(ImmediateLocation::FieldAccess { receiver, .. }) | Some(ImmediateLocation::MethodCall { receiver })
254 if receiver.is_some()
255 )
256 }
257
258 pub(crate) fn dot_receiver(&self) -> Option<&ast::Expr> {
259 match &self.completion_location {
260 Some(ImmediateLocation::MethodCall { receiver })
261 | Some(ImmediateLocation::FieldAccess { receiver, .. }) => receiver.as_ref(),
262 _ => None,
263 }
264 }
265
258 pub(crate) fn expects_use_tree(&self) -> bool { 266 pub(crate) fn expects_use_tree(&self) -> bool {
259 matches!(self.completion_location, Some(ImmediateLocation::Use)) 267 matches!(self.completion_location, Some(ImmediateLocation::Use))
260 } 268 }
@@ -267,6 +275,7 @@ impl<'a> CompletionContext<'a> {
267 matches!(self.completion_location, Some(ImmediateLocation::ItemList)) 275 matches!(self.completion_location, Some(ImmediateLocation::ItemList))
268 } 276 }
269 277
278 // fn expects_value(&self) -> bool {
270 pub(crate) fn expects_expression(&self) -> bool { 279 pub(crate) fn expects_expression(&self) -> bool {
271 self.is_expr 280 self.is_expr
272 } 281 }
@@ -540,16 +549,7 @@ impl<'a> CompletionContext<'a> {
540 self.name_ref_syntax = 549 self.name_ref_syntax =
541 find_node_at_offset(original_file, name_ref.syntax().text_range().start()); 550 find_node_at_offset(original_file, name_ref.syntax().text_range().start());
542 551
543 let name_range = name_ref.syntax().text_range(); 552 if matches!(self.completion_location, Some(ImmediateLocation::ItemList)) {
544 let top_node = name_ref
545 .syntax()
546 .ancestors()
547 .take_while(|it| it.text_range() == name_range)
548 .last()
549 .unwrap();
550
551 if matches!(top_node.parent().map(|it| it.kind()), Some(SOURCE_FILE) | Some(ITEM_LIST)) {
552 self.is_new_item = true;
553 return; 553 return;
554 } 554 }
555 555
@@ -623,33 +623,8 @@ impl<'a> CompletionContext<'a> {
623 .unwrap_or(false); 623 .unwrap_or(false);
624 self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some(); 624 self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some();
625 } 625 }
626 626 self.is_call |=
627 if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) { 627 matches!(self.completion_location, Some(ImmediateLocation::MethodCall { .. }));
628 // The receiver comes before the point of insertion of the fake
629 // ident, so it should have the same range in the non-modified file
630 self.dot_receiver = field_expr
631 .expr()
632 .map(|e| e.syntax().text_range())
633 .and_then(|r| find_node_with_range(original_file, r));
634 self.dot_receiver_is_ambiguous_float_literal =
635 if let Some(ast::Expr::Literal(l)) = &self.dot_receiver {
636 match l.kind() {
637 ast::LiteralKind::FloatNumber { .. } => l.token().text().ends_with('.'),
638 _ => false,
639 }
640 } else {
641 false
642 };
643 }
644
645 if let Some(method_call_expr) = ast::MethodCallExpr::cast(parent) {
646 // As above
647 self.dot_receiver = method_call_expr
648 .receiver()
649 .map(|e| e.syntax().text_range())
650 .and_then(|r| find_node_with_range(original_file, r));
651 self.is_call = true;
652 }
653 } 628 }
654} 629}
655 630
diff --git a/crates/ide_completion/src/patterns.rs b/crates/ide_completion/src/patterns.rs
index 26516046b..080898aef 100644
--- a/crates/ide_completion/src/patterns.rs
+++ b/crates/ide_completion/src/patterns.rs
@@ -7,13 +7,13 @@ use syntax::{
7 ast::{self, LoopBodyOwner}, 7 ast::{self, LoopBodyOwner},
8 match_ast, AstNode, Direction, SyntaxElement, 8 match_ast, AstNode, Direction, SyntaxElement,
9 SyntaxKind::*, 9 SyntaxKind::*,
10 SyntaxNode, SyntaxToken, TextSize, T, 10 SyntaxNode, SyntaxToken, TextRange, TextSize, T,
11}; 11};
12 12
13#[cfg(test)] 13#[cfg(test)]
14use crate::test_utils::{check_pattern_is_applicable, check_pattern_is_not_applicable}; 14use crate::test_utils::{check_pattern_is_applicable, check_pattern_is_not_applicable};
15 15
16/// Direct parent container of the cursor position 16/// Immediate previous node to what we are completing.
17#[derive(Copy, Clone, Debug, PartialEq, Eq)] 17#[derive(Copy, Clone, Debug, PartialEq, Eq)]
18pub(crate) enum ImmediatePrevSibling { 18pub(crate) enum ImmediatePrevSibling {
19 IfExpr, 19 IfExpr,
@@ -21,7 +21,7 @@ pub(crate) enum ImmediatePrevSibling {
21 ImplDefType, 21 ImplDefType,
22} 22}
23 23
24/// Direct parent container of the cursor position 24/// Direct parent "thing" of what we are currently completing.
25#[derive(Clone, Debug, PartialEq, Eq)] 25#[derive(Clone, Debug, PartialEq, Eq)]
26pub(crate) enum ImmediateLocation { 26pub(crate) enum ImmediateLocation {
27 Use, 27 Use,
@@ -37,6 +37,15 @@ pub(crate) enum ImmediateLocation {
37 // Fake file ast node 37 // Fake file ast node
38 ModDeclaration(ast::Module), 38 ModDeclaration(ast::Module),
39 // Original file ast node 39 // Original file ast node
40 MethodCall {
41 receiver: Option<ast::Expr>,
42 },
43 // Original file ast node
44 FieldAccess {
45 receiver: Option<ast::Expr>,
46 receiver_is_ambiguous_float_literal: bool,
47 },
48 // Original file ast node
40 /// The record expr of the field name we are completing 49 /// The record expr of the field name we are completing
41 RecordExpr(ast::RecordExpr), 50 RecordExpr(ast::RecordExpr),
42 // Original file ast node 51 // Original file ast node
@@ -164,12 +173,38 @@ pub(crate) fn determine_location(
164 Some(TRAIT) => ImmediateLocation::Trait, 173 Some(TRAIT) => ImmediateLocation::Trait,
165 _ => return None, 174 _ => return None,
166 }, 175 },
167 ast::Module(it) => if it.item_list().is_none() { 176 ast::Module(it) => {
177 if it.item_list().is_none() {
168 ImmediateLocation::ModDeclaration(it) 178 ImmediateLocation::ModDeclaration(it)
169 } else { 179 } else {
170 return None 180 return None;
181 }
171 }, 182 },
172 ast::Attr(it) => ImmediateLocation::Attribute(it), 183 ast::Attr(it) => ImmediateLocation::Attribute(it),
184 ast::FieldExpr(it) => {
185 let receiver = it
186 .expr()
187 .map(|e| e.syntax().text_range())
188 .and_then(|r| find_node_with_range(original_file, r));
189 let receiver_is_ambiguous_float_literal = if let Some(ast::Expr::Literal(l)) = &receiver {
190 match l.kind() {
191 ast::LiteralKind::FloatNumber { .. } => l.token().text().ends_with('.'),
192 _ => false,
193 }
194 } else {
195 false
196 };
197 ImmediateLocation::FieldAccess {
198 receiver,
199 receiver_is_ambiguous_float_literal,
200 }
201 },
202 ast::MethodCallExpr(it) => ImmediateLocation::MethodCall {
203 receiver: it
204 .receiver()
205 .map(|e| e.syntax().text_range())
206 .and_then(|r| find_node_with_range(original_file, r)),
207 },
173 _ => return None, 208 _ => return None,
174 } 209 }
175 }; 210 };
@@ -194,6 +229,10 @@ fn maximize_name_ref(name_ref: &ast::NameRef) -> SyntaxNode {
194 name_ref.syntax().clone() 229 name_ref.syntax().clone()
195} 230}
196 231
232fn find_node_with_range<N: AstNode>(syntax: &SyntaxNode, range: TextRange) -> Option<N> {
233 syntax.covering_element(range).ancestors().find_map(N::cast)
234}
235
197pub(crate) fn inside_impl_trait_block(element: SyntaxElement) -> bool { 236pub(crate) fn inside_impl_trait_block(element: SyntaxElement) -> bool {
198 // Here we search `impl` keyword up through the all ancestors, unlike in `has_impl_parent`, 237 // Here we search `impl` keyword up through the all ancestors, unlike in `has_impl_parent`,
199 // where we only check the first parent with different text range. 238 // where we only check the first parent with different text range.
diff --git a/crates/ide_completion/src/render/function.rs b/crates/ide_completion/src/render/function.rs
index 3ec77ca0f..1abeed96d 100644
--- a/crates/ide_completion/src/render/function.rs
+++ b/crates/ide_completion/src/render/function.rs
@@ -154,7 +154,7 @@ impl<'a> FunctionRender<'a> {
154 }; 154 };
155 155
156 let mut params_pats = Vec::new(); 156 let mut params_pats = Vec::new();
157 let params_ty = if self.ctx.completion.dot_receiver.is_some() || self.receiver.is_some() { 157 let params_ty = if self.ctx.completion.has_dot_receiver() || self.receiver.is_some() {
158 self.func.method_params(self.ctx.db()).unwrap_or_default() 158 self.func.method_params(self.ctx.db()).unwrap_or_default()
159 } else { 159 } else {
160 if let Some(s) = ast_params.self_param() { 160 if let Some(s) = ast_params.self_param() {