Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-completion/src/completions/fn_param.rs')
| -rw-r--r-- | crates/ide-completion/src/completions/fn_param.rs | 88 |
1 files changed, 65 insertions, 23 deletions
diff --git a/crates/ide-completion/src/completions/fn_param.rs b/crates/ide-completion/src/completions/fn_param.rs index 34d25c9c67..bd0b69215c 100644 --- a/crates/ide-completion/src/completions/fn_param.rs +++ b/crates/ide-completion/src/completions/fn_param.rs @@ -4,9 +4,9 @@ use hir::HirDisplay; use ide_db::FxHashMap; use itertools::Either; use syntax::{ - AstNode, Direction, SyntaxKind, TextRange, TextSize, algo, + AstNode, Direction, SmolStr, SyntaxKind, TextRange, TextSize, ToSmolStr, algo, ast::{self, HasModuleItem}, - match_ast, + format_smolstr, match_ast, }; use crate::{ @@ -25,19 +25,32 @@ pub(crate) fn complete_fn_param( ctx: &CompletionContext<'_>, pattern_ctx: &PatternContext, ) -> Option<()> { - let (ParamContext { param_list, kind, .. }, impl_or_trait) = match pattern_ctx { + let (ParamContext { param_list, kind, param, .. }, impl_or_trait) = match pattern_ctx { PatternContext { param_ctx: Some(kind), impl_or_trait, .. } => (kind, impl_or_trait), _ => return None, }; + let qualifier = param_qualifier(param); let comma_wrapper = comma_wrapper(ctx); let mut add_new_item_to_acc = |label: &str| { - let mk_item = |label: &str, range: TextRange| { - CompletionItem::new(CompletionItemKind::Binding, range, label, ctx.edition) + let label = label.strip_prefix(qualifier.as_str()).unwrap_or(label); + let insert = if label.starts_with('#') { + // FIXME: `#[attr] it: i32` -> `#[attr] mut it: i32` + label.to_smolstr() + } else { + format_smolstr!("{qualifier}{label}") + }; + let mk_item = |insert_text: &str, range: TextRange| { + let mut item = + CompletionItem::new(CompletionItemKind::Binding, range, label, ctx.edition); + if insert_text != label { + item.insert_text(insert_text); + } + item }; let item = match &comma_wrapper { - Some((fmt, range)) => mk_item(&fmt(label), *range), - None => mk_item(label, ctx.source_range()), + Some((fmt, range)) => mk_item(&fmt(&insert), *range), + None => mk_item(&insert, ctx.source_range()), }; // Completion lookup is omitted intentionally here. // See the full discussion: https://github.com/rust-lang/rust-analyzer/issues/12073 @@ -46,13 +59,18 @@ pub(crate) fn complete_fn_param( match kind { ParamKind::Function(function) => { - fill_fn_params(ctx, function, param_list, impl_or_trait, add_new_item_to_acc); + fill_fn_params(ctx, function, param_list, param, impl_or_trait, add_new_item_to_acc); } ParamKind::Closure(closure) => { - let stmt_list = closure.syntax().ancestors().find_map(ast::StmtList::cast)?; - params_from_stmt_list_scope(ctx, stmt_list, |name, ty| { - add_new_item_to_acc(&format!("{}: {ty}", name.display(ctx.db, ctx.edition))); - }); + if is_simple_param(param) { + let stmt_list = closure.syntax().ancestors().find_map(ast::StmtList::cast)?; + params_from_stmt_list_scope(ctx, stmt_list, |name, ty| { + add_new_item_to_acc(&format_smolstr!( + "{}: {ty}", + name.display(ctx.db, ctx.edition) + )); + }); + } } } @@ -63,6 +81,7 @@ fn fill_fn_params( ctx: &CompletionContext<'_>, function: &ast::Fn, param_list: &ast::ParamList, + current_param: &ast::Param, impl_or_trait: &Option<Either<ast::Impl, ast::Trait>>, mut add_new_item_to_acc: impl FnMut(&str), ) { @@ -71,15 +90,17 @@ fn fill_fn_params( let mut extract_params = |f: ast::Fn| { f.param_list().into_iter().flat_map(|it| it.params()).for_each(|param| { if let Some(pat) = param.pat() { - // FIXME: We should be able to turn these into SmolStr without having to allocate a String - let whole_param = param.syntax().text().to_string(); - let binding = pat.syntax().text().to_string(); + let whole_param = param.to_smolstr(); + let binding = pat.to_smolstr(); file_params.entry(whole_param).or_insert(binding); } }); }; for node in ctx.token.parent_ancestors() { + if !is_simple_param(current_param) { + break; + } match_ast! { match node { ast::SourceFile(it) => it.items().filter_map(|item| match item { @@ -99,11 +120,13 @@ fn fill_fn_params( }; } - if let Some(stmt_list) = function.syntax().parent().and_then(ast::StmtList::cast) { + if let Some(stmt_list) = function.syntax().parent().and_then(ast::StmtList::cast) + && is_simple_param(current_param) + { params_from_stmt_list_scope(ctx, stmt_list, |name, ty| { file_params - .entry(format!("{}: {ty}", name.display(ctx.db, ctx.edition))) - .or_insert(name.display(ctx.db, ctx.edition).to_string()); + .entry(format_smolstr!("{}: {ty}", name.display(ctx.db, ctx.edition))) + .or_insert(name.display(ctx.db, ctx.edition).to_smolstr()); }); } remove_duplicated(&mut file_params, param_list.params()); @@ -139,11 +162,11 @@ fn params_from_stmt_list_scope( } fn remove_duplicated( - file_params: &mut FxHashMap<String, String>, + file_params: &mut FxHashMap<SmolStr, SmolStr>, fn_params: ast::AstChildren<ast::Param>, ) { fn_params.for_each(|param| { - let whole_param = param.syntax().text().to_string(); + let whole_param = param.to_smolstr(); file_params.remove(&whole_param); match param.pat() { @@ -151,7 +174,7 @@ fn remove_duplicated( // if the type is missing we are checking the current param to be completed // in which case this would find itself removing the suggestions due to itself Some(pattern) if param.ty().is_some() => { - let binding = pattern.syntax().text().to_string(); + let binding = pattern.to_smolstr(); file_params.retain(|_, v| v != &binding); } _ => (), @@ -173,7 +196,7 @@ fn should_add_self_completions( } } -fn comma_wrapper(ctx: &CompletionContext<'_>) -> Option<(impl Fn(&str) -> String, TextRange)> { +fn comma_wrapper(ctx: &CompletionContext<'_>) -> Option<(impl Fn(&str) -> SmolStr, TextRange)> { let param = ctx.original_token.parent_ancestors().find(|node| node.kind() == SyntaxKind::PARAM)?; @@ -196,5 +219,24 @@ fn comma_wrapper(ctx: &CompletionContext<'_>) -> Option<(impl Fn(&str) -> String matches!(prev_token_kind, SyntaxKind::COMMA | SyntaxKind::L_PAREN | SyntaxKind::PIPE); let leading = if has_leading_comma { "" } else { ", " }; - Some((move |label: &_| format!("{leading}{label}{trailing}"), param.text_range())) + Some((move |label: &_| format_smolstr!("{leading}{label}{trailing}"), param.text_range())) +} + +fn is_simple_param(param: &ast::Param) -> bool { + param + .pat() + .is_none_or(|pat| matches!(pat, ast::Pat::IdentPat(ident_pat) if ident_pat.pat().is_none())) +} + +fn param_qualifier(param: &ast::Param) -> SmolStr { + let mut b = syntax::SmolStrBuilder::new(); + if let Some(ast::Pat::IdentPat(pat)) = param.pat() { + if pat.ref_token().is_some() { + b.push_str("ref "); + } + if pat.mut_token().is_some() { + b.push_str("mut "); + } + } + b.finish() } |