]> git.lizzy.rs Git - rust.git/blob - src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_binexpr.rs
Auto merge of #103913 - Neutron3529:patch-1, r=thomcc
[rust.git] / src / tools / rust-analyzer / crates / ide-assists / src / handlers / flip_binexpr.rs
1 use syntax::ast::{self, AstNode, BinExpr};
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 +$0 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.selection_trimmed());
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<ast::BinaryOp> for FlipAction {
60     fn from(op_kind: ast::BinaryOp) -> Self {
61         match op_kind {
62             ast::BinaryOp::Assignment { .. } => FlipAction::DontFlip,
63             ast::BinaryOp::CmpOp(ast::CmpOp::Ord { ordering, strict }) => {
64                 let rev_op = match (ordering, strict) {
65                     (ast::Ordering::Less, true) => ">",
66                     (ast::Ordering::Less, false) => ">=",
67                     (ast::Ordering::Greater, true) => "<",
68                     (ast::Ordering::Greater, false) => "<=",
69                 };
70                 FlipAction::FlipAndReplaceOp(rev_op)
71             }
72             _ => FlipAction::Flip,
73         }
74     }
75 }
76
77 #[cfg(test)]
78 mod tests {
79     use super::*;
80
81     use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
82
83     #[test]
84     fn flip_binexpr_target_is_the_op() {
85         check_assist_target(flip_binexpr, "fn f() { let res = 1 ==$0 2; }", "==")
86     }
87
88     #[test]
89     fn flip_binexpr_not_applicable_for_assignment() {
90         check_assist_not_applicable(flip_binexpr, "fn f() { let mut _x = 1; _x +=$0 2 }")
91     }
92
93     #[test]
94     fn flip_binexpr_works_for_eq() {
95         check_assist(flip_binexpr, "fn f() { let res = 1 ==$0 2; }", "fn f() { let res = 2 == 1; }")
96     }
97
98     #[test]
99     fn flip_binexpr_works_for_gt() {
100         check_assist(flip_binexpr, "fn f() { let res = 1 >$0 2; }", "fn f() { let res = 2 < 1; }")
101     }
102
103     #[test]
104     fn flip_binexpr_works_for_lteq() {
105         check_assist(flip_binexpr, "fn f() { let res = 1 <=$0 2; }", "fn f() { let res = 2 >= 1; }")
106     }
107
108     #[test]
109     fn flip_binexpr_works_for_complex_expr() {
110         check_assist(
111             flip_binexpr,
112             "fn f() { let res = (1 + 1) ==$0 (2 + 2); }",
113             "fn f() { let res = (2 + 2) == (1 + 1); }",
114         )
115     }
116
117     #[test]
118     fn flip_binexpr_works_inside_match() {
119         check_assist(
120             flip_binexpr,
121             r#"
122             fn dyn_eq(&self, other: &dyn Diagnostic) -> bool {
123                 match other.downcast_ref::<Self>() {
124                     None => false,
125                     Some(it) => it ==$0 self,
126                 }
127             }
128             "#,
129             r#"
130             fn dyn_eq(&self, other: &dyn Diagnostic) -> bool {
131                 match other.downcast_ref::<Self>() {
132                     None => false,
133                     Some(it) => self == it,
134                 }
135             }
136             "#,
137         )
138     }
139 }