aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src/assists/invert_if.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists/src/assists/invert_if.rs')
-rw-r--r--crates/ra_assists/src/assists/invert_if.rs102
1 files changed, 102 insertions, 0 deletions
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 000000000..bababa3e2
--- /dev/null
+++ b/crates/ra_assists/src/assists/invert_if.rs
@@ -0,0 +1,102 @@
1use hir::db::HirDatabase;
2use ra_syntax::ast::{self, AstNode};
3use ra_syntax::T;
4
5use crate::{Assist, AssistCtx, AssistId};
6
7// Assist: invert_if
8//
9// Apply invert_if
10// This transforms if expressions of the form `if !x {A} else {B}` into `if x {B} else {A}`
11// This also works with `!=`. This assist can only be applied with the cursor
12// on `if`.
13//
14// ```
15// fn main() {
16// if<|> !y { A } else { B }
17// }
18// ```
19// ->
20// ```
21// fn main() {
22// if y { B } else { A }
23// }
24// ```
25
26pub(crate) fn invert_if(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
27 let if_keyword = ctx.find_token_at_offset(T![if])?;
28 let expr = ast::IfExpr::cast(if_keyword.parent())?;
29 let if_range = if_keyword.text_range();
30 let cursor_in_range = ctx.frange.range.is_subrange(&if_range);
31 if !cursor_in_range {
32 return None;
33 }
34
35 let cond = expr.condition()?.expr()?;
36 let then_node = expr.then_branch()?.syntax().clone();
37
38 if let ast::ElseBranch::Block(else_block) = expr.else_branch()? {
39 let flip_cond = invert_boolean_expression(&cond)?;
40 let cond_range = cond.syntax().text_range();
41 let else_node = else_block.syntax();
42 let else_range = else_node.text_range();
43 let then_range = then_node.text_range();
44 return ctx.add_assist(AssistId("invert_if"), "invert if branches", |edit| {
45 edit.target(if_range);
46 edit.replace(cond_range, flip_cond.syntax().text());
47 edit.replace(else_range, then_node.text());
48 edit.replace(then_range, else_node.text());
49 });
50 }
51
52 None
53}
54
55pub(crate) fn invert_boolean_expression(expr: &ast::Expr) -> Option<ast::Expr> {
56 match expr {
57 ast::Expr::BinExpr(bin) => match bin.op_kind()? {
58 ast::BinOp::NegatedEqualityTest => bin.replace_op(T![==]).map(|it| it.into()),
59 _ => None,
60 },
61 ast::Expr::PrefixExpr(pe) => match pe.op_kind()? {
62 ast::PrefixOp::Not => pe.expr(),
63 _ => None,
64 },
65 _ => None,
66 }
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72
73 use crate::helpers::{check_assist, check_assist_not_applicable};
74
75 #[test]
76 fn invert_if_remove_inequality() {
77 check_assist(
78 invert_if,
79 "fn f() { i<|>f x != 3 { 1 } else { 3 + 2 } }",
80 "fn f() { i<|>f x == 3 { 3 + 2 } else { 1 } }",
81 )
82 }
83
84 #[test]
85 fn invert_if_remove_not() {
86 check_assist(
87 invert_if,
88 "fn f() { <|>if !cond { 3 * 2 } else { 1 } }",
89 "fn f() { <|>if cond { 1 } else { 3 * 2 } }",
90 )
91 }
92
93 #[test]
94 fn invert_if_doesnt_apply_with_cursor_not_on_if() {
95 check_assist_not_applicable(invert_if, "fn f() { if !<|>cond { 3 * 2 } else { 1 } }")
96 }
97
98 #[test]
99 fn invert_if_doesnt_apply_without_negated() {
100 check_assist_not_applicable(invert_if, "fn f() { i<|>f cond { 3 * 2 } else { 1 } }")
101 }
102}