github.com/Racer159/jackal@v0.32.7-0.20240401174413-0bd2339e4f2e/src/pkg/message/spinner.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // SPDX-FileCopyrightText: 2021-Present The Jackal Authors 3 4 // Package message provides a rich set of functions for displaying messages to the user. 5 package message 6 7 import ( 8 "bufio" 9 "bytes" 10 "fmt" 11 "strings" 12 13 "github.com/pterm/pterm" 14 ) 15 16 var activeSpinner *Spinner 17 18 var sequence = []string{` ⠋ `, ` ⠙ `, ` ⠹ `, ` ⠸ `, ` ⠼ `, ` ⠴ `, ` ⠦ `, ` ⠧ `, ` ⠇ `, ` ⠏ `} 19 20 // Spinner is a wrapper around pterm.SpinnerPrinter. 21 type Spinner struct { 22 spinner *pterm.SpinnerPrinter 23 startText string 24 termWidth int 25 preserveWrites bool 26 } 27 28 // NewProgressSpinner creates a new progress spinner. 29 func NewProgressSpinner(format string, a ...any) *Spinner { 30 if activeSpinner != nil { 31 activeSpinner.Updatef(format, a...) 32 debugPrinter(2, "Active spinner already exists") 33 return activeSpinner 34 } 35 36 var spinner *pterm.SpinnerPrinter 37 text := pterm.Sprintf(format, a...) 38 if NoProgress { 39 Info(text) 40 } else { 41 spinner, _ = pterm.DefaultSpinner. 42 WithRemoveWhenDone(false). 43 // Src: https://github.com/gernest/wow/blob/master/spin/spinners.go#L335 44 WithSequence(sequence...). 45 Start(text) 46 } 47 48 activeSpinner = &Spinner{ 49 spinner: spinner, 50 startText: text, 51 termWidth: pterm.GetTerminalWidth(), 52 } 53 54 return activeSpinner 55 } 56 57 // EnablePreserveWrites enables preserving writes to the terminal. 58 func (p *Spinner) EnablePreserveWrites() { 59 p.preserveWrites = true 60 } 61 62 // DisablePreserveWrites disables preserving writes to the terminal. 63 func (p *Spinner) DisablePreserveWrites() { 64 p.preserveWrites = false 65 } 66 67 // Write the given text to the spinner. 68 func (p *Spinner) Write(raw []byte) (int, error) { 69 size := len(raw) 70 if NoProgress { 71 if p.preserveWrites { 72 pterm.Printfln(" %s", string(raw)) 73 } 74 75 return size, nil 76 } 77 78 // Split the text into lines and update the spinner for each line. 79 scanner := bufio.NewScanner(bytes.NewReader(raw)) 80 scanner.Split(bufio.ScanLines) 81 for scanner.Scan() { 82 // Only be fancy if preserve writes is enabled. 83 if p.preserveWrites { 84 text := pterm.Sprintf(" %s", scanner.Text()) 85 pterm.Fprinto(p.spinner.Writer, strings.Repeat(" ", pterm.GetTerminalWidth())) 86 pterm.Fprintln(p.spinner.Writer, text) 87 } else { 88 // Otherwise just update the spinner text. 89 p.spinner.UpdateText(scanner.Text()) 90 } 91 } 92 93 return size, nil 94 } 95 96 // Updatef updates the spinner text. 97 func (p *Spinner) Updatef(format string, a ...any) { 98 if NoProgress { 99 debugPrinter(2, fmt.Sprintf(format, a...)) 100 return 101 } 102 103 pterm.Fprinto(p.spinner.Writer, strings.Repeat(" ", pterm.GetTerminalWidth())) 104 text := pterm.Sprintf(format, a...) 105 p.spinner.UpdateText(text) 106 } 107 108 // Stop the spinner. 109 func (p *Spinner) Stop() { 110 if p.spinner != nil && p.spinner.IsActive { 111 _ = p.spinner.Stop() 112 } 113 activeSpinner = nil 114 } 115 116 // Success prints a success message and stops the spinner. 117 func (p *Spinner) Success() { 118 p.Successf(p.startText) 119 } 120 121 // Successf prints a success message with the spinner and stops it. 122 func (p *Spinner) Successf(format string, a ...any) { 123 text := pterm.Sprintf(format, a...) 124 if p.spinner != nil { 125 p.spinner.Success(text) 126 } else { 127 Info(text) 128 } 129 p.Stop() 130 } 131 132 // Errorf prints an error message with the spinner. 133 func (p *Spinner) Errorf(err error, format string, a ...any) { 134 Warnf(format, a...) 135 debugPrinter(2, err) 136 } 137 138 // Fatal calls message.Fatalf with the given error. 139 func (p *Spinner) Fatal(err error) { 140 p.Fatalf(err, p.startText) 141 } 142 143 // Fatalf calls message.Fatalf with the given error and format. 144 func (p *Spinner) Fatalf(err error, format string, a ...any) { 145 if p.spinner != nil { 146 p.spinner.RemoveWhenDone = true 147 _ = p.spinner.Stop() 148 activeSpinner = nil 149 } 150 Fatalf(err, format, a...) 151 }