4 edit::{AstNodeEdit, IndentLevel},
9 use crate::{utils::unwrap_trivial_block, 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())?;
34 let mut parent = block.syntax().parent()?;
35 if ast::MatchArm::can_cast(parent.kind()) {
36 parent = parent.ancestors().find(|it| ast::MatchExpr::can_cast(it.kind()))?
39 let parent = ast::Expr::cast(parent)?;
41 match parent.clone() {
42 ast::Expr::ForExpr(_) | ast::Expr::WhileExpr(_) | ast::Expr::LoopExpr(_) => (),
43 ast::Expr::MatchExpr(_) => block = block.dedent(IndentLevel(1)),
44 ast::Expr::IfExpr(if_expr) => {
45 let then_branch = if_expr.then_branch()?;
46 if then_branch == block {
47 if let Some(ancestor) = if_expr.syntax().parent().and_then(ast::IfExpr::cast) {
48 // For `else if` blocks
49 let ancestor_then_branch = ancestor.then_branch()?;
51 let target = then_branch.syntax().text_range();
52 return acc.add(assist_id, assist_label, target, |edit| {
53 let range_to_del_else_if = TextRange::new(
54 ancestor_then_branch.syntax().text_range().end(),
55 l_curly_token.text_range().start(),
57 let range_to_del_rest = TextRange::new(
58 then_branch.syntax().text_range().end(),
59 if_expr.syntax().text_range().end(),
62 edit.delete(range_to_del_rest);
63 edit.delete(range_to_del_else_if);
66 update_expr_string(then_branch.to_string(), &[' ', '{']),
71 let target = block.syntax().text_range();
72 return acc.add(assist_id, assist_label, target, |edit| {
73 let range_to_del = TextRange::new(
74 then_branch.syntax().text_range().end(),
75 l_curly_token.text_range().start(),
78 edit.delete(range_to_del);
79 edit.replace(target, update_expr_string(block.to_string(), &[' ', '{']));
86 let unwrapped = unwrap_trivial_block(block);
87 let target = unwrapped.syntax().text_range();
88 acc.add(assist_id, assist_label, target, |builder| {
90 parent.syntax().text_range(),
91 update_expr_string(unwrapped.to_string(), &[' ', '{', '\n']),
96 fn update_expr_string(expr_str: String, trim_start_pat: &[char]) -> String {
97 let expr_string = expr_str.trim_start_matches(trim_start_pat);
98 let mut expr_string_lines: Vec<&str> = expr_string.lines().collect();
99 expr_string_lines.pop(); // Delete last line
103 .map(|line| line.replacen(" ", "", 1)) // Delete indentation
104 .collect::<Vec<String>>()
110 use crate::tests::{check_assist, check_assist_not_applicable};
144 fn simple_if_else() {
176 fn simple_if_else_if() {
210 fn simple_if_else_if_nested() {
246 fn simple_if_else_if_nested_else() {
286 fn simple_if_else_if_nested_middle() {
324 fn simple_if_bad_cursor_position() {
325 check_assist_not_applicable(
377 fn simple_if_in_for() {
474 fn unwrap_match_arm() {
481 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
482 Some((*id, rel_path))
490 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
491 Some((*id, rel_path))
498 fn simple_if_in_while_bad_cursor_position() {
499 check_assist_not_applicable(