diff options
-rw-r--r-- | crates/ra_ide_api_light/src/assists/introduce_variable.rs | 136 |
1 files changed, 125 insertions, 11 deletions
diff --git a/crates/ra_ide_api_light/src/assists/introduce_variable.rs b/crates/ra_ide_api_light/src/assists/introduce_variable.rs index 3e4434c23..9035beba8 100644 --- a/crates/ra_ide_api_light/src/assists/introduce_variable.rs +++ b/crates/ra_ide_api_light/src/assists/introduce_variable.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use ra_syntax::{ | 1 | use ra_syntax::{ |
2 | ast::{self, AstNode}, | 2 | ast::{self, AstNode}, |
3 | SyntaxKind::WHITESPACE, | 3 | SyntaxKind::WHITESPACE, SyntaxKind::MATCH_ARM, SyntaxKind::LAMBDA_EXPR, |
4 | SyntaxNode, TextUnit, | 4 | SyntaxNode, TextUnit, |
5 | }; | 5 | }; |
6 | 6 | ||
@@ -10,7 +10,7 @@ pub fn introduce_variable<'a>(ctx: AssistCtx) -> Option<Assist> { | |||
10 | let node = ctx.covering_node(); | 10 | let node = ctx.covering_node(); |
11 | let expr = node.ancestors().filter_map(ast::Expr::cast).next()?; | 11 | let expr = node.ancestors().filter_map(ast::Expr::cast).next()?; |
12 | 12 | ||
13 | let anchor_stmt = anchor_stmt(expr)?; | 13 | let (anchor_stmt, wrap_in_block) = anchor_stmt(expr)?; |
14 | let indent = anchor_stmt.prev_sibling()?; | 14 | let indent = anchor_stmt.prev_sibling()?; |
15 | if indent.kind() != WHITESPACE { | 15 | if indent.kind() != WHITESPACE { |
16 | return None; | 16 | return None; |
@@ -18,7 +18,14 @@ pub fn introduce_variable<'a>(ctx: AssistCtx) -> Option<Assist> { | |||
18 | ctx.build("introduce variable", move |edit| { | 18 | ctx.build("introduce variable", move |edit| { |
19 | let mut buf = String::new(); | 19 | let mut buf = String::new(); |
20 | 20 | ||
21 | buf.push_str("let var_name = "); | 21 | let cursor_offset = if wrap_in_block { |
22 | buf.push_str("{ let var_name = "); | ||
23 | TextUnit::of_str("{ let ") | ||
24 | } else { | ||
25 | buf.push_str("let var_name = "); | ||
26 | TextUnit::of_str("let ") | ||
27 | }; | ||
28 | |||
22 | expr.syntax().text().push_to(&mut buf); | 29 | expr.syntax().text().push_to(&mut buf); |
23 | let full_stmt = ast::ExprStmt::cast(anchor_stmt); | 30 | let full_stmt = ast::ExprStmt::cast(anchor_stmt); |
24 | let is_full_stmt = if let Some(expr_stmt) = full_stmt { | 31 | let is_full_stmt = if let Some(expr_stmt) = full_stmt { |
@@ -36,28 +43,44 @@ pub fn introduce_variable<'a>(ctx: AssistCtx) -> Option<Assist> { | |||
36 | indent.text().push_to(&mut buf); | 43 | indent.text().push_to(&mut buf); |
37 | edit.replace(expr.syntax().range(), "var_name".to_string()); | 44 | edit.replace(expr.syntax().range(), "var_name".to_string()); |
38 | edit.insert(anchor_stmt.range().start(), buf); | 45 | edit.insert(anchor_stmt.range().start(), buf); |
46 | if wrap_in_block { | ||
47 | edit.insert(anchor_stmt.range().end(), " }"); | ||
48 | } | ||
39 | } | 49 | } |
40 | edit.set_cursor(anchor_stmt.range().start() + TextUnit::of_str("let ")); | 50 | edit.set_cursor(anchor_stmt.range().start() + cursor_offset); |
41 | }) | 51 | }) |
42 | } | 52 | } |
43 | 53 | ||
44 | /// Statement or last in the block expression, which will follow | 54 | /// Returns the syntax node which will follow the freshly introduced var |
45 | /// the freshly introduced var. | 55 | /// and a boolean indicating whether we have to wrap it within a { } block |
46 | fn anchor_stmt(expr: &ast::Expr) -> Option<&SyntaxNode> { | 56 | /// to produce correct code. |
47 | expr.syntax().ancestors().find(|&node| { | 57 | /// It can be a statement, the last in a block expression or a wanna be block |
58 | /// expression like a lamba or match arm. | ||
59 | fn anchor_stmt(expr: &ast::Expr) -> Option<(&SyntaxNode, bool)> { | ||
60 | expr.syntax().ancestors().find_map(|node| { | ||
48 | if ast::Stmt::cast(node).is_some() { | 61 | if ast::Stmt::cast(node).is_some() { |
49 | return true; | 62 | return Some((node, false)); |
50 | } | 63 | } |
64 | |||
51 | if let Some(expr) = node | 65 | if let Some(expr) = node |
52 | .parent() | 66 | .parent() |
53 | .and_then(ast::Block::cast) | 67 | .and_then(ast::Block::cast) |
54 | .and_then(|it| it.expr()) | 68 | .and_then(|it| it.expr()) |
55 | { | 69 | { |
56 | if expr.syntax() == node { | 70 | if expr.syntax() == node { |
57 | return true; | 71 | return Some((node, false)); |
58 | } | 72 | } |
59 | } | 73 | } |
60 | false | 74 | |
75 | if let Some(parent) = node.parent() { | ||
76 | if parent.kind() == MATCH_ARM | ||
77 | || parent.kind() == LAMBDA_EXPR | ||
78 | { | ||
79 | return Some((node, true)); | ||
80 | } | ||
81 | } | ||
82 | |||
83 | None | ||
61 | }) | 84 | }) |
62 | } | 85 | } |
63 | 86 | ||
@@ -161,4 +184,95 @@ fn foo() { | |||
161 | }", | 184 | }", |
162 | ); | 185 | ); |
163 | } | 186 | } |
187 | |||
188 | #[test] | ||
189 | fn test_introduce_var_in_match_arm_no_block() { | ||
190 | check_assist_range( | ||
191 | introduce_variable, | ||
192 | " | ||
193 | fn main() { | ||
194 | let x = true; | ||
195 | let tuple = match x { | ||
196 | true => (<|>2 + 2<|>, true) | ||
197 | _ => (0, false) | ||
198 | }; | ||
199 | } | ||
200 | ", | ||
201 | " | ||
202 | fn main() { | ||
203 | let x = true; | ||
204 | let tuple = match x { | ||
205 | true => { let <|>var_name = 2 + 2; (var_name, true) } | ||
206 | _ => (0, false) | ||
207 | }; | ||
208 | } | ||
209 | ", | ||
210 | ); | ||
211 | } | ||
212 | |||
213 | #[test] | ||
214 | fn test_introduce_var_in_match_arm_with_block() { | ||
215 | check_assist_range( | ||
216 | introduce_variable, | ||
217 | " | ||
218 | fn main() { | ||
219 | let x = true; | ||
220 | let tuple = match x { | ||
221 | true => { | ||
222 | let y = 1; | ||
223 | (<|>2 + y<|>, true) | ||
224 | } | ||
225 | _ => (0, false) | ||
226 | }; | ||
227 | } | ||
228 | ", | ||
229 | " | ||
230 | fn main() { | ||
231 | let x = true; | ||
232 | let tuple = match x { | ||
233 | true => { | ||
234 | let y = 1; | ||
235 | let <|>var_name = 2 + y; | ||
236 | (var_name, true) | ||
237 | } | ||
238 | _ => (0, false) | ||
239 | }; | ||
240 | } | ||
241 | ", | ||
242 | ); | ||
243 | } | ||
244 | |||
245 | #[test] | ||
246 | fn test_introduce_var_in_closure_no_block() { | ||
247 | check_assist_range( | ||
248 | introduce_variable, | ||
249 | " | ||
250 | fn main() { | ||
251 | let lambda = |x: u32| <|>x * 2<|>; | ||
252 | } | ||
253 | ", | ||
254 | " | ||
255 | fn main() { | ||
256 | let lambda = |x: u32| { let <|>var_name = x * 2; var_name }; | ||
257 | } | ||
258 | ", | ||
259 | ); | ||
260 | } | ||
261 | |||
262 | #[test] | ||
263 | fn test_introduce_var_in_closure_with_block() { | ||
264 | check_assist_range( | ||
265 | introduce_variable, | ||
266 | " | ||
267 | fn main() { | ||
268 | let lambda = |x: u32| { <|>x * 2<|> }; | ||
269 | } | ||
270 | ", | ||
271 | " | ||
272 | fn main() { | ||
273 | let lambda = |x: u32| { let <|>var_name = x * 2; var_name }; | ||
274 | } | ||
275 | ", | ||
276 | ); | ||
277 | } | ||
164 | } | 278 | } |