From 8728da7985b08744af14312921d02f00f02fd074 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Sat, 7 Mar 2026 20:26:35 +1100 Subject: [PATCH] Only reset selection in ReApplyFilter when search prompt is active Without this check, the selection was being reset to 0 whenever ReApplyFilter was called for the current filter context, even when the user wasn't actively typing in the search prompt (e.g. when the model updates in the background). This was causing unexpected cursor jumps. Co-Authored-By: Claude Opus 4.6 --- pkg/gui/controllers/helpers/search_helper.go | 2 +- ...ter_preserves_selection_on_model_change.go | 63 +++++++++++++++++++ pkg/integration/tests/test_list.go | 1 + 3 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 pkg/integration/tests/filter_and_search/filter_preserves_selection_on_model_change.go diff --git a/pkg/gui/controllers/helpers/search_helper.go b/pkg/gui/controllers/helpers/search_helper.go index 506e8a736..9b3dcec64 100644 --- a/pkg/gui/controllers/helpers/search_helper.go +++ b/pkg/gui/controllers/helpers/search_helper.go @@ -240,7 +240,7 @@ func (self *SearchHelper) ReApplyFilter(context types.Context) { filterableContext, ok := context.(types.IFilterableContext) if ok { state := self.searchState() - if context == state.Context { + if context == state.Context && self.c.Context().Current().GetKey() == self.c.Contexts().Search.GetKey() { filterableContext.SetSelection(0) filterableContext.GetView().SetOriginY(0) } diff --git a/pkg/integration/tests/filter_and_search/filter_preserves_selection_on_model_change.go b/pkg/integration/tests/filter_and_search/filter_preserves_selection_on_model_change.go new file mode 100644 index 000000000..eaafbaa66 --- /dev/null +++ b/pkg/integration/tests/filter_and_search/filter_preserves_selection_on_model_change.go @@ -0,0 +1,63 @@ +package filter_and_search + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var FilterPreservesSelectionOnModelChange = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Verify that when a filter is active and the model changes, the selection is preserved", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell *Shell) { + shell.EmptyCommit("first commit") + shell.NewBranch("branch-alpha") + shell.NewBranch("branch-beta") + shell.NewBranch("branch-gamma") + shell.NewBranch("checked-out-branch") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Branches(). + Focus(). + Lines( + Contains("checked-out-branch").IsSelected(), + Contains("branch-alpha"), + Contains("branch-beta"), + Contains("branch-gamma"), + Contains("master"), + ). + FilterOrSearch("branch-"). + Lines( + Contains("branch-alpha").IsSelected(), + Contains("branch-beta"), + Contains("branch-gamma"), + ). + // Move cursor to a non-zero position + SelectNextItem(). + SelectNextItem(). + Lines( + Contains("branch-alpha"), + Contains("branch-beta"), + Contains("branch-gamma").IsSelected(), + ) + + // Trigger a model update while staying on the Branches view. + // Using a shell command that creates a new branch sorting after + // branch-gamma, so the selection index still points to the same item. + t.GlobalPress(keys.Universal.ExecuteShellCommand) + t.ExpectPopup().Prompt(). + Title(Equals("Shell command:")). + Type("git branch branch-zeta"). + Confirm() + + // Verify that the selection is still on branch-gamma (not reset to 0) + t.Views().Branches(). + Lines( + Contains("branch-alpha"), + Contains("branch-beta"), + Contains("branch-gamma").IsSelected(), + Contains("branch-zeta"), + ) + }, +}) diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index 804a8764f..b98de18d8 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -238,6 +238,7 @@ var tests = []*components.IntegrationTest{ filter_and_search.FilterMenuByKeybinding, filter_and_search.FilterMenuCancelFilterWithEscape, filter_and_search.FilterMenuWithNoKeybindings, + filter_and_search.FilterPreservesSelectionOnModelChange, filter_and_search.FilterRemoteBranches, filter_and_search.FilterRemotes, filter_and_search.FilterSearchHistory,