diff options
Diffstat (limited to 'crates/ide_completion/src/context.rs')
-rw-r--r-- | crates/ide_completion/src/context.rs | 143 |
1 files changed, 94 insertions, 49 deletions
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index 6dc6769df..dfac8f29f 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs | |||
@@ -31,6 +31,24 @@ pub(crate) enum PatternRefutability { | |||
31 | Irrefutable, | 31 | Irrefutable, |
32 | } | 32 | } |
33 | 33 | ||
34 | /// Direct parent container of the cursor position | ||
35 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
36 | pub(crate) enum ImmediateLocation { | ||
37 | Impl, | ||
38 | Trait, | ||
39 | RecordFieldList, | ||
40 | RefPatOrExpr, | ||
41 | IdentPat, | ||
42 | BlockExpr, | ||
43 | ItemList, | ||
44 | } | ||
45 | |||
46 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
47 | pub enum PrevSibling { | ||
48 | Trait, | ||
49 | Impl, | ||
50 | } | ||
51 | |||
34 | /// `CompletionContext` is created early during completion to figure out, where | 52 | /// `CompletionContext` is created early during completion to figure out, where |
35 | /// exactly is the cursor, syntax-wise. | 53 | /// exactly is the cursor, syntax-wise. |
36 | #[derive(Debug)] | 54 | #[derive(Debug)] |
@@ -48,14 +66,19 @@ pub(crate) struct CompletionContext<'a> { | |||
48 | pub(super) expected_name: Option<NameOrNameRef>, | 66 | pub(super) expected_name: Option<NameOrNameRef>, |
49 | pub(super) expected_type: Option<Type>, | 67 | pub(super) expected_type: Option<Type>, |
50 | pub(super) name_ref_syntax: Option<ast::NameRef>, | 68 | pub(super) name_ref_syntax: Option<ast::NameRef>, |
51 | pub(super) function_syntax: Option<ast::Fn>, | 69 | |
52 | pub(super) use_item_syntax: Option<ast::Use>, | 70 | pub(super) use_item_syntax: Option<ast::Use>, |
53 | pub(super) record_lit_syntax: Option<ast::RecordExpr>, | 71 | |
54 | pub(super) record_pat_syntax: Option<ast::RecordPat>, | 72 | /// The parent function of the cursor position if it exists. |
55 | pub(super) record_field_syntax: Option<ast::RecordExprField>, | 73 | pub(super) function_def: Option<ast::Fn>, |
56 | /// The parent impl of the cursor position if it exists. | 74 | /// The parent impl of the cursor position if it exists. |
57 | pub(super) impl_def: Option<ast::Impl>, | 75 | pub(super) impl_def: Option<ast::Impl>, |
58 | 76 | ||
77 | /// RecordExpr the token is a field of | ||
78 | pub(super) record_lit_syntax: Option<ast::RecordExpr>, | ||
79 | /// RecordPat the token is a field of | ||
80 | pub(super) record_pat_syntax: Option<ast::RecordPat>, | ||
81 | |||
59 | // potentially set if we are completing a lifetime | 82 | // potentially set if we are completing a lifetime |
60 | pub(super) lifetime_syntax: Option<ast::Lifetime>, | 83 | pub(super) lifetime_syntax: Option<ast::Lifetime>, |
61 | pub(super) lifetime_param_syntax: Option<ast::LifetimeParam>, | 84 | pub(super) lifetime_param_syntax: Option<ast::LifetimeParam>, |
@@ -66,6 +89,8 @@ pub(crate) struct CompletionContext<'a> { | |||
66 | pub(super) is_pat_or_const: Option<PatternRefutability>, | 89 | pub(super) is_pat_or_const: Option<PatternRefutability>, |
67 | pub(super) is_param: bool, | 90 | pub(super) is_param: bool, |
68 | 91 | ||
92 | pub(super) completion_location: Option<ImmediateLocation>, | ||
93 | |||
69 | /// FIXME: `ActiveParameter` is string-based, which is very very wrong | 94 | /// FIXME: `ActiveParameter` is string-based, which is very very wrong |
70 | pub(super) active_parameter: Option<ActiveParameter>, | 95 | pub(super) active_parameter: Option<ActiveParameter>, |
71 | /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path. | 96 | /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path. |
@@ -94,20 +119,12 @@ pub(crate) struct CompletionContext<'a> { | |||
94 | pub(super) locals: Vec<(String, Local)>, | 119 | pub(super) locals: Vec<(String, Local)>, |
95 | 120 | ||
96 | pub(super) mod_declaration_under_caret: Option<ast::Module>, | 121 | pub(super) mod_declaration_under_caret: Option<ast::Module>, |
97 | pub(super) has_trait_parent: bool, | ||
98 | pub(super) has_impl_parent: bool, | ||
99 | 122 | ||
100 | // keyword patterns | 123 | // keyword patterns |
101 | pub(super) previous_token: Option<SyntaxToken>, | 124 | pub(super) previous_token: Option<SyntaxToken>, |
102 | pub(super) block_expr_parent: bool, | ||
103 | pub(super) bind_pat_parent: bool, | ||
104 | pub(super) ref_pat_parent: bool, | ||
105 | pub(super) in_loop_body: bool, | 125 | pub(super) in_loop_body: bool, |
106 | pub(super) has_field_list_parent: bool, | 126 | pub(super) prev_sibling: Option<PrevSibling>, |
107 | pub(super) trait_as_prev_sibling: bool, | ||
108 | pub(super) impl_as_prev_sibling: bool, | ||
109 | pub(super) is_match_arm: bool, | 127 | pub(super) is_match_arm: bool, |
110 | pub(super) has_item_list_or_source_file_parent: bool, | ||
111 | pub(super) incomplete_let: bool, | 128 | pub(super) incomplete_let: bool, |
112 | 129 | ||
113 | no_completion_required: bool, | 130 | no_completion_required: bool, |
@@ -159,11 +176,10 @@ impl<'a> CompletionContext<'a> { | |||
159 | name_ref_syntax: None, | 176 | name_ref_syntax: None, |
160 | lifetime_syntax: None, | 177 | lifetime_syntax: None, |
161 | lifetime_param_syntax: None, | 178 | lifetime_param_syntax: None, |
162 | function_syntax: None, | 179 | function_def: None, |
163 | use_item_syntax: None, | 180 | use_item_syntax: None, |
164 | record_lit_syntax: None, | 181 | record_lit_syntax: None, |
165 | record_pat_syntax: None, | 182 | record_pat_syntax: None, |
166 | record_field_syntax: None, | ||
167 | impl_def: None, | 183 | impl_def: None, |
168 | active_parameter: ActiveParameter::at(db, position), | 184 | active_parameter: ActiveParameter::at(db, position), |
169 | is_label_ref: false, | 185 | is_label_ref: false, |
@@ -185,17 +201,10 @@ impl<'a> CompletionContext<'a> { | |||
185 | attribute_under_caret: None, | 201 | attribute_under_caret: None, |
186 | mod_declaration_under_caret: None, | 202 | mod_declaration_under_caret: None, |
187 | previous_token: None, | 203 | previous_token: None, |
188 | block_expr_parent: false, | ||
189 | bind_pat_parent: false, | ||
190 | ref_pat_parent: false, | ||
191 | in_loop_body: false, | 204 | in_loop_body: false, |
192 | has_trait_parent: false, | 205 | completion_location: None, |
193 | has_impl_parent: false, | 206 | prev_sibling: None, |
194 | has_field_list_parent: false, | ||
195 | trait_as_prev_sibling: false, | ||
196 | impl_as_prev_sibling: false, | ||
197 | is_match_arm: false, | 207 | is_match_arm: false, |
198 | has_item_list_or_source_file_parent: false, | ||
199 | no_completion_required: false, | 208 | no_completion_required: false, |
200 | incomplete_let: false, | 209 | incomplete_let: false, |
201 | locals, | 210 | locals, |
@@ -274,23 +283,68 @@ impl<'a> CompletionContext<'a> { | |||
274 | self.previous_token.as_ref().map_or(false, |tok| tok.kind() == kind) | 283 | self.previous_token.as_ref().map_or(false, |tok| tok.kind() == kind) |
275 | } | 284 | } |
276 | 285 | ||
286 | pub(crate) fn has_impl_or_trait_parent(&self) -> bool { | ||
287 | matches!( | ||
288 | self.completion_location, | ||
289 | Some(ImmediateLocation::Trait) | Some(ImmediateLocation::Impl) | ||
290 | ) | ||
291 | } | ||
292 | |||
293 | pub(crate) fn has_block_expr_parent(&self) -> bool { | ||
294 | matches!(self.completion_location, Some(ImmediateLocation::BlockExpr)) | ||
295 | } | ||
296 | |||
297 | pub(crate) fn has_item_list_parent(&self) -> bool { | ||
298 | matches!(self.completion_location, Some(ImmediateLocation::ItemList)) | ||
299 | } | ||
300 | |||
301 | pub(crate) fn has_ident_or_ref_pat_parent(&self) -> bool { | ||
302 | matches!( | ||
303 | self.completion_location, | ||
304 | Some(ImmediateLocation::IdentPat) | Some(ImmediateLocation::RefPatOrExpr) | ||
305 | ) | ||
306 | } | ||
307 | |||
308 | pub(crate) fn has_impl_parent(&self) -> bool { | ||
309 | matches!(self.completion_location, Some(ImmediateLocation::Impl)) | ||
310 | } | ||
311 | |||
312 | pub(crate) fn has_field_list_parent(&self) -> bool { | ||
313 | matches!(self.completion_location, Some(ImmediateLocation::RecordFieldList)) | ||
314 | } | ||
315 | |||
316 | pub(crate) fn has_impl_or_trait_prev_sibling(&self) -> bool { | ||
317 | self.prev_sibling.is_some() | ||
318 | } | ||
319 | |||
277 | fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) { | 320 | fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) { |
278 | let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap(); | 321 | let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap(); |
279 | let syntax_element = NodeOrToken::Token(fake_ident_token); | 322 | let syntax_element = NodeOrToken::Token(fake_ident_token); |
280 | self.previous_token = previous_token(syntax_element.clone()); | 323 | self.previous_token = previous_token(syntax_element.clone()); |
281 | self.block_expr_parent = has_block_expr_parent(syntax_element.clone()); | ||
282 | self.bind_pat_parent = has_bind_pat_parent(syntax_element.clone()); | ||
283 | self.ref_pat_parent = has_ref_parent(syntax_element.clone()); | ||
284 | self.in_loop_body = is_in_loop_body(syntax_element.clone()); | 324 | self.in_loop_body = is_in_loop_body(syntax_element.clone()); |
285 | self.has_trait_parent = has_trait_parent(syntax_element.clone()); | ||
286 | self.has_impl_parent = has_impl_parent(syntax_element.clone()); | ||
287 | self.has_field_list_parent = has_field_list_parent(syntax_element.clone()); | ||
288 | self.impl_as_prev_sibling = has_impl_as_prev_sibling(syntax_element.clone()); | ||
289 | self.trait_as_prev_sibling = has_trait_as_prev_sibling(syntax_element.clone()); | ||
290 | self.is_match_arm = is_match_arm(syntax_element.clone()); | 325 | self.is_match_arm = is_match_arm(syntax_element.clone()); |
326 | if has_impl_as_prev_sibling(syntax_element.clone()) { | ||
327 | self.prev_sibling = Some(PrevSibling::Impl) | ||
328 | } else if has_trait_as_prev_sibling(syntax_element.clone()) { | ||
329 | self.prev_sibling = Some(PrevSibling::Trait) | ||
330 | } | ||
331 | |||
332 | if has_block_expr_parent(syntax_element.clone()) { | ||
333 | self.completion_location = Some(ImmediateLocation::BlockExpr); | ||
334 | } else if has_bind_pat_parent(syntax_element.clone()) { | ||
335 | self.completion_location = Some(ImmediateLocation::IdentPat); | ||
336 | } else if has_ref_parent(syntax_element.clone()) { | ||
337 | self.completion_location = Some(ImmediateLocation::RefPatOrExpr); | ||
338 | } else if has_impl_parent(syntax_element.clone()) { | ||
339 | self.completion_location = Some(ImmediateLocation::Impl); | ||
340 | } else if has_field_list_parent(syntax_element.clone()) { | ||
341 | self.completion_location = Some(ImmediateLocation::RecordFieldList); | ||
342 | } else if has_trait_parent(syntax_element.clone()) { | ||
343 | self.completion_location = Some(ImmediateLocation::Trait); | ||
344 | } else if has_item_list_or_source_file_parent(syntax_element.clone()) { | ||
345 | self.completion_location = Some(ImmediateLocation::ItemList); | ||
346 | } | ||
291 | 347 | ||
292 | self.has_item_list_or_source_file_parent = | ||
293 | has_item_list_or_source_file_parent(syntax_element.clone()); | ||
294 | self.mod_declaration_under_caret = | 348 | self.mod_declaration_under_caret = |
295 | find_node_at_offset::<ast::Module>(&file_with_fake_ident, offset) | 349 | find_node_at_offset::<ast::Module>(&file_with_fake_ident, offset) |
296 | .filter(|module| module.item_list().is_none()); | 350 | .filter(|module| module.item_list().is_none()); |
@@ -542,31 +596,20 @@ impl<'a> CompletionContext<'a> { | |||
542 | .last() | 596 | .last() |
543 | .unwrap(); | 597 | .unwrap(); |
544 | 598 | ||
545 | match top_node.parent().map(|it| it.kind()) { | 599 | if matches!(top_node.parent().map(|it| it.kind()), Some(SOURCE_FILE) | Some(ITEM_LIST)) { |
546 | Some(SOURCE_FILE) | Some(ITEM_LIST) => { | 600 | self.is_new_item = true; |
547 | self.is_new_item = true; | 601 | return; |
548 | return; | ||
549 | } | ||
550 | _ => (), | ||
551 | } | 602 | } |
552 | 603 | ||
553 | self.use_item_syntax = | 604 | self.use_item_syntax = |
554 | self.sema.token_ancestors_with_macros(self.token.clone()).find_map(ast::Use::cast); | 605 | self.sema.token_ancestors_with_macros(self.token.clone()).find_map(ast::Use::cast); |
555 | 606 | ||
556 | self.function_syntax = self | 607 | self.function_def = self |
557 | .sema | 608 | .sema |
558 | .token_ancestors_with_macros(self.token.clone()) | 609 | .token_ancestors_with_macros(self.token.clone()) |
559 | .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) | 610 | .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) |
560 | .find_map(ast::Fn::cast); | 611 | .find_map(ast::Fn::cast); |
561 | 612 | ||
562 | self.record_field_syntax = self | ||
563 | .sema | ||
564 | .token_ancestors_with_macros(self.token.clone()) | ||
565 | .take_while(|it| { | ||
566 | it.kind() != SOURCE_FILE && it.kind() != MODULE && it.kind() != CALL_EXPR | ||
567 | }) | ||
568 | .find_map(ast::RecordExprField::cast); | ||
569 | |||
570 | let parent = match name_ref.syntax().parent() { | 613 | let parent = match name_ref.syntax().parent() { |
571 | Some(it) => it, | 614 | Some(it) => it, |
572 | None => return, | 615 | None => return, |
@@ -639,6 +682,7 @@ impl<'a> CompletionContext<'a> { | |||
639 | } | 682 | } |
640 | } | 683 | } |
641 | } | 684 | } |
685 | |||
642 | if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) { | 686 | if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) { |
643 | // The receiver comes before the point of insertion of the fake | 687 | // The receiver comes before the point of insertion of the fake |
644 | // ident, so it should have the same range in the non-modified file | 688 | // ident, so it should have the same range in the non-modified file |
@@ -656,6 +700,7 @@ impl<'a> CompletionContext<'a> { | |||
656 | false | 700 | false |
657 | }; | 701 | }; |
658 | } | 702 | } |
703 | |||
659 | if let Some(method_call_expr) = ast::MethodCallExpr::cast(parent) { | 704 | if let Some(method_call_expr) = ast::MethodCallExpr::cast(parent) { |
660 | // As above | 705 | // As above |
661 | self.dot_receiver = method_call_expr | 706 | self.dot_receiver = method_call_expr |