1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-05-27 23:08:02 +02:00

Keep sort order when filtering lists sorted by date (#3282)

- **PR Description**

Keep the sort order stable when filtering lists of things sorted by date
(branches, stashes, reflog entries).
This commit is contained in:
Stefan Haller 2024-02-16 13:57:36 +01:00 committed by GitHub
commit a2ff2e6dd9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 45 additions and 25 deletions

View File

@ -16,7 +16,7 @@ jobs:
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v4 uses: actions/setup-go@v4
with: with:
go-version: 1.20.x go-version: 1.21.x
- name: Run goreleaser - name: Run goreleaser
uses: goreleaser/goreleaser-action@v4 uses: goreleaser/goreleaser-action@v4
with: with:

View File

@ -1,7 +1,7 @@
name: Continuous Integration name: Continuous Integration
env: env:
GO_VERSION: 1.20 GO_VERSION: 1.21
on: on:
push: push:
@ -32,7 +32,7 @@ jobs:
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v4 uses: actions/setup-go@v4
with: with:
go-version: 1.20.x go-version: 1.21.x
- name: Test code - name: Test code
# we're passing -short so that we skip the integration tests, which will be run in parallel below # we're passing -short so that we skip the integration tests, which will be run in parallel below
run: | run: |
@ -89,7 +89,7 @@ jobs:
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v4 uses: actions/setup-go@v4
with: with:
go-version: 1.20.x go-version: 1.21.x
- name: Print git version - name: Print git version
run: git --version run: git --version
- name: Test code - name: Test code
@ -115,7 +115,7 @@ jobs:
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v4 uses: actions/setup-go@v4
with: with:
go-version: 1.20.x go-version: 1.21.x
- name: Build linux binary - name: Build linux binary
run: | run: |
GOOS=linux go build GOOS=linux go build
@ -142,7 +142,7 @@ jobs:
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v4 uses: actions/setup-go@v4
with: with:
go-version: 1.20.x go-version: 1.21.x
- name: Check Vendor Directory - name: Check Vendor Directory
# ensure our vendor directory matches up with our go modules # ensure our vendor directory matches up with our go modules
run: | run: |
@ -168,7 +168,7 @@ jobs:
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v4 uses: actions/setup-go@v4
with: with:
go-version: 1.20.x go-version: 1.21.x
- name: Lint - name: Lint
uses: golangci/golangci-lint-action@v3.7.0 uses: golangci/golangci-lint-action@v3.7.0
with: with:

View File

@ -30,5 +30,5 @@ linters-settings:
max-func-lines: 0 max-func-lines: 0
run: run:
go: '1.20' go: '1.21'
timeout: 10m timeout: 10m

View File

@ -2,7 +2,7 @@
# docker build -t lazygit . # docker build -t lazygit .
# docker run -it lazygit:latest /bin/sh # docker run -it lazygit:latest /bin/sh
FROM golang:1.20 as build FROM golang:1.21 as build
WORKDIR /go/src/github.com/jesseduffield/lazygit/ WORKDIR /go/src/github.com/jesseduffield/lazygit/
COPY go.mod go.sum ./ COPY go.mod go.sum ./
RUN go mod download RUN go mod download

2
go.mod
View File

@ -1,6 +1,6 @@
module github.com/jesseduffield/lazygit module github.com/jesseduffield/lazygit
go 1.20 go 1.21
require ( require (
github.com/OpenPeeDeeP/xdg v1.0.0 github.com/OpenPeeDeeP/xdg v1.0.0

1
go.sum
View File

@ -299,6 +299,7 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M= github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M=
github.com/thoas/go-funk v0.9.1/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q=
github.com/urfave/cli v1.20.1-0.20180226030253-8e01ec4cd3e2/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.20.1-0.20180226030253-8e01ec4cd3e2/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=

View File

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

View File

@ -1,6 +1,7 @@
package context package context
import ( import (
"slices"
"strings" "strings"
"github.com/jesseduffield/lazygit/pkg/utils" "github.com/jesseduffield/lazygit/pkg/utils"
@ -16,14 +17,21 @@ type FilteredList[T any] struct {
getFilterFields func(T) []string getFilterFields func(T) []string
filter 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 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]{ return &FilteredList[T]{
getList: getList, getList: getList,
getFilterFields: getFilterFields, getFilterFields: getFilterFields,
mutex: &deadlock.Mutex{}, 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 { self.filteredIndices = lo.Map(matches, func(match fuzzy.Match, _ int) int {
return match.Index 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 *SearchHistory
} }
func NewFilteredListViewModel[T HasID](getList func() []T, getFilterFields func(T) []string) *FilteredListViewModel[T] { func NewFilteredListViewModel[T HasID](getList func() []T, getFilterFields func(T) []string, shouldRetainSortOrder func() bool) *FilteredListViewModel[T] {
filteredList := NewFilteredList(getList, getFilterFields) filteredList := NewFilteredList(getList, getFilterFields, shouldRetainSortOrder)
self := &FilteredListViewModel[T]{ self := &FilteredListViewModel[T]{
FilteredList: filteredList, FilteredList: filteredList,

View File

@ -61,6 +61,10 @@ func NewMenuViewModel(c *ContextCommon) *MenuViewModel {
self.FilteredListViewModel = NewFilteredListViewModel( self.FilteredListViewModel = NewFilteredListViewModel(
func() []*types.MenuItem { return self.menuItems }, func() []*types.MenuItem { return self.menuItems },
func(item *types.MenuItem) []string { return item.LabelColumns }, 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 return self
@ -108,13 +112,6 @@ func (self *MenuViewModel) GetDisplayStrings(_ int, _ int) [][]string {
} }
func (self *MenuViewModel) GetNonModelItems() []*NonModelItem { func (self *MenuViewModel) GetNonModelItems() []*NonModelItem {
// Don't display section headers when we are filtering. The reason is that
// filtering changes the order of the items (they are sorted by best match),
// so all the sections would be messed up.
if self.FilteredListViewModel.IsFiltering() {
return []*NonModelItem{}
}
result := []*NonModelItem{} result := []*NonModelItem{}
menuItems := self.FilteredListViewModel.GetItems() menuItems := self.FilteredListViewModel.GetItems()
var prevSection *types.MenuSection = nil var prevSection *types.MenuSection = nil

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -26,6 +26,7 @@ var FilterMenu = NewIntegrationTest(NewIntegrationTestArgs{
Filter("Ignore"). Filter("Ignore").
Lines( Lines(
// menu has filtered down to the one item that matches the filter // menu has filtered down to the one item that matches the filter
Contains(`--- Local ---`),
Contains(`Ignore`).IsSelected(), Contains(`Ignore`).IsSelected(),
). ).
Confirm() Confirm()

View File

@ -20,6 +20,7 @@ var FilterMenuCancelFilterWithEscape = NewIntegrationTest(NewIntegrationTestArgs
Filter("Ignore"). Filter("Ignore").
Lines( Lines(
// menu has filtered down to the one item that matches the filter // menu has filtered down to the one item that matches the filter
Contains(`--- Local ---`),
Contains(`Ignore`).IsSelected(), Contains(`Ignore`).IsSelected(),
) )

View File

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