aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src/fill_struct_fields.rs
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2019-04-21 20:13:52 +0100
committerAleksey Kladov <[email protected]>2019-04-21 20:13:52 +0100
commitf313ac45af0b9c81cf411a8a4a6873e742bb8562 (patch)
tree61f3e898b7e86c64b113e690ee55a94882c25911 /crates/ra_assists/src/fill_struct_fields.rs
parent7cc845e88d870173e1baa39ce4d3885a5b1f7043 (diff)
use structured editing API for fill struct assist
Diffstat (limited to 'crates/ra_assists/src/fill_struct_fields.rs')
-rw-r--r--crates/ra_assists/src/fill_struct_fields.rs113
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 @@
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
26struct 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
33impl<DB> FillStructFields<'_, '_, DB> 11 // Collect all fields from struct definition
34where 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 "#,