Skip to content
Merged
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
39 changes: 33 additions & 6 deletions color.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ 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" ||
(!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd()))
NoColor = noColorIsSet() || os.Getenv("TERM") == "dumb" || !stdoutIsTerminal()

// Output defines the standard output of the print functions. By default,
// os.Stdout is used.
Output = colorable.NewColorableStdout()
// stdOut() is used.
Output = stdOut()

// Error defines a color supporting writer for os.Stderr.
Error = colorable.NewColorableStderr()
// Error defines the standard error of the print functions. By default,
// stdErr() is used.
Error = stdErr()

// colorsCache is used to reduce the count of created Color objects and
// allows to reuse already created objects with required Attribute.
Expand All @@ -40,6 +40,33 @@ func noColorIsSet() bool {
return os.Getenv("NO_COLOR") != ""
}

// stdoutIsTerminal returns true if os.Stdout is a terminal.
// Returns false if os.Stdout is nil (e.g., when running as a Windows service).
func stdoutIsTerminal() bool {
if os.Stdout == nil {
return false
}
return isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd())
}

// stdOut returns a writer for color output.
// Returns io.Discard if os.Stdout is nil (e.g., when running as a Windows service).
func stdOut() io.Writer {
if os.Stdout == nil {
return io.Discard
}
return colorable.NewColorableStdout()
}

// stdErr returns a writer for color error output.
// Returns io.Discard if os.Stderr is nil (e.g., when running as a Windows service).
func stdErr() io.Writer {
if os.Stderr == nil {
return io.Discard
}
return colorable.NewColorableStderr()
}

// Color defines a custom color object which is defined by SGR parameters.
type Color struct {
params []Attribute
Expand Down
36 changes: 36 additions & 0 deletions color_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,42 @@ func Test_noColorIsSet(t *testing.T) {
}
}

func TestStdoutIsTerminal_NilStdout(t *testing.T) {
stdout := os.Stdout
os.Stdout = nil
t.Cleanup(func() {
os.Stdout = stdout
})

if stdoutIsTerminal() {
t.Fatal("stdoutIsTerminal() = true, want false")
}
}

func TestStdOut_NilStdout(t *testing.T) {
stdout := os.Stdout
os.Stdout = nil
t.Cleanup(func() {
os.Stdout = stdout
})

if got := stdOut(); got != io.Discard {
t.Fatalf("stdOut() = %v, want %v", got, io.Discard)
}
}

func TestStdErr_NilStderr(t *testing.T) {
stderr := os.Stderr
os.Stderr = nil
t.Cleanup(func() {
os.Stderr = stderr
})

if got := stdErr(); got != io.Discard {
t.Fatalf("stdErr() = %v, want %v", got, io.Discard)
}
}

func TestColorVisual(t *testing.T) {
// First Visual Test
Output = colorable.NewColorableStdout()
Expand Down
3 changes: 3 additions & 0 deletions color_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import (
func init() {
// Opt-in for ansi color support for current process.
// https://learn.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#output-sequences
if os.Stdout == nil {
return
}
var outMode uint32
out := windows.Handle(os.Stdout.Fd())
if err := windows.GetConsoleMode(out, &outMode); err != nil {
Expand Down
Loading