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