diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_assists/src/assists/early_return.rs | 183 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/make.rs | 16 |
2 files changed, 171 insertions, 28 deletions
diff --git a/crates/ra_assists/src/assists/early_return.rs b/crates/ra_assists/src/assists/early_return.rs index ad6c5695a..827170f8f 100644 --- a/crates/ra_assists/src/assists/early_return.rs +++ b/crates/ra_assists/src/assists/early_return.rs | |||
@@ -37,7 +37,9 @@ use crate::{ | |||
37 | // ``` | 37 | // ``` |
38 | pub(crate) fn convert_to_guarded_return(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | 38 | pub(crate) fn convert_to_guarded_return(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { |
39 | let if_expr: ast::IfExpr = ctx.find_node_at_offset()?; | 39 | let if_expr: ast::IfExpr = ctx.find_node_at_offset()?; |
40 | let expr = if_expr.condition()?.expr()?; | 40 | let cond = if_expr.condition()?; |
41 | let pat = &cond.pat(); | ||
42 | let expr = cond.expr()?; | ||
41 | let then_block = if_expr.then_branch()?.block()?; | 43 | let then_block = if_expr.then_branch()?.block()?; |
42 | if if_expr.else_branch().is_some() { | 44 | if if_expr.else_branch().is_some() { |
43 | return None; | 45 | return None; |
@@ -63,8 +65,8 @@ pub(crate) fn convert_to_guarded_return(ctx: AssistCtx<impl HirDatabase>) -> Opt | |||
63 | let parent_container = parent_block.syntax().parent()?.parent()?; | 65 | let parent_container = parent_block.syntax().parent()?.parent()?; |
64 | 66 | ||
65 | let early_expression = match parent_container.kind() { | 67 | let early_expression = match parent_container.kind() { |
66 | WHILE_EXPR | LOOP_EXPR => Some("continue;"), | 68 | WHILE_EXPR | LOOP_EXPR => Some("continue"), |
67 | FN_DEF => Some("return;"), | 69 | FN_DEF => Some("return"), |
68 | _ => None, | 70 | _ => None, |
69 | }?; | 71 | }?; |
70 | 72 | ||
@@ -77,31 +79,67 @@ pub(crate) fn convert_to_guarded_return(ctx: AssistCtx<impl HirDatabase>) -> Opt | |||
77 | 79 | ||
78 | ctx.add_assist(AssistId("convert_to_guarded_return"), "convert to guarded return", |edit| { | 80 | ctx.add_assist(AssistId("convert_to_guarded_return"), "convert to guarded return", |edit| { |
79 | let if_indent_level = IndentLevel::from_node(&if_expr.syntax()); | 81 | let if_indent_level = IndentLevel::from_node(&if_expr.syntax()); |
80 | let new_if_expr = | 82 | let new_block = match pat { |
81 | if_indent_level.increase_indent(make::if_expression(&expr, early_expression)); | 83 | None => { |
82 | let then_block_items = IndentLevel::from(1).decrease_indent(then_block.clone()); | 84 | // If. |
83 | let end_of_then = then_block_items.syntax().last_child_or_token().unwrap(); | 85 | let early_expression = &(early_expression.to_owned() + ";"); |
84 | let end_of_then = | 86 | let new_if_expr = |
85 | if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) { | 87 | if_indent_level.increase_indent(make::if_expression(&expr, early_expression)); |
86 | end_of_then.prev_sibling_or_token().unwrap() | 88 | let then_block_items = IndentLevel::from(1).decrease_indent(then_block.clone()); |
87 | } else { | 89 | let end_of_then = then_block_items.syntax().last_child_or_token().unwrap(); |
88 | end_of_then | 90 | let end_of_then = |
89 | }; | 91 | if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) { |
90 | let mut new_if_and_then_statements = new_if_expr.syntax().children_with_tokens().chain( | 92 | end_of_then.prev_sibling_or_token().unwrap() |
91 | then_block_items | 93 | } else { |
92 | .syntax() | 94 | end_of_then |
93 | .children_with_tokens() | 95 | }; |
94 | .skip(1) | 96 | let mut new_if_and_then_statements = |
95 | .take_while(|i| *i != end_of_then), | 97 | new_if_expr.syntax().children_with_tokens().chain( |
96 | ); | 98 | then_block_items |
97 | let new_block = replace_children( | 99 | .syntax() |
98 | &parent_block.syntax(), | 100 | .children_with_tokens() |
99 | RangeInclusive::new( | 101 | .skip(1) |
100 | if_expr.clone().syntax().clone().into(), | 102 | .take_while(|i| *i != end_of_then), |
101 | if_expr.syntax().clone().into(), | 103 | ); |
102 | ), | 104 | replace_children( |
103 | &mut new_if_and_then_statements, | 105 | &parent_block.syntax(), |
104 | ); | 106 | RangeInclusive::new( |
107 | if_expr.clone().syntax().clone().into(), | ||
108 | if_expr.syntax().clone().into(), | ||
109 | ), | ||
110 | &mut new_if_and_then_statements, | ||
111 | ) | ||
112 | } | ||
113 | _ => { | ||
114 | // If-let. | ||
115 | let new_match_expr = | ||
116 | if_indent_level.increase_indent(make::let_match_early(expr, early_expression)); | ||
117 | let then_block_items = IndentLevel::from(1).decrease_indent(then_block.clone()); | ||
118 | let end_of_then = then_block_items.syntax().last_child_or_token().unwrap(); | ||
119 | let end_of_then = | ||
120 | if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) { | ||
121 | end_of_then.prev_sibling_or_token().unwrap() | ||
122 | } else { | ||
123 | end_of_then | ||
124 | }; | ||
125 | let mut then_statements = new_match_expr.syntax().children_with_tokens().chain( | ||
126 | then_block_items | ||
127 | .syntax() | ||
128 | .children_with_tokens() | ||
129 | .skip(1) | ||
130 | .take_while(|i| *i != end_of_then), | ||
131 | ); | ||
132 | let new_block = replace_children( | ||
133 | &parent_block.syntax(), | ||
134 | RangeInclusive::new( | ||
135 | if_expr.clone().syntax().clone().into(), | ||
136 | if_expr.syntax().clone().into(), | ||
137 | ), | ||
138 | &mut then_statements, | ||
139 | ); | ||
140 | new_block | ||
141 | } | ||
142 | }; | ||
105 | edit.target(if_expr.syntax().text_range()); | 143 | edit.target(if_expr.syntax().text_range()); |
106 | edit.replace_ast(parent_block, ast::Block::cast(new_block).unwrap()); | 144 | edit.replace_ast(parent_block, ast::Block::cast(new_block).unwrap()); |
107 | edit.set_cursor(cursor_position); | 145 | edit.set_cursor(cursor_position); |
@@ -144,6 +182,37 @@ mod tests { | |||
144 | } | 182 | } |
145 | 183 | ||
146 | #[test] | 184 | #[test] |
185 | fn convert_let_inside_fn() { | ||
186 | check_assist( | ||
187 | convert_to_guarded_return, | ||
188 | r#" | ||
189 | fn main(n: Option<String>) { | ||
190 | bar(); | ||
191 | if<|> let Some(n) = n { | ||
192 | foo(n); | ||
193 | |||
194 | //comment | ||
195 | bar(); | ||
196 | } | ||
197 | } | ||
198 | "#, | ||
199 | r#" | ||
200 | fn main(n: Option<String>) { | ||
201 | bar(); | ||
202 | le<|>t n = match n { | ||
203 | Some(it) => it, | ||
204 | None => return, | ||
205 | }; | ||
206 | foo(n); | ||
207 | |||
208 | //comment | ||
209 | bar(); | ||
210 | } | ||
211 | "#, | ||
212 | ); | ||
213 | } | ||
214 | |||
215 | #[test] | ||
147 | fn convert_inside_while() { | 216 | fn convert_inside_while() { |
148 | check_assist( | 217 | check_assist( |
149 | convert_to_guarded_return, | 218 | convert_to_guarded_return, |
@@ -172,6 +241,35 @@ mod tests { | |||
172 | } | 241 | } |
173 | 242 | ||
174 | #[test] | 243 | #[test] |
244 | fn convert_let_inside_while() { | ||
245 | check_assist( | ||
246 | convert_to_guarded_return, | ||
247 | r#" | ||
248 | fn main() { | ||
249 | while true { | ||
250 | if<|> let Some(n) = n { | ||
251 | foo(n); | ||
252 | bar(); | ||
253 | } | ||
254 | } | ||
255 | } | ||
256 | "#, | ||
257 | r#" | ||
258 | fn main() { | ||
259 | while true { | ||
260 | le<|>t n = match n { | ||
261 | Some(it) => it, | ||
262 | None => continue, | ||
263 | }; | ||
264 | foo(n); | ||
265 | bar(); | ||
266 | } | ||
267 | } | ||
268 | "#, | ||
269 | ); | ||
270 | } | ||
271 | |||
272 | #[test] | ||
175 | fn convert_inside_loop() { | 273 | fn convert_inside_loop() { |
176 | check_assist( | 274 | check_assist( |
177 | convert_to_guarded_return, | 275 | convert_to_guarded_return, |
@@ -200,6 +298,35 @@ mod tests { | |||
200 | } | 298 | } |
201 | 299 | ||
202 | #[test] | 300 | #[test] |
301 | fn convert_let_inside_loop() { | ||
302 | check_assist( | ||
303 | convert_to_guarded_return, | ||
304 | r#" | ||
305 | fn main() { | ||
306 | loop { | ||
307 | if<|> let Some(n) = n { | ||
308 | foo(n); | ||
309 | bar(); | ||
310 | } | ||
311 | } | ||
312 | } | ||
313 | "#, | ||
314 | r#" | ||
315 | fn main() { | ||
316 | loop { | ||
317 | le<|>t n = match n { | ||
318 | Some(it) => it, | ||
319 | None => continue, | ||
320 | }; | ||
321 | foo(n); | ||
322 | bar(); | ||
323 | } | ||
324 | } | ||
325 | "#, | ||
326 | ); | ||
327 | } | ||
328 | |||
329 | #[test] | ||
203 | fn ignore_already_converted_if() { | 330 | fn ignore_already_converted_if() { |
204 | check_assist_not_applicable( | 331 | check_assist_not_applicable( |
205 | convert_to_guarded_return, | 332 | convert_to_guarded_return, |
diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs index 3d5f18bfa..979819d4b 100644 --- a/crates/ra_syntax/src/ast/make.rs +++ b/crates/ra_syntax/src/ast/make.rs | |||
@@ -110,6 +110,22 @@ pub fn match_arm_list(arms: impl Iterator<Item = ast::MatchArm>) -> ast::MatchAr | |||
110 | } | 110 | } |
111 | } | 111 | } |
112 | 112 | ||
113 | pub fn let_match_early(expr: ast::Expr, early_expression: &str) -> ast::LetStmt { | ||
114 | return from_text(&format!( | ||
115 | r#"let {} = match {} {{ | ||
116 | Some(it) => it, | ||
117 | None => {}, | ||
118 | }};"#, | ||
119 | expr.syntax().text(), | ||
120 | expr.syntax().text(), | ||
121 | early_expression | ||
122 | )); | ||
123 | |||
124 | fn from_text(text: &str) -> ast::LetStmt { | ||
125 | ast_from_text(&format!("fn f() {{ {} }}", text)) | ||
126 | } | ||
127 | } | ||
128 | |||
113 | pub fn where_pred(path: ast::Path, bounds: impl Iterator<Item = ast::TypeBound>) -> ast::WherePred { | 129 | pub fn where_pred(path: ast::Path, bounds: impl Iterator<Item = ast::TypeBound>) -> ast::WherePred { |
114 | let bounds = bounds.map(|b| b.syntax().to_string()).join(" + "); | 130 | let bounds = bounds.map(|b| b.syntax().to_string()).join(" + "); |
115 | return from_text(&format!("{}: {}", path.syntax(), bounds)); | 131 | return from_text(&format!("{}: {}", path.syntax(), bounds)); |