Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 6f4354f

Browse files
committed
add assist for filling fields by replacing ellipsis in record syntax
1 parent 03b3cb6 commit 6f4354f

File tree

3 files changed

+259
-0
lines changed

3 files changed

+259
-0
lines changed
Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
use syntax::{
2+
ast::{self, make},
3+
AstNode,
4+
};
5+
6+
use crate::{AssistContext, AssistId, Assists};
7+
8+
// Assist: fill_record_pattern_fields
9+
//
10+
// Fills fields by replacing rest pattern in record patterns.
11+
//
12+
// ```
13+
// struct Bar { y: Y, z: Z }
14+
//
15+
// fn foo(bar: Bar) {
16+
// let Bar { ..$0 } = bar;
17+
// }
18+
// ```
19+
// ->
20+
// ```
21+
// struct Bar { y: Y, z: Z }
22+
//
23+
// fn foo(bar: Bar) {
24+
// let Bar { y, z } = bar;
25+
// }
26+
// ```
27+
pub(crate) fn fill_record_pattern_fields(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
28+
let record_pat = ctx.find_node_at_offset::<ast::RecordPat>()?;
29+
30+
let ellipsis = record_pat.record_pat_field_list().and_then(|r| r.rest_pat())?;
31+
if !ellipsis.syntax().text_range().contains_inclusive(ctx.offset()) {
32+
return None;
33+
}
34+
35+
let target_range = ellipsis.syntax().text_range();
36+
37+
let missing_fields = ctx.sema.record_pattern_missing_fields(&record_pat);
38+
39+
let old_field_list = record_pat.record_pat_field_list()?;
40+
let new_field_list = make::record_pat_field_list(old_field_list.fields()).clone_for_update();
41+
for (f, _) in missing_fields.iter() {
42+
let field =
43+
make::record_pat_field_shorthand(make::name_ref(&f.name(ctx.sema.db).to_smol_str()));
44+
new_field_list.add_field(field.clone_for_update());
45+
}
46+
47+
let old_range = ctx.sema.original_range_opt(old_field_list.syntax())?;
48+
if old_range.file_id != ctx.file_id() {
49+
return None;
50+
}
51+
52+
acc.add(
53+
AssistId("fill_record_pattern_fields", crate::AssistKind::RefactorRewrite),
54+
"Fill structure fields",
55+
target_range,
56+
move |builder| builder.replace_ast(old_field_list, new_field_list),
57+
)
58+
}
59+
60+
#[cfg(test)]
61+
mod tests {
62+
use super::*;
63+
use crate::tests::{check_assist, check_assist_not_applicable};
64+
65+
#[test]
66+
fn fill_fields_enum_with_only_ellipsis() {
67+
check_assist(
68+
fill_record_pattern_fields,
69+
r#"
70+
enum Foo {
71+
A(X),
72+
B{y: Y, z: Z}
73+
}
74+
75+
fn bar(foo: Foo) {
76+
match foo {
77+
Foo::A(_) => false,
78+
Foo::B{ ..$0 } => true,
79+
};
80+
}
81+
"#,
82+
r#"
83+
enum Foo {
84+
A(X),
85+
B{y: Y, z: Z}
86+
}
87+
88+
fn bar(foo: Foo) {
89+
match foo {
90+
Foo::A(_) => false,
91+
Foo::B{ y, z } => true,
92+
};
93+
}
94+
"#,
95+
)
96+
}
97+
98+
#[test]
99+
fn fill_fields_enum_with_fields() {
100+
check_assist(
101+
fill_record_pattern_fields,
102+
r#"
103+
enum Foo {
104+
A(X),
105+
B{y: Y, z: Z}
106+
}
107+
108+
fn bar(foo: Foo) {
109+
match foo {
110+
Foo::A(_) => false,
111+
Foo::B{ y, ..$0 } => true,
112+
};
113+
}
114+
"#,
115+
r#"
116+
enum Foo {
117+
A(X),
118+
B{y: Y, z: Z}
119+
}
120+
121+
fn bar(foo: Foo) {
122+
match foo {
123+
Foo::A(_) => false,
124+
Foo::B{ y, z } => true,
125+
};
126+
}
127+
"#,
128+
)
129+
}
130+
131+
#[test]
132+
fn fill_fields_struct_with_only_ellipsis() {
133+
check_assist(
134+
fill_record_pattern_fields,
135+
r#"
136+
struct Bar {
137+
y: Y,
138+
z: Z,
139+
}
140+
141+
fn foo(bar: Bar) {
142+
let Bar { ..$0 } = bar;
143+
}
144+
"#,
145+
r#"
146+
struct Bar {
147+
y: Y,
148+
z: Z,
149+
}
150+
151+
fn foo(bar: Bar) {
152+
let Bar { y, z } = bar;
153+
}
154+
"#,
155+
)
156+
}
157+
158+
#[test]
159+
fn fill_fields_struct_with_fields() {
160+
check_assist(
161+
fill_record_pattern_fields,
162+
r#"
163+
struct Bar {
164+
y: Y,
165+
z: Z,
166+
}
167+
168+
fn foo(bar: Bar) {
169+
let Bar { y, ..$0 } = bar;
170+
}
171+
"#,
172+
r#"
173+
struct Bar {
174+
y: Y,
175+
z: Z,
176+
}
177+
178+
fn foo(bar: Bar) {
179+
let Bar { y, z } = bar;
180+
}
181+
"#,
182+
)
183+
}
184+
185+
#[test]
186+
fn not_applicable_when_not_in_ellipsis() {
187+
check_assist_not_applicable(
188+
fill_record_pattern_fields,
189+
r#"
190+
enum Foo {
191+
A(X),
192+
B{y: Y, z: Z}
193+
}
194+
195+
fn bar(foo: Foo) {
196+
match foo {
197+
Foo::A(_) => false,
198+
Foo::B{..}$0 => true,
199+
};
200+
}
201+
"#,
202+
);
203+
check_assist_not_applicable(
204+
fill_record_pattern_fields,
205+
r#"
206+
enum Foo {
207+
A(X),
208+
B{y: Y, z: Z}
209+
}
210+
211+
fn bar(foo: Foo) {
212+
match foo {
213+
Foo::A(_) => false,
214+
Foo::B$0{..} => true,
215+
};
216+
}
217+
"#,
218+
);
219+
check_assist_not_applicable(
220+
fill_record_pattern_fields,
221+
r#"
222+
enum Foo {
223+
A(X),
224+
B{y: Y, z: Z}
225+
}
226+
227+
fn bar(foo: Foo) {
228+
match foo {
229+
Foo::A(_) => false,
230+
Foo::$0B{..} => true,
231+
};
232+
}
233+
"#,
234+
);
235+
}
236+
}

