diff --git a/README.md b/README.md index e64889f..b191be2 100644 --- a/README.md +++ b/README.md @@ -16,29 +16,43 @@ Because this package adopts the spinner character sets from https://github.com/b this package is released under the Apache 2.0 License. ## Yet Another CLi Spinner? -The other Go spinner ties the ability to show updated information to the -spinner's animation, meaning you can't always show all the information you want -to the end user without changing the animation speed. In addition, there were -also some API design choices that have made it unsafe for concurrent use, which -presents challenges when trying to update the text in the spinner while it's -animating. - -There was also an interest in the spinner being able to represent a task, and to +This project was created after it was realized that the most popular spinner +library for Go had some limitations, that couldn't be fixed without a massive +overhaul of the API. + +The other spinner ties the ability to show updated messages to the spinner's +animation, meaning you can't always show all the information you want to the end +user without changing the animation speed. This means you need to trade off +animation aesthetics to show "realtime" information. It was a goal to avoid this +problem. + +In addition, there were also some API design choices that have made it unsafe +for concurrent use, which presents challenges when trying to update the text in +the spinner while it's animating. This could result in undefined behavior due to +data races. + +There were also some variable-width spinners in that other project that did +not render correctly. Because the width of the spinner animation would change, +so would the position of the message on the screen. `yacspin` uses a dynamic +width when animating, so your message should appear static relative to the +animating spinner. + +Finally, there was an interest in the spinner being able to represent a task, and to indicate whether it failed or was successful. This would have further compounded -the API issues mentioned above. +the API changes needed above to support in an intuitive way. This project takes inspiration from that other project, and takes a new approach to address the challenges above. ## Features #### Provided Spinners -There are over 70 spinners available in the `CharSets` package variable. They +There are over 80 spinners available in the `CharSets` package variable. They were borrowed from [github.com/briandowns/spinner](https://github.com/briandowns/spinner). -There is a table with all of the spinners [at the bottom of this README](#Spinners). +There is a table with most of the spinners [at the bottom of this README](#Spinners). #### Dynamic Width of Animation Because of how some spinners are animated, they may have different widths are -different times in the animation. The spinner calculates the maximum width, and +different times in the animation. `yacspin` calculates the maximum width, and pads the animation to ensure the text's position on the screen doesn't change. This results in a smoother looking animation. @@ -52,10 +66,10 @@ This results in a smoother looking animation. The spinner has both a `Stop()` and `StopFail()` method, which allows the spinner to result in a success message or a failure message. The messages, colors, and even the character used to denote success or failure are -customizable in either the initial config or via the methods. +customizable in either the initial config or via the spinner's methods. -By doing this you can use the spinner to display the status of a list of tasks -being executed serially. +By doing this you can use a single `yacspin` spinner to display the status of a +list of tasks being executed serially. ##### Stop ![Animation with Success](https://raw.githubusercontent.com/theckman/yacspin-gifs/master/features/stop.gif) @@ -65,7 +79,7 @@ being executed serially. #### Concurrency The spinner is safe for concurrent use, so you can update any of its settings -via methods whether the spinner is stopped or is currently running. +via methods whether the spinner is stopped or is currently animating. #### Live Updates Most spinners tie the ability to show new messages with the animation of the diff --git a/colors.go b/colors.go index c1c369e..1a0bc8a 100644 --- a/colors.go +++ b/colors.go @@ -18,7 +18,7 @@ import ( // ValidColors holds the list of the strings that are mapped to // github.com/fatih/color color attributes. Any of these colors / attributes can -// be used with the *Spinner type. +// be used with the *Spinner type, and it should be reflected in the output. var ValidColors = map[string]struct{}{ // default colors for backwards compatibility "black": {}, diff --git a/spinner.go b/spinner.go index 7d279d9..a97cba8 100644 --- a/spinner.go +++ b/spinner.go @@ -3,12 +3,18 @@ // project. Specifically this project borrows the default character sets, and // color mappings to github.com/fatih/color colors, from that project. // -// This also supports an alternate mode of operation for Winodws OS and dumb -// terminals. This is discovered automatically when creating the spinner. +// This spinner should support all major operating systems, and is tested +// against Linux, MacOS, and Windows. +// +// This spinner also supports an alternate mode of operation when the TERM +// environment variable is set to "dumb". This is discovered automatically when +// constructing the spinner. // // Within the yacspin package there are some default spinners stored in the -// yacspin.CharSets variable, but you can also provide your own. There is also a -// list of known colors in the yacspin.ValidColors variable. +// yacspin.CharSets variable, and you can also provide your own. There is also a +// list of known colors in the yacspin.ValidColors variable, if you'd like to +// see what's supported. If you've used github.com/fatih/color before, they +// should look familiar. // // cfg := yacspin.Config{ // Frequency: 100 * time.Millisecond, @@ -83,20 +89,25 @@ type Config struct { // Frequency specifies how often to animate the spinner. Optimal value // depends on the character set you use. // - // Note: This is a required value (cannot be 0) + // Note: This is a required value (cannot be 0). Frequency time.Duration // Writer is the place where we are outputting the spinner, and can't be - // changed on the fly. If omitted, this defaults to os.Stdout. + // changed after the *Spinner has been constructed. If omitted (nil), this + // defaults to os.Stdout. Writer io.Writer - // HideCursor describes whether the cursor should be hidden by the spinner. - // If it is hidden, it will be restored when the spinner stops. This can't - // be changed on the fly. + // HideCursor describes whether the cursor should be hidden by the spinner + // while animating. If it is hidden, it will be restored when the spinner + // stops. This can't be changed after the *Spinner has been constructed. + // + // Please note, if the program crashes or is killed you may need to reset + // your terminal for the cursor to appear again. HideCursor bool // ColorAll describes whether to color everything (all) or just the spinner - // character(s). This cannot be changed. + // character(s). This cannot be changed after the *Spinner has been + // constructed. ColorAll bool // Colors are the colors used for the different printed messages. This @@ -109,8 +120,9 @@ type Config struct { // Prefix is the string printed immediately before the spinner. Prefix string - // Suffix is the string printed immediately after the spinner. It's - // recommended that this string starts with an space ` ` character. + // Suffix is the string printed immediately after the spinner and before the + // message. It's recommended that this string starts with an space ` ` + // character. Suffix string // SuffixAutoColon configures whether the spinner adds a colon after the @@ -130,7 +142,7 @@ type Config struct { StopMessage string // StopCharacter is spinner character used when Stop() is called. - // Recommended character is ✓. + // Recommended character is ✓, and can be more than just one character. StopCharacter string // StopColors are the colors used for the Stop() printed line. This respects @@ -140,8 +152,9 @@ type Config struct { // StopFailMessage is the message used when StopFail() is called. StopFailMessage string - // StopFailCharacter is the spinner character used when StopFail() is called. - // Recommended character is ✗. + // StopFailCharacter is the spinner character used when StopFail() is + // called. Recommended character is ✗, and can be more than just one + // character. StopFailCharacter string // StopFailColors are the colors used for the StopFail() printed line. This @@ -149,9 +162,9 @@ type Config struct { StopFailColors []string } -// Spinner is the struct type representing a spinner. It's configured via the -// Config type, and controlled via its methods. Some configuration can also be -// updated via methods. +// Spinner is a type representing an animated CLi terminal spinner. It's +// configured via the Config struct type, and controlled via its methods. Some +// of its configuration can also be updated via methods. // // Note: You need to use New() to construct a *Spinner. type Spinner struct { @@ -287,8 +300,8 @@ func (s *Spinner) notifyDataChange() { } } -// SpinnerStatus describes the status of the spinner. See the possible constant -// values. +// SpinnerStatus describes the status of the spinner. See the package constants +// for the list of all possible statuses type SpinnerStatus uint32 const ( @@ -310,7 +323,7 @@ const ( // SpinnerPaused is a paused spinner SpinnerPaused - // SpinnerUnpausing is a unpausing spinner + // SpinnerUnpausing is an unpausing spinner SpinnerUnpausing ) @@ -342,7 +355,7 @@ func (s *Spinner) Status() SpinnerStatus { return SpinnerStatus(atomic.LoadUint32(s.status)) } -// Start begins the spinner on the Writer in the Config provided to New(). Onnly +// Start begins the spinner on the Writer in the Config provided to New(). Only // possible error is if the spinner is already runninng. func (s *Spinner) Start() error { // move us to the starting state @@ -375,8 +388,8 @@ func (s *Spinner) Start() error { } // Pause puts the spinner in a state where it no longer animates or renders -// updates to data. This function blocks until the spinner's internal goroutine -// enters a paused state. +// updates to data. This function blocks until the spinner's internal painting +// goroutine enters a paused state. // // If you want to make a few configuration changes and have them to appear at // the same time, like changing the suffix, message, and color, you can Pause() @@ -404,7 +417,7 @@ func (s *Spinner) Pause() error { // Unpause returns the spinner back to a running state after pausing. See // Pause() documentation for more detail. This function blocks until the -// spinner's internal goroutine acknowledges the request to unpause. +// spinner's internal painting goroutine acknowledges the request to unpause. // // If the spinner is not paused this returns an error. func (s *Spinner) Unpause() error { @@ -775,8 +788,8 @@ func (s *Spinner) Prefix(prefix string) { s.notifyDataChange() } -// Suffix updates the Suffix after the spinner character. It's recommended that -// this start with an empty space. +// Suffix updates the Suffix printed after the spinner character and before the +// message. It's recommended that this start with an empty space. func (s *Spinner) Suffix(suffix string) { s.mu.Lock() defer s.mu.Unlock() @@ -786,7 +799,7 @@ func (s *Spinner) Suffix(suffix string) { s.notifyDataChange() } -// Message updates the Message displayed after he suffix. +// Message updates the Message displayed after the suffix. func (s *Spinner) Message(message string) { s.mu.Lock() defer s.mu.Unlock() @@ -829,6 +842,9 @@ func (s *Spinner) StopMessage(message string) { // StopColors updates the colors used for the stop message. See Colors() method // documentation for more context. +// +// StopFailColors() is the method to control the colors in the failed stop +// message. func (s *Spinner) StopColors(colors ...string) error { colorFn, err := colorFunc(colors...) if err != nil { @@ -845,8 +861,8 @@ func (s *Spinner) StopColors(colors ...string) error { return nil } -// StopCharacter sets the single "character" to use for the spinner. Recommended -// character is ✓. +// StopCharacter sets the single "character" to use for the spinner when +// stopping. Recommended character is ✓. func (s *Spinner) StopCharacter(char string) { n := runewidth.StringWidth(char) @@ -890,8 +906,8 @@ func (s *Spinner) StopFailColors(colors ...string) error { return nil } -// StopFailCharacter sets the single "character" to use for the spinner. Recommended -// character is ✗. +// StopFailCharacter sets the single "character" to use for the spinner when +// stopping for a failure. Recommended character is ✗. func (s *Spinner) StopFailCharacter(char string) { n := runewidth.StringWidth(char) @@ -908,7 +924,7 @@ func (s *Spinner) StopFailCharacter(char string) { } // CharSet updates the set of characters (strings) to use for the spinner. You -// can provide your own, or use one from the CharSets variable. +// can provide your own, or use one from the yacspin.CharSets variable. // // The character sets available in the CharSets variable are from the // https://github.com/briandowns/spinner project.