mirror of
https://github.com/zrepl/zrepl.git
synced 2024-12-13 10:40:41 +01:00
a58ce74ed0
Primary goals: - Scrollable output ( fixes #245 ) - Sending job signals from status view - Filtering of output by filesystem Implementation: - original TUI framework: github.com/rivo/tview - but: tview is quasi-unmaintained, didn't support some features - => use fork https://gitlab.com/tslocum/cview - however, don't buy into either too much to avoid lock-in - instead: **port over the existing status UI drawing code and adjust it to produce strings instead of directly drawing into the termbox buffer** Co-authored-by: Calistoc <calistoc@protonmail.com> Co-authored-by: InsanePrawn <insane.prawny@gmail.com> fixes #245 fixes #220
120 lines
2.3 KiB
Go
120 lines
2.3 KiB
Go
package stringbuilder
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/go-playground/validator/v10"
|
|
)
|
|
|
|
type B struct {
|
|
// const
|
|
indentMultiplier int
|
|
|
|
// mut
|
|
sb *strings.Builder
|
|
indent int
|
|
width int
|
|
x, y int
|
|
}
|
|
|
|
type Config struct {
|
|
IndentMultiplier int `validate:"gte=1"`
|
|
Width int `validate:"gte=1"`
|
|
}
|
|
|
|
var validate = validator.New()
|
|
|
|
func New(config Config) *B {
|
|
|
|
if err := validate.Struct(config); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
return &B{sb: &strings.Builder{}, width: config.Width, indentMultiplier: config.IndentMultiplier}
|
|
}
|
|
|
|
func (b *B) String() string { return b.sb.String() }
|
|
|
|
func (w *B) Newline() {
|
|
w.Write("\n")
|
|
}
|
|
|
|
func (w *B) PrintfDrawIndentedAndWrappedIfMultiline(format string, args ...interface{}) {
|
|
whole := fmt.Sprintf(format, args...)
|
|
if strings.ContainsAny(whole, "\n\r") {
|
|
w.AddIndent(1)
|
|
defer w.AddIndent(-1)
|
|
}
|
|
w.Write(whole)
|
|
}
|
|
|
|
func (w *B) Printf(format string, args ...interface{}) {
|
|
whole := fmt.Sprintf(format, args...)
|
|
w.Write(whole)
|
|
}
|
|
|
|
func (t *B) AddIndent(delta int) {
|
|
t.indent += delta * t.indentMultiplier
|
|
}
|
|
|
|
func (t *B) AddIndentAndNewline(delta int) {
|
|
t.indent += delta * t.indentMultiplier
|
|
t.Write("\n")
|
|
}
|
|
|
|
func (w *B) Write(s string) {
|
|
for _, c := range s {
|
|
if c == '\n' {
|
|
fmt.Fprint(w.sb, "\n")
|
|
w.x = 0
|
|
fmt.Fprint(w.sb, Times(" ", w.indent-w.x))
|
|
w.x = w.indent
|
|
w.y++
|
|
continue
|
|
}
|
|
if w.x >= w.width {
|
|
fmt.Fprint(w.sb, "\n")
|
|
w.x = 0
|
|
fmt.Fprint(w.sb, Times(" ", w.indent-w.x))
|
|
w.x = w.indent
|
|
}
|
|
fmt.Fprintf(w.sb, "%c", c)
|
|
w.x++
|
|
}
|
|
}
|
|
|
|
func Times(str string, n int) (out string) {
|
|
for i := 0; i < n; i++ {
|
|
out += str
|
|
}
|
|
return
|
|
}
|
|
|
|
func RightPad(str string, length int, pad string) string {
|
|
if len(str) > length {
|
|
return str[:length]
|
|
}
|
|
return str + strings.Repeat(pad, length-len(str))
|
|
}
|
|
|
|
// changeCount = 0 indicates stall / no progress
|
|
func (w *B) DrawBar(length int, bytes, totalBytes int64, changeCount int) {
|
|
const arrowPositions = `>\|/`
|
|
var completedLength int
|
|
if totalBytes > 0 {
|
|
completedLength = int(int64(length) * bytes / totalBytes)
|
|
if completedLength > length {
|
|
completedLength = length
|
|
}
|
|
} else if totalBytes == bytes {
|
|
completedLength = length
|
|
}
|
|
|
|
w.Write("[")
|
|
w.Write(Times("=", completedLength))
|
|
w.Write(string(arrowPositions[changeCount%len(arrowPositions)]))
|
|
w.Write(Times("-", length-completedLength))
|
|
w.Write("]")
|
|
}
|