Skip to content

Commit 080f064

Browse files
authored
Improve color detection across platforms (#885)
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`.
1 parent fe44028 commit 080f064

File tree

2 files changed

+104
-3
lines changed

2 files changed

+104
-3
lines changed

Diff for: Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ futures-timer = "3.0"
9696
async-std = "1.12"
9797
serde_json = "1.0"
9898
serial_test = "2.0.0"
99+
temp-env = "0.3.6"
99100

100101
#
101102
# Examples

Diff for: src/style.rs

+103-3
Original file line numberDiff line numberDiff line change
@@ -161,9 +161,23 @@ pub fn style<D: Display>(val: D) -> StyledContent<D> {
161161
///
162162
/// This does not always provide a good result.
163163
pub fn available_color_count() -> u16 {
164-
env::var("TERM")
165-
.map(|x| if x.contains("256color") { 256 } else { 8 })
166-
.unwrap_or(8)
164+
#[cfg(windows)]
165+
{
166+
// Check if we're running in a pseudo TTY, which supports true color.
167+
// Fall back to env vars otherwise for other terminals on Windows.
168+
if crate::ansi_support::supports_ansi() {
169+
return u16::MAX;
170+
}
171+
}
172+
173+
const DEFAULT: u16 = 8;
174+
env::var("COLORTERM")
175+
.or_else(|_| env::var("TERM"))
176+
.map_or(DEFAULT, |x| match x {
177+
_ if x.contains("24bit") || x.contains("truecolor") => u16::MAX,
178+
_ if x.contains("256") => 256,
179+
_ => DEFAULT,
180+
})
167181
}
168182

169183
/// Forces colored output on or off globally, overriding NO_COLOR.
@@ -519,3 +533,89 @@ impl_display!(for ResetColor);
519533
fn parse_next_u8<'a>(iter: &mut impl Iterator<Item = &'a str>) -> Option<u8> {
520534
iter.next().and_then(|s| s.parse().ok())
521535
}
536+
537+
#[cfg(test)]
538+
mod tests {
539+
use super::*;
540+
541+
// On Windows many env var tests will fail so we need to conditionally check for ANSI support.
542+
// This allows other terminals on Windows to still assert env var support.
543+
macro_rules! skip_windows_ansi_supported {
544+
() => {
545+
#[cfg(windows)]
546+
{
547+
if crate::ansi_support::supports_ansi() {
548+
return;
549+
}
550+
}
551+
};
552+
}
553+
554+
#[cfg_attr(windows, test)]
555+
#[cfg(windows)]
556+
fn windows_always_truecolor() {
557+
// This should always be true on supported Windows 10+,
558+
// but downlevel Windows clients and other terminals may fail `cargo test` otherwise.
559+
if crate::ansi_support::supports_ansi() {
560+
assert_eq!(u16::MAX, available_color_count());
561+
};
562+
}
563+
564+
#[test]
565+
fn colorterm_overrides_term() {
566+
skip_windows_ansi_supported!();
567+
temp_env::with_vars(
568+
[
569+
("COLORTERM", Some("truecolor")),
570+
("TERM", Some("xterm-256color")),
571+
],
572+
|| {
573+
assert_eq!(u16::MAX, available_color_count());
574+
},
575+
);
576+
}
577+
578+
#[test]
579+
fn term_24bits() {
580+
skip_windows_ansi_supported!();
581+
temp_env::with_vars(
582+
[("COLORTERM", None), ("TERM", Some("xterm-24bits"))],
583+
|| {
584+
assert_eq!(u16::MAX, available_color_count());
585+
},
586+
);
587+
}
588+
589+
#[test]
590+
fn term_256color() {
591+
skip_windows_ansi_supported!();
592+
temp_env::with_vars(
593+
[("COLORTERM", None), ("TERM", Some("xterm-256color"))],
594+
|| {
595+
assert_eq!(256u16, available_color_count());
596+
},
597+
);
598+
}
599+
600+
#[test]
601+
fn default_color_count() {
602+
skip_windows_ansi_supported!();
603+
temp_env::with_vars([("COLORTERM", None::<&str>), ("TERM", None)], || {
604+
assert_eq!(8, available_color_count());
605+
});
606+
}
607+
608+
#[test]
609+
fn unsupported_term_colorterm_values() {
610+
skip_windows_ansi_supported!();
611+
temp_env::with_vars(
612+
[
613+
("COLORTERM", Some("gibberish")),
614+
("TERM", Some("gibberish")),
615+
],
616+
|| {
617+
assert_eq!(8u16, available_color_count());
618+
},
619+
);
620+
}
621+
}

0 commit comments

Comments
 (0)