289b30715d
This commit converts the dependency management from Godeps to the vendor folder, also switching the tool from godep to trash. Since the upstream tool lacks a few features proposed via a few PRs, until those PRs are merged in (if), use github.com/karalabe/trash. You can update dependencies via trash --update. All dependencies have been updated to their latest version. Parts of the build system are reworked to drop old notions of Godeps and invocation of the go vet command so that it doesn't run against the vendor folder, as that will just blow up during vetting. The conversion drops OpenCL (and hence GPU mining support) from ethash and our codebase. The short reasoning is that there's noone to maintain and having opencl libs in our deps messes up builds as go install ./... tries to build them, failing with unsatisfied link errors for the C OpenCL deps. golang.org/x/net/context is not vendored in. We expect it to be fetched by the user (i.e. using go get). To keep ci.go builds reproducible the package is "vendored" in build/_vendor.
279 lines
6.2 KiB
Go
279 lines
6.2 KiB
Go
// 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{}
|
|
}
|