Use first class task objects instead of global counter

The global counter approach is easy to understand but it's brittle and depends on implicit behaviour that is not very discoverable.

With a global counter, if any goroutine accidentally decrements the counter twice, we'll think lazygit is idle when it's actually busy.
Likewise if a goroutine accidentally increments the counter twice we'll think lazygit is busy when it's actually idle.
With the new approach we have a map of tasks where each task can either be busy or not. We create a new task and add it to the map
when we spawn a worker goroutine (among other things) and we remove it once the task is done.

The task can also be paused and continued for situations where we switch back and forth between running a program and asking for user
input.

In order for this to work with `git push` (and other commands that require credentials) we need to obtain the task from gocui when
we create the worker goroutine, and then pass it along to the commands package to pause/continue the task as required. This is
MUCH more discoverable than the old approach which just decremented and incremented the global counter from within the commands package,
but it's at the cost of expanding some function signatures (arguably a good thing).

Likewise, whenever you want to call WithWaitingStatus or WithLoaderPanel the callback will now have access to the task for pausing/
continuing. We only need to actually make use of this functionality in a couple of places so it's a high price to pay, but I don't
know if I want to introduce a WithWaitingStatusTask and WithLoaderPanelTask function (open to suggestions).
This commit is contained in:
Jesse Duffield
2023-07-09 11:32:27 +10:00
parent 9e79ee5fe3
commit 14ecc15e71
43 changed files with 320 additions and 247 deletions

View File

