aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src/add_missing_impl_members.rs
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2019-04-22 11:01:33 +0100
committerAleksey Kladov <[email protected]>2019-04-22 11:01:33 +0100
commit268e739c94d2e9edbb45374dfcc252b1648d3181 (patch)
tree25820553d53e8a638003e315076c6c074464fd9e /crates/ra_assists/src/add_missing_impl_members.rs
parentb811922a530b7b22469b386a521946f1003911c7 (diff)
move add_missing_members to structured editing API
Currently, this is more code, and we also loose auto-indenting of bodies, but, long-term, this is the right approach
Diffstat (limited to 'crates/ra_assists/src/add_missing_impl_members.rs')
-rw-r--r--crates/ra_assists/src/add_missing_impl_members.rs149
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 @@
1use std::fmt::Write; 1use crate::{Assist, AssistId, AssistCtx, ast_editor::{AstEditor, AstBuilder}};
2
3use crate::{Assist, AssistId, AssistCtx};
4 2
5use hir::db::HirDatabase; 3use hir::db::HirDatabase;
6use ra_syntax::{SmolStr, SyntaxKind, TextRange, TextUnit, TreeArc}; 4use ra_syntax::{SmolStr, TreeArc};
7use ra_syntax::ast::{self, AstNode, AstToken, FnDef, ImplItem, ImplItemKind, NameOwner}; 5use ra_syntax::ast::{self, AstNode, FnDef, ImplItem, ImplItemKind, NameOwner};
8use ra_db::FilePosition; 6use ra_db::FilePosition;
9use ra_fmt::{leading_indent, reindent};
10
11use itertools::Itertools;
12 7
13enum AddMissingImplMembersMode { 8enum 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
92fn 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`.
123fn resolve_target_trait_def( 105fn resolve_target_trait_def(
@@ -134,22 +116,6 @@ fn resolve_target_trait_def(
134 } 116 }
135} 117}
136 118
137fn 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)]
154mod tests { 120mod tests {
155 use super::*; 121 use super::*;
@@ -170,7 +136,7 @@ struct S;
170 136
171impl Foo for S { 137impl Foo for S {
172 fn bar(&self) {} 138 fn bar(&self) {}
173 <|> 139<|>
174}", 140}",
175 " 141 "
176trait Foo { 142trait Foo {
@@ -183,12 +149,9 @@ struct S;
183 149
184impl Foo for S { 150impl 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
209impl Foo for S { 172impl Foo for S {
210 fn bar(&self) {} 173 fn bar(&self) {}
211 <|> 174<|>
212}", 175}",
213 " 176 "
214trait Foo { 177trait Foo {
@@ -221,9 +184,8 @@ struct S;
221 184
222impl Foo for S { 185impl 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 { <|> }",
240trait Foo { fn foo(&self); } 202trait Foo { fn foo(&self); }
241struct S; 203struct S;
242impl Foo for S { 204impl 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 {}<|>",
259trait Foo { fn foo(&self); } 219trait Foo { fn foo(&self); }
260struct S; 220struct S;
261impl Foo for S { 221impl 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 "
299trait Foo {
300 fn valid(some: u32) -> bool;
301}
302struct S;
303
304mod my_mod {
305 impl crate::Foo for S { <|> }
306}",
307 "
308trait Foo {
309 fn valid(some: u32) -> bool;
310}
311struct S;
312
313mod 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}
343struct S; 272struct S;
344impl Foo for S { 273impl 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}
368struct S; 295struct S;
369impl Foo for S { 296impl Foo for S {
370 fn valid(some: u32) -> bool { false }<|> 297 <|>fn valid(some: u32) -> bool { false }
371}", 298}",
372 ) 299 )
373 } 300 }