1
0
mirror of https://github.com/gizak/termui.git synced 2025-04-27 13:48:51 +08:00
termui/style_parser.go

244 lines
5.5 KiB
Go
Raw Normal View History

2019-01-23 20:12:10 -08:00
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can
// be found in the LICENSE file.
package termui
import (
2022-11-07 04:21:40 -08:00
"fmt"
2019-01-23 20:12:10 -08:00
"strings"
)
const (
tokenFg = "fg"
tokenBg = "bg"
tokenModifier = "mod"
tokenItemSeparator = ","
tokenValueSeparator = ":"
tokenBeginStyledText = '['
tokenEndStyledText = ']'
tokenBeginStyle = '('
tokenEndStyle = ')'
)
type parserState uint
const (
parserStateDefault parserState = iota
parserStateStyleItems
parserStateStyledText
)
2019-02-23 16:54:20 -08:00
// StyleParserColorMap can be modified to add custom color parsing to text
var StyleParserColorMap = map[string]Color{
2019-01-23 20:12:10 -08:00
"red": ColorRed,
"blue": ColorBlue,
"black": ColorBlack,
"cyan": ColorCyan,
"yellow": ColorYellow,
"white": ColorWhite,
"clear": ColorClear,
"green": ColorGreen,
"magenta": ColorMagenta,
}
var modifierMap = map[string]Modifier{
"bold": ModifierBold,
"underline": ModifierUnderline,
"reverse": ModifierReverse,
}
// readStyle translates an []rune like `fg:red,mod:bold,bg:white` to a style
func readStyle(runes []rune, defaultStyle Style) Style {
style := defaultStyle
split := strings.Split(string(runes), tokenItemSeparator)
for _, item := range split {
pair := strings.Split(item, tokenValueSeparator)
if len(pair) == 2 {
switch pair[0] {
case tokenFg:
2019-02-23 16:54:20 -08:00
style.Fg = StyleParserColorMap[pair[1]]
2019-01-23 20:12:10 -08:00
case tokenBg:
2019-02-23 16:54:20 -08:00
style.Bg = StyleParserColorMap[pair[1]]
2019-01-23 20:12:10 -08:00
case tokenModifier:
style.Modifier = modifierMap[pair[1]]
}
}
}
return style
}
2022-11-07 04:21:40 -08:00
func processToken(token, previous string) (string, string) {
fmt.Println("1", token)
2022-11-07 03:55:40 -08:00
index := strings.Index(token, ")")
if index == -1 {
2022-11-07 04:21:40 -08:00
return "", ""
2022-11-07 03:55:40 -08:00
}
styleString := token[0:index]
2022-11-07 04:21:40 -08:00
restOfString := token[index:]
return styleString, restOfString
}
2022-11-07 08:26:51 -08:00
func lookLeftForBracket(s string) (string, string) {
index := strings.LastIndex(s, "[")
return s[0:index], s[index+1:]
}
func lookRightForEndStyle(s string) (string, string) {
index := strings.Index(s, ")")
return s[0:index], s[index+1:]
}
func BreakByStyles(s string) []string {
tokens := strings.Split(s, "](")
if len(tokens) == 1 {
return tokens
}
2022-11-07 08:54:53 -08:00
buff := []string{}
2022-11-07 08:26:51 -08:00
styleString := ""
remainder := tokens[0]
i := 1
for {
prefix, item := lookLeftForBracket(remainder)
styleString, remainder = lookRightForEndStyle(tokens[i])
i++
2022-11-07 08:54:53 -08:00
buff = append(buff, prefix)
buff = append(buff, item)
buff = append(buff, styleString)
2022-11-07 08:26:51 -08:00
if !strings.Contains(remainder, "[") {
2022-11-07 08:54:53 -08:00
buff = append(buff, remainder)
2022-11-07 08:26:51 -08:00
break
}
}
2022-11-07 08:54:53 -08:00
return buff
2022-11-07 08:26:51 -08:00
}
2022-11-07 08:54:53 -08:00
func containsColorOrMod(s string) bool {
if strings.Contains(s, "fg:") {
return true
}
if strings.Contains(s, "bg:") {
return true
}
if strings.Contains(s, "mod:") {
return true
2022-11-07 04:24:12 -08:00
}
2022-11-07 08:26:51 -08:00
2022-11-07 08:54:53 -08:00
return false
}
2019-02-23 16:54:20 -08:00
// ParseStyles parses a string for embedded Styles and returns []Cell with the correct styling.
2019-02-01 22:16:02 -08:00
// Uses defaultStyle for any text without an embedded style.
// Syntax is of the form [text](fg:<color>,mod:<attribute>,bg:<color>).
// Ordering does not matter. All fields are optional.
2019-02-23 16:54:20 -08:00
func ParseStyles(s string, defaultStyle Style) []Cell {
cells := []Cell{}
2022-11-07 08:54:53 -08:00
fmt.Println("11")
items := BreakByStyles(s)
fmt.Println("11", len(items))
if len(items) == 1 {
runes := []rune(s)
for _, _rune := range runes {
cells = append(cells, Cell{_rune, defaultStyle})
}
return cells
}
2022-11-07 03:55:40 -08:00
2022-11-07 08:54:53 -08:00
//test blue fg:blue,bg:white,mod:bold and red fg:red and maybe even foo bg:red !
for i := len(items) - 1; i > -1; i-- {
if containsColorOrMod(items[i]) {
} else {
fmt.Println(items[i])
}
}
return cells
}
func ParseStyles2(s string, defaultStyle Style) []Cell {
2019-01-23 20:12:10 -08:00
cells := []Cell{}
runes := []rune(s)
state := parserStateDefault
styledText := []rune{}
styleItems := []rune{}
squareCount := 0
reset := func() {
styledText = []rune{}
styleItems = []rune{}
state = parserStateDefault
squareCount = 0
}
rollback := func() {
cells = append(cells, RunesToStyledCells(styledText, defaultStyle)...)
cells = append(cells, RunesToStyledCells(styleItems, defaultStyle)...)
reset()
}
// chop first and last runes
chop := func(s []rune) []rune {
return s[1 : len(s)-1]
}
for i, _rune := range runes {
switch state {
case parserStateDefault:
if _rune == tokenBeginStyledText {
state = parserStateStyledText
squareCount = 1
styledText = append(styledText, _rune)
} else {
cells = append(cells, Cell{_rune, defaultStyle})
}
case parserStateStyledText:
switch {
case squareCount == 0:
switch _rune {
case tokenBeginStyle:
state = parserStateStyleItems
styleItems = append(styleItems, _rune)
default:
rollback()
switch _rune {
case tokenBeginStyledText:
state = parserStateStyledText
squareCount = 1
styleItems = append(styleItems, _rune)
default:
cells = append(cells, Cell{_rune, defaultStyle})
}
}
case len(runes) == i+1:
rollback()
styledText = append(styledText, _rune)
case _rune == tokenBeginStyledText:
squareCount++
styledText = append(styledText, _rune)
case _rune == tokenEndStyledText:
squareCount--
styledText = append(styledText, _rune)
default:
styledText = append(styledText, _rune)
}
case parserStateStyleItems:
styleItems = append(styleItems, _rune)
if _rune == tokenEndStyle {
style := readStyle(chop(styleItems), defaultStyle)
cells = append(cells, RunesToStyledCells(chop(styledText), style)...)
reset()
} else if len(runes) == i+1 {
rollback()
}
}
}
return cells
}