Skip to content

Commit be8a704

Browse files
committed
Cleanup extact variable
1 parent 3bb1b30 commit be8a704

File tree

1 file changed

+55
-48
lines changed

1 file changed

+55
-48
lines changed

crates/ra_assists/src/handlers/extract_variable.rs

Lines changed: 55 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ use ra_syntax::{
22
ast::{self, AstNode},
33
SyntaxKind::{
44
BLOCK_EXPR, BREAK_EXPR, COMMENT, LAMBDA_EXPR, LOOP_EXPR, MATCH_ARM, PATH_EXPR, RETURN_EXPR,
5-
WHITESPACE,
65
},
76
SyntaxNode,
87
};
@@ -36,22 +35,20 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option
3635
mark::hit!(extract_var_in_comment_is_not_applicable);
3736
return None;
3837
}
39-
let expr = node.ancestors().find_map(valid_target_expr)?;
40-
let (anchor_stmt, wrap_in_block) = anchor_stmt(&expr)?;
41-
let indent = anchor_stmt.prev_sibling_or_token()?.as_token()?.clone();
42-
if indent.kind() != WHITESPACE {
43-
return None;
44-
}
45-
let target = expr.syntax().text_range();
38+
let to_extract = node.ancestors().find_map(valid_target_expr)?;
39+
let anchor = Anchor::from(&to_extract)?;
40+
let indent = anchor.syntax().prev_sibling_or_token()?.as_token()?.clone();
41+
let target = to_extract.syntax().text_range();
4642
acc.add(
4743
AssistId("extract_variable", AssistKind::RefactorExtract),
4844
"Extract into variable",
4945
target,
5046
move |edit| {
51-
let field_shorthand = match expr.syntax().parent().and_then(ast::RecordField::cast) {
52-
Some(field) => field.name_ref(),
53-
None => None,
54-
};
47+
let field_shorthand =
48+
match to_extract.syntax().parent().and_then(ast::RecordField::cast) {
49+
Some(field) => field.name_ref(),
50+
None => None,
51+
};
5552

5653
let mut buf = String::new();
5754

@@ -60,26 +57,20 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option
6057
None => "var_name".to_string(),
6158
};
6259
let expr_range = match &field_shorthand {
63-
Some(it) => it.syntax().text_range().cover(expr.syntax().text_range()),
64-
None => expr.syntax().text_range(),
60+
Some(it) => it.syntax().text_range().cover(to_extract.syntax().text_range()),
61+
None => to_extract.syntax().text_range(),
6562
};
6663

67-
if wrap_in_block {
64+
if let Anchor::WrapInBlock(_) = anchor {
6865
format_to!(buf, "{{ let {} = ", var_name);
6966
} else {
7067
format_to!(buf, "let {} = ", var_name);
7168
};
72-
format_to!(buf, "{}", expr.syntax());
69+
format_to!(buf, "{}", to_extract.syntax());
7370

74-
let full_stmt = ast::ExprStmt::cast(anchor_stmt.clone());
75-
let is_full_stmt = if let Some(expr_stmt) = &full_stmt {
76-
Some(expr.syntax().clone()) == expr_stmt.expr().map(|e| e.syntax().clone())
77-
} else {
78-
false
79-
};
80-
if is_full_stmt {
71+
if let Anchor::Replace(stmt) = anchor {
8172
mark::hit!(test_extract_var_expr_stmt);
82-
if full_stmt.unwrap().semicolon_token().is_none() {
73+
if stmt.semicolon_token().is_none() {
8374
buf.push_str(";");
8475
}
8576
match ctx.config.snippet_cap {
@@ -107,7 +98,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option
10798
}
10899

109100
edit.replace(expr_range, var_name.clone());
110-
let offset = anchor_stmt.text_range().start();
101+
let offset = anchor.syntax().text_range().start();
111102
match ctx.config.snippet_cap {
112103
Some(cap) => {
113104
let snip =
@@ -117,8 +108,8 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option
117108
None => edit.insert(offset, buf),
118109
}
119110

120-
if wrap_in_block {
121-
edit.insert(anchor_stmt.text_range().end(), " }");
111+
if let Anchor::WrapInBlock(_) = anchor {
112+
edit.insert(anchor.syntax().text_range().end(), " }");
122113
}
123114
},
124115
)
@@ -138,32 +129,48 @@ fn valid_target_expr(node: SyntaxNode) -> Option<ast::Expr> {
138129
}
139130
}
140131

141-
/// Returns the syntax node which will follow the freshly extractd var
142-
/// and a boolean indicating whether we have to wrap it within a { } block
143-
/// to produce correct code.
144-
/// It can be a statement, the last in a block expression or a wanna be block
145-
/// expression like a lambda or match arm.
146-
fn anchor_stmt(expr: &ast::Expr) -> Option<(SyntaxNode, bool)> {
147-
expr.syntax().ancestors().find_map(|node| {
148-
if let Some(expr) = node.parent().and_then(ast::BlockExpr::cast).and_then(|it| it.expr()) {
149-
if expr.syntax() == &node {
150-
mark::hit!(test_extract_var_last_expr);
151-
return Some((node, false));
132+
enum Anchor {
133+
Before(SyntaxNode),
134+
Replace(ast::ExprStmt),
135+
WrapInBlock(SyntaxNode),
136+
}
137+
138+
impl Anchor {
139+
fn from(to_extract: &ast::Expr) -> Option<Anchor> {
140+
to_extract.syntax().ancestors().find_map(|node| {
141+
if let Some(expr) =
142+
node.parent().and_then(ast::BlockExpr::cast).and_then(|it| it.expr())
143+
{
144+
if expr.syntax() == &node {
145+
mark::hit!(test_extract_var_last_expr);
146+
return Some(Anchor::Before(node));
147+
}
152148
}
153-
}
154149

155-
if let Some(parent) = node.parent() {
156-
if parent.kind() == MATCH_ARM || parent.kind() == LAMBDA_EXPR {
157-
return Some((node, true));
150+
if let Some(parent) = node.parent() {
151+
if parent.kind() == MATCH_ARM || parent.kind() == LAMBDA_EXPR {
152+
return Some(Anchor::WrapInBlock(node));
153+
}
158154
}
159-
}
160155

161-
if ast::Stmt::cast(node.clone()).is_some() {
162-
return Some((node, false));
163-
}
156+
if let Some(stmt) = ast::Stmt::cast(node.clone()) {
157+
if let ast::Stmt::ExprStmt(stmt) = stmt {
158+
if stmt.expr().as_ref() == Some(to_extract) {
159+
return Some(Anchor::Replace(stmt));
160+
}
161+
}
162+
return Some(Anchor::Before(node));
163+
}
164+
None
165+
})
166+
}
164167

165-
None
166-
})
168+
fn syntax(&self) -> &SyntaxNode {
169+
match self {
170+
Anchor::Before(it) | Anchor::WrapInBlock(it) => it,
171+
Anchor::Replace(stmt) => stmt.syntax(),
172+
}
173+
}
167174
}
168175

169176
#[cfg(test)]

0 commit comments

Comments
 (0)