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

This commit is contained in:
L. Yeung 2024-11-11 00:36:33 +08:00
parent 8295e48ed3
commit 388fa2e128
No known key found for this signature in database
GPG key ID: 89C1D47587DCB5F8
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()
} }