aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists')
-rw-r--r--crates/ra_assists/src/fill_struct_fields.rs159
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 @@
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, StructField};
4use hir::db::HirDatabase;
5 4
6use ra_syntax::ast::{self, AstNode}; 5use ra_syntax::ast::{self, AstNode};
7 6
@@ -9,41 +8,95 @@ 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<StructField>,
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.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}