From b1566a5e68b5f09c1c2469ee8356f2e247987884 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Sun, 16 Aug 2026 06:42:06 +0200 Subject: [PATCH] Extract the shared core of the diff-line restores MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Putting a view back on a remembered line as it re-renders is two things: the plumbing that watches the content arrive, reveals it at the right moment and places the view, and the search that says which row to land on. Only the second is specific to what is being remembered, and a second kind of it is about to arrive — the change line an action leaves the selection on, which is found by counting rather than by identity. Behaviour-preserving. Co-authored-by: Claude Opus 5 (1M context) --- .../controllers/helpers/diff_line_restore.go | 77 +++++++++++++++---- 1 file changed, 62 insertions(+), 15 deletions(-) diff --git a/pkg/gui/controllers/helpers/diff_line_restore.go b/pkg/gui/controllers/helpers/diff_line_restore.go index 7a35d7638..c11364998 100644 --- a/pkg/gui/controllers/helpers/diff_line_restore.go +++ b/pkg/gui/controllers/helpers/diff_line_restore.go @@ -230,20 +230,68 @@ func (self *DiffLineHelper) findDiffLine(view *gocui.View, identity types.DiffLi func (self *DiffLineHelper) restoreDiffLinePositionOnRerender( view *gocui.View, candidates []diffLineAnchor, place func(anchor diffLineAnchor, viewLine int), ) { - manager := self.c.GetViewBufferManagerForView(view) - if manager == nil || len(candidates) == 0 { + if len(candidates) == 0 { return } - // The readiness check below runs on the task's own goroutine, where neither the - // view's dimensions nor the repo we are in may be read — a repo switch replaces - // the latter — so take both here, on the UI thread, for it to work from. - viewHeight := view.InnerHeight() + // The search of the loading content runs on the task's own goroutine, where the + // repo we are in may not be read — a repo switch replaces it — so take it here, on + // the UI thread, for the search to work from. worktreePath := self.c.Git().RepoPaths.WorktreePath() + // Which candidate the search settled on, for place to put back where it was. + found := diffLineAnchor{} + + self.installDiffLineRestore(view, + func(rows []gocui.DiffLineContent, offset int) (int, bool) { + for i, row := range rows { + if rowShowsDiffLine(row, worktreePath, candidates[0].identity) { + found = candidates[0] + return offset + i, true + } + } + return 0, false + }, + func(contents []gocui.DiffLineContent) (int, bool) { + anchor, bufferLine := self.nearestSurvivingCandidate(contents, candidates) + if bufferLine == -1 { + return 0, false + } + found = anchor + return bufferLine, true + }, + func(viewLine int) { place(found, viewLine) }, + ) +} + +// installDiffLineRestore is what the restores are built on: it arranges for view's +// next re-render to be revealed with the row a search finds in it placed by place, +// instead of from the top. +// +// The search comes in two halves, because the content arrives a line at a time. +// findEarly is given the rows that have loaded since it last looked, so that the +// re-render can be revealed as soon as the row is there rather than waiting for the +// rest of a long diff; it can only go by what the renderer states about a row, a +// partly-loaded diff being unparseable. findComplete is given the whole rendering at +// the swap, for a target the incremental search couldn't settle on. Either returns the +// buffer line it found, and place is not called at all when neither does. +func (self *DiffLineHelper) installDiffLineRestore( + view *gocui.View, + findEarly func(rows []gocui.DiffLineContent, offset int) (int, bool), + findComplete func(contents []gocui.DiffLineContent) (int, bool), + place func(viewLine int), +) { + manager := self.c.GetViewBufferManagerForView(view) + if manager == nil { + return + } + + // The readiness check below runs on the task's own goroutine, which may not read + // the view's dimensions, so take them here, on the UI thread. + viewHeight := view.InnerHeight() + // What the search of the loading content has found, and how far it has looked, so // that each line is looked at once. - found := diffLineAnchor{} foundLine := -1 scanned := 0 @@ -251,11 +299,8 @@ func (self *DiffLineHelper) restoreDiffLinePositionOnRerender( FirstPaintReady: func() bool { if foundLine == -1 { rows := view.OffscreenDiffLineContentsFrom(scanned) - for i, row := range rows { - if rowShowsDiffLine(row, worktreePath, candidates[0].identity) { - found, foundLine = candidates[0], scanned+i - break - } + if bufferLine, ok := findEarly(rows, scanned); ok { + foundLine = bufferLine } scanned += len(rows) if foundLine == -1 { @@ -267,9 +312,11 @@ func (self *DiffLineHelper) restoreDiffLinePositionOnRerender( return view.OffscreenLineCount() >= foundLine+viewHeight }, Apply: func(swapIn func()) bool { - anchor, bufferLine := found, foundLine + bufferLine := foundLine if bufferLine == -1 { - anchor, bufferLine = self.nearestSurvivingCandidate(view.OffscreenDiffLineContents(), candidates) + if line, ok := findComplete(view.OffscreenDiffLineContents()); ok { + bufferLine = line + } } swapIn() @@ -281,7 +328,7 @@ func (self *DiffLineHelper) restoreDiffLinePositionOnRerender( if !ok { return false } - place(anchor, viewLine) + place(viewLine) return true }, })