diff --git a/crates/ra_assists/src/assists/apply_demorgan.rs b/crates/ra_assists/src/assists/apply_demorgan.rs index 068da1774d..0d59a0d241 100644 --- a/crates/ra_assists/src/assists/apply_demorgan.rs +++ b/crates/ra_assists/src/assists/apply_demorgan.rs @@ -57,7 +57,7 @@ fn opposite_logic_op(kind: ast::BinOp) -> Option<&'static str> { } // This function tries to undo unary negation, or inequality -fn undo_negation(node: SyntaxNode) -> Option { +pub(crate) fn undo_negation(node: SyntaxNode) -> Option { match ast::Expr::cast(node)? { ast::Expr::BinExpr(bin) => match bin.op_kind()? { ast::BinOp::NegatedEqualityTest => { diff --git a/crates/ra_assists/src/assists/invert_if.rs b/crates/ra_assists/src/assists/invert_if.rs new file mode 100644 index 0000000000..9a53a3d18f --- /dev/null +++ b/crates/ra_assists/src/assists/invert_if.rs @@ -0,0 +1,85 @@ +use hir::db::HirDatabase; +use ra_syntax::ast::{self, AstNode}; +use ra_syntax::{TextRange, TextUnit}; + +use crate::{Assist, AssistCtx, AssistId}; +use super::apply_demorgan::undo_negation; + +// Assist: invert_if +// +// Apply invert_if +// This transforms if expressions of the form `if !x {A} else {B}` into `if x {B} else {A}` +// This also works with `!=`. This assist can only be applied with the cursor +// on `if`. +// +// ``` +// fn main() { +// if<|> !y {A} else {B} +// } +// ``` +// -> +// ``` +// fn main() { +// if y {B} else {A} +// } +// ``` + + +pub(crate) fn invert_if(ctx: AssistCtx) -> Option { + let expr = ctx.find_node_at_offset::()?; + let expr_range = expr.syntax().text_range(); + let if_range = TextRange::offset_len(expr_range.start(), TextUnit::from_usize(2)); + let cursor_in_range = ctx.frange.range.is_subrange(&if_range); + if !cursor_in_range { + return None; + } + + let cond = expr.condition()?.expr()?.syntax().clone(); + let then_node = expr.then_branch()?.syntax().clone(); + + if let ast::ElseBranch::Block(else_block) = expr.else_branch()? { + let flip_cond = undo_negation(cond.clone())?; + let cond_range = cond.text_range(); + let else_node = else_block.syntax(); + let else_range = else_node.text_range(); + let then_range = then_node.text_range(); + ctx.add_assist(AssistId("invert_if"), "invert if branches", |edit| { + edit.target(if_range); + edit.replace(cond_range, flip_cond); + edit.replace(else_range, then_node.text()); + edit.replace(then_range, else_node.text()); + }) + + } else { + None + } + + +} + +#[cfg(test)] +mod tests { + use super::*; + + use crate::helpers::{check_assist, check_assist_not_applicable}; + + #[test] + fn invert_if_remove_inequality() { + check_assist(invert_if, "fn f() { i<|>f x != 3 {1} else {3 + 2} }", "fn f() { i<|>f x == 3 {3 + 2} else {1} }") + } + + #[test] + fn invert_if_remove_not() { + check_assist(invert_if, "fn f() { <|>if !cond {3 * 2} else {1} }", "fn f() { <|>if cond {1} else {3 * 2} }") + } + + #[test] + fn invert_if_doesnt_apply_with_cursor_not_on_if() { + check_assist_not_applicable(invert_if, "fn f() { if !<|>cond {3 * 2} else {1} }") + } + + #[test] + fn invert_if_doesnt_apply_without_negated() { + check_assist_not_applicable(invert_if, "fn f() { i<|>f cond {3 * 2} else {1} }") + } +} diff --git a/crates/ra_assists/src/doc_tests/generated.rs b/crates/ra_assists/src/doc_tests/generated.rs index 176761efb9..1ccc016d32 100644 --- a/crates/ra_assists/src/doc_tests/generated.rs +++ b/crates/ra_assists/src/doc_tests/generated.rs @@ -341,6 +341,23 @@ fn main() { ) } +#[test] +fn doctest_invert_if() { + check( + "invert_if", + r#####" +fn main() { + if<|> !y {A} else {B} +} +"#####, + r#####" +fn main() { + if y {B} else {A} +} +"#####, + ) +} + #[test] fn doctest_make_raw_string() { check( diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index f2f0dacbf7..a372bd8b9d 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs @@ -97,6 +97,7 @@ mod assists { mod add_impl; mod add_new; mod apply_demorgan; + mod invert_if; mod flip_comma; mod flip_binexpr; mod flip_trait_bound; @@ -122,6 +123,7 @@ mod assists { add_impl::add_impl, add_new::add_new, apply_demorgan::apply_demorgan, + invert_if::invert_if, change_visibility::change_visibility, fill_match_arms::fill_match_arms, merge_match_arms::merge_match_arms, diff --git a/docs/user/assists.md b/docs/user/assists.md index 8da7578e2f..6e7811bd6c 100644 --- a/docs/user/assists.md +++ b/docs/user/assists.md @@ -329,6 +329,25 @@ fn main() { } ``` +## `invert_if` + +Apply invert_if +This transforms if expressions of the form `if !x {A} else {B}` into `if x {B} else {A}` +This also works with `!=`. This assist can only be applied with the cursor +on `if`. + +```rust +// BEFORE +fn main() { + if┃ !y {A} else {B} +} + +// AFTER +fn main() { + if y {B} else {A} +} +``` + ## `make_raw_string` Adds `r#` to a plain string literal.