aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists/src')
-rw-r--r--crates/ra_assists/src/add_missing_impl_members.rs96
-rw-r--r--crates/ra_assists/src/ast_editor.rs41
2 files changed, 87 insertions, 50 deletions
diff --git a/crates/ra_assists/src/add_missing_impl_members.rs b/crates/ra_assists/src/add_missing_impl_members.rs
index 0e49b0401..0c903a563 100644
--- a/crates/ra_assists/src/add_missing_impl_members.rs
+++ b/crates/ra_assists/src/add_missing_impl_members.rs
@@ -2,9 +2,10 @@ use crate::{Assist, AssistId, AssistCtx, ast_editor::{AstEditor, AstBuilder}};
2 2
3use hir::{HasSource, db::HirDatabase}; 3use hir::{HasSource, db::HirDatabase};
4use ra_syntax::{SmolStr, TreeArc}; 4use ra_syntax::{SmolStr, TreeArc};
5use ra_syntax::ast::{self, AstNode, FnDef, ImplItem, ImplItemKind, NameOwner}; 5use ra_syntax::ast::{self, AstNode, ImplItem, ImplItemKind, NameOwner};
6use ra_db::FilePosition; 6use ra_db::FilePosition;
7 7
8#[derive(PartialEq)]
8enum AddMissingImplMembersMode { 9enum AddMissingImplMembersMode {
9 DefaultMethodsOnly, 10 DefaultMethodsOnly,
10 NoDefaultMethods, 11 NoDefaultMethods,
@@ -45,39 +46,50 @@ fn add_missing_impl_members_inner(
45 resolve_target_trait_def(ctx.db, &analyzer, impl_node)? 46 resolve_target_trait_def(ctx.db, &analyzer, impl_node)?
46 }; 47 };
47 48
48 let missing_fns: Vec<_> = { 49 let def_name = |kind| -> Option<&SmolStr> {
49 let fn_def_opt = |kind| if let ImplItemKind::FnDef(def) = kind { Some(def) } else { None }; 50 match kind {
50 let def_name = |def| -> Option<&SmolStr> { FnDef::name(def).map(ast::Name::text) }; 51 ImplItemKind::FnDef(def) => def.name(),
51 52 ImplItemKind::TypeAliasDef(def) => def.name(),
52 let trait_items = 53 ImplItemKind::ConstDef(def) => def.name(),
53 trait_def.syntax().descendants().find_map(ast::ItemList::cast)?.impl_items(); 54 }
54 let impl_items = impl_item_list.impl_items(); 55 .map(ast::Name::text)
55
56 let trait_fns = trait_items.map(ImplItem::kind).filter_map(fn_def_opt);
57 let impl_fns = impl_items.map(ImplItem::kind).filter_map(fn_def_opt).collect::<Vec<_>>();
58
59 trait_fns
60 .filter(|t| def_name(t).is_some())
61 .filter(|t| match mode {
62 AddMissingImplMembersMode::DefaultMethodsOnly => t.body().is_some(),
63 AddMissingImplMembersMode::NoDefaultMethods => t.body().is_none(),
64 })
65 .filter(|t| impl_fns.iter().all(|i| def_name(i) != def_name(t)))
66 .collect()
67 }; 56 };
68 if missing_fns.is_empty() { 57
58 let trait_items = trait_def.item_list()?.impl_items();
59 let impl_items = impl_item_list.impl_items().collect::<Vec<_>>();
60
61 let missing_items: Vec<_> = trait_items
62 .filter(|t| def_name(t.kind()).is_some())
63 .filter(|t| match t.kind() {
64 ImplItemKind::FnDef(def) => match mode {
65 AddMissingImplMembersMode::DefaultMethodsOnly => def.body().is_some(),
66 AddMissingImplMembersMode::NoDefaultMethods => def.body().is_none(),
67 },
68 _ => mode == AddMissingImplMembersMode::NoDefaultMethods,
69 })
70 .filter(|t| impl_items.iter().all(|i| def_name(i.kind()) != def_name(t.kind())))
71 .collect();
72 if missing_items.is_empty() {
69 return None; 73 return None;
70 } 74 }
71 75
72 ctx.add_action(AssistId(assist_id), label, |edit| { 76 ctx.add_action(AssistId(assist_id), label, |edit| {
73 let n_existing_items = impl_item_list.impl_items().count(); 77 let n_existing_items = impl_item_list.impl_items().count();
74 let fns = missing_fns.into_iter().map(add_body_and_strip_docstring).collect::<Vec<_>>();
75
76 let mut ast_editor = AstEditor::new(impl_item_list); 78 let mut ast_editor = AstEditor::new(impl_item_list);
77 if n_existing_items == 0 { 79 if n_existing_items == 0 {
78 ast_editor.make_multiline(); 80 ast_editor.make_multiline();
79 } 81 }
80 ast_editor.append_functions(fns.iter().map(|it| &**it)); 82
83 for item in missing_items {
84 let it = match item.kind() {
85 ImplItemKind::FnDef(def) => {
86 strip_docstring(ImplItem::cast(add_body(def).syntax()).unwrap())
87 }
88 _ => strip_docstring(item),
89 };
90 ast_editor.append_item(&it)
91 }
92
81 let first_new_item = ast_editor.ast().impl_items().nth(n_existing_items).unwrap(); 93 let first_new_item = ast_editor.ast().impl_items().nth(n_existing_items).unwrap();
82 let cursor_poisition = first_new_item.syntax().range().start(); 94 let cursor_poisition = first_new_item.syntax().range().start();
83 ast_editor.into_text_edit(edit.text_edit_builder()); 95 ast_editor.into_text_edit(edit.text_edit_builder());
@@ -88,14 +100,19 @@ fn add_missing_impl_members_inner(
88 ctx.build() 100 ctx.build()
89} 101}
90 102
91fn add_body_and_strip_docstring(fn_def: &ast::FnDef) -> TreeArc<ast::FnDef> { 103fn strip_docstring(item: &ast::ImplItem) -> TreeArc<ast::ImplItem> {
104 let mut ast_editor = AstEditor::new(item);
105 ast_editor.strip_attrs_and_docs();
106 ast_editor.ast().to_owned()
107}
108
109fn add_body(fn_def: &ast::FnDef) -> TreeArc<ast::FnDef> {
92 let mut ast_editor = AstEditor::new(fn_def); 110 let mut ast_editor = AstEditor::new(fn_def);
93 if fn_def.body().is_none() { 111 if fn_def.body().is_none() {
94 ast_editor.set_body(&AstBuilder::<ast::Block>::single_expr( 112 ast_editor.set_body(&AstBuilder::<ast::Block>::single_expr(
95 &AstBuilder::<ast::Expr>::unimplemented(), 113 &AstBuilder::<ast::Expr>::unimplemented(),
96 )); 114 ));
97 } 115 }
98 ast_editor.strip_attrs_and_docs();
99 ast_editor.ast().to_owned() 116 ast_editor.ast().to_owned()
100} 117}
101 118
@@ -126,6 +143,10 @@ mod tests {
126 add_missing_impl_members, 143 add_missing_impl_members,
127 " 144 "
128trait Foo { 145trait Foo {
146 type Output;
147
148 const CONST: usize = 42;
149
129 fn foo(&self); 150 fn foo(&self);
130 fn bar(&self); 151 fn bar(&self);
131 fn baz(&self); 152 fn baz(&self);
@@ -139,6 +160,10 @@ impl Foo for S {
139}", 160}",
140 " 161 "
141trait Foo { 162trait Foo {
163 type Output;
164
165 const CONST: usize = 42;
166
142 fn foo(&self); 167 fn foo(&self);
143 fn bar(&self); 168 fn bar(&self);
144 fn baz(&self); 169 fn baz(&self);
@@ -148,7 +173,9 @@ struct S;
148 173
149impl Foo for S { 174impl Foo for S {
150 fn bar(&self) {} 175 fn bar(&self) {}
151 <|>fn foo(&self) { unimplemented!() } 176 <|>type Output;
177 const CONST: usize = 42;
178 fn foo(&self) { unimplemented!() }
152 fn baz(&self) { unimplemented!() } 179 fn baz(&self) { unimplemented!() }
153 180
154}", 181}",
@@ -256,6 +283,8 @@ impl Foo for S { <|> }",
256#[doc(alias = "test alias")] 283#[doc(alias = "test alias")]
257trait Foo { 284trait Foo {
258 /// doc string 285 /// doc string
286 type Output;
287
259 #[must_use] 288 #[must_use]
260 fn foo(&self); 289 fn foo(&self);
261} 290}
@@ -265,12 +294,15 @@ impl Foo for S {}<|>"#,
265#[doc(alias = "test alias")] 294#[doc(alias = "test alias")]
266trait Foo { 295trait Foo {
267 /// doc string 296 /// doc string
297 type Output;
298
268 #[must_use] 299 #[must_use]
269 fn foo(&self); 300 fn foo(&self);
270} 301}
271struct S; 302struct S;
272impl Foo for S { 303impl Foo for S {
273 <|>fn foo(&self) { unimplemented!() } 304 <|>type Output;
305 fn foo(&self) { unimplemented!() }
274}"#, 306}"#,
275 ) 307 )
276 } 308 }
@@ -281,6 +313,10 @@ impl Foo for S {
281 add_missing_default_members, 313 add_missing_default_members,
282 " 314 "
283trait Foo { 315trait Foo {
316 type Output;
317
318 const CONST: usize = 42;
319
284 fn valid(some: u32) -> bool { false } 320 fn valid(some: u32) -> bool { false }
285 fn foo(some: u32) -> bool; 321 fn foo(some: u32) -> bool;
286} 322}
@@ -288,6 +324,10 @@ struct S;
288impl Foo for S { <|> }", 324impl Foo for S { <|> }",
289 " 325 "
290trait Foo { 326trait Foo {
327 type Output;
328
329 const CONST: usize = 42;
330
291 fn valid(some: u32) -> bool { false } 331 fn valid(some: u32) -> bool { false }
292 fn foo(some: u32) -> bool; 332 fn foo(some: u32) -> bool;
293} 333}
diff --git a/crates/ra_assists/src/ast_editor.rs b/crates/ra_assists/src/ast_editor.rs
index de0529b32..5f8ba3df6 100644
--- a/crates/ra_assists/src/ast_editor.rs
+++ b/crates/ra_assists/src/ast_editor.rs
@@ -163,11 +163,7 @@ impl AstEditor<ast::ItemList> {
163 self.do_make_multiline() 163 self.do_make_multiline()
164 } 164 }
165 165
166 pub fn append_functions<'a>(&mut self, fns: impl Iterator<Item = &'a ast::FnDef>) { 166 pub fn append_item(&mut self, item: &ast::ImplItem) {
167 fns.for_each(|it| self.append_function(it))
168 }
169
170 pub fn append_function(&mut self, fn_def: &ast::FnDef) {
171 let (indent, position) = match self.ast().impl_items().last() { 167 let (indent, position) = match self.ast().impl_items().last() {
172 Some(it) => ( 168 Some(it) => (
173 leading_indent(it.syntax()).unwrap_or("").to_string(), 169 leading_indent(it.syntax()).unwrap_or("").to_string(),
@@ -182,8 +178,7 @@ impl AstEditor<ast::ItemList> {
182 }, 178 },
183 }; 179 };
184 let ws = tokens::WsBuilder::new(&format!("\n{}", indent)); 180 let ws = tokens::WsBuilder::new(&format!("\n{}", indent));
185 let to_insert: ArrayVec<[SyntaxElement; 2]> = 181 let to_insert: ArrayVec<[SyntaxElement; 2]> = [ws.ws().into(), item.syntax().into()].into();
186 [ws.ws().into(), fn_def.syntax().into()].into();
187 self.ast = self.insert_children(position, to_insert.into_iter()); 182 self.ast = self.insert_children(position, to_insert.into_iter());
188 } 183 }
189 184
@@ -192,6 +187,23 @@ impl AstEditor<ast::ItemList> {
192 } 187 }
193} 188}
194 189
190impl AstEditor<ast::ImplItem> {
191 pub fn strip_attrs_and_docs(&mut self) {
192 while let Some(start) = self
193 .ast()
194 .syntax()
195 .children_with_tokens()
196 .find(|it| it.kind() == ATTR || it.kind() == COMMENT)
197 {
198 let end = match start.next_sibling_or_token() {
199 Some(el) if el.kind() == WHITESPACE => el,
200 Some(_) | None => start,
201 };
202 self.ast = self.replace_children(RangeInclusive::new(start, end), iter::empty());
203 }
204 }
205}
206
195impl AstEditor<ast::FnDef> { 207impl AstEditor<ast::FnDef> {
196 pub fn set_body(&mut self, body: &ast::Block) { 208 pub fn set_body(&mut self, body: &ast::Block) {
197 let mut to_insert: ArrayVec<[SyntaxElement; 2]> = ArrayVec::new(); 209 let mut to_insert: ArrayVec<[SyntaxElement; 2]> = ArrayVec::new();
@@ -210,21 +222,6 @@ impl AstEditor<ast::FnDef> {
210 let replace_range = RangeInclusive::new(old_body_or_semi, old_body_or_semi); 222 let replace_range = RangeInclusive::new(old_body_or_semi, old_body_or_semi);
211 self.ast = self.replace_children(replace_range, to_insert.into_iter()) 223 self.ast = self.replace_children(replace_range, to_insert.into_iter())
212 } 224 }
213
214 pub fn strip_attrs_and_docs(&mut self) {
215 while let Some(start) = self
216 .ast()
217 .syntax()
218 .children_with_tokens()
219 .find(|it| it.kind() == ATTR || it.kind() == COMMENT)
220 {
221 let end = match start.next_sibling_or_token() {
222 Some(el) if el.kind() == WHITESPACE => el,
223 Some(_) | None => start,
224 };
225 self.ast = self.replace_children(RangeInclusive::new(start, end), iter::empty());
226 }
227 }
228} 225}
229 226
230pub struct AstBuilder<N: AstNode> { 227pub struct AstBuilder<N: AstNode> {