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) <noreply@anthropic.com>
This commit is contained in:
Stefan HallerandClaude Opus 5.5 committed 2026-10-13 16:28:02 +02:00
1 parent 56b6c5af31
commit f91d20f67d
3 files changed
+9 -5

No files matched your search

+4 -2
View File
@@ -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,
}
}
@@ -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"))
},
})
+5
View File
@@ -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