diff --git a/pkg/gui/context.go b/pkg/gui/context.go index 1adfec35c..ecab07c76 100644 --- a/pkg/gui/context.go +++ b/pkg/gui/context.go @@ -373,3 +373,15 @@ func (self *ContextMgr) NextInStack(c types.Context) types.Context { panic("context not in stack") } + +// IsInStack reports whether the given context is somewhere in the context stack. +// Unlike NextInStack it never panics, so it's safe to call before dereferencing +// the stack from a caller that might run with the context off-stack. +func (self *ContextMgr) IsInStack(c types.Context) bool { + self.RLock() + defer self.RUnlock() + + return lo.ContainsBy(self.ContextStack, func(other types.Context) bool { + return other.GetKey() == c.GetKey() + }) +} diff --git a/pkg/gui/controllers/main_view_controller.go b/pkg/gui/controllers/main_view_controller.go index 8d44aa3f9..9a37b0265 100644 --- a/pkg/gui/controllers/main_view_controller.go +++ b/pkg/gui/controllers/main_view_controller.go @@ -45,6 +45,11 @@ func (self *MainViewController) GetKeybindings(opts types.KeybindingsOpts) []*ty // line in the branch's pull request). selectionShown := self.context.GetView().Highlight + // The commit and find-fixup-base commands act on the working tree, so — like the + // old staging panel, which was always the working-tree diff — we only surface them + // while the focused main view plays that role (see workingTreeAction). + stagingMode := self.diffMainViewType() == types.DiffMainViewTypeStaging + var enterDescription string var editDescription string var editTooltip string @@ -59,6 +64,21 @@ func (self *MainViewController) GetKeybindings(opts types.KeybindingsOpts) []*ty openPullRequestTooltip = "Open a browser at the selected line in the diff of the current branch's pull request, so that you can comment on it. Only works for local branches that have a pull request on GitHub." } + var commitDescription string + var commitTooltip string + var commitWithoutHookDescription string + var commitWithEditorDescription string + var findBaseCommitDescription string + var findBaseCommitTooltip string + if stagingMode { + commitDescription = self.c.Tr.Commit + commitTooltip = self.c.Tr.CommitTooltip + commitWithoutHookDescription = self.c.Tr.CommitChangesWithoutHook + commitWithEditorDescription = self.c.Tr.CommitChangesWithEditor + findBaseCommitDescription = self.c.Tr.FindBaseCommitForFixup + findBaseCommitTooltip = self.c.Tr.FindBaseCommitForFixupTooltip + } + return []*types.Binding{ { Keys: opts.GetKeys(opts.Config.Universal.TogglePanel), @@ -112,6 +132,28 @@ func (self *MainViewController) GetKeybindings(opts types.KeybindingsOpts) []*ty Description: openPullRequestDescription, Tooltip: openPullRequestTooltip, }, + { + Keys: opts.GetKeys(opts.Config.Files.CommitChanges), + Handler: self.workingTreeAction(self.c.Helpers().WorkingTree.HandleCommitPress), + Description: commitDescription, + Tooltip: commitTooltip, + }, + { + Keys: opts.GetKeys(opts.Config.Files.CommitChangesWithoutHook), + Handler: self.workingTreeAction(self.c.Helpers().WorkingTree.HandleWIPCommitPress), + Description: commitWithoutHookDescription, + }, + { + Keys: opts.GetKeys(opts.Config.Files.CommitChangesWithEditor), + Handler: self.workingTreeAction(self.c.Helpers().WorkingTree.HandleCommitEditorPress), + Description: commitWithEditorDescription, + }, + { + Keys: opts.GetKeys(opts.Config.Files.FindBaseCommitForFixup), + Handler: self.workingTreeAction(self.c.Helpers().FixupHelper.HandleFindBaseCommitForFixupPress), + Description: findBaseCommitDescription, + Tooltip: findBaseCommitTooltip, + }, { Keys: opts.GetKeys(opts.Config.Main.ToggleSelectHunk), Handler: self.toggleSelectHunk, @@ -383,6 +425,37 @@ func (self *MainViewController) focusedMainViewActions() types.FocusedMainViewAc return sidePanelContext.GetFocusedMainViewActions() } +// diffMainViewType reports what the side panel beneath the focused main view makes its +// primary action mean — staging (the working tree), patch-building, or nothing — or +// DiffMainViewTypeNone when this pane isn't focused or has no diff panel beneath it. The +// IsInStack guard is essential: NextInStack panics for a context that isn't in the stack, +// and GetKeybindings (which calls this) runs for off-stack panes too — at startup and +// during cheatsheet generation, where the stack is empty. +func (self *MainViewController) diffMainViewType() types.DiffMainViewType { + if !self.c.Context().IsInStack(self.context) { + return types.DiffMainViewTypeNone + } + if diffContext, ok := self.c.Context().NextInStack(self.context).(types.DiffMainViewContext); ok { + return diffContext.GetDiffMainViewType() + } + return types.DiffMainViewTypeNone +} + +// workingTreeAction wraps a working-tree command (commit, find-fixup-base) so it runs +// only while the focused main view shows the working-tree diff — the role the staging +// panel used to play, and the only place these commands mean anything. Over any other +// diff (a commit, a stash) the key is a no-op, so committing can't be triggered by +// accident while browsing history. Re-checks on each press rather than capturing the +// state at keybinding-registration time. +func (self *MainViewController) workingTreeAction(action func() error) func() error { + return func() error { + if self.diffMainViewType() != types.DiffMainViewTypeStaging { + return nil + } + return action() + } +} + // primaryAction acts on the selected diff line(s) — a single line, a range, or a hunk — // delegating to the side panel beneath the focused main view, since what the action means // is the panel's business: the working tree stages, while commits toggle the selection diff --git a/pkg/gui/types/context.go b/pkg/gui/types/context.go index 82ef1ce9d..f965d6e9f 100644 --- a/pkg/gui/types/context.go +++ b/pkg/gui/types/context.go @@ -441,6 +441,7 @@ type IContextMgr interface { CurrentSide() Context CurrentPopup() []Context NextInStack(context Context) Context + IsInStack(c Context) bool IsCurrent(c Context) bool IsCurrentOrParent(c Context) bool ForEach(func(Context))