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 | 348 | ||||
-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, 388 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..58649c47e --- /dev/null +++ b/crates/ra_assists/src/handlers/unwrap_block.rs | |||
@@ -0,0 +1,348 @@ | |||
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: BlockExpr) -> Option<Expr> { | ||
80 | let cursor_in_range = block.l_curly_token()?.text_range().contains_range(cursor_range); | ||
81 | |||
82 | if cursor_in_range { | ||
83 | Some(unwrap_trivial_block(block)) | ||
84 | } else { | ||
85 | None | ||
86 | } | ||
87 | } | ||
88 | |||
89 | #[cfg(test)] | ||
90 | mod tests { | ||
91 | use crate::helpers::{check_assist, check_assist_not_applicable}; | ||
92 | |||
93 | use super::*; | ||
94 | |||
95 | #[test] | ||
96 | fn simple_if() { | ||
97 | check_assist( | ||
98 | unwrap_block, | ||
99 | r#" | ||
100 | fn main() { | ||
101 | bar(); | ||
102 | if true {<|> | ||
103 | foo(); | ||
104 | |||
105 | //comment | ||
106 | bar(); | ||
107 | } else { | ||
108 | println!("bar"); | ||
109 | } | ||
110 | } | ||
111 | "#, | ||
112 | r#" | ||
113 | fn main() { | ||
114 | bar(); | ||
115 | <|>foo(); | ||
116 | |||
117 | //comment | ||
118 | bar(); | ||
119 | } | ||
120 | "#, | ||
121 | ); | ||
122 | } | ||
123 | |||
124 | #[test] | ||
125 | fn simple_if_else() { | ||
126 | check_assist( | ||
127 | unwrap_block, | ||
128 | r#" | ||
129 | fn main() { | ||
130 | bar(); | ||
131 | if true { | ||
132 | foo(); | ||
133 | |||
134 | //comment | ||
135 | bar(); | ||
136 | } else {<|> | ||
137 | println!("bar"); | ||
138 | } | ||
139 | } | ||
140 | "#, | ||
141 | r#" | ||
142 | fn main() { | ||
143 | bar(); | ||
144 | <|>println!("bar"); | ||
145 | } | ||
146 | "#, | ||
147 | ); | ||
148 | } | ||
149 | |||
150 | #[test] | ||
151 | fn simple_if_else_if() { | ||
152 | check_assist( | ||
153 | unwrap_block, | ||
154 | r#" | ||
155 | fn main() { | ||
156 | //bar(); | ||
157 | if true { | ||
158 | println!("true"); | ||
159 | |||
160 | //comment | ||
161 | //bar(); | ||
162 | } else if false {<|> | ||
163 | println!("bar"); | ||
164 | } else { | ||
165 | println!("foo"); | ||
166 | } | ||
167 | } | ||
168 | "#, | ||
169 | r#" | ||
170 | fn main() { | ||
171 | //bar(); | ||
172 | <|>println!("bar"); | ||
173 | } | ||
174 | "#, | ||
175 | ); | ||
176 | } | ||
177 | |||
178 | #[test] | ||
179 | fn simple_if_bad_cursor_position() { | ||
180 | check_assist_not_applicable( | ||
181 | unwrap_block, | ||
182 | r#" | ||
183 | fn main() { | ||
184 | bar();<|> | ||
185 | if true { | ||
186 | foo(); | ||
187 | |||
188 | //comment | ||
189 | bar(); | ||
190 | } else { | ||
191 | println!("bar"); | ||
192 | } | ||
193 | } | ||
194 | "#, | ||
195 | ); | ||
196 | } | ||
197 | |||
198 | #[test] | ||
199 | fn simple_for() { | ||
200 | check_assist( | ||
201 | unwrap_block, | ||
202 | r#" | ||
203 | fn main() { | ||
204 | for i in 0..5 {<|> | ||
205 | if true { | ||
206 | foo(); | ||
207 | |||
208 | //comment | ||
209 | bar(); | ||
210 | } else { | ||
211 | println!("bar"); | ||
212 | } | ||
213 | } | ||
214 | } | ||
215 | "#, | ||
216 | r#" | ||
217 | fn main() { | ||
218 | <|>if true { | ||
219 | foo(); | ||
220 | |||
221 | //comment | ||
222 | bar(); | ||
223 | } else { | ||
224 | println!("bar"); | ||
225 | } | ||
226 | } | ||
227 | "#, | ||
228 | ); | ||
229 | } | ||
230 | |||
231 | #[test] | ||
232 | fn simple_if_in_for() { | ||
233 | check_assist( | ||
234 | unwrap_block, | ||
235 | r#" | ||
236 | fn main() { | ||
237 | for i in 0..5 { | ||
238 | if true {<|> | ||
239 | foo(); | ||
240 | |||
241 | //comment | ||
242 | bar(); | ||
243 | } else { | ||
244 | println!("bar"); | ||
245 | } | ||
246 | } | ||
247 | } | ||
248 | "#, | ||
249 | r#" | ||
250 | fn main() { | ||
251 | for i in 0..5 { | ||
252 | <|>foo(); | ||
253 | |||
254 | //comment | ||
255 | bar(); | ||
256 | } | ||
257 | } | ||
258 | "#, | ||
259 | ); | ||
260 | } | ||
261 | |||
262 | #[test] | ||
263 | fn simple_loop() { | ||
264 | check_assist( | ||
265 | unwrap_block, | ||
266 | r#" | ||
267 | fn main() { | ||
268 | loop {<|> | ||
269 | if true { | ||
270 | foo(); | ||
271 | |||
272 | //comment | ||
273 | bar(); | ||
274 | } else { | ||
275 | println!("bar"); | ||
276 | } | ||
277 | } | ||
278 | } | ||
279 | "#, | ||
280 | r#" | ||
281 | fn main() { | ||
282 | <|>if true { | ||
283 | foo(); | ||
284 | |||
285 | //comment | ||
286 | bar(); | ||
287 | } else { | ||
288 | println!("bar"); | ||
289 | } | ||
290 | } | ||
291 | "#, | ||
292 | ); | ||
293 | } | ||
294 | |||
295 | #[test] | ||
296 | fn simple_while() { | ||
297 | check_assist( | ||
298 | unwrap_block, | ||
299 | r#" | ||
300 | fn main() { | ||
301 | while true {<|> | ||
302 | if true { | ||
303 | foo(); | ||
304 | |||
305 | //comment | ||
306 | bar(); | ||
307 | } else { | ||
308 | println!("bar"); | ||
309 | } | ||
310 | } | ||
311 | } | ||
312 | "#, | ||
313 | r#" | ||
314 | fn main() { | ||
315 | <|>if true { | ||
316 | foo(); | ||
317 | |||
318 | //comment | ||
319 | bar(); | ||
320 | } else { | ||
321 | println!("bar"); | ||
322 | } | ||
323 | } | ||
324 | "#, | ||
325 | ); | ||
326 | } | ||
327 | |||
328 | #[test] | ||
329 | fn simple_if_in_while_bad_cursor_position() { | ||
330 | check_assist_not_applicable( | ||
331 | unwrap_block, | ||
332 | r#" | ||
333 | fn main() { | ||
334 | while true { | ||
335 | if true { | ||
336 | foo();<|> | ||
337 | |||
338 | //comment | ||
339 | bar(); | ||
340 | } else { | ||
341 | println!("bar"); | ||
342 | } | ||
343 | } | ||
344 | } | ||
345 | "#, | ||
346 | ); | ||
347 | } | ||
348 | } | ||
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 | ``` | ||