aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src/add_missing_impl_members.rs
diff options
context:
space:
mode:
authorbors[bot] <bors[bot]@users.noreply.github.com>2019-04-22 11:08:18 +0100
committerbors[bot] <bors[bot]@users.noreply.github.com>2019-04-22 11:08:18 +0100
commit38c0a1e3331f4b2b9efc7caa20b5927874024686 (patch)
tree321dd22a931c2ca11d2e207d734f283844af656d /crates/ra_assists/src/add_missing_impl_members.rs
parent76e0129a21661029dc6cdbea2412ab53efe33aa1 (diff)
parentb73a978b95810b188090a37e002a22403a9067bd (diff)
Merge #1184
1184: Start structured editing API r=matklad a=matklad I think I finally understand how to provide nice, mutable structured editing API on top of red-green trees. The problem I am trying to solve is that any modification to a particular `SyntaxNode` returns an independent new file. So, if you are editing a struct literal, and add a field, you get back a SourceFile, and you have to find the struct literal inside it yourself! This happens because our trees are immutable, but have parent pointers. The main idea here is to introduce `AstEditor<T>` type, which abstracts away that API. So, you create an `AstEditor` for node you want to edit and call various `&mut` taking methods on it. Internally, `AstEditor` stores both the original node and the current node. All edits are applied to the current node, which is replaced by the corresponding node in the new file. In the end, `AstEditor` computes a text edit between old and new nodes. Note that this also should sole a problem when you create an anchor pointing to a subnode and mutate the parent node, invalidating anchor. Because mutation needs `&mut`, all anchors must be killed before modification. Co-authored-by: Aleksey Kladov <[email protected]>
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 }