diff options
Diffstat (limited to 'crates/ide_completion/src/context.rs')
-rw-r--r-- | crates/ide_completion/src/context.rs | 152 |
1 files changed, 82 insertions, 70 deletions
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index cb4f08e53..6177caa12 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs | |||
@@ -29,6 +29,29 @@ pub(crate) enum PatternRefutability { | |||
29 | Irrefutable, | 29 | Irrefutable, |
30 | } | 30 | } |
31 | 31 | ||
32 | #[derive(Debug)] | ||
33 | pub(crate) struct PathCompletionContext { | ||
34 | /// If this is a call with () already there | ||
35 | call_kind: Option<CallKind>, | ||
36 | /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path. | ||
37 | pub(super) is_trivial_path: bool, | ||
38 | /// If not a trivial path, the prefix (qualifier). | ||
39 | pub(super) path_qual: Option<ast::Path>, | ||
40 | pub(super) is_path_type: bool, | ||
41 | pub(super) has_type_args: bool, | ||
42 | /// `true` if we are a statement or a last expr in the block. | ||
43 | pub(super) can_be_stmt: bool, | ||
44 | /// `true` if we expect an expression at the cursor position. | ||
45 | pub(super) is_expr: bool, | ||
46 | pub(super) in_loop_body: bool, | ||
47 | } | ||
48 | |||
49 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
50 | pub(crate) enum CallKind { | ||
51 | Pat, | ||
52 | Mac, | ||
53 | Expr, | ||
54 | } | ||
32 | /// `CompletionContext` is created early during completion to figure out, where | 55 | /// `CompletionContext` is created early during completion to figure out, where |
33 | /// exactly is the cursor, syntax-wise. | 56 | /// exactly is the cursor, syntax-wise. |
34 | #[derive(Debug)] | 57 | #[derive(Debug)] |
@@ -45,14 +68,13 @@ pub(crate) struct CompletionContext<'a> { | |||
45 | pub(super) krate: Option<hir::Crate>, | 68 | pub(super) krate: Option<hir::Crate>, |
46 | pub(super) expected_name: Option<NameOrNameRef>, | 69 | pub(super) expected_name: Option<NameOrNameRef>, |
47 | pub(super) expected_type: Option<Type>, | 70 | pub(super) expected_type: Option<Type>, |
48 | pub(super) name_ref_syntax: Option<ast::NameRef>, | ||
49 | |||
50 | pub(super) use_item_syntax: Option<ast::Use>, | ||
51 | 71 | ||
52 | /// The parent function of the cursor position if it exists. | 72 | /// The parent function of the cursor position if it exists. |
53 | pub(super) function_def: Option<ast::Fn>, | 73 | pub(super) function_def: Option<ast::Fn>, |
54 | /// The parent impl of the cursor position if it exists. | 74 | /// The parent impl of the cursor position if it exists. |
55 | pub(super) impl_def: Option<ast::Impl>, | 75 | pub(super) impl_def: Option<ast::Impl>, |
76 | pub(super) name_ref_syntax: Option<ast::NameRef>, | ||
77 | pub(super) use_item_syntax: Option<ast::Use>, | ||
56 | 78 | ||
57 | // potentially set if we are completing a lifetime | 79 | // potentially set if we are completing a lifetime |
58 | pub(super) lifetime_syntax: Option<ast::Lifetime>, | 80 | pub(super) lifetime_syntax: Option<ast::Lifetime>, |
@@ -67,29 +89,12 @@ pub(crate) struct CompletionContext<'a> { | |||
67 | pub(super) completion_location: Option<ImmediateLocation>, | 89 | pub(super) completion_location: Option<ImmediateLocation>, |
68 | pub(super) prev_sibling: Option<ImmediatePrevSibling>, | 90 | pub(super) prev_sibling: Option<ImmediatePrevSibling>, |
69 | pub(super) attribute_under_caret: Option<ast::Attr>, | 91 | pub(super) attribute_under_caret: Option<ast::Attr>, |
92 | pub(super) previous_token: Option<SyntaxToken>, | ||
70 | 93 | ||
71 | /// FIXME: `ActiveParameter` is string-based, which is very very wrong | 94 | pub(super) path_context: Option<PathCompletionContext>, |
72 | pub(super) active_parameter: Option<ActiveParameter>, | 95 | pub(super) active_parameter: Option<ActiveParameter>, |
73 | /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path. | ||
74 | pub(super) is_trivial_path: bool, | ||
75 | /// If not a trivial path, the prefix (qualifier). | ||
76 | pub(super) path_qual: Option<ast::Path>, | ||
77 | /// `true` if we are a statement or a last expr in the block. | ||
78 | pub(super) can_be_stmt: bool, | ||
79 | /// `true` if we expect an expression at the cursor position. | ||
80 | pub(super) is_expr: bool, | ||
81 | /// If this is a call (method or function) in particular, i.e. the () are already there. | ||
82 | pub(super) is_call: bool, | ||
83 | /// Like `is_call`, but for tuple patterns. | ||
84 | pub(super) is_pattern_call: bool, | ||
85 | /// If this is a macro call, i.e. the () are already there. | ||
86 | pub(super) is_macro_call: bool, | ||
87 | pub(super) is_path_type: bool, | ||
88 | pub(super) has_type_args: bool, | ||
89 | pub(super) locals: Vec<(String, Local)>, | 96 | pub(super) locals: Vec<(String, Local)>, |
90 | 97 | ||
91 | pub(super) previous_token: Option<SyntaxToken>, | ||
92 | pub(super) in_loop_body: bool, | ||
93 | pub(super) incomplete_let: bool, | 98 | pub(super) incomplete_let: bool, |
94 | 99 | ||
95 | no_completion_required: bool, | 100 | no_completion_required: bool, |
@@ -136,36 +141,27 @@ impl<'a> CompletionContext<'a> { | |||
136 | original_token, | 141 | original_token, |
137 | token, | 142 | token, |
138 | krate, | 143 | krate, |
139 | lifetime_allowed: false, | ||
140 | expected_name: None, | 144 | expected_name: None, |
141 | expected_type: None, | 145 | expected_type: None, |
146 | function_def: None, | ||
147 | impl_def: None, | ||
142 | name_ref_syntax: None, | 148 | name_ref_syntax: None, |
149 | use_item_syntax: None, | ||
143 | lifetime_syntax: None, | 150 | lifetime_syntax: None, |
144 | lifetime_param_syntax: None, | 151 | lifetime_param_syntax: None, |
145 | function_def: None, | 152 | lifetime_allowed: false, |
146 | use_item_syntax: None, | ||
147 | impl_def: None, | ||
148 | active_parameter: ActiveParameter::at(db, position), | ||
149 | is_label_ref: false, | 153 | is_label_ref: false, |
150 | is_param: false, | ||
151 | is_pat_or_const: None, | 154 | is_pat_or_const: None, |
152 | is_trivial_path: false, | 155 | is_param: false, |
153 | path_qual: None, | ||
154 | can_be_stmt: false, | ||
155 | is_expr: false, | ||
156 | is_call: false, | ||
157 | is_pattern_call: false, | ||
158 | is_macro_call: false, | ||
159 | is_path_type: false, | ||
160 | has_type_args: false, | ||
161 | previous_token: None, | ||
162 | in_loop_body: false, | ||
163 | completion_location: None, | 156 | completion_location: None, |
164 | prev_sibling: None, | 157 | prev_sibling: None, |
165 | no_completion_required: false, | ||
166 | incomplete_let: false, | ||
167 | attribute_under_caret: None, | 158 | attribute_under_caret: None, |
159 | previous_token: None, | ||
160 | path_context: None, | ||
161 | active_parameter: ActiveParameter::at(db, position), | ||
168 | locals, | 162 | locals, |
163 | incomplete_let: false, | ||
164 | no_completion_required: false, | ||
169 | }; | 165 | }; |
170 | 166 | ||
171 | let mut original_file = original_file.syntax().clone(); | 167 | let mut original_file = original_file.syntax().clone(); |
@@ -250,14 +246,14 @@ impl<'a> CompletionContext<'a> { | |||
250 | pub(crate) fn has_dot_receiver(&self) -> bool { | 246 | pub(crate) fn has_dot_receiver(&self) -> bool { |
251 | matches!( | 247 | matches!( |
252 | &self.completion_location, | 248 | &self.completion_location, |
253 | Some(ImmediateLocation::FieldAccess { receiver, .. }) | Some(ImmediateLocation::MethodCall { receiver }) | 249 | Some(ImmediateLocation::FieldAccess { receiver, .. }) | Some(ImmediateLocation::MethodCall { receiver,.. }) |
254 | if receiver.is_some() | 250 | if receiver.is_some() |
255 | ) | 251 | ) |
256 | } | 252 | } |
257 | 253 | ||
258 | pub(crate) fn dot_receiver(&self) -> Option<&ast::Expr> { | 254 | pub(crate) fn dot_receiver(&self) -> Option<&ast::Expr> { |
259 | match &self.completion_location { | 255 | match &self.completion_location { |
260 | Some(ImmediateLocation::MethodCall { receiver }) | 256 | Some(ImmediateLocation::MethodCall { receiver, .. }) |
261 | | Some(ImmediateLocation::FieldAccess { receiver, .. }) => receiver.as_ref(), | 257 | | Some(ImmediateLocation::FieldAccess { receiver, .. }) => receiver.as_ref(), |
262 | _ => None, | 258 | _ => None, |
263 | } | 259 | } |
@@ -275,11 +271,6 @@ impl<'a> CompletionContext<'a> { | |||
275 | matches!(self.completion_location, Some(ImmediateLocation::ItemList)) | 271 | matches!(self.completion_location, Some(ImmediateLocation::ItemList)) |
276 | } | 272 | } |
277 | 273 | ||
278 | // fn expects_value(&self) -> bool { | ||
279 | pub(crate) fn expects_expression(&self) -> bool { | ||
280 | self.is_expr | ||
281 | } | ||
282 | |||
283 | pub(crate) fn has_block_expr_parent(&self) -> bool { | 274 | pub(crate) fn has_block_expr_parent(&self) -> bool { |
284 | matches!(self.completion_location, Some(ImmediateLocation::BlockExpr)) | 275 | matches!(self.completion_location, Some(ImmediateLocation::BlockExpr)) |
285 | } | 276 | } |
@@ -316,6 +307,22 @@ impl<'a> CompletionContext<'a> { | |||
316 | ) || self.attribute_under_caret.is_some() | 307 | ) || self.attribute_under_caret.is_some() |
317 | } | 308 | } |
318 | 309 | ||
310 | pub(crate) fn expects_expression(&self) -> bool { | ||
311 | self.path_context.as_ref().map_or(false, |it| it.is_expr) | ||
312 | } | ||
313 | |||
314 | pub(crate) fn path_call_kind(&self) -> Option<CallKind> { | ||
315 | self.path_context.as_ref().and_then(|it| it.call_kind) | ||
316 | } | ||
317 | |||
318 | pub(crate) fn is_trivial_path(&self) -> bool { | ||
319 | self.path_context.as_ref().map_or(false, |it| it.is_trivial_path) | ||
320 | } | ||
321 | |||
322 | pub(crate) fn path_qual(&self) -> Option<&ast::Path> { | ||
323 | self.path_context.as_ref().and_then(|it| it.path_qual.as_ref()) | ||
324 | } | ||
325 | |||
319 | fn fill_impl_def(&mut self) { | 326 | fn fill_impl_def(&mut self) { |
320 | self.impl_def = self | 327 | self.impl_def = self |
321 | .sema | 328 | .sema |
@@ -441,7 +448,6 @@ impl<'a> CompletionContext<'a> { | |||
441 | let for_is_prev2 = for_is_prev2(syntax_element.clone()); | 448 | let for_is_prev2 = for_is_prev2(syntax_element.clone()); |
442 | (fn_is_prev && !inside_impl_trait_block) || for_is_prev2 | 449 | (fn_is_prev && !inside_impl_trait_block) || for_is_prev2 |
443 | }; | 450 | }; |
444 | self.in_loop_body = is_in_loop_body(syntax_element.clone()); | ||
445 | 451 | ||
446 | self.incomplete_let = | 452 | self.incomplete_let = |
447 | syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| { | 453 | syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| { |
@@ -549,10 +555,6 @@ impl<'a> CompletionContext<'a> { | |||
549 | self.name_ref_syntax = | 555 | self.name_ref_syntax = |
550 | find_node_at_offset(original_file, name_ref.syntax().text_range().start()); | 556 | find_node_at_offset(original_file, name_ref.syntax().text_range().start()); |
551 | 557 | ||
552 | if matches!(self.completion_location, Some(ImmediateLocation::ItemList)) { | ||
553 | return; | ||
554 | } | ||
555 | |||
556 | self.use_item_syntax = | 558 | self.use_item_syntax = |
557 | self.sema.token_ancestors_with_macros(self.token.clone()).find_map(ast::Use::cast); | 559 | self.sema.token_ancestors_with_macros(self.token.clone()).find_map(ast::Use::cast); |
558 | 560 | ||
@@ -568,22 +570,34 @@ impl<'a> CompletionContext<'a> { | |||
568 | }; | 570 | }; |
569 | 571 | ||
570 | if let Some(segment) = ast::PathSegment::cast(parent) { | 572 | if let Some(segment) = ast::PathSegment::cast(parent) { |
573 | let path_ctx = self.path_context.get_or_insert(PathCompletionContext { | ||
574 | call_kind: None, | ||
575 | is_trivial_path: false, | ||
576 | path_qual: None, | ||
577 | has_type_args: false, | ||
578 | is_path_type: false, | ||
579 | can_be_stmt: false, | ||
580 | is_expr: false, | ||
581 | in_loop_body: false, | ||
582 | }); | ||
583 | path_ctx.in_loop_body = is_in_loop_body(name_ref.syntax()); | ||
571 | let path = segment.parent_path(); | 584 | let path = segment.parent_path(); |
572 | self.is_call = path | ||
573 | .syntax() | ||
574 | .parent() | ||
575 | .and_then(ast::PathExpr::cast) | ||
576 | .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast)) | ||
577 | .is_some(); | ||
578 | self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some(); | ||
579 | self.is_pattern_call = | ||
580 | path.syntax().parent().and_then(ast::TupleStructPat::cast).is_some(); | ||
581 | 585 | ||
582 | self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); | 586 | if let Some(p) = path.syntax().parent() { |
583 | self.has_type_args = segment.generic_arg_list().is_some(); | 587 | path_ctx.call_kind = match_ast! { |
588 | match p { | ||
589 | ast::PathExpr(it) => it.syntax().parent().and_then(ast::CallExpr::cast).map(|_| CallKind::Expr), | ||
590 | ast::MacroCall(it) => it.excl_token().and(Some(CallKind::Mac)), | ||
591 | ast::TupleStructPat(_it) => Some(CallKind::Pat), | ||
592 | _ => None | ||
593 | } | ||
594 | }; | ||
595 | } | ||
596 | path_ctx.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); | ||
597 | path_ctx.has_type_args = segment.generic_arg_list().is_some(); | ||
584 | 598 | ||
585 | if let Some(path) = path_or_use_tree_qualifier(&path) { | 599 | if let Some(path) = path_or_use_tree_qualifier(&path) { |
586 | self.path_qual = path | 600 | path_ctx.path_qual = path |
587 | .segment() | 601 | .segment() |
588 | .and_then(|it| { | 602 | .and_then(|it| { |
589 | find_node_with_range::<ast::PathSegment>( | 603 | find_node_with_range::<ast::PathSegment>( |
@@ -601,11 +615,11 @@ impl<'a> CompletionContext<'a> { | |||
601 | } | 615 | } |
602 | } | 616 | } |
603 | 617 | ||
604 | self.is_trivial_path = true; | 618 | path_ctx.is_trivial_path = true; |
605 | 619 | ||
606 | // Find either enclosing expr statement (thing with `;`) or a | 620 | // Find either enclosing expr statement (thing with `;`) or a |
607 | // block. If block, check that we are the last expr. | 621 | // block. If block, check that we are the last expr. |
608 | self.can_be_stmt = name_ref | 622 | path_ctx.can_be_stmt = name_ref |
609 | .syntax() | 623 | .syntax() |
610 | .ancestors() | 624 | .ancestors() |
611 | .find_map(|node| { | 625 | .find_map(|node| { |
@@ -621,10 +635,8 @@ impl<'a> CompletionContext<'a> { | |||
621 | None | 635 | None |
622 | }) | 636 | }) |
623 | .unwrap_or(false); | 637 | .unwrap_or(false); |
624 | self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some(); | 638 | path_ctx.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some(); |
625 | } | 639 | } |
626 | self.is_call |= | ||
627 | matches!(self.completion_location, Some(ImmediateLocation::MethodCall { .. })); | ||
628 | } | 640 | } |
629 | } | 641 | } |
630 | 642 | ||