Skip to content

Commit 556ee52

Browse files
committed
rustfmt: Add an ineffectual --file-lines flag behind a feature
This adds a `--file-lines` flag that is behind the Cargo feature `experimental-file-lines`. The argument is parsed and propagated, 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 e557450 commit 556ee52

File tree

3 files changed

+160
-1
lines changed

3 files changed

+160
-1
lines changed

Cargo.lock

Lines changed: 12 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: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ include = ["src/*.rs", "Cargo.toml"]
1212
[features]
1313
default = ["cargo-fmt"]
1414
cargo-fmt = []
15+
# Feature for the in-development --file-lines argument, limiting formatting to
16+
# specified line ranges.
17+
experimental-file-lines = ["nom"]
1518

1619
[dependencies]
1720
toml = "0.1.20"
@@ -25,3 +28,5 @@ syntex_syntax = "0.23.0"
2528
log = "0.3.2"
2629
env_logger = "0.3.1"
2730
getopts = "0.2"
31+
cfg-if = "0.1.0"
32+
nom = { version = "1.2.1", optional = true }

src/bin/rustfmt.rs

Lines changed: 143 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,23 @@
1010

1111
#![cfg(not(test))]
1212

13+
#[macro_use]
14+
extern crate cfg_if;
1315
#[macro_use]
1416
extern crate log;
17+
#[cfg(feature = "experimental-file-lines")]
18+
#[macro_use]
19+
extern crate nom;
20+
1521
extern crate rustfmt;
1622
extern crate toml;
1723
extern crate env_logger;
1824
extern crate getopts;
1925

2026
use rustfmt::{run, run_from_stdin};
2127
use rustfmt::config::{Config, WriteMode};
28+
#[cfg(feature = "experimental-file-lines")]
29+
use rustfmt::config::FileLinesMap;
2230

2331
use std::env;
2432
use std::fs::{self, File};
@@ -42,6 +50,11 @@ enum Operation {
4250
InvalidInput(String),
4351
/// No file specified, read from stdin
4452
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+
},
4558
}
4659

4760
/// 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>,
112125

113126
fn update_config(config: &mut Config, matches: &Matches) -> Result<(), String> {
114127
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");
116131

117132
let write_mode = matches.opt_str("write-mode");
118133
match matches.opt_str("write-mode").map(|wm| WriteMode::from_str(&wm)) {
@@ -144,6 +159,13 @@ fn execute() -> i32 {
144159
"Recursively searches the given path for the rustfmt.toml config file. If not \
145160
found reverts to the input file path",
146161
"[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+
}
147169

148170
let matches = match opts.parse(env::args().skip(1)) {
149171
Ok(m) => m,
@@ -220,6 +242,28 @@ fn execute() -> i32 {
220242
}
221243
0
222244
}
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+
}
223267
}
224268
}
225269

@@ -275,6 +319,47 @@ fn determine_operation(matches: &Matches) -> Operation {
275319
Some(dir)
276320
});
277321

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+
278363
// if no file argument is supplied, read from stdin
279364
if matches.free.is_empty() {
280365

@@ -291,3 +376,60 @@ fn determine_operation(matches: &Matches) -> Operation {
291376

292377
Operation::Format(files, config_path)
293378
}
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

Comments
 (0)