aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_assists/src/fill_struct_fields.rs155
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 @@
1use std::fmt::Write; 1use std::fmt::Write;
2 2
3use hir::{AdtDef, Ty, source_binder}; 3use hir::{AdtDef, Ty, db::HirDatabase, source_binder::function_from_child_node};
4use hir::db::HirDatabase;
5 4
6use ra_syntax::ast::{self, AstNode}; 5use ra_syntax::ast::{self, AstNode};
7 6
@@ -9,41 +8,89 @@ use crate::{AssistCtx, Assist, AssistId};
9 8
10pub(crate) fn fill_struct_fields(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 9pub(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 = 26struct 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); 33impl<DB> FillStructFields<'_, '_, DB>
23 let source_map = function.body_source_map(ctx.db); 34where
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}