diff options
Diffstat (limited to 'crates/ide_assists/src/handlers/merge_imports.rs')
-rw-r--r-- | crates/ide_assists/src/handlers/merge_imports.rs | 343 |
1 files changed, 343 insertions, 0 deletions
diff --git a/crates/ide_assists/src/handlers/merge_imports.rs b/crates/ide_assists/src/handlers/merge_imports.rs new file mode 100644 index 000000000..7bd7e1e36 --- /dev/null +++ b/crates/ide_assists/src/handlers/merge_imports.rs | |||
@@ -0,0 +1,343 @@ | |||
1 | use ide_db::helpers::insert_use::{try_merge_imports, try_merge_trees, MergeBehavior}; | ||
2 | use syntax::{ | ||
3 | algo::{neighbor, SyntaxRewriter}, | ||
4 | ast, AstNode, | ||
5 | }; | ||
6 | |||
7 | use crate::{ | ||
8 | assist_context::{AssistContext, Assists}, | ||
9 | utils::next_prev, | ||
10 | AssistId, AssistKind, | ||
11 | }; | ||
12 | |||
13 | // Assist: merge_imports | ||
14 | // | ||
15 | // Merges two imports with a common prefix. | ||
16 | // | ||
17 | // ``` | ||
18 | // use std::$0fmt::Formatter; | ||
19 | // use std::io; | ||
20 | // ``` | ||
21 | // -> | ||
22 | // ``` | ||
23 | // use std::{fmt::Formatter, io}; | ||
24 | // ``` | ||
25 | pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
26 | let tree: ast::UseTree = ctx.find_node_at_offset()?; | ||
27 | let mut rewriter = SyntaxRewriter::default(); | ||
28 | let mut offset = ctx.offset(); | ||
29 | |||
30 | if let Some(use_item) = tree.syntax().parent().and_then(ast::Use::cast) { | ||
31 | let (merged, to_delete) = | ||
32 | next_prev().filter_map(|dir| neighbor(&use_item, dir)).find_map(|use_item2| { | ||
33 | try_merge_imports(&use_item, &use_item2, MergeBehavior::Full).zip(Some(use_item2)) | ||
34 | })?; | ||
35 | |||
36 | rewriter.replace_ast(&use_item, &merged); | ||
37 | rewriter += to_delete.remove(); | ||
38 | |||
39 | if to_delete.syntax().text_range().end() < offset { | ||
40 | offset -= to_delete.syntax().text_range().len(); | ||
41 | } | ||
42 | } else { | ||
43 | let (merged, to_delete) = | ||
44 | next_prev().filter_map(|dir| neighbor(&tree, dir)).find_map(|use_tree| { | ||
45 | try_merge_trees(&tree, &use_tree, MergeBehavior::Full).zip(Some(use_tree)) | ||
46 | })?; | ||
47 | |||
48 | rewriter.replace_ast(&tree, &merged); | ||
49 | rewriter += to_delete.remove(); | ||
50 | |||
51 | if to_delete.syntax().text_range().end() < offset { | ||
52 | offset -= to_delete.syntax().text_range().len(); | ||
53 | } | ||
54 | }; | ||
55 | |||
56 | let target = tree.syntax().text_range(); | ||
57 | acc.add( | ||
58 | AssistId("merge_imports", AssistKind::RefactorRewrite), | ||
59 | "Merge imports", | ||
60 | target, | ||
61 | |builder| { | ||
62 | builder.rewrite(rewriter); | ||
63 | }, | ||
64 | ) | ||
65 | } | ||
66 | |||
67 | #[cfg(test)] | ||
68 | mod tests { | ||
69 | use crate::tests::{check_assist, check_assist_not_applicable}; | ||
70 | |||
71 | use super::*; | ||
72 | |||
73 | #[test] | ||
74 | fn test_merge_equal() { | ||
75 | check_assist( | ||
76 | merge_imports, | ||
77 | r" | ||
78 | use std::fmt$0::{Display, Debug}; | ||
79 | use std::fmt::{Display, Debug}; | ||
80 | ", | ||
81 | r" | ||
82 | use std::fmt::{Debug, Display}; | ||
83 | ", | ||
84 | ) | ||
85 | } | ||
86 | |||
87 | #[test] | ||
88 | fn test_merge_first() { | ||
89 | check_assist( | ||
90 | merge_imports, | ||
91 | r" | ||
92 | use std::fmt$0::Debug; | ||
93 | use std::fmt::Display; | ||
94 | ", | ||
95 | r" | ||
96 | use std::fmt::{Debug, Display}; | ||
97 | ", | ||
98 | ) | ||
99 | } | ||
100 | |||
101 | #[test] | ||
102 | fn test_merge_second() { | ||
103 | check_assist( | ||
104 | merge_imports, | ||
105 | r" | ||
106 | use std::fmt::Debug; | ||
107 | use std::fmt$0::Display; | ||
108 | ", | ||
109 | r" | ||
110 | use std::fmt::{Debug, Display}; | ||
111 | ", | ||
112 | ); | ||
113 | } | ||
114 | |||
115 | #[test] | ||
116 | fn merge_self1() { | ||
117 | check_assist( | ||
118 | merge_imports, | ||
119 | r" | ||
120 | use std::fmt$0; | ||
121 | use std::fmt::Display; | ||
122 | ", | ||
123 | r" | ||
124 | use std::fmt::{self, Display}; | ||
125 | ", | ||
126 | ); | ||
127 | } | ||
128 | |||
129 | #[test] | ||
130 | fn merge_self2() { | ||
131 | check_assist( | ||
132 | merge_imports, | ||
133 | r" | ||
134 | use std::{fmt, $0fmt::Display}; | ||
135 | ", | ||
136 | r" | ||
137 | use std::{fmt::{self, Display}}; | ||
138 | ", | ||
139 | ); | ||
140 | } | ||
141 | |||
142 | #[test] | ||
143 | fn skip_pub1() { | ||
144 | check_assist_not_applicable( | ||
145 | merge_imports, | ||
146 | r" | ||
147 | pub use std::fmt$0::Debug; | ||
148 | use std::fmt::Display; | ||
149 | ", | ||
150 | ); | ||
151 | } | ||
152 | |||
153 | #[test] | ||
154 | fn skip_pub_last() { | ||
155 | check_assist_not_applicable( | ||
156 | merge_imports, | ||
157 | r" | ||
158 | use std::fmt$0::Debug; | ||
159 | pub use std::fmt::Display; | ||
160 | ", | ||
161 | ); | ||
162 | } | ||
163 | |||
164 | #[test] | ||
165 | fn skip_pub_crate_pub() { | ||
166 | check_assist_not_applicable( | ||
167 | merge_imports, | ||
168 | r" | ||
169 | pub(crate) use std::fmt$0::Debug; | ||
170 | pub use std::fmt::Display; | ||
171 | ", | ||
172 | ); | ||
173 | } | ||
174 | |||
175 | #[test] | ||
176 | fn skip_pub_pub_crate() { | ||
177 | check_assist_not_applicable( | ||
178 | merge_imports, | ||
179 | r" | ||
180 | pub use std::fmt$0::Debug; | ||
181 | pub(crate) use std::fmt::Display; | ||
182 | ", | ||
183 | ); | ||
184 | } | ||
185 | |||
186 | #[test] | ||
187 | fn merge_pub() { | ||
188 | check_assist( | ||
189 | merge_imports, | ||
190 | r" | ||
191 | pub use std::fmt$0::Debug; | ||
192 | pub use std::fmt::Display; | ||
193 | ", | ||
194 | r" | ||
195 | pub use std::fmt::{Debug, Display}; | ||
196 | ", | ||
197 | ) | ||
198 | } | ||
199 | |||
200 | #[test] | ||
201 | fn merge_pub_crate() { | ||
202 | check_assist( | ||
203 | merge_imports, | ||
204 | r" | ||
205 | pub(crate) use std::fmt$0::Debug; | ||
206 | pub(crate) use std::fmt::Display; | ||
207 | ", | ||
208 | r" | ||
209 | pub(crate) use std::fmt::{Debug, Display}; | ||
210 | ", | ||
211 | ) | ||
212 | } | ||
213 | |||
214 | #[test] | ||
215 | fn test_merge_nested() { | ||
216 | check_assist( | ||
217 | merge_imports, | ||
218 | r" | ||
219 | use std::{fmt$0::Debug, fmt::Display}; | ||
220 | ", | ||
221 | r" | ||
222 | use std::{fmt::{Debug, Display}}; | ||
223 | ", | ||
224 | ); | ||
225 | } | ||
226 | |||
227 | #[test] | ||
228 | fn test_merge_nested2() { | ||
229 | check_assist( | ||
230 | merge_imports, | ||
231 | r" | ||
232 | use std::{fmt::Debug, fmt$0::Display}; | ||
233 | ", | ||
234 | r" | ||
235 | use std::{fmt::{Debug, Display}}; | ||
236 | ", | ||
237 | ); | ||
238 | } | ||
239 | |||
240 | #[test] | ||
241 | fn test_merge_single_wildcard_diff_prefixes() { | ||
242 | check_assist( | ||
243 | merge_imports, | ||
244 | r" | ||
245 | use std$0::cell::*; | ||
246 | use std::str; | ||
247 | ", | ||
248 | r" | ||
249 | use std::{cell::*, str}; | ||
250 | ", | ||
251 | ) | ||
252 | } | ||
253 | |||
254 | #[test] | ||
255 | fn test_merge_both_wildcard_diff_prefixes() { | ||
256 | check_assist( | ||
257 | merge_imports, | ||
258 | r" | ||
259 | use std$0::cell::*; | ||
260 | use std::str::*; | ||
261 | ", | ||
262 | r" | ||
263 | use std::{cell::*, str::*}; | ||
264 | ", | ||
265 | ) | ||
266 | } | ||
267 | |||
268 | #[test] | ||
269 | fn removes_just_enough_whitespace() { | ||
270 | check_assist( | ||
271 | merge_imports, | ||
272 | r" | ||
273 | use foo$0::bar; | ||
274 | use foo::baz; | ||
275 | |||
276 | /// Doc comment | ||
277 | ", | ||
278 | r" | ||
279 | use foo::{bar, baz}; | ||
280 | |||
281 | /// Doc comment | ||
282 | ", | ||
283 | ); | ||
284 | } | ||
285 | |||
286 | #[test] | ||
287 | fn works_with_trailing_comma() { | ||
288 | check_assist( | ||
289 | merge_imports, | ||
290 | r" | ||
291 | use { | ||
292 | foo$0::bar, | ||
293 | foo::baz, | ||
294 | }; | ||
295 | ", | ||
296 | r" | ||
297 | use { | ||
298 | foo::{bar, baz}, | ||
299 | }; | ||
300 | ", | ||
301 | ); | ||
302 | check_assist( | ||
303 | merge_imports, | ||
304 | r" | ||
305 | use { | ||
306 | foo::baz, | ||
307 | foo$0::bar, | ||
308 | }; | ||
309 | ", | ||
310 | r" | ||
311 | use { | ||
312 | foo::{bar, baz}, | ||
313 | }; | ||
314 | ", | ||
315 | ); | ||
316 | } | ||
317 | |||
318 | #[test] | ||
319 | fn test_double_comma() { | ||
320 | check_assist( | ||
321 | merge_imports, | ||
322 | r" | ||
323 | use foo::bar::baz; | ||
324 | use foo::$0{ | ||
325 | FooBar, | ||
326 | }; | ||
327 | ", | ||
328 | r" | ||
329 | use foo::{FooBar, bar::baz}; | ||
330 | ", | ||
331 | ) | ||
332 | } | ||
333 | |||
334 | #[test] | ||
335 | fn test_empty_use() { | ||
336 | check_assist_not_applicable( | ||
337 | merge_imports, | ||
338 | r" | ||
339 | use std::$0 | ||
340 | fn main() {}", | ||
341 | ); | ||
342 | } | ||
343 | } | ||