aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src/assists
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists/src/assists')
-rw-r--r--crates/ra_assists/src/assists/apply_demorgan.rs40
-rw-r--r--crates/ra_assists/src/assists/invert_if.rs55
2 files changed, 43 insertions, 52 deletions
diff --git a/crates/ra_assists/src/assists/apply_demorgan.rs b/crates/ra_assists/src/assists/apply_demorgan.rs
index 0d59a0d24..7c57c0560 100644
--- a/crates/ra_assists/src/assists/apply_demorgan.rs
+++ b/crates/ra_assists/src/assists/apply_demorgan.rs
@@ -1,6 +1,6 @@
1use super::invert_if::invert_boolean_expression;
1use hir::db::HirDatabase; 2use hir::db::HirDatabase;
2use ra_syntax::ast::{self, AstNode}; 3use ra_syntax::ast::{self, AstNode};
3use ra_syntax::SyntaxNode;
4 4
5use crate::{Assist, AssistCtx, AssistId}; 5use crate::{Assist, AssistCtx, AssistId};
6 6
@@ -32,18 +32,18 @@ pub(crate) fn apply_demorgan(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist>
32 if !cursor_in_range { 32 if !cursor_in_range {
33 return None; 33 return None;
34 } 34 }
35 let lhs = expr.lhs()?.syntax().clone(); 35 let lhs = expr.lhs()?;
36 let lhs_range = lhs.text_range(); 36 let lhs_range = lhs.syntax().text_range();
37 let rhs = expr.rhs()?.syntax().clone(); 37 let rhs = expr.rhs()?;
38 let rhs_range = rhs.text_range(); 38 let rhs_range = rhs.syntax().text_range();
39 let not_lhs = undo_negation(lhs)?; 39 let not_lhs = invert_boolean_expression(&lhs)?;
40 let not_rhs = undo_negation(rhs)?; 40 let not_rhs = invert_boolean_expression(&rhs)?;
41 41
42 ctx.add_assist(AssistId("apply_demorgan"), "apply demorgan's law", |edit| { 42 ctx.add_assist(AssistId("apply_demorgan"), "apply demorgan's law", |edit| {
43 edit.target(op_range); 43 edit.target(op_range);
44 edit.replace(op_range, opposite_op); 44 edit.replace(op_range, opposite_op);
45 edit.replace(lhs_range, format!("!({}", not_lhs)); 45 edit.replace(lhs_range, format!("!({}", not_lhs.syntax().text()));
46 edit.replace(rhs_range, format!("{})", not_rhs)); 46 edit.replace(rhs_range, format!("{})", not_rhs.syntax().text()));
47 }) 47 })
48} 48}
49 49
@@ -56,28 +56,6 @@ fn opposite_logic_op(kind: ast::BinOp) -> Option<&'static str> {
56 } 56 }
57} 57}
58 58
59// This function tries to undo unary negation, or inequality
60pub(crate) fn undo_negation(node: SyntaxNode) -> Option<String> {
61 match ast::Expr::cast(node)? {
62 ast::Expr::BinExpr(bin) => match bin.op_kind()? {
63 ast::BinOp::NegatedEqualityTest => {
64 let lhs = bin.lhs()?.syntax().text();
65 let rhs = bin.rhs()?.syntax().text();
66 Some(format!("{} == {}", lhs, rhs))
67 }
68 _ => None,
69 },
70 ast::Expr::PrefixExpr(pe) => match pe.op_kind()? {
71 ast::PrefixOp::Not => {
72 let child = pe.expr()?.syntax().text();
73 Some(String::from(child))
74 }
75 _ => None,
76 },
77 _ => None,
78 }
79}
80
81#[cfg(test)] 59#[cfg(test)]
82mod tests { 60mod tests {
83 use super::*; 61 use super::*;
diff --git a/crates/ra_assists/src/assists/invert_if.rs b/crates/ra_assists/src/assists/invert_if.rs
index c2c8529fe..bababa3e2 100644
--- a/crates/ra_assists/src/assists/invert_if.rs
+++ b/crates/ra_assists/src/assists/invert_if.rs
@@ -1,8 +1,7 @@
1use hir::db::HirDatabase; 1use hir::db::HirDatabase;
2use ra_syntax::ast::{self, AstNode}; 2use ra_syntax::ast::{self, AstNode};
3use ra_syntax::{TextRange, TextUnit}; 3use ra_syntax::T;
4 4
5use super::apply_demorgan::undo_negation;
6use crate::{Assist, AssistCtx, AssistId}; 5use crate::{Assist, AssistCtx, AssistId};
7 6
8// Assist: invert_if 7// Assist: invert_if
@@ -14,42 +13,56 @@ use crate::{Assist, AssistCtx, AssistId};
14// 13//
15// ``` 14// ```
16// fn main() { 15// fn main() {
17// if<|> !y {A} else {B} 16// if<|> !y { A } else { B }
18// } 17// }
19// ``` 18// ```
20// -> 19// ->
21// ``` 20// ```
22// fn main() { 21// fn main() {
23// if y {B} else {A} 22// if y { B } else { A }
24// } 23// }
25// ``` 24// ```
26 25
27pub(crate) fn invert_if(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 26pub(crate) fn invert_if(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
28 let expr = ctx.find_node_at_offset::<ast::IfExpr>()?; 27 let if_keyword = ctx.find_token_at_offset(T![if])?;
29 let expr_range = expr.syntax().text_range(); 28 let expr = ast::IfExpr::cast(if_keyword.parent())?;
30 let if_range = TextRange::offset_len(expr_range.start(), TextUnit::from_usize(2)); 29 let if_range = if_keyword.text_range();
31 let cursor_in_range = ctx.frange.range.is_subrange(&if_range); 30 let cursor_in_range = ctx.frange.range.is_subrange(&if_range);
32 if !cursor_in_range { 31 if !cursor_in_range {
33 return None; 32 return None;
34 } 33 }
35 34
36 let cond = expr.condition()?.expr()?.syntax().clone(); 35 let cond = expr.condition()?.expr()?;
37 let then_node = expr.then_branch()?.syntax().clone(); 36 let then_node = expr.then_branch()?.syntax().clone();
38 37
39 if let ast::ElseBranch::Block(else_block) = expr.else_branch()? { 38 if let ast::ElseBranch::Block(else_block) = expr.else_branch()? {
40 let flip_cond = undo_negation(cond.clone())?; 39 let flip_cond = invert_boolean_expression(&cond)?;
41 let cond_range = cond.text_range(); 40 let cond_range = cond.syntax().text_range();
42 let else_node = else_block.syntax(); 41 let else_node = else_block.syntax();
43 let else_range = else_node.text_range(); 42 let else_range = else_node.text_range();
44 let then_range = then_node.text_range(); 43 let then_range = then_node.text_range();
45 ctx.add_assist(AssistId("invert_if"), "invert if branches", |edit| { 44 return ctx.add_assist(AssistId("invert_if"), "invert if branches", |edit| {
46 edit.target(if_range); 45 edit.target(if_range);
47 edit.replace(cond_range, flip_cond); 46 edit.replace(cond_range, flip_cond.syntax().text());
48 edit.replace(else_range, then_node.text()); 47 edit.replace(else_range, then_node.text());
49 edit.replace(then_range, else_node.text()); 48 edit.replace(then_range, else_node.text());
50 }) 49 });
51 } else { 50 }
52 None 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,
53 } 66 }
54} 67}
55 68
@@ -63,8 +76,8 @@ mod tests {
63 fn invert_if_remove_inequality() { 76 fn invert_if_remove_inequality() {
64 check_assist( 77 check_assist(
65 invert_if, 78 invert_if,
66 "fn f() { i<|>f x != 3 {1} else {3 + 2} }", 79 "fn f() { i<|>f x != 3 { 1 } else { 3 + 2 } }",
67 "fn f() { i<|>f x == 3 {3 + 2} else {1} }", 80 "fn f() { i<|>f x == 3 { 3 + 2 } else { 1 } }",
68 ) 81 )
69 } 82 }
70 83
@@ -72,18 +85,18 @@ mod tests {
72 fn invert_if_remove_not() { 85 fn invert_if_remove_not() {
73 check_assist( 86 check_assist(
74 invert_if, 87 invert_if,
75 "fn f() { <|>if !cond {3 * 2} else {1} }", 88 "fn f() { <|>if !cond { 3 * 2 } else { 1 } }",
76 "fn f() { <|>if cond {1} else {3 * 2} }", 89 "fn f() { <|>if cond { 1 } else { 3 * 2 } }",
77 ) 90 )
78 } 91 }
79 92
80 #[test] 93 #[test]
81 fn invert_if_doesnt_apply_with_cursor_not_on_if() { 94 fn invert_if_doesnt_apply_with_cursor_not_on_if() {
82 check_assist_not_applicable(invert_if, "fn f() { if !<|>cond {3 * 2} else {1} }") 95 check_assist_not_applicable(invert_if, "fn f() { if !<|>cond { 3 * 2 } else { 1 } }")
83 } 96 }
84 97
85 #[test] 98 #[test]
86 fn invert_if_doesnt_apply_without_negated() { 99 fn invert_if_doesnt_apply_without_negated() {
87 check_assist_not_applicable(invert_if, "fn f() { i<|>f cond {3 * 2} else {1} }") 100 check_assist_not_applicable(invert_if, "fn f() { i<|>f cond { 3 * 2 } else { 1 } }")
88 } 101 }
89} 102}