What I am trying to do is use a spinner to indicate work progress in a few goroutines. The issue I am having is that both the spinner message and job completion message is being logged in the same line. What I am asking is how can I have the spinner pinned to the bottom of so that the spinner status is not getting in the way of the logged messages itself?
The output I am getting is
a
⡿ 1/26c
⣽ 2/26b
⣯ 3/26e
⢿ 4/26d
⣾ 5/26g
⣟ 6/26f
What I am trying to get is
a
b
c
d
e
f
⣟ 6/26
Temp working code. (I realize my go routines may not be very elegant, but this is just an example).
package main
import (
"fmt"
"os"
"os/signal"
"sync"
"syscall"
"time"
"github.com/theckman/yacspin"
)
func main() {
// create a spinner
spinner, err := createSpinner()
if err != nil {
fmt.Printf("failed to make spinner from config struct: %v\n", err)
os.Exit(1)
}
// start the spinner
if err := spinner.Start(); err != nil {
panic(err)
}
wg := &sync.WaitGroup{}
wg.Add(1)
var (
total int
current int
)
spinnerCh := make(chan int, 0)
data := make(chan string)
// only want one go routine at a time but this is not important
max := make(chan struct{}, 1)
go func(s *yacspin.Spinner) {
// tried moving this into the worker goroutine also, but same effect
for range spinnerCh {
current = 1
if err := s.Pause(); err != nil {
panic(err)
}
s.Message(fmt.Sprintf("%d/%d", current, total))
if err := s.Unpause(); err != nil {
panic(err)
}
}
}(spinner)
go func() {
defer wg.Done()
for d := range data {
wg.Add(1)
go func(wg *sync.WaitGroup, d string) {
max <- struct{}{}
defer func() {
<-max
}()
// function is doing work and printing the result once done.
fmt.Println(d)
// sends a value to the spinner go routine so that it can show
// the updated count
time.Sleep(500 * time.Millisecond)
spinnerCh <- 1
wg.Done()
}(wg, d)
}
}()
// simulate queing some work
ss := []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"}
for _, s := range ss {
data <- s
}
total = len(ss)
close(data)
wg.Wait()
close(spinnerCh)
}
func createSpinner() (*yacspin.Spinner, error) {
// build the configuration, each field is documented
cfg := yacspin.Config{
Frequency: 100 * time.Millisecond,
CharSet: yacspin.CharSets[11],
Suffix: " ", // puts a least one space between the animating spinner and the Message
// Message: "collecting files",
SuffixAutoColon: true,
ColorAll: true,
Colors: []string{"fgYellow"},
StopCharacter: "✓",
StopColors: []string{"fgGreen"},
StopMessage: "done",
StopFailCharacter: "✗",
StopFailColors: []string{"fgRed"},
StopFailMessage: "failed",
}
s, err := yacspin.New(cfg)
if err != nil {
return nil, fmt.Errorf("failed to make spinner from struct: %w", err)
}
return s, nil
}
func stopOnSignal(spinner *yacspin.Spinner) {
// ensure we stop the spinner before exiting, otherwise cursor will remain
// hidden and terminal will require a `reset`
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM)
go func() {
<-sigCh
spinner.StopFailMessage("interrupted")
// ignoring error intentionally
_ = spinner.StopFail()
os.Exit(0)
}()
}
CodePudding user response:
Based on your initial question, here is the solution for not showing the spinner in the lines:
- Introduce a variable
pauseItForAMoment := false
which will help to ignore to do Pause or not in one of your goroutine.
func main() {
pauseItForAMoment := false
And the goroutine looks like this:
// tried moving this into the worker goroutine also, but same effect
for range spinnerCh {
current = 1
if !pauseItForAMoment {
if err := s.Pause(); err != nil {
panic(err)
}
s.Message(fmt.Sprintf("%d/%d", current, total))
if err := s.Unpause(); err != nil {
panic(err)
}
}
}
}(spinner)
- Stop and Start the spinner while printing
d
pauseItForAMoment = true
spinner.Prefix(d)
spinner.Stop()
// fmt.Println(d)
spinner.Start()
pauseItForAMoment = false
- Change few of the spinner config values to:
StopCharacter: " ",
// StopMessage: "done",
Note it, your goroutines are not exactly correct to print them in the order, which you know already, hope you will fix that.
Output looks like this:
Here is the code if something is not clear
CodePudding user response:
Author of the library here. Based on the snippet you shared in the
Here is the code on the Go Playground, since I am too dumb to figure out how to copy and paste it here without losing formatting.
I hope this helps!