refactor: simpler prompt caching logic

This commit is contained in:
Jan De Dobbeleer 2024-07-20 23:50:26 +02:00 committed by Jan De Dobbeleer
parent c5195f6668
commit 083c204694
5 changed files with 99 additions and 99 deletions

2
src/cache/cache.go vendored
View file

@ -33,7 +33,7 @@ var (
TEMPLATECACHE = fmt.Sprintf("template_cache_%s", pid())
TOGGLECACHE = fmt.Sprintf("toggle_cache_%s", pid())
PROMPTCOUNTCACHE = fmt.Sprintf("prompt_count_cache_%s", pid())
PROMPTCACHE = fmt.Sprintf("prompt_cache_%s", pid())
ENGINECACHE = fmt.Sprintf("engine_cache_%s", pid())
)
type Entry struct {

View file

@ -16,7 +16,7 @@ import (
var cycle *color.Cycle = &color.Cycle{}
type promptCache struct {
type engineCache struct {
Prompt string
CurrentLineLength int
RPrompt string
@ -36,7 +36,9 @@ type Engine struct {
activeSegment *config.Segment
previousActiveSegment *config.Segment
promptCache *promptCache
engineCache *engineCache
cached bool
}
func (e *Engine) write(text string) {
@ -510,32 +512,40 @@ func (e *Engine) adjustTrailingDiamondColorOverrides() {
}
}
func (e *Engine) checkPromptCache() bool {
data, ok := e.Env.Cache().Get(cache.PROMPTCACHE)
func (e *Engine) restoreEngineFromCache() bool {
if !e.Env.Flags().Cached {
return false
}
data, ok := e.Env.Cache().Get(cache.ENGINECACHE)
if !ok {
return false
}
e.promptCache = &promptCache{}
err := json.Unmarshal([]byte(data), e.promptCache)
var engineCache engineCache
err := json.Unmarshal([]byte(data), &engineCache)
if err != nil {
return false
}
e.write(e.promptCache.Prompt)
e.currentLineLength = e.promptCache.CurrentLineLength
e.rprompt = e.promptCache.RPrompt
e.rpromptLength = e.promptCache.RPromptLength
e.engineCache = &engineCache
e.write(e.engineCache.Prompt)
e.currentLineLength = e.engineCache.CurrentLineLength
e.rprompt = e.engineCache.RPrompt
e.rpromptLength = e.engineCache.RPromptLength
e.cached = true
return true
}
func (e *Engine) updatePromptCache(value *promptCache) {
func (e *Engine) updateEngineCache(value *engineCache) {
cacheJSON, err := json.Marshal(value)
if err != nil {
return
}
e.Env.Cache().Set(cache.PROMPTCACHE, string(cacheJSON), 1440)
e.Env.Cache().Set(cache.ENGINECACHE, string(cacheJSON), 1440)
}
// New returns a prompt engine initialized with the

View file

@ -10,74 +10,10 @@ import (
)
func (e *Engine) Primary() string {
needsPrimaryRPrompt := e.needsPrimaryRPrompt()
needsPrimaryRightPrompt := e.needsPrimaryRightPrompt()
var (
useCache bool
updateCache bool
)
if e.Env.Shell() == shell.PWSH || e.Env.Shell() == shell.PWSH5 {
// For PowerShell, use a cached prompt if available, otherwise render a new prompt.
if e.Env.Flags().Cached && e.checkPromptCache() {
useCache = true
} else {
updateCache = true
}
}
if !useCache {
if e.Config.ShellIntegration {
exitCode, _ := e.Env.StatusCodes()
e.write(terminal.CommandFinished(exitCode, e.Env.Flags().NoExitCode))
e.write(terminal.PromptStart())
}
// cache a pointer to the color cycle
cycle = &e.Config.Cycle
var cancelNewline, didRender bool
for i, block := range e.Config.Blocks {
// do not print a leading newline when we're at the first row and the prompt is cleared
if i == 0 {
row, _ := e.Env.CursorPosition()
cancelNewline = e.Env.Flags().Cleared || e.Env.Flags().PromptCount == 1 || row == 1
}
// skip setting a newline when we didn't print anything yet
if i != 0 {
cancelNewline = !didRender
}
if block.Type == config.RPrompt && !needsPrimaryRPrompt {
continue
}
if e.renderBlock(block, cancelNewline) {
didRender = true
}
}
if len(e.Config.ConsoleTitleTemplate) > 0 && !e.Env.Flags().Plain {
title := e.getTitleTemplateText()
e.write(terminal.FormatTitle(title))
}
if e.Config.FinalSpace {
e.write(" ")
e.currentLineLength++
}
if e.Config.ITermFeatures != nil && e.isIterm() {
host, _ := e.Env.Host()
e.write(terminal.RenderItermFeatures(e.Config.ITermFeatures, e.Env.Shell(), e.Env.Pwd(), e.Env.User(), host))
}
if e.Config.ShellIntegration && e.Config.TransientPrompt == nil {
e.write(terminal.CommandStart())
}
e.pwd()
if !e.restoreEngineFromCache() {
e.writePrimaryPrompt(needsPrimaryRightPrompt)
}
switch e.Env.Shell() {
@ -88,7 +24,7 @@ func (e *Engine) Primary() string {
// Warp doesn't support RPROMPT so we need to write it manually
if e.isWarp() {
e.writePrimaryRPrompt()
e.writePrimaryRightPrompt()
// escape double quotes contained in the prompt
prompt := fmt.Sprintf("PS1=\"%s\"", strings.ReplaceAll(e.string(), `"`, `\"`))
return prompt
@ -100,27 +36,80 @@ func (e *Engine) Primary() string {
return prompt
default:
if updateCache {
// Cache the new prompt.
e.updatePromptCache(&promptCache{
Prompt: e.prompt.String(),
CurrentLineLength: e.currentLineLength,
RPrompt: e.rprompt,
RPromptLength: e.rpromptLength,
})
}
if !needsPrimaryRPrompt {
if !needsPrimaryRightPrompt {
break
}
e.writePrimaryRPrompt()
e.writePrimaryRightPrompt()
}
if !e.cached {
e.updateEngineCache(&engineCache{
Prompt: e.prompt.String(),
CurrentLineLength: e.currentLineLength,
RPrompt: e.rprompt,
RPromptLength: e.rpromptLength,
})
}
return e.string()
}
func (e *Engine) needsPrimaryRPrompt() bool {
func (e *Engine) writePrimaryPrompt(needsPrimaryRPrompt bool) {
if e.Config.ShellIntegration {
exitCode, _ := e.Env.StatusCodes()
e.write(terminal.CommandFinished(exitCode, e.Env.Flags().NoExitCode))
e.write(terminal.PromptStart())
}
// cache a pointer to the color cycle
cycle = &e.Config.Cycle
var cancelNewline, didRender bool
for i, block := range e.Config.Blocks {
// do not print a leading newline when we're at the first row and the prompt is cleared
if i == 0 {
row, _ := e.Env.CursorPosition()
cancelNewline = e.Env.Flags().Cleared || e.Env.Flags().PromptCount == 1 || row == 1
}
// skip setting a newline when we didn't print anything yet
if i != 0 {
cancelNewline = !didRender
}
if block.Type == config.RPrompt && !needsPrimaryRPrompt {
continue
}
if e.renderBlock(block, cancelNewline) {
didRender = true
}
}
if len(e.Config.ConsoleTitleTemplate) > 0 && !e.Env.Flags().Plain {
title := e.getTitleTemplateText()
e.write(terminal.FormatTitle(title))
}
if e.Config.FinalSpace {
e.write(" ")
e.currentLineLength++
}
if e.Config.ITermFeatures != nil && e.isIterm() {
host, _ := e.Env.Host()
e.write(terminal.RenderItermFeatures(e.Config.ITermFeatures, e.Env.Shell(), e.Env.Pwd(), e.Env.User(), host))
}
if e.Config.ShellIntegration && e.Config.TransientPrompt == nil {
e.write(terminal.CommandStart())
}
e.pwd()
}
func (e *Engine) needsPrimaryRightPrompt() bool {
switch e.Env.Shell() {
case shell.PWSH, shell.PWSH5, shell.GENERIC, shell.ZSH:
return true
@ -129,7 +118,7 @@ func (e *Engine) needsPrimaryRPrompt() bool {
}
}
func (e *Engine) writePrimaryRPrompt() {
func (e *Engine) writePrimaryRightPrompt() {
space, OK := e.canWriteRightBlock(e.rpromptLength, true)
if !OK {
return

View file

@ -45,10 +45,10 @@ func (e *Engine) Tooltip(tip string) string {
case shell.PWSH, shell.PWSH5:
defer func() {
// If a prompt cache is available, we update the right prompt to the new tooltip for reuse.
if e.checkPromptCache() {
e.promptCache.RPrompt = text
e.promptCache.RPromptLength = length
e.updatePromptCache(e.promptCache)
if e.restoreEngineFromCache() {
e.engineCache.RPrompt = text
e.engineCache.RPromptLength = length
e.updateEngineCache(e.engineCache)
}
}()

View file

@ -176,6 +176,7 @@ New-Module -Name "oh-my-posh-core" -ScriptBlock {
"--column=$column"
"--terminal-width=$terminalWidth"
"--no-status=$script:NoExitCode"
"--cached=$true"
)
$standardOut = (Start-Utf8Process $script:OMPExecutable $arguments) -join ''
if (!$standardOut) {