Skip to main content

anstyle_progress/
query.rs

1/// Determines whether the terminal supports ANSI OSC 9;4.
2pub fn supports_term_progress(is_terminal: bool) -> bool {
3    if !is_terminal {
4        return false;
5    }
6
7    // Terminal feature detection, includes
8    // - iTerm support in v3.6.6
9    let term_features = std::env::var("TERM_FEATURES")
10        .ok()
11        .map(|v| term_features_has_progress(&v))
12        .unwrap_or(false);
13    if term_features {
14        return true;
15    }
16
17    // https://github.com/wezterm/wezterm/issues/6581
18    // https://ghostty.org/docs/install/release-notes/1-2-0#graphical-progress-bars
19    let term_program = std::env::var("TERM_PROGRAM").ok();
20    if matches!(term_program.as_deref(), Some("WezTerm") | Some("ghostty")) {
21        return true;
22    }
23
24    // https://github.com/microsoft/terminal/pull/8055
25    if std::env::var("WT_SESSION").is_ok() {
26        return true;
27    }
28
29    // https://conemu.github.io/en/AnsiEscapeCodes.html#ConEmu_specific_OSC
30    if std::env::var("ConEmuANSI").ok() == Some("ON".into()) {
31        return true;
32    }
33
34    // Ptyxis added OSC 9;4 support in 48.0.
35    // See https://gitlab.gnome.org/chergert/ptyxis/-/issues/305
36    let ptyxis = std::env::var("PTYXIS_VERSION")
37        .ok()
38        .and_then(|version| version.split(".").next()?.parse::<i32>().ok())
39        .map(|major_version| major_version >= 48)
40        .unwrap_or(false);
41    if ptyxis {
42        return true;
43    }
44
45    false
46}
47
48// For iTerm, the TERM_FEATURES value "P" indicates OSC 9;4 support.
49//
50// Context: https://iterm2.com/feature-reporting/
51fn term_features_has_progress(value: &str) -> bool {
52    let mut current = String::new();
53
54    for ch in value.chars() {
55        if !ch.is_ascii_alphanumeric() {
56            break;
57        }
58        if ch.is_ascii_uppercase() {
59            if current == "P" {
60                return true;
61            }
62            current.clear();
63            current.push(ch);
64        } else {
65            current.push(ch);
66        }
67    }
68    current == "P"
69}
70
71#[cfg(test)]
72mod tests {
73    use super::term_features_has_progress;
74
75    #[test]
76    fn term_features_progress_detection() {
77        // With PROGRESS feature ("P")
78        assert!(term_features_has_progress("MBT2ScP"));
79
80        // Without PROGRESS feature
81        assert!(!term_features_has_progress("MBT2Sc"));
82    }
83}