From f08ed43f5e7b6486ab3b365087f659f06b74555c Mon Sep 17 00:00:00 2001 From: Fatih Arslan Date: Thu, 13 May 2021 22:29:58 +0300 Subject: [PATCH] Add `NO_COLOR` support to disable color output This PR adds support for the enviroment variable `NO_COLOR`. If set (regardless of its value), the `colors` package disables color output. For more information about this environment variable please checkout this website: https://no-color.org closes: https://github.com/fatih/color/issues/136 --- README.md | 11 +++++++---- color.go | 23 +++++++++++++++++++---- color_test.go | 47 +++++++++++++++++++++++++++++++++++++++++++++-- doc.go | 2 ++ 4 files changed, 73 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index ce40086..5c751f2 100644 --- a/README.md +++ b/README.md @@ -127,11 +127,14 @@ fmt.Println("All text will now be bold magenta.") There might be a case where you want to explicitly disable/enable color output. the `go-isatty` package will automatically disable color output for non-tty output streams -(for example if the output were piped directly to `less`) +(for example if the output were piped directly to `less`). -`Color` has support to disable/enable colors both globally and for single color -definitions. For example suppose you have a CLI app and a `--no-color` bool flag. You -can easily disable the color output with: +The `color` package also disables color output if the [`NO_COLOR`](https://no-color.org) environment +variable is set (regardless of its value). + +`Color` has support to disable/enable colors programatically both globally and +for single color definitions. For example suppose you have a CLI app and a +`--no-color` bool flag. You can easily disable the color output with: ```go var flagNoColor = flag.Bool("no-color", false, "Disable color output") diff --git a/color.go b/color.go index fa49c98..98a60f3 100644 --- a/color.go +++ b/color.go @@ -15,9 +15,11 @@ import ( var ( // NoColor defines if the output is colorized or not. It's dynamically set to // false or true based on the stdout's file descriptor referring to a terminal - // or not. This is a global option and affects all colors. For more control - // over each color block use the methods DisableColor() individually. - NoColor = os.Getenv("TERM") == "dumb" || + // or not. It's also set to true if the NO_COLOR environment variable is + // set (regardless of its value). This is a global option and affects all + // colors. For more control over each color block use the methods + // DisableColor() individually. + NoColor = noColorExists() || os.Getenv("TERM") == "dumb" || (!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd())) // Output defines the standard output of the print functions. By default @@ -33,6 +35,12 @@ var ( colorsCacheMu sync.Mutex // protects colorsCache ) +// noColorExists returns true if the environment variable NO_COLOR exists. +func noColorExists() bool { + _, exists := os.LookupEnv("NO_COLOR") + return exists +} + // Color defines a custom color object which is defined by SGR parameters. type Color struct { params []Attribute @@ -108,7 +116,14 @@ const ( // New returns a newly created color object. func New(value ...Attribute) *Color { - c := &Color{params: make([]Attribute, 0)} + c := &Color{ + params: make([]Attribute, 0), + } + + if noColorExists() { + c.noColor = boolPtr(true) + } + c.Add(value...) return c } diff --git a/color_test.go b/color_test.go index a8ed14f..0ee2a7b 100644 --- a/color_test.go +++ b/color_test.go @@ -142,9 +142,52 @@ func TestNoColor(t *testing.T) { // global check NoColor = true - defer func() { + t.Cleanup(func() { NoColor = false - }() + }) + + for _, c := range testColors { + p := New(c.code) + p.Print(c.text) + + line, _ := rb.ReadString('\n') + if line != c.text { + t.Errorf("Expecting %s, got '%s'\n", c.text, line) + } + } +} + +func TestNoColor_Env(t *testing.T) { + rb := new(bytes.Buffer) + Output = rb + + testColors := []struct { + text string + code Attribute + }{ + {text: "black", code: FgBlack}, + {text: "red", code: FgRed}, + {text: "green", code: FgGreen}, + {text: "yellow", code: FgYellow}, + {text: "blue", code: FgBlue}, + {text: "magent", code: FgMagenta}, + {text: "cyan", code: FgCyan}, + {text: "white", code: FgWhite}, + {text: "hblack", code: FgHiBlack}, + {text: "hred", code: FgHiRed}, + {text: "hgreen", code: FgHiGreen}, + {text: "hyellow", code: FgHiYellow}, + {text: "hblue", code: FgHiBlue}, + {text: "hmagent", code: FgHiMagenta}, + {text: "hcyan", code: FgHiCyan}, + {text: "hwhite", code: FgHiWhite}, + } + + os.Setenv("NO_COLOR", "") + t.Cleanup(func() { + os.Unsetenv("NO_COLOR") + }) + for _, c := range testColors { p := New(c.code) p.Print(c.text) diff --git a/doc.go b/doc.go index cf1e965..04541de 100644 --- a/doc.go +++ b/doc.go @@ -118,6 +118,8 @@ the color output with: color.NoColor = true // disables colorized output } +You can also disable the color by setting the NO_COLOR environment variable to any value. + It also has support for single color definitions (local). You can disable/enable color output on the fly: