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.rs355
1 files changed, 269 insertions, 86 deletions
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs
index 8c8b149a1..2514dda7c 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},
@@ -20,7 +22,7 @@ use ide_db::{
20use syntax::TextRange; 22use syntax::TextRange;
21 23
22use crate::{ 24use crate::{
23 item::{ImportEdit, Relevance}, 25 item::{CompletionRelevance, ImportEdit},
24 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, 26 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind,
25}; 27};
26 28
@@ -116,19 +118,6 @@ impl<'a> RenderContext<'a> {
116 fn docs(&self, node: impl HasAttrs) -> Option<Documentation> { 118 fn docs(&self, node: impl HasAttrs) -> Option<Documentation> {
117 node.docs(self.db()) 119 node.docs(self.db())
118 } 120 }
119
120 fn expected_name_and_type(&self) -> Option<(String, Type)> {
121 if let Some(record_field) = &self.completion.record_field_syntax {
122 cov_mark::hit!(record_field_type_match);
123 let (struct_field, _local) = self.completion.sema.resolve_record_field(record_field)?;
124 Some((struct_field.name(self.db()).to_string(), struct_field.signature_ty(self.db())))
125 } else if let Some(active_parameter) = &self.completion.active_parameter {
126 cov_mark::hit!(active_param_type_match);
127 Some((active_parameter.name.clone(), active_parameter.ty.clone()))
128 } else {
129 None
130 }
131 }
132} 121}
133 122
134/// Generic renderer for completion items. 123/// Generic renderer for completion items.
@@ -149,24 +138,27 @@ impl<'a> Render<'a> {
149 CompletionKind::Reference, 138 CompletionKind::Reference,
150 self.ctx.source_range(), 139 self.ctx.source_range(),
151 name.to_string(), 140 name.to_string(),
152 ) 141 );
153 .kind(SymbolKind::Field) 142 item.kind(SymbolKind::Field)
154 .detail(ty.display(self.ctx.db()).to_string()) 143 .detail(ty.display(self.ctx.db()).to_string())
155 .set_documentation(field.docs(self.ctx.db())) 144 .set_documentation(field.docs(self.ctx.db()))
156 .set_deprecated(is_deprecated); 145 .set_deprecated(is_deprecated);
157 146
158 if let Some(relevance) = compute_relevance(&self.ctx, &ty, &name.to_string()) { 147 item.set_relevance(compute_relevance(&self.ctx, &ty, &name.to_string()));
159 item = item.set_relevance(relevance);
160 }
161 148
162 item.build() 149 item.build()
163 } 150 }
164 151
165 fn add_tuple_field(&mut self, field: usize, ty: &Type) -> CompletionItem { 152 fn add_tuple_field(&mut self, field: usize, ty: &Type) -> CompletionItem {
166 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), field.to_string()) 153 let mut item = CompletionItem::new(
167 .kind(SymbolKind::Field) 154 CompletionKind::Reference,
168 .detail(ty.display(self.ctx.db()).to_string()) 155 self.ctx.source_range(),
169 .build() 156 field.to_string(),
157 );
158
159 item.kind(SymbolKind::Field).detail(ty.display(self.ctx.db()).to_string());
160
161 item.build()
170 } 162 }
171 163
172 fn render_resolution( 164 fn render_resolution(
@@ -225,15 +217,13 @@ impl<'a> Render<'a> {
225 CompletionItemKind::SymbolKind(SymbolKind::SelfParam) 217 CompletionItemKind::SymbolKind(SymbolKind::SelfParam)
226 } 218 }
227 ScopeDef::Unknown => { 219 ScopeDef::Unknown => {
228 let item = CompletionItem::new( 220 let mut item = CompletionItem::new(
229 CompletionKind::Reference, 221 CompletionKind::Reference,
230 self.ctx.source_range(), 222 self.ctx.source_range(),
231 local_name, 223 local_name,
232 ) 224 );
233 .kind(CompletionItemKind::UnresolvedReference) 225 item.kind(CompletionItemKind::UnresolvedReference).add_import(import_to_add);
234 .add_import(import_to_add) 226 return Some(item.build());
235 .build();
236 return Some(item);
237 } 227 }
238 }; 228 };
239 229
@@ -242,25 +232,29 @@ impl<'a> Render<'a> {
242 if let ScopeDef::Local(local) = resolution { 232 if let ScopeDef::Local(local) = resolution {
243 let ty = local.ty(self.ctx.db()); 233 let ty = local.ty(self.ctx.db());
244 if !ty.is_unknown() { 234 if !ty.is_unknown() {
245 item = item.detail(ty.display(self.ctx.db()).to_string()); 235 item.detail(ty.display(self.ctx.db()).to_string());
246 } 236 }
247 }; 237 };
248 238
249 if let ScopeDef::Local(local) = resolution { 239 if let ScopeDef::Local(local) = resolution {
250 let ty = local.ty(self.ctx.db()); 240 let ty = local.ty(self.ctx.db());
251 if let Some(relevance) = compute_relevance(&self.ctx, &ty, &local_name) { 241
252 item = item.set_relevance(relevance) 242 let mut relevance = compute_relevance(&self.ctx, &ty, &local_name);
253 } 243 relevance.is_local = true;
254 if let Some((_expected_name, expected_type)) = self.ctx.expected_name_and_type() { 244 item.set_relevance(relevance);
255 if let Some(ty_without_ref) = expected_type.remove_ref() { 245
256 if ty_without_ref == ty { 246 if let Some(expected_type) = self.ctx.completion.expected_type.as_ref() {
257 cov_mark::hit!(suggest_ref); 247 if &ty != expected_type {
258 let mutability = if expected_type.is_mutable_reference() { 248 if let Some(ty_without_ref) = expected_type.remove_ref() {
259 Mutability::Mut 249 if relevance_type_match(self.ctx.db().upcast(), &ty, &ty_without_ref) {
260 } else { 250 cov_mark::hit!(suggest_ref);
261 Mutability::Shared 251 let mutability = if expected_type.is_mutable_reference() {
262 }; 252 Mutability::Mut
263 item = item.ref_match(mutability) 253 } else {
254 Mutability::Shared
255 };
256 item.ref_match(mutability);
257 }
264 } 258 }
265 } 259 }
266 } 260 }
@@ -281,21 +275,17 @@ impl<'a> Render<'a> {
281 }; 275 };
282 if has_non_default_type_params { 276 if has_non_default_type_params {
283 cov_mark::hit!(inserts_angle_brackets_for_generics); 277 cov_mark::hit!(inserts_angle_brackets_for_generics);
284 item = item 278 item.lookup_by(local_name.clone())
285 .lookup_by(local_name.clone())
286 .label(format!("{}<…>", local_name)) 279 .label(format!("{}<…>", local_name))
287 .insert_snippet(cap, format!("{}<$0>", local_name)); 280 .insert_snippet(cap, format!("{}<$0>", local_name));
288 } 281 }
289 } 282 }
290 } 283 }
291 284 item.kind(kind)
292 Some( 285 .add_import(import_to_add)
293 item.kind(kind) 286 .set_documentation(self.docs(resolution))
294 .add_import(import_to_add) 287 .set_deprecated(self.is_deprecated(resolution));
295 .set_documentation(self.docs(resolution)) 288 Some(item.build())
296 .set_deprecated(self.is_deprecated(resolution))
297 .build(),
298 )
299 } 289 }
300 290
301 fn docs(&self, resolution: &ScopeDef) -> Option<Documentation> { 291 fn docs(&self, resolution: &ScopeDef) -> Option<Documentation> {
@@ -323,23 +313,27 @@ impl<'a> Render<'a> {
323 } 313 }
324} 314}
325 315
326fn compute_relevance(ctx: &RenderContext, ty: &Type, name: &str) -> Option<Relevance> { 316fn compute_relevance(ctx: &RenderContext, ty: &Type, name: &str) -> CompletionRelevance {
327 let (expected_name, expected_type) = ctx.expected_name_and_type()?; 317 let mut res = CompletionRelevance::default();
328 let mut res = Relevance::default(); 318
329 res.exact_type_match = ty == &expected_type; 319 res.exact_type_match = Some(ty) == ctx.completion.expected_type.as_ref();
330 res.exact_name_match = name == &expected_name; 320 res.exact_name_match = Some(name) == ctx.completion.expected_name.as_deref();
331 Some(res) 321
322 res
323}
324
325fn relevance_type_match(db: &dyn HirDatabase, ty: &Type, expected_type: &Type) -> bool {
326 ty == expected_type || ty.autoderef(db).any(|deref_ty| &deref_ty == expected_type)
332} 327}
333 328
334#[cfg(test)] 329#[cfg(test)]
335mod tests { 330mod tests {
336 use std::cmp::Reverse;
337
338 use expect_test::{expect, Expect}; 331 use expect_test::{expect, Expect};
332 use itertools::Itertools;
339 333
340 use crate::{ 334 use crate::{
341 test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG}, 335 test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG},
342 CompletionKind, Relevance, 336 CompletionKind, CompletionRelevance,
343 }; 337 };
344 338
345 fn check(ra_fixture: &str, expect: Expect) { 339 fn check(ra_fixture: &str, expect: Expect) {
@@ -348,26 +342,40 @@ mod tests {
348 } 342 }
349 343
350 fn check_relevance(ra_fixture: &str, expect: Expect) { 344 fn check_relevance(ra_fixture: &str, expect: Expect) {
351 fn display_relevance(relevance: Relevance) -> &'static str { 345 fn display_relevance(relevance: CompletionRelevance) -> String {
352 match relevance { 346 let relevance_factors = vec![
353 Relevance { exact_type_match: true, exact_name_match: true } => "[type+name]", 347 (relevance.exact_type_match, "type"),
354 Relevance { exact_type_match: true, exact_name_match: false } => "[type]", 348 (relevance.exact_name_match, "name"),
355 Relevance { exact_type_match: false, exact_name_match: true } => "[name]", 349 (relevance.is_local, "local"),
356 Relevance { exact_type_match: false, exact_name_match: false } => "[]", 350 ]
357 } 351 .into_iter()
352 .filter_map(|(cond, desc)| if cond { Some(desc) } else { None })
353 .join("+");
354
355 format!("[{}]", relevance_factors)
358 } 356 }
359 357
360 let mut completions = get_all_items(TEST_CONFIG, ra_fixture); 358 let actual = get_all_items(TEST_CONFIG, ra_fixture)
361 completions.sort_by_key(|it| (Reverse(it.relevance()), it.label().to_string()));
362 let actual = completions
363 .into_iter() 359 .into_iter()
364 .filter(|it| it.completion_kind == CompletionKind::Reference) 360 .filter(|it| it.completion_kind == CompletionKind::Reference)
365 .map(|it| { 361 .flat_map(|it| {
362 let mut items = vec![];
363
366 let tag = it.kind().unwrap().tag(); 364 let tag = it.kind().unwrap().tag();
367 let relevance = display_relevance(it.relevance()); 365 let relevance = display_relevance(it.relevance());
368 format!("{} {} {}\n", tag, it.label(), relevance) 366 items.push(format!("{} {} {}\n", tag, it.label(), relevance));
367
368 if let Some((mutability, relevance)) = it.ref_match() {
369 let label = format!("&{}{}", mutability.as_keyword_for_ref(), it.label());
370 let relevance = display_relevance(relevance);
371
372 items.push(format!("{} {} {}\n", tag, label, relevance));
373 }
374
375 items
369 }) 376 })
370 .collect::<String>(); 377 .collect::<String>();
378
371 expect.assert_eq(&actual); 379 expect.assert_eq(&actual);
372 } 380 }
373 381
@@ -829,7 +837,6 @@ fn foo(xs: Vec<i128>)
829 837
830 #[test] 838 #[test]
831 fn active_param_relevance() { 839 fn active_param_relevance() {
832 cov_mark::check!(active_param_type_match);
833 check_relevance( 840 check_relevance(
834 r#" 841 r#"
835struct S { foo: i64, bar: u32, baz: u32 } 842struct S { foo: i64, bar: u32, baz: u32 }
@@ -837,16 +844,15 @@ fn test(bar: u32) { }
837fn foo(s: S) { test(s.$0) } 844fn foo(s: S) { test(s.$0) }
838"#, 845"#,
839 expect![[r#" 846 expect![[r#"
847 fd foo []
840 fd bar [type+name] 848 fd bar [type+name]
841 fd baz [type] 849 fd baz [type]
842 fd foo []
843 "#]], 850 "#]],
844 ); 851 );
845 } 852 }
846 853
847 #[test] 854 #[test]
848 fn record_field_relevances() { 855 fn record_field_relevances() {
849 cov_mark::check!(record_field_type_match);
850 check_relevance( 856 check_relevance(
851 r#" 857 r#"
852struct A { foo: i64, bar: u32, baz: u32 } 858struct A { foo: i64, bar: u32, baz: u32 }
@@ -854,9 +860,9 @@ struct B { x: (), y: f32, bar: u32 }
854fn foo(a: A) { B { bar: a.$0 }; } 860fn foo(a: A) { B { bar: a.$0 }; }
855"#, 861"#,
856 expect![[r#" 862 expect![[r#"
863 fd foo []
857 fd bar [type+name] 864 fd bar [type+name]
858 fd baz [type] 865 fd baz [type]
859 fd foo []
860 "#]], 866 "#]],
861 ) 867 )
862 } 868 }
@@ -884,9 +890,9 @@ fn f(foo: i64) { }
884fn foo(a: A) { f(B { bar: a.$0 }); } 890fn foo(a: A) { f(B { bar: a.$0 }); }
885"#, 891"#,
886 expect![[r#" 892 expect![[r#"
893 fd foo []
887 fd bar [type+name] 894 fd bar [type+name]
888 fd baz [type] 895 fd baz [type]
889 fd foo []
890 "#]], 896 "#]],
891 ); 897 );
892 } 898 }
@@ -899,7 +905,7 @@ struct WorldSnapshot { _f: () };
899fn go(world: &WorldSnapshot) { go(w$0) } 905fn go(world: &WorldSnapshot) { go(w$0) }
900"#, 906"#,
901 expect![[r#" 907 expect![[r#"
902 lc world [type+name] 908 lc world [type+name+local]
903 st WorldSnapshot [] 909 st WorldSnapshot []
904 fn go(…) [] 910 fn go(…) []
905 "#]], 911 "#]],
@@ -914,9 +920,69 @@ struct Foo;
914fn f(foo: &Foo) { f(foo, w$0) } 920fn f(foo: &Foo) { f(foo, w$0) }
915"#, 921"#,
916 expect![[r#" 922 expect![[r#"
923 lc foo [local]
917 st Foo [] 924 st Foo []
918 fn f(…) [] 925 fn f(…) []
919 lc foo [] 926 "#]],
927 );
928 }
929
930 #[test]
931 fn score_fn_type_and_name_match() {
932 check_relevance(
933 r#"
934struct A { bar: u8 }
935fn baz() -> u8 { 0 }
936fn bar() -> u8 { 0 }
937fn f() { A { bar: b$0 }; }
938"#,
939 expect![[r#"
940 fn baz() [type]
941 st A []
942 fn bar() [type+name]
943 fn f() []
944 "#]],
945 );
946 }
947
948 #[test]
949 fn score_method_type_and_name_match() {
950 check_relevance(
951 r#"
952fn baz(aaa: u32){}
953struct Foo;
954impl Foo {
955fn aaa(&self) -> u32 { 0 }
956fn bbb(&self) -> u32 { 0 }
957fn ccc(&self) -> u64 { 0 }
958}
959fn f() {
960 baz(Foo.$0
961}
962"#,
963 expect![[r#"
964 me aaa() [type+name]
965 me bbb() [type]
966 me ccc() []
967 "#]],
968 );
969 }
970
971 #[test]
972 fn score_method_name_match_only() {
973 check_relevance(
974 r#"
975fn baz(aaa: u32){}
976struct Foo;
977impl Foo {
978fn aaa(&self) -> u64 { 0 }
979}
980fn f() {
981 baz(Foo.$0
982}
983"#,
984 expect![[r#"
985 me aaa() [name]
920 "#]], 986 "#]],
921 ); 987 );
922 } 988 }
@@ -976,9 +1042,10 @@ fn main() {
976 Local, 1042 Local,
977 ), 1043 ),
978 detail: "S", 1044 detail: "S",
979 relevance: Relevance { 1045 relevance: CompletionRelevance {
980 exact_name_match: true, 1046 exact_name_match: true,
981 exact_type_match: false, 1047 exact_type_match: false,
1048 is_local: true,
982 }, 1049 },
983 ref_match: "&mut ", 1050 ref_match: "&mut ",
984 }, 1051 },
@@ -986,4 +1053,120 @@ fn main() {
986 "#]], 1053 "#]],
987 ) 1054 )
988 } 1055 }
1056
1057 #[test]
1058 fn suggest_deref() {
1059 check_relevance(
1060 r#"
1061#[lang = "deref"]
1062trait Deref {
1063 type Target;
1064 fn deref(&self) -> &Self::Target;
1065}
1066
1067struct S;
1068struct T(S);
1069
1070impl Deref for T {
1071 type Target = S;
1072
1073 fn deref(&self) -> &Self::Target {
1074 &self.0
1075 }
1076}
1077
1078fn foo(s: &S) {}
1079
1080fn main() {
1081 let t = T(S);
1082 let m = 123;
1083
1084 foo($0);
1085}
1086 "#,
1087 expect![[r#"
1088 lc m [local]
1089 lc t [local]
1090 lc &t [type+local]
1091 st T []
1092 st S []
1093 fn main() []
1094 tt Deref []
1095 fn foo(…) []
1096 "#]],
1097 )
1098 }
1099
1100 #[test]
1101 fn suggest_deref_mut() {
1102 check_relevance(
1103 r#"
1104#[lang = "deref"]
1105trait Deref {
1106 type Target;
1107 fn deref(&self) -> &Self::Target;
1108}
1109
1110#[lang = "deref_mut"]
1111pub trait DerefMut: Deref {
1112 fn deref_mut(&mut self) -> &mut Self::Target;
1113}
1114
1115struct S;
1116struct T(S);
1117
1118impl Deref for T {
1119 type Target = S;
1120
1121 fn deref(&self) -> &Self::Target {
1122 &self.0
1123 }
1124}
1125
1126impl DerefMut for T {
1127 fn deref_mut(&mut self) -> &mut Self::Target {
1128 &mut self.0
1129 }
1130}
1131
1132fn foo(s: &mut S) {}
1133
1134fn main() {
1135 let t = T(S);
1136 let m = 123;
1137
1138 foo($0);
1139}
1140 "#,
1141 expect![[r#"
1142 lc m [local]
1143 lc t [local]
1144 lc &mut t [type+local]
1145 tt DerefMut []
1146 tt Deref []
1147 fn foo(…) []
1148 st T []
1149 st S []
1150 fn main() []
1151 "#]],
1152 )
1153 }
1154
1155 #[test]
1156 fn locals() {
1157 check_relevance(
1158 r#"
1159fn foo(bar: u32) {
1160 let baz = 0;
1161
1162 f$0
1163}
1164"#,
1165 expect![[r#"
1166 lc baz [local]
1167 lc bar [local]
1168 fn foo(…) []
1169 "#]],
1170 );
1171 }
989} 1172}