diff options
Diffstat (limited to 'crates/ide_completion/src/context.rs')
-rw-r--r-- | crates/ide_completion/src/context.rs | 197 |
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 | ||
18 | use crate::{ | 18 | use 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)] | ||
35 | pub(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)] | ||
46 | pub(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 | |||
130 | impl<'a> CompletionContext<'a> { | 103 | impl<'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()) { |