aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/gizak/termui/textbuilder.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/gizak/termui/textbuilder.go')
-rw-r--r--vendor/github.com/gizak/termui/textbuilder.go278
1 files changed, 278 insertions, 0 deletions
diff --git a/vendor/github.com/gizak/termui/textbuilder.go b/vendor/github.com/gizak/termui/textbuilder.go
new file mode 100644
index 000000000..06a019bed
--- /dev/null
+++ b/vendor/github.com/gizak/termui/textbuilder.go
@@ -0,0 +1,278 @@
+// Copyright 2016 Zack Guo <gizak@icloud.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 (
+ "regexp"
+ "strings"
+
+ "github.com/mitchellh/go-wordwrap"
+)
+
+// TextBuilder is a minimal interface to produce text []Cell using specific syntax (markdown).
+type TextBuilder interface {
+ Build(s string, fg, bg Attribute) []Cell
+}
+
+// DefaultTxBuilder is set to be MarkdownTxBuilder.
+var DefaultTxBuilder = NewMarkdownTxBuilder()
+
+// MarkdownTxBuilder implements TextBuilder interface, using markdown syntax.
+type MarkdownTxBuilder struct {
+ baseFg Attribute
+ baseBg Attribute
+ plainTx []rune
+ markers []marker
+}
+
+type marker struct {
+ st int
+ ed int
+ fg Attribute
+ bg Attribute
+}
+
+var colorMap = map[string]Attribute{
+ "red": ColorRed,
+ "blue": ColorBlue,
+ "black": ColorBlack,
+ "cyan": ColorCyan,
+ "yellow": ColorYellow,
+ "white": ColorWhite,
+ "default": ColorDefault,
+ "green": ColorGreen,
+ "magenta": ColorMagenta,
+}
+
+var attrMap = map[string]Attribute{
+ "bold": AttrBold,
+ "underline": AttrUnderline,
+ "reverse": AttrReverse,
+}
+
+func rmSpc(s string) string {
+ reg := regexp.MustCompile(`\s+`)
+ return reg.ReplaceAllString(s, "")
+}
+
+// readAttr translates strings like `fg-red,fg-bold,bg-white` to fg and bg Attribute
+func (mtb MarkdownTxBuilder) readAttr(s string) (Attribute, Attribute) {
+ fg := mtb.baseFg
+ bg := mtb.baseBg
+
+ updateAttr := func(a Attribute, attrs []string) Attribute {
+ for _, s := range attrs {
+ // replace the color
+ if c, ok := colorMap[s]; ok {
+ a &= 0xFF00 // erase clr 0 ~ 8 bits
+ a |= c // set clr
+ }
+ // add attrs
+ if c, ok := attrMap[s]; ok {
+ a |= c
+ }
+ }
+ return a
+ }
+
+ ss := strings.Split(s, ",")
+ fgs := []string{}
+ bgs := []string{}
+ for _, v := range ss {
+ subs := strings.Split(v, "-")
+ if len(subs) > 1 {
+ if subs[0] == "fg" {
+ fgs = append(fgs, subs[1])
+ }
+ if subs[0] == "bg" {
+ bgs = append(bgs, subs[1])
+ }
+ }
+ }
+
+ fg = updateAttr(fg, fgs)
+ bg = updateAttr(bg, bgs)
+ return fg, bg
+}
+
+func (mtb *MarkdownTxBuilder) reset() {
+ mtb.plainTx = []rune{}
+ mtb.markers = []marker{}
+}
+
+// parse streams and parses text into normalized text and render sequence.
+func (mtb *MarkdownTxBuilder) parse(str string) {
+ rs := str2runes(str)
+ normTx := []rune{}
+ square := []rune{}
+ brackt := []rune{}
+ accSquare := false
+ accBrackt := false
+ cntSquare := 0
+
+ reset := func() {
+ square = []rune{}
+ brackt = []rune{}
+ accSquare = false
+ accBrackt = false
+ cntSquare = 0
+ }
+ // pipe stacks into normTx and clear
+ rollback := func() {
+ normTx = append(normTx, square...)
+ normTx = append(normTx, brackt...)
+ reset()
+ }
+ // chop first and last
+ chop := func(s []rune) []rune {
+ return s[1 : len(s)-1]
+ }
+
+ for i, r := range rs {
+ switch {
+ // stacking brackt
+ case accBrackt:
+ brackt = append(brackt, r)
+ if ')' == r {
+ fg, bg := mtb.readAttr(string(chop(brackt)))
+ st := len(normTx)
+ ed := len(normTx) + len(square) - 2
+ mtb.markers = append(mtb.markers, marker{st, ed, fg, bg})
+ normTx = append(normTx, chop(square)...)
+ reset()
+ } else if i+1 == len(rs) {
+ rollback()
+ }
+ // stacking square
+ case accSquare:
+ switch {
+ // squares closed and followed by a '('
+ case cntSquare == 0 && '(' == r:
+ accBrackt = true
+ brackt = append(brackt, '(')
+ // squares closed but not followed by a '('
+ case cntSquare == 0:
+ rollback()
+ if '[' == r {
+ accSquare = true
+ cntSquare = 1
+ brackt = append(brackt, '[')
+ } else {
+ normTx = append(normTx, r)
+ }
+ // hit the end
+ case i+1 == len(rs):
+ square = append(square, r)
+ rollback()
+ case '[' == r:
+ cntSquare++
+ square = append(square, '[')
+ case ']' == r:
+ cntSquare--
+ square = append(square, ']')
+ // normal char
+ default:
+ square = append(square, r)
+ }
+ // stacking normTx
+ default:
+ if '[' == r {
+ accSquare = true
+ cntSquare = 1
+ square = append(square, '[')
+ } else {
+ normTx = append(normTx, r)
+ }
+ }
+ }
+
+ mtb.plainTx = normTx
+}
+
+func wrapTx(cs []Cell, wl int) []Cell {
+ tmpCell := make([]Cell, len(cs))
+ copy(tmpCell, cs)
+
+ // get the plaintext
+ plain := CellsToStr(cs)
+
+ // wrap
+ plainWrapped := wordwrap.WrapString(plain, uint(wl))
+
+ // find differences and insert
+ finalCell := tmpCell // finalcell will get the inserts and is what is returned
+
+ plainRune := []rune(plain)
+ plainWrappedRune := []rune(plainWrapped)
+ trigger := "go"
+ plainRuneNew := plainRune
+
+ for trigger != "stop" {
+ plainRune = plainRuneNew
+ for i := range plainRune {
+ if plainRune[i] == plainWrappedRune[i] {
+ trigger = "stop"
+ } else if plainRune[i] != plainWrappedRune[i] && plainWrappedRune[i] == 10 {
+ trigger = "go"
+ cell := Cell{10, 0, 0}
+ j := i - 0
+
+ // insert a cell into the []Cell in correct position
+ tmpCell[i] = cell
+
+ // insert the newline into plain so we avoid indexing errors
+ plainRuneNew = append(plainRune, 10)
+ copy(plainRuneNew[j+1:], plainRuneNew[j:])
+ plainRuneNew[j] = plainWrappedRune[j]
+
+ // restart the inner for loop until plain and plain wrapped are
+ // the same; yeah, it's inefficient, but the text amounts
+ // should be small
+ break
+
+ } else if plainRune[i] != plainWrappedRune[i] &&
+ plainWrappedRune[i-1] == 10 && // if the prior rune is a newline
+ plainRune[i] == 32 { // and this rune is a space
+ trigger = "go"
+ // need to delete plainRune[i] because it gets rid of an extra
+ // space
+ plainRuneNew = append(plainRune[:i], plainRune[i+1:]...)
+ break
+
+ } else {
+ trigger = "stop" // stops the outer for loop
+ }
+ }
+ }
+
+ finalCell = tmpCell
+
+ return finalCell
+}
+
+// Build implements TextBuilder interface.
+func (mtb MarkdownTxBuilder) Build(s string, fg, bg Attribute) []Cell {
+ mtb.baseFg = fg
+ mtb.baseBg = bg
+ mtb.reset()
+ mtb.parse(s)
+ cs := make([]Cell, len(mtb.plainTx))
+ for i := range cs {
+ cs[i] = Cell{Ch: mtb.plainTx[i], Fg: fg, Bg: bg}
+ }
+ for _, mrk := range mtb.markers {
+ for i := mrk.st; i < mrk.ed; i++ {
+ cs[i].Fg = mrk.fg
+ cs[i].Bg = mrk.bg
+ }
+ }
+
+ return cs
+}
+
+// NewMarkdownTxBuilder returns a TextBuilder employing markdown syntax.
+func NewMarkdownTxBuilder() TextBuilder {
+ return MarkdownTxBuilder{}
+}