feat(cache): make template cache available to all prompts

This commit is contained in:
L. Yeung 2024-11-11 00:36:33 +08:00 committed by Jan De Dobbeleer
parent 69f7f06d4e
commit 1a313f48ec
11 changed files with 69 additions and 97 deletions

View file

@ -23,7 +23,6 @@ type Template struct {
Jobs int Jobs int
WSL bool WSL bool
Root bool Root bool
Initialized bool
} }
func (t *Template) AddSegmentData(key string, value any) { func (t *Template) AddSegmentData(key string, value any) {

View file

@ -76,6 +76,7 @@ func createPrintCmd() *cobra.Command {
NoExitCode: noStatus, NoExitCode: noStatus,
Column: column, Column: column,
JobCount: jobCount, JobCount: jobCount,
IsPrimary: args[0] == prompt.PRIMARY,
SaveCache: saveCache, SaveCache: saveCache,
} }

View file

@ -140,6 +140,8 @@ func (segment *Segment) Render() {
segment.SetText(text) segment.SetText(text)
segment.setCache() 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) segment.env.TemplateCache().AddSegmentData(segment.Name(), segment.writer)
} }

View file

@ -1,58 +1,36 @@
package maps package maps
import ( import "sync"
"sync"
)
func NewConcurrent() *Concurrent { func NewConcurrent() *Concurrent {
return &Concurrent{ var cm Concurrent
data: make(map[string]any), return &cm
}
} }
type Concurrent struct { type Concurrent sync.Map
data map[string]any
sync.RWMutex
}
func (cm *Concurrent) Set(key string, value any) { func (cm *Concurrent) Set(key string, value any) {
cm.Lock() (*sync.Map)(cm).Store(key, value)
defer cm.Unlock()
if cm.data == nil {
cm.data = make(map[string]any)
}
cm.data[key] = value
} }
func (cm *Concurrent) Get(key string) (any, bool) { func (cm *Concurrent) Get(key string) (any, bool) {
cm.RLock() return (*sync.Map)(cm).Load(key)
defer cm.RUnlock()
if cm.data == nil {
return nil, false
}
value, ok := cm.data[key]
return value, ok
} }
func (cm *Concurrent) Delete(key string) { func (cm *Concurrent) Delete(key string) {
cm.Lock() (*sync.Map)(cm).Delete(key)
defer cm.Unlock()
delete(cm.data, key)
} }
func (cm *Concurrent) Contains(key string) bool { func (cm *Concurrent) Contains(key string) bool {
_, ok := cm.Get(key) _, ok := (*sync.Map)(cm).Load(key)
return ok return ok
} }
func (cm *Concurrent) ToSimple() Simple { func (cm *Concurrent) ToSimple() Simple {
cm.RLock() list := make(map[string]any)
defer cm.RUnlock() (*sync.Map)(cm).Range(func(key, value any) bool {
list[key.(string)] = value
return cm.data return true
})
return list
} }

View file

@ -3,7 +3,9 @@ package maps
type Simple map[string]any type Simple map[string]any
func (m Simple) ToConcurrent() *Concurrent { func (m Simple) ToConcurrent() *Concurrent {
return &Concurrent{ var cm Concurrent
data: m, for k, v := range m {
cm.Set(k, v)
} }
return &cm
} }

View file

