Skip to content

Commit dd0b3ca

Browse files
authored
Merge pull request #8 from oli-obk/diffy_mc_diffface
Create a more compact diff format
2 parents 44e2fe7 + 2b9c2b3 commit dd0b3ca

File tree

7 files changed

+182
-146
lines changed

7 files changed

+182
-146
lines changed

Cargo.lock

Lines changed: 3 additions & 43 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 & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ rustc_version = "0.4"
1515
colored = "2"
1616
# Features chosen to match those required by env_logger, to avoid rebuilds
1717
regex = { version = "1.5.5", default-features = false, features = ["perf", "std"] }
18-
pretty_assertions = "1.2.1"
18+
diff = "0.1.13"
1919
crossbeam = "0.8.1"
2020
lazy_static = "1.4.0"
2121
serde = { version = "1.0", features = ["derive"] }

src/diff.rs

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
use colored::*;
2+
use diff::{chars, lines, Result, Result::*};
3+
4+
#[derive(Default)]
5+
struct DiffState<'a> {
6+
/// Whether we've already printed something, so we should print starting context, too.
7+
print_start_context: bool,
8+
/// When we skip lines, remember the last `CONTEXT` ones to
9+
/// display after the "skipped N lines" message
10+
skipped_lines: Vec<&'a str>,
11+
/// When we see a removed line, we don't print it, we
12+
/// keep it around to compare it with the next added line.
13+
prev_left: Option<&'a str>,
14+
}
15+
16+
/// How many lines of context are displayed around the actual diffs
17+
const CONTEXT: usize = 2;
18+
19+
impl<'a> DiffState<'a> {
20+
/// Print `... n lines skipped ...` followed by the last `CONTEXT` lines.
21+
fn print_end_skip(&self, skipped: usize) {
22+
self.print_skipped_msg(skipped);
23+
for line in self.skipped_lines.iter().rev().take(CONTEXT).rev() {
24+
eprintln!(" {line}");
25+
}
26+
}
27+
28+
fn print_skipped_msg(&self, skipped: usize) {
29+
match skipped {
30+
// When the amount of skipped lines is exactly `CONTEXT * 2`, we already
31+
// print all the context and don't actually skip anything.
32+
0 => {}
33+
// Instead of writing a line saying we skipped one line, print that one line
34+
1 => eprintln!(" {}", self.skipped_lines[CONTEXT]),
35+
_ => eprintln!("... {skipped} lines skipped ..."),
36+
}
37+
}
38+
39+
/// Print an initial `CONTEXT` amount of lines.
40+
fn print_start_skip(&self) {
41+
for line in self.skipped_lines.iter().take(CONTEXT) {
42+
eprintln!(" {line}");
43+
}
44+
}
45+
46+
fn print_skip(&mut self) {
47+
let half = self.skipped_lines.len() / 2;
48+
if !self.print_start_context {
49+
self.print_start_context = true;
50+
self.print_end_skip(self.skipped_lines.len().saturating_sub(CONTEXT));
51+
} else if half < CONTEXT {
52+
// Print all the skipped lines if the amount of context desired is less than the amount of lines
53+
for line in self.skipped_lines.drain(..) {
54+
eprintln!(" {line}");
55+
}
56+
} else {
57+
self.print_start_skip();
58+
let skipped = self.skipped_lines.len() - CONTEXT * 2;
59+
self.print_end_skip(skipped);
60+
}
61+
self.skipped_lines.clear();
62+
}
63+
64+
fn skip(&mut self, line: &'a str) {
65+
self.skipped_lines.push(line);
66+
}
67+
68+
fn print_prev(&mut self) {
69+
if let Some(l) = self.prev_left.take() {
70+
self.print_left(l);
71+
}
72+
}
73+
74+
fn print_left(&self, l: &str) {
75+
eprintln!("{}{}", "-".red(), l.red());
76+
}
77+
78+
fn print_right(&self, r: &str) {
79+
eprintln!("{}{}", "+".green(), r.green());
80+
}
81+
82+
fn row(&mut self, row: Result<&'a str>) {
83+
match row {
84+
Left(l) => {
85+
self.print_skip();
86+
self.print_prev();
87+
self.prev_left = Some(l);
88+
}
89+
Both(l, _) => {
90+
self.print_prev();
91+
self.skip(l);
92+
}
93+
Right(r) => {
94+
// When there's an added line after a removed line, we'll want to special case some print cases.
95+
// FIXME(oli-obk): also do special printing modes when there are multiple lines that only have minor changes.
96+
if let Some(l) = self.prev_left.take() {
97+
let diff = chars(l, r);
98+
let mut seen_l = false;
99+
let mut seen_r = false;
100+
for char in &diff {
101+
match char {
102+
Left(l) if !l.is_whitespace() => seen_l = true,
103+
Right(r) if !r.is_whitespace() => seen_r = true,
104+
_ => {}
105+
}
106+
}
107+
if seen_l && seen_r {
108+
// The line both adds and removes chars, print both lines, but highlight their differences instead of
109+
// drawing the entire line in red/green.
110+
eprint!("{}", "-".red());
111+
for char in &diff {
112+
match char {
113+
Left(l) => eprint!("{}", l.to_string().red()),
114+
Right(_) => {}
115+
Both(l, _) => eprint!("{}", l),
116+
}
117+
}
118+
eprintln!();
119+
eprint!("{}", "+".green());
120+
for char in &diff {
121+
match char {
122+
Left(_) => {}
123+
Right(r) => eprint!("{}", r.to_string().green()),
124+
Both(l, _) => eprint!("{}", l),
125+
}
126+
}
127+
eprintln!();
128+
} else {
129+
// The line only adds or only removes chars, print a single line highlighting their differences.
130+
eprint!("{}", "~".yellow());
131+
for char in diff {
132+
match char {
133+
Left(l) => eprint!("{}", l.to_string().red()),
134+
Both(l, _) => eprint!("{}", l),
135+
Right(r) => eprint!("{}", r.to_string().green()),
136+
}
137+
}
138+
eprintln!();
139+
}
140+
} else {
141+
self.print_skip();
142+
self.print_right(r);
143+
}
144+
}
145+
}
146+
}
147+
148+
fn finish(self) {
149+
self.print_start_skip();
150+
self.print_skipped_msg(self.skipped_lines.len().saturating_sub(CONTEXT));
151+
eprintln!()
152+
}
153+
}
154+
155+
pub fn print_diff(expected: &str, actual: &str) {
156+
let mut state = DiffState::default();
157+
for row in lines(expected, actual) {
158+
state.row(row);
159+
}
160+
state.finish();
161+
}

src/lib.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use crate::dependencies::build_dependencies;
2525
use crate::parser::{Comments, Condition};
2626

2727
mod dependencies;
28+
mod diff;
2829
mod parser;
2930
mod rustc_stderr;
3031
#[cfg(test)]
@@ -321,12 +322,10 @@ pub fn run_tests_generic(config: Config, file_filter: impl Fn(&Path) -> bool + S
321322
actual,
322323
expected,
323324
} => {
324-
eprintln!("actual output differed from expected {}", path.display());
325-
eprintln!(
326-
"{}",
327-
pretty_assertions::StrComparison::new(expected, actual)
328-
);
329-
eprintln!()
325+
eprintln!("{}", "actual output differed from expected".underline());
326+
eprintln!("{}", format!("--- {}", path.display()).red());
327+
eprintln!("{}", "+++ <stderr output>".green());
328+
diff::print_diff(expected, actual);
330329
}
331330
Error::ErrorsWithoutPattern { path: None, msgs } => {
332331
eprintln!(

tests/integrations/basic-fail/Cargo.lock

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

0 commit comments

Comments
 (0)