aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src/fill_struct_fields.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists/src/fill_struct_fields.rs')
-rw-r--r--crates/ra_assists/src/fill_struct_fields.rs142
1 files changed, 65 insertions, 77 deletions
diff --git a/crates/ra_assists/src/fill_struct_fields.rs b/crates/ra_assists/src/fill_struct_fields.rs
index 663b4f669..54b70e17d 100644
--- a/crates/ra_assists/src/fill_struct_fields.rs
+++ b/crates/ra_assists/src/fill_struct_fields.rs
@@ -1,94 +1,55 @@
1use std::fmt::Write;
2
3use hir::{AdtDef, db::HirDatabase}; 1use hir::{AdtDef, db::HirDatabase};
4 2
5use ra_syntax::ast::{self, AstNode}; 3use ra_syntax::ast::{self, AstNode};
6 4
7use crate::{AssistCtx, Assist, AssistId}; 5use crate::{AssistCtx, Assist, AssistId, ast_editor::{AstEditor, AstBuilder}};
8 6
9pub(crate) fn fill_struct_fields(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 7pub(crate) fn fill_struct_fields(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
10 let struct_lit = ctx.node_at_offset::<ast::StructLit>()?; 8 let struct_lit = ctx.node_at_offset::<ast::StructLit>()?;
11 let mut fsf = FillStructFields { 9 let named_field_list = struct_lit.named_field_list()?;
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() {
19 return None;
20 }
21 fsf.remove_already_included_fields()?;
22 fsf.add_action()?;
23 ctx.build()
24}
25 10
26struct FillStructFields<'a, 'b: 'a, DB> { 11 // Collect all fields from struct definition
27 ctx: &'a mut AssistCtx<'b, DB>, 12 let mut fields = {
28 named_field_list: &'a ast::NamedFieldList, 13 let analyzer =
29 struct_fields: Vec<(String, String)>, 14 hir::SourceAnalyzer::new(ctx.db, ctx.frange.file_id, struct_lit.syntax(), None);
30 struct_lit: &'a ast::StructLit, 15 let struct_lit_ty = analyzer.type_of(ctx.db, struct_lit.into())?;
31}
32
33impl<DB> FillStructFields<'_, '_, DB>
34where
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 }
48
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 analyzer = hir::SourceAnalyzer::new(
55 self.ctx.db,
56 self.ctx.frange.file_id,
57 self.struct_lit.syntax(),
58 None,
59 );
60 let struct_lit_ty = analyzer.type_of(self.ctx.db, self.struct_lit.into())?;
61 let struct_def = match struct_lit_ty.as_adt() { 16 let struct_def = match struct_lit_ty.as_adt() {
62 Some((AdtDef::Struct(s), _)) => s, 17 Some((AdtDef::Struct(s), _)) => s,
63 _ => return None, 18 _ => return None,
64 }; 19 };
65 self.struct_fields = struct_def 20 struct_def.fields(ctx.db)
66 .fields(self.ctx.db) 21 };
67 .into_iter()
68 .map(|f| (f.name(self.ctx.db).to_string(), "()".into()))
69 .collect();
70 Some(())
71 }
72 22
73 fn remove_already_included_fields(&mut self) -> Option<()> { 23 // Filter out existing fields
74 for ast_field in self.named_field_list.fields() { 24 for ast_field in named_field_list.fields() {
75 let expr = ast_field.expr()?.syntax().text().to_string(); 25 let name_from_ast = ast_field.name_ref()?.text().to_string();
76 let name_from_ast = ast_field.name_ref()?.text().to_string(); 26 fields.retain(|field| field.name(ctx.db).to_string() != name_from_ast);
77 if let Some(idx) = self.struct_fields.iter().position(|(n, _)| n == &name_from_ast) { 27 }
78 self.struct_fields[idx] = (name_from_ast, expr); 28 if fields.is_empty() {
79 } 29 return None;
80 }
81 Some(())
82 } 30 }
83 31
84 fn struct_fields_string(&mut self) -> Option<String> { 32 let db = ctx.db;
85 let mut buf = String::from("{\n"); 33 ctx.add_action(AssistId("fill_struct_fields"), "fill struct fields", |edit| {
86 for (name, expr) in &self.struct_fields { 34 let mut ast_editor = AstEditor::new(named_field_list);
87 write!(&mut buf, " {}: {},\n", name, expr).unwrap(); 35 if named_field_list.fields().count() == 0 && fields.len() > 2 {
36 ast_editor.make_multiline();
37 };
38
39 for field in fields {
40 let field = AstBuilder::<ast::NamedField>::from_pieces(
41 &AstBuilder::<ast::NameRef>::new(&field.name(db).to_string()),
42 Some(&AstBuilder::<ast::Expr>::unit()),
43 );
44 ast_editor.append_field(&field);
88 } 45 }
89 buf.push_str("}"); 46
90 Some(buf) 47 edit.target(struct_lit.syntax().range());
91 } 48 edit.set_cursor(struct_lit.syntax().range().start());
49
50 ast_editor.into_text_edit(edit.text_edit_builder());
51 });
52 ctx.build()
92} 53}
93 54
94#[cfg(test)] 55#[cfg(test)]
@@ -225,14 +186,41 @@ mod tests {
225 186
226 fn main() { 187 fn main() {
227 let s = <|>S { 188 let s = <|>S {
189 c: (1, 2),
190 e: "foo",
228 a: (), 191 a: (),
229 b: (), 192 b: (),
230 c: (1, 2),
231 d: (), 193 d: (),
232 e: "foo",
233 } 194 }
234 } 195 }
235 "#, 196 "#,
236 ); 197 );
237 } 198 }
199
200 #[test]
201 fn fill_struct_short() {
202 check_assist(
203 fill_struct_fields,
204 r#"
205 struct S {
206 foo: u32,
207 bar: String,
208 }
209
210 fn main() {
211 let s = S {<|> };
212 }
213 "#,
214 r#"
215 struct S {
216 foo: u32,
217 bar: String,
218 }
219
220 fn main() {
221 let s = <|>S { foo: (), bar: () };
222 }
223 "#,
224 );
225 }
238} 226}