@@ -2,7 +2,6 @@ use ra_syntax::{
2
2
ast:: { self , AstNode } ,
3
3
SyntaxKind :: {
4
4
BLOCK_EXPR , BREAK_EXPR , COMMENT , LAMBDA_EXPR , LOOP_EXPR , MATCH_ARM , PATH_EXPR , RETURN_EXPR ,
5
- WHITESPACE ,
6
5
} ,
7
6
SyntaxNode ,
8
7
} ;
@@ -36,22 +35,20 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option
36
35
mark:: hit!( extract_var_in_comment_is_not_applicable) ;
37
36
return None ;
38
37
}
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 ( ) ;
46
42
acc. add (
47
43
AssistId ( "extract_variable" , AssistKind :: RefactorExtract ) ,
48
44
"Extract into variable" ,
49
45
target,
50
46
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
+ } ;
55
52
56
53
let mut buf = String :: new ( ) ;
57
54
@@ -60,26 +57,20 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option
60
57
None => "var_name" . to_string ( ) ,
61
58
} ;
62
59
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 ( ) ,
65
62
} ;
66
63
67
- if wrap_in_block {
64
+ if let Anchor :: WrapInBlock ( _ ) = anchor {
68
65
format_to ! ( buf, "{{ let {} = " , var_name) ;
69
66
} else {
70
67
format_to ! ( buf, "let {} = " , var_name) ;
71
68
} ;
72
- format_to ! ( buf, "{}" , expr . syntax( ) ) ;
69
+ format_to ! ( buf, "{}" , to_extract . syntax( ) ) ;
73
70
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 {
81
72
mark:: hit!( test_extract_var_expr_stmt) ;
82
- if full_stmt . unwrap ( ) . semicolon_token ( ) . is_none ( ) {
73
+ if stmt . semicolon_token ( ) . is_none ( ) {
83
74
buf. push_str ( ";" ) ;
84
75
}
85
76
match ctx. config . snippet_cap {
@@ -107,7 +98,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option
107
98
}
108
99
109
100
edit. replace ( expr_range, var_name. clone ( ) ) ;
110
- let offset = anchor_stmt . text_range ( ) . start ( ) ;
101
+ let offset = anchor . syntax ( ) . text_range ( ) . start ( ) ;
111
102
match ctx. config . snippet_cap {
112
103
Some ( cap) => {
113
104
let snip =
@@ -117,8 +108,8 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option
117
108
None => edit. insert ( offset, buf) ,
118
109
}
119
110
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 ( ) , " }" ) ;
122
113
}
123
114
} ,
124
115
)
@@ -138,32 +129,48 @@ fn valid_target_expr(node: SyntaxNode) -> Option<ast::Expr> {
138
129
}
139
130
}
140
131
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
+ }
152
148
}
153
- }
154
149
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
+ }
158
154
}
159
- }
160
155
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
+ }
164
167
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
+ }
167
174
}
168
175
169
176
#[ cfg( test) ]
0 commit comments