diff options
Diffstat (limited to 'crates/ra_assists')
-rw-r--r-- | crates/ra_assists/src/fill_struct_fields.rs | 155 |
1 files changed, 122 insertions, 33 deletions
diff --git a/crates/ra_assists/src/fill_struct_fields.rs b/crates/ra_assists/src/fill_struct_fields.rs index 90ce9c577..6e61a58fc 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}; |
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,89 @@ 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<(String, String)>, | ||
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 | ||
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> { | ||
33 | let mut buf = String::from("{\n"); | 87 | let mut buf = String::from("{\n"); |
34 | let struct_fields = struct_def.fields(db); | 88 | for (name, expr) in &self.struct_fields { |
35 | for field in struct_fields { | 89 | write!(&mut buf, " {}: {},\n", name, expr).unwrap(); |
36 | let field_name = field.name(db).to_string(); | ||
37 | write!(&mut buf, " {}: (),\n", field_name).unwrap(); | ||
38 | } | 90 | } |
39 | buf.push_str("}"); | 91 | buf.push_str("}"); |
40 | 92 | Some(buf) | |
41 | edit.target(struct_lit.syntax().range()); | 93 | } |
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 | } | 94 | } |
48 | 95 | ||
49 | #[cfg(test)] | 96 | #[cfg(test)] |
@@ -62,7 +109,7 @@ mod tests { | |||
62 | b: String, | 109 | b: String, |
63 | c: (i32, i32), | 110 | c: (i32, i32), |
64 | d: D, | 111 | d: D, |
65 | r: &'a str, | 112 | e: &'a str, |
66 | } | 113 | } |
67 | 114 | ||
68 | fn main() { | 115 | fn main() { |
@@ -75,7 +122,7 @@ mod tests { | |||
75 | b: String, | 122 | b: String, |
76 | c: (i32, i32), | 123 | c: (i32, i32), |
77 | d: D, | 124 | d: D, |
78 | r: &'a str, | 125 | e: &'a str, |
79 | } | 126 | } |
80 | 127 | ||
81 | fn main() { | 128 | fn main() { |
@@ -84,7 +131,7 @@ mod tests { | |||
84 | b: (), | 131 | b: (), |
85 | c: (), | 132 | c: (), |
86 | d: (), | 133 | d: (), |
87 | r: (), | 134 | e: (), |
88 | } | 135 | } |
89 | } | 136 | } |
90 | "#, | 137 | "#, |
@@ -101,7 +148,7 @@ mod tests { | |||
101 | b: String, | 148 | b: String, |
102 | c: (i32, i32), | 149 | c: (i32, i32), |
103 | d: D, | 150 | d: D, |
104 | r: &'a str, | 151 | e: &'a str, |
105 | } | 152 | } |
106 | 153 | ||
107 | fn main() { | 154 | fn main() { |
@@ -148,4 +195,46 @@ mod tests { | |||
148 | "#, | 195 | "#, |
149 | ); | 196 | ); |
150 | } | 197 | } |
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 | } | ||
151 | } | 240 | } |