Skip to content

Commit

Permalink
feat: suspend on ctrl+z
Browse files Browse the repository at this point in the history
... unless the WithoutJobControl() option is specified.
  • Loading branch information
knz committed Oct 7, 2022
1 parent 4e68f4f commit 5dbda96
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 0 deletions.
15 changes: 15 additions & 0 deletions commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,3 +170,18 @@ func Sequentially(cmds ...Cmd) Cmd {
return nil
}
}

// Suspend causes the process to suspend itself. By default, this
// command is automatically sent when Ctrl+Z is pressed. When the
// WithoutJobControl program option is specified, Ctrl+Z becomes
// controllable by the pgoram and the program can choose to send the
// Suspend command upon a different key input or some other event.
func Suspend() Cmd {
return func() Msg {
return suspendMsg{}
}
}

// suspendMsg is the internal message used to suspend the process.
// You can send a suspendMsg with Suspend.
type suspendMsg struct{}
19 changes: 19 additions & 0 deletions exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,22 @@ func (p *Program) exec(c ExecCommand, fn ExecCallback) {
go p.Send(fn(err))
}
}

type fnAsCommand func()

var _ ExecCommand = fnAsCommand(nil)

// Run is part of the tea.ExecCommand interface.
func (d fnAsCommand) Run() error {
d()
return nil
}

// SetStdin is part of the tea.ExecCommand interface.
func (d fnAsCommand) SetStdin(io.Reader) {}

// SetStdout is part of the tea.ExecCommand interface.
func (d fnAsCommand) SetStdout(io.Writer) {}

// SetStderr is part of the tea.ExecCommand interface.
func (d fnAsCommand) SetStderr(io.Writer) {}
9 changes: 9 additions & 0 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,12 @@ func WithANSICompressor() ProgramOption {
p.startupOptions |= withANSICompressor
}
}

// WithoutJobControl disables support for suspending the process when
// Ctrl+Z is pressed. By default, Ctrl+Z causes SIGTSTPT to be
// emitted, to support job control in the surrounding unix shell.
func WithoutJobControl() ProgramOption {
return func(p *Program) {
p.disableSuspendOnCtrlZ = true
}
}
24 changes: 24 additions & 0 deletions tea.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@ type Program struct {
// is on by default.
CatchPanics bool

// disableSuspendOnCtrlZ removes direct bubbletea support for Ctrl+Z
// (suspend process).
disableSuspendOnCtrlZ bool

ignoreSignals bool

killc chan bool
Expand Down Expand Up @@ -413,6 +417,26 @@ func (p *Program) StartReturningModel() (Model, error) {
}
}
}()

case KeyMsg:
if !p.disableSuspendOnCtrlZ && msg.Type == KeyCtrlZ && !msg.Alt {
cmds <- Suspend()
continue
}

case suspendMsg:
if sigTermStop != 0 {
// NB: this blocks.
cmds <- Exec(fnAsCommand(func() {
pr, err := os.FindProcess(os.Getpid())
if err != nil {
// No-op.
return
}
_ = pr.Signal(sigTermStop)
}), nil)
}
continue
}

// Process internal messages for the renderer.
Expand Down

0 comments on commit 5dbda96

Please sign in to comment.