From 1a313f48ec239ce5dc1b0e70bafd5be9c564c9c2 Mon Sep 17 00:00:00 2001 From: "L. Yeung" Date: Mon, 11 Nov 2024 00:36:33 +0800 Subject: [PATCH] feat(cache): make template cache available to all prompts --- src/cache/template.go | 1 - src/cli/print.go | 1 + src/config/segment.go | 2 + src/maps/concurrent.go | 50 +++++++----------------- src/maps/simple.go | 6 ++- src/prompt/engine.go | 20 +++++----- src/prompt/segments_test.go | 4 +- src/runtime/environment.go | 4 +- src/runtime/mock/environment.go | 4 -- src/runtime/terminal.go | 69 ++++++++++++++++----------------- src/template/render_pool.go | 5 --- 11 files changed, 69 insertions(+), 97 deletions(-) diff --git a/src/cache/template.go b/src/cache/template.go index 27631f79..7906eff2 100644 --- a/src/cache/template.go +++ b/src/cache/template.go @@ -23,7 +23,6 @@ type Template struct { Jobs int WSL bool Root bool - Initialized bool } func (t *Template) AddSegmentData(key string, value any) { diff --git a/src/cli/print.go b/src/cli/print.go index ecafbde8..b4e4d0d9 100644 --- a/src/cli/print.go +++ b/src/cli/print.go @@ -76,6 +76,7 @@ func createPrintCmd() *cobra.Command { NoExitCode: noStatus, Column: column, JobCount: jobCount, + IsPrimary: args[0] == prompt.PRIMARY, SaveCache: saveCache, } diff --git a/src/config/segment.go b/src/config/segment.go index a21b4a38..2fe6ae97 100644 --- a/src/config/segment.go +++ b/src/config/segment.go @@ -140,6 +140,8 @@ func (segment *Segment) Render() { segment.SetText(text) segment.setCache() + + // We do this to make `.Text` available for a cross-segment reference in an extra prompt. segment.env.TemplateCache().AddSegmentData(segment.Name(), segment.writer) } diff --git a/src/maps/concurrent.go b/src/maps/concurrent.go index 7db17305..c5bb1537 100644 --- a/src/maps/concurrent.go +++ b/src/maps/concurrent.go @@ -1,58 +1,36 @@ package maps -import ( - "sync" -) +import "sync" func NewConcurrent() *Concurrent { - return &Concurrent{ - data: make(map[string]any), - } + var cm Concurrent + return &cm } -type Concurrent struct { - data map[string]any - sync.RWMutex -} +type Concurrent sync.Map func (cm *Concurrent) Set(key string, value any) { - cm.Lock() - defer cm.Unlock() - - if cm.data == nil { - cm.data = make(map[string]any) - } - - cm.data[key] = value + (*sync.Map)(cm).Store(key, value) } func (cm *Concurrent) Get(key string) (any, bool) { - cm.RLock() - defer cm.RUnlock() - - if cm.data == nil { - return nil, false - } - - value, ok := cm.data[key] - return value, ok + return (*sync.Map)(cm).Load(key) } func (cm *Concurrent) Delete(key string) { - cm.Lock() - defer cm.Unlock() - - delete(cm.data, key) + (*sync.Map)(cm).Delete(key) } func (cm *Concurrent) Contains(key string) bool { - _, ok := cm.Get(key) + _, ok := (*sync.Map)(cm).Load(key) return ok } func (cm *Concurrent) ToSimple() Simple { - cm.RLock() - defer cm.RUnlock() - - return cm.data + list := make(map[string]any) + (*sync.Map)(cm).Range(func(key, value any) bool { + list[key.(string)] = value + return true + }) + return list } diff --git a/src/maps/simple.go b/src/maps/simple.go index a1f39c1a..696a4550 100644 --- a/src/maps/simple.go +++ b/src/maps/simple.go @@ -3,7 +3,9 @@ package maps type Simple map[string]any func (m Simple) ToConcurrent() *Concurrent { - return &Concurrent{ - data: m, + var cm Concurrent + for k, v := range m { + cm.Set(k, v) } + return &cm } diff --git a/src/prompt/engine.go b/src/prompt/engine.go index 20d8c2e8..b95212dd 100644 --- a/src/prompt/engine.go +++ b/src/prompt/engine.go @@ -468,21 +468,19 @@ func New(flags *runtime.Flags) *Engine { env.Init() cfg := config.Load(env) + env.Var = cfg.Var - // load the template cache for extra prompts prior to - // rendering any template - if flags.Type == DEBUG || - flags.Type == SECONDARY || - flags.Type == TRANSIENT || - flags.Type == VALID || - flags.Type == ERROR { - env.LoadTemplateCache() - } + // To prevent cross-segment template referencing issues, this should not be moved elsewhere. + // Related: https://github.com/JanDeDobbeleer/oh-my-posh/discussions/2885#discussioncomment-4497439 + env.PopulateTemplateCache() template.Init(env) - env.Var = cfg.Var - flags.HasTransient = cfg.TransientPrompt != nil + flags.HasExtra = cfg.DebugPrompt != nil || + cfg.SecondaryPrompt != nil || + cfg.TransientPrompt != nil || + cfg.ValidLine != nil || + cfg.ErrorLine != nil terminal.Init(env.Shell()) terminal.BackgroundColor = cfg.TerminalBackground.ResolveTemplate() diff --git a/src/prompt/segments_test.go b/src/prompt/segments_test.go index eae4e022..fc5231ee 100644 --- a/src/prompt/segments_test.go +++ b/src/prompt/segments_test.go @@ -10,7 +10,9 @@ import ( ) func TestRenderBlock(t *testing.T) { - engine := New(&runtime.Flags{}) + engine := New(&runtime.Flags{ + IsPrimary: true, + }) block := &config.Block{ Segments: []*config.Segment{ { diff --git a/src/runtime/environment.go b/src/runtime/environment.go index bde949f5..64f9abdf 100644 --- a/src/runtime/environment.go +++ b/src/runtime/environment.go @@ -69,7 +69,6 @@ type Environment interface { ConvertToWindowsPath(path string) string Connection(connectionType ConnectionType) (*Connection, error) TemplateCache() *cache.Template - LoadTemplateCache() CursorPosition() (row, col int) SystemInfo() (*SystemInfo, error) Debug(message string) @@ -93,7 +92,8 @@ type Flags struct { TerminalWidth int ExecutionTime float64 JobCount int - HasTransient bool + IsPrimary bool + HasExtra bool Debug bool Plain bool Strict bool diff --git a/src/runtime/mock/environment.go b/src/runtime/mock/environment.go index 50f7d63e..a0e3d045 100644 --- a/src/runtime/mock/environment.go +++ b/src/runtime/mock/environment.go @@ -231,10 +231,6 @@ func (env *Environment) TemplateCache() *cache.Template { return args.Get(0).(*cache.Template) } -func (env *Environment) LoadTemplateCache() { - _ = env.Called() -} - func (env *Environment) MockGitCommand(dir, returnValue string, args ...string) { args = append([]string{"-C", dir, "--no-optional-locks", "-c", "core.quotepath=false", "-c", "color.status=false"}, args...) env.On("RunCommand", "git", args).Return(returnValue, nil) diff --git a/src/runtime/terminal.go b/src/runtime/terminal.go index 63727b95..53bebffe 100644 --- a/src/runtime/terminal.go +++ b/src/runtime/terminal.go @@ -37,10 +37,10 @@ type Terminal struct { deviceCache *cache.File sessionCache *cache.File tmplCache *cache.Template + lsDirMap maps.Concurrent cwd string host string networks []*Connection - lsDirMap maps.Concurrent } func (term *Terminal) Init() { @@ -78,7 +78,7 @@ func (term *Terminal) Init() { Commands: maps.NewConcurrent(), } - term.tmplCache = &cache.Template{} + term.tmplCache = new(cache.Template) } func (term *Terminal) ResolveConfigPath() { @@ -580,8 +580,8 @@ func (term *Terminal) Session() cache.Cache { func (term *Terminal) saveTemplateCache() { // only store this when in a primary prompt - // and when we have a transient prompt in the config - canSave := term.CmdFlags.Type == PRIMARY && term.CmdFlags.HasTransient + // and when we have any extra prompt in the config + canSave := term.CmdFlags.Type == PRIMARY && term.CmdFlags.HasExtra if !canSave { return } @@ -619,40 +619,15 @@ func (term *Terminal) clearCacheFiles() { } } -func (term *Terminal) LoadTemplateCache() { - defer term.Trace(time.Now()) - - val, OK := term.sessionCache.Get(cache.TEMPLATECACHE) - if !OK { +func (term *Terminal) PopulateTemplateCache() { + if !term.CmdFlags.IsPrimary { + // Load the template cache for a non-primary prompt before rendering any templates. + term.loadTemplateCache() return } - var tmplCache cache.Template - - err := json.Unmarshal([]byte(val), &tmplCache) - if err != nil { - term.Error(err) - return - } - - tmplCache.Segments = tmplCache.SegmentsCache.ToConcurrent() - tmplCache.Initialized = true - - term.tmplCache = &tmplCache -} - -func (term *Terminal) Logs() string { - return log.String() -} - -func (term *Terminal) TemplateCache() *cache.Template { - defer term.Trace(time.Now()) tmplCache := term.tmplCache - if tmplCache.Initialized { - return tmplCache - } - tmplCache.Root = term.Root() tmplCache.Shell = term.Shell() tmplCache.ShellVersion = term.CmdFlags.ShellVersion @@ -697,9 +672,33 @@ func (term *Terminal) TemplateCache() *cache.Template { if shlvl, err := strconv.Atoi(val); err == nil { tmplCache.SHLVL = shlvl } +} - tmplCache.Initialized = true - return tmplCache +func (term *Terminal) loadTemplateCache() { + defer term.Trace(time.Now()) + + val, OK := term.sessionCache.Get(cache.TEMPLATECACHE) + if !OK { + return + } + + tmplCache := term.tmplCache + + err := json.Unmarshal([]byte(val), &tmplCache) + if err != nil { + term.Error(err) + return + } + + tmplCache.Segments = tmplCache.SegmentsCache.ToConcurrent() +} + +func (term *Terminal) Logs() string { + return log.String() +} + +func (term *Terminal) TemplateCache() *cache.Template { + return term.tmplCache } func (term *Terminal) DirMatchesOneOf(dir string, regexes []string) (match bool) { diff --git a/src/template/render_pool.go b/src/template/render_pool.go index 1eb7f162..6f576fe1 100644 --- a/src/template/render_pool.go +++ b/src/template/render_pool.go @@ -20,11 +20,6 @@ type context struct { func (c *context) init(t *Text) { c.Data = t.Context - - if c.Initialized { - return - } - c.Getenv = env.Getenv c.Template = *env.TemplateCache() }