Add extended and true colors (#3)
Co-authored-by: jolheiser <john.olheiser@gmail.com> Reviewed-on: https://gitea.com/jolheiser/beaver/pulls/3 Co-authored-by: John Olheiser <john.olheiser@gmail.com> Co-committed-by: John Olheiser <john.olheiser@gmail.com>pull/4/head v1.1.0
parent
5e1722bd43
commit
c628fd1987
|
@ -1,23 +0,0 @@
|
|||
linters:
|
||||
enable:
|
||||
- deadcode
|
||||
- dogsled
|
||||
- dupl
|
||||
- errcheck
|
||||
- funlen
|
||||
- gocognit
|
||||
- goconst
|
||||
- gocritic
|
||||
- gocyclo
|
||||
- gofmt
|
||||
- golint
|
||||
- gosimple
|
||||
- govet
|
||||
- misspell
|
||||
- prealloc
|
||||
- staticcheck
|
||||
- structcheck
|
||||
- typecheck
|
||||
- unparam
|
||||
- unused
|
||||
- varcheck
|
|
@ -0,0 +1,7 @@
|
|||
Copyright 2020 John Olheiser
|
||||
|
||||
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.
|
8
Makefile
8
Makefile
|
@ -1,13 +1,5 @@
|
|||
GO ?= go
|
||||
|
||||
.PHONY: lint
|
||||
lint:
|
||||
@hash golangci-lint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
export BINARY="golangci-lint"; \
|
||||
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.23.1; \
|
||||
fi
|
||||
golangci-lint run --timeout 5m
|
||||
|
||||
.PHONY: fmt
|
||||
fmt:
|
||||
$(GO) fmt ./...
|
||||
|
|
|
@ -50,4 +50,8 @@ Beaver allows you to customize the colors of various parts of the message
|
|||
color.Trace = color.New(color.FgMagenta)
|
||||
```
|
||||
|
||||
[More info for the `color` package](color/README.md)
|
||||
[More info for the `color` package](color/README.md)
|
||||
|
||||
## LICENSE
|
||||
|
||||
[MIT](LICENSE)
|
|
@ -104,4 +104,12 @@ func main() {
|
|||
color.Time = color.New(color.FgCyan)
|
||||
color.Stack = color.New(color.FgGreen)
|
||||
b.Info("Full options emulating Gitea")
|
||||
|
||||
// Extended example
|
||||
ex := color.NewExtended(color.Gold1, color.DarkGreen)
|
||||
fmt.Println(ex.Format("Gold on Dark Green"))
|
||||
|
||||
// True color example
|
||||
birb := color.NewTrue(color.MustParseHex("#007D96"), &color.RGB{})
|
||||
fmt.Println(birb.Format("Birb blue on black"))
|
||||
}
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
Beaver comes with the `color` sub-package that can be used even without Beaver calls.
|
||||
|
||||
`Color` is an interface that simply needs `Format(text string) string` to fulfill it.
|
||||
Any logger in beaver can be set to a `Color`.
|
||||
|
||||
## Formatting a string with a single attribute
|
||||
```go
|
||||
text := color.FgRed.Format("red")
|
||||
|
@ -12,34 +15,10 @@ text := color.FgRed.Format("red")
|
|||
text := color.New(color.BgGreen, color.FgRed, color.Bold).Format("green background, red text, and bold")
|
||||
```
|
||||
|
||||
## Formatting strings with multiple colors
|
||||
The following are different ways to print `This is a color test!`
|
||||
where the word `color` is red while everything else is `green`.
|
||||
## Extended colors
|
||||
|
||||
### setup
|
||||
Assuming each example is preceded by
|
||||
```go
|
||||
green := color.New(color.FgGreen)
|
||||
red := color.New(color.FgRed)
|
||||
```
|
||||
Extended colors are 256-color extensions. They can be referred to by name, available in [extended_colors.go](extended_colors.go).
|
||||
|
||||
#### pure `fmt.Println`
|
||||
```go
|
||||
fmt.Println(green.Format("This is a"), red.Format("color"), green.Format("test!"))
|
||||
```
|
||||
## True colors
|
||||
|
||||
#### `color.Wrap`
|
||||
```go
|
||||
fmt.Println(green.Wrap("This is a #{color} test!", red))
|
||||
```
|
||||
|
||||
#### string formatting with `fmt.Printf` and `color.Wrap`
|
||||
```go
|
||||
fmt.Printf(green.Wrap("This is a #{%s} test!\n", red), "color")
|
||||
```
|
||||
|
||||
#### using a different directive prefix
|
||||
```go
|
||||
color.DirectivePrefix('$')
|
||||
fmt.Printf(green.Wrap("This is a ${%s} test!\n", red), "color")
|
||||
```
|
||||
True colors are full RBG colors, they can be either created with RGB values or parsed from hex.
|
|
@ -1,87 +0,0 @@
|
|||
package color
|
||||
|
||||
// Attribute defines a single SGR Code
|
||||
type Attribute int
|
||||
|
||||
// Logger 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
|
||||
)
|
||||
|
||||
// Foreground Hi-Intensity text colors
|
||||
const (
|
||||
FgHiBlack Attribute = iota + 90
|
||||
FgHiRed
|
||||
FgHiGreen
|
||||
FgHiYellow
|
||||
FgHiBlue
|
||||
FgHiMagenta
|
||||
FgHiCyan
|
||||
FgHiWhite
|
||||
)
|
||||
|
||||
// Background text colors
|
||||
const (
|
||||
BgBlack Attribute = iota + 40
|
||||
BgRed
|
||||
BgGreen
|
||||
BgYellow
|
||||
BgBlue
|
||||
BgMagenta
|
||||
BgCyan
|
||||
BgWhite
|
||||
)
|
||||
|
||||
// Background Hi-Intensity text colors
|
||||
const (
|
||||
BgHiBlack Attribute = iota + 100
|
||||
BgHiRed
|
||||
BgHiGreen
|
||||
BgHiYellow
|
||||
BgHiBlue
|
||||
BgHiMagenta
|
||||
BgHiCyan
|
||||
BgHiWhite
|
||||
)
|
||||
|
||||
var attrCache = make(map[string]*Color)
|
||||
|
||||
func attr(a Attribute) *Color {
|
||||
if c, ok := attrCache[string(a)]; ok {
|
||||
return c
|
||||
}
|
||||
c := New(a)
|
||||
attrCache[string(a)] = c
|
||||
return c
|
||||
}
|
||||
|
||||
// Format is a quick way to format a string using a single attribute
|
||||
func (a Attribute) Format(text string) string {
|
||||
return attr(a).Format(text)
|
||||
}
|
||||
|
||||
// Format is a quick way to format a formatted string using a single attribute
|
||||
func (a Attribute) Formatf(text string, v ...interface{}) string {
|
||||
return attr(a).Formatf(text, v...)
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
package color
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Interface guard
|
||||
var _ Color = &Basic{}
|
||||
|
||||
// Basic defines a custom color object which is defined by SGR parameters.
|
||||
type Basic struct {
|
||||
Attrs []BasicAttribute
|
||||
}
|
||||
|
||||
// BasicAttribute defines a single SGR Code
|
||||
type BasicAttribute int
|
||||
|
||||
// Logger attributes
|
||||
const (
|
||||
Reset BasicAttribute = iota
|
||||
Bold
|
||||
Faint
|
||||
Italic
|
||||
Underline
|
||||
BlinkSlow
|
||||
BlinkRapid
|
||||
ReverseVideo
|
||||
Concealed
|
||||
CrossedOut
|
||||
)
|
||||
|
||||
// New returns a new Color with attrs Attributes
|
||||
func New(attrs ...BasicAttribute) *Basic {
|
||||
return &Basic{Attrs: attrs}
|
||||
}
|
||||
|
||||
// Add adds new Attributes to a Color
|
||||
func (b *Basic) Add(attrs ...BasicAttribute) {
|
||||
for _, attr := range attrs {
|
||||
has := false
|
||||
for _, att := range b.Attrs {
|
||||
if attr == att {
|
||||
has = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !has {
|
||||
b.Attrs = append(b.Attrs, attr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// String returns a string representation of the sum of a Color's Attributes
|
||||
func (b *Basic) String() string {
|
||||
attrs := 0
|
||||
for _, attr := range b.Attrs {
|
||||
attrs += int(attr)
|
||||
}
|
||||
return fmt.Sprintf("%d", attrs)
|
||||
}
|
||||
|
||||
func (b *Basic) sequence() string {
|
||||
format := make([]string, len(b.Attrs))
|
||||
for i, v := range b.Attrs {
|
||||
format[i] = strconv.Itoa(int(v))
|
||||
}
|
||||
|
||||
return strings.Join(format, ";")
|
||||
}
|
||||
|
||||
func (b *Basic) wrap(s string) string {
|
||||
return b.format() + s + b.unformat()
|
||||
}
|
||||
|
||||
func (b *Basic) format() string {
|
||||
return fmt.Sprintf("%s[%sm", escape, b.sequence())
|
||||
}
|
||||
|
||||
func (b *Basic) unformat() string {
|
||||
return fmt.Sprintf("%s[%dm", escape, Reset)
|
||||
}
|
||||
|
||||
func (b *Basic) Format(text string) string {
|
||||
if len(b.Attrs) > 0 {
|
||||
return b.wrap(text)
|
||||
}
|
||||
return text
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
package color
|
||||
|
||||
// Foreground text colors
|
||||
const (
|
||||
FgBlack BasicAttribute = iota + 30
|
||||
FgRed
|
||||
FgGreen
|
||||
FgYellow
|
||||
FgBlue
|
||||
FgMagenta
|
||||
FgCyan
|
||||
FgWhite
|
||||
)
|
||||
|
||||
// Foreground Hi-Intensity text colors
|
||||
const (
|
||||
FgHiBlack BasicAttribute = iota + 90
|
||||
FgHiRed
|
||||
FgHiGreen
|
||||
FgHiYellow
|
||||
FgHiBlue
|
||||
FgHiMagenta
|
||||
FgHiCyan
|
||||
FgHiWhite
|
||||
)
|
||||
|
||||
// Background text colors
|
||||
const (
|
||||
BgBlack BasicAttribute = iota + 40
|
||||
BgRed
|
||||
BgGreen
|
||||
BgYellow
|
||||
BgBlue
|
||||
BgMagenta
|
||||
BgCyan
|
||||
BgWhite
|
||||
)
|
||||
|
||||
// Background Hi-Intensity text colors
|
||||
const (
|
||||
BgHiBlack BasicAttribute = iota + 100
|
||||
BgHiRed
|
||||
BgHiGreen
|
||||
BgHiYellow
|
||||
BgHiBlue
|
||||
BgHiMagenta
|
||||
BgHiCyan
|
||||
BgHiWhite
|
||||
)
|
|
@ -0,0 +1,38 @@
|
|||
package color
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
var color = New()
|
||||
|
||||
func TestColor(t *testing.T) {
|
||||
tt := []struct {
|
||||
Name string
|
||||
SetAttrs []BasicAttribute
|
||||
AddAttrs []BasicAttribute
|
||||
Size int
|
||||
}{
|
||||
{"Init", nil, nil, 0},
|
||||
{"Set 2", []BasicAttribute{Bold, FgBlack}, nil, 2},
|
||||
{"Add 2", nil, []BasicAttribute{Italic, BgWhite}, 4},
|
||||
{"Set 3", []BasicAttribute{Bold, FgBlack, BgWhite}, nil, 3},
|
||||
{"Add 3", nil, []BasicAttribute{Italic, FgWhite, BgBlack}, 6},
|
||||
{"Add Same", nil, []BasicAttribute{Italic, FgWhite, BgBlack}, 6},
|
||||
{"Add 2 Same", nil, []BasicAttribute{Italic, FgWhite, BgGreen}, 7},
|
||||
}
|
||||
|
||||
for _, tc := range tt {
|
||||
t.Run(tc.Name, func(t *testing.T) {
|
||||
if tc.SetAttrs != nil {
|
||||
color.Attrs = tc.SetAttrs
|
||||
}
|
||||
if tc.AddAttrs != nil {
|
||||
color.Add(tc.AddAttrs...)
|
||||
}
|
||||
if len(color.Attrs) != tc.Size {
|
||||
t.Logf("color has `%d` attributes, but should have `%d`", len(color.Attrs), tc.Size)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
101
color/color.go
101
color/color.go
|
@ -1,112 +1,17 @@
|
|||
package color
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const escape = "\x1b"
|
||||
|
||||
var (
|
||||
directive = `{[^}]*}`
|
||||
directiveRe = regexp.MustCompile("[#]" + directive)
|
||||
)
|
||||
|
||||
// DirectivePrefix changes the directive prefix for parsing color wrap directives
|
||||
func DirectivePrefix(p rune) {
|
||||
directiveRe = regexp.MustCompile("[" + string(p) + "]" + directive)
|
||||
}
|
||||
|
||||
// Color defines a custom color object which is defined by SGR parameters.
|
||||
type Color struct {
|
||||
Attrs []Attribute
|
||||
}
|
||||
|
||||
// New returns a new Color with attrs Attributes
|
||||
func New(attrs ...Attribute) *Color {
|
||||
return &Color{Attrs: attrs}
|
||||
}
|
||||
|
||||
// Add adds new Attributes to a Color
|
||||
func (c *Color) Add(attrs ...Attribute) {
|
||||
for _, attr := range attrs {
|
||||
has := false
|
||||
for _, att := range c.Attrs {
|
||||
if attr == att {
|
||||
has = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !has {
|
||||
c.Attrs = append(c.Attrs, attr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// String returns a string representation of the sum of a Color's Attributes
|
||||
func (c *Color) String() string {
|
||||
attrs := 0
|
||||
for _, attr := range c.Attrs {
|
||||
attrs += int(attr)
|
||||
}
|
||||
return fmt.Sprintf("%d", attrs)
|
||||
}
|
||||
|
||||
func (c *Color) sequence() string {
|
||||
format := make([]string, len(c.Attrs))
|
||||
for i, v := range c.Attrs {
|
||||
format[i] = strconv.Itoa(int(v))
|
||||
}
|
||||
|
||||
return strings.Join(format, ";")
|
||||
}
|
||||
|
||||
func (c *Color) wrap(s string) string {
|
||||
return c.format() + s + unformat()
|
||||
}
|
||||
|
||||
func (c *Color) format() string {
|
||||
return fmt.Sprintf("%s[%sm", escape, c.sequence())
|
||||
}
|
||||
|
||||
func unformat() string {
|
||||
return fmt.Sprintf("%s[%dm", escape, Reset)
|
||||
}
|
||||
|
||||
// Format returns a string wrapped in the color
|
||||
func (c *Color) Format(text string) string {
|
||||
if len(c.Attrs) > 0 {
|
||||
return c.wrap(text)
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
// Formatf returns a formatted string wrapped in the color
|
||||
func (c *Color) Formatf(format string, v ...interface{}) string {
|
||||
return c.Format(fmt.Sprintf(format, v...))
|
||||
}
|
||||
|
||||
func parse(text string, primary, secondary *Color) string {
|
||||
for {
|
||||
loc := directiveRe.FindStringIndex(text)
|
||||
if loc == nil {
|
||||
break
|
||||
}
|
||||
text = text[:loc[0]] + unformat() + secondary.Format(text[loc[0]+2:loc[1]-1]) + primary.format() + text[loc[1]:]
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
// Wrap returns a string wrapped in a primary color, with #{} directives wrapped in a secondary color
|
||||
func (c *Color) Wrap(text string, secondary *Color) string {
|
||||
text = parse(text, c, secondary)
|
||||
return c.Format(text)
|
||||
type Color interface {
|
||||
Format(text string) string
|
||||
}
|
||||
|
||||
// ParseLevel parses a string and returns a Beaver Level's Color, defaulting to Info
|
||||
func ParseLevel(level string) *Color {
|
||||
func ParseLevel(level string) Color {
|
||||
switch strings.ToUpper(level) {
|
||||
case "T", "TRACE":
|
||||
return Trace
|
||||
|
|
|
@ -5,47 +5,14 @@ import (
|
|||
"testing"
|
||||
)
|
||||
|
||||
var color = New()
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
func TestColor(t *testing.T) {
|
||||
tt := []struct {
|
||||
Name string
|
||||
SetAttrs []Attribute
|
||||
AddAttrs []Attribute
|
||||
Size int
|
||||
}{
|
||||
{"Init", nil, nil, 0},
|
||||
{"Set 2", []Attribute{Bold, FgBlack}, nil, 2},
|
||||
{"Add 2", nil, []Attribute{Italic, BgWhite}, 4},
|
||||
{"Set 3", []Attribute{Bold, FgBlack, BgWhite}, nil, 3},
|
||||
{"Add 3", nil, []Attribute{Italic, FgWhite, BgBlack}, 6},
|
||||
{"Add Same", nil, []Attribute{Italic, FgWhite, BgBlack}, 6},
|
||||
{"Add 2 Same", nil, []Attribute{Italic, FgWhite, BgGreen}, 7},
|
||||
}
|
||||
|
||||
for _, tc := range tt {
|
||||
t.Run(tc.Name, func(t *testing.T) {
|
||||
if tc.SetAttrs != nil {
|
||||
color.Attrs = tc.SetAttrs
|
||||
}
|
||||
if tc.AddAttrs != nil {
|
||||
color.Add(tc.AddAttrs...)
|
||||
}
|
||||
if len(color.Attrs) != tc.Size {
|
||||
t.Logf("color has `%d` attributes, but should have `%d`", len(color.Attrs), tc.Size)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseLevel(t *testing.T) {
|
||||
tt := []struct {
|
||||
Parse string
|
||||
Expected *Color
|
||||
Expected Color
|
||||
}{
|
||||
{"T", Trace},
|
||||
{"Trace", Trace},
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
package color
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Interface guard
|
||||
var _ Color = &Extended{}
|
||||
|
||||
const (
|
||||
extendedFG = "38;5;"
|
||||
extendedBG = "48;5;"
|
||||
)
|
||||
|
||||
// Extended defines a custom color object which is defined by SGR parameters.
|
||||
type Extended struct {
|
||||
FG ExtendedAttribute
|
||||
BG ExtendedAttribute
|
||||
}
|
||||
|
||||
// NewExtended returns a new Color with an ExtendedAttribute FG and BG
|
||||
func NewExtended(fg, bg ExtendedAttribute) *Extended {
|
||||
return &Extended{
|
||||
FG: fg,
|
||||
BG: bg,
|
||||
}
|
||||
}
|
||||
|
||||
// String returns a string representation of the sum of a Color's Attributes
|
||||
func (e *Extended) String() string {
|
||||
return fmt.Sprintf("%d", e.FG+e.BG)
|
||||
}
|
||||
|
||||
func (e *Extended) wrap(s string) string {
|
||||
return e.format() + s + e.unformat()
|
||||
}
|
||||
|
||||
func (e *Extended) format() string {
|
||||
return fmt.Sprintf("%s[%s%dm%s[%s%dm", escape, extendedFG, e.FG, escape, extendedBG, e.BG)
|
||||
}
|
||||
|
||||
func (e *Extended) unformat() string {
|
||||
return fmt.Sprintf("%s[%dm", escape, Reset)
|
||||
}
|
||||
|
||||
func (e *Extended) Format(text string) string {
|
||||
return e.wrap(text)
|
||||
}
|
|
@ -0,0 +1,519 @@
|
|||
package color
|
||||
|
||||
// ExtendedAttribute defines an extended color
|
||||
type ExtendedAttribute int
|
||||
|
||||
const (
|
||||
// #000000
|
||||
Black ExtendedAttribute = iota
|
||||
// #800000
|
||||
Maroon
|
||||
// #008000
|
||||
Green
|
||||
// #808000
|
||||
Olive
|
||||
// #000080
|
||||
Navy
|
||||
// #800080
|
||||
Purple
|
||||
// #008080
|
||||
Teal
|
||||
// #c0c0c0
|
||||
Silver
|
||||
// #808080
|
||||
Grey
|
||||
// #ff0000
|
||||
Red
|
||||
// #00ff00
|
||||
Lime
|
||||
// #ffff00
|
||||
Yellow
|
||||
// #0000ff
|
||||
Blue
|
||||
// #ff00ff
|
||||
Fuchsia
|
||||
// #00ffff
|
||||
Aqua
|
||||
// #ffffff
|
||||
White
|
||||
// #000000
|
||||
Grey0
|
||||
// #00005f
|
||||
NavyBlue
|
||||
// #000087
|
||||
DarkBlue
|
||||
// #0000af
|
||||
Blue3
|
||||
// #0000d7
|
||||
Blue3_1
|
||||
// #0000ff
|
||||
Blue1
|
||||
// #005f00
|
||||
DarkGreen
|
||||
// #005f5f
|
||||
DeepSkyBlue4
|
||||
// #005f87
|
||||
DeepSkyBlue4_1
|
||||
// #005faf
|
||||
DeepSkyBlue4_2
|
||||
// #005fd7
|
||||
DodgerBlue3
|
||||
// #005fff
|
||||
DodgerBlue2
|
||||
// #008700
|
||||
Green4
|
||||
// #00875f
|
||||
SpringGreen4
|
||||
// #008787
|
||||
Turquoise4
|
||||
// #0087af
|
||||
DeepSkyBlue3
|
||||
// #0087d7
|
||||
DeepSkyBlue3_1
|
||||
// #0087ff
|
||||
DodgerBlue1
|
||||
// #00af00
|
||||
Green3
|
||||
// #00af5f
|
||||
SpringGreen3
|
||||
// #00af87
|
||||
DarkCyan
|
||||
// #00afaf
|
||||
LightSeaGreen
|
||||
// #00afd7
|
||||
DeepSkyBlue2
|
||||
// #00afff
|
||||
DeepSkyBlue1
|
||||
// #00d700
|
||||
Green3_1
|
||||
// #00d75f
|
||||
SpringGreen3_1
|
||||
// #00d787
|
||||
SpringGreen2
|
||||
// #00d7af
|
||||
Cyan3
|
||||
// #00d7d7
|
||||
DarkTurquoise
|
||||
// #00d7ff
|
||||
Turquoise2
|
||||
// #00ff00
|
||||
Green1
|
||||
// #00ff5f
|
||||
SpringGreen2_1
|
||||
// #00ff87
|
||||
SpringGreen1
|
||||
// #00ffaf
|
||||
MediumSpringGreen
|
||||
// #00ffd7
|
||||
Cyan2
|
||||
// #00ffff
|
||||
Cyan1
|
||||
// #5f0000
|
||||
DarkRed
|
||||
// #5f005f
|
||||
DeepPink4
|
||||
// #5f0087
|
||||
Purple4
|
||||
// #5f00af
|
||||
Purple4_1
|
||||
// #5f00d7
|
||||
Purple3
|
||||
// #5f00ff
|
||||
BlueViolet
|
||||
// #5f5f00
|
||||
Orange4
|
||||
// #5f5f5f
|
||||
Grey37
|
||||
// #5f5f87
|
||||
MediumPurple4
|
||||
// #5f5faf
|
||||
SlateBlue3
|
||||
// #5f5fd7
|
||||
SlateBlue3_1
|
||||
// #5f5fff
|
||||
RoyalBlue1
|
||||
// #5f8700
|
||||
Chartreuse4
|
||||
// #5f875f
|
||||
DarkSeaGreen4
|
||||
// #5f8787
|
||||
PaleTurquoise4
|
||||
// #5f87af
|
||||
SteelBlue
|
||||
// #5f87d7
|
||||
SteelBlue3
|
||||
// #5f87ff
|
||||
CornflowerBlue
|
||||
// #5faf00
|
||||
Chartreuse3
|
||||
// #5faf5f
|
||||
DarkSeaGreen4_1
|
||||
// #5faf87
|
||||
CadetBlue
|
||||
// #5fafaf
|
||||
CadetBlue_1
|
||||
// #5fafd7
|
||||
SkyBlue3
|
||||
// #5fafff
|
||||
SteelBlue1
|
||||
// #5fd700
|
||||
Chartreuse3_1
|
||||
// #5fd75f
|
||||
PaleGreen3
|
||||
// #5fd787
|
||||
SeaGreen3
|
||||
// #5fd7af
|
||||
Aquamarine3
|
||||
// #5fd7d7
|
||||
MediumTurquoise
|
||||
// #5fd7ff
|
||||
SteelBlue1_1
|
||||
// #5fff00
|
||||
Chartreuse2
|
||||
// #5fff5f
|
||||
SeaGreen2
|
||||
// #5fff87
|
||||
SeaGreen1
|
||||
// #5fffaf
|
||||
SeaGreen1_1
|
||||
// #5fffd7
|
||||
Aquamarine1
|
||||
// #5fffff
|
||||
DarkSlateGray2
|
||||
// #870000
|
||||
DarkRed_1
|
||||
// #87005f
|
||||
DeepPink4_1
|
||||
// #870087
|
||||
DarkMagenta
|
||||
// #8700af
|
||||
DarkMagenta_1
|
||||
// #8700d7
|
||||
DarkViolet
|
||||
// #8700ff
|
||||
Purple_1
|
||||
// #875f00
|
||||
Orange4_1
|
||||
// #875f5f
|
||||
LightPink4
|
||||
// #875f87
|
||||
Plum4
|
||||
// #875faf
|
||||
MediumPurple3
|
||||
// #875fd7
|
||||
MediumPurple3_1
|
||||
// #875fff
|
||||
SlateBlue1
|
||||
// #878700
|
||||
Yellow4
|
||||
// #87875f
|
||||
Wheat4
|
||||
// #878787
|
||||
Grey53
|
||||
// #8787af
|
||||
LightSlateGrey
|
||||
// #8787d7
|
||||
MediumPurple
|
||||
// #8787ff
|
||||
LightSlateBlue
|
||||
// #87af00
|
||||
Yellow4_1
|
||||
// #87af5f
|
||||
DarkOliveGreen3
|
||||
// #87af87
|
||||
DarkSeaGreen
|
||||
// #87afaf
|
||||
LightSkyBlue3
|
||||
// #87afd7
|
||||
LightSkyBlue3_1
|
||||
// #87afff
|
||||
SkyBlue2
|
||||
// #87d700
|
||||
Chartreuse2_1
|
||||
// #87d75f
|
||||
DarkOliveGreen3_1
|
||||
// #87d787
|
||||
PaleGreen3_1
|
||||
// #87d7af
|
||||
DarkSeaGreen3
|
||||
// #87d7d7
|
||||
DarkSlateGray3
|
||||
// #87d7ff
|
||||
SkyBlue1
|
||||
// #87ff00
|
||||
Chartreuse1
|
||||
// #87ff5f
|
||||
LightGreen
|
||||
// #87ff87
|
||||
LightGreen_1
|
||||
// #87ffaf
|
||||
PaleGreen1
|
||||
// #87ffd7
|
||||
Aquamarine1_1
|
||||
// #87ffff
|
||||
DarkSlateGray1
|
||||
// #af0000
|
||||
Red3
|
||||
// #af005f
|
||||
DeepPink4_2
|
||||
// #af0087
|
||||
MediumVioletRed
|
||||
// #af00af
|
||||
Magenta3
|
||||
// #af00d7
|
||||
DarkViolet_1
|
||||
// #af00ff
|
||||
Purple_2
|
||||
// #af5f00
|
||||
DarkOrange3
|
||||
// #af5f5f
|
||||
IndianRed
|
||||
// #af5f87
|
||||
HotPink3
|
||||
// #af5faf
|
||||
MediumOrchid3
|
||||
// #af5fd7
|
||||
MediumOrchid
|
||||
// #af5fff
|
||||
MediumPurple2
|
||||
// #af8700
|
||||
DarkGoldenrod
|
||||
// #af875f
|
||||
LightSalmon3
|
||||
// #af8787
|
||||
RosyBrown
|
||||
// #af87af
|
||||
Grey63
|
||||
// #af87d7
|
||||
MediumPurple2_1
|
||||
// #af87ff
|
||||
MediumPurple1
|
||||
// #afaf00
|
||||
Gold3
|
||||
// #afaf5f
|
||||
DarkKhaki
|
||||
// #afaf87
|
||||
NavajoWhite3
|
||||
// #afafaf
|
||||
Grey69
|
||||
// #afafd7
|
||||
LightSteelBlue3
|
||||
// #afafff
|
||||
LightSteelBlue
|
||||
// #afd700
|
||||
Yellow3
|
||||
// #afd75f
|
||||
DarkOliveGreen3_2
|
||||
// #afd787
|
||||
DarkSeaGreen3_1
|
||||
// #afd7af
|
||||
DarkSeaGreen2
|
||||
// #afd7d7
|
||||
LightCyan3
|
||||
// #afd7ff
|
||||
LightSkyBlue1
|
||||
// #afff00
|
||||
GreenYellow
|
||||
// #afff5f
|
||||
DarkOliveGreen2
|
||||
// #afff87
|
||||
PaleGreen1_1
|
||||
// #afffaf
|
||||
DarkSeaGreen2_1
|
||||
// #afffd7
|
||||
DarkSeaGreen1
|
||||
// #afffff
|
||||
PaleTurquoise1
|
||||
// #d70000
|
||||
Red3_1
|
||||
// #d7005f
|
||||
DeepPink3
|
||||
// #d70087
|
||||
DeepPink3_1
|
||||
// #d700af
|
||||
Magenta3_1
|
||||
// #d700d7
|
||||
Magenta3_2
|
||||
// #d700ff
|
||||
Magenta2
|
||||
// #d75f00
|
||||
DarkOrange3_1
|
||||
// #d75f5f
|
||||
IndianRed_1
|
||||
// #d75f87
|
||||
HotPink3_1
|
||||
// #d75faf
|
||||
HotPink2
|
||||
// #d75fd7
|
||||
Orchid
|
||||
// #d75fff
|
||||
MediumOrchid1
|
||||
// #d78700
|
||||
Orange3
|
||||
// #d7875f
|
||||
LightSalmon3_1
|
||||
// #d78787
|
||||
LightPink3
|
||||
// #d787af
|
||||
Pink3
|
||||
// #d787d7
|
||||
Plum3
|
||||
// #d787ff
|
||||
Violet
|
||||
// #d7af00
|
||||
Gold3_1
|
||||
// #d7af5f
|
||||
LightGoldenrod3
|
||||
// #d7af87
|
||||
Tan
|
||||
// #d7afaf
|
||||
MistyRose3
|
||||
// #d7afd7
|
||||
Thistle3
|
||||
// #d7afff
|
||||
Plum2
|
||||
// #d7d700
|
||||
Yellow3_1
|
||||
// #d7d75f
|
||||
Khaki3
|
||||
// #d7d787
|
||||
LightGoldenrod2
|
||||
// #d7d7af
|
||||
LightYellow3
|
||||
// #d7d7d7
|
||||
Grey84
|
||||
// #d7d7ff
|
||||
LightSteelBlue1
|
||||
// #d7ff00
|
||||
Yellow2
|
||||
// #d7ff5f
|
||||
DarkOliveGreen1
|
||||
// #d7ff87
|
||||
DarkOliveGreen1_1
|
||||
// #d7ffaf
|
||||
DarkSeaGreen1_1
|
||||
// #d7ffd7
|
||||
Honeydew2
|
||||
// #d7ffff
|
||||
LightCyan1
|
||||
// #ff0000
|
||||
Red1
|
||||
// #ff005f
|
||||
DeepPink2
|
||||
// #ff0087
|
||||
DeepPink1
|
||||
// #ff00af
|
||||
DeepPink1_1
|
||||
// #ff00d7
|
||||
Magenta2_1
|
||||
// #ff00ff
|
||||
Magenta1
|
||||
// #ff5f00
|
||||
OrangeRed1
|
||||
// #ff5f5f
|
||||
IndianRed1
|
||||
// #ff5f87
|
||||
IndianRed1_1
|
||||
// #ff5faf
|
||||
HotPink
|
||||
// #ff5fd7
|
||||
HotPink_1
|
||||
// #ff5fff
|
||||
MediumOrchid1_1
|
||||
// #ff8700
|
||||
DarkOrange
|
||||
// #ff875f
|
||||
Salmon1
|
||||
// #ff8787
|
||||
LightCoral
|
||||
// #ff87af
|
||||
PaleVioletRed1
|
||||
// #ff87d7
|
||||
Orchid2
|
||||
// #ff87ff
|
||||
Orchid1
|
||||
// #ffaf00
|
||||
Orange1
|
||||
// #ffaf5f
|
||||
SandyBrown
|
||||
// #ffaf87
|
||||
LightSalmon1
|
||||
// #ffafaf
|
||||
LightPink1
|
||||
// #ffafd7
|
||||
Pink1
|
||||
// #ffafff
|
||||
Plum1
|
||||
// #ffd700
|
||||
Gold1
|
||||
// #ffd75f
|
||||
LightGoldenrod2_1
|
||||
// #ffd787
|
||||
LightGoldenrod2_2
|
||||
// #ffd7af
|
||||
NavajoWhite1
|
||||
// #ffd7d7
|
||||
MistyRose1
|
||||
// #ffd7ff
|
||||
Thistle1
|
||||
// #ffff00
|
||||
Yellow1
|
||||
// #ffff5f
|
||||
LightGoldenrod1
|
||||
// #ffff87
|
||||
Khaki1
|
||||
// #ffffaf
|
||||
Wheat1
|
||||
// #ffffd7
|
||||
Cornsilk1
|
||||
// #ffffff
|
||||
Grey100
|
||||
// #080808
|
||||
Grey3
|
||||
// #121212
|
||||
Grey7
|
||||
// #1c1c1c
|
||||
Grey11
|
||||
// #262626
|
||||
Grey15
|
||||
// #303030
|
||||
Grey19
|
||||
// #3a3a3a
|
||||
Grey23
|
||||
// #444444
|
||||
Grey27
|
||||
// #4e4e4e
|
||||
Grey30
|
||||
// #585858
|
||||
Grey35
|
||||
// #626262
|
||||
Grey39
|
||||
// #6c6c6c
|
||||
Grey42
|
||||
// #767676
|
||||
Grey46
|
||||
// #808080
|
||||
Grey50
|
||||
// #8a8a8a
|
||||
Grey54
|
||||
// #949494
|
||||
Grey58
|
||||
// #9e9e9e
|
||||
Grey62
|
||||
// #a8a8a8
|
||||
Grey66
|
||||
// #b2b2b2
|
||||
Grey70
|
||||
// #bcbcbc
|
||||
Grey74
|
||||
// #c6c6c6
|
||||
Grey78
|
||||
// #d0d0d0
|
||||
Grey82
|
||||
// #dadada
|
||||
Grey85
|
||||
// #e4e4e4
|
||||
Grey89
|
||||
// #eeeeee
|
||||
Grey93
|
||||
)
|
|
@ -0,0 +1,66 @@
|
|||
package color
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type RGB struct {
|
||||
Red int
|
||||
Green int
|
||||
Blue int
|
||||
}
|
||||
|
||||
func MustParseHex(hexColor string) *RGB {
|
||||
c, err := ParseHex(hexColor)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func ParseHex(hexColor string) (*RGB, error) {
|
||||
hexColor = strings.TrimPrefix(hexColor, "#")
|
||||
|
||||
// Convert to individual parts
|
||||
var rh, gh, bh string
|
||||
switch len(hexColor) {
|
||||
case 3:
|
||||
rh, gh, bh = hexColor[:1]+hexColor[:1], hexColor[1:2]+hexColor[1:2], hexColor[2:3]+hexColor[2:3]
|
||||
case 6:
|
||||
rh, gh, bh = hexColor[0:2], hexColor[2:4], hexColor[4:6]
|
||||
default:
|
||||
return nil, errors.New("invalid hex string")
|
||||
}
|
||||
|
||||
// Convert to bytes
|
||||
rb, err := hex.DecodeString(rh)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gb, err := hex.DecodeString(gh)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bb, err := hex.DecodeString(bh)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &RGB{
|
||||
Red: int(rb[0]),
|
||||
Green: int(gb[0]),
|
||||
Blue: int(bb[0]),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// String returns an r;g;b representation of an RGB
|
||||
func (r *RGB) String() string {
|
||||
return r.format()
|
||||
}
|
||||
|
||||
func (r *RGB) format() string {
|
||||
return fmt.Sprintf("%d;%d;%d", r.Red, r.Green, r.Blue)
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package color
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestParseHex(t *testing.T) {
|
||||
tt := []struct {
|
||||
Name string
|
||||
Hex string
|
||||
RGB *RGB
|
||||
Error bool
|
||||
}{
|
||||
{Name: "WhiteShort", Hex: "FFF", RGB: &RGB{255, 255, 255}, Error: false},
|
||||
{Name: "WhiteLong", Hex: "FFFFFF", RGB: &RGB{255, 255, 255}, Error: false},
|
||||
{Name: "WhiteInvalid", Hex: "F", RGB: nil, Error: true},
|
||||
{Name: "InvalidHex", Hex: "ZZZ", RGB: nil, Error: true},
|
||||
}
|
||||
|
||||
for _, tc := range tt {
|
||||
t.Run(tc.Name, func(t *testing.T) {
|
||||
rgb, err := ParseHex(tc.Hex)
|
||||
if err != nil {
|
||||
if tc.Error {
|
||||
return // Pass
|
||||
}
|
||||
t.Log(err)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
if rgb.Red != tc.RGB.Red || rgb.Green != tc.RGB.Green || rgb.Blue != tc.RGB.Blue {
|
||||
t.Logf("hex was parsed incorrectly: parsed %#v vs expected %#v", rgb, tc.RGB)
|
||||
t.Fail()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package color
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Interface guard
|
||||
var _ Color = &True{}
|
||||
|
||||
const (
|
||||
trueFG = "38;2;"
|
||||
trueBG = "48;2;"
|
||||
)
|
||||
|
||||
type True struct {
|
||||
FG *RGB
|
||||
BG *RGB
|
||||
}
|
||||
|
||||
// NewTrue returns a new Color with RGB FG and BG
|
||||
func NewTrue(fg, bg *RGB) *True {
|
||||
return &True{
|
||||
FG: fg,
|
||||
BG: bg,
|
||||
}
|
||||
}
|
||||
|
||||
// String returns a string representation of the sum of a Color's Attributes
|
||||
func (t *True) String() string {
|
||||
return fmt.Sprintf("%d", t.FG.Red+t.FG.Green+t.FG.Blue+t.BG.Red+t.BG.Green+t.BG.Blue)
|
||||
}
|
||||
|
||||
func (t *True) wrap(s string) string {
|
||||
return t.format() + s + t.unformat()
|
||||
}
|
||||
|
||||
func (t *True) format() string {
|
||||
return fmt.Sprintf("%s[%s%sm%s[%s%sm", escape, trueFG, t.FG.format(), escape, trueBG, t.BG.format())
|
||||
}
|
||||
|
||||
func (t *True) unformat() string {
|
||||
return fmt.Sprintf("%s[%dm", escape, Reset)
|
||||
}
|
||||
|
||||
func (t *True) Format(text string) string {
|
||||
return t.wrap(text)
|
||||
}
|
Reference in New Issue