Implement forcing CLI colour on, and document it better

This is necessary to make some old tests work when testing colour
against non-interactive outputs.

Change-Id: Id89f8a1f45c587fede35a69db85f7a52f2c0a981
This commit is contained in:
Jade Lovelace 2024-08-01 12:26:16 -07:00
parent 700762d8b2
commit 378ec5fb06
3 changed files with 25 additions and 3 deletions

View file

@ -427,6 +427,7 @@ I grepped `src/` for `get[eE]nv\("` to find the mentions in Lix code.
- `NIX_SHOW_STATS_PATH` - Writes those statistics into a file at the given path instead of stdout. Undocumented. - `NIX_SHOW_STATS_PATH` - Writes those statistics into a file at the given path instead of stdout. Undocumented.
- `NIX_SHOW_SYMBOLS` - Dumps the symbol table into the show-stats json output. - `NIX_SHOW_SYMBOLS` - Dumps the symbol table into the show-stats json output.
- `TERM` - If `dumb` or unset, disables ANSI colour output. - `TERM` - If `dumb` or unset, disables ANSI colour output.
- `FORCE_COLOR`, `CLICOLOR_FORCE` - Enables ANSI colour output if `NO_COLOR`/`NOCOLOR` not set.
- `NO_COLOR`, `NOCOLOR` - Disables ANSI colour output. - `NO_COLOR`, `NOCOLOR` - Disables ANSI colour output.
- `_NIX_DEVELOPER_SHOW_UNKNOWN_LOCATIONS` - Highlights unknown locations in errors. - `_NIX_DEVELOPER_SHOW_UNKNOWN_LOCATIONS` - Highlights unknown locations in errors.
- `NIX_PROFILE` - Selects which profile `nix-env` will operate on. Documented elsewhere. - `NIX_PROFILE` - Selects which profile `nix-env` will operate on. Documented elsewhere.

View file

@ -9,9 +9,21 @@ namespace nix {
bool shouldANSI() bool shouldANSI()
{ {
return isatty(STDERR_FILENO) // Implements the behaviour described by https://bixense.com/clicolors/
&& getEnv("TERM").value_or("dumb") != "dumb" // As well as https://force-color.org/ for compatibility, since it fits in the same shape.
&& !(getEnv("NO_COLOR").has_value() || getEnv("NOCOLOR").has_value()); // NO_COLOR CLICOLOR CLICOLOR_FORCE Colours?
// set x x No
// unset x set Yes
// unset x unset If attached to a terminal
// [we choose the "modern" approach of colour-by-default]
auto compute = []() -> bool {
bool mustNotColour = getEnv("NO_COLOR").has_value() || getEnv("NOCOLOR").has_value();
bool shouldForce = getEnv("CLICOLOR_FORCE").has_value() || getEnv("FORCE_COLOR").has_value();
bool isTerminal = isatty(STDERR_FILENO) && getEnv("TERM").value_or("dumb") != "dumb";
return !mustNotColour && (shouldForce || isTerminal);
};
static bool cached = compute();
return cached;
} }
std::string filterANSIEscapes(std::string_view s, bool filterAll, unsigned int width) std::string filterANSIEscapes(std::string_view s, bool filterAll, unsigned int width)

View file

@ -9,6 +9,15 @@ namespace nix {
/** /**
* Determine whether ANSI escape sequences are appropriate for the * Determine whether ANSI escape sequences are appropriate for the
* present output. * present output.
*
* This follows the rules described on https://bixense.com/clicolors/
* with CLICOLOR defaulted to enabled (and thus ignored).
*
* That is to say, the following procedure is followed in order:
* - NO_COLOR or NOCOLOR set -> always disable colour
* - CLICOLOR_FORCE or FORCE_COLOR set -> enable colour
* - The output is a tty; TERM != "dumb" -> enable colour
* - Otherwise -> disable colour
*/ */
bool shouldANSI(); bool shouldANSI();