mirror of
https://github.com/mudler/luet.git
synced 2025-09-01 23:37:07 +00:00
Update vendor
This commit is contained in:
137
vendor/github.com/jedib0t/go-pretty/text/align.go
generated
vendored
Normal file
137
vendor/github.com/jedib0t/go-pretty/text/align.go
generated
vendored
Normal file
@@ -0,0 +1,137 @@
|
||||
package text
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// Align denotes how text is to be aligned horizontally.
|
||||
type Align int
|
||||
|
||||
// Align enumerations
|
||||
const (
|
||||
AlignDefault Align = iota // same as AlignLeft
|
||||
AlignLeft // "left "
|
||||
AlignCenter // " center "
|
||||
AlignJustify // "justify it"
|
||||
AlignRight // " right"
|
||||
)
|
||||
|
||||
// Apply aligns the text as directed. For ex.:
|
||||
// * AlignDefault.Apply("Jon Snow", 12) returns "Jon Snow "
|
||||
// * AlignLeft.Apply("Jon Snow", 12) returns "Jon Snow "
|
||||
// * AlignCenter.Apply("Jon Snow", 12) returns " Jon Snow "
|
||||
// * AlignJustify.Apply("Jon Snow", 12) returns "Jon Snow"
|
||||
// * AlignRight.Apply("Jon Snow", 12) returns " Jon Snow"
|
||||
func (a Align) Apply(text string, maxLength int) string {
|
||||
text = a.trimString(text)
|
||||
sLen := utf8.RuneCountInString(text)
|
||||
sLenWoE := RuneCount(text)
|
||||
numEscChars := sLen - sLenWoE
|
||||
|
||||
// now, align the text
|
||||
switch a {
|
||||
case AlignDefault, AlignLeft:
|
||||
return fmt.Sprintf("%-"+strconv.Itoa(maxLength+numEscChars)+"s", text)
|
||||
case AlignCenter:
|
||||
if sLenWoE < maxLength {
|
||||
// left pad with half the number of spaces needed before using %text
|
||||
return fmt.Sprintf("%"+strconv.Itoa(maxLength+numEscChars)+"s",
|
||||
text+strings.Repeat(" ", int((maxLength-sLenWoE)/2)))
|
||||
}
|
||||
case AlignJustify:
|
||||
return a.justifyText(text, sLenWoE, maxLength)
|
||||
}
|
||||
return fmt.Sprintf("%"+strconv.Itoa(maxLength+numEscChars)+"s", text)
|
||||
}
|
||||
|
||||
// HTMLProperty returns the equivalent HTML horizontal-align tag property.
|
||||
func (a Align) HTMLProperty() string {
|
||||
switch a {
|
||||
case AlignLeft:
|
||||
return "align=\"left\""
|
||||
case AlignCenter:
|
||||
return "align=\"center\""
|
||||
case AlignJustify:
|
||||
return "align=\"justify\""
|
||||
case AlignRight:
|
||||
return "align=\"right\""
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// MarkdownProperty returns the equivalent Markdown horizontal-align separator.
|
||||
func (a Align) MarkdownProperty() string {
|
||||
switch a {
|
||||
case AlignLeft:
|
||||
return ":--- "
|
||||
case AlignCenter:
|
||||
return ":---:"
|
||||
case AlignRight:
|
||||
return " ---:"
|
||||
default:
|
||||
return " --- "
|
||||
}
|
||||
}
|
||||
|
||||
func (a Align) justifyText(text string, textLength int, maxLength int) string {
|
||||
// split the text into individual words
|
||||
wordsUnfiltered := strings.Split(text, " ")
|
||||
words := Filter(wordsUnfiltered, func(item string) bool {
|
||||
return item != ""
|
||||
})
|
||||
// empty string implies spaces for maxLength
|
||||
if len(words) == 0 {
|
||||
return strings.Repeat(" ", maxLength)
|
||||
}
|
||||
// get the number of spaces to insert into the text
|
||||
numSpacesNeeded := maxLength - textLength + strings.Count(text, " ")
|
||||
numSpacesNeededBetweenWords := 0
|
||||
if len(words) > 1 {
|
||||
numSpacesNeededBetweenWords = numSpacesNeeded / (len(words) - 1)
|
||||
}
|
||||
// create the output string word by word with spaces in between
|
||||
var outText strings.Builder
|
||||
outText.Grow(maxLength)
|
||||
for idx, word := range words {
|
||||
if idx > 0 {
|
||||
// insert spaces only after the first word
|
||||
if idx == len(words)-1 {
|
||||
// insert all the remaining space before the last word
|
||||
outText.WriteString(strings.Repeat(" ", numSpacesNeeded))
|
||||
numSpacesNeeded = 0
|
||||
} else {
|
||||
// insert the determined number of spaces between each word
|
||||
outText.WriteString(strings.Repeat(" ", numSpacesNeededBetweenWords))
|
||||
// and reduce the number of spaces needed after this
|
||||
numSpacesNeeded -= numSpacesNeededBetweenWords
|
||||
}
|
||||
}
|
||||
outText.WriteString(word)
|
||||
if idx == len(words)-1 && numSpacesNeeded > 0 {
|
||||
outText.WriteString(strings.Repeat(" ", numSpacesNeeded))
|
||||
}
|
||||
}
|
||||
return outText.String()
|
||||
}
|
||||
|
||||
func (a Align) trimString(text string) string {
|
||||
switch a {
|
||||
case AlignDefault, AlignLeft:
|
||||
if strings.HasSuffix(text, " ") {
|
||||
return strings.TrimRight(text, " ")
|
||||
}
|
||||
case AlignRight:
|
||||
if strings.HasPrefix(text, " ") {
|
||||
return strings.TrimLeft(text, " ")
|
||||
}
|
||||
default:
|
||||
if strings.HasPrefix(text, " ") || strings.HasSuffix(text, " ") {
|
||||
return strings.Trim(text, " ")
|
||||
}
|
||||
}
|
||||
return text
|
||||
}
|
55
vendor/github.com/jedib0t/go-pretty/text/ansi.go
generated
vendored
Normal file
55
vendor/github.com/jedib0t/go-pretty/text/ansi.go
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
package text
|
||||
|
||||
import "strings"
|
||||
|
||||
// ANSICodesSupported will be true on consoles where ANSI Escape Codes/Sequences
|
||||
// are supported.
|
||||
var ANSICodesSupported = areANSICodesSupported()
|
||||
|
||||
// Escape encodes the string with the ANSI Escape Sequence.
|
||||
// For ex.:
|
||||
// Escape("Ghost", "") == "Ghost"
|
||||
// Escape("Ghost", "\x1b[91m") == "\x1b[91mGhost\x1b[0m"
|
||||
// Escape("\x1b[94mGhost\x1b[0mLady", "\x1b[91m") == "\x1b[94mGhost\x1b[0m\x1b[91mLady\x1b[0m"
|
||||
// Escape("Nymeria\x1b[94mGhost\x1b[0mLady", "\x1b[91m") == "\x1b[91mNymeria\x1b[94mGhost\x1b[0m\x1b[91mLady\x1b[0m"
|
||||
// Escape("Nymeria \x1b[94mGhost\x1b[0m Lady", "\x1b[91m") == "\x1b[91mNymeria \x1b[94mGhost\x1b[0m\x1b[91m Lady\x1b[0m"
|
||||
func Escape(str string, escapeSeq string) string {
|
||||
out := ""
|
||||
if !strings.HasPrefix(str, EscapeStart) {
|
||||
out += escapeSeq
|
||||
}
|
||||
out += strings.Replace(str, EscapeReset, EscapeReset+escapeSeq, -1)
|
||||
if !strings.HasSuffix(out, EscapeReset) {
|
||||
out += EscapeReset
|
||||
}
|
||||
if strings.Contains(out, escapeSeq+EscapeReset) {
|
||||
out = strings.Replace(out, escapeSeq+EscapeReset, "", -1)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// StripEscape strips all ANSI Escape Sequence from the string.
|
||||
// For ex.:
|
||||
// StripEscape("Ghost") == "Ghost"
|
||||
// StripEscape("\x1b[91mGhost\x1b[0m") == "Ghost"
|
||||
// StripEscape("\x1b[94mGhost\x1b[0m\x1b[91mLady\x1b[0m") == "GhostLady"
|
||||
// StripEscape("\x1b[91mNymeria\x1b[94mGhost\x1b[0m\x1b[91mLady\x1b[0m") == "NymeriaGhostLady"
|
||||
// StripEscape("\x1b[91mNymeria \x1b[94mGhost\x1b[0m\x1b[91m Lady\x1b[0m") == "Nymeria Ghost Lady"
|
||||
func StripEscape(str string) string {
|
||||
var out strings.Builder
|
||||
out.Grow(RuneCount(str))
|
||||
|
||||
isEscSeq := false
|
||||
for _, sChr := range str {
|
||||
if sChr == EscapeStartRune {
|
||||
isEscSeq = true
|
||||
}
|
||||
if !isEscSeq {
|
||||
out.WriteRune(sChr)
|
||||
}
|
||||
if isEscSeq && sChr == EscapeStopRune {
|
||||
isEscSeq = false
|
||||
}
|
||||
}
|
||||
return out.String()
|
||||
}
|
7
vendor/github.com/jedib0t/go-pretty/text/ansi_unix.go
generated
vendored
Normal file
7
vendor/github.com/jedib0t/go-pretty/text/ansi_unix.go
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
// +build !windows
|
||||
|
||||
package text
|
||||
|
||||
func areANSICodesSupported() bool {
|
||||
return true
|
||||
}
|
31
vendor/github.com/jedib0t/go-pretty/text/ansi_windows.go
generated
vendored
Normal file
31
vendor/github.com/jedib0t/go-pretty/text/ansi_windows.go
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
// +build windows
|
||||
|
||||
package text
|
||||
|
||||
import (
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var (
|
||||
enableVTPMutex = sync.Mutex{}
|
||||
)
|
||||
|
||||
func areANSICodesSupported() bool {
|
||||
enableVTPMutex.Lock()
|
||||
defer enableVTPMutex.Unlock()
|
||||
|
||||
outHandle := windows.Handle(os.Stdout.Fd())
|
||||
var outMode uint32
|
||||
if err := windows.GetConsoleMode(outHandle, &outMode); err == nil {
|
||||
if outMode&windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING != 0 {
|
||||
return true
|
||||
}
|
||||
if err := windows.SetConsoleMode(outHandle, outMode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err == nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
183
vendor/github.com/jedib0t/go-pretty/text/color.go
generated
vendored
Normal file
183
vendor/github.com/jedib0t/go-pretty/text/color.go
generated
vendored
Normal file
@@ -0,0 +1,183 @@
|
||||
package text
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
colorsEnabled = areANSICodesSupported()
|
||||
)
|
||||
|
||||
// DisableColors (forcefully) disables color coding globally.
|
||||
func DisableColors() {
|
||||
colorsEnabled = false
|
||||
}
|
||||
|
||||
// EnableColors (forcefully) enables color coding globally.
|
||||
func EnableColors() {
|
||||
colorsEnabled = true
|
||||
}
|
||||
|
||||
// The logic here is inspired from github.com/fatih/color; the following is
|
||||
// the the bare minimum logic required to print Colored to the console.
|
||||
// The differences:
|
||||
// * This one caches the escape sequences for cases with multiple colors
|
||||
// * This one handles cases where the incoming already has colors in the
|
||||
// form of escape sequences; in which case, text that does not have any
|
||||
// escape sequences are colored/escaped
|
||||
|
||||
// Color represents a single color to render with.
|
||||
type Color int
|
||||
|
||||
// Base colors -- attributes in reality
|
||||
const (
|
||||
Reset Color = iota
|
||||
Bold
|
||||
Faint
|
||||
Italic
|
||||
Underline
|
||||
BlinkSlow
|
||||
BlinkRapid
|
||||
ReverseVideo
|
||||
Concealed
|
||||
CrossedOut
|
||||
)
|
||||
|
||||
// Foreground colors
|
||||
const (
|
||||
FgBlack Color = iota + 30
|
||||
FgRed
|
||||
FgGreen
|
||||
FgYellow
|
||||
FgBlue
|
||||
FgMagenta
|
||||
FgCyan
|
||||
FgWhite
|
||||
)
|
||||
|
||||
// Foreground Hi-Intensity colors
|
||||
const (
|
||||
FgHiBlack Color = iota + 90
|
||||
FgHiRed
|
||||
FgHiGreen
|
||||
FgHiYellow
|
||||
FgHiBlue
|
||||
FgHiMagenta
|
||||
FgHiCyan
|
||||
FgHiWhite
|
||||
)
|
||||
|
||||
// Background colors
|
||||
const (
|
||||
BgBlack Color = iota + 40
|
||||
BgRed
|
||||
BgGreen
|
||||
BgYellow
|
||||
BgBlue
|
||||
BgMagenta
|
||||
BgCyan
|
||||
BgWhite
|
||||
)
|
||||
|
||||
// Background Hi-Intensity colors
|
||||
const (
|
||||
BgHiBlack Color = iota + 100
|
||||
BgHiRed
|
||||
BgHiGreen
|
||||
BgHiYellow
|
||||
BgHiBlue
|
||||
BgHiMagenta
|
||||
BgHiCyan
|
||||
BgHiWhite
|
||||
)
|
||||
|
||||
// EscapeSeq returns the ANSI escape sequence for the color.
|
||||
func (c Color) EscapeSeq() string {
|
||||
return EscapeStart + strconv.Itoa(int(c)) + EscapeStop
|
||||
}
|
||||
|
||||
// HTMLProperty returns the "class" attribute for the color.
|
||||
func (c Color) HTMLProperty() string {
|
||||
out := ""
|
||||
if class, ok := colorCSSClassMap[c]; ok {
|
||||
out = fmt.Sprintf("class=\"%s\"", class)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Sprint colorizes and prints the given string(s).
|
||||
func (c Color) Sprint(a ...interface{}) string {
|
||||
return colorize(fmt.Sprint(a...), c.EscapeSeq())
|
||||
}
|
||||
|
||||
// Sprintf formats and colorizes and prints the given string(s).
|
||||
func (c Color) Sprintf(format string, a ...interface{}) string {
|
||||
return colorize(fmt.Sprintf(format, a...), c.EscapeSeq())
|
||||
}
|
||||
|
||||
// Colors represents an array of Color objects to render with.
|
||||
// Example: Colors{FgCyan, BgBlack}
|
||||
type Colors []Color
|
||||
|
||||
var (
|
||||
// colorsSeqMap caches the escape sequence for a set of colors
|
||||
colorsSeqMap = sync.Map{}
|
||||
)
|
||||
|
||||
// EscapeSeq returns the ANSI escape sequence for the colors set.
|
||||
func (c Colors) EscapeSeq() string {
|
||||
if len(c) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
colorsKey := fmt.Sprintf("%#v", c)
|
||||
escapeSeq, ok := colorsSeqMap.Load(colorsKey)
|
||||
if !ok || escapeSeq == "" {
|
||||
colorNums := make([]string, len(c))
|
||||
for idx, color := range c {
|
||||
colorNums[idx] = strconv.Itoa(int(color))
|
||||
}
|
||||
escapeSeq = EscapeStart + strings.Join(colorNums, ";") + EscapeStop
|
||||
colorsSeqMap.Store(colorsKey, escapeSeq)
|
||||
}
|
||||
return escapeSeq.(string)
|
||||
}
|
||||
|
||||
// HTMLProperty returns the "class" attribute for the colors.
|
||||
func (c Colors) HTMLProperty() string {
|
||||
if len(c) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
var classes []string
|
||||
for _, color := range c {
|
||||
if class, ok := colorCSSClassMap[color]; ok {
|
||||
classes = append(classes, class)
|
||||
}
|
||||
}
|
||||
if len(classes) > 1 {
|
||||
sort.Strings(classes)
|
||||
}
|
||||
return fmt.Sprintf("class=\"%s\"", strings.Join(classes, " "))
|
||||
}
|
||||
|
||||
// Sprint colorizes and prints the given string(s).
|
||||
func (c Colors) Sprint(a ...interface{}) string {
|
||||
return colorize(fmt.Sprint(a...), c.EscapeSeq())
|
||||
}
|
||||
|
||||
// Sprintf formats and colorizes and prints the given string(s).
|
||||
func (c Colors) Sprintf(format string, a ...interface{}) string {
|
||||
return colorize(fmt.Sprintf(format, a...), c.EscapeSeq())
|
||||
}
|
||||
|
||||
func colorize(s string, escapeSeq string) string {
|
||||
if !colorsEnabled || escapeSeq == "" {
|
||||
return s
|
||||
}
|
||||
return Escape(s, escapeSeq)
|
||||
}
|
48
vendor/github.com/jedib0t/go-pretty/text/color_html.go
generated
vendored
Normal file
48
vendor/github.com/jedib0t/go-pretty/text/color_html.go
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
package text
|
||||
|
||||
var (
|
||||
// colorCSSClassMap contains the equivalent CSS-class for all colors
|
||||
colorCSSClassMap = map[Color]string{
|
||||
Bold: "bold",
|
||||
Faint: "faint",
|
||||
Italic: "italic",
|
||||
Underline: "underline",
|
||||
BlinkSlow: "blink-slow",
|
||||
BlinkRapid: "blink-rapid",
|
||||
ReverseVideo: "reverse-video",
|
||||
Concealed: "concealed",
|
||||
CrossedOut: "crossed-out",
|
||||
FgBlack: "fg-black",
|
||||
FgRed: "fg-red",
|
||||
FgGreen: "fg-green",
|
||||
FgYellow: "fg-yellow",
|
||||
FgBlue: "fg-blue",
|
||||
FgMagenta: "fg-magenta",
|
||||
FgCyan: "fg-cyan",
|
||||
FgWhite: "fg-white",
|
||||
FgHiBlack: "fg-hi-black",
|
||||
FgHiRed: "fg-hi-red",
|
||||
FgHiGreen: "fg-hi-green",
|
||||
FgHiYellow: "fg-hi-yellow",
|
||||
FgHiBlue: "fg-hi-blue",
|
||||
FgHiMagenta: "fg-hi-magenta",
|
||||
FgHiCyan: "fg-hi-cyan",
|
||||
FgHiWhite: "fg-hi-white",
|
||||
BgBlack: "bg-black",
|
||||
BgRed: "bg-red",
|
||||
BgGreen: "bg-green",
|
||||
BgYellow: "bg-yellow",
|
||||
BgBlue: "bg-blue",
|
||||
BgMagenta: "bg-magenta",
|
||||
BgCyan: "bg-cyan",
|
||||
BgWhite: "bg-white",
|
||||
BgHiBlack: "bg-hi-black",
|
||||
BgHiRed: "bg-hi-red",
|
||||
BgHiGreen: "bg-hi-green",
|
||||
BgHiYellow: "bg-hi-yellow",
|
||||
BgHiBlue: "bg-hi-blue",
|
||||
BgHiMagenta: "bg-hi-magenta",
|
||||
BgHiCyan: "bg-hi-cyan",
|
||||
BgHiWhite: "bg-hi-white",
|
||||
}
|
||||
)
|
39
vendor/github.com/jedib0t/go-pretty/text/cursor.go
generated
vendored
Normal file
39
vendor/github.com/jedib0t/go-pretty/text/cursor.go
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
package text
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Cursor helps move the cursor on the console in multiple directions.
|
||||
type Cursor rune
|
||||
|
||||
const (
|
||||
// CursorDown helps move the Cursor Down X lines
|
||||
CursorDown Cursor = 'B'
|
||||
|
||||
// CursorLeft helps move the Cursor Left X characters
|
||||
CursorLeft Cursor = 'D'
|
||||
|
||||
// CursorRight helps move the Cursor Right X characters
|
||||
CursorRight Cursor = 'C'
|
||||
|
||||
// CursorUp helps move the Cursor Up X lines
|
||||
CursorUp Cursor = 'A'
|
||||
|
||||
// EraseLine helps erase all characters to the Right of the Cursor in the
|
||||
// current line
|
||||
EraseLine Cursor = 'K'
|
||||
)
|
||||
|
||||
// Sprint prints the Escape Sequence to move the Cursor once.
|
||||
func (c Cursor) Sprint() string {
|
||||
return fmt.Sprintf("%s%c", EscapeStart, c)
|
||||
}
|
||||
|
||||
// Sprintn prints the Escape Sequence to move the Cursor "n" times.
|
||||
func (c Cursor) Sprintn(n int) string {
|
||||
if c == EraseLine {
|
||||
return c.Sprint()
|
||||
}
|
||||
return fmt.Sprintf("%s%d%c", EscapeStart, n, c)
|
||||
}
|
12
vendor/github.com/jedib0t/go-pretty/text/filter.go
generated
vendored
Normal file
12
vendor/github.com/jedib0t/go-pretty/text/filter.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
package text
|
||||
|
||||
// Filter filters the slice 's' to items which return truth when passed to 'f'.
|
||||
func Filter(s []string, f func(string) bool) []string {
|
||||
var out []string
|
||||
for _, item := range s {
|
||||
if f(item) {
|
||||
out = append(out, item)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
28
vendor/github.com/jedib0t/go-pretty/text/format.go
generated
vendored
Normal file
28
vendor/github.com/jedib0t/go-pretty/text/format.go
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
package text
|
||||
|
||||
import "strings"
|
||||
|
||||
// Format denotes the "case" to use for text.
|
||||
type Format int
|
||||
|
||||
// Format enumerations
|
||||
const (
|
||||
FormatDefault Format = iota // default_Case
|
||||
FormatLower // lower
|
||||
FormatTitle // Title
|
||||
FormatUpper // UPPER
|
||||
)
|
||||
|
||||
// Apply converts the text as directed.
|
||||
func (tc Format) Apply(text string) string {
|
||||
switch tc {
|
||||
case FormatLower:
|
||||
return strings.ToLower(text)
|
||||
case FormatTitle:
|
||||
return strings.Title(text)
|
||||
case FormatUpper:
|
||||
return strings.ToUpper(text)
|
||||
default:
|
||||
return text
|
||||
}
|
||||
}
|
203
vendor/github.com/jedib0t/go-pretty/text/string.go
generated
vendored
Normal file
203
vendor/github.com/jedib0t/go-pretty/text/string.go
generated
vendored
Normal file
@@ -0,0 +1,203 @@
|
||||
package text
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/mattn/go-runewidth"
|
||||
)
|
||||
|
||||
// Constants
|
||||
const (
|
||||
EscapeReset = EscapeStart + "0" + EscapeStop
|
||||
EscapeStart = "\x1b["
|
||||
EscapeStartRune = rune(27) // \x1b
|
||||
EscapeStop = "m"
|
||||
EscapeStopRune = 'm'
|
||||
)
|
||||
|
||||
// InsertEveryN inserts the rune every N characters in the string. For ex.:
|
||||
// InsertEveryN("Ghost", '-', 1) == "G-h-o-s-t"
|
||||
// InsertEveryN("Ghost", '-', 2) == "Gh-os-t"
|
||||
// InsertEveryN("Ghost", '-', 3) == "Gho-st"
|
||||
// InsertEveryN("Ghost", '-', 4) == "Ghos-t"
|
||||
// InsertEveryN("Ghost", '-', 5) == "Ghost"
|
||||
func InsertEveryN(str string, runeToInsert rune, n int) string {
|
||||
if n <= 0 {
|
||||
return str
|
||||
}
|
||||
|
||||
sLen := RuneCount(str)
|
||||
var out strings.Builder
|
||||
out.Grow(sLen + (sLen / n))
|
||||
outLen, isEscSeq := 0, false
|
||||
for idx, c := range str {
|
||||
if c == EscapeStartRune {
|
||||
isEscSeq = true
|
||||
}
|
||||
|
||||
if !isEscSeq && outLen > 0 && (outLen%n) == 0 && idx != sLen {
|
||||
out.WriteRune(runeToInsert)
|
||||
}
|
||||
out.WriteRune(c)
|
||||
if !isEscSeq {
|
||||
outLen += RuneWidth(c)
|
||||
}
|
||||
|
||||
if isEscSeq && c == EscapeStopRune {
|
||||
isEscSeq = false
|
||||
}
|
||||
}
|
||||
return out.String()
|
||||
}
|
||||
|
||||
// LongestLineLen returns the length of the longest "line" within the
|
||||
// argument string. For ex.:
|
||||
// LongestLineLen("Ghost!\nCome back here!\nRight now!") == 15
|
||||
func LongestLineLen(str string) int {
|
||||
maxLength, currLength, isEscSeq := 0, 0, false
|
||||
for _, c := range str {
|
||||
if c == EscapeStartRune {
|
||||
isEscSeq = true
|
||||
} else if isEscSeq && c == EscapeStopRune {
|
||||
isEscSeq = false
|
||||
continue
|
||||
}
|
||||
|
||||
if c == '\n' {
|
||||
if currLength > maxLength {
|
||||
maxLength = currLength
|
||||
}
|
||||
currLength = 0
|
||||
} else if !isEscSeq {
|
||||
currLength += RuneWidth(c)
|
||||
}
|
||||
}
|
||||
if currLength > maxLength {
|
||||
maxLength = currLength
|
||||
}
|
||||
return maxLength
|
||||
}
|
||||
|
||||
// Pad pads the given string with as many characters as needed to make it as
|
||||
// long as specified (maxLen). This function does not count escape sequences
|
||||
// while calculating length of the string. Ex.:
|
||||
// Pad("Ghost", 0, ' ') == "Ghost"
|
||||
// Pad("Ghost", 3, ' ') == "Ghost"
|
||||
// Pad("Ghost", 5, ' ') == "Ghost"
|
||||
// Pad("Ghost", 7, ' ') == "Ghost "
|
||||
// Pad("Ghost", 10, '.') == "Ghost....."
|
||||
func Pad(str string, maxLen int, paddingChar rune) string {
|
||||
strLen := RuneCount(str)
|
||||
if strLen < maxLen {
|
||||
str += strings.Repeat(string(paddingChar), maxLen-strLen)
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
// RepeatAndTrim repeats the given string until it is as long as maxRunes.
|
||||
// For ex.:
|
||||
// RepeatAndTrim("Ghost", 0) == ""
|
||||
// RepeatAndTrim("Ghost", 5) == "Ghost"
|
||||
// RepeatAndTrim("Ghost", 7) == "GhostGh"
|
||||
// RepeatAndTrim("Ghost", 10) == "GhostGhost"
|
||||
func RepeatAndTrim(str string, maxRunes int) string {
|
||||
if maxRunes == 0 {
|
||||
return ""
|
||||
} else if maxRunes == utf8.RuneCountInString(str) {
|
||||
return str
|
||||
}
|
||||
repeatedS := strings.Repeat(str, int(maxRunes/utf8.RuneCountInString(str))+1)
|
||||
return Trim(repeatedS, maxRunes)
|
||||
}
|
||||
|
||||
// RuneCount is similar to utf8.RuneCountInString, except for the fact that it
|
||||
// ignores escape sequences while counting. For ex.:
|
||||
// RuneCount("") == 0
|
||||
// RuneCount("Ghost") == 5
|
||||
// RuneCount("\x1b[33mGhost\x1b[0m") == 5
|
||||
// RuneCount("\x1b[33mGhost\x1b[0") == 5
|
||||
func RuneCount(str string) int {
|
||||
count, isEscSeq := 0, false
|
||||
for _, c := range str {
|
||||
if c == EscapeStartRune {
|
||||
isEscSeq = true
|
||||
} else if isEscSeq {
|
||||
if c == EscapeStopRune {
|
||||
isEscSeq = false
|
||||
}
|
||||
} else {
|
||||
count += RuneWidth(c)
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
// RuneWidth returns the mostly accurate character-width of the rune. This is
|
||||
// not 100% accurate as the character width is usually dependant on the
|
||||
// typeface (font) used in the console/terminal. For ex.:
|
||||
// RuneWidth('A') == 1
|
||||
// RuneWidth('ツ') == 2
|
||||
// RuneWidth('⊙') == 1
|
||||
// RuneWidth('︿') == 2
|
||||
// RuneWidth(0x27) == 0
|
||||
func RuneWidth(r rune) int {
|
||||
return runewidth.RuneWidth(r)
|
||||
}
|
||||
|
||||
// Snip returns the given string with a fixed length. For ex.:
|
||||
// Snip("Ghost", 0, "~") == "Ghost"
|
||||
// Snip("Ghost", 1, "~") == "~"
|
||||
// Snip("Ghost", 3, "~") == "Gh~"
|
||||
// Snip("Ghost", 5, "~") == "Ghost"
|
||||
// Snip("Ghost", 7, "~") == "Ghost "
|
||||
// Snip("\x1b[33mGhost\x1b[0m", 7, "~") == "\x1b[33mGhost\x1b[0m "
|
||||
func Snip(str string, length int, snipIndicator string) string {
|
||||
if length > 0 {
|
||||
lenStr := RuneCount(str)
|
||||
if lenStr > length {
|
||||
lenStrFinal := length - RuneCount(snipIndicator)
|
||||
return Trim(str, lenStrFinal) + snipIndicator
|
||||
}
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
// Trim trims a string to the given length while ignoring escape sequences. For
|
||||
// ex.:
|
||||
// Trim("Ghost", 3) == "Gho"
|
||||
// Trim("Ghost", 6) == "Ghost"
|
||||
// Trim("\x1b[33mGhost\x1b[0m", 3) == "\x1b[33mGho\x1b[0m"
|
||||
// Trim("\x1b[33mGhost\x1b[0m", 6) == "\x1b[33mGhost\x1b[0m"
|
||||
func Trim(str string, maxLen int) string {
|
||||
if maxLen <= 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
var out strings.Builder
|
||||
out.Grow(maxLen)
|
||||
|
||||
outLen, isEscSeq, lastEscSeq := 0, false, strings.Builder{}
|
||||
for _, sChr := range str {
|
||||
out.WriteRune(sChr)
|
||||
if sChr == EscapeStartRune {
|
||||
isEscSeq = true
|
||||
lastEscSeq.Reset()
|
||||
lastEscSeq.WriteRune(sChr)
|
||||
} else if isEscSeq {
|
||||
lastEscSeq.WriteRune(sChr)
|
||||
if sChr == EscapeStopRune {
|
||||
isEscSeq = false
|
||||
}
|
||||
} else {
|
||||
outLen++
|
||||
if outLen == maxLen {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if lastEscSeq.Len() > 0 && lastEscSeq.String() != EscapeReset {
|
||||
out.WriteString(EscapeReset)
|
||||
}
|
||||
return out.String()
|
||||
}
|
191
vendor/github.com/jedib0t/go-pretty/text/transformer.go
generated
vendored
Normal file
191
vendor/github.com/jedib0t/go-pretty/text/transformer.go
generated
vendored
Normal file
@@ -0,0 +1,191 @@
|
||||
package text
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-openapi/strfmt"
|
||||
)
|
||||
|
||||
// Transformer related constants
|
||||
const (
|
||||
unixTimeMinMilliseconds = int64(10000000000)
|
||||
unixTimeMinMicroseconds = unixTimeMinMilliseconds * 1000
|
||||
unixTimeMinNanoSeconds = unixTimeMinMicroseconds * 1000
|
||||
)
|
||||
|
||||
// Transformer related variables
|
||||
var (
|
||||
colorsNumberPositive = Colors{FgHiGreen}
|
||||
colorsNumberNegative = Colors{FgHiRed}
|
||||
colorsNumberZero = Colors{}
|
||||
colorsURL = Colors{Underline, FgBlue}
|
||||
)
|
||||
|
||||
// Transformer helps format the contents of an object to the user's liking.
|
||||
type Transformer func(val interface{}) string
|
||||
|
||||
// NewNumberTransformer returns a number Transformer that:
|
||||
// * transforms the number as directed by 'format' (ex.: %.2f)
|
||||
// * colors negative values Red
|
||||
// * colors positive values Green
|
||||
func NewNumberTransformer(format string) Transformer {
|
||||
return func(val interface{}) string {
|
||||
if number, ok := val.(int); ok {
|
||||
return transformInt(format, int64(number))
|
||||
}
|
||||
if number, ok := val.(int8); ok {
|
||||
return transformInt(format, int64(number))
|
||||
}
|
||||
if number, ok := val.(int16); ok {
|
||||
return transformInt(format, int64(number))
|
||||
}
|
||||
if number, ok := val.(int32); ok {
|
||||
return transformInt(format, int64(number))
|
||||
}
|
||||
if number, ok := val.(int64); ok {
|
||||
return transformInt(format, int64(number))
|
||||
}
|
||||
if number, ok := val.(uint); ok {
|
||||
return transformUint(format, uint64(number))
|
||||
}
|
||||
if number, ok := val.(uint8); ok {
|
||||
return transformUint(format, uint64(number))
|
||||
}
|
||||
if number, ok := val.(uint16); ok {
|
||||
return transformUint(format, uint64(number))
|
||||
}
|
||||
if number, ok := val.(uint32); ok {
|
||||
return transformUint(format, uint64(number))
|
||||
}
|
||||
if number, ok := val.(uint64); ok {
|
||||
return transformUint(format, uint64(number))
|
||||
}
|
||||
if number, ok := val.(float32); ok {
|
||||
return transformFloat(format, float64(number))
|
||||
}
|
||||
if number, ok := val.(float64); ok {
|
||||
return transformFloat(format, float64(number))
|
||||
}
|
||||
return fmt.Sprint(val)
|
||||
}
|
||||
}
|
||||
|
||||
func transformInt(format string, val int64) string {
|
||||
if val < 0 {
|
||||
return colorsNumberNegative.Sprintf("-"+format, -val)
|
||||
}
|
||||
if val > 0 {
|
||||
return colorsNumberPositive.Sprintf(format, val)
|
||||
}
|
||||
return colorsNumberZero.Sprintf(format, val)
|
||||
}
|
||||
|
||||
func transformUint(format string, val uint64) string {
|
||||
if val > 0 {
|
||||
return colorsNumberPositive.Sprintf(format, val)
|
||||
}
|
||||
return colorsNumberZero.Sprintf(format, val)
|
||||
}
|
||||
|
||||
func transformFloat(format string, val float64) string {
|
||||
if val < 0 {
|
||||
return colorsNumberNegative.Sprintf("-"+format, -val)
|
||||
}
|
||||
if val > 0 {
|
||||
return colorsNumberPositive.Sprintf(format, val)
|
||||
}
|
||||
return colorsNumberZero.Sprintf(format, val)
|
||||
}
|
||||
|
||||
// NewJSONTransformer returns a Transformer that can format a JSON string or an
|
||||
// object into pretty-indented JSON-strings.
|
||||
func NewJSONTransformer(prefix string, indent string) Transformer {
|
||||
return func(val interface{}) string {
|
||||
if valStr, ok := val.(string); ok {
|
||||
var b bytes.Buffer
|
||||
if err := json.Indent(&b, []byte(strings.TrimSpace(valStr)), prefix, indent); err == nil {
|
||||
return string(b.Bytes())
|
||||
}
|
||||
} else if b, err := json.MarshalIndent(val, prefix, indent); err == nil {
|
||||
return string(b)
|
||||
}
|
||||
return fmt.Sprintf("%#v", val)
|
||||
}
|
||||
}
|
||||
|
||||
// NewTimeTransformer returns a Transformer that can format a timestamp (a
|
||||
// time.Time or strfmt.DateTime object) into a well-defined time format defined
|
||||
// using the provided layout (ex.: time.RFC3339).
|
||||
//
|
||||
// If a non-nil location value is provided, the time will be localized to that
|
||||
// location (use time.Local to get localized timestamps).
|
||||
func NewTimeTransformer(layout string, location *time.Location) Transformer {
|
||||
return func(val interface{}) string {
|
||||
formatTime := func(t time.Time) string {
|
||||
rsp := ""
|
||||
if t.Unix() > 0 {
|
||||
if location != nil {
|
||||
t = t.In(location)
|
||||
}
|
||||
rsp = t.Format(layout)
|
||||
}
|
||||
return rsp
|
||||
}
|
||||
|
||||
rsp := fmt.Sprint(val)
|
||||
if valDate, ok := val.(strfmt.DateTime); ok {
|
||||
rsp = formatTime(time.Time(valDate))
|
||||
} else if valTime, ok := val.(time.Time); ok {
|
||||
rsp = formatTime(valTime)
|
||||
} else if valStr, ok := val.(string); ok {
|
||||
if valTime, err := time.Parse(time.RFC3339, valStr); err == nil {
|
||||
rsp = formatTime(valTime)
|
||||
}
|
||||
}
|
||||
return rsp
|
||||
}
|
||||
}
|
||||
|
||||
// NewUnixTimeTransformer returns a Transformer that can format a unix-timestamp
|
||||
// into a well-defined time format as defined by 'layout'. This can handle
|
||||
// unix-time in Seconds, MilliSeconds, Microseconds and Nanoseconds.
|
||||
//
|
||||
// If a non-nil location value is provided, the time will be localized to that
|
||||
// location (use time.Local to get localized timestamps).
|
||||
func NewUnixTimeTransformer(layout string, location *time.Location) Transformer {
|
||||
timeTransformer := NewTimeTransformer(layout, location)
|
||||
formatUnixTime := func(unixTime int64) string {
|
||||
if unixTime >= unixTimeMinNanoSeconds {
|
||||
unixTime = unixTime / time.Second.Nanoseconds()
|
||||
} else if unixTime >= unixTimeMinMicroseconds {
|
||||
unixTime = unixTime / (time.Second.Nanoseconds() / 1000)
|
||||
} else if unixTime >= unixTimeMinMilliseconds {
|
||||
unixTime = unixTime / (time.Second.Nanoseconds() / 1000000)
|
||||
}
|
||||
return timeTransformer(time.Unix(unixTime, 0))
|
||||
}
|
||||
|
||||
return func(val interface{}) string {
|
||||
if unixTime, ok := val.(int64); ok {
|
||||
return formatUnixTime(unixTime)
|
||||
} else if unixTimeStr, ok := val.(string); ok {
|
||||
if unixTime, err := strconv.ParseInt(unixTimeStr, 10, 64); err == nil {
|
||||
return formatUnixTime(unixTime)
|
||||
}
|
||||
}
|
||||
return fmt.Sprint(val)
|
||||
}
|
||||
}
|
||||
|
||||
// NewURLTransformer returns a Transformer that can format and pretty print a string
|
||||
// that contains an URL (the text is underlined and colored Blue).
|
||||
func NewURLTransformer() Transformer {
|
||||
return func(val interface{}) string {
|
||||
return colorsURL.Sprint(val)
|
||||
}
|
||||
}
|
67
vendor/github.com/jedib0t/go-pretty/text/valign.go
generated
vendored
Normal file
67
vendor/github.com/jedib0t/go-pretty/text/valign.go
generated
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
package text
|
||||
|
||||
import "strings"
|
||||
|
||||
// VAlign denotes how text is to be aligned vertically.
|
||||
type VAlign int
|
||||
|
||||
// VAlign enumerations
|
||||
const (
|
||||
VAlignDefault VAlign = iota // same as VAlignTop
|
||||
VAlignTop // "top\n\n"
|
||||
VAlignMiddle // "\nmiddle\n"
|
||||
VAlignBottom // "\n\nbottom"
|
||||
)
|
||||
|
||||
// Apply aligns the lines vertically. For ex.:
|
||||
// * VAlignTop.Apply({"Game", "Of", "Thrones"}, 5)
|
||||
// returns {"Game", "Of", "Thrones", "", ""}
|
||||
// * VAlignMiddle.Apply({"Game", "Of", "Thrones"}, 5)
|
||||
// returns {"", "Game", "Of", "Thrones", ""}
|
||||
// * VAlignBottom.Apply({"Game", "Of", "Thrones"}, 5)
|
||||
// returns {"", "", "Game", "Of", "Thrones"}
|
||||
func (va VAlign) Apply(lines []string, maxLines int) []string {
|
||||
if len(lines) == maxLines {
|
||||
return lines
|
||||
} else if len(lines) > maxLines {
|
||||
maxLines = len(lines)
|
||||
}
|
||||
|
||||
insertIdx := 0
|
||||
if va == VAlignMiddle {
|
||||
insertIdx = int(maxLines-len(lines)) / 2
|
||||
} else if va == VAlignBottom {
|
||||
insertIdx = maxLines - len(lines)
|
||||
}
|
||||
|
||||
linesOut := strings.Split(strings.Repeat("\n", maxLines-1), "\n")
|
||||
for idx, line := range lines {
|
||||
linesOut[idx+insertIdx] = line
|
||||
}
|
||||
return linesOut
|
||||
}
|
||||
|
||||
// ApplyStr aligns the string (of 1 or more lines) vertically. For ex.:
|
||||
// * VAlignTop.ApplyStr("Game\nOf\nThrones", 5)
|
||||
// returns {"Game", "Of", "Thrones", "", ""}
|
||||
// * VAlignMiddle.ApplyStr("Game\nOf\nThrones", 5)
|
||||
// returns {"", "Game", "Of", "Thrones", ""}
|
||||
// * VAlignBottom.ApplyStr("Game\nOf\nThrones", 5)
|
||||
// returns {"", "", "Game", "Of", "Thrones"}
|
||||
func (va VAlign) ApplyStr(text string, maxLines int) []string {
|
||||
return va.Apply(strings.Split(text, "\n"), maxLines)
|
||||
}
|
||||
|
||||
// HTMLProperty returns the equivalent HTML vertical-align tag property.
|
||||
func (va VAlign) HTMLProperty() string {
|
||||
switch va {
|
||||
case VAlignTop:
|
||||
return "valign=\"top\""
|
||||
case VAlignMiddle:
|
||||
return "valign=\"middle\""
|
||||
case VAlignBottom:
|
||||
return "valign=\"bottom\""
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
256
vendor/github.com/jedib0t/go-pretty/text/wrap.go
generated
vendored
Normal file
256
vendor/github.com/jedib0t/go-pretty/text/wrap.go
generated
vendored
Normal file
@@ -0,0 +1,256 @@
|
||||
package text
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// WrapHard wraps a string to the given length using a newline. Handles strings
|
||||
// with ANSI escape sequences (such as text color) without breaking the text
|
||||
// formatting. Breaks all words that go beyond the line boundary.
|
||||
//
|
||||
// For examples, refer to the unit-tests or GoDoc examples.
|
||||
func WrapHard(str string, wrapLen int) string {
|
||||
if wrapLen <= 0 {
|
||||
return ""
|
||||
}
|
||||
str = strings.Replace(str, "\t", " ", -1)
|
||||
sLen := utf8.RuneCountInString(str)
|
||||
if sLen <= wrapLen {
|
||||
return str
|
||||
}
|
||||
|
||||
out := &strings.Builder{}
|
||||
out.Grow(sLen + (sLen / wrapLen))
|
||||
for idx, paragraph := range strings.Split(str, "\n\n") {
|
||||
if idx > 0 {
|
||||
out.WriteString("\n\n")
|
||||
}
|
||||
wrapHard(paragraph, wrapLen, out)
|
||||
}
|
||||
|
||||
return out.String()
|
||||
}
|
||||
|
||||
// WrapSoft wraps a string to the given length using a newline. Handles strings
|
||||
// with ANSI escape sequences (such as text color) without breaking the text
|
||||
// formatting. Tries to move words that go beyond the line boundary to the next
|
||||
// line.
|
||||
//
|
||||
// For examples, refer to the unit-tests or GoDoc examples.
|
||||
func WrapSoft(str string, wrapLen int) string {
|
||||
if wrapLen <= 0 {
|
||||
return ""
|
||||
}
|
||||
str = strings.Replace(str, "\t", " ", -1)
|
||||
sLen := utf8.RuneCountInString(str)
|
||||
if sLen <= wrapLen {
|
||||
return str
|
||||
}
|
||||
|
||||
out := &strings.Builder{}
|
||||
out.Grow(sLen + (sLen / wrapLen))
|
||||
for idx, paragraph := range strings.Split(str, "\n\n") {
|
||||
if idx > 0 {
|
||||
out.WriteString("\n\n")
|
||||
}
|
||||
wrapSoft(paragraph, wrapLen, out)
|
||||
}
|
||||
|
||||
return out.String()
|
||||
}
|
||||
|
||||
// WrapText is very similar to WrapHard except for one minor difference. Unlike
|
||||
// WrapHard which discards line-breaks and respects only paragraph-breaks, this
|
||||
// function respects line-breaks too.
|
||||
//
|
||||
// For examples, refer to the unit-tests or GoDoc examples.
|
||||
func WrapText(str string, wrapLen int) string {
|
||||
if wrapLen <= 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
var out strings.Builder
|
||||
sLen := utf8.RuneCountInString(str)
|
||||
out.Grow(sLen + (sLen / wrapLen))
|
||||
lineIdx, isEscSeq, lastEscSeq := 0, false, ""
|
||||
for _, char := range str {
|
||||
if char == EscapeStartRune {
|
||||
isEscSeq = true
|
||||
lastEscSeq = ""
|
||||
}
|
||||
if isEscSeq {
|
||||
lastEscSeq += string(char)
|
||||
}
|
||||
|
||||
appendChar(char, wrapLen, &lineIdx, isEscSeq, lastEscSeq, &out)
|
||||
|
||||
if isEscSeq && char == EscapeStopRune {
|
||||
isEscSeq = false
|
||||
}
|
||||
if lastEscSeq == EscapeReset {
|
||||
lastEscSeq = ""
|
||||
}
|
||||
}
|
||||
if lastEscSeq != "" && lastEscSeq != EscapeReset {
|
||||
out.WriteString(EscapeReset)
|
||||
}
|
||||
return out.String()
|
||||
}
|
||||
|
||||
func appendChar(char rune, wrapLen int, lineLen *int, inEscSeq bool, lastSeenEscSeq string, out *strings.Builder) {
|
||||
// handle reaching the end of the line as dictated by wrapLen or by finding
|
||||
// a newline character
|
||||
if (*lineLen == wrapLen && !inEscSeq && char != '\n') || (char == '\n') {
|
||||
if lastSeenEscSeq != "" {
|
||||
// terminate escape sequence and the line; and restart the escape
|
||||
// sequence in the next line
|
||||
out.WriteString(EscapeReset)
|
||||
out.WriteRune('\n')
|
||||
out.WriteString(lastSeenEscSeq)
|
||||
} else {
|
||||
// just start a new line
|
||||
out.WriteRune('\n')
|
||||
}
|
||||
// reset line index to 0th character
|
||||
*lineLen = 0
|
||||
}
|
||||
|
||||
// if the rune is not a new line, output it
|
||||
if char != '\n' {
|
||||
out.WriteRune(char)
|
||||
|
||||
// increment the line index if not in the middle of an escape sequence
|
||||
if !inEscSeq {
|
||||
*lineLen++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func appendWord(word string, lineIdx *int, lastSeenEscSeq string, wrapLen int, out *strings.Builder) {
|
||||
inEscSeq := false
|
||||
for _, char := range word {
|
||||
if char == EscapeStartRune {
|
||||
inEscSeq = true
|
||||
lastSeenEscSeq = ""
|
||||
}
|
||||
if inEscSeq {
|
||||
lastSeenEscSeq += string(char)
|
||||
}
|
||||
|
||||
appendChar(char, wrapLen, lineIdx, inEscSeq, lastSeenEscSeq, out)
|
||||
|
||||
if inEscSeq && char == EscapeStopRune {
|
||||
inEscSeq = false
|
||||
}
|
||||
if lastSeenEscSeq == EscapeReset {
|
||||
lastSeenEscSeq = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func extractOpenEscapeSeq(str string) string {
|
||||
escapeSeq, inEscSeq := "", false
|
||||
for _, char := range str {
|
||||
if char == EscapeStartRune {
|
||||
inEscSeq = true
|
||||
escapeSeq = ""
|
||||
}
|
||||
if inEscSeq {
|
||||
escapeSeq += string(char)
|
||||
}
|
||||
if char == EscapeStopRune {
|
||||
inEscSeq = false
|
||||
}
|
||||
}
|
||||
if escapeSeq == EscapeReset {
|
||||
escapeSeq = ""
|
||||
}
|
||||
return escapeSeq
|
||||
}
|
||||
|
||||
func terminateLine(wrapLen int, lineLen *int, lastSeenEscSeq string, out *strings.Builder) {
|
||||
if *lineLen < wrapLen {
|
||||
out.WriteString(strings.Repeat(" ", wrapLen-*lineLen))
|
||||
}
|
||||
// something is already on the line; terminate it
|
||||
if lastSeenEscSeq != "" {
|
||||
out.WriteString(EscapeReset)
|
||||
}
|
||||
out.WriteRune('\n')
|
||||
out.WriteString(lastSeenEscSeq)
|
||||
*lineLen = 0
|
||||
}
|
||||
|
||||
func terminateOutput(lastSeenEscSeq string, out *strings.Builder) {
|
||||
if lastSeenEscSeq != "" && lastSeenEscSeq != EscapeReset && !strings.HasSuffix(out.String(), EscapeReset) {
|
||||
out.WriteString(EscapeReset)
|
||||
}
|
||||
}
|
||||
|
||||
func wrapHard(paragraph string, wrapLen int, out *strings.Builder) {
|
||||
lineLen, lastSeenEscSeq := 0, ""
|
||||
words := strings.Fields(paragraph)
|
||||
for wordIdx, word := range words {
|
||||
escSeq := extractOpenEscapeSeq(word)
|
||||
if escSeq != "" {
|
||||
lastSeenEscSeq = escSeq
|
||||
}
|
||||
if lineLen > 0 {
|
||||
out.WriteRune(' ')
|
||||
lineLen++
|
||||
}
|
||||
|
||||
wordLen := RuneCount(word)
|
||||
if lineLen+wordLen <= wrapLen { // word fits within the line
|
||||
out.WriteString(word)
|
||||
lineLen += wordLen
|
||||
} else { // word doesn't fit within the line; hard-wrap
|
||||
appendWord(word, &lineLen, lastSeenEscSeq, wrapLen, out)
|
||||
}
|
||||
|
||||
// end of line; but more words incoming
|
||||
if lineLen == wrapLen && wordIdx < len(words)-1 {
|
||||
terminateLine(wrapLen, &lineLen, lastSeenEscSeq, out)
|
||||
}
|
||||
}
|
||||
terminateOutput(lastSeenEscSeq, out)
|
||||
}
|
||||
|
||||
func wrapSoft(paragraph string, wrapLen int, out *strings.Builder) {
|
||||
lineLen, lastSeenEscSeq := 0, ""
|
||||
words := strings.Fields(paragraph)
|
||||
for wordIdx, word := range words {
|
||||
escSeq := extractOpenEscapeSeq(word)
|
||||
if escSeq != "" {
|
||||
lastSeenEscSeq = escSeq
|
||||
}
|
||||
spacing, spacingLen := "", 0
|
||||
if lineLen > 0 {
|
||||
spacing, spacingLen = " ", 1
|
||||
}
|
||||
|
||||
wordLen := RuneCount(word)
|
||||
if lineLen+spacingLen+wordLen <= wrapLen { // word fits within the line
|
||||
out.WriteString(spacing)
|
||||
out.WriteString(word)
|
||||
lineLen += spacingLen + wordLen
|
||||
} else { // word doesn't fit within the line
|
||||
if lineLen > 0 { // something is already on the line; terminate it
|
||||
terminateLine(wrapLen, &lineLen, lastSeenEscSeq, out)
|
||||
}
|
||||
if wordLen <= wrapLen { // word fits within a single line
|
||||
out.WriteString(word)
|
||||
lineLen = wordLen
|
||||
} else { // word doesn't fit within a single line; hard-wrap
|
||||
appendWord(word, &lineLen, lastSeenEscSeq, wrapLen, out)
|
||||
}
|
||||
}
|
||||
|
||||
// end of line; but more words incoming
|
||||
if lineLen == wrapLen && wordIdx < len(words)-1 {
|
||||
terminateLine(wrapLen, &lineLen, lastSeenEscSeq, out)
|
||||
}
|
||||
}
|
||||
terminateOutput(lastSeenEscSeq, out)
|
||||
}
|
Reference in New Issue
Block a user