Documentation
¶
Overview ¶
Package runner runs an interactive subprocess from inside a Bubble Tea program: it suspends the TUI (releasing the terminal so the subprocess can take over stdin/stdout/stderr), executes the command, then re-enters the alt-screen once the subprocess exits.
Use it for editors ($EDITOR), pagers (less, man), full-screen TUIs (htop, k9s), or one-shot interactive commands (ssh, kubectl exec). For the duration of the run the TUI is fully suspended — the subprocess owns the terminal.
Usage:
// dispatch from a screen's Update on some key:
cmd := exec.Command(os.Getenv("EDITOR"), "/tmp/scratch")
return s, runner.Run(cmd)
// receive the result on a later Update tick:
case runner.Result:
s.last = msg // msg.Cmd.ProcessState is populated; msg.Err is the run error
Index ¶
- func Capture(cmd *exec.Cmd) tea.Cmd
- func CaptureWith(opts CaptureOptions) tea.Cmd
- func Kill(m CaptureStarted) error
- func Next(msg tea.Msg) tea.Cmd
- func Run(cmd *exec.Cmd) tea.Cmd
- func RunWith(opts Options) tea.Cmd
- func RunWithNotice(cmd *exec.Cmd, notice string) tea.Cmd
- type CaptureOptions
- type CaptureStarted
- type Captured
- type CapturedLine
- type Options
- type Result
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Capture ¶ added in v0.19.0
Capture runs cmd without handing the terminal over, streaming its stdout and stderr back as messages while the TUI stays live.
This is the counterpart to Run, not a mode of it. Run suspends the program and gives the subprocess the real TTY, which is right for an editor, a pager, or htop — and which means the output is gone the moment the TUI repaints. Capture is for the other kind of subprocess: the one whose output you actually want to read. Teeing a full-screen program would be meaningless, so the two cannot be the same call.
The message sequence is CaptureStarted, then a CapturedLine per line, then exactly one Captured. Each message carries the handle needed to ask for the next one — see Next — so the consumer drives the read at its own pace:
case runner.CaptureStarted, runner.CapturedLine:
return s, runner.Next(msg)
The app shell does this for you when app.Options.OutputKey is set, and forwards every message on to the active screen besides. Nothing here depends on tuilib: the shell translates these messages into log records, because the log format, the source attribution and the read-marker are shell knowledge, not runner knowledge.
Backpressure is real and deliberate: the stream buffers a bounded number of lines and a consumer that stops calling Next will eventually stall the subprocess rather than grow memory without limit.
func CaptureWith ¶ added in v0.19.0
func CaptureWith(opts CaptureOptions) tea.Cmd
CaptureWith runs a subprocess with the given options. See Capture.
func Kill ¶ added in v0.19.0
func Kill(m CaptureStarted) error
Kill stops the subprocess behind a CaptureStarted, and everything it spawned.
The "and everything it spawned" is the part that matters. Captures are usually a shell wrapping a build, and killing the shell alone leaves the compiler running — still writing into a pipe nobody reads, with no handle left to stop it by. On Unix the whole process group is signalled; see setProcessGroup, and capture_windows.go for what Windows can't do here.
Safe to call on a run that never started or has already exited — both report nil, since in either case there is nothing left to stop.
func Next ¶ added in v0.19.0
Next returns the command that reads the next message from the capture that msg belongs to, or nil for anything else — including Captured, after which there is nothing more to read.
func Run ¶
Run returns a tea.Cmd that suspends the program, runs cmd connected to the controlling terminal, and posts a Result when the subprocess exits. The screen is cleared before the subprocess starts (use RunWith with NoClear=true to opt out).
Plumbing the runner takes care of:
- Stdin/Stdout/Stderr default to os.Stdin/Stdout/Stderr (real TTY file descriptors) when not already set, so the subprocess gets direct terminal access and TIOCGWINSZ works.
- LINES and COLUMNS env vars are populated from the current terminal size, as a fallback for ncurses-style programs that miss the post-resume SIGWINCH on some terminal emulators (htop, top, less are the usual suspects).
Types ¶
type CaptureOptions ¶ added in v0.19.0
type CaptureOptions struct {
// Cmd is the subprocess to run. Required.
Cmd *exec.Cmd
// Label names the run in the log and in the kill picker. Defaults to
// the command's base name, which is also what the log uses as the
// line's Source — for captured output the honest answer to "what
// produced this line" is the command, not the screen that launched it.
Label string
}
CaptureOptions configures CaptureWith.
type CaptureStarted ¶ added in v0.19.0
type CaptureStarted struct {
RunID int64
Label string
Cmd *exec.Cmd
// contains filtered or unexported fields
}
CaptureStarted is delivered when a capture begins. It carries the *exec.Cmd so a consumer can retain a kill handle: nothing else in the sequence offers one, and by the time Captured arrives there is nothing left to signal.
type Captured ¶ added in v0.19.0
type Captured struct {
RunID int64
Label string
Cmd *exec.Cmd
Err error
// contains filtered or unexported fields
}
Captured is delivered once, after the last CapturedLine, when the subprocess has exited. Err is the *exec.ExitError for a non-zero exit, or the start error when the process never ran.
type CapturedLine ¶ added in v0.19.0
type CapturedLine struct {
RunID int64
Label string
Text string
Stderr bool
// contains filtered or unexported fields
}
CapturedLine is one line of subprocess output.
type Options ¶
type Options struct {
// Cmd is the subprocess to run. Required.
Cmd *exec.Cmd
// Notice, when non-empty, is printed once to stderr after the TUI
// suspends and before the subprocess starts. Use it for slow handoffs
// (kubectl exec, ssh, anything with a perceptible connect latency) so
// the user sees feedback instead of a blank gap. The subprocess is
// free to clear the screen on startup; that's fine, the goal is
// feedback during the handoff, not a persistent banner.
Notice string
// NoClear suppresses the screen clear that normally precedes the
// subprocess. By default the terminal is cleared so the alt-screen
// exit doesn't leave TUI artifacts visible during commands that
// don't repaint (sh -c, echo, short scripts). Set NoClear=true to
// preserve whatever was on the normal screen prior to the TUI.
NoClear bool
}
Options configures RunWith. The zero value clears the screen and prints no notice — the right defaults for typical interactive subprocesses.