10
10
11
11
#![ cfg( not( test) ) ]
12
12
13
+ #[ macro_use]
14
+ extern crate cfg_if;
13
15
#[ macro_use]
14
16
extern crate log;
17
+ #[ cfg( feature = "experimental-file-lines" ) ]
18
+ #[ macro_use]
19
+ extern crate nom;
20
+
15
21
extern crate rustfmt;
16
22
extern crate toml;
17
23
extern crate env_logger;
18
24
extern crate getopts;
19
25
20
26
use rustfmt:: { run, run_from_stdin} ;
21
27
use rustfmt:: config:: { Config , WriteMode } ;
28
+ #[ cfg( feature = "experimental-file-lines" ) ]
29
+ use rustfmt:: config:: FileLinesMap ;
22
30
23
31
use std:: env;
24
32
use std:: fs:: { self , File } ;
@@ -42,6 +50,11 @@ enum Operation {
42
50
InvalidInput ( String ) ,
43
51
/// No file specified, read from stdin
44
52
Stdin ( String , Option < PathBuf > ) ,
53
+ /// Format a set of line ranges.
54
+ #[ cfg( feature = "experimental-file-lines" ) ]
55
+ FormatLineRanges {
56
+ file_lines_map : FileLinesMap ,
57
+ } ,
45
58
}
46
59
47
60
/// Try to find a project file in the given directory and its parents. Returns the path of a the
@@ -112,7 +125,9 @@ fn match_cli_path_or_file(config_path: Option<PathBuf>,
112
125
113
126
fn update_config ( config : & mut Config , matches : & Matches ) -> Result < ( ) , String > {
114
127
config. verbose = matches. opt_present ( "verbose" ) ;
115
- config. skip_children = matches. opt_present ( "skip-children" ) ;
128
+ // `file-lines` implies `skip-children`.
129
+ config. skip_children = matches. opt_present ( "skip-children" ) ||
130
+ matches. opt_present ( "file-lines" ) ;
116
131
117
132
let write_mode = matches. opt_str ( "write-mode" ) ;
118
133
match matches. opt_str ( "write-mode" ) . map ( |wm| WriteMode :: from_str ( & wm) ) {
@@ -144,6 +159,13 @@ fn execute() -> i32 {
144
159
"Recursively searches the given path for the rustfmt.toml config file. If not \
145
160
found reverts to the input file path",
146
161
"[Path for the configuration file]" ) ;
162
+ if cfg ! ( feature = "experimental-file-lines" ) {
163
+ opts. optmulti ( "" ,
164
+ "file-lines" ,
165
+ "Format specified line RANGEs in FILENAME. RANGEs are inclusive of both \
166
+ endpoints. May be specified multiple times.",
167
+ "FILENAME:RANGE,RANGE,..." ) ;
168
+ }
147
169
148
170
let matches = match opts. parse ( env:: args ( ) . skip ( 1 ) ) {
149
171
Ok ( m) => m,
@@ -220,6 +242,28 @@ fn execute() -> i32 {
220
242
}
221
243
0
222
244
}
245
+ #[ cfg( feature = "experimental-file-lines" ) ]
246
+ // TODO: figure out what to do with config_path.
247
+ Operation :: FormatLineRanges { file_lines_map } => {
248
+ for ( file, line_ranges) in file_lines_map {
249
+ let ( mut config, config_path) = resolve_config ( file. parent ( ) . unwrap ( ) )
250
+ . expect ( & format ! ( "Error resolving config \
251
+ for {}",
252
+ file. display( ) ) ) ;
253
+ if let Some ( path) = config_path. as_ref ( ) {
254
+ println ! ( "Using rustfmt config file {} for {}" ,
255
+ path. display( ) ,
256
+ file. display( ) ) ;
257
+ }
258
+ if let Err ( e) = update_config ( & mut config, & matches) {
259
+ print_usage ( & opts, & e) ;
260
+ return 1 ;
261
+ }
262
+ config. line_ranges = line_ranges;
263
+ run ( & file, & config) ;
264
+ }
265
+ 0
266
+ }
223
267
}
224
268
}
225
269
@@ -275,6 +319,47 @@ fn determine_operation(matches: &Matches) -> Operation {
275
319
Some ( dir)
276
320
} ) ;
277
321
322
+ if matches. opt_present ( "file-lines" ) {
323
+ cfg_if ! {
324
+ if #[ cfg( feature = "experimental-file-lines" ) ] {
325
+ fn handle_file_lines( args: & [ String ] ) -> Operation {
326
+ use rustfmt:: config:: { FileLinesMap , LineRanges } ;
327
+
328
+ use nom:: IResult ;
329
+
330
+ let mut file_lines_map = FileLinesMap :: new( ) ;
331
+ for range_spec in args {
332
+ let res = parse:: file_lines_arg( range_spec) ;
333
+
334
+ let invalid = || {
335
+ Operation :: InvalidInput ( format!( "invalid file-lines argument: {}" ,
336
+ range_spec) )
337
+ } ;
338
+
339
+ let ( file, line_ranges) = match res {
340
+ IResult :: Error ( _) => return invalid( ) ,
341
+ IResult :: Incomplete ( _) => return invalid( ) ,
342
+ IResult :: Done ( remaining, _) if !remaining. is_empty( ) => {
343
+ return invalid( )
344
+ }
345
+ IResult :: Done ( _, ( file, line_ranges) ) => ( file, line_ranges) ,
346
+ } ;
347
+
348
+ let entry = file_lines_map. entry( file) . or_insert( LineRanges ( Vec :: new( ) ) ) ;
349
+ entry. 0 . extend( line_ranges. 0 ) ;
350
+ }
351
+ return Operation :: FormatLineRanges { file_lines_map: file_lines_map } ;
352
+ }
353
+ } else {
354
+ fn handle_file_lines( _: & [ String ] ) -> ! {
355
+ unimplemented!( ) ;
356
+ }
357
+ }
358
+ }
359
+
360
+ return handle_file_lines ( & matches. opt_strs ( "file-lines" ) ) ;
361
+ }
362
+
278
363
// if no file argument is supplied, read from stdin
279
364
if matches. free . is_empty ( ) {
280
365
@@ -291,3 +376,60 @@ fn determine_operation(matches: &Matches) -> Operation {
291
376
292
377
Operation :: Format ( files, config_path)
293
378
}
379
+
380
+
381
+ /// Parser for the `file-lines` argument.
382
+ #[ cfg( feature = "experimental-file-lines" ) ]
383
+ mod parse {
384
+ use std:: path:: PathBuf ;
385
+ use std:: str:: FromStr ;
386
+ use rustfmt:: config:: { LineRange , LineRanges } ;
387
+
388
+ use nom:: digit;
389
+
390
+ named ! ( pub file_lines_arg<& str , ( PathBuf , LineRanges ) >,
391
+ chain!(
392
+ file: map!(
393
+ is_not_s!( ":" ) ,
394
+ PathBuf :: from
395
+ ) ~
396
+ tag_s!( ":" ) ~
397
+ line_ranges: line_ranges,
398
+ || ( file, line_ranges)
399
+ )
400
+ ) ;
401
+
402
+ named ! ( usize_digit<& str , usize >,
403
+ map_res!(
404
+ digit,
405
+ FromStr :: from_str
406
+ )
407
+ ) ;
408
+
409
+ named ! ( line_range<& str , LineRange >,
410
+ map_res!(
411
+ separated_pair!(
412
+ usize_digit,
413
+ tag_s!( "-" ) ,
414
+ usize_digit
415
+ ) ,
416
+ |pair: ( usize , usize ) | {
417
+ let ( start, end) = pair;
418
+ if end < start {
419
+ return Err ( format!( "empty line range: {}-{}" , start, end) ) ;
420
+ }
421
+ Ok ( pair)
422
+ }
423
+ )
424
+ ) ;
425
+
426
+ named ! ( line_ranges<& str , LineRanges >,
427
+ map!(
428
+ separated_nonempty_list!(
429
+ tag_s!( "," ) ,
430
+ line_range
431
+ ) ,
432
+ LineRanges
433
+ )
434
+ ) ;
435
+ }
0 commit comments