Files
lazygit/pkg/gocui/task.go
Stefan HallerandClaude Opus 4.8 8655d3f5a5 Exclude view-buffer render tasks from the busy query
The repo-switch busy query must not count view-buffer content rendering:
those tasks paint a view rather than drive a git operation, so leaving
one running across a switch is harmless (the switch's own refresh
re-renders). More importantly, they fire on nearly every focus/selection
change — including the context activation that runs right before a
menu/prompt confirmation handler (e.g. confirming worktree creation).
A synchronous busy check in such a handler would otherwise see that
render and make the very switch the handler is about to request refuse
itself.

Route ViewBufferManager's tasks through a new gocui NewBackgroundTask so
they're tracked for idle detection but excluded from the busy query. The
task "background" flag now covers two kinds of non-blocking work: the
background routines (and their refreshes) tagged earlier, and view
rendering.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 18:09:33 +02:00

115 lines
2.4 KiB
Go

package gocui
// A task represents the fact that the program is busy doing something, which
// is useful for integration tests which only want to proceed when the program
// is idle.
type Task interface {
Done()
Pause()
Continue()
// not exporting these because we don't need to
isBusy() bool
isBackground() bool
}
type TaskImpl struct {
id int
busy bool
onDone func()
withMutex func(func())
// Background tasks don't count towards the program being "busy" for the
// purpose of deciding whether a repo switch is safe (see
// TaskManager.hasBusyForegroundTaskExcept). Two kinds of work are tagged
// this way: the ongoing background routines (auto-fetch, files refresh,
// external-change detection) and the refreshes they trigger, whose model
// writes are already guarded against a concurrent repo switch by the repo
// generation; and view-buffer content rendering, which only paints a view
// and so is harmless to leave running across a switch. What stays
// foreground is lazygit driving a git operation and applying its results
// to the model — exactly the work a repo switch must not run underneath.
background bool
}
func (self *TaskImpl) Done() {
self.onDone()
}
func (self *TaskImpl) Pause() {
self.withMutex(func() {
self.busy = false
})
}
func (self *TaskImpl) Continue() {
self.withMutex(func() {
self.busy = true
})
}
func (self *TaskImpl) isBusy() bool {
return self.busy
}
func (self *TaskImpl) isBackground() bool {
return self.background
}
type TaskStatus int
const (
TaskStatusBusy TaskStatus = iota
TaskStatusPaused
TaskStatusDone
)
type FakeTask struct {
status TaskStatus
}
func NewFakeTask() *FakeTask {
return &FakeTask{
status: TaskStatusBusy,
}
}
func (self *FakeTask) Done() {
self.status = TaskStatusDone
}
func (self *FakeTask) Pause() {
self.status = TaskStatusPaused
}
func (self *FakeTask) Continue() {
self.status = TaskStatusBusy
}
func (self *FakeTask) isBusy() bool {
return self.status == TaskStatusBusy
}
func (self *FakeTask) isBackground() bool {
return false
}
func (self *FakeTask) Status() TaskStatus {
return self.status
}
func (self *FakeTask) FormatStatus() string {
return formatTaskStatus(self.status)
}
func formatTaskStatus(status TaskStatus) string {
switch status {
case TaskStatusBusy:
return "busy"
case TaskStatusPaused:
return "paused"
case TaskStatusDone:
return "done"
}
return "unknown"
}