diff options
Diffstat (limited to 'crates/ra_assists/src')
-rw-r--r-- | crates/ra_assists/src/handlers/early_return.rs | 2 | ||||
-rw-r--r-- | crates/ra_assists/src/handlers/replace_if_let_with_match.rs | 37 | ||||
-rw-r--r-- | crates/ra_assists/src/handlers/unwrap_block.rs | 164 |
3 files changed, 121 insertions, 82 deletions
diff --git a/crates/ra_assists/src/handlers/early_return.rs b/crates/ra_assists/src/handlers/early_return.rs index 4cc75a7ce..dfade7432 100644 --- a/crates/ra_assists/src/handlers/early_return.rs +++ b/crates/ra_assists/src/handlers/early_return.rs | |||
@@ -154,7 +154,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) | |||
154 | parent_block: &ast::BlockExpr, | 154 | parent_block: &ast::BlockExpr, |
155 | if_expr: &ast::IfExpr, | 155 | if_expr: &ast::IfExpr, |
156 | ) -> SyntaxNode { | 156 | ) -> SyntaxNode { |
157 | let then_block_items = then_block.dedent(IndentLevel::from(1)); | 157 | let then_block_items = then_block.dedent(IndentLevel(1)); |
158 | let end_of_then = then_block_items.syntax().last_child_or_token().unwrap(); | 158 | let end_of_then = then_block_items.syntax().last_child_or_token().unwrap(); |
159 | let end_of_then = | 159 | let end_of_then = |
160 | if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) { | 160 | if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) { |
diff --git a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs index e016f51c3..dfcd787de 100644 --- a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs +++ b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs | |||
@@ -51,6 +51,7 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext) | |||
51 | acc.add(AssistId("replace_if_let_with_match"), "Replace with match", target, move |edit| { | 51 | acc.add(AssistId("replace_if_let_with_match"), "Replace with match", target, move |edit| { |
52 | let match_expr = { | 52 | let match_expr = { |
53 | let then_arm = { | 53 | let then_arm = { |
54 | let then_block = then_block.reset_indent().indent(IndentLevel(1)); | ||
54 | let then_expr = unwrap_trivial_block(then_block); | 55 | let then_expr = unwrap_trivial_block(then_block); |
55 | make::match_arm(vec![pat.clone()], then_expr) | 56 | make::match_arm(vec![pat.clone()], then_expr) |
56 | }; | 57 | }; |
@@ -64,8 +65,8 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext) | |||
64 | let else_expr = unwrap_trivial_block(else_block); | 65 | let else_expr = unwrap_trivial_block(else_block); |
65 | make::match_arm(vec![pattern], else_expr) | 66 | make::match_arm(vec![pattern], else_expr) |
66 | }; | 67 | }; |
67 | make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm])) | 68 | let match_expr = make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm])); |
68 | .indent(IndentLevel::from_node(if_expr.syntax())) | 69 | match_expr.indent(IndentLevel::from_node(if_expr.syntax())) |
69 | }; | 70 | }; |
70 | 71 | ||
71 | edit.replace_ast::<ast::Expr>(if_expr.into(), match_expr); | 72 | edit.replace_ast::<ast::Expr>(if_expr.into(), match_expr); |
@@ -213,4 +214,36 @@ fn foo(x: Result<i32, ()>) { | |||
213 | "#, | 214 | "#, |
214 | ); | 215 | ); |
215 | } | 216 | } |
217 | |||
218 | #[test] | ||
219 | fn nested_indent() { | ||
220 | check_assist( | ||
221 | replace_if_let_with_match, | ||
222 | r#" | ||
223 | fn main() { | ||
224 | if true { | ||
225 | <|>if let Ok(rel_path) = path.strip_prefix(root_path) { | ||
226 | let rel_path = RelativePathBuf::from_path(rel_path).ok()?; | ||
227 | Some((*id, rel_path)) | ||
228 | } else { | ||
229 | None | ||
230 | } | ||
231 | } | ||
232 | } | ||
233 | "#, | ||
234 | r#" | ||
235 | fn main() { | ||
236 | if true { | ||
237 | match path.strip_prefix(root_path) { | ||
238 | Ok(rel_path) => { | ||
239 | let rel_path = RelativePathBuf::from_path(rel_path).ok()?; | ||
240 | Some((*id, rel_path)) | ||
241 | } | ||
242 | _ => None, | ||
243 | } | ||
244 | } | ||
245 | } | ||
246 | "#, | ||
247 | ) | ||
248 | } | ||
216 | } | 249 | } |
diff --git a/crates/ra_assists/src/handlers/unwrap_block.rs b/crates/ra_assists/src/handlers/unwrap_block.rs index 8440c7d0f..1fb13f481 100644 --- a/crates/ra_assists/src/handlers/unwrap_block.rs +++ b/crates/ra_assists/src/handlers/unwrap_block.rs | |||
@@ -1,7 +1,10 @@ | |||
1 | use ra_fmt::unwrap_trivial_block; | 1 | use ra_fmt::unwrap_trivial_block; |
2 | use ra_syntax::{ | 2 | use ra_syntax::{ |
3 | ast::{self, ElseBranch, Expr, LoopBodyOwner}, | 3 | ast::{ |
4 | match_ast, AstNode, TextRange, T, | 4 | self, |
5 | edit::{AstNodeEdit, IndentLevel}, | ||
6 | }, | ||
7 | AstNode, TextRange, T, | ||
5 | }; | 8 | }; |
6 | 9 | ||
7 | use crate::{AssistContext, AssistId, Assists}; | 10 | use crate::{AssistContext, AssistId, Assists}; |
@@ -24,94 +27,73 @@ use crate::{AssistContext, AssistId, Assists}; | |||
24 | // } | 27 | // } |
25 | // ``` | 28 | // ``` |
26 | pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 29 | pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
27 | let l_curly_token = ctx.find_token_at_offset(T!['{'])?; | ||
28 | let block = ast::BlockExpr::cast(l_curly_token.parent())?; | ||
29 | let parent = block.syntax().parent()?; | ||
30 | let assist_id = AssistId("unwrap_block"); | 30 | let assist_id = AssistId("unwrap_block"); |
31 | let assist_label = "Unwrap block"; | 31 | let assist_label = "Unwrap block"; |
32 | 32 | ||
33 | let (expr, expr_to_unwrap) = match_ast! { | 33 | let l_curly_token = ctx.find_token_at_offset(T!['{'])?; |
34 | match parent { | 34 | let mut block = ast::BlockExpr::cast(l_curly_token.parent())?; |
35 | ast::ForExpr(for_expr) => { | 35 | let mut parent = block.syntax().parent()?; |
36 | let block_expr = for_expr.loop_body()?; | 36 | if ast::MatchArm::can_cast(parent.kind()) { |
37 | let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?; | 37 | parent = parent.ancestors().find(|it| ast::MatchExpr::can_cast(it.kind()))? |
38 | (ast::Expr::ForExpr(for_expr), expr_to_unwrap) | 38 | } |
39 | }, | ||
40 | ast::WhileExpr(while_expr) => { | ||
41 | let block_expr = while_expr.loop_body()?; | ||
42 | let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?; | ||
43 | (ast::Expr::WhileExpr(while_expr), expr_to_unwrap) | ||
44 | }, | ||
45 | ast::LoopExpr(loop_expr) => { | ||
46 | let block_expr = loop_expr.loop_body()?; | ||
47 | let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?; | ||
48 | (ast::Expr::LoopExpr(loop_expr), expr_to_unwrap) | ||
49 | }, | ||
50 | ast::IfExpr(if_expr) => { | ||
51 | let mut resp = None; | ||
52 | |||
53 | let then_branch = if_expr.then_branch()?; | ||
54 | if then_branch.l_curly_token()?.text_range().contains_range(ctx.frange.range) { | ||
55 | if let Some(ancestor) = if_expr.syntax().parent().and_then(ast::IfExpr::cast) { | ||
56 | // For `else if` blocks | ||
57 | let ancestor_then_branch = ancestor.then_branch()?; | ||
58 | let l_curly_token = then_branch.l_curly_token()?; | ||
59 | |||
60 | let target = then_branch.syntax().text_range(); | ||
61 | return acc.add(assist_id, assist_label, target, |edit| { | ||
62 | let range_to_del_else_if = TextRange::new(ancestor_then_branch.syntax().text_range().end(), l_curly_token.text_range().start()); | ||
63 | let range_to_del_rest = TextRange::new(then_branch.syntax().text_range().end(), if_expr.syntax().text_range().end()); | ||
64 | |||
65 | edit.delete(range_to_del_rest); | ||
66 | edit.delete(range_to_del_else_if); | ||
67 | edit.replace(target, update_expr_string(then_branch.to_string(), &[' ', '{'])); | ||
68 | }); | ||
69 | } else { | ||
70 | resp = Some((ast::Expr::IfExpr(if_expr.clone()), Expr::BlockExpr(then_branch))); | ||
71 | } | ||
72 | } else if let Some(else_branch) = if_expr.else_branch() { | ||
73 | match else_branch { | ||
74 | ElseBranch::Block(else_block) => { | ||
75 | let l_curly_token = else_block.l_curly_token()?; | ||
76 | if l_curly_token.text_range().contains_range(ctx.frange.range) { | ||
77 | let target = else_block.syntax().text_range(); | ||
78 | return acc.add(assist_id, assist_label, target, |edit| { | ||
79 | let range_to_del = TextRange::new(then_branch.syntax().text_range().end(), l_curly_token.text_range().start()); | ||
80 | |||
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 | 39 | ||
90 | resp? | 40 | let parent = ast::Expr::cast(parent)?; |
91 | }, | 41 | |
92 | _ => return None, | 42 | match parent.clone() { |
43 | ast::Expr::ForExpr(_) | ast::Expr::WhileExpr(_) | ast::Expr::LoopExpr(_) => (), | ||
44 | ast::Expr::MatchExpr(_) => block = block.dedent(IndentLevel(1)), | ||
45 | ast::Expr::IfExpr(if_expr) => { | ||
46 | let then_branch = if_expr.then_branch()?; | ||
47 | if then_branch == block { | ||
48 | if let Some(ancestor) = if_expr.syntax().parent().and_then(ast::IfExpr::cast) { | ||
49 | // For `else if` blocks | ||
50 | let ancestor_then_branch = ancestor.then_branch()?; | ||
51 | |||
52 | let target = then_branch.syntax().text_range(); | ||
53 | return acc.add(assist_id, assist_label, target, |edit| { | ||
54 | let range_to_del_else_if = TextRange::new( | ||
55 | ancestor_then_branch.syntax().text_range().end(), | ||
56 | l_curly_token.text_range().start(), | ||
57 | ); | ||
58 | let range_to_del_rest = TextRange::new( | ||
59 | then_branch.syntax().text_range().end(), | ||
60 | if_expr.syntax().text_range().end(), | ||
61 | ); | ||
62 | |||
63 | edit.delete(range_to_del_rest); | ||
64 | edit.delete(range_to_del_else_if); | ||
65 | edit.replace( | ||
66 | target, | ||
67 | update_expr_string(then_branch.to_string(), &[' ', '{']), | ||
68 | ); | ||
69 | }); | ||
70 | } | ||
71 | } else { | ||
72 | let target = block.syntax().text_range(); | ||
73 | return acc.add(assist_id, assist_label, target, |edit| { | ||
74 | let range_to_del = TextRange::new( | ||
75 | then_branch.syntax().text_range().end(), | ||
76 | l_curly_token.text_range().start(), | ||
77 | ); | ||
78 | |||
79 | edit.delete(range_to_del); | ||
80 | edit.replace(target, update_expr_string(block.to_string(), &[' ', '{'])); | ||
81 | }); | ||
82 | } | ||
93 | } | 83 | } |
84 | _ => return None, | ||
94 | }; | 85 | }; |
95 | 86 | ||
96 | let target = expr_to_unwrap.syntax().text_range(); | 87 | let unwrapped = unwrap_trivial_block(block); |
97 | acc.add(assist_id, assist_label, target, |edit| { | 88 | let target = unwrapped.syntax().text_range(); |
98 | edit.replace( | 89 | acc.add(assist_id, assist_label, target, |builder| { |
99 | expr.syntax().text_range(), | 90 | builder.replace( |
100 | update_expr_string(expr_to_unwrap.to_string(), &[' ', '{', '\n']), | 91 | parent.syntax().text_range(), |
92 | update_expr_string(unwrapped.to_string(), &[' ', '{', '\n']), | ||
101 | ); | 93 | ); |
102 | }) | 94 | }) |
103 | } | 95 | } |
104 | 96 | ||
105 | fn extract_expr(cursor_range: TextRange, block: ast::BlockExpr) -> Option<ast::Expr> { | ||
106 | let cursor_in_range = block.l_curly_token()?.text_range().contains_range(cursor_range); | ||
107 | |||
108 | if cursor_in_range { | ||
109 | Some(unwrap_trivial_block(block)) | ||
110 | } else { | ||
111 | None | ||
112 | } | ||
113 | } | ||
114 | |||
115 | fn update_expr_string(expr_str: String, trim_start_pat: &[char]) -> String { | 97 | fn update_expr_string(expr_str: String, trim_start_pat: &[char]) -> String { |
116 | let expr_string = expr_str.trim_start_matches(trim_start_pat); | 98 | let expr_string = expr_str.trim_start_matches(trim_start_pat); |
117 | let mut expr_string_lines: Vec<&str> = expr_string.lines().collect(); | 99 | let mut expr_string_lines: Vec<&str> = expr_string.lines().collect(); |
@@ -490,6 +472,30 @@ mod tests { | |||
490 | } | 472 | } |
491 | 473 | ||
492 | #[test] | 474 | #[test] |
475 | fn unwrap_match_arm() { | ||
476 | check_assist( | ||
477 | unwrap_block, | ||
478 | r#" | ||
479 | fn main() { | ||
480 | match rel_path { | ||
481 | Ok(rel_path) => {<|> | ||
482 | let rel_path = RelativePathBuf::from_path(rel_path).ok()?; | ||
483 | Some((*id, rel_path)) | ||
484 | } | ||
485 | Err(_) => None, | ||
486 | } | ||
487 | } | ||
488 | "#, | ||
489 | r#" | ||
490 | fn main() { | ||
491 | let rel_path = RelativePathBuf::from_path(rel_path).ok()?; | ||
492 | Some((*id, rel_path)) | ||
493 | } | ||
494 | "#, | ||
495 | ); | ||
496 | } | ||
497 | |||
498 | #[test] | ||
493 | fn simple_if_in_while_bad_cursor_position() { | 499 | fn simple_if_in_while_bad_cursor_position() { |
494 | check_assist_not_applicable( | 500 | check_assist_not_applicable( |
495 | unwrap_block, | 501 | unwrap_block, |