blob: 810118ad4e2dd91bb863852f8b1c7690c504861e [file] [log] [blame]
// Copyright 2023 The Dawn & Tint Authors
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package term
import (
"io"
"os"
"github.com/mattn/go-colorable"
"github.com/mattn/go-isatty"
)
// ANSI escape sequences
const (
Escape = "\u001B["
PositionLeft = Escape + "0G"
Reset = Escape + "0m"
Bold = Escape + "1m"
Red = Escape + "31m"
Green = Escape + "32m"
Yellow = Escape + "33m"
Blue = Escape + "34m"
Magenta = Escape + "35m"
Cyan = Escape + "36m"
White = Escape + "37m"
)
// CanUseAnsiEscapeSequences looks at the process's environment to determine
// whether its sensible to emit ansi-escape sequences.
func CanUseAnsiEscapeSequences() bool {
if os.Getenv("TERM") != "dumb" ||
(isatty.IsTerminal(os.Stdout.Fd()) && isatty.IsTerminal(os.Stderr.Fd())) ||
(isatty.IsCygwinTerminal(os.Stdout.Fd()) && isatty.IsCygwinTerminal(os.Stderr.Fd())) {
if _, disable := os.LookupEnv("NO_COLOR"); !disable {
return true
}
}
return false
}
// NewAnsiWriter returns an io.WriteCloser that writes to os.Stdout and can
// consume ANSI escape sequences.
// The returned writer will ensure that concurrent writes are printed whole
// (no interleaving between Write()s).
// If enable is true, then ANSI escape sequences will be emitted
// If enable is false, then ANSI escape sequences will be stripped before
// writing to stdout.
// The returned io.WriteCloser must be closed to flush any pending data
func NewAnsiWriter(enable bool) io.WriteCloser {
// Create a thread-safe, color supporting stdout wrapper.
if enable {
return newMuxWriter(colorable.NewColorableStdout())
} else {
return newMuxWriter(colorable.NewNonColorable(os.Stdout))
}
}
type muxWriter struct {
data chan []byte
err chan error
}
// newMuxWriter returns a thread-safe io.WriteCloser, that writes to w
func newMuxWriter(w io.Writer) *muxWriter {
m := muxWriter{
data: make(chan []byte, 256),
err: make(chan error, 1),
}
go func() {
defer close(m.err)
for data := range m.data {
_, err := w.Write(data)
if err != nil {
m.err <- err
return
}
}
m.err <- nil
}()
return &m
}
func (w *muxWriter) Write(data []byte) (n int, err error) {
w.data <- append([]byte{}, data...)
return len(data), nil
}
func (w *muxWriter) Close() error {
close(w.data)
return <-w.err
}