Skip to content

Commit 1af301c

Browse files
committed
Merge pull request #294 from sinhpham/diff_write_mode
Add diff write mode https://github.com/nrc/rustfmt/issues/261
2 parents 6e4ea78 + e7a5f93 commit 1af301c

File tree

7 files changed

+137
-106
lines changed

7 files changed

+137
-106
lines changed

Cargo.lock

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

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ toml = "0.1.20"
1717
rustc-serialize = "0.3.14"
1818
unicode-segmentation = "0.1.2"
1919
regex = "0.1.41"
20+
diff = "0.1.5"
21+
term = "0.2.11"
2022

2123
[dev-dependencies]
22-
diff = "0.1.0"
23-
term = "0.2"

src/bin/rustfmt.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ fn main() {
8282
}
8383

8484
fn print_usage<S: Into<String>>(reason: S) {
85-
println!("{}\n\r usage: rustfmt [-h Help] [--write-mode=[replace|overwrite|display]] <file_name>", reason.into());
85+
println!("{}\n\r usage: rustfmt [-h Help] [--write-mode=[replace|overwrite|display|diff]] <file_name>", reason.into());
8686
}
8787

8888
fn determine_params<I>(args: I) -> Option<(Vec<String>, WriteMode)>

src/filemap.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@
1414
use strings::string_buffer::StringBuffer;
1515
use std::collections::HashMap;
1616
use std::fs::{self, File};
17-
use std::io::{self, Write, stdout};
17+
use std::io::{self, Write, Read, stdout};
1818
use WriteMode;
1919
use NewlineStyle;
2020
use config::Config;
21+
use rustfmt_diff::{make_diff, print_diff};
2122

