-
Notifications
You must be signed in to change notification settings - Fork 384
Create a more compact diff format #2400
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
Closed
Closed
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
a41f31e
Create a more compact diff format
oli-obk c1ce7eb
Avoid printing `1 lines skipped`
oli-obk 511f6ba
Deduplicate skipped lines printing
oli-obk 62e6df0
Show some context around a diff
oli-obk 9a23165
Document all the things
oli-obk File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
use colored::*; | ||
use diff::{chars, lines, Result, Result::*}; | ||
|
||
#[derive(Default)] | ||
struct DiffState<'a> { | ||
/// 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> { | ||
fn print_skip(&mut self) { | ||
let half = self.skipped_lines.len() / 2; | ||
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 { | ||
// Print an initial `CONTEXT` amount of lines. | ||
for line in self.skipped_lines.iter().take(CONTEXT) { | ||
eprintln!(" {line}"); | ||
} | ||
let skipped = self.skipped_lines.len() - CONTEXT * 2; | ||
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 the last `CONTEXT` amount of lines. | ||
for line in self.skipped_lines.iter().rev().take(CONTEXT).rev() { | ||
eprintln!(" {line}"); | ||
} | ||
} | ||
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(mut self) { | ||
self.print_skip(); | ||
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(); | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.