Skip to content

Commit

Permalink
refactor: update and refactor the color tag parse logic
Browse files Browse the repository at this point in the history
  • Loading branch information
inhere committed Jun 28, 2022
1 parent daca06f commit efc6d3f
Show file tree
Hide file tree
Showing 3 changed files with 201 additions and 38 deletions.
210 changes: 172 additions & 38 deletions color_tag.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,11 @@ var (
* internal defined color tags
*************************************************************/

// There are internal defined color tags
// Usage: <tag>content text</>
// There are internal defined fg color tags
//
// Usage:
// <tag>content text</>
//
// @notice 加 0 在前面是为了防止之前的影响到现在的设置
var colorTags = map[string]string{
// basic tags
Expand Down Expand Up @@ -72,7 +75,9 @@ var colorTags = map[string]string{
"magenta": "0;35",
"mga": "0;35", // short name
"magentaB": "1;35", // with bold
"magenta1": "1;35",
"mgb": "1;35",
"mga1": "1;35",
"mgaB": "1;35",

// light/hi tags
Expand All @@ -90,7 +95,7 @@ var colorTags = map[string]string{
"light_magenta": "0;95",
"hiMagenta": "0;95",
"hi_magenta": "0;95",
"lightMagentaB": "1;95", // with bold
"lightMagenta1": "1;95", // with bold
"hiMagentaB": "1;95", // with bold
"hi_magenta_b": "1;95",
"lightRed": "0;91",
Expand Down Expand Up @@ -127,9 +132,14 @@ var colorTags = map[string]string{
// option
"bold": "1",
"b": "1",
"italic": "3",
"i": "3", // italic
"underscore": "4",
"us": "4", // short name for 'underscore'
"blink": "5",
"fb": "6", // fast blink
"reverse": "7",
"st": "9", // strikethrough

// alert tags, like bootstrap's alert
"suc": "1;32", // same "green" and "bold"
Expand All @@ -146,12 +156,141 @@ var colorTags = map[string]string{
"error": "97;41", // fg light white; bg red
}

/*************************************************************
* internal defined tag attributes
*************************************************************/

// built-in attributes for fg,bg 16-colors and op codes.
var (
attrFgs = map[string]string{
// basic colors

"black": FgBlack.Code(),
"red": "31",
"green": "32",
"brown": "33", // #A52A2A
"yellow": "33",
"ylw": "33",
"blue": "34",
"cyan": "36",
"magenta": "35",
"mga": "35",
"white": FgWhite.Code(),
"default": "39", // no color
"normal": "39", // no color

// light/hi colors

"darkGray": FgDarkGray.Code(),
"dark_gray": "90",
"gray": "90",
"lightYellow": "93",
"light_yellow": "93",
"hiYellow": "93",
"hi_yellow": "93",
"lightMagenta": "95",
"light_magenta": "95",
"hiMagenta": "95",
"hi_magenta": "95",
"hi_mga": "95",
"lightRed": "91",
"light_red": "91",
"hiRed": "91",
"hi_red": "91",
"lightGreen": "92",
"light_green": "92",
"hiGreen": "92",
"hi_green": "92",
"lightBlue": "94",
"light_blue": "94",
"hiBlue": "94",
"hi_blue": "94",
"lightCyan": "96",
"light_cyan": "96",
"hiCyan": "96",
"hi_cyan": "96",
"lightWhite": "97",
"light_white": "97",
}

attrBgs = map[string]string{
// basic colors

"black": BgBlack.Code(),
"red": "41",
"green": "42",
"brown": "43", // #A52A2A
"yellow": "43",
"ylw": "43",
"blue": "44",
"cyan": "46",
"magenta": "45",
"mga": "45",
"white": FgWhite.Code(),
"default": "49", // no color
"normal": "49", // no color

// light/hi colors

"darkGray": BgDarkGray.Code(),
"dark_gray": "100",
"gray": "100",
"lightYellow": "103",
"light_yellow": "103",
"hiYellow": "103",
"hi_yellow": "103",
"lightMagenta": "105",
"light_magenta": "105",
"hiMagenta": "105",
"hi_magenta": "105",
"hi_mga": "105",
"lightRed": "101",
"light_red": "101",
"hiRed": "101",
"hi_red": "101",
"lightGreen": "102",
"light_green": "102",
"hiGreen": "102",
"hi_green": "102",
"lightBlue": "104",
"light_blue": "104",
"hiBlue": "104",
"hi_blue": "104",
"lightCyan": "106",
"light_cyan": "106",
"hiCyan": "106",
"hi_cyan": "106",
"lightWhite": BgLightWhite.Code(),
"light_white": "107",
}

attrOpts = map[string]string{
"reset": OpReset.Code(),
"bold": OpBold.Code(),
"b": OpBold.Code(),
"fuzzy": OpFuzzy.Code(),
"italic": OpItalic.Code(),
"i": OpItalic.Code(),
"underscore": OpUnderscore.Code(),
"us": OpUnderscore.Code(),
"u": OpUnderscore.Code(),
"blink": OpBlink.Code(),
"fastblink": OpFastBlink.Code(),
"fb": OpFastBlink.Code(),
"reverse": OpReverse.Code(),
"concealed": OpConcealed.Code(),
"strikethrough": OpStrikethrough.Code(),
"st": OpStrikethrough.Code(),
}
)

/*************************************************************
* parse color tags
*************************************************************/

var (
tagParser = TagParser{}
// regex for match color 256 code
rxNumStr = regexp.MustCompile("^[0-9]{1,3}$")
rxHexCode = regexp.MustCompile("^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$")
)
Expand Down Expand Up @@ -182,11 +321,18 @@ func (tp *TagParser) ParseByEnv(str string) string {
if !Enable || !SupportColor() {
return ClearTag(str)
}

return tp.Parse(str)
}

// Parse parse given string, replace color tag and return rendered string
//
// Use built in tags:
// <TAG_NAME>CONTENT</>
// // e.g: `<info>message</>`
//
// Custom tag attributes:
// `<fg=VALUE;bg=VALUE;op=VALUES>CONTENT</>`
// // e.g: `<fg=167;bg=232>wel</>`
func (tp *TagParser) Parse(str string) string {
// not contains color tag
if !strings.Contains(str, "</>") {
Expand All @@ -198,14 +344,15 @@ func (tp *TagParser) Parse(str string) string {

// item: 0 full text 1 tag name 2 tag content
for _, item := range matched {
full, tag, content := item[0], item[1], item[2]
full, tag, body := item[0], item[1], item[2]

// use defined tag name: "<info>content</>" -> tag: "info"
// use defined color tag name: "<info>content</>" -> tag: "info"
if !strings.ContainsRune(tag, '=') {
code := colorTags[tag]
if len(code) > 0 {
now := RenderString(code, content)
// old := WrapTag(content, tag) is equals to var 'full'
if code := colorTags[tag]; len(code) > 0 {
str = strings.Replace(str, full, RenderString(code, body), 1)
} else if code, ok := namedRgbMap[tag]; ok {
code = strings.Replace(code, ",", ";", -1)
now := RenderString(FgRGBPfx+code, body)
str = strings.Replace(str, full, now, 1)
}
continue
Expand All @@ -214,18 +361,13 @@ func (tp *TagParser) Parse(str string) string {
// custom color in tag
// - basic: "fg=white;bg=blue;op=bold"
if code := ParseCodeFromAttr(tag); len(code) > 0 {
now := RenderString(code, content)
str = strings.Replace(str, full, now, 1)
str = strings.Replace(str, full, RenderString(code, body), 1)
}
}

return str
}

// func (tp *TagParser) ParseAttr(attr string) (code string) {
// return
// }

// ReplaceTag parse string, replace color tag and return rendered string
func ReplaceTag(str string) string {
return tagParser.ParseByEnv(str)
Expand All @@ -236,17 +378,20 @@ func ReplaceTag(str string) string {
// attr format:
// // VALUE please see var: FgColors, BgColors, AllOptions
// "fg=VALUE;bg=VALUE;op=VALUE"
//
// 16 color:
// "fg=yellow"
// "bg=red"
// "op=bold,underscore" option is allow multi value
// "op=bold,underscore" // option is allow multi value
// "fg=white;bg=blue;op=bold"
// "fg=white;op=bold,underscore"
//
// 256 color:
// "fg=167"
// "fg=167;bg=23"
// "fg=167;bg=23;op=bold"
// true color:
//
// True color:
// // hex
// "fg=fc1cac"
// "fg=fc1cac;bg=c2c3c4"
Expand All @@ -270,31 +415,27 @@ func ParseCodeFromAttr(attr string) (code string) {
pos, val := item[1], item[2]
switch pos {
case "fg":
if c, ok := FgColors[val]; ok { // basic
codes = append(codes, c.String())
} else if c, ok := ExFgColors[val]; ok { // extra
codes = append(codes, c.String())
if code, ok := attrFgs[val]; ok { // attr fg
codes = append(codes, code)
} else if code := rgbHex256toCode(val, false); code != "" {
codes = append(codes, code)
}
case "bg":
if c, ok := BgColors[val]; ok { // basic bg
codes = append(codes, c.String())
} else if c, ok := ExBgColors[val]; ok { // extra bg
codes = append(codes, c.String())
if code, ok := attrBgs[val]; ok { // attr bg
codes = append(codes, code)
} else if code := rgbHex256toCode(val, true); code != "" {
codes = append(codes, code)
}
case "op": // options allow multi value
if strings.Contains(val, ",") {
ns := strings.Split(val, ",")
for _, n := range ns {
if c, ok := AllOptions[n]; ok {
codes = append(codes, c.String())
if code, ok := attrOpts[n]; ok { // attr ops
codes = append(codes, code)
}
}
} else if c, ok := AllOptions[val]; ok {
codes = append(codes, c.String())
} else if code, ok := attrOpts[val]; ok {
codes = append(codes, code)
}
}
}
Expand Down Expand Up @@ -327,7 +468,6 @@ func ClearTag(s string) string {
if !strings.Contains(s, "</>") {
return s
}

return stripRegex.ReplaceAllString(s, "")
}

Expand All @@ -350,7 +490,6 @@ func WrapTag(s string, tag string) string {
if s == "" || tag == "" {
return s
}

return fmt.Sprintf("<%s>%s</>", tag, s)
}

Expand Down Expand Up @@ -410,12 +549,7 @@ func (tg Tag) Println(a ...interface{}) {

// Sprint render messages
func (tg Tag) Sprint(a ...interface{}) string {
name := string(tg)
// if stl := GetStyle(name); !stl.IsEmpty() {
// return stl.Render(args...)
// }

return RenderCode(GetTagCode(name), a...)
return RenderCode(GetTagCode(string(tg)), a...)
}

// Sprintf format and render messages
Expand Down
20 changes: 20 additions & 0 deletions color_tag_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package color

import (
"fmt"
"log"
"testing"

Expand Down Expand Up @@ -62,18 +63,34 @@ def <info>info text
is.Equal("", s)
}

func TestTagParser_Parse_c16_opt(t *testing.T) {
s := tagParser.Parse("<fg=mga;op=i>msg</>")

fmt.Println(s)
assert.Equal(t, "\x1b[35;3mmsg\x1b[0m", s)
}

func TestTagParser_Parse_named_rgb_code(t *testing.T) {
s := tagParser.Parse("<deepskyblue>deepskyblue style msg</>")

fmt.Println(s)
assert.Equal(t, "\x1b[38;2;0;191;255mdeepskyblue style msg\x1b[0m", s)
}

func TestTagParser_Parse_hex_rgb_c256(t *testing.T) {
is := assert.New(t)
p := NewTagParser()

s := "custom tag: <fg=e7b2a1>hello, welcome</>"
r := p.Parse(s)
fmt.Println(r)
is.NotContains(r, "<")
is.NotContains(r, ">")
is.Equal("custom tag: \x1B[38;2;231;178;161mhello, welcome\x1B[0m", r)

s = "custom tag: <fg=e7b2a1;bg=176;op=bold>hello, welcome</>"
r = p.Parse(s)
fmt.Println(r)
is.NotContains(r, "<")
is.NotContains(r, ">")
is.Equal("custom tag: \x1b[38;2;231;178;161;48;5;176;1mhello, welcome\x1b[0m", r)
Expand All @@ -85,6 +102,9 @@ func TestParseCodeFromAttr_basic(t *testing.T) {
s := ParseCodeFromAttr("=")
is.Equal("", s)

s = ParseCodeFromAttr("no attr")
is.Equal("", s)

s = ParseCodeFromAttr("fg=lightRed;bg=lightRed;op=bold,blink")
is.Equal("91;101;1;5", s)

Expand Down
Loading

0 comments on commit efc6d3f

Please sign in to comment.