forked from Mirrors/bubbletea
fix: kill should result in Start returning an error
This fixes Kill resulting in a final nil model being returned. We can also drop the kill channel and rely on our existing context channel.
This commit is contained in:
parent
fd18c149df
commit
1ed623fdc0
29
tea.go
29
tea.go
|
@ -11,6 +11,7 @@ package tea
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
@ -26,6 +27,9 @@ import (
|
||||||
"golang.org/x/term"
|
"golang.org/x/term"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ErrProgramKilled is returned by [Program.Run] when the program got killed.
|
||||||
|
var ErrProgramKilled = errors.New("program was killed")
|
||||||
|
|
||||||
// Msg contain data from the result of a IO operation. Msgs trigger the update
|
// Msg contain data from the result of a IO operation. Msgs trigger the update
|
||||||
// function and, henceforth, the UI.
|
// function and, henceforth, the UI.
|
||||||
type Msg interface{}
|
type Msg interface{}
|
||||||
|
@ -91,6 +95,7 @@ type Program struct {
|
||||||
startupOptions startupOptions
|
startupOptions startupOptions
|
||||||
|
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
|
cancel context.CancelFunc
|
||||||
|
|
||||||
msgs chan Msg
|
msgs chan Msg
|
||||||
errs chan error
|
errs chan error
|
||||||
|
@ -110,8 +115,6 @@ type Program struct {
|
||||||
altScreenWasActive bool
|
altScreenWasActive bool
|
||||||
ignoreSignals bool
|
ignoreSignals bool
|
||||||
|
|
||||||
killc chan bool
|
|
||||||
|
|
||||||
// Stores the original reference to stdin for cases where input is not a
|
// Stores the original reference to stdin for cases where input is not a
|
||||||
// TTY on windows and we've automatically opened CONIN$ to receive input.
|
// TTY on windows and we've automatically opened CONIN$ to receive input.
|
||||||
// When the program exits this will be restored.
|
// When the program exits this will be restored.
|
||||||
|
@ -137,7 +140,6 @@ func NewProgram(model Model, opts ...ProgramOption) *Program {
|
||||||
initialModel: model,
|
initialModel: model,
|
||||||
input: os.Stdin,
|
input: os.Stdin,
|
||||||
msgs: make(chan Msg),
|
msgs: make(chan Msg),
|
||||||
killc: make(chan bool, 1),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply all options to the program.
|
// Apply all options to the program.
|
||||||
|
@ -262,8 +264,8 @@ func (p *Program) handleCommands(cmds chan Cmd) chan struct{} {
|
||||||
func (p *Program) eventLoop(model Model, cmds chan Cmd) (Model, error) {
|
func (p *Program) eventLoop(model Model, cmds chan Cmd) (Model, error) {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-p.killc:
|
case <-p.ctx.Done():
|
||||||
return nil, nil
|
return model, nil
|
||||||
|
|
||||||
case err := <-p.errs:
|
case err := <-p.errs:
|
||||||
return model, err
|
return model, err
|
||||||
|
@ -342,9 +344,8 @@ func (p *Program) Run() (Model, error) {
|
||||||
cmds := make(chan Cmd)
|
cmds := make(chan Cmd)
|
||||||
p.errs = make(chan error)
|
p.errs = make(chan error)
|
||||||
|
|
||||||
var cancelContext context.CancelFunc
|
p.ctx, p.cancel = context.WithCancel(context.Background())
|
||||||
p.ctx, cancelContext = context.WithCancel(context.Background())
|
defer p.cancel()
|
||||||
defer cancelContext()
|
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case p.startupOptions.has(withInputTTY):
|
case p.startupOptions.has(withInputTTY):
|
||||||
|
@ -455,9 +456,13 @@ func (p *Program) Run() (Model, error) {
|
||||||
|
|
||||||
// Run event loop, handle updates and draw.
|
// Run event loop, handle updates and draw.
|
||||||
model, err := p.eventLoop(model, cmds)
|
model, err := p.eventLoop(model, cmds)
|
||||||
|
killed := p.ctx.Err() != nil
|
||||||
|
if killed {
|
||||||
|
err = ErrProgramKilled
|
||||||
|
}
|
||||||
|
|
||||||
// Tear down.
|
// Tear down.
|
||||||
cancelContext()
|
p.cancel()
|
||||||
|
|
||||||
// Wait for input loop to finish.
|
// Wait for input loop to finish.
|
||||||
if p.cancelReader.Cancel() {
|
if p.cancelReader.Cancel() {
|
||||||
|
@ -468,7 +473,7 @@ func (p *Program) Run() (Model, error) {
|
||||||
handlers.shutdown()
|
handlers.shutdown()
|
||||||
|
|
||||||
// Restore terminal state.
|
// Restore terminal state.
|
||||||
p.shutdown(false)
|
p.shutdown(killed)
|
||||||
|
|
||||||
return model, err
|
return model, err
|
||||||
}
|
}
|
||||||
|
@ -515,9 +520,9 @@ func (p *Program) Quit() {
|
||||||
|
|
||||||
// Kill stops the program immediately and restores the former terminal state.
|
// Kill stops the program immediately and restores the former terminal state.
|
||||||
// The final render that you would normally see when quitting will be skipped.
|
// The final render that you would normally see when quitting will be skipped.
|
||||||
|
// [program.Run] returns a [ErrProgramKilled] error.
|
||||||
func (p *Program) Kill() {
|
func (p *Program) Kill() {
|
||||||
p.killc <- true
|
p.cancel()
|
||||||
p.shutdown(true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// shutdown performs operations to free up resources and restore the terminal
|
// shutdown performs operations to free up resources and restore the terminal
|
||||||
|
|
|
@ -92,8 +92,8 @@ func TestTeaKill(t *testing.T) {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if _, err := p.Run(); err != nil {
|
if _, err := p.Run(); err != ErrProgramKilled {
|
||||||
t.Fatal(err)
|
t.Fatalf("Expected %v, got %v", ErrProgramKilled, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue