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.rs271
1 files changed, 158 insertions, 113 deletions
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs
index eddaaa6f3..db31896e5 100644
--- a/crates/ide_completion/src/render.rs
+++ b/crates/ide_completion/src/render.rs
@@ -13,13 +13,15 @@ mod builder_ext;
13use hir::{ 13use hir::{
14 AsAssocItem, Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability, ScopeDef, Type, 14 AsAssocItem, Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability, ScopeDef, Type,
15}; 15};
16use ide_db::{helpers::SnippetCap, RootDatabase, SymbolKind}; 16use ide_db::{
17 helpers::{item_name, SnippetCap},
18 RootDatabase, SymbolKind,
19};
17use syntax::TextRange; 20use syntax::TextRange;
18use test_utils::mark;
19 21
20use crate::{ 22use crate::{
21 item::ImportEdit, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, 23 item::{CompletionRelevance, ImportEdit},
22 CompletionScore, 24 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind,
23}; 25};
24 26
25use crate::render::{enum_variant::render_variant, function::render_fn, macro_::render_macro}; 27use crate::render::{enum_variant::render_variant, function::render_fn, macro_::render_macro};
@@ -51,18 +53,20 @@ pub(crate) fn render_resolution<'a>(
51pub(crate) fn render_resolution_with_import<'a>( 53pub(crate) fn render_resolution_with_import<'a>(
52 ctx: RenderContext<'a>, 54 ctx: RenderContext<'a>,
53 import_edit: ImportEdit, 55 import_edit: ImportEdit,
54 resolution: &ScopeDef,
55) -> Option<CompletionItem> { 56) -> Option<CompletionItem> {
57 let resolution = ScopeDef::from(import_edit.import.original_item);
56 let local_name = match resolution { 58 let local_name = match resolution {
57 ScopeDef::ModuleDef(ModuleDef::Function(f)) => f.name(ctx.completion.db).to_string(), 59 ScopeDef::ModuleDef(ModuleDef::Function(f)) => f.name(ctx.completion.db).to_string(),
58 ScopeDef::ModuleDef(ModuleDef::Const(c)) => c.name(ctx.completion.db)?.to_string(), 60 ScopeDef::ModuleDef(ModuleDef::Const(c)) => c.name(ctx.completion.db)?.to_string(),
59 ScopeDef::ModuleDef(ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db).to_string(), 61 ScopeDef::ModuleDef(ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db).to_string(),
60 _ => import_edit.import_path.segments().last()?.to_string(), 62 _ => item_name(ctx.db(), import_edit.import.original_item)?.to_string(),
61 }; 63 };
62 Render::new(ctx).render_resolution(local_name, Some(import_edit), resolution).map(|mut item| { 64 Render::new(ctx).render_resolution(local_name, Some(import_edit), &resolution).map(
63 item.completion_kind = CompletionKind::Magic; 65 |mut item| {
64 item 66 item.completion_kind = CompletionKind::Magic;
65 }) 67 item
68 },
69 )
66} 70}
67 71
68/// Interface for data and methods required for items rendering. 72/// Interface for data and methods required for items rendering.
@@ -113,13 +117,13 @@ impl<'a> RenderContext<'a> {
113 node.docs(self.db()) 117 node.docs(self.db())
114 } 118 }
115 119
116 fn active_name_and_type(&self) -> Option<(String, Type)> { 120 fn expected_name_and_type(&self) -> Option<(String, Type)> {
117 if let Some(record_field) = &self.completion.record_field_syntax { 121 if let Some(record_field) = &self.completion.record_field_syntax {
118 mark::hit!(record_field_type_match); 122 cov_mark::hit!(record_field_type_match);
119 let (struct_field, _local) = self.completion.sema.resolve_record_field(record_field)?; 123 let (struct_field, _local) = self.completion.sema.resolve_record_field(record_field)?;
120 Some((struct_field.name(self.db()).to_string(), struct_field.signature_ty(self.db()))) 124 Some((struct_field.name(self.db()).to_string(), struct_field.signature_ty(self.db())))
121 } else if let Some(active_parameter) = &self.completion.active_parameter { 125 } else if let Some(active_parameter) = &self.completion.active_parameter {
122 mark::hit!(active_param_type_match); 126 cov_mark::hit!(active_param_type_match);
123 Some((active_parameter.name.clone(), active_parameter.ty.clone())) 127 Some((active_parameter.name.clone(), active_parameter.ty.clone()))
124 } else { 128 } else {
125 None 129 None
@@ -145,24 +149,29 @@ impl<'a> Render<'a> {
145 CompletionKind::Reference, 149 CompletionKind::Reference,
146 self.ctx.source_range(), 150 self.ctx.source_range(),
147 name.to_string(), 151 name.to_string(),
148 ) 152 );
149 .kind(SymbolKind::Field) 153 item.kind(SymbolKind::Field)
150 .detail(ty.display(self.ctx.db()).to_string()) 154 .detail(ty.display(self.ctx.db()).to_string())
151 .set_documentation(field.docs(self.ctx.db())) 155 .set_documentation(field.docs(self.ctx.db()))
152 .set_deprecated(is_deprecated); 156 .set_deprecated(is_deprecated);
153 157
154 if let Some(score) = compute_score(&self.ctx, &ty, &name.to_string()) { 158 if let Some(relevance) = compute_relevance(&self.ctx, &ty, &name.to_string()) {
155 item = item.set_score(score); 159 item.set_relevance(relevance);
156 } 160 }
157 161
158 item.build() 162 item.build()
159 } 163 }
160 164
161 fn add_tuple_field(&mut self, field: usize, ty: &Type) -> CompletionItem { 165 fn add_tuple_field(&mut self, field: usize, ty: &Type) -> CompletionItem {
162 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), field.to_string()) 166 let mut item = CompletionItem::new(
163 .kind(SymbolKind::Field) 167 CompletionKind::Reference,
164 .detail(ty.display(self.ctx.db()).to_string()) 168 self.ctx.source_range(),
165 .build() 169 field.to_string(),
170 );
171
172 item.kind(SymbolKind::Field).detail(ty.display(self.ctx.db()).to_string());
173
174 item.build()
166 } 175 }
167 176
168 fn render_resolution( 177 fn render_resolution(
@@ -221,15 +230,13 @@ impl<'a> Render<'a> {
221 CompletionItemKind::SymbolKind(SymbolKind::SelfParam) 230 CompletionItemKind::SymbolKind(SymbolKind::SelfParam)
222 } 231 }
223 ScopeDef::Unknown => { 232 ScopeDef::Unknown => {
224 let item = CompletionItem::new( 233 let mut item = CompletionItem::new(
225 CompletionKind::Reference, 234 CompletionKind::Reference,
226 self.ctx.source_range(), 235 self.ctx.source_range(),
227 local_name, 236 local_name,
228 ) 237 );
229 .kind(CompletionItemKind::UnresolvedReference) 238 item.kind(CompletionItemKind::UnresolvedReference).add_import(import_to_add);
230 .add_import(import_to_add) 239 return Some(item.build());
231 .build();
232 return Some(item);
233 } 240 }
234 }; 241 };
235 242
@@ -238,20 +245,27 @@ impl<'a> Render<'a> {
238 if let ScopeDef::Local(local) = resolution { 245 if let ScopeDef::Local(local) = resolution {
239 let ty = local.ty(self.ctx.db()); 246 let ty = local.ty(self.ctx.db());
240 if !ty.is_unknown() { 247 if !ty.is_unknown() {
241 item = item.detail(ty.display(self.ctx.db()).to_string()); 248 item.detail(ty.display(self.ctx.db()).to_string());
242 } 249 }
243 }; 250 };
244 251
245 let mut ref_match = None;
246 if let ScopeDef::Local(local) = resolution { 252 if let ScopeDef::Local(local) = resolution {
247 if let Some((active_name, active_type)) = self.ctx.active_name_and_type() { 253 let ty = local.ty(self.ctx.db());
248 let ty = local.ty(self.ctx.db()); 254 if let Some(relevance) = compute_relevance(&self.ctx, &ty, &local_name) {
249 if let Some(score) = 255 item.set_relevance(relevance);
250 compute_score_from_active(&active_type, &active_name, &ty, &local_name) 256 }
251 { 257 if let Some((_expected_name, expected_type)) = self.ctx.expected_name_and_type() {
252 item = item.set_score(score); 258 if let Some(ty_without_ref) = expected_type.remove_ref() {
259 if ty_without_ref == ty {
260 cov_mark::hit!(suggest_ref);
261 let mutability = if expected_type.is_mutable_reference() {
262 Mutability::Mut
263 } else {
264 Mutability::Shared
265 };
266 item.ref_match(mutability);
267 }
253 } 268 }
254 ref_match = refed_type_matches(&active_type, &active_name, &ty, &local_name);
255 } 269 }
256 } 270 }
257 271
@@ -269,23 +283,18 @@ impl<'a> Render<'a> {
269 _ => false, 283 _ => false,
270 }; 284 };
271 if has_non_default_type_params { 285 if has_non_default_type_params {
272 mark::hit!(inserts_angle_brackets_for_generics); 286 cov_mark::hit!(inserts_angle_brackets_for_generics);
273 item = item 287 item.lookup_by(local_name.clone())
274 .lookup_by(local_name.clone())
275 .label(format!("{}<…>", local_name)) 288 .label(format!("{}<…>", local_name))
276 .insert_snippet(cap, format!("{}<$0>", local_name)); 289 .insert_snippet(cap, format!("{}<$0>", local_name));
277 } 290 }
278 } 291 }
279 } 292 }
280 293 item.kind(kind)
281 Some( 294 .add_import(import_to_add)
282 item.kind(kind) 295 .set_documentation(self.docs(resolution))
283 .add_import(import_to_add) 296 .set_deprecated(self.is_deprecated(resolution));
284 .set_ref_match(ref_match) 297 Some(item.build())
285 .set_documentation(self.docs(resolution))
286 .set_deprecated(self.is_deprecated(resolution))
287 .build(),
288 )
289 } 298 }
290 299
291 fn docs(&self, resolution: &ScopeDef) -> Option<Documentation> { 300 fn docs(&self, resolution: &ScopeDef) -> Option<Documentation> {
@@ -313,56 +322,23 @@ impl<'a> Render<'a> {
313 } 322 }
314} 323}
315 324
316fn compute_score_from_active( 325fn compute_relevance(ctx: &RenderContext, ty: &Type, name: &str) -> Option<CompletionRelevance> {
317 active_type: &Type, 326 let (expected_name, expected_type) = ctx.expected_name_and_type()?;
318 active_name: &str, 327 let mut res = CompletionRelevance::default();
319 ty: &Type, 328 res.exact_type_match = ty == &expected_type;
320 name: &str, 329 res.exact_name_match = name == &expected_name;
321) -> Option<CompletionScore> {
322 // Compute score
323 // For the same type
324 if active_type != ty {
325 return None;
326 }
327
328 let mut res = CompletionScore::TypeMatch;
329
330 // If same type + same name then go top position
331 if active_name == name {
332 res = CompletionScore::TypeAndNameMatch
333 }
334
335 Some(res) 330 Some(res)
336} 331}
337fn refed_type_matches(
338 active_type: &Type,
339 active_name: &str,
340 ty: &Type,
341 name: &str,
342) -> Option<(Mutability, CompletionScore)> {
343 let derefed_active = active_type.remove_ref()?;
344 let score = compute_score_from_active(&derefed_active, &active_name, &ty, &name)?;
345 Some((
346 if active_type.is_mutable_reference() { Mutability::Mut } else { Mutability::Shared },
347 score,
348 ))
349}
350
351fn compute_score(ctx: &RenderContext, ty: &Type, name: &str) -> Option<CompletionScore> {
352 let (active_name, active_type) = ctx.active_name_and_type()?;
353 compute_score_from_active(&active_type, &active_name, ty, name)
354}
355 332
356#[cfg(test)] 333#[cfg(test)]
357mod tests { 334mod tests {
358 use std::cmp::Reverse; 335 use std::cmp::Reverse;
359 336
360 use expect_test::{expect, Expect}; 337 use expect_test::{expect, Expect};
361 use test_utils::mark;
362 338
363 use crate::{ 339 use crate::{
364 test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG}, 340 test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG},
365 CompletionKind, CompletionScore, 341 CompletionKind, CompletionRelevance,
366 }; 342 };
367 343
368 fn check(ra_fixture: &str, expect: Expect) { 344 fn check(ra_fixture: &str, expect: Expect) {
@@ -370,24 +346,27 @@ mod tests {
370 expect.assert_debug_eq(&actual); 346 expect.assert_debug_eq(&actual);
371 } 347 }
372 348
373 fn check_scores(ra_fixture: &str, expect: Expect) { 349 fn check_relevance(ra_fixture: &str, expect: Expect) {
374 fn display_score(score: Option<CompletionScore>) -> &'static str { 350 fn display_relevance(relevance: CompletionRelevance) -> &'static str {
375 match score { 351 match relevance {
376 Some(CompletionScore::TypeMatch) => "[type]", 352 CompletionRelevance { exact_type_match: true, exact_name_match: true } => {
377 Some(CompletionScore::TypeAndNameMatch) => "[type+name]", 353 "[type+name]"
378 None => "[]".into(), 354 }
355 CompletionRelevance { exact_type_match: true, exact_name_match: false } => "[type]",
356 CompletionRelevance { exact_type_match: false, exact_name_match: true } => "[name]",
357 CompletionRelevance { exact_type_match: false, exact_name_match: false } => "[]",
379 } 358 }
380 } 359 }
381 360
382 let mut completions = get_all_items(TEST_CONFIG, ra_fixture); 361 let mut completions = get_all_items(TEST_CONFIG, ra_fixture);
383 completions.sort_by_key(|it| (Reverse(it.score()), it.label().to_string())); 362 completions.sort_by_key(|it| (Reverse(it.relevance()), it.label().to_string()));
384 let actual = completions 363 let actual = completions
385 .into_iter() 364 .into_iter()
386 .filter(|it| it.completion_kind == CompletionKind::Reference) 365 .filter(|it| it.completion_kind == CompletionKind::Reference)
387 .map(|it| { 366 .map(|it| {
388 let tag = it.kind().unwrap().tag(); 367 let tag = it.kind().unwrap().tag();
389 let score = display_score(it.score()); 368 let relevance = display_relevance(it.relevance());
390 format!("{} {} {}\n", tag, it.label(), score) 369 format!("{} {} {}\n", tag, it.label(), relevance)
391 }) 370 })
392 .collect::<String>(); 371 .collect::<String>();
393 expect.assert_eq(&actual); 372 expect.assert_eq(&actual);
@@ -734,7 +713,7 @@ fn foo(s: S) { s.$0 }
734 713
735 #[test] 714 #[test]
736 fn no_call_parens_if_fn_ptr_needed() { 715 fn no_call_parens_if_fn_ptr_needed() {
737 mark::check!(no_call_parens_if_fn_ptr_needed); 716 cov_mark::check!(no_call_parens_if_fn_ptr_needed);
738 check_edit( 717 check_edit(
739 "foo", 718 "foo",
740 r#" 719 r#"
@@ -758,7 +737,7 @@ fn main() -> ManualVtable {
758 737
759 #[test] 738 #[test]
760 fn no_parens_in_use_item() { 739 fn no_parens_in_use_item() {
761 mark::check!(no_parens_in_use_item); 740 cov_mark::check!(no_parens_in_use_item);
762 check_edit( 741 check_edit(
763 "foo", 742 "foo",
764 r#" 743 r#"
@@ -802,7 +781,7 @@ fn f(foo: &Foo) { foo.foo(); }
802 781
803 #[test] 782 #[test]
804 fn inserts_angle_brackets_for_generics() { 783 fn inserts_angle_brackets_for_generics() {
805 mark::check!(inserts_angle_brackets_for_generics); 784 cov_mark::check!(inserts_angle_brackets_for_generics);
806 check_edit( 785 check_edit(
807 "Vec", 786 "Vec",
808 r#" 787 r#"
@@ -850,9 +829,9 @@ fn foo(xs: Vec<i128>)
850 } 829 }
851 830
852 #[test] 831 #[test]
853 fn active_param_score() { 832 fn active_param_relevance() {
854 mark::check!(active_param_type_match); 833 cov_mark::check!(active_param_type_match);
855 check_scores( 834 check_relevance(
856 r#" 835 r#"
857struct S { foo: i64, bar: u32, baz: u32 } 836struct S { foo: i64, bar: u32, baz: u32 }
858fn test(bar: u32) { } 837fn test(bar: u32) { }
@@ -867,9 +846,9 @@ fn foo(s: S) { test(s.$0) }
867 } 846 }
868 847
869 #[test] 848 #[test]
870 fn record_field_scores() { 849 fn record_field_relevances() {
871 mark::check!(record_field_type_match); 850 cov_mark::check!(record_field_type_match);
872 check_scores( 851 check_relevance(
873 r#" 852 r#"
874struct A { foo: i64, bar: u32, baz: u32 } 853struct A { foo: i64, bar: u32, baz: u32 }
875struct B { x: (), y: f32, bar: u32 } 854struct B { x: (), y: f32, bar: u32 }
@@ -884,8 +863,8 @@ fn foo(a: A) { B { bar: a.$0 }; }
884 } 863 }
885 864
886 #[test] 865 #[test]
887 fn record_field_and_call_scores() { 866 fn record_field_and_call_relevances() {
888 check_scores( 867 check_relevance(
889 r#" 868 r#"
890struct A { foo: i64, bar: u32, baz: u32 } 869struct A { foo: i64, bar: u32, baz: u32 }
891struct B { x: (), y: f32, bar: u32 } 870struct B { x: (), y: f32, bar: u32 }
@@ -898,7 +877,7 @@ fn foo(a: A) { B { bar: f(a.$0) }; }
898 fd baz [] 877 fd baz []
899 "#]], 878 "#]],
900 ); 879 );
901 check_scores( 880 check_relevance(
902 r#" 881 r#"
903struct A { foo: i64, bar: u32, baz: u32 } 882struct A { foo: i64, bar: u32, baz: u32 }
904struct B { x: (), y: f32, bar: u32 } 883struct B { x: (), y: f32, bar: u32 }
@@ -915,7 +894,7 @@ fn foo(a: A) { f(B { bar: a.$0 }); }
915 894
916 #[test] 895 #[test]
917 fn prioritize_exact_ref_match() { 896 fn prioritize_exact_ref_match() {
918 check_scores( 897 check_relevance(
919 r#" 898 r#"
920struct WorldSnapshot { _f: () }; 899struct WorldSnapshot { _f: () };
921fn go(world: &WorldSnapshot) { go(w$0) } 900fn go(world: &WorldSnapshot) { go(w$0) }
@@ -930,7 +909,7 @@ fn go(world: &WorldSnapshot) { go(w$0) }
930 909
931 #[test] 910 #[test]
932 fn too_many_arguments() { 911 fn too_many_arguments() {
933 check_scores( 912 check_relevance(
934 r#" 913 r#"
935struct Foo; 914struct Foo;
936fn f(foo: &Foo) { f(foo, w$0) } 915fn f(foo: &Foo) { f(foo, w$0) }
@@ -942,4 +921,70 @@ fn f(foo: &Foo) { f(foo, w$0) }
942 "#]], 921 "#]],
943 ); 922 );
944 } 923 }
924
925 #[test]
926 fn suggest_ref_mut() {
927 cov_mark::check!(suggest_ref);
928 check(
929 r#"
930struct S;
931fn foo(s: &mut S) {}
932fn main() {
933 let mut s = S;
934 foo($0);
935}
936 "#,
937 expect![[r#"
938 [
939 CompletionItem {
940 label: "S",
941 source_range: 70..70,
942 delete: 70..70,
943 insert: "S",
944 kind: SymbolKind(
945 Struct,
946 ),
947 },
948 CompletionItem {
949 label: "foo(…)",
950 source_range: 70..70,
951 delete: 70..70,
952 insert: "foo(${1:&mut s})$0",
953 kind: SymbolKind(
954 Function,
955 ),
956 lookup: "foo",
957 detail: "-> ()",
958 trigger_call_info: true,
959 },
960 CompletionItem {
961 label: "main()",
962 source_range: 70..70,
963 delete: 70..70,
964 insert: "main()$0",
965 kind: SymbolKind(
966 Function,
967 ),
968 lookup: "main",
969 detail: "-> ()",
970 },
971 CompletionItem {
972 label: "s",
973 source_range: 70..70,
974 delete: 70..70,
975 insert: "s",
976 kind: SymbolKind(
977 Local,
978 ),
979 detail: "S",
980 relevance: CompletionRelevance {
981 exact_name_match: true,
982 exact_type_match: false,
983 },
984 ref_match: "&mut ",
985 },
986 ]
987 "#]],
988 )
989 }
945} 990}