mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-01-24 05:36:19 +02:00
a5f3515ad8
Something dumb that we're currently doing is expecting list items to define an ID method which returns a string. We use that when copying items to clipboard with ctrl+o and when getting a ref name for diffing. This commit gets us a little deeper into that hole by explicitly requiring list items to implement that method so that we can easily use the new helper functions in list_controller_trait.go. In future we need to just remove the whole ID thing entirely but I'm too lazy to do that right now.
157 lines
4.4 KiB
Go
157 lines
4.4 KiB
Go
package controllers
|
|
|
|
import "github.com/jesseduffield/lazygit/pkg/gui/types"
|
|
|
|
// Embed this into your list controller to get some convenience methods for
|
|
// ensuring a single item is selected, etc.
|
|
|
|
type ListControllerTrait[T comparable] struct {
|
|
c *ControllerCommon
|
|
context types.IListContext
|
|
getSelectedItem func() T
|
|
getSelectedItems func() ([]T, int, int)
|
|
}
|
|
|
|
func NewListControllerTrait[T comparable](
|
|
c *ControllerCommon,
|
|
context types.IListContext,
|
|
getSelected func() T,
|
|
getSelectedItems func() ([]T, int, int),
|
|
) *ListControllerTrait[T] {
|
|
return &ListControllerTrait[T]{
|
|
c: c,
|
|
context: context,
|
|
getSelectedItem: getSelected,
|
|
getSelectedItems: getSelectedItems,
|
|
}
|
|
}
|
|
|
|
// Convenience function for combining multiple disabledReason callbacks.
|
|
// The first callback to return a disabled reason will be the one returned.
|
|
func (self *ListControllerTrait[T]) require(callbacks ...func() *types.DisabledReason) func() *types.DisabledReason {
|
|
return func() *types.DisabledReason {
|
|
for _, callback := range callbacks {
|
|
if disabledReason := callback(); disabledReason != nil {
|
|
return disabledReason
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// Convenience function for enforcing that a single item is selected.
|
|
// Also takes callbacks for additional disabled reasons, and passes the selected
|
|
// item into each one.
|
|
func (self *ListControllerTrait[T]) singleItemSelected(callbacks ...func(T) *types.DisabledReason) func() *types.DisabledReason {
|
|
return func() *types.DisabledReason {
|
|
if self.context.GetList().AreMultipleItemsSelected() {
|
|
return &types.DisabledReason{Text: self.c.Tr.RangeSelectNotSupported}
|
|
}
|
|
|
|
var zeroValue T
|
|
item := self.getSelectedItem()
|
|
if item == zeroValue {
|
|
return &types.DisabledReason{Text: self.c.Tr.NoItemSelected}
|
|
}
|
|
|
|
for _, callback := range callbacks {
|
|
if reason := callback(item); reason != nil {
|
|
return reason
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// Ensures that at least one item is selected.
|
|
func (self *ListControllerTrait[T]) itemRangeSelected(callbacks ...func([]T, int, int) *types.DisabledReason) func() *types.DisabledReason {
|
|
return func() *types.DisabledReason {
|
|
items, startIdx, endIdx := self.getSelectedItems()
|
|
if len(items) == 0 {
|
|
return &types.DisabledReason{Text: self.c.Tr.NoItemSelected}
|
|
}
|
|
|
|
for _, callback := range callbacks {
|
|
if reason := callback(items, startIdx, endIdx); reason != nil {
|
|
return reason
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func (self *ListControllerTrait[T]) itemsSelected(callbacks ...func([]T) *types.DisabledReason) func() *types.DisabledReason { //nolint:unused
|
|
return func() *types.DisabledReason {
|
|
items, _, _ := self.getSelectedItems()
|
|
if len(items) == 0 {
|
|
return &types.DisabledReason{Text: self.c.Tr.NoItemSelected}
|
|
}
|
|
|
|
for _, callback := range callbacks {
|
|
if reason := callback(items); reason != nil {
|
|
return reason
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// Passes the selected item to the callback. Used for handler functions.
|
|
func (self *ListControllerTrait[T]) withItem(callback func(T) error) func() error {
|
|
return func() error {
|
|
var zeroValue T
|
|
commit := self.getSelectedItem()
|
|
if commit == zeroValue {
|
|
return self.c.ErrorMsg(self.c.Tr.NoItemSelected)
|
|
}
|
|
|
|
return callback(commit)
|
|
}
|
|
}
|
|
|
|
func (self *ListControllerTrait[T]) withItems(callback func([]T) error) func() error {
|
|
return func() error {
|
|
items, _, _ := self.getSelectedItems()
|
|
if len(items) == 0 {
|
|
return self.c.ErrorMsg(self.c.Tr.NoItemSelected)
|
|
}
|
|
|
|
return callback(items)
|
|
}
|
|
}
|
|
|
|
// like withItems but also passes the start and end index of the selection
|
|
func (self *ListControllerTrait[T]) withItemsRange(callback func([]T, int, int) error) func() error {
|
|
return func() error {
|
|
items, startIdx, endIdx := self.getSelectedItems()
|
|
if len(items) == 0 {
|
|
return self.c.ErrorMsg(self.c.Tr.NoItemSelected)
|
|
}
|
|
|
|
return callback(items, startIdx, endIdx)
|
|
}
|
|
}
|
|
|
|
// Like withItem, but doesn't show an error message if no item is selected.
|
|
// Use this for click actions (it's a no-op to click empty space)
|
|
func (self *ListControllerTrait[T]) withItemGraceful(callback func(T) error) func() error {
|
|
return func() error {
|
|
var zeroValue T
|
|
commit := self.getSelectedItem()
|
|
if commit == zeroValue {
|
|
return nil
|
|
}
|
|
|
|
return callback(commit)
|
|
}
|
|
}
|
|
|
|
// All controllers must implement this method so we're defining it here for convenience
|
|
func (self *ListControllerTrait[T]) Context() types.Context {
|
|
return self.context
|
|
}
|