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
WSL bool
Root bool
Initialized bool
}
func (t *Template) AddSegmentData(key string, value any) {

View file

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

View file

@ -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)
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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()

View file

@ -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{
{

View file

@ -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

View file

@ -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)

View file

@ -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) {

View file

@ -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()
}