diff options
Diffstat (limited to 'crates/ide_completion/src/render.rs')
-rw-r--r-- | crates/ide_completion/src/render.rs | 280 |
1 files changed, 210 insertions, 70 deletions
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs index 8c8b149a1..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}, |
@@ -20,7 +22,7 @@ use ide_db::{ | |||
20 | use syntax::TextRange; | 22 | use syntax::TextRange; |
21 | 23 | ||
22 | use crate::{ | 24 | use crate::{ |
23 | item::{ImportEdit, Relevance}, | 25 | item::{CompletionRelevance, ImportEdit}, |
24 | CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, | 26 | CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, |
25 | }; | 27 | }; |
26 | 28 | ||
@@ -149,24 +151,27 @@ impl<'a> Render<'a> { | |||
149 | CompletionKind::Reference, | 151 | CompletionKind::Reference, |
150 | self.ctx.source_range(), | 152 | self.ctx.source_range(), |
151 | name.to_string(), | 153 | name.to_string(), |
152 | ) | 154 | ); |
153 | .kind(SymbolKind::Field) | 155 | item.kind(SymbolKind::Field) |
154 | .detail(ty.display(self.ctx.db()).to_string()) | 156 | .detail(ty.display(self.ctx.db()).to_string()) |
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 = item.set_relevance(relevance); | ||
160 | } | ||
161 | 161 | ||
162 | item.build() | 162 | item.build() |
163 | } | 163 | } |
164 | 164 | ||
165 | fn add_tuple_field(&mut self, field: usize, ty: &Type) -> CompletionItem { | 165 | fn add_tuple_field(&mut self, field: usize, ty: &Type) -> CompletionItem { |
166 | CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), field.to_string()) | 166 | let mut item = CompletionItem::new( |
167 | .kind(SymbolKind::Field) | 167 | CompletionKind::Reference, |
168 | .detail(ty.display(self.ctx.db()).to_string()) | 168 | self.ctx.source_range(), |
169 | .build() | 169 | field.to_string(), |
170 | ); | ||
171 | |||
172 | item.kind(SymbolKind::Field).detail(ty.display(self.ctx.db()).to_string()); | ||
173 | |||
174 | item.build() | ||
170 | } | 175 | } |
171 | 176 | ||
172 | fn render_resolution( | 177 | fn render_resolution( |
@@ -225,15 +230,13 @@ impl<'a> Render<'a> { | |||
225 | CompletionItemKind::SymbolKind(SymbolKind::SelfParam) | 230 | CompletionItemKind::SymbolKind(SymbolKind::SelfParam) |
226 | } | 231 | } |
227 | ScopeDef::Unknown => { | 232 | ScopeDef::Unknown => { |
228 | let item = CompletionItem::new( | 233 | let mut item = CompletionItem::new( |
229 | CompletionKind::Reference, | 234 | CompletionKind::Reference, |
230 | self.ctx.source_range(), | 235 | self.ctx.source_range(), |
231 | local_name, | 236 | local_name, |
232 | ) | 237 | ); |
233 | .kind(CompletionItemKind::UnresolvedReference) | 238 | item.kind(CompletionItemKind::UnresolvedReference).add_import(import_to_add); |
234 | .add_import(import_to_add) | 239 | return Some(item.build()); |
235 | .build(); | ||
236 | return Some(item); | ||
237 | } | 240 | } |
238 | }; | 241 | }; |
239 | 242 | ||
@@ -242,25 +245,29 @@ impl<'a> Render<'a> { | |||
242 | if let ScopeDef::Local(local) = resolution { | 245 | if let ScopeDef::Local(local) = resolution { |
243 | let ty = local.ty(self.ctx.db()); | 246 | let ty = local.ty(self.ctx.db()); |
244 | if !ty.is_unknown() { | 247 | if !ty.is_unknown() { |
245 | item = item.detail(ty.display(self.ctx.db()).to_string()); | 248 | item.detail(ty.display(self.ctx.db()).to_string()); |
246 | } | 249 | } |
247 | }; | 250 | }; |
248 | 251 | ||
249 | if let ScopeDef::Local(local) = resolution { | 252 | if let ScopeDef::Local(local) = resolution { |
250 | let ty = local.ty(self.ctx.db()); | 253 | let ty = local.ty(self.ctx.db()); |
251 | if let Some(relevance) = compute_relevance(&self.ctx, &ty, &local_name) { | 254 | |
252 | item = item.set_relevance(relevance) | 255 | let mut relevance = compute_relevance(&self.ctx, &ty, &local_name); |
253 | } | 256 | relevance.is_local = true; |
257 | item.set_relevance(relevance); | ||
258 | |||
254 | 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() { |
255 | if let Some(ty_without_ref) = expected_type.remove_ref() { | 260 | if ty != expected_type { |
256 | if ty_without_ref == ty { | 261 | if let Some(ty_without_ref) = expected_type.remove_ref() { |
257 | cov_mark::hit!(suggest_ref); | 262 | if relevance_type_match(self.ctx.db().upcast(), &ty, &ty_without_ref) { |
258 | let mutability = if expected_type.is_mutable_reference() { | 263 | cov_mark::hit!(suggest_ref); |
259 | Mutability::Mut | 264 | let mutability = if expected_type.is_mutable_reference() { |
260 | } else { | 265 | Mutability::Mut |
261 | Mutability::Shared | 266 | } else { |
262 | }; | 267 | Mutability::Shared |
263 | item = item.ref_match(mutability) | 268 | }; |
269 | item.ref_match(mutability); | ||
270 | } | ||
264 | } | 271 | } |
265 | } | 272 | } |
266 | } | 273 | } |
@@ -281,21 +288,17 @@ impl<'a> Render<'a> { | |||
281 | }; | 288 | }; |
282 | if has_non_default_type_params { | 289 | if has_non_default_type_params { |
283 | cov_mark::hit!(inserts_angle_brackets_for_generics); | 290 | cov_mark::hit!(inserts_angle_brackets_for_generics); |
284 | item = item | 291 | item.lookup_by(local_name.clone()) |
285 | .lookup_by(local_name.clone()) | ||
286 | .label(format!("{}<…>", local_name)) | 292 | .label(format!("{}<…>", local_name)) |
287 | .insert_snippet(cap, format!("{}<$0>", local_name)); | 293 | .insert_snippet(cap, format!("{}<$0>", local_name)); |
288 | } | 294 | } |
289 | } | 295 | } |
290 | } | 296 | } |
291 | 297 | item.kind(kind) | |
292 | Some( | 298 | .add_import(import_to_add) |
293 | item.kind(kind) | 299 | .set_documentation(self.docs(resolution)) |
294 | .add_import(import_to_add) | 300 | .set_deprecated(self.is_deprecated(resolution)); |
295 | .set_documentation(self.docs(resolution)) | 301 | Some(item.build()) |
296 | .set_deprecated(self.is_deprecated(resolution)) | ||
297 | .build(), | ||
298 | ) | ||
299 | } | 302 | } |
300 | 303 | ||
301 | fn docs(&self, resolution: &ScopeDef) -> Option<Documentation> { | 304 | fn docs(&self, resolution: &ScopeDef) -> Option<Documentation> { |
@@ -323,23 +326,29 @@ impl<'a> Render<'a> { | |||
323 | } | 326 | } |
324 | } | 327 | } |
325 | 328 | ||
326 | fn compute_relevance(ctx: &RenderContext, ty: &Type, name: &str) -> Option<Relevance> { | 329 | fn compute_relevance(ctx: &RenderContext, ty: &Type, name: &str) -> CompletionRelevance { |
327 | let (expected_name, expected_type) = ctx.expected_name_and_type()?; | 330 | let mut res = CompletionRelevance::default(); |
328 | let mut res = Relevance::default(); | 331 | |
329 | res.exact_type_match = ty == &expected_type; | 332 | if let Some((expected_name, expected_type)) = ctx.expected_name_and_type() { |
330 | res.exact_name_match = name == &expected_name; | 333 | res.exact_type_match = ty == &expected_type; |
331 | Some(res) | 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) | ||
332 | } | 342 | } |
333 | 343 | ||
334 | #[cfg(test)] | 344 | #[cfg(test)] |
335 | mod tests { | 345 | mod tests { |
336 | use std::cmp::Reverse; | ||
337 | |||
338 | use expect_test::{expect, Expect}; | 346 | use expect_test::{expect, Expect}; |
347 | use itertools::Itertools; | ||
339 | 348 | ||
340 | use crate::{ | 349 | use crate::{ |
341 | test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG}, | 350 | test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG}, |
342 | CompletionKind, Relevance, | 351 | CompletionKind, CompletionRelevance, |
343 | }; | 352 | }; |
344 | 353 | ||
345 | fn check(ra_fixture: &str, expect: Expect) { | 354 | fn check(ra_fixture: &str, expect: Expect) { |
@@ -348,26 +357,40 @@ mod tests { | |||
348 | } | 357 | } |
349 | 358 | ||
350 | fn check_relevance(ra_fixture: &str, expect: Expect) { | 359 | fn check_relevance(ra_fixture: &str, expect: Expect) { |
351 | fn display_relevance(relevance: Relevance) -> &'static str { | 360 | fn display_relevance(relevance: CompletionRelevance) -> String { |
352 | match relevance { | 361 | let relevance_factors = vec![ |
353 | Relevance { exact_type_match: true, exact_name_match: true } => "[type+name]", | 362 | (relevance.exact_type_match, "type"), |
354 | Relevance { exact_type_match: true, exact_name_match: false } => "[type]", | 363 | (relevance.exact_name_match, "name"), |
355 | Relevance { exact_type_match: false, exact_name_match: true } => "[name]", | 364 | (relevance.is_local, "local"), |
356 | Relevance { exact_type_match: false, exact_name_match: false } => "[]", | 365 | ] |
357 | } | 366 | .into_iter() |
367 | .filter_map(|(cond, desc)| if cond { Some(desc) } else { None }) | ||
368 | .join("+"); | ||
369 | |||
370 | format!("[{}]", relevance_factors) | ||
358 | } | 371 | } |
359 | 372 | ||
360 | let mut completions = get_all_items(TEST_CONFIG, ra_fixture); | 373 | 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() | 374 | .into_iter() |
364 | .filter(|it| it.completion_kind == CompletionKind::Reference) | 375 | .filter(|it| it.completion_kind == CompletionKind::Reference) |
365 | .map(|it| { | 376 | .flat_map(|it| { |
377 | let mut items = vec![]; | ||
378 | |||
366 | let tag = it.kind().unwrap().tag(); | 379 | let tag = it.kind().unwrap().tag(); |
367 | let relevance = display_relevance(it.relevance()); | 380 | let relevance = display_relevance(it.relevance()); |
368 | 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 | ||
369 | }) | 391 | }) |
370 | .collect::<String>(); | 392 | .collect::<String>(); |
393 | |||
371 | expect.assert_eq(&actual); | 394 | expect.assert_eq(&actual); |
372 | } | 395 | } |
373 | 396 | ||
@@ -837,9 +860,9 @@ fn test(bar: u32) { } | |||
837 | fn foo(s: S) { test(s.$0) } | 860 | fn foo(s: S) { test(s.$0) } |
838 | "#, | 861 | "#, |
839 | expect![[r#" | 862 | expect![[r#" |
863 | fd foo [] | ||
840 | fd bar [type+name] | 864 | fd bar [type+name] |
841 | fd baz [type] | 865 | fd baz [type] |
842 | fd foo [] | ||
843 | "#]], | 866 | "#]], |
844 | ); | 867 | ); |
845 | } | 868 | } |
@@ -854,9 +877,9 @@ struct B { x: (), y: f32, bar: u32 } | |||
854 | fn foo(a: A) { B { bar: a.$0 }; } | 877 | fn foo(a: A) { B { bar: a.$0 }; } |
855 | "#, | 878 | "#, |
856 | expect![[r#" | 879 | expect![[r#" |
880 | fd foo [] | ||
857 | fd bar [type+name] | 881 | fd bar [type+name] |
858 | fd baz [type] | 882 | fd baz [type] |
859 | fd foo [] | ||
860 | "#]], | 883 | "#]], |
861 | ) | 884 | ) |
862 | } | 885 | } |
@@ -884,9 +907,9 @@ fn f(foo: i64) { } | |||
884 | fn foo(a: A) { f(B { bar: a.$0 }); } | 907 | fn foo(a: A) { f(B { bar: a.$0 }); } |
885 | "#, | 908 | "#, |
886 | expect![[r#" | 909 | expect![[r#" |
910 | fd foo [] | ||
887 | fd bar [type+name] | 911 | fd bar [type+name] |
888 | fd baz [type] | 912 | fd baz [type] |
889 | fd foo [] | ||
890 | "#]], | 913 | "#]], |
891 | ); | 914 | ); |
892 | } | 915 | } |
@@ -899,7 +922,7 @@ struct WorldSnapshot { _f: () }; | |||
899 | fn go(world: &WorldSnapshot) { go(w$0) } | 922 | fn go(world: &WorldSnapshot) { go(w$0) } |
900 | "#, | 923 | "#, |
901 | expect![[r#" | 924 | expect![[r#" |
902 | lc world [type+name] | 925 | lc world [type+name+local] |
903 | st WorldSnapshot [] | 926 | st WorldSnapshot [] |
904 | fn go(…) [] | 927 | fn go(…) [] |
905 | "#]], | 928 | "#]], |
@@ -914,9 +937,9 @@ struct Foo; | |||
914 | fn f(foo: &Foo) { f(foo, w$0) } | 937 | fn f(foo: &Foo) { f(foo, w$0) } |
915 | "#, | 938 | "#, |
916 | expect![[r#" | 939 | expect![[r#" |
940 | lc foo [local] | ||
917 | st Foo [] | 941 | st Foo [] |
918 | fn f(…) [] | 942 | fn f(…) [] |
919 | lc foo [] | ||
920 | "#]], | 943 | "#]], |
921 | ); | 944 | ); |
922 | } | 945 | } |
@@ -976,9 +999,10 @@ fn main() { | |||
976 | Local, | 999 | Local, |
977 | ), | 1000 | ), |
978 | detail: "S", | 1001 | detail: "S", |
979 | relevance: Relevance { | 1002 | relevance: CompletionRelevance { |
980 | exact_name_match: true, | 1003 | exact_name_match: true, |
981 | exact_type_match: false, | 1004 | exact_type_match: false, |
1005 | is_local: true, | ||
982 | }, | 1006 | }, |
983 | ref_match: "&mut ", | 1007 | ref_match: "&mut ", |
984 | }, | 1008 | }, |
@@ -986,4 +1010,120 @@ fn main() { | |||
986 | "#]], | 1010 | "#]], |
987 | ) | 1011 | ) |
988 | } | 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 | } | ||
989 | } | 1129 | } |