vendor github.com/containers/image/v5@v5.5.1

Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
This commit is contained in:
Valentin Rothberg
2020-06-17 16:20:57 +02:00
parent b70dfae2ae
commit dd7dd75334
70 changed files with 2872 additions and 564 deletions

View File

@@ -6,28 +6,15 @@ import (
"fmt"
"io"
"log"
"runtime/debug"
"strings"
"time"
"unicode/utf8"
"github.com/acarl005/stripansi"
"github.com/mattn/go-runewidth"
"github.com/vbauerster/mpb/v5/decor"
)
// BarFiller interface.
// Bar renders itself by calling BarFiller's Fill method. You can
// literally have any bar kind, by implementing this interface and
// passing it to the *Progress.Add(...) *Bar method.
type BarFiller interface {
Fill(w io.Writer, width int, stat *decor.Statistics)
}
// BarFillerFunc is function type adapter to convert function into Filler.
type BarFillerFunc func(w io.Writer, width int, stat *decor.Statistics)
func (f BarFillerFunc) Fill(w io.Writer, width int, stat *decor.Statistics) {
f(w, width, stat)
}
// Bar represents a progress Bar.
type Bar struct {
priority int // used by heap
@@ -55,21 +42,22 @@ type Bar struct {
recoveredPanic interface{}
}
type extFunc func(in io.Reader, tw int, st *decor.Statistics) (out io.Reader, lines int)
type extFunc func(in io.Reader, reqWidth int, st decor.Statistics) (out io.Reader, lines int)
type bState struct {
baseF BarFiller
filler BarFiller
id int
width int
priority int
reqWidth int
total int64
current int64
refill int64
lastN int64
iterated bool
trimSpace bool
toComplete bool
completeFlushed bool
ignoreComplete bool
dropOnComplete bool
noPop bool
aDecorators []decor.Decorator
pDecorators []decor.Decorator
@@ -77,12 +65,10 @@ type bState struct {
ewmaDecorators []decor.EwmaDecorator
shutdownListeners []decor.ShutdownListener
bufP, bufB, bufA *bytes.Buffer
filler BarFiller
middleware func(BarFiller) BarFiller
extender extFunc
// priority overrides *Bar's priority, if set
priority int
// dropOnComplete propagates to *Bar
dropOnComplete bool
// runningBar is a key for *pState.parkedBars
runningBar *Bar
@@ -146,13 +132,8 @@ func (b *Bar) Current() int64 {
// Given default bar style is "[=>-]<+", refill rune is '+'.
// To set bar style use mpb.BarStyle(string) BarOption.
func (b *Bar) SetRefill(amount int64) {
type refiller interface {
SetRefill(int64)
}
b.operateState <- func(s *bState) {
if f, ok := s.baseF.(refiller); ok {
f.SetRefill(amount)
}
s.refill = amount
}
}
@@ -318,44 +299,40 @@ func (b *Bar) serve(ctx context.Context, s *bState) {
}
func (b *Bar) render(tw int) {
if b.recoveredPanic != nil {
b.toShutdown = false
b.frameCh <- b.panicToFrame(tw)
return
}
select {
case b.operateState <- func(s *bState) {
stat := newStatistics(tw, s)
defer func() {
// recovering if user defined decorator panics for example
if p := recover(); p != nil {
b.dlogger.Println(p)
s.extender = makePanicExtender(p)
frame, lines := s.extender(nil, s.reqWidth, stat)
b.extendedLines = lines
b.toShutdown = !b.toShutdown
b.recoveredPanic = p
b.toShutdown = !s.completeFlushed
b.frameCh <- b.panicToFrame(tw)
b.frameCh <- frame
b.dlogger.Println(p)
}
s.completeFlushed = s.toComplete
}()
st := newStatistics(s)
frame := s.draw(tw, st)
frame, b.extendedLines = s.extender(frame, tw, st)
frame, lines := s.extender(s.draw(stat), s.reqWidth, stat)
b.extendedLines = lines
b.toShutdown = s.toComplete && !s.completeFlushed
s.completeFlushed = s.toComplete
b.frameCh <- frame
}:
case <-b.done:
s := b.cacheState
st := newStatistics(s)
frame := s.draw(tw, st)
frame, b.extendedLines = s.extender(frame, tw, st)
stat := newStatistics(tw, s)
var r io.Reader
if b.recoveredPanic == nil {
r = s.draw(stat)
}
frame, lines := s.extender(r, s.reqWidth, stat)
b.extendedLines = lines
b.frameCh <- frame
}
}
func (b *Bar) panicToFrame(termWidth int) io.Reader {
return strings.NewReader(fmt.Sprintf(fmt.Sprintf("%%.%dv\n", termWidth), b.recoveredPanic))
}
func (b *Bar) subscribeDecorators() {
var averageDecorators []decor.AverageDecorator
var ewmaDecorators []decor.EwmaDecorator
@@ -398,34 +375,41 @@ func (b *Bar) wSyncTable() [][]chan int {
}
}
func (s *bState) draw(termWidth int, stat *decor.Statistics) io.Reader {
func (s *bState) draw(stat decor.Statistics) io.Reader {
if !s.trimSpace {
stat.AvailableWidth -= 2
s.bufB.WriteByte(' ')
defer s.bufB.WriteByte(' ')
}
nlr := strings.NewReader("\n")
tw := stat.AvailableWidth
for _, d := range s.pDecorators {
s.bufP.WriteString(d.Decor(stat))
str := d.Decor(stat)
stat.AvailableWidth -= runewidth.StringWidth(stripansi.Strip(str))
s.bufP.WriteString(str)
}
if stat.AvailableWidth <= 0 {
trunc := strings.NewReader(runewidth.Truncate(stripansi.Strip(s.bufP.String()), tw, "…"))
s.bufP.Reset()
return io.MultiReader(trunc, s.bufB, nlr)
}
tw = stat.AvailableWidth
for _, d := range s.aDecorators {
s.bufA.WriteString(d.Decor(stat))
str := d.Decor(stat)
stat.AvailableWidth -= runewidth.StringWidth(stripansi.Strip(str))
s.bufA.WriteString(str)
}
if stat.AvailableWidth <= 0 {
trunc := strings.NewReader(runewidth.Truncate(stripansi.Strip(s.bufA.String()), tw, "…"))
s.bufA.Reset()
return io.MultiReader(s.bufP, s.bufB, trunc, nlr)
}
s.bufA.WriteByte('\n')
s.filler.Fill(s.bufB, s.reqWidth, stat)
prependCount := utf8.RuneCount(s.bufP.Bytes())
appendCount := utf8.RuneCount(s.bufA.Bytes()) - 1
if fitWidth := s.width; termWidth > 1 {
if !s.trimSpace {
// reserve space for edge spaces
termWidth -= 2
s.bufB.WriteByte(' ')
defer s.bufB.WriteByte(' ')
}
if prependCount+s.width+appendCount > termWidth {
fitWidth = termWidth - prependCount - appendCount
}
s.filler.Fill(s.bufB, fitWidth, stat)
}
return io.MultiReader(s.bufP, s.bufB, s.bufA)
return io.MultiReader(s.bufP, s.bufB, s.bufA, nlr)
}
func (s *bState) wSyncTable() [][]chan int {
@@ -450,12 +434,14 @@ func (s *bState) wSyncTable() [][]chan int {
return table
}
func newStatistics(s *bState) *decor.Statistics {
return &decor.Statistics{
ID: s.id,
Completed: s.completeFlushed,
Total: s.total,
Current: s.current,
func newStatistics(tw int, s *bState) decor.Statistics {
return decor.Statistics{
ID: s.id,
AvailableWidth: tw,
Total: s.total,
Current: s.current,
Refill: s.refill,
Completed: s.completeFlushed,
}
}
@@ -476,3 +462,17 @@ func ewmaIterationUpdate(done bool, s *bState, dur time.Duration) {
d.EwmaUpdate(s.lastN, dur)
}
}
func makePanicExtender(p interface{}) extFunc {
pstr := fmt.Sprint(p)
stack := debug.Stack()
stackLines := bytes.Count(stack, []byte("\n"))
return func(_ io.Reader, _ int, st decor.Statistics) (io.Reader, int) {
mr := io.MultiReader(
strings.NewReader(runewidth.Truncate(pstr, st.AvailableWidth, "…")),
strings.NewReader(fmt.Sprintf("\n%#v\n", st)),
bytes.NewReader(stack),
)
return mr, stackLines + 1
}
}

View File

@@ -2,137 +2,29 @@ package mpb
import (
"io"
"unicode/utf8"
"github.com/vbauerster/mpb/v5/decor"
"github.com/vbauerster/mpb/v5/internal"
)
const (
rLeft = iota
rFill
rTip
rEmpty
rRight
rRevTip
rRefill
)
// DefaultBarStyle is a string containing 7 runes.
// Each rune is a building block of a progress bar.
// BarFiller interface.
// Bar (without decorators) renders itself by calling BarFiller's Fill method.
//
// '1st rune' stands for left boundary rune
// `reqWidth` is requested width, which is set via:
// func WithWidth(width int) ContainerOption
// func BarWidth(width int) BarOption
//
// '2nd rune' stands for fill rune
// Default implementations can be obtained via:
//
// '3rd rune' stands for tip rune
// func NewBarFiller(style string, reverse bool) BarFiller
// func NewSpinnerFiller(style []string, alignment SpinnerAlignment) BarFiller
//
// '4th rune' stands for empty rune
//
// '5th rune' stands for right boundary rune
//
// '6th rune' stands for reverse tip rune
//
// '7th rune' stands for refill rune
//
const DefaultBarStyle string = "[=>-]<+"
type barFiller struct {
format [][]byte
tip []byte
refill int64
reverse bool
flush func(w io.Writer, bb [][]byte)
type BarFiller interface {
Fill(w io.Writer, reqWidth int, stat decor.Statistics)
}
// NewBarFiller constucts mpb.BarFiller, to be used with *Progress.Add(...) *Bar method.
func NewBarFiller(style string, reverse bool) BarFiller {
if style == "" {
style = DefaultBarStyle
}
bf := &barFiller{
format: make([][]byte, utf8.RuneCountInString(style)),
reverse: reverse,
}
bf.SetStyle(style)
return bf
}
func (s *barFiller) SetStyle(style string) {
if !utf8.ValidString(style) {
return
}
src := make([][]byte, 0, utf8.RuneCountInString(style))
for _, r := range style {
src = append(src, []byte(string(r)))
}
copy(s.format, src)
s.SetReverse(s.reverse)
}
func (s *barFiller) SetReverse(reverse bool) {
if reverse {
s.tip = s.format[rRevTip]
s.flush = reverseFlush
} else {
s.tip = s.format[rTip]
s.flush = regularFlush
}
s.reverse = reverse
}
func (s *barFiller) SetRefill(amount int64) {
s.refill = amount
}
func (s *barFiller) Fill(w io.Writer, width int, stat *decor.Statistics) {
// don't count rLeft and rRight as progress
width -= 2
if width < 2 {
return
}
w.Write(s.format[rLeft])
defer w.Write(s.format[rRight])
bb := make([][]byte, width)
cwidth := int(internal.PercentageRound(stat.Total, stat.Current, width))
for i := 0; i < cwidth; i++ {
bb[i] = s.format[rFill]
}
if s.refill > 0 {
var rwidth int
if s.refill > stat.Current {
rwidth = cwidth
} else {
rwidth = int(internal.PercentageRound(stat.Total, int64(s.refill), width))
}
for i := 0; i < rwidth; i++ {
bb[i] = s.format[rRefill]
}
}
if cwidth > 0 && cwidth < width {
bb[cwidth-1] = s.tip
}
for i := cwidth; i < width; i++ {
bb[i] = s.format[rEmpty]
}
s.flush(w, bb)
}
func regularFlush(w io.Writer, bb [][]byte) {
for i := 0; i < len(bb); i++ {
w.Write(bb[i])
}
}
func reverseFlush(w io.Writer, bb [][]byte) {
for i := len(bb) - 1; i >= 0; i-- {
w.Write(bb[i])
}
// BarFillerFunc is function type adapter to convert function into BarFiller.
type BarFillerFunc func(w io.Writer, reqWidth int, stat decor.Statistics)
func (f BarFillerFunc) Fill(w io.Writer, reqWidth int, stat decor.Statistics) {
f(w, reqWidth, stat)
}

173
vendor/github.com/vbauerster/mpb/v5/bar_filler_bar.go generated vendored Normal file
View File

@@ -0,0 +1,173 @@
package mpb
import (
"bytes"
"io"
"unicode/utf8"
"github.com/mattn/go-runewidth"
"github.com/vbauerster/mpb/v5/decor"
"github.com/vbauerster/mpb/v5/internal"
)
const (
rLeft = iota
rFill
rTip
rSpace
rRight
rRevTip
rRefill
)
// DefaultBarStyle is a string containing 7 runes.
// Each rune is a building block of a progress bar.
//
// '1st rune' stands for left boundary rune
//
// '2nd rune' stands for fill rune
//
// '3rd rune' stands for tip rune
//
// '4th rune' stands for space rune
//
// '5th rune' stands for right boundary rune
//
// '6th rune' stands for reverse tip rune
//
// '7th rune' stands for refill rune
//
const DefaultBarStyle string = "[=>-]<+"
type barFiller struct {
format [][]byte
rwidth []int
tip []byte
refill int64
reverse bool
flush func(io.Writer, *space, [][]byte)
}
type space struct {
space []byte
rwidth int
count int
}
// NewBarFiller constucts mpb.BarFiller, to be used with *Progress.Add(...) *Bar method.
func NewBarFiller(style string, reverse bool) BarFiller {
bf := &barFiller{
format: make([][]byte, len(DefaultBarStyle)),
rwidth: make([]int, len(DefaultBarStyle)),
reverse: reverse,
}
bf.SetStyle(style)
return bf
}
func (s *barFiller) SetStyle(style string) {
if !utf8.ValidString(style) {
panic("invalid bar style")
}
if style == "" {
style = DefaultBarStyle
}
src := make([][]byte, utf8.RuneCountInString(style))
i := 0
for _, r := range style {
s.rwidth[i] = runewidth.RuneWidth(r)
src[i] = []byte(string(r))
i++
}
copy(s.format, src)
s.SetReverse(s.reverse)
}
func (s *barFiller) SetReverse(reverse bool) {
if reverse {
s.tip = s.format[rRevTip]
s.flush = reverseFlush
} else {
s.tip = s.format[rTip]
s.flush = regularFlush
}
s.reverse = reverse
}
func (s *barFiller) Fill(w io.Writer, reqWidth int, stat decor.Statistics) {
width := internal.WidthForBarFiller(reqWidth, stat.AvailableWidth)
if brackets := s.rwidth[rLeft] + s.rwidth[rRight]; width < brackets {
return
} else {
// don't count brackets as progress
width -= brackets
}
w.Write(s.format[rLeft])
defer w.Write(s.format[rRight])
cwidth := int(internal.PercentageRound(stat.Total, stat.Current, width))
space := &space{
space: s.format[rSpace],
rwidth: s.rwidth[rSpace],
count: width - cwidth,
}
index, refill := 0, 0
bb := make([][]byte, cwidth)
if cwidth > 0 && cwidth != width {
bb[index] = s.tip
cwidth -= s.rwidth[rTip]
index++
}
if stat.Refill > 0 {
refill = int(internal.PercentageRound(stat.Total, int64(stat.Refill), width))
if refill > cwidth {
refill = cwidth
}
cwidth -= refill
}
for cwidth > 0 {
bb[index] = s.format[rFill]
cwidth -= s.rwidth[rFill]
index++
}
for refill > 0 {
bb[index] = s.format[rRefill]
refill -= s.rwidth[rRefill]
index++
}
if cwidth+refill < 0 || space.rwidth > 1 {
buf := new(bytes.Buffer)
s.flush(buf, space, bb[:index])
io.WriteString(w, runewidth.Truncate(buf.String(), width, "…"))
return
}
s.flush(w, space, bb)
}
func regularFlush(w io.Writer, space *space, bb [][]byte) {
for i := len(bb) - 1; i >= 0; i-- {
w.Write(bb[i])
}
for space.count > 0 {
w.Write(space.space)
space.count -= space.rwidth
}
}
func reverseFlush(w io.Writer, space *space, bb [][]byte) {
for space.count > 0 {
w.Write(space.space)
space.count -= space.rwidth
}
for i := 0; i < len(bb); i++ {
w.Write(bb[i])
}
}

View File

@@ -6,6 +6,7 @@ import (
"unicode/utf8"
"github.com/vbauerster/mpb/v5/decor"
"github.com/vbauerster/mpb/v5/internal"
)
// SpinnerAlignment enum.
@@ -39,7 +40,8 @@ func NewSpinnerFiller(style []string, alignment SpinnerAlignment) BarFiller {
return filler
}
func (s *spinnerFiller) Fill(w io.Writer, width int, stat *decor.Statistics) {
func (s *spinnerFiller) Fill(w io.Writer, reqWidth int, stat decor.Statistics) {
width := internal.WidthForBarFiller(reqWidth, stat.AvailableWidth)
frame := s.frames[s.count%uint(len(s.frames))]
frameWidth := utf8.RuneCountInString(frame)

View File

@@ -46,7 +46,7 @@ func BarID(id int) BarOption {
// BarWidth sets bar width independent of the container.
func BarWidth(width int) BarOption {
return func(s *bState) {
s.width = width
s.reqWidth = width
}
}
@@ -77,19 +77,22 @@ func BarFillerClearOnComplete() BarOption {
// BarFillerOnComplete replaces bar's filler with message, on complete event.
func BarFillerOnComplete(message string) BarOption {
return func(s *bState) {
s.filler = makeBarFillerOnComplete(s.baseF, message)
}
return BarFillerMiddleware(func(base BarFiller) BarFiller {
return BarFillerFunc(func(w io.Writer, reqWidth int, st decor.Statistics) {
if st.Completed {
io.WriteString(w, message)
} else {
base.Fill(w, reqWidth, st)
}
})
})
}
func makeBarFillerOnComplete(filler BarFiller, message string) BarFiller {
return BarFillerFunc(func(w io.Writer, width int, st *decor.Statistics) {
if st.Completed {
io.WriteString(w, message)
} else {
filler.Fill(w, width, st)
}
})
// BarFillerMiddleware provides a way to augment default BarFiller.
func BarFillerMiddleware(middle func(BarFiller) BarFiller) BarOption {
return func(s *bState) {
s.middleware = middle
}
}
// BarPriority sets bar's priority. Zero is highest priority, i.e. bar
@@ -103,21 +106,20 @@ func BarPriority(priority int) BarOption {
// BarExtender is an option to extend bar to the next new line, with
// arbitrary output.
func BarExtender(extender BarFiller) BarOption {
if extender == nil {
func BarExtender(filler BarFiller) BarOption {
if filler == nil {
return nil
}
return func(s *bState) {
s.extender = makeExtFunc(extender)
s.extender = makeExtFunc(filler)
}
}
func makeExtFunc(extender BarFiller) extFunc {
func makeExtFunc(filler BarFiller) extFunc {
buf := new(bytes.Buffer)
nl := []byte("\n")
return func(r io.Reader, tw int, st *decor.Statistics) (io.Reader, int) {
extender.Fill(buf, tw, st)
return io.MultiReader(r, buf), bytes.Count(buf.Bytes(), nl)
return func(r io.Reader, reqWidth int, st decor.Statistics) (io.Reader, int) {
filler.Fill(buf, reqWidth, st)
return io.MultiReader(r, buf), bytes.Count(buf.Bytes(), []byte("\n"))
}
}
@@ -139,7 +141,7 @@ func BarStyle(style string) BarOption {
SetStyle(string)
}
return func(s *bState) {
if t, ok := s.baseF.(styleSetter); ok {
if t, ok := s.filler.(styleSetter); ok {
t.SetStyle(style)
}
}
@@ -159,7 +161,7 @@ func BarReverse() BarOption {
SetReverse(bool)
}
return func(s *bState) {
if t, ok := s.baseF.(revSetter); ok {
if t, ok := s.filler.(revSetter); ok {
t.SetReverse(true)
}
}
@@ -189,7 +191,7 @@ func MakeFillerTypeSpecificBarOption(
cb func(interface{}),
) BarOption {
return func(s *bState) {
if t, ok := typeChecker(s.baseF); ok {
if t, ok := typeChecker(s.filler); ok {
cb(t)
}
}

View File

@@ -21,14 +21,11 @@ func WithWaitGroup(wg *sync.WaitGroup) ContainerOption {
}
}
// WithWidth sets container width. Default is 80. Bars inherit this
// width, as long as no BarWidth is applied.
func WithWidth(w int) ContainerOption {
// WithWidth sets container width. If not set underlying bars will
// occupy whole term width.
func WithWidth(width int) ContainerOption {
return func(s *pState) {
if w < 0 {
return
}
s.width = w
s.reqWidth = width
}
}

View File

@@ -7,7 +7,7 @@ import (
"io"
"os"
"golang.org/x/crypto/ssh/terminal"
"github.com/mattn/go-isatty"
)
// NotATTY not a TeleTYpewriter error.
@@ -30,13 +30,14 @@ func New(out io.Writer) *Writer {
w := &Writer{out: out}
if f, ok := out.(*os.File); ok {
w.fd = f.Fd()
w.isTerminal = terminal.IsTerminal(int(w.fd))
w.isTerminal = isatty.IsTerminal(w.fd)
}
return w
}
// Flush flushes the underlying buffer.
func (w *Writer) Flush(lineCount int) (err error) {
// some terminals interpret clear 0 lines as clear 1
if w.lineCount > 0 {
w.clearLines()
}
@@ -63,9 +64,9 @@ func (w *Writer) ReadFrom(r io.Reader) (n int64, err error) {
// GetWidth returns width of underlying terminal.
func (w *Writer) GetWidth() (int, error) {
if w.isTerminal {
tw, _, err := terminal.GetSize(int(w.fd))
return tw, err
if !w.isTerminal {
return -1, NotATTY
}
return -1, NotATTY
tw, _, err := GetSize(w.fd)
return tw, err
}

View File

@@ -2,8 +2,21 @@
package cwriter
import "fmt"
import (
"fmt"
"golang.org/x/sys/unix"
)
func (w *Writer) clearLines() {
fmt.Fprintf(w.out, cuuAndEd, w.lineCount)
}
// GetSize returns the dimensions of the given terminal.
func GetSize(fd uintptr) (width, height int, err error) {
ws, err := unix.IoctlGetWinsize(int(fd), unix.TIOCGWINSZ)
if err != nil {
return -1, -1, err
}
return int(ws.Col), int(ws.Row), nil
}

View File

@@ -14,7 +14,6 @@ var (
procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition")
procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW")
procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute")
)
type coord struct {
@@ -41,8 +40,9 @@ func (w *Writer) clearLines() {
if !w.isTerminal {
fmt.Fprintf(w.out, cuuAndEd, w.lineCount)
}
var info consoleScreenBufferInfo
procGetConsoleScreenBufferInfo.Call(w.fd, uintptr(unsafe.Pointer(&info)))
info := new(consoleScreenBufferInfo)
procGetConsoleScreenBufferInfo.Call(w.fd, uintptr(unsafe.Pointer(info)))
info.cursorPosition.y -= int16(w.lineCount)
if info.cursorPosition.y < 0 {
@@ -51,10 +51,19 @@ func (w *Writer) clearLines() {
procSetConsoleCursorPosition.Call(w.fd, uintptr(uint32(uint16(info.cursorPosition.y))<<16|uint32(uint16(info.cursorPosition.x))))
// clear the lines
cursor := coord{
cursor := &coord{
x: info.window.left,
y: info.cursorPosition.y,
}
count := uint32(info.size.x) * uint32(w.lineCount)
procFillConsoleOutputCharacter.Call(w.fd, uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(new(uint32))))
procFillConsoleOutputCharacter.Call(w.fd, uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(cursor)), uintptr(unsafe.Pointer(new(uint32))))
}
// GetSize returns the visible dimensions of the given terminal.
//
// These dimensions don't include any scrollback buffer height.
func GetSize(fd uintptr) (width, height int, err error) {
info := new(consoleScreenBufferInfo)
procGetConsoleScreenBufferInfo.Call(fd, uintptr(unsafe.Pointer(info)))
return int(info.window.right - info.window.left), int(info.window.bottom - info.window.top), nil
}

View File

@@ -1,21 +1,21 @@
package decor
// Any decorator displays text, that can be changed during decorator's
// lifetime via provided func call back.
// lifetime via provided DecorFunc.
//
// `f` call back which provides string to display
// `fn` DecorFunc callback
//
// `wcc` optional WC config
//
func Any(f func(*Statistics) string, wcc ...WC) Decorator {
return &any{initWC(wcc...), f}
func Any(fn DecorFunc, wcc ...WC) Decorator {
return &any{initWC(wcc...), fn}
}
type any struct {
WC
f func(*Statistics) string
fn DecorFunc
}
func (d *any) Decor(s *Statistics) string {
return d.FormatMsg(d.f(s))
func (d *any) Decor(s Statistics) string {
return d.FormatMsg(d.fn(s))
}

View File

@@ -46,21 +46,21 @@ func Counters(unit int, pairFmt string, wcc ...WC) Decorator {
return Any(chooseSizeProducer(unit, pairFmt), wcc...)
}
func chooseSizeProducer(unit int, format string) func(*Statistics) string {
func chooseSizeProducer(unit int, format string) DecorFunc {
if format == "" {
format = "%d / %d"
}
switch unit {
case UnitKiB:
return func(s *Statistics) string {
return func(s Statistics) string {
return fmt.Sprintf(format, SizeB1024(s.Current), SizeB1024(s.Total))
}
case UnitKB:
return func(s *Statistics) string {
return func(s Statistics) string {
return fmt.Sprintf(format, SizeB1000(s.Current), SizeB1000(s.Total))
}
default:
return func(s *Statistics) string {
return func(s Statistics) string {
return fmt.Sprintf(format, s.Current, s.Total)
}
}

View File

@@ -3,9 +3,9 @@ package decor
import (
"fmt"
"time"
"unicode/utf8"
"github.com/acarl005/stripansi"
"github.com/mattn/go-runewidth"
)
const (
@@ -47,22 +47,32 @@ const (
// Statistics consists of progress related statistics, that Decorator
// may need.
type Statistics struct {
ID int
Completed bool
Total int64
Current int64
ID int
AvailableWidth int
Total int64
Current int64
Refill int64
Completed bool
}
// Decorator interface.
// Implementors should embed WC type, that way only single method
// Decor(*Statistics) needs to be implemented, the rest will be handled
// by WC type.
// Most of the time there is no need to implement this interface
// manually, as decor package already provides a wide range of decorators
// which implement this interface. If however built-in decorators don't
// meet your needs, you're free to implement your own one by implementing
// this particular interface. The easy way to go is to convert a
// `DecorFunc` into a `Decorator` interface by using provided
// `func Any(DecorFunc, ...WC) Decorator`.
type Decorator interface {
Configurator
Synchronizer
Decor(*Statistics) string
Decor(Statistics) string
}
// DecorFunc func type.
// To be used with `func Any`(DecorFunc, ...WC) Decorator`.
type DecorFunc func(Statistics) string
// Synchronizer interface.
// All decorators implement this interface implicitly. Its Sync
// method exposes width sync channel, if DSyncWidth bit is set.
@@ -117,38 +127,35 @@ var (
// W represents width and C represents bit set of width related config.
// A decorator should embed WC, to enable width synchronization.
type WC struct {
W int
C int
dynFormat string
wsync chan int
W int
C int
fill func(s string, w int) string
wsync chan int
}
// FormatMsg formats final message according to WC.W and WC.C.
// Should be called by any Decorator implementation.
func (wc *WC) FormatMsg(msg string) string {
var format string
runeCount := utf8.RuneCountInString(stripansi.Strip(msg))
ansiCount := utf8.RuneCountInString(msg) - runeCount
pureWidth := runewidth.StringWidth(msg)
stripWidth := runewidth.StringWidth(stripansi.Strip(msg))
maxCell := wc.W
if (wc.C & DSyncWidth) != 0 {
cellCount := stripWidth
if (wc.C & DextraSpace) != 0 {
runeCount++
cellCount++
}
wc.wsync <- runeCount
max := <-wc.wsync
format = fmt.Sprintf(wc.dynFormat, ansiCount+max)
} else {
format = fmt.Sprintf(wc.dynFormat, ansiCount+wc.W)
wc.wsync <- cellCount
maxCell = <-wc.wsync
}
return fmt.Sprintf(format, msg)
return wc.fill(msg, maxCell+(pureWidth-stripWidth))
}
// Init initializes width related config.
func (wc *WC) Init() WC {
wc.dynFormat = "%%"
wc.fill = runewidth.FillLeft
if (wc.C & DidentRight) != 0 {
wc.dynFormat += "-"
wc.fill = runewidth.FillRight
}
wc.dynFormat += "%ds"
if (wc.C & DSyncWidth) != 0 {
// it's deliberate choice to override wsync on each Init() call,
// this way globals like WCSyncSpace can be reused

View File

@@ -25,11 +25,11 @@ func Elapsed(style TimeStyle, wcc ...WC) Decorator {
func NewElapsed(style TimeStyle, startTime time.Time, wcc ...WC) Decorator {
var msg string
producer := chooseTimeProducer(style)
f := func(s *Statistics) string {
fn := func(s Statistics) string {
if !s.Completed {
msg = producer(time.Since(startTime))
}
return msg
}
return Any(f, wcc...)
return Any(fn, wcc...)
}

View File

@@ -63,7 +63,7 @@ type movingAverageETA struct {
producer func(time.Duration) string
}
func (d *movingAverageETA) Decor(s *Statistics) string {
func (d *movingAverageETA) Decor(s Statistics) string {
v := math.Round(d.average.Value())
remaining := time.Duration((s.Total - s.Current) * int64(v))
if d.normalizer != nil {
@@ -117,7 +117,7 @@ type averageETA struct {
producer func(time.Duration) string
}
func (d *averageETA) Decor(s *Statistics) string {
func (d *averageETA) Decor(s Statistics) string {
var remaining time.Duration
if s.Current != 0 {
durPerItem := float64(time.Since(d.startTime)) / float64(s.Current)

View File

@@ -1,9 +1,10 @@
package decor
import (
"fmt"
"strings"
"unicode/utf8"
"github.com/acarl005/stripansi"
"github.com/mattn/go-runewidth"
)
// Merge wraps its decorator argument with intention to sync width
@@ -64,18 +65,18 @@ func (d *mergeDecorator) Base() Decorator {
return d.Decorator
}
func (d *mergeDecorator) Decor(s *Statistics) string {
func (d *mergeDecorator) Decor(s Statistics) string {
msg := d.Decorator.Decor(s)
msgLen := utf8.RuneCountInString(msg)
pureWidth := runewidth.StringWidth(msg)
stripWidth := runewidth.StringWidth(stripansi.Strip(msg))
cellCount := stripWidth
if (d.wc.C & DextraSpace) != 0 {
msgLen++
cellCount++
}
var total int
max := utf8.RuneCountInString(d.placeHolders[0].FormatMsg(""))
total += max
pw := (msgLen - max) / len(d.placeHolders)
rem := (msgLen - max) % len(d.placeHolders)
total := runewidth.StringWidth(d.placeHolders[0].FormatMsg(""))
pw := (cellCount - total) / len(d.placeHolders)
rem := (cellCount - total) % len(d.placeHolders)
var diff int
for i := 1; i < len(d.placeHolders); i++ {
@@ -87,20 +88,20 @@ func (d *mergeDecorator) Decor(s *Statistics) string {
width = 0
}
}
max = utf8.RuneCountInString(ph.FormatMsg(strings.Repeat(" ", width)))
max := runewidth.StringWidth(ph.FormatMsg(strings.Repeat(" ", width)))
total += max
diff = max - pw
}
d.wc.wsync <- pw + rem
max = <-d.wc.wsync
return fmt.Sprintf(fmt.Sprintf(d.wc.dynFormat, max+total), msg)
max := <-d.wc.wsync
return d.wc.fill(msg, max+total+(pureWidth-stripWidth))
}
type placeHolderDecorator struct {
WC
}
func (d *placeHolderDecorator) Decor(*Statistics) string {
func (d *placeHolderDecorator) Decor(Statistics) string {
return ""
}

View File

@@ -8,5 +8,5 @@ package decor
// `wcc` optional WC config
//
func Name(str string, wcc ...WC) Decorator {
return Any(func(*Statistics) string { return str }, wcc...)
return Any(func(Statistics) string { return str }, wcc...)
}

View File

@@ -24,7 +24,7 @@ type onCompleteWrapper struct {
msg string
}
func (d *onCompleteWrapper) Decor(s *Statistics) string {
func (d *onCompleteWrapper) Decor(s Statistics) string {
if s.Completed {
wc := d.GetConf()
return wc.FormatMsg(d.msg)

View File

@@ -50,7 +50,7 @@ func NewPercentage(format string, wcc ...WC) Decorator {
if format == "" {
format = "% d"
}
f := func(s *Statistics) string {
f := func(s Statistics) string {
p := internal.Percentage(s.Total, s.Current, 100)
return fmt.Sprintf(format, percentageType(p))
}

View File

@@ -78,7 +78,7 @@ type movingAverageSpeed struct {
msg string
}
func (d *movingAverageSpeed) Decor(s *Statistics) string {
func (d *movingAverageSpeed) Decor(s Statistics) string {
if !s.Completed {
var speed float64
if v := d.average.Value(); v > 0 {
@@ -140,7 +140,7 @@ type averageSpeed struct {
msg string
}
func (d *averageSpeed) Decor(s *Statistics) string {
func (d *averageSpeed) Decor(s Statistics) string {
if !s.Completed {
speed := float64(s.Current) / float64(time.Since(d.startTime))
d.msg = d.producer(speed * 1e9)

View File

@@ -12,7 +12,7 @@ func Spinner(frames []string, wcc ...WC) Decorator {
frames = defaultSpinnerStyle
}
var count uint
f := func(s *Statistics) string {
f := func(s Statistics) string {
frame := frames[count%uint(len(frames))]
count++
return frame

View File

@@ -3,8 +3,9 @@ module github.com/vbauerster/mpb/v5
require (
github.com/VividCortex/ewma v1.1.1
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f // indirect
github.com/mattn/go-isatty v0.0.12
github.com/mattn/go-runewidth v0.0.9
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299
)
go 1.14

View File

@@ -2,12 +2,10 @@ github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdc
github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5 h1:Q7tZBpemrlsc2I7IyODzhtallWRSm4Q0d09pL6XbQtU=
golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f h1:gWF768j/LaZugp8dyS4UwsslYCYz9XgFxvlgsn0n9H8=
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

View File

@@ -0,0 +1,8 @@
package internal
func WidthForBarFiller(reqWidth, available int) int {
if reqWidth <= 0 || reqWidth >= available {
return available
}
return reqWidth
}

View File

@@ -19,8 +19,6 @@ import (
const (
// default RefreshRate
prr = 120 * time.Millisecond
// default width
pwidth = 80
)
// Progress represents the container that renders Progress bars
@@ -46,7 +44,7 @@ type pState struct {
// following are provided/overrided by user
idCount int
width int
reqWidth int
popCompleted bool
rr time.Duration
uwg *sync.WaitGroup
@@ -70,7 +68,6 @@ func New(options ...ContainerOption) *Progress {
func NewWithContext(ctx context.Context, options ...ContainerOption) *Progress {
s := &pState{
bHeap: priorityQueue{},
width: pwidth,
rr: prr,
parkedBars: make(map[*Bar]*Bar),
output: os.Stdout,
@@ -113,7 +110,7 @@ func (p *Progress) AddSpinner(total int64, alignment SpinnerAlignment, options .
// Panics if *Progress instance is done, i.e. called after *Progress.Wait().
func (p *Progress) Add(total int64, filler BarFiller, options ...BarOption) *Bar {
if filler == nil {
filler = NewBarFiller(DefaultBarStyle, false)
filler = BarFillerFunc(func(io.Writer, int, decor.Statistics) {})
}
p.bwg.Add(1)
result := make(chan *Bar)
@@ -215,14 +212,46 @@ func (p *Progress) serve(s *pState, cw *cwriter.Writer) {
op(s)
case <-p.refreshCh:
if err := s.render(cw); err != nil {
go p.dlogger.Println(err)
p.dlogger.Println(err)
}
case <-s.shutdownNotifier:
if s.heapUpdated {
if err := s.render(cw); err != nil {
p.dlogger.Println(err)
}
}
return
}
}
}
func (s *pState) newTicker(done <-chan struct{}) chan time.Time {
ch := make(chan time.Time)
if s.shutdownNotifier == nil {
s.shutdownNotifier = make(chan struct{})
}
go func() {
if s.renderDelay != nil {
<-s.renderDelay
}
if s.refreshSrc == nil {
ticker := time.NewTicker(s.rr)
defer ticker.Stop()
s.refreshSrc = ticker.C
}
for {
select {
case tick := <-s.refreshSrc:
ch <- tick
case <-done:
close(s.shutdownNotifier)
return
}
}
}()
return ch
}
func (s *pState) render(cw *cwriter.Writer) error {
if s.heapUpdated {
s.updateSyncMatrix()
@@ -233,7 +262,7 @@ func (s *pState) render(cw *cwriter.Writer) error {
tw, err := cw.GetWidth()
if err != nil {
tw = s.width
tw = s.reqWidth
}
for i := 0; i < s.bHeap.Len(); i++ {
bar := s.bHeap[i]
@@ -250,11 +279,16 @@ func (s *pState) flush(cw *cwriter.Writer) error {
b := heap.Pop(&s.bHeap).(*Bar)
cw.ReadFrom(<-b.frameCh)
if b.toShutdown {
// shutdown at next flush
// this ensures no bar ends up with less than 100% rendered
defer func() {
if b.recoveredPanic != nil {
s.barShutdownQueue = append(s.barShutdownQueue, b)
}()
b.toShutdown = false
} else {
// shutdown at next flush
// this ensures no bar ends up with less than 100% rendered
defer func() {
s.barShutdownQueue = append(s.barShutdownQueue, b)
}()
}
}
lineCount += b.extendedLines + 1
bm[b] = struct{}{}
@@ -295,33 +329,6 @@ func (s *pState) flush(cw *cwriter.Writer) error {
return cw.Flush(lineCount)
}
func (s *pState) newTicker(done <-chan struct{}) chan time.Time {
ch := make(chan time.Time)
if s.shutdownNotifier == nil {
s.shutdownNotifier = make(chan struct{})
}
go func() {
if s.renderDelay != nil {
<-s.renderDelay
}
if s.refreshSrc == nil {
ticker := time.NewTicker(s.rr)
defer ticker.Stop()
s.refreshSrc = ticker.C
}
for {
select {
case tick := <-s.refreshSrc:
ch <- tick
case <-done:
close(s.shutdownNotifier)
return
}
}
}()
return ch
}
func (s *pState) updateSyncMatrix() {
s.pMatrix = make(map[int][]chan int)
s.aMatrix = make(map[int][]chan int)
@@ -342,16 +349,13 @@ func (s *pState) updateSyncMatrix() {
func (s *pState) makeBarState(total int64, filler BarFiller, options ...BarOption) *bState {
bs := &bState{
total: total,
baseF: extractBaseFiller(filler),
filler: filler,
priority: s.idCount,
id: s.idCount,
width: s.width,
priority: s.idCount,
reqWidth: s.reqWidth,
total: total,
filler: filler,
extender: func(r io.Reader, _ int, _ decor.Statistics) (io.Reader, int) { return r, 0 },
debugOut: s.debugOut,
extender: func(r io.Reader, _ int, _ *decor.Statistics) (io.Reader, int) {
return r, 0
},
}
for _, opt := range options {
@@ -360,13 +364,18 @@ func (s *pState) makeBarState(total int64, filler BarFiller, options ...BarOptio
}
}
if bs.middleware != nil {
bs.filler = bs.middleware(filler)
bs.middleware = nil
}
if s.popCompleted && !bs.noPop {
bs.priority = -1
}
bs.bufP = bytes.NewBuffer(make([]byte, 0, bs.width))
bs.bufB = bytes.NewBuffer(make([]byte, 0, bs.width))
bs.bufA = bytes.NewBuffer(make([]byte, 0, bs.width))
bs.bufP = bytes.NewBuffer(make([]byte, 0, 128))
bs.bufB = bytes.NewBuffer(make([]byte, 0, 256))
bs.bufA = bytes.NewBuffer(make([]byte, 0, 128))
return bs
}
@@ -387,13 +396,3 @@ func syncWidth(matrix map[int][]chan int) {
}()
}
}
func extractBaseFiller(f BarFiller) BarFiller {
type wrapper interface {
Base() BarFiller
}
if f, ok := f.(wrapper); ok {
return extractBaseFiller(f.Base())
}
return f
}