diff options
Diffstat (limited to 'crates/ide_completion/src/render.rs')
-rw-r--r-- | crates/ide_completion/src/render.rs | 236 |
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 | ||
11 | mod builder_ext; | 11 | mod builder_ext; |
12 | 12 | ||
13 | use base_db::Upcast; | ||
14 | use hir::{ | 13 | use hir::{ |
15 | db::HirDatabase, AsAssocItem, Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability, | 14 | AsAssocItem, Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability, ScopeDef, Type, |
16 | ScopeDef, Type, | ||
17 | }; | 15 | }; |
18 | use ide_db::{ | 16 | use ide_db::{ |
19 | helpers::{item_name, SnippetCap}, | 17 | helpers::{item_name, SnippetCap}, |
@@ -22,8 +20,8 @@ use ide_db::{ | |||
22 | use syntax::TextRange; | 20 | use syntax::TextRange; |
23 | 21 | ||
24 | use crate::{ | 22 | use crate::{ |
25 | item::{CompletionRelevance, ImportEdit}, | 23 | item::ImportEdit, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, |
26 | CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, | 24 | CompletionRelevance, |
27 | }; | 25 | }; |
28 | 26 | ||
29 | use crate::render::{enum_variant::render_variant, function::render_fn, macro_::render_macro}; | 27 | use 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 | ||
316 | fn compute_relevance(ctx: &RenderContext, ty: &Type, name: &str) -> CompletionRelevance { | 309 | fn 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(); | 319 | fn 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 | ||
325 | fn relevance_type_match(db: &dyn HirDatabase, ty: &Type, expected_type: &Type) -> bool { | 325 | fn 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#" | ||
458 | fn foo<T>(a: u32, b: u32, t: T) -> (u32, T) { (a, t) } | ||
459 | |||
460 | fn 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#" | ||
1245 | enum Foo { A, B } | ||
1246 | fn foo() { | ||
1247 | bar($0); | ||
1248 | } | ||
1249 | fn 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#" | ||
1265 | enum Foo { A, B } | ||
1266 | fn foo() { | ||
1267 | bar($0); | ||
1268 | } | ||
1269 | fn 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"] | ||
1288 | trait Deref { | ||
1289 | type Target; | ||
1290 | fn deref(&self) -> &Self::Target; | ||
1291 | } | ||
1292 | |||
1293 | struct S; | ||
1294 | struct T(S); | ||
1295 | |||
1296 | impl Deref for T { | ||
1297 | type Target = S; | ||
1298 | |||
1299 | fn deref(&self) -> &Self::Target { | ||
1300 | &self.0 | ||
1301 | } | ||
1302 | } | ||
1303 | |||
1304 | fn foo(s: &S) {} | ||
1305 | fn bar() -> T {} | ||
1306 | |||
1307 | fn 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 | } |