@@ -4,9 +4,11 @@ use std::iter;
4
4
5
5
use hir:: { Adt , HasSource , Semantics } ;
6
6
use ra_ide_db:: RootDatabase ;
7
- use ra_syntax:: ast:: { self , edit:: IndentLevel , make, AstNode , NameOwner } ;
8
7
9
8
use crate :: { Assist , AssistCtx , AssistId } ;
9
+ use ra_syntax:: ast:: { self , edit:: IndentLevel , make, AstNode , NameOwner } ;
10
+
11
+ use ast:: { MatchArm , Pat } ;
10
12
11
13
// Assist: fill_match_arms
12
14
//
@@ -36,16 +38,6 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> {
36
38
let match_expr = ctx. find_node_at_offset :: < ast:: MatchExpr > ( ) ?;
37
39
let match_arm_list = match_expr. match_arm_list ( ) ?;
38
40
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
-
49
41
let expr = match_expr. expr ( ) ?;
50
42
let enum_def = resolve_enum_def ( & ctx. sema , & expr) ?;
51
43
let module = ctx. sema . scope ( expr. syntax ( ) ) . module ( ) ?;
@@ -55,30 +47,54 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> {
55
47
return None ;
56
48
}
57
49
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
+
58
57
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
+ }
59
68
60
69
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 ) ;
62
71
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) ) ;
70
74
71
75
edit. target ( match_expr. syntax ( ) . text_range ( ) ) ;
72
76
edit. set_cursor ( expr. syntax ( ) . text_range ( ) . start ( ) ) ;
73
77
edit. replace_ast ( match_arm_list, new_arm_list) ;
74
78
} )
75
79
}
76
80
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
82
98
}
83
99
84
100
fn 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
110
126
111
127
#[ cfg( test) ]
112
128
mod tests {
113
- use crate :: helpers:: { check_assist, check_assist_target} ;
129
+ use crate :: helpers:: { check_assist, check_assist_not_applicable , check_assist_target} ;
114
130
115
131
use super :: fill_match_arms;
116
132
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
+
117
269
#[ test]
118
270
fn fill_match_arms_empty_body ( ) {
119
271
check_assist (
0 commit comments