Perform string-task view updates on the UI thread

The closures that render static content to a main view (newStringTask
and friends) ran on the ViewBufferManager's task goroutine, calling
SetViewContent/SetOrigin/ResetViewOrigin directly on the view. Those
touch view state (the line buffer, hover cells, the origin) that the UI
thread concurrently reads and mutates while laying out and drawing, so
they raced it -- e.g. a string task's SetContent clearing the view's
lines while the UI thread's CopyContent read them, or its SetOrigin
racing the layout's OriginY read.

Bounce the whole closure onto the UI thread instead, so the view is only
touched there. The bounce blocks (OnUIThreadAndWaitBackground) so the
task still completes only once the content has actually been rendered,
which the integration-test idle detection relies on; the background
variant keeps it from counting towards the app being busy, matching the
existing treatment of view rendering as work that must not block a repo
switch.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Stefan Haller 2026-07-08 07:53:14 +02:00
parent c58aa2a23b
commit 303372d917

View file

@ -59,8 +59,10 @@ func (gui *Gui) newStringTaskWithoutScroll(view *gocui.View, str string) error {
manager := gui.getManager(view)
f := func(tasks.TaskOpts) error {
gui.c.SetViewContent(view, str)
return nil
return gui.g.OnUIThreadAndWaitBackground(func() error {
gui.c.SetViewContent(view, str)
return nil
})
}
if err := manager.NewTask(f, manager.GetTaskKey()); err != nil {
@ -74,9 +76,11 @@ func (gui *Gui) newStringTaskWithScroll(view *gocui.View, str string, originX in
manager := gui.getManager(view)
f := func(tasks.TaskOpts) error {
gui.c.SetViewContent(view, str)
view.SetOrigin(originX, originY)
return nil
return gui.g.OnUIThreadAndWaitBackground(func() error {
gui.c.SetViewContent(view, str)
view.SetOrigin(originX, originY)
return nil
})
}
if err := manager.NewTask(f, manager.GetTaskKey()); err != nil {
@ -90,9 +94,11 @@ func (gui *Gui) newStringTaskWithKey(view *gocui.View, str string, key string) e
manager := gui.getManager(view)
f := func(tasks.TaskOpts) error {
gui.c.ResetViewOrigin(view)
gui.c.SetViewContent(view, str)
return nil
return gui.g.OnUIThreadAndWaitBackground(func() error {
gui.c.ResetViewOrigin(view)
gui.c.SetViewContent(view, str)
return nil
})
}
if err := manager.NewTask(f, key); err != nil {