diff options
Diffstat (limited to 'crates/ra_assists/src/handlers/introduce_variable.rs')
-rw-r--r-- | crates/ra_assists/src/handlers/introduce_variable.rs | 140 |
1 files changed, 77 insertions, 63 deletions
diff --git a/crates/ra_assists/src/handlers/introduce_variable.rs b/crates/ra_assists/src/handlers/introduce_variable.rs index eda9ac296..31d6539f7 100644 --- a/crates/ra_assists/src/handlers/introduce_variable.rs +++ b/crates/ra_assists/src/handlers/introduce_variable.rs | |||
@@ -4,12 +4,12 @@ use ra_syntax::{ | |||
4 | BLOCK_EXPR, BREAK_EXPR, COMMENT, LAMBDA_EXPR, LOOP_EXPR, MATCH_ARM, PATH_EXPR, RETURN_EXPR, | 4 | BLOCK_EXPR, BREAK_EXPR, COMMENT, LAMBDA_EXPR, LOOP_EXPR, MATCH_ARM, PATH_EXPR, RETURN_EXPR, |
5 | WHITESPACE, | 5 | WHITESPACE, |
6 | }, | 6 | }, |
7 | SyntaxNode, TextSize, | 7 | SyntaxNode, |
8 | }; | 8 | }; |
9 | use stdx::format_to; | 9 | use stdx::format_to; |
10 | use test_utils::tested_by; | 10 | use test_utils::mark; |
11 | 11 | ||
12 | use crate::{Assist, AssistCtx, AssistId}; | 12 | use crate::{AssistContext, AssistId, Assists}; |
13 | 13 | ||
14 | // Assist: introduce_variable | 14 | // Assist: introduce_variable |
15 | // | 15 | // |
@@ -23,17 +23,17 @@ use crate::{Assist, AssistCtx, AssistId}; | |||
23 | // -> | 23 | // -> |
24 | // ``` | 24 | // ``` |
25 | // fn main() { | 25 | // fn main() { |
26 | // let var_name = (1 + 2); | 26 | // let $0var_name = (1 + 2); |
27 | // var_name * 4; | 27 | // var_name * 4; |
28 | // } | 28 | // } |
29 | // ``` | 29 | // ``` |
30 | pub(crate) fn introduce_variable(ctx: AssistCtx) -> Option<Assist> { | 30 | pub(crate) fn introduce_variable(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
31 | if ctx.frange.range.is_empty() { | 31 | if ctx.frange.range.is_empty() { |
32 | return None; | 32 | return None; |
33 | } | 33 | } |
34 | let node = ctx.covering_element(); | 34 | let node = ctx.covering_element(); |
35 | if node.kind() == COMMENT { | 35 | if node.kind() == COMMENT { |
36 | tested_by!(introduce_var_in_comment_is_not_applicable); | 36 | mark::hit!(introduce_var_in_comment_is_not_applicable); |
37 | return None; | 37 | return None; |
38 | } | 38 | } |
39 | let expr = node.ancestors().find_map(valid_target_expr)?; | 39 | let expr = node.ancestors().find_map(valid_target_expr)?; |
@@ -42,17 +42,17 @@ pub(crate) fn introduce_variable(ctx: AssistCtx) -> Option<Assist> { | |||
42 | if indent.kind() != WHITESPACE { | 42 | if indent.kind() != WHITESPACE { |
43 | return None; | 43 | return None; |
44 | } | 44 | } |
45 | ctx.add_assist(AssistId("introduce_variable"), "Extract into variable", move |edit| { | 45 | let target = expr.syntax().text_range(); |
46 | acc.add(AssistId("introduce_variable"), "Extract into variable", target, move |edit| { | ||
46 | let mut buf = String::new(); | 47 | let mut buf = String::new(); |
47 | 48 | ||
48 | let cursor_offset = if wrap_in_block { | 49 | if wrap_in_block { |
49 | buf.push_str("{ let var_name = "); | 50 | buf.push_str("{ let var_name = "); |
50 | TextSize::of("{ let ") | ||
51 | } else { | 51 | } else { |
52 | buf.push_str("let var_name = "); | 52 | buf.push_str("let var_name = "); |
53 | TextSize::of("let ") | ||
54 | }; | 53 | }; |
55 | format_to!(buf, "{}", expr.syntax()); | 54 | format_to!(buf, "{}", expr.syntax()); |
55 | |||
56 | let full_stmt = ast::ExprStmt::cast(anchor_stmt.clone()); | 56 | let full_stmt = ast::ExprStmt::cast(anchor_stmt.clone()); |
57 | let is_full_stmt = if let Some(expr_stmt) = &full_stmt { | 57 | let is_full_stmt = if let Some(expr_stmt) = &full_stmt { |
58 | Some(expr.syntax().clone()) == expr_stmt.expr().map(|e| e.syntax().clone()) | 58 | Some(expr.syntax().clone()) == expr_stmt.expr().map(|e| e.syntax().clone()) |
@@ -60,33 +60,47 @@ pub(crate) fn introduce_variable(ctx: AssistCtx) -> Option<Assist> { | |||
60 | false | 60 | false |
61 | }; | 61 | }; |
62 | if is_full_stmt { | 62 | if is_full_stmt { |
63 | tested_by!(test_introduce_var_expr_stmt); | 63 | mark::hit!(test_introduce_var_expr_stmt); |
64 | if full_stmt.unwrap().semicolon_token().is_none() { | 64 | if full_stmt.unwrap().semicolon_token().is_none() { |
65 | buf.push_str(";"); | 65 | buf.push_str(";"); |
66 | } | 66 | } |
67 | edit.replace(expr.syntax().text_range(), buf); | 67 | let offset = expr.syntax().text_range(); |
68 | } else { | 68 | match ctx.config.snippet_cap { |
69 | buf.push_str(";"); | 69 | Some(cap) => { |
70 | 70 | let snip = buf.replace("let var_name", "let $0var_name"); | |
71 | // We want to maintain the indent level, | 71 | edit.replace_snippet(cap, offset, snip) |
72 | // but we do not want to duplicate possible | 72 | } |
73 | // extra newlines in the indent block | 73 | None => edit.replace(offset, buf), |
74 | let text = indent.text(); | ||
75 | if text.starts_with('\n') { | ||
76 | buf.push_str("\n"); | ||
77 | buf.push_str(text.trim_start_matches('\n')); | ||
78 | } else { | ||
79 | buf.push_str(text); | ||
80 | } | 74 | } |
75 | return; | ||
76 | } | ||
81 | 77 | ||
82 | edit.target(expr.syntax().text_range()); | 78 | buf.push_str(";"); |
83 | edit.replace(expr.syntax().text_range(), "var_name".to_string()); | 79 | |
84 | edit.insert(anchor_stmt.text_range().start(), buf); | 80 | // We want to maintain the indent level, |
85 | if wrap_in_block { | 81 | // but we do not want to duplicate possible |
86 | edit.insert(anchor_stmt.text_range().end(), " }"); | 82 | // extra newlines in the indent block |
83 | let text = indent.text(); | ||
84 | if text.starts_with('\n') { | ||
85 | buf.push_str("\n"); | ||
86 | buf.push_str(text.trim_start_matches('\n')); | ||
87 | } else { | ||
88 | buf.push_str(text); | ||
89 | } | ||
90 | |||
91 | edit.replace(expr.syntax().text_range(), "var_name".to_string()); | ||
92 | let offset = anchor_stmt.text_range().start(); | ||
93 | match ctx.config.snippet_cap { | ||
94 | Some(cap) => { | ||
95 | let snip = buf.replace("let var_name", "let $0var_name"); | ||
96 | edit.insert_snippet(cap, offset, snip) | ||
87 | } | 97 | } |
98 | None => edit.insert(offset, buf), | ||
99 | } | ||
100 | |||
101 | if wrap_in_block { | ||
102 | edit.insert(anchor_stmt.text_range().end(), " }"); | ||
88 | } | 103 | } |
89 | edit.set_cursor(anchor_stmt.text_range().start() + cursor_offset); | ||
90 | }) | 104 | }) |
91 | } | 105 | } |
92 | 106 | ||
@@ -111,9 +125,9 @@ fn valid_target_expr(node: SyntaxNode) -> Option<ast::Expr> { | |||
111 | /// expression like a lambda or match arm. | 125 | /// expression like a lambda or match arm. |
112 | fn anchor_stmt(expr: ast::Expr) -> Option<(SyntaxNode, bool)> { | 126 | fn anchor_stmt(expr: ast::Expr) -> Option<(SyntaxNode, bool)> { |
113 | expr.syntax().ancestors().find_map(|node| { | 127 | expr.syntax().ancestors().find_map(|node| { |
114 | if let Some(expr) = node.parent().and_then(ast::Block::cast).and_then(|it| it.expr()) { | 128 | if let Some(expr) = node.parent().and_then(ast::BlockExpr::cast).and_then(|it| it.expr()) { |
115 | if expr.syntax() == &node { | 129 | if expr.syntax() == &node { |
116 | tested_by!(test_introduce_var_last_expr); | 130 | mark::hit!(test_introduce_var_last_expr); |
117 | return Some((node, false)); | 131 | return Some((node, false)); |
118 | } | 132 | } |
119 | } | 133 | } |
@@ -134,9 +148,9 @@ fn anchor_stmt(expr: ast::Expr) -> Option<(SyntaxNode, bool)> { | |||
134 | 148 | ||
135 | #[cfg(test)] | 149 | #[cfg(test)] |
136 | mod tests { | 150 | mod tests { |
137 | use test_utils::covers; | 151 | use test_utils::mark; |
138 | 152 | ||
139 | use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target}; | 153 | use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; |
140 | 154 | ||
141 | use super::*; | 155 | use super::*; |
142 | 156 | ||
@@ -144,37 +158,37 @@ mod tests { | |||
144 | fn test_introduce_var_simple() { | 158 | fn test_introduce_var_simple() { |
145 | check_assist( | 159 | check_assist( |
146 | introduce_variable, | 160 | introduce_variable, |
147 | " | 161 | r#" |
148 | fn foo() { | 162 | fn foo() { |
149 | foo(<|>1 + 1<|>); | 163 | foo(<|>1 + 1<|>); |
150 | }", | 164 | }"#, |
151 | " | 165 | r#" |
152 | fn foo() { | 166 | fn foo() { |
153 | let <|>var_name = 1 + 1; | 167 | let $0var_name = 1 + 1; |
154 | foo(var_name); | 168 | foo(var_name); |
155 | }", | 169 | }"#, |
156 | ); | 170 | ); |
157 | } | 171 | } |
158 | 172 | ||
159 | #[test] | 173 | #[test] |
160 | fn introduce_var_in_comment_is_not_applicable() { | 174 | fn introduce_var_in_comment_is_not_applicable() { |
161 | covers!(introduce_var_in_comment_is_not_applicable); | 175 | mark::check!(introduce_var_in_comment_is_not_applicable); |
162 | check_assist_not_applicable(introduce_variable, "fn main() { 1 + /* <|>comment<|> */ 1; }"); | 176 | check_assist_not_applicable(introduce_variable, "fn main() { 1 + /* <|>comment<|> */ 1; }"); |
163 | } | 177 | } |
164 | 178 | ||
165 | #[test] | 179 | #[test] |
166 | fn test_introduce_var_expr_stmt() { | 180 | fn test_introduce_var_expr_stmt() { |
167 | covers!(test_introduce_var_expr_stmt); | 181 | mark::check!(test_introduce_var_expr_stmt); |
168 | check_assist( | 182 | check_assist( |
169 | introduce_variable, | 183 | introduce_variable, |
170 | " | 184 | r#" |
171 | fn foo() { | 185 | fn foo() { |
172 | <|>1 + 1<|>; | 186 | <|>1 + 1<|>; |
173 | }", | 187 | }"#, |
174 | " | 188 | r#" |
175 | fn foo() { | 189 | fn foo() { |
176 | let <|>var_name = 1 + 1; | 190 | let $0var_name = 1 + 1; |
177 | }", | 191 | }"#, |
178 | ); | 192 | ); |
179 | check_assist( | 193 | check_assist( |
180 | introduce_variable, | 194 | introduce_variable, |
@@ -185,7 +199,7 @@ fn foo() { | |||
185 | }", | 199 | }", |
186 | " | 200 | " |
187 | fn foo() { | 201 | fn foo() { |
188 | let <|>var_name = { let x = 0; x }; | 202 | let $0var_name = { let x = 0; x }; |
189 | something_else(); | 203 | something_else(); |
190 | }", | 204 | }", |
191 | ); | 205 | ); |
@@ -201,7 +215,7 @@ fn foo() { | |||
201 | }", | 215 | }", |
202 | " | 216 | " |
203 | fn foo() { | 217 | fn foo() { |
204 | let <|>var_name = 1; | 218 | let $0var_name = 1; |
205 | var_name + 1; | 219 | var_name + 1; |
206 | }", | 220 | }", |
207 | ); | 221 | ); |
@@ -209,7 +223,7 @@ fn foo() { | |||
209 | 223 | ||
210 | #[test] | 224 | #[test] |
211 | fn test_introduce_var_last_expr() { | 225 | fn test_introduce_var_last_expr() { |
212 | covers!(test_introduce_var_last_expr); | 226 | mark::check!(test_introduce_var_last_expr); |
213 | check_assist( | 227 | check_assist( |
214 | introduce_variable, | 228 | introduce_variable, |
215 | " | 229 | " |
@@ -218,7 +232,7 @@ fn foo() { | |||
218 | }", | 232 | }", |
219 | " | 233 | " |
220 | fn foo() { | 234 | fn foo() { |
221 | let <|>var_name = 1 + 1; | 235 | let $0var_name = 1 + 1; |
222 | bar(var_name) | 236 | bar(var_name) |
223 | }", | 237 | }", |
224 | ); | 238 | ); |
@@ -230,7 +244,7 @@ fn foo() { | |||
230 | }", | 244 | }", |
231 | " | 245 | " |
232 | fn foo() { | 246 | fn foo() { |
233 | let <|>var_name = bar(1 + 1); | 247 | let $0var_name = bar(1 + 1); |
234 | var_name | 248 | var_name |
235 | }", | 249 | }", |
236 | ) | 250 | ) |
@@ -253,7 +267,7 @@ fn main() { | |||
253 | fn main() { | 267 | fn main() { |
254 | let x = true; | 268 | let x = true; |
255 | let tuple = match x { | 269 | let tuple = match x { |
256 | true => { let <|>var_name = 2 + 2; (var_name, true) } | 270 | true => { let $0var_name = 2 + 2; (var_name, true) } |
257 | _ => (0, false) | 271 | _ => (0, false) |
258 | }; | 272 | }; |
259 | } | 273 | } |
@@ -283,7 +297,7 @@ fn main() { | |||
283 | let tuple = match x { | 297 | let tuple = match x { |
284 | true => { | 298 | true => { |
285 | let y = 1; | 299 | let y = 1; |
286 | let <|>var_name = 2 + y; | 300 | let $0var_name = 2 + y; |
287 | (var_name, true) | 301 | (var_name, true) |
288 | } | 302 | } |
289 | _ => (0, false) | 303 | _ => (0, false) |
@@ -304,7 +318,7 @@ fn main() { | |||
304 | ", | 318 | ", |
305 | " | 319 | " |
306 | fn main() { | 320 | fn main() { |
307 | let lambda = |x: u32| { let <|>var_name = x * 2; var_name }; | 321 | let lambda = |x: u32| { let $0var_name = x * 2; var_name }; |
308 | } | 322 | } |
309 | ", | 323 | ", |
310 | ); | 324 | ); |
@@ -321,7 +335,7 @@ fn main() { | |||
321 | ", | 335 | ", |
322 | " | 336 | " |
323 | fn main() { | 337 | fn main() { |
324 | let lambda = |x: u32| { let <|>var_name = x * 2; var_name }; | 338 | let lambda = |x: u32| { let $0var_name = x * 2; var_name }; |
325 | } | 339 | } |
326 | ", | 340 | ", |
327 | ); | 341 | ); |
@@ -338,7 +352,7 @@ fn main() { | |||
338 | ", | 352 | ", |
339 | " | 353 | " |
340 | fn main() { | 354 | fn main() { |
341 | let <|>var_name = Some(true); | 355 | let $0var_name = Some(true); |
342 | let o = var_name; | 356 | let o = var_name; |
343 | } | 357 | } |
344 | ", | 358 | ", |
@@ -356,7 +370,7 @@ fn main() { | |||
356 | ", | 370 | ", |
357 | " | 371 | " |
358 | fn main() { | 372 | fn main() { |
359 | let <|>var_name = bar.foo(); | 373 | let $0var_name = bar.foo(); |
360 | let v = var_name; | 374 | let v = var_name; |
361 | } | 375 | } |
362 | ", | 376 | ", |
@@ -374,7 +388,7 @@ fn foo() -> u32 { | |||
374 | ", | 388 | ", |
375 | " | 389 | " |
376 | fn foo() -> u32 { | 390 | fn foo() -> u32 { |
377 | let <|>var_name = 2 + 2; | 391 | let $0var_name = 2 + 2; |
378 | return var_name; | 392 | return var_name; |
379 | } | 393 | } |
380 | ", | 394 | ", |
@@ -396,7 +410,7 @@ fn foo() -> u32 { | |||
396 | fn foo() -> u32 { | 410 | fn foo() -> u32 { |
397 | 411 | ||
398 | 412 | ||
399 | let <|>var_name = 2 + 2; | 413 | let $0var_name = 2 + 2; |
400 | return var_name; | 414 | return var_name; |
401 | } | 415 | } |
402 | ", | 416 | ", |
@@ -413,7 +427,7 @@ fn foo() -> u32 { | |||
413 | " | 427 | " |
414 | fn foo() -> u32 { | 428 | fn foo() -> u32 { |
415 | 429 | ||
416 | let <|>var_name = 2 + 2; | 430 | let $0var_name = 2 + 2; |
417 | return var_name; | 431 | return var_name; |
418 | } | 432 | } |
419 | ", | 433 | ", |
@@ -438,7 +452,7 @@ fn foo() -> u32 { | |||
438 | // bar | 452 | // bar |
439 | 453 | ||
440 | 454 | ||
441 | let <|>var_name = 2 + 2; | 455 | let $0var_name = 2 + 2; |
442 | return var_name; | 456 | return var_name; |
443 | } | 457 | } |
444 | ", | 458 | ", |
@@ -459,7 +473,7 @@ fn main() { | |||
459 | " | 473 | " |
460 | fn main() { | 474 | fn main() { |
461 | let result = loop { | 475 | let result = loop { |
462 | let <|>var_name = 2 + 2; | 476 | let $0var_name = 2 + 2; |
463 | break var_name; | 477 | break var_name; |
464 | }; | 478 | }; |
465 | } | 479 | } |
@@ -478,7 +492,7 @@ fn main() { | |||
478 | ", | 492 | ", |
479 | " | 493 | " |
480 | fn main() { | 494 | fn main() { |
481 | let <|>var_name = 0f32 as u32; | 495 | let $0var_name = 0f32 as u32; |
482 | let v = var_name; | 496 | let v = var_name; |
483 | } | 497 | } |
484 | ", | 498 | ", |