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 // Konsole added support in 26.04.0.
46 // https://invent.kde.org/utilities/konsole/-/merge_requests/1054
47 let konsole = std::env::var("KONSOLE_VERSION")
48 .ok()
49 .and_then(|version| version.parse::<u32>().ok())
50 .map(|version| version >= 260400)
51 .unwrap_or(false);
52 if konsole {
53 return true;
54 }
55
56 false
57}
58
59// For iTerm, the TERM_FEATURES value "P" indicates OSC 9;4 support.
60//
61// Context: https://iterm2.com/feature-reporting/
62fn term_features_has_progress(value: &str) -> bool {
63 let mut current = String::new();
64
65 for ch in value.chars() {
66 if !ch.is_ascii_alphanumeric() {
67 break;
68 }
69 if ch.is_ascii_uppercase() {
70 if current == "P" {
71 return true;
72 }
73 current.clear();
74 current.push(ch);
75 } else {
76 current.push(ch);
77 }
78 }
79 current == "P"
80}
81
82#[cfg(test)]
83mod tests {
84 use super::term_features_has_progress;
85
86 #[test]
87 fn term_features_progress_detection() {
88 // With PROGRESS feature ("P")
89 assert!(term_features_has_progress("MBT2ScP"));
90
91 // Without PROGRESS feature
92 assert!(!term_features_has_progress("MBT2Sc"));
93 }
94}