aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src
diff options
context:
space:
mode:
authorBenjamin Coenen <[email protected]>2020-05-10 14:31:51 +0100
committerBenjamin Coenen <[email protected]>2020-05-10 14:31:51 +0100
commit98a7bb24358c0f1e9353195d6933f5973c8edaba (patch)
treed75767b7caf8d1e61e24cc9602d7914933f9a08a /crates/ra_assists/src
parent4578154b608fa075595103d0c933da60d55b25c8 (diff)
do not remove then block when you unwrap else block #4361
Signed-off-by: Benjamin Coenen <[email protected]>
Diffstat (limited to 'crates/ra_assists/src')
-rw-r--r--crates/ra_assists/src/handlers/unwrap_block.rs221
1 files changed, 193 insertions, 28 deletions
diff --git a/crates/ra_assists/src/handlers/unwrap_block.rs b/crates/ra_assists/src/handlers/unwrap_block.rs
index eba0631a4..e52ec557e 100644
--- a/crates/ra_assists/src/handlers/unwrap_block.rs
+++ b/crates/ra_assists/src/handlers/unwrap_block.rs
@@ -1,6 +1,6 @@
1use crate::{AssistContext, AssistId, Assists}; 1use crate::{AssistContext, AssistId, Assists};
2 2
3use ast::LoopBodyOwner; 3use ast::{ElseBranch, Expr, LoopBodyOwner};
4use ra_fmt::unwrap_trivial_block; 4use ra_fmt::unwrap_trivial_block;
5use ra_syntax::{ast, match_ast, AstNode, TextRange, T}; 5use ra_syntax::{ast, match_ast, AstNode, TextRange, T};
6 6
@@ -25,19 +25,11 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
25 let l_curly_token = ctx.find_token_at_offset(T!['{'])?; 25 let l_curly_token = ctx.find_token_at_offset(T!['{'])?;
26 let block = ast::BlockExpr::cast(l_curly_token.parent())?; 26 let block = ast::BlockExpr::cast(l_curly_token.parent())?;
27 let parent = block.syntax().parent()?; 27 let parent = block.syntax().parent()?;
28 let assist_id = AssistId("unwrap_block");
29 let assist_label = "Unwrap block";
30
28 let (expr, expr_to_unwrap) = match_ast! { 31 let (expr, expr_to_unwrap) = match_ast! {
29 match parent { 32 match parent {
30 ast::IfExpr(if_expr) => {
31 let expr_to_unwrap = if_expr.blocks().find_map(|expr| extract_expr(ctx.frange.range, expr));
32 let expr_to_unwrap = expr_to_unwrap?;
33 // Find if we are in a else if block
34 let ancestor = if_expr.syntax().parent().and_then(ast::IfExpr::cast);
35
36 match ancestor {
37 None => (ast::Expr::IfExpr(if_expr), expr_to_unwrap),
38 Some(ancestor) => (ast::Expr::IfExpr(ancestor), expr_to_unwrap),
39 }
40 },
41 ast::ForExpr(for_expr) => { 33 ast::ForExpr(for_expr) => {
42 let block_expr = for_expr.loop_body()?; 34 let block_expr = for_expr.loop_body()?;
43 let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?; 35 let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?;
@@ -53,27 +45,62 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
53 let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?; 45 let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?;
54 (ast::Expr::LoopExpr(loop_expr), expr_to_unwrap) 46 (ast::Expr::LoopExpr(loop_expr), expr_to_unwrap)
55 }, 47 },
48 ast::IfExpr(if_expr) => {
49 let mut resp = None;
50
51 let then_branch = if_expr.then_branch()?;
52 if then_branch.l_curly_token()?.text_range().contains_range(ctx.frange.range) {
53 if let Some(ancestor) = if_expr.syntax().parent().and_then(ast::IfExpr::cast) {
54 // For `else if` blocks
55 let ancestor_then_branch = ancestor.then_branch()?;
56 let l_curly_token = then_branch.l_curly_token()?;
57
58 let target = then_branch.syntax().text_range();
59 return acc.add(assist_id, assist_label, target, |edit| {
60 let range_to_del_else_if = TextRange::new(ancestor_then_branch.syntax().text_range().end(), l_curly_token.text_range().start());
61 let range_to_del_rest = TextRange::new(then_branch.syntax().text_range().end(), if_expr.syntax().text_range().end());
62
63 edit.set_cursor(ancestor_then_branch.syntax().text_range().end());
64 edit.delete(range_to_del_rest);
65 edit.delete(range_to_del_else_if);
66 edit.replace(target, update_expr_string(then_branch.to_string(), &[' ', '{']));
67 });
68 } else {
69 resp = Some((ast::Expr::IfExpr(if_expr.clone()), Expr::BlockExpr(then_branch)));
70 }
71 } else if let Some(else_branch) = if_expr.else_branch() {
72 match else_branch {
73 ElseBranch::Block(else_block) => {
74 let l_curly_token = else_block.l_curly_token()?;
75 if l_curly_token.text_range().contains_range(ctx.frange.range) {
76 let target = else_block.syntax().text_range();
77 return acc.add(assist_id, assist_label, target, |edit| {
78 let range_to_del = TextRange::new(then_branch.syntax().text_range().end(), l_curly_token.text_range().start());
79
80 edit.set_cursor(then_branch.syntax().text_range().end());
81 edit.delete(range_to_del);
82 edit.replace(target, update_expr_string(else_block.to_string(), &[' ', '{']));
83 });
84 }
85 },
86 ElseBranch::IfExpr(_) => {},
87 }
88 }
89
90 resp?
91 },
56 _ => return None, 92 _ => return None,
57 } 93 }
58 }; 94 };
59 95
60 let target = expr_to_unwrap.syntax().text_range(); 96 let target = expr_to_unwrap.syntax().text_range();
61 acc.add(AssistId("unwrap_block"), "Unwrap block", target, |edit| { 97 acc.add(assist_id, assist_label, target, |edit| {
62 edit.set_cursor(expr.syntax().text_range().start()); 98 edit.set_cursor(expr.syntax().text_range().start());
63 99
64 let pat_start: &[_] = &[' ', '{', '\n']; 100 edit.replace(
65 let expr_to_unwrap = expr_to_unwrap.to_string(); 101 expr.syntax().text_range(),
66 let expr_string = expr_to_unwrap.trim_start_matches(pat_start); 102 update_expr_string(expr_to_unwrap.to_string(), &[' ', '{', '\n']),
67 let mut expr_string_lines: Vec<&str> = expr_string.lines().collect(); 103 );
68 expr_string_lines.pop(); // Delete last line
69
70 let expr_string = expr_string_lines
71 .into_iter()
72 .map(|line| line.replacen(" ", "", 1)) // Delete indentation
73 .collect::<Vec<String>>()
74 .join("\n");
75
76 edit.replace(expr.syntax().text_range(), expr_string);
77 }) 104 })
78} 105}
79 106
@@ -87,6 +114,18 @@ fn extract_expr(cursor_range: TextRange, block: ast::BlockExpr) -> Option<ast::E
87 } 114 }
88} 115}
89 116
117fn update_expr_string(expr_str: String, trim_start_pat: &[char]) -> String {
118 let expr_string = expr_str.trim_start_matches(trim_start_pat);
119 let mut expr_string_lines: Vec<&str> = expr_string.lines().collect();
120 expr_string_lines.pop(); // Delete last line
121
122 expr_string_lines
123 .into_iter()
124 .map(|line| line.replacen(" ", "", 1)) // Delete indentation
125 .collect::<Vec<String>>()
126 .join("\n")
127}
128
90#[cfg(test)] 129#[cfg(test)]
91mod tests { 130mod tests {
92 use crate::tests::{check_assist, check_assist_not_applicable}; 131 use crate::tests::{check_assist, check_assist_not_applicable};
@@ -142,7 +181,13 @@ mod tests {
142 r#" 181 r#"
143 fn main() { 182 fn main() {
144 bar(); 183 bar();
145 <|>println!("bar"); 184 if true {
185 foo();
186
187 //comment
188 bar();
189 }<|>
190 println!("bar");
146 } 191 }
147 "#, 192 "#,
148 ); 193 );
@@ -170,7 +215,127 @@ mod tests {
170 r#" 215 r#"
171 fn main() { 216 fn main() {
172 //bar(); 217 //bar();
173 <|>println!("bar"); 218 if true {
219 println!("true");
220
221 //comment
222 //bar();
223 }<|>
224 println!("bar");
225 }
226 "#,
227 );
228 }
229
230 #[test]
231 fn simple_if_else_if_nested() {
232 check_assist(
233 unwrap_block,
234 r#"
235 fn main() {
236 //bar();
237 if true {
238 println!("true");
239
240 //comment
241 //bar();
242 } else if false {
243 println!("bar");
244 } else if true {<|>
245 println!("foo");
246 }
247 }
248 "#,
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 }<|>
260 println!("foo");
261 }
262 "#,
263 );
264 }
265
266 #[test]
267 fn simple_if_else_if_nested_else() {
268 check_assist(
269 unwrap_block,
270 r#"
271 fn main() {
272 //bar();
273 if true {
274 println!("true");
275
276 //comment
277 //bar();
278 } else if false {
279 println!("bar");
280 } else if true {
281 println!("foo");
282 } else {<|>
283 println!("else");
284 }
285 }
286 "#,
287 r#"
288 fn main() {
289 //bar();
290 if true {
291 println!("true");
292
293 //comment
294 //bar();
295 } else if false {
296 println!("bar");
297 } else if true {
298 println!("foo");
299 }<|>
300 println!("else");
301 }
302 "#,
303 );
304 }
305
306 #[test]
307 fn simple_if_else_if_nested_middle() {
308 check_assist(
309 unwrap_block,
310 r#"
311 fn main() {
312 //bar();
313 if true {
314 println!("true");
315
316 //comment
317 //bar();
318 } else if false {
319 println!("bar");
320 } else if true {<|>
321 println!("foo");
322 } else {
323 println!("else");
324 }
325 }
326 "#,
327 r#"
328 fn main() {
329 //bar();
330 if true {
331 println!("true");
332
333 //comment
334 //bar();
335 } else if false {
336 println!("bar");
337 }<|>
338 println!("foo");
174 } 339 }
175 "#, 340 "#,
176 ); 341 );