-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcontext.go
152 lines (127 loc) · 3.18 KB
/
context.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
package ttyprogress
import (
"context"
"io"
"os"
"sync"
"time"
"github.com/mandelsoft/ttycolors"
"github.com/mandelsoft/ttyprogress/blocks"
"github.com/mandelsoft/ttyprogress/specs"
)
// Context is a set of lines on a terminal
// used to display some live progress information.
// It can be used to display an arbitrary number of
// progress elements, which are independently.
// Leading elements will leave the text window
// used once they are finished.
type Context interface {
Container
// GetTTYContext returns the underlying ttycolors.TTYContext.
GetTTYContext() ttycolors.TTYContext
IsColorsEnabled() bool
EnableColors(b ...bool) Context
// Blocks returns the underlying
// blocks.Blocks object used
// to display the progress elements.
// It can directly be used in combination
// with progress elements.
// But all active blocks will prohibit the
// progress object to complete.
Blocks() *blocks.Blocks
// Done returns the done channel.
// A Context is done, if it is closed and
// all progress elements are finished.
Done() <-chan struct{}
// Close closes the Context. No more
// progress elements can be added anymore.
Close() error
// Wait until the Context is Done.
// If a context.Context is given, Wait
// also returns if the context is canceled.
Wait(ctx context.Context) error
}
type _progress struct {
lock sync.Mutex
blocks *blocks.Blocks
ticker *time.Ticker
elements []Element
closed bool
}
var _ Container = (*_progress)(nil)
// For creates a new Context, which manages a terminal line range
// used to indicate progress of some actions.
// This line range is always at the end of the given
// writer, which must refer to a terminal device.
// Context indicators are added by explicitly calling
// the appropriate constructors. They take the Context
// they should be attached to as first argument.
func For(opt ...io.Writer) Context {
p := &_progress{
blocks: blocks.New(opt...),
ticker: time.NewTicker(specs.Tick),
}
go p.listen()
return p
}
func (p *_progress) Blocks() *blocks.Blocks {
return p.blocks
}
func (p *_progress) GetTTYContext() ttycolors.TTYContext {
return p.blocks.GetTTYGontext()
}
func (p *_progress) IsColorsEnabled() bool {
return p.Blocks().IsColorsEnabled()
}
func (p *_progress) EnableColors(b ...bool) Context {
p.Blocks().EnableColors(b...)
return p
}
func (p *_progress) AddBlock(b *blocks.Block) error {
p.lock.Lock()
defer p.lock.Unlock()
if p.closed {
return os.ErrClosed
}
return p.blocks.AddBlock(b)
}
func (p *_progress) Done() <-chan struct{} {
return p.blocks.Done()
}
func (p *_progress) Close() error {
p.lock.Lock()
defer p.lock.Unlock()
p.blocks.CloseOnDone()
p.closed = true
return nil
}
func (p *_progress) IsClosed() bool {
p.lock.Lock()
defer p.lock.Unlock()
return p.closed
}
func (p *_progress) Wait(ctx context.Context) error {
return p.blocks.Wait(ctx)
}
func (p *_progress) listen() {
for {
select {
case <-p.ticker.C:
p.tick()
case <-p.Done():
p.ticker.Stop()
return
}
}
}
func (p *_progress) tick() {
flush := false
for _, b := range p.blocks.Blocks() {
if e, ok := b.Payload().(Ticker); ok {
flush = e.Tick() || flush
}
}
if flush {
p.blocks.Flush()
}
}