258 lines
6.1 KiB
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
|
|
}
|