Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 17 additions & 6 deletions pkg/gui/background.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package gui
import (
"fmt"
"runtime"
"sync/atomic"
"time"

"github.com/jesseduffield/lazygit/pkg/gocui"
Expand All @@ -13,17 +14,27 @@ import (
type BackgroundRoutineMgr struct {
gui *Gui

// if we've suspended the gui (e.g. because we've switched to a subprocess)
// we typically want to pause some things that are running like background
// file refreshes
pauseBackgroundRefreshes bool
// When this is greater than zero, the background routines (e.g. file refresh)
// skip their work. We pause them while the gui is suspended (e.g. for a
// subprocess) and while lazygit is itself driving a git operation that would
// otherwise be caught mid-flight (see the waiting-status helpers). It's a
// count rather than a bool because these pause scopes can overlap.
pauseRefreshesCount atomic.Int32

// a channel to trigger an immediate background fetch; we use this when switching repos
triggerFetch chan struct{}
}

func (self *BackgroundRoutineMgr) PauseBackgroundRefreshes(pause bool) {
self.pauseBackgroundRefreshes = pause
if pause {
self.pauseRefreshesCount.Add(1)
} else {
self.pauseRefreshesCount.Add(-1)
}
}

func (self *BackgroundRoutineMgr) backgroundRefreshesPaused() bool {
return self.pauseRefreshesCount.Load() > 0
}

func (self *BackgroundRoutineMgr) startBackgroundRoutines() {
Expand Down Expand Up @@ -124,7 +135,7 @@ func (self *BackgroundRoutineMgr) goEvery(interval time.Duration, stop chan stru
ticker := time.NewTicker(interval)
defer ticker.Stop()
doit := func(retriggered bool) {
if self.pauseBackgroundRefreshes {
if self.backgroundRefreshesPaused() {
return
}
self.gui.c.OnWorker(func(gocui.Task) error {
Expand Down
10 changes: 10 additions & 0 deletions pkg/gui/controllers/helpers/app_status_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,22 @@ func (self *AppStatusHelper) WithWaitingStatus(message string, f func(gocui.Task
}

func (self *AppStatusHelper) WithWaitingStatusImpl(message string, f func(gocui.Task) error, task gocui.Task) error {
// A waiting status means lazygit is driving a git operation itself (often
// one that internally runs a rebase and continues it). Pause the background
// routines for its duration so they don't refresh from an intermediate
// state and reveal, say, the half-finished history of a reword.
self.c.PauseBackgroundRefreshes(true)
defer self.c.PauseBackgroundRefreshes(false)

return self.statusMgr().WithWaitingStatus(message, self.renderAppStatus, func(waitingStatusHandle *status.WaitingStatusHandle) error {
return f(appStatusHelperTask{task, waitingStatusHandle})
})
}

func (self *AppStatusHelper) WithWaitingStatusSync(message string, f func() error) error {
self.c.PauseBackgroundRefreshes(true)
defer self.c.PauseBackgroundRefreshes(false)

return self.statusMgr().WithWaitingStatus(message, func() {}, func(*status.WaitingStatusHandle) error {
stop := make(chan struct{})
defer func() { close(stop) }()
Expand Down
8 changes: 8 additions & 0 deletions pkg/gui/controllers/helpers/inline_status_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,14 @@ func (self *InlineStatusHelper) WithInlineStatus(opts InlineStatusOpts, f func(g
visible := view.Visible && self.windowHelper.TopViewInWindow(context.GetWindowName(), false) == view
if visible && context.IsItemVisible(opts.Item) {
self.c.OnWorker(func(task gocui.Task) error {
// An inline status is just a waiting status rendered on the item
// rather than in the bottom line, so it gets the same treatment:
// pause the background routines while we drive the operation. (The
// off-screen branch below goes through WithWaitingStatus, which
// already does this.)
self.c.PauseBackgroundRefreshes(true)
defer self.c.PauseBackgroundRefreshes(false)

self.start(opts)
defer self.stop(opts)

Expand Down
4 changes: 4 additions & 0 deletions pkg/gui/gui_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ func (self *guiCommon) Resume() error {
return self.gui.resume()
}

func (self *guiCommon) PauseBackgroundRefreshes(pause bool) {
self.gui.BackgroundRoutineMgr.PauseBackgroundRefreshes(pause)
}

func (self *guiCommon) Context() types.IContextMgr {
return self.gui.State.ContextMgr
}
Expand Down
4 changes: 4 additions & 0 deletions pkg/gui/types/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ type IGuiCommon interface {
Suspend() error
Resume() error

// Pause or resume the background routines. Calls nest, so every pause must be balanced
// by a resume.
PauseBackgroundRefreshes(pause bool)

Context() IContextMgr
ContextForKey(key ContextKey) Context

Expand Down
Loading