diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index b57f49690ac0..526e90bd0b4f 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -3024,7 +3024,7 @@ impl Callable { } /// For IDE only -#[derive(Debug, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum ScopeDef { ModuleDef(ModuleDef), MacroDef(MacroDef), diff --git a/crates/ide_completion/src/completions.rs b/crates/ide_completion/src/completions.rs index 8474ebec0d9c..974264c319bd 100644 --- a/crates/ide_completion/src/completions.rs +++ b/crates/ide_completion/src/completions.rs @@ -84,7 +84,7 @@ impl Completions { &mut self, ctx: &CompletionContext, local_name: hir::Name, - resolution: &hir::ScopeDef, + resolution: hir::ScopeDef, ) { if ctx.is_scope_def_hidden(resolution) { cov_mark::hit!(qualified_path_doc_hidden); @@ -115,7 +115,7 @@ impl Completions { if !ctx.is_visible(&func) { return; } - self.add_opt(render_fn(RenderContext::new(ctx), None, local_name, func)); + self.add(render_fn(RenderContext::new(ctx), None, local_name, func)); } pub(crate) fn add_method( @@ -128,7 +128,7 @@ impl Completions { if !ctx.is_visible(&func) { return; } - self.add_opt(render_method(RenderContext::new(ctx), None, receiver, local_name, func)); + self.add(render_method(RenderContext::new(ctx), None, receiver, local_name, func)); } pub(crate) fn add_const(&mut self, ctx: &CompletionContext, konst: hir::Const) { diff --git a/crates/ide_completion/src/completions/lifetime.rs b/crates/ide_completion/src/completions/lifetime.rs index f5308254346e..4082414f029f 100644 --- a/crates/ide_completion/src/completions/lifetime.rs +++ b/crates/ide_completion/src/completions/lifetime.rs @@ -35,7 +35,7 @@ pub(crate) fn complete_lifetime(acc: &mut Completions, ctx: &CompletionContext) ctx.scope.process_all_names(&mut |name, res| { if let ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) = res { if param_lifetime != Some(&*name.to_smol_str()) { - acc.add_resolution(ctx, name, &res); + acc.add_resolution(ctx, name, res); } } }); @@ -51,7 +51,7 @@ pub(crate) fn complete_label(acc: &mut Completions, ctx: &CompletionContext) { } ctx.scope.process_all_names(&mut |name, res| { if let ScopeDef::Label(_) = res { - acc.add_resolution(ctx, name, &res); + acc.add_resolution(ctx, name, res); } }); } diff --git a/crates/ide_completion/src/completions/pattern.rs b/crates/ide_completion/src/completions/pattern.rs index b3b27f251f6f..a140ca4239bf 100644 --- a/crates/ide_completion/src/completions/pattern.rs +++ b/crates/ide_completion/src/completions/pattern.rs @@ -59,7 +59,7 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { _ => false, }; if add_resolution { - acc.add_resolution(ctx, name, &res); + acc.add_resolution(ctx, name, res); } }); } diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs index a0d6d5cdc6b9..656d46b10bec 100644 --- a/crates/ide_completion/src/completions/qualified_path.rs +++ b/crates/ide_completion/src/completions/qualified_path.rs @@ -44,7 +44,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon } } if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = def { - acc.add_resolution(ctx, name, &def); + acc.add_resolution(ctx, name, def); } } } @@ -64,7 +64,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon .next() { if let Some(name) = next.name(ctx.db) { - acc.add_resolution(ctx, name, &ScopeDef::ModuleDef(next.into())); + acc.add_resolution(ctx, name, ScopeDef::ModuleDef(next.into())); } } } @@ -80,7 +80,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon _ => false, }; if add_resolution { - acc.add_resolution(ctx, name, &def); + acc.add_resolution(ctx, name, def); } } } @@ -147,7 +147,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon }; if add_resolution { - acc.add_resolution(ctx, name, &def); + acc.add_resolution(ctx, name, def); } } } diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs index ac1ba2da7c43..e7980c12d77d 100644 --- a/crates/ide_completion/src/completions/unqualified_path.rs +++ b/crates/ide_completion/src/completions/unqualified_path.rs @@ -24,7 +24,7 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC cov_mark::hit!(unqualified_path_only_modules_in_import); ctx.process_all_names(&mut |name, res| { if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res { - acc.add_resolution(ctx, name, &res); + acc.add_resolution(ctx, name, res); } }); @@ -43,7 +43,7 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC _ => false, }; if add_resolution { - acc.add_resolution(ctx, name, &res); + acc.add_resolution(ctx, name, res); } }); return; @@ -61,7 +61,7 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC } } if let hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res { - acc.add_resolution(ctx, name, &res); + acc.add_resolution(ctx, name, res); } }); return; @@ -76,7 +76,7 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC _ => false, }; if add_resolution { - acc.add_resolution(ctx, name, &res); + acc.add_resolution(ctx, name, res); } }); return; @@ -134,7 +134,7 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC _ => true, }; if add_resolution { - acc.add_resolution(ctx, name, &res); + acc.add_resolution(ctx, name, res); } }); } diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index c6af285e86c3..d0ed98898986 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs @@ -278,7 +278,7 @@ impl<'a> CompletionContext<'a> { self.is_visible_impl(&item.visibility(self.db), &item.attrs(self.db), item.krate(self.db)) } - pub(crate) fn is_scope_def_hidden(&self, scope_def: &ScopeDef) -> bool { + pub(crate) fn is_scope_def_hidden(&self, scope_def: ScopeDef) -> bool { if let (Some(attrs), Some(krate)) = (scope_def.attrs(self.db), scope_def.krate(self.db)) { return self.is_doc_hidden(&attrs, krate); } @@ -303,7 +303,7 @@ impl<'a> CompletionContext<'a> { /// A version of [`SemanticsScope::process_all_names`] that filters out `#[doc(hidden)]` items. pub(crate) fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef)) { self.scope.process_all_names(&mut |name, def| { - if self.is_scope_def_hidden(&def) { + if self.is_scope_def_hidden(def) { return; } @@ -367,7 +367,7 @@ impl<'a> CompletionContext<'a> { parse.reparse(&edit).tree() }; let fake_ident_token = - file_with_fake_ident.syntax().token_at_offset(offset).right_biased().unwrap(); + file_with_fake_ident.syntax().token_at_offset(offset).right_biased()?; let original_token = original_file.syntax().token_at_offset(offset).left_biased()?; let token = sema.descend_into_macros_single(original_token.clone()); diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs index 4b4fbb3ed9e2..404a2fa9d33e 100644 --- a/crates/ide_completion/src/render.rs +++ b/crates/ide_completion/src/render.rs @@ -11,7 +11,7 @@ pub(crate) mod struct_literal; mod builder_ext; -use hir::{AsAssocItem, HasAttrs, HirDisplay}; +use hir::{AsAssocItem, HasAttrs, HirDisplay, ScopeDef}; use ide_db::{ helpers::{item_name, SnippetCap}, RootDatabase, SymbolKind, @@ -47,8 +47,8 @@ impl<'a> RenderContext<'a> { self.completion.source_range() } - fn is_deprecated(&self, node: impl HasAttrs) -> bool { - let attrs = node.attrs(self.db()); + fn is_deprecated(&self, def: impl HasAttrs) -> bool { + let attrs = def.attrs(self.db()); attrs.by_key("deprecated").exists() || attrs.by_key("rustc_deprecated").exists() } @@ -71,8 +71,8 @@ impl<'a> RenderContext<'a> { .unwrap_or(false) } - fn docs(&self, node: impl HasAttrs) -> Option { - node.docs(self.db()) + fn docs(&self, def: impl HasAttrs) -> Option { + def.docs(self.db()) } } @@ -128,7 +128,7 @@ pub(crate) fn render_tuple_field( pub(crate) fn render_resolution( ctx: RenderContext<'_>, local_name: hir::Name, - resolution: &hir::ScopeDef, + resolution: ScopeDef, ) -> Option { render_resolution_(ctx, local_name, None, resolution) } @@ -137,65 +137,36 @@ pub(crate) fn render_resolution_with_import( ctx: RenderContext<'_>, import_edit: ImportEdit, ) -> Option { - let resolution = hir::ScopeDef::from(import_edit.import.original_item); + let resolution = ScopeDef::from(import_edit.import.original_item); let local_name = match resolution { - hir::ScopeDef::ModuleDef(hir::ModuleDef::Function(f)) => f.name(ctx.completion.db), - hir::ScopeDef::ModuleDef(hir::ModuleDef::Const(c)) => c.name(ctx.completion.db)?, - hir::ScopeDef::ModuleDef(hir::ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db), + ScopeDef::ModuleDef(hir::ModuleDef::Function(f)) => f.name(ctx.completion.db), + ScopeDef::ModuleDef(hir::ModuleDef::Const(c)) => c.name(ctx.completion.db)?, + ScopeDef::ModuleDef(hir::ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db), _ => item_name(ctx.db(), import_edit.import.original_item)?, }; - render_resolution_(ctx, local_name, Some(import_edit), &resolution) + render_resolution_(ctx, local_name, Some(import_edit), resolution) } fn render_resolution_( ctx: RenderContext<'_>, local_name: hir::Name, import_to_add: Option, - resolution: &hir::ScopeDef, + resolution: ScopeDef, ) -> Option { let _p = profile::span("render_resolution"); use hir::ModuleDef::*; - let kind = match resolution { - hir::ScopeDef::ModuleDef(Function(func)) => { - return render_fn(ctx, import_to_add, Some(local_name), *func); - } - hir::ScopeDef::ModuleDef(Variant(_)) if ctx.completion.pattern_ctx.is_some() => { - CompletionItemKind::SymbolKind(SymbolKind::Variant) - } - hir::ScopeDef::ModuleDef(Variant(var)) => { - let item = render_variant(ctx, import_to_add, Some(local_name), *var, None); - return Some(item); - } - hir::ScopeDef::MacroDef(mac) => { - let item = render_macro(ctx, import_to_add, local_name, *mac); - return item; - } + let db = ctx.db(); - hir::ScopeDef::ModuleDef(Module(..)) => CompletionItemKind::SymbolKind(SymbolKind::Module), - hir::ScopeDef::ModuleDef(Adt(adt)) => CompletionItemKind::SymbolKind(match adt { - hir::Adt::Struct(_) => SymbolKind::Struct, - hir::Adt::Union(_) => SymbolKind::Union, - hir::Adt::Enum(_) => SymbolKind::Enum, - }), - hir::ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::SymbolKind(SymbolKind::Const), - hir::ScopeDef::ModuleDef(Static(..)) => CompletionItemKind::SymbolKind(SymbolKind::Static), - hir::ScopeDef::ModuleDef(Trait(..)) => CompletionItemKind::SymbolKind(SymbolKind::Trait), - hir::ScopeDef::ModuleDef(TypeAlias(..)) => { - CompletionItemKind::SymbolKind(SymbolKind::TypeAlias) + let kind = match resolution { + ScopeDef::ModuleDef(Function(func)) => { + return Some(render_fn(ctx, import_to_add, Some(local_name), func)); } - hir::ScopeDef::ModuleDef(BuiltinType(..)) => CompletionItemKind::BuiltinType, - hir::ScopeDef::GenericParam(param) => CompletionItemKind::SymbolKind(match param { - hir::GenericParam::TypeParam(_) => SymbolKind::TypeParam, - hir::GenericParam::LifetimeParam(_) => SymbolKind::LifetimeParam, - hir::GenericParam::ConstParam(_) => SymbolKind::ConstParam, - }), - hir::ScopeDef::Local(..) => CompletionItemKind::SymbolKind(SymbolKind::Local), - hir::ScopeDef::Label(..) => CompletionItemKind::SymbolKind(SymbolKind::Label), - hir::ScopeDef::AdtSelfType(..) | hir::ScopeDef::ImplSelfType(..) => { - CompletionItemKind::SymbolKind(SymbolKind::SelfParam) + ScopeDef::ModuleDef(Variant(var)) if ctx.completion.pattern_ctx.is_none() => { + return Some(render_variant(ctx, import_to_add, Some(local_name), var, None)); } - hir::ScopeDef::Unknown => { + ScopeDef::MacroDef(mac) => return render_macro(ctx, import_to_add, local_name, mac), + ScopeDef::Unknown => { let mut item = CompletionItem::new( CompletionItemKind::UnresolvedReference, ctx.source_range(), @@ -206,14 +177,37 @@ fn render_resolution_( } return Some(item.build()); } + + ScopeDef::ModuleDef(Variant(_)) => CompletionItemKind::SymbolKind(SymbolKind::Variant), + ScopeDef::ModuleDef(Module(..)) => CompletionItemKind::SymbolKind(SymbolKind::Module), + ScopeDef::ModuleDef(Adt(adt)) => CompletionItemKind::SymbolKind(match adt { + hir::Adt::Struct(_) => SymbolKind::Struct, + hir::Adt::Union(_) => SymbolKind::Union, + hir::Adt::Enum(_) => SymbolKind::Enum, + }), + ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::SymbolKind(SymbolKind::Const), + ScopeDef::ModuleDef(Static(..)) => CompletionItemKind::SymbolKind(SymbolKind::Static), + ScopeDef::ModuleDef(Trait(..)) => CompletionItemKind::SymbolKind(SymbolKind::Trait), + ScopeDef::ModuleDef(TypeAlias(..)) => CompletionItemKind::SymbolKind(SymbolKind::TypeAlias), + ScopeDef::ModuleDef(BuiltinType(..)) => CompletionItemKind::BuiltinType, + ScopeDef::GenericParam(param) => CompletionItemKind::SymbolKind(match param { + hir::GenericParam::TypeParam(_) => SymbolKind::TypeParam, + hir::GenericParam::LifetimeParam(_) => SymbolKind::LifetimeParam, + hir::GenericParam::ConstParam(_) => SymbolKind::ConstParam, + }), + ScopeDef::Local(..) => CompletionItemKind::SymbolKind(SymbolKind::Local), + ScopeDef::Label(..) => CompletionItemKind::SymbolKind(SymbolKind::Label), + ScopeDef::AdtSelfType(..) | ScopeDef::ImplSelfType(..) => { + CompletionItemKind::SymbolKind(SymbolKind::SelfParam) + } }; let local_name = local_name.to_smol_str(); let mut item = CompletionItem::new(kind, ctx.source_range(), local_name.clone()); - if let hir::ScopeDef::Local(local) = resolution { - let ty = local.ty(ctx.db()); + if let ScopeDef::Local(local) = resolution { + let ty = local.ty(db); if !ty.is_unknown() { - item.detail(ty.display(ctx.db()).to_string()); + item.detail(ty.display(db).to_string()); } item.set_relevance(CompletionRelevance { @@ -229,15 +223,15 @@ fn render_resolution_( }; // Add `<>` for generic types - if matches!( + let type_path_no_ty_args = matches!( ctx.completion.path_context, Some(PathCompletionContext { kind: Some(PathKind::Type), has_type_args: false, .. }) - ) && ctx.completion.config.add_call_parenthesis - { + ) && ctx.completion.config.add_call_parenthesis; + if type_path_no_ty_args { if let Some(cap) = ctx.snippet_cap() { let has_non_default_type_params = match resolution { - hir::ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(ctx.db()), - hir::ScopeDef::ModuleDef(TypeAlias(it)) => it.has_non_default_type_params(ctx.db()), + ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(db), + ScopeDef::ModuleDef(TypeAlias(it)) => it.has_non_default_type_params(db), _ => false, }; if has_non_default_type_params { @@ -248,7 +242,7 @@ fn render_resolution_( } } } - item.set_documentation(scope_def_docs(ctx.db(), resolution)) + item.set_documentation(scope_def_docs(db, resolution)) .set_deprecated(scope_def_is_deprecated(&ctx, resolution)); if let Some(import_to_add) = import_to_add { @@ -257,26 +251,26 @@ fn render_resolution_( Some(item.build()) } -fn scope_def_docs(db: &RootDatabase, resolution: &hir::ScopeDef) -> Option { +fn scope_def_docs(db: &RootDatabase, resolution: ScopeDef) -> Option { use hir::ModuleDef::*; match resolution { - hir::ScopeDef::ModuleDef(Module(it)) => it.docs(db), - hir::ScopeDef::ModuleDef(Adt(it)) => it.docs(db), - hir::ScopeDef::ModuleDef(Variant(it)) => it.docs(db), - hir::ScopeDef::ModuleDef(Const(it)) => it.docs(db), - hir::ScopeDef::ModuleDef(Static(it)) => it.docs(db), - hir::ScopeDef::ModuleDef(Trait(it)) => it.docs(db), - hir::ScopeDef::ModuleDef(TypeAlias(it)) => it.docs(db), + ScopeDef::ModuleDef(Module(it)) => it.docs(db), + ScopeDef::ModuleDef(Adt(it)) => it.docs(db), + ScopeDef::ModuleDef(Variant(it)) => it.docs(db), + ScopeDef::ModuleDef(Const(it)) => it.docs(db), + ScopeDef::ModuleDef(Static(it)) => it.docs(db), + ScopeDef::ModuleDef(Trait(it)) => it.docs(db), + ScopeDef::ModuleDef(TypeAlias(it)) => it.docs(db), _ => None, } } -fn scope_def_is_deprecated(ctx: &RenderContext<'_>, resolution: &hir::ScopeDef) -> bool { +fn scope_def_is_deprecated(ctx: &RenderContext<'_>, resolution: ScopeDef) -> bool { match resolution { - hir::ScopeDef::ModuleDef(it) => ctx.is_deprecated_assoc_item(*it), - hir::ScopeDef::MacroDef(it) => ctx.is_deprecated(*it), - hir::ScopeDef::GenericParam(it) => ctx.is_deprecated(*it), - hir::ScopeDef::AdtSelfType(it) => ctx.is_deprecated(*it), + ScopeDef::ModuleDef(it) => ctx.is_deprecated_assoc_item(it), + ScopeDef::MacroDef(it) => ctx.is_deprecated(it), + ScopeDef::GenericParam(it) => ctx.is_deprecated(it), + ScopeDef::AdtSelfType(it) => ctx.is_deprecated(it), _ => false, } } diff --git a/crates/ide_completion/src/render/const_.rs b/crates/ide_completion/src/render/const_.rs index c40490ac0b8c..65c9d1d63ede 100644 --- a/crates/ide_completion/src/render/const_.rs +++ b/crates/ide_completion/src/render/const_.rs @@ -2,53 +2,32 @@ use hir::{AsAssocItem, HasSource}; use ide_db::SymbolKind; -use syntax::{ast::Const, display::const_label}; +use syntax::display::const_label; use crate::{item::CompletionItem, render::RenderContext}; pub(crate) fn render_const(ctx: RenderContext<'_>, const_: hir::Const) -> Option { let _p = profile::span("render_const"); - ConstRender::new(ctx, const_)?.render() + render(ctx, const_) } -#[derive(Debug)] -struct ConstRender<'a> { - ctx: RenderContext<'a>, - const_: hir::Const, - ast_node: Const, -} +fn render(ctx: RenderContext<'_>, const_: hir::Const) -> Option { + let db = ctx.db(); + let name = const_.name(db)?.to_smol_str(); + // FIXME: This is parsing files! + let detail = const_label(&const_.source(db)?.value); -impl<'a> ConstRender<'a> { - fn new(ctx: RenderContext<'a>, const_: hir::Const) -> Option> { - let ast_node = const_.source(ctx.db())?.value; - Some(ConstRender { ctx, const_, ast_node }) - } + let mut item = CompletionItem::new(SymbolKind::Const, ctx.source_range(), name.clone()); + item.set_documentation(ctx.docs(const_)) + .set_deprecated(ctx.is_deprecated(const_) || ctx.is_deprecated_assoc_item(const_)) + .detail(detail); - fn render(self) -> Option { - let name = self.const_.name(self.ctx.db())?.to_smol_str(); - let detail = self.detail(); - - let mut item = - CompletionItem::new(SymbolKind::Const, self.ctx.source_range(), name.clone()); - item.set_documentation(self.ctx.docs(self.const_)) - .set_deprecated( - self.ctx.is_deprecated(self.const_) - || self.ctx.is_deprecated_assoc_item(self.const_), - ) - .detail(detail); - - let db = self.ctx.db(); - if let Some(actm) = self.const_.as_assoc_item(db) { - if let Some(trt) = actm.containing_trait_or_trait_impl(db) { - item.trait_name(trt.name(db).to_smol_str()); - item.insert_text(name); - } + if let Some(actm) = const_.as_assoc_item(db) { + if let Some(trt) = actm.containing_trait_or_trait_impl(db) { + item.trait_name(trt.name(db).to_smol_str()); + item.insert_text(name); } - - Some(item.build()) } - fn detail(&self) -> String { - const_label(&self.ast_node) - } + Some(item.build()) } diff --git a/crates/ide_completion/src/render/enum_variant.rs b/crates/ide_completion/src/render/enum_variant.rs index 26cb73d09c9e..f613f0dfde2d 100644 --- a/crates/ide_completion/src/render/enum_variant.rs +++ b/crates/ide_completion/src/render/enum_variant.rs @@ -1,10 +1,10 @@ //! Renderer for `enum` variants. -use std::{iter, mem}; +use std::iter; -use hir::{HasAttrs, HirDisplay}; +use hir::{db::HirDatabase, HasAttrs, HirDisplay, StructKind}; use ide_db::SymbolKind; -use stdx::format_to; +use itertools::Itertools; use crate::{ item::{CompletionItem, ImportEdit}, @@ -20,116 +20,86 @@ pub(crate) fn render_variant( path: Option, ) -> CompletionItem { let _p = profile::span("render_enum_variant"); - EnumRender::new(ctx, local_name, variant, path).render(import_to_add) + render(ctx, local_name, variant, path, import_to_add) } -#[derive(Debug)] -struct EnumRender<'a> { - ctx: RenderContext<'a>, +fn render( + ctx @ RenderContext { completion }: RenderContext<'_>, + local_name: Option, variant: hir::Variant, path: Option, - qualified_name: hir::ModPath, - short_qualified_name: hir::ModPath, - variant_kind: hir::StructKind, + import_to_add: Option, +) -> CompletionItem { + let db = completion.db; + let name = local_name.unwrap_or_else(|| variant.name(db)); + let variant_kind = variant.kind(db); + + let (qualified_name, short_qualified_name, qualified) = match path { + Some(path) => { + let short = hir::ModPath::from_segments( + hir::PathKind::Plain, + path.segments().iter().skip(path.segments().len().saturating_sub(2)).cloned(), + ); + (path, short, true) + } + None => ( + hir::ModPath::from_segments(hir::PathKind::Plain, iter::once(name.clone())), + hir::ModPath::from_segments(hir::PathKind::Plain, iter::once(name)), + false, + ), + }; + + // FIXME: ModPath::to_smol_str()? + let mut item = + CompletionItem::new(SymbolKind::Variant, ctx.source_range(), qualified_name.to_string()); + item.set_documentation(variant.docs(db)) + .set_deprecated(ctx.is_deprecated(variant)) + .detail(detail(db, variant, variant_kind)); + + if let Some(import_to_add) = import_to_add { + item.add_import(import_to_add); + } + + // FIXME: ModPath::to_smol_str()? + let short_qualified_name = short_qualified_name.to_string(); + if variant_kind == hir::StructKind::Tuple { + cov_mark::hit!(inserts_parens_for_tuple_enums); + let params = Params::Anonymous(variant.fields(db).len()); + item.add_call_parens(ctx.completion, short_qualified_name, params); + } else if qualified { + item.lookup_by(short_qualified_name); + } + + let ty = variant.parent_enum(ctx.completion.db).ty(ctx.completion.db); + item.set_relevance(CompletionRelevance { + type_match: compute_type_match(ctx.completion, &ty), + ..CompletionRelevance::default() + }); + + if let Some(ref_match) = compute_ref_match(ctx.completion, &ty) { + item.ref_match(ref_match); + } + + item.build() } -impl<'a> EnumRender<'a> { - fn new( - ctx: RenderContext<'a>, - local_name: Option, - variant: hir::Variant, - path: Option, - ) -> EnumRender<'a> { - let name = local_name.unwrap_or_else(|| variant.name(ctx.db())); - let variant_kind = variant.kind(ctx.db()); +fn detail(db: &dyn HirDatabase, variant: hir::Variant, variant_kind: StructKind) -> String { + let detail_types = variant.fields(db).into_iter().map(|field| (field.name(db), field.ty(db))); - let (qualified_name, short_qualified_name) = match &path { - Some(path) => { - let short = hir::ModPath::from_segments( - hir::PathKind::Plain, - path.segments().iter().skip(path.segments().len().saturating_sub(2)).cloned(), - ); - (path.clone(), short) - } - None => ( - hir::ModPath::from_segments(hir::PathKind::Plain, iter::once(name.clone())), - hir::ModPath::from_segments(hir::PathKind::Plain, iter::once(name)), - ), - }; - - EnumRender { ctx, variant, path, qualified_name, short_qualified_name, variant_kind } - } - fn render(self, import_to_add: Option) -> CompletionItem { - let mut item = CompletionItem::new( - SymbolKind::Variant, - self.ctx.source_range(), - self.qualified_name.to_string(), - ); - item.set_documentation(self.variant.docs(self.ctx.db())) - .set_deprecated(self.ctx.is_deprecated(self.variant)) - .detail(self.detail()); - - if let Some(import_to_add) = import_to_add { - item.add_import(import_to_add); + match variant_kind { + hir::StructKind::Tuple | hir::StructKind::Unit => { + format!("({})", detail_types.format_with(", ", |(_, t), f| f(&t.display(db)))) } - - if self.variant_kind == hir::StructKind::Tuple { - cov_mark::hit!(inserts_parens_for_tuple_enums); - let params = Params::Anonymous(self.variant.fields(self.ctx.db()).len()); - item.add_call_parens( - self.ctx.completion, - self.short_qualified_name.to_string(), - params, - ); - } else if self.path.is_some() { - item.lookup_by(self.short_qualified_name.to_string()); + hir::StructKind::Record => { + format!( + "{{{}}}", + detail_types.format_with(", ", |(n, t), f| { + f(&n)?; + f(&": ")?; + f(&t.display(db)) + }), + ) } - - let ty = self.variant.parent_enum(self.ctx.completion.db).ty(self.ctx.completion.db); - item.set_relevance(CompletionRelevance { - type_match: compute_type_match(self.ctx.completion, &ty), - ..CompletionRelevance::default() - }); - - if let Some(ref_match) = compute_ref_match(self.ctx.completion, &ty) { - item.ref_match(ref_match); - } - - item.build() - } - - fn detail(&self) -> String { - let detail_types = self - .variant - .fields(self.ctx.db()) - .into_iter() - .map(|field| (field.name(self.ctx.db()), field.ty(self.ctx.db()))); - - let mut b = String::new(); - let mut first_run = true; - match self.variant_kind { - hir::StructKind::Tuple | hir::StructKind::Unit => { - format_to!(b, "("); - for (_, t) in detail_types { - if !mem::take(&mut first_run) { - format_to!(b, ", "); - } - format_to!(b, "{}", t.display(self.ctx.db())); - } - format_to!(b, ")"); - } - hir::StructKind::Record => { - format_to!(b, "{{"); - for (n, t) in detail_types { - if !mem::take(&mut first_run) { - format_to!(b, ", "); - } - format_to!(b, "{}: {}", n, t.display(self.ctx.db())); - } - format_to!(b, "}}"); - } - } - b } } diff --git a/crates/ide_completion/src/render/function.rs b/crates/ide_completion/src/render/function.rs index 918210f2f368..f166b87ab631 100644 --- a/crates/ide_completion/src/render/function.rs +++ b/crates/ide_completion/src/render/function.rs @@ -1,11 +1,12 @@ //! Renderer for function calls. -use hir::{AsAssocItem, HirDisplay}; +use hir::{db::HirDatabase, AsAssocItem, HirDisplay}; use ide_db::SymbolKind; use itertools::Itertools; use stdx::format_to; use crate::{ + context::CompletionContext, item::{CompletionItem, CompletionItemKind, CompletionRelevance, ImportEdit}, render::{ builder_ext::Params, compute_exact_name_match, compute_ref_match, compute_type_match, @@ -13,14 +14,19 @@ use crate::{ }, }; +enum FuncType { + Function, + Method(Option), +} + pub(crate) fn render_fn( ctx: RenderContext<'_>, import_to_add: Option, local_name: Option, - fn_: hir::Function, -) -> Option { + func: hir::Function, +) -> CompletionItem { let _p = profile::span("render_fn"); - Some(FunctionRender::new(ctx, None, local_name, fn_, false)?.render(import_to_add)) + render(ctx, local_name, func, FuncType::Function, import_to_add) } pub(crate) fn render_method( @@ -28,135 +34,120 @@ pub(crate) fn render_method( import_to_add: Option, receiver: Option, local_name: Option, - fn_: hir::Function, -) -> Option { - let _p = profile::span("render_method"); - Some(FunctionRender::new(ctx, receiver, local_name, fn_, true)?.render(import_to_add)) -} - -#[derive(Debug)] -struct FunctionRender<'a> { - ctx: RenderContext<'a>, - name: hir::Name, - receiver: Option, func: hir::Function, - is_method: bool, +) -> CompletionItem { + let _p = profile::span("render_method"); + render(ctx, local_name, func, FuncType::Method(receiver), import_to_add) } -impl<'a> FunctionRender<'a> { - fn new( - ctx: RenderContext<'a>, - receiver: Option, - local_name: Option, - fn_: hir::Function, - is_method: bool, - ) -> Option> { - let name = local_name.unwrap_or_else(|| fn_.name(ctx.db())); +fn render( + ctx @ RenderContext { completion }: RenderContext<'_>, + local_name: Option, + func: hir::Function, + func_type: FuncType, + import_to_add: Option, +) -> CompletionItem { + let db = completion.db; - Some(FunctionRender { ctx, name, receiver, func: fn_, is_method }) - } + let name = local_name.unwrap_or_else(|| func.name(db)); + let params = params(completion, func, &func_type); - fn render(self, import_to_add: Option) -> CompletionItem { - let params = self.params(); - let call = match &self.receiver { - Some(receiver) => format!("{}.{}", receiver, &self.name), - None => self.name.to_string(), - }; - let mut item = CompletionItem::new(self.kind(), self.ctx.source_range(), call.clone()); - item.set_documentation(self.ctx.docs(self.func)) - .set_deprecated( - self.ctx.is_deprecated(self.func) || self.ctx.is_deprecated_assoc_item(self.func), - ) - .detail(self.detail()) - .add_call_parens(self.ctx.completion, call.clone(), params); - - if import_to_add.is_none() { - let db = self.ctx.db(); - if let Some(actm) = self.func.as_assoc_item(db) { - if let Some(trt) = actm.containing_trait_or_trait_impl(db) { - item.trait_name(trt.name(db).to_smol_str()); - } - } - } - - if let Some(import_to_add) = import_to_add { - item.add_import(import_to_add); - } - item.lookup_by(self.name.to_smol_str()); - - let ret_type = self.func.ret_type(self.ctx.db()); - item.set_relevance(CompletionRelevance { - type_match: compute_type_match(self.ctx.completion, &ret_type), - exact_name_match: compute_exact_name_match(self.ctx.completion, &call), - ..CompletionRelevance::default() - }); - - if let Some(ref_match) = compute_ref_match(self.ctx.completion, &ret_type) { - // FIXME - // For now we don't properly calculate the edits for ref match - // completions on methods, so we've disabled them. See #8058. - if !self.is_method { - item.ref_match(ref_match); - } - } - - item.build() - } - - fn detail(&self) -> String { - let ret_ty = self.func.ret_type(self.ctx.db()); - let mut detail = format!("fn({})", self.params_display()); - if !ret_ty.is_unit() { - format_to!(detail, " -> {}", ret_ty.display(self.ctx.db())); - } - detail - } - - fn params_display(&self) -> String { - if let Some(self_param) = self.func.self_param(self.ctx.db()) { - let params = self - .func - .assoc_fn_params(self.ctx.db()) - .into_iter() - .skip(1) // skip the self param because we are manually handling that - .map(|p| p.ty().display(self.ctx.db()).to_string()); - - std::iter::once(self_param.display(self.ctx.db()).to_owned()).chain(params).join(", ") - } else { - let params = self - .func - .assoc_fn_params(self.ctx.db()) - .into_iter() - .map(|p| p.ty().display(self.ctx.db()).to_string()) - .join(", "); - params - } - } - - fn params(&self) -> Params { - let (params, self_param) = - if self.ctx.completion.has_dot_receiver() || self.receiver.is_some() { - (self.func.method_params(self.ctx.db()).unwrap_or_default(), None) - } else { - let self_param = self.func.self_param(self.ctx.db()); - - let mut assoc_params = self.func.assoc_fn_params(self.ctx.db()); - if self_param.is_some() { - assoc_params.remove(0); - } - (assoc_params, self_param) - }; - - Params::Named(self_param, params) - } - - fn kind(&self) -> CompletionItemKind { - if self.func.self_param(self.ctx.db()).is_some() { + // FIXME: SmolStr? + let call = match &func_type { + FuncType::Method(Some(receiver)) => format!("{}.{}", receiver, &name), + _ => name.to_string(), + }; + let mut item = CompletionItem::new( + if func.self_param(db).is_some() { CompletionItemKind::Method } else { - SymbolKind::Function.into() + CompletionItemKind::SymbolKind(SymbolKind::Function) + }, + ctx.source_range(), + call.clone(), + ); + item.set_documentation(ctx.docs(func)) + .set_deprecated(ctx.is_deprecated(func) || ctx.is_deprecated_assoc_item(func)) + .detail(detail(db, func)) + .add_call_parens(completion, call.clone(), params); + + if import_to_add.is_none() { + if let Some(actm) = func.as_assoc_item(db) { + if let Some(trt) = actm.containing_trait_or_trait_impl(db) { + item.trait_name(trt.name(db).to_smol_str()); + } } } + + if let Some(import_to_add) = import_to_add { + item.add_import(import_to_add); + } + item.lookup_by(name.to_smol_str()); + + let ret_type = func.ret_type(db); + item.set_relevance(CompletionRelevance { + type_match: compute_type_match(completion, &ret_type), + exact_name_match: compute_exact_name_match(completion, &call), + ..CompletionRelevance::default() + }); + + if let Some(ref_match) = compute_ref_match(completion, &ret_type) { + // FIXME + // For now we don't properly calculate the edits for ref match + // completions on methods, so we've disabled them. See #8058. + if matches!(func_type, FuncType::Function) { + item.ref_match(ref_match); + } + } + + item.build() +} + +fn detail(db: &dyn HirDatabase, func: hir::Function) -> String { + let ret_ty = func.ret_type(db); + let mut detail = format!("fn({})", params_display(db, func)); + if !ret_ty.is_unit() { + format_to!(detail, " -> {}", ret_ty.display(db)); + } + detail +} + +fn params_display(db: &dyn HirDatabase, func: hir::Function) -> String { + if let Some(self_param) = func.self_param(db) { + let assoc_fn_params = func.assoc_fn_params(db); + let params = assoc_fn_params + .iter() + .skip(1) // skip the self param because we are manually handling that + .map(|p| p.ty().display(db)); + format!( + "{}{}", + self_param.display(db), + params.format_with("", |display, f| { + f(&", ")?; + f(&display) + }) + ) + } else { + let assoc_fn_params = func.assoc_fn_params(db); + assoc_fn_params.iter().map(|p| p.ty().display(db)).join(", ") + } +} + +fn params(ctx: &CompletionContext<'_>, func: hir::Function, func_type: &FuncType) -> Params { + let (params, self_param) = + if ctx.has_dot_receiver() || matches!(func_type, FuncType::Method(Some(_))) { + (func.method_params(ctx.db).unwrap_or_default(), None) + } else { + let self_param = func.self_param(ctx.db); + + let mut assoc_params = func.assoc_fn_params(ctx.db); + if self_param.is_some() { + assoc_params.remove(0); + } + (assoc_params, self_param) + }; + + Params::Named(self_param, params) } #[cfg(test)] diff --git a/crates/ide_completion/src/render/macro_.rs b/crates/ide_completion/src/render/macro_.rs index ac1f152aeac3..9c7b9a6db3ce 100644 --- a/crates/ide_completion/src/render/macro_.rs +++ b/crates/ide_completion/src/render/macro_.rs @@ -1,7 +1,7 @@ //! Renderer for macro invocations. use either::Either; -use hir::HasSource; +use hir::{db::HirDatabase, Documentation, HasSource}; use ide_db::SymbolKind; use syntax::{ display::{fn_as_proc_macro_label, macro_label}, @@ -21,94 +21,96 @@ pub(crate) fn render_macro( macro_: hir::MacroDef, ) -> Option { let _p = profile::span("render_macro"); - MacroRender::new(ctx, name, macro_).render(import_to_add) + render(ctx, name, macro_, import_to_add) } -#[derive(Debug)] -struct MacroRender<'a> { - ctx: RenderContext<'a>, - name: SmolStr, +fn render( + ctx @ RenderContext { completion }: RenderContext<'_>, + name: hir::Name, macro_: hir::MacroDef, - docs: Option, - bra: &'static str, - ket: &'static str, + import_to_add: Option, +) -> Option { + let db = completion.db; + + let source_range = if completion.is_immediately_after_macro_bang() { + cov_mark::hit!(completes_macro_call_if_cursor_at_bang_token); + completion.token.parent().map(|it| it.text_range()) + } else { + Some(ctx.source_range()) + }?; + + let name = name.to_smol_str(); + let docs = ctx.docs(macro_); + let docs_str = docs.as_ref().map(Documentation::as_str).unwrap_or_default(); + let (bra, ket) = + if macro_.is_fn_like() { guess_macro_braces(&name, docs_str) } else { ("", "") }; + + let needs_bang = macro_.is_fn_like() + && !matches!(completion.path_kind(), Some(PathKind::Mac | PathKind::Use)); + + let mut item = CompletionItem::new( + SymbolKind::from(macro_.kind()), + source_range, + label(&ctx, needs_bang, bra, ket, &name), + ); + item.set_deprecated(ctx.is_deprecated(macro_)) + .set_detail(detail(db, macro_)) + .set_documentation(docs); + + if let Some(import_to_add) = import_to_add { + item.add_import(import_to_add); + } + + let name = &*name; + + match ctx.snippet_cap() { + Some(cap) if needs_bang && !completion.path_is_call() => { + let snippet = format!("{}!{}$0{}", name, bra, ket); + let lookup = banged_name(name); + item.insert_snippet(cap, snippet).lookup_by(lookup); + } + _ if needs_bang => { + let banged_name = banged_name(name); + item.insert_text(banged_name.clone()).lookup_by(banged_name); + } + _ => { + cov_mark::hit!(dont_insert_macro_call_parens_unncessary); + item.insert_text(name); + } + }; + + Some(item.build()) } -impl<'a> MacroRender<'a> { - fn new(ctx: RenderContext<'a>, name: hir::Name, macro_: hir::MacroDef) -> MacroRender<'a> { - let name = name.to_smol_str(); - let docs = ctx.docs(macro_); - let docs_str = docs.as_ref().map_or("", |s| s.as_str()); - let (bra, ket) = - if macro_.is_fn_like() { guess_macro_braces(&name, docs_str) } else { ("", "") }; - - MacroRender { ctx, name, macro_, docs, bra, ket } - } - - fn render(self, import_to_add: Option) -> Option { - let source_range = if self.ctx.completion.is_immediately_after_macro_bang() { - cov_mark::hit!(completes_macro_call_if_cursor_at_bang_token); - self.ctx.completion.token.parent().map(|it| it.text_range()) +fn label( + ctx: &RenderContext<'_>, + needs_bang: bool, + bra: &str, + ket: &str, + name: &SmolStr, +) -> SmolStr { + if needs_bang { + if ctx.snippet_cap().is_some() { + SmolStr::from_iter([&*name, "!", bra, "…", ket]) } else { - Some(self.ctx.source_range()) - }?; - let mut item = - CompletionItem::new(SymbolKind::from(self.macro_.kind()), source_range, self.label()); - item.set_deprecated(self.ctx.is_deprecated(self.macro_)).set_detail(self.detail()); - - if let Some(import_to_add) = import_to_add { - item.add_import(import_to_add); + banged_name(name) } - - let needs_bang = self.macro_.is_fn_like() - && !matches!(self.ctx.completion.path_kind(), Some(PathKind::Mac | PathKind::Use)); - let has_parens = self.ctx.completion.path_is_call(); - - match self.ctx.snippet_cap() { - Some(cap) if needs_bang && !has_parens => { - let snippet = format!("{}!{}$0{}", self.name, self.bra, self.ket); - let lookup = self.banged_name(); - item.insert_snippet(cap, snippet).lookup_by(lookup); - } - _ if needs_bang => { - let lookup = self.banged_name(); - item.insert_text(self.banged_name()).lookup_by(lookup); - } - _ => { - cov_mark::hit!(dont_insert_macro_call_parens_unncessary); - item.insert_text(&*self.name); - } - }; - - item.set_documentation(self.docs); - Some(item.build()) + } else { + name.clone() } +} - fn needs_bang(&self) -> bool { - !matches!(self.ctx.completion.path_kind(), Some(PathKind::Mac | PathKind::Use)) - } +fn banged_name(name: &str) -> SmolStr { + SmolStr::from_iter([name, "!"]) +} - fn label(&self) -> SmolStr { - if !self.macro_.is_fn_like() { - self.name.clone() - } else if self.needs_bang() && self.ctx.snippet_cap().is_some() { - SmolStr::from_iter([&*self.name, "!", self.bra, "…", self.ket]) - } else { - self.banged_name() - } - } - - fn banged_name(&self) -> SmolStr { - SmolStr::from_iter([&*self.name, "!"]) - } - - fn detail(&self) -> Option { - let detail = match self.macro_.source(self.ctx.db())?.value { - Either::Left(node) => macro_label(&node), - Either::Right(node) => fn_as_proc_macro_label(&node), - }; - Some(detail) - } +fn detail(db: &dyn HirDatabase, macro_: hir::MacroDef) -> Option { + // FIXME: This is parsing the file! + let detail = match macro_.source(db)?.value { + Either::Left(node) => macro_label(&node), + Either::Right(node) => fn_as_proc_macro_label(&node), + }; + Some(detail) } fn guess_macro_braces(macro_name: &str, docs: &str) -> (&'static str, &'static str) { @@ -147,7 +149,7 @@ mod tests { fn dont_insert_macro_call_parens_unncessary() { cov_mark::check!(dont_insert_macro_call_parens_unncessary); check_edit( - "frobnicate!", + "frobnicate", r#" //- /main.rs crate:main deps:foo use foo::$0; @@ -161,7 +163,7 @@ use foo::frobnicate; ); check_edit( - "frobnicate!", + "frobnicate", r#" macro_rules! frobnicate { () => () } fn main() { frob$0!(); } diff --git a/crates/ide_completion/src/render/type_alias.rs b/crates/ide_completion/src/render/type_alias.rs index 259fe25a6cd6..5df21fb36cdd 100644 --- a/crates/ide_completion/src/render/type_alias.rs +++ b/crates/ide_completion/src/render/type_alias.rs @@ -2,10 +2,7 @@ use hir::{AsAssocItem, HasSource}; use ide_db::SymbolKind; -use syntax::{ - ast::{HasName, TypeAlias}, - display::type_label, -}; +use syntax::{ast::HasName, display::type_label}; use crate::{item::CompletionItem, render::RenderContext}; @@ -14,7 +11,7 @@ pub(crate) fn render_type_alias( type_alias: hir::TypeAlias, ) -> Option { let _p = profile::span("render_type_alias"); - TypeAliasRender::new(ctx, type_alias)?.render(false) + render(ctx, type_alias, false) } pub(crate) fn render_type_alias_with_eq( @@ -22,53 +19,38 @@ pub(crate) fn render_type_alias_with_eq( type_alias: hir::TypeAlias, ) -> Option { let _p = profile::span("render_type_alias_with_eq"); - TypeAliasRender::new(ctx, type_alias)?.render(true) + render(ctx, type_alias, true) } -#[derive(Debug)] -struct TypeAliasRender<'a> { - ctx: RenderContext<'a>, +fn render( + ctx: RenderContext<'_>, type_alias: hir::TypeAlias, - ast_node: TypeAlias, -} + with_eq: bool, +) -> Option { + let db = ctx.db(); -impl<'a> TypeAliasRender<'a> { - fn new(ctx: RenderContext<'a>, type_alias: hir::TypeAlias) -> Option> { - let ast_node = type_alias.source(ctx.db())?.value; - Some(TypeAliasRender { ctx, type_alias, ast_node }) - } - - fn render(self, with_eq: bool) -> Option { - let name = self.ast_node.name().map(|name| { - if with_eq { - format!("{} = ", name.text()) - } else { - name.text().to_string() - } - })?; - let detail = self.detail(); - - let mut item = - CompletionItem::new(SymbolKind::TypeAlias, self.ctx.source_range(), name.clone()); - item.set_documentation(self.ctx.docs(self.type_alias)) - .set_deprecated( - self.ctx.is_deprecated(self.type_alias) - || self.ctx.is_deprecated_assoc_item(self.type_alias), - ) - .detail(detail); - - let db = self.ctx.db(); - if let Some(actm) = self.type_alias.as_assoc_item(db) { - if let Some(trt) = actm.containing_trait_or_trait_impl(db) { - item.trait_name(trt.name(db).to_smol_str()); - item.insert_text(name); - } + // FIXME: This parses the file! + let ast_node = type_alias.source(db)?.value; + let name = ast_node.name().map(|name| { + if with_eq { + format!("{} = ", name.text()) + } else { + name.text().to_string() } + })?; + let detail = type_label(&ast_node); - Some(item.build()) + let mut item = CompletionItem::new(SymbolKind::TypeAlias, ctx.source_range(), name.clone()); + item.set_documentation(ctx.docs(type_alias)) + .set_deprecated(ctx.is_deprecated(type_alias) || ctx.is_deprecated_assoc_item(type_alias)) + .detail(detail); + + if let Some(actm) = type_alias.as_assoc_item(db) { + if let Some(trt) = actm.containing_trait_or_trait_impl(db) { + item.trait_name(trt.name(db).to_smol_str()); + item.insert_text(name); + } } - fn detail(&self) -> String { - type_label(&self.ast_node) - } + Some(item.build()) } diff --git a/crates/ide_completion/src/tests/use_tree.rs b/crates/ide_completion/src/tests/use_tree.rs index 4cde258122cc..5f20e342e02d 100644 --- a/crates/ide_completion/src/tests/use_tree.rs +++ b/crates/ide_completion/src/tests/use_tree.rs @@ -129,7 +129,7 @@ struct Bar; "#, expect![[r#" st Foo - ma foo! macro_rules! foo_ + ma foo macro_rules! foo_ "#]], ); }