]> git.lizzy.rs Git - rust.git/blob - crates/assists/src/handlers/unwrap_block.rs
Kill RAW_ literals
[rust.git] / crates / assists / src / handlers / unwrap_block.rs
1 use syntax::{
2     ast::{
3         self,
4         edit::{AstNodeEdit, IndentLevel},
5     },
6     AstNode, TextRange, T,
7 };
8
9 use 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 // ```
28 pub(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_syntax_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
96 fn 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)]
109 mod 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#"
478 fn 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#"
489 fn 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 }