diff --git a/bar.go b/bar.go index 86e7569..0f993cf 100644 --- a/bar.go +++ b/bar.go @@ -250,11 +250,6 @@ ch := make(chan state, 1) b.stateReqCh <- ch return <-ch -} - -func (b *Bar) bytes(termWidth int) []byte { - s := b.getState() - return draw(&s, termWidth) } func (b *Bar) server(ctx context.Context, wg *sync.WaitGroup, id int, total int64, width int, format string) { @@ -357,7 +352,12 @@ } } -func draw(s *state, termWidth int) []byte { +func (b *Bar) bytes(termWidth int, ws *widthSync) []byte { + s := b.getState() + return draw(&s, termWidth, ws) +} + +func draw(s *state, termWidth int, ws *widthSync) []byte { if termWidth <= 0 { termWidth = s.width } @@ -367,13 +367,13 @@ // render append functions to the right of the bar var appendBlock []byte for _, f := range s.appendFuncs { - appendBlock = append(appendBlock, []byte(f(stat))...) + appendBlock = append(appendBlock, []byte(f(stat, nil, nil))...) } // render prepend functions to the left of the bar var prependBlock []byte - for _, f := range s.prependFuncs { - prependBlock = append(prependBlock, []byte(f(stat))...) + for i, f := range s.prependFuncs { + prependBlock = append(prependBlock, []byte(f(stat, ws.listen[i], ws.result[i]))...) } prependCount := utf8.RuneCount(prependBlock) diff --git a/decorators.go b/decorators.go index 3d54a51..6c3fbaf 100644 --- a/decorators.go +++ b/decorators.go @@ -16,7 +16,7 @@ ) // DecoratorFunc is a function that can be prepended and appended to the progress bar -type DecoratorFunc func(s *Statistics) string +type DecoratorFunc func(s *Statistics, myWidth chan<- int, maxWidth <-chan int) string type decorator struct { kind decoratorFuncType @@ -25,7 +25,7 @@ func (b *Bar) PrependName(name string, padding int) *Bar { layout := "%" + strconv.Itoa(padding) + "s" - b.PrependFunc(func(s *Statistics) string { + b.PrependFunc(func(s *Statistics, myWidth chan<- int, maxWidth <-chan int) string { return fmt.Sprintf(layout, name) }) return b @@ -33,7 +33,7 @@ func (b *Bar) PrependCounters(unit Units, padding int) *Bar { layout := "%" + strconv.Itoa(padding) + "s" - b.PrependFunc(func(s *Statistics) string { + b.PrependFunc(func(s *Statistics, myWidth chan<- int, maxWidth <-chan int) string { current := Format(s.Current).To(unit) total := Format(s.Total).To(unit) str := fmt.Sprintf("%s / %s", current, total) @@ -44,7 +44,7 @@ func (b *Bar) PrependETA(padding int) *Bar { layout := "ETA%" + strconv.Itoa(padding) + "s" - b.PrependFunc(func(s *Statistics) string { + b.PrependFunc(func(s *Statistics, myWidth chan<- int, maxWidth <-chan int) string { return fmt.Sprintf(layout, time.Duration(s.Eta().Seconds())*time.Second) }) return b @@ -52,7 +52,7 @@ func (b *Bar) AppendETA(padding int) *Bar { layout := "ETA %" + strconv.Itoa(padding) + "s" - b.AppendFunc(func(s *Statistics) string { + b.AppendFunc(func(s *Statistics, myWidth chan<- int, maxWidth <-chan int) string { return fmt.Sprintf(layout, time.Duration(s.Eta().Seconds())*time.Second) }) return b @@ -60,21 +60,21 @@ func (b *Bar) PrependElapsed(padding int) *Bar { layout := "%" + strconv.Itoa(padding) + "s" - b.PrependFunc(func(s *Statistics) string { + b.PrependFunc(func(s *Statistics, myWidth chan<- int, maxWidth <-chan int) string { return fmt.Sprintf(layout, time.Duration(s.TimeElapsed.Seconds())*time.Second) }) return b } func (b *Bar) AppendElapsed() *Bar { - b.AppendFunc(func(s *Statistics) string { + b.AppendFunc(func(s *Statistics, myWidth chan<- int, maxWidth <-chan int) string { return fmt.Sprint(time.Duration(s.TimeElapsed.Seconds()) * time.Second) }) return b } func (b *Bar) AppendPercentage() *Bar { - b.AppendFunc(func(s *Statistics) string { + b.AppendFunc(func(s *Statistics, myWidth chan<- int, maxWidth <-chan int) string { completed := percentage(s.Total, s.Current, 100) return fmt.Sprintf("%3d %%", completed) }) @@ -83,7 +83,7 @@ func (b *Bar) PrependPercentage(padding int) *Bar { layout := "%" + strconv.Itoa(padding) + "d %%" - b.PrependFunc(func(s *Statistics) string { + b.PrependFunc(func(s *Statistics, myWidth chan<- int, maxWidth <-chan int) string { completed := percentage(s.Total, s.Current, 100) return fmt.Sprintf(layout, completed) }) diff --git a/progress.go b/progress.go index 1d7179d..28da56a 100644 --- a/progress.go +++ b/progress.go @@ -177,7 +177,7 @@ if isClosed(p.done) { panic(ErrCallAfterStop) } - respCh := make(chan int) + respCh := make(chan int, 1) p.barCountReqCh <- respCh return <-respCh } @@ -199,6 +199,11 @@ return } close(p.operationCh) +} + +type widthSync struct { + listen []chan int + result []chan int } // server monitors underlying channels and renders any progress bars @@ -219,6 +224,8 @@ } wg.Done() } + var numPrependers int + // var numAppenders int for { select { case w := <-p.outChangeReqCh: @@ -244,6 +251,10 @@ } op.result <- ok } + if len(bars) > 0 { + numPrependers = len(bars[0].GetPrependers()) + // numAppenders = len(bars[0].GetAppenders()) + } case respCh := <-p.barCountReqCh: respCh <- len(bars) case beforeRender = <-p.brCh: @@ -256,6 +267,38 @@ if beforeRender != nil { beforeRender(bars) + } + + prepWidthSync := &widthSync{ + listen: make([]chan int, numPrependers), + result: make([]chan int, numPrependers), + } + for i := 0; i < numPrependers; i++ { + prepWidthSync.listen[i] = make(chan int, numBars) + prepWidthSync.result[i] = make(chan int, numBars) + } + stopWidthListen := make(chan struct{}) + for i, listenCh := range prepWidthSync.listen { + go func(listenCh <-chan int, resultCh chan<- int) { + widths := make([]int, 0, numBars) + loop: + for { + select { + case w := <-listenCh: + widths = append(widths, w) + if len(widths) == numBars { + break loop + } + case <-stopWidthListen: + return + } + } + result := max(widths) + for i := 0; i < numBars; i++ { + resultCh <- result + } + // close(resultCh) + }(listenCh, prepWidthSync.result[i]) } width, _, _ := cwriter.GetTermSize() @@ -265,17 +308,24 @@ for i := 0; i < numBars; i++ { go func() { defer recoverIfPanic() - drawer(ibars, ibbCh) + drawer(ibars, ibbCh, prepWidthSync) }() } go func() { wg.Wait() close(ibbCh) + close(stopWidthListen) + for _, ch := range prepWidthSync.result { + close(ch) + } + for _, ch := range prepWidthSync.listen { + close(ch) + } }() m := make(map[int][]byte, len(bars)) - for r := range ibbCh { - m[r.index] = r.buf + for ibb := range ibbCh { + m[ibb.index] = ibb.buf } for i := 0; i < len(bars); i++ { cw.Write(m[i]) @@ -297,7 +347,7 @@ func drawer(ibars <-chan indexedBar, ibbCh chan<- indexedBarBuffer) { for b := range ibars { - buf := b.bar.bytes(b.termWidth) + buf := b.bar.bytes(b.termWidth, ws) buf = append(buf, '\n') ibbCh <- indexedBarBuffer{b.index, buf} } @@ -324,3 +374,15 @@ return false } } + +func max(slice []int) int { + max := slice[0] + + for i := 1; i < len(slice); i++ { + if slice[i] > max { + max = slice[i] + } + } + + return max +}