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.rs280
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
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
@@ -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
326fn compute_relevance(ctx: &RenderContext, ty: &Type, name: &str) -> Option<Relevance> { 329fn 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
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)
332} 342}
333 343
334#[cfg(test)] 344#[cfg(test)]
335mod tests { 345mod 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) { }
837fn foo(s: S) { test(s.$0) } 860fn 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 }
854fn foo(a: A) { B { bar: a.$0 }; } 877fn 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) { }
884fn foo(a: A) { f(B { bar: a.$0 }); } 907fn 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: () };
899fn go(world: &WorldSnapshot) { go(w$0) } 922fn 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;
914fn f(foo: &Foo) { f(foo, w$0) } 937fn 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"]
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 }
989} 1129}