Codebase list golang-github-vbauerster-mpb / c1651cf
state with draw behaviour Vladimir Bauer 9 years ago
1 changed file(s) with 103 addition(s) and 61 deletion(s). Raw diff Collapse all Expand all
+103
-61
bar.go less more
1919 leftEnd byte
2020 rightEnd byte
2121
22 incrCh chan int64
23 trimLeftCh chan bool
24 trimRightCh chan bool
25 stateReqCh chan chan state
26 decoratorCh chan *decorator
27 flushedCh chan struct{}
28 removeReqCh chan struct{}
29 done chan struct{}
30
31 refill *reFill
22 incrCh chan int64
23 trimLeftCh chan bool
24 trimRightCh chan bool
25 refillCh chan *refill
26 stateReqCh chan chan state
27 decoratorCh chan *decorator
28 flushedCh chan struct{}
29 removeReqCh chan struct{}
30 completeReqCh chan struct{}
31 done chan struct{}
32
3233 lastState state
3334 }
3435
4546 }
4647
4748 type (
49 refill struct {
50 char byte
51 till int64
52 }
4853 state struct {
4954 total, current int64
5055 timeElapsed, timePerItem time.Duration
56 trimLeftSpace, trimRightSpace bool
5157 appendFuncs, prependFuncs []DecoratorFunc
52 trimLeftSpace, trimRightSpace bool
53 }
54 reFill struct {
55 c byte
56 till int64
58 fill byte
59 empty byte
60 tip byte
61 leftEnd byte
62 rightEnd byte
63 etaAlpha float64
64 width int
65 refill *refill
5766 }
5867 )
5968
6776 etaAlpha: 0.25,
6877 width: width,
6978
70 incrCh: make(chan int64, 1),
71 trimLeftCh: make(chan bool),
72 trimRightCh: make(chan bool),
73 stateReqCh: make(chan chan state, 1),
74 decoratorCh: make(chan *decorator),
75 flushedCh: make(chan struct{}, 1),
76 removeReqCh: make(chan struct{}),
77 done: make(chan struct{}),
79 incrCh: make(chan int64, 1),
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{}),
7889 }
7990 go b.server(ctx, wg, total)
8091 return b
157168
158169 // Incr increments progress bar
159170 func (b *Bar) Incr(n int) {
160 if IsClosed(b.done) {
161 return
162 }
163 if n > 0 {
164 b.incrCh <- int64(n)
165 }
171 if n < 1 || IsClosed(b.done) {
172 return
173 }
174 b.incrCh <- int64(n)
166175 }
167176
168177 // IncrWithReFill increments pb with different fill character
169178 func (b *Bar) IncrWithReFill(n int, c byte) {
170 if n > 0 {
171 b.Incr(n)
172 b.refill = &reFill{c, int64(n)}
173 }
179 if IsClosed(b.done) {
180 return
181 }
182 b.Incr(n)
183 b.refillCh <- &refill{c, int64(n)}
174184 }
175185
176186 // Current returns the actual current.
224234 b.decoratorCh <- &decorator{decAppendZero, nil}
225235 }
226236
237 // Completed signals to the bar, that process has been completed.
238 // You should call this method when total is unknown and you reached the point
239 // of process completion.
240 func (b *Bar) Completed() {
241 if IsClosed(b.done) {
242 return
243 }
244 b.completeReqCh <- struct{}{}
245 }
246
227247 func (b *Bar) bytes(width int) []byte {
228248 if width <= 0 {
229249 width = b.width
230250 }
231251 if IsClosed(b.done) {
232 return b.draw(b.lastState, width)
252 return b.lastState.draw(width)
233253 }
234254 ch := make(chan state, 1)
235255 b.stateReqCh <- ch
236 return b.draw(<-ch, width)
256 s := <-ch
257 return s.draw(width)
237258 }
238259
239260 func (b *Bar) server(ctx context.Context, wg *sync.WaitGroup, total int64) {
249270 select {
250271 case i := <-b.incrCh:
251272 n := state.current + i
252 if n > total {
273 if total > 0 && n > total {
253274 state.current = total
254275 completed = true
255276 blockStartTime = time.Now()
274295 state.prependFuncs = nil
275296 }
276297 case ch := <-b.stateReqCh:
298 state.fill = b.fill
299 state.empty = b.empty
300 state.tip = b.tip
301 state.leftEnd = b.leftEnd
302 state.rightEnd = b.rightEnd
303 state.etaAlpha = b.etaAlpha
304 state.width = b.width
277305 ch <- state
306 case state.refill = <-b.refillCh:
278307 case state.trimLeftSpace = <-b.trimLeftCh:
279308 case state.trimRightSpace = <-b.trimRightCh:
280309 case <-b.flushedCh:
281310 if completed {
282311 return
283312 }
313 case <-b.completeReqCh:
314 return
284315 case <-b.removeReqCh:
285316 return
286317 case <-ctx.Done():
308339 b.removeReqCh <- struct{}{}
309340 }
310341
311 func (b *Bar) draw(s state, termWidth int) []byte {
312 buf := make([]byte, 0, termWidth)
313
342 func (s *state) draw(termWidth int) []byte {
314343 stat := &Statistics{
315344 Total: s.total,
316345 Current: s.current,
319348 TimePerItemEstimate: s.timePerItem,
320349 }
321350
322 barBlock := b.fillBar(s.total, s.current, b.width)
323
324351 // render append functions to the right of the bar
325352 var appendBlock []byte
326353 for _, f := range s.appendFuncs {
333360 prependBlock = append(prependBlock, []byte(f(stat))...)
334361 }
335362
363 barBlock := s.fillBar()
336364 prependCount := utf8.RuneCount(prependBlock)
337365 barCount := utf8.RuneCount(barBlock)
338366 appendCount := utf8.RuneCount(appendBlock)
351379
352380 totalCount := prependCount + barCount + appendCount
353381 if totalCount >= termWidth {
354 newWidth := termWidth - prependCount - appendCount
355 barBlock = b.fillBar(s.total, s.current, newWidth-1)
356 }
357
382 s.width = termWidth - prependCount - appendCount - 1
383 barBlock = s.fillBar()
384 }
385
386 buf := make([]byte, 0, termWidth)
358387 for _, block := range [...][]byte{prependBlock, leftSpace, barBlock, rightSpace, appendBlock} {
359388 buf = append(buf, block...)
360389 }
362391 return buf
363392 }
364393
365 func (b *Bar) fillBar(total, current int64, width int) []byte {
366 if width < 2 {
394 func (s *state) fillBar() []byte {
395 if s.width < 2 {
367396 return []byte{}
368397 }
369398
370 buf := make([]byte, width)
371 completedWidth := percentage(total, current, width)
372
373 if b.refill != nil {
374 till := percentage(total, b.refill.till, width)
399 buf := make([]byte, s.width)
400 completedWidth := percentage(s.total, s.current, s.width)
401
402 if s.refill != nil {
403 till := percentage(s.total, s.refill.till, s.width)
375404 for i := 1; i < till; i++ {
376 buf[i] = b.refill.c
405 buf[i] = s.refill.char
377406 }
378407 for i := till; i < completedWidth; i++ {
379 buf[i] = b.fill
408 buf[i] = s.fill
380409 }
381410 } else {
382411 for i := 1; i < completedWidth; i++ {
383 buf[i] = b.fill
412 buf[i] = s.fill
384413 }
385414 }
386415
387 for i := completedWidth; i < width-1; i++ {
388 buf[i] = b.empty
416 for i := completedWidth; i < s.width-1; i++ {
417 buf[i] = s.empty
389418 }
390419 // set tip bit
391 if completedWidth > 0 && completedWidth < width {
392 buf[completedWidth-1] = b.tip
420 if completedWidth > 0 && completedWidth < s.width {
421 buf[completedWidth-1] = s.tip
393422 }
394423 // set left and right ends bits
395 buf[0], buf[width-1] = b.leftEnd, b.rightEnd
424 buf[0], buf[s.width-1] = s.leftEnd, s.rightEnd
396425
397426 return buf
398427 }
428457 }
429458
430459 func percentage(total, current int64, ratio int) int {
431 if total == 0 {
460 if total <= 0 {
432461 return 0
433462 }
434463 return int(float64(ratio) * float64(current) / float64(total))
435464 }
465
466 func getSpinner() func() byte {
467 chars := []byte(`-\|/`)
468 repeat := len(chars) - 1
469 index := repeat
470 return func() byte {
471 if index == repeat {
472 index = -1
473 }
474 index++
475 return chars[index]
476 }
477 }