diff options
author | Lukas Wirth <[email protected]> | 2021-06-02 14:21:18 +0100 |
---|---|---|
committer | Lukas Wirth <[email protected]> | 2021-06-02 14:21:18 +0100 |
commit | 9271941a950026836511bd1c85e15e26a480b824 (patch) | |
tree | 972b83f69c876003df59596cb8d109dc1b28c5d4 /crates/ide_completion/src | |
parent | dbdfeeeff91b5e42d8687df09dda1d29f99b34f8 (diff) |
Add MethodCall and FieldAccess variants to ImmediateLocation
Diffstat (limited to 'crates/ide_completion/src')
-rw-r--r-- | crates/ide_completion/src/completions/dot.rs | 7 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/flyimport.rs | 6 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/keyword.rs | 2 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/postfix.rs | 17 | ||||
-rw-r--r-- | crates/ide_completion/src/context.rs | 51 | ||||
-rw-r--r-- | crates/ide_completion/src/patterns.rs | 45 | ||||
-rw-r--r-- | crates/ide_completion/src/render/function.rs | 2 |
7 files changed, 81 insertions, 49 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. |
10 | pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { | 10 | pub(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 | ||
32 | fn complete_undotted_self(acc: &mut Completions, ctx: &CompletionContext) { | 32 | fn 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 | ||
175 | fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAssets> { | 175 | fn 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/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/context.rs b/crates/ide_completion/src/context.rs index 7c46c815d..eeb4333f8 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs | |||
@@ -80,9 +80,6 @@ pub(crate) struct CompletionContext<'a> { | |||
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. | 81 | /// Something is typed at the "top" level, in module or impl/trait. |
82 | pub(super) is_new_item: bool, | 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. | 83 | /// If this is a call (method or function) in particular, i.e. the () are already there. |
87 | pub(super) is_call: bool, | 84 | pub(super) is_call: bool, |
88 | /// Like `is_call`, but for tuple patterns. | 85 | /// Like `is_call`, but for tuple patterns. |
@@ -159,8 +156,6 @@ impl<'a> CompletionContext<'a> { | |||
159 | can_be_stmt: false, | 156 | can_be_stmt: false, |
160 | is_expr: false, | 157 | is_expr: false, |
161 | is_new_item: false, | 158 | is_new_item: false, |
162 | dot_receiver: None, | ||
163 | dot_receiver_is_ambiguous_float_literal: false, | ||
164 | is_call: false, | 159 | is_call: false, |
165 | is_pattern_call: false, | 160 | is_pattern_call: false, |
166 | is_macro_call: false, | 161 | is_macro_call: false, |
@@ -255,6 +250,22 @@ impl<'a> CompletionContext<'a> { | |||
255 | ) | 250 | ) |
256 | } | 251 | } |
257 | 252 | ||
253 | pub(crate) fn has_dot_receiver(&self) -> bool { | ||
254 | matches!( | ||
255 | &self.completion_location, | ||
256 | Some(ImmediateLocation::FieldAccess { receiver, .. }) | Some(ImmediateLocation::MethodCall { receiver }) | ||
257 | if receiver.is_some() | ||
258 | ) | ||
259 | } | ||
260 | |||
261 | pub(crate) fn dot_receiver(&self) -> Option<&ast::Expr> { | ||
262 | match &self.completion_location { | ||
263 | Some(ImmediateLocation::MethodCall { receiver }) | ||
264 | | Some(ImmediateLocation::FieldAccess { receiver, .. }) => receiver.as_ref(), | ||
265 | _ => None, | ||
266 | } | ||
267 | } | ||
268 | |||
258 | pub(crate) fn expects_use_tree(&self) -> bool { | 269 | pub(crate) fn expects_use_tree(&self) -> bool { |
259 | matches!(self.completion_location, Some(ImmediateLocation::Use)) | 270 | matches!(self.completion_location, Some(ImmediateLocation::Use)) |
260 | } | 271 | } |
@@ -267,6 +278,7 @@ impl<'a> CompletionContext<'a> { | |||
267 | matches!(self.completion_location, Some(ImmediateLocation::ItemList)) | 278 | matches!(self.completion_location, Some(ImmediateLocation::ItemList)) |
268 | } | 279 | } |
269 | 280 | ||
281 | // fn expects_value(&self) -> bool { | ||
270 | pub(crate) fn expects_expression(&self) -> bool { | 282 | pub(crate) fn expects_expression(&self) -> bool { |
271 | self.is_expr | 283 | self.is_expr |
272 | } | 284 | } |
@@ -623,33 +635,8 @@ impl<'a> CompletionContext<'a> { | |||
623 | .unwrap_or(false); | 635 | .unwrap_or(false); |
624 | self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some(); | 636 | self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some(); |
625 | } | 637 | } |
626 | 638 | self.is_call |= | |
627 | if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) { | 639 | 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 | } | 640 | } |
654 | } | 641 | } |
655 | 642 | ||
diff --git a/crates/ide_completion/src/patterns.rs b/crates/ide_completion/src/patterns.rs index 26516046b..bf3a3f61e 100644 --- a/crates/ide_completion/src/patterns.rs +++ b/crates/ide_completion/src/patterns.rs | |||
@@ -7,7 +7,7 @@ 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)] |
@@ -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 | ||
232 | fn find_node_with_range<N: AstNode>(syntax: &SyntaxNode, range: TextRange) -> Option<N> { | ||
233 | syntax.covering_element(range).ancestors().find_map(N::cast) | ||
234 | } | ||
235 | |||
197 | pub(crate) fn inside_impl_trait_block(element: SyntaxElement) -> bool { | 236 | pub(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() { |