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