diff --git a/src/unix_term.rs b/src/unix_term.rs index 815d976a..154af2c7 100644 --- a/src/unix_term.rs +++ b/src/unix_term.rs @@ -27,7 +27,7 @@ pub(crate) fn is_a_color_terminal(out: &Term) -> bool { return false; } - if env::var("NO_COLOR").is_ok() { + if env::var_os("NO_COLOR").is_some_and(|v| !v.is_empty()) { return false; } diff --git a/src/utils.rs b/src/utils.rs index 3a48628e..e4537e4f 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -823,6 +823,16 @@ macro_rules! impl_fmt { write!(f, "{}", self.style.attrs)?; reset = true; } + } else { + // NO_COLOR and similar controls disable color, but per + // text attributes (bold, italic, + // underline, etc.) are not "color" and should remain + // visible. Emit them outside the colors_enabled gate so + // they survive when colors are suppressed. + if !self.style.attrs.is_empty() { + write!(f, "{}", self.style.attrs)?; + reset = true; + } } fmt::$name::fmt(&self.val, f)?; if reset { @@ -1227,3 +1237,46 @@ fn test_style_from_non_ascii_bg() { // silently ignores non-ascii assert_eq!(parsed_style, Style::default()); } + +#[test] +fn test_attrs_survive_colors_disabled() { + // Regression test for : + // per , `NO_COLOR` suppresses *color*, not text + // attributes. When color output is otherwise disabled, attributes such as + // bold/italic/underline should still be emitted (with a trailing reset). + let prev = colors_enabled(); + set_colors_enabled(false); + + let bold = style("foo").bold().to_string(); + assert!( + bold.contains("\x1b[1m"), + "bold escape sequence missing when colors are disabled: {bold:?}" + ); + assert!( + bold.ends_with("\x1b[0m"), + "reset escape sequence missing after bold: {bold:?}" + ); + + let italic = style("bar").italic().to_string(); + assert!( + italic.contains("\x1b[3m"), + "italic escape sequence missing when colors are disabled: {italic:?}" + ); + assert!( + italic.ends_with("\x1b[0m"), + "reset escape sequence missing after italic: {italic:?}" + ); + + // Foreground color is *still* suppressed when colors are disabled. + let colored = style("baz").red().to_string(); + assert!( + !colored.contains("\x1b[31m"), + "foreground color escape sequence should be suppressed when colors are disabled: {colored:?}" + ); + assert!( + !colored.contains("\x1b[0m"), + "reset escape sequence should not be emitted when nothing is styled: {colored:?}" + ); + + set_colors_enabled(prev); +} diff --git a/src/windows_term/mod.rs b/src/windows_term/mod.rs index 8d8a7803..a967c094 100644 --- a/src/windows_term/mod.rs +++ b/src/windows_term/mod.rs @@ -63,7 +63,7 @@ pub(crate) fn is_a_color_terminal(out: &Term) -> bool { if !is_a_terminal(out) { return false; } - if env::var("NO_COLOR").is_ok() { + if env::var_os("NO_COLOR").is_some_and(|v| !v.is_empty()) { return false; } if msys_tty_on(out) {