From b207e5781e2c1392d95696606b83134a5493cd42 Mon Sep 17 00:00:00 2001 From: roife Date: Tue, 3 Sep 2024 05:01:56 +0800 Subject: [PATCH 1/3] refactor: move ide_assist::utils::suggest_name to ide-db --- .../ide-assists/src/handlers/extract_variable.rs | 3 ++- .../src/handlers/generate_delegate_trait.rs | 3 ++- .../src/handlers/introduce_named_generic.rs | 3 ++- .../replace_is_method_with_if_let_method.rs | 3 ++- crates/ide-assists/src/utils.rs | 1 - crates/ide-db/src/lib.rs | 1 + .../src/syntax_helpers}/suggest_name.rs | 15 +++++++-------- 7 files changed, 16 insertions(+), 13 deletions(-) rename crates/{ide-assists/src/utils => ide-db/src/syntax_helpers}/suggest_name.rs (97%) diff --git a/crates/ide-assists/src/handlers/extract_variable.rs b/crates/ide-assists/src/handlers/extract_variable.rs index 5ae75bb1ff8e..a43a4b5e1a06 100644 --- a/crates/ide-assists/src/handlers/extract_variable.rs +++ b/crates/ide-assists/src/handlers/extract_variable.rs @@ -1,4 +1,5 @@ use hir::TypeInfo; +use ide_db::syntax_helpers::suggest_name; use syntax::{ ast::{self, edit::IndentLevel, edit_in_place::Indent, make, AstNode, HasName}, ted, NodeOrToken, @@ -6,7 +7,7 @@ use syntax::{ SyntaxNode, T, }; -use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists}; +use crate::{AssistContext, AssistId, AssistKind, Assists}; // Assist: extract_variable // diff --git a/crates/ide-assists/src/handlers/generate_delegate_trait.rs b/crates/ide-assists/src/handlers/generate_delegate_trait.rs index bf4ce5c907e8..c22d19574fb4 100644 --- a/crates/ide-assists/src/handlers/generate_delegate_trait.rs +++ b/crates/ide-assists/src/handlers/generate_delegate_trait.rs @@ -2,13 +2,14 @@ use std::ops::Not; use crate::{ assist_context::{AssistContext, Assists}, - utils::{convert_param_list_to_arg_list, suggest_name}, + utils::convert_param_list_to_arg_list, }; use either::Either; use hir::{db::HirDatabase, HasVisibility}; use ide_db::{ assists::{AssistId, GroupLabel}, path_transform::PathTransform, + syntax_helpers::suggest_name, FxHashMap, FxHashSet, }; use itertools::Itertools; diff --git a/crates/ide-assists/src/handlers/introduce_named_generic.rs b/crates/ide-assists/src/handlers/introduce_named_generic.rs index 543b7f7ab632..a734a6cc2bc8 100644 --- a/crates/ide-assists/src/handlers/introduce_named_generic.rs +++ b/crates/ide-assists/src/handlers/introduce_named_generic.rs @@ -1,9 +1,10 @@ +use ide_db::syntax_helpers::suggest_name; use syntax::{ ast::{self, edit_in_place::GenericParamsOwnerEdit, make, AstNode, HasGenericParams}, ted, }; -use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists}; +use crate::{AssistContext, AssistId, AssistKind, Assists}; // Assist: introduce_named_generic // diff --git a/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs b/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs index 59bb0c45e143..a856da092153 100644 --- a/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs +++ b/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs @@ -1,9 +1,10 @@ +use ide_db::syntax_helpers::suggest_name; use syntax::{ ast::{self, make, AstNode}, ted, }; -use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists}; +use crate::{AssistContext, AssistId, AssistKind, Assists}; // Assist: replace_is_some_with_if_let_some // diff --git a/crates/ide-assists/src/utils.rs b/crates/ide-assists/src/utils.rs index b8a6f3b6dbeb..19d1ef3157d1 100644 --- a/crates/ide-assists/src/utils.rs +++ b/crates/ide-assists/src/utils.rs @@ -23,7 +23,6 @@ use crate::assist_context::{AssistContext, SourceChangeBuilder}; mod gen_trait_fn_body; pub(crate) mod ref_field_expr; -pub(crate) mod suggest_name; pub(crate) fn unwrap_trivial_block(block_expr: ast::BlockExpr) -> ast::Expr { extract_trivial_expression(&block_expr) diff --git a/crates/ide-db/src/lib.rs b/crates/ide-db/src/lib.rs index 8a2068e9039f..ab161f0ce571 100644 --- a/crates/ide-db/src/lib.rs +++ b/crates/ide-db/src/lib.rs @@ -38,6 +38,7 @@ pub mod syntax_helpers { pub mod format_string_exprs; pub use hir::insert_whitespace_into_node; pub mod node_ext; + pub mod suggest_name; pub use parser::LexedStr; } diff --git a/crates/ide-assists/src/utils/suggest_name.rs b/crates/ide-db/src/syntax_helpers/suggest_name.rs similarity index 97% rename from crates/ide-assists/src/utils/suggest_name.rs rename to crates/ide-db/src/syntax_helpers/suggest_name.rs index 3130ef069557..14128e74438b 100644 --- a/crates/ide-assists/src/utils/suggest_name.rs +++ b/crates/ide-db/src/syntax_helpers/suggest_name.rs @@ -1,14 +1,16 @@ //! This module contains functions to suggest names for expressions, functions and other items use hir::Semantics; -use ide_db::{FxHashSet, RootDatabase}; use itertools::Itertools; +use rustc_hash::FxHashSet; use stdx::to_lower_snake_case; use syntax::{ ast::{self, HasName}, match_ast, AstNode, Edition, SmolStr, }; +use crate::RootDatabase; + /// Trait names, that will be ignored when in `impl Trait` and `dyn Trait` const USELESS_TRAITS: &[&str] = &["Send", "Sync", "Copy", "Clone", "Eq", "PartialEq"]; @@ -66,10 +68,7 @@ const USELESS_METHODS: &[&str] = &[ /// The function checks if the name conflicts with existing generic parameters. /// If so, it will try to resolve the conflict by adding a number suffix, e.g. /// `T`, `T0`, `T1`, ... -pub(crate) fn for_unique_generic_name( - name: &str, - existing_params: &ast::GenericParamList, -) -> SmolStr { +pub fn for_unique_generic_name(name: &str, existing_params: &ast::GenericParamList) -> SmolStr { let param_names = existing_params .generic_params() .map(|param| match param { @@ -101,7 +100,7 @@ pub(crate) fn for_unique_generic_name( /// /// If the name conflicts with existing generic parameters, it will try to /// resolve the conflict with `for_unique_generic_name`. -pub(crate) fn for_impl_trait_as_generic( +pub fn for_impl_trait_as_generic( ty: &ast::ImplTraitType, existing_params: &ast::GenericParamList, ) -> SmolStr { @@ -132,7 +131,7 @@ pub(crate) fn for_impl_trait_as_generic( /// /// Currently it sticks to the first name found. // FIXME: Microoptimize and return a `SmolStr` here. -pub(crate) fn for_variable(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> String { +pub fn for_variable(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> String { // `from_param` does not benefit from stripping // it need the largest context possible // so we check firstmost @@ -184,7 +183,7 @@ fn normalize(name: &str) -> Option { fn is_valid_name(name: &str) -> bool { matches!( - ide_db::syntax_helpers::LexedStr::single_token(syntax::Edition::CURRENT_FIXME, name), + super::LexedStr::single_token(syntax::Edition::CURRENT_FIXME, name), Some((syntax::SyntaxKind::IDENT, _error)) ) } From 492e66ceab4708429d803e9f794267351ed848e3 Mon Sep 17 00:00:00 2001 From: roife Date: Tue, 3 Sep 2024 05:15:21 +0800 Subject: [PATCH 2/3] feat: suggest name in let_stmt and fn_param --- crates/ide-completion/src/completions.rs | 10 ++++++++++ .../ide-completion/src/completions/pattern.rs | 13 +++++++++++++ crates/ide-completion/src/context.rs | 1 + crates/ide-completion/src/context/analysis.rs | 13 +++++++++++++ .../ide-db/src/syntax_helpers/suggest_name.rs | 18 ++++++++++++++++-- 5 files changed, 53 insertions(+), 2 deletions(-) diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs index b537150608bb..414627fbabae 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 60cfb7e5a8c5..2a06fc401755 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 bcd9df941947..d457ba32bf00 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, pub(crate) has_type_ascription: bool, + pub(crate) should_suggest_name: bool, pub(crate) parent_pat: Option, pub(crate) ref_token: Option, pub(crate) mut_token: Option, diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index 292c419498d1..1f9e3edf625d 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 14128e74438b..6ee526a67ea5 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 { + 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 { fn from_type(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option { 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 { From 35ed65a5133de574ca3b3dba67b8090868a8f2fc Mon Sep 17 00:00:00 2001 From: roife Date: Tue, 3 Sep 2024 05:16:02 +0800 Subject: [PATCH 3/3] tests: suggesting names in completions for let_stmt and fn_param --- crates/ide-completion/src/tests/pattern.rs | 73 ++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/crates/ide-completion/src/tests/pattern.rs b/crates/ide-completion/src/tests/pattern.rs index 6a0b67e291af..bd3e7c72bcd6 100644 --- a/crates/ide-completion/src/tests/pattern.rs +++ b/crates/ide-completion/src/tests/pattern.rs @@ -198,6 +198,7 @@ fn foo(a$0: Tuple) { st Unit bn Record {…} Record { field$1 }$0 bn Tuple(…) Tuple($1)$0 + bn tuple kw mut kw ref "#]], @@ -850,3 +851,75 @@ fn foo() { "#, ); } + +#[test] +fn suggest_name_for_pattern() { + check_edit( + "s1", + r#" +struct S1; + +fn foo() { + let $0 = S1; +} +"#, + r#" +struct S1; + +fn foo() { + let s1 = S1; +} +"#, + ); + + check_edit( + "s1", + r#" +struct S1; + +fn foo(s$0: S1) { +} +"#, + r#" +struct S1; + +fn foo(s1: S1) { +} +"#, + ); + + // Tests for &adt + check_edit( + "s1", + r#" +struct S1; + +fn foo() { + let $0 = &S1; +} +"#, + r#" +struct S1; + +fn foo() { + let s1 = &S1; +} +"#, + ); + + // Do not suggest reserved keywords + check_empty( + r#" +struct Struct; + +fn foo() { + let $0 = Struct; +} +"#, + expect![[r#" + st Struct + kw mut + kw ref + "#]], + ); +}