forked from janeczku/go-spinner
-
Notifications
You must be signed in to change notification settings - Fork 0
/
spinner.go
121 lines (107 loc) · 2.1 KB
/
spinner.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
package spinner
import (
"fmt"
"io"
"sync"
"syscall"
"time"
"golang.org/x/crypto/ssh/terminal"
)
const (
// 150ms per frame
DEFAULT_FRAME_RATE = time.Millisecond * 150
)
var DefaultCharset = []string{"|", "/", "-", "\\"}
type Spinner struct {
sync.Mutex
Title string
Charset []string
FrameRate time.Duration
runChan chan struct{}
stopOnce sync.Once
Output io.Writer
NoTty bool
}
// create spinner object
func NewSpinner(title string) *Spinner {
sp := &Spinner{
Title: title,
Charset: DefaultCharset,
FrameRate: DEFAULT_FRAME_RATE,
runChan: make(chan struct{}),
}
var stdout interface{} = syscall.Stdout
stdoutFD, ok := stdout.(int)
if !(ok && terminal.IsTerminal(stdoutFD)) {
sp.NoTty = true
}
return sp
}
// start a new spinner, title can be an empty string
func StartNew(title string) *Spinner {
return NewSpinner(title).Start()
}
// start spinner
func (sp *Spinner) Start() *Spinner {
go sp.writer()
return sp
}
// set custom spinner frame rate
func (sp *Spinner) SetSpeed(rate time.Duration) *Spinner {
sp.Lock()
sp.FrameRate = rate
sp.Unlock()
return sp
}
// set custom spinner character set
func (sp *Spinner) SetCharset(chars []string) *Spinner {
sp.Lock()
sp.Charset = chars
sp.Unlock()
return sp
}
// stop and clear the spinner
func (sp *Spinner) Stop() {
//prevent multiple calls
sp.stopOnce.Do(func() {
close(sp.runChan)
sp.clearLine()
})
}
// spinner animation
func (sp *Spinner) animate() {
var out string
for i := 0; i < len(sp.Charset); i++ {
out = sp.Charset[i] + " " + sp.Title
switch {
case sp.Output != nil:
fmt.Fprint(sp.Output, out)
//fmt.Fprint(sp.Output, "\r"+out)
case !sp.NoTty:
fmt.Print(out)
//fmt.Print("\r" + out)
}
time.Sleep(sp.FrameRate)
sp.clearLine()
}
}
// write out spinner animation until runChan is closed
func (sp *Spinner) writer() {
sp.animate()
for {
select {
case <-sp.runChan:
return
default:
sp.animate()
}
}
}
// workaround for Mac OS < 10 compatibility
func (sp *Spinner) clearLine() {
if !sp.NoTty {
fmt.Printf("\033[2K")
fmt.Println()
fmt.Printf("\033[1A")
}
}