1 use crate::{Assist, AssistCtx, AssistId};
3 use ast::LoopBodyOwner;
4 use ra_fmt::unwrap_trivial_block;
5 use ra_syntax::{ast, match_ast, AstNode, TextRange, T};
7 // Assist: unwrap_block
9 // This assist removes if...else, for, while and loop control statements to just keep the body.
24 pub(crate) fn unwrap_block(ctx: AssistCtx) -> Option<Assist> {
25 let l_curly_token = ctx.find_token_at_offset(T!['{'])?;
26 let block = ast::BlockExpr::cast(l_curly_token.parent())?;
27 let parent = block.syntax().parent()?;
28 let (expr, expr_to_unwrap) = match_ast! {
30 ast::IfExpr(if_expr) => {
31 let expr_to_unwrap = if_expr.blocks().find_map(|expr| extract_expr(ctx.frange.range, expr));
32 let expr_to_unwrap = expr_to_unwrap?;
33 // Find if we are in a else if block
34 let ancestor = if_expr.syntax().parent().and_then(ast::IfExpr::cast);
37 None => (ast::Expr::IfExpr(if_expr), expr_to_unwrap),
38 Some(ancestor) => (ast::Expr::IfExpr(ancestor), expr_to_unwrap),
41 ast::ForExpr(for_expr) => {
42 let block_expr = for_expr.loop_body()?;
43 let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?;
44 (ast::Expr::ForExpr(for_expr), expr_to_unwrap)
46 ast::WhileExpr(while_expr) => {
47 let block_expr = while_expr.loop_body()?;
48 let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?;
49 (ast::Expr::WhileExpr(while_expr), expr_to_unwrap)
51 ast::LoopExpr(loop_expr) => {
52 let block_expr = loop_expr.loop_body()?;
53 let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?;
54 (ast::Expr::LoopExpr(loop_expr), expr_to_unwrap)
60 ctx.add_assist(AssistId("unwrap_block"), "Unwrap block", |edit| {
61 edit.set_cursor(expr.syntax().text_range().start());
62 edit.target(expr_to_unwrap.syntax().text_range());
64 let pat_start: &[_] = &[' ', '{', '\n'];
65 let expr_to_unwrap = expr_to_unwrap.to_string();
66 let expr_string = expr_to_unwrap.trim_start_matches(pat_start);
67 let mut expr_string_lines: Vec<&str> = expr_string.lines().collect();
68 expr_string_lines.pop(); // Delete last line
70 let expr_string = expr_string_lines
72 .map(|line| line.replacen(" ", "", 1)) // Delete indentation
73 .collect::<Vec<String>>()
76 edit.replace(expr.syntax().text_range(), expr_string);
80 fn extract_expr(cursor_range: TextRange, block: ast::BlockExpr) -> Option<ast::Expr> {
81 let cursor_in_range = block.l_curly_token()?.text_range().contains_range(cursor_range);
84 Some(unwrap_trivial_block(block))
92 use crate::tests::{check_assist, check_assist_not_applicable};
126 fn simple_if_else() {
152 fn simple_if_else_if() {
180 fn simple_if_bad_cursor_position() {
181 check_assist_not_applicable(
233 fn simple_if_in_for() {
330 fn simple_if_in_while_bad_cursor_position() {
331 check_assist_not_applicable(