Skip to content

Commit aa0cc0c

Browse files
bors[bot]c410-f3r
andcommitted
Merge #1002
1002: Fill partial fields r=matklad a=c410-f3r Fixes #992 Co-authored-by: Caio <[email protected]>
2 parents b038832 + 99c4560 commit aa0cc0c

File tree

1 file changed

+122
-33
lines changed

1 file changed

+122
-33
lines changed

crates/ra_assists/src/fill_struct_fields.rs

Lines changed: 122 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,96 @@
11
use std::fmt::Write;
22

3-
use hir::{AdtDef, Ty, source_binder};
4-
use hir::db::HirDatabase;
3+
use hir::{AdtDef, Ty, db::HirDatabase, source_binder::function_from_child_node};
54

65
use ra_syntax::ast::{self, AstNode};
76

87
use crate::{AssistCtx, Assist, AssistId};
98

109
pub(crate) fn fill_struct_fields(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
1110
let struct_lit = ctx.node_at_offset::<ast::StructLit>()?;
12-
let named_field_list = struct_lit.named_field_list()?;
13-
14-
// If we already have existing struct fields, don't provide the assist.
15-
if named_field_list.fields().count() > 0 {
11+
let mut fsf = FillStructFields {
12+
ctx: &mut ctx,
13+
named_field_list: struct_lit.named_field_list()?,
14+
struct_fields: vec![],
15+
struct_lit,
16+
};
17+
fsf.evaluate_struct_def_fields()?;
18+
if fsf.struct_lit_and_def_have_the_same_number_of_fields() {
1619
return None;
1720
}
21+
fsf.remove_already_included_fields()?;
22+
fsf.add_action()?;
23+
ctx.build()
24+
}
1825

19-
let function =
20-
source_binder::function_from_child_node(ctx.db, ctx.frange.file_id, struct_lit.syntax())?;
26+
struct FillStructFields<'a, 'b: 'a, DB> {
27+
ctx: &'a mut AssistCtx<'b, DB>,
28+
named_field_list: &'a ast::NamedFieldList,
29+
struct_fields: Vec<(String, String)>,
30+
struct_lit: &'a ast::StructLit,
31+
}
2132

