11use hir:: TypeInfo ;
22use ide_db:: syntax_helpers:: suggest_name;
33use 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 ,
610 SyntaxKind :: { BLOCK_EXPR , BREAK_EXPR , COMMENT , LOOP_EXPR , MATCH_GUARD , PATH_EXPR , RETURN_EXPR } ,
711 SyntaxNode , T ,
812} ;
@@ -105,39 +109,46 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
105109 ) ,
106110 } ;
107111
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+
108123 let ident_pat = match parent {
109124 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 )
111126 }
112127 _ if needs_adjust
113128 && !needs_ref
114129 && ty. as_ref ( ) . is_some_and ( |ty| ty. is_mutable_reference ( ) ) =>
115130 {
116- make:: ident_pat ( false , true , make :: name ( & var_name ) )
131+ make. ident_pat ( false , true , pat_name )
117132 }
118- _ => make:: ident_pat ( false , false , make :: name ( & var_name ) ) ,
133+ _ => make. ident_pat ( false , false , pat_name ) ,
119134 } ;
120135
121136 let to_extract_no_ref = match ty. as_ref ( ) . filter ( |_| needs_ref) {
122137 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 )
124139 }
125140 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 )
127142 }
128143 _ => to_extract_no_ref,
129144 } ;
130145
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) ) ;
135147
136148 match anchor {
137149 Anchor :: Before ( place) => {
138150 let prev_ws = place. prev_sibling_or_token ( ) . and_then ( |it| it. into_token ( ) ) ;
139151 let indent_to = IndentLevel :: from_node ( & place) ;
140- let insert_place = edit. make_syntax_mut ( place) ;
141152
142153 // Adjust ws to insert depending on if this is all inline or on separate lines
143154 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
146157 " " . to_owned ( )
147158 } ;
148159
149- ted :: insert_all_raw (
150- ted :: Position :: before ( insert_place ) ,
160+ editor . insert_all (
161+ Position :: before ( place ) ,
151162 vec ! [
152163 let_stmt. syntax( ) . clone( ) . into( ) ,
153164 make:: tokens:: whitespace( & trailing_ws) . into( ) ,
154165 ] ,
155166 ) ;
156167
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 ( ) ) ;
166169 }
167170 Anchor :: Replace ( stmt) => {
168171 cov_mark:: hit!( test_extract_var_expr_stmt) ;
169172
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 ( ) ) ;
180174 }
181175 Anchor :: WrapInBlock ( to_wrap) => {
182176 let indent_to = to_wrap. indent_level ( ) ;
183177
184178 let block = if to_wrap. syntax ( ) == & expr_replace {
185179 // Since `expr_replace` is the same that needs to be wrapped in a block,
186180 // 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) )
192182 } 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 ( ) ) )
207186 } ;
208187
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 ( ) ) ;
223189
224190 // fixup indentation of block
225191 block. indent ( indent_to) ;
226192 }
227193 }
194+
195+ editor. add_mappings ( make. finish_with_mappings ( ) ) ;
196+ edit. add_file_edits ( ctx. file_id ( ) , editor) ;
228197 edit. rename ( ) ;
229198 } ,
230199 )
0 commit comments