From 3eb5841b831e3ee88a02b94b16e71f47344f3b01 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Wed, 25 Mar 2026 18:55:28 +0100 Subject: [PATCH] Fix searching commits or main view after switching repos When switching to a different repo, and then back to the original one, searching would no longer work. The reason is that our contexts set callbacks on their views; when switching to a different repo we instantiate a new set of contexts, so they will overwrite the views' callbacks with their new ones, but when switching back to the original repo, we reuse the old contexts because they are still in memory, but they won't set their callbacks again since they only do this on construction. To fix this, replace the view-local callbacks with a global one on the gui that takes the view as an argument, so that the callback can look up the associated context dynamically. --- go.mod | 8 +-- go.sum | 16 +++--- pkg/gui/context/local_commits_context.go | 3 - pkg/gui/context/main_context.go | 6 +- pkg/gui/context/patch_explorer_context.go | 17 +++--- pkg/gui/context/sub_commits_context.go | 3 - pkg/gui/gui.go | 18 ++++++ pkg/gui/types/context.go | 1 + vendor/github.com/jesseduffield/gocui/gui.go | 55 ++++++++++++++----- vendor/github.com/jesseduffield/gocui/view.go | 12 ++-- vendor/modules.txt | 14 ++--- 11 files changed, 95 insertions(+), 58 deletions(-) diff --git a/go.mod b/go.mod index 1e35a0ca2..76287c0e2 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/gookit/color v1.4.2 github.com/integrii/flaggy v1.4.0 github.com/jesseduffield/generics v0.0.0-20250517122708-b0b4a53a6f5c - github.com/jesseduffield/gocui v0.3.1-0.20260308162933-5e45e57b5564 + github.com/jesseduffield/gocui v0.3.1-0.20260327132312-944dab3bc980 github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5 github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 github.com/karimkhaleel/jsonschema v0.0.0-20231001195015-d933f0d94ea3 @@ -37,7 +37,7 @@ require ( github.com/stretchr/testify v1.10.0 github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 - golang.org/x/sync v0.19.0 + golang.org/x/sync v0.20.0 golang.org/x/sys v0.42.0 gopkg.in/ozeidan/fuzzy-patricia.v3 v3.0.0 gopkg.in/yaml.v3 v3.0.1 @@ -66,8 +66,8 @@ require ( github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect golang.org/x/net v0.47.0 // indirect - golang.org/x/term v0.40.0 // indirect - golang.org/x/text v0.34.0 // indirect + golang.org/x/term v0.41.0 // indirect + golang.org/x/text v0.35.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/fsnotify.v1 v1.4.7 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect diff --git a/go.sum b/go.sum index ca4fa7e66..996508bcf 100644 --- a/go.sum +++ b/go.sum @@ -159,8 +159,8 @@ github.com/invopop/jsonschema v0.10.0 h1:c1ktzNLBun3LyQQhyty5WE3lulbOdIIyOVlkmDL github.com/invopop/jsonschema v0.10.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0= github.com/jesseduffield/generics v0.0.0-20250517122708-b0b4a53a6f5c h1:tC2PaiisXAC5sOjDPfMArSnbswDObtCssx+xn28edX4= github.com/jesseduffield/generics v0.0.0-20250517122708-b0b4a53a6f5c/go.mod h1:F2fEBk0ddf6ixrBrJjY7phfQ3hL9rXG0uSjvwYe50bE= -github.com/jesseduffield/gocui v0.3.1-0.20260308162933-5e45e57b5564 h1:aB/Ytu+OCEpjft/BehqbH8/PTdgLREqbCvjK1JXctoo= -github.com/jesseduffield/gocui v0.3.1-0.20260308162933-5e45e57b5564/go.mod h1:lQCd2TvvNXVKFBowy4A7xxZbUp+1KEiGs4j0Q5Zt9gQ= +github.com/jesseduffield/gocui v0.3.1-0.20260327132312-944dab3bc980 h1:LEZwOrBm9S+4lRlXpoz+RSzSvhOVE+6v/Rk+A7Kg00Q= +github.com/jesseduffield/gocui v0.3.1-0.20260327132312-944dab3bc980/go.mod h1:lQCd2TvvNXVKFBowy4A7xxZbUp+1KEiGs4j0Q5Zt9gQ= github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5 h1:CDuQmfOjAtb1Gms6a1p5L2P8RhbLUq5t8aL7PiQd2uY= github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5/go.mod h1:qxN4mHOAyeIDLP7IK7defgPClM/z1Kze8VVQiaEjzsQ= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= @@ -366,8 +366,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= -golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20170407050850-f3918c30c5c2/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -419,8 +419,8 @@ golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg= -golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= +golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU= +golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -431,8 +431,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= -golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= +golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= +golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/pkg/gui/context/local_commits_context.go b/pkg/gui/context/local_commits_context.go index a730d6530..55415c761 100644 --- a/pkg/gui/context/local_commits_context.go +++ b/pkg/gui/context/local_commits_context.go @@ -134,9 +134,6 @@ func NewLocalCommitsContext(c *ContextCommon) *LocalCommitsContext { }, } - ctx.GetView().SetRenderSearchStatus(ctx.SearchTrait.RenderSearchStatus) - ctx.GetView().SetOnSelectItem(ctx.OnSearchSelect) - return ctx } diff --git a/pkg/gui/context/main_context.go b/pkg/gui/context/main_context.go index 716f20bca..f749c4be2 100644 --- a/pkg/gui/context/main_context.go +++ b/pkg/gui/context/main_context.go @@ -31,12 +31,12 @@ func NewMainContext( SearchTrait: NewSearchTrait(c), } - ctx.GetView().SetRenderSearchStatus(ctx.SearchTrait.RenderSearchStatus) - ctx.GetView().SetOnSelectItem(func(int) {}) - return ctx } func (self *MainContext) ModelSearchResults(searchStr string, caseSensitive bool) []gocui.SearchPosition { return nil } + +func (self *MainContext) OnSearchSelect(int) { +} diff --git a/pkg/gui/context/patch_explorer_context.go b/pkg/gui/context/patch_explorer_context.go index 082800127..167bdb41f 100644 --- a/pkg/gui/context/patch_explorer_context.go +++ b/pkg/gui/context/patch_explorer_context.go @@ -53,15 +53,6 @@ func NewPatchExplorerContext( SearchTrait: NewSearchTrait(c), } - ctx.GetView().SetRenderSearchStatus(ctx.SearchTrait.RenderSearchStatus) - ctx.GetView().SetOnSelectItem(func(selectedLineIdx int) { - ctx.GetMutex().Lock() - defer ctx.GetMutex().Unlock() - ctx.inOnSelectItemCallback = true - ctx.NavigateTo(selectedLineIdx) - ctx.inOnSelectItemCallback = false - }) - ctx.SetHandleRenderFunc(ctx.OnViewWidthChanged) return ctx @@ -146,6 +137,14 @@ func (self *PatchExplorerContext) ModelSearchResults(searchStr string, caseSensi return nil } +func (self *PatchExplorerContext) OnSearchSelect(selectedLineIdx int) { + self.GetMutex().Lock() + defer self.GetMutex().Unlock() + self.inOnSelectItemCallback = true + self.NavigateTo(selectedLineIdx) + self.inOnSelectItemCallback = false +} + func (self *PatchExplorerContext) OnViewWidthChanged() { if state := self.GetState(); state != nil { state.OnViewWidthChanged(self.GetView()) diff --git a/pkg/gui/context/sub_commits_context.go b/pkg/gui/context/sub_commits_context.go index 7e9d9ccab..55a06f286 100644 --- a/pkg/gui/context/sub_commits_context.go +++ b/pkg/gui/context/sub_commits_context.go @@ -134,9 +134,6 @@ func NewSubCommitsContext( }, } - ctx.GetView().SetRenderSearchStatus(ctx.SearchTrait.RenderSearchStatus) - ctx.GetView().SetOnSelectItem(ctx.OnSearchSelect) - return ctx } diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index 34d388391..50176e013 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -398,6 +398,24 @@ func (gui *Gui) onNewRepo(startArgs appTypes.StartArgs, contextKey types.Context return nil }) + gui.g.SetOnSelectSearchResultFunc(func(v *gocui.View, selectedLineIdx int) { + ctx, ok := gui.helpers.View.ContextForView(v.Name()) + if ok { + if searchableContext, ok := ctx.(types.ISearchableContext); ok { + searchableContext.OnSearchSelect(selectedLineIdx) + } + } + }) + + gui.g.SetRenderSearchStatusFunc(func(v *gocui.View, index int, total int) { + ctx, ok := gui.helpers.View.ContextForView(v.Name()) + if ok { + if searchableContext, ok := ctx.(types.ISearchableContext); ok { + searchableContext.RenderSearchStatus(index, total) + } + } + }) + // if a context key has been given, push that instead, and set its index to 0 if contextKey != context.NO_CONTEXT { contextToPush = gui.c.ContextForKey(contextKey) diff --git a/pkg/gui/types/context.go b/pkg/gui/types/context.go index afc8e11eb..09ed94e5a 100644 --- a/pkg/gui/types/context.go +++ b/pkg/gui/types/context.go @@ -151,6 +151,7 @@ type ISearchableContext interface { // This must be implemented by each concrete context. Return nil if not searching the model. ModelSearchResults(searchStr string, caseSensitive bool) []gocui.SearchPosition + OnSearchSelect(selectedLineIdx int) } type DiffableContext interface { diff --git a/vendor/github.com/jesseduffield/gocui/gui.go b/vendor/github.com/jesseduffield/gocui/gui.go index 96c622b3a..0d97e280b 100644 --- a/vendor/github.com/jesseduffield/gocui/gui.go +++ b/vendor/github.com/jesseduffield/gocui/gui.go @@ -139,21 +139,23 @@ type Gui struct { ReplayedEvents replayedEvents playRecording bool - tabClickBindings []*tabClickBinding - viewMouseBindings []*ViewMouseBinding - lastClick *clickInfo - gEvents chan GocuiEvent - userEvents chan userEvent - views []*View - currentView *View - managers []Manager - keybindings []*keybinding - focusHandler func(bool) error - openHyperlink func(string, string) error - maxX, maxY int - outputMode OutputMode - stop chan struct{} - blacklist []Key + tabClickBindings []*tabClickBinding + viewMouseBindings []*ViewMouseBinding + lastClick *clickInfo + gEvents chan GocuiEvent + userEvents chan userEvent + views []*View + currentView *View + managers []Manager + keybindings []*keybinding + focusHandler func(bool) error + openHyperlink func(string, string) error + onSelectSearchResultFunc func(*View, int) + renderSearchStatusFunc func(*View, int, int) + maxX, maxY int + outputMode OutputMode + stop chan struct{} + blacklist []Key // BgColor and FgColor allow to configure the background and foreground // colors of the GUI. @@ -355,11 +357,26 @@ func (g *Gui) SetView(name string, x0, y0, x1, y1 int, overlaps byte) (*View, er v.Overlaps = overlaps g.views = append(g.views, v) + v.setOnSelectResult(g.onSelectSearchItem) + v.setRenderSearchStatus(g.renderSearchStatus) + g.Mutexes.ViewsMutex.Unlock() return v, errors.Wrap(ErrUnknownView, 0) } +func (g *Gui) onSelectSearchItem(v *View, selectedLineIdx int) { + if g.onSelectSearchResultFunc != nil { + g.onSelectSearchResultFunc(v, selectedLineIdx) + } +} + +func (g *Gui) renderSearchStatus(v *View, selected int, total int) { + if g.renderSearchStatusFunc != nil { + g.renderSearchStatusFunc(v, selected, total) + } +} + // SetViewBeneath sets a view stacked beneath another view func (g *Gui) SetViewBeneath(name string, aboveViewName string, height int) (*View, error) { aboveView, err := g.View(aboveViewName) @@ -643,6 +660,14 @@ func (g *Gui) SetOpenHyperlinkFunc(openHyperlinkFunc func(string, string) error) g.openHyperlink = openHyperlinkFunc } +func (g *Gui) SetOnSelectSearchResultFunc(onSelectSearchResultFunc func(*View, int)) { + g.onSelectSearchResultFunc = onSelectSearchResultFunc +} + +func (g *Gui) SetRenderSearchStatusFunc(renderSearchStatusFunc func(*View, int, int)) { + g.renderSearchStatusFunc = renderSearchStatusFunc +} + // getKey takes an empty interface with a key and returns the corresponding // typed Key or rune. func getKey(key any) (Key, rune, error) { diff --git a/vendor/github.com/jesseduffield/gocui/view.go b/vendor/github.com/jesseduffield/gocui/view.go index 9ba8b7efc..16da0a380 100644 --- a/vendor/github.com/jesseduffield/gocui/view.go +++ b/vendor/github.com/jesseduffield/gocui/view.go @@ -214,21 +214,21 @@ type searcher struct { searchPositions []SearchPosition modelSearchResults []SearchPosition currentSearchIndex int - onSelectItem func(int) - renderSearchStatus func(int, int) + onSelectItem func(*View, int) + renderSearchStatus func(*View, int, int) } -func (v *View) SetRenderSearchStatus(renderSearchStatus func(int, int)) { +func (v *View) setRenderSearchStatus(renderSearchStatus func(*View, int, int)) { v.searcher.renderSearchStatus = renderSearchStatus } -func (v *View) SetOnSelectItem(onSelectItem func(int)) { +func (v *View) setOnSelectResult(onSelectItem func(*View, int)) { v.searcher.onSelectItem = onSelectItem } func (v *View) renderSearchStatus(index int, itemCount int) { if v.searcher.renderSearchStatus != nil { - v.searcher.renderSearchStatus(index, itemCount) + v.searcher.renderSearchStatus(v, index, itemCount) } } @@ -286,7 +286,7 @@ func (v *View) SelectSearchResult(index int) { v.FocusPoint(v.ox, y, true) v.renderSearchStatus(index, itemCount) if v.searcher.onSelectItem != nil { - v.searcher.onSelectItem(y) + v.searcher.onSelectItem(v, y) } } diff --git a/vendor/modules.txt b/vendor/modules.txt index fce15cd50..222e83cfd 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -89,7 +89,7 @@ github.com/integrii/flaggy github.com/jesseduffield/generics/maps github.com/jesseduffield/generics/orderedset github.com/jesseduffield/generics/set -# github.com/jesseduffield/gocui v0.3.1-0.20260308162933-5e45e57b5564 +# github.com/jesseduffield/gocui v0.3.1-0.20260327132312-944dab3bc980 ## explicit; go 1.25 github.com/jesseduffield/gocui # github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5 @@ -188,19 +188,19 @@ golang.org/x/exp/constraints golang.org/x/exp/slices # golang.org/x/net v0.47.0 ## explicit; go 1.24.0 -# golang.org/x/sync v0.19.0 -## explicit; go 1.24.0 +# golang.org/x/sync v0.20.0 +## explicit; go 1.25.0 golang.org/x/sync/errgroup # golang.org/x/sys v0.42.0 ## explicit; go 1.25.0 golang.org/x/sys/plan9 golang.org/x/sys/unix golang.org/x/sys/windows -# golang.org/x/term v0.40.0 -## explicit; go 1.24.0 +# golang.org/x/term v0.41.0 +## explicit; go 1.25.0 golang.org/x/term -# golang.org/x/text v0.34.0 -## explicit; go 1.24.0 +# golang.org/x/text v0.35.0 +## explicit; go 1.25.0 golang.org/x/text/encoding golang.org/x/text/encoding/internal/identifier golang.org/x/text/runes