diff options
Diffstat (limited to 'crates/completion/src/completions')
-rw-r--r-- | crates/completion/src/completions/record.rs | 108 |
1 files changed, 105 insertions, 3 deletions
diff --git a/crates/completion/src/completions/record.rs b/crates/completion/src/completions/record.rs index 0f611084b..2049b9d09 100644 --- a/crates/completion/src/completions/record.rs +++ b/crates/completion/src/completions/record.rs | |||
@@ -1,16 +1,43 @@ | |||
1 | //! Complete fields in record literals and patterns. | 1 | //! Complete fields in record literals and patterns. |
2 | use crate::{CompletionContext, Completions}; | 2 | use assists::utils::FamousDefs; |
3 | use syntax::ast::Expr; | ||
4 | |||
5 | use crate::{ | ||
6 | item::CompletionKind, CompletionContext, CompletionItem, CompletionItemKind, Completions, | ||
7 | }; | ||
3 | 8 | ||
4 | pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { | 9 | pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { |
5 | let missing_fields = match (ctx.record_pat_syntax.as_ref(), ctx.record_lit_syntax.as_ref()) { | 10 | let missing_fields = match (ctx.record_pat_syntax.as_ref(), ctx.record_lit_syntax.as_ref()) { |
6 | (None, None) => return None, | 11 | (None, None) => return None, |
7 | (Some(_), Some(_)) => unreachable!("A record cannot be both a literal and a pattern"), | 12 | (Some(_), Some(_)) => unreachable!("A record cannot be both a literal and a pattern"), |
8 | (Some(record_pat), _) => ctx.sema.record_pattern_missing_fields(record_pat), | 13 | (Some(record_pat), _) => ctx.sema.record_pattern_missing_fields(record_pat), |
9 | (_, Some(record_lit)) => ctx.sema.record_literal_missing_fields(record_lit), | 14 | (_, Some(record_lit)) => { |
15 | let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_lit.clone())); | ||
16 | let default_trait = FamousDefs(&ctx.sema, ctx.krate).core_default_Default(); | ||
17 | let impl_default_trait = default_trait | ||
18 | .and_then(|default_trait| ty.map(|ty| ty.impls_trait(ctx.db, default_trait, &[]))) | ||
19 | .unwrap_or(false); | ||
20 | |||
21 | let missing_fields = ctx.sema.record_literal_missing_fields(record_lit); | ||
22 | if impl_default_trait && !missing_fields.is_empty() { | ||
23 | acc.add( | ||
24 | CompletionItem::new( | ||
25 | CompletionKind::Snippet, | ||
26 | ctx.source_range(), | ||
27 | "..Default::default()", | ||
28 | ) | ||
29 | .insert_text("..Default::default()") | ||
30 | .kind(CompletionItemKind::Field) | ||
31 | .build(), | ||
32 | ); | ||
33 | } | ||
34 | |||
35 | missing_fields | ||
36 | } | ||
10 | }; | 37 | }; |
11 | 38 | ||
12 | for (field, ty) in missing_fields { | 39 | for (field, ty) in missing_fields { |
13 | acc.add_field(ctx, field, &ty) | 40 | acc.add_field(ctx, field, &ty); |
14 | } | 41 | } |
15 | 42 | ||
16 | Some(()) | 43 | Some(()) |
@@ -18,6 +45,7 @@ pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> | |||
18 | 45 | ||
19 | #[cfg(test)] | 46 | #[cfg(test)] |
20 | mod tests { | 47 | mod tests { |
48 | use assists::utils::FamousDefs; | ||
21 | use expect_test::{expect, Expect}; | 49 | use expect_test::{expect, Expect}; |
22 | 50 | ||
23 | use crate::{test_utils::completion_list, CompletionKind}; | 51 | use crate::{test_utils::completion_list, CompletionKind}; |
@@ -27,6 +55,80 @@ mod tests { | |||
27 | expect.assert_eq(&actual); | 55 | expect.assert_eq(&actual); |
28 | } | 56 | } |
29 | 57 | ||
58 | fn check_snippet(ra_fixture: &str, expect: Expect) { | ||
59 | let actual = completion_list( | ||
60 | &format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE), | ||
61 | CompletionKind::Snippet, | ||
62 | ); | ||
63 | expect.assert_eq(&actual); | ||
64 | } | ||
65 | |||
66 | #[test] | ||
67 | fn test_record_literal_field_default() { | ||
68 | let test_code = r#" | ||
69 | struct S { foo: u32, bar: usize } | ||
70 | |||
71 | impl core::default::Default for S { | ||
72 | fn default() -> Self { | ||
73 | S { | ||
74 | foo: 0, | ||
75 | bar: 0, | ||
76 | } | ||
77 | } | ||
78 | } | ||
79 | |||
80 | fn process(f: S) { | ||
81 | let other = S { | ||
82 | foo: 5, | ||
83 | .<|> | ||
84 | }; | ||
85 | } | ||
86 | "#; | ||
87 | check( | ||
88 | test_code, | ||
89 | expect![[r#" | ||
90 | fd bar usize | ||
91 | "#]], | ||
92 | ); | ||
93 | |||
94 | check_snippet( | ||
95 | test_code, | ||
96 | expect![[r#" | ||
97 | fd ..Default::default() | ||
98 | sn pd | ||
99 | sn ppd | ||
100 | "#]], | ||
101 | ); | ||
102 | } | ||
103 | |||
104 | #[test] | ||
105 | fn test_record_literal_field_without_default() { | ||
106 | let test_code = r#" | ||
107 | struct S { foo: u32, bar: usize } | ||
108 | |||
109 | fn process(f: S) { | ||
110 | let other = S { | ||
111 | foo: 5, | ||
112 | .<|> | ||
113 | }; | ||
114 | } | ||
115 | "#; | ||
116 | check( | ||
117 | test_code, | ||
118 | expect![[r#" | ||
119 | fd bar usize | ||
120 | "#]], | ||
121 | ); | ||
122 | |||
123 | check_snippet( | ||
124 | test_code, | ||
125 | expect![[r#" | ||
126 | sn pd | ||
127 | sn ppd | ||
128 | "#]], | ||
129 | ); | ||
130 | } | ||
131 | |||
30 | #[test] | 132 | #[test] |
31 | fn test_record_pattern_field() { | 133 | fn test_record_pattern_field() { |
32 | check( | 134 | check( |