@@ -6,7 +6,9 @@ use ide_db::syntax_helpers::suggest_name;
6
6
use ide_db:: RootDatabase ;
7
7
use ide_db:: { famous_defs:: FamousDefs , helpers:: mod_path_to_ast} ;
8
8
use itertools:: Itertools ;
9
- use syntax:: ast:: edit_in_place:: Removable ;
9
+ use syntax:: ast:: edit:: IndentLevel ;
10
+ use syntax:: ast:: edit_in_place:: Indent ;
11
+ use syntax:: ast:: syntax_factory:: SyntaxFactory ;
10
12
use syntax:: ast:: { self , make, AstNode , MatchArmList , MatchExpr , Pat } ;
11
13
12
14
use crate :: { utils, AssistContext , AssistId , AssistKind , Assists } ;
@@ -200,8 +202,8 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
200
202
AssistId ( "add_missing_match_arms" , AssistKind :: QuickFix ) ,
201
203
"Fill match arms" ,
202
204
ctx. sema . original_range ( match_expr. syntax ( ) ) . range ,
203
- |edit | {
204
- let new_match_arm_list = match_arm_list . clone_for_update ( ) ;
205
+ |builder | {
206
+ let make = SyntaxFactory :: new ( ) ;
205
207
206
208
// having any hidden variants means that we need a catch-all arm
207
209
needs_catch_all_arm |= has_hidden_variants;
@@ -211,89 +213,85 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
211
213
// filter out hidden patterns because they're handled by the catch-all arm
212
214
!hidden
213
215
} )
214
- . map ( |( pat, _) | {
215
- make:: match_arm ( pat, None , make:: ext:: expr_todo ( ) ) . clone_for_update ( )
216
- } ) ;
216
+ . map ( |( pat, _) | make. match_arm ( pat, None , make:: ext:: expr_todo ( ) ) ) ;
217
217
218
- let catch_all_arm = new_match_arm_list
218
+ let mut arms : Vec < _ > = match_arm_list
219
219
. arms ( )
220
- . find ( |arm| matches ! ( arm. pat( ) , Some ( ast:: Pat :: WildcardPat ( _) ) ) ) ;
221
- if let Some ( arm) = catch_all_arm {
222
- let is_empty_expr = arm. expr ( ) . is_none_or ( |e| match e {
223
- ast:: Expr :: BlockExpr ( b) => {
224
- b. statements ( ) . next ( ) . is_none ( ) && b. tail_expr ( ) . is_none ( )
220
+ . filter ( |arm| {
221
+ if matches ! ( arm. pat( ) , Some ( ast:: Pat :: WildcardPat ( _) ) ) {
222
+ let is_empty_expr = arm. expr ( ) . is_none_or ( |e| match e {
223
+ ast:: Expr :: BlockExpr ( b) => {
224
+ b. statements ( ) . next ( ) . is_none ( ) && b. tail_expr ( ) . is_none ( )
225
+ }
226
+ ast:: Expr :: TupleExpr ( t) => t. fields ( ) . next ( ) . is_none ( ) ,
227
+ _ => false ,
228
+ } ) ;
229
+ if is_empty_expr {
230
+ false
231
+ } else {
232
+ cov_mark:: hit!( add_missing_match_arms_empty_expr) ;
233
+ true
234
+ }
235
+ } else {
236
+ true
225
237
}
226
- ast:: Expr :: TupleExpr ( t) => t. fields ( ) . next ( ) . is_none ( ) ,
227
- _ => false ,
228
- } ) ;
229
- if is_empty_expr {
230
- arm. remove ( ) ;
231
- } else {
232
- cov_mark:: hit!( add_missing_match_arms_empty_expr) ;
233
- }
234
- }
238
+ } )
239
+ . collect ( ) ;
235
240
236
- let mut added_arms = Vec :: new ( ) ;
237
- let mut todo_placeholders = Vec :: new ( ) ;
238
- for arm in missing_arms {
239
- todo_placeholders. push ( arm. expr ( ) . unwrap ( ) ) ;
240
- added_arms. push ( arm) ;
241
- }
241
+ let first_new_arm_idx = arms. len ( ) ;
242
+ arms. extend ( missing_arms) ;
242
243
243
244
if needs_catch_all_arm && !has_catch_all_arm {
244
245
cov_mark:: hit!( added_wildcard_pattern) ;
245
- let arm =
246
- make:: match_arm ( make:: wildcard_pat ( ) . into ( ) , None , make:: ext:: expr_todo ( ) )
247
- . clone_for_update ( ) ;
248
- todo_placeholders. push ( arm. expr ( ) . unwrap ( ) ) ;
249
- added_arms. push ( arm) ;
250
- }
251
-
252
- let first_new_arm = added_arms. first ( ) . cloned ( ) ;
253
- let last_new_arm = added_arms. last ( ) . cloned ( ) ;
254
-
255
- for arm in added_arms {
256
- new_match_arm_list. add_arm ( arm) ;
246
+ let arm = make. match_arm ( make:: wildcard_pat ( ) . into ( ) , None , make:: ext:: expr_todo ( ) ) ;
247
+ arms. push ( arm) ;
257
248
}
258
249
259
- if let Some ( cap) = ctx. config . snippet_cap {
260
- if let Some ( it) = first_new_arm
261
- . and_then ( |arm| arm. syntax ( ) . descendants ( ) . find_map ( ast:: WildcardPat :: cast) )
262
- {
263
- edit. add_placeholder_snippet ( cap, it) ;
264
- }
265
-
266
- for placeholder in todo_placeholders {
267
- edit. add_placeholder_snippet ( cap, placeholder) ;
268
- }
269
-
270
- if let Some ( arm) = last_new_arm {
271
- edit. add_tabstop_after ( cap, arm) ;
272
- }
273
- }
250
+ let new_match_arm_list = make. match_arm_list ( arms) ;
274
251
275
- // FIXME: Hack for mutable syntax trees not having great support for macros
252
+ // FIXME: Hack for syntax trees not having great support for macros
276
253
// Just replace the element that the original range came from
277
254
let old_place = {
278
255
// Find the original element
279
256
let file = ctx. sema . parse ( arm_list_range. file_id ) ;
280
257
let old_place = file. syntax ( ) . covering_element ( arm_list_range. range ) ;
281
258
282
- // Make `old_place` mut
283
259
match old_place {
284
- syntax:: SyntaxElement :: Node ( it) => {
285
- syntax:: SyntaxElement :: from ( edit. make_syntax_mut ( it) )
286
- }
260
+ syntax:: SyntaxElement :: Node ( it) => it,
287
261
syntax:: SyntaxElement :: Token ( it) => {
288
262
// If a token is found, it is '{' or '}'
289
263
// The parent is `{ ... }`
290
- let parent = it. parent ( ) . expect ( "Token must have a parent." ) ;
291
- syntax:: SyntaxElement :: from ( edit. make_syntax_mut ( parent) )
264
+ it. parent ( ) . expect ( "Token must have a parent." )
292
265
}
293
266
}
294
267
} ;
295
268
296
- syntax:: ted:: replace ( old_place, new_match_arm_list. syntax ( ) ) ;
269
+ let mut editor = builder. make_editor ( & old_place) ;
270
+ new_match_arm_list. indent ( IndentLevel :: from_node ( & old_place) ) ;
271
+ editor. replace ( old_place, new_match_arm_list. syntax ( ) ) ;
272
+
273
+ if let Some ( cap) = ctx. config . snippet_cap {
274
+ if let Some ( it) = new_match_arm_list
275
+ . arms ( )
276
+ . nth ( first_new_arm_idx)
277
+ . and_then ( |arm| arm. syntax ( ) . descendants ( ) . find_map ( ast:: WildcardPat :: cast) )
278
+ {
279
+ editor. add_annotation ( it. syntax ( ) , builder. make_placeholder_snippet ( cap) ) ;
280
+ }
281
+
282
+ for arm in new_match_arm_list. arms ( ) . skip ( first_new_arm_idx) {
283
+ if let Some ( expr) = arm. expr ( ) {
284
+ editor. add_annotation ( expr. syntax ( ) , builder. make_placeholder_snippet ( cap) ) ;
285
+ }
286
+ }
287
+
288
+ if let Some ( arm) = new_match_arm_list. arms ( ) . skip ( first_new_arm_idx) . last ( ) {
289
+ editor. add_annotation ( arm. syntax ( ) , builder. make_tabstop_after ( cap) ) ;
290
+ }
291
+ }
292
+
293
+ editor. add_mappings ( make. finish_with_mappings ( ) ) ;
294
+ builder. add_file_edits ( ctx. file_id ( ) , editor) ;
297
295
} ,
298
296
)
299
297
}
@@ -1377,6 +1375,9 @@ fn main() {
1377
1375
) ;
1378
1376
}
1379
1377
1378
+ // FIXME: Preserving comments is quite hard in the current transitional syntax editing model.
1379
+ // Once we migrate to new trivia model addressed in #6854, remove the ignore attribute.
1380
+ #[ ignore]
1380
1381
#[ test]
1381
1382
fn add_missing_match_arms_preserves_comments ( ) {
1382
1383
check_assist (
@@ -1405,6 +1406,9 @@ fn foo(a: A) {
1405
1406
) ;
1406
1407
}
1407
1408
1409
+ // FIXME: Preserving comments is quite hard in the current transitional syntax editing model.
1410
+ // Once we migrate to new trivia model addressed in #6854, remove the ignore attribute.
1411
+ #[ ignore]
1408
1412
#[ test]
1409
1413
fn add_missing_match_arms_preserves_comments_empty ( ) {
1410
1414
check_assist (
@@ -1502,10 +1506,10 @@ enum Test {
1502
1506
1503
1507
fn foo(t: Test) {
1504
1508
m!(match t {
1505
- Test::A => ${1:todo!()},
1506
- Test::B => ${2:todo!()},
1507
- Test::C => ${3:todo!()},$0
1508
- });
1509
+ Test::A => ${1:todo!()},
1510
+ Test::B => ${2:todo!()},
1511
+ Test::C => ${3:todo!()},$0
1512
+ });
1509
1513
}"# ,
1510
1514
) ;
1511
1515
}
0 commit comments