diff options
Diffstat (limited to 'crates/ra_ide/src/completion/completion_context.rs')
-rw-r--r-- | crates/ra_ide/src/completion/completion_context.rs | 27 |
1 files changed, 19 insertions, 8 deletions
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index 118fceb2e..c4646b727 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs | |||
@@ -9,9 +9,10 @@ use ra_syntax::{ | |||
9 | SyntaxKind::*, | 9 | SyntaxKind::*, |
10 | SyntaxNode, SyntaxToken, TextRange, TextSize, | 10 | SyntaxNode, SyntaxToken, TextRange, TextSize, |
11 | }; | 11 | }; |
12 | use ra_text_edit::AtomTextEdit; | 12 | use ra_text_edit::Indel; |
13 | 13 | ||
14 | use crate::{call_info::ActiveParameter, completion::CompletionConfig, FilePosition}; | 14 | use crate::{call_info::ActiveParameter, completion::CompletionConfig, FilePosition}; |
15 | use test_utils::mark; | ||
15 | 16 | ||
16 | /// `CompletionContext` is created early during completion to figure out, where | 17 | /// `CompletionContext` is created early during completion to figure out, where |
17 | /// exactly is the cursor, syntax-wise. | 18 | /// exactly is the cursor, syntax-wise. |
@@ -34,7 +35,7 @@ pub(crate) struct CompletionContext<'a> { | |||
34 | pub(super) record_pat_syntax: Option<ast::RecordPat>, | 35 | pub(super) record_pat_syntax: Option<ast::RecordPat>, |
35 | pub(super) record_field_syntax: Option<ast::RecordField>, | 36 | pub(super) record_field_syntax: Option<ast::RecordField>, |
36 | pub(super) impl_def: Option<ast::ImplDef>, | 37 | pub(super) impl_def: Option<ast::ImplDef>, |
37 | /// FIXME: `ActiveParameter` is string-based, which is very wrong | 38 | /// FIXME: `ActiveParameter` is string-based, which is very very wrong |
38 | pub(super) active_parameter: Option<ActiveParameter>, | 39 | pub(super) active_parameter: Option<ActiveParameter>, |
39 | pub(super) is_param: bool, | 40 | pub(super) is_param: bool, |
40 | /// If a name-binding or reference to a const in a pattern. | 41 | /// If a name-binding or reference to a const in a pattern. |
@@ -58,7 +59,7 @@ pub(crate) struct CompletionContext<'a> { | |||
58 | pub(super) is_macro_call: bool, | 59 | pub(super) is_macro_call: bool, |
59 | pub(super) is_path_type: bool, | 60 | pub(super) is_path_type: bool, |
60 | pub(super) has_type_args: bool, | 61 | pub(super) has_type_args: bool, |
61 | pub(super) is_attribute: bool, | 62 | pub(super) attribute_under_caret: Option<ast::Attr>, |
62 | } | 63 | } |
63 | 64 | ||
64 | impl<'a> CompletionContext<'a> { | 65 | impl<'a> CompletionContext<'a> { |
@@ -76,7 +77,7 @@ impl<'a> CompletionContext<'a> { | |||
76 | // actual completion. | 77 | // actual completion. |
77 | let file_with_fake_ident = { | 78 | let file_with_fake_ident = { |
78 | let parse = db.parse(position.file_id); | 79 | let parse = db.parse(position.file_id); |
79 | let edit = AtomTextEdit::insert(position.offset, "intellijRulezz".to_string()); | 80 | let edit = Indel::insert(position.offset, "intellijRulezz".to_string()); |
80 | parse.reparse(&edit).tree() | 81 | parse.reparse(&edit).tree() |
81 | }; | 82 | }; |
82 | let fake_ident_token = | 83 | let fake_ident_token = |
@@ -116,7 +117,7 @@ impl<'a> CompletionContext<'a> { | |||
116 | is_path_type: false, | 117 | is_path_type: false, |
117 | has_type_args: false, | 118 | has_type_args: false, |
118 | dot_receiver_is_ambiguous_float_literal: false, | 119 | dot_receiver_is_ambiguous_float_literal: false, |
119 | is_attribute: false, | 120 | attribute_under_caret: None, |
120 | }; | 121 | }; |
121 | 122 | ||
122 | let mut original_file = original_file.syntax().clone(); | 123 | let mut original_file = original_file.syntax().clone(); |
@@ -169,7 +170,17 @@ impl<'a> CompletionContext<'a> { | |||
169 | match self.token.kind() { | 170 | match self.token.kind() { |
170 | // workaroud when completion is triggered by trigger characters. | 171 | // workaroud when completion is triggered by trigger characters. |
171 | IDENT => self.original_token.text_range(), | 172 | IDENT => self.original_token.text_range(), |
172 | _ => TextRange::empty(self.offset), | 173 | _ => { |
174 | // If we haven't characters between keyword and our cursor we take the keyword start range to edit | ||
175 | if self.token.kind().is_keyword() | ||
176 | && self.offset == self.original_token.text_range().end() | ||
177 | { | ||
178 | mark::hit!(completes_bindings_from_for_with_in_prefix); | ||
179 | TextRange::empty(self.original_token.text_range().start()) | ||
180 | } else { | ||
181 | TextRange::empty(self.offset) | ||
182 | } | ||
183 | } | ||
173 | } | 184 | } |
174 | } | 185 | } |
175 | 186 | ||
@@ -200,6 +211,7 @@ impl<'a> CompletionContext<'a> { | |||
200 | Some(ty) | 211 | Some(ty) |
201 | }) | 212 | }) |
202 | .flatten(); | 213 | .flatten(); |
214 | self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset); | ||
203 | 215 | ||
204 | // First, let's try to complete a reference to some declaration. | 216 | // First, let's try to complete a reference to some declaration. |
205 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) { | 217 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) { |
@@ -318,7 +330,6 @@ impl<'a> CompletionContext<'a> { | |||
318 | .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast)) | 330 | .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast)) |
319 | .is_some(); | 331 | .is_some(); |
320 | self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some(); | 332 | self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some(); |
321 | self.is_attribute = path.syntax().parent().and_then(ast::Attr::cast).is_some(); | ||
322 | 333 | ||
323 | self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); | 334 | self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); |
324 | self.has_type_args = segment.type_arg_list().is_some(); | 335 | self.has_type_args = segment.type_arg_list().is_some(); |
@@ -344,7 +355,7 @@ impl<'a> CompletionContext<'a> { | |||
344 | stmt.syntax().text_range() == name_ref.syntax().text_range(), | 355 | stmt.syntax().text_range() == name_ref.syntax().text_range(), |
345 | ); | 356 | ); |
346 | } | 357 | } |
347 | if let Some(block) = ast::Block::cast(node) { | 358 | if let Some(block) = ast::BlockExpr::cast(node) { |
348 | return Some( | 359 | return Some( |
349 | block.expr().map(|e| e.syntax().text_range()) | 360 | block.expr().map(|e| e.syntax().text_range()) |
350 | == Some(name_ref.syntax().text_range()), | 361 | == Some(name_ref.syntax().text_range()), |