From 1edfbc5e8067dbc29f09934d97202aa31a3bb3a8 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Wed, 19 Aug 2026 17:08:07 +0200 Subject: [PATCH] Move the post-action reveal onto the diff-line helper It is built entirely on that helper, and the render chokepoint that is about to want it lives in the gui package, which cannot reach a controller. Co-Authored-By: Claude Opus 5 (1M context) --- .../controllers/helpers/diff_line_restore.go | 49 ++++++++++++++++++ pkg/gui/controllers/main_view_controller.go | 50 ------------------- .../controllers/working_tree_diff_actions.go | 2 +- 3 files changed, 50 insertions(+), 51 deletions(-) diff --git a/pkg/gui/controllers/helpers/diff_line_restore.go b/pkg/gui/controllers/helpers/diff_line_restore.go index d0cb25023..b1d8b6b64 100644 --- a/pkg/gui/controllers/helpers/diff_line_restore.go +++ b/pkg/gui/controllers/helpers/diff_line_restore.go @@ -549,3 +549,52 @@ func patchLineOf(info types.DiffLineInfo) patchLine { return patchLine{path: info.Path, kind: types.DiffLineContext, line: info.NewLine} } } + +// RevealSelectionAfterAction moves a diff pane's selection to the change that takes the +// place of the one just acted on, once the changed diff has re-rendered. Call it with +// the pane acted in, the pane the work carries on in, and the first line of the +// selection, before triggering the re-render. +// +// The line acted on is gone from the diff, so what is remembered is its place among the +// diff's changes: the next change moves up into it, which is where you want to be to +// carry on. A range collapses to a single line at its start, and hunk mode selects the +// whole block it lands in, so that pressing the key again acts on the next hunk. The +// target pane inherits that select mode, this being the same piece of work continuing +// in another pane — and shows no selection until the restore places one, so that what +// it was left showing the last time it was used doesn't appear for a frame. +// +// done is called once the selection is where it belongs, or once it turns out that no +// render is coming to put it there, for a caller that must not let the user act again +// in between. +func (self *DiffLineHelper) RevealSelectionAfterAction( + source types.DiffPaneContext, target types.DiffPaneContext, firstLineIdx int, done func(), +) { + ordinal, ok := self.ChangeLineOrdinal(source.GetView(), firstLineIdx) + if !ok { + done() + return + } + + sel := source.DiffSelectState() + if sel.Mode == types.DiffSelectModeRange { + sel.Mode = types.DiffSelectModeLine + sel.RangeIsSticky = false + } + *target.DiffSelectState() = *sel + selectHunk := sel.Mode == types.DiffSelectModeHunk + + targetView := target.GetView() + if target != source { + target.SetHasSelectableContent(false) + self.c.Context().UpdateSelectionHighlights() + } + + self.RevealChangeLineAtOrdinal(targetView, ordinal, func(viewLine int) { + if selectHunk { + self.SelectChangeBlock(target, viewLine, true) + return + } + targetView.CancelRangeSelect() + self.ShowSelectionAtLine(targetView, viewLine, true) + }, done) +} diff --git a/pkg/gui/controllers/main_view_controller.go b/pkg/gui/controllers/main_view_controller.go index a850de364..02792a5da 100644 --- a/pkg/gui/controllers/main_view_controller.go +++ b/pkg/gui/controllers/main_view_controller.go @@ -340,56 +340,6 @@ func (self *MainViewController) primaryAction() error { return actions.PrimaryAction(self.context, first, last) } -// revealSelectionAfterAction moves the selection to the change that takes the place of -// the one just acted on, once the changed diff has re-rendered. Call it from the panel's -// action handler with the pane it acted in, the pane the work carries on in, and the -// first line of the selection, before triggering the re-render. -// -// The line acted on is gone from the diff, so what is remembered is its place among the -// diff's changes: the next change moves up into it, which is where you want to be to -// carry on. A range collapses to a single line at its start, and hunk mode selects the -// whole block it lands in, so that pressing the key again acts on the next hunk. The -// target pane inherits that select mode, this being the same piece of work continuing -// in another pane — and shows no selection until the restore places one, so that what -// it was left showing the last time it was used doesn't appear for a frame. -// -// done is called once the selection is where it belongs, or once it turns out that no -// render is coming to put it there, for a caller that must not let the user act again -// in between. -func revealSelectionAfterAction( - c *ControllerCommon, source types.DiffPaneContext, target types.DiffPaneContext, - firstLineIdx int, done func(), -) { - ordinal, ok := c.Helpers().DiffLine.ChangeLineOrdinal(source.GetView(), firstLineIdx) - if !ok { - done() - return - } - - sel := source.DiffSelectState() - if sel.Mode == types.DiffSelectModeRange { - sel.Mode = types.DiffSelectModeLine - sel.RangeIsSticky = false - } - *target.DiffSelectState() = *sel - selectHunk := sel.Mode == types.DiffSelectModeHunk - - targetView := target.GetView() - if target != source { - target.SetHasSelectableContent(false) - c.Context().UpdateSelectionHighlights() - } - - c.Helpers().DiffLine.RevealChangeLineAtOrdinal(targetView, ordinal, func(viewLine int) { - if selectHunk { - c.Helpers().DiffLine.SelectChangeBlock(target, viewLine, true) - return - } - targetView.CancelRangeSelect() - c.Helpers().DiffLine.ShowSelectionAtLine(targetView, viewLine, true) - }, done) -} - // discardSelection takes the selected diff lines back out of what they are part of, // which — like the primary action — is the panel's business, and so is the re-render // that follows. diff --git a/pkg/gui/controllers/working_tree_diff_actions.go b/pkg/gui/controllers/working_tree_diff_actions.go index f6681d16f..92695aca0 100644 --- a/pkg/gui/controllers/working_tree_diff_actions.go +++ b/pkg/gui/controllers/working_tree_diff_actions.go @@ -378,7 +378,7 @@ func (self *WorkingTreeDiffActions) revealSelectionInPaneItLandsIn( // that, and until it has been the selection is still on lines that aren't there any // more — so a key pressed meanwhile would act on nothing. self.c.GocuiGui().BeginBlockingEvents() - revealSelectionAfterAction(self.c, pane, target, firstLineIdx, + self.c.Helpers().DiffLine.RevealSelectionAfterAction(pane, target, firstLineIdx, self.c.GocuiGui().EndBlockingEvents) }