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 +++++++++++++++++++++++++++++------- 1 file changed, 147 insertions(+), 35 deletions(-) (limited to 'crates/ide_completion/src/render.rs') 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() [] + "#]], + ) + } } -- cgit v1.2.3