From 847317ccbae3128b227d97864d31ffed405e7511 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 6 Dec 2025 22:43:24 +0800 Subject: [PATCH] Add default varname for TryEnum postfix completion Example --- ```rust fn main() { let bar = Some(true); bar.i$0 } ``` **Before this PR** ```rust fn main() { let bar = Some(true); if let Some($1) = bar { $0 } } ``` **After this PR** ```rust fn main() { let bar = Some(true); if let Some(${1:bar}) = bar { $0 } } ``` --- .../ide-completion/src/completions/postfix.rs | 76 ++++++++++++++----- .../ide-db/src/syntax_helpers/suggest_name.rs | 15 +++- 2 files changed, 70 insertions(+), 21 deletions(-) diff --git a/crates/ide-completion/src/completions/postfix.rs b/crates/ide-completion/src/completions/postfix.rs index 4dd84daf0649..7f67ef848ece 100644 --- a/crates/ide-completion/src/completions/postfix.rs +++ b/crates/ide-completion/src/completions/postfix.rs @@ -8,16 +8,18 @@ use ide_db::{ RootDatabase, SnippetCap, documentation::{Documentation, HasDocs}, imports::insert_use::ImportScope, + syntax_helpers::suggest_name::NameGenerator, text_edit::TextEdit, ty_filter::TryEnum, }; use itertools::Itertools; use stdx::never; use syntax::{ + SmolStr, SyntaxKind::{EXPR_STMT, STMT_LIST}, T, TextRange, TextSize, ToSmolStr, ast::{self, AstNode, AstToken}, - match_ast, + format_smolstr, match_ast, }; use crate::{ @@ -117,15 +119,20 @@ pub(crate) fn complete_postfix( if let Some(parent_expr) = ast::Expr::cast(parent) { is_in_cond = is_in_condition(&parent_expr); } + let placeholder = suggest_receiver_name(dot_receiver, "0", &ctx.sema); match &try_enum { Some(try_enum) if is_in_cond => match try_enum { TryEnum::Result => { - postfix_snippet("let", "let Ok(_)", &format!("let Ok($0) = {receiver_text}")) - .add_to(acc, ctx.db); + postfix_snippet( + "let", + "let Ok(_)", + &format!("let Ok({placeholder}) = {receiver_text}"), + ) + .add_to(acc, ctx.db); postfix_snippet( "letm", "let Ok(mut _)", - &format!("let Ok(mut $0) = {receiver_text}"), + &format!("let Ok(mut {placeholder}) = {receiver_text}"), ) .add_to(acc, ctx.db); } @@ -133,13 +140,13 @@ pub(crate) fn complete_postfix( postfix_snippet( "let", "let Some(_)", - &format!("let Some($0) = {receiver_text}"), + &format!("let Some({placeholder}) = {receiver_text}"), ) .add_to(acc, ctx.db); postfix_snippet( "letm", "let Some(mut _)", - &format!("let Some(mut $0) = {receiver_text}"), + &format!("let Some(mut {placeholder}) = {receiver_text}"), ) .add_to(acc, ctx.db); } @@ -186,26 +193,29 @@ pub(crate) fn complete_postfix( } } if let Some(try_enum) = &try_enum { + let placeholder = suggest_receiver_name(dot_receiver, "1", &ctx.sema); match try_enum { TryEnum::Result => { postfix_snippet( "ifl", "if let Ok {}", - &format!("if let Ok($1) = {receiver_text} {{\n $0\n}}"), + &format!("if let Ok({placeholder}) = {receiver_text} {{\n $0\n}}"), ) .add_to(acc, ctx.db); postfix_snippet( "lete", "let Ok else {}", - &format!("let Ok($1) = {receiver_text} else {{\n $2\n}};\n$0"), + &format!( + "let Ok({placeholder}) = {receiver_text} else {{\n $2\n}};\n$0" + ), ) .add_to(acc, ctx.db); postfix_snippet( "while", "while let Ok {}", - &format!("while let Ok($1) = {receiver_text} {{\n $0\n}}"), + &format!("while let Ok({placeholder}) = {receiver_text} {{\n $0\n}}"), ) .add_to(acc, ctx.db); } @@ -213,21 +223,23 @@ pub(crate) fn complete_postfix( postfix_snippet( "ifl", "if let Some {}", - &format!("if let Some($1) = {receiver_text} {{\n $0\n}}"), + &format!("if let Some({placeholder}) = {receiver_text} {{\n $0\n}}"), ) .add_to(acc, ctx.db); postfix_snippet( "lete", "let Some else {}", - &format!("let Some($1) = {receiver_text} else {{\n $2\n}};\n$0"), + &format!( + "let Some({placeholder}) = {receiver_text} else {{\n $2\n}};\n$0" + ), ) .add_to(acc, ctx.db); postfix_snippet( "while", "while let Some {}", - &format!("while let Some($1) = {receiver_text} {{\n $0\n}}"), + &format!("while let Some({placeholder}) = {receiver_text} {{\n $0\n}}"), ) .add_to(acc, ctx.db); } @@ -302,6 +314,34 @@ pub(crate) fn complete_postfix( } } +fn suggest_receiver_name( + receiver: &ast::Expr, + n: &str, + sema: &Semantics<'_, RootDatabase>, +) -> SmolStr { + let placeholder = |name| format_smolstr!("${{{n}:{name}}}"); + + match receiver { + ast::Expr::PathExpr(path) => { + if let Some(name) = path.path().and_then(|it| it.as_single_name_ref()) { + return placeholder(name.text().as_str()); + } + } + ast::Expr::RefExpr(it) => { + if let Some(receiver) = it.expr() { + return suggest_receiver_name(&receiver, n, sema); + } + } + _ => {} + } + + let name = NameGenerator::new_with_names([].into_iter()).try_for_variable(receiver, sema); + match name { + Some(name) => placeholder(&name), + None => format_smolstr!("${n}"), + } +} + fn get_receiver_text( sema: &Semantics<'_, RootDatabase>, receiver: &ast::Expr, @@ -616,7 +656,7 @@ fn main() { r#" fn main() { let bar = Some(true); - if let Some($1) = bar { + if let Some(${1:bar}) = bar { $0 } } @@ -666,7 +706,7 @@ fn main() { r#" fn main() { let bar = Some(true); - if let Some($0) = bar + if let Some(${0:bar}) = bar } "#, ); @@ -682,7 +722,7 @@ fn main() { r#" fn main() { let bar = Some(true); - if true && let Some($0) = bar + if true && let Some(${0:bar}) = bar } "#, ); @@ -698,7 +738,7 @@ fn main() { r#" fn main() { let bar = Some(true); - if true && true && let Some($0) = bar + if true && true && let Some(${0:bar}) = bar } "#, ); @@ -718,7 +758,7 @@ fn main() { r#" fn main() { let bar = Some(true); - let Some($1) = bar else { + let Some(${1:bar}) = bar else { $2 }; $0 @@ -792,7 +832,7 @@ fn main() { r#" fn main() { let bar = &Some(true); - if let Some($1) = bar { + if let Some(${1:bar}) = bar { $0 } } diff --git a/crates/ide-db/src/syntax_helpers/suggest_name.rs b/crates/ide-db/src/syntax_helpers/suggest_name.rs index 1a0ef55a8b25..273328a8d270 100644 --- a/crates/ide-db/src/syntax_helpers/suggest_name.rs +++ b/crates/ide-db/src/syntax_helpers/suggest_name.rs @@ -197,10 +197,19 @@ impl NameGenerator { expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>, ) -> SmolStr { + self.try_for_variable(expr, sema).unwrap_or(SmolStr::new_static("var_name")) + } + + /// Similar to `for_variable`, but fallback returns `None` + pub fn try_for_variable( + &mut self, + expr: &ast::Expr, + sema: &Semantics<'_, RootDatabase>, + ) -> Option { // `from_param` does not benefit from stripping it need the largest // context possible so we check firstmost if let Some(name) = from_param(expr, sema) { - return self.suggest_name(&name); + return Some(self.suggest_name(&name)); } let mut next_expr = Some(expr.clone()); @@ -209,7 +218,7 @@ impl NameGenerator { .or_else(|| from_type(&expr, sema)) .or_else(|| from_field_name(&expr)); if let Some(name) = name { - return self.suggest_name(&name); + return Some(self.suggest_name(&name)); } match expr { @@ -229,7 +238,7 @@ impl NameGenerator { } } - self.suggest_name("var_name") + None } /// Insert a name into the pool