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