|
|
@ -7,6 +7,7 @@ import ( |
|
|
|
"time" |
|
|
|
) |
|
|
|
|
|
|
|
// color render
|
|
|
|
var ( |
|
|
|
red = color.FgRed.Render |
|
|
|
green = color.FgGreen.Render |
|
|
@ -14,7 +15,8 @@ var ( |
|
|
|
) |
|
|
|
|
|
|
|
var ( |
|
|
|
DELAY = 800 * time.Millisecond |
|
|
|
DELAY = 800 * time.Millisecond |
|
|
|
UPDATE = 50 * time.Millisecond |
|
|
|
) |
|
|
|
|
|
|
|
const ( |
|
|
@ -27,20 +29,31 @@ type Spinner struct { |
|
|
|
msg string |
|
|
|
delay time.Duration |
|
|
|
run bool |
|
|
|
Suffix string |
|
|
|
suffix string |
|
|
|
mode []string |
|
|
|
animeIndex int |
|
|
|
length int |
|
|
|
msgCh chan string |
|
|
|
} |
|
|
|
|
|
|
|
func (s *Spinner) updateLength() *Spinner { |
|
|
|
s.length = len(s.suffix) + 2 + len(s.msg) |
|
|
|
return s |
|
|
|
} |
|
|
|
|
|
|
|
func (s *Spinner) next() string { |
|
|
|
s.animeIndex = (s.animeIndex + 1) % len(s.mode) |
|
|
|
return s.mode[s.animeIndex] |
|
|
|
} |
|
|
|
|
|
|
|
// SetMsg 设置spinner的消息
|
|
|
|
func (s *Spinner) SetMsg(msg string) *Spinner { |
|
|
|
s.msg = msg |
|
|
|
return s |
|
|
|
return s.updateLength() |
|
|
|
} |
|
|
|
|
|
|
|
func (s *Spinner) SetSuffix(suffix string) *Spinner { |
|
|
|
s.suffix = suffix |
|
|
|
return s.updateLength() |
|
|
|
} |
|
|
|
|
|
|
|
// SetDelay 更新spinner的动画间隔
|
|
|
@ -52,14 +65,15 @@ func (s *Spinner) SetDelay(delay time.Duration) *Spinner { |
|
|
|
|
|
|
|
// New 创建一个新的Spinner
|
|
|
|
func New(msg string, mode []string) *Spinner { |
|
|
|
return &Spinner{ |
|
|
|
spinner := &Spinner{ |
|
|
|
msg: msg, |
|
|
|
delay: DELAY / time.Duration(len(mode)), |
|
|
|
run: false, |
|
|
|
Suffix: "", |
|
|
|
suffix: "", |
|
|
|
mode: mode, |
|
|
|
animeIndex: 0, |
|
|
|
} |
|
|
|
return spinner.updateLength() |
|
|
|
} |
|
|
|
|
|
|
|
// Start 启动Spinner动画
|
|
|
@ -67,24 +81,58 @@ func (s *Spinner) Start() *Spinner { |
|
|
|
if s.run { |
|
|
|
return s |
|
|
|
} |
|
|
|
s.msgCh = make( |
|
|
|
chan string, |
|
|
|
5, |
|
|
|
) |
|
|
|
go s.show() |
|
|
|
return s |
|
|
|
} |
|
|
|
|
|
|
|
func (s *Spinner) Print(msg string) { |
|
|
|
if s.run { |
|
|
|
s.msgCh <- msg |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
func (s *Spinner) show() *Spinner { |
|
|
|
s.run = true |
|
|
|
go func() { |
|
|
|
for s.run { |
|
|
|
s.animeIndex = (s.animeIndex + 1) % len(s.mode) |
|
|
|
time.Sleep(s.delay) |
|
|
|
} |
|
|
|
}() |
|
|
|
for s.run { |
|
|
|
var strBuilder = strings.Builder{} |
|
|
|
select { |
|
|
|
case msg := <-s.msgCh: |
|
|
|
strBuilder := strings.Builder{} |
|
|
|
strBuilder.WriteRune('\r') |
|
|
|
strBuilder.WriteString(msg) |
|
|
|
if len(msg) < s.length { |
|
|
|
strBuilder.WriteString( |
|
|
|
strings.Repeat( |
|
|
|
" ", |
|
|
|
s.length-len(msg), |
|
|
|
), |
|
|
|
) |
|
|
|
strBuilder.WriteString("\n") |
|
|
|
} |
|
|
|
fmt.Print(strBuilder.String()) |
|
|
|
default: |
|
|
|
} |
|
|
|
strBuilder := strings.Builder{} |
|
|
|
strBuilder.WriteRune('\r') |
|
|
|
strBuilder.WriteString(s.Suffix) |
|
|
|
strBuilder.WriteString(s.suffix) |
|
|
|
strBuilder.WriteString(cyan(s.next())) |
|
|
|
strBuilder.WriteString(" ") |
|
|
|
strBuilder.WriteString(s.msg) |
|
|
|
fmt.Print(strBuilder.String()) |
|
|
|
time.Sleep(s.delay) |
|
|
|
time.Sleep(UPDATE) |
|
|
|
} |
|
|
|
return s |
|
|
|
} |
|
|
|
|
|
|
|
func (s *Spinner) joinMsg(msg ...string) string { |
|
|
|
var str = strings.Builder{} |
|
|
|
for _, v := range msg { |
|
|
@ -110,10 +158,10 @@ func (s *Spinner) Finish(info ...string) { |
|
|
|
} |
|
|
|
s.msg = s.joinMsg(info...) |
|
|
|
s.run = false |
|
|
|
|
|
|
|
close(s.msgCh) |
|
|
|
fmt.Printf( |
|
|
|
"\r%s%s %s\n\n", |
|
|
|
s.Suffix, |
|
|
|
s.suffix, |
|
|
|
green(_SUCCESS), |
|
|
|
s.msg, |
|
|
|
) |
|
|
@ -126,9 +174,10 @@ func (s *Spinner) Abort(info ...string) { |
|
|
|
} |
|
|
|
s.msg = s.joinMsg(info...) |
|
|
|
s.run = false |
|
|
|
close(s.msgCh) |
|
|
|
fmt.Printf( |
|
|
|
"\r%s%s %s\n\n", |
|
|
|
s.Suffix, |
|
|
|
s.suffix, |
|
|
|
red(_FAILURE), |
|
|
|
s.msg, |
|
|
|
) |
|
|
|