diff --git a/crates/hir_expand/src/name.rs b/crates/hir_expand/src/name.rs index 552603dce7a..2962b02e51b 100644 --- a/crates/hir_expand/src/name.rs +++ b/crates/hir_expand/src/name.rs @@ -67,12 +67,30 @@ pub fn missing() -> Name { Name::new_text("[missing name]".into()) } + /// Returns the tuple index this name represents if it is a tuple field. pub fn as_tuple_index(&self) -> Option { match self.0 { Repr::TupleField(idx) => Some(idx), _ => None, } } + + /// Returns the text this name represents if it isn't a tuple field. + pub fn as_text(&self) -> Option { + match &self.0 { + Repr::Text(it) => Some(it.clone()), + _ => None, + } + } + + /// Returns the textual representation of this name as a [`SmolStr`]. + /// Prefer using this over [`ToString::to_string`] if possible as this conversion is cheaper. + pub fn to_smol_str(&self) -> SmolStr { + match &self.0 { + Repr::Text(it) => it.clone(), + Repr::TupleField(it) => SmolStr::new(&it.to_string()), + } + } } pub trait AsName { diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index 25da46f1d7e..c30e6e40020 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs @@ -102,7 +102,9 @@ pub(crate) struct CompletionContext<'a> { pub(super) function_def: Option, /// The parent impl of the cursor position if it exists. pub(super) impl_def: Option, + /// The NameLike under the cursor in the original file if it exists. pub(super) name_syntax: Option, + pub(super) incomplete_let: bool, pub(super) completion_location: Option, pub(super) prev_sibling: Option, @@ -112,9 +114,7 @@ pub(crate) struct CompletionContext<'a> { pub(super) lifetime_ctx: Option, pub(super) pattern_ctx: Option, pub(super) path_context: Option, - pub(super) locals: Vec<(String, Local)>, - - pub(super) incomplete_let: bool, + pub(super) locals: Vec<(Name, Local)>, no_completion_required: bool, } @@ -148,7 +148,7 @@ pub(super) fn new( let mut locals = vec![]; scope.process_all_names(&mut |name, scope| { if let ScopeDef::Local(local) = scope { - locals.push((name.to_string(), local)); + locals.push((name, local)); } }); let mut ctx = CompletionContext { @@ -492,14 +492,6 @@ fn is_doc_hidden(&self, attrs: &hir::Attrs, defining_crate: hir::Crate) -> bool false } - fn fill_impl_def(&mut self) { - self.impl_def = self - .sema - .token_ancestors_with_macros(self.token.clone()) - .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) - .find_map(ast::Impl::cast); - } - fn expected_type_and_name(&self) -> (Option, Option) { let mut node = match self.token.parent() { Some(it) => it, @@ -654,6 +646,16 @@ fn fill( self.prev_sibling = determine_prev_sibling(&name_like); self.name_syntax = find_node_at_offset(original_file, name_like.syntax().text_range().start()); + self.impl_def = self + .sema + .token_ancestors_with_macros(self.token.clone()) + .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) + .find_map(ast::Impl::cast); + self.function_def = self + .sema + .token_ancestors_with_macros(self.token.clone()) + .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) + .find_map(ast::Fn::cast); match name_like { ast::NameLike::Lifetime(lifetime) => { self.classify_lifetime(original_file, lifetime, offset); @@ -691,8 +693,6 @@ fn classify_lifetime( } fn classify_name(&mut self, name: ast::Name) { - self.fill_impl_def(); - if let Some(bind_pat) = name.syntax().parent().and_then(ast::IdentPat::cast) { let is_name_in_field_pat = bind_pat .syntax() @@ -740,14 +740,6 @@ fn classify_name(&mut self, name: ast::Name) { } fn classify_name_ref(&mut self, original_file: &SyntaxNode, name_ref: ast::NameRef) { - self.fill_impl_def(); - - self.function_def = self - .sema - .token_ancestors_with_macros(self.token.clone()) - .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) - .find_map(ast::Fn::cast); - let parent = match name_ref.syntax().parent() { Some(it) => it, None => return, diff --git a/crates/ide_completion/src/render/builder_ext.rs b/crates/ide_completion/src/render/builder_ext.rs index 33d3a5ee188..ea8222f76a7 100644 --- a/crates/ide_completion/src/render/builder_ext.rs +++ b/crates/ide_completion/src/render/builder_ext.rs @@ -1,12 +1,14 @@ //! Extensions for `Builder` structure required for item rendering. +use either::Either; use itertools::Itertools; +use syntax::ast::{self, HasName}; use crate::{context::CallKind, item::Builder, patterns::ImmediateLocation, CompletionContext}; #[derive(Debug)] pub(super) enum Params { - Named(Vec), + Named(Vec<(Either, hir::Param)>), Anonymous(usize), } @@ -76,10 +78,46 @@ pub(super) fn add_call_parens( self.trigger_call_info(); let snippet = match (ctx.config.add_call_argument_snippets, params) { (true, Params::Named(params)) => { - let function_params_snippet = - params.iter().enumerate().format_with(", ", |(index, param_name), f| { - f(&format_args!("${{{}:{}}}", index + 1, param_name)) - }); + let function_params_snippet = params.iter().enumerate().format_with( + ", ", + |(index, (param_source, param)), f| { + let name; + let text; + let (ref_, name) = match param_source { + Either::Left(self_param) => ( + match self_param.kind() { + ast::SelfParamKind::Owned => "", + ast::SelfParamKind::Ref => "&", + ast::SelfParamKind::MutRef => "&mut ", + }, + "self", + ), + Either::Right(it) => { + let n = (|| { + let mut pat = it.pat()?; + loop { + match pat { + ast::Pat::IdentPat(pat) => break pat.name(), + ast::Pat::RefPat(it) => pat = it.pat()?, + _ => return None, + } + } + })(); + match n { + Some(n) => { + name = n; + text = name.text(); + let text = text.as_str().trim_start_matches('_'); + let ref_ = ref_of_param(ctx, text, param.ty()); + (ref_, text) + } + None => ("", "_"), + } + } + }; + f(&format_args!("${{{}:{}{}}}", index + 1, ref_, name)) + }, + ); format!("{}({})$0", name, function_params_snippet) } _ => { @@ -93,3 +131,13 @@ pub(super) fn add_call_parens( self.lookup_by(name).label(label).insert_snippet(cap, snippet) } } +fn ref_of_param(ctx: &CompletionContext, arg: &str, ty: &hir::Type) -> &'static str { + if let Some(derefed_ty) = ty.remove_ref() { + for (name, local) in ctx.locals.iter() { + if name.as_text().as_deref() == Some(arg) && local.ty(ctx.db) == derefed_ty { + return if ty.is_mutable_reference() { "&mut " } else { "&" }; + } + } + } + "" +} diff --git a/crates/ide_completion/src/render/function.rs b/crates/ide_completion/src/render/function.rs index cc95bd53690..769a0ed21f5 100644 --- a/crates/ide_completion/src/render/function.rs +++ b/crates/ide_completion/src/render/function.rs @@ -1,5 +1,6 @@ //! Renderer for function calls. +use either::Either; use hir::{AsAssocItem, HasSource, HirDisplay}; use ide_db::SymbolKind; use itertools::Itertools; @@ -160,47 +161,25 @@ fn ty_display(&self) -> String { format!("-> {}", ret_ty.display(self.ctx.db())) } - fn add_arg(&self, arg: &str, ty: &hir::Type) -> String { - if let Some(derefed_ty) = ty.remove_ref() { - for (name, local) in self.ctx.completion.locals.iter() { - if name == arg && local.ty(self.ctx.db()) == derefed_ty { - let mutability = if ty.is_mutable_reference() { "&mut " } else { "&" }; - return format!("{}{}", mutability, arg); - } - } - } - arg.to_string() - } - fn params(&self) -> Params { let ast_params = match self.ast_node.param_list() { Some(it) => it, None => return Params::Named(Vec::new()), }; + let params = ast_params.params().map(Either::Right); - let mut params_pats = Vec::new(); - let params_ty = if self.ctx.completion.has_dot_receiver() || self.receiver.is_some() { - self.func.method_params(self.ctx.db()).unwrap_or_default() + let params = if self.ctx.completion.has_dot_receiver() || self.receiver.is_some() { + params.zip(self.func.method_params(self.ctx.db()).unwrap_or_default()).collect() } else { - if let Some(s) = ast_params.self_param() { - cov_mark::hit!(parens_for_method_call_as_assoc_fn); - params_pats.push(Some(s.to_string())); - } - self.func.assoc_fn_params(self.ctx.db()) + ast_params + .self_param() + .map(Either::Left) + .into_iter() + .chain(params) + .zip(self.func.assoc_fn_params(self.ctx.db())) + .collect() }; - params_pats - .extend(ast_params.params().into_iter().map(|it| it.pat().map(|it| it.to_string()))); - let params = params_pats - .into_iter() - .zip(params_ty) - .flat_map(|(pat, param_ty)| { - let pat = pat?; - let name = pat; - let arg = name.trim_start_matches("mut ").trim_start_matches('_'); - Some(self.add_arg(arg, param_ty.ty())) - }) - .collect(); Params::Named(params) } @@ -310,7 +289,6 @@ fn foo(&self, x: i32) { #[test] fn parens_for_method_call_as_assoc_fn() { - cov_mark::check!(parens_for_method_call_as_assoc_fn); check_edit( "foo", r#"