2223
// A map of the files of a crate, with their new content
2324
pub type FileMap = HashMap<String, StringBuffer>;
@@ -104,6 +105,17 @@ fn write_file(text: &StringBuffer,
104105
let stdout_lock = stdout.lock();
105106
try!(write_system_newlines(stdout_lock, text, config));
106107
}
108+
WriteMode::Diff => {
109+
println!("Diff of {}:\n", filename);
110+
let mut f = try!(File::open(filename));
111+
let mut ori_text = String::new();
112+
try!(f.read_to_string(&mut ori_text));
113+
let mut v = Vec::new();
114+
try!(write_system_newlines(&mut v, text, config));
115+
let fmt_text = String::from_utf8(v).unwrap();
116+
let diff = make_diff(&ori_text, &fmt_text, 3);
117+
print_diff(diff, |line_num| format!("\nDiff at line {}:", line_num));
118+
}
107119
WriteMode::Return(_) => {
108120
// io::Write is not implemented for String, working around with
109121
// Vec<u8>

src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ extern crate strings;
3030

3131
extern crate unicode_segmentation;
3232
extern crate regex;
33+
extern crate diff;
34+
extern crate term;
3335

3436
use rustc::session::Session;
3537
use rustc::session::config as rustc_config;
@@ -67,6 +69,7 @@ mod rewrite;
6769
mod string;
6870
mod comment;
6971
mod modules;
72+
pub mod rustfmt_diff;
7073

7174
const MIN_STRING: usize = 10;
7275
// When we get scoped annotations, we should have rustfmt::skip.
@@ -82,6 +85,8 @@ pub enum WriteMode {
8285
NewFile(&'static str),
8386
// Write the output to stdout.
8487
Display,
88+
// Write the diff to stdout.
89+
Diff,
8590
// Return the result as a mapping from filenames to Strings.
8691
Return(&'static Fn(HashMap<String, String>)),
8792
}
@@ -94,6 +99,7 @@ impl FromStr for WriteMode {
9499
"replace" => Ok(WriteMode::Replace),
95100
"display" => Ok(WriteMode::Display),
96101
"overwrite" => Ok(WriteMode::Overwrite),
102+
"diff" => Ok(WriteMode::Diff),
97103
_ => Err(()),
98104
}
99105
}

src/rustfmt_diff.rs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
use std::collections::VecDeque;
2+
use diff;
3+
use term;
4+
5+
pub enum DiffLine {
6+
Context(String),
7+
Expected(String),
8+
Resulting(String),
9+
}
10+
11+
pub struct Mismatch {
12+
pub line_number: u32,
13+
pub lines: Vec<DiffLine>,
14+
}
15+
16+
impl Mismatch {
17+
fn new(line_number: u32) -> Mismatch {
18+
Mismatch { line_number: line_number, lines: Vec::new() }
19+
}
20+
}
21+
22+
// Produces a diff between the expected output and actual output of rustfmt.
23+
pub fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec<Mismatch> {
24+
let mut line_number = 1;
25+
let mut context_queue: VecDeque<&str> = VecDeque::with_capacity(context_size);
26+
let mut lines_since_mismatch = context_size + 1;
27+
let mut results = Vec::new();
28+
let mut mismatch = Mismatch::new(0);
29+
30+
for result in diff::lines(expected, actual) {
31+
match result {
32+
diff::Result::Left(str) => {
33+
if lines_since_mismatch >= context_size {
34+
results.push(mismatch);
35+
mismatch = Mismatch::new(line_number - context_queue.len() as u32);
36+
}
37+
38+
while let Some(line) = context_queue.pop_front() {
39+
mismatch.lines.push(DiffLine::Context(line.to_owned()));
40+
}
41+
42+
mismatch.lines.push(DiffLine::Resulting(str.to_owned()));
43+
lines_since_mismatch = 0;
44+
}
45+
diff::Result::Right(str) => {
46+
if lines_since_mismatch >= context_size {
47+
results.push(mismatch);
48+
mismatch = Mismatch::new(line_number - context_queue.len() as u32);
49+
}
50+
51+
while let Some(line) = context_queue.pop_front() {
52+
mismatch.lines.push(DiffLine::Context(line.to_owned()));
53+
}
54+
55+
mismatch.lines.push(DiffLine::Expected(str.to_owned()));
56+
line_number += 1;
57+
lines_since_mismatch = 0;
58+
}
59+
diff::Result::Both(str, _) => {
60+
if context_queue.len() >= context_size {
61+
let _ = context_queue.pop_front();
62+
}
63+
64+
if lines_since_mismatch < context_size {
65+
mismatch.lines.push(DiffLine::Context(str.to_owned()));
66+
} else {
67+
context_queue.push_back(str);
68+
}
69+
70+
line_number += 1;
71+
lines_since_mismatch += 1;
72+
}
73+
}
74+
}
75+
76+
results.push(mismatch);
77+
results.remove(0);
78+
79+
results
80+
}
81+
82+
pub fn print_diff<F>(diff: Vec<Mismatch>, get_section_title: F)
83+
where F: Fn(u32) -> String
84+
{
85+
let mut t = term::stdout().unwrap();
86+
for mismatch in diff {
87+
t.fg(term::color::BRIGHT_WHITE).unwrap();
88+
let title = get_section_title(mismatch.line_number);
89+
writeln!(t, "{}", title).unwrap();
90+
91+
for line in mismatch.lines {
92+
match line {
93+
DiffLine::Context(ref str) => {
94+
t.fg(term::color::WHITE).unwrap();
95+
writeln!(t, " {}⏎", str).unwrap();
96+
}
97+
DiffLine::Expected(ref str) => {
98+
t.fg(term::color::GREEN).unwrap();
99+
writeln!(t, "+{}⏎", str).unwrap();
100+
}
101+
DiffLine::Resulting(ref str) => {
102+
t.fg(term::color::RED).unwrap();
103+
writeln!(t, "-{}⏎", str).unwrap();
104+
}
105+
}
106+
}
107+
}
108+
t.reset().unwrap();
109+
}

tests/system.rs

Lines changed: 4 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,13 @@ extern crate diff;
1515
extern crate regex;
1616
extern crate term;
1717

18-
use std::collections::{VecDeque, HashMap};
18+
use std::collections::HashMap;
1919
use std::fs;
2020
use std::io::{self, Read, BufRead, BufReader};
2121
use std::thread;
2222
use rustfmt::*;
2323
use rustfmt::config::Config;
24+
use rustfmt::rustfmt_diff::*;
2425

2526
static DIFF_CONTEXT_SIZE: usize = 3;
2627

@@ -94,27 +95,7 @@ fn print_mismatches(result: HashMap<String, Vec<Mismatch>>) {
9495
let mut t = term::stdout().unwrap();
9596

9697
for (file_name, diff) in result {
97-
for mismatch in diff {
98-
t.fg(term::color::BRIGHT_WHITE).unwrap();
99-
writeln!(t, "\nMismatch at {}:{}:", file_name, mismatch.line_number).unwrap();
100-
101-
for line in mismatch.lines {
102-
match line {
103-
DiffLine::Context(ref str) => {
104-
t.fg(term::color::WHITE).unwrap();
105-
writeln!(t, " {}⏎", str).unwrap();
106-
}
107-
DiffLine::Expected(ref str) => {
108-
t.fg(term::color::GREEN).unwrap();
109-
writeln!(t, "+{}⏎", str).unwrap();
110-
}
111-
DiffLine::Resulting(ref str) => {
112-
t.fg(term::color::RED).unwrap();
113-
writeln!(t, "-{}⏎", str).unwrap();
114-
}
115-
}
116-
}
117-
}
98+
print_diff(diff, |line_num| format!("\nMismatch at {}:{}:", file_name, line_num));
11899
}
119100

120101
assert!(t.reset().unwrap());
@@ -206,7 +187,7 @@ fn handle_result(result: HashMap<String, String>) {
206187
// TODO: speedup by running through bytes iterator
207188
f.read_to_string(&mut text).ok().expect("Failed reading target.");
208189
if fmt_text != text {
209-
let diff = make_diff(&fmt_text, &text, DIFF_CONTEXT_SIZE);
190+
let diff = make_diff(&text, &fmt_text, DIFF_CONTEXT_SIZE);
210191
failures.insert(file_name, diff);
211192
}
212193
}
@@ -226,80 +207,3 @@ fn get_target(file_name: &str, target: Option<&str>) -> String {
226207
file_name.to_owned()
227208
}
228209
}
229-
230-
pub enum DiffLine {
231-
Context(String),
232-
Expected(String),
233-
Resulting(String),
234-
}
235-
236-
pub struct Mismatch {
237-
line_number: u32,
238-
pub lines: Vec<DiffLine>,
239-
}
240-
241-
impl Mismatch {
242-
fn new(line_number: u32) -> Mismatch {
243-
Mismatch { line_number: line_number, lines: Vec::new() }
244-
}
245-
}
246-
247-
// Produces a diff between the expected output and actual output of rustfmt.
248-
fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec<Mismatch> {
249-
let mut line_number = 1;
250-
let mut context_queue: VecDeque<&str> = VecDeque::with_capacity(context_size);
251-
let mut lines_since_mismatch = context_size + 1;
252-
let mut results = Vec::new();
253-
let mut mismatch = Mismatch::new(0);
254-
255-
for result in diff::lines(expected, actual) {
256-
match result {
257-
diff::Result::Left(str) => {
258-
if lines_since_mismatch >= context_size {
259-
results.push(mismatch);
260-
mismatch = Mismatch::new(line_number - context_queue.len() as u32);
261-
}
262-
263-
while let Some(line) = context_queue.pop_front() {
264-
mismatch.lines.push(DiffLine::Context(line.to_owned()));
265-
}
266-
267-
mismatch.lines.push(DiffLine::Resulting(str.to_owned()));
268-
lines_since_mismatch = 0;
269-
}
270-
diff::Result::Right(str) => {
271-
if lines_since_mismatch >= context_size {
272-
results.push(mismatch);
273-
mismatch = Mismatch::new(line_number - context_queue.len() as u32);
274-
}
275-
276-
while let Some(line) = context_queue.pop_front() {
277-
mismatch.lines.push(DiffLine::Context(line.to_owned()));
278-
}
279-
280-
mismatch.lines.push(DiffLine::Expected(str.to_owned()));
281-
line_number += 1;
282-
lines_since_mismatch = 0;
283-
}
284-
diff::Result::Both(str, _) => {
285-
if context_queue.len() >= context_size {
286-
let _ = context_queue.pop_front();
287-
}
288-
289-
if lines_since_mismatch < context_size {
290-
mismatch.lines.push(DiffLine::Context(str.to_owned()));
291-
} else {
292-
context_queue.push_back(str);
293-
}
294-
295-
line_number += 1;
296-
lines_since_mismatch += 1;
297-
}
298-
}
299-
}
300-
301-
results.push(mismatch);
302-
results.remove(0);
303-
304-
results
305-
}

0 commit comments

Comments
 (0)