aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_assists/src/handlers/move_guard.rs
diff options
context:
space:
mode:
authorChetan Khilosiya <[email protected]>2021-02-22 18:47:48 +0000
committerChetan Khilosiya <[email protected]>2021-02-22 19:29:16 +0000
commite4756cb4f6e66097638b9d101589358976be2ba8 (patch)
treeb6ca0ae6b45b57834476ae0f9985cec3a6bd9090 /crates/ide_assists/src/handlers/move_guard.rs
parent8687053b118f47ce1a4962d0baa19b22d40d2758 (diff)
7526: Rename crate assists to ide_assists.
Diffstat (limited to 'crates/ide_assists/src/handlers/move_guard.rs')
-rw-r--r--crates/ide_assists/src/handlers/move_guard.rs367
1 files changed, 367 insertions, 0 deletions
diff --git a/crates/ide_assists/src/handlers/move_guard.rs b/crates/ide_assists/src/handlers/move_guard.rs
new file mode 100644
index 000000000..3f22302a9
--- /dev/null
+++ b/crates/ide_assists/src/handlers/move_guard.rs
@@ -0,0 +1,367 @@
1use syntax::{
2 ast::{edit::AstNodeEdit, make, AstNode, BlockExpr, Expr, IfExpr, MatchArm},
3 SyntaxKind::WHITESPACE,
4};
5
6use 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 } $0if 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// ```
35pub(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 None,
46 )
47 .indent(arm_expr.indent_level());
48
49 let target = guard.syntax().text_range();
50 acc.add(
51 AssistId("move_guard_to_arm_body", AssistKind::RefactorRewrite),
52 "Move guard to arm body",
53 target,
54 |edit| {
55 match space_before_guard {
56 Some(element) if element.kind() == WHITESPACE => {
57 edit.delete(element.text_range());
58 }
59 _ => (),
60 };
61
62 edit.delete(guard.syntax().text_range());
63 edit.replace_ast(arm_expr, if_expr);
64 },
65 )
66}
67
68// Assist: move_arm_cond_to_match_guard
69//
70// Moves if expression from match arm body into a guard.
71//
72// ```
73// enum Action { Move { distance: u32 }, Stop }
74//
75// fn handle(action: Action) {
76// match action {
77// Action::Move { distance } => $0if distance > 10 { foo() },
78// _ => (),
79// }
80// }
81// ```
82// ->
83// ```
84// enum Action { Move { distance: u32 }, Stop }
85//
86// fn handle(action: Action) {
87// match action {
88// Action::Move { distance } if distance > 10 => foo(),
89// _ => (),
90// }
91// }
92// ```
93pub(crate) fn move_arm_cond_to_match_guard(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
94 let match_arm: MatchArm = ctx.find_node_at_offset::<MatchArm>()?;
95 let match_pat = match_arm.pat()?;
96 let arm_body = match_arm.expr()?;
97
98 let mut replace_node = None;
99 let if_expr: IfExpr = IfExpr::cast(arm_body.syntax().clone()).or_else(|| {
100 let block_expr = BlockExpr::cast(arm_body.syntax().clone())?;
101 if let Expr::IfExpr(e) = block_expr.tail_expr()? {
102 replace_node = Some(block_expr.syntax().clone());
103 Some(e)
104 } else {
105 None
106 }
107 })?;
108 let replace_node = replace_node.unwrap_or_else(|| if_expr.syntax().clone());
109
110 let cond = if_expr.condition()?;
111 let then_block = if_expr.then_branch()?;
112
113 // Not support if with else branch
114 if if_expr.else_branch().is_some() {
115 return None;
116 }
117 // Not support moving if let to arm guard
118 if cond.pat().is_some() {
119 return None;
120 }
121
122 let buf = format!(" if {}", cond.syntax().text());
123
124 acc.add(
125 AssistId("move_arm_cond_to_match_guard", AssistKind::RefactorRewrite),
126 "Move condition to match guard",
127 replace_node.text_range(),
128 |edit| {
129 let then_only_expr = then_block.statements().next().is_none();
130
131 match &then_block.tail_expr() {
132 Some(then_expr) if then_only_expr => {
133 edit.replace(replace_node.text_range(), then_expr.syntax().text())
134 }
135 _ if replace_node != *if_expr.syntax() => {
136 // Dedent because if_expr is in a BlockExpr
137 let replace_with = then_block.dedent(1.into()).syntax().text();
138 edit.replace(replace_node.text_range(), replace_with)
139 }
140 _ => edit.replace(replace_node.text_range(), then_block.syntax().text()),
141 }
142
143 edit.insert(match_pat.syntax().text_range().end(), buf);
144 },
145 )
146}
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151
152 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
153
154 #[test]
155 fn move_guard_to_arm_body_target() {
156 check_assist_target(
157 move_guard_to_arm_body,
158 r#"
159fn main() {
160 match 92 {
161 x $0if x > 10 => false,
162 _ => true
163 }
164}
165"#,
166 r#"if x > 10"#,
167 );
168 }
169
170 #[test]
171 fn move_guard_to_arm_body_works() {
172 check_assist(
173 move_guard_to_arm_body,
174 r#"
175fn main() {
176 match 92 {
177 x $0if x > 10 => false,
178 _ => true
179 }
180}
181"#,
182 r#"
183fn main() {
184 match 92 {
185 x => if x > 10 {
186 false
187 },
188 _ => true
189 }
190}
191"#,
192 );
193 }
194
195 #[test]
196 fn move_guard_to_arm_body_works_complex_match() {
197 check_assist(
198 move_guard_to_arm_body,
199 r#"
200fn main() {
201 match 92 {
202 $0x @ 4 | x @ 5 if x > 5 => true,
203 _ => false
204 }
205}
206"#,
207 r#"
208fn main() {
209 match 92 {
210 x @ 4 | x @ 5 => if x > 5 {
211 true
212 },
213 _ => false
214 }
215}
216"#,
217 );
218 }
219
220 #[test]
221 fn move_arm_cond_to_match_guard_works() {
222 check_assist(
223 move_arm_cond_to_match_guard,
224 r#"
225fn main() {
226 match 92 {
227 x => if x > 10 { $0false },
228 _ => true
229 }
230}
231"#,
232 r#"
233fn main() {
234 match 92 {
235 x if x > 10 => false,
236 _ => true
237 }
238}
239"#,
240 );
241 }
242
243 #[test]
244 fn move_arm_cond_in_block_to_match_guard_works() {
245 check_assist(
246 move_arm_cond_to_match_guard,
247 r#"
248fn main() {
249 match 92 {
250 x => {
251 $0if x > 10 {
252 false
253 }
254 },
255 _ => true
256 }
257}
258"#,
259 r#"
260fn main() {
261 match 92 {
262 x if x > 10 => false,
263 _ => true
264 }
265}
266"#,
267 );
268 }
269
270 #[test]
271 fn move_arm_cond_to_match_guard_if_let_not_works() {
272 check_assist_not_applicable(
273 move_arm_cond_to_match_guard,
274 r#"
275fn main() {
276 match 92 {
277 x => if let 62 = x { $0false },
278 _ => true
279 }
280}
281"#,
282 );
283 }
284
285 #[test]
286 fn move_arm_cond_to_match_guard_if_empty_body_works() {
287 check_assist(
288 move_arm_cond_to_match_guard,
289 r#"
290fn main() {
291 match 92 {
292 x => if x > 10 { $0 },
293 _ => true
294 }
295}
296"#,
297 r#"
298fn main() {
299 match 92 {
300 x if x > 10 => { },
301 _ => true
302 }
303}
304"#,
305 );
306 }
307
308 #[test]
309 fn move_arm_cond_to_match_guard_if_multiline_body_works() {
310 check_assist(
311 move_arm_cond_to_match_guard,
312 r#"
313fn main() {
314 match 92 {
315 x => if x > 10 {
316 92;$0
317 false
318 },
319 _ => true
320 }
321}
322"#,
323 r#"
324fn main() {
325 match 92 {
326 x if x > 10 => {
327 92;
328 false
329 },
330 _ => true
331 }
332}
333"#,
334 );
335 }
336
337 #[test]
338 fn move_arm_cond_in_block_to_match_guard_if_multiline_body_works() {
339 check_assist(
340 move_arm_cond_to_match_guard,
341 r#"
342fn main() {
343 match 92 {
344 x => {
345 if x > 10 {
346 92;$0
347 false
348 }
349 }
350 _ => true
351 }
352}
353"#,
354 r#"
355fn main() {
356 match 92 {
357 x if x > 10 => {
358 92;
359 false
360 }
361 _ => true
362 }
363}
364"#,
365 )
366 }
367}