diff options
-rw-r--r-- | crates/assists/src/handlers/unmerge_use.rs | 228 | ||||
-rw-r--r-- | crates/assists/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/assists/src/tests/generated.rs | 14 | ||||
-rw-r--r-- | crates/ide_db/src/helpers/insert_use.rs | 2 | ||||
-rw-r--r-- | crates/syntax/src/ast/make.rs | 8 |
5 files changed, 251 insertions, 3 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 @@ | |||
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 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 | |||
67 | fn 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)] | ||
83 | mod 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" | ||
93 | use std::fmt::Debug$0; | ||
94 | ", | ||
95 | ); | ||
96 | check_assist_not_applicable( | ||
97 | unmerge_use, | ||
98 | r" | ||
99 | use std::fmt::{Debug$0}; | ||
100 | ", | ||
101 | ); | ||
102 | check_assist_not_applicable( | ||
103 | unmerge_use, | ||
104 | r" | ||
105 | use 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" | ||
115 | use std::fmt::*$0; | ||
116 | ", | ||
117 | ); | ||
118 | } | ||
119 | |||
120 | #[test] | ||
121 | fn unmerge_use_item() { | ||
122 | check_assist( | ||
123 | unmerge_use, | ||
124 | r" | ||
125 | use std::fmt::{Debug, Display$0}; | ||
126 | ", | ||
127 | r" | ||
128 | use std::fmt::{Debug}; | ||
129 | use std::fmt::Display; | ||
130 | ", | ||
131 | ); | ||
132 | |||
133 | check_assist( | ||
134 | unmerge_use, | ||
135 | r" | ||
136 | use std::fmt::{Debug, format$0, Display}; | ||
137 | ", | ||
138 | r" | ||
139 | use std::fmt::{Debug, Display}; | ||
140 | use std::fmt::format; | ||
141 | ", | ||
142 | ); | ||
143 | } | ||
144 | |||
145 | #[test] | ||
146 | fn unmerge_glob_import() { | ||
147 | check_assist( | ||
148 | unmerge_use, | ||
149 | r" | ||
150 | use std::fmt::{*$0, Display}; | ||
151 | ", | ||
152 | r" | ||
153 | use std::fmt::{Display}; | ||
154 | use std::fmt::*; | ||
155 | ", | ||
156 | ); | ||
157 | } | ||
158 | |||
159 | #[test] | ||
160 | fn unmerge_renamed_use_item() { | ||
161 | check_assist( | ||
162 | unmerge_use, | ||
163 | r" | ||
164 | use std::fmt::{Debug, Display as Disp$0}; | ||
165 | ", | ||
166 | r" | ||
167 | use std::fmt::{Debug}; | ||
168 | use 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" | ||
178 | mod format { | ||
179 | use std::fmt::{Debug, Display$0 as Disp, format}; | ||
180 | } | ||
181 | ", | ||
182 | r" | ||
183 | mod 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" | ||
196 | use foo::bar::{baz::{qux$0, foobar}, barbaz}; | ||
197 | ", | ||
198 | r" | ||
199 | use foo::bar::{baz::{foobar}, barbaz}; | ||
200 | use foo::bar::baz::qux; | ||
201 | ", | ||
202 | ); | ||
203 | check_assist( | ||
204 | unmerge_use, | ||
205 | r" | ||
206 | use foo::bar::{baz$0::{qux, foobar}, barbaz}; | ||
207 | ", | ||
208 | r" | ||
209 | use foo::bar::{barbaz}; | ||
210 | use 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" | ||
220 | pub use std::fmt::{Debug, Display$0}; | ||
221 | ", | ||
222 | r" | ||
223 | pub use std::fmt::{Debug}; | ||
224 | pub use std::fmt::Display; | ||
225 | ", | ||
226 | ); | ||
227 | } | ||
228 | } | ||
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs index 1080294ab..3d7971806 100644 --- a/crates/assists/src/lib.rs +++ b/crates/assists/src/lib.rs | |||
@@ -156,6 +156,7 @@ mod handlers { | |||
156 | mod replace_unwrap_with_match; | 156 | mod replace_unwrap_with_match; |
157 | mod split_import; | 157 | mod split_import; |
158 | mod toggle_ignore; | 158 | mod toggle_ignore; |
159 | mod unmerge_use; | ||
159 | mod unwrap_block; | 160 | mod unwrap_block; |
160 | mod wrap_return_type_in_result; | 161 | mod wrap_return_type_in_result; |
161 | 162 | ||
@@ -213,6 +214,7 @@ mod handlers { | |||
213 | replace_unwrap_with_match::replace_unwrap_with_match, | 214 | replace_unwrap_with_match::replace_unwrap_with_match, |
214 | split_import::split_import, | 215 | split_import::split_import, |
215 | toggle_ignore::toggle_ignore, | 216 | toggle_ignore::toggle_ignore, |
217 | unmerge_use::unmerge_use, | ||
216 | unwrap_block::unwrap_block, | 218 | unwrap_block::unwrap_block, |
217 | wrap_return_type_in_result::wrap_return_type_in_result, | 219 | wrap_return_type_in_result::wrap_return_type_in_result, |
218 | // These are manually sorted for better priorities | 220 | // These are manually sorted for better priorities |
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs index 217f577eb..d48d063b4 100644 --- a/crates/assists/src/tests/generated.rs +++ b/crates/assists/src/tests/generated.rs | |||
@@ -1138,6 +1138,20 @@ fn arithmetics { | |||
1138 | } | 1138 | } |
1139 | 1139 | ||
1140 | #[test] | 1140 | #[test] |
1141 | fn doctest_unmerge_use() { | ||
1142 | check_doc_test( | ||
1143 | "unmerge_use", | ||
1144 | r#####" | ||
1145 | use std::fmt::{Debug, Display$0}; | ||
1146 | "#####, | ||
1147 | r#####" | ||
1148 | use std::fmt::{Debug}; | ||
1149 | use std::fmt::Display; | ||
1150 | "#####, | ||
1151 | ) | ||
1152 | } | ||
1153 | |||
1154 | #[test] | ||
1141 | fn doctest_unwrap_block() { | 1155 | fn doctest_unwrap_block() { |
1142 | check_doc_test( | 1156 | check_doc_test( |
1143 | "unwrap_block", | 1157 | "unwrap_block", |
diff --git a/crates/ide_db/src/helpers/insert_use.rs b/crates/ide_db/src/helpers/insert_use.rs index 0c180e9bc..d2f9f5d25 100644 --- a/crates/ide_db/src/helpers/insert_use.rs +++ b/crates/ide_db/src/helpers/insert_use.rs | |||
@@ -97,7 +97,7 @@ pub fn insert_use<'a>( | |||
97 | ) -> SyntaxRewriter<'a> { | 97 | ) -> SyntaxRewriter<'a> { |
98 | let _p = profile::span("insert_use"); | 98 | let _p = profile::span("insert_use"); |
99 | let mut rewriter = SyntaxRewriter::default(); | 99 | let mut rewriter = SyntaxRewriter::default(); |
100 | let use_item = make::use_(make::use_tree(path.clone(), None, None, false)); | 100 | let use_item = make::use_(None, make::use_tree(path.clone(), None, None, false)); |
101 | // merge into existing imports if possible | 101 | // merge into existing imports if possible |
102 | if let Some(mb) = merge { | 102 | if let Some(mb) = merge { |
103 | for existing_use in scope.as_syntax_node().children().filter_map(ast::Use::cast) { | 103 | for existing_use in scope.as_syntax_node().children().filter_map(ast::Use::cast) { |
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index 1ed8a96e5..9ffc3ae11 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs | |||
@@ -108,8 +108,12 @@ pub fn use_tree_list(use_trees: impl IntoIterator<Item = ast::UseTree>) -> ast:: | |||
108 | ast_from_text(&format!("use {{{}}};", use_trees)) | 108 | ast_from_text(&format!("use {{{}}};", use_trees)) |
109 | } | 109 | } |
110 | 110 | ||
111 | pub fn use_(use_tree: ast::UseTree) -> ast::Use { | 111 | pub fn use_(visibility: Option<ast::Visibility>, use_tree: ast::UseTree) -> ast::Use { |
112 | ast_from_text(&format!("use {};", use_tree)) | 112 | let visibility = match visibility { |
113 | None => String::new(), | ||
114 | Some(it) => format!("{} ", it), | ||
115 | }; | ||
116 | ast_from_text(&format!("{}use {};", visibility, use_tree)) | ||
113 | } | 117 | } |
114 | 118 | ||
115 | pub fn record_expr_field(name: ast::NameRef, expr: Option<ast::Expr>) -> ast::RecordExprField { | 119 | pub fn record_expr_field(name: ast::NameRef, expr: Option<ast::Expr>) -> ast::RecordExprField { |