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.rs197
1 files changed, 66 insertions, 131 deletions
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index 66577df94..7c46c815d 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -17,9 +17,8 @@ use text_edit::Indel;
17 17
18use crate::{ 18use crate::{
19 patterns::{ 19 patterns::{
20 for_is_prev2, has_bind_pat_parent, has_block_expr_parent, has_field_list_parent, 20 determine_location, determine_prev_sibling, for_is_prev2, inside_impl_trait_block,
21 has_impl_parent, has_item_list_or_source_file_parent, has_prev_sibling, has_ref_parent, 21 is_in_loop_body, previous_token, ImmediateLocation, ImmediatePrevSibling,
22 has_trait_parent, inside_impl_trait_block, is_in_loop_body, is_match_arm, previous_token,
23 }, 22 },
24 CompletionConfig, 23 CompletionConfig,
25}; 24};
@@ -30,24 +29,6 @@ pub(crate) enum PatternRefutability {
30 Irrefutable, 29 Irrefutable,
31} 30}
32 31
33/// Direct parent container of the cursor position
34#[derive(Copy, Clone, Debug, PartialEq, Eq)]
35pub(crate) enum ImmediateLocation {
36 Impl,
37 Trait,
38 RecordFieldList,
39 RefPatOrExpr,
40 IdentPat,
41 BlockExpr,
42 ItemList,
43}
44
45#[derive(Copy, Clone, Debug, PartialEq, Eq)]
46pub(crate) enum PrevSibling {
47 Trait,
48 Impl,
49}
50
51/// `CompletionContext` is created early during completion to figure out, where 32/// `CompletionContext` is created early during completion to figure out, where
52/// exactly is the cursor, syntax-wise. 33/// exactly is the cursor, syntax-wise.
53#[derive(Debug)] 34#[derive(Debug)]
@@ -73,11 +54,6 @@ pub(crate) struct CompletionContext<'a> {
73 /// The parent impl of the cursor position if it exists. 54 /// The parent impl of the cursor position if it exists.
74 pub(super) impl_def: Option<ast::Impl>, 55 pub(super) impl_def: Option<ast::Impl>,
75 56
76 /// RecordExpr the token is a field of
77 pub(super) record_lit_syntax: Option<ast::RecordExpr>,
78 /// RecordPat the token is a field of
79 pub(super) record_pat_syntax: Option<ast::RecordPat>,
80
81 // potentially set if we are completing a lifetime 57 // potentially set if we are completing a lifetime
82 pub(super) lifetime_syntax: Option<ast::Lifetime>, 58 pub(super) lifetime_syntax: Option<ast::Lifetime>,
83 pub(super) lifetime_param_syntax: Option<ast::LifetimeParam>, 59 pub(super) lifetime_param_syntax: Option<ast::LifetimeParam>,
@@ -89,6 +65,8 @@ pub(crate) struct CompletionContext<'a> {
89 pub(super) is_param: bool, 65 pub(super) is_param: bool,
90 66
91 pub(super) completion_location: Option<ImmediateLocation>, 67 pub(super) completion_location: Option<ImmediateLocation>,
68 pub(super) prev_sibling: Option<ImmediatePrevSibling>,
69 pub(super) attribute_under_caret: Option<ast::Attr>,
92 70
93 /// FIXME: `ActiveParameter` is string-based, which is very very wrong 71 /// FIXME: `ActiveParameter` is string-based, which is very very wrong
94 pub(super) active_parameter: Option<ActiveParameter>, 72 pub(super) active_parameter: Option<ActiveParameter>,
@@ -96,7 +74,6 @@ pub(crate) struct CompletionContext<'a> {
96 pub(super) is_trivial_path: bool, 74 pub(super) is_trivial_path: bool,
97 /// If not a trivial path, the prefix (qualifier). 75 /// If not a trivial path, the prefix (qualifier).
98 pub(super) path_qual: Option<ast::Path>, 76 pub(super) path_qual: Option<ast::Path>,
99 pub(super) after_if: bool,
100 /// `true` if we are a statement or a last expr in the block. 77 /// `true` if we are a statement or a last expr in the block.
101 pub(super) can_be_stmt: bool, 78 pub(super) can_be_stmt: bool,
102 /// `true` if we expect an expression at the cursor position. 79 /// `true` if we expect an expression at the cursor position.
@@ -114,19 +91,15 @@ pub(crate) struct CompletionContext<'a> {
114 pub(super) is_macro_call: bool, 91 pub(super) is_macro_call: bool,
115 pub(super) is_path_type: bool, 92 pub(super) is_path_type: bool,
116 pub(super) has_type_args: bool, 93 pub(super) has_type_args: bool,
117 pub(super) attribute_under_caret: Option<ast::Attr>,
118 pub(super) mod_declaration_under_caret: Option<ast::Module>,
119 pub(super) locals: Vec<(String, Local)>, 94 pub(super) locals: Vec<(String, Local)>,
120 95
121 // keyword patterns
122 pub(super) previous_token: Option<SyntaxToken>, 96 pub(super) previous_token: Option<SyntaxToken>,
123 pub(super) prev_sibling: Option<PrevSibling>,
124 pub(super) in_loop_body: bool, 97 pub(super) in_loop_body: bool,
125 pub(super) is_match_arm: bool,
126 pub(super) incomplete_let: bool, 98 pub(super) incomplete_let: bool,
127 99
128 no_completion_required: bool, 100 no_completion_required: bool,
129} 101}
102
130impl<'a> CompletionContext<'a> { 103impl<'a> CompletionContext<'a> {
131 pub(super) fn new( 104 pub(super) fn new(
132 db: &'a RootDatabase, 105 db: &'a RootDatabase,
@@ -176,8 +149,6 @@ impl<'a> CompletionContext<'a> {
176 lifetime_param_syntax: None, 149 lifetime_param_syntax: None,
177 function_def: None, 150 function_def: None,
178 use_item_syntax: None, 151 use_item_syntax: None,
179 record_lit_syntax: None,
180 record_pat_syntax: None,
181 impl_def: None, 152 impl_def: None,
182 active_parameter: ActiveParameter::at(db, position), 153 active_parameter: ActiveParameter::at(db, position),
183 is_label_ref: false, 154 is_label_ref: false,
@@ -185,7 +156,6 @@ impl<'a> CompletionContext<'a> {
185 is_pat_or_const: None, 156 is_pat_or_const: None,
186 is_trivial_path: false, 157 is_trivial_path: false,
187 path_qual: None, 158 path_qual: None,
188 after_if: false,
189 can_be_stmt: false, 159 can_be_stmt: false,
190 is_expr: false, 160 is_expr: false,
191 is_new_item: false, 161 is_new_item: false,
@@ -196,15 +166,13 @@ impl<'a> CompletionContext<'a> {
196 is_macro_call: false, 166 is_macro_call: false,
197 is_path_type: false, 167 is_path_type: false,
198 has_type_args: false, 168 has_type_args: false,
199 attribute_under_caret: None,
200 mod_declaration_under_caret: None,
201 previous_token: None, 169 previous_token: None,
202 in_loop_body: false, 170 in_loop_body: false,
203 completion_location: None, 171 completion_location: None,
204 prev_sibling: None, 172 prev_sibling: None,
205 is_match_arm: false,
206 no_completion_required: false, 173 no_completion_required: false,
207 incomplete_let: false, 174 incomplete_let: false,
175 attribute_under_caret: None,
208 locals, 176 locals,
209 }; 177 };
210 178
@@ -247,7 +215,6 @@ impl<'a> CompletionContext<'a> {
247 break; 215 break;
248 } 216 }
249 } 217 }
250 ctx.fill_keyword_patterns(&speculative_file, offset);
251 ctx.fill(&original_file, speculative_file, offset); 218 ctx.fill(&original_file, speculative_file, offset);
252 Some(ctx) 219 Some(ctx)
253 } 220 }
@@ -281,88 +248,63 @@ impl<'a> CompletionContext<'a> {
281 self.previous_token.as_ref().map_or(false, |tok| tok.kind() == kind) 248 self.previous_token.as_ref().map_or(false, |tok| tok.kind() == kind)
282 } 249 }
283 250
284 pub(crate) fn has_impl_or_trait_parent(&self) -> bool { 251 pub(crate) fn expects_assoc_item(&self) -> bool {
285 matches!( 252 matches!(
286 self.completion_location, 253 self.completion_location,
287 Some(ImmediateLocation::Trait) | Some(ImmediateLocation::Impl) 254 Some(ImmediateLocation::Trait) | Some(ImmediateLocation::Impl)
288 ) 255 )
289 } 256 }
290 257
291 pub(crate) fn has_block_expr_parent(&self) -> bool { 258 pub(crate) fn expects_use_tree(&self) -> bool {
292 matches!(self.completion_location, Some(ImmediateLocation::BlockExpr)) 259 matches!(self.completion_location, Some(ImmediateLocation::Use))
293 } 260 }
294 261
295 pub(crate) fn has_item_list_parent(&self) -> bool { 262 pub(crate) fn expects_non_trait_assoc_item(&self) -> bool {
263 matches!(self.completion_location, Some(ImmediateLocation::Impl))
264 }
265
266 pub(crate) fn expects_item(&self) -> bool {
296 matches!(self.completion_location, Some(ImmediateLocation::ItemList)) 267 matches!(self.completion_location, Some(ImmediateLocation::ItemList))
297 } 268 }
298 269
299 pub(crate) fn has_ident_or_ref_pat_parent(&self) -> bool { 270 pub(crate) fn expects_expression(&self) -> bool {
271 self.is_expr
272 }
273
274 pub(crate) fn has_block_expr_parent(&self) -> bool {
275 matches!(self.completion_location, Some(ImmediateLocation::BlockExpr))
276 }
277
278 pub(crate) fn expects_ident_pat_or_ref_expr(&self) -> bool {
300 matches!( 279 matches!(
301 self.completion_location, 280 self.completion_location,
302 Some(ImmediateLocation::IdentPat) | Some(ImmediateLocation::RefPatOrExpr) 281 Some(ImmediateLocation::IdentPat) | Some(ImmediateLocation::RefExpr)
303 ) 282 )
304 } 283 }
305 284
306 pub(crate) fn has_impl_parent(&self) -> bool { 285 pub(crate) fn expect_record_field(&self) -> bool {
307 matches!(self.completion_location, Some(ImmediateLocation::Impl)) 286 matches!(self.completion_location, Some(ImmediateLocation::RecordField))
308 }
309
310 pub(crate) fn has_field_list_parent(&self) -> bool {
311 matches!(self.completion_location, Some(ImmediateLocation::RecordFieldList))
312 } 287 }
313 288
314 pub(crate) fn has_impl_or_trait_prev_sibling(&self) -> bool { 289 pub(crate) fn has_impl_or_trait_prev_sibling(&self) -> bool {
315 self.prev_sibling.is_some() 290 matches!(
291 self.prev_sibling,
292 Some(ImmediatePrevSibling::ImplDefType) | Some(ImmediatePrevSibling::TraitDefName)
293 )
316 } 294 }
317 295
318 pub(crate) fn is_path_disallowed(&self) -> bool { 296 pub(crate) fn after_if(&self) -> bool {
319 self.record_lit_syntax.is_some() 297 matches!(self.prev_sibling, Some(ImmediatePrevSibling::IfExpr))
320 || self.record_pat_syntax.is_some()
321 || self.attribute_under_caret.is_some()
322 || self.mod_declaration_under_caret.is_some()
323 || self.has_impl_or_trait_parent()
324 } 298 }
325 299
326 fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) { 300 pub(crate) fn is_path_disallowed(&self) -> bool {
327 let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap(); 301 matches!(
328 let syntax_element = NodeOrToken::Token(fake_ident_token); 302 self.completion_location,
329 self.previous_token = previous_token(syntax_element.clone()); 303 Some(ImmediateLocation::Attribute(_))
330 self.in_loop_body = is_in_loop_body(syntax_element.clone()); 304 | Some(ImmediateLocation::ModDeclaration(_))
331 self.is_match_arm = is_match_arm(syntax_element.clone()); 305 | Some(ImmediateLocation::RecordPat(_))
332 if has_prev_sibling(syntax_element.clone(), IMPL) { 306 | Some(ImmediateLocation::RecordExpr(_))
333 self.prev_sibling = Some(PrevSibling::Impl) 307 ) || self.attribute_under_caret.is_some()
334 } else if has_prev_sibling(syntax_element.clone(), TRAIT) {
335 self.prev_sibling = Some(PrevSibling::Trait)
336 }
337
338 if has_block_expr_parent(syntax_element.clone()) {
339 self.completion_location = Some(ImmediateLocation::BlockExpr);
340 } else if has_bind_pat_parent(syntax_element.clone()) {
341 self.completion_location = Some(ImmediateLocation::IdentPat);
342 } else if has_ref_parent(syntax_element.clone()) {
343 self.completion_location = Some(ImmediateLocation::RefPatOrExpr);
344 } else if has_impl_parent(syntax_element.clone()) {
345 self.completion_location = Some(ImmediateLocation::Impl);
346 } else if has_field_list_parent(syntax_element.clone()) {
347 self.completion_location = Some(ImmediateLocation::RecordFieldList);
348 } else if has_trait_parent(syntax_element.clone()) {
349 self.completion_location = Some(ImmediateLocation::Trait);
350 } else if has_item_list_or_source_file_parent(syntax_element.clone()) {
351 self.completion_location = Some(ImmediateLocation::ItemList);
352 }
353
354 self.mod_declaration_under_caret =
355 find_node_at_offset::<ast::Module>(&file_with_fake_ident, offset)
356 .filter(|module| module.item_list().is_none());
357 self.incomplete_let =
358 syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| {
359 it.syntax().text_range().end() == syntax_element.text_range().end()
360 });
361
362 let inside_impl_trait_block = inside_impl_trait_block(syntax_element.clone());
363 let fn_is_prev = self.previous_token_is(T![fn]);
364 let for_is_prev2 = for_is_prev2(syntax_element.clone());
365 self.no_completion_required = (fn_is_prev && !inside_impl_trait_block) || for_is_prev2;
366 } 308 }
367 309
368 fn fill_impl_def(&mut self) { 310 fn fill_impl_def(&mut self) {
@@ -480,23 +422,43 @@ impl<'a> CompletionContext<'a> {
480 file_with_fake_ident: SyntaxNode, 422 file_with_fake_ident: SyntaxNode,
481 offset: TextSize, 423 offset: TextSize,
482 ) { 424 ) {
425 let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap();
426 let syntax_element = NodeOrToken::Token(fake_ident_token);
427 self.previous_token = previous_token(syntax_element.clone());
428 self.attribute_under_caret = syntax_element.ancestors().find_map(ast::Attr::cast);
429 self.no_completion_required = {
430 let inside_impl_trait_block = inside_impl_trait_block(syntax_element.clone());
431 let fn_is_prev = self.previous_token_is(T![fn]);
432 let for_is_prev2 = for_is_prev2(syntax_element.clone());
433 (fn_is_prev && !inside_impl_trait_block) || for_is_prev2
434 };
435 self.in_loop_body = is_in_loop_body(syntax_element.clone());
436
437 self.incomplete_let =
438 syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| {
439 it.syntax().text_range().end() == syntax_element.text_range().end()
440 });
441
483 let (expected_type, expected_name) = self.expected_type_and_name(); 442 let (expected_type, expected_name) = self.expected_type_and_name();
484 self.expected_type = expected_type; 443 self.expected_type = expected_type;
485 self.expected_name = expected_name; 444 self.expected_name = expected_name;
486 self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset); 445
487 let name_like = match find_node_at_offset(&&file_with_fake_ident, offset) { 446 let name_like = match find_node_at_offset(&&file_with_fake_ident, offset) {
488 Some(it) => it, 447 Some(it) => it,
489 None => return, 448 None => return,
490 }; 449 };
450 self.completion_location =
451 determine_location(&self.sema, original_file, offset, &name_like);
452 self.prev_sibling = determine_prev_sibling(&name_like);
491 match name_like { 453 match name_like {
492 ast::NameLike::Lifetime(lifetime) => { 454 ast::NameLike::Lifetime(lifetime) => {
493 self.classify_lifetime(original_file, lifetime, offset); 455 self.classify_lifetime(original_file, lifetime, offset);
494 } 456 }
495 ast::NameLike::NameRef(name_ref) => { 457 ast::NameLike::NameRef(name_ref) => {
496 self.classify_name_ref(original_file, name_ref, offset); 458 self.classify_name_ref(original_file, name_ref);
497 } 459 }
498 ast::NameLike::Name(name) => { 460 ast::NameLike::Name(name) => {
499 self.classify_name(original_file, name, offset); 461 self.classify_name(name);
500 } 462 }
501 } 463 }
502 } 464 }
@@ -530,7 +492,7 @@ impl<'a> CompletionContext<'a> {
530 } 492 }
531 } 493 }
532 494
533 fn classify_name(&mut self, original_file: &SyntaxNode, name: ast::Name, offset: TextSize) { 495 fn classify_name(&mut self, name: ast::Name) {
534 if let Some(bind_pat) = name.syntax().parent().and_then(ast::IdentPat::cast) { 496 if let Some(bind_pat) = name.syntax().parent().and_then(ast::IdentPat::cast) {
535 self.is_pat_or_const = Some(PatternRefutability::Refutable); 497 self.is_pat_or_const = Some(PatternRefutability::Refutable);
536 // if any of these is here our bind pat can't be a const pat anymore 498 // if any of these is here our bind pat can't be a const pat anymore
@@ -568,28 +530,12 @@ impl<'a> CompletionContext<'a> {
568 530
569 self.fill_impl_def(); 531 self.fill_impl_def();
570 } 532 }
533
571 self.is_param |= is_node::<ast::Param>(name.syntax()); 534 self.is_param |= is_node::<ast::Param>(name.syntax());
572 if ast::RecordPatField::for_field_name(&name).is_some() {
573 self.record_pat_syntax =
574 self.sema.find_node_at_offset_with_macros(&original_file, offset);
575 }
576 } 535 }
577 536
578 fn classify_name_ref( 537 fn classify_name_ref(&mut self, original_file: &SyntaxNode, name_ref: ast::NameRef) {
579 &mut self,
580 original_file: &SyntaxNode,
581 name_ref: ast::NameRef,
582 offset: TextSize,
583 ) {
584 self.fill_impl_def(); 538 self.fill_impl_def();
585 if ast::RecordExprField::for_field_name(&name_ref).is_some() {
586 self.record_lit_syntax =
587 self.sema.find_node_at_offset_with_macros(original_file, offset);
588 }
589 if ast::RecordPatField::for_field_name_ref(&name_ref).is_some() {
590 self.record_pat_syntax =
591 self.sema.find_node_at_offset_with_macros(&original_file, offset);
592 }
593 539
594 self.name_ref_syntax = 540 self.name_ref_syntax =
595 find_node_at_offset(original_file, name_ref.syntax().text_range().start()); 541 find_node_at_offset(original_file, name_ref.syntax().text_range().start());
@@ -676,17 +622,6 @@ impl<'a> CompletionContext<'a> {
676 }) 622 })
677 .unwrap_or(false); 623 .unwrap_or(false);
678 self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some(); 624 self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some();
679
680 if let Some(off) = name_ref.syntax().text_range().start().checked_sub(2.into()) {
681 if let Some(if_expr) =
682 self.sema.find_node_at_offset_with_macros::<ast::IfExpr>(original_file, off)
683 {
684 if if_expr.syntax().text_range().end() < name_ref.syntax().text_range().start()
685 {
686 self.after_if = true;
687 }
688 }
689 }
690 } 625 }
691 626
692 if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) { 627 if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) {