diff options
Diffstat (limited to 'crates/assists/src/handlers')
-rw-r--r-- | crates/assists/src/handlers/unmerge_use.rs | 213 |
1 files changed, 213 insertions, 0 deletions
diff --git a/crates/assists/src/handlers/unmerge_use.rs b/crates/assists/src/handlers/unmerge_use.rs new file mode 100644 index 000000000..d7dfe70d9 --- /dev/null +++ b/crates/assists/src/handlers/unmerge_use.rs | |||
@@ -0,0 +1,213 @@ | |||
1 | use syntax::{ | ||
2 | algo::SyntaxRewriter, | ||
3 | ast::{self, edit::AstNodeEdit, VisibilityOwner}, | ||
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()?; | ||
26 | |||
27 | let tree_list = tree.syntax().parent().and_then(ast::UseTreeList::cast)?; | ||
28 | if tree_list.use_trees().count() < 2 { | ||
29 | return None; | ||
30 | } | ||
31 | |||
32 | let use_: ast::Use = tree_list.syntax().ancestors().find_map(ast::Use::cast)?; | ||
33 | let path = resolve_full_path(&tree)?; | ||
34 | |||
35 | let new_use = ast::make::use_( | ||
36 | use_.visibility(), | ||
37 | ast::make::use_tree(path, None, tree.rename(), tree.star_token().is_some()), | ||
38 | ); | ||
39 | |||
40 | let mut rewriter = SyntaxRewriter::default(); | ||
41 | rewriter += tree.remove(); | ||
42 | rewriter.insert_after(use_.syntax(), &ast::make::tokens::single_newline()); | ||
43 | if let ident_level @ 1..=usize::MAX = use_.indent_level().0 as usize { | ||
44 | rewriter.insert_after( | ||
45 | use_.syntax(), | ||
46 | &ast::make::tokens::whitespace(&" ".repeat(4 * ident_level)), | ||
47 | ); | ||
48 | } | ||
49 | rewriter.insert_after(use_.syntax(), new_use.syntax()); | ||
50 | |||
51 | let target = tree.syntax().text_range(); | ||
52 | acc.add( | ||
53 | AssistId("unmerge_use", AssistKind::RefactorRewrite), | ||
54 | "Unmerge use", | ||
55 | target, | ||
56 | |builder| { | ||
57 | builder.rewrite(rewriter); | ||
58 | }, | ||
59 | ) | ||
60 | } | ||
61 | |||
62 | fn resolve_full_path(tree: &ast::UseTree) -> Option<ast::Path> { | ||
63 | let mut paths = tree | ||
64 | .syntax() | ||
65 | .ancestors() | ||
66 | .take_while(|n| n.kind() != SyntaxKind::USE_KW) | ||
67 | .filter_map(ast::UseTree::cast) | ||
68 | .filter_map(|t| t.path()); | ||
69 | |||
70 | let mut final_path = paths.next()?; | ||
71 | for path in paths { | ||
72 | final_path = ast::make::path_concat(path, final_path) | ||
73 | } | ||
74 | Some(final_path) | ||
75 | } | ||
76 | |||
77 | #[cfg(test)] | ||
78 | mod tests { | ||
79 | use crate::tests::{check_assist, check_assist_not_applicable}; | ||
80 | |||
81 | use super::*; | ||
82 | |||
83 | #[test] | ||
84 | fn skip_single_use_item() { | ||
85 | check_assist_not_applicable( | ||
86 | unmerge_use, | ||
87 | r" | ||
88 | use std::fmt::Debug$0; | ||
89 | ", | ||
90 | ); | ||
91 | check_assist_not_applicable( | ||
92 | unmerge_use, | ||
93 | r" | ||
94 | use std::fmt::{Debug$0}; | ||
95 | ", | ||
96 | ); | ||
97 | check_assist_not_applicable( | ||
98 | unmerge_use, | ||
99 | r" | ||
100 | use std::fmt::Debug as Dbg$0; | ||
101 | ", | ||
102 | ); | ||
103 | } | ||
104 | |||
105 | #[test] | ||
106 | fn skip_single_glob_import() { | ||
107 | check_assist_not_applicable( | ||
108 | unmerge_use, | ||
109 | r" | ||
110 | use std::fmt::*$0; | ||
111 | ", | ||
112 | ); | ||
113 | } | ||
114 | |||
115 | #[test] | ||
116 | fn unmerge_use_item() { | ||
117 | check_assist( | ||
118 | unmerge_use, | ||
119 | r" | ||
120 | use std::fmt::{Debug, Display$0}; | ||
121 | ", | ||
122 | r" | ||
123 | use std::fmt::{Debug}; | ||
124 | use std::fmt::Display; | ||
125 | ", | ||
126 | ); | ||
127 | |||
128 | check_assist( | ||
129 | unmerge_use, | ||
130 | r" | ||
131 | use std::fmt::{Debug, format$0, Display}; | ||
132 | ", | ||
133 | r" | ||
134 | use std::fmt::{Debug, Display}; | ||
135 | use std::fmt::format; | ||
136 | ", | ||
137 | ); | ||
138 | } | ||
139 | |||
140 | #[test] | ||
141 | fn unmerge_glob_import() { | ||
142 | check_assist( | ||
143 | unmerge_use, | ||
144 | r" | ||
145 | use std::fmt::{*$0, Display}; | ||
146 | ", | ||
147 | r" | ||
148 | use std::fmt::{Display}; | ||
149 | use std::fmt::*; | ||
150 | ", | ||
151 | ); | ||
152 | } | ||
153 | |||
154 | #[test] | ||
155 | fn unmerge_renamed_use_item() { | ||
156 | check_assist( | ||
157 | unmerge_use, | ||
158 | r" | ||
159 | use std::fmt::{Debug, Display as Disp$0}; | ||
160 | ", | ||
161 | r" | ||
162 | use std::fmt::{Debug}; | ||
163 | use std::fmt::Display as Disp; | ||
164 | ", | ||
165 | ); | ||
166 | } | ||
167 | |||
168 | #[test] | ||
169 | fn unmerge_indented_use_item() { | ||
170 | check_assist( | ||
171 | unmerge_use, | ||
172 | r" | ||
173 | mod format { | ||
174 | use std::fmt::{Debug, Display$0 as Disp, format}; | ||
175 | } | ||
176 | ", | ||
177 | r" | ||
178 | mod format { | ||
179 | use std::fmt::{Debug, format}; | ||
180 | use std::fmt::Display as Disp; | ||
181 | } | ||
182 | ", | ||
183 | ); | ||
184 | } | ||
185 | |||
186 | #[test] | ||
187 | fn unmerge_nested_use_item() { | ||
188 | check_assist( | ||
189 | unmerge_use, | ||
190 | r" | ||
191 | use foo::bar::{baz::{qux$0, foobar}, barbaz}; | ||
192 | ", | ||
193 | r" | ||
194 | use foo::bar::{baz::{foobar}, barbaz}; | ||
195 | use foo::bar::baz::qux; | ||
196 | ", | ||
197 | ); | ||
198 | } | ||
199 | |||
200 | #[test] | ||
201 | fn unmerge_use_item_with_visibility() { | ||
202 | check_assist( | ||
203 | unmerge_use, | ||
204 | r" | ||
205 | pub use std::fmt::{Debug, Display$0}; | ||
206 | ", | ||
207 | r" | ||
208 | pub use std::fmt::{Debug}; | ||
209 | pub use std::fmt::Display; | ||
210 | ", | ||
211 | ); | ||
212 | } | ||
213 | } | ||