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

@ -13,25 +13,32 @@ import (
type FilesController struct {
baseController // nolint: unused
c *ControllerCommon
*ListControllerTrait[*filetree.FileNode]
c *ControllerCommon
}
var _ types.IController = &FilesController{}
func NewFilesController(
common *ControllerCommon,
c *ControllerCommon,
) *FilesController {
return &FilesController{
c: common,
c: c,
ListControllerTrait: NewListControllerTrait[*filetree.FileNode](
c,
c.Contexts().Files,
c.Contexts().Files.GetSelected,
),
}
}
func (self *FilesController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
return []*types.Binding{
{
Key: opts.GetKey(opts.Config.Universal.Select),
Handler: self.checkSelectedFileNode(self.press),
Description: self.c.Tr.ToggleStaged,
Key: opts.GetKey(opts.Config.Universal.Select),
Handler: self.withItem(self.press),
GetDisabledReason: self.require(self.singleItemSelected()),
Description: self.c.Tr.ToggleStaged,
},
{
Key: opts.GetKey(opts.Config.Files.OpenStatusFilter),
@ -71,20 +78,23 @@ func (self *FilesController) GetKeybindings(opts types.KeybindingsOpts) []*types
Tooltip: self.c.Tr.FindBaseCommitForFixupTooltip,
},
{
Key: opts.GetKey(opts.Config.Universal.Edit),
Handler: self.checkSelectedFileNode(self.edit),
Description: self.c.Tr.EditFile,
Key: opts.GetKey(opts.Config.Universal.Edit),
Handler: self.withItem(self.edit),
GetDisabledReason: self.require(self.singleItemSelected()),
Description: self.c.Tr.EditFile,
},
{
Key: opts.GetKey(opts.Config.Universal.OpenFile),
Handler: self.Open,
Description: self.c.Tr.OpenFile,
Key: opts.GetKey(opts.Config.Universal.OpenFile),
Handler: self.Open,
GetDisabledReason: self.require(self.singleItemSelected()),
Description: self.c.Tr.OpenFile,
},
{
Key: opts.GetKey(opts.Config.Files.IgnoreFile),
Handler: self.checkSelectedFileNode(self.ignoreOrExcludeMenu),
Description: self.c.Tr.Actions.IgnoreExcludeFile,
OpensMenu: true,
Key: opts.GetKey(opts.Config.Files.IgnoreFile),
Handler: self.withItem(self.ignoreOrExcludeMenu),
GetDisabledReason: self.require(self.singleItemSelected()),
Description: self.c.Tr.Actions.IgnoreExcludeFile,
OpensMenu: true,
},
{
Key: opts.GetKey(opts.Config.Files.RefreshFiles),
@ -108,9 +118,10 @@ func (self *FilesController) GetKeybindings(opts types.KeybindingsOpts) []*types
Description: self.c.Tr.ToggleStagedAll,
},
{
Key: opts.GetKey(opts.Config.Universal.GoInto),
Handler: self.enter,
Description: self.c.Tr.FileEnter,
Key: opts.GetKey(opts.Config.Universal.GoInto),
Handler: self.enter,
GetDisabledReason: self.require(self.singleItemSelected()),
Description: self.c.Tr.FileEnter,
},
{
Key: opts.GetKey(opts.Config.Commits.ViewResetOptions),
@ -130,9 +141,10 @@ func (self *FilesController) GetKeybindings(opts types.KeybindingsOpts) []*types
Description: self.c.Tr.ToggleTreeView,
},
{
Key: opts.GetKey(opts.Config.Universal.OpenDiffTool),
Handler: self.checkSelectedFileNode(self.openDiffTool),
Description: self.c.Tr.OpenDiffTool,
Key: opts.GetKey(opts.Config.Universal.OpenDiffTool),
Handler: self.withItem(self.openDiffTool),
GetDisabledReason: self.require(self.singleItemSelected()),
Description: self.c.Tr.OpenDiffTool,
},
{
Key: opts.GetKey(opts.Config.Files.OpenMergeTool),
@ -254,7 +266,7 @@ func (self *FilesController) GetOnRenderToMain() func() error {
}
func (self *FilesController) GetOnClick() func() error {
return self.checkSelectedFileNode(self.press)
return self.withItemGraceful(self.press)
}
// if we are dealing with a status for which there is no key in this map,
@ -411,17 +423,6 @@ func (self *FilesController) press(node *filetree.FileNode) error {
return self.context().HandleFocus(types.OnFocusOpts{})
}
func (self *FilesController) checkSelectedFileNode(callback func(*filetree.FileNode) error) func() error {
return func() error {
node := self.context().GetSelected()
if node == nil {
return nil
}
return callback(node)
}
}
func (self *FilesController) Context() types.Context {
return self.context()
}
@ -798,7 +799,8 @@ func (self *FilesController) openCopyMenu() error {
self.c.Toast(self.c.Tr.FileNameCopiedToast)
return nil
},
Key: 'n',
DisabledReason: self.require(self.singleItemSelected())(),
Key: 'n',
}
copyPathItem := &types.MenuItem{
Label: self.c.Tr.CopyFilePath,
@ -809,7 +811,8 @@ func (self *FilesController) openCopyMenu() error {
self.c.Toast(self.c.Tr.FilePathCopiedToast)
return nil
},
Key: 'p',
DisabledReason: self.require(self.singleItemSelected())(),
Key: 'p',
}
copyFileDiffItem := &types.MenuItem{
Label: self.c.Tr.CopySelectedDiff,
@ -827,6 +830,14 @@ func (self *FilesController) openCopyMenu() error {
self.c.Toast(self.c.Tr.FileDiffCopiedToast)
return nil
},
DisabledReason: self.require(self.singleItemSelected(
func(file *filetree.FileNode) *types.DisabledReason {
if !node.GetHasStagedOrTrackedChanges() {
return &types.DisabledReason{Text: self.c.Tr.NoContentToCopyError}
}
return nil
},
))(),
Key: 's',
}
copyAllDiff := &types.MenuItem{
@ -844,21 +855,17 @@ func (self *FilesController) openCopyMenu() error {
self.c.Toast(self.c.Tr.AllFilesDiffCopiedToast)
return nil
},
DisabledReason: self.require(
func() *types.DisabledReason {
if !self.anyStagedOrTrackedFile() {
return &types.DisabledReason{Text: self.c.Tr.NoContentToCopyError}
}
return nil
},
)(),
Key: 'a',
}
if node == nil {
copyNameItem.DisabledReason = &types.DisabledReason{Text: self.c.Tr.NoContentToCopyError}
copyPathItem.DisabledReason = &types.DisabledReason{Text: self.c.Tr.NoContentToCopyError}
copyFileDiffItem.DisabledReason = &types.DisabledReason{Text: self.c.Tr.NoContentToCopyError}
}
if node != nil && !node.GetHasStagedOrTrackedChanges() {
copyFileDiffItem.DisabledReason = &types.DisabledReason{Text: self.c.Tr.NoContentToCopyError}
}
if !self.anyStagedOrTrackedFile() {
copyAllDiff.DisabledReason = &types.DisabledReason{Text: self.c.Tr.NoContentToCopyError}
}
return self.c.Menu(types.CreateMenuOptions{
Title: self.c.Tr.CopyToClipboardMenu,
Items: []*types.MenuItem{