1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-03-21 21:47:32 +02:00

Add WithInlineStatus helper function

Very similar to WithWaitingStatus, except that the status is shown in a view
next to the affected item, rather than in the status bar.

Not used by anything yet; again, committing separately to get smaller commits.
This commit is contained in:
Stefan Haller 2023-09-22 16:09:02 +02:00
parent 9d55d71fdd
commit 7075b66bc6
5 changed files with 144 additions and 0 deletions

View File

@ -112,6 +112,7 @@ func (gui *Gui) resetHelpersAndControllers() {
Confirmation: helpers.NewConfirmationHelper(helperCommon),
Mode: modeHelper,
AppStatus: appStatusHelper,
InlineStatus: helpers.NewInlineStatusHelper(helperCommon),
WindowArrangement: helpers.NewWindowArrangementHelper(
gui.c,
windowHelper,

View File

@ -46,6 +46,7 @@ type Helpers struct {
Confirmation *ConfirmationHelper
Mode *ModeHelper
AppStatus *AppStatusHelper
InlineStatus *InlineStatusHelper
WindowArrangement *WindowArrangementHelper
Search *SearchHelper
Worktree *WorktreeHelper
@ -81,6 +82,7 @@ func NewStubHelpers() *Helpers {
Confirmation: &ConfirmationHelper{},
Mode: &ModeHelper{},
AppStatus: &AppStatusHelper{},
InlineStatus: &InlineStatusHelper{},
WindowArrangement: &WindowArrangementHelper{},
Search: &SearchHelper{},
Worktree: &WorktreeHelper{},

View File

@ -0,0 +1,129 @@
package helpers
import (
"time"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/sasha-s/go-deadlock"
)
type InlineStatusHelper struct {
c *HelperCommon
contextsWithInlineStatus map[types.ContextKey]*inlineStatusInfo
mutex *deadlock.Mutex
}
func NewInlineStatusHelper(c *HelperCommon) *InlineStatusHelper {
return &InlineStatusHelper{
c: c,
contextsWithInlineStatus: make(map[types.ContextKey]*inlineStatusInfo),
mutex: &deadlock.Mutex{},
}
}
type InlineStatusOpts struct {
Item types.HasUrn
Operation types.ItemOperation
ContextKey types.ContextKey
}
type inlineStatusInfo struct {
refCount int
stop chan struct{}
}
// A custom task for WithInlineStatus calls; it wraps the original one and
// hides the status whenever the task is paused, and shows it again when
// continued.
type inlineStatusHelperTask struct {
gocui.Task
inlineStatusHelper *InlineStatusHelper
opts InlineStatusOpts
}
// poor man's version of explicitly saying that struct X implements interface Y
var _ gocui.Task = inlineStatusHelperTask{}
func (self inlineStatusHelperTask) Pause() {
self.inlineStatusHelper.stop(self.opts)
self.Task.Pause()
self.inlineStatusHelper.renderContext(self.opts.ContextKey)
}
func (self inlineStatusHelperTask) Continue() {
self.Task.Continue()
self.inlineStatusHelper.start(self.opts)
}
func (self *InlineStatusHelper) WithInlineStatus(opts InlineStatusOpts, f func(gocui.Task) error) {
self.c.OnWorker(func(task gocui.Task) {
self.start(opts)
err := f(inlineStatusHelperTask{task, self, opts})
if err != nil {
self.c.OnUIThread(func() error {
return self.c.Error(err)
})
}
self.stop(opts)
})
}
func (self *InlineStatusHelper) start(opts InlineStatusOpts) {
self.c.State().SetItemOperation(opts.Item, opts.Operation)
self.mutex.Lock()
defer self.mutex.Unlock()
info := self.contextsWithInlineStatus[opts.ContextKey]
if info == nil {
info = &inlineStatusInfo{refCount: 0, stop: make(chan struct{})}
self.contextsWithInlineStatus[opts.ContextKey] = info
go utils.Safe(func() {
ticker := time.NewTicker(time.Millisecond * utils.LoaderAnimationInterval)
defer ticker.Stop()
outer:
for {
select {
case <-ticker.C:
self.renderContext(opts.ContextKey)
case <-info.stop:
break outer
}
}
})
}
info.refCount++
}
func (self *InlineStatusHelper) stop(opts InlineStatusOpts) {
self.mutex.Lock()
if info := self.contextsWithInlineStatus[opts.ContextKey]; info != nil {
info.refCount--
if info.refCount <= 0 {
info.stop <- struct{}{}
delete(self.contextsWithInlineStatus, opts.ContextKey)
}
}
self.mutex.Unlock()
self.c.State().ClearItemOperation(opts.Item)
}
func (self *InlineStatusHelper) renderContext(contextKey types.ContextKey) {
self.c.OnUIThread(func() error {
_ = self.c.ContextForKey(contextKey).HandleRender()
return nil
})
}

View File

@ -5,6 +5,7 @@ import (
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/gui/controllers/helpers"
"github.com/jesseduffield/lazygit/pkg/gui/types"
)
@ -199,3 +200,8 @@ func (self *guiCommon) RunningIntegrationTest() bool {
func (self *guiCommon) InDemo() bool {
return self.gui.integrationTest != nil && self.gui.integrationTest.IsDemo()
}
func (self *guiCommon) WithInlineStatus(item types.HasUrn, operation types.ItemOperation, contextKey types.ContextKey, f func(gocui.Task) error) error {
self.gui.helpers.InlineStatus.WithInlineStatus(helpers.InlineStatusOpts{Item: item, Operation: operation, ContextKey: contextKey}, f)
return nil
}

View File

@ -87,6 +87,12 @@ type IGuiCommon interface {
// resized, if in accordion mode.
AfterLayout(f func() error)
// Wraps a function, attaching the given operation to the given item while
// the function is executing, and also causes the given context to be
// redrawn periodically. This allows the operation to be visualized with a
// spinning loader animation (e.g. when a branch is being pushed).
WithInlineStatus(item HasUrn, operation ItemOperation, contextKey ContextKey, f func(gocui.Task) error) error
// 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
GocuiGui() *gocui.Gui