dep ensure

Michael MurΓ© created

Change summary

vendor/github.com/MichaelMure/go-term-text/.travis.yml     |   2 
vendor/github.com/MichaelMure/go-term-text/Readme.md       |   9 
vendor/github.com/MichaelMure/go-term-text/escape_state.go | 265 ++++++++
vendor/github.com/MichaelMure/go-term-text/go.mod          |   4 
vendor/github.com/MichaelMure/go-term-text/go.sum          |   4 
vendor/github.com/MichaelMure/go-term-text/wrap.go         | 149 +++-
6 files changed, 387 insertions(+), 46 deletions(-)

Detailed changes

vendor/github.com/MichaelMure/go-term-text/Readme.md πŸ”—

@@ -20,6 +20,7 @@ Included algorithms cover:
 - trimming
 - alignment
 - escape sequence extraction and reapplication
+- escape sequence snapshot and simplification
 - truncation
 
 ## Example
@@ -31,7 +32,7 @@ import (
 	"fmt"
 	"strings"
 
-	text "github.com/MichaelMure/go-term-text"
+	"github.com/MichaelMure/go-term-text"
 )
 
 func main() {
@@ -41,8 +42,10 @@ func main() {
     		"various graphic design. δΈ€εͺ A Quick \x1b[31mζ•ζ·ηš„η‹ Fox " +
     		"η‹Έθ·³θΏ‡δΊ†\x1b[0mDogδΈ€εͺ懒狗。"
 
-	output, n := text.WrapWithPadIndent(input, 60,
-    		"\x1b[34m<-indent-> \x1b[0m", "\x1b[33m<-pad-> \x1b[0m")
+	output, n := text.Wrap(input, 60,
+            text.WrapIndent("\x1b[34m<-indent-> \x1b[0m"),
+            text.WrapPad("\x1b[33m<-pad-> \x1b[0m"),
+    )
 
 	fmt.Printf("output has %d lines\n\n", n)
 

vendor/github.com/MichaelMure/go-term-text/escape_state.go πŸ”—

@@ -0,0 +1,265 @@
+package text
+
+import (
+	"fmt"
+	"strconv"
+	"strings"
+)
+
+const Escape = '\x1b'
+
+type EscapeState struct {
+	Bold       bool
+	Dim        bool
+	Italic     bool
+	Underlined bool
+	Blink      bool
+	Reverse    bool
+	Hidden     bool
+	CrossedOut bool
+
+	FgColor Color
+	BgColor Color
+}
+
+type Color interface {
+	Codes() []string
+}
+
+func (es *EscapeState) Witness(s string) {
+	inEscape := false
+	var start int
+
+	runes := []rune(s)
+
+	for i, r := range runes {
+		if r == Escape {
+			inEscape = true
+			start = i
+			continue
+		}
+		if inEscape {
+			if r == 'm' {
+				inEscape = false
+				es.witnessCode(string(runes[start+1 : i]))
+			}
+			continue
+		}
+	}
+}
+
+func (es *EscapeState) witnessCode(s string) {
+	if s == "" {
+		return
+	}
+	if s == "[" {
+		es.reset()
+		return
+	}
+	if len(s) < 2 {
+		return
+	}
+	if s[0] != '[' {
+		return
+	}
+
+	s = s[1:]
+	split := strings.Split(s, ";")
+
+	dequeue := func() {
+		split = split[1:]
+	}
+
+	color := func(ground int) Color {
+		if len(split) < 1 {
+			// the whole sequence is broken, ignoring the rest
+			return nil
+		}
+
+		subCode := split[0]
+		dequeue()
+
+		switch subCode {
+		case "2":
+			if len(split) < 3 {
+				return nil
+			}
+			r, err := strconv.Atoi(split[0])
+			dequeue()
+			if err != nil {
+				return nil
+			}
+			g, err := strconv.Atoi(split[0])
+			dequeue()
+			if err != nil {
+				return nil
+			}
+			b, err := strconv.Atoi(split[0])
+			dequeue()
+			if err != nil {
+				return nil
+			}
+			return &ColorRGB{ground: ground, R: r, G: g, B: b}
+
+		case "5":
+			if len(split) < 1 {
+				return nil
+			}
+			index, err := strconv.Atoi(split[0])
+			dequeue()
+			if err != nil {
+				return nil
+			}
+			return &Color256{ground: ground, Index: index}
+
+		}
+		return nil
+	}
+
+	for len(split) > 0 {
+		code, err := strconv.Atoi(split[0])
+		if err != nil {
+			return
+		}
+		dequeue()
+
+		switch {
+		case code == 0:
+			es.reset()
+
+		case code == 1:
+			es.Bold = true
+		case code == 2:
+			es.Dim = true
+		case code == 3:
+			es.Italic = true
+		case code == 4:
+			es.Underlined = true
+		case code == 5:
+			es.Blink = true
+		// case code == 6:
+		case code == 7:
+			es.Reverse = true
+		case code == 8:
+			es.Hidden = true
+		case code == 9:
+			es.CrossedOut = true
+
+		case code == 21:
+			es.Bold = false
+		case code == 22:
+			es.Dim = false
+		case code == 23:
+			es.Italic = false
+		case code == 24:
+			es.Underlined = false
+		case code == 25:
+			es.Blink = false
+		// case code == 26:
+		case code == 27:
+			es.Reverse = false
+		case code == 28:
+			es.Hidden = false
+		case code == 29:
+			es.CrossedOut = false
+
+		case (code >= 30 && code <= 37) || code == 39 || (code >= 90 && code <= 97):
+			es.FgColor = ColorIndex(code)
+
+		case (code >= 40 && code <= 47) || code == 49 || (code >= 100 && code <= 107):
+			es.BgColor = ColorIndex(code)
+
+		case code == 38:
+			es.FgColor = color(code)
+			if es.FgColor == nil {
+				return
+			}
+
+		case code == 48:
+			es.BgColor = color(code)
+			if es.BgColor == nil {
+				return
+			}
+		}
+	}
+}
+
+func (es *EscapeState) reset() {
+	*es = EscapeState{}
+}
+
+func (es *EscapeState) String() string {
+	var codes []string
+
+	if es.Bold {
+		codes = append(codes, strconv.Itoa(1))
+	}
+	if es.Dim {
+		codes = append(codes, strconv.Itoa(2))
+	}
+	if es.Italic {
+		codes = append(codes, strconv.Itoa(3))
+	}
+	if es.Underlined {
+		codes = append(codes, strconv.Itoa(4))
+	}
+	if es.Blink {
+		codes = append(codes, strconv.Itoa(5))
+	}
+	if es.Reverse {
+		codes = append(codes, strconv.Itoa(7))
+	}
+	if es.Hidden {
+		codes = append(codes, strconv.Itoa(8))
+	}
+	if es.CrossedOut {
+		codes = append(codes, strconv.Itoa(9))
+	}
+
+	if es.FgColor != nil {
+		codes = append(codes, es.FgColor.Codes()...)
+	}
+	if es.BgColor != nil {
+		codes = append(codes, es.BgColor.Codes()...)
+	}
+
+	if len(codes) == 0 {
+		return "\x1b[0m"
+	}
+
+	return fmt.Sprintf("\x1b[%sm", strings.Join(codes, ";"))
+}
+
+type ColorIndex int
+
+func (cInd ColorIndex) Codes() []string {
+	return []string{strconv.Itoa(int(cInd))}
+}
+
+type Color256 struct {
+	ground int
+	Index  int
+}
+
+func (c256 Color256) Codes() []string {
+	return []string{
+		strconv.Itoa(c256.ground),
+		"5",
+		strconv.Itoa(c256.Index),
+	}
+}
+
+type ColorRGB struct {
+	ground  int
+	R, G, B int
+}
+
+func (cRGB ColorRGB) Codes() []string {
+	return []string{
+		strconv.Itoa(cRGB.ground),
+		"2",
+		strconv.Itoa(cRGB.R),
+		strconv.Itoa(cRGB.G),
+		strconv.Itoa(cRGB.B),
+	}
+}

vendor/github.com/MichaelMure/go-term-text/go.sum πŸ”—

@@ -1,7 +1,7 @@
 github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
-github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
+github.com/mattn/go-runewidth v0.0.6 h1:V2iyH+aX9C5fsYCpK60U8BYIvmhqxuOL3JZcqc1NB7k=
+github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=

vendor/github.com/MichaelMure/go-term-text/wrap.go πŸ”—

@@ -13,52 +13,89 @@ func init() {
 	runewidth.DefaultCondition.EastAsianWidth = false
 }
 
-// Wrap a text for a given line size.
-// Handle properly terminal color escape code
-func Wrap(text string, lineWidth int) (string, int) {
-	return WrapLeftPadded(text, lineWidth, 0)
+type wrapOpts struct {
+	indent string
+	pad    string
+	align  Alignment
 }
 
-// WrapLeftPadded wrap a text for a given line size with a left padding.
-// Handle properly terminal color escape code
-func WrapLeftPadded(text string, lineWidth int, leftPad int) (string, int) {
-	pad := strings.Repeat(" ", leftPad)
-	return WrapWithPad(text, lineWidth, pad)
+// WrapOption is a functional option for the Wrap() function
+type WrapOption func(opts *wrapOpts)
+
+// WrapPad configure the padding with a string for Wrap()
+func WrapPad(pad string) WrapOption {
+	return func(opts *wrapOpts) {
+		opts.pad = pad
+	}
 }
 
-// WrapWithPad wrap a text for a given line size with a custom left padding
-// Handle properly terminal color escape code
-func WrapWithPad(text string, lineWidth int, pad string) (string, int) {
-	return WrapWithPadIndent(text, lineWidth, pad, pad)
+// WrapPadded configure the padding with a number of space characters for Wrap()
+func WrapPadded(padLen int) WrapOption {
+	return func(opts *wrapOpts) {
+		opts.pad = strings.Repeat(" ", padLen)
+	}
 }
 
-// WrapWithPad wrap a text for a given line size with a custom left padding
-// This function also align the result depending on the requested alignment.
-// Handle properly terminal color escape code
-func WrapWithPadAlign(text string, lineWidth int, pad string, align Alignment) (string, int) {
-	return WrapWithPadIndentAlign(text, lineWidth, pad, pad, align)
+// WrapPad configure the indentation on the first line for Wrap()
+func WrapIndent(indent string) WrapOption {
+	return func(opts *wrapOpts) {
+		opts.indent = indent
+	}
 }
 
-// WrapWithPadIndent wrap a text for a given line size with a custom left padding
-// and a first line indent. The padding is not effective on the first line, indent
-// is used instead, which allow to implement indents and outdents.
-// Handle properly terminal color escape code
-func WrapWithPadIndent(text string, lineWidth int, indent string, pad string) (string, int) {
-	return WrapWithPadIndentAlign(text, lineWidth, indent, pad, NoAlign)
+// WrapAlign configure the text alignment for Wrap()
+func WrapAlign(align Alignment) WrapOption {
+	return func(opts *wrapOpts) {
+		opts.align = align
+	}
 }
 
-// WrapWithPadIndentAlign wrap a text for a given line size with a custom left padding
-// and a first line indent. The padding is not effective on the first line, indent
-// is used instead, which allow to implement indents and outdents.
-// This function also align the result depending on the requested alignment.
+// allWrapOpts compile the set of WrapOption into a final wrapOpts
+// from the default values.
+func allWrapOpts(opts []WrapOption) *wrapOpts {
+	wrapOpts := &wrapOpts{
+		indent: "",
+		pad:    "",
+		align:  NoAlign,
+	}
+	for _, opt := range opts {
+		opt(wrapOpts)
+	}
+	if wrapOpts.indent == "" {
+		wrapOpts.indent = wrapOpts.pad
+	}
+	return wrapOpts
+}
+
+// Wrap a text for a given line size.
 // Handle properly terminal color escape code
-func WrapWithPadIndentAlign(text string, lineWidth int, indent string, pad string, align Alignment) (string, int) {
+// Options are accepted to configure things like indent, padding or alignment.
+// Return the wrapped text and the number of lines
+func Wrap(text string, lineWidth int, opts ...WrapOption) (string, int) {
+	wrapOpts := allWrapOpts(opts)
+
+	if lineWidth <= 0 {
+		return "", 1
+	}
+
 	var lines []string
 	nbLine := 0
 
+	if len(wrapOpts.indent) >= lineWidth {
+		// fallback rendering
+		lines = append(lines, strings.Repeat("β­¬", lineWidth))
+		nbLine++
+		wrapOpts.indent = wrapOpts.pad
+	}
+	if len(wrapOpts.pad) >= lineWidth {
+		// fallback rendering
+		line := strings.Repeat("β­¬", lineWidth)
+		return strings.Repeat(line+"\n", 5), 5
+	}
+
 	// Start with the indent
-	padStr := indent
-	padLen := Len(indent)
+	padStr := wrapOpts.indent
+	padLen := Len(wrapOpts.indent)
 
 	// tabs are formatted as 4 spaces
 	text = strings.Replace(text, "\t", "    ", -1)
@@ -67,8 +104,8 @@ func WrapWithPadIndentAlign(text string, lineWidth int, indent string, pad strin
 	for i, line := range strings.Split(text, "\n") {
 		// on the second line, use the padding instead
 		if i == 1 {
-			padStr = pad
-			padLen = Len(pad)
+			padStr = wrapOpts.pad
+			padLen = Len(wrapOpts.pad)
 		}
 
 		if line == "" || strings.TrimSpace(line) == "" {
@@ -87,14 +124,14 @@ func WrapWithPadIndentAlign(text string, lineWidth int, indent string, pad strin
 			// use the first wrapped line, ignore everything else and
 			// wrap the remaining of the line with the normal padding.
 
-			content := LineAlign(strings.TrimRight(split[0], " "), lineWidth-padLen, align)
+			content := LineAlign(strings.TrimRight(split[0], " "), lineWidth-padLen, wrapOpts.align)
 			lines = append(lines, padStr+content)
 			nbLine++
 			line = strings.TrimPrefix(line, split[0])
 			line = strings.TrimLeft(line, " ")
 
-			padStr = pad
-			padLen = Len(pad)
+			padStr = wrapOpts.pad
+			padLen = Len(wrapOpts.pad)
 			wrapped = softwrapLine(line, lineWidth-padLen)
 			split = strings.Split(wrapped, "\n")
 		}
@@ -102,10 +139,10 @@ func WrapWithPadIndentAlign(text string, lineWidth int, indent string, pad strin
 		for j, seg := range split {
 			if j == 0 {
 				// keep the left padding of the wrapped line
-				content := LineAlign(strings.TrimRight(seg, " "), lineWidth-padLen, align)
+				content := LineAlign(strings.TrimRight(seg, " "), lineWidth-padLen, wrapOpts.align)
 				lines = append(lines, padStr+content)
 			} else {
-				content := LineAlign(strings.TrimSpace(seg), lineWidth-padLen, align)
+				content := LineAlign(strings.TrimSpace(seg), lineWidth-padLen, wrapOpts.align)
 				lines = append(lines, padStr+content)
 			}
 			nbLine++
@@ -115,6 +152,42 @@ func WrapWithPadIndentAlign(text string, lineWidth int, indent string, pad strin
 	return strings.Join(lines, "\n"), nbLine
 }
 
+// WrapLeftPadded wrap a text for a given line size with a left padding.
+// Handle properly terminal color escape code
+func WrapLeftPadded(text string, lineWidth int, leftPad int) (string, int) {
+	return Wrap(text, lineWidth, WrapPadded(leftPad))
+}
+
+// WrapWithPad wrap a text for a given line size with a custom left padding
+// Handle properly terminal color escape code
+func WrapWithPad(text string, lineWidth int, pad string) (string, int) {
+	return Wrap(text, lineWidth, WrapPad(pad))
+}
+
+// WrapWithPad wrap a text for a given line size with a custom left padding
+// This function also align the result depending on the requested alignment.
+// Handle properly terminal color escape code
+func WrapWithPadAlign(text string, lineWidth int, pad string, align Alignment) (string, int) {
+	return Wrap(text, lineWidth, WrapPad(pad), WrapAlign(align))
+}
+
+// WrapWithPadIndent wrap a text for a given line size with a custom left padding
+// and a first line indent. The padding is not effective on the first line, indent
+// is used instead, which allow to implement indents and outdents.
+// Handle properly terminal color escape code
+func WrapWithPadIndent(text string, lineWidth int, indent string, pad string) (string, int) {
+	return Wrap(text, lineWidth, WrapIndent(indent), WrapPad(pad))
+}
+
+// WrapWithPadIndentAlign wrap a text for a given line size with a custom left padding
+// and a first line indent. The padding is not effective on the first line, indent
+// is used instead, which allow to implement indents and outdents.
+// This function also align the result depending on the requested alignment.
+// Handle properly terminal color escape code
+func WrapWithPadIndentAlign(text string, lineWidth int, indent string, pad string, align Alignment) (string, int) {
+	return Wrap(text, lineWidth, WrapIndent(indent), WrapPad(pad), WrapAlign(align))
+}
+
 // Break a line into several lines so that each line consumes at most
 // 'textWidth' cells.  Lines break at groups of white spaces and multibyte
 // chars. Nothing is removed from the original text so that it behaves like a