From 7d865ef071a78736866a9e73d0348352f4332754 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 26 May 2021 22:39:47 +0200 Subject: Merge pattern completion related bools into an enum --- crates/ide_completion/src/completions/pattern.rs | 13 +- .../src/completions/unqualified_path.rs | 4 +- crates/ide_completion/src/context.rs | 152 +++++++++++---------- crates/ide_completion/src/render.rs | 4 +- 4 files changed, 93 insertions(+), 80 deletions(-) diff --git a/crates/ide_completion/src/completions/pattern.rs b/crates/ide_completion/src/completions/pattern.rs index 8dc9ab73c..3329a4844 100644 --- a/crates/ide_completion/src/completions/pattern.rs +++ b/crates/ide_completion/src/completions/pattern.rs @@ -1,17 +1,18 @@ //! Completes constants and paths in patterns. -use crate::{CompletionContext, Completions}; +use crate::{context::IsPatOrConst, CompletionContext, Completions}; /// Completes constants and paths in patterns. pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { - if !(ctx.is_pat_binding_or_const || ctx.is_irrefutable_pat_binding) { + if ctx.is_pat_or_const == IsPatOrConst::No { return; } if ctx.record_pat_syntax.is_some() { return; } - if !ctx.is_irrefutable_pat_binding { + let refutable = ctx.is_pat_or_const == IsPatOrConst::Refutable; + if refutable { if let Some(hir::Adt::Enum(e)) = ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) { @@ -31,14 +32,14 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { acc.add_struct_pat(ctx, *strukt, Some(name.clone())); true } - hir::ModuleDef::Variant(variant) if !ctx.is_irrefutable_pat_binding => { + hir::ModuleDef::Variant(variant) if refutable => { acc.add_variant_pat(ctx, *variant, Some(name.clone())); true } hir::ModuleDef::Adt(hir::Adt::Enum(..)) | hir::ModuleDef::Variant(..) | hir::ModuleDef::Const(..) - | hir::ModuleDef::Module(..) => !ctx.is_irrefutable_pat_binding, + | hir::ModuleDef::Module(..) => refutable, _ => false, }, hir::ScopeDef::MacroDef(_) => true, @@ -47,7 +48,7 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { acc.add_struct_pat(ctx, strukt, Some(name.clone())); true } - Some(hir::Adt::Enum(_)) => !ctx.is_irrefutable_pat_binding, + Some(hir::Adt::Enum(_)) => refutable, _ => true, }, _ => false, diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs index 6ea0efe9f..b8f8ef25f 100644 --- a/crates/ide_completion/src/completions/unqualified_path.rs +++ b/crates/ide_completion/src/completions/unqualified_path.rs @@ -88,7 +88,7 @@ fn quux(x: Option) { } } "#, - expect![[""]], + expect![[r#""#]], ); } @@ -104,7 +104,7 @@ fn quux(x: Option) { } } "#, - expect![[""]], + expect![[r#""#]], ); } diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index 85c7edabb..9d761aa43 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs @@ -25,6 +25,13 @@ use crate::{ CompletionConfig, }; +#[derive(Debug, PartialEq, Eq)] +pub(crate) enum IsPatOrConst { + No, + Refutable, + Irrefutable, +} + /// `CompletionContext` is created early during completion to figure out, where /// exactly is the cursor, syntax-wise. #[derive(Debug)] @@ -42,23 +49,26 @@ pub(crate) struct CompletionContext<'a> { pub(super) expected_name: Option, pub(super) expected_type: Option, pub(super) name_ref_syntax: Option, - pub(super) lifetime_syntax: Option, - pub(super) lifetime_param_syntax: Option, pub(super) function_syntax: Option, pub(super) use_item_syntax: Option, pub(super) record_lit_syntax: Option, pub(super) record_pat_syntax: Option, pub(super) record_field_syntax: Option, + /// The parent impl of the cursor position if it exists. pub(super) impl_def: Option, + + // potentially set if we are completing a lifetime + pub(super) lifetime_syntax: Option, + pub(super) lifetime_param_syntax: Option, pub(super) lifetime_allowed: bool, + pub(super) is_label_ref: bool, + + // potentially set if we are completing a name + pub(super) is_pat_or_const: IsPatOrConst, + pub(super) is_param: bool, + /// FIXME: `ActiveParameter` is string-based, which is very very wrong pub(super) active_parameter: Option, - pub(super) is_param: bool, - pub(super) is_label_ref: bool, - /// If a name-binding or reference to a const in a pattern. - /// Irrefutable patterns (like let) are excluded. - pub(super) is_pat_binding_or_const: bool, - pub(super) is_irrefutable_pat_binding: bool, /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path. pub(super) is_trivial_path: bool, /// If not a trivial path, the prefix (qualifier). @@ -103,7 +113,6 @@ pub(crate) struct CompletionContext<'a> { no_completion_required: bool, } - impl<'a> CompletionContext<'a> { pub(super) fn new( db: &'a RootDatabase, @@ -160,8 +169,7 @@ impl<'a> CompletionContext<'a> { active_parameter: ActiveParameter::at(db, position), is_label_ref: false, is_param: false, - is_pat_binding_or_const: false, - is_irrefutable_pat_binding: false, + is_pat_or_const: IsPatOrConst::No, is_trivial_path: false, path_qual: None, after_if: false, @@ -417,67 +425,19 @@ impl<'a> CompletionContext<'a> { self.expected_type = expected_type; self.expected_name = expected_name; self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset); - - if let Some(lifetime) = find_node_at_offset::(&file_with_fake_ident, offset) - { - self.classify_lifetime(original_file, lifetime, offset); - } - - // First, let's try to complete a reference to some declaration. - if let Some(name_ref) = find_node_at_offset::(&file_with_fake_ident, offset) { - // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`. - // See RFC#1685. - if is_node::(name_ref.syntax()) { - self.is_param = true; - return; - } - // FIXME: remove this (V) duplication and make the check more precise - if name_ref.syntax().ancestors().find_map(ast::RecordPatFieldList::cast).is_some() { - self.record_pat_syntax = - self.sema.find_node_at_offset_with_macros(&original_file, offset); - } - self.classify_name_ref(original_file, name_ref, offset); - } - - // Otherwise, see if this is a declaration. We can use heuristics to - // suggest declaration names, see `CompletionKind::Magic`. - if let Some(name) = find_node_at_offset::(&file_with_fake_ident, offset) { - if let Some(bind_pat) = name.syntax().ancestors().find_map(ast::IdentPat::cast) { - self.is_pat_binding_or_const = true; - if bind_pat.at_token().is_some() - || bind_pat.ref_token().is_some() - || bind_pat.mut_token().is_some() - { - self.is_pat_binding_or_const = false; - } - if bind_pat.syntax().parent().and_then(ast::RecordPatFieldList::cast).is_some() { - self.is_pat_binding_or_const = false; - } - if let Some(Some(pat)) = bind_pat.syntax().ancestors().find_map(|node| { - match_ast! { - match node { - ast::LetStmt(it) => Some(it.pat()), - ast::Param(it) => Some(it.pat()), - _ => None, - } - } - }) { - if pat.syntax().text_range().contains_range(bind_pat.syntax().text_range()) { - self.is_pat_binding_or_const = false; - self.is_irrefutable_pat_binding = true; - } - } - - self.fill_impl_def(); + let name_like = match find_node_at_offset(&&file_with_fake_ident, offset) { + Some(it) => it, + None => return, + }; + match name_like { + ast::NameLike::Lifetime(lifetime) => { + self.classify_lifetime(original_file, lifetime, offset); } - if is_node::(name.syntax()) { - self.is_param = true; - return; + ast::NameLike::NameRef(name_ref) => { + self.classify_name_ref(original_file, name_ref, offset); } - // FIXME: remove this (^) duplication and make the check more precise - if name.syntax().ancestors().find_map(ast::RecordPatFieldList::cast).is_some() { - self.record_pat_syntax = - self.sema.find_node_at_offset_with_macros(&original_file, offset); + ast::NameLike::Name(name) => { + self.classify_name(original_file, name, offset); } } } @@ -511,12 +471,64 @@ impl<'a> CompletionContext<'a> { } } + fn classify_name(&mut self, original_file: &SyntaxNode, name: ast::Name, offset: TextSize) { + if let Some(bind_pat) = name.syntax().parent().and_then(ast::IdentPat::cast) { + self.is_pat_or_const = IsPatOrConst::Refutable; + // if any of these is here our bind pat can't be a const pat anymore + let complex_ident_pat = bind_pat.at_token().is_some() + || bind_pat.ref_token().is_some() + || bind_pat.mut_token().is_some(); + if complex_ident_pat { + self.is_pat_or_const = IsPatOrConst::No; + } else if let Some(pat_field) = + bind_pat.syntax().parent().and_then(ast::RecordPatField::cast) + { + if pat_field.name_ref().is_none() { + self.is_pat_or_const = IsPatOrConst::No; + } + } else { + let irrefutable_pat = bind_pat.syntax().ancestors().find_map(|node| { + match_ast! { + match node { + ast::LetStmt(it) => Some(it.pat()), + ast::Param(it) => Some(it.pat()), + _ => None, + } + } + }); + if let Some(Some(pat)) = irrefutable_pat { + // This check is here since we could be inside a pattern in the initializer expression of the let statement. + if pat.syntax().text_range().contains_range(bind_pat.syntax().text_range()) { + self.is_pat_or_const = IsPatOrConst::Irrefutable; + } + } + } + + self.fill_impl_def(); + } + if is_node::(name.syntax()) { + self.is_param = true; + return; + } + // FIXME: remove this (V) duplication and make the check more precise + if name.syntax().ancestors().find_map(ast::RecordPatFieldList::cast).is_some() { + self.record_pat_syntax = + self.sema.find_node_at_offset_with_macros(&original_file, offset); + } + } + fn classify_name_ref( &mut self, original_file: &SyntaxNode, name_ref: ast::NameRef, offset: TextSize, ) { + // FIXME: remove this (^) duplication and make the check more precise + if name_ref.syntax().ancestors().find_map(ast::RecordPatFieldList::cast).is_some() { + self.record_pat_syntax = + self.sema.find_node_at_offset_with_macros(&original_file, offset); + } + self.name_ref_syntax = find_node_at_offset(original_file, name_ref.syntax().text_range().start()); let name_range = name_ref.syntax().text_range(); diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs index d7f96b864..21f0da5f8 100644 --- a/crates/ide_completion/src/render.rs +++ b/crates/ide_completion/src/render.rs @@ -20,6 +20,7 @@ use ide_db::{ use syntax::TextRange; use crate::{ + context::IsPatOrConst, item::{CompletionRelevanceTypeMatch, ImportEdit}, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, CompletionRelevance, }; @@ -188,8 +189,7 @@ impl<'a> Render<'a> { return render_fn(self.ctx, import_to_add, Some(local_name), *func); } ScopeDef::ModuleDef(Variant(_)) - if self.ctx.completion.is_pat_binding_or_const - | self.ctx.completion.is_irrefutable_pat_binding => + if self.ctx.completion.is_pat_or_const != IsPatOrConst::No => { CompletionItemKind::SymbolKind(SymbolKind::Variant) } -- cgit v1.2.3