Skip to content

Commit

Permalink
Improve color detection across platforms
Browse files Browse the repository at this point in the history
Fixes #882 by improving `style::available_color_count()`:
- Checks ANSI support on Windows.
- Uses the COLORTERM environment variable and falls back to the TERM environment variable.
- Supports "xterm-24bit" and "truecolor" values which return `u16::MAX`.
  • Loading branch information
heaths committed May 7, 2024
1 parent fce58c8 commit 41652cc
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 3 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ futures-timer = "3.0"
async-std = "1.12"
serde_json = "1.0"
serial_test = "2.0.0"
temp-env = "0.3.6"

#
# Examples
Expand Down
106 changes: 103 additions & 3 deletions src/style.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,9 +161,23 @@ pub fn style<D: Display>(val: D) -> StyledContent<D> {
///
/// This does not always provide a good result.
pub fn available_color_count() -> u16 {
env::var("TERM")
.map(|x| if x.contains("256color") { 256 } else { 8 })
.unwrap_or(8)
#[cfg(windows)]
{
// Check if we're running in a pseudo TTY, which supports true color.
// Fall back to env vars otherwise for other terminals on Windows.
if crate::ansi_support::supports_ansi() {
return u16::MAX;
}
}

const DEFAULT: u16 = 8;
env::var("COLORTERM")
.or_else(|_| env::var("TERM"))
.map_or(DEFAULT, |x| match x {
_ if x.contains("24bit") || x.contains("truecolor") => u16::MAX,
_ if x.contains("256") => 256,
_ => DEFAULT,
})
}

/// Forces colored output on or off globally, overriding NO_COLOR.
Expand Down Expand Up @@ -519,3 +533,89 @@ impl_display!(for ResetColor);
fn parse_next_u8<'a>(iter: &mut impl Iterator<Item = &'a str>) -> Option<u8> {
iter.next().and_then(|s| s.parse().ok())
}

#[cfg(test)]
mod tests {
use super::*;

// On Windows many env var tests will fail so we need to conditionally check for ANSI support.
// This allows other terminals on Windows to still assert env var support.
macro_rules! skip_windows_ansi_supported {
() => {
#[cfg(windows)]
{
if crate::ansi_support::supports_ansi() {
return;
}
}
};
}

#[cfg_attr(windows, test)]
#[cfg(windows)]
fn windows_always_truecolor() {
// This should always be true on supported Windows 10+,
// but downlevel Windows clients and other terminals may fail `cargo test` otherwise.
if crate::ansi_support::supports_ansi() {
assert_eq!(u16::MAX, available_color_count());
};
}

#[test]
fn colorterm_overrides_term() {
skip_windows_ansi_supported!();
temp_env::with_vars(
[
("COLORTERM", Some("truecolor")),
("TERM", Some("xterm-256color")),
],
|| {
assert_eq!(u16::MAX, available_color_count());
},
);
}

#[test]
fn term_24bits() {
skip_windows_ansi_supported!();
temp_env::with_vars(
[("COLORTERM", None), ("TERM", Some("xterm-24bits"))],
|| {
assert_eq!(u16::MAX, available_color_count());
},
);
}

#[test]
fn term_256color() {
skip_windows_ansi_supported!();
temp_env::with_vars(
[("COLORTERM", None), ("TERM", Some("xterm-256color"))],
|| {
assert_eq!(256u16, available_color_count());
},
);
}

#[test]
fn default_color_count() {
skip_windows_ansi_supported!();
temp_env::with_vars([("COLORTERM", None::<&str>), ("TERM", None)], || {
assert_eq!(8, available_color_count());
});
}

#[test]
fn unsupported_term_colorterm_values() {
skip_windows_ansi_supported!();
temp_env::with_vars(
[
("COLORTERM", Some("gibberish")),
("TERM", Some("gibberish")),
],
|| {
assert_eq!(8u16, available_color_count());
},
);
}
}

0 comments on commit 41652cc

Please sign in to comment.