diff options
-rw-r--r-- | crates/assists/src/handlers/unmerge_use.rs | 213 | ||||
-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, 236 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..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 | } | ||
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 { |