mirror of
https://github.com/jesseduffield/lazygit.git
synced 2024-11-28 09:08:41 +02:00
start refactoring gui
This commit is contained in:
parent
fa8571e1f4
commit
a90b6efded
@ -106,8 +106,8 @@ func (gui *Gui) renderAppStatus() {
|
||||
})
|
||||
}
|
||||
|
||||
// WithWaitingStatus wraps a function and shows a waiting status while the function is still executing
|
||||
func (gui *Gui) WithWaitingStatus(message string, f func() error) error {
|
||||
// withWaitingStatus wraps a function and shows a waiting status while the function is still executing
|
||||
func (gui *Gui) withWaitingStatus(message string, f func() error) error {
|
||||
go utils.Safe(func() {
|
||||
id := gui.statusManager.addWaitingStatus(message)
|
||||
|
||||
@ -119,7 +119,7 @@ func (gui *Gui) WithWaitingStatus(message string, f func() error) error {
|
||||
|
||||
if err := f(); err != nil {
|
||||
gui.OnUIThread(func() error {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
@ -7,6 +7,8 @@ import (
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
@ -62,7 +64,7 @@ func (gui *Gui) refreshBranches() {
|
||||
|
||||
branches, err := gui.Git.Loaders.Branches.Load(reflogCommits)
|
||||
if err != nil {
|
||||
_ = gui.surfaceError(err)
|
||||
_ = gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
gui.State.Branches = branches
|
||||
@ -81,7 +83,7 @@ func (gui *Gui) handleBranchPress() error {
|
||||
return nil
|
||||
}
|
||||
if gui.State.Panels.Branches.SelectedLineIdx == 0 {
|
||||
return gui.createErrorPanel(gui.Tr.AlreadyCheckedOutBranch)
|
||||
return gui.PopupHandler.ErrorMsg(gui.Tr.AlreadyCheckedOutBranch)
|
||||
}
|
||||
branch := gui.getSelectedBranch()
|
||||
gui.logAction(gui.Tr.Actions.CheckoutBranch)
|
||||
@ -111,16 +113,16 @@ func (gui *Gui) handleCopyPullRequestURLPress() error {
|
||||
branchExistsOnRemote := gui.Git.Remote.CheckRemoteBranchExists(branch.Name)
|
||||
|
||||
if !branchExistsOnRemote {
|
||||
return gui.surfaceError(errors.New(gui.Tr.NoBranchOnRemote))
|
||||
return gui.PopupHandler.Error(errors.New(gui.Tr.NoBranchOnRemote))
|
||||
}
|
||||
|
||||
url, err := hostingServiceMgr.GetPullRequestURL(branch.Name, "")
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
gui.logAction(gui.Tr.Actions.CopyPullRequestURL)
|
||||
if err := gui.OSCommand.CopyToClipboard(url); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
gui.raiseToast(gui.Tr.PullRequestURLCopiedToClipboard)
|
||||
@ -129,16 +131,12 @@ func (gui *Gui) handleCopyPullRequestURLPress() error {
|
||||
}
|
||||
|
||||
func (gui *Gui) handleGitFetch() error {
|
||||
if err := gui.createLoaderPanel(gui.Tr.FetchWait); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go utils.Safe(func() {
|
||||
err := gui.fetch()
|
||||
gui.handleCredentialsPopup(err)
|
||||
_ = gui.refreshSidePanels(refreshOptions{mode: ASYNC})
|
||||
return gui.PopupHandler.WithLoaderPanel(gui.Tr.FetchWait, func() error {
|
||||
if err := gui.fetch(); err != nil {
|
||||
_ = gui.PopupHandler.Error(err)
|
||||
}
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC})
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) handleForceCheckout() error {
|
||||
@ -146,15 +144,15 @@ func (gui *Gui) handleForceCheckout() error {
|
||||
message := gui.Tr.SureForceCheckout
|
||||
title := gui.Tr.ForceCheckoutBranch
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: title,
|
||||
prompt: message,
|
||||
handleConfirm: func() error {
|
||||
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: title,
|
||||
Prompt: message,
|
||||
HandleConfirm: func() error {
|
||||
gui.logAction(gui.Tr.Actions.ForceCheckoutBranch)
|
||||
if err := gui.Git.Branch.Checkout(branch.Name, git_commands.CheckoutOptions{Force: true}); err != nil {
|
||||
_ = gui.surfaceError(err)
|
||||
_ = gui.PopupHandler.Error(err)
|
||||
}
|
||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC})
|
||||
},
|
||||
})
|
||||
}
|
||||
@ -180,7 +178,7 @@ func (gui *Gui) handleCheckoutRef(ref string, options handleCheckoutRefOptions)
|
||||
gui.State.Panels.Commits.LimitCommits = true
|
||||
}
|
||||
|
||||
return gui.WithWaitingStatus(waitingStatus, func() error {
|
||||
return gui.PopupHandler.WithWaitingStatus(waitingStatus, func() error {
|
||||
if err := gui.Git.Branch.Checkout(ref, cmdOptions); err != nil {
|
||||
// note, this will only work for english-language git commands. If we force git to use english, and the error isn't this one, then the user will receive an english command they may not understand. I'm not sure what the best solution to this is. Running the command once in english and a second time in the native language is one option
|
||||
|
||||
@ -190,52 +188,52 @@ 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.ask(askOpts{
|
||||
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||
|
||||
title: gui.Tr.AutoStashTitle,
|
||||
prompt: gui.Tr.AutoStashPrompt,
|
||||
handleConfirm: func() error {
|
||||
Title: gui.Tr.AutoStashTitle,
|
||||
Prompt: gui.Tr.AutoStashPrompt,
|
||||
HandleConfirm: func() error {
|
||||
if err := gui.Git.Stash.Save(gui.Tr.StashPrefix + ref); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
if err := gui.Git.Branch.Checkout(ref, cmdOptions); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
onSuccess()
|
||||
if err := gui.Git.Stash.Pop(0); err != nil {
|
||||
if err := gui.refreshSidePanels(refreshOptions{mode: BLOCK_UI}); err != nil {
|
||||
if err := gui.refreshSidePanels(types.RefreshOptions{Mode: types.BLOCK_UI}); err != nil {
|
||||
return err
|
||||
}
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
return gui.refreshSidePanels(refreshOptions{mode: BLOCK_UI})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Mode: types.BLOCK_UI})
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if err := gui.surfaceError(err); err != nil {
|
||||
if err := gui.PopupHandler.Error(err); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
onSuccess()
|
||||
|
||||
return gui.refreshSidePanels(refreshOptions{mode: BLOCK_UI})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Mode: types.BLOCK_UI})
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) handleCheckoutByName() error {
|
||||
return gui.prompt(promptOpts{
|
||||
title: gui.Tr.BranchName + ":",
|
||||
findSuggestionsFunc: gui.getRefsSuggestionsFunc(),
|
||||
handleConfirm: func(response string) error {
|
||||
return gui.PopupHandler.Prompt(popup.PromptOpts{
|
||||
Title: gui.Tr.BranchName + ":",
|
||||
FindSuggestionsFunc: gui.getRefsSuggestionsFunc(),
|
||||
HandleConfirm: func(response string) error {
|
||||
gui.logAction("Checkout branch")
|
||||
return gui.handleCheckoutRef(response, handleCheckoutRefOptions{
|
||||
onRefNotFound: func(ref string) error {
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.BranchNotFoundTitle,
|
||||
prompt: fmt.Sprintf("%s %s%s", gui.Tr.BranchNotFoundPrompt, ref, "?"),
|
||||
handleConfirm: func() error {
|
||||
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: gui.Tr.BranchNotFoundTitle,
|
||||
Prompt: fmt.Sprintf("%s %s%s", gui.Tr.BranchNotFoundPrompt, ref, "?"),
|
||||
HandleConfirm: func() error {
|
||||
return gui.createNewBranchWithName(ref)
|
||||
},
|
||||
})
|
||||
@ -260,11 +258,11 @@ func (gui *Gui) createNewBranchWithName(newBranchName string) error {
|
||||
}
|
||||
|
||||
if err := gui.Git.Branch.New(newBranchName, branch.Name); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
gui.State.Panels.Branches.SelectedLineIdx = 0
|
||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC})
|
||||
}
|
||||
|
||||
func (gui *Gui) handleDeleteBranch() error {
|
||||
@ -278,7 +276,7 @@ func (gui *Gui) deleteBranch(force bool) error {
|
||||
}
|
||||
checkedOutBranch := gui.getCheckedOutBranch()
|
||||
if checkedOutBranch.Name == selectedBranch.Name {
|
||||
return gui.createErrorPanel(gui.Tr.CantDeleteCheckOutBranch)
|
||||
return gui.PopupHandler.ErrorMsg(gui.Tr.CantDeleteCheckOutBranch)
|
||||
}
|
||||
return gui.deleteNamedBranch(selectedBranch, force)
|
||||
}
|
||||
@ -298,19 +296,19 @@ func (gui *Gui) deleteNamedBranch(selectedBranch *models.Branch, force bool) err
|
||||
},
|
||||
)
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: title,
|
||||
prompt: message,
|
||||
handleConfirm: func() error {
|
||||
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: title,
|
||||
Prompt: message,
|
||||
HandleConfirm: func() error {
|
||||
gui.logAction(gui.Tr.Actions.DeleteBranch)
|
||||
if err := gui.Git.Branch.Delete(selectedBranch.Name, force); err != nil {
|
||||
errMessage := err.Error()
|
||||
if !force && strings.Contains(errMessage, "git branch -D ") {
|
||||
return gui.deleteNamedBranch(selectedBranch, true)
|
||||
}
|
||||
return gui.createErrorPanel(errMessage)
|
||||
return gui.PopupHandler.ErrorMsg(errMessage)
|
||||
}
|
||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []RefreshableView{BRANCHES}})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.BRANCHES}})
|
||||
},
|
||||
})
|
||||
}
|
||||
@ -321,11 +319,11 @@ func (gui *Gui) mergeBranchIntoCheckedOutBranch(branchName string) error {
|
||||
}
|
||||
|
||||
if gui.Git.Branch.IsHeadDetached() {
|
||||
return gui.createErrorPanel("Cannot merge branch in detached head state. You might have checked out a commit directly or a remote branch, in which case you should checkout the local branch you want to be on")
|
||||
return gui.PopupHandler.ErrorMsg("Cannot merge branch in detached head state. You might have checked out a commit directly or a remote branch, in which case you should checkout the local branch you want to be on")
|
||||
}
|
||||
checkedOutBranchName := gui.getCheckedOutBranch().Name
|
||||
if checkedOutBranchName == branchName {
|
||||
return gui.createErrorPanel(gui.Tr.CantMergeBranchIntoItself)
|
||||
return gui.PopupHandler.ErrorMsg(gui.Tr.CantMergeBranchIntoItself)
|
||||
}
|
||||
prompt := utils.ResolvePlaceholderString(
|
||||
gui.Tr.ConfirmMerge,
|
||||
@ -335,10 +333,10 @@ func (gui *Gui) mergeBranchIntoCheckedOutBranch(branchName string) error {
|
||||
},
|
||||
)
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.MergingTitle,
|
||||
prompt: prompt,
|
||||
handleConfirm: func() error {
|
||||
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: gui.Tr.MergingTitle,
|
||||
Prompt: prompt,
|
||||
HandleConfirm: func() error {
|
||||
gui.logAction(gui.Tr.Actions.Merge)
|
||||
err := gui.Git.Branch.Merge(branchName, git_commands.MergeOpts{})
|
||||
return gui.handleGenericMergeCommandResult(err)
|
||||
@ -367,7 +365,7 @@ func (gui *Gui) handleRebaseOntoBranch(selectedBranchName string) error {
|
||||
|
||||
checkedOutBranch := gui.getCheckedOutBranch().Name
|
||||
if selectedBranchName == checkedOutBranch {
|
||||
return gui.createErrorPanel(gui.Tr.CantRebaseOntoSelf)
|
||||
return gui.PopupHandler.ErrorMsg(gui.Tr.CantRebaseOntoSelf)
|
||||
}
|
||||
prompt := utils.ResolvePlaceholderString(
|
||||
gui.Tr.ConfirmRebase,
|
||||
@ -377,10 +375,10 @@ func (gui *Gui) handleRebaseOntoBranch(selectedBranchName string) error {
|
||||
},
|
||||
)
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.RebasingTitle,
|
||||
prompt: prompt,
|
||||
handleConfirm: func() error {
|
||||
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: gui.Tr.RebasingTitle,
|
||||
Prompt: prompt,
|
||||
HandleConfirm: func() error {
|
||||
gui.logAction(gui.Tr.Actions.RebaseBranch)
|
||||
err := gui.Git.Rebase.RebaseBranch(selectedBranchName)
|
||||
return gui.handleGenericMergeCommandResult(err)
|
||||
@ -395,13 +393,13 @@ func (gui *Gui) handleFastForward() error {
|
||||
}
|
||||
|
||||
if !branch.IsTrackingRemote() {
|
||||
return gui.createErrorPanel(gui.Tr.FwdNoUpstream)
|
||||
return gui.PopupHandler.ErrorMsg(gui.Tr.FwdNoUpstream)
|
||||
}
|
||||
if !branch.RemoteBranchStoredLocally() {
|
||||
return gui.createErrorPanel(gui.Tr.FwdNoLocalUpstream)
|
||||
return gui.PopupHandler.ErrorMsg(gui.Tr.FwdNoLocalUpstream)
|
||||
}
|
||||
if branch.HasCommitsToPush() {
|
||||
return gui.createErrorPanel(gui.Tr.FwdCommitsToPush)
|
||||
return gui.PopupHandler.ErrorMsg(gui.Tr.FwdCommitsToPush)
|
||||
}
|
||||
|
||||
action := gui.Tr.Actions.FastForwardBranch
|
||||
@ -413,19 +411,21 @@ func (gui *Gui) handleFastForward() error {
|
||||
"to": branch.Name,
|
||||
},
|
||||
)
|
||||
go utils.Safe(func() {
|
||||
_ = gui.createLoaderPanel(message)
|
||||
|
||||
return gui.PopupHandler.WithLoaderPanel(message, func() error {
|
||||
if gui.State.Panels.Branches.SelectedLineIdx == 0 {
|
||||
_ = gui.pullWithLock(PullFilesOptions{action: action, FastForwardOnly: true})
|
||||
} else {
|
||||
gui.logAction(action)
|
||||
err := gui.Git.Sync.FastForward(branch.Name, branch.UpstreamRemote, branch.UpstreamBranch)
|
||||
gui.handleCredentialsPopup(err)
|
||||
_ = gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []RefreshableView{BRANCHES}})
|
||||
if err != nil {
|
||||
_ = gui.PopupHandler.Error(err)
|
||||
}
|
||||
_ = gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.BRANCHES}})
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) handleCreateResetToBranchMenu() error {
|
||||
@ -444,13 +444,13 @@ func (gui *Gui) handleRenameBranch() error {
|
||||
}
|
||||
|
||||
promptForNewName := func() error {
|
||||
return gui.prompt(promptOpts{
|
||||
title: gui.Tr.NewBranchNamePrompt + " " + branch.Name + ":",
|
||||
initialContent: branch.Name,
|
||||
handleConfirm: func(newBranchName string) error {
|
||||
return gui.PopupHandler.Prompt(popup.PromptOpts{
|
||||
Title: gui.Tr.NewBranchNamePrompt + " " + branch.Name + ":",
|
||||
InitialContent: branch.Name,
|
||||
HandleConfirm: func(newBranchName string) error {
|
||||
gui.logAction(gui.Tr.Actions.RenameBranch)
|
||||
if err := gui.Git.Branch.Rename(branch.Name, newBranchName); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
// need to find where the branch is now so that we can re-select it. That means we need to refetch the branches synchronously and then find our branch
|
||||
@ -478,10 +478,10 @@ func (gui *Gui) handleRenameBranch() error {
|
||||
return promptForNewName()
|
||||
}
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.LcRenameBranch,
|
||||
prompt: gui.Tr.RenameBranchWarning,
|
||||
handleConfirm: promptForNewName,
|
||||
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: gui.Tr.LcRenameBranch,
|
||||
Prompt: gui.Tr.RenameBranchWarning,
|
||||
HandleConfirm: promptForNewName,
|
||||
})
|
||||
}
|
||||
|
||||
@ -513,10 +513,10 @@ func (gui *Gui) handleNewBranchOffCurrentItem() error {
|
||||
prefilledName = strings.SplitAfterN(item.ID(), "/", 2)[1]
|
||||
}
|
||||
|
||||
return gui.prompt(promptOpts{
|
||||
title: message,
|
||||
initialContent: prefilledName,
|
||||
handleConfirm: func(response string) error {
|
||||
return gui.PopupHandler.Prompt(popup.PromptOpts{
|
||||
Title: message,
|
||||
InitialContent: prefilledName,
|
||||
HandleConfirm: func(response string) error {
|
||||
gui.logAction(gui.Tr.Actions.CreateBranch)
|
||||
if err := gui.Git.Branch.New(sanitizedBranchName(response), item.ID()); err != nil {
|
||||
return err
|
||||
@ -536,7 +536,7 @@ func (gui *Gui) handleNewBranchOffCurrentItem() error {
|
||||
|
||||
gui.State.Panels.Branches.SelectedLineIdx = 0
|
||||
|
||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC})
|
||||
},
|
||||
})
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
package gui
|
||||
|
||||
import "github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||
)
|
||||
|
||||
// you can only copy from one context at a time, because the order and position of commits matter
|
||||
|
||||
@ -143,11 +146,11 @@ func (gui *Gui) HandlePasteCommits() error {
|
||||
return err
|
||||
}
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.CherryPick,
|
||||
prompt: gui.Tr.SureCherryPick,
|
||||
handleConfirm: func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.CherryPickingStatus, func() error {
|
||||
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: gui.Tr.CherryPick,
|
||||
Prompt: gui.Tr.SureCherryPick,
|
||||
HandleConfirm: func() error {
|
||||
return gui.PopupHandler.WithWaitingStatus(gui.Tr.CherryPickingStatus, func() error {
|
||||
gui.logAction(gui.Tr.Actions.CherryPick)
|
||||
err := gui.Git.Rebase.CherryPickCommits(gui.State.Modes.CherryPicking.CherryPickedCommits)
|
||||
return gui.handleGenericMergeCommandResult(err)
|
||||
|
@ -4,6 +4,8 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/patch"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
func (gui *Gui) getSelectedCommitFileNode() *filetree.CommitFileNode {
|
||||
@ -65,10 +67,10 @@ func (gui *Gui) handleCheckoutCommitFile() error {
|
||||
|
||||
gui.logAction(gui.Tr.Actions.CheckoutFile)
|
||||
if err := gui.Git.WorkingTree.CheckoutFile(gui.State.CommitFileTreeViewModel.GetParent(), node.GetPath()); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC})
|
||||
}
|
||||
|
||||
func (gui *Gui) handleDiscardOldFileChange() error {
|
||||
@ -78,11 +80,11 @@ func (gui *Gui) handleDiscardOldFileChange() error {
|
||||
|
||||
fileName := gui.getSelectedCommitFileName()
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.DiscardFileChangesTitle,
|
||||
prompt: gui.Tr.DiscardFileChangesPrompt,
|
||||
handleConfirm: func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.RebasingStatus, func() error {
|
||||
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: gui.Tr.DiscardFileChangesTitle,
|
||||
Prompt: gui.Tr.DiscardFileChangesPrompt,
|
||||
HandleConfirm: func() error {
|
||||
return gui.PopupHandler.WithWaitingStatus(gui.Tr.RebasingStatus, func() error {
|
||||
gui.logAction(gui.Tr.Actions.DiscardOldFileChange)
|
||||
if err := gui.Git.Rebase.DiscardOldFileChanges(gui.State.Commits, gui.State.Panels.Commits.SelectedLineIdx, fileName); err != nil {
|
||||
if err := gui.handleGenericMergeCommandResult(err); err != nil {
|
||||
@ -90,7 +92,7 @@ func (gui *Gui) handleDiscardOldFileChange() error {
|
||||
}
|
||||
}
|
||||
|
||||
return gui.refreshSidePanels(refreshOptions{mode: BLOCK_UI})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Mode: types.BLOCK_UI})
|
||||
})
|
||||
},
|
||||
})
|
||||
@ -109,7 +111,7 @@ func (gui *Gui) refreshCommitFilesView() error {
|
||||
|
||||
files, err := gui.Git.Loaders.CommitFiles.GetFilesInDiff(from, to, reverse)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
gui.State.CommitFileTreeViewModel.SetParent(to)
|
||||
gui.State.CommitFileTreeViewModel.SetFiles(files)
|
||||
@ -133,7 +135,7 @@ func (gui *Gui) handleEditCommitFile() error {
|
||||
}
|
||||
|
||||
if node.File == nil {
|
||||
return gui.createErrorPanel(gui.Tr.ErrCannotEditDirectory)
|
||||
return gui.PopupHandler.ErrorMsg(gui.Tr.ErrCannotEditDirectory)
|
||||
}
|
||||
|
||||
return gui.editFile(node.GetPath())
|
||||
@ -167,7 +169,7 @@ func (gui *Gui) handleToggleFileForPatch() error {
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
if gui.Git.Patch.PatchManager.IsEmpty() {
|
||||
@ -178,10 +180,10 @@ func (gui *Gui) handleToggleFileForPatch() error {
|
||||
}
|
||||
|
||||
if gui.Git.Patch.PatchManager.Active() && gui.Git.Patch.PatchManager.To != gui.State.CommitFileTreeViewModel.GetParent() {
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.DiscardPatch,
|
||||
prompt: gui.Tr.DiscardPatchConfirm,
|
||||
handleConfirm: func() error {
|
||||
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: gui.Tr.DiscardPatch,
|
||||
Prompt: gui.Tr.DiscardPatchConfirm,
|
||||
HandleConfirm: func() error {
|
||||
gui.Git.Patch.PatchManager.Reset()
|
||||
return toggleTheFile()
|
||||
},
|
||||
@ -226,10 +228,10 @@ func (gui *Gui) enterCommitFile(opts OnFocusOpts) error {
|
||||
}
|
||||
|
||||
if gui.Git.Patch.PatchManager.Active() && gui.Git.Patch.PatchManager.To != gui.State.CommitFileTreeViewModel.GetParent() {
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.DiscardPatch,
|
||||
prompt: gui.Tr.DiscardPatchConfirm,
|
||||
handleConfirm: func() error {
|
||||
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: gui.Tr.DiscardPatch,
|
||||
Prompt: gui.Tr.DiscardPatchConfirm,
|
||||
HandleConfirm: func() error {
|
||||
gui.Git.Patch.PatchManager.Reset()
|
||||
return enterTheFile()
|
||||
},
|
||||
|
@ -12,7 +12,7 @@ func (gui *Gui) handleCommitConfirm() error {
|
||||
message := strings.TrimSpace(gui.Views.CommitMessage.TextArea.GetContent())
|
||||
gui.State.failedCommitMessage = message
|
||||
if message == "" {
|
||||
return gui.createErrorPanel(gui.Tr.CommitWithoutMessageErr)
|
||||
return gui.PopupHandler.ErrorMsg(gui.Tr.CommitWithoutMessageErr)
|
||||
}
|
||||
|
||||
cmdObj := gui.Git.Commit.CommitCmdObj(message)
|
||||
|
@ -6,6 +6,8 @@ import (
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/loaders"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
@ -29,7 +31,7 @@ func (gui *Gui) onCommitFocus() error {
|
||||
state.LimitCommits = false
|
||||
go utils.Safe(func() {
|
||||
if err := gui.refreshCommitsWithLimit(); err != nil {
|
||||
_ = gui.surfaceError(err)
|
||||
_ = gui.PopupHandler.Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -122,7 +124,7 @@ func (gui *Gui) refreshCommitsWithLimit() error {
|
||||
FilterPath: gui.State.Modes.Filtering.GetPath(),
|
||||
IncludeRebaseCommits: true,
|
||||
RefName: gui.refForLog(),
|
||||
All: gui.State.ShowWholeGitGraph,
|
||||
All: gui.ShowWholeGitGraph,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
@ -170,7 +172,7 @@ func (gui *Gui) handleCommitSquashDown() error {
|
||||
}
|
||||
|
||||
if len(gui.State.Commits) <= 1 {
|
||||
return gui.createErrorPanel(gui.Tr.YouNoCommitsToSquash)
|
||||
return gui.PopupHandler.ErrorMsg(gui.Tr.YouNoCommitsToSquash)
|
||||
}
|
||||
|
||||
applied, err := gui.handleMidRebaseCommand("squash")
|
||||
@ -181,11 +183,11 @@ func (gui *Gui) handleCommitSquashDown() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.Squash,
|
||||
prompt: gui.Tr.SureSquashThisCommit,
|
||||
handleConfirm: func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.SquashingStatus, func() error {
|
||||
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: gui.Tr.Squash,
|
||||
Prompt: gui.Tr.SureSquashThisCommit,
|
||||
HandleConfirm: func() error {
|
||||
return gui.PopupHandler.WithWaitingStatus(gui.Tr.SquashingStatus, func() error {
|
||||
gui.logAction(gui.Tr.Actions.SquashCommitDown)
|
||||
err := gui.Git.Rebase.InteractiveRebase(gui.State.Commits, gui.State.Panels.Commits.SelectedLineIdx, "squash")
|
||||
return gui.handleGenericMergeCommandResult(err)
|
||||
@ -200,7 +202,7 @@ func (gui *Gui) handleCommitFixup() error {
|
||||
}
|
||||
|
||||
if len(gui.State.Commits) <= 1 {
|
||||
return gui.createErrorPanel(gui.Tr.YouNoCommitsToSquash)
|
||||
return gui.PopupHandler.ErrorMsg(gui.Tr.YouNoCommitsToSquash)
|
||||
}
|
||||
|
||||
applied, err := gui.handleMidRebaseCommand("fixup")
|
||||
@ -211,11 +213,11 @@ func (gui *Gui) handleCommitFixup() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.Fixup,
|
||||
prompt: gui.Tr.SureFixupThisCommit,
|
||||
handleConfirm: func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.FixingStatus, func() error {
|
||||
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: gui.Tr.Fixup,
|
||||
Prompt: gui.Tr.SureFixupThisCommit,
|
||||
HandleConfirm: func() error {
|
||||
return gui.PopupHandler.WithWaitingStatus(gui.Tr.FixingStatus, func() error {
|
||||
gui.logAction(gui.Tr.Actions.FixupCommit)
|
||||
err := gui.Git.Rebase.InteractiveRebase(gui.State.Commits, gui.State.Panels.Commits.SelectedLineIdx, "fixup")
|
||||
return gui.handleGenericMergeCommandResult(err)
|
||||
@ -244,20 +246,20 @@ func (gui *Gui) handleRewordCommit() error {
|
||||
|
||||
message, err := gui.Git.Commit.GetCommitMessage(commit.Sha)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
// TODO: use the commit message panel here
|
||||
return gui.prompt(promptOpts{
|
||||
title: gui.Tr.LcRewordCommit,
|
||||
initialContent: message,
|
||||
handleConfirm: func(response string) error {
|
||||
return gui.PopupHandler.Prompt(popup.PromptOpts{
|
||||
Title: gui.Tr.LcRewordCommit,
|
||||
InitialContent: message,
|
||||
HandleConfirm: func(response string) error {
|
||||
gui.logAction(gui.Tr.Actions.RewordCommit)
|
||||
if err := gui.Git.Rebase.RewordCommit(gui.State.Commits, gui.State.Panels.Commits.SelectedLineIdx, response); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC})
|
||||
},
|
||||
})
|
||||
}
|
||||
@ -278,7 +280,7 @@ func (gui *Gui) handleRewordCommitEditor() error {
|
||||
gui.logAction(gui.Tr.Actions.RewordCommit)
|
||||
subProcess, err := gui.Git.Rebase.RewordCommitInEditor(gui.State.Commits, gui.State.Panels.Commits.SelectedLineIdx)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
if subProcess != nil {
|
||||
return gui.runSubprocessWithSuspenseAndRefresh(subProcess)
|
||||
@ -301,7 +303,7 @@ func (gui *Gui) handleMidRebaseCommand(action string) (bool, error) {
|
||||
// our input or we set a lazygit client as the EDITOR env variable and have it
|
||||
// request us to edit the commit message when prompted.
|
||||
if action == "reword" {
|
||||
return true, gui.createErrorPanel(gui.Tr.LcRewordNotSupported)
|
||||
return true, gui.PopupHandler.ErrorMsg(gui.Tr.LcRewordNotSupported)
|
||||
}
|
||||
|
||||
gui.logAction("Update rebase TODO")
|
||||
@ -311,7 +313,7 @@ func (gui *Gui) handleMidRebaseCommand(action string) (bool, error) {
|
||||
)
|
||||
|
||||
if err := gui.Git.Rebase.EditRebaseTodo(gui.State.Panels.Commits.SelectedLineIdx, action); err != nil {
|
||||
return false, gui.surfaceError(err)
|
||||
return false, gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
return true, gui.refreshRebaseCommits()
|
||||
@ -330,11 +332,11 @@ func (gui *Gui) handleCommitDelete() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.DeleteCommitTitle,
|
||||
prompt: gui.Tr.DeleteCommitPrompt,
|
||||
handleConfirm: func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.DeletingStatus, func() error {
|
||||
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: gui.Tr.DeleteCommitTitle,
|
||||
Prompt: gui.Tr.DeleteCommitPrompt,
|
||||
HandleConfirm: func() error {
|
||||
return gui.PopupHandler.WithWaitingStatus(gui.Tr.DeletingStatus, func() error {
|
||||
gui.logAction(gui.Tr.Actions.DropCommit)
|
||||
err := gui.Git.Rebase.InteractiveRebase(gui.State.Commits, gui.State.Panels.Commits.SelectedLineIdx, "drop")
|
||||
return gui.handleGenericMergeCommandResult(err)
|
||||
@ -361,13 +363,13 @@ func (gui *Gui) handleCommitMoveDown() error {
|
||||
gui.logCommand(fmt.Sprintf("Moving commit %s down", selectedCommit.ShortSha()), false)
|
||||
|
||||
if err := gui.Git.Rebase.MoveTodoDown(index); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
gui.State.Panels.Commits.SelectedLineIdx++
|
||||
return gui.refreshRebaseCommits()
|
||||
}
|
||||
|
||||
return gui.WithWaitingStatus(gui.Tr.MovingStatus, func() error {
|
||||
return gui.PopupHandler.WithWaitingStatus(gui.Tr.MovingStatus, func() error {
|
||||
gui.logAction(gui.Tr.Actions.MoveCommitDown)
|
||||
err := gui.Git.Rebase.MoveCommitDown(gui.State.Commits, index)
|
||||
if err == nil {
|
||||
@ -398,13 +400,13 @@ func (gui *Gui) handleCommitMoveUp() error {
|
||||
)
|
||||
|
||||
if err := gui.Git.Rebase.MoveTodoDown(index - 1); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
gui.State.Panels.Commits.SelectedLineIdx--
|
||||
return gui.refreshRebaseCommits()
|
||||
}
|
||||
|
||||
return gui.WithWaitingStatus(gui.Tr.MovingStatus, func() error {
|
||||
return gui.PopupHandler.WithWaitingStatus(gui.Tr.MovingStatus, func() error {
|
||||
gui.logAction(gui.Tr.Actions.MoveCommitUp)
|
||||
err := gui.Git.Rebase.MoveCommitDown(gui.State.Commits, index-1)
|
||||
if err == nil {
|
||||
@ -427,7 +429,7 @@ func (gui *Gui) handleCommitEdit() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
return gui.WithWaitingStatus(gui.Tr.RebasingStatus, func() error {
|
||||
return gui.PopupHandler.WithWaitingStatus(gui.Tr.RebasingStatus, func() error {
|
||||
gui.logAction(gui.Tr.Actions.EditCommit)
|
||||
err = gui.Git.Rebase.InteractiveRebase(gui.State.Commits, gui.State.Panels.Commits.SelectedLineIdx, "edit")
|
||||
return gui.handleGenericMergeCommandResult(err)
|
||||
@ -439,11 +441,11 @@ func (gui *Gui) handleCommitAmendTo() error {
|
||||
return err
|
||||
}
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.AmendCommitTitle,
|
||||
prompt: gui.Tr.AmendCommitPrompt,
|
||||
handleConfirm: func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.AmendingStatus, func() error {
|
||||
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: gui.Tr.AmendCommitTitle,
|
||||
Prompt: gui.Tr.AmendCommitPrompt,
|
||||
HandleConfirm: func() error {
|
||||
return gui.PopupHandler.WithWaitingStatus(gui.Tr.AmendingStatus, func() error {
|
||||
gui.logAction(gui.Tr.Actions.AmendCommit)
|
||||
err := gui.Git.Rebase.AmendTo(gui.State.Commits[gui.State.Panels.Commits.SelectedLineIdx].Sha)
|
||||
return gui.handleGenericMergeCommandResult(err)
|
||||
@ -478,17 +480,17 @@ func (gui *Gui) handleCommitRevert() error {
|
||||
if commit.IsMerge() {
|
||||
return gui.createRevertMergeCommitMenu(commit)
|
||||
} else {
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.Actions.RevertCommit,
|
||||
prompt: utils.ResolvePlaceholderString(
|
||||
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: gui.Tr.Actions.RevertCommit,
|
||||
Prompt: utils.ResolvePlaceholderString(
|
||||
gui.Tr.ConfirmRevertCommit,
|
||||
map[string]string{
|
||||
"selectedCommit": commit.ShortSha(),
|
||||
}),
|
||||
handleConfirm: func() error {
|
||||
HandleConfirm: func() error {
|
||||
gui.logAction(gui.Tr.Actions.RevertCommit)
|
||||
if err := gui.Git.Commit.Revert(commit.Sha); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
return gui.afterRevertCommit()
|
||||
},
|
||||
@ -497,33 +499,33 @@ func (gui *Gui) handleCommitRevert() error {
|
||||
}
|
||||
|
||||
func (gui *Gui) createRevertMergeCommitMenu(commit *models.Commit) error {
|
||||
menuItems := make([]*menuItem, len(commit.Parents))
|
||||
menuItems := make([]*popup.MenuItem, len(commit.Parents))
|
||||
for i, parentSha := range commit.Parents {
|
||||
i := i
|
||||
message, err := gui.Git.Commit.GetCommitMessageFirstLine(parentSha)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
menuItems[i] = &menuItem{
|
||||
displayString: fmt.Sprintf("%s: %s", utils.SafeTruncate(parentSha, 8), message),
|
||||
onPress: func() error {
|
||||
menuItems[i] = &popup.MenuItem{
|
||||
DisplayString: fmt.Sprintf("%s: %s", utils.SafeTruncate(parentSha, 8), message),
|
||||
OnPress: func() error {
|
||||
parentNumber := i + 1
|
||||
gui.logAction(gui.Tr.Actions.RevertCommit)
|
||||
if err := gui.Git.Commit.RevertMerge(commit.Sha, parentNumber); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
return gui.afterRevertCommit()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return gui.createMenu(gui.Tr.SelectParentCommitForMerge, menuItems, createMenuOptions{showCancel: true})
|
||||
return gui.PopupHandler.Menu(popup.CreateMenuOptions{Title: gui.Tr.SelectParentCommitForMerge, Items: menuItems})
|
||||
}
|
||||
|
||||
func (gui *Gui) afterRevertCommit() error {
|
||||
gui.State.Panels.Commits.SelectedLineIdx++
|
||||
return gui.refreshSidePanels(refreshOptions{mode: BLOCK_UI, scope: []RefreshableView{COMMITS, BRANCHES}})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Mode: types.BLOCK_UI, Scope: []types.RefreshableView{types.COMMITS, types.BRANCHES}})
|
||||
}
|
||||
|
||||
func (gui *Gui) handleViewCommitFiles() error {
|
||||
@ -552,16 +554,16 @@ func (gui *Gui) handleCreateFixupCommit() error {
|
||||
},
|
||||
)
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.CreateFixupCommit,
|
||||
prompt: prompt,
|
||||
handleConfirm: func() error {
|
||||
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: gui.Tr.CreateFixupCommit,
|
||||
Prompt: prompt,
|
||||
HandleConfirm: func() error {
|
||||
gui.logAction(gui.Tr.Actions.CreateFixupCommit)
|
||||
if err := gui.Git.Commit.CreateFixupCommit(commit.Sha); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC})
|
||||
},
|
||||
})
|
||||
}
|
||||
@ -583,11 +585,11 @@ func (gui *Gui) handleSquashAllAboveFixupCommits() error {
|
||||
},
|
||||
)
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.SquashAboveCommits,
|
||||
prompt: prompt,
|
||||
handleConfirm: func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.SquashingStatus, func() error {
|
||||
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: gui.Tr.SquashAboveCommits,
|
||||
Prompt: prompt,
|
||||
HandleConfirm: func() error {
|
||||
return gui.PopupHandler.WithWaitingStatus(gui.Tr.SquashingStatus, func() error {
|
||||
gui.logAction(gui.Tr.Actions.SquashAllAboveFixupCommits)
|
||||
err := gui.Git.Rebase.SquashAllAboveFixupCommits(commit.Sha)
|
||||
return gui.handleGenericMergeCommandResult(err)
|
||||
@ -606,39 +608,40 @@ func (gui *Gui) handleTagCommit() error {
|
||||
}
|
||||
|
||||
func (gui *Gui) createTagMenu(commitSha string) error {
|
||||
items := []*menuItem{
|
||||
{
|
||||
displayString: gui.Tr.LcLightweightTag,
|
||||
onPress: func() error {
|
||||
return gui.handleCreateLightweightTag(commitSha)
|
||||
return gui.PopupHandler.Menu(popup.CreateMenuOptions{
|
||||
Title: gui.Tr.TagMenuTitle,
|
||||
Items: []*popup.MenuItem{
|
||||
{
|
||||
DisplayString: gui.Tr.LcLightweightTag,
|
||||
OnPress: func() error {
|
||||
return gui.handleCreateLightweightTag(commitSha)
|
||||
},
|
||||
},
|
||||
{
|
||||
DisplayString: gui.Tr.LcAnnotatedTag,
|
||||
OnPress: func() error {
|
||||
return gui.handleCreateAnnotatedTag(commitSha)
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayString: gui.Tr.LcAnnotatedTag,
|
||||
onPress: func() error {
|
||||
return gui.handleCreateAnnotatedTag(commitSha)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return gui.createMenu(gui.Tr.TagMenuTitle, items, createMenuOptions{showCancel: true})
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) afterTagCreate() error {
|
||||
gui.State.Panels.Tags.SelectedLineIdx = 0 // Set to the top
|
||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []RefreshableView{COMMITS, TAGS}})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.COMMITS, types.TAGS}})
|
||||
}
|
||||
|
||||
func (gui *Gui) handleCreateAnnotatedTag(commitSha string) error {
|
||||
return gui.prompt(promptOpts{
|
||||
title: gui.Tr.TagNameTitle,
|
||||
handleConfirm: func(tagName string) error {
|
||||
return gui.prompt(promptOpts{
|
||||
title: gui.Tr.TagMessageTitle,
|
||||
handleConfirm: func(msg string) error {
|
||||
return gui.PopupHandler.Prompt(popup.PromptOpts{
|
||||
Title: gui.Tr.TagNameTitle,
|
||||
HandleConfirm: func(tagName string) error {
|
||||
return gui.PopupHandler.Prompt(popup.PromptOpts{
|
||||
Title: gui.Tr.TagMessageTitle,
|
||||
HandleConfirm: func(msg string) error {
|
||||
gui.logAction(gui.Tr.Actions.CreateAnnotatedTag)
|
||||
if err := gui.Git.Tag.CreateAnnotated(tagName, commitSha, msg); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
return gui.afterTagCreate()
|
||||
},
|
||||
@ -648,12 +651,12 @@ func (gui *Gui) handleCreateAnnotatedTag(commitSha string) error {
|
||||
}
|
||||
|
||||
func (gui *Gui) handleCreateLightweightTag(commitSha string) error {
|
||||
return gui.prompt(promptOpts{
|
||||
title: gui.Tr.TagNameTitle,
|
||||
handleConfirm: func(tagName string) error {
|
||||
return gui.PopupHandler.Prompt(popup.PromptOpts{
|
||||
Title: gui.Tr.TagNameTitle,
|
||||
HandleConfirm: func(tagName string) error {
|
||||
gui.logAction(gui.Tr.Actions.CreateLightweightTag)
|
||||
if err := gui.Git.Tag.CreateLightweight(tagName, commitSha); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
return gui.afterTagCreate()
|
||||
},
|
||||
@ -666,10 +669,10 @@ func (gui *Gui) handleCheckoutCommit() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.LcCheckoutCommit,
|
||||
prompt: gui.Tr.SureCheckoutThisCommit,
|
||||
handleConfirm: func() error {
|
||||
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: gui.Tr.LcCheckoutCommit,
|
||||
Prompt: gui.Tr.SureCheckoutThisCommit,
|
||||
HandleConfirm: func() error {
|
||||
gui.logAction(gui.Tr.Actions.CheckoutCommit)
|
||||
return gui.handleCheckoutRef(commit.Sha, handleCheckoutRefOptions{})
|
||||
},
|
||||
@ -679,7 +682,7 @@ func (gui *Gui) handleCheckoutCommit() error {
|
||||
func (gui *Gui) handleCreateCommitResetMenu() error {
|
||||
commit := gui.getSelectedLocalCommit()
|
||||
if commit == nil {
|
||||
return gui.createErrorPanel(gui.Tr.NoCommitsThisBranch)
|
||||
return gui.PopupHandler.ErrorMsg(gui.Tr.NoCommitsThisBranch)
|
||||
}
|
||||
|
||||
return gui.createResetMenu(commit.Sha)
|
||||
@ -689,7 +692,7 @@ func (gui *Gui) handleOpenSearchForCommitsPanel(string) error {
|
||||
// we usually lazyload these commits but now that we're searching we need to load them now
|
||||
if gui.State.Panels.Commits.LimitCommits {
|
||||
gui.State.Panels.Commits.LimitCommits = false
|
||||
if err := gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []RefreshableView{COMMITS}}); err != nil {
|
||||
if err := gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.COMMITS}}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -701,7 +704,7 @@ func (gui *Gui) handleGotoBottomForCommitsPanel() error {
|
||||
// we usually lazyload these commits but now that we're searching we need to load them now
|
||||
if gui.State.Panels.Commits.LimitCommits {
|
||||
gui.State.Panels.Commits.LimitCommits = false
|
||||
if err := gui.refreshSidePanels(refreshOptions{mode: SYNC, scope: []RefreshableView{COMMITS}}); err != nil {
|
||||
if err := gui.refreshSidePanels(types.RefreshOptions{Mode: types.SYNC, Scope: []types.RefreshableView{types.COMMITS}}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -723,12 +726,12 @@ func (gui *Gui) handleCopySelectedCommitMessageToClipboard() error {
|
||||
|
||||
message, err := gui.Git.Commit.GetCommitMessage(commit.Sha)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
gui.logAction(gui.Tr.Actions.CopyCommitMessageToClipboard)
|
||||
if err := gui.OSCommand.CopyToClipboard(message); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
gui.raiseToast(gui.Tr.CommitMessageCopiedToClipboard)
|
||||
@ -737,87 +740,87 @@ func (gui *Gui) handleCopySelectedCommitMessageToClipboard() error {
|
||||
}
|
||||
|
||||
func (gui *Gui) handleOpenLogMenu() error {
|
||||
return gui.createMenu(gui.Tr.LogMenuTitle, []*menuItem{
|
||||
{
|
||||
displayString: gui.Tr.ToggleShowGitGraphAll,
|
||||
onPress: func() error {
|
||||
gui.State.ShowWholeGitGraph = !gui.State.ShowWholeGitGraph
|
||||
return gui.PopupHandler.Menu(popup.CreateMenuOptions{
|
||||
Title: gui.Tr.LogMenuTitle,
|
||||
Items: []*popup.MenuItem{
|
||||
{
|
||||
DisplayString: gui.Tr.ToggleShowGitGraphAll,
|
||||
OnPress: func() error {
|
||||
gui.ShowWholeGitGraph = !gui.ShowWholeGitGraph
|
||||
|
||||
if gui.State.ShowWholeGitGraph {
|
||||
gui.State.Panels.Commits.LimitCommits = false
|
||||
}
|
||||
if gui.ShowWholeGitGraph {
|
||||
gui.State.Panels.Commits.LimitCommits = false
|
||||
}
|
||||
|
||||
return gui.WithWaitingStatus(gui.Tr.LcLoadingCommits, func() error {
|
||||
return gui.refreshSidePanels(refreshOptions{mode: SYNC, scope: []RefreshableView{COMMITS}})
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
displayString: gui.Tr.ShowGitGraph,
|
||||
opensMenu: true,
|
||||
onPress: func() error {
|
||||
onSelect := func(value string) {
|
||||
gui.UserConfig.Git.Log.ShowGraph = value
|
||||
gui.render()
|
||||
}
|
||||
return gui.createMenu(gui.Tr.LogMenuTitle, []*menuItem{
|
||||
{
|
||||
displayString: "always",
|
||||
onPress: func() error {
|
||||
onSelect("always")
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
displayString: "never",
|
||||
onPress: func() error {
|
||||
onSelect("never")
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
displayString: "when maximised",
|
||||
onPress: func() error {
|
||||
onSelect("when-maximised")
|
||||
return nil
|
||||
},
|
||||
},
|
||||
}, createMenuOptions{showCancel: true})
|
||||
},
|
||||
},
|
||||
{
|
||||
displayString: gui.Tr.SortCommits,
|
||||
opensMenu: true,
|
||||
onPress: func() error {
|
||||
onSelect := func(value string) error {
|
||||
gui.UserConfig.Git.Log.Order = value
|
||||
return gui.WithWaitingStatus(gui.Tr.LcLoadingCommits, func() error {
|
||||
return gui.refreshSidePanels(refreshOptions{mode: SYNC, scope: []RefreshableView{COMMITS}})
|
||||
return gui.PopupHandler.WithWaitingStatus(gui.Tr.LcLoadingCommits, func() error {
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Mode: types.SYNC, Scope: []types.RefreshableView{types.COMMITS}})
|
||||
})
|
||||
}
|
||||
return gui.createMenu(gui.Tr.LogMenuTitle, []*menuItem{
|
||||
{
|
||||
displayString: "topological (topo-order)",
|
||||
onPress: func() error {
|
||||
return onSelect("topo-order")
|
||||
},
|
||||
},
|
||||
{
|
||||
DisplayString: gui.Tr.ShowGitGraph,
|
||||
OpensMenu: true,
|
||||
OnPress: func() error {
|
||||
onPress := func(value string) func() error {
|
||||
return func() error {
|
||||
gui.UserConfig.Git.Log.ShowGraph = value
|
||||
gui.render()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return gui.PopupHandler.Menu(popup.CreateMenuOptions{
|
||||
Title: gui.Tr.LogMenuTitle,
|
||||
Items: []*popup.MenuItem{
|
||||
{
|
||||
DisplayString: "always",
|
||||
OnPress: onPress("always"),
|
||||
},
|
||||
{
|
||||
DisplayString: "never",
|
||||
OnPress: onPress("never"),
|
||||
},
|
||||
{
|
||||
DisplayString: "when maximised",
|
||||
OnPress: onPress("when-maximised"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayString: "date-order",
|
||||
onPress: func() error {
|
||||
return onSelect("date-order")
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
DisplayString: gui.Tr.SortCommits,
|
||||
OpensMenu: true,
|
||||
OnPress: func() error {
|
||||
onPress := func(value string) func() error {
|
||||
return func() error {
|
||||
gui.UserConfig.Git.Log.Order = value
|
||||
return gui.PopupHandler.WithWaitingStatus(gui.Tr.LcLoadingCommits, func() error {
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Mode: types.SYNC, Scope: []types.RefreshableView{types.COMMITS}})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return gui.PopupHandler.Menu(popup.CreateMenuOptions{
|
||||
Title: gui.Tr.LogMenuTitle,
|
||||
Items: []*popup.MenuItem{
|
||||
{
|
||||
DisplayString: "topological (topo-order)",
|
||||
OnPress: onPress("topo-order"),
|
||||
},
|
||||
{
|
||||
DisplayString: "date-order",
|
||||
OnPress: onPress("date-order"),
|
||||
},
|
||||
{
|
||||
DisplayString: "author-date-order",
|
||||
OnPress: onPress("author-date-order"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayString: "author-date-order",
|
||||
onPress: func() error {
|
||||
return onSelect("author-date-order")
|
||||
},
|
||||
},
|
||||
}, createMenuOptions{showCancel: true})
|
||||
})
|
||||
},
|
||||
},
|
||||
},
|
||||
}, createMenuOptions{showCancel: true})
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) handleOpenCommitInBrowser() error {
|
||||
@ -830,12 +833,12 @@ func (gui *Gui) handleOpenCommitInBrowser() error {
|
||||
|
||||
url, err := hostingServiceMgr.GetCommitURL(commit.Sha)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
gui.logAction(gui.Tr.Actions.OpenCommitInBrowser)
|
||||
if err := gui.OSCommand.OpenLink(url); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -5,53 +5,12 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/theme"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
type createPopupPanelOpts struct {
|
||||
hasLoader bool
|
||||
editable bool
|
||||
title string
|
||||
prompt string
|
||||
handleConfirm func() error
|
||||
handleConfirmPrompt func(string) error
|
||||
handleClose func() error
|
||||
|
||||
// when handlersManageFocus is true, do not return from the confirmation context automatically. It's expected that the handlers will manage focus, whether that means switching to another context, or manually returning the context.
|
||||
handlersManageFocus bool
|
||||
|
||||
findSuggestionsFunc func(string) []*types.Suggestion
|
||||
}
|
||||
|
||||
type askOpts struct {
|
||||
title string
|
||||
prompt string
|
||||
handleConfirm func() error
|
||||
handleClose func() error
|
||||
handlersManageFocus bool
|
||||
}
|
||||
|
||||
type promptOpts struct {
|
||||
title string
|
||||
initialContent string
|
||||
findSuggestionsFunc func(string) []*types.Suggestion
|
||||
handleConfirm func(string) error
|
||||
}
|
||||
|
||||
func (gui *Gui) ask(opts askOpts) error {
|
||||
return gui.PopupHandler.Ask(opts)
|
||||
}
|
||||
|
||||
func (gui *Gui) prompt(opts promptOpts) error {
|
||||
return gui.PopupHandler.Prompt(opts)
|
||||
}
|
||||
|
||||
func (gui *Gui) createLoaderPanel(prompt string) error {
|
||||
return gui.PopupHandler.Loader(prompt)
|
||||
}
|
||||
|
||||
func (gui *Gui) wrappedConfirmationFunction(handlersManageFocus bool, function func() error) func() error {
|
||||
return func() error {
|
||||
if err := gui.closeConfirmationPrompt(handlersManageFocus); err != nil {
|
||||
@ -60,7 +19,7 @@ func (gui *Gui) wrappedConfirmationFunction(handlersManageFocus bool, function f
|
||||
|
||||
if function != nil {
|
||||
if err := function(); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -76,7 +35,7 @@ func (gui *Gui) wrappedPromptConfirmationFunction(handlersManageFocus bool, func
|
||||
|
||||
if function != nil {
|
||||
if err := function(getResponse()); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -179,31 +138,31 @@ func (gui *Gui) prepareConfirmationPanel(
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) createPopupPanel(opts createPopupPanelOpts) error {
|
||||
func (gui *Gui) createPopupPanel(opts popup.CreatePopupPanelOpts) error {
|
||||
// remove any previous keybindings
|
||||
gui.clearConfirmationViewKeyBindings()
|
||||
|
||||
err := gui.prepareConfirmationPanel(
|
||||
opts.title,
|
||||
opts.prompt,
|
||||
opts.hasLoader,
|
||||
opts.findSuggestionsFunc,
|
||||
opts.editable,
|
||||
opts.Title,
|
||||
opts.Prompt,
|
||||
opts.HasLoader,
|
||||
opts.FindSuggestionsFunc,
|
||||
opts.Editable,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
confirmationView := gui.Views.Confirmation
|
||||
confirmationView.Editable = opts.editable
|
||||
confirmationView.Editable = opts.Editable
|
||||
confirmationView.Editor = gocui.EditorFunc(gui.defaultEditor)
|
||||
|
||||
if opts.editable {
|
||||
if opts.Editable {
|
||||
textArea := confirmationView.TextArea
|
||||
textArea.Clear()
|
||||
textArea.TypeString(opts.prompt)
|
||||
textArea.TypeString(opts.Prompt)
|
||||
confirmationView.RenderTextArea()
|
||||
} else {
|
||||
if err := gui.renderString(confirmationView, opts.prompt); err != nil {
|
||||
if err := gui.renderString(confirmationView, opts.Prompt); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -215,7 +174,7 @@ func (gui *Gui) createPopupPanel(opts createPopupPanelOpts) error {
|
||||
return gui.pushContext(gui.State.Contexts.Confirmation)
|
||||
}
|
||||
|
||||
func (gui *Gui) setKeyBindings(opts createPopupPanelOpts) error {
|
||||
func (gui *Gui) setKeyBindings(opts popup.CreatePopupPanelOpts) error {
|
||||
actions := utils.ResolvePlaceholderString(
|
||||
gui.Tr.CloseConfirm,
|
||||
map[string]string{
|
||||
@ -226,10 +185,10 @@ func (gui *Gui) setKeyBindings(opts createPopupPanelOpts) error {
|
||||
|
||||
_ = gui.renderString(gui.Views.Options, actions)
|
||||
var onConfirm func() error
|
||||
if opts.handleConfirmPrompt != nil {
|
||||
onConfirm = gui.wrappedPromptConfirmationFunction(opts.handlersManageFocus, opts.handleConfirmPrompt, func() string { return gui.Views.Confirmation.TextArea.GetContent() })
|
||||
if opts.HandleConfirmPrompt != nil {
|
||||
onConfirm = gui.wrappedPromptConfirmationFunction(opts.HandlersManageFocus, opts.HandleConfirmPrompt, func() string { return gui.Views.Confirmation.TextArea.GetContent() })
|
||||
} else {
|
||||
onConfirm = gui.wrappedConfirmationFunction(opts.handlersManageFocus, opts.handleConfirm)
|
||||
onConfirm = gui.wrappedConfirmationFunction(opts.HandlersManageFocus, opts.HandleConfirm)
|
||||
}
|
||||
|
||||
type confirmationKeybinding struct {
|
||||
@ -240,8 +199,8 @@ func (gui *Gui) setKeyBindings(opts createPopupPanelOpts) error {
|
||||
|
||||
keybindingConfig := gui.UserConfig.Keybinding
|
||||
onSuggestionConfirm := gui.wrappedPromptConfirmationFunction(
|
||||
opts.handlersManageFocus,
|
||||
opts.handleConfirmPrompt,
|
||||
opts.HandlersManageFocus,
|
||||
opts.HandleConfirmPrompt,
|
||||
gui.getSelectedSuggestionValue,
|
||||
)
|
||||
|
||||
@ -259,7 +218,7 @@ func (gui *Gui) setKeyBindings(opts createPopupPanelOpts) error {
|
||||
{
|
||||
viewName: "confirmation",
|
||||
key: gui.getKey(keybindingConfig.Universal.Return),
|
||||
handler: gui.wrappedConfirmationFunction(opts.handlersManageFocus, opts.handleClose),
|
||||
handler: gui.wrappedConfirmationFunction(opts.HandlersManageFocus, opts.HandleClose),
|
||||
},
|
||||
{
|
||||
viewName: "confirmation",
|
||||
@ -284,7 +243,7 @@ func (gui *Gui) setKeyBindings(opts createPopupPanelOpts) error {
|
||||
{
|
||||
viewName: "suggestions",
|
||||
key: gui.getKey(keybindingConfig.Universal.Return),
|
||||
handler: gui.wrappedConfirmationFunction(opts.handlersManageFocus, opts.handleClose),
|
||||
handler: gui.wrappedConfirmationFunction(opts.HandlersManageFocus, opts.HandleClose),
|
||||
},
|
||||
{
|
||||
viewName: "suggestions",
|
||||
@ -317,19 +276,3 @@ func (gui *Gui) wrappedHandler(f func() error) func(g *gocui.Gui, v *gocui.View)
|
||||
return f()
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) createErrorPanel(message string) error {
|
||||
return gui.PopupHandler.Error(message)
|
||||
}
|
||||
|
||||
func (gui *Gui) surfaceError(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err == gocui.ErrQuit {
|
||||
return err
|
||||
}
|
||||
|
||||
return gui.createErrorPanel(err.Error())
|
||||
}
|
||||
|
@ -174,12 +174,13 @@ func (gui *Gui) contextTree() ContextTree {
|
||||
OnGetOptionsMap: gui.getMergingOptions,
|
||||
},
|
||||
Credentials: &BasicContext{
|
||||
OnFocus: OnFocusWrapper(gui.handleCredentialsViewFocused),
|
||||
OnFocus: OnFocusWrapper(gui.handleAskFocused),
|
||||
Kind: PERSISTENT_POPUP,
|
||||
ViewName: "credentials",
|
||||
Key: CREDENTIALS_CONTEXT_KEY,
|
||||
},
|
||||
Confirmation: &BasicContext{
|
||||
OnFocus: OnFocusWrapper(gui.handleAskFocused),
|
||||
Kind: TEMPORARY_POPUP,
|
||||
ViewName: "confirmation",
|
||||
Key: CONFIRMATION_CONTEXT_KEY,
|
||||
|
243
pkg/gui/controllers/submodules_controller.go
Normal file
243
pkg/gui/controllers/submodules_controller.go
Normal file
@ -0,0 +1,243 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/common"
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
// if Go let me do private struct embedding of structs with public fields (which it should)
|
||||
// I would just do that. But alas.
|
||||
type ControllerCommon struct {
|
||||
*common.Common
|
||||
IGuiCommon
|
||||
}
|
||||
|
||||
type SubmodulesController struct {
|
||||
// I've said publicly that I'm against single-letter variable names but in this
|
||||
// case I would actually prefer a _zero_ letter variable name in the form of
|
||||
// struct embedding, but Go does not allow hiding public fields in an embedded struct
|
||||
// to the client
|
||||
c *ControllerCommon
|
||||
enterSubmoduleFn func(submodule *models.SubmoduleConfig) error
|
||||
getSelectedSubmodule func() *models.SubmoduleConfig
|
||||
git *commands.GitCommand
|
||||
submodules []*models.SubmoduleConfig
|
||||
}
|
||||
|
||||
func NewSubmodulesController(
|
||||
c *ControllerCommon,
|
||||
enterSubmoduleFn func(submodule *models.SubmoduleConfig) error,
|
||||
git *commands.GitCommand,
|
||||
submodules []*models.SubmoduleConfig,
|
||||
getSelectedSubmodule func() *models.SubmoduleConfig,
|
||||
) *SubmodulesController {
|
||||
return &SubmodulesController{
|
||||
c: c,
|
||||
enterSubmoduleFn: enterSubmoduleFn,
|
||||
git: git,
|
||||
submodules: submodules,
|
||||
getSelectedSubmodule: getSelectedSubmodule,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SubmodulesController) Keybindings(getKey func(key string) interface{}, config config.KeybindingConfig) []*types.Binding {
|
||||
return []*types.Binding{
|
||||
{
|
||||
Key: getKey(config.Universal.GoInto),
|
||||
Handler: self.forSubmodule(self.enter),
|
||||
Description: self.c.Tr.LcEnterSubmodule,
|
||||
},
|
||||
{
|
||||
Key: getKey(config.Universal.Remove),
|
||||
Handler: self.forSubmodule(self.remove),
|
||||
Description: self.c.Tr.LcRemoveSubmodule,
|
||||
},
|
||||
{
|
||||
Key: getKey(config.Submodules.Update),
|
||||
Handler: self.forSubmodule(self.update),
|
||||
Description: self.c.Tr.LcSubmoduleUpdate,
|
||||
},
|
||||
{
|
||||
Key: getKey(config.Universal.New),
|
||||
Handler: self.add,
|
||||
Description: self.c.Tr.LcAddSubmodule,
|
||||
},
|
||||
{
|
||||
Key: getKey(config.Universal.Edit),
|
||||
Handler: self.forSubmodule(self.editURL),
|
||||
Description: self.c.Tr.LcEditSubmoduleUrl,
|
||||
},
|
||||
{
|
||||
Key: getKey(config.Submodules.Init),
|
||||
Handler: self.forSubmodule(self.init),
|
||||
Description: self.c.Tr.LcInitSubmodule,
|
||||
},
|
||||
{
|
||||
Key: getKey(config.Submodules.BulkMenu),
|
||||
Handler: self.openBulkActionsMenu,
|
||||
Description: self.c.Tr.LcViewBulkSubmoduleOptions,
|
||||
OpensMenu: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SubmodulesController) enter(submodule *models.SubmoduleConfig) error {
|
||||
return self.enterSubmoduleFn(submodule)
|
||||
}
|
||||
|
||||
func (self *SubmodulesController) add() error {
|
||||
return self.c.Prompt(popup.PromptOpts{
|
||||
Title: self.c.Tr.LcNewSubmoduleUrl,
|
||||
HandleConfirm: func(submoduleUrl string) error {
|
||||
nameSuggestion := filepath.Base(strings.TrimSuffix(submoduleUrl, filepath.Ext(submoduleUrl)))
|
||||
|
||||
return self.c.Prompt(popup.PromptOpts{
|
||||
Title: self.c.Tr.LcNewSubmoduleName,
|
||||
InitialContent: nameSuggestion,
|
||||
HandleConfirm: func(submoduleName string) error {
|
||||
|
||||
return self.c.Prompt(popup.PromptOpts{
|
||||
Title: self.c.Tr.LcNewSubmodulePath,
|
||||
InitialContent: submoduleName,
|
||||
HandleConfirm: func(submodulePath string) error {
|
||||
return self.c.WithWaitingStatus(self.c.Tr.LcAddingSubmoduleStatus, func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.AddSubmodule)
|
||||
err := self.git.Submodule.Add(submoduleName, submodulePath, submoduleUrl)
|
||||
if err != nil {
|
||||
_ = self.c.Error(err)
|
||||
}
|
||||
|
||||
return self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.SUBMODULES}})
|
||||
})
|
||||
},
|
||||
})
|
||||
},
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (self *SubmodulesController) editURL(submodule *models.SubmoduleConfig) error {
|
||||
return self.c.Prompt(popup.PromptOpts{
|
||||
Title: fmt.Sprintf(self.c.Tr.LcUpdateSubmoduleUrl, submodule.Name),
|
||||
InitialContent: submodule.Url,
|
||||
HandleConfirm: func(newUrl string) error {
|
||||
return self.c.WithWaitingStatus(self.c.Tr.LcUpdatingSubmoduleUrlStatus, func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.UpdateSubmoduleUrl)
|
||||
err := self.git.Submodule.UpdateUrl(submodule.Name, submodule.Path, newUrl)
|
||||
if err != nil {
|
||||
_ = self.c.Error(err)
|
||||
}
|
||||
|
||||
return self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.SUBMODULES}})
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (self *SubmodulesController) init(submodule *models.SubmoduleConfig) error {
|
||||
return self.c.WithWaitingStatus(self.c.Tr.LcInitializingSubmoduleStatus, func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.InitialiseSubmodule)
|
||||
err := self.git.Submodule.Init(submodule.Path)
|
||||
if err != nil {
|
||||
_ = self.c.Error(err)
|
||||
}
|
||||
|
||||
return self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.SUBMODULES}})
|
||||
})
|
||||
}
|
||||
|
||||
func (self *SubmodulesController) openBulkActionsMenu() error {
|
||||
return self.c.Menu(popup.CreateMenuOptions{
|
||||
Title: self.c.Tr.LcBulkSubmoduleOptions,
|
||||
Items: []*popup.MenuItem{
|
||||
{
|
||||
DisplayStrings: []string{self.c.Tr.LcBulkInitSubmodules, style.FgGreen.Sprint(self.git.Submodule.BulkInitCmdObj().ToString())},
|
||||
OnPress: func() error {
|
||||
return self.c.WithWaitingStatus(self.c.Tr.LcRunningCommand, func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.BulkInitialiseSubmodules)
|
||||
err := self.git.Submodule.BulkInitCmdObj().Run()
|
||||
if err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
return self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.SUBMODULES}})
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
DisplayStrings: []string{self.c.Tr.LcBulkUpdateSubmodules, style.FgYellow.Sprint(self.git.Submodule.BulkUpdateCmdObj().ToString())},
|
||||
OnPress: func() error {
|
||||
return self.c.WithWaitingStatus(self.c.Tr.LcRunningCommand, func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.BulkUpdateSubmodules)
|
||||
if err := self.git.Submodule.BulkUpdateCmdObj().Run(); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
return self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.SUBMODULES}})
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
DisplayStrings: []string{self.c.Tr.LcBulkDeinitSubmodules, style.FgRed.Sprint(self.git.Submodule.BulkDeinitCmdObj().ToString())},
|
||||
OnPress: func() error {
|
||||
return self.c.WithWaitingStatus(self.c.Tr.LcRunningCommand, func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.BulkDeinitialiseSubmodules)
|
||||
if err := self.git.Submodule.BulkDeinitCmdObj().Run(); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
return self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.SUBMODULES}})
|
||||
})
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (self *SubmodulesController) update(submodule *models.SubmoduleConfig) error {
|
||||
return self.c.WithWaitingStatus(self.c.Tr.LcUpdatingSubmoduleStatus, func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.UpdateSubmodule)
|
||||
err := self.git.Submodule.Update(submodule.Path)
|
||||
if err != nil {
|
||||
_ = self.c.Error(err)
|
||||
}
|
||||
|
||||
return self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.SUBMODULES}})
|
||||
})
|
||||
}
|
||||
|
||||
func (self *SubmodulesController) remove(submodule *models.SubmoduleConfig) error {
|
||||
return self.c.Ask(popup.AskOpts{
|
||||
Title: self.c.Tr.RemoveSubmodule,
|
||||
Prompt: fmt.Sprintf(self.c.Tr.RemoveSubmodulePrompt, submodule.Name),
|
||||
HandleConfirm: func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.RemoveSubmodule)
|
||||
if err := self.git.Submodule.Delete(submodule); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
return self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.SUBMODULES, types.FILES}})
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (self *SubmodulesController) forSubmodule(callback func(*models.SubmoduleConfig) error) func() error {
|
||||
return func() error {
|
||||
submodule := self.getSelectedSubmodule()
|
||||
if submodule == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return callback(submodule)
|
||||
}
|
||||
}
|
13
pkg/gui/controllers/types.go
Normal file
13
pkg/gui/controllers/types.go
Normal file
@ -0,0 +1,13 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
type IGuiCommon interface {
|
||||
popup.IPopupHandler
|
||||
|
||||
LogAction(string)
|
||||
Refresh(types.RefreshOptions) error
|
||||
}
|
@ -4,6 +4,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
@ -48,15 +49,16 @@ func (gui *Gui) handleSubmitCredential() error {
|
||||
return err
|
||||
}
|
||||
|
||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC})
|
||||
}
|
||||
|
||||
func (gui *Gui) handleCloseCredentialsView() error {
|
||||
gui.Views.Credentials.ClearTextArea()
|
||||
gui.credentials <- ""
|
||||
return gui.returnFromContext()
|
||||
}
|
||||
|
||||
func (gui *Gui) handleCredentialsViewFocused() error {
|
||||
func (gui *Gui) handleAskFocused() error {
|
||||
keybindingConfig := gui.UserConfig.Keybinding
|
||||
|
||||
message := utils.ResolvePlaceholderString(
|
||||
@ -69,18 +71,3 @@ func (gui *Gui) handleCredentialsViewFocused() error {
|
||||
|
||||
return gui.renderString(gui.Views.Options, message)
|
||||
}
|
||||
|
||||
// handleCredentialsPopup handles the views after executing a command that might ask for credentials
|
||||
func (gui *Gui) handleCredentialsPopup(cmdErr error) {
|
||||
if cmdErr != nil {
|
||||
errMessage := cmdErr.Error()
|
||||
if strings.Contains(errMessage, "Invalid username, password or passphrase") {
|
||||
errMessage = gui.Tr.PassUnameWrong
|
||||
}
|
||||
_ = gui.returnFromContext()
|
||||
// we are not logging this error because it may contain a password or a passphrase
|
||||
_ = gui.createErrorPanel(errMessage)
|
||||
} else {
|
||||
_ = gui.closeConfirmationPrompt(false)
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,9 @@ import (
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
@ -62,18 +64,18 @@ func (gui *Gui) resolveTemplate(templateStr string, promptResponses []string) (s
|
||||
func (gui *Gui) inputPrompt(prompt config.CustomCommandPrompt, promptResponses []string, responseIdx int, wrappedF func() error) error {
|
||||
title, err := gui.resolveTemplate(prompt.Title, promptResponses)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
initialValue, err := gui.resolveTemplate(prompt.InitialValue, promptResponses)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
return gui.prompt(promptOpts{
|
||||
title: title,
|
||||
initialContent: initialValue,
|
||||
handleConfirm: func(str string) error {
|
||||
return gui.PopupHandler.Prompt(popup.PromptOpts{
|
||||
Title: title,
|
||||
InitialContent: initialValue,
|
||||
HandleConfirm: func(str string) error {
|
||||
promptResponses[responseIdx] = str
|
||||
return wrappedF()
|
||||
},
|
||||
@ -82,7 +84,7 @@ func (gui *Gui) inputPrompt(prompt config.CustomCommandPrompt, promptResponses [
|
||||
|
||||
func (gui *Gui) menuPrompt(prompt config.CustomCommandPrompt, promptResponses []string, responseIdx int, wrappedF func() error) error {
|
||||
// need to make a menu here some how
|
||||
menuItems := make([]*menuItem, len(prompt.Options))
|
||||
menuItems := make([]*popup.MenuItem, len(prompt.Options))
|
||||
for i, option := range prompt.Options {
|
||||
option := option
|
||||
|
||||
@ -93,22 +95,22 @@ func (gui *Gui) menuPrompt(prompt config.CustomCommandPrompt, promptResponses []
|
||||
}
|
||||
name, err := gui.resolveTemplate(nameTemplate, promptResponses)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
description, err := gui.resolveTemplate(option.Description, promptResponses)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
value, err := gui.resolveTemplate(option.Value, promptResponses)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
menuItems[i] = &menuItem{
|
||||
displayStrings: []string{name, style.FgYellow.Sprint(description)},
|
||||
onPress: func() error {
|
||||
menuItems[i] = &popup.MenuItem{
|
||||
DisplayStrings: []string{name, style.FgYellow.Sprint(description)},
|
||||
OnPress: func() error {
|
||||
promptResponses[responseIdx] = value
|
||||
return wrappedF()
|
||||
},
|
||||
@ -117,30 +119,30 @@ func (gui *Gui) menuPrompt(prompt config.CustomCommandPrompt, promptResponses []
|
||||
|
||||
title, err := gui.resolveTemplate(prompt.Title, promptResponses)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
return gui.createMenu(title, menuItems, createMenuOptions{showCancel: true})
|
||||
return gui.PopupHandler.Menu(popup.CreateMenuOptions{Title: title, Items: menuItems})
|
||||
}
|
||||
|
||||
func (gui *Gui) GenerateMenuCandidates(commandOutput, filter, valueFormat, labelFormat string) ([]commandMenuEntry, error) {
|
||||
reg, err := regexp.Compile(filter)
|
||||
if err != nil {
|
||||
return nil, gui.surfaceError(errors.New("unable to parse filter regex, error: " + err.Error()))
|
||||
return nil, gui.PopupHandler.Error(errors.New("unable to parse filter regex, error: " + err.Error()))
|
||||
}
|
||||
|
||||
buff := bytes.NewBuffer(nil)
|
||||
|
||||
valueTemp, err := template.New("format").Parse(valueFormat)
|
||||
if err != nil {
|
||||
return nil, gui.surfaceError(errors.New("unable to parse value format, error: " + err.Error()))
|
||||
return nil, gui.PopupHandler.Error(errors.New("unable to parse value format, error: " + err.Error()))
|
||||
}
|
||||
|
||||
colorFuncMap := style.TemplateFuncMapAddColors(template.FuncMap{})
|
||||
|
||||
descTemp, err := template.New("format").Funcs(colorFuncMap).Parse(labelFormat)
|
||||
if err != nil {
|
||||
return nil, gui.surfaceError(errors.New("unable to parse label format, error: " + err.Error()))
|
||||
return nil, gui.PopupHandler.Error(errors.New("unable to parse label format, error: " + err.Error()))
|
||||
}
|
||||
|
||||
candidates := []commandMenuEntry{}
|
||||
@ -165,7 +167,7 @@ func (gui *Gui) GenerateMenuCandidates(commandOutput, filter, valueFormat, label
|
||||
|
||||
err = valueTemp.Execute(buff, tmplData)
|
||||
if err != nil {
|
||||
return candidates, gui.surfaceError(err)
|
||||
return candidates, gui.PopupHandler.Error(err)
|
||||
}
|
||||
entry := commandMenuEntry{
|
||||
value: strings.TrimSpace(buff.String()),
|
||||
@ -175,7 +177,7 @@ func (gui *Gui) GenerateMenuCandidates(commandOutput, filter, valueFormat, label
|
||||
buff.Reset()
|
||||
err = descTemp.Execute(buff, tmplData)
|
||||
if err != nil {
|
||||
return candidates, gui.surfaceError(err)
|
||||
return candidates, gui.PopupHandler.Error(err)
|
||||
}
|
||||
entry.label = strings.TrimSpace(buff.String())
|
||||
} else {
|
||||
@ -193,33 +195,33 @@ func (gui *Gui) menuPromptFromCommand(prompt config.CustomCommandPrompt, promptR
|
||||
// Collect cmd to run from config
|
||||
cmdStr, err := gui.resolveTemplate(prompt.Command, promptResponses)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
// Collect Filter regexp
|
||||
filter, err := gui.resolveTemplate(prompt.Filter, promptResponses)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
// Run and save output
|
||||
message, err := gui.Git.Custom.RunWithOutput(cmdStr)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
// Need to make a menu out of what the cmd has displayed
|
||||
candidates, err := gui.GenerateMenuCandidates(message, filter, prompt.ValueFormat, prompt.LabelFormat)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
menuItems := make([]*menuItem, len(candidates))
|
||||
menuItems := make([]*popup.MenuItem, len(candidates))
|
||||
for i := range candidates {
|
||||
i := i
|
||||
menuItems[i] = &menuItem{
|
||||
displayStrings: []string{candidates[i].label},
|
||||
onPress: func() error {
|
||||
menuItems[i] = &popup.MenuItem{
|
||||
DisplayStrings: []string{candidates[i].label},
|
||||
OnPress: func() error {
|
||||
promptResponses[responseIdx] = candidates[i].value
|
||||
return wrappedF()
|
||||
},
|
||||
@ -228,10 +230,10 @@ func (gui *Gui) menuPromptFromCommand(prompt config.CustomCommandPrompt, promptR
|
||||
|
||||
title, err := gui.resolveTemplate(prompt.Title, promptResponses)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
return gui.createMenu(title, menuItems, createMenuOptions{showCancel: true})
|
||||
return gui.PopupHandler.Menu(popup.CreateMenuOptions{Title: title, Items: menuItems})
|
||||
}
|
||||
|
||||
func (gui *Gui) handleCustomCommandKeybinding(customCommand config.CustomCommand) func() error {
|
||||
@ -241,7 +243,7 @@ func (gui *Gui) handleCustomCommandKeybinding(customCommand config.CustomCommand
|
||||
f := func() error {
|
||||
cmdStr, err := gui.resolveTemplate(customCommand.Command, promptResponses)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
if customCommand.Subprocess {
|
||||
@ -252,7 +254,7 @@ func (gui *Gui) handleCustomCommandKeybinding(customCommand config.CustomCommand
|
||||
if loadingText == "" {
|
||||
loadingText = gui.Tr.LcRunningCustomCommandStatus
|
||||
}
|
||||
return gui.WithWaitingStatus(loadingText, func() error {
|
||||
return gui.PopupHandler.WithWaitingStatus(loadingText, func() error {
|
||||
gui.logAction(gui.Tr.Actions.CustomCommand)
|
||||
cmdObj := gui.OSCommand.Cmd.NewShell(cmdStr)
|
||||
if customCommand.Stream {
|
||||
@ -260,9 +262,9 @@ func (gui *Gui) handleCustomCommandKeybinding(customCommand config.CustomCommand
|
||||
}
|
||||
err := cmdObj.Run()
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
return gui.refreshSidePanels(refreshOptions{})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{})
|
||||
})
|
||||
}
|
||||
|
||||
@ -291,7 +293,7 @@ func (gui *Gui) handleCustomCommandKeybinding(customCommand config.CustomCommand
|
||||
return gui.menuPromptFromCommand(prompt, promptResponses, idx, wrappedF)
|
||||
}
|
||||
default:
|
||||
return gui.createErrorPanel("custom command prompt must have a type of 'input', 'menu' or 'menuFromCommand'")
|
||||
return gui.PopupHandler.ErrorMsg("custom command prompt must have a type of 'input', 'menu' or 'menuFromCommand'")
|
||||
}
|
||||
|
||||
}
|
||||
@ -300,8 +302,8 @@ func (gui *Gui) handleCustomCommandKeybinding(customCommand config.CustomCommand
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) GetCustomCommandKeybindings() []*Binding {
|
||||
bindings := []*Binding{}
|
||||
func (gui *Gui) GetCustomCommandKeybindings() []*types.Binding {
|
||||
bindings := []*types.Binding{}
|
||||
customCommands := gui.UserConfig.CustomCommands
|
||||
|
||||
for _, customCommand := range customCommands {
|
||||
@ -334,7 +336,7 @@ func (gui *Gui) GetCustomCommandKeybindings() []*Binding {
|
||||
description = customCommand.Command
|
||||
}
|
||||
|
||||
bindings = append(bindings, &Binding{
|
||||
bindings = append(bindings, &types.Binding{
|
||||
ViewName: viewName,
|
||||
Contexts: contexts,
|
||||
Key: gui.getKey(customCommand.Key),
|
||||
|
@ -28,7 +28,7 @@ func isShowingDiff(gui *Gui) bool {
|
||||
func (gui *Gui) IncreaseContextInDiffView() error {
|
||||
if isShowingDiff(gui) {
|
||||
if err := gui.CheckCanChangeContext(); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
gui.UserConfig.Git.DiffContextSize = gui.UserConfig.Git.DiffContextSize + 1
|
||||
@ -43,7 +43,7 @@ func (gui *Gui) DecreaseContextInDiffView() error {
|
||||
|
||||
if isShowingDiff(gui) && old_size > 1 {
|
||||
if err := gui.CheckCanChangeContext(); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
gui.UserConfig.Git.DiffContextSize = old_size - 1
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/patch"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@ -144,8 +145,8 @@ func TestDoesntIncreaseContextInDiffViewInContextWhenInPatchBuildingMode(t *test
|
||||
gui.Git.Patch.PatchManager.Start("from", "to", false, false)
|
||||
|
||||
errorCount := 0
|
||||
gui.PopupHandler = &TestPopupHandler{
|
||||
onError: func(message string) error {
|
||||
gui.PopupHandler = &popup.TestPopupHandler{
|
||||
OnErrorMsg: func(message string) error {
|
||||
assert.Equal(t, gui.Tr.CantChangeContextSizeError, message)
|
||||
errorCount += 1
|
||||
return nil
|
||||
@ -166,8 +167,8 @@ func TestDoesntDecreaseContextInDiffViewInContextWhenInPatchBuildingMode(t *test
|
||||
gui.Git.Patch.PatchManager.Start("from", "to", false, false)
|
||||
|
||||
errorCount := 0
|
||||
gui.PopupHandler = &TestPopupHandler{
|
||||
onError: func(message string) error {
|
||||
gui.PopupHandler = &popup.TestPopupHandler{
|
||||
OnErrorMsg: func(message string) error {
|
||||
assert.Equal(t, gui.Tr.CantChangeContextSizeError, message)
|
||||
errorCount += 1
|
||||
return nil
|
||||
|
@ -5,11 +5,13 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/modes/diffing"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
func (gui *Gui) exitDiffMode() error {
|
||||
gui.State.Modes.Diffing = diffing.New()
|
||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC})
|
||||
}
|
||||
|
||||
func (gui *Gui) renderDiff() error {
|
||||
@ -105,31 +107,31 @@ func (gui *Gui) diffStr() string {
|
||||
func (gui *Gui) handleCreateDiffingMenuPanel() error {
|
||||
names := gui.currentDiffTerminals()
|
||||
|
||||
menuItems := []*menuItem{}
|
||||
menuItems := []*popup.MenuItem{}
|
||||
for _, name := range names {
|
||||
name := name
|
||||
menuItems = append(menuItems, []*menuItem{
|
||||
menuItems = append(menuItems, []*popup.MenuItem{
|
||||
{
|
||||
displayString: fmt.Sprintf("%s %s", gui.Tr.LcDiff, name),
|
||||
onPress: func() error {
|
||||
DisplayString: fmt.Sprintf("%s %s", gui.Tr.LcDiff, name),
|
||||
OnPress: func() error {
|
||||
gui.State.Modes.Diffing.Ref = name
|
||||
// can scope this down based on current view but too lazy right now
|
||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC})
|
||||
},
|
||||
},
|
||||
}...)
|
||||
}
|
||||
|
||||
menuItems = append(menuItems, []*menuItem{
|
||||
menuItems = append(menuItems, []*popup.MenuItem{
|
||||
{
|
||||
displayString: gui.Tr.LcEnterRefToDiff,
|
||||
onPress: func() error {
|
||||
return gui.prompt(promptOpts{
|
||||
title: gui.Tr.LcEnteRefName,
|
||||
findSuggestionsFunc: gui.getRefsSuggestionsFunc(),
|
||||
handleConfirm: func(response string) error {
|
||||
DisplayString: gui.Tr.LcEnterRefToDiff,
|
||||
OnPress: func() error {
|
||||
return gui.PopupHandler.Prompt(popup.PromptOpts{
|
||||
Title: gui.Tr.LcEnteRefName,
|
||||
FindSuggestionsFunc: gui.getRefsSuggestionsFunc(),
|
||||
HandleConfirm: func(response string) error {
|
||||
gui.State.Modes.Diffing.Ref = strings.TrimSpace(response)
|
||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC})
|
||||
},
|
||||
})
|
||||
},
|
||||
@ -137,23 +139,23 @@ func (gui *Gui) handleCreateDiffingMenuPanel() error {
|
||||
}...)
|
||||
|
||||
if gui.State.Modes.Diffing.Active() {
|
||||
menuItems = append(menuItems, []*menuItem{
|
||||
menuItems = append(menuItems, []*popup.MenuItem{
|
||||
{
|
||||
displayString: gui.Tr.LcSwapDiff,
|
||||
onPress: func() error {
|
||||
DisplayString: gui.Tr.LcSwapDiff,
|
||||
OnPress: func() error {
|
||||
gui.State.Modes.Diffing.Reverse = !gui.State.Modes.Diffing.Reverse
|
||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC})
|
||||
},
|
||||
},
|
||||
{
|
||||
displayString: gui.Tr.LcExitDiffMode,
|
||||
onPress: func() error {
|
||||
DisplayString: gui.Tr.LcExitDiffMode,
|
||||
OnPress: func() error {
|
||||
gui.State.Modes.Diffing = diffing.New()
|
||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC})
|
||||
},
|
||||
},
|
||||
}...)
|
||||
}
|
||||
|
||||
return gui.createMenu(gui.Tr.DiffingMenuTitle, menuItems, createMenuOptions{showCancel: true})
|
||||
return gui.PopupHandler.Menu(popup.CreateMenuOptions{Title: gui.Tr.DiffingMenuTitle, Items: menuItems})
|
||||
}
|
||||
|
@ -1,36 +1,41 @@
|
||||
package gui
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
func (gui *Gui) handleCreateDiscardMenu() error {
|
||||
node := gui.getSelectedFileNode()
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var menuItems []*menuItem
|
||||
var menuItems []*popup.MenuItem
|
||||
if node.File == nil {
|
||||
menuItems = []*menuItem{
|
||||
menuItems = []*popup.MenuItem{
|
||||
{
|
||||
displayString: gui.Tr.LcDiscardAllChanges,
|
||||
onPress: func() error {
|
||||
DisplayString: gui.Tr.LcDiscardAllChanges,
|
||||
OnPress: func() error {
|
||||
gui.logAction(gui.Tr.Actions.DiscardAllChangesInDirectory)
|
||||
if err := gui.Git.WorkingTree.DiscardAllDirChanges(node); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []RefreshableView{FILES}})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES}})
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if node.GetHasStagedChanges() && node.GetHasUnstagedChanges() {
|
||||
menuItems = append(menuItems, &menuItem{
|
||||
displayString: gui.Tr.LcDiscardUnstagedChanges,
|
||||
onPress: func() error {
|
||||
menuItems = append(menuItems, &popup.MenuItem{
|
||||
DisplayString: gui.Tr.LcDiscardUnstagedChanges,
|
||||
OnPress: func() error {
|
||||
gui.logAction(gui.Tr.Actions.DiscardUnstagedChangesInDirectory)
|
||||
if err := gui.Git.WorkingTree.DiscardUnstagedDirChanges(node); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []RefreshableView{FILES}})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES}})
|
||||
},
|
||||
})
|
||||
}
|
||||
@ -41,43 +46,43 @@ func (gui *Gui) handleCreateDiscardMenu() error {
|
||||
if file.IsSubmodule(submodules) {
|
||||
submodule := file.SubmoduleConfig(submodules)
|
||||
|
||||
menuItems = []*menuItem{
|
||||
menuItems = []*popup.MenuItem{
|
||||
{
|
||||
displayString: gui.Tr.LcSubmoduleStashAndReset,
|
||||
onPress: func() error {
|
||||
return gui.handleResetSubmodule(submodule)
|
||||
DisplayString: gui.Tr.LcSubmoduleStashAndReset,
|
||||
OnPress: func() error {
|
||||
return gui.resetSubmodule(submodule)
|
||||
},
|
||||
},
|
||||
}
|
||||
} else {
|
||||
menuItems = []*menuItem{
|
||||
menuItems = []*popup.MenuItem{
|
||||
{
|
||||
displayString: gui.Tr.LcDiscardAllChanges,
|
||||
onPress: func() error {
|
||||
DisplayString: gui.Tr.LcDiscardAllChanges,
|
||||
OnPress: func() error {
|
||||
gui.logAction(gui.Tr.Actions.DiscardAllChangesInFile)
|
||||
if err := gui.Git.WorkingTree.DiscardAllFileChanges(file); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []RefreshableView{FILES}})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES}})
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if file.HasStagedChanges && file.HasUnstagedChanges {
|
||||
menuItems = append(menuItems, &menuItem{
|
||||
displayString: gui.Tr.LcDiscardUnstagedChanges,
|
||||
onPress: func() error {
|
||||
menuItems = append(menuItems, &popup.MenuItem{
|
||||
DisplayString: gui.Tr.LcDiscardUnstagedChanges,
|
||||
OnPress: func() error {
|
||||
gui.logAction(gui.Tr.Actions.DiscardAllUnstagedChangesInFile)
|
||||
if err := gui.Git.WorkingTree.DiscardUnstagedFileChanges(file); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []RefreshableView{FILES}})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES}})
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return gui.createMenu(node.GetPath(), menuItems, createMenuOptions{showCancel: true})
|
||||
return gui.PopupHandler.Menu(popup.CreateMenuOptions{Title: node.GetPath(), Items: menuItems})
|
||||
}
|
||||
|
@ -3,34 +3,36 @@ package gui
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
)
|
||||
|
||||
func (gui *Gui) handleCreateExtrasMenuPanel() error {
|
||||
menuItems := []*menuItem{
|
||||
{
|
||||
displayString: gui.Tr.ToggleShowCommandLog,
|
||||
onPress: func() error {
|
||||
currentContext := gui.currentStaticContext()
|
||||
if gui.ShowExtrasWindow && currentContext.GetKey() == COMMAND_LOG_CONTEXT_KEY {
|
||||
if err := gui.returnFromContext(); err != nil {
|
||||
return err
|
||||
return gui.PopupHandler.Menu(popup.CreateMenuOptions{
|
||||
Title: gui.Tr.CommandLog,
|
||||
Items: []*popup.MenuItem{
|
||||
{
|
||||
DisplayString: gui.Tr.ToggleShowCommandLog,
|
||||
OnPress: func() error {
|
||||
currentContext := gui.currentStaticContext()
|
||||
if gui.ShowExtrasWindow && currentContext.GetKey() == COMMAND_LOG_CONTEXT_KEY {
|
||||
if err := gui.returnFromContext(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
show := !gui.ShowExtrasWindow
|
||||
gui.ShowExtrasWindow = show
|
||||
gui.Config.GetAppState().HideCommandLog = !show
|
||||
_ = gui.Config.SaveAppState()
|
||||
return nil
|
||||
show := !gui.ShowExtrasWindow
|
||||
gui.ShowExtrasWindow = show
|
||||
gui.Config.GetAppState().HideCommandLog = !show
|
||||
_ = gui.Config.SaveAppState()
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
DisplayString: gui.Tr.FocusCommandLog,
|
||||
OnPress: gui.handleFocusCommandLog,
|
||||
},
|
||||
},
|
||||
{
|
||||
displayString: gui.Tr.FocusCommandLog,
|
||||
onPress: gui.handleFocusCommandLog,
|
||||
},
|
||||
}
|
||||
|
||||
return gui.createMenu(gui.Tr.CommandLog, menuItems, createMenuOptions{showCancel: true})
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) handleFocusCommandLog() error {
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
@ -117,7 +118,7 @@ func (gui *Gui) watchFilesForChanges() {
|
||||
}
|
||||
// only refresh if we're not already
|
||||
if !gui.State.IsRefreshingFiles {
|
||||
_ = gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []RefreshableView{FILES}})
|
||||
_ = gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES}})
|
||||
}
|
||||
|
||||
// watch for errors
|
||||
|
@ -12,6 +12,8 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/mergeconflicts"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
@ -67,7 +69,7 @@ func (gui *Gui) filesRenderToMain() error {
|
||||
|
||||
gui.resetMergeStateWithLock()
|
||||
|
||||
cmdObj := gui.Git.WorkingTree.WorktreeFileDiffCmdObj(node, false, !node.GetHasUnstagedChanges() && node.GetHasStagedChanges(), gui.State.IgnoreWhitespaceInDiffView)
|
||||
cmdObj := gui.Git.WorkingTree.WorktreeFileDiffCmdObj(node, false, !node.GetHasUnstagedChanges() && node.GetHasStagedChanges(), gui.IgnoreWhitespaceInDiffView)
|
||||
|
||||
refreshOpts := refreshMainOpts{main: &viewUpdateOpts{
|
||||
title: gui.Tr.UnstagedChanges,
|
||||
@ -76,7 +78,7 @@ func (gui *Gui) filesRenderToMain() error {
|
||||
|
||||
if node.GetHasUnstagedChanges() {
|
||||
if node.GetHasStagedChanges() {
|
||||
cmdObj := gui.Git.WorkingTree.WorktreeFileDiffCmdObj(node, false, true, gui.State.IgnoreWhitespaceInDiffView)
|
||||
cmdObj := gui.Git.WorkingTree.WorktreeFileDiffCmdObj(node, false, true, gui.IgnoreWhitespaceInDiffView)
|
||||
|
||||
refreshOpts.secondary = &viewUpdateOpts{
|
||||
title: gui.Tr.StagedChanges,
|
||||
@ -191,7 +193,7 @@ func (gui *Gui) enterFile(opts OnFocusOpts) error {
|
||||
return gui.switchToMerge()
|
||||
}
|
||||
if file.HasMergeConflicts {
|
||||
return gui.createErrorPanel(gui.Tr.FileStagingRequirements)
|
||||
return gui.PopupHandler.ErrorMsg(gui.Tr.FileStagingRequirements)
|
||||
}
|
||||
|
||||
return gui.pushContext(gui.State.Contexts.Staging, opts)
|
||||
@ -213,36 +215,36 @@ func (gui *Gui) handleFilePress() error {
|
||||
if file.HasUnstagedChanges {
|
||||
gui.logAction(gui.Tr.Actions.StageFile)
|
||||
if err := gui.Git.WorkingTree.StageFile(file.Name); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
} else {
|
||||
gui.logAction(gui.Tr.Actions.UnstageFile)
|
||||
if err := gui.Git.WorkingTree.UnStageFile(file.Names(), file.Tracked); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// if any files within have inline merge conflicts we can't stage or unstage,
|
||||
// or it'll end up with those >>>>>> lines actually staged
|
||||
if node.GetHasInlineMergeConflicts() {
|
||||
return gui.createErrorPanel(gui.Tr.ErrStageDirWithInlineMergeConflicts)
|
||||
return gui.PopupHandler.ErrorMsg(gui.Tr.ErrStageDirWithInlineMergeConflicts)
|
||||
}
|
||||
|
||||
if node.GetHasUnstagedChanges() {
|
||||
gui.logAction(gui.Tr.Actions.StageFile)
|
||||
if err := gui.Git.WorkingTree.StageFile(node.Path); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
} else {
|
||||
// pretty sure it doesn't matter that we're always passing true here
|
||||
gui.logAction(gui.Tr.Actions.UnstageFile)
|
||||
if err := gui.Git.WorkingTree.UnStageFile([]string{node.Path}, true); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{FILES}}); err != nil {
|
||||
if err := gui.refreshSidePanels(types.RefreshOptions{Scope: []types.RefreshableView{types.FILES}}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -273,10 +275,10 @@ func (gui *Gui) handleStageAll() error {
|
||||
err = gui.Git.WorkingTree.StageAll()
|
||||
}
|
||||
if err != nil {
|
||||
_ = gui.surfaceError(err)
|
||||
_ = gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
if err := gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{FILES}}); err != nil {
|
||||
if err := gui.refreshSidePanels(types.RefreshOptions{Scope: []types.RefreshableView{types.FILES}}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -290,7 +292,7 @@ func (gui *Gui) handleIgnoreFile() error {
|
||||
}
|
||||
|
||||
if node.GetPath() == ".gitignore" {
|
||||
return gui.createErrorPanel("Cannot ignore .gitignore")
|
||||
return gui.PopupHandler.ErrorMsg("Cannot ignore .gitignore")
|
||||
}
|
||||
|
||||
unstageFiles := func() error {
|
||||
@ -306,10 +308,10 @@ func (gui *Gui) handleIgnoreFile() error {
|
||||
}
|
||||
|
||||
if node.GetIsTracked() {
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.IgnoreTracked,
|
||||
prompt: gui.Tr.IgnoreTrackedPrompt,
|
||||
handleConfirm: func() error {
|
||||
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: gui.Tr.IgnoreTracked,
|
||||
Prompt: gui.Tr.IgnoreTrackedPrompt,
|
||||
HandleConfirm: func() error {
|
||||
gui.logAction(gui.Tr.Actions.IgnoreFile)
|
||||
// not 100% sure if this is necessary but I'll assume it is
|
||||
if err := unstageFiles(); err != nil {
|
||||
@ -323,7 +325,7 @@ func (gui *Gui) handleIgnoreFile() error {
|
||||
if err := gui.Git.WorkingTree.Ignore(node.GetPath()); err != nil {
|
||||
return err
|
||||
}
|
||||
return gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{FILES}})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Scope: []types.RefreshableView{types.FILES}})
|
||||
},
|
||||
})
|
||||
}
|
||||
@ -335,16 +337,16 @@ func (gui *Gui) handleIgnoreFile() error {
|
||||
}
|
||||
|
||||
if err := gui.Git.WorkingTree.Ignore(node.GetPath()); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
return gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{FILES}})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Scope: []types.RefreshableView{types.FILES}})
|
||||
}
|
||||
|
||||
func (gui *Gui) handleWIPCommitPress() error {
|
||||
skipHookPrefix := gui.UserConfig.Git.SkipHookPrefix
|
||||
if skipHookPrefix == "" {
|
||||
return gui.createErrorPanel(gui.Tr.SkipHookPrefixNotConfigured)
|
||||
return gui.PopupHandler.ErrorMsg(gui.Tr.SkipHookPrefixNotConfigured)
|
||||
}
|
||||
|
||||
textArea := gui.Views.CommitMessage.TextArea
|
||||
@ -381,11 +383,11 @@ func (gui *Gui) prepareFilesForCommit() error {
|
||||
|
||||
func (gui *Gui) handleCommitPress() error {
|
||||
if err := gui.prepareFilesForCommit(); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
if gui.State.FileTreeViewModel.GetItemsLength() == 0 {
|
||||
return gui.createErrorPanel(gui.Tr.NoFilesStagedTitle)
|
||||
return gui.PopupHandler.ErrorMsg(gui.Tr.NoFilesStagedTitle)
|
||||
}
|
||||
|
||||
if len(gui.stagedFiles()) == 0 {
|
||||
@ -403,7 +405,7 @@ func (gui *Gui) handleCommitPress() error {
|
||||
prefixReplace := commitPrefixConfig.Replace
|
||||
rgx, err := regexp.Compile(prefixPattern)
|
||||
if err != nil {
|
||||
return gui.createErrorPanel(fmt.Sprintf("%s: %s", gui.Tr.LcCommitPrefixPatternError, err.Error()))
|
||||
return gui.PopupHandler.ErrorMsg(fmt.Sprintf("%s: %s", gui.Tr.LcCommitPrefixPatternError, err.Error()))
|
||||
}
|
||||
prefix := rgx.ReplaceAllString(gui.getCheckedOutBranch().Name, prefixReplace)
|
||||
gui.Views.CommitMessage.ClearTextArea()
|
||||
@ -421,16 +423,16 @@ func (gui *Gui) handleCommitPress() error {
|
||||
}
|
||||
|
||||
func (gui *Gui) promptToStageAllAndRetry(retry func() error) error {
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.NoFilesStagedTitle,
|
||||
prompt: gui.Tr.NoFilesStagedPrompt,
|
||||
handleConfirm: func() error {
|
||||
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: gui.Tr.NoFilesStagedTitle,
|
||||
Prompt: gui.Tr.NoFilesStagedPrompt,
|
||||
HandleConfirm: func() error {
|
||||
gui.logAction(gui.Tr.Actions.StageAllFiles)
|
||||
if err := gui.Git.WorkingTree.StageAll(); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
if err := gui.refreshFilesAndSubmodules(); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
return retry()
|
||||
@ -440,7 +442,7 @@ func (gui *Gui) promptToStageAllAndRetry(retry func() error) error {
|
||||
|
||||
func (gui *Gui) handleAmendCommitPress() error {
|
||||
if gui.State.FileTreeViewModel.GetItemsLength() == 0 {
|
||||
return gui.createErrorPanel(gui.Tr.NoFilesStagedTitle)
|
||||
return gui.PopupHandler.ErrorMsg(gui.Tr.NoFilesStagedTitle)
|
||||
}
|
||||
|
||||
if len(gui.stagedFiles()) == 0 {
|
||||
@ -448,13 +450,13 @@ func (gui *Gui) handleAmendCommitPress() error {
|
||||
}
|
||||
|
||||
if len(gui.State.Commits) == 0 {
|
||||
return gui.createErrorPanel(gui.Tr.NoCommitToAmend)
|
||||
return gui.PopupHandler.ErrorMsg(gui.Tr.NoCommitToAmend)
|
||||
}
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: strings.Title(gui.Tr.AmendLastCommit),
|
||||
prompt: gui.Tr.SureToAmend,
|
||||
handleConfirm: func() error {
|
||||
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: strings.Title(gui.Tr.AmendLastCommit),
|
||||
Prompt: gui.Tr.SureToAmend,
|
||||
HandleConfirm: func() error {
|
||||
cmdObj := gui.Git.Commit.AmendHeadCmdObj()
|
||||
gui.logAction(gui.Tr.Actions.AmendCommit)
|
||||
return gui.withGpgHandling(cmdObj, gui.Tr.AmendingStatus, nil)
|
||||
@ -466,7 +468,7 @@ func (gui *Gui) handleAmendCommitPress() error {
|
||||
// their editor rather than via the popup panel
|
||||
func (gui *Gui) handleCommitEditorPress() error {
|
||||
if gui.State.FileTreeViewModel.GetItemsLength() == 0 {
|
||||
return gui.createErrorPanel(gui.Tr.NoFilesStagedTitle)
|
||||
return gui.PopupHandler.ErrorMsg(gui.Tr.NoFilesStagedTitle)
|
||||
}
|
||||
|
||||
if len(gui.stagedFiles()) == 0 {
|
||||
@ -480,28 +482,29 @@ func (gui *Gui) handleCommitEditorPress() error {
|
||||
}
|
||||
|
||||
func (gui *Gui) handleStatusFilterPressed() error {
|
||||
menuItems := []*menuItem{
|
||||
{
|
||||
displayString: gui.Tr.FilterStagedFiles,
|
||||
onPress: func() error {
|
||||
return gui.setStatusFiltering(filetree.DisplayStaged)
|
||||
return gui.PopupHandler.Menu(popup.CreateMenuOptions{
|
||||
Title: gui.Tr.FilteringMenuTitle,
|
||||
Items: []*popup.MenuItem{
|
||||
{
|
||||
DisplayString: gui.Tr.FilterStagedFiles,
|
||||
OnPress: func() error {
|
||||
return gui.setStatusFiltering(filetree.DisplayStaged)
|
||||
},
|
||||
},
|
||||
{
|
||||
DisplayString: gui.Tr.FilterUnstagedFiles,
|
||||
OnPress: func() error {
|
||||
return gui.setStatusFiltering(filetree.DisplayUnstaged)
|
||||
},
|
||||
},
|
||||
{
|
||||
DisplayString: gui.Tr.ResetCommitFilterState,
|
||||
OnPress: func() error {
|
||||
return gui.setStatusFiltering(filetree.DisplayAll)
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayString: gui.Tr.FilterUnstagedFiles,
|
||||
onPress: func() error {
|
||||
return gui.setStatusFiltering(filetree.DisplayUnstaged)
|
||||
},
|
||||
},
|
||||
{
|
||||
displayString: gui.Tr.ResetCommitFilterState,
|
||||
onPress: func() error {
|
||||
return gui.setStatusFiltering(filetree.DisplayAll)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return gui.createMenu(gui.Tr.FilteringMenuTitle, menuItems, createMenuOptions{showCancel: true})
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) setStatusFiltering(filter filetree.FileTreeDisplayFilter) error {
|
||||
@ -517,7 +520,7 @@ func (gui *Gui) editFile(filename string) error {
|
||||
func (gui *Gui) editFileAtLine(filename string, lineNumber int) error {
|
||||
cmdStr, err := gui.Git.File.GetEditCmdStr(filename, lineNumber)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
gui.logAction(gui.Tr.Actions.EditFile)
|
||||
@ -533,7 +536,7 @@ func (gui *Gui) handleFileEdit() error {
|
||||
}
|
||||
|
||||
if node.File == nil {
|
||||
return gui.createErrorPanel(gui.Tr.ErrCannotEditDirectory)
|
||||
return gui.PopupHandler.ErrorMsg(gui.Tr.ErrCannotEditDirectory)
|
||||
}
|
||||
|
||||
return gui.editFile(node.GetPath())
|
||||
@ -549,7 +552,7 @@ func (gui *Gui) handleFileOpen() error {
|
||||
}
|
||||
|
||||
func (gui *Gui) handleRefreshFiles() error {
|
||||
return gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{FILES}})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Scope: []types.RefreshableView{types.FILES}})
|
||||
}
|
||||
|
||||
func (gui *Gui) refreshStateFiles() error {
|
||||
@ -666,10 +669,10 @@ func (gui *Gui) refreshStateFiles() error {
|
||||
func (gui *Gui) promptToContinueRebase() error {
|
||||
gui.takeOverMergeConflictScrolling()
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: "continue",
|
||||
prompt: gui.Tr.ConflictsResolved,
|
||||
handleConfirm: func() error {
|
||||
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: "continue",
|
||||
Prompt: gui.Tr.ConflictsResolved,
|
||||
HandleConfirm: func() error {
|
||||
return gui.genericMergeCommand(REBASE_OPTION_CONTINUE)
|
||||
},
|
||||
})
|
||||
@ -730,15 +733,15 @@ func (gui *Gui) handlePullFiles() error {
|
||||
if !currentBranch.IsTrackingRemote() {
|
||||
suggestedRemote := getSuggestedRemote(gui.State.Remotes)
|
||||
|
||||
return gui.prompt(promptOpts{
|
||||
title: gui.Tr.EnterUpstream,
|
||||
initialContent: suggestedRemote + " " + currentBranch.Name,
|
||||
findSuggestionsFunc: gui.getRemoteBranchesSuggestionsFunc(" "),
|
||||
handleConfirm: func(upstream string) error {
|
||||
return gui.PopupHandler.Prompt(popup.PromptOpts{
|
||||
Title: gui.Tr.EnterUpstream,
|
||||
InitialContent: suggestedRemote + " " + currentBranch.Name,
|
||||
FindSuggestionsFunc: gui.getRemoteBranchesSuggestionsFunc(" "),
|
||||
HandleConfirm: func(upstream string) error {
|
||||
var upstreamBranch, upstreamRemote string
|
||||
split := strings.Split(upstream, " ")
|
||||
if len(split) != 2 {
|
||||
return gui.createErrorPanel(gui.Tr.InvalidUpstream)
|
||||
return gui.PopupHandler.ErrorMsg(gui.Tr.InvalidUpstream)
|
||||
}
|
||||
|
||||
upstreamRemote = split[0]
|
||||
@ -749,7 +752,7 @@ func (gui *Gui) handlePullFiles() error {
|
||||
if strings.Contains(errorMessage, "does not exist") {
|
||||
errorMessage = fmt.Sprintf("upstream branch %s not found.\nIf you expect it to exist, you should fetch (with 'f').\nOtherwise, you should push (with 'shift+P')", upstream)
|
||||
}
|
||||
return gui.createErrorPanel(errorMessage)
|
||||
return gui.PopupHandler.ErrorMsg(errorMessage)
|
||||
}
|
||||
return gui.pullFiles(PullFilesOptions{UpstreamRemote: upstreamRemote, UpstreamBranch: upstreamBranch, action: action})
|
||||
},
|
||||
@ -767,14 +770,9 @@ type PullFilesOptions struct {
|
||||
}
|
||||
|
||||
func (gui *Gui) pullFiles(opts PullFilesOptions) error {
|
||||
if err := gui.createLoaderPanel(gui.Tr.PullWait); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: this doesn't look like a good idea. Why the goroutine?
|
||||
go utils.Safe(func() { _ = gui.pullWithLock(opts) })
|
||||
|
||||
return nil
|
||||
return gui.PopupHandler.WithLoaderPanel(gui.Tr.PullWait, func() error {
|
||||
return gui.pullWithLock(opts)
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) pullWithLock(opts PullFilesOptions) error {
|
||||
@ -804,10 +802,7 @@ type pushOpts struct {
|
||||
}
|
||||
|
||||
func (gui *Gui) push(opts pushOpts) error {
|
||||
if err := gui.createLoaderPanel(gui.Tr.PushWait); err != nil {
|
||||
return err
|
||||
}
|
||||
go utils.Safe(func() {
|
||||
return gui.PopupHandler.WithLoaderPanel(gui.Tr.PushWait, func() error {
|
||||
gui.logAction(gui.Tr.Actions.Push)
|
||||
err := gui.Git.Sync.Push(git_commands.PushOpts{
|
||||
Force: opts.force,
|
||||
@ -816,28 +811,29 @@ func (gui *Gui) push(opts pushOpts) error {
|
||||
SetUpstream: opts.setUpstream,
|
||||
})
|
||||
|
||||
if err != nil && !opts.force && strings.Contains(err.Error(), "Updates were rejected") {
|
||||
forcePushDisabled := gui.UserConfig.Git.DisableForcePushing
|
||||
if forcePushDisabled {
|
||||
_ = gui.createErrorPanel(gui.Tr.UpdatesRejectedAndForcePushDisabled)
|
||||
return
|
||||
}
|
||||
_ = gui.ask(askOpts{
|
||||
title: gui.Tr.ForcePush,
|
||||
prompt: gui.Tr.ForcePushPrompt,
|
||||
handleConfirm: func() error {
|
||||
newOpts := opts
|
||||
newOpts.force = true
|
||||
if err != nil {
|
||||
if !opts.force && strings.Contains(err.Error(), "Updates were rejected") {
|
||||
forcePushDisabled := gui.UserConfig.Git.DisableForcePushing
|
||||
if forcePushDisabled {
|
||||
_ = gui.PopupHandler.ErrorMsg(gui.Tr.UpdatesRejectedAndForcePushDisabled)
|
||||
return nil
|
||||
}
|
||||
_ = gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: gui.Tr.ForcePush,
|
||||
Prompt: gui.Tr.ForcePushPrompt,
|
||||
HandleConfirm: func() error {
|
||||
newOpts := opts
|
||||
newOpts.force = true
|
||||
|
||||
return gui.push(newOpts)
|
||||
},
|
||||
})
|
||||
return
|
||||
return gui.push(newOpts)
|
||||
},
|
||||
})
|
||||
return nil
|
||||
}
|
||||
_ = gui.PopupHandler.Error(err)
|
||||
}
|
||||
gui.handleCredentialsPopup(err)
|
||||
_ = gui.refreshSidePanels(refreshOptions{mode: ASYNC})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC})
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) pushFiles() error {
|
||||
@ -870,11 +866,11 @@ func (gui *Gui) pushFiles() error {
|
||||
if gui.Git.Config.GetPushToCurrent() {
|
||||
return gui.push(pushOpts{setUpstream: true})
|
||||
} else {
|
||||
return gui.prompt(promptOpts{
|
||||
title: gui.Tr.EnterUpstream,
|
||||
initialContent: suggestedRemote + " " + currentBranch.Name,
|
||||
findSuggestionsFunc: gui.getRemoteBranchesSuggestionsFunc(" "),
|
||||
handleConfirm: func(upstream string) error {
|
||||
return gui.PopupHandler.Prompt(popup.PromptOpts{
|
||||
Title: gui.Tr.EnterUpstream,
|
||||
InitialContent: suggestedRemote + " " + currentBranch.Name,
|
||||
FindSuggestionsFunc: gui.getRemoteBranchesSuggestionsFunc(" "),
|
||||
HandleConfirm: func(upstream string) error {
|
||||
var upstreamBranch, upstreamRemote string
|
||||
split := strings.Split(upstream, " ")
|
||||
if len(split) == 2 {
|
||||
@ -914,13 +910,13 @@ func getSuggestedRemote(remotes []*models.Remote) string {
|
||||
func (gui *Gui) requestToForcePush(opts pushOpts) error {
|
||||
forcePushDisabled := gui.UserConfig.Git.DisableForcePushing
|
||||
if forcePushDisabled {
|
||||
return gui.createErrorPanel(gui.Tr.ForcePushDisabled)
|
||||
return gui.PopupHandler.ErrorMsg(gui.Tr.ForcePushDisabled)
|
||||
}
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.ForcePush,
|
||||
prompt: gui.Tr.ForcePushPrompt,
|
||||
handleConfirm: func() error {
|
||||
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: gui.Tr.ForcePush,
|
||||
Prompt: gui.Tr.ForcePushPrompt,
|
||||
HandleConfirm: func() error {
|
||||
return gui.push(opts)
|
||||
},
|
||||
})
|
||||
@ -950,16 +946,16 @@ func (gui *Gui) switchToMerge() error {
|
||||
func (gui *Gui) openFile(filename string) error {
|
||||
gui.logAction(gui.Tr.Actions.OpenFile)
|
||||
if err := gui.OSCommand.OpenFile(filename); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) handleCustomCommand() error {
|
||||
return gui.prompt(promptOpts{
|
||||
title: gui.Tr.CustomCommand,
|
||||
findSuggestionsFunc: gui.getCustomCommandsHistorySuggestionsFunc(),
|
||||
handleConfirm: func(command string) error {
|
||||
return gui.PopupHandler.Prompt(popup.PromptOpts{
|
||||
Title: gui.Tr.CustomCommand,
|
||||
FindSuggestionsFunc: gui.getCustomCommandsHistorySuggestionsFunc(),
|
||||
HandleConfirm: func(command string) error {
|
||||
gui.Config.GetAppState().CustomCommandsHistory = utils.Limit(
|
||||
utils.Uniq(
|
||||
append(gui.Config.GetAppState().CustomCommandsHistory, command),
|
||||
@ -981,24 +977,25 @@ func (gui *Gui) handleCustomCommand() error {
|
||||
}
|
||||
|
||||
func (gui *Gui) handleCreateStashMenu() error {
|
||||
menuItems := []*menuItem{
|
||||
{
|
||||
displayString: gui.Tr.LcStashAllChanges,
|
||||
onPress: func() error {
|
||||
gui.logAction(gui.Tr.Actions.StashAllChanges)
|
||||
return gui.handleStashSave(gui.Git.Stash.Save)
|
||||
return gui.PopupHandler.Menu(popup.CreateMenuOptions{
|
||||
Title: gui.Tr.LcStashOptions,
|
||||
Items: []*popup.MenuItem{
|
||||
{
|
||||
DisplayString: gui.Tr.LcStashAllChanges,
|
||||
OnPress: func() error {
|
||||
gui.logAction(gui.Tr.Actions.StashAllChanges)
|
||||
return gui.handleStashSave(gui.Git.Stash.Save)
|
||||
},
|
||||
},
|
||||
{
|
||||
DisplayString: gui.Tr.LcStashStagedChanges,
|
||||
OnPress: func() error {
|
||||
gui.logAction(gui.Tr.Actions.StashStagedChanges)
|
||||
return gui.handleStashSave(gui.Git.Stash.SaveStagedChanges)
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayString: gui.Tr.LcStashStagedChanges,
|
||||
onPress: func() error {
|
||||
gui.logAction(gui.Tr.Actions.StashStagedChanges)
|
||||
return gui.handleStashSave(gui.Git.Stash.SaveStagedChanges)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return gui.createMenu(gui.Tr.LcStashOptions, menuItems, createMenuOptions{showCancel: true})
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) handleStashChanges() error {
|
||||
@ -1052,10 +1049,10 @@ func (gui *Gui) handleToggleFileTreeView() error {
|
||||
}
|
||||
|
||||
func (gui *Gui) handleOpenMergeTool() error {
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.MergeToolTitle,
|
||||
prompt: gui.Tr.MergeToolPrompt,
|
||||
handleConfirm: func() error {
|
||||
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: gui.Tr.MergeToolTitle,
|
||||
Prompt: gui.Tr.MergeToolPrompt,
|
||||
HandleConfirm: func() error {
|
||||
gui.logAction(gui.Tr.Actions.OpenMergeTool)
|
||||
return gui.runSubprocessWithSuspenseAndRefresh(
|
||||
gui.Git.WorkingTree.OpenMergeToolCmdObj(),
|
||||
@ -1063,3 +1060,35 @@ func (gui *Gui) handleOpenMergeTool() error {
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) resetSubmodule(submodule *models.SubmoduleConfig) error {
|
||||
return gui.PopupHandler.WithWaitingStatus(gui.Tr.LcResettingSubmoduleStatus, func() error {
|
||||
gui.logAction(gui.Tr.Actions.ResetSubmodule)
|
||||
|
||||
file := gui.fileForSubmodule(submodule)
|
||||
if file != nil {
|
||||
if err := gui.Git.WorkingTree.UnStageFile(file.Names(), file.Tracked); err != nil {
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := gui.Git.Submodule.Stash(submodule); err != nil {
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
if err := gui.Git.Submodule.Reset(submodule); err != nil {
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES, types.SUBMODULES}})
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) fileForSubmodule(submodule *models.SubmoduleConfig) *models.File {
|
||||
for _, file := range gui.State.FileManager.GetAllFiles() {
|
||||
if file.IsSubmodule([]*models.SubmoduleConfig{submodule}) {
|
||||
return file
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -1,11 +1,16 @@
|
||||
package gui
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
func (gui *Gui) validateNotInFilterMode() (bool, error) {
|
||||
if gui.State.Modes.Filtering.Active() {
|
||||
err := gui.ask(askOpts{
|
||||
title: gui.Tr.MustExitFilterModeTitle,
|
||||
prompt: gui.Tr.MustExitFilterModePrompt,
|
||||
handleConfirm: gui.exitFilterMode,
|
||||
err := gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: gui.Tr.MustExitFilterModeTitle,
|
||||
Prompt: gui.Tr.MustExitFilterModePrompt,
|
||||
HandleConfirm: gui.exitFilterMode,
|
||||
})
|
||||
|
||||
return false, err
|
||||
@ -23,7 +28,7 @@ func (gui *Gui) clearFiltering() error {
|
||||
gui.State.ScreenMode = SCREEN_NORMAL
|
||||
}
|
||||
|
||||
return gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{COMMITS}})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Scope: []types.RefreshableView{types.COMMITS}})
|
||||
}
|
||||
|
||||
func (gui *Gui) setFiltering(path string) error {
|
||||
@ -36,7 +41,7 @@ func (gui *Gui) setFiltering(path string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{COMMITS}, then: func() {
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Scope: []types.RefreshableView{types.COMMITS}, Then: func() {
|
||||
gui.State.Contexts.BranchCommits.GetPanelState().SetSelectedLineIdx(0)
|
||||
}})
|
||||
}
|
||||
|
@ -3,6 +3,8 @@ package gui
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||
)
|
||||
|
||||
func (gui *Gui) handleCreateFilteringMenuPanel() error {
|
||||
@ -20,24 +22,24 @@ func (gui *Gui) handleCreateFilteringMenuPanel() error {
|
||||
}
|
||||
}
|
||||
|
||||
menuItems := []*menuItem{}
|
||||
menuItems := []*popup.MenuItem{}
|
||||
|
||||
if fileName != "" {
|
||||
menuItems = append(menuItems, &menuItem{
|
||||
displayString: fmt.Sprintf("%s '%s'", gui.Tr.LcFilterBy, fileName),
|
||||
onPress: func() error {
|
||||
menuItems = append(menuItems, &popup.MenuItem{
|
||||
DisplayString: fmt.Sprintf("%s '%s'", gui.Tr.LcFilterBy, fileName),
|
||||
OnPress: func() error {
|
||||
return gui.setFiltering(fileName)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
menuItems = append(menuItems, &menuItem{
|
||||
displayString: gui.Tr.LcFilterPathOption,
|
||||
onPress: func() error {
|
||||
return gui.prompt(promptOpts{
|
||||
findSuggestionsFunc: gui.getFilePathSuggestionsFunc(),
|
||||
title: gui.Tr.EnterFileName,
|
||||
handleConfirm: func(response string) error {
|
||||
menuItems = append(menuItems, &popup.MenuItem{
|
||||
DisplayString: gui.Tr.LcFilterPathOption,
|
||||
OnPress: func() error {
|
||||
return gui.PopupHandler.Prompt(popup.PromptOpts{
|
||||
FindSuggestionsFunc: gui.getFilePathSuggestionsFunc(),
|
||||
Title: gui.Tr.EnterFileName,
|
||||
HandleConfirm: func(response string) error {
|
||||
return gui.setFiltering(strings.TrimSpace(response))
|
||||
},
|
||||
})
|
||||
@ -45,11 +47,11 @@ func (gui *Gui) handleCreateFilteringMenuPanel() error {
|
||||
})
|
||||
|
||||
if gui.State.Modes.Filtering.Active() {
|
||||
menuItems = append(menuItems, &menuItem{
|
||||
displayString: gui.Tr.LcExitFilterMode,
|
||||
onPress: gui.clearFiltering,
|
||||
menuItems = append(menuItems, &popup.MenuItem{
|
||||
DisplayString: gui.Tr.LcExitFilterMode,
|
||||
OnPress: gui.clearFiltering,
|
||||
})
|
||||
}
|
||||
|
||||
return gui.createMenu(gui.Tr.FilteringMenuTitle, menuItems, createMenuOptions{showCancel: true})
|
||||
return gui.PopupHandler.Menu(popup.CreateMenuOptions{Title: gui.Tr.FilteringMenuTitle, Items: menuItems})
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ func (gui *Gui) getBranchNameSuggestionsFunc() func(string) []*types.Suggestion
|
||||
// Notably, unlike other suggestion functions we're not showing all the options
|
||||
// if nothing has been typed because there'll be too much to display efficiently
|
||||
func (gui *Gui) getFilePathSuggestionsFunc() func(string) []*types.Suggestion {
|
||||
_ = gui.WithWaitingStatus(gui.Tr.LcLoadingFileSuggestions, func() error {
|
||||
_ = gui.PopupHandler.WithWaitingStatus(gui.Tr.LcLoadingFileSuggestions, func() error {
|
||||
trie := patricia.NewTrie()
|
||||
// load every non-gitignored file in the repo
|
||||
ignore, err := gitignore.FromGit()
|
||||
|
@ -3,6 +3,7 @@ package gui
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
@ -13,16 +14,16 @@ func (gui *Gui) handleCreateGitFlowMenu() error {
|
||||
}
|
||||
|
||||
if !gui.Git.Flow.GitFlowEnabled() {
|
||||
return gui.createErrorPanel("You need to install git-flow and enable it in this repo to use git-flow features")
|
||||
return gui.PopupHandler.ErrorMsg("You need to install git-flow and enable it in this repo to use git-flow features")
|
||||
}
|
||||
|
||||
startHandler := func(branchType string) func() error {
|
||||
return func() error {
|
||||
title := utils.ResolvePlaceholderString(gui.Tr.NewGitFlowBranchPrompt, map[string]string{"branchType": branchType})
|
||||
|
||||
return gui.prompt(promptOpts{
|
||||
title: title,
|
||||
handleConfirm: func(name string) error {
|
||||
return gui.PopupHandler.Prompt(popup.PromptOpts{
|
||||
Title: title,
|
||||
HandleConfirm: func(name string) error {
|
||||
gui.logAction(gui.Tr.Actions.GitFlowStart)
|
||||
return gui.runSubprocessWithSuspenseAndRefresh(
|
||||
gui.Git.Flow.StartCmdObj(branchType, name),
|
||||
@ -32,39 +33,40 @@ func (gui *Gui) handleCreateGitFlowMenu() error {
|
||||
}
|
||||
}
|
||||
|
||||
menuItems := []*menuItem{
|
||||
{
|
||||
// not localising here because it's one to one with the actual git flow commands
|
||||
displayString: fmt.Sprintf("finish branch '%s'", branch.Name),
|
||||
onPress: func() error {
|
||||
return gui.gitFlowFinishBranch(branch.Name)
|
||||
return gui.PopupHandler.Menu(popup.CreateMenuOptions{
|
||||
Title: "git flow",
|
||||
Items: []*popup.MenuItem{
|
||||
{
|
||||
// not localising here because it's one to one with the actual git flow commands
|
||||
DisplayString: fmt.Sprintf("finish branch '%s'", branch.Name),
|
||||
OnPress: func() error {
|
||||
return gui.gitFlowFinishBranch(branch.Name)
|
||||
},
|
||||
},
|
||||
{
|
||||
DisplayString: "start feature",
|
||||
OnPress: startHandler("feature"),
|
||||
},
|
||||
{
|
||||
DisplayString: "start hotfix",
|
||||
OnPress: startHandler("hotfix"),
|
||||
},
|
||||
{
|
||||
DisplayString: "start bugfix",
|
||||
OnPress: startHandler("bugfix"),
|
||||
},
|
||||
{
|
||||
DisplayString: "start release",
|
||||
OnPress: startHandler("release"),
|
||||
},
|
||||
},
|
||||
{
|
||||
displayString: "start feature",
|
||||
onPress: startHandler("feature"),
|
||||
},
|
||||
{
|
||||
displayString: "start hotfix",
|
||||
onPress: startHandler("hotfix"),
|
||||
},
|
||||
{
|
||||
displayString: "start bugfix",
|
||||
onPress: startHandler("bugfix"),
|
||||
},
|
||||
{
|
||||
displayString: "start release",
|
||||
onPress: startHandler("release"),
|
||||
},
|
||||
}
|
||||
|
||||
return gui.createMenu("git flow", menuItems, createMenuOptions{})
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) gitFlowFinishBranch(branchName string) error {
|
||||
cmdObj, err := gui.Git.Flow.FinishCmdObj(branchName)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
gui.logAction(gui.Tr.Actions.GitFlowFinish)
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
@ -176,7 +177,7 @@ func (gui *Gui) scrollDownConfirmationPanel() error {
|
||||
}
|
||||
|
||||
func (gui *Gui) handleRefresh() error {
|
||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC})
|
||||
}
|
||||
|
||||
func (gui *Gui) handleMouseDownMain() error {
|
||||
@ -218,10 +219,10 @@ func (gui *Gui) fetch() (err error) {
|
||||
err = gui.Git.Sync.Fetch(git_commands.FetchOptions{})
|
||||
|
||||
if err != nil && strings.Contains(err.Error(), "exit status 128") {
|
||||
_ = gui.createErrorPanel(gui.Tr.PassUnameWrong)
|
||||
_ = gui.PopupHandler.ErrorMsg(gui.Tr.PassUnameWrong)
|
||||
}
|
||||
|
||||
_ = gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{BRANCHES, COMMITS, REMOTES, TAGS}, mode: ASYNC})
|
||||
_ = gui.refreshSidePanels(types.RefreshOptions{Scope: []types.RefreshableView{types.BRANCHES, types.COMMITS, types.REMOTES, types.TAGS}, Mode: types.ASYNC})
|
||||
|
||||
return err
|
||||
}
|
||||
@ -232,7 +233,7 @@ func (gui *Gui) backgroundFetch() (err error) {
|
||||
|
||||
err = gui.Git.Sync.Fetch(git_commands.FetchOptions{Background: true})
|
||||
|
||||
_ = gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{BRANCHES, COMMITS, REMOTES, TAGS}, mode: ASYNC})
|
||||
_ = gui.refreshSidePanels(types.RefreshOptions{Scope: []types.RefreshableView{types.BRANCHES, types.COMMITS, types.REMOTES, types.TAGS}, Mode: types.ASYNC})
|
||||
|
||||
return err
|
||||
}
|
||||
@ -247,7 +248,7 @@ func (gui *Gui) handleCopySelectedSideContextItemToClipboard() error {
|
||||
|
||||
gui.logAction(gui.Tr.Actions.CopyToClipboard)
|
||||
if err := gui.OSCommand.CopyToClipboard(itemId); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
truncatedItemId := utils.TruncateWithEllipsis(strings.Replace(itemId, "\n", " ", -1), 50)
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
// Currently there is a bug where if we switch to a subprocess from within
|
||||
@ -23,7 +24,7 @@ func (gui *Gui) withGpgHandling(cmdObj oscommands.ICmdObj, waitingStatus string,
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := gui.refreshSidePanels(refreshOptions{mode: ASYNC}); err != nil {
|
||||
if err := gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -34,7 +35,7 @@ func (gui *Gui) withGpgHandling(cmdObj oscommands.ICmdObj, waitingStatus string,
|
||||
}
|
||||
|
||||
func (gui *Gui) RunAndStream(cmdObj oscommands.ICmdObj, waitingStatus string, onSuccess func() error) error {
|
||||
return gui.WithWaitingStatus(waitingStatus, func() error {
|
||||
return gui.PopupHandler.WithWaitingStatus(waitingStatus, func() error {
|
||||
cmdObj := gui.OSCommand.Cmd.NewShell(cmdObj.ToString())
|
||||
cmdObj.AddEnvVars("TERM=dumb")
|
||||
cmdWriter := gui.getCmdWriter()
|
||||
@ -46,8 +47,8 @@ func (gui *Gui) RunAndStream(cmdObj oscommands.ICmdObj, waitingStatus string, on
|
||||
if _, err := cmd.Stdout.Write([]byte(fmt.Sprintf("%s\n", style.FgRed.Sprint(err.Error())))); err != nil {
|
||||
gui.Log.Error(err)
|
||||
}
|
||||
_ = gui.refreshSidePanels(refreshOptions{mode: ASYNC})
|
||||
return gui.surfaceError(
|
||||
_ = gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC})
|
||||
return gui.PopupHandler.Error(
|
||||
fmt.Errorf(
|
||||
gui.Tr.GitCommandFailed, gui.UserConfig.Keybinding.Universal.ExtrasMenu,
|
||||
),
|
||||
@ -60,6 +61,6 @@ func (gui *Gui) RunAndStream(cmdObj oscommands.ICmdObj, waitingStatus string, on
|
||||
}
|
||||
}
|
||||
|
||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC})
|
||||
})
|
||||
}
|
||||
|
235
pkg/gui/gui.go
235
pkg/gui/gui.go
@ -18,6 +18,7 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/common"
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/controllers"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/lbl"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/mergeconflicts"
|
||||
@ -75,11 +76,11 @@ type Gui struct {
|
||||
OSCommand *oscommands.OSCommand
|
||||
|
||||
// this is the state of the GUI for the current repo
|
||||
State *guiState
|
||||
State *GuiRepoState
|
||||
|
||||
// this is a mapping of repos to gui states, so that we can restore the original
|
||||
// gui state when returning from a subrepo
|
||||
RepoStateMap map[Repo]*guiState
|
||||
RepoStateMap map[Repo]*GuiRepoState
|
||||
Config config.AppConfigurer
|
||||
Updater *updates.Updater
|
||||
statusManager *statusManager
|
||||
@ -101,7 +102,7 @@ type Gui struct {
|
||||
|
||||
// when you enter into a submodule we'll append the superproject's path to this array
|
||||
// so that you can return to the superproject
|
||||
RepoPathStack []string
|
||||
RepoPathStack *utils.StringStack
|
||||
|
||||
// this tells us whether our views have been initially set up
|
||||
ViewsSetup bool
|
||||
@ -121,10 +122,21 @@ type Gui struct {
|
||||
|
||||
suggestionsAsyncHandler *tasks.AsyncHandler
|
||||
|
||||
PopupHandler PopupHandler
|
||||
PopupHandler popup.IPopupHandler
|
||||
|
||||
IsNewRepo bool
|
||||
|
||||
Controllers Controllers
|
||||
|
||||
// flag as to whether or not the diff view should ignore whitespace
|
||||
IgnoreWhitespaceInDiffView bool
|
||||
|
||||
// if this is true, we'll load our commits using `git log --all`
|
||||
ShowWholeGitGraph bool
|
||||
RetainOriginalDir bool
|
||||
|
||||
PrevLayout PrevLayout
|
||||
|
||||
// this is the initial dir we are in upon opening lazygit. We hold onto this
|
||||
// in case we want to restore it before quitting for users who have set up
|
||||
// the feature for changing directory upon quit.
|
||||
@ -134,6 +146,80 @@ type Gui struct {
|
||||
InitialDir string
|
||||
}
|
||||
|
||||
// we keep track of some stuff from one render to the next to see if certain
|
||||
// things have changed
|
||||
type PrevLayout struct {
|
||||
Information string
|
||||
MainWidth int
|
||||
MainHeight int
|
||||
}
|
||||
|
||||
type GuiRepoState struct {
|
||||
// the file panels (files and commit files) can render as a tree, so we have
|
||||
// managers for them which handle rendering a flat list of files in tree form
|
||||
FileTreeViewModel *filetree.FileTreeViewModel
|
||||
CommitFileTreeViewModel *filetree.CommitFileTreeViewModel
|
||||
Submodules []*models.SubmoduleConfig
|
||||
Branches []*models.Branch
|
||||
Commits []*models.Commit
|
||||
StashEntries []*models.StashEntry
|
||||
SubCommits []*models.Commit
|
||||
Remotes []*models.Remote
|
||||
RemoteBranches []*models.RemoteBranch
|
||||
Tags []*models.Tag
|
||||
// FilteredReflogCommits are the ones that appear in the reflog panel.
|
||||
// when in filtering mode we only include the ones that match the given path
|
||||
FilteredReflogCommits []*models.Commit
|
||||
// ReflogCommits are the ones used by the branches panel to obtain recency values
|
||||
// if we're not in filtering mode, CommitFiles and FilteredReflogCommits will be
|
||||
// one and the same
|
||||
ReflogCommits []*models.Commit
|
||||
|
||||
// Suggestions will sometimes appear when typing into a prompt
|
||||
Suggestions []*types.Suggestion
|
||||
MenuItems []*popup.MenuItem
|
||||
BisectInfo *git_commands.BisectInfo
|
||||
Updating bool
|
||||
Panels *panelStates
|
||||
SplitMainPanel bool
|
||||
MainContext ContextKey // used to keep the main and secondary views' contexts in sync
|
||||
|
||||
IsRefreshingFiles bool
|
||||
Searching searchingState
|
||||
Ptmx *os.File
|
||||
StartupStage StartupStage // Allows us to not load everything at once
|
||||
|
||||
Modes Modes
|
||||
|
||||
ContextManager ContextManager
|
||||
Contexts ContextTree
|
||||
ViewContextMap map[string]Context
|
||||
ViewTabContextMap map[string][]tabContext
|
||||
|
||||
// WindowViewNameMap is a mapping of windows to the current view of that window.
|
||||
// Some views move between windows for example the commitFiles view and when cycling through
|
||||
// side windows we need to know which view to give focus to for a given window
|
||||
WindowViewNameMap map[string]string
|
||||
|
||||
// tells us whether we've set up our views for the current repo. We'll need to
|
||||
// do this whenever we switch back and forth between repos to get the views
|
||||
// back in sync with the repo state
|
||||
ViewsSetup bool
|
||||
|
||||
// for displaying suggestions while typing in a file name
|
||||
FilesTrie *patricia.Trie
|
||||
|
||||
// this is the message of the last failed commit attempt
|
||||
failedCommitMessage string
|
||||
|
||||
// TODO: move these into the gui struct
|
||||
ScreenMode WindowMaximisation
|
||||
}
|
||||
|
||||
type Controllers struct {
|
||||
Submodules *controllers.SubmodulesController
|
||||
}
|
||||
|
||||
type listPanelState struct {
|
||||
SelectedLineIdx int
|
||||
}
|
||||
@ -296,75 +382,6 @@ type guiMutexes struct {
|
||||
SubprocessMutex sync.Mutex
|
||||
}
|
||||
|
||||
type guiState struct {
|
||||
// the file panels (files and commit files) can render as a tree, so we have
|
||||
// managers for them which handle rendering a flat list of files in tree form
|
||||
FileTreeViewModel *filetree.FileTreeViewModel
|
||||
CommitFileTreeViewModel *filetree.CommitFileTreeViewModel
|
||||
|
||||
Submodules []*models.SubmoduleConfig
|
||||
Branches []*models.Branch
|
||||
Commits []*models.Commit
|
||||
StashEntries []*models.StashEntry
|
||||
// Suggestions will sometimes appear when typing into a prompt
|
||||
Suggestions []*types.Suggestion
|
||||
// FilteredReflogCommits are the ones that appear in the reflog panel.
|
||||
// when in filtering mode we only include the ones that match the given path
|
||||
FilteredReflogCommits []*models.Commit
|
||||
// ReflogCommits are the ones used by the branches panel to obtain recency values
|
||||
// if we're not in filtering mode, CommitFiles and FilteredReflogCommits will be
|
||||
// one and the same
|
||||
ReflogCommits []*models.Commit
|
||||
SubCommits []*models.Commit
|
||||
Remotes []*models.Remote
|
||||
RemoteBranches []*models.RemoteBranch
|
||||
Tags []*models.Tag
|
||||
MenuItems []*menuItem
|
||||
BisectInfo *git_commands.BisectInfo
|
||||
|
||||
Updating bool
|
||||
Panels *panelStates
|
||||
SplitMainPanel bool
|
||||
MainContext ContextKey // used to keep the main and secondary views' contexts in sync
|
||||
RetainOriginalDir bool
|
||||
IsRefreshingFiles bool
|
||||
Searching searchingState
|
||||
// if this is true, we'll load our commits using `git log --all`
|
||||
ShowWholeGitGraph bool
|
||||
ScreenMode WindowMaximisation
|
||||
Ptmx *os.File
|
||||
PrevMainWidth int
|
||||
PrevMainHeight int
|
||||
OldInformation string
|
||||
StartupStage StartupStage // Allows us to not load everything at once
|
||||
|
||||
Modes Modes
|
||||
|
||||
ContextManager ContextManager
|
||||
Contexts ContextTree
|
||||
ViewContextMap map[string]Context
|
||||
ViewTabContextMap map[string][]tabContext
|
||||
|
||||
// WindowViewNameMap is a mapping of windows to the current view of that window.
|
||||
// Some views move between windows for example the commitFiles view and when cycling through
|
||||
// side windows we need to know which view to give focus to for a given window
|
||||
WindowViewNameMap map[string]string
|
||||
|
||||
// tells us whether we've set up our views for the current repo. We'll need to
|
||||
// do this whenever we switch back and forth between repos to get the views
|
||||
// back in sync with the repo state
|
||||
ViewsSetup bool
|
||||
|
||||
// flag as to whether or not the diff view should ignore whitespace
|
||||
IgnoreWhitespaceInDiffView bool
|
||||
|
||||
// for displaying suggestions while typing in a file name
|
||||
FilesTrie *patricia.Trie
|
||||
|
||||
// this is the message of the last failed commit attempt
|
||||
failedCommitMessage string
|
||||
}
|
||||
|
||||
// reuseState determines if we pull the repo state from our repo state map or
|
||||
// just re-initialize it. For now we're only re-using state when we're going
|
||||
// in and out of submodules, for the sake of having the cursor back on the submodule
|
||||
@ -400,14 +417,13 @@ func (gui *Gui) resetState(filterPath string, reuseState bool) {
|
||||
initialContext = contexts.BranchCommits
|
||||
}
|
||||
|
||||
gui.State = &guiState{
|
||||
gui.State = &GuiRepoState{
|
||||
FileTreeViewModel: filetree.NewFileTreeViewModel(make([]*models.File, 0), gui.Log, showTree),
|
||||
CommitFileTreeViewModel: filetree.NewCommitFileTreeViewModel(make([]*models.CommitFile, 0), gui.Log, showTree),
|
||||
Commits: make([]*models.Commit, 0),
|
||||
FilteredReflogCommits: make([]*models.Commit, 0),
|
||||
ReflogCommits: make([]*models.Commit, 0),
|
||||
StashEntries: make([]*models.StashEntry, 0),
|
||||
BisectInfo: gui.Git.Bisect.GetInfo(),
|
||||
Panels: &panelStates{
|
||||
// TODO: work out why some of these are -1 and some are 0. Last time I checked there was a good reason but I'm less certain now
|
||||
Files: &filePanelState{listPanelState{SelectedLineIdx: -1}},
|
||||
@ -446,6 +462,21 @@ func (gui *Gui) resetState(filterPath string, reuseState bool) {
|
||||
gui.RepoStateMap[Repo(currentDir)] = gui.State
|
||||
}
|
||||
|
||||
type guiCommon struct {
|
||||
gui *Gui
|
||||
popup.IPopupHandler
|
||||
}
|
||||
|
||||
var _ controllers.IGuiCommon = &guiCommon{}
|
||||
|
||||
func (self *guiCommon) LogAction(msg string) {
|
||||
self.gui.logAction(msg)
|
||||
}
|
||||
|
||||
func (self *guiCommon) Refresh(opts types.RefreshOptions) error {
|
||||
return self.gui.refreshSidePanels(opts)
|
||||
}
|
||||
|
||||
// for now the split view will always be on
|
||||
// NewGui builds a new gui handler
|
||||
func NewGui(
|
||||
@ -464,8 +495,8 @@ func NewGui(
|
||||
statusManager: &statusManager{},
|
||||
viewBufferManagerMap: map[string]*tasks.ViewBufferManager{},
|
||||
showRecentRepos: showRecentRepos,
|
||||
RepoPathStack: []string{},
|
||||
RepoStateMap: map[Repo]*guiState{},
|
||||
RepoPathStack: &utils.StringStack{},
|
||||
RepoStateMap: map[Repo]*GuiRepoState{},
|
||||
CmdLog: []string{},
|
||||
suggestionsAsyncHandler: tasks.NewAsyncHandler(),
|
||||
|
||||
@ -501,11 +532,31 @@ func NewGui(
|
||||
|
||||
gui.watchFilesForChanges()
|
||||
|
||||
gui.PopupHandler = &RealPopupHandler{gui: gui}
|
||||
gui.PopupHandler = popup.NewPopupHandler(
|
||||
cmn,
|
||||
gui.createPopupPanel,
|
||||
func() error { return gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC}) },
|
||||
func() error { return gui.closeConfirmationPrompt(false) },
|
||||
gui.createMenu,
|
||||
gui.withWaitingStatus,
|
||||
)
|
||||
|
||||
authors.SetCustomAuthors(gui.UserConfig.Gui.AuthorColors)
|
||||
presentation.SetCustomBranches(gui.UserConfig.Gui.BranchColors)
|
||||
|
||||
guiCommon := &guiCommon{gui: gui, IPopupHandler: gui.PopupHandler}
|
||||
controllerCommon := &controllers.ControllerCommon{IGuiCommon: guiCommon, Common: cmn}
|
||||
|
||||
gui.Controllers = Controllers{
|
||||
Submodules: controllers.NewSubmodulesController(
|
||||
controllerCommon,
|
||||
gui.enterSubmodule,
|
||||
gui.Git,
|
||||
gui.State.Submodules,
|
||||
gui.getSelectedSubmodule,
|
||||
),
|
||||
}
|
||||
|
||||
return gui, nil
|
||||
}
|
||||
|
||||
@ -601,7 +652,7 @@ func (gui *Gui) RunAndHandleError() error {
|
||||
|
||||
switch err {
|
||||
case gocui.ErrQuit:
|
||||
if gui.State.RetainOriginalDir {
|
||||
if gui.RetainOriginalDir {
|
||||
if err := gui.recordDirectory(gui.InitialDir); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -633,7 +684,7 @@ func (gui *Gui) runSubprocessWithSuspenseAndRefresh(subprocess oscommands.ICmdOb
|
||||
return err
|
||||
}
|
||||
|
||||
if err := gui.refreshSidePanels(refreshOptions{mode: ASYNC}); err != nil {
|
||||
if err := gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -654,7 +705,7 @@ func (gui *Gui) runSubprocessWithSuspense(subprocess oscommands.ICmdObj) (bool,
|
||||
}
|
||||
|
||||
if err := gui.g.Suspend(); err != nil {
|
||||
return false, gui.surfaceError(err)
|
||||
return false, gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
gui.PauseBackgroundThreads = true
|
||||
@ -668,7 +719,7 @@ func (gui *Gui) runSubprocessWithSuspense(subprocess oscommands.ICmdObj) (bool,
|
||||
gui.PauseBackgroundThreads = false
|
||||
|
||||
if cmdErr != nil {
|
||||
return false, gui.surfaceError(cmdErr)
|
||||
return false, gui.PopupHandler.Error(cmdErr)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
@ -703,7 +754,7 @@ func (gui *Gui) loadNewRepo() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := gui.refreshSidePanels(refreshOptions{mode: ASYNC}); err != nil {
|
||||
if err := gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -723,7 +774,7 @@ func (gui *Gui) showInitialPopups(tasks []func(chan struct{}) error) {
|
||||
task := task
|
||||
go utils.Safe(func() {
|
||||
if err := task(done); err != nil {
|
||||
_ = gui.surfaceError(err)
|
||||
_ = gui.PopupHandler.Error(err)
|
||||
}
|
||||
})
|
||||
|
||||
@ -740,11 +791,11 @@ func (gui *Gui) showIntroPopupMessage(done chan struct{}) error {
|
||||
return gui.Config.SaveAppState()
|
||||
}
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: "",
|
||||
prompt: gui.Tr.IntroPopupMessage,
|
||||
handleConfirm: onConfirm,
|
||||
handleClose: onConfirm,
|
||||
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: "",
|
||||
Prompt: gui.Tr.IntroPopupMessage,
|
||||
HandleConfirm: onConfirm,
|
||||
HandleClose: onConfirm,
|
||||
})
|
||||
}
|
||||
|
||||
@ -775,9 +826,9 @@ func (gui *Gui) startBackgroundFetch() {
|
||||
}
|
||||
err := gui.backgroundFetch()
|
||||
if err != nil && strings.Contains(err.Error(), "exit status 128") && isNew {
|
||||
_ = gui.ask(askOpts{
|
||||
title: gui.Tr.NoAutomaticGitFetchTitle,
|
||||
prompt: gui.Tr.NoAutomaticGitFetchBody,
|
||||
_ = gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: gui.Tr.NoAutomaticGitFetchTitle,
|
||||
Prompt: gui.Tr.NoAutomaticGitFetchBody,
|
||||
})
|
||||
} else {
|
||||
gui.goEvery(time.Second*time.Duration(userConfig.Refresher.FetchInterval), gui.stopChan, func() error {
|
||||
|
@ -9,28 +9,9 @@ import (
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/constants"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
// Binding - a keybinding mapping a key and modifier to a handler. The keypress
|
||||
// is only handled if the given view has focus, or handled globally if the view
|
||||
// is ""
|
||||
type Binding struct {
|
||||
ViewName string
|
||||
Contexts []string
|
||||
Handler func() error
|
||||
Key interface{} // FIXME: find out how to get `gocui.Key | rune`
|
||||
Modifier gocui.Modifier
|
||||
Description string
|
||||
Alternative string
|
||||
Tag string // e.g. 'navigation'. Used for grouping things in the cheatsheet
|
||||
OpensMenu bool
|
||||
}
|
||||
|
||||
// GetDisplayStrings returns the display string of a file
|
||||
func (b *Binding) GetDisplayStrings(isFocused bool) []string {
|
||||
return []string{GetKeyDisplay(b.Key), b.Description}
|
||||
}
|
||||
|
||||
var keyMapReversed = map[gocui.Key]string{
|
||||
gocui.KeyF1: "f1",
|
||||
gocui.KeyF2: "f2",
|
||||
@ -203,10 +184,10 @@ func (gui *Gui) getKey(key string) interface{} {
|
||||
}
|
||||
|
||||
// GetInitialKeybindings is a function.
|
||||
func (gui *Gui) GetInitialKeybindings() []*Binding {
|
||||
func (gui *Gui) GetInitialKeybindings() []*types.Binding {
|
||||
config := gui.UserConfig.Keybinding
|
||||
|
||||
bindings := []*Binding{
|
||||
bindings := []*types.Binding{
|
||||
{
|
||||
ViewName: "",
|
||||
Key: gui.getKey(config.Universal.Quit),
|
||||
@ -1713,57 +1694,6 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
||||
Handler: gui.handleCopySelectedSideContextItemToClipboard,
|
||||
Description: gui.Tr.LcCopySubmoduleNameToClipboard,
|
||||
},
|
||||
{
|
||||
ViewName: "files",
|
||||
Contexts: []string{string(SUBMODULES_CONTEXT_KEY)},
|
||||
Key: gui.getKey(config.Universal.GoInto),
|
||||
Handler: gui.forSubmodule(gui.handleSubmoduleEnter),
|
||||
Description: gui.Tr.LcEnterSubmodule,
|
||||
},
|
||||
{
|
||||
ViewName: "files",
|
||||
Contexts: []string{string(SUBMODULES_CONTEXT_KEY)},
|
||||
Key: gui.getKey(config.Universal.Remove),
|
||||
Handler: gui.forSubmodule(gui.removeSubmodule),
|
||||
Description: gui.Tr.LcRemoveSubmodule,
|
||||
OpensMenu: true,
|
||||
},
|
||||
{
|
||||
ViewName: "files",
|
||||
Contexts: []string{string(SUBMODULES_CONTEXT_KEY)},
|
||||
Key: gui.getKey(config.Submodules.Update),
|
||||
Handler: gui.forSubmodule(gui.handleUpdateSubmodule),
|
||||
Description: gui.Tr.LcSubmoduleUpdate,
|
||||
},
|
||||
{
|
||||
ViewName: "files",
|
||||
Contexts: []string{string(SUBMODULES_CONTEXT_KEY)},
|
||||
Key: gui.getKey(config.Universal.New),
|
||||
Handler: gui.handleAddSubmodule,
|
||||
Description: gui.Tr.LcAddSubmodule,
|
||||
},
|
||||
{
|
||||
ViewName: "files",
|
||||
Contexts: []string{string(SUBMODULES_CONTEXT_KEY)},
|
||||
Key: gui.getKey(config.Universal.Edit),
|
||||
Handler: gui.forSubmodule(gui.handleEditSubmoduleUrl),
|
||||
Description: gui.Tr.LcEditSubmoduleUrl,
|
||||
},
|
||||
{
|
||||
ViewName: "files",
|
||||
Contexts: []string{string(SUBMODULES_CONTEXT_KEY)},
|
||||
Key: gui.getKey(config.Submodules.Init),
|
||||
Handler: gui.forSubmodule(gui.handleSubmoduleInit),
|
||||
Description: gui.Tr.LcInitSubmodule,
|
||||
},
|
||||
{
|
||||
ViewName: "files",
|
||||
Contexts: []string{string(SUBMODULES_CONTEXT_KEY)},
|
||||
Key: gui.getKey(config.Submodules.BulkMenu),
|
||||
Handler: gui.handleBulkSubmoduleActionsMenu,
|
||||
Description: gui.Tr.LcViewBulkSubmoduleOptions,
|
||||
OpensMenu: true,
|
||||
},
|
||||
{
|
||||
ViewName: "files",
|
||||
Contexts: []string{string(FILES_CONTEXT_KEY)},
|
||||
@ -1841,8 +1771,28 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
||||
},
|
||||
}
|
||||
|
||||
type ContextKeybindings struct {
|
||||
contextKey ContextKey
|
||||
viewName string
|
||||
bindings []*types.Binding
|
||||
}
|
||||
|
||||
for _, contextKeybindings := range []ContextKeybindings{
|
||||
{
|
||||
contextKey: SUBMODULES_CONTEXT_KEY,
|
||||
viewName: "files",
|
||||
bindings: gui.Controllers.Submodules.Keybindings(gui.getKey, config),
|
||||
},
|
||||
} {
|
||||
for _, binding := range contextKeybindings.bindings {
|
||||
binding.Contexts = []string{string(contextKeybindings.contextKey)}
|
||||
binding.ViewName = contextKeybindings.viewName
|
||||
bindings = append(bindings, binding)
|
||||
}
|
||||
}
|
||||
|
||||
for _, viewName := range []string{"status", "branches", "files", "commits", "commitFiles", "stash", "menu"} {
|
||||
bindings = append(bindings, []*Binding{
|
||||
bindings = append(bindings, []*types.Binding{
|
||||
{ViewName: viewName, Key: gui.getKey(config.Universal.PrevBlock), Modifier: gocui.ModNone, Handler: gui.previousSideWindow},
|
||||
{ViewName: viewName, Key: gui.getKey(config.Universal.NextBlock), Modifier: gocui.ModNone, Handler: gui.nextSideWindow},
|
||||
{ViewName: viewName, Key: gui.getKey(config.Universal.PrevBlockAlt), Modifier: gocui.ModNone, Handler: gui.previousSideWindow},
|
||||
@ -1859,7 +1809,7 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
||||
log.Fatal("Jump to block keybindings cannot be set. Exactly 5 keybindings must be supplied.")
|
||||
} else {
|
||||
for i, window := range windows {
|
||||
bindings = append(bindings, &Binding{
|
||||
bindings = append(bindings, &types.Binding{
|
||||
ViewName: "",
|
||||
Key: gui.getKey(config.Universal.JumpToBlock[i]),
|
||||
Modifier: gocui.ModNone,
|
||||
@ -1868,7 +1818,7 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
||||
}
|
||||
|
||||
for viewName := range gui.State.Contexts.initialViewTabContextMap() {
|
||||
bindings = append(bindings, []*Binding{
|
||||
bindings = append(bindings, []*types.Binding{
|
||||
{
|
||||
ViewName: viewName,
|
||||
Key: gui.getKey(config.Universal.NextTab),
|
||||
|
@ -234,9 +234,9 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||
// if the commit files view is the view to be displayed for its window, we'll display it
|
||||
gui.Views.CommitFiles.Visible = gui.getViewNameForWindow(gui.State.Contexts.CommitFiles.GetWindowName()) == "commitFiles"
|
||||
|
||||
if gui.State.OldInformation != informationStr {
|
||||
if gui.PrevLayout.Information != informationStr {
|
||||
gui.setViewContent(gui.Views.Information, informationStr)
|
||||
gui.State.OldInformation = informationStr
|
||||
gui.PrevLayout.Information = informationStr
|
||||
}
|
||||
|
||||
if !gui.ViewsSetup {
|
||||
@ -277,9 +277,9 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||
gui.Views.Main.SetOnSelectItem(gui.onSelectItemWrapper(gui.handlelineByLineNavigateTo))
|
||||
|
||||
mainViewWidth, mainViewHeight := gui.Views.Main.Size()
|
||||
if mainViewWidth != gui.State.PrevMainWidth || mainViewHeight != gui.State.PrevMainHeight {
|
||||
gui.State.PrevMainWidth = mainViewWidth
|
||||
gui.State.PrevMainHeight = mainViewHeight
|
||||
if mainViewWidth != gui.PrevLayout.MainWidth || mainViewHeight != gui.PrevLayout.MainHeight {
|
||||
gui.PrevLayout.MainWidth = mainViewWidth
|
||||
gui.PrevLayout.MainHeight = mainViewHeight
|
||||
if err := gui.onResize(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ func (gui *Gui) copySelectedToClipboard() error {
|
||||
|
||||
gui.logAction(gui.Tr.Actions.CopySelectedTextToClipboard)
|
||||
if err := gui.OSCommand.CopyToClipboard(selected); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
func (gui *Gui) menuListContext() IListContext {
|
||||
@ -391,15 +392,15 @@ func (gui *Gui) getListContexts() []IListContext {
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) getListContextKeyBindings() []*Binding {
|
||||
bindings := make([]*Binding, 0)
|
||||
func (gui *Gui) getListContextKeyBindings() []*types.Binding {
|
||||
bindings := make([]*types.Binding, 0)
|
||||
|
||||
keybindingConfig := gui.UserConfig.Keybinding
|
||||
|
||||
for _, listContext := range gui.getListContexts() {
|
||||
listContext := listContext
|
||||
|
||||
bindings = append(bindings, []*Binding{
|
||||
bindings = append(bindings, []*types.Binding{
|
||||
{ViewName: listContext.GetViewName(), Tag: "navigation", Contexts: []string{string(listContext.GetKey())}, Key: gui.getKey(keybindingConfig.Universal.PrevItemAlt), Modifier: gocui.ModNone, Handler: listContext.handlePrevLine},
|
||||
{ViewName: listContext.GetViewName(), Tag: "navigation", Contexts: []string{string(listContext.GetKey())}, Key: gui.getKey(keybindingConfig.Universal.PrevItem), Modifier: gocui.ModNone, Handler: listContext.handlePrevLine},
|
||||
{ViewName: listContext.GetViewName(), Tag: "navigation", Contexts: []string{string(listContext.GetKey())}, Key: gocui.MouseWheelUp, Modifier: gocui.ModNone, Handler: listContext.handlePrevLine},
|
||||
@ -423,7 +424,7 @@ func (gui *Gui) getListContextKeyBindings() []*Binding {
|
||||
gotoBottomHandler = gui.handleGotoBottomForCommitsPanel
|
||||
}
|
||||
|
||||
bindings = append(bindings, []*Binding{
|
||||
bindings = append(bindings, []*types.Binding{
|
||||
{
|
||||
ViewName: listContext.GetViewName(),
|
||||
Contexts: []string{string(listContext.GetKey())},
|
||||
|
@ -3,31 +3,12 @@ package gui
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||
"github.com/jesseduffield/lazygit/pkg/theme"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
type menuItem struct {
|
||||
displayString string
|
||||
displayStrings []string
|
||||
onPress func() error
|
||||
// only applies when displayString is used
|
||||
opensMenu bool
|
||||
}
|
||||
|
||||
// every item in a list context needs an ID
|
||||
func (i *menuItem) ID() string {
|
||||
if i.displayString != "" {
|
||||
return i.displayString
|
||||
}
|
||||
|
||||
return strings.Join(i.displayStrings, "-")
|
||||
}
|
||||
|
||||
// specific functions
|
||||
|
||||
func (gui *Gui) getMenuOptions() map[string]string {
|
||||
keybindingConfig := gui.UserConfig.Keybinding
|
||||
|
||||
@ -42,37 +23,34 @@ func (gui *Gui) handleMenuClose() error {
|
||||
return gui.returnFromContext()
|
||||
}
|
||||
|
||||
type createMenuOptions struct {
|
||||
showCancel bool
|
||||
}
|
||||
|
||||
func (gui *Gui) createMenu(title string, items []*menuItem, createMenuOptions createMenuOptions) error {
|
||||
if createMenuOptions.showCancel {
|
||||
// note: items option is mutated by this function
|
||||
func (gui *Gui) createMenu(opts popup.CreateMenuOptions) error {
|
||||
if !opts.HideCancel {
|
||||
// this is mutative but I'm okay with that for now
|
||||
items = append(items, &menuItem{
|
||||
displayStrings: []string{gui.Tr.LcCancel},
|
||||
onPress: func() error {
|
||||
opts.Items = append(opts.Items, &popup.MenuItem{
|
||||
DisplayStrings: []string{gui.Tr.LcCancel},
|
||||
OnPress: func() error {
|
||||
return nil
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
gui.State.MenuItems = items
|
||||
gui.State.MenuItems = opts.Items
|
||||
|
||||
stringArrays := make([][]string, len(items))
|
||||
for i, item := range items {
|
||||
if item.opensMenu && item.displayStrings != nil {
|
||||
stringArrays := make([][]string, len(opts.Items))
|
||||
for i, item := range opts.Items {
|
||||
if item.OpensMenu && item.DisplayStrings != nil {
|
||||
return errors.New("Message for the developer of this app: you've set opensMenu with displaystrings on the menu panel. Bad developer!. Apologies, user")
|
||||
}
|
||||
|
||||
if item.displayStrings == nil {
|
||||
styledStr := item.displayString
|
||||
if item.opensMenu {
|
||||
if item.DisplayStrings == nil {
|
||||
styledStr := item.DisplayString
|
||||
if item.OpensMenu {
|
||||
styledStr = opensMenuStyle(styledStr)
|
||||
}
|
||||
stringArrays[i] = []string{styledStr}
|
||||
} else {
|
||||
stringArrays[i] = item.displayStrings
|
||||
stringArrays[i] = item.DisplayStrings
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,7 +58,7 @@ func (gui *Gui) createMenu(title string, items []*menuItem, createMenuOptions cr
|
||||
|
||||
x0, y0, x1, y1 := gui.getConfirmationPanelDimensions(false, list)
|
||||
menuView, _ := gui.g.SetView("menu", x0, y0, x1, y1, 0)
|
||||
menuView.Title = title
|
||||
menuView.Title = opts.Title
|
||||
menuView.FgColor = theme.GocuiDefaultTextColor
|
||||
menuView.SetOnSelectItem(gui.onSelectItemWrapper(func(selectedLine int) error {
|
||||
return nil
|
||||
@ -97,7 +75,7 @@ func (gui *Gui) onMenuPress() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := gui.State.MenuItems[selectedLine].onPress(); err != nil {
|
||||
if err := gui.State.MenuItems[selectedLine].OnPress(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/mergeconflicts"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
func (gui *Gui) handleSelectPrevConflictHunk() error {
|
||||
@ -189,7 +190,7 @@ func (gui *Gui) getMergingOptions() map[string]string {
|
||||
}
|
||||
|
||||
func (gui *Gui) handleEscapeMerge() error {
|
||||
if err := gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{FILES}}); err != nil {
|
||||
if err := gui.refreshSidePanels(types.RefreshOptions{Scope: []types.RefreshableView{types.FILES}}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -199,7 +200,7 @@ func (gui *Gui) handleEscapeMerge() error {
|
||||
func (gui *Gui) onLastConflictResolved() error {
|
||||
// as part of refreshing files, we handle the situation where a file has had
|
||||
// its merge conflicts resolved.
|
||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []RefreshableView{FILES}})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{mode: types.ASYNC, scope: []types.RefreshableView{types.FILES}})
|
||||
}
|
||||
|
||||
func (gui *Gui) resetMergeState() {
|
||||
|
@ -4,13 +4,15 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
func (gui *Gui) getBindings(v *gocui.View) []*Binding {
|
||||
func (gui *Gui) getBindings(v *gocui.View) []*types.Binding {
|
||||
var (
|
||||
bindingsGlobal, bindingsPanel []*Binding
|
||||
bindingsGlobal, bindingsPanel []*types.Binding
|
||||
)
|
||||
|
||||
bindings := append(gui.GetCustomCommandKeybindings(), gui.GetInitialKeybindings()...)
|
||||
@ -30,11 +32,11 @@ func (gui *Gui) getBindings(v *gocui.View) []*Binding {
|
||||
|
||||
// append dummy element to have a separator between
|
||||
// panel and global keybindings
|
||||
bindingsPanel = append(bindingsPanel, &Binding{})
|
||||
bindingsPanel = append(bindingsPanel, &types.Binding{})
|
||||
return append(bindingsPanel, bindingsGlobal...)
|
||||
}
|
||||
|
||||
func (gui *Gui) displayDescription(binding *Binding) string {
|
||||
func (gui *Gui) displayDescription(binding *types.Binding) string {
|
||||
if binding.OpensMenu {
|
||||
return opensMenuStyle(binding.Description)
|
||||
}
|
||||
@ -54,13 +56,13 @@ func (gui *Gui) handleCreateOptionsMenu() error {
|
||||
|
||||
bindings := gui.getBindings(view)
|
||||
|
||||
menuItems := make([]*menuItem, len(bindings))
|
||||
menuItems := make([]*popup.MenuItem, len(bindings))
|
||||
|
||||
for i, binding := range bindings {
|
||||
binding := binding // note to self, never close over loop variables
|
||||
menuItems[i] = &menuItem{
|
||||
displayStrings: []string{GetKeyDisplay(binding.Key), gui.displayDescription(binding)},
|
||||
onPress: func() error {
|
||||
menuItems[i] = &popup.MenuItem{
|
||||
DisplayStrings: []string{GetKeyDisplay(binding.Key), gui.displayDescription(binding)},
|
||||
OnPress: func() error {
|
||||
if binding.Key == nil {
|
||||
return nil
|
||||
}
|
||||
@ -72,5 +74,9 @@ func (gui *Gui) handleCreateOptionsMenu() error {
|
||||
}
|
||||
}
|
||||
|
||||
return gui.createMenu(strings.Title(gui.Tr.LcMenu), menuItems, createMenuOptions{})
|
||||
return gui.PopupHandler.Menu(popup.CreateMenuOptions{
|
||||
Title: strings.Title(gui.Tr.LcMenu),
|
||||
Items: menuItems,
|
||||
HideCancel: true,
|
||||
})
|
||||
}
|
||||
|
@ -4,41 +4,43 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
func (gui *Gui) handleCreatePatchOptionsMenu() error {
|
||||
if !gui.Git.Patch.PatchManager.Active() {
|
||||
return gui.createErrorPanel(gui.Tr.NoPatchError)
|
||||
return gui.PopupHandler.ErrorMsg(gui.Tr.NoPatchError)
|
||||
}
|
||||
|
||||
menuItems := []*menuItem{
|
||||
menuItems := []*popup.MenuItem{
|
||||
{
|
||||
displayString: "reset patch",
|
||||
onPress: gui.handleResetPatch,
|
||||
DisplayString: "reset patch",
|
||||
OnPress: gui.handleResetPatch,
|
||||
},
|
||||
{
|
||||
displayString: "apply patch",
|
||||
onPress: func() error { return gui.handleApplyPatch(false) },
|
||||
DisplayString: "apply patch",
|
||||
OnPress: func() error { return gui.handleApplyPatch(false) },
|
||||
},
|
||||
{
|
||||
displayString: "apply patch in reverse",
|
||||
onPress: func() error { return gui.handleApplyPatch(true) },
|
||||
DisplayString: "apply patch in reverse",
|
||||
OnPress: func() error { return gui.handleApplyPatch(true) },
|
||||
},
|
||||
}
|
||||
|
||||
if gui.Git.Patch.PatchManager.CanRebase && gui.Git.Status.WorkingTreeState() == enums.REBASE_MODE_NONE {
|
||||
menuItems = append(menuItems, []*menuItem{
|
||||
menuItems = append(menuItems, []*popup.MenuItem{
|
||||
{
|
||||
displayString: fmt.Sprintf("remove patch from original commit (%s)", gui.Git.Patch.PatchManager.To),
|
||||
onPress: gui.handleDeletePatchFromCommit,
|
||||
DisplayString: fmt.Sprintf("remove patch from original commit (%s)", gui.Git.Patch.PatchManager.To),
|
||||
OnPress: gui.handleDeletePatchFromCommit,
|
||||
},
|
||||
{
|
||||
displayString: "move patch out into index",
|
||||
onPress: gui.handleMovePatchIntoWorkingTree,
|
||||
DisplayString: "move patch out into index",
|
||||
OnPress: gui.handleMovePatchIntoWorkingTree,
|
||||
},
|
||||
{
|
||||
displayString: "move patch into new commit",
|
||||
onPress: gui.handlePullPatchIntoNewCommit,
|
||||
DisplayString: "move patch into new commit",
|
||||
OnPress: gui.handlePullPatchIntoNewCommit,
|
||||
},
|
||||
}...)
|
||||
|
||||
@ -49,10 +51,10 @@ func (gui *Gui) handleCreatePatchOptionsMenu() error {
|
||||
menuItems = append(
|
||||
menuItems[:1],
|
||||
append(
|
||||
[]*menuItem{
|
||||
[]*popup.MenuItem{
|
||||
{
|
||||
displayString: fmt.Sprintf("move patch to selected commit (%s)", selectedCommit.Sha),
|
||||
onPress: gui.handleMovePatchToSelectedCommit,
|
||||
DisplayString: fmt.Sprintf("move patch to selected commit (%s)", selectedCommit.Sha),
|
||||
OnPress: gui.handleMovePatchToSelectedCommit,
|
||||
},
|
||||
}, menuItems[1:]...,
|
||||
)...,
|
||||
@ -61,7 +63,7 @@ func (gui *Gui) handleCreatePatchOptionsMenu() error {
|
||||
}
|
||||
}
|
||||
|
||||
return gui.createMenu(gui.Tr.PatchOptionsTitle, menuItems, createMenuOptions{showCancel: true})
|
||||
return gui.PopupHandler.Menu(popup.CreateMenuOptions{Title: gui.Tr.PatchOptionsTitle, Items: menuItems})
|
||||
}
|
||||
|
||||
func (gui *Gui) getPatchCommitIndex() int {
|
||||
@ -75,7 +77,7 @@ func (gui *Gui) getPatchCommitIndex() int {
|
||||
|
||||
func (gui *Gui) validateNormalWorkingTreeState() (bool, error) {
|
||||
if gui.Git.Status.WorkingTreeState() != enums.REBASE_MODE_NONE {
|
||||
return false, gui.createErrorPanel(gui.Tr.CantPatchWhileRebasingError)
|
||||
return false, gui.PopupHandler.ErrorMsg(gui.Tr.CantPatchWhileRebasingError)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
@ -96,7 +98,7 @@ func (gui *Gui) handleDeletePatchFromCommit() error {
|
||||
return err
|
||||
}
|
||||
|
||||
return gui.WithWaitingStatus(gui.Tr.RebasingStatus, func() error {
|
||||
return gui.PopupHandler.WithWaitingStatus(gui.Tr.RebasingStatus, func() error {
|
||||
commitIndex := gui.getPatchCommitIndex()
|
||||
gui.logAction(gui.Tr.Actions.RemovePatchFromCommit)
|
||||
err := gui.Git.Patch.DeletePatchesFromCommit(gui.State.Commits, commitIndex)
|
||||
@ -113,7 +115,7 @@ func (gui *Gui) handleMovePatchToSelectedCommit() error {
|
||||
return err
|
||||
}
|
||||
|
||||
return gui.WithWaitingStatus(gui.Tr.RebasingStatus, func() error {
|
||||
return gui.PopupHandler.WithWaitingStatus(gui.Tr.RebasingStatus, func() error {
|
||||
commitIndex := gui.getPatchCommitIndex()
|
||||
gui.logAction(gui.Tr.Actions.MovePatchToSelectedCommit)
|
||||
err := gui.Git.Patch.MovePatchToSelectedCommit(gui.State.Commits, commitIndex, gui.State.Panels.Commits.SelectedLineIdx)
|
||||
@ -131,7 +133,7 @@ func (gui *Gui) handleMovePatchIntoWorkingTree() error {
|
||||
}
|
||||
|
||||
pull := func(stash bool) error {
|
||||
return gui.WithWaitingStatus(gui.Tr.RebasingStatus, func() error {
|
||||
return gui.PopupHandler.WithWaitingStatus(gui.Tr.RebasingStatus, func() error {
|
||||
commitIndex := gui.getPatchCommitIndex()
|
||||
gui.logAction(gui.Tr.Actions.MovePatchIntoIndex)
|
||||
err := gui.Git.Patch.MovePatchIntoIndex(gui.State.Commits, commitIndex, stash)
|
||||
@ -140,10 +142,10 @@ func (gui *Gui) handleMovePatchIntoWorkingTree() error {
|
||||
}
|
||||
|
||||
if len(gui.trackedFiles()) > 0 {
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.MustStashTitle,
|
||||
prompt: gui.Tr.MustStashWarning,
|
||||
handleConfirm: func() error {
|
||||
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: gui.Tr.MustStashTitle,
|
||||
Prompt: gui.Tr.MustStashWarning,
|
||||
HandleConfirm: func() error {
|
||||
return pull(true)
|
||||
},
|
||||
})
|
||||
@ -161,7 +163,7 @@ func (gui *Gui) handlePullPatchIntoNewCommit() error {
|
||||
return err
|
||||
}
|
||||
|
||||
return gui.WithWaitingStatus(gui.Tr.RebasingStatus, func() error {
|
||||
return gui.PopupHandler.WithWaitingStatus(gui.Tr.RebasingStatus, func() error {
|
||||
commitIndex := gui.getPatchCommitIndex()
|
||||
gui.logAction(gui.Tr.Actions.MovePatchIntoNewCommit)
|
||||
err := gui.Git.Patch.PullPatchIntoNewCommit(gui.State.Commits, commitIndex)
|
||||
@ -180,9 +182,9 @@ func (gui *Gui) handleApplyPatch(reverse bool) error {
|
||||
}
|
||||
gui.logAction(action)
|
||||
if err := gui.Git.Patch.PatchManager.ApplyPatches(reverse); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC})
|
||||
}
|
||||
|
||||
func (gui *Gui) handleResetPatch() error {
|
||||
|
223
pkg/gui/popup/popup_handler.go
Normal file
223
pkg/gui/popup/popup_handler.go
Normal file
@ -0,0 +1,223 @@
|
||||
package popup
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/common"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
type IPopupHandler interface {
|
||||
ErrorMsg(message string) error
|
||||
Error(err error) error
|
||||
Ask(opts AskOpts) error
|
||||
Prompt(opts PromptOpts) error
|
||||
WithLoaderPanel(message string, f func() error) error
|
||||
WithWaitingStatus(message string, f func() error) error
|
||||
Menu(opts CreateMenuOptions) error
|
||||
}
|
||||
|
||||
type CreateMenuOptions struct {
|
||||
Title string
|
||||
Items []*MenuItem
|
||||
HideCancel bool
|
||||
}
|
||||
|
||||
type CreatePopupPanelOpts struct {
|
||||
HasLoader bool
|
||||
Editable bool
|
||||
Title string
|
||||
Prompt string
|
||||
HandleConfirm func() error
|
||||
HandleConfirmPrompt func(string) error
|
||||
HandleClose func() error
|
||||
|
||||
// when HandlersManageFocus is true, do not return from the confirmation context automatically. It's expected that the handlers will manage focus, whether that means switching to another context, or manually returning the context.
|
||||
HandlersManageFocus bool
|
||||
|
||||
FindSuggestionsFunc func(string) []*types.Suggestion
|
||||
}
|
||||
|
||||
type AskOpts struct {
|
||||
Title string
|
||||
Prompt string
|
||||
HandleConfirm func() error
|
||||
HandleClose func() error
|
||||
HandlersManageFocus bool
|
||||
}
|
||||
|
||||
type PromptOpts struct {
|
||||
Title string
|
||||
InitialContent string
|
||||
FindSuggestionsFunc func(string) []*types.Suggestion
|
||||
HandleConfirm func(string) error
|
||||
}
|
||||
|
||||
type MenuItem struct {
|
||||
DisplayString string
|
||||
DisplayStrings []string
|
||||
OnPress func() error
|
||||
// only applies when displayString is used
|
||||
OpensMenu bool
|
||||
}
|
||||
|
||||
type RealPopupHandler struct {
|
||||
*common.Common
|
||||
index int
|
||||
sync.Mutex
|
||||
createPopupPanelFn func(CreatePopupPanelOpts) error
|
||||
onErrorFn func() error
|
||||
closePopupFn func() error
|
||||
createMenuFn func(CreateMenuOptions) error
|
||||
withWaitingStatusFn func(message string, f func() error) error
|
||||
}
|
||||
|
||||
var _ IPopupHandler = &RealPopupHandler{}
|
||||
|
||||
func NewPopupHandler(
|
||||
common *common.Common,
|
||||
createPopupPanelFn func(CreatePopupPanelOpts) error,
|
||||
onErrorFn func() error,
|
||||
closePopupFn func() error,
|
||||
createMenuFn func(CreateMenuOptions) error,
|
||||
withWaitingStatusFn func(message string, f func() error) error,
|
||||
) *RealPopupHandler {
|
||||
return &RealPopupHandler{
|
||||
Common: common,
|
||||
index: 0,
|
||||
createPopupPanelFn: createPopupPanelFn,
|
||||
onErrorFn: onErrorFn,
|
||||
closePopupFn: closePopupFn,
|
||||
createMenuFn: createMenuFn,
|
||||
withWaitingStatusFn: withWaitingStatusFn,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *RealPopupHandler) Menu(opts CreateMenuOptions) error {
|
||||
return self.createMenuFn(opts)
|
||||
}
|
||||
|
||||
func (self *RealPopupHandler) WithWaitingStatus(message string, f func() error) error {
|
||||
return self.withWaitingStatusFn(message, f)
|
||||
}
|
||||
|
||||
func (self *RealPopupHandler) Error(err error) error {
|
||||
if err == gocui.ErrQuit {
|
||||
return err
|
||||
}
|
||||
|
||||
return self.ErrorMsg(err.Error())
|
||||
}
|
||||
|
||||
func (self *RealPopupHandler) ErrorMsg(message string) error {
|
||||
self.Lock()
|
||||
self.index++
|
||||
self.Unlock()
|
||||
|
||||
coloredMessage := style.FgRed.Sprint(strings.TrimSpace(message))
|
||||
if err := self.onErrorFn(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return self.Ask(AskOpts{
|
||||
Title: self.Tr.Error,
|
||||
Prompt: coloredMessage,
|
||||
})
|
||||
}
|
||||
|
||||
func (self *RealPopupHandler) Ask(opts AskOpts) error {
|
||||
self.Lock()
|
||||
self.index++
|
||||
self.Unlock()
|
||||
|
||||
return self.createPopupPanelFn(CreatePopupPanelOpts{
|
||||
Title: opts.Title,
|
||||
Prompt: opts.Prompt,
|
||||
HandleConfirm: opts.HandleConfirm,
|
||||
HandleClose: opts.HandleClose,
|
||||
HandlersManageFocus: opts.HandlersManageFocus,
|
||||
})
|
||||
}
|
||||
|
||||
func (self *RealPopupHandler) Prompt(opts PromptOpts) error {
|
||||
self.Lock()
|
||||
self.index++
|
||||
self.Unlock()
|
||||
|
||||
return self.createPopupPanelFn(CreatePopupPanelOpts{
|
||||
Title: opts.Title,
|
||||
Prompt: opts.InitialContent,
|
||||
Editable: true,
|
||||
HandleConfirmPrompt: opts.HandleConfirm,
|
||||
FindSuggestionsFunc: opts.FindSuggestionsFunc,
|
||||
})
|
||||
}
|
||||
|
||||
func (self *RealPopupHandler) WithLoaderPanel(message string, f func() error) error {
|
||||
index := 0
|
||||
self.Lock()
|
||||
self.index++
|
||||
index = self.index
|
||||
self.Unlock()
|
||||
|
||||
err := self.createPopupPanelFn(CreatePopupPanelOpts{
|
||||
Prompt: message,
|
||||
HasLoader: true,
|
||||
})
|
||||
if err != nil {
|
||||
self.Log.Error(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
go utils.Safe(func() {
|
||||
if err := f(); err != nil {
|
||||
self.Log.Error(err)
|
||||
}
|
||||
|
||||
self.Lock()
|
||||
if index == self.index {
|
||||
_ = self.closePopupFn()
|
||||
}
|
||||
self.Unlock()
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type TestPopupHandler struct {
|
||||
OnErrorMsg func(message string) error
|
||||
OnAsk func(opts AskOpts) error
|
||||
OnPrompt func(opts PromptOpts) error
|
||||
}
|
||||
|
||||
func (self *TestPopupHandler) Error(err error) error {
|
||||
return self.ErrorMsg(err.Error())
|
||||
}
|
||||
|
||||
func (self *TestPopupHandler) ErrorMsg(message string) error {
|
||||
return self.OnErrorMsg(message)
|
||||
}
|
||||
|
||||
func (self *TestPopupHandler) Ask(opts AskOpts) error {
|
||||
return self.OnAsk(opts)
|
||||
}
|
||||
|
||||
func (self *TestPopupHandler) Prompt(opts PromptOpts) error {
|
||||
return self.OnPrompt(opts)
|
||||
}
|
||||
|
||||
func (self *TestPopupHandler) WithLoaderPanel(message string, f func() error) error {
|
||||
return f()
|
||||
}
|
||||
|
||||
func (self *TestPopupHandler) WithWaitingStatus(message string, f func() error) error {
|
||||
return f()
|
||||
}
|
||||
|
||||
func (self *TestPopupHandler) Menu(opts CreateMenuOptions) error {
|
||||
panic("not yet implemented")
|
||||
}
|
@ -1,87 +0,0 @@
|
||||
package gui
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
)
|
||||
|
||||
type PopupHandler interface {
|
||||
Error(message string) error
|
||||
Ask(opts askOpts) error
|
||||
Prompt(opts promptOpts) error
|
||||
Loader(message string) error
|
||||
}
|
||||
|
||||
type RealPopupHandler struct {
|
||||
gui *Gui
|
||||
}
|
||||
|
||||
func (self *RealPopupHandler) Error(message string) error {
|
||||
gui := self.gui
|
||||
|
||||
coloredMessage := style.FgRed.Sprint(strings.TrimSpace(message))
|
||||
if err := gui.refreshSidePanels(refreshOptions{mode: ASYNC}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return self.Ask(askOpts{
|
||||
title: gui.Tr.Error,
|
||||
prompt: coloredMessage,
|
||||
})
|
||||
}
|
||||
|
||||
func (self *RealPopupHandler) Ask(opts askOpts) error {
|
||||
gui := self.gui
|
||||
|
||||
return gui.createPopupPanel(createPopupPanelOpts{
|
||||
title: opts.title,
|
||||
prompt: opts.prompt,
|
||||
handleConfirm: opts.handleConfirm,
|
||||
handleClose: opts.handleClose,
|
||||
handlersManageFocus: opts.handlersManageFocus,
|
||||
})
|
||||
}
|
||||
|
||||
func (self *RealPopupHandler) Prompt(opts promptOpts) error {
|
||||
gui := self.gui
|
||||
|
||||
return gui.createPopupPanel(createPopupPanelOpts{
|
||||
title: opts.title,
|
||||
prompt: opts.initialContent,
|
||||
editable: true,
|
||||
handleConfirmPrompt: opts.handleConfirm,
|
||||
findSuggestionsFunc: opts.findSuggestionsFunc,
|
||||
})
|
||||
}
|
||||
|
||||
func (self *RealPopupHandler) Loader(message string) error {
|
||||
gui := self.gui
|
||||
|
||||
return gui.createPopupPanel(createPopupPanelOpts{
|
||||
prompt: message,
|
||||
hasLoader: true,
|
||||
})
|
||||
}
|
||||
|
||||
type TestPopupHandler struct {
|
||||
onError func(message string) error
|
||||
onAsk func(opts askOpts) error
|
||||
onPrompt func(opts promptOpts) error
|
||||
}
|
||||
|
||||
func (self *TestPopupHandler) Error(message string) error {
|
||||
return self.onError(message)
|
||||
}
|
||||
|
||||
func (self *TestPopupHandler) Ask(opts askOpts) error {
|
||||
return self.onAsk(opts)
|
||||
}
|
||||
|
||||
func (self *TestPopupHandler) Prompt(opts promptOpts) error {
|
||||
return self.onPrompt(opts)
|
||||
}
|
||||
|
||||
func (self *TestPopupHandler) Loader(message string) error {
|
||||
return nil
|
||||
}
|
@ -5,30 +5,31 @@ import (
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/hosting_service"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||
)
|
||||
|
||||
func (gui *Gui) createPullRequestMenu(selectedBranch *models.Branch, checkedOutBranch *models.Branch) error {
|
||||
menuItems := make([]*menuItem, 0, 4)
|
||||
menuItems := make([]*popup.MenuItem, 0, 4)
|
||||
|
||||
fromToDisplayStrings := func(from string, to string) []string {
|
||||
return []string{fmt.Sprintf("%s → %s", from, to)}
|
||||
}
|
||||
|
||||
menuItemsForBranch := func(branch *models.Branch) []*menuItem {
|
||||
return []*menuItem{
|
||||
menuItemsForBranch := func(branch *models.Branch) []*popup.MenuItem {
|
||||
return []*popup.MenuItem{
|
||||
{
|
||||
displayStrings: fromToDisplayStrings(branch.Name, gui.Tr.LcDefaultBranch),
|
||||
onPress: func() error {
|
||||
DisplayStrings: fromToDisplayStrings(branch.Name, gui.Tr.LcDefaultBranch),
|
||||
OnPress: func() error {
|
||||
return gui.createPullRequest(branch.Name, "")
|
||||
},
|
||||
},
|
||||
{
|
||||
displayStrings: fromToDisplayStrings(branch.Name, gui.Tr.LcSelectBranch),
|
||||
onPress: func() error {
|
||||
return gui.prompt(promptOpts{
|
||||
title: branch.Name + " →",
|
||||
findSuggestionsFunc: gui.getBranchNameSuggestionsFunc(),
|
||||
handleConfirm: func(targetBranchName string) error {
|
||||
DisplayStrings: fromToDisplayStrings(branch.Name, gui.Tr.LcSelectBranch),
|
||||
OnPress: func() error {
|
||||
return gui.PopupHandler.Prompt(popup.PromptOpts{
|
||||
Title: branch.Name + " →",
|
||||
FindSuggestionsFunc: gui.getBranchNameSuggestionsFunc(),
|
||||
HandleConfirm: func(targetBranchName string) error {
|
||||
return gui.createPullRequest(branch.Name, targetBranchName)
|
||||
}},
|
||||
)
|
||||
@ -39,9 +40,9 @@ func (gui *Gui) createPullRequestMenu(selectedBranch *models.Branch, checkedOutB
|
||||
|
||||
if selectedBranch != checkedOutBranch {
|
||||
menuItems = append(menuItems,
|
||||
&menuItem{
|
||||
displayStrings: fromToDisplayStrings(checkedOutBranch.Name, selectedBranch.Name),
|
||||
onPress: func() error {
|
||||
&popup.MenuItem{
|
||||
DisplayStrings: fromToDisplayStrings(checkedOutBranch.Name, selectedBranch.Name),
|
||||
OnPress: func() error {
|
||||
return gui.createPullRequest(checkedOutBranch.Name, selectedBranch.Name)
|
||||
},
|
||||
},
|
||||
@ -51,20 +52,20 @@ func (gui *Gui) createPullRequestMenu(selectedBranch *models.Branch, checkedOutB
|
||||
|
||||
menuItems = append(menuItems, menuItemsForBranch(selectedBranch)...)
|
||||
|
||||
return gui.createMenu(fmt.Sprintf(gui.Tr.CreatePullRequestOptions), menuItems, createMenuOptions{showCancel: true})
|
||||
return gui.PopupHandler.Menu(popup.CreateMenuOptions{Title: fmt.Sprintf(gui.Tr.CreatePullRequestOptions), Items: menuItems})
|
||||
}
|
||||
|
||||
func (gui *Gui) createPullRequest(from string, to string) error {
|
||||
hostingServiceMgr := gui.getHostingServiceMgr()
|
||||
url, err := hostingServiceMgr.GetPullRequestURL(from, to)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
gui.logAction(gui.Tr.Actions.OpenPullRequest)
|
||||
|
||||
if err := gui.OSCommand.OpenLink(url); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"os"
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||
)
|
||||
|
||||
// when a user runs lazygit with the LAZYGIT_NEW_DIR_FILE env variable defined
|
||||
@ -28,12 +29,12 @@ func (gui *Gui) recordDirectory(dirName string) error {
|
||||
}
|
||||
|
||||
func (gui *Gui) handleQuitWithoutChangingDirectory() error {
|
||||
gui.State.RetainOriginalDir = true
|
||||
gui.RetainOriginalDir = true
|
||||
return gui.quit()
|
||||
}
|
||||
|
||||
func (gui *Gui) handleQuit() error {
|
||||
gui.State.RetainOriginalDir = false
|
||||
gui.RetainOriginalDir = false
|
||||
return gui.quit()
|
||||
}
|
||||
|
||||
@ -53,12 +54,8 @@ func (gui *Gui) handleTopLevelReturn() error {
|
||||
}
|
||||
|
||||
repoPathStack := gui.RepoPathStack
|
||||
if len(repoPathStack) > 0 {
|
||||
n := len(repoPathStack) - 1
|
||||
|
||||
path := repoPathStack[n]
|
||||
|
||||
gui.RepoPathStack = repoPathStack[:n]
|
||||
if !repoPathStack.IsEmpty() {
|
||||
path := repoPathStack.Pop()
|
||||
|
||||
return gui.dispatchSwitchToRepo(path, true)
|
||||
}
|
||||
@ -76,10 +73,10 @@ func (gui *Gui) quit() error {
|
||||
}
|
||||
|
||||
if gui.UserConfig.ConfirmOnQuit {
|
||||
return gui.ask(askOpts{
|
||||
title: "",
|
||||
prompt: gui.Tr.ConfirmQuit,
|
||||
handleConfirm: func() error {
|
||||
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: "",
|
||||
Prompt: gui.Tr.ConfirmQuit,
|
||||
HandleConfirm: func() error {
|
||||
return gocui.ErrQuit
|
||||
},
|
||||
})
|
||||
|
@ -5,6 +5,8 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
type RebaseOption string
|
||||
@ -22,13 +24,13 @@ func (gui *Gui) handleCreateRebaseOptionsMenu() error {
|
||||
options = append(options, REBASE_OPTION_SKIP)
|
||||
}
|
||||
|
||||
menuItems := make([]*menuItem, len(options))
|
||||
menuItems := make([]*popup.MenuItem, len(options))
|
||||
for i, option := range options {
|
||||
// note to self. Never, EVER, close over loop variables in a function
|
||||
option := option
|
||||
menuItems[i] = &menuItem{
|
||||
displayString: option,
|
||||
onPress: func() error {
|
||||
menuItems[i] = &popup.MenuItem{
|
||||
DisplayString: option,
|
||||
OnPress: func() error {
|
||||
return gui.genericMergeCommand(option)
|
||||
},
|
||||
}
|
||||
@ -41,14 +43,14 @@ func (gui *Gui) handleCreateRebaseOptionsMenu() error {
|
||||
title = gui.Tr.RebaseOptionsTitle
|
||||
}
|
||||
|
||||
return gui.createMenu(title, menuItems, createMenuOptions{showCancel: true})
|
||||
return gui.PopupHandler.Menu(popup.CreateMenuOptions{Title: title, Items: menuItems})
|
||||
}
|
||||
|
||||
func (gui *Gui) genericMergeCommand(command string) error {
|
||||
status := gui.Git.Status.WorkingTreeState()
|
||||
|
||||
if status != enums.REBASE_MODE_MERGING && status != enums.REBASE_MODE_REBASING {
|
||||
return gui.createErrorPanel(gui.Tr.NotMergingOrRebasing)
|
||||
return gui.PopupHandler.ErrorMsg(gui.Tr.NotMergingOrRebasing)
|
||||
}
|
||||
|
||||
gui.logAction(fmt.Sprintf("Merge/Rebase: %s", command))
|
||||
@ -97,7 +99,7 @@ func isMergeConflictErr(errStr string) bool {
|
||||
}
|
||||
|
||||
func (gui *Gui) handleGenericMergeCommandResult(result error) error {
|
||||
if err := gui.refreshSidePanels(refreshOptions{mode: ASYNC}); err != nil {
|
||||
if err := gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC}); err != nil {
|
||||
return err
|
||||
}
|
||||
if result == nil {
|
||||
@ -110,14 +112,14 @@ func (gui *Gui) handleGenericMergeCommandResult(result error) error {
|
||||
// assume in this case that we're already done
|
||||
return nil
|
||||
} else if isMergeConflictErr(result.Error()) {
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.FoundConflictsTitle,
|
||||
prompt: gui.Tr.FoundConflicts,
|
||||
handlersManageFocus: true,
|
||||
handleConfirm: func() error {
|
||||
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: gui.Tr.FoundConflictsTitle,
|
||||
Prompt: gui.Tr.FoundConflicts,
|
||||
HandlersManageFocus: true,
|
||||
HandleConfirm: func() error {
|
||||
return gui.pushContext(gui.State.Contexts.Files)
|
||||
},
|
||||
handleClose: func() error {
|
||||
HandleClose: func() error {
|
||||
if err := gui.returnFromContext(); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -126,17 +128,17 @@ func (gui *Gui) handleGenericMergeCommandResult(result error) error {
|
||||
},
|
||||
})
|
||||
} else {
|
||||
return gui.createErrorPanel(result.Error())
|
||||
return gui.PopupHandler.ErrorMsg(result.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) abortMergeOrRebaseWithConfirm() error {
|
||||
// prompt user to confirm that they want to abort, then do it
|
||||
mode := gui.workingTreeStateNoun()
|
||||
return gui.ask(askOpts{
|
||||
title: fmt.Sprintf(gui.Tr.AbortTitle, mode),
|
||||
prompt: fmt.Sprintf(gui.Tr.AbortPrompt, mode),
|
||||
handleConfirm: func() error {
|
||||
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: fmt.Sprintf(gui.Tr.AbortTitle, mode),
|
||||
Prompt: fmt.Sprintf(gui.Tr.AbortPrompt, mode),
|
||||
HandleConfirm: func() error {
|
||||
return gui.genericMergeCommand(REBASE_OPTION_ABORT)
|
||||
},
|
||||
})
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_config"
|
||||
"github.com/jesseduffield/lazygit/pkg/env"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
@ -16,24 +17,24 @@ func (gui *Gui) handleCreateRecentReposMenu() error {
|
||||
reposCount := utils.Min(len(recentRepoPaths), 20)
|
||||
|
||||
// we won't show the current repo hence the -1
|
||||
menuItems := make([]*menuItem, reposCount-1)
|
||||
menuItems := make([]*popup.MenuItem, reposCount-1)
|
||||
for i, path := range recentRepoPaths[1:reposCount] {
|
||||
path := path // cos we're closing over the loop variable
|
||||
menuItems[i] = &menuItem{
|
||||
displayStrings: []string{
|
||||
menuItems[i] = &popup.MenuItem{
|
||||
DisplayStrings: []string{
|
||||
filepath.Base(path),
|
||||
style.FgMagenta.Sprint(path),
|
||||
},
|
||||
onPress: func() error {
|
||||
OnPress: func() error {
|
||||
// if we were in a submodule, we want to forget about that stack of repos
|
||||
// so that hitting escape in the new repo does nothing
|
||||
gui.RepoPathStack = []string{}
|
||||
gui.RepoPathStack.Clear()
|
||||
return gui.dispatchSwitchToRepo(path, false)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return gui.createMenu(gui.Tr.RecentRepos, menuItems, createMenuOptions{showCancel: true})
|
||||
return gui.PopupHandler.Menu(popup.CreateMenuOptions{Title: gui.Tr.RecentRepos, Items: menuItems})
|
||||
}
|
||||
|
||||
func (gui *Gui) handleShowAllBranchLogs() error {
|
||||
@ -57,7 +58,7 @@ func (gui *Gui) dispatchSwitchToRepo(path string, reuse bool) error {
|
||||
|
||||
if err := os.Chdir(path); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return gui.createErrorPanel(gui.Tr.ErrRepositoryMovedOrDeleted)
|
||||
return gui.PopupHandler.ErrorMsg(gui.Tr.ErrRepositoryMovedOrDeleted)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package gui
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||
)
|
||||
|
||||
// list panel functions
|
||||
@ -55,7 +56,7 @@ func (gui *Gui) refreshReflogCommits() error {
|
||||
commits, onlyObtainedNewReflogCommits, err := gui.Git.Loaders.ReflogCommits.
|
||||
GetReflogCommits(lastReflogCommit, filterPath)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
if onlyObtainedNewReflogCommits {
|
||||
@ -87,10 +88,10 @@ func (gui *Gui) handleCheckoutReflogCommit() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := gui.ask(askOpts{
|
||||
title: gui.Tr.LcCheckoutCommit,
|
||||
prompt: gui.Tr.SureCheckoutThisCommit,
|
||||
handleConfirm: func() error {
|
||||
err := gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: gui.Tr.LcCheckoutCommit,
|
||||
Prompt: gui.Tr.SureCheckoutThisCommit,
|
||||
HandleConfirm: func() error {
|
||||
gui.logAction(gui.Tr.Actions.CheckoutReflogCommit)
|
||||
return gui.handleCheckoutRef(commit.Sha, handleCheckoutRefOptions{})
|
||||
},
|
||||
|
@ -4,6 +4,8 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
@ -52,16 +54,18 @@ func (gui *Gui) handleDeleteRemoteBranch() error {
|
||||
}
|
||||
message := fmt.Sprintf("%s '%s'?", gui.Tr.DeleteRemoteBranchMessage, remoteBranch.FullName())
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.DeleteRemoteBranch,
|
||||
prompt: message,
|
||||
handleConfirm: func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.DeletingStatus, func() error {
|
||||
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: gui.Tr.DeleteRemoteBranch,
|
||||
Prompt: message,
|
||||
HandleConfirm: func() error {
|
||||
return gui.PopupHandler.WithWaitingStatus(gui.Tr.DeletingStatus, func() error {
|
||||
gui.logAction(gui.Tr.Actions.DeleteRemoteBranch)
|
||||
err := gui.Git.Remote.DeleteRemoteBranch(remoteBranch.RemoteName, remoteBranch.Name)
|
||||
gui.handleCredentialsPopup(err)
|
||||
if err != nil {
|
||||
_ = gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
return gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{BRANCHES, REMOTES}})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Scope: []types.RefreshableView{types.BRANCHES, types.REMOTES}})
|
||||
})
|
||||
},
|
||||
})
|
||||
@ -84,16 +88,16 @@ func (gui *Gui) handleSetBranchUpstream() error {
|
||||
},
|
||||
)
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.SetUpstreamTitle,
|
||||
prompt: message,
|
||||
handleConfirm: func() error {
|
||||
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: gui.Tr.SetUpstreamTitle,
|
||||
Prompt: message,
|
||||
HandleConfirm: func() error {
|
||||
gui.logAction(gui.Tr.Actions.SetBranchUpstream)
|
||||
if err := gui.Git.Branch.SetUpstream(selectedBranch.RemoteName, selectedBranch.Name, checkedOutBranch.Name); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
return gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{BRANCHES, REMOTES}})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Scope: []types.RefreshableView{types.BRANCHES, types.REMOTES}})
|
||||
},
|
||||
})
|
||||
}
|
||||
|
@ -5,7 +5,9 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
@ -42,7 +44,7 @@ func (gui *Gui) refreshRemotes() error {
|
||||
|
||||
remotes, err := gui.Git.Loaders.Remotes.GetRemotes()
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
gui.State.Remotes = remotes
|
||||
@ -79,17 +81,17 @@ func (gui *Gui) handleRemoteEnter() error {
|
||||
}
|
||||
|
||||
func (gui *Gui) handleAddRemote() error {
|
||||
return gui.prompt(promptOpts{
|
||||
title: gui.Tr.LcNewRemoteName,
|
||||
handleConfirm: func(remoteName string) error {
|
||||
return gui.prompt(promptOpts{
|
||||
title: gui.Tr.LcNewRemoteUrl,
|
||||
handleConfirm: func(remoteUrl string) error {
|
||||
return gui.PopupHandler.Prompt(popup.PromptOpts{
|
||||
Title: gui.Tr.LcNewRemoteName,
|
||||
HandleConfirm: func(remoteName string) error {
|
||||
return gui.PopupHandler.Prompt(popup.PromptOpts{
|
||||
Title: gui.Tr.LcNewRemoteUrl,
|
||||
HandleConfirm: func(remoteUrl string) error {
|
||||
gui.logAction(gui.Tr.Actions.AddRemote)
|
||||
if err := gui.Git.Remote.AddRemote(remoteName, remoteUrl); err != nil {
|
||||
return err
|
||||
}
|
||||
return gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{REMOTES}})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Scope: []types.RefreshableView{types.REMOTES}})
|
||||
},
|
||||
})
|
||||
},
|
||||
@ -103,16 +105,16 @@ func (gui *Gui) handleRemoveRemote() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.LcRemoveRemote,
|
||||
prompt: gui.Tr.LcRemoveRemotePrompt + " '" + remote.Name + "'?",
|
||||
handleConfirm: func() error {
|
||||
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: gui.Tr.LcRemoveRemote,
|
||||
Prompt: gui.Tr.LcRemoveRemotePrompt + " '" + remote.Name + "'?",
|
||||
HandleConfirm: func() error {
|
||||
gui.logAction(gui.Tr.Actions.RemoveRemote)
|
||||
if err := gui.Git.Remote.RemoveRemote(remote.Name); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
return gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{BRANCHES, REMOTES}})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Scope: []types.RefreshableView{types.BRANCHES, types.REMOTES}})
|
||||
},
|
||||
})
|
||||
}
|
||||
@ -130,14 +132,14 @@ func (gui *Gui) handleEditRemote() error {
|
||||
},
|
||||
)
|
||||
|
||||
return gui.prompt(promptOpts{
|
||||
title: editNameMessage,
|
||||
initialContent: remote.Name,
|
||||
handleConfirm: func(updatedRemoteName string) error {
|
||||
return gui.PopupHandler.Prompt(popup.PromptOpts{
|
||||
Title: editNameMessage,
|
||||
InitialContent: remote.Name,
|
||||
HandleConfirm: func(updatedRemoteName string) error {
|
||||
if updatedRemoteName != remote.Name {
|
||||
gui.logAction(gui.Tr.Actions.UpdateRemote)
|
||||
if err := gui.Git.Remote.RenameRemote(remote.Name, updatedRemoteName); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -154,15 +156,15 @@ func (gui *Gui) handleEditRemote() error {
|
||||
url = urls[0]
|
||||
}
|
||||
|
||||
return gui.prompt(promptOpts{
|
||||
title: editUrlMessage,
|
||||
initialContent: url,
|
||||
handleConfirm: func(updatedRemoteUrl string) error {
|
||||
return gui.PopupHandler.Prompt(popup.PromptOpts{
|
||||
Title: editUrlMessage,
|
||||
InitialContent: url,
|
||||
HandleConfirm: func(updatedRemoteUrl string) error {
|
||||
gui.logAction(gui.Tr.Actions.UpdateRemote)
|
||||
if err := gui.Git.Remote.UpdateRemoteUrl(updatedRemoteName, updatedRemoteUrl); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
return gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{BRANCHES, REMOTES}})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Scope: []types.RefreshableView{types.BRANCHES, types.REMOTES}})
|
||||
},
|
||||
})
|
||||
},
|
||||
@ -175,13 +177,15 @@ func (gui *Gui) handleFetchRemote() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
return gui.WithWaitingStatus(gui.Tr.FetchingRemoteStatus, func() error {
|
||||
return gui.PopupHandler.WithWaitingStatus(gui.Tr.FetchingRemoteStatus, func() error {
|
||||
gui.Mutexes.FetchMutex.Lock()
|
||||
defer gui.Mutexes.FetchMutex.Unlock()
|
||||
|
||||
err := gui.Git.Sync.FetchRemote(remote.Name)
|
||||
gui.handleCredentialsPopup(err)
|
||||
if err != nil {
|
||||
_ = gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
return gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{BRANCHES, REMOTES}})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Scope: []types.RefreshableView{types.BRANCHES, types.REMOTES}})
|
||||
})
|
||||
}
|
||||
|
@ -3,12 +3,14 @@ package gui
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
func (gui *Gui) resetToRef(ref string, strength string, envVars []string) error {
|
||||
if err := gui.Git.Commit.ResetToCommit(ref, strength, envVars); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
gui.State.Panels.Commits.SelectedLineIdx = 0
|
||||
@ -20,7 +22,7 @@ func (gui *Gui) resetToRef(ref string, strength string, envVars []string) error
|
||||
return err
|
||||
}
|
||||
|
||||
if err := gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{FILES, BRANCHES, REFLOG, COMMITS}}); err != nil {
|
||||
if err := gui.refreshSidePanels(types.RefreshOptions{Scope: []types.RefreshableView{types.FILES, types.BRANCHES, types.REFLOG, types.COMMITS}}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -29,20 +31,23 @@ func (gui *Gui) resetToRef(ref string, strength string, envVars []string) error
|
||||
|
||||
func (gui *Gui) createResetMenu(ref string) error {
|
||||
strengths := []string{"soft", "mixed", "hard"}
|
||||
menuItems := make([]*menuItem, len(strengths))
|
||||
menuItems := make([]*popup.MenuItem, len(strengths))
|
||||
for i, strength := range strengths {
|
||||
strength := strength
|
||||
menuItems[i] = &menuItem{
|
||||
displayStrings: []string{
|
||||
menuItems[i] = &popup.MenuItem{
|
||||
DisplayStrings: []string{
|
||||
fmt.Sprintf("%s reset", strength),
|
||||
style.FgRed.Sprintf("reset --%s %s", strength, ref),
|
||||
},
|
||||
onPress: func() error {
|
||||
OnPress: func() error {
|
||||
gui.logAction("Reset")
|
||||
return gui.resetToRef(ref, strength, []string{})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return gui.createMenu(fmt.Sprintf("%s %s", gui.Tr.LcResetTo, ref), menuItems, createMenuOptions{showCancel: true})
|
||||
return gui.PopupHandler.Menu(popup.CreateMenuOptions{
|
||||
Title: fmt.Sprintf("%s %s", gui.Tr.LcResetTo, ref),
|
||||
Items: menuItems,
|
||||
})
|
||||
}
|
||||
|
@ -4,6 +4,8 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/patch"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
func (gui *Gui) refreshStagingPanel(forceSecondaryFocused bool, selectedLineIdx int) error {
|
||||
@ -112,10 +114,10 @@ func (gui *Gui) handleResetSelection() error {
|
||||
}
|
||||
|
||||
if !gui.UserConfig.Gui.SkipUnstageLineWarning {
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.UnstageLinesTitle,
|
||||
prompt: gui.Tr.UnstageLinesPrompt,
|
||||
handleConfirm: func() error {
|
||||
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: gui.Tr.UnstageLinesTitle,
|
||||
Prompt: gui.Tr.UnstageLinesPrompt,
|
||||
HandleConfirm: func() error {
|
||||
return gui.withLBLActiveCheck(func(state *LblPanelState) error {
|
||||
return gui.applySelection(true, state)
|
||||
})
|
||||
@ -149,14 +151,14 @@ func (gui *Gui) applySelection(reverse bool, state *LblPanelState) error {
|
||||
gui.logAction(gui.Tr.Actions.ApplyPatch)
|
||||
err := gui.Git.WorkingTree.ApplyPatch(patch, applyFlags...)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
if state.SelectingRange() {
|
||||
state.SetLineSelectMode()
|
||||
}
|
||||
|
||||
if err := gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{FILES}}); err != nil {
|
||||
if err := gui.refreshSidePanels(types.RefreshOptions{Scope: []types.RefreshableView{types.FILES}}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := gui.refreshStagingPanel(false, -1); err != nil {
|
||||
|
@ -2,6 +2,8 @@ package gui
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
// list panel functions
|
||||
@ -54,7 +56,7 @@ func (gui *Gui) handleStashApply() error {
|
||||
err := gui.Git.Stash.Apply(stashEntry.Index)
|
||||
_ = gui.postStashRefresh()
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -63,10 +65,10 @@ func (gui *Gui) handleStashApply() error {
|
||||
return apply()
|
||||
}
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.StashApply,
|
||||
prompt: gui.Tr.SureApplyStashEntry,
|
||||
handleConfirm: func() error {
|
||||
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: gui.Tr.StashApply,
|
||||
Prompt: gui.Tr.SureApplyStashEntry,
|
||||
HandleConfirm: func() error {
|
||||
return apply()
|
||||
},
|
||||
})
|
||||
@ -85,7 +87,7 @@ func (gui *Gui) handleStashPop() error {
|
||||
err := gui.Git.Stash.Pop(stashEntry.Index)
|
||||
_ = gui.postStashRefresh()
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -94,10 +96,10 @@ func (gui *Gui) handleStashPop() error {
|
||||
return pop()
|
||||
}
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.StashPop,
|
||||
prompt: gui.Tr.SurePopStashEntry,
|
||||
handleConfirm: func() error {
|
||||
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: gui.Tr.StashPop,
|
||||
Prompt: gui.Tr.SurePopStashEntry,
|
||||
HandleConfirm: func() error {
|
||||
return pop()
|
||||
},
|
||||
})
|
||||
@ -109,15 +111,15 @@ func (gui *Gui) handleStashDrop() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.StashDrop,
|
||||
prompt: gui.Tr.SureDropStashEntry,
|
||||
handleConfirm: func() error {
|
||||
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: gui.Tr.StashDrop,
|
||||
Prompt: gui.Tr.SureDropStashEntry,
|
||||
HandleConfirm: func() error {
|
||||
gui.logAction(gui.Tr.Actions.Stash)
|
||||
err := gui.Git.Stash.Drop(stashEntry.Index)
|
||||
_ = gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{STASH}})
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
@ -125,12 +127,12 @@ func (gui *Gui) handleStashDrop() error {
|
||||
}
|
||||
|
||||
func (gui *Gui) postStashRefresh() error {
|
||||
return gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{STASH, FILES}})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Scope: []types.RefreshableView{types.STASH, types.FILES}})
|
||||
}
|
||||
|
||||
func (gui *Gui) handleStashSave(stashFunc func(message string) error) error {
|
||||
if len(gui.trackedFiles()) == 0 && len(gui.stagedFiles()) == 0 {
|
||||
return gui.createErrorPanel(gui.Tr.NoTrackedStagedFilesStash)
|
||||
return gui.PopupHandler.ErrorMsg(gui.Tr.NoTrackedStagedFilesStash)
|
||||
}
|
||||
|
||||
return gui.prompt(promptOpts{
|
||||
@ -139,7 +141,7 @@ func (gui *Gui) handleStashSave(stashFunc func(message string) error) error {
|
||||
err := stashFunc(stashComment)
|
||||
_ = gui.postStashRefresh()
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
||||
"github.com/jesseduffield/lazygit/pkg/constants"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
@ -49,8 +50,10 @@ func cursorInSubstring(cx int, prefix string, substring string) bool {
|
||||
}
|
||||
|
||||
func (gui *Gui) handleCheckForUpdate() error {
|
||||
gui.Updater.CheckForNewUpdate(gui.onUserUpdateCheckFinish, true)
|
||||
return gui.createLoaderPanel(gui.Tr.CheckingForUpdates)
|
||||
return gui.PopupHandler.WithWaitingStatus(gui.Tr.CheckingForUpdates, func() error {
|
||||
gui.Updater.CheckForNewUpdate(gui.onUserUpdateCheckFinish, true)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) handleStatusClick() error {
|
||||
@ -136,17 +139,21 @@ func (gui *Gui) askForConfigFile(action func(file string) error) error {
|
||||
case 1:
|
||||
return action(confPaths[0])
|
||||
default:
|
||||
menuItems := make([]*menuItem, len(confPaths))
|
||||
menuItems := make([]*popup.MenuItem, len(confPaths))
|
||||
for i, file := range confPaths {
|
||||
i := i
|
||||
menuItems[i] = &menuItem{
|
||||
displayString: file,
|
||||
onPress: func() error {
|
||||
menuItems[i] = &popup.MenuItem{
|
||||
DisplayString: file,
|
||||
OnPress: func() error {
|
||||
return action(confPaths[i])
|
||||
},
|
||||
}
|
||||
}
|
||||
return gui.createMenu(gui.Tr.SelectConfigFile, menuItems, createMenuOptions{})
|
||||
return gui.PopupHandler.Menu(popup.CreateMenuOptions{
|
||||
Title: gui.Tr.SelectConfigFile,
|
||||
Items: menuItems,
|
||||
HideCancel: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@ package gui
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/loaders"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||
)
|
||||
|
||||
// list panel functions
|
||||
@ -42,10 +43,10 @@ func (gui *Gui) handleCheckoutSubCommit() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := gui.ask(askOpts{
|
||||
title: gui.Tr.LcCheckoutCommit,
|
||||
prompt: gui.Tr.SureCheckoutThisCommit,
|
||||
handleConfirm: func() error {
|
||||
err := gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: gui.Tr.LcCheckoutCommit,
|
||||
Prompt: gui.Tr.SureCheckoutThisCommit,
|
||||
HandleConfirm: func() error {
|
||||
gui.logAction(gui.Tr.Actions.CheckoutCommit)
|
||||
return gui.handleCheckoutRef(commit.Sha, handleCheckoutRefOptions{})
|
||||
},
|
||||
|
@ -3,8 +3,6 @@ package gui
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
@ -36,7 +34,7 @@ func (gui *Gui) submodulesRenderToMain() error {
|
||||
if file == nil {
|
||||
task = NewRenderStringTask(prefix)
|
||||
} else {
|
||||
cmdObj := gui.Git.WorkingTree.WorktreeFileDiffCmdObj(file, false, !file.HasUnstagedChanges && file.HasStagedChanges, gui.State.IgnoreWhitespaceInDiffView)
|
||||
cmdObj := gui.Git.WorkingTree.WorktreeFileDiffCmdObj(file, false, !file.HasUnstagedChanges && file.HasStagedChanges, gui.IgnoreWhitespaceInDiffView)
|
||||
task = NewRunCommandTaskWithPrefix(cmdObj.GetCmd(), prefix)
|
||||
}
|
||||
}
|
||||
@ -60,205 +58,12 @@ func (gui *Gui) refreshStateSubmoduleConfigs() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) handleSubmoduleEnter(submodule *models.SubmoduleConfig) error {
|
||||
return gui.enterSubmodule(submodule)
|
||||
}
|
||||
|
||||
func (gui *Gui) enterSubmodule(submodule *models.SubmoduleConfig) error {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
gui.RepoPathStack = append(gui.RepoPathStack, wd)
|
||||
gui.RepoPathStack.Push(wd)
|
||||
|
||||
return gui.dispatchSwitchToRepo(submodule.Path, true)
|
||||
}
|
||||
|
||||
func (gui *Gui) removeSubmodule(submodule *models.SubmoduleConfig) error {
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.RemoveSubmodule,
|
||||
prompt: fmt.Sprintf(gui.Tr.RemoveSubmodulePrompt, submodule.Name),
|
||||
handleConfirm: func() error {
|
||||
gui.logAction(gui.Tr.Actions.RemoveSubmodule)
|
||||
if err := gui.Git.Submodule.Delete(submodule); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
|
||||
return gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{SUBMODULES, FILES}})
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) handleResetSubmodule(submodule *models.SubmoduleConfig) error {
|
||||
return gui.WithWaitingStatus(gui.Tr.LcResettingSubmoduleStatus, func() error {
|
||||
return gui.resetSubmodule(submodule)
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) fileForSubmodule(submodule *models.SubmoduleConfig) *models.File {
|
||||
for _, file := range gui.State.FileTreeViewModel.GetAllFiles() {
|
||||
if file.IsSubmodule([]*models.SubmoduleConfig{submodule}) {
|
||||
return file
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) resetSubmodule(submodule *models.SubmoduleConfig) error {
|
||||
gui.logAction(gui.Tr.Actions.ResetSubmodule)
|
||||
|
||||
file := gui.fileForSubmodule(submodule)
|
||||
if file != nil {
|
||||
if err := gui.Git.WorkingTree.UnStageFile(file.Names(), file.Tracked); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := gui.Git.Submodule.Stash(submodule); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
if err := gui.Git.Submodule.Reset(submodule); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
|
||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []RefreshableView{FILES, SUBMODULES}})
|
||||
}
|
||||
|
||||
func (gui *Gui) handleAddSubmodule() error {
|
||||
return gui.prompt(promptOpts{
|
||||
title: gui.Tr.LcNewSubmoduleUrl,
|
||||
handleConfirm: func(submoduleUrl string) error {
|
||||
nameSuggestion := filepath.Base(strings.TrimSuffix(submoduleUrl, filepath.Ext(submoduleUrl)))
|
||||
|
||||
return gui.prompt(promptOpts{
|
||||
title: gui.Tr.LcNewSubmoduleName,
|
||||
initialContent: nameSuggestion,
|
||||
handleConfirm: func(submoduleName string) error {
|
||||
|
||||
return gui.prompt(promptOpts{
|
||||
title: gui.Tr.LcNewSubmodulePath,
|
||||
initialContent: submoduleName,
|
||||
handleConfirm: func(submodulePath string) error {
|
||||
return gui.WithWaitingStatus(gui.Tr.LcAddingSubmoduleStatus, func() error {
|
||||
gui.logAction(gui.Tr.Actions.AddSubmodule)
|
||||
err := gui.Git.Submodule.Add(submoduleName, submodulePath, submoduleUrl)
|
||||
gui.handleCredentialsPopup(err)
|
||||
|
||||
return gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{SUBMODULES}})
|
||||
})
|
||||
},
|
||||
})
|
||||
},
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func (gui *Gui) handleEditSubmoduleUrl(submodule *models.SubmoduleConfig) error {
|
||||
return gui.prompt(promptOpts{
|
||||
title: fmt.Sprintf(gui.Tr.LcUpdateSubmoduleUrl, submodule.Name),
|
||||
initialContent: submodule.Url,
|
||||
handleConfirm: func(newUrl string) error {
|
||||
return gui.WithWaitingStatus(gui.Tr.LcUpdatingSubmoduleUrlStatus, func() error {
|
||||
gui.logAction(gui.Tr.Actions.UpdateSubmoduleUrl)
|
||||
err := gui.Git.Submodule.UpdateUrl(submodule.Name, submodule.Path, newUrl)
|
||||
gui.handleCredentialsPopup(err)
|
||||
|
||||
return gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{SUBMODULES}})
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) handleSubmoduleInit(submodule *models.SubmoduleConfig) error {
|
||||
return gui.WithWaitingStatus(gui.Tr.LcInitializingSubmoduleStatus, func() error {
|
||||
gui.logAction(gui.Tr.Actions.InitialiseSubmodule)
|
||||
err := gui.Git.Submodule.Init(submodule.Path)
|
||||
gui.handleCredentialsPopup(err)
|
||||
|
||||
return gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{SUBMODULES}})
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) forSubmodule(callback func(*models.SubmoduleConfig) error) func() error {
|
||||
return func() error {
|
||||
submodule := gui.getSelectedSubmodule()
|
||||
if submodule == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return callback(submodule)
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) handleBulkSubmoduleActionsMenu() error {
|
||||
menuItems := []*menuItem{
|
||||
{
|
||||
displayStrings: []string{gui.Tr.LcBulkInitSubmodules, style.FgGreen.Sprint(gui.Git.Submodule.BulkInitCmdObj().ToString())},
|
||||
onPress: func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.LcRunningCommand, func() error {
|
||||
gui.logAction(gui.Tr.Actions.BulkInitialiseSubmodules)
|
||||
err := gui.Git.Submodule.BulkInitCmdObj().Run()
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
|
||||
return gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{SUBMODULES}})
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
displayStrings: []string{gui.Tr.LcBulkUpdateSubmodules, style.FgYellow.Sprint(gui.Git.Submodule.BulkUpdateCmdObj().ToString())},
|
||||
onPress: func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.LcRunningCommand, func() error {
|
||||
gui.logAction(gui.Tr.Actions.BulkUpdateSubmodules)
|
||||
if err := gui.Git.Submodule.BulkUpdateCmdObj().Run(); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
|
||||
return gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{SUBMODULES}})
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
displayStrings: []string{gui.Tr.LcSubmoduleStashAndReset, style.FgRed.Sprintf("git stash in each submodule && %s", gui.Git.Submodule.ForceBulkUpdateCmdObj().ToString())},
|
||||
onPress: func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.LcRunningCommand, func() error {
|
||||
gui.logAction(gui.Tr.Actions.BulkStashAndResetSubmodules)
|
||||
if err := gui.Git.Submodule.ResetSubmodules(gui.State.Submodules); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
|
||||
return gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{SUBMODULES}})
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
displayStrings: []string{gui.Tr.LcBulkDeinitSubmodules, style.FgRed.Sprint(gui.Git.Submodule.BulkDeinitCmdObj().ToString())},
|
||||
onPress: func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.LcRunningCommand, func() error {
|
||||
gui.logAction(gui.Tr.Actions.BulkDeinitialiseSubmodules)
|
||||
if err := gui.Git.Submodule.BulkDeinitCmdObj().Run(); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
|
||||
return gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{SUBMODULES}})
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return gui.createMenu(gui.Tr.LcBulkSubmoduleOptions, menuItems, createMenuOptions{showCancel: true})
|
||||
}
|
||||
|
||||
func (gui *Gui) handleUpdateSubmodule(submodule *models.SubmoduleConfig) error {
|
||||
return gui.WithWaitingStatus(gui.Tr.LcUpdatingSubmoduleStatus, func() error {
|
||||
gui.logAction(gui.Tr.Actions.UpdateSubmodule)
|
||||
err := gui.Git.Submodule.Update(submodule.Path)
|
||||
gui.handleCredentialsPopup(err)
|
||||
|
||||
return gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{SUBMODULES}})
|
||||
})
|
||||
}
|
||||
|
@ -2,6 +2,8 @@ package gui
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
@ -41,7 +43,7 @@ func (gui *Gui) tagsRenderToMain() error {
|
||||
func (gui *Gui) refreshTags() error {
|
||||
tags, err := gui.Git.Loaders.Tags.GetTags()
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
gui.State.Tags = tags
|
||||
@ -78,15 +80,15 @@ func (gui *Gui) handleDeleteTag(tag *models.Tag) error {
|
||||
},
|
||||
)
|
||||
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.DeleteTagTitle,
|
||||
prompt: prompt,
|
||||
handleConfirm: func() error {
|
||||
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: gui.Tr.DeleteTagTitle,
|
||||
Prompt: prompt,
|
||||
HandleConfirm: func() error {
|
||||
gui.logAction(gui.Tr.Actions.DeleteTag)
|
||||
if err := gui.Git.Tag.Delete(tag.Name); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []RefreshableView{COMMITS, TAGS}})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.COMMITS, types.TAGS}})
|
||||
},
|
||||
})
|
||||
}
|
||||
@ -99,15 +101,17 @@ func (gui *Gui) handlePushTag(tag *models.Tag) error {
|
||||
},
|
||||
)
|
||||
|
||||
return gui.prompt(promptOpts{
|
||||
title: title,
|
||||
initialContent: "origin",
|
||||
findSuggestionsFunc: gui.getRemoteSuggestionsFunc(),
|
||||
handleConfirm: func(response string) error {
|
||||
return gui.WithWaitingStatus(gui.Tr.PushingTagStatus, func() error {
|
||||
return gui.PopupHandler.Prompt(popup.PromptOpts{
|
||||
Title: title,
|
||||
InitialContent: "origin",
|
||||
FindSuggestionsFunc: gui.getRemoteSuggestionsFunc(),
|
||||
HandleConfirm: func(response string) error {
|
||||
return gui.PopupHandler.WithWaitingStatus(gui.Tr.PushingTagStatus, func() error {
|
||||
gui.logAction(gui.Tr.Actions.PushTag)
|
||||
err := gui.Git.Tag.Push(response, tag.Name)
|
||||
gui.handleCredentialsPopup(err)
|
||||
if err != nil {
|
||||
_ = gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
18
pkg/gui/types/keybindings.go
Normal file
18
pkg/gui/types/keybindings.go
Normal file
@ -0,0 +1,18 @@
|
||||
package types
|
||||
|
||||
import "github.com/jesseduffield/gocui"
|
||||
|
||||
// Binding - a keybinding mapping a key and modifier to a handler. The keypress
|
||||
// is only handled if the given view has focus, or handled globally if the view
|
||||
// is ""
|
||||
type Binding struct {
|
||||
ViewName string
|
||||
Contexts []string
|
||||
Handler func() error
|
||||
Key interface{} // FIXME: find out how to get `gocui.Key | rune`
|
||||
Modifier gocui.Modifier
|
||||
Description string
|
||||
Alternative string
|
||||
Tag string // e.g. 'navigation'. Used for grouping things in the cheatsheet
|
||||
OpensMenu bool
|
||||
}
|
32
pkg/gui/types/refresh.go
Normal file
32
pkg/gui/types/refresh.go
Normal file
@ -0,0 +1,32 @@
|
||||
package types
|
||||
|
||||
// models/views that we can refresh
|
||||
type RefreshableView int
|
||||
|
||||
const (
|
||||
COMMITS RefreshableView = iota
|
||||
BRANCHES
|
||||
FILES
|
||||
STASH
|
||||
REFLOG
|
||||
TAGS
|
||||
REMOTES
|
||||
STATUS
|
||||
SUBMODULES
|
||||
// not actually a view. Will refactor this later
|
||||
BISECT_INFO
|
||||
)
|
||||
|
||||
type RefreshMode int
|
||||
|
||||
const (
|
||||
SYNC RefreshMode = iota // wait until everything is done before returning
|
||||
ASYNC // return immediately, allowing each independent thing to update itself
|
||||
BLOCK_UI // wrap code in an update call to ensure UI updates all at once and keybindings aren't executed till complete
|
||||
)
|
||||
|
||||
type RefreshOptions struct {
|
||||
Then func()
|
||||
Scope []RefreshableView // e.g. []int{COMMITS, BRANCHES}. Leave empty to refresh everything
|
||||
Mode RefreshMode // one of SYNC (default), ASYNC, and BLOCK_UI
|
||||
}
|
@ -2,6 +2,8 @@ package gui
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
@ -89,7 +91,7 @@ func (gui *Gui) reflogUndo() error {
|
||||
undoingStatus := gui.Tr.UndoingStatus
|
||||
|
||||
if gui.Git.Status.WorkingTreeState() == enums.REBASE_MODE_REBASING {
|
||||
return gui.createErrorPanel(gui.Tr.LcCantUndoWhileRebasing)
|
||||
return gui.PopupHandler.ErrorMsg(gui.Tr.LcCantUndoWhileRebasing)
|
||||
}
|
||||
|
||||
return gui.parseReflogForActions(func(counter int, action reflogAction) (bool, error) {
|
||||
@ -124,7 +126,7 @@ func (gui *Gui) reflogRedo() error {
|
||||
redoingStatus := gui.Tr.RedoingStatus
|
||||
|
||||
if gui.Git.Status.WorkingTreeState() == enums.REBASE_MODE_REBASING {
|
||||
return gui.createErrorPanel(gui.Tr.LcCantRedoWhileRebasing)
|
||||
return gui.PopupHandler.ErrorMsg(gui.Tr.LcCantRedoWhileRebasing)
|
||||
}
|
||||
|
||||
return gui.parseReflogForActions(func(counter int, action reflogAction) (bool, error) {
|
||||
@ -166,7 +168,7 @@ type handleHardResetWithAutoStashOptions struct {
|
||||
func (gui *Gui) handleHardResetWithAutoStash(commitSha string, options handleHardResetWithAutoStashOptions) error {
|
||||
reset := func() error {
|
||||
if err := gui.resetToRef(commitSha, "hard", options.EnvVars); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -175,24 +177,24 @@ func (gui *Gui) handleHardResetWithAutoStash(commitSha string, options handleHar
|
||||
dirtyWorkingTree := len(gui.trackedFiles()) > 0 || len(gui.stagedFiles()) > 0
|
||||
if dirtyWorkingTree {
|
||||
// offer to autostash changes
|
||||
return gui.ask(askOpts{
|
||||
title: gui.Tr.AutoStashTitle,
|
||||
prompt: gui.Tr.AutoStashPrompt,
|
||||
handleConfirm: func() error {
|
||||
return gui.WithWaitingStatus(options.WaitingStatus, func() error {
|
||||
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: gui.Tr.AutoStashTitle,
|
||||
Prompt: gui.Tr.AutoStashPrompt,
|
||||
HandleConfirm: func() error {
|
||||
return gui.PopupHandler.WithWaitingStatus(options.WaitingStatus, func() error {
|
||||
if err := gui.Git.Stash.Save(gui.Tr.StashPrefix + commitSha); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
if err := reset(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err := gui.Git.Stash.Pop(0)
|
||||
if err := gui.refreshSidePanels(refreshOptions{}); err != nil {
|
||||
if err := gui.refreshSidePanels(types.RefreshOptions{}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
@ -200,7 +202,7 @@ func (gui *Gui) handleHardResetWithAutoStash(commitSha string, options handleHar
|
||||
})
|
||||
}
|
||||
|
||||
return gui.WithWaitingStatus(options.WaitingStatus, func() error {
|
||||
return gui.PopupHandler.WithWaitingStatus(options.WaitingStatus, func() error {
|
||||
return reset()
|
||||
})
|
||||
}
|
||||
|
@ -4,13 +4,14 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||
)
|
||||
|
||||
func (gui *Gui) showUpdatePrompt(newVersion string) error {
|
||||
return gui.ask(askOpts{
|
||||
title: "New version available!",
|
||||
prompt: fmt.Sprintf("Download version %s? (enter/esc)", newVersion),
|
||||
handleConfirm: func() error {
|
||||
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: "New version available!",
|
||||
Prompt: fmt.Sprintf("Download version %s? (enter/esc)", newVersion),
|
||||
HandleConfirm: func() error {
|
||||
gui.startUpdating(newVersion)
|
||||
return nil
|
||||
},
|
||||
@ -19,10 +20,10 @@ func (gui *Gui) showUpdatePrompt(newVersion string) error {
|
||||
|
||||
func (gui *Gui) onUserUpdateCheckFinish(newVersion string, err error) error {
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
if newVersion == "" {
|
||||
return gui.createErrorPanel("New version not found")
|
||||
return gui.PopupHandler.ErrorMsg("New version not found")
|
||||
}
|
||||
return gui.showUpdatePrompt(newVersion)
|
||||
}
|
||||
@ -55,7 +56,7 @@ func (gui *Gui) onUpdateFinish(statusId int, err error) error {
|
||||
gui.OnUIThread(func() error {
|
||||
_ = gui.renderString(gui.Views.AppStatus, "")
|
||||
if err != nil {
|
||||
return gui.createErrorPanel("Update failed: " + err.Error())
|
||||
return gui.PopupHandler.ErrorMsg("Update failed: " + err.Error())
|
||||
}
|
||||
return nil
|
||||
})
|
||||
@ -64,10 +65,10 @@ func (gui *Gui) onUpdateFinish(statusId int, err error) error {
|
||||
}
|
||||
|
||||
func (gui *Gui) createUpdateQuitConfirmation() error {
|
||||
return gui.ask(askOpts{
|
||||
title: "Currently Updating",
|
||||
prompt: "An update is in progress. Are you sure you want to quit?",
|
||||
handleConfirm: func() error {
|
||||
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||
Title: "Currently Updating",
|
||||
Prompt: "An update is in progress. Are you sure you want to quit?",
|
||||
HandleConfirm: func() error {
|
||||
return gocui.ErrQuit
|
||||
},
|
||||
})
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/spkg/bom"
|
||||
)
|
||||
@ -15,34 +16,18 @@ func (gui *Gui) getCyclableWindows() []string {
|
||||
return []string{"status", "files", "branches", "commits", "stash"}
|
||||
}
|
||||
|
||||
// models/views that we can refresh
|
||||
type RefreshableView int
|
||||
|
||||
const (
|
||||
COMMITS RefreshableView = iota
|
||||
BRANCHES
|
||||
FILES
|
||||
STASH
|
||||
REFLOG
|
||||
TAGS
|
||||
REMOTES
|
||||
STATUS
|
||||
SUBMODULES
|
||||
// not actually a view. Will refactor this later
|
||||
BISECT_INFO
|
||||
)
|
||||
|
||||
func getScopeNames(scopes []RefreshableView) []string {
|
||||
scopeNameMap := map[RefreshableView]string{
|
||||
COMMITS: "commits",
|
||||
BRANCHES: "branches",
|
||||
FILES: "files",
|
||||
SUBMODULES: "submodules",
|
||||
STASH: "stash",
|
||||
REFLOG: "reflog",
|
||||
TAGS: "tags",
|
||||
REMOTES: "remotes",
|
||||
STATUS: "status",
|
||||
func getScopeNames(scopes []types.RefreshableView) []string {
|
||||
scopeNameMap := map[types.RefreshableView]string{
|
||||
types.COMMITS: "commits",
|
||||
types.BRANCHES: "branches",
|
||||
types.FILES: "files",
|
||||
types.SUBMODULES: "submodules",
|
||||
types.STASH: "stash",
|
||||
types.REFLOG: "reflog",
|
||||
types.TAGS: "tags",
|
||||
types.REMOTES: "remotes",
|
||||
types.STATUS: "status",
|
||||
types.BISECT_INFO: "bisect",
|
||||
}
|
||||
|
||||
scopeNames := make([]string, len(scopes))
|
||||
@ -53,69 +38,55 @@ func getScopeNames(scopes []RefreshableView) []string {
|
||||
return scopeNames
|
||||
}
|
||||
|
||||
func getModeName(mode RefreshMode) string {
|
||||
func getModeName(mode types.RefreshMode) string {
|
||||
switch mode {
|
||||
case SYNC:
|
||||
case types.SYNC:
|
||||
return "sync"
|
||||
case ASYNC:
|
||||
case types.ASYNC:
|
||||
return "async"
|
||||
case BLOCK_UI:
|
||||
case types.BLOCK_UI:
|
||||
return "block-ui"
|
||||
default:
|
||||
return "unknown mode"
|
||||
}
|
||||
}
|
||||
|
||||
type RefreshMode int
|
||||
|
||||
const (
|
||||
SYNC RefreshMode = iota // wait until everything is done before returning
|
||||
ASYNC // return immediately, allowing each independent thing to update itself
|
||||
BLOCK_UI // wrap code in an update call to ensure UI updates all at once and keybindings aren't executed till complete
|
||||
)
|
||||
|
||||
type refreshOptions struct {
|
||||
then func()
|
||||
scope []RefreshableView // e.g. []int{COMMITS, BRANCHES}. Leave empty to refresh everything
|
||||
mode RefreshMode // one of SYNC (default), ASYNC, and BLOCK_UI
|
||||
}
|
||||
|
||||
func arrToMap(arr []RefreshableView) map[RefreshableView]bool {
|
||||
output := map[RefreshableView]bool{}
|
||||
func arrToMap(arr []types.RefreshableView) map[types.RefreshableView]bool {
|
||||
output := map[types.RefreshableView]bool{}
|
||||
for _, el := range arr {
|
||||
output[el] = true
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
func (gui *Gui) refreshSidePanels(options refreshOptions) error {
|
||||
if options.scope == nil {
|
||||
func (gui *Gui) refreshSidePanels(options types.RefreshOptions) error {
|
||||
if options.Scope == nil {
|
||||
gui.Log.Infof(
|
||||
"refreshing all scopes in %s mode",
|
||||
getModeName(options.mode),
|
||||
getModeName(options.Mode),
|
||||
)
|
||||
} else {
|
||||
gui.Log.Infof(
|
||||
"refreshing the following scopes in %s mode: %s",
|
||||
getModeName(options.mode),
|
||||
strings.Join(getScopeNames(options.scope), ","),
|
||||
getModeName(options.Mode),
|
||||
strings.Join(getScopeNames(options.Scope), ","),
|
||||
)
|
||||
}
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
|
||||
f := func() {
|
||||
var scopeMap map[RefreshableView]bool
|
||||
if len(options.scope) == 0 {
|
||||
scopeMap = arrToMap([]RefreshableView{COMMITS, BRANCHES, FILES, STASH, REFLOG, TAGS, REMOTES, STATUS, BISECT_INFO})
|
||||
var scopeMap map[types.RefreshableView]bool
|
||||
if len(options.Scope) == 0 {
|
||||
scopeMap = arrToMap([]types.RefreshableView{types.COMMITS, types.BRANCHES, types.FILES, types.STASH, types.REFLOG, types.TAGS, types.REMOTES, types.STATUS, types.BISECT_INFO})
|
||||
} else {
|
||||
scopeMap = arrToMap(options.scope)
|
||||
scopeMap = arrToMap(options.Scope)
|
||||
}
|
||||
|
||||
if scopeMap[COMMITS] || scopeMap[BRANCHES] || scopeMap[REFLOG] || scopeMap[BISECT_INFO] {
|
||||
if scopeMap[types.COMMITS] || scopeMap[types.BRANCHES] || scopeMap[types.REFLOG] || scopeMap[types.BISECT_INFO] {
|
||||
wg.Add(1)
|
||||
func() {
|
||||
if options.mode == ASYNC {
|
||||
if options.Mode == types.ASYNC {
|
||||
go utils.Safe(func() { gui.refreshCommits() })
|
||||
} else {
|
||||
gui.refreshCommits()
|
||||
@ -124,10 +95,10 @@ func (gui *Gui) refreshSidePanels(options refreshOptions) error {
|
||||
}()
|
||||
}
|
||||
|
||||
if scopeMap[FILES] || scopeMap[SUBMODULES] {
|
||||
if scopeMap[types.FILES] || scopeMap[types.SUBMODULES] {
|
||||
wg.Add(1)
|
||||
func() {
|
||||
if options.mode == ASYNC {
|
||||
if options.Mode == types.ASYNC {
|
||||
go utils.Safe(func() { _ = gui.refreshFilesAndSubmodules() })
|
||||
} else {
|
||||
_ = gui.refreshFilesAndSubmodules()
|
||||
@ -136,10 +107,10 @@ func (gui *Gui) refreshSidePanels(options refreshOptions) error {
|
||||
}()
|
||||
}
|
||||
|
||||
if scopeMap[STASH] {
|
||||
if scopeMap[types.STASH] {
|
||||
wg.Add(1)
|
||||
func() {
|
||||
if options.mode == ASYNC {
|
||||
if options.Mode == types.ASYNC {
|
||||
go utils.Safe(func() { _ = gui.refreshStashEntries() })
|
||||
} else {
|
||||
_ = gui.refreshStashEntries()
|
||||
@ -148,10 +119,10 @@ func (gui *Gui) refreshSidePanels(options refreshOptions) error {
|
||||
}()
|
||||
}
|
||||
|
||||
if scopeMap[TAGS] {
|
||||
if scopeMap[types.TAGS] {
|
||||
wg.Add(1)
|
||||
func() {
|
||||
if options.mode == ASYNC {
|
||||
if options.Mode == types.ASYNC {
|
||||
go utils.Safe(func() { _ = gui.refreshTags() })
|
||||
} else {
|
||||
_ = gui.refreshTags()
|
||||
@ -160,10 +131,10 @@ func (gui *Gui) refreshSidePanels(options refreshOptions) error {
|
||||
}()
|
||||
}
|
||||
|
||||
if scopeMap[REMOTES] {
|
||||
if scopeMap[types.REMOTES] {
|
||||
wg.Add(1)
|
||||
func() {
|
||||
if options.mode == ASYNC {
|
||||
if options.Mode == types.ASYNC {
|
||||
go utils.Safe(func() { _ = gui.refreshRemotes() })
|
||||
} else {
|
||||
_ = gui.refreshRemotes()
|
||||
@ -176,12 +147,12 @@ func (gui *Gui) refreshSidePanels(options refreshOptions) error {
|
||||
|
||||
gui.refreshStatus()
|
||||
|
||||
if options.then != nil {
|
||||
options.then()
|
||||
if options.Then != nil {
|
||||
options.Then()
|
||||
}
|
||||
}
|
||||
|
||||
if options.mode == BLOCK_UI {
|
||||
if options.Mode == types.BLOCK_UI {
|
||||
gui.OnUIThread(func() error {
|
||||
f()
|
||||
return nil
|
||||
|
@ -1,10 +1,10 @@
|
||||
package gui
|
||||
|
||||
func (gui *Gui) toggleWhitespaceInDiffView() error {
|
||||
gui.State.IgnoreWhitespaceInDiffView = !gui.State.IgnoreWhitespaceInDiffView
|
||||
gui.IgnoreWhitespaceInDiffView = !gui.IgnoreWhitespaceInDiffView
|
||||
|
||||
toastMessage := gui.Tr.ShowingWhitespaceInDiffView
|
||||
if gui.State.IgnoreWhitespaceInDiffView {
|
||||
if gui.IgnoreWhitespaceInDiffView {
|
||||
toastMessage = gui.Tr.IgnoringWhitespaceInDiffView
|
||||
}
|
||||
gui.raiseToast(toastMessage)
|
||||
|
@ -3,7 +3,9 @@ package gui
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
func (gui *Gui) handleCreateResetMenu() error {
|
||||
@ -14,92 +16,92 @@ func (gui *Gui) handleCreateResetMenu() error {
|
||||
nukeStr = fmt.Sprintf("%s (%s)", nukeStr, gui.Tr.LcAndResetSubmodules)
|
||||
}
|
||||
|
||||
menuItems := []*menuItem{
|
||||
menuItems := []*popup.MenuItem{
|
||||
{
|
||||
displayStrings: []string{
|
||||
DisplayStrings: []string{
|
||||
gui.Tr.LcDiscardAllChangesToAllFiles,
|
||||
red.Sprint(nukeStr),
|
||||
},
|
||||
onPress: func() error {
|
||||
OnPress: func() error {
|
||||
gui.logAction(gui.Tr.Actions.NukeWorkingTree)
|
||||
if err := gui.Git.WorkingTree.ResetAndClean(); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []RefreshableView{FILES}})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES}})
|
||||
},
|
||||
},
|
||||
{
|
||||
displayStrings: []string{
|
||||
DisplayStrings: []string{
|
||||
gui.Tr.LcDiscardAnyUnstagedChanges,
|
||||
red.Sprint("git checkout -- ."),
|
||||
},
|
||||
onPress: func() error {
|
||||
OnPress: func() error {
|
||||
gui.logAction(gui.Tr.Actions.DiscardUnstagedFileChanges)
|
||||
if err := gui.Git.WorkingTree.DiscardAnyUnstagedFileChanges(); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []RefreshableView{FILES}})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES}})
|
||||
},
|
||||
},
|
||||
{
|
||||
displayStrings: []string{
|
||||
DisplayStrings: []string{
|
||||
gui.Tr.LcDiscardUntrackedFiles,
|
||||
red.Sprint("git clean -fd"),
|
||||
},
|
||||
onPress: func() error {
|
||||
OnPress: func() error {
|
||||
gui.logAction(gui.Tr.Actions.RemoveUntrackedFiles)
|
||||
if err := gui.Git.WorkingTree.RemoveUntrackedFiles(); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []RefreshableView{FILES}})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES}})
|
||||
},
|
||||
},
|
||||
{
|
||||
displayStrings: []string{
|
||||
DisplayStrings: []string{
|
||||
gui.Tr.LcSoftReset,
|
||||
red.Sprint("git reset --soft HEAD"),
|
||||
},
|
||||
onPress: func() error {
|
||||
OnPress: func() error {
|
||||
gui.logAction(gui.Tr.Actions.SoftReset)
|
||||
if err := gui.Git.WorkingTree.ResetSoft("HEAD"); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []RefreshableView{FILES}})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES}})
|
||||
},
|
||||
},
|
||||
{
|
||||
displayStrings: []string{
|
||||
DisplayStrings: []string{
|
||||
"mixed reset",
|
||||
red.Sprint("git reset --mixed HEAD"),
|
||||
},
|
||||
onPress: func() error {
|
||||
OnPress: func() error {
|
||||
gui.logAction(gui.Tr.Actions.MixedReset)
|
||||
if err := gui.Git.WorkingTree.ResetMixed("HEAD"); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []RefreshableView{FILES}})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES}})
|
||||
},
|
||||
},
|
||||
{
|
||||
displayStrings: []string{
|
||||
DisplayStrings: []string{
|
||||
gui.Tr.LcHardReset,
|
||||
red.Sprint("git reset --hard HEAD"),
|
||||
},
|
||||
onPress: func() error {
|
||||
OnPress: func() error {
|
||||
gui.logAction(gui.Tr.Actions.HardReset)
|
||||
if err := gui.Git.WorkingTree.ResetHard("HEAD"); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
return gui.PopupHandler.Error(err)
|
||||
}
|
||||
|
||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []RefreshableView{FILES}})
|
||||
return gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES}})
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return gui.createMenu("", menuItems, createMenuOptions{showCancel: true})
|
||||
return gui.PopupHandler.Menu(popup.CreateMenuOptions{Title: "", Items: menuItems})
|
||||
}
|
||||
|
@ -504,7 +504,6 @@ func chineseTranslationSet() TranslationSet {
|
||||
InitialiseSubmodule: "初始化子模块",
|
||||
BulkInitialiseSubmodules: "批量初始化子模块",
|
||||
BulkUpdateSubmodules: "批量更新子模块",
|
||||
BulkStashAndResetSubmodules: "批量存储和重置子模块",
|
||||
BulkDeinitialiseSubmodules: "批量取消初始化子模块",
|
||||
UpdateSubmodule: "更新子模块",
|
||||
DeleteTag: "删除标签",
|
||||
|
@ -540,7 +540,6 @@ type Actions struct {
|
||||
InitialiseSubmodule string
|
||||
BulkInitialiseSubmodules string
|
||||
BulkUpdateSubmodules string
|
||||
BulkStashAndResetSubmodules string
|
||||
BulkDeinitialiseSubmodules string
|
||||
UpdateSubmodule string
|
||||
CreateLightweightTag string
|
||||
@ -651,7 +650,7 @@ func EnglishTranslationSet() TranslationSet {
|
||||
NoBranchesThisRepo: "No branches for this repo",
|
||||
CommitMessageConfirm: "{{.keyBindClose}}: close, {{.keyBindNewLine}}: new line, {{.keyBindConfirm}}: confirm",
|
||||
CommitWithoutMessageErr: "You cannot commit without a commit message",
|
||||
CloseConfirm: "{{.keyBindClose}}: close, {{.keyBindConfirm}}: confirm",
|
||||
CloseConfirm: "{{.keyBindClose}}: close/cancel, {{.keyBindConfirm}}: confirm",
|
||||
LcClose: "close",
|
||||
LcQuit: "quit",
|
||||
LcSquashDown: "squash down",
|
||||
@ -1097,7 +1096,6 @@ func EnglishTranslationSet() TranslationSet {
|
||||
InitialiseSubmodule: "Initialise submodule",
|
||||
BulkInitialiseSubmodules: "Bulk initialise submodules",
|
||||
BulkUpdateSubmodules: "Bulk update submodules",
|
||||
BulkStashAndResetSubmodules: "Bulk stash and reset submodules",
|
||||
BulkDeinitialiseSubmodules: "Bulk deinitialise submodules",
|
||||
UpdateSubmodule: "Update submodule",
|
||||
DeleteTag: "Delete tag",
|
||||
|
@ -144,12 +144,10 @@ func (u *Updater) CheckForNewUpdate(onFinish func(string, error) error, userRequ
|
||||
return
|
||||
}
|
||||
|
||||
go utils.Safe(func() {
|
||||
newVersion, err := u.checkForNewUpdate()
|
||||
if err = onFinish(newVersion, err); err != nil {
|
||||
u.Log.Error(err)
|
||||
}
|
||||
})
|
||||
newVersion, err := u.checkForNewUpdate()
|
||||
if err = onFinish(newVersion, err); err != nil {
|
||||
u.Log.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *Updater) skipUpdateCheck() bool {
|
||||
|
27
pkg/utils/string_stack.go
Normal file
27
pkg/utils/string_stack.go
Normal file
@ -0,0 +1,27 @@
|
||||
package utils
|
||||
|
||||
type StringStack struct {
|
||||
stack []string
|
||||
}
|
||||
|
||||
func (self *StringStack) Push(s string) {
|
||||
self.stack = append(self.stack, s)
|
||||
}
|
||||
|
||||
func (self *StringStack) Pop() string {
|
||||
if len(self.stack) == 0 {
|
||||
return ""
|
||||
}
|
||||
n := len(self.stack) - 1
|
||||
last := self.stack[n]
|
||||
self.stack = self.stack[:n]
|
||||
return last
|
||||
}
|
||||
|
||||
func (self *StringStack) IsEmpty() bool {
|
||||
return len(self.stack) == 0
|
||||
}
|
||||
|
||||
func (self *StringStack) Clear() {
|
||||
self.stack = []string{}
|
||||
}
|
Loading…
Reference in New Issue
Block a user