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:
commit
a2ff2e6dd9
2
.github/workflows/cd.yml
vendored
2
.github/workflows/cd.yml
vendored
@ -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:
|
||||||
|
12
.github/workflows/ci.yml
vendored
12
.github/workflows/ci.yml
vendored
@ -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:
|
||||||
|
@ -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
|
||||||
|
@ -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
2
go.mod
@ -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
1
go.sum
@ -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=
|
||||||
|
@ -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 {
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
|
@ -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 {
|
||||||
|
@ -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 {
|
||||||
|
@ -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 {
|
||||||
|
@ -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 {
|
||||||
|
@ -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 {
|
||||||
|
@ -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 {
|
||||||
|
@ -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 {
|
||||||
|
@ -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()
|
||||||
|
@ -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(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -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().
|
||||||
|
Loading…
x
Reference in New Issue
Block a user