From 7c940246d2712a875abfbb966664d4dcedf7dd50 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 30 May 2026 05:34:25 +0800 Subject: [PATCH] fix: no complete keyword colons before colons Example --- ```rust fn foo() { $0::bar } ``` **Before this PR** ```rust fn foo() { self::::bar } ``` **After this PR** ```rust fn foo() { self::bar } ``` --- crates/ide-completion/src/completions.rs | 25 ++++++++++++++++--- crates/ide-completion/src/render.rs | 8 +++++- crates/ide-completion/src/tests/expression.rs | 23 +++++++++++++++++ 3 files changed, 52 insertions(+), 4 deletions(-) diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs index 20048ea97b2c..fd764c4f6168 100644 --- a/crates/ide-completion/src/completions.rs +++ b/crates/ide-completion/src/completions.rs @@ -97,11 +97,30 @@ impl Completions { item.add_to(self, ctx.db); } + pub(crate) fn add_keyword_with_colon( + &mut self, + ctx: &CompletionContext<'_, '_>, + keyword: &'static str, + ) { + debug_assert!(!keyword.ends_with("::")); + let label = syntax::format_smolstr!("{keyword}::"); + let mut item = CompletionItem::new( + CompletionItemKind::Keyword, + ctx.source_range(), + label.clone(), + ctx.edition, + ); + if ctx.token.next_token().is_some_and(|it| it.kind() == syntax::T![::]) { + item.insert_text(keyword); + } + item.add_to(self, ctx.db); + } + pub(crate) fn add_nameref_keywords_with_colon(&mut self, ctx: &CompletionContext<'_, '_>) { - ["self::", "crate::"].into_iter().for_each(|kw| self.add_keyword(ctx, kw)); + ["self", "crate"].into_iter().for_each(|kw| self.add_keyword_with_colon(ctx, kw)); if ctx.depth_from_crate_root > 0 { - self.add_keyword(ctx, "super::"); + self.add_keyword_with_colon(ctx, "super"); } } @@ -152,7 +171,7 @@ impl Completions { && len > 0 && len < ctx.depth_from_crate_root { - self.add_keyword(ctx, "super::"); + self.add_keyword_with_colon(ctx, "super"); } } diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs index bb92aad1841e..e64c47b95bf4 100644 --- a/crates/ide-completion/src/render.rs +++ b/crates/ide-completion/src/render.rs @@ -622,12 +622,18 @@ pub(crate) fn render_type_keyword_snippet( ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, label: &str, - snippet: &str, + mut snippet: &str, ) -> Builder { let source_range = ctx.source_range(); let mut item = CompletionItem::new(CompletionItemKind::Keyword, source_range, label, ctx.edition); + if let Some(keyword) = snippet.strip_suffix("::") + && ctx.token.next_token().is_some_and(|it| it.kind() == syntax::T![::]) + { + snippet = keyword; + } + let insert_text = if !snippet.contains('$') { item.insert_text(snippet); snippet diff --git a/crates/ide-completion/src/tests/expression.rs b/crates/ide-completion/src/tests/expression.rs index eb99664c3538..b479ced15c9f 100644 --- a/crates/ide-completion/src/tests/expression.rs +++ b/crates/ide-completion/src/tests/expression.rs @@ -1193,6 +1193,29 @@ fn foo() { i!(module) }"#, ); } +#[test] +fn complete_self_kw() { + check_edit("self::", r#"fn foo() { $0 }"#, r#"fn foo() { self:: }"#); + + check_edit("self::", r#"fn foo() -> $0 {}"#, r#"fn foo() -> self:: {}"#); + + check_edit("self::", r#"fn foo() { $0::bar }"#, r#"fn foo() { self::bar }"#); + + check_edit("self::", r#"fn foo() -> $0::bar {}"#, r#"fn foo() -> self::bar {}"#); + + check_edit("self::", r#"fn foo() { $0bar::baz }"#, r#"fn foo() { self::bar::baz }"#); + + check_edit( + "self::", + r#" +macro_rules! i { ($i:ident) => { $i::bar } } +fn foo() { i!($0) }"#, + r#" +macro_rules! i { ($i:ident) => { $i::bar } } +fn foo() { i!(self) }"#, + ); +} + #[test] fn else_completion_after_if() { check(