Merge pull request #558 from johannhof/master

Stdin support
This commit is contained in:
Nick Cameron 2015-11-03 03:39:21 -05:00
commit 3c60328df8
3 changed files with 83 additions and 13 deletions

View file

@ -17,7 +17,7 @@ extern crate toml;
extern crate env_logger;
extern crate getopts;
use rustfmt::{WriteMode, run};
use rustfmt::{WriteMode, run, run_from_stdin};
use rustfmt::config::Config;
use std::env;
@ -35,6 +35,8 @@ enum Operation {
Help,
/// Invalid program input, including reason.
InvalidInput(String),
/// No file specified, read from stdin
Stdin(String, WriteMode),
}
/// Try to find a project file in the input file directory and its parents.
@ -75,7 +77,7 @@ fn execute() -> i32 {
opts.optflag("h", "help", "show this message");
opts.optopt("",
"write-mode",
"mode to write in",
"mode to write in (not usable when piping from stdin)",
"[replace|overwrite|display|diff|coverage]");
let operation = determine_operation(&opts, env::args().skip(1));
@ -89,6 +91,18 @@ fn execute() -> i32 {
print_usage(&opts, "");
0
}
Operation::Stdin(input, write_mode) => {
// try to read config from local directory
let config = match lookup_and_read_project_file(&Path::new(".")) {
Ok((path, toml)) => {
Config::from_toml(&toml)
}
Err(_) => Default::default(),
};
run_from_stdin(input, write_mode, &config);
0
}
Operation::Format(file, write_mode) => {
let config = match lookup_and_read_project_file(&file) {
Ok((path, toml)) => {
@ -138,6 +152,19 @@ fn determine_operation<I>(opts: &Options, args: I) -> Operation
return Operation::Help;
}
// if no file argument is supplied, read from stdin
if matches.free.len() == 0 {
let mut buffer = String::new();
match io::stdin().read_to_string(&mut buffer) {
Ok(..) => (),
Err(e) => return Operation::InvalidInput(e.to_string()),
}
// WriteMode is always plain for Stdin
return Operation::Stdin(buffer, WriteMode::Plain);
}
let write_mode = match matches.opt_str("write-mode") {
Some(mode) => {
match mode.parse() {
@ -148,9 +175,5 @@ fn determine_operation<I>(opts: &Options, args: I) -> Operation
None => WriteMode::Replace,
};
if matches.free.len() != 1 {
return Operation::InvalidInput("Please provide one file to format".into());
}
Operation::Format(PathBuf::from(&matches.free[0]), write_mode)
}

View file

@ -46,11 +46,11 @@ pub fn write_all_files(file_map: &FileMap,
Ok(result)
}
fn write_file(text: &StringBuffer,
filename: &str,
mode: WriteMode,
config: &Config)
-> Result<Option<String>, io::Error> {
pub fn write_file(text: &StringBuffer,
filename: &str,
mode: WriteMode,
config: &Config)
-> Result<Option<String>, io::Error> {
// prints all newlines either as `\n` or as `\r\n`
fn write_system_newlines<T>(mut writer: T,
@ -100,11 +100,16 @@ fn write_file(text: &StringBuffer,
let file = try!(File::create(&filename));
try!(write_system_newlines(file, text, config));
}
WriteMode::Plain => {
let stdout = stdout();
let stdout = stdout.lock();
try!(write_system_newlines(stdout, text, config));
}
WriteMode::Display | WriteMode::Coverage => {
println!("{}:\n", filename);
let stdout = stdout();
let stdout_lock = stdout.lock();
try!(write_system_newlines(stdout_lock, text, config));
let stdout = stdout.lock();
try!(write_system_newlines(stdout, text, config));
}
WriteMode::Diff => {
println!("Diff of {}:\n", filename);

View file

@ -193,6 +193,8 @@ pub enum WriteMode {
Return,
// Display how much of the input file was processed
Coverage,
// Unfancy stdout
Plain,
}
impl FromStr for WriteMode {
@ -205,6 +207,7 @@ impl FromStr for WriteMode {
"overwrite" => Ok(WriteMode::Overwrite),
"diff" => Ok(WriteMode::Diff),
"coverage" => Ok(WriteMode::Coverage),
"plain" => Ok(WriteMode::Plain),
_ => Err(()),
}
}
@ -386,6 +389,33 @@ pub fn fmt_lines(file_map: &mut FileMap, config: &Config) -> FormatReport {
report
}
pub fn format_string(input: String, config: &Config, mode: WriteMode) -> FileMap {
let path = "stdin";
let mut parse_session = ParseSess::new();
let krate = parse::parse_crate_from_source_str(path.to_owned(),
input,
Vec::new(),
&parse_session);
// Suppress error output after parsing.
let emitter = Box::new(EmitterWriter::new(Box::new(Vec::new()), None));
parse_session.span_diagnostic.handler = Handler::with_emitter(false, emitter);
// FIXME: we still use a FileMap even though we only have
// one file, because fmt_lines requires a FileMap
let mut file_map = FileMap::new();
// do the actual formatting
let mut visitor = FmtVisitor::from_codemap(&parse_session, config, Some(mode));
visitor.format_separate_mod(&krate.module, path);
// append final newline
visitor.buffer.push_str("\n");
file_map.insert(path.to_owned(), visitor.buffer);
return file_map;
}
pub fn format(file: &Path, config: &Config, mode: WriteMode) -> FileMap {
let mut parse_session = ParseSess::new();
let krate = parse::parse_crate_from_file(file, Vec::new(), &parse_session);
@ -418,3 +448,15 @@ pub fn run(file: &Path, write_mode: WriteMode, config: &Config) {
println!("Error writing files: {}", msg);
}
}
// Similar to run, but takes an input String instead of a file to format
pub fn run_from_stdin(input: String, mode: WriteMode, config: &Config) {
let mut result = format_string(input, config, mode);
fmt_lines(&mut result, config);
let write_result = filemap::write_file(&result["stdin"], "stdin", mode, config);
if let Err(msg) = write_result {
panic!("Error writing to stdout: {}", msg);
}
}