diff options
author | Stefan Haller <stefan@haller-berlin.de> | 2023-09-18 10:26:11 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-09-18 10:26:11 +0200 |
commit | e2a966443bcc4c71d6c83f1bee90288c22c57ddb (patch) | |
tree | 059376c3745f60abd7548dca2c65687946c38742 | |
parent | 679148449a27486196e4d5aa7f1bf27d8853e9fe (diff) | |
parent | 0b13c3ca87313a94d5c7972db39b7e64fbea5d67 (diff) |
Add a DisabledReason mechanism for menu items and keybindings (#2992)
-rw-r--r-- | pkg/gui/context/menu_context.go | 7 | ||||
-rw-r--r-- | pkg/gui/controllers/branches_controller.go | 218 | ||||
-rw-r--r-- | pkg/gui/controllers/helpers/cherry_pick_helper.go | 4 | ||||
-rw-r--r-- | pkg/gui/controllers/helpers/confirmation_helper.go | 13 | ||||
-rw-r--r-- | pkg/gui/controllers/helpers/merge_and_rebase_helper.go | 3 | ||||
-rw-r--r-- | pkg/gui/controllers/local_commits_controller.go | 245 | ||||
-rw-r--r-- | pkg/gui/controllers/menu_controller.go | 2 | ||||
-rw-r--r-- | pkg/gui/controllers/options_menu_action.go | 13 | ||||
-rw-r--r-- | pkg/gui/gui_common.go | 4 | ||||
-rw-r--r-- | pkg/gui/keybindings.go | 16 | ||||
-rw-r--r-- | pkg/gui/types/common.go | 5 | ||||
-rw-r--r-- | pkg/gui/types/keybindings.go | 7 | ||||
-rw-r--r-- | pkg/i18n/english.go | 16 | ||||
-rw-r--r-- | pkg/integration/tests/branch/reset_to_upstream.go | 4 |
14 files changed, 346 insertions, 211 deletions
diff --git a/pkg/gui/context/menu_context.go b/pkg/gui/context/menu_context.go index 287ed92ec..f972f2fbb 100644 --- a/pkg/gui/context/menu_context.go +++ b/pkg/gui/context/menu_context.go @@ -90,6 +90,9 @@ func (self *MenuViewModel) GetDisplayStrings(_ int, _ int) [][]string { return lo.Map(menuItems, func(item *types.MenuItem, _ int) []string { displayStrings := item.LabelColumns + if item.DisabledReason != "" { + displayStrings[0] = style.FgDefault.SetStrikethrough().Sprint(displayStrings[0]) + } if !showKeys { return displayStrings @@ -169,6 +172,10 @@ func (self *MenuContext) GetKeybindings(opts types.KeybindingsOpts) []*types.Bin } func (self *MenuContext) OnMenuPress(selectedItem *types.MenuItem) error { + if selectedItem != nil && selectedItem.DisabledReason != "" { + return self.c.ErrorMsg(selectedItem.DisabledReason) + } + if err := self.c.PopContext(); err != nil { return err } diff --git a/pkg/gui/controllers/branches_controller.go b/pkg/gui/controllers/branches_controller.go index 1dc1f77e8..3c18b955c 100644 --- a/pkg/gui/controllers/branches_controller.go +++ b/pkg/gui/controllers/branches_controller.go @@ -12,6 +12,7 @@ import ( "github.com/jesseduffield/lazygit/pkg/gui/controllers/helpers" "github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/utils" + "github.com/samber/lo" ) type BranchesController struct { @@ -75,9 +76,10 @@ func (self *BranchesController) GetKeybindings(opts types.KeybindingsOpts) []*ty OpensMenu: true, }, { - Key: opts.GetKey(opts.Config.Branches.RebaseBranch), - Handler: opts.Guards.OutsideFilterMode(self.rebase), - Description: self.c.Tr.RebaseBranch, + Key: opts.GetKey(opts.Config.Branches.RebaseBranch), + Handler: opts.Guards.OutsideFilterMode(self.rebase), + Description: self.c.Tr.RebaseBranch, + GetDisabledReason: self.getDisabledReasonForRebase, }, { Key: opts.GetKey(opts.Config.Branches.MergeIntoCurrentBranch), @@ -140,32 +142,55 @@ func (self *BranchesController) GetOnRenderToMain() func() error { } func (self *BranchesController) setUpstream(selectedBranch *models.Branch) error { - options := []*types.MenuItem{ - { - LabelColumns: []string{self.c.Tr.ViewDivergenceFromUpstream}, - OnPress: func() error { - branch := self.context().GetSelected() - if branch == nil { - return nil - } + viewDivergenceItem := &types.MenuItem{ + LabelColumns: []string{self.c.Tr.ViewDivergenceFromUpstream}, + OnPress: func() error { + branch := self.context().GetSelected() + if branch == nil { + return nil + } - if !branch.RemoteBranchStoredLocally() { - return self.c.ErrorMsg(self.c.Tr.DivergenceNoUpstream) - } - return self.c.Helpers().SubCommits.ViewSubCommits(helpers.ViewSubCommitsOpts{ - Ref: branch, - TitleRef: fmt.Sprintf("%s <-> %s", branch.RefName(), branch.ShortUpstreamRefName()), - RefToShowDivergenceFrom: branch.FullUpstreamRefName(), - Context: self.context(), - ShowBranchHeads: false, - }) - }, - Key: 'v', + return self.c.Helpers().SubCommits.ViewSubCommits(helpers.ViewSubCommitsOpts{ + Ref: branch, + TitleRef: fmt.Sprintf("%s <-> %s", branch.RefName(), branch.ShortUpstreamRefName()), + RefToShowDivergenceFrom: branch.FullUpstreamRefName(), + Context: self.context(), + ShowBranchHeads: false, + }) }, - { - LabelColumns: []string{self.c.Tr.UnsetUpstream}, - OnPress: func() error { - if err := self.c.Git().Branch.UnsetUpstream(selectedBranch.Name); err != nil { + Key: 'v', + } + + unsetUpstreamItem := &types.MenuItem{ + LabelColumns: []string{self.c.Tr.UnsetUpstream}, + OnPress: func() error { + if err := self.c.Git().Branch.UnsetUpstream(selectedBranch.Name); err != nil { + return self.c.Error(err) + } + if err := self.c.Refresh(types.RefreshOptions{ + Mode: types.SYNC, + Scope: []types.RefreshableView{ + types.BRANCHES, + types.COMMITS, + }, + }); err != nil { + return self.c.Error(err) + } + return nil + }, + Key: 'u', + } + + setUpstreamItem := &types.MenuItem{ + LabelColumns: []string{self.c.Tr.SetUpstream}, + OnPress: func() error { + return self.c.Helpers().Upstream.PromptForUpstreamWithoutInitialContent(selectedBranch, func(upstream string) error { + upstreamRemote, upstreamBranch, err := self.c.Helpers().Upstream.ParseUpstream(upstream) + if err != nil { + return self.c.Error(err) + } + + if err := self.c.Git().Branch.SetUpstream(upstreamRemote, upstreamBranch, selectedBranch.Name); err != nil { return self.c.Error(err) } if err := self.c.Refresh(types.RefreshOptions{ @@ -178,75 +203,48 @@ func (self *BranchesController) setUpstream(selectedBranch *models.Branch) error return self.c.Error(err) } return nil - }, - Key: 'u', + }) }, - { - LabelColumns: []string{self.c.Tr.SetUpstream}, - OnPress: func() error { - return self.c.Helpers().Upstream.PromptForUpstreamWithoutInitialContent(selectedBranch, func(upstream string) error { - upstreamRemote, upstreamBranch, err := self.c.Helpers().Upstream.ParseUpstream(upstream) - if err != nil { - return self.c.Error(err) - } + Key: 's', + } - if err := self.c.Git().Branch.SetUpstream(upstreamRemote, upstreamBranch, selectedBranch.Name); err != nil { - return self.c.Error(err) - } - if err := self.c.Refresh(types.RefreshOptions{ - Mode: types.SYNC, - Scope: []types.RefreshableView{ - types.BRANCHES, - types.COMMITS, - }, - }); err != nil { - return self.c.Error(err) - } - return nil - }) - }, - Key: 's', + upstream := lo.Ternary(selectedBranch.RemoteBranchStoredLocally(), + fmt.Sprintf("%s/%s", selectedBranch.UpstreamRemote, selectedBranch.Name), + self.c.Tr.UpstreamGenericName) + upstreamResetOptions := utils.ResolvePlaceholderString( + self.c.Tr.ViewUpstreamResetOptions, + map[string]string{"upstream": upstream}, + ) + upstreamResetTooltip := utils.ResolvePlaceholderString( + self.c.Tr.ViewUpstreamResetOptionsTooltip, + map[string]string{"upstream": upstream}, + ) + + upstreamResetItem := &types.MenuItem{ + LabelColumns: []string{upstreamResetOptions}, + OpensMenu: true, + OnPress: func() error { + err := self.c.Helpers().Refs.CreateGitResetMenu(upstream) + if err != nil { + return self.c.Error(err) + } + return nil }, + Tooltip: upstreamResetTooltip, + Key: 'g', } - if selectedBranch.IsTrackingRemote() { - upstream := fmt.Sprintf("%s/%s", selectedBranch.UpstreamRemote, selectedBranch.Name) - upstreamResetOptions := utils.ResolvePlaceholderString( - self.c.Tr.ViewUpstreamResetOptions, - map[string]string{"upstream": upstream}, - ) - upstreamResetTooltip := utils.ResolvePlaceholderString( - self.c.Tr.ViewUpstreamResetOptionsTooltip, - map[string]string{"upstream": upstream}, - ) - - options = append(options, &types.MenuItem{ - LabelColumns: []string{upstreamResetOptions}, - OpensMenu: true, - OnPress: func() error { - if selectedBranch.RemoteBranchNotStoredLocally() { - return self.c.ErrorMsg(self.c.Tr.UpstreamNotStoredLocallyError) - } + if !selectedBranch.RemoteBranchStoredLocally() { + viewDivergenceItem.DisabledReason = self.c.Tr.UpstreamNotSetError + unsetUpstreamItem.DisabledReason = self.c.Tr.UpstreamNotSetError + upstreamResetItem.DisabledReason = self.c.Tr.UpstreamNotSetError + } - err := self.c.Helpers().Refs.CreateGitResetMenu(upstream) - if err != nil { - return self.c.Error(err) - } - return nil - }, - Tooltip: upstreamResetTooltip, - Key: 'g', - }) - } else { - options = append(options, &types.MenuItem{ - LabelColumns: []string{self.c.Tr.ViewUpstreamDisabledResetOptions}, - OpensMenu: true, - OnPress: func() error { - return self.c.ErrorMsg(self.c.Tr.UpstreamNotSetError) - }, - Tooltip: self.c.Tr.UpstreamNotSetError, - Key: 'g', - }) + options := []*types.MenuItem{ + viewDivergenceItem, + unsetUpstreamItem, + setUpstreamItem, + upstreamResetItem, } return self.c.Menu(types.CreateMenuOptions{ @@ -468,7 +466,6 @@ func (self *BranchesController) forceDelete(branch *models.Branch) error { } func (self *BranchesController) delete(branch *models.Branch) error { - menuItems := []*types.MenuItem{} checkedOutBranch := self.c.Helpers().Refs.GetCheckedOutRef() localDeleteItem := &types.MenuItem{ @@ -479,25 +476,18 @@ func (self *BranchesController) delete(branch *models.Branch) error { }, } if checkedOutBranch.Name == branch.Name { - localDeleteItem = &types.MenuItem{ - Label: self.c.Tr.DeleteLocalBranch, - Key: 'c', - Tooltip: self.c.Tr.CantDeleteCheckOutBranch, - OnPress: func() error { - return self.c.ErrorMsg(self.c.Tr.CantDeleteCheckOutBranch) - }, - } + localDeleteItem.DisabledReason = self.c.Tr.CantDeleteCheckOutBranch } - menuItems = append(menuItems, localDeleteItem) - if branch.IsTrackingRemote() && !branch.UpstreamGone { - menuItems = append(menuItems, &types.MenuItem{ - Label: self.c.Tr.DeleteRemoteBranch, - Key: 'r', - OnPress: func() error { - return self.remoteDelete(branch) - }, - }) + remoteDeleteItem := &types.MenuItem{ + Label: self.c.Tr.DeleteRemoteBranch, + Key: 'r', + OnPress: func() error { + return self.remoteDelete(branch) + }, + } + if !branch.IsTrackingRemote() || branch.UpstreamGone { + remoteDeleteItem.DisabledReason = self.c.Tr.UpstreamNotSetError } menuTitle := utils.ResolvePlaceholderString( @@ -509,7 +499,7 @@ func (self *BranchesController) delete(branch *models.Branch) error { return self.c.Menu(types.CreateMenuOptions{ Title: menuTitle, - Items: menuItems, + Items: []*types.MenuItem{localDeleteItem, remoteDeleteItem}, }) } @@ -523,6 +513,16 @@ func (self *BranchesController) rebase() error { return self.c.Helpers().MergeAndRebase.RebaseOntoRef(selectedBranchName) } +func (self *BranchesController) getDisabledReasonForRebase() string { + selectedBranchName := self.context().GetSelected().Name + checkedOutBranch := self.c.Helpers().Refs.GetCheckedOutRef().Name + if selectedBranchName == checkedOutBranch { + return self.c.Tr.CantRebaseOntoSelf + } + + return "" +} + func (self *BranchesController) fastForward(branch *models.Branch) error { if !branch.IsTrackingRemote() { return self.c.ErrorMsg(self.c.Tr.FwdNoUpstream) diff --git a/pkg/gui/controllers/helpers/cherry_pick_helper.go b/pkg/gui/controllers/helpers/cherry_pick_helper.go index 2e8a11f7d..e27e469b6 100644 --- a/pkg/gui/controllers/helpers/cherry_pick_helper.go +++ b/pkg/gui/controllers/helpers/cherry_pick_helper.go @@ -85,6 +85,10 @@ func (self *CherryPickHelper) Paste() error { }) } +func (self *CherryPickHelper) CanPaste() bool { + return self.getData().Active() +} + func (self *CherryPickHelper) Reset() error { self.getData().ContextKey = "" self.getData().CherryPickedCommits = nil diff --git a/pkg/gui/controllers/helpers/confirmation_helper.go b/pkg/gui/controllers/helpers/confirmation_helper.go index c920df39f..7d4ca1464 100644 --- a/pkg/gui/controllers/helpers/confirmation_helper.go +++ b/pkg/gui/controllers/helpers/confirmation_helper.go @@ -333,7 +333,7 @@ func (self *ConfirmationHelper) resizeMenu() { tooltip := "" selectedItem := self.c.Contexts().Menu.GetSelected() if selectedItem != nil { - tooltip = selectedItem.Tooltip + tooltip = self.TooltipForMenuItem(selectedItem) } tooltipHeight := getMessageHeight(true, tooltip, panelWidth) + 2 // plus 2 for the frame _, _ = self.c.GocuiGui().SetView(self.c.Views().Tooltip.Name(), x0, tooltipTop, x1, tooltipTop+tooltipHeight-1, 0) @@ -382,3 +382,14 @@ func (self *ConfirmationHelper) IsPopupPanel(viewName string) bool { func (self *ConfirmationHelper) IsPopupPanelFocused() bool { return self.IsPopupPanel(self.c.CurrentContext().GetViewName()) } + +func (self *ConfirmationHelper) TooltipForMenuItem(menuItem *types.MenuItem) string { + tooltip := menuItem.Tooltip + if menuItem.DisabledReason != "" { + if tooltip != "" { + tooltip += "\n\n" + } + tooltip += style.FgRed.Sprintf(self.c.Tr.DisabledMenuItemPrefix) + menuItem.DisabledReason + } + return tooltip +} diff --git a/pkg/gui/controllers/helpers/merge_and_rebase_helper.go b/pkg/gui/controllers/helpers/merge_and_rebase_helper.go index a88615271..de34f2193 100644 --- a/pkg/gui/controllers/helpers/merge_and_rebase_helper.go +++ b/pkg/gui/controllers/helpers/merge_and_rebase_helper.go @@ -220,9 +220,6 @@ func (self *MergeAndRebaseHelper) PromptToContinueRebase() error { func (self *MergeAndRebaseHelper) RebaseOntoRef(ref string) error { checkedOutBranch := self.refsHelper.GetCheckedOutRef().Name - if ref == checkedOutBranch { - return self.c.ErrorMsg(self.c.Tr.CantRebaseOntoSelf) - } menuItems := []*types.MenuItem{ { Label: self.c.Tr.SimpleRebase, diff --git a/pkg/gui/controllers/local_commits_controller.go b/pkg/gui/controllers/local_commits_controller.go index 1bd079f53..2c4794195 100644 --- a/pkg/gui/controllers/local_commits_controller.go +++ b/pkg/gui/controllers/local_commits_controller.go @@ -44,70 +44,83 @@ func NewLocalCommitsController( func (self *LocalCommitsController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { outsideFilterModeBindings := []*types.Binding{ { - Key: opts.GetKey(opts.Config.Commits.SquashDown), - Handler: self.checkSelected(self.squashDown), - Description: self.c.Tr.SquashDown, + Key: opts.GetKey(opts.Config.Commits.SquashDown), + Handler: self.checkSelected(self.squashDown), + GetDisabledReason: self.callGetDisabledReasonFuncWithSelectedCommit(self.getDisabledReasonForSquashDown), + Description: self.c.Tr.SquashDown, }, { - Key: opts.GetKey(opts.Config.Commits.MarkCommitAsFixup), - Handler: self.checkSelected(self.fixup), - Description: self.c.Tr.FixupCommit, + Key: opts.GetKey(opts.Config.Commits.MarkCommitAsFixup), + Handler: self.checkSelected(self.fixup), + GetDisabledReason: self.callGetDisabledReasonFuncWithSelectedCommit(self.getDisabledReasonForFixup), + Description: self.c.Tr.FixupCommit, }, { - Key: opts.GetKey(opts.Config.Commits.RenameCommit), - Handler: self.checkSelected(self.reword), - Description: self.c.Tr.RewordCommit, + Key: opts.GetKey(opts.Config.Commits.RenameCommit), + Handler: self.checkSelected(self.reword), + GetDisabledReason: self.getDisabledReasonForRebaseCommandWithSelectedCommit(todo.Reword), + Description: self.c.Tr.RewordCommit, }, { - Key: opts.GetKey(opts.Config.Commits.RenameCommitWithEditor), - Handler: self.checkSelected(self.rewordEditor), - Description: self.c.Tr.RenameCommitEditor, + Key: opts.GetKey(opts.Config.Commits.RenameCommitWithEditor), + Handler: self.checkSelected(self.rewordEditor), + GetDisabledReason: self.getDisabledReasonForRebaseCommandWithSelectedCommit(todo.Reword), + Description: self.c.Tr.RenameCommitEditor, }, { - Key: opts.GetKey(opts.Config.Universal.Remove), - Handler: self.checkSelected(self.drop), - Description: self.c.Tr.DeleteCommit, + Key: opts.GetKey(opts.Config.Universal.Remove), + Handler: self.checkSelected(self.drop), + GetDisabledReason: self.getDisabledReasonForRebaseCommandWithSelectedCommit(todo.Drop), + Description: self.c.Tr.DeleteCommit, }, { - Key: opts.GetKey(opts.Config.Universal.Edit), - Handler: self.checkSelected(self.edit), - Description: self.c.Tr.EditCommit, + Key: opts.GetKey(opts.Config.Universal.Edit), + Handler: self.checkSelected(self.edit), + GetDisabledReason: self.getDisabledReasonForRebaseCommandWithSelectedCommit(todo.Edit), + Description: self.c.Tr.EditCommit, }, { - Key: opts.GetKey(opts.Config.Commits.PickCommit), - Handler: self.checkSelected(self.pick), - Description: self.c.Tr.PickCommit, + Key: opts.GetKey(opts.Config.Commits.PickCommit), + Handler: self.checkSelected(self.pick), + GetDisabledReason: self.getDisabledReasonForRebaseCommandWithSelectedCommit(todo.Pick), + Description: self.c.Tr.PickCommit, }, { - Key: opts.GetKey(opts.Config.Commits.CreateFixupCommit), - Handler: self.checkSelected(self.createFixupCommit), - Description: self.c.Tr.CreateFixupCommitDescription, + Key: opts.GetKey(opts.Config.Commits.CreateFixupCommit), + Handler: self.checkSelected(self.createFixupCommit), + GetDisabledReason: self.disabledIfNoSelectedCommit(), + Description: self.c.Tr.CreateFixupCommitDescription, }, { - Key: opts.GetKey(opts.Config.Commits.SquashAboveCommits), - Handler: self.checkSelected(self.squashAllAboveFixupCommits), - Description: self.c.Tr.SquashAboveCommits, + Key: opts.GetKey(opts.Config.Commits.SquashAboveCommits), + Handler: self.checkSelected(self.squashAllAboveFixupCommits), + GetDisabledReason: self.callGetDisabledReasonFuncWithSelectedCommit(self.getDisabledReasonForSquashAllAboveFixupCommits), + Description: self.c.Tr.SquashAboveCommits, }, { - Key: opts.GetKey(opts.Config.Commits.MoveDownCommit), - Handler: self.checkSelected(self.moveDown), - Description: self.c.Tr.MoveDownCommit, + Key: opts.GetKey(opts.Config.Commits.MoveDownCommit), + Handler: self.checkSelected(self.moveDown), + GetDisabledReason: self.disabledIfNoSelectedCommit(), + Description: self.c.Tr.MoveDownCommit, }, { - Key: opts.GetKey(opts.Config.Commits.MoveUpCommit), - Handler: self.checkSelected(self.moveUp), - Description: self.c.Tr.MoveUpCommit, + Key: opts.GetKey(opts.Config.Commits.MoveUpCommit), + Handler: self.checkSelected(self.moveUp), + GetDisabledReason: self.disabledIfNoSelectedCommit(), + Description: self.c.Tr.MoveUpCommit, }, { - Key: opts.GetKey(opts.Config.Commits.PasteCommits), - Handler: self.paste, - Description: self.c.Tr.PasteCommits, + Key: opts.GetKey(opts.Config.Commits.PasteCommits), + Handler: self.paste, + GetDisabledReason: self.getDisabledReasonForPaste, + Description: self.c.Tr.PasteCommits, }, { - Key: opts.GetKey(opts.Config.Commits.MarkCommitAsBaseForRebase), - Handler: self.checkSelected(self.markAsBaseCommit), - Description: self.c.Tr.MarkAsBaseCommit, - Tooltip: self.c.Tr.MarkAsBaseCommitTooltip, + Key: opts.GetKey(opts.Config.Commits.MarkCommitAsBaseForRebase), + Handler: self.checkSelected(self.markAsBaseCommit), + GetDisabledReason: self.disabledIfNoSelectedCommit(), + Description: self.c.Tr.MarkAsBaseCommit, + Tooltip: self.c.Tr.MarkAsBaseCommitTooltip, }, // overriding these navigation keybindings because we might need to load // more commits on demand @@ -131,25 +144,29 @@ func (self *LocalCommitsController) GetKeybindings(opts types.KeybindingsOpts) [ bindings := append(outsideFilterModeBindings, []*types.Binding{ { - Key: opts.GetKey(opts.Config.Commits.AmendToCommit), - Handler: self.checkSelected(self.amendTo), - Description: self.c.Tr.AmendToCommit, + Key: opts.GetKey(opts.Config.Commits.AmendToCommit), + Handler: self.checkSelected(self.amendTo), + GetDisabledReason: self.callGetDisabledReasonFuncWithSelectedCommit(self.getDisabledReasonForAmendTo), + Description: self.c.Tr.AmendToCommit, }, { - Key: opts.GetKey(opts.Config.Commits.ResetCommitAuthor), - Handler: self.checkSelected(self.amendAttribute), - Description: self.c.Tr.SetResetCommitAuthor, - OpensMenu: true, + Key: opts.GetKey(opts.Config.Commits.ResetCommitAuthor), + Handler: self.checkSelected(self.amendAttribute), + GetDisabledReason: self.callGetDisabledReasonFuncWithSelectedCommit(self.getDisabledReasonForAmendTo), + Description: self.c.Tr.SetResetCommitAuthor, + OpensMenu: true, }, { - Key: opts.GetKey(opts.Config.Commits.RevertCommit), - Handler: self.checkSelected(self.revert), - Description: self.c.Tr.RevertCommit, + Key: opts.GetKey(opts.Config.Commits.RevertCommit), + Handler: self.checkSelected(self.revert), + GetDisabledReason: self.disabledIfNoSelectedCommit(), + Description: self.c.Tr.RevertCommit, }, { - Key: opts.GetKey(opts.Config.Commits.CreateTag), - Handler: self.checkSelected(self.createTag), - Description: self.c.Tr.TagCommit, + Key: opts.GetKey(opts.Config.Commits.CreateTag), + Handler: self.checkSelected(self.createTag), + GetDisabledReason: self.disabledIfNoSelectedCommit(), + Description: self.c.Tr.TagCommit, }, { Key: opts.GetKey(opts.Config.Commits.OpenLogMenu), @@ -208,10 +225,6 @@ func secondaryPatchPanelUpdateOpts(c *ControllerCommon) *types.ViewUpdateOpts { } func (self *LocalCommitsController) squashDown(commit *models.Commit) error { - if self.context().GetSelectedLineIdx() >= len(self.c.Model().Commits)-1 { - return self.c.ErrorMsg(self.c.Tr.CannotSquashOrFixupFirstCommit) - } - applied, err := self.handleMidRebaseCommand(todo.Squash, commit) if err != nil { return err @@ -232,11 +245,15 @@ func (self *LocalCommitsController) squashDown(commit *models.Commit) error { }) } -func (self *LocalCommitsController) fixup(commit *models.Commit) error { +func (self *LocalCommitsController) getDisabledReasonForSquashDown(commit *models.Commit) string { if self.context().GetSelectedLineIdx() >= len(self.c.Model().Commits)-1 { - return self.c.ErrorMsg(self.c.Tr.CannotSquashOrFixupFirstCommit) + return self.c.Tr.CannotSquashOrFixupFirstCommit } + return self.rebaseCommandEnabled(todo.Squash, commit) +} + +func (self *LocalCommitsController) fixup(commit *models.Commit) error { applied, err := self.handleMidRebaseCommand(todo.Fixup, commit) if err != nil { return err @@ -257,6 +274,14 @@ func (self *LocalCommitsController) fixup(commit *models.Commit) error { }) } +func (self *LocalCommitsController) getDisabledReasonForFixup(commit *models.Commit) string { + if self.context().GetSelectedLineIdx() >= len(self.c.Model().Commits)-1 { + return self.c.Tr.CannotSquashOrFixupFirstCommit + } + + return self.rebaseCommandEnabled(todo.Squash, commit) +} + func (self *LocalCommitsController) reword(commit *models.Commit) error { applied, err := self.handleMidRebaseCommand(todo.Reword, commit) if err != nil { @@ -428,8 +453,33 @@ func (self *LocalCommitsController) interactiveRebase(action todo.TodoCommand) e // commit meaning you are trying to edit the todo file rather than actually // begin a rebase. It then updates the todo file with that action func (self *LocalCommitsController) handleMidRebaseCommand(action todo.TodoCommand, commit *models.Commit) (bool, error) { + if !commit.IsTODO() { + return false, nil + } + + self.c.LogAction("Update rebase TODO") + + msg := utils.ResolvePlaceholderString( + self.c.Tr.Log.HandleMidRebaseCommand, + map[string]string{ + "shortSha": commit.ShortSha(), + "action": action.String(), + }, + ) + self.c.LogCommand(msg, false) + + if err := self.c.Git().Rebase.EditRebaseTodo(commit, action); err != nil { + return false, self.c.Error(err) + } + + return true, self.c.Refresh(types.RefreshOptions{ + Mode: types.SYNC, Scope: []types.RefreshableView{types.REBASE_COMMITS}, + }) +} + +func (self *LocalCommitsController) rebaseCommandEnabled(action todo.TodoCommand, commit *models.Commit) string { if commit.Action == models.ActionConflict { - return true, self.c.ErrorMsg(self.c.Tr.ChangingThisActionIsNotAllowed) + return self.c.Tr.ChangingThisActionIsNotAllowed } if !commit.IsTODO() { @@ -437,11 +487,11 @@ func (self *LocalCommitsController) handleMidRebaseCommand(action todo.TodoComma // If we are in a rebase, the only action that is allowed for // non-todo commits is rewording the current head commit if !(action == todo.Reword && self.isHeadCommit()) { - return true, self.c.ErrorMsg(self.c.Tr.AlreadyRebasing) + return self.c.Tr.AlreadyRebasing } } - return false, nil + return "" } // for now we do not support setting 'reword' because it requires an editor @@ -449,31 +499,14 @@ func (self *LocalCommitsController) handleMidRebaseCommand(action todo.TodoComma // our input or we set a lazygit client as the EDITOR env variable and have it // request us to edit the commit message when prompted. if action == todo.Reword { - return true, self.c.ErrorMsg(self.c.Tr.RewordNotSupported) + return self.c.Tr.RewordNotSupported } if allowed := isChangeOfRebaseTodoAllowed(action); !allowed { - return true, self.c.ErrorMsg(self.c.Tr.ChangingThisActionIsNotAllowed) + return self.c.Tr.ChangingThisActionIsNotAllowed } - self.c.LogAction("Update rebase TODO") - - msg := utils.ResolvePlaceholderString( - self.c.Tr.Log.HandleMidRebaseCommand, - map[string]string{ - "shortSha": commit.ShortSha(), - "action": action.String(), - }, - ) - self.c.LogCommand(msg, false) - - if err := self.c.Git().Rebase.EditRebaseTodo(commit, action); err != nil { - return false, self.c.Error(err) - } - - return true, self.c.Refresh(types.RefreshOptions{ - Mode: types.SYNC, Scope: []types.RefreshableView{types.REBASE_COMMITS}, - }) + return "" } func (self *LocalCommitsController) moveDown(commit *models.Commit) error { @@ -601,6 +634,14 @@ func (self *LocalCommitsController) amendTo(commit *models.Commit) error { }) } +func (self *LocalCommitsController) getDisabledReasonForAmendTo(commit *models.Commit) string { + if !self.isHeadCommit() && self.c.Git().Status.WorkingTreeState() != enums.REBASE_MODE_NONE { + return self.c.Tr.AlreadyRebasing + } + + return "" +} + func (self *LocalCommitsController) amendAttribute(commit *models.Commit) error { if self.c.Git().Status.WorkingTreeState() != enums.REBASE_MODE_NONE && !self.isHeadCommit() { return self.c.ErrorMsg(self.c.Tr.AlreadyRebasing) @@ -772,6 +813,14 @@ func (self *LocalCommitsController) squashAllAboveFixupCommits(commit *models.Co }) } +func (self *LocalCommitsController) getDisabledReasonForSquashAllAboveFixupCommits(commit *models.Commit) string { + if self.c.Git().Status.WorkingTreeState() != enums.REBASE_MODE_NONE { + return self.c.Tr.AlreadyRebasing + } + + return "" +} + func (self *LocalCommitsController) createTag(commit *models.Commit) error { return self.c.Helpers().Tags.OpenCreateTagPrompt(commit.Sha, func() {}) } @@ -896,13 +945,35 @@ func (self *LocalCommitsController) checkSelected(callback func(*models.Commit) return func() error { commit := self.context().GetSelected() if commit == nil { - return nil + // The enabled callback should have checked for this + panic("no commit selected") + } + + return callback(commit) + } +} + +func (self *LocalCommitsController) callGetDisabledReasonFuncWithSelectedCommit(callback func(*models.Commit) string) func() string { + return func() string { + commit := self.context().GetSelected() + if commit == nil { + return self.c.Tr.NoCommitSelected } return callback(commit) } } +func (self *LocalCommitsController) disabledIfNoSelectedCommit() func() string { + return self.callGetDisabledReasonFuncWithSelectedCommit(func(*models.Commit) string { return "" }) +} + +func (self *LocalCommitsController) getDisabledReasonForRebaseCommandWithSelectedCommit(action todo.TodoCommand) func() string { + return self.callGetDisabledReasonFuncWithSelectedCommit(func(commit *models.Commit) string { + return self.rebaseCommandEnabled(action, commit) + }) +} + func (self *LocalCommitsController) GetOnFocus() func(types.OnFocusOpts) error { return func(types.OnFocusOpts) error { context := self.context() @@ -931,6 +1002,14 @@ func (self *LocalCommitsController) paste() error { return self.c.Helpers().CherryPick.Paste() } +func (self *LocalCommitsController) getDisabledReasonForPaste() string { + if !self.c.Helpers().CherryPick.CanPaste() { + return self.c.Tr.NoCopiedCommits + } + + return "" +} + func (self *LocalCommitsController) markAsBaseCommit(commit *models.Commit) error { if commit.Sha == self.c.Modes().MarkedBaseCommit.GetSha() { // Reset when invoking it again on the marked commit diff --git a/pkg/gui/controllers/menu_controller.go b/pkg/gui/controllers/menu_controller.go index 108bd9cf7..0af32ef71 100644 --- a/pkg/gui/controllers/menu_controller.go +++ b/pkg/gui/controllers/menu_controller.go @@ -54,7 +54,7 @@ func (self *MenuController) GetOnFocus() func(types.OnFocusOpts) error { return func(types.OnFocusOpts) error { selectedMenuItem := self.context().GetSelected() if selectedMenuItem != nil { - self.c.Views().Tooltip.SetContent(selectedMenuItem.Tooltip) + self.c.Views().Tooltip.SetContent(self.c.Helpers().Confirmation.TooltipForMenuItem(selectedMenuItem)) } return nil } diff --git a/pkg/gui/controllers/options_menu_action.go b/pkg/gui/controllers/options_menu_action.go index 341fe8fb3..27a2915b8 100644 --- a/pkg/gui/controllers/options_menu_action.go +++ b/pkg/gui/controllers/options_menu_action.go @@ -25,6 +25,10 @@ func (self *OptionsMenuAction) Call() error { appendBindings := func(bindings []*types.Binding, section *types.MenuSection) { |