diff options
-rw-r--r-- | src/options/set.rs | 3 | ||||
-rw-r--r-- | src/utils/mod.rs | 1 | ||||
-rw-r--r-- | src/utils/workarounds.rs | 60 |
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() +} |