From fe2ecfc60f9aaef7be884d3c80664ec71f508794 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Sat, 5 Sep 2026 10:36:55 +0200 Subject: [PATCH] Follow the selection with the search in the focused main view Search the diff, then move the selection with the arrow keys, and pressing "n" goes to the match after the one the search was last on rather than to the one after the selection. So walking through the diff and then picking up the search where you got to takes as many presses as there are matches behind you. List views and the staging view keep the current match in step with their selection; do the same here. Co-authored-by: Claude Opus 5 (1M context) --- pkg/gui/controllers/main_view_controller.go | 9 ++++ .../main_view/search_follows_the_selection.go | 44 +++++++++++++++++++ pkg/integration/tests/test_list.go | 1 + 3 files changed, 54 insertions(+) create mode 100644 pkg/integration/tests/main_view/search_follows_the_selection.go diff --git a/pkg/gui/controllers/main_view_controller.go b/pkg/gui/controllers/main_view_controller.go index b40055025..a5ad2aff4 100644 --- a/pkg/gui/controllers/main_view_controller.go +++ b/pkg/gui/controllers/main_view_controller.go @@ -329,6 +329,10 @@ func (self *MainViewController) onDragInFocusedView(opts gocui.ViewMouseBindingO func (self *MainViewController) onDragRelease(gocui.ViewMouseBindingOpts) error { self.draggingWithMouse = false self.dragAutoscroller.Cancel() + + // The drag moved the selection without going through showSelectionAtLine: gocui + // moves the cursor for it. Let the search catch up with where it ended. + self.context.GetView().SetNearestSearchPosition() return nil } @@ -489,6 +493,11 @@ func hunkModeApplies(c *ControllerCommon, view *gocui.View, changeViewLine int) // screen already. func showSelectionAtLine(view *gocui.View, lineIdx int, scrollIntoView bool) { view.FocusPoint(0, lo.Clamp(lineIdx, 0, max(0, view.ViewLinesHeight()-1)), scrollIntoView) + + // A search carries on from where the selection now is, so that stepping to the + // next match goes to the one after it rather than the one after the match the + // user last stepped to. + view.SetNearestSearchPosition() } func (self *MainViewController) selectHunkAround(changeViewLine int, scrollIntoView bool) { diff --git a/pkg/integration/tests/main_view/search_follows_the_selection.go b/pkg/integration/tests/main_view/search_follows_the_selection.go new file mode 100644 index 000000000..55048cf0f --- /dev/null +++ b/pkg/integration/tests/main_view/search_follows_the_selection.go @@ -0,0 +1,44 @@ +package main_view + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var SearchFollowsTheSelection = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Stepping to the next match in the focused main view carries on from the selection", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell *Shell) { + shell.CreateFileAndAdd("file1", "") + shell.Commit("one") + + shell.UpdateFile("file1", "NEEDLE a\ntwo\nNEEDLE b\nfour\nNEEDLE c\n") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Files(). + IsFocused(). + Press(keys.Universal.FocusMainView) + + t.Views().Main(). + IsFocused(). + FilterOrSearch("NEEDLE"). + SelectedLines(Contains("+NEEDLE a")). + Tap(func() { + t.Views().Search().Content(Contains("matches for 'NEEDLE' (1 of 3)")) + }). + // Move the selection past the second match by hand. + SelectNextItem(). + SelectNextItem(). + SelectNextItem(). + SelectedLines(Contains("+four")). + Tap(func() { + t.Views().Search().Content(Contains("matches for 'NEEDLE' (2 of 3)")) + }). + // So the next match is the one after where the selection is, not the one + // after the match it was last on. + Press(keys.Universal.NextMatch). + SelectedLines(Contains("+NEEDLE c")) + }, +}) diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index 363c3a265..e4df6b23f 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -378,6 +378,7 @@ var tests = []*components.IntegrationTest{ main_view.NoSelectionWhenNoChanges, main_view.RangeSelectDiffLines, main_view.SearchCollapsesTheSelection, + main_view.SearchFollowsTheSelection, main_view.SelectBelowALongCommitMessage, main_view.SelectBelowALongDiffstat, main_view.SelectDiffLines,