diff --git a/log/term/colorwriter_windows.go b/log/term/colorwriter_windows.go index 4d2d67375..fcacda3a6 100644 --- a/log/term/colorwriter_windows.go +++ b/log/term/colorwriter_windows.go @@ -25,7 +25,7 @@ type colorWriter struct { // platform support for ANSI color codes. If w is not a terminal it is // returned unmodified. func NewColorWriter(w io.Writer) io.Writer { - if !IsTerminal(w) { + if !IsConsole(w) { return w } diff --git a/log/term/terminal_windows.go b/log/term/terminal_windows.go index 82353ce52..066adf29e 100644 --- a/log/term/terminal_windows.go +++ b/log/term/terminal_windows.go @@ -28,6 +28,11 @@ const ( // IsTerminal returns true if w writes to a terminal. func IsTerminal(w io.Writer) bool { + return IsConsole(w) || IsMSYSTerminal(w) +} + +// IsConsole returns true if w writes to a Windows console. +func IsConsole(w io.Writer) bool { var handle syscall.Handle if fw, ok := w.(fder); ok { @@ -40,8 +45,20 @@ func IsTerminal(w io.Writer) bool { var st uint32 // If the terminal is a Windows console, succeed right away. - if err := syscall.GetConsoleMode(handle, &st); err == nil && st != 0 { - return true + err := syscall.GetConsoleMode(handle, &st) + + return (err == nil && st != 0) +} + +// IsMSYSTerminal returns true if w writes to a MSYS/MSYS2 terminal. +func IsMSYSTerminal(w io.Writer) bool { + var handle syscall.Handle + + if fw, ok := w.(fder); ok { + handle = syscall.Handle(fw.Fd()) + } else { + // The writer has no file-descriptor and so can't be a terminal. + return false } // The terminal is not a cmd.exe terminal, let's try to detect MSYS2 terminals. diff --git a/log/term/terminal_windows_test.go b/log/term/terminal_windows_test.go index 1640ff452..b6da8e3bc 100644 --- a/log/term/terminal_windows_test.go +++ b/log/term/terminal_windows_test.go @@ -54,3 +54,18 @@ func TestIsTerminal(t *testing.T) { t.Errorf("output is supposed to be a terminal") } } + +func TestIsConsole(t *testing.T) { + // This is necessary because depending on whether `go test` is called with + // the `-v` option, stdout will or will not be bound, changing the behavior + // of the test. So we refer to it directly to avoid flakyness. + handle := getConsoleHandle() + + writer := &myWriter{ + fd: uintptr(handle), + } + + if !IsConsole(writer) { + t.Errorf("output is supposed to be a console") + } +}