Display rename information when before and after paths are different

pull/286/head
Wilfred Hughes 2022-05-08 11:50:55 +07:00
parent 2d8e1cf180
commit 1a6c5b8e7f
8 changed files with 156 additions and 101 deletions

@ -9,6 +9,11 @@ Improved handling of comments and regexp literals in Perl.
Added Elvish support. Added Elvish support.
### Display
Difftastic now displays information about file renames. Previously, it
would only show the new name.
## 0.28 (released 29th April 2022) ## 0.28 (released 29th April 2022)
### Parsing ### Parsing

@ -1,174 +1,174 @@
sample_files/b2_math_before.h sample_files/b2_math_after.h sample_files/b2_math_before.h sample_files/b2_math_after.h
637e91c1528894ec0529bc845ef79c88 - 716834c475a1e1ca964757100e85ac88 -
sample_files/bad_combine_before.rs sample_files/bad_combine_after.rs sample_files/bad_combine_before.rs sample_files/bad_combine_after.rs
9f61f6c7e485a2726685f718fef1f9bd - 0371c894b0dff01ee2c3e0bc7fdbeba0 -
sample_files/change_outer_before.el sample_files/change_outer_after.el sample_files/change_outer_before.el sample_files/change_outer_after.el
1857b63ba1bfa0ccc0a4243db6b1c5c2 - d211879a64cd8dfb087d4a43ffe1a91c -
sample_files/clojure_before.clj sample_files/clojure_after.clj sample_files/clojure_before.clj sample_files/clojure_after.clj
b916e224f289888252cd7597bab339e6 - 8783be478554d6948894fd5d441e1089 -
sample_files/comments_before.rs sample_files/comments_after.rs sample_files/comments_before.rs sample_files/comments_after.rs
f7b56285b9db37d84405f647fb15412f - 1172d43fba1610ca9bf7fbac0129a344 -
sample_files/context_before.rs sample_files/context_after.rs sample_files/context_before.rs sample_files/context_after.rs
ef267b3bbea4b56a111427a11b24cc6a - be9622d48c4d99f74e5ac7b45b806826 -
sample_files/contiguous_before.js sample_files/contiguous_after.js sample_files/contiguous_before.js sample_files/contiguous_after.js
9d7bc73c3551064e67f40155abc84798 - 3d7e5e8f2782544f8650ed8cf7f54296 -
sample_files/css_before.css sample_files/css_after.css sample_files/css_before.css sample_files/css_after.css
76e37e865774d0d17e73b71f627b62d7 - d70196243265851ab9c9d2b747a79644 -
sample_files/dart_before.dart sample_files/dart_after.dart sample_files/dart_before.dart sample_files/dart_after.dart
7a18450edf0db2ff3868f46a2d4e277c - e9b21a630927910cc003ab283a11742d -
sample_files/elisp_before.el sample_files/elisp_after.el sample_files/elisp_before.el sample_files/elisp_after.el
f4233ebbe6c46a7c07bc88eca20e4856 - 11648795d1fad1dd54c7c20a36e0229c -
sample_files/elisp_contiguous_before.el sample_files/elisp_contiguous_after.el sample_files/elisp_contiguous_before.el sample_files/elisp_contiguous_after.el
e3946aef566a707c718edd7a86340566 - 1045d696fb8e2157f93ac325402fd098 -
sample_files/elm_before.elm sample_files/elm_after.elm sample_files/elm_before.elm sample_files/elm_after.elm
1fe758b3148056d9c744f75640e7cd83 - f42633bd95ab837f77ff34ebcd9387f7 -
sample_files/elvish_before.elv sample_files/elvish_after.elv sample_files/elvish_before.elv sample_files/elvish_after.elv
0107abd4d8c25034207bedd407a499ac - b7750d685b47f9430e6be41ce7bf5b5c -
sample_files/hack_before.php sample_files/hack_after.php sample_files/hack_before.php sample_files/hack_after.php
b8c51005df7e1eaaeaf738a4353ac88f - 40d122bf3a88e1cf7825bc0b44be3312 -
sample_files/haskell_before.hs sample_files/haskell_after.hs sample_files/haskell_before.hs sample_files/haskell_after.hs
9c668c79e56f1e1cecf1c02759c195a9 - 61c34a0e256be17fa665bdee58e0d30c -
sample_files/hcl_before.hcl sample_files/hcl_after.hcl sample_files/hcl_before.hcl sample_files/hcl_after.hcl
f8a18b1f0b50c1a1736e13306cf3db52 - bb8c254fc96a1c173d2604f5442eb9b5 -
sample_files/helpful_before.el sample_files/helpful_after.el sample_files/helpful_before.el sample_files/helpful_after.el
bce74573e003cc6b729a63a4bc34c4af - 70fce536c4a7c020a47ad23fdeaaa6bd -
sample_files/helpful-unit-test-before.el sample_files/helpful-unit-test-after.el sample_files/helpful-unit-test-before.el sample_files/helpful-unit-test-after.el
79597af48ff80bcf9f5d02d20c51606d - c27fa4159c25bd3da6bef231b51a6e42 -
sample_files/identical_before.scala sample_files/identical_after.scala sample_files/identical_before.scala sample_files/identical_after.scala
9c7319f61833e46a0a8cb6c01cc997c9 - 53b03b5e718e9d6b366d3762fcbeff15 -
sample_files/if_before.py sample_files/if_after.py sample_files/if_before.py sample_files/if_after.py
ec9c3b52643b5fde34ea7432b9d537ac - bdc08d613f781b410f3af9145dae64c9 -
sample_files/janet_before.janet sample_files/janet_after.janet sample_files/janet_before.janet sample_files/janet_after.janet
677604a16ef62f6b6252d76d76e86265 - f9805273f9fb89a74e018d79673d4a39 -
sample_files/java_before.java sample_files/java_after.java sample_files/java_before.java sample_files/java_after.java
22c27b91fd67d2b894de9a620bcf5c35 - 9c4d96d145e64d7b9db802f59e480081 -
sample_files/javascript_before.js sample_files/javascript_after.js sample_files/javascript_before.js sample_files/javascript_after.js
f4bfe92df94f89942bacc73e4a9db882 - d3a51b2e55b6ce0ed0a1c73a8e12abe8 -
sample_files/javascript_simple_before.js sample_files/javascript_simple_after.js sample_files/javascript_simple_before.js sample_files/javascript_simple_after.js
d0e0bb7b9e78643cecbfc9217241aafe - 4f8ba3289eb678dd871eef91707a97b1 -
sample_files/json_before.json sample_files/json_after.json sample_files/json_before.json sample_files/json_after.json
3b9ee16f0ff09a830e5769da05bd8b95 - c14c170dfe0eabc9d1c8853a4b6ae638 -
sample_files/jsx_before.jsx sample_files/jsx_after.jsx sample_files/jsx_before.jsx sample_files/jsx_after.jsx
f6211fad4ccff5b7a92dbe52d25470e8 - 24d5929272f73f63b330234e6abed6ac -
sample_files/load_before.js sample_files/load_after.js sample_files/load_before.js sample_files/load_after.js
58df6bfac4f237d3a1dd9201e7873f1c - e0390cda57dc05b539eb6214dc6b87d1 -
sample_files/lua_before.lua sample_files/lua_after.lua sample_files/lua_before.lua sample_files/lua_after.lua
c3d81271c060bd97dd246c1c5ea6a138 - 709bce8626215c1f207570420487abac -
sample_files/metadata_before.clj sample_files/metadata_after.clj sample_files/metadata_before.clj sample_files/metadata_after.clj
b71577801352071fd1c6a9079f2d9dbc - 39c353f9fe498a9886ca1b01778159e3 -
sample_files/modules_before.ml sample_files/modules_after.ml sample_files/modules_before.ml sample_files/modules_after.ml
694bcfa17f8adf5ed6297994287e0841 - b51ac4a770a939d5c049dcaa5eb698b1 -
sample_files/multibyte_before.py sample_files/multibyte_after.py sample_files/multibyte_before.py sample_files/multibyte_after.py
9287243986455b75e560080f3fd16ced - b18d41620a3c5738ccf23e1c8739ed22 -
sample_files/multiline_string_before.ml sample_files/multiline_string_after.ml sample_files/multiline_string_before.ml sample_files/multiline_string_after.ml
170c55099a9fdbecd39352905a691819 - ee07940f955a13caabb9b06a3152b010 -
sample_files/nest_before.rs sample_files/nest_after.rs sample_files/nest_before.rs sample_files/nest_after.rs
811805002ed9196d1156388785a1f09d - 5028e773b7845afb50a9de3cc38ed448 -
sample_files/nested_slider_before.rs sample_files/nested_slider_after.rs sample_files/nested_slider_before.rs sample_files/nested_slider_after.rs
3a901b805dd8b541c43edb96c7e4e148 - 8c0dc12d8b13af7b4839af09f2f5dcaf -
sample_files/nesting_before.el sample_files/nesting_after.el sample_files/nesting_before.el sample_files/nesting_after.el
16639761819b53b9216a9031ae94455c - ddf6f790193312ac1d78e040d3d343bf -
sample_files/nix_before.nix sample_files/nix_after.nix sample_files/nix_before.nix sample_files/nix_after.nix
337430bc90562b18dbaec9b53c0f950e - e0e3cb59c91d1b21701c1b44fd4571ba -
sample_files/ocaml_before.ml sample_files/ocaml_after.ml sample_files/ocaml_before.ml sample_files/ocaml_after.ml
1fffa5fa9392f8b46eb8b4f90c938dc2 - 4a7b5c6286aa8cfeafb04d0711495b6b -
sample_files/outer_delimiter_before.el sample_files/outer_delimiter_after.el sample_files/outer_delimiter_before.el sample_files/outer_delimiter_after.el
73130b8572a4f17fa6cf828f74e226ce - 4bf8de79905ffaafb49c3e563ba1468f -
sample_files/perl_before.pl sample_files/perl_after.pl sample_files/perl_before.pl sample_files/perl_after.pl
ae10c90122289e0f4298c1b962a74c2e - 778488458f4705ce21cd3211d0ce63da -
sample_files/preprocesor_before.h sample_files/preprocesor_after.h sample_files/preprocesor_before.h sample_files/preprocesor_after.h
3e4331cb935cbe735a79ebc43786cd3a - aa90bac79c8b7a5d04846c563edcf92e -
sample_files/ruby_before.rb sample_files/ruby_after.rb sample_files/ruby_before.rb sample_files/ruby_after.rb
4a9847a91e32ec6afdc2f0b01e28d2d6 - 6bbf0b8a71bb1233550d22b37c77674a -
sample_files/scala_before.scala sample_files/scala_after.scala sample_files/scala_before.scala sample_files/scala_after.scala
b276168d30704b4242fc82dc1f8382d8 - 1ff2abc236ce112f663a1add9eebcdd7 -
sample_files/Session_before.kt sample_files/Session_after.kt sample_files/Session_before.kt sample_files/Session_after.kt
46994b58bb24200f82866951013f03ce - 1e3f28302745aa4b83564aad1ddb7150 -
sample_files/simple_before.js sample_files/simple_after.js sample_files/simple_before.js sample_files/simple_after.js
4fa7016f884e76dddba4cab12cab8c61 - e7a89d7a63ce6d2138224a0dbd66ba6c -
sample_files/simple_before.txt sample_files/simple_after.txt sample_files/simple_before.txt sample_files/simple_after.txt
4b653ebe89321835c35722dd065cf6a2 - f216c97d25554d2deac8cdb3c064db14 -
sample_files/slider_before.rs sample_files/slider_after.rs sample_files/slider_before.rs sample_files/slider_after.rs
50e1df5af0bf4a1fa7211e079196f1a9 - e5b0260017f9b80debd71953984c3ae6 -
sample_files/slow_before.rs sample_files/slow_after.rs sample_files/slow_before.rs sample_files/slow_after.rs
a1d8070fda3b8fa65886a90bde64a2ab - 280716322c80218eec16062a5c4e8115 -
sample_files/small_before.js sample_files/small_after.js sample_files/small_before.js sample_files/small_after.js
27bcac13aa17141718a3e6b8c0ac8f47 - 1110121ff2743f3d89af9cf49f0089db -
sample_files/swift_before.swift sample_files/swift_after.swift sample_files/swift_before.swift sample_files/swift_after.swift
eeab25a68552f051a6392b5e713fbd29 - 85dc32d2a38ea4dceca5050b71d66ad5 -
sample_files/syntax_error_before.js sample_files/syntax_error_after.js sample_files/syntax_error_before.js sample_files/syntax_error_after.js
fe636ad27b1aa75e0b153dfe248023bb - a68c7499bc627b396dd9e0ab5377a3f0 -
sample_files/tab_before.c sample_files/tab_after.c sample_files/tab_before.c sample_files/tab_after.c
36ba3231eeba6f0b67a6be9db454de19 - 1074b346606db234a158740b16e993e8 -
sample_files/text_before.txt sample_files/text_after.txt sample_files/text_before.txt sample_files/text_after.txt
dfc3495b8d5931029b479f0c878a3219 - 173ee2a30fe72380cc448ca376d47825 -
sample_files/todomvc_before.gleam sample_files/todomvc_after.gleam sample_files/todomvc_before.gleam sample_files/todomvc_after.gleam
c1d8b44875121d81c583dd3a8fb43232 - 83d3d2bfbb9093623970a07589f131c2 -
sample_files/toml_before.toml sample_files/toml_after.toml sample_files/toml_before.toml sample_files/toml_after.toml
1e2de7235c339b07a0784498453e896c - f6110340ed334ca58815194806e3e0ab -
sample_files/typing_before.ml sample_files/typing_after.ml sample_files/typing_before.ml sample_files/typing_after.ml
ceba89e4ffc8406454d337638c7d45e6 - b512629fd282fa116ddec72c72ea8ad2 -
sample_files/whitespace_before.tsx sample_files/whitespace_after.tsx sample_files/whitespace_before.tsx sample_files/whitespace_after.tsx
c4151c5a44b11e04fd11c2594597ed33 - fe09099c5fa63b5e1ca6f4b30fe5d2e6 -
sample_files/yaml_before.yaml sample_files/yaml_after.yaml sample_files/yaml_before.yaml sample_files/yaml_after.yaml
8339ac699789fb3d17becce27dd3af6b - c1d93254974e832b9407c47074479e37 -
sample_files/zig_before.zig sample_files/zig_after.zig sample_files/zig_before.zig sample_files/zig_after.zig
fe7f694c4223c83ecadbbf96f791ccad - 4d0698e869f23d86fa8f3f5fc4601fc8 -

