1use syntax::{
2 AstNode, SyntaxKind, T, TextRange,
3 ast::{
4 self,
5 edit::{AstNodeEdit, IndentLevel},
6 make,
7 },
8};
9
10use crate::{AssistContext, AssistId, Assists};
11
12pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
30 let assist_id = AssistId::refactor_rewrite("unwrap_block");
31 let assist_label = "Unwrap block";
32 let l_curly_token = ctx.find_token_syntax_at_offset(T!['{'])?;
33 let mut block = ast::BlockExpr::cast(l_curly_token.parent_ancestors().nth(1)?)?;
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 let kind = parent.kind();
41 if matches!(kind, SyntaxKind::STMT_LIST | SyntaxKind::EXPR_STMT) {
42 acc.add(assist_id, assist_label, target, |builder| {
43 builder.replace(block.syntax().text_range(), update_expr_string(block.to_string()));
44 })
45 } else if matches!(kind, SyntaxKind::LET_STMT) {
46 let parent = ast::LetStmt::cast(parent)?;
47 let pattern = ast::Pat::cast(parent.syntax().first_child()?)?;
48 let ty = parent.ty();
49 let list = block.stmt_list()?;
50 let replaced = match list.syntax().last_child() {
51 Some(last) => {
52 let stmts: Vec<ast::Stmt> = list.statements().collect();
53 let initializer = ast::Expr::cast(last)?;
54 let let_stmt = make::let_stmt(pattern, ty, Some(initializer));
55 if !stmts.is_empty() {
56 let block = make::block_expr(stmts, None);
57 format!("{}\n {}", update_expr_string(block.to_string()), let_stmt)
58 } else {
59 let_stmt.to_string()
60 }
61 }
62 None => {
63 let empty_tuple = make::ext::expr_unit();
64 make::let_stmt(pattern, ty, Some(empty_tuple)).to_string()
65 }
66 };
67 acc.add(assist_id, assist_label, target, |builder| {
68 builder.replace(parent.syntax().text_range(), replaced);
69 })
70 } else {
71 let parent = ast::Expr::cast(parent)?;
72 match parent.clone() {
73 ast::Expr::ForExpr(_) | ast::Expr::WhileExpr(_) | ast::Expr::LoopExpr(_) => (),
74 ast::Expr::MatchExpr(_) => block = block.dedent(IndentLevel(1)),
75 ast::Expr::IfExpr(if_expr) => {
76 let then_branch = if_expr.then_branch()?;
77 if then_branch == block {
78 if let Some(ancestor) = if_expr.syntax().parent().and_then(ast::IfExpr::cast) {
79 let ancestor_then_branch = ancestor.then_branch()?;
81
82 return acc.add(assist_id, assist_label, target, |edit| {
83 let range_to_del_else_if = TextRange::new(
84 ancestor_then_branch.syntax().text_range().end(),
85 l_curly_token.text_range().start(),
86 );
87 let range_to_del_rest = TextRange::new(
88 then_branch.syntax().text_range().end(),
89 if_expr.syntax().text_range().end(),
90 );
91
92 edit.delete(range_to_del_rest);
93 edit.delete(range_to_del_else_if);
94 edit.replace(
95 target,
96 update_expr_string_without_newline(then_branch.to_string()),
97 );
98 });
99 }
100 } else {
101 return acc.add(assist_id, assist_label, target, |edit| {
102 let range_to_del = TextRange::new(
103 then_branch.syntax().text_range().end(),
104 l_curly_token.text_range().start(),
105 );
106
107 edit.delete(range_to_del);
108 edit.replace(target, update_expr_string_without_newline(block.to_string()));
109 });
110 }
111 }
112 _ => return None,
113 };
114
115 acc.add(assist_id, assist_label, target, |builder| {
116 builder.replace(parent.syntax().text_range(), update_expr_string(block.to_string()));
117 })
118 }
119}
120
121fn update_expr_string(expr_string: String) -> String {
122 update_expr_string_with_pat(expr_string, &[' ', '\n'])
123}
124
125fn update_expr_string_without_newline(expr_string: String) -> String {
126 update_expr_string_with_pat(expr_string, &[' '])
127}
128
129fn update_expr_string_with_pat(expr_str: String, whitespace_pat: &[char]) -> String {
130 let after_open_brace_index = expr_str.find('{').map_or(0, |it| it + 1);
135 let expr_str = expr_str[after_open_brace_index..].trim_start_matches(whitespace_pat);
136
137 let expr_str = expr_str.trim_end_matches(whitespace_pat);
140 let expr_str = expr_str[..expr_str.len() - 1].trim_end_matches(whitespace_pat);
141
142 expr_str
143 .lines()
144 .map(|line| line.replacen(" ", "", 1)) .collect::<Vec<String>>()
146 .join("\n")
147}
148
149#[cfg(test)]
150mod tests {
151 use crate::tests::{check_assist, check_assist_not_applicable};
152
153 use super::*;
154
155 #[test]
156 fn unwrap_tail_expr_block() {
157 check_assist(
158 unwrap_block,
159 r#"
160fn main() {
161 $0{
162 92
163 }
164}
165"#,
166 r#"
167fn main() {
168 92
169}
170"#,
171 )
172 }
173
174 #[test]
175 fn unwrap_stmt_expr_block() {
176 check_assist(
177 unwrap_block,
178 r#"
179fn main() {
180 $0{
181 92;
182 }
183 ()
184}
185"#,
186 r#"
187fn main() {
188 92;
189 ()
190}
191"#,
192 );
193 check_assist(
195 unwrap_block,
196 r#"
197fn main() {
198 $0{
199 92
200 }
201 ()
202}
203"#,
204 r#"
205fn main() {
206 92
207 ()
208}
209"#,
210 );
211 }
212
213 #[test]
214 fn simple_if() {
215 check_assist(
216 unwrap_block,
217 r#"
218fn main() {
219 bar();
220 if true {$0
221 foo();
222
223 // comment
224 bar();
225 } else {
226 println!("bar");
227 }
228}
229"#,
230 r#"
231fn main() {
232 bar();
233 foo();
234
235 // comment
236 bar();
237}
238"#,
239 );
240 }
241
242 #[test]
243 fn simple_if_else() {
244 check_assist(
245 unwrap_block,
246 r#"
247fn main() {
248 bar();
249 if true {
250 foo();
251
252 // comment
253 bar();
254 } else {$0
255 println!("bar");
256 }
257}
258"#,
259 r#"
260fn main() {
261 bar();
262 if true {
263 foo();
264
265 // comment
266 bar();
267 }
268 println!("bar");
269}
270"#,
271 );
272 }
273
274 #[test]
275 fn simple_if_else_if() {
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 {$0
287 println!("bar");
288 } else {
289 println!("foo");
290 }
291}
292"#,
293 r#"
294fn main() {
295 // bar();
296 if true {
297 println!("true");
298
299 // comment
300 // bar();
301 }
302 println!("bar");
303}
304"#,
305 );
306 }
307
308 #[test]
309 fn simple_if_else_if_nested() {
310 check_assist(
311 unwrap_block,
312 r#"
313fn main() {
314 // bar();
315 if true {
316 println!("true");
317
318 // comment
319 // bar();
320 } else if false {
321 println!("bar");
322 } else if true {$0
323 println!("foo");
324 }
325}
326"#,
327 r#"
328fn main() {
329 // bar();
330 if true {
331 println!("true");
332
333 // comment
334 // bar();
335 } else if false {
336 println!("bar");
337 }
338 println!("foo");
339}
340"#,
341 );
342 }
343
344 #[test]
345 fn simple_if_else_if_nested_else() {
346 check_assist(
347 unwrap_block,
348 r#"
349fn main() {
350 // bar();
351 if true {
352 println!("true");
353
354 // comment
355 // bar();
356 } else if false {
357 println!("bar");
358 } else if true {
359 println!("foo");
360 } else {$0
361 println!("else");
362 }
363}
364"#,
365 r#"
366fn main() {
367 // bar();
368 if true {
369 println!("true");
370
371 // comment
372 // bar();
373 } else if false {
374 println!("bar");
375 } else if true {
376 println!("foo");
377 }
378 println!("else");
379}
380"#,
381 );
382 }
383
384 #[test]
385 fn simple_if_else_if_nested_middle() {
386 check_assist(
387 unwrap_block,
388 r#"
389fn main() {
390 // bar();
391 if true {
392 println!("true");
393
394 // comment
395 // bar();
396 } else if false {
397 println!("bar");
398 } else if true {$0
399 println!("foo");
400 } else {
401 println!("else");
402 }
403}
404"#,
405 r#"
406fn main() {
407 // bar();
408 if true {
409 println!("true");
410
411 // comment
412 // bar();
413 } else if false {
414 println!("bar");
415 }
416 println!("foo");
417}
418"#,
419 );
420 }
421
422 #[test]
423 fn simple_if_bad_cursor_position() {
424 check_assist_not_applicable(
425 unwrap_block,
426 r#"
427fn main() {
428 bar();$0
429 if true {
430 foo();
431
432 // comment
433 bar();
434 } else {
435 println!("bar");
436 }
437}
438"#,
439 );
440 }
441
442 #[test]
443 fn simple_for() {
444 check_assist(
445 unwrap_block,
446 r#"
447fn main() {
448 for i in 0..5 {$0
449 if true {
450 foo();
451
452 // comment
453 bar();
454 } else {
455 println!("bar");
456 }
457 }
458}
459"#,
460 r#"
461fn main() {
462 if true {
463 foo();
464
465 // comment
466 bar();
467 } else {
468 println!("bar");
469 }
470}
471"#,
472 );
473 }
474
475 #[test]
476 fn simple_if_in_for() {
477 check_assist(
478 unwrap_block,
479 r#"
480fn main() {
481 for i in 0..5 {
482 if true {$0
483 foo();
484
485 // comment
486 bar();
487 } else {
488 println!("bar");
489 }
490 }
491}
492"#,
493 r#"
494fn main() {
495 for i in 0..5 {
496 foo();
497
498 // comment
499 bar();
500 }
501}
502"#,
503 );
504 }
505
506 #[test]
507 fn simple_loop() {
508 check_assist(
509 unwrap_block,
510 r#"
511fn main() {
512 loop {$0
513 if true {
514 foo();
515
516 // comment
517 bar();
518 } else {
519 println!("bar");
520 }
521 }
522}
523"#,
524 r#"
525fn main() {
526 if true {
527 foo();
528
529 // comment
530 bar();
531 } else {
532 println!("bar");
533 }
534}
535"#,
536 );
537 }
538
539 #[test]
540 fn simple_while() {
541 check_assist(
542 unwrap_block,
543 r#"
544fn main() {
545 while true {$0
546 if true {
547 foo();
548
549 // comment
550 bar();
551 } else {
552 println!("bar");
553 }
554 }
555}
556"#,
557 r#"
558fn main() {
559 if true {
560 foo();
561
562 // comment
563 bar();
564 } else {
565 println!("bar");
566 }
567}
568"#,
569 );
570 }
571
572 #[test]
573 fn unwrap_match_arm() {
574 check_assist(
575 unwrap_block,
576 r#"
577fn main() {
578 match rel_path {
579 Ok(rel_path) => {$0
580 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
581 Some((*id, rel_path))
582 }
583 Err(_) => None,
584 }
585}
586"#,
587 r#"
588fn main() {
589 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
590 Some((*id, rel_path))
591}
592"#,
593 );
594 }
595
596 #[test]
597 fn simple_if_in_while_bad_cursor_position() {
598 check_assist_not_applicable(
599 unwrap_block,
600 r#"
601fn main() {
602 while true {
603 if true {
604 foo();$0
605
606 // comment
607 bar();
608 } else {
609 println!("bar");
610 }
611 }
612}
613"#,
614 );
615 }
616
617 #[test]
618 fn simple_single_line() {
619 check_assist(
620 unwrap_block,
621 r#"
622fn main() {
623 {$0 0 }
624}
625"#,
626 r#"
627fn main() {
628 0
629}
630"#,
631 );
632 }
633
634 #[test]
635 fn simple_nested_block() {
636 check_assist(
637 unwrap_block,
638 r#"
639fn main() {
640 $0{
641 {
642 3
643 }
644 }
645}
646"#,
647 r#"
648fn main() {
649 {
650 3
651 }
652}
653"#,
654 );
655 }
656
657 #[test]
658 fn nested_single_line() {
659 check_assist(
660 unwrap_block,
661 r#"
662fn main() {
663 {$0 { println!("foo"); } }
664}
665"#,
666 r#"
667fn main() {
668 { println!("foo"); }
669}
670"#,
671 );
672
673 check_assist(
674 unwrap_block,
675 r#"
676fn main() {
677 {$0 { 0 } }
678}
679"#,
680 r#"
681fn main() {
682 { 0 }
683}
684"#,
685 );
686 }
687
688 #[test]
689 fn simple_if_single_line() {
690 check_assist(
691 unwrap_block,
692 r#"
693fn main() {
694 if true {$0 /* foo */ foo() } else { bar() /* bar */}
695}
696"#,
697 r#"
698fn main() {
699 /* foo */ foo()
700}
701"#,
702 );
703 }
704
705 #[test]
706 fn if_single_statement() {
707 check_assist(
708 unwrap_block,
709 r#"
710fn main() {
711 if true {$0
712 return 3;
713 }
714}
715"#,
716 r#"
717fn main() {
718 return 3;
719}
720"#,
721 );
722 }
723
724 #[test]
725 fn multiple_statements() {
726 check_assist(
727 unwrap_block,
728 r#"
729fn main() -> i32 {
730 if 2 > 1 {$0
731 let a = 5;
732 return 3;
733 }
734 5
735}
736"#,
737 r#"
738fn main() -> i32 {
739 let a = 5;
740 return 3;
741 5
742}
743"#,
744 );
745 }
746
747 #[test]
748 fn unwrap_block_in_let_initializers() {
749 check_assist(
751 unwrap_block,
752 r#"
753fn main() {
754 let x = {$0};
755}
756"#,
757 r#"
758fn main() {
759 let x = ();
760}
761"#,
762 );
763 check_assist(
764 unwrap_block,
765 r#"
766fn main() {
767 let x = {$0
768 bar
769 };
770}
771"#,
772 r#"
773fn main() {
774 let x = bar;
775}
776"#,
777 );
778 check_assist(
779 unwrap_block,
780 r#"
781fn main() -> i32 {
782 let _ = {$01; 2};
783}
784"#,
785 r#"
786fn main() -> i32 {
787 1;
788 let _ = 2;
789}
790"#,
791 );
792 check_assist(
793 unwrap_block,
794 r#"
795fn main() -> i32 {
796 let mut a = {$01; 2};
797}
798"#,
799 r#"
800fn main() -> i32 {
801 1;
802 let mut a = 2;
803}
804"#,
805 );
806 }
807
808 #[test]
809 fn unwrap_if_in_let_initializers() {
810 check_assist(
812 unwrap_block,
813 r#"
814fn main() {
815 let a = 1;
816 let x = if a - 1 == 0 {$0
817 foo
818 } else {
819 bar
820 };
821}
822"#,
823 r#"
824fn main() {
825 let a = 1;
826 let x = foo;
827}
828"#,
829 );
830 }
831
832 #[test]
833 fn unwrap_block_with_modifiers() {
834 check_assist(
836 unwrap_block,
837 r#"
838fn main() {
839 unsafe $0{
840 bar;
841 }
842}
843"#,
844 r#"
845fn main() {
846 bar;
847}
848"#,
849 );
850 check_assist(
851 unwrap_block,
852 r#"
853fn main() {
854 async move $0{
855 bar;
856 }
857}
858"#,
859 r#"
860fn main() {
861 bar;
862}
863"#,
864 );
865 check_assist(
866 unwrap_block,
867 r#"
868fn main() {
869 try $0{
870 bar;
871 }
872}
873"#,
874 r#"
875fn main() {
876 bar;
877}
878"#,
879 );
880 }
881}