crates/ide-assists/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ mod handlers {
137137
mod extract_struct_from_enum_variant;
138138
mod extract_type_alias;
139139
mod extract_variable;
140+
mod fill_record_pattern_fields;
140141
mod fix_visibility;
141142
mod flip_binexpr;
142143
mod flip_comma;
@@ -254,6 +255,7 @@ mod handlers {
254255
extract_expressions_from_format_string::extract_expressions_from_format_string,
255256
extract_struct_from_enum_variant::extract_struct_from_enum_variant,
256257
extract_type_alias::extract_type_alias,
258+
fill_record_pattern_fields::fill_record_pattern_fields,
257259
fix_visibility::fix_visibility,
258260
flip_binexpr::flip_binexpr,
259261
flip_comma::flip_comma,

crates/ide-assists/src/tests/generated.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -909,6 +909,27 @@ fn main() {
909909
)
910910
}
911911

912+
#[test]
913+
fn doctest_fill_record_pattern_fields() {
914+
check_doc_test(
915+
"fill_record_pattern_fields",
916+
r#####"
917+
struct Bar { y: Y, z: Z }
918+
919+
fn foo(bar: Bar) {
920+
let Bar { ..$0 } = bar;
921+
}
922+
"#####,
923+
r#####"
924+
struct Bar { y: Y, z: Z }
925+
926+
fn foo(bar: Bar) {
927+
let Bar { y, z } = bar;
928+
}
929+
"#####,
930+
)
931+
}
932+
912933
#[test]
913934
fn doctest_fix_visibility() {
914935
check_doc_test(

0 commit comments

Comments
 (0)