Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions pkg/gui/context/filtered_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ import (
type FilteredList[T any] struct {
filteredIndices []int // if nil, we are not filtering

getList func() []T
getFilterFields func(T) []string
filter string
getList func() []T
getFilterFields func(T) []string
preprocessFilter func(string) string
filter string

mutex deadlock.Mutex
}
Expand All @@ -26,6 +27,10 @@ func NewFilteredList[T any](getList func() []T, getFilterFields func(T) []string
}
}

func (self *FilteredList[T]) SetPreprocessFilterFunc(preprocessFilter func(string) string) {
self.preprocessFilter = preprocessFilter
}

func (self *FilteredList[T]) GetFilter() string {
return self.filter
}
Expand Down Expand Up @@ -79,15 +84,20 @@ func (self *FilteredList[T]) applyFilter(useFuzzySearch bool) {
self.mutex.Lock()
defer self.mutex.Unlock()

if self.filter == "" {
filter := self.filter
if self.preprocessFilter != nil {
filter = self.preprocessFilter(filter)
}

if filter == "" {
self.filteredIndices = nil
} else {
source := &fuzzySource[T]{
list: self.getList(),
getFilterFields: self.getFilterFields,
}

matches := utils.FindFrom(self.filter, source, useFuzzySearch)
matches := utils.FindFrom(filter, source, useFuzzySearch)
self.filteredIndices = lo.Map(matches, func(match fuzzy.Match, _ int) int {
return match.Index
})
Expand Down
7 changes: 7 additions & 0 deletions pkg/gui/context/filtered_list_view_model.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package context

import "github.com/jesseduffield/lazygit/pkg/i18n"

type FilteredListViewModel[T HasID] struct {
*FilteredList[T]
*ListViewModel[T]
Expand Down Expand Up @@ -33,3 +35,8 @@ func (self *FilteredListViewModel[T]) ClearFilter() {

self.SetSelection(unfilteredIndex)
}

// Default implementation of most filterable contexts. Can be overridden if needed.
func (self *FilteredListViewModel[T]) FilterPrefix(tr *i18n.TranslationSet) string {
return tr.FilterPrefix
}
45 changes: 39 additions & 6 deletions pkg/gui/context/menu_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ package context

import (
"errors"
"strings"

"github.com/jesseduffield/lazygit/pkg/gui/keybindings"
"github.com/jesseduffield/lazygit/pkg/gui/style"
"github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/i18n"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/samber/lo"
)
Expand Down Expand Up @@ -48,11 +50,12 @@ func NewMenuContext(
}

type MenuViewModel struct {
c *ContextCommon
menuItems []*types.MenuItem
prompt string
promptLines []string
columnAlignment []utils.Alignment
c *ContextCommon
menuItems []*types.MenuItem
prompt string
promptLines []string
columnAlignment []utils.Alignment
allowFilteringKeybindings bool
*FilteredListViewModel[*types.MenuItem]
}

Expand All @@ -62,11 +65,29 @@ func NewMenuViewModel(c *ContextCommon) *MenuViewModel {
c: c,
}

filterKeybindings := false

self.FilteredListViewModel = NewFilteredListViewModel(
func() []*types.MenuItem { return self.menuItems },
func(item *types.MenuItem) []string { return item.LabelColumns },
func(item *types.MenuItem) []string {
if filterKeybindings {
return []string{keybindings.LabelFromKey(item.Key)}
}

return item.LabelColumns
},
)

self.FilteredListViewModel.SetPreprocessFilterFunc(func(filter string) string {
if self.allowFilteringKeybindings && strings.HasPrefix(filter, "@") {
filterKeybindings = true
return filter[1:]
}

filterKeybindings = false
return filter
})

return self
}

Expand All @@ -92,6 +113,10 @@ func (self *MenuViewModel) SetPromptLines(promptLines []string) {
self.promptLines = promptLines
}

func (self *MenuViewModel) SetAllowFilteringKeybindings(allow bool) {
self.allowFilteringKeybindings = allow
}

// TODO: move into presentation package
func (self *MenuViewModel) GetDisplayStrings(_ int, _ int) [][]string {
menuItems := self.FilteredListViewModel.GetItems()
Expand Down Expand Up @@ -214,3 +239,11 @@ func (self *MenuContext) OnMenuPress(selectedItem *types.MenuItem) error {
func (self *MenuContext) RangeSelectEnabled() bool {
return false
}

func (self *MenuContext) FilterPrefix(tr *i18n.TranslationSet) string {
if self.allowFilteringKeybindings {
return tr.FilterPrefixMenu
}

return self.FilteredListViewModel.FilterPrefix(tr)
}
4 changes: 2 additions & 2 deletions pkg/gui/controllers/helpers/search_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func (self *SearchHelper) OpenFilterPrompt(context types.IFilterableContext) err

state.Context = context

self.searchPrefixView().SetContent(self.c.Tr.FilterPrefix)
self.searchPrefixView().SetContent(context.FilterPrefix(self.c.Tr))
promptView := self.promptView()
promptView.ClearTextArea()
self.OnPromptContentChanged("")
Expand Down Expand Up @@ -69,7 +69,7 @@ func (self *SearchHelper) DisplayFilterStatus(context types.IFilterableContext)
state.Context = context
searchString := context.GetFilter()

self.searchPrefixView().SetContent(self.c.Tr.FilterPrefix)
self.searchPrefixView().SetContent(context.FilterPrefix(self.c.Tr))

promptView := self.promptView()
keybindingConfig := self.c.UserConfig().Keybinding
Expand Down
6 changes: 3 additions & 3 deletions pkg/gui/controllers/helpers/window_arrangement_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,10 @@ func (self *WindowArrangementHelper) GetWindowDimensions(informationStr string,
repoState := self.c.State().GetRepoState()

var searchPrefix string
if repoState.GetSearchState().SearchType() == types.SearchTypeSearch {
searchPrefix = self.c.Tr.SearchPrefix
if filterableContext, ok := repoState.GetSearchState().Context.(types.IFilterableContext); ok {
searchPrefix = filterableContext.FilterPrefix(self.c.Tr)
} else {
searchPrefix = self.c.Tr.FilterPrefix
searchPrefix = self.c.Tr.SearchPrefix
}

args := WindowArrangementArgs{
Expand Down
9 changes: 5 additions & 4 deletions pkg/gui/controllers/options_menu_action.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,11 @@ func (self *OptionsMenuAction) Call() error {
appendBindings(navigation, &types.MenuSection{Title: self.c.Tr.KeybindingsMenuSectionNavigation, Column: 1})

return self.c.Menu(types.CreateMenuOptions{
Title: self.c.Tr.Keybindings,
Items: menuItems,
HideCancel: true,
ColumnAlignment: []utils.Alignment{utils.AlignRight, utils.AlignLeft},
Title: self.c.Tr.Keybindings,
Items: menuItems,
HideCancel: true,
ColumnAlignment: []utils.Alignment{utils.AlignRight, utils.AlignLeft},
AllowFilteringKeybindings: true,
})
}

Expand Down
1 change: 1 addition & 0 deletions pkg/gui/menu_panel.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ func (gui *Gui) createMenu(opts types.CreateMenuOptions) error {

gui.State.Contexts.Menu.SetMenuItems(opts.Items, opts.ColumnAlignment)
gui.State.Contexts.Menu.SetPrompt(opts.Prompt)
gui.State.Contexts.Menu.SetAllowFilteringKeybindings(opts.AllowFilteringKeybindings)
gui.State.Contexts.Menu.SetSelection(0)

gui.Views.Menu.Title = opts.Title
Expand Down
11 changes: 6 additions & 5 deletions pkg/gui/types/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,11 +147,12 @@ const (
)

type CreateMenuOptions struct {
Title string
Prompt string // a message that will be displayed above the menu options
Items []*MenuItem
HideCancel bool
ColumnAlignment []utils.Alignment
Title string
Prompt string // a message that will be displayed above the menu options
Items []*MenuItem
HideCancel bool
ColumnAlignment []utils.Alignment
AllowFilteringKeybindings bool
}

type CreatePopupPanelOpts struct {
Expand Down
2 changes: 2 additions & 0 deletions pkg/gui/types/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/gui/patch_exploring"
"github.com/jesseduffield/lazygit/pkg/i18n"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/sasha-s/go-deadlock"
)
Expand Down Expand Up @@ -130,6 +131,7 @@ type IFilterableContext interface {
ReApplyFilter(bool)
IsFiltering() bool
IsFilterableContext()
FilterPrefix(tr *i18n.TranslationSet) string
}

type ISearchableContext interface {
Expand Down
2 changes: 2 additions & 0 deletions pkg/i18n/english.go
Original file line number Diff line number Diff line change
Expand Up @@ -816,6 +816,7 @@ type TranslationSet struct {
SearchKeybindings string
SearchPrefix string
FilterPrefix string
FilterPrefixMenu string
ExitSearchMode string
ExitTextFilterMode string
Switch string
Expand Down Expand Up @@ -1881,6 +1882,7 @@ func EnglishTranslationSet() *TranslationSet {
SearchKeybindings: "%s: Next match, %s: Previous match, %s: Exit search mode",
SearchPrefix: "Search: ",
FilterPrefix: "Filter: ",
FilterPrefixMenu: "Filter (prepend '@' to filter keybindings): ",
WorktreesTitle: "Worktrees",
WorktreeTitle: "Worktree",
Switch: "Switch",
Expand Down
2 changes: 1 addition & 1 deletion pkg/integration/tests/filter_and_search/filter_menu.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ var FilterMenu = NewIntegrationTest(NewIntegrationTestArgs{
Tap(func() {
t.ExpectPopup().Menu().
Title(Equals("Keybindings")).
LineCount(GreaterThan(1))
LineCount(GreaterThan(2))
})
},
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package filter_and_search

import (
"github.com/jesseduffield/lazygit/pkg/config"
. "github.com/jesseduffield/lazygit/pkg/integration/components"
)

var FilterMenuByKeybinding = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Filtering the keybindings menu by keybinding",
ExtraCmdArgs: []string{},
Skip: false,
SetupConfig: func(config *config.AppConfig) {},
SetupRepo: func(shell *Shell) {
},
Run: func(t *TestDriver, keys config.KeybindingConfig) {
t.Views().Files().
Press(keys.Universal.OptionMenu).
Tap(func() {
t.ExpectPopup().Menu().
Title(Equals("Keybindings")).
Filter("@+").
Lines(
// menu has filtered down to the one item that matches the filter
Contains("--- Global ---"),
Contains("+ Next screen mode").IsSelected(),
).
Confirm()
}).

// Upon opening the menu again, the filter should have been reset
Press(keys.Universal.OptionMenu).
Tap(func() {
t.ExpectPopup().Menu().
Title(Equals("Keybindings")).
LineCount(GreaterThan(1))
})
},
})
1 change: 1 addition & 0 deletions pkg/integration/tests/test_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ var tests = []*components.IntegrationTest{
filter_and_search.FilterFiles,
filter_and_search.FilterFuzzy,
filter_and_search.FilterMenu,
filter_and_search.FilterMenuByKeybinding,
filter_and_search.FilterMenuCancelFilterWithEscape,
filter_and_search.FilterMenuWithNoKeybindings,
filter_and_search.FilterRemoteBranches,
Expand Down
Loading