diff options
Diffstat (limited to 'crates/ra_ide/src/completion/presentation.rs')
-rw-r--r-- | crates/ra_ide/src/completion/presentation.rs | 314 |
1 files changed, 273 insertions, 41 deletions
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs index f8dac1d54..78df9cbdb 100644 --- a/crates/ra_ide/src/completion/presentation.rs +++ b/crates/ra_ide/src/completion/presentation.rs | |||
@@ -6,7 +6,6 @@ use stdx::SepBy; | |||
6 | use test_utils::tested_by; | 6 | use test_utils::tested_by; |
7 | 7 | ||
8 | use crate::{ | 8 | use crate::{ |
9 | call_info::call_info, | ||
10 | completion::{ | 9 | completion::{ |
11 | completion_item::Builder, CompletionContext, CompletionItem, CompletionItemKind, | 10 | completion_item::Builder, CompletionContext, CompletionItem, CompletionItemKind, |
12 | CompletionKind, Completions, | 11 | CompletionKind, Completions, |
@@ -23,20 +22,20 @@ impl Completions { | |||
23 | ty: &Type, | 22 | ty: &Type, |
24 | ) { | 23 | ) { |
25 | let is_deprecated = is_deprecated(field, ctx.db); | 24 | let is_deprecated = is_deprecated(field, ctx.db); |
26 | let mut completion_item = CompletionItem::new( | 25 | let ty = ty.display(ctx.db).to_string(); |
27 | CompletionKind::Reference, | 26 | let name = field.name(ctx.db); |
28 | ctx.source_range(), | 27 | let mut completion_item = |
29 | field.name(ctx.db).to_string(), | 28 | CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.to_string()) |
30 | ) | 29 | .kind(CompletionItemKind::Field) |
31 | .kind(CompletionItemKind::Field) | 30 | .detail(ty.clone()) |
32 | .detail(ty.display(ctx.db).to_string()) | 31 | .set_documentation(field.docs(ctx.db)) |
33 | .set_documentation(field.docs(ctx.db)) | 32 | .set_deprecated(is_deprecated); |
34 | .set_deprecated(is_deprecated) | 33 | |
35 | .build(); | 34 | if let Some(score) = compute_score(ctx, &ty, &name.to_string()) { |
36 | 35 | completion_item = completion_item.set_score(score); | |
37 | compute_score(&mut completion_item, ctx); | 36 | } |
38 | 37 | ||
39 | self.add(completion_item); | 38 | completion_item.add_to(self); |
40 | } | 39 | } |
41 | 40 | ||
42 | pub(crate) fn add_tuple_field(&mut self, ctx: &CompletionContext, field: usize, ty: &Type) { | 41 | pub(crate) fn add_tuple_field(&mut self, ctx: &CompletionContext, field: usize, ty: &Type) { |
@@ -305,40 +304,40 @@ impl Completions { | |||
305 | } | 304 | } |
306 | } | 305 | } |
307 | 306 | ||
308 | pub(crate) fn compute_score(completion_item: &mut CompletionItem, ctx: &CompletionContext) { | 307 | pub(crate) fn compute_score( |
308 | ctx: &CompletionContext, | ||
309 | // FIXME: this definitely should be a `Type` | ||
310 | ty: &str, | ||
311 | name: &str, | ||
312 | ) -> Option<CompletionScore> { | ||
309 | let (active_name, active_type) = if let Some(record_field) = &ctx.record_field_syntax { | 313 | let (active_name, active_type) = if let Some(record_field) = &ctx.record_field_syntax { |
310 | if let Some((struct_field, _)) = ctx.sema.resolve_record_field(record_field) { | 314 | tested_by!(test_struct_field_completion_in_record_lit); |
311 | ( | 315 | let (struct_field, _local) = ctx.sema.resolve_record_field(record_field)?; |
312 | struct_field.name(ctx.db).to_string(), | 316 | ( |
313 | struct_field.signature_ty(ctx.db).display(ctx.db).to_string(), | 317 | struct_field.name(ctx.db).to_string(), |
314 | ) | 318 | struct_field.signature_ty(ctx.db).display(ctx.db).to_string(), |
315 | } else { | 319 | ) |
316 | return; | 320 | } else if let Some(active_parameter) = &ctx.active_parameter { |
317 | } | 321 | tested_by!(test_struct_field_completion_in_func_call); |
318 | } else if let Some(call_info) = call_info(ctx.db, ctx.file_position) { | 322 | (active_parameter.name.clone(), active_parameter.ty.clone()) |
319 | if call_info.active_parameter_type().is_some() | ||
320 | && call_info.active_parameter_name().is_some() | ||
321 | { | ||
322 | (call_info.active_parameter_name().unwrap(), call_info.active_parameter_type().unwrap()) | ||
323 | } else { | ||
324 | return; | ||
325 | } | ||
326 | } else { | 323 | } else { |
327 | return; | 324 | return None; |
328 | }; | 325 | }; |
329 | 326 | ||
330 | // Compute score | 327 | // Compute score |
331 | // For the same type | 328 | // For the same type |
332 | if let Some(a_parameter_type) = completion_item.detail() { | 329 | if &active_type != ty { |
333 | if &active_type == a_parameter_type { | 330 | return None; |
334 | // If same type + same name then go top position | 331 | } |
335 | if active_name == completion_item.label() { | 332 | |
336 | completion_item.set_score(CompletionScore::TypeAndNameMatch); | 333 | let mut res = CompletionScore::TypeMatch; |
337 | } else { | 334 | |
338 | completion_item.set_score(CompletionScore::TypeMatch); | 335 | // If same type + same name then go top position |
339 | } | 336 | if active_name == name { |
340 | } | 337 | res = CompletionScore::TypeAndNameMatch |
341 | } | 338 | } |
339 | |||
340 | Some(res) | ||
342 | } | 341 | } |
343 | 342 | ||
344 | enum Params { | 343 | enum Params { |
@@ -1072,4 +1071,237 @@ mod tests { | |||
1072 | "### | 1071 | "### |
1073 | ); | 1072 | ); |
1074 | } | 1073 | } |
1074 | |||
1075 | #[test] | ||
1076 | fn test_struct_field_completion_in_func_call() { | ||
1077 | covers!(test_struct_field_completion_in_func_call); | ||
1078 | assert_debug_snapshot!( | ||
1079 | do_reference_completion( | ||
1080 | r" | ||
1081 | struct A { another_field: i64, the_field: u32, my_string: String } | ||
1082 | fn test(my_param: u32) -> u32 { my_param } | ||
1083 | fn foo(a: A) { | ||
1084 | test(a.<|>) | ||
1085 | } | ||
1086 | ", | ||
1087 | ), | ||
1088 | @r###" | ||
1089 | [ | ||
1090 | CompletionItem { | ||
1091 | label: "another_field", | ||
1092 | source_range: [201; 201), | ||
1093 | delete: [201; 201), | ||
1094 | insert: "another_field", | ||
1095 | kind: Field, | ||
1096 | detail: "i64", | ||
1097 | }, | ||
1098 | CompletionItem { | ||
1099 | label: "my_string", | ||
1100 | source_range: [201; 201), | ||
1101 | delete: [201; 201), | ||
1102 | insert: "my_string", | ||
1103 | kind: Field, | ||
1104 | detail: "{unknown}", | ||
1105 | }, | ||
1106 | CompletionItem { | ||
1107 | label: "the_field", | ||
1108 | source_range: [201; 201), | ||
1109 | delete: [201; 201), | ||
1110 | insert: "the_field", | ||
1111 | kind: Field, | ||
1112 | detail: "u32", | ||
1113 | score: TypeMatch, | ||
1114 | }, | ||
1115 | ] | ||
1116 | "### | ||
1117 | ); | ||
1118 | } | ||
1119 | |||
1120 | #[test] | ||
1121 | fn test_struct_field_completion_in_func_call_with_type_and_name() { | ||
1122 | assert_debug_snapshot!( | ||
1123 | do_reference_completion( | ||
1124 | r" | ||
1125 | struct A { another_field: i64, another_good_type: u32, the_field: u32 } | ||
1126 | fn test(the_field: u32) -> u32 { the_field } | ||
1127 | fn foo(a: A) { | ||
1128 | test(a.<|>) | ||
1129 | } | ||
1130 | ", | ||
1131 | ), | ||
1132 | @r###" | ||
1133 | [ | ||
1134 | CompletionItem { | ||
1135 | label: "another_field", | ||
1136 | source_range: [208; 208), | ||
1137 | delete: [208; 208), | ||
1138 | insert: "another_field", | ||
1139 | kind: Field, | ||
1140 | detail: "i64", | ||
1141 | }, | ||
1142 | CompletionItem { | ||
1143 | label: "another_good_type", | ||
1144 | source_range: [208; 208), | ||
1145 | delete: [208; 208), | ||
1146 | insert: "another_good_type", | ||
1147 | kind: Field, | ||
1148 | detail: "u32", | ||
1149 | score: TypeMatch, | ||
1150 | }, | ||
1151 | CompletionItem { | ||
1152 | label: "the_field", | ||
1153 | source_range: [208; 208), | ||
1154 | delete: [208; 208), | ||
1155 | insert: "the_field", | ||
1156 | kind: Field, | ||
1157 | detail: "u32", | ||
1158 | score: TypeAndNameMatch, | ||
1159 | }, | ||
1160 | ] | ||
1161 | "### | ||
1162 | ); | ||
1163 | } | ||
1164 | |||
1165 | #[test] | ||
1166 | fn test_struct_field_completion_in_record_lit() { | ||
1167 | covers!(test_struct_field_completion_in_func_call); | ||
1168 | assert_debug_snapshot!( | ||
1169 | do_reference_completion( | ||
1170 | r" | ||
1171 | struct A { another_field: i64, another_good_type: u32, the_field: u32 } | ||
1172 | struct B { my_string: String, my_vec: Vec<u32>, the_field: u32 } | ||
1173 | fn foo(a: A) { | ||
1174 | let b = B { | ||
1175 | the_field: a.<|> | ||
1176 | }; | ||
1177 | } | ||
1178 | ", | ||
1179 | ), | ||
1180 | @r###" | ||
1181 | [ | ||
1182 | CompletionItem { | ||
1183 | label: "another_field", | ||
1184 | source_range: [270; 270), | ||
1185 | delete: [270; 270), | ||
1186 | insert: "another_field", | ||
1187 | kind: Field, | ||
1188 | detail: "i64", | ||
1189 | }, | ||
1190 | CompletionItem { | ||
1191 | label: "another_good_type", | ||
1192 | source_range: [270; 270), | ||
1193 | delete: [270; 270), | ||
1194 | insert: "another_good_type", | ||
1195 | kind: Field, | ||
1196 | detail: "u32", | ||
1197 | score: TypeMatch, | ||
1198 | }, | ||
1199 | CompletionItem { | ||
1200 | label: "the_field", | ||
1201 | source_range: [270; 270), | ||
1202 | delete: [270; 270), | ||
1203 | insert: "the_field", | ||
1204 | kind: Field, | ||
1205 | detail: "u32", | ||
1206 | score: TypeAndNameMatch, | ||
1207 | }, | ||
1208 | ] | ||
1209 | "### | ||
1210 | ); | ||
1211 | } | ||
1212 | |||
1213 | #[test] | ||
1214 | fn test_struct_field_completion_in_record_lit_and_fn_call() { | ||
1215 | assert_debug_snapshot!( | ||
1216 | do_reference_completion( | ||
1217 | r" | ||
1218 | struct A { another_field: i64, another_good_type: u32, the_field: u32 } | ||
1219 | struct B { my_string: String, my_vec: Vec<u32>, the_field: u32 } | ||
1220 | fn test(the_field: i64) -> i64 { the_field } | ||
1221 | fn foo(a: A) { | ||
1222 | let b = B { | ||
1223 | the_field: test(a.<|>) | ||
1224 | }; | ||
1225 | } | ||
1226 | ", | ||
1227 | ), | ||
1228 | @r###" | ||
1229 | [ | ||
1230 | CompletionItem { | ||
1231 | label: "another_field", | ||
1232 | source_range: [336; 336), | ||
1233 | delete: [336; 336), | ||
1234 | insert: "another_field", | ||
1235 | kind: Field, | ||
1236 | detail: "i64", | ||
1237 | score: TypeMatch, | ||
1238 | }, | ||
1239 | CompletionItem { | ||
1240 | label: "another_good_type", | ||
1241 | source_range: [336; 336), | ||
1242 | delete: [336; 336), | ||
1243 | insert: "another_good_type", | ||
1244 | kind: Field, | ||
1245 | detail: "u32", | ||
1246 | }, | ||
1247 | CompletionItem { | ||
1248 | label: "the_field", | ||
1249 | source_range: [336; 336), | ||
1250 | delete: [336; 336), | ||
1251 | insert: "the_field", | ||
1252 | kind: Field, | ||
1253 | detail: "u32", | ||
1254 | }, | ||
1255 | ] | ||
1256 | "### | ||
1257 | ); | ||
1258 | } | ||
1259 | |||
1260 | #[test] | ||
1261 | fn test_struct_field_completion_in_fn_call_and_record_lit() { | ||
1262 | assert_debug_snapshot!( | ||
1263 | do_reference_completion( | ||
1264 | r" | ||
1265 | struct A { another_field: i64, another_good_type: u32, the_field: u32 } | ||
1266 | struct B { my_string: String, my_vec: Vec<u32>, the_field: u32 } | ||
1267 | fn test(the_field: i64) -> i64 { the_field } | ||
1268 | fn foo(a: A) { | ||
1269 | test(B { | ||
1270 | the_field: a.<|> | ||
1271 | }); | ||
1272 | } | ||
1273 | ", | ||
1274 | ), | ||
1275 | @r###" | ||
1276 | [ | ||
1277 | CompletionItem { | ||
1278 | label: "another_field", | ||
1279 | source_range: [328; 328), | ||
1280 | delete: [328; 328), | ||
1281 | insert: "another_field", | ||
1282 | kind: Field, | ||
1283 | detail: "i64", | ||
1284 | }, | ||
1285 | CompletionItem { | ||
1286 | label: "another_good_type", | ||
1287 | source_range: [328; 328), | ||
1288 | delete: [328; 328), | ||
1289 | insert: "another_good_type", | ||
1290 | kind: Field, | ||
1291 | detail: "u32", | ||
1292 | score: TypeMatch, | ||
1293 | }, | ||
1294 | CompletionItem { | ||
1295 | label: "the_field", | ||
1296 | source_range: [328; 328), | ||
1297 | delete: [328; 328), | ||
1298 | insert: "the_field", | ||
1299 | kind: Field, | ||
1300 | detail: "u32", | ||
1301 | score: TypeAndNameMatch, | ||
1302 | }, | ||
1303 | ] | ||
1304 | "### | ||
1305 | ); | ||
1306 | } | ||
1075 | } | 1307 | } |