11
11
12
12
use std:: { collections:: HashMap , str:: FromStr } ;
13
13
14
- use biome_deserialize:: StringSet ;
15
14
use biome_diagnostics:: { adapters:: IniError , Error } ;
16
15
use biome_formatter:: { IndentWidth , LineEnding , LineWidth } ;
17
- use indexmap:: IndexSet ;
18
16
use serde:: { Deserialize , Deserializer } ;
19
17
20
18
use crate :: {
@@ -50,13 +48,23 @@ impl EditorConfig {
50
48
formatter : self . options . remove ( "*" ) . map ( |o| o. to_biome ( ) ) ,
51
49
..Default :: default ( )
52
50
} ;
51
+ let mut errors = vec ! [ ] ;
53
52
let overrides: Vec < _ > = self
54
53
. options
55
54
. into_iter ( )
56
- . map ( |( k, v) | OverridePattern {
57
- include : Some ( StringSet :: new ( IndexSet :: from ( [ k] ) ) ) ,
58
- formatter : Some ( v. to_biome_override ( ) ) ,
59
- ..Default :: default ( )
55
+ . map ( |( k, v) | {
56
+ let patterns = match expand_unknown_glob_patterns ( & k) {
57
+ Ok ( patterns) => patterns,
58
+ Err ( err) => {
59
+ errors. push ( err) ;
60
+ vec ! [ k]
61
+ }
62
+ } ;
63
+ OverridePattern {
64
+ include : Some ( patterns. into_iter ( ) . collect ( ) ) ,
65
+ formatter : Some ( v. to_biome_override ( ) ) ,
66
+ ..Default :: default ( )
67
+ }
60
68
} )
61
69
. collect ( ) ;
62
70
config. overrides = Some ( Overrides ( overrides) ) ;
@@ -65,15 +73,7 @@ impl EditorConfig {
65
73
}
66
74
67
75
fn validate ( & self ) -> Vec < EditorConfigDiagnostic > {
68
- let mut errors: Vec < _ > = self . options . values ( ) . flat_map ( |o| o. validate ( ) ) . collect ( ) ;
69
-
70
- // biome doesn't currently support all the glob patterns that .editorconfig does
71
- errors. extend (
72
- self . options
73
- . keys ( )
74
- . filter ( |k| k. contains ( '{' ) || k. contains ( '}' ) )
75
- . map ( |pattern| EditorConfigDiagnostic :: unknown_glob_pattern ( pattern. clone ( ) ) ) ,
76
- ) ;
76
+ let errors: Vec < _ > = self . options . values ( ) . flat_map ( |o| o. validate ( ) ) . collect ( ) ;
77
77
78
78
errors
79
79
}
@@ -170,6 +170,142 @@ where
170
170
. map ( Some )
171
171
}
172
172
173
+ /// Turn an unknown glob pattern into a list of known glob patterns. This is part of a hack to support all editorconfig patterns.
174
+ ///
175
+ /// TODO: remove in biome 2.0
176
+ fn expand_unknown_glob_patterns ( pattern : & str ) -> Result < Vec < String > , EditorConfigDiagnostic > {
177
+ struct Variants {
178
+ /// index of the { character
179
+ start : usize ,
180
+ /// index of the } character
181
+ end : usize ,
182
+ variants : Option < VariantType > ,
183
+ }
184
+
185
+ impl Variants {
186
+ fn new ( start : usize ) -> Self {
187
+ Self {
188
+ start,
189
+ end : start,
190
+ variants : None ,
191
+ }
192
+ }
193
+
194
+ fn parse_to_variants ( & mut self , s : & str ) -> Result < ( ) , EditorConfigDiagnostic > {
195
+ let s = s. trim_start_matches ( '{' ) . trim_end_matches ( '}' ) ;
196
+ if s. contains ( ".." ) {
197
+ let mut parts = s. split ( ".." ) ;
198
+ let start = parts. next ( ) . ok_or_else ( || {
199
+ EditorConfigDiagnostic :: invalid_glob_pattern (
200
+ s,
201
+ "Range pattern must have exactly two parts" ,
202
+ )
203
+ } ) ?;
204
+ let end = parts. next ( ) . ok_or_else ( || {
205
+ EditorConfigDiagnostic :: invalid_glob_pattern (
206
+ s,
207
+ "Range pattern must have exactly two parts" ,
208
+ )
209
+ } ) ?;
210
+ if parts. next ( ) . is_some ( ) {
211
+ return Err ( EditorConfigDiagnostic :: invalid_glob_pattern (
212
+ s,
213
+ "Range pattern must have exactly two parts" ,
214
+ ) ) ;
215
+ }
216
+
217
+ let start = start. parse ( ) . map_err ( |err| {
218
+ EditorConfigDiagnostic :: invalid_glob_pattern (
219
+ s,
220
+ format ! ( "Error parsing the start of the range: {}" , err) ,
221
+ )
222
+ } ) ?;
223
+ let end = end. parse ( ) . map_err ( |err| {
224
+ EditorConfigDiagnostic :: invalid_glob_pattern (
225
+ s,
226
+ format ! ( "Error parsing the end of the range: {}" , err) ,
227
+ )
228
+ } ) ?;
229
+ self . variants = Some ( VariantType :: Range ( ( start, end) ) ) ;
230
+ } else {
231
+ self . variants = Some ( VariantType :: List (
232
+ s. split ( ',' ) . map ( |s| s. to_string ( ) ) . collect ( ) ,
233
+ ) ) ;
234
+ }
235
+
236
+ Ok ( ( ) )
237
+ }
238
+
239
+ fn variants ( & self ) -> Vec < String > {
240
+ match & self . variants {
241
+ Some ( VariantType :: List ( ref list) ) => list. clone ( ) ,
242
+ Some ( VariantType :: Range ( ( start, end) ) ) => {
243
+ let mut variants = vec ! [ ] ;
244
+ for i in * start..=* end {
245
+ variants. push ( i. to_string ( ) ) ;
246
+ }
247
+ variants
248
+ }
249
+ None => vec ! [ ] ,
250
+ }
251
+ }
252
+ }
253
+
254
+ enum VariantType {
255
+ List ( Vec < String > ) ,
256
+ Range ( ( i64 , i64 ) ) ,
257
+ }
258
+
259
+ let mut all_variants = vec ! [ ] ;
260
+ let mut current_variants = None ;
261
+ for ( i, c) in pattern. chars ( ) . enumerate ( ) {
262
+ match c {
263
+ '{' => {
264
+ if current_variants. is_none ( ) {
265
+ current_variants = Some ( Variants :: new ( i) ) ;
266
+ } else {
267
+ // TODO: error, recursive brace expansion is not supported
268
+ }
269
+ }
270
+ '}' => {
271
+ if let Some ( mut v) = current_variants. take ( ) {
272
+ v. end = i;
273
+ v. parse_to_variants ( & pattern[ v. start ..=v. end ] ) ?;
274
+ all_variants. push ( v) ;
275
+ }
276
+ }
277
+ _ => { }
278
+ }
279
+ }
280
+
281
+ if all_variants. is_empty ( ) {
282
+ return Ok ( vec ! [ pattern. to_string( ) ] ) ;
283
+ }
284
+
285
+ let mut expanded_patterns = vec ! [ ] ;
286
+ for variants in all_variants. iter ( ) . rev ( ) {
287
+ if expanded_patterns. is_empty ( ) {
288
+ for variant in & variants. variants ( ) {
289
+ let mut pattern = pattern. to_string ( ) ;
290
+ pattern. replace_range ( variants. start ..=variants. end , variant) ;
291
+ expanded_patterns. push ( pattern) ;
292
+ }
293
+ } else {
294
+ let mut new_patterns = vec ! [ ] ;
295
+ for existing in & expanded_patterns {
296
+ for variant in & variants. variants ( ) {
297
+ let mut pattern = existing. clone ( ) ;
298
+ pattern. replace_range ( variants. start ..=variants. end , variant) ;
299
+ new_patterns. push ( pattern) ;
300
+ }
301
+ }
302
+ expanded_patterns = new_patterns;
303
+ }
304
+ }
305
+
306
+ Ok ( expanded_patterns)
307
+ }
308
+
173
309
#[ cfg( test) ]
174
310
mod tests {
175
311
use super :: * ;
@@ -256,20 +392,46 @@ insert_final_newline = false
256
392
}
257
393
258
394
#[ test]
259
- fn should_emit_diagnostic_glob_pattern ( ) {
260
- let input = r#"
261
- root = true
395
+ fn should_expand_glob_pattern_list ( ) {
396
+ let pattern = "package.json" ;
397
+ let mut expanded =
398
+ expand_unknown_glob_patterns ( pattern) . expect ( "Failed to expand glob pattern" ) ;
399
+ expanded. sort ( ) ;
400
+ assert_eq ! ( expanded, vec![ "package.json" ] ) ;
401
+
402
+ let pattern = "{package.json,.travis.yml}" ;
403
+ let mut expanded =
404
+ expand_unknown_glob_patterns ( pattern) . expect ( "Failed to expand glob pattern" ) ;
405
+ expanded. sort ( ) ;
406
+ assert_eq ! ( expanded, vec![ ".travis.yml" , "package.json" ] ) ;
407
+ }
262
408
263
- [{package.json,.travis.yml}]
264
- indent_style = space
265
- "# ;
409
+ #[ test]
410
+ fn should_expand_glob_pattern_list_2 ( ) {
411
+ let pattern = "**/{foo,bar}.{test,spec}.js" ;
412
+ let mut expanded =
413
+ expand_unknown_glob_patterns ( pattern) . expect ( "Failed to expand glob pattern" ) ;
414
+ expanded. sort ( ) ;
415
+ assert_eq ! (
416
+ expanded,
417
+ vec![
418
+ "**/bar.spec.js" ,
419
+ "**/bar.test.js" ,
420
+ "**/foo.spec.js" ,
421
+ "**/foo.test.js" ,
422
+ ]
423
+ ) ;
424
+ }
266
425
267
- let conf = parse_str ( input) . expect ( "Failed to parse editorconfig" ) ;
268
- let ( _, errors) = conf. to_biome ( ) ;
269
- assert_eq ! ( errors. len( ) , 1 ) ;
270
- assert ! ( matches!(
271
- errors[ 0 ] ,
272
- EditorConfigDiagnostic :: UnknownGlobPattern ( _)
273
- ) ) ;
426
+ #[ test]
427
+ fn should_expand_glob_pattern_range ( ) {
428
+ let pattern = "**/bar.{1..4}.js" ;
429
+ let mut expanded =
430
+ expand_unknown_glob_patterns ( pattern) . expect ( "Failed to expand glob pattern" ) ;
431
+ expanded. sort ( ) ;
432
+ assert_eq ! (
433
+ expanded,
434
+ vec![ "**/bar.1.js" , "**/bar.2.js" , "**/bar.3.js" , "**/bar.4.js" , ]
435
+ ) ;
274
436
}
275
437
}
0 commit comments