From ef0d5420d45ec2a74d4b535b1e7bf2fa23da479f Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Fri, 2 Apr 2021 21:30:39 +1100 Subject: [PATCH] use suspense rather than close the gui when switching to a subprocess --- pkg/app/app.go | 2 +- pkg/gui/commit_message_panel.go | 10 ++----- pkg/gui/commits_panel.go | 3 +- pkg/gui/custom_commands.go | 3 +- pkg/gui/errors.go | 3 -- pkg/gui/files_panel.go | 27 ++++-------------- pkg/gui/git_flow.go | 12 ++++---- pkg/gui/gui.go | 49 ++++++++++++++++++++------------- pkg/gui/rebase_options_panel.go | 5 +--- 9 files changed, 49 insertions(+), 65 deletions(-) diff --git a/pkg/app/app.go b/pkg/app/app.go index 602dffad2..7a53b2be5 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -239,7 +239,7 @@ func (app *App) Run() error { os.Exit(0) } - err := app.Gui.RunWithSubprocesses() + err := app.Gui.RunWithRestarts() return err } diff --git a/pkg/gui/commit_message_panel.go b/pkg/gui/commit_message_panel.go index 983d02af8..35d3b0c80 100644 --- a/pkg/gui/commit_message_panel.go +++ b/pkg/gui/commit_message_panel.go @@ -11,17 +11,13 @@ import ( // runSyncOrAsyncCommand takes the output of a command that may have returned // either no error, an error, or a subprocess to execute, and if a subprocess -// needs to be set on the gui object, it does so, and then returns the error -// the bool returned tells us whether the calling code should continue +// needs to be run, it runs it func (gui *Gui) runSyncOrAsyncCommand(sub *exec.Cmd, err error) (bool, error) { if err != nil { - if err != gui.Errors.ErrSubProcess { - return false, gui.surfaceError(err) - } + return false, gui.surfaceError(err) } if sub != nil { - gui.SubProcess = sub - return false, gui.Errors.ErrSubProcess + return false, gui.runSubprocessWithSuspense(sub) } return true, nil } diff --git a/pkg/gui/commits_panel.go b/pkg/gui/commits_panel.go index af4b9feed..811c8613d 100644 --- a/pkg/gui/commits_panel.go +++ b/pkg/gui/commits_panel.go @@ -263,8 +263,7 @@ func (gui *Gui) handleRenameCommitEditor() error { return gui.surfaceError(err) } if subProcess != nil { - gui.SubProcess = subProcess - return gui.Errors.ErrSubProcess + return gui.runSubprocessWithSuspense(subProcess) } return nil diff --git a/pkg/gui/custom_commands.go b/pkg/gui/custom_commands.go index 1411ec34f..09af7065c 100644 --- a/pkg/gui/custom_commands.go +++ b/pkg/gui/custom_commands.go @@ -60,8 +60,7 @@ func (gui *Gui) handleCustomCommandKeybinding(customCommand config.CustomCommand } if customCommand.Subprocess { - gui.PrepareShellSubProcess(cmdStr) - return nil + return gui.runSubprocessWithSuspense(gui.OSCommand.PrepareShellSubProcess(cmdStr)) } loadingText := customCommand.LoadingText diff --git a/pkg/gui/errors.go b/pkg/gui/errors.go index d8aa7530d..e0f37d3ea 100644 --- a/pkg/gui/errors.go +++ b/pkg/gui/errors.go @@ -5,7 +5,6 @@ import "github.com/go-errors/errors" // SentinelErrors are the errors that have special meaning and need to be checked // by calling functions. The less of these, the better type SentinelErrors struct { - ErrSubProcess error ErrNoFiles error ErrSwitchRepo error ErrRestart error @@ -25,7 +24,6 @@ const UNKNOWN_VIEW_ERROR_MSG = "unknown view" // localising things in the code. func (gui *Gui) GenerateSentinelErrors() { gui.Errors = SentinelErrors{ - ErrSubProcess: errors.New(gui.Tr.RunningSubprocess), ErrNoFiles: errors.New(gui.Tr.NoChangedFiles), ErrSwitchRepo: errors.New("switching repo"), ErrRestart: errors.New("restarting"), @@ -34,7 +32,6 @@ func (gui *Gui) GenerateSentinelErrors() { func (gui *Gui) sentinelErrorsArr() []error { return []error{ - gui.Errors.ErrSubProcess, gui.Errors.ErrNoFiles, gui.Errors.ErrSwitchRepo, gui.Errors.ErrRestart, diff --git a/pkg/gui/files_panel.go b/pkg/gui/files_panel.go index 2c9640f41..59ec172b3 100644 --- a/pkg/gui/files_panel.go +++ b/pkg/gui/files_panel.go @@ -17,7 +17,6 @@ import ( "github.com/jesseduffield/lazygit/pkg/config" "github.com/jesseduffield/lazygit/pkg/gui/filetree" "github.com/jesseduffield/lazygit/pkg/utils" - "github.com/mgutz/str" ) // list panel functions @@ -475,24 +474,9 @@ func (gui *Gui) handleCommitEditorPress() error { return gui.promptToStageAllAndRetry(gui.handleCommitEditorPress) } - gui.PrepareSubProcess("git commit") - return nil -} - -// PrepareSubProcess - prepare a subprocess for execution and tell the gui to switch to it -func (gui *Gui) PrepareSubProcess(command string) { - splitCmd := str.ToArgv(command) - gui.SubProcess = gui.OSCommand.PrepareSubProcess(splitCmd[0], splitCmd[1:]...) - gui.g.Update(func(g *gocui.Gui) error { - return gui.Errors.ErrSubProcess - }) -} - -func (gui *Gui) PrepareShellSubProcess(command string) { - gui.SubProcess = gui.OSCommand.PrepareShellSubProcess(command) - gui.g.Update(func(g *gocui.Gui) error { - return gui.Errors.ErrSubProcess - }) + return gui.runSubprocessWithSuspense( + gui.OSCommand.PrepareSubProcess("git", "commit"), + ) } func (gui *Gui) editFile(filename string) error { @@ -816,8 +800,9 @@ func (gui *Gui) handleCustomCommand() error { return gui.prompt(promptOpts{ title: gui.Tr.CustomCommand, handleConfirm: func(command string) error { - gui.SubProcess = gui.OSCommand.PrepareShellSubProcess(command) - return gui.Errors.ErrSubProcess + return gui.runSubprocessWithSuspense( + gui.OSCommand.PrepareShellSubProcess(command), + ) }, }) } diff --git a/pkg/gui/git_flow.go b/pkg/gui/git_flow.go index 4440c6ebd..6af93794c 100644 --- a/pkg/gui/git_flow.go +++ b/pkg/gui/git_flow.go @@ -31,9 +31,9 @@ func (gui *Gui) gitFlowFinishBranch(gitFlowConfig string, branchName string) err return gui.createErrorPanel(gui.Tr.NotAGitFlowBranch) } - subProcess := gui.OSCommand.PrepareSubProcess("git", "flow", branchType, "finish", suffix) - gui.SubProcess = subProcess - return gui.Errors.ErrSubProcess + return gui.runSubprocessWithSuspense( + gui.OSCommand.PrepareSubProcess("git", "flow", branchType, "finish", suffix), + ) } func (gui *Gui) handleCreateGitFlowMenu() error { @@ -55,9 +55,9 @@ func (gui *Gui) handleCreateGitFlowMenu() error { return gui.prompt(promptOpts{ title: title, handleConfirm: func(name string) error { - subProcess := gui.OSCommand.PrepareSubProcess("git", "flow", branchType, "start", name) - gui.SubProcess = subProcess - return gui.Errors.ErrSubProcess + return gui.runSubprocessWithSuspense( + gui.OSCommand.PrepareSubProcess("git", "flow", branchType, "start", name), + ) }, }) } diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index b44994c32..2e58f430d 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -476,10 +476,9 @@ func (gui *Gui) Run() error { return err } -// RunWithSubprocesses loops, instantiating a new gocui.Gui with each iteration -// if the error returned from a run is a ErrSubProcess, it runs the subprocess -// otherwise it handles the error, possibly by quitting the application -func (gui *Gui) RunWithSubprocesses() error { +// RunWithRestarts loops, instantiating a new gocui.Gui with each iteration +// (i.e. when switching repos or restarting). If it's a random error, we quit +func (gui *Gui) RunWithRestarts() error { gui.StartTime = time.Now() go utils.Safe(gui.replayRecordedEvents) @@ -512,11 +511,6 @@ func (gui *Gui) RunWithSubprocesses() error { return nil case gui.Errors.ErrSwitchRepo, gui.Errors.ErrRestart: continue - case gui.Errors.ErrSubProcess: - - if err := gui.runCommand(); err != nil { - return err - } default: return err } @@ -524,23 +518,40 @@ func (gui *Gui) RunWithSubprocesses() error { } } -func (gui *Gui) runCommand() error { - gui.SubProcess.Stdout = os.Stdout - gui.SubProcess.Stderr = os.Stdout - gui.SubProcess.Stdin = os.Stdin +func (gui *Gui) runSubprocessWithSuspense(subprocess *exec.Cmd) error { + if err := gocui.Screen.Suspend(); err != nil { + return gui.surfaceError(err) + } - fmt.Fprintf(os.Stdout, "\n%s\n\n", utils.ColoredString("+ "+strings.Join(gui.SubProcess.Args, " "), color.FgBlue)) + cmdErr := gui.runSubprocess(subprocess) - if err := gui.SubProcess.Run(); err != nil { + if err := gocui.Screen.Resume(); err != nil { + return gui.surfaceError(err) + } + + if err := gui.refreshSidePanels(refreshOptions{mode: ASYNC}); err != nil { + return err + } + + return gui.surfaceError(cmdErr) +} + +func (gui *Gui) runSubprocess(subprocess *exec.Cmd) error { + subprocess.Stdout = os.Stdout + subprocess.Stderr = os.Stdout + subprocess.Stdin = os.Stdin + + fmt.Fprintf(os.Stdout, "\n%s\n\n", utils.ColoredString("+ "+strings.Join(subprocess.Args, " "), color.FgBlue)) + + if err := subprocess.Run(); err != nil { // not handling the error explicitly because usually we're going to see it // in the output anyway gui.Log.Error(err) } - gui.SubProcess.Stdout = ioutil.Discard - gui.SubProcess.Stderr = ioutil.Discard - gui.SubProcess.Stdin = nil - gui.SubProcess = nil + subprocess.Stdout = ioutil.Discard + subprocess.Stderr = ioutil.Discard + subprocess.Stdin = nil fmt.Fprintf(os.Stdout, "\n%s", utils.ColoredString(gui.Tr.PressEnterToReturn, color.FgGreen)) fmt.Scanln() // wait for enter press diff --git a/pkg/gui/rebase_options_panel.go b/pkg/gui/rebase_options_panel.go index 5ffd7b3bd..01f91d3cb 100644 --- a/pkg/gui/rebase_options_panel.go +++ b/pkg/gui/rebase_options_panel.go @@ -50,8 +50,7 @@ func (gui *Gui) genericMergeCommand(command string) error { if status == commands.REBASE_MODE_MERGING && command != "abort" && gui.Config.GetUserConfig().Git.Merging.ManualCommit { sub := gui.OSCommand.PrepareSubProcess("git", commandType, fmt.Sprintf("--%s", command)) if sub != nil { - gui.SubProcess = sub - return gui.Errors.ErrSubProcess + return gui.runSubprocessWithSuspense(sub) } return nil } @@ -68,8 +67,6 @@ func (gui *Gui) handleGenericMergeCommandResult(result error) error { } if result == nil { return nil - } else if result == gui.Errors.ErrSubProcess { - return result } else if strings.Contains(result.Error(), "No changes - did you forget to use") { return gui.genericMergeCommand("skip") } else if strings.Contains(result.Error(), "The previous cherry-pick is now empty") {