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.rs59
1 files changed, 29 insertions, 30 deletions
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index 560fb19e6..2113abbb2 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -24,6 +24,7 @@ use test_utils::mark;
24#[derive(Debug)] 24#[derive(Debug)]
25pub(crate) struct CompletionContext<'a> { 25pub(crate) struct CompletionContext<'a> {
26 pub(super) sema: Semantics<'a, RootDatabase>, 26 pub(super) sema: Semantics<'a, RootDatabase>,
27 pub(super) scope: SemanticsScope<'a>,
27 pub(super) db: &'a RootDatabase, 28 pub(super) db: &'a RootDatabase,
28 pub(super) config: &'a CompletionConfig, 29 pub(super) config: &'a CompletionConfig,
29 pub(super) offset: TextSize, 30 pub(super) offset: TextSize,
@@ -34,12 +35,12 @@ pub(crate) struct CompletionContext<'a> {
34 pub(super) krate: Option<hir::Crate>, 35 pub(super) krate: Option<hir::Crate>,
35 pub(super) expected_type: Option<Type>, 36 pub(super) expected_type: Option<Type>,
36 pub(super) name_ref_syntax: Option<ast::NameRef>, 37 pub(super) name_ref_syntax: Option<ast::NameRef>,
37 pub(super) function_syntax: Option<ast::FnDef>, 38 pub(super) function_syntax: Option<ast::Fn>,
38 pub(super) use_item_syntax: Option<ast::UseItem>, 39 pub(super) use_item_syntax: Option<ast::Use>,
39 pub(super) record_lit_syntax: Option<ast::RecordLit>, 40 pub(super) record_lit_syntax: Option<ast::RecordExpr>,
40 pub(super) record_pat_syntax: Option<ast::RecordPat>, 41 pub(super) record_pat_syntax: Option<ast::RecordPat>,
41 pub(super) record_field_syntax: Option<ast::RecordField>, 42 pub(super) record_field_syntax: Option<ast::RecordExprField>,
42 pub(super) impl_def: Option<ast::ImplDef>, 43 pub(super) impl_def: Option<ast::Impl>,
43 /// FIXME: `ActiveParameter` is string-based, which is very very wrong 44 /// FIXME: `ActiveParameter` is string-based, which is very very wrong
44 pub(super) active_parameter: Option<ActiveParameter>, 45 pub(super) active_parameter: Option<ActiveParameter>,
45 pub(super) is_param: bool, 46 pub(super) is_param: bool,
@@ -53,6 +54,8 @@ pub(crate) struct CompletionContext<'a> {
53 pub(super) after_if: bool, 54 pub(super) after_if: bool,
54 /// `true` if we are a statement or a last expr in the block. 55 /// `true` if we are a statement or a last expr in the block.
55 pub(super) can_be_stmt: bool, 56 pub(super) can_be_stmt: bool,
57 /// `true` if we expect an expression at the cursor position.
58 pub(super) is_expr: bool,
56 /// Something is typed at the "top" level, in module or impl/trait. 59 /// Something is typed at the "top" level, in module or impl/trait.
57 pub(super) is_new_item: bool, 60 pub(super) is_new_item: bool,
58 /// The receiver if this is a field or method access, i.e. writing something.<|> 61 /// The receiver if this is a field or method access, i.e. writing something.<|>
@@ -60,6 +63,8 @@ pub(crate) struct CompletionContext<'a> {
60 pub(super) dot_receiver_is_ambiguous_float_literal: bool, 63 pub(super) dot_receiver_is_ambiguous_float_literal: bool,
61 /// If this is a call (method or function) in particular, i.e. the () are already there. 64 /// If this is a call (method or function) in particular, i.e. the () are already there.
62 pub(super) is_call: bool, 65 pub(super) is_call: bool,
66 /// Like `is_call`, but for tuple patterns.
67 pub(super) is_pattern_call: bool,
63 /// If this is a macro call, i.e. the () are already there. 68 /// If this is a macro call, i.e. the () are already there.
64 pub(super) is_macro_call: bool, 69 pub(super) is_macro_call: bool,
65 pub(super) is_path_type: bool, 70 pub(super) is_path_type: bool,
@@ -104,8 +109,10 @@ impl<'a> CompletionContext<'a> {
104 let original_token = 109 let original_token =
105 original_file.syntax().token_at_offset(position.offset).left_biased()?; 110 original_file.syntax().token_at_offset(position.offset).left_biased()?;
106 let token = sema.descend_into_macros(original_token.clone()); 111 let token = sema.descend_into_macros(original_token.clone());
112 let scope = sema.scope_at_offset(&token.parent(), position.offset);
107 let mut ctx = CompletionContext { 113 let mut ctx = CompletionContext {
108 sema, 114 sema,
115 scope,
109 db, 116 db,
110 config, 117 config,
111 original_token, 118 original_token,
@@ -127,9 +134,11 @@ impl<'a> CompletionContext<'a> {
127 path_prefix: None, 134 path_prefix: None,
128 after_if: false, 135 after_if: false,
129 can_be_stmt: false, 136 can_be_stmt: false,
137 is_expr: false,
130 is_new_item: false, 138 is_new_item: false,
131 dot_receiver: None, 139 dot_receiver: None,
132 is_call: false, 140 is_call: false,
141 is_pattern_call: false,
133 is_macro_call: false, 142 is_macro_call: false,
134 is_path_type: false, 143 is_path_type: false,
135 has_type_args: false, 144 has_type_args: false,
@@ -196,30 +205,17 @@ impl<'a> CompletionContext<'a> {
196 // The range of the identifier that is being completed. 205 // The range of the identifier that is being completed.
197 pub(crate) fn source_range(&self) -> TextRange { 206 pub(crate) fn source_range(&self) -> TextRange {
198 // check kind of macro-expanded token, but use range of original token 207 // check kind of macro-expanded token, but use range of original token
199 match self.token.kind() { 208 if self.token.kind() == IDENT || self.token.kind().is_keyword() {
200 // workaroud when completion is triggered by trigger characters. 209 mark::hit!(completes_if_prefix_is_keyword);
201 IDENT => self.original_token.text_range(), 210 self.original_token.text_range()
202 _ => { 211 } else {
203 // If we haven't characters between keyword and our cursor we take the keyword start range to edit 212 TextRange::empty(self.offset)
204 if self.token.kind().is_keyword()
205 && self.offset == self.original_token.text_range().end()
206 {
207 mark::hit!(completes_bindings_from_for_with_in_prefix);
208 TextRange::empty(self.original_token.text_range().start())
209 } else {
210 TextRange::empty(self.offset)
211 }
212 }
213 } 213 }
214 } 214 }
215 215
216 pub(crate) fn scope(&self) -> SemanticsScope<'_, RootDatabase> {
217 self.sema.scope_at_offset(&self.token.parent(), self.offset)
218 }
219
220 fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) { 216 fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) {
221 let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap(); 217 let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap();
222 let syntax_element = NodeOrToken::Token(fake_ident_token.clone()); 218 let syntax_element = NodeOrToken::Token(fake_ident_token);
223 self.block_expr_parent = has_block_expr_parent(syntax_element.clone()); 219 self.block_expr_parent = has_block_expr_parent(syntax_element.clone());
224 self.unsafe_is_prev = unsafe_is_prev(syntax_element.clone()); 220 self.unsafe_is_prev = unsafe_is_prev(syntax_element.clone());
225 self.if_is_prev = if_is_prev(syntax_element.clone()); 221 self.if_is_prev = if_is_prev(syntax_element.clone());
@@ -232,7 +228,7 @@ impl<'a> CompletionContext<'a> {
232 self.trait_as_prev_sibling = has_trait_as_prev_sibling(syntax_element.clone()); 228 self.trait_as_prev_sibling = has_trait_as_prev_sibling(syntax_element.clone());
233 self.is_match_arm = is_match_arm(syntax_element.clone()); 229 self.is_match_arm = is_match_arm(syntax_element.clone());
234 self.has_item_list_or_source_file_parent = 230 self.has_item_list_or_source_file_parent =
235 has_item_list_or_source_file_parent(syntax_element.clone()); 231 has_item_list_or_source_file_parent(syntax_element);
236 } 232 }
237 233
238 fn fill( 234 fn fill(
@@ -320,7 +316,7 @@ impl<'a> CompletionContext<'a> {
320 self.name_ref_syntax = 316 self.name_ref_syntax =
321 find_node_at_offset(&original_file, name_ref.syntax().text_range().start()); 317 find_node_at_offset(&original_file, name_ref.syntax().text_range().start());
322 let name_range = name_ref.syntax().text_range(); 318 let name_range = name_ref.syntax().text_range();
323 if ast::RecordField::for_field_name(&name_ref).is_some() { 319 if ast::RecordExprField::for_field_name(&name_ref).is_some() {
324 self.record_lit_syntax = 320 self.record_lit_syntax =
325 self.sema.find_node_at_offset_with_macros(&original_file, offset); 321 self.sema.find_node_at_offset_with_macros(&original_file, offset);
326 } 322 }
@@ -329,7 +325,7 @@ impl<'a> CompletionContext<'a> {
329 .sema 325 .sema
330 .ancestors_with_macros(self.token.parent()) 326 .ancestors_with_macros(self.token.parent())
331 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) 327 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
332 .find_map(ast::ImplDef::cast); 328 .find_map(ast::Impl::cast);
333 329
334 let top_node = name_ref 330 let top_node = name_ref
335 .syntax() 331 .syntax()
@@ -347,13 +343,13 @@ impl<'a> CompletionContext<'a> {
347 } 343 }
348 344
349 self.use_item_syntax = 345 self.use_item_syntax =
350 self.sema.ancestors_with_macros(self.token.parent()).find_map(ast::UseItem::cast); 346 self.sema.ancestors_with_macros(self.token.parent()).find_map(ast::Use::cast);
351 347
352 self.function_syntax = self 348 self.function_syntax = self
353 .sema 349 .sema
354 .ancestors_with_macros(self.token.parent()) 350 .ancestors_with_macros(self.token.parent())
355 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) 351 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
356 .find_map(ast::FnDef::cast); 352 .find_map(ast::Fn::cast);
357 353
358 self.record_field_syntax = self 354 self.record_field_syntax = self
359 .sema 355 .sema
@@ -361,7 +357,7 @@ impl<'a> CompletionContext<'a> {
361 .take_while(|it| { 357 .take_while(|it| {
362 it.kind() != SOURCE_FILE && it.kind() != MODULE && it.kind() != CALL_EXPR 358 it.kind() != SOURCE_FILE && it.kind() != MODULE && it.kind() != CALL_EXPR
363 }) 359 })
364 .find_map(ast::RecordField::cast); 360 .find_map(ast::RecordExprField::cast);
365 361
366 let parent = match name_ref.syntax().parent() { 362 let parent = match name_ref.syntax().parent() {
367 Some(it) => it, 363 Some(it) => it,
@@ -377,6 +373,8 @@ impl<'a> CompletionContext<'a> {
377 .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast)) 373 .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast))
378 .is_some(); 374 .is_some();
379 self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some(); 375 self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some();
376 self.is_pattern_call =
377 path.syntax().parent().and_then(ast::TupleStructPat::cast).is_some();
380 378
381 self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); 379 self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some();
382 self.has_type_args = segment.type_arg_list().is_some(); 380 self.has_type_args = segment.type_arg_list().is_some();
@@ -412,6 +410,7 @@ impl<'a> CompletionContext<'a> {
412 None 410 None
413 }) 411 })
414 .unwrap_or(false); 412 .unwrap_or(false);
413 self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some();
415 414
416 if let Some(off) = name_ref.syntax().text_range().start().checked_sub(2.into()) { 415 if let Some(off) = name_ref.syntax().text_range().start().checked_sub(2.into()) {
417 if let Some(if_expr) = 416 if let Some(if_expr) =