Skip to content

Commit

Permalink
Highlight diffs
Browse files Browse the repository at this point in the history
  • Loading branch information
dtolnay committed Nov 26, 2019
1 parent f139d95 commit d12a3b5
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 3 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ exclude = ["screenshots/*"]
readme = "README.md"

[dependencies]
diffr-lib = "=0.1.2"
glob = "0.3"
lazy_static = "1.3"
serde = { version = "1.0.103", features = ["derive"] }
Expand Down
112 changes: 112 additions & 0 deletions src/diff.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
use diffr_lib::{diff, tokenize, DiffInput, HashedSpan, Snake, Tokenization};
use std::cmp;
use std::iter::Peekable;
use std::slice;

pub struct Diff<'a> {
pub worth_printing: bool,
expected: &'a str,
expected_tokens: Vec<HashedSpan>,
actual: &'a str,
actual_tokens: Vec<HashedSpan>,
common: Vec<Snake>,
}

impl<'a> Diff<'a> {
pub fn compute(expected: &'a str, actual: &'a str) -> Self {
let mut actual_tokens = Vec::new();
tokenize(actual.as_bytes(), 0, &mut actual_tokens);
let added = Tokenization::new(actual.as_bytes(), &actual_tokens);

let mut expected_tokens = Vec::new();
tokenize(expected.as_bytes(), 0, &mut expected_tokens);
let removed = Tokenization::new(expected.as_bytes(), &expected_tokens);

let input = DiffInput { added, removed };
let mut scratch = Vec::new();
let mut common = Vec::new();
diff(&input, &mut scratch, &mut common);

let min_len = cmp::max(expected_tokens.len(), actual_tokens.len());
let common_len = common.iter().map(|snake| snake.len).sum::<isize>() as usize;
let worth_printing = common_len / 4 >= min_len / 5;

Diff {
worth_printing,
expected,
expected_tokens,
actual,
actual_tokens,
common,
}
}

pub fn iter(&self, input: &str) -> Iter {
if input == self.expected {
Iter {
pos: 0,
input: self.expected,
tokens: &self.expected_tokens,
common: self.common.iter().peekable(),
token_index: |snake| snake.x0,
}
} else if input == self.actual {
Iter {
pos: 0,
input: self.actual,
tokens: &self.actual_tokens,
common: self.common.iter().peekable(),
token_index: |snake| snake.y0,
}
} else {
panic!("unrecognized input");
}
}
}

pub struct Iter<'a> {
pos: usize,
input: &'a str,
tokens: &'a [HashedSpan],
common: Peekable<slice::Iter<'a, Snake>>,
token_index: fn(&Snake) -> isize,
}

pub enum Chunk<'a> {
Common(&'a str),
Unique(&'a str),
}

impl<'a> Iterator for Iter<'a> {
type Item = Chunk<'a>;

fn next(&mut self) -> Option<Self::Item> {
match self.common.peek() {
Some(common) => {
let index = (self.token_index)(common);
let begin = &self.tokens[index as usize];
if self.pos < begin.lo {
let chunk = &self.input[self.pos..begin.lo];
self.pos = begin.lo;
Some(Chunk::Unique(chunk))
} else {
let index = (self.token_index)(common) + common.len - 1;
let end = &self.tokens[index as usize];
let chunk = &self.input[begin.lo..end.hi];
self.common.next().unwrap();
self.pos = end.hi;
Some(Chunk::Common(chunk))
}
}
None => {
if self.pos < self.input.len() {
let chunk = &self.input[self.pos..];
self.pos = self.input.len();
Some(Chunk::Unique(chunk))
} else {
None
}
}
}
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ mod path;

mod cargo;
mod dependencies;
mod diff;
mod env;
mod error;
mod features;
Expand Down
31 changes: 28 additions & 3 deletions src/message.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use termcolor::Color::{self, *};

use super::{Expected, Test};
use crate::diff::{Chunk, Diff};
use crate::error::Error;
use crate::normalize;
use crate::term;
Expand Down Expand Up @@ -129,13 +130,14 @@ pub(crate) fn mismatch(expected: &str, actual: &str) {
println!("mismatch");
term::reset();
println!();
let diff = Diff::compute(expected, actual);
term::bold_color(Blue);
println!("EXPECTED:");
snippet(Blue, expected);
snippet_diff(Blue, expected, Some(&diff));
println!();
term::bold_color(Red);
println!("ACTUAL OUTPUT:");
snippet(Red, actual);
snippet_diff(Red, actual, Some(&diff));
println!();
}

Expand Down Expand Up @@ -203,13 +205,36 @@ pub(crate) fn warnings(warnings: &str) {
}

fn snippet(color: Color, content: &str) {
snippet_diff(color, content, None);
}

fn snippet_diff(color: Color, content: &str, diff: Option<&Diff>) {
fn dotted_line() {
println!("{}", "┈".repeat(60));
}

term::color(color);
dotted_line();
print!("{}", content);

match diff {
Some(diff) if diff.worth_printing => {
for chunk in diff.iter(content) {
match chunk {
Chunk::Common(s) => {
term::color(color);
print!("{}", s);
}
Chunk::Unique(s) => {
term::bold_color(color);
print!("\x1B[7m{}", s);
}
}
}
}
_ => print!("{}", content),
}

term::color(color);
dotted_line();
term::reset();
}

0 comments on commit d12a3b5

Please sign in to comment.