diff --git a/crates/ide_completion/src/completions/fn_param.rs b/crates/ide_completion/src/completions/fn_param.rs index 1488436df3..d8d3b8e85b 100644 --- a/crates/ide_completion/src/completions/fn_param.rs +++ b/crates/ide_completion/src/completions/fn_param.rs @@ -7,8 +7,8 @@ use syntax::{ }; use crate::{ - context::ParamKind, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, - Completions, + context::{ParamKind, PatternContext}, + CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions, }; /// Complete repeated parameters, both name and type. For example, if all @@ -16,7 +16,8 @@ use crate::{ /// `spam: &mut Spam` insert text/label and `spam` lookup string will be /// suggested. pub(crate) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { - if ctx.is_param != Some(ParamKind::Function) { + if !matches!(ctx.pattern_ctx, Some(PatternContext { is_param: Some(ParamKind::Function), .. })) + { return None; } diff --git a/crates/ide_completion/src/completions/pattern.rs b/crates/ide_completion/src/completions/pattern.rs index f892a31505..c63c274151 100644 --- a/crates/ide_completion/src/completions/pattern.rs +++ b/crates/ide_completion/src/completions/pattern.rs @@ -1,11 +1,14 @@ //! Completes constants and paths in patterns. -use crate::{context::PatternRefutability, CompletionContext, Completions}; +use crate::{ + context::{PatternContext, PatternRefutability}, + CompletionContext, Completions, +}; /// Completes constants and paths in patterns. pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { - let refutable = match ctx.is_pat_or_const { - Some(it) => it == PatternRefutability::Refutable, + let refutable = match ctx.pattern_ctx { + Some(PatternContext { refutability, .. }) => refutability == PatternRefutability::Refutable, None => return, }; diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index f4637c401b..6c6f8f8512 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs @@ -54,6 +54,12 @@ pub(crate) struct PathCompletionContext { pub(super) in_loop_body: bool, } +#[derive(Debug)] +pub(super) struct PatternContext { + pub(super) refutability: PatternRefutability, + pub(super) is_param: Option, +} + #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub(crate) enum CallKind { Pat, @@ -95,15 +101,12 @@ pub(crate) struct CompletionContext<'a> { 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: Option, - pub(super) is_param: Option, - pub(super) completion_location: Option, pub(super) prev_sibling: Option, pub(super) attribute_under_caret: Option, pub(super) previous_token: Option, + pub(super) pattern_ctx: Option, pub(super) path_context: Option, pub(super) active_parameter: Option, pub(super) locals: Vec<(String, Local)>, @@ -163,8 +166,7 @@ impl<'a> CompletionContext<'a> { lifetime_param_syntax: None, lifetime_allowed: false, is_label_ref: false, - is_pat_or_const: None, - is_param: None, + pattern_ctx: None, completion_location: None, prev_sibling: None, attribute_under_caret: None, @@ -642,50 +644,51 @@ impl<'a> CompletionContext<'a> { } fn classify_name(&mut self, name: ast::Name) { + self.fill_impl_def(); + if let Some(bind_pat) = name.syntax().parent().and_then(ast::IdentPat::cast) { - self.is_pat_or_const = Some(PatternRefutability::Refutable); - if !bind_pat.is_simple_ident() { - self.is_pat_or_const = None; - } 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 = Some(PatternRefutability::Irrefutable); - } - } - - let is_name_in_field_pat = bind_pat - .syntax() - .parent() - .and_then(ast::RecordPatField::cast) - .map_or(false, |pat_field| pat_field.name_ref().is_none()); - if is_name_in_field_pat { - self.is_pat_or_const = None; - } + let is_name_in_field_pat = bind_pat + .syntax() + .parent() + .and_then(ast::RecordPatField::cast) + .map_or(false, |pat_field| pat_field.name_ref().is_none()); + if is_name_in_field_pat { + return; + } + if bind_pat.is_simple_ident() { + let mut is_param = None; + let refutability = bind_pat + .syntax() + .ancestors() + .skip_while(|it| ast::Pat::can_cast(it.kind())) + .next() + .map_or(PatternRefutability::Irrefutable, |node| { + match_ast! { + match node { + ast::LetStmt(__) => PatternRefutability::Irrefutable, + ast::Param(param) => { + let is_closure_param = param + .syntax() + .ancestors() + .nth(2) + .and_then(ast::ClosureExpr::cast) + .is_some(); + is_param = Some(if is_closure_param { + ParamKind::Closure + } else { + ParamKind::Function + }); + PatternRefutability::Irrefutable + }, + ast::MatchArm(__) => PatternRefutability::Refutable, + ast::Condition(__) => PatternRefutability::Refutable, + ast::ForExpr(__) => PatternRefutability::Irrefutable, + _ => PatternRefutability::Irrefutable, + } + } + }); + self.pattern_ctx = Some(PatternContext { refutability, is_param }); } - - self.fill_impl_def(); - } - - if let Some(param) = name - .syntax() - .ancestors() - .find_map(ast::Param::cast) - .filter(|it| it.syntax().text_range() == name.syntax().text_range()) - { - let is_closure_param = - param.syntax().ancestors().nth(2).and_then(ast::ClosureExpr::cast).is_some(); - self.is_param = - Some(if is_closure_param { ParamKind::Closure } else { ParamKind::Function }); } } diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs index 527838e8bf..5ff5d10466 100644 --- a/crates/ide_completion/src/render.rs +++ b/crates/ide_completion/src/render.rs @@ -167,7 +167,7 @@ fn render_resolution_( hir::ScopeDef::ModuleDef(Function(func)) => { return render_fn(ctx, import_to_add, Some(local_name), *func); } - hir::ScopeDef::ModuleDef(Variant(_)) if ctx.completion.is_pat_or_const.is_some() => { + hir::ScopeDef::ModuleDef(Variant(_)) if ctx.completion.pattern_ctx.is_some() => { CompletionItemKind::SymbolKind(SymbolKind::Variant) } hir::ScopeDef::ModuleDef(Variant(var)) => { diff --git a/crates/ide_completion/src/render/pattern.rs b/crates/ide_completion/src/render/pattern.rs index 67961a602f..306e418e91 100644 --- a/crates/ide_completion/src/render/pattern.rs +++ b/crates/ide_completion/src/render/pattern.rs @@ -5,8 +5,10 @@ use ide_db::helpers::SnippetCap; use itertools::Itertools; use crate::{ - context::ParamKind, item::CompletionKind, render::RenderContext, CompletionItem, - CompletionItemKind, + context::{ParamKind, PatternContext}, + item::CompletionKind, + render::RenderContext, + CompletionItem, CompletionItemKind, }; pub(crate) fn render_struct_pat( @@ -86,7 +88,10 @@ fn render_pat( _ => return None, }; - if ctx.completion.is_param == Some(ParamKind::Function) { + if matches!( + ctx.completion.pattern_ctx, + Some(PatternContext { is_param: Some(ParamKind::Function), .. }) + ) { pat.push(':'); pat.push(' '); pat.push_str(name);