aboutsummaryrefslogtreecommitdiff
path: root/crates/assists/src/handlers/unwrap_block.rs
diff options
context:
space:
mode:
authorZac Pullar-Strecker <[email protected]>2020-08-24 10:19:53 +0100
committerZac Pullar-Strecker <[email protected]>2020-08-24 10:20:13 +0100
commit7bbca7a1b3f9293d2f5cc5745199bc5f8396f2f0 (patch)
treebdb47765991cb973b2cd5481a088fac636bd326c /crates/assists/src/handlers/unwrap_block.rs
parentca464650eeaca6195891199a93f4f76cf3e7e697 (diff)
parente65d48d1fb3d4d91d9dc1148a7a836ff5c9a3c87 (diff)
Merge remote-tracking branch 'upstream/master' into 503-hover-doc-links
Diffstat (limited to 'crates/assists/src/handlers/unwrap_block.rs')
-rw-r--r--crates/assists/src/handlers/unwrap_block.rs517
1 files changed, 517 insertions, 0 deletions
diff --git a/crates/assists/src/handlers/unwrap_block.rs b/crates/assists/src/handlers/unwrap_block.rs
new file mode 100644
index 000000000..3851aeb3e
--- /dev/null
+++ b/crates/assists/src/handlers/unwrap_block.rs
@@ -0,0 +1,517 @@
1use syntax::{
2 ast::{
3 self,
4 edit::{AstNodeEdit, IndentLevel},
5 },
6 AstNode, TextRange, T,
7};
8
9use crate::{utils::unwrap_trivial_block, AssistContext, AssistId, AssistKind, Assists};
10
11// Assist: unwrap_block
12//
13// This assist removes if...else, for, while and loop control statements to just keep the body.
14//
15// ```
16// fn foo() {
17// if true {<|>
18// println!("foo");
19// }
20// }
21// ```
22// ->
23// ```
24// fn foo() {
25// println!("foo");
26// }
27// ```
28pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
29 let assist_id = AssistId("unwrap_block", AssistKind::RefactorRewrite);
30 let assist_label = "Unwrap block";
31
32 let l_curly_token = ctx.find_token_at_offset(T!['{'])?;
33 let mut block = ast::BlockExpr::cast(l_curly_token.parent())?;
34 let mut parent = block.syntax().parent()?;
35 if ast::MatchArm::can_cast(parent.kind()) {
36 parent = parent.ancestors().find(|it| ast::MatchExpr::can_cast(it.kind()))?
37 }
38
39 let parent = ast::Expr::cast(parent)?;
40
41 match parent.clone() {
42 ast::Expr::ForExpr(_) | ast::Expr::WhileExpr(_) | ast::Expr::LoopExpr(_) => (),
43 ast::Expr::MatchExpr(_) => block = block.dedent(IndentLevel(1)),
44 ast::Expr::IfExpr(if_expr) => {
45 let then_branch = if_expr.then_branch()?;
46 if then_branch == block {
47 if let Some(ancestor) = if_expr.syntax().parent().and_then(ast::IfExpr::cast) {
48 // For `else if` blocks
49 let ancestor_then_branch = ancestor.then_branch()?;
50
51 let target = then_branch.syntax().text_range();
52 return acc.add(assist_id, assist_label, target, |edit| {
53 let range_to_del_else_if = TextRange::new(
54 ancestor_then_branch.syntax().text_range().end(),
55 l_curly_token.text_range().start(),
56 );
57 let range_to_del_rest = TextRange::new(
58 then_branch.syntax().text_range().end(),
59 if_expr.syntax().text_range().end(),
60 );
61
62 edit.delete(range_to_del_rest);
63 edit.delete(range_to_del_else_if);
64 edit.replace(
65 target,
66 update_expr_string(then_branch.to_string(), &[' ', '{']),
67 );
68 });
69 }
70 } else {
71 let target = block.syntax().text_range();
72 return acc.add(assist_id, assist_label, target, |edit| {
73 let range_to_del = TextRange::new(
74 then_branch.syntax().text_range().end(),
75 l_curly_token.text_range().start(),
76 );
77
78 edit.delete(range_to_del);
79 edit.replace(target, update_expr_string(block.to_string(), &[' ', '{']));
80 });
81 }
82 }
83 _ => return None,
84 };
85
86 let unwrapped = unwrap_trivial_block(block);
87 let target = unwrapped.syntax().text_range();
88 acc.add(assist_id, assist_label, target, |builder| {
89 builder.replace(
90 parent.syntax().text_range(),
91 update_expr_string(unwrapped.to_string(), &[' ', '{', '\n']),
92 );
93 })
94}
95
96fn update_expr_string(expr_str: String, trim_start_pat: &[char]) -> String {
97 let expr_string = expr_str.trim_start_matches(trim_start_pat);
98 let mut expr_string_lines: Vec<&str> = expr_string.lines().collect();
99 expr_string_lines.pop(); // Delete last line
100
101 expr_string_lines
102 .into_iter()
103 .map(|line| line.replacen(" ", "", 1)) // Delete indentation
104 .collect::<Vec<String>>()
105 .join("\n")
106}
107
108#[cfg(test)]
109mod tests {
110 use crate::tests::{check_assist, check_assist_not_applicable};
111
112 use super::*;
113
114 #[test]
115 fn simple_if() {
116 check_assist(
117 unwrap_block,
118 r#"
119 fn main() {
120 bar();
121 if true {<|>
122 foo();
123
124 //comment
125 bar();
126 } else {
127 println!("bar");
128 }
129 }
130 "#,
131 r#"
132 fn main() {
133 bar();
134 foo();
135
136 //comment
137 bar();
138 }
139 "#,
140 );
141 }
142
143 #[test]
144 fn simple_if_else() {
145 check_assist(
146 unwrap_block,
147 r#"
148 fn main() {
149 bar();
150 if true {
151 foo();
152
153 //comment
154 bar();
155 } else {<|>
156 println!("bar");
157 }
158 }
159 "#,
160 r#"
161 fn main() {
162 bar();
163 if true {
164 foo();
165
166 //comment
167 bar();
168 }
169 println!("bar");
170 }
171 "#,
172 );
173 }
174
175 #[test]
176 fn simple_if_else_if() {
177 check_assist(
178 unwrap_block,
179 r#"
180 fn main() {
181 //bar();
182 if true {
183 println!("true");
184
185 //comment
186 //bar();
187 } else if false {<|>
188 println!("bar");
189 } else {
190 println!("foo");
191 }
192 }
193 "#,
194 r#"
195 fn main() {
196 //bar();
197 if true {
198 println!("true");
199
200 //comment
201 //bar();
202 }
203 println!("bar");
204 }
205 "#,
206 );
207 }
208
209 #[test]
210 fn simple_if_else_if_nested() {
211 check_assist(
212 unwrap_block,
213 r#"
214 fn main() {
215 //bar();
216 if true {
217 println!("true");
218
219 //comment
220 //bar();
221 } else if false {
222 println!("bar");
223 } else if true {<|>
224 println!("foo");
225 }
226 }
227 "#,
228 r#"
229 fn main() {
230 //bar();
231 if true {
232 println!("true");
233
234 //comment
235 //bar();
236 } else if false {
237 println!("bar");
238 }
239 println!("foo");
240 }
241 "#,
242 );
243 }
244
245 #[test]
246 fn simple_if_else_if_nested_else() {
247 check_assist(
248 unwrap_block,
249 r#"
250 fn main() {
251 //bar();
252 if true {
253 println!("true");
254
255 //comment
256 //bar();
257 } else if false {
258 println!("bar");
259 } else if true {
260 println!("foo");
261 } else {<|>
262 println!("else");
263 }
264 }
265 "#,
266 r#"
267 fn main() {
268 //bar();
269 if true {
270 println!("true");
271
272 //comment
273 //bar();
274 } else if false {
275 println!("bar");
276 } else if true {
277 println!("foo");
278 }
279 println!("else");
280 }
281 "#,
282 );
283 }
284
285 #[test]
286 fn simple_if_else_if_nested_middle() {
287 check_assist(
288 unwrap_block,
289 r#"
290 fn main() {
291 //bar();
292 if true {
293 println!("true");
294
295 //comment
296 //bar();
297 } else if false {
298 println!("bar");
299 } else if true {<|>
300 println!("foo");
301 } else {
302 println!("else");
303 }
304 }
305 "#,
306 r#"
307 fn main() {
308 //bar();
309 if true {
310 println!("true");
311
312 //comment
313 //bar();
314 } else if false {
315 println!("bar");
316 }
317 println!("foo");
318 }
319 "#,
320 );
321 }
322
323 #[test]
324 fn simple_if_bad_cursor_position() {
325 check_assist_not_applicable(
326 unwrap_block,
327 r#"
328 fn main() {
329 bar();<|>
330 if true {
331 foo();
332
333 //comment
334 bar();
335 } else {
336 println!("bar");
337 }
338 }
339 "#,
340 );
341 }
342
343 #[test]
344 fn simple_for() {
345 check_assist(
346 unwrap_block,
347 r#"
348 fn main() {
349 for i in 0..5 {<|>
350 if true {
351 foo();
352
353 //comment
354 bar();
355 } else {
356 println!("bar");
357 }
358 }
359 }
360 "#,
361 r#"
362 fn main() {
363 if true {
364 foo();
365
366 //comment
367 bar();
368 } else {
369 println!("bar");
370 }
371 }
372 "#,
373 );
374 }
375
376 #[test]
377 fn simple_if_in_for() {
378 check_assist(
379 unwrap_block,
380 r#"
381 fn main() {
382 for i in 0..5 {
383 if true {<|>
384 foo();
385
386 //comment
387 bar();
388 } else {
389 println!("bar");
390 }
391 }
392 }
393 "#,
394 r#"
395 fn main() {
396 for i in 0..5 {
397 foo();
398
399 //comment
400 bar();
401 }
402 }
403 "#,
404 );
405 }
406
407 #[test]
408 fn simple_loop() {
409 check_assist(
410 unwrap_block,
411 r#"
412 fn main() {
413 loop {<|>
414 if true {
415 foo();
416
417 //comment
418 bar();
419 } else {
420 println!("bar");
421 }
422 }
423 }
424 "#,
425 r#"
426 fn main() {
427 if true {
428 foo();
429
430 //comment
431 bar();
432 } else {
433 println!("bar");
434 }
435 }
436 "#,
437 );
438 }
439
440 #[test]
441 fn simple_while() {
442 check_assist(
443 unwrap_block,
444 r#"
445 fn main() {
446 while true {<|>
447 if true {
448 foo();
449
450 //comment
451 bar();
452 } else {
453 println!("bar");
454 }
455 }
456 }
457 "#,
458 r#"
459 fn main() {
460 if true {
461 foo();
462
463 //comment
464 bar();
465 } else {
466 println!("bar");
467 }
468 }
469 "#,
470 );
471 }
472
473 #[test]
474 fn unwrap_match_arm() {
475 check_assist(
476 unwrap_block,
477 r#"
478fn main() {
479 match rel_path {
480 Ok(rel_path) => {<|>
481 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
482 Some((*id, rel_path))
483 }
484 Err(_) => None,
485 }
486}
487"#,
488 r#"
489fn main() {
490 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
491 Some((*id, rel_path))
492}
493"#,
494 );
495 }
496
497 #[test]
498 fn simple_if_in_while_bad_cursor_position() {
499 check_assist_not_applicable(
500 unwrap_block,
501 r#"
502 fn main() {
503 while true {
504 if true {
505 foo();<|>
506
507 //comment
508 bar();
509 } else {
510 println!("bar");
511 }
512 }
513 }
514 "#,
515 );
516 }
517}