Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement CLICOLOR_FORCE environment variable #12148

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/libcmd/markdown.cc
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ static std::string doRenderMarkdownToTerminal(std::string_view markdown)
if (!rndr_res)
throw Error("allocation error while rendering Markdown");

return filterANSIEscapes(std::string(buf->data, buf->size), !isTTY());
return filterANSIEscapes(std::string(buf->data, buf->size), !shouldANSI());
}

std::string renderMarkdownToTerminal(std::string_view markdown)
Expand Down
2 changes: 1 addition & 1 deletion src/libmain/progress-bar.cc
Original file line number Diff line number Diff line change
Expand Up @@ -557,7 +557,7 @@ class ProgressBar : public Logger

Logger * makeProgressBar()
{
return new ProgressBar(isTTY());
return new ProgressBar(shouldANSI());
}

void startProgressBar()
Expand Down
4 changes: 3 additions & 1 deletion src/libmain/shared.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#include "loggers.hh"
#include "progress-bar.hh"
#include "signals.hh"
#include "terminal.hh"


#include <algorithm>
#include <exception>
Expand Down Expand Up @@ -370,7 +372,7 @@ int handleExceptions(const std::string & programName, std::function<void()> fun)

RunPager::RunPager()
{
if (!isatty(STDOUT_FILENO)) return;
if (!isOutputARealTerminal(StandardOutputStream::Stdout)) return;
char * pager = getenv("NIX_PAGER");
if (!pager) pager = getenv("PAGER");
if (pager && ((std::string) pager == "" || (std::string) pager == "cat")) return;
Expand Down
2 changes: 1 addition & 1 deletion src/libutil/logging.cc
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class SimpleLogger : public Logger
: printBuildLogs(printBuildLogs)
{
systemd = getEnv("IN_SYSTEMD") == "1";
tty = isTTY();
tty = shouldANSI();
}

bool isVerbose() override {
Expand Down
26 changes: 20 additions & 6 deletions src/libutil/terminal.cc
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,28 @@ inline std::pair<int, size_t> charWidthUTF8Helper(std::string_view s)

namespace nix {

bool isTTY()
bool isOutputARealTerminal(StandardOutputStream fileno)
{
static const bool tty =
isatty(STDERR_FILENO)
&& getEnv("TERM").value_or("dumb") != "dumb"
&& !(getEnv("NO_COLOR").has_value() || getEnv("NOCOLOR").has_value());
return isatty(int(fileno)) && getEnv("TERM").value_or("dumb") != "dumb";
}

return tty;
bool shouldANSI(StandardOutputStream fileno)
{
// Implements the behaviour described by https://bixense.com/clicolors/
// As well as https://force-color.org/ for compatibility, since it fits in the same shape.
// 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]
Comment on lines +74 to +77
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm assuming the x means "any value"...? Could you just write that, please? 😆

It took me so long to understand the difference between "x" and "set" x)

auto compute = [](StandardOutputStream fileno) -> 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 = isOutputARealTerminal(fileno);
return !mustNotColour && (shouldForce || isTerminal);
};
static bool cached[2] = {compute(StandardOutputStream::Stdout), compute(StandardOutputStream::Stderr)};
return cached[int(fileno) - 1];
}

std::string filterANSIEscapes(std::string_view s, bool filterAll, unsigned int width)
Expand Down
34 changes: 33 additions & 1 deletion src/libutil/terminal.hh
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,43 @@
#include <string>

namespace nix {

enum class StandardOutputStream {
Stdout = 1,
Stderr = 2,
};

/**
* Determine whether the output is a real terminal (i.e. not dumb, not a pipe).
*
* This is probably not what you want, you may want shouldANSI() or something
* more specific. Think about how the output should work with a pager or
* entirely non-interactive scripting use.
*
* The user may be redirecting the Lix output to a pager, but have stderr
* connected to a terminal. Think about where you are outputting the text when
* deciding whether to use STDERR_FILENO or STDOUT_FILENO.
*
* \param fileno file descriptor number to check if it is a tty
*/
bool isOutputARealTerminal(StandardOutputStream fileno);

/**
* Determine whether ANSI escape sequences are appropriate for the
* 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
*
* \param fileno which file descriptor number to consider. Use the one you are outputting to
*/
bool isTTY();
bool shouldANSI(StandardOutputStream fileno = StandardOutputStream::Stderr);

/**
* Truncate a string to 'width' printable characters. If 'filterAll'
Expand Down
2 changes: 1 addition & 1 deletion src/nix-env/nix-env.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1091,7 +1091,7 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
return;
}

bool tty = isTTY();
bool tty = shouldANSI();
RunPager pager;

Table table;
Expand Down
2 changes: 1 addition & 1 deletion src/nix/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,7 @@ void mainWrapped(int argc, char * * argv)
settings.verboseBuild = false;

// If on a terminal, progress will be displayed via progress bars etc. (thus verbosity=notice)
if (nix::isTTY()) {
if (nix::isOutputARealTerminal(StandardOutputStream::Stderr)) {
verbosity = lvlNotice;
} else {
verbosity = lvlInfo;
Expand Down
2 changes: 1 addition & 1 deletion src/nix/prefetch.cc
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ static int main_nix_prefetch_url(int argc, char * * argv)

Finally f([]() { stopProgressBar(); });

if (isTTY())
if (isOutputARealTerminal(StandardOutputStream::Stderr))
startProgressBar();

auto store = openStore();
Expand Down
Loading