From 2ed11336b56586f645804f912059f827891d19bf Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Tue, 5 Aug 2025 15:16:17 +0200 Subject: [PATCH] Allow filtering for keybindings by prepending filter string with '@' --- pkg/gui/context/menu_context.go | 36 +++++++++++++++--- pkg/gui/controllers/options_menu_action.go | 9 +++-- pkg/gui/menu_panel.go | 1 + pkg/gui/types/common.go | 11 +++--- .../filter_menu_by_keybinding.go | 38 +++++++++++++++++++ pkg/integration/tests/test_list.go | 1 + 6 files changed, 81 insertions(+), 15 deletions(-) create mode 100644 pkg/integration/tests/filter_and_search/filter_menu_by_keybinding.go diff --git a/pkg/gui/context/menu_context.go b/pkg/gui/context/menu_context.go index 3d5937a9d..d3f1b9cc4 100644 --- a/pkg/gui/context/menu_context.go +++ b/pkg/gui/context/menu_context.go @@ -2,6 +2,7 @@ package context import ( "errors" + "strings" "github.com/jesseduffield/lazygit/pkg/gui/keybindings" "github.com/jesseduffield/lazygit/pkg/gui/style" @@ -48,11 +49,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] } @@ -62,11 +64,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 } @@ -92,6 +112,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() diff --git a/pkg/gui/controllers/options_menu_action.go b/pkg/gui/controllers/options_menu_action.go index f295a19f4..01d4f1bb6 100644 --- a/pkg/gui/controllers/options_menu_action.go +++ b/pkg/gui/controllers/options_menu_action.go @@ -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, }) } diff --git a/pkg/gui/menu_panel.go b/pkg/gui/menu_panel.go index 75fd6700d..b0e69cfe0 100644 --- a/pkg/gui/menu_panel.go +++ b/pkg/gui/menu_panel.go @@ -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 diff --git a/pkg/gui/types/common.go b/pkg/gui/types/common.go index 7d8528ee8..be8c10a15 100644 --- a/pkg/gui/types/common.go +++ b/pkg/gui/types/common.go @@ -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 { diff --git a/pkg/integration/tests/filter_and_search/filter_menu_by_keybinding.go b/pkg/integration/tests/filter_and_search/filter_menu_by_keybinding.go new file mode 100644 index 000000000..aee4b907a --- /dev/null +++ b/pkg/integration/tests/filter_and_search/filter_menu_by_keybinding.go @@ -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)) + }) + }, +}) diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index 3e94d39ea..000b21339 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -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,