From 8bc75011debe3dfaf5ed9d274bf9de84083eb4ca Mon Sep 17 00:00:00 2001 From: Sam Shen Date: Wed, 4 Dec 2019 13:21:09 -0800 Subject: [PATCH] initial code --- .buildkite/pipeline.yaml | 23 ++++++ README.md | 19 ++++- colorize.go | 163 +++++++++++++++++++++++++++++++++++++++ colorize_test.go | 81 +++++++++++++++++++ default_styles.png | Bin 0 -> 32926 bytes go.mod | 10 +++ go.sum | 14 ++++ screenshot.png | Bin 0 -> 21106 bytes 8 files changed, 309 insertions(+), 1 deletion(-) create mode 100644 .buildkite/pipeline.yaml create mode 100644 colorize.go create mode 100644 colorize_test.go create mode 100644 default_styles.png create mode 100644 go.mod create mode 100644 go.sum create mode 100644 screenshot.png diff --git a/.buildkite/pipeline.yaml b/.buildkite/pipeline.yaml new file mode 100644 index 0000000..a08e9dd --- /dev/null +++ b/.buildkite/pipeline.yaml @@ -0,0 +1,23 @@ +# Copyright 2019 Soluble Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +steps: + - label: ":rocket: Go" + command: "golangci-lint run -E gofmt -E stylecheck && go test -cover" + plugins: + - docker#v3.3.0: + image: gcr.io/soluble-ci/go-jnode-ci:latest + always-pull: true + shell: [ "/bin/bash", "-e", "-c" ] + propagate-uid-gid: true \ No newline at end of file diff --git a/README.md b/README.md index eca0b90..a67b568 100644 --- a/README.md +++ b/README.md @@ -1 +1,18 @@ -# go-colorize \ No newline at end of file +# go-colorize + +Go from this: + +```go +Colorize("\n{primary:%s} eat {bg-success:pizza :pizza:}{bg-primary: and drink }{warning:beer} %s, but not {bg-danger:%s}\n\n", + "Most folks like to", ":beer:", "everyone") +``` + +To this: + +![Screenshot](screenshot.png) + +Works with https://github.com/kyokomi/emoji and https://github.com/fatih/color. + +Default styles are: + +![Default Styles](default_styles.png) diff --git a/colorize.go b/colorize.go new file mode 100644 index 0000000..8d48f80 --- /dev/null +++ b/colorize.go @@ -0,0 +1,163 @@ +// Copyright 2019 Soluble Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/* +colorize does ANSI colored output and emoji substitution. + +Colorize takes a fmt.Printf() template string and looks for styles +in the format: + + {style-name:text} + +And inserts the color esacpe sequences around the text. It then passes +the template off to emoji.Sprintf() from https://github.com/kyokomi/emoji. + +It uses the color attribute names, color.Output, and color.NoColor from +https://github.com/fatih/color. + +The style names are modeled off the basic bootstrap color names. + +Example use: + + colorize.Colorize("{primary:We} want {bg-success:beer :beer:} and {warning:pizza} :pizza:") + +*/ +package colorize + +import ( + "fmt" + "strconv" + "strings" + + "github.com/fatih/color" + "github.com/kyokomi/emoji" +) + +const escape = "\x1b" + +// Style is a ANSI escape code sequence +type Style struct { + sequence string + enable *bool +} + +// NewStyle creates a style from a set of attributes +func NewStyle(attributes ...color.Attribute) *Style { + codes := make([]string, len(attributes)) + for i, attr := range attributes { + codes[i] = strconv.Itoa(int(attr)) + } + sequence := fmt.Sprintf("%s[%sm", escape, strings.Join(codes, ";")) + return &Style{sequence, nil} +} + +// Styles maps style names to styles +var Styles = map[string]*Style{ + "primary": NewStyle(color.FgHiBlue), + "secondary": NewStyle(color.FgBlue), + "light": NewStyle(color.BgHiBlack), + "info": nil, + "success": NewStyle(color.FgGreen), + "warning": NewStyle(color.FgYellow), + "danger": NewStyle(color.FgHiRed), + "bg-primary": NewStyle(color.FgHiWhite, color.BgHiBlue), + "bg-secondary": NewStyle(color.FgHiWhite, color.BgBlue), + "bg-success": NewStyle(color.FgHiWhite, color.BgGreen), + "bg-warning": NewStyle(color.FgBlack, color.BgYellow), + "bg-danger": NewStyle(color.FgBlack, color.BgHiRed), +} +var reset = NewStyle(color.Reset) + +// IsEnabled returns true if the style is enabled, either specifically +// to this style or globally via color.NoColor +func (style *Style) IsEnabled() bool { + if style == nil { + return false + } + if style.enable != nil { + return *style.enable + } + return !color.NoColor +} + +// Enable or disable this style +func (style *Style) Enable(val bool) *Style { + style.enable = &val + return style +} + +// Colorize some text, printing the output to color.Output +func Colorize(template string, values ...interface{}) { + fmt.Fprint(color.Output, SColorize(template, values...)) +} + +// Colorize some text, returning a string +func SColorize(template string, values ...interface{}) string { + var b strings.Builder + state := ' ' + var nameStart int + var style *Style + var frag strings.Builder + for i, ch := range template { + switch state { + case ' ': + if ch == '{' { + nameStart = i + state = ch + } else { + b.WriteRune(ch) + if ch == '\\' { + state = ch + } + } + case '\\': + b.WriteRune(ch) + state = ' ' + case '{': + if ch == ':' { + name := template[nameStart+1 : i] + style = Styles[name] + frag.Reset() + state = '}' + } + case '}': + if ch == '\\' { + state = ']' + } else if ch == '}' { + var enabled = style.IsEnabled() + if enabled { + b.WriteString(style.sequence) + } + b.WriteString(frag.String()) + if enabled { + b.WriteString(reset.sequence) + } + state = ' ' + } else { + frag.WriteRune(ch) + } + case ']': + frag.WriteRune(ch) + state = '}' + + } + } + switch state { + case '{', '}', ']': + // if we found a '{' w/o completion, append everything from the start + b.WriteString(template[nameStart:]) + } + template = b.String() + return emoji.Sprintf(template, values...) +} diff --git a/colorize_test.go b/colorize_test.go new file mode 100644 index 0000000..0ddd352 --- /dev/null +++ b/colorize_test.go @@ -0,0 +1,81 @@ +// Copyright 2019 Soluble Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package colorize + +import ( + "fmt" + "github.com/fatih/color" + "sort" + "strings" + "testing" +) + +func TestDemoDefaults(t *testing.T) { + t.SkipNow() + var template strings.Builder + params := make([]interface{}, 0, len(Styles)) + names := make([]string, 0, len(Styles)) + for k := range Styles { + names = append(names, k) + } + sort.Strings(names) + for _, k := range names { + template.WriteString(fmt.Sprintf("{%s: %%12s }\n", k)) + params = append(params, k) + } + Colorize(template.String(), params...) + Colorize("\n{primary:%s} eat {bg-success:pizza :pizza:}{bg-primary: and drink }{warning:beer} %s, but not {bg-danger:%s}\n\n", + "Most folks like to", ":beer:", "everyone") +} + +func TestBasic(t *testing.T) { + s := SColorize("{primary:%s} %s", "hello", "world") + if !strings.Contains(s, "hello") || !strings.Contains(s, "world") { + t.Errorf("string contents wrong") + } + Styles["primary"].Enable(false) + if Styles["primary"].IsEnabled() { + t.Error("primary is enabled") + } + if Styles["nope"].IsEnabled() { + t.Error("nope is enabled") + } + if SColorize("{primary:hello} world") != "hello world" { + t.Error("disable style didn't work") + } + color.Output = &strings.Builder{} + Colorize("hello, world") + if color.Output.(*strings.Builder).String() != "hello, world" { + t.Error("string didn't output") + } +} + +func TestEscapes(t *testing.T) { + if e := SColorize("\\\n\r\t\v"); e != "\\\n\r\t\v" { + t.Errorf("escape sequences wrong: %s", e) + } +} + +func TestIncomplete(t *testing.T) { + if s := SColorize("{primary:foo"); s != "{primary:foo" { + t.Error(s) + } + if s := SColorize("{nope:xxx\\}}"); s != "xxx}" { + t.Error(s) + } + if s := SColorize("{nope:foo\\ bar"); s != "{nope:foo\\ bar" { + t.Error(s) + } +} diff --git a/default_styles.png b/default_styles.png new file mode 100644 index 0000000000000000000000000000000000000000..4cdcfa8b1cedcdf7d6129946770ec8d841ffdc64 GIT binary patch literal 32926 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 literal 0 HcmV?d00001 diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..bdb1e21 --- /dev/null +++ b/go.mod @@ -0,0 +1,10 @@ +module github.com/soluble-ai/go-colorize + +go 1.13 + +require ( + github.com/fatih/color v1.7.0 + github.com/kyokomi/emoji v2.1.0+incompatible + github.com/mattn/go-colorable v0.1.4 // indirect + github.com/mattn/go-isatty v0.0.10 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..4fb48c2 --- /dev/null +++ b/go.sum @@ -0,0 +1,14 @@ +github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/kyokomi/emoji v2.1.0+incompatible h1:+DYU2RgpI6OHG4oQkM5KlqD3Wd3UPEsX8jamTo1Mp6o= +github.com/kyokomi/emoji v2.1.0+incompatible/go.mod h1:mZ6aGCD7yk8j6QY6KICwnZ2pxoszVseX1DNoGtU2tBA= +github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20191008105621-543471e840be h1:QAcqgptGM8IQBC9K/RC4o+O9YmqEm0diQn9QmZw/0mU= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/screenshot.png b/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..799a353094ef6d6ba0a16f327a1d46017e3db009 GIT binary patch literal 21106 zcmZ^~19&FQ)+qW06MJGzY}=aHwr$(CZD(TJwrz7_TPNS%`^P=^-hQ6yT3t(3t7?@y zOjcS1777yz006*>i3-XC03a>jFck#Y_gjVpIROBGku(z!kQEaUz>~GNF*dU_0sutA zl2pJo6GX7OT@2%EfDvO6ACZh8Kw}a6!twAF5Rzg+k%S{5`3IvCDO8(7{h=$v$