From c03ce23ea62a0e6e8295fdd82d58b20304ff1c6e Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 1 Nov 2025 16:22:37 +0800 Subject: [PATCH] Add ide-assist: unmerge_else_if Unmerge `else if` into the `else {}`. Example --- ```rust fn foo() { if cond1 { xxx() } else if$0 cond2 { yyy() } else { zzz() } } ``` -> ```rust fn foo() { if cond1 { xxx() } else { if cond2 { yyy() } else { zzz() } } } ``` --- .../src/handlers/unmerge_else_if.rs | 135 ++++++++++++++++++ crates/ide-assists/src/lib.rs | 2 + crates/ide-assists/src/tests/generated.rs | 31 ++++ 3 files changed, 168 insertions(+) create mode 100644 crates/ide-assists/src/handlers/unmerge_else_if.rs diff --git a/crates/ide-assists/src/handlers/unmerge_else_if.rs b/crates/ide-assists/src/handlers/unmerge_else_if.rs new file mode 100644 index 000000000000..16f65849f684 --- /dev/null +++ b/crates/ide-assists/src/handlers/unmerge_else_if.rs @@ -0,0 +1,135 @@ +use crate::{ + AssistId, + assist_context::{AssistContext, Assists}, +}; +use syntax::{ + AstNode, T, + ast::{self, edit::AstNodeEdit, syntax_factory::SyntaxFactory}, +}; + +// Assist: unmerge_else_if +// +// Unmerge `else if` into the `else {}`. +// +// ``` +// fn foo() { +// if cond1 { +// xxx() +// } else if$0 cond2 { +// yyy() +// } else { +// zzz() +// } +// } +// ``` +// -> +// ``` +// fn foo() { +// if cond1 { +// xxx() +// } else { +// if cond2 { +// yyy() +// } else { +// zzz() +// } +// } +// } +// ``` +pub(crate) fn unmerge_else_if(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + let if_keyword = ctx.find_token_syntax_at_offset(T![if])?; + let if_expr = if_keyword.parent().and_then(ast::IfExpr::cast)?; + let _parent_if = if_expr.syntax().parent().and_then(ast::IfExpr::cast)?; + + let target = if_expr.syntax().text_range(); + acc.add(AssistId::refactor_rewrite("unmerge_else_if"), "Unmerge else if", target, |builder| { + let mut edit = builder.make_editor(if_expr.syntax()); + let make = SyntaxFactory::with_mappings(); + + let new_if_expr = if_expr.reset_indent().indent(1.into()).into(); + let new_block = make.block_expr(None, Some(new_if_expr)).indent(if_expr.indent_level()); + edit.replace(if_expr.syntax(), new_block.syntax()); + + edit.add_mappings(make.finish_with_mappings()); + builder.add_file_edits(ctx.vfs_file_id(), edit); + }) +} + +#[cfg(test)] +mod tests { + use crate::tests::{check_assist, check_assist_not_applicable}; + + use super::*; + + #[test] + fn with_indent() { + check_assist( + unmerge_else_if, + r#" + fn foo() { + { + if cond1 { + xxx() + } else if$0 cond2 { + yyy( + "..." + ) + } else { + zzz( + "..." + ) + } + } + }"#, + r#" + fn foo() { + { + if cond1 { + xxx() + } else { + if cond2 { + yyy( + "..." + ) + } else { + zzz( + "..." + ) + } + } + } + }"#, + ); + } + + #[test] + fn not_applicable_outside_if_keyword() { + check_assist_not_applicable( + unmerge_else_if, + r#" + fn foo() { + if cond1 { + xxx() + } else if cond2$0 { + yyy() + } else { + zzz() + } + }"#, + ); + + check_assist_not_applicable( + unmerge_else_if, + r#" + fn foo() { + if cond1 { + xxx() + } else$0 if cond2 { + yyy() + } else { + zzz() + } + }"#, + ); + } +} diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs index e9f2d686465e..a99836426327 100644 --- a/crates/ide-assists/src/lib.rs +++ b/crates/ide-assists/src/lib.rs @@ -225,6 +225,7 @@ mod handlers { mod toggle_async_sugar; mod toggle_ignore; mod toggle_macro_delimiter; + mod unmerge_else_if; mod unmerge_imports; mod unmerge_match_arm; mod unnecessary_async; @@ -372,6 +373,7 @@ mod handlers { toggle_ignore::toggle_ignore, toggle_macro_delimiter::toggle_macro_delimiter, unmerge_match_arm::unmerge_match_arm, + unmerge_else_if::unmerge_else_if, unmerge_imports::unmerge_imports, unnecessary_async::unnecessary_async, unqualify_method_call::unqualify_method_call, diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs index a99fe8de333d..5bd07a0e0cbc 100644 --- a/crates/ide-assists/src/tests/generated.rs +++ b/crates/ide-assists/src/tests/generated.rs @@ -3577,6 +3577,37 @@ sth!{ } ) } +#[test] +fn doctest_unmerge_else_if() { + check_doc_test( + "unmerge_else_if", + r#####" +fn foo() { + if cond1 { + xxx() + } else if$0 cond2 { + yyy() + } else { + zzz() + } +} +"#####, + r#####" +fn foo() { + if cond1 { + xxx() + } else { + if cond2 { + yyy() + } else { + zzz() + } + } +} +"#####, + ) +} + #[test] fn doctest_unmerge_imports() { check_doc_test(