diff options
Diffstat (limited to 'crates/ide_completion')
-rw-r--r-- | crates/ide_completion/src/completions/postfix/format_like.rs | 2 | ||||
-rw-r--r-- | crates/ide_completion/src/item.rs | 37 | ||||
-rw-r--r-- | crates/ide_completion/src/render.rs | 223 |
3 files changed, 217 insertions, 45 deletions
diff --git a/crates/ide_completion/src/completions/postfix/format_like.rs b/crates/ide_completion/src/completions/postfix/format_like.rs index 3afc63021..cee4eec10 100644 --- a/crates/ide_completion/src/completions/postfix/format_like.rs +++ b/crates/ide_completion/src/completions/postfix/format_like.rs | |||
@@ -59,7 +59,7 @@ pub(crate) fn add_format_like_completions( | |||
59 | /// Checks whether provided item is a string literal. | 59 | /// Checks whether provided item is a string literal. |
60 | fn string_literal_contents(item: &ast::String) -> Option<String> { | 60 | fn string_literal_contents(item: &ast::String) -> Option<String> { |
61 | let item = item.text(); | 61 | let item = item.text(); |
62 | if item.len() >= 2 && item.starts_with("\"") && item.ends_with("\"") { | 62 | if item.len() >= 2 && item.starts_with('\"') && item.ends_with('\"') { |
63 | return Some(item[1..item.len() - 1].to_owned()); | 63 | return Some(item[1..item.len() - 1].to_owned()); |
64 | } | 64 | } |
65 | 65 | ||
diff --git a/crates/ide_completion/src/item.rs b/crates/ide_completion/src/item.rs index 3febab32b..9a4b5217a 100644 --- a/crates/ide_completion/src/item.rs +++ b/crates/ide_completion/src/item.rs | |||
@@ -144,6 +144,21 @@ pub struct CompletionRelevance { | |||
144 | /// } | 144 | /// } |
145 | /// ``` | 145 | /// ``` |
146 | pub exact_type_match: bool, | 146 | pub exact_type_match: bool, |
147 | /// This is set in cases like these: | ||
148 | /// | ||
149 | /// ``` | ||
150 | /// fn foo(bar: u32) { | ||
151 | /// $0 // `bar` is local | ||
152 | /// } | ||
153 | /// ``` | ||
154 | /// | ||
155 | /// ``` | ||
156 | /// fn foo() { | ||
157 | /// let bar = 0; | ||
158 | /// $0 // `bar` is local | ||
159 | /// } | ||
160 | /// ``` | ||
161 | pub is_local: bool, | ||
147 | } | 162 | } |
148 | 163 | ||
149 | impl CompletionRelevance { | 164 | impl CompletionRelevance { |
@@ -163,6 +178,9 @@ impl CompletionRelevance { | |||
163 | score += 1; | 178 | score += 1; |
164 | } | 179 | } |
165 | if self.exact_type_match { | 180 | if self.exact_type_match { |
181 | score += 3; | ||
182 | } | ||
183 | if self.is_local { | ||
166 | score += 1; | 184 | score += 1; |
167 | } | 185 | } |
168 | 186 | ||
@@ -551,9 +569,24 @@ mod tests { | |||
551 | vec![CompletionRelevance::default()], | 569 | vec![CompletionRelevance::default()], |
552 | vec![ | 570 | vec![ |
553 | CompletionRelevance { exact_name_match: true, ..CompletionRelevance::default() }, | 571 | CompletionRelevance { exact_name_match: true, ..CompletionRelevance::default() }, |
554 | CompletionRelevance { exact_type_match: true, ..CompletionRelevance::default() }, | 572 | CompletionRelevance { is_local: true, ..CompletionRelevance::default() }, |
555 | ], | 573 | ], |
556 | vec![CompletionRelevance { exact_name_match: true, exact_type_match: true }], | 574 | vec![CompletionRelevance { |
575 | exact_name_match: true, | ||
576 | is_local: true, | ||
577 | ..CompletionRelevance::default() | ||
578 | }], | ||
579 | vec![CompletionRelevance { exact_type_match: true, ..CompletionRelevance::default() }], | ||
580 | vec![CompletionRelevance { | ||
581 | exact_name_match: true, | ||
582 | exact_type_match: true, | ||
583 | ..CompletionRelevance::default() | ||
584 | }], | ||
585 | vec![CompletionRelevance { | ||
586 | exact_name_match: true, | ||
587 | exact_type_match: true, | ||
588 | is_local: true, | ||
589 | }], | ||
557 | ]; | 590 | ]; |
558 | 591 | ||
559 | check_relevance_score_ordered(expected_relevance_order); | 592 | check_relevance_score_ordered(expected_relevance_order); |
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs index db31896e5..905f0b197 100644 --- a/crates/ide_completion/src/render.rs +++ b/crates/ide_completion/src/render.rs | |||
@@ -10,8 +10,10 @@ pub(crate) mod type_alias; | |||
10 | 10 | ||
11 | mod builder_ext; | 11 | mod builder_ext; |
12 | 12 | ||
13 | use base_db::Upcast; | ||
13 | use hir::{ | 14 | use hir::{ |
14 | AsAssocItem, Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability, ScopeDef, Type, | 15 | db::HirDatabase, AsAssocItem, Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability, |
16 | ScopeDef, Type, | ||
15 | }; | 17 | }; |
16 | use ide_db::{ | 18 | use ide_db::{ |
17 | helpers::{item_name, SnippetCap}, | 19 | helpers::{item_name, SnippetCap}, |
@@ -155,9 +157,7 @@ impl<'a> Render<'a> { | |||
155 | .set_documentation(field.docs(self.ctx.db())) | 157 | .set_documentation(field.docs(self.ctx.db())) |
156 | .set_deprecated(is_deprecated); | 158 | .set_deprecated(is_deprecated); |
157 | 159 | ||
158 | if let Some(relevance) = compute_relevance(&self.ctx, &ty, &name.to_string()) { | 160 | item.set_relevance(compute_relevance(&self.ctx, &ty, &name.to_string())); |
159 | item.set_relevance(relevance); | ||
160 | } | ||
161 | 161 | ||
162 | item.build() | 162 | item.build() |
163 | } | 163 | } |
@@ -251,19 +251,23 @@ impl<'a> Render<'a> { | |||
251 | 251 | ||
252 | if let ScopeDef::Local(local) = resolution { | 252 | if let ScopeDef::Local(local) = resolution { |
253 | let ty = local.ty(self.ctx.db()); | 253 | let ty = local.ty(self.ctx.db()); |
254 | if let Some(relevance) = compute_relevance(&self.ctx, &ty, &local_name) { | 254 | |
255 | item.set_relevance(relevance); | 255 | let mut relevance = compute_relevance(&self.ctx, &ty, &local_name); |
256 | } | 256 | relevance.is_local = true; |
257 | item.set_relevance(relevance); | ||
258 | |||
257 | if let Some((_expected_name, expected_type)) = self.ctx.expected_name_and_type() { | 259 | if let Some((_expected_name, expected_type)) = self.ctx.expected_name_and_type() { |
258 | if let Some(ty_without_ref) = expected_type.remove_ref() { | 260 | if ty != expected_type { |
259 | if ty_without_ref == ty { | 261 | if let Some(ty_without_ref) = expected_type.remove_ref() { |
260 | cov_mark::hit!(suggest_ref); | 262 | if relevance_type_match(self.ctx.db().upcast(), &ty, &ty_without_ref) { |
261 | let mutability = if expected_type.is_mutable_reference() { | 263 | cov_mark::hit!(suggest_ref); |
262 | Mutability::Mut | 264 | let mutability = if expected_type.is_mutable_reference() { |
263 | } else { | 265 | Mutability::Mut |
264 | Mutability::Shared | 266 | } else { |
265 | }; | 267 | Mutability::Shared |
266 | item.ref_match(mutability); | 268 | }; |
269 | item.ref_match(mutability); | ||
270 | } | ||
267 | } | 271 | } |
268 | } | 272 | } |
269 | } | 273 | } |
@@ -322,19 +326,25 @@ impl<'a> Render<'a> { | |||
322 | } | 326 | } |
323 | } | 327 | } |
324 | 328 | ||
325 | fn compute_relevance(ctx: &RenderContext, ty: &Type, name: &str) -> Option<CompletionRelevance> { | 329 | fn compute_relevance(ctx: &RenderContext, ty: &Type, name: &str) -> CompletionRelevance { |
326 | let (expected_name, expected_type) = ctx.expected_name_and_type()?; | ||
327 | let mut res = CompletionRelevance::default(); | 330 | let mut res = CompletionRelevance::default(); |
328 | res.exact_type_match = ty == &expected_type; | 331 | |
329 | res.exact_name_match = name == &expected_name; | 332 | if let Some((expected_name, expected_type)) = ctx.expected_name_and_type() { |
330 | Some(res) | 333 | res.exact_type_match = ty == &expected_type; |
334 | res.exact_name_match = name == &expected_name; | ||
335 | } | ||
336 | |||
337 | res | ||
338 | } | ||
339 | |||
340 | fn relevance_type_match(db: &dyn HirDatabase, ty: &Type, expected_type: &Type) -> bool { | ||
341 | ty == expected_type || ty.autoderef(db).any(|deref_ty| &deref_ty == expected_type) | ||
331 | } | 342 | } |
332 | 343 | ||
333 | #[cfg(test)] | 344 | #[cfg(test)] |
334 | mod tests { | 345 | mod tests { |
335 | use std::cmp::Reverse; | ||
336 | |||
337 | use expect_test::{expect, Expect}; | 346 | use expect_test::{expect, Expect}; |
347 | use itertools::Itertools; | ||
338 | 348 | ||
339 | use crate::{ | 349 | use crate::{ |
340 | test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG}, | 350 | test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG}, |
@@ -347,28 +357,40 @@ mod tests { | |||
347 | } | 357 | } |
348 | 358 | ||
349 | fn check_relevance(ra_fixture: &str, expect: Expect) { | 359 | fn check_relevance(ra_fixture: &str, expect: Expect) { |
350 | fn display_relevance(relevance: CompletionRelevance) -> &'static str { | 360 | fn display_relevance(relevance: CompletionRelevance) -> String { |
351 | match relevance { | 361 | let relevance_factors = vec![ |
352 | CompletionRelevance { exact_type_match: true, exact_name_match: true } => { | 362 | (relevance.exact_type_match, "type"), |
353 | "[type+name]" | 363 | (relevance.exact_name_match, "name"), |
354 | } | 364 | (relevance.is_local, "local"), |
355 | CompletionRelevance { exact_type_match: true, exact_name_match: false } => "[type]", | 365 | ] |
356 | CompletionRelevance { exact_type_match: false, exact_name_match: true } => "[name]", | 366 | .into_iter() |
357 | CompletionRelevance { exact_type_match: false, exact_name_match: false } => "[]", | 367 | .filter_map(|(cond, desc)| if cond { Some(desc) } else { None }) |
358 | } | 368 | .join("+"); |
369 | |||
370 | format!("[{}]", relevance_factors) | ||
359 | } | 371 | } |
360 | 372 | ||
361 | let mut completions = get_all_items(TEST_CONFIG, ra_fixture); | 373 | let actual = get_all_items(TEST_CONFIG, ra_fixture) |
362 | completions.sort_by_key(|it| (Reverse(it.relevance()), it.label().to_string())); | ||
363 | let actual = completions | ||
364 | .into_iter() | 374 | .into_iter() |
365 | .filter(|it| it.completion_kind == CompletionKind::Reference) | 375 | .filter(|it| it.completion_kind == CompletionKind::Reference) |
366 | .map(|it| { | 376 | .flat_map(|it| { |
377 | let mut items = vec![]; | ||
378 | |||
367 | let tag = it.kind().unwrap().tag(); | 379 | let tag = it.kind().unwrap().tag(); |
368 | let relevance = display_relevance(it.relevance()); | 380 | let relevance = display_relevance(it.relevance()); |
369 | format!("{} {} {}\n", tag, it.label(), relevance) | 381 | items.push(format!("{} {} {}\n", tag, it.label(), relevance)); |
382 | |||
383 | if let Some((mutability, relevance)) = it.ref_match() { | ||
384 | let label = format!("&{}{}", mutability.as_keyword_for_ref(), it.label()); | ||
385 | let relevance = display_relevance(relevance); | ||
386 | |||
387 | items.push(format!("{} {} {}\n", tag, label, relevance)); | ||
388 | } | ||
389 | |||
390 | items | ||
370 | }) | 391 | }) |
371 | .collect::<String>(); | 392 | .collect::<String>(); |
393 | |||
372 | expect.assert_eq(&actual); | 394 | expect.assert_eq(&actual); |
373 | } | 395 | } |
374 | 396 | ||
@@ -838,9 +860,9 @@ fn test(bar: u32) { } | |||
838 | fn foo(s: S) { test(s.$0) } | 860 | fn foo(s: S) { test(s.$0) } |
839 | "#, | 861 | "#, |
840 | expect![[r#" | 862 | expect![[r#" |
863 | fd foo [] | ||
841 | fd bar [type+name] | 864 | fd bar [type+name] |
842 | fd baz [type] | 865 | fd baz [type] |
843 | fd foo [] | ||
844 | "#]], | 866 | "#]], |
845 | ); | 867 | ); |
846 | } | 868 | } |
@@ -855,9 +877,9 @@ struct B { x: (), y: f32, bar: u32 } | |||
855 | fn foo(a: A) { B { bar: a.$0 }; } | 877 | fn foo(a: A) { B { bar: a.$0 }; } |
856 | "#, | 878 | "#, |
857 | expect![[r#" | 879 | expect![[r#" |
880 | fd foo [] | ||
858 | fd bar [type+name] | 881 | fd bar [type+name] |
859 | fd baz [type] | 882 | fd baz [type] |
860 | fd foo [] | ||
861 | "#]], | 883 | "#]], |
862 | ) | 884 | ) |
863 | } | 885 | } |
@@ -885,9 +907,9 @@ fn f(foo: i64) { } | |||
885 | fn foo(a: A) { f(B { bar: a.$0 }); } | 907 | fn foo(a: A) { f(B { bar: a.$0 }); } |
886 | "#, | 908 | "#, |
887 | expect![[r#" | 909 | expect![[r#" |
910 | fd foo [] | ||
888 | fd bar [type+name] | 911 | fd bar [type+name] |
889 | fd baz [type] | 912 | fd baz [type] |
890 | fd foo [] | ||
891 | "#]], | 913 | "#]], |
892 | ); | 914 | ); |
893 | } | 915 | } |
@@ -900,7 +922,7 @@ struct WorldSnapshot { _f: () }; | |||
900 | fn go(world: &WorldSnapshot) { go(w$0) } | 922 | fn go(world: &WorldSnapshot) { go(w$0) } |
901 | "#, | 923 | "#, |
902 | expect![[r#" | 924 | expect![[r#" |
903 | lc world [type+name] | 925 | lc world [type+name+local] |
904 | st WorldSnapshot [] | 926 | st WorldSnapshot [] |
905 | fn go(…) [] | 927 | fn go(…) [] |
906 | "#]], | 928 | "#]], |
@@ -915,9 +937,9 @@ struct Foo; | |||
915 | fn f(foo: &Foo) { f(foo, w$0) } | 937 | fn f(foo: &Foo) { f(foo, w$0) } |
916 | "#, | 938 | "#, |
917 | expect![[r#" | 939 | expect![[r#" |
940 | lc foo [local] | ||
918 | st Foo [] | 941 | st Foo [] |
919 | fn f(…) [] | 942 | fn f(…) [] |
920 | lc foo [] | ||
921 | "#]], | 943 | "#]], |
922 | ); | 944 | ); |
923 | } | 945 | } |
@@ -980,6 +1002,7 @@ fn main() { | |||
980 | relevance: CompletionRelevance { | 1002 | relevance: CompletionRelevance { |
981 | exact_name_match: true, | 1003 | exact_name_match: true, |
982 | exact_type_match: false, | 1004 | exact_type_match: false, |
1005 | is_local: true, | ||
983 | }, | 1006 | }, |
984 | ref_match: "&mut ", | 1007 | ref_match: "&mut ", |
985 | }, | 1008 | }, |
@@ -987,4 +1010,120 @@ fn main() { | |||
987 | "#]], | 1010 | "#]], |
988 | ) | 1011 | ) |
989 | } | 1012 | } |
1013 | |||
1014 | #[test] | ||
1015 | fn suggest_deref() { | ||
1016 | check_relevance( | ||
1017 | r#" | ||
1018 | #[lang = "deref"] | ||
1019 | trait Deref { | ||
1020 | type Target; | ||
1021 | fn deref(&self) -> &Self::Target; | ||
1022 | } | ||
1023 | |||
1024 | struct S; | ||
1025 | struct T(S); | ||
1026 | |||
1027 | impl Deref for T { | ||
1028 | type Target = S; | ||
1029 | |||
1030 | fn deref(&self) -> &Self::Target { | ||
1031 | &self.0 | ||
1032 | } | ||
1033 | } | ||
1034 | |||
1035 | fn foo(s: &S) {} | ||
1036 | |||
1037 | fn main() { | ||
1038 | let t = T(S); | ||
1039 | let m = 123; | ||
1040 | |||
1041 | foo($0); | ||
1042 | } | ||
1043 | "#, | ||
1044 | expect![[r#" | ||
1045 | lc m [local] | ||
1046 | lc t [local] | ||
1047 | lc &t [type+local] | ||
1048 | st T [] | ||
1049 | st S [] | ||
1050 | fn main() [] | ||
1051 | tt Deref [] | ||
1052 | fn foo(…) [] | ||
1053 | "#]], | ||
1054 | ) | ||
1055 | } | ||
1056 | |||
1057 | #[test] | ||
1058 | fn suggest_deref_mut() { | ||
1059 | check_relevance( | ||
1060 | r#" | ||
1061 | #[lang = "deref"] | ||
1062 | trait Deref { | ||
1063 | type Target; | ||
1064 | fn deref(&self) -> &Self::Target; | ||
1065 | } | ||
1066 | |||
1067 | #[lang = "deref_mut"] | ||
1068 | pub trait DerefMut: Deref { | ||
1069 | fn deref_mut(&mut self) -> &mut Self::Target; | ||
1070 | } | ||
1071 | |||
1072 | struct S; | ||
1073 | struct T(S); | ||
1074 | |||
1075 | impl Deref for T { | ||
1076 | type Target = S; | ||
1077 | |||
1078 | fn deref(&self) -> &Self::Target { | ||
1079 | &self.0 | ||
1080 | } | ||
1081 | } | ||
1082 | |||
1083 | impl DerefMut for T { | ||
1084 | fn deref_mut(&mut self) -> &mut Self::Target { | ||
1085 | &mut self.0 | ||
1086 | } | ||
1087 | } | ||
1088 | |||
1089 | fn foo(s: &mut S) {} | ||
1090 | |||
1091 | fn main() { | ||
1092 | let t = T(S); | ||
1093 | let m = 123; | ||
1094 | |||
1095 | foo($0); | ||
1096 | } | ||
1097 | "#, | ||
1098 | expect![[r#" | ||
1099 | lc m [local] | ||
1100 | lc t [local] | ||
1101 | lc &mut t [type+local] | ||
1102 | tt DerefMut [] | ||
1103 | tt Deref [] | ||
1104 | fn foo(…) [] | ||
1105 | st T [] | ||
1106 | st S [] | ||
1107 | fn main() [] | ||
1108 | "#]], | ||
1109 | ) | ||
1110 | } | ||
1111 | |||
1112 | #[test] | ||
1113 | fn locals() { | ||
1114 | check_relevance( | ||
1115 | r#" | ||
1116 | fn foo(bar: u32) { | ||
1117 | let baz = 0; | ||
1118 | |||
1119 | f$0 | ||
1120 | } | ||
1121 | "#, | ||
1122 | expect![[r#" | ||
1123 | lc baz [local] | ||
1124 | lc bar [local] | ||
1125 | fn foo(…) [] | ||
1126 | "#]], | ||
1127 | ); | ||
1128 | } | ||
990 | } | 1129 | } |