mirror of https://github.com/cheat/cheat.git
Merge pull request #538 from chrisallenlane/improved-search
feat(search): issue #260
This commit is contained in:
commit
b13246978a
|
@ -7,6 +7,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/cheat/cheat/internal/config"
|
"github.com/cheat/cheat/internal/config"
|
||||||
|
"github.com/cheat/cheat/internal/sheet"
|
||||||
"github.com/cheat/cheat/internal/sheets"
|
"github.com/cheat/cheat/internal/sheets"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -35,6 +36,23 @@ func cmdSearch(opts map[string]interface{}, conf config.Config) {
|
||||||
// local cheatsheets)
|
// local cheatsheets)
|
||||||
consolidated := sheets.Consolidate(cheatsheets)
|
consolidated := sheets.Consolidate(cheatsheets)
|
||||||
|
|
||||||
|
// if <cheatsheet> was provided, search that single sheet only
|
||||||
|
if opts["<cheatsheet>"] != nil {
|
||||||
|
|
||||||
|
cheatsheet := opts["<cheatsheet>"].(string)
|
||||||
|
|
||||||
|
// assert that the cheatsheet exists
|
||||||
|
s, ok := consolidated[cheatsheet]
|
||||||
|
if !ok {
|
||||||
|
fmt.Printf("No cheatsheet found for '%s'.\n", cheatsheet)
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
consolidated = map[string]sheet.Sheet{
|
||||||
|
cheatsheet: s,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// sort the cheatsheets alphabetically, and search for matches
|
// sort the cheatsheets alphabetically, and search for matches
|
||||||
for _, sheet := range sheets.Sort(consolidated) {
|
for _, sheet := range sheets.Sort(consolidated) {
|
||||||
|
|
||||||
|
@ -53,17 +71,28 @@ func cmdSearch(opts map[string]interface{}, conf config.Config) {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// search the sheet
|
// `Search` will return text entries that match the search terms. We're
|
||||||
matches := sheet.Search(reg, conf.Color(opts))
|
// using it here to overwrite the prior cheatsheet Text, filtering it to
|
||||||
|
// only what is relevant
|
||||||
|
sheet.Text = sheet.Search(reg)
|
||||||
|
|
||||||
// display the results
|
// if the sheet did not match the search, ignore it and move on
|
||||||
if len(matches) > 0 {
|
if sheet.Text == "" {
|
||||||
fmt.Printf("%s:\n", sheet.Title)
|
continue
|
||||||
for _, m := range matches {
|
|
||||||
fmt.Printf(" %d: %s\n", m.Line, m.Text)
|
|
||||||
}
|
|
||||||
fmt.Print("\n")
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
// if colorization was requested, apply it here
|
||||||
|
if conf.Color(opts) {
|
||||||
|
sheet.Colorize(conf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// output the cheatsheet title
|
||||||
|
fmt.Printf("%s:\n", sheet.Title)
|
||||||
|
|
||||||
|
// indent each line of content with two spaces
|
||||||
|
for _, line := range strings.Split(sheet.Text, "\n") {
|
||||||
|
fmt.Printf(" %s\n", line)
|
||||||
|
}
|
||||||
|
fmt.Println("")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,6 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/alecthomas/chroma/quick"
|
|
||||||
|
|
||||||
"github.com/cheat/cheat/internal/config"
|
"github.com/cheat/cheat/internal/config"
|
||||||
"github.com/cheat/cheat/internal/sheets"
|
"github.com/cheat/cheat/internal/sheets"
|
||||||
)
|
)
|
||||||
|
@ -43,29 +41,11 @@ func cmdView(opts map[string]interface{}, conf config.Config) {
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !conf.Color(opts) {
|
// apply colorization if requested
|
||||||
fmt.Print(sheet.Text)
|
if conf.Color(opts) {
|
||||||
os.Exit(0)
|
sheet.Colorize(conf)
|
||||||
}
|
}
|
||||||
|
|
||||||
// otherwise, colorize the output
|
// display the cheatsheet
|
||||||
// if the syntax was not specified, default to bash
|
fmt.Print(sheet.Text)
|
||||||
lex := sheet.Syntax
|
|
||||||
if lex == "" {
|
|
||||||
lex = "bash"
|
|
||||||
}
|
|
||||||
|
|
||||||
// apply syntax highlighting
|
|
||||||
err = quick.Highlight(
|
|
||||||
os.Stdout,
|
|
||||||
sheet.Text,
|
|
||||||
lex,
|
|
||||||
conf.Formatter,
|
|
||||||
conf.Style,
|
|
||||||
)
|
|
||||||
|
|
||||||
// if colorization somehow failed, output non-colorized text
|
|
||||||
if err != nil {
|
|
||||||
fmt.Print(sheet.Text)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ import (
|
||||||
"github.com/cheat/cheat/internal/config"
|
"github.com/cheat/cheat/internal/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
const version = "3.5.1"
|
const version = "3.6.0"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
|
|
1
go.mod
1
go.mod
|
@ -7,7 +7,6 @@ require (
|
||||||
github.com/davecgh/go-spew v1.1.1
|
github.com/davecgh/go-spew v1.1.1
|
||||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815
|
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815
|
||||||
github.com/mattn/go-isatty v0.0.12
|
github.com/mattn/go-isatty v0.0.12
|
||||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b
|
|
||||||
github.com/mitchellh/go-homedir v1.1.0
|
github.com/mitchellh/go-homedir v1.1.0
|
||||||
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0
|
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0
|
||||||
gopkg.in/yaml.v2 v2.2.8
|
gopkg.in/yaml.v2 v2.2.8
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -22,8 +22,6 @@ github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs
|
||||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
|
|
||||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
|
||||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
package sheet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
|
||||||
|
"github.com/cheat/cheat/internal/config"
|
||||||
|
|
||||||
|
"github.com/alecthomas/chroma/quick"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Colorize applies syntax-highlighting to a cheatsheet's Text.
|
||||||
|
func (s *Sheet) Colorize(conf config.Config) {
|
||||||
|
|
||||||
|
// if the syntax was not specified, default to bash
|
||||||
|
lex := s.Syntax
|
||||||
|
if lex == "" {
|
||||||
|
lex = "bash"
|
||||||
|
}
|
||||||
|
|
||||||
|
// write colorized text into a buffer
|
||||||
|
var buf bytes.Buffer
|
||||||
|
err := quick.Highlight(
|
||||||
|
&buf,
|
||||||
|
s.Text,
|
||||||
|
lex,
|
||||||
|
conf.Formatter,
|
||||||
|
conf.Style,
|
||||||
|
)
|
||||||
|
|
||||||
|
// if colorization somehow failed, do nothing
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise, swap the cheatsheet's Text with its colorized equivalent
|
||||||
|
s.Text = buf.String()
|
||||||
|
}
|
|
@ -1,7 +0,0 @@
|
||||||
package sheet
|
|
||||||
|
|
||||||
// Match encapsulates search matches within cheatsheets
|
|
||||||
type Match struct {
|
|
||||||
Line int
|
|
||||||
Text string
|
|
||||||
}
|
|
|
@ -3,43 +3,22 @@ package sheet
|
||||||
import (
|
import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/mgutz/ansi"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Search searches for regexp matches in a cheatsheet's text, and optionally
|
// Search returns lines within a sheet's Text that match the search regex
|
||||||
// colorizes matching strings.
|
func (s *Sheet) Search(reg *regexp.Regexp) string {
|
||||||
func (s *Sheet) Search(reg *regexp.Regexp, colorize bool) []Match {
|
|
||||||
|
|
||||||
// record matches
|
// record matches
|
||||||
matches := []Match{}
|
matches := ""
|
||||||
|
|
||||||
// search through the cheatsheet's text line by line
|
// search through the cheatsheet's text line by line
|
||||||
// TODO: searching line-by-line is surely the "naive" approach. Revisit this
|
for _, line := range strings.Split(s.Text, "\n\n") {
|
||||||
// later with an eye for performance improvements.
|
|
||||||
for linenum, line := range strings.Split(s.Text, "\n") {
|
|
||||||
|
|
||||||
// exit early if the line doesn't match the regex
|
// exit early if the line doesn't match the regex
|
||||||
if !reg.MatchString(line) {
|
if reg.MatchString(line) {
|
||||||
continue
|
matches += line + "\n\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
// init the match
|
|
||||||
m := Match{
|
|
||||||
Line: linenum + 1,
|
|
||||||
Text: strings.TrimSpace(line),
|
|
||||||
}
|
|
||||||
|
|
||||||
// colorize the matching text if so configured
|
|
||||||
if colorize {
|
|
||||||
m.Text = reg.ReplaceAllStringFunc(m.Text, func(matched string) string {
|
|
||||||
return ansi.Color(matched, "red+b")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// record the match
|
|
||||||
matches = append(matches, m)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return matches
|
return strings.TrimSpace(matches)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,6 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestSearchNoMatch ensures that the expected output is returned when no
|
// TestSearchNoMatch ensures that the expected output is returned when no
|
||||||
|
@ -24,21 +22,21 @@ func TestSearchNoMatch(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// search the sheet
|
// search the sheet
|
||||||
matches := sheet.Search(reg, false)
|
matches := sheet.Search(reg)
|
||||||
|
|
||||||
// assert that no matches were found
|
// assert that no matches were found
|
||||||
if len(matches) != 0 {
|
if matches != "" {
|
||||||
t.Errorf("failure: expected no matches: got: %s", spew.Sdump(matches))
|
t.Errorf("failure: expected no matches: got: %s", matches)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestSearchSingleMatchNoColor asserts that the expected output is returned
|
// TestSearchSingleMatch asserts that the expected output is returned
|
||||||
// when a single match is returned, and no colorization is applied.
|
// when a single match is returned
|
||||||
func TestSearchSingleMatchNoColor(t *testing.T) {
|
func TestSearchSingleMatch(t *testing.T) {
|
||||||
|
|
||||||
// mock a cheatsheet
|
// mock a cheatsheet
|
||||||
sheet := Sheet{
|
sheet := Sheet{
|
||||||
Text: "The quick brown fox\njumped over\nthe lazy dog.",
|
Text: "The quick brown fox\njumped over\n\nthe lazy dog.",
|
||||||
}
|
}
|
||||||
|
|
||||||
// compile the search regex
|
// compile the search regex
|
||||||
|
@ -48,69 +46,28 @@ func TestSearchSingleMatchNoColor(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// search the sheet
|
// search the sheet
|
||||||
matches := sheet.Search(reg, false)
|
matches := sheet.Search(reg)
|
||||||
|
|
||||||
// specify the expected results
|
// specify the expected results
|
||||||
want := []Match{
|
want := "The quick brown fox\njumped over"
|
||||||
Match{
|
|
||||||
Line: 1,
|
|
||||||
Text: "The quick brown fox",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// assert that the correct matches were returned
|
// assert that the correct matches were returned
|
||||||
if !reflect.DeepEqual(matches, want) {
|
if matches != want {
|
||||||
t.Errorf(
|
t.Errorf(
|
||||||
"failed to return expected matches: want:\n%s, got:\n%s",
|
"failed to return expected matches: want:\n%s, got:\n%s",
|
||||||
spew.Sdump(want),
|
want,
|
||||||
spew.Sdump(matches),
|
matches,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestSearchSingleMatchColorized asserts that the expected output is returned
|
// TestSearchMultiMatch asserts that the expected output is returned
|
||||||
// when a single match is returned, and colorization is applied
|
// when a multiple matches are returned
|
||||||
func TestSearchSingleMatchColorized(t *testing.T) {
|
func TestSearchMultiMatch(t *testing.T) {
|
||||||
|
|
||||||
// mock a cheatsheet
|
// mock a cheatsheet
|
||||||
sheet := Sheet{
|
sheet := Sheet{
|
||||||
Text: "The quick brown fox\njumped over\nthe lazy dog.",
|
Text: "The quick brown fox\n\njumped over\n\nthe lazy dog.",
|
||||||
}
|
|
||||||
|
|
||||||
// compile the search regex
|
|
||||||
reg, err := regexp.Compile("(?i)fox")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("failed to compile regex: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// search the sheet
|
|
||||||
matches := sheet.Search(reg, true)
|
|
||||||
|
|
||||||
// specify the expected results
|
|
||||||
want := []Match{
|
|
||||||
Match{
|
|
||||||
Line: 1,
|
|
||||||
Text: "The quick brown \x1b[1;31mfox\x1b[0m",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// assert that the correct matches were returned
|
|
||||||
if !reflect.DeepEqual(matches, want) {
|
|
||||||
t.Errorf(
|
|
||||||
"failed to return expected matches: want:\n%s, got:\n%s",
|
|
||||||
spew.Sdump(want),
|
|
||||||
spew.Sdump(matches),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestSearchMultiMatchNoColor asserts that the expected output is returned
|
|
||||||
// when a multiple matches are returned, and no colorization is applied
|
|
||||||
func TestSearchMultiMatchNoColor(t *testing.T) {
|
|
||||||
|
|
||||||
// mock a cheatsheet
|
|
||||||
sheet := Sheet{
|
|
||||||
Text: "The quick brown fox\njumped over\nthe lazy dog.",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// compile the search regex
|
// compile the search regex
|
||||||
|
@ -120,66 +77,17 @@ func TestSearchMultiMatchNoColor(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// search the sheet
|
// search the sheet
|
||||||
matches := sheet.Search(reg, false)
|
matches := sheet.Search(reg)
|
||||||
|
|
||||||
// specify the expected results
|
// specify the expected results
|
||||||
want := []Match{
|
want := "The quick brown fox\n\nthe lazy dog."
|
||||||
Match{
|
|
||||||
Line: 1,
|
|
||||||
Text: "The quick brown fox",
|
|
||||||
},
|
|
||||||
Match{
|
|
||||||
Line: 3,
|
|
||||||
Text: "the lazy dog.",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// assert that the correct matches were returned
|
// assert that the correct matches were returned
|
||||||
if !reflect.DeepEqual(matches, want) {
|
if !reflect.DeepEqual(matches, want) {
|
||||||
t.Errorf(
|
t.Errorf(
|
||||||
"failed to return expected matches: want:\n%s, got:\n%s",
|
"failed to return expected matches: want:\n%s, got:\n%s",
|
||||||
spew.Sdump(want),
|
want,
|
||||||
spew.Sdump(matches),
|
matches,
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestSearchMultiMatchColorized asserts that the expected output is returned
|
|
||||||
// when a multiple matches are returned, and colorization is applied
|
|
||||||
func TestSearchMultiMatchColorized(t *testing.T) {
|
|
||||||
|
|
||||||
// mock a cheatsheet
|
|
||||||
sheet := Sheet{
|
|
||||||
Text: "The quick brown fox\njumped over\nthe lazy dog.",
|
|
||||||
}
|
|
||||||
|
|
||||||
// compile the search regex
|
|
||||||
reg, err := regexp.Compile("(?i)the")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("failed to compile regex: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// search the sheet
|
|
||||||
matches := sheet.Search(reg, true)
|
|
||||||
|
|
||||||
// specify the expected results
|
|
||||||
want := []Match{
|
|
||||||
Match{
|
|
||||||
Line: 1,
|
|
||||||
Text: "\x1b[1;31mThe\x1b[0m quick brown fox",
|
|
||||||
},
|
|
||||||
Match{
|
|
||||||
Line: 3,
|
|
||||||
Text: "\x1b[1;31mthe\x1b[0m lazy dog.",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// assert that the correct matches were returned
|
|
||||||
if !reflect.DeepEqual(matches, want) {
|
|
||||||
t.Errorf(
|
|
||||||
"failed to return expected matches: want:\n%s, got:\n%s",
|
|
||||||
spew.Sdump(want),
|
|
||||||
spew.Sdump(matches),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
language: go
|
|
||||||
go:
|
|
||||||
- tip
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
- go get github.com/mattn/goveralls
|
|
||||||
- go get golang.org/x/tools/cmd/cover
|
|
||||||
script:
|
|
||||||
- $HOME/gopath/bin/goveralls -repotoken xnXqRGwgW3SXIguzxf90ZSK1GPYZPaGrw
|
|
|
@ -1,21 +0,0 @@
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2016 Yasuhiro Matsumoto
|
|
||||||
|
|
||||||
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.
|
|
|
@ -1,48 +0,0 @@
|
||||||
# go-colorable
|
|
||||||
|
|
||||||
[![Godoc Reference](https://godoc.org/github.com/mattn/go-colorable?status.svg)](http://godoc.org/github.com/mattn/go-colorable)
|
|
||||||
[![Build Status](https://travis-ci.org/mattn/go-colorable.svg?branch=master)](https://travis-ci.org/mattn/go-colorable)
|
|
||||||
[![Coverage Status](https://coveralls.io/repos/github/mattn/go-colorable/badge.svg?branch=master)](https://coveralls.io/github/mattn/go-colorable?branch=master)
|
|
||||||
[![Go Report Card](https://goreportcard.com/badge/mattn/go-colorable)](https://goreportcard.com/report/mattn/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.SetFormatter(&logrus.TextFormatter{ForceColors: true})
|
|
||||||
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,29 +0,0 @@
|
||||||
// +build appengine
|
|
||||||
|
|
||||||
package colorable
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
_ "github.com/mattn/go-isatty"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewColorable return new instance of Writer which handle escape sequence.
|
|
||||||
func NewColorable(file *os.File) io.Writer {
|
|
||||||
if file == nil {
|
|
||||||
panic("nil passed instead of *os.File to NewColorable()")
|
|
||||||
}
|
|
||||||
|
|
||||||
return file
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewColorableStdout return new instance of Writer which handle escape sequence for stdout.
|
|
||||||
func NewColorableStdout() io.Writer {
|
|
||||||
return os.Stdout
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewColorableStderr return new instance of Writer which handle escape sequence for stderr.
|
|
||||||
func NewColorableStderr() io.Writer {
|
|
||||||
return os.Stderr
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
// +build !windows
|
|
||||||
// +build !appengine
|
|
||||||
|
|
||||||
package colorable
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
_ "github.com/mattn/go-isatty"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewColorable return new instance of Writer which handle escape sequence.
|
|
||||||
func NewColorable(file *os.File) io.Writer {
|
|
||||||
if file == nil {
|
|
||||||
panic("nil passed instead of *os.File to NewColorable()")
|
|
||||||
}
|
|
||||||
|
|
||||||
return file
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewColorableStdout return new instance of Writer which handle escape sequence for stdout.
|
|
||||||
func NewColorableStdout() io.Writer {
|
|
||||||
return os.Stdout
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewColorableStderr return new instance of Writer which handle escape sequence for stderr.
|
|
||||||
func NewColorableStderr() io.Writer {
|
|
||||||
return os.Stderr
|
|
||||||
}
|
|
|
@ -1,884 +0,0 @@
|
||||||
// +build windows
|
|
||||||
// +build !appengine
|
|
||||||
|
|
||||||
package colorable
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"io"
|
|
||||||
"math"
|
|
||||||
"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
|
|
||||||
}
|
|
||||||
|
|
||||||
type consoleCursorInfo struct {
|
|
||||||
size dword
|
|
||||||
visible int32
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
|
||||||
procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
|
|
||||||
procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute")
|
|
||||||
procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition")
|
|
||||||
procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW")
|
|
||||||
procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute")
|
|
||||||
procGetConsoleCursorInfo = kernel32.NewProc("GetConsoleCursorInfo")
|
|
||||||
procSetConsoleCursorInfo = kernel32.NewProc("SetConsoleCursorInfo")
|
|
||||||
procSetConsoleTitle = kernel32.NewProc("SetConsoleTitleW")
|
|
||||||
)
|
|
||||||
|
|
||||||
// Writer provide colorable Writer to the console
|
|
||||||
type Writer struct {
|
|
||||||
out io.Writer
|
|
||||||
handle syscall.Handle
|
|
||||||
oldattr word
|
|
||||||
oldpos coord
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewColorable return new instance of Writer which handle escape sequence from File.
|
|
||||||
func NewColorable(file *os.File) io.Writer {
|
|
||||||
if file == nil {
|
|
||||||
panic("nil passed instead of *os.File to NewColorable()")
|
|
||||||
}
|
|
||||||
|
|
||||||
if isatty.IsTerminal(file.Fd()) {
|
|
||||||
var csbi consoleScreenBufferInfo
|
|
||||||
handle := syscall.Handle(file.Fd())
|
|
||||||
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
|
||||||
return &Writer{out: file, handle: handle, oldattr: csbi.attributes, oldpos: coord{0, 0}}
|
|
||||||
}
|
|
||||||
return file
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewColorableStdout return new instance of Writer which handle escape sequence for stdout.
|
|
||||||
func NewColorableStdout() io.Writer {
|
|
||||||
return NewColorable(os.Stdout)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewColorableStderr return new instance of Writer which handle escape sequence for stderr.
|
|
||||||
func NewColorableStderr() io.Writer {
|
|
||||||
return NewColorable(os.Stderr)
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
|
|
||||||
// `\033]0;TITLESTR\007`
|
|
||||||
func doTitleSequence(er *bytes.Reader) error {
|
|
||||||
var c byte
|
|
||||||
var err error
|
|
||||||
|
|
||||||
c, err = er.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if c != '0' && c != '2' {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
c, err = er.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if c != ';' {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
title := make([]byte, 0, 80)
|
|
||||||
for {
|
|
||||||
c, err = er.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if c == 0x07 || c == '\n' {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
title = append(title, c)
|
|
||||||
}
|
|
||||||
if len(title) > 0 {
|
|
||||||
title8, err := syscall.UTF16PtrFromString(string(title))
|
|
||||||
if err == nil {
|
|
||||||
procSetConsoleTitle.Call(uintptr(unsafe.Pointer(title8)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write write data on console
|
|
||||||
func (w *Writer) Write(data []byte) (n int, err error) {
|
|
||||||
var csbi consoleScreenBufferInfo
|
|
||||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
|
||||||
|
|
||||||
er := bytes.NewReader(data)
|
|
||||||
var bw [1]byte
|
|
||||||
loop:
|
|
||||||
for {
|
|
||||||
c1, err := er.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
break loop
|
|
||||||
}
|
|
||||||
if c1 != 0x1b {
|
|
||||||
bw[0] = c1
|
|
||||||
w.out.Write(bw[:])
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
c2, err := er.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
break loop
|
|
||||||
}
|
|
||||||
|
|
||||||
if c2 == ']' {
|
|
||||||
if err := doTitleSequence(er); err != nil {
|
|
||||||
break loop
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if c2 != 0x5b {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var buf bytes.Buffer
|
|
||||||
var m byte
|
|
||||||
for {
|
|
||||||
c, err := er.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
break loop
|
|
||||||
}
|
|
||||||
if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' {
|
|
||||||
m = c
|
|
||||||
break
|
|
||||||
}
|
|
||||||
buf.Write([]byte(string(c)))
|
|
||||||
}
|
|
||||||
|
|
||||||
switch m {
|
|
||||||
case 'A':
|
|
||||||
n, err = strconv.Atoi(buf.String())
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
|
||||||
csbi.cursorPosition.y -= short(n)
|
|
||||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
|
||||||
case 'B':
|
|
||||||
n, err = strconv.Atoi(buf.String())
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
|
||||||
csbi.cursorPosition.y += short(n)
|
|
||||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
|
||||||
case 'C':
|
|
||||||
n, err = strconv.Atoi(buf.String())
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
|
||||||
csbi.cursorPosition.x += short(n)
|
|
||||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
|
||||||
case 'D':
|
|
||||||
n, err = strconv.Atoi(buf.String())
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
|
||||||
csbi.cursorPosition.x -= short(n)
|
|
||||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
|
||||||
case 'E':
|
|
||||||
n, err = strconv.Atoi(buf.String())
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
|
||||||
csbi.cursorPosition.x = 0
|
|
||||||
csbi.cursorPosition.y += short(n)
|
|
||||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
|
||||||
case 'F':
|
|
||||||
n, err = strconv.Atoi(buf.String())
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
|
||||||
csbi.cursorPosition.x = 0
|
|
||||||
csbi.cursorPosition.y -= short(n)
|
|
||||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
|
||||||
case 'G':
|
|
||||||
n, err = strconv.Atoi(buf.String())
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
|
||||||
csbi.cursorPosition.x = short(n - 1)
|
|
||||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
|
||||||
case 'H', 'f':
|
|
||||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
|
||||||
if buf.Len() > 0 {
|
|
||||||
token := strings.Split(buf.String(), ";")
|
|
||||||
switch len(token) {
|
|
||||||
case 1:
|
|
||||||
n1, err := strconv.Atoi(token[0])
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
csbi.cursorPosition.y = short(n1 - 1)
|
|
||||||
case 2:
|
|
||||||
n1, err := strconv.Atoi(token[0])
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
n2, err := strconv.Atoi(token[1])
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
csbi.cursorPosition.x = short(n2 - 1)
|
|
||||||
csbi.cursorPosition.y = short(n1 - 1)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
csbi.cursorPosition.y = 0
|
|
||||||
}
|
|
||||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
|
||||||
case 'J':
|
|
||||||
n := 0
|
|
||||||
if buf.Len() > 0 {
|
|
||||||
n, err = strconv.Atoi(buf.String())
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var count, written dword
|
|
||||||
var cursor coord
|
|
||||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
|
||||||
switch n {
|
|
||||||
case 0:
|
|
||||||
cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y}
|
|
||||||
count = dword(csbi.size.x - csbi.cursorPosition.x + (csbi.size.y-csbi.cursorPosition.y)*csbi.size.x)
|
|
||||||
case 1:
|
|
||||||
cursor = coord{x: csbi.window.left, y: csbi.window.top}
|
|
||||||
count = dword(csbi.size.x - csbi.cursorPosition.x + (csbi.window.top-csbi.cursorPosition.y)*csbi.size.x)
|
|
||||||
case 2:
|
|
||||||
cursor = coord{x: csbi.window.left, y: csbi.window.top}
|
|
||||||
count = dword(csbi.size.x - csbi.cursorPosition.x + (csbi.size.y-csbi.cursorPosition.y)*csbi.size.x)
|
|
||||||
}
|
|
||||||
procFillConsoleOutputCharacter.Call(uintptr(w.handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
|
|
||||||
procFillConsoleOutputAttribute.Call(uintptr(w.handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
|
|
||||||
case 'K':
|
|
||||||
n := 0
|
|
||||||
if buf.Len() > 0 {
|
|
||||||
n, err = strconv.Atoi(buf.String())
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
|
||||||
var cursor coord
|
|
||||||
var count, written dword
|
|
||||||
switch n {
|
|
||||||
case 0:
|
|
||||||
cursor = coord{x: csbi.cursorPosition.x + 1, y: csbi.cursorPosition.y}
|
|
||||||
count = dword(csbi.size.x - csbi.cursorPosition.x - 1)
|
|
||||||
case 1:
|
|
||||||
cursor = coord{x: csbi.window.left, y: csbi.window.top + csbi.cursorPosition.y}
|
|
||||||
count = dword(csbi.size.x - csbi.cursorPosition.x)
|
|
||||||
case 2:
|
|
||||||
cursor = coord{x: csbi.window.left, y: csbi.window.top + csbi.cursorPosition.y}
|
|
||||||
count = dword(csbi.size.x)
|
|
||||||
}
|
|
||||||
procFillConsoleOutputCharacter.Call(uintptr(w.handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
|
|
||||||
procFillConsoleOutputAttribute.Call(uintptr(w.handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
|
|
||||||
case 'm':
|
|
||||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
|
||||||
attr := csbi.attributes
|
|
||||||
cs := buf.String()
|
|
||||||
if cs == "" {
|
|
||||||
procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(w.oldattr))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
token := strings.Split(cs, ";")
|
|
||||||
for i := 0; i < len(token); i++ {
|
|
||||||
ns := token[i]
|
|
||||||
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 n == 22 || n == 25:
|
|
||||||
attr |= foregroundIntensity
|
|
||||||
case n == 27:
|
|
||||||
attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4)
|
|
||||||
case 30 <= n && n <= 37:
|
|
||||||
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" || token[i+1] == "05") {
|
|
||||||
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 &= 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
|
|
||||||
case 90 <= n && n <= 97:
|
|
||||||
attr = (attr & backgroundMask)
|
|
||||||
attr |= foregroundIntensity
|
|
||||||
if (n-90)&1 != 0 {
|
|
||||||
attr |= foregroundRed
|
|
||||||
}
|
|
||||||
if (n-90)&2 != 0 {
|
|
||||||
attr |= foregroundGreen
|
|
||||||
}
|
|
||||||
if (n-90)&4 != 0 {
|
|
||||||
attr |= foregroundBlue
|
|
||||||
}
|
|
||||||
case 100 <= n && n <= 107:
|
|
||||||
attr = (attr & foregroundMask)
|
|
||||||
attr |= backgroundIntensity
|
|
||||||
if (n-100)&1 != 0 {
|
|
||||||
attr |= backgroundRed
|
|
||||||
}
|
|
||||||
if (n-100)&2 != 0 {
|
|
||||||
attr |= backgroundGreen
|
|
||||||
}
|
|
||||||
if (n-100)&4 != 0 {
|
|
||||||
attr |= backgroundBlue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(attr))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 'h':
|
|
||||||
var ci consoleCursorInfo
|
|
||||||
cs := buf.String()
|
|
||||||
if cs == "5>" {
|
|
||||||
procGetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
|
||||||
ci.visible = 0
|
|
||||||
procSetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
|
||||||
} else if cs == "?25" {
|
|
||||||
procGetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
|
||||||
ci.visible = 1
|
|
||||||
procSetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
|
||||||
}
|
|
||||||
case 'l':
|
|
||||||
var ci consoleCursorInfo
|
|
||||||
cs := buf.String()
|
|
||||||
if cs == "5>" {
|
|
||||||
procGetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
|
||||||
ci.visible = 1
|
|
||||||
procSetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
|
||||||
} else if cs == "?25" {
|
|
||||||
procGetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
|
||||||
ci.visible = 0
|
|
||||||
procSetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
|
||||||
}
|
|
||||||
case 's':
|
|
||||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
|
||||||
w.oldpos = csbi.cursorPosition
|
|
||||||
case 'u':
|
|
||||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&w.oldpos)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return len(data), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type consoleColor struct {
|
|
||||||
rgb int
|
|
||||||
red bool
|
|
||||||
green bool
|
|
||||||
blue bool
|
|
||||||
intensity bool
|
|
||||||
}
|
|
||||||
|
|
||||||
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 color16 = []consoleColor{
|
|
||||||
{0x000000, false, false, false, false},
|
|
||||||
{0x000080, false, false, true, false},
|
|
||||||
{0x008000, false, true, false, false},
|
|
||||||
{0x008080, false, true, true, false},
|
|
||||||
{0x800000, true, false, false, false},
|
|
||||||
{0x800080, true, false, true, false},
|
|
||||||
{0x808000, true, true, false, false},
|
|
||||||
{0xc0c0c0, true, true, true, false},
|
|
||||||
{0x808080, false, false, false, true},
|
|
||||||
{0x0000ff, false, false, true, true},
|
|
||||||
{0x00ff00, false, true, false, true},
|
|
||||||
{0x00ffff, false, true, true, true},
|
|
||||||
{0xff0000, true, false, false, true},
|
|
||||||
{0xff00ff, true, false, true, true},
|
|
||||||
{0xffff00, true, true, false, true},
|
|
||||||
{0xffffff, true, true, true, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
type hsv struct {
|
|
||||||
h, s, v float32
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a hsv) dist(b hsv) float32 {
|
|
||||||
dh := a.h - b.h
|
|
||||||
switch {
|
|
||||||
case dh > 0.5:
|
|
||||||
dh = 1 - dh
|
|
||||||
case dh < -0.5:
|
|
||||||
dh = -1 - dh
|
|
||||||
}
|
|
||||||
ds := a.s - b.s
|
|
||||||
dv := a.v - b.v
|
|
||||||
return float32(math.Sqrt(float64(dh*dh + ds*ds + dv*dv)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func toHSV(rgb int) hsv {
|
|
||||||
r, g, b := float32((rgb&0xFF0000)>>16)/256.0,
|
|
||||||
float32((rgb&0x00FF00)>>8)/256.0,
|
|
||||||
float32(rgb&0x0000FF)/256.0
|
|
||||||
min, max := minmax3f(r, g, b)
|
|
||||||
h := max - min
|
|
||||||
if h > 0 {
|
|
||||||
if max == r {
|
|
||||||
h = (g - b) / h
|
|
||||||
if h < 0 {
|
|
||||||
h += 6
|
|
||||||
}
|
|
||||||
} else if max == g {
|
|
||||||
h = 2 + (b-r)/h
|
|
||||||
} else {
|
|
||||||
h = 4 + (r-g)/h
|
|
||||||
}
|
|
||||||
}
|
|
||||||
h /= 6.0
|
|
||||||
s := max - min
|
|
||||||
if max != 0 {
|
|
||||||
s /= max
|
|
||||||
}
|
|
||||||
v := max
|
|
||||||
return hsv{h: h, s: s, v: v}
|
|
||||||
}
|
|
||||||
|
|
||||||
type hsvTable []hsv
|
|
||||||
|
|
||||||
func toHSVTable(rgbTable []consoleColor) hsvTable {
|
|
||||||
t := make(hsvTable, len(rgbTable))
|
|
||||||
for i, c := range rgbTable {
|
|
||||||
t[i] = toHSV(c.rgb)
|
|
||||||
}
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t hsvTable) find(rgb int) consoleColor {
|
|
||||||
hsv := toHSV(rgb)
|
|
||||||
n := 7
|
|
||||||
l := float32(5.0)
|
|
||||||
for i, p := range t {
|
|
||||||
d := hsv.dist(p)
|
|
||||||
if d < l {
|
|
||||||
l, n = d, i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return color16[n]
|
|
||||||
}
|
|
||||||
|
|
||||||
func minmax3f(a, b, c float32) (min, max float32) {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var n256foreAttr []word
|
|
||||||
var n256backAttr []word
|
|
||||||
|
|
||||||
func n256setup() {
|
|
||||||
n256foreAttr = make([]word, 256)
|
|
||||||
n256backAttr = make([]word, 256)
|
|
||||||
t := toHSVTable(color16)
|
|
||||||
for i, rgb := range color256 {
|
|
||||||
c := t.find(rgb)
|
|
||||||
n256foreAttr[i] = c.foregroundAttr()
|
|
||||||
n256backAttr[i] = c.backgroundAttr()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,55 +0,0 @@
|
||||||
package colorable
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NonColorable hold writer but remove escape sequence.
|
|
||||||
type NonColorable struct {
|
|
||||||
out io.Writer
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewNonColorable return new instance of Writer which remove escape sequence from Writer.
|
|
||||||
func NewNonColorable(w io.Writer) io.Writer {
|
|
||||||
return &NonColorable{out: w}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write write data on console
|
|
||||||
func (w *NonColorable) Write(data []byte) (n int, err error) {
|
|
||||||
er := bytes.NewReader(data)
|
|
||||||
var bw [1]byte
|
|
||||||
loop:
|
|
||||||
for {
|
|
||||||
c1, err := er.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
break loop
|
|
||||||
}
|
|
||||||
if c1 != 0x1b {
|
|
||||||
bw[0] = c1
|
|
||||||
w.out.Write(bw[:])
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
c2, err := er.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
break loop
|
|
||||||
}
|
|
||||||
if c2 != 0x5b {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var buf bytes.Buffer
|
|
||||||
for {
|
|
||||||
c, err := er.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
break loop
|
|
||||||
}
|
|
||||||
if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
buf.Write([]byte(string(c)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return len(data), nil
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
*.test
|
|
|
@ -1,9 +0,0 @@
|
||||||
The MIT License (MIT)
|
|
||||||
Copyright (c) 2013 Mario L. Gutierrez
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
|
@ -1,121 +0,0 @@
|
||||||
# ansi
|
|
||||||
|
|
||||||
Package ansi is a small, fast library to create ANSI colored strings and codes.
|
|
||||||
|
|
||||||
## Install
|
|
||||||
|
|
||||||
Get it
|
|
||||||
|
|
||||||
```sh
|
|
||||||
go get -u github.com/mgutz/ansi
|
|
||||||
```
|
|
||||||
|
|
||||||
## Example
|
|
||||||
|
|
||||||
```go
|
|
||||||
import "github.com/mgutz/ansi"
|
|
||||||
|
|
||||||
// colorize a string, SLOW
|
|
||||||
msg := ansi.Color("foo", "red+b:white")
|
|
||||||
|
|
||||||
// create a FAST closure function to avoid computation of ANSI code
|
|
||||||
phosphorize := ansi.ColorFunc("green+h:black")
|
|
||||||
msg = phosphorize("Bring back the 80s!")
|
|
||||||
msg2 := phospohorize("Look, I'm a CRT!")
|
|
||||||
|
|
||||||
// cache escape codes and build strings manually
|
|
||||||
lime := ansi.ColorCode("green+h:black")
|
|
||||||
reset := ansi.ColorCode("reset")
|
|
||||||
|
|
||||||
fmt.Println(lime, "Bring back the 80s!", reset)
|
|
||||||
```
|
|
||||||
|
|
||||||
Other examples
|
|
||||||
|
|
||||||
```go
|
|
||||||
Color(s, "red") // red
|
|
||||||
Color(s, "red+b") // red bold
|
|
||||||
Color(s, "red+B") // red blinking
|
|
||||||
Color(s, "red+u") // red underline
|
|
||||||
Color(s, "red+bh") // red bold bright
|
|
||||||
Color(s, "red:white") // red on white
|
|
||||||
Color(s, "red+b:white+h") // red bold on white bright
|
|
||||||
Color(s, "red+B:white+h") // red blink on white bright
|
|
||||||
Color(s, "off") // turn off ansi codes
|
|
||||||
```
|
|
||||||
|
|
||||||
To view color combinations, from project directory in terminal.
|
|
||||||
|
|
||||||
```sh
|
|
||||||
go test
|
|
||||||
```
|
|
||||||
|
|
||||||
## Style format
|
|
||||||
|
|
||||||
```go
|
|
||||||
"foregroundColor+attributes:backgroundColor+attributes"
|
|
||||||
```
|
|
||||||
|
|
||||||
Colors
|
|
||||||
|
|
||||||
* black
|
|
||||||
* red
|
|
||||||
* green
|
|
||||||
* yellow
|
|
||||||
* blue
|
|
||||||
* magenta
|
|
||||||
* cyan
|
|
||||||
* white
|
|
||||||
* 0...255 (256 colors)
|
|
||||||
|
|
||||||
Foreground Attributes
|
|
||||||
|
|
||||||
* B = Blink
|
|
||||||
* b = bold
|
|
||||||
* h = high intensity (bright)
|
|
||||||
* i = inverse
|
|
||||||
* s = strikethrough
|
|
||||||
* u = underline
|
|
||||||
|
|
||||||
Background Attributes
|
|
||||||
|
|
||||||
* h = high intensity (bright)
|
|
||||||
|
|
||||||
## Constants
|
|
||||||
|
|
||||||
* ansi.Reset
|
|
||||||
* ansi.DefaultBG
|
|
||||||
* ansi.DefaultFG
|
|
||||||
* ansi.Black
|
|
||||||
* ansi.Red
|
|
||||||
* ansi.Green
|
|
||||||
* ansi.Yellow
|
|
||||||
* ansi.Blue
|
|
||||||
* ansi.Magenta
|
|
||||||
* ansi.Cyan
|
|
||||||
* ansi.White
|
|
||||||
* ansi.LightBlack
|
|
||||||
* ansi.LightRed
|
|
||||||
* ansi.LightGreen
|
|
||||||
* ansi.LightYellow
|
|
||||||
* ansi.LightBlue
|
|
||||||
* ansi.LightMagenta
|
|
||||||
* ansi.LightCyan
|
|
||||||
* ansi.LightWhite
|
|
||||||
|
|
||||||
## References
|
|
||||||
|
|
||||||
Wikipedia ANSI escape codes [Colors](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors)
|
|
||||||
|
|
||||||
General [tips and formatting](http://misc.flogisoft.com/bash/tip_colors_and_formatting)
|
|
||||||
|
|
||||||
What about support on Windows? Use [colorable by mattn](https://github.com/mattn/go-colorable).
|
|
||||||
Ansi and colorable are used by [logxi](https://github.com/mgutz/logxi) to support logging in
|
|
||||||
color on Windows.
|
|
||||||
|
|
||||||
## MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2013 Mario Gutierrez mario@mgutz.com
|
|
||||||
|
|
||||||
See the file LICENSE for copying permission.
|
|
||||||
|
|
|
@ -1,285 +0,0 @@
|
||||||
package ansi
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
black = iota
|
|
||||||
red
|
|
||||||
green
|
|
||||||
yellow
|
|
||||||
blue
|
|
||||||
magenta
|
|
||||||
cyan
|
|
||||||
white
|
|
||||||
defaultt = 9
|
|
||||||
|
|
||||||
normalIntensityFG = 30
|
|
||||||
highIntensityFG = 90
|
|
||||||
normalIntensityBG = 40
|
|
||||||
highIntensityBG = 100
|
|
||||||
|
|
||||||
start = "\033["
|
|
||||||
bold = "1;"
|
|
||||||
blink = "5;"
|
|
||||||
underline = "4;"
|
|
||||||
inverse = "7;"
|
|
||||||
strikethrough = "9;"
|
|
||||||
|
|
||||||
// Reset is the ANSI reset escape sequence
|
|
||||||
Reset = "\033[0m"
|
|
||||||
// DefaultBG is the default background
|
|
||||||
DefaultBG = "\033[49m"
|
|
||||||
// DefaultFG is the default foreground
|
|
||||||
DefaultFG = "\033[39m"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Black FG
|
|
||||||
var Black string
|
|
||||||
|
|
||||||
// Red FG
|
|
||||||
var Red string
|
|
||||||
|
|
||||||
// Green FG
|
|
||||||
var Green string
|
|
||||||
|
|
||||||
// Yellow FG
|
|
||||||
var Yellow string
|
|
||||||
|
|
||||||
// Blue FG
|
|
||||||
var Blue string
|
|
||||||
|
|
||||||
// Magenta FG
|
|
||||||
var Magenta string
|
|
||||||
|
|
||||||
// Cyan FG
|
|
||||||
var Cyan string
|
|
||||||
|
|
||||||
// White FG
|
|
||||||
var White string
|
|
||||||
|
|
||||||
// LightBlack FG
|
|
||||||
var LightBlack string
|
|
||||||
|
|
||||||
// LightRed FG
|
|
||||||
var LightRed string
|
|
||||||
|
|
||||||
// LightGreen FG
|
|
||||||
var LightGreen string
|
|
||||||
|
|
||||||
// LightYellow FG
|
|
||||||
var LightYellow string
|
|
||||||
|
|
||||||
// LightBlue FG
|
|
||||||
var LightBlue string
|
|
||||||
|
|
||||||
// LightMagenta FG
|
|
||||||
var LightMagenta string
|
|
||||||
|
|
||||||
// LightCyan FG
|
|
||||||
var LightCyan string
|
|
||||||
|
|
||||||
// LightWhite FG
|
|
||||||
var LightWhite string
|
|
||||||
|
|
||||||
var (
|
|
||||||
plain = false
|
|
||||||
// Colors maps common color names to their ANSI color code.
|
|
||||||
Colors = map[string]int{
|
|
||||||
"black": black,
|
|
||||||
"red": red,
|
|
||||||
"green": green,
|
|
||||||
"yellow": yellow,
|
|
||||||
"blue": blue,
|
|
||||||
"magenta": magenta,
|
|
||||||
"cyan": cyan,
|
|
||||||
"white": white,
|
|
||||||
"default": defaultt,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
for i := 0; i < 256; i++ {
|
|
||||||
Colors[strconv.Itoa(i)] = i
|
|
||||||
}
|
|
||||||
|
|
||||||
Black = ColorCode("black")
|
|
||||||
Red = ColorCode("red")
|
|
||||||
Green = ColorCode("green")
|
|
||||||
Yellow = ColorCode("yellow")
|
|
||||||
Blue = ColorCode("blue")
|
|
||||||
Magenta = ColorCode("magenta")
|
|
||||||
Cyan = ColorCode("cyan")
|
|
||||||
White = ColorCode("white")
|
|
||||||
LightBlack = ColorCode("black+h")
|
|
||||||
LightRed = ColorCode("red+h")
|
|
||||||
LightGreen = ColorCode("green+h")
|
|
||||||
LightYellow = ColorCode("yellow+h")
|
|
||||||
LightBlue = ColorCode("blue+h")
|
|
||||||
LightMagenta = ColorCode("magenta+h")
|
|
||||||
LightCyan = ColorCode("cyan+h")
|
|
||||||
LightWhite = ColorCode("white+h")
|
|
||||||
}
|
|
||||||
|
|
||||||
// ColorCode returns the ANSI color color code for style.
|
|
||||||
func ColorCode(style string) string {
|
|
||||||
return colorCode(style).String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gets the ANSI color code for a style.
|
|
||||||
func colorCode(style string) *bytes.Buffer {
|
|
||||||
buf := bytes.NewBufferString("")
|
|
||||||
if plain || style == "" {
|
|
||||||
return buf
|
|
||||||
}
|
|
||||||
if style == "reset" {
|
|
||||||
buf.WriteString(Reset)
|
|
||||||
return buf
|
|
||||||
} else if style == "off" {
|
|
||||||
return buf
|
|
||||||
}
|
|
||||||
|
|
||||||
foregroundBackground := strings.Split(style, ":")
|
|
||||||
foreground := strings.Split(foregroundBackground[0], "+")
|
|
||||||
fgKey := foreground[0]
|
|
||||||
fg := Colors[fgKey]
|
|
||||||
fgStyle := ""
|
|
||||||
if len(foreground) > 1 {
|
|
||||||
fgStyle = foreground[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
bg, bgStyle := "", ""
|
|
||||||
|
|
||||||
if len(foregroundBackground) > 1 {
|
|
||||||
background := strings.Split(foregroundBackground[1], "+")
|
|
||||||
bg = background[0]
|
|
||||||
if len(background) > 1 {
|
|
||||||
bgStyle = background[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buf.WriteString(start)
|
|
||||||
base := normalIntensityFG
|
|
||||||
if len(fgStyle) > 0 {
|
|
||||||
if strings.Contains(fgStyle, "b") {
|
|
||||||
buf.WriteString(bold)
|
|
||||||
}
|
|
||||||
if strings.Contains(fgStyle, "B") {
|
|
||||||
buf.WriteString(blink)
|
|
||||||
}
|
|
||||||
if strings.Contains(fgStyle, "u") {
|
|
||||||
buf.WriteString(underline)
|
|
||||||
}
|
|
||||||
if strings.Contains(fgStyle, "i") {
|
|
||||||
buf.WriteString(inverse)
|
|
||||||
}
|
|
||||||
if strings.Contains(fgStyle, "s") {
|
|
||||||
buf.WriteString(strikethrough)
|
|
||||||
}
|
|
||||||
if strings.Contains(fgStyle, "h") {
|
|
||||||
base = highIntensityFG
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if 256-color
|
|
||||||
n, err := strconv.Atoi(fgKey)
|
|
||||||
if err == nil {
|
|
||||||
fmt.Fprintf(buf, "38;5;%d;", n)
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(buf, "%d;", base+fg)
|
|
||||||
}
|
|
||||||
|
|
||||||
base = normalIntensityBG
|
|
||||||
if len(bg) > 0 {
|
|
||||||
if strings.Contains(bgStyle, "h") {
|
|
||||||
base = highIntensityBG
|
|
||||||
}
|
|
||||||
// if 256-color
|
|
||||||
n, err := strconv.Atoi(bg)
|
|
||||||
if err == nil {
|
|
||||||
fmt.Fprintf(buf, "48;5;%d;", n)
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(buf, "%d;", base+Colors[bg])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove last ";"
|
|
||||||
buf.Truncate(buf.Len() - 1)
|
|
||||||
buf.WriteRune('m')
|
|
||||||
return buf
|
|
||||||
}
|
|
||||||
|
|
||||||
// Color colors a string based on the ANSI color code for style.
|
|
||||||
func Color(s, style string) string {
|
|
||||||
if plain || len(style) < 1 {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
buf := colorCode(style)
|
|
||||||
buf.WriteString(s)
|
|
||||||
buf.WriteString(Reset)
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ColorFunc creates a closure to avoid computation ANSI color code.
|
|
||||||
func ColorFunc(style string) func(string) string {
|
|
||||||
if style == "" {
|
|
||||||
return func(s string) string {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
color := ColorCode(style)
|
|
||||||
return func(s string) string {
|
|
||||||
if plain || s == "" {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
buf := bytes.NewBufferString(color)
|
|
||||||
buf.WriteString(s)
|
|
||||||
buf.WriteString(Reset)
|
|
||||||
result := buf.String()
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DisableColors disables ANSI color codes. The default is false (colors are on).
|
|
||||||
func DisableColors(disable bool) {
|
|
||||||
plain = disable
|
|
||||||
if plain {
|
|
||||||
Black = ""
|
|
||||||
Red = ""
|
|
||||||
Green = ""
|
|
||||||
Yellow = ""
|
|
||||||
Blue = ""
|
|
||||||
Magenta = ""
|
|
||||||
Cyan = ""
|
|
||||||
White = ""
|
|
||||||
LightBlack = ""
|
|
||||||
LightRed = ""
|
|
||||||
LightGreen = ""
|
|
||||||
LightYellow = ""
|
|
||||||
LightBlue = ""
|
|
||||||
LightMagenta = ""
|
|
||||||
LightCyan = ""
|
|
||||||
LightWhite = ""
|
|
||||||
} else {
|
|
||||||
Black = ColorCode("black")
|
|
||||||
Red = ColorCode("red")
|
|
||||||
Green = ColorCode("green")
|
|
||||||
Yellow = ColorCode("yellow")
|
|
||||||
Blue = ColorCode("blue")
|
|
||||||
Magenta = ColorCode("magenta")
|
|
||||||
Cyan = ColorCode("cyan")
|
|
||||||
White = ColorCode("white")
|
|
||||||
LightBlack = ColorCode("black+h")
|
|
||||||
LightRed = ColorCode("red+h")
|
|
||||||
LightGreen = ColorCode("green+h")
|
|
||||||
LightYellow = ColorCode("yellow+h")
|
|
||||||
LightBlue = ColorCode("blue+h")
|
|
||||||
LightMagenta = ColorCode("magenta+h")
|
|
||||||
LightCyan = ColorCode("cyan+h")
|
|
||||||
LightWhite = ColorCode("white+h")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,65 +0,0 @@
|
||||||
/*
|
|
||||||
Package ansi is a small, fast library to create ANSI colored strings and codes.
|
|
||||||
|
|
||||||
Installation
|
|
||||||
|
|
||||||
# this installs the color viewer and the package
|
|
||||||
go get -u github.com/mgutz/ansi/cmd/ansi-mgutz
|
|
||||||
|
|
||||||
Example
|
|
||||||
|
|
||||||
// colorize a string, SLOW
|
|
||||||
msg := ansi.Color("foo", "red+b:white")
|
|
||||||
|
|
||||||
// create a closure to avoid recalculating ANSI code compilation
|
|
||||||
phosphorize := ansi.ColorFunc("green+h:black")
|
|
||||||
msg = phosphorize("Bring back the 80s!")
|
|
||||||
msg2 := phospohorize("Look, I'm a CRT!")
|
|
||||||
|
|
||||||
// cache escape codes and build strings manually
|
|
||||||
lime := ansi.ColorCode("green+h:black")
|
|
||||||
reset := ansi.ColorCode("reset")
|
|
||||||
|
|
||||||
fmt.Println(lime, "Bring back the 80s!", reset)
|
|
||||||
|
|
||||||
Other examples
|
|
||||||
|
|
||||||
Color(s, "red") // red
|
|
||||||
Color(s, "red+b") // red bold
|
|
||||||
Color(s, "red+B") // red blinking
|
|
||||||
Color(s, "red+u") // red underline
|
|
||||||
Color(s, "red+bh") // red bold bright
|
|
||||||
Color(s, "red:white") // red on white
|
|
||||||
Color(s, "red+b:white+h") // red bold on white bright
|
|
||||||
Color(s, "red+B:white+h") // red blink on white bright
|
|
||||||
|
|
||||||
To view color combinations, from terminal
|
|
||||||
|
|
||||||
ansi-mgutz
|
|
||||||
|
|
||||||
Style format
|
|
||||||
|
|
||||||
"foregroundColor+attributes:backgroundColor+attributes"
|
|
||||||
|
|
||||||
Colors
|
|
||||||
|
|
||||||
black
|
|
||||||
red
|
|
||||||
green
|
|
||||||
yellow
|
|
||||||
blue
|
|
||||||
magenta
|
|
||||||
cyan
|
|
||||||
white
|
|
||||||
|
|
||||||
Attributes
|
|
||||||
|
|
||||||
b = bold foreground
|
|
||||||
B = Blink foreground
|
|
||||||
u = underline foreground
|
|
||||||
h = high intensity (bright) foreground, background
|
|
||||||
i = inverse
|
|
||||||
|
|
||||||
Wikipedia ANSI escape codes [Colors](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors)
|
|
||||||
*/
|
|
||||||
package ansi
|
|
|
@ -1,57 +0,0 @@
|
||||||
package ansi
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"sort"
|
|
||||||
|
|
||||||
colorable "github.com/mattn/go-colorable"
|
|
||||||
)
|
|
||||||
|
|
||||||
// PrintStyles prints all style combinations to the terminal.
|
|
||||||
func PrintStyles() {
|
|
||||||
// for compatibility with Windows, not needed for *nix
|
|
||||||
stdout := colorable.NewColorableStdout()
|
|
||||||
|
|
||||||
bgColors := []string{
|
|
||||||
"",
|
|
||||||
":black",
|
|
||||||
":red",
|
|
||||||
":green",
|
|
||||||
":yellow",
|
|
||||||
":blue",
|
|
||||||
":magenta",
|
|
||||||
":cyan",
|
|
||||||
":white",
|
|
||||||
}
|
|
||||||
|
|
||||||
keys := make([]string, 0, len(Colors))
|
|
||||||
for k := range Colors {
|
|
||||||
keys = append(keys, k)
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Sort(sort.StringSlice(keys))
|
|
||||||
|
|
||||||
for _, fg := range keys {
|
|
||||||
for _, bg := range bgColors {
|
|
||||||
fmt.Fprintln(stdout, padColor(fg, []string{"" + bg, "+b" + bg, "+bh" + bg, "+u" + bg}))
|
|
||||||
fmt.Fprintln(stdout, padColor(fg, []string{"+s" + bg, "+i" + bg}))
|
|
||||||
fmt.Fprintln(stdout, padColor(fg, []string{"+uh" + bg, "+B" + bg, "+Bb" + bg /* backgrounds */, "" + bg + "+h"}))
|
|
||||||
fmt.Fprintln(stdout, padColor(fg, []string{"+b" + bg + "+h", "+bh" + bg + "+h", "+u" + bg + "+h", "+uh" + bg + "+h"}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func pad(s string, length int) string {
|
|
||||||
for len(s) < length {
|
|
||||||
s += " "
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func padColor(color string, styles []string) string {
|
|
||||||
buffer := ""
|
|
||||||
for _, style := range styles {
|
|
||||||
buffer += Color(pad(color+style, 20), color+style)
|
|
||||||
}
|
|
||||||
return buffer
|
|
||||||
}
|
|
|
@ -41,12 +41,8 @@ github.com/dlclark/regexp2
|
||||||
github.com/dlclark/regexp2/syntax
|
github.com/dlclark/regexp2/syntax
|
||||||
# github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815
|
# github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815
|
||||||
github.com/docopt/docopt-go
|
github.com/docopt/docopt-go
|
||||||
# github.com/mattn/go-colorable v0.0.9
|
|
||||||
github.com/mattn/go-colorable
|
|
||||||
# github.com/mattn/go-isatty v0.0.12
|
# github.com/mattn/go-isatty v0.0.12
|
||||||
github.com/mattn/go-isatty
|
github.com/mattn/go-isatty
|
||||||
# github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b
|
|
||||||
github.com/mgutz/ansi
|
|
||||||
# github.com/mitchellh/go-homedir v1.1.0
|
# github.com/mitchellh/go-homedir v1.1.0
|
||||||
github.com/mitchellh/go-homedir
|
github.com/mitchellh/go-homedir
|
||||||
# golang.org/x/sys v0.0.0-20200116001909-b77594299b42
|
# golang.org/x/sys v0.0.0-20200116001909-b77594299b42
|
||||||
|
|
Loading…
Reference in New Issue