Skip to content

Honor NO_COLOR without suppressing text attributes - #294

Open
MsfPablo wants to merge 1 commit into
console-rs:mainfrom
MsfPablo:fix-291
Open

Honor NO_COLOR without suppressing text attributes#294
MsfPablo wants to merge 1 commit into
console-rs:mainfrom
MsfPablo:fix-291

Conversation

@MsfPablo

Copy link
Copy Markdown

Summary

The current StyledObject formatting path in console drops the
attribute escapes (bold, italic, underline, blink, reverse, hidden,
strikethrough) whenever colors are disabled — and NO_COLOR (per
no-color.org) is what disables colors. That contradicts the spec, which
is explicit that only color is suppressed:

Command-line software which adds ANSI color to its output by default
should check for a NO_COLOR environment variable that, when present
(regardless of its value), prevents the addition of ANSI color.

console also currently disables color when NO_COLOR="" is set,
because env::var("NO_COLOR").is_ok() returns true for any value
(including the empty string). Per the spec the presence of the variable
alone should disable color — but a deliberately-empty variable should be
a no-op.

Changes

  • Move the attribute escape emission in impl_fmt! (src/utils.rs)
    outside the colors_enabled() / colors_enabled_stderr() gate, so
    attributes are still emitted (with a trailing reset) when colors are
    suppressed.
  • Replace env::var("NO_COLOR").is_ok() with a non-empty check
    (env::var_os("NO_COLOR").is_some_and(|v| !v.is_empty())) in
    src/unix_term.rs and src/windows_term/mod.rs, matching the
    spec.
  • Add a regression test (test_attrs_survive_colors_disabled) covering
    both behaviors.

Verification

$ make test          # 12 test runs, all pass
$ make lint          # clippy with --deny warnings, clean
$ make format-check  # rustfmt --check, clean

The new test verifies:

  • style("foo").bold() still emits \x1b[1m…\x1b[0m when
    colors_enabled() is false.
  • style("bar").italic() still emits \x1b[3m…\x1b[0m in the
    same condition.
  • style("baz").red() does not emit \x1b[31m and does
    not emit a trailing reset when colors are disabled.

AI disclosure

This PR was drafted with AI assistance (Claude) and self-reviewed. I
read the relevant code paths, reproduced the failing behavior locally
against the crate's existing test harness, and verified the fix does not
change the rendered output for the unaffected code paths.

Fixes #291

Per <https://no-color.org/>, the NO_COLOR convention is intended to
disable *color* output, not text attributes such as bold, italic, or
underline. The current behavior of the StyledObject fmt implementation
drops the attribute escapes whenever colors are disabled (which NO_COLOR
triggers), contradicting the spec.

Likewise, an empty value like `NO_COLOR=""` should not disable color,
but `env::var("NO_COLOR").is_ok()` returns true for any value,
including the empty string.

This commit:
* moves the attribute escape emission in impl_fmt! outside the
  colors_enabled() gate so attrs survive when colors are suppressed;
* changes `env::var("NO_COLOR").is_ok()` in the unix and windows
  backends to a non-empty check that matches the spec;
* adds a regression test exercising both behaviors.

Fixes console-rs#291
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

NO_COLOR drops non-color attributes and treats empty value as active

1 participant