Hold the file-path suggestions trie outside the model

The file-path suggestions trie is rebuilt asynchronously and then read by
the suggestions search, which runs on an AsyncHandler worker. It lived in
Model().FilesTrie, so that worker read the (UI-thread-only) model. Move
it to an atomic pointer on the SuggestionsHelper instead: it's the only
place that uses it, the helper is recreated per repo (so the cache still
resets on a repo switch), and an atomic pointer is safe to store from the
build and load from the search worker.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Stefan Haller 2026-07-14 13:37:38 +02:00
parent 25a3689c01
commit 9f2886f96f
3 changed files with 15 additions and 13 deletions

View file

@ -3,6 +3,7 @@ package helpers
import (
"fmt"
"strings"
"sync/atomic"
"github.com/jesseduffield/generics/set"
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
@ -28,14 +29,20 @@ import (
type SuggestionsHelper struct {
c *HelperCommon
// filesTrie holds the repo's file paths for file-path suggestions. It's
// rebuilt asynchronously and read from the suggestions worker goroutine, so
// it lives here as an atomic pointer rather than in the (UI-thread-only)
// model.
filesTrie atomic.Pointer[patricia.Trie]
}
func NewSuggestionsHelper(
c *HelperCommon,
) *SuggestionsHelper {
return &SuggestionsHelper{
c: c,
}
self := &SuggestionsHelper{c: c}
self.filesTrie.Store(patricia.NewTrie())
return self
}
func (self *SuggestionsHelper) getRemoteNames() []string {
@ -137,9 +144,9 @@ func (self *SuggestionsHelper) GetFilePathSuggestionsFunc() func(string) []*type
trie.Insert(patricia.Prefix(file), file)
}
// cache the trie for future use
self.filesTrie.Store(trie)
self.c.OnUIThread(func() error {
// cache the trie for future use
self.c.Model().FilesTrie = trie
self.c.Contexts().Suggestions.RefreshSuggestions()
return nil
})
@ -148,9 +155,10 @@ func (self *SuggestionsHelper) GetFilePathSuggestionsFunc() func(string) []*type
})
return func(input string) []*types.Suggestion {
filesTrie := self.filesTrie.Load()
matchingNames := []string{}
if self.c.UserConfig().Gui.UseFuzzySearch() {
_ = self.c.Model().FilesTrie.VisitFuzzy(patricia.Prefix(input), true, func(prefix patricia.Prefix, item patricia.Item, skipped int) error {
_ = filesTrie.VisitFuzzy(patricia.Prefix(input), true, func(prefix patricia.Prefix, item patricia.Item, skipped int) error {
matchingNames = append(matchingNames, item.(string))
return nil
})
@ -159,7 +167,7 @@ func (self *SuggestionsHelper) GetFilePathSuggestionsFunc() func(string) []*type
matchingNames = utils.FilterStrings(input, matchingNames, true)
} else {
substrings := strings.Fields(input)
_ = self.c.Model().FilesTrie.Visit(func(prefix patricia.Prefix, item patricia.Item) error {
_ = filesTrie.Visit(func(prefix patricia.Prefix, item patricia.Item) error {
for _, sub := range substrings {
if !utils.CaseAwareContains(item.(string), sub) {
return nil

View file

@ -49,7 +49,6 @@ import (
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/samber/lo"
"github.com/sasha-s/go-deadlock"
"gopkg.in/ozeidan/fuzzy-patricia.v3/patricia"
)
const StartupPopupVersion = 5
@ -639,7 +638,6 @@ func (gui *Gui) resetState(startArgs appTypes.StartArgs) types.Context {
FilteredReflogCommits: make([]*models.Commit, 0),
ReflogCommits: make([]*models.Commit, 0),
BisectInfo: git_commands.NewNullBisectInfo(),
FilesTrie: patricia.NewTrie(),
Authors: map[string]*models.Author{},
MainBranches: git_commands.NewMainBranches(gui.c.Common, gui.os.Cmd),
HashPool: &utils.StringPool{},

View file

@ -11,7 +11,6 @@ import (
"github.com/jesseduffield/lazygit/pkg/tasks"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/sasha-s/go-deadlock"
"gopkg.in/ozeidan/fuzzy-patricia.v3/patricia"
)
type HelperCommon struct {
@ -348,9 +347,6 @@ type Model struct {
MainBranches *git_commands.MainBranches
// for displaying suggestions while typing in a file name
FilesTrie *patricia.Trie
Authors map[string]*models.Author
HashPool *utils.StringPool