diff options
Diffstat (limited to 'crates/ide_completion/src/render.rs')
-rw-r--r-- | crates/ide_completion/src/render.rs | 230 |
1 files changed, 187 insertions, 43 deletions
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs index 2514dda7c..4e4923e0d 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,38 @@ 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 | match ctx.expected_type.as_ref() { |
311 | Some(expected_type) => { | ||
312 | // We don't ever consider unit type to be an exact type match, since | ||
313 | // nearly always this is not meaningful to the user. | ||
314 | completion_ty == expected_type && !expected_type.is_unit() | ||
315 | } | ||
316 | None => false, | ||
317 | } | ||
318 | } | ||
318 | 319 | ||
319 | res.exact_type_match = Some(ty) == ctx.completion.expected_type.as_ref(); | 320 | 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(); | 321 | let completion_name = completion_name.into(); |
321 | 322 | ||
322 | res | 323 | Some(&completion_name) == ctx.expected_name.as_ref() |
323 | } | 324 | } |
324 | 325 | ||
325 | fn relevance_type_match(db: &dyn HirDatabase, ty: &Type, expected_type: &Type) -> bool { | 326 | 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) | 327 | let expected_type = ctx.expected_type.as_ref()?; |
328 | if completion_ty != expected_type { | ||
329 | let expected_type_without_ref = expected_type.remove_ref()?; | ||
330 | if completion_ty.autoderef(ctx.db).any(|deref_ty| deref_ty == expected_type_without_ref) { | ||
331 | cov_mark::hit!(suggest_ref); | ||
332 | let mutability = if expected_type.is_mutable_reference() { | ||
333 | Mutability::Mut | ||
334 | } else { | ||
335 | Mutability::Shared | ||
336 | }; | ||
337 | return Some(mutability); | ||
338 | }; | ||
339 | } | ||
340 | None | ||
327 | } | 341 | } |
328 | 342 | ||
329 | #[cfg(test)] | 343 | #[cfg(test)] |
@@ -432,6 +446,44 @@ fn main() { Foo::Fo$0 } | |||
432 | } | 446 | } |
433 | 447 | ||
434 | #[test] | 448 | #[test] |
449 | fn fn_detail_includes_args_and_return_type() { | ||
450 | check( | ||
451 | r#" | ||
452 | fn foo<T>(a: u32, b: u32, t: T) -> (u32, T) { (a, t) } | ||
453 | |||
454 | fn main() { fo$0 } | ||
455 | "#, | ||
456 | expect![[r#" | ||
457 | [ | ||
458 | CompletionItem { | ||
459 | label: "foo(…)", | ||
460 | source_range: 68..70, | ||
461 | delete: 68..70, | ||
462 | insert: "foo(${1:a}, ${2:b}, ${3:t})$0", | ||
463 | kind: SymbolKind( | ||
464 | Function, | ||
465 | ), | ||
466 | lookup: "foo", | ||
467 | detail: "fn(u32, u32, T) -> (u32, T)", | ||
468 | trigger_call_info: true, | ||
469 | }, | ||
470 | CompletionItem { | ||
471 | label: "main()", | ||
472 | source_range: 68..70, | ||
473 | delete: 68..70, | ||
474 | insert: "main()$0", | ||
475 | kind: SymbolKind( | ||
476 | Function, | ||
477 | ), | ||
478 | lookup: "main", | ||
479 | detail: "fn()", | ||
480 | }, | ||
481 | ] | ||
482 | "#]], | ||
483 | ); | ||
484 | } | ||
485 | |||
486 | #[test] | ||
435 | fn enum_detail_just_parentheses_for_unit() { | 487 | fn enum_detail_just_parentheses_for_unit() { |
436 | check( | 488 | check( |
437 | r#" | 489 | r#" |
@@ -477,6 +529,11 @@ fn main() { let _: m::Spam = S$0 } | |||
477 | ), | 529 | ), |
478 | lookup: "Spam::Bar", | 530 | lookup: "Spam::Bar", |
479 | detail: "(i32)", | 531 | detail: "(i32)", |
532 | relevance: CompletionRelevance { | ||
533 | exact_name_match: false, | ||
534 | exact_type_match: true, | ||
535 | is_local: false, | ||
536 | }, | ||
480 | trigger_call_info: true, | 537 | trigger_call_info: true, |
481 | }, | 538 | }, |
482 | CompletionItem { | 539 | CompletionItem { |
@@ -498,6 +555,11 @@ fn main() { let _: m::Spam = S$0 } | |||
498 | ), | 555 | ), |
499 | lookup: "Spam::Foo", | 556 | lookup: "Spam::Foo", |
500 | detail: "()", | 557 | detail: "()", |
558 | relevance: CompletionRelevance { | ||
559 | exact_name_match: false, | ||
560 | exact_type_match: true, | ||
561 | is_local: false, | ||
562 | }, | ||
501 | }, | 563 | }, |
502 | CompletionItem { | 564 | CompletionItem { |
503 | label: "main()", | 565 | label: "main()", |
@@ -508,7 +570,7 @@ fn main() { let _: m::Spam = S$0 } | |||
508 | Function, | 570 | Function, |
509 | ), | 571 | ), |
510 | lookup: "main", | 572 | lookup: "main", |
511 | detail: "-> ()", | 573 | detail: "fn()", |
512 | }, | 574 | }, |
513 | ] | 575 | ] |
514 | "#]], | 576 | "#]], |
@@ -537,7 +599,7 @@ fn main() { som$0 } | |||
537 | Function, | 599 | Function, |
538 | ), | 600 | ), |
539 | lookup: "main", | 601 | lookup: "main", |
540 | detail: "-> ()", | 602 | detail: "fn()", |
541 | }, | 603 | }, |
542 | CompletionItem { | 604 | CompletionItem { |
543 | label: "something_deprecated()", | 605 | label: "something_deprecated()", |
@@ -548,7 +610,7 @@ fn main() { som$0 } | |||
548 | Function, | 610 | Function, |
549 | ), | 611 | ), |
550 | lookup: "something_deprecated", | 612 | lookup: "something_deprecated", |
551 | detail: "-> ()", | 613 | detail: "fn()", |
552 | deprecated: true, | 614 | deprecated: true, |
553 | }, | 615 | }, |
554 | CompletionItem { | 616 | CompletionItem { |
@@ -560,7 +622,7 @@ fn main() { som$0 } | |||
560 | Function, | 622 | Function, |
561 | ), | 623 | ), |
562 | lookup: "something_else_deprecated", | 624 | lookup: "something_else_deprecated", |
563 | detail: "-> ()", | 625 | detail: "fn()", |
564 | deprecated: true, | 626 | deprecated: true, |
565 | }, | 627 | }, |
566 | ] | 628 | ] |
@@ -611,7 +673,7 @@ impl S { | |||
611 | insert: "bar()$0", | 673 | insert: "bar()$0", |
612 | kind: Method, | 674 | kind: Method, |
613 | lookup: "bar", | 675 | lookup: "bar", |
614 | detail: "-> ()", | 676 | detail: "fn(self)", |
615 | documentation: Documentation( | 677 | documentation: Documentation( |
616 | "Method docs", | 678 | "Method docs", |
617 | ), | 679 | ), |
@@ -711,7 +773,7 @@ fn foo(s: S) { s.$0 } | |||
711 | insert: "the_method()$0", | 773 | insert: "the_method()$0", |
712 | kind: Method, | 774 | kind: Method, |
713 | lookup: "the_method", | 775 | lookup: "the_method", |
714 | detail: "-> ()", | 776 | detail: "fn(&self)", |
715 | }, | 777 | }, |
716 | ] | 778 | ] |
717 | "#]], | 779 | "#]], |
@@ -1019,7 +1081,7 @@ fn main() { | |||
1019 | Function, | 1081 | Function, |
1020 | ), | 1082 | ), |
1021 | lookup: "foo", | 1083 | lookup: "foo", |
1022 | detail: "-> ()", | 1084 | detail: "fn(&mut S)", |
1023 | trigger_call_info: true, | 1085 | trigger_call_info: true, |
1024 | }, | 1086 | }, |
1025 | CompletionItem { | 1087 | CompletionItem { |
@@ -1031,7 +1093,7 @@ fn main() { | |||
1031 | Function, | 1093 | Function, |
1032 | ), | 1094 | ), |
1033 | lookup: "main", | 1095 | lookup: "main", |
1034 | detail: "-> ()", | 1096 | detail: "fn()", |
1035 | }, | 1097 | }, |
1036 | CompletionItem { | 1098 | CompletionItem { |
1037 | label: "s", | 1099 | label: "s", |
@@ -1169,4 +1231,86 @@ fn foo(bar: u32) { | |||
1169 | "#]], | 1231 | "#]], |
1170 | ); | 1232 | ); |
1171 | } | 1233 | } |
1234 | |||
1235 | #[test] | ||
1236 | fn enum_owned() { | ||
1237 | check_relevance( | ||
1238 | r#" | ||
1239 | enum Foo { A, B } | ||
1240 | fn foo() { | ||
1241 | bar($0); | ||
1242 | } | ||
1243 | fn bar(t: Foo) {} | ||
1244 | "#, | ||
1245 | expect![[r#" | ||
1246 | ev Foo::A [type] | ||
1247 | ev Foo::B [type] | ||
1248 | en Foo [] | ||
1249 | fn bar(…) [] | ||
1250 | fn foo() [] | ||
1251 | "#]], | ||
1252 | ); | ||
1253 | } | ||
1254 | |||
1255 | #[test] | ||
1256 | fn enum_ref() { | ||
1257 | check_relevance( | ||
1258 | r#" | ||
1259 | enum Foo { A, B } | ||
1260 | fn foo() { | ||
1261 | bar($0); | ||
1262 | } | ||
1263 | fn bar(t: &Foo) {} | ||
1264 | "#, | ||
1265 | expect![[r#" | ||
1266 | ev Foo::A [] | ||
1267 | ev &Foo::A [type] | ||
1268 | ev Foo::B [] | ||
1269 | ev &Foo::B [type] | ||
1270 | en Foo [] | ||
1271 | fn bar(…) [] | ||
1272 | fn foo() [] | ||
1273 | "#]], | ||
1274 | ); | ||
1275 | } | ||
1276 | |||
1277 | #[test] | ||
1278 | fn suggest_deref_fn_ret() { | ||
1279 | check_relevance( | ||
1280 | r#" | ||
1281 | #[lang = "deref"] | ||
1282 | trait Deref { | ||
1283 | type Target; | ||
1284 | fn deref(&self) -> &Self::Target; | ||
1285 | } | ||
1286 | |||
1287 | struct S; | ||
1288 | struct T(S); | ||
1289 | |||
1290 | impl Deref for T { | ||
1291 | type Target = S; | ||
1292 | |||
1293 | fn deref(&self) -> &Self::Target { | ||
1294 | &self.0 | ||
1295 | } | ||
1296 | } | ||
1297 | |||
1298 | fn foo(s: &S) {} | ||
1299 | fn bar() -> T {} | ||
1300 | |||
1301 | fn main() { | ||
1302 | foo($0); | ||
1303 | } | ||
1304 | "#, | ||
1305 | expect![[r#" | ||
1306 | tt Deref [] | ||
1307 | fn bar() [] | ||
1308 | fn &bar() [type] | ||
1309 | fn foo(…) [] | ||
1310 | st T [] | ||
1311 | st S [] | ||
1312 | fn main() [] | ||
1313 | "#]], | ||
1314 | ) | ||
1315 | } | ||
1172 | } | 1316 | } |