]> git.lizzy.rs Git - rust.git/blob - crates/ide_assists/src/handlers/unmerge_use.rs
Merge #8142
[rust.git] / crates / ide_assists / src / handlers / unmerge_use.rs
1 use syntax::{
2     ast::{self, VisibilityOwner},
3     ted::{self, Position},
4     AstNode, SyntaxKind,
5 };
6
7 use crate::{
8     assist_context::{AssistContext, Assists},
9     AssistId, AssistKind,
10 };
11
12 // Assist: unmerge_use
13 //
14 // Extracts single use item from use list.
15 //
16 // ```
17 // use std::fmt::{Debug, Display$0};
18 // ```
19 // ->
20 // ```
21 // use std::fmt::{Debug};
22 // use std::fmt::Display;
23 // ```
24 pub(crate) fn unmerge_use(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
25     let tree: ast::UseTree = ctx.find_node_at_offset::<ast::UseTree>()?.clone_for_update();
26
27     let tree_list = tree.syntax().parent().and_then(ast::UseTreeList::cast)?;
28     if tree_list.use_trees().count() < 2 {
29         cov_mark::hit!(skip_single_use_item);
30         return None;
31     }
32
33     let use_: ast::Use = tree_list.syntax().ancestors().find_map(ast::Use::cast)?;
34     let path = resolve_full_path(&tree)?;
35
36     let old_parent_range = use_.syntax().parent()?.text_range();
37     let new_parent = use_.syntax().parent()?;
38
39     let target = tree.syntax().text_range();
40     acc.add(
41         AssistId("unmerge_use", AssistKind::RefactorRewrite),
42         "Unmerge use",
43         target,
44         |builder| {
45             let new_use = ast::make::use_(
46                 use_.visibility(),
47                 ast::make::use_tree(
48                     path,
49                     tree.use_tree_list(),
50                     tree.rename(),
51                     tree.star_token().is_some(),
52                 ),
53             )
54             .clone_for_update();
55
56             tree.remove();
57             ted::insert(Position::after(use_.syntax()), new_use.syntax());
58
59             builder.replace(old_parent_range, new_parent.to_string());
60         },
61     )
62 }
63
64 fn resolve_full_path(tree: &ast::UseTree) -> Option<ast::Path> {
65     let mut paths = tree
66         .syntax()
67         .ancestors()
68         .take_while(|n| n.kind() != SyntaxKind::USE_KW)
69         .filter_map(ast::UseTree::cast)
70         .filter_map(|t| t.path());
71
72     let mut final_path = paths.next()?;
73     for path in paths {
74         final_path = ast::make::path_concat(path, final_path)
75     }
76     Some(final_path)
77 }
78
79 #[cfg(test)]
80 mod tests {
81     use crate::tests::{check_assist, check_assist_not_applicable};
82
83     use super::*;
84
85     #[test]
86     fn skip_single_use_item() {
87         cov_mark::check!(skip_single_use_item);
88         check_assist_not_applicable(
89             unmerge_use,
90             r"
91 use std::fmt::Debug$0;
92 ",
93         );
94         check_assist_not_applicable(
95             unmerge_use,
96             r"
97 use std::fmt::{Debug$0};
98 ",
99         );
100         check_assist_not_applicable(
101             unmerge_use,
102             r"
103 use std::fmt::Debug as Dbg$0;
104 ",
105         );
106     }
107
108     #[test]
109     fn skip_single_glob_import() {
110         check_assist_not_applicable(
111             unmerge_use,
112             r"
113 use std::fmt::*$0;
114 ",
115         );
116     }
117
118     #[test]
119     fn unmerge_use_item() {
120         check_assist(
121             unmerge_use,
122             r"
123 use std::fmt::{Debug, Display$0};
124 ",
125             r"
126 use std::fmt::{Debug};
127 use std::fmt::Display;
128 ",
129         );
130
131         check_assist(
132             unmerge_use,
133             r"
134 use std::fmt::{Debug, format$0, Display};
135 ",
136             r"
137 use std::fmt::{Debug, Display};
138 use std::fmt::format;
139 ",
140         );
141     }
142
143     #[test]
144     fn unmerge_glob_import() {
145         check_assist(
146             unmerge_use,
147             r"
148 use std::fmt::{*$0, Display};
149 ",
150             r"
151 use std::fmt::{Display};
152 use std::fmt::*;
153 ",
154         );
155     }
156
157     #[test]
158     fn unmerge_renamed_use_item() {
159         check_assist(
160             unmerge_use,
161             r"
162 use std::fmt::{Debug, Display as Disp$0};
163 ",
164             r"
165 use std::fmt::{Debug};
166 use std::fmt::Display as Disp;
167 ",
168         );
169     }
170
171     #[test]
172     fn unmerge_indented_use_item() {
173         check_assist(
174             unmerge_use,
175             r"
176 mod format {
177     use std::fmt::{Debug, Display$0 as Disp, format};
178 }
179 ",
180             r"
181 mod format {
182     use std::fmt::{Debug, format};
183     use std::fmt::Display as Disp;
184 }
185 ",
186         );
187     }
188
189     #[test]
190     fn unmerge_nested_use_item() {
191         check_assist(
192             unmerge_use,
193             r"
194 use foo::bar::{baz::{qux$0, foobar}, barbaz};
195 ",
196             r"
197 use foo::bar::{baz::{foobar}, barbaz};
198 use foo::bar::baz::qux;
199 ",
200         );
201         check_assist(
202             unmerge_use,
203             r"
204 use foo::bar::{baz$0::{qux, foobar}, barbaz};
205 ",
206             r"
207 use foo::bar::{barbaz};
208 use foo::bar::baz::{qux, foobar};
209 ",
210         );
211     }
212
213     #[test]
214     fn unmerge_use_item_with_visibility() {
215         check_assist(
216             unmerge_use,
217             r"
218 pub use std::fmt::{Debug, Display$0};
219 ",
220             r"
221 pub use std::fmt::{Debug};
222 pub use std::fmt::Display;
223 ",
224         );
225     }
226 }