summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorThomas Otto <th1000s@posteo.net>2022-10-10 21:40:06 +0200
committerDan Davison <dandavison7@gmail.com>2022-10-11 16:19:25 -0400
commit60492f318f6e46bfb8ba0b4396cce3210897a1c0 (patch)
tree49b7e5dffca4a7f6fcb617c616545f12cc645405 /src
parent65044c0e0d4b0e3baeae92791d1add0ade061f2f (diff)
Add terminal width fallback via stty if on Windows/MSYS2
Also new workarounds.rs file, and DELTA_NO_WORKAROUNDS env var to disable these.
Diffstat (limited to 'src')
-rw-r--r--src/options/set.rs3
-rw-r--r--src/utils/mod.rs1
-rw-r--r--src/utils/workarounds.rs60
3 files changed, 63 insertions, 1 deletions
diff --git a/src/options/set.rs b/src/options/set.rs
index 7cb8b0a7..88615f7a 100644
--- a/src/options/set.rs
+++ b/src/options/set.rs
@@ -609,7 +609,8 @@ fn set_widths_and_isatty(opt: &mut cli::Opt) {
// If one extra character for e.g. `less --status-column` is required use "-1"
// as an argument, also see #41, #10, #115 and #727.
- opt.computed.available_terminal_width = term_stdout.size().1 as usize;
+ opt.computed.available_terminal_width =
+ crate::utils::workarounds::windows_msys2_width_fix(term_stdout.size(), &term_stdout);
let (decorations_width, background_color_extends_to_terminal_width) = match opt.width.as_deref()
{
diff --git a/src/utils/mod.rs b/src/utils/mod.rs
index 9cd8cfaa..257c7b03 100644
--- a/src/utils/mod.rs
+++ b/src/utils/mod.rs
@@ -5,3 +5,4 @@ pub mod process;
pub mod regex_replacement;
pub mod round_char_boundary;
pub mod syntect;
+pub mod workarounds;
diff --git a/src/utils/workarounds.rs b/src/utils/workarounds.rs
new file mode 100644
index 00000000..a47a1db8
--- /dev/null
+++ b/src/utils/workarounds.rs
@@ -0,0 +1,60 @@
+// env var which should disable workarounds
+const NO_WORKAROUNDS: &str = "DELTA_NO_WORKAROUNDS";
+
+// Work around a bug in the 'console' crate (inherited from 'terminal-size', #25): On Windows
+// it can not determine the width of an MSYS2 / MINGW64 terminal (e.g. from Git-Bash) correctly.
+// Instead use the usually included stty util from the MSYS2 distribution.
+#[cfg(target_os = "windows")]
+pub fn windows_msys2_width_fix(height_width: (u16, u16), term_stdout: &console::Term) -> usize {
+ fn guess_real_width(current_width: u16, term_stdout: &console::Term) -> Option<u16> {
+ use std::process::{Command, Stdio};
+
+ let term_var = std::env::var("TERM").ok()?;
+ // More checks before actually calling stty.
+ if term_var.starts_with("xterm")
+ && term_stdout.is_term()
+ && term_stdout.features().is_msys_tty()
+ {
+ if std::env::var(NO_WORKAROUNDS).is_ok() {
+ return Some(current_width);
+ }
+
+ // stderr/2 is passed to the Command below.
+ let pseudo_term = "/dev/fd/2";
+
+ // Read width via stty helper program (e.g. "C:\Program Files\Git\usr\bin\stty.exe")
+ // which gets both the MSYS2 and cmd.exe width right.
+ let result = Command::new("stty")
+ .stderr(Stdio::inherit())
+ .arg("-F")
+ .arg(pseudo_term)
+ .arg("size")
+ .output()
+ .ok()?;
+
+ if result.status.success() {
+ let size = std::str::from_utf8(&result.stdout).ok()?;
+ let mut it = size.split_whitespace();
+ let _height = it.next()?;
+ return it.next().map(|width| width.parse().ok())?;
+ }
+ }
+ None
+ }
+
+ // Calling an external binary is slow, so make sure this is actually necessary.
+ // The fallback values of 25 lines by 80 columns (sometimes zero indexed) are a good
+ // indicator.
+ let (height, width) = height_width;
+ match (height, width) {
+ (24..=25, 79..=80) => guess_real_width(width, term_stdout).unwrap_or(width),
+ _ => width,
+ }
+ .into()
+}
+
+#[cfg(not(target_os = "windows"))]
+pub fn windows_msys2_width_fix(height_width: (u16, u16), _: &console::Term) -> usize {
+ let _ = NO_WORKAROUNDS;
+ height_width.1.into()
+}