Repository: dandavison/delta
Pull request #1108 (2022-06-20 06:15:50)

Bump sysinfo from 0.23.0 to 0.24.5
Bumps [sysinfo](https://github.com/GuillaumeGomez/sysinfo) from 0.23.0 to 0.24.5.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/GuillaumeGomez/sysinfo/blob/master/CHANGELOG.md">sysinfo's changelog</a>.</em></p>
<blockquote>
<h1>0.24.5</h1>
<ul>
<li>Implement <code>Hash</code> trait on <code>Uid</code> and <code>Gid</code> types.</li>
<li>Remove dependency <code>once_cell</code> for targets other than <code>linux</code>, <code>android</code> and <code>windows</code>.</li>
</ul>
<h1>0.24.4</h1>
<ul>
<li>Windows: Fix <code>System::refresh_process</code> when required higher priviledges.</li>
</ul>
<h1>0.24.3</h1>
<ul>
<li>macOS: Fix <code>System::refresh_processes</code> badly handling updates.</li>
<li>FreeBSD: Improve performance of <code>System::refresh_processes</code>.</li>
</ul>
<h1>0.24.2</h1>
<ul>
<li>Windows: Fix CPU usage computation.</li>
<li>Windows: Enable extra feature on <code>winapi</code>.</li>
<li>macOS: Fix executable path retrieval.</li>
</ul>
<h1>0.24.1</h1>
<ul>
<li>Use <code>saturating_*</code> function for mathematical operations to prevent overflows/underflows.</li>
</ul>
<h1>0.24.0</h1>
<ul>
<li>Rename <code>Processor</code> into <code>Cpu</code> and <code>ProcessorExt</code> into <code>CpuExt</code>.</li>
<li>Retrieve information about a process' owner.</li>
<li>Add <code>SystemExt::get_user_by_id</code>.</li>
<li>Add <code>ProcessExt::user_id</code>.</li>
<li>Add <code>ProcessExt::group_id</code>.</li>
<li>Add <code>user</code>-related methods to <code>ProcessRefreshKind</code>.</li>
<li>Linux: Improve performance when creating new <code>Process</code> by improving retrieval of user ID and group ID.</li>
</ul>
<h1>0.23.14</h1>
<ul>
<li>Linux: Fix processes' virtual memory computation.</li>
</ul>
<h1>0.23.13</h1>
<ul>
<li>macOS/FreeBSD: Fix <code>System::refresh_process</code> and <code>System::refresh_process_specifics</code> returned value.</li>
<li>Linux: Small performance improvement when updating process list.</li>
</ul>
<h1>0.23.12</h1>
<ul>
<li>Linux: Improve <code>System::refresh_cpu</code> performance.</li>
<li>Fix clippy lints.</li>
</ul>
<h1>0.23.11</h1>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a href="https://github.com/GuillaumeGomez/sysinfo/commits">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=sysinfo&package-manager=cargo&previous-version=0.23.0&new-version=0.24.5)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index b4cffe4..2942d05 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -155,9 +155,13 @@ dependencies = [
 
 [[package]]
 name = "bytelines"
-version = "2.2.2"
+version = "2.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "791e4e40d13e1463dee537b254225c12c46ec7328f1817c6264873bc166f615f"
+checksum = "784face321c535fcd9a1456632fa720aa53ea0640b57341d961c8c09de2da59f"
+dependencies = [
+ "futures",
+ "tokio",
+]
 
 [[package]]
 name = "bytemuck"
@@ -171,6 +175,12 @@ version = "1.3.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
 
+[[package]]
+name = "bytes"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
+
 [[package]]
 name = "bytesize"
 version = "1.1.0"
@@ -490,6 +500,95 @@ dependencies = [
  "percent-encoding",
 ]
 
+[[package]]
+name = "futures"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868"
+
+[[package]]
+name = "futures-task"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a"
+
+[[package]]
+name = "futures-util"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
 [[package]]
 name = "getrandom"
 version = "0.1.16"
@@ -962,6 +1061,18 @@ dependencies = [
  "siphasher",
 ]
 
+[[package]]
+name = "pin-project-lite"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
 [[package]]
 name = "pkg-config"
 version = "0.3.19"
@@ -1231,6 +1342,12 @@ version = "0.3.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "533494a8f9b724d33625ab53c6c4800f7cc445895924a8ef649222dcb76e938b"
 
+[[package]]
+name = "slab"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32"
+
 [[package]]
 name = "smol_str"
 version = "0.1.21"
@@ -1304,9 +1421,9 @@ dependencies = [
 
 [[package]]
 name = "sysinfo"
-version = "0.23.0"
+version = "0.24.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e757000a4bed2b1be9be65a3f418b9696adf30bb419214c73997422de73a591"
+checksum = "7d80929a3b477bce3a64360ca82bfb361eacce1dcb7b1fb31e8e5e181e37c212"
 dependencies = [
  "cfg-if 1.0.0",
  "core-foundation-sys",
@@ -1418,6 +1535,17 @@ version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
 
+[[package]]
+name = "tokio"
+version = "1.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee"
+dependencies = [
+ "bytes",
+ "memchr",
+ "pin-project-lite",
+]
+
 [[package]]
 name = "toml"
 version = "0.5.8"
diff --git a/Cargo.toml b/Cargo.toml
index d2d3501..0e5c109 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -23,7 +23,7 @@ ansi_term = "0.12.1"
 atty = "0.2.14"
 bitflags = "1.3.2"
 box_drawing = "0.1.2"
-bytelines = "2.2.2"
+bytelines = "2.4.0"
 clap = { version = "3.0.7", features = ["derive"] }
 console = "0.15.0"
 ctrlc = "3.2.1"
@@ -50,7 +50,7 @@ default-features = false
 features = []
 
 [dependencies.sysinfo]
-version = "0.23.0"
+version = "0.24.5"
 # no default features to disable the use of threads
 default-features = false
 features = []
diff --git a/README.md b/README.md
index 2ac97a8..6ac1a81 100644
--- a/README.md
+++ b/README.md
@@ -23,10 +23,12 @@
 
 [interactive]
     diffFilter = delta --color-only
+[add.interactive]
+    useBuiltin = false # required for git 2.37.0
 
 [delta]
-    navigate = true  # use n and N to move between diff sections
-    light = false    # set to true if you're in a terminal w/ a light background color (e.g. the default macOS terminal)
+    navigate = true    # use n and N to move between diff sections
+    light = false      # set to true if you're in a terminal w/ a light background color (e.g. the default macOS terminal)
 
 [merge]
     conflictstyle = diff3
diff --git a/manual/src/features-named-groups-of-settings.md b/manual/src/features-named-groups-of-settings.md
index 0dde9eb..6dcc725 100644
--- a/manual/src/features-named-groups-of-settings.md
+++ b/manual/src/features-named-groups-of-settings.md
@@ -25,3 +25,17 @@ All delta options can go under the `[delta]` section in your git config file. Ho
 ```
 
 <table><tr><td><img width=400px src="https://user-images.githubusercontent.com/52205/86275048-a96ee500-bba0-11ea-8a19-584f69758aee.png" alt="image" /></td></tr></table>
+
+The environment variable `DELTA_FEATURES` can used to enable features from the command line: it should be set to a space-separated string of feature names.
+If you precede this with a `+` symbol, then the features are _added_ to those configured elsewhere, instead of replacing them.
+This is very useful, for example to temporarily switch delta to side-by-side mode you can do
+
+```
+export DELTA_FEATURES=+side-by-side
+```
+
+and to undo that:
+
+```
+export DELTA_FEATURES=+
+```
diff --git a/src/cli.rs b/src/cli.rs
index 7bb4140..14b0e23 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -329,9 +329,9 @@ pub struct Opt {
     ///
     /// A feature is a named collection of delta options in ~/.gitconfig. See FEATURES section. The
     /// environment variable DELTA_FEATURES can be set to a space-separated list of feature names.
-    /// If this is preceded with a space, the features from the environment variable will be added
+    /// If this is preceded with a + character, the features from the environment variable will be added
     /// to those specified in git config. E.g. DELTA_FEATURES=+side-by-side can be used to activate
-    /// side-by-side temporarily.
+    /// side-by-side temporarily (use DELTA_FEATURES=+ to go back to just the features from git config).
     pub features: Option<String>,
 
     #[clap(
diff --git a/src/delta.rs b/src/delta.rs
index 27b3051..5cda526 100644
--- a/src/delta.rs
+++ b/src/delta.rs
@@ -13,6 +13,7 @@ use crate::handlers::hunk_header::ParsedHunkHeader;
 use crate::handlers::{self, merge_conflict};
 use crate::paint::Painter;
 use crate::style::DecorationStyle;
+use crate::utils;
 
 #[derive(Clone, Debug, PartialEq)]
 pub enum State {
@@ -181,10 +182,25 @@ impl<'a> StateMachine<'a> {
     }
 
     fn ingest_line(&mut self, raw_line_bytes: &[u8]) {
-        // TODO: retain raw_line as Cow
-        self.raw_line = String::from_utf8_lossy(raw_line_bytes).to_string();
+        match String::from_utf8(raw_line_bytes.to_vec()) {
+            Ok(utf8) => self.ingest_line_utf8(utf8),
+            Err(_) => {
+                let raw_line = String::from_utf8_lossy(raw_line_bytes);
+                let truncated_len = utils::round_char_boundary::floor_char_boundary(
+                    &raw_line,
+                    self.config.max_line_length,
+                );
+                self.raw_line = raw_line[..truncated_len].to_string();
+                self.line = self.raw_line.clone();
+            }
+        }
+    }
+
+    fn ingest_line_utf8(&mut self, raw_line: String) {
+        self.raw_line = raw_line;
         // When a file has \r\n line endings, git sometimes adds ANSI escape sequences between the
         // \r and \n, in which case byte_lines does not remove the \r. Remove it now.
+        // TODO: Limit the number of characters we examine when looking for the \r?
         if let Some(cr_index) = self.raw_line.rfind('\r') {
             if ansi::strip_ansi_codes(&self.raw_line[cr_index + 1..]).is_empty() {
                 self.raw_line = format!(
diff --git a/src/features/hyperlinks.rs b/src/features/hyperlinks.rs
index b5f60af..b45de0f 100644
--- a/src/features/hyperlinks.rs
+++ b/src/features/hyperlinks.rs
@@ -26,8 +26,12 @@ pub fn format_commit_line_with_osc8_commit_hyperlink<'a>(
 ) -> Cow<'a, str> {
     if let Some(commit_link_format) = &config.hyperlinks_commit_link_format {
         COMMIT_LINE_REGEX.replace(line, |captures: &Captures| {
-            let commit = captures.get(2).unwrap().as_str();
-            format_osc8_hyperlink(&commit_link_format.replace("{commit}", commit), commit)
+            let prefix = captures.get(1).map(|m| m.as_str()).unwrap_or("");
+            let commit = captures.get(2).map(|m| m.as_str()).unwrap();
+            let suffix = captures.get(3).map(|m| m.as_str()).unwrap_or("");
+            let formatted_commit =
+                format_osc8_hyperlink(&commit_link_format.replace("{commit}", commit), commit);
+            format!("{}{}{}", prefix, formatted_commit, suffix)
         })
     } else if let Some(GitConfigEntry::GitRemote(repo)) =
         config.git_config.as_ref().and_then(get_remote_url)
diff --git a/src/handlers/commit_meta.rs b/src/handlers/commit_meta.rs
index 98bdfad..f60ba37 100644
--- a/src/handlers/commit_meta.rs
+++ b/src/handlers/commit_meta.rs
@@ -16,6 +16,7 @@ impl<'a> StateMachine<'a> {
         }
         let mut handled_line = false;
         self.painter.paint_buffered_minus_and_plus_lines();
+        self.handle_pending_line_with_diff_name()?;
         self.state = State::CommitMeta;
         if self.should_handle() {
             self.painter.emit()?;
diff --git a/src/main.rs b/src/main.rs
index 1d2b763..625b513 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -136,21 +136,19 @@ fn run_app() -> std::io::Result<i32> {
         OutputType::from_mode(&env, config.paging_mode, config.pager.clone(), &config).unwrap();
     let mut writer = output_type.handle().unwrap();
 
-    match (config.minus_file.as_ref(), config.plus_file.as_ref()) {
-        (None, None) => {}
-        (Some(minus_file), Some(plus_file)) => {
-            let exit_code = subcommands::diff::diff(minus_file, plus_file, &config, &mut writer);
-            return Ok(exit_code);
-        }
-        _ => {
-            eprintln!(
-                "\
+    if let (Some(minus_file), Some(plus_file)) = (&config.minus_file, &config.plus_file) {
+        let exit_code = subcommands::diff::diff(minus_file, plus_file, &config, &mut writer);
+        return Ok(exit_code);
+    }
+
+    if atty::is(atty::Stream::Stdin) {
+        eprintln!(
+            "\
     The main way to use delta is to configure it as the pager for git: \
     see https://github.com/dandavison/delta#configuration. \
     You can also use delta to diff two files: `delta file_A file_B`."
-            );
-            return Ok(config.error_exit_code);
-        }
+        );
+        return Ok(config.error_exit_code);
     }
 
     if let Err(error) = delta(io::stdin().lock().byte_lines(), &mut writer, &config) {
diff --git a/src/utils/mod.rs b/src/utils/mod.rs
index 20adef2..9cd8cfa 100644
--- a/src/utils/mod.rs
+++ b/src/utils/mod.rs
@@ -3,4 +3,5 @@ pub mod bat;
 pub mod path;
 pub mod process;
 pub mod regex_replacement;
+pub mod round_char_boundary;
 pub mod syntect;
diff --git a/src/utils/process.rs b/src/utils/process.rs
index ab4c4ed..8fac92e 100644
--- a/src/utils/process.rs
+++ b/src/utils/process.rs
@@ -1195,7 +1195,7 @@ pub mod tests {
         }
 
         // Tests that caller is something like "cargo test" or "cargo tarpaulin"
-        let find_test = |args: &[String]| find_calling_process(args, &["test", "tarpaulin"]);
+        let find_test = |args: &[String]| find_calling_process(args, &["t", "test", "tarpaulin"]);
         assert_eq!(calling_process_cmdline(info, find_test), Some(()));
 
         let nonsense = ppid_distance
diff --git a/src/utils/round_char_boundary.rs b/src/utils/round_char_boundary.rs
new file mode 100644
index 0000000..c000134
--- /dev/null
+++ b/src/utils/round_char_boundary.rs
@@ -0,0 +1,24 @@
+// Taken from https://github.com/rust-lang/rust/pull/86497
+// TODO: Remove when this is in the version of the Rust standard library that delta is building
+// against.
+
+#[inline]
+const fn is_utf8_char_boundary(b: u8) -> bool {
+    // This is bit magic equivalent to: b < 128 || b >= 192
+    (b as i8) >= -0x40
+}
+
+#[inline]
+pub fn floor_char_boundary(s: &str, index: usize) -> usize {
+    if index >= s.len() {
+        s.len()
+    } else {
+        let lower_bound = index.saturating_sub(3);
+        let new_index = s.as_bytes()[lower_bound..=index]
+            .iter()
+            .rposition(|b| is_utf8_char_boundary(*b));
+
+        // SAFETY: we know that the character boundary will be within four bytes
+        unsafe { lower_bound + new_index.unwrap_unchecked() }
+    }
+}


Test patch:
diff --git a/src/tests/test_example_diffs.rs b/src/tests/test_example_diffs.rs
index 325f2e9..d3800a6 100644
--- a/src/tests/test_example_diffs.rs
+++ b/src/tests/test_example_diffs.rs
@@ -1626,6 +1626,13 @@ src/align.rs:71: impl<'a> Alignment<'a> { │
             .expect_contains("a b ⟶   c d\n");
     }
 
+    #[test]
+    fn test_file_removal_in_log_output() {
+        DeltaTest::with_args(&[])
+            .with_input(GIT_LOG_FILE_REMOVAL_IN_FIRST_COMMIT)
+            .expect_after_header("#partial\n\nremoved: a");
+    }
+
     const GIT_DIFF_SINGLE_HUNK: &str = "\
 commit 94907c0f136f46dc46ffae2dc92dca9af7eb7c2e
 Author: Dan Davison <dandavison7@gmail.com>
@@ -2385,5 +2392,27 @@ index d00491f..0cfbf08 100644
 @@ -1 +1 @@
 -1
 +2
+";
+
+    const GIT_LOG_FILE_REMOVAL_IN_FIRST_COMMIT: &str = "
+commit 4117f616160180c0c57ea64840eadd08b7fa32a4
+Author: Björn Steinbrink <bsteinbr@gmail.com>
+Date:   Tue Jun 21 14:51:59 2022 +0200
+
+    remove file
+
+diff --git a a
+deleted file mode 100644
+index e69de29..0000000
+
+commit 190cce5dffeb9050fd6a27780f16d84b19c07dc0
+Author: Björn Steinbrink <bsteinbr@gmail.com>
+Date:   Tue Jun 21 14:48:20 2022 +0200
+
+    add file
+
+diff --git a a
+new file mode 100644
+index 0000000..e69de29
 ";
 }