1
1
use hir:: TypeInfo ;
2
2
use ide_db:: syntax_helpers:: suggest_name;
3
3
use syntax:: {
4
- ast:: { self , edit:: IndentLevel , edit_in_place:: Indent , make, AstNode , HasName } ,
5
- ted, NodeOrToken ,
4
+ ast:: {
5
+ self , edit:: IndentLevel , edit_in_place:: Indent , make, syntax_factory:: SyntaxFactory ,
6
+ AstNode ,
7
+ } ,
8
+ syntax_editor:: Position ,
9
+ NodeOrToken ,
6
10
SyntaxKind :: { BLOCK_EXPR , BREAK_EXPR , COMMENT , LOOP_EXPR , MATCH_GUARD , PATH_EXPR , RETURN_EXPR } ,
7
11
SyntaxNode , T ,
8
12
} ;
@@ -105,39 +109,46 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
105
109
) ,
106
110
} ;
107
111
112
+ let make = SyntaxFactory :: new ( ) ;
113
+ let mut editor = edit. make_editor ( & expr_replace) ;
114
+
115
+ let pat_name = make. name ( & var_name) ;
116
+ let name_expr = make. expr_path ( make:: ext:: ident_path ( & var_name) ) ;
117
+
118
+ if let Some ( cap) = ctx. config . snippet_cap {
119
+ let tabstop = edit. make_tabstop_before ( cap) ;
120
+ editor. add_annotation ( pat_name. syntax ( ) . clone ( ) , tabstop) ;
121
+ }
122
+
108
123
let ident_pat = match parent {
109
124
Some ( ast:: Expr :: RefExpr ( expr) ) if expr. mut_token ( ) . is_some ( ) => {
110
- make:: ident_pat ( false , true , make :: name ( & var_name ) )
125
+ make. ident_pat ( false , true , pat_name )
111
126
}
112
127
_ if needs_adjust
113
128
&& !needs_ref
114
129
&& ty. as_ref ( ) . is_some_and ( |ty| ty. is_mutable_reference ( ) ) =>
115
130
{
116
- make:: ident_pat ( false , true , make :: name ( & var_name ) )
131
+ make. ident_pat ( false , true , pat_name )
117
132
}
118
- _ => make:: ident_pat ( false , false , make :: name ( & var_name ) ) ,
133
+ _ => make. ident_pat ( false , false , pat_name ) ,
119
134
} ;
120
135
121
136
let to_extract_no_ref = match ty. as_ref ( ) . filter ( |_| needs_ref) {
122
137
Some ( receiver_type) if receiver_type. is_mutable_reference ( ) => {
123
- make:: expr_ref ( to_extract_no_ref, true )
138
+ make. expr_ref ( to_extract_no_ref, true )
124
139
}
125
140
Some ( receiver_type) if receiver_type. is_reference ( ) => {
126
- make:: expr_ref ( to_extract_no_ref, false )
141
+ make. expr_ref ( to_extract_no_ref, false )
127
142
}
128
143
_ => to_extract_no_ref,
129
144
} ;
130
145
131
- let expr_replace = edit. make_syntax_mut ( expr_replace) ;
132
- let let_stmt =
133
- make:: let_stmt ( ident_pat. into ( ) , None , Some ( to_extract_no_ref) ) . clone_for_update ( ) ;
134
- let name_expr = make:: expr_path ( make:: ext:: ident_path ( & var_name) ) . clone_for_update ( ) ;
146
+ let let_stmt = make. let_stmt ( ident_pat. into ( ) , None , Some ( to_extract_no_ref) ) ;
135
147
136
148
match anchor {
137
149
Anchor :: Before ( place) => {
138
150
let prev_ws = place. prev_sibling_or_token ( ) . and_then ( |it| it. into_token ( ) ) ;
139
151
let indent_to = IndentLevel :: from_node ( & place) ;
140
- let insert_place = edit. make_syntax_mut ( place) ;
141
152
142
153
// Adjust ws to insert depending on if this is all inline or on separate lines
143
154
let trailing_ws = if prev_ws. is_some_and ( |it| it. text ( ) . starts_with ( '\n' ) ) {
@@ -146,85 +157,43 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
146
157
" " . to_owned ( )
147
158
} ;
148
159
149
- ted :: insert_all_raw (
150
- ted :: Position :: before ( insert_place ) ,
160
+ editor . insert_all (
161
+ Position :: before ( place ) ,
151
162
vec ! [
152
163
let_stmt. syntax( ) . clone( ) . into( ) ,
153
164
make:: tokens:: whitespace( & trailing_ws) . into( ) ,
154
165
] ,
155
166
) ;
156
167
157
- ted:: replace ( expr_replace, name_expr. syntax ( ) ) ;
158
-
159
- if let Some ( cap) = ctx. config . snippet_cap {
160
- if let Some ( ast:: Pat :: IdentPat ( ident_pat) ) = let_stmt. pat ( ) {
161
- if let Some ( name) = ident_pat. name ( ) {
162
- edit. add_tabstop_before ( cap, name) ;
163
- }
164
- }
165
- }
168
+ editor. replace ( expr_replace, name_expr. syntax ( ) ) ;
166
169
}
167
170
Anchor :: Replace ( stmt) => {
168
171
cov_mark:: hit!( test_extract_var_expr_stmt) ;
169
172
170
- let stmt_replace = edit. make_mut ( stmt) ;
171
- ted:: replace ( stmt_replace. syntax ( ) , let_stmt. syntax ( ) ) ;
172
-
173
- if let Some ( cap) = ctx. config . snippet_cap {
174
- if let Some ( ast:: Pat :: IdentPat ( ident_pat) ) = let_stmt. pat ( ) {
175
- if let Some ( name) = ident_pat. name ( ) {
176
- edit. add_tabstop_before ( cap, name) ;
177
- }
178
- }
179
- }
173
+ editor. replace ( stmt. syntax ( ) , let_stmt. syntax ( ) ) ;
180
174
}
181
175
Anchor :: WrapInBlock ( to_wrap) => {
182
176
let indent_to = to_wrap. indent_level ( ) ;
183
177
184
178
let block = if to_wrap. syntax ( ) == & expr_replace {
185
179
// Since `expr_replace` is the same that needs to be wrapped in a block,
186
180
// we can just directly replace it with a block
187
- let block =
188
- make:: block_expr ( [ let_stmt. into ( ) ] , Some ( name_expr) ) . clone_for_update ( ) ;
189
- ted:: replace ( expr_replace, block. syntax ( ) ) ;
190
-
191
- block
181
+ make. block_expr ( [ let_stmt. into ( ) ] , Some ( name_expr) )
192
182
} else {
193
- // `expr_replace` is a descendant of `to_wrap`, so both steps need to be
194
- // handled separately, otherwise we wrap the wrong expression
195
- let to_wrap = edit. make_mut ( to_wrap) ;
196
-
197
- // Replace the target expr first so that we don't need to find where
198
- // `expr_replace` is in the wrapped `to_wrap`
199
- ted:: replace ( expr_replace, name_expr. syntax ( ) ) ;
200
-
201
- // Wrap `to_wrap` in a block
202
- let block = make:: block_expr ( [ let_stmt. into ( ) ] , Some ( to_wrap. clone ( ) ) )
203
- . clone_for_update ( ) ;
204
- ted:: replace ( to_wrap. syntax ( ) , block. syntax ( ) ) ;
205
-
206
- block
183
+ // `expr_replace` is a descendant of `to_wrap`, so we just replace it with `name_expr`.
184
+ editor. replace ( expr_replace, name_expr. syntax ( ) ) ;
185
+ make. block_expr ( [ let_stmt. into ( ) ] , Some ( to_wrap. clone ( ) ) )
207
186
} ;
208
187
209
- if let Some ( cap) = ctx. config . snippet_cap {
210
- // Adding a tabstop to `name` requires finding the let stmt again, since
211
- // the existing `let_stmt` is not actually added to the tree
212
- let pat = block. statements ( ) . find_map ( |stmt| {
213
- let ast:: Stmt :: LetStmt ( let_stmt) = stmt else { return None } ;
214
- let_stmt. pat ( )
215
- } ) ;
216
-
217
- if let Some ( ast:: Pat :: IdentPat ( ident_pat) ) = pat {
218
- if let Some ( name) = ident_pat. name ( ) {
219
- edit. add_tabstop_before ( cap, name) ;
220
- }
221
- }
222
- }
188
+ editor. replace ( to_wrap. syntax ( ) , block. syntax ( ) ) ;
223
189
224
190
// fixup indentation of block
225
191
block. indent ( indent_to) ;
226
192
}
227
193
}
194
+
195
+ editor. add_mappings ( make. finish_with_mappings ( ) ) ;
196
+ edit. add_file_edits ( ctx. file_id ( ) , editor) ;
228
197
edit. rename ( ) ;
229
198
} ,
230
199
)
0 commit comments