| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129 |
- package tea
- import (
- "io"
- "os"
- "os/exec"
- )
- // execMsg is used internally to run an ExecCommand sent with Exec.
- type execMsg struct {
- cmd ExecCommand
- fn ExecCallback
- }
- // Exec is used to perform arbitrary I/O in a blocking fashion, effectively
- // pausing the Program while execution is running and resuming it when
- // execution has completed.
- //
- // Most of the time you'll want to use ExecProcess, which runs an exec.Cmd.
- //
- // For non-interactive i/o you should use a Cmd (that is, a tea.Cmd).
- func Exec(c ExecCommand, fn ExecCallback) Cmd {
- return func() Msg {
- return execMsg{cmd: c, fn: fn}
- }
- }
- // ExecProcess runs the given *exec.Cmd in a blocking fashion, effectively
- // pausing the Program while the command is running. After the *exec.Cmd exists
- // the Program resumes. It's useful for spawning other interactive applications
- // such as editors and shells from within a Program.
- //
- // To produce the command, pass an *exec.Cmd and a function which returns
- // a message containing the error which may have occurred when running the
- // ExecCommand.
- //
- // type VimFinishedMsg struct { err error }
- //
- // c := exec.Command("vim", "file.txt")
- //
- // cmd := ExecProcess(c, func(err error) Msg {
- // return VimFinishedMsg{err: err}
- // })
- //
- // Or, if you don't care about errors, you could simply:
- //
- // cmd := ExecProcess(exec.Command("vim", "file.txt"), nil)
- //
- // For non-interactive i/o you should use a Cmd (that is, a tea.Cmd).
- func ExecProcess(c *exec.Cmd, fn ExecCallback) Cmd {
- return Exec(wrapExecCommand(c), fn)
- }
- // ExecCallback is used when executing an *exec.Command to return a message
- // with an error, which may or may not be nil.
- type ExecCallback func(error) Msg
- // ExecCommand can be implemented to execute things in a blocking fashion in
- // the current terminal.
- type ExecCommand interface {
- Run() error
- SetStdin(io.Reader)
- SetStdout(io.Writer)
- SetStderr(io.Writer)
- }
- // wrapExecCommand wraps an exec.Cmd so that it satisfies the ExecCommand
- // interface so it can be used with Exec.
- func wrapExecCommand(c *exec.Cmd) ExecCommand {
- return &osExecCommand{Cmd: c}
- }
- // osExecCommand is a layer over an exec.Cmd that satisfies the ExecCommand
- // interface.
- type osExecCommand struct{ *exec.Cmd }
- // SetStdin sets stdin on underlying exec.Cmd to the given io.Reader.
- func (c *osExecCommand) SetStdin(r io.Reader) {
- // If unset, have the command use the same input as the terminal.
- if c.Stdin == nil {
- c.Stdin = r
- }
- }
- // SetStdout sets stdout on underlying exec.Cmd to the given io.Writer.
- func (c *osExecCommand) SetStdout(w io.Writer) {
- // If unset, have the command use the same output as the terminal.
- if c.Stdout == nil {
- c.Stdout = w
- }
- }
- // SetStderr sets stderr on the underlying exec.Cmd to the given io.Writer.
- func (c *osExecCommand) SetStderr(w io.Writer) {
- // If unset, use stderr for the command's stderr
- if c.Stderr == nil {
- c.Stderr = w
- }
- }
- // exec runs an ExecCommand and delivers the results to the program as a Msg.
- func (p *Program) exec(c ExecCommand, fn ExecCallback) {
- if err := p.ReleaseTerminal(); err != nil {
- // If we can't release input, abort.
- if fn != nil {
- go p.Send(fn(err))
- }
- return
- }
- c.SetStdin(p.input)
- c.SetStdout(p.output.TTY())
- c.SetStderr(os.Stderr)
- // Execute system command.
- if err := c.Run(); err != nil {
- _ = p.RestoreTerminal() // also try to restore the terminal.
- if fn != nil {
- go p.Send(fn(err))
- }
- return
- }
- // Have the program re-capture input.
- err := p.RestoreTerminal()
- if fn != nil {
- go p.Send(fn(err))
- }
- }
|