Codebase list golang-github-vbauerster-mpb / 4011be3
Record golang-github-vbauerster-mpb (7.3.2-1) in archive suite sid Andreas Tille 4 years ago
79 changed file(s) with 2024 addition(s) and 688 deletion(s). Raw diff Collapse all Expand all
0 name: Test
1
2 on: [push, pull_request]
3
4 jobs:
5 test:
6 strategy:
7 matrix:
8 go-version: [1.16, 1.17]
9 os: [ubuntu-latest, macos-latest, windows-latest]
10 runs-on: ${{ matrix.os }}
11 steps:
12 - name: Setup Go
13 uses: actions/setup-go@v2
14 with:
15 go-version: ${{ matrix.go-version }}
16 - name: Checkout code
17 uses: actions/checkout@v2
18 - uses: actions/cache@v2
19 with:
20 # In order:
21 # * Module download cache
22 # * Build cache (Linux)
23 # * Build cache (Mac)
24 # * Build cache (Windows)
25 path: |
26 ~/go/pkg/mod
27 ~/.cache/go-build
28 ~/Library/Caches/go-build
29 %LocalAppData%\go-build
30 key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
31 restore-keys: |
32 ${{ runner.os }}-go-
33 - name: Test
34 run: go test -race ./...
+0
-11
.travis.yml less more
0 language: go
1 arch:
2 - amd64
3 - ppc64le
4
5 go:
6 - 1.14.x
7
8 script:
9 - go test -race ./...
10 - for i in _examples/*/; do go build $i/*.go || exit 1; done
00 # Multi Progress Bar
11
22 [![GoDoc](https://pkg.go.dev/badge/github.com/vbauerster/mpb)](https://pkg.go.dev/github.com/vbauerster/mpb/v7)
3 [![Build Status](https://travis-ci.org/vbauerster/mpb.svg?branch=master)](https://travis-ci.org/vbauerster/mpb)
4 [![Go Report Card](https://goreportcard.com/badge/github.com/vbauerster/mpb)](https://goreportcard.com/report/github.com/vbauerster/mpb)
3 [![Test status](https://github.com/vbauerster/mpb/actions/workflows/test.yml/badge.svg)](https://github.com/vbauerster/mpb/actions/workflows/test.yml)
54 [![Donate with PayPal](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/vbauerster)
65
76 **mpb** is a Go lib for rendering progress bars in terminal applications.
3635
3736 total := 100
3837 name := "Single Bar:"
39 // adding a single bar, which will inherit container's width
40 bar := p.Add(int64(total),
41 // progress bar filler with customized style
42 mpb.NewBarFiller(mpb.BarStyle().Lbound("╢").Filler("▌").Tip("▌").Padding("░").Rbound("╟")),
38 // create a single bar, which will inherit container's width
39 bar := p.New(int64(total),
40 // BarFillerBuilder with custom style
41 mpb.BarStyle().Lbound("╢").Filler("▌").Tip("▌").Padding("░").Rbound("╟"),
4342 mpb.PrependDecorators(
4443 // display our name with one space on the right
4544 decor.Name(name, decor.WC{W: len(name) + 1, C: decor.DidentRight}),
6564
6665 ```go
6766 var wg sync.WaitGroup
68 // passed &wg will be accounted at p.Wait() call
67 // passed wg will be accounted at p.Wait() call
6968 p := mpb.New(mpb.WithWaitGroup(&wg))
7069 total, numBars := 100, 3
7170 wg.Add(numBars)
8382 // replace ETA decorator with "done" message, OnComplete event
8483 decor.OnComplete(
8584 // ETA decorator with ewma age of 60
86 decor.EwmaETA(decor.ET_STYLE_GO, 60), "done",
85 decor.EwmaETA(decor.ET_STYLE_GO, 60, decor.WCSyncWidth), "done",
8786 ),
8887 ),
8988 )
103102 }
104103 }()
105104 }
106 // Waiting for passed &wg and for all bars to complete and flush
105 // wait for passed wg and for all bars to complete and flush
107106 p.Wait()
108107 ```
109108
11
22 go 1.14
33
4 require github.com/vbauerster/mpb/v7 v7.0.3
4 require github.com/vbauerster/mpb/v7 v7.3.2
1212
1313 func main() {
1414 var wg sync.WaitGroup
15 // passed wg will be accounted at p.Wait() call
1516 p := mpb.New(mpb.WithWaitGroup(&wg))
1617 total, numBars := 100, 3
1718 wg.Add(numBars)
5455 }
5556 }()
5657 }
57 // wait for all bars to complete and flush
58 // wait for passed wg and for all bars to complete and flush
5859 p.Wait()
5960 }
11
22 go 1.14
33
4 require github.com/vbauerster/mpb/v7 v7.0.3
4 require github.com/vbauerster/mpb/v7 v7.3.2
1515 defer cancel()
1616
1717 var wg sync.WaitGroup
18 // passed wg will be accounted at p.Wait() call
1819 p := mpb.NewWithContext(ctx, mpb.WithWaitGroup(&wg))
1920 total := 300
2021 numBars := 3
4849 }
4950 }()
5051 }
51
52 // wait for passed wg and for all bars to complete and flush
5253 p.Wait()
5354 }
11
22 go 1.14
33
4 require github.com/vbauerster/mpb/v7 v7.0.3
4 require github.com/vbauerster/mpb/v7 v7.3.2
1515
1616 func main() {
1717 doneWg := new(sync.WaitGroup)
18 // passed doneWg will be accounted at p.Wait() call
1819 p := mpb.New(mpb.WithWaitGroup(doneWg))
1920 numBars := 4
2021
6364 go newTask(doneWg, b, numBars-i)
6465 }()
6566 }
66
67 // wait for passed doneWg and for all bars to complete and flush
6768 p.Wait()
6869 }
6970
11
22 go 1.14
33
4 require github.com/vbauerster/mpb/v7 v7.0.3
4 require github.com/vbauerster/mpb/v7 v7.3.2
1212 p := mpb.New()
1313
1414 total := 100
15 bar := p.Add(int64(total), nil,
15 bar := p.New(int64(total),
16 mpb.NopStyle(), // make main bar style nop, so there are just decorators
17 mpb.BarExtender(extended(mpb.BarStyle())), // extend wtih normal bar on the next line
1618 mpb.PrependDecorators(
1719 decor.Name("Percentage: "),
1820 decor.NewPercentage("%d"),
2325 decor.AverageETA(decor.ET_STYLE_GO), "done",
2426 ),
2527 ),
26 mpb.BarExtender(nlBarFiller(mpb.NewBarFiller(mpb.BarStyle()))),
2728 )
2829 // simulating some work
2930 max := 100 * time.Millisecond
3536 p.Wait()
3637 }
3738
38 func nlBarFiller(filler mpb.BarFiller) mpb.BarFiller {
39 func extended(builder mpb.BarFillerBuilder) mpb.BarFiller {
40 filler := builder.Build()
3941 return mpb.BarFillerFunc(func(w io.Writer, reqWidth int, st decor.Statistics) {
4042 filler.Fill(w, reqWidth, st)
4143 w.Write([]byte("\n"))
11
22 go 1.14
33
4 require github.com/vbauerster/mpb/v7 v7.0.3
4 require github.com/vbauerster/mpb/v7 v7.3.2
1111
1212 func main() {
1313 var wg sync.WaitGroup
14 // passed wg will be accounted at p.Wait() call
1415 p := mpb.New(
1516 mpb.WithWaitGroup(&wg),
16 // container's width.
1717 mpb.WithWidth(60),
1818 )
1919 total, numBars := 100, 3
5454 }
5555 }()
5656 }
57 // wait for all bars to complete and flush
57 // wait for passed wg and for all bars to complete and flush
5858 p.Wait()
5959 }
11
22 go 1.14
33
4 require github.com/vbauerster/mpb/v7 v7.0.3
4 require github.com/vbauerster/mpb/v7 v7.3.2
11
22 go 1.14
33
4 require github.com/vbauerster/mpb/v7 v7.0.3
4 require github.com/vbauerster/mpb/v7 v7.3.2
1818 mpb.WithRefreshRate(180*time.Millisecond),
1919 )
2020
21 bar := p.Add(total,
22 mpb.NewBarFiller(mpb.BarStyle().Rbound("|")),
21 bar := p.New(total,
22 mpb.BarStyle().Rbound("|"),
2323 mpb.PrependDecorators(
2424 decor.CountersKibiByte("% .2f / % .2f"),
2525 ),
11
22 go 1.14
33
4 require github.com/vbauerster/mpb/v7 v7.0.3
4 require github.com/vbauerster/mpb/v7 v7.3.2
1111
1212 func main() {
1313 var wg sync.WaitGroup
14 // pass &wg (optional), so p will wait for it eventually
14 // passed wg will be accounted at p.Wait() call
1515 p := mpb.New(mpb.WithWaitGroup(&wg), mpb.WithWidth(60))
1616 total, numBars := 100, 3
1717 wg.Add(numBars)
5858 }
5959 }()
6060 }
61 // Waiting for passed &wg and for all bars to complete and flush
61 // wait for passed wg and for all bars to complete and flush
6262 p.Wait()
6363 }
6464
11
22 go 1.14
33
4 require github.com/vbauerster/mpb/v7 v7.0.3
4 require github.com/vbauerster/mpb/v7 v7.3.2
1919 bs.Tip("\u001b[0m⛵\u001b[36;1m")
2020 bs.Padding("_")
2121 bs.Rbound("\u001b[0m]")
22 bar := p.Add(int64(total),
23 mpb.NewBarFiller(bs),
22 bar := p.New(int64(total), bs,
2423 mpb.PrependDecorators(decor.Name(name)),
2524 mpb.AppendDecorators(decor.Percentage()),
2625 )
11
22 go 1.14
33
4 require github.com/vbauerster/mpb/v7 v7.0.3
4 require github.com/vbauerster/mpb/v7 v7.3.2
1111
1212 func main() {
1313 var wg sync.WaitGroup
14 // passed &wg will be accounted at p.Wait() call
14 // passed wg will be accounted at p.Wait() call
1515 p := mpb.New(mpb.WithWaitGroup(&wg))
1616 total, numBars := 100, 3
1717 wg.Add(numBars)
2929 // replace ETA decorator with "done" message, OnComplete event
3030 decor.OnComplete(
3131 // ETA decorator with ewma age of 60
32 decor.EwmaETA(decor.ET_STYLE_GO, 60), "done",
32 decor.EwmaETA(decor.ET_STYLE_GO, 60, decor.WCSyncWidth), "done",
3333 ),
3434 ),
3535 )
4949 }
5050 }()
5151 }
52 // Waiting for passed &wg and for all bars to complete and flush
52 // wait for passed wg and for all bars to complete and flush
5353 p.Wait()
5454 }
11
22 go 1.14
33
4 require github.com/vbauerster/mpb/v7 v7.0.3
4 require github.com/vbauerster/mpb/v7 v7.3.2
1212
1313 func main() {
1414 var wg sync.WaitGroup
15 p := mpb.New(mpb.WithWaitGroup(&wg), mpb.WithDebugOutput(os.Stderr))
15 // passed wg will be accounted at p.Wait() call
16 p := mpb.New(
17 mpb.WithWaitGroup(&wg),
18 mpb.WithDebugOutput(os.Stderr),
19 )
1620
1721 wantPanic := strings.Repeat("Panic ", 64)
1822 numBars := 3
3034 }
3135 }()
3236 }
33
37 // wait for passed wg and for all bars to complete and flush
3438 p.Wait()
3539 }
3640
11
22 go 1.14
33
4 require github.com/vbauerster/mpb/v7 v7.0.3
4 require github.com/vbauerster/mpb/v7 v7.3.2
11
22 go 1.14
33
4 require github.com/vbauerster/mpb/v7 v7.0.3
4 require github.com/vbauerster/mpb/v7 v7.3.2
1919 func main() {
2020 flag.Parse()
2121 var wg sync.WaitGroup
22 // pass &wg (optional), so p will wait for it eventually
22 // passed wg will be accounted at p.Wait() call
2323 p := mpb.New(
2424 mpb.WithWaitGroup(&wg),
2525 mpb.ContainerOptional(
6767 }
6868 }()
6969 }
70 // Waiting for passed &wg and for all bars to complete and flush
70 // wait for passed wg and for all bars to complete and flush
7171 p.Wait()
7272 fmt.Println("done")
7373 }
11
22 go 1.14
33
4 require github.com/vbauerster/mpb/v7 v7.0.3
4 require github.com/vbauerster/mpb/v7 v7.3.2
1111
1212 func main() {
1313 var wg sync.WaitGroup
14 // passed wg will be accounted at p.Wait() call
1415 p := mpb.New(mpb.WithWaitGroup(&wg))
1516 total := 100
1617 numBars := 3
2324 mpb.BarOptional(mpb.BarRemoveOnComplete(), i == 0),
2425 mpb.PrependDecorators(
2526 decor.Name(name),
26 decor.EwmaETA(decor.ET_STYLE_GO, 60, decor.WCSyncSpace),
2727 ),
28 mpb.AppendDecorators(decor.Percentage()),
28 mpb.AppendDecorators(
29 decor.Any(func(s decor.Statistics) string {
30 return fmt.Sprintf("completed: %v", s.Completed)
31 }, decor.WCSyncSpaceR),
32 decor.Any(func(s decor.Statistics) string {
33 return fmt.Sprintf("aborted: %v", s.Aborted)
34 }, decor.WCSyncSpaceR),
35 decor.OnComplete(decor.NewPercentage("%d", decor.WCSyncSpace), "done"),
36 decor.OnAbort(decor.NewPercentage("%d", decor.WCSyncSpace), "ohno"),
37 ),
2938 )
3039 go func() {
3140 defer wg.Done()
3241 rng := rand.New(rand.NewSource(time.Now().UnixNano()))
3342 max := 100 * time.Millisecond
3443 for i := 0; !bar.Completed(); i++ {
35 // start variable is solely for EWMA calculation
36 // EWMA's unit of measure is an iteration's duration
37 start := time.Now()
3844 if bar.ID() == 2 && i >= 42 {
39 // aborting and removing while bar is running
40 bar.Abort(true)
45 bar.Abort(false)
4146 }
4247 time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10)
4348 bar.Increment()
44 // we need to call DecoratorEwmaUpdate to fulfill ewma decorator's contract
45 bar.DecoratorEwmaUpdate(time.Since(start))
4649 }
4750 }()
4851 }
49
52 // wait for passed wg and for all bars to complete and flush
5053 p.Wait()
5154 }
11
22 go 1.14
33
4 require github.com/vbauerster/mpb/v7 v7.0.3
4 require github.com/vbauerster/mpb/v7 v7.3.2
1111
1212 func main() {
1313 var wg sync.WaitGroup
14 // pass &wg (optional), so p will wait for it eventually
14 // passed wg will be accounted at p.Wait() call
1515 p := mpb.New(mpb.WithWaitGroup(&wg))
1616 total, numBars := 100, 3
1717 wg.Add(numBars)
1818
1919 for i := 0; i < numBars; i++ {
2020 name := fmt.Sprintf("Bar#%d:", i)
21 bs := mpb.BarStyle()
22 if i == 1 {
23 // reverse Bar#1
24 bs = bs.Tip("<").Reverse()
25 }
26 bar := p.Add(int64(total),
27 mpb.NewBarFiller(bs),
21 bar := p.New(int64(total), condBuilder(i == 1),
2822 mpb.PrependDecorators(
2923 // simple name decorator
3024 decor.Name(name),
5549 }
5650 }()
5751 }
58 // Waiting for passed &wg and for all bars to complete and flush
52 // wait for passed wg and for all bars to complete and flush
5953 p.Wait()
6054 }
55
56 func condBuilder(cond bool) mpb.BarFillerBuilder {
57 return mpb.BarFillerBuilderFunc(func() mpb.BarFiller {
58 bs := mpb.BarStyle()
59 if cond {
60 // reverse Bar on cond
61 bs = bs.Tip("<").Reverse()
62 }
63 return bs.Build()
64 })
65 }
11
22 go 1.14
33
4 require github.com/vbauerster/mpb/v7 v7.0.3
4 require github.com/vbauerster/mpb/v7 v7.3.2
1313
1414 total := 100
1515 name := "Single Bar:"
16 // adding a single bar, which will inherit container's width
17 bar := p.Add(int64(total),
18 // progress bar filler with customized style
19 mpb.NewBarFiller(mpb.BarStyle().Lbound("╢").Filler("▌").Tip("▌").Padding("░").Rbound("╟")),
16 // create a single bar, which will inherit container's width
17 bar := p.New(int64(total),
18 // BarFillerBuilder with custom style
19 mpb.BarStyle().Lbound("╢").Filler("▌").Tip("▌").Padding("░").Rbound("╟"),
2020 mpb.PrependDecorators(
2121 // display our name with one space on the right
2222 decor.Name(name, decor.WC{W: len(name) + 1, C: decor.DidentRight}),
11
22 go 1.14
33
4 require github.com/vbauerster/mpb/v7 v7.0.3
4 require github.com/vbauerster/mpb/v7 v7.3.2
1313
1414 total := 100
1515 name := "Single Bar:"
16 bar := p.Add(int64(total),
17 mpb.NewBarFiller(mpb.BarStyle().Tip(`-`, `\`, `|`, `/`)),
16 bar := p.New(int64(total),
17 mpb.BarStyle().Tip(`-`, `\`, `|`, `/`),
1818 mpb.PrependDecorators(decor.Name(name)),
1919 mpb.AppendDecorators(decor.Percentage()),
2020 )
11
22 go 1.14
33
4 require github.com/vbauerster/mpb/v7 v7.0.3
4 require github.com/vbauerster/mpb/v7 v7.3.2
1111
1212 func main() {
1313 var wg sync.WaitGroup
14 // passed wg will be accounted at p.Wait() call
1415 p := mpb.New(
1516 mpb.WithWaitGroup(&wg),
16 mpb.WithWidth(14),
17 mpb.WithWidth(16),
1718 )
1819 total, numBars := 101, 3
1920 wg.Add(numBars)
2021
21 spinnerStyle := []string{"∙∙∙", "●∙∙", "∙●∙", "∙∙●", "∙∙∙"}
22
2322 for i := 0; i < numBars; i++ {
2423 name := fmt.Sprintf("Bar#%d:", i)
25 var bar *mpb.Bar
26 if i == 0 {
27 bar = p.Add(int64(total),
28 mpb.NewBarFiller(mpb.BarStyle().Lbound("╢").Filler("▌").Tip("▌").Padding("░").Rbound("╟")),
29 mpb.PrependDecorators(
30 // simple name decorator
31 decor.Name(name),
24 bar := p.New(int64(total), condBuilder(i != 0),
25 mpb.PrependDecorators(
26 // simple name decorator
27 decor.Name(name),
28 ),
29 mpb.AppendDecorators(
30 // replace ETA decorator with "done" message, OnComplete event
31 decor.OnComplete(
32 // ETA decorator with ewma age of 60
33 decor.EwmaETA(decor.ET_STYLE_GO, 60), "done",
3234 ),
33 mpb.AppendDecorators(
34 // replace ETA decorator with "done" message, OnComplete event
35 decor.OnComplete(
36 // ETA decorator with ewma age of 60
37 decor.EwmaETA(decor.ET_STYLE_GO, 60), "done",
38 ),
39 ),
40 )
41 } else {
42 bar = p.Add(int64(total),
43 mpb.NewBarFiller(mpb.SpinnerStyle(spinnerStyle...)),
44 mpb.PrependDecorators(
45 // simple name decorator
46 decor.Name(name),
47 ),
48 mpb.AppendDecorators(
49 // replace ETA decorator with "done" message, OnComplete event
50 decor.OnComplete(
51 // ETA decorator with ewma age of 60
52 decor.EwmaETA(decor.ET_STYLE_GO, 60), "done",
53 ),
54 ),
55 )
56 }
57
35 ),
36 )
5837 // simulating some work
5938 go func() {
6039 defer wg.Done()
7150 }
7251 }()
7352 }
74 // wait for all bars to complete and flush
53 // wait for passed wg and for all bars to complete and flush
7554 p.Wait()
7655 }
56
57 func condBuilder(cond bool) mpb.BarFillerBuilder {
58 return mpb.BarFillerBuilderFunc(func() mpb.BarFiller {
59 if cond {
60 // spinner Bar on cond
61 frames := []string{"∙∙∙", "●∙∙", "∙●∙", "∙∙●", "∙∙∙"}
62 return mpb.SpinnerStyle(frames...).Build()
63 }
64 return mpb.BarStyle().Lbound("╢").Filler("▌").Tip("▌").Padding("░").Rbound("╟").Build()
65 })
66 }
11
22 go 1.14
33
4 require github.com/vbauerster/mpb/v7 v7.0.3
4 require github.com/vbauerster/mpb/v7 v7.3.2
1111
1212 func main() {
1313 var wg sync.WaitGroup
14 // pass &wg (optional), so p will wait for it eventually
14 // passed wg will be accounted at p.Wait() call
1515 p := mpb.New(mpb.WithWaitGroup(&wg), mpb.WithWidth(64))
1616 total, numBars := 100, 3
1717 wg.Add(numBars)
4343 }
4444 }()
4545 }
46 // Waiting for passed &wg and for all bars to complete and flush
46 // wait for passed wg and for all bars to complete and flush
4747 p.Wait()
4848 }
11
22 go 1.14
33
4 require github.com/vbauerster/mpb/v7 v7.0.3
4 require github.com/vbauerster/mpb/v7 v7.3.2
1515
1616 func main() {
1717 var wg sync.WaitGroup
18 p := mpb.New(
19 mpb.WithWaitGroup(&wg),
20 mpb.WithRefreshRate(50*time.Millisecond),
21 )
18 // passed wg will be accounted at p.Wait() call
19 p := mpb.New(mpb.WithWaitGroup(&wg))
2220 wg.Add(totalBars)
2321
2422 for i := 0; i < totalBars; i++ {
4644 }
4745 }()
4846 }
49
47 // wait for passed wg and for all bars to complete and flush
5048 p.Wait()
5149 }
33
44 require (
55 github.com/mattn/go-runewidth v0.0.13
6 github.com/vbauerster/mpb/v7 v7.0.3
6 github.com/vbauerster/mpb/v7 v7.3.2
77 )
0 module github.com/vbauerster/mpb/_examples/tipOnComplete
1
2 go 1.14
3
4 require github.com/vbauerster/mpb/v7 v7.3.2
0 package main
1
2 import (
3 "math/rand"
4 "time"
5
6 "github.com/vbauerster/mpb/v7"
7 "github.com/vbauerster/mpb/v7/decor"
8 )
9
10 func main() {
11 // initialize progress container, with custom width
12 p := mpb.New(mpb.WithWidth(80))
13
14 total := 100
15 name := "Single Bar:"
16 bar := p.New(int64(total),
17 mpb.BarStyle().TipOnComplete(">"),
18 mpb.PrependDecorators(decor.Name(name)),
19 mpb.AppendDecorators(decor.Percentage()),
20 )
21 // simulating some work
22 max := 100 * time.Millisecond
23 for i := 0; i < total; i++ {
24 time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10)
25 bar.Increment()
26 }
27 // wait for our bar to complete and flush
28 p.Wait()
29 }
+191
-114
bar.go less more
44 "context"
55 "fmt"
66 "io"
7 "log"
87 "runtime/debug"
98 "strings"
9 "sync"
1010 "time"
1111
1212 "github.com/acarl005/stripansi"
1919 priority int // used by heap
2020 index int // used by heap
2121
22 extendedLines int
2322 toShutdown bool
2423 toDrop bool
2524 noPop bool
2625 hasEwmaDecorators bool
2726 operateState chan func(*bState)
28 frameCh chan io.Reader
29 syncTableCh chan [][]chan int
30 completed chan bool
27 frameCh chan *frame
3128
3229 // cancel is called either by user or on complete event
3330 cancel func()
3431 // done is closed after cacheState is assigned
3532 done chan struct{}
36 // cacheState is populated, right after close(shutdown)
33 // cacheState is populated, right after close(b.done)
3734 cacheState *bState
3835
3936 container *Progress
40 dlogger *log.Logger
4137 recoveredPanic interface{}
4238 }
4339
5248 total int64
5349 current int64
5450 refill int64
55 lastN int64
56 iterated bool
51 lastIncrement int64
5752 trimSpace bool
5853 completed bool
5954 completeFlushed bool
55 aborted bool
6056 triggerComplete bool
6157 dropOnComplete bool
6258 noPop bool
6561 averageDecorators []decor.AverageDecorator
6662 ewmaDecorators []decor.EwmaDecorator
6763 shutdownListeners []decor.ShutdownListener
68 bufP, bufB, bufA *bytes.Buffer
64 buffers [3]*bytes.Buffer
6965 filler BarFiller
7066 middleware func(BarFiller) BarFiller
7167 extender extenderFunc
7672 debugOut io.Writer
7773 }
7874
75 type frame struct {
76 reader io.Reader
77 lines int
78 }
79
7980 func newBar(container *Progress, bs *bState) *Bar {
80 logPrefix := fmt.Sprintf("%sbar#%02d ", container.dlogger.Prefix(), bs.id)
8181 ctx, cancel := context.WithCancel(container.ctx)
8282
8383 bar := &Bar{
8686 toDrop: bs.dropOnComplete,
8787 noPop: bs.noPop,
8888 operateState: make(chan func(*bState)),
89 frameCh: make(chan io.Reader, 1),
90 syncTableCh: make(chan [][]chan int, 1),
91 completed: make(chan bool, 1),
89 frameCh: make(chan *frame, 1),
9290 done: make(chan struct{}),
9391 cancel: cancel,
94 dlogger: log.New(bs.debugOut, logPrefix, log.Lshortfile),
9592 }
9693
9794 go bar.serve(ctx, bs)
104101 if r == nil {
105102 panic("expected non nil io.Reader")
106103 }
107 return newProxyReader(r, b)
104 return b.newProxyReader(r)
108105 }
109106
110107 // ID returs id of the bar.
144141
145142 // TraverseDecorators traverses all available decorators and calls cb func on each.
146143 func (b *Bar) TraverseDecorators(cb func(decor.Decorator)) {
144 done := make(chan struct{})
147145 select {
148146 case b.operateState <- func(s *bState) {
149147 for _, decorators := range [...][]decor.Decorator{
154152 cb(extractBaseDecorator(d))
155153 }
156154 }
157 }:
155 close(done)
156 }:
157 <-done
158158 case <-b.done:
159159 }
160160 }
161161
162162 // SetTotal sets total dynamically.
163 // If total is less than or equal to zero it takes progress' current value.
163 // If total is negative it takes progress' current value.
164164 func (b *Bar) SetTotal(total int64, triggerComplete bool) {
165165 select {
166166 case b.operateState <- func(s *bState) {
167167 s.triggerComplete = triggerComplete
168 if total <= 0 {
168 if total < 0 {
169169 s.total = s.current
170170 } else {
171171 s.total = total
173173 if s.triggerComplete && !s.completed {
174174 s.current = s.total
175175 s.completed = true
176 go b.refreshTillShutdown()
176 go b.forceRefreshIfLastUncompleted()
177177 }
178178 }:
179179 case <-b.done:
185185 func (b *Bar) SetCurrent(current int64) {
186186 select {
187187 case b.operateState <- func(s *bState) {
188 s.iterated = true
189 s.lastN = current - s.current
188 s.lastIncrement = current - s.current
190189 s.current = current
191190 if s.triggerComplete && s.current >= s.total {
192191 s.current = s.total
193192 s.completed = true
194 go b.refreshTillShutdown()
193 go b.forceRefreshIfLastUncompleted()
195194 }
196195 }:
197196 case <-b.done:
210209
211210 // IncrInt64 increments progress by amount of n.
212211 func (b *Bar) IncrInt64(n int64) {
213 select {
214 case b.operateState <- func(s *bState) {
215 s.iterated = true
216 s.lastN = n
212 if n <= 0 {
213 return
214 }
215 select {
216 case b.operateState <- func(s *bState) {
217 s.lastIncrement = n
217218 s.current += n
218219 if s.triggerComplete && s.current >= s.total {
219220 s.current = s.total
220221 s.completed = true
221 go b.refreshTillShutdown()
222 go b.forceRefreshIfLastUncompleted()
222223 }
223224 }:
224225 case <-b.done:
232233 func (b *Bar) DecoratorEwmaUpdate(dur time.Duration) {
233234 select {
234235 case b.operateState <- func(s *bState) {
235 ewmaIterationUpdate(false, s, dur)
236 }:
237 case <-b.done:
238 ewmaIterationUpdate(true, b.cacheState, dur)
236 if s.lastIncrement > 0 {
237 s.decoratorEwmaUpdate(dur)
238 s.lastIncrement = 0
239 } else {
240 panic("increment required before ewma iteration update")
241 }
242 }:
243 case <-b.done:
244 if b.cacheState.lastIncrement > 0 {
245 b.cacheState.decoratorEwmaUpdate(dur)
246 b.cacheState.lastIncrement = 0
247 }
239248 }
240249 }
241250
245254 func (b *Bar) DecoratorAverageAdjust(start time.Time) {
246255 select {
247256 case b.operateState <- func(s *bState) {
248 for _, d := range s.averageDecorators {
249 d.AverageAdjust(start)
250 }
257 s.decoratorAverageAdjust(start)
251258 }:
252259 case <-b.done:
253260 }
257264 // priority, i.e. bar will be on top. If you don't need to set priority
258265 // dynamically, better use BarPriority option.
259266 func (b *Bar) SetPriority(priority int) {
260 select {
261 case <-b.done:
262 default:
263 b.container.setBarPriority(b, priority)
264 }
265 }
266
267 // Abort interrupts bar's running goroutine. Call this, if you'd like
268 // to stop/remove bar before completion event. It has no effect after
269 // completion event. If drop is true bar will be removed as well.
267 b.container.UpdateBarPriority(b, priority)
268 }
269
270 // Abort interrupts bar's running goroutine. Abort won't be engaged
271 // if bar is already in complete state. If drop is true bar will be
272 // removed as well.
270273 func (b *Bar) Abort(drop bool) {
271 select {
272 case <-b.done:
273 default:
274 if drop {
275 b.container.dropBar(b)
276 }
274 done := make(chan struct{})
275 select {
276 case b.operateState <- func(s *bState) {
277 if s.completed {
278 close(done)
279 return
280 }
281 s.aborted = true
277282 b.cancel()
283 // container must be run during lifetime of this inner goroutine
284 // we control this by done channel declared above
285 go func() {
286 if drop {
287 b.container.dropBar(b)
288 } else {
289 var uncompleted int
290 b.container.traverseBars(func(bar *Bar) bool {
291 if b != bar && !bar.Completed() {
292 uncompleted++
293 return false
294 }
295 return true
296 })
297 if uncompleted == 0 {
298 b.container.refreshCh <- time.Now()
299 }
300 }
301 close(done) // release hold of Abort
302 }()
303 }:
304 // guarantee: container is alive during lifetime of this hold
305 <-done
306 case <-b.done:
278307 }
279308 }
280309
281310 // Completed reports whether the bar is in completed state.
282311 func (b *Bar) Completed() bool {
283 select {
284 case b.operateState <- func(s *bState) { b.completed <- s.completed }:
285 return <-b.completed
312 result := make(chan bool)
313 select {
314 case b.operateState <- func(s *bState) { result <- s.completed }:
315 return <-result
286316 case <-b.done:
287317 return true
288318 }
295325 case op := <-b.operateState:
296326 op(s)
297327 case <-ctx.Done():
328 s.decoratorShutdownNotify()
298329 b.cacheState = s
299330 close(b.done)
300 // Notifying decorators about shutdown event
301 for _, sl := range s.shutdownListeners {
302 sl.Shutdown()
303 }
304331 return
305332 }
306333 }
314341 // recovering if user defined decorator panics for example
315342 if p := recover(); p != nil {
316343 if b.recoveredPanic == nil {
344 if s.debugOut != nil {
345 fmt.Fprintln(s.debugOut, p)
346 _, _ = s.debugOut.Write(debug.Stack())
347 }
317348 s.extender = makePanicExtender(p)
318349 b.toShutdown = !b.toShutdown
319350 b.recoveredPanic = p
320351 }
321 frame, lines := s.extender(nil, s.reqWidth, stat)
322 b.extendedLines = lines
323 b.frameCh <- frame
324 b.dlogger.Println(p)
352 reader, lines := s.extender(nil, s.reqWidth, stat)
353 b.frameCh <- &frame{reader, lines + 1}
325354 }
326355 s.completeFlushed = s.completed
327356 }()
328 frame, lines := s.extender(s.draw(stat), s.reqWidth, stat)
329 b.extendedLines = lines
357 reader, lines := s.extender(s.draw(stat), s.reqWidth, stat)
330358 b.toShutdown = s.completed && !s.completeFlushed
331 b.frameCh <- frame
359 b.frameCh <- &frame{reader, lines + 1}
332360 }:
333361 case <-b.done:
334362 s := b.cacheState
337365 if b.recoveredPanic == nil {
338366 r = s.draw(stat)
339367 }
340 frame, lines := s.extender(r, s.reqWidth, stat)
341 b.extendedLines = lines
342 b.frameCh <- frame
368 reader, lines := s.extender(r, s.reqWidth, stat)
369 b.frameCh <- &frame{reader, lines + 1}
343370 }
344371 }
345372
358385 shutdownListeners = append(shutdownListeners, d)
359386 }
360387 })
388 b.hasEwmaDecorators = len(ewmaDecorators) != 0
361389 select {
362390 case b.operateState <- func(s *bState) {
363391 s.averageDecorators = averageDecorators
364392 s.ewmaDecorators = ewmaDecorators
365393 s.shutdownListeners = shutdownListeners
366394 }:
367 b.hasEwmaDecorators = len(ewmaDecorators) != 0
368 case <-b.done:
369 }
370 }
371
372 func (b *Bar) refreshTillShutdown() {
373 for {
374 select {
375 case b.container.refreshCh <- time.Now():
376 case <-b.done:
377 return
395 case <-b.done:
396 }
397 }
398
399 func (b *Bar) forceRefreshIfLastUncompleted() {
400 var uncompleted int
401 b.container.traverseBars(func(bar *Bar) bool {
402 if b != bar && !bar.Completed() {
403 uncompleted++
404 return false
405 }
406 return true
407 })
408 if uncompleted == 0 {
409 for {
410 select {
411 case b.container.refreshCh <- time.Now():
412 case <-b.done:
413 return
414 }
378415 }
379416 }
380417 }
381418
382419 func (b *Bar) wSyncTable() [][]chan int {
383 select {
384 case b.operateState <- func(s *bState) { b.syncTableCh <- s.wSyncTable() }:
385 return <-b.syncTableCh
420 result := make(chan [][]chan int)
421 select {
422 case b.operateState <- func(s *bState) { result <- s.wSyncTable() }:
423 return <-result
386424 case <-b.done:
387425 return b.cacheState.wSyncTable()
388426 }
389427 }
390428
391429 func (s *bState) draw(stat decor.Statistics) io.Reader {
430 bufP, bufB, bufA := s.buffers[0], s.buffers[1], s.buffers[2]
392431 nlr := strings.NewReader("\n")
393432 tw := stat.AvailableWidth
394433 for _, d := range s.pDecorators {
395434 str := d.Decor(stat)
396435 stat.AvailableWidth -= runewidth.StringWidth(stripansi.Strip(str))
397 s.bufP.WriteString(str)
436 bufP.WriteString(str)
398437 }
399438 if stat.AvailableWidth < 1 {
400 trunc := strings.NewReader(runewidth.Truncate(stripansi.Strip(s.bufP.String()), tw, "…"))
401 s.bufP.Reset()
439 trunc := strings.NewReader(runewidth.Truncate(stripansi.Strip(bufP.String()), tw, "…"))
440 bufP.Reset()
402441 return io.MultiReader(trunc, nlr)
403442 }
404443
405444 if !s.trimSpace && stat.AvailableWidth > 1 {
406445 stat.AvailableWidth -= 2
407 s.bufB.WriteByte(' ')
408 defer s.bufB.WriteByte(' ')
446 bufB.WriteByte(' ')
447 defer bufB.WriteByte(' ')
409448 }
410449
411450 tw = stat.AvailableWidth
412451 for _, d := range s.aDecorators {
413452 str := d.Decor(stat)
414453 stat.AvailableWidth -= runewidth.StringWidth(stripansi.Strip(str))
415 s.bufA.WriteString(str)
454 bufA.WriteString(str)
416455 }
417456 if stat.AvailableWidth < 1 {
418 trunc := strings.NewReader(runewidth.Truncate(stripansi.Strip(s.bufA.String()), tw, "…"))
419 s.bufA.Reset()
420 return io.MultiReader(s.bufP, s.bufB, trunc, nlr)
421 }
422
423 s.filler.Fill(s.bufB, s.reqWidth, stat)
424
425 return io.MultiReader(s.bufP, s.bufB, s.bufA, nlr)
457 trunc := strings.NewReader(runewidth.Truncate(stripansi.Strip(bufA.String()), tw, "…"))
458 bufA.Reset()
459 return io.MultiReader(bufP, bufB, trunc, nlr)
460 }
461
462 s.filler.Fill(bufB, s.reqWidth, stat)
463
464 return io.MultiReader(bufP, bufB, bufA, nlr)
426465 }
427466
428467 func (s *bState) wSyncTable() [][]chan int {
445484 table[0] = columns[0:pCount]
446485 table[1] = columns[pCount : pCount+aCount : pCount+aCount]
447486 return table
487 }
488
489 func (s bState) decoratorEwmaUpdate(dur time.Duration) {
490 wg := new(sync.WaitGroup)
491 for i := 0; i < len(s.ewmaDecorators); i++ {
492 switch d := s.ewmaDecorators[i]; i {
493 case len(s.ewmaDecorators) - 1:
494 d.EwmaUpdate(s.lastIncrement, dur)
495 default:
496 wg.Add(1)
497 go func() {
498 d.EwmaUpdate(s.lastIncrement, dur)
499 wg.Done()
500 }()
501 }
502 }
503 wg.Wait()
504 }
505
506 func (s bState) decoratorAverageAdjust(start time.Time) {
507 wg := new(sync.WaitGroup)
508 for i := 0; i < len(s.averageDecorators); i++ {
509 switch d := s.averageDecorators[i]; i {
510 case len(s.averageDecorators) - 1:
511 d.AverageAdjust(start)
512 default:
513 wg.Add(1)
514 go func() {
515 d.AverageAdjust(start)
516 wg.Done()
517 }()
518 }
519 }
520 wg.Wait()
521 }
522
523 func (s bState) decoratorShutdownNotify() {
524 wg := new(sync.WaitGroup)
525 for i := 0; i < len(s.shutdownListeners); i++ {
526 switch d := s.shutdownListeners[i]; i {
527 case len(s.shutdownListeners) - 1:
528 d.Shutdown()
529 default:
530 wg.Add(1)
531 go func() {
532 d.Shutdown()
533 wg.Done()
534 }()
535 }
536 }
537 wg.Wait()
448538 }
449539
450540 func newStatistics(tw int, s *bState) decor.Statistics {
455545 Current: s.current,
456546 Refill: s.refill,
457547 Completed: s.completeFlushed,
548 Aborted: s.aborted,
458549 }
459550 }
460551
465556 return d
466557 }
467558
468 func ewmaIterationUpdate(done bool, s *bState, dur time.Duration) {
469 if !done && !s.iterated {
470 panic("increment required before ewma iteration update")
471 } else {
472 s.iterated = false
473 }
474 for _, d := range s.ewmaDecorators {
475 d.EwmaUpdate(s.lastN, dur)
476 }
477 }
478
479559 func makePanicExtender(p interface{}) extenderFunc {
480560 pstr := fmt.Sprint(p)
481 stack := debug.Stack()
482 stackLines := bytes.Count(stack, []byte("\n"))
483561 return func(_ io.Reader, _ int, st decor.Statistics) (io.Reader, int) {
484562 mr := io.MultiReader(
485563 strings.NewReader(runewidth.Truncate(pstr, st.AvailableWidth, "…")),
486 strings.NewReader(fmt.Sprintf("\n%#v\n", st)),
487 bytes.NewReader(stack),
564 strings.NewReader("\n"),
488565 )
489 return mr, stackLines + 1
490 }
491 }
566 return mr, 0
567 }
568 }
88 // BarFiller interface.
99 // Bar (without decorators) renders itself by calling BarFiller's Fill method.
1010 //
11 // reqWidth is requested width, set by `func WithWidth(int) ContainerOption`.
11 // reqWidth is requested width set by `func WithWidth(int) ContainerOption`.
1212 // If not set, it defaults to terminal width.
13 //
14 // Default implementations can be obtained via:
15 //
16 // func NewBarFiller(BarStyle()) BarFiller
17 // func NewBarFiller(SpinnerStyle()) BarFiller
1813 //
1914 type BarFiller interface {
2015 Fill(w io.Writer, reqWidth int, stat decor.Statistics)
2116 }
2217
2318 // BarFillerBuilder interface.
19 // Default implementations are:
20 //
21 // BarStyle()
22 // SpinnerStyle()
23 // NopStyle()
24 //
2425 type BarFillerBuilder interface {
2526 Build() BarFiller
2627 }
2728
28 // BarFillerFunc is function type adapter to convert function into BarFiller.
29 // BarFillerFunc is function type adapter to convert compatible function
30 // into BarFiller interface.
2931 type BarFillerFunc func(w io.Writer, reqWidth int, stat decor.Statistics)
3032
3133 func (f BarFillerFunc) Fill(w io.Writer, reqWidth int, stat decor.Statistics) {
3234 f(w, reqWidth, stat)
3335 }
3436
37 // BarFillerBuilderFunc is function type adapter to convert compatible
38 // function into BarFillerBuilder interface.
39 type BarFillerBuilderFunc func() BarFiller
40
41 func (f BarFillerBuilderFunc) Build() BarFiller {
42 return f()
43 }
44
3545 // NewBarFiller constructs a BarFiller from provided BarFillerBuilder.
46 // Deprecated. Prefer using `*Progress.New(...)` directly.
3647 func NewBarFiller(b BarFillerBuilder) BarFiller {
3748 return b.Build()
3849 }
2525 Filler(string) BarStyleComposer
2626 Refiller(string) BarStyleComposer
2727 Padding(string) BarStyleComposer
28 Tip(...string) BarStyleComposer
28 TipOnComplete(string) BarStyleComposer
29 Tip(frames ...string) BarStyleComposer
2930 Reverse() BarStyleComposer
3031 }
3132
3233 type bFiller struct {
34 rev bool
3335 components [components]*component
3436 tip struct {
35 count uint
36 frames []*component
37 }
38 flush func(dst io.Writer, filling, padding [][]byte)
37 count uint
38 onComplete *component
39 frames []*component
40 }
3941 }
4042
4143 type component struct {
4446 }
4547
4648 type barStyle struct {
47 lbound string
48 rbound string
49 filler string
50 refiller string
51 padding string
52 tip []string
53 rev bool
49 lbound string
50 rbound string
51 filler string
52 refiller string
53 padding string
54 tipOnComplete string
55 tipFrames []string
56 rev bool
5457 }
5558
5659 // BarStyle constructs default bar style which can be altered via
5760 // BarStyleComposer interface.
5861 func BarStyle() BarStyleComposer {
5962 return &barStyle{
60 lbound: "[",
61 rbound: "]",
62 filler: "=",
63 refiller: "+",
64 padding: "-",
65 tip: []string{">"},
63 lbound: "[",
64 rbound: "]",
65 filler: "=",
66 refiller: "+",
67 padding: "-",
68 tipFrames: []string{">"},
6669 }
6770 }
6871
9194 return s
9295 }
9396
94 func (s *barStyle) Tip(tip ...string) BarStyleComposer {
95 if len(tip) != 0 {
96 s.tip = append(s.tip[:0], tip...)
97 func (s *barStyle) TipOnComplete(tip string) BarStyleComposer {
98 s.tipOnComplete = tip
99 return s
100 }
101
102 func (s *barStyle) Tip(frames ...string) BarStyleComposer {
103 if len(frames) != 0 {
104 s.tipFrames = append(s.tipFrames[:0], frames...)
97105 }
98106 return s
99107 }
104112 }
105113
106114 func (s *barStyle) Build() BarFiller {
107 bf := new(bFiller)
108 if s.rev {
109 bf.flush = func(dst io.Writer, filling, padding [][]byte) {
110 flush(dst, padding, filling)
111 }
112 } else {
113 bf.flush = flush
114 }
115 bf := &bFiller{rev: s.rev}
115116 bf.components[iLbound] = &component{
116117 width: runewidth.StringWidth(stripansi.Strip(s.lbound)),
117118 bytes: []byte(s.lbound),
132133 width: runewidth.StringWidth(stripansi.Strip(s.padding)),
133134 bytes: []byte(s.padding),
134135 }
135 bf.tip.frames = make([]*component, len(s.tip))
136 for i, t := range s.tip {
136 bf.tip.onComplete = &component{
137 width: runewidth.StringWidth(stripansi.Strip(s.tipOnComplete)),
138 bytes: []byte(s.tipOnComplete),
139 }
140 bf.tip.frames = make([]*component, len(s.tipFrames))
141 for i, t := range s.tipFrames {
137142 bf.tip.frames[i] = &component{
138143 width: runewidth.StringWidth(stripansi.Strip(t)),
139144 bytes: []byte(t),
145150 func (s *bFiller) Fill(w io.Writer, width int, stat decor.Statistics) {
146151 width = internal.CheckRequestedWidth(width, stat.AvailableWidth)
147152 brackets := s.components[iLbound].width + s.components[iRbound].width
148 if width < brackets {
149 return
150 }
151153 // don't count brackets as progress
152154 width -= brackets
153
154 w.Write(s.components[iLbound].bytes)
155 defer w.Write(s.components[iRbound].bytes)
156
157 curWidth := int(internal.PercentageRound(stat.Total, stat.Current, width))
158 refWidth, filled := 0, curWidth
159 filling := make([][]byte, 0, curWidth)
160
161 if curWidth > 0 && curWidth != width {
162 tipFrame := s.tip.frames[s.tip.count%uint(len(s.tip.frames))]
163 filling = append(filling, tipFrame.bytes)
164 curWidth -= tipFrame.width
155 if width < 0 {
156 return
157 }
158
159 ow := optimisticWriter(w)
160 ow(s.components[iLbound].bytes)
161 defer ow(s.components[iRbound].bytes)
162
163 if width == 0 {
164 return
165 }
166
167 var filling [][]byte
168 var padding [][]byte
169 var tip *component
170 var filled int
171 var refWidth int
172 curWidth := int(internal.PercentageRound(stat.Total, stat.Current, uint(width)))
173
174 if stat.Current >= stat.Total {
175 tip = s.tip.onComplete
176 } else {
177 tip = s.tip.frames[s.tip.count%uint(len(s.tip.frames))]
178 }
179
180 if curWidth > 0 {
181 filling = append(filling, tip.bytes)
182 filled += tip.width
165183 s.tip.count++
166184 }
167185
168 if stat.Refill > 0 && curWidth > 0 {
169 refWidth = int(internal.PercentageRound(stat.Total, int64(stat.Refill), width))
170 if refWidth > curWidth {
171 refWidth = curWidth
172 }
186 if stat.Refill > 0 {
187 refWidth = int(internal.PercentageRound(stat.Total, stat.Refill, uint(width)))
173188 curWidth -= refWidth
174 }
175
176 for curWidth > 0 && curWidth >= s.components[iFiller].width {
177 filling = append(filling, s.components[iFiller].bytes)
178 curWidth -= s.components[iFiller].width
179 if s.components[iFiller].width == 0 {
180 break
181 }
182 }
183
184 for refWidth > 0 && refWidth >= s.components[iRefiller].width {
185 filling = append(filling, s.components[iRefiller].bytes)
186 refWidth -= s.components[iRefiller].width
187 if s.components[iRefiller].width == 0 {
188 break
189 }
190 }
191
192 filled -= curWidth + refWidth
189 refWidth += curWidth
190 }
191
192 for filled < curWidth {
193 if curWidth-filled >= s.components[iFiller].width {
194 filling = append(filling, s.components[iFiller].bytes)
195 if s.components[iFiller].width == 0 {
196 break
197 }
198 filled += s.components[iFiller].width
199 } else {
200 filling = append(filling, []byte("…"))
201 filled++
202 }
203 }
204
205 for filled < refWidth {
206 if refWidth-filled >= s.components[iRefiller].width {
207 filling = append(filling, s.components[iRefiller].bytes)
208 if s.components[iRefiller].width == 0 {
209 break
210 }
211 filled += s.components[iRefiller].width
212 } else {
213 filling = append(filling, []byte("…"))
214 filled++
215 }
216 }
217
193218 padWidth := width - filled
194 padding := make([][]byte, 0, padWidth)
195 for padWidth > 0 && padWidth >= s.components[iPadding].width {
196 padding = append(padding, s.components[iPadding].bytes)
197 padWidth -= s.components[iPadding].width
198 if s.components[iPadding].width == 0 {
199 break
200 }
201 }
202
203219 for padWidth > 0 {
204 padding = append(padding, []byte("…"))
205 padWidth--
206 }
207
208 s.flush(w, filling, padding)
209 }
210
211 func flush(dst io.Writer, filling, padding [][]byte) {
220 if padWidth >= s.components[iPadding].width {
221 padding = append(padding, s.components[iPadding].bytes)
222 if s.components[iPadding].width == 0 {
223 break
224 }
225 padWidth -= s.components[iPadding].width
226 } else {
227 padding = append(padding, []byte("…"))
228 padWidth--
229 }
230 }
231
232 if s.rev {
233 flush(ow, padding, filling)
234 } else {
235 flush(ow, filling, padding)
236 }
237 }
238
239 func flush(ow func([]byte), filling, padding [][]byte) {
212240 for i := len(filling) - 1; i >= 0; i-- {
213 dst.Write(filling[i])
241 ow(filling[i])
214242 }
215243 for i := 0; i < len(padding); i++ {
216 dst.Write(padding[i])
217 }
218 }
244 ow(padding[i])
245 }
246 }
247
248 func optimisticWriter(w io.Writer) func([]byte) {
249 return func(p []byte) {
250 _, err := w.Write(p)
251 if err != nil {
252 panic(err)
253 }
254 }
255 }
0 package mpb
1
2 import (
3 "io"
4
5 "github.com/vbauerster/mpb/v7/decor"
6 )
7
8 // NopStyle provides BarFillerBuilder which builds NOP BarFiller.
9 func NopStyle() BarFillerBuilder {
10 return BarFillerBuilderFunc(func() BarFiller {
11 return BarFillerFunc(func(io.Writer, int, decor.Statistics) {})
12 })
13 }
7272 return
7373 }
7474
75 var err error
7576 rest := width - frameWidth
7677 switch s.position {
7778 case positionLeft:
78 io.WriteString(w, frame+strings.Repeat(" ", rest))
79 _, err = io.WriteString(w, frame+strings.Repeat(" ", rest))
7980 case positionRight:
80 io.WriteString(w, strings.Repeat(" ", rest)+frame)
81 _, err = io.WriteString(w, strings.Repeat(" ", rest)+frame)
8182 default:
8283 str := strings.Repeat(" ", rest/2) + frame + strings.Repeat(" ", rest/2+rest%2)
83 io.WriteString(w, str)
84 _, err = io.WriteString(w, str)
85 }
86 if err != nil {
87 panic(err)
8488 }
8589 s.count++
8690 }
44 "io"
55
66 "github.com/vbauerster/mpb/v7/decor"
7 "github.com/vbauerster/mpb/v7/internal"
87 )
98
109 // BarOption is a func option to alter default behavior of a bar.
1110 type BarOption func(*bState)
11
12 func skipNil(decorators []decor.Decorator) (filtered []decor.Decorator) {
13 for _, d := range decorators {
14 if d != nil {
15 filtered = append(filtered, d)
16 }
17 }
18 return
19 }
1220
1321 func (s *bState) addDecorators(dest *[]decor.Decorator, decorators ...decor.Decorator) {
1422 type mergeWrapper interface {
2533 // AppendDecorators let you inject decorators to the bar's right side.
2634 func AppendDecorators(decorators ...decor.Decorator) BarOption {
2735 return func(s *bState) {
28 s.addDecorators(&s.aDecorators, decorators...)
36 s.addDecorators(&s.aDecorators, skipNil(decorators)...)
2937 }
3038 }
3139
3240 // PrependDecorators let you inject decorators to the bar's left side.
3341 func PrependDecorators(decorators ...decor.Decorator) BarOption {
3442 return func(s *bState) {
35 s.addDecorators(&s.pDecorators, decorators...)
43 s.addDecorators(&s.pDecorators, skipNil(decorators)...)
3644 }
3745 }
3846
8088 return BarFillerMiddleware(func(base BarFiller) BarFiller {
8189 return BarFillerFunc(func(w io.Writer, reqWidth int, st decor.Statistics) {
8290 if st.Completed {
83 io.WriteString(w, message)
91 _, err := io.WriteString(w, message)
92 if err != nil {
93 panic(err)
94 }
8495 } else {
8596 base.Fill(w, reqWidth, st)
8697 }
137148 }
138149 }
139150
140 // BarOptional will invoke provided option only when pick is true.
141 func BarOptional(option BarOption, pick bool) BarOption {
142 return BarOptOn(option, internal.Predicate(pick))
151 // BarOptional will invoke provided option only when cond is true.
152 func BarOptional(option BarOption, cond bool) BarOption {
153 if cond {
154 return option
155 }
156 return nil
143157 }
144158
145159 // BarOptOn will invoke provided option only when higher order predicate
6262 till := 30
6363 refiller := "+"
6464
65 bar := p.Add(int64(total), mpb.NewBarFiller(mpb.BarStyle().Refiller(refiller)), mpb.BarFillerTrim())
65 bar := p.New(int64(total), mpb.BarStyle().Refiller(refiller), mpb.BarFillerTrim())
6666
6767 bar.SetRefill(int64(till))
6868 bar.IncrBy(till)
151151 bs.Tip(string(runes[2]))
152152 bs.Padding(string(runes[3]))
153153 bs.Rbound(string(runes[4]))
154 bar := p.Add(int64(total), mpb.NewBarFiller(bs), mpb.BarFillerTrim())
154 bar := p.New(int64(total), bs, mpb.BarFillerTrim())
155155
156156 for i := 0; i < total; i++ {
157157 bar.Increment()
254254 }
255255
256256 func TestDecorStatisticsAvailableWidth(t *testing.T) {
257 total := 100
258 down := make(chan struct{})
259 checkDone := make(chan struct{})
257260 td1 := func(s decor.Statistics) string {
258261 if s.AvailableWidth != 80 {
259262 t.Errorf("expected AvailableWidth %d got %d\n", 80, s.AvailableWidth)
260263 }
261264 return fmt.Sprintf("\x1b[31;1;4m%s\x1b[0m", strings.Repeat("0", 20))
262265 }
263 checkDone := make(chan struct{})
264266 td2 := func(s decor.Statistics) string {
265267 defer func() {
266 checkDone <- struct{}{}
268 select {
269 case checkDone <- struct{}{}:
270 default:
271 }
267272 }()
268273 if s.AvailableWidth != 40 {
269274 t.Errorf("expected AvailableWidth %d got %d\n", 40, s.AvailableWidth)
270275 }
271276 return ""
272277 }
273 total := 100
274 down := make(chan struct{})
275278 p := mpb.New(
276279 mpb.WithWidth(100),
277280 mpb.WithShutdownNotifier(down),
292295 for {
293296 select {
294297 case <-checkDone:
295 bar.Abort(false)
298 bar.Abort(true)
296299 case <-down:
297300 return
298301 }
00 package mpb
11
22 import (
3 "io/ioutil"
3 "sync"
44 "testing"
5
6 "github.com/vbauerster/mpb/v7/decor"
75 )
86
9 func BenchmarkIncrSingleBar(b *testing.B) {
10 p := New(WithOutput(ioutil.Discard), WithWidth(80))
11 bar := p.AddBar(int64(b.N))
12 for i := 0; i < b.N; i++ {
13 bar.Increment()
14 }
7 const total = 1000
8
9 func BenchmarkIncrementOneBar(b *testing.B) {
10 benchBody(1, b)
1511 }
1612
17 func BenchmarkIncrSingleBarWhileIsNotCompleted(b *testing.B) {
18 p := New(WithOutput(ioutil.Discard), WithWidth(80))
19 bar := p.AddBar(int64(b.N))
20 for !bar.Completed() {
21 bar.Increment()
22 }
13 func BenchmarkIncrementTwoBars(b *testing.B) {
14 benchBody(2, b)
2315 }
2416
25 func BenchmarkIncrSingleBarWithNameDecorator(b *testing.B) {
26 p := New(WithOutput(ioutil.Discard), WithWidth(80))
27 bar := p.AddBar(int64(b.N), PrependDecorators(decor.Name("test")))
28 for i := 0; i < b.N; i++ {
29 bar.Increment()
30 }
17 func BenchmarkIncrementThreeBars(b *testing.B) {
18 benchBody(3, b)
3119 }
3220
33 func BenchmarkIncrSingleBarWithNameAndEwmaETADecorator(b *testing.B) {
34 p := New(WithOutput(ioutil.Discard), WithWidth(80))
35 bar := p.AddBar(int64(b.N),
36 PrependDecorators(decor.Name("test")),
37 AppendDecorators(decor.EwmaETA(decor.ET_STYLE_GO, 60)),
38 )
21 func BenchmarkIncrementFourBars(b *testing.B) {
22 benchBody(4, b)
23 }
24
25 func benchBody(n int, b *testing.B) {
26 p := New(WithOutput(nil), WithWidth(80))
27 wg := new(sync.WaitGroup)
28 b.ResetTimer()
3929 for i := 0; i < b.N; i++ {
40 bar.Increment()
30 for j := 0; j < n; j++ {
31 switch j {
32 case n - 1:
33 bar := p.AddBar(total)
34 for c := 0; c < total; c++ {
35 bar.Increment()
36 }
37 if !bar.Completed() {
38 b.Fail()
39 }
40 default:
41 wg.Add(1)
42 go func() {
43 bar := p.AddBar(total)
44 for c := 0; c < total; c++ {
45 bar.Increment()
46 }
47 if !bar.Completed() {
48 b.Fail()
49 }
50 wg.Done()
51 }()
52 }
53 }
54 wg.Wait()
4155 }
56 p.Wait()
4257 }
44 "io/ioutil"
55 "sync"
66 "time"
7
8 "github.com/vbauerster/mpb/v7/internal"
97 )
108
119 // ContainerOption is a func option to alter default behavior of a bar
6159 // have been rendered.
6260 func WithShutdownNotifier(ch chan struct{}) ContainerOption {
6361 return func(s *pState) {
64 s.shutdownNotifier = ch
62 select {
63 case <-ch:
64 default:
65 s.shutdownNotifier = ch
66 }
6567 }
6668 }
6769
9698 }
9799 }
98100
99 // ContainerOptional will invoke provided option only when pick is true.
100 func ContainerOptional(option ContainerOption, pick bool) ContainerOption {
101 return ContainerOptOn(option, internal.Predicate(pick))
101 // ContainerOptional will invoke provided option only when cond is true.
102 func ContainerOptional(option ContainerOption, cond bool) ContainerOption {
103 if cond {
104 return option
105 }
106 return nil
102107 }
103108
104109 // ContainerOptOn will invoke provided option only when higher order
1717 func BenchmarkWithJoin(b *testing.B) {
1818 bCuuAndEd := [][]byte{[]byte("\x1b["), []byte("A\x1b[J")}
1919 for i := 0; i < b.N; i++ {
20 ioutil.Discard.Write(bytes.Join(bCuuAndEd, []byte(strconv.Itoa(4))))
20 _, _ = ioutil.Discard.Write(bytes.Join(bCuuAndEd, []byte(strconv.Itoa(4))))
2121 }
2222 }
2323
2525 escOpen := []byte("\x1b[")
2626 cuuAndEd := []byte("A\x1b[J")
2727 for i := 0; i < b.N; i++ {
28 ioutil.Discard.Write(append(strconv.AppendInt(escOpen, 4, 10), cuuAndEd...))
28 _, _ = ioutil.Discard.Write(append(strconv.AppendInt(escOpen, 4, 10), cuuAndEd...))
2929 }
3030 }
3131
3232 func BenchmarkWithCopy(b *testing.B) {
3333 w := New(ioutil.Discard)
34 w.lineCount = 4
34 w.lines = 4
3535 for i := 0; i < b.N; i++ {
36 w.ansiCuuAndEd()
36 _ = w.ansiCuuAndEd()
3737 }
3838 }
2121 type Writer struct {
2222 out io.Writer
2323 buf bytes.Buffer
24 lineCount int
24 lines int
2525 fd int
2626 isTerminal bool
2727 }
3737 }
3838
3939 // Flush flushes the underlying buffer.
40 func (w *Writer) Flush(lineCount int) (err error) {
40 func (w *Writer) Flush(lines int) (err error) {
4141 // some terminals interpret 'cursor up 0' as 'cursor up 1'
42 if w.lineCount > 0 {
42 if w.lines > 0 {
4343 err = w.clearLines()
4444 if err != nil {
4545 return
4646 }
4747 }
48 w.lineCount = lineCount
48 w.lines = lines
4949 _, err = w.buf.WriteTo(w.out)
5050 return
5151 }
7575 return tw, err
7676 }
7777
78 func (w *Writer) ansiCuuAndEd() (err error) {
78 func (w *Writer) ansiCuuAndEd() error {
7979 buf := make([]byte, 8)
80 buf = strconv.AppendInt(buf[:copy(buf, escOpen)], int64(w.lineCount), 10)
81 _, err = w.out.Write(append(buf, cuuAndEd...))
82 return
80 buf = strconv.AppendInt(buf[:copy(buf, escOpen)], int64(w.lines), 10)
81 _, err := w.out.Write(append(buf, cuuAndEd...))
82 return err
8383 }
2525 return err
2626 }
2727
28 info.CursorPosition.Y -= int16(w.lineCount)
28 info.CursorPosition.Y -= int16(w.lines)
2929 if info.CursorPosition.Y < 0 {
3030 info.CursorPosition.Y = 0
3131 }
3939 X: info.Window.Left,
4040 Y: info.CursorPosition.Y,
4141 }
42 count := uint32(info.Size.X) * uint32(w.lineCount)
42 count := uint32(info.Size.X) * uint32(w.lines)
4343 _, _, _ = procFillConsoleOutputCharacter.Call(
4444 uintptr(w.fd),
4545 uintptr(' '),
0 golang-github-vbauerster-mpb (7.0.3-1) unstable; urgency=medium
0 golang-github-vbauerster-mpb (7.3.2-1) unstable; urgency=medium
11
2 * New upstream release
2 * Team upload.
3 * New upstream version
4 * Standards-Version: 4.6.0 (routine-update)
5 * debhelper-compat 13 (routine-update)
6 * Rules-Requires-Root: no (routine-update)
7 * (Build-)Depends: golang-github-mattn-go-runewidth-dev
38
4 -- Reinhard Tartler <siretart@tauware.de> Sat, 28 Aug 2021 22:10:19 +0200
5
6 golang-github-vbauerster-mpb (6.0.3-2) unstable; urgency=medium
7
8 * Upload to unstable
9
10 -- Reinhard Tartler <siretart@tauware.de> Sat, 28 Aug 2021 21:11:02 +0200
11
12 golang-github-vbauerster-mpb (6.0.3-1) experimental; urgency=medium
13
14 * Team upload
15 * Upgrade to version v6.0.3, Closes: #987511)
16 Breaking changes (cf. https://github.com/vbauerster/mpb/releases):
17 Removed:
18 func BarStyle(style string) BarOption
19 func BarReverse() BarOption
20 func SpinnerStyle(frames []string) BarOption
21 func TrimSpace() BarOption
22 To set bar style use BarFiller constructors.
23 .
24 Added:
25 func ContainerOptional(option ContainerOption, pick bool) ContainerOption
26 func BarOptional(option BarOption, pick bool) BarOption
27
28 -- Reinhard Tartler <siretart@tauware.de> Sun, 25 Apr 2021 11:32:28 -0400
9 -- Andreas Tille <tille@debian.org> Wed, 26 Jan 2022 20:33:53 +0100
2910
3011 golang-github-vbauerster-mpb (5.0.3-2) unstable; urgency=low
3112
33 Maintainer: Debian Go Packaging Team <team+pkg-go@tracker.debian.org>
44 Uploaders: Reinhard Tartler <siretart@tauware.de>,
55 Dmitry Smirnov <onlyjob@debian.org>,
6 Build-Depends: debhelper-compat (= 12),
6 Build-Depends: debhelper-compat (= 13),
77 dh-golang,
88 golang-any,
99 golang-github-acarl005-stripansi-dev,
1010 golang-github-mattn-go-isatty-dev,
1111 golang-github-mattn-go-runewidth-dev,
12 golang-github-rivo-uniseg-dev,
1312 golang-github-vividcortex-ewma-dev,
14 Standards-Version: 4.5.0
13 golang-golang-x-crypto-dev
14 Standards-Version: 4.6.0
1515 Homepage: https://github.com/vbauerster/mpb
16 Rules-Requires-Root: no
1617 Vcs-Browser: https://salsa.debian.org/go-team/packages/golang-github-vbauerster-mpb
1718 Vcs-Git: https://salsa.debian.org/go-team/packages/golang-github-vbauerster-mpb.git
1819 XS-Go-Import-Path: github.com/vbauerster/mpb
2425 golang-github-acarl005-stripansi-dev,
2526 golang-github-mattn-go-isatty-dev,
2627 golang-github-mattn-go-runewidth-dev,
27 golang-github-rivo-uniseg-dev,
2828 golang-github-vividcortex-ewma-dev,
29 golang-golang-x-crypto-dev
2930 Description: multi progress bar for Go cli applications
3031 mpb is a golang library for rendering progress bars in terminal
3132 applications.
0 # auto-generated, DO NOT MODIFY.
1 # The authoritative copy of this file lives at:
2 # https://salsa.debian.org/go-team/infra/pkg-go-tools/blob/master/config/gitlabciyml.go
3 ---
4 include:
5 - https://salsa.debian.org/go-team/infra/pkg-go-tools/-/raw/master/pipeline/test-archive.yml
5252 Current int64
5353 Refill int64
5454 Completed bool
55 Aborted bool
5556 }
5657
5758 // Decorator interface.
1616 // +----+--------+---------+--------+
1717 //
1818 func Merge(decorator Decorator, placeholders ...WC) Decorator {
19 if decorator == nil {
20 return nil
21 }
1922 if _, ok := decorator.Sync(); !ok || len(placeholders) == 0 {
2023 return decorator
2124 }
0 package decor
1
2 // OnAbort returns decorator, which wraps provided decorator with sole
3 // purpose to display provided message on abort event. It has no effect
4 // if bar.Abort(drop bool) is called with true argument.
5 //
6 // `decorator` Decorator to wrap
7 //
8 // `message` message to display on abort event
9 //
10 func OnAbort(decorator Decorator, message string) Decorator {
11 if decorator == nil {
12 return nil
13 }
14 d := &onAbortWrapper{
15 Decorator: decorator,
16 msg: message,
17 }
18 if md, ok := decorator.(*mergeDecorator); ok {
19 d.Decorator, md.Decorator = md.Decorator, d
20 return md
21 }
22 return d
23 }
24
25 type onAbortWrapper struct {
26 Decorator
27 msg string
28 }
29
30 func (d *onAbortWrapper) Decor(s Statistics) string {
31 if s.Aborted {
32 wc := d.GetConf()
33 return wc.FormatMsg(d.msg)
34 }
35 return d.Decorator.Decor(s)
36 }
37
38 func (d *onAbortWrapper) Base() Decorator {
39 return d.Decorator
40 }
00 package decor
11
2 // OnComplete returns decorator, which wraps provided decorator, with
2 // OnComplete returns decorator, which wraps provided decorator with
33 // sole purpose to display provided message on complete event.
44 //
55 // `decorator` Decorator to wrap
77 // `message` message to display on complete event
88 //
99 func OnComplete(decorator Decorator, message string) Decorator {
10 if decorator == nil {
11 return nil
12 }
1013 d := &onCompleteWrapper{
1114 Decorator: decorator,
1215 msg: message,
0 package decor
1
2 // OnPredicate returns decorator if predicate evaluates to true.
3 //
4 // `decorator` Decorator
5 //
6 // `predicate` func() bool
7 //
8 func OnPredicate(decorator Decorator, predicate func() bool) Decorator {
9 if predicate() {
10 return decorator
11 }
12 return nil
13 }
14
15 // OnCondition returns decorator if condition is true.
16 //
17 // `decorator` Decorator
18 //
19 // `cond` bool
20 //
21 func OnCondition(decorator Decorator, cond bool) Decorator {
22 if cond {
23 return decorator
24 }
25 return nil
26 }
0 package decor
1
2 import "io"
3
4 func optimisticStringWriter(w io.Writer) func(string) {
5 return func(s string) {
6 _, err := io.WriteString(w, s)
7 if err != nil {
8 panic(err)
9 }
10 }
11 }
11
22 import (
33 "fmt"
4 "io"
54 "strconv"
65
76 "github.com/vbauerster/mpb/v7/internal"
2322 }
2423 }
2524
26 io.WriteString(st, strconv.FormatFloat(float64(s), 'f', prec, 64))
27
25 osw := optimisticStringWriter(st)
26 osw(strconv.FormatFloat(float64(s), 'f', prec, 64))
2827 if st.Flag(' ') {
29 io.WriteString(st, " ")
28 osw(" ")
3029 }
31 io.WriteString(st, "%")
30 osw("%")
3231 }
3332
3433 // Percentage returns percentage decorator. It's a wrapper of NewPercentage.
11
22 import (
33 "fmt"
4 "io"
5 "math"
64 "strconv"
75 )
86
4644 unit = _iMiB
4745 case self < _iTiB:
4846 unit = _iGiB
49 case self <= math.MaxInt64:
47 default:
5048 unit = _iTiB
5149 }
5250
53 io.WriteString(st, strconv.FormatFloat(float64(self)/float64(unit), 'f', prec, 64))
54
51 osw := optimisticStringWriter(st)
52 osw(strconv.FormatFloat(float64(self)/float64(unit), 'f', prec, 64))
5553 if st.Flag(' ') {
56 io.WriteString(st, " ")
54 osw(" ")
5755 }
58 io.WriteString(st, unit.String())
56 osw(unit.String())
5957 }
6058
6159 const (
9593 unit = _MB
9694 case self < _TB:
9795 unit = _GB
98 case self <= math.MaxInt64:
96 default:
9997 unit = _TB
10098 }
10199
102 io.WriteString(st, strconv.FormatFloat(float64(self)/float64(unit), 'f', prec, 64))
103
100 osw := optimisticStringWriter(st)
101 osw(strconv.FormatFloat(float64(self)/float64(unit), 'f', prec, 64))
104102 if st.Flag(' ') {
105 io.WriteString(st, " ")
103 osw(" ")
106104 }
107 io.WriteString(st, unit.String())
105 osw(unit.String())
108106 }
11
22 import (
33 "fmt"
4 "io"
54 "math"
65 "time"
76
2322
2423 func (self *speedFormatter) Format(st fmt.State, verb rune) {
2524 self.Formatter.Format(st, verb)
26 io.WriteString(st, "/s")
25 optimisticStringWriter(st)("/s")
2726 }
2827
2928 // EwmaSpeed exponential-weighted-moving-average based speed decorator.
55 "unicode/utf8"
66 )
77
8 func TestDraw(t *testing.T) {
8 func TestDrawDefault(t *testing.T) {
99 // key is termWidth
1010 testSuite := map[int][]struct {
1111 style BarStyleComposer
1919 }{
2020 0: {
2121 {
22 style: BarStyle(),
2223 name: "t,c{60,20}",
2324 total: 60,
2425 current: 20,
2526 want: "",
2627 },
2728 {
29 style: BarStyle(),
2830 name: "t,c{60,20}trim",
2931 total: 60,
3032 current: 20,
3436 },
3537 1: {
3638 {
39 style: BarStyle(),
3740 name: "t,c{60,20}",
3841 total: 60,
3942 current: 20,
4043 want: "",
4144 },
4245 {
46 style: BarStyle(),
4347 name: "t,c{60,20}trim",
4448 total: 60,
4549 current: 20,
4953 },
5054 2: {
5155 {
56 style: BarStyle(),
5257 name: "t,c{60,20}",
5358 total: 60,
5459 current: 20,
5560 want: " ",
5661 },
5762 {
63 style: BarStyle(),
5864 name: "t,c{60,20}trim",
5965 total: 60,
6066 current: 20,
6470 },
6571 3: {
6672 {
73 style: BarStyle(),
6774 name: "t,c{60,20}",
6875 total: 60,
6976 current: 20,
7077 want: " ",
7178 },
7279 {
80 style: BarStyle(),
7381 name: "t,c{60,20}trim",
7482 total: 60,
7583 current: 20,
7684 trim: true,
7785 want: "[-]",
7886 },
87 {
88 style: BarStyle(),
89 name: "t,c{60,59}",
90 total: 60,
91 current: 59,
92 want: " ",
93 },
94 {
95 style: BarStyle(),
96 name: "t,c{60,59}trim",
97 total: 60,
98 current: 59,
99 trim: true,
100 want: "[>]",
101 },
102 {
103 style: BarStyle(),
104 name: "t,c{60,60}",
105 total: 60,
106 current: 60,
107 want: " ",
108 },
109 {
110 style: BarStyle(),
111 name: "t,c{60,60}trim",
112 total: 60,
113 current: 60,
114 trim: true,
115 want: "[=]",
116 },
79117 },
80118 4: {
81119 {
120 style: BarStyle(),
82121 name: "t,c{60,20}",
83122 total: 60,
84123 current: 20,
85124 want: " [] ",
86125 },
87126 {
127 style: BarStyle(),
88128 name: "t,c{60,20}trim",
89129 total: 60,
90130 current: 20,
91131 trim: true,
92132 want: "[>-]",
93133 },
134 {
135 style: BarStyle(),
136 name: "t,c{60,59}",
137 total: 60,
138 current: 59,
139 want: " [] ",
140 },
141 {
142 style: BarStyle(),
143 name: "t,c{60,59}trim",
144 total: 60,
145 current: 59,
146 trim: true,
147 want: "[=>]",
148 },
149 {
150 style: BarStyle(),
151 name: "t,c{60,60}",
152 total: 60,
153 current: 60,
154 want: " [] ",
155 },
156 {
157 style: BarStyle(),
158 name: "t,c{60,60}trim",
159 total: 60,
160 current: 60,
161 trim: true,
162 want: "[==]",
163 },
94164 },
95165 5: {
96166 {
167 style: BarStyle(),
97168 name: "t,c{60,20}",
98169 total: 60,
99170 current: 20,
100171 want: " [-] ",
101172 },
102173 {
174 style: BarStyle(),
103175 name: "t,c{60,20}trim",
104176 total: 60,
105177 current: 20,
106178 trim: true,
107179 want: "[>--]",
108180 },
181 {
182 style: BarStyle(),
183 name: "t,c{60,59}",
184 total: 60,
185 current: 59,
186 want: " [>] ",
187 },
188 {
189 style: BarStyle(),
190 name: "t,c{60,59}trim",
191 total: 60,
192 current: 59,
193 trim: true,
194 want: "[==>]",
195 },
196 {
197 style: BarStyle(),
198 name: "t,c{60,60}",
199 total: 60,
200 current: 60,
201 want: " [=] ",
202 },
203 {
204 style: BarStyle(),
205 name: "t,c{60,60}trim",
206 total: 60,
207 current: 60,
208 trim: true,
209 want: "[===]",
210 },
109211 },
110212 6: {
111213 {
214 style: BarStyle(),
112215 name: "t,c{60,20}",
113216 total: 60,
114217 current: 20,
115218 want: " [>-] ",
116219 },
117220 {
221 style: BarStyle(),
118222 name: "t,c{60,20}trim",
119223 total: 60,
120224 current: 20,
121225 trim: true,
122226 want: "[>---]",
123227 },
228 {
229 style: BarStyle(),
230 name: "t,c{60,59}",
231 total: 60,
232 current: 59,
233 want: " [=>] ",
234 },
235 {
236 style: BarStyle(),
237 name: "t,c{60,59}trim",
238 total: 60,
239 current: 59,
240 trim: true,
241 want: "[===>]",
242 },
243 {
244 style: BarStyle(),
245 name: "t,c{60,60}",
246 total: 60,
247 current: 60,
248 want: " [==] ",
249 },
250 {
251 style: BarStyle(),
252 name: "t,c{60,60}trim",
253 total: 60,
254 current: 60,
255 trim: true,
256 want: "[====]",
257 },
124258 },
125259 7: {
126260 {
261 style: BarStyle(),
127262 name: "t,c{60,20}",
128263 total: 60,
129264 current: 20,
130265 want: " [>--] ",
131266 },
132267 {
268 style: BarStyle(),
133269 name: "t,c{60,20}trim",
134270 total: 60,
135271 current: 20,
136272 trim: true,
137273 want: "[=>---]",
138274 },
275 {
276 style: BarStyle(),
277 name: "t,c{60,59}",
278 total: 60,
279 current: 59,
280 want: " [==>] ",
281 },
282 {
283 style: BarStyle(),
284 name: "t,c{60,59}trim",
285 total: 60,
286 current: 59,
287 trim: true,
288 want: "[====>]",
289 },
290 {
291 style: BarStyle(),
292 name: "t,c{60,60}",
293 total: 60,
294 current: 60,
295 want: " [===] ",
296 },
297 {
298 style: BarStyle(),
299 name: "t,c{60,60}trim",
300 total: 60,
301 current: 60,
302 trim: true,
303 want: "[=====]",
304 },
139305 },
140306 8: {
141307 {
308 style: BarStyle(),
142309 name: "t,c{60,20}",
143310 total: 60,
144311 current: 20,
145312 want: " [>---] ",
146313 },
147314 {
315 style: BarStyle(),
148316 name: "t,c{60,20}trim",
149317 total: 60,
150318 current: 20,
151319 trim: true,
152320 want: "[=>----]",
153321 },
322 {
323 style: BarStyle(),
324 name: "t,c{60,59}",
325 total: 60,
326 current: 59,
327 want: " [===>] ",
328 },
329 {
330 style: BarStyle(),
331 name: "t,c{60,59}trim",
332 total: 60,
333 current: 59,
334 trim: true,
335 want: "[=====>]",
336 },
337 {
338 style: BarStyle(),
339 name: "t,c{60,60}",
340 total: 60,
341 current: 60,
342 want: " [====] ",
343 },
344 {
345 style: BarStyle(),
346 name: "t,c{60,60}trim",
347 total: 60,
348 current: 60,
349 trim: true,
350 want: "[======]",
351 },
154352 },
155353 80: {
156354 {
355 style: BarStyle(),
157356 name: "t,c{60,20}",
158357 total: 60,
159358 current: 20,
160359 want: " [========================>---------------------------------------------------] ",
161360 },
162361 {
362 style: BarStyle(),
163363 name: "t,c{60,20}trim",
164364 total: 60,
165365 current: 20,
167367 want: "[=========================>----------------------------------------------------]",
168368 },
169369 {
370 style: BarStyle(),
170371 name: "t,c,bw{60,20,60}",
171372 total: 60,
172373 current: 20,
174375 want: " [==================>---------------------------------------] ",
175376 },
176377 {
378 style: BarStyle(),
177379 name: "t,c,bw{60,20,60}trim",
178380 total: 60,
179381 current: 20,
181383 trim: true,
182384 want: "[==================>---------------------------------------]",
183385 },
386 {
387 style: BarStyle(),
388 name: "t,c{60,59}",
389 total: 60,
390 current: 59,
391 want: " [==========================================================================>-] ",
392 },
393 {
394 style: BarStyle(),
395 name: "t,c{60,59}trim",
396 total: 60,
397 current: 59,
398 trim: true,
399 want: "[============================================================================>-]",
400 },
401 {
402 style: BarStyle(),
403 name: "t,c,bw{60,59,60}",
404 total: 60,
405 current: 59,
406 barWidth: 60,
407 want: " [========================================================>-] ",
408 },
409 {
410 style: BarStyle(),
411 name: "t,c,bw{60,59,60}trim",
412 total: 60,
413 current: 59,
414 barWidth: 60,
415 trim: true,
416 want: "[========================================================>-]",
417 },
418 {
419 style: BarStyle(),
420 name: "t,c{60,60}",
421 total: 60,
422 current: 60,
423 want: " [============================================================================] ",
424 },
425 {
426 style: BarStyle(),
427 name: "t,c{60,60}trim",
428 total: 60,
429 current: 60,
430 trim: true,
431 want: "[==============================================================================]",
432 },
433 {
434 style: BarStyle(),
435 name: "t,c,bw{60,60,60}",
436 total: 60,
437 current: 60,
438 barWidth: 60,
439 want: " [==========================================================] ",
440 },
441 {
442 style: BarStyle(),
443 name: "t,c,bw{60,60,60}trim",
444 total: 60,
445 current: 60,
446 barWidth: 60,
447 trim: true,
448 want: "[==========================================================]",
449 },
184450 },
185451 99: {
186452 {
187 style: BarStyle().Lbound("[").Filler("の").Tip("だ").Padding("つ").Rbound("]"),
188 name: "[のだつ] t,c{99,1}",
189 total: 99,
453 style: BarStyle(),
454 name: "t,c{100,1}",
455 total: 100,
190456 current: 1,
191 want: " [だつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつ…] ",
192 },
193 {
194 style: BarStyle().Lbound("[").Filler("の").Tip("だ").Padding("つ").Rbound("]"),
195 name: "[のだつ] t,c{99,2}",
196 total: 99,
197 current: 2,
198 want: " [だつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつ…] ",
199 },
200 {
201 style: BarStyle().Lbound("[").Filler("の").Tip("だ").Padding("つ").Rbound("]"),
202 name: "[のだつ] t,c{99,3}",
203 total: 99,
204 current: 3,
205 want: " [だつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつ…] ",
206 },
207 {
208 style: BarStyle().Lbound("[").Filler("の").Tip("だ").Padding("つ").Rbound("]"),
209 name: "[のだつ] t,c{99,4}",
210 total: 99,
211 current: 4,
212 want: " [のだつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつ…] ",
213 },
214 {
215 style: BarStyle().Lbound("[").Filler("の").Tip("だ").Padding("つ").Rbound("]"),
216 name: "[のだつ] t,c{99,5}",
217 total: 99,
218 current: 5,
219 want: " [のだつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつ…] ",
220 },
221 {
222 style: BarStyle().Lbound("[").Filler("の").Tip("だ").Padding("つ").Rbound("]"),
223 name: "[のだつ] t,c{99,6}",
224 total: 99,
225 current: 6,
226 want: " [ののだつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつ…] ",
227 },
228 {
229 style: BarStyle().Lbound("[").Filler("の").Tip("だ").Padding("つ").Rbound("]").Reverse(),
230 name: "[のだつ] t,c{99,6}rev",
231 total: 99,
232 current: 6,
233 want: " […つつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつだのの] ",
457 want: " [>----------------------------------------------------------------------------------------------] ",
458 },
459 {
460 style: BarStyle(),
461 name: "t,c{100,1}trim",
462 total: 100,
463 current: 1,
464 trim: true,
465 want: "[>------------------------------------------------------------------------------------------------]",
466 },
467 {
468 style: BarStyle(),
469 name: "t,c,r{100,40,33}",
470 total: 100,
471 current: 40,
472 refill: 33,
473 want: " [+++++++++++++++++++++++++++++++======>---------------------------------------------------------] ",
474 },
475 {
476 style: BarStyle(),
477 name: "t,c,r{100,40,33}trim",
478 total: 100,
479 current: 40,
480 refill: 33,
481 trim: true,
482 want: "[++++++++++++++++++++++++++++++++======>----------------------------------------------------------]",
483 },
484 {
485 style: BarStyle().Tip("<").Reverse(),
486 name: "t,c,r{100,40,33},rev",
487 total: 100,
488 current: 40,
489 refill: 33,
490 want: " [---------------------------------------------------------<======+++++++++++++++++++++++++++++++] ",
491 },
492 {
493 style: BarStyle().Tip("<").Reverse(),
494 name: "t,c,r{100,40,33}trim,rev",
495 total: 100,
496 current: 40,
497 refill: 33,
498 trim: true,
499 want: "[----------------------------------------------------------<======++++++++++++++++++++++++++++++++]",
500 },
501 {
502 style: BarStyle(),
503 name: "t,c{100,99}",
504 total: 100,
505 current: 99,
506 want: " [=============================================================================================>-] ",
507 },
508 {
509 style: BarStyle(),
510 name: "t,c{100,99}trim",
511 total: 100,
512 current: 99,
513 trim: true,
514 want: "[===============================================================================================>-]",
515 },
516 {
517 style: BarStyle(),
518 name: "t,c{100,100}",
519 total: 100,
520 current: 100,
521 want: " [===============================================================================================] ",
522 },
523 {
524 style: BarStyle(),
525 name: "t,c{100,100}trim",
526 total: 100,
527 current: 100,
528 trim: true,
529 want: "[=================================================================================================]",
530 },
531 {
532 style: BarStyle(),
533 name: "t,c,r{100,100,99}",
534 total: 100,
535 current: 100,
536 refill: 99,
537 want: " [++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=] ",
538 },
539 {
540 style: BarStyle(),
541 name: "t,c,r{100,100,99}trim",
542 total: 100,
543 current: 100,
544 refill: 99,
545 trim: true,
546 want: "[++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=]",
547 },
548 {
549 style: BarStyle().Reverse(),
550 name: "t,c,r{100,100,99}rev",
551 total: 100,
552 current: 100,
553 refill: 99,
554 want: " [=++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++] ",
555 },
556 {
557 style: BarStyle().Reverse(),
558 name: "t,c,r{100,100,99}trim,rev",
559 total: 100,
560 current: 100,
561 refill: 99,
562 trim: true,
563 want: "[=++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]",
564 },
565 {
566 style: BarStyle(),
567 name: "t,c,r{100,100,100}",
568 total: 100,
569 current: 100,
570 refill: 100,
571 want: " [+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++] ",
572 },
573 {
574 style: BarStyle(),
575 name: "t,c,r{100,100,100}trim",
576 total: 100,
577 current: 100,
578 refill: 100,
579 trim: true,
580 want: "[+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]",
581 },
582 {
583 style: BarStyle().Reverse(),
584 name: "t,c,r{100,100,100}rev",
585 total: 100,
586 current: 100,
587 refill: 100,
588 want: " [+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++] ",
589 },
590 {
591 style: BarStyle().Reverse(),
592 name: "t,c,r{100,100,100}trim",
593 total: 100,
594 current: 100,
595 refill: 100,
596 trim: true,
597 want: "[+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]",
234598 },
235599 },
236600 100: {
237601 {
602 style: BarStyle(),
238603 name: "t,c{100,0}",
239604 total: 100,
240605 current: 0,
241606 want: " [------------------------------------------------------------------------------------------------] ",
242607 },
243608 {
609 style: BarStyle(),
244610 name: "t,c{100,0}trim",
245611 total: 100,
246612 current: 0,
248614 want: "[--------------------------------------------------------------------------------------------------]",
249615 },
250616 {
617 style: BarStyle(),
251618 name: "t,c{100,1}",
252619 total: 100,
253620 current: 1,
254621 want: " [>-----------------------------------------------------------------------------------------------] ",
255622 },
256623 {
624 style: BarStyle(),
257625 name: "t,c{100,1}trim",
258626 total: 100,
259627 current: 1,
261629 want: "[>-------------------------------------------------------------------------------------------------]",
262630 },
263631 {
632 style: BarStyle(),
264633 name: "t,c{100,99}",
265634 total: 100,
266635 current: 99,
267636 want: " [==============================================================================================>-] ",
268637 },
269638 {
639 style: BarStyle(),
270640 name: "t,c{100,99}trim",
271641 total: 100,
272642 current: 99,
274644 want: "[================================================================================================>-]",
275645 },
276646 {
647 style: BarStyle(),
277648 name: "t,c{100,100}",
278649 total: 100,
279650 current: 100,
280651 want: " [================================================================================================] ",
281652 },
282653 {
654 style: BarStyle(),
283655 name: "t,c{100,100}trim",
284656 total: 100,
285657 current: 100,
287659 want: "[==================================================================================================]",
288660 },
289661 {
662 style: BarStyle(),
663 name: "t,c,r{100,100,99}",
664 total: 100,
665 current: 100,
666 refill: 99,
667 want: " [+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=] ",
668 },
669 {
670 style: BarStyle(),
671 name: "t,c,r{100,100,99}trim",
672 total: 100,
673 current: 100,
674 refill: 99,
675 trim: true,
676 want: "[+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=]",
677 },
678 {
679 style: BarStyle(),
680 name: "t,c,r{100,100,100}",
681 total: 100,
682 current: 100,
683 refill: 100,
684 want: " [++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++] ",
685 },
686 {
687 style: BarStyle(),
290688 name: "t,c,r{100,100,100}trim",
291689 total: 100,
292690 current: 100,
295693 want: "[++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]",
296694 },
297695 {
298 name: "t,c{100,33}",
299 total: 100,
300 current: 33,
301 want: " [===============================>----------------------------------------------------------------] ",
302 },
303 {
304 name: "t,c{100,33}trim",
305 total: 100,
306 current: 33,
307 trim: true,
308 want: "[===============================>------------------------------------------------------------------]",
309 },
310 {
311 style: BarStyle().Tip("<").Reverse(),
312 name: "t,c{100,33}trim,rev",
313 total: 100,
314 current: 33,
315 trim: true,
316 want: "[------------------------------------------------------------------<===============================]",
317 },
318 {
319 name: "t,c,r{100,33,33}",
320 total: 100,
321 current: 33,
322 refill: 33,
323 want: " [+++++++++++++++++++++++++++++++>----------------------------------------------------------------] ",
324 },
325 {
326 name: "t,c,r{100,33,33}trim",
327 total: 100,
328 current: 33,
329 refill: 33,
330 trim: true,
331 want: "[+++++++++++++++++++++++++++++++>------------------------------------------------------------------]",
332 },
333 {
334 style: BarStyle().Tip("<").Reverse(),
335 name: "t,c,r{100,33,33}trim,rev",
336 total: 100,
337 current: 33,
338 refill: 33,
339 trim: true,
340 want: "[------------------------------------------------------------------<+++++++++++++++++++++++++++++++]",
341 },
342 {
696 style: BarStyle().Reverse(),
697 name: "t,c,r{100,100,99}rev",
698 total: 100,
699 current: 100,
700 refill: 99,
701 want: " [=+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++] ",
702 },
703 {
704 style: BarStyle().Reverse(),
705 name: "t,c,r{100,100,99}trim,rev",
706 total: 100,
707 current: 100,
708 refill: 99,
709 trim: true,
710 want: "[=+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]",
711 },
712 {
713 style: BarStyle().Reverse(),
714 name: "t,c,r{100,100,100}rev",
715 total: 100,
716 current: 100,
717 refill: 100,
718 want: " [++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++] ",
719 },
720 {
721 style: BarStyle().Reverse(),
722 name: "t,c,r{100,100,100}trim",
723 total: 100,
724 current: 100,
725 refill: 100,
726 trim: true,
727 want: "[++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]",
728 },
729 {
730 style: BarStyle(),
343731 name: "t,c,r{100,40,33}",
344732 total: 100,
345733 current: 40,
347735 want: " [++++++++++++++++++++++++++++++++=====>----------------------------------------------------------] ",
348736 },
349737 {
738 style: BarStyle(),
350739 name: "t,c,r{100,40,33}trim",
351740 total: 100,
352741 current: 40,
370759 refill: 33,
371760 trim: true,
372761 want: "[-----------------------------------------------------------<======++++++++++++++++++++++++++++++++]",
373 },
374 },
375 197: {
376 {
377 name: "t,c,r{97486999,2805950,2805483}trim",
378 total: 97486999,
379 current: 2805950,
380 refill: 2805483,
381 barWidth: 60,
382 trim: true,
383 want: "[+>--------------------------------------------------------]",
384762 },
385763 },
386764 }
388766 var tmpBuf bytes.Buffer
389767 for tw, cases := range testSuite {
390768 for _, tc := range cases {
391 if tc.style == nil {
392 tc.style = BarStyle()
393 }
394 s := newTestState(NewBarFiller(tc.style))
769 s := newTestState(tc.style.Build())
395770 s.reqWidth = tc.barWidth
396771 s.total = tc.total
397772 s.current = tc.current
398773 s.trimSpace = tc.trim
399774 s.refill = tc.refill
400775 tmpBuf.Reset()
401 tmpBuf.ReadFrom(s.draw(newStatistics(tw, s)))
776 _, err := tmpBuf.ReadFrom(s.draw(newStatistics(tw, s)))
777 if err != nil {
778 t.FailNow()
779 }
780 by := tmpBuf.Bytes()
781
782 got := string(by[:len(by)-1])
783 if !utf8.ValidString(got) {
784 t.Fail()
785 }
786 if got != tc.want {
787 t.Errorf("termWidth:%d %q want: %q %d, got: %q %d\n", tw, tc.name, tc.want, utf8.RuneCountInString(tc.want), got, utf8.RuneCountInString(got))
788 }
789 }
790 }
791 }
792
793 func TestDrawTipOnComplete(t *testing.T) {
794 // key is termWidth
795 testSuite := map[int][]struct {
796 style BarStyleComposer
797 name string
798 total int64
799 current int64
800 refill int64
801 barWidth int
802 trim bool
803 want string
804 }{
805 3: {
806 {
807 style: BarStyle().TipOnComplete(">"),
808 name: "t,c{60,60}",
809 total: 60,
810 current: 60,
811 want: " ",
812 },
813 {
814 style: BarStyle().TipOnComplete(">"),
815 name: "t,c{60,60}trim",
816 total: 60,
817 current: 60,
818 trim: true,
819 want: "[>]",
820 },
821 },
822 4: {
823 {
824 style: BarStyle().TipOnComplete(">"),
825 name: "t,c{60,59}",
826 total: 60,
827 current: 59,
828 want: " [] ",
829 },
830 {
831 style: BarStyle().TipOnComplete(">"),
832 name: "t,c{60,59}trim",
833 total: 60,
834 current: 59,
835 trim: true,
836 want: "[=>]",
837 },
838 {
839 style: BarStyle().TipOnComplete(">"),
840 name: "t,c{60,60}",
841 total: 60,
842 current: 60,
843 want: " [] ",
844 },
845 {
846 style: BarStyle().TipOnComplete(">"),
847 name: "t,c{60,60}trim",
848 total: 60,
849 current: 60,
850 trim: true,
851 want: "[=>]",
852 },
853 },
854 5: {
855 {
856 style: BarStyle().TipOnComplete(">"),
857 name: "t,c{60,59}",
858 total: 60,
859 current: 59,
860 want: " [>] ",
861 },
862 {
863 style: BarStyle().TipOnComplete(">"),
864 name: "t,c{60,59}trim",
865 total: 60,
866 current: 59,
867 trim: true,
868 want: "[==>]",
869 },
870 {
871 style: BarStyle().TipOnComplete(">"),
872 name: "t,c{60,60}",
873 total: 60,
874 current: 60,
875 want: " [>] ",
876 },
877 {
878 style: BarStyle().TipOnComplete(">"),
879 name: "t,c{60,60}trim",
880 total: 60,
881 current: 60,
882 trim: true,
883 want: "[==>]",
884 },
885 },
886 6: {
887 {
888 style: BarStyle().TipOnComplete(">"),
889 name: "t,c{60,59}",
890 total: 60,
891 current: 59,
892 want: " [=>] ",
893 },
894 {
895 style: BarStyle().TipOnComplete(">"),
896 name: "t,c{60,59}trim",
897 total: 60,
898 current: 59,
899 trim: true,
900 want: "[===>]",
901 },
902 {
903 style: BarStyle().TipOnComplete(">"),
904 name: "t,c{60,60}",
905 total: 60,
906 current: 60,
907 want: " [=>] ",
908 },
909 {
910 style: BarStyle().TipOnComplete(">"),
911 name: "t,c{60,60}trim",
912 total: 60,
913 current: 60,
914 trim: true,
915 want: "[===>]",
916 },
917 },
918 7: {
919 {
920 style: BarStyle().TipOnComplete(">"),
921 name: "t,c{60,59}",
922 total: 60,
923 current: 59,
924 want: " [==>] ",
925 },
926 {
927 style: BarStyle().TipOnComplete(">"),
928 name: "t,c{60,59}trim",
929 total: 60,
930 current: 59,
931 trim: true,
932 want: "[====>]",
933 },
934 {
935 style: BarStyle().TipOnComplete(">"),
936 name: "t,c{60,60}",
937 total: 60,
938 current: 60,
939 want: " [==>] ",
940 },
941 {
942 style: BarStyle().TipOnComplete(">"),
943 name: "t,c{60,60}trim",
944 total: 60,
945 current: 60,
946 trim: true,
947 want: "[====>]",
948 },
949 },
950 8: {
951 {
952 style: BarStyle().TipOnComplete(">"),
953 name: "t,c{60,59}",
954 total: 60,
955 current: 59,
956 want: " [===>] ",
957 },
958 {
959 style: BarStyle().TipOnComplete(">"),
960 name: "t,c{60,59}trim",
961 total: 60,
962 current: 59,
963 trim: true,
964 want: "[=====>]",
965 },
966 {
967 style: BarStyle().TipOnComplete(">"),
968 name: "t,c{60,60}",
969 total: 60,
970 current: 60,
971 want: " [===>] ",
972 },
973 {
974 style: BarStyle().TipOnComplete(">"),
975 name: "t,c{60,60}trim",
976 total: 60,
977 current: 60,
978 trim: true,
979 want: "[=====>]",
980 },
981 },
982 80: {
983 {
984 style: BarStyle().TipOnComplete(">"),
985 name: "t,c{60,59}",
986 total: 60,
987 current: 59,
988 want: " [==========================================================================>-] ",
989 },
990 {
991 style: BarStyle().TipOnComplete(">"),
992 name: "t,c{60,59}trim",
993 total: 60,
994 current: 59,
995 trim: true,
996 want: "[============================================================================>-]",
997 },
998 {
999 style: BarStyle().TipOnComplete(">"),
1000 name: "t,c,bw{60,59,60}",
1001 total: 60,
1002 current: 59,
1003 barWidth: 60,
1004 want: " [========================================================>-] ",
1005 },
1006 {
1007 style: BarStyle().TipOnComplete(">"),
1008 name: "t,c,bw{60,59,60}trim",
1009 total: 60,
1010 current: 59,
1011 barWidth: 60,
1012 trim: true,
1013 want: "[========================================================>-]",
1014 },
1015 {
1016 style: BarStyle().TipOnComplete(">"),
1017 name: "t,c{60,60}",
1018 total: 60,
1019 current: 60,
1020 want: " [===========================================================================>] ",
1021 },
1022 {
1023 style: BarStyle().TipOnComplete(">"),
1024 name: "t,c{60,60}trim",
1025 total: 60,
1026 current: 60,
1027 trim: true,
1028 want: "[=============================================================================>]",
1029 },
1030 {
1031 style: BarStyle().TipOnComplete(">"),
1032 name: "t,c,bw{60,60,60}",
1033 total: 60,
1034 current: 60,
1035 barWidth: 60,
1036 want: " [=========================================================>] ",
1037 },
1038 {
1039 style: BarStyle().TipOnComplete(">"),
1040 name: "t,c,bw{60,60,60}trim",
1041 total: 60,
1042 current: 60,
1043 barWidth: 60,
1044 trim: true,
1045 want: "[=========================================================>]",
1046 },
1047 },
1048 99: {
1049 {
1050 style: BarStyle().TipOnComplete(">"),
1051 name: "t,c{100,99}",
1052 total: 100,
1053 current: 99,
1054 want: " [=============================================================================================>-] ",
1055 },
1056 {
1057 style: BarStyle().TipOnComplete(">"),
1058 name: "t,c{100,99}trim",
1059 total: 100,
1060 current: 99,
1061 trim: true,
1062 want: "[===============================================================================================>-]",
1063 },
1064 {
1065 style: BarStyle().TipOnComplete(">"),
1066 name: "t,c{100,100}",
1067 total: 100,
1068 current: 100,
1069 want: " [==============================================================================================>] ",
1070 },
1071 {
1072 style: BarStyle().TipOnComplete(">"),
1073 name: "t,c{100,100}trim",
1074 total: 100,
1075 current: 100,
1076 trim: true,
1077 want: "[================================================================================================>]",
1078 },
1079 {
1080 style: BarStyle().TipOnComplete(">"),
1081 name: "t,c,r{100,100,99}",
1082 total: 100,
1083 current: 100,
1084 refill: 99,
1085 want: " [++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>] ",
1086 },
1087 {
1088 style: BarStyle().TipOnComplete(">"),
1089 name: "t,c,r{100,100,99}trim",
1090 total: 100,
1091 current: 100,
1092 refill: 99,
1093 trim: true,
1094 want: "[++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>]",
1095 },
1096 {
1097 style: BarStyle().TipOnComplete("<").Reverse(),
1098 name: `t,c,r{100,100,99}TipOnComplete("<").Reverse()`,
1099 total: 100,
1100 current: 100,
1101 refill: 99,
1102 want: " [<++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++] ",
1103 },
1104 {
1105 style: BarStyle().TipOnComplete("<").Reverse(),
1106 name: `t,c,r{100,100,99}TipOnComplete("<").Reverse()trim`,
1107 total: 100,
1108 current: 100,
1109 refill: 99,
1110 trim: true,
1111 want: "[<++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]",
1112 },
1113 {
1114 style: BarStyle().TipOnComplete(">"),
1115 name: "t,c,r{100,100,100}",
1116 total: 100,
1117 current: 100,
1118 refill: 100,
1119 want: " [++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>] ",
1120 },
1121 {
1122 style: BarStyle().TipOnComplete(">"),
1123 name: "t,c,r{100,100,100}trim",
1124 total: 100,
1125 current: 100,
1126 refill: 100,
1127 trim: true,
1128 want: "[++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>]",
1129 },
1130 {
1131 style: BarStyle().TipOnComplete("<").Reverse(),
1132 name: `t,c,r{100,100,100}TipOnComplete("<").Reverse()`,
1133 total: 100,
1134 current: 100,
1135 refill: 100,
1136 want: " [<++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++] ",
1137 },
1138 {
1139 style: BarStyle().TipOnComplete("<").Reverse(),
1140 name: `t,c,r{100,100,100}TipOnComplete("<").Reverse()trim`,
1141 total: 100,
1142 current: 100,
1143 refill: 100,
1144 trim: true,
1145 want: "[<++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]",
1146 },
1147 },
1148 100: {
1149 {
1150 style: BarStyle().TipOnComplete(">"),
1151 name: "t,c{100,99}",
1152 total: 100,
1153 current: 99,
1154 want: " [==============================================================================================>-] ",
1155 },
1156 {
1157 style: BarStyle().TipOnComplete(">"),
1158 name: "t,c{100,99}trim",
1159 total: 100,
1160 current: 99,
1161 trim: true,
1162 want: "[================================================================================================>-]",
1163 },
1164 {
1165 style: BarStyle().TipOnComplete(">"),
1166 name: "t,c{100,100}",
1167 total: 100,
1168 current: 100,
1169 want: " [===============================================================================================>] ",
1170 },
1171 {
1172 style: BarStyle().TipOnComplete(">"),
1173 name: "t,c{100,100}trim",
1174 total: 100,
1175 current: 100,
1176 trim: true,
1177 want: "[=================================================================================================>]",
1178 },
1179 {
1180 style: BarStyle().TipOnComplete(">"),
1181 name: "t,c,r{100,100,99}",
1182 total: 100,
1183 current: 100,
1184 refill: 99,
1185 want: " [+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>] ",
1186 },
1187 {
1188 style: BarStyle().TipOnComplete(">"),
1189 name: "t,c,r{100,100,99}trim",
1190 total: 100,
1191 current: 100,
1192 refill: 99,
1193 trim: true,
1194 want: "[+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>]",
1195 },
1196 {
1197 style: BarStyle().TipOnComplete(">"),
1198 name: "t,c,r{100,100,100}",
1199 total: 100,
1200 current: 100,
1201 refill: 100,
1202 want: " [+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>] ",
1203 },
1204 {
1205 style: BarStyle().TipOnComplete(">"),
1206 name: "t,c,r{100,100,100}trim",
1207 total: 100,
1208 current: 100,
1209 refill: 100,
1210 trim: true,
1211 want: "[+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>]",
1212 },
1213 },
1214 }
1215
1216 var tmpBuf bytes.Buffer
1217 for tw, cases := range testSuite {
1218 for _, tc := range cases {
1219 s := newTestState(tc.style.Build())
1220 s.reqWidth = tc.barWidth
1221 s.total = tc.total
1222 s.current = tc.current
1223 s.trimSpace = tc.trim
1224 s.refill = tc.refill
1225 tmpBuf.Reset()
1226 _, err := tmpBuf.ReadFrom(s.draw(newStatistics(tw, s)))
1227 if err != nil {
1228 t.FailNow()
1229 }
1230 by := tmpBuf.Bytes()
1231
1232 got := string(by[:len(by)-1])
1233 if !utf8.ValidString(got) {
1234 t.Fail()
1235 }
1236 if got != tc.want {
1237 t.Errorf("termWidth:%d %q want: %q %d, got: %q %d\n", tw, tc.name, tc.want, utf8.RuneCountInString(tc.want), got, utf8.RuneCountInString(got))
1238 }
1239 }
1240 }
1241 }
1242
1243 func TestDrawDoubleWidth(t *testing.T) {
1244 // key is termWidth
1245 testSuite := map[int][]struct {
1246 style BarStyleComposer
1247 name string
1248 total int64
1249 current int64
1250 refill int64
1251 barWidth int
1252 trim bool
1253 want string
1254 }{
1255 99: {
1256 {
1257 style: BarStyle().Lbound("の").Rbound("の"),
1258 name: `t,c{100,1}.Lbound("の").Rbound("の")`,
1259 total: 100,
1260 current: 1,
1261 want: " の>--------------------------------------------------------------------------------------------の ",
1262 },
1263 {
1264 style: BarStyle().Lbound("の").Rbound("の"),
1265 name: `t,c{100,1}.Lbound("の").Rbound("の")`,
1266 total: 100,
1267 current: 2,
1268 want: " の=>-------------------------------------------------------------------------------------------の ",
1269 },
1270 {
1271 style: BarStyle().Tip("だ"),
1272 name: `t,c{100,1}Tip("だ")`,
1273 total: 100,
1274 current: 1,
1275 want: " [だ---------------------------------------------------------------------------------------------] ",
1276 },
1277 {
1278 style: BarStyle().Tip("だ"),
1279 name: `t,c{100,2}Tip("だ")`,
1280 total: 100,
1281 current: 2,
1282 want: " [だ---------------------------------------------------------------------------------------------] ",
1283 },
1284 {
1285 style: BarStyle().Tip("だ"),
1286 name: `t,c{100,3}Tip("だ")`,
1287 total: 100,
1288 current: 3,
1289 want: " [=だ--------------------------------------------------------------------------------------------] ",
1290 },
1291 {
1292 style: BarStyle().Tip("だ"),
1293 name: `t,c{100,99}Tip("だ")`,
1294 total: 100,
1295 current: 99,
1296 want: " [============================================================================================だ-] ",
1297 },
1298 {
1299 style: BarStyle().Tip("だ"),
1300 name: `t,c{100,100}Tip("だ")`,
1301 total: 100,
1302 current: 100,
1303 want: " [===============================================================================================] ",
1304 },
1305 {
1306 style: BarStyle().TipOnComplete("だ"),
1307 name: `t,c{100,100}TipOnComplete("だ")`,
1308 total: 100,
1309 current: 100,
1310 want: " [=============================================================================================だ] ",
1311 },
1312 {
1313 style: BarStyle().Filler("の").Tip("だ").Padding("つ"),
1314 name: `t,c{100,1}Filler("の").Tip("だ").Padding("つ")`,
1315 total: 100,
1316 current: 1,
1317 want: " [だつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつ…] ",
1318 },
1319 {
1320 style: BarStyle().Filler("の").Tip("だ").Padding("つ"),
1321 name: `t,c{100,2}Filler("の").Tip("だ").Padding("つ")`,
1322 total: 100,
1323 current: 2,
1324 want: " [だつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつつ…] ",
1325 },
1326 {
1327 style: BarStyle().Filler("の").Tip("だ").Padding("つ"),
1328 name: `t,c{100,99}Filler("の").Tip("だ").Padding("つ")`,
1329 total: 100,
1330 current: 99,
1331 want: " [ののののののののののののののののののののののののののののののののののののののののののののののだ…] ",
1332 },
1333 {
1334 style: BarStyle().Filler("の").Tip("だ").Padding("つ"),
1335 name: `t,c{100,100}.Filler("の").Tip("だ").Padding("つ")`,
1336 total: 100,
1337 current: 100,
1338 want: " […ののののののののののののののののののののののののののののののののののののののののののののののの] ",
1339 },
1340 {
1341 style: BarStyle().Filler("の").Tip("だ").Padding("つ").Reverse(),
1342 name: `t,c{100,100}Filler("の").Tip("だ").Padding("つ").Reverse()`,
1343 total: 100,
1344 current: 100,
1345 want: " [ののののののののののののののののののののののののののののののののののののののののののののののの…] ",
1346 },
1347 {
1348 style: BarStyle().Filler("の").Tip("だ").TipOnComplete("だ").Padding("つ"),
1349 name: `t,c{100,99}Filler("の").Tip("だ").TipOnComplete("だ").Padding("つ")`,
1350 total: 100,
1351 current: 99,
1352 want: " [ののののののののののののののののののののののののののののののののののののののののののののののだ…] ",
1353 },
1354 {
1355 style: BarStyle().Filler("の").Tip("だ").TipOnComplete("だ").Padding("つ"),
1356 name: `t,c{100,100}.Filler("の").Tip("だ").TipOnComplete("だ").Padding("つ")`,
1357 total: 100,
1358 current: 100,
1359 want: " […ののののののののののののののののののののののののののののののののののののののののののののののだ] ",
1360 },
1361 {
1362 style: BarStyle().Filler("の").Tip("だ").TipOnComplete("だ").Padding("つ").Reverse(),
1363 name: `t,c{100,100}.Filler("の").Tip("だ").TipOnComplete("だ").Padding("つ").Reverse()`,
1364 total: 100,
1365 current: 100,
1366 want: " [だのののののののののののののののののののののののののののののののののののののののののののののの…] ",
1367 },
1368 {
1369 style: BarStyle().Refiller("の"),
1370 name: `t,c,r{100,100,99}Refiller("の")`,
1371 total: 100,
1372 current: 100,
1373 refill: 99,
1374 want: " [ののののののののののののののののののののののののののののののののののののののののののののののの=] ",
1375 },
1376 {
1377 style: BarStyle().Refiller("の"),
1378 name: `t,c,r{100,100,99}Refiller("の")trim`,
1379 total: 100,
1380 current: 100,
1381 refill: 99,
1382 trim: true,
1383 want: "[のののののののののののののののののののののののののののののののののののののののののののののののの=]",
1384 },
1385 },
1386 }
1387
1388 var tmpBuf bytes.Buffer
1389 for tw, cases := range testSuite {
1390 for _, tc := range cases {
1391 s := newTestState(tc.style.Build())
1392 s.reqWidth = tc.barWidth
1393 s.total = tc.total
1394 s.current = tc.current
1395 s.trimSpace = tc.trim
1396 s.refill = tc.refill
1397 tmpBuf.Reset()
1398 _, err := tmpBuf.ReadFrom(s.draw(newStatistics(tw, s)))
1399 if err != nil {
1400 t.FailNow()
1401 }
4021402 by := tmpBuf.Bytes()
4031403
4041404 got := string(by[:len(by)-1])
4131413 }
4141414
4151415 func newTestState(filler BarFiller) *bState {
416 s := &bState{
1416 bs := &bState{
4171417 filler: filler,
418 bufP: new(bytes.Buffer),
419 bufB: new(bytes.Buffer),
420 bufA: new(bytes.Buffer),
4211418 }
422 return s
1419 for i := 0; i < len(bs.buffers); i++ {
1420 bs.buffers[i] = bytes.NewBuffer(make([]byte, 0, 512))
1421 }
1422 return bs
4231423 }
1616
1717 total := 100
1818 name := "Single Bar:"
19 // adding a single bar, which will inherit container's width
20 bar := p.Add(int64(total),
21 // progress bar filler with customized style
22 mpb.NewBarFiller(mpb.BarStyle().Lbound("╢").Filler("▌").Tip("▌").Padding("░").Rbound("╟")),
19 // create a single bar, which will inherit container's width
20 bar := p.New(int64(total),
21 // BarFillerBuilder with custom style
22 mpb.BarStyle().Lbound("╢").Filler("▌").Tip("▌").Padding("░").Rbound("╟"),
2323 mpb.PrependDecorators(
2424 // display our name with one space on the right
2525 decor.Name(name, decor.WC{W: len(name) + 1, C: decor.DidentRight}),
7777 defer proxyReader.Close()
7878
7979 // and copy from reader, ignoring errors
80 io.Copy(ioutil.Discard, proxyReader)
80 _, _ = io.Copy(ioutil.Discard, proxyReader)
8181
8282 p.Wait()
8383 }
33 github.com/VividCortex/ewma v1.2.0
44 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
55 github.com/mattn/go-runewidth v0.0.13
6 golang.org/x/sys v0.0.0-20210616094352-59db8d763f22
6 golang.org/x/sys v0.0.0-20220114195835-da31bd327af9
77 )
88
99 go 1.14
55 github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
66 github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
77 github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
8 golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio=
9 golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
8 golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0=
9 golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
22 import "math"
33
44 // Percentage is a helper function, to calculate percentage.
5 func Percentage(total, current int64, width int) float64 {
5 func Percentage(total, current int64, width uint) float64 {
66 if total <= 0 {
77 return 0
88 }
1313 }
1414
1515 // PercentageRound same as Percentage but with math.Round.
16 func PercentageRound(total, current int64, width int) float64 {
16 func PercentageRound(total, current int64, width uint) float64 {
1717 return math.Round(Percentage(total, current, width))
1818 }
33
44 func TestPercentage(t *testing.T) {
55 // key is barWidth
6 testSuite := map[int][]struct {
6 testSuite := map[uint][]struct {
77 name string
88 total int64
99 current int64
+0
-6
internal/predicate.go less more
0 package internal
1
2 // Predicate helper for internal use.
3 func Predicate(pick bool) func() bool {
4 return func() bool { return pick }
5 }
55 "context"
66 "fmt"
77 "io"
8 "io/ioutil"
9 "log"
108 "math"
119 "os"
1210 "sync"
1816
1917 const (
2018 // default RefreshRate
21 prr = 120 * time.Millisecond
19 prr = 150 * time.Millisecond
2220 )
2321
2422 // Progress represents a container that renders one or more progress
3230 done chan struct{}
3331 refreshCh chan time.Time
3432 once sync.Once
35 dlogger *log.Logger
3633 }
3734
3835 // pState holds bars in its priorityQueue. It gets passed to
7471 rr: prr,
7572 parkedBars: make(map[*Bar]*Bar),
7673 output: os.Stdout,
77 debugOut: ioutil.Discard,
7874 }
7975
8076 for _, opt := range options {
9086 bwg: new(sync.WaitGroup),
9187 operateState: make(chan func(*pState)),
9288 done: make(chan struct{}),
93 dlogger: log.New(s.debugOut, "[mpb] ", log.Lshortfile),
9489 }
9590
9691 p.cwg.Add(1)
9893 return p
9994 }
10095
101 // AddBar creates a bar with default bar filler. Different filler can
102 // be chosen and applied via `*Progress.Add(...) *Bar` method.
96 // AddBar creates a bar with default bar filler.
10397 func (p *Progress) AddBar(total int64, options ...BarOption) *Bar {
104 return p.Add(total, NewBarFiller(BarStyle()), options...)
105 }
106
107 // AddSpinner creates a bar with default spinner filler. Different
108 // filler can be chosen and applied via `*Progress.Add(...) *Bar`
109 // method.
98 return p.New(total, BarStyle(), options...)
99 }
100
101 // AddSpinner creates a bar with default spinner filler.
110102 func (p *Progress) AddSpinner(total int64, options ...BarOption) *Bar {
111 return p.Add(total, NewBarFiller(SpinnerStyle()), options...)
103 return p.New(total, SpinnerStyle(), options...)
104 }
105
106 // New creates a bar with provided BarFillerBuilder.
107 func (p *Progress) New(total int64, builder BarFillerBuilder, options ...BarOption) *Bar {
108 return p.Add(total, builder.Build(), options...)
112109 }
113110
114111 // Add creates a bar which renders itself by provided filler.
116113 // Panics if *Progress instance is done, i.e. called after *Progress.Wait().
117114 func (p *Progress) Add(total int64, filler BarFiller, options ...BarOption) *Bar {
118115 if filler == nil {
119 filler = BarFillerFunc(func(io.Writer, int, decor.Statistics) {})
116 filler = NopStyle().Build()
120117 }
121118 p.bwg.Add(1)
122119 result := make(chan *Bar)
156153 }
157154 }
158155
159 func (p *Progress) setBarPriority(b *Bar, priority int) {
156 func (p *Progress) traverseBars(cb func(b *Bar) bool) {
157 done := make(chan struct{})
158 select {
159 case p.operateState <- func(s *pState) {
160 for i := 0; i < s.bHeap.Len(); i++ {
161 bar := s.bHeap[i]
162 if !cb(bar) {
163 break
164 }
165 }
166 close(done)
167 }:
168 <-done
169 case <-p.done:
170 }
171 }
172
173 // UpdateBarPriority same as *Bar.SetPriority(int).
174 func (p *Progress) UpdateBarPriority(b *Bar, priority int) {
160175 select {
161176 case p.operateState <- func(s *pState) {
162177 if b.index < 0 {
169184 }
170185 }
171186
172 // UpdateBarPriority same as *Bar.SetPriority(int).
173 func (p *Progress) UpdateBarPriority(b *Bar, priority int) {
174 p.setBarPriority(b, priority)
175 }
176
177187 // BarCount returns bars count.
178188 func (p *Progress) BarCount() int {
179 result := make(chan int, 1)
189 result := make(chan int)
180190 select {
181191 case p.operateState <- func(s *pState) { result <- s.bHeap.Len() }:
182192 return <-result
218228 op(s)
219229 case <-p.refreshCh:
220230 if err := s.render(cw); err != nil {
221 p.dlogger.Println(err)
231 if s.debugOut != nil {
232 _, e := fmt.Fprintln(s.debugOut, err)
233 if e != nil {
234 panic(err)
235 }
236 } else {
237 panic(err)
238 }
222239 }
223240 case <-s.shutdownNotifier:
224 if s.heapUpdated {
241 for s.heapUpdated {
225242 if err := s.render(cw); err != nil {
226 p.dlogger.Println(err)
243 if s.debugOut != nil {
244 _, e := fmt.Fprintln(s.debugOut, err)
245 if e != nil {
246 panic(err)
247 }
248 } else {
249 panic(err)
250 }
227251 }
228252 }
229253 return
290314 }
291315
292316 func (s *pState) flush(cw *cwriter.Writer) error {
293 var lineCount int
294 bm := make(map[*Bar]struct{}, s.bHeap.Len())
317 var totalLines int
318 bm := make(map[*Bar]int, s.bHeap.Len())
295319 for s.bHeap.Len() > 0 {
296320 b := heap.Pop(&s.bHeap).(*Bar)
297 cw.ReadFrom(<-b.frameCh)
321 frame := <-b.frameCh
322 _, err := cw.ReadFrom(frame.reader)
323 if err != nil {
324 return err
325 }
298326 if b.toShutdown {
299327 if b.recoveredPanic != nil {
300328 s.barShutdownQueue = append(s.barShutdownQueue, b)
307335 }()
308336 }
309337 }
310 lineCount += b.extendedLines + 1
311 bm[b] = struct{}{}
338 bm[b] = frame.lines
339 totalLines += frame.lines
312340 }
313341
314342 for _, b := range s.barShutdownQueue {
319347 b.toDrop = true
320348 }
321349 if s.popCompleted && !b.noPop {
322 lineCount -= b.extendedLines + 1
350 totalLines -= bm[b]
323351 b.toDrop = true
324352 }
325353 if b.toDrop {
334362 heap.Push(&s.bHeap, b)
335363 }
336364
337 return cw.Flush(lineCount)
365 return cw.Flush(totalLines)
338366 }
339367
340368 func (s *pState) updateSyncMatrix() {
385413 bs.priority = -(math.MaxInt32 - s.idCount)
386414 }
387415
388 bs.bufP = bytes.NewBuffer(make([]byte, 0, 128))
389 bs.bufB = bytes.NewBuffer(make([]byte, 0, 256))
390 bs.bufA = bytes.NewBuffer(make([]byte, 0, 128))
416 for i := 0; i < len(bs.buffers); i++ {
417 bs.buffers[i] = bytes.NewBuffer(make([]byte, 0, 512))
418 }
391419
392420 return bs
393421 }
6868 wg.Wait()
6969 count := p.BarCount()
7070 if count != 2 {
71 t.Errorf("BarCount want: %q, got: %q\n", 2, count)
71 t.Errorf("BarCount want: %d, got: %d\n", 2, count)
7272 }
7373 bars[1].Abort(true)
7474 bars[2].Abort(true)
7676 }
7777
7878 func TestWithContext(t *testing.T) {
79 shutdown := make(chan struct{})
7980 ctx, cancel := context.WithCancel(context.Background())
80 shutdown := make(chan struct{})
81 p := mpb.NewWithContext(ctx,
82 mpb.WithOutput(ioutil.Discard),
83 mpb.WithRefreshRate(50*time.Millisecond),
84 mpb.WithShutdownNotifier(shutdown),
85 )
81 p := mpb.NewWithContext(ctx, mpb.WithShutdownNotifier(shutdown), mpb.WithOutput(ioutil.Discard))
8682
87 total := 10000
88 numBars := 3
89 bars := make([]*mpb.Bar, 0, numBars)
90 for i := 0; i < numBars; i++ {
91 bar := p.AddBar(int64(total))
92 bars = append(bars, bar)
93 go func() {
94 for !bar.Completed() {
95 bar.Increment()
96 time.Sleep(randomDuration(100 * time.Millisecond))
97 }
98 }()
99 }
83 start := make(chan struct{})
84 done := make(chan struct{})
85 fail := make(chan struct{})
86 bar := p.AddBar(0) // never complete bar
87 go func() {
88 close(start)
89 for !bar.Completed() {
90 bar.Increment()
91 time.Sleep(randomDuration(100 * time.Millisecond))
92 }
93 close(done)
94 }()
10095
101 time.Sleep(50 * time.Millisecond)
96 go func() {
97 select {
98 case <-done:
99 p.Wait()
100 case <-time.After(150 * time.Millisecond):
101 close(fail)
102 }
103 }()
104
105 <-start
102106 cancel()
103
104 p.Wait()
105107 select {
106108 case <-shutdown:
107 case <-time.After(100 * time.Millisecond):
108 t.Error("Progress didn't stop")
109 case <-fail:
110 t.Error("Progress didn't shutdown")
109111 }
110112 }
111113
126128 *mpb.MaxWidthDistributor = makeWrapper(*mpb.MaxWidthDistributor, start, end)
127129
128130 total := 80
129 numBars := 3
131 numBars := 6
130132 p := mpb.New(mpb.WithOutput(ioutil.Discard))
131133 for i := 0; i < numBars; i++ {
132134 bar := p.AddBar(int64(total),
140142 rng := rand.New(rand.NewSource(time.Now().UnixNano()))
141143 for i := 0; i < total; i++ {
142144 start := time.Now()
143 if bar.ID() >= numBars-1 && i >= 42 {
144 bar.Abort(true)
145 if id := bar.ID(); id > 1 && i >= 42 {
146 if id&1 == 1 {
147 bar.Abort(true)
148 } else {
149 bar.Abort(false)
150 }
145151 }
146 time.Sleep((time.Duration(rng.Intn(10)+1) * (10 * time.Millisecond)) / 2)
147 bar.Increment()
152 time.Sleep((time.Duration(rng.Intn(10)+1) * (50 * time.Millisecond)) / 2)
153 bar.IncrInt64(rand.Int63n(5) + 1)
148154 bar.DecoratorEwmaUpdate(time.Since(start))
149155 }
150156 }()
1010 bar *Bar
1111 }
1212
13 func (x *proxyReader) Read(p []byte) (int, error) {
13 func (x proxyReader) Read(p []byte) (int, error) {
1414 n, err := x.ReadCloser.Read(p)
1515 x.bar.IncrBy(n)
1616 if err == io.EOF {
17 go x.bar.SetTotal(0, true)
17 go x.bar.SetTotal(-1, true)
1818 }
1919 return n, err
2020 }
2121
2222 type proxyWriterTo struct {
23 io.ReadCloser // *proxyReader
24 wt io.WriterTo
25 bar *Bar
23 proxyReader
24 wt io.WriterTo
2625 }
2726
28 func (x *proxyWriterTo) WriteTo(w io.Writer) (int64, error) {
27 func (x proxyWriterTo) WriteTo(w io.Writer) (int64, error) {
2928 n, err := x.wt.WriteTo(w)
3029 x.bar.IncrInt64(n)
3130 if err == io.EOF {
32 go x.bar.SetTotal(0, true)
31 go x.bar.SetTotal(-1, true)
3332 }
3433 return n, err
3534 }
3635
3736 type ewmaProxyReader struct {
38 io.ReadCloser // *proxyReader
39 bar *Bar
40 iT time.Time
37 proxyReader
4138 }
4239
43 func (x *ewmaProxyReader) Read(p []byte) (int, error) {
44 n, err := x.ReadCloser.Read(p)
40 func (x ewmaProxyReader) Read(p []byte) (int, error) {
41 start := time.Now()
42 n, err := x.proxyReader.Read(p)
4543 if n > 0 {
46 x.bar.DecoratorEwmaUpdate(time.Since(x.iT))
47 x.iT = time.Now()
44 x.bar.DecoratorEwmaUpdate(time.Since(start))
4845 }
4946 return n, err
5047 }
5148
5249 type ewmaProxyWriterTo struct {
53 io.ReadCloser // *ewmaProxyReader
54 wt io.WriterTo // *proxyWriterTo
55 bar *Bar
56 iT time.Time
50 ewmaProxyReader
51 wt proxyWriterTo
5752 }
5853
59 func (x *ewmaProxyWriterTo) WriteTo(w io.Writer) (int64, error) {
54 func (x ewmaProxyWriterTo) WriteTo(w io.Writer) (int64, error) {
55 start := time.Now()
6056 n, err := x.wt.WriteTo(w)
6157 if n > 0 {
62 x.bar.DecoratorEwmaUpdate(time.Since(x.iT))
63 x.iT = time.Now()
58 x.bar.DecoratorEwmaUpdate(time.Since(start))
6459 }
6560 return n, err
6661 }
6762
68 func newProxyReader(r io.Reader, bar *Bar) io.ReadCloser {
69 rc := toReadCloser(r)
70 rc = &proxyReader{rc, bar}
71
72 if wt, isWriterTo := r.(io.WriterTo); bar.hasEwmaDecorators {
73 now := time.Now()
74 rc = &ewmaProxyReader{rc, bar, now}
75 if isWriterTo {
76 rc = &ewmaProxyWriterTo{rc, wt, bar, now}
63 func (b *Bar) newProxyReader(r io.Reader) (rc io.ReadCloser) {
64 pr := proxyReader{toReadCloser(r), b}
65 if wt, ok := r.(io.WriterTo); ok {
66 pw := proxyWriterTo{pr, wt}
67 if b.hasEwmaDecorators {
68 rc = ewmaProxyWriterTo{ewmaProxyReader{pr}, pw}
69 } else {
70 rc = pw
7771 }
78 } else if isWriterTo {
79 rc = &proxyWriterTo{rc, wt, bar}
72 } else if b.hasEwmaDecorators {
73 rc = ewmaProxyReader{pr}
74 } else {
75 rc = pr
8076 }
8177 return rc
8278 }
6666 p := mpb.New(mpb.WithOutput(ioutil.Discard))
6767
6868 var reader io.Reader = strings.NewReader(content)
69 wt := reader.(io.WriterTo)
70 tReader := &testWriterTo{reader, wt, false}
69 tReader := &testWriterTo{reader, reader.(io.WriterTo), false}
7170
7271 bar := p.AddBar(int64(len(content)), mpb.BarFillerTrim())
7372