aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/cli/internal/spinner
diff options
context:
space:
mode:
Diffstat (limited to 'cli/internal/spinner')
-rw-r--r--cli/internal/spinner/spinner.go89
1 files changed, 89 insertions, 0 deletions
diff --git a/cli/internal/spinner/spinner.go b/cli/internal/spinner/spinner.go
new file mode 100644
index 0000000..8ce6b4a
--- /dev/null
+++ b/cli/internal/spinner/spinner.go
@@ -0,0 +1,89 @@
+package spinner
+
+import (
+ "context"
+ "fmt"
+ "io"
+ "time"
+
+ "github.com/mitchellh/cli"
+ progressbar "github.com/schollz/progressbar/v3"
+ "github.com/vercel/turbo/cli/internal/ui"
+)
+
+// getWriterAndColor unwraps cli.Ui instances until it gets to a BasicUi.
+// If it happens to spot a ColoredUi along the way, it marks that color is
+// enabled.
+func getWriterAndColor(terminal cli.Ui, useColor bool) (io.Writer, bool) {
+ switch terminal := terminal.(type) {
+ case *cli.BasicUi:
+ return terminal.Writer, useColor
+ case *cli.ColoredUi:
+ return getWriterAndColor(terminal.Ui, true)
+ case *cli.ConcurrentUi:
+ return getWriterAndColor(terminal.Ui, useColor)
+ case *cli.PrefixedUi:
+ return getWriterAndColor(terminal.Ui, useColor)
+ case *cli.MockUi:
+ return terminal.OutputWriter, false
+ default:
+ panic(fmt.Sprintf("unknown Ui: %v", terminal))
+ }
+}
+
+// WaitFor runs fn, and prints msg to the terminal if it takes longer
+// than initialDelay to complete. Depending on the terminal configuration, it may
+// display a single instance of msg, or an infinite spinner, updated every 250ms.
+func WaitFor(ctx context.Context, fn func(), terminal cli.Ui, msg string, initialDelay time.Duration) error {
+ doneCh := make(chan struct{})
+ go func() {
+ fn()
+ close(doneCh)
+ }()
+ if ui.IsTTY {
+ select {
+ case <-ctx.Done():
+ return nil
+ case <-time.After(initialDelay):
+ writer, useColor := getWriterAndColor(terminal, false)
+ bar := progressbar.NewOptions(
+ -1,
+ progressbar.OptionEnableColorCodes(useColor),
+ progressbar.OptionSetDescription(fmt.Sprintf("[yellow]%v[reset]", msg)),
+ progressbar.OptionSpinnerType(14),
+ progressbar.OptionSetWriter(writer),
+ )
+ for {
+ select {
+ case <-doneCh:
+ err := bar.Finish()
+ terminal.Output("")
+ return err
+ case <-time.After(250 * time.Millisecond):
+ if err := bar.Add(1); err != nil {
+ return err
+ }
+ case <-ctx.Done():
+ return nil
+ }
+ }
+ case <-doneCh:
+ return nil
+ }
+ } else {
+ // wait for the timeout before displaying a message, even with no tty
+ select {
+ case <-ctx.Done():
+ return nil
+ case <-doneCh:
+ return nil
+ case <-time.After(initialDelay):
+ terminal.Output(msg)
+ }
+ select {
+ case <-ctx.Done():
+ case <-doneCh:
+ }
+ return nil
+ }
+}