diff options
-rw-r--r-- | crates/ra_assists/src/fill_struct_fields.rs | 159 |
1 files changed, 127 insertions, 32 deletions
diff --git a/crates/ra_assists/src/fill_struct_fields.rs b/crates/ra_assists/src/fill_struct_fields.rs index 90ce9c577..398dbcd21 100644 --- a/crates/ra_assists/src/fill_struct_fields.rs +++ b/crates/ra_assists/src/fill_struct_fields.rs | |||
@@ -1,7 +1,6 @@ | |||
1 | use std::fmt::Write; | 1 | use std::fmt::Write; |
2 | 2 | ||
3 | use hir::{AdtDef, Ty, source_binder}; | 3 | use hir::{AdtDef, Ty, db::HirDatabase, source_binder::function_from_child_node, StructField}; |
4 | use hir::db::HirDatabase; | ||
5 | 4 | ||
6 | use ra_syntax::ast::{self, AstNode}; | 5 | use ra_syntax::ast::{self, AstNode}; |
7 | 6 | ||
@@ -9,41 +8,95 @@ use crate::{AssistCtx, Assist, AssistId}; | |||
9 | 8 | ||
10 | pub(crate) fn fill_struct_fields(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | 9 | pub(crate) fn fill_struct_fields(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { |
11 | let struct_lit = ctx.node_at_offset::<ast::StructLit>()?; | 10 | let struct_lit = ctx.node_at_offset::<ast::StructLit>()?; |
12 | let named_field_list = struct_lit.named_field_list()?; | 11 | let mut fsf = FillStructFields { |
13 | 12 | ctx: &mut ctx, | |
14 | // If we already have existing struct fields, don't provide the assist. | 13 | named_field_list: struct_lit.named_field_list()?, |
15 | if named_field_list.fields().count() > 0 { | 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() { | ||
16 | return None; | 19 | return None; |
17 | } | 20 | } |
21 | fsf.remove_already_included_fields()?; | ||
22 | fsf.add_action()?; | ||
23 | ctx.build() | ||
24 | } | ||
18 | 25 | ||
19 | let function = | 26 | struct FillStructFields<'a, 'b: 'a, DB> { |
20 | source_binder::function_from_child_node(ctx.db, ctx.frange.file_id, struct_lit.syntax())?; | 27 | ctx: &'a mut AssistCtx<'b, DB>, |
28 | named_field_list: &'a ast::NamedFieldList, | ||
29 | struct_fields: Vec<StructField>, | ||
30 | struct_lit: &'a ast::StructLit, | ||
31 | } | ||
21 | 32 | ||
22 | let infer_result = function.infer(ctx.db); | 33 | impl<DB> FillStructFields<'_, '_, DB> |
23 | let source_map = function.body_source_map(ctx.db); | 34 | where |
24 | let node_expr = source_map.node_expr(struct_lit.into())?; | 35 | DB: HirDatabase, |
25 | let struct_lit_ty = infer_result[node_expr].clone(); | 36 | { |
26 | let struct_def = match struct_lit_ty { | 37 | fn add_action(&mut self) -> Option<()> { |
27 | Ty::Adt { def_id: AdtDef::Struct(s), .. } => s, | 38 | let named_field_list = self.named_field_list; |
28 | _ => return None, | 39 | let struct_fields_string = self.struct_fields_string()?; |
29 | }; | 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 | } | ||
30 | 48 | ||
31 | let db = ctx.db; | 49 | fn struct_lit_and_def_have_the_same_number_of_fields(&self) -> bool { |
32 | ctx.add_action(AssistId("fill_struct_fields"), "fill struct fields", |edit| { | 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.fields(self.ctx.db); | ||
68 | Some(()) | ||
69 | } | ||
70 | |||
71 | fn remove_already_included_fields(&mut self) -> Option<()> { | ||
72 | for ast_field in self.named_field_list.fields() { | ||
73 | let name_from_ast = ast_field.name_ref()?.text().to_string(); | ||
74 | if let Some(idx) = self | ||
75 | .struct_fields | ||
76 | .iter() | ||
77 | .map(|f| f.name(self.ctx.db).to_string()) | ||
78 | .position(|n| n == name_from_ast) | ||
79 | { | ||
80 | self.struct_fields.remove(idx); | ||
81 | } | ||
82 | } | ||
83 | Some(()) | ||
84 | } | ||
85 | |||
86 | fn struct_fields_string(&self) -> Option<String> { | ||
33 | let mut buf = String::from("{\n"); | 87 | let mut buf = String::from("{\n"); |
34 | let struct_fields = struct_def.fields(db); | 88 | for field in self.named_field_list.fields() { |
35 | for field in struct_fields { | 89 | let expr = field.expr()?.syntax().text().to_string(); |
36 | let field_name = field.name(db).to_string(); | 90 | let field_name = field.name_ref()?.syntax().text().to_string(); |
91 | write!(&mut buf, " {}: {},\n", field_name, expr).unwrap(); | ||
92 | } | ||
93 | for field in &self.struct_fields { | ||
94 | let field_name = field.name(self.ctx.db).to_string(); | ||
37 | write!(&mut buf, " {}: (),\n", field_name).unwrap(); | 95 | write!(&mut buf, " {}: (),\n", field_name).unwrap(); |
38 | } | 96 | } |
39 | buf.push_str("}"); | 97 | buf.push_str("}"); |
40 | 98 | Some(buf) | |
41 | edit.target(struct_lit.syntax().range()); | 99 | } |
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() | ||
47 | } | 100 | } |
48 | 101 | ||
49 | #[cfg(test)] | 102 | #[cfg(test)] |
@@ -62,7 +115,7 @@ mod tests { | |||
62 | b: String, | 115 | b: String, |
63 | c: (i32, i32), | 116 | c: (i32, i32), |
64 | d: D, | 117 | d: D, |
65 | r: &'a str, | 118 | e: &'a str, |
66 | } | 119 | } |
67 | 120 | ||
68 | fn main() { | 121 | fn main() { |
@@ -75,7 +128,7 @@ mod tests { | |||
75 | b: String, | 128 | b: String, |
76 | c: (i32, i32), | 129 | c: (i32, i32), |
77 | d: D, | 130 | d: D, |
78 | r: &'a str, | 131 | e: &'a str, |
79 | } | 132 | } |
80 | 133 | ||
81 | fn main() { | 134 | fn main() { |
@@ -84,7 +137,7 @@ mod tests { | |||
84 | b: (), | 137 | b: (), |
85 | c: (), | 138 | c: (), |
86 | d: (), | 139 | d: (), |
87 | r: (), | 140 | e: (), |
88 | } | 141 | } |
89 | } | 142 | } |
90 | "#, | 143 | "#, |
@@ -101,7 +154,7 @@ mod tests { | |||
101 | b: String, | 154 | b: String, |
102 | c: (i32, i32), | 155 | c: (i32, i32), |
103 | d: D, | 156 | d: D, |
104 | r: &'a str, | 157 | e: &'a str, |
105 | } | 158 | } |
106 | 159 | ||
107 | fn main() { | 160 | fn main() { |
@@ -148,4 +201,46 @@ mod tests { | |||
148 | "#, | 201 | "#, |
149 | ); | 202 | ); |
150 | } | 203 | } |
204 | |||
205 | #[test] | ||
206 | fn fill_struct_fields_partial() { | ||
207 | check_assist( | ||
208 | fill_struct_fields, | ||
209 | r#" | ||
210 | struct S<'a, D> { | ||
211 | a: u32, | ||
212 | b: String, | ||
213 | c: (i32, i32), | ||
214 | d: D, | ||
215 | e: &'a str, | ||
216 | } | ||
217 | |||
218 | fn main() { | ||
219 | let s = S { | ||
220 | c: (1, 2), | ||
221 | e: "foo",<|> | ||
222 | } | ||
223 | } | ||
224 | "#, | ||
225 | r#" | ||
226 | struct S<'a, D> { | ||
227 | a: u32, | ||
228 | b: String, | ||
229 | c: (i32, i32), | ||
230 | d: D, | ||
231 | e: &'a str, | ||
232 | } | ||
233 | |||
234 | fn main() { | ||
235 | let s = <|>S { | ||
236 | c: (1, 2), | ||
237 | e: "foo", | ||
238 | a: (), | ||
239 | b: (), | ||
240 | d: (), | ||
241 | } | ||
242 | } | ||
243 | "#, | ||
244 | ); | ||
245 | } | ||
151 | } | 246 | } |