diff options
Diffstat (limited to 'crates/ra_ide/src/completion/completion_context.rs')
-rw-r--r-- | crates/ra_ide/src/completion/completion_context.rs | 59 |
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)] |
25 | pub(crate) struct CompletionContext<'a> { | 25 | pub(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) = |