diff options
Diffstat (limited to 'crates/assists/src/handlers/move_guard.rs')
-rw-r--r-- | crates/assists/src/handlers/move_guard.rs | 293 |
1 files changed, 293 insertions, 0 deletions
diff --git a/crates/assists/src/handlers/move_guard.rs b/crates/assists/src/handlers/move_guard.rs new file mode 100644 index 000000000..452115fe6 --- /dev/null +++ b/crates/assists/src/handlers/move_guard.rs | |||
@@ -0,0 +1,293 @@ | |||
1 | use syntax::{ | ||
2 | ast::{edit::AstNodeEdit, make, AstNode, IfExpr, MatchArm}, | ||
3 | SyntaxKind::WHITESPACE, | ||
4 | }; | ||
5 | |||
6 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | ||
7 | |||
8 | // Assist: move_guard_to_arm_body | ||
9 | // | ||
10 | // Moves match guard into match arm body. | ||
11 | // | ||
12 | // ``` | ||
13 | // enum Action { Move { distance: u32 }, Stop } | ||
14 | // | ||
15 | // fn handle(action: Action) { | ||
16 | // match action { | ||
17 | // Action::Move { distance } <|>if distance > 10 => foo(), | ||
18 | // _ => (), | ||
19 | // } | ||
20 | // } | ||
21 | // ``` | ||
22 | // -> | ||
23 | // ``` | ||
24 | // enum Action { Move { distance: u32 }, Stop } | ||
25 | // | ||
26 | // fn handle(action: Action) { | ||
27 | // match action { | ||
28 | // Action::Move { distance } => if distance > 10 { | ||
29 | // foo() | ||
30 | // }, | ||
31 | // _ => (), | ||
32 | // } | ||
33 | // } | ||
34 | // ``` | ||
35 | pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
36 | let match_arm = ctx.find_node_at_offset::<MatchArm>()?; | ||
37 | let guard = match_arm.guard()?; | ||
38 | let space_before_guard = guard.syntax().prev_sibling_or_token(); | ||
39 | |||
40 | let guard_condition = guard.expr()?; | ||
41 | let arm_expr = match_arm.expr()?; | ||
42 | let if_expr = make::expr_if( | ||
43 | make::condition(guard_condition, None), | ||
44 | make::block_expr(None, Some(arm_expr.clone())), | ||
45 | ) | ||
46 | .indent(arm_expr.indent_level()); | ||
47 | |||
48 | let target = guard.syntax().text_range(); | ||
49 | acc.add( | ||
50 | AssistId("move_guard_to_arm_body", AssistKind::RefactorRewrite), | ||
51 | "Move guard to arm body", | ||
52 | target, | ||
53 | |edit| { | ||
54 | match space_before_guard { | ||
55 | Some(element) if element.kind() == WHITESPACE => { | ||
56 | edit.delete(element.text_range()); | ||
57 | } | ||
58 | _ => (), | ||
59 | }; | ||
60 | |||
61 | edit.delete(guard.syntax().text_range()); | ||
62 | edit.replace_ast(arm_expr, if_expr); | ||
63 | }, | ||
64 | ) | ||
65 | } | ||
66 | |||
67 | // Assist: move_arm_cond_to_match_guard | ||
68 | // | ||
69 | // Moves if expression from match arm body into a guard. | ||
70 | // | ||
71 | // ``` | ||
72 | // enum Action { Move { distance: u32 }, Stop } | ||
73 | // | ||
74 | // fn handle(action: Action) { | ||
75 | // match action { | ||
76 | // Action::Move { distance } => <|>if distance > 10 { foo() }, | ||
77 | // _ => (), | ||
78 | // } | ||
79 | // } | ||
80 | // ``` | ||
81 | // -> | ||
82 | // ``` | ||
83 | // enum Action { Move { distance: u32 }, Stop } | ||
84 | // | ||
85 | // fn handle(action: Action) { | ||
86 | // match action { | ||
87 | // Action::Move { distance } if distance > 10 => foo(), | ||
88 | // _ => (), | ||
89 | // } | ||
90 | // } | ||
91 | // ``` | ||
92 | pub(crate) fn move_arm_cond_to_match_guard(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
93 | let match_arm: MatchArm = ctx.find_node_at_offset::<MatchArm>()?; | ||
94 | let match_pat = match_arm.pat()?; | ||
95 | |||
96 | let arm_body = match_arm.expr()?; | ||
97 | let if_expr: IfExpr = IfExpr::cast(arm_body.syntax().clone())?; | ||
98 | let cond = if_expr.condition()?; | ||
99 | let then_block = if_expr.then_branch()?; | ||
100 | |||
101 | // Not support if with else branch | ||
102 | if if_expr.else_branch().is_some() { | ||
103 | return None; | ||
104 | } | ||
105 | // Not support moving if let to arm guard | ||
106 | if cond.pat().is_some() { | ||
107 | return None; | ||
108 | } | ||
109 | |||
110 | let buf = format!(" if {}", cond.syntax().text()); | ||
111 | |||
112 | let target = if_expr.syntax().text_range(); | ||
113 | acc.add( | ||
114 | AssistId("move_arm_cond_to_match_guard", AssistKind::RefactorRewrite), | ||
115 | "Move condition to match guard", | ||
116 | target, | ||
117 | |edit| { | ||
118 | let then_only_expr = then_block.statements().next().is_none(); | ||
119 | |||
120 | match &then_block.expr() { | ||
121 | Some(then_expr) if then_only_expr => { | ||
122 | edit.replace(if_expr.syntax().text_range(), then_expr.syntax().text()) | ||
123 | } | ||
124 | _ => edit.replace(if_expr.syntax().text_range(), then_block.syntax().text()), | ||
125 | } | ||
126 | |||
127 | edit.insert(match_pat.syntax().text_range().end(), buf); | ||
128 | }, | ||
129 | ) | ||
130 | } | ||
131 | |||
132 | #[cfg(test)] | ||
133 | mod tests { | ||
134 | use super::*; | ||
135 | |||
136 | use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; | ||
137 | |||
138 | #[test] | ||
139 | fn move_guard_to_arm_body_target() { | ||
140 | check_assist_target( | ||
141 | move_guard_to_arm_body, | ||
142 | r#" | ||
143 | fn main() { | ||
144 | match 92 { | ||
145 | x <|>if x > 10 => false, | ||
146 | _ => true | ||
147 | } | ||
148 | } | ||
149 | "#, | ||
150 | r#"if x > 10"#, | ||
151 | ); | ||
152 | } | ||
153 | |||
154 | #[test] | ||
155 | fn move_guard_to_arm_body_works() { | ||
156 | check_assist( | ||
157 | move_guard_to_arm_body, | ||
158 | r#" | ||
159 | fn main() { | ||
160 | match 92 { | ||
161 | x <|>if x > 10 => false, | ||
162 | _ => true | ||
163 | } | ||
164 | } | ||
165 | "#, | ||
166 | r#" | ||
167 | fn main() { | ||
168 | match 92 { | ||
169 | x => if x > 10 { | ||
170 | false | ||
171 | }, | ||
172 | _ => true | ||
173 | } | ||
174 | } | ||
175 | "#, | ||
176 | ); | ||
177 | } | ||
178 | |||
179 | #[test] | ||
180 | fn move_guard_to_arm_body_works_complex_match() { | ||
181 | check_assist( | ||
182 | move_guard_to_arm_body, | ||
183 | r#" | ||
184 | fn main() { | ||
185 | match 92 { | ||
186 | <|>x @ 4 | x @ 5 if x > 5 => true, | ||
187 | _ => false | ||
188 | } | ||
189 | } | ||
190 | "#, | ||
191 | r#" | ||
192 | fn main() { | ||
193 | match 92 { | ||
194 | x @ 4 | x @ 5 => if x > 5 { | ||
195 | true | ||
196 | }, | ||
197 | _ => false | ||
198 | } | ||
199 | } | ||
200 | "#, | ||
201 | ); | ||
202 | } | ||
203 | |||
204 | #[test] | ||
205 | fn move_arm_cond_to_match_guard_works() { | ||
206 | check_assist( | ||
207 | move_arm_cond_to_match_guard, | ||
208 | r#" | ||
209 | fn main() { | ||
210 | match 92 { | ||
211 | x => if x > 10 { <|>false }, | ||
212 | _ => true | ||
213 | } | ||
214 | } | ||
215 | "#, | ||
216 | r#" | ||
217 | fn main() { | ||
218 | match 92 { | ||
219 | x if x > 10 => false, | ||
220 | _ => true | ||
221 | } | ||
222 | } | ||
223 | "#, | ||
224 | ); | ||
225 | } | ||
226 | |||
227 | #[test] | ||
228 | fn move_arm_cond_to_match_guard_if_let_not_works() { | ||
229 | check_assist_not_applicable( | ||
230 | move_arm_cond_to_match_guard, | ||
231 | r#" | ||
232 | fn main() { | ||
233 | match 92 { | ||
234 | x => if let 62 = x { <|>false }, | ||
235 | _ => true | ||
236 | } | ||
237 | } | ||
238 | "#, | ||
239 | ); | ||
240 | } | ||
241 | |||
242 | #[test] | ||
243 | fn move_arm_cond_to_match_guard_if_empty_body_works() { | ||
244 | check_assist( | ||
245 | move_arm_cond_to_match_guard, | ||
246 | r#" | ||
247 | fn main() { | ||
248 | match 92 { | ||
249 | x => if x > 10 { <|> }, | ||
250 | _ => true | ||
251 | } | ||
252 | } | ||
253 | "#, | ||
254 | r#" | ||
255 | fn main() { | ||
256 | match 92 { | ||
257 | x if x > 10 => { }, | ||
258 | _ => true | ||
259 | } | ||
260 | } | ||
261 | "#, | ||
262 | ); | ||
263 | } | ||
264 | |||
265 | #[test] | ||
266 | fn move_arm_cond_to_match_guard_if_multiline_body_works() { | ||
267 | check_assist( | ||
268 | move_arm_cond_to_match_guard, | ||
269 | r#" | ||
270 | fn main() { | ||
271 | match 92 { | ||
272 | x => if x > 10 { | ||
273 | 92;<|> | ||
274 | false | ||
275 | }, | ||
276 | _ => true | ||
277 | } | ||
278 | } | ||
279 | "#, | ||
280 | r#" | ||
281 | fn main() { | ||
282 | match 92 { | ||
283 | x if x > 10 => { | ||
284 | 92; | ||
285 | false | ||
286 | }, | ||
287 | _ => true | ||
288 | } | ||
289 | } | ||
290 | "#, | ||
291 | ); | ||
292 | } | ||
293 | } | ||