aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/completion/completion_context.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src/completion/completion_context.rs')
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs27
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};
12use ra_text_edit::AtomTextEdit; 12use ra_text_edit::Indel;
13 13
14use crate::{call_info::ActiveParameter, completion::CompletionConfig, FilePosition}; 14use crate::{call_info::ActiveParameter, completion::CompletionConfig, FilePosition};
15use 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
64impl<'a> CompletionContext<'a> { 65impl<'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()),