@ -17,7 +17,8 @@ pub fn print(
lhs_positions: &[MatchedPos], lhs_positions: &[MatchedPos],
rhs_positions: &[MatchedPos], rhs_positions: &[MatchedPos],
hunks: &[Hunk], hunks: &[Hunk],
display_path: &str, lhs_display_path: &str,
rhs_display_path: &str,
lang_name: &str, lang_name: &str,
) { ) {
let (lhs_colored, rhs_colored) = if display_options.use_color { let (lhs_colored, rhs_colored) = if display_options.use_color {
@ -51,7 +52,8 @@ pub fn print(
println!( println!(
"{}", "{}",
style::header( style::header(
display_path, lhs_display_path,
rhs_display_path,
i + 1, i + 1,
hunks.len(), hunks.len(),
lang_name, lang_name,

@ -133,10 +133,11 @@ fn main() {
byte_limit, byte_limit,
display_options, display_options,
missing_as_empty, missing_as_empty,
display_path,
language_override, language_override,
lhs_path, lhs_path,
rhs_path, rhs_path,
lhs_display_path,
rhs_display_path,
} => { } => {
let lhs_path = Path::new(&lhs_path); let lhs_path = Path::new(&lhs_path);
let rhs_path = Path::new(&rhs_path); let rhs_path = Path::new(&rhs_path);
@ -166,7 +167,8 @@ fn main() {
}); });
} else { } else {
let diff_result = diff_file( let diff_result = diff_file(
&display_path, &lhs_display_path,
&rhs_display_path,
lhs_path, lhs_path,
rhs_path, rhs_path,
&display_options, &display_options,
@ -183,7 +185,8 @@ fn main() {
/// Print a diff between two files. /// Print a diff between two files.
fn diff_file( fn diff_file(
display_path: &str, lhs_display_path: &str,
rhs_display_path: &str,
lhs_path: &Path, lhs_path: &Path,
rhs_path: &Path, rhs_path: &Path,
display_options: &DisplayOptions, display_options: &DisplayOptions,
@ -194,7 +197,8 @@ fn diff_file(
) -> DiffResult { ) -> DiffResult {
let (lhs_bytes, rhs_bytes) = read_files_or_die(lhs_path, rhs_path, missing_as_empty); let (lhs_bytes, rhs_bytes) = read_files_or_die(lhs_path, rhs_path, missing_as_empty);
diff_file_content( diff_file_content(
display_path, lhs_display_path,
rhs_display_path,
&lhs_bytes, &lhs_bytes,
&rhs_bytes, &rhs_bytes,
display_options.tab_width, display_options.tab_width,
@ -205,7 +209,8 @@ fn diff_file(
} }
fn diff_file_content( fn diff_file_content(
display_path: &str, lhs_display_path: &str,
rhs_display_path: &str,
lhs_bytes: &[u8], lhs_bytes: &[u8],
rhs_bytes: &[u8], rhs_bytes: &[u8],
tab_width: usize, tab_width: usize,
@ -215,7 +220,8 @@ fn diff_file_content(
) -> DiffResult { ) -> DiffResult {
if is_probably_binary(lhs_bytes) || is_probably_binary(rhs_bytes) { if is_probably_binary(lhs_bytes) || is_probably_binary(rhs_bytes) {
return DiffResult { return DiffResult {
path: display_path.into(), lhs_display_path: lhs_display_path.into(),
rhs_display_path: rhs_display_path.into(),
language: None, language: None,
lhs_src: FileContent::Binary(lhs_bytes.to_vec()), lhs_src: FileContent::Binary(lhs_bytes.to_vec()),
rhs_src: FileContent::Binary(rhs_bytes.to_vec()), rhs_src: FileContent::Binary(rhs_bytes.to_vec()),
@ -244,7 +250,7 @@ fn diff_file_content(
} }
// TODO: take a Path directly instead. // TODO: take a Path directly instead.
let path = Path::new(&display_path); let guess_path = Path::new(&rhs_display_path);
// Take the larger of the two files when guessing the // Take the larger of the two files when guessing the
// language. This is useful when we've added or removed a whole // language. This is useful when we've added or removed a whole
@ -254,14 +260,15 @@ fn diff_file_content(
} else { } else {
&rhs_src &rhs_src
}; };
let language = language_override.or_else(|| guess(path, guess_src)); let language = language_override.or_else(|| guess(guess_path, guess_src));
let lang_config = language.map(tsp::from_language); let lang_config = language.map(tsp::from_language);
if lhs_bytes == rhs_bytes { if lhs_bytes == rhs_bytes {
// If the two files are completely identical, return early // If the two files are completely identical, return early
// rather than doing any more work. // rather than doing any more work.
return DiffResult { return DiffResult {
path: display_path.into(), lhs_display_path: lhs_display_path.into(),
rhs_display_path: rhs_display_path.into(),
language: lang_config.map(|l| l.name.into()), language: lang_config.map(|l| l.name.into()),
lhs_src: FileContent::Text("".into()), lhs_src: FileContent::Text("".into()),
rhs_src: FileContent::Text("".into()), rhs_src: FileContent::Text("".into()),
@ -337,7 +344,8 @@ fn diff_file_content(
}; };
DiffResult { DiffResult {
path: display_path.into(), lhs_display_path: lhs_display_path.into(),
rhs_display_path: rhs_display_path.into(),
language: lang_name, language: lang_name,
lhs_src: FileContent::Text(lhs_src), lhs_src: FileContent::Text(lhs_src),
rhs_src: FileContent::Text(rhs_src), rhs_src: FileContent::Text(rhs_src),
@ -375,6 +383,7 @@ fn diff_directories<'a>(
diff_file( diff_file(
&rel_path.to_string_lossy(), &rel_path.to_string_lossy(),
&rel_path.to_string_lossy(), // todo
&lhs_path, &lhs_path,
&rhs_path, &rhs_path,
&display_options, &display_options,
@ -407,7 +416,8 @@ fn print_diff_result(display_options: &DisplayOptions, summary: &DiffResult) {
println!( println!(
"{}", "{}",
style::header( style::header(
&summary.path, &summary.lhs_display_path,
&summary.rhs_display_path,
1, 1,
1, 1,
&lang_name, &lang_name,
@ -435,7 +445,8 @@ fn print_diff_result(display_options: &DisplayOptions, summary: &DiffResult) {
&summary.lhs_positions, &summary.lhs_positions,
&summary.rhs_positions, &summary.rhs_positions,
&hunks, &hunks,
&summary.path, &summary.lhs_display_path,
&summary.rhs_display_path,
&lang_name, &lang_name,
); );
} }
@ -443,7 +454,8 @@ fn print_diff_result(display_options: &DisplayOptions, summary: &DiffResult) {
side_by_side::print( side_by_side::print(
&hunks, &hunks,
display_options, display_options,
&summary.path, &summary.lhs_display_path,
&summary.rhs_display_path,
&lang_name, &lang_name,
lhs_src, lhs_src,
rhs_src, rhs_src,
@ -459,7 +471,8 @@ fn print_diff_result(display_options: &DisplayOptions, summary: &DiffResult) {
println!( println!(
"{}", "{}",
style::header( style::header(
&summary.path, &summary.lhs_display_path,
&summary.rhs_display_path,
1, 1,
1, 1,
"binary", "binary",
@ -479,7 +492,8 @@ fn print_diff_result(display_options: &DisplayOptions, summary: &DiffResult) {
println!( println!(
"{}", "{}",
style::header( style::header(
&summary.path, &summary.lhs_display_path,
&summary.rhs_display_path,
1, 1,
1, 1,
"binary", "binary",
@ -524,6 +538,7 @@ mod tests {
fn test_diff_identical_content() { fn test_diff_identical_content() {
let s = "foo"; let s = "foo";
let res = diff_file_content( let res = diff_file_content(
"foo.el",
"foo.el", "foo.el",
s.as_bytes(), s.as_bytes(),
s.as_bytes(), s.as_bytes(),

@ -173,10 +173,17 @@ pub enum Mode {
byte_limit: usize, byte_limit: usize,
display_options: DisplayOptions, display_options: DisplayOptions,
missing_as_empty: bool, missing_as_empty: bool,
display_path: String,
language_override: Option<guess_language::Language>, language_override: Option<guess_language::Language>,
/// The path where we can read the LHS file. This is often a
/// temporary file generated by source control.
lhs_path: OsString, lhs_path: OsString,
/// The path where we can read the RHS file. This is often a
/// temporary file generated by source control.
rhs_path: OsString, rhs_path: OsString,
/// The path that we should display for the LHS file.
lhs_display_path: String,
/// The path that we should display for the RHS file.
rhs_display_path: String,
}, },
DumpTreeSitter { DumpTreeSitter {
path: String, path: String,
@ -225,8 +232,9 @@ pub fn parse_args() -> Mode {
info!("CLI arguments: {:?}", args); info!("CLI arguments: {:?}", args);
// TODO: document these different ways of calling difftastic. // TODO: document these different ways of calling difftastic.
let (display_path, lhs_path, rhs_path) = match &args[..] { let (lhs_display_path, rhs_display_path, lhs_path, rhs_path) = match &args[..] {
[lhs_path, rhs_path] => ( [lhs_path, rhs_path] => (
lhs_path.to_owned(),
rhs_path.to_owned(), rhs_path.to_owned(),
lhs_path.to_owned(), lhs_path.to_owned(),
rhs_path.to_owned(), rhs_path.to_owned(),
@ -234,17 +242,18 @@ pub fn parse_args() -> Mode {
[display_path, lhs_tmp_file, _lhs_hash, _lhs_mode, rhs_tmp_file, _rhs_hash, _rhs_mode] => { [display_path, lhs_tmp_file, _lhs_hash, _lhs_mode, rhs_tmp_file, _rhs_hash, _rhs_mode] => {
// https://git-scm.com/docs/git#Documentation/git.txt-codeGITEXTERNALDIFFcode // https://git-scm.com/docs/git#Documentation/git.txt-codeGITEXTERNALDIFFcode
( (
display_path.to_owned(),
display_path.to_owned(), display_path.to_owned(),
lhs_tmp_file.to_owned(), lhs_tmp_file.to_owned(),
rhs_tmp_file.to_owned(), rhs_tmp_file.to_owned(),
) )
} }
[_old_name, lhs_tmp_file, _lhs_hash, _lhs_mode, rhs_tmp_file, _rhs_hash, _rhs_mode, new_name, _similarity] => [old_name, lhs_tmp_file, _lhs_hash, _lhs_mode, rhs_tmp_file, _rhs_hash, _rhs_mode, new_name, _similarity] =>
{ {
// Rename file. // Rename file.
// TODO: mention old name as well as diffing.
// TODO: where does git document these 9 arguments? // TODO: where does git document these 9 arguments?
( (
old_name.to_owned(),
new_name.to_owned(), new_name.to_owned(),
lhs_tmp_file.to_owned(), lhs_tmp_file.to_owned(),
rhs_tmp_file.to_owned(), rhs_tmp_file.to_owned(),
@ -347,10 +356,11 @@ pub fn parse_args() -> Mode {
byte_limit, byte_limit,
display_options, display_options,
missing_as_empty, missing_as_empty,
display_path: display_path.to_string_lossy().to_string(),
language_override, language_override,
lhs_path: lhs_path.to_owned(), lhs_path: lhs_path.to_owned(),
rhs_path: rhs_path.to_owned(), rhs_path: rhs_path.to_owned(),
lhs_display_path: lhs_display_path.to_string_lossy().to_string(),
rhs_display_path: rhs_display_path.to_string_lossy().to_string(),
} }
} }

@ -78,7 +78,8 @@ fn format_missing_line_num(
/// Display `src` in a single column (e.g. a file removal or addition). /// Display `src` in a single column (e.g. a file removal or addition).
fn display_single_column( fn display_single_column(
display_path: &str, lhs_display_path: &str,
rhs_display_path: &str,
lang_name: &str, lang_name: &str,
src: &str, src: &str,
is_lhs: bool, is_lhs: bool,
@ -89,7 +90,8 @@ fn display_single_column(
let mut result = String::with_capacity(src.len()); let mut result = String::with_capacity(src.len());
result.push_str(&style::header( result.push_str(&style::header(
display_path, lhs_display_path,
rhs_display_path,
1, 1,
1, 1,
lang_name, lang_name,
@ -300,7 +302,8 @@ fn highlight_as_novel(
pub fn print( pub fn print(
hunks: &[Hunk], hunks: &[Hunk],
display_options: &DisplayOptions, display_options: &DisplayOptions,
display_path: &str, lhs_display_path: &str,
rhs_display_path: &str,
lang_name: &str, lang_name: &str,
lhs_src: &str, lhs_src: &str,
rhs_src: &str, rhs_src: &str,
@ -332,7 +335,8 @@ pub fn print(
println!( println!(
"{}", "{}",
display_single_column( display_single_column(
display_path, lhs_display_path,
rhs_display_path,
lang_name, lang_name,
&rhs_colored_src, &rhs_colored_src,
false, false,
@ -346,7 +350,8 @@ pub fn print(
println!( println!(
"{}", "{}",
display_single_column( display_single_column(
display_path, lhs_display_path,
rhs_display_path,
lang_name, lang_name,
&lhs_colored_src, &lhs_colored_src,
true, true,
@ -385,7 +390,8 @@ pub fn print(
println!( println!(
"{}", "{}",
style::header( style::header(
display_path, lhs_display_path,
rhs_display_path,
i + 1, i + 1,
hunks.len(), hunks.len(),
lang_name, lang_name,
@ -628,6 +634,7 @@ mod tests {
fn test_display_single_column() { fn test_display_single_column() {
// Basic smoke test. // Basic smoke test.
let res = display_single_column( let res = display_single_column(
"foo.py",
"foo.py", "foo.py",
"Python", "Python",
"print(123)\n", "print(123)\n",
@ -719,7 +726,8 @@ mod tests {
print( print(
&hunks, &hunks,
&display_options, &display_options,
"foo.el", "foo-old.el",
"foo-new.el",
"Emacs Lisp", "Emacs Lisp",
"foo", "foo",
"bar", "bar",

@ -323,32 +323,46 @@ pub fn apply_colors(
apply(s, &styles) apply(s, &styles)
} }
fn apply_header_color(s: &str, use_color: bool, background: BackgroundColor) -> String {
if use_color {
if background.is_dark() {
s.bright_yellow().to_string()
} else {
s.yellow().to_string()
}
.bold()
.to_string()
} else {
s.to_string()
}
}
pub fn header( pub fn header(
file_name: &str, lhs_display_path: &str,
rhs_display_path: &str,
hunk_num: usize, hunk_num: usize,
hunk_total: usize, hunk_total: usize,
language_name: &str, language_name: &str,
use_color: bool, use_color: bool,
background: BackgroundColor, background: BackgroundColor,
) -> String { ) -> String {
let file_name_pretty = if use_color {
if background.is_dark() {
file_name.bright_yellow().to_string()
} else {
file_name.yellow().to_string()
}
.bold()
.to_string()
} else {
file_name.to_string()
};
let divider = if hunk_total == 1 { let divider = if hunk_total == 1 {
"".to_owned() "".to_owned()
} else { } else {
format!("{}/{} --- ", hunk_num, hunk_total) format!("{}/{} --- ", hunk_num, hunk_total)
}; };
format!("{} --- {}{}", file_name_pretty, divider, language_name) let rhs_path_pretty = apply_header_color(rhs_display_path, use_color, background);
if hunk_num == 1 && lhs_display_path != rhs_display_path {
let lhs_path_pretty = apply_header_color(lhs_display_path, use_color, background);
let renamed = format!("Renamed {} to {}", lhs_path_pretty, rhs_path_pretty,);
format!(
"{}\n{} --- {}{}",
renamed, rhs_path_pretty, divider, language_name
)
} else {
format!("{} --- {}{}", rhs_path_pretty, divider, language_name)
}
} }
#[cfg(test)] #[cfg(test)]

@ -8,7 +8,8 @@ pub enum FileContent {
#[derive(Debug)] #[derive(Debug)]
pub struct DiffResult { pub struct DiffResult {
pub path: String, pub lhs_display_path: String,
pub rhs_display_path: String,
pub language: Option<String>, pub language: Option<String>,
pub lhs_src: FileContent, pub lhs_src: FileContent,
pub rhs_src: FileContent, pub rhs_src: FileContent,