forked from Mirrors/bubbletea
feat: kill the program and restore terminal state
* Add Kill() to force kill the program from the outside * Prevent renderer channel from being closed multiple times
This commit is contained in:
parent
74f0972e4e
commit
42cd4c3191
|
@ -32,6 +32,7 @@ type standardRenderer struct {
|
||||||
lastRender string
|
lastRender string
|
||||||
linesRendered int
|
linesRendered int
|
||||||
useANSICompressor bool
|
useANSICompressor bool
|
||||||
|
once sync.Once
|
||||||
|
|
||||||
// essentially whether or not we're using the full size of the terminal
|
// essentially whether or not we're using the full size of the terminal
|
||||||
altScreenActive bool
|
altScreenActive bool
|
||||||
|
@ -72,7 +73,9 @@ func (r *standardRenderer) start() {
|
||||||
func (r *standardRenderer) stop() {
|
func (r *standardRenderer) stop() {
|
||||||
r.flush()
|
r.flush()
|
||||||
clearLine(r.out)
|
clearLine(r.out)
|
||||||
close(r.done)
|
r.once.Do(func() {
|
||||||
|
close(r.done)
|
||||||
|
})
|
||||||
|
|
||||||
if r.useANSICompressor {
|
if r.useANSICompressor {
|
||||||
if w, ok := r.out.(io.WriteCloser); ok {
|
if w, ok := r.out.(io.WriteCloser); ok {
|
||||||
|
@ -84,7 +87,9 @@ func (r *standardRenderer) stop() {
|
||||||
// kill halts the renderer. The final frame will not be rendered.
|
// kill halts the renderer. The final frame will not be rendered.
|
||||||
func (r *standardRenderer) kill() {
|
func (r *standardRenderer) kill() {
|
||||||
clearLine(r.out)
|
clearLine(r.out)
|
||||||
close(r.done)
|
r.once.Do(func() {
|
||||||
|
close(r.done)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// listen waits for ticks on the ticker, or a signal to stop the renderer.
|
// listen waits for ticks on the ticker, or a signal to stop the renderer.
|
||||||
|
|
25
tea.go
25
tea.go
|
@ -97,6 +97,8 @@ type Program struct {
|
||||||
// is on by default.
|
// is on by default.
|
||||||
CatchPanics bool
|
CatchPanics bool
|
||||||
|
|
||||||
|
killc chan bool
|
||||||
|
|
||||||
console console.Console
|
console console.Console
|
||||||
|
|
||||||
// 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
|
||||||
|
@ -248,6 +250,7 @@ func NewProgram(model Model, opts ...ProgramOption) *Program {
|
||||||
input: os.Stdin,
|
input: os.Stdin,
|
||||||
msgs: make(chan Msg),
|
msgs: make(chan Msg),
|
||||||
CatchPanics: true,
|
CatchPanics: true,
|
||||||
|
killc: make(chan bool, 1),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply all options to the program.
|
// Apply all options to the program.
|
||||||
|
@ -486,6 +489,8 @@ func (p *Program) StartReturningModel() (Model, error) {
|
||||||
// Handle updates and draw.
|
// Handle updates and draw.
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
case <-p.killc:
|
||||||
|
return nil, nil
|
||||||
case err := <-errs:
|
case err := <-errs:
|
||||||
cancelContext()
|
cancelContext()
|
||||||
waitForGoroutines(cancelReader.Cancel())
|
waitForGoroutines(cancelReader.Cancel())
|
||||||
|
@ -577,13 +582,25 @@ func (p *Program) Quit() {
|
||||||
p.Send(Quit())
|
p.Send(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.
|
||||||
|
//
|
||||||
|
// This method is currently provisional. The method signature may alter
|
||||||
|
// slightly, or it may be removed in a future version of this package.
|
||||||
|
func (p *Program) Kill() {
|
||||||
|
p.killc <- true
|
||||||
|
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
|
||||||
// to its original state.
|
// to its original state.
|
||||||
func (p *Program) shutdown(kill bool) {
|
func (p *Program) shutdown(kill bool) {
|
||||||
if kill {
|
if p.renderer != nil {
|
||||||
p.renderer.kill()
|
if kill {
|
||||||
} else {
|
p.renderer.kill()
|
||||||
p.renderer.stop()
|
} else {
|
||||||
|
p.renderer.stop()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
p.ExitAltScreen()
|
p.ExitAltScreen()
|
||||||
p.DisableMouseCellMotion()
|
p.DisableMouseCellMotion()
|
||||||
|
|
Loading…
Reference in New Issue