Files
lazygit/pkg/gui/controllers/context_lines_controller.go
T
Stefan HallerandClaude Opus 5 20625407d6 Keep your place in the diff when changing the context size
Pressing { or } re-rendered the diff with less or more context around each
change and dropped you back at the top of it, so finding your way back to
what you were reading was on you.

Remember where the view is before triggering the re-render, and have the
restore put it back there. The line to remember is the selected one when a
selection is showing, and the middle visible line otherwise — what you are
looking at, rather than the view's top edge. It is remembered by identity,
because the new rendering puts it on a different line of the view, and it
is found again by the records a diff renderer states for its rows, or by
parsing the rendering as a diff where it still looks like one.

The line may not be there at all afterwards: shrinking the context takes
context lines away. So the lines around it come along as fallbacks, nearest
first, and the view lands on the nearest one that survived, put back on the
screen row it was on — leaving what you were reading where it was, give or
take the line that went. The walk that collects them covers the whole diff
rather than stopping at the nearest change on either side: those always
survive a context-size change, but not everything a re-render can do to a
diff is that considerate.

Under a diff renderer that says nothing about its rows there is nothing to
look for at all, and the view is left at the offset it had instead: often not
the line it was on, but always closer to it than the top of the diff. That is
what the re-render asks for besides the restore, since it is a different
command from the one that produced what is on screen and would otherwise be
taken for a diff the user has never seen.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-27 18:26:55 +02:00

103 lines
3.2 KiB
Go

package controllers
import (
"errors"
"fmt"
"math"
"github.com/jesseduffield/lazygit/pkg/gui/context"
"github.com/jesseduffield/lazygit/pkg/gui/types"
)
// This controller lets you change the context size for diffs. The 'context' in 'context size' refers to the conventional meaning of the word 'context' in a diff, as opposed to lazygit's own idea of a 'context'.
type ContextLinesController struct {
baseController
c *ControllerCommon
}
var _ types.IController = &ContextLinesController{}
func NewContextLinesController(
c *ControllerCommon,
) *ContextLinesController {
return &ContextLinesController{
baseController: baseController{},
c: c,
}
}
func (self *ContextLinesController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
bindings := []*types.Binding{
{
Keys: opts.GetKeys(opts.Config.Universal.IncreaseContextInDiffView),
Handler: self.Increase,
Description: self.c.Tr.IncreaseContextInDiffView,
Tooltip: self.c.Tr.IncreaseContextInDiffViewTooltip,
},
{
Keys: opts.GetKeys(opts.Config.Universal.DecreaseContextInDiffView),
Handler: self.Decrease,
Description: self.c.Tr.DecreaseContextInDiffView,
Tooltip: self.c.Tr.DecreaseContextInDiffViewTooltip,
},
}
return bindings
}
func (self *ContextLinesController) Context() types.Context {
return nil
}
func (self *ContextLinesController) Increase() error {
if err := self.checkCanChangeContext(); err != nil {
return err
}
if self.c.UserConfig().Git.DiffContextSize < math.MaxUint64 {
self.c.UserConfig().Git.DiffContextSize++
}
return self.applyChange()
}
func (self *ContextLinesController) Decrease() error {
if err := self.checkCanChangeContext(); err != nil {
return err
}
if self.c.UserConfig().Git.DiffContextSize > 0 {
self.c.UserConfig().Git.DiffContextSize--
}
return self.applyChange()
}
func (self *ContextLinesController) applyChange() error {
self.c.Toast(fmt.Sprintf(self.c.Tr.DiffContextSizeChanged, self.c.UserConfig().Git.DiffContextSize))
currentContext := self.c.Context().CurrentSide()
switch currentContext.GetKey() {
// we make an exception for our staging and patch building contexts because they actually need to refresh their state afterwards.
case context.PATCH_BUILDING_MAIN_CONTEXT_KEY:
self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.PATCH_BUILDING}})
case context.STAGING_MAIN_CONTEXT_KEY, context.STAGING_SECONDARY_CONTEXT_KEY:
self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.STAGING}})
default:
// The diff is about to be rendered again with more or less context around
// each change, which reads as the lines you were looking at moving up or down
// the view; keep them where they are instead.
self.c.Helpers().DiffLine.PreserveDiffPositionOnRerender(self.c.Contexts().Normal.GetView())
self.c.Helpers().DiffLine.PreserveDiffPositionOnRerender(self.c.Contexts().NormalSecondary.GetView())
currentContext.HandleRenderToMain()
}
return nil
}
func (self *ContextLinesController) checkCanChangeContext() error {
if self.c.Git().Patch.PatchBuilder.Active() {
return errors.New(self.c.Tr.CantChangeContextSizeError)
}
return nil
}