From f91d20f67db71d9986b2d86cb0d6dc7bf584bc9e Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Tue, 13 Oct 2026 16:28:02 +0200 Subject: [PATCH] Don't let a render stop reading before it has filled the view If the main view is scrolled more than about 5000 lines down a diff and another commit with a diff of more than 5000 lines is selected, the main view keeps showing the previous diff. A render reads its command's output into an off-screen buffer, and shows it only once it has read enough lines to fill the view or has reached the end of the output. It then reads on as far as an accurate scrollbar needs, and waits for the user to scroll further. Both counts include the lines above the view, so that a re-render of the diff the user is scrolled into fills the view where it is. But the 5000-line cap that is meant for the scrollbar applies to the lines above the view as well. So if the view is scrolled further down than that, the render stops reading before it has filled the view, and never shows anything. The view also counts as loading until the next render starts, so the layout doesn't keep it from scrolling past the end of the diff in the meantime. This has been a problem since 9e23111172 made renders read into an off-screen buffer. Before that, the lines of a render that stopped early showed up anyway. Apply the cap to the lines for the scrollbar only. Also never ask for fewer lines than fill the view. A view no more than four lines tall needs fewer lines than that for its scrollbar, even at the top of a diff. A render of another diff shows it from the top, so it doesn't need the lines above the view at all. A later commit stops it from reading them. Co-Authored-By: Claude Opus 5.5 (1M context) --- pkg/gui/view_helpers.go | 6 ++++-- .../show_another_long_diff_after_scrolling_far_down.go | 3 --- pkg/tasks/tasks.go | 5 +++++ 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/pkg/gui/view_helpers.go b/pkg/gui/view_helpers.go index 9d70b6e4b..ed4002748 100644 --- a/pkg/gui/view_helpers.go +++ b/pkg/gui/view_helpers.go @@ -44,10 +44,12 @@ func (gui *Gui) linesToReadFromCmdTask(v *gocui.View) tasks.LinesToRead { linesToReadForAccurateScrollbar := min( // However, cap it at some arbitrary max limit, so that we don't get // performance problems for huge monitors or tiny font sizes - height*(height-1)/minScrollbarHeight+oy, 5000) + height*(height-1)/minScrollbarHeight, 5000) + oy return tasks.LinesToRead{ - Total: linesToReadForAccurateScrollbar, + // A view no more than a few lines tall needs fewer lines for its scrollbar + // than it takes to fill it, and the task must read at least those. + Total: max(linesToReadForAccurateScrollbar, linesForFirstRefresh), InitialRefreshAfter: linesForFirstRefresh, } } diff --git a/pkg/integration/tests/main_view/show_another_long_diff_after_scrolling_far_down.go b/pkg/integration/tests/main_view/show_another_long_diff_after_scrolling_far_down.go index 094cb99ba..2f0580663 100644 --- a/pkg/integration/tests/main_view/show_another_long_diff_after_scrolling_far_down.go +++ b/pkg/integration/tests/main_view/show_another_long_diff_after_scrolling_far_down.go @@ -47,9 +47,6 @@ var ShowAnotherLongDiffAfterScrollingFarDown = NewIntegrationTest(NewIntegration SelectedLine(Contains("first commit")) t.Views().Main(). - /* EXPECTED: Content(Contains("diff --git a/first.txt b/first.txt")) - ACTUAL: */ - Content(Contains("diff --git a/second.txt b/second.txt")) }, }) diff --git a/pkg/tasks/tasks.go b/pkg/tasks/tasks.go index 510fffa49..7e0c10676 100644 --- a/pkg/tasks/tasks.go +++ b/pkg/tasks/tasks.go @@ -167,6 +167,11 @@ type LinesToRead struct { // Number of lines after which we have read enough to fill the view, and can // do an initial refresh. Only set for the initial read request; -1 for // subsequent requests. + // + // Unless Total is -1, it must not be more than Total. The task shows nothing + // of what it reads until it has read this many lines or has reached the end + // of its input, so a task that stops reading before then leaves the view + // showing the previous render. InitialRefreshAfter int // Function to call after reading the lines is done