From b30c7345139764cb13b86e2cd5c916fd6bd9cf5f Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Wed, 5 Aug 2026 07:18:55 +0200 Subject: [PATCH] Handle entering and leaving filtering mode in one place Setting a filter and clearing it are the same transition in opposite directions: mutate the mode, bring the screen mode in line with it, reload the views that depend on the filter, and put the selection somewhere sensible in the reloaded commit list. They were implemented twice, once in the filtering menu and once in ModeHelper, which is how the two came to repaint the commit list in different ways. Derive the screen mode and the panel switch from whether a filter is active after the change, so both directions fall out of the same code, and give ModeHelper the entry points for both. The filtering menu is left with nothing but the menu. --- pkg/gui/controllers/filtering_menu_action.go | 40 ++---------- pkg/gui/controllers/helpers/mode_helper.go | 64 +++++++++++++++++--- 2 files changed, 60 insertions(+), 44 deletions(-) diff --git a/pkg/gui/controllers/filtering_menu_action.go b/pkg/gui/controllers/filtering_menu_action.go index 7ae26c4ef..0bd7ca902 100644 --- a/pkg/gui/controllers/filtering_menu_action.go +++ b/pkg/gui/controllers/filtering_menu_action.go @@ -3,7 +3,6 @@ package controllers import ( "fmt" - "github.com/jesseduffield/lazygit/pkg/gui/controllers/helpers" "github.com/jesseduffield/lazygit/pkg/gui/types" ) @@ -42,7 +41,7 @@ func (self *FilteringMenuAction) Call() error { menuItems = append(menuItems, &types.MenuItem{ Label: fmt.Sprintf("%s '%s'", self.c.Tr.FilterBy, fileName), OnPress: func() error { - return self.setFilteringPath(fileName) + return self.c.Helpers().Mode.SetFilteringPath(fileName) }, Tooltip: tooltip, }) @@ -52,7 +51,7 @@ func (self *FilteringMenuAction) Call() error { menuItems = append(menuItems, &types.MenuItem{ Label: fmt.Sprintf("%s '%s'", self.c.Tr.FilterBy, author), OnPress: func() error { - return self.setFilteringAuthor(author) + return self.c.Helpers().Mode.SetFilteringAuthor(author) }, Tooltip: tooltip, }) @@ -65,7 +64,7 @@ func (self *FilteringMenuAction) Call() error { FindSuggestionsFunc: self.c.Helpers().Suggestions.GetFilePathSuggestionsFunc(), Title: self.c.Tr.EnterFileName, HandleConfirm: func(response string) error { - return self.setFilteringPath(response) + return self.c.Helpers().Mode.SetFilteringPath(response) }, }) @@ -81,7 +80,7 @@ func (self *FilteringMenuAction) Call() error { FindSuggestionsFunc: self.c.Helpers().Suggestions.GetAuthorsSuggestionsFunc(), Title: self.c.Tr.EnterAuthor, HandleConfirm: func(response string) error { - return self.setFilteringAuthor(response) + return self.c.Helpers().Mode.SetFilteringAuthor(response) }, }) @@ -99,34 +98,3 @@ func (self *FilteringMenuAction) Call() error { return self.c.Menu(types.CreateMenuOptions{Title: self.c.Tr.FilteringMenuTitle, Items: menuItems}) } - -func (self *FilteringMenuAction) setFilteringPath(path string) error { - self.c.Modes().Filtering.Reset() - self.c.Modes().Filtering.SetPath(path) - return self.setFiltering() -} - -func (self *FilteringMenuAction) setFilteringAuthor(author string) error { - self.c.Modes().Filtering.Reset() - self.c.Modes().Filtering.SetAuthor(author) - return self.setFiltering() -} - -func (self *FilteringMenuAction) setFiltering() error { - self.c.Modes().Filtering.SetSelectedCommitHash(self.c.Contexts().LocalCommits.GetSelectedCommitHash()) - - repoState := self.c.State().GetRepoState() - if repoState.GetScreenMode() == types.SCREEN_NORMAL { - repoState.SetScreenMode(types.SCREEN_HALF) - } - - self.c.Context().Push(self.c.Contexts().LocalCommits, types.OnFocusOpts{}) - - self.c.Refresh(types.RefreshOptions{Scope: helpers.ScopesToRefreshWhenFilteringModeChanges(), Then: func() error { - self.c.Contexts().LocalCommits.SetSelection(0) - self.c.Contexts().LocalCommits.HandleFocus(types.OnFocusOpts{}) - return nil - }}) - - return nil -} diff --git a/pkg/gui/controllers/helpers/mode_helper.go b/pkg/gui/controllers/helpers/mode_helper.go index 50f35341b..8b88205e0 100644 --- a/pkg/gui/controllers/helpers/mode_helper.go +++ b/pkg/gui/controllers/helpers/mode_helper.go @@ -182,16 +182,40 @@ func (self *ModeHelper) ExitFilterMode() error { return self.ClearFiltering() } +func (self *ModeHelper) SetFilteringPath(path string) error { + return self.setFiltering(func() { + self.c.Modes().Filtering.SetPath(path) + }) +} + +func (self *ModeHelper) SetFilteringAuthor(author string) error { + return self.setFiltering(func() { + self.c.Modes().Filtering.SetAuthor(author) + }) +} + +func (self *ModeHelper) setFiltering(setFilter func()) error { + self.changeFiltering( + func() { + // Whatever we were filtering by before is replaced, not added to + self.c.Modes().Filtering.Reset() + setFilter() + self.c.Modes().Filtering.SetSelectedCommitHash( + self.c.Contexts().LocalCommits.GetSelectedCommitHash()) + }, + func() { + self.c.Contexts().LocalCommits.SetSelection(0) + }, + ) + return nil +} + func (self *ModeHelper) ClearFiltering() error { selectedCommitHash := self.c.Contexts().LocalCommits.GetSelectedCommitHash() - self.c.Modes().Filtering.Reset() - if self.c.State().GetRepoState().GetScreenMode() == types.SCREEN_HALF { - self.c.State().GetRepoState().SetScreenMode(types.SCREEN_NORMAL) - } - self.c.Refresh(types.RefreshOptions{ - Scope: ScopesToRefreshWhenFilteringModeChanges(), - Then: func() error { + self.changeFiltering( + self.c.Modes().Filtering.Reset, + func() { // Find the commit that was last selected in filtering mode, and select it again after refreshing if !self.c.Contexts().LocalCommits.SelectCommitByHash(selectedCommitHash) { // If we couldn't find it (either because no commit was selected @@ -200,12 +224,36 @@ func (self *ModeHelper) ClearFiltering() error { // before we entered filtering self.c.Contexts().LocalCommits.SelectCommitByHash(self.c.Modes().Filtering.GetSelectedCommitHash()) } + }, + ) + return nil +} +// changeFiltering applies a change to the filtering mode: setFilter mutates the +// mode, then the screen mode and the focused panel are brought in line with it, +// the views whose contents depend on the filter are reloaded, and selectCommit +// puts the selection where it belongs in the reloaded commit list. +func (self *ModeHelper) changeFiltering(setFilter func(), selectCommit func()) { + setFilter() + + repoState := self.c.State().GetRepoState() + if self.c.Modes().Filtering.Active() { + if repoState.GetScreenMode() == types.SCREEN_NORMAL { + repoState.SetScreenMode(types.SCREEN_HALF) + } + self.c.Context().Push(self.c.Contexts().LocalCommits, types.OnFocusOpts{}) + } else if repoState.GetScreenMode() == types.SCREEN_HALF { + repoState.SetScreenMode(types.SCREEN_NORMAL) + } + + self.c.Refresh(types.RefreshOptions{ + Scope: ScopesToRefreshWhenFilteringModeChanges(), + Then: func() error { + selectCommit() self.c.PostRefreshUpdate(self.c.Contexts().LocalCommits) return nil }, }) - return nil } // Stashes really only need to be refreshed when filtering by path, not by author, but it's too much