aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock1
-rw-r--r--crates/assists/src/utils.rs12
-rw-r--r--crates/completion/Cargo.toml1
-rw-r--r--crates/completion/src/completions/record.rs108
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 = [
253name = "completion" 253name = "completion"
254version = "0.0.0" 254version = "0.0.0"
255dependencies = [ 255dependencies = [
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
260pub mod default {
261 pub trait Default {
262 fn default() -> Self;
263 }
264}
265
260pub mod iter { 266pub 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
329pub mod prelude { 335pub 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]
333pub use prelude::*; 339pub 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"
14log = "0.4.8" 14log = "0.4.8"
15rustc-hash = "1.1.0" 15rustc-hash = "1.1.0"
16 16
17assists = { path = "../assists", version = "0.0.0" }
17stdx = { path = "../stdx", version = "0.0.0" } 18stdx = { path = "../stdx", version = "0.0.0" }
18syntax = { path = "../syntax", version = "0.0.0" } 19syntax = { path = "../syntax", version = "0.0.0" }
19text_edit = { path = "../text_edit", version = "0.0.0" } 20text_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.
2use crate::{CompletionContext, Completions}; 2use assists::utils::FamousDefs;
3use syntax::ast::Expr;
4
5use crate::{
6 item::CompletionKind, CompletionContext, CompletionItem, CompletionItemKind, Completions,
7};
3 8
4pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { 9pub(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)]
20mod tests { 47mod 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#"
69struct S { foo: u32, bar: usize }
70
71impl core::default::Default for S {
72 fn default() -> Self {
73 S {
74 foo: 0,
75 bar: 0,
76 }
77 }
78}
79
80fn 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#"
107struct S { foo: u32, bar: usize }
108
109fn 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(