diff options
author | Lukas Wirth <[email protected]> | 2021-05-26 22:46:00 +0100 |
---|---|---|
committer | Lukas Wirth <[email protected]> | 2021-05-26 22:46:00 +0100 |
commit | 1f897f7319da4852eac53ff765ed4e5b66a31b67 (patch) | |
tree | db7303f0a209234cdaf5c57620175a3576d72c85 /crates/ide_completion | |
parent | 666fc1cec10a41f88db56dfb339785eb1e7dd521 (diff) |
Set `record_pat_syntax` more precisely in CompletionContext
Diffstat (limited to 'crates/ide_completion')
-rw-r--r-- | crates/ide_completion/src/completions/pattern.rs | 40 | ||||
-rw-r--r-- | crates/ide_completion/src/context.rs | 54 | ||||
-rw-r--r-- | crates/ide_completion/src/render.rs | 5 |
3 files changed, 58 insertions, 41 deletions
diff --git a/crates/ide_completion/src/completions/pattern.rs b/crates/ide_completion/src/completions/pattern.rs index 3329a4844..b84e9a967 100644 --- a/crates/ide_completion/src/completions/pattern.rs +++ b/crates/ide_completion/src/completions/pattern.rs | |||
@@ -1,17 +1,14 @@ | |||
1 | //! Completes constants and paths in patterns. | 1 | //! Completes constants and paths in patterns. |
2 | 2 | ||
3 | use crate::{context::IsPatOrConst, CompletionContext, Completions}; | 3 | use crate::{context::PatternRefutability, CompletionContext, Completions}; |
4 | 4 | ||
5 | /// Completes constants and paths in patterns. | 5 | /// Completes constants and paths in patterns. |
6 | pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { | 6 | pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { |
7 | if ctx.is_pat_or_const == IsPatOrConst::No { | 7 | let refutable = match ctx.is_pat_or_const { |
8 | return; | 8 | Some(it) => it == PatternRefutability::Refutable, |
9 | } | 9 | None => return, |
10 | if ctx.record_pat_syntax.is_some() { | 10 | }; |
11 | return; | ||
12 | } | ||
13 | 11 | ||
14 | let refutable = ctx.is_pat_or_const == IsPatOrConst::Refutable; | ||
15 | if refutable { | 12 | if refutable { |
16 | if let Some(hir::Adt::Enum(e)) = | 13 | if let Some(hir::Adt::Enum(e)) = |
17 | ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) | 14 | ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) |
@@ -403,4 +400,31 @@ impl Foo { | |||
403 | "#]], | 400 | "#]], |
404 | ) | 401 | ) |
405 | } | 402 | } |
403 | |||
404 | #[test] | ||
405 | fn completes_in_record_field_pat() { | ||
406 | check_snippet( | ||
407 | r#" | ||
408 | struct Foo { bar: Bar } | ||
409 | struct Bar(u32); | ||
410 | fn outer(Foo { bar: $0 }: Foo) {} | ||
411 | "#, | ||
412 | expect![[r#" | ||
413 | bn Foo Foo { bar$1 }$0 | ||
414 | bn Bar Bar($1)$0 | ||
415 | "#]], | ||
416 | ) | ||
417 | } | ||
418 | |||
419 | #[test] | ||
420 | fn skips_in_record_field_pat_name() { | ||
421 | check_snippet( | ||
422 | r#" | ||
423 | struct Foo { bar: Bar } | ||
424 | struct Bar(u32); | ||
425 | fn outer(Foo { bar$0 }: Foo) {} | ||
426 | "#, | ||
427 | expect![[r#""#]], | ||
428 | ) | ||
429 | } | ||
406 | } | 430 | } |
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index 9d761aa43..6dc6769df 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs | |||
@@ -25,9 +25,8 @@ use crate::{ | |||
25 | CompletionConfig, | 25 | CompletionConfig, |
26 | }; | 26 | }; |
27 | 27 | ||
28 | #[derive(Debug, PartialEq, Eq)] | 28 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] |
29 | pub(crate) enum IsPatOrConst { | 29 | pub(crate) enum PatternRefutability { |
30 | No, | ||
31 | Refutable, | 30 | Refutable, |
32 | Irrefutable, | 31 | Irrefutable, |
33 | } | 32 | } |
@@ -64,7 +63,7 @@ pub(crate) struct CompletionContext<'a> { | |||
64 | pub(super) is_label_ref: bool, | 63 | pub(super) is_label_ref: bool, |
65 | 64 | ||
66 | // potentially set if we are completing a name | 65 | // potentially set if we are completing a name |
67 | pub(super) is_pat_or_const: IsPatOrConst, | 66 | pub(super) is_pat_or_const: Option<PatternRefutability>, |
68 | pub(super) is_param: bool, | 67 | pub(super) is_param: bool, |
69 | 68 | ||
70 | /// FIXME: `ActiveParameter` is string-based, which is very very wrong | 69 | /// FIXME: `ActiveParameter` is string-based, which is very very wrong |
@@ -169,7 +168,7 @@ impl<'a> CompletionContext<'a> { | |||
169 | active_parameter: ActiveParameter::at(db, position), | 168 | active_parameter: ActiveParameter::at(db, position), |
170 | is_label_ref: false, | 169 | is_label_ref: false, |
171 | is_param: false, | 170 | is_param: false, |
172 | is_pat_or_const: IsPatOrConst::No, | 171 | is_pat_or_const: None, |
173 | is_trivial_path: false, | 172 | is_trivial_path: false, |
174 | path_qual: None, | 173 | path_qual: None, |
175 | after_if: false, | 174 | after_if: false, |
@@ -473,19 +472,13 @@ impl<'a> CompletionContext<'a> { | |||
473 | 472 | ||
474 | fn classify_name(&mut self, original_file: &SyntaxNode, name: ast::Name, offset: TextSize) { | 473 | fn classify_name(&mut self, original_file: &SyntaxNode, name: ast::Name, offset: TextSize) { |
475 | if let Some(bind_pat) = name.syntax().parent().and_then(ast::IdentPat::cast) { | 474 | if let Some(bind_pat) = name.syntax().parent().and_then(ast::IdentPat::cast) { |
476 | self.is_pat_or_const = IsPatOrConst::Refutable; | 475 | self.is_pat_or_const = Some(PatternRefutability::Refutable); |
477 | // if any of these is here our bind pat can't be a const pat anymore | 476 | // if any of these is here our bind pat can't be a const pat anymore |
478 | let complex_ident_pat = bind_pat.at_token().is_some() | 477 | let complex_ident_pat = bind_pat.at_token().is_some() |
479 | || bind_pat.ref_token().is_some() | 478 | || bind_pat.ref_token().is_some() |
480 | || bind_pat.mut_token().is_some(); | 479 | || bind_pat.mut_token().is_some(); |
481 | if complex_ident_pat { | 480 | if complex_ident_pat { |
482 | self.is_pat_or_const = IsPatOrConst::No; | 481 | self.is_pat_or_const = None; |
483 | } else if let Some(pat_field) = | ||
484 | bind_pat.syntax().parent().and_then(ast::RecordPatField::cast) | ||
485 | { | ||
486 | if pat_field.name_ref().is_none() { | ||
487 | self.is_pat_or_const = IsPatOrConst::No; | ||
488 | } | ||
489 | } else { | 482 | } else { |
490 | let irrefutable_pat = bind_pat.syntax().ancestors().find_map(|node| { | 483 | let irrefutable_pat = bind_pat.syntax().ancestors().find_map(|node| { |
491 | match_ast! { | 484 | match_ast! { |
@@ -499,19 +492,24 @@ impl<'a> CompletionContext<'a> { | |||
499 | if let Some(Some(pat)) = irrefutable_pat { | 492 | if let Some(Some(pat)) = irrefutable_pat { |
500 | // This check is here since we could be inside a pattern in the initializer expression of the let statement. | 493 | // This check is here since we could be inside a pattern in the initializer expression of the let statement. |
501 | if pat.syntax().text_range().contains_range(bind_pat.syntax().text_range()) { | 494 | if pat.syntax().text_range().contains_range(bind_pat.syntax().text_range()) { |
502 | self.is_pat_or_const = IsPatOrConst::Irrefutable; | 495 | self.is_pat_or_const = Some(PatternRefutability::Irrefutable); |
503 | } | 496 | } |
504 | } | 497 | } |
498 | |||
499 | let is_name_in_field_pat = bind_pat | ||
500 | .syntax() | ||
501 | .parent() | ||
502 | .and_then(ast::RecordPatField::cast) | ||
503 | .map_or(false, |pat_field| pat_field.name_ref().is_none()); | ||
504 | if is_name_in_field_pat { | ||
505 | self.is_pat_or_const = None; | ||
506 | } | ||
505 | } | 507 | } |
506 | 508 | ||
507 | self.fill_impl_def(); | 509 | self.fill_impl_def(); |
508 | } | 510 | } |
509 | if is_node::<ast::Param>(name.syntax()) { | 511 | self.is_param |= is_node::<ast::Param>(name.syntax()); |
510 | self.is_param = true; | 512 | if ast::RecordPatField::for_field_name(&name).is_some() { |
511 | return; | ||
512 | } | ||
513 | // FIXME: remove this (V) duplication and make the check more precise | ||
514 | if name.syntax().ancestors().find_map(ast::RecordPatFieldList::cast).is_some() { | ||
515 | self.record_pat_syntax = | 513 | self.record_pat_syntax = |
516 | self.sema.find_node_at_offset_with_macros(&original_file, offset); | 514 | self.sema.find_node_at_offset_with_macros(&original_file, offset); |
517 | } | 515 | } |
@@ -523,22 +521,20 @@ impl<'a> CompletionContext<'a> { | |||
523 | name_ref: ast::NameRef, | 521 | name_ref: ast::NameRef, |
524 | offset: TextSize, | 522 | offset: TextSize, |
525 | ) { | 523 | ) { |
526 | // FIXME: remove this (^) duplication and make the check more precise | 524 | self.fill_impl_def(); |
527 | if name_ref.syntax().ancestors().find_map(ast::RecordPatFieldList::cast).is_some() { | 525 | if ast::RecordExprField::for_field_name(&name_ref).is_some() { |
526 | self.record_lit_syntax = | ||
527 | self.sema.find_node_at_offset_with_macros(original_file, offset); | ||
528 | } | ||
529 | if ast::RecordPatField::for_field_name_ref(&name_ref).is_some() { | ||
528 | self.record_pat_syntax = | 530 | self.record_pat_syntax = |
529 | self.sema.find_node_at_offset_with_macros(&original_file, offset); | 531 | self.sema.find_node_at_offset_with_macros(&original_file, offset); |
530 | } | 532 | } |
531 | 533 | ||
532 | self.name_ref_syntax = | 534 | self.name_ref_syntax = |
533 | find_node_at_offset(original_file, name_ref.syntax().text_range().start()); | 535 | find_node_at_offset(original_file, name_ref.syntax().text_range().start()); |
534 | let name_range = name_ref.syntax().text_range(); | ||
535 | if ast::RecordExprField::for_field_name(&name_ref).is_some() { | ||
536 | self.record_lit_syntax = | ||
537 | self.sema.find_node_at_offset_with_macros(original_file, offset); | ||
538 | } | ||
539 | |||
540 | self.fill_impl_def(); | ||
541 | 536 | ||
537 | let name_range = name_ref.syntax().text_range(); | ||
542 | let top_node = name_ref | 538 | let top_node = name_ref |
543 | .syntax() | 539 | .syntax() |
544 | .ancestors() | 540 | .ancestors() |
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs index 21f0da5f8..91300c56e 100644 --- a/crates/ide_completion/src/render.rs +++ b/crates/ide_completion/src/render.rs | |||
@@ -20,7 +20,6 @@ use ide_db::{ | |||
20 | use syntax::TextRange; | 20 | use syntax::TextRange; |
21 | 21 | ||
22 | use crate::{ | 22 | use crate::{ |
23 | context::IsPatOrConst, | ||
24 | item::{CompletionRelevanceTypeMatch, ImportEdit}, | 23 | item::{CompletionRelevanceTypeMatch, ImportEdit}, |
25 | CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, CompletionRelevance, | 24 | CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, CompletionRelevance, |
26 | }; | 25 | }; |
@@ -188,9 +187,7 @@ impl<'a> Render<'a> { | |||
188 | ScopeDef::ModuleDef(Function(func)) => { | 187 | ScopeDef::ModuleDef(Function(func)) => { |
189 | return render_fn(self.ctx, import_to_add, Some(local_name), *func); | 188 | return render_fn(self.ctx, import_to_add, Some(local_name), *func); |
190 | } | 189 | } |
191 | ScopeDef::ModuleDef(Variant(_)) | 190 | ScopeDef::ModuleDef(Variant(_)) if self.ctx.completion.is_pat_or_const.is_some() => { |
192 | if self.ctx.completion.is_pat_or_const != IsPatOrConst::No => | ||
193 | { | ||
194 | CompletionItemKind::SymbolKind(SymbolKind::Variant) | 191 | CompletionItemKind::SymbolKind(SymbolKind::Variant) |
195 | } | 192 | } |
196 | ScopeDef::ModuleDef(Variant(var)) => { | 193 | ScopeDef::ModuleDef(Variant(var)) => { |