aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukas Wirth <[email protected]>2021-05-26 22:46:00 +0100
committerLukas Wirth <[email protected]>2021-05-26 22:46:00 +0100
commit1f897f7319da4852eac53ff765ed4e5b66a31b67 (patch)
treedb7303f0a209234cdaf5c57620175a3576d72c85
parent666fc1cec10a41f88db56dfb339785eb1e7dd521 (diff)
Set `record_pat_syntax` more precisely in CompletionContext
-rw-r--r--crates/ide_completion/src/completions/pattern.rs40
-rw-r--r--crates/ide_completion/src/context.rs54
-rw-r--r--crates/ide_completion/src/render.rs5
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
3use crate::{context::IsPatOrConst, CompletionContext, Completions}; 3use crate::{context::PatternRefutability, CompletionContext, Completions};
4 4
5/// Completes constants and paths in patterns. 5/// Completes constants and paths in patterns.
6pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { 6pub(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#"
408struct Foo { bar: Bar }
409struct Bar(u32);
410fn 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#"
423struct Foo { bar: Bar }
424struct Bar(u32);
425fn 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)]
29pub(crate) enum IsPatOrConst { 29pub(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::{
20use syntax::TextRange; 20use syntax::TextRange;
21 21
22use crate::{ 22use 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)) => {