diff options
Diffstat (limited to 'crates/assists/src/handlers/flip_binexpr.rs')
-rw-r--r-- | crates/assists/src/handlers/flip_binexpr.rs | 142 |
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 @@ | |||
1 | use syntax::ast::{AstNode, BinExpr, BinOp}; | ||
2 | |||
3 | use 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 | // ``` | ||
20 | pub(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 | |||
50 | enum 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 | |||
59 | impl 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)] | ||
73 | mod 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 | } | ||