diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_assists/src/add_missing_impl_members.rs | 96 | ||||
-rw-r--r-- | crates/ra_assists/src/ast_editor.rs | 41 |
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 | ||
3 | use hir::{HasSource, db::HirDatabase}; | 3 | use hir::{HasSource, db::HirDatabase}; |
4 | use ra_syntax::{SmolStr, TreeArc}; | 4 | use ra_syntax::{SmolStr, TreeArc}; |
5 | use ra_syntax::ast::{self, AstNode, FnDef, ImplItem, ImplItemKind, NameOwner}; | 5 | use ra_syntax::ast::{self, AstNode, ImplItem, ImplItemKind, NameOwner}; |
6 | use ra_db::FilePosition; | 6 | use ra_db::FilePosition; |
7 | 7 | ||
8 | #[derive(PartialEq)] | ||
8 | enum AddMissingImplMembersMode { | 9 | enum 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 | ||
91 | fn add_body_and_strip_docstring(fn_def: &ast::FnDef) -> TreeArc<ast::FnDef> { | 103 | fn 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 | |||
109 | fn 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 | " |
128 | trait Foo { | 145 | trait 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 | " |
141 | trait Foo { | 162 | trait 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 | ||
149 | impl Foo for S { | 174 | impl 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")] |
257 | trait Foo { | 284 | trait 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")] |
266 | trait Foo { | 295 | trait 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 | } |
271 | struct S; | 302 | struct S; |
272 | impl Foo for S { | 303 | impl 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 | " |
283 | trait Foo { | 315 | trait 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; | |||
288 | impl Foo for S { <|> }", | 324 | impl Foo for S { <|> }", |
289 | " | 325 | " |
290 | trait Foo { | 326 | trait 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 | ||
190 | impl 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 | |||
195 | impl AstEditor<ast::FnDef> { | 207 | impl 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 | ||
230 | pub struct AstBuilder<N: AstNode> { | 227 | pub struct AstBuilder<N: AstNode> { |