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