diff options
| author | Sinh Pham <phamansinh@gmail.com> | 2015-09-10 18:27:22 -0400 |
|---|---|---|
| committer | Sinh Pham <phamansinh@gmail.com> | 2015-09-10 18:30:07 -0400 |
| commit | e7a5f9327ea8abbac1e953b31c6e25c1717f6c3d (patch) | |
| tree | 243e065ea8cef70ac2b9db14add993ba2d0e11b0 /src | |
| parent | 6e4ea7842b2e42099953fcba8f7b76c8626bdf4a (diff) | |
Add diff write mode https://github.com/nrc/rustfmt/issues/261
Diffstat (limited to 'src')
| -rw-r--r-- | src/bin/rustfmt.rs | 2 | ||||
| -rw-r--r-- | src/filemap.rs | 14 | ||||
| -rw-r--r-- | src/lib.rs | 6 | ||||
| -rw-r--r-- | src/rustfmt_diff.rs | 109 |
4 files changed, 129 insertions, 2 deletions
diff --git a/src/bin/rustfmt.rs b/src/bin/rustfmt.rs index 86a4faa05f5..9f2387f5c2c 100644 --- a/src/bin/rustfmt.rs +++ b/src/bin/rustfmt.rs @@ -82,7 +82,7 @@ fn main() { } fn print_usage<S: Into<String>>(reason: S) { - println!("{}\n\r usage: rustfmt [-h Help] [--write-mode=[replace|overwrite|display]] <file_name>", reason.into()); + println!("{}\n\r usage: rustfmt [-h Help] [--write-mode=[replace|overwrite|display|diff]] <file_name>", reason.into()); } fn determine_params<I>(args: I) -> Option<(Vec<String>, WriteMode)> diff --git a/src/filemap.rs b/src/filemap.rs index 8bb5a7285bb..91aaece6558 100644 --- a/src/filemap.rs +++ b/src/filemap.rs @@ -14,10 +14,11 @@ use strings::string_buffer::StringBuffer; use std::collections::HashMap; use std::fs::{self, File}; -use std::io::{self, Write, stdout}; +use std::io::{self, Write, Read, stdout}; use WriteMode; use NewlineStyle; use config::Config; +use rustfmt_diff::{make_diff, print_diff}; // A map of the files of a crate, with their new content pub type FileMap = HashMap<String, StringBuffer>; @@ -104,6 +105,17 @@ fn write_file(text: &StringBuffer, let stdout_lock = stdout.lock(); try!(write_system_newlines(stdout_lock, text, config)); } + WriteMode::Diff => { + println!("Diff of {}:\n", filename); + let mut f = try!(File::open(filename)); + let mut ori_text = String::new(); + try!(f.read_to_string(&mut ori_text)); + let mut v = Vec::new(); + try!(write_system_newlines(&mut v, text, config)); + let fmt_text = String::from_utf8(v).unwrap(); + let diff = make_diff(&ori_text, &fmt_text, 3); + print_diff(diff, |line_num| format!("\nDiff at line {}:", line_num)); + } WriteMode::Return(_) => { // io::Write is not implemented for String, working around with // Vec<u8> diff --git a/src/lib.rs b/src/lib.rs index ab748f62a95..7975c77e06d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,6 +30,8 @@ extern crate strings; extern crate unicode_segmentation; extern crate regex; +extern crate diff; +extern crate term; use rustc::session::Session; use rustc::session::config as rustc_config; @@ -67,6 +69,7 @@ mod rewrite; mod string; mod comment; mod modules; +pub mod rustfmt_diff; const MIN_STRING: usize = 10; // When we get scoped annotations, we should have rustfmt::skip. @@ -82,6 +85,8 @@ pub enum WriteMode { NewFile(&'static str), // Write the output to stdout. Display, + // Write the diff to stdout. + Diff, // Return the result as a mapping from filenames to Strings. Return(&'static Fn(HashMap<String, String>)), } @@ -94,6 +99,7 @@ impl FromStr for WriteMode { "replace" => Ok(WriteMode::Replace), "display" => Ok(WriteMode::Display), "overwrite" => Ok(WriteMode::Overwrite), + "diff" => Ok(WriteMode::Diff), _ => Err(()), } } diff --git a/src/rustfmt_diff.rs b/src/rustfmt_diff.rs new file mode 100644 index 00000000000..375cfd661fa --- /dev/null +++ b/src/rustfmt_diff.rs @@ -0,0 +1,109 @@ +use std::collections::VecDeque; +use diff; +use term; + +pub enum DiffLine { + Context(String), + Expected(String), + Resulting(String), +} + +pub struct Mismatch { + pub line_number: u32, + pub lines: Vec<DiffLine>, +} + +impl Mismatch { + fn new(line_number: u32) -> Mismatch { + Mismatch { line_number: line_number, lines: Vec::new() } + } +} + +// Produces a diff between the expected output and actual output of rustfmt. +pub fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec<Mismatch> { + let mut line_number = 1; + let mut context_queue: VecDeque<&str> = VecDeque::with_capacity(context_size); + let mut lines_since_mismatch = context_size + 1; + let mut results = Vec::new(); + let mut mismatch = Mismatch::new(0); + + for result in diff::lines(expected, actual) { + match result { + diff::Result::Left(str) => { + if lines_since_mismatch >= context_size { + results.push(mismatch); + mismatch = Mismatch::new(line_number - context_queue.len() as u32); + } + + while let Some(line) = context_queue.pop_front() { + mismatch.lines.push(DiffLine::Context(line.to_owned())); + } + + mismatch.lines.push(DiffLine::Resulting(str.to_owned())); + lines_since_mismatch = 0; + } + diff::Result::Right(str) => { + if lines_since_mismatch >= context_size { + results.push(mismatch); + mismatch = Mismatch::new(line_number - context_queue.len() as u32); + } + + while let Some(line) = context_queue.pop_front() { + mismatch.lines.push(DiffLine::Context(line.to_owned())); + } + + mismatch.lines.push(DiffLine::Expected(str.to_owned())); + line_number += 1; + lines_since_mismatch = 0; + } + diff::Result::Both(str, _) => { + if context_queue.len() >= context_size { + let _ = context_queue.pop_front(); + } + + if lines_since_mismatch < context_size { + mismatch.lines.push(DiffLine::Context(str.to_owned())); + } else { + context_queue.push_back(str); + } + + line_number += 1; + lines_since_mismatch += 1; + } + } + } + + results.push(mismatch); + results.remove(0); + + results +} + +pub fn print_diff<F>(diff: Vec<Mismatch>, get_section_title: F) + where F: Fn(u32) -> String +{ + let mut t = term::stdout().unwrap(); + for mismatch in diff { + t.fg(term::color::BRIGHT_WHITE).unwrap(); + let title = get_section_title(mismatch.line_number); + writeln!(t, "{}", title).unwrap(); + + for line in mismatch.lines { + match line { + DiffLine::Context(ref str) => { + t.fg(term::color::WHITE).unwrap(); + writeln!(t, " {}⏎", str).unwrap(); + } + DiffLine::Expected(ref str) => { + t.fg(term::color::GREEN).unwrap(); + writeln!(t, "+{}⏎", str).unwrap(); + } + DiffLine::Resulting(ref str) => { + t.fg(term::color::RED).unwrap(); + writeln!(t, "-{}⏎", str).unwrap(); + } + } + } + } + t.reset().unwrap(); +} |
