diff options
author | Andrea Pretto <[email protected]> | 2019-01-30 20:36:49 +0000 |
---|---|---|
committer | Andrea Pretto <[email protected]> | 2019-01-30 22:01:01 +0000 |
commit | a5fe4a08fb9b6e5df4f9aa1481fb62f6938897af (patch) | |
tree | cf3ff7b55f2d730124c7580bc18bf67ac4b5967f /crates/ra_ide_api_light/src | |
parent | 7a1494ced5d762cdebf590619fc3326c4a876a7b (diff) |
Some improvements to introduce_variable.
Diffstat (limited to 'crates/ra_ide_api_light/src')
-rw-r--r-- | crates/ra_ide_api_light/src/assists.rs | 8 | ||||
-rw-r--r-- | crates/ra_ide_api_light/src/assists/introduce_variable.rs | 169 | ||||
-rw-r--r-- | crates/ra_ide_api_light/src/test_utils.rs | 12 |
3 files changed, 181 insertions, 8 deletions
diff --git a/crates/ra_ide_api_light/src/assists.rs b/crates/ra_ide_api_light/src/assists.rs index aea8397c9..8905b0419 100644 --- a/crates/ra_ide_api_light/src/assists.rs +++ b/crates/ra_ide_api_light/src/assists.rs | |||
@@ -197,6 +197,14 @@ fn check_assist(assist: fn(AssistCtx) -> Option<Assist>, before: &str, after: &s | |||
197 | } | 197 | } |
198 | 198 | ||
199 | #[cfg(test)] | 199 | #[cfg(test)] |
200 | fn check_assist_not_applicable(assist: fn(AssistCtx) -> Option<Assist>, text: &str) { | ||
201 | crate::test_utils::check_action_not_applicable(text, |file, off| { | ||
202 | let range = TextRange::offset_len(off, 0.into()); | ||
203 | AssistCtx::new(file, range).apply(assist) | ||
204 | }) | ||
205 | } | ||
206 | |||
207 | #[cfg(test)] | ||
200 | fn check_assist_range(assist: fn(AssistCtx) -> Option<Assist>, before: &str, after: &str) { | 208 | fn check_assist_range(assist: fn(AssistCtx) -> Option<Assist>, before: &str, after: &str) { |
201 | crate::test_utils::check_action_range(before, after, |file, range| { | 209 | crate::test_utils::check_action_range(before, after, |file, range| { |
202 | AssistCtx::new(file, range).apply(assist) | 210 | AssistCtx::new(file, range).apply(assist) |
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 9035beba8..ed13bddc4 100644 --- a/crates/ra_ide_api_light/src/assists/introduce_variable.rs +++ b/crates/ra_ide_api_light/src/assists/introduce_variable.rs | |||
@@ -1,15 +1,18 @@ | |||
1 | use ra_syntax::{ | 1 | use ra_syntax::{ |
2 | ast::{self, AstNode}, | 2 | ast::{self, AstNode}, |
3 | SyntaxKind::WHITESPACE, SyntaxKind::MATCH_ARM, SyntaxKind::LAMBDA_EXPR, | 3 | SyntaxKind::{ |
4 | SyntaxNode, TextUnit, | 4 | WHITESPACE, MATCH_ARM, LAMBDA_EXPR, PATH_EXPR, BREAK_EXPR, LOOP_EXPR, RETURN_EXPR, COMMENT |
5 | }, SyntaxNode, TextUnit, | ||
5 | }; | 6 | }; |
6 | 7 | ||
7 | use crate::assists::{AssistCtx, Assist}; | 8 | use crate::assists::{AssistCtx, Assist}; |
8 | 9 | ||
9 | pub fn introduce_variable<'a>(ctx: AssistCtx) -> Option<Assist> { | 10 | pub fn introduce_variable<'a>(ctx: AssistCtx) -> Option<Assist> { |
10 | let node = ctx.covering_node(); | 11 | let node = ctx.covering_node(); |
11 | let expr = node.ancestors().filter_map(ast::Expr::cast).next()?; | 12 | if !valid_covering_node(node) { |
12 | 13 | return None; | |
14 | } | ||
15 | let expr = node.ancestors().filter_map(valid_target_expr).next()?; | ||
13 | let (anchor_stmt, wrap_in_block) = anchor_stmt(expr)?; | 16 | let (anchor_stmt, wrap_in_block) = anchor_stmt(expr)?; |
14 | let indent = anchor_stmt.prev_sibling()?; | 17 | let indent = anchor_stmt.prev_sibling()?; |
15 | if indent.kind() != WHITESPACE { | 18 | if indent.kind() != WHITESPACE { |
@@ -51,6 +54,21 @@ pub fn introduce_variable<'a>(ctx: AssistCtx) -> Option<Assist> { | |||
51 | }) | 54 | }) |
52 | } | 55 | } |
53 | 56 | ||
57 | fn valid_covering_node(node: &SyntaxNode) -> bool { | ||
58 | node.kind() != COMMENT | ||
59 | } | ||
60 | /// Check wether the node is a valid expression which can be extracted to a variable. | ||
61 | /// In general that's true for any expression, but in some cases that would produce invalid code. | ||
62 | fn valid_target_expr(node: &SyntaxNode) -> Option<&ast::Expr> { | ||
63 | return match node.kind() { | ||
64 | PATH_EXPR => None, | ||
65 | BREAK_EXPR => ast::BreakExpr::cast(node).and_then(|e| e.expr()), | ||
66 | RETURN_EXPR => ast::ReturnExpr::cast(node).and_then(|e| e.expr()), | ||
67 | LOOP_EXPR => ast::ReturnExpr::cast(node).and_then(|e| e.expr()), | ||
68 | _ => ast::Expr::cast(node), | ||
69 | }; | ||
70 | } | ||
71 | |||
54 | /// Returns the syntax node which will follow the freshly introduced var | 72 | /// Returns the syntax node which will follow the freshly introduced var |
55 | /// and a boolean indicating whether we have to wrap it within a { } block | 73 | /// and a boolean indicating whether we have to wrap it within a { } block |
56 | /// to produce correct code. | 74 | /// to produce correct code. |
@@ -73,9 +91,7 @@ fn anchor_stmt(expr: &ast::Expr) -> Option<(&SyntaxNode, bool)> { | |||
73 | } | 91 | } |
74 | 92 | ||
75 | if let Some(parent) = node.parent() { | 93 | if let Some(parent) = node.parent() { |
76 | if parent.kind() == MATCH_ARM | 94 | if parent.kind() == MATCH_ARM || parent.kind() == LAMBDA_EXPR { |
77 | || parent.kind() == LAMBDA_EXPR | ||
78 | { | ||
79 | return Some((node, true)); | 95 | return Some((node, true)); |
80 | } | 96 | } |
81 | } | 97 | } |
@@ -87,7 +103,7 @@ fn anchor_stmt(expr: &ast::Expr) -> Option<(&SyntaxNode, bool)> { | |||
87 | #[cfg(test)] | 103 | #[cfg(test)] |
88 | mod tests { | 104 | mod tests { |
89 | use super::*; | 105 | use super::*; |
90 | use crate::assists::check_assist_range; | 106 | use crate::assists::{ check_assist, check_assist_not_applicable, check_assist_range }; |
91 | 107 | ||
92 | #[test] | 108 | #[test] |
93 | fn test_introduce_var_simple() { | 109 | fn test_introduce_var_simple() { |
@@ -275,4 +291,141 @@ fn main() { | |||
275 | ", | 291 | ", |
276 | ); | 292 | ); |
277 | } | 293 | } |
294 | |||
295 | #[test] | ||
296 | fn test_introduce_var_path_simple() { | ||
297 | check_assist( | ||
298 | introduce_variable, | ||
299 | " | ||
300 | fn main() { | ||
301 | let o = S<|>ome(true); | ||
302 | } | ||
303 | ", | ||
304 | " | ||
305 | fn main() { | ||
306 | let <|>var_name = Some(true); | ||
307 | let o = var_name; | ||
308 | } | ||
309 | ", | ||
310 | ); | ||
311 | } | ||
312 | |||
313 | #[test] | ||
314 | fn test_introduce_var_path_method() { | ||
315 | check_assist( | ||
316 | introduce_variable, | ||
317 | " | ||
318 | fn main() { | ||
319 | let v = b<|>ar.foo(); | ||
320 | } | ||
321 | ", | ||
322 | " | ||
323 | fn main() { | ||
324 | let <|>var_name = bar.foo(); | ||
325 | let v = var_name; | ||
326 | } | ||
327 | ", | ||
328 | ); | ||
329 | } | ||
330 | |||
331 | #[test] | ||
332 | fn test_introduce_var_return() { | ||
333 | check_assist( | ||
334 | introduce_variable, | ||
335 | " | ||
336 | fn foo() -> u32 { | ||
337 | r<|>eturn 2 + 2; | ||
338 | } | ||
339 | ", | ||
340 | " | ||
341 | fn foo() -> u32 { | ||
342 | let <|>var_name = 2 + 2; | ||
343 | return var_name; | ||
344 | } | ||
345 | ", | ||
346 | ); | ||
347 | } | ||
348 | |||
349 | #[test] | ||
350 | fn test_introduce_var_break() { | ||
351 | check_assist( | ||
352 | introduce_variable, | ||
353 | " | ||
354 | fn main() { | ||
355 | let result = loop { | ||
356 | b<|>reak 2 + 2; | ||
357 | }; | ||
358 | } | ||
359 | ", | ||
360 | " | ||
361 | fn main() { | ||
362 | let result = loop { | ||
363 | let <|>var_name = 2 + 2; | ||
364 | break var_name; | ||
365 | }; | ||
366 | } | ||
367 | ", | ||
368 | ); | ||
369 | } | ||
370 | |||
371 | #[test] | ||
372 | fn test_introduce_var_for_cast() { | ||
373 | check_assist( | ||
374 | introduce_variable, | ||
375 | " | ||
376 | fn main() { | ||
377 | let v = 0f32 a<|>s u32; | ||
378 | } | ||
379 | ", | ||
380 | " | ||
381 | fn main() { | ||
382 | let <|>var_name = 0f32 as u32; | ||
383 | let v = var_name; | ||
384 | } | ||
385 | ", | ||
386 | ); | ||
387 | } | ||
388 | |||
389 | #[test] | ||
390 | fn test_introduce_var_for_return_not_applicable() { | ||
391 | check_assist_not_applicable( | ||
392 | introduce_variable, | ||
393 | " | ||
394 | fn foo() { | ||
395 | r<|>eturn; | ||
396 | } | ||
397 | ", | ||
398 | ); | ||
399 | } | ||
400 | |||
401 | #[test] | ||
402 | fn test_introduce_var_for_break_not_applicable() { | ||
403 | check_assist_not_applicable( | ||
404 | introduce_variable, | ||
405 | " | ||
406 | fn main() { | ||
407 | loop { | ||
408 | b<|>reak; | ||
409 | }; | ||
410 | } | ||
411 | ", | ||
412 | ); | ||
413 | } | ||
414 | |||
415 | #[test] | ||
416 | fn test_introduce_var_in_comment_not_applicable() { | ||
417 | check_assist_not_applicable( | ||
418 | introduce_variable, | ||
419 | " | ||
420 | fn main() { | ||
421 | let x = true; | ||
422 | let tuple = match x { | ||
423 | // c<|>omment | ||
424 | true => (2 + 2, true) | ||
425 | _ => (0, false) | ||
426 | }; | ||
427 | } | ||
428 | ", | ||
429 | ); | ||
430 | } | ||
278 | } | 431 | } |
diff --git a/crates/ra_ide_api_light/src/test_utils.rs b/crates/ra_ide_api_light/src/test_utils.rs index dc2470aa3..22ded2435 100644 --- a/crates/ra_ide_api_light/src/test_utils.rs +++ b/crates/ra_ide_api_light/src/test_utils.rs | |||
@@ -23,6 +23,18 @@ pub fn check_action<F: Fn(&SourceFile, TextUnit) -> Option<LocalEdit>>( | |||
23 | assert_eq_text!(after, &actual); | 23 | assert_eq_text!(after, &actual); |
24 | } | 24 | } |
25 | 25 | ||
26 | pub fn check_action_not_applicable<F: Fn(&SourceFile, TextUnit) -> Option<LocalEdit>>( | ||
27 | text: &str, | ||
28 | f: F, | ||
29 | ) { | ||
30 | let (text_cursor_pos, text) = extract_offset(text); | ||
31 | let file = SourceFile::parse(&text); | ||
32 | assert!( | ||
33 | f(&file, text_cursor_pos).is_none(), | ||
34 | "code action is applicable but it shouldn't" | ||
35 | ); | ||
36 | } | ||
37 | |||
26 | pub fn check_action_range<F: Fn(&SourceFile, TextRange) -> Option<LocalEdit>>( | 38 | pub fn check_action_range<F: Fn(&SourceFile, TextRange) -> Option<LocalEdit>>( |
27 | before: &str, | 39 | before: &str, |
28 | after: &str, | 40 | after: &str, |