diff options
author | Benjamin Coenen <[email protected]> | 2020-04-15 21:10:57 +0100 |
---|---|---|
committer | Benjamin Coenen <[email protected]> | 2020-04-15 21:10:57 +0100 |
commit | 06076f95a7ca764696b055eb754e163f884eefaa (patch) | |
tree | 34c8978d4caa962e62c1e88fcdf6f7a0baaef80e /crates/ra_ide/src | |
parent | c5d18f570c9751ca9ec2db7c8530fd6095a6465d (diff) |
feat: improve dot completions in a struct literal expression
Signed-off-by: Benjamin Coenen <[email protected]>
Diffstat (limited to 'crates/ra_ide/src')
-rw-r--r-- | crates/ra_ide/src/completion.rs | 2 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/complete_dot.rs | 141 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/completion_context.rs | 10 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/completion_item.rs | 74 |
4 files changed, 196 insertions, 31 deletions
diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs index 3be8b1903..185450508 100644 --- a/crates/ra_ide/src/completion.rs +++ b/crates/ra_ide/src/completion.rs | |||
@@ -95,7 +95,7 @@ pub(crate) fn completions( | |||
95 | complete_trait_impl::complete_trait_impl(&mut acc, &ctx); | 95 | complete_trait_impl::complete_trait_impl(&mut acc, &ctx); |
96 | 96 | ||
97 | // Reorder completion items if there is a sort_option | 97 | // Reorder completion items if there is a sort_option |
98 | acc.sort(); | 98 | acc.sort(&ctx); |
99 | 99 | ||
100 | Some(acc) | 100 | Some(acc) |
101 | } | 101 | } |
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs index cb899d8ff..2e228b638 100644 --- a/crates/ra_ide/src/completion/complete_dot.rs +++ b/crates/ra_ide/src/completion/complete_dot.rs | |||
@@ -50,7 +50,9 @@ fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Ty | |||
50 | let fields = receiver.fields(ctx.db); | 50 | let fields = receiver.fields(ctx.db); |
51 | 51 | ||
52 | // If we use this implementation we can delete call_info in the CompletionContext | 52 | // If we use this implementation we can delete call_info in the CompletionContext |
53 | if let Some(call_info) = call_info(ctx.db, ctx.file_position) { | 53 | if let Some(record_field) = &ctx.record_field_syntax { |
54 | acc.with_sort_option(SortOption::RecordField(record_field.clone())); | ||
55 | } else if let Some(call_info) = call_info(ctx.db, ctx.file_position) { | ||
54 | acc.with_sort_option(SortOption::CallFn(call_info)); | 56 | acc.with_sort_option(SortOption::CallFn(call_info)); |
55 | } | 57 | } |
56 | 58 | ||
@@ -241,6 +243,143 @@ mod tests { | |||
241 | } | 243 | } |
242 | 244 | ||
243 | #[test] | 245 | #[test] |
246 | fn test_struct_field_completion_in_record_lit() { | ||
247 | assert_debug_snapshot!( | ||
248 | do_ref_completion_without_sort( | ||
249 | r" | ||
250 | struct A { another_field: i64, another_good_type: u32, the_field: u32 } | ||
251 | struct B { my_string: String, my_vec: Vec<u32>, the_field: u32 } | ||
252 | fn foo(a: A) { | ||
253 | let b = B { | ||
254 | the_field: a.<|> | ||
255 | }; | ||
256 | } | ||
257 | ", | ||
258 | ), | ||
259 | @r###" | ||
260 | [ | ||
261 | CompletionItem { | ||
262 | label: "the_field", | ||
263 | source_range: [270; 270), | ||
264 | delete: [270; 270), | ||
265 | insert: "the_field", | ||
266 | kind: Field, | ||
267 | detail: "u32", | ||
268 | }, | ||
269 | CompletionItem { | ||
270 | label: "another_good_type", | ||
271 | source_range: [270; 270), | ||
272 | delete: [270; 270), | ||
273 | insert: "another_good_type", | ||
274 | kind: Field, | ||
275 | detail: "u32", | ||
276 | }, | ||
277 | CompletionItem { | ||
278 | label: "another_field", | ||
279 | source_range: [270; 270), | ||
280 | delete: [270; 270), | ||
281 | insert: "another_field", | ||
282 | kind: Field, | ||
283 | detail: "i64", | ||
284 | }, | ||
285 | ] | ||
286 | "### | ||
287 | ); | ||
288 | } | ||
289 | |||
290 | #[test] | ||
291 | fn test_struct_field_completion_in_record_lit_and_fn_call() { | ||
292 | assert_debug_snapshot!( | ||
293 | do_ref_completion_without_sort( | ||
294 | r" | ||
295 | struct A { another_field: i64, another_good_type: u32, the_field: u32 } | ||
296 | struct B { my_string: String, my_vec: Vec<u32>, the_field: u32 } | ||
297 | fn test(the_field: i64) -> i64 { the_field } | ||
298 | fn foo(a: A) { | ||
299 | let b = B { | ||
300 | the_field: test(a.<|>) | ||
301 | }; | ||
302 | } | ||
303 | ", | ||
304 | ), | ||
305 | @r###" | ||
306 | [ | ||
307 | CompletionItem { | ||
308 | label: "another_field", | ||
309 | source_range: [336; 336), | ||
310 | delete: [336; 336), | ||
311 | insert: "another_field", | ||
312 | kind: Field, | ||
313 | detail: "i64", | ||
314 | }, | ||
315 | CompletionItem { | ||
316 | label: "another_good_type", | ||
317 | source_range: [336; 336), | ||
318 | delete: [336; 336), | ||
319 | insert: "another_good_type", | ||
320 | kind: Field, | ||
321 | detail: "u32", | ||
322 | }, | ||
323 | CompletionItem { | ||
324 | label: "the_field", | ||
325 | source_range: [336; 336), | ||
326 | delete: [336; 336), | ||
327 | insert: "the_field", | ||
328 | kind: Field, | ||
329 | detail: "u32", | ||
330 | }, | ||
331 | ] | ||
332 | "### | ||
333 | ); | ||
334 | } | ||
335 | |||
336 | #[test] | ||
337 | fn test_struct_field_completion_in_fn_call_and_record_lit() { | ||
338 | assert_debug_snapshot!( | ||
339 | do_ref_completion_without_sort( | ||
340 | r" | ||
341 | struct A { another_field: i64, another_good_type: u32, the_field: u32 } | ||
342 | struct B { my_string: String, my_vec: Vec<u32>, the_field: u32 } | ||
343 | fn test(the_field: i64) -> i64 { the_field } | ||
344 | fn foo(a: A) { | ||
345 | test(B { | ||
346 | the_field: a.<|> | ||
347 | }); | ||
348 | } | ||
349 | ", | ||
350 | ), | ||
351 | @r###" | ||
352 | [ | ||
353 | CompletionItem { | ||
354 | label: "the_field", | ||
355 | source_range: [328; 328), | ||
356 | delete: [328; 328), | ||
357 | insert: "the_field", | ||
358 | kind: Field, | ||
359 | detail: "u32", | ||
360 | }, | ||
361 | CompletionItem { | ||
362 | label: "another_good_type", | ||
363 | source_range: [328; 328), | ||
364 | delete: [328; 328), | ||
365 | insert: "another_good_type", | ||
366 | kind: Field, | ||
367 | detail: "u32", | ||
368 | }, | ||
369 | CompletionItem { | ||
370 | label: "another_field", | ||
371 | source_range: [328; 328), | ||
372 | delete: [328; 328), | ||
373 | insert: "another_field", | ||
374 | kind: Field, | ||
375 | detail: "i64", | ||
376 | }, | ||
377 | ] | ||
378 | "### | ||
379 | ); | ||
380 | } | ||
381 | |||
382 | #[test] | ||
244 | fn test_struct_field_completion_self() { | 383 | fn test_struct_field_completion_self() { |
245 | assert_debug_snapshot!( | 384 | assert_debug_snapshot!( |
246 | do_ref_completion( | 385 | do_ref_completion( |
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index da054f7a2..dd9cc7daa 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs | |||
@@ -32,6 +32,7 @@ pub(crate) struct CompletionContext<'a> { | |||
32 | pub(super) use_item_syntax: Option<ast::UseItem>, | 32 | pub(super) use_item_syntax: Option<ast::UseItem>, |
33 | pub(super) record_lit_syntax: Option<ast::RecordLit>, | 33 | pub(super) record_lit_syntax: Option<ast::RecordLit>, |
34 | pub(super) record_pat_syntax: Option<ast::RecordPat>, | 34 | pub(super) record_pat_syntax: Option<ast::RecordPat>, |
35 | pub(super) record_field_syntax: Option<ast::RecordField>, | ||
35 | pub(super) impl_def: Option<ast::ImplDef>, | 36 | pub(super) impl_def: Option<ast::ImplDef>, |
36 | pub(super) call_info: Option<CallInfo>, | 37 | pub(super) call_info: Option<CallInfo>, |
37 | pub(super) is_param: bool, | 38 | pub(super) is_param: bool, |
@@ -98,6 +99,7 @@ impl<'a> CompletionContext<'a> { | |||
98 | use_item_syntax: None, | 99 | use_item_syntax: None, |
99 | record_lit_syntax: None, | 100 | record_lit_syntax: None, |
100 | record_pat_syntax: None, | 101 | record_pat_syntax: None, |
102 | record_field_syntax: None, | ||
101 | impl_def: None, | 103 | impl_def: None, |
102 | is_param: false, | 104 | is_param: false, |
103 | is_pat_binding_or_const: false, | 105 | is_pat_binding_or_const: false, |
@@ -274,6 +276,14 @@ impl<'a> CompletionContext<'a> { | |||
274 | .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) | 276 | .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) |
275 | .find_map(ast::FnDef::cast); | 277 | .find_map(ast::FnDef::cast); |
276 | 278 | ||
279 | self.record_field_syntax = self | ||
280 | .sema | ||
281 | .ancestors_with_macros(self.token.parent()) | ||
282 | .take_while(|it| { | ||
283 | it.kind() != SOURCE_FILE && it.kind() != MODULE && it.kind() != CALL_EXPR | ||
284 | }) | ||
285 | .find_map(ast::RecordField::cast); | ||
286 | |||
277 | let parent = match name_ref.syntax().parent() { | 287 | let parent = match name_ref.syntax().parent() { |
278 | Some(it) => it, | 288 | Some(it) => it, |
279 | None => return, | 289 | None => return, |
diff --git a/crates/ra_ide/src/completion/completion_item.rs b/crates/ra_ide/src/completion/completion_item.rs index f8e6e53f1..c9c3fdc0e 100644 --- a/crates/ra_ide/src/completion/completion_item.rs +++ b/crates/ra_ide/src/completion/completion_item.rs | |||
@@ -2,9 +2,10 @@ | |||
2 | 2 | ||
3 | use std::{cmp::Ordering, fmt}; | 3 | use std::{cmp::Ordering, fmt}; |
4 | 4 | ||
5 | use super::completion_context::CompletionContext; | ||
5 | use crate::CallInfo; | 6 | use crate::CallInfo; |
6 | use hir::Documentation; | 7 | use hir::{Documentation, HirDisplay}; |
7 | use ra_syntax::TextRange; | 8 | use ra_syntax::{ast::RecordField, TextRange}; |
8 | use ra_text_edit::TextEdit; | 9 | use ra_text_edit::TextEdit; |
9 | 10 | ||
10 | /// `CompletionItem` describes a single completion variant in the editor pop-up. | 11 | /// `CompletionItem` describes a single completion variant in the editor pop-up. |
@@ -301,7 +302,7 @@ impl<'a> Into<CompletionItem> for Builder { | |||
301 | #[derive(Debug)] | 302 | #[derive(Debug)] |
302 | pub(crate) enum SortOption { | 303 | pub(crate) enum SortOption { |
303 | CallFn(CallInfo), | 304 | CallFn(CallInfo), |
304 | // LitStruct, | 305 | RecordField(RecordField), |
305 | } | 306 | } |
306 | 307 | ||
307 | /// Represents an in-progress set of completions being built. | 308 | /// Represents an in-progress set of completions being built. |
@@ -327,40 +328,55 @@ impl Completions { | |||
327 | self.sort_option = Some(sort_option); | 328 | self.sort_option = Some(sort_option); |
328 | } | 329 | } |
329 | 330 | ||
330 | pub(crate) fn sort(&mut self) { | 331 | pub(crate) fn sort(&mut self, ctx: &CompletionContext) { |
331 | if self.sort_option.is_none() { | 332 | if self.sort_option.is_none() { |
332 | return; | 333 | return; |
333 | } | 334 | } |
334 | let sort_option = self.sort_option.as_ref().unwrap(); | ||
335 | 335 | ||
336 | match sort_option { | 336 | let (active_name, active_type) = match self.sort_option.as_ref().unwrap() { |
337 | SortOption::CallFn(call_info) => { | 337 | SortOption::CallFn(call_info) => { |
338 | if let Some(active_parameter_type) = call_info.active_parameter_type() { | 338 | if call_info.active_parameter_type().is_none() |
339 | let active_parameter_name = call_info.active_parameter_name().unwrap(); | 339 | || call_info.active_parameter_name().is_none() |
340 | 340 | { | |
341 | self.buf.sort_by(|a, b| { | 341 | return; |
342 | // For the same type | 342 | } |
343 | if let Some(a_parameter_type) = &a.detail { | 343 | ( |
344 | if &active_parameter_type == a_parameter_type { | 344 | call_info.active_parameter_name().unwrap(), |
345 | // If same type + same name then go top position | 345 | call_info.active_parameter_type().unwrap(), |
346 | if active_parameter_name != a.label { | 346 | ) |
347 | if let Some(b_parameter_type) = &b.detail { | 347 | } |
348 | if &active_parameter_type == b_parameter_type { | 348 | SortOption::RecordField(record_field) => { |
349 | return Ordering::Equal; | 349 | if let Some((struct_field, _)) = ctx.sema.resolve_record_field(record_field) { |
350 | } | 350 | ( |
351 | } | 351 | struct_field.name(ctx.db).to_string(), |
352 | } | 352 | struct_field.signature_ty(ctx.db).display(ctx.db).to_string(), |
353 | Ordering::Less | 353 | ) |
354 | } else { | 354 | } else { |
355 | Ordering::Greater | 355 | return; |
356 | } | ||
357 | } | ||
358 | }; | ||
359 | |||
360 | self.buf.sort_by(|a, b| { | ||
361 | // For the same type | ||
362 | if let Some(a_parameter_type) = &a.detail { | ||
363 | if &active_type == a_parameter_type { | ||
364 | // If same type + same name then go top position | ||
365 | if active_name != a.label { | ||
366 | if let Some(b_parameter_type) = &b.detail { | ||
367 | if &active_type == b_parameter_type { | ||
368 | return Ordering::Equal; | ||
356 | } | 369 | } |
357 | } else { | ||
358 | Ordering::Greater | ||
359 | } | 370 | } |
360 | }); | 371 | } |
372 | Ordering::Less | ||
373 | } else { | ||
374 | Ordering::Greater | ||
361 | } | 375 | } |
362 | } // _ => unimplemented!("sort options not already implemented"), | 376 | } else { |
363 | } | 377 | Ordering::Greater |
378 | } | ||
379 | }); | ||
364 | } | 380 | } |
365 | } | 381 | } |
366 | 382 | ||