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