Skip to content

Commit

Permalink
fix: kill should result in Start returning an error
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
muesli committed Oct 12, 2022
1 parent 6b77c8f commit 17bd2bf
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 15 deletions.
31 changes: 18 additions & 13 deletions tea.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ package tea

import (
"context"
"errors"
"fmt"
"io"
"os"
Expand All @@ -27,6 +28,9 @@ import (
"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
// function and, henceforth, the UI.
type Msg interface{}
Expand Down Expand Up @@ -91,7 +95,8 @@ type Program struct {
// treated as bits. These options can be set via various ProgramOptions.
startupOptions startupOptions

ctx context.Context
ctx context.Context
cancel context.CancelFunc

msgs chan Msg
errs chan error
Expand All @@ -111,8 +116,6 @@ type Program struct {
altScreenWasActive bool
ignoreSignals bool

killc chan bool

// 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.
// When the program exits this will be restored.
Expand All @@ -138,7 +141,6 @@ func NewProgram(model Model, opts ...ProgramOption) *Program {
initialModel: model,
input: os.Stdin,
msgs: make(chan Msg),
killc: make(chan bool, 1),
}

// Apply all options to the program.
Expand Down Expand Up @@ -263,8 +265,8 @@ func (p *Program) handleCommands(cmds chan Cmd) chan struct{} {
func (p *Program) eventLoop(model Model, cmds chan Cmd) (Model, error) {
for {
select {
case <-p.killc:
return nil, nil
case <-p.ctx.Done():
return model, nil

case err := <-p.errs:
return model, err
Expand Down Expand Up @@ -343,9 +345,8 @@ func (p *Program) Run() (Model, error) {
cmds := make(chan Cmd)
p.errs = make(chan error)

var cancelContext context.CancelFunc
p.ctx, cancelContext = context.WithCancel(context.Background())
defer cancelContext()
p.ctx, p.cancel = context.WithCancel(context.Background())
defer p.cancel()

switch {
case p.startupOptions.has(withInputTTY):
Expand Down Expand Up @@ -457,9 +458,13 @@ func (p *Program) Run() (Model, error) {

// Run event loop, handle updates and draw.
model, err := p.eventLoop(model, cmds)
killed := p.ctx.Err() != nil
if killed {
err = ErrProgramKilled
}

// Tear down.
cancelContext()
p.cancel()

// Wait for input loop to finish.
if p.cancelReader.Cancel() {
Expand All @@ -470,7 +475,7 @@ func (p *Program) Run() (Model, error) {
handlers.shutdown()

// Restore terminal state.
p.shutdown(false)
p.shutdown(killed)

return model, err
}
Expand Down Expand Up @@ -517,9 +522,9 @@ func (p *Program) Quit() {

// Kill stops the program immediately and restores the former terminal state.
// The final render that you would normally see when quitting will be skipped.
// [program.Run] returns a [ErrProgramKilled] error.
func (p *Program) Kill() {
p.killc <- true
p.shutdown(true)
p.cancel()
}

// shutdown performs operations to free up resources and restore the terminal
Expand Down
4 changes: 2 additions & 2 deletions tea_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func TestTeaKill(t *testing.T) {
}
}()

if _, err := p.Run(); err != nil {
t.Fatal(err)
if _, err := p.Run(); err != ErrProgramKilled {
t.Fatalf("Expected %v, got %v", ErrProgramKilled, err)
}
}

0 comments on commit 17bd2bf

Please sign in to comment.