ra_ap_ide_assists/handlers/
flip_trait_bound.rs

1use syntax::{
2    Direction, T,
3    algo::non_trivia_sibling,
4    ast::{self, AstNode},
5};
6
7use crate::{AssistContext, AssistId, Assists};
8
9// Assist: flip_trait_bound
10//
11// Flips two trait bounds.
12//
13// ```
14// fn foo<T: Clone +$0 Copy>() { }
15// ```
16// ->
17// ```
18// fn foo<T: Copy + Clone>() { }
19// ```
20pub(crate) fn flip_trait_bound(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
21    // Only flip on the `+` token
22    let plus = ctx.find_token_syntax_at_offset(T![+])?;
23
24    // Make sure we're in a `TypeBoundList`
25    let parent = ast::TypeBoundList::cast(plus.parent()?)?;
26
27    let before = non_trivia_sibling(plus.clone().into(), Direction::Prev)?.into_node()?;
28    let after = non_trivia_sibling(plus.clone().into(), Direction::Next)?.into_node()?;
29
30    let target = plus.text_range();
31    acc.add(
32        AssistId::refactor_rewrite("flip_trait_bound"),
33        "Flip trait bounds",
34        target,
35        |builder| {
36            let mut editor = builder.make_editor(parent.syntax());
37            editor.replace(before.clone(), after.clone());
38            editor.replace(after, before);
39            builder.add_file_edits(ctx.vfs_file_id(), editor);
40        },
41    )
42}
43
44#[cfg(test)]
45mod tests {
46    use super::*;
47
48    use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
49
50    #[test]
51    fn flip_trait_bound_assist_available() {
52        check_assist_target(flip_trait_bound, "struct S<T> where T: A $0+ B + C { }", "+")
53    }
54
55    #[test]
56    fn flip_trait_bound_not_applicable_for_single_trait_bound() {
57        check_assist_not_applicable(flip_trait_bound, "struct S<T> where T: $0A { }")
58    }
59
60    #[test]
61    fn flip_trait_bound_works_for_dyn() {
62        check_assist(flip_trait_bound, "fn f<'a>(x: dyn Copy $0+ 'a)", "fn f<'a>(x: dyn 'a + Copy)")
63    }
64
65    #[test]
66    fn flip_trait_bound_works_for_struct() {
67        check_assist(
68            flip_trait_bound,
69            "struct S<T> where T: A $0+ B { }",
70            "struct S<T> where T: B + A { }",
71        )
72    }
73
74    #[test]
75    fn flip_trait_bound_works_for_trait_impl() {
76        check_assist(
77            flip_trait_bound,
78            "impl X for S<T> where T: A +$0 B { }",
79            "impl X for S<T> where T: B + A { }",
80        )
81    }
82
83    #[test]
84    fn flip_trait_bound_works_for_fn() {
85        check_assist(flip_trait_bound, "fn f<T: A $0+ B>(t: T) { }", "fn f<T: B + A>(t: T) { }")
86    }
87
88    #[test]
89    fn flip_trait_bound_works_for_fn_where_clause() {
90        check_assist(
91            flip_trait_bound,
92            "fn f<T>(t: T) where T: A +$0 B { }",
93            "fn f<T>(t: T) where T: B + A { }",
94        )
95    }
96
97    #[test]
98    fn flip_trait_bound_works_for_lifetime() {
99        check_assist(
100            flip_trait_bound,
101            "fn f<T>(t: T) where T: A $0+ 'static { }",
102            "fn f<T>(t: T) where T: 'static + A { }",
103        )
104    }
105
106    #[test]
107    fn flip_trait_bound_works_for_complex_bounds() {
108        check_assist(
109            flip_trait_bound,
110            "struct S<T> where T: A<T> $0+ b_mod::B<T> + C<T> { }",
111            "struct S<T> where T: b_mod::B<T> + A<T> + C<T> { }",
112        )
113    }
114
115    #[test]
116    fn flip_trait_bound_works_for_long_bounds() {
117        check_assist(
118            flip_trait_bound,
119            "struct S<T> where T: A + B + C + D + E + F +$0 G + H + I + J { }",
120            "struct S<T> where T: A + B + C + D + E + G + F + H + I + J { }",
121        )
122    }
123}