rust-cli / rust-cli/anstyle

NO_COLOR suppresses non-color attributes, not just color

Open
#337 6 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-stream C-bug S-triage
Dominant language
HTML
Stars
171
Forks
44
Avg merge
2h 34m
Merged PRs (30d)
6

Description

NO_COLOR suppresses non-color attributes, not just color

Summary

When NO_COLOR is set, anstream strips all ANSI escape sequences —
bold, dim, italic, underline, reverse, strikethrough, etc. — not only
color. This diverges from no-color.org
(or its source),
whose FAQ is explicit:

Q: Should the presence of NO_COLOR disable other styling such as
bold, underline, and italic?

A: No. This standard only signals the user's intention regarding
adding ANSI color to text output.

Reproduction

NO_COLOR resolves to ColorChoice::NeverStripStream, whose
StripBytes engine (driven by anstyle_parse) discards every CSI escape
without inspecting its SGR parameters. Verified on anstream 1.0.0:

use std::io::Write as _;

let mut out = anstream::AutoStream::new(Vec::new(), anstream::ColorChoice::Never);
write!(out, "\x1b[1mbold\x1b[22m \x1b[4munderline\x1b[24m \x1b[31mred\x1b[39m").unwrap();
assert_eq!(String::from_utf8(out.into_inner()).unwrap(), "bold underline red");

Bold and underline are stripped along with the red color. Per the FAQ, only
color should be suppressed; the expected output is
\x1b[1mbold\x1b[22m \x1b[4munderline\x1b[24m red.

Suggested direction

A spec-correct color-only strip needs to distinguish color SGR parameters
from attribute parameters and re-emit only the latter. Most of the
primitives for this already live in the workspace: anstyle::Style
separates fg/bg/underline-color from effects, and anstyle-parse
already produces the SGR parameter list via csi_dispatch. The missing
piece is a decode step (param list → Style delta) so a strip adapter can
track the active style and render only effects. If a shared decoder
lived in anstyle, other consumers could reuse it for the same spec
alignment — but the shape is of course the maintainers' call.

One design point worth flagging: ColorChoice::Never is reached today for
NO_COLOR, for non-terminals, and for CLICOLOR disabled. The FAQ
constrains only the NO_COLOR case, so it may be cleanest for NO_COLOR
to resolve to a color-only path while the other Never callers keep the
full strip (pipe-to-file output today is fully flat, which is arguably
desirable for logs).

Notes

  • The empty-string trigger is already handled correctly upstream:
    anstyle_query::no_color() is non_empty(NO_COLOR), so NO_COLOR=""
    disables nothing, per spec. This issue is only about the scope of
    suppression.
  • This cascades to dependents routing through anstream (env_logger,
    clap with its color feature), which would inherit the fix.
  • crossterm is a working example of color-only NO_COLOR handling —
    it gates Colored (fg/bg/underline-color) but emits SetAttributes
    unconditionally.
  • Related: #192 (env-var support context).

Versioning

I'd frame this as a bug fix (patch bump) with a one-line CHANGELOG note
acknowledging the observable output change, but defer to the project's
own version policy.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the NO_COLOR path from anstream::ColorChoice::Never through StripStream and StripBytes, then inspect anstyle::Style and anstyle-parse's csi_dispatch. Reproduce the example and trace how SGR parameters are discarded. Done means NO_COLOR preserves non-color effects while suppressing color, with coverage for the described output and existing Never behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
cli, devtools
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.