Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create a more compact diff format #8

Merged
merged 6 commits into from
Aug 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 3 additions & 43 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ rustc_version = "0.4"
colored = "2"
# Features chosen to match those required by env_logger, to avoid rebuilds
regex = { version = "1.5.5", default-features = false, features = ["perf", "std"] }
pretty_assertions = "1.2.1"
diff = "0.1.13"
crossbeam = "0.8.1"
lazy_static = "1.4.0"
serde = { version = "1.0", features = ["derive"] }
Expand Down
161 changes: 161 additions & 0 deletions src/diff.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
use colored::*;
use diff::{chars, lines, Result, Result::*};

#[derive(Default)]
struct DiffState<'a> {
/// Whether we've already printed something, so we should print starting context, too.
print_start_context: bool,
/// When we skip lines, remember the last `CONTEXT` ones to
/// display after the "skipped N lines" message
skipped_lines: Vec<&'a str>,
/// When we see a removed line, we don't print it, we
/// keep it around to compare it with the next added line.
prev_left: Option<&'a str>,
}

/// How many lines of context are displayed around the actual diffs
const CONTEXT: usize = 2;

impl<'a> DiffState<'a> {
/// Print `... n lines skipped ...` followed by the last `CONTEXT` lines.
fn print_end_skip(&self, skipped: usize) {
self.print_skipped_msg(skipped);
for line in self.skipped_lines.iter().rev().take(CONTEXT).rev() {
eprintln!(" {line}");
}
}

fn print_skipped_msg(&self, skipped: usize) {
match skipped {
// When the amount of skipped lines is exactly `CONTEXT * 2`, we already
// print all the context and don't actually skip anything.
0 => {}
// Instead of writing a line saying we skipped one line, print that one line
1 => eprintln!(" {}", self.skipped_lines[CONTEXT]),
_ => eprintln!("... {skipped} lines skipped ..."),
}
}

/// Print an initial `CONTEXT` amount of lines.
fn print_start_skip(&self) {
for line in self.skipped_lines.iter().take(CONTEXT) {
eprintln!(" {line}");
}
}

fn print_skip(&mut self) {
let half = self.skipped_lines.len() / 2;
if !self.print_start_context {
self.print_start_context = true;
self.print_end_skip(self.skipped_lines.len().saturating_sub(CONTEXT));
} else if half < CONTEXT {
// Print all the skipped lines if the amount of context desired is less than the amount of lines
for line in self.skipped_lines.drain(..) {
eprintln!(" {line}");
}
} else {
self.print_start_skip();
let skipped = self.skipped_lines.len() - CONTEXT * 2;
self.print_end_skip(skipped);
}
self.skipped_lines.clear();
}

fn skip(&mut self, line: &'a str) {
self.skipped_lines.push(line);
}

fn print_prev(&mut self) {
if let Some(l) = self.prev_left.take() {
self.print_left(l);
}
}

fn print_left(&self, l: &str) {
eprintln!("{}{}", "-".red(), l.red());
}

fn print_right(&self, r: &str) {
eprintln!("{}{}", "+".green(), r.green());
}

fn row(&mut self, row: Result<&'a str>) {
match row {
Left(l) => {
self.print_skip();
self.print_prev();
self.prev_left = Some(l);
}
Both(l, _) => {
self.print_prev();
self.skip(l);
}
Right(r) => {
// When there's an added line after a removed line, we'll want to special case some print cases.
// FIXME(oli-obk): also do special printing modes when there are multiple lines that only have minor changes.
if let Some(l) = self.prev_left.take() {
let diff = chars(l, r);
let mut seen_l = false;
let mut seen_r = false;
for char in &diff {
match char {
Left(l) if !l.is_whitespace() => seen_l = true,
Right(r) if !r.is_whitespace() => seen_r = true,
_ => {}
}
}
if seen_l && seen_r {
// The line both adds and removes chars, print both lines, but highlight their differences instead of
// drawing the entire line in red/green.
eprint!("{}", "-".red());
for char in &diff {
match char {
Left(l) => eprint!("{}", l.to_string().red()),
Right(_) => {}
Both(l, _) => eprint!("{}", l),
}
}
eprintln!();
eprint!("{}", "+".green());
for char in &diff {
match char {
Left(_) => {}
Right(r) => eprint!("{}", r.to_string().green()),
Both(l, _) => eprint!("{}", l),
}
}
eprintln!();
} else {
// The line only adds or only removes chars, print a single line highlighting their differences.
eprint!("{}", "~".yellow());
for char in diff {
match char {
Left(l) => eprint!("{}", l.to_string().red()),
Both(l, _) => eprint!("{}", l),
Right(r) => eprint!("{}", r.to_string().green()),
}
}
eprintln!();
}
} else {
self.print_skip();
self.print_right(r);
}
}
}
}

fn finish(self) {
self.print_start_skip();
self.print_skipped_msg(self.skipped_lines.len().saturating_sub(CONTEXT));
eprintln!()
}
}

pub fn print_diff(expected: &str, actual: &str) {
let mut state = DiffState::default();
for row in lines(expected, actual) {
state.row(row);
}
state.finish();
}
11 changes: 5 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use crate::dependencies::build_dependencies;
use crate::parser::{Comments, Condition};

mod dependencies;
mod diff;
mod parser;
mod rustc_stderr;
#[cfg(test)]
Expand Down Expand Up @@ -321,12 +322,10 @@ pub fn run_tests_generic(config: Config, file_filter: impl Fn(&Path) -> bool + S
actual,
expected,
} => {
eprintln!("actual output differed from expected {}", path.display());
eprintln!(
"{}",
pretty_assertions::StrComparison::new(expected, actual)
);
eprintln!()
eprintln!("{}", "actual output differed from expected".underline());
eprintln!("{}", format!("--- {}", path.display()).red());
eprintln!("{}", "+++ <stderr output>".green());
diff::print_diff(expected, actual);
}
Error::ErrorsWithoutPattern { path: None, msgs } => {
eprintln!(
Expand Down
42 changes: 1 addition & 41 deletions tests/integrations/basic-fail/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading