aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src/handlers/unwrap_block.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists/src/handlers/unwrap_block.rs')
-rw-r--r--crates/ra_assists/src/handlers/unwrap_block.rs512
1 files changed, 512 insertions, 0 deletions
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..8440c7d0f
--- /dev/null
+++ b/crates/ra_assists/src/handlers/unwrap_block.rs
@@ -0,0 +1,512 @@
1use ra_fmt::unwrap_trivial_block;
2use ra_syntax::{
3 ast::{self, ElseBranch, Expr, LoopBodyOwner},
4 match_ast, AstNode, TextRange, T,
5};
6
7use crate::{AssistContext, AssistId, Assists};
8
9// Assist: unwrap_block
10//
11// This assist removes if...else, for, while and loop control statements to just keep the body.
12//
13// ```
14// fn foo() {
15// if true {<|>
16// println!("foo");
17// }
18// }
19// ```
20// ->
21// ```
22// fn foo() {
23// println!("foo");
24// }
25// ```
26pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
27 let l_curly_token = ctx.find_token_at_offset(T!['{'])?;
28 let block = ast::BlockExpr::cast(l_curly_token.parent())?;
29 let parent = block.syntax().parent()?;
30 let assist_id = AssistId("unwrap_block");
31 let assist_label = "Unwrap block";
32
33 let (expr, expr_to_unwrap) = match_ast! {
34 match parent {
35 ast::ForExpr(for_expr) => {
36 let block_expr = for_expr.loop_body()?;
37 let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?;
38 (ast::Expr::ForExpr(for_expr), expr_to_unwrap)
39 },
40 ast::WhileExpr(while_expr) => {
41 let block_expr = while_expr.loop_body()?;
42 let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?;
43 (ast::Expr::WhileExpr(while_expr), expr_to_unwrap)
44 },
45 ast::LoopExpr(loop_expr) => {
46 let block_expr = loop_expr.loop_body()?;
47 let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?;
48 (ast::Expr::LoopExpr(loop_expr), expr_to_unwrap)
49 },
50 ast::IfExpr(if_expr) => {
51 let mut resp = None;
52
53 let then_branch = if_expr.then_branch()?;
54 if then_branch.l_curly_token()?.text_range().contains_range(ctx.frange.range) {
55 if let Some(ancestor) = if_expr.syntax().parent().and_then(ast::IfExpr::cast) {
56 // For `else if` blocks
57 let ancestor_then_branch = ancestor.then_branch()?;
58 let l_curly_token = then_branch.l_curly_token()?;
59
60 let target = then_branch.syntax().text_range();
61 return acc.add(assist_id, assist_label, target, |edit| {
62 let range_to_del_else_if = TextRange::new(ancestor_then_branch.syntax().text_range().end(), l_curly_token.text_range().start());
63 let range_to_del_rest = TextRange::new(then_branch.syntax().text_range().end(), if_expr.syntax().text_range().end());
64
65 edit.delete(range_to_del_rest);
66 edit.delete(range_to_del_else_if);
67 edit.replace(target, update_expr_string(then_branch.to_string(), &[' ', '{']));
68 });
69 } else {
70 resp = Some((ast::Expr::IfExpr(if_expr.clone()), Expr::BlockExpr(then_branch)));
71 }
72 } else if let Some(else_branch) = if_expr.else_branch() {
73 match else_branch {
74 ElseBranch::Block(else_block) => {
75 let l_curly_token = else_block.l_curly_token()?;
76 if l_curly_token.text_range().contains_range(ctx.frange.range) {
77 let target = else_block.syntax().text_range();
78 return acc.add(assist_id, assist_label, target, |edit| {
79 let range_to_del = TextRange::new(then_branch.syntax().text_range().end(), l_curly_token.text_range().start());
80
81 edit.delete(range_to_del);
82 edit.replace(target, update_expr_string(else_block.to_string(), &[' ', '{']));
83 });
84 }
85 },
86 ElseBranch::IfExpr(_) => {},
87 }
88 }
89
90 resp?
91 },
92 _ => return None,
93 }
94 };
95
96 let target = expr_to_unwrap.syntax().text_range();
97 acc.add(assist_id, assist_label, target, |edit| {
98 edit.replace(
99 expr.syntax().text_range(),
100 update_expr_string(expr_to_unwrap.to_string(), &[' ', '{', '\n']),
101 );
102 })
103}
104
105fn extract_expr(cursor_range: TextRange, block: ast::BlockExpr) -> Option<ast::Expr> {
106 let cursor_in_range = block.l_curly_token()?.text_range().contains_range(cursor_range);
107
108 if cursor_in_range {
109 Some(unwrap_trivial_block(block))
110 } else {
111 None
112 }
113}
114
115fn update_expr_string(expr_str: String, trim_start_pat: &[char]) -> String {
116 let expr_string = expr_str.trim_start_matches(trim_start_pat);
117 let mut expr_string_lines: Vec<&str> = expr_string.lines().collect();
118 expr_string_lines.pop(); // Delete last line
119
120 expr_string_lines
121 .into_iter()
122 .map(|line| line.replacen(" ", "", 1)) // Delete indentation
123 .collect::<Vec<String>>()
124 .join("\n")
125}
126
127#[cfg(test)]
128mod tests {
129 use crate::tests::{check_assist, check_assist_not_applicable};
130
131 use super::*;
132
133 #[test]
134 fn simple_if() {
135 check_assist(
136 unwrap_block,
137 r#"
138 fn main() {
139 bar();
140 if true {<|>
141 foo();
142
143 //comment
144 bar();
145 } else {
146 println!("bar");
147 }
148 }
149 "#,
150 r#"
151 fn main() {
152 bar();
153 foo();
154
155 //comment
156 bar();
157 }
158 "#,
159 );
160 }
161
162 #[test]
163 fn simple_if_else() {
164 check_assist(
165 unwrap_block,
166 r#"
167 fn main() {
168 bar();
169 if true {
170 foo();
171
172 //comment
173 bar();
174 } else {<|>
175 println!("bar");
176 }
177 }
178 "#,
179 r#"
180 fn main() {
181 bar();
182 if true {
183 foo();
184
185 //comment
186 bar();
187 }
188 println!("bar");
189 }
190 "#,
191 );
192 }
193
194 #[test]
195 fn simple_if_else_if() {
196 check_assist(
197 unwrap_block,
198 r#"
199 fn main() {
200 //bar();
201 if true {
202 println!("true");
203
204 //comment
205 //bar();
206 } else if false {<|>
207 println!("bar");
208 } else {
209 println!("foo");
210 }
211 }
212 "#,
213 r#"
214 fn main() {
215 //bar();
216 if true {
217 println!("true");
218
219 //comment
220 //bar();
221 }
222 println!("bar");
223 }
224 "#,
225 );
226 }
227
228 #[test]
229 fn simple_if_else_if_nested() {
230 check_assist(
231 unwrap_block,
232 r#"
233 fn main() {
234 //bar();
235 if true {
236 println!("true");
237
238 //comment
239 //bar();
240 } else if false {
241 println!("bar");
242 } else if true {<|>
243 println!("foo");
244 }
245 }
246 "#,
247 r#"
248 fn main() {
249 //bar();
250 if true {
251 println!("true");
252
253 //comment
254 //bar();
255 } else if false {
256 println!("bar");
257 }
258 println!("foo");
259 }
260 "#,
261 );
262 }
263
264 #[test]
265 fn simple_if_else_if_nested_else() {
266 check_assist(
267 unwrap_block,
268 r#"
269 fn main() {
270 //bar();
271 if true {
272 println!("true");
273
274 //comment
275 //bar();
276 } else if false {
277 println!("bar");
278 } else if true {
279 println!("foo");
280 } else {<|>
281 println!("else");
282 }
283 }
284 "#,
285 r#"
286 fn main() {
287 //bar();
288 if true {
289 println!("true");
290
291 //comment
292 //bar();
293 } else if false {
294 println!("bar");
295 } else if true {
296 println!("foo");
297 }
298 println!("else");
299 }
300 "#,
301 );
302 }
303
304 #[test]
305 fn simple_if_else_if_nested_middle() {
306 check_assist(
307 unwrap_block,
308 r#"
309 fn main() {
310 //bar();
311 if true {
312 println!("true");
313
314 //comment
315 //bar();
316 } else if false {
317 println!("bar");
318 } else if true {<|>
319 println!("foo");
320 } else {
321 println!("else");
322 }
323 }
324 "#,
325 r#"
326 fn main() {
327 //bar();
328 if true {
329 println!("true");
330
331 //comment
332 //bar();
333 } else if false {
334 println!("bar");
335 }
336 println!("foo");
337 }
338 "#,
339 );
340 }
341
342 #[test]
343 fn simple_if_bad_cursor_position() {
344 check_assist_not_applicable(
345 unwrap_block,
346 r#"
347 fn main() {
348 bar();<|>
349 if true {
350 foo();
351
352 //comment
353 bar();
354 } else {
355 println!("bar");
356 }
357 }
358 "#,
359 );
360 }
361
362 #[test]
363 fn simple_for() {
364 check_assist(
365 unwrap_block,
366 r#"
367 fn main() {
368 for i in 0..5 {<|>
369 if true {
370 foo();
371
372 //comment
373 bar();
374 } else {
375 println!("bar");
376 }
377 }
378 }
379 "#,
380 r#"
381 fn main() {
382 if true {
383 foo();
384
385 //comment
386 bar();
387 } else {
388 println!("bar");
389 }
390 }
391 "#,
392 );
393 }
394
395 #[test]
396 fn simple_if_in_for() {
397 check_assist(
398 unwrap_block,
399 r#"
400 fn main() {
401 for i in 0..5 {
402 if true {<|>
403 foo();
404
405 //comment
406 bar();
407 } else {
408 println!("bar");
409 }
410 }
411 }
412 "#,
413 r#"
414 fn main() {
415 for i in 0..5 {
416 foo();
417
418 //comment
419 bar();
420 }
421 }
422 "#,
423 );
424 }
425
426 #[test]
427 fn simple_loop() {
428 check_assist(
429 unwrap_block,
430 r#"
431 fn main() {
432 loop {<|>
433 if true {
434 foo();
435
436 //comment
437 bar();
438 } else {
439 println!("bar");
440 }
441 }
442 }
443 "#,
444 r#"
445 fn main() {
446 if true {
447 foo();
448
449 //comment
450 bar();
451 } else {
452 println!("bar");
453 }
454 }
455 "#,
456 );
457 }
458
459 #[test]
460 fn simple_while() {
461 check_assist(
462 unwrap_block,
463 r#"
464 fn main() {
465 while true {<|>
466 if true {
467 foo();
468
469 //comment
470 bar();
471 } else {
472 println!("bar");
473 }
474 }
475 }
476 "#,
477 r#"
478 fn main() {
479 if true {
480 foo();
481
482 //comment
483 bar();
484 } else {
485 println!("bar");
486 }
487 }
488 "#,
489 );
490 }
491
492 #[test]
493 fn simple_if_in_while_bad_cursor_position() {
494 check_assist_not_applicable(
495 unwrap_block,
496 r#"
497 fn main() {
498 while true {
499 if true {
500 foo();<|>
501
502 //comment
503 bar();
504 } else {
505 println!("bar");
506 }
507 }
508 }
509 "#,
510 );
511 }
512}