diff options
-rw-r--r-- | crates/ra_assists/src/doc_tests/generated.rs | 19 | ||||
-rw-r--r-- | crates/ra_assists/src/handlers/unwrap_block.rs | 349 | ||||
-rw-r--r-- | crates/ra_assists/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/expr_extensions.rs | 2 | ||||
-rw-r--r-- | docs/user/assists.md | 18 |
5 files changed, 389 insertions, 1 deletions
diff --git a/crates/ra_assists/src/doc_tests/generated.rs b/crates/ra_assists/src/doc_tests/generated.rs index d6a34b609..6696cc832 100644 --- a/crates/ra_assists/src/doc_tests/generated.rs +++ b/crates/ra_assists/src/doc_tests/generated.rs | |||
@@ -728,3 +728,22 @@ use std::{collections::HashMap}; | |||
728 | "#####, | 728 | "#####, |
729 | ) | 729 | ) |
730 | } | 730 | } |
731 | |||
732 | #[test] | ||
733 | fn doctest_unwrap_block() { | ||
734 | check( | ||
735 | "unwrap_block", | ||
736 | r#####" | ||
737 | fn foo() { | ||
738 | if true {<|> | ||
739 | println!("foo"); | ||
740 | } | ||
741 | } | ||
742 | "#####, | ||
743 | r#####" | ||
744 | fn foo() { | ||
745 | println!("foo"); | ||
746 | } | ||
747 | "#####, | ||
748 | ) | ||
749 | } | ||
diff --git a/crates/ra_assists/src/handlers/unwrap_block.rs b/crates/ra_assists/src/handlers/unwrap_block.rs new file mode 100644 index 000000000..8912ce645 --- /dev/null +++ b/crates/ra_assists/src/handlers/unwrap_block.rs | |||
@@ -0,0 +1,349 @@ | |||
1 | use crate::{Assist, AssistCtx, AssistId}; | ||
2 | |||
3 | use ast::{BlockExpr, Expr, ForExpr, IfExpr, LoopBodyOwner, LoopExpr, WhileExpr}; | ||
4 | use ra_fmt::unwrap_trivial_block; | ||
5 | use ra_syntax::{ast, AstNode, TextRange, T}; | ||
6 | |||
7 | // Assist: unwrap_block | ||
8 | // | ||
9 | // This assist removes if...else, for, while and loop control statements to just keep the body. | ||
10 | // | ||
11 | // ``` | ||
12 | // fn foo() { | ||
13 | // if true {<|> | ||
14 | // println!("foo"); | ||
15 | // } | ||
16 | // } | ||
17 | // ``` | ||
18 | // -> | ||
19 | // ``` | ||
20 | // fn foo() { | ||
21 | // println!("foo"); | ||
22 | // } | ||
23 | // ``` | ||
24 | pub(crate) fn unwrap_block(ctx: AssistCtx) -> Option<Assist> { | ||
25 | let l_curly_token = ctx.find_token_at_offset(T!['{'])?; | ||
26 | |||
27 | let res = if let Some(if_expr) = l_curly_token.ancestors().find_map(IfExpr::cast) { | ||
28 | // if expression | ||
29 | let expr_to_unwrap = if_expr.blocks().find_map(|expr| extract_expr(ctx.frange.range, expr)); | ||
30 | let expr_to_unwrap = expr_to_unwrap?; | ||
31 | // Find if we are in a else if block | ||
32 | let ancestor = if_expr.syntax().ancestors().skip(1).find_map(ast::IfExpr::cast); | ||
33 | |||
34 | if let Some(ancestor) = ancestor { | ||
35 | Some((ast::Expr::IfExpr(ancestor), expr_to_unwrap)) | ||
36 | } else { | ||
37 | Some((ast::Expr::IfExpr(if_expr), expr_to_unwrap)) | ||
38 | } | ||
39 | } else if let Some(for_expr) = l_curly_token.ancestors().find_map(ForExpr::cast) { | ||
40 | // for expression | ||
41 | let block_expr = for_expr.loop_body()?; | ||
42 | extract_expr(ctx.frange.range, block_expr) | ||
43 | .map(|expr_to_unwrap| (ast::Expr::ForExpr(for_expr), expr_to_unwrap)) | ||
44 | } else if let Some(while_expr) = l_curly_token.ancestors().find_map(WhileExpr::cast) { | ||
45 | // while expression | ||
46 | let block_expr = while_expr.loop_body()?; | ||
47 | extract_expr(ctx.frange.range, block_expr) | ||
48 | .map(|expr_to_unwrap| (ast::Expr::WhileExpr(while_expr), expr_to_unwrap)) | ||
49 | } else if let Some(loop_expr) = l_curly_token.ancestors().find_map(LoopExpr::cast) { | ||
50 | // loop expression | ||
51 | let block_expr = loop_expr.loop_body()?; | ||
52 | extract_expr(ctx.frange.range, block_expr) | ||
53 | .map(|expr_to_unwrap| (ast::Expr::LoopExpr(loop_expr), expr_to_unwrap)) | ||
54 | } else { | ||
55 | None | ||
56 | }; | ||
57 | |||
58 | let (expr, expr_to_unwrap) = res?; | ||
59 | ctx.add_assist(AssistId("unwrap_block"), "Unwrap block", |edit| { | ||
60 | edit.set_cursor(expr.syntax().text_range().start()); | ||
61 | edit.target(expr_to_unwrap.syntax().text_range()); | ||
62 | |||
63 | let pat_start: &[_] = &[' ', '{', '\n']; | ||
64 | let expr_to_unwrap = expr_to_unwrap.to_string(); | ||
65 | let expr_string = expr_to_unwrap.trim_start_matches(pat_start); | ||
66 | let mut expr_string_lines: Vec<&str> = expr_string.lines().collect(); | ||
67 | expr_string_lines.pop(); // Delete last line | ||
68 | |||
69 | let expr_string = expr_string_lines | ||
70 | .into_iter() | ||
71 | .map(|line| line.replacen(" ", "", 1)) // Delete indentation | ||
72 | .collect::<Vec<String>>() | ||
73 | .join("\n"); | ||
74 | |||
75 | edit.replace(expr.syntax().text_range(), expr_string); | ||
76 | }) | ||
77 | } | ||
78 | |||
79 | fn extract_expr(cursor_range: TextRange, block_expr: BlockExpr) -> Option<Expr> { | ||
80 | let block = block_expr.block()?; | ||
81 | let cursor_in_range = block.l_curly_token()?.text_range().contains_range(cursor_range); | ||
82 | |||
83 | if cursor_in_range { | ||
84 | Some(unwrap_trivial_block(block_expr)) | ||
85 | } else { | ||
86 | None | ||
87 | } | ||
88 | } | ||
89 | |||
90 | #[cfg(test)] | ||
91 | mod tests { | ||
92 | use crate::helpers::{check_assist, check_assist_not_applicable}; | ||
93 | |||
94 | use super::*; | ||
95 | |||
96 | #[test] | ||
97 | fn simple_if() { | ||
98 | check_assist( | ||
99 | unwrap_block, | ||
100 | r#" | ||
101 | fn main() { | ||
102 | bar(); | ||
103 | if true {<|> | ||
104 | foo(); | ||
105 | |||
106 | //comment | ||
107 | bar(); | ||
108 | } else { | ||
109 | println!("bar"); | ||
110 | } | ||
111 | } | ||
112 | "#, | ||
113 | r#" | ||
114 | fn main() { | ||
115 | bar(); | ||
116 | <|>foo(); | ||
117 | |||
118 | //comment | ||
119 | bar(); | ||
120 | } | ||
121 | "#, | ||
122 | ); | ||
123 | } | ||
124 | |||
125 | #[test] | ||
126 | fn simple_if_else() { | ||
127 | check_assist( | ||
128 | unwrap_block, | ||
129 | r#" | ||
130 | fn main() { | ||
131 | bar(); | ||
132 | if true { | ||
133 | foo(); | ||
134 | |||
135 | //comment | ||
136 | bar(); | ||
137 | } else {<|> | ||
138 | println!("bar"); | ||
139 | } | ||
140 | } | ||
141 | "#, | ||
142 | r#" | ||
143 | fn main() { | ||
144 | bar(); | ||
145 | <|>println!("bar"); | ||
146 | } | ||
147 | "#, | ||
148 | ); | ||
149 | } | ||
150 | |||
151 | #[test] | ||
152 | fn simple_if_else_if() { | ||
153 | check_assist( | ||
154 | unwrap_block, | ||
155 | r#" | ||
156 | fn main() { | ||
157 | //bar(); | ||
158 | if true { | ||
159 | println!("true"); | ||
160 | |||
161 | //comment | ||
162 | //bar(); | ||
163 | } else if false {<|> | ||
164 | println!("bar"); | ||
165 | } else { | ||
166 | println!("foo"); | ||
167 | } | ||
168 | } | ||
169 | "#, | ||
170 | r#" | ||
171 | fn main() { | ||
172 | //bar(); | ||
173 | <|>println!("bar"); | ||
174 | } | ||
175 | "#, | ||
176 | ); | ||
177 | } | ||
178 | |||
179 | #[test] | ||
180 | fn simple_if_bad_cursor_position() { | ||
181 | check_assist_not_applicable( | ||
182 | unwrap_block, | ||
183 | r#" | ||
184 | fn main() { | ||
185 | bar();<|> | ||
186 | if true { | ||
187 | foo(); | ||
188 | |||
189 | //comment | ||
190 | bar(); | ||
191 | } else { | ||
192 | println!("bar"); | ||
193 | } | ||
194 | } | ||
195 | "#, | ||
196 | ); | ||
197 | } | ||
198 | |||
199 | #[test] | ||
200 | fn simple_for() { | ||
201 | check_assist( | ||
202 | unwrap_block, | ||
203 | r#" | ||
204 | fn main() { | ||
205 | for i in 0..5 {<|> | ||
206 | if true { | ||
207 | foo(); | ||
208 | |||
209 | //comment | ||
210 | bar(); | ||
211 | } else { | ||
212 | println!("bar"); | ||
213 | } | ||
214 | } | ||
215 | } | ||
216 | "#, | ||
217 | r#" | ||
218 | fn main() { | ||
219 | <|>if true { | ||
220 | foo(); | ||
221 | |||
222 | //comment | ||
223 | bar(); | ||
224 | } else { | ||
225 | println!("bar"); | ||
226 | } | ||
227 | } | ||
228 | "#, | ||
229 | ); | ||
230 | } | ||
231 | |||
232 | #[test] | ||
233 | fn simple_if_in_for() { | ||
234 | check_assist( | ||
235 | unwrap_block, | ||
236 | r#" | ||
237 | fn main() { | ||
238 | for i in 0..5 { | ||
239 | if true {<|> | ||
240 | foo(); | ||
241 | |||
242 | //comment | ||
243 | bar(); | ||
244 | } else { | ||
245 | println!("bar"); | ||
246 | } | ||
247 | } | ||
248 | } | ||
249 | "#, | ||
250 | r#" | ||
251 | fn main() { | ||
252 | for i in 0..5 { | ||
253 | <|>foo(); | ||
254 | |||
255 | //comment | ||
256 | bar(); | ||
257 | } | ||
258 | } | ||
259 | "#, | ||
260 | ); | ||
261 | } | ||
262 | |||
263 | #[test] | ||
264 | fn simple_loop() { | ||
265 | check_assist( | ||
266 | unwrap_block, | ||
267 | r#" | ||
268 | fn main() { | ||
269 | loop {<|> | ||
270 | if true { | ||
271 | foo(); | ||
272 | |||
273 | //comment | ||
274 | bar(); | ||
275 | } else { | ||
276 | println!("bar"); | ||
277 | } | ||
278 | } | ||
279 | } | ||
280 | "#, | ||
281 | r#" | ||
282 | fn main() { | ||
283 | <|>if true { | ||
284 | foo(); | ||
285 | |||
286 | //comment | ||
287 | bar(); | ||
288 | } else { | ||
289 | println!("bar"); | ||
290 | } | ||
291 | } | ||
292 | "#, | ||
293 | ); | ||
294 | } | ||
295 | |||
296 | #[test] | ||
297 | fn simple_while() { | ||
298 | check_assist( | ||
299 | unwrap_block, | ||
300 | r#" | ||
301 | fn main() { | ||
302 | while true {<|> | ||
303 | if true { | ||
304 | foo(); | ||
305 | |||
306 | //comment | ||
307 | bar(); | ||
308 | } else { | ||
309 | println!("bar"); | ||
310 | } | ||
311 | } | ||
312 | } | ||
313 | "#, | ||
314 | r#" | ||
315 | fn main() { | ||
316 | <|>if true { | ||
317 | foo(); | ||
318 | |||
319 | //comment | ||
320 | bar(); | ||
321 | } else { | ||
322 | println!("bar"); | ||
323 | } | ||
324 | } | ||
325 | "#, | ||
326 | ); | ||
327 | } | ||
328 | |||
329 | #[test] | ||
330 | fn simple_if_in_while_bad_cursor_position() { | ||
331 | check_assist_not_applicable( | ||
332 | unwrap_block, | ||
333 | r#" | ||
334 | fn main() { | ||
335 | while true { | ||
336 | if true { | ||
337 | foo();<|> | ||
338 | |||
339 | //comment | ||
340 | bar(); | ||
341 | } else { | ||
342 | println!("bar"); | ||
343 | } | ||
344 | } | ||
345 | } | ||
346 | "#, | ||
347 | ); | ||
348 | } | ||
349 | } | ||
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index 64bd87afb..c5df86600 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs | |||
@@ -143,6 +143,7 @@ mod handlers { | |||
143 | mod split_import; | 143 | mod split_import; |
144 | mod add_from_impl_for_enum; | 144 | mod add_from_impl_for_enum; |
145 | mod reorder_fields; | 145 | mod reorder_fields; |
146 | mod unwrap_block; | ||
146 | 147 | ||
147 | pub(crate) fn all() -> &'static [AssistHandler] { | 148 | pub(crate) fn all() -> &'static [AssistHandler] { |
148 | &[ | 149 | &[ |
@@ -181,6 +182,7 @@ mod handlers { | |||
181 | replace_unwrap_with_match::replace_unwrap_with_match, | 182 | replace_unwrap_with_match::replace_unwrap_with_match, |
182 | split_import::split_import, | 183 | split_import::split_import, |
183 | add_from_impl_for_enum::add_from_impl_for_enum, | 184 | add_from_impl_for_enum::add_from_impl_for_enum, |
185 | unwrap_block::unwrap_block, | ||
184 | // These are manually sorted for better priorities | 186 | // These are manually sorted for better priorities |
185 | add_missing_impl_members::add_missing_impl_members, | 187 | add_missing_impl_members::add_missing_impl_members, |
186 | add_missing_impl_members::add_missing_default_members, | 188 | add_missing_impl_members::add_missing_default_members, |
diff --git a/crates/ra_syntax/src/ast/expr_extensions.rs b/crates/ra_syntax/src/ast/expr_extensions.rs index 7ee36e60c..7771d6759 100644 --- a/crates/ra_syntax/src/ast/expr_extensions.rs +++ b/crates/ra_syntax/src/ast/expr_extensions.rs | |||
@@ -43,7 +43,7 @@ impl ast::IfExpr { | |||
43 | Some(res) | 43 | Some(res) |
44 | } | 44 | } |
45 | 45 | ||
46 | fn blocks(&self) -> AstChildren<ast::BlockExpr> { | 46 | pub fn blocks(&self) -> AstChildren<ast::BlockExpr> { |
47 | support::children(self.syntax()) | 47 | support::children(self.syntax()) |
48 | } | 48 | } |
49 | } | 49 | } |
diff --git a/docs/user/assists.md b/docs/user/assists.md index 5a83c4a98..ee515949e 100644 --- a/docs/user/assists.md +++ b/docs/user/assists.md | |||
@@ -697,3 +697,21 @@ use std::┃collections::HashMap; | |||
697 | // AFTER | 697 | // AFTER |
698 | use std::{collections::HashMap}; | 698 | use std::{collections::HashMap}; |
699 | ``` | 699 | ``` |
700 | |||
701 | ## `unwrap_block` | ||
702 | |||
703 | This assist removes if...else, for, while and loop control statements to just keep the body. | ||
704 | |||
705 | ```rust | ||
706 | // BEFORE | ||
707 | fn foo() { | ||
708 | if true {┃ | ||
709 | println!("foo"); | ||
710 | } | ||
711 | } | ||
712 | |||
713 | // AFTER | ||
714 | fn foo() { | ||
715 | println!("foo"); | ||
716 | } | ||
717 | ``` | ||