From cdc47d6512bcf25bc8ecad416bbf29bff201878e 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 | 45 ++++++++++++++++++ pkg/gui/controllers/main_view_controller.go | 46 ------------------- .../controllers/working_tree_diff_actions.go | 2 +- 3 files changed, 46 insertions(+), 47 deletions(-) diff --git a/pkg/gui/controllers/helpers/diff_line_restore.go b/pkg/gui/controllers/helpers/diff_line_restore.go index 38a070717..8220ff64d 100644 --- a/pkg/gui/controllers/helpers/diff_line_restore.go +++ b/pkg/gui/controllers/helpers/diff_line_restore.go @@ -543,3 +543,48 @@ 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 buffer line the selection +// starts on, 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, firstBufferLine int, done func(), +) { + ordinal := self.ChangeLineOrdinal(source.GetView(), firstBufferLine) + + 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 a4b5787ce..a52421ed3 100644 --- a/pkg/gui/controllers/main_view_controller.go +++ b/pkg/gui/controllers/main_view_controller.go @@ -343,52 +343,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 -// buffer line the selection starts on, 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, - firstBufferLine int, done func(), -) { - ordinal := c.Helpers().DiffLine.ChangeLineOrdinal(source.GetView(), firstBufferLine) - - 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 0dd1383da..a8b7d3d8e 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, firstBufferLine, + self.c.Helpers().DiffLine.RevealSelectionAfterAction(pane, target, firstBufferLine, self.c.GocuiGui().EndBlockingEvents) }