GithubHelp home page GithubHelp logo

briandowns / spinner Goto Github PK

View Code? Open in Web Editor NEW
2.3K 20.0 132.0 2.88 MB

Go (golang) package with 90 configurable terminal spinner/progress indicators.

License: Apache License 2.0

Go 99.36% Makefile 0.64%
go golang spinner statusbar cli terminal terminal-ui progress-bar progressbar indicator

spinner's Introduction

Spinner

GoDoc CircleCI

spinner is a simple package to add a spinner / progress indicator to any terminal application. Examples can be found below as well as full examples in the examples directory.

For more detail about the library and its features, reference your local godoc once installed.

Contributions welcome!

Installation

go get github.com/briandowns/spinner

Available Character Sets

90 Character Sets. Some examples below:

(Numbered by their slice index)

index character set sample gif
0 ←↖↑↗→↘↓↙ Sample Gif
1 ▁▃▄▅▆▇█▇▆▅▄▃▁ Sample Gif
2 ▖▘▝▗ Sample Gif
3 ┤┘┴└├┌┬┐ Sample Gif
4 ◢◣◤◥ Sample Gif
5 ◰◳◲◱ Sample Gif
6 ◴◷◶◵ Sample Gif
7 ◐◓◑◒ Sample Gif
8 .oO@* Sample Gif
9 |/-\ Sample Gif
10 ◡◡⊙⊙◠◠ Sample Gif
11 ⣾⣽⣻⢿⡿⣟⣯⣷ Sample Gif
12 >))'> >))'> >))'> >))'> >))'> <'((< <'((< <'((< Sample Gif
13 ⠁⠂⠄⡀⢀⠠⠐⠈ Sample Gif
14 ⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏ Sample Gif
15 abcdefghijklmnopqrstuvwxyz Sample Gif
16 ▉▊▋▌▍▎▏▎▍▌▋▊▉ Sample Gif
17 ■□▪▫ Sample Gif
18 ←↑→↓ Sample Gif
19 ╫╪ Sample Gif
20 ⇐⇖⇑⇗⇒⇘⇓⇙ Sample Gif
21 ⠁⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈ Sample Gif
22 ⠈⠉⠋⠓⠒⠐⠐⠒⠖⠦⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈ Sample Gif
23 ⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠴⠲⠒⠂⠂⠒⠚⠙⠉⠁ Sample Gif
24 ⠋⠙⠚⠒⠂⠂⠒⠲⠴⠦⠖⠒⠐⠐⠒⠓⠋ Sample Gif
25 ヲァィゥェォャュョッアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン Sample Gif
26 . .. ... Sample Gif
27 ▁▂▃▄▅▆▇█▉▊▋▌▍▎▏▏▎▍▌▋▊▉█▇▆▅▄▃▂▁ Sample Gif
28 .oO°Oo. Sample Gif
29 +x Sample Gif
30 v<^> Sample Gif
31 >>---> >>---> >>---> >>---> >>---> <---<< <---<< <---<< <---<< <---<< Sample Gif
32 | || ||| |||| ||||| |||||| ||||| |||| ||| || | Sample Gif
33 [] [=] [==] [===] [====] [=====] [======] [=======] [========] [=========] [==========] Sample Gif
34 (*---------) (-*--------) (--*-------) (---*------) (----*-----) (-----*----) (------*---) (-------*--) (--------*-) (---------*) Sample Gif
35 █▒▒▒▒▒▒▒▒▒ ███▒▒▒▒▒▒▒ █████▒▒▒▒▒ ███████▒▒▒ ██████████ Sample Gif
36 [ ] [=> ] [===> ] [=====> ] [======> ] [========> ] [==========> ] [============> ] [==============> ] [================> ] [==================> ] [===================>] Sample Gif
37 🕐 🕑 🕒 🕓 🕔 🕕 🕖 🕗 🕘 🕙 🕚 🕛 Sample Gif
38 🕐 🕜 🕑 🕝 🕒 🕞 🕓 🕟 🕔 🕠 🕕 🕡 🕖 🕢 🕗 🕣 🕘 🕤 🕙 🕥 🕚 🕦 🕛 🕧 Sample Gif
39 🌍 🌎 🌏 Sample Gif
40 ◜ ◝ ◞ ◟ Sample Gif
41 ⬒ ⬔ ⬓ ⬕ Sample Gif
42 ⬖ ⬘ ⬗ ⬙ Sample Gif
43 [>>> >] []>>>> [] [] >>>> [] [] >>>> [] [] >>>> [] [] >>>>[] [>> >>] Sample Gif

Features

  • Start
  • Stop
  • Restart
  • Reverse direction
  • Update the spinner character set
  • Update the spinner speed
  • Prefix or append text
  • Change spinner color, background, and text attributes such as bold / italics
  • Get spinner status
  • Chain, pipe, redirect output
  • Output final string on spinner/indicator completion

Examples

package main

import (
	"github.com/briandowns/spinner"
	"time"
)

func main() {
	s := spinner.New(spinner.CharSets[9], 100*time.Millisecond)  // Build our new spinner
	s.Start()                                                    // Start the spinner
	time.Sleep(4 * time.Second)                                  // Run for some time to simulate work
	s.Stop()
}

Update the character set and restart the spinner

s.UpdateCharSet(spinner.CharSets[1])  // Update spinner to use a different character set
s.Restart()                           // Restart the spinner
time.Sleep(4 * time.Second)
s.Stop()

Update spin speed and restart the spinner

s.UpdateSpeed(200 * time.Millisecond) // Update the speed the spinner spins at
s.Restart()
time.Sleep(4 * time.Second)
s.Stop()

Reverse the direction of the spinner

s.Reverse() // Reverse the direction the spinner is spinning
s.Restart()
time.Sleep(4 * time.Second)
s.Stop()

Provide your own spinner

(or send me an issue or pull request to add to the project)

someSet := []string{"+", "-"}
s := spinner.New(someSet, 100*time.Millisecond)

Prefix or append text to the spinner

s.Prefix = "prefixed text: " // Prefix text before the spinner
s.Suffix = "  :appended text" // Append text after the spinner

Set or change the color of the spinner. Default color is white. The spinner will need to be restarted to pick up the change.

s.Color("red") // Set the spinner color to red

You can specify both the background and foreground color, as well as additional attributes such as bold or underline.

s.Color("red", "bold") // Set the spinner color to a bold red

To set the background to black, the foreground to a bold red:

s.Color("bgBlack", "bold", "fgRed")

Below is the full color and attribute list:

// default colors
red
black
green
yellow
blue
magenta
cyan
white

// attributes
reset
bold
faint
italic
underline
blinkslow
blinkrapid
reversevideo
concealed
crossedout

// foreground text
fgBlack
fgRed
fgGreen
fgYellow
fgBlue
fgMagenta
fgCyan
fgWhite

// foreground Hi-Intensity text
fgHiBlack
fgHiRed
fgHiGreen
fgHiYellow
fgHiBlue
fgHiMagenta
fgHiCyan
fgHiWhite

// background text
bgBlack
bgRed
bgGreen
bgYellow
bgBlue
bgMagenta
bgCyan
bgWhite

// background Hi-Intensity text
bgHiBlack
bgHiRed
bgHiGreen
bgHiYellow
bgHiBlue
bgHiMagenta
bgHiCyan
bgHiWhite

Generate a sequence of numbers

setOfDigits := spinner.GenerateNumberSequence(25)    // Generate a 25 digit string of numbers
s := spinner.New(setOfDigits, 100*time.Millisecond)

Get spinner status

fmt.Println(s.Active())

Unix pipe and redirect

Feature suggested and write up by dekz

Setting the Spinner Writer to Stderr helps show progress to the user, with the enhancement to chain, pipe or redirect the output.

This is the preferred method of setting a Writer at this time.

s := spinner.New(spinner.CharSets[11], 100*time.Millisecond, spinner.WithWriter(os.Stderr))
s.Suffix = " Encrypting data..."
s.Start()
// Encrypt the data into ciphertext
fmt.Println(os.Stdout, ciphertext)
> myprog encrypt "Secret text" > encrypted.txt
⣯ Encrypting data...
> cat encrypted.txt
1243hjkbas23i9ah27sj39jghv237n2oa93hg83

Final String Output

Add additional output when the spinner/indicator has completed. The "final" output string can be multi-lined and will be written to wherever the io.Writer has been configured for.

s := spinner.New(spinner.CharSets[9], 100*time.Millisecond)
s.FinalMSG = "Complete!\nNew line!\nAnother one!\n"
s.Start()                 
time.Sleep(4 * time.Second)
s.Stop()                   

Output

Complete!
New line!
Another one!

spinner's People

Contributors

alexaandru avatar askalice avatar briandowns avatar cmzz avatar divan avatar emford avatar gongzhxu avatar guitarbum722 avatar harrymichal avatar ilgooz avatar ivenk avatar jdmaldon avatar joshrickert avatar jrslv avatar kylecarbs avatar mike-dax avatar mislav avatar mjmac avatar mxygem avatar navusas avatar pbnj avatar pnickolov avatar qneyrat avatar santosh653 avatar schollz avatar simonwaldherr avatar slippycheeze avatar soldiermoth avatar srisco avatar testwill avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

spinner's Issues

Colored spinner can not be restarted

The following code won't work for me (iTerm2 + zsh):

package main

import (
	"time"

	"github.com/briandowns/spinner"
)

func main() {
	sp := spinner.New(
		spinner.CharSets[11],
		time.Millisecond*100,
		spinner.WithColor("green"),
	)
	sp.Stop()

	time.Sleep(time.Second * 1)

	sp.Start()
	defer sp.Stop()

	time.Sleep(time.Second * 5)
}

This code demonstrates an issue I already documented in #103. I need to call sp.Stop() right after creating it to stop it from starting right away. The following restart doesn't work.

It works when removing spinner.WithColor("green").

Does not overwrite line in sakura terminal

Hi guys,

Thanks for this package. It is really useful.

I recently switched from urxvt to sakura terminal. In urxvt, the spinner successfully overwrote the previous line. In sakura, it did not. It continuously writes new lines.

I am just beginning in go, but from what I can tell, the erase function essentially backspaces for each character in the previously printed line. Would it be better if we just used fmt.Fprintf(s.Writer, "\033[2K")?

\033[2K works in Sakura in other programs that use it.

More info on terminal escape codes

If you think this would work, I can PR.

Thanks!

Running under BASH; erase() seems to backspace too many times

Running the example spinner from the README on a system under the BASH shell results in the spinner jumping backwards where the erase() function is backspacing more characters than are visible on the display. I can reproduce this in both Cygwin's BASH implementation and on a remote Linux system via PuTTY SSH client with BASH as the login shell on the remote system.

I double-checked and verified that this behaviour is not introduced by my previous fix for windows-support.

This may be unrelated to using BASH specifically, but more a UNIX/Posix shell issue in general.

Terminal is not configured properly after Ctrl+C signal

Describe the bug
When you have a spinner with the HideCursor property set to true and you stop it in the middle of execution with a Ctrl+C signal the returned terminal still has the cursor hidden.

Expected behavior
If a stop signal is received it should make the cursor visible.

TestRestart not always pass on macOS Terminal

using master

Ilkers-MacBook-Pro:spinner ilgooz$ go test -run TestRestart
PASS
ok  	github.com/briandowns/spinner	0.465s
Ilkers-MacBook-Pro:spinner ilgooz$ go test -run TestRestart
--- FAIL: TestRestart (0.45s)
	spinner_test.go:124: Expected ==, got 
		[]byte{0x1b, 0x5b, 0x33, 0x36, 0x6d, 0xe2, 0x97, 0xa2, 0x1b, 0x5b, 0x30, 0x6d, 0x20, 0x8, 0x8, 0x7f, 0x7f, 0x8, 0x8, 0x1b, 0x5b, 0x33, 0x36, 0x6d, 0xe2, 0x97, 0xa3, 0x1b, 0x5b, 0x30, 0x6d, 0x20, 0x8, 0x8, 0x7f, 0x7f, 0x8, 0x8, 0x1b, 0x5b, 0x33, 0x36, 0x6d, 0xe2, 0x97, 0xa4, 0x1b, 0x5b, 0x30, 0x6d, 0x20, 0x8, 0x8, 0x7f, 0x7f, 0x8, 0x8, 0x1b, 0x5b, 0x33, 0x36, 0x6d, 0xe2, 0x97, 0xa5, 0x1b, 0x5b, 0x30, 0x6d, 0x20, 0x8, 0x8, 0x7f, 0x7f, 0x8, 0x8, 0x1b, 0x5b, 0x33, 0x36, 0x6d, 0xe2} != 
		[]byte{0x97, 0xa2, 0x1b, 0x5b, 0x30, 0x6d, 0x20, 0x8, 0x8, 0x7f, 0x7f, 0x8, 0x8, 0x1b, 0x5b, 0x33, 0x36, 0x6d, 0xe2, 0x97, 0xa3, 0x1b, 0x5b, 0x30, 0x6d, 0x20, 0x8, 0x8, 0x7f, 0x7f, 0x8, 0x8, 0x1b, 0x5b, 0x33, 0x36, 0x6d, 0xe2, 0x97, 0xa4, 0x1b, 0x5b, 0x30, 0x6d, 0x20, 0x8, 0x8, 0x7f, 0x7f, 0x8, 0x8, 0x1b, 0x5b, 0x33, 0x36, 0x6d, 0xe2, 0x97, 0xa5, 0x1b, 0x5b, 0x30, 0x6d, 0x20, 0x8, 0x8, 0x7f, 0x7f, 0x8, 0x8, 0x1b, 0x5b, 0x33, 0x36, 0x6d, 0xe2, 0x97, 0xa2, 0x1b, 0x5b, 0x30, 0x6d, 0x20}

Finalised message (optional)

I have some thoughts on a finalized string. Right now the Spinner completes and erases all that has been written.

In my code I am dealing with this myself, but it is something that might fit within this package.

Old behaviour:

⣯ Encrypting data...

s.Stop()

New optional Behaviour:

s.FinalMessage = "✔Data encryption successful"

⣯ Encrypting data...

s.Stop()

✔ Data encryption successful

This is useful as a checklist at the end of a long CLI command:

✔ Data encryption successful
✔ File uploaded to s3
⣯ Publishing...

Do you believe this is useful for Spinner or is something I should continue to roll my own?

Detect for dumb terminals

Spinner looks funny on dumb terminals. Is there any way to detect terminal capabilities and only output spinner if terminal could display it?

2017-11-23-195641_658x71_scrot

Awkward `color` option behavior

I kinda like how configuring a spinner works, but setting a color is awkward. While the usage is documented and states, that the spinner is restarted with the given color, this is kinda awkward when using it in a constructor:

// This spinner starts right away:
sp := spinner.New(
	spinner.CharSets[11],
	time.Millisecond*100,
	spinner.WithColor("green"),
)

Maybe this should be revisited since the default state of a spinner should be stopped.

An extra space character added to the spinner line

See this screenshot:

image

In this example, I set:

s.Prefix = "[ "
s.Suffix = " ] message..."

however, I still get an empty space after message... and before the cursor indicator of my terminal emulator.

It seems like there's an extra space somewhere in the code.

39 not working on some linux shells

steps to reproduce:
create a spinner and start it on a linux shell
see the following

lllllllllllllloading message 🌍

instead of

loading message 🌍

in brief, the character is not properly removed, by leaving an offset when rewriting

Question about prefix or suffix color?

This is a question and not really an issue. Is it possible to have the prefix, suffix or FinalMSG printed with color?

For example, I can create a simple spinner and make it bold, yellow:

s := spinner.New(spinner.CharSets[29], 150*time.Millisecond)
s.Color("yellow", "bold")
s.Start()

Works just fine, I see my spinner doing it's thing in bold, yellow.

But if I want to have the prefix or suffix or FinalMSG in color, is it possible? Say for example, cyan?

s.Prefix = "doing stuff... "
s.Suffix = "wrapping up.."
s.FinalMSG = "all done"

I can achieve the same result by manually adding color and not using prefix, suffix or FinalMSG but thought I would ask in case I'm missing something.

spinners not rewriting line properly in iterm2

Reported by a user (@liztio) of a tool of mine that uses spinner (thanks!)
image

I installed iterm2 and found that disabling unicode 9 text width fixes this, but that option is on by default and generally something users probably want. I've not dug into this further yet.

Having multiple spinners is borked.

func TestMultiple(t *testing.T) {
    fmt.Println("TestMultiple")
    a := New(CharSets[0], 100*time.Millisecond)
    b := New(CharSets[1], 250*time.Millisecond)
    a.Start()
    b.Start()
    time.Sleep(3 * time.Second)
    a.Stop()
    b.Stop()
}

This produces flickering between the two spinners, instead of them spinning independently next to each other.

As I see it, there are a couple of options for fixing it:

  1. Only allowing one spinner (simpler)
  2. Using termbox or equivalent to allow moving the cursor arbitrarily (more flexible, but complex).

I'm leaning towards implementing 1, and leaving the option for having a spinner/multispinner package later to have simpler semantics.

TestRestart fails in 1.11

This did not fail in 1.10:

github.com/briandowns/spinner
--- FAIL: TestRestart (0.45s)
    spinner_test.go:125: Expected ==, got 
        []byte{0xd, 0x1b, 0x5b, 0x4b, 0xd, 0x1b, 0x5b, 0x4b, 0xe2, 0x97, 0xa2, 0x20, 0x8, 0x8, 0x7f, 0x7f, 0xd, 0x1b, 0x5b, 0x4b, 0xe2, 0x97, 0xa3, 0x20, 0x8, 0x8, 0x7f, 0x7f, 0xd, 0x1b, 0x5b, 0x4b, 0xe2, 0x97, 0xa4, 0x20, 0x8, 0x8, 0x7f, 0x7f, 0xd, 0x1b, 0x5b, 0x4b, 0xe2, 0x97, 0xa5, 0x20, 0x8, 0x8, 0x7f, 0x7f, 0xd, 0x1b} != 
        []byte{0x5b, 0x4b, 0xd, 0x1b, 0x5b, 0x4b, 0xe2, 0x97, 0xa2, 0x20, 0x8, 0x8, 0x7f, 0x7f, 0xd, 0x1b, 0x5b, 0x4b, 0xe2, 0x97, 0xa3, 0x20, 0x8, 0x8, 0x7f, 0x7f, 0xd, 0x1b, 0x5b, 0x4b, 0xe2, 0x97, 0xa4, 0x20, 0x8, 0x8, 0x7f, 0x7f, 0xd, 0x1b, 0x5b, 0x4b, 0xe2, 0x97, 0xa5, 0x20, 0x8, 0x8, 0x7f, 0x7f, 0xd, 0x1b, 0x5b, 0x4b}

FAIL

Using go 1.14.2 on Fedora Rawhide.

"Stop()" not clearing characters properly on Windows

I'm seeing some strange behavior on Windows when a spinner is stopped and then a line of text is printed.

Starting system service... is my spinner text.

Then in my code I do something like:

s := spinner.New(/* ... */)
s.Start()

// ... do some stuff ....

s.Stop()

fmt.Println("Started system service")

But the output seems to get smashed together:

C:\>prog.exe service start
Started system servicervice...

See the mangled servicervice.

My assumption was that it didn't properly clear the line of text before printing the next line so I put a timer in but the same result occurred.

s.Stop()

time.Sleep(time.Second)

fmt.Println("Started system service")

Is this a known issue?

Tags in this repository are not recognized by Go Modules

It's because the repo uses 1.6 as a tag and not v1.6 which would be recognized by semver, or better v1.6.0.

If you tag the future releases properly, users with go.mod wouldn't end up with something like

require (
	github.com/briandowns/spinner v0.0.0-20190319032542-ac46072a5a91
    ...

avoid creating a new line for spinner

first of all thank you for this library,
secondly we are using a fork of this lib in minikube, because if we use upstream it creates a new line for the spinner and ruins the next line

😄  minikube v1.21.0 on Darwin 11.4
✨  Automatically selected the docker driver. Other choices: hyperkit, parallels, virtualbox, ssh
👍  Starting control plane node minikube in cluster minikube
🚜  Pulling base image ...
| 🔎  Verifying Kubernetes components...
    ▪ Using image gcr.io/k8s-minikube/storage-provisioner:v5
🌟  Enabled addons: storage-provisioner, default-storageclass
🏄  Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default

here is the PR that the fork #108

I undrestand the PR might not be best for your project but is there any other way to fix this upstream so we could use it in minikube without a fork?

Running with -race finds races in spinner and tests

Running the following trivial program with the race detector enabled (go run -race spintest.go) exposes a race between goroutines created by Spinner#Start():

package main

import (
        "fmt"
        "time"

        "github.com/briandowns/spinner"
)

func main() {
        s := spinner.New(spinner.CharSets[9], 100*time.Millisecond)

        for i := 0; i < 5; i++ {
                s.Prefix = fmt.Sprintf("Doing some stuff (%d) ", i)
                s.Start()
                time.Sleep(1 * time.Second)
                s.Stop()
        }
}

Indeed, just running go test -race also exposes this race and others in the tests themselves.

Issues with Stop() method

The "Stop()" method does not work properly after the major refactoring done in:
2f506e0

printing to the console after calling Stop(), does not clean up the line, and the spinner leaves "left overs"

Here is a short program to help reproduce:

package main

import (
    "github.com/briandowns/spinner"
    "sync"
    "time"
)

var m sync.Mutex
var wg sync.WaitGroup
var s = spinner.New(spinner.CharSets[14], 100*time.Millisecond)

func main() {
    s.Prefix = "Spinner Text"
    s.Start()
    for i := 0; i < 20; i++ {
        wg.Add(1)
        go printToConsole()
        time.Sleep(100 * time.Millisecond)
    }
    wg.Wait()
}
func printToConsole() {
    m.Lock()
    s.Stop()
    println("Some String...")
    s.Start()
    m.Unlock()
    wg.Done()
}

Random character showing up along with spinning wheel character

Hi,

Thanks for the awesome package. We have using it since quite a while in minishift to show spinning wheel while a user is waiting for registration process is complete.

However, there are some random characters showing up when spinning wheel is displayed in eclipse console.

Starting local OpenShift cluster using 'virtualbox' hypervisor...
--
Registering machine using subscription-manager
�[37m\|�[0m ��  ���[37m/�[0m ��  ���[37m-�[0m ��  ���[37m\�[0m ��  ���[37m\|�[0m ��  ���[37m/�[0m ��  ���[37m-�[0m ��  ���[37m\�[0m

More info here minishift/minishift#1277.

Any idea why we are getting such characters?

Add iota constants for spinner.CharSets

What about declaring package constants?

const (
	Arrows = iota
	BarUpDown
        SomeFancyDescriptionForNumber2
)

This would result in

spinner.New(spinner.CharSets[spinner.Arrows], 100*time.Millisecond)

Spinner Eats Lines on CentOS 7

We have this as part of our automation for tracking the status of a executing Task, and on the environments that it was created on (Macs) it works correctly were as the automation progresses each line is added with automation information and the spinner stays on the new line. On my CentOS VM the spinner causes the Task line to eat (backspace) all characters on the line that it is on, then it moves to the line above and continues until the Task line with Spinner are sitting in the top left corner.

The Spinner type being used is #34

Code is:
//define spinner charset and speed
spin := spinner.New(spinner.CharSets[34], 100*time.Millisecond)
spin.Prefix = "\033[?25lTask " + taskID + ": "
spin.Start()

Test error with Golang 1.16 beta 1

On Fedora Rawhide with Golang 1.16 beta 1, I've got the following error:

Testing    in: /builddir/build/BUILD/spinner-1.12.0/_build/src
         PATH: /builddir/build/BUILD/spinner-1.12.0/_build/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/sbin
       GOPATH: /builddir/build/BUILD/spinner-1.12.0/_build:/usr/share/gocode
  GO111MODULE: off
      command: go test -buildmode pie -compiler gc -ldflags " -X github.com/briandowns/spinner/version=1.12.0 -extldflags '-Wl,-z,relro -Wl,--as-needed  -Wl,-z,now -specs=/usr/lib/rpm/redhat/redhat-hardened-ld  '"
      testing: github.com/briandowns/spinner
github.com/briandowns/spinner


▁ ���WWW���

▃ ���WWW���

▄ ���WWW���

▅ ���WWW���

▆ ���WWW���

▇ ���WWW���

█ ���WWW���

▇ ���WWW���

▆ ���WWW���

▅ ���WWW���

▄ ���WWW���

▃ ���WWW���

▁ ���WWW���

▁ ���WWW���

▃ ���WWW���

▄ ���WWW���

▅ ���WWW���

▆ ���WWW���

▇ ���WWW���

█ ���WWW���

▇ ���WWW���

▆ ���WWW���

▅ ���WWW���

▄ ���WWW���

▃ ���WWW���

▁ ���WWW���

▁ ���WWW���

▃ ���WWW���

▄ ���WWW���

▅ ���WWW���

--- FAIL: TestRestart (0.45s)
    spinner_test.go:125: Expected ==, got 
        []byte{0xd, 0x1b, 0x5b, 0x4b, 0xd, 0xe2, 0x97, 0xa2, 0x20, 0x8, 0x8, 0x8, 0x57, 0x57, 0x57, 0x8, 0x8, 0x8, 0x1b, 0x5b, 0x4b, 0x1b, 0x5b, 0x4b, 0x1b, 0x5b, 0x4b, 0xd, 0x1b, 0x5b, 0x4b, 0xd, 0xe2, 0x97, 0xa3, 0x20, 0x8, 0x8, 0x8, 0x57, 0x57, 0x57, 0x8, 0x8, 0x8, 0x1b, 0x5b, 0x4b, 0x1b, 0x5b, 0x4b, 0x1b, 0x5b, 0x4b, 0xd, 0x1b, 0x5b, 0x4b, 0xd, 0xe2, 0x97, 0xa4, 0x20, 0x8, 0x8, 0x8, 0x57, 0x57, 0x57, 0x8, 0x8, 0x8, 0x1b, 0x5b, 0x4b, 0x1b, 0x5b, 0x4b, 0x1b, 0x5b, 0x4b, 0xd, 0x1b, 0x5b, 0x4b, 0xd, 0xe2, 0x97, 0xa5, 0x20, 0x8, 0x8, 0x8, 0x57, 0x57, 0x57, 0x8, 0x8, 0x8, 0x1b, 0x5b, 0x4b, 0x1b, 0x5b, 0x4b, 0x1b, 0x5b, 0x4b, 0xd, 0x1b, 0x5b, 0x4b} != 
        []byte{0xd, 0xe2, 0x97, 0xa2, 0x20, 0x8, 0x8, 0x8, 0x57, 0x57, 0x57, 0x8, 0x8, 0x8, 0x1b, 0x5b, 0x4b, 0x1b, 0x5b, 0x4b, 0x1b, 0x5b, 0x4b, 0xd, 0x1b, 0x5b, 0x4b, 0xd, 0x1b, 0x5b, 0x4b, 0xd, 0xe2, 0x97, 0xa3, 0x20, 0x8, 0x8, 0x8, 0x57, 0x57, 0x57, 0x8, 0x8, 0x8, 0x1b, 0x5b, 0x4b, 0x1b, 0x5b, 0x4b, 0x1b, 0x5b, 0x4b, 0xd, 0x1b, 0x5b, 0x4b, 0xd, 0xe2, 0x97, 0xa4, 0x20, 0x8, 0x8, 0x8, 0x57, 0x57, 0x57, 0x8, 0x8, 0x8, 0x1b, 0x5b, 0x4b, 0x1b, 0x5b, 0x4b, 0x1b, 0x5b, 0x4b, 0xd, 0x1b, 0x5b, 0x4b, 0xd, 0xe2, 0x97, 0xa5, 0x20, 0x8, 0x8, 0x8, 0x57, 0x57, 0x57, 0x8, 0x8, 0x8, 0x1b, 0x5b, 0x4b, 0x1b, 0x5b, 0x4b, 0x1b, 0x5b, 0x4b, 0xd, 0x1b, 0x5b, 0x4b}


◡◡ ����WWWW����

⊙⊙ ����WWWW����

◠◠ ����WWWW����

◡◡ ����WWWW����


⊙⊙ ����WWWW����

◡◡ ����WWWW����

◠◠ ����WWWW����


⊙⊙ ����WWWW����

◠◠ ����WWWW����

◡◡ ����WWWW����
This is on the same line as the spinner: 

← ���WWW���

↖ ���WWW���

↑ ���WWW���

↗ ���WWW���

→ ���WWW���

↘ ���WWW���

↓ ���WWW���

↙ ���WWW���

← ���WWW���

↖ ���WWW���

↑ ���WWW���

↗ ���WWW���

→ ���WWW���

↘ ���WWW���

↓ ���WWW���

↙ ���WWW���

← ���WWW���

↖ ���WWW���

↑ ���WWW���

↗ ���WWW���

→ ���WWW���

↘ ���WWW���

↓ ���WWW���

↙ ���WWW���

← ���WWW���

↖ ���WWW���

↑ ���WWW���

↗ ���WWW���

→ ���WWW���

↘ ���WWW���
FAIL
exit status 1
FAIL	github.com/briandowns/spinner	16.789s

There is a race issue

How to produces?

spinner.go

package spinner

import (
	"time"

	"github.com/briandowns/spinner"
)

var s *spinner.Spinner

func init() {
	style := spinner.CharSets[36]
	interval := 100 * time.Millisecond
	s = spinner.New(style, interval)
}

// Start spinner
func Start(task string) {
	s.Start()
}

// Stop spinner
func Stop(task string, err error) {
	s.Stop()
}

spinner_test.go

package spinner

import (
	"fmt"
	"testing"
	"time"
)

func TestSpinner(t *testing.T) {
	t.Run("failure", func(t *testing.T) {
		Start("task 2")
		time.Sleep(1 * time.Second)
		Stop("task 2", fmt.Errorf("error happened"))
	})
	t.Run("success", func(t *testing.T) {
		Start("task 1")
		time.Sleep(1 * time.Second)
		Stop("task 1", nil)
	})
}

Then run test command,

go test -v ./... --race

Will get failure message.

=== RUN   TestSpinner
=== RUN   TestSpinner/failure
=== RUN   TestSpinner/success
==================
WARNING: DATA RACE
Read at 0x00c000140340 by goroutine 9:
  github.com/briandowns/spinner.(*Spinner).Start.func1()
      /Users/minhuang/.go/pkg/mod/github.com/briandowns/[email protected]/spinner.go:288 +0x109

Previous write at 0x00c000140340 by goroutine 11:
  github.com/briandowns/spinner.(*Spinner).Start()
      /Users/minhuang/.go/pkg/mod/github.com/briandowns/[email protected]/spinner.go:278 +0xa7
  github.com/metrue/fx/pkg/spinner.Start()
      /Users/minhuang/Codes/fx/pkg/spinner/spiner.go:19 +0x4b
  github.com/metrue/fx/pkg/spinner.TestSpinner.func2()
      /Users/minhuang/Codes/fx/pkg/spinner/spiner_test.go:16 +0x2b
  testing.tRunner()
      /usr/local/opt/go/libexec/src/testing/testing.go:992 +0x1eb

Goroutine 9 (running) created at:
  github.com/briandowns/spinner.(*Spinner).Start()
      /Users/minhuang/.go/pkg/mod/github.com/briandowns/[email protected]/spinner.go:281 +0xef
  github.com/metrue/fx/pkg/spinner.Start()
      /Users/minhuang/Codes/fx/pkg/spinner/spiner.go:19 +0x4f
  github.com/metrue/fx/pkg/spinner.TestSpinner.func1()
      /Users/minhuang/Codes/fx/pkg/spinner/spiner_test.go:11 +0x2f
  testing.tRunner()
      /usr/local/opt/go/libexec/src/testing/testing.go:992 +0x1eb

Goroutine 11 (running) created at:
  testing.(*T).Run()
      /usr/local/opt/go/libexec/src/testing/testing.go:1043 +0x660
  github.com/metrue/fx/pkg/spinner.TestSpinner()
      /Users/minhuang/Codes/fx/pkg/spinner/spiner_test.go:15 +0x8c
  testing.tRunner()
      /usr/local/opt/go/libexec/src/testing/testing.go:992 +0x1eb
==================
    TestSpinner/success: testing.go:906: race detected during execution of test
    TestSpinner: testing.go:906: race detected during execution of test
--- FAIL: TestSpinner (2.01s)
    --- PASS: TestSpinner/failure (1.00s)
    --- FAIL: TestSpinner/success (1.00s)
    : testing.go:906: race detected during execution of test
FAIL
FAIL	github.com/metrue/fx/pkg/spinner	2.039s
FAIL

Provide the io.Writer

Is it possible to provide the io.Writer on creation. I would like the spinner output to go to Stderr rather than Stdout so that progress can be shown while the result can be piped out.

myprog run > output.txt
⣯ Doing work
> cat output.txt
output from the run command

Long status message and short final message garbles output

In the following program:

package main

import (
	"github.com/briandowns/spinner"
	"time"
)

func main() {
	s := spinner.New(spinner.CharSets[9], 100*time.Millisecond)  // Build our new spinner
        s.Suffix = "  " + "This is a really long message"
        s.FinalMSG = "  " + "Short\n"
	s.Start()                                                    // Start the spinner
	time.Sleep(4 * time.Second)                                  // Run for some time to simulate work
	s.Stop()
}

When the spinner stops, the output is:

$ go run demo.go 
  Short is a really long message 

I think the last thing the spinner should do is erase the long message no?

Multiline stdout with suffix

I am trying to use spinner and the spinner keep printing stdout line by line instead of refreshing:

Please enter your username: asd
⠋ Loggin into platform as asd...
⠙ Loggin into platform as asd...
⠚ Loggin into platform as asd...
⠒ Loggin into platform as asd...
⠂ Loggin into platform as asd...
⠂ Loggin into platform as asd...
⠒ Loggin into platform as asd...
⠲ Loggin into platform as asd...
⠴ Loggin into platform as asd...
⠦ Loggin into platform as asd...
⠖ Loggin into platform as asd...

Below attached my code, is it a normal behavior?

reader := bufio.NewReader(os.Stdin)

fmt.Printf("Please enter your username: ")
username, _ := reader.ReadString('\n')
username = strings.TrimSuffix(username, "\n")

s := spinner.New(spinner.CharSets[24], 100*time.Millisecond)
s.Suffix = fmt.Sprintf(" Loggin into platform as %s...\n", username)
s.Color("magenta")
s.FinalMSG = fmt.Sprintf("Successfully signed in as %s\n", username)
s.Start()
time.Sleep(2 * time.Second)

s.Stop()

Expose RWMutex to lock state so that spinner doesnt output when it shouldnt

I came across this bug where sometimes the spinner will output even after we execute Stop(). One solution I notice is if you were to expose the RWMutex. Could you expose it either making the attribute public or expose through functions?

Below is an example of what I am seeing

func main() {
	s := spinner.New(spinner.CharSets[22], 1*time.Microsecond)
	s.Start()
	time.Sleep(5 * time.Second)
	s.Stop()
	for i := 0; i < 10; i++ {
		fmt.Printf("hello %d\n", i)
	}
}

A solution might be

# in github.com/briandowns/spinner
func Lock(){
 s.lock.Lock()
}

func Unlock(){
 s.lock.Unlock()
}
# in app
func main() {
	s := spinner.New(spinner.CharSets[22], 1*time.Microsecond)
	s.Start()
	time.Sleep(5 * time.Second)
	s.Stop()
        s.Lock()
	for i := 0; i < 10; i++ {
		fmt.Printf("hello %d\n", i)
	}
        s.Unlock()
}

Stop() doesn't clean up the current line

After upgrading to the latest version (from 1.13.0) flushing stopped working for me on Ubuntu.

Here is an example:

package main

import (
	"fmt"
	"time"

	"github.com/briandowns/spinner"
)

func main() {
	s := spinner.New(spinner.CharSets[9], 100*time.Millisecond)  // Build our new spinner
	s.Prefix = "prefixed: " // Prefix text before the spinner
	s.Suffix = "  :appended" // Append text after the spinner
	s.Start()                                                    // Start the spinner
	time.Sleep(4 * time.Second)                                  // Run for some time to simulate work
	s.Stop()
	fmt.Println("test")
}

Outputs in v1.16.0 and in v1.14.0 (doesn't erase the previous message):

prefixed: \  :appended test

Outputs in v1.15.0 (still incorrect, but moves the cursor to the beginning of the line):

testixed: \  :appended

Output in v1.13.0:

test

Show Spinner.FinalMSG without having to Start() and Stop()

Thank you for this CLI spinner package.

I've used a similar CLI spinner package in node.js (https://github.com/sindresorhus/ora).
The package had some nice APIs that allowed users to use ora to log out messages.

For example:

  • Informational messages:
    ora('Some info message').info() would print out ℹ Some info message
    Note: doesn't require starting or stopping CLI spinner.

  • Status messages:

    progress = ora('Process started...')
    progress.start()
    // some work
    if (/*progress finished successfully*/) progress.succeed("Process successfully finished")
    else progress.failed("Process failed to complete")

    This would output
    ✔ Process successfully finished - if successful, or
    ✖ Process failed to complete - if failure

It would certainly be nice if this spinner package offered a similar API, but I'm especially interested if this package exposes a way for a user to print out informational messages without starting/stopping spinner.

Race condition after calling `Stop()`

This sounds similar to #89, but I believe that this race condition is elsewhere and that #90 doesn't fix it.

We have code similar to:

s.Stop()
fmt.Fprintln(w, "hello")

The writer w is the same stream that the spinner is configured to write to.

Unlike #89, the race condition isn't around initializing the spinner with a Writer, but at the Fprintln call after calling Stop(). From what I understand, Stop only sends a message to the goroutine created in Start, but it doesn't block until that goroutine is cleaned up. Because of that, there is a chance that this erase() will still race with Fprintln in our app.

We discovered this through an intermittent failure in our test suite ran with go test -race ./....

Potential solution: Stop should block until there is zero potential for the spinner to continue writing to the output stream, or there should be another method such as StopAndWait() with this behavior.

Spinner may not start/restart properly when stopped often

Consider the following basic program:

package main

import (
	"fmt"
	"time"

	"github.com/briandowns/spinner"
)

func main() {
	mySpinner := spinner.New(spinner.CharSets[11], 100 * time.Millisecond)
	mySpinner.Suffix = " Starting spinner"
	mySpinner.Start()
	time.Sleep(500 * time.Millisecond)

	i := 0
	for {
		mySpinner.Stop()
		fmt.Printf("This is message #%d\n", i)
		mySpinner.Restart()
		i += 1
		time.Sleep(500 * time.Millisecond)
	}
}

The expected behavior is that the fmt.Printf(...) lines are printed one by one, with just the spinner running below it. However, after some time (usually less than printing 100 lines in my limited testing), the spinner no longer appears below the output of invocations of fmt.Printf(...) and all that appears is just the lines being printed.

I've also tried wrapping the Restart() call like so:

for ok := true; ok; ok = !mySpinner.Active() {
    mySpinner.Restart()
}

but this doesn't seem to fix the issue - the execution proceeds without the spinner.

How to multiple spinners at once?

Hi there.

Say I have 3 concurrent processes running at the same time can I have 3 spinners running at the same time, displaying one for each process? I've tried the naive approach of creating a spinner for each process but the spinners overwrite each other, competing for stdout.

Is it possible?

Cheers!

Spinner keeps erasing my line

Hi there,
I am having a problem using the spinner correctly :(

Here is my code:

func printStatus(s string) func() {
        spin := spinner.New(spinner.CharSets[14], 1*time.Second)
        spin.Color("fgBlack")
        spin.Prefix = s
        spin.Start()
        time.Sleep(4 * time.Second)

        return func() {
                spin.Stop()
                fmt.Printf("\n")
        }
}

When the spinner is executing, it starts backspacing my lines:
e.g.

Creating cust
Creating credentia⠙

(from "Creating customer" and "Creating credentials")

Every spin iteration moves back one more. In Bash this appears to work correctly.

I could get over this by using Prefix, but when the spinner ends it deletes the whole line, which isn't what I want. I want the output to remain.

What can I do here?

Thank you!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.