@@ -4,9 +4,11 @@ use std::iter;
44
55use hir:: { Adt , HasSource , Semantics } ;
66use ra_ide_db:: RootDatabase ;
7- use ra_syntax:: ast:: { self , edit:: IndentLevel , make, AstNode , NameOwner } ;
87
98use crate :: { Assist , AssistCtx , AssistId } ;
9+ use ra_syntax:: ast:: { self , edit:: IndentLevel , make, AstNode , NameOwner } ;
10+
11+ use ast:: { MatchArm , Pat } ;
1012
1113// Assist: fill_match_arms
1214//
@@ -36,16 +38,6 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> {
3638 let match_expr = ctx. find_node_at_offset :: < ast:: MatchExpr > ( ) ?;
3739 let match_arm_list = match_expr. match_arm_list ( ) ?;
3840
39- // We already have some match arms, so we don't provide any assists.
40- // Unless if there is only one trivial match arm possibly created
41- // by match postfix complete. Trivial match arm is the catch all arm.
42- let mut existing_arms = match_arm_list. arms ( ) ;
43- if let Some ( arm) = existing_arms. next ( ) {
44- if !is_trivial ( & arm) || existing_arms. next ( ) . is_some ( ) {
45- return None ;
46- }
47- } ;
48-
4941 let expr = match_expr. expr ( ) ?;
5042 let enum_def = resolve_enum_def ( & ctx. sema , & expr) ?;
5143 let module = ctx. sema . scope ( expr. syntax ( ) ) . module ( ) ?;
@@ -55,30 +47,54 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> {
5547 return None ;
5648 }
5749
50+ let mut arms: Vec < MatchArm > = match_arm_list. arms ( ) . collect ( ) ;
51+ if arms. len ( ) == 1 {
52+ if let Some ( Pat :: PlaceholderPat ( ..) ) = arms[ 0 ] . pat ( ) {
53+ arms. clear ( ) ;
54+ }
55+ }
56+
5857 let db = ctx. db ;
58+ let missing_arms: Vec < MatchArm > = variants
59+ . into_iter ( )
60+ . filter_map ( |variant| build_pat ( db, module, variant) )
61+ . filter ( |variant_pat| is_variant_missing ( & mut arms, variant_pat) )
62+ . map ( |pat| make:: match_arm ( iter:: once ( pat) , make:: expr_unit ( ) ) )
63+ . collect ( ) ;
64+
65+ if missing_arms. is_empty ( ) {
66+ return None ;
67+ }
5968
6069 ctx. add_assist ( AssistId ( "fill_match_arms" ) , "Fill match arms" , |edit| {
61- let indent_level = IndentLevel :: from_node ( match_arm_list . syntax ( ) ) ;
70+ arms . extend ( missing_arms ) ;
6271
63- let new_arm_list = {
64- let arms = variants
65- . into_iter ( )
66- . filter_map ( |variant| build_pat ( db, module, variant) )
67- . map ( |pat| make:: match_arm ( iter:: once ( pat) , make:: expr_unit ( ) ) ) ;
68- indent_level. increase_indent ( make:: match_arm_list ( arms) )
69- } ;
72+ let indent_level = IndentLevel :: from_node ( match_arm_list. syntax ( ) ) ;
73+ let new_arm_list = indent_level. increase_indent ( make:: match_arm_list ( arms) ) ;
7074
7175 edit. target ( match_expr. syntax ( ) . text_range ( ) ) ;
7276 edit. set_cursor ( expr. syntax ( ) . text_range ( ) . start ( ) ) ;
7377 edit. replace_ast ( match_arm_list, new_arm_list) ;
7478 } )
7579}
7680
77- fn is_trivial ( arm : & ast:: MatchArm ) -> bool {
78- match arm. pat ( ) {
79- Some ( ast:: Pat :: PlaceholderPat ( ..) ) => true ,
80- _ => false ,
81- }
81+ fn is_variant_missing ( existing_arms : & mut Vec < MatchArm > , var : & Pat ) -> bool {
82+ existing_arms. iter ( ) . filter_map ( |arm| arm. pat ( ) ) . all ( |pat| {
83+ // Special casee OrPat as separate top-level pats
84+ let top_level_pats: Vec < Pat > = match pat {
85+ Pat :: OrPat ( pats) => pats. pats ( ) . collect :: < Vec < _ > > ( ) ,
86+ _ => vec ! [ pat] ,
87+ } ;
88+
89+ !top_level_pats. iter ( ) . any ( |pat| does_pat_match_variant ( pat, var) )
90+ } )
91+ }
92+
93+ fn does_pat_match_variant ( pat : & Pat , var : & Pat ) -> bool {
94+ let pat_head = pat. syntax ( ) . first_child ( ) . map ( |node| node. text ( ) ) ;
95+ let var_head = var. syntax ( ) . first_child ( ) . map ( |node| node. text ( ) ) ;
96+
97+ pat_head == var_head
8298}
8399
84100fn resolve_enum_def ( sema : & Semantics < RootDatabase > , expr : & ast:: Expr ) -> Option < hir:: Enum > {
@@ -110,10 +126,146 @@ fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> O
110126
111127#[ cfg( test) ]
112128mod tests {
113- use crate :: helpers:: { check_assist, check_assist_target} ;
129+ use crate :: helpers:: { check_assist, check_assist_not_applicable , check_assist_target} ;
114130
115131 use super :: fill_match_arms;
116132
133+ #[ test]
134+ fn all_match_arms_provided ( ) {
135+ check_assist_not_applicable (
136+ fill_match_arms,
137+ r#"
138+ enum A {
139+ As,
140+ Bs{x:i32, y:Option<i32>},
141+ Cs(i32, Option<i32>),
142+ }
143+ fn main() {
144+ match A::As<|> {
145+ A::As,
146+ A::Bs{x,y:Some(_)} => (),
147+ A::Cs(_, Some(_)) => (),
148+ }
149+ }
150+ "# ,
151+ ) ;
152+ }
153+
154+ #[ test]
155+ fn partial_fill_record_tuple ( ) {
156+ check_assist (
157+ fill_match_arms,
158+ r#"
159+ enum A {
160+ As,
161+ Bs{x:i32, y:Option<i32>},
162+ Cs(i32, Option<i32>),
163+ }
164+ fn main() {
165+ match A::As<|> {
166+ A::Bs{x,y:Some(_)} => (),
167+ A::Cs(_, Some(_)) => (),
168+ }
169+ }
170+ "# ,
171+ r#"
172+ enum A {
173+ As,
174+ Bs{x:i32, y:Option<i32>},
175+ Cs(i32, Option<i32>),
176+ }
177+ fn main() {
178+ match <|>A::As {
179+ A::Bs{x,y:Some(_)} => (),
180+ A::Cs(_, Some(_)) => (),
181+ A::As => (),
182+ }
183+ }
184+ "# ,
185+ ) ;
186+ }
187+
188+ #[ test]
189+ fn partial_fill_or_pat ( ) {
190+ check_assist (
191+ fill_match_arms,
192+ r#"
193+ enum A {
194+ As,
195+ Bs,
196+ Cs(Option<i32>),
197+ }
198+ fn main() {
199+ match A::As<|> {
200+ A::Cs(_) | A::Bs => (),
201+ }
202+ }
203+ "# ,
204+ r#"
205+ enum A {
206+ As,
207+ Bs,
208+ Cs(Option<i32>),
209+ }
210+ fn main() {
211+ match <|>A::As {
212+ A::Cs(_) | A::Bs => (),
213+ A::As => (),
214+ }
215+ }
216+ "# ,
217+ ) ;
218+ }
219+
220+ #[ test]
221+ fn partial_fill ( ) {
222+ check_assist (
223+ fill_match_arms,
224+ r#"
225+ enum A {
226+ As,
227+ Bs,
228+ Cs,
229+ Ds(String),
230+ Es(B),
231+ }
232+ enum B {
233+ Xs,
234+ Ys,
235+ }
236+ fn main() {
237+ match A::As<|> {
238+ A::Bs if 0 < 1 => (),
239+ A::Ds(_value) => (),
240+ A::Es(B::Xs) => (),
241+ }
242+ }
243+ "# ,
244+ r#"
245+ enum A {
246+ As,
247+ Bs,
248+ Cs,
249+ Ds(String),
250+ Es(B),
251+ }
252+ enum B {
253+ Xs,
254+ Ys,
255+ }
256+ fn main() {
257+ match <|>A::As {
258+ A::Bs if 0 < 1 => (),
259+ A::Ds(_value) => (),
260+ A::Es(B::Xs) => (),
261+ A::As => (),
262+ A::Cs => (),
263+ }
264+ }
265+ "# ,
266+ ) ;
267+ }
268+
117269 #[ test]
118270 fn fill_match_arms_empty_body ( ) {
119271 check_assist (
0 commit comments