diff --git a/crates/ide_completion/src/completions.rs b/crates/ide_completion/src/completions.rs index b8a904a3a0..36cee48aed 100644 --- a/crates/ide_completion/src/completions.rs +++ b/crates/ide_completion/src/completions.rs @@ -103,10 +103,14 @@ impl Completions { item.add_to(self); } - pub(crate) fn add_nameref_keywords(&mut self, ctx: &CompletionContext) { + pub(crate) fn add_nameref_keywords_with_colon(&mut self, ctx: &CompletionContext) { ["self::", "super::", "crate::"].into_iter().for_each(|kw| self.add_keyword(ctx, kw)); } + pub(crate) fn add_nameref_keywords(&mut self, ctx: &CompletionContext) { + ["self", "super", "crate"].into_iter().for_each(|kw| self.add_keyword(ctx, kw)); + } + pub(crate) fn add_crate_roots(&mut self, ctx: &CompletionContext) { ctx.process_all_names(&mut |name, res| match res { ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) if m.is_crate_root(ctx.db) => { diff --git a/crates/ide_completion/src/completions/attribute.rs b/crates/ide_completion/src/completions/attribute.rs index c79a9816f4..eb887c3f73 100644 --- a/crates/ide_completion/src/completions/attribute.rs +++ b/crates/ide_completion/src/completions/attribute.rs @@ -107,7 +107,7 @@ pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) acc.add_resolution(ctx, name, def); } }); - acc.add_nameref_keywords(ctx); + acc.add_nameref_keywords_with_colon(ctx); } } diff --git a/crates/ide_completion/src/completions/attribute/derive.rs b/crates/ide_completion/src/completions/attribute/derive.rs index 4611811539..4f524dd73b 100644 --- a/crates/ide_completion/src/completions/attribute/derive.rs +++ b/crates/ide_completion/src/completions/attribute/derive.rs @@ -102,7 +102,7 @@ pub(crate) fn complete_derive(acc: &mut Completions, ctx: &CompletionContext) { None => acc.add_resolution(ctx, name, def), } }); - acc.add_nameref_keywords(ctx); + acc.add_nameref_keywords_with_colon(ctx); } } } diff --git a/crates/ide_completion/src/completions/mod_.rs b/crates/ide_completion/src/completions/mod_.rs index fb42e4e72f..7641086ff8 100644 --- a/crates/ide_completion/src/completions/mod_.rs +++ b/crates/ide_completion/src/completions/mod_.rs @@ -9,14 +9,16 @@ use ide_db::{ }; use rustc_hash::FxHashSet; -use crate::{patterns::ImmediateLocation, CompletionItem}; +use crate::{context::NameContext, CompletionItem}; use crate::{context::CompletionContext, Completions}; /// Complete mod declaration, i.e. `mod $0;` pub(crate) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { - let mod_under_caret = match &ctx.completion_location { - Some(ImmediateLocation::ModDeclaration(mod_under_caret)) => mod_under_caret, + let mod_under_caret = match &ctx.name_ctx { + Some(NameContext::Module(mod_under_caret)) if mod_under_caret.item_list().is_none() => { + mod_under_caret + } _ => return None, }; diff --git a/crates/ide_completion/src/completions/pattern.rs b/crates/ide_completion/src/completions/pattern.rs index 00f9bdfb51..a11bcc96fa 100644 --- a/crates/ide_completion/src/completions/pattern.rs +++ b/crates/ide_completion/src/completions/pattern.rs @@ -202,7 +202,7 @@ fn pattern_path_completion( acc.add_resolution(ctx, name, res); } }); - acc.add_nameref_keywords(ctx); + acc.add_nameref_keywords_with_colon(ctx); } } } diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs index 235d7870c7..94142e274a 100644 --- a/crates/ide_completion/src/completions/unqualified_path.rs +++ b/crates/ide_completion/src/completions/unqualified_path.rs @@ -17,21 +17,15 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC } match ctx.path_context { Some(PathCompletionCtx { - kind: - Some( - PathKind::Attr { .. } - | PathKind::Derive - | PathKind::Pat - | PathKind::Use { .. } - | PathKind::Vis { .. }, - ), + is_absolute_path: false, + qualifier: None, + kind: None | Some(PathKind::Expr | PathKind::Type | PathKind::Mac), .. - }) => return, - Some(PathCompletionCtx { is_absolute_path: false, qualifier: None, .. }) => (), + }) => (), _ => return, } - ["self", "super", "crate"].into_iter().for_each(|kw| acc.add_keyword(ctx, kw)); + acc.add_nameref_keywords(ctx); match &ctx.completion_location { Some(ImmediateLocation::ItemList | ImmediateLocation::Trait | ImmediateLocation::Impl) => { diff --git a/crates/ide_completion/src/completions/use_.rs b/crates/ide_completion/src/completions/use_.rs index 3f757f9816..94df46efb0 100644 --- a/crates/ide_completion/src/completions/use_.rs +++ b/crates/ide_completion/src/completions/use_.rs @@ -113,7 +113,7 @@ pub(crate) fn complete_use_tree(acc: &mut Completions, ctx: &CompletionContext) acc.add_resolution(ctx, name, res); } }); - acc.add_nameref_keywords(ctx); + acc.add_nameref_keywords_with_colon(ctx); } } } diff --git a/crates/ide_completion/src/completions/vis.rs b/crates/ide_completion/src/completions/vis.rs index ca6f6dff1e..338e003437 100644 --- a/crates/ide_completion/src/completions/vis.rs +++ b/crates/ide_completion/src/completions/vis.rs @@ -45,7 +45,7 @@ pub(crate) fn complete_vis(acc: &mut Completions, ctx: &CompletionContext) { cov_mark::hit!(kw_completion_in); acc.add_keyword(ctx, "in"); } - ["self", "super", "crate"].into_iter().for_each(|kw| acc.add_keyword(ctx, kw)); + acc.add_nameref_keywords(ctx); } _ => {} } diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index 5f6b8f7db5..9c2e2e75de 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs @@ -100,6 +100,30 @@ pub(super) enum LifetimeContext { LabelDef, } +#[derive(Debug)] +#[allow(dead_code)] +pub(super) enum NameContext { + Const, + ConstParam, + Enum, + Function, + IdentPat, + MacroDef, + MacroRules, + /// Fake node + Module(ast::Module), + RecordField, + Rename, + SelfParam, + Static, + Struct, + Trait, + TypeAlias, + TypeParam, + Union, + Variant, +} + #[derive(Clone, Debug, PartialEq, Eq)] pub(crate) enum ParamKind { Function(ast::Fn), @@ -140,6 +164,7 @@ pub(crate) struct CompletionContext<'a> { pub(super) fake_attribute_under_caret: Option, pub(super) previous_token: Option, + pub(super) name_ctx: Option, pub(super) lifetime_ctx: Option, pub(super) pattern_ctx: Option, pub(super) path_context: Option, @@ -197,7 +222,7 @@ impl<'a> CompletionContext<'a> { } pub(crate) fn expects_variant(&self) -> bool { - matches!(self.completion_location, Some(ImmediateLocation::Variant)) + matches!(self.name_ctx, Some(NameContext::Variant)) } pub(crate) fn expects_non_trait_assoc_item(&self) -> bool { @@ -221,10 +246,8 @@ impl<'a> CompletionContext<'a> { } pub(crate) fn expect_field(&self) -> bool { - matches!( - self.completion_location, - Some(ImmediateLocation::RecordField | ImmediateLocation::TupleField) - ) + matches!(self.completion_location, Some(ImmediateLocation::TupleField)) + || matches!(self.name_ctx, Some(NameContext::RecordField)) } pub(crate) fn has_impl_or_trait_prev_sibling(&self) -> bool { @@ -254,13 +277,9 @@ impl<'a> CompletionContext<'a> { ) || matches!( self.completion_location, - Some( - ImmediateLocation::ModDeclaration(_) - | ImmediateLocation::RecordPat(_) - | ImmediateLocation::RecordExpr(_) - | ImmediateLocation::Rename - ) + Some(ImmediateLocation::RecordPat(_) | ImmediateLocation::RecordExpr(_)) ) + || matches!(self.name_ctx, Some(NameContext::Module(_) | NameContext::Rename)) } pub(crate) fn expects_expression(&self) -> bool { @@ -429,6 +448,7 @@ impl<'a> CompletionContext<'a> { name_syntax: None, lifetime_ctx: None, pattern_ctx: None, + name_ctx: None, completion_location: None, prev_sibling: None, fake_attribute_under_caret: None, @@ -800,7 +820,12 @@ impl<'a> CompletionContext<'a> { } } ast::NameLike::Name(name) => { - self.pattern_ctx = Self::classify_name(&self.sema, original_file, name); + if let Some((name_ctx, pat_ctx)) = + Self::classify_name(&self.sema, original_file, name) + { + self.pattern_ctx = pat_ctx; + self.name_ctx = Some(name_ctx); + } } } } @@ -833,17 +858,44 @@ impl<'a> CompletionContext<'a> { _sema: &Semantics, original_file: &SyntaxNode, name: ast::Name, - ) -> Option { - let bind_pat = name.syntax().parent().and_then(ast::IdentPat::cast)?; - 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 None; - } - Some(pattern_context_for(original_file, bind_pat.into())) + ) -> Option<(NameContext, Option)> { + let parent = name.syntax().parent()?; + let mut pat_ctx = None; + let name_ctx = match_ast! { + match parent { + ast::Const(_) => NameContext::Const, + ast::ConstParam(_) => NameContext::ConstParam, + ast::Enum(_) => NameContext::Enum, + ast::Fn(_) => NameContext::Function, + ast::IdentPat(bind_pat) => { + 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 { + pat_ctx = Some(pattern_context_for(original_file, bind_pat.into())); + } + + NameContext::IdentPat + }, + ast::MacroDef(_) => NameContext::MacroDef, + ast::MacroRules(_) => NameContext::MacroRules, + ast::Module(module) => NameContext::Module(module), + ast::RecordField(_) => NameContext::RecordField, + ast::Rename(_) => NameContext::Rename, + ast::SelfParam(_) => NameContext::SelfParam, + ast::Static(_) => NameContext::Static, + ast::Struct(_) => NameContext::Struct, + ast::Trait(_) => NameContext::Trait, + ast::TypeAlias(_) => NameContext::TypeAlias, + ast::TypeParam(_) => NameContext::TypeParam, + ast::Union(_) => NameContext::Union, + ast::Variant(_) => NameContext::Variant, + _ => return None, + } + }; + Some((name_ctx, pat_ctx)) } fn classify_name_ref( diff --git a/crates/ide_completion/src/patterns.rs b/crates/ide_completion/src/patterns.rs index 5fd9602b46..6fdec78385 100644 --- a/crates/ide_completion/src/patterns.rs +++ b/crates/ide_completion/src/patterns.rs @@ -41,21 +41,16 @@ pub(crate) enum TypeAnnotation { /// from which file the nodes are. #[derive(Clone, Debug, PartialEq, Eq)] pub(crate) enum ImmediateLocation { - Rename, Impl, Trait, - RecordField, TupleField, RefExpr, IdentPat, StmtList, ItemList, TypeBound, - Variant, /// Original file ast node TypeAnnotation(TypeAnnotation), - /// Fake file ast node - ModDeclaration(ast::Module), /// Original file ast node MethodCall { receiver: Option, @@ -80,6 +75,7 @@ pub(crate) enum ImmediateLocation { /// The record pat of the field name we are completing /// /// Original file ast node + // FIXME: This should be moved to pattern_ctx RecordPat(ast::RecordPat), } @@ -211,17 +207,10 @@ pub(crate) fn determine_location( let res = match_ast! { match parent { ast::IdentPat(_) => ImmediateLocation::IdentPat, - ast::Rename(_) => ImmediateLocation::Rename, ast::StmtList(_) => ImmediateLocation::StmtList, ast::SourceFile(_) => ImmediateLocation::ItemList, ast::ItemList(_) => ImmediateLocation::ItemList, ast::RefExpr(_) => ImmediateLocation::RefExpr, - ast::Variant(_) => ImmediateLocation::Variant, - ast::RecordField(it) => if it.ty().map_or(false, |it| it.syntax().text_range().contains(offset)) { - return None; - } else { - ImmediateLocation::RecordField - }, ast::RecordExprFieldList(_) => sema .find_node_at_offset_with_macros(original_file, offset) .map(ImmediateLocation::RecordExprUpdate)?, @@ -237,13 +226,6 @@ pub(crate) fn determine_location( ast::GenericArgList(_) => sema .find_node_at_offset_with_macros(original_file, offset) .map(ImmediateLocation::GenericArgList)?, - ast::Module(it) => { - if it.item_list().is_none() { - ImmediateLocation::ModDeclaration(it) - } else { - return None; - } - }, ast::FieldExpr(it) => { let receiver = find_in_original_file(it.expr(), original_file); let receiver_is_ambiguous_float_literal = if let Some(ast::Expr::Literal(l)) = &receiver { @@ -475,13 +457,6 @@ mod tests { check_location(r"impl A { fn f$0 }", None); } - #[test] - fn test_record_field_loc() { - check_location(r"struct Foo { f$0 }", ImmediateLocation::RecordField); - check_location(r"struct Foo { f$0 pub f: i32}", ImmediateLocation::RecordField); - check_location(r"struct Foo { pub f: i32, f$0 }", ImmediateLocation::RecordField); - } - #[test] fn test_block_expr_loc() { check_location(r"fn my_fn() { let a = 2; f$0 }", ImmediateLocation::StmtList);