From 73d7b443ec2a534aa14ada8092900d0a3d941c64 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Wed, 8 Jul 2026 19:15:45 +0200 Subject: [PATCH] Render content-only when a task reads more lines into a view Reading more lines into a lazy-loaded view (e.g. a diff being scrolled) never changes the window layout, and after the first screenful it doesn't even change the visible content - the new lines land below the viewport, so the only thing that changes on screen is the scrollbar thumb. Yet each read triggered a full render: a layout pass plus a redraw of every view. On a slow terminal that full-screen repaint on every read is a big part of why scrolling through a not-yet-fully-read diff stutters. Route the task's refresh through a content-only render instead. It skips the layout pass and only redraws the views whose content changed, leaving tcell's cell-level dirty tracking to emit just the cells that actually differ (in the steady state, the scrollbar column). Co-Authored-By: Claude Opus 4.8 (1M context) --- pkg/gui/tasks_adapter.go | 7 ++++++- pkg/gui/view_helpers.go | 8 ++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/pkg/gui/tasks_adapter.go b/pkg/gui/tasks_adapter.go index dd7999107..acad4fb75 100644 --- a/pkg/gui/tasks_adapter.go +++ b/pkg/gui/tasks_adapter.go @@ -118,7 +118,12 @@ func (gui *Gui) getManager(view *gocui.View) *tasks.ViewBufferManager { view.Reset() }, func() { - gui.render() + // As the task reads more lines, the only thing that changes is the + // view's content (and its scrollbar); the window layout doesn't. So a + // content-only render is enough, and it's much cheaper than a full + // layout-and-redraw on every read - which matters a lot when reading + // a long diff, where reads happen repeatedly as the user scrolls. + gui.renderContentOnly() }, func() { // Need to check if the content of the view is well past the origin. diff --git a/pkg/gui/view_helpers.go b/pkg/gui/view_helpers.go index 453ccd6c9..d139984fa 100644 --- a/pkg/gui/view_helpers.go +++ b/pkg/gui/view_helpers.go @@ -121,6 +121,14 @@ func (gui *Gui) render() { gui.c.OnUIThread(func() error { return nil }) } +// renderContentOnly triggers a re-render that skips the layout pass and only +// redraws the views whose content changed (relying on tcell's cell-level dirty +// tracking to emit just the cells that actually differ). Use it when only a +// view's content changed, not the window layout. +func (gui *Gui) renderContentOnly() { + gui.c.OnUIThreadContentOnly(func() error { return nil }) +} + // postRefreshUpdate is to be called on a context after the state that it depends on has been refreshed // if the context's view is set to another context we do nothing. // if the context's view is the current view we trigger a focus; re-selecting the current item.