aboutsummaryrefslogtreecommitdiff
path: root/crates/assists/src/handlers
diff options
context:
space:
mode:
Diffstat (limited to 'crates/assists/src/handlers')
-rw-r--r--crates/assists/src/handlers/unmerge_use.rs213
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 @@
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 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
62fn 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)]
78mod 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"
88use std::fmt::Debug$0;
89",
90 );
91 check_assist_not_applicable(
92 unmerge_use,
93 r"
94use std::fmt::{Debug$0};
95",
96 );
97 check_assist_not_applicable(
98 unmerge_use,
99 r"
100use 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"
110use std::fmt::*$0;
111",
112 );
113 }
114
115 #[test]
116 fn unmerge_use_item() {
117 check_assist(
118 unmerge_use,
119 r"
120use std::fmt::{Debug, Display$0};
121",
122 r"
123use std::fmt::{Debug};
124use std::fmt::Display;
125",
126 );
127
128 check_assist(
129 unmerge_use,
130 r"
131use std::fmt::{Debug, format$0, Display};
132",
133 r"
134use std::fmt::{Debug, Display};
135use std::fmt::format;
136",
137 );
138 }
139
140 #[test]
141 fn unmerge_glob_import() {
142 check_assist(
143 unmerge_use,
144 r"
145use std::fmt::{*$0, Display};
146",
147 r"
148use std::fmt::{Display};
149use std::fmt::*;
150",
151 );
152 }
153
154 #[test]
155 fn unmerge_renamed_use_item() {
156 check_assist(
157 unmerge_use,
158 r"
159use std::fmt::{Debug, Display as Disp$0};
160",
161 r"
162use std::fmt::{Debug};
163use 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"
173mod format {
174 use std::fmt::{Debug, Display$0 as Disp, format};
175}
176",
177 r"
178mod 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"
191use foo::bar::{baz::{qux$0, foobar}, barbaz};
192",
193 r"
194use foo::bar::{baz::{foobar}, barbaz};
195use 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"
205pub use std::fmt::{Debug, Display$0};
206",
207 r"
208pub use std::fmt::{Debug};
209pub use std::fmt::Display;
210",
211 );
212 }
213}