fortlit/vendor/github.com/alecthomas/colour/colour.go

258 lines
6.1 KiB
Go

// Package colour ([docs][1]) provides [Quake-style colour formatting][2] for Unix terminals.
//
// It is a drop-in replacement for the fmt package.
//
// The package level functions can be used to write to stdout (or strings or
// other files). If stdout is not a terminal, colour formatting will be
// stripped.
//
// eg.
//
// colour.Printf("^0black ^1red ^2green ^3yellow ^4blue ^5magenta ^6cyan ^7white^R\n")
//
//
// For more control a Printer object can be created with various helper
// functions. This can be used to do useful things such as strip formatting,
// write to strings, and so on.
//
// The following sequences are converted to their equivalent ANSI colours:
//
// ^0 = Black
// ^1 = Red
// ^2 = Green
// ^3 = Yellow
// ^4 = Blue
// ^5 = Cyan (light blue)
// ^6 = Magenta (purple)
// ^7 = White
// ^8 = Black Background
// ^9 = Red Background
// ^a = Green Background
// ^b = Yellow Background
// ^c = Blue Background
// ^d = Cyan (light blue) Background
// ^e = Magenta (purple) Background
// ^f = White Background
// ^R = Reset
// ^U = Underline
// ^B = Bold
//
// [1]: http://godoc.org/github.com/alecthomas/colour
// [2]: http://www.holysh1t.net/quake-live-colors-nickname/
package colour
import (
"bytes"
"fmt"
"io"
"os"
"regexp"
"github.com/mattn/go-isatty"
)
var (
extract = regexp.MustCompile(`(\^[0-9a-fRDUB])|(\^\^)|([^^]+)`)
colours = map[byte]string{
'0': "\033[30m",
'1': "\033[31m",
'2': "\033[32m",
'3': "\033[33m",
'4': "\033[34m",
'5': "\033[35m",
'6': "\033[36m",
'7': "\033[37m",
'8': "\033[40m",
'9': "\033[41m",
'a': "\033[42m",
'b': "\033[43m",
'c': "\033[44m",
'd': "\033[45m",
'e': "\033[46m",
'f': "\033[47m",
'R': "\033[0m", // reset
'B': "\033[1m", // bold
'D': "\033[2m", // dim
'U': "\033[4m",
}
// Stdout is an conditional colour writer for os.Stdout.
Stdout = TTY(os.Stdout)
// Stderr is an conditional colour writer for os.Stderr.
Stderr = TTY(os.Stderr)
)
func Println(args ...interface{}) (n int, err error) {
return Stdout.Println(args...)
}
func Sprintln(args ...interface{}) string {
s := String()
_, _ = s.Println(args...)
return s.String()
}
func Fprintln(w io.Writer, args ...interface{}) (n int, err error) {
return TTY(w).Println(args...)
}
func Print(args ...interface{}) (n int, err error) {
return Stdout.Print(args...)
}
func Sprint(args ...interface{}) string {
s := String()
_, _ = s.Print(args...)
return s.String()
}
func Fprint(w io.Writer, args ...interface{}) (n int, err error) {
return TTY(w).Print(args...)
}
func Printf(format string, args ...interface{}) (n int, err error) {
return Stdout.Printf(format, args...)
}
func Sprintf(format string, args ...interface{}) string {
s := String()
_, _ = s.Printf(format, args...)
return s.String()
}
func Fprintf(w io.Writer, format string, args ...interface{}) (n int, err error) {
return TTY(w).Printf(format, args...)
}
// A Printer implements functions that accept Quake-style colour formatting
// and print coloured text.
type Printer interface {
Println(args ...interface{}) (n int, err error)
Print(args ...interface{}) (n int, err error)
Printf(format string, args ...interface{}) (n int, err error)
}
// TTY creates a Printer that colourises output if w is a terminal, or strips
// formatting if it is not.
func TTY(w io.Writer) Printer {
if f, ok := w.(*os.File); ok && isatty.IsTerminal(f.Fd()) {
return &colourPrinter{w}
}
return &stripPrinter{w}
}
// Colour creats a new ANSI colour Printer on w.
func Colour(w io.Writer) Printer {
return &colourPrinter{w}
}
// Strip returns a Printer that strips all colour codes from printed strings before writing to w.
func Strip(w io.Writer) Printer {
return &stripPrinter{w}
}
type StringPrinter struct {
Printer
w *bytes.Buffer
}
// String creates a new Printer that writes ANSI coloured text to a buffer.
// Use the String() method to return the printed string.
func String() *StringPrinter {
w := &bytes.Buffer{}
f := Colour(w)
return &StringPrinter{f, w}
}
// StringStripper writes text stripped of colour formatting codes to a string.
// Use the String() method to return the printed string.
func StringStripper() *StringPrinter {
w := &bytes.Buffer{}
f := Strip(w)
return &StringPrinter{f, w}
}
func (s *StringPrinter) String() string {
return s.w.String()
}
type colourPrinter struct {
w io.Writer
}
func (c *colourPrinter) Println(args ...interface{}) (n int, err error) {
for i, arg := range args {
if s, ok := arg.(string); ok {
args[i] = FormatString(s)
}
}
return fmt.Fprintln(c.w, args...)
}
func (c *colourPrinter) Print(args ...interface{}) (n int, err error) {
for i, arg := range args {
if s, ok := arg.(string); ok {
args[i] = FormatString(s)
}
}
return fmt.Fprint(c.w, args...)
}
func (c *colourPrinter) Printf(format string, args ...interface{}) (n int, err error) {
return fmt.Fprintf(c.w, FormatString(format), args...)
}
type stripPrinter struct {
w io.Writer
}
func (p *stripPrinter) Println(args ...interface{}) (n int, err error) {
return fmt.Fprintln(p.w, stripArgs(args...)...)
}
func (p *stripPrinter) Print(args ...interface{}) (n int, err error) {
return fmt.Fprint(p.w, stripArgs(args...)...)
}
func (p *stripPrinter) Printf(format string, args ...interface{}) (n int, err error) {
return fmt.Fprintf(p.w, StripFormatting(format), args...)
}
func FormatString(s string) string {
out := &bytes.Buffer{}
for _, match := range extract.FindAllStringSubmatch(s, -1) {
if match[1] != "" {
n := match[1][1]
out.WriteString(colours[n])
} else if match[2] != "" {
out.WriteString("^")
} else {
out.WriteString(match[3])
}
}
return out.String()
}
func StripFormatting(s string) string {
out := &bytes.Buffer{}
for _, match := range extract.FindAllStringSubmatch(s, -1) {
if match[2] != "" {
out.WriteString("^")
} else if match[1] == "" {
out.WriteString(match[3])
}
}
return out.String()
}
func stripArgs(args ...interface{}) []interface{} {
for i, arg := range args {
if s, ok := arg.(string); ok {
args[i] = StripFormatting(s)
}
}
return args
}