diff options
author | Florian Diebold <[email protected]> | 2018-12-25 16:43:58 +0000 |
---|---|---|
committer | Florian Diebold <[email protected]> | 2018-12-25 16:43:58 +0000 |
commit | 3e4d41d1e409315ce42cb3c3479236b5e73d0643 (patch) | |
tree | 0ae517904e3324d7a9779fb83e11574e1427ee29 /crates/ra_analysis | |
parent | 3befd1a9e82809fef5bc68950d3265dbcbbd5527 (diff) |
Determine receiver for completion in a more robust way
Also rename a parameter.
Diffstat (limited to 'crates/ra_analysis')
-rw-r--r-- | crates/ra_analysis/src/completion/complete_dot.rs | 4 | ||||
-rw-r--r-- | crates/ra_analysis/src/completion/completion_context.rs | 48 |
2 files changed, 31 insertions, 21 deletions
diff --git a/crates/ra_analysis/src/completion/complete_dot.rs b/crates/ra_analysis/src/completion/complete_dot.rs index fa62da210..93d657576 100644 --- a/crates/ra_analysis/src/completion/complete_dot.rs +++ b/crates/ra_analysis/src/completion/complete_dot.rs | |||
@@ -33,9 +33,9 @@ pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) -> Ca | |||
33 | Ok(()) | 33 | Ok(()) |
34 | } | 34 | } |
35 | 35 | ||
36 | fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, ty: Ty) -> Cancelable<()> { | 36 | fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) -> Cancelable<()> { |
37 | // TODO: autoderef etc. | 37 | // TODO: autoderef etc. |
38 | match ty { | 38 | match receiver { |
39 | Ty::Adt { def_id, .. } => { | 39 | Ty::Adt { def_id, .. } => { |
40 | match def_id.resolve(ctx.db)? { | 40 | match def_id.resolve(ctx.db)? { |
41 | Def::Struct(s) => { | 41 | Def::Struct(s) => { |
diff --git a/crates/ra_analysis/src/completion/completion_context.rs b/crates/ra_analysis/src/completion/completion_context.rs index 12e98e870..978772fd4 100644 --- a/crates/ra_analysis/src/completion/completion_context.rs +++ b/crates/ra_analysis/src/completion/completion_context.rs | |||
@@ -1,12 +1,13 @@ | |||
1 | use ra_editor::find_node_at_offset; | 1 | use ra_editor::find_node_at_offset; |
2 | use ra_text_edit::AtomTextEdit; | 2 | use ra_text_edit::AtomTextEdit; |
3 | use ra_syntax::{ | 3 | use ra_syntax::{ |
4 | algo::find_leaf_at_offset, | 4 | algo::{find_leaf_at_offset, find_covering_node}, |
5 | ast, | 5 | ast, |
6 | AstNode, | 6 | AstNode, |
7 | SyntaxNodeRef, | 7 | SyntaxNodeRef, |
8 | SourceFileNode, | 8 | SourceFileNode, |
9 | TextUnit, | 9 | TextUnit, |
10 | TextRange, | ||
10 | SyntaxKind::*, | 11 | SyntaxKind::*, |
11 | }; | 12 | }; |
12 | use hir::source_binder; | 13 | use hir::source_binder; |
@@ -65,7 +66,7 @@ impl<'a> CompletionContext<'a> { | |||
65 | Ok(Some(ctx)) | 66 | Ok(Some(ctx)) |
66 | } | 67 | } |
67 | 68 | ||
68 | fn fill(&mut self, original_file: &SourceFileNode, offset: TextUnit) { | 69 | fn fill(&mut self, original_file: &'a SourceFileNode, offset: TextUnit) { |
69 | // Insert a fake ident to get a valid parse tree. We will use this file | 70 | // Insert a fake ident to get a valid parse tree. We will use this file |
70 | // to determine context, though the original_file will be used for | 71 | // to determine context, though the original_file will be used for |
71 | // actual completion. | 72 | // actual completion. |
@@ -82,7 +83,7 @@ impl<'a> CompletionContext<'a> { | |||
82 | self.is_param = true; | 83 | self.is_param = true; |
83 | return; | 84 | return; |
84 | } | 85 | } |
85 | self.classify_name_ref(&file, name_ref); | 86 | self.classify_name_ref(original_file, name_ref); |
86 | } | 87 | } |
87 | 88 | ||
88 | // Otherwise, see if this is a declaration. We can use heuristics to | 89 | // Otherwise, see if this is a declaration. We can use heuristics to |
@@ -94,7 +95,7 @@ impl<'a> CompletionContext<'a> { | |||
94 | } | 95 | } |
95 | } | 96 | } |
96 | } | 97 | } |
97 | fn classify_name_ref(&mut self, file: &SourceFileNode, name_ref: ast::NameRef) { | 98 | fn classify_name_ref(&mut self, original_file: &'a SourceFileNode, name_ref: ast::NameRef) { |
98 | let name_range = name_ref.syntax().range(); | 99 | let name_range = name_ref.syntax().range(); |
99 | let top_node = name_ref | 100 | let top_node = name_ref |
100 | .syntax() | 101 | .syntax() |
@@ -144,7 +145,9 @@ impl<'a> CompletionContext<'a> { | |||
144 | }; | 145 | }; |
145 | 146 | ||
146 | if let Some(off) = name_ref.syntax().range().start().checked_sub(2.into()) { | 147 | if let Some(off) = name_ref.syntax().range().start().checked_sub(2.into()) { |
147 | if let Some(if_expr) = find_node_at_offset::<ast::IfExpr>(file.syntax(), off) { | 148 | if let Some(if_expr) = |
149 | find_node_at_offset::<ast::IfExpr>(original_file.syntax(), off) | ||
150 | { | ||
148 | if if_expr.syntax().range().end() < name_ref.syntax().range().start() { | 151 | if if_expr.syntax().range().end() < name_ref.syntax().range().start() { |
149 | self.after_if = true; | 152 | self.after_if = true; |
150 | } | 153 | } |
@@ -152,26 +155,33 @@ impl<'a> CompletionContext<'a> { | |||
152 | } | 155 | } |
153 | } | 156 | } |
154 | } | 157 | } |
155 | if let Some(_field_expr) = ast::FieldExpr::cast(parent) { | 158 | if let Some(field_expr) = ast::FieldExpr::cast(parent) { |
156 | self.dot_receiver = self | 159 | // The receiver comes before the point of insertion of the fake |
157 | .leaf | 160 | // ident, so it should have the same range in the non-modified file |
158 | .ancestors() | 161 | self.dot_receiver = field_expr |
159 | .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) | 162 | .expr() |
160 | .find_map(ast::FieldExpr::cast) | 163 | .map(|e| e.syntax().range()) |
161 | .and_then(ast::FieldExpr::expr); | 164 | .and_then(|r| find_node_with_range(original_file.syntax(), r)); |
162 | } | 165 | } |
163 | if let Some(_method_call_expr) = ast::MethodCallExpr::cast(parent) { | 166 | if let Some(method_call_expr) = ast::MethodCallExpr::cast(parent) { |
164 | self.dot_receiver = self | 167 | // As above |
165 | .leaf | 168 | self.dot_receiver = method_call_expr |
166 | .ancestors() | 169 | .expr() |
167 | .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) | 170 | .map(|e| e.syntax().range()) |
168 | .find_map(ast::MethodCallExpr::cast) | 171 | .and_then(|r| find_node_with_range(original_file.syntax(), r)); |
169 | .and_then(ast::MethodCallExpr::expr); | ||
170 | self.is_method_call = true; | 172 | self.is_method_call = true; |
171 | } | 173 | } |
172 | } | 174 | } |
173 | } | 175 | } |
174 | 176 | ||
177 | fn find_node_with_range<'a, N: AstNode<'a>>( | ||
178 | syntax: SyntaxNodeRef<'a>, | ||
179 | range: TextRange, | ||
180 | ) -> Option<N> { | ||
181 | let node = find_covering_node(syntax, range); | ||
182 | node.ancestors().find_map(N::cast) | ||
183 | } | ||
184 | |||
175 | fn is_node<'a, N: AstNode<'a>>(node: SyntaxNodeRef<'a>) -> bool { | 185 | fn is_node<'a, N: AstNode<'a>>(node: SyntaxNodeRef<'a>) -> bool { |
176 | match node.ancestors().filter_map(N::cast).next() { | 186 | match node.ancestors().filter_map(N::cast).next() { |
177 | None => false, | 187 | None => false, |