diff --git a/pkg/gui/controllers/commit_diff_actions.go b/pkg/gui/controllers/commit_diff_actions.go new file mode 100644 index 000000000..bd3ebb7ab --- /dev/null +++ b/pkg/gui/controllers/commit_diff_actions.go @@ -0,0 +1,45 @@ +package controllers + +import ( + "github.com/jesseduffield/lazygit/pkg/gui/types" +) + +// CommitDiffActions implements what a panel showing a commit's diff offers on that diff +// in the focused main view. Five panels do: the commit files panel shows the diff of one +// file of a commit, and the commits, sub-commits, stash and reflog panels the whole diff +// of whatever they have selected. They all offer the same thing and differ only in which +// diff they show, so they share this, each saying which diff that is. +type CommitDiffActions struct { + c *ControllerCommon + + // The panel this belongs to, and what it is showing the diff of — nil when it has + // nothing selected, and so no diff. + panel types.Context + target func() *commitDiffTarget +} + +// commitDiffTarget is the diff a panel is showing: the two ends of it, and whether it +// belongs to a commit lazygit may rewrite. +type commitDiffTarget struct { + from string + to string + canRebase bool +} + +var _ types.FocusedMainViewDiffSource = &CommitDiffActions{} + +func NewCommitDiffActions( + c *ControllerCommon, panel types.Context, target func() *commitDiffTarget, +) *CommitDiffActions { + return &CommitDiffActions{c: c, panel: panel, target: target} +} + +// PlainDiff hands out the diff the panel is showing, for the given files — the same +// diff as in the main view, only without the commit's message and stat above it. +func (self *CommitDiffActions) PlainDiff(_ types.DiffPaneContext, paths []string) string { + target := self.target() + if target == nil { + return "" + } + return self.c.Helpers().Diff.PlainDiffBetweenRefs(target.from, target.to, paths) +} diff --git a/pkg/gui/controllers/commits_files_controller.go b/pkg/gui/controllers/commits_files_controller.go index bb197ae73..143375b71 100644 --- a/pkg/gui/controllers/commits_files_controller.go +++ b/pkg/gui/controllers/commits_files_controller.go @@ -23,6 +23,9 @@ type CommitFilesController struct { baseController *ListControllerTrait[*filetree.CommitFileNode] c *ControllerCommon + + // what this panel offers on the diff it shows in the focused main view + diffActions *CommitDiffActions } var _ types.IController = &CommitFilesController{} @@ -30,7 +33,7 @@ var _ types.IController = &CommitFilesController{} func NewCommitFilesController( c *ControllerCommon, ) *CommitFilesController { - return &CommitFilesController{ + controller := &CommitFilesController{ baseController: baseController{}, c: c, ListControllerTrait: NewListControllerTrait( @@ -40,6 +43,18 @@ func NewCommitFilesController( c.Contexts().CommitFiles.GetSelectedItems, ), } + controller.diffActions = NewCommitDiffActions(c, c.Contexts().CommitFiles, controller.diffTarget) + return controller +} + +// diffTarget is the commit whose files this panel is showing. Its main view shows the +// diff of that commit. +func (self *CommitFilesController) diffTarget() *commitDiffTarget { + if self.context().GetRef() == nil && self.context().GetRefRange() == nil { + return nil + } + from, to := self.context().GetFromAndToForDiff() + return &commitDiffTarget{from: from, to: to, canRebase: self.context().GetCanRebase()} } func (self *CommitFilesController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { @@ -193,15 +208,7 @@ func (self *CommitFilesController) GetOnRenderToMain() func() { } func (self *CommitFilesController) GetFocusedMainViewDiffSource() types.FocusedMainViewDiffSource { - return self -} - -// PlainDiff hands out the commit's diff for the given files. Both panes show the same -// diff here — the secondary one shows the custom patch built from it, which is not a -// diff of the commit — so which pane asks makes no difference. -func (self *CommitFilesController) PlainDiff(_ types.DiffPaneContext, paths []string) string { - from, to := self.context().GetFromAndToForDiff() - return self.c.Helpers().Diff.PlainDiffBetweenRefs(from, to, paths) + return self.diffActions } func (self *CommitFilesController) copyDiffToClipboard(paths []string, toastMessage string) error { diff --git a/pkg/gui/controllers/reflog_commits_controller.go b/pkg/gui/controllers/reflog_commits_controller.go index 10b641d93..9cfb34f11 100644 --- a/pkg/gui/controllers/reflog_commits_controller.go +++ b/pkg/gui/controllers/reflog_commits_controller.go @@ -10,6 +10,9 @@ type ReflogCommitsController struct { baseController *ListControllerTrait[*models.Commit] c *ControllerCommon + + // what this panel offers on the diff it shows in the focused main view + diffActions *CommitDiffActions } var _ types.IController = &ReflogCommitsController{} @@ -17,7 +20,7 @@ var _ types.IController = &ReflogCommitsController{} func NewReflogCommitsController( c *ControllerCommon, ) *ReflogCommitsController { - return &ReflogCommitsController{ + controller := &ReflogCommitsController{ baseController: baseController{}, ListControllerTrait: NewListControllerTrait( c, @@ -27,6 +30,19 @@ func NewReflogCommitsController( ), c: c, } + controller.diffActions = NewCommitDiffActions(c, c.Contexts().ReflogCommits, controller.diffTarget) + return controller +} + +// diffTarget is the reflog entry the panel has selected, whose diff its main view +// shows. A reflog entry is never a commit of the checked-out branch as far as we are +// concerned, so nothing here may be rewritten. +func (self *ReflogCommitsController) diffTarget() *commitDiffTarget { + commit := self.context().GetSelected() + if commit == nil { + return nil + } + return &commitDiffTarget{from: commit.ParentRefName(), to: commit.RefName()} } func (self *ReflogCommitsController) Context() types.Context { @@ -38,17 +54,7 @@ func (self *ReflogCommitsController) context() *context.ReflogCommitsContext { } func (self *ReflogCommitsController) GetFocusedMainViewDiffSource() types.FocusedMainViewDiffSource { - return self -} - -// PlainDiff hands out the reflog entry's diff for the given files — the same diff its -// main view shows, only without the entry's message and stat above it. -func (self *ReflogCommitsController) PlainDiff(_ types.DiffPaneContext, paths []string) string { - commit := self.context().GetSelected() - if commit == nil { - return "" - } - return self.c.Helpers().Diff.PlainDiffBetweenRefs(commit.ParentRefName(), commit.RefName(), paths) + return self.diffActions } func (self *ReflogCommitsController) GetOnRenderToMain() func() { diff --git a/pkg/gui/controllers/switch_to_diff_files_controller.go b/pkg/gui/controllers/switch_to_diff_files_controller.go index e8dd886cb..30db57355 100644 --- a/pkg/gui/controllers/switch_to_diff_files_controller.go +++ b/pkg/gui/controllers/switch_to_diff_files_controller.go @@ -25,17 +25,48 @@ type SwitchToDiffFilesController struct { baseController c *ControllerCommon context CanSwitchToDiffFiles + + // what this panel offers on the diff it shows in the focused main view + diffActions *CommitDiffActions } func NewSwitchToDiffFilesController( c *ControllerCommon, context CanSwitchToDiffFiles, ) *SwitchToDiffFilesController { - return &SwitchToDiffFilesController{ + controller := &SwitchToDiffFilesController{ baseController: baseController{}, c: c, context: context, } + controller.diffActions = NewCommitDiffActions(c, context, controller.diffTarget) + return controller +} + +// diffTarget is the commit — or stash entry, or range of commits — the panel has +// selected, whose whole diff its main view shows. +func (self *SwitchToDiffFilesController) diffTarget() *commitDiffTarget { + ref := self.context.GetSelectedRef() + if ref == nil { + return nil + } + refRange := self.context.GetSelectedRefRangeForDiffFiles() + from, to := context.FromAndToForDiff(ref, refRange) + return &commitDiffTarget{from: from, to: to, canRebase: self.canRebase(ref, refRange)} +} + +// canRebase reports whether the given selection is one lazygit may rewrite: the panel +// has to allow it in the first place, a range of commits can't be rewritten as one, +// and in diffing mode what the main view shows is a diff against another ref rather +// than the commit itself, unless that other ref is the selected commit. +func (self *SwitchToDiffFilesController) canRebase(ref models.Ref, refRange *types.RefRange) bool { + if !self.context.CanRebase() { + return false + } + if self.c.Modes().Diffing.Active() { + return self.c.Modes().Diffing.Ref == ref.RefName() + } + return refRange == nil } func (self *SwitchToDiffFilesController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { @@ -66,19 +97,7 @@ func (self *SwitchToDiffFilesController) GetOnDoubleClick() func() error { } func (self *SwitchToDiffFilesController) GetFocusedMainViewDiffSource() types.FocusedMainViewDiffSource { - return self -} - -// PlainDiff hands out the diff of the panel's selected commit (or range of them) for -// the given files — the same diff its main view shows, only without the commit's -// message and stat above it. -func (self *SwitchToDiffFilesController) PlainDiff(_ types.DiffPaneContext, paths []string) string { - ref := self.context.GetSelectedRef() - if ref == nil { - return "" - } - from, to := context.FromAndToForDiff(ref, self.context.GetSelectedRefRangeForDiffFiles()) - return self.c.Helpers().Diff.PlainDiffBetweenRefs(from, to, paths) + return self.diffActions } func (self *SwitchToDiffFilesController) enter() error { @@ -86,16 +105,7 @@ func (self *SwitchToDiffFilesController) enter() error { refsRange := self.context.GetSelectedRefRangeForDiffFiles() commitFilesContext := self.c.Contexts().CommitFiles - canRebase := self.context.CanRebase() - if canRebase { - if self.c.Modes().Diffing.Active() { - if self.c.Modes().Diffing.Ref != ref.RefName() { - canRebase = false - } - } else if refsRange != nil { - canRebase = false - } - } + canRebase := self.canRebase(ref, refsRange) commitFilesContext.ClearFilter() commitFilesContext.ReInit(ref, refsRange)