diff --git a/pkg/gui/pty.go b/pkg/gui/pty.go index dbf968048..e10ea8ec7 100644 --- a/pkg/gui/pty.go +++ b/pkg/gui/pty.go @@ -100,6 +100,7 @@ func (gui *Gui) newPtyTask(view *gocui.View, cmd *exec.Cmd, prefix string) error cols, rows := gui.desiredPtySize(view) var p oscommands.Pty + var fallbackPipe io.ReadCloser start := func() (tasks.Cmd, io.Reader) { // The pty (and pager) wrap to this width; apply it here, on the // task's goroutine once the previous task has stopped, so it doesn't @@ -109,7 +110,11 @@ func (gui *Gui) newPtyTask(view *gocui.View, cmd *exec.Cmd, prefix string) error sp, err := oscommands.StartPty(cmd, cols, rows) if err != nil { gui.c.Log.Error(err) - return tasks.ExecCmd{Cmd: cmd}, nil + // Fall back to running the command without a pty: the pager is + // lost, but the command's output still renders. + execCmd, pipe := startCmdWithPipe(cmd, gui.c.Log) + fallbackPipe = pipe + return execCmd, pipe } p = sp.Pty @@ -125,6 +130,10 @@ func (gui *Gui) newPtyTask(view *gocui.View, cmd *exec.Cmd, prefix string) error if p != nil { p.Close() } + if fallbackPipe != nil { + fallbackPipe.Close() + fallbackPipe = nil + } delete(gui.viewPtmxMap, view.Name()) gui.Mutexes.PtyMutex.Unlock() } diff --git a/pkg/gui/tasks_adapter.go b/pkg/gui/tasks_adapter.go index 3eb446a90..3dce93874 100644 --- a/pkg/gui/tasks_adapter.go +++ b/pkg/gui/tasks_adapter.go @@ -52,12 +52,15 @@ func (gui *Gui) newCmdTask(view *gocui.View, cmd *exec.Cmd, prefix string) error // startCmdWithPipe starts cmd with its stdout and stderr going to a single // pipe, and returns the command along with the pipe's read end, in the shape -// that NewCmdTask expects from its start func. +// that NewCmdTask expects from its start func. It never returns a nil reader, +// because NewCmdTask's scanner panics on one: when the pipe can't be created +// the command isn't started at all, and an empty reader is returned so that +// the task shuts down cleanly with the error in the log. func startCmdWithPipe(cmd *exec.Cmd, log *logrus.Entry) (tasks.Cmd, io.ReadCloser) { r, err := cmd.StdoutPipe() if err != nil { log.Error(err) - r = nil + return tasks.ExecCmd{Cmd: cmd}, io.NopCloser(strings.NewReader("")) } cmd.Stderr = cmd.Stdout diff --git a/pkg/gui/tasks_adapter_test.go b/pkg/gui/tasks_adapter_test.go index 7821f835a..48c1bb45f 100644 --- a/pkg/gui/tasks_adapter_test.go +++ b/pkg/gui/tasks_adapter_test.go @@ -20,8 +20,5 @@ func TestStartCmdWithPipeWhenPipeCannotBeCreated(t *testing.T) { // NewCmdTask's scanner panics on a nil reader, so startCmdWithPipe must // not return one even when it can't create the pipe. - /* EXPECTED: assert.NotNil(t, r) - ACTUAL: */ - assert.Nil(t, r) }