From 694f017e3c02bf84a8e6b86967ab2694c11eadea Mon Sep 17 00:00:00 2001 From: Blair Noctis Date: Sat, 25 Nov 2023 19:05:19 +0800 Subject: [PATCH] feat: diff view with --diff (-d) flag --- Cargo.lock | 59 ++++++++++++++++++++++++++++++++--------------------- Cargo.toml | 1 + src/cli.rs | 5 +++++ src/diff.rs | 28 +++++++++++++++++++++++++ src/main.rs | 20 +++++++++++++++++- 5 files changed, 89 insertions(+), 24 deletions(-) create mode 100644 src/diff.rs diff --git a/Cargo.lock b/Cargo.lock index 2123179..992624a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -82,7 +82,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88903cb14723e4d4003335bb7f8a14f27691649105346a0f0957466c096adfe6" dependencies = [ "anstyle", - "bstr", + "bstr 1.7.0", "doc-comment", "predicates", "predicates-core", @@ -123,6 +123,15 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +[[package]] +name = "bstr" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +dependencies = [ + "memchr", +] + [[package]] name = "bstr" version = "1.7.0" @@ -142,9 +151,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.4.6" +version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956" +checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b" dependencies = [ "clap_builder", "clap_derive", @@ -152,9 +161,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.6" +version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45" +checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663" dependencies = [ "anstream", "anstyle", @@ -165,18 +174,18 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.4.3" +version = "4.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ae8ba90b9d8b007efe66e55e48fb936272f5ca00349b5b0e89877520d35ea7" +checksum = "bffe91f06a11b4b9420f62103854e90867812cd5d01557f853c5ee8e791b12ae" dependencies = [ "clap", ] [[package]] name = "clap_derive" -version = "4.4.2" +version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" dependencies = [ "heck", "proc-macro2", @@ -186,15 +195,15 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" [[package]] name = "clap_mangen" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b44f35c514163027542f7147797ff930523eea288e03642727348ef1a9666f6b" +checksum = "d3be86020147691e1d2ef58f75346a3d4d94807bfc473e377d52f09f0f7d77f7" dependencies = [ "clap", "roff", @@ -540,9 +549,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.3.5" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ "bitflags 1.3.2", ] @@ -590,9 +599,9 @@ checksum = "b833d8d034ea094b1ea68aa6d5c740e0d04bad9d16568d08ba6f76823a114316" [[package]] name = "rustix" -version = "0.38.20" +version = "0.38.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67ce50cb2e16c2903e30d1cbccfd8387a74b9d4c938b6a4c5ec6cc7556f7a8a0" +checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" dependencies = [ "bitflags 2.4.1", "errno", @@ -635,6 +644,7 @@ dependencies = [ "rayon", "regex", "regex-automata", + "similar", "tempfile", "thiserror", "unescape", @@ -642,18 +652,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.189" +version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537" +checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.189" +version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" +checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3" dependencies = [ "proc-macro2", "quote", @@ -665,6 +675,9 @@ name = "similar" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2aeaf503862c419d66959f5d7ca015337d864e9c49485d771b732e2a20453597" +dependencies = [ + "bstr 0.2.17", +] [[package]] name = "strsim" @@ -685,9 +698,9 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.8.0" +version = "3.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" +checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" dependencies = [ "cfg-if", "fastrand", diff --git a/Cargo.toml b/Cargo.toml index e44aede..27eb4c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ memmap2 = "0.9.0" tempfile = "3.8.0" thiserror = "1.0.50" clap.workspace = true +similar = { version = "2.3.0", features = ["inline", "bytes"] } [dev-dependencies] assert_cmd = "2.0.12" diff --git a/src/cli.rs b/src/cli.rs index e96e609..c3482aa 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -20,6 +20,11 @@ pub struct Options { /// format are likely to change in the future). pub preview: bool, + #[arg(short, long)] + /// Show changes as a unified diff. You may want to use a rich + /// color pager to colorize it. + pub diff: bool, + #[arg( short = 'F', long = "fixed-strings", diff --git a/src/diff.rs b/src/diff.rs new file mode 100644 index 0000000..ec7fdff --- /dev/null +++ b/src/diff.rs @@ -0,0 +1,28 @@ +use similar::TextDiff; +use std::path::Path; + +use crate::error::{Error, Result}; + +pub(crate) fn create_udiff, UN: AsRef<[u8]>>( + old: UO, + new: UN, + context: usize, + path: Option<&Path>, +) -> Result { + let diff = TextDiff::from_lines(old.as_ref(), new.as_ref()); + let path = if let Some(path) = path { + if let Some(spath) = path.to_str() { + spath + } else { + return Err(Error::InvalidPath(path.into())); + } + } else { + "" + }; + + let mut udiff = diff.unified_diff(); + udiff.header(path, path); + udiff.context_radius(context); + + Ok(udiff.to_string()) +} diff --git a/src/main.rs b/src/main.rs index 8fa735f..1f12911 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ mod cli; mod error; mod input; +pub(crate) mod diff; pub(crate) mod replacer; use clap::Parser; @@ -69,7 +70,9 @@ fn try_main() -> Result<()> { .collect() }; - if options.preview || sources.first() == Some(&Source::Stdin) { + if options.preview + || sources.first() == Some(&Source::Stdin) && !options.diff + { let mut handle = stdout().lock(); for (source, replaced) in sources.iter().zip(replaced) { @@ -78,6 +81,21 @@ fn try_main() -> Result<()> { } handle.write_all(&replaced)?; } + } else if options.diff { + use crate::diff::create_udiff; + + let mut handle = stdout().lock(); + + for ((source, mmap), replaced) in + sources.iter().zip(&mmaps).zip(replaced) + { + let path = match source { + Source::Stdin => None, + Source::File(path) => Some(path.as_path()), + }; + // TODO: custom context radius + writeln!(handle, "{}", create_udiff(mmap, replaced, 3, path)?)?; + } } else { // Windows requires closing mmap before writing: // > The requested operation cannot be performed on a file with a user-mapped section open