4 edit::{AstNodeEdit, IndentLevel},
6 AstNode, SyntaxKind, TextRange, T,
9 use crate::{AssistContext, AssistId, AssistKind, Assists};
11 // Assist: unwrap_block
13 // This assist removes if...else, for, while and loop control statements to just keep the body.
28 pub(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";
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()))?
40 if matches!(parent.kind(), SyntaxKind::STMT_LIST | SyntaxKind::EXPR_STMT) {
41 return acc.add(assist_id, assist_label, target, |builder| {
42 builder.replace(block.syntax().text_range(), update_expr_string(block.to_string()));
46 let parent = ast::Expr::cast(parent)?;
48 match parent.clone() {
49 ast::Expr::ForExpr(_) | ast::Expr::WhileExpr(_) | ast::Expr::LoopExpr(_) => (),
50 ast::Expr::MatchExpr(_) => block = block.dedent(IndentLevel(1)),
51 ast::Expr::IfExpr(if_expr) => {
52 let then_branch = if_expr.then_branch()?;
53 if then_branch == block {
54 if let Some(ancestor) = if_expr.syntax().parent().and_then(ast::IfExpr::cast) {
55 // For `else if` blocks
56 let ancestor_then_branch = ancestor.then_branch()?;
58 return acc.add(assist_id, assist_label, target, |edit| {
59 let range_to_del_else_if = TextRange::new(
60 ancestor_then_branch.syntax().text_range().end(),
61 l_curly_token.text_range().start(),
63 let range_to_del_rest = TextRange::new(
64 then_branch.syntax().text_range().end(),
65 if_expr.syntax().text_range().end(),
68 edit.delete(range_to_del_rest);
69 edit.delete(range_to_del_else_if);
72 update_expr_string_without_newline(then_branch.to_string()),
77 return acc.add(assist_id, assist_label, target, |edit| {
78 let range_to_del = TextRange::new(
79 then_branch.syntax().text_range().end(),
80 l_curly_token.text_range().start(),
83 edit.delete(range_to_del);
84 edit.replace(target, update_expr_string_without_newline(block.to_string()));
91 acc.add(assist_id, assist_label, target, |builder| {
92 builder.replace(parent.syntax().text_range(), update_expr_string(block.to_string()));
96 fn update_expr_string(expr_string: String) -> String {
97 update_expr_string_with_pat(expr_string, &[' ', '\n'])
100 fn update_expr_string_without_newline(expr_string: String) -> String {
101 update_expr_string_with_pat(expr_string, &[' '])
104 fn update_expr_string_with_pat(expr_str: String, whitespace_pat: &[char]) -> String {
105 // Remove leading whitespace, index [1..] to remove the leading '{',
106 // then continue to remove leading whitespace.
108 expr_str.trim_start_matches(whitespace_pat)[1..].trim_start_matches(whitespace_pat);
110 // Remove trailing whitespace, index [..expr_str.len() - 1] to remove the trailing '}',
111 // then continue to remove trailing whitespace.
112 let expr_str = expr_str.trim_end_matches(whitespace_pat);
113 let expr_str = expr_str[..expr_str.len() - 1].trim_end_matches(whitespace_pat);
117 .map(|line| line.replacen(" ", "", 1)) // Delete indentation
118 .collect::<Vec<String>>()
124 use crate::tests::{check_assist, check_assist_not_applicable};
129 fn unwrap_tail_expr_block() {
148 fn unwrap_stmt_expr_block() {
166 // Pedantically, we should add an `;` here...
216 fn simple_if_else() {
248 fn simple_if_else_if() {
282 fn simple_if_else_if_nested() {
318 fn simple_if_else_if_nested_else() {
358 fn simple_if_else_if_nested_middle() {
396 fn simple_if_bad_cursor_position() {
397 check_assist_not_applicable(
449 fn simple_if_in_for() {
546 fn unwrap_match_arm() {
553 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
554 Some((*id, rel_path))
562 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
563 Some((*id, rel_path))
570 fn simple_if_in_while_bad_cursor_position() {
571 check_assist_not_applicable(
591 fn simple_single_line() {
608 fn simple_nested_block() {
631 fn nested_single_line() {
636 {$0 { println!("foo"); } }
662 fn simple_if_single_line() {
667 if true {$0 /* foo */ foo() } else { bar() /* bar */}
679 fn if_single_statement() {
698 fn multiple_statements() {