diff options
Diffstat (limited to 'crates/ra_assists/src/add_missing_impl_members.rs')
-rw-r--r-- | crates/ra_assists/src/add_missing_impl_members.rs | 149 |
1 files changed, 38 insertions, 111 deletions
diff --git a/crates/ra_assists/src/add_missing_impl_members.rs b/crates/ra_assists/src/add_missing_impl_members.rs index c82447b84..17c2af899 100644 --- a/crates/ra_assists/src/add_missing_impl_members.rs +++ b/crates/ra_assists/src/add_missing_impl_members.rs | |||
@@ -1,14 +1,9 @@ | |||
1 | use std::fmt::Write; | 1 | use crate::{Assist, AssistId, AssistCtx, ast_editor::{AstEditor, AstBuilder}}; |
2 | |||
3 | use crate::{Assist, AssistId, AssistCtx}; | ||
4 | 2 | ||
5 | use hir::db::HirDatabase; | 3 | use hir::db::HirDatabase; |
6 | use ra_syntax::{SmolStr, SyntaxKind, TextRange, TextUnit, TreeArc}; | 4 | use ra_syntax::{SmolStr, TreeArc}; |
7 | use ra_syntax::ast::{self, AstNode, AstToken, FnDef, ImplItem, ImplItemKind, NameOwner}; | 5 | use ra_syntax::ast::{self, AstNode, FnDef, ImplItem, ImplItemKind, NameOwner}; |
8 | use ra_db::FilePosition; | 6 | use ra_db::FilePosition; |
9 | use ra_fmt::{leading_indent, reindent}; | ||
10 | |||
11 | use itertools::Itertools; | ||
12 | 7 | ||
13 | enum AddMissingImplMembersMode { | 8 | enum AddMissingImplMembersMode { |
14 | DefaultMethodsOnly, | 9 | DefaultMethodsOnly, |
@@ -76,48 +71,35 @@ fn add_missing_impl_members_inner( | |||
76 | } | 71 | } |
77 | 72 | ||
78 | ctx.add_action(AssistId(assist_id), label, |edit| { | 73 | ctx.add_action(AssistId(assist_id), label, |edit| { |
79 | let (parent_indent, indent) = { | 74 | let n_existing_items = impl_item_list.impl_items().count(); |
80 | // FIXME: Find a way to get the indent already used in the file. | 75 | let fns = missing_fns.into_iter().map(add_body_and_strip_docstring).collect::<Vec<_>>(); |
81 | // Now, we copy the indent of first item or indent with 4 spaces relative to impl block | 76 | |
82 | const DEFAULT_INDENT: &str = " "; | 77 | let mut ast_editor = AstEditor::new(impl_item_list); |
83 | let first_item = impl_item_list.impl_items().next(); | 78 | if n_existing_items == 0 { |
84 | let first_item_indent = | 79 | ast_editor.make_multiline(); |
85 | first_item.and_then(|i| leading_indent(i.syntax())).map(ToOwned::to_owned); | 80 | } |
86 | let impl_block_indent = leading_indent(impl_node.syntax()).unwrap_or_default(); | 81 | ast_editor.append_functions(fns.iter().map(|it| &**it)); |
87 | 82 | let first_new_item = ast_editor.ast().impl_items().nth(n_existing_items).unwrap(); | |
88 | ( | 83 | let cursor_poisition = first_new_item.syntax().range().start(); |
89 | impl_block_indent.to_owned(), | 84 | ast_editor.into_text_edit(edit.text_edit_builder()); |
90 | first_item_indent.unwrap_or_else(|| impl_block_indent.to_owned() + DEFAULT_INDENT), | 85 | |
91 | ) | 86 | edit.set_cursor(cursor_poisition); |
92 | }; | ||
93 | |||
94 | let changed_range = { | ||
95 | let children = impl_item_list.syntax().children_with_tokens(); | ||
96 | let last_whitespace = | ||
97 | children.filter_map(|it| ast::Whitespace::cast(it.as_token()?)).last(); | ||
98 | |||
99 | last_whitespace.map(|w| w.syntax().range()).unwrap_or_else(|| { | ||
100 | let in_brackets = impl_item_list.syntax().range().end() - TextUnit::of_str("}"); | ||
101 | TextRange::from_to(in_brackets, in_brackets) | ||
102 | }) | ||
103 | }; | ||
104 | |||
105 | let func_bodies = format!("\n{}", missing_fns.into_iter().map(build_func_body).join("\n")); | ||
106 | let trailing_whitespace = format!("\n{}", parent_indent); | ||
107 | let func_bodies = reindent(&func_bodies, &indent) + &trailing_whitespace; | ||
108 | |||
109 | let replaced_text_range = TextUnit::of_str(&func_bodies); | ||
110 | |||
111 | edit.replace(changed_range, func_bodies); | ||
112 | // FIXME: place the cursor on the first unimplemented? | ||
113 | edit.set_cursor( | ||
114 | changed_range.start() + replaced_text_range - TextUnit::of_str(&trailing_whitespace), | ||
115 | ); | ||
116 | }); | 87 | }); |
117 | 88 | ||
118 | ctx.build() | 89 | ctx.build() |
119 | } | 90 | } |
120 | 91 | ||
92 | fn add_body_and_strip_docstring(fn_def: &ast::FnDef) -> TreeArc<ast::FnDef> { | ||
93 | let mut ast_editor = AstEditor::new(fn_def); | ||
94 | if fn_def.body().is_none() { | ||
95 | ast_editor.set_body(&AstBuilder::<ast::Block>::single_expr( | ||
96 | &AstBuilder::<ast::Expr>::unimplemented(), | ||
97 | )); | ||
98 | } | ||
99 | ast_editor.strip_attrs_and_docs(); | ||
100 | ast_editor.ast().to_owned() | ||
101 | } | ||
102 | |||
121 | /// Given an `ast::ImplBlock`, resolves the target trait (the one being | 103 | /// Given an `ast::ImplBlock`, resolves the target trait (the one being |
122 | /// implemented) to a `ast::TraitDef`. | 104 | /// implemented) to a `ast::TraitDef`. |
123 | fn resolve_target_trait_def( | 105 | fn resolve_target_trait_def( |
@@ -134,22 +116,6 @@ fn resolve_target_trait_def( | |||
134 | } | 116 | } |
135 | } | 117 | } |
136 | 118 | ||
137 | fn build_func_body(def: &ast::FnDef) -> String { | ||
138 | let mut buf = String::new(); | ||
139 | |||
140 | for child in def.syntax().children_with_tokens() { | ||
141 | match (child.prev_sibling_or_token().map(|c| c.kind()), child.kind()) { | ||
142 | (_, SyntaxKind::SEMI) => buf.push_str(" {\n unimplemented!()\n}"), | ||
143 | (_, SyntaxKind::ATTR) | (_, SyntaxKind::COMMENT) => {} | ||
144 | (Some(SyntaxKind::ATTR), SyntaxKind::WHITESPACE) | ||
145 | | (Some(SyntaxKind::COMMENT), SyntaxKind::WHITESPACE) => {} | ||
146 | _ => write!(buf, "{}", child).unwrap(), | ||
147 | }; | ||
148 | } | ||
149 | |||
150 | buf.trim_end().to_string() | ||
151 | } | ||
152 | |||
153 | #[cfg(test)] | 119 | #[cfg(test)] |
154 | mod tests { | 120 | mod tests { |
155 | use super::*; | 121 | use super::*; |
@@ -170,7 +136,7 @@ struct S; | |||
170 | 136 | ||
171 | impl Foo for S { | 137 | impl Foo for S { |
172 | fn bar(&self) {} | 138 | fn bar(&self) {} |
173 | <|> | 139 | <|> |
174 | }", | 140 | }", |
175 | " | 141 | " |
176 | trait Foo { | 142 | trait Foo { |
@@ -183,12 +149,9 @@ struct S; | |||
183 | 149 | ||
184 | impl Foo for S { | 150 | impl Foo for S { |
185 | fn bar(&self) {} | 151 | fn bar(&self) {} |
186 | fn foo(&self) { | 152 | <|>fn foo(&self) { unimplemented!() } |
187 | unimplemented!() | 153 | fn baz(&self) { unimplemented!() } |
188 | } | 154 | |
189 | fn baz(&self) { | ||
190 | unimplemented!() | ||
191 | }<|> | ||
192 | }", | 155 | }", |
193 | ); | 156 | ); |
194 | } | 157 | } |
@@ -208,7 +171,7 @@ struct S; | |||
208 | 171 | ||
209 | impl Foo for S { | 172 | impl Foo for S { |
210 | fn bar(&self) {} | 173 | fn bar(&self) {} |
211 | <|> | 174 | <|> |
212 | }", | 175 | }", |
213 | " | 176 | " |
214 | trait Foo { | 177 | trait Foo { |
@@ -221,9 +184,8 @@ struct S; | |||
221 | 184 | ||
222 | impl Foo for S { | 185 | impl Foo for S { |
223 | fn bar(&self) {} | 186 | fn bar(&self) {} |
224 | fn foo(&self) { | 187 | <|>fn foo(&self) { unimplemented!() } |
225 | unimplemented!() | 188 | |
226 | }<|> | ||
227 | }", | 189 | }", |
228 | ); | 190 | ); |
229 | } | 191 | } |
@@ -240,9 +202,7 @@ impl Foo for S { <|> }", | |||
240 | trait Foo { fn foo(&self); } | 202 | trait Foo { fn foo(&self); } |
241 | struct S; | 203 | struct S; |
242 | impl Foo for S { | 204 | impl Foo for S { |
243 | fn foo(&self) { | 205 | <|>fn foo(&self) { unimplemented!() } |
244 | unimplemented!() | ||
245 | }<|> | ||
246 | }", | 206 | }", |
247 | ); | 207 | ); |
248 | } | 208 | } |
@@ -259,9 +219,7 @@ impl Foo for S {}<|>", | |||
259 | trait Foo { fn foo(&self); } | 219 | trait Foo { fn foo(&self); } |
260 | struct S; | 220 | struct S; |
261 | impl Foo for S { | 221 | impl Foo for S { |
262 | fn foo(&self) { | 222 | <|>fn foo(&self) { unimplemented!() } |
263 | unimplemented!() | ||
264 | }<|> | ||
265 | }", | 223 | }", |
266 | ) | 224 | ) |
267 | } | 225 | } |
@@ -292,35 +250,6 @@ impl Foo for S { <|> }", | |||
292 | } | 250 | } |
293 | 251 | ||
294 | #[test] | 252 | #[test] |
295 | fn test_indented_impl_block() { | ||
296 | check_assist( | ||
297 | add_missing_impl_members, | ||
298 | " | ||
299 | trait Foo { | ||
300 | fn valid(some: u32) -> bool; | ||
301 | } | ||
302 | struct S; | ||
303 | |||
304 | mod my_mod { | ||
305 | impl crate::Foo for S { <|> } | ||
306 | }", | ||
307 | " | ||
308 | trait Foo { | ||
309 | fn valid(some: u32) -> bool; | ||
310 | } | ||
311 | struct S; | ||
312 | |||
313 | mod my_mod { | ||
314 | impl crate::Foo for S { | ||
315 | fn valid(some: u32) -> bool { | ||
316 | unimplemented!() | ||
317 | }<|> | ||
318 | } | ||
319 | }", | ||
320 | ) | ||
321 | } | ||
322 | |||
323 | #[test] | ||
324 | fn test_with_docstring_and_attrs() { | 253 | fn test_with_docstring_and_attrs() { |
325 | check_assist( | 254 | check_assist( |
326 | add_missing_impl_members, | 255 | add_missing_impl_members, |
@@ -342,9 +271,7 @@ trait Foo { | |||
342 | } | 271 | } |
343 | struct S; | 272 | struct S; |
344 | impl Foo for S { | 273 | impl Foo for S { |
345 | fn foo(&self) { | 274 | <|>fn foo(&self) { unimplemented!() } |
346 | unimplemented!() | ||
347 | }<|> | ||
348 | }"#, | 275 | }"#, |
349 | ) | 276 | ) |
350 | } | 277 | } |
@@ -367,7 +294,7 @@ trait Foo { | |||
367 | } | 294 | } |
368 | struct S; | 295 | struct S; |
369 | impl Foo for S { | 296 | impl Foo for S { |
370 | fn valid(some: u32) -> bool { false }<|> | 297 | <|>fn valid(some: u32) -> bool { false } |
371 | }", | 298 | }", |
372 | ) | 299 | ) |
373 | } | 300 | } |