aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_completion
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_completion')
-rw-r--r--crates/ide_completion/src/completions/postfix/format_like.rs2
-rw-r--r--crates/ide_completion/src/item.rs37
-rw-r--r--crates/ide_completion/src/render.rs223
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.
60fn string_literal_contents(item: &ast::String) -> Option<String> { 60fn 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
149impl CompletionRelevance { 164impl 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
11mod builder_ext; 11mod builder_ext;
12 12
13use base_db::Upcast;
13use hir::{ 14use hir::{
14 AsAssocItem, Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability, ScopeDef, Type, 15 db::HirDatabase, AsAssocItem, Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability,
16 ScopeDef, Type,
15}; 17};
16use ide_db::{ 18use 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
325fn compute_relevance(ctx: &RenderContext, ty: &Type, name: &str) -> Option<CompletionRelevance> { 329fn 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
340fn 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)]
334mod tests { 345mod 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) { }
838fn foo(s: S) { test(s.$0) } 860fn 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 }
855fn foo(a: A) { B { bar: a.$0 }; } 877fn 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) { }
885fn foo(a: A) { f(B { bar: a.$0 }); } 907fn 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: () };
900fn go(world: &WorldSnapshot) { go(w$0) } 922fn 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;
915fn f(foo: &Foo) { f(foo, w$0) } 937fn 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"]
1019trait Deref {
1020 type Target;
1021 fn deref(&self) -> &Self::Target;
1022}
1023
1024struct S;
1025struct T(S);
1026
1027impl Deref for T {
1028 type Target = S;
1029
1030 fn deref(&self) -> &Self::Target {
1031 &self.0
1032 }
1033}
1034
1035fn foo(s: &S) {}
1036
1037fn 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"]
1062trait Deref {
1063 type Target;
1064 fn deref(&self) -> &Self::Target;
1065}
1066
1067#[lang = "deref_mut"]
1068pub trait DerefMut: Deref {
1069 fn deref_mut(&mut self) -> &mut Self::Target;
1070}
1071
1072struct S;
1073struct T(S);
1074
1075impl Deref for T {
1076 type Target = S;
1077
1078 fn deref(&self) -> &Self::Target {
1079 &self.0
1080 }
1081}
1082
1083impl DerefMut for T {
1084 fn deref_mut(&mut self) -> &mut Self::Target {
1085 &mut self.0
1086 }
1087}
1088
1089fn foo(s: &mut S) {}
1090
1091fn 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#"
1116fn 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}