@ -468,21 +468,19 @@ func New(flags *runtime.Flags) *Engine {
env.Init() env.Init()
cfg := config.Load(env) cfg := config.Load(env)
env.Var = cfg.Var
// load the template cache for extra prompts prior to // To prevent cross-segment template referencing issues, this should not be moved elsewhere.
// rendering any template // Related: https://github.com/JanDeDobbeleer/oh-my-posh/discussions/2885#discussioncomment-4497439
if flags.Type == DEBUG || env.PopulateTemplateCache()
flags.Type == SECONDARY ||
flags.Type == TRANSIENT ||
flags.Type == VALID ||
flags.Type == ERROR {
env.LoadTemplateCache()
}
template.Init(env) template.Init(env)
env.Var = cfg.Var flags.HasExtra = cfg.DebugPrompt != nil ||
flags.HasTransient = cfg.TransientPrompt != nil cfg.SecondaryPrompt != nil ||
cfg.TransientPrompt != nil ||
cfg.ValidLine != nil ||
cfg.ErrorLine != nil
terminal.Init(env.Shell()) terminal.Init(env.Shell())
terminal.BackgroundColor = cfg.TerminalBackground.ResolveTemplate() terminal.BackgroundColor = cfg.TerminalBackground.ResolveTemplate()

View file

@ -10,7 +10,9 @@ import (
) )
func TestRenderBlock(t *testing.T) { func TestRenderBlock(t *testing.T) {
engine := New(&runtime.Flags{}) engine := New(&runtime.Flags{
IsPrimary: true,
})
block := &config.Block{ block := &config.Block{
Segments: []*config.Segment{ Segments: []*config.Segment{
{ {

View file

@ -69,7 +69,6 @@ type Environment interface {
ConvertToWindowsPath(path string) string ConvertToWindowsPath(path string) string
Connection(connectionType ConnectionType) (*Connection, error) Connection(connectionType ConnectionType) (*Connection, error)
TemplateCache() *cache.Template TemplateCache() *cache.Template
LoadTemplateCache()
CursorPosition() (row, col int) CursorPosition() (row, col int)
SystemInfo() (*SystemInfo, error) SystemInfo() (*SystemInfo, error)
Debug(message string) Debug(message string)
@ -93,7 +92,8 @@ type Flags struct {
TerminalWidth int TerminalWidth int
ExecutionTime float64 ExecutionTime float64
JobCount int JobCount int
HasTransient bool IsPrimary bool
HasExtra bool
Debug bool Debug bool
Plain bool Plain bool
Strict bool Strict bool

View file

@ -231,10 +231,6 @@ func (env *Environment) TemplateCache() *cache.Template {
return args.Get(0).(*cache.Template) return args.Get(0).(*cache.Template)
} }
func (env *Environment) LoadTemplateCache() {
_ = env.Called()
}
func (env *Environment) MockGitCommand(dir, returnValue string, args ...string) { 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...) 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) env.On("RunCommand", "git", args).Return(returnValue, nil)

View file

@ -37,10 +37,10 @@ type Terminal struct {
deviceCache *cache.File deviceCache *cache.File
sessionCache *cache.File sessionCache *cache.File
tmplCache *cache.Template tmplCache *cache.Template
lsDirMap maps.Concurrent
cwd string cwd string
host string host string
networks []*Connection networks []*Connection
lsDirMap maps.Concurrent
} }
func (term *Terminal) Init() { func (term *Terminal) Init() {
@ -78,7 +78,7 @@ func (term *Terminal) Init() {
Commands: maps.NewConcurrent(), Commands: maps.NewConcurrent(),
} }
term.tmplCache = &cache.Template{} term.tmplCache = new(cache.Template)
} }
func (term *Terminal) ResolveConfigPath() { func (term *Terminal) ResolveConfigPath() {
@ -580,8 +580,8 @@ func (term *Terminal) Session() cache.Cache {
func (term *Terminal) saveTemplateCache() { func (term *Terminal) saveTemplateCache() {
// only store this when in a primary prompt // only store this when in a primary prompt
// and when we have a transient prompt in the config // and when we have any extra prompt in the config
canSave := term.CmdFlags.Type == PRIMARY && term.CmdFlags.HasTransient canSave := term.CmdFlags.Type == PRIMARY && term.CmdFlags.HasExtra
if !canSave { if !canSave {
return return
} }
@ -619,40 +619,15 @@ func (term *Terminal) clearCacheFiles() {
} }
} }
func (term *Terminal) LoadTemplateCache() { func (term *Terminal) PopulateTemplateCache() {
defer term.Trace(time.Now()) if !term.CmdFlags.IsPrimary {
// Load the template cache for a non-primary prompt before rendering any templates.
val, OK := term.sessionCache.Get(cache.TEMPLATECACHE) term.loadTemplateCache()
if !OK {
return 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 tmplCache := term.tmplCache
if tmplCache.Initialized {
return tmplCache
}
tmplCache.Root = term.Root() tmplCache.Root = term.Root()
tmplCache.Shell = term.Shell() tmplCache.Shell = term.Shell()
tmplCache.ShellVersion = term.CmdFlags.ShellVersion tmplCache.ShellVersion = term.CmdFlags.ShellVersion
@ -697,9 +672,33 @@ func (term *Terminal) TemplateCache() *cache.Template {
if shlvl, err := strconv.Atoi(val); err == nil { if shlvl, err := strconv.Atoi(val); err == nil {
tmplCache.SHLVL = shlvl tmplCache.SHLVL = shlvl
} }
}
tmplCache.Initialized = true func (term *Terminal) loadTemplateCache() {
return tmplCache 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) { func (term *Terminal) DirMatchesOneOf(dir string, regexes []string) (match bool) {

View file

@ -20,11 +20,6 @@ type context struct {
func (c *context) init(t *Text) { func (c *context) init(t *Text) {
c.Data = t.Context c.Data = t.Context
if c.Initialized {
return
}
c.Getenv = env.Getenv c.Getenv = env.Getenv
c.Template = *env.TemplateCache() c.Template = *env.TemplateCache()
} }