aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src/handlers/move_bounds.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists/src/handlers/move_bounds.rs')
-rw-r--r--crates/ra_assists/src/handlers/move_bounds.rs137
1 files changed, 137 insertions, 0 deletions
diff --git a/crates/ra_assists/src/handlers/move_bounds.rs b/crates/ra_assists/src/handlers/move_bounds.rs
new file mode 100644
index 000000000..90793b5fc
--- /dev/null
+++ b/crates/ra_assists/src/handlers/move_bounds.rs
@@ -0,0 +1,137 @@
1use ra_syntax::{
2 ast::{self, edit, make, AstNode, NameOwner, TypeBoundsOwner},
3 SyntaxElement,
4 SyntaxKind::*,
5};
6
7use crate::{Assist, AssistCtx, AssistId};
8
9// Assist: move_bounds_to_where_clause
10//
11// Moves inline type bounds to a where clause.
12//
13// ```
14// fn apply<T, U, <|>F: FnOnce(T) -> U>(f: F, x: T) -> U {
15// f(x)
16// }
17// ```
18// ->
19// ```
20// fn apply<T, U, F>(f: F, x: T) -> U where F: FnOnce(T) -> U {
21// f(x)
22// }
23// ```
24pub(crate) fn move_bounds_to_where_clause(ctx: AssistCtx) -> Option<Assist> {
25 let type_param_list = ctx.find_node_at_offset::<ast::TypeParamList>()?;
26
27 let mut type_params = type_param_list.type_params();
28 if type_params.all(|p| p.type_bound_list().is_none()) {
29 return None;
30 }
31
32 let parent = type_param_list.syntax().parent()?;
33 if parent.children_with_tokens().any(|it| it.kind() == WHERE_CLAUSE) {
34 return None;
35 }
36
37 let anchor: SyntaxElement = match parent.kind() {
38 FN_DEF => ast::FnDef::cast(parent)?.body()?.syntax().clone().into(),
39 TRAIT_DEF => ast::TraitDef::cast(parent)?.item_list()?.syntax().clone().into(),
40 IMPL_BLOCK => ast::ImplBlock::cast(parent)?.item_list()?.syntax().clone().into(),
41 ENUM_DEF => ast::EnumDef::cast(parent)?.variant_list()?.syntax().clone().into(),
42 STRUCT_DEF => parent
43 .children_with_tokens()
44 .find(|it| it.kind() == RECORD_FIELD_DEF_LIST || it.kind() == SEMI)?,
45 _ => return None,
46 };
47
48 ctx.add_assist(AssistId("move_bounds_to_where_clause"), "Move to where clause", |edit| {
49 let new_params = type_param_list
50 .type_params()
51 .filter(|it| it.type_bound_list().is_some())
52 .map(|type_param| {
53 let without_bounds = type_param.remove_bounds();
54 (type_param, without_bounds)
55 });
56
57 let new_type_param_list = edit::replace_descendants(&type_param_list, new_params);
58 edit.replace_ast(type_param_list.clone(), new_type_param_list);
59
60 let where_clause = {
61 let predicates = type_param_list.type_params().filter_map(build_predicate);
62 make::where_clause(predicates)
63 };
64
65 let to_insert = match anchor.prev_sibling_or_token() {
66 Some(ref elem) if elem.kind() == WHITESPACE => format!("{} ", where_clause.syntax()),
67 _ => format!(" {}", where_clause.syntax()),
68 };
69 edit.insert(anchor.text_range().start(), to_insert);
70 edit.target(type_param_list.syntax().text_range());
71 })
72}
73
74fn build_predicate(param: ast::TypeParam) -> Option<ast::WherePred> {
75 let path = make::path_from_name_ref(make::name_ref(&param.name()?.syntax().to_string()));
76 let predicate = make::where_pred(path, param.type_bound_list()?.bounds());
77 Some(predicate)
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83
84 use crate::helpers::check_assist;
85
86 #[test]
87 fn move_bounds_to_where_clause_fn() {
88 check_assist(
89 move_bounds_to_where_clause,
90 r#"
91 fn foo<T: u32, <|>F: FnOnce(T) -> T>() {}
92 "#,
93 r#"
94 fn foo<T, <|>F>() where T: u32, F: FnOnce(T) -> T {}
95 "#,
96 );
97 }
98
99 #[test]
100 fn move_bounds_to_where_clause_impl() {
101 check_assist(
102 move_bounds_to_where_clause,
103 r#"
104 impl<U: u32, <|>T> A<U, T> {}
105 "#,
106 r#"
107 impl<U, <|>T> A<U, T> where U: u32 {}
108 "#,
109 );
110 }
111
112 #[test]
113 fn move_bounds_to_where_clause_struct() {
114 check_assist(
115 move_bounds_to_where_clause,
116 r#"
117 struct A<<|>T: Iterator<Item = u32>> {}
118 "#,
119 r#"
120 struct A<<|>T> where T: Iterator<Item = u32> {}
121 "#,
122 );
123 }
124
125 #[test]
126 fn move_bounds_to_where_clause_tuple_struct() {
127 check_assist(
128 move_bounds_to_where_clause,
129 r#"
130 struct Pair<<|>T: u32>(T, T);
131 "#,
132 r#"
133 struct Pair<<|>T>(T, T) where T: u32;
134 "#,
135 );
136 }
137}