From 405bbb3aa46ecbfa5124969739695565d9841b9c Mon Sep 17 00:00:00 2001 From: Josh Mcguigan Date: Mon, 15 Mar 2021 19:26:59 -0700 Subject: completions: centralize calculation of relevance and ref matches --- crates/ide_completion/src/render.rs | 182 ++++++++++++++++++----- crates/ide_completion/src/render/enum_variant.rs | 13 +- crates/ide_completion/src/render/function.rs | 27 ++-- 3 files changed, 172 insertions(+), 50 deletions(-) diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs index 2514dda7c..09a27de71 100644 --- a/crates/ide_completion/src/render.rs +++ b/crates/ide_completion/src/render.rs @@ -10,10 +10,8 @@ pub(crate) mod type_alias; mod builder_ext; -use base_db::Upcast; use hir::{ - db::HirDatabase, AsAssocItem, Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability, - ScopeDef, Type, + AsAssocItem, Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability, ScopeDef, Type, }; use ide_db::{ helpers::{item_name, SnippetCap}, @@ -22,8 +20,8 @@ use ide_db::{ use syntax::TextRange; use crate::{ - item::{CompletionRelevance, ImportEdit}, - CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, + item::ImportEdit, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, + CompletionRelevance, }; use crate::render::{enum_variant::render_variant, function::render_fn, macro_::render_macro}; @@ -144,7 +142,15 @@ impl<'a> Render<'a> { .set_documentation(field.docs(self.ctx.db())) .set_deprecated(is_deprecated); - item.set_relevance(compute_relevance(&self.ctx, &ty, &name.to_string())); + item.set_relevance(CompletionRelevance { + exact_type_match: compute_exact_type_match(self.ctx.completion, ty), + exact_name_match: compute_exact_name_match(self.ctx.completion, name.to_string()), + ..CompletionRelevance::default() + }); + + if let Some(ref_match) = compute_ref_match(self.ctx.completion, ty) { + item.ref_match(ref_match); + } item.build() } @@ -234,31 +240,18 @@ impl<'a> Render<'a> { if !ty.is_unknown() { item.detail(ty.display(self.ctx.db()).to_string()); } - }; - if let ScopeDef::Local(local) = resolution { - let ty = local.ty(self.ctx.db()); + item.set_relevance(CompletionRelevance { + exact_type_match: compute_exact_type_match(self.ctx.completion, &ty), + exact_name_match: compute_exact_name_match(self.ctx.completion, local_name.clone()), + is_local: true, + ..CompletionRelevance::default() + }); - let mut relevance = compute_relevance(&self.ctx, &ty, &local_name); - relevance.is_local = true; - item.set_relevance(relevance); - - if let Some(expected_type) = self.ctx.completion.expected_type.as_ref() { - if &ty != expected_type { - if let Some(ty_without_ref) = expected_type.remove_ref() { - if relevance_type_match(self.ctx.db().upcast(), &ty, &ty_without_ref) { - cov_mark::hit!(suggest_ref); - let mutability = if expected_type.is_mutable_reference() { - Mutability::Mut - } else { - Mutability::Shared - }; - item.ref_match(mutability); - } - } - } + if let Some(ref_match) = compute_ref_match(self.ctx.completion, &ty) { + item.ref_match(ref_match); } - } + }; // Add `<>` for generic types if self.ctx.completion.is_path_type @@ -313,17 +306,44 @@ impl<'a> Render<'a> { } } -fn compute_relevance(ctx: &RenderContext, ty: &Type, name: &str) -> CompletionRelevance { - let mut res = CompletionRelevance::default(); +fn compute_exact_type_match(ctx: &CompletionContext, completion_ty: &hir::Type) -> bool { + if let Some(expected_type) = ctx.expected_type.as_ref() { + // We don't ever consider unit type to be an exact type match, since + // nearly always this is not meaningful to the user. + completion_ty == expected_type && !expected_type.is_unit() + } else { + false + } +} - res.exact_type_match = Some(ty) == ctx.completion.expected_type.as_ref(); - res.exact_name_match = Some(name) == ctx.completion.expected_name.as_deref(); +fn compute_exact_name_match(ctx: &CompletionContext, completion_name: impl Into) -> bool { + let completion_name = completion_name.into(); - res + Some(&completion_name) == ctx.expected_name.as_ref() } -fn relevance_type_match(db: &dyn HirDatabase, ty: &Type, expected_type: &Type) -> bool { - ty == expected_type || ty.autoderef(db).any(|deref_ty| &deref_ty == expected_type) +fn compute_ref_match(ctx: &CompletionContext, completion_ty: &hir::Type) -> Option { + let mut ref_match = None; + if let Some(expected_type) = &ctx.expected_type { + if completion_ty != expected_type { + if let Some(expected_type_without_ref) = expected_type.remove_ref() { + if completion_ty == &expected_type_without_ref + || completion_ty + .autoderef(ctx.db) + .any(|deref_ty| deref_ty == expected_type_without_ref) + { + cov_mark::hit!(suggest_ref); + let mutability = if expected_type.is_mutable_reference() { + Mutability::Mut + } else { + Mutability::Shared + }; + ref_match = Some(mutability); + } + } + } + }; + ref_match } #[cfg(test)] @@ -477,6 +497,11 @@ fn main() { let _: m::Spam = S$0 } ), lookup: "Spam::Bar", detail: "(i32)", + relevance: CompletionRelevance { + exact_name_match: false, + exact_type_match: true, + is_local: false, + }, trigger_call_info: true, }, CompletionItem { @@ -498,6 +523,11 @@ fn main() { let _: m::Spam = S$0 } ), lookup: "Spam::Foo", detail: "()", + relevance: CompletionRelevance { + exact_name_match: false, + exact_type_match: true, + is_local: false, + }, }, CompletionItem { label: "main()", @@ -1169,4 +1199,86 @@ fn foo(bar: u32) { "#]], ); } + + #[test] + fn enum_owned() { + check_relevance( + r#" +enum Foo { A, B } +fn foo() { + bar($0); +} +fn bar(t: Foo) {} +"#, + expect![[r#" + ev Foo::A [type] + ev Foo::B [type] + en Foo [] + fn bar(…) [] + fn foo() [] + "#]], + ); + } + + #[test] + fn enum_ref() { + check_relevance( + r#" +enum Foo { A, B } +fn foo() { + bar($0); +} +fn bar(t: &Foo) {} +"#, + expect![[r#" + ev Foo::A [] + ev &Foo::A [type] + ev Foo::B [] + ev &Foo::B [type] + en Foo [] + fn bar(…) [] + fn foo() [] + "#]], + ); + } + + #[test] + fn suggest_deref_fn_ret() { + check_relevance( + r#" +#[lang = "deref"] +trait Deref { + type Target; + fn deref(&self) -> &Self::Target; +} + +struct S; +struct T(S); + +impl Deref for T { + type Target = S; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +fn foo(s: &S) {} +fn bar() -> T {} + +fn main() { + foo($0); +} + "#, + expect![[r#" + tt Deref [] + fn bar() [] + fn &bar() [type] + fn foo(…) [] + st T [] + st S [] + fn main() [] + "#]], + ) + } } diff --git a/crates/ide_completion/src/render/enum_variant.rs b/crates/ide_completion/src/render/enum_variant.rs index e8cfcc0c7..374247b05 100644 --- a/crates/ide_completion/src/render/enum_variant.rs +++ b/crates/ide_completion/src/render/enum_variant.rs @@ -6,7 +6,8 @@ use itertools::Itertools; use crate::{ item::{CompletionItem, CompletionKind, ImportEdit}, - render::{builder_ext::Params, RenderContext}, + render::{builder_ext::Params, compute_exact_type_match, compute_ref_match, RenderContext}, + CompletionRelevance, }; pub(crate) fn render_variant<'a>( @@ -74,6 +75,16 @@ impl<'a> EnumRender<'a> { item.lookup_by(self.short_qualified_name); } + let ty = self.variant.parent_enum(self.ctx.completion.db).ty(self.ctx.completion.db); + item.set_relevance(CompletionRelevance { + exact_type_match: compute_exact_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() } diff --git a/crates/ide_completion/src/render/function.rs b/crates/ide_completion/src/render/function.rs index 47e26a5d8..194ea135e 100644 --- a/crates/ide_completion/src/render/function.rs +++ b/crates/ide_completion/src/render/function.rs @@ -6,7 +6,10 @@ use syntax::ast::Fn; use crate::{ item::{CompletionItem, CompletionItemKind, CompletionKind, CompletionRelevance, ImportEdit}, - render::{builder_ext::Params, RenderContext}, + render::{ + builder_ext::Params, compute_exact_name_match, compute_exact_type_match, compute_ref_match, + RenderContext, + }, }; pub(crate) fn render_fn<'a>( @@ -52,23 +55,19 @@ impl<'a> FunctionRender<'a> { self.ctx.is_deprecated(self.func) || self.ctx.is_deprecated_assoc_item(self.func), ) .detail(self.detail()) - .add_call_parens(self.ctx.completion, self.name, params) + .add_call_parens(self.ctx.completion, self.name.clone(), params) .add_import(import_to_add); - let mut relevance = CompletionRelevance::default(); - if let Some(expected_type) = &self.ctx.completion.expected_type { - let ret_ty = self.func.ret_type(self.ctx.db()); + let ret_type = self.func.ret_type(self.ctx.db()); + item.set_relevance(CompletionRelevance { + exact_type_match: compute_exact_type_match(self.ctx.completion, &ret_type), + exact_name_match: compute_exact_name_match(self.ctx.completion, self.name.clone()), + ..CompletionRelevance::default() + }); - // We don't ever consider a function which returns unit type to be an - // exact type match, since nearly always this is not meaningful to the - // user. - relevance.exact_type_match = &ret_ty == expected_type && !ret_ty.is_unit(); + if let Some(ref_match) = compute_ref_match(self.ctx.completion, &ret_type) { + item.ref_match(ref_match); } - if let Some(expected_name) = &self.ctx.completion.expected_name { - relevance.exact_name_match = - expected_name == &self.func.name(self.ctx.db()).to_string(); - } - item.set_relevance(relevance); item.build() } -- cgit v1.2.3