Unnamed repository; edit this file 'description' to name the repository.
feat: suggest name in let_stmt and fn_param
roife 2024-09-03
parent b207e57 · commit 492e66c
-rw-r--r--crates/ide-completion/src/completions.rs10
-rw-r--r--crates/ide-completion/src/completions/pattern.rs13
-rw-r--r--crates/ide-completion/src/context.rs1
-rw-r--r--crates/ide-completion/src/context/analysis.rs13
-rw-r--r--crates/ide-db/src/syntax_helpers/suggest_name.rs18
5 files changed, 53 insertions, 2 deletions
diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs
index b537150608..414627fbab 100644
--- a/crates/ide-completion/src/completions.rs
+++ b/crates/ide-completion/src/completions.rs
@@ -617,6 +617,16 @@ impl Completions {
}
self.add_opt(render_struct_pat(RenderContext::new(ctx), pattern_ctx, strukt, local_name));
}
+
+ pub(crate) fn suggest_name(&mut self, ctx: &CompletionContext<'_>, name: &str) {
+ let item = CompletionItem::new(
+ CompletionItemKind::Binding,
+ ctx.source_range(),
+ SmolStr::from(name),
+ ctx.edition,
+ );
+ item.add_to(self, ctx.db);
+ }
}
/// Calls the callback for each variant of the provided enum with the path to the variant.
diff --git a/crates/ide-completion/src/completions/pattern.rs b/crates/ide-completion/src/completions/pattern.rs
index 60cfb7e5a8..2a06fc4017 100644
--- a/crates/ide-completion/src/completions/pattern.rs
+++ b/crates/ide-completion/src/completions/pattern.rs
@@ -1,6 +1,7 @@
//! Completes constants and paths in unqualified patterns.
use hir::{db::DefDatabase, AssocItem, ScopeDef};
+use ide_db::syntax_helpers::suggest_name;
use syntax::ast::Pat;
use crate::{
@@ -45,6 +46,18 @@ pub(crate) fn complete_pattern(
return;
}
+ // Suggest name only in let-stmt and fn param
+ if pattern_ctx.should_suggest_name {
+ if let Some(suggested) = ctx
+ .expected_type
+ .as_ref()
+ .map(|ty| ty.strip_references())
+ .and_then(|ty| suggest_name::for_type(&ty, ctx.db, ctx.edition))
+ {
+ acc.suggest_name(ctx, &suggested);
+ }
+ }
+
let refutable = pattern_ctx.refutability == PatternRefutability::Refutable;
let single_variant_enum = |enum_: hir::Enum| ctx.db.enum_data(enum_.into()).variants.len() == 1;
diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs
index bcd9df9419..d457ba32bf 100644
--- a/crates/ide-completion/src/context.rs
+++ b/crates/ide-completion/src/context.rs
@@ -264,6 +264,7 @@ pub(crate) struct PatternContext {
pub(crate) refutability: PatternRefutability,
pub(crate) param_ctx: Option<ParamContext>,
pub(crate) has_type_ascription: bool,
+ pub(crate) should_suggest_name: bool,
pub(crate) parent_pat: Option<ast::Pat>,
pub(crate) ref_token: Option<SyntaxToken>,
pub(crate) mut_token: Option<SyntaxToken>,
diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs
index 292c419498..1f9e3edf62 100644
--- a/crates/ide-completion/src/context/analysis.rs
+++ b/crates/ide-completion/src/context/analysis.rs
@@ -1430,10 +1430,23 @@ fn pattern_context_for(
_ => (None, None),
};
+ // Only suggest name in let-stmt or fn param
+ let should_suggest_name = matches!(
+ &pat,
+ ast::Pat::IdentPat(it)
+ if it.syntax()
+ .parent()
+ .map_or(false, |node| {
+ let kind = node.kind();
+ ast::LetStmt::can_cast(kind) || ast::Param::can_cast(kind)
+ })
+ );
+
PatternContext {
refutability,
param_ctx,
has_type_ascription,
+ should_suggest_name,
parent_pat: pat.syntax().parent().and_then(ast::Pat::cast),
mut_token,
ref_token,
diff --git a/crates/ide-db/src/syntax_helpers/suggest_name.rs b/crates/ide-db/src/syntax_helpers/suggest_name.rs
index 14128e7443..6ee526a67e 100644
--- a/crates/ide-db/src/syntax_helpers/suggest_name.rs
+++ b/crates/ide-db/src/syntax_helpers/suggest_name.rs
@@ -60,6 +60,21 @@ const USELESS_METHODS: &[&str] = &[
"into_future",
];
+/// Suggest a name for given type.
+///
+/// The function will strip references first, and suggest name from the inner type.
+///
+/// - If `ty` is an ADT, it will suggest the name of the ADT.
+/// + If `ty` is wrapped in `Box`, `Option` or `Result`, it will suggest the name from the inner type.
+/// - If `ty` is a trait, it will suggest the name of the trait.
+/// - If `ty` is an `impl Trait`, it will suggest the name of the first trait.
+///
+/// If the suggested name conflicts with reserved keywords, it will return `None`.
+pub fn for_type(ty: &hir::Type, db: &RootDatabase, edition: Edition) -> Option<String> {
+ let ty = ty.strip_references();
+ name_of_type(&ty, db, edition)
+}
+
/// Suggest a unique name for generic parameter.
///
/// `existing_params` is used to check if the name conflicts with existing
@@ -269,10 +284,9 @@ fn var_name_from_pat(pat: &ast::Pat) -> Option<ast::Name> {
fn from_type(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<String> {
let ty = sema.type_of_expr(expr)?.adjusted();
- let ty = ty.remove_ref().unwrap_or(ty);
let edition = sema.scope(expr.syntax())?.krate().edition(sema.db);
- name_of_type(&ty, sema.db, edition)
+ for_type(&ty, sema.db, edition)
}
fn name_of_type(ty: &hir::Type, db: &RootDatabase, edition: Edition) -> Option<String> {