diff options
Diffstat (limited to 'crates/ra_assists/src/fill_struct_fields.rs')
-rw-r--r-- | crates/ra_assists/src/fill_struct_fields.rs | 113 |
1 files changed, 36 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..ca128168a 100644 --- a/crates/ra_assists/src/fill_struct_fields.rs +++ b/crates/ra_assists/src/fill_struct_fields.rs | |||
@@ -1,94 +1,53 @@ | |||
1 | use std::fmt::Write; | ||
2 | |||
3 | use hir::{AdtDef, db::HirDatabase}; | 1 | use hir::{AdtDef, db::HirDatabase}; |
4 | 2 | ||
5 | use ra_syntax::ast::{self, AstNode}; | 3 | use ra_syntax::ast::{self, AstNode}; |
6 | 4 | ||
7 | use crate::{AssistCtx, Assist, AssistId}; | 5 | use crate::{AssistCtx, Assist, AssistId, ast_editor::{AstEditor, AstBuilder}}; |
8 | 6 | ||
9 | pub(crate) fn fill_struct_fields(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | 7 | pub(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 | |||
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 | } | ||
32 | 10 | ||
33 | impl<DB> FillStructFields<'_, '_, DB> | 11 | // Collect all fields from struct definition |
34 | where | 12 | let mut fields = { |
35 | DB: HirDatabase, | 13 | let analyzer = |
36 | { | 14 | hir::SourceAnalyzer::new(ctx.db, ctx.frange.file_id, struct_lit.syntax(), None); |
37 | fn add_action(&mut self) -> Option<()> { | 15 | let struct_lit_ty = analyzer.type_of(ctx.db, struct_lit.into())?; |
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 = | ||
41 | AstBuilder::<ast::NamedField>::from_text(&format!("{}: ()", field.name(db))); | ||
42 | ast_editor.append_field(&field); | ||
88 | } | 43 | } |
89 | buf.push_str("}"); | 44 | |
90 | Some(buf) | 45 | edit.target(struct_lit.syntax().range()); |
91 | } | 46 | edit.set_cursor(struct_lit.syntax().range().start()); |
47 | |||
48 | ast_editor.into_text_edit(edit.text_edit_builder()); | ||
49 | }); | ||
50 | ctx.build() | ||
92 | } | 51 | } |
93 | 52 | ||
94 | #[cfg(test)] | 53 | #[cfg(test)] |
@@ -225,11 +184,11 @@ mod tests { | |||
225 | 184 | ||
226 | fn main() { | 185 | fn main() { |
227 | let s = <|>S { | 186 | let s = <|>S { |
187 | c: (1, 2), | ||
188 | e: "foo", | ||
228 | a: (), | 189 | a: (), |
229 | b: (), | 190 | b: (), |
230 | c: (1, 2), | ||
231 | d: (), | 191 | d: (), |
232 | e: "foo", | ||
233 | } | 192 | } |
234 | } | 193 | } |
235 | "#, | 194 | "#, |