diff options
-rw-r--r-- | Cargo.lock | 1 | ||||
-rw-r--r-- | crates/assists/src/utils.rs | 12 | ||||
-rw-r--r-- | crates/completion/Cargo.toml | 1 | ||||
-rw-r--r-- | crates/completion/src/completions/record.rs | 108 |
4 files changed, 118 insertions, 4 deletions
diff --git a/Cargo.lock b/Cargo.lock index 494011068..715a80978 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -253,6 +253,7 @@ dependencies = [ | |||
253 | name = "completion" | 253 | name = "completion" |
254 | version = "0.0.0" | 254 | version = "0.0.0" |
255 | dependencies = [ | 255 | dependencies = [ |
256 | "assists", | ||
256 | "base_db", | 257 | "base_db", |
257 | "expect-test", | 258 | "expect-test", |
258 | "hir", | 259 | "hir", |
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs index 7071fe96b..7bd338e99 100644 --- a/crates/assists/src/utils.rs +++ b/crates/assists/src/utils.rs | |||
@@ -257,6 +257,12 @@ pub mod convert { | |||
257 | } | 257 | } |
258 | } | 258 | } |
259 | 259 | ||
260 | pub mod default { | ||
261 | pub trait Default { | ||
262 | fn default() -> Self; | ||
263 | } | ||
264 | } | ||
265 | |||
260 | pub mod iter { | 266 | pub mod iter { |
261 | pub use self::traits::{collect::IntoIterator, iterator::Iterator}; | 267 | pub use self::traits::{collect::IntoIterator, iterator::Iterator}; |
262 | mod traits { | 268 | mod traits { |
@@ -327,7 +333,7 @@ pub mod option { | |||
327 | } | 333 | } |
328 | 334 | ||
329 | pub mod prelude { | 335 | pub mod prelude { |
330 | pub use crate::{convert::From, iter::{IntoIterator, Iterator}, option::Option::{self, *}}; | 336 | pub use crate::{convert::From, iter::{IntoIterator, Iterator}, option::Option::{self, *}, default::Default}; |
331 | } | 337 | } |
332 | #[prelude_import] | 338 | #[prelude_import] |
333 | pub use prelude::*; | 339 | pub use prelude::*; |
@@ -345,6 +351,10 @@ pub use prelude::*; | |||
345 | self.find_enum("core:option:Option") | 351 | self.find_enum("core:option:Option") |
346 | } | 352 | } |
347 | 353 | ||
354 | pub fn core_default_Default(&self) -> Option<Trait> { | ||
355 | self.find_trait("core:default:Default") | ||
356 | } | ||
357 | |||
348 | pub fn core_iter_Iterator(&self) -> Option<Trait> { | 358 | pub fn core_iter_Iterator(&self) -> Option<Trait> { |
349 | self.find_trait("core:iter:traits:iterator:Iterator") | 359 | self.find_trait("core:iter:traits:iterator:Iterator") |
350 | } | 360 | } |
diff --git a/crates/completion/Cargo.toml b/crates/completion/Cargo.toml index b79ee33f7..3015ec9e0 100644 --- a/crates/completion/Cargo.toml +++ b/crates/completion/Cargo.toml | |||
@@ -14,6 +14,7 @@ itertools = "0.9.0" | |||
14 | log = "0.4.8" | 14 | log = "0.4.8" |
15 | rustc-hash = "1.1.0" | 15 | rustc-hash = "1.1.0" |
16 | 16 | ||
17 | assists = { path = "../assists", version = "0.0.0" } | ||
17 | stdx = { path = "../stdx", version = "0.0.0" } | 18 | stdx = { path = "../stdx", version = "0.0.0" } |
18 | syntax = { path = "../syntax", version = "0.0.0" } | 19 | syntax = { path = "../syntax", version = "0.0.0" } |
19 | text_edit = { path = "../text_edit", version = "0.0.0" } | 20 | text_edit = { path = "../text_edit", version = "0.0.0" } |
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( |