aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src/handlers/extract_variable.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists/src/handlers/extract_variable.rs')
-rw-r--r--crates/ra_assists/src/handlers/extract_variable.rs137
1 files changed, 71 insertions, 66 deletions
diff --git a/crates/ra_assists/src/handlers/extract_variable.rs b/crates/ra_assists/src/handlers/extract_variable.rs
index c4150d2bb..481baf1a4 100644
--- a/crates/ra_assists/src/handlers/extract_variable.rs
+++ b/crates/ra_assists/src/handlers/extract_variable.rs
@@ -9,7 +9,7 @@ use ra_syntax::{
9use stdx::format_to; 9use stdx::format_to;
10use test_utils::mark; 10use test_utils::mark;
11 11
12use crate::{AssistContext, AssistId, Assists}; 12use crate::{AssistContext, AssistId, AssistKind, Assists};
13 13
14// Assist: extract_variable 14// Assist: extract_variable
15// 15//
@@ -43,80 +43,85 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option
43 return None; 43 return None;
44 } 44 }
45 let target = expr.syntax().text_range(); 45 let target = expr.syntax().text_range();
46 acc.add(AssistId("extract_variable"), "Extract into variable", target, move |edit| { 46 acc.add(
47 let field_shorthand = match expr.syntax().parent().and_then(ast::RecordField::cast) { 47 AssistId("extract_variable", AssistKind::RefactorExtract),
48 Some(field) => field.name_ref(), 48 "Extract into variable",
49 None => None, 49 target,
50 }; 50 move |edit| {
51 51 let field_shorthand = match expr.syntax().parent().and_then(ast::RecordField::cast) {
52 let mut buf = String::new(); 52 Some(field) => field.name_ref(),
53 53 None => None,
54 let var_name = match &field_shorthand { 54 };
55 Some(it) => it.to_string(), 55
56 None => "var_name".to_string(), 56 let mut buf = String::new();
57 }; 57
58 let expr_range = match &field_shorthand { 58 let var_name = match &field_shorthand {
59 Some(it) => it.syntax().text_range().cover(expr.syntax().text_range()), 59 Some(it) => it.to_string(),
60 None => expr.syntax().text_range(), 60 None => "var_name".to_string(),
61 }; 61 };
62 62 let expr_range = match &field_shorthand {
63 if wrap_in_block { 63 Some(it) => it.syntax().text_range().cover(expr.syntax().text_range()),
64 format_to!(buf, "{{ let {} = ", var_name); 64 None => expr.syntax().text_range(),
65 } else { 65 };
66 format_to!(buf, "let {} = ", var_name); 66
67 }; 67 if wrap_in_block {
68 format_to!(buf, "{}", expr.syntax()); 68 format_to!(buf, "{{ let {} = ", var_name);
69 69 } else {
70 let full_stmt = ast::ExprStmt::cast(anchor_stmt.clone()); 70 format_to!(buf, "let {} = ", var_name);
71 let is_full_stmt = if let Some(expr_stmt) = &full_stmt { 71 };
72 Some(expr.syntax().clone()) == expr_stmt.expr().map(|e| e.syntax().clone()) 72 format_to!(buf, "{}", expr.syntax());
73 } else { 73
74 false 74 let full_stmt = ast::ExprStmt::cast(anchor_stmt.clone());
75 }; 75 let is_full_stmt = if let Some(expr_stmt) = &full_stmt {
76 if is_full_stmt { 76 Some(expr.syntax().clone()) == expr_stmt.expr().map(|e| e.syntax().clone())
77 mark::hit!(test_extract_var_expr_stmt); 77 } else {
78 if full_stmt.unwrap().semicolon_token().is_none() { 78 false
79 buf.push_str(";"); 79 };
80 if is_full_stmt {
81 mark::hit!(test_extract_var_expr_stmt);
82 if full_stmt.unwrap().semicolon_token().is_none() {
83 buf.push_str(";");
84 }
85 match ctx.config.snippet_cap {
86 Some(cap) => {
87 let snip = buf
88 .replace(&format!("let {}", var_name), &format!("let $0{}", var_name));
89 edit.replace_snippet(cap, expr_range, snip)
90 }
91 None => edit.replace(expr_range, buf),
92 }
93 return;
94 }
95
96 buf.push_str(";");
97
98 // We want to maintain the indent level,
99 // but we do not want to duplicate possible
100 // extra newlines in the indent block
101 let text = indent.text();
102 if text.starts_with('\n') {
103 buf.push_str("\n");
104 buf.push_str(text.trim_start_matches('\n'));
105 } else {
106 buf.push_str(text);
80 } 107 }
108
109 edit.replace(expr_range, var_name.clone());
110 let offset = anchor_stmt.text_range().start();
81 match ctx.config.snippet_cap { 111 match ctx.config.snippet_cap {
82 Some(cap) => { 112 Some(cap) => {
83 let snip = 113 let snip =
84 buf.replace(&format!("let {}", var_name), &format!("let $0{}", var_name)); 114 buf.replace(&format!("let {}", var_name), &format!("let $0{}", var_name));
85 edit.replace_snippet(cap, expr_range, snip) 115 edit.insert_snippet(cap, offset, snip)
86 } 116 }
87 None => edit.replace(expr_range, buf), 117 None => edit.insert(offset, buf),
88 } 118 }
89 return;
90 }
91 119
92 buf.push_str(";"); 120 if wrap_in_block {
93 121 edit.insert(anchor_stmt.text_range().end(), " }");
94 // We want to maintain the indent level,
95 // but we do not want to duplicate possible
96 // extra newlines in the indent block
97 let text = indent.text();
98 if text.starts_with('\n') {
99 buf.push_str("\n");
100 buf.push_str(text.trim_start_matches('\n'));
101 } else {
102 buf.push_str(text);
103 }
104
105 edit.replace(expr_range, var_name.clone());
106 let offset = anchor_stmt.text_range().start();
107 match ctx.config.snippet_cap {
108 Some(cap) => {
109 let snip =
110 buf.replace(&format!("let {}", var_name), &format!("let $0{}", var_name));
111 edit.insert_snippet(cap, offset, snip)
112 } 122 }
113 None => edit.insert(offset, buf), 123 },
114 } 124 )
115
116 if wrap_in_block {
117 edit.insert(anchor_stmt.text_range().end(), " }");
118 }
119 })
120} 125}
121 126
122/// Check whether the node is a valid expression which can be extracted to a variable. 127/// Check whether the node is a valid expression which can be extracted to a variable.