aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src/assists/flip_binexpr.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists/src/assists/flip_binexpr.rs')
-rw-r--r--crates/ra_assists/src/assists/flip_binexpr.rs141
1 files changed, 141 insertions, 0 deletions
diff --git a/crates/ra_assists/src/assists/flip_binexpr.rs b/crates/ra_assists/src/assists/flip_binexpr.rs
new file mode 100644
index 000000000..b55b36a8e
--- /dev/null
+++ b/crates/ra_assists/src/assists/flip_binexpr.rs
@@ -0,0 +1,141 @@
1use hir::db::HirDatabase;
2use ra_syntax::ast::{AstNode, BinExpr, BinOp};
3
4use crate::{Assist, AssistCtx, AssistId};
5
6/// Flip binary expression assist.
7pub(crate) fn flip_binexpr(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
8 let expr = ctx.node_at_offset::<BinExpr>()?;
9 let lhs = expr.lhs()?.syntax().clone();
10 let rhs = expr.rhs()?.syntax().clone();
11 let op_range = expr.op_token()?.text_range();
12 // The assist should be applied only if the cursor is on the operator
13 let cursor_in_range = ctx.frange.range.is_subrange(&op_range);
14 if !cursor_in_range {
15 return None;
16 }
17 let action: FlipAction = expr.op_kind()?.into();
18 // The assist should not be applied for certain operators
19 if let FlipAction::DontFlip = action {
20 return None;
21 }
22
23 ctx.add_action(AssistId("flip_binexpr"), "flip binary expression", |edit| {
24 edit.target(op_range);
25 if let FlipAction::FlipAndReplaceOp(new_op) = action {
26 edit.replace(op_range, new_op);
27 }
28 edit.replace(lhs.text_range(), rhs.text());
29 edit.replace(rhs.text_range(), lhs.text());
30 });
31
32 ctx.build()
33}
34
35enum FlipAction {
36 // Flip the expression
37 Flip,
38 // Flip the expression and replace the operator with this string
39 FlipAndReplaceOp(&'static str),
40 // Do not flip the expression
41 DontFlip,
42}
43
44impl From<BinOp> for FlipAction {
45 fn from(op_kind: BinOp) -> Self {
46 match op_kind {
47 BinOp::Assignment => FlipAction::DontFlip,
48 BinOp::AddAssign => FlipAction::DontFlip,
49 BinOp::DivAssign => FlipAction::DontFlip,
50 BinOp::MulAssign => FlipAction::DontFlip,
51 BinOp::RemAssign => FlipAction::DontFlip,
52 BinOp::ShrAssign => FlipAction::DontFlip,
53 BinOp::ShlAssign => FlipAction::DontFlip,
54 BinOp::SubAssign => FlipAction::DontFlip,
55 BinOp::BitOrAssign => FlipAction::DontFlip,
56 BinOp::BitAndAssign => FlipAction::DontFlip,
57 BinOp::BitXorAssign => FlipAction::DontFlip,
58 BinOp::GreaterTest => FlipAction::FlipAndReplaceOp("<"),
59 BinOp::GreaterEqualTest => FlipAction::FlipAndReplaceOp("<="),
60 BinOp::LesserTest => FlipAction::FlipAndReplaceOp(">"),
61 BinOp::LesserEqualTest => FlipAction::FlipAndReplaceOp(">="),
62 _ => FlipAction::Flip,
63 }
64 }
65}
66
67#[cfg(test)]
68mod tests {
69 use super::*;
70
71 use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target};
72
73 #[test]
74 fn flip_binexpr_target_is_the_op() {
75 check_assist_target(flip_binexpr, "fn f() { let res = 1 ==<|> 2; }", "==")
76 }
77
78 #[test]
79 fn flip_binexpr_not_applicable_for_assignment() {
80 check_assist_not_applicable(flip_binexpr, "fn f() { let mut _x = 1; _x +=<|> 2 }")
81 }
82
83 #[test]
84 fn flip_binexpr_works_for_eq() {
85 check_assist(
86 flip_binexpr,
87 "fn f() { let res = 1 ==<|> 2; }",
88 "fn f() { let res = 2 ==<|> 1; }",
89 )
90 }
91
92 #[test]
93 fn flip_binexpr_works_for_gt() {
94 check_assist(
95 flip_binexpr,
96 "fn f() { let res = 1 ><|> 2; }",
97 "fn f() { let res = 2 <<|> 1; }",
98 )
99 }
100
101 #[test]
102 fn flip_binexpr_works_for_lteq() {
103 check_assist(
104 flip_binexpr,
105 "fn f() { let res = 1 <=<|> 2; }",
106 "fn f() { let res = 2 >=<|> 1; }",
107 )
108 }
109
110 #[test]
111 fn flip_binexpr_works_for_complex_expr() {
112 check_assist(
113 flip_binexpr,
114 "fn f() { let res = (1 + 1) ==<|> (2 + 2); }",
115 "fn f() { let res = (2 + 2) ==<|> (1 + 1); }",
116 )
117 }
118
119 #[test]
120 fn flip_binexpr_works_inside_match() {
121 check_assist(
122 flip_binexpr,
123 r#"
124 fn dyn_eq(&self, other: &dyn Diagnostic) -> bool {
125 match other.downcast_ref::<Self>() {
126 None => false,
127 Some(it) => it ==<|> self,
128 }
129 }
130 "#,
131 r#"
132 fn dyn_eq(&self, other: &dyn Diagnostic) -> bool {
133 match other.downcast_ref::<Self>() {
134 None => false,
135 Some(it) => self ==<|> it,
136 }
137 }
138 "#,
139 )
140 }
141}