aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_completion/src/render.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_completion/src/render.rs')
-rw-r--r--crates/ide_completion/src/render.rs236
1 files changed, 193 insertions, 43 deletions
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs
index 2514dda7c..36655667c 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)]
@@ -432,6 +452,44 @@ fn main() { Foo::Fo$0 }
432 } 452 }
433 453
434 #[test] 454 #[test]
455 fn fn_detail_includes_args_and_return_type() {
456 check(
457 r#"
458fn foo<T>(a: u32, b: u32, t: T) -> (u32, T) { (a, t) }
459
460fn main() { fo$0 }
461"#,
462 expect![[r#"
463 [
464 CompletionItem {
465 label: "foo(…)",
466 source_range: 68..70,
467 delete: 68..70,
468 insert: "foo(${1:a}, ${2:b}, ${3:t})$0",
469 kind: SymbolKind(
470 Function,
471 ),
472 lookup: "foo",
473 detail: "fn(u32, u32, T) -> (u32, T)",
474 trigger_call_info: true,
475 },
476 CompletionItem {
477 label: "main()",
478 source_range: 68..70,
479 delete: 68..70,
480 insert: "main()$0",
481 kind: SymbolKind(
482 Function,
483 ),
484 lookup: "main",
485 detail: "fn()",
486 },
487 ]
488 "#]],
489 );
490 }
491
492 #[test]
435 fn enum_detail_just_parentheses_for_unit() { 493 fn enum_detail_just_parentheses_for_unit() {
436 check( 494 check(
437 r#" 495 r#"
@@ -477,6 +535,11 @@ fn main() { let _: m::Spam = S$0 }
477 ), 535 ),
478 lookup: "Spam::Bar", 536 lookup: "Spam::Bar",
479 detail: "(i32)", 537 detail: "(i32)",
538 relevance: CompletionRelevance {
539 exact_name_match: false,
540 exact_type_match: true,
541 is_local: false,
542 },
480 trigger_call_info: true, 543 trigger_call_info: true,
481 }, 544 },
482 CompletionItem { 545 CompletionItem {
@@ -498,6 +561,11 @@ fn main() { let _: m::Spam = S$0 }
498 ), 561 ),
499 lookup: "Spam::Foo", 562 lookup: "Spam::Foo",
500 detail: "()", 563 detail: "()",
564 relevance: CompletionRelevance {
565 exact_name_match: false,
566 exact_type_match: true,
567 is_local: false,
568 },
501 }, 569 },
502 CompletionItem { 570 CompletionItem {
503 label: "main()", 571 label: "main()",
@@ -508,7 +576,7 @@ fn main() { let _: m::Spam = S$0 }
508 Function, 576 Function,
509 ), 577 ),
510 lookup: "main", 578 lookup: "main",
511 detail: "-> ()", 579 detail: "fn()",
512 }, 580 },
513 ] 581 ]
514 "#]], 582 "#]],
@@ -537,7 +605,7 @@ fn main() { som$0 }
537 Function, 605 Function,
538 ), 606 ),
539 lookup: "main", 607 lookup: "main",
540 detail: "-> ()", 608 detail: "fn()",
541 }, 609 },
542 CompletionItem { 610 CompletionItem {
543 label: "something_deprecated()", 611 label: "something_deprecated()",
@@ -548,7 +616,7 @@ fn main() { som$0 }
548 Function, 616 Function,
549 ), 617 ),
550 lookup: "something_deprecated", 618 lookup: "something_deprecated",
551 detail: "-> ()", 619 detail: "fn()",
552 deprecated: true, 620 deprecated: true,
553 }, 621 },
554 CompletionItem { 622 CompletionItem {
@@ -560,7 +628,7 @@ fn main() { som$0 }
560 Function, 628 Function,
561 ), 629 ),
562 lookup: "something_else_deprecated", 630 lookup: "something_else_deprecated",
563 detail: "-> ()", 631 detail: "fn()",
564 deprecated: true, 632 deprecated: true,
565 }, 633 },
566 ] 634 ]
@@ -611,7 +679,7 @@ impl S {
611 insert: "bar()$0", 679 insert: "bar()$0",
612 kind: Method, 680 kind: Method,
613 lookup: "bar", 681 lookup: "bar",
614 detail: "-> ()", 682 detail: "fn(self)",
615 documentation: Documentation( 683 documentation: Documentation(
616 "Method docs", 684 "Method docs",
617 ), 685 ),
@@ -711,7 +779,7 @@ fn foo(s: S) { s.$0 }
711 insert: "the_method()$0", 779 insert: "the_method()$0",
712 kind: Method, 780 kind: Method,
713 lookup: "the_method", 781 lookup: "the_method",
714 detail: "-> ()", 782 detail: "fn(&self)",
715 }, 783 },
716 ] 784 ]
717 "#]], 785 "#]],
@@ -1019,7 +1087,7 @@ fn main() {
1019 Function, 1087 Function,
1020 ), 1088 ),
1021 lookup: "foo", 1089 lookup: "foo",
1022 detail: "-> ()", 1090 detail: "fn(&mut S)",
1023 trigger_call_info: true, 1091 trigger_call_info: true,
1024 }, 1092 },
1025 CompletionItem { 1093 CompletionItem {
@@ -1031,7 +1099,7 @@ fn main() {
1031 Function, 1099 Function,
1032 ), 1100 ),
1033 lookup: "main", 1101 lookup: "main",
1034 detail: "-> ()", 1102 detail: "fn()",
1035 }, 1103 },
1036 CompletionItem { 1104 CompletionItem {
1037 label: "s", 1105 label: "s",
@@ -1169,4 +1237,86 @@ fn foo(bar: u32) {
1169 "#]], 1237 "#]],
1170 ); 1238 );
1171 } 1239 }
1240
1241 #[test]
1242 fn enum_owned() {
1243 check_relevance(
1244 r#"
1245enum Foo { A, B }
1246fn foo() {
1247 bar($0);
1248}
1249fn bar(t: Foo) {}
1250"#,
1251 expect![[r#"
1252 ev Foo::A [type]
1253 ev Foo::B [type]
1254 en Foo []
1255 fn bar(…) []
1256 fn foo() []
1257 "#]],
1258 );
1259 }
1260
1261 #[test]
1262 fn enum_ref() {
1263 check_relevance(
1264 r#"
1265enum Foo { A, B }
1266fn foo() {
1267 bar($0);
1268}
1269fn bar(t: &Foo) {}
1270"#,
1271 expect![[r#"
1272 ev Foo::A []
1273 ev &Foo::A [type]
1274 ev Foo::B []
1275 ev &Foo::B [type]
1276 en Foo []
1277 fn bar(…) []
1278 fn foo() []
1279 "#]],
1280 );
1281 }
1282
1283 #[test]
1284 fn suggest_deref_fn_ret() {
1285 check_relevance(
1286 r#"
1287#[lang = "deref"]
1288trait Deref {
1289 type Target;
1290 fn deref(&self) -> &Self::Target;
1291}
1292
1293struct S;
1294struct T(S);
1295
1296impl Deref for T {
1297 type Target = S;
1298
1299 fn deref(&self) -> &Self::Target {
1300 &self.0
1301 }
1302}
1303
1304fn foo(s: &S) {}
1305fn bar() -> T {}
1306
1307fn main() {
1308 foo($0);
1309}
1310 "#,
1311 expect![[r#"
1312 tt Deref []
1313 fn bar() []
1314 fn &bar() [type]
1315 fn foo(…) []
1316 st T []
1317 st S []
1318 fn main() []
1319 "#]],
1320 )
1321 }
1172} 1322}