From d9fa02c53bb9b401f1b5ca07e8ed239862052a42 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Sat, 15 Aug 2020 16:36:39 +1000 Subject: clean up interface for popup panels --- pkg/gui/app_status_manager.go | 2 +- pkg/gui/branches_panel.go | 149 ++++++++++++++++++++++------------- pkg/gui/commit_files_panel.go | 65 +++++++++------ pkg/gui/commit_message_panel.go | 6 +- pkg/gui/commits_panel.go | 166 +++++++++++++++++++++++++-------------- pkg/gui/confirmation_panel.go | 145 ++++++++++++++++++++++------------ pkg/gui/credentials_panel.go | 10 +-- pkg/gui/diffing.go | 4 +- pkg/gui/files_panel.go | 102 ++++++++++++++---------- pkg/gui/filtering.go | 16 ++-- pkg/gui/filtering_menu_panel.go | 4 +- pkg/gui/git_flow.go | 3 +- pkg/gui/global_handlers.go | 8 +- pkg/gui/gui.go | 37 ++++++--- pkg/gui/layout.go | 4 +- pkg/gui/menu_panel.go | 2 +- pkg/gui/merge_panel.go | 14 +++- pkg/gui/patch_building_panel.go | 6 +- pkg/gui/patch_options_panel.go | 12 ++- pkg/gui/quitting.go | 12 ++- pkg/gui/rebase_options_panel.go | 13 ++- pkg/gui/reflog_panel.go | 12 ++- pkg/gui/remote_branches_panel.go | 45 +++++++---- pkg/gui/remotes_panel.go | 31 ++++---- pkg/gui/searching.go | 10 +-- pkg/gui/staging_panel.go | 16 ++-- pkg/gui/stash_panel.go | 46 +++++++---- pkg/gui/status_panel.go | 2 +- pkg/gui/tags_panel.go | 25 +++--- pkg/gui/tasks_adapter.go | 2 +- pkg/gui/undoing.go | 38 +++++---- pkg/gui/updates.go | 33 +++++--- pkg/gui/view_helpers.go | 26 +++--- pkg/i18n/english.go | 6 ++ 34 files changed, 675 insertions(+), 397 deletions(-) (limited to 'pkg') diff --git a/pkg/gui/app_status_manager.go b/pkg/gui/app_status_manager.go index 54393c443..9d8ac3725 100644 --- a/pkg/gui/app_status_manager.go +++ b/pkg/gui/app_status_manager.go @@ -66,7 +66,7 @@ func (gui *Gui) WithWaitingStatus(name string, f func() error) error { if appStatus == "" { return } - gui.renderString(gui.g, "appStatus", appStatus) + gui.renderString("appStatus", appStatus) } }() diff --git a/pkg/gui/branches_panel.go b/pkg/gui/branches_panel.go index ed8e7819e..22dddb65e 100644 --- a/pkg/gui/branches_panel.go +++ b/pkg/gui/branches_panel.go @@ -125,7 +125,7 @@ func (gui *Gui) handleCreatePullRequestPress(g *gocui.Gui, v *gocui.View) error } func (gui *Gui) handleGitFetch(g *gocui.Gui, v *gocui.View) error { - if err := gui.createLoaderPanel(gui.g, v, gui.Tr.SLocalize("FetchWait")); err != nil { + if err := gui.createLoaderPanel(v, gui.Tr.SLocalize("FetchWait")); err != nil { return err } go func() { @@ -140,12 +140,19 @@ func (gui *Gui) handleForceCheckout(g *gocui.Gui, v *gocui.View) error { branch := gui.getSelectedBranch() message := gui.Tr.SLocalize("SureForceCheckout") title := gui.Tr.SLocalize("ForceCheckoutBranch") - return gui.createConfirmationPanel(g, v, true, title, message, func(g *gocui.Gui, v *gocui.View) error { - if err := gui.GitCommand.Checkout(branch.Name, commands.CheckoutOptions{Force: true}); err != nil { - _ = gui.surfaceError(err) - } - return gui.refreshSidePanels(refreshOptions{mode: ASYNC}) - }, nil) + + return gui.createConfirmationPanel(createConfirmationPanelOpts{ + returnToView: v, + returnFocusOnClose: true, + title: title, + prompt: message, + handleConfirm: func() error { + if err := gui.GitCommand.Checkout(branch.Name, commands.CheckoutOptions{Force: true}); err != nil { + _ = gui.surfaceError(err) + } + return gui.refreshSidePanels(refreshOptions{mode: ASYNC}) + }, + }) } type handleCheckoutRefOptions struct { @@ -179,24 +186,29 @@ func (gui *Gui) handleCheckoutRef(ref string, options handleCheckoutRefOptions) if strings.Contains(err.Error(), "Please commit your changes or stash them before you switch branch") { // offer to autostash changes - return gui.createConfirmationPanel(gui.g, gui.getBranchesView(), true, gui.Tr.SLocalize("AutoStashTitle"), gui.Tr.SLocalize("AutoStashPrompt"), func(g *gocui.Gui, v *gocui.View) error { - - if err := gui.GitCommand.StashSave(gui.Tr.SLocalize("StashPrefix") + ref); err != nil { - return gui.surfaceError(err) - } - if err := gui.GitCommand.Checkout(ref, cmdOptions); err != nil { - return gui.surfaceError(err) - } - - onSuccess() - if err := gui.GitCommand.StashDo(0, "pop"); err != nil { - if err := gui.refreshSidePanels(refreshOptions{mode: BLOCK_UI}); err != nil { - return err + return gui.createConfirmationPanel(createConfirmationPanelOpts{ + returnToView: gui.getBranchesView(), + returnFocusOnClose: true, + title: gui.Tr.SLocalize("AutoStashTitle"), + prompt: gui.Tr.SLocalize("AutoStashPrompt"), + handleConfirm: func() error { + if err := gui.GitCommand.StashSave(gui.Tr.SLocalize("StashPrefix") + ref); err != nil { + return gui.surfaceError(err) } - return gui.surfaceError(err) - } - return gui.refreshSidePanels(refreshOptions{mode: BLOCK_UI}) - }, nil) + if err := gui.GitCommand.Checkout(ref, cmdOptions); err != nil { + return gui.surfaceError(err) + } + + onSuccess() + if err := gui.GitCommand.StashDo(0, "pop"); err != nil { + if err := gui.refreshSidePanels(refreshOptions{mode: BLOCK_UI}); err != nil { + return err + } + return gui.surfaceError(err) + } + return gui.refreshSidePanels(refreshOptions{mode: BLOCK_UI}) + }, + }) } if err := gui.surfaceError(err); err != nil { @@ -210,12 +222,19 @@ func (gui *Gui) handleCheckoutRef(ref string, options handleCheckoutRefOptions) } func (gui *Gui) handleCheckoutByName(g *gocui.Gui, v *gocui.View) error { - return gui.createPromptPanel(g, v, gui.Tr.SLocalize("BranchName")+":", "", func(g *gocui.Gui, v *gocui.View) error { - return gui.handleCheckoutRef(gui.trimmedContent(v), handleCheckoutRefOptions{ + return gui.createPromptPanel(v, gui.Tr.SLocalize("BranchName")+":", "", func(response string) error { + return gui.handleCheckoutRef(response, handleCheckoutRefOptions{ onRefNotFound: func(ref string) error { - return gui.createConfirmationPanel(gui.g, v, true, gui.Tr.SLocalize("BranchNotFoundTitle"), fmt.Sprintf("%s %s%s", gui.Tr.SLocalize("BranchNotFoundPrompt"), ref, "?"), func(_g *gocui.Gui, _v *gocui.View) error { - return gui.createNewBranchWithName(ref) - }, nil) + + return gui.createConfirmationPanel(createConfirmationPanelOpts{ + returnToView: v, + returnFocusOnClose: true, + title: gui.Tr.SLocalize("BranchNotFoundTitle"), + prompt: fmt.Sprintf("%s %s%s", gui.Tr.SLocalize("BranchNotFoundPrompt"), ref, "?"), + handleConfirm: func() error { + return gui.createNewBranchWithName(ref) + }, + }) }, }) }) @@ -240,8 +259,8 @@ func (gui *Gui) handleNewBranch(g *gocui.Gui, v *gocui.View) error { "branchName": branch.Name, }, ) - return gui.createPromptPanel(g, v, message, "", func(g *gocui.Gui, v *gocui.View) error { - return gui.createNewBranchWithName(gui.trimmedContent(v)) + return gui.createPromptPanel(v, message, "", func(response string) error { + return gui.createNewBranchWithName(response) }) } @@ -288,16 +307,23 @@ func (gui *Gui) deleteNamedBranch(g *gocui.Gui, v *gocui.View, selectedBranch *c "selectedBranchName": selectedBranch.Name, }, ) - return gui.createConfirmationPanel(g, v, true, title, message, func(g *gocui.Gui, v *gocui.View) error { - if err := gui.GitCommand.DeleteBranch(selectedBranch.Name, force); err != nil { - errMessage := err.Error() - if !force && strings.Contains(errMessage, "is not fully merged") { - return gui.deleteNamedBranch(g, v, selectedBranch, true) + + return gui.createConfirmationPanel(createConfirmationPanelOpts{ + returnToView: v, + returnFocusOnClose: true, + title: title, + prompt: message, + handleConfirm: func() error { + if err := gui.GitCommand.DeleteBranch(selectedBranch.Name, force); err != nil { + errMessage := err.Error() + if !force && strings.Contains(errMessage, "is not fully merged") { + return gui.deleteNamedBranch(g, v, selectedBranch, true) + } + return gui.createErrorPanel(errMessage) } - return gui.createErrorPanel(errMessage) - } - return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []int{BRANCHES}}) - }, nil) + return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []int{BRANCHES}}) + }, + }) } func (gui *Gui) mergeBranchIntoCheckedOutBranch(branchName string) error { @@ -319,12 +345,17 @@ func (gui *Gui) mergeBranchIntoCheckedOutBranch(branchName string) error { "selectedBranch": branchName, }, ) - return gui.createConfirmationPanel(gui.g, gui.getBranchesView(), true, gui.Tr.SLocalize("MergingTitle"), prompt, - func(g *gocui.Gui, v *gocui.View) error { + return gui.createConfirmationPanel(createConfirmationPanelOpts{ + returnToView: gui.getBranchesView(), + returnFocusOnClose: true, + title: gui.Tr.SLocalize("MergingTitle"), + prompt: prompt, + handleConfirm: func() error { err := gui.GitCommand.Merge(branchName, commands.MergeOpts{}) return gui.handleGenericMergeCommandResult(err) - }, nil) + }, + }) } func (gui *Gui) handleMerge(g *gocui.Gui, v *gocui.View) error { @@ -357,11 +388,17 @@ func (gui *Gui) handleRebaseOntoBranch(selectedBranchName string) error { "selectedBranch": selectedBranchName, }, ) - return gui.createConfirmationPanel(gui.g, gui.getBranchesView(), true, gui.Tr.SLocalize("RebasingTitle"), prompt, - func(g *gocui.Gui, v *gocui.View) error { + + return gui.createConfirmationPanel(createConfirmationPanelOpts{ + returnToView: gui.getBranchesView(), + returnFocusOnClose: true, + title: gui.Tr.SLocalize("RebasingTitle"), + prompt: prompt, + handleConfirm: func() error { err := gui.GitCommand.RebaseBranch(selectedBranchName) return gui.handleGenericMergeCommandResult(err) - }, nil) + }, + }) } func (gui *Gui) handleFastForward(g *gocui.Gui, v *gocui.View) error { @@ -396,7 +433,7 @@ func (gui *Gui) handleFastForward(g *gocui.Gui, v *gocui.View) error { }, ) go func() { - _ = gui.createLoaderPanel(gui.g, v, message) + _ = gui.createLoaderPanel(v, message) if gui.State.Panels.Branches.SelectedLine == 0 { _ = gui.pullWithMode("ff-only", PullFilesOptions{}) @@ -500,14 +537,13 @@ func (gui *Gui) handleRenameBranch(g *gocui.Gui, v *gocui.View) error { // way to get it to show up in the reflog) promptForNewName := func() error { - return gui.createPromptPanel(g, v, gui.Tr.SLocalize("NewBranchNamePrompt")+" "+branch.Name+":", "", func(g *gocui.Gui, v *gocui.View) error { - newName := gui.trimmedContent(v) - if err := gui.GitCommand.RenameBranch(branch.Name, newName); err != nil { + return gui.createPromptPanel(v, gui.Tr.SLocalize("NewBranchNamePrompt")+" "+branch.Name+":", "", func(newBranchName string) error { + if err := gui.GitCommand.RenameBranch(branch.Name, newBranchName); err != nil { return gui.surfaceError(err) } // need to checkout so that the branch shows up in our reflog and therefore // doesn't get lost among all the other branches when we switch to something else - if err := gui.GitCommand.Checkout(newName, commands.CheckoutOptions{Force: false}); err != nil { + if err := gui.GitCommand.Checkout(newBranchName, commands.CheckoutOptions{Force: false}); err != nil { return gui.surfaceError(err) } @@ -522,9 +558,14 @@ func (gui *Gui) handleRenameBranch(g *gocui.Gui, v *gocui.View) error { if notTrackingRemote { return promptForNewName() } - return gui.createConfirmationPanel(gui.g, v, true, gui.Tr.SLocalize("renameBranch"), gui.Tr.SLocalize("RenameBranchWarning"), func(_g *gocui.Gui, _v *gocui.View) error { - return promptForNewName() - }, nil) + + return gui.createConfirmationPanel(createConfirmationPanelOpts{ + returnToView: v, + returnFocusOnClose: true, + title: gui.Tr.SLocalize("renameBranch"), + prompt: gui.Tr.SLocalize("RenameBranchWarning"), + handleConfirm: promptForNewName, + }) } func (gui *Gui) currentBranch() *commands.Branch { diff --git a/pkg/gui/commit_files_panel.go b/pkg/gui/commit_files_panel.go index 886fe3d7b..b10f0f4bb 100644 --- a/pkg/gui/commit_files_panel.go +++ b/pkg/gui/commit_files_panel.go @@ -36,7 +36,7 @@ func (gui *Gui) handleCommitFileSelect(g *gocui.Gui, v *gocui.View) error { commitFile := gui.getSelectedCommitFile() if commitFile == nil { - gui.renderString(g, "commitFiles", gui.Tr.SLocalize("NoCommiteFiles")) + gui.renderString("commitFiles", gui.Tr.SLocalize("NoCommiteFiles")) return nil } @@ -57,7 +57,7 @@ func (gui *Gui) handleCommitFileSelect(g *gocui.Gui, v *gocui.View) error { } func (gui *Gui) handleSwitchToCommitsPanel(g *gocui.Gui, v *gocui.View) error { - return gui.switchFocus(g, v, gui.getCommitsView()) + return gui.switchFocus(v, gui.getCommitsView()) } func (gui *Gui) handleCheckoutCommitFile(g *gocui.Gui, v *gocui.View) error { @@ -77,17 +77,23 @@ func (gui *Gui) handleDiscardOldFileChange(g *gocui.Gui, v *gocui.View) error { fileName := gui.State.CommitFiles[gui.State.Panels.CommitFiles.SelectedLine].Name - return gui.createConfirmationPanel(gui.g, v, true, gui.Tr.SLocalize("DiscardFileChangesTitle"), gui.Tr.SLocalize("DiscardFileChangesPrompt"), func(g *gocui.Gui, v *gocui.View) error { - return gui.WithWaitingStatus(gui.Tr.SLocalize("RebasingStatus"), func() error { - if err := gui.GitCommand.DiscardOldFileChanges(gui.State.Commits, gui.State.Panels.Commits.SelectedLine, fileName); err != nil { - if err := gui.handleGenericMergeCommandResult(err); err != nil { - return err + return gui.createConfirmationPanel(createConfirmationPanelOpts{ + returnToView: v, + returnFocusOnClose: true, + title: gui.Tr.SLocalize("DiscardFileChangesTitle"), + prompt: gui.Tr.SLocalize("DiscardFileChangesPrompt"), + handleConfirm: func() error { + return gui.WithWaitingStatus(gui.Tr.SLocalize("RebasingStatus"), func() error { + if err := gui.GitCommand.DiscardOldFileChanges(gui.State.Commits, gui.State.Panels.Commits.SelectedLine, fileName); err != nil { + if err := gui.handleGenericMergeCommandResult(err); err != nil { + return err + } } - } - return gui.refreshSidePanels(refreshOptions{mode: BLOCK_UI}) - }) - }, nil) + return gui.refreshSidePanels(refreshOptions{mode: BLOCK_UI}) + }) + }, + }) } func (gui *Gui) refreshCommitFilesView() error { @@ -144,7 +150,7 @@ func (gui *Gui) handleToggleFileForPatch(g *gocui.Gui, v *gocui.View) error { commitFile := gui.getSelectedCommitFile() if commitFile == nil { - gui.renderString(g, "commitFiles", gui.Tr.SLocalize("NoCommiteFiles")) + gui.renderString("commitFiles", gui.Tr.SLocalize("NoCommiteFiles")) return nil } @@ -161,10 +167,16 @@ func (gui *Gui) handleToggleFileForPatch(g *gocui.Gui, v *gocui.View) error { } if gui.GitCommand.PatchManager.CommitSelected() && gui.GitCommand.PatchManager.CommitSha != commitFile.Sha { - return gui.createConfirmationPanel(g, v, true, gui.Tr.SLocalize("DiscardPatch"), gui.Tr.SLocalize("DiscardPatchConfirm"), func(g *gocui.Gui, v *gocui.View) error { - gui.GitCommand.PatchManager.Reset() - return toggleTheFile() - }, nil) + return gui.createConfirmationPanel(createConfirmationPanelOpts{ + returnToView: v, + returnFocusOnClose: true, + title: gui.Tr.SLocalize("DiscardPatch"), + prompt: gui.Tr.SLocalize("DiscardPatchConfirm"), + handleConfirm: func() error { + gui.GitCommand.PatchManager.Reset() + return toggleTheFile() + }, + }) } return toggleTheFile() @@ -200,7 +212,7 @@ func (gui *Gui) enterCommitFile(selectedLineIdx int) error { commitFile := gui.getSelectedCommitFile() if commitFile == nil { - gui.renderString(gui.g, "commitFiles", gui.Tr.SLocalize("NoCommiteFiles")) + gui.renderString("commitFiles", gui.Tr.SLocalize("NoCommiteFiles")) return nil } @@ -212,18 +224,25 @@ func (gui *Gui) enterCommitFile(selectedLineIdx int) error { } gui.changeMainViewsContext("patch-building") - if err := gui.switchFocus(gui.g, gui.getCommitFilesView(), gui.getMainView()); err != nil { + if err := gui.switchFocus(gui.getCommitFilesView(), gui.getMainView()); err != nil { return err } return gui.refreshPatchBuildingPanel(selectedLineIdx) } if gui.GitCommand.PatchManager.CommitSelected() && gui.GitCommand.PatchManager.CommitSha != commitFile.Sha { - return gui.createConfirmationPanel(gui.g, gui.getCommitFilesView(), false, gui.Tr.SLocalize("DiscardPatch"), gui.Tr.SLocalize("DiscardPatchConfirm"), func(g *gocui.Gui, v *gocui.View) error { - gui.GitCommand.PatchManager.Reset() - return enterTheFile(selectedLineIdx) - }, func(g *gocui.Gui, v *gocui.View) error { - return gui.switchFocus(gui.g, nil, gui.getCommitFilesView()) + return gui.createConfirmationPanel(createConfirmationPanelOpts{ + returnToView: gui.getCommitFilesView(), + returnFocusOnClose: false, + title: gui.Tr.SLocalize("DiscardPatch"), + prompt: gui.Tr.SLocalize("DiscardPatchConfirm"), + handleConfirm: func() error { + gui.GitCommand.PatchManager.Reset() + return enterTheFile(selectedLineIdx) + }, + handleClose: func() error { + return gui.switchFocus(nil, gui.getCommitFilesView()) + }, }) } diff --git a/pkg/gui/commit_message_panel.go b/pkg/gui/commit_message_panel.go index bcc47f6b5..af79795f1 100644 --- a/pkg/gui/commit_message_panel.go +++ b/pkg/gui/commit_message_panel.go @@ -47,13 +47,13 @@ func (gui *Gui) handleCommitConfirm(g *gocui.Gui, v *gocui.View) error { _ = v.SetCursor(0, 0) _ = v.SetOrigin(0, 0) _, _ = g.SetViewOnBottom("commitMessage") - _ = gui.switchFocus(g, v, gui.getFilesView()) + _ = gui.switchFocus(v, gui.getFilesView()) return gui.refreshSidePanels(refreshOptions{mode: ASYNC}) } func (gui *Gui) handleCommitClose(g *gocui.Gui, v *gocui.View) error { _, _ = g.SetViewOnBottom("commitMessage") - return gui.switchFocus(g, v, gui.getFilesView()) + return gui.switchFocus(v, gui.getFilesView()) } func (gui *Gui) handleCommitFocused(g *gocui.Gui, v *gocui.View) error { @@ -69,7 +69,7 @@ func (gui *Gui) handleCommitFocused(g *gocui.Gui, v *gocui.View) error { "keyBindNewLine": "tab", }, ) - gui.renderString(g, "options", message) + gui.renderString("options", message) return nil } diff --git a/pkg/gui/commits_panel.go b/pkg/gui/commits_panel.go index c7ea04b20..2c1beafc7 100644 --- a/pkg/gui/commits_panel.go +++ b/pkg/gui/commits_panel.go @@ -155,12 +155,18 @@ func (gui *Gui) handleCommitSquashDown(g *gocui.Gui, v *gocui.View) error { return nil } - return gui.createConfirmationPanel(g, v, true, gui.Tr.SLocalize("Squash"), gui.Tr.SLocalize("SureSquashThisCommit"), func(g *gocui.Gui, v *gocui.View) error { - return gui.WithWaitingStatus(gui.Tr.SLocalize("SquashingStatus"), func() error { - err := gui.GitCommand.InteractiveRebase(gui.State.Commits, gui.State.Panels.Commits.SelectedLine, "squash") - return gui.handleGenericMergeCommandResult(err) - }) - }, nil) + return gui.createConfirmationPanel(createConfirmationPanelOpts{ + returnToView: v, + returnFocusOnClose: true, + title: gui.Tr.SLocalize("Squash"), + prompt: gui.Tr.SLocalize("SureSquashThisCommit"), + handleConfirm: func() error { + return gui.WithWaitingStatus(gui.Tr.SLocalize("SquashingStatus"), func() error { + err := gui.GitCommand.InteractiveRebase(gui.State.Commits, gui.State.Panels.Commits.SelectedLine, "squash") + return gui.handleGenericMergeCommandResult(err) + }) + }, + }) } func (gui *Gui) handleCommitFixup(g *gocui.Gui, v *gocui.View) error { @@ -180,12 +186,18 @@ func (gui *Gui) handleCommitFixup(g *gocui.Gui, v *gocui.View) error { return nil } - return gui.createConfirmationPanel(g, v, true, gui.Tr.SLocalize("Fixup"), gui.Tr.SLocalize("SureFixupThisCommit"), func(g *gocui.Gui, v *gocui.View) error { - return gui.WithWaitingStatus(gui.Tr.SLocalize("FixingStatus"), func() error { - err := gui.GitCommand.InteractiveRebase(gui.State.Commits, gui.State.Panels.Commits.SelectedLine, "fixup") - return gui.handleGenericMergeCommandResult(err) - }) - }, nil) + return gui.createConfirmationPanel(createConfirmationPanelOpts{ + returnToView: v, + returnFocusOnClose: true, + title: gui.Tr.SLocalize("Fixup"), + prompt: gui.Tr.SLocalize("SureFixupThisCommit"), + handleConfirm: func() error { + return gui.WithWaitingStatus(gui.Tr.SLocalize("FixingStatus"), func() error { + err := gui.GitCommand.InteractiveRebase(gui.State.Commits, gui.State.Panels.Commits.SelectedLine, "fixup") + return gui.handleGenericMergeCommandResult(err) + }) + }, + }) } func (gui *Gui) handleRenameCommit(g *gocui.Gui, v *gocui.View) error { @@ -204,8 +216,8 @@ func (gui *Gui) handleRenameCommit(g *gocui.Gui, v *gocui.View) error { if gui.State.Panels.Commits.SelectedLine != 0 { return gui.createErrorPanel(gui.Tr.SLocalize("OnlyRenameTopCommit")) } - return gui.createPromptPanel(g, v, gui.Tr.SLocalize("renameCommit"), "", func(g *gocui.Gui, v *gocui.View) error { - if err := gui.GitCommand.RenameCommit(v.Buffer()); err != nil { + return gui.createPromptPanel(v, gui.Tr.SLocalize("renameCommit"), "", func(response string) error { + if err := gui.GitCommand.RenameCommit(response); err != nil { return gui.surfaceError(err) } @@ -276,12 +288,18 @@ func (gui *Gui) handleCommitDelete(g *gocui.Gui, v *gocui.View) error { return nil } - return gui.createConfirmationPanel(gui.g, v, true, gui.Tr.SLocalize("DeleteCommitTitle"), gui.Tr.SLocalize("DeleteCommitPrompt"), func(*gocui.Gui, *gocui.View) error { - return gui.WithWaitingStatus(gui.Tr.SLocalize("DeletingStatus"), func() error { - err := gui.GitCommand.InteractiveRebase(gui.State.Commits, gui.State.Panels.Commits.SelectedLine, "drop") - return gui.handleGenericMergeCommandResult(err) - }) - }, nil) + return gui.createConfirmationPanel(createConfirmationPanelOpts{ + returnToView: v, + returnFocusOnClose: true, + title: gui.Tr.SLocalize("DeleteCommitTitle"), + prompt: gui.Tr.SLocalize("DeleteCommitPrompt"), + handleConfirm: func() error { + return gui.WithWaitingStatus(gui.Tr.SLocalize("DeletingStatus"), func() error { + err := gui.GitCommand.InteractiveRebase(gui.State.Commits, gui.State.Panels.Commits.SelectedLine, "drop") + return gui.handleGenericMergeCommandResult(err) + }) + }, + }) } func (gui *Gui) handleCommitMoveDown(g *gocui.Gui, v *gocui.View) error { @@ -362,12 +380,18 @@ func (gui *Gui) handleCommitAmendTo(g *gocui.Gui, v *gocui.View) error { return err } - return gui.createConfirmationPanel(gui.g, v, true, gui.Tr.SLocalize("AmendCommitTitle"), gui.Tr.SLocalize("AmendCommitPrompt"), func(*gocui.Gui, *gocui.View) error { - return gui.WithWaitingStatus(gui.Tr.SLocalize("AmendingStatus"), func() error { - err := gui.GitCommand.AmendTo(gui.State.Commits[gui.State.Panels.Commits.SelectedLine].Sha) - return gui.handleGenericMergeCommandResult(err) - }) - }, nil) + return gui.createConfirmationPanel(createConfirmationPanelOpts{ + returnToView: v, + returnFocusOnClose: true, + title: gui.Tr.SLocalize("AmendCommitTitle"), + prompt: gui.Tr.SLocalize("AmendCommitPrompt"), + handleConfirm: func() error { + return gui.WithWaitingStatus(gui.Tr.SLocalize("AmendingStatus"), func() error { + err := gui.GitCommand.AmendTo(gui.State.Commits[gui.State.Panels.Commits.SelectedLine].Sha) + return gui.handleGenericMergeCommandResult(err) + }) + }, + }) } func (gui *Gui) handleCommitPick(g *gocui.Gui, v *gocui.View) error { @@ -474,12 +498,18 @@ func (gui *Gui) HandlePasteCommits(g *gocui.Gui, v *gocui.View) error { return err } - return gui.createConfirmationPanel(g, v, true, gui.Tr.SLocalize("CherryPick"), gui.Tr.SLocalize("SureCherryPick"), func(g *gocui.Gui, v *gocui.View) error { - return gui.WithWaitingStatus(gui.Tr.SLocalize("CherryPickingStatus"), func() error { - err := gui.GitCommand.CherryPickCommits(gui.State.CherryPickedCommits) - return gui.handleGenericMergeCommandResult(err) - }) - }, nil) + return gui.createConfirmationPanel(createConfirmationPanelOpts{ + returnToView: v, + returnFocusOnClose: true, + title: gui.Tr.SLocalize("CherryPick"), + prompt: gui.Tr.SLocalize("SureCherryPick"), + handleConfirm: func() error { + return gui.WithWaitingStatus(gui.Tr.SLocalize("CherryPickingStatus"), func() error { + err := gui.GitCommand.CherryPickCommits(gui.State.CherryPickedCommits) + return gui.handleGenericMergeCommandResult(err) + }) + }, + }) } func (gui *Gui) handleSwitchToCommitFilesPanel(g *gocui.Gui, v *gocui.View) error { @@ -487,7 +517,7 @@ func (gui *Gui) handleSwitchToCommitFilesPanel(g *gocui.Gui, v *gocui.View) erro return err } - return gui.switchFocus(g, gui.getCommitsView(), gui.getCommitFilesView()) + return gui.switchFocus(gui.getCommitsView(), gui.getCommitFilesView()) } func (gui *Gui) hasCommit(commits []*commands.Commit, target string) (int, bool) { @@ -513,18 +543,24 @@ func (gui *Gui) handleCreateFixupCommit(g *gocui.Gui, v *gocui.View) error { return nil } - return gui.createConfirmationPanel(g, v, true, gui.Tr.SLocalize("CreateFixupCommit"), gui.Tr.TemplateLocalize( - "SureCreateFixupCommit", - Teml{ - "commit": commit.Sha, - }, - ), func(g *gocui.Gui, v *gocui.View) error { - if err := gui.GitCommand.CreateFixupCommit(commit.Sha); err != nil { - return gui.surfaceError(err) - } + return gui.createConfirmationPanel(createConfirmationPanelOpts{ + returnToView: v, + returnFocusOnClose: true, + title: gui.Tr.SLocalize("CreateFixupCommit"), + prompt: gui.Tr.TemplateLocalize( + "SureCreateFixupCommit", + Teml{ + "commit": commit.Sha, + }, + ), + handleConfirm: func() error { + if err := gui.GitCommand.CreateFixupCommit(commit.Sha); err != nil { + return gui.surfaceError(err) + } - return gui.refreshSidePanels(refreshOptions{mode: ASYNC}) - }, nil) + return gui.refreshSidePanels(refreshOptions{mode: ASYNC}) + }, + }) } func (gui *Gui) handleSquashAllAboveFixupCommits(g *gocui.Gui, v *gocui.View) error { @@ -537,17 +573,23 @@ func (gui *Gui) handleSquashAllAboveFixupCommits(g *gocui.Gui, v *gocui.View) er return nil } - return gui.createConfirmationPanel(g, v, true, gui.Tr.SLocalize("SquashAboveCommits"), gui.Tr.TemplateLocalize( - "SureSquashAboveCommits", - Teml{ - "commit": commit.Sha, + return gui.createConfirmationPanel(createConfirmationPanelOpts{ + returnToView: v, + returnFocusOnClose: true, + title: gui.Tr.SLocalize("SquashAboveCommits"), + prompt: gui.Tr.TemplateLocalize( + "SureSquashAboveCommits", + Teml{ + "commit": commit.Sha, + }, + ), + handleConfirm: func() error { + return gui.WithWaitingStatus(gui.Tr.SLocalize("SquashingStatus"), func() error { + err := gui.GitCommand.SquashAllAboveFixupCommits(commit.Sha) + return gui.handleGenericMergeCommandResult(err) + }) }, - ), func(g *gocui.Gui, v *gocui.View) error { - return gui.WithWaitingStatus(gui.Tr.SLocalize("SquashingStatus"), func() error { - err := gui.GitCommand.SquashAllAboveFixupCommits(commit.Sha) - return gui.handleGenericMergeCommandResult(err) - }) - }, nil) + }) } func (gui *Gui) handleTagCommit(g *gocui.Gui, v *gocui.View) error { @@ -563,8 +605,8 @@ func (gui *Gui) handleTagCommit(g *gocui.Gui, v *gocui.View) error { } func (gui *Gui) handleCreateLightweightTag(commitSha string) error { - return gui.createPromptPanel(gui.g, gui.getCommitsView(), gui.Tr.SLocalize("TagNameTitle"), "", func(g *gocui.Gui, v *gocui.View) error { - if err := gui.GitCommand.CreateLightweightTag(v.Buffer(), commitSha); err != nil { + return gui.createPromptPanel(gui.getCommitsView(), gui.Tr.SLocalize("TagNameTitle"), "", func(response string) error { + if err := gui.GitCommand.CreateLightweightTag(response, commitSha); err != nil { return gui.surfaceError(err) } return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []int{COMMITS, TAGS}}) @@ -577,9 +619,15 @@ func (gui *Gui) handleCheckoutCommit(g *gocui.Gui, v *gocui.View) error { return nil } - return gui.createConfirmationPanel(g, gui.getCommitsView(), true, gui.Tr.SLocalize("checkoutCommit"), gui.Tr.SLocalize("SureCheckoutThisCommit"), func(g *gocui.Gui, v *gocui.View) error { - return gui.handleCheckoutRef(commit.Sha, handleCheckoutRefOptions{}) - }, nil) + return gui.createConfirmationPanel(createConfirmationPanelOpts{ + returnToView: gui.getCommitsView(), + returnFocusOnClose: true, + title: gui.Tr.SLocalize("checkoutCommit"), + prompt: gui.Tr.SLocalize("SureCheckoutThisCommit"), + handleConfirm: func() error { + return gui.handleCheckoutRef(commit.Sha, handleCheckoutRefOptions{}) + }, + }) } func (gui *Gui) renderBranchCommitsWithSelection() error { diff --git a/pkg/gui/confirmation_panel.go b/pkg/gui/confirmation_panel.go index 037be36bd..40fbca36d 100644 --- a/pkg/gui/confirmation_panel.go +++ b/pkg/gui/confirmation_panel.go @@ -15,11 +15,76 @@ import ( "github.com/jesseduffield/lazygit/pkg/theme" ) -func (gui *Gui) wrappedConfirmationFunction(function func(*gocui.Gui, *gocui.View) error, returnFocusOnClose bool) func(*gocui.Gui, *gocui.View) error { +type createPopupPanelOpts struct { + returnToView *gocui.View + hasLoader bool + returnFocusOnClose bool + editable bool + title string + prompt string + handleConfirm func() error + handleConfirmPrompt func(string) error + handleClose func() error +} + +type createConfirmationPanelOpts struct { + returnToView *gocui.View + returnFocusOnClose bool + title string + prompt string + handleConfirm func() error + handleClose func() error +} + +func (gui *Gui) createLoaderPanel(currentView *gocui.View, prompt string) error { + return gui.createPopupPanel(createPopupPanelOpts{ + returnToView: currentView, + prompt: prompt, + hasLoader: true, + returnFocusOnClose: true, + }) +} + +func (gui *Gui) createConfirmationPanel(opts createConfirmationPanelOpts) error { + return gui.createPopupPanel(createPopupPanelOpts{ + returnToView: opts.returnToView, + title: opts.title, + prompt: opts.prompt, + returnFocusOnClose: opts.returnFocusOnClose, + handleConfirm: opts.handleConfirm, + handleClose: opts.handleClose, + }) +} + +func (gui *Gui) createPromptPanel(currentView *gocui.View, title string, initialContent string, handleConfirm func(string) error) error { + return gui.createPopupPanel(createPopupPanelOpts{ + returnToView: currentView, + title: title, + prompt: initialContent, + returnFocusOnClose: true, + editable: true, + handleConfirmPrompt: handleConfirm, + }) +} + +func (gui *Gui) wrappedConfirmationFunction(function func() error, returnFocusOnClose bool) func(*gocui.Gui, *gocui.View) error { return func(g *gocui.Gui, v *gocui.View) error { if function != nil { - if err := function(g, v); err != nil { + if err := function(); err != nil { + return err + } + } + + return gui.closeConfirmationPrompt(g, returnFocusOnClose) + } +} + +func (gui *Gui) wrappedPromptConfirmationFunction(function func(string) error, returnFocusOnClose bool) func(*gocui.Gui, *gocui.View) error { + return func(g *gocui.Gui, v *gocui.View) error { + + if function != nil { + if err := function(v.Buffer()); err != nil { return err } } @@ -96,7 +161,7 @@ func (gui *Gui) prepareConfirmationPanel(currentView *gocui.View, title, prompt confirmationView.FgColor = theme.GocuiDefaultTextColor } gui.g.Update(func(g *gocui.Gui) error { - return gui.switchFocus(gui.g, currentView, confirmationView) + return gui.switchFocus(currentView, confirmationView) }) return confirmationView, nil } @@ -110,21 +175,21 @@ func (gui *Gui) onNewPopupPanel() { } } -func (gui *Gui) createPopupPanel(g *gocui.Gui, currentView *gocui.View, title, prompt string, hasLoader bool, returnFocusOnClose bool, editable bool, handleConfirm, handleClose func(*gocui.Gui, *gocui.View) error) error { +func (gui *Gui) createPopupPanel(opts createPopupPanelOpts) error { gui.onNewPopupPanel() - g.Update(func(g *gocui.Gui) error { + gui.g.Update(func(g *gocui.Gui) error { // delete the existing confirmation panel if it exists if view, _ := g.View("confirmation"); view != nil { if err := gui.closeConfirmationPrompt(g, true); err != nil { gui.Log.Error(err) } } - confirmationView, err := gui.prepareConfirmationPanel(currentView, title, prompt, hasLoader) + confirmationView, err := gui.prepareConfirmationPanel(opts.returnToView, opts.title, opts.prompt, opts.hasLoader) if err != nil { return err } - confirmationView.Editable = editable - if editable { + confirmationView.Editable = opts.editable + if opts.editable { go func() { // TODO: remove this wait (right now if you remove it the EditGotoToEndOfLine method doesn't seem to work) time.Sleep(time.Millisecond) @@ -135,26 +200,13 @@ func (gui *Gui) createPopupPanel(g *gocui.Gui, currentView *gocui.View, title, p }() } - gui.renderString(g, "confirmation", prompt) - return gui.setKeyBindings(g, handleConfirm, handleClose, returnFocusOnClose) + gui.renderString("confirmation", opts.prompt) + return gui.setKeyBindings(opts) }) return nil } -func (gui *Gui) createLoaderPanel(g *gocui.Gui, currentView *gocui.View, prompt string) error { - return gui.createPopupPanel(g, currentView, "", prompt, true, true, false, nil, nil) -} - -// it is very important that within this function we never include the original prompt in any error messages, because it may contain e.g. a user password -func (gui *Gui) createConfirmationPanel(g *gocui.Gui, currentView *gocui.View, returnFocusOnClose bool, title, prompt string, handleConfirm, handleClose func(*gocui.Gui, *gocui.View) error) error { - return gui.createPopupPanel(g, currentView, title, prompt, false, returnFocusOnClose, false, handleConfirm, handleClose) -} - -func (gui *Gui) createPromptPanel(g *gocui.Gui, currentView *gocui.View, title string, initialContent string, handleConfirm func(*gocui.Gui, *gocui.View) error) error { - return gui.createPopupPanel(gui.g, currentView, title, initialContent, false, true, true, handleConfirm, nil) -} - -func (gui *Gui) setKeyBindings(g *gocui.Gui, handleConfirm, handleClose func(*gocui.Gui, *gocui.View) error, returnFocusOnClose bool) error { +func (gui *Gui) setKeyBindings(opts createPopupPanelOpts) error { actions := gui.Tr.TemplateLocalize( "CloseConfirm", Teml{ @@ -163,40 +215,33 @@ func (gui *Gui) setKeyBindings(g *gocui.Gui, handleConfirm, handleClose func(*go }, ) - gui.renderString(g, "options", actions) - if err := g.SetKeybinding("confirmation", nil, gocui.KeyEnter, gocui.ModNone, gui.wrappedConfirmationFunction(handleConfirm, returnFocusOnClose)); err != nil { - return err - } - return g.SetKeybinding("confirmation", nil, gocui.KeyEsc, gocui.ModNone, gui.wrappedConfirmationFunction(handleClose, returnFocusOnClose)) -} - -// createSpecificErrorPanel allows you to create an error popup, specifying the -// view to be focused when the user closes the popup, and a boolean specifying -// whether we will log the error. If the message may include a user password, -// this function is to be used over the more generic createErrorPanel, with -// willLog set to false -func (gui *Gui) createSpecificErrorPanel(message string, nextView *gocui.View, willLog bool) error { - if willLog { - go func() { - // when reporting is switched on this log call sometimes introduces - // a delay on the error panel popping up. Here I'm adding a second wait - // so that the error is logged while the user is reading the error message - time.Sleep(time.Second) - gui.Log.Error(message) - }() + gui.renderString("options", actions) + if opts.handleConfirmPrompt != nil { + if err := gui.g.SetKeybinding("confirmation", nil, gocui.KeyEnter, gocui.ModNone, gui.wrappedPromptConfirmationFunction(opts.handleConfirmPrompt, opts.returnFocusOnClose)); err != nil { + return err + } + } else { + if err := gui.g.SetKeybinding("confirmation", nil, gocui.KeyEnter, gocui.ModNone, gui.wrappedConfirmationFunction(opts.handleConfirm, opts.returnFocusOnClose)); err != nil { + return err + } } + return gui.g.SetKeybinding("confirmation", nil, gocui.KeyEsc, gocui.ModNone, gui.wrappedConfirmationFunction(opts.handleClose, opts.returnFocusOnClose)) +} + +func (gui *Gui) createErrorPanel(message string) error { colorFunction := color.New(color.FgRed).SprintFunc() coloredMessage := colorFunction(strings.TrimSpace(message)) if err := gui.refreshSidePanels(refreshOptions{mode: ASYNC}); err != nil { return err } - return gui.createConfirmationPanel(gui.g, nextView, true, gui.Tr.SLocalize("Error"), coloredMessage, nil, nil) -} - -func (gui *Gui) createErrorPanel(message string) error { - return gui.createSpecificErrorPanel(message, gui.g.CurrentView(), true) + return gui.createConfirmationPanel(createConfirmationPanelOpts{ + returnToView: gui.g.CurrentView(), + title: gui.Tr.SLocalize("Error"), + prompt: coloredMessage, + returnFocusOnClose: true, + }) } func (gui *Gui) surfaceError(err error) error { diff --git a/pkg/gui/credentials_panel.go b/pkg/gui/credentials_panel.go index 9333c2e7a..7858f26f4 100644 --- a/pkg/gui/credentials_panel.go +++ b/pkg/gui/credentials_panel.go @@ -20,7 +20,7 @@ func (gui *Gui) promptUserForCredential(passOrUname string) string { credentialsView.Title = gui.Tr.SLocalize("CredentialsPassword") credentialsView.Mask = '*' } - err := gui.switchFocus(g, gui.g.CurrentView(), credentialsView) + err := gui.switchFocus(gui.g.CurrentView(), credentialsView) if err != nil { return err } @@ -43,7 +43,7 @@ func (gui *Gui) handleSubmitCredential(g *gocui.Gui, v *gocui.View) error { if err != nil { nextView = gui.getFilesView() } - err = gui.switchFocus(g, nil, nextView) + err = gui.switchFocus(nil, nextView) if err != nil { return err } @@ -57,7 +57,7 @@ func (gui *Gui) handleCloseCredentialsView(g *gocui.Gui, v *gocui.View) error { } gui.credentials <- "" - return gui.switchFocus(g, nil, gui.getFilesView()) + return gui.switchFocus(nil, gui.getFilesView()) } func (gui *Gui) handleCredentialsViewFocused(g *gocui.Gui, v *gocui.View) error { @@ -72,7 +72,7 @@ func (gui *Gui) handleCredentialsViewFocused(g *gocui.Gui, v *gocui.View) error "keyBindConfirm": "enter", }, ) - gui.renderString(g, "options", message) + gui.renderString("options", message) return nil } @@ -85,7 +85,7 @@ func (gui *Gui) handleCredentialsPopup(cmdErr error) { errMessage = gui.Tr.SLocalize("PassUnameWrong") } // we are not logging this error because it may contain a password - _ = gui.createSpecificErrorPanel(errMessage, gui.getFilesView(), false) + gui.createErrorPanel(errMessage) } else { _ = gui.closeConfirmationPrompt(gui.g, true) } diff --git a/pkg/gui/diffing.go b/pkg/gui/diffing.go index 633efa600..24c351b00 100644 --- a/pkg/gui/diffing.go +++ b/pkg/gui/diffing.go @@ -150,8 +150,8 @@ func (gui *Gui) handleCreateDiffingMenuPanel(g *gocui.Gui, v *gocui.View) error { displayString: gui.Tr.SLocalize("enterRefToDiff"), onPress: func() error { - return gui.createPromptPanel(gui.g, v, gui.Tr.SLocalize("enteRefName"), "", func(g *gocui.Gui, promptView *gocui.View) error { - gui.State.Diff.Ref = strings.TrimSpace(promptView.Buffer()) + return gui.createPromptPanel(v, gui.Tr.SLocalize("enteRefName"), "", func(response string) error { + gui.State.Diff.Ref = strings.TrimSpace(response) return gui.refreshSidePanels(refreshOptions{mode: ASYNC}) }) }, diff --git a/pkg/gui/files_panel.go b/pkg/gui/files_panel.go index 994a49a0a..125a06651 100644 --- a/pkg/gui/files_panel.go +++ b/pkg/gui/files_panel.go @@ -172,7 +172,7 @@ func (gui *Gui) enterFile(forceSecondaryFocused bool, selectedLineIdx int) error return gui.createErrorPanel(gui.Tr.SLocalize("FileStagingRequirements")) } gui.changeMainViewsContext("staging") - if err := gui.switchFocus(gui.g, gui.getFilesView(), gui.getMainView()); err != nil { + if err := gui.switchFocus(gui.getFilesView(), gui.getMainView()); err != nil { return err } return gui.refreshStagingPanel(forceSecondaryFocused, selectedLineIdx) @@ -249,9 +249,12 @@ func (gui *Gui) handleIgnoreFile(g *gocui.Gui, v *gocui.View) error { } if file.Tracked { - return gui.createConfirmationPanel(gui.g, gui.g.CurrentView(), true, gui.Tr.SLocalize("IgnoreTracked"), gui.Tr.SLocalize("IgnoreTrackedPrompt"), - // On confirmation - func(_ *gocui.Gui, _ *gocui.View) error { + return gui.createConfirmationPanel(createConfirmationPanelOpts{ + returnToView: gui.g.CurrentView(), + returnFocusOnClose: true, + title: gui.Tr.SLocalize("IgnoreTracked"), + prompt: gui.Tr.SLocalize("IgnoreTrackedPrompt"), + handleConfirm: func() error { if err := gui.GitCommand.Ignore(file.Name); err != nil { return err } @@ -259,7 +262,8 @@ func (gui *Gui) handleIgnoreFile(g *gocui.Gui, v *gocui.View) error { return err } return gui.refreshSidePanels(refreshOptions{scope: []int{FILES}}) - }, nil) + }, + }) } if err := gui.GitCommand.Ignore(file.Name); err != nil { @@ -275,7 +279,7 @@ func (gui *Gui) handleWIPCommitPress(g *gocui.Gui, filesView *gocui.View) error return gui.createErrorPanel(gui.Tr.SLocalize("SkipHookPrefixNotConfigured")) } - gui.renderString(g, "commitMessage", skipHookPreifx) + gui.renderString("commitMessage", skipHookPreifx) if err := gui.getCommitMessageView().SetCursor(len(skipHookPreifx), 0); err != nil { return err } @@ -299,7 +303,7 @@ func (gui *Gui) handleCommitPress(g *gocui.Gui, filesView *gocui.View) error { return gui.createErrorPanel(fmt.Sprintf("%s: %s", gui.Tr.SLocalize("commitPrefixPatternError"), err.Error())) } prefix := rgx.ReplaceAllString(gui.getCheckedOutBranch().Name, prefixReplace) - gui.renderString(g, "commitMessage", prefix) + gui.renderString("commitMessage", prefix) if err := commitMessageView.SetCursor(len(prefix), 0); err != nil { return err } @@ -310,7 +314,7 @@ func (gui *Gui) handleCommitPress(g *gocui.Gui, filesView *gocui.View) error { return err } - if err := gui.switchFocus(g, filesView, commitMessageView); err != nil { + if err := gui.switchFocus(filesView, commitMessageView); err != nil { return err } @@ -321,9 +325,12 @@ func (gui *Gui) handleCommitPress(g *gocui.Gui, filesView *gocui.View) error { } func (gui *Gui) promptToStageAllAndRetry(retry func() error) error { - return gui.createConfirmationPanel( - gui.g, gui.getFilesView(), true, gui.Tr.SLocalize("NoFilesStagedTitle"), gui.Tr.SLocalize("NoFilesStagedPrompt"), - func(*gocui.Gui, *gocui.View) error { + return gui.createConfirmationPanel(createConfirmationPanelOpts{ + returnToView: gui.getFilesView(), + returnFocusOnClose: true, + title: gui.Tr.SLocalize("NoFilesStagedTitle"), + prompt: gui.Tr.SLocalize("NoFilesStagedPrompt"), + handleConfirm: func() error { if err := gui.GitCommand.StageAll(); err != nil { return gui.surfaceError(err) } @@ -332,7 +339,8 @@ func (gui *Gui) promptToStageAllAndRetry(retry func() error) error { } return retry() - }, nil) + }, + }) } func (gui *Gui) handleAmendCommitPress(g *gocui.Gui, filesView *gocui.View) error { @@ -346,20 +354,23 @@ func (gui *Gui) handleAmendCommitPress(g *gocui.Gui, filesView *gocui.View) erro return gui.createErrorPanel(gui.Tr.SLocalize("NoCommitToAmend")) } - title := strings.Title(gui.Tr.SLocalize("AmendLastCommit")) - question := gui.Tr.SLocalize("SureToAmend") - - return gui.createConfirmationPanel(g, filesView, true, title, question, func(g *gocui.Gui, v *gocui.View) error { - ok, err := gui.runSyncOrAsyncCommand(gui.GitCommand.AmendHead()) - if err != nil { - return err - } - if !ok { - return nil - } + return gui.createConfirmationPanel(createConfirmationPanelOpts{ + returnToView: filesView, + returnFocusOnClose: true, + title: strings.Title(gui.Tr.SLocalize("AmendLastCommit")), + prompt: gui.Tr.SLocalize("SureToAmend"), + handleConfirm: func() error { + ok, err := gui.runSyncOrAsyncCommand(gui.GitCommand.AmendHead()) + if err != nil { + return err + } + if !ok { + return nil + } - return gui.refreshSidePanels(refreshOptions{mode: ASYNC}) - }, nil) + return gui.refreshSidePanels(refreshOptions{mode: ASYNC}) + }, + }) } // handleCommitEditorPress - handle when the user wants to commit changes via @@ -450,8 +461,7 @@ func (gui *Gui) handlePullFiles(g *gocui.Gui, v *gocui.View) error { } } - return gui.createPromptPanel(g, v, gui.Tr.SLocalize("EnterUpstream"), "origin/"+currentBranch.Name, func(g *gocui.Gui, v *gocui.View) error { - upstream := gui.trimmedContent(v) + return gui.createPromptPanel(v, gui.Tr.SLocalize("EnterUpstream"), "origin/"+currentBranch.Name, func(upstream string) error { if err := gui.GitCommand.SetUpstreamBranch(upstream); err != nil { errorMessage := err.Error() if strings.Contains(errorMessage, "does not exist") { @@ -472,7 +482,7 @@ type PullFilesOptions struct { } func (gui *Gui) pullFiles(opts PullFilesOptions) error { - if err := gui.createLoaderPanel(gui.g, gui.g.CurrentView(), gui.Tr.SLocalize("PullWait")); err != nil { + if err := gui.createLoaderPanel(gui.g.CurrentView(), gui.Tr.SLocalize("PullWait")); err != nil { return err } @@ -512,16 +522,22 @@ func (gui *Gui) pullWithMode(mode string, opts PullFilesOptions) error { } func (gui *Gui) pushWithForceFlag(g *gocui.Gui, v *gocui.View, force bool, upstream string, args string) error { - if err := gui.createLoaderPanel(gui.g, v, gui.Tr.SLocalize("PushWait")); err != nil { + if err := gui.createLoaderPanel(v, gui.Tr.SLocalize("PushWait")); err != nil { return err } go func() { branchName := gui.getCheckedOutBranch().Name err := gui.GitCommand.Push(branchName, force, upstream, args, gui.promptUserForCredential) if err != nil && !force && strings.Contains(err.Error(), "Updates were rejected") { - gui.createConfirmationPanel(g, v, true, gui.Tr.SLocalize("ForcePush"), gui.Tr.SLocalize("ForcePushPrompt"), func(g *gocui.Gui, v *gocui.View) error { - return gui.pushWithForceFlag(gui.g, v, true, upstream, args) - }, nil) + gui.createConfirmationPanel(createConfirmationPanelOpts{ + returnToView: v, + returnFocusOnClose: true, + title: gui.Tr.SLocalize("ForcePush"), + prompt: gui.Tr.SLocalize("ForcePushPrompt"), + handleConfirm: func() error { + return gui.pushWithForceFlag(gui.g, v, true, upstream, args) + }, + }) return } @@ -550,16 +566,23 @@ func (gui *Gui) pushFiles(g *gocui.Gui, v *gocui.View) error { if gui.GitCommand.PushToCurrent { return gui.pushWithForceFlag(g, v, false, "", "--set-upstream") } else { - return gui.createPromptPanel(g, v, gui.Tr.SLocalize("EnterUpstream"), "origin "+currentBranch.Name, func(g *gocui.Gui, v *gocui.View) error { - return gui.pushWithForceFlag(g, v, false, gui.trimmedContent(v), "") + return gui.createPromptPanel(v, gui.Tr.SLocalize("EnterUpstream"), "origin "+currentBranch.Name, func(response string) error { + return gui.pushWithForceFlag(g, v, false, response, "") }) } } else if currentBranch.Pullables == "0" { return gui.pushWithForceFlag(g, v, false, "", "") } - return gui.createConfirmationPanel(g, v, true, gui.Tr.SLocalize("ForcePush"), gui.Tr.SLocalize("ForcePushPrompt"), func(g *gocui.Gui, v *gocui.View) error { - return gui.pushWithForceFlag(g, v, true, "", "") - }, nil) + + return gui.createConfirmationPanel(createConfirmationPanelOpts{ + returnToView: v, + returnFocusOnClose: true, + title: gui.Tr.SLocalize("ForcePush"), + prompt: gui.Tr.SLocalize("ForcePushPrompt"), + handleConfirm: func() error { + return gui.pushWithForceFlag(g, v, true, "", "") + }, + }) } func (gui *Gui) handleSwitchToMerge(g *gocui.Gui, v *gocui.View) error { @@ -574,7 +597,7 @@ func (gui *Gui) handleSwitchToMerge(g *gocui.Gui, v *gocui.View) error { return gui.createErrorPanel(gui.Tr.SLocalize("FileNoMergeCons")) } gui.changeMainViewsContext("merging") - if err := gui.switchFocus(g, v, gui.getMainView()); err != nil { + if err := gui.switchFocus(v, gui.getMainView()); err != nil { return err } return gui.refreshMergePanel() @@ -597,8 +620,7 @@ func (gui *Gui) anyFilesWithMergeConflicts() bool { } func (gui *Gui) handleCustomCommand(g *gocui.Gui, v *gocui.View) error { - return gui.createPromptPanel(g, v, gui.Tr.SLocalize("CustomCommand"), "", func(g *gocui.Gui, v *gocui.View) error { - command := gui.trimmedContent(v) + return gui.createPromptPanel(v, gui.Tr.SLocalize("CustomCommand"), "", func(command string) error { gui.SubProcess = gui.OSCommand.RunCustomCommand(command) return gui.Errors.ErrSubProcess }) diff --git a/pkg/gui/filtering.go b/pkg/gui/filtering.go index 66232e3fb..3acb3c34a 100644 --- a/pkg/gui/filtering.go +++ b/pkg/gui/filtering.go @@ -1,16 +1,22 @@ package gui -import "github.com/jesseduffield/gocui" - func (gui *Gui) inFilterMode() bool { return gui.State.FilterPath != "" } func (gui *Gui) validateNotInFilterMode() (bool, error) { if gui.inFilterMode() { - return false, gui.createConfirmationPanel(gui.g, gui.g.CurrentView(), true, gui.Tr.SLocalize("MustExitFilterModeTitle"), gui.Tr.SLocalize("MustExitFilterModePrompt"), func(*gocui.Gui, *gocui.View) error { - return gui.exitFilterMode() - }, nil) + err := gui.createConfirmationPanel(createConfirmationPanelOpts{ + returnToView: gui.g.CurrentView(), + returnFocusOnClose: true, + title: gui.Tr.SLocalize("MustExitFilterModeTitle"), + prompt: gui.Tr.SLocalize("MustExitFilterModePrompt"), + handleConfirm: func() error { + return gui.exitFilterMode() + }, + }) + + return false, err } return true, nil } diff --git a/pkg/gui/filtering_menu_panel.go b/pkg/gui/filtering_menu_panel.go index 856b71572..dcd2be5fd 100644 --- a/pkg/gui/filtering_menu_panel.go +++ b/pkg/gui/filtering_menu_panel.go @@ -41,8 +41,8 @@ func (gui *Gui) handleCreateFilteringMenuPanel(g *gocui.Gui, v *gocui.View) erro menuItems = append(menuItems, &menuItem{ displayString: gui.Tr.SLocalize("filterPathOption"), onPress: func() error { - return gui.createPromptPanel(gui.g, v, gui.Tr.SLocalize("enterFileName"), "", func(g *gocui.Gui, promptView *gocui.View) error { - gui.State.FilterPath = strings.TrimSpace(promptView.Buffer()) + return gui.createPromptPanel(v, gui.Tr.SLocalize("enterFileName"), "", func(response string) error { + gui.State.FilterPath = strings.TrimSpace(response) return gui.Errors.ErrRestart }) }, diff --git a/pkg/gui/git_flow.go b/pkg/gui/git_flow.go index 9076ae7f0..cddc0ae9d 100644 --- a/pkg/gui/git_flow.go +++ b/pkg/gui/git_flow.go @@ -51,8 +51,7 @@ func (gui *Gui) handleCreateGitFlowMenu(g *gocui.Gui, v *gocui.View) error { startHandler := func(branchType string) func() error { return func() error { title := gui.Tr.TemplateLocalize("NewBranchNamePrompt", map[string]interface{}{"branchType": branchType}) - return gui.createPromptPanel(gui.g, gui.getMenuView(), title, "", func(g *gocui.Gui, v *gocui.View) error { - name := gui.trimmedContent(v) + return gui.createPromptPanel(gui.getMenuView(), title, "", func(name string) error { subProcess := gui.OSCommand.PrepareSubProcess("git", "flow", branchType, "start", name) gui.SubProcess = subProcess return gui.Errors.ErrSubProcess diff --git a/pkg/gui/global_handlers.go b/pkg/gui/global_handlers.go index 46e558989..8282cea2d 100644 --- a/pkg/gui/global_handlers.go +++ b/pkg/gui/global_handlers.go @@ -4,7 +4,6 @@ import ( "math" "strings" - "github.com/fatih/color" "github.com/jesseduffield/gocui" "github.com/jesseduffield/lazygit/pkg/commands" "github.com/jesseduffield/lazygit/pkg/utils" @@ -176,12 +175,7 @@ func (gui *Gui) fetch(canPromptForCredentials bool) (err error) { err = gui.GitCommand.Fetch(fetchOpts) if canPromptForCredentials && err != nil && strings.Contains(err.Error(), "exit status 128") { - colorFunction := color.New(color.FgRed).SprintFunc() - coloredMessage := colorFunction(strings.TrimSpace(gui.Tr.SLocalize("PassUnameWrong"))) - close := func(g *gocui.Gui, v *gocui.View) error { - return nil - } - _ = gui.createConfirmationPanel(gui.g, gui.g.CurrentView(), true, gui.Tr.SLocalize("Error"), coloredMessage, close, close) + gui.createErrorPanel(gui.Tr.SLocalize("PassUnameWrong")) } gui.refreshSidePanels(refreshOptions{scope: []int{BRANCHES, COMMITS, REMOTES, TAGS}, mode: ASYNC}) diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index 71cda50f7..ec582e131 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -463,21 +463,35 @@ func (gui *Gui) showInitialPopups(tasks []func(chan struct{}) error) { } func (gui *Gui) showShamelessSelfPromotionMessage(done chan struct{}) error { - onConfirm := func(g *gocui.Gui, v *gocui.View) error { + onConfirm := func() error { done <- struct{}{} return gui.Config.WriteToUserConfig("startupPopupVersion", StartupPopupVersion) } - return gui.createConfirmationPanel(gui.g, nil, true, gui.Tr.SLocalize("ShamelessSelfPromotionTitle"), gui.Tr.SLocalize("ShamelessSelfPromotionMessage"), onConfirm, onConfirm) + return gui.createConfirmationPanel(createConfirmationPanelOpts{ + returnToView: nil, + returnFocusOnClose: true, + title: gui.Tr.SLocalize("ShamelessSelfPromotionTitle"), + prompt: gui.Tr.SLocalize("ShamelessSelfPromotionMessage"), + handleConfirm: onConfirm, + handleClose: onConfirm, + }) } func (gui *Gui) promptAnonymousReporting(done chan struct{}) error { - return gui.createConfirmationPanel(gui.g, nil, true, gui.Tr.SLocalize("AnonymousReportingTitle"), gui.Tr.SLocalize("AnonymousReportingPrompt"), func(g *gocui.Gui, v *gocui.View) error { - done <- struct{}{} - return gui.Config.WriteToUserConfig("reporting", "on") - }, func(g *gocui.Gui, v *gocui.View) error { - done <- struct{}{} - return gui.Config.WriteToUserConfig("reporting", "off") + return gui.createConfirmationPanel(createConfirmationPanelOpts{ + returnToView: nil, + returnFocusOnClose: true, + title: gui.Tr.SLocalize("AnonymousReportingTitle"), + prompt: gui.Tr.SLocalize("AnonymousReportingPrompt"), + handleConfirm: func() error { + done <- struct{}{} + return gui.Config.WriteToUserConfig("reporting", "on") + }, + handleClose: func() error { + done <- struct{}{} + return gui.Config.WriteToUserConfig("reporting", "off") + }, }) } @@ -504,7 +518,12 @@ func (gui *Gui) startBackgroundFetch() { } err := gui.fetch(false) if err != nil && strings.Contains(err.Error(), "exit status 128") && isNew { - _ = gui.createConfirmationPanel(gui.g, gui.g.CurrentView(), true, gui.Tr.SLocalize("NoAutomaticGitFetchTitle"), gui.Tr.SLocalize("NoAutomaticGitFetchBody"), nil, nil) + _ = gui.createConfirmationPanel(createConfirmationPanelOpts{ + returnToView: gui.g.CurrentView(), + returnFocusOnClose: true, + title: gui.Tr.SLocalize("NoAutomaticGitFetchTitle"), + prompt: gui.Tr.SLocalize("NoAutomaticGitFetchBody"), + }) } else { gui.goEvery(time.Second*60, gui.stopChan, func() error { err := gui.fetch(false) diff --git a/pkg/gui/layout.go b/pkg/gui/layout.go index 9f49c0d25..8eecdda48 100644 --- a/pkg/gui/layout.go +++ b/pkg/gui/layout.go @@ -326,7 +326,7 @@ func (gui *Gui) layout(g *gocui.Gui) error { informationView.BgColor = gocui.ColorDefault informationView.FgColor = gocui.ColorGreen informationView.Frame = false - gui.renderString(g, "information", INFO_SECTION_PADDING+informationStr) + gui.renderString("information", INFO_SECTION_PADDING+informationStr) } if gui.State.OldInformation != informationStr { gui.setViewContent(informationView, informationStr) @@ -342,7 +342,7 @@ func (gui *Gui) layout(g *gocui.Gui) error { return err } - if err := gui.switchFocus(gui.g, nil, initialView); err != nil { + if err := gui.switchFocus(nil, initialView); err != nil { return err } } diff --git a/pkg/gui/menu_panel.go b/pkg/gui/menu_panel.go index 9b2eefac5..b8fc80b81 100644 --- a/pkg/gui/menu_panel.go +++ b/pkg/gui/menu_panel.go @@ -119,7 +119,7 @@ func (gui *Gui) createMenu(title string, items []*menuItem, createMenuOptions cr } } currentView := gui.g.CurrentView() - return gui.switchFocus(gui.g, currentView, menuView) + return gui.switchFocus(currentView, menuView) }) return nil } diff --git a/pkg/gui/merge_panel.go b/pkg/gui/merge_panel.go index a70d7fbbb..5918b9476 100644 --- a/pkg/gui/merge_panel.go +++ b/pkg/gui/merge_panel.go @@ -308,7 +308,7 @@ func (gui *Gui) handleEscapeMerge(g *gocui.Gui, v *gocui.View) error { // it's possible this method won't be called from the merging view so we need to // ensure we only 'return' focus if we already have it if gui.g.CurrentView() == gui.getMainView() { - return gui.switchFocus(g, v, gui.getFilesView()) + return gui.switchFocus(v, gui.getFilesView()) } return nil } @@ -336,9 +336,15 @@ func (gui *Gui) handleCompleteMerge() error { func (gui *Gui) promptToContinue() error { gui.takeOverScrolling() - return gui.createConfirmationPanel(gui.g, gui.getFilesView(), true, "continue", gui.Tr.SLocalize("ConflictsResolved"), func(g *gocui.Gui, v *gocui.View) error { - return gui.genericMergeCommand("continue") - }, nil) + return gui.createConfirmationPanel(createConfirmationPanelOpts{ + returnToView: gui.getFilesView(), + returnFocusOnClose: true, + title: "continue", + prompt: gui.Tr.SLocalize("ConflictsResolved"), + handleConfirm: func() error { + return gui.genericMergeCommand("continue") + }, + }) } func (gui *Gui) canScrollMergePanel() bool { diff --git a/pkg/gui/patch_building_panel.go b/pkg/gui/patch_building_panel.go index def631d47..ad05f33c9 100644 --- a/pkg/gui/patch_building_panel.go +++ b/pkg/gui/patch_building_panel.go @@ -18,7 +18,7 @@ func (gui *Gui) refreshPatchBuildingPanel(selectedLineIdx int) error { // get diff from commit file that's currently selected commitFile := gui.getSelectedCommitFile() if commitFile == nil { - gui.renderString(gui.g, "commitFiles", gui.Tr.SLocalize("NoCommiteFiles")) + gui.renderString("commitFiles", gui.Tr.SLocalize("NoCommiteFiles")) return nil } @@ -58,7 +58,7 @@ func (gui *Gui) handleToggleSelectionForPatch(g *gocui.Gui, v *gocui.View) error // add range of lines to those set for the file commitFile := gui.getSelectedCommitFile() if commitFile == nil { - gui.renderString(gui.g, "commitFiles", gui.Tr.SLocalize("NoCommiteFiles")) + gui.renderString("commitFiles", gui.Tr.SLocalize("NoCommiteFiles")) return nil } @@ -83,7 +83,7 @@ func (gui *Gui) handleEscapePatchBuildingPanel(g *gocui.Gui, v *gocui.View) erro gui.State.SplitMainPanel = false } - return gui.switchFocus(gui.g, nil, gui.getCommitFilesView()) + return gui.switchFocus(nil, gui.getCommitFilesView()) } func (gui *Gui) refreshSecondaryPatchPanel() error { diff --git a/pkg/gui/patch_options_panel.go b/pkg/gui/patch_options_panel.go index 4ec3cd228..5407e7c31 100644 --- a/pkg/gui/patch_options_panel.go +++ b/pkg/gui/patch_options_panel.go @@ -133,9 +133,15 @@ func (gui *Gui) handlePullPatchIntoWorkingTree() error { } if len(gui.trackedFiles()) > 0 { - return gui.createConfirmationPanel(gui.g, gui.g.CurrentView(), true, gui.Tr.SLocalize("MustStashTitle"), gui.Tr.SLocalize("MustStashWarning"), func(*gocui.Gui, *gocui.View) error { - return pull(true) - }, nil) + return gui.createConfirmationPanel(createConfirmationPanelOpts{ + returnToView: gui.g.CurrentView(), + returnFocusOnClose: true, + title: gui.Tr.SLocalize("MustStashTitle"), + prompt: gui.Tr.SLocalize("MustStashWarning"), + handleConfirm: func() error { + return pull(true) + }, + }) } else { return pull(false) } diff --git a/pkg/gui/quitting.go b/pkg/gui/quitting.go index b6cd57e23..915babcca 100644 --- a/pkg/gui/quitting.go +++ b/pkg/gui/quitting.go @@ -55,9 +55,15 @@ func (gui *Gui) quit(v *gocui.View) error { } if gui.Config.GetUserConfig().GetBool("confirmOnQuit") { - return gui.createConfirmationPanel(gui.g, v, true, "", gui.Tr.SLocalize("ConfirmQuit"), func(g *gocui.Gui, v *gocui.View) error { - return gocui.ErrQuit - }, nil) + return gui.createConfirmationPanel(createConfirmationPanelOpts{ + returnToView: v, + returnFocusOnClose: true, + title: "", + prompt: gui.Tr.SLocalize("ConfirmQuit"), + handleConfirm: func() error { + return gocui.ErrQuit + }, + }) } return gocui.ErrQuit diff --git a/pkg/gui/rebase_options_panel.go b/pkg/gui/rebase_options_panel.go index d34733591..ae0db7d0f 100644 --- a/pkg/gui/rebase_options_panel.go +++ b/pkg/gui/rebase_options_panel.go @@ -78,13 +78,18 @@ func (gui *Gui) handleGenericMergeCommandResult(result error) error { // assume in this case that we're already done return nil } else if strings.Contains(result.Error(), "When you have resolved this problem") || strings.Contains(result.Error(), "fix conflicts") || strings.Contains(result.Error(), "Resolve all conflicts manually") { - return gui.createConfirmationPanel(gui.g, gui.getFilesView(), true, gui.Tr.SLocalize("FoundConflictsTitle"), gui.Tr.SLocalize("FoundConflicts"), - func(g *gocui.Gui, v *gocui.View) error { + return gui.createConfirmationPanel(createConfirmationPanelOpts{ + returnToView: gui.getFilesView(), + returnFocusOnClose: true, + title: gui.Tr.SLocalize("FoundConflictsTitle"), + prompt: gui.Tr.SLocalize("FoundConflicts"), + handleConfirm: func() error { return nil - }, func(g *gocui.Gui, v *gocui.View) error { + }, + handleClose: func() error { return gui.genericMergeCommand("abort") }, - ) + }) } else { return gui.createErrorPanel(result.Error()) } diff --git a/pkg/gui/reflog_panel.go b/pkg/gui/reflog_panel.go index 0e69cbbd8..b985f0faa 100644 --- a/pkg/gui/reflog_panel.go +++ b/pkg/gui/reflog_panel.go @@ -121,9 +121,15 @@ func (gui *Gui) handleCheckoutReflogCommit(g *gocui.Gui, v *gocui.View) error { return nil } - err := gui.createConfirmationPanel(g, gui.getCommitsView(), true, gui.Tr.SLocalize("checkoutCommit"), gui.Tr.SLocalize("SureCheckoutThisCommit"), func(g *gocui.Gui, v *gocui.View) error { - return gui.handleCheckoutRef(commit.Sha, handleCheckoutRefOptions{}) - }, nil) + err := gui.createConfirmationPanel(createConfirmationPanelOpts{ + returnToView: gui.getCommitsView(), + returnFocusOnClose: true, + title: gui.Tr.SLocalize("checkoutCommit"), + prompt: gui.Tr.SLocalize("SureCheckoutThisCommit"), + handleConfirm: func() error { + return gui.handleCheckoutRef(commit.Sha, handleCheckoutRefOptions{}) + }, + }) if err != nil { return err } diff --git a/pkg/gui/remote_branches_panel.go b/pkg/gui/remote_branches_panel.go index 4d92e6f7e..be0092359 100644 --- a/pkg/gui/remote_branches_panel.go +++ b/pkg/gui/remote_branches_panel.go @@ -94,15 +94,22 @@ func (gui *Gui) handleDeleteRemoteBranch(g *gocui.Gui, v *gocui.View) error { return nil } message := fmt.Sprintf("%s '%s'?", gui.Tr.SLocalize("DeleteRemoteBranchMessage"), remoteBranch.FullName()) - return gui.createConfirmationPanel(g, v, true, gui.Tr.SLocalize("DeleteRemoteBranch"), message, func(*gocui.Gui, *gocui.View) error { - return gui.WithWaitingStatus(gui.Tr.SLocalize("DeletingStatus"), func() error { - if err := gui.GitCommand.DeleteRemoteBranch(