mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-06-06 23:46:13 +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
|
// 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 {
|
func (gui *Gui) withWaitingStatus(message string, f func() error) error {
|
||||||
go utils.Safe(func() {
|
go utils.Safe(func() {
|
||||||
id := gui.statusManager.addWaitingStatus(message)
|
id := gui.statusManager.addWaitingStatus(message)
|
||||||
|
|
||||||
@ -119,7 +119,7 @@ func (gui *Gui) WithWaitingStatus(message string, f func() error) error {
|
|||||||
|
|
||||||
if err := f(); err != nil {
|
if err := f(); err != nil {
|
||||||
gui.OnUIThread(func() error {
|
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/git_commands"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"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"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -62,7 +64,7 @@ func (gui *Gui) refreshBranches() {
|
|||||||
|
|
||||||
branches, err := gui.Git.Loaders.Branches.Load(reflogCommits)
|
branches, err := gui.Git.Loaders.Branches.Load(reflogCommits)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = gui.surfaceError(err)
|
_ = gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
gui.State.Branches = branches
|
gui.State.Branches = branches
|
||||||
@ -81,7 +83,7 @@ func (gui *Gui) handleBranchPress() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if gui.State.Panels.Branches.SelectedLineIdx == 0 {
|
if gui.State.Panels.Branches.SelectedLineIdx == 0 {
|
||||||
return gui.createErrorPanel(gui.Tr.AlreadyCheckedOutBranch)
|
return gui.PopupHandler.ErrorMsg(gui.Tr.AlreadyCheckedOutBranch)
|
||||||
}
|
}
|
||||||
branch := gui.getSelectedBranch()
|
branch := gui.getSelectedBranch()
|
||||||
gui.logAction(gui.Tr.Actions.CheckoutBranch)
|
gui.logAction(gui.Tr.Actions.CheckoutBranch)
|
||||||
@ -111,16 +113,16 @@ func (gui *Gui) handleCopyPullRequestURLPress() error {
|
|||||||
branchExistsOnRemote := gui.Git.Remote.CheckRemoteBranchExists(branch.Name)
|
branchExistsOnRemote := gui.Git.Remote.CheckRemoteBranchExists(branch.Name)
|
||||||
|
|
||||||
if !branchExistsOnRemote {
|
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, "")
|
url, err := hostingServiceMgr.GetPullRequestURL(branch.Name, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
gui.logAction(gui.Tr.Actions.CopyPullRequestURL)
|
gui.logAction(gui.Tr.Actions.CopyPullRequestURL)
|
||||||
if err := gui.OSCommand.CopyToClipboard(url); err != nil {
|
if err := gui.OSCommand.CopyToClipboard(url); err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
gui.raiseToast(gui.Tr.PullRequestURLCopiedToClipboard)
|
gui.raiseToast(gui.Tr.PullRequestURLCopiedToClipboard)
|
||||||
@ -129,16 +131,12 @@ func (gui *Gui) handleCopyPullRequestURLPress() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleGitFetch() error {
|
func (gui *Gui) handleGitFetch() error {
|
||||||
if err := gui.createLoaderPanel(gui.Tr.FetchWait); err != nil {
|
return gui.PopupHandler.WithLoaderPanel(gui.Tr.FetchWait, func() error {
|
||||||
return err
|
if err := gui.fetch(); err != nil {
|
||||||
}
|
_ = gui.PopupHandler.Error(err)
|
||||||
|
}
|
||||||
go utils.Safe(func() {
|
return gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC})
|
||||||
err := gui.fetch()
|
|
||||||
gui.handleCredentialsPopup(err)
|
|
||||||
_ = gui.refreshSidePanels(refreshOptions{mode: ASYNC})
|
|
||||||
})
|
})
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleForceCheckout() error {
|
func (gui *Gui) handleForceCheckout() error {
|
||||||
@ -146,15 +144,15 @@ func (gui *Gui) handleForceCheckout() error {
|
|||||||
message := gui.Tr.SureForceCheckout
|
message := gui.Tr.SureForceCheckout
|
||||||
title := gui.Tr.ForceCheckoutBranch
|
title := gui.Tr.ForceCheckoutBranch
|
||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: title,
|
Title: title,
|
||||||
prompt: message,
|
Prompt: message,
|
||||||
handleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
gui.logAction(gui.Tr.Actions.ForceCheckoutBranch)
|
gui.logAction(gui.Tr.Actions.ForceCheckoutBranch)
|
||||||
if err := gui.Git.Branch.Checkout(branch.Name, git_commands.CheckoutOptions{Force: true}); err != nil {
|
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
|
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 {
|
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
|
// 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") {
|
if strings.Contains(err.Error(), "Please commit your changes or stash them before you switch branch") {
|
||||||
// offer to autostash changes
|
// offer to autostash changes
|
||||||
return gui.ask(askOpts{
|
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
|
|
||||||
title: gui.Tr.AutoStashTitle,
|
Title: gui.Tr.AutoStashTitle,
|
||||||
prompt: gui.Tr.AutoStashPrompt,
|
Prompt: gui.Tr.AutoStashPrompt,
|
||||||
handleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
if err := gui.Git.Stash.Save(gui.Tr.StashPrefix + ref); err != nil {
|
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 {
|
if err := gui.Git.Branch.Checkout(ref, cmdOptions); err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
onSuccess()
|
onSuccess()
|
||||||
if err := gui.Git.Stash.Pop(0); err != nil {
|
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 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
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onSuccess()
|
onSuccess()
|
||||||
|
|
||||||
return gui.refreshSidePanels(refreshOptions{mode: BLOCK_UI})
|
return gui.refreshSidePanels(types.RefreshOptions{Mode: types.BLOCK_UI})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleCheckoutByName() error {
|
func (gui *Gui) handleCheckoutByName() error {
|
||||||
return gui.prompt(promptOpts{
|
return gui.PopupHandler.Prompt(popup.PromptOpts{
|
||||||
title: gui.Tr.BranchName + ":",
|
Title: gui.Tr.BranchName + ":",
|
||||||
findSuggestionsFunc: gui.getRefsSuggestionsFunc(),
|
FindSuggestionsFunc: gui.getRefsSuggestionsFunc(),
|
||||||
handleConfirm: func(response string) error {
|
HandleConfirm: func(response string) error {
|
||||||
gui.logAction("Checkout branch")
|
gui.logAction("Checkout branch")
|
||||||
return gui.handleCheckoutRef(response, handleCheckoutRefOptions{
|
return gui.handleCheckoutRef(response, handleCheckoutRefOptions{
|
||||||
onRefNotFound: func(ref string) error {
|
onRefNotFound: func(ref string) error {
|
||||||
return gui.ask(askOpts{
|
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: gui.Tr.BranchNotFoundTitle,
|
Title: gui.Tr.BranchNotFoundTitle,
|
||||||
prompt: fmt.Sprintf("%s %s%s", gui.Tr.BranchNotFoundPrompt, ref, "?"),
|
Prompt: fmt.Sprintf("%s %s%s", gui.Tr.BranchNotFoundPrompt, ref, "?"),
|
||||||
handleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
return gui.createNewBranchWithName(ref)
|
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 {
|
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
|
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 {
|
func (gui *Gui) handleDeleteBranch() error {
|
||||||
@ -278,7 +276,7 @@ func (gui *Gui) deleteBranch(force bool) error {
|
|||||||
}
|
}
|
||||||
checkedOutBranch := gui.getCheckedOutBranch()
|
checkedOutBranch := gui.getCheckedOutBranch()
|
||||||
if checkedOutBranch.Name == selectedBranch.Name {
|
if checkedOutBranch.Name == selectedBranch.Name {
|
||||||
return gui.createErrorPanel(gui.Tr.CantDeleteCheckOutBranch)
|
return gui.PopupHandler.ErrorMsg(gui.Tr.CantDeleteCheckOutBranch)
|
||||||
}
|
}
|
||||||
return gui.deleteNamedBranch(selectedBranch, force)
|
return gui.deleteNamedBranch(selectedBranch, force)
|
||||||
}
|
}
|
||||||
@ -298,19 +296,19 @@ func (gui *Gui) deleteNamedBranch(selectedBranch *models.Branch, force bool) err
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: title,
|
Title: title,
|
||||||
prompt: message,
|
Prompt: message,
|
||||||
handleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
gui.logAction(gui.Tr.Actions.DeleteBranch)
|
gui.logAction(gui.Tr.Actions.DeleteBranch)
|
||||||
if err := gui.Git.Branch.Delete(selectedBranch.Name, force); err != nil {
|
if err := gui.Git.Branch.Delete(selectedBranch.Name, force); err != nil {
|
||||||
errMessage := err.Error()
|
errMessage := err.Error()
|
||||||
if !force && strings.Contains(errMessage, "git branch -D ") {
|
if !force && strings.Contains(errMessage, "git branch -D ") {
|
||||||
return gui.deleteNamedBranch(selectedBranch, true)
|
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() {
|
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
|
checkedOutBranchName := gui.getCheckedOutBranch().Name
|
||||||
if checkedOutBranchName == branchName {
|
if checkedOutBranchName == branchName {
|
||||||
return gui.createErrorPanel(gui.Tr.CantMergeBranchIntoItself)
|
return gui.PopupHandler.ErrorMsg(gui.Tr.CantMergeBranchIntoItself)
|
||||||
}
|
}
|
||||||
prompt := utils.ResolvePlaceholderString(
|
prompt := utils.ResolvePlaceholderString(
|
||||||
gui.Tr.ConfirmMerge,
|
gui.Tr.ConfirmMerge,
|
||||||
@ -335,10 +333,10 @@ func (gui *Gui) mergeBranchIntoCheckedOutBranch(branchName string) error {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: gui.Tr.MergingTitle,
|
Title: gui.Tr.MergingTitle,
|
||||||
prompt: prompt,
|
Prompt: prompt,
|
||||||
handleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
gui.logAction(gui.Tr.Actions.Merge)
|
gui.logAction(gui.Tr.Actions.Merge)
|
||||||
err := gui.Git.Branch.Merge(branchName, git_commands.MergeOpts{})
|
err := gui.Git.Branch.Merge(branchName, git_commands.MergeOpts{})
|
||||||
return gui.handleGenericMergeCommandResult(err)
|
return gui.handleGenericMergeCommandResult(err)
|
||||||
@ -367,7 +365,7 @@ func (gui *Gui) handleRebaseOntoBranch(selectedBranchName string) error {
|
|||||||
|
|
||||||
checkedOutBranch := gui.getCheckedOutBranch().Name
|
checkedOutBranch := gui.getCheckedOutBranch().Name
|
||||||
if selectedBranchName == checkedOutBranch {
|
if selectedBranchName == checkedOutBranch {
|
||||||
return gui.createErrorPanel(gui.Tr.CantRebaseOntoSelf)
|
return gui.PopupHandler.ErrorMsg(gui.Tr.CantRebaseOntoSelf)
|
||||||
}
|
}
|
||||||
prompt := utils.ResolvePlaceholderString(
|
prompt := utils.ResolvePlaceholderString(
|
||||||
gui.Tr.ConfirmRebase,
|
gui.Tr.ConfirmRebase,
|
||||||
@ -377,10 +375,10 @@ func (gui *Gui) handleRebaseOntoBranch(selectedBranchName string) error {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: gui.Tr.RebasingTitle,
|
Title: gui.Tr.RebasingTitle,
|
||||||
prompt: prompt,
|
Prompt: prompt,
|
||||||
handleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
gui.logAction(gui.Tr.Actions.RebaseBranch)
|
gui.logAction(gui.Tr.Actions.RebaseBranch)
|
||||||
err := gui.Git.Rebase.RebaseBranch(selectedBranchName)
|
err := gui.Git.Rebase.RebaseBranch(selectedBranchName)
|
||||||
return gui.handleGenericMergeCommandResult(err)
|
return gui.handleGenericMergeCommandResult(err)
|
||||||
@ -395,13 +393,13 @@ func (gui *Gui) handleFastForward() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !branch.IsTrackingRemote() {
|
if !branch.IsTrackingRemote() {
|
||||||
return gui.createErrorPanel(gui.Tr.FwdNoUpstream)
|
return gui.PopupHandler.ErrorMsg(gui.Tr.FwdNoUpstream)
|
||||||
}
|
}
|
||||||
if !branch.RemoteBranchStoredLocally() {
|
if !branch.RemoteBranchStoredLocally() {
|
||||||
return gui.createErrorPanel(gui.Tr.FwdNoLocalUpstream)
|
return gui.PopupHandler.ErrorMsg(gui.Tr.FwdNoLocalUpstream)
|
||||||
}
|
}
|
||||||
if branch.HasCommitsToPush() {
|
if branch.HasCommitsToPush() {
|
||||||
return gui.createErrorPanel(gui.Tr.FwdCommitsToPush)
|
return gui.PopupHandler.ErrorMsg(gui.Tr.FwdCommitsToPush)
|
||||||
}
|
}
|
||||||
|
|
||||||
action := gui.Tr.Actions.FastForwardBranch
|
action := gui.Tr.Actions.FastForwardBranch
|
||||||
@ -413,19 +411,21 @@ func (gui *Gui) handleFastForward() error {
|
|||||||
"to": branch.Name,
|
"to": branch.Name,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
go utils.Safe(func() {
|
|
||||||
_ = gui.createLoaderPanel(message)
|
|
||||||
|
|
||||||
|
return gui.PopupHandler.WithLoaderPanel(message, func() error {
|
||||||
if gui.State.Panels.Branches.SelectedLineIdx == 0 {
|
if gui.State.Panels.Branches.SelectedLineIdx == 0 {
|
||||||
_ = gui.pullWithLock(PullFilesOptions{action: action, FastForwardOnly: true})
|
_ = gui.pullWithLock(PullFilesOptions{action: action, FastForwardOnly: true})
|
||||||
} else {
|
} else {
|
||||||
gui.logAction(action)
|
gui.logAction(action)
|
||||||
err := gui.Git.Sync.FastForward(branch.Name, branch.UpstreamRemote, branch.UpstreamBranch)
|
err := gui.Git.Sync.FastForward(branch.Name, branch.UpstreamRemote, branch.UpstreamBranch)
|
||||||
gui.handleCredentialsPopup(err)
|
if err != nil {
|
||||||
_ = gui.refreshSidePanels(refreshOptions{mode: ASYNC, scope: []RefreshableView{BRANCHES}})
|
_ = 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 {
|
func (gui *Gui) handleCreateResetToBranchMenu() error {
|
||||||
@ -444,13 +444,13 @@ func (gui *Gui) handleRenameBranch() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
promptForNewName := func() error {
|
promptForNewName := func() error {
|
||||||
return gui.prompt(promptOpts{
|
return gui.PopupHandler.Prompt(popup.PromptOpts{
|
||||||
title: gui.Tr.NewBranchNamePrompt + " " + branch.Name + ":",
|
Title: gui.Tr.NewBranchNamePrompt + " " + branch.Name + ":",
|
||||||
initialContent: branch.Name,
|
InitialContent: branch.Name,
|
||||||
handleConfirm: func(newBranchName string) error {
|
HandleConfirm: func(newBranchName string) error {
|
||||||
gui.logAction(gui.Tr.Actions.RenameBranch)
|
gui.logAction(gui.Tr.Actions.RenameBranch)
|
||||||
if err := gui.Git.Branch.Rename(branch.Name, newBranchName); err != nil {
|
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
|
// 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 promptForNewName()
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: gui.Tr.LcRenameBranch,
|
Title: gui.Tr.LcRenameBranch,
|
||||||
prompt: gui.Tr.RenameBranchWarning,
|
Prompt: gui.Tr.RenameBranchWarning,
|
||||||
handleConfirm: promptForNewName,
|
HandleConfirm: promptForNewName,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -513,10 +513,10 @@ func (gui *Gui) handleNewBranchOffCurrentItem() error {
|
|||||||
prefilledName = strings.SplitAfterN(item.ID(), "/", 2)[1]
|
prefilledName = strings.SplitAfterN(item.ID(), "/", 2)[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.prompt(promptOpts{
|
return gui.PopupHandler.Prompt(popup.PromptOpts{
|
||||||
title: message,
|
Title: message,
|
||||||
initialContent: prefilledName,
|
InitialContent: prefilledName,
|
||||||
handleConfirm: func(response string) error {
|
HandleConfirm: func(response string) error {
|
||||||
gui.logAction(gui.Tr.Actions.CreateBranch)
|
gui.logAction(gui.Tr.Actions.CreateBranch)
|
||||||
if err := gui.Git.Branch.New(sanitizedBranchName(response), item.ID()); err != nil {
|
if err := gui.Git.Branch.New(sanitizedBranchName(response), item.ID()); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -536,7 +536,7 @@ func (gui *Gui) handleNewBranchOffCurrentItem() error {
|
|||||||
|
|
||||||
gui.State.Panels.Branches.SelectedLineIdx = 0
|
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
|
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
|
// 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 err
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: gui.Tr.CherryPick,
|
Title: gui.Tr.CherryPick,
|
||||||
prompt: gui.Tr.SureCherryPick,
|
Prompt: gui.Tr.SureCherryPick,
|
||||||
handleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
return gui.WithWaitingStatus(gui.Tr.CherryPickingStatus, func() error {
|
return gui.PopupHandler.WithWaitingStatus(gui.Tr.CherryPickingStatus, func() error {
|
||||||
gui.logAction(gui.Tr.Actions.CherryPick)
|
gui.logAction(gui.Tr.Actions.CherryPick)
|
||||||
err := gui.Git.Rebase.CherryPickCommits(gui.State.Modes.CherryPicking.CherryPickedCommits)
|
err := gui.Git.Rebase.CherryPickCommits(gui.State.Modes.CherryPicking.CherryPickedCommits)
|
||||||
return gui.handleGenericMergeCommandResult(err)
|
return gui.handleGenericMergeCommandResult(err)
|
||||||
|
@ -4,6 +4,8 @@ import (
|
|||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/patch"
|
"github.com/jesseduffield/lazygit/pkg/commands/patch"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
|
"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 {
|
func (gui *Gui) getSelectedCommitFileNode() *filetree.CommitFileNode {
|
||||||
@ -65,10 +67,10 @@ func (gui *Gui) handleCheckoutCommitFile() error {
|
|||||||
|
|
||||||
gui.logAction(gui.Tr.Actions.CheckoutFile)
|
gui.logAction(gui.Tr.Actions.CheckoutFile)
|
||||||
if err := gui.Git.WorkingTree.CheckoutFile(gui.State.CommitFileTreeViewModel.GetParent(), node.GetPath()); err != nil {
|
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 {
|
func (gui *Gui) handleDiscardOldFileChange() error {
|
||||||
@ -78,11 +80,11 @@ func (gui *Gui) handleDiscardOldFileChange() error {
|
|||||||
|
|
||||||
fileName := gui.getSelectedCommitFileName()
|
fileName := gui.getSelectedCommitFileName()
|
||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: gui.Tr.DiscardFileChangesTitle,
|
Title: gui.Tr.DiscardFileChangesTitle,
|
||||||
prompt: gui.Tr.DiscardFileChangesPrompt,
|
Prompt: gui.Tr.DiscardFileChangesPrompt,
|
||||||
handleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
return gui.WithWaitingStatus(gui.Tr.RebasingStatus, func() error {
|
return gui.PopupHandler.WithWaitingStatus(gui.Tr.RebasingStatus, func() error {
|
||||||
gui.logAction(gui.Tr.Actions.DiscardOldFileChange)
|
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.Git.Rebase.DiscardOldFileChanges(gui.State.Commits, gui.State.Panels.Commits.SelectedLineIdx, fileName); err != nil {
|
||||||
if err := gui.handleGenericMergeCommandResult(err); 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)
|
files, err := gui.Git.Loaders.CommitFiles.GetFilesInDiff(from, to, reverse)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
gui.State.CommitFileTreeViewModel.SetParent(to)
|
gui.State.CommitFileTreeViewModel.SetParent(to)
|
||||||
gui.State.CommitFileTreeViewModel.SetFiles(files)
|
gui.State.CommitFileTreeViewModel.SetFiles(files)
|
||||||
@ -133,7 +135,7 @@ func (gui *Gui) handleEditCommitFile() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if node.File == nil {
|
if node.File == nil {
|
||||||
return gui.createErrorPanel(gui.Tr.ErrCannotEditDirectory)
|
return gui.PopupHandler.ErrorMsg(gui.Tr.ErrCannotEditDirectory)
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.editFile(node.GetPath())
|
return gui.editFile(node.GetPath())
|
||||||
@ -167,7 +169,7 @@ func (gui *Gui) handleToggleFileForPatch() error {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if gui.Git.Patch.PatchManager.IsEmpty() {
|
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() {
|
if gui.Git.Patch.PatchManager.Active() && gui.Git.Patch.PatchManager.To != gui.State.CommitFileTreeViewModel.GetParent() {
|
||||||
return gui.ask(askOpts{
|
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: gui.Tr.DiscardPatch,
|
Title: gui.Tr.DiscardPatch,
|
||||||
prompt: gui.Tr.DiscardPatchConfirm,
|
Prompt: gui.Tr.DiscardPatchConfirm,
|
||||||
handleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
gui.Git.Patch.PatchManager.Reset()
|
gui.Git.Patch.PatchManager.Reset()
|
||||||
return toggleTheFile()
|
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() {
|
if gui.Git.Patch.PatchManager.Active() && gui.Git.Patch.PatchManager.To != gui.State.CommitFileTreeViewModel.GetParent() {
|
||||||
return gui.ask(askOpts{
|
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: gui.Tr.DiscardPatch,
|
Title: gui.Tr.DiscardPatch,
|
||||||
prompt: gui.Tr.DiscardPatchConfirm,
|
Prompt: gui.Tr.DiscardPatchConfirm,
|
||||||
handleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
gui.Git.Patch.PatchManager.Reset()
|
gui.Git.Patch.PatchManager.Reset()
|
||||||
return enterTheFile()
|
return enterTheFile()
|
||||||
},
|
},
|
||||||
|
@ -12,7 +12,7 @@ func (gui *Gui) handleCommitConfirm() error {
|
|||||||
message := strings.TrimSpace(gui.Views.CommitMessage.TextArea.GetContent())
|
message := strings.TrimSpace(gui.Views.CommitMessage.TextArea.GetContent())
|
||||||
gui.State.failedCommitMessage = message
|
gui.State.failedCommitMessage = message
|
||||||
if message == "" {
|
if message == "" {
|
||||||
return gui.createErrorPanel(gui.Tr.CommitWithoutMessageErr)
|
return gui.PopupHandler.ErrorMsg(gui.Tr.CommitWithoutMessageErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmdObj := gui.Git.Commit.CommitCmdObj(message)
|
cmdObj := gui.Git.Commit.CommitCmdObj(message)
|
||||||
|
@ -6,6 +6,8 @@ import (
|
|||||||
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/loaders"
|
"github.com/jesseduffield/lazygit/pkg/commands/loaders"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"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"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -29,7 +31,7 @@ func (gui *Gui) onCommitFocus() error {
|
|||||||
state.LimitCommits = false
|
state.LimitCommits = false
|
||||||
go utils.Safe(func() {
|
go utils.Safe(func() {
|
||||||
if err := gui.refreshCommitsWithLimit(); err != nil {
|
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(),
|
FilterPath: gui.State.Modes.Filtering.GetPath(),
|
||||||
IncludeRebaseCommits: true,
|
IncludeRebaseCommits: true,
|
||||||
RefName: gui.refForLog(),
|
RefName: gui.refForLog(),
|
||||||
All: gui.State.ShowWholeGitGraph,
|
All: gui.ShowWholeGitGraph,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -170,7 +172,7 @@ func (gui *Gui) handleCommitSquashDown() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(gui.State.Commits) <= 1 {
|
if len(gui.State.Commits) <= 1 {
|
||||||
return gui.createErrorPanel(gui.Tr.YouNoCommitsToSquash)
|
return gui.PopupHandler.ErrorMsg(gui.Tr.YouNoCommitsToSquash)
|
||||||
}
|
}
|
||||||
|
|
||||||
applied, err := gui.handleMidRebaseCommand("squash")
|
applied, err := gui.handleMidRebaseCommand("squash")
|
||||||
@ -181,11 +183,11 @@ func (gui *Gui) handleCommitSquashDown() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: gui.Tr.Squash,
|
Title: gui.Tr.Squash,
|
||||||
prompt: gui.Tr.SureSquashThisCommit,
|
Prompt: gui.Tr.SureSquashThisCommit,
|
||||||
handleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
return gui.WithWaitingStatus(gui.Tr.SquashingStatus, func() error {
|
return gui.PopupHandler.WithWaitingStatus(gui.Tr.SquashingStatus, func() error {
|
||||||
gui.logAction(gui.Tr.Actions.SquashCommitDown)
|
gui.logAction(gui.Tr.Actions.SquashCommitDown)
|
||||||
err := gui.Git.Rebase.InteractiveRebase(gui.State.Commits, gui.State.Panels.Commits.SelectedLineIdx, "squash")
|
err := gui.Git.Rebase.InteractiveRebase(gui.State.Commits, gui.State.Panels.Commits.SelectedLineIdx, "squash")
|
||||||
return gui.handleGenericMergeCommandResult(err)
|
return gui.handleGenericMergeCommandResult(err)
|
||||||
@ -200,7 +202,7 @@ func (gui *Gui) handleCommitFixup() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(gui.State.Commits) <= 1 {
|
if len(gui.State.Commits) <= 1 {
|
||||||
return gui.createErrorPanel(gui.Tr.YouNoCommitsToSquash)
|
return gui.PopupHandler.ErrorMsg(gui.Tr.YouNoCommitsToSquash)
|
||||||
}
|
}
|
||||||
|
|
||||||
applied, err := gui.handleMidRebaseCommand("fixup")
|
applied, err := gui.handleMidRebaseCommand("fixup")
|
||||||
@ -211,11 +213,11 @@ func (gui *Gui) handleCommitFixup() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: gui.Tr.Fixup,
|
Title: gui.Tr.Fixup,
|
||||||
prompt: gui.Tr.SureFixupThisCommit,
|
Prompt: gui.Tr.SureFixupThisCommit,
|
||||||
handleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
return gui.WithWaitingStatus(gui.Tr.FixingStatus, func() error {
|
return gui.PopupHandler.WithWaitingStatus(gui.Tr.FixingStatus, func() error {
|
||||||
gui.logAction(gui.Tr.Actions.FixupCommit)
|
gui.logAction(gui.Tr.Actions.FixupCommit)
|
||||||
err := gui.Git.Rebase.InteractiveRebase(gui.State.Commits, gui.State.Panels.Commits.SelectedLineIdx, "fixup")
|
err := gui.Git.Rebase.InteractiveRebase(gui.State.Commits, gui.State.Panels.Commits.SelectedLineIdx, "fixup")
|
||||||
return gui.handleGenericMergeCommandResult(err)
|
return gui.handleGenericMergeCommandResult(err)
|
||||||
@ -244,20 +246,20 @@ func (gui *Gui) handleRewordCommit() error {
|
|||||||
|
|
||||||
message, err := gui.Git.Commit.GetCommitMessage(commit.Sha)
|
message, err := gui.Git.Commit.GetCommitMessage(commit.Sha)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: use the commit message panel here
|
// TODO: use the commit message panel here
|
||||||
return gui.prompt(promptOpts{
|
return gui.PopupHandler.Prompt(popup.PromptOpts{
|
||||||
title: gui.Tr.LcRewordCommit,
|
Title: gui.Tr.LcRewordCommit,
|
||||||
initialContent: message,
|
InitialContent: message,
|
||||||
handleConfirm: func(response string) error {
|
HandleConfirm: func(response string) error {
|
||||||
gui.logAction(gui.Tr.Actions.RewordCommit)
|
gui.logAction(gui.Tr.Actions.RewordCommit)
|
||||||
if err := gui.Git.Rebase.RewordCommit(gui.State.Commits, gui.State.Panels.Commits.SelectedLineIdx, response); err != nil {
|
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)
|
gui.logAction(gui.Tr.Actions.RewordCommit)
|
||||||
subProcess, err := gui.Git.Rebase.RewordCommitInEditor(gui.State.Commits, gui.State.Panels.Commits.SelectedLineIdx)
|
subProcess, err := gui.Git.Rebase.RewordCommitInEditor(gui.State.Commits, gui.State.Panels.Commits.SelectedLineIdx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
if subProcess != nil {
|
if subProcess != nil {
|
||||||
return gui.runSubprocessWithSuspenseAndRefresh(subProcess)
|
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
|
// 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.
|
// request us to edit the commit message when prompted.
|
||||||
if action == "reword" {
|
if action == "reword" {
|
||||||
return true, gui.createErrorPanel(gui.Tr.LcRewordNotSupported)
|
return true, gui.PopupHandler.ErrorMsg(gui.Tr.LcRewordNotSupported)
|
||||||
}
|
}
|
||||||
|
|
||||||
gui.logAction("Update rebase TODO")
|
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 {
|
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()
|
return true, gui.refreshRebaseCommits()
|
||||||
@ -330,11 +332,11 @@ func (gui *Gui) handleCommitDelete() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: gui.Tr.DeleteCommitTitle,
|
Title: gui.Tr.DeleteCommitTitle,
|
||||||
prompt: gui.Tr.DeleteCommitPrompt,
|
Prompt: gui.Tr.DeleteCommitPrompt,
|
||||||
handleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
return gui.WithWaitingStatus(gui.Tr.DeletingStatus, func() error {
|
return gui.PopupHandler.WithWaitingStatus(gui.Tr.DeletingStatus, func() error {
|
||||||
gui.logAction(gui.Tr.Actions.DropCommit)
|
gui.logAction(gui.Tr.Actions.DropCommit)
|
||||||
err := gui.Git.Rebase.InteractiveRebase(gui.State.Commits, gui.State.Panels.Commits.SelectedLineIdx, "drop")
|
err := gui.Git.Rebase.InteractiveRebase(gui.State.Commits, gui.State.Panels.Commits.SelectedLineIdx, "drop")
|
||||||
return gui.handleGenericMergeCommandResult(err)
|
return gui.handleGenericMergeCommandResult(err)
|
||||||
@ -361,13 +363,13 @@ func (gui *Gui) handleCommitMoveDown() error {
|
|||||||
gui.logCommand(fmt.Sprintf("Moving commit %s down", selectedCommit.ShortSha()), false)
|
gui.logCommand(fmt.Sprintf("Moving commit %s down", selectedCommit.ShortSha()), false)
|
||||||
|
|
||||||
if err := gui.Git.Rebase.MoveTodoDown(index); err != nil {
|
if err := gui.Git.Rebase.MoveTodoDown(index); err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
gui.State.Panels.Commits.SelectedLineIdx++
|
gui.State.Panels.Commits.SelectedLineIdx++
|
||||||
return gui.refreshRebaseCommits()
|
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)
|
gui.logAction(gui.Tr.Actions.MoveCommitDown)
|
||||||
err := gui.Git.Rebase.MoveCommitDown(gui.State.Commits, index)
|
err := gui.Git.Rebase.MoveCommitDown(gui.State.Commits, index)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@ -398,13 +400,13 @@ func (gui *Gui) handleCommitMoveUp() error {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if err := gui.Git.Rebase.MoveTodoDown(index - 1); err != nil {
|
if err := gui.Git.Rebase.MoveTodoDown(index - 1); err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
gui.State.Panels.Commits.SelectedLineIdx--
|
gui.State.Panels.Commits.SelectedLineIdx--
|
||||||
return gui.refreshRebaseCommits()
|
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)
|
gui.logAction(gui.Tr.Actions.MoveCommitUp)
|
||||||
err := gui.Git.Rebase.MoveCommitDown(gui.State.Commits, index-1)
|
err := gui.Git.Rebase.MoveCommitDown(gui.State.Commits, index-1)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@ -427,7 +429,7 @@ func (gui *Gui) handleCommitEdit() error {
|
|||||||
return nil
|
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)
|
gui.logAction(gui.Tr.Actions.EditCommit)
|
||||||
err = gui.Git.Rebase.InteractiveRebase(gui.State.Commits, gui.State.Panels.Commits.SelectedLineIdx, "edit")
|
err = gui.Git.Rebase.InteractiveRebase(gui.State.Commits, gui.State.Panels.Commits.SelectedLineIdx, "edit")
|
||||||
return gui.handleGenericMergeCommandResult(err)
|
return gui.handleGenericMergeCommandResult(err)
|
||||||
@ -439,11 +441,11 @@ func (gui *Gui) handleCommitAmendTo() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: gui.Tr.AmendCommitTitle,
|
Title: gui.Tr.AmendCommitTitle,
|
||||||
prompt: gui.Tr.AmendCommitPrompt,
|
Prompt: gui.Tr.AmendCommitPrompt,
|
||||||
handleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
return gui.WithWaitingStatus(gui.Tr.AmendingStatus, func() error {
|
return gui.PopupHandler.WithWaitingStatus(gui.Tr.AmendingStatus, func() error {
|
||||||
gui.logAction(gui.Tr.Actions.AmendCommit)
|
gui.logAction(gui.Tr.Actions.AmendCommit)
|
||||||
err := gui.Git.Rebase.AmendTo(gui.State.Commits[gui.State.Panels.Commits.SelectedLineIdx].Sha)
|
err := gui.Git.Rebase.AmendTo(gui.State.Commits[gui.State.Panels.Commits.SelectedLineIdx].Sha)
|
||||||
return gui.handleGenericMergeCommandResult(err)
|
return gui.handleGenericMergeCommandResult(err)
|
||||||
@ -478,17 +480,17 @@ func (gui *Gui) handleCommitRevert() error {
|
|||||||
if commit.IsMerge() {
|
if commit.IsMerge() {
|
||||||
return gui.createRevertMergeCommitMenu(commit)
|
return gui.createRevertMergeCommitMenu(commit)
|
||||||
} else {
|
} else {
|
||||||
return gui.ask(askOpts{
|
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: gui.Tr.Actions.RevertCommit,
|
Title: gui.Tr.Actions.RevertCommit,
|
||||||
prompt: utils.ResolvePlaceholderString(
|
Prompt: utils.ResolvePlaceholderString(
|
||||||
gui.Tr.ConfirmRevertCommit,
|
gui.Tr.ConfirmRevertCommit,
|
||||||
map[string]string{
|
map[string]string{
|
||||||
"selectedCommit": commit.ShortSha(),
|
"selectedCommit": commit.ShortSha(),
|
||||||
}),
|
}),
|
||||||
handleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
gui.logAction(gui.Tr.Actions.RevertCommit)
|
gui.logAction(gui.Tr.Actions.RevertCommit)
|
||||||
if err := gui.Git.Commit.Revert(commit.Sha); err != nil {
|
if err := gui.Git.Commit.Revert(commit.Sha); err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
return gui.afterRevertCommit()
|
return gui.afterRevertCommit()
|
||||||
},
|
},
|
||||||
@ -497,33 +499,33 @@ func (gui *Gui) handleCommitRevert() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) createRevertMergeCommitMenu(commit *models.Commit) 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 {
|
for i, parentSha := range commit.Parents {
|
||||||
i := i
|
i := i
|
||||||
message, err := gui.Git.Commit.GetCommitMessageFirstLine(parentSha)
|
message, err := gui.Git.Commit.GetCommitMessageFirstLine(parentSha)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
menuItems[i] = &menuItem{
|
menuItems[i] = &popup.MenuItem{
|
||||||
displayString: fmt.Sprintf("%s: %s", utils.SafeTruncate(parentSha, 8), message),
|
DisplayString: fmt.Sprintf("%s: %s", utils.SafeTruncate(parentSha, 8), message),
|
||||||
onPress: func() error {
|
OnPress: func() error {
|
||||||
parentNumber := i + 1
|
parentNumber := i + 1
|
||||||
gui.logAction(gui.Tr.Actions.RevertCommit)
|
gui.logAction(gui.Tr.Actions.RevertCommit)
|
||||||
if err := gui.Git.Commit.RevertMerge(commit.Sha, parentNumber); err != nil {
|
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.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 {
|
func (gui *Gui) afterRevertCommit() error {
|
||||||
gui.State.Panels.Commits.SelectedLineIdx++
|
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 {
|
func (gui *Gui) handleViewCommitFiles() error {
|
||||||
@ -552,16 +554,16 @@ func (gui *Gui) handleCreateFixupCommit() error {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: gui.Tr.CreateFixupCommit,
|
Title: gui.Tr.CreateFixupCommit,
|
||||||
prompt: prompt,
|
Prompt: prompt,
|
||||||
handleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
gui.logAction(gui.Tr.Actions.CreateFixupCommit)
|
gui.logAction(gui.Tr.Actions.CreateFixupCommit)
|
||||||
if err := gui.Git.Commit.CreateFixupCommit(commit.Sha); err != nil {
|
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{
|
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: gui.Tr.SquashAboveCommits,
|
Title: gui.Tr.SquashAboveCommits,
|
||||||
prompt: prompt,
|
Prompt: prompt,
|
||||||
handleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
return gui.WithWaitingStatus(gui.Tr.SquashingStatus, func() error {
|
return gui.PopupHandler.WithWaitingStatus(gui.Tr.SquashingStatus, func() error {
|
||||||
gui.logAction(gui.Tr.Actions.SquashAllAboveFixupCommits)
|
gui.logAction(gui.Tr.Actions.SquashAllAboveFixupCommits)
|
||||||
err := gui.Git.Rebase.SquashAllAboveFixupCommits(commit.Sha)
|
err := gui.Git.Rebase.SquashAllAboveFixupCommits(commit.Sha)
|
||||||
return gui.handleGenericMergeCommandResult(err)
|
return gui.handleGenericMergeCommandResult(err)
|
||||||
@ -606,39 +608,40 @@ func (gui *Gui) handleTagCommit() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) createTagMenu(commitSha string) error {
|
func (gui *Gui) createTagMenu(commitSha string) error {
|
||||||
items := []*menuItem{
|
return gui.PopupHandler.Menu(popup.CreateMenuOptions{
|
||||||
{
|
Title: gui.Tr.TagMenuTitle,
|
||||||
displayString: gui.Tr.LcLightweightTag,
|
Items: []*popup.MenuItem{
|
||||||
onPress: func() error {
|
{
|
||||||
return gui.handleCreateLightweightTag(commitSha)
|
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 {
|
func (gui *Gui) afterTagCreate() error {
|
||||||
gui.State.Panels.Tags.SelectedLineIdx = 0 // Set to the top
|
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 {
|
func (gui *Gui) handleCreateAnnotatedTag(commitSha string) error {
|
||||||
return gui.prompt(promptOpts{
|
return gui.PopupHandler.Prompt(popup.PromptOpts{
|
||||||
title: gui.Tr.TagNameTitle,
|
Title: gui.Tr.TagNameTitle,
|
||||||
handleConfirm: func(tagName string) error {
|
HandleConfirm: func(tagName string) error {
|
||||||
return gui.prompt(promptOpts{
|
return gui.PopupHandler.Prompt(popup.PromptOpts{
|
||||||
title: gui.Tr.TagMessageTitle,
|
Title: gui.Tr.TagMessageTitle,
|
||||||
handleConfirm: func(msg string) error {
|
HandleConfirm: func(msg string) error {
|
||||||
gui.logAction(gui.Tr.Actions.CreateAnnotatedTag)
|
gui.logAction(gui.Tr.Actions.CreateAnnotatedTag)
|
||||||
if err := gui.Git.Tag.CreateAnnotated(tagName, commitSha, msg); err != nil {
|
if err := gui.Git.Tag.CreateAnnotated(tagName, commitSha, msg); err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
return gui.afterTagCreate()
|
return gui.afterTagCreate()
|
||||||
},
|
},
|
||||||
@ -648,12 +651,12 @@ func (gui *Gui) handleCreateAnnotatedTag(commitSha string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleCreateLightweightTag(commitSha string) error {
|
func (gui *Gui) handleCreateLightweightTag(commitSha string) error {
|
||||||
return gui.prompt(promptOpts{
|
return gui.PopupHandler.Prompt(popup.PromptOpts{
|
||||||
title: gui.Tr.TagNameTitle,
|
Title: gui.Tr.TagNameTitle,
|
||||||
handleConfirm: func(tagName string) error {
|
HandleConfirm: func(tagName string) error {
|
||||||
gui.logAction(gui.Tr.Actions.CreateLightweightTag)
|
gui.logAction(gui.Tr.Actions.CreateLightweightTag)
|
||||||
if err := gui.Git.Tag.CreateLightweight(tagName, commitSha); err != nil {
|
if err := gui.Git.Tag.CreateLightweight(tagName, commitSha); err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
return gui.afterTagCreate()
|
return gui.afterTagCreate()
|
||||||
},
|
},
|
||||||
@ -666,10 +669,10 @@ func (gui *Gui) handleCheckoutCommit() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: gui.Tr.LcCheckoutCommit,
|
Title: gui.Tr.LcCheckoutCommit,
|
||||||
prompt: gui.Tr.SureCheckoutThisCommit,
|
Prompt: gui.Tr.SureCheckoutThisCommit,
|
||||||
handleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
gui.logAction(gui.Tr.Actions.CheckoutCommit)
|
gui.logAction(gui.Tr.Actions.CheckoutCommit)
|
||||||
return gui.handleCheckoutRef(commit.Sha, handleCheckoutRefOptions{})
|
return gui.handleCheckoutRef(commit.Sha, handleCheckoutRefOptions{})
|
||||||
},
|
},
|
||||||
@ -679,7 +682,7 @@ func (gui *Gui) handleCheckoutCommit() error {
|
|||||||
func (gui *Gui) handleCreateCommitResetMenu() error {
|
func (gui *Gui) handleCreateCommitResetMenu() error {
|
||||||
commit := gui.getSelectedLocalCommit()
|
commit := gui.getSelectedLocalCommit()
|
||||||
if commit == nil {
|
if commit == nil {
|
||||||
return gui.createErrorPanel(gui.Tr.NoCommitsThisBranch)
|
return gui.PopupHandler.ErrorMsg(gui.Tr.NoCommitsThisBranch)
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.createResetMenu(commit.Sha)
|
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
|
// we usually lazyload these commits but now that we're searching we need to load them now
|
||||||
if gui.State.Panels.Commits.LimitCommits {
|
if gui.State.Panels.Commits.LimitCommits {
|
||||||
gui.State.Panels.Commits.LimitCommits = false
|
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
|
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
|
// we usually lazyload these commits but now that we're searching we need to load them now
|
||||||
if gui.State.Panels.Commits.LimitCommits {
|
if gui.State.Panels.Commits.LimitCommits {
|
||||||
gui.State.Panels.Commits.LimitCommits = false
|
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
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -723,12 +726,12 @@ func (gui *Gui) handleCopySelectedCommitMessageToClipboard() error {
|
|||||||
|
|
||||||
message, err := gui.Git.Commit.GetCommitMessage(commit.Sha)
|
message, err := gui.Git.Commit.GetCommitMessage(commit.Sha)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
gui.logAction(gui.Tr.Actions.CopyCommitMessageToClipboard)
|
gui.logAction(gui.Tr.Actions.CopyCommitMessageToClipboard)
|
||||||
if err := gui.OSCommand.CopyToClipboard(message); err != nil {
|
if err := gui.OSCommand.CopyToClipboard(message); err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
gui.raiseToast(gui.Tr.CommitMessageCopiedToClipboard)
|
gui.raiseToast(gui.Tr.CommitMessageCopiedToClipboard)
|
||||||
@ -737,87 +740,87 @@ func (gui *Gui) handleCopySelectedCommitMessageToClipboard() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleOpenLogMenu() error {
|
func (gui *Gui) handleOpenLogMenu() error {
|
||||||
return gui.createMenu(gui.Tr.LogMenuTitle, []*menuItem{
|
return gui.PopupHandler.Menu(popup.CreateMenuOptions{
|
||||||
{
|
Title: gui.Tr.LogMenuTitle,
|
||||||
displayString: gui.Tr.ToggleShowGitGraphAll,
|
Items: []*popup.MenuItem{
|
||||||
onPress: func() error {
|
{
|
||||||
gui.State.ShowWholeGitGraph = !gui.State.ShowWholeGitGraph
|
DisplayString: gui.Tr.ToggleShowGitGraphAll,
|
||||||
|
OnPress: func() error {
|
||||||
|
gui.ShowWholeGitGraph = !gui.ShowWholeGitGraph
|
||||||
|
|
||||||
if gui.State.ShowWholeGitGraph {
|
if gui.ShowWholeGitGraph {
|
||||||
gui.State.Panels.Commits.LimitCommits = false
|
gui.State.Panels.Commits.LimitCommits = false
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.WithWaitingStatus(gui.Tr.LcLoadingCommits, func() error {
|
return gui.PopupHandler.WithWaitingStatus(gui.Tr.LcLoadingCommits, func() error {
|
||||||
return gui.refreshSidePanels(refreshOptions{mode: SYNC, scope: []RefreshableView{COMMITS}})
|
return gui.refreshSidePanels(types.RefreshOptions{Mode: types.SYNC, Scope: []types.RefreshableView{types.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.createMenu(gui.Tr.LogMenuTitle, []*menuItem{
|
},
|
||||||
{
|
{
|
||||||
displayString: "topological (topo-order)",
|
DisplayString: gui.Tr.ShowGitGraph,
|
||||||
onPress: func() error {
|
OpensMenu: true,
|
||||||
return onSelect("topo-order")
|
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 {
|
func (gui *Gui) handleOpenCommitInBrowser() error {
|
||||||
@ -830,12 +833,12 @@ func (gui *Gui) handleOpenCommitInBrowser() error {
|
|||||||
|
|
||||||
url, err := hostingServiceMgr.GetCommitURL(commit.Sha)
|
url, err := hostingServiceMgr.GetCommitURL(commit.Sha)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
gui.logAction(gui.Tr.Actions.OpenCommitInBrowser)
|
gui.logAction(gui.Tr.Actions.OpenCommitInBrowser)
|
||||||
if err := gui.OSCommand.OpenLink(url); err != nil {
|
if err := gui.OSCommand.OpenLink(url); err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -5,53 +5,12 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
"github.com/jesseduffield/lazygit/pkg/theme"
|
"github.com/jesseduffield/lazygit/pkg/theme"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"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 {
|
func (gui *Gui) wrappedConfirmationFunction(handlersManageFocus bool, function func() error) func() error {
|
||||||
return func() error {
|
return func() error {
|
||||||
if err := gui.closeConfirmationPrompt(handlersManageFocus); err != nil {
|
if err := gui.closeConfirmationPrompt(handlersManageFocus); err != nil {
|
||||||
@ -60,7 +19,7 @@ func (gui *Gui) wrappedConfirmationFunction(handlersManageFocus bool, function f
|
|||||||
|
|
||||||
if function != nil {
|
if function != nil {
|
||||||
if err := function(); err != 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 function != nil {
|
||||||
if err := function(getResponse()); err != 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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) createPopupPanel(opts createPopupPanelOpts) error {
|
func (gui *Gui) createPopupPanel(opts popup.CreatePopupPanelOpts) error {
|
||||||
// remove any previous keybindings
|
// remove any previous keybindings
|
||||||
gui.clearConfirmationViewKeyBindings()
|
gui.clearConfirmationViewKeyBindings()
|
||||||
|
|
||||||
err := gui.prepareConfirmationPanel(
|
err := gui.prepareConfirmationPanel(
|
||||||
opts.title,
|
opts.Title,
|
||||||
opts.prompt,
|
opts.Prompt,
|
||||||
opts.hasLoader,
|
opts.HasLoader,
|
||||||
opts.findSuggestionsFunc,
|
opts.FindSuggestionsFunc,
|
||||||
opts.editable,
|
opts.Editable,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
confirmationView := gui.Views.Confirmation
|
confirmationView := gui.Views.Confirmation
|
||||||
confirmationView.Editable = opts.editable
|
confirmationView.Editable = opts.Editable
|
||||||
confirmationView.Editor = gocui.EditorFunc(gui.defaultEditor)
|
confirmationView.Editor = gocui.EditorFunc(gui.defaultEditor)
|
||||||
|
|
||||||
if opts.editable {
|
if opts.Editable {
|
||||||
textArea := confirmationView.TextArea
|
textArea := confirmationView.TextArea
|
||||||
textArea.Clear()
|
textArea.Clear()
|
||||||
textArea.TypeString(opts.prompt)
|
textArea.TypeString(opts.Prompt)
|
||||||
confirmationView.RenderTextArea()
|
confirmationView.RenderTextArea()
|
||||||
} else {
|
} else {
|
||||||
if err := gui.renderString(confirmationView, opts.prompt); err != nil {
|
if err := gui.renderString(confirmationView, opts.Prompt); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -215,7 +174,7 @@ func (gui *Gui) createPopupPanel(opts createPopupPanelOpts) error {
|
|||||||
return gui.pushContext(gui.State.Contexts.Confirmation)
|
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(
|
actions := utils.ResolvePlaceholderString(
|
||||||
gui.Tr.CloseConfirm,
|
gui.Tr.CloseConfirm,
|
||||||
map[string]string{
|
map[string]string{
|
||||||
@ -226,10 +185,10 @@ func (gui *Gui) setKeyBindings(opts createPopupPanelOpts) error {
|
|||||||
|
|
||||||
_ = gui.renderString(gui.Views.Options, actions)
|
_ = gui.renderString(gui.Views.Options, actions)
|
||||||
var onConfirm func() error
|
var onConfirm func() error
|
||||||
if opts.handleConfirmPrompt != nil {
|
if opts.HandleConfirmPrompt != nil {
|
||||||
onConfirm = gui.wrappedPromptConfirmationFunction(opts.handlersManageFocus, opts.handleConfirmPrompt, func() string { return gui.Views.Confirmation.TextArea.GetContent() })
|
onConfirm = gui.wrappedPromptConfirmationFunction(opts.HandlersManageFocus, opts.HandleConfirmPrompt, func() string { return gui.Views.Confirmation.TextArea.GetContent() })
|
||||||
} else {
|
} else {
|
||||||
onConfirm = gui.wrappedConfirmationFunction(opts.handlersManageFocus, opts.handleConfirm)
|
onConfirm = gui.wrappedConfirmationFunction(opts.HandlersManageFocus, opts.HandleConfirm)
|
||||||
}
|
}
|
||||||
|
|
||||||
type confirmationKeybinding struct {
|
type confirmationKeybinding struct {
|
||||||
@ -240,8 +199,8 @@ func (gui *Gui) setKeyBindings(opts createPopupPanelOpts) error {
|
|||||||
|
|
||||||
keybindingConfig := gui.UserConfig.Keybinding
|
keybindingConfig := gui.UserConfig.Keybinding
|
||||||
onSuggestionConfirm := gui.wrappedPromptConfirmationFunction(
|
onSuggestionConfirm := gui.wrappedPromptConfirmationFunction(
|
||||||
opts.handlersManageFocus,
|
opts.HandlersManageFocus,
|
||||||
opts.handleConfirmPrompt,
|
opts.HandleConfirmPrompt,
|
||||||
gui.getSelectedSuggestionValue,
|
gui.getSelectedSuggestionValue,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -259,7 +218,7 @@ func (gui *Gui) setKeyBindings(opts createPopupPanelOpts) error {
|
|||||||
{
|
{
|
||||||
viewName: "confirmation",
|
viewName: "confirmation",
|
||||||
key: gui.getKey(keybindingConfig.Universal.Return),
|
key: gui.getKey(keybindingConfig.Universal.Return),
|
||||||
handler: gui.wrappedConfirmationFunction(opts.handlersManageFocus, opts.handleClose),
|
handler: gui.wrappedConfirmationFunction(opts.HandlersManageFocus, opts.HandleClose),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
viewName: "confirmation",
|
viewName: "confirmation",
|
||||||
@ -284,7 +243,7 @@ func (gui *Gui) setKeyBindings(opts createPopupPanelOpts) error {
|
|||||||
{
|
{
|
||||||
viewName: "suggestions",
|
viewName: "suggestions",
|
||||||
key: gui.getKey(keybindingConfig.Universal.Return),
|
key: gui.getKey(keybindingConfig.Universal.Return),
|
||||||
handler: gui.wrappedConfirmationFunction(opts.handlersManageFocus, opts.handleClose),
|
handler: gui.wrappedConfirmationFunction(opts.HandlersManageFocus, opts.HandleClose),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
viewName: "suggestions",
|
viewName: "suggestions",
|
||||||
@ -317,19 +276,3 @@ func (gui *Gui) wrappedHandler(f func() error) func(g *gocui.Gui, v *gocui.View)
|
|||||||
return f()
|
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,
|
OnGetOptionsMap: gui.getMergingOptions,
|
||||||
},
|
},
|
||||||
Credentials: &BasicContext{
|
Credentials: &BasicContext{
|
||||||
OnFocus: OnFocusWrapper(gui.handleCredentialsViewFocused),
|
OnFocus: OnFocusWrapper(gui.handleAskFocused),
|
||||||
Kind: PERSISTENT_POPUP,
|
Kind: PERSISTENT_POPUP,
|
||||||
ViewName: "credentials",
|
ViewName: "credentials",
|
||||||
Key: CREDENTIALS_CONTEXT_KEY,
|
Key: CREDENTIALS_CONTEXT_KEY,
|
||||||
},
|
},
|
||||||
Confirmation: &BasicContext{
|
Confirmation: &BasicContext{
|
||||||
|
OnFocus: OnFocusWrapper(gui.handleAskFocused),
|
||||||
Kind: TEMPORARY_POPUP,
|
Kind: TEMPORARY_POPUP,
|
||||||
ViewName: "confirmation",
|
ViewName: "confirmation",
|
||||||
Key: CONFIRMATION_CONTEXT_KEY,
|
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"
|
"strings"
|
||||||
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -48,15 +49,16 @@ func (gui *Gui) handleSubmitCredential() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC})
|
return gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleCloseCredentialsView() error {
|
func (gui *Gui) handleCloseCredentialsView() error {
|
||||||
|
gui.Views.Credentials.ClearTextArea()
|
||||||
gui.credentials <- ""
|
gui.credentials <- ""
|
||||||
return gui.returnFromContext()
|
return gui.returnFromContext()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleCredentialsViewFocused() error {
|
func (gui *Gui) handleAskFocused() error {
|
||||||
keybindingConfig := gui.UserConfig.Keybinding
|
keybindingConfig := gui.UserConfig.Keybinding
|
||||||
|
|
||||||
message := utils.ResolvePlaceholderString(
|
message := utils.ResolvePlaceholderString(
|
||||||
@ -69,18 +71,3 @@ func (gui *Gui) handleCredentialsViewFocused() error {
|
|||||||
|
|
||||||
return gui.renderString(gui.Views.Options, message)
|
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/gocui"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
"github.com/jesseduffield/lazygit/pkg/config"
|
"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/style"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"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 {
|
func (gui *Gui) inputPrompt(prompt config.CustomCommandPrompt, promptResponses []string, responseIdx int, wrappedF func() error) error {
|
||||||
title, err := gui.resolveTemplate(prompt.Title, promptResponses)
|
title, err := gui.resolveTemplate(prompt.Title, promptResponses)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
initialValue, err := gui.resolveTemplate(prompt.InitialValue, promptResponses)
|
initialValue, err := gui.resolveTemplate(prompt.InitialValue, promptResponses)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.prompt(promptOpts{
|
return gui.PopupHandler.Prompt(popup.PromptOpts{
|
||||||
title: title,
|
Title: title,
|
||||||
initialContent: initialValue,
|
InitialContent: initialValue,
|
||||||
handleConfirm: func(str string) error {
|
HandleConfirm: func(str string) error {
|
||||||
promptResponses[responseIdx] = str
|
promptResponses[responseIdx] = str
|
||||||
return wrappedF()
|
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 {
|
func (gui *Gui) menuPrompt(prompt config.CustomCommandPrompt, promptResponses []string, responseIdx int, wrappedF func() error) error {
|
||||||
// need to make a menu here some how
|
// 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 {
|
for i, option := range prompt.Options {
|
||||||
option := option
|
option := option
|
||||||
|
|
||||||
@ -93,22 +95,22 @@ func (gui *Gui) menuPrompt(prompt config.CustomCommandPrompt, promptResponses []
|
|||||||
}
|
}
|
||||||
name, err := gui.resolveTemplate(nameTemplate, promptResponses)
|
name, err := gui.resolveTemplate(nameTemplate, promptResponses)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
description, err := gui.resolveTemplate(option.Description, promptResponses)
|
description, err := gui.resolveTemplate(option.Description, promptResponses)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
value, err := gui.resolveTemplate(option.Value, promptResponses)
|
value, err := gui.resolveTemplate(option.Value, promptResponses)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
menuItems[i] = &menuItem{
|
menuItems[i] = &popup.MenuItem{
|
||||||
displayStrings: []string{name, style.FgYellow.Sprint(description)},
|
DisplayStrings: []string{name, style.FgYellow.Sprint(description)},
|
||||||
onPress: func() error {
|
OnPress: func() error {
|
||||||
promptResponses[responseIdx] = value
|
promptResponses[responseIdx] = value
|
||||||
return wrappedF()
|
return wrappedF()
|
||||||
},
|
},
|
||||||
@ -117,30 +119,30 @@ func (gui *Gui) menuPrompt(prompt config.CustomCommandPrompt, promptResponses []
|
|||||||
|
|
||||||
title, err := gui.resolveTemplate(prompt.Title, promptResponses)
|
title, err := gui.resolveTemplate(prompt.Title, promptResponses)
|
||||||
if err != nil {
|
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) {
|
func (gui *Gui) GenerateMenuCandidates(commandOutput, filter, valueFormat, labelFormat string) ([]commandMenuEntry, error) {
|
||||||
reg, err := regexp.Compile(filter)
|
reg, err := regexp.Compile(filter)
|
||||||
if err != nil {
|
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)
|
buff := bytes.NewBuffer(nil)
|
||||||
|
|
||||||
valueTemp, err := template.New("format").Parse(valueFormat)
|
valueTemp, err := template.New("format").Parse(valueFormat)
|
||||||
if err != nil {
|
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{})
|
colorFuncMap := style.TemplateFuncMapAddColors(template.FuncMap{})
|
||||||
|
|
||||||
descTemp, err := template.New("format").Funcs(colorFuncMap).Parse(labelFormat)
|
descTemp, err := template.New("format").Funcs(colorFuncMap).Parse(labelFormat)
|
||||||
if err != nil {
|
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{}
|
candidates := []commandMenuEntry{}
|
||||||
@ -165,7 +167,7 @@ func (gui *Gui) GenerateMenuCandidates(commandOutput, filter, valueFormat, label
|
|||||||
|
|
||||||
err = valueTemp.Execute(buff, tmplData)
|
err = valueTemp.Execute(buff, tmplData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return candidates, gui.surfaceError(err)
|
return candidates, gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
entry := commandMenuEntry{
|
entry := commandMenuEntry{
|
||||||
value: strings.TrimSpace(buff.String()),
|
value: strings.TrimSpace(buff.String()),
|
||||||
@ -175,7 +177,7 @@ func (gui *Gui) GenerateMenuCandidates(commandOutput, filter, valueFormat, label
|
|||||||
buff.Reset()
|
buff.Reset()
|
||||||
err = descTemp.Execute(buff, tmplData)
|
err = descTemp.Execute(buff, tmplData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return candidates, gui.surfaceError(err)
|
return candidates, gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
entry.label = strings.TrimSpace(buff.String())
|
entry.label = strings.TrimSpace(buff.String())
|
||||||
} else {
|
} else {
|
||||||
@ -193,33 +195,33 @@ func (gui *Gui) menuPromptFromCommand(prompt config.CustomCommandPrompt, promptR
|
|||||||
// Collect cmd to run from config
|
// Collect cmd to run from config
|
||||||
cmdStr, err := gui.resolveTemplate(prompt.Command, promptResponses)
|
cmdStr, err := gui.resolveTemplate(prompt.Command, promptResponses)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect Filter regexp
|
// Collect Filter regexp
|
||||||
filter, err := gui.resolveTemplate(prompt.Filter, promptResponses)
|
filter, err := gui.resolveTemplate(prompt.Filter, promptResponses)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run and save output
|
// Run and save output
|
||||||
message, err := gui.Git.Custom.RunWithOutput(cmdStr)
|
message, err := gui.Git.Custom.RunWithOutput(cmdStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Need to make a menu out of what the cmd has displayed
|
// Need to make a menu out of what the cmd has displayed
|
||||||
candidates, err := gui.GenerateMenuCandidates(message, filter, prompt.ValueFormat, prompt.LabelFormat)
|
candidates, err := gui.GenerateMenuCandidates(message, filter, prompt.ValueFormat, prompt.LabelFormat)
|
||||||
if err != nil {
|
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 {
|
for i := range candidates {
|
||||||
i := i
|
i := i
|
||||||
menuItems[i] = &menuItem{
|
menuItems[i] = &popup.MenuItem{
|
||||||
displayStrings: []string{candidates[i].label},
|
DisplayStrings: []string{candidates[i].label},
|
||||||
onPress: func() error {
|
OnPress: func() error {
|
||||||
promptResponses[responseIdx] = candidates[i].value
|
promptResponses[responseIdx] = candidates[i].value
|
||||||
return wrappedF()
|
return wrappedF()
|
||||||
},
|
},
|
||||||
@ -228,10 +230,10 @@ func (gui *Gui) menuPromptFromCommand(prompt config.CustomCommandPrompt, promptR
|
|||||||
|
|
||||||
title, err := gui.resolveTemplate(prompt.Title, promptResponses)
|
title, err := gui.resolveTemplate(prompt.Title, promptResponses)
|
||||||
if err != nil {
|
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 {
|
func (gui *Gui) handleCustomCommandKeybinding(customCommand config.CustomCommand) func() error {
|
||||||
@ -241,7 +243,7 @@ func (gui *Gui) handleCustomCommandKeybinding(customCommand config.CustomCommand
|
|||||||
f := func() error {
|
f := func() error {
|
||||||
cmdStr, err := gui.resolveTemplate(customCommand.Command, promptResponses)
|
cmdStr, err := gui.resolveTemplate(customCommand.Command, promptResponses)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if customCommand.Subprocess {
|
if customCommand.Subprocess {
|
||||||
@ -252,7 +254,7 @@ func (gui *Gui) handleCustomCommandKeybinding(customCommand config.CustomCommand
|
|||||||
if loadingText == "" {
|
if loadingText == "" {
|
||||||
loadingText = gui.Tr.LcRunningCustomCommandStatus
|
loadingText = gui.Tr.LcRunningCustomCommandStatus
|
||||||
}
|
}
|
||||||
return gui.WithWaitingStatus(loadingText, func() error {
|
return gui.PopupHandler.WithWaitingStatus(loadingText, func() error {
|
||||||
gui.logAction(gui.Tr.Actions.CustomCommand)
|
gui.logAction(gui.Tr.Actions.CustomCommand)
|
||||||
cmdObj := gui.OSCommand.Cmd.NewShell(cmdStr)
|
cmdObj := gui.OSCommand.Cmd.NewShell(cmdStr)
|
||||||
if customCommand.Stream {
|
if customCommand.Stream {
|
||||||
@ -260,9 +262,9 @@ func (gui *Gui) handleCustomCommandKeybinding(customCommand config.CustomCommand
|
|||||||
}
|
}
|
||||||
err := cmdObj.Run()
|
err := cmdObj.Run()
|
||||||
if err != nil {
|
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)
|
return gui.menuPromptFromCommand(prompt, promptResponses, idx, wrappedF)
|
||||||
}
|
}
|
||||||
default:
|
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 {
|
func (gui *Gui) GetCustomCommandKeybindings() []*types.Binding {
|
||||||
bindings := []*Binding{}
|
bindings := []*types.Binding{}
|
||||||
customCommands := gui.UserConfig.CustomCommands
|
customCommands := gui.UserConfig.CustomCommands
|
||||||
|
|
||||||
for _, customCommand := range customCommands {
|
for _, customCommand := range customCommands {
|
||||||
@ -334,7 +336,7 @@ func (gui *Gui) GetCustomCommandKeybindings() []*Binding {
|
|||||||
description = customCommand.Command
|
description = customCommand.Command
|
||||||
}
|
}
|
||||||
|
|
||||||
bindings = append(bindings, &Binding{
|
bindings = append(bindings, &types.Binding{
|
||||||
ViewName: viewName,
|
ViewName: viewName,
|
||||||
Contexts: contexts,
|
Contexts: contexts,
|
||||||
Key: gui.getKey(customCommand.Key),
|
Key: gui.getKey(customCommand.Key),
|
||||||
|
@ -28,7 +28,7 @@ func isShowingDiff(gui *Gui) bool {
|
|||||||
func (gui *Gui) IncreaseContextInDiffView() error {
|
func (gui *Gui) IncreaseContextInDiffView() error {
|
||||||
if isShowingDiff(gui) {
|
if isShowingDiff(gui) {
|
||||||
if err := gui.CheckCanChangeContext(); err != nil {
|
if err := gui.CheckCanChangeContext(); err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
gui.UserConfig.Git.DiffContextSize = gui.UserConfig.Git.DiffContextSize + 1
|
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 isShowingDiff(gui) && old_size > 1 {
|
||||||
if err := gui.CheckCanChangeContext(); err != nil {
|
if err := gui.CheckCanChangeContext(); err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
gui.UserConfig.Git.DiffContextSize = old_size - 1
|
gui.UserConfig.Git.DiffContextSize = old_size - 1
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/patch"
|
"github.com/jesseduffield/lazygit/pkg/commands/patch"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -144,8 +145,8 @@ func TestDoesntIncreaseContextInDiffViewInContextWhenInPatchBuildingMode(t *test
|
|||||||
gui.Git.Patch.PatchManager.Start("from", "to", false, false)
|
gui.Git.Patch.PatchManager.Start("from", "to", false, false)
|
||||||
|
|
||||||
errorCount := 0
|
errorCount := 0
|
||||||
gui.PopupHandler = &TestPopupHandler{
|
gui.PopupHandler = &popup.TestPopupHandler{
|
||||||
onError: func(message string) error {
|
OnErrorMsg: func(message string) error {
|
||||||
assert.Equal(t, gui.Tr.CantChangeContextSizeError, message)
|
assert.Equal(t, gui.Tr.CantChangeContextSizeError, message)
|
||||||
errorCount += 1
|
errorCount += 1
|
||||||
return nil
|
return nil
|
||||||
@ -166,8 +167,8 @@ func TestDoesntDecreaseContextInDiffViewInContextWhenInPatchBuildingMode(t *test
|
|||||||
gui.Git.Patch.PatchManager.Start("from", "to", false, false)
|
gui.Git.Patch.PatchManager.Start("from", "to", false, false)
|
||||||
|
|
||||||
errorCount := 0
|
errorCount := 0
|
||||||
gui.PopupHandler = &TestPopupHandler{
|
gui.PopupHandler = &popup.TestPopupHandler{
|
||||||
onError: func(message string) error {
|
OnErrorMsg: func(message string) error {
|
||||||
assert.Equal(t, gui.Tr.CantChangeContextSizeError, message)
|
assert.Equal(t, gui.Tr.CantChangeContextSizeError, message)
|
||||||
errorCount += 1
|
errorCount += 1
|
||||||
return nil
|
return nil
|
||||||
|
@ -5,11 +5,13 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/modes/diffing"
|
"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 {
|
func (gui *Gui) exitDiffMode() error {
|
||||||
gui.State.Modes.Diffing = diffing.New()
|
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 {
|
func (gui *Gui) renderDiff() error {
|
||||||
@ -105,31 +107,31 @@ func (gui *Gui) diffStr() string {
|
|||||||
func (gui *Gui) handleCreateDiffingMenuPanel() error {
|
func (gui *Gui) handleCreateDiffingMenuPanel() error {
|
||||||
names := gui.currentDiffTerminals()
|
names := gui.currentDiffTerminals()
|
||||||
|
|
||||||
menuItems := []*menuItem{}
|
menuItems := []*popup.MenuItem{}
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
name := name
|
name := name
|
||||||
menuItems = append(menuItems, []*menuItem{
|
menuItems = append(menuItems, []*popup.MenuItem{
|
||||||
{
|
{
|
||||||
displayString: fmt.Sprintf("%s %s", gui.Tr.LcDiff, name),
|
DisplayString: fmt.Sprintf("%s %s", gui.Tr.LcDiff, name),
|
||||||
onPress: func() error {
|
OnPress: func() error {
|
||||||
gui.State.Modes.Diffing.Ref = name
|
gui.State.Modes.Diffing.Ref = name
|
||||||
// can scope this down based on current view but too lazy right now
|
// 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,
|
DisplayString: gui.Tr.LcEnterRefToDiff,
|
||||||
onPress: func() error {
|
OnPress: func() error {
|
||||||
return gui.prompt(promptOpts{
|
return gui.PopupHandler.Prompt(popup.PromptOpts{
|
||||||
title: gui.Tr.LcEnteRefName,
|
Title: gui.Tr.LcEnteRefName,
|
||||||
findSuggestionsFunc: gui.getRefsSuggestionsFunc(),
|
FindSuggestionsFunc: gui.getRefsSuggestionsFunc(),
|
||||||
handleConfirm: func(response string) error {
|
HandleConfirm: func(response string) error {
|
||||||
gui.State.Modes.Diffing.Ref = strings.TrimSpace(response)
|
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() {
|
if gui.State.Modes.Diffing.Active() {
|
||||||
menuItems = append(menuItems, []*menuItem{
|
menuItems = append(menuItems, []*popup.MenuItem{
|
||||||
{
|
{
|
||||||
displayString: gui.Tr.LcSwapDiff,
|
DisplayString: gui.Tr.LcSwapDiff,
|
||||||
onPress: func() error {
|
OnPress: func() error {
|
||||||
gui.State.Modes.Diffing.Reverse = !gui.State.Modes.Diffing.Reverse
|
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,
|
DisplayString: gui.Tr.LcExitDiffMode,
|
||||||
onPress: func() error {
|
OnPress: func() error {
|
||||||
gui.State.Modes.Diffing = diffing.New()
|
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
|
package gui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
|
)
|
||||||
|
|
||||||
func (gui *Gui) handleCreateDiscardMenu() error {
|
func (gui *Gui) handleCreateDiscardMenu() error {
|
||||||
node := gui.getSelectedFileNode()
|
node := gui.getSelectedFileNode()
|
||||||
if node == nil {
|
if node == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var menuItems []*menuItem
|
var menuItems []*popup.MenuItem
|
||||||
if node.File == nil {
|
if node.File == nil {
|
||||||
menuItems = []*menuItem{
|
menuItems = []*popup.MenuItem{
|
||||||
{
|
{
|
||||||
displayString: gui.Tr.LcDiscardAllChanges,
|
DisplayString: gui.Tr.LcDiscardAllChanges,
|
||||||
onPress: func() error {
|
OnPress: func() error {
|
||||||
gui.logAction(gui.Tr.Actions.DiscardAllChangesInDirectory)
|
gui.logAction(gui.Tr.Actions.DiscardAllChangesInDirectory)
|
||||||
if err := gui.Git.WorkingTree.DiscardAllDirChanges(node); err != nil {
|
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() {
|
if node.GetHasStagedChanges() && node.GetHasUnstagedChanges() {
|
||||||
menuItems = append(menuItems, &menuItem{
|
menuItems = append(menuItems, &popup.MenuItem{
|
||||||
displayString: gui.Tr.LcDiscardUnstagedChanges,
|
DisplayString: gui.Tr.LcDiscardUnstagedChanges,
|
||||||
onPress: func() error {
|
OnPress: func() error {
|
||||||
gui.logAction(gui.Tr.Actions.DiscardUnstagedChangesInDirectory)
|
gui.logAction(gui.Tr.Actions.DiscardUnstagedChangesInDirectory)
|
||||||
if err := gui.Git.WorkingTree.DiscardUnstagedDirChanges(node); err != nil {
|
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) {
|
if file.IsSubmodule(submodules) {
|
||||||
submodule := file.SubmoduleConfig(submodules)
|
submodule := file.SubmoduleConfig(submodules)
|
||||||
|
|
||||||
menuItems = []*menuItem{
|
menuItems = []*popup.MenuItem{
|
||||||
{
|
{
|
||||||
displayString: gui.Tr.LcSubmoduleStashAndReset,
|
DisplayString: gui.Tr.LcSubmoduleStashAndReset,
|
||||||
onPress: func() error {
|
OnPress: func() error {
|
||||||
return gui.handleResetSubmodule(submodule)
|
return gui.resetSubmodule(submodule)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
menuItems = []*menuItem{
|
menuItems = []*popup.MenuItem{
|
||||||
{
|
{
|
||||||
displayString: gui.Tr.LcDiscardAllChanges,
|
DisplayString: gui.Tr.LcDiscardAllChanges,
|
||||||
onPress: func() error {
|
OnPress: func() error {
|
||||||
gui.logAction(gui.Tr.Actions.DiscardAllChangesInFile)
|
gui.logAction(gui.Tr.Actions.DiscardAllChangesInFile)
|
||||||
if err := gui.Git.WorkingTree.DiscardAllFileChanges(file); err != nil {
|
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 {
|
if file.HasStagedChanges && file.HasUnstagedChanges {
|
||||||
menuItems = append(menuItems, &menuItem{
|
menuItems = append(menuItems, &popup.MenuItem{
|
||||||
displayString: gui.Tr.LcDiscardUnstagedChanges,
|
DisplayString: gui.Tr.LcDiscardUnstagedChanges,
|
||||||
onPress: func() error {
|
OnPress: func() error {
|
||||||
gui.logAction(gui.Tr.Actions.DiscardAllUnstagedChangesInFile)
|
gui.logAction(gui.Tr.Actions.DiscardAllUnstagedChangesInFile)
|
||||||
if err := gui.Git.WorkingTree.DiscardUnstagedFileChanges(file); err != nil {
|
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 (
|
import (
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (gui *Gui) handleCreateExtrasMenuPanel() error {
|
func (gui *Gui) handleCreateExtrasMenuPanel() error {
|
||||||
menuItems := []*menuItem{
|
return gui.PopupHandler.Menu(popup.CreateMenuOptions{
|
||||||
{
|
Title: gui.Tr.CommandLog,
|
||||||
displayString: gui.Tr.ToggleShowCommandLog,
|
Items: []*popup.MenuItem{
|
||||||
onPress: func() error {
|
{
|
||||||
currentContext := gui.currentStaticContext()
|
DisplayString: gui.Tr.ToggleShowCommandLog,
|
||||||
if gui.ShowExtrasWindow && currentContext.GetKey() == COMMAND_LOG_CONTEXT_KEY {
|
OnPress: func() error {
|
||||||
if err := gui.returnFromContext(); err != nil {
|
currentContext := gui.currentStaticContext()
|
||||||
return err
|
if gui.ShowExtrasWindow && currentContext.GetKey() == COMMAND_LOG_CONTEXT_KEY {
|
||||||
|
if err := gui.returnFromContext(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
show := !gui.ShowExtrasWindow
|
||||||
show := !gui.ShowExtrasWindow
|
gui.ShowExtrasWindow = show
|
||||||
gui.ShowExtrasWindow = show
|
gui.Config.GetAppState().HideCommandLog = !show
|
||||||
gui.Config.GetAppState().HideCommandLog = !show
|
_ = gui.Config.SaveAppState()
|
||||||
_ = gui.Config.SaveAppState()
|
return nil
|
||||||
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 {
|
func (gui *Gui) handleFocusCommandLog() error {
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/fsnotify/fsnotify"
|
"github.com/fsnotify/fsnotify"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
@ -117,7 +118,7 @@ func (gui *Gui) watchFilesForChanges() {
|
|||||||
}
|
}
|
||||||
// only refresh if we're not already
|
// only refresh if we're not already
|
||||||
if !gui.State.IsRefreshingFiles {
|
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
|
// watch for errors
|
||||||
|
@ -12,6 +12,8 @@ import (
|
|||||||
"github.com/jesseduffield/lazygit/pkg/config"
|
"github.com/jesseduffield/lazygit/pkg/config"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
|
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/mergeconflicts"
|
"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"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -67,7 +69,7 @@ func (gui *Gui) filesRenderToMain() error {
|
|||||||
|
|
||||||
gui.resetMergeStateWithLock()
|
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{
|
refreshOpts := refreshMainOpts{main: &viewUpdateOpts{
|
||||||
title: gui.Tr.UnstagedChanges,
|
title: gui.Tr.UnstagedChanges,
|
||||||
@ -76,7 +78,7 @@ func (gui *Gui) filesRenderToMain() error {
|
|||||||
|
|
||||||
if node.GetHasUnstagedChanges() {
|
if node.GetHasUnstagedChanges() {
|
||||||
if node.GetHasStagedChanges() {
|
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{
|
refreshOpts.secondary = &viewUpdateOpts{
|
||||||
title: gui.Tr.StagedChanges,
|
title: gui.Tr.StagedChanges,
|
||||||
@ -191,7 +193,7 @@ func (gui *Gui) enterFile(opts OnFocusOpts) error {
|
|||||||
return gui.switchToMerge()
|
return gui.switchToMerge()
|
||||||
}
|
}
|
||||||
if file.HasMergeConflicts {
|
if file.HasMergeConflicts {
|
||||||
return gui.createErrorPanel(gui.Tr.FileStagingRequirements)
|
return gui.PopupHandler.ErrorMsg(gui.Tr.FileStagingRequirements)
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.pushContext(gui.State.Contexts.Staging, opts)
|
return gui.pushContext(gui.State.Contexts.Staging, opts)
|
||||||
@ -213,36 +215,36 @@ func (gui *Gui) handleFilePress() error {
|
|||||||
if file.HasUnstagedChanges {
|
if file.HasUnstagedChanges {
|
||||||
gui.logAction(gui.Tr.Actions.StageFile)
|
gui.logAction(gui.Tr.Actions.StageFile)
|
||||||
if err := gui.Git.WorkingTree.StageFile(file.Name); err != nil {
|
if err := gui.Git.WorkingTree.StageFile(file.Name); err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
gui.logAction(gui.Tr.Actions.UnstageFile)
|
gui.logAction(gui.Tr.Actions.UnstageFile)
|
||||||
if err := gui.Git.WorkingTree.UnStageFile(file.Names(), file.Tracked); err != nil {
|
if err := gui.Git.WorkingTree.UnStageFile(file.Names(), file.Tracked); err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// if any files within have inline merge conflicts we can't stage or unstage,
|
// if any files within have inline merge conflicts we can't stage or unstage,
|
||||||
// or it'll end up with those >>>>>> lines actually staged
|
// or it'll end up with those >>>>>> lines actually staged
|
||||||
if node.GetHasInlineMergeConflicts() {
|
if node.GetHasInlineMergeConflicts() {
|
||||||
return gui.createErrorPanel(gui.Tr.ErrStageDirWithInlineMergeConflicts)
|
return gui.PopupHandler.ErrorMsg(gui.Tr.ErrStageDirWithInlineMergeConflicts)
|
||||||
}
|
}
|
||||||
|
|
||||||
if node.GetHasUnstagedChanges() {
|
if node.GetHasUnstagedChanges() {
|
||||||
gui.logAction(gui.Tr.Actions.StageFile)
|
gui.logAction(gui.Tr.Actions.StageFile)
|
||||||
if err := gui.Git.WorkingTree.StageFile(node.Path); err != nil {
|
if err := gui.Git.WorkingTree.StageFile(node.Path); err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// pretty sure it doesn't matter that we're always passing true here
|
// pretty sure it doesn't matter that we're always passing true here
|
||||||
gui.logAction(gui.Tr.Actions.UnstageFile)
|
gui.logAction(gui.Tr.Actions.UnstageFile)
|
||||||
if err := gui.Git.WorkingTree.UnStageFile([]string{node.Path}, true); err != nil {
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,10 +275,10 @@ func (gui *Gui) handleStageAll() error {
|
|||||||
err = gui.Git.WorkingTree.StageAll()
|
err = gui.Git.WorkingTree.StageAll()
|
||||||
}
|
}
|
||||||
if err != nil {
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -290,7 +292,7 @@ func (gui *Gui) handleIgnoreFile() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if node.GetPath() == ".gitignore" {
|
if node.GetPath() == ".gitignore" {
|
||||||
return gui.createErrorPanel("Cannot ignore .gitignore")
|
return gui.PopupHandler.ErrorMsg("Cannot ignore .gitignore")
|
||||||
}
|
}
|
||||||
|
|
||||||
unstageFiles := func() error {
|
unstageFiles := func() error {
|
||||||
@ -306,10 +308,10 @@ func (gui *Gui) handleIgnoreFile() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if node.GetIsTracked() {
|
if node.GetIsTracked() {
|
||||||
return gui.ask(askOpts{
|
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: gui.Tr.IgnoreTracked,
|
Title: gui.Tr.IgnoreTracked,
|
||||||
prompt: gui.Tr.IgnoreTrackedPrompt,
|
Prompt: gui.Tr.IgnoreTrackedPrompt,
|
||||||
handleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
gui.logAction(gui.Tr.Actions.IgnoreFile)
|
gui.logAction(gui.Tr.Actions.IgnoreFile)
|
||||||
// not 100% sure if this is necessary but I'll assume it is
|
// not 100% sure if this is necessary but I'll assume it is
|
||||||
if err := unstageFiles(); err != nil {
|
if err := unstageFiles(); err != nil {
|
||||||
@ -323,7 +325,7 @@ func (gui *Gui) handleIgnoreFile() error {
|
|||||||
if err := gui.Git.WorkingTree.Ignore(node.GetPath()); err != nil {
|
if err := gui.Git.WorkingTree.Ignore(node.GetPath()); err != nil {
|
||||||
return err
|
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 {
|
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 {
|
func (gui *Gui) handleWIPCommitPress() error {
|
||||||
skipHookPrefix := gui.UserConfig.Git.SkipHookPrefix
|
skipHookPrefix := gui.UserConfig.Git.SkipHookPrefix
|
||||||
if skipHookPrefix == "" {
|
if skipHookPrefix == "" {
|
||||||
return gui.createErrorPanel(gui.Tr.SkipHookPrefixNotConfigured)
|
return gui.PopupHandler.ErrorMsg(gui.Tr.SkipHookPrefixNotConfigured)
|
||||||
}
|
}
|
||||||
|
|
||||||
textArea := gui.Views.CommitMessage.TextArea
|
textArea := gui.Views.CommitMessage.TextArea
|
||||||
@ -381,11 +383,11 @@ func (gui *Gui) prepareFilesForCommit() error {
|
|||||||
|
|
||||||
func (gui *Gui) handleCommitPress() error {
|
func (gui *Gui) handleCommitPress() error {
|
||||||
if err := gui.prepareFilesForCommit(); err != nil {
|
if err := gui.prepareFilesForCommit(); err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if gui.State.FileTreeViewModel.GetItemsLength() == 0 {
|
if gui.State.FileTreeViewModel.GetItemsLength() == 0 {
|
||||||
return gui.createErrorPanel(gui.Tr.NoFilesStagedTitle)
|
return gui.PopupHandler.ErrorMsg(gui.Tr.NoFilesStagedTitle)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(gui.stagedFiles()) == 0 {
|
if len(gui.stagedFiles()) == 0 {
|
||||||
@ -403,7 +405,7 @@ func (gui *Gui) handleCommitPress() error {
|
|||||||
prefixReplace := commitPrefixConfig.Replace
|
prefixReplace := commitPrefixConfig.Replace
|
||||||
rgx, err := regexp.Compile(prefixPattern)
|
rgx, err := regexp.Compile(prefixPattern)
|
||||||
if err != nil {
|
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)
|
prefix := rgx.ReplaceAllString(gui.getCheckedOutBranch().Name, prefixReplace)
|
||||||
gui.Views.CommitMessage.ClearTextArea()
|
gui.Views.CommitMessage.ClearTextArea()
|
||||||
@ -421,16 +423,16 @@ func (gui *Gui) handleCommitPress() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) promptToStageAllAndRetry(retry func() error) error {
|
func (gui *Gui) promptToStageAllAndRetry(retry func() error) error {
|
||||||
return gui.ask(askOpts{
|
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: gui.Tr.NoFilesStagedTitle,
|
Title: gui.Tr.NoFilesStagedTitle,
|
||||||
prompt: gui.Tr.NoFilesStagedPrompt,
|
Prompt: gui.Tr.NoFilesStagedPrompt,
|
||||||
handleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
gui.logAction(gui.Tr.Actions.StageAllFiles)
|
gui.logAction(gui.Tr.Actions.StageAllFiles)
|
||||||
if err := gui.Git.WorkingTree.StageAll(); err != nil {
|
if err := gui.Git.WorkingTree.StageAll(); err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
if err := gui.refreshFilesAndSubmodules(); err != nil {
|
if err := gui.refreshFilesAndSubmodules(); err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return retry()
|
return retry()
|
||||||
@ -440,7 +442,7 @@ func (gui *Gui) promptToStageAllAndRetry(retry func() error) error {
|
|||||||
|
|
||||||
func (gui *Gui) handleAmendCommitPress() error {
|
func (gui *Gui) handleAmendCommitPress() error {
|
||||||
if gui.State.FileTreeViewModel.GetItemsLength() == 0 {
|
if gui.State.FileTreeViewModel.GetItemsLength() == 0 {
|
||||||
return gui.createErrorPanel(gui.Tr.NoFilesStagedTitle)
|
return gui.PopupHandler.ErrorMsg(gui.Tr.NoFilesStagedTitle)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(gui.stagedFiles()) == 0 {
|
if len(gui.stagedFiles()) == 0 {
|
||||||
@ -448,13 +450,13 @@ func (gui *Gui) handleAmendCommitPress() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(gui.State.Commits) == 0 {
|
if len(gui.State.Commits) == 0 {
|
||||||
return gui.createErrorPanel(gui.Tr.NoCommitToAmend)
|
return gui.PopupHandler.ErrorMsg(gui.Tr.NoCommitToAmend)
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: strings.Title(gui.Tr.AmendLastCommit),
|
Title: strings.Title(gui.Tr.AmendLastCommit),
|
||||||
prompt: gui.Tr.SureToAmend,
|
Prompt: gui.Tr.SureToAmend,
|
||||||
handleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
cmdObj := gui.Git.Commit.AmendHeadCmdObj()
|
cmdObj := gui.Git.Commit.AmendHeadCmdObj()
|
||||||
gui.logAction(gui.Tr.Actions.AmendCommit)
|
gui.logAction(gui.Tr.Actions.AmendCommit)
|
||||||
return gui.withGpgHandling(cmdObj, gui.Tr.AmendingStatus, nil)
|
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
|
// their editor rather than via the popup panel
|
||||||
func (gui *Gui) handleCommitEditorPress() error {
|
func (gui *Gui) handleCommitEditorPress() error {
|
||||||
if gui.State.FileTreeViewModel.GetItemsLength() == 0 {
|
if gui.State.FileTreeViewModel.GetItemsLength() == 0 {
|
||||||
return gui.createErrorPanel(gui.Tr.NoFilesStagedTitle)
|
return gui.PopupHandler.ErrorMsg(gui.Tr.NoFilesStagedTitle)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(gui.stagedFiles()) == 0 {
|
if len(gui.stagedFiles()) == 0 {
|
||||||
@ -480,28 +482,29 @@ func (gui *Gui) handleCommitEditorPress() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleStatusFilterPressed() error {
|
func (gui *Gui) handleStatusFilterPressed() error {
|
||||||
menuItems := []*menuItem{
|
return gui.PopupHandler.Menu(popup.CreateMenuOptions{
|
||||||
{
|
Title: gui.Tr.FilteringMenuTitle,
|
||||||
displayString: gui.Tr.FilterStagedFiles,
|
Items: []*popup.MenuItem{
|
||||||
onPress: func() error {
|
{
|
||||||
return gui.setStatusFiltering(filetree.DisplayStaged)
|
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 {
|
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 {
|
func (gui *Gui) editFileAtLine(filename string, lineNumber int) error {
|
||||||
cmdStr, err := gui.Git.File.GetEditCmdStr(filename, lineNumber)
|
cmdStr, err := gui.Git.File.GetEditCmdStr(filename, lineNumber)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
gui.logAction(gui.Tr.Actions.EditFile)
|
gui.logAction(gui.Tr.Actions.EditFile)
|
||||||
@ -533,7 +536,7 @@ func (gui *Gui) handleFileEdit() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if node.File == nil {
|
if node.File == nil {
|
||||||
return gui.createErrorPanel(gui.Tr.ErrCannotEditDirectory)
|
return gui.PopupHandler.ErrorMsg(gui.Tr.ErrCannotEditDirectory)
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.editFile(node.GetPath())
|
return gui.editFile(node.GetPath())
|
||||||
@ -549,7 +552,7 @@ func (gui *Gui) handleFileOpen() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleRefreshFiles() 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 {
|
func (gui *Gui) refreshStateFiles() error {
|
||||||
@ -666,10 +669,10 @@ func (gui *Gui) refreshStateFiles() error {
|
|||||||
func (gui *Gui) promptToContinueRebase() error {
|
func (gui *Gui) promptToContinueRebase() error {
|
||||||
gui.takeOverMergeConflictScrolling()
|
gui.takeOverMergeConflictScrolling()
|
||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: "continue",
|
Title: "continue",
|
||||||
prompt: gui.Tr.ConflictsResolved,
|
Prompt: gui.Tr.ConflictsResolved,
|
||||||
handleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
return gui.genericMergeCommand(REBASE_OPTION_CONTINUE)
|
return gui.genericMergeCommand(REBASE_OPTION_CONTINUE)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -730,15 +733,15 @@ func (gui *Gui) handlePullFiles() error {
|
|||||||
if !currentBranch.IsTrackingRemote() {
|
if !currentBranch.IsTrackingRemote() {
|
||||||
suggestedRemote := getSuggestedRemote(gui.State.Remotes)
|
suggestedRemote := getSuggestedRemote(gui.State.Remotes)
|
||||||
|
|
||||||
return gui.prompt(promptOpts{
|
return gui.PopupHandler.Prompt(popup.PromptOpts{
|
||||||
title: gui.Tr.EnterUpstream,
|
Title: gui.Tr.EnterUpstream,
|
||||||
initialContent: suggestedRemote + " " + currentBranch.Name,
|
InitialContent: suggestedRemote + " " + currentBranch.Name,
|
||||||
findSuggestionsFunc: gui.getRemoteBranchesSuggestionsFunc(" "),
|
FindSuggestionsFunc: gui.getRemoteBranchesSuggestionsFunc(" "),
|
||||||
handleConfirm: func(upstream string) error {
|
HandleConfirm: func(upstream string) error {
|
||||||
var upstreamBranch, upstreamRemote string
|
var upstreamBranch, upstreamRemote string
|
||||||
split := strings.Split(upstream, " ")
|
split := strings.Split(upstream, " ")
|
||||||
if len(split) != 2 {
|
if len(split) != 2 {
|
||||||
return gui.createErrorPanel(gui.Tr.InvalidUpstream)
|
return gui.PopupHandler.ErrorMsg(gui.Tr.InvalidUpstream)
|
||||||
}
|
}
|
||||||
|
|
||||||
upstreamRemote = split[0]
|
upstreamRemote = split[0]
|
||||||
@ -749,7 +752,7 @@ func (gui *Gui) handlePullFiles() error {
|
|||||||
if strings.Contains(errorMessage, "does not exist") {
|
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)
|
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})
|
return gui.pullFiles(PullFilesOptions{UpstreamRemote: upstreamRemote, UpstreamBranch: upstreamBranch, action: action})
|
||||||
},
|
},
|
||||||
@ -767,14 +770,9 @@ type PullFilesOptions struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) pullFiles(opts PullFilesOptions) error {
|
func (gui *Gui) pullFiles(opts PullFilesOptions) error {
|
||||||
if err := gui.createLoaderPanel(gui.Tr.PullWait); err != nil {
|
return gui.PopupHandler.WithLoaderPanel(gui.Tr.PullWait, func() error {
|
||||||
return err
|
return gui.pullWithLock(opts)
|
||||||
}
|
})
|
||||||
|
|
||||||
// TODO: this doesn't look like a good idea. Why the goroutine?
|
|
||||||
go utils.Safe(func() { _ = gui.pullWithLock(opts) })
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) pullWithLock(opts PullFilesOptions) error {
|
func (gui *Gui) pullWithLock(opts PullFilesOptions) error {
|
||||||
@ -804,10 +802,7 @@ type pushOpts struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) push(opts pushOpts) error {
|
func (gui *Gui) push(opts pushOpts) error {
|
||||||
if err := gui.createLoaderPanel(gui.Tr.PushWait); err != nil {
|
return gui.PopupHandler.WithLoaderPanel(gui.Tr.PushWait, func() error {
|
||||||
return err
|
|
||||||
}
|
|
||||||
go utils.Safe(func() {
|
|
||||||
gui.logAction(gui.Tr.Actions.Push)
|
gui.logAction(gui.Tr.Actions.Push)
|
||||||
err := gui.Git.Sync.Push(git_commands.PushOpts{
|
err := gui.Git.Sync.Push(git_commands.PushOpts{
|
||||||
Force: opts.force,
|
Force: opts.force,
|
||||||
@ -816,28 +811,29 @@ func (gui *Gui) push(opts pushOpts) error {
|
|||||||
SetUpstream: opts.setUpstream,
|
SetUpstream: opts.setUpstream,
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil && !opts.force && strings.Contains(err.Error(), "Updates were rejected") {
|
if err != nil {
|
||||||
forcePushDisabled := gui.UserConfig.Git.DisableForcePushing
|
if !opts.force && strings.Contains(err.Error(), "Updates were rejected") {
|
||||||
if forcePushDisabled {
|
forcePushDisabled := gui.UserConfig.Git.DisableForcePushing
|
||||||
_ = gui.createErrorPanel(gui.Tr.UpdatesRejectedAndForcePushDisabled)
|
if forcePushDisabled {
|
||||||
return
|
_ = gui.PopupHandler.ErrorMsg(gui.Tr.UpdatesRejectedAndForcePushDisabled)
|
||||||
}
|
return nil
|
||||||
_ = gui.ask(askOpts{
|
}
|
||||||
title: gui.Tr.ForcePush,
|
_ = gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
prompt: gui.Tr.ForcePushPrompt,
|
Title: gui.Tr.ForcePush,
|
||||||
handleConfirm: func() error {
|
Prompt: gui.Tr.ForcePushPrompt,
|
||||||
newOpts := opts
|
HandleConfirm: func() error {
|
||||||
newOpts.force = true
|
newOpts := opts
|
||||||
|
newOpts.force = true
|
||||||
|
|
||||||
return gui.push(newOpts)
|
return gui.push(newOpts)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
return
|
return nil
|
||||||
|
}
|
||||||
|
_ = gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
gui.handleCredentialsPopup(err)
|
return gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC})
|
||||||
_ = gui.refreshSidePanels(refreshOptions{mode: ASYNC})
|
|
||||||
})
|
})
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) pushFiles() error {
|
func (gui *Gui) pushFiles() error {
|
||||||
@ -870,11 +866,11 @@ func (gui *Gui) pushFiles() error {
|
|||||||
if gui.Git.Config.GetPushToCurrent() {
|
if gui.Git.Config.GetPushToCurrent() {
|
||||||
return gui.push(pushOpts{setUpstream: true})
|
return gui.push(pushOpts{setUpstream: true})
|
||||||
} else {
|
} else {
|
||||||
return gui.prompt(promptOpts{
|
return gui.PopupHandler.Prompt(popup.PromptOpts{
|
||||||
title: gui.Tr.EnterUpstream,
|
Title: gui.Tr.EnterUpstream,
|
||||||
initialContent: suggestedRemote + " " + currentBranch.Name,
|
InitialContent: suggestedRemote + " " + currentBranch.Name,
|
||||||
findSuggestionsFunc: gui.getRemoteBranchesSuggestionsFunc(" "),
|
FindSuggestionsFunc: gui.getRemoteBranchesSuggestionsFunc(" "),
|
||||||
handleConfirm: func(upstream string) error {
|
HandleConfirm: func(upstream string) error {
|
||||||
var upstreamBranch, upstreamRemote string
|
var upstreamBranch, upstreamRemote string
|
||||||
split := strings.Split(upstream, " ")
|
split := strings.Split(upstream, " ")
|
||||||
if len(split) == 2 {
|
if len(split) == 2 {
|
||||||
@ -914,13 +910,13 @@ func getSuggestedRemote(remotes []*models.Remote) string {
|
|||||||
func (gui *Gui) requestToForcePush(opts pushOpts) error {
|
func (gui *Gui) requestToForcePush(opts pushOpts) error {
|
||||||
forcePushDisabled := gui.UserConfig.Git.DisableForcePushing
|
forcePushDisabled := gui.UserConfig.Git.DisableForcePushing
|
||||||
if forcePushDisabled {
|
if forcePushDisabled {
|
||||||
return gui.createErrorPanel(gui.Tr.ForcePushDisabled)
|
return gui.PopupHandler.ErrorMsg(gui.Tr.ForcePushDisabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: gui.Tr.ForcePush,
|
Title: gui.Tr.ForcePush,
|
||||||
prompt: gui.Tr.ForcePushPrompt,
|
Prompt: gui.Tr.ForcePushPrompt,
|
||||||
handleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
return gui.push(opts)
|
return gui.push(opts)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -950,16 +946,16 @@ func (gui *Gui) switchToMerge() error {
|
|||||||
func (gui *Gui) openFile(filename string) error {
|
func (gui *Gui) openFile(filename string) error {
|
||||||
gui.logAction(gui.Tr.Actions.OpenFile)
|
gui.logAction(gui.Tr.Actions.OpenFile)
|
||||||
if err := gui.OSCommand.OpenFile(filename); err != nil {
|
if err := gui.OSCommand.OpenFile(filename); err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleCustomCommand() error {
|
func (gui *Gui) handleCustomCommand() error {
|
||||||
return gui.prompt(promptOpts{
|
return gui.PopupHandler.Prompt(popup.PromptOpts{
|
||||||
title: gui.Tr.CustomCommand,
|
Title: gui.Tr.CustomCommand,
|
||||||
findSuggestionsFunc: gui.getCustomCommandsHistorySuggestionsFunc(),
|
FindSuggestionsFunc: gui.getCustomCommandsHistorySuggestionsFunc(),
|
||||||
handleConfirm: func(command string) error {
|
HandleConfirm: func(command string) error {
|
||||||
gui.Config.GetAppState().CustomCommandsHistory = utils.Limit(
|
gui.Config.GetAppState().CustomCommandsHistory = utils.Limit(
|
||||||
utils.Uniq(
|
utils.Uniq(
|
||||||
append(gui.Config.GetAppState().CustomCommandsHistory, command),
|
append(gui.Config.GetAppState().CustomCommandsHistory, command),
|
||||||
@ -981,24 +977,25 @@ func (gui *Gui) handleCustomCommand() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleCreateStashMenu() error {
|
func (gui *Gui) handleCreateStashMenu() error {
|
||||||
menuItems := []*menuItem{
|
return gui.PopupHandler.Menu(popup.CreateMenuOptions{
|
||||||
{
|
Title: gui.Tr.LcStashOptions,
|
||||||
displayString: gui.Tr.LcStashAllChanges,
|
Items: []*popup.MenuItem{
|
||||||
onPress: func() error {
|
{
|
||||||
gui.logAction(gui.Tr.Actions.StashAllChanges)
|
DisplayString: gui.Tr.LcStashAllChanges,
|
||||||
return gui.handleStashSave(gui.Git.Stash.Save)
|
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 {
|
func (gui *Gui) handleStashChanges() error {
|
||||||
@ -1052,10 +1049,10 @@ func (gui *Gui) handleToggleFileTreeView() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleOpenMergeTool() error {
|
func (gui *Gui) handleOpenMergeTool() error {
|
||||||
return gui.ask(askOpts{
|
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: gui.Tr.MergeToolTitle,
|
Title: gui.Tr.MergeToolTitle,
|
||||||
prompt: gui.Tr.MergeToolPrompt,
|
Prompt: gui.Tr.MergeToolPrompt,
|
||||||
handleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
gui.logAction(gui.Tr.Actions.OpenMergeTool)
|
gui.logAction(gui.Tr.Actions.OpenMergeTool)
|
||||||
return gui.runSubprocessWithSuspenseAndRefresh(
|
return gui.runSubprocessWithSuspenseAndRefresh(
|
||||||
gui.Git.WorkingTree.OpenMergeToolCmdObj(),
|
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
|
package gui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
|
)
|
||||||
|
|
||||||
func (gui *Gui) validateNotInFilterMode() (bool, error) {
|
func (gui *Gui) validateNotInFilterMode() (bool, error) {
|
||||||
if gui.State.Modes.Filtering.Active() {
|
if gui.State.Modes.Filtering.Active() {
|
||||||
err := gui.ask(askOpts{
|
err := gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: gui.Tr.MustExitFilterModeTitle,
|
Title: gui.Tr.MustExitFilterModeTitle,
|
||||||
prompt: gui.Tr.MustExitFilterModePrompt,
|
Prompt: gui.Tr.MustExitFilterModePrompt,
|
||||||
handleConfirm: gui.exitFilterMode,
|
HandleConfirm: gui.exitFilterMode,
|
||||||
})
|
})
|
||||||
|
|
||||||
return false, err
|
return false, err
|
||||||
@ -23,7 +28,7 @@ func (gui *Gui) clearFiltering() error {
|
|||||||
gui.State.ScreenMode = SCREEN_NORMAL
|
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 {
|
func (gui *Gui) setFiltering(path string) error {
|
||||||
@ -36,7 +41,7 @@ func (gui *Gui) setFiltering(path string) error {
|
|||||||
return err
|
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)
|
gui.State.Contexts.BranchCommits.GetPanelState().SetSelectedLineIdx(0)
|
||||||
}})
|
}})
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,8 @@ package gui
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (gui *Gui) handleCreateFilteringMenuPanel() error {
|
func (gui *Gui) handleCreateFilteringMenuPanel() error {
|
||||||
@ -20,24 +22,24 @@ func (gui *Gui) handleCreateFilteringMenuPanel() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
menuItems := []*menuItem{}
|
menuItems := []*popup.MenuItem{}
|
||||||
|
|
||||||
if fileName != "" {
|
if fileName != "" {
|
||||||
menuItems = append(menuItems, &menuItem{
|
menuItems = append(menuItems, &popup.MenuItem{
|
||||||
displayString: fmt.Sprintf("%s '%s'", gui.Tr.LcFilterBy, fileName),
|
DisplayString: fmt.Sprintf("%s '%s'", gui.Tr.LcFilterBy, fileName),
|
||||||
onPress: func() error {
|
OnPress: func() error {
|
||||||
return gui.setFiltering(fileName)
|
return gui.setFiltering(fileName)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
menuItems = append(menuItems, &menuItem{
|
menuItems = append(menuItems, &popup.MenuItem{
|
||||||
displayString: gui.Tr.LcFilterPathOption,
|
DisplayString: gui.Tr.LcFilterPathOption,
|
||||||
onPress: func() error {
|
OnPress: func() error {
|
||||||
return gui.prompt(promptOpts{
|
return gui.PopupHandler.Prompt(popup.PromptOpts{
|
||||||
findSuggestionsFunc: gui.getFilePathSuggestionsFunc(),
|
FindSuggestionsFunc: gui.getFilePathSuggestionsFunc(),
|
||||||
title: gui.Tr.EnterFileName,
|
Title: gui.Tr.EnterFileName,
|
||||||
handleConfirm: func(response string) error {
|
HandleConfirm: func(response string) error {
|
||||||
return gui.setFiltering(strings.TrimSpace(response))
|
return gui.setFiltering(strings.TrimSpace(response))
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -45,11 +47,11 @@ func (gui *Gui) handleCreateFilteringMenuPanel() error {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if gui.State.Modes.Filtering.Active() {
|
if gui.State.Modes.Filtering.Active() {
|
||||||
menuItems = append(menuItems, &menuItem{
|
menuItems = append(menuItems, &popup.MenuItem{
|
||||||
displayString: gui.Tr.LcExitFilterMode,
|
DisplayString: gui.Tr.LcExitFilterMode,
|
||||||
onPress: gui.clearFiltering,
|
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
|
// 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
|
// if nothing has been typed because there'll be too much to display efficiently
|
||||||
func (gui *Gui) getFilePathSuggestionsFunc() func(string) []*types.Suggestion {
|
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()
|
trie := patricia.NewTrie()
|
||||||
// load every non-gitignored file in the repo
|
// load every non-gitignored file in the repo
|
||||||
ignore, err := gitignore.FromGit()
|
ignore, err := gitignore.FromGit()
|
||||||
|
@ -3,6 +3,7 @@ package gui
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -13,16 +14,16 @@ func (gui *Gui) handleCreateGitFlowMenu() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !gui.Git.Flow.GitFlowEnabled() {
|
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 {
|
startHandler := func(branchType string) func() error {
|
||||||
return func() error {
|
return func() error {
|
||||||
title := utils.ResolvePlaceholderString(gui.Tr.NewGitFlowBranchPrompt, map[string]string{"branchType": branchType})
|
title := utils.ResolvePlaceholderString(gui.Tr.NewGitFlowBranchPrompt, map[string]string{"branchType": branchType})
|
||||||
|
|
||||||
return gui.prompt(promptOpts{
|
return gui.PopupHandler.Prompt(popup.PromptOpts{
|
||||||
title: title,
|
Title: title,
|
||||||
handleConfirm: func(name string) error {
|
HandleConfirm: func(name string) error {
|
||||||
gui.logAction(gui.Tr.Actions.GitFlowStart)
|
gui.logAction(gui.Tr.Actions.GitFlowStart)
|
||||||
return gui.runSubprocessWithSuspenseAndRefresh(
|
return gui.runSubprocessWithSuspenseAndRefresh(
|
||||||
gui.Git.Flow.StartCmdObj(branchType, name),
|
gui.Git.Flow.StartCmdObj(branchType, name),
|
||||||
@ -32,39 +33,40 @@ func (gui *Gui) handleCreateGitFlowMenu() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
menuItems := []*menuItem{
|
return gui.PopupHandler.Menu(popup.CreateMenuOptions{
|
||||||
{
|
Title: "git flow",
|
||||||
// not localising here because it's one to one with the actual git flow commands
|
Items: []*popup.MenuItem{
|
||||||
displayString: fmt.Sprintf("finish branch '%s'", branch.Name),
|
{
|
||||||
onPress: func() error {
|
// not localising here because it's one to one with the actual git flow commands
|
||||||
return gui.gitFlowFinishBranch(branch.Name)
|
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 {
|
func (gui *Gui) gitFlowFinishBranch(branchName string) error {
|
||||||
cmdObj, err := gui.Git.Flow.FinishCmdObj(branchName)
|
cmdObj, err := gui.Git.Flow.FinishCmdObj(branchName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
gui.logAction(gui.Tr.Actions.GitFlowFinish)
|
gui.logAction(gui.Tr.Actions.GitFlowFinish)
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -176,7 +177,7 @@ func (gui *Gui) scrollDownConfirmationPanel() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleRefresh() 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 {
|
func (gui *Gui) handleMouseDownMain() error {
|
||||||
@ -218,10 +219,10 @@ func (gui *Gui) fetch() (err error) {
|
|||||||
err = gui.Git.Sync.Fetch(git_commands.FetchOptions{})
|
err = gui.Git.Sync.Fetch(git_commands.FetchOptions{})
|
||||||
|
|
||||||
if err != nil && strings.Contains(err.Error(), "exit status 128") {
|
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
|
return err
|
||||||
}
|
}
|
||||||
@ -232,7 +233,7 @@ func (gui *Gui) backgroundFetch() (err error) {
|
|||||||
|
|
||||||
err = gui.Git.Sync.Fetch(git_commands.FetchOptions{Background: true})
|
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
|
return err
|
||||||
}
|
}
|
||||||
@ -247,7 +248,7 @@ func (gui *Gui) handleCopySelectedSideContextItemToClipboard() error {
|
|||||||
|
|
||||||
gui.logAction(gui.Tr.Actions.CopyToClipboard)
|
gui.logAction(gui.Tr.Actions.CopyToClipboard)
|
||||||
if err := gui.OSCommand.CopyToClipboard(itemId); err != nil {
|
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)
|
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/commands/oscommands"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
"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
|
// 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
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := gui.refreshSidePanels(refreshOptions{mode: ASYNC}); err != nil {
|
if err := gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC}); err != nil {
|
||||||
return err
|
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 {
|
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 := gui.OSCommand.Cmd.NewShell(cmdObj.ToString())
|
||||||
cmdObj.AddEnvVars("TERM=dumb")
|
cmdObj.AddEnvVars("TERM=dumb")
|
||||||
cmdWriter := gui.getCmdWriter()
|
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 {
|
if _, err := cmd.Stdout.Write([]byte(fmt.Sprintf("%s\n", style.FgRed.Sprint(err.Error())))); err != nil {
|
||||||
gui.Log.Error(err)
|
gui.Log.Error(err)
|
||||||
}
|
}
|
||||||
_ = gui.refreshSidePanels(refreshOptions{mode: ASYNC})
|
_ = gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC})
|
||||||
return gui.surfaceError(
|
return gui.PopupHandler.Error(
|
||||||
fmt.Errorf(
|
fmt.Errorf(
|
||||||
gui.Tr.GitCommandFailed, gui.UserConfig.Keybinding.Universal.ExtrasMenu,
|
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/commands/oscommands"
|
||||||
"github.com/jesseduffield/lazygit/pkg/common"
|
"github.com/jesseduffield/lazygit/pkg/common"
|
||||||
"github.com/jesseduffield/lazygit/pkg/config"
|
"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/filetree"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/lbl"
|
"github.com/jesseduffield/lazygit/pkg/gui/lbl"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/mergeconflicts"
|
"github.com/jesseduffield/lazygit/pkg/gui/mergeconflicts"
|
||||||
@ -75,11 +76,11 @@ type Gui struct {
|
|||||||
OSCommand *oscommands.OSCommand
|
OSCommand *oscommands.OSCommand
|
||||||
|
|
||||||
// this is the state of the GUI for the current repo
|
// 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
|
// this is a mapping of repos to gui states, so that we can restore the original
|
||||||
// gui state when returning from a subrepo
|
// gui state when returning from a subrepo
|
||||||
RepoStateMap map[Repo]*guiState
|
RepoStateMap map[Repo]*GuiRepoState
|
||||||
Config config.AppConfigurer
|
Config config.AppConfigurer
|
||||||
Updater *updates.Updater
|
Updater *updates.Updater
|
||||||
statusManager *statusManager
|
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
|
// when you enter into a submodule we'll append the superproject's path to this array
|
||||||
// so that you can return to the superproject
|
// so that you can return to the superproject
|
||||||
RepoPathStack []string
|
RepoPathStack *utils.StringStack
|
||||||
|
|
||||||
// this tells us whether our views have been initially set up
|
// this tells us whether our views have been initially set up
|
||||||
ViewsSetup bool
|
ViewsSetup bool
|
||||||
@ -121,10 +122,21 @@ type Gui struct {
|
|||||||
|
|
||||||
suggestionsAsyncHandler *tasks.AsyncHandler
|
suggestionsAsyncHandler *tasks.AsyncHandler
|
||||||
|
|
||||||
PopupHandler PopupHandler
|
PopupHandler popup.IPopupHandler
|
||||||
|
|
||||||
IsNewRepo bool
|
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
|
// 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
|
// in case we want to restore it before quitting for users who have set up
|
||||||
// the feature for changing directory upon quit.
|
// the feature for changing directory upon quit.
|
||||||
@ -134,6 +146,80 @@ type Gui struct {
|
|||||||
InitialDir string
|
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 {
|
type listPanelState struct {
|
||||||
SelectedLineIdx int
|
SelectedLineIdx int
|
||||||
}
|
}
|
||||||
@ -296,75 +382,6 @@ type guiMutexes struct {
|
|||||||
SubprocessMutex sync.Mutex
|
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
|
// 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
|
// 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
|
// 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
|
initialContext = contexts.BranchCommits
|
||||||
}
|
}
|
||||||
|
|
||||||
gui.State = &guiState{
|
gui.State = &GuiRepoState{
|
||||||
FileTreeViewModel: filetree.NewFileTreeViewModel(make([]*models.File, 0), gui.Log, showTree),
|
FileTreeViewModel: filetree.NewFileTreeViewModel(make([]*models.File, 0), gui.Log, showTree),
|
||||||
CommitFileTreeViewModel: filetree.NewCommitFileTreeViewModel(make([]*models.CommitFile, 0), gui.Log, showTree),
|
CommitFileTreeViewModel: filetree.NewCommitFileTreeViewModel(make([]*models.CommitFile, 0), gui.Log, showTree),
|
||||||
Commits: make([]*models.Commit, 0),
|
Commits: make([]*models.Commit, 0),
|
||||||
FilteredReflogCommits: make([]*models.Commit, 0),
|
FilteredReflogCommits: make([]*models.Commit, 0),
|
||||||
ReflogCommits: make([]*models.Commit, 0),
|
ReflogCommits: make([]*models.Commit, 0),
|
||||||
StashEntries: make([]*models.StashEntry, 0),
|
StashEntries: make([]*models.StashEntry, 0),
|
||||||
BisectInfo: gui.Git.Bisect.GetInfo(),
|
|
||||||
Panels: &panelStates{
|
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
|
// 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}},
|
Files: &filePanelState{listPanelState{SelectedLineIdx: -1}},
|
||||||
@ -446,6 +462,21 @@ func (gui *Gui) resetState(filterPath string, reuseState bool) {
|
|||||||
gui.RepoStateMap[Repo(currentDir)] = gui.State
|
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
|
// for now the split view will always be on
|
||||||
// NewGui builds a new gui handler
|
// NewGui builds a new gui handler
|
||||||
func NewGui(
|
func NewGui(
|
||||||
@ -464,8 +495,8 @@ func NewGui(
|
|||||||
statusManager: &statusManager{},
|
statusManager: &statusManager{},
|
||||||
viewBufferManagerMap: map[string]*tasks.ViewBufferManager{},
|
viewBufferManagerMap: map[string]*tasks.ViewBufferManager{},
|
||||||
showRecentRepos: showRecentRepos,
|
showRecentRepos: showRecentRepos,
|
||||||
RepoPathStack: []string{},
|
RepoPathStack: &utils.StringStack{},
|
||||||
RepoStateMap: map[Repo]*guiState{},
|
RepoStateMap: map[Repo]*GuiRepoState{},
|
||||||
CmdLog: []string{},
|
CmdLog: []string{},
|
||||||
suggestionsAsyncHandler: tasks.NewAsyncHandler(),
|
suggestionsAsyncHandler: tasks.NewAsyncHandler(),
|
||||||
|
|
||||||
@ -501,11 +532,31 @@ func NewGui(
|
|||||||
|
|
||||||
gui.watchFilesForChanges()
|
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)
|
authors.SetCustomAuthors(gui.UserConfig.Gui.AuthorColors)
|
||||||
presentation.SetCustomBranches(gui.UserConfig.Gui.BranchColors)
|
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
|
return gui, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -601,7 +652,7 @@ func (gui *Gui) RunAndHandleError() error {
|
|||||||
|
|
||||||
switch err {
|
switch err {
|
||||||
case gocui.ErrQuit:
|
case gocui.ErrQuit:
|
||||||
if gui.State.RetainOriginalDir {
|
if gui.RetainOriginalDir {
|
||||||
if err := gui.recordDirectory(gui.InitialDir); err != nil {
|
if err := gui.recordDirectory(gui.InitialDir); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -633,7 +684,7 @@ func (gui *Gui) runSubprocessWithSuspenseAndRefresh(subprocess oscommands.ICmdOb
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := gui.refreshSidePanels(refreshOptions{mode: ASYNC}); err != nil {
|
if err := gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -654,7 +705,7 @@ func (gui *Gui) runSubprocessWithSuspense(subprocess oscommands.ICmdObj) (bool,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := gui.g.Suspend(); err != nil {
|
if err := gui.g.Suspend(); err != nil {
|
||||||
return false, gui.surfaceError(err)
|
return false, gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
gui.PauseBackgroundThreads = true
|
gui.PauseBackgroundThreads = true
|
||||||
@ -668,7 +719,7 @@ func (gui *Gui) runSubprocessWithSuspense(subprocess oscommands.ICmdObj) (bool,
|
|||||||
gui.PauseBackgroundThreads = false
|
gui.PauseBackgroundThreads = false
|
||||||
|
|
||||||
if cmdErr != nil {
|
if cmdErr != nil {
|
||||||
return false, gui.surfaceError(cmdErr)
|
return false, gui.PopupHandler.Error(cmdErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
return true, nil
|
return true, nil
|
||||||
@ -703,7 +754,7 @@ func (gui *Gui) loadNewRepo() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := gui.refreshSidePanels(refreshOptions{mode: ASYNC}); err != nil {
|
if err := gui.refreshSidePanels(types.RefreshOptions{Mode: types.ASYNC}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -723,7 +774,7 @@ func (gui *Gui) showInitialPopups(tasks []func(chan struct{}) error) {
|
|||||||
task := task
|
task := task
|
||||||
go utils.Safe(func() {
|
go utils.Safe(func() {
|
||||||
if err := task(done); err != nil {
|
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.Config.SaveAppState()
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: "",
|
Title: "",
|
||||||
prompt: gui.Tr.IntroPopupMessage,
|
Prompt: gui.Tr.IntroPopupMessage,
|
||||||
handleConfirm: onConfirm,
|
HandleConfirm: onConfirm,
|
||||||
handleClose: onConfirm,
|
HandleClose: onConfirm,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -775,9 +826,9 @@ func (gui *Gui) startBackgroundFetch() {
|
|||||||
}
|
}
|
||||||
err := gui.backgroundFetch()
|
err := gui.backgroundFetch()
|
||||||
if err != nil && strings.Contains(err.Error(), "exit status 128") && isNew {
|
if err != nil && strings.Contains(err.Error(), "exit status 128") && isNew {
|
||||||
_ = gui.ask(askOpts{
|
_ = gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: gui.Tr.NoAutomaticGitFetchTitle,
|
Title: gui.Tr.NoAutomaticGitFetchTitle,
|
||||||
prompt: gui.Tr.NoAutomaticGitFetchBody,
|
Prompt: gui.Tr.NoAutomaticGitFetchBody,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
gui.goEvery(time.Second*time.Duration(userConfig.Refresher.FetchInterval), gui.stopChan, func() error {
|
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/gocui"
|
||||||
"github.com/jesseduffield/lazygit/pkg/constants"
|
"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{
|
var keyMapReversed = map[gocui.Key]string{
|
||||||
gocui.KeyF1: "f1",
|
gocui.KeyF1: "f1",
|
||||||
gocui.KeyF2: "f2",
|
gocui.KeyF2: "f2",
|
||||||
@ -203,10 +184,10 @@ func (gui *Gui) getKey(key string) interface{} {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetInitialKeybindings is a function.
|
// GetInitialKeybindings is a function.
|
||||||
func (gui *Gui) GetInitialKeybindings() []*Binding {
|
func (gui *Gui) GetInitialKeybindings() []*types.Binding {
|
||||||
config := gui.UserConfig.Keybinding
|
config := gui.UserConfig.Keybinding
|
||||||
|
|
||||||
bindings := []*Binding{
|
bindings := []*types.Binding{
|
||||||
{
|
{
|
||||||
ViewName: "",
|
ViewName: "",
|
||||||
Key: gui.getKey(config.Universal.Quit),
|
Key: gui.getKey(config.Universal.Quit),
|
||||||
@ -1713,57 +1694,6 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
|||||||
Handler: gui.handleCopySelectedSideContextItemToClipboard,
|
Handler: gui.handleCopySelectedSideContextItemToClipboard,
|
||||||
Description: gui.Tr.LcCopySubmoduleNameToClipboard,
|
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",
|
ViewName: "files",
|
||||||
Contexts: []string{string(FILES_CONTEXT_KEY)},
|
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"} {
|
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.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.NextBlock), Modifier: gocui.ModNone, Handler: gui.nextSideWindow},
|
||||||
{ViewName: viewName, Key: gui.getKey(config.Universal.PrevBlockAlt), Modifier: gocui.ModNone, Handler: gui.previousSideWindow},
|
{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.")
|
log.Fatal("Jump to block keybindings cannot be set. Exactly 5 keybindings must be supplied.")
|
||||||
} else {
|
} else {
|
||||||
for i, window := range windows {
|
for i, window := range windows {
|
||||||
bindings = append(bindings, &Binding{
|
bindings = append(bindings, &types.Binding{
|
||||||
ViewName: "",
|
ViewName: "",
|
||||||
Key: gui.getKey(config.Universal.JumpToBlock[i]),
|
Key: gui.getKey(config.Universal.JumpToBlock[i]),
|
||||||
Modifier: gocui.ModNone,
|
Modifier: gocui.ModNone,
|
||||||
@ -1868,7 +1818,7 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for viewName := range gui.State.Contexts.initialViewTabContextMap() {
|
for viewName := range gui.State.Contexts.initialViewTabContextMap() {
|
||||||
bindings = append(bindings, []*Binding{
|
bindings = append(bindings, []*types.Binding{
|
||||||
{
|
{
|
||||||
ViewName: viewName,
|
ViewName: viewName,
|
||||||
Key: gui.getKey(config.Universal.NextTab),
|
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
|
// 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"
|
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.setViewContent(gui.Views.Information, informationStr)
|
||||||
gui.State.OldInformation = informationStr
|
gui.PrevLayout.Information = informationStr
|
||||||
}
|
}
|
||||||
|
|
||||||
if !gui.ViewsSetup {
|
if !gui.ViewsSetup {
|
||||||
@ -277,9 +277,9 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
|||||||
gui.Views.Main.SetOnSelectItem(gui.onSelectItemWrapper(gui.handlelineByLineNavigateTo))
|
gui.Views.Main.SetOnSelectItem(gui.onSelectItemWrapper(gui.handlelineByLineNavigateTo))
|
||||||
|
|
||||||
mainViewWidth, mainViewHeight := gui.Views.Main.Size()
|
mainViewWidth, mainViewHeight := gui.Views.Main.Size()
|
||||||
if mainViewWidth != gui.State.PrevMainWidth || mainViewHeight != gui.State.PrevMainHeight {
|
if mainViewWidth != gui.PrevLayout.MainWidth || mainViewHeight != gui.PrevLayout.MainHeight {
|
||||||
gui.State.PrevMainWidth = mainViewWidth
|
gui.PrevLayout.MainWidth = mainViewWidth
|
||||||
gui.State.PrevMainHeight = mainViewHeight
|
gui.PrevLayout.MainHeight = mainViewHeight
|
||||||
if err := gui.onResize(); err != nil {
|
if err := gui.onResize(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,7 @@ func (gui *Gui) copySelectedToClipboard() error {
|
|||||||
|
|
||||||
gui.logAction(gui.Tr.Actions.CopySelectedTextToClipboard)
|
gui.logAction(gui.Tr.Actions.CopySelectedTextToClipboard)
|
||||||
if err := gui.OSCommand.CopyToClipboard(selected); err != nil {
|
if err := gui.OSCommand.CopyToClipboard(selected); err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (gui *Gui) menuListContext() IListContext {
|
func (gui *Gui) menuListContext() IListContext {
|
||||||
@ -391,15 +392,15 @@ func (gui *Gui) getListContexts() []IListContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) getListContextKeyBindings() []*Binding {
|
func (gui *Gui) getListContextKeyBindings() []*types.Binding {
|
||||||
bindings := make([]*Binding, 0)
|
bindings := make([]*types.Binding, 0)
|
||||||
|
|
||||||
keybindingConfig := gui.UserConfig.Keybinding
|
keybindingConfig := gui.UserConfig.Keybinding
|
||||||
|
|
||||||
for _, listContext := range gui.getListContexts() {
|
for _, listContext := range gui.getListContexts() {
|
||||||
listContext := listContext
|
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.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: 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},
|
{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
|
gotoBottomHandler = gui.handleGotoBottomForCommitsPanel
|
||||||
}
|
}
|
||||||
|
|
||||||
bindings = append(bindings, []*Binding{
|
bindings = append(bindings, []*types.Binding{
|
||||||
{
|
{
|
||||||
ViewName: listContext.GetViewName(),
|
ViewName: listContext.GetViewName(),
|
||||||
Contexts: []string{string(listContext.GetKey())},
|
Contexts: []string{string(listContext.GetKey())},
|
||||||
|
@ -3,31 +3,12 @@ package gui
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
|
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||||
"github.com/jesseduffield/lazygit/pkg/theme"
|
"github.com/jesseduffield/lazygit/pkg/theme"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"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 {
|
func (gui *Gui) getMenuOptions() map[string]string {
|
||||||
keybindingConfig := gui.UserConfig.Keybinding
|
keybindingConfig := gui.UserConfig.Keybinding
|
||||||
|
|
||||||
@ -42,37 +23,34 @@ func (gui *Gui) handleMenuClose() error {
|
|||||||
return gui.returnFromContext()
|
return gui.returnFromContext()
|
||||||
}
|
}
|
||||||
|
|
||||||
type createMenuOptions struct {
|
// note: items option is mutated by this function
|
||||||
showCancel bool
|
func (gui *Gui) createMenu(opts popup.CreateMenuOptions) error {
|
||||||
}
|
if !opts.HideCancel {
|
||||||
|
|
||||||
func (gui *Gui) createMenu(title string, items []*menuItem, createMenuOptions createMenuOptions) error {
|
|
||||||
if createMenuOptions.showCancel {
|
|
||||||
// this is mutative but I'm okay with that for now
|
// this is mutative but I'm okay with that for now
|
||||||
items = append(items, &menuItem{
|
opts.Items = append(opts.Items, &popup.MenuItem{
|
||||||
displayStrings: []string{gui.Tr.LcCancel},
|
DisplayStrings: []string{gui.Tr.LcCancel},
|
||||||
onPress: func() error {
|
OnPress: func() error {
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
gui.State.MenuItems = items
|
gui.State.MenuItems = opts.Items
|
||||||
|
|
||||||
stringArrays := make([][]string, len(items))
|
stringArrays := make([][]string, len(opts.Items))
|
||||||
for i, item := range items {
|
for i, item := range opts.Items {
|
||||||
if item.opensMenu && item.displayStrings != nil {
|
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")
|
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 {
|
if item.DisplayStrings == nil {
|
||||||
styledStr := item.displayString
|
styledStr := item.DisplayString
|
||||||
if item.opensMenu {
|
if item.OpensMenu {
|
||||||
styledStr = opensMenuStyle(styledStr)
|
styledStr = opensMenuStyle(styledStr)
|
||||||
}
|
}
|
||||||
stringArrays[i] = []string{styledStr}
|
stringArrays[i] = []string{styledStr}
|
||||||
} else {
|
} 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)
|
x0, y0, x1, y1 := gui.getConfirmationPanelDimensions(false, list)
|
||||||
menuView, _ := gui.g.SetView("menu", x0, y0, x1, y1, 0)
|
menuView, _ := gui.g.SetView("menu", x0, y0, x1, y1, 0)
|
||||||
menuView.Title = title
|
menuView.Title = opts.Title
|
||||||
menuView.FgColor = theme.GocuiDefaultTextColor
|
menuView.FgColor = theme.GocuiDefaultTextColor
|
||||||
menuView.SetOnSelectItem(gui.onSelectItemWrapper(func(selectedLine int) error {
|
menuView.SetOnSelectItem(gui.onSelectItemWrapper(func(selectedLine int) error {
|
||||||
return nil
|
return nil
|
||||||
@ -97,7 +75,7 @@ func (gui *Gui) onMenuPress() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := gui.State.MenuItems[selectedLine].onPress(); err != nil {
|
if err := gui.State.MenuItems[selectedLine].OnPress(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/mergeconflicts"
|
"github.com/jesseduffield/lazygit/pkg/gui/mergeconflicts"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (gui *Gui) handleSelectPrevConflictHunk() error {
|
func (gui *Gui) handleSelectPrevConflictHunk() error {
|
||||||
@ -189,7 +190,7 @@ func (gui *Gui) getMergingOptions() map[string]string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleEscapeMerge() error {
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,7 +200,7 @@ func (gui *Gui) handleEscapeMerge() error {
|
|||||||
func (gui *Gui) onLastConflictResolved() error {
|
func (gui *Gui) onLastConflictResolved() error {
|
||||||
// as part of refreshing files, we handle the situation where a file has had
|
// as part of refreshing files, we handle the situation where a file has had
|
||||||
// its merge conflicts resolved.
|
// 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() {
|
func (gui *Gui) resetMergeState() {
|
||||||
|
@ -4,13 +4,15 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (gui *Gui) getBindings(v *gocui.View) []*Binding {
|
func (gui *Gui) getBindings(v *gocui.View) []*types.Binding {
|
||||||
var (
|
var (
|
||||||
bindingsGlobal, bindingsPanel []*Binding
|
bindingsGlobal, bindingsPanel []*types.Binding
|
||||||
)
|
)
|
||||||
|
|
||||||
bindings := append(gui.GetCustomCommandKeybindings(), gui.GetInitialKeybindings()...)
|
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
|
// append dummy element to have a separator between
|
||||||
// panel and global keybindings
|
// panel and global keybindings
|
||||||
bindingsPanel = append(bindingsPanel, &Binding{})
|
bindingsPanel = append(bindingsPanel, &types.Binding{})
|
||||||
return append(bindingsPanel, bindingsGlobal...)
|
return append(bindingsPanel, bindingsGlobal...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) displayDescription(binding *Binding) string {
|
func (gui *Gui) displayDescription(binding *types.Binding) string {
|
||||||
if binding.OpensMenu {
|
if binding.OpensMenu {
|
||||||
return opensMenuStyle(binding.Description)
|
return opensMenuStyle(binding.Description)
|
||||||
}
|
}
|
||||||
@ -54,13 +56,13 @@ func (gui *Gui) handleCreateOptionsMenu() error {
|
|||||||
|
|
||||||
bindings := gui.getBindings(view)
|
bindings := gui.getBindings(view)
|
||||||
|
|
||||||
menuItems := make([]*menuItem, len(bindings))
|
menuItems := make([]*popup.MenuItem, len(bindings))
|
||||||
|
|
||||||
for i, binding := range bindings {
|
for i, binding := range bindings {
|
||||||
binding := binding // note to self, never close over loop variables
|
binding := binding // note to self, never close over loop variables
|
||||||
menuItems[i] = &menuItem{
|
menuItems[i] = &popup.MenuItem{
|
||||||
displayStrings: []string{GetKeyDisplay(binding.Key), gui.displayDescription(binding)},
|
DisplayStrings: []string{GetKeyDisplay(binding.Key), gui.displayDescription(binding)},
|
||||||
onPress: func() error {
|
OnPress: func() error {
|
||||||
if binding.Key == nil {
|
if binding.Key == nil {
|
||||||
return 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"
|
"fmt"
|
||||||
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
"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 {
|
func (gui *Gui) handleCreatePatchOptionsMenu() error {
|
||||||
if !gui.Git.Patch.PatchManager.Active() {
|
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",
|
DisplayString: "reset patch",
|
||||||
onPress: gui.handleResetPatch,
|
OnPress: gui.handleResetPatch,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayString: "apply patch",
|
DisplayString: "apply patch",
|
||||||
onPress: func() error { return gui.handleApplyPatch(false) },
|
OnPress: func() error { return gui.handleApplyPatch(false) },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayString: "apply patch in reverse",
|
DisplayString: "apply patch in reverse",
|
||||||
onPress: func() error { return gui.handleApplyPatch(true) },
|
OnPress: func() error { return gui.handleApplyPatch(true) },
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if gui.Git.Patch.PatchManager.CanRebase && gui.Git.Status.WorkingTreeState() == enums.REBASE_MODE_NONE {
|
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),
|
DisplayString: fmt.Sprintf("remove patch from original commit (%s)", gui.Git.Patch.PatchManager.To),
|
||||||
onPress: gui.handleDeletePatchFromCommit,
|
OnPress: gui.handleDeletePatchFromCommit,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayString: "move patch out into index",
|
DisplayString: "move patch out into index",
|
||||||
onPress: gui.handleMovePatchIntoWorkingTree,
|
OnPress: gui.handleMovePatchIntoWorkingTree,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayString: "move patch into new commit",
|
DisplayString: "move patch into new commit",
|
||||||
onPress: gui.handlePullPatchIntoNewCommit,
|
OnPress: gui.handlePullPatchIntoNewCommit,
|
||||||
},
|
},
|
||||||
}...)
|
}...)
|
||||||
|
|
||||||
@ -49,10 +51,10 @@ func (gui *Gui) handleCreatePatchOptionsMenu() error {
|
|||||||
menuItems = append(
|
menuItems = append(
|
||||||
menuItems[:1],
|
menuItems[:1],
|
||||||
append(
|
append(
|
||||||
[]*menuItem{
|
[]*popup.MenuItem{
|
||||||
{
|
{
|
||||||
displayString: fmt.Sprintf("move patch to selected commit (%s)", selectedCommit.Sha),
|
DisplayString: fmt.Sprintf("move patch to selected commit (%s)", selectedCommit.Sha),
|
||||||
onPress: gui.handleMovePatchToSelectedCommit,
|
OnPress: gui.handleMovePatchToSelectedCommit,
|
||||||
},
|
},
|
||||||
}, menuItems[1:]...,
|
}, 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 {
|
func (gui *Gui) getPatchCommitIndex() int {
|
||||||
@ -75,7 +77,7 @@ func (gui *Gui) getPatchCommitIndex() int {
|
|||||||
|
|
||||||
func (gui *Gui) validateNormalWorkingTreeState() (bool, error) {
|
func (gui *Gui) validateNormalWorkingTreeState() (bool, error) {
|
||||||
if gui.Git.Status.WorkingTreeState() != enums.REBASE_MODE_NONE {
|
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
|
return true, nil
|
||||||
}
|
}
|
||||||
@ -96,7 +98,7 @@ func (gui *Gui) handleDeletePatchFromCommit() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.WithWaitingStatus(gui.Tr.RebasingStatus, func() error {
|
return gui.PopupHandler.WithWaitingStatus(gui.Tr.RebasingStatus, func() error {
|
||||||
commitIndex := gui.getPatchCommitIndex()
|
commitIndex := gui.getPatchCommitIndex()
|
||||||
gui.logAction(gui.Tr.Actions.RemovePatchFromCommit)
|
gui.logAction(gui.Tr.Actions.RemovePatchFromCommit)
|
||||||
err := gui.Git.Patch.DeletePatchesFromCommit(gui.State.Commits, commitIndex)
|
err := gui.Git.Patch.DeletePatchesFromCommit(gui.State.Commits, commitIndex)
|
||||||
@ -113,7 +115,7 @@ func (gui *Gui) handleMovePatchToSelectedCommit() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.WithWaitingStatus(gui.Tr.RebasingStatus, func() error {
|
return gui.PopupHandler.WithWaitingStatus(gui.Tr.RebasingStatus, func() error {
|
||||||
commitIndex := gui.getPatchCommitIndex()
|
commitIndex := gui.getPatchCommitIndex()
|
||||||
gui.logAction(gui.Tr.Actions.MovePatchToSelectedCommit)
|
gui.logAction(gui.Tr.Actions.MovePatchToSelectedCommit)
|
||||||
err := gui.Git.Patch.MovePatchToSelectedCommit(gui.State.Commits, commitIndex, gui.State.Panels.Commits.SelectedLineIdx)
|
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 {
|
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()
|
commitIndex := gui.getPatchCommitIndex()
|
||||||
gui.logAction(gui.Tr.Actions.MovePatchIntoIndex)
|
gui.logAction(gui.Tr.Actions.MovePatchIntoIndex)
|
||||||
err := gui.Git.Patch.MovePatchIntoIndex(gui.State.Commits, commitIndex, stash)
|
err := gui.Git.Patch.MovePatchIntoIndex(gui.State.Commits, commitIndex, stash)
|
||||||
@ -140,10 +142,10 @@ func (gui *Gui) handleMovePatchIntoWorkingTree() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(gui.trackedFiles()) > 0 {
|
if len(gui.trackedFiles()) > 0 {
|
||||||
return gui.ask(askOpts{
|
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: gui.Tr.MustStashTitle,
|
Title: gui.Tr.MustStashTitle,
|
||||||
prompt: gui.Tr.MustStashWarning,
|
Prompt: gui.Tr.MustStashWarning,
|
||||||
handleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
return pull(true)
|
return pull(true)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -161,7 +163,7 @@ func (gui *Gui) handlePullPatchIntoNewCommit() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.WithWaitingStatus(gui.Tr.RebasingStatus, func() error {
|
return gui.PopupHandler.WithWaitingStatus(gui.Tr.RebasingStatus, func() error {
|
||||||
commitIndex := gui.getPatchCommitIndex()
|
commitIndex := gui.getPatchCommitIndex()
|
||||||
gui.logAction(gui.Tr.Actions.MovePatchIntoNewCommit)
|
gui.logAction(gui.Tr.Actions.MovePatchIntoNewCommit)
|
||||||
err := gui.Git.Patch.PullPatchIntoNewCommit(gui.State.Commits, commitIndex)
|
err := gui.Git.Patch.PullPatchIntoNewCommit(gui.State.Commits, commitIndex)
|
||||||
@ -180,9 +182,9 @@ func (gui *Gui) handleApplyPatch(reverse bool) error {
|
|||||||
}
|
}
|
||||||
gui.logAction(action)
|
gui.logAction(action)
|
||||||
if err := gui.Git.Patch.PatchManager.ApplyPatches(reverse); err != nil {
|
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 {
|
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/hosting_service"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"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 {
|
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 {
|
fromToDisplayStrings := func(from string, to string) []string {
|
||||||
return []string{fmt.Sprintf("%s → %s", from, to)}
|
return []string{fmt.Sprintf("%s → %s", from, to)}
|
||||||
}
|
}
|
||||||
|
|
||||||
menuItemsForBranch := func(branch *models.Branch) []*menuItem {
|
menuItemsForBranch := func(branch *models.Branch) []*popup.MenuItem {
|
||||||
return []*menuItem{
|
return []*popup.MenuItem{
|
||||||
{
|
{
|
||||||
displayStrings: fromToDisplayStrings(branch.Name, gui.Tr.LcDefaultBranch),
|
DisplayStrings: fromToDisplayStrings(branch.Name, gui.Tr.LcDefaultBranch),
|
||||||
onPress: func() error {
|
OnPress: func() error {
|
||||||
return gui.createPullRequest(branch.Name, "")
|
return gui.createPullRequest(branch.Name, "")
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayStrings: fromToDisplayStrings(branch.Name, gui.Tr.LcSelectBranch),
|
DisplayStrings: fromToDisplayStrings(branch.Name, gui.Tr.LcSelectBranch),
|
||||||
onPress: func() error {
|
OnPress: func() error {
|
||||||
return gui.prompt(promptOpts{
|
return gui.PopupHandler.Prompt(popup.PromptOpts{
|
||||||
title: branch.Name + " →",
|
Title: branch.Name + " →",
|
||||||
findSuggestionsFunc: gui.getBranchNameSuggestionsFunc(),
|
FindSuggestionsFunc: gui.getBranchNameSuggestionsFunc(),
|
||||||
handleConfirm: func(targetBranchName string) error {
|
HandleConfirm: func(targetBranchName string) error {
|
||||||
return gui.createPullRequest(branch.Name, targetBranchName)
|
return gui.createPullRequest(branch.Name, targetBranchName)
|
||||||
}},
|
}},
|
||||||
)
|
)
|
||||||
@ -39,9 +40,9 @@ func (gui *Gui) createPullRequestMenu(selectedBranch *models.Branch, checkedOutB
|
|||||||
|
|
||||||
if selectedBranch != checkedOutBranch {
|
if selectedBranch != checkedOutBranch {
|
||||||
menuItems = append(menuItems,
|
menuItems = append(menuItems,
|
||||||
&menuItem{
|
&popup.MenuItem{
|
||||||
displayStrings: fromToDisplayStrings(checkedOutBranch.Name, selectedBranch.Name),
|
DisplayStrings: fromToDisplayStrings(checkedOutBranch.Name, selectedBranch.Name),
|
||||||
onPress: func() error {
|
OnPress: func() error {
|
||||||
return gui.createPullRequest(checkedOutBranch.Name, selectedBranch.Name)
|
return gui.createPullRequest(checkedOutBranch.Name, selectedBranch.Name)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -51,20 +52,20 @@ func (gui *Gui) createPullRequestMenu(selectedBranch *models.Branch, checkedOutB
|
|||||||
|
|
||||||
menuItems = append(menuItems, menuItemsForBranch(selectedBranch)...)
|
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 {
|
func (gui *Gui) createPullRequest(from string, to string) error {
|
||||||
hostingServiceMgr := gui.getHostingServiceMgr()
|
hostingServiceMgr := gui.getHostingServiceMgr()
|
||||||
url, err := hostingServiceMgr.GetPullRequestURL(from, to)
|
url, err := hostingServiceMgr.GetPullRequestURL(from, to)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
gui.logAction(gui.Tr.Actions.OpenPullRequest)
|
gui.logAction(gui.Tr.Actions.OpenPullRequest)
|
||||||
|
|
||||||
if err := gui.OSCommand.OpenLink(url); err != nil {
|
if err := gui.OSCommand.OpenLink(url); err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/jesseduffield/gocui"
|
"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
|
// 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 {
|
func (gui *Gui) handleQuitWithoutChangingDirectory() error {
|
||||||
gui.State.RetainOriginalDir = true
|
gui.RetainOriginalDir = true
|
||||||
return gui.quit()
|
return gui.quit()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleQuit() error {
|
func (gui *Gui) handleQuit() error {
|
||||||
gui.State.RetainOriginalDir = false
|
gui.RetainOriginalDir = false
|
||||||
return gui.quit()
|
return gui.quit()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,12 +54,8 @@ func (gui *Gui) handleTopLevelReturn() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
repoPathStack := gui.RepoPathStack
|
repoPathStack := gui.RepoPathStack
|
||||||
if len(repoPathStack) > 0 {
|
if !repoPathStack.IsEmpty() {
|
||||||
n := len(repoPathStack) - 1
|
path := repoPathStack.Pop()
|
||||||
|
|
||||||
path := repoPathStack[n]
|
|
||||||
|
|
||||||
gui.RepoPathStack = repoPathStack[:n]
|
|
||||||
|
|
||||||
return gui.dispatchSwitchToRepo(path, true)
|
return gui.dispatchSwitchToRepo(path, true)
|
||||||
}
|
}
|
||||||
@ -76,10 +73,10 @@ func (gui *Gui) quit() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if gui.UserConfig.ConfirmOnQuit {
|
if gui.UserConfig.ConfirmOnQuit {
|
||||||
return gui.ask(askOpts{
|
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: "",
|
Title: "",
|
||||||
prompt: gui.Tr.ConfirmQuit,
|
Prompt: gui.Tr.ConfirmQuit,
|
||||||
handleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
return gocui.ErrQuit
|
return gocui.ErrQuit
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -5,6 +5,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
"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
|
type RebaseOption string
|
||||||
@ -22,13 +24,13 @@ func (gui *Gui) handleCreateRebaseOptionsMenu() error {
|
|||||||
options = append(options, REBASE_OPTION_SKIP)
|
options = append(options, REBASE_OPTION_SKIP)
|
||||||
}
|
}
|
||||||
|
|
||||||
menuItems := make([]*menuItem, len(options))
|
menuItems := make([]*popup.MenuItem, len(options))
|
||||||
for i, option := range options {
|
for i, option := range options {
|
||||||
// note to self. Never, EVER, close over loop variables in a function
|
// note to self. Never, EVER, close over loop variables in a function
|
||||||
option := option
|
option := option
|
||||||
menuItems[i] = &menuItem{
|
menuItems[i] = &popup.MenuItem{
|
||||||
displayString: option,
|
DisplayString: option,
|
||||||
onPress: func() error {
|
OnPress: func() error {
|
||||||
return gui.genericMergeCommand(option)
|
return gui.genericMergeCommand(option)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -41,14 +43,14 @@ func (gui *Gui) handleCreateRebaseOptionsMenu() error {
|
|||||||
title = gui.Tr.RebaseOptionsTitle
|
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 {
|
func (gui *Gui) genericMergeCommand(command string) error {
|
||||||
status := gui.Git.Status.WorkingTreeState()
|
status := gui.Git.Status.WorkingTreeState()
|
||||||
|
|
||||||
if status != enums.REBASE_MODE_MERGING && status != enums.REBASE_MODE_REBASING {
|
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))
|
gui.logAction(fmt.Sprintf("Merge/Rebase: %s", command))
|
||||||
@ -97,7 +99,7 @@ func isMergeConflictErr(errStr string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleGenericMergeCommandResult(result error) error {
|
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
|
return err
|
||||||
}
|
}
|
||||||
if result == nil {
|
if result == nil {
|
||||||
@ -110,14 +112,14 @@ func (gui *Gui) handleGenericMergeCommandResult(result error) error {
|
|||||||
// assume in this case that we're already done
|
// assume in this case that we're already done
|
||||||
return nil
|
return nil
|
||||||
} else if isMergeConflictErr(result.Error()) {
|
} else if isMergeConflictErr(result.Error()) {
|
||||||
return gui.ask(askOpts{
|
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: gui.Tr.FoundConflictsTitle,
|
Title: gui.Tr.FoundConflictsTitle,
|
||||||
prompt: gui.Tr.FoundConflicts,
|
Prompt: gui.Tr.FoundConflicts,
|
||||||
handlersManageFocus: true,
|
HandlersManageFocus: true,
|
||||||
handleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
return gui.pushContext(gui.State.Contexts.Files)
|
return gui.pushContext(gui.State.Contexts.Files)
|
||||||
},
|
},
|
||||||
handleClose: func() error {
|
HandleClose: func() error {
|
||||||
if err := gui.returnFromContext(); err != nil {
|
if err := gui.returnFromContext(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -126,17 +128,17 @@ func (gui *Gui) handleGenericMergeCommandResult(result error) error {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
return gui.createErrorPanel(result.Error())
|
return gui.PopupHandler.ErrorMsg(result.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) abortMergeOrRebaseWithConfirm() error {
|
func (gui *Gui) abortMergeOrRebaseWithConfirm() error {
|
||||||
// prompt user to confirm that they want to abort, then do it
|
// prompt user to confirm that they want to abort, then do it
|
||||||
mode := gui.workingTreeStateNoun()
|
mode := gui.workingTreeStateNoun()
|
||||||
return gui.ask(askOpts{
|
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: fmt.Sprintf(gui.Tr.AbortTitle, mode),
|
Title: fmt.Sprintf(gui.Tr.AbortTitle, mode),
|
||||||
prompt: fmt.Sprintf(gui.Tr.AbortPrompt, mode),
|
Prompt: fmt.Sprintf(gui.Tr.AbortPrompt, mode),
|
||||||
handleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
return gui.genericMergeCommand(REBASE_OPTION_ABORT)
|
return gui.genericMergeCommand(REBASE_OPTION_ABORT)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/git_config"
|
"github.com/jesseduffield/lazygit/pkg/commands/git_config"
|
||||||
"github.com/jesseduffield/lazygit/pkg/env"
|
"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/gui/style"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
@ -16,24 +17,24 @@ func (gui *Gui) handleCreateRecentReposMenu() error {
|
|||||||
reposCount := utils.Min(len(recentRepoPaths), 20)
|
reposCount := utils.Min(len(recentRepoPaths), 20)
|
||||||
|
|
||||||
// we won't show the current repo hence the -1
|
// 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] {
|
for i, path := range recentRepoPaths[1:reposCount] {
|
||||||
path := path // cos we're closing over the loop variable
|
path := path // cos we're closing over the loop variable
|
||||||
menuItems[i] = &menuItem{
|
menuItems[i] = &popup.MenuItem{
|
||||||
displayStrings: []string{
|
DisplayStrings: []string{
|
||||||
filepath.Base(path),
|
filepath.Base(path),
|
||||||
style.FgMagenta.Sprint(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
|
// 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
|
// so that hitting escape in the new repo does nothing
|
||||||
gui.RepoPathStack = []string{}
|
gui.RepoPathStack.Clear()
|
||||||
return gui.dispatchSwitchToRepo(path, false)
|
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 {
|
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 err := os.Chdir(path); err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
return gui.createErrorPanel(gui.Tr.ErrRepositoryMovedOrDeleted)
|
return gui.PopupHandler.ErrorMsg(gui.Tr.ErrRepositoryMovedOrDeleted)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package gui
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||||
)
|
)
|
||||||
|
|
||||||
// list panel functions
|
// list panel functions
|
||||||
@ -55,7 +56,7 @@ func (gui *Gui) refreshReflogCommits() error {
|
|||||||
commits, onlyObtainedNewReflogCommits, err := gui.Git.Loaders.ReflogCommits.
|
commits, onlyObtainedNewReflogCommits, err := gui.Git.Loaders.ReflogCommits.
|
||||||
GetReflogCommits(lastReflogCommit, filterPath)
|
GetReflogCommits(lastReflogCommit, filterPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if onlyObtainedNewReflogCommits {
|
if onlyObtainedNewReflogCommits {
|
||||||
@ -87,10 +88,10 @@ func (gui *Gui) handleCheckoutReflogCommit() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
err := gui.ask(askOpts{
|
err := gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: gui.Tr.LcCheckoutCommit,
|
Title: gui.Tr.LcCheckoutCommit,
|
||||||
prompt: gui.Tr.SureCheckoutThisCommit,
|
Prompt: gui.Tr.SureCheckoutThisCommit,
|
||||||
handleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
gui.logAction(gui.Tr.Actions.CheckoutReflogCommit)
|
gui.logAction(gui.Tr.Actions.CheckoutReflogCommit)
|
||||||
return gui.handleCheckoutRef(commit.Sha, handleCheckoutRefOptions{})
|
return gui.handleCheckoutRef(commit.Sha, handleCheckoutRefOptions{})
|
||||||
},
|
},
|
||||||
|
@ -4,6 +4,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"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"
|
"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())
|
message := fmt.Sprintf("%s '%s'?", gui.Tr.DeleteRemoteBranchMessage, remoteBranch.FullName())
|
||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: gui.Tr.DeleteRemoteBranch,
|
Title: gui.Tr.DeleteRemoteBranch,
|
||||||
prompt: message,
|
Prompt: message,
|
||||||
handleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
return gui.WithWaitingStatus(gui.Tr.DeletingStatus, func() error {
|
return gui.PopupHandler.WithWaitingStatus(gui.Tr.DeletingStatus, func() error {
|
||||||
gui.logAction(gui.Tr.Actions.DeleteRemoteBranch)
|
gui.logAction(gui.Tr.Actions.DeleteRemoteBranch)
|
||||||
err := gui.Git.Remote.DeleteRemoteBranch(remoteBranch.RemoteName, remoteBranch.Name)
|
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{
|
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: gui.Tr.SetUpstreamTitle,
|
Title: gui.Tr.SetUpstreamTitle,
|
||||||
prompt: message,
|
Prompt: message,
|
||||||
handleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
gui.logAction(gui.Tr.Actions.SetBranchUpstream)
|
gui.logAction(gui.Tr.Actions.SetBranchUpstream)
|
||||||
if err := gui.Git.Branch.SetUpstream(selectedBranch.RemoteName, selectedBranch.Name, checkedOutBranch.Name); err != nil {
|
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"
|
"strings"
|
||||||
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"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/style"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -42,7 +44,7 @@ func (gui *Gui) refreshRemotes() error {
|
|||||||
|
|
||||||
remotes, err := gui.Git.Loaders.Remotes.GetRemotes()
|
remotes, err := gui.Git.Loaders.Remotes.GetRemotes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
gui.State.Remotes = remotes
|
gui.State.Remotes = remotes
|
||||||
@ -79,17 +81,17 @@ func (gui *Gui) handleRemoteEnter() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleAddRemote() error {
|
func (gui *Gui) handleAddRemote() error {
|
||||||
return gui.prompt(promptOpts{
|
return gui.PopupHandler.Prompt(popup.PromptOpts{
|
||||||
title: gui.Tr.LcNewRemoteName,
|
Title: gui.Tr.LcNewRemoteName,
|
||||||
handleConfirm: func(remoteName string) error {
|
HandleConfirm: func(remoteName string) error {
|
||||||
return gui.prompt(promptOpts{
|
return gui.PopupHandler.Prompt(popup.PromptOpts{
|
||||||
title: gui.Tr.LcNewRemoteUrl,
|
Title: gui.Tr.LcNewRemoteUrl,
|
||||||
handleConfirm: func(remoteUrl string) error {
|
HandleConfirm: func(remoteUrl string) error {
|
||||||
gui.logAction(gui.Tr.Actions.AddRemote)
|
gui.logAction(gui.Tr.Actions.AddRemote)
|
||||||
if err := gui.Git.Remote.AddRemote(remoteName, remoteUrl); err != nil {
|
if err := gui.Git.Remote.AddRemote(remoteName, remoteUrl); err != nil {
|
||||||
return err
|
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 nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: gui.Tr.LcRemoveRemote,
|
Title: gui.Tr.LcRemoveRemote,
|
||||||
prompt: gui.Tr.LcRemoveRemotePrompt + " '" + remote.Name + "'?",
|
Prompt: gui.Tr.LcRemoveRemotePrompt + " '" + remote.Name + "'?",
|
||||||
handleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
gui.logAction(gui.Tr.Actions.RemoveRemote)
|
gui.logAction(gui.Tr.Actions.RemoveRemote)
|
||||||
if err := gui.Git.Remote.RemoveRemote(remote.Name); err != nil {
|
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{
|
return gui.PopupHandler.Prompt(popup.PromptOpts{
|
||||||
title: editNameMessage,
|
Title: editNameMessage,
|
||||||
initialContent: remote.Name,
|
InitialContent: remote.Name,
|
||||||
handleConfirm: func(updatedRemoteName string) error {
|
HandleConfirm: func(updatedRemoteName string) error {
|
||||||
if updatedRemoteName != remote.Name {
|
if updatedRemoteName != remote.Name {
|
||||||
gui.logAction(gui.Tr.Actions.UpdateRemote)
|
gui.logAction(gui.Tr.Actions.UpdateRemote)
|
||||||
if err := gui.Git.Remote.RenameRemote(remote.Name, updatedRemoteName); err != nil {
|
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]
|
url = urls[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.prompt(promptOpts{
|
return gui.PopupHandler.Prompt(popup.PromptOpts{
|
||||||
title: editUrlMessage,
|
Title: editUrlMessage,
|
||||||
initialContent: url,
|
InitialContent: url,
|
||||||
handleConfirm: func(updatedRemoteUrl string) error {
|
HandleConfirm: func(updatedRemoteUrl string) error {
|
||||||
gui.logAction(gui.Tr.Actions.UpdateRemote)
|
gui.logAction(gui.Tr.Actions.UpdateRemote)
|
||||||
if err := gui.Git.Remote.UpdateRemoteUrl(updatedRemoteName, updatedRemoteUrl); err != nil {
|
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 nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.WithWaitingStatus(gui.Tr.FetchingRemoteStatus, func() error {
|
return gui.PopupHandler.WithWaitingStatus(gui.Tr.FetchingRemoteStatus, func() error {
|
||||||
gui.Mutexes.FetchMutex.Lock()
|
gui.Mutexes.FetchMutex.Lock()
|
||||||
defer gui.Mutexes.FetchMutex.Unlock()
|
defer gui.Mutexes.FetchMutex.Unlock()
|
||||||
|
|
||||||
err := gui.Git.Sync.FetchRemote(remote.Name)
|
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 (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
"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 {
|
func (gui *Gui) resetToRef(ref string, strength string, envVars []string) error {
|
||||||
if err := gui.Git.Commit.ResetToCommit(ref, strength, envVars); err != nil {
|
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
|
gui.State.Panels.Commits.SelectedLineIdx = 0
|
||||||
@ -20,7 +22,7 @@ func (gui *Gui) resetToRef(ref string, strength string, envVars []string) error
|
|||||||
return err
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,20 +31,23 @@ func (gui *Gui) resetToRef(ref string, strength string, envVars []string) error
|
|||||||
|
|
||||||
func (gui *Gui) createResetMenu(ref string) error {
|
func (gui *Gui) createResetMenu(ref string) error {
|
||||||
strengths := []string{"soft", "mixed", "hard"}
|
strengths := []string{"soft", "mixed", "hard"}
|
||||||
menuItems := make([]*menuItem, len(strengths))
|
menuItems := make([]*popup.MenuItem, len(strengths))
|
||||||
for i, strength := range strengths {
|
for i, strength := range strengths {
|
||||||
strength := strength
|
strength := strength
|
||||||
menuItems[i] = &menuItem{
|
menuItems[i] = &popup.MenuItem{
|
||||||
displayStrings: []string{
|
DisplayStrings: []string{
|
||||||
fmt.Sprintf("%s reset", strength),
|
fmt.Sprintf("%s reset", strength),
|
||||||
style.FgRed.Sprintf("reset --%s %s", strength, ref),
|
style.FgRed.Sprintf("reset --%s %s", strength, ref),
|
||||||
},
|
},
|
||||||
onPress: func() error {
|
OnPress: func() error {
|
||||||
gui.logAction("Reset")
|
gui.logAction("Reset")
|
||||||
return gui.resetToRef(ref, strength, []string{})
|
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"
|
"strings"
|
||||||
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/patch"
|
"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 {
|
func (gui *Gui) refreshStagingPanel(forceSecondaryFocused bool, selectedLineIdx int) error {
|
||||||
@ -112,10 +114,10 @@ func (gui *Gui) handleResetSelection() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !gui.UserConfig.Gui.SkipUnstageLineWarning {
|
if !gui.UserConfig.Gui.SkipUnstageLineWarning {
|
||||||
return gui.ask(askOpts{
|
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: gui.Tr.UnstageLinesTitle,
|
Title: gui.Tr.UnstageLinesTitle,
|
||||||
prompt: gui.Tr.UnstageLinesPrompt,
|
Prompt: gui.Tr.UnstageLinesPrompt,
|
||||||
handleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
return gui.withLBLActiveCheck(func(state *LblPanelState) error {
|
return gui.withLBLActiveCheck(func(state *LblPanelState) error {
|
||||||
return gui.applySelection(true, state)
|
return gui.applySelection(true, state)
|
||||||
})
|
})
|
||||||
@ -149,14 +151,14 @@ func (gui *Gui) applySelection(reverse bool, state *LblPanelState) error {
|
|||||||
gui.logAction(gui.Tr.Actions.ApplyPatch)
|
gui.logAction(gui.Tr.Actions.ApplyPatch)
|
||||||
err := gui.Git.WorkingTree.ApplyPatch(patch, applyFlags...)
|
err := gui.Git.WorkingTree.ApplyPatch(patch, applyFlags...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if state.SelectingRange() {
|
if state.SelectingRange() {
|
||||||
state.SetLineSelectMode()
|
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
|
return err
|
||||||
}
|
}
|
||||||
if err := gui.refreshStagingPanel(false, -1); err != nil {
|
if err := gui.refreshStagingPanel(false, -1); err != nil {
|
||||||
|
@ -2,6 +2,8 @@ package gui
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// list panel functions
|
// list panel functions
|
||||||
@ -54,7 +56,7 @@ func (gui *Gui) handleStashApply() error {
|
|||||||
err := gui.Git.Stash.Apply(stashEntry.Index)
|
err := gui.Git.Stash.Apply(stashEntry.Index)
|
||||||
_ = gui.postStashRefresh()
|
_ = gui.postStashRefresh()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -63,10 +65,10 @@ func (gui *Gui) handleStashApply() error {
|
|||||||
return apply()
|
return apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: gui.Tr.StashApply,
|
Title: gui.Tr.StashApply,
|
||||||
prompt: gui.Tr.SureApplyStashEntry,
|
Prompt: gui.Tr.SureApplyStashEntry,
|
||||||
handleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
return apply()
|
return apply()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -85,7 +87,7 @@ func (gui *Gui) handleStashPop() error {
|
|||||||
err := gui.Git.Stash.Pop(stashEntry.Index)
|
err := gui.Git.Stash.Pop(stashEntry.Index)
|
||||||
_ = gui.postStashRefresh()
|
_ = gui.postStashRefresh()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -94,10 +96,10 @@ func (gui *Gui) handleStashPop() error {
|
|||||||
return pop()
|
return pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: gui.Tr.StashPop,
|
Title: gui.Tr.StashPop,
|
||||||
prompt: gui.Tr.SurePopStashEntry,
|
Prompt: gui.Tr.SurePopStashEntry,
|
||||||
handleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
return pop()
|
return pop()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -109,15 +111,15 @@ func (gui *Gui) handleStashDrop() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: gui.Tr.StashDrop,
|
Title: gui.Tr.StashDrop,
|
||||||
prompt: gui.Tr.SureDropStashEntry,
|
Prompt: gui.Tr.SureDropStashEntry,
|
||||||
handleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
gui.logAction(gui.Tr.Actions.Stash)
|
gui.logAction(gui.Tr.Actions.Stash)
|
||||||
err := gui.Git.Stash.Drop(stashEntry.Index)
|
err := gui.Git.Stash.Drop(stashEntry.Index)
|
||||||
_ = gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{STASH}})
|
_ = gui.refreshSidePanels(refreshOptions{scope: []RefreshableView{STASH}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
@ -125,12 +127,12 @@ func (gui *Gui) handleStashDrop() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) postStashRefresh() 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 {
|
func (gui *Gui) handleStashSave(stashFunc func(message string) error) error {
|
||||||
if len(gui.trackedFiles()) == 0 && len(gui.stagedFiles()) == 0 {
|
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{
|
return gui.prompt(promptOpts{
|
||||||
@ -139,7 +141,7 @@ func (gui *Gui) handleStashSave(stashFunc func(message string) error) error {
|
|||||||
err := stashFunc(stashComment)
|
err := stashFunc(stashComment)
|
||||||
_ = gui.postStashRefresh()
|
_ = gui.postStashRefresh()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
||||||
"github.com/jesseduffield/lazygit/pkg/constants"
|
"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/presentation"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
@ -49,8 +50,10 @@ func cursorInSubstring(cx int, prefix string, substring string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleCheckForUpdate() error {
|
func (gui *Gui) handleCheckForUpdate() error {
|
||||||
gui.Updater.CheckForNewUpdate(gui.onUserUpdateCheckFinish, true)
|
return gui.PopupHandler.WithWaitingStatus(gui.Tr.CheckingForUpdates, func() error {
|
||||||
return gui.createLoaderPanel(gui.Tr.CheckingForUpdates)
|
gui.Updater.CheckForNewUpdate(gui.onUserUpdateCheckFinish, true)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleStatusClick() error {
|
func (gui *Gui) handleStatusClick() error {
|
||||||
@ -136,17 +139,21 @@ func (gui *Gui) askForConfigFile(action func(file string) error) error {
|
|||||||
case 1:
|
case 1:
|
||||||
return action(confPaths[0])
|
return action(confPaths[0])
|
||||||
default:
|
default:
|
||||||
menuItems := make([]*menuItem, len(confPaths))
|
menuItems := make([]*popup.MenuItem, len(confPaths))
|
||||||
for i, file := range confPaths {
|
for i, file := range confPaths {
|
||||||
i := i
|
i := i
|
||||||
menuItems[i] = &menuItem{
|
menuItems[i] = &popup.MenuItem{
|
||||||
displayString: file,
|
DisplayString: file,
|
||||||
onPress: func() error {
|
OnPress: func() error {
|
||||||
return action(confPaths[i])
|
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 (
|
import (
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/loaders"
|
"github.com/jesseduffield/lazygit/pkg/commands/loaders"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||||
)
|
)
|
||||||
|
|
||||||
// list panel functions
|
// list panel functions
|
||||||
@ -42,10 +43,10 @@ func (gui *Gui) handleCheckoutSubCommit() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
err := gui.ask(askOpts{
|
err := gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: gui.Tr.LcCheckoutCommit,
|
Title: gui.Tr.LcCheckoutCommit,
|
||||||
prompt: gui.Tr.SureCheckoutThisCommit,
|
Prompt: gui.Tr.SureCheckoutThisCommit,
|
||||||
handleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
gui.logAction(gui.Tr.Actions.CheckoutCommit)
|
gui.logAction(gui.Tr.Actions.CheckoutCommit)
|
||||||
return gui.handleCheckoutRef(commit.Sha, handleCheckoutRefOptions{})
|
return gui.handleCheckoutRef(commit.Sha, handleCheckoutRefOptions{})
|
||||||
},
|
},
|
||||||
|
@ -3,8 +3,6 @@ package gui
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||||
@ -36,7 +34,7 @@ func (gui *Gui) submodulesRenderToMain() error {
|
|||||||
if file == nil {
|
if file == nil {
|
||||||
task = NewRenderStringTask(prefix)
|
task = NewRenderStringTask(prefix)
|
||||||
} else {
|
} 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)
|
task = NewRunCommandTaskWithPrefix(cmdObj.GetCmd(), prefix)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -60,205 +58,12 @@ func (gui *Gui) refreshStateSubmoduleConfigs() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleSubmoduleEnter(submodule *models.SubmoduleConfig) error {
|
|
||||||
return gui.enterSubmodule(submodule)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) enterSubmodule(submodule *models.SubmoduleConfig) error {
|
func (gui *Gui) enterSubmodule(submodule *models.SubmoduleConfig) error {
|
||||||
wd, err := os.Getwd()
|
wd, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
gui.RepoPathStack = append(gui.RepoPathStack, wd)
|
gui.RepoPathStack.Push(wd)
|
||||||
|
|
||||||
return gui.dispatchSwitchToRepo(submodule.Path, true)
|
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 (
|
import (
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"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"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -41,7 +43,7 @@ func (gui *Gui) tagsRenderToMain() error {
|
|||||||
func (gui *Gui) refreshTags() error {
|
func (gui *Gui) refreshTags() error {
|
||||||
tags, err := gui.Git.Loaders.Tags.GetTags()
|
tags, err := gui.Git.Loaders.Tags.GetTags()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
gui.State.Tags = tags
|
gui.State.Tags = tags
|
||||||
@ -78,15 +80,15 @@ func (gui *Gui) handleDeleteTag(tag *models.Tag) error {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
return gui.ask(askOpts{
|
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: gui.Tr.DeleteTagTitle,
|
Title: gui.Tr.DeleteTagTitle,
|
||||||
prompt: prompt,
|
Prompt: prompt,
|
||||||
handleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
gui.logAction(gui.Tr.Actions.DeleteTag)
|
gui.logAction(gui.Tr.Actions.DeleteTag)
|
||||||
if err := gui.Git.Tag.Delete(tag.Name); err != nil {
|
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{
|
return gui.PopupHandler.Prompt(popup.PromptOpts{
|
||||||
title: title,
|
Title: title,
|
||||||
initialContent: "origin",
|
InitialContent: "origin",
|
||||||
findSuggestionsFunc: gui.getRemoteSuggestionsFunc(),
|
FindSuggestionsFunc: gui.getRemoteSuggestionsFunc(),
|
||||||
handleConfirm: func(response string) error {
|
HandleConfirm: func(response string) error {
|
||||||
return gui.WithWaitingStatus(gui.Tr.PushingTagStatus, func() error {
|
return gui.PopupHandler.WithWaitingStatus(gui.Tr.PushingTagStatus, func() error {
|
||||||
gui.logAction(gui.Tr.Actions.PushTag)
|
gui.logAction(gui.Tr.Actions.PushTag)
|
||||||
err := gui.Git.Tag.Push(response, tag.Name)
|
err := gui.Git.Tag.Push(response, tag.Name)
|
||||||
gui.handleCredentialsPopup(err)
|
if err != nil {
|
||||||
|
_ = gui.PopupHandler.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
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 (
|
import (
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
"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"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -89,7 +91,7 @@ func (gui *Gui) reflogUndo() error {
|
|||||||
undoingStatus := gui.Tr.UndoingStatus
|
undoingStatus := gui.Tr.UndoingStatus
|
||||||
|
|
||||||
if gui.Git.Status.WorkingTreeState() == enums.REBASE_MODE_REBASING {
|
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) {
|
return gui.parseReflogForActions(func(counter int, action reflogAction) (bool, error) {
|
||||||
@ -124,7 +126,7 @@ func (gui *Gui) reflogRedo() error {
|
|||||||
redoingStatus := gui.Tr.RedoingStatus
|
redoingStatus := gui.Tr.RedoingStatus
|
||||||
|
|
||||||
if gui.Git.Status.WorkingTreeState() == enums.REBASE_MODE_REBASING {
|
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) {
|
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 {
|
func (gui *Gui) handleHardResetWithAutoStash(commitSha string, options handleHardResetWithAutoStashOptions) error {
|
||||||
reset := func() error {
|
reset := func() error {
|
||||||
if err := gui.resetToRef(commitSha, "hard", options.EnvVars); err != nil {
|
if err := gui.resetToRef(commitSha, "hard", options.EnvVars); err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -175,24 +177,24 @@ func (gui *Gui) handleHardResetWithAutoStash(commitSha string, options handleHar
|
|||||||
dirtyWorkingTree := len(gui.trackedFiles()) > 0 || len(gui.stagedFiles()) > 0
|
dirtyWorkingTree := len(gui.trackedFiles()) > 0 || len(gui.stagedFiles()) > 0
|
||||||
if dirtyWorkingTree {
|
if dirtyWorkingTree {
|
||||||
// offer to autostash changes
|
// offer to autostash changes
|
||||||
return gui.ask(askOpts{
|
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: gui.Tr.AutoStashTitle,
|
Title: gui.Tr.AutoStashTitle,
|
||||||
prompt: gui.Tr.AutoStashPrompt,
|
Prompt: gui.Tr.AutoStashPrompt,
|
||||||
handleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
return gui.WithWaitingStatus(options.WaitingStatus, func() error {
|
return gui.PopupHandler.WithWaitingStatus(options.WaitingStatus, func() error {
|
||||||
if err := gui.Git.Stash.Save(gui.Tr.StashPrefix + commitSha); err != nil {
|
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 {
|
if err := reset(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err := gui.Git.Stash.Pop(0)
|
err := gui.Git.Stash.Pop(0)
|
||||||
if err := gui.refreshSidePanels(refreshOptions{}); err != nil {
|
if err := gui.refreshSidePanels(types.RefreshOptions{}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
return nil
|
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()
|
return reset()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -4,13 +4,14 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (gui *Gui) showUpdatePrompt(newVersion string) error {
|
func (gui *Gui) showUpdatePrompt(newVersion string) error {
|
||||||
return gui.ask(askOpts{
|
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: "New version available!",
|
Title: "New version available!",
|
||||||
prompt: fmt.Sprintf("Download version %s? (enter/esc)", newVersion),
|
Prompt: fmt.Sprintf("Download version %s? (enter/esc)", newVersion),
|
||||||
handleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
gui.startUpdating(newVersion)
|
gui.startUpdating(newVersion)
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
@ -19,10 +20,10 @@ func (gui *Gui) showUpdatePrompt(newVersion string) error {
|
|||||||
|
|
||||||
func (gui *Gui) onUserUpdateCheckFinish(newVersion string, err error) error {
|
func (gui *Gui) onUserUpdateCheckFinish(newVersion string, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.PopupHandler.Error(err)
|
||||||
}
|
}
|
||||||
if newVersion == "" {
|
if newVersion == "" {
|
||||||
return gui.createErrorPanel("New version not found")
|
return gui.PopupHandler.ErrorMsg("New version not found")
|
||||||
}
|
}
|
||||||
return gui.showUpdatePrompt(newVersion)
|
return gui.showUpdatePrompt(newVersion)
|
||||||
}
|
}
|
||||||
@ -55,7 +56,7 @@ func (gui *Gui) onUpdateFinish(statusId int, err error) error {
|
|||||||
gui.OnUIThread(func() error {
|
gui.OnUIThread(func() error {
|
||||||
_ = gui.renderString(gui.Views.AppStatus, "")
|
_ = gui.renderString(gui.Views.AppStatus, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gui.createErrorPanel("Update failed: " + err.Error())
|
return gui.PopupHandler.ErrorMsg("Update failed: " + err.Error())
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
@ -64,10 +65,10 @@ func (gui *Gui) onUpdateFinish(statusId int, err error) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) createUpdateQuitConfirmation() error {
|
func (gui *Gui) createUpdateQuitConfirmation() error {
|
||||||
return gui.ask(askOpts{
|
return gui.PopupHandler.Ask(popup.AskOpts{
|
||||||
title: "Currently Updating",
|
Title: "Currently Updating",
|
||||||
prompt: "An update is in progress. Are you sure you want to quit?",
|
Prompt: "An update is in progress. Are you sure you want to quit?",
|
||||||
handleConfirm: func() error {
|
HandleConfirm: func() error {
|
||||||
return gocui.ErrQuit
|
return gocui.ErrQuit
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
"github.com/spkg/bom"
|
"github.com/spkg/bom"
|
||||||
)
|
)
|
||||||
@ -15,34 +16,18 @@ func (gui *Gui) getCyclableWindows() []string {
|
|||||||
return []string{"status", "files", "branches", "commits", "stash"}
|
return []string{"status", "files", "branches", "commits", "stash"}
|
||||||
}
|
}
|
||||||
|
|
||||||
// models/views that we can refresh
|
func getScopeNames(scopes []types.RefreshableView) []string {
|
||||||
type RefreshableView int
|
scopeNameMap := map[types.RefreshableView]string{
|
||||||
|
types.COMMITS: "commits",
|
||||||
const (
|
types.BRANCHES: "branches",
|
||||||
COMMITS RefreshableView = iota
|
types.FILES: "files",
|
||||||
BRANCHES
|
types.SUBMODULES: "submodules",
|
||||||
FILES
|
types.STASH: "stash",
|
||||||
STASH
|
types.REFLOG: "reflog",
|
||||||
REFLOG
|
types.TAGS: "tags",
|
||||||
TAGS
|
types.REMOTES: "remotes",
|
||||||
REMOTES
|
types.STATUS: "status",
|
||||||
STATUS
|
types.BISECT_INFO: "bisect",
|
||||||
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",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
scopeNames := make([]string, len(scopes))
|
scopeNames := make([]string, len(scopes))
|
||||||
@ -53,69 +38,55 @@ func getScopeNames(scopes []RefreshableView) []string {
|
|||||||
return scopeNames
|
return scopeNames
|
||||||
}
|
}
|
||||||
|
|
||||||
func getModeName(mode RefreshMode) string {
|
func getModeName(mode types.RefreshMode) string {
|
||||||
switch mode {
|
switch mode {
|
||||||
case SYNC:
|
case types.SYNC:
|
||||||
return "sync"
|
return "sync"
|
||||||
case ASYNC:
|
case types.ASYNC:
|
||||||
return "async"
|
return "async"
|
||||||
case BLOCK_UI:
|
case types.BLOCK_UI:
|
||||||
return "block-ui"
|
return "block-ui"
|
||||||
default:
|
default:
|
||||||
return "unknown mode"
|
return "unknown mode"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type RefreshMode int
|
func arrToMap(arr []types.RefreshableView) map[types.RefreshableView]bool {
|
||||||
|
output := map[types.RefreshableView]bool{}
|
||||||
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{}
|
|
||||||
for _, el := range arr {
|
for _, el := range arr {
|
||||||
output[el] = true
|
output[el] = true
|
||||||
}
|
}
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) refreshSidePanels(options refreshOptions) error {
|
func (gui *Gui) refreshSidePanels(options types.RefreshOptions) error {
|
||||||
if options.scope == nil {
|
if options.Scope == nil {
|
||||||
gui.Log.Infof(
|
gui.Log.Infof(
|
||||||
"refreshing all scopes in %s mode",
|
"refreshing all scopes in %s mode",
|
||||||
getModeName(options.mode),
|
getModeName(options.Mode),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
gui.Log.Infof(
|
gui.Log.Infof(
|
||||||
"refreshing the following scopes in %s mode: %s",
|
"refreshing the following scopes in %s mode: %s",
|
||||||
getModeName(options.mode),
|
getModeName(options.Mode),
|
||||||
strings.Join(getScopeNames(options.scope), ","),
|
strings.Join(getScopeNames(options.Scope), ","),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
wg := sync.WaitGroup{}
|
wg := sync.WaitGroup{}
|
||||||
|
|
||||||
f := func() {
|
f := func() {
|
||||||
var scopeMap map[RefreshableView]bool
|
var scopeMap map[types.RefreshableView]bool
|
||||||
if len(options.scope) == 0 {
|
if len(options.Scope) == 0 {
|
||||||
scopeMap = arrToMap([]RefreshableView{COMMITS, BRANCHES, FILES, STASH, REFLOG, TAGS, REMOTES, STATUS, BISECT_INFO})
|
scopeMap = arrToMap([]types.RefreshableView{types.COMMITS, types.BRANCHES, types.FILES, types.STASH, types.REFLOG, types.TAGS, types.REMOTES, types.STATUS, types.BISECT_INFO})
|
||||||
} else {
|
} 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)
|
wg.Add(1)
|
||||||
func() {
|
func() {
|
||||||
if options.mode == ASYNC {
|
if options.Mode == types.ASYNC {
|
||||||
go utils.Safe(func() { gui.refreshCommits() })
|
go utils.Safe(func() { gui.refreshCommits() })
|
||||||
} else {
|
} else {
|
||||||
gui.refreshCommits()
|
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)
|
wg.Add(1)
|
||||||
func() {
|
func() {
|
||||||
if options.mode == ASYNC {
|
if options.Mode == types.ASYNC {
|
||||||
go utils.Safe(func() { _ = gui.refreshFilesAndSubmodules() })
|
go utils.Safe(func() { _ = gui.refreshFilesAndSubmodules() })
|
||||||
} else {
|
} else {
|
||||||
_ = gui.refreshFilesAndSubmodules()
|
_ = gui.refreshFilesAndSubmodules()
|
||||||
@ -136,10 +107,10 @@ func (gui *Gui) refreshSidePanels(options refreshOptions) error {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
if scopeMap[STASH] {
|
if scopeMap[types.STASH] {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
func() {
|
func() {
|
||||||
if options.mode == ASYNC {
|
if options.Mode == types.ASYNC {
|
||||||
go utils.Safe(func() { _ = gui.refreshStashEntries() })
|
go utils.Safe(func() { _ = gui.refreshStashEntries() })
|
||||||
} else {
|
} else {
|
||||||
_ = gui.refreshStashEntries()
|
_ = gui.refreshStashEntries()
|
||||||
@ -148,10 +119,10 @@ func (gui *Gui) refreshSidePanels(options refreshOptions) error {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
if scopeMap[TAGS] {
|
if scopeMap[types.TAGS] {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
func() {
|
func() {
|
||||||
if options.mode == ASYNC {
|
if options.Mode == types.ASYNC {
|
||||||
go utils.Safe(func() { _ = gui.refreshTags() })
|
go utils.Safe(func() { _ = gui.refreshTags() })
|
||||||
} else {
|
} else {
|
||||||
_ = gui.refreshTags()
|
_ = gui.refreshTags()
|
||||||
@ -160,10 +131,10 @@ func (gui *Gui) refreshSidePanels(options refreshOptions) error {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
if scopeMap[REMOTES] {
|
if scopeMap[types.REMOTES] {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
func() {
|
func() {
|
||||||
if options.mode == ASYNC {
|
if options.Mode == types.ASYNC {
|
||||||
go utils.Safe(func() { _ = gui.refreshRemotes() })
|
go utils.Safe(func() { _ = gui.refreshRemotes() })
|
||||||
} else {
|
} else {
|
||||||
_ = gui.refreshRemotes()
|
_ = gui.refreshRemotes()
|
||||||
@ -176,12 +147,12 @@ func (gui *Gui) refreshSidePanels(options refreshOptions) error {
|
|||||||
|
|
||||||
gui.refreshStatus()
|
gui.refreshStatus()
|
||||||
|
|
||||||
if options.then != nil {
|
if options.Then != nil {
|
||||||
options.then()
|
options.Then()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.mode == BLOCK_UI {
|
if options.Mode == types.BLOCK_UI {
|
||||||
gui.OnUIThread(func() error {
|
gui.OnUIThread(func() error {
|
||||||
f()
|
f()
|
||||||
return nil
|
return nil
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package gui
|
package gui
|
||||||
|
|
||||||
func (gui *Gui) toggleWhitespaceInDiffView() error {
|
func (gui *Gui) toggleWhitespaceInDiffView() error {
|
||||||
gui.State.IgnoreWhitespaceInDiffView = !gui.State.IgnoreWhitespaceInDiffView
|
gui.IgnoreWhitespaceInDiffView = !gui.IgnoreWhitespaceInDiffView
|
||||||
|
|
||||||
toastMessage := gui.Tr.ShowingWhitespaceInDiffView
|
toastMessage := gui.Tr.ShowingWhitespaceInDiffView
|
||||||
if gui.State.IgnoreWhitespaceInDiffView {
|
if gui.IgnoreWhitespaceInDiffView {
|
||||||
toastMessage = gui.Tr.IgnoringWhitespaceInDiffView
|
toastMessage = gui.Tr.IgnoringWhitespaceInDiffView
|
||||||
}
|
}
|
||||||
gui.raiseToast(toastMessage)
|
gui.raiseToast(toastMessage)
|
||||||
|
@ -3,7 +3,9 @@ package gui
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/popup"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (gui *Gui) handleCreateResetMenu() error {
|
func (gui *Gui) handleCreateResetMenu() error {
|
||||||
@ -14,92 +16,92 @@ func (gui *Gui) handleCreateResetMenu() error {
|
|||||||
nukeStr = fmt.Sprintf("%s (%s)", nukeStr, gui.Tr.LcAndResetSubmodules)
|
nukeStr = fmt.Sprintf("%s (%s)", nukeStr, gui.Tr.LcAndResetSubmodules)
|
||||||
}
|
}
|
||||||
|
|
||||||
menuItems := []*menuItem{
|
menuItems := []*popup.MenuItem{
|
||||||
{
|
{
|
||||||
displayStrings: []string{
|
DisplayStrings: []string{
|
||||||
gui.Tr.LcDiscardAllChangesToAllFiles,
|
gui.Tr.LcDiscardAllChangesToAllFiles,
|
||||||
red.Sprint(nukeStr),
|
red.Sprint(nukeStr),
|
||||||
},
|
},
|
||||||
onPress: func() error {
|
OnPress: func() error {
|
||||||
gui.logAction(gui.Tr.Actions.NukeWorkingTree)
|
gui.logAction(gui.Tr.Actions.NukeWorkingTree)
|
||||||
if err := gui.Git.WorkingTree.ResetAndClean(); err != nil {
|
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,
|
gui.Tr.LcDiscardAnyUnstagedChanges,
|
||||||
red.Sprint("git checkout -- ."),
|
red.Sprint("git checkout -- ."),
|
||||||
},
|
},
|
||||||
onPress: func() error {
|
OnPress: func() error {
|
||||||
gui.logAction(gui.Tr.Actions.DiscardUnstagedFileChanges)
|
gui.logAction(gui.Tr.Actions.DiscardUnstagedFileChanges)
|
||||||
if err := gui.Git.WorkingTree.DiscardAnyUnstagedFileChanges(); err != nil {
|
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,
|
gui.Tr.LcDiscardUntrackedFiles,
|
||||||
red.Sprint("git clean -fd"),
|
red.Sprint("git clean -fd"),
|
||||||
},
|
},
|
||||||
onPress: func() error {
|
OnPress: func() error {
|
||||||
gui.logAction(gui.Tr.Actions.RemoveUntrackedFiles)
|
gui.logAction(gui.Tr.Actions.RemoveUntrackedFiles)
|
||||||
if err := gui.Git.WorkingTree.RemoveUntrackedFiles(); err != nil {
|
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,
|
gui.Tr.LcSoftReset,
|
||||||
red.Sprint("git reset --soft HEAD"),
|
red.Sprint("git reset --soft HEAD"),
|
||||||
},
|
},
|
||||||
onPress: func() error {
|
OnPress: func() error {
|
||||||
gui.logAction(gui.Tr.Actions.SoftReset)
|
gui.logAction(gui.Tr.Actions.SoftReset)
|
||||||
if err := gui.Git.WorkingTree.ResetSoft("HEAD"); err != nil {
|
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",
|
"mixed reset",
|
||||||
red.Sprint("git reset --mixed HEAD"),
|
red.Sprint("git reset --mixed HEAD"),
|
||||||
},
|
},
|
||||||
onPress: func() error {
|
OnPress: func() error {
|
||||||
gui.logAction(gui.Tr.Actions.MixedReset)
|
gui.logAction(gui.Tr.Actions.MixedReset)
|
||||||
if err := gui.Git.WorkingTree.ResetMixed("HEAD"); err != nil {
|
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,
|
gui.Tr.LcHardReset,
|
||||||
red.Sprint("git reset --hard HEAD"),
|
red.Sprint("git reset --hard HEAD"),
|
||||||
},
|
},
|
||||||
onPress: func() error {
|
OnPress: func() error {
|
||||||
gui.logAction(gui.Tr.Actions.HardReset)
|
gui.logAction(gui.Tr.Actions.HardReset)
|
||||||
if err := gui.Git.WorkingTree.ResetHard("HEAD"); err != nil {
|
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: "初始化子模块",
|
InitialiseSubmodule: "初始化子模块",
|
||||||
BulkInitialiseSubmodules: "批量初始化子模块",
|
BulkInitialiseSubmodules: "批量初始化子模块",
|
||||||
BulkUpdateSubmodules: "批量更新子模块",
|
BulkUpdateSubmodules: "批量更新子模块",
|
||||||
BulkStashAndResetSubmodules: "批量存储和重置子模块",
|
|
||||||
BulkDeinitialiseSubmodules: "批量取消初始化子模块",
|
BulkDeinitialiseSubmodules: "批量取消初始化子模块",
|
||||||
UpdateSubmodule: "更新子模块",
|
UpdateSubmodule: "更新子模块",
|
||||||
DeleteTag: "删除标签",
|
DeleteTag: "删除标签",
|
||||||
|
@ -540,7 +540,6 @@ type Actions struct {
|
|||||||
InitialiseSubmodule string
|
InitialiseSubmodule string
|
||||||
BulkInitialiseSubmodules string
|
BulkInitialiseSubmodules string
|
||||||
BulkUpdateSubmodules string
|
BulkUpdateSubmodules string
|
||||||
BulkStashAndResetSubmodules string
|
|
||||||
BulkDeinitialiseSubmodules string
|
BulkDeinitialiseSubmodules string
|
||||||
UpdateSubmodule string
|
UpdateSubmodule string
|
||||||
CreateLightweightTag string
|
CreateLightweightTag string
|
||||||
@ -651,7 +650,7 @@ func EnglishTranslationSet() TranslationSet {
|
|||||||
NoBranchesThisRepo: "No branches for this repo",
|
NoBranchesThisRepo: "No branches for this repo",
|
||||||
CommitMessageConfirm: "{{.keyBindClose}}: close, {{.keyBindNewLine}}: new line, {{.keyBindConfirm}}: confirm",
|
CommitMessageConfirm: "{{.keyBindClose}}: close, {{.keyBindNewLine}}: new line, {{.keyBindConfirm}}: confirm",
|
||||||
CommitWithoutMessageErr: "You cannot commit without a commit message",
|
CommitWithoutMessageErr: "You cannot commit without a commit message",
|
||||||
CloseConfirm: "{{.keyBindClose}}: close, {{.keyBindConfirm}}: confirm",
|
CloseConfirm: "{{.keyBindClose}}: close/cancel, {{.keyBindConfirm}}: confirm",
|
||||||
LcClose: "close",
|
LcClose: "close",
|
||||||
LcQuit: "quit",
|
LcQuit: "quit",
|
||||||
LcSquashDown: "squash down",
|
LcSquashDown: "squash down",
|
||||||
@ -1097,7 +1096,6 @@ func EnglishTranslationSet() TranslationSet {
|
|||||||
InitialiseSubmodule: "Initialise submodule",
|
InitialiseSubmodule: "Initialise submodule",
|
||||||
BulkInitialiseSubmodules: "Bulk initialise submodules",
|
BulkInitialiseSubmodules: "Bulk initialise submodules",
|
||||||
BulkUpdateSubmodules: "Bulk update submodules",
|
BulkUpdateSubmodules: "Bulk update submodules",
|
||||||
BulkStashAndResetSubmodules: "Bulk stash and reset submodules",
|
|
||||||
BulkDeinitialiseSubmodules: "Bulk deinitialise submodules",
|
BulkDeinitialiseSubmodules: "Bulk deinitialise submodules",
|
||||||
UpdateSubmodule: "Update submodule",
|
UpdateSubmodule: "Update submodule",
|
||||||
DeleteTag: "Delete tag",
|
DeleteTag: "Delete tag",
|
||||||
|
@ -144,12 +144,10 @@ func (u *Updater) CheckForNewUpdate(onFinish func(string, error) error, userRequ
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
go utils.Safe(func() {
|
newVersion, err := u.checkForNewUpdate()
|
||||||
newVersion, err := u.checkForNewUpdate()
|
if err = onFinish(newVersion, err); err != nil {
|
||||||
if err = onFinish(newVersion, err); err != nil {
|
u.Log.Error(err)
|
||||||
u.Log.Error(err)
|
}
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Updater) skipUpdateCheck() bool {
|
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…
x
Reference in New Issue
Block a user