Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve color detection across platforms #885

Merged
merged 1 commit into from
Jun 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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());
},
);
}
}