aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_completion
diff options
context:
space:
mode:
authorJosh Mcguigan <[email protected]>2021-03-16 02:26:59 +0000
committerJosh Mcguigan <[email protected]>2021-03-16 02:40:42 +0000
commit405bbb3aa46ecbfa5124969739695565d9841b9c (patch)
treed72f4691d4ebb199953fa2ffb52ed3a8b1501a73 /crates/ide_completion
parentc0a2b4e826e1da20d3cfa8c279fcdffa24f32a7d (diff)
completions: centralize calculation of relevance and ref matches
Diffstat (limited to 'crates/ide_completion')
-rw-r--r--crates/ide_completion/src/render.rs182
-rw-r--r--crates/ide_completion/src/render/enum_variant.rs13
-rw-r--r--crates/ide_completion/src/render/function.rs27
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;
10 10
11mod builder_ext; 11mod builder_ext;
12 12
13use base_db::Upcast;
14use hir::{ 13use hir::{
15 db::HirDatabase, AsAssocItem, Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability, 14 AsAssocItem, Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability, ScopeDef, Type,
16 ScopeDef, Type,
17}; 15};
18use ide_db::{ 16use ide_db::{
19 helpers::{item_name, SnippetCap}, 17 helpers::{item_name, SnippetCap},
@@ -22,8 +20,8 @@ use ide_db::{
22use syntax::TextRange; 20use syntax::TextRange;
23 21
24use crate::{ 22use crate::{
25 item::{CompletionRelevance, ImportEdit}, 23 item::ImportEdit, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind,
26 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, 24 CompletionRelevance,
27}; 25};
28 26
29use crate::render::{enum_variant::render_variant, function::render_fn, macro_::render_macro}; 27use crate::render::{enum_variant::render_variant, function::render_fn, macro_::render_macro};
@@ -144,7 +142,15 @@ impl<'a> Render<'a> {
144 .set_documentation(field.docs(self.ctx.db())) 142 .set_documentation(field.docs(self.ctx.db()))
145 .set_deprecated(is_deprecated); 143 .set_deprecated(is_deprecated);
146 144
147 item.set_relevance(compute_relevance(&self.ctx, &ty, &name.to_string())); 145 item.set_relevance(CompletionRelevance {
146 exact_type_match: compute_exact_type_match(self.ctx.completion, ty),
147 exact_name_match: compute_exact_name_match(self.ctx.completion, name.to_string()),
148 ..CompletionRelevance::default()
149 });
150
151 if let Some(ref_match) = compute_ref_match(self.ctx.completion, ty) {
152 item.ref_match(ref_match);
153 }
148 154
149 item.build() 155 item.build()
150 } 156 }
@@ -234,31 +240,18 @@ impl<'a> Render<'a> {
234 if !ty.is_unknown() { 240 if !ty.is_unknown() {
235 item.detail(ty.display(self.ctx.db()).to_string()); 241 item.detail(ty.display(self.ctx.db()).to_string());
236 } 242 }
237 };
238 243
239 if let ScopeDef::Local(local) = resolution { 244 item.set_relevance(CompletionRelevance {
240 let ty = local.ty(self.ctx.db()); 245 exact_type_match: compute_exact_type_match(self.ctx.completion, &ty),
246 exact_name_match: compute_exact_name_match(self.ctx.completion, local_name.clone()),
247 is_local: true,
248 ..CompletionRelevance::default()
249 });
241 250
242 let mut relevance = compute_relevance(&self.ctx, &ty, &local_name); 251 if let Some(ref_match) = compute_ref_match(self.ctx.completion, &ty) {
243 relevance.is_local = true; 252 item.ref_match(ref_match);
244 item.set_relevance(relevance);
245
246 if let Some(expected_type) = self.ctx.completion.expected_type.as_ref() {
247 if &ty != expected_type {
248 if let Some(ty_without_ref) = expected_type.remove_ref() {
249 if relevance_type_match(self.ctx.db().upcast(), &ty, &ty_without_ref) {
250 cov_mark::hit!(suggest_ref);
251 let mutability = if expected_type.is_mutable_reference() {
252 Mutability::Mut
253 } else {
254 Mutability::Shared
255 };
256 item.ref_match(mutability);
257 }
258 }
259 }
260 } 253 }
261 } 254 };
262 255
263 // Add `<>` for generic types 256 // Add `<>` for generic types
264 if self.ctx.completion.is_path_type 257 if self.ctx.completion.is_path_type
@@ -313,17 +306,44 @@ impl<'a> Render<'a> {
313 } 306 }
314} 307}
315 308
316fn compute_relevance(ctx: &RenderContext, ty: &Type, name: &str) -> CompletionRelevance { 309fn compute_exact_type_match(ctx: &CompletionContext, completion_ty: &hir::Type) -> bool {
317 let mut res = CompletionRelevance::default(); 310 if let Some(expected_type) = ctx.expected_type.as_ref() {
311 // We don't ever consider unit type to be an exact type match, since
312 // nearly always this is not meaningful to the user.
313 completion_ty == expected_type && !expected_type.is_unit()
314 } else {
315 false
316 }
317}
318 318
319 res.exact_type_match = Some(ty) == ctx.completion.expected_type.as_ref(); 319fn compute_exact_name_match(ctx: &CompletionContext, completion_name: impl Into<String>) -> bool {
320 res.exact_name_match = Some(name) == ctx.completion.expected_name.as_deref(); 320 let completion_name = completion_name.into();
321 321
322 res 322 Some(&completion_name) == ctx.expected_name.as_ref()
323} 323}
324 324
325fn relevance_type_match(db: &dyn HirDatabase, ty: &Type, expected_type: &Type) -> bool { 325fn compute_ref_match(ctx: &CompletionContext, completion_ty: &hir::Type) -> Option<Mutability> {
326 ty == expected_type || ty.autoderef(db).any(|deref_ty| &deref_ty == expected_type) 326 let mut ref_match = None;
327 if let Some(expected_type) = &ctx.expected_type {
328 if completion_ty != expected_type {
329 if let Some(expected_type_without_ref) = expected_type.remove_ref() {
330 if completion_ty == &expected_type_without_ref
331 || completion_ty
332 .autoderef(ctx.db)
333 .any(|deref_ty| deref_ty == expected_type_without_ref)
334 {
335 cov_mark::hit!(suggest_ref);
336 let mutability = if expected_type.is_mutable_reference() {
337 Mutability::Mut
338 } else {
339 Mutability::Shared
340 };
341 ref_match = Some(mutability);
342 }
343 }
344 }
345 };
346 ref_match
327} 347}
328 348
329#[cfg(test)] 349#[cfg(test)]
@@ -477,6 +497,11 @@ fn main() { let _: m::Spam = S$0 }
477 ), 497 ),
478 lookup: "Spam::Bar", 498 lookup: "Spam::Bar",
479 detail: "(i32)", 499 detail: "(i32)",
500 relevance: CompletionRelevance {
501 exact_name_match: false,
502 exact_type_match: true,
503 is_local: false,
504 },
480 trigger_call_info: true, 505 trigger_call_info: true,
481 }, 506 },
482 CompletionItem { 507 CompletionItem {
@@ -498,6 +523,11 @@ fn main() { let _: m::Spam = S$0 }
498 ), 523 ),
499 lookup: "Spam::Foo", 524 lookup: "Spam::Foo",
500 detail: "()", 525 detail: "()",
526 relevance: CompletionRelevance {
527 exact_name_match: false,
528 exact_type_match: true,
529 is_local: false,
530 },
501 }, 531 },
502 CompletionItem { 532 CompletionItem {
503 label: "main()", 533 label: "main()",
@@ -1169,4 +1199,86 @@ fn foo(bar: u32) {
1169 "#]], 1199 "#]],
1170 ); 1200 );
1171 } 1201 }
1202
1203 #[test]
1204 fn enum_owned() {
1205 check_relevance(
1206 r#"
1207enum Foo { A, B }
1208fn foo() {
1209 bar($0);
1210}
1211fn bar(t: Foo) {}
1212"#,
1213 expect![[r#"
1214 ev Foo::A [type]
1215 ev Foo::B [type]
1216 en Foo []
1217 fn bar(…) []
1218 fn foo() []
1219 "#]],
1220 );
1221 }
1222
1223 #[test]
1224 fn enum_ref() {
1225 check_relevance(
1226 r#"
1227enum Foo { A, B }
1228fn foo() {
1229 bar($0);
1230}
1231fn bar(t: &Foo) {}
1232"#,
1233 expect![[r#"
1234 ev Foo::A []
1235 ev &Foo::A [type]
1236 ev Foo::B []
1237 ev &Foo::B [type]
1238 en Foo []
1239 fn bar(…) []
1240 fn foo() []
1241 "#]],
1242 );
1243 }
1244
1245 #[test]
1246 fn suggest_deref_fn_ret() {
1247 check_relevance(
1248 r#"
1249#[lang = "deref"]
1250trait Deref {
1251 type Target;
1252 fn deref(&self) -> &Self::Target;
1253}
1254
1255struct S;
1256struct T(S);
1257
1258impl Deref for T {
1259 type Target = S;
1260
1261 fn deref(&self) -> &Self::Target {
1262 &self.0
1263 }
1264}
1265
1266fn foo(s: &S) {}
1267fn bar() -> T {}
1268
1269fn main() {
1270 foo($0);
1271}
1272 "#,
1273 expect![[r#"
1274 tt Deref []
1275 fn bar() []
1276 fn &bar() [type]
1277 fn foo(…) []
1278 st T []
1279 st S []
1280 fn main() []
1281 "#]],
1282 )
1283 }
1172} 1284}
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;
6 6
7use crate::{ 7use crate::{
8 item::{CompletionItem, CompletionKind, ImportEdit}, 8 item::{CompletionItem, CompletionKind, ImportEdit},
9 render::{builder_ext::Params, RenderContext}, 9 render::{builder_ext::Params, compute_exact_type_match, compute_ref_match, RenderContext},
10 CompletionRelevance,
10}; 11};
11 12
12pub(crate) fn render_variant<'a>( 13pub(crate) fn render_variant<'a>(
@@ -74,6 +75,16 @@ impl<'a> EnumRender<'a> {
74 item.lookup_by(self.short_qualified_name); 75 item.lookup_by(self.short_qualified_name);
75 } 76 }
76 77
78 let ty = self.variant.parent_enum(self.ctx.completion.db).ty(self.ctx.completion.db);
79 item.set_relevance(CompletionRelevance {
80 exact_type_match: compute_exact_type_match(self.ctx.completion, &ty),
81 ..CompletionRelevance::default()
82 });
83
84 if let Some(ref_match) = compute_ref_match(self.ctx.completion, &ty) {
85 item.ref_match(ref_match);
86 }
87
77 item.build() 88 item.build()
78 } 89 }
79 90
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;
6 6
7use crate::{ 7use crate::{
8 item::{CompletionItem, CompletionItemKind, CompletionKind, CompletionRelevance, ImportEdit}, 8 item::{CompletionItem, CompletionItemKind, CompletionKind, CompletionRelevance, ImportEdit},
9 render::{builder_ext::Params, RenderContext}, 9 render::{
10 builder_ext::Params, compute_exact_name_match, compute_exact_type_match, compute_ref_match,
11 RenderContext,
12 },
10}; 13};
11 14
12pub(crate) fn render_fn<'a>( 15pub(crate) fn render_fn<'a>(
@@ -52,23 +55,19 @@ impl<'a> FunctionRender<'a> {
52 self.ctx.is_deprecated(self.func) || self.ctx.is_deprecated_assoc_item(self.func), 55 self.ctx.is_deprecated(self.func) || self.ctx.is_deprecated_assoc_item(self.func),
53 ) 56 )
54 .detail(self.detail()) 57 .detail(self.detail())
55 .add_call_parens(self.ctx.completion, self.name, params) 58 .add_call_parens(self.ctx.completion, self.name.clone(), params)
56 .add_import(import_to_add); 59 .add_import(import_to_add);
57 60
58 let mut relevance = CompletionRelevance::default(); 61 let ret_type = self.func.ret_type(self.ctx.db());
59 if let Some(expected_type) = &self.ctx.completion.expected_type { 62 item.set_relevance(CompletionRelevance {
60 let ret_ty = self.func.ret_type(self.ctx.db()); 63 exact_type_match: compute_exact_type_match(self.ctx.completion, &ret_type),
64 exact_name_match: compute_exact_name_match(self.ctx.completion, self.name.clone()),
65 ..CompletionRelevance::default()
66 });
61 67
62 // We don't ever consider a function which returns unit type to be an 68 if let Some(ref_match) = compute_ref_match(self.ctx.completion, &ret_type) {
63 // exact type match, since nearly always this is not meaningful to the 69 item.ref_match(ref_match);
64 // user.
65 relevance.exact_type_match = &ret_ty == expected_type && !ret_ty.is_unit();
66 } 70 }
67 if let Some(expected_name) = &self.ctx.completion.expected_name {
68 relevance.exact_name_match =
69 expected_name == &self.func.name(self.ctx.db()).to_string();
70 }
71 item.set_relevance(relevance);
72 71
73 item.build() 72 item.build()
74 } 73 }