Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #22179 from A4-Tacks/pure-unwrap-block
feat: Add unwrap_block, offer unwrap_block and unwrap_branch
| -rw-r--r-- | crates/ide-assists/src/handlers/unwrap_branch.rs | 165 | ||||
| -rw-r--r-- | crates/ide-assists/src/lib.rs | 1 | ||||
| -rw-r--r-- | crates/ide-assists/src/tests/generated.rs | 23 |
3 files changed, 188 insertions, 1 deletions
diff --git a/crates/ide-assists/src/handlers/unwrap_branch.rs b/crates/ide-assists/src/handlers/unwrap_branch.rs index 0f7eb262a8..0c94b165ee 100644 --- a/crates/ide-assists/src/handlers/unwrap_branch.rs +++ b/crates/ide-assists/src/handlers/unwrap_branch.rs @@ -1,3 +1,4 @@ +use either::Either; use syntax::{ AstNode, SyntaxElement, SyntaxKind, SyntaxNode, T, ast::{ @@ -86,6 +87,55 @@ pub(crate) fn unwrap_branch(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio }) } +// Assist: unwrap_block +// +// This assist removes braces and unwrap single expressions block. +// +// ``` +// fn foo() { +// match () { +// _ => {$0 +// bar() +// } +// } +// } +// ``` +// -> +// ``` +// fn foo() { +// match () { +// _ => bar(), +// } +// } +// ``` +pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + let l_curly_token = ctx.find_token_syntax_at_offset(T!['{'])?; + let block = l_curly_token.parent_ancestors().nth(1).and_then(ast::BlockExpr::cast)?; + let target = block.syntax().text_range(); + let tail_expr = block.tail_expr()?; + let stmt_list = block.stmt_list()?; + let container = Either::<ast::MatchArm, ast::ClosureExpr>::cast(block.syntax().parent()?)?; + + if stmt_list.statements().next().is_some() { + return None; + } + + acc.add(AssistId::refactor_rewrite("unwrap_block"), "Unwrap block", target, |builder| { + let editor = builder.make_editor(block.syntax()); + let replacement = stmt_list.dedent(tail_expr.indent_level()).indent(block.indent_level()); + let mut replacement = extract_statements(replacement); + + if container.left().is_some_and(|it| it.comma_token().is_none()) + && !tail_expr.is_block_like() + { + replacement.push(editor.make().token(T![,]).into()); + } + + editor.replace_with_many(block.syntax(), replacement); + builder.add_file_edits(ctx.vfs_file_id(), editor); + }) +} + fn delete_else_before(container: SyntaxNode, editor: &SyntaxEditor) { let make = editor.make(); let Some(else_token) = container @@ -158,7 +208,10 @@ fn wrap_block_raw(expr: &ast::Expr, make: &SyntaxFactory) -> ast::BlockExpr { #[cfg(test)] mod tests { - use crate::tests::{check_assist, check_assist_not_applicable, check_assist_with_label}; + use crate::tests::{ + check_assist, check_assist_by_label, check_assist_not_applicable, + check_assist_not_applicable_by_label, check_assist_with_label, + }; use super::*; @@ -1037,4 +1090,114 @@ fn main() { "Unwrap branch", ); } + + #[test] + fn unwrap_block_in_branch() { + check_assist_by_label( + unwrap_block, + r#" +fn main() { + match rel_path { + Ok(rel_path) => {$0 + if true { + foo() + } + } + Err(_) => None, + } +} +"#, + r#" +fn main() { + match rel_path { + Ok(rel_path) => if true { + foo() + } + Err(_) => None, + } +} +"#, + "Unwrap block", + ); + + check_assist_by_label( + unwrap_block, + r#" +fn main() { + match rel_path { + Ok(rel_path) => {$0 + 1 + 2 + } + Err(_) => None, + } +} +"#, + r#" +fn main() { + match rel_path { + Ok(rel_path) => 1 + 2, + Err(_) => None, + } +} +"#, + "Unwrap block", + ); + } + + #[test] + fn unwrap_block_in_branch_non_standalone() { + check_assist_not_applicable_by_label( + unwrap_block, + r#" +fn main() { + match rel_path { + Ok(rel_path) => { + if true {$0 + foo() + } + } + Err(_) => None, + } +} +"#, + "Unwrap block", + ); + } + + #[test] + fn unwrap_block_in_branch_non_tail_expr_only() { + check_assist_not_applicable_by_label( + unwrap_block, + r#" +fn main() { + match rel_path { + Ok(rel_path) => {$0 + x; + y + } + Err(_) => None, + } +} +"#, + "Unwrap block", + ); + } + + #[test] + fn unwrap_block_in_closure() { + check_assist_by_label( + unwrap_block, + r#" +fn main() { + let f = || {$0 foo() }; +} +"#, + r#" +fn main() { + let f = || foo(); +} +"#, + "Unwrap block", + ); + } } diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs index 1fe385e95b..18ff63470d 100644 --- a/crates/ide-assists/src/lib.rs +++ b/crates/ide-assists/src/lib.rs @@ -380,6 +380,7 @@ mod handlers { unmerge_imports::unmerge_imports, unnecessary_async::unnecessary_async, unqualify_method_call::unqualify_method_call, + unwrap_branch::unwrap_block, unwrap_branch::unwrap_branch, unwrap_return_type::unwrap_return_type, unwrap_tuple::unwrap_tuple, diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs index 7e66f02475..d3ee35aa86 100644 --- a/crates/ide-assists/src/tests/generated.rs +++ b/crates/ide-assists/src/tests/generated.rs @@ -3731,6 +3731,29 @@ mod std { pub mod ops { pub trait Add { fn add(self, _: Self) {} } impl Add for } #[test] +fn doctest_unwrap_block() { + check_doc_test( + "unwrap_block", + r#####" +fn foo() { + match () { + _ => {$0 + bar() + } + } +} +"#####, + r#####" +fn foo() { + match () { + _ => bar(), + } +} +"#####, + ) +} + +#[test] fn doctest_unwrap_branch() { check_doc_test( "unwrap_branch", |