22-
let infer_result = function.infer(ctx.db);
23-
let source_map = function.body_source_map(ctx.db);
24-
let node_expr = source_map.node_expr(struct_lit.into())?;
25-
let struct_lit_ty = infer_result[node_expr].clone();
26-
let struct_def = match struct_lit_ty {
27-
Ty::Adt { def_id: AdtDef::Struct(s), .. } => s,
28-
_ => return None,
29-
};
33+
impl<DB> FillStructFields<'_, '_, DB>
34+
where
35+
DB: HirDatabase,
36+
{
37+
fn add_action(&mut self) -> Option<()> {
38+
let named_field_list = self.named_field_list;
39+
let struct_fields_string = self.struct_fields_string()?;
40+
let struct_lit = self.struct_lit;
41+
self.ctx.add_action(AssistId("fill_struct_fields"), "fill struct fields", |edit| {
42+
edit.target(struct_lit.syntax().range());
43+
edit.set_cursor(struct_lit.syntax().range().start());
44+
edit.replace_node_and_indent(named_field_list.syntax(), struct_fields_string);
45+
});
46+
Some(())
47+
}
3048

31-
let db = ctx.db;
32-
ctx.add_action(AssistId("fill_struct_fields"), "fill struct fields", |edit| {
49+
fn struct_lit_and_def_have_the_same_number_of_fields(&self) -> bool {
50+
self.named_field_list.fields().count() == self.struct_fields.len()
51+
}
52+
53+
fn evaluate_struct_def_fields(&mut self) -> Option<()> {
54+
let function = function_from_child_node(
55+
self.ctx.db,
56+
self.ctx.frange.file_id,
57+
self.struct_lit.syntax(),
58+
)?;
59+
let infer_result = function.infer(self.ctx.db);
60+
let source_map = function.body_source_map(self.ctx.db);
61+
let node_expr = source_map.node_expr(self.struct_lit.into())?;
62+
let struct_lit_ty = infer_result[node_expr].clone();
63+
let struct_def = match struct_lit_ty {
64+
Ty::Adt { def_id: AdtDef::Struct(s), .. } => s,
65+
_ => return None,
66+
};
67+
self.struct_fields = struct_def
68+
.fields(self.ctx.db)
69+
.into_iter()
70+
.map(|f| (f.name(self.ctx.db).to_string(), "()".into()))
71+
.collect();
72+
Some(())
73+
}
74+
75+
fn remove_already_included_fields(&mut self) -> Option<()> {
76+
for ast_field in self.named_field_list.fields() {
77+
let expr = ast_field.expr()?.syntax().text().to_string();
78+
let name_from_ast = ast_field.name_ref()?.text().to_string();
79+
if let Some(idx) = self.struct_fields.iter().position(|(n, _)| n == &name_from_ast) {
80+
self.struct_fields[idx] = (name_from_ast, expr);
81+
}
82+
}
83+
Some(())
84+
}
85+
86+
fn struct_fields_string(&mut self) -> Option<String> {
3387
let mut buf = String::from("{\n");
34-
let struct_fields = struct_def.fields(db);
35-
for field in struct_fields {
36-
let field_name = field.name(db).to_string();
37-
write!(&mut buf, " {}: (),\n", field_name).unwrap();
88+
for (name, expr) in &self.struct_fields {
89+
write!(&mut buf, " {}: {},\n", name, expr).unwrap();
3890
}
3991
buf.push_str("}");
40-
41-
edit.target(struct_lit.syntax().range());
42-
edit.set_cursor(struct_lit.syntax().range().start());
43-
edit.replace_node_and_indent(named_field_list.syntax(), buf);
44-
});
45-
46-
ctx.build()
92+
Some(buf)
93+
}
4794
}
4895

4996
#[cfg(test)]
@@ -62,7 +109,7 @@ mod tests {
62109
b: String,
63110
c: (i32, i32),
64111
d: D,
65-
r: &'a str,
112+
e: &'a str,
66113
}
67114
68115
fn main() {
@@ -75,7 +122,7 @@ mod tests {
75122
b: String,
76123
c: (i32, i32),
77124
d: D,
78-
r: &'a str,
125+
e: &'a str,
79126
}
80127
81128
fn main() {
@@ -84,7 +131,7 @@ mod tests {
84131
b: (),
85132
c: (),
86133
d: (),
87-
r: (),
134+
e: (),
88135
}
89136
}
90137
"#,
@@ -101,7 +148,7 @@ mod tests {
101148
b: String,
102149
c: (i32, i32),
103150
d: D,
104-
r: &'a str,
151+
e: &'a str,
105152
}
106153
107154
fn main() {
@@ -148,4 +195,46 @@ mod tests {
148195
"#,
149196
);
150197
}
198+
199+
#[test]
200+
fn fill_struct_fields_partial() {
201+
check_assist(
202+
fill_struct_fields,
203+
r#"
204+
struct S<'a, D> {
205+
a: u32,
206+
b: String,
207+
c: (i32, i32),
208+
d: D,
209+
e: &'a str,
210+
}
211+
212+
fn main() {
213+
let s = S {
214+
c: (1, 2),
215+
e: "foo",<|>
216+
}
217+
}
218+
"#,
219+
r#"
220+
struct S<'a, D> {
221+
a: u32,
222+
b: String,
223+
c: (i32, i32),
224+
d: D,
225+
e: &'a str,
226+
}
227+
228+
fn main() {
229+
let s = <|>S {
230+
a: (),
231+
b: (),
232+
c: (1, 2),
233+
d: (),
234+
e: "foo",
235+
}
236+
}
237+
"#,
238+
);
239+
}
151240
}

0 commit comments

Comments
 (0)