Codebase list golang-github-vbauerster-mpb / 2a2fa99
use runes to format pb Vladimir Bauer 9 years ago
2 changed file(s) with 131 addition(s) and 96 deletion(s). Raw diff Collapse all Expand all
+113
-91
bar.go less more
77 "unicode/utf8"
88 )
99
10 type formatRunes [5]rune
11
1012 // Bar represents a progress Bar
1113 type Bar struct {
12 fillCh, emptyCh, tipCh, leftEndCh, rightEndCh chan byte
14 formatElementCh chan runeFormatElement
1315
1416 widthCh chan int
1517 etaAlphaCh chan float64
4143 }
4244
4345 type (
46 runeFormatElement struct {
47 char rune
48 index uint8
49 }
4450 refill struct {
45 char byte
51 char rune
4652 till int64
4753 }
4854 state struct {
49 id int
50 fill byte
51 empty byte
52 tip byte
53 leftEnd byte
54 rightEnd byte
55 id int64
56 format formatRunes
5557 etaAlpha float64
5658 barWidth int
5759 total int64
6769 }
6870 )
6971
70 func newBar(ctx context.Context, wg *sync.WaitGroup, total int64, width, id int) *Bar {
72 func newBar(ctx context.Context, wg *sync.WaitGroup, id, total int64, width int, format string) *Bar {
7173 b := &Bar{
72 fillCh: make(chan byte),
73 emptyCh: make(chan byte),
74 tipCh: make(chan byte),
75 leftEndCh: make(chan byte),
76 rightEndCh: make(chan byte),
77 etaAlphaCh: make(chan float64),
78 incrCh: make(chan int64, 1),
79 widthCh: make(chan int),
80 trimLeftCh: make(chan bool),
81 trimRightCh: make(chan bool),
82 refillCh: make(chan *refill),
83 stateReqCh: make(chan chan state, 1),
84 decoratorCh: make(chan *decorator),
85 flushedCh: make(chan struct{}, 1),
86 removeReqCh: make(chan struct{}),
87 completeReqCh: make(chan struct{}),
88 done: make(chan struct{}),
89 }
90 go b.server(ctx, wg, total, width, id)
74 formatElementCh: make(chan runeFormatElement),
75 etaAlphaCh: make(chan float64),
76 incrCh: make(chan int64, 1),
77 widthCh: make(chan int),
78 trimLeftCh: make(chan bool),
79 trimRightCh: make(chan bool),
80 refillCh: make(chan *refill),
81 stateReqCh: make(chan chan state, 1),
82 decoratorCh: make(chan *decorator),
83 flushedCh: make(chan struct{}, 1),
84 removeReqCh: make(chan struct{}),
85 completeReqCh: make(chan struct{}),
86 done: make(chan struct{}),
87 }
88 go b.server(ctx, wg, id, total, width, format)
9189 return b
9290 }
9391
120118
121119 // SetFill sets character representing completed progress.
122120 // Defaults to '='
123 func (b *Bar) SetFill(c byte) *Bar {
124 if IsClosed(b.done) {
125 return b
126 }
127 b.fillCh <- c
121 func (b *Bar) SetFill(r rune) *Bar {
122 if IsClosed(b.done) {
123 return b
124 }
125 b.formatElementCh <- runeFormatElement{r, 1}
128126 return b
129127 }
130128
131129 // SetTip sets character representing tip of progress.
132130 // Defaults to '>'
133 func (b *Bar) SetTip(c byte) *Bar {
134 if IsClosed(b.done) {
135 return b
136 }
137 b.tipCh <- c
131 func (b *Bar) SetTip(r rune) *Bar {
132 if IsClosed(b.done) {
133 return b
134 }
135 b.formatElementCh <- runeFormatElement{r, 2}
138136 return b
139137 }
140138
141139 // SetEmpty sets character representing the empty progress
142140 // Defaults to '-'
143 func (b *Bar) SetEmpty(c byte) *Bar {
144 if IsClosed(b.done) {
145 return b
146 }
147 b.emptyCh <- c
141 func (b *Bar) SetEmpty(r rune) *Bar {
142 if IsClosed(b.done) {
143 return b
144 }
145 b.formatElementCh <- runeFormatElement{r, 3}
148146 return b
149147 }
150148
151149 // SetLeftEnd sets character representing the left most border
152150 // Defaults to '['
153 func (b *Bar) SetLeftEnd(c byte) *Bar {
154 if IsClosed(b.done) {
155 return b
156 }
157 b.leftEndCh <- c
151 func (b *Bar) SetLeftEnd(r rune) *Bar {
152 if IsClosed(b.done) {
153 return b
154 }
155 b.formatElementCh <- runeFormatElement{r, 0}
158156 return b
159157 }
160158
161159 // SetRightEnd sets character representing the right most border
162160 // Defaults to ']'
163 func (b *Bar) SetRightEnd(c byte) *Bar {
164 if IsClosed(b.done) {
165 return b
166 }
167 b.rightEndCh <- c
161 func (b *Bar) SetRightEnd(r rune) *Bar {
162 if IsClosed(b.done) {
163 return b
164 }
165 b.formatElementCh <- runeFormatElement{r, 4}
168166 return b
169167 }
170168
193191 }
194192
195193 // IncrWithReFill increments pb with different fill character
196 func (b *Bar) IncrWithReFill(n int, c byte) {
194 func (b *Bar) IncrWithReFill(n int, r rune) {
197195 if IsClosed(b.done) {
198196 return
199197 }
200198 b.Incr(n)
201 b.refillCh <- &refill{c, int64(n)}
199 b.refillCh <- &refill{r, int64(n)}
202200 }
203201
204202 // GetAppenders returns slice of appender DecoratorFunc
221219 }
222220
223221 // GetID returs id of the bar
224 func (b *Bar) GetID() int {
222 func (b *Bar) GetID() int64 {
225223 state := b.getState()
226224 return state.id
227225 }
290288 return state.draw(termWidth)
291289 }
292290
293 func (b *Bar) server(ctx context.Context, wg *sync.WaitGroup, total int64, width, id int) {
291 func (b *Bar) server(ctx context.Context, wg *sync.WaitGroup, id, total int64, width int, format string) {
294292 var completed bool
295293 timeStarted := time.Now()
296294 blockStartTime := timeStarted
297295 state := state{
298296 id: id,
299 fill: '=',
300 empty: '-',
301 tip: '>',
302 leftEnd: '[',
303 rightEnd: ']',
297 format: formatRunes{'[', '=', '>', '-', ']'},
304298 etaAlpha: 0.25,
305299 barWidth: width,
306300 total: total,
307301 }
308302 if total <= 0 {
309303 state.simpleSpinner = getSpinner()
304 } else if format != "" {
305 for i, n := 0, 0; len(format) > 0; i++ {
306 state.format[i], n = utf8.DecodeRuneInString(format)
307 format = format[n:]
308 }
310309 }
311310 defer func() {
312311 b.stop(&state)
342341 }
343342 case ch := <-b.stateReqCh:
344343 ch <- state
345 case state.fill = <-b.fillCh:
346 case state.empty = <-b.emptyCh:
347 case state.tip = <-b.tipCh:
348 case state.leftEnd = <-b.leftEndCh:
349 case state.rightEnd = <-b.rightEndCh:
344 case e := <-b.formatElementCh:
345 state.format[e.index] = e.char
350346 case state.barWidth = <-b.widthCh:
351347 case state.refill = <-b.refillCh:
352348 case state.trimLeftSpace = <-b.trimLeftCh:
448444 return []byte{}
449445 }
450446
447 // bar width without leftEnd and rightEnd characters
448 barWidth := width - 2
449 formatBytes := s.convertFormatRunesToBytes()
450
451451 if s.simpleSpinner != nil {
452 return []byte{s.leftEnd, s.simpleSpinner(), s.rightEnd}
453 }
454
455 buf := make([]byte, width)
456 completedWidth := percentage(s.total, s.current, width)
452 var buf []byte
453 for _, block := range [...][]byte{formatBytes[0], []byte{s.simpleSpinner()}, formatBytes[4]} {
454 buf = append(buf, block...)
455 }
456 return buf
457 }
458
459 completedWidth := percentage(s.total, s.current, barWidth)
460
461 buf := make([]byte, 0, width)
462 // append leftEnd rune
463 buf = append(buf, formatBytes[0]...)
457464
458465 if s.refill != nil {
459 till := percentage(s.total, s.refill.till, width)
460 for i := 1; i < till; i++ {
461 buf[i] = s.refill.char
462 }
463 for i := till; i < completedWidth; i++ {
464 buf[i] = s.fill
466 till := percentage(s.total, s.refill.till, barWidth)
467 rbytes := make([]byte, utf8.RuneLen(s.refill.char))
468 utf8.EncodeRune(rbytes, s.refill.char)
469 for i := 0; i < till; i++ {
470 buf = append(buf, rbytes...)
471 }
472 for i := till; i < completedWidth-1; i++ {
473 // append fill rune
474 buf = append(buf, formatBytes[1]...)
465475 }
466476 } else {
467 for i := 1; i < completedWidth; i++ {
468 buf[i] = s.fill
469 }
470 }
471
472 for i := completedWidth; i < width-1; i++ {
473 buf[i] = s.empty
477 for i := 0; i < completedWidth-1; i++ {
478 // append fill rune
479 buf = append(buf, formatBytes[1]...)
480 }
474481 }
475482 // set tip bit
476 if completedWidth > 0 && completedWidth < s.barWidth {
477 buf[completedWidth-1] = s.tip
478 }
479 // set left and right ends bits
480 buf[0], buf[width-1] = s.leftEnd, s.rightEnd
481
483 if completedWidth > 0 && completedWidth < barWidth {
484 // buf[completedWidth-1] = s.tip
485 buf = append(buf, formatBytes[2]...)
486 }
487
488 for i := completedWidth + 1; i < barWidth; i++ {
489 // append empty rune
490 buf = append(buf, formatBytes[3]...)
491 }
492 // append rightEnd rune
493 buf = append(buf, formatBytes[4]...)
482494 return buf
495 }
496
497 func (s *state) convertFormatRunesToBytes() [5][]byte {
498 var formatBytes [5][]byte
499 for i, r := range s.format {
500 buf := make([]byte, utf8.RuneLen(r))
501 utf8.EncodeRune(buf, r)
502 formatBytes[i] = buf
503 }
504 return formatBytes
483505 }
484506
485507 func calcTimePerItemEstimate(tpie time.Duration, blockStartTime time.Time, alpha float64, items int64) time.Duration {
77 "os"
88 "sync"
99 "time"
10 "unicode/utf8"
1011
1112 "github.com/vbauerster/mpb/cwriter"
1213 )
5556 // WaitGroup for internal rendering sync
5657 wg *sync.WaitGroup
5758
58 out io.Writer
59 width int
59 out io.Writer
60 width int
61 format string
6062
6163 operationCh chan *operation
6264 rrChangeReqCh chan time.Duration
131133 // AddBar creates a new progress bar and adds to the container
132134 // pancis, if called on stopped Progress instance, i.e after Stop()
133135 func (p *Progress) AddBar(total int64) *Bar {
134 return p.AddBarWithID(total, 0)
136 return p.AddBarWithID(0, total)
135137 }
136138
137139 // AddBarWithID creates a new progress bar and adds to the container
138140 // pancis, if called on stopped Progress instance, i.e after Stop()
139 func (p *Progress) AddBarWithID(total int64, id int) *Bar {
141 func (p *Progress) AddBarWithID(id, total int64) *Bar {
140142 if IsClosed(p.done) {
141143 panic(ErrCallAfterStop)
142144 }
143145 result := make(chan bool)
144 bar := newBar(p.ctx, p.wg, total, p.width, id)
146 bar := newBar(p.ctx, p.wg, id, total, p.width, p.format)
145147 p.operationCh <- &operation{barAdd, bar, result}
146148 if <-result {
147149 p.wg.Add(1)
169171 respCh := make(chan int)
170172 p.barCountReqCh <- respCh
171173 return <-respCh
174 }
175
176 // Format sets custom format for underlying bar(s).
177 // The format string, must consist of ASCII characters only.
178 // The default one is "[=>-]"
179 func (p *Progress) Format(format string) *Progress {
180 if utf8.RuneCountInString(format) != 5 {
181 return p
182 }
183 p.format = format
184 return p
172185 }
173186
174187 // Stop waits for bars to finish rendering and stops the rendering goroutine