aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src/assists
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists/src/assists')
-rw-r--r--crates/ra_assists/src/assists/flip_trait_bound.rs119
1 files changed, 119 insertions, 0 deletions
diff --git a/crates/ra_assists/src/assists/flip_trait_bound.rs b/crates/ra_assists/src/assists/flip_trait_bound.rs
new file mode 100644
index 000000000..1625b241f
--- /dev/null
+++ b/crates/ra_assists/src/assists/flip_trait_bound.rs
@@ -0,0 +1,119 @@
1use hir::db::HirDatabase;
2use ra_syntax::{
3 algo::non_trivia_sibling,
4 ast::{self, AstNode},
5 Direction, T,
6};
7
8use crate::{Assist, AssistCtx, AssistId};
9
10// Assist: flip_trait_bound
11//
12// Flips two trait bounds.
13//
14// ```
15// fn foo<T: Clone +<|> Copy>() { }
16// ```
17// ->
18// ```
19// fn foo<T: Copy + Clone>() { }
20// ```
21pub(crate) fn flip_trait_bound(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
22 // We want to replicate the behavior of `flip_binexpr` by only suggesting
23 // the assist when the cursor is on a `+`
24 let plus = ctx.find_token_at_offset(T![+])?;
25
26 // Make sure we're in a `TypeBoundList`
27 if ast::TypeBoundList::cast(plus.parent()).is_none() {
28 return None;
29 }
30
31 let (before, after) = (
32 non_trivia_sibling(plus.clone().into(), Direction::Prev)?,
33 non_trivia_sibling(plus.clone().into(), Direction::Next)?,
34 );
35
36 ctx.add_action(AssistId("flip_trait_bound"), "flip trait bound", |edit| {
37 edit.target(plus.text_range());
38 edit.replace(before.text_range(), after.to_string());
39 edit.replace(after.text_range(), before.to_string());
40 });
41
42 ctx.build()
43}
44
45#[cfg(test)]
46mod tests {
47 use super::*;
48
49 use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target};
50
51 #[test]
52 fn flip_trait_bound_assist_available() {
53 check_assist_target(flip_trait_bound, "struct S<T> where T: A <|>+ B + C { }", "+")
54 }
55
56 #[test]
57 fn flip_trait_bound_not_applicable_for_single_trait_bound() {
58 check_assist_not_applicable(flip_trait_bound, "struct S<T> where T: <|>A { }")
59 }
60
61 #[test]
62 fn flip_trait_bound_works_for_struct() {
63 check_assist(
64 flip_trait_bound,
65 "struct S<T> where T: A <|>+ B { }",
66 "struct S<T> where T: B <|>+ A { }",
67 )
68 }
69
70 #[test]
71 fn flip_trait_bound_works_for_trait_impl() {
72 check_assist(
73 flip_trait_bound,
74 "impl X for S<T> where T: A +<|> B { }",
75 "impl X for S<T> where T: B +<|> A { }",
76 )
77 }
78
79 #[test]
80 fn flip_trait_bound_works_for_fn() {
81 check_assist(flip_trait_bound, "fn f<T: A <|>+ B>(t: T) { }", "fn f<T: B <|>+ A>(t: T) { }")
82 }
83
84 #[test]
85 fn flip_trait_bound_works_for_fn_where_clause() {
86 check_assist(
87 flip_trait_bound,
88 "fn f<T>(t: T) where T: A +<|> B { }",
89 "fn f<T>(t: T) where T: B +<|> A { }",
90 )
91 }
92
93 #[test]
94 fn flip_trait_bound_works_for_lifetime() {
95 check_assist(
96 flip_trait_bound,
97 "fn f<T>(t: T) where T: A <|>+ 'static { }",
98 "fn f<T>(t: T) where T: 'static <|>+ A { }",
99 )
100 }
101
102 #[test]
103 fn flip_trait_bound_works_for_complex_bounds() {
104 check_assist(
105 flip_trait_bound,
106 "struct S<T> where T: A<T> <|>+ b_mod::B<T> + C<T> { }",
107 "struct S<T> where T: b_mod::B<T> <|>+ A<T> + C<T> { }",
108 )
109 }
110
111 #[test]
112 fn flip_trait_bound_works_for_long_bounds() {
113 check_assist(
114 flip_trait_bound,
115 "struct S<T> where T: A + B + C + D + E + F +<|> G + H + I + J { }",
116 "struct S<T> where T: A + B + C + D + E + G +<|> F + H + I + J { }",
117 )
118 }
119}