2020-04-29 06:52:55 -05:00
|
|
|
use crate::{Assist, AssistCtx, AssistId};
|
|
|
|
|
2020-05-04 07:55:24 -05:00
|
|
|
use ast::LoopBodyOwner;
|
2020-04-29 06:52:55 -05:00
|
|
|
use ra_fmt::unwrap_trivial_block;
|
2020-05-04 07:55:24 -05:00
|
|
|
use ra_syntax::{ast, match_ast, AstNode, TextRange, T};
|
2020-04-29 06:52:55 -05:00
|
|
|
|
|
|
|
// Assist: unwrap_block
|
|
|
|
//
|
2020-04-29 07:53:47 -05:00
|
|
|
// This assist removes if...else, for, while and loop control statements to just keep the body.
|
2020-04-29 06:52:55 -05:00
|
|
|
//
|
|
|
|
// ```
|
|
|
|
// fn foo() {
|
|
|
|
// if true {<|>
|
|
|
|
// println!("foo");
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// ```
|
|
|
|
// ->
|
|
|
|
// ```
|
|
|
|
// fn foo() {
|
2020-04-29 07:53:47 -05:00
|
|
|
// println!("foo");
|
2020-04-29 06:52:55 -05:00
|
|
|
// }
|
|
|
|
// ```
|
|
|
|
pub(crate) fn unwrap_block(ctx: AssistCtx) -> Option<Assist> {
|
2020-05-02 05:20:39 -05:00
|
|
|
let l_curly_token = ctx.find_token_at_offset(T!['{'])?;
|
2020-05-04 07:55:24 -05:00
|
|
|
let block = ast::BlockExpr::cast(l_curly_token.parent())?;
|
|
|
|
let parent = block.syntax().parent()?;
|
|
|
|
let (expr, expr_to_unwrap) = match_ast! {
|
|
|
|
match parent {
|
|
|
|
ast::IfExpr(if_expr) => {
|
|
|
|
let expr_to_unwrap = if_expr.blocks().find_map(|expr| extract_expr(ctx.frange.range, expr));
|
|
|
|
let expr_to_unwrap = expr_to_unwrap?;
|
|
|
|
// Find if we are in a else if block
|
|
|
|
let ancestor = if_expr.syntax().parent().and_then(ast::IfExpr::cast);
|
2020-05-02 05:20:39 -05:00
|
|
|
|
2020-05-04 07:55:24 -05:00
|
|
|
match ancestor {
|
|
|
|
None => (ast::Expr::IfExpr(if_expr), expr_to_unwrap),
|
|
|
|
Some(ancestor) => (ast::Expr::IfExpr(ancestor), expr_to_unwrap),
|
|
|
|
}
|
|
|
|
},
|
|
|
|
ast::ForExpr(for_expr) => {
|
|
|
|
let block_expr = for_expr.loop_body()?;
|
|
|
|
let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?;
|
|
|
|
(ast::Expr::ForExpr(for_expr), expr_to_unwrap)
|
|
|
|
},
|
|
|
|
ast::WhileExpr(while_expr) => {
|
|
|
|
let block_expr = while_expr.loop_body()?;
|
|
|
|
let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?;
|
|
|
|
(ast::Expr::WhileExpr(while_expr), expr_to_unwrap)
|
|
|
|
},
|
|
|
|
ast::LoopExpr(loop_expr) => {
|
|
|
|
let block_expr = loop_expr.loop_body()?;
|
|
|
|
let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?;
|
|
|
|
(ast::Expr::LoopExpr(loop_expr), expr_to_unwrap)
|
|
|
|
},
|
|
|
|
_ => return None,
|
2020-04-29 06:52:55 -05:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
ctx.add_assist(AssistId("unwrap_block"), "Unwrap block", |edit| {
|
|
|
|
edit.set_cursor(expr.syntax().text_range().start());
|
|
|
|
edit.target(expr_to_unwrap.syntax().text_range());
|
|
|
|
|
|
|
|
let pat_start: &[_] = &[' ', '{', '\n'];
|
|
|
|
let expr_to_unwrap = expr_to_unwrap.to_string();
|
|
|
|
let expr_string = expr_to_unwrap.trim_start_matches(pat_start);
|
|
|
|
let mut expr_string_lines: Vec<&str> = expr_string.lines().collect();
|
|
|
|
expr_string_lines.pop(); // Delete last line
|
|
|
|
|
|
|
|
let expr_string = expr_string_lines
|
|
|
|
.into_iter()
|
|
|
|
.map(|line| line.replacen(" ", "", 1)) // Delete indentation
|
|
|
|
.collect::<Vec<String>>()
|
|
|
|
.join("\n");
|
|
|
|
|
|
|
|
edit.replace(expr.syntax().text_range(), expr_string);
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-05-04 07:55:24 -05:00
|
|
|
fn extract_expr(cursor_range: TextRange, block: ast::BlockExpr) -> Option<ast::Expr> {
|
2020-05-01 09:41:29 -05:00
|
|
|
let cursor_in_range = block.l_curly_token()?.text_range().contains_range(cursor_range);
|
|
|
|
|
|
|
|
if cursor_in_range {
|
2020-05-02 05:31:11 -05:00
|
|
|
Some(unwrap_trivial_block(block))
|
2020-05-01 09:41:29 -05:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-29 06:52:55 -05:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2020-05-06 03:16:55 -05:00
|
|
|
use crate::tests::{check_assist, check_assist_not_applicable};
|
2020-04-29 06:52:55 -05:00
|
|
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn simple_if() {
|
|
|
|
check_assist(
|
|
|
|
unwrap_block,
|
|
|
|
r#"
|
|
|
|
fn main() {
|
|
|
|
bar();
|
|
|
|
if true {<|>
|
|
|
|
foo();
|
|
|
|
|
|
|
|
//comment
|
|
|
|
bar();
|
|
|
|
} else {
|
|
|
|
println!("bar");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
r#"
|
|
|
|
fn main() {
|
|
|
|
bar();
|
|
|
|
<|>foo();
|
|
|
|
|
|
|
|
//comment
|
|
|
|
bar();
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn simple_if_else() {
|
|
|
|
check_assist(
|
|
|
|
unwrap_block,
|
|
|
|
r#"
|
|
|
|
fn main() {
|
|
|
|
bar();
|
|
|
|
if true {
|
|
|
|
foo();
|
|
|
|
|
|
|
|
//comment
|
|
|
|
bar();
|
|
|
|
} else {<|>
|
|
|
|
println!("bar");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
r#"
|
|
|
|
fn main() {
|
|
|
|
bar();
|
|
|
|
<|>println!("bar");
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn simple_if_else_if() {
|
|
|
|
check_assist(
|
|
|
|
unwrap_block,
|
|
|
|
r#"
|
|
|
|
fn main() {
|
|
|
|
//bar();
|
|
|
|
if true {
|
|
|
|
println!("true");
|
|
|
|
|
|
|
|
//comment
|
|
|
|
//bar();
|
|
|
|
} else if false {<|>
|
|
|
|
println!("bar");
|
|
|
|
} else {
|
|
|
|
println!("foo");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
r#"
|
|
|
|
fn main() {
|
|
|
|
//bar();
|
|
|
|
<|>println!("bar");
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn simple_if_bad_cursor_position() {
|
|
|
|
check_assist_not_applicable(
|
|
|
|
unwrap_block,
|
|
|
|
r#"
|
|
|
|
fn main() {
|
|
|
|
bar();<|>
|
|
|
|
if true {
|
|
|
|
foo();
|
|
|
|
|
|
|
|
//comment
|
|
|
|
bar();
|
|
|
|
} else {
|
|
|
|
println!("bar");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn simple_for() {
|
|
|
|
check_assist(
|
|
|
|
unwrap_block,
|
|
|
|
r#"
|
|
|
|
fn main() {
|
|
|
|
for i in 0..5 {<|>
|
|
|
|
if true {
|
|
|
|
foo();
|
|
|
|
|
|
|
|
//comment
|
|
|
|
bar();
|
|
|
|
} else {
|
|
|
|
println!("bar");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
r#"
|
|
|
|
fn main() {
|
|
|
|
<|>if true {
|
|
|
|
foo();
|
|
|
|
|
|
|
|
//comment
|
|
|
|
bar();
|
|
|
|
} else {
|
|
|
|
println!("bar");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn simple_if_in_for() {
|
|
|
|
check_assist(
|
|
|
|
unwrap_block,
|
|
|
|
r#"
|
|
|
|
fn main() {
|
|
|
|
for i in 0..5 {
|
|
|
|
if true {<|>
|
|
|
|
foo();
|
|
|
|
|
|
|
|
//comment
|
|
|
|
bar();
|
|
|
|
} else {
|
|
|
|
println!("bar");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
r#"
|
|
|
|
fn main() {
|
|
|
|
for i in 0..5 {
|
|
|
|
<|>foo();
|
|
|
|
|
|
|
|
//comment
|
|
|
|
bar();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn simple_loop() {
|
|
|
|
check_assist(
|
|
|
|
unwrap_block,
|
|
|
|
r#"
|
|
|
|
fn main() {
|
|
|
|
loop {<|>
|
|
|
|
if true {
|
|
|
|
foo();
|
|
|
|
|
|
|
|
//comment
|
|
|
|
bar();
|
|
|
|
} else {
|
|
|
|
println!("bar");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
r#"
|
|
|
|
fn main() {
|
|
|
|
<|>if true {
|
|
|
|
foo();
|
|
|
|
|
|
|
|
//comment
|
|
|
|
bar();
|
|
|
|
} else {
|
|
|
|
println!("bar");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn simple_while() {
|
|
|
|
check_assist(
|
|
|
|
unwrap_block,
|
|
|
|
r#"
|
|
|
|
fn main() {
|
|
|
|
while true {<|>
|
|
|
|
if true {
|
|
|
|
foo();
|
|
|
|
|
|
|
|
//comment
|
|
|
|
bar();
|
|
|
|
} else {
|
|
|
|
println!("bar");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
r#"
|
|
|
|
fn main() {
|
|
|
|
<|>if true {
|
|
|
|
foo();
|
|
|
|
|
|
|
|
//comment
|
|
|
|
bar();
|
|
|
|
} else {
|
|
|
|
println!("bar");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn simple_if_in_while_bad_cursor_position() {
|
|
|
|
check_assist_not_applicable(
|
|
|
|
unwrap_block,
|
|
|
|
r#"
|
|
|
|
fn main() {
|
|
|
|
while true {
|
|
|
|
if true {
|
|
|
|
foo();<|>
|
|
|
|
|
|
|
|
//comment
|
|
|
|
bar();
|
|
|
|
} else {
|
|
|
|
println!("bar");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|