diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 18:31:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 18:31:44 +0000 |
commit | c23a457e72abe608715ac76f076f47dc42af07a5 (patch) | |
tree | 2772049aaf84b5c9d0ed12ec8d86812f7a7904b6 /vendor/prettydiff | |
parent | Releasing progress-linux version 1.73.0+dfsg1-1~progress7.99u1. (diff) | |
download | rustc-c23a457e72abe608715ac76f076f47dc42af07a5.tar.xz rustc-c23a457e72abe608715ac76f076f47dc42af07a5.zip |
Merging upstream version 1.74.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/prettydiff')
-rw-r--r-- | vendor/prettydiff/.cargo-checksum.json | 1 | ||||
-rw-r--r-- | vendor/prettydiff/Cargo.lock | 622 | ||||
-rw-r--r-- | vendor/prettydiff/Cargo.toml | 53 | ||||
-rw-r--r-- | vendor/prettydiff/LICENSE | 21 | ||||
-rw-r--r-- | vendor/prettydiff/README.md | 135 | ||||
-rw-r--r-- | vendor/prettydiff/src/basic.rs | 169 | ||||
-rw-r--r-- | vendor/prettydiff/src/format_table.rs | 38 | ||||
-rw-r--r-- | vendor/prettydiff/src/lcs.rs | 227 | ||||
-rw-r--r-- | vendor/prettydiff/src/lib.rs | 20 | ||||
-rw-r--r-- | vendor/prettydiff/src/main.rs | 51 | ||||
-rw-r--r-- | vendor/prettydiff/src/text.rs | 877 |
11 files changed, 2214 insertions, 0 deletions
diff --git a/vendor/prettydiff/.cargo-checksum.json b/vendor/prettydiff/.cargo-checksum.json new file mode 100644 index 000000000..ecd9fa9dd --- /dev/null +++ b/vendor/prettydiff/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.lock":"87cb860de6bc0d894deaf44641c4034ebec8f0ae8a283f160ae6d7b875deb343","Cargo.toml":"a769bd8cdeb6fb5bf404b07d43b33f143509fe0e68f8f883ac87001d0f8d3999","LICENSE":"95b7a3611fd946d5a09aaa5f5270dc97632980e394fe749e1e5b6a5156716a3f","README.md":"7c3efef58b23289741daab7f048b26d758a0418cb304d24ea78f2db37a780c33","screens/app.png":"2f6c9cc5dd759920789b23d228125d2aab4f4ff59de43e418e92f8a645a88a1b","screens/diff_chars.png":"74adc717d599b53ffd1460a7c381d9be366d04308c4eb0672c9baf09cbffef0b","screens/diff_lines.png":"9e003be6b27837a08d38d216c381d16ecd3dc1cf3abcb387b72f7455e3b23263","screens/diff_slice.png":"5c5f0fde321b4c18db923bd0d53db6097ae673c9d5bc5f1748ea81f0010196ae","src/basic.rs":"4a312f1b541bc252efbdb42a5887d23d6351b2445c157becc7729ef8987460de","src/format_table.rs":"09faa7f16c0bb9db61cc63044e008ee86e88c479f3254dfd2af81f7679e6f726","src/lcs.rs":"91e6afc5e0a28354cf20446f8a308c0a37d1c7849fb564e5355b01141f0b89a9","src/lib.rs":"ad47c5609a4036579f86399c42dd48aec18ca683d77ebf8468fa01b8c4d42b53","src/main.rs":"3f95441205e1021c337abbb56ec40e31e1161a7c6eefe0e19e745004c48e5bb8","src/text.rs":"6dc399b32c32e6607f4c4e3556dede049c7b7e33e685b056c1c44176ec0e917a"},"package":"8ff1fec61082821f8236cf6c0c14e8172b62ce8a72a0eedc30d3b247bb68dc11"}
\ No newline at end of file diff --git a/vendor/prettydiff/Cargo.lock b/vendor/prettydiff/Cargo.lock new file mode 100644 index 000000000..3eab886cc --- /dev/null +++ b/vendor/prettydiff/Cargo.lock @@ -0,0 +1,622 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "csv" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b015497079b9a9d69c02ad25de6c0a6edef051ea6360a327d0bd05802ef64ad" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" +dependencies = [ + "memchr", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + +[[package]] +name = "errno" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d6a0976c999d473fe89ad888d5a284e55366d9dc9038b1ba2aa15128c4afa0" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.45.0", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + +[[package]] +name = "io-lifetimes" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +dependencies = [ + "hermit-abi 0.3.1", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "is-terminal" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "256017f749ab3117e93acb91063009e1f1bb56d03965b14c2c8df4eb02c524d8" +dependencies = [ + "hermit-abi 0.3.1", + "io-lifetimes", + "rustix", + "windows-sys 0.45.0", +] + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.141" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" + +[[package]] +name = "linux-raw-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "pad" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ad9b889f1b12e0b9ee24db044b5129150d5eada288edc800f789928dc8c0e3" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "prettydiff" +version = "0.6.4" +dependencies = [ + "ansi_term", + "pad", + "prettytable-rs", + "structopt", +] + +[[package]] +name = "prettytable-rs" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eea25e07510aa6ab6547308ebe3c036016d162b8da920dbb079e3ba8acf3d95a" +dependencies = [ + "csv", + "encode_unicode", + "is-terminal", + "lazy_static", + "term", + "unicode-width", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom", + "redox_syscall", + "thiserror", +] + +[[package]] +name = "rustix" +version = "0.37.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aae838e49b3d63e9274e1c01833cc8139d3fec468c3b84688c628f44b1ae11d" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.45.0", +] + +[[package]] +name = "rustversion" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" + +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "serde" +version = "1.0.159" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065" + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "structopt" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" +dependencies = [ + "clap", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "term" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +dependencies = [ + "dirs-next", + "rustversion", + "winapi", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.13", +] + +[[package]] +name = "unicode-ident" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" diff --git a/vendor/prettydiff/Cargo.toml b/vendor/prettydiff/Cargo.toml new file mode 100644 index 000000000..6b398dcaf --- /dev/null +++ b/vendor/prettydiff/Cargo.toml @@ -0,0 +1,53 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2018" +name = "prettydiff" +version = "0.6.4" +authors = ["Roman Koblov <penpen938@me.com>"] +description = "Side-by-side diff for two files" +homepage = "https://github.com/romankoblov/prettydiff" +readme = "README.md" +keywords = [ + "diff", + "text", + "compare", + "changes", +] +categories = ["text-processing"] +license = "MIT" +repository = "https://github.com/romankoblov/prettydiff" + +[[bin]] +name = "prettydiff" +required-features = ["cli"] + +[dependencies.ansi_term] +version = "0.12" + +[dependencies.pad] +version = "0.1.6" + +[dependencies.prettytable-rs] +version = "0.10.0" +optional = true + +[dependencies.structopt] +version = "0.3" +optional = true + +[features] +cli = [ + "prettytable-rs", + "structopt", +] +default = ["cli"] diff --git a/vendor/prettydiff/LICENSE b/vendor/prettydiff/LICENSE new file mode 100644 index 000000000..86ace6afc --- /dev/null +++ b/vendor/prettydiff/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Roman Koblov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/prettydiff/README.md b/vendor/prettydiff/README.md new file mode 100644 index 000000000..3d2aa64ee --- /dev/null +++ b/vendor/prettydiff/README.md @@ -0,0 +1,135 @@ +# prettydiff + +[![Crate](https://img.shields.io/crates/v/prettydiff.svg)](https://crates.io/crates/prettydiff) +[![docs.rs](https://docs.rs/prettydiff/badge.svg)](https://docs.rs/prettydiff) + +Side-by-side diff for two files in Rust. App && Library. + +## Examples + +### Slice diff + +```rust +use prettydiff::diff_slice; + +println!("Diff: {}", diff_slice(&[1, 2, 3, 4, 5, 6], &[2, 3, 5, 7])); +println!( + "Diff: {}", + diff_slice(&["q", "a", "b", "x", "c", "d"], &["a", "b", "y", "c", "d", "f"]) +); +println!( + "Diff: {}", + diff_slice(&["a", "c", "d", "b"], &["a", "e", "b"]) +); + ``` + + ![diff_slice](https://raw.githubusercontent.com/romankoblov/prettydiff/master/screens/diff_slice.png) + +Get vector of changes: + +```rust +use prettydiff::diff_slice; + +assert_eq!( + diff_slice(&["q", "a", "b", "x", "c", "d"], &["a", "b", "y", "c", "d", "f"]).diff, + vec![ + DiffOp::Remove(&["q"]), + DiffOp::Equal(&["a", "b"]), + DiffOp::Replace(&["x"], &["y"]), + DiffOp::Equal(&["c", "d"]), + DiffOp::Insert(&["f"]), + ] +); +``` + +### Diff line by chars or words + +![diff_chars](https://raw.githubusercontent.com/romankoblov/prettydiff/master/screens/diff_chars.png) + +```rust +use prettydiff::{diff_chars, diff_words}; + +println!("diff_chars: {}", diff_chars("abefcd", "zadqwc")); +println!( + "diff_chars: {}", + diff_chars( + "The quick brown fox jumps over the lazy dog", + "The quick brown dog leaps over the lazy cat" + ) +); +println!( + "diff_chars: {}", + diff_chars( + "The red brown fox jumped over the rolling log", + "The brown spotted fox leaped over the rolling log" + ) +); +println!( + "diff_chars: {}", + diff_chars( + "The red brown fox jumped over the rolling log", + "The brown spotted fox leaped over the rolling log" + ) + .set_highlight_whitespace(true) +); +println!( + "diff_words: {}", + diff_words( + "The red brown fox jumped over the rolling log", + "The brown spotted fox leaped over the rolling log" + ) +); +println!( + "diff_words: {}", + diff_words( + "The quick brown fox jumps over the lazy dog", + "The quick, brown dog leaps over the lazy cat" + ) +); +``` + +### Diff lines + +![diff_lines](https://raw.githubusercontent.com/romankoblov/prettydiff/master/screens/diff_lines.png) + +```rust +use prettydiff::diff_lines; + +let code1_a = r#" +void func1() { + x += 1 +} + +void func2() { + x += 2 +} + "#; +let code1_b = r#" +void func1(a: u32) { + x += 1 +} + +void functhreehalves() { + x += 1.5 +} + +void func2() { + x += 2 +} + +void func3(){} +"#; +println!("diff_lines:"); +println!("{}", diff_lines(code1_a, code1_b)); +``` + +## App + +This crate also provides app for side-by-side diff: + +```sh +cargo install prettydiff +prettydiff left_file.txt right_file.txt +``` + +![App](https://raw.githubusercontent.com/romankoblov/prettydiff/master/screens/app.png)
\ No newline at end of file diff --git a/vendor/prettydiff/src/basic.rs b/vendor/prettydiff/src/basic.rs new file mode 100644 index 000000000..d35392a7b --- /dev/null +++ b/vendor/prettydiff/src/basic.rs @@ -0,0 +1,169 @@ +//! Basic diff functions +use crate::lcs; +use ansi_term::Colour; +use std::fmt; + +/// Single change in original slice needed to get new slice +#[derive(Debug, PartialEq, Eq)] +pub enum DiffOp<'a, T: 'a> { + /// Appears only in second slice + Insert(&'a [T]), + /// Appears in both slices, but changed + Replace(&'a [T], &'a [T]), + /// Appears only in first slice + Remove(&'a [T]), + /// Appears on both slices + Equal(&'a [T]), +} + +/// Diffs any slices which implements PartialEq +pub fn diff<'a, T: PartialEq>(x: &'a [T], y: &'a [T]) -> Vec<DiffOp<'a, T>> { + let mut ops: Vec<DiffOp<T>> = Vec::new(); + let table = lcs::Table::new(x, y); + + let mut i = 0; + let mut j = 0; + + for m in table.matches_zero() { + let x_seq = &x[i..m.x]; + let y_seq = &y[j..m.y]; + + if i < m.x && j < m.y { + ops.push(DiffOp::Replace(x_seq, y_seq)); + } else if i < m.x { + ops.push(DiffOp::Remove(x_seq)); + } else if j < m.y { + ops.push(DiffOp::Insert(y_seq)); + } + + i = m.x + m.len; + j = m.y + m.len; + + if m.len > 0 { + ops.push(DiffOp::Equal(&x[m.x..i])); + } + } + ops +} + +/// Container for slice diff result. Can be pretty-printed by Display trait. +#[derive(Debug, PartialEq, Eq)] +pub struct SliceChangeset<'a, T> { + pub diff: Vec<DiffOp<'a, T>>, +} + +impl<'a, T: fmt::Display> SliceChangeset<'a, T> { + pub fn format(&self, skip_same: bool) -> String { + let mut out: Vec<String> = Vec::with_capacity(self.diff.len()); + for op in &self.diff { + match op { + DiffOp::Equal(a) => { + if !skip_same || a.len() == 1 { + for i in a.iter() { + out.push(format!(" {}", i)) + } + } else if a.len() > 1 { + out.push(format!(" ... skip({}) ...", a.len())); + } + } + + DiffOp::Insert(a) => { + for i in a.iter() { + out.push(Colour::Green.paint(format!("+ {}", i)).to_string()); + } + } + + DiffOp::Remove(a) => { + for i in a.iter() { + out.push(Colour::Red.paint(format!("- {}", i)).to_string()); + } + } + DiffOp::Replace(a, b) => { + let min_len = std::cmp::min(a.len(), b.len()); + let max_len = std::cmp::max(a.len(), b.len()); + + for i in 0..min_len { + out.push( + Colour::Yellow + .paint(format!("~ {} -> {}", a[i], b[i])) + .to_string(), + ); + } + for i in min_len..max_len { + if max_len == a.len() { + out.push(Colour::Red.paint(format!("- {}", a[i])).to_string()); + } else { + out.push(Colour::Green.paint(format!("+ {}", b[i])).to_string()); + } + } + } + } + } + format!("[\n{}\n]", out.join(",\n")) + } +} + +impl<'a, T: fmt::Display> fmt::Display for SliceChangeset<'a, T> { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "{}", self.format(true)) + } +} + +/// Diff two arbitary slices with elements that support Display trait +pub fn diff_slice<'a, T: PartialEq + std::fmt::Display>( + x: &'a [T], + y: &'a [T], +) -> SliceChangeset<'a, T> { + let diff = diff(x, y); + SliceChangeset { diff } +} + +#[test] +fn test_basic() { + assert_eq!( + diff(&[1, 2, 3, 4, 5, 6], &[2, 3, 5, 7]), + vec![ + DiffOp::Remove(&[1]), + DiffOp::Equal(&[2, 3]), + DiffOp::Remove(&[4]), + DiffOp::Equal(&[5]), + DiffOp::Replace(&[6], &[7]), + ] + ); + + assert_eq!( + diff_slice( + &["q", "a", "b", "x", "c", "d"], + &["a", "b", "y", "c", "d", "f"], + ) + .diff, + vec![ + DiffOp::Remove(&["q"]), + DiffOp::Equal(&["a", "b"]), + DiffOp::Replace(&["x"], &["y"]), + DiffOp::Equal(&["c", "d"]), + DiffOp::Insert(&["f"]), + ] + ); + + assert_eq!( + diff(&["a", "c", "d", "b"], &["a", "e", "b"]), + vec![ + DiffOp::Equal(&["a"]), + DiffOp::Replace(&["c", "d"], &["e"]), + DiffOp::Equal(&["b"]), + ] + ); + println!("Diff: {}", diff_slice(&[1, 2, 3, 4, 5, 6], &[2, 3, 5, 7])); + println!( + "Diff: {}", + diff_slice( + &["q", "a", "b", "x", "c", "d"], + &["a", "b", "y", "c", "d", "f"] + ) + ); + println!( + "Diff: {}", + diff_slice(&["a", "c", "d", "b"], &["a", "e", "b"]) + ); +} diff --git a/vendor/prettydiff/src/format_table.rs b/vendor/prettydiff/src/format_table.rs new file mode 100644 index 000000000..7293faefd --- /dev/null +++ b/vendor/prettydiff/src/format_table.rs @@ -0,0 +1,38 @@ +//! Setup unicode-formatted table for prettytable +//! +//! TODO: Move to separate crate + +use prettytable::format; +use prettytable::Table; + +fn format_table(table: &mut Table) { + table.set_format( + format::FormatBuilder::new() + .column_separator('│') + .borders('│') + .separators( + &[format::LinePosition::Top], + format::LineSeparator::new('─', '┬', '┌', '┐'), + ) + .separators( + &[format::LinePosition::Title], + format::LineSeparator::new('─', '┼', '├', '┤'), + ) + .separators( + &[format::LinePosition::Intern], + format::LineSeparator::new('─', '┼', '├', '┤'), + ) + .separators( + &[format::LinePosition::Bottom], + format::LineSeparator::new('─', '┴', '└', '┘'), + ) + .padding(1, 1) + .build(), + ); +} +/// Returns Table with unicode formatter +pub fn new() -> Table { + let mut table = Table::new(); + format_table(&mut table); + table +} diff --git a/vendor/prettydiff/src/lcs.rs b/vendor/prettydiff/src/lcs.rs new file mode 100644 index 000000000..fc57edc77 --- /dev/null +++ b/vendor/prettydiff/src/lcs.rs @@ -0,0 +1,227 @@ +//! Common functions for [Longest common subsequences](https://en.wikipedia.org/wiki/Longest_common_subsequence_problem) +//! on slice. + +cfg_prettytable! { + use crate::format_table; + use prettytable::{Cell, Row}; +} +use std::cmp::max; + +#[derive(Debug)] +pub struct Table<'a, T: 'a> { + x: &'a [T], + y: &'a [T], + table: Vec<Vec<usize>>, +} + +/// Implements Longest Common Subsequences Table +/// Memory requirement: O(N^2) +/// +/// Based on [Wikipedia article](https://en.wikipedia.org/wiki/Longest_common_subsequence_problem) +impl<'a, T> Table<'a, T> +where + T: PartialEq, +{ + /// Creates new table for search common subsequences in x and y + pub fn new(x: &'a [T], y: &'a [T]) -> Table<'a, T> { + let x_len = x.len() + 1; + let y_len = y.len() + 1; + let mut table = vec![vec![0; y_len]; x_len]; + + for i in 1..x_len { + for j in 1..y_len { + table[i][j] = if x[i - 1] == y[j - 1] { + table[i - 1][j - 1] + 1 + } else { + max(table[i][j - 1], table[i - 1][j]) + }; + } + } + + Table { x, y, table } + } + + fn seq_iter(&self) -> TableIter<T> { + TableIter { + x: self.x.len(), + y: self.y.len(), + table: self, + } + } + fn get_match(&self, x: usize, y: usize, len: usize) -> Match<T> { + Match { + x, + y, + len, + table: self, + } + } + + /// Returns matches between X and Y + pub fn matches(&self) -> Vec<Match<T>> { + let mut matches: Vec<Match<T>> = Vec::new(); + for (x, y) in self.seq_iter() { + if let Some(last) = matches.last_mut() { + if last.x == x + 1 && last.y == y + 1 { + last.x = x; + last.y = y; + last.len += 1; + continue; + } + } + matches.push(self.get_match(x, y, 1)); + } + matches.reverse(); + matches + } + + /// Returns matches between X and Y with zero-len match at the end + pub fn matches_zero(&self) -> Vec<Match<T>> { + let mut matches = self.matches(); + matches.push(self.get_match(self.x.len(), self.y.len(), 0)); + matches + } + + /// Find longest sequence + pub fn longest_seq(&self) -> Vec<&T> { + self.matches(); + let mut common: Vec<_> = self.seq_iter().map(|(x, _y)| &self.x[x]).collect(); + common.reverse(); + common + } +} + +#[cfg(feature = "prettytable-rs")] +/// Prints pretty-table for LCS +impl<'a, T> std::fmt::Display for Table<'a, T> +where + T: std::fmt::Display, +{ + fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + let mut table = format_table::new(); + let mut header = vec!["".to_string(), "Ø".to_string()]; + for i in self.x { + header.push(format!("{}", i)); + } + + table.set_titles(Row::new( + header.into_iter().map(|i| Cell::new(&i)).collect(), + )); + for j in 0..=self.y.len() { + let mut row = vec![if j == 0 { + "Ø".to_string() + } else { + format!("{}", self.y[j - 1]) + }]; + for i in 0..=self.x.len() { + row.push(format!("{}", self.table[i][j])); + } + table.add_row(row.into_iter().map(|i| Cell::new(&i)).collect()); + } + write!(formatter, "\n{}", table) + } +} + +struct TableIter<'a, T: 'a> { + x: usize, + y: usize, + table: &'a Table<'a, T>, +} + +impl<'a, T> Iterator for TableIter<'a, T> { + type Item = (usize, usize); + fn next(&mut self) -> Option<Self::Item> { + let table = &self.table.table; + + while self.x != 0 && self.y != 0 { + let cur = table[self.x][self.y]; + + if cur == table[self.x - 1][self.y] { + self.x -= 1; + continue; + } + self.y -= 1; + if cur == table[self.x][self.y] { + continue; + } + self.x -= 1; + return Some((self.x, self.y)); + } + None + } +} + +pub struct Match<'a, T: 'a> { + pub x: usize, + pub y: usize, + pub len: usize, + table: &'a Table<'a, T>, +} + +impl<'a, T> Match<'a, T> { + /// Returns matched sequence + pub fn seq(&self) -> &[T] { + &self.table.x[self.x..(self.x + self.len)] + } +} + +#[test] +fn test_table() { + let x = vec!["A", "G", "C", "A", "T"]; + let y = vec!["G", "A", "C"]; + + let table = Table::new(&x, &y); + assert_eq!( + format!("{}", table), + r#" +┌───┬───┬───┬───┬───┬───┬───┐ +│ │ Ø │ A │ G │ C │ A │ T │ +├───┼───┼───┼───┼───┼───┼───┤ +│ Ø │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ +├───┼───┼───┼───┼───┼───┼───┤ +│ G │ 0 │ 0 │ 1 │ 1 │ 1 │ 1 │ +├───┼───┼───┼───┼───┼───┼───┤ +│ A │ 0 │ 1 │ 1 │ 1 │ 2 │ 2 │ +├───┼───┼───┼───┼───┼───┼───┤ +│ C │ 0 │ 1 │ 1 │ 2 │ 2 │ 2 │ +└───┴───┴───┴───┴───┴───┴───┘ +"# + ); + assert_eq!(table.longest_seq(), vec![&"A", &"C"]); +} + +#[test] + +fn test_table_match() { + let test_v = vec![ + ( + "The quick brown fox jumps over the lazy dog", + "The quick brown dog leaps over the lazy cat", + "The quick brown o ps over the lazy ", + vec!["The quick brown ", "o", " ", "ps over the lazy "], + ), + ("ab:c", "ba:b:c", "ab:c", vec!["a", "b:c"]), + ( + "The red brown fox jumped over the rolling log", + "The brown spotted fox leaped over the rolling log", + "The brown fox ped over the rolling log", + vec!["The ", "brown ", "fox ", "ped over the rolling log"], + ), + ]; + for (x_str, y_str, exp_str, match_exp) in test_v { + let x: Vec<_> = x_str.split("").collect(); + let y: Vec<_> = y_str.split("").collect(); + + // Trim empty elements + let table = Table::new(&x[1..(x.len() - 1)], &y[1..(y.len() - 1)]); + let seq = table + .longest_seq() + .iter() + .map(|i| i.to_string()) + .collect::<Vec<String>>() + .join(""); + assert_eq!(seq, exp_str); + let matches: Vec<_> = table.matches().iter().map(|m| m.seq().join("")).collect(); + assert_eq!(matches, match_exp); + } +} diff --git a/vendor/prettydiff/src/lib.rs b/vendor/prettydiff/src/lib.rs new file mode 100644 index 000000000..ff9dae6ce --- /dev/null +++ b/vendor/prettydiff/src/lib.rs @@ -0,0 +1,20 @@ +macro_rules! cfg_prettytable {( $($item:item)* ) => ( + $( + #[cfg(feature = "prettytable-rs")] + $item + )* +)} + +#[cfg(feature = "prettytable-rs")] +#[macro_use] +extern crate prettytable; + +pub mod basic; +cfg_prettytable! { + pub mod format_table; +} +pub mod lcs; +pub mod text; + +pub use crate::basic::diff_slice; +pub use crate::text::{diff_chars, diff_lines, diff_words}; diff --git a/vendor/prettydiff/src/main.rs b/vendor/prettydiff/src/main.rs new file mode 100644 index 000000000..9b6a59bbd --- /dev/null +++ b/vendor/prettydiff/src/main.rs @@ -0,0 +1,51 @@ +use std::fs::File; +use std::io::prelude::*; +use std::path::PathBuf; +use structopt::StructOpt; + +/// Side-by-side diff for two files +#[derive(StructOpt, Debug)] +#[structopt(name = "prettydiff")] +struct Opt { + /// Left file + #[structopt(name = "LEFT", parse(from_os_str))] + left: PathBuf, + /// Right file + #[structopt(name = "RIGHT", parse(from_os_str))] + right: PathBuf, + /// Don't show lines numbers + #[structopt(long = "disable_lines")] + disable_lines: bool, + /// Show non-changed blocks + #[structopt(long = "show_same")] + show_same: bool, + /// Align new lines inside change block + #[structopt(long = "disable_align")] + disable_align: bool, +} + +fn read_file(path: &PathBuf) -> std::io::Result<String> { + let mut file = File::open(path)?; + let mut contents = String::new(); + file.read_to_string(&mut contents)?; + Ok(contents) +} + +fn main() -> std::io::Result<()> { + let opt = Opt::from_args(); + + let left_data = read_file(&opt.left)?; + let left_name = opt.left.into_os_string().into_string().unwrap(); + + let right_data = read_file(&opt.right)?; + let right_name = opt.right.into_os_string().into_string().unwrap(); + + prettydiff::diff_lines(&left_data, &right_data) + .names(&left_name, &right_name) + .set_show_lines(!opt.disable_lines) + .set_diff_only(!opt.show_same) + .set_align_new_lines(!opt.disable_align) + .prettytable(); + + Ok(()) +} diff --git a/vendor/prettydiff/src/text.rs b/vendor/prettydiff/src/text.rs new file mode 100644 index 000000000..0d5fc03c3 --- /dev/null +++ b/vendor/prettydiff/src/text.rs @@ -0,0 +1,877 @@ +//! Utils for diff text +pub use ansi_term::Style; + +use crate::basic; +cfg_prettytable! { + use crate::format_table; + use prettytable::{Cell, Row}; +} +use ansi_term::Colour; +use pad::{Alignment, PadStr}; +use std::{ + cmp::{max, min}, + fmt, +}; + +pub struct StringSplitIter<'a, F> +where + F: Fn(char) -> bool, +{ + last: usize, + text: &'a str, + matched: Option<&'a str>, + iter: std::str::MatchIndices<'a, F>, +} + +impl<'a, F> Iterator for StringSplitIter<'a, F> +where + F: Fn(char) -> bool, +{ + type Item = &'a str; + fn next(&mut self) -> Option<Self::Item> { + if let Some(m) = self.matched { + self.matched = None; + Some(m) + } else if let Some((idx, matched)) = self.iter.next() { + let res = if self.last != idx { + self.matched = Some(matched); + &self.text[self.last..idx] + } else { + matched + }; + self.last = idx + matched.len(); + Some(res) + } else if self.last < self.text.len() { + let res = &self.text[self.last..]; + self.last = self.text.len(); + Some(res) + } else { + None + } + } +} + +pub fn collect_strings<T: ToString>(it: impl Iterator<Item = T>) -> Vec<String> { + it.map(|s| s.to_string()).collect::<Vec<String>>() +} + +/// Split string by clousure (Fn(char)->bool) keeping delemiters +pub fn split_by_char_fn<F>(text: &'_ str, pat: F) -> StringSplitIter<'_, F> +where + F: Fn(char) -> bool, +{ + StringSplitIter { + last: 0, + text, + matched: None, + iter: text.match_indices(pat), + } +} + +/// Split string by non-alphanumeric characters keeping delemiters +pub fn split_words(text: &str) -> impl Iterator<Item = &str> { + split_by_char_fn(text, |c: char| !c.is_alphanumeric()) +} + +/// Container for inline text diff result. Can be pretty-printed by Display trait. +#[derive(Debug, PartialEq)] +pub struct InlineChangeset<'a> { + old: Vec<&'a str>, + new: Vec<&'a str>, + separator: &'a str, + highlight_whitespace: bool, + insert_style: Style, + insert_whitespace_style: Style, + remove_style: Style, + remove_whitespace_style: Style, +} + +impl<'a> InlineChangeset<'a> { + pub fn new(old: Vec<&'a str>, new: Vec<&'a str>) -> InlineChangeset<'a> { + InlineChangeset { + old, + new, + separator: "", + highlight_whitespace: true, + insert_style: Colour::Green.normal(), + insert_whitespace_style: Colour::White.on(Colour::Green), + remove_style: Colour::Red.strikethrough(), + remove_whitespace_style: Colour::White.on(Colour::Red), + } + } + /// Highlight whitespaces in case of insert/remove? + pub fn set_highlight_whitespace(mut self, val: bool) -> Self { + self.highlight_whitespace = val; + self + } + + /// Style of inserted text + pub fn set_insert_style(mut self, val: Style) -> Self { + self.insert_style = val; + self + } + + /// Style of inserted whitespace + pub fn set_insert_whitespace_style(mut self, val: Style) -> Self { + self.insert_whitespace_style = val; + self + } + + /// Style of removed text + pub fn set_remove_style(mut self, val: Style) -> Self { + self.remove_style = val; + self + } + + /// Style of removed whitespace + pub fn set_remove_whitespace_style(mut self, val: Style) -> Self { + self.remove_whitespace_style = val; + self + } + + /// Set output separator + pub fn set_separator(mut self, val: &'a str) -> Self { + self.separator = val; + self + } + + /// Returns Vec of changes + pub fn diff(&self) -> Vec<basic::DiffOp<'a, &str>> { + basic::diff(&self.old, &self.new) + } + + fn apply_style(&self, style: Style, whitespace_style: Style, a: &[&str]) -> String { + let s = a.join(self.separator); + if self.highlight_whitespace { + collect_strings(split_by_char_fn(&s, |c| c.is_whitespace()).map(|s| { + let style = if s + .chars() + .next() + .map_or_else(|| false, |c| c.is_whitespace()) + { + whitespace_style + } else { + style + }; + style.paint(s) + })) + .join("") + } else { + style.paint(s).to_string() + } + } + + fn remove_color(&self, a: &[&str]) -> String { + self.apply_style(self.remove_style, self.remove_whitespace_style, a) + } + + fn insert_color(&self, a: &[&str]) -> String { + self.apply_style(self.insert_style, self.insert_whitespace_style, a) + } + /// Returns formatted string with colors + pub fn format(&self) -> String { + let diff = self.diff(); + let mut out: Vec<String> = Vec::with_capacity(diff.len()); + for op in diff { + match op { + basic::DiffOp::Equal(a) => out.push(a.join(self.separator)), + basic::DiffOp::Insert(a) => out.push(self.insert_color(a)), + basic::DiffOp::Remove(a) => out.push(self.remove_color(a)), + basic::DiffOp::Replace(a, b) => { + out.push(self.remove_color(a)); + out.push(self.insert_color(b)); + } + } + } + out.join(self.separator) + } +} + +impl<'a> fmt::Display for InlineChangeset<'a> { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "{}", self.format()) + } +} + +pub fn diff_chars<'a>(old: &'a str, new: &'a str) -> InlineChangeset<'a> { + let old: Vec<&str> = old.split("").filter(|&i| i != "").collect(); + let new: Vec<&str> = new.split("").filter(|&i| i != "").collect(); + + InlineChangeset::new(old, new) +} + +/// Diff two strings by words (contiguous) +pub fn diff_words<'a>(old: &'a str, new: &'a str) -> InlineChangeset<'a> { + InlineChangeset::new(split_words(old).collect(), split_words(new).collect()) +} + +#[cfg(feature = "prettytable-rs")] +fn color_multilines(color: Colour, s: &str) -> String { + collect_strings(s.split('\n').map(|i| color.paint(i))).join("\n") +} + +#[derive(Debug)] +pub struct ContextConfig<'a> { + pub context_size: usize, + pub skipping_marker: &'a str, +} + +/// Container for line-by-line text diff result. Can be pretty-printed by Display trait. +#[derive(Debug, PartialEq, Eq)] +pub struct LineChangeset<'a> { + old: Vec<&'a str>, + new: Vec<&'a str>, + + names: Option<(&'a str, &'a str)>, + diff_only: bool, + show_lines: bool, + trim_new_lines: bool, + aling_new_lines: bool, +} + +impl<'a> LineChangeset<'a> { + pub fn new(old: Vec<&'a str>, new: Vec<&'a str>) -> LineChangeset<'a> { + LineChangeset { + old, + new, + names: None, + diff_only: false, + show_lines: true, + trim_new_lines: true, + aling_new_lines: false, + } + } + + /// Sets names for side-by-side diff + pub fn names(mut self, old: &'a str, new: &'a str) -> Self { + self.names = Some((old, new)); + self + } + /// Show only differences for side-by-side diff + pub fn set_diff_only(mut self, val: bool) -> Self { + self.diff_only = val; + self + } + /// Show lines in side-by-side diff + pub fn set_show_lines(mut self, val: bool) -> Self { + self.show_lines = val; + self + } + /// Trim new lines in side-by-side diff + pub fn set_trim_new_lines(mut self, val: bool) -> Self { + self.trim_new_lines = val; + self + } + /// Align new lines inside diff + pub fn set_align_new_lines(mut self, val: bool) -> Self { + self.aling_new_lines = val; + self + } + /// Returns Vec of changes + pub fn diff(&self) -> Vec<basic::DiffOp<'a, &str>> { + basic::diff(&self.old, &self.new) + } + + #[cfg(feature = "prettytable-rs")] + fn prettytable_process(&self, a: &[&str], color: Option<Colour>) -> (String, usize) { + let mut start = 0; + let mut stop = a.len(); + if self.trim_new_lines { + for (index, element) in a.iter().enumerate() { + if *element != "" { + break; + } + start = index + 1; + } + for (index, element) in a.iter().enumerate().rev() { + if *element != "" { + stop = index + 1; + break; + } + } + } + let out = &a[start..stop]; + if let Some(color) = color { + ( + collect_strings(out.iter().map(|i| color.paint(*i).to_string())) + .join("\n") + .replace("\t", " "), + start, + ) + } else { + (out.join("\n").replace("\t", " "), start) + } + } + + #[cfg(feature = "prettytable-rs")] + fn prettytable_process_replace( + &self, + old: &[&str], + new: &[&str], + ) -> ((String, String), (usize, usize)) { + let (old, old_offset) = self.prettytable_process(old, None); + let (new, new_offset) = self.prettytable_process(new, None); + + let mut old_out = String::new(); + let mut new_out = String::new(); + + for op in diff_words(&old, &new).diff() { + match op { + basic::DiffOp::Equal(a) => { + old_out.push_str(&a.join("")); + new_out.push_str(&a.join("")); + } + basic::DiffOp::Insert(a) => { + new_out.push_str(&color_multilines(Colour::Green, &a.join(""))); + } + basic::DiffOp::Remove(a) => { + old_out.push_str(&color_multilines(Colour::Red, &a.join(""))); + } + basic::DiffOp::Replace(a, b) => { + old_out.push_str(&color_multilines(Colour::Red, &a.join(""))); + new_out.push_str(&color_multilines(Colour::Green, &b.join(""))); + } + } + } + + ((old_out, new_out), (old_offset, new_offset)) + } + + #[cfg(feature = "prettytable-rs")] + /// Prints side-by-side diff in table + pub fn prettytable(&self) { + let mut table = format_table::new(); + if let Some((old, new)) = &self.names { + let mut header = vec![]; + if self.show_lines { + header.push(Cell::new("")); + } + header.push(Cell::new(&Colour::Cyan.paint(old.to_string()).to_string())); + if self.show_lines { + header.push(Cell::new("")); + } + header.push(Cell::new(&Colour::Cyan.paint(new.to_string()).to_string())); + table.set_titles(Row::new(header)); + } + let mut old_lines = 1; + let mut new_lines = 1; + let mut out: Vec<(usize, String, usize, String)> = Vec::new(); + for op in &self.diff() { + match op { + basic::DiffOp::Equal(a) => { + let (old, offset) = self.prettytable_process(a, None); + if !self.diff_only { + out.push((old_lines + offset, old.clone(), new_lines + offset, old)); + } + old_lines += a.len(); + new_lines += a.len(); + } + basic::DiffOp::Insert(a) => { + let (new, offset) = self.prettytable_process(a, Some(Colour::Green)); + out.push((old_lines, "".to_string(), new_lines + offset, new)); + new_lines += a.len(); + } + basic::DiffOp::Remove(a) => { + let (old, offset) = self.prettytable_process(a, Some(Colour::Red)); + out.push((old_lines + offset, old, new_lines, "".to_string())); + old_lines += a.len(); + } + basic::DiffOp::Replace(a, b) => { + let ((old, new), (old_offset, new_offset)) = + self.prettytable_process_replace(a, b); + out.push((old_lines + old_offset, old, new_lines + new_offset, new)); + old_lines += a.len(); + new_lines += b.len(); + } + }; + } + for (old_lines, old, new_lines, new) in out { + if self.trim_new_lines && old.trim() == "" && new.trim() == "" { + continue; + } + if self.show_lines { + table.add_row(row![old_lines, old, new_lines, new]); + } else { + table.add_row(row![old, new]); + } + } + table.printstd(); + } + + fn remove_color(&self, a: &str) -> String { + Colour::Red.strikethrough().paint(a).to_string() + } + + fn insert_color(&self, a: &str) -> String { + Colour::Green.paint(a).to_string() + } + + /// Returns formatted string with colors + pub fn format(&self) -> String { + self.format_with_context(None, false) + } + + /// Formats lines in DiffOp::Equal + fn format_equal( + &self, + lines: &[&str], + display_line_numbers: bool, + prefix_size: usize, + line_counter: &mut usize, + ) -> Option<String> { + lines + .iter() + .map(|line| { + let res = if display_line_numbers { + format!("{} ", *line_counter) + .pad_to_width_with_alignment(prefix_size, Alignment::Right) + + line + } else { + "".pad_to_width(prefix_size) + line + }; + *line_counter += 1; + res + }) + .reduce(|acc, line| acc + "\n" + &line) + } + + /// Formats lines in DiffOp::Remove + fn format_remove( + &self, + lines: &[&str], + display_line_numbers: bool, + prefix_size: usize, + line_counter: &mut usize, + ) -> String { + lines + .iter() + .map(|line| { + let res = if display_line_numbers { + format!("{} ", *line_counter) + .pad_to_width_with_alignment(prefix_size, Alignment::Right) + + &self.remove_color(line) + } else { + "".pad_to_width(prefix_size) + &self.remove_color(line) + }; + *line_counter += 1; + res + }) + .reduce(|acc, line| acc + "\n" + &line) + .unwrap() + } + + /// Formats lines in DiffOp::Insert + fn format_insert(&self, lines: &[&str], prefix_size: usize) -> String { + lines + .iter() + .map(|line| "".pad_to_width(prefix_size) + &self.insert_color(line)) + .reduce(|acc, line| acc + "\n" + &line) + .unwrap() + } + + /// Returns formatted string with colors. + /// May omit identical lines, if `context_size` is `Some(k)`. + /// In this case, only print identical lines if they are within `k` lines + /// of a changed line (as in `diff -C`). + pub fn format_with_context( + &self, + context_config: Option<ContextConfig>, + display_line_numbers: bool, + ) -> String { + let line_number_size = if display_line_numbers { + (self.old.len() as f64).log10().ceil() as usize + } else { + 0 + }; + let skipping_marker_size = if let Some(ContextConfig { + skipping_marker, .. + }) = context_config + { + skipping_marker.len() + } else { + 0 + }; + let prefix_size = max(line_number_size, skipping_marker_size) + 1; + + let mut next_line = 1; + + let mut diff = self.diff().into_iter().peekable(); + let mut out: Vec<String> = Vec::with_capacity(diff.len()); + let mut at_beginning = true; + while let Some(op) = diff.next() { + match op { + basic::DiffOp::Equal(a) => match context_config { + None => out.push(a.join("\n")), + Some(ContextConfig { + context_size, + skipping_marker, + }) => { + let mut lines = a; + if !at_beginning { + let upper_bound = min(context_size, lines.len()); + if let Some(newlines) = self.format_equal( + &lines[..upper_bound], + display_line_numbers, + prefix_size, + &mut next_line, + ) { + out.push(newlines) + } + lines = &lines[upper_bound..]; + } + if lines.len() == 0 { + continue; + } + let lower_bound = if lines.len() > context_size { + lines.len() - context_size + } else { + 0 + }; + if lower_bound > 0 { + out.push(skipping_marker.to_string()); + next_line += lower_bound + } + if diff.peek().is_none() { + continue; + } + if let Some(newlines) = self.format_equal( + &lines[lower_bound..], + display_line_numbers, + prefix_size, + &mut next_line, + ) { + out.push(newlines) + } + } + }, + basic::DiffOp::Insert(a) => out.push(self.format_insert(a, prefix_size)), + basic::DiffOp::Remove(a) => out.push(self.format_remove( + a, + display_line_numbers, + prefix_size, + &mut next_line, + )), + basic::DiffOp::Replace(a, b) => { + out.push(self.format_remove( + a, + display_line_numbers, + prefix_size, + &mut next_line, + )); + out.push(self.format_insert(b, prefix_size)); + } + } + at_beginning = false; + } + out.join("\n") + } +} + +impl<'a> fmt::Display for LineChangeset<'a> { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "{}", self.format()) + } +} + +pub fn diff_lines<'a>(old: &'a str, new: &'a str) -> LineChangeset<'a> { + let old: Vec<&str> = old.lines().collect(); + let new: Vec<&str> = new.lines().collect(); + + LineChangeset::new(old, new) +} + +fn _test_splitter_basic(text: &str, exp: &[&str]) { + let res = collect_strings( + split_by_char_fn(&text, |c: char| c.is_whitespace()).map(|s| s.to_string()), + ); + assert_eq!(res, exp) +} + +#[test] +fn test_splitter() { + _test_splitter_basic( + " blah test2 test3 ", + &[" ", " ", "blah", " ", "test2", " ", "test3", " ", " "], + ); + _test_splitter_basic( + "\tblah test2 test3 ", + &["\t", "blah", " ", "test2", " ", "test3", " ", " "], + ); + _test_splitter_basic( + "\tblah test2 test3 t", + &["\t", "blah", " ", "test2", " ", "test3", " ", " ", "t"], + ); + _test_splitter_basic( + "\tblah test2 test3 tt", + &["\t", "blah", " ", "test2", " ", "test3", " ", " ", "tt"], + ); +} + +#[test] +fn test_basic() { + println!("diff_chars: {}", diff_chars("abefcd", "zadqwc")); + println!( + "diff_chars: {}", + diff_chars( + "The quick brown fox jumps over the lazy dog", + "The quick brown dog leaps over the lazy cat" + ) + ); + println!( + "diff_chars: {}", + diff_chars( + "The red brown fox jumped over the rolling log", + "The brown spotted fox leaped over the rolling log" + ) + ); + println!( + "diff_chars: {}", + diff_chars( + "The red brown fox jumped over the rolling log", + "The brown spotted fox leaped over the rolling log" + ) + .set_highlight_whitespace(true) + ); + println!( + "diff_words: {}", + diff_words( + "The red brown fox jumped over the rolling log", + "The brown spotted fox leaped over the rolling log" + ) + ); + println!( + "diff_words: {}", + diff_words( + "The quick brown fox jumps over the lazy dog", + "The quick, brown dog leaps over the lazy cat" + ) + ); +} + +#[test] +fn test_split_words() { + assert_eq!( + collect_strings(split_words("Hello World")), + ["Hello", " ", "World"] + ); + assert_eq!( + collect_strings(split_words("Hello😋World")), + ["Hello", "😋", "World"] + ); + assert_eq!( + collect_strings(split_words( + "The red brown fox\tjumped, over the rolling log" + )), + [ + "The", " ", "red", " ", "brown", " ", "fox", "\t", "jumped", ",", " ", "over", " ", + "the", " ", "rolling", " ", "log" + ] + ); +} + +#[test] +fn test_diff_lines() { + let code1_a = r#" +void func1() { + x += 1 +} + +void func2() { + x += 2 +} + "#; + let code1_b = r#" +void func1(a: u32) { + x += 1 +} + +void functhreehalves() { + x += 1.5 +} + +void func2() { + x += 2 +} + +void func3(){} +"#; + println!("diff_lines:"); + println!("{}", diff_lines(code1_a, code1_b)); + println!("===="); + diff_lines(code1_a, code1_b) + .names("left", "right") + .set_align_new_lines(true) + .prettytable(); +} + +fn _test_colors(changeset: &InlineChangeset, exp: &[(Option<Style>, &str)]) { + let color_s: String = collect_strings(exp.iter().map(|(style_opt, s)| { + if let Some(style) = style_opt { + style.paint(s.to_string()).to_string() + } else { + s.to_string() + } + })) + .join(""); + assert_eq!(format!("{}", changeset), color_s); +} + +#[test] +fn test_diff_words_issue_1() { + let insert_style = Colour::Green.normal(); + let insert_whitespace_style = Colour::White.on(Colour::Green); + let remove_style = Colour::Red.strikethrough(); + let remove_whitespace_style = Colour::White.on(Colour::Red); + let d1 = diff_words( + "und meine Unschuld beweisen!", + "und ich werde meine Unschuld beweisen!", + ) + .set_insert_style(insert_style) + .set_insert_whitespace_style(insert_whitespace_style) + .set_remove_style(remove_style) + .set_remove_whitespace_style(remove_whitespace_style); + + println!("diff_words: {} {:?}", d1, d1.diff()); + + _test_colors( + &d1, + &[ + (None, "und "), + (Some(insert_style), "ich"), + (Some(insert_whitespace_style), " "), + (Some(insert_style), "werde"), + (Some(insert_whitespace_style), " "), + (None, "meine Unschuld beweisen!"), + ], + ); + _test_colors( + &d1.set_highlight_whitespace(false), + &[ + (None, "und "), + (Some(insert_style), "ich werde "), + (None, "meine Unschuld beweisen!"), + ], + ); + let d2 = diff_words( + "Campaignings aus dem Ausland gegen meine Person ausfindig", + "Campaignings ausfindig", + ); + println!("diff_words: {} {:?}", d2, d2.diff()); + _test_colors( + &d2, + &[ + (None, "Campaignings "), + (Some(remove_style), "aus"), + (Some(remove_whitespace_style), " "), + (Some(remove_style), "dem"), + (Some(remove_whitespace_style), " "), + (Some(remove_style), "Ausland"), + (Some(remove_whitespace_style), " "), + (Some(remove_style), "gegen"), + (Some(remove_whitespace_style), " "), + (Some(remove_style), "meine"), + (Some(remove_whitespace_style), " "), + (Some(remove_style), "Person"), + (Some(remove_whitespace_style), " "), + (None, "ausfindig"), + ], + ); + let d3 = diff_words("des kriminellen Videos", "des kriminell erstellten Videos"); + println!("diff_words: {} {:?}", d3, d3.diff()); + _test_colors( + &d3, + &[ + (None, "des "), + (Some(remove_style), "kriminellen"), + (Some(insert_style), "kriminell"), + (None, " "), + (Some(insert_style), "erstellten"), + (Some(insert_whitespace_style), " "), + (None, "Videos"), + ], + ); +} + +#[test] +fn test_prettytable_process() { + let d1 = diff_lines( + r#"line1 + line2 + line3 + "#, + r#"line1 + line2 + line2.5 + line3 + "#, + ); + + println!("diff_lines: {} {:?}", d1, d1.diff()); + assert_eq!(d1.prettytable_process(&["a", "b", "c"], None), (String::from("a\nb\nc"), 0)); + assert_eq!(d1.prettytable_process(&["a", "b", "c", ""], None), (String::from("a\nb\nc"), 0)); + assert_eq!(d1.prettytable_process(&["", "a", "b", "c"], None), (String::from("a\nb\nc"), 1)); + assert_eq!(d1.prettytable_process(&["", "a", "b", "c", ""], None), (String::from("a\nb\nc"), 1)); +} + +#[test] +fn test_format_with_context() { + let d = diff_lines( + r#"line1 + line2 + line3 + line4 + line5 + line6 + line7 + line8 + line9 + line10 + line11 + line12"#, + r#"line1 + line2 + line4 + line5 + line6.5 + line7 + line8 + line9 + line10 + line11.5 + line12"#, + ); + let context = |n| ContextConfig { + context_size: n, + skipping_marker: "...", + }; + println!( + "diff_lines:\n{}\n{:?}", + d.format_with_context(Some(context(0)), true), + d.diff() + ); + let formatted_none = d.format_with_context(None, true); + let formatted_some_0 = d.format_with_context(Some(context(0)), true); + let formatted_some_1 = d.format_with_context(Some(context(1)), true); + let formatted_some_2 = d.format_with_context(Some(context(2)), true); + // With a context of size 2, every line is present + assert_eq!( + formatted_none.lines().count(), + formatted_some_2.lines().count() + ); + // with a context of size 1: + // * line 1 is replaced by '...' (-0 lines) + // * line 8-9 are replaced by '...' (-1 line) + assert_eq!( + formatted_none.lines().count() - 1, + formatted_some_1.lines().count() + ); + // with a context of size 0: + // * lines 1-2 are replaced by '...' (-1 line) + // * lines 4-5 are replaced by '...' (-1 line) + // * lines 7-10 are replaced by '...' (-3 lines) + // * line 12 is replaced by '...' (-0 lines) + assert_eq!( + formatted_none.lines().count() - 5, + formatted_some_0.lines().count() + ); +} |