1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-07-30 03:23:08 +03:00

Enforce single-item selection in various actions

We want to show an error when the user tries to invoke an action that expects only
a single item to be selected.

We're using the GetDisabledReason field to enforce this (as well as DisabledReason
on menu items).

I've created a ListControllerTrait to store some shared convenience functions for this.
This commit is contained in:
Jesse Duffield
2024-01-14 13:51:20 +11:00
parent 280b4d60f8
commit 51fb82d6bf
45 changed files with 854 additions and 757 deletions

View File

@ -17,48 +17,61 @@ import (
type BranchesController struct {
baseController
*ListControllerTrait[*models.Branch]
c *ControllerCommon
}
var _ types.IController = &BranchesController{}
func NewBranchesController(
common *ControllerCommon,
c *ControllerCommon,
) *BranchesController {
return &BranchesController{
baseController: baseController{},
c: common,
c: c,
ListControllerTrait: NewListControllerTrait[*models.Branch](
c,
c.Contexts().Branches,
c.Contexts().Branches.GetSelected,
),
}
}
func (self *BranchesController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
return []*types.Binding{
{
Key: opts.GetKey(opts.Config.Universal.Select),
Handler: self.checkSelected(self.press),
GetDisabledReason: self.getDisabledReasonForPress,
Description: self.c.Tr.Checkout,
Key: opts.GetKey(opts.Config.Universal.Select),
Handler: self.withItem(self.press),
GetDisabledReason: self.require(
self.singleItemSelected(),
self.notPulling,
),
Description: self.c.Tr.Checkout,
},
{
Key: opts.GetKey(opts.Config.Universal.New),
Handler: self.checkSelected(self.newBranch),
Description: self.c.Tr.NewBranch,
Key: opts.GetKey(opts.Config.Universal.New),
Handler: self.withItem(self.newBranch),
GetDisabledReason: self.require(self.singleItemSelected()),
Description: self.c.Tr.NewBranch,
},
{
Key: opts.GetKey(opts.Config.Branches.CreatePullRequest),
Handler: self.checkSelected(self.handleCreatePullRequest),
Description: self.c.Tr.CreatePullRequest,
Key: opts.GetKey(opts.Config.Branches.CreatePullRequest),
Handler: self.withItem(self.handleCreatePullRequest),
GetDisabledReason: self.require(self.singleItemSelected()),
Description: self.c.Tr.CreatePullRequest,
},
{
Key: opts.GetKey(opts.Config.Branches.ViewPullRequestOptions),
Handler: self.checkSelected(self.handleCreatePullRequestMenu),
Description: self.c.Tr.CreatePullRequestOptions,
OpensMenu: true,
Key: opts.GetKey(opts.Config.Branches.ViewPullRequestOptions),
Handler: self.withItem(self.handleCreatePullRequestMenu),
GetDisabledReason: self.require(self.singleItemSelected()),
Description: self.c.Tr.CreatePullRequestOptions,
OpensMenu: true,
},
{
Key: opts.GetKey(opts.Config.Branches.CopyPullRequestURL),
Handler: self.copyPullRequestURL,
Description: self.c.Tr.CopyPullRequestURL,
Key: opts.GetKey(opts.Config.Branches.CopyPullRequestURL),
Handler: self.copyPullRequestURL,
GetDisabledReason: self.require(self.singleItemSelected()),
Description: self.c.Tr.CopyPullRequestURL,
},
{
Key: opts.GetKey(opts.Config.Branches.CheckoutBranchByName),
@ -66,60 +79,69 @@ func (self *BranchesController) GetKeybindings(opts types.KeybindingsOpts) []*ty
Description: self.c.Tr.CheckoutByName,
},
{
Key: opts.GetKey(opts.Config.Branches.ForceCheckoutBranch),
Handler: self.forceCheckout,
Description: self.c.Tr.ForceCheckout,
Key: opts.GetKey(opts.Config.Branches.ForceCheckoutBranch),
Handler: self.forceCheckout,
GetDisabledReason: self.require(self.singleItemSelected()),
Description: self.c.Tr.ForceCheckout,
},
{
Key: opts.GetKey(opts.Config.Universal.Remove),
Handler: self.checkSelectedAndReal(self.delete),
Description: self.c.Tr.ViewDeleteOptions,
OpensMenu: true,
Key: opts.GetKey(opts.Config.Universal.Remove),
Handler: self.withItem(self.delete),
GetDisabledReason: self.require(self.singleItemSelected(self.branchIsReal)),
Description: self.c.Tr.ViewDeleteOptions,
OpensMenu: true,
},
{
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.RebaseBranch),
Handler: opts.Guards.OutsideFilterMode(self.rebase),
GetDisabledReason: self.require(
self.singleItemSelected(self.notRebasingOntoSelf),
),
Description: self.c.Tr.RebaseBranch,
},
{
Key: opts.GetKey(opts.Config.Branches.MergeIntoCurrentBranch),
Handler: opts.Guards.OutsideFilterMode(self.merge),
Description: self.c.Tr.MergeIntoCurrentBranch,
Key: opts.GetKey(opts.Config.Branches.MergeIntoCurrentBranch),
Handler: opts.Guards.OutsideFilterMode(self.merge),
GetDisabledReason: self.require(self.singleItemSelected()),
Description: self.c.Tr.MergeIntoCurrentBranch,
},
{
Key: opts.GetKey(opts.Config.Branches.FastForward),
Handler: self.checkSelectedAndReal(self.fastForward),
Description: self.c.Tr.FastForward,
Key: opts.GetKey(opts.Config.Branches.FastForward),
Handler: self.withItem(self.fastForward),
GetDisabledReason: self.require(self.singleItemSelected(self.branchIsReal)),
Description: self.c.Tr.FastForward,
},
{
Key: opts.GetKey(opts.Config.Branches.CreateTag),
Handler: self.checkSelected(self.createTag),
Description: self.c.Tr.CreateTag,
Key: opts.GetKey(opts.Config.Branches.CreateTag),
Handler: self.withItem(self.createTag),
GetDisabledReason: self.require(self.singleItemSelected()),
Description: self.c.Tr.CreateTag,
},
{
Key: opts.GetKey(opts.Config.Branches.SortOrder),
Handler: self.createSortMenu,
Description: self.c.Tr.SortOrder,
OpensMenu: true,
},
{
Key: opts.GetKey(opts.Config.Commits.ViewResetOptions),
Handler: self.checkSelected(self.createResetMenu),
Description: self.c.Tr.ViewResetOptions,
OpensMenu: true,
Key: opts.GetKey(opts.Config.Commits.ViewResetOptions),
Handler: self.withItem(self.createResetMenu),
GetDisabledReason: self.require(self.singleItemSelected()),
Description: self.c.Tr.ViewResetOptions,
OpensMenu: true,
},
{
Key: opts.GetKey(opts.Config.Branches.RenameBranch),
Handler: self.checkSelectedAndReal(self.rename),
Description: self.c.Tr.RenameBranch,
Key: opts.GetKey(opts.Config.Branches.RenameBranch),
Handler: self.withItem(self.rename),
GetDisabledReason: self.require(self.singleItemSelected(self.branchIsReal)),
Description: self.c.Tr.RenameBranch,
},
{
Key: opts.GetKey(opts.Config.Branches.SetUpstream),
Handler: self.checkSelected(self.viewUpstreamOptions),
Description: self.c.Tr.ViewBranchUpstreamOptions,
Tooltip: self.c.Tr.ViewBranchUpstreamOptionsTooltip,
OpensMenu: true,
Key: opts.GetKey(opts.Config.Branches.SetUpstream),
Handler: self.withItem(self.viewUpstreamOptions),
GetDisabledReason: self.require(self.singleItemSelected()),
Description: self.c.Tr.ViewBranchUpstreamOptions,
Tooltip: self.c.Tr.ViewBranchUpstreamOptionsTooltip,
OpensMenu: true,
},
}
}
@ -308,7 +330,7 @@ func (self *BranchesController) press(selectedBranch *models.Branch) error {
return self.c.Helpers().Refs.CheckoutRef(selectedBranch.Name, types.CheckoutRefOptions{})
}
func (self *BranchesController) getDisabledReasonForPress() *types.DisabledReason {
func (self *BranchesController) notPulling() *types.DisabledReason {
currentBranch := self.c.Helpers().Refs.GetCheckedOutRef()
if currentBranch != nil {
op := self.c.State().GetItemOperation(currentBranch)
@ -561,8 +583,8 @@ func (self *BranchesController) rebase() error {
return self.c.Helpers().MergeAndRebase.RebaseOntoRef(selectedBranchName)
}
func (self *BranchesController) getDisabledReasonForRebase() *types.DisabledReason {
selectedBranchName := self.context().GetSelected().Name
func (self *BranchesController) notRebasingOntoSelf(branch *models.Branch) *types.DisabledReason {
selectedBranchName := branch.Name
checkedOutBranch := self.c.Helpers().Refs.GetCheckedOutRef().Name
if selectedBranchName == checkedOutBranch {
return &types.DisabledReason{Text: self.c.Tr.CantRebaseOntoSelf}
@ -753,24 +775,10 @@ func (self *BranchesController) createPullRequest(from string, to string) error
return nil
}
func (self *BranchesController) checkSelected(callback func(*models.Branch) error) func() error {
return func() error {
selectedItem := self.context().GetSelected()
if selectedItem == nil {
return nil
}
return callback(selectedItem)
func (self *BranchesController) branchIsReal(branch *models.Branch) *types.DisabledReason {
if !branch.IsRealBranch() {
return &types.DisabledReason{Text: self.c.Tr.SelectedItemIsNotABranch}
}
}
func (self *BranchesController) checkSelectedAndReal(callback func(*models.Branch) error) func() error {
return func() error {
selectedItem := self.context().GetSelected()
if selectedItem == nil || !selectedItem.IsRealBranch() {
return nil
}
return callback(selectedItem)
}
return nil
}