Update colors (#2)

Simplify example

Update colors

Add wrapping

Signed-off-by: jolheiser <john.olheiser@gmail.com>

Co-authored-by: jolheiser <john.olheiser@gmail.com>
Reviewed-on: https://gitea.com/jolheiser/beaver/pulls/2
pull/4/head v1.0.2
John Olheiser 2020-02-26 20:01:20 +00:00
parent f3053ba01b
commit 5e1722bd43
5 changed files with 175 additions and 77 deletions

View File

@ -48,4 +48,6 @@ Beaver allows you to customize the colors of various parts of the message
```go
// Set Trace logging to Magenta instead of Bold-Cyan
color.Trace = color.New(color.FgMagenta)
```
```
[More info for the `color` package](color/README.md)

45
color/README.md 100644
View File

@ -0,0 +1,45 @@
# Color
Beaver comes with the `color` sub-package that can be used even without Beaver calls.
## Formatting a string with a single attribute
```go
text := color.FgRed.Format("red")
```
## Formatting a string with a full color
```go
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`.
### setup
Assuming each example is preceded by
```go
green := color.New(color.FgGreen)
red := color.New(color.FgRed)
```
#### pure `fmt.Println`
```go
fmt.Println(green.Format("This is a"), red.Format("color"), green.Format("test!"))
```
#### `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")
```

87
color/attribute.go 100644
View File

@ -0,0 +1,87 @@
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...)
}

View File

@ -2,82 +2,28 @@ 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
}
// Attribute defines a single SGR Code
type Attribute int
const escape = "\x1b"
// 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
)
// New returns a new Color with attrs Attributes
func New(attrs ...Attribute) *Color {
return &Color{Attrs: attrs}
@ -118,17 +64,18 @@ func (c *Color) sequence() string {
}
func (c *Color) wrap(s string) string {
return c.format() + s + c.unformat()
return c.format() + s + unformat()
}
func (c *Color) format() string {
return fmt.Sprintf("%s[%sm", escape, c.sequence())
}
func (c *Color) unformat() string {
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)
@ -136,6 +83,28 @@ func (c *Color) Format(text string) string {
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)
}
// ParseLevel parses a string and returns a Beaver Level's Color, defaulting to Info
func ParseLevel(level string) *Color {
switch strings.ToUpper(level) {

View File

@ -5,7 +5,6 @@ import (
"io"
"os"
"runtime"
"strings"
"time"
"go.jolheiser.com/beaver/color"
@ -41,12 +40,12 @@ func New(writer io.Writer, level Level, opts FormatOptions) *Logger {
func (l *Logger) write(level Level, text string) {
if l.Level <= level {
_, file, line, _ := runtime.Caller(l.skip)
var message string
if l.Format.TimePrefix {
message += color.Time.Format(timePrefix(time.Now())) + " "
}
if l.Format.StackPrefix {
_, file, line, _ := runtime.Caller(l.skip)
idx := len(file) - l.Format.StackLimit
if l.Format.StackLimit <= 0 {
idx = 0
@ -75,11 +74,7 @@ func (l *Logger) write(level Level, text string) {
}
func (l *Logger) log(level Level, v ...interface{}) {
var args = make([]string, len(v))
for i := 0; i < len(v); i++ {
args[i] = fmt.Sprintf("%v", v[i])
}
text := strings.Join(args, " ")
text := fmt.Sprint(v...)
l.write(level, text)
}