aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src/handlers/early_return.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists/src/handlers/early_return.rs')
-rw-r--r--crates/ra_assists/src/handlers/early_return.rs168
1 files changed, 83 insertions, 85 deletions
diff --git a/crates/ra_assists/src/handlers/early_return.rs b/crates/ra_assists/src/handlers/early_return.rs
index 4bd6040b2..66b296081 100644
--- a/crates/ra_assists/src/handlers/early_return.rs
+++ b/crates/ra_assists/src/handlers/early_return.rs
@@ -2,14 +2,18 @@ use std::{iter::once, ops::RangeInclusive};
2 2
3use ra_syntax::{ 3use ra_syntax::{
4 algo::replace_children, 4 algo::replace_children,
5 ast::{self, edit::IndentLevel, make}, 5 ast::{
6 self,
7 edit::{AstNodeEdit, IndentLevel},
8 make,
9 },
6 AstNode, 10 AstNode,
7 SyntaxKind::{FN_DEF, LOOP_EXPR, L_CURLY, R_CURLY, WHILE_EXPR, WHITESPACE}, 11 SyntaxKind::{FN_DEF, LOOP_EXPR, L_CURLY, R_CURLY, WHILE_EXPR, WHITESPACE},
8 SyntaxNode, 12 SyntaxNode,
9}; 13};
10 14
11use crate::{ 15use crate::{
12 assist_ctx::{Assist, AssistCtx}, 16 assist_context::{AssistContext, Assists},
13 utils::invert_boolean_expression, 17 utils::invert_boolean_expression,
14 AssistId, 18 AssistId,
15}; 19};
@@ -36,7 +40,7 @@ use crate::{
36// bar(); 40// bar();
37// } 41// }
38// ``` 42// ```
39pub(crate) fn convert_to_guarded_return(ctx: AssistCtx) -> Option<Assist> { 43pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
40 let if_expr: ast::IfExpr = ctx.find_node_at_offset()?; 44 let if_expr: ast::IfExpr = ctx.find_node_at_offset()?;
41 if if_expr.else_branch().is_some() { 45 if if_expr.else_branch().is_some() {
42 return None; 46 return None;
@@ -93,96 +97,90 @@ pub(crate) fn convert_to_guarded_return(ctx: AssistCtx) -> Option<Assist> {
93 } 97 }
94 98
95 then_block.syntax().last_child_or_token().filter(|t| t.kind() == R_CURLY)?; 99 then_block.syntax().last_child_or_token().filter(|t| t.kind() == R_CURLY)?;
96 let cursor_position = ctx.frange.range.start(); 100 let cursor_position = ctx.offset();
97 101
98 let target = if_expr.syntax().text_range(); 102 let target = if_expr.syntax().text_range();
99 ctx.add_assist( 103 acc.add(AssistId("convert_to_guarded_return"), "Convert to guarded return", target, |edit| {
100 AssistId("convert_to_guarded_return"), 104 let if_indent_level = IndentLevel::from_node(&if_expr.syntax());
101 "Convert to guarded return", 105 let new_block = match if_let_pat {
102 target, 106 None => {
103 |edit| { 107 // If.
104 let if_indent_level = IndentLevel::from_node(&if_expr.syntax()); 108 let new_expr = {
105 let new_block = match if_let_pat { 109 let then_branch =
106 None => { 110 make::block_expr(once(make::expr_stmt(early_expression).into()), None);
107 // If. 111 let cond = invert_boolean_expression(cond_expr);
108 let new_expr = { 112 make::expr_if(make::condition(cond, None), then_branch).indent(if_indent_level)
109 let then_branch = 113 };
110 make::block_expr(once(make::expr_stmt(early_expression).into()), None); 114 replace(new_expr.syntax(), &then_block, &parent_block, &if_expr)
111 let cond = invert_boolean_expression(cond_expr); 115 }
112 let e = make::expr_if(make::condition(cond, None), then_branch); 116 Some((path, bound_ident)) => {
113 if_indent_level.increase_indent(e) 117 // If-let.
114 }; 118 let match_expr = {
115 replace(new_expr.syntax(), &then_block, &parent_block, &if_expr) 119 let happy_arm = {
116 } 120 let pat = make::tuple_struct_pat(
117 Some((path, bound_ident)) => { 121 path,
118 // If-let. 122 once(make::bind_pat(make::name("it")).into()),
119 let match_expr = {
120 let happy_arm = {
121 let pat = make::tuple_struct_pat(
122 path,
123 once(make::bind_pat(make::name("it")).into()),
124 );
125 let expr = {
126 let name_ref = make::name_ref("it");
127 let segment = make::path_segment(name_ref);
128 let path = make::path_unqualified(segment);
129 make::expr_path(path)
130 };
131 make::match_arm(once(pat.into()), expr)
132 };
133
134 let sad_arm = make::match_arm(
135 // FIXME: would be cool to use `None` or `Err(_)` if appropriate
136 once(make::placeholder_pat().into()),
137 early_expression,
138 ); 123 );
139 124 let expr = {
140 make::expr_match(cond_expr, make::match_arm_list(vec![happy_arm, sad_arm])) 125 let name_ref = make::name_ref("it");
126 let segment = make::path_segment(name_ref);
127 let path = make::path_unqualified(segment);
128 make::expr_path(path)
129 };
130 make::match_arm(once(pat.into()), expr)
141 }; 131 };
142 132
143 let let_stmt = make::let_stmt( 133 let sad_arm = make::match_arm(
144 make::bind_pat(make::name(&bound_ident.syntax().to_string())).into(), 134 // FIXME: would be cool to use `None` or `Err(_)` if appropriate
145 Some(match_expr), 135 once(make::placeholder_pat().into()),
136 early_expression,
146 ); 137 );
147 let let_stmt = if_indent_level.increase_indent(let_stmt); 138
148 replace(let_stmt.syntax(), &then_block, &parent_block, &if_expr) 139 make::expr_match(cond_expr, make::match_arm_list(vec![happy_arm, sad_arm]))
149 } 140 };
150 }; 141
151 edit.replace_ast(parent_block, ast::BlockExpr::cast(new_block).unwrap()); 142 let let_stmt = make::let_stmt(
152 edit.set_cursor(cursor_position); 143 make::bind_pat(make::name(&bound_ident.syntax().to_string())).into(),
153 144 Some(match_expr),
154 fn replace(
155 new_expr: &SyntaxNode,
156 then_block: &ast::BlockExpr,
157 parent_block: &ast::BlockExpr,
158 if_expr: &ast::IfExpr,
159 ) -> SyntaxNode {
160 let then_block_items = IndentLevel::from(1).decrease_indent(then_block.clone());
161 let end_of_then = then_block_items.syntax().last_child_or_token().unwrap();
162 let end_of_then =
163 if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) {
164 end_of_then.prev_sibling_or_token().unwrap()
165 } else {
166 end_of_then
167 };
168 let mut then_statements = new_expr.children_with_tokens().chain(
169 then_block_items
170 .syntax()
171 .children_with_tokens()
172 .skip(1)
173 .take_while(|i| *i != end_of_then),
174 ); 145 );
175 replace_children( 146 let let_stmt = let_stmt.indent(if_indent_level);
176 &parent_block.syntax(), 147 replace(let_stmt.syntax(), &then_block, &parent_block, &if_expr)
177 RangeInclusive::new(
178 if_expr.clone().syntax().clone().into(),
179 if_expr.syntax().clone().into(),
180 ),
181 &mut then_statements,
182 )
183 } 148 }
184 }, 149 };
185 ) 150 edit.replace_ast(parent_block, ast::BlockExpr::cast(new_block).unwrap());
151 edit.set_cursor(cursor_position);
152
153 fn replace(
154 new_expr: &SyntaxNode,
155 then_block: &ast::BlockExpr,
156 parent_block: &ast::BlockExpr,
157 if_expr: &ast::IfExpr,
158 ) -> SyntaxNode {
159 let then_block_items = then_block.dedent(IndentLevel::from(1));
160 let end_of_then = then_block_items.syntax().last_child_or_token().unwrap();
161 let end_of_then =
162 if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) {
163 end_of_then.prev_sibling_or_token().unwrap()
164 } else {
165 end_of_then
166 };
167 let mut then_statements = new_expr.children_with_tokens().chain(
168 then_block_items
169 .syntax()
170 .children_with_tokens()
171 .skip(1)
172 .take_while(|i| *i != end_of_then),
173 );
174 replace_children(
175 &parent_block.syntax(),
176 RangeInclusive::new(
177 if_expr.clone().syntax().clone().into(),
178 if_expr.syntax().clone().into(),
179 ),
180 &mut then_statements,
181 )
182 }
183 })
186} 184}
187 185
188#[cfg(test)] 186#[cfg(test)]