From f8caeef1958d0c6e098e6e16e942f46aa69df4ec Mon Sep 17 00:00:00 2001 From: Dieter Eickstaedt Date: Fri, 30 Jun 2023 15:17:09 +0200 Subject: [PATCH] feat: Timeout for Spin Command (#385) * feat: Timeout for Spin Command * fix: spin timeout --------- Co-authored-by: Maas Lalani --- spin/command.go | 3 ++- spin/options.go | 19 ++++++++++++------- spin/spin.go | 22 ++++++++++++++++++++-- 3 files changed, 34 insertions(+), 10 deletions(-) diff --git a/spin/command.go b/spin/command.go index 082ef3788..9482b7fae 100644 --- a/spin/command.go +++ b/spin/command.go @@ -26,7 +26,8 @@ func (o Options) Run() error { title: o.TitleStyle.ToLipgloss().Render(o.Title), command: o.Command, align: o.Align, - showOutput: o.ShowOutput && isTTY, + timeout: o.Timeout, + hasTimeout: o.Timeout > 0, } p := tea.NewProgram(m, tea.WithOutput(os.Stderr)) mm, err := p.Run() diff --git a/spin/options.go b/spin/options.go index e277fb5d0..9b55c4aa5 100644 --- a/spin/options.go +++ b/spin/options.go @@ -1,15 +1,20 @@ package spin -import "github.com/charmbracelet/gum/style" +import ( + "time" + + "github.com/charmbracelet/gum/style" +) // Options is the customization options for the spin command. type Options struct { Command []string `arg:"" help:"Command to run"` - ShowOutput bool `help:"Show or pipe output of command during execution" default:"false" env:"GUM_SPIN_SHOW_OUTPUT"` - Spinner string `help:"Spinner type" short:"s" type:"spinner" enum:"line,dot,minidot,jump,pulse,points,globe,moon,monkey,meter,hamburger" default:"dot" env:"GUM_SPIN_SPINNER"` - SpinnerStyle style.Styles `embed:"" prefix:"spinner." set:"defaultForeground=212" envprefix:"GUM_SPIN_SPINNER_"` - Title string `help:"Text to display to user while spinning" default:"Loading..." env:"GUM_SPIN_TITLE"` - TitleStyle style.Styles `embed:"" prefix:"title." envprefix:"GUM_SPIN_TITLE_"` - Align string `help:"Alignment of spinner with regard to the title" short:"a" type:"align" enum:"left,right" default:"left" env:"GUM_SPIN_ALIGN"` + ShowOutput bool `help:"Show or pipe output of command during execution" default:"false" env:"GUM_SPIN_SHOW_OUTPUT"` + Spinner string `help:"Spinner type" short:"s" type:"spinner" enum:"line,dot,minidot,jump,pulse,points,globe,moon,monkey,meter,hamburger" default:"dot" env:"GUM_SPIN_SPINNER"` + SpinnerStyle style.Styles `embed:"" prefix:"spinner." set:"defaultForeground=212" envprefix:"GUM_SPIN_SPINNER_"` + Title string `help:"Text to display to user while spinning" default:"Loading..." env:"GUM_SPIN_TITLE"` + TitleStyle style.Styles `embed:"" prefix:"title." envprefix:"GUM_SPIN_TITLE_"` + Align string `help:"Alignment of spinner with regard to the title" short:"a" type:"align" enum:"left,right" default:"left" env:"GUM_SPIN_ALIGN"` + Timeout time.Duration `help:"Timeout until spin command aborts" default:"0" env:"GUM_SPIN_TIMEOUT"` } diff --git a/spin/spin.go b/spin/spin.go index 2648b9a76..48f6f7faf 100644 --- a/spin/spin.go +++ b/spin/spin.go @@ -17,6 +17,10 @@ package spin import ( "os/exec" "strings" + "time" + + "github.com/charmbracelet/gum/internal/exit" + "github.com/charmbracelet/gum/timeout" "github.com/charmbracelet/bubbles/spinner" tea "github.com/charmbracelet/bubbletea" @@ -31,6 +35,8 @@ type model struct { status int stdout string showOutput bool + timeout time.Duration + hasTimeout bool } var outbuf strings.Builder @@ -71,14 +77,19 @@ func (m model) Init() tea.Cmd { return tea.Batch( m.spinner.Tick, commandStart(m.command), + timeout.Init(m.timeout, nil), ) } func (m model) View() string { + var str string + if m.hasTimeout { + str = timeout.Str(m.timeout) + } var header string if m.align == "left" { - header = m.spinner.View() + " " + m.title + header = m.spinner.View() + str + " " + m.title } else { - header = m.title + " " + m.spinner.View() + header = str + " " + m.title + " " + m.spinner.View() } if !m.showOutput { return header @@ -89,6 +100,13 @@ func (m model) View() string { func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var cmd tea.Cmd switch msg := msg.(type) { + case timeout.TickTimeoutMsg: + if msg.TimeoutValue <= 0 { + m.status = exit.StatusAborted + return m, tea.Quit + } + m.timeout = msg.TimeoutValue + return m, timeout.Tick(msg.TimeoutValue, msg.Data) case finishCommandMsg: m.stdout = msg.stdout m.status = msg.status