aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_ide_api_light/src/assists/introduce_variable.rs136
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 @@
1use ra_syntax::{ 1use 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
46fn 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.
59fn 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 "
193fn main() {
194 let x = true;
195 let tuple = match x {
196 true => (<|>2 + 2<|>, true)
197 _ => (0, false)
198 };
199}
200",
201 "
202fn 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 "
218fn 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 "
230fn 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 "
250fn main() {
251 let lambda = |x: u32| <|>x * 2<|>;
252}
253",
254 "
255fn 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 "
267fn main() {
268 let lambda = |x: u32| { <|>x * 2<|> };
269}
270",
271 "
272fn main() {
273 let lambda = |x: u32| { let <|>var_name = x * 2; var_name };
274}
275",
276 );
277 }
164} 278}