summaryrefslogtreecommitdiff
path: root/src/utils/workarounds.rs
blob: a47a1db83edf28cb28ebf790db0f1c5cda9e5aec (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
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()
}