mirror of
https://github.com/peco/peco.git
synced 2026-09-10 07:16:29 -04:00
First pass at removing termbox from common APIs
Start tackling from styles. This commit still should break the existing application, as there are still instances where the termbox-native styles are accessed directly via struct field lookups. Because we separated colors from attributes, you can no longer simply refer to Style.fg without merging them aith the attributes such as bold, underline, etc. Meanwhile, in order to fix tests we have dicovered the existance of os.UserHomeDir(), so we're going to use that.
This commit is contained in:
parent
4045ebad41
commit
8075a29013
120
config.go
120
config.go
|
|
@ -6,21 +6,16 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"strconv"
|
||||
|
||||
"github.com/nsf/termbox-go"
|
||||
"github.com/peco/peco/filter"
|
||||
"github.com/peco/peco/internal/util"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var homedirFunc = util.Homedir
|
||||
|
||||
// NewConfig creates a new Config
|
||||
func (c *Config) Init() error {
|
||||
c.Keymap = make(map[string]string)
|
||||
c.InitialMatcher = IgnoreCaseMatch
|
||||
c.Style.Init()
|
||||
c.Style = NewStyleSet()
|
||||
c.Prompt = "QUERY>"
|
||||
c.Layout = LayoutTypeTopDown
|
||||
c.Use256Color = false
|
||||
|
|
@ -64,105 +59,32 @@ func (c *Config) ReadFilename(filename string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
stringToFg = map[string]termbox.Attribute{
|
||||
"default": termbox.ColorDefault,
|
||||
"black": termbox.ColorBlack,
|
||||
"red": termbox.ColorRed,
|
||||
"green": termbox.ColorGreen,
|
||||
"yellow": termbox.ColorYellow,
|
||||
"blue": termbox.ColorBlue,
|
||||
"magenta": termbox.ColorMagenta,
|
||||
"cyan": termbox.ColorCyan,
|
||||
"white": termbox.ColorWhite,
|
||||
}
|
||||
stringToBg = map[string]termbox.Attribute{
|
||||
"on_default": termbox.ColorDefault,
|
||||
"on_black": termbox.ColorBlack,
|
||||
"on_red": termbox.ColorRed,
|
||||
"on_green": termbox.ColorGreen,
|
||||
"on_yellow": termbox.ColorYellow,
|
||||
"on_blue": termbox.ColorBlue,
|
||||
"on_magenta": termbox.ColorMagenta,
|
||||
"on_cyan": termbox.ColorCyan,
|
||||
"on_white": termbox.ColorWhite,
|
||||
}
|
||||
stringToFgAttr = map[string]termbox.Attribute{
|
||||
"bold": termbox.AttrBold,
|
||||
"underline": termbox.AttrUnderline,
|
||||
"reverse": termbox.AttrReverse,
|
||||
}
|
||||
stringToBgAttr = map[string]termbox.Attribute{
|
||||
"on_bold": termbox.AttrBold,
|
||||
}
|
||||
)
|
||||
|
||||
// NewStyleSet creates a new StyleSet struct
|
||||
func NewStyleSet() *StyleSet {
|
||||
ss := &StyleSet{}
|
||||
ss := &StyleSet{
|
||||
Basic: NewStyle(),
|
||||
Query: NewStyle(),
|
||||
Matched: NewStyle(),
|
||||
SavedSelection: NewStyle(),
|
||||
Selected: NewStyle(),
|
||||
}
|
||||
ss.Init()
|
||||
return ss
|
||||
}
|
||||
|
||||
func (ss *StyleSet) Init() {
|
||||
ss.Basic.fg = termbox.ColorDefault
|
||||
ss.Basic.bg = termbox.ColorDefault
|
||||
ss.Query.fg = termbox.ColorDefault
|
||||
ss.Query.bg = termbox.ColorDefault
|
||||
ss.Matched.fg = termbox.ColorCyan
|
||||
ss.Matched.bg = termbox.ColorDefault
|
||||
ss.SavedSelection.fg = termbox.ColorBlack | termbox.AttrBold
|
||||
ss.SavedSelection.bg = termbox.ColorCyan
|
||||
ss.Selected.fg = termbox.ColorDefault | termbox.AttrUnderline
|
||||
ss.Selected.bg = termbox.ColorMagenta
|
||||
}
|
||||
|
||||
// UnmarshalJSON satisfies json.RawMessage.
|
||||
func (s *Style) UnmarshalJSON(buf []byte) error {
|
||||
raw := []string{}
|
||||
if err := json.Unmarshal(buf, &raw); err != nil {
|
||||
return errors.Wrapf(err, "failed to unmarshal Style")
|
||||
}
|
||||
return stringsToStyle(s, raw)
|
||||
}
|
||||
|
||||
func stringsToStyle(style *Style, raw []string) error {
|
||||
style.fg = termbox.ColorDefault
|
||||
style.bg = termbox.ColorDefault
|
||||
|
||||
for _, s := range raw {
|
||||
fg, ok := stringToFg[s]
|
||||
if ok {
|
||||
style.fg = fg
|
||||
} else {
|
||||
if fg, err := strconv.ParseUint(s, 10, 8); err == nil {
|
||||
style.fg = termbox.Attribute(fg+1)
|
||||
}
|
||||
}
|
||||
|
||||
bg, ok := stringToBg[s]
|
||||
if ok {
|
||||
style.bg = bg
|
||||
} else {
|
||||
if strings.HasPrefix(s, "on_") {
|
||||
if bg, err := strconv.ParseUint(s[3:], 10, 8); err == nil {
|
||||
style.bg = termbox.Attribute(bg+1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, s := range raw {
|
||||
if fgAttr, ok := stringToFgAttr[s]; ok {
|
||||
style.fg |= fgAttr
|
||||
}
|
||||
|
||||
if bgAttr, ok := stringToBgAttr[s]; ok {
|
||||
style.bg |= bgAttr
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
ss.Basic.Reset()
|
||||
ss.Query.Reset()
|
||||
ss.Matched.Reset().
|
||||
Foreground(ColorCyan)
|
||||
ss.SavedSelection.Reset().
|
||||
Foreground(ColorBlack).
|
||||
Background(ColorCyan).
|
||||
Bold(true)
|
||||
ss.Selected.Reset().
|
||||
Foreground(ColorDefault).
|
||||
Background(ColorMagenta).
|
||||
Underline(true)
|
||||
}
|
||||
|
||||
// This is a variable because we want to change its behavior
|
||||
|
|
@ -187,7 +109,7 @@ func LocateRcfile(locater configLocateFunc) (string, error) {
|
|||
// $XDG_CONFIG_DIR/peco/config.json (where XDG_CONFIG_DIR is listed in $XDG_CONFIG_DIRS)
|
||||
// ~/.peco/config.json
|
||||
|
||||
home, uErr := homedirFunc()
|
||||
home, uErr := os.UserHomeDir()
|
||||
|
||||
// Try dir supplied via env var
|
||||
if dir := os.Getenv("XDG_CONFIG_HOME"); dir != "" {
|
||||
|
|
|
|||
116
config_test.go
116
config_test.go
|
|
@ -1,4 +1,4 @@
|
|||
package peco
|
||||
package peco_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
|
@ -9,7 +9,8 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/nsf/termbox-go"
|
||||
"github.com/lestrrat-go/envload"
|
||||
"github.com/peco/peco"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
|
@ -30,7 +31,7 @@ func TestReadRC(t *testing.T) {
|
|||
"Prompt": "[peco]"
|
||||
}
|
||||
`
|
||||
var cfg Config
|
||||
var cfg peco.Config
|
||||
if !assert.NoError(t, cfg.Init(), "Config.Init should succeed") {
|
||||
return
|
||||
}
|
||||
|
|
@ -39,31 +40,32 @@ func TestReadRC(t *testing.T) {
|
|||
return
|
||||
}
|
||||
|
||||
expected := Config{
|
||||
expected := peco.Config{
|
||||
Keymap: map[string]string{
|
||||
"C-j": "peco.Finish",
|
||||
"C-x,C-c": "peco.Finish",
|
||||
},
|
||||
InitialMatcher: IgnoreCaseMatch,
|
||||
Layout: DefaultLayoutType,
|
||||
InitialMatcher: peco.IgnoreCaseMatch,
|
||||
Layout: peco.DefaultLayoutType,
|
||||
Prompt: "[peco]",
|
||||
Style: StyleSet{
|
||||
Matched: Style{
|
||||
fg: termbox.ColorCyan | termbox.AttrBold,
|
||||
bg: termbox.ColorRed,
|
||||
},
|
||||
Query: Style{
|
||||
fg: termbox.ColorYellow | termbox.AttrBold,
|
||||
bg: termbox.ColorDefault,
|
||||
},
|
||||
Selected: Style{
|
||||
fg: termbox.ColorBlack | termbox.AttrUnderline,
|
||||
bg: termbox.ColorCyan,
|
||||
},
|
||||
SavedSelection: Style{
|
||||
fg: termbox.ColorBlack | termbox.AttrBold,
|
||||
bg: termbox.ColorCyan,
|
||||
},
|
||||
Style: &peco.StyleSet{
|
||||
Basic: peco.NewStyle(),
|
||||
Matched: peco.NewStyle().
|
||||
Foreground(peco.ColorCyan).
|
||||
Background(peco.ColorRed).
|
||||
Bold(true),
|
||||
Query: peco.NewStyle().
|
||||
Foreground(peco.ColorYellow).
|
||||
Background(peco.ColorDefault).
|
||||
Bold(true),
|
||||
Selected: peco.NewStyle().
|
||||
Foreground(peco.ColorBlack).
|
||||
Background(peco.ColorCyan).
|
||||
Underline(true),
|
||||
SavedSelection: peco.NewStyle().
|
||||
Foreground(peco.ColorBlack).
|
||||
Background(peco.ColorCyan).
|
||||
Bold(true),
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -74,48 +76,66 @@ func TestReadRC(t *testing.T) {
|
|||
|
||||
type stringsToStyleTest struct {
|
||||
strings []string
|
||||
style *Style
|
||||
style *peco.Style
|
||||
}
|
||||
|
||||
func TestStringsToStyle(t *testing.T) {
|
||||
tests := []stringsToStyleTest{
|
||||
stringsToStyleTest{
|
||||
strings: []string{"on_default", "default"},
|
||||
style: &Style{fg: termbox.ColorDefault, bg: termbox.ColorDefault},
|
||||
style: peco.NewStyle().
|
||||
Foreground(peco.ColorDefault).
|
||||
Background(peco.ColorDefault),
|
||||
},
|
||||
stringsToStyleTest{
|
||||
strings: []string{"bold", "on_blue", "yellow"},
|
||||
style: &Style{fg: termbox.ColorYellow | termbox.AttrBold, bg: termbox.ColorBlue},
|
||||
style: peco.NewStyle().
|
||||
Foreground(peco.ColorYellow).
|
||||
Background(peco.ColorBlue).
|
||||
Bold(true),
|
||||
},
|
||||
stringsToStyleTest{
|
||||
strings: []string{"underline", "on_cyan", "black"},
|
||||
style: &Style{fg: termbox.ColorBlack | termbox.AttrUnderline, bg: termbox.ColorCyan},
|
||||
style: peco.NewStyle().
|
||||
Foreground(peco.ColorBlack).
|
||||
Background(peco.ColorCyan).
|
||||
Underline(true),
|
||||
},
|
||||
stringsToStyleTest{
|
||||
strings: []string{"reverse", "on_red", "white"},
|
||||
style: &Style{fg: termbox.ColorWhite | termbox.AttrReverse, bg: termbox.ColorRed},
|
||||
style: peco.NewStyle().
|
||||
Foreground(peco.ColorWhite).
|
||||
Background(peco.ColorRed).
|
||||
Reverse(true),
|
||||
},
|
||||
stringsToStyleTest{
|
||||
strings: []string{"on_bold", "on_magenta", "green"},
|
||||
style: &Style{fg: termbox.ColorGreen, bg: termbox.ColorMagenta | termbox.AttrBold},
|
||||
style: peco.NewStyle().
|
||||
Foreground(peco.ColorGreen).
|
||||
Background(peco.ColorMagenta).
|
||||
Bold(true),
|
||||
},
|
||||
stringsToStyleTest{
|
||||
strings: []string{"underline", "on_240", "214"},
|
||||
style: &Style{fg: (214+1) | termbox.AttrUnderline, bg: 240+1},
|
||||
style: peco.NewStyle().
|
||||
Foreground(214 + 1).
|
||||
Background(240 + 1).
|
||||
Underline(true),
|
||||
},
|
||||
}
|
||||
|
||||
t.Logf("Checking strings -> color mapping...")
|
||||
var a Style
|
||||
for _, test := range tests {
|
||||
t.Logf(" checking %s...", test.strings)
|
||||
if !assert.NoError(t, stringsToStyle(&a, test.strings), "stringsToStyle should succeed") {
|
||||
return
|
||||
}
|
||||
var a peco.Style
|
||||
for _, tc := range tests {
|
||||
tc := tc
|
||||
t.Run(strings.Join(tc.strings, ","), func(t *testing.T) {
|
||||
if !assert.NoError(t, a.FromStrings(tc.strings...), "stringsToStyle should succeed") {
|
||||
return
|
||||
}
|
||||
|
||||
if !assert.Equal(t, test.style, &a, "Expected '%s' to be '%#v', but got '%#v'", test.strings, test.style, a) {
|
||||
return
|
||||
}
|
||||
if !assert.Equal(t, tc.style, &a, "Expected '%s' to be '%#v', but got '%#v'", tc.strings, tc.style, a) {
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -125,16 +145,20 @@ func TestLocateRcfile(t *testing.T) {
|
|||
return
|
||||
}
|
||||
|
||||
homedirFunc = func() (string, error) {
|
||||
return dir, nil
|
||||
home, err := os.UserHomeDir()
|
||||
if !assert.NoError(t, err, `could not find user home directory`) {
|
||||
return
|
||||
}
|
||||
|
||||
el := envload.New()
|
||||
defer el.Restore()
|
||||
|
||||
expected := []string{
|
||||
filepath.Join(dir, "peco"),
|
||||
filepath.Join(dir, "1", "peco"),
|
||||
filepath.Join(dir, "2", "peco"),
|
||||
filepath.Join(dir, "3", "peco"),
|
||||
filepath.Join(dir, ".peco"),
|
||||
filepath.Join(home, ".peco"),
|
||||
}
|
||||
|
||||
i := 0
|
||||
|
|
@ -161,10 +185,10 @@ func TestLocateRcfile(t *testing.T) {
|
|||
fmt.Sprintf("%c", filepath.ListSeparator),
|
||||
))
|
||||
|
||||
LocateRcfile(locater)
|
||||
expected[0] = filepath.Join(dir, ".config", "peco")
|
||||
peco.LocateRcfile(locater)
|
||||
expected[0] = filepath.Join(home, ".config", "peco")
|
||||
os.Setenv("XDG_CONFIG_HOME", "")
|
||||
i = 0
|
||||
LocateRcfile(locater)
|
||||
peco.LocateRcfile(locater)
|
||||
|
||||
}
|
||||
|
|
|
|||
7
go.mod
7
go.mod
|
|
@ -4,11 +4,14 @@ go 1.12
|
|||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.0 // indirect
|
||||
github.com/gdamore/tcell v1.4.0
|
||||
github.com/gdamore/tcell/v2 v2.1.0
|
||||
github.com/google/btree v0.0.0-20161213163243-0c3044bc8bad
|
||||
github.com/jessevdk/go-flags v1.1.0
|
||||
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc
|
||||
github.com/lestrrat-go/pdebug v0.0.0-20180220043849-39f9a71bcabe
|
||||
github.com/mattn/go-runewidth v0.0.0-20161012013512-737072b4e32b
|
||||
github.com/nsf/termbox-go v0.0.0-20190817171036-93860e161317
|
||||
github.com/mattn/go-runewidth v0.0.9
|
||||
github.com/nsf/termbox-go v0.0.0-20201124104050-ed494de23a00
|
||||
github.com/pkg/errors v0.0.0-20161029093637-248dadf4e906
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312
|
||||
|
|
|
|||
20
interface.go
20
interface.go
|
|
@ -102,7 +102,7 @@ type Peco struct {
|
|||
singleKeyJumpPrefixMap map[rune]uint
|
||||
singleKeyJumpShowPrefix bool
|
||||
skipReadConfig bool
|
||||
styles StyleSet
|
||||
styles *StyleSet
|
||||
use256Color bool
|
||||
|
||||
// Source is where we buffer input. It gets reused when a new query is
|
||||
|
|
@ -289,7 +289,7 @@ type Config struct {
|
|||
Matcher string `json:"Matcher"` // Deprecated.
|
||||
InitialMatcher string `json:"InitialMatcher"` // Use this instead of Matcher
|
||||
InitialFilter string `json:"InitialFilter"`
|
||||
Style StyleSet `json:"Style"`
|
||||
Style *StyleSet `json:"Style"`
|
||||
Prompt string `json:"Prompt"`
|
||||
Layout string `json:"Layout"`
|
||||
Use256Color bool `json:"Use256Color"`
|
||||
|
|
@ -333,17 +333,11 @@ type CustomFilterConfig struct {
|
|||
|
||||
// StyleSet holds styles for various sections
|
||||
type StyleSet struct {
|
||||
Basic Style `json:"Basic"`
|
||||
SavedSelection Style `json:"SavedSelection"`
|
||||
Selected Style `json:"Selected"`
|
||||
Query Style `json:"Query"`
|
||||
Matched Style `json:"Matched"`
|
||||
}
|
||||
|
||||
// Style describes termbox styles
|
||||
type Style struct {
|
||||
fg termbox.Attribute
|
||||
bg termbox.Attribute
|
||||
Basic *Style `json:"Basic"`
|
||||
SavedSelection *Style `json:"SavedSelection"`
|
||||
Selected *Style `json:"Selected"`
|
||||
Query *Style `json:"Query"`
|
||||
Matched *Style `json:"Matched"`
|
||||
}
|
||||
|
||||
type Caret struct {
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
package util
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
)
|
||||
|
||||
func Homedir() (string, error) {
|
||||
home := os.Getenv("HOME")
|
||||
if home == "" {
|
||||
return "", errors.New("environment variable HOME not set")
|
||||
}
|
||||
|
||||
return home, nil
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
// +build !darwin,!windows
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
)
|
||||
|
||||
func Homedir() (string, error) {
|
||||
home := os.Getenv("HOME")
|
||||
if home == "" {
|
||||
return "", errors.New("error: Environment variable HOME not set")
|
||||
}
|
||||
|
||||
return home, nil
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
package util
|
||||
|
||||
import "os/user"
|
||||
|
||||
func Homedir() (string, error) {
|
||||
u, err := user.Current()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return u.HomeDir, nil
|
||||
}
|
||||
|
|
@ -35,7 +35,7 @@ func TestIssue212_SanityCheck(t *testing.T) {
|
|||
return
|
||||
}
|
||||
|
||||
defstyle := StyleSet{}
|
||||
defstyle := NewStyleSet()
|
||||
defstyle.Init()
|
||||
if !assert.Equal(t, state.config.Style, defstyle, "should be default style") {
|
||||
return
|
||||
|
|
|
|||
2
peco.go
2
peco.go
|
|
@ -134,7 +134,7 @@ func (p *Peco) Screen() Screen {
|
|||
}
|
||||
|
||||
func (p *Peco) Styles() *StyleSet {
|
||||
return &p.styles
|
||||
return p.styles
|
||||
}
|
||||
|
||||
func (p *Peco) Use256Color() bool {
|
||||
|
|
|
|||
195
style.go
Normal file
195
style.go
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
package peco
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/nsf/termbox-go"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Color represents a color. In termbox terms, colors are part of attributes
|
||||
// so there is really no point in having a different type, but we do this
|
||||
// so that the API is easier to understand
|
||||
type Color = termbox.Attribute
|
||||
|
||||
const (
|
||||
ColorDefault = termbox.ColorDefault
|
||||
ColorBlack = termbox.ColorBlack
|
||||
ColorRed = termbox.ColorRed
|
||||
ColorGreen = termbox.ColorGreen
|
||||
ColorYellow = termbox.ColorYellow
|
||||
ColorBlue = termbox.ColorBlue
|
||||
ColorMagenta = termbox.ColorMagenta
|
||||
ColorCyan = termbox.ColorCyan
|
||||
ColorWhite = termbox.ColorWhite
|
||||
ColorDarkGray = termbox.ColorDarkGray
|
||||
ColorLightRed = termbox.ColorLightRed
|
||||
ColorLightGreen = termbox.ColorLightGreen
|
||||
ColorLightYellow = termbox.ColorLightYellow
|
||||
ColorLightBlue = termbox.ColorLightBlue
|
||||
ColorLightMagenta = termbox.ColorLightMagenta
|
||||
ColorLightCyan = termbox.ColorLightCyan
|
||||
ColorLightGray = termbox.ColorLightGray
|
||||
)
|
||||
|
||||
// Style represents the set of styles to be applied when printing to the
|
||||
// terminal.
|
||||
type Style struct {
|
||||
// TODO: separate tcell/termbox versions so we can hide termbox from the
|
||||
// rest of the peco codebase
|
||||
bg termbox.Attribute
|
||||
fg termbox.Attribute
|
||||
attrs termbox.Attribute
|
||||
}
|
||||
|
||||
var (
|
||||
stringToFg = map[string]termbox.Attribute{
|
||||
"default": termbox.ColorDefault,
|
||||
"black": termbox.ColorBlack,
|
||||
"red": termbox.ColorRed,
|
||||
"green": termbox.ColorGreen,
|
||||
"yellow": termbox.ColorYellow,
|
||||
"blue": termbox.ColorBlue,
|
||||
"magenta": termbox.ColorMagenta,
|
||||
"cyan": termbox.ColorCyan,
|
||||
"white": termbox.ColorWhite,
|
||||
}
|
||||
stringToBg = map[string]termbox.Attribute{
|
||||
"on_default": termbox.ColorDefault,
|
||||
"on_black": termbox.ColorBlack,
|
||||
"on_red": termbox.ColorRed,
|
||||
"on_green": termbox.ColorGreen,
|
||||
"on_yellow": termbox.ColorYellow,
|
||||
"on_blue": termbox.ColorBlue,
|
||||
"on_magenta": termbox.ColorMagenta,
|
||||
"on_cyan": termbox.ColorCyan,
|
||||
"on_white": termbox.ColorWhite,
|
||||
}
|
||||
stringToFgAttr = map[string]termbox.Attribute{
|
||||
"bold": termbox.AttrBold,
|
||||
"underline": termbox.AttrUnderline,
|
||||
"reverse": termbox.AttrReverse,
|
||||
}
|
||||
stringToBgAttr = map[string]termbox.Attribute{
|
||||
"on_bold": termbox.AttrBold,
|
||||
}
|
||||
)
|
||||
|
||||
var stylePool = sync.Pool{
|
||||
New: func() interface{} { return NewStyle() },
|
||||
}
|
||||
|
||||
func NewStyle() *Style {
|
||||
return &Style{}
|
||||
}
|
||||
|
||||
func FetchStyle() *Style {
|
||||
return stylePool.Get().(*Style)
|
||||
}
|
||||
|
||||
func (s *Style) Release() {
|
||||
s.Reset()
|
||||
stylePool.Put(s)
|
||||
}
|
||||
|
||||
func (s *Style) Reset() *Style {
|
||||
s.bg = 0
|
||||
s.fg = 0
|
||||
s.attrs = 0
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Style) Foreground(c Color) *Style {
|
||||
s.fg = c
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Style) Background(c Color) *Style {
|
||||
s.bg = c
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Style) setAttr(attr termbox.Attribute, on bool) *Style {
|
||||
if on {
|
||||
s.attrs |= attr
|
||||
} else {
|
||||
s.attrs &^= attr
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Style) Bold(v bool) *Style {
|
||||
return s.setAttr(termbox.AttrBold, v)
|
||||
}
|
||||
|
||||
func (s *Style) Blink(v bool) *Style {
|
||||
return s.setAttr(termbox.AttrBlink, v)
|
||||
}
|
||||
|
||||
func (s *Style) Hidden(v bool) *Style {
|
||||
return s.setAttr(termbox.AttrHidden, v)
|
||||
}
|
||||
|
||||
func (s *Style) Dim(v bool) *Style {
|
||||
return s.setAttr(termbox.AttrDim, v)
|
||||
}
|
||||
|
||||
func (s *Style) Underline(v bool) *Style {
|
||||
return s.setAttr(termbox.AttrUnderline, v)
|
||||
}
|
||||
|
||||
func (s *Style) Cursive(v bool) *Style {
|
||||
return s.setAttr(termbox.AttrCursive, v)
|
||||
}
|
||||
|
||||
func (s *Style) Reverse(v bool) *Style {
|
||||
return s.setAttr(termbox.AttrReverse, v)
|
||||
}
|
||||
|
||||
// UnmarshalJSON satisfies json.RawMessage.
|
||||
func (s *Style) UnmarshalJSON(buf []byte) error {
|
||||
raw := []string{}
|
||||
if err := json.Unmarshal(buf, &raw); err != nil {
|
||||
return errors.Wrapf(err, "failed to unmarshal Style")
|
||||
}
|
||||
return s.FromStrings(raw...)
|
||||
}
|
||||
|
||||
func (style *Style) FromStrings(raw ...string) error {
|
||||
style.Reset()
|
||||
|
||||
for _, s := range raw {
|
||||
fg, ok := stringToFg[s]
|
||||
if ok {
|
||||
style.Foreground(fg)
|
||||
} else {
|
||||
if fg, err := strconv.ParseUint(s, 10, 8); err == nil {
|
||||
style.Foreground(termbox.Attribute(fg + 1))
|
||||
}
|
||||
}
|
||||
|
||||
bg, ok := stringToBg[s]
|
||||
if ok {
|
||||
style.Background(bg)
|
||||
} else {
|
||||
if strings.HasPrefix(s, "on_") {
|
||||
if bg, err := strconv.ParseUint(s[3:], 10, 8); err == nil {
|
||||
style.Background(termbox.Attribute(bg + 1))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, s := range raw {
|
||||
if fgAttr, ok := stringToFgAttr[s]; ok {
|
||||
style.setAttr(fgAttr, true)
|
||||
} else if bgAttr, ok := stringToBgAttr[s]; ok {
|
||||
style.setAttr(bgAttr, true)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Loading…
Reference in a new issue