summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Haller <stefan@haller-berlin.de>2024-02-16 13:57:36 +0100
committerGitHub <noreply@github.com>2024-02-16 13:57:36 +0100
commita2ff2e6dd92c7b1bcfa41798548d3cd4f439dfa8 (patch)
tree0fa1e4bff75ae6f5269abc9b9c27440ce0f444dd
parentc4eedec9d59feb3942af5a425bd38611a386c3c9 (diff)
parent9f0b4d0000c52d23a2dfe0092fe2f6f1714e6ea5 (diff)
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).
-rw-r--r--.github/workflows/cd.yml2
-rw-r--r--.github/workflows/ci.yml12
-rw-r--r--.golangci.yml2
-rw-r--r--Dockerfile2
-rw-r--r--go.mod2
-rw-r--r--go.sum1
-rw-r--r--pkg/gui/context/branches_context.go1
-rw-r--r--pkg/gui/context/filtered_list.go19
-rw-r--r--pkg/gui/context/filtered_list_view_model.go4
-rw-r--r--pkg/gui/context/menu_context.go11
-rw-r--r--pkg/gui/context/reflog_commits_context.go1
-rw-r--r--pkg/gui/context/remote_branches_context.go1
-rw-r--r--pkg/gui/context/remotes_context.go1
-rw-r--r--pkg/gui/context/stash_context.go1
-rw-r--r--pkg/gui/context/submodules_context.go1
-rw-r--r--pkg/gui/context/tags_context.go1
-rw-r--r--pkg/gui/context/worktrees_context.go1
-rw-r--r--pkg/integration/tests/filter_and_search/filter_menu.go1
-rw-r--r--pkg/integration/tests/filter_and_search/filter_menu_cancel_filter_with_escape.go1
-rw-r--r--pkg/integration/tests/filter_and_search/filter_updates_when_model_changes.go5
20 files changed, 45 insertions, 25 deletions
diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml
index b1a1a08d2..0d126d16e 100644
--- a/.github/workflows/cd.yml
+++ b/.github/workflows/cd.yml
@@ -16,7 +16,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v4
with:
- go-version: 1.20.x
+ go-version: 1.21.x
- name: Run goreleaser
uses: goreleaser/goreleaser-action@v4
with:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 14bc6e304..9f225ccfa 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,7 +1,7 @@
name: Continuous Integration
env:
- GO_VERSION: 1.20
+ GO_VERSION: 1.21
on:
push:
@@ -32,7 +32,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v4
with:
- go-version: 1.20.x
+ go-version: 1.21.x
- name: Test code
# we're passing -short so that we skip the integration tests, which will be run in parallel below
run: |
@@ -89,7 +89,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v4
with:
- go-version: 1.20.x
+ go-version: 1.21.x
- name: Print git version
run: git --version
- name: Test code
@@ -115,7 +115,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v4
with:
- go-version: 1.20.x
+ go-version: 1.21.x
- name: Build linux binary
run: |
GOOS=linux go build
@@ -142,7 +142,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v4
with:
- go-version: 1.20.x
+ go-version: 1.21.x
- name: Check Vendor Directory
# ensure our vendor directory matches up with our go modules
run: |
@@ -168,7 +168,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v4
with:
- go-version: 1.20.x
+ go-version: 1.21.x
- name: Lint
uses: golangci/golangci-lint-action@v3.7.0
with:
diff --git a/.golangci.yml b/.golangci.yml
index 258660b62..c8884c93a 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -30,5 +30,5 @@ linters-settings:
max-func-lines: 0
run:
- go: '1.20'
+ go: '1.21'
timeout: 10m
diff --git a/Dockerfile b/Dockerfile
index 90a69b445..976f2221d 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -2,7 +2,7 @@
# docker build -t lazygit .
# 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/
COPY go.mod go.sum ./
RUN go mod download
diff --git a/go.mod b/go.mod
index 4ecdfe514..64ecc4c6d 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
module github.com/jesseduffield/lazygit
-go 1.20
+go 1.21
require (
github.com/OpenPeeDeeP/xdg v1.0.0
diff --git a/go.sum b/go.sum
index fcf3fbf79..733c1c268 100644
--- a/go.sum
+++ b/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/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
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/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=
diff --git a/pkg/gui/context/branches_context.go b/pkg/gui/context/branches_context.go
index d2647ef84..6317a60b2 100644
--- a/pkg/gui/context/branches_context.go
+++ b/pkg/gui/context/branches_context.go
@@ -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 {
diff --git a/pkg/gui/context/filtered_list.go b/pkg/gui/context/filtered_list.go
index 82d9b81c8..13b9c166a 100644
--- a/pkg/gui/context/filtered_list.go
+++ b/pkg/gui/context/filtered_list.go
@@ -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)
+ }
}
}
diff --git a/pkg/gui/context/filtered_list_view_model.go b/pkg/gui/context/filtered_list_view_model.go
index 2c2841964..b52fcbc0a 100644
--- a/pkg/gui/context/filtered_list_view_model.go
+++ b/pkg/gui/context/filtered_list_view_model.go
@@ -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,
diff --git a/pkg/gui/context/menu_context.go b/pkg/gui/context/menu_context.go
index bb1060de6..7f8656fe7 100644
--- a/pkg/gui/context/menu_context.go
+++ b/pkg/gui/context/menu_context.go
@@ -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
@@ -108,13 +112,6 @@ func (self *MenuViewModel) GetDisplayStrings(_ int, _ int) [][]string {
}
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{}
menuItems := self.FilteredListViewModel.GetItems()
var prevSection *types.MenuSection = nil
diff --git a/pkg/gui/context/reflog_commits_context.go b/pkg/gui/context/reflog_commits_context.go
index 65137d633..6791932ba 100644
--- a/pkg/gui/context/reflog_commits_context.go
+++ b/pkg/gui/context/reflog_commits_context.go
@@ -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 {
diff --git a/pkg/gui/context/remote_branches_context.go b/pkg/gui/context/remote_branches_context.go
index 884d3debb..9de792f27 100644
--- a/pkg/gui/context/remote_branches_context.go
+++ b/pkg/gui/context/remote_branches_context.go
@@ -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 {
diff --git a/pkg/gui/context/remotes_context.go b/pkg/gui/context/remotes_context.go
index 73ea428aa..51fc1c036 100644
--- a/pkg/gui/context/remotes_context.go
+++ b/pkg/gui/context/remotes_context.go
@@ -22,6 +22,7 @@ func NewRemotesContext(c *ContextCommon) *RemotesContext {
func(remote *models.Remote) []string {
return []string{remote.Name}
},
+ nil,
)
getDisplayStrings := func(_ int, _ int) [][]string {
diff --git a/pkg/gui/context/stash_context.go b/pkg/gui/context/stash_context.go
index c8d487688..c832f85ff 100644
--- a/pkg/gui/context/stash_context.go
+++ b/pkg/gui/context/stash_context.go
@@ -24,6 +24,7 @@ func NewStashContext(
func(stashEntry *models.StashEntry) []string {
return []string{stashEntry.Name}
},
+ func() bool { return true },
)
getDisplayStrings := func(_ int, _ int) [][]string {
diff --git a/pkg/gui/context/submodules_context.go b/pkg/gui/context/submodules_context.go
index 82deb25af..aff8f64ab 100644
--- a/pkg/gui/context/submodules_context.go
+++ b/pkg/gui/context/submodules_context.go
@@ -19,6 +19,7 @@ func NewSubmodulesContext(c *ContextCommon) *SubmodulesContext {
func(submodule *models.SubmoduleConfig) []string {
return []string{submodule.Name}
},
+ nil,
)
getDisplayStrings := func(_ int, _ int) [][]string {
diff --git a/pkg/gui/context/tags_context.go b/pkg/gui/context/tags_context.go
index d827564dd..c5ae2ccd5 100644
--- a/pkg/gui/context/tags_context.go
+++ b/pkg/gui/context/tags_context.go
@@ -24,6 +24,7 @@ func NewTagsContext(
func(tag *models.Tag) []string {
return []string{tag.Name, tag.Message}
},
+ nil,
)
getDisplayStrings := func(_ int, _ int) [][]string {
diff --git a/pkg/gui/context/worktrees_context.go b/pkg/gui/context/worktrees_context.go
index 3e45f2d45..55618de85 100644
--- a/pkg/gui/context/worktrees_context.go
+++ b/pkg/gui/context/worktrees_context.go
@@ -19,6 +19,7 @@ func NewWorktreesContext(c *ContextCommon) *WorktreesContext {
func(Worktree *models.Worktree) []string {
return []string{Worktree.Name}
},
+ nil,
)
getDisplayStrings := func(_ int, _ int) [][]string {
diff --git a/pkg/integration/tests/filter_and_search/filter_menu.go b/pkg/integration/tests/filter_and_search/filter_menu.go
index 70c8244f8..59c47fb71 100644
--- a/pkg/integration/tests/filter_and_search/filter_menu.go
+++ b/pkg/integration/tests/filter_and_search/filter_menu.go
@@ -26,6 +26,7 @@ var FilterMenu = NewIntegrationTest(NewIntegrationTestArgs{
Filter("Ignore").
Lines(
// menu has filtered down to the one item that matches the filter
+ Contains(`--- Local ---`),
Contains(`Ignore`).IsSelected(),
).
Confirm()
diff --git a/pkg/integration/tests/filter_and_search/filter_menu_cancel_filter_with_escape.go b/pkg/integration/tests/filter_and_search/filter_menu_cancel_filter_with_escape.go
index 6fb7166ca..daf55fd0d 100644
--- a/pkg/integration/tests/filter_and_search/filter_menu_cancel_filter_with_escape.go
+++ b/pkg/integration/tests/filter_and_search/filter_menu_cancel_filter_with_escape.go
@@ -20,6 +20,7 @@ var FilterMenuCancelFilterWithEscape = NewIntegrationTest(NewIntegrationTestArgs
Filter("Ignore").
Lines(
// menu has filtered down to the one item that matches the filter
+ Contains(`--- Local ---`),
Contains(`Ignore`).IsSelected(),
)
diff --git a/pkg/integration/tests/filter_and_search/filter_updates_when_model_changes.go b/pkg/integration/tests/filter_and_search/filter_updates_when_model_changes.go
index ae3c862c0..60ce9b580 100644
--- a/pkg/integration/tests/filter_and_search/filter_updates_when_model_changes.go
+++ b/pkg/integration/tests/filter_and_search/filter_updates_when_model_changes.go
@@ -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().