@@ -6,7 +6,9 @@ use ide_db::syntax_helpers::suggest_name;
66use ide_db:: RootDatabase ;
77use ide_db:: { famous_defs:: FamousDefs , helpers:: mod_path_to_ast} ;
88use 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 ;
1012use syntax:: ast:: { self , make, AstNode , MatchArmList , MatchExpr , Pat } ;
1113
1214use crate :: { utils, AssistContext , AssistId , AssistKind , Assists } ;
@@ -200,8 +202,8 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
200202 AssistId ( "add_missing_match_arms" , AssistKind :: QuickFix ) ,
201203 "Fill match arms" ,
202204 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 ( ) ;
205207
206208 // having any hidden variants means that we need a catch-all arm
207209 needs_catch_all_arm |= has_hidden_variants;
@@ -211,89 +213,85 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
211213 // filter out hidden patterns because they're handled by the catch-all arm
212214 !hidden
213215 } )
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 ( ) ) ) ;
217217
218- let catch_all_arm = new_match_arm_list
218+ let mut arms : Vec < _ > = match_arm_list
219219 . 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
225237 }
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 ( ) ;
235240
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) ;
242243
243244 if needs_catch_all_arm && !has_catch_all_arm {
244245 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) ;
257248 }
258249
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) ;
274251
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
276253 // Just replace the element that the original range came from
277254 let old_place = {
278255 // Find the original element
279256 let file = ctx. sema . parse ( arm_list_range. file_id ) ;
280257 let old_place = file. syntax ( ) . covering_element ( arm_list_range. range ) ;
281258
282- // Make `old_place` mut
283259 match old_place {
284- syntax:: SyntaxElement :: Node ( it) => {
285- syntax:: SyntaxElement :: from ( edit. make_syntax_mut ( it) )
286- }
260+ syntax:: SyntaxElement :: Node ( it) => it,
287261 syntax:: SyntaxElement :: Token ( it) => {
288262 // If a token is found, it is '{' or '}'
289263 // 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." )
292265 }
293266 }
294267 } ;
295268
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) ;
297295 } ,
298296 )
299297}
@@ -1502,10 +1500,10 @@ enum Test {
15021500
15031501fn foo(t: Test) {
15041502 m!(match t {
1505- Test::A => ${1:todo!()},
1506- Test::B => ${2:todo!()},
1507- Test::C => ${3:todo!()},$0
1508- });
1503+ Test::A => ${1:todo!()},
1504+ Test::B => ${2:todo!()},
1505+ Test::C => ${3:todo!()},$0
1506+ });
15091507}"# ,
15101508 ) ;
15111509 }
0 commit comments