mirror of https://github.com/mickael-menu/zk
Add the core Styler, a default TTY implementation and a {{style}} template helper
parent
8ed6667743
commit
e47eabf421
@ -0,0 +1,42 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/aymerick/raymond"
|
||||
"github.com/mickael-menu/zk/core"
|
||||
"github.com/mickael-menu/zk/util"
|
||||
)
|
||||
|
||||
// RegisterStyle register the {{style}} template helpers which stylizes the
|
||||
// text input according to predefined styling rules.
|
||||
//
|
||||
// {{style "date" created}}
|
||||
// {{#style "red"}}Hello, world{{/style}}
|
||||
func RegisterStyle(styler core.Styler, logger util.Logger) {
|
||||
style := func(keys string, text string) string {
|
||||
rules := make([]core.StyleRule, 0)
|
||||
for _, key := range strings.Fields(keys) {
|
||||
rules = append(rules, core.StyleRule(key))
|
||||
}
|
||||
res, err := styler.Style(text, rules...)
|
||||
if err != nil {
|
||||
logger.Err(err)
|
||||
return text
|
||||
} else {
|
||||
return res
|
||||
}
|
||||
}
|
||||
|
||||
raymond.RegisterHelper("style", func(rules string, opt interface{}) string {
|
||||
switch arg := opt.(type) {
|
||||
case *raymond.Options:
|
||||
return style(rules, arg.Fn())
|
||||
case string:
|
||||
return style(rules, arg)
|
||||
default:
|
||||
logger.Printf("the {{style}} template helper is expecting a string as input, received: %v", opt)
|
||||
return ""
|
||||
}
|
||||
})
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
package tty
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/mickael-menu/zk/core"
|
||||
)
|
||||
|
||||
// Styler is a text styler using ANSI escape codes to be used with a TTY.
|
||||
type Styler struct{}
|
||||
|
||||
func NewStyler() *Styler {
|
||||
return &Styler{}
|
||||
}
|
||||
|
||||
// FIXME: Semantic rules
|
||||
func (s *Styler) Style(text string, rules ...core.StyleRule) (string, error) {
|
||||
attrs, err := s.attributes(rules)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(attrs) == 0 {
|
||||
return text, nil
|
||||
}
|
||||
return color.New(attrs...).Sprint(text), nil
|
||||
}
|
||||
|
||||
var attrsMapping = map[core.StyleRule]color.Attribute{
|
||||
"reset": color.Reset,
|
||||
"bold": color.Bold,
|
||||
"faint": color.Faint,
|
||||
"italic": color.Italic,
|
||||
"underline": color.Underline,
|
||||
"blink-slow": color.BlinkSlow,
|
||||
"blink-fast": color.BlinkRapid,
|
||||
"hidden": color.Concealed,
|
||||
"strikethrough": color.CrossedOut,
|
||||
|
||||
"black": color.FgBlack,
|
||||
"red": color.FgRed,
|
||||
"green": color.FgGreen,
|
||||
"yellow": color.FgYellow,
|
||||
"blue": color.FgBlue,
|
||||
"magenta": color.FgMagenta,
|
||||
"cyan": color.FgCyan,
|
||||
"white": color.FgWhite,
|
||||
|
||||
"black-bg": color.BgBlack,
|
||||
"red-bg": color.BgRed,
|
||||
"green-bg": color.BgGreen,
|
||||
"yellow-bg": color.BgYellow,
|
||||
"blue-bg": color.BgBlue,
|
||||
"magenta-bg": color.BgMagenta,
|
||||
"cyan-bg": color.BgCyan,
|
||||
"white-bg": color.BgWhite,
|
||||
|
||||
"bright-black": color.FgHiBlack,
|
||||
"bright-red": color.FgHiRed,
|
||||
"bright-green": color.FgHiGreen,
|
||||
"bright-yellow": color.FgHiYellow,
|
||||
"bright-blue": color.FgHiBlue,
|
||||
"bright-magenta": color.FgHiMagenta,
|
||||
"bright-cyan": color.FgHiCyan,
|
||||
"bright-white": color.FgHiWhite,
|
||||
|
||||
"bright-black-bg": color.BgHiBlack,
|
||||
"bright-red-bg": color.BgHiRed,
|
||||
"bright-green-bg": color.BgHiGreen,
|
||||
"bright-yellow-bg": color.BgHiYellow,
|
||||
"bright-blue-bg": color.BgHiBlue,
|
||||
"bright-magenta-bg": color.BgHiMagenta,
|
||||
"bright-cyan-bg": color.BgHiCyan,
|
||||
"bright-white-bg": color.BgHiWhite,
|
||||
}
|
||||
|
||||
func (s *Styler) attributes(rules []core.StyleRule) ([]color.Attribute, error) {
|
||||
attrs := make([]color.Attribute, 0)
|
||||
|
||||
for _, rule := range rules {
|
||||
attr, ok := attrsMapping[rule]
|
||||
if !ok {
|
||||
return attrs, fmt.Errorf("unknown styling rule: %v", rule)
|
||||
} else {
|
||||
attrs = append(attrs, attr)
|
||||
}
|
||||
}
|
||||
|
||||
return attrs, nil
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
package tty
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/mickael-menu/zk/core"
|
||||
"github.com/mickael-menu/zk/util/assert"
|
||||
)
|
||||
|
||||
func createStyler() *Styler {
|
||||
color.NoColor = false // Otherwise the color codes are not injected during tests
|
||||
return &Styler{}
|
||||
}
|
||||
|
||||
func TestStyleNoRule(t *testing.T) {
|
||||
res, err := createStyler().Style("Hello")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, res, "Hello")
|
||||
}
|
||||
|
||||
func TestStyleOneRule(t *testing.T) {
|
||||
res, err := createStyler().Style("Hello", core.StyleRule("red"))
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, res, "\033[31mHello\033[0m")
|
||||
}
|
||||
|
||||
func TestStyleMultipleRule(t *testing.T) {
|
||||
res, err := createStyler().Style("Hello", core.StyleRule("red"), core.StyleRule("bold"))
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, res, "\033[31;1mHello\033[0m")
|
||||
}
|
||||
|
||||
func TestStyleUnknownRule(t *testing.T) {
|
||||
_, err := createStyler().Style("Hello", core.StyleRule("unknown"))
|
||||
assert.Err(t, err, "unknown styling rule: unknown")
|
||||
}
|
||||
|
||||
func TestStyleAllRules(t *testing.T) {
|
||||
styler := createStyler()
|
||||
test := func(rule string, expected string) {
|
||||
res, err := styler.Style("Hello", core.StyleRule(rule))
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, res, "\033["+expected+"Hello\033[0m")
|
||||
}
|
||||
|
||||
test("reset", "0m")
|
||||
test("bold", "1m")
|
||||
test("faint", "2m")
|
||||
test("italic", "3m")
|
||||
test("underline", "4m")
|
||||
test("blink-slow", "5m")
|
||||
test("blink-fast", "6m")
|
||||
test("hidden", "8m")
|
||||
test("strikethrough", "9m")
|
||||
|
||||
test("black", "30m")
|
||||
test("red", "31m")
|
||||
test("green", "32m")
|
||||
test("yellow", "33m")
|
||||
test("blue", "34m")
|
||||
test("magenta", "35m")
|
||||
test("cyan", "36m")
|
||||
test("white", "37m")
|
||||
|
||||
test("black-bg", "40m")
|
||||
test("red-bg", "41m")
|
||||
test("green-bg", "42m")
|
||||
test("yellow-bg", "43m")
|
||||
test("blue-bg", "44m")
|
||||
test("magenta-bg", "45m")
|
||||
test("cyan-bg", "46m")
|
||||
test("white-bg", "47m")
|
||||
|
||||
test("bright-black", "90m")
|
||||
test("bright-red", "91m")
|
||||
test("bright-green", "92m")
|
||||
test("bright-yellow", "93m")
|
||||
test("bright-blue", "94m")
|
||||
test("bright-magenta", "95m")
|
||||
test("bright-cyan", "96m")
|
||||
test("bright-white", "97m")
|
||||
|
||||
test("bright-black-bg", "100m")
|
||||
test("bright-red-bg", "101m")
|
||||
test("bright-green-bg", "102m")
|
||||
test("bright-yellow-bg", "103m")
|
||||
test("bright-blue-bg", "104m")
|
||||
test("bright-magenta-bg", "105m")
|
||||
test("bright-cyan-bg", "106m")
|
||||
test("bright-white-bg", "107m")
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package core
|
||||
|
||||
// Styler stylizes text according to predefined styling rules.
|
||||
//
|
||||
// A rule key can be either semantic, e.g. "title" or explicit, e.g. "red".
|
||||
type Styler interface {
|
||||
Style(text string, rules ...StyleRule) (string, error)
|
||||
}
|
||||
|
||||
// StyleRule is a key representing a single styling rule.
|
||||
type StyleRule string
|
||||
|
||||
// NullStyler is a Styler with no styling rules.
|
||||
var NullStyler = nullStyler{}
|
||||
|
||||
type nullStyler struct{}
|
||||
|
||||
func (s nullStyler) Style(text string, rule ...StyleRule) (string, error) {
|
||||
return text, nil
|
||||
}
|
Loading…
Reference in New Issue