aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Coenen <[email protected]>2020-04-15 21:10:57 +0100
committerBenjamin Coenen <[email protected]>2020-04-15 21:10:57 +0100
commit06076f95a7ca764696b055eb754e163f884eefaa (patch)
tree34c8978d4caa962e62c1e88fcdf6f7a0baaef80e
parentc5d18f570c9751ca9ec2db7c8530fd6095a6465d (diff)
feat: improve dot completions in a struct literal expression
Signed-off-by: Benjamin Coenen <[email protected]>
-rw-r--r--crates/ra_ide/src/completion.rs2
-rw-r--r--crates/ra_ide/src/completion/complete_dot.rs141
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs10
-rw-r--r--crates/ra_ide/src/completion/completion_item.rs74
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
3use std::{cmp::Ordering, fmt}; 3use std::{cmp::Ordering, fmt};
4 4
5use super::completion_context::CompletionContext;
5use crate::CallInfo; 6use crate::CallInfo;
6use hir::Documentation; 7use hir::{Documentation, HirDisplay};
7use ra_syntax::TextRange; 8use ra_syntax::{ast::RecordField, TextRange};
8use ra_text_edit::TextEdit; 9use 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)]
302pub(crate) enum SortOption { 303pub(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