Files
lazygit/pkg/gui/controllers/switch_to_focused_main_view_controller.go
T
Stefan HallerandClaude Opus 5 a606e12ec7 Show git's own diff when the renderer's can't be acted on
A diff renderer may lay a diff out however it likes: line numbers in a
gutter, the +/- column replaced by colour, the two sides in columns. Once
it has, we can only tell which line of which file a row shows if the
renderer says so. Under a renderer that doesn't say, the main view holds
a diff that can be read but not staged, edited or copied from. That is no
good now that the main view is where you stage.

So focusing it brings git's own diff instead, and every re-render while it
stays focused keeps to that, so staging a hunk doesn't flip back. Browsing
is untouched: you see what the renderer produced until you focus the view
to act on it.

Whether the renderer says anything is settled by asking it rather than by
watching it work: run it on empty input and see whether it announces the
protocol. Announcing is a property of the renderer, so the answer is known
before we render anything, and a diff with no lines to describe can't fool
it. Watching would have to see a diff go by first, and a binary file's diff
holds nothing that would tell the two cases apart. The answer is remembered
until the renderer changes. git itself is asked the same question, with the
renderer's own arguments, since it announces itself for exactly the formats
whose output can't be read back as a diff.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-04 19:01:02 +02:00

106 lines
3.5 KiB
Go

package controllers
import (
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
"github.com/jesseduffield/lazygit/pkg/gocui"
"github.com/jesseduffield/lazygit/pkg/gui/context"
"github.com/jesseduffield/lazygit/pkg/gui/types"
)
// This controller is for all contexts that can focus their main view.
var _ types.IController = &SwitchToFocusedMainViewController{}
type SwitchToFocusedMainViewController struct {
baseController
c *ControllerCommon
context types.Context
}
func NewSwitchToFocusedMainViewController(
c *ControllerCommon,
context types.Context,
) *SwitchToFocusedMainViewController {
return &SwitchToFocusedMainViewController{
baseController: baseController{},
c: c,
context: context,
}
}
func (self *SwitchToFocusedMainViewController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
bindings := []*types.Binding{
{
Keys: opts.GetKeys(opts.Config.Universal.FocusMainView),
Handler: self.handleFocusMainView,
Description: self.c.Tr.FocusMainView,
Tag: "global",
},
}
return bindings
}
func (self *SwitchToFocusedMainViewController) GetMouseKeybindings(opts types.KeybindingsOpts) []*gocui.ViewMouseBinding {
return []*gocui.ViewMouseBinding{
{
ViewName: "main",
Key: gocui.MouseLeft,
Handler: self.onClickMain,
FocusedView: self.context.GetViewName(),
},
{
ViewName: "secondary",
Key: gocui.MouseLeft,
Handler: self.onClickSecondary,
FocusedView: self.context.GetViewName(),
},
}
}
func (self *SwitchToFocusedMainViewController) Context() types.Context {
return self.context
}
func (self *SwitchToFocusedMainViewController) onClickMain(opts gocui.ViewMouseBindingOpts) error {
return self.focusMainView(self.c.Contexts().Normal, opts.Y)
}
func (self *SwitchToFocusedMainViewController) onClickSecondary(opts gocui.ViewMouseBindingOpts) error {
return self.focusMainView(self.c.Contexts().NormalSecondary, opts.Y)
}
func (self *SwitchToFocusedMainViewController) handleFocusMainView() error {
// Usually the main pane, but the content can be in the secondary one alone: a file
// with nothing but staged changes shows them there.
mainViewContext := self.c.Contexts().Normal
if self.c.State().GetRepoState().GetMainPanes() == types.SecondaryPaneOnly {
mainViewContext = self.c.Contexts().NormalSecondary
}
return self.focusMainView(mainViewContext, -1)
}
func (self *SwitchToFocusedMainViewController) focusMainView(mainViewContext *context.MainContext, clickedLineIdx int) error {
mainViewContext.ClearSearchString()
self.c.Context().Push(mainViewContext, types.OnFocusOpts{})
if _, ok := self.context.(types.DiffMainViewContext); !ok {
return nil
}
// The diff on screen was produced for reading, and the renderer that produced it may
// have laid it out in a way that says nothing about which line of which file each row
// is. Now that the user wants to act on it, it is re-rendered as git's own diff — the
// panel below decides that for itself, from the same question — and the selection
// goes on that instead of on rows we can't place.
if self.c.Helpers().DiffLine.MainViewDiffMode() == git_commands.DiffModeRaw {
self.c.Helpers().DiffLine.RenderFocusedMainViewAgain(mainViewContext.GetView(), self.context, func() {
self.c.Helpers().DiffLine.EstablishSelection(mainViewContext, clickedLineIdx)
})
return nil
}
self.c.Helpers().DiffLine.EstablishSelection(mainViewContext, clickedLineIdx)
return nil
}