Let a main pane be named without the context package

The mode of a diff view's selection lives on the context of the pane showing
it, and until now only the controllers that drive the selection needed to
name that pane, all of which have it as the concrete context it is.

A side panel is about to be handed the pane a command was invoked in, so
that it can act on what is selected there — and it can't be handed a
context, the interface it is handed one through being declared where a
context's concrete type isn't available. So the state moves to where such an
interface can speak of it, and the panes gain one: a context that has a diff
selection.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Stefan Haller
2026-10-05 10:03:31 +02:00
co-authored by Claude Opus 5
parent 81851dcbbf
commit 57757a1b8e
4 changed files with 60 additions and 47 deletions
+6 -30
View File
@@ -9,45 +9,21 @@ type MainContext struct {
*SimpleContext
*SearchTrait
diffSelect DiffSelectState
diffSelect types.DiffSelectState
// selectableContentRenderKey names the render whose content HasSelectableContent
// was worked out from. What there is to select is a property of the content, so an
// answer about the content of another render says nothing about this one.
selectableContentRenderKey string
}
var _ types.ISearchableContext = (*MainContext)(nil)
// DiffSelectMode is how the focused main view's diff selection extends from the
// cursor: a single line, a range from a fixed anchor, or the change block (hunk)
// around the cursor.
type DiffSelectMode int
const (
DiffSelectModeLine DiffSelectMode = iota
DiffSelectModeRange
DiffSelectModeHunk
var (
_ types.ISearchableContext = (*MainContext)(nil)
_ types.DiffPaneContext = (*MainContext)(nil)
)
// DiffSelectState holds the *mode* of the focused main view's diff selection. The
// selected line and the range anchor themselves live in the gocui view (its cursor
// and range-select start), so only the mode lives here. It's on the context rather
// than on the controller because the controller that drives the selection, the
// controller that establishes it on focus, and the pane-toggle that seeds it on the
// other pane all reach the pane through its context.
type DiffSelectState struct {
Mode DiffSelectMode
// When a range is sticky, moving the cursor without holding shift extends the
// range; otherwise it collapses the range back to a single line.
RangeIsSticky bool
// Whether hunk mode was turned on by the user rather than being the configured
// default. This decides whether escape leaves hunk mode or leaves the view.
UserEnabledHunkMode bool
}
// DiffSelectState returns the focused main view's selection mode state, for the
// controllers to read and mutate directly.
func (self *MainContext) DiffSelectState() *DiffSelectState {
func (self *MainContext) DiffSelectState() *types.DiffSelectState {
return &self.diffSelect
}
@@ -56,7 +32,7 @@ func (self *MainContext) DiffSelectState() *DiffSelectState {
// view's range anchor is cleared too, so the next render highlights the cursor line
// only.
func (self *MainContext) ResetDiffSelectMode() {
self.diffSelect.Mode = DiffSelectModeLine
self.diffSelect.Mode = types.DiffSelectModeLine
self.diffSelect.RangeIsSticky = false
self.diffSelect.UserEnabledHunkMode = false
self.GetView().CancelRangeSelect()
+17 -17
View File
@@ -43,7 +43,7 @@ func (self *MainViewController) GetKeybindings(opts types.KeybindingsOpts) []*ty
Keys: opts.GetKeys(opts.Config.Main.ToggleSelectHunk),
Handler: self.toggleSelectHunk,
DescriptionFunc: self.diffSelectionDescription(func() string {
if self.diffSelectState().Mode == context.DiffSelectModeHunk {
if self.diffSelectState().Mode == types.DiffSelectModeHunk {
return self.c.Tr.SelectLineByLine
}
return self.c.Tr.SelectHunk
@@ -163,7 +163,7 @@ func (self *MainViewController) escapeDescription() string {
// selectingHunkEnabledByUser reports whether we are in hunk mode because the user
// asked for it, as opposed to it being the configured default.
func (self *MainViewController) selectingHunkEnabledByUser() bool {
return self.diffSelectState().Mode == context.DiffSelectModeHunk && self.diffSelectState().UserEnabledHunkMode
return self.diffSelectState().Mode == types.DiffSelectModeHunk && self.diffSelectState().UserEnabledHunkMode
}
// isDiffView reports whether the focused main view currently shows a diff, and so
@@ -189,7 +189,7 @@ func (self *MainViewController) diffMainViewType() types.DiffMainViewType {
}
// diffSelectState returns this pane's diff selection mode state.
func (self *MainViewController) diffSelectState() *context.DiffSelectState {
func (self *MainViewController) diffSelectState() *types.DiffSelectState {
return self.context.DiffSelectState()
}
@@ -250,7 +250,7 @@ func (self *MainViewController) selectClickedDiffLine(viewLine int) {
return
}
view := self.context.GetView()
if self.diffSelectState().Mode == context.DiffSelectModeHunk &&
if self.diffSelectState().Mode == types.DiffSelectModeHunk &&
self.c.Helpers().DiffLine.IsChangeLine(view, viewLine) {
self.selectHunkAround(viewLine, false)
return
@@ -313,7 +313,7 @@ func (self *MainViewController) selectHunkAround(changeViewLine int, scrollIntoV
view := self.context.GetView()
start, end, ok := self.c.Helpers().DiffLine.ChangeBlockBounds(view, changeViewLine)
if !ok {
self.diffSelectState().Mode = context.DiffSelectModeLine
self.diffSelectState().Mode = types.DiffSelectModeLine
view.CancelRangeSelect()
showSelectionAtLine(view, changeViewLine, scrollIntoView)
return
@@ -370,7 +370,7 @@ func (self *MainViewController) placeNavigationTarget(target int) {
v.SetOrigin(0, target)
return
}
if self.diffSelectState().Mode == context.DiffSelectModeHunk {
if self.diffSelectState().Mode == types.DiffSelectModeHunk {
self.selectHunkAround(target, true)
return
}
@@ -403,9 +403,9 @@ func (self *MainViewController) moveCursor(delta int) {
// sticky range is kept, so the move extends it.
func (self *MainViewController) collapseForLineMove() {
sel := self.diffSelectState()
if sel.Mode == context.DiffSelectModeHunk ||
(sel.Mode == context.DiffSelectModeRange && !sel.RangeIsSticky) {
sel.Mode = context.DiffSelectModeLine
if sel.Mode == types.DiffSelectModeHunk ||
(sel.Mode == types.DiffSelectModeRange && !sel.RangeIsSticky) {
sel.Mode = types.DiffSelectModeLine
self.context.GetView().CancelRangeSelect()
}
}
@@ -419,7 +419,7 @@ func (self *MainViewController) adjustSelection(delta int) {
self.handleLineChange(delta)
return
}
if self.diffSelectState().Mode == context.DiffSelectModeHunk && (delta == 1 || delta == -1) {
if self.diffSelectState().Mode == types.DiffSelectModeHunk && (delta == 1 || delta == -1) {
self.navigate(self.c.Helpers().DiffLine.AdjacentChangeBlock, delta > 0)
return
}
@@ -438,7 +438,7 @@ func (self *MainViewController) selectAbsoluteLine(target int) {
// range mode and either it's sticky or the anchor and cursor differ, i.e. a
// non-sticky range that has actually been extended.
func (self *MainViewController) selectingRange() bool {
if self.diffSelectState().Mode != context.DiffSelectModeRange {
if self.diffSelectState().Mode != types.DiffSelectModeRange {
return false
}
start, end := self.context.GetView().SelectedLineRange()
@@ -453,11 +453,11 @@ func (self *MainViewController) toggleSelectHunk() error {
return nil
}
sel := self.diffSelectState()
if sel.Mode == context.DiffSelectModeHunk {
sel.Mode = context.DiffSelectModeLine
if sel.Mode == types.DiffSelectModeHunk {
sel.Mode = types.DiffSelectModeLine
v.CancelRangeSelect()
} else {
sel.Mode = context.DiffSelectModeHunk
sel.Mode = types.DiffSelectModeHunk
sel.UserEnabledHunkMode = true
self.selectHunkAround(v.SelectedLineIdx(), true)
}
@@ -473,11 +473,11 @@ func (self *MainViewController) toggleRangeSelect() error {
}
sel := self.diffSelectState()
if self.selectingRange() {
sel.Mode = context.DiffSelectModeLine
sel.Mode = types.DiffSelectModeLine
sel.RangeIsSticky = false
v.CancelRangeSelect()
} else {
sel.Mode = context.DiffSelectModeRange
sel.Mode = types.DiffSelectModeRange
sel.RangeIsSticky = true
v.SetRangeSelectStart(v.SelectedLineIdx())
}
@@ -493,7 +493,7 @@ func (self *MainViewController) extendRange(forward bool) error {
}
sel := self.diffSelectState()
if !self.selectingRange() {
sel.Mode = context.DiffSelectModeRange
sel.Mode = types.DiffSelectModeRange
v.SetRangeSelectStart(v.SelectedLineIdx())
}
sel.RangeIsSticky = false
+9
View File
@@ -211,6 +211,15 @@ const (
DiffMainViewTypePatchBuilding
)
// DiffPaneContext is one of the two panes the main section can show, as the thing
// that holds a diff with a selection in it. The panels that act on such a selection
// are handed the pane it was made in, and speak to it through this.
type DiffPaneContext interface {
Context
DiffSelectState() *DiffSelectState
}
type IListContext interface {
Context
+28
View File
@@ -0,0 +1,28 @@
package types
// DiffSelectMode is how the focused main view's diff selection extends from the
// cursor: a single line, a range from a fixed anchor, or the change block (hunk)
// around the cursor.
type DiffSelectMode int
const (
DiffSelectModeLine DiffSelectMode = iota
DiffSelectModeRange
DiffSelectModeHunk
)
// DiffSelectState holds the *mode* of the focused main view's diff selection. The
// selected line and the range anchor themselves live in the gocui view (its cursor
// and range-select start), so only the mode lives here. It's on the context rather
// than on the controller because the controller that drives the selection, the
// controller that establishes it on focus, and the pane-toggle that seeds it on the
// other pane all reach the pane through its context.
type DiffSelectState struct {
Mode DiffSelectMode
// When a range is sticky, moving the cursor without holding shift extends the
// range; otherwise it collapses the range back to a single line.
RangeIsSticky bool
// Whether hunk mode was turned on by the user rather than being the configured
// default. This decides whether escape leaves hunk mode or leaves the view.
UserEnabledHunkMode bool
}