diff options
Diffstat (limited to 'crates/assists/src/handlers/add_custom_impl.rs')
-rw-r--r-- | crates/assists/src/handlers/add_custom_impl.rs | 284 |
1 files changed, 0 insertions, 284 deletions
diff --git a/crates/assists/src/handlers/add_custom_impl.rs b/crates/assists/src/handlers/add_custom_impl.rs deleted file mode 100644 index 669dd9b21..000000000 --- a/crates/assists/src/handlers/add_custom_impl.rs +++ /dev/null | |||
@@ -1,284 +0,0 @@ | |||
1 | use ide_db::imports_locator; | ||
2 | use itertools::Itertools; | ||
3 | use syntax::{ | ||
4 | ast::{self, make, AstNode}, | ||
5 | Direction, SmolStr, | ||
6 | SyntaxKind::{IDENT, WHITESPACE}, | ||
7 | TextRange, TextSize, | ||
8 | }; | ||
9 | |||
10 | use crate::{ | ||
11 | assist_config::SnippetCap, | ||
12 | assist_context::{AssistBuilder, AssistContext, Assists}, | ||
13 | utils::mod_path_to_ast, | ||
14 | AssistId, AssistKind, | ||
15 | }; | ||
16 | |||
17 | // Assist: add_custom_impl | ||
18 | // | ||
19 | // Adds impl block for derived trait. | ||
20 | // | ||
21 | // ``` | ||
22 | // #[derive(Deb<|>ug, Display)] | ||
23 | // struct S; | ||
24 | // ``` | ||
25 | // -> | ||
26 | // ``` | ||
27 | // #[derive(Display)] | ||
28 | // struct S; | ||
29 | // | ||
30 | // impl Debug for S { | ||
31 | // $0 | ||
32 | // } | ||
33 | // ``` | ||
34 | pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
35 | let attr = ctx.find_node_at_offset::<ast::Attr>()?; | ||
36 | |||
37 | let attr_name = attr | ||
38 | .syntax() | ||
39 | .descendants_with_tokens() | ||
40 | .filter(|t| t.kind() == IDENT) | ||
41 | .find_map(syntax::NodeOrToken::into_token) | ||
42 | .filter(|t| t.text() == "derive")? | ||
43 | .text() | ||
44 | .clone(); | ||
45 | |||
46 | let trait_token = | ||
47 | ctx.token_at_offset().find(|t| t.kind() == IDENT && *t.text() != attr_name)?; | ||
48 | let trait_path = make::path_unqualified(make::path_segment(make::name_ref(trait_token.text()))); | ||
49 | |||
50 | let annotated = attr.syntax().siblings(Direction::Next).find_map(ast::Name::cast)?; | ||
51 | let annotated_name = annotated.syntax().text().to_string(); | ||
52 | let insert_pos = annotated.syntax().parent()?.text_range().end(); | ||
53 | |||
54 | let current_module = ctx.sema.scope(annotated.syntax()).module()?; | ||
55 | let current_crate = current_module.krate(); | ||
56 | |||
57 | let found_traits = imports_locator::find_imports(&ctx.sema, current_crate, trait_token.text()) | ||
58 | .into_iter() | ||
59 | .filter_map(|candidate: either::Either<hir::ModuleDef, hir::MacroDef>| match candidate { | ||
60 | either::Either::Left(hir::ModuleDef::Trait(trait_)) => Some(trait_), | ||
61 | _ => None, | ||
62 | }) | ||
63 | .flat_map(|trait_| { | ||
64 | current_module | ||
65 | .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_)) | ||
66 | .as_ref() | ||
67 | .map(mod_path_to_ast) | ||
68 | .zip(Some(trait_)) | ||
69 | }); | ||
70 | |||
71 | let mut no_traits_found = true; | ||
72 | for (trait_path, _trait) in found_traits.inspect(|_| no_traits_found = false) { | ||
73 | add_assist(acc, ctx.config.snippet_cap, &attr, &trait_path, &annotated_name, insert_pos)?; | ||
74 | } | ||
75 | if no_traits_found { | ||
76 | add_assist(acc, ctx.config.snippet_cap, &attr, &trait_path, &annotated_name, insert_pos)?; | ||
77 | } | ||
78 | Some(()) | ||
79 | } | ||
80 | |||
81 | fn add_assist( | ||
82 | acc: &mut Assists, | ||
83 | snippet_cap: Option<SnippetCap>, | ||
84 | attr: &ast::Attr, | ||
85 | trait_path: &ast::Path, | ||
86 | annotated_name: &str, | ||
87 | insert_pos: TextSize, | ||
88 | ) -> Option<()> { | ||
89 | let target = attr.syntax().text_range(); | ||
90 | let input = attr.token_tree()?; | ||
91 | let label = format!("Add custom impl `{}` for `{}`", trait_path, annotated_name); | ||
92 | let trait_name = trait_path.segment().and_then(|seg| seg.name_ref())?; | ||
93 | |||
94 | acc.add(AssistId("add_custom_impl", AssistKind::Refactor), label, target, |builder| { | ||
95 | update_attribute(builder, &input, &trait_name, &attr); | ||
96 | match snippet_cap { | ||
97 | Some(cap) => { | ||
98 | builder.insert_snippet( | ||
99 | cap, | ||
100 | insert_pos, | ||
101 | format!("\n\nimpl {} for {} {{\n $0\n}}", trait_path, annotated_name), | ||
102 | ); | ||
103 | } | ||
104 | None => { | ||
105 | builder.insert( | ||
106 | insert_pos, | ||
107 | format!("\n\nimpl {} for {} {{\n\n}}", trait_path, annotated_name), | ||
108 | ); | ||
109 | } | ||
110 | } | ||
111 | }) | ||
112 | } | ||
113 | |||
114 | fn update_attribute( | ||
115 | builder: &mut AssistBuilder, | ||
116 | input: &ast::TokenTree, | ||
117 | trait_name: &ast::NameRef, | ||
118 | attr: &ast::Attr, | ||
119 | ) { | ||
120 | let new_attr_input = input | ||
121 | .syntax() | ||
122 | .descendants_with_tokens() | ||
123 | .filter(|t| t.kind() == IDENT) | ||
124 | .filter_map(|t| t.into_token().map(|t| t.text().clone())) | ||
125 | .filter(|t| t != trait_name.text()) | ||
126 | .collect::<Vec<SmolStr>>(); | ||
127 | let has_more_derives = !new_attr_input.is_empty(); | ||
128 | |||
129 | if has_more_derives { | ||
130 | let new_attr_input = format!("({})", new_attr_input.iter().format(", ")); | ||
131 | builder.replace(input.syntax().text_range(), new_attr_input); | ||
132 | } else { | ||
133 | let attr_range = attr.syntax().text_range(); | ||
134 | builder.delete(attr_range); | ||
135 | |||
136 | let line_break_range = attr | ||
137 | .syntax() | ||
138 | .next_sibling_or_token() | ||
139 | .filter(|t| t.kind() == WHITESPACE) | ||
140 | .map(|t| t.text_range()) | ||
141 | .unwrap_or_else(|| TextRange::new(TextSize::from(0), TextSize::from(0))); | ||
142 | builder.delete(line_break_range); | ||
143 | } | ||
144 | } | ||
145 | |||
146 | #[cfg(test)] | ||
147 | mod tests { | ||
148 | use crate::tests::{check_assist, check_assist_not_applicable}; | ||
149 | |||
150 | use super::*; | ||
151 | |||
152 | #[test] | ||
153 | fn add_custom_impl_qualified() { | ||
154 | check_assist( | ||
155 | add_custom_impl, | ||
156 | " | ||
157 | mod fmt { | ||
158 | pub trait Debug {} | ||
159 | } | ||
160 | |||
161 | #[derive(Debu<|>g)] | ||
162 | struct Foo { | ||
163 | bar: String, | ||
164 | } | ||
165 | ", | ||
166 | " | ||
167 | mod fmt { | ||
168 | pub trait Debug {} | ||
169 | } | ||
170 | |||
171 | struct Foo { | ||
172 | bar: String, | ||
173 | } | ||
174 | |||
175 | impl fmt::Debug for Foo { | ||
176 | $0 | ||
177 | } | ||
178 | ", | ||
179 | ) | ||
180 | } | ||
181 | #[test] | ||
182 | fn add_custom_impl_for_unique_input() { | ||
183 | check_assist( | ||
184 | add_custom_impl, | ||
185 | " | ||
186 | #[derive(Debu<|>g)] | ||
187 | struct Foo { | ||
188 | bar: String, | ||
189 | } | ||
190 | ", | ||
191 | " | ||
192 | struct Foo { | ||
193 | bar: String, | ||
194 | } | ||
195 | |||
196 | impl Debug for Foo { | ||
197 | $0 | ||
198 | } | ||
199 | ", | ||
200 | ) | ||
201 | } | ||
202 | |||
203 | #[test] | ||
204 | fn add_custom_impl_for_with_visibility_modifier() { | ||
205 | check_assist( | ||
206 | add_custom_impl, | ||
207 | " | ||
208 | #[derive(Debug<|>)] | ||
209 | pub struct Foo { | ||
210 | bar: String, | ||
211 | } | ||
212 | ", | ||
213 | " | ||
214 | pub struct Foo { | ||
215 | bar: String, | ||
216 | } | ||
217 | |||
218 | impl Debug for Foo { | ||
219 | $0 | ||
220 | } | ||
221 | ", | ||
222 | ) | ||
223 | } | ||
224 | |||
225 | #[test] | ||
226 | fn add_custom_impl_when_multiple_inputs() { | ||
227 | check_assist( | ||
228 | add_custom_impl, | ||
229 | " | ||
230 | #[derive(Display, Debug<|>, Serialize)] | ||
231 | struct Foo {} | ||
232 | ", | ||
233 | " | ||
234 | #[derive(Display, Serialize)] | ||
235 | struct Foo {} | ||
236 | |||
237 | impl Debug for Foo { | ||
238 | $0 | ||
239 | } | ||
240 | ", | ||
241 | ) | ||
242 | } | ||
243 | |||
244 | #[test] | ||
245 | fn test_ignore_derive_macro_without_input() { | ||
246 | check_assist_not_applicable( | ||
247 | add_custom_impl, | ||
248 | " | ||
249 | #[derive(<|>)] | ||
250 | struct Foo {} | ||
251 | ", | ||
252 | ) | ||
253 | } | ||
254 | |||
255 | #[test] | ||
256 | fn test_ignore_if_cursor_on_param() { | ||
257 | check_assist_not_applicable( | ||
258 | add_custom_impl, | ||
259 | " | ||
260 | #[derive<|>(Debug)] | ||
261 | struct Foo {} | ||
262 | ", | ||
263 | ); | ||
264 | |||
265 | check_assist_not_applicable( | ||
266 | add_custom_impl, | ||
267 | " | ||
268 | #[derive(Debug)<|>] | ||
269 | struct Foo {} | ||
270 | ", | ||
271 | ) | ||
272 | } | ||
273 | |||
274 | #[test] | ||
275 | fn test_ignore_if_not_derive() { | ||
276 | check_assist_not_applicable( | ||
277 | add_custom_impl, | ||
278 | " | ||
279 | #[allow(non_camel_<|>case_types)] | ||
280 | struct Foo {} | ||
281 | ", | ||
282 | ) | ||
283 | } | ||
284 | } | ||