Skip to content

Commit

Permalink
Add terminal width fallback via stty if on Windows/MSYS2
Browse files Browse the repository at this point in the history
Also new workarounds.rs file, and DELTA_NO_WORKAROUNDS env var
to disable these.
  • Loading branch information
th1000s committed Oct 10, 2022
1 parent 1db4041 commit cc3b021
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 1 deletion.
3 changes: 2 additions & 1 deletion src/options/set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand Down
1 change: 1 addition & 0 deletions src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ pub mod process;
pub mod regex_replacement;
pub mod round_char_boundary;
pub mod syntect;
pub mod workarounds;
60 changes: 60 additions & 0 deletions src/utils/workarounds.rs
Original file line number Diff line number Diff line change
@@ -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: 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()
}

0 comments on commit cc3b021

Please sign in to comment.