aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_completion/src/context.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_completion/src/context.rs')
-rw-r--r--crates/ide_completion/src/context.rs152
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)]
33pub(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)]
50pub(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