Skip to content
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
43 changes: 16 additions & 27 deletions color.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ var (
// 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 = noColorIsSet() || os.Getenv("TERM") == "dumb" ||
// NOTE: NO_COLOR environment variable will only be checked at init.
NoColor = hasNoColorEnv() || 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,
Expand All @@ -35,15 +36,15 @@ var (
colorsCacheMu sync.Mutex // protects colorsCache
)

// noColorIsSet returns true if the environment variable NO_COLOR is set to a non-empty string.
func noColorIsSet() bool {
// hasNoColorEnv returns true if the environment variable NO_COLOR is set to a non-empty string.
func hasNoColorEnv() bool {
return os.Getenv("NO_COLOR") != ""
}

// Color defines a custom color object which is defined by SGR parameters.
type Color struct {
params []Attribute
noColor *bool
noColor bool
}

// Attribute defines a single SGR Code
Expand Down Expand Up @@ -148,8 +149,10 @@ func New(value ...Attribute) *Color {
params: make([]Attribute, 0),
}

if noColorIsSet() {
c.noColor = boolPtr(true)
if NoColor {
c.noColor = true
} else {
c.noColor = false
}

c.Add(value...)
Expand Down Expand Up @@ -200,7 +203,7 @@ func Unset() {

// Set sets the SGR sequence.
func (c *Color) Set() *Color {
if c.isNoColorSet() {
if c.noColor {
return c
}

Expand All @@ -209,7 +212,7 @@ func (c *Color) Set() *Color {
}

func (c *Color) unset() {
if c.isNoColorSet() {
if c.noColor {
return
}

Expand All @@ -220,7 +223,7 @@ func (c *Color) unset() {
// a low-level function, and users should use the higher-level functions, such
// as color.Fprint, color.Print, etc.
func (c *Color) SetWriter(w io.Writer) *Color {
if c.isNoColorSet() {
if c.noColor {
return c
}

Expand All @@ -231,7 +234,7 @@ func (c *Color) SetWriter(w io.Writer) *Color {
// UnsetWriter resets all escape attributes and clears the output with the give
// io.Writer. Usually should be called after SetWriter().
func (c *Color) UnsetWriter(w io.Writer) {
if c.isNoColorSet() {
if c.noColor {
return
}

Expand Down Expand Up @@ -414,7 +417,7 @@ func (c *Color) sequence() string {
// wrap wraps the s string with the colors attributes. The string is ready to
// be printed.
func (c *Color) wrap(s string) string {
if c.isNoColorSet() {
if c.noColor {
return s
}

Expand Down Expand Up @@ -444,23 +447,13 @@ func (c *Color) unformat() string {
// code and still being able to output. Can be used for flags like
// "--no-color". To enable back use EnableColor() method.
func (c *Color) DisableColor() {
c.noColor = boolPtr(true)
c.noColor = true
}

// EnableColor enables the color output. Use it in conjunction with
// DisableColor(). Otherwise, this method has no side effects.
func (c *Color) EnableColor() {
c.noColor = boolPtr(false)
}

func (c *Color) isNoColorSet() bool {
// check first if we have user set action
if c.noColor != nil {
return *c.noColor
}

// if not return the global option, which is disabled by default
return NoColor
c.noColor = false
}

// Equals returns a boolean value indicating whether two colors are equal.
Expand Down Expand Up @@ -494,10 +487,6 @@ func (c *Color) attrExists(a Attribute) bool {
return false
}

func boolPtr(v bool) *bool {
return &v
}

func getCachedColor(p Attribute) *Color {
colorsCacheMu.Lock()
defer colorsCacheMu.Unlock()
Expand Down
48 changes: 3 additions & 45 deletions color_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,49 +158,7 @@ func TestNoColor(t *testing.T) {
}
}

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", "1")
t.Cleanup(func() {
os.Unsetenv("NO_COLOR")
})

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 Test_noColorIsSet(t *testing.T) {
func Test_hasNoColorEnv(t *testing.T) {
tests := []struct {
name string
act func()
Expand Down Expand Up @@ -228,8 +186,8 @@ func Test_noColorIsSet(t *testing.T) {
os.Unsetenv("NO_COLOR")
})
tt.act()
if got := noColorIsSet(); got != tt.want {
t.Errorf("noColorIsSet() = %v, want %v", got, tt.want)
if got := hasNoColorEnv(); got != tt.want {
t.Errorf("hasNoColorEnv() = %v, want %v", got, tt.want)
}
})
}
Expand Down