1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2024-11-28 09:08:41 +02:00

Optionally keep sort order stable when filtering lists

For some lists it is useful to keep the same sort order when filtering (rather
than sorting by best match like we usually do). Add an optional function to
FilteredList to make this possible, and use it whenever we show lists of things
sorted by date (branches, stashes, reflog entries), as well as menu items
because this allows us to keep the section headers in the keybindings menu,
which is useful for understanding what you are looking at when filtering.
This commit is contained in:
Stefan Haller 2024-01-29 17:50:45 +01:00
parent 57ac9c2189
commit 649048c336
12 changed files with 32 additions and 8 deletions

View File

@ -22,6 +22,7 @@ func NewBranchesContext(c *ContextCommon) *BranchesContext {
func(branch *models.Branch) []string {
return []string{branch.Name}
},
func() bool { return c.AppState.LocalBranchSortOrder != "alphabetical" },
)
getDisplayStrings := func(_ int, _ int) [][]string {

View File

@ -1,6 +1,7 @@
package context
import (
"slices"
"strings"
"github.com/jesseduffield/lazygit/pkg/utils"
@ -16,14 +17,21 @@ type FilteredList[T any] struct {
getFilterFields func(T) []string
filter string
// Normally, filtered items are presented sorted by best match. If this
// function returns true, they retain their original sort order instead;
// this is useful for lists that show items sorted by date, for example.
// Leaving this nil is equivalent to returning false.
shouldRetainSortOrder func() bool
mutex *deadlock.Mutex
}
func NewFilteredList[T any](getList func() []T, getFilterFields func(T) []string) *FilteredList[T] {
func NewFilteredList[T any](getList func() []T, getFilterFields func(T) []string, shouldRetainSortOrder func() bool) *FilteredList[T] {
return &FilteredList[T]{
getList: getList,
getFilterFields: getFilterFields,
mutex: &deadlock.Mutex{},
getList: getList,
getFilterFields: getFilterFields,
shouldRetainSortOrder: shouldRetainSortOrder,
mutex: &deadlock.Mutex{},
}
}
@ -92,6 +100,9 @@ func (self *FilteredList[T]) applyFilter() {
self.filteredIndices = lo.Map(matches, func(match fuzzy.Match, _ int) int {
return match.Index
})
if self.shouldRetainSortOrder != nil && self.shouldRetainSortOrder() {
slices.Sort(self.filteredIndices)
}
}
}

View File

@ -6,8 +6,8 @@ type FilteredListViewModel[T HasID] struct {
*SearchHistory
}
func NewFilteredListViewModel[T HasID](getList func() []T, getFilterFields func(T) []string) *FilteredListViewModel[T] {
filteredList := NewFilteredList(getList, getFilterFields)
func NewFilteredListViewModel[T HasID](getList func() []T, getFilterFields func(T) []string, shouldRetainSortOrder func() bool) *FilteredListViewModel[T] {
filteredList := NewFilteredList(getList, getFilterFields, shouldRetainSortOrder)
self := &FilteredListViewModel[T]{
FilteredList: filteredList,

View File

@ -61,6 +61,10 @@ func NewMenuViewModel(c *ContextCommon) *MenuViewModel {
self.FilteredListViewModel = NewFilteredListViewModel(
func() []*types.MenuItem { return self.menuItems },
func(item *types.MenuItem) []string { return item.LabelColumns },
// The only menu that the user is likely to filter in is the keybindings
// menu; retain the sort order in that one because this allows us to
// keep the section headers while filtering:
func() bool { return true },
)
return self

View File

@ -24,6 +24,7 @@ func NewReflogCommitsContext(c *ContextCommon) *ReflogCommitsContext {
func(commit *models.Commit) []string {
return []string{commit.ShortSha(), commit.Name}
},
func() bool { return true },
)
getDisplayStrings := func(_ int, _ int) [][]string {

View File

@ -26,6 +26,7 @@ func NewRemoteBranchesContext(
func(remoteBranch *models.RemoteBranch) []string {
return []string{remoteBranch.Name}
},
func() bool { return c.AppState.RemoteBranchSortOrder != "alphabetical" },
)
getDisplayStrings := func(_ int, _ int) [][]string {

View File

@ -22,6 +22,7 @@ func NewRemotesContext(c *ContextCommon) *RemotesContext {
func(remote *models.Remote) []string {
return []string{remote.Name}
},
nil,
)
getDisplayStrings := func(_ int, _ int) [][]string {

View File

@ -24,6 +24,7 @@ func NewStashContext(
func(stashEntry *models.StashEntry) []string {
return []string{stashEntry.Name}
},
func() bool { return true },
)
getDisplayStrings := func(_ int, _ int) [][]string {

View File

@ -19,6 +19,7 @@ func NewSubmodulesContext(c *ContextCommon) *SubmodulesContext {
func(submodule *models.SubmoduleConfig) []string {
return []string{submodule.Name}
},
nil,
)
getDisplayStrings := func(_ int, _ int) [][]string {

View File

@ -24,6 +24,7 @@ func NewTagsContext(
func(tag *models.Tag) []string {
return []string{tag.Name, tag.Message}
},
nil,
)
getDisplayStrings := func(_ int, _ int) [][]string {

View File

@ -19,6 +19,7 @@ func NewWorktreesContext(c *ContextCommon) *WorktreesContext {
func(Worktree *models.Worktree) []string {
return []string{Worktree.Name}
},
nil,
)
getDisplayStrings := func(_ int, _ int) [][]string {

View File

@ -27,9 +27,10 @@ var FilterUpdatesWhenModelChanges = NewIntegrationTest(NewIntegrationTestArgs{
).
FilterOrSearch("branch").
Lines(
Contains("branch-to-delete").IsSelected(),
Contains("checked-out-branch"),
Contains("checked-out-branch").IsSelected(),
Contains("branch-to-delete"),
).
SelectNextItem().
Press(keys.Universal.Remove).
Tap(func() {
t.ExpectPopup().