Skip to content

Commit ffdf9bd

Browse files
committed
rustfmt: Add --file-lines flag behind an env var
This adds a `--file-lines` flag that is present if the RUSTFMT_EXPERIMENTAL_FILE_LINES environment variable is set to "1". The argument is parsed and propagated into the config, but so far it is only an alternative and verbose way to specify files and set `skip_children`. Refs https://github.com/nrc/rustfmt/issues/434
1 parent fd297b3 commit ffdf9bd

File tree

3 files changed

+134
-1
lines changed

3 files changed

+134
-1
lines changed

Cargo.lock

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,4 @@ syntex_syntax = "0.23.0"
2525
log = "0.3.2"
2626
env_logger = "0.3.1"
2727
getopts = "0.2"
28+
nom = "1.2.1"

src/bin/rustfmt.rs

Lines changed: 127 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,19 @@
1212

1313
#[macro_use]
1414
extern crate log;
15+
#[macro_use]
16+
extern crate nom;
17+
1518
extern crate rustfmt;
1619
extern crate toml;
1720
extern crate env_logger;
1821
extern crate getopts;
1922

23+
use nom::IResult;
24+
2025
use rustfmt::{run, run_from_stdin};
2126
use rustfmt::config::{Config, WriteMode};
27+
use rustfmt::config::{FileLinesMap, LineRanges};
2228

2329
use std::env;
2430
use std::fs::{self, File};
@@ -50,6 +56,10 @@ enum Operation {
5056
input: String,
5157
config_path: Option<PathBuf>,
5258
},
59+
/// Format a set of line ranges.
60+
FormatLineRanges {
61+
file_lines_map: FileLinesMap,
62+
},
5363
}
5464

5565
/// Try to find a project file in the given directory and its parents. Returns the path of a the
@@ -120,7 +130,9 @@ fn match_cli_path_or_file(config_path: Option<PathBuf>,
120130

121131
fn update_config(config: &mut Config, matches: &Matches) -> Result<(), String> {
122132
config.verbose = matches.opt_present("verbose");
123-
config.skip_children = matches.opt_present("skip-children");
133+
// `file-lines` implies `skip-children`.
134+
config.skip_children = matches.opt_present("skip-children") ||
135+
(file_lines_enabled() && matches.opt_present("file-lines"));
124136

125137
let write_mode = matches.opt_str("write-mode");
126138
match matches.opt_str("write-mode").map(|wm| WriteMode::from_str(&wm)) {
@@ -133,6 +145,13 @@ fn update_config(config: &mut Config, matches: &Matches) -> Result<(), String> {
133145
}
134146
}
135147

148+
fn file_lines_enabled() -> bool {
149+
match env::var("RUSTFMT_EXPERIMENTAL_FILE_LINES") {
150+
Ok(ref v) if v == "1" => true,
151+
_ => false,
152+
}
153+
}
154+
136155
fn execute() -> i32 {
137156
let mut opts = Options::new();
138157
opts.optflag("h", "help", "show this message");
@@ -152,6 +171,13 @@ fn execute() -> i32 {
152171
"Recursively searches the given path for the rustfmt.toml config file. If not \
153172
found reverts to the input file path",
154173
"[Path for the configuration file]");
174+
if file_lines_enabled() {
175+
opts.optmulti("",
176+
"file-lines",
177+
"Format specified line RANGEs in FILE. RANGEs are inclusive of both \
178+
endpoints. May be specified multiple times.",
179+
"FILE:RANGE,RANGE,...");
180+
}
155181

156182
let matches = match opts.parse(env::args().skip(1)) {
157183
Ok(m) => m,
@@ -228,6 +254,27 @@ fn execute() -> i32 {
228254
}
229255
0
230256
}
257+
// TODO: figure out what to do with config_path.
258+
Operation::FormatLineRanges { file_lines_map } => {
259+
for (file, line_ranges) in file_lines_map {
260+
let (mut config, config_path) = resolve_config(file.parent().unwrap())
261+
.expect(&format!("Error resolving config \
262+
for {}",
263+
file.display()));
264+
if let Some(path) = config_path.as_ref() {
265+
println!("Using rustfmt config file {} for {}",
266+
path.display(),
267+
file.display());
268+
}
269+
if let Err(e) = update_config(&mut config, &matches) {
270+
print_usage(&opts, &e);
271+
return 1;
272+
}
273+
config.line_ranges = line_ranges;
274+
run(&file, &config);
275+
}
276+
0
277+
}
231278
}
232279
}
233280

@@ -283,6 +330,29 @@ fn determine_operation(matches: &Matches) -> Operation {
283330
Some(dir)
284331
});
285332

333+
if file_lines_enabled() && matches.opt_present("file-lines") {
334+
let file_lines = matches.opt_strs("file-lines");
335+
let mut file_lines_map = FileLinesMap::new();
336+
for range_spec in file_lines {
337+
let invalid = || {
338+
Operation::InvalidInput {
339+
reason: format!("invalid file-lines argument: {}", range_spec),
340+
}
341+
};
342+
343+
let (file, line_ranges) = match parse::file_lines_arg(&range_spec) {
344+
IResult::Error(_) |
345+
IResult::Incomplete(_) => return invalid(),
346+
IResult::Done(remaining, _) if !remaining.is_empty() => return invalid(),
347+
IResult::Done(_, (file, line_ranges)) => (file, line_ranges),
348+
};
349+
350+
let entry = file_lines_map.entry(file).or_insert(LineRanges(Vec::new()));
351+
entry.0.extend(line_ranges.0);
352+
}
353+
return Operation::FormatLineRanges { file_lines_map: file_lines_map };
354+
}
355+
286356
// if no file argument is supplied, read from stdin
287357
if matches.free.is_empty() {
288358

@@ -305,3 +375,59 @@ fn determine_operation(matches: &Matches) -> Operation {
305375
config_path: config_path,
306376
}
307377
}
378+
379+
380+
/// Parser for the `file-lines` argument.
381+
mod parse {
382+
use std::path::PathBuf;
383+
use std::str::FromStr;
384+
use rustfmt::config::{LineRange, LineRanges};
385+
386+
use nom::digit;
387+
388+
named!(pub file_lines_arg<&str, (PathBuf, LineRanges)>,
389+
chain!(
390+
file: map!(
391+
is_not_s!(":"),
392+
PathBuf::from
393+
) ~
394+
tag_s!(":") ~
395+
line_ranges: line_ranges,
396+
|| (file, line_ranges)
397+
)
398+
);
399+
400+
named!(usize_digit<&str, usize>,
401+
map_res!(
402+
digit,
403+
FromStr::from_str
404+
)
405+
);
406+
407+
named!(line_range<&str, LineRange>,
408+
map_res!(
409+
separated_pair!(
410+
usize_digit,
411+
tag_s!("-"),
412+
usize_digit
413+
),
414+
|pair| {
415+
let (lo, hi) = pair;
416+
if lo < hi {
417+
return Err(format!("empty line range: {}-{}", lo, hi));
418+
}
419+
Ok(LineRange { lo: lo, hi: hi })
420+
}
421+
)
422+
);
423+
424+
named!(line_ranges<&str, LineRanges>,
425+
map!(
426+
separated_nonempty_list!(
427+
tag_s!(","),
428+
line_range
429+
),
430+
LineRanges
431+
)
432+
);
433+
}

0 commit comments

Comments
 (0)