mirror of https://github.com/ethereum/go-ethereum
PR #856 broke command line editing by wrapping stdout with a filter that interprets ANSI escape sequences to fix colored printing on windows. Implement the printer in Go instead so it can do its own platform-dependent coloring. As a nice side effect, the JS console is now noticeably more responsive when printing results. Fixes #1608 Fixes #1612pull/1642/head
parent
05c66529b2
commit
0ef80bb3d0
@ -0,0 +1,3 @@ |
||||
language: go |
||||
go: 1.3 |
||||
|
@ -0,0 +1,20 @@ |
||||
The MIT License (MIT) |
||||
|
||||
Copyright (c) 2013 Fatih Arslan |
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of |
||||
this software and associated documentation files (the "Software"), to deal in |
||||
the Software without restriction, including without limitation the rights to |
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of |
||||
the Software, and to permit persons to whom the Software is furnished to do so, |
||||
subject to the following conditions: |
||||
|
||||
The above copyright notice and this permission notice shall be included in all |
||||
copies or substantial portions of the Software. |
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS |
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR |
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER |
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
@ -0,0 +1,151 @@ |
||||
# Color [![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](http://godoc.org/github.com/fatih/color) [![Build Status](http://img.shields.io/travis/fatih/color.svg?style=flat-square)](https://travis-ci.org/fatih/color) |
||||
|
||||
|
||||
|
||||
Color lets you use colorized outputs in terms of [ANSI Escape Codes](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors) in Go (Golang). It has support for Windows too! The API can be used in several ways, pick one that suits you. |
||||
|
||||
|
||||
|
||||
![Color](http://i.imgur.com/c1JI0lA.png) |
||||
|
||||
|
||||
## Install |
||||
|
||||
```bash |
||||
go get github.com/fatih/color |
||||
``` |
||||
|
||||
## Examples |
||||
|
||||
### Standard colors |
||||
|
||||
```go |
||||
// Print with default helper functions |
||||
color.Cyan("Prints text in cyan.") |
||||
|
||||
// A newline will be appended automatically |
||||
color.Blue("Prints %s in blue.", "text") |
||||
|
||||
// These are using the default foreground colors |
||||
color.Red("We have red") |
||||
color.Magenta("And many others ..") |
||||
|
||||
``` |
||||
|
||||
### Mix and reuse colors |
||||
|
||||
```go |
||||
// Create a new color object |
||||
c := color.New(color.FgCyan).Add(color.Underline) |
||||
c.Println("Prints cyan text with an underline.") |
||||
|
||||
// Or just add them to New() |
||||
d := color.New(color.FgCyan, color.Bold) |
||||
d.Printf("This prints bold cyan %s\n", "too!.") |
||||
|
||||
// Mix up foreground and background colors, create new mixes! |
||||
red := color.New(color.FgRed) |
||||
|
||||
boldRed := red.Add(color.Bold) |
||||
boldRed.Println("This will print text in bold red.") |
||||
|
||||
whiteBackground := red.Add(color.BgWhite) |
||||
whiteBackground.Println("Red text with white background.") |
||||
``` |
||||
|
||||
### Custom print functions (PrintFunc) |
||||
|
||||
```go |
||||
// Create a custom print function for convenience |
||||
red := color.New(color.FgRed).PrintfFunc() |
||||
red("Warning") |
||||
red("Error: %s", err) |
||||
|
||||
// Mix up multiple attributes |
||||
notice := color.New(color.Bold, color.FgGreen).PrintlnFunc() |
||||
notice("Don't forget this...") |
||||
``` |
||||
|
||||
### Insert into noncolor strings (SprintFunc) |
||||
|
||||
```go |
||||
// Create SprintXxx functions to mix strings with other non-colorized strings: |
||||
yellow := color.New(color.FgYellow).SprintFunc() |
||||
red := color.New(color.FgRed).SprintFunc() |
||||
fmt.Printf("This is a %s and this is %s.\n", yellow("warning"), red("error")) |
||||
|
||||
info := color.New(color.FgWhite, color.BgGreen).SprintFunc() |
||||
fmt.Printf("This %s rocks!\n", info("package")) |
||||
|
||||
// Use helper functions |
||||
fmt.Printf("This", color.RedString("warning"), "should be not neglected.") |
||||
fmt.Printf(color.GreenString("Info:"), "an important message." ) |
||||
|
||||
// Windows supported too! Just don't forget to change the output to color.Output |
||||
fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS")) |
||||
``` |
||||
|
||||
### Plug into existing code |
||||
|
||||
```go |
||||
// Use handy standard colors |
||||
color.Set(color.FgYellow) |
||||
|
||||
fmt.Println("Existing text will now be in yellow") |
||||
fmt.Printf("This one %s\n", "too") |
||||
|
||||
color.Unset() // Don't forget to unset |
||||
|
||||
// You can mix up parameters |
||||
color.Set(color.FgMagenta, color.Bold) |
||||
defer color.Unset() // Use it in your function |
||||
|
||||
fmt.Println("All text will now be bold magenta.") |
||||
``` |
||||
|
||||
### Disable color |
||||
|
||||
There might be a case where you want to disable color output (for example to |
||||
pipe the standard output of your app to somewhere else). `Color` has support to |
||||
disable colors both globally and for single color definition. For example |
||||
suppose you have a CLI app and a `--no-color` bool flag. You can easily disable |
||||
the color output with: |
||||
|
||||
```go |
||||
|
||||
var flagNoColor = flag.Bool("no-color", false, "Disable color output") |
||||
|
||||
if *flagNoColor { |
||||
color.NoColor = true // disables colorized output |
||||
} |
||||
``` |
||||
|
||||
It also has support for single color definitions (local). You can |
||||
disable/enable color output on the fly: |
||||
|
||||
```go |
||||
c := color.New(color.FgCyan) |
||||
c.Println("Prints cyan text") |
||||
|
||||
c.DisableColor() |
||||
c.Println("This is printed without any color") |
||||
|
||||
c.EnableColor() |
||||
c.Println("This prints again cyan...") |
||||
``` |
||||
|
||||
## Todo |
||||
|
||||
* Save/Return previous values |
||||
* Evaluate fmt.Formatter interface |
||||
|
||||
|
||||
## Credits |
||||
|
||||
* [Fatih Arslan](https://github.com/fatih) |
||||
* Windows support via @shiena: [ansicolor](https://github.com/shiena/ansicolor) |
||||
|
||||
## License |
||||
|
||||
The MIT License (MIT) - see [`LICENSE.md`](https://github.com/fatih/color/blob/master/LICENSE.md) for more details |
||||
|
@ -0,0 +1,353 @@ |
||||
package color |
||||
|
||||
import ( |
||||
"fmt" |
||||
"os" |
||||
"strconv" |
||||
"strings" |
||||
|
||||
"github.com/mattn/go-isatty" |
||||
"github.com/shiena/ansicolor" |
||||
) |
||||
|
||||
// NoColor defines if the output is colorized or not. It's dynamically set to
|
||||
// false or true based on the stdout's file descriptor referring to a terminal
|
||||
// or not. This is a global option and affects all colors. For more control
|
||||
// over each color block use the methods DisableColor() individually.
|
||||
var NoColor = !isatty.IsTerminal(os.Stdout.Fd()) |
||||
|
||||
// Color defines a custom color object which is defined by SGR parameters.
|
||||
type Color struct { |
||||
params []Attribute |
||||
noColor *bool |
||||
} |
||||
|
||||
// Attribute defines a single SGR Code
|
||||
type Attribute int |
||||
|
||||
const escape = "\x1b" |
||||
|
||||
// Base attributes
|
||||
const ( |
||||
Reset Attribute = iota |
||||
Bold |
||||
Faint |
||||
Italic |
||||
Underline |
||||
BlinkSlow |
||||
BlinkRapid |
||||
ReverseVideo |
||||
Concealed |
||||
CrossedOut |
||||
) |
||||
|
||||
// Foreground text colors
|
||||
const ( |
||||
FgBlack Attribute = iota + 30 |
||||
FgRed |
||||
FgGreen |
||||
FgYellow |
||||
FgBlue |
||||
FgMagenta |
||||
FgCyan |
||||
FgWhite |
||||
) |
||||
|
||||
// Background text colors
|
||||
const ( |
||||
BgBlack Attribute = iota + 40 |
||||
BgRed |
||||
BgGreen |
||||
BgYellow |
||||
BgBlue |
||||
BgMagenta |
||||
BgCyan |
||||
BgWhite |
||||
) |
||||
|
||||
// New returns a newly created color object.
|
||||
func New(value ...Attribute) *Color { |
||||
c := &Color{params: make([]Attribute, 0)} |
||||
c.Add(value...) |
||||
return c |
||||
} |
||||
|
||||
// Set sets the given parameters immediately. It will change the color of
|
||||
// output with the given SGR parameters until color.Unset() is called.
|
||||
func Set(p ...Attribute) *Color { |
||||
c := New(p...) |
||||
c.Set() |
||||
return c |
||||
} |
||||
|
||||
// Unset resets all escape attributes and clears the output. Usually should
|
||||
// be called after Set().
|
||||
func Unset() { |
||||
if NoColor { |
||||
return |
||||
} |
||||
|
||||
fmt.Fprintf(Output, "%s[%dm", escape, Reset) |
||||
} |
||||
|
||||
// Set sets the SGR sequence.
|
||||
func (c *Color) Set() *Color { |
||||
if c.isNoColorSet() { |
||||
return c |
||||
} |
||||
|
||||
fmt.Fprintf(Output, c.format()) |
||||
return c |
||||
} |
||||
|
||||
func (c *Color) unset() { |
||||
if c.isNoColorSet() { |
||||
return |
||||
} |
||||
|
||||
Unset() |
||||
} |
||||
|
||||
// Add is used to chain SGR parameters. Use as many as parameters to combine
|
||||
// and create custom color objects. Example: Add(color.FgRed, color.Underline).
|
||||
func (c *Color) Add(value ...Attribute) *Color { |
||||
c.params = append(c.params, value...) |
||||
return c |
||||
} |
||||
|
||||
func (c *Color) prepend(value Attribute) { |
||||
c.params = append(c.params, 0) |
||||
copy(c.params[1:], c.params[0:]) |
||||
c.params[0] = value |
||||
} |
||||
|
||||
// Output defines the standard output of the print functions. By default
|
||||
// os.Stdout is used.
|
||||
var Output = ansicolor.NewAnsiColorWriter(os.Stdout) |
||||
|
||||
// Print formats using the default formats for its operands and writes to
|
||||
// standard output. Spaces are added between operands when neither is a
|
||||
// string. It returns the number of bytes written and any write error
|
||||
// encountered. This is the standard fmt.Print() method wrapped with the given
|
||||
// color.
|
||||
func (c *Color) Print(a ...interface{}) (n int, err error) { |
||||
c.Set() |
||||
defer c.unset() |
||||
|
||||
return fmt.Fprint(Output, a...) |
||||
} |
||||
|
||||
// Printf formats according to a format specifier and writes to standard output.
|
||||
// It returns the number of bytes written and any write error encountered.
|
||||
// This is the standard fmt.Printf() method wrapped with the given color.
|
||||
func (c *Color) Printf(format string, a ...interface{}) (n int, err error) { |
||||
c.Set() |
||||
defer c.unset() |
||||
|
||||
return fmt.Fprintf(Output, format, a...) |
||||
} |
||||
|
||||
// Println formats using the default formats for its operands and writes to
|
||||
// standard output. Spaces are always added between operands and a newline is
|
||||
// appended. It returns the number of bytes written and any write error
|
||||
// encountered. This is the standard fmt.Print() method wrapped with the given
|
||||
// color.
|
||||
func (c *Color) Println(a ...interface{}) (n int, err error) { |
||||
c.Set() |
||||
defer c.unset() |
||||
|
||||
return fmt.Fprintln(Output, a...) |
||||
} |
||||
|
||||
// PrintFunc returns a new function that prints the passed arguments as
|
||||
// colorized with color.Print().
|
||||
func (c *Color) PrintFunc() func(a ...interface{}) { |
||||
return func(a ...interface{}) { c.Print(a...) } |
||||
} |
||||
|
||||
// PrintfFunc returns a new function that prints the passed arguments as
|
||||
// colorized with color.Printf().
|
||||
func (c *Color) PrintfFunc() func(format string, a ...interface{}) { |
||||
return func(format string, a ...interface{}) { c.Printf(format, a...) } |
||||
} |
||||
|
||||
// PrintlnFunc returns a new function that prints the passed arguments as
|
||||
// colorized with color.Println().
|
||||
func (c *Color) PrintlnFunc() func(a ...interface{}) { |
||||
return func(a ...interface{}) { c.Println(a...) } |
||||
} |
||||
|
||||
// SprintFunc returns a new function that returns colorized strings for the
|
||||
// given arguments with fmt.Sprint(). Useful to put into or mix into other
|
||||
// string. Windows users should use this in conjuction with color.Output, example:
|
||||
//
|
||||
// put := New(FgYellow).SprintFunc()
|
||||
// fmt.Fprintf(color.Output, "This is a %s", put("warning"))
|
||||
func (c *Color) SprintFunc() func(a ...interface{}) string { |
||||
return func(a ...interface{}) string { |
||||
return c.wrap(fmt.Sprint(a...)) |
||||
} |
||||
} |
||||
|
||||
// SprintfFunc returns a new function that returns colorized strings for the
|
||||
// given arguments with fmt.Sprintf(). Useful to put into or mix into other
|
||||
// string. Windows users should use this in conjuction with color.Output.
|
||||
func (c *Color) SprintfFunc() func(format string, a ...interface{}) string { |
||||
return func(format string, a ...interface{}) string { |
||||
return c.wrap(fmt.Sprintf(format, a...)) |
||||
} |
||||
} |
||||
|
||||
// SprintlnFunc returns a new function that returns colorized strings for the
|
||||
// given arguments with fmt.Sprintln(). Useful to put into or mix into other
|
||||
// string. Windows users should use this in conjuction with color.Output.
|
||||
func (c *Color) SprintlnFunc() func(a ...interface{}) string { |
||||
return func(a ...interface{}) string { |
||||
return c.wrap(fmt.Sprintln(a...)) |
||||
} |
||||
} |
||||
|
||||
// sequence returns a formated SGR sequence to be plugged into a "\x1b[...m"
|
||||
// an example output might be: "1;36" -> bold cyan
|
||||
func (c *Color) sequence() string { |
||||
format := make([]string, len(c.params)) |
||||
for i, v := range c.params { |
||||
format[i] = strconv.Itoa(int(v)) |
||||
} |
||||
|
||||
return strings.Join(format, ";") |
||||
} |
||||
|
||||
// wrap wraps the s string with the colors attributes. The string is ready to
|
||||
// be printed.
|
||||
func (c *Color) wrap(s string) string { |
||||
if c.isNoColorSet() { |
||||
return s |
||||
} |
||||
|
||||
return c.format() + s + c.unformat() |
||||
} |
||||
|
||||
func (c *Color) format() string { |
||||
return fmt.Sprintf("%s[%sm", escape, c.sequence()) |
||||
} |
||||
|
||||
func (c *Color) unformat() string { |
||||
return fmt.Sprintf("%s[%dm", escape, Reset) |
||||
} |
||||
|
||||
// DisableColor disables the color output. Useful to not change any existing
|
||||
// code and still being able to output. Can be used for flags like
|
||||
// "--no-color". To enable back use EnableColor() method.
|
||||
func (c *Color) DisableColor() { |
||||
c.noColor = boolPtr(true) |
||||
} |
||||
|
||||
// EnableColor enables the color output. Use it in conjuction with
|
||||
// DisableColor(). Otherwise this method has no side effects.
|
||||
func (c *Color) EnableColor() { |
||||
c.noColor = boolPtr(false) |
||||
} |
||||
|
||||
func (c *Color) isNoColorSet() bool { |
||||
// check first if we have user setted action
|
||||
if c.noColor != nil { |
||||
return *c.noColor |
||||
} |
||||
|
||||
// if not return the global option, which is disabled by default
|
||||
return NoColor |
||||
} |
||||
|
||||
func boolPtr(v bool) *bool { |
||||
return &v |
||||
} |
||||
|
||||
// Black is an convenient helper function to print with black foreground. A
|
||||
// newline is appended to format by default.
|
||||
func Black(format string, a ...interface{}) { printColor(format, FgBlack, a...) } |
||||
|
||||
// Red is an convenient helper function to print with red foreground. A
|
||||
// newline is appended to format by default.
|
||||
func Red(format string, a ...interface{}) { printColor(format, FgRed, a...) } |
||||
|
||||
// Green is an convenient helper function to print with green foreground. A
|
||||
// newline is appended to format by default.
|
||||
func Green(format string, a ...interface{}) { printColor(format, FgGreen, a...) } |
||||
|
||||
// Yellow is an convenient helper function to print with yellow foreground.
|
||||
// A newline is appended to format by default.
|
||||
func Yellow(format string, a ...interface{}) { printColor(format, FgYellow, a...) } |
||||
|
||||
// Blue is an convenient helper function to print with blue foreground. A
|
||||
// newline is appended to format by default.
|
||||
func Blue(format string, a ...interface{}) { printColor(format, FgBlue, a...) } |
||||
|
||||
// Magenta is an convenient helper function to print with magenta foreground.
|
||||
// A newline is appended to format by default.
|
||||
func Magenta(format string, a ...interface{}) { printColor(format, FgMagenta, a...) } |
||||
|
||||
// Cyan is an convenient helper function to print with cyan foreground. A
|
||||
// newline is appended to format by default.
|
||||
func Cyan(format string, a ...interface{}) { printColor(format, FgCyan, a...) } |
||||
|
||||
// White is an convenient helper function to print with white foreground. A
|
||||
// newline is appended to format by default.
|
||||
func White(format string, a ...interface{}) { printColor(format, FgWhite, a...) } |
||||
|
||||
func printColor(format string, p Attribute, a ...interface{}) { |
||||
if !strings.HasSuffix(format, "\n") { |
||||
format += "\n" |
||||
} |
||||
|
||||
c := &Color{params: []Attribute{p}} |
||||
c.Printf(format, a...) |
||||
} |
||||
|
||||
// BlackString is an convenient helper function to return a string with black
|
||||
// foreground.
|
||||
func BlackString(format string, a ...interface{}) string { |
||||
return New(FgBlack).SprintfFunc()(format, a...) |
||||
} |
||||
|
||||
// RedString is an convenient helper function to return a string with red
|
||||
// foreground.
|
||||
func RedString(format string, a ...interface{}) string { |
||||
return New(FgRed).SprintfFunc()(format, a...) |
||||
} |
||||
|
||||
// GreenString is an convenient helper function to return a string with green
|
||||
// foreground.
|
||||
func GreenString(format string, a ...interface{}) string { |
||||
return New(FgGreen).SprintfFunc()(format, a...) |
||||
} |
||||
|
||||
// YellowString is an convenient helper function to return a string with yellow
|
||||
// foreground.
|
||||
func YellowString(format string, a ...interface{}) string { |
||||
return New(FgYellow).SprintfFunc()(format, a...) |
||||
} |
||||
|
||||
// BlueString is an convenient helper function to return a string with blue
|
||||
// foreground.
|
||||
func BlueString(format string, a ...interface{}) string { |
||||
return New(FgBlue).SprintfFunc()(format, a...) |
||||
} |
||||
|
||||
// MagentaString is an convenient helper function to return a string with magenta
|
||||
// foreground.
|
||||
func MagentaString(format string, a ...interface{}) string { |
||||
return New(FgMagenta).SprintfFunc()(format, a...) |
||||
} |
||||
|
||||
// CyanString is an convenient helper function to return a string with cyan
|
||||
// foreground.
|
||||
func CyanString(format string, a ...interface{}) string { |
||||
return New(FgCyan).SprintfFunc()(format, a...) |
||||
} |
||||
|
||||
// WhiteString is an convenient helper function to return a string with white
|
||||
// foreground.
|
||||
func WhiteString(format string, a ...interface{}) string { |
||||
return New(FgWhite).SprintfFunc()(format, a...) |
||||
} |
@ -0,0 +1,176 @@ |
||||
package color |
||||
|
||||
import ( |
||||
"bytes" |
||||
"fmt" |
||||
"os" |
||||
"testing" |
||||
|
||||
"github.com/shiena/ansicolor" |
||||
) |
||||
|
||||
// Testing colors is kinda different. First we test for given colors and their
|
||||
// escaped formatted results. Next we create some visual tests to be tested.
|
||||
// Each visual test includes the color name to be compared.
|
||||
func TestColor(t *testing.T) { |
||||
rb := new(bytes.Buffer) |
||||
Output = rb |
||||
|
||||
testColors := []struct { |
||||
text string |
||||
code Attribute |
||||
}{ |
||||
{text: "black", code: FgBlack}, |
||||
{text: "red", code: FgRed}, |
||||
{text: "green", code: FgGreen}, |
||||
{text: "yellow", code: FgYellow}, |
||||
{text: "blue", code: FgBlue}, |
||||
{text: "magent", code: FgMagenta}, |
||||
{text: "cyan", code: FgCyan}, |
||||
{text: "white", code: FgWhite}, |
||||
} |
||||
|
||||
for _, c := range testColors { |
||||
New(c.code).Print(c.text) |
||||
|
||||
line, _ := rb.ReadString('\n') |
||||
scannedLine := fmt.Sprintf("%q", line) |
||||
colored := fmt.Sprintf("\x1b[%dm%s\x1b[0m", c.code, c.text) |
||||
escapedForm := fmt.Sprintf("%q", colored) |
||||
|
||||
fmt.Printf("%s\t: %s\n", c.text, line) |
||||
|
||||
if scannedLine != escapedForm { |
||||
t.Errorf("Expecting %s, got '%s'\n", escapedForm, scannedLine) |
||||
} |
||||
} |
||||
} |
||||
|
||||
func TestNoColor(t *testing.T) { |
||||
rb := new(bytes.Buffer) |
||||
Output = rb |
||||
|
||||
testColors := []struct { |
||||
text string |
||||
code Attribute |
||||
}{ |
||||
{text: "black", code: FgBlack}, |
||||
{text: "red", code: FgRed}, |
||||
{text: "green", code: FgGreen}, |
||||
{text: "yellow", code: FgYellow}, |
||||
{text: "blue", code: FgBlue}, |
||||
{text: "magent", code: FgMagenta}, |
||||
{text: "cyan", code: FgCyan}, |
||||
{text: "white", code: FgWhite}, |
||||
} |
||||
|
||||
for _, c := range testColors { |
||||
p := New(c.code) |
||||
p.DisableColor() |
||||
p.Print(c.text) |
||||
|
||||
line, _ := rb.ReadString('\n') |
||||
if line != c.text { |
||||
t.Errorf("Expecting %s, got '%s'\n", c.text, line) |
||||
} |
||||
} |
||||
|
||||
// global check
|
||||
NoColor = true |
||||
defer func() { |
||||
NoColor = false |
||||
}() |
||||
for _, c := range testColors { |
||||
p := New(c.code) |
||||
p.Print(c.text) |
||||
|
||||
line, _ := rb.ReadString('\n') |
||||
if line != c.text { |
||||
t.Errorf("Expecting %s, got '%s'\n", c.text, line) |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
func TestColorVisual(t *testing.T) { |
||||
// First Visual Test
|
||||
fmt.Println("") |
||||
Output = ansicolor.NewAnsiColorWriter(os.Stdout) |
||||
|
||||
New(FgRed).Printf("red\t") |
||||
New(BgRed).Print(" ") |
||||
New(FgRed, Bold).Println(" red") |
||||
|
||||
New(FgGreen).Printf("green\t") |
||||
New(BgGreen).Print(" ") |
||||
New(FgGreen, Bold).Println(" green") |
||||
|
||||
New(FgYellow).Printf("yellow\t") |
||||
New(BgYellow).Print(" ") |
||||
New(FgYellow, Bold).Println(" yellow") |
||||
|
||||
New(FgBlue).Printf("blue\t") |
||||
New(BgBlue).Print(" ") |
||||
New(FgBlue, Bold).Println(" blue") |
||||
|
||||
New(FgMagenta).Printf("magenta\t") |
||||
New(BgMagenta).Print(" ") |
||||
New(FgMagenta, Bold).Println(" magenta") |
||||
|
||||
New(FgCyan).Printf("cyan\t") |
||||
New(BgCyan).Print(" ") |
||||
New(FgCyan, Bold).Println(" cyan") |
||||
|
||||
New(FgWhite).Printf("white\t") |
||||
New(BgWhite).Print(" ") |
||||
New(FgWhite, Bold).Println(" white") |
||||
fmt.Println("") |
||||
|
||||
// Second Visual test
|
||||
Black("black") |
||||
Red("red") |
||||
Green("green") |
||||
Yellow("yellow") |
||||
Blue("blue") |
||||
Magenta("magenta") |
||||
Cyan("cyan") |
||||
White("white") |
||||
|
||||
// Third visual test
|
||||
fmt.Println() |
||||
Set(FgBlue) |
||||
fmt.Println("is this blue?") |
||||
Unset() |
||||
|
||||
Set(FgMagenta) |
||||
fmt.Println("and this magenta?") |
||||
Unset() |
||||
|
||||
// Fourth Visual test
|
||||
fmt.Println() |
||||
blue := New(FgBlue).PrintlnFunc() |
||||
blue("blue text with custom print func") |
||||
|
||||
red := New(FgRed).PrintfFunc() |
||||
red("red text with a printf func: %d\n", 123) |
||||
|
||||
put := New(FgYellow).SprintFunc() |
||||
warn := New(FgRed).SprintFunc() |
||||
|
||||
fmt.Fprintf(Output, "this is a %s and this is %s.\n", put("warning"), warn("error")) |
||||
|
||||
info := New(FgWhite, BgGreen).SprintFunc() |
||||
fmt.Fprintf(Output, "this %s rocks!\n", info("package")) |
||||
|
||||
// Fifth Visual Test
|
||||
fmt.Println() |
||||
|
||||
fmt.Fprintln(Output, BlackString("black")) |
||||
fmt.Fprintln(Output, RedString("red")) |
||||
fmt.Fprintln(Output, GreenString("green")) |
||||
fmt.Fprintln(Output, YellowString("yellow")) |
||||
fmt.Fprintln(Output, BlueString("blue")) |
||||
fmt.Fprintln(Output, MagentaString("magenta")) |
||||
fmt.Fprintln(Output, CyanString("cyan")) |
||||
fmt.Fprintln(Output, WhiteString("white")) |
||||
} |
@ -0,0 +1,114 @@ |
||||
/* |
||||
Package color is an ANSI color package to output colorized or SGR defined |
||||
output to the standard output. The API can be used in several way, pick one |
||||
that suits you. |
||||
|
||||
Use simple and default helper functions with predefined foreground colors: |
||||
|
||||
color.Cyan("Prints text in cyan.") |
||||
|
||||
// a newline will be appended automatically
|
||||
color.Blue("Prints %s in blue.", "text") |
||||
|
||||
// More default foreground colors..
|
||||
color.Red("We have red") |
||||
color.Yellow("Yellow color too!") |
||||
color.Magenta("And many others ..") |
||||
|
||||
However there are times where custom color mixes are required. Below are some |
||||
examples to create custom color objects and use the print functions of each |
||||
separate color object. |
||||
|
||||
// Create a new color object
|
||||
c := color.New(color.FgCyan).Add(color.Underline) |
||||
c.Println("Prints cyan text with an underline.") |
||||
|
||||
// Or just add them to New()
|
||||
d := color.New(color.FgCyan, color.Bold) |
||||
d.Printf("This prints bold cyan %s\n", "too!.") |
||||
|
||||
|
||||
// Mix up foreground and background colors, create new mixes!
|
||||
red := color.New(color.FgRed) |
||||
|
||||
boldRed := red.Add(color.Bold) |
||||
boldRed.Println("This will print text in bold red.") |
||||
|
||||
whiteBackground := red.Add(color.BgWhite) |
||||
whiteBackground.Println("Red text with White background.") |
||||
|
||||
|
||||
You can create PrintXxx functions to simplify even more: |
||||
|
||||
// Create a custom print function for convenient
|
||||
red := color.New(color.FgRed).PrintfFunc() |
||||
red("warning") |
||||
red("error: %s", err) |
||||
|
||||
// Mix up multiple attributes
|
||||
notice := color.New(color.Bold, color.FgGreen).PrintlnFunc() |
||||
notice("don't forget this...") |
||||
|
||||
|
||||
Or create SprintXxx functions to mix strings with other non-colorized strings: |
||||
|
||||
yellow := New(FgYellow).SprintFunc() |
||||
red := New(FgRed).SprintFunc() |
||||
|
||||
fmt.Printf("this is a %s and this is %s.\n", yellow("warning"), red("error")) |
||||
|
||||
info := New(FgWhite, BgGreen).SprintFunc() |
||||
fmt.Printf("this %s rocks!\n", info("package")) |
||||
|
||||
Windows support is enabled by default. All Print functions works as intended. |
||||
However only for color.SprintXXX functions, user should use fmt.FprintXXX and |
||||
set the output to color.Output: |
||||
|
||||
fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS")) |
||||
|
||||
info := New(FgWhite, BgGreen).SprintFunc() |
||||
fmt.Fprintf(color.Output, "this %s rocks!\n", info("package")) |
||||
|
||||
Using with existing code is possible. Just use the Set() method to set the |
||||
standard output to the given parameters. That way a rewrite of an existing |
||||
code is not required. |
||||
|
||||
// Use handy standard colors.
|
||||
color.Set(color.FgYellow) |
||||
|
||||
fmt.Println("Existing text will be now in Yellow") |
||||
fmt.Printf("This one %s\n", "too") |
||||
|
||||
color.Unset() // don't forget to unset
|
||||
|
||||
// You can mix up parameters
|
||||
color.Set(color.FgMagenta, color.Bold) |
||||
defer color.Unset() // use it in your function
|
||||
|
||||
fmt.Println("All text will be now bold magenta.") |
||||
|
||||
There might be a case where you want to disable color output (for example to |
||||
pipe the standard output of your app to somewhere else). `Color` has support to |
||||
disable colors both globally and for single color definition. For example |
||||
suppose you have a CLI app and a `--no-color` bool flag. You can easily disable |
||||
the color output with: |
||||
|
||||
var flagNoColor = flag.Bool("no-color", false, "Disable color output") |
||||
|
||||
if *flagNoColor { |
||||
color.NoColor = true // disables colorized output
|
||||
} |
||||
|
||||
It also has support for single color definitions (local). You can |
||||
disable/enable color output on the fly: |
||||
|
||||
c := color.New(color.FgCyan) |
||||
c.Println("Prints cyan text") |
||||
|
||||
c.DisableColor() |
||||
c.Println("This is printed without any color") |
||||
|
||||
c.EnableColor() |
||||
c.Println("This prints again cyan...") |
||||
*/ |
||||
package color |
@ -1,42 +0,0 @@ |
||||
# go-colorable |
||||
|
||||
Colorable writer for windows. |
||||
|
||||
For example, most of logger packages doesn't show colors on windows. (I know we can do it with ansicon. But I don't want.) |
||||
This package is possible to handle escape sequence for ansi color on windows. |
||||
|
||||
## Too Bad! |
||||
|
||||
![](https://raw.githubusercontent.com/mattn/go-colorable/gh-pages/bad.png) |
||||
|
||||
|
||||
## So Good! |
||||
|
||||
![](https://raw.githubusercontent.com/mattn/go-colorable/gh-pages/good.png) |
||||
|
||||
## Usage |
||||
|
||||
```go |
||||
logrus.SetOutput(colorable.NewColorableStdout()) |
||||
|
||||
logrus.Info("succeeded") |
||||
logrus.Warn("not correct") |
||||
logrus.Error("something error") |
||||
logrus.Fatal("panic") |
||||
``` |
||||
|
||||
You can compile above code on non-windows OSs. |
||||
|
||||
## Installation |
||||
|
||||
``` |
||||
$ go get github.com/mattn/go-colorable |
||||
``` |
||||
|
||||
# License |
||||
|
||||
MIT |
||||
|
||||
# Author |
||||
|
||||
Yasuhiro Matsumoto (a.k.a mattn) |
@ -1,16 +0,0 @@ |
||||
// +build !windows
|
||||
|
||||
package colorable |
||||
|
||||
import ( |
||||
"io" |
||||
"os" |
||||
) |
||||
|
||||
func NewColorableStdout() io.Writer { |
||||
return os.Stdout |
||||
} |
||||
|
||||
func NewColorableStderr() io.Writer { |
||||
return os.Stderr |
||||
} |
@ -1,594 +0,0 @@ |
||||
package colorable |
||||
|
||||
import ( |
||||
"bytes" |
||||
"fmt" |
||||
"io" |
||||
"os" |
||||
"strconv" |
||||
"strings" |
||||
"syscall" |
||||
"unsafe" |
||||
|
||||
"github.com/mattn/go-isatty" |
||||
) |
||||
|
||||
const ( |
||||
foregroundBlue = 0x1 |
||||
foregroundGreen = 0x2 |
||||
foregroundRed = 0x4 |
||||
foregroundIntensity = 0x8 |
||||
foregroundMask = (foregroundRed | foregroundBlue | foregroundGreen | foregroundIntensity) |
||||
backgroundBlue = 0x10 |
||||
backgroundGreen = 0x20 |
||||
backgroundRed = 0x40 |
||||
backgroundIntensity = 0x80 |
||||
backgroundMask = (backgroundRed | backgroundBlue | backgroundGreen | backgroundIntensity) |
||||
) |
||||
|
||||
type wchar uint16 |
||||
type short int16 |
||||
type dword uint32 |
||||
type word uint16 |
||||
|
||||
type coord struct { |
||||
x short |
||||
y short |
||||
} |
||||
|
||||
type smallRect struct { |
||||
left short |
||||
top short |
||||
right short |
||||
bottom short |
||||
} |
||||
|
||||
type consoleScreenBufferInfo struct { |
||||
size coord |
||||
cursorPosition coord |
||||
attributes word |
||||
window smallRect |
||||
maximumWindowSize coord |
||||
} |
||||
|
||||
var ( |
||||
kernel32 = syscall.NewLazyDLL("kernel32.dll") |
||||
procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") |
||||
procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute") |
||||
) |
||||
|
||||
type Writer struct { |
||||
out io.Writer |
||||
handle syscall.Handle |
||||
lastbuf bytes.Buffer |
||||
oldattr word |
||||
} |
||||
|
||||
func NewColorableStdout() io.Writer { |
||||
var csbi consoleScreenBufferInfo |
||||
out := os.Stdout |
||||
if !isatty.IsTerminal(out.Fd()) { |
||||
return out |
||||
} |
||||
handle := syscall.Handle(out.Fd()) |
||||
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) |
||||
return &Writer{out: out, handle: handle, oldattr: csbi.attributes} |
||||
} |
||||
|
||||
func NewColorableStderr() io.Writer { |
||||
var csbi consoleScreenBufferInfo |
||||
out := os.Stderr |
||||
if !isatty.IsTerminal(out.Fd()) { |
||||
return out |
||||
} |
||||
handle := syscall.Handle(out.Fd()) |
||||
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) |
||||
return &Writer{out: out, handle: handle, oldattr: csbi.attributes} |
||||
} |
||||
|
||||
var color256 = map[int]int{ |
||||
0: 0x000000, |
||||
1: 0x800000, |
||||
2: 0x008000, |
||||
3: 0x808000, |
||||
4: 0x000080, |
||||
5: 0x800080, |
||||
6: 0x008080, |
||||
7: 0xc0c0c0, |
||||
8: 0x808080, |
||||
9: 0xff0000, |
||||
10: 0x00ff00, |
||||
11: 0xffff00, |
||||
12: 0x0000ff, |
||||
13: 0xff00ff, |
||||
14: 0x00ffff, |
||||
15: 0xffffff, |
||||
16: 0x000000, |
||||
17: 0x00005f, |
||||
18: 0x000087, |
||||
19: 0x0000af, |
||||
20: 0x0000d7, |
||||
21: 0x0000ff, |
||||
22: 0x005f00, |
||||
23: 0x005f5f, |
||||
24: 0x005f87, |
||||
25: 0x005faf, |
||||
26: 0x005fd7, |
||||
27: 0x005fff, |
||||
28: 0x008700, |
||||
29: 0x00875f, |
||||
30: 0x008787, |
||||
31: 0x0087af, |
||||
32: 0x0087d7, |
||||
33: 0x0087ff, |
||||
34: 0x00af00, |
||||
35: 0x00af5f, |
||||
36: 0x00af87, |
||||
37: 0x00afaf, |
||||
38: 0x00afd7, |
||||
39: 0x00afff, |
||||
40: 0x00d700, |
||||
41: 0x00d75f, |
||||
42: 0x00d787, |
||||
43: 0x00d7af, |
||||
44: 0x00d7d7, |
||||
45: 0x00d7ff, |
||||
46: 0x00ff00, |
||||
47: 0x00ff5f, |
||||
48: 0x00ff87, |
||||
49: 0x00ffaf, |
||||
50: 0x00ffd7, |
||||
51: 0x00ffff, |
||||
52: 0x5f0000, |
||||
53: 0x5f005f, |
||||
54: 0x5f0087, |
||||
55: 0x5f00af, |
||||
56: 0x5f00d7, |
||||
57: 0x5f00ff, |
||||
58: 0x5f5f00, |
||||
59: 0x5f5f5f, |
||||
60: 0x5f5f87, |
||||
61: 0x5f5faf, |
||||
62: 0x5f5fd7, |
||||
63: 0x5f5fff, |
||||
64: 0x5f8700, |
||||
65: 0x5f875f, |
||||
66: 0x5f8787, |
||||
67: 0x5f87af, |
||||
68: 0x5f87d7, |
||||
69: 0x5f87ff, |
||||
70: 0x5faf00, |
||||
71: 0x5faf5f, |
||||
72: 0x5faf87, |
||||
73: 0x5fafaf, |
||||
74: 0x5fafd7, |
||||
75: 0x5fafff, |
||||
76: 0x5fd700, |
||||
77: 0x5fd75f, |
||||
78: 0x5fd787, |
||||
79: 0x5fd7af, |
||||
80: 0x5fd7d7, |
||||
81: 0x5fd7ff, |
||||
82: 0x5fff00, |
||||
83: 0x5fff5f, |
||||
84: 0x5fff87, |
||||
85: 0x5fffaf, |
||||
86: 0x5fffd7, |
||||
87: 0x5fffff, |
||||
88: 0x870000, |
||||
89: 0x87005f, |
||||
90: 0x870087, |
||||
91: 0x8700af, |
||||
92: 0x8700d7, |
||||
93: 0x8700ff, |
||||
94: 0x875f00, |
||||
95: 0x875f5f, |
||||
96: 0x875f87, |
||||
97: 0x875faf, |
||||
98: 0x875fd7, |
||||
99: 0x875fff, |
||||
100: 0x878700, |
||||
101: 0x87875f, |
||||
102: 0x878787, |
||||
103: 0x8787af, |
||||
104: 0x8787d7, |
||||
105: 0x8787ff, |
||||
106: 0x87af00, |
||||
107: 0x87af5f, |
||||
108: 0x87af87, |
||||
109: 0x87afaf, |
||||
110: 0x87afd7, |
||||
111: 0x87afff, |
||||
112: 0x87d700, |
||||
113: 0x87d75f, |
||||
114: 0x87d787, |
||||
115: 0x87d7af, |
||||
116: 0x87d7d7, |
||||
117: 0x87d7ff, |
||||
118: 0x87ff00, |
||||
119: 0x87ff5f, |
||||
120: 0x87ff87, |
||||
121: 0x87ffaf, |
||||
122: 0x87ffd7, |
||||
123: 0x87ffff, |
||||
124: 0xaf0000, |
||||
125: 0xaf005f, |
||||
126: 0xaf0087, |
||||
127: 0xaf00af, |
||||
128: 0xaf00d7, |
||||
129: 0xaf00ff, |
||||
130: 0xaf5f00, |
||||
131: 0xaf5f5f, |
||||
132: 0xaf5f87, |
||||
133: 0xaf5faf, |
||||
134: 0xaf5fd7, |
||||
135: 0xaf5fff, |
||||
136: 0xaf8700, |
||||
137: 0xaf875f, |
||||
138: 0xaf8787, |
||||
139: 0xaf87af, |
||||
140: 0xaf87d7, |
||||
141: 0xaf87ff, |
||||
142: 0xafaf00, |
||||
143: 0xafaf5f, |
||||
144: 0xafaf87, |
||||
145: 0xafafaf, |
||||
146: 0xafafd7, |
||||
147: 0xafafff, |
||||
148: 0xafd700, |
||||
149: 0xafd75f, |
||||
150: 0xafd787, |
||||
151: 0xafd7af, |
||||
152: 0xafd7d7, |
||||
153: 0xafd7ff, |
||||
154: 0xafff00, |
||||
155: 0xafff5f, |
||||
156: 0xafff87, |
||||
157: 0xafffaf, |
||||
158: 0xafffd7, |
||||
159: 0xafffff, |
||||
160: 0xd70000, |
||||
161: 0xd7005f, |
||||
162: 0xd70087, |
||||
163: 0xd700af, |
||||
164: 0xd700d7, |
||||
165: 0xd700ff, |
||||
166: 0xd75f00, |
||||
167: 0xd75f5f, |
||||
168: 0xd75f87, |
||||
169: 0xd75faf, |
||||
170: 0xd75fd7, |
||||
171: 0xd75fff, |
||||
172: 0xd78700, |
||||
173: 0xd7875f, |
||||
174: 0xd78787, |
||||
175: 0xd787af, |
||||
176: 0xd787d7, |
||||
177: 0xd787ff, |
||||
178: 0xd7af00, |
||||
179: 0xd7af5f, |
||||
180: 0xd7af87, |
||||
181: 0xd7afaf, |
||||
182: 0xd7afd7, |
||||
183: 0xd7afff, |
||||
184: 0xd7d700, |
||||
185: 0xd7d75f, |
||||
186: 0xd7d787, |
||||
187: 0xd7d7af, |
||||
188: 0xd7d7d7, |
||||
189: 0xd7d7ff, |
||||
190: 0xd7ff00, |
||||
191: 0xd7ff5f, |
||||
192: 0xd7ff87, |
||||
193: 0xd7ffaf, |
||||
194: 0xd7ffd7, |
||||
195: 0xd7ffff, |
||||
196: 0xff0000, |
||||
197: 0xff005f, |
||||
198: 0xff0087, |
||||
199: 0xff00af, |
||||
200: 0xff00d7, |
||||
201: 0xff00ff, |
||||
202: 0xff5f00, |
||||
203: 0xff5f5f, |
||||
204: 0xff5f87, |
||||
205: 0xff5faf, |
||||
206: 0xff5fd7, |
||||
207: 0xff5fff, |
||||
208: 0xff8700, |
||||
209: 0xff875f, |
||||
210: 0xff8787, |
||||
211: 0xff87af, |
||||
212: 0xff87d7, |
||||
213: 0xff87ff, |
||||
214: 0xffaf00, |
||||
215: 0xffaf5f, |
||||
216: 0xffaf87, |
||||
217: 0xffafaf, |
||||
218: 0xffafd7, |
||||
219: 0xffafff, |
||||
220: 0xffd700, |
||||
221: 0xffd75f, |
||||
222: 0xffd787, |
||||
223: 0xffd7af, |
||||
224: 0xffd7d7, |
||||
225: 0xffd7ff, |
||||
226: 0xffff00, |
||||
227: 0xffff5f, |
||||
228: 0xffff87, |
||||
229: 0xffffaf, |
||||
230: 0xffffd7, |
||||
231: 0xffffff, |
||||
232: 0x080808, |
||||
233: 0x121212, |
||||
234: 0x1c1c1c, |
||||
235: 0x262626, |
||||
236: 0x303030, |
||||
237: 0x3a3a3a, |
||||
238: 0x444444, |
||||
239: 0x4e4e4e, |
||||
240: 0x585858, |
||||
241: 0x626262, |
||||
242: 0x6c6c6c, |
||||
243: 0x767676, |
||||
244: 0x808080, |
||||
245: 0x8a8a8a, |
||||
246: 0x949494, |
||||
247: 0x9e9e9e, |
||||
248: 0xa8a8a8, |
||||
249: 0xb2b2b2, |
||||
250: 0xbcbcbc, |
||||
251: 0xc6c6c6, |
||||
252: 0xd0d0d0, |
||||
253: 0xdadada, |
||||
254: 0xe4e4e4, |
||||
255: 0xeeeeee, |
||||
} |
||||
|
||||
func (w *Writer) Write(data []byte) (n int, err error) { |
||||
var csbi consoleScreenBufferInfo |
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) |
||||
|
||||
er := bytes.NewBuffer(data) |
||||
loop: |
||||
for { |
||||
r1, _, err := procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) |
||||
if r1 == 0 { |
||||
break loop |
||||
} |
||||
|
||||
c1, _, err := er.ReadRune() |
||||
if err != nil { |
||||
break loop |
||||
} |
||||
if c1 != 0x1b { |
||||
fmt.Fprint(w.out, string(c1)) |
||||
continue |
||||
} |
||||
c2, _, err := er.ReadRune() |
||||
if err != nil { |
||||
w.lastbuf.WriteRune(c1) |
||||
break loop |
||||
} |
||||
if c2 != 0x5b { |
||||
w.lastbuf.WriteRune(c1) |
||||
w.lastbuf.WriteRune(c2) |
||||
continue |
||||
} |
||||
|
||||
var buf bytes.Buffer |
||||
var m rune |
||||
for { |
||||
c, _, err := er.ReadRune() |
||||
if err != nil { |
||||
w.lastbuf.WriteRune(c1) |
||||
w.lastbuf.WriteRune(c2) |
||||
w.lastbuf.Write(buf.Bytes()) |
||||
break loop |
||||
} |
||||
if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' { |
||||
m = c |
||||
break |
||||
} |
||||
buf.Write([]byte(string(c))) |
||||
} |
||||
|
||||
switch m { |
||||
case 'm': |
||||
attr := csbi.attributes |
||||
cs := buf.String() |
||||
if cs == "" { |
||||
procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(w.oldattr)) |
||||
continue |
||||
} |
||||
token := strings.Split(cs, ";") |
||||
for i, ns := range token { |
||||
if n, err = strconv.Atoi(ns); err == nil { |
||||
switch { |
||||
case n == 0 || n == 100: |
||||
attr = w.oldattr |
||||
case 1 <= n && n <= 5: |
||||
attr |= foregroundIntensity |
||||
case n == 7: |
||||
attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4) |
||||
case 22 == n || n == 25 || n == 25: |
||||
attr |= foregroundIntensity |
||||
case n == 27: |
||||
attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4) |
||||
case 30 <= n && n <= 37: |
||||
attr = (attr & backgroundMask) |
||||
if (n-30)&1 != 0 { |
||||
attr |= foregroundRed |
||||
} |
||||
if (n-30)&2 != 0 { |
||||
attr |= foregroundGreen |
||||
} |
||||
if (n-30)&4 != 0 { |
||||
attr |= foregroundBlue |
||||
} |
||||
case n == 38: // set foreground color.
|
||||
if i < len(token)-2 && token[i+1] == "5" { |
||||
if n256, err := strconv.Atoi(token[i+2]); err == nil { |
||||
if n256foreAttr == nil { |
||||
n256setup() |
||||
} |
||||
attr &= backgroundMask |
||||
attr |= n256foreAttr[n256] |
||||
i += 2 |
||||
} |
||||
} else { |
||||
attr = attr & (w.oldattr & backgroundMask) |
||||
} |
||||
case n == 39: // reset foreground color.
|
||||
attr &= backgroundMask |
||||
attr |= w.oldattr & foregroundMask |
||||
case 40 <= n && n <= 47: |
||||
attr = (attr & foregroundMask) |
||||
if (n-40)&1 != 0 { |
||||
attr |= backgroundRed |
||||
} |
||||
if (n-40)&2 != 0 { |
||||
attr |= backgroundGreen |
||||
} |
||||
if (n-40)&4 != 0 { |
||||
attr |= backgroundBlue |
||||
} |
||||
case n == 48: // set background color.
|
||||
if i < len(token)-2 && token[i+1] == "5" { |
||||
if n256, err := strconv.Atoi(token[i+2]); err == nil { |
||||
if n256backAttr == nil { |
||||
n256setup() |
||||
} |
||||
attr &= foregroundMask |
||||
attr |= n256backAttr[n256] |
||||
i += 2 |
||||
} |
||||
} else { |
||||
attr = attr & (w.oldattr & foregroundMask) |
||||
} |
||||
case n == 49: // reset foreground color.
|
||||
attr &= foregroundMask |
||||
attr |= w.oldattr & backgroundMask |
||||
} |
||||
procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(attr)) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
return len(data) - w.lastbuf.Len(), nil |
||||
} |
||||
|
||||
type consoleColor struct { |
||||
red bool |
||||
green bool |
||||
blue bool |
||||
intensity bool |
||||
} |
||||
|
||||
func minmax3(a, b, c int) (min, max int) { |
||||
if a < b { |
||||
if b < c { |
||||
return a, c |
||||
} else if a < c { |
||||
return a, b |
||||
} else { |
||||
return c, b |
||||
} |
||||
} else { |
||||
if a < c { |
||||
return b, c |
||||
} else if b < c { |
||||
return b, a |
||||
} else { |
||||
return c, a |
||||
} |
||||
} |
||||
} |
||||
|
||||
func toConsoleColor(rgb int) (c consoleColor) { |
||||
r, g, b := (rgb&0xFF0000)>>16, (rgb&0x00FF00)>>8, rgb&0x0000FF |
||||
min, max := minmax3(r, g, b) |
||||
a := (min + max) / 2 |
||||
if r < 128 && g < 128 && b < 128 { |
||||
if r >= a { |
||||
c.red = true |
||||
} |
||||
if g >= a { |
||||
c.green = true |
||||
} |
||||
if b >= a { |
||||
c.blue = true |
||||
} |
||||
// non-intensed white is lighter than intensed black, so swap those.
|
||||
if c.red && c.green && c.blue { |
||||
c.red, c.green, c.blue = false, false, false |
||||
c.intensity = true |
||||
} |
||||
} else { |
||||
if min < 128 { |
||||
min = 128 |
||||
a = (min + max) / 2 |
||||
} |
||||
if r >= a { |
||||
c.red = true |
||||
} |
||||
if g >= a { |
||||
c.green = true |
||||
} |
||||
if b >= a { |
||||
c.blue = true |
||||
} |
||||
c.intensity = true |
||||
// intensed black is darker than non-intensed white, so swap those.
|
||||
if !c.red && !c.green && !c.blue { |
||||
c.red, c.green, c.blue = true, true, true |
||||
c.intensity = false |
||||
} |
||||
} |
||||
return c |
||||
} |
||||
|
||||
func (c consoleColor) foregroundAttr() (attr word) { |
||||
if c.red { |
||||
attr |= foregroundRed |
||||
} |
||||
if c.green { |
||||
attr |= foregroundGreen |
||||
} |
||||
if c.blue { |
||||
attr |= foregroundBlue |
||||
} |
||||
if c.intensity { |
||||
attr |= foregroundIntensity |
||||
} |
||||
return |
||||
} |
||||
|
||||
func (c consoleColor) backgroundAttr() (attr word) { |
||||
if c.red { |
||||
attr |= backgroundRed |
||||
} |
||||
if c.green { |
||||
attr |= backgroundGreen |
||||
} |
||||
if c.blue { |
||||
attr |= backgroundBlue |
||||
} |
||||
if c.intensity { |
||||
attr |= backgroundIntensity |
||||
} |
||||
return |
||||
} |
||||
|
||||
var n256foreAttr []word |
||||
var n256backAttr []word |
||||
|
||||
func n256setup() { |
||||
n256foreAttr = make([]word, 256) |
||||
n256backAttr = make([]word, 256) |
||||
for i, rgb := range color256 { |
||||
c := toConsoleColor(rgb) |
||||
n256foreAttr[i] = c.foregroundAttr() |
||||
n256backAttr[i] = c.backgroundAttr() |
||||
} |
||||
} |
@ -0,0 +1,27 @@ |
||||
# Created by http://www.gitignore.io |
||||
|
||||
### Go ### |
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects) |
||||
*.o |
||||
*.a |
||||
*.so |
||||
|
||||
# Folders |
||||
_obj |
||||
_test |
||||
|
||||
# Architecture specific extensions/prefixes |
||||
*.[568vq] |
||||
[568vq].out |
||||
|
||||
*.cgo1.go |
||||
*.cgo2.c |
||||
_cgo_defun.c |
||||
_cgo_gotypes.go |
||||
_cgo_export.* |
||||
|
||||
_testmain.go |
||||
|
||||
*.exe |
||||
*.test |
||||
|
@ -0,0 +1,21 @@ |
||||
The MIT License (MIT) |
||||
|
||||
Copyright (c) [2014] [shiena] |
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
of this software and associated documentation files (the "Software"), to deal |
||||
in the Software without restriction, including without limitation the rights |
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
copies of the Software, and to permit persons to whom the Software is |
||||
furnished to do so, subject to the following conditions: |
||||
|
||||
The above copyright notice and this permission notice shall be included in all |
||||
copies or substantial portions of the Software. |
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
SOFTWARE. |
@ -0,0 +1,100 @@ |
||||
[![GoDoc](https://godoc.org/github.com/shiena/ansicolor?status.svg)](https://godoc.org/github.com/shiena/ansicolor) |
||||
|
||||
# ansicolor |
||||
|
||||
Ansicolor library provides color console in Windows as ANSICON for Golang. |
||||
|
||||
## Features |
||||
|
||||
|Escape sequence|Text attributes| |
||||
|---------------|----| |
||||
|\x1b[0m|All attributes off(color at startup)| |
||||
|\x1b[1m|Bold on(enable foreground intensity)| |
||||
|\x1b[4m|Underline on| |
||||
|\x1b[5m|Blink on(enable background intensity)| |
||||
|\x1b[21m|Bold off(disable foreground intensity)| |
||||
|\x1b[24m|Underline off| |
||||
|\x1b[25m|Blink off(disable background intensity)| |
||||
|
||||
|Escape sequence|Foreground colors| |
||||
|---------------|----| |
||||
|\x1b[30m|Black| |
||||
|\x1b[31m|Red| |
||||
|\x1b[32m|Green| |
||||
|\x1b[33m|Yellow| |
||||
|\x1b[34m|Blue| |
||||
|\x1b[35m|Magenta| |
||||
|\x1b[36m|Cyan| |
||||
|\x1b[37m|White| |
||||
|\x1b[39m|Default(foreground color at startup)| |
||||
|\x1b[90m|Light Gray| |
||||
|\x1b[91m|Light Red| |
||||
|\x1b[92m|Light Green| |
||||
|\x1b[93m|Light Yellow| |
||||
|\x1b[94m|Light Blue| |
||||
|\x1b[95m|Light Magenta| |
||||
|\x1b[96m|Light Cyan| |
||||
|\x1b[97m|Light White| |
||||
|
||||
|Escape sequence|Background colors| |
||||
|---------------|----| |
||||
|\x1b[40m|Black| |
||||
|\x1b[41m|Red| |
||||
|\x1b[42m|Green| |
||||
|\x1b[43m|Yellow| |
||||
|\x1b[44m|Blue| |
||||
|\x1b[45m|Magenta| |
||||
|\x1b[46m|Cyan| |
||||
|\x1b[47m|White| |
||||
|\x1b[49m|Default(background color at startup)| |
||||
|\x1b[100m|Light Gray| |
||||
|\x1b[101m|Light Red| |
||||
|\x1b[102m|Light Green| |
||||
|\x1b[103m|Light Yellow| |
||||
|\x1b[104m|Light Blue| |
||||
|\x1b[105m|Light Magenta| |
||||
|\x1b[106m|Light Cyan| |
||||
|\x1b[107m|Light White| |
||||
|
||||
## Example |
||||
|
||||
```go |
||||
package main |
||||
|
||||
import ( |
||||
"fmt" |
||||
"os" |
||||
|
||||
"github.com/shiena/ansicolor" |
||||
) |
||||
|
||||
func main() { |
||||
w := ansicolor.NewAnsiColorWriter(os.Stdout) |
||||
text := "%sforeground %sbold%s %sbackground%s\n" |
||||
fmt.Fprintf(w, text, "\x1b[31m", "\x1b[1m", "\x1b[21m", "\x1b[41;32m", "\x1b[0m") |
||||
fmt.Fprintf(w, text, "\x1b[32m", "\x1b[1m", "\x1b[21m", "\x1b[42;31m", "\x1b[0m") |
||||
fmt.Fprintf(w, text, "\x1b[33m", "\x1b[1m", "\x1b[21m", "\x1b[43;34m", "\x1b[0m") |
||||
fmt.Fprintf(w, text, "\x1b[34m", "\x1b[1m", "\x1b[21m", "\x1b[44;33m", "\x1b[0m") |
||||
fmt.Fprintf(w, text, "\x1b[35m", "\x1b[1m", "\x1b[21m", "\x1b[45;36m", "\x1b[0m") |
||||
fmt.Fprintf(w, text, "\x1b[36m", "\x1b[1m", "\x1b[21m", "\x1b[46;35m", "\x1b[0m") |
||||
fmt.Fprintf(w, text, "\x1b[37m", "\x1b[1m", "\x1b[21m", "\x1b[47;30m", "\x1b[0m") |
||||
} |
||||
``` |
||||
|
||||
![screenshot](https://gist.githubusercontent.com/shiena/a1bada24b525314a7d5e/raw/c763aa7cda6e4fefaccf831e2617adc40b6151c7/main.png) |
||||
|
||||
## See also: |
||||
|
||||
- https://github.com/daviddengcn/go-colortext |
||||
- https://github.com/adoxa/ansicon |
||||
- https://github.com/aslakhellesoy/wac |
||||
- https://github.com/wsxiaoys/terminal |
||||
|
||||
## Contributing |
||||
|
||||
1. Fork it |
||||
2. Create your feature branch (`git checkout -b my-new-feature`) |
||||
3. Commit your changes (`git commit -am 'Add some feature'`) |
||||
4. Push to the branch (`git push origin my-new-feature`) |
||||
5. Create new Pull Request |
||||
|
@ -0,0 +1,20 @@ |
||||
// Copyright 2014 shiena Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package ansicolor provides color console in Windows as ANSICON.
|
||||
package ansicolor |
||||
|
||||
import "io" |
||||
|
||||
// NewAnsiColorWriter creates and initializes a new ansiColorWriter
|
||||
// using io.Writer w as its initial contents.
|
||||
// In the console of Windows, which change the foreground and background
|
||||
// colors of the text by the escape sequence.
|
||||
// In the console of other systems, which writes to w all text.
|
||||
func NewAnsiColorWriter(w io.Writer) io.Writer { |
||||
if _, ok := w.(*ansiColorWriter); !ok { |
||||
return &ansiColorWriter{w: w} |
||||
} |
||||
return w |
||||
} |
@ -0,0 +1,27 @@ |
||||
// Copyright 2014 shiena Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/* |
||||
|
||||
The ansicolor command colors a console text by ANSI escape sequence like wac. |
||||
|
||||
$ go get github.com/shiena/ansicolor/ansicolor |
||||
|
||||
See also: |
||||
https://github.com/aslakhellesoy/wac
|
||||
|
||||
*/ |
||||
package main |
||||
|
||||
import ( |
||||
"io" |
||||
"os" |
||||
|
||||
"github.com/shiena/ansicolor" |
||||
) |
||||
|
||||
func main() { |
||||
w := ansicolor.NewAnsiColorWriter(os.Stdout) |
||||
io.Copy(w, os.Stdin) |
||||
} |
@ -0,0 +1,17 @@ |
||||
// Copyright 2014 shiena Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !windows
|
||||
|
||||
package ansicolor |
||||
|
||||
import "io" |
||||
|
||||
type ansiColorWriter struct { |
||||
w io.Writer |
||||
} |
||||
|
||||
func (cw *ansiColorWriter) Write(p []byte) (int, error) { |
||||
return cw.w.Write(p) |
||||
} |
@ -0,0 +1,25 @@ |
||||
package ansicolor_test |
||||
|
||||
import ( |
||||
"bytes" |
||||
"testing" |
||||
|
||||
"github.com/shiena/ansicolor" |
||||
) |
||||
|
||||
func TestNewAnsiColor1(t *testing.T) { |
||||
inner := bytes.NewBufferString("") |
||||
w := ansicolor.NewAnsiColorWriter(inner) |
||||
if w == inner { |
||||
t.Errorf("Get %#v, want %#v", w, inner) |
||||
} |
||||
} |
||||
|
||||
func TestNewAnsiColor2(t *testing.T) { |
||||
inner := bytes.NewBufferString("") |
||||
w1 := ansicolor.NewAnsiColorWriter(inner) |
||||
w2 := ansicolor.NewAnsiColorWriter(w1) |
||||
if w1 != w2 { |
||||
t.Errorf("Get %#v, want %#v", w1, w2) |
||||
} |
||||
} |
@ -0,0 +1,351 @@ |
||||
// Copyright 2014 shiena Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build windows
|
||||
|
||||
package ansicolor |
||||
|
||||
import ( |
||||
"bytes" |
||||
"io" |
||||
"strings" |
||||
"syscall" |
||||
"unsafe" |
||||
) |
||||
|
||||
type csiState int |
||||
|
||||
const ( |
||||
outsideCsiCode csiState = iota |
||||
firstCsiCode |
||||
secondCsiCode |
||||
) |
||||
|
||||
type ansiColorWriter struct { |
||||
w io.Writer |
||||
state csiState |
||||
paramBuf bytes.Buffer |
||||
} |
||||
|
||||
const ( |
||||
firstCsiChar byte = '\x1b' |
||||
secondeCsiChar byte = '[' |
||||
separatorChar byte = ';' |
||||
sgrCode byte = 'm' |
||||
) |
||||
|
||||
const ( |
||||
foregroundBlue = uint16(0x0001) |
||||
foregroundGreen = uint16(0x0002) |
||||
foregroundRed = uint16(0x0004) |
||||
foregroundIntensity = uint16(0x0008) |
||||
backgroundBlue = uint16(0x0010) |
||||
backgroundGreen = uint16(0x0020) |
||||
backgroundRed = uint16(0x0040) |
||||
backgroundIntensity = uint16(0x0080) |
||||
underscore = uint16(0x8000) |
||||
|
||||
foregroundMask = foregroundBlue | foregroundGreen | foregroundRed | foregroundIntensity |
||||
backgroundMask = backgroundBlue | backgroundGreen | backgroundRed | backgroundIntensity |
||||
) |
||||
|
||||
const ( |
||||
ansiReset = "0" |
||||
ansiIntensityOn = "1" |
||||
ansiIntensityOff = "21" |
||||
ansiUnderlineOn = "4" |
||||
ansiUnderlineOff = "24" |
||||
ansiBlinkOn = "5" |
||||
ansiBlinkOff = "25" |
||||
|
||||
ansiForegroundBlack = "30" |
||||
ansiForegroundRed = "31" |
||||
ansiForegroundGreen = "32" |
||||
ansiForegroundYellow = "33" |
||||
ansiForegroundBlue = "34" |
||||
ansiForegroundMagenta = "35" |
||||
ansiForegroundCyan = "36" |
||||
ansiForegroundWhite = "37" |
||||
ansiForegroundDefault = "39" |
||||
|
||||
ansiBackgroundBlack = "40" |
||||
ansiBackgroundRed = "41" |
||||
ansiBackgroundGreen = "42" |
||||
ansiBackgroundYellow = "43" |
||||
ansiBackgroundBlue = "44" |
||||
ansiBackgroundMagenta = "45" |
||||
ansiBackgroundCyan = "46" |
||||
ansiBackgroundWhite = "47" |
||||
ansiBackgroundDefault = "49" |
||||
|
||||
ansiLightForegroundGray = "90" |
||||
ansiLightForegroundRed = "91" |
||||
ansiLightForegroundGreen = "92" |
||||
ansiLightForegroundYellow = "93" |
||||
ansiLightForegroundBlue = "94" |
||||
ansiLightForegroundMagenta = "95" |
||||
ansiLightForegroundCyan = "96" |
||||
ansiLightForegroundWhite = "97" |
||||
|
||||
ansiLightBackgroundGray = "100" |
||||
ansiLightBackgroundRed = "101" |
||||
ansiLightBackgroundGreen = "102" |
||||
ansiLightBackgroundYellow = "103" |
||||
ansiLightBackgroundBlue = "104" |
||||
ansiLightBackgroundMagenta = "105" |
||||
ansiLightBackgroundCyan = "106" |
||||
ansiLightBackgroundWhite = "107" |
||||
) |
||||
|
||||
type drawType int |
||||
|
||||
const ( |
||||
foreground drawType = iota |
||||
background |
||||
) |
||||
|
||||
type winColor struct { |
||||
code uint16 |
||||
drawType drawType |
||||
} |
||||
|
||||
var colorMap = map[string]winColor{ |
||||
ansiForegroundBlack: {0, foreground}, |
||||
ansiForegroundRed: {foregroundRed, foreground}, |
||||
ansiForegroundGreen: {foregroundGreen, foreground}, |
||||
ansiForegroundYellow: {foregroundRed | foregroundGreen, foreground}, |
||||
ansiForegroundBlue: {foregroundBlue, foreground}, |
||||
ansiForegroundMagenta: {foregroundRed | foregroundBlue, foreground}, |
||||
ansiForegroundCyan: {foregroundGreen | foregroundBlue, foreground}, |
||||
ansiForegroundWhite: {foregroundRed | foregroundGreen | foregroundBlue, foreground}, |
||||
ansiForegroundDefault: {foregroundRed | foregroundGreen | foregroundBlue, foreground}, |
||||
|
||||
ansiBackgroundBlack: {0, background}, |
||||
ansiBackgroundRed: {backgroundRed, background}, |
||||
ansiBackgroundGreen: {backgroundGreen, background}, |
||||
ansiBackgroundYellow: {backgroundRed | backgroundGreen, background}, |
||||
ansiBackgroundBlue: {backgroundBlue, background}, |
||||
ansiBackgroundMagenta: {backgroundRed | backgroundBlue, background}, |
||||
ansiBackgroundCyan: {backgroundGreen | backgroundBlue, background}, |
||||
ansiBackgroundWhite: {backgroundRed | backgroundGreen | backgroundBlue, background}, |
||||
ansiBackgroundDefault: {0, background}, |
||||
|
||||
ansiLightForegroundGray: {foregroundIntensity, foreground}, |
||||
ansiLightForegroundRed: {foregroundIntensity | foregroundRed, foreground}, |
||||
ansiLightForegroundGreen: {foregroundIntensity | foregroundGreen, foreground}, |
||||
ansiLightForegroundYellow: {foregroundIntensity | foregroundRed | foregroundGreen, foreground}, |
||||
ansiLightForegroundBlue: {foregroundIntensity | foregroundBlue, foreground}, |
||||
ansiLightForegroundMagenta: {foregroundIntensity | foregroundRed | foregroundBlue, foreground}, |
||||
ansiLightForegroundCyan: {foregroundIntensity | foregroundGreen | foregroundBlue, foreground}, |
||||
ansiLightForegroundWhite: {foregroundIntensity | foregroundRed | foregroundGreen | foregroundBlue, foreground}, |
||||
|
||||
ansiLightBackgroundGray: {backgroundIntensity, background}, |
||||
ansiLightBackgroundRed: {backgroundIntensity | backgroundRed, background}, |
||||
ansiLightBackgroundGreen: {backgroundIntensity | backgroundGreen, background}, |
||||
ansiLightBackgroundYellow: {backgroundIntensity | backgroundRed | backgroundGreen, background}, |
||||
ansiLightBackgroundBlue: {backgroundIntensity | backgroundBlue, background}, |
||||
ansiLightBackgroundMagenta: {backgroundIntensity | backgroundRed | backgroundBlue, background}, |
||||
ansiLightBackgroundCyan: {backgroundIntensity | backgroundGreen | backgroundBlue, background}, |
||||
ansiLightBackgroundWhite: {backgroundIntensity | backgroundRed | backgroundGreen | backgroundBlue, background}, |
||||
} |
||||
|
||||
var ( |
||||
kernel32 = syscall.NewLazyDLL("kernel32.dll") |
||||
procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute") |
||||
procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") |
||||
defaultAttr *textAttributes |
||||
) |
||||
|
||||
func init() { |
||||
screenInfo := getConsoleScreenBufferInfo(uintptr(syscall.Stdout)) |
||||
if screenInfo != nil { |
||||
colorMap[ansiForegroundDefault] = winColor{ |
||||
screenInfo.WAttributes & (foregroundRed | foregroundGreen | foregroundBlue), |
||||
foreground, |
||||
} |
||||
colorMap[ansiBackgroundDefault] = winColor{ |
||||
screenInfo.WAttributes & (backgroundRed | backgroundGreen | backgroundBlue), |
||||
background, |
||||
} |
||||
defaultAttr = convertTextAttr(screenInfo.WAttributes) |
||||
} |
||||
} |
||||
|
||||
type coord struct { |
||||
X, Y int16 |
||||
} |
||||
|
||||
type smallRect struct { |
||||
Left, Top, Right, Bottom int16 |
||||
} |
||||
|
||||
type consoleScreenBufferInfo struct { |
||||
DwSize coord |
||||
DwCursorPosition coord |
||||
WAttributes uint16 |
||||
SrWindow smallRect |
||||
DwMaximumWindowSize coord |
||||
} |
||||
|
||||
func getConsoleScreenBufferInfo(hConsoleOutput uintptr) *consoleScreenBufferInfo { |
||||
var csbi consoleScreenBufferInfo |
||||
ret, _, _ := procGetConsoleScreenBufferInfo.Call( |
||||
hConsoleOutput, |
||||
uintptr(unsafe.Pointer(&csbi))) |
||||
if ret == 0 { |
||||
return nil |
||||
} |
||||
return &csbi |
||||
} |
||||
|
||||
func setConsoleTextAttribute(hConsoleOutput uintptr, wAttributes uint16) bool { |
||||
ret, _, _ := procSetConsoleTextAttribute.Call( |
||||
hConsoleOutput, |
||||
uintptr(wAttributes)) |
||||
return ret != 0 |
||||
} |
||||
|
||||
type textAttributes struct { |
||||
foregroundColor uint16 |
||||
backgroundColor uint16 |
||||
foregroundIntensity uint16 |
||||
backgroundIntensity uint16 |
||||
underscore uint16 |
||||
otherAttributes uint16 |
||||
} |
||||
|
||||
func convertTextAttr(winAttr uint16) *textAttributes { |
||||
fgColor := winAttr & (foregroundRed | foregroundGreen | foregroundBlue) |
||||
bgColor := winAttr & (backgroundRed | backgroundGreen | backgroundBlue) |
||||
fgIntensity := winAttr & foregroundIntensity |
||||
bgIntensity := winAttr & backgroundIntensity |
||||
underline := winAttr & underscore |
||||
otherAttributes := winAttr &^ (foregroundMask | backgroundMask | underscore) |
||||
return &textAttributes{fgColor, bgColor, fgIntensity, bgIntensity, underline, otherAttributes} |
||||
} |
||||
|
||||
func convertWinAttr(textAttr *textAttributes) uint16 { |
||||
var winAttr uint16 = 0 |
||||
winAttr |= textAttr.foregroundColor |
||||
winAttr |= textAttr.backgroundColor |
||||
winAttr |= textAttr.foregroundIntensity |
||||
winAttr |= textAttr.backgroundIntensity |
||||
winAttr |= textAttr.underscore |
||||
winAttr |= textAttr.otherAttributes |
||||
return winAttr |
||||
} |
||||
|
||||
func changeColor(param []byte) { |
||||
if defaultAttr == nil { |
||||
return |
||||
} |
||||
|
||||
screenInfo := getConsoleScreenBufferInfo(uintptr(syscall.Stdout)) |
||||
if screenInfo == nil { |
||||
return |
||||
} |
||||
|
||||
winAttr := convertTextAttr(screenInfo.WAttributes) |
||||
strParam := string(param) |
||||
if len(strParam) <= 0 { |
||||
strParam = "0" |
||||
} |
||||
csiParam := strings.Split(strParam, string(separatorChar)) |
||||
for _, p := range csiParam { |
||||
c, ok := colorMap[p] |
||||
switch { |
||||
case !ok: |
||||
switch p { |
||||
case ansiReset: |
||||
winAttr.foregroundColor = defaultAttr.foregroundColor |
||||
winAttr.backgroundColor = defaultAttr.backgroundColor |
||||
winAttr.foregroundIntensity = defaultAttr.foregroundIntensity |
||||
winAttr.backgroundIntensity = defaultAttr.backgroundIntensity |
||||
winAttr.underscore = 0 |
||||
winAttr.otherAttributes = 0 |
||||
case ansiIntensityOn: |
||||
winAttr.foregroundIntensity = foregroundIntensity |
||||
case ansiIntensityOff: |
||||
winAttr.foregroundIntensity = 0 |
||||
case ansiUnderlineOn: |
||||
winAttr.underscore = underscore |
||||
case ansiUnderlineOff: |
||||
winAttr.underscore = 0 |
||||
case ansiBlinkOn: |
||||
winAttr.backgroundIntensity = backgroundIntensity |
||||
case ansiBlinkOff: |
||||
winAttr.backgroundIntensity = 0 |
||||
default: |
||||
// unknown code
|
||||
} |
||||
case c.drawType == foreground: |
||||
winAttr.foregroundColor = c.code |
||||
case c.drawType == background: |
||||
winAttr.backgroundColor = c.code |
||||
} |
||||
} |
||||
winTextAttribute := convertWinAttr(winAttr) |
||||
setConsoleTextAttribute(uintptr(syscall.Stdout), winTextAttribute) |
||||
} |
||||
|
||||
func parseEscapeSequence(command byte, param []byte) { |
||||
switch command { |
||||
case sgrCode: |
||||
changeColor(param) |
||||
} |
||||
} |
||||
|
||||
func isParameterChar(b byte) bool { |
||||
return ('0' <= b && b <= '9') || b == separatorChar |
||||
} |
||||
|
||||
func (cw *ansiColorWriter) Write(p []byte) (int, error) { |
||||
r, nw, nc, first, last := 0, 0, 0, 0, 0 |
||||
var err error |
||||
for i, ch := range p { |
||||
switch cw.state { |
||||
case outsideCsiCode: |
||||
if ch == firstCsiChar { |
||||
nc++ |
||||
cw.state = firstCsiCode |
||||
} |
||||
case firstCsiCode: |
||||
switch ch { |
||||
case firstCsiChar: |
||||
nc++ |
||||
break |
||||
case secondeCsiChar: |
||||
nc++ |
||||
cw.state = secondCsiCode |
||||
last = i - 1 |
||||
default: |
||||
cw.state = outsideCsiCode |
||||
} |
||||
case secondCsiCode: |
||||
nc++ |
||||
if isParameterChar(ch) { |
||||
cw.paramBuf.WriteByte(ch) |
||||
} else { |
||||
nw, err = cw.w.Write(p[first:last]) |
||||
r += nw |
||||
if err != nil { |
||||
return r, err |
||||
} |
||||
first = i + 1 |
||||
param := cw.paramBuf.Bytes() |
||||
cw.paramBuf.Reset() |
||||
parseEscapeSequence(ch, param) |
||||
cw.state = outsideCsiCode |
||||
} |
||||
default: |
||||
cw.state = outsideCsiCode |
||||
} |
||||
} |
||||
|
||||
if cw.state == outsideCsiCode { |
||||
nw, err = cw.w.Write(p[first:len(p)]) |
||||
} |
||||
|
||||
return r + nw + nc, err |
||||
} |
@ -0,0 +1,236 @@ |
||||
// Copyright 2014 shiena Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build windows
|
||||
|
||||
package ansicolor_test |
||||
|
||||
import ( |
||||
"bytes" |
||||
"fmt" |
||||
"syscall" |
||||
"testing" |
||||
|
||||
"github.com/shiena/ansicolor" |
||||
. "github.com/shiena/ansicolor" |
||||
) |
||||
|
||||
func TestWritePlanText(t *testing.T) { |
||||
inner := bytes.NewBufferString("") |
||||
w := ansicolor.NewAnsiColorWriter(inner) |
||||
expected := "plain text" |
||||
fmt.Fprintf(w, expected) |
||||
actual := inner.String() |
||||
if actual != expected { |
||||
t.Errorf("Get %s, want %s", actual, expected) |
||||
} |
||||
} |
||||
|
||||
func TestWriteParseText(t *testing.T) { |
||||
inner := bytes.NewBufferString("") |
||||
w := ansicolor.NewAnsiColorWriter(inner) |
||||
|
||||
inputTail := "\x1b[0mtail text" |
||||
expectedTail := "tail text" |
||||
fmt.Fprintf(w, inputTail) |
||||
actualTail := inner.String() |
||||
inner.Reset() |
||||
if actualTail != expectedTail { |
||||
t.Errorf("Get %s, want %s", actualTail, expectedTail) |
||||
} |
||||
|
||||
inputHead := "head text\x1b[0m" |
||||
expectedHead := "head text" |
||||
fmt.Fprintf(w, inputHead) |
||||
actualHead := inner.String() |
||||
inner.Reset() |
||||
if actualHead != expectedHead { |
||||
t.Errorf("Get %s, want %s", actualHead, expectedHead) |
||||
} |
||||
|
||||
inputBothEnds := "both ends \x1b[0m text" |
||||
expectedBothEnds := "both ends text" |
||||
fmt.Fprintf(w, inputBothEnds) |
||||
actualBothEnds := inner.String() |
||||
inner.Reset() |
||||
if actualBothEnds != expectedBothEnds { |
||||
t.Errorf("Get %s, want %s", actualBothEnds, expectedBothEnds) |
||||
} |
||||
|
||||
inputManyEsc := "\x1b\x1b\x1b\x1b[0m many esc" |
||||
expectedManyEsc := "\x1b\x1b\x1b many esc" |
||||
fmt.Fprintf(w, inputManyEsc) |
||||
actualManyEsc := inner.String() |
||||
inner.Reset() |
||||
if actualManyEsc != expectedManyEsc { |
||||
t.Errorf("Get %s, want %s", actualManyEsc, expectedManyEsc) |
||||
} |
||||
|
||||
expectedSplit := "split text" |
||||
for _, ch := range "split \x1b[0m text" { |
||||
fmt.Fprintf(w, string(ch)) |
||||
} |
||||
actualSplit := inner.String() |
||||
inner.Reset() |
||||
if actualSplit != expectedSplit { |
||||
t.Errorf("Get %s, want %s", actualSplit, expectedSplit) |
||||
} |
||||
} |
||||
|
||||
type screenNotFoundError struct { |
||||
error |
||||
} |
||||
|
||||
func writeAnsiColor(expectedText, colorCode string) (actualText string, actualAttributes uint16, err error) { |
||||
inner := bytes.NewBufferString("") |
||||
w := ansicolor.NewAnsiColorWriter(inner) |
||||
fmt.Fprintf(w, "\x1b[%sm%s", colorCode, expectedText) |
||||
|
||||
actualText = inner.String() |
||||
screenInfo := GetConsoleScreenBufferInfo(uintptr(syscall.Stdout)) |
||||
if screenInfo != nil { |
||||
actualAttributes = screenInfo.WAttributes |
||||
} else { |
||||
err = &screenNotFoundError{} |
||||
} |
||||
return |
||||
} |
||||
|
||||
type testParam struct { |
||||
text string |
||||
attributes uint16 |
||||
ansiColor string |
||||
} |
||||
|
||||
func TestWriteAnsiColorText(t *testing.T) { |
||||
screenInfo := GetConsoleScreenBufferInfo(uintptr(syscall.Stdout)) |
||||
if screenInfo == nil { |
||||
t.Fatal("Could not get ConsoleScreenBufferInfo") |
||||
} |
||||
defer ChangeColor(screenInfo.WAttributes) |
||||
defaultFgColor := screenInfo.WAttributes & uint16(0x0007) |
||||
defaultBgColor := screenInfo.WAttributes & uint16(0x0070) |
||||
defaultFgIntensity := screenInfo.WAttributes & uint16(0x0008) |
||||
defaultBgIntensity := screenInfo.WAttributes & uint16(0x0080) |
||||
|
||||
fgParam := []testParam{ |
||||
{"foreground black ", uint16(0x0000 | 0x0000), "30"}, |
||||
{"foreground red ", uint16(0x0004 | 0x0000), "31"}, |
||||
{"foreground green ", uint16(0x0002 | 0x0000), "32"}, |
||||
{"foreground yellow ", uint16(0x0006 | 0x0000), "33"}, |
||||
{"foreground blue ", uint16(0x0001 | 0x0000), "34"}, |
||||
{"foreground magenta", uint16(0x0005 | 0x0000), "35"}, |
||||
{"foreground cyan ", uint16(0x0003 | 0x0000), "36"}, |
||||
{"foreground white ", uint16(0x0007 | 0x0000), "37"}, |
||||
{"foreground default", defaultFgColor | 0x0000, "39"}, |
||||
{"foreground light gray ", uint16(0x0000 | 0x0008 | 0x0000), "90"}, |
||||
{"foreground light red ", uint16(0x0004 | 0x0008 | 0x0000), "91"}, |
||||
{"foreground light green ", uint16(0x0002 | 0x0008 | 0x0000), "92"}, |
||||
{"foreground light yellow ", uint16(0x0006 | 0x0008 | 0x0000), "93"}, |
||||
{"foreground light blue ", uint16(0x0001 | 0x0008 | 0x0000), "94"}, |
||||
{"foreground light magenta", uint16(0x0005 | 0x0008 | 0x0000), "95"}, |
||||
{"foreground light cyan ", uint16(0x0003 | 0x0008 | 0x0000), "96"}, |
||||
{"foreground light white ", uint16(0x0007 | 0x0008 | 0x0000), "97"}, |
||||
} |
||||
|
||||
bgParam := []testParam{ |
||||
{"background black ", uint16(0x0007 | 0x0000), "40"}, |
||||
{"background red ", uint16(0x0007 | 0x0040), "41"}, |
||||
{"background green ", uint16(0x0007 | 0x0020), "42"}, |
||||
{"background yellow ", uint16(0x0007 | 0x0060), "43"}, |
||||
{"background blue ", uint16(0x0007 | 0x0010), "44"}, |
||||
{"background magenta", uint16(0x0007 | 0x0050), "45"}, |
||||
{"background cyan ", uint16(0x0007 | 0x0030), "46"}, |
||||
{"background white ", uint16(0x0007 | 0x0070), "47"}, |
||||
{"background default", uint16(0x0007) | defaultBgColor, "49"}, |
||||
{"background light gray ", uint16(0x0007 | 0x0000 | 0x0080), "100"}, |
||||
{"background light red ", uint16(0x0007 | 0x0040 | 0x0080), "101"}, |
||||
{"background light green ", uint16(0x0007 | 0x0020 | 0x0080), "102"}, |
||||
{"background light yellow ", uint16(0x0007 | 0x0060 | 0x0080), "103"}, |
||||
{"background light blue ", uint16(0x0007 | 0x0010 | 0x0080), "104"}, |
||||
{"background light magenta", uint16(0x0007 | 0x0050 | 0x0080), "105"}, |
||||
{"background light cyan ", uint16(0x0007 | 0x0030 | 0x0080), "106"}, |
||||
{"background light white ", uint16(0x0007 | 0x0070 | 0x0080), "107"}, |
||||
} |
||||
|
||||
resetParam := []testParam{ |
||||
{"all reset", defaultFgColor | defaultBgColor | defaultFgIntensity | defaultBgIntensity, "0"}, |
||||
{"all reset", defaultFgColor | defaultBgColor | defaultFgIntensity | defaultBgIntensity, ""}, |
||||
} |
||||
|
||||
boldParam := []testParam{ |
||||
{"bold on", uint16(0x0007 | 0x0008), "1"}, |
||||
{"bold off", uint16(0x0007), "21"}, |
||||
} |
||||
|
||||
underscoreParam := []testParam{ |
||||
{"underscore on", uint16(0x0007 | 0x8000), "4"}, |
||||
{"underscore off", uint16(0x0007), "24"}, |
||||
} |
||||
|
||||
blinkParam := []testParam{ |
||||
{"blink on", uint16(0x0007 | 0x0080), "5"}, |
||||
{"blink off", uint16(0x0007), "25"}, |
||||
} |
||||
|
||||
mixedParam := []testParam{ |
||||
{"both black, bold, underline, blink", uint16(0x0000 | 0x0000 | 0x0008 | 0x8000 | 0x0080), "30;40;1;4;5"}, |
||||
{"both red, bold, underline, blink", uint16(0x0004 | 0x0040 | 0x0008 | 0x8000 | 0x0080), "31;41;1;4;5"}, |
||||
{"both green, bold, underline, blink", uint16(0x0002 | 0x0020 | 0x0008 | 0x8000 | 0x0080), "32;42;1;4;5"}, |
||||
{"both yellow, bold, underline, blink", uint16(0x0006 | 0x0060 | 0x0008 | 0x8000 | 0x0080), "33;43;1;4;5"}, |
||||
{"both blue, bold, underline, blink", uint16(0x0001 | 0x0010 | 0x0008 | 0x8000 | 0x0080), "34;44;1;4;5"}, |
||||
{"both magenta, bold, underline, blink", uint16(0x0005 | 0x0050 | 0x0008 | 0x8000 | 0x0080), "35;45;1;4;5"}, |
||||
{"both cyan, bold, underline, blink", uint16(0x0003 | 0x0030 | 0x0008 | 0x8000 | 0x0080), "36;46;1;4;5"}, |
||||
{"both white, bold, underline, blink", uint16(0x0007 | 0x0070 | 0x0008 | 0x8000 | 0x0080), "37;47;1;4;5"}, |
||||
{"both default, bold, underline, blink", uint16(defaultFgColor | defaultBgColor | 0x0008 | 0x8000 | 0x0080), "39;49;1;4;5"}, |
||||
} |
||||
|
||||
assertTextAttribute := func(expectedText string, expectedAttributes uint16, ansiColor string) { |
||||
actualText, actualAttributes, err := writeAnsiColor(expectedText, ansiColor) |
||||
if actualText != expectedText { |
||||
t.Errorf("Get %s, want %s", actualText, expectedText) |
||||
} |
||||
if err != nil { |
||||
t.Fatal("Could not get ConsoleScreenBufferInfo") |
||||
} |
||||
if actualAttributes != expectedAttributes { |
||||
t.Errorf("Text: %s, Get 0x%04x, want 0x%04x", expectedText, actualAttributes, expectedAttributes) |
||||
} |
||||
} |
||||
|
||||
for _, v := range fgParam { |
||||
ResetColor() |
||||
assertTextAttribute(v.text, v.attributes, v.ansiColor) |
||||
} |
||||
|
||||
for _, v := range bgParam { |
||||
ChangeColor(uint16(0x0070 | 0x0007)) |
||||
assertTextAttribute(v.text, v.attributes, v.ansiColor) |
||||
} |
||||
|
||||
for _, v := range resetParam { |
||||
ChangeColor(uint16(0x0000 | 0x0070 | 0x0008)) |
||||
assertTextAttribute(v.text, v.attributes, v.ansiColor) |
||||
} |
||||
|
||||
ResetColor() |
||||
for _, v := range boldParam { |
||||
assertTextAttribute(v.text, v.attributes, v.ansiColor) |
||||
} |
||||
|
||||
ResetColor() |
||||
for _, v := range underscoreParam { |
||||
assertTextAttribute(v.text, v.attributes, v.ansiColor) |
||||
} |
||||
|
||||
ResetColor() |
||||
for _, v := range blinkParam { |
||||
assertTextAttribute(v.text, v.attributes, v.ansiColor) |
||||
} |
||||
|
||||
for _, v := range mixedParam { |
||||
ResetColor() |
||||
assertTextAttribute(v.text, v.attributes, v.ansiColor) |
||||
} |
||||
} |
@ -0,0 +1,24 @@ |
||||
// Copyright 2014 shiena Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ansicolor_test |
||||
|
||||
import ( |
||||
"fmt" |
||||
"os" |
||||
|
||||
"github.com/shiena/ansicolor" |
||||
) |
||||
|
||||
func ExampleNewAnsiColorWriter() { |
||||
w := ansicolor.NewAnsiColorWriter(os.Stdout) |
||||
text := "%sforeground %sbold%s %sbackground%s\n" |
||||
fmt.Fprintf(w, text, "\x1b[31m", "\x1b[1m", "\x1b[21m", "\x1b[41;32m", "\x1b[0m") |
||||
fmt.Fprintf(w, text, "\x1b[32m", "\x1b[1m", "\x1b[21m", "\x1b[42;31m", "\x1b[0m") |
||||
fmt.Fprintf(w, text, "\x1b[33m", "\x1b[1m", "\x1b[21m", "\x1b[43;34m", "\x1b[0m") |
||||
fmt.Fprintf(w, text, "\x1b[34m", "\x1b[1m", "\x1b[21m", "\x1b[44;33m", "\x1b[0m") |
||||
fmt.Fprintf(w, text, "\x1b[35m", "\x1b[1m", "\x1b[21m", "\x1b[45;36m", "\x1b[0m") |
||||
fmt.Fprintf(w, text, "\x1b[36m", "\x1b[1m", "\x1b[21m", "\x1b[46;35m", "\x1b[0m") |
||||
fmt.Fprintf(w, text, "\x1b[37m", "\x1b[1m", "\x1b[21m", "\x1b[47;30m", "\x1b[0m") |
||||
} |
@ -0,0 +1,19 @@ |
||||
// Copyright 2014 shiena Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build windows
|
||||
|
||||
package ansicolor |
||||
|
||||
import "syscall" |
||||
|
||||
var GetConsoleScreenBufferInfo = getConsoleScreenBufferInfo |
||||
|
||||
func ChangeColor(color uint16) { |
||||
setConsoleTextAttribute(uintptr(syscall.Stdout), color) |
||||
} |
||||
|
||||
func ResetColor() { |
||||
ChangeColor(uint16(0x0007)) |
||||
} |
@ -1,137 +0,0 @@ |
||||
// Copyright 2014 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package jsre |
||||
|
||||
const pp_js = ` |
||||
function pp(object, indent) { |
||||
try { |
||||
JSON.stringify(object) |
||||
} catch(e) { |
||||
return pp(e, indent); |
||||
} |
||||
|
||||
var str = ""; |
||||
if(object instanceof Array) { |
||||
str += "["; |
||||
for(var i = 0, l = object.length; i < l; i++) { |
||||
str += pp(object[i], indent); |
||||
|
||||
if(i < l-1) { |
||||
str += ", "; |
||||
} |
||||
} |
||||
str += " ]"; |
||||
} else if (object instanceof Error) { |
||||
str += "\033[31m" + "Error:\033[0m " + object.message; |
||||
} else if (isBigNumber(object)) { |
||||
str += "\033[32m'" + object.toString(10) + "'"; |
||||
} else if(typeof(object) === "object") { |
||||
str += "{\n"; |
||||
indent += " "; |
||||
|
||||
var fields = getFields(object); |
||||
var last = fields[fields.length - 1]; |
||||
fields.forEach(function (key) { |
||||
str += indent + key + ": "; |
||||
try { |
||||
str += pp(object[key], indent); |
||||
} catch (e) { |
||||
str += pp(e, indent); |
||||
} |
||||
if(key !== last) { |
||||
str += ","; |
||||
} |
||||
str += "\n"; |
||||
}); |
||||
str += indent.substr(2, indent.length) + "}"; |
||||
} else if(typeof(object) === "string") { |
||||
str += "\033[32m'" + object + "'"; |
||||
} else if(typeof(object) === "undefined") { |
||||
str += "\033[1m\033[30m" + object; |
||||
} else if(typeof(object) === "number") { |
||||
str += "\033[31m" + object; |
||||
} else if(typeof(object) === "function") { |
||||
str += "\033[35m" + object.toString().split(" {")[0]; |
||||
} else { |
||||
str += object; |
||||
} |
||||
|
||||
str += "\033[0m"; |
||||
|
||||
return str; |
||||
} |
||||
|
||||
var redundantFields = [ |
||||
'valueOf', |
||||
'toString', |
||||
'toLocaleString', |
||||
'hasOwnProperty', |
||||
'isPrototypeOf', |
||||
'propertyIsEnumerable', |
||||
'constructor' |
||||
]; |
||||
|
||||
var getFields = function (object) { |
||||
var members = Object.getOwnPropertyNames(object); |
||||
if (object.constructor && object.constructor.prototype) { |
||||
members = members.concat(Object.getOwnPropertyNames(object.constructor.prototype)); |
||||
} |
||||
|
||||
var fields = members.filter(function (member) { |
||||
return !isMemberFunction(object, member) |
||||
}).sort() |
||||
var funcs = members.filter(function (member) { |
||||
return isMemberFunction(object, member) |
||||
}).sort() |
||||
|
||||
var results = fields.concat(funcs); |
||||
return results.filter(function (field) { |
||||
return redundantFields.indexOf(field) === -1; |
||||
}); |
||||
}; |
||||
|
||||
var isMemberFunction = function(object, member) { |
||||
try { |
||||
return typeof(object[member]) === "function"; |
||||
} catch(e) { |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
var isBigNumber = function (object) { |
||||
var result = typeof BigNumber !== 'undefined' && object instanceof BigNumber; |
||||
|
||||
if (!result) { |
||||
if (typeof(object) === "object" && object.constructor != null) { |
||||
result = object.constructor.toString().indexOf("function BigNumber(") == 0; |
||||
} |
||||
} |
||||
|
||||
return result |
||||
}; |
||||
|
||||
function prettyPrint(/* */) { |
||||
var args = arguments; |
||||
var ret = ""; |
||||
for(var i = 0, l = args.length; i < l; i++) { |
||||
ret += pp(args[i], "") + "\n"; |
||||
} |
||||
return ret; |
||||
} |
||||
|
||||
var print = prettyPrint; |
||||
` |
@ -0,0 +1,220 @@ |
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package jsre |
||||
|
||||
import ( |
||||
"fmt" |
||||
"sort" |
||||
"strconv" |
||||
"strings" |
||||
|
||||
"github.com/fatih/color" |
||||
"github.com/robertkrimen/otto" |
||||
) |
||||
|
||||
const ( |
||||
maxPrettyPrintLevel = 3 |
||||
indentString = " " |
||||
) |
||||
|
||||
var ( |
||||
functionColor = color.New(color.FgMagenta) |
||||
specialColor = color.New(color.Bold) |
||||
numberColor = color.New(color.FgRed) |
||||
stringColor = color.New(color.FgGreen) |
||||
) |
||||
|
||||
// these fields are hidden when printing objects.
|
||||
var boringKeys = map[string]bool{ |
||||
"valueOf": true, |
||||
"toString": true, |
||||
"toLocaleString": true, |
||||
"hasOwnProperty": true, |
||||
"isPrototypeOf": true, |
||||
"propertyIsEnumerable": true, |
||||
"constructor": true, |
||||
} |
||||
|
||||
// prettyPrint writes value to standard output.
|
||||
func prettyPrint(vm *otto.Otto, value otto.Value) { |
||||
ppctx{vm}.printValue(value, 0) |
||||
} |
||||
|
||||
type ppctx struct{ vm *otto.Otto } |
||||
|
||||
func (ctx ppctx) indent(level int) string { |
||||
return strings.Repeat(indentString, level) |
||||
} |
||||
|
||||
func (ctx ppctx) printValue(v otto.Value, level int) { |
||||
switch { |
||||
case v.IsObject(): |
||||
ctx.printObject(v.Object(), level) |
||||
case v.IsNull(): |
||||
specialColor.Print("null") |
||||
case v.IsUndefined(): |
||||
specialColor.Print("undefined") |
||||
case v.IsString(): |
||||
s, _ := v.ToString() |
||||
stringColor.Printf("%q", s) |
||||
case v.IsBoolean(): |
||||
b, _ := v.ToBoolean() |
||||
specialColor.Printf("%t", b) |
||||
case v.IsNaN(): |
||||
numberColor.Printf("NaN") |
||||
case v.IsNumber(): |
||||
s, _ := v.ToString() |
||||
numberColor.Printf("%s", s) |
||||
default: |
||||
fmt.Printf("<unprintable>") |
||||
} |
||||
} |
||||
|
||||
func (ctx ppctx) printObject(obj *otto.Object, level int) { |
||||
switch obj.Class() { |
||||
case "Array": |
||||
lv, _ := obj.Get("length") |
||||
len, _ := lv.ToInteger() |
||||
if len == 0 { |
||||
fmt.Printf("[]") |
||||
return |
||||
} |
||||
if level > maxPrettyPrintLevel { |
||||
fmt.Print("[...]") |
||||
return |
||||
} |
||||
fmt.Print("[") |
||||
for i := int64(0); i < len; i++ { |
||||
el, err := obj.Get(strconv.FormatInt(i, 10)) |
||||
if err == nil { |
||||
ctx.printValue(el, level+1) |
||||
} |
||||
if i < len-1 { |
||||
fmt.Printf(", ") |
||||
} |
||||
} |
||||
fmt.Print("]") |
||||
|
||||
case "Object": |
||||
// Print values from bignumber.js as regular numbers.
|
||||
if ctx.isBigNumber(obj) { |
||||
numberColor.Print(toString(obj)) |
||||
return |
||||
} |
||||
// Otherwise, print all fields indented, but stop if we're too deep.
|
||||
keys := ctx.fields(obj) |
||||
if len(keys) == 0 { |
||||
fmt.Print("{}") |
||||
return |
||||
} |
||||
if level > maxPrettyPrintLevel { |
||||
fmt.Print("{...}") |
||||
return |
||||
} |
||||
fmt.Println("{") |
||||
for i, k := range keys { |
||||
v, _ := obj.Get(k) |
||||
fmt.Printf("%s%s: ", ctx.indent(level+1), k) |
||||
ctx.printValue(v, level+1) |
||||
if i < len(keys)-1 { |
||||
fmt.Printf(",") |
||||
} |
||||
fmt.Println() |
||||
} |
||||
fmt.Printf("%s}", ctx.indent(level)) |
||||
|
||||
case "Function": |
||||
// Use toString() to display the argument list if possible.
|
||||
if robj, err := obj.Call("toString"); err != nil { |
||||
functionColor.Print("function()") |
||||
} else { |
||||
desc := strings.Trim(strings.Split(robj.String(), "{")[0], " \t\n") |
||||
desc = strings.Replace(desc, " (", "(", 1) |
||||
functionColor.Print(desc) |
||||
} |
||||
|
||||
case "RegExp": |
||||
stringColor.Print(toString(obj)) |
||||
|
||||
default: |
||||
if v, _ := obj.Get("toString"); v.IsFunction() && level <= maxPrettyPrintLevel { |
||||
s, _ := obj.Call("toString") |
||||
fmt.Printf("<%s %s>", obj.Class(), s.String()) |
||||
} else { |
||||
fmt.Printf("<%s>", obj.Class()) |
||||
} |
||||
} |
||||
} |
||||
|
||||
func (ctx ppctx) fields(obj *otto.Object) []string { |
||||
var ( |
||||
vals, methods []string |
||||
seen = make(map[string]bool) |
||||
) |
||||
add := func(k string) { |
||||
if seen[k] || boringKeys[k] { |
||||
return |
||||
} |
||||
seen[k] = true |
||||
if v, _ := obj.Get(k); v.IsFunction() { |
||||
methods = append(methods, k) |
||||
} else { |
||||
vals = append(vals, k) |
||||
} |
||||
} |
||||
// add own properties
|
||||
ctx.doOwnProperties(obj.Value(), add) |
||||
// add properties of the constructor
|
||||
if cp := constructorPrototype(obj); cp != nil { |
||||
ctx.doOwnProperties(cp.Value(), add) |
||||
} |
||||
sort.Strings(vals) |
||||
sort.Strings(methods) |
||||
return append(vals, methods...) |
||||
} |
||||
|
||||
func (ctx ppctx) doOwnProperties(v otto.Value, f func(string)) { |
||||
Object, _ := ctx.vm.Object("Object") |
||||
rv, _ := Object.Call("getOwnPropertyNames", v) |
||||
gv, _ := rv.Export() |
||||
for _, v := range gv.([]interface{}) { |
||||
f(v.(string)) |
||||
} |
||||
} |
||||
|
||||
func (ctx ppctx) isBigNumber(v *otto.Object) bool { |
||||
BigNumber, err := ctx.vm.Run("BigNumber.prototype") |
||||
if err != nil { |
||||
panic(err) |
||||
} |
||||
cp := constructorPrototype(v) |
||||
return cp != nil && cp.Value() == BigNumber |
||||
} |
||||
|
||||
func toString(obj *otto.Object) string { |
||||
s, _ := obj.Call("toString") |
||||
return s.String() |
||||
} |
||||
|
||||
func constructorPrototype(obj *otto.Object) *otto.Object { |
||||
if v, _ := obj.Get("constructor"); v.Object() != nil { |
||||
if v, _ = v.Object().Get("prototype"); v.Object() != nil { |
||||
return v.Object() |
||||
} |
||||
} |
||||
return nil |
||||
} |
Loading…
Reference in new issue