@@ -2,6 +2,8 @@ package git_commands
import (
"fmt"
"github.com/jesseduffield/gocui"
)
type RemoteCommands struct {
@@ -46,12 +48,12 @@ func (self *RemoteCommands) UpdateRemoteUrl(remoteName string, updatedUrl string
return self.cmd.New(cmdArgs).Run()
}
func (self *RemoteCommands) DeleteRemoteBranch(remoteName string, branchName string) error {
func (self *RemoteCommands) DeleteRemoteBranch(task *gocui.Task, remoteName string, branchName string) error {
cmdArgs := NewGitCmd("push").
Arg(remoteName, "--delete", branchName).
ToArgv()
return self.cmd.New(cmdArgs).PromptOnCredentialRequest().WithMutex(self.syncMutex).Run()
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).WithMutex(self.syncMutex).Run()
}
// CheckRemoteBranchExists Returns remote branch

View File

@@ -2,6 +2,7 @@ package git_commands
import (
"github.com/go-errors/errors"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
)
@@ -23,7 +24,7 @@ type PushOpts struct {
SetUpstream bool
}
func (self *SyncCommands) PushCmdObj(opts PushOpts) (oscommands.ICmdObj, error) {
func (self *SyncCommands) PushCmdObj(task *gocui.Task, opts PushOpts) (oscommands.ICmdObj, error) {
if opts.UpstreamBranch != "" && opts.UpstreamRemote == "" {
return nil, errors.New(self.Tr.MustSpecifyOriginError)
}
@@ -35,12 +36,12 @@ func (self *SyncCommands) PushCmdObj(opts PushOpts) (oscommands.ICmdObj, error)
ArgIf(opts.UpstreamBranch != "", opts.UpstreamBranch).
ToArgv()
cmdObj := self.cmd.New(cmdArgs).PromptOnCredentialRequest().WithMutex(self.syncMutex)
cmdObj := self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).WithMutex(self.syncMutex)
return cmdObj, nil
}
func (self *SyncCommands) Push(opts PushOpts) error {
cmdObj, err := self.PushCmdObj(opts)
func (self *SyncCommands) Push(task *gocui.Task, opts PushOpts) error {
cmdObj, err := self.PushCmdObj(task, opts)
if err != nil {
return err
}
@@ -48,28 +49,24 @@ func (self *SyncCommands) Push(opts PushOpts) error {
return cmdObj.Run()
}
type FetchOptions struct {
Background bool
}
// Fetch fetch git repo
func (self *SyncCommands) FetchCmdObj(opts FetchOptions) oscommands.ICmdObj {
func (self *SyncCommands) Fetch(task *gocui.Task) error {
cmdArgs := NewGitCmd("fetch").
ArgIf(self.UserConfig.Git.FetchAll, "--all").
ToArgv()
cmdObj := self.cmd.New(cmdArgs)
if opts.Background {
cmdObj.DontLog().FailOnCredentialRequest()
} else {
cmdObj.PromptOnCredentialRequest()
}
return cmdObj.WithMutex(self.syncMutex)
cmdObj.PromptOnCredentialRequest(task)
return cmdObj.WithMutex(self.syncMutex).Run()
}
func (self *SyncCommands) Fetch(opts FetchOptions) error {
cmdObj := self.FetchCmdObj(opts)
return cmdObj.Run()
func (self *SyncCommands) FetchBackground() error {
cmdArgs := NewGitCmd("fetch").
ArgIf(self.UserConfig.Git.FetchAll, "--all").
ToArgv()
cmdObj := self.cmd.New(cmdArgs)
cmdObj.DontLog().FailOnCredentialRequest()
return cmdObj.WithMutex(self.syncMutex).Run()
}
type PullOptions struct {
@@ -78,7 +75,7 @@ type PullOptions struct {
FastForwardOnly bool
}
func (self *SyncCommands) Pull(opts PullOptions) error {
func (self *SyncCommands) Pull(task *gocui.Task, opts PullOptions) error {
cmdArgs := NewGitCmd("pull").
Arg("--no-edit").
ArgIf(opts.FastForwardOnly, "--ff-only").
@@ -88,22 +85,22 @@ func (self *SyncCommands) Pull(opts PullOptions) error {
// setting GIT_SEQUENCE_EDITOR to ':' as a way of skipping it, in case the user
// has 'pull.rebase = interactive' configured.
return self.cmd.New(cmdArgs).AddEnvVars("GIT_SEQUENCE_EDITOR=:").PromptOnCredentialRequest().WithMutex(self.syncMutex).Run()
return self.cmd.New(cmdArgs).AddEnvVars("GIT_SEQUENCE_EDITOR=:").PromptOnCredentialRequest(task).WithMutex(self.syncMutex).Run()
}
func (self *SyncCommands) FastForward(branchName string, remoteName string, remoteBranchName string) error {
func (self *SyncCommands) FastForward(task *gocui.Task, branchName string, remoteName string, remoteBranchName string) error {
cmdArgs := NewGitCmd("fetch").
Arg(remoteName).
Arg(remoteBranchName + ":" + branchName).
ToArgv()
return self.cmd.New(cmdArgs).PromptOnCredentialRequest().WithMutex(self.syncMutex).Run()
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).WithMutex(self.syncMutex).Run()
}
func (self *SyncCommands) FetchRemote(remoteName string) error {
func (self *SyncCommands) FetchRemote(task *gocui.Task, remoteName string) error {
cmdArgs := NewGitCmd("fetch").
Arg(remoteName).
ToArgv()
return self.cmd.New(cmdArgs).PromptOnCredentialRequest().WithMutex(self.syncMutex).Run()
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).WithMutex(self.syncMutex).Run()
}

View File

@@ -1,5 +1,7 @@
package git_commands
import "github.com/jesseduffield/gocui"
type TagCommands struct {
*GitCommon
}
@@ -34,9 +36,9 @@ func (self *TagCommands) Delete(tagName string) error {
return self.cmd.New(cmdArgs).Run()
}
func (self *TagCommands) Push(remoteName string, tagName string) error {
func (self *TagCommands) Push(task *gocui.Task, remoteName string, tagName string) error {
cmdArgs := NewGitCmd("push").Arg(remoteName, "tag", tagName).
ToArgv()
return self.cmd.New(cmdArgs).PromptOnCredentialRequest().WithMutex(self.syncMutex).Run()
return self.cmd.New(cmdArgs).PromptOnCredentialRequest(task).WithMutex(self.syncMutex).Run()
}

View File

@@ -4,6 +4,7 @@ import (
"os/exec"
"strings"
"github.com/jesseduffield/gocui"
"github.com/samber/lo"
"github.com/sasha-s/go-deadlock"
)
@@ -56,13 +57,14 @@ type ICmdObj interface {
// returns true if IgnoreEmptyError() was called
ShouldIgnoreEmptyError() bool
PromptOnCredentialRequest() ICmdObj
PromptOnCredentialRequest(task *gocui.Task) ICmdObj
FailOnCredentialRequest() ICmdObj
WithMutex(mutex *deadlock.Mutex) ICmdObj
Mutex() *deadlock.Mutex
GetCredentialStrategy() CredentialStrategy
GetTask() *gocui.Task
}
type CmdObj struct {
@@ -85,6 +87,7 @@ type CmdObj struct {
// if set to true, it means we might be asked to enter a username/password by this command.
credentialStrategy CredentialStrategy
task *gocui.Task
// can be set so that we don't run certain commands simultaneously
mutex *deadlock.Mutex
@@ -192,8 +195,9 @@ func (self *CmdObj) RunAndProcessLines(onLine func(line string) (bool, error)) e
return self.runner.RunAndProcessLines(self, onLine)
}
func (self *CmdObj) PromptOnCredentialRequest() ICmdObj {
func (self *CmdObj) PromptOnCredentialRequest(task *gocui.Task) ICmdObj {
self.credentialStrategy = PROMPT
self.task = task
return self
}
@@ -207,3 +211,7 @@ func (self *CmdObj) FailOnCredentialRequest() ICmdObj {
func (self *CmdObj) GetCredentialStrategy() CredentialStrategy {
return self.credentialStrategy
}
func (self *CmdObj) GetTask() *gocui.Task {
return self.task
}

View File

@@ -8,6 +8,7 @@ import (
"strings"
"github.com/go-errors/errors"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/sirupsen/logrus"
)
@@ -308,7 +309,7 @@ func (self *cmdObjRunner) runAndDetectCredentialRequest(
tr := io.TeeReader(handler.stdoutPipe, cmdWriter)
go utils.Safe(func() {
self.processOutput(tr, handler.stdinPipe, promptUserForCredential)
self.processOutput(tr, handler.stdinPipe, promptUserForCredential, cmdObj.GetTask())
})
})
}
@@ -317,6 +318,7 @@ func (self *cmdObjRunner) processOutput(
reader io.Reader,
writer io.Writer,
promptUserForCredential func(CredentialType) <-chan string,
task *gocui.Task,
) {
checkForCredentialRequest := self.getCheckForCredentialRequestFunc()
@@ -327,13 +329,9 @@ func (self *cmdObjRunner) processOutput(
askFor, ok := checkForCredentialRequest(newBytes)
if ok {
responseChan := promptUserForCredential(askFor)
// We assume that the busy count is greater than zero here because we're
// in the middle of a command. We decrement it so that The user can be prompted
// without lazygit thinking it's still doing its own processing. This helps
// integration tests know how long to wait before typing in a response.
self.guiIO.DecrementBusyCount()
task.Pause()
toInput := <-responseChan
self.guiIO.IncrementBusyCount()
task.Continue()
// If the return data is empty we don't write anything to stdin
if toInput != "" {
_, _ = writer.Write([]byte(toInput))

View File

@@ -27,9 +27,6 @@ type guiIO struct {
// that a command requests it.
// the 'credential' arg is something like 'username' or 'password'
promptForCredentialFn func(credential CredentialType) <-chan string
IncrementBusyCount func()
DecrementBusyCount func()
}
func NewGuiIO(
@@ -37,16 +34,12 @@ func NewGuiIO(
logCommandFn func(string, bool),
newCmdWriterFn func() io.Writer,
promptForCredentialFn func(CredentialType) <-chan string,
IncrementBusyCount func(),
DecrementBusyCount func(),
) *guiIO {
return &guiIO{
log: log,
logCommandFn: logCommandFn,
newCmdWriterFn: newCmdWriterFn,
promptForCredentialFn: promptForCredentialFn,
IncrementBusyCount: IncrementBusyCount,
DecrementBusyCount: DecrementBusyCount,
}
}
@@ -58,7 +51,5 @@ func NewNullGuiIO(log *logrus.Entry) *guiIO {
logCommandFn: func(string, bool) {},
newCmdWriterFn: func() io.Writer { return io.Discard },
promptForCredentialFn: failPromptFn,
IncrementBusyCount: func() {},
DecrementBusyCount: func() {},
}
}

View File

@@ -4,7 +4,7 @@ import (
"strings"
"time"
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/utils"
)
@@ -86,7 +86,7 @@ func (self *BackgroundRoutineMgr) goEvery(interval time.Duration, stop chan stru
if self.pauseBackgroundRefreshes {
continue
}
self.gui.c.OnWorker(func() { _ = function() })
self.gui.c.OnWorker(func(*gocui.Task) { _ = function() })
case <-stop:
return
}
@@ -95,7 +95,7 @@ func (self *BackgroundRoutineMgr) goEvery(interval time.Duration, stop chan stru
}
func (self *BackgroundRoutineMgr) backgroundFetch() (err error) {
err = self.gui.git.Sync.Fetch(git_commands.FetchOptions{Background: true})
err = self.gui.git.Sync.FetchBackground()
_ = self.gui.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.BRANCHES, types.COMMITS, types.REMOTES, types.TAGS}, Mode: types.ASYNC})

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"strings"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/context"
@@ -363,11 +364,12 @@ func (self *BranchesController) fastForward(branch *models.Branch) error {
},
)
return self.c.WithLoaderPanel(message, func() error {
return self.c.WithLoaderPanel(message, func(task *gocui.Task) error {
if branch == self.c.Helpers().Refs.GetCheckedOutRef() {
self.c.LogAction(action)
err := self.c.Git().Sync.Pull(
task,
git_commands.PullOptions{
RemoteName: branch.UpstreamRemote,
BranchName: branch.UpstreamBranch,
@@ -381,7 +383,7 @@ func (self *BranchesController) fastForward(branch *models.Branch) error {
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
} else {
self.c.LogAction(action)
err := self.c.Git().Sync.FastForward(branch.Name, branch.UpstreamRemote, branch.UpstreamBranch)
err := self.c.Git().Sync.FastForward(task, branch.Name, branch.UpstreamRemote, branch.UpstreamBranch)
if err != nil {
_ = self.c.Error(err)
}

View File

@@ -177,7 +177,7 @@ func (self *CommitFilesController) discard(node *filetree.CommitFileNode) error
Title: self.c.Tr.DiscardFileChangesTitle,
Prompt: prompt,
HandleConfirm: func() error {
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func() error {
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func(*gocui.Task) error {
self.c.LogAction(self.c.Tr.Actions.DiscardOldFileChange)
if err := self.c.Git().Rebase.DiscardOldFileChanges(self.c.Model().Commits, self.c.Contexts().LocalCommits.GetSelectedLineIdx(), node.GetPath()); err != nil {
if err := self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(err); err != nil {
@@ -205,7 +205,7 @@ func (self *CommitFilesController) edit(node *filetree.CommitFileNode) error {
func (self *CommitFilesController) toggleForPatch(node *filetree.CommitFileNode) error {
toggle := func() error {
return self.c.WithWaitingStatus(self.c.Tr.UpdatingPatch, func() error {
return self.c.WithWaitingStatus(self.c.Tr.UpdatingPatch, func(*gocui.Task) error {
if !self.c.Git().Patch.PatchBuilder.Active() {
if err := self.startPatchBuilder(); err != nil {
return err

View File

@@ -3,6 +3,7 @@ package controllers
import (
"fmt"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
"github.com/jesseduffield/lazygit/pkg/gui/types"
)
@@ -116,7 +117,7 @@ func (self *CustomPatchOptionsMenuAction) handleDeletePatchFromCommit() error {
return err
}
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func() error {
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func(*gocui.Task) error {
commitIndex := self.getPatchCommitIndex()
self.c.LogAction(self.c.Tr.Actions.RemovePatchFromCommit)
err := self.c.Git().Patch.DeletePatchesFromCommit(self.c.Model().Commits, commitIndex)
@@ -133,7 +134,7 @@ func (self *CustomPatchOptionsMenuAction) handleMovePatchToSelectedCommit() erro
return err
}
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func() error {
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func(*gocui.Task) error {
commitIndex := self.getPatchCommitIndex()
self.c.LogAction(self.c.Tr.Actions.MovePatchToSelectedCommit)
err := self.c.Git().Patch.MovePatchToSelectedCommit(self.c.Model().Commits, commitIndex, self.c.Contexts().LocalCommits.GetSelectedLineIdx())
@@ -151,7 +152,7 @@ func (self *CustomPatchOptionsMenuAction) handleMovePatchIntoWorkingTree() error
}
pull := func(stash bool) error {
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func() error {
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func(*gocui.Task) error {
commitIndex := self.getPatchCommitIndex()
self.c.LogAction(self.c.Tr.Actions.MovePatchIntoIndex)
err := self.c.Git().Patch.MovePatchIntoIndex(self.c.Model().Commits, commitIndex, stash)
@@ -181,7 +182,7 @@ func (self *CustomPatchOptionsMenuAction) handlePullPatchIntoNewCommit() error {
return err
}
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func() error {
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func(*gocui.Task) error {
commitIndex := self.getPatchCommitIndex()
self.c.LogAction(self.c.Tr.Actions.MovePatchIntoNewCommit)
err := self.c.Git().Patch.PullPatchIntoNewCommit(self.c.Model().Commits, commitIndex)

View File

@@ -4,7 +4,6 @@ import (
"strings"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/context"
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
@@ -801,17 +800,17 @@ func (self *FilesController) onClickSecondary(opts gocui.ViewMouseBindingOpts) e
}
func (self *FilesController) fetch() error {
return self.c.WithLoaderPanel(self.c.Tr.FetchWait, func() error {
if err := self.fetchAux(); err != nil {
return self.c.WithLoaderPanel(self.c.Tr.FetchWait, func(task *gocui.Task) error {
if err := self.fetchAux(task); err != nil {
_ = self.c.Error(err)
}
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
})
}
func (self *FilesController) fetchAux() (err error) {
func (self *FilesController) fetchAux(task *gocui.Task) (err error) {
self.c.LogAction("Fetch")
err = self.c.Git().Sync.Fetch(git_commands.FetchOptions{})
err = self.c.Git().Sync.Fetch(task)
if err != nil && strings.Contains(err.Error(), "exit status 128") {
_ = self.c.ErrorMsg(self.c.Tr.PassUnameWrong)

View File

@@ -1,6 +1,7 @@
package controllers
import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/context"
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
@@ -145,7 +146,7 @@ func (self *FilesRemoveController) remove(node *filetree.FileNode) error {
}
func (self *FilesRemoveController) ResetSubmodule(submodule *models.SubmoduleConfig) error {
return self.c.WithWaitingStatus(self.c.Tr.ResettingSubmoduleStatus, func() error {
return self.c.WithWaitingStatus(self.c.Tr.ResettingSubmoduleStatus, func(*gocui.Task) error {
self.c.LogAction(self.c.Tr.Actions.ResetSubmodule)
file := self.c.Helpers().WorkingTree.FileForSubmodule(submodule)

View File

@@ -3,6 +3,7 @@ package helpers
import (
"time"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/gui/status"
)
@@ -26,12 +27,12 @@ func (self *AppStatusHelper) Toast(message string) {
}
// withWaitingStatus wraps a function and shows a waiting status while the function is still executing
func (self *AppStatusHelper) WithWaitingStatus(message string, f func() error) {
self.c.OnWorker(func() {
func (self *AppStatusHelper) WithWaitingStatus(message string, f func(*gocui.Task) error) {
self.c.OnWorker(func(task *gocui.Task) {
self.statusMgr().WithWaitingStatus(message, func() {
self.renderAppStatus()
if err := f(); err != nil {
if err := f(task); err != nil {
self.c.OnUIThread(func() error {
return self.c.Error(err)
})
@@ -49,7 +50,7 @@ func (self *AppStatusHelper) GetStatusString() string {
}
func (self *AppStatusHelper) renderAppStatus() {
self.c.OnWorker(func() {
self.c.OnWorker(func(_ *gocui.Task) {
ticker := time.NewTicker(time.Millisecond * 50)
defer ticker.Stop()
for range ticker.C {

View File

@@ -1,6 +1,7 @@
package helpers
import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/modes/cherrypicking"
"github.com/jesseduffield/lazygit/pkg/gui/types"
@@ -75,7 +76,7 @@ func (self *CherryPickHelper) Paste() error {
Title: self.c.Tr.CherryPick,
Prompt: self.c.Tr.SureCherryPick,
HandleConfirm: func() error {
return self.c.WithWaitingStatus(self.c.Tr.CherryPickingStatus, func() error {
return self.c.WithWaitingStatus(self.c.Tr.CherryPickingStatus, func(*gocui.Task) error {
self.c.LogAction(self.c.Tr.Actions.CherryPick)
err := self.c.Git().Rebase.CherryPickCommits(self.getData().CherryPickedCommits)
return self.rebaseHelper.CheckMergeOrRebase(err)

View File

@@ -3,6 +3,7 @@ package helpers
import (
"fmt"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/gui/types"
)
@@ -41,7 +42,7 @@ func (self *GpgHelper) WithGpgHandling(cmdObj oscommands.ICmdObj, waitingStatus
}
func (self *GpgHelper) runAndStream(cmdObj oscommands.ICmdObj, waitingStatus string, onSuccess func() error) error {
return self.c.WithWaitingStatus(waitingStatus, func() error {
return self.c.WithWaitingStatus(waitingStatus, func(*gocui.Task) error {
if err := cmdObj.StreamOutput().Run(); err != nil {
_ = self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
return self.c.Error(

View File

@@ -7,6 +7,7 @@ import (
"github.com/jesseduffield/generics/set"
"github.com/jesseduffield/generics/slices"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
@@ -86,7 +87,9 @@ func (self *RefreshHelper) Refresh(options types.RefreshOptions) error {
refresh := func(f func()) {
if options.Mode == types.ASYNC {
self.c.OnWorker(f)
self.c.OnWorker(func(t *gocui.Task) {
f()
})
} else {
f()
}
@@ -198,7 +201,7 @@ func getModeName(mode types.RefreshMode) string {
func (self *RefreshHelper) refreshReflogCommitsConsideringStartup() {
switch self.c.State().GetRepoState().GetStartupStage() {
case types.INITIAL:
self.c.OnWorker(func() {
self.c.OnWorker(func(_ *gocui.Task) {
_ = self.refreshReflogCommits()
self.refreshBranches()
self.c.State().GetRepoState().SetStartupStage(types.COMPLETE)

View File

@@ -5,6 +5,7 @@ import (
"strings"
"github.com/jesseduffield/generics/slices"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/style"
@@ -50,7 +51,7 @@ func (self *RefsHelper) CheckoutRef(ref string, options types.CheckoutRefOptions
self.c.Contexts().LocalCommits.SetLimitCommits(true)
}
return self.c.WithWaitingStatus(waitingStatus, func() error {
return self.c.WithWaitingStatus(waitingStatus, func(*gocui.Task) error {
if err := self.c.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

View File

@@ -5,6 +5,7 @@ import (
"os"
"github.com/jesseduffield/generics/slices"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
"github.com/jesseduffield/lazygit/pkg/gui/types"
@@ -100,7 +101,7 @@ func (self *SuggestionsHelper) GetBranchNameSuggestionsFunc() func(string) []*ty
// Notably, unlike other suggestion functions we're not showing all the options
// if nothing has been typed because there'll be too much to display efficiently
func (self *SuggestionsHelper) GetFilePathSuggestionsFunc() func(string) []*types.Suggestion {
_ = self.c.WithWaitingStatus(self.c.Tr.LoadingFileSuggestions, func() error {
_ = self.c.WithWaitingStatus(self.c.Tr.LoadingFileSuggestions, func(*gocui.Task) error {
trie := patricia.NewTrie()
// load every non-gitignored file in the repo
ignore, err := gitignore.FromGit()

View File

@@ -1,6 +1,7 @@
package helpers
import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/updates"
"github.com/jesseduffield/lazygit/pkg/utils"
@@ -37,7 +38,7 @@ func (self *UpdateHelper) CheckForUpdateInBackground() {
}
func (self *UpdateHelper) CheckForUpdateInForeground() error {
return self.c.WithWaitingStatus(self.c.Tr.CheckingForUpdates, func() error {
return self.c.WithWaitingStatus(self.c.Tr.CheckingForUpdates, func(*gocui.Task) error {
self.updater.CheckForNewUpdate(func(newVersion string, err error) error {
if err != nil {
return self.c.Error(err)
@@ -53,7 +54,7 @@ func (self *UpdateHelper) CheckForUpdateInForeground() error {
}
func (self *UpdateHelper) startUpdating(newVersion string) {
_ = self.c.WithWaitingStatus(self.c.Tr.UpdateInProgressWaitingStatus, func() error {
_ = self.c.WithWaitingStatus(self.c.Tr.UpdateInProgressWaitingStatus, func(*gocui.Task) error {
self.c.State().SetUpdating(true)
err := self.updater.Update(newVersion)
return self.onUpdateFinish(err)

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"github.com/fsmiamoto/git-todo-parser/todo"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
"github.com/jesseduffield/lazygit/pkg/gui/context"
@@ -217,7 +218,7 @@ func (self *LocalCommitsController) squashDown(commit *models.Commit) error {
Title: self.c.Tr.Squash,
Prompt: self.c.Tr.SureSquashThisCommit,
HandleConfirm: func() error {
return self.c.WithWaitingStatus(self.c.Tr.SquashingStatus, func() error {
return self.c.WithWaitingStatus(self.c.Tr.SquashingStatus, func(*gocui.Task) error {
self.c.LogAction(self.c.Tr.Actions.SquashCommitDown)
return self.interactiveRebase(todo.Squash)
})
@@ -242,7 +243,7 @@ func (self *LocalCommitsController) fixup(commit *models.Commit) error {
Title: self.c.Tr.Fixup,
Prompt: self.c.Tr.SureFixupThisCommit,
HandleConfirm: func() error {
return self.c.WithWaitingStatus(self.c.Tr.FixingStatus, func() error {
return self.c.WithWaitingStatus(self.c.Tr.FixingStatus, func(*gocui.Task) error {
self.c.LogAction(self.c.Tr.Actions.FixupCommit)
return self.interactiveRebase(todo.Fixup)
})
@@ -338,7 +339,7 @@ func (self *LocalCommitsController) drop(commit *models.Commit) error {
Title: self.c.Tr.DeleteCommitTitle,
Prompt: self.c.Tr.DeleteCommitPrompt,
HandleConfirm: func() error {
return self.c.WithWaitingStatus(self.c.Tr.DeletingStatus, func() error {
return self.c.WithWaitingStatus(self.c.Tr.DeletingStatus, func(*gocui.Task) error {
self.c.LogAction(self.c.Tr.Actions.DropCommit)
return self.interactiveRebase(todo.Drop)
})
@@ -355,7 +356,7 @@ func (self *LocalCommitsController) edit(commit *models.Commit) error {
return nil
}
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func() error {
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func(*gocui.Task) error {
self.c.LogAction(self.c.Tr.Actions.EditCommit)
err := self.c.Git().Rebase.EditRebase(commit.Sha)
return self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(err)
@@ -460,7 +461,7 @@ func (self *LocalCommitsController) moveDown(commit *models.Commit) error {
return self.c.ErrorMsg(self.c.Tr.AlreadyRebasing)
}
return self.c.WithWaitingStatus(self.c.Tr.MovingStatus, func() error {
return self.c.WithWaitingStatus(self.c.Tr.MovingStatus, func(*gocui.Task) error {
self.c.LogAction(self.c.Tr.Actions.MoveCommitDown)
err := self.c.Git().Rebase.MoveCommitDown(self.c.Model().Commits, index)
if err == nil {
@@ -498,7 +499,7 @@ func (self *LocalCommitsController) moveUp(commit *models.Commit) error {
return self.c.ErrorMsg(self.c.Tr.AlreadyRebasing)
}
return self.c.WithWaitingStatus(self.c.Tr.MovingStatus, func() error {
return self.c.WithWaitingStatus(self.c.Tr.MovingStatus, func(*gocui.Task) error {
self.c.LogAction(self.c.Tr.Actions.MoveCommitUp)
err := self.c.Git().Rebase.MoveCommitUp(self.c.Model().Commits, index)
if err == nil {
@@ -524,7 +525,7 @@ func (self *LocalCommitsController) amendTo(commit *models.Commit) error {
Title: self.c.Tr.AmendCommitTitle,
Prompt: self.c.Tr.AmendCommitPrompt,
HandleConfirm: func() error {
return self.c.WithWaitingStatus(self.c.Tr.AmendingStatus, func() error {
return self.c.WithWaitingStatus(self.c.Tr.AmendingStatus, func(*gocui.Task) error {
self.c.LogAction(self.c.Tr.Actions.AmendCommit)
err := self.c.Git().Rebase.AmendTo(self.c.Model().Commits, self.context().GetView().SelectedLineIdx())
return self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(err)
@@ -558,7 +559,7 @@ func (self *LocalCommitsController) amendAttribute(commit *models.Commit) error
}
func (self *LocalCommitsController) resetAuthor() error {
return self.c.WithWaitingStatus(self.c.Tr.AmendingStatus, func() error {
return self.c.WithWaitingStatus(self.c.Tr.AmendingStatus, func(*gocui.Task) error {
self.c.LogAction(self.c.Tr.Actions.ResetCommitAuthor)
if err := self.c.Git().Rebase.ResetCommitAuthor(self.c.Model().Commits, self.context().GetSelectedLineIdx()); err != nil {
return self.c.Error(err)
@@ -573,7 +574,7 @@ func (self *LocalCommitsController) setAuthor() error {
Title: self.c.Tr.SetAuthorPromptTitle,
FindSuggestionsFunc: self.c.Helpers().Suggestions.GetAuthorsSuggestionsFunc(),
HandleConfirm: func(value string) error {
return self.c.WithWaitingStatus(self.c.Tr.AmendingStatus, func() error {
return self.c.WithWaitingStatus(self.c.Tr.AmendingStatus, func(*gocui.Task) error {
self.c.LogAction(self.c.Tr.Actions.SetCommitAuthor)
if err := self.c.Git().Rebase.SetCommitAuthor(self.c.Model().Commits, self.context().GetSelectedLineIdx(), value); err != nil {
return self.c.Error(err)
@@ -671,7 +672,7 @@ func (self *LocalCommitsController) squashAllAboveFixupCommits(commit *models.Co
Title: self.c.Tr.SquashAboveCommits,
Prompt: prompt,
HandleConfirm: func() error {
return self.c.WithWaitingStatus(self.c.Tr.SquashingStatus, func() error {
return self.c.WithWaitingStatus(self.c.Tr.SquashingStatus, func(*gocui.Task) error {
self.c.LogAction(self.c.Tr.Actions.SquashAllAboveFixupCommits)
err := self.c.Git().Rebase.SquashAllAboveFixupCommits(commit)
return self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(err)
@@ -723,7 +724,7 @@ func (self *LocalCommitsController) handleOpenLogMenu() error {
self.context().SetLimitCommits(false)
}
return self.c.WithWaitingStatus(self.c.Tr.LoadingCommits, func() error {
return self.c.WithWaitingStatus(self.c.Tr.LoadingCommits, func(*gocui.Task) error {
return self.c.Refresh(
types.RefreshOptions{Mode: types.SYNC, Scope: []types.RefreshableView{types.COMMITS}},
)
@@ -766,7 +767,7 @@ func (self *LocalCommitsController) handleOpenLogMenu() error {
onPress := func(value string) func() error {
return func() error {
self.c.UserConfig.Git.Log.Order = value
return self.c.WithWaitingStatus(self.c.Tr.LoadingCommits, func() error {
return self.c.WithWaitingStatus(self.c.Tr.LoadingCommits, func(*gocui.Task) error {
return self.c.Refresh(
types.RefreshOptions{
Mode: types.SYNC,
@@ -816,7 +817,7 @@ func (self *LocalCommitsController) GetOnFocus() func(types.OnFocusOpts) error {
context := self.context()
if context.GetSelectedLineIdx() > COMMIT_THRESHOLD && context.GetLimitCommits() {
context.SetLimitCommits(false)
self.c.OnWorker(func() {
self.c.OnWorker(func(_ *gocui.Task) {
if err := self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.COMMITS}}); err != nil {
_ = self.c.Error(err)
}

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"strings"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/context"
"github.com/jesseduffield/lazygit/pkg/gui/types"
@@ -117,9 +118,9 @@ func (self *RemoteBranchesController) delete(selectedBranch *models.RemoteBranch
Title: self.c.Tr.DeleteRemoteBranch,
Prompt: message,
HandleConfirm: func() error {
return self.c.WithWaitingStatus(self.c.Tr.DeletingStatus, func() error {
return self.c.WithWaitingStatus(self.c.Tr.DeletingStatus, func(task *gocui.Task) error {
self.c.LogAction(self.c.Tr.Actions.DeleteRemoteBranch)
err := self.c.Git().Remote.DeleteRemoteBranch(selectedBranch.RemoteName, selectedBranch.Name)
err := self.c.Git().Remote.DeleteRemoteBranch(task, selectedBranch.RemoteName, selectedBranch.Name)
if err != nil {
_ = self.c.Error(err)
}

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"strings"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/context"
"github.com/jesseduffield/lazygit/pkg/gui/style"
@@ -197,8 +198,8 @@ func (self *RemotesController) edit(remote *models.Remote) error {
}
func (self *RemotesController) fetch(remote *models.Remote) error {
return self.c.WithWaitingStatus(self.c.Tr.FetchingRemoteStatus, func() error {
err := self.c.Git().Sync.FetchRemote(remote.Name)
return self.c.WithWaitingStatus(self.c.Tr.FetchingRemoteStatus, func(task *gocui.Task) error {
err := self.c.Git().Sync.FetchRemote(task, remote.Name)
if err != nil {
_ = self.c.Error(err)
}

View File

@@ -1,6 +1,7 @@
package controllers
import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/gui/context"
"github.com/jesseduffield/lazygit/pkg/gui/types"
)
@@ -59,7 +60,7 @@ func (self *SubCommitsController) GetOnFocus() func(types.OnFocusOpts) error {
context := self.context()
if context.GetSelectedLineIdx() > COMMIT_THRESHOLD && context.GetLimitCommits() {
context.SetLimitCommits(false)
self.c.OnWorker(func() {
self.c.OnWorker(func(_ *gocui.Task) {
if err := self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.SUB_COMMITS}}); err != nil {
_ = self.c.Error(err)
}

View File

@@ -5,6 +5,7 @@ import (
"path/filepath"
"strings"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/context"
"github.com/jesseduffield/lazygit/pkg/gui/style"
@@ -130,7 +131,7 @@ func (self *SubmodulesController) add() error {
Title: self.c.Tr.NewSubmodulePath,
InitialContent: submoduleName,
HandleConfirm: func(submodulePath string) error {
return self.c.WithWaitingStatus(self.c.Tr.AddingSubmoduleStatus, func() error {
return self.c.WithWaitingStatus(self.c.Tr.AddingSubmoduleStatus, func(*gocui.Task) error {
self.c.LogAction(self.c.Tr.Actions.AddSubmodule)
err := self.c.Git().Submodule.Add(submoduleName, submodulePath, submoduleUrl)
if err != nil {
@@ -152,7 +153,7 @@ func (self *SubmodulesController) editURL(submodule *models.SubmoduleConfig) err
Title: fmt.Sprintf(self.c.Tr.UpdateSubmoduleUrl, submodule.Name),
InitialContent: submodule.Url,
HandleConfirm: func(newUrl string) error {
return self.c.WithWaitingStatus(self.c.Tr.UpdatingSubmoduleUrlStatus, func() error {
return self.c.WithWaitingStatus(self.c.Tr.UpdatingSubmoduleUrlStatus, func(*gocui.Task) error {
self.c.LogAction(self.c.Tr.Actions.UpdateSubmoduleUrl)
err := self.c.Git().Submodule.UpdateUrl(submodule.Name, submodule.Path, newUrl)
if err != nil {
@@ -166,7 +167,7 @@ func (self *SubmodulesController) editURL(submodule *models.SubmoduleConfig) err
}
func (self *SubmodulesController) init(submodule *models.SubmoduleConfig) error {
return self.c.WithWaitingStatus(self.c.Tr.InitializingSubmoduleStatus, func() error {
return self.c.WithWaitingStatus(self.c.Tr.InitializingSubmoduleStatus, func(*gocui.Task) error {
self.c.LogAction(self.c.Tr.Actions.InitialiseSubmodule)
err := self.c.Git().Submodule.Init(submodule.Path)
if err != nil {
@@ -184,7 +185,7 @@ func (self *SubmodulesController) openBulkActionsMenu() error {
{
LabelColumns: []string{self.c.Tr.BulkInitSubmodules, style.FgGreen.Sprint(self.c.Git().Submodule.BulkInitCmdObj().ToString())},
OnPress: func() error {
return self.c.WithWaitingStatus(self.c.Tr.RunningCommand, func() error {
return self.c.WithWaitingStatus(self.c.Tr.RunningCommand, func(*gocui.Task) error {
self.c.LogAction(self.c.Tr.Actions.BulkInitialiseSubmodules)
err := self.c.Git().Submodule.BulkInitCmdObj().Run()
if err != nil {
@@ -199,7 +200,7 @@ func (self *SubmodulesController) openBulkActionsMenu() error {
{
LabelColumns: []string{self.c.Tr.BulkUpdateSubmodules, style.FgYellow.Sprint(self.c.Git().Submodule.BulkUpdateCmdObj().ToString())},
OnPress: func() error {
return self.c.WithWaitingStatus(self.c.Tr.RunningCommand, func() error {
return self.c.WithWaitingStatus(self.c.Tr.RunningCommand, func(*gocui.Task) error {
self.c.LogAction(self.c.Tr.Actions.BulkUpdateSubmodules)
if err := self.c.Git().Submodule.BulkUpdateCmdObj().Run(); err != nil {
return self.c.Error(err)
@@ -213,7 +214,7 @@ func (self *SubmodulesController) openBulkActionsMenu() error {
{
LabelColumns: []string{self.c.Tr.BulkDeinitSubmodules, style.FgRed.Sprint(self.c.Git().Submodule.BulkDeinitCmdObj().ToString())},
OnPress: func() error {
return self.c.WithWaitingStatus(self.c.Tr.RunningCommand, func() error {
return self.c.WithWaitingStatus(self.c.Tr.RunningCommand, func(*gocui.Task) error {
self.c.LogAction(self.c.Tr.Actions.BulkDeinitialiseSubmodules)
if err := self.c.Git().Submodule.BulkDeinitCmdObj().Run(); err != nil {
return self.c.Error(err)
@@ -229,7 +230,7 @@ func (self *SubmodulesController) openBulkActionsMenu() error {
}
func (self *SubmodulesController) update(submodule *models.SubmoduleConfig) error {
return self.c.WithWaitingStatus(self.c.Tr.UpdatingSubmoduleStatus, func() error {
return self.c.WithWaitingStatus(self.c.Tr.UpdatingSubmoduleStatus, func(*gocui.Task) error {
self.c.LogAction(self.c.Tr.Actions.UpdateSubmodule)
err := self.c.Git().Submodule.Update(submodule.Path)
if err != nil {

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"strings"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/types"
@@ -138,15 +139,16 @@ type PullFilesOptions struct {
}
func (self *SyncController) PullAux(opts PullFilesOptions) error {
return self.c.WithLoaderPanel(self.c.Tr.PullWait, func() error {
return self.pullWithLock(opts)
return self.c.WithLoaderPanel(self.c.Tr.PullWait, func(task *gocui.Task) error {
return self.pullWithLock(task, opts)
})
}
func (self *SyncController) pullWithLock(opts PullFilesOptions) error {
func (self *SyncController) pullWithLock(task *gocui.Task, opts PullFilesOptions) error {
self.c.LogAction(opts.Action)
err := self.c.Git().Sync.Pull(
task,
git_commands.PullOptions{
RemoteName: opts.UpstreamRemote,
BranchName: opts.UpstreamBranch,
@@ -165,14 +167,16 @@ type pushOpts struct {
}
func (self *SyncController) pushAux(opts pushOpts) error {
return self.c.WithLoaderPanel(self.c.Tr.PushWait, func() error {
return self.c.WithLoaderPanel(self.c.Tr.PushWait, func(task *gocui.Task) error {
self.c.LogAction(self.c.Tr.Actions.Push)
err := self.c.Git().Sync.Push(git_commands.PushOpts{
Force: opts.force,
UpstreamRemote: opts.upstreamRemote,
UpstreamBranch: opts.upstreamBranch,
SetUpstream: opts.setUpstream,
})
err := self.c.Git().Sync.Push(
task,
git_commands.PushOpts{
Force: opts.force,
UpstreamRemote: opts.upstreamRemote,
UpstreamBranch: opts.upstreamBranch,
SetUpstream: opts.setUpstream,
})
if err != nil {
if !opts.force && strings.Contains(err.Error(), "Updates were rejected") {
forcePushDisabled := self.c.UserConfig.Git.DisableForcePushing

View File

@@ -1,6 +1,7 @@
package controllers
import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/context"
"github.com/jesseduffield/lazygit/pkg/gui/types"
@@ -121,9 +122,9 @@ func (self *TagsController) push(tag *models.Tag) error {
InitialContent: "origin",
FindSuggestionsFunc: self.c.Helpers().Suggestions.GetRemoteSuggestionsFunc(),
HandleConfirm: func(response string) error {
return self.c.WithWaitingStatus(self.c.Tr.PushingTagStatus, func() error {
return self.c.WithWaitingStatus(self.c.Tr.PushingTagStatus, func(task *gocui.Task) error {
self.c.LogAction(self.c.Tr.Actions.PushTag)
err := self.c.Git().Tag.Push(response, tag.Name)
err := self.c.Git().Tag.Push(task, response, tag.Name)
if err != nil {
_ = self.c.Error(err)
}

View File

@@ -3,6 +3,7 @@ package controllers
import (
"fmt"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
"github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/utils"
@@ -247,7 +248,7 @@ func (self *UndoController) hardResetWithAutoStash(commitSha string, options har
Title: self.c.Tr.AutoStashTitle,
Prompt: self.c.Tr.AutoStashPrompt,
HandleConfirm: func() error {
return self.c.WithWaitingStatus(options.WaitingStatus, func() error {
return self.c.WithWaitingStatus(options.WaitingStatus, func(*gocui.Task) error {
if err := self.c.Git().Stash.Save(self.c.Tr.StashPrefix + commitSha); err != nil {
return self.c.Error(err)
}
@@ -268,7 +269,7 @@ func (self *UndoController) hardResetWithAutoStash(commitSha string, options har
})
}
return self.c.WithWaitingStatus(options.WaitingStatus, func() error {
return self.c.WithWaitingStatus(options.WaitingStatus, func(*gocui.Task) error {
return reset()
})
}

View File

@@ -472,10 +472,10 @@ func NewGui(
func() error { return gui.State.ContextMgr.Pop() },
func() types.Context { return gui.State.ContextMgr.Current() },
gui.createMenu,
func(message string, f func() error) { gui.helpers.AppStatus.WithWaitingStatus(message, f) },
func(message string, f func(*gocui.Task) error) { gui.helpers.AppStatus.WithWaitingStatus(message, f) },
func(message string) { gui.helpers.AppStatus.Toast(message) },
func() string { return gui.Views.Confirmation.TextArea.GetContent() },
func(f func()) { gui.c.OnWorker(f) },
func(f func(*gocui.Task)) { gui.c.OnWorker(f) },
)
guiCommon := &guiCommon{gui: gui, IPopupHandler: gui.PopupHandler}
@@ -488,8 +488,6 @@ func NewGui(
gui.LogCommand,
gui.getCmdWriter,
credentialsHelper.PromptUserForCredential,
func() { gui.g.IncrementBusyCount() },
func() { gui.g.DecrementBusyCount() },
)
osCommand := oscommands.NewOSCommand(cmn, config, oscommands.GetPlatform(), guiIO)
@@ -786,15 +784,15 @@ func (gui *Gui) showInitialPopups(tasks []func(chan struct{}) error) {
gui.waitForIntro.Add(len(tasks))
done := make(chan struct{})
gui.c.OnWorker(func() {
gui.c.OnWorker(func(gocuiTask *gocui.Task) {
for _, task := range tasks {
if err := task(done); err != nil {
_ = gui.c.Error(err)
}
gui.g.DecrementBusyCount()
gocuiTask.Pause()
<-done
gui.g.IncrementBusyCount()
gocuiTask.Continue()
gui.waitForIntro.Done()
}
})
@@ -835,7 +833,7 @@ func (gui *Gui) onUIThread(f func() error) {
})
}
func (gui *Gui) onWorker(f func()) {
func (gui *Gui) onWorker(f func(*gocui.Task)) {
gui.g.OnWorker(f)
}

View File

@@ -136,7 +136,7 @@ func (self *guiCommon) OnUIThread(f func() error) {
self.gui.onUIThread(f)
}
func (self *guiCommon) OnWorker(f func()) {
func (self *guiCommon) OnWorker(f func(*gocui.Task)) {
self.gui.onWorker(f)
}

View File

@@ -1,6 +1,9 @@
package popup
import "github.com/jesseduffield/lazygit/pkg/gui/types"
import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/gui/types"
)
type FakePopupHandler struct {
OnErrorMsg func(message string) error
@@ -30,12 +33,12 @@ func (self *FakePopupHandler) Prompt(opts types.PromptOpts) error {
return self.OnPrompt(opts)
}
func (self *FakePopupHandler) WithLoaderPanel(message string, f func() error) error {
return f()
func (self *FakePopupHandler) WithLoaderPanel(message string, f func(*gocui.Task) error) error {
return f(&gocui.Task{})
}
func (self *FakePopupHandler) WithWaitingStatus(message string, f func() error) error {
return f()
func (self *FakePopupHandler) WithWaitingStatus(message string, f func(*gocui.Task) error) error {
return f(&gocui.Task{})
}
func (self *FakePopupHandler) Menu(opts types.CreateMenuOptions) error {

View File

@@ -21,10 +21,10 @@ type PopupHandler struct {
popContextFn func() error
currentContextFn func() types.Context
createMenuFn func(types.CreateMenuOptions) error
withWaitingStatusFn func(message string, f func() error)
withWaitingStatusFn func(message string, f func(*gocui.Task) error)
toastFn func(message string)
getPromptInputFn func() string
onWorker func(func())
onWorker func(func(*gocui.Task))
}
var _ types.IPopupHandler = &PopupHandler{}
@@ -36,10 +36,10 @@ func NewPopupHandler(
popContextFn func() error,
currentContextFn func() types.Context,
createMenuFn func(types.CreateMenuOptions) error,
withWaitingStatusFn func(message string, f func() error),
withWaitingStatusFn func(message string, f func(*gocui.Task) error),
toastFn func(message string),
getPromptInputFn func() string,
onWorker func(func()),
onWorker func(func(*gocui.Task)),
) *PopupHandler {
return &PopupHandler{
Common: common,
@@ -64,7 +64,7 @@ func (self *PopupHandler) Toast(message string) {
self.toastFn(message)
}
func (self *PopupHandler) WithWaitingStatus(message string, f func() error) error {
func (self *PopupHandler) WithWaitingStatus(message string, f func(*gocui.Task) error) error {
self.withWaitingStatusFn(message, f)
return nil
}
@@ -124,7 +124,7 @@ func (self *PopupHandler) Prompt(opts types.PromptOpts) error {
})
}
func (self *PopupHandler) WithLoaderPanel(message string, f func() error) error {
func (self *PopupHandler) WithLoaderPanel(message string, f func(*gocui.Task) error) error {
index := 0
self.Lock()
self.index++
@@ -143,8 +143,8 @@ func (self *PopupHandler) WithLoaderPanel(message string, f func() error) error
return nil
}
self.onWorker(func() {
if err := f(); err != nil {
self.onWorker(func(task *gocui.Task) {
if err := f(task); err != nil {
self.Log.Error(err)
}

View File

@@ -6,6 +6,7 @@ import (
"text/template"
"github.com/jesseduffield/generics/slices"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/gui/controllers/helpers"
"github.com/jesseduffield/lazygit/pkg/gui/style"
@@ -264,7 +265,7 @@ func (self *HandlerCreator) finalHandler(customCommand config.CustomCommand, ses
loadingText = self.c.Tr.RunningCustomCommandStatus
}
return self.c.WithWaitingStatus(loadingText, func() error {
return self.c.WithWaitingStatus(loadingText, func(*gocui.Task) error {
self.c.LogAction(self.c.Tr.Actions.CustomCommand)
if customCommand.Stream {

View File

@@ -130,8 +130,9 @@ func (gui *Gui) getManager(view *gocui.View) *tasks.ViewBufferManager {
func() {
_ = view.SetOrigin(0, 0)
},
gui.c.GocuiGui().IncrementBusyCount,
gui.c.GocuiGui().DecrementBusyCount,
func() *gocui.Task {
return gui.c.GocuiGui().NewTask()
},
)
gui.viewBufferManagerMap[view.Name()] = manager
}

View File

@@ -79,7 +79,7 @@ type IGuiCommon interface {
OnUIThread(f func() error)
// Runs a function in a goroutine. Use this whenever you want to run a goroutine and keep track of the fact
// that lazygit is still busy. See docs/dev/Busy.md
OnWorker(f func())
OnWorker(f func(*gocui.Task))
// returns the gocui Gui struct. There is a good chance you don't actually want to use
// this struct and instead want to use another method above
@@ -121,8 +121,8 @@ type IPopupHandler interface {
Confirm(opts ConfirmOpts) error
// Shows a popup prompting the user for input.
Prompt(opts PromptOpts) error
WithLoaderPanel(message string, f func() error) error
WithWaitingStatus(message string, f func() error) error
WithLoaderPanel(message string, f func(*gocui.Task) error) error
WithWaitingStatus(message string, f func(*gocui.Task) error) error
Menu(opts CreateMenuOptions) error
Toast(message string)
GetPromptInput() string

View File

@@ -1,6 +1,7 @@
package tasks
import (
"github.com/jesseduffield/gocui"
"github.com/sasha-s/go-deadlock"
)
@@ -17,10 +18,10 @@ type AsyncHandler struct {
lastId int
mutex deadlock.Mutex
onReject func()
onWorker func(func())
onWorker func(func(*gocui.Task))
}
func NewAsyncHandler(onWorker func(func())) *AsyncHandler {
func NewAsyncHandler(onWorker func(func(*gocui.Task))) *AsyncHandler {
return &AsyncHandler{
mutex: deadlock.Mutex{},
onWorker: onWorker,
@@ -33,7 +34,7 @@ func (self *AsyncHandler) Do(f func() func()) {
id := self.currentId
self.mutex.Unlock()
self.onWorker(func() {
self.onWorker(func(*gocui.Task) {
after := f()
self.handle(after, id)
})

View File

@@ -5,6 +5,7 @@ import (
"sync"
"testing"
"github.com/jesseduffield/gocui"
"github.com/stretchr/testify/assert"
)
@@ -12,8 +13,8 @@ func TestAsyncHandler(t *testing.T) {
wg := sync.WaitGroup{}
wg.Add(2)
onWorker := func(f func()) {
go f()
onWorker := func(f func(*gocui.Task)) {
go f(&gocui.Task{})
}
handler := NewAsyncHandler(onWorker)
handler.onReject = func() {

View File

@@ -9,6 +9,7 @@ import (
"sync"
"time"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/sasha-s/go-deadlock"
@@ -49,8 +50,7 @@ type ViewBufferManager struct {
onEndOfInput func()
// see docs/dev/Busy.md
incrementBusyCount func()
decrementBusyCount func()
newTask func() *gocui.Task
// if the user flicks through a heap of items, with each one
// spawning a process to render something to the main view,
@@ -80,19 +80,17 @@ func NewViewBufferManager(
refreshView func(),
onEndOfInput func(),
onNewKey func(),
incrementBusyCount func(),
decrementBusyCount func(),
newTask func() *gocui.Task,
) *ViewBufferManager {
return &ViewBufferManager{
Log: log,
writer: writer,
beforeStart: beforeStart,
refreshView: refreshView,
onEndOfInput: onEndOfInput,
readLines: make(chan LinesToRead, 1024),
onNewKey: onNewKey,
incrementBusyCount: incrementBusyCount,
decrementBusyCount: decrementBusyCount,
Log: log,
writer: writer,
beforeStart: beforeStart,
refreshView: refreshView,
onEndOfInput: onEndOfInput,
readLines: make(chan LinesToRead, 1024),
onNewKey: onNewKey,
newTask: newTask,
}
}
@@ -298,18 +296,18 @@ type TaskOpts struct {
}
func (self *ViewBufferManager) NewTask(f func(TaskOpts) error, key string) error {
self.incrementBusyCount()
task := self.newTask()
var decrementCounterOnce sync.Once
var completeTaskOnce sync.Once
decrementCounter := func() {
decrementCounterOnce.Do(func() {
self.decrementBusyCount()
completeTask := func() {
completeTaskOnce.Do(func() {
task.Done()
})
}
go utils.Safe(func() {
defer decrementCounter()
defer completeTask()
self.taskIDMutex.Lock()
self.newTaskID++
@@ -349,7 +347,7 @@ func (self *ViewBufferManager) NewTask(f func(TaskOpts) error, key string) error
self.waitingMutex.Unlock()
if err := f(TaskOpts{Stop: stop, InitialContentLoaded: decrementCounter}); err != nil {
if err := f(TaskOpts{Stop: stop, InitialContentLoaded: completeTask}); err != nil {
self.Log.Error(err) // might need an onError callback
}

View File

@@ -10,6 +10,7 @@ import (
"testing"
"time"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/secureexec"
"github.com/jesseduffield/lazygit/pkg/utils"
)
@@ -31,7 +32,10 @@ func TestNewCmdTaskInstantStop(t *testing.T) {
onEndOfInput, getOnEndOfInputCallCount := getCounter()
onNewKey, getOnNewKeyCallCount := getCounter()
onDone, getOnDoneCallCount := getCounter()
incBusyCount, decBusyCount, getBusyCount := getIncDecCounter(1)
task := &gocui.Task{}
newTask := func() *gocui.Task {
return task
}
manager := NewViewBufferManager(
utils.NewDummyLog(),
@@ -40,8 +44,7 @@ func TestNewCmdTaskInstantStop(t *testing.T) {
refreshView,
onEndOfInput,
onNewKey,
incBusyCount,
decBusyCount,
newTask,
)
stop := make(chan struct{})
@@ -57,7 +60,7 @@ func TestNewCmdTaskInstantStop(t *testing.T) {
fn := manager.NewCmdTask(start, "prefix\n", LinesToRead{20, -1}, onDone)
_ = fn(TaskOpts{Stop: stop, InitialContentLoaded: decBusyCount})
_ = fn(TaskOpts{Stop: stop, InitialContentLoaded: func() { task.Done() }})
callCountExpectations := []struct {
expected int