summaryrefslogtreecommitdiffstats
path: root/pkg
diff options
context:
space:
mode:
authorJesse Duffield <jessedduffield@gmail.com>2023-05-27 14:14:43 +1000
committerJesse Duffield <jessedduffield@gmail.com>2023-07-03 12:54:13 +1000
commita9e2c8129f6e1cdfd58446d7ce5080fcabc2ea04 (patch)
tree272c6f737052d6e06f71c6e1e9ce355410238f1a /pkg
parentfd861826bc11754caf4ee4651dbadf9544792d1f (diff)
Introduce filtered list view model
We're going to start supporting filtering of list views
Diffstat (limited to 'pkg')
-rw-r--r--pkg/gui/command_log_panel.go4
-rw-r--r--pkg/gui/context.go53
-rw-r--r--pkg/gui/context/branches_context.go13
-rw-r--r--pkg/gui/context/filtered_list.go56
-rw-r--r--pkg/gui/context/filtered_list_view_model.go26
-rw-r--r--pkg/gui/context/list_view_model.go (renamed from pkg/gui/context/basic_view_model.go)14
-rw-r--r--pkg/gui/context/local_commits_context.go15
-rw-r--r--pkg/gui/context/menu_context.go13
-rw-r--r--pkg/gui/context/patch_explorer_context.go14
-rw-r--r--pkg/gui/context/reflog_commits_context.go13
-rw-r--r--pkg/gui/context/remote_branches_context.go15
-rw-r--r--pkg/gui/context/remotes_context.go13
-rw-r--r--pkg/gui/context/search_trait.go70
-rw-r--r--pkg/gui/context/stash_context.go13
-rw-r--r--pkg/gui/context/sub_commits_context.go18
-rw-r--r--pkg/gui/context/submodules_context.go13
-rw-r--r--pkg/gui/context/suggestions_context.go8
-rw-r--r--pkg/gui/context/tags_context.go13
-rw-r--r--pkg/gui/controllers.go15
-rw-r--r--pkg/gui/controllers/filter_controller.go48
-rw-r--r--pkg/gui/controllers/helpers/confirmation_helper.go4
-rw-r--r--pkg/gui/controllers/helpers/helpers.go2
-rw-r--r--pkg/gui/controllers/helpers/search_helper.go196
-rw-r--r--pkg/gui/controllers/helpers/window_arrangement_helper.go12
-rw-r--r--pkg/gui/controllers/list_controller.go13
-rw-r--r--pkg/gui/controllers/local_commits_controller.go4
-rw-r--r--pkg/gui/controllers/patch_explorer_controller.go6
-rw-r--r--pkg/gui/controllers/search_controller.go48
-rw-r--r--pkg/gui/controllers/search_prompt_controller.go53
-rw-r--r--pkg/gui/editors.go11
-rw-r--r--pkg/gui/filetree/file_tree_view_model.go2
-rw-r--r--pkg/gui/gui.go20
-rw-r--r--pkg/gui/gui_common.go4
-rw-r--r--pkg/gui/keybindings.go12
-rw-r--r--pkg/gui/layout.go14
-rw-r--r--pkg/gui/menu_panel.go3
-rw-r--r--pkg/gui/searching.go103
-rw-r--r--pkg/gui/types/common.go5
-rw-r--r--pkg/gui/types/context.go21
-rw-r--r--pkg/gui/types/search_state.go31
-rw-r--r--pkg/gui/views.go2
-rw-r--r--pkg/i18n/english.go6
-rw-r--r--pkg/utils/slice.go11
43 files changed, 798 insertions, 232 deletions
diff --git a/pkg/gui/command_log_panel.go b/pkg/gui/command_log_panel.go
index 0a5ccfae3..2faee3572 100644
--- a/pkg/gui/command_log_panel.go
+++ b/pkg/gui/command_log_panel.go
@@ -136,10 +136,6 @@ func (gui *Gui) getRandomTip() string {
formattedKey(config.Universal.Return),
),
fmt.Sprintf(
- "To search for a string in your panel, press '%s'",
- formattedKey(config.Universal.StartSearch),
- ),
- fmt.Sprintf(
"You can page through the items of a panel using '%s' and '%s'",
formattedKey(config.Universal.PrevPage),
formattedKey(config.Universal.NextPage),
diff --git a/pkg/gui/context.go b/pkg/gui/context.go
index b55713f27..26cec4c23 100644
--- a/pkg/gui/context.go
+++ b/pkg/gui/context.go
@@ -200,9 +200,21 @@ func (self *ContextMgr) RemoveContexts(contextsToRemove []types.Context) error {
func (self *ContextMgr) deactivateContext(c types.Context, opts types.OnFocusLostOpts) error {
view, _ := self.gui.c.GocuiGui().View(c.GetViewName())
- if view != nil && view.IsSearching() {
- if err := self.gui.onSearchEscape(); err != nil {
- return err
+ if opts.NewContextKey != context.SEARCH_CONTEXT_KEY {
+
+ if searchableContext, ok := c.(types.ISearchableContext); ok {
+ if view != nil && view.IsSearching() {
+ view.ClearSearch()
+ searchableContext.ClearSearchString()
+ self.gui.helpers.Search.Cancel()
+ }
+ }
+
+ if filterableContext, ok := c.(types.IFilterableContext); ok {
+ if filterableContext.GetFilter() != "" {
+ filterableContext.ClearFilter()
+ self.gui.helpers.Search.Cancel()
+ }
}
}
@@ -234,6 +246,17 @@ func (self *ContextMgr) ActivateContext(c types.Context, opts types.OnFocusOpts)
return err
}
+ if searchableContext, ok := c.(types.ISearchableContext); ok {
+ if searchableContext.GetSearchString() != "" {
+ self.gui.helpers.Search.DisplaySearchPrompt(searchableContext)
+ }
+ }
+ if filterableContext, ok := c.(types.IFilterableContext); ok {
+ if filterableContext.GetFilter() != "" {
+ self.gui.helpers.Search.DisplayFilterPrompt(filterableContext)
+ }
+ }
+
desiredTitle := c.Title()
if desiredTitle != "" {
v.Title = desiredTitle
@@ -326,6 +349,30 @@ func (self *ContextMgr) IsCurrent(c types.Context) bool {
return self.Current().GetKey() == c.GetKey()
}
+func (self *ContextMgr) AllFilterable() []types.IFilterableContext {
+ var result []types.IFilterableContext
+
+ for _, context := range self.allContexts.Flatten() {
+ if ctx, ok := context.(types.IFilterableContext); ok {
+ result = append(result, ctx)
+ }
+ }
+
+ return result
+}
+
+func (self *ContextMgr) AllSearchable() []types.ISearchableContext {
+ var result []types.ISearchableContext
+
+ for _, context := range self.allContexts.Flatten() {
+ if ctx, ok := context.(types.ISearchableContext); ok {
+ result = append(result, ctx)
+ }
+ }
+
+ return result
+}
+
// all list contexts
func (self *ContextMgr) AllList() []types.IListContext {
var listContexts []types.IListContext
diff --git a/pkg/gui/context/branches_context.go b/pkg/gui/context/branches_context.go
index c2463ad20..497b3a2c4 100644
--- a/pkg/gui/context/branches_context.go
+++ b/pkg/gui/context/branches_context.go
@@ -7,7 +7,7 @@ import (
)
type BranchesContext struct {
- *BasicViewModel[*models.Branch]
+ *FilteredListViewModel[*models.Branch]
*ListContextTrait
}
@@ -17,11 +17,16 @@ var (
)
func NewBranchesContext(c *ContextCommon) *BranchesContext {
- viewModel := NewBasicViewModel(func() []*models.Branch { return c.Model().Branches })
+ viewModel := NewFilteredListViewModel(
+ func() []*models.Branch { return c.Model().Branches },
+ func(branch *models.Branch) []string {
+ return []string{branch.Name}
+ },
+ )
getDisplayStrings := func(startIdx int, length int) [][]string {
return presentation.GetBranchListDisplayStrings(
- c.Model().Branches,
+ viewModel.GetItems(),
c.State().GetRepoState().GetScreenMode() != types.SCREEN_NORMAL,
c.Modes().Diffing.Ref,
c.Tr,
@@ -30,7 +35,7 @@ func NewBranchesContext(c *ContextCommon) *BranchesContext {
}
self := &BranchesContext{
- BasicViewModel: viewModel,
+ FilteredListViewModel: viewModel,
ListContextTrait: &ListContextTrait{
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
View: c.Views().Branches,
diff --git a/pkg/gui/context/filtered_list.go b/pkg/gui/context/filtered_list.go
new file mode 100644
index 000000000..1317924ad
--- /dev/null
+++ b/pkg/gui/context/filtered_list.go
@@ -0,0 +1,56 @@
+package context
+
+import (
+ "strings"
+
+ "github.com/jesseduffield/lazygit/pkg/utils"
+)
+
+type FilteredList[T any] struct {
+ filteredIndices []int // if nil, we are not filtering
+
+ getList func() []T
+ getFilterFields func(T) []string
+ filter string
+}
+
+func (self *FilteredList[T]) GetFilter() string {
+ return self.filter
+}
+
+func (self *FilteredList[T]) SetFilter(filter string) {
+ self.filter = filter
+
+ self.applyFilter()
+}
+
+func (self *FilteredList[T]) ClearFilter() {
+ self.SetFilter("")
+}
+
+func (self *FilteredList[T]) GetList() []T {
+ if self.filteredIndices == nil {
+ return self.getList()
+ }
+ return utils.ValuesAtIndices(self.getList(), self.filteredIndices)
+}
+
+func (self *FilteredList[T]) UnfilteredLen() int {
+ return len(self.getList())
+}
+
+func (self *FilteredList[T]) applyFilter() {
+ if self.filter == "" {
+ self.filteredIndices = nil
+ } else {
+ self.filteredIndices = []int{}
+ for i, item := range self.getList() {
+ for _, field := range self.getFilterFields(item) {
+ if strings.Contains(field, self.filter) {
+ self.filteredIndices = append(self.filteredIndices, i)
+ break
+ }
+ }
+ }
+ }
+}
diff --git a/pkg/gui/context/filtered_list_view_model.go b/pkg/gui/context/filtered_list_view_model.go
new file mode 100644
index 000000000..01b020841
--- /dev/null
+++ b/pkg/gui/context/filtered_list_view_model.go
@@ -0,0 +1,26 @@
+package context
+
+type FilteredListViewModel[T any] struct {
+ *FilteredList[T]
+ *ListViewModel[T]
+}
+
+func NewFilteredListViewModel[T any](getList func() []T, getFilterFields func(T) []string) *FilteredListViewModel[T] {
+ filteredList := &FilteredList[T]{
+ getList: getList,
+ getFilterFields: getFilterFields,
+ }
+
+ self := &FilteredListViewModel[T]{
+ FilteredList: filteredList,
+ }
+
+ listViewModel := NewListViewModel(filteredList.GetList)
+
+ self.ListViewModel = listViewModel
+
+ return self
+}
+
+// used for type switch
+func (self *FilteredListViewModel[T]) IsFilterableContext() {}
diff --git a/pkg/gui/context/basic_view_model.go b/pkg/gui/context/list_view_model.go
index a53be4d91..b70330d7d 100644
--- a/pkg/gui/context/basic_view_model.go
+++ b/pkg/gui/context/list_view_model.go
@@ -2,13 +2,13 @@ package context
import "github.com/jesseduffield/lazygit/pkg/gui/context/traits"
-type BasicViewModel[T any] struct {
+type ListViewModel[T any] struct {
*traits.ListCursor
getModel func() []T
}
-func NewBasicViewModel[T any](getModel func() []T) *BasicViewModel[T] {
- self := &BasicViewModel[T]{
+func NewListViewModel[T any](getModel func() []T) *ListViewModel[T] {
+ self := &ListViewModel[T]{
getModel: getModel,
}
@@ -17,11 +17,11 @@ func NewBasicViewModel[T any](getModel func() []T) *BasicViewModel[T] {
return self
}
-func (self *BasicViewModel[T]) Len() int {
+func (self *ListViewModel[T]) Len() int {
return len(self.getModel())
}
-func (self *BasicViewModel[T]) GetSelected() T {
+func (self *ListViewModel[T]) GetSelected() T {
if self.Len() == 0 {
return Zero[T]()
}
@@ -29,6 +29,10 @@ func (self *BasicViewModel[T]) GetSelected() T {
return self.getModel()[self.GetSelectedLineIdx()]
}
+func (self *ListViewModel[T]) GetItems() []T {
+ return self.getModel()
+}
+
func Zero[T any]() T {
return *new(T)
}
diff --git a/pkg/gui/context/local_commits_context.go b/pkg/gui/context/local_commits_context.go
index 74363c52f..84204591c 100644
--- a/pkg/gui/context/local_commits_context.go
+++ b/pkg/gui/context/local_commits_context.go
@@ -13,6 +13,7 @@ import (
type LocalCommitsContext struct {
*LocalCommitsViewModel
*ListContextTrait
+ *SearchTrait
}
var (
@@ -57,8 +58,9 @@ func NewLocalCommitsContext(c *ContextCommon) *LocalCommitsContext {
)
}
- return &LocalCommitsContext{
+ ctx := &LocalCommitsContext{
LocalCommitsViewModel: viewModel,
+ SearchTrait: NewSearchTrait(c),
ListContextTrait: &ListContextTrait{
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
View: c.Views().Commits,
@@ -73,6 +75,13 @@ func NewLocalCommitsContext(c *ContextCommon) *LocalCommitsContext {
refreshViewportOnChange: true,
},
}
+
+ ctx.GetView().SetOnSelectItem(ctx.SearchTrait.onSelectItemWrapper(func(selectedLineIdx int) error {
+ ctx.GetList().SetSelectedLineIdx(selectedLineIdx)
+ return ctx.HandleFocus(types.OnFocusOpts{})
+ }))
+
+ return ctx
}
func (self *LocalCommitsContext) GetSelectedItemId() string {
@@ -85,7 +94,7 @@ func (self *LocalCommitsContext) GetSelectedItemId() string {
}
type LocalCommitsViewModel struct {
- *BasicViewModel[*models.Commit]
+ *ListViewModel[*models.Commit]
// If this is true we limit the amount of commits we load, for the sake of keeping things fast.
// If the user attempts to scroll past the end of the list, we will load more commits.
@@ -97,7 +106,7 @@ type LocalCommitsViewModel struct {
func NewLocalCommitsViewModel(getModel func() []*models.Commit, c *ContextCommon) *LocalCommitsViewModel {
self := &LocalCommitsViewModel{
- BasicViewModel: NewBasicViewModel(getModel),
+ ListViewModel: NewListViewModel(getModel),
limitCommits: true,
showWholeGitGraph: c.UserConfig.Git.Log.ShowWholeGraph,
}
diff --git a/pkg/gui/context/menu_context.go b/pkg/gui/context/menu_context.go
index 6f84a8274..088640ea0 100644
--- a/pkg/gui/context/menu_context.go
+++ b/pkg/gui/context/menu_context.go
@@ -56,7 +56,7 @@ func (self *MenuContext) GetSelectedItemId() string {
type MenuViewModel struct {
c *ContextCommon
menuItems []*types.MenuItem
- *BasicViewModel[*types.MenuItem]
+ *FilteredListViewModel[*types.MenuItem]
}
func NewMenuViewModel(c *ContextCommon) *MenuViewModel {
@@ -65,7 +65,10 @@ func NewMenuViewModel(c *ContextCommon) *MenuViewModel {
c: c,
}
- self.BasicViewModel = NewBasicViewModel(func() []*types.MenuItem { return self.menuItems })
+ self.FilteredListViewModel = NewFilteredListViewModel(
+ func() []*types.MenuItem { return self.menuItems },
+ func(item *types.MenuItem) []string { return item.LabelColumns },
+ )
return self
}
@@ -76,11 +79,12 @@ func (self *MenuViewModel) SetMenuItems(items []*types.MenuItem) {
// TODO: move into presentation package
func (self *MenuViewModel) GetDisplayStrings(_startIdx int, _length int) [][]string {
- showKeys := slices.Some(self.menuItems, func(item *types.MenuItem) bool {
+ menuItems := self.FilteredListViewModel.GetItems()
+ showKeys := slices.Some(menuItems, func(item *types.MenuItem) bool {
return item.Key != nil
})
- return slices.Map(self.menuItems, func(item *types.MenuItem) []string {
+ return slices.Map(menuItems, func(item *types.MenuItem) []string {
displayStrings := item.LabelColumns
if !showKeys {
@@ -93,6 +97,7 @@ func (self *MenuViewModel) GetDisplayStrings(_startIdx int, _length int) [][]str
self.c.UserConfig.Keybinding.Universal.Confirm,
self.c.UserConfig.Keybinding.Universal.Select,
self.c.UserConfig.Keybinding.Universal.Return,
+ self.c.UserConfig.Keybinding.Universal.StartSearch,
}
keyLabel := keybindings.LabelFromKey(item.Key)
keyStyle := style.FgCyan
diff --git a/pkg/gui/context/patch_explorer_context.go b/pkg/gui/context/patch_explorer_context.go
index 1c986ee1d..17ecae4ae 100644
--- a/pkg/gui/context/patch_explorer_context.go
+++ b/pkg/gui/context/patch_explorer_context.go
@@ -9,6 +9,7 @@ import (
type PatchExplorerContext struct {
*SimpleContext
+ *SearchTrait
state *patch_exploring.State
viewTrait *ViewTrait
@@ -28,7 +29,7 @@ func NewPatchExplorerContext(
c *ContextCommon,
) *PatchExplorerContext {
- return &PatchExplorerContext{
+ ctx := &PatchExplorerContext{
state: nil,
viewTrait: NewViewTrait(view),
c: c,
@@ -42,7 +43,18 @@ func NewPatchExplorerContext(
Focusable: true,
HighlightOnFocus: true,
})),
+ SearchTrait: NewSearchTrait(c),
}
+
+ ctx.GetView().SetOnSelectItem(ctx.SearchTrait.onSelectItemWrapper(
+ func(selectedLineIdx int) error {
+ ctx.GetMutex().Lock()
+ defer ctx.GetMutex().Unlock()
+ return ctx.NavigateTo(ctx.c.IsCurrentContext(ctx), selectedLineIdx)
+ }),
+ )
+
+ return ctx
}
func (self *PatchExplorerContext) IsPatchExplorerContext() {}
diff --git a/pkg/gui/context/reflog_commits_context.go b/pkg/gui/context/reflog_commits_context.go
index a92d605c8..421a7c8d5 100644
--- a/pkg/gui/context/reflog_commits_context.go
+++ b/pkg/gui/context/reflog_commits_context.go
@@ -9,7 +9,7 @@ import (
)
type ReflogCommitsContext struct {
- *BasicViewModel[*models.Commit]
+ *FilteredListViewModel[*models.Commit]
*ListContextTrait
}
@@ -19,11 +19,16 @@ var (
)
func NewReflogCommitsContext(c *ContextCommon) *ReflogCommitsContext {
- viewModel := NewBasicViewModel(func() []*models.Commit { return c.Model().FilteredReflogCommits })
+ viewModel := NewFilteredListViewModel(
+ func() []*models.Commit { return c.Model().FilteredReflogCommits },
+ func(commit *models.Commit) []string {
+ return []string{commit.ShortSha(), commit.Name}
+ },
+ )
getDisplayStrings := func(startIdx int, length int) [][]string {
return presentation.GetReflogCommitListDisplayStrings(
- c.Model().FilteredReflogCommits,
+ viewModel.GetItems(),
c.State().GetRepoState().GetScreenMode() != types.SCREEN_NORMAL,
c.Modes().CherryPicking.SelectedShaSet(),
c.Modes().Diffing.Ref,
@@ -35,7 +40,7 @@ func NewReflogCommitsContext(c *ContextCommon) *ReflogCommitsContext {
}
return &ReflogCommitsContext{
- BasicViewModel: viewModel,
+ FilteredListViewModel: viewModel,
ListContextTrait: &ListContextTrait{
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
View: c.Views().ReflogCommits,
diff --git a/pkg/gui/context/remote_branches_context.go b/pkg/gui/context/remote_branches_context.go
index a085c18cc..602a19a65 100644
--- a/pkg/gui/context/remote_branches_context.go
+++ b/pkg/gui/context/remote_branches_context.go
@@ -7,7 +7,7 @@ import (
)
type RemoteBranchesContext struct {
- *BasicViewModel[*models.RemoteBranch]
+ *FilteredListViewModel[*models.RemoteBranch]
*ListContextTrait
*DynamicTitleBuilder
}
@@ -20,15 +20,20 @@ var (
func NewRemoteBranchesContext(
c *ContextCommon,
) *RemoteBranchesContext {
- viewModel := NewBasicViewModel(func() []*models.RemoteBranch { return c.Model().RemoteBranches })
+ viewModel := NewFilteredListViewModel(
+ func() []*models.RemoteBranch { return c.Model().RemoteBranches },
+ func(remoteBranch *models.RemoteBranch) []string {
+ return []string{remoteBranch.Name}
+ },
+ )
getDisplayStrings := func(startIdx int, length int) [][]string {
- return presentation.GetRemoteBranchListDisplayStrings(c.Model().RemoteBranches, c.Modes().Diffing.Ref)
+ return presentation.GetRemoteBranchListDisplayStrings(viewModel.GetItems(), c.Modes().Diffing.Ref)
}
return &RemoteBranchesContext{
- BasicViewModel: viewModel,
- DynamicTitleBuilder: NewDynamicTitleBuilder(c.Tr.RemoteBranchesDynamicTitle),
+ FilteredListViewModel: viewModel,
+ DynamicTitleBuilder: NewDynamicTitleBuilder(c.Tr.RemoteBranchesDynamicTitle),
ListContextTrait: &ListContextTrait{
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
View: c.Views().RemoteBranches,
diff --git a/pkg/gui/context/remotes_context.go b/pkg/gui/context/remotes_context.go
index d1082ab52..f5e2a97ab 100644
--- a/pkg/gui/context/remotes_context.go
+++ b/pkg/gui/context/remotes_context.go
@@ -7,7 +7,7 @@ import (
)
type RemotesContext struct {
- *BasicViewModel[*models.Remote]
+ *FilteredListViewModel[*models.Remote]
*ListContextTrait
}
@@ -17,14 +17,19 @@ var (
)
func NewRemotesContext(c *ContextCommon) *RemotesContext {
- viewModel := NewBasicViewModel(func() []*models.Remote { return c.Model().Remotes })
+ viewModel := NewFilteredListViewModel(
+ func() []*models.Remote { return c.Model().Remotes },
+ func(remote *models.Remote) []string {
+ return []string{remote.Name}
+ },
+ )
getDisplayStrings := func(startIdx int, length int) [][]string {
- return presentation.GetRemoteListDisplayStrings(c.Model().Remotes, c.Modes().Diffing.Ref)
+ return presentation.GetRemoteListDisplayStrings(viewModel.GetItems(), c.Modes().Diffing.Ref)
}
return &RemotesContext{
- BasicViewModel: viewModel,
+ FilteredListViewModel: viewModel,
ListContextTrait: &ListContextTrait{
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
View: c.Views().Remotes,
diff --git a/pkg/gui/context/search_trait.go b/pkg/gui/context/search_trait.go
new file mode 100644
index 000000000..5e745f995
--- /dev/null
+++ b/pkg/gui/context/search_trait.go
@@ -0,0 +1,70 @@
+package context
+
+import (
+ "fmt"
+
+ "github.com/jesseduffield/lazygit/pkg/gui/keybindings"
+ "github.com/jesseduffield/lazygit/pkg/theme"
+)
+
+type SearchTrait struct {
+ c *ContextCommon
+
+ searchString string
+}
+
+func NewSearchTrait(c *ContextCommon) *SearchTrait {
+ return &SearchTrait{c: c}
+}
+
+func (self *SearchTrait) GetSearchString() string {
+ return self.searchString
+}
+
+func (self *SearchTrait) SetSearchString(searchString string) {
+ self.searchString = searchString
+}
+
+func (self *SearchTrait) ClearSearchString() {
+ self.SetSearchString("")
+}
+
+// used for type switch
+func (self *SearchTrait) IsSearchableContext() {}
+
+func (self *SearchTrait) onSelectItemWrapper(innerFunc func(int) error) func(int, int, int) error {
+ keybindingConfig := self.c.UserConfig.Keybinding
+
+ return func(y int, index int, total int) error {
+ if total == 0 {
+ self.c.SetViewContent(
+ self.c.Views().Search,
+ fmt.Sprintf(
+ self.c.Tr.NoMatchesFor,
+ self.searchString,
+ theme.OptionsFgColor.Sprintf(self.c.Tr.ExitSearchMode, keybindings.Label(keybindingConfig.Universal.Return)),
+ ),
+ )
+ return nil
+ }
+ self.c.SetViewContent(
+ self.c.Views().Search,
+ fmt.Sprintf(
+ self.c.Tr.MatchesFor,
+ self.searchString,
+ index+1,
+ total,
+ theme.OptionsFgColor.Sprintf(
+ self.c.Tr.SearchKeybindings,
+ keybindings.Label(keybindingConfig.Universal.NextMatch),
+ keybindings.Label(keybindingConfig.Universal.PrevMatch),
+ keybindings.Label(keybindingConfig.Universal.Return),
+ ),
+ ),
+ )
+ if err := innerFunc(y); err != nil {
+ return err
+ }
+ return nil
+ }
+}
diff --git a/pkg/gui/context/stash_context.go b/pkg/gui/context/stash_context.go
index 386292c00..7bd4740f8 100644
--- a/pkg/gui/context/stash_context.go
+++ b/pkg/gui/context/stash_context.go
@@ -7,7 +7,7 @@ import (
)
type StashContext struct {
- *BasicViewModel[*models.StashEntry]
+ *FilteredListViewModel[*models.StashEntry]
*ListContextTrait
}
@@ -19,14 +19,19 @@ var (
func NewStashContext(
c *ContextCommon,
) *StashContext {
- viewModel := NewBasicViewModel(func() []*models.StashEntry { return c.Model().StashEntries })
+ viewModel := NewFilteredListViewModel(
+ func() []*models.StashEntry { return c.Model().StashEntries },
+ func(stashEntry *models.StashEntry) []string {
+ return []string{stashEntry.Name}
+ },
+ )
getDisplayStrings := func(startIdx int, length int) [][]string {
- return presentation.GetStashEntryListDisplayStrings(c.Model().StashEntries, c.Modes().Diffing.Ref)
+ return presentation.GetStashEntryListDisplayStrings(viewModel.GetItems(), c.Modes().Diffing.Ref)
}
return &StashContext{
- BasicViewModel: viewModel,
+ FilteredListViewModel: viewModel,
ListContextTrait: &ListContextTrait{
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
View: c.Views().Stash,
diff --git a/pkg/gui/context/sub_commits_context.go b/pkg/gui/context/sub_commits_context.go
index e2c89aa14..0cf884589 100644
--- a/pkg/gui/context/sub_commits_context.go
+++ b/pkg/gui/