mirror of
https://github.com/JanDeDobbeleer/oh-my-posh.git
synced 2025-03-05 20:49:04 -08:00
refactor: split engine logic
This commit is contained in:
parent
6c181c1120
commit
a1b95a0274
|
@ -75,7 +75,7 @@ Exports the config to an image file using customized output options.`,
|
||||||
Writer: writer,
|
Writer: writer,
|
||||||
}
|
}
|
||||||
|
|
||||||
prompt := eng.PrintPrimary()
|
prompt := eng.Primary()
|
||||||
|
|
||||||
imageCreator := &engine.ImageRenderer{
|
imageCreator := &engine.ImageRenderer{
|
||||||
AnsiString: prompt,
|
AnsiString: prompt,
|
||||||
|
|
|
@ -67,21 +67,21 @@ var printCmd = &cobra.Command{
|
||||||
|
|
||||||
switch args[0] {
|
switch args[0] {
|
||||||
case "debug":
|
case "debug":
|
||||||
fmt.Print(eng.PrintExtraPrompt(engine.Debug))
|
fmt.Print(eng.ExtraPrompt(engine.Debug))
|
||||||
case "primary":
|
case "primary":
|
||||||
fmt.Print(eng.PrintPrimary())
|
fmt.Print(eng.Primary())
|
||||||
case "secondary":
|
case "secondary":
|
||||||
fmt.Print(eng.PrintExtraPrompt(engine.Secondary))
|
fmt.Print(eng.ExtraPrompt(engine.Secondary))
|
||||||
case "transient":
|
case "transient":
|
||||||
fmt.Print(eng.PrintExtraPrompt(engine.Transient))
|
fmt.Print(eng.ExtraPrompt(engine.Transient))
|
||||||
case "right":
|
case "right":
|
||||||
fmt.Print(eng.PrintRPrompt())
|
fmt.Print(eng.RPrompt())
|
||||||
case "tooltip":
|
case "tooltip":
|
||||||
fmt.Print(eng.PrintTooltip(command))
|
fmt.Print(eng.Tooltip(command))
|
||||||
case "valid":
|
case "valid":
|
||||||
fmt.Print(eng.PrintExtraPrompt(engine.Valid))
|
fmt.Print(eng.ExtraPrompt(engine.Valid))
|
||||||
case "error":
|
case "error":
|
||||||
fmt.Print(eng.PrintExtraPrompt(engine.Error))
|
fmt.Print(eng.ExtraPrompt(engine.Error))
|
||||||
default:
|
default:
|
||||||
_ = cmd.Help()
|
_ = cmd.Help()
|
||||||
}
|
}
|
||||||
|
|
65
src/engine/debug.go
Normal file
65
src/engine/debug.go
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
package engine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/jandedobbeleer/oh-my-posh/src/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// debug will loop through your config file and output the timings for each segments
|
||||||
|
func (e *Engine) PrintDebug(startTime time.Time, version string) string {
|
||||||
|
e.write(fmt.Sprintf("\n%s %s\n", log.Text("Version:").Green().Bold().Plain(), version))
|
||||||
|
sh := e.Env.Shell()
|
||||||
|
shellVersion := e.Env.Getenv("POSH_SHELL_VERSION")
|
||||||
|
if len(shellVersion) != 0 {
|
||||||
|
sh += fmt.Sprintf(" (%s)", shellVersion)
|
||||||
|
}
|
||||||
|
e.write(fmt.Sprintf("\n%s %s\n", log.Text("Shell:").Green().Bold().Plain(), sh))
|
||||||
|
e.write(log.Text("\nSegments:\n\n").Green().Bold().Plain().String())
|
||||||
|
// console title timing
|
||||||
|
titleStartTime := time.Now()
|
||||||
|
e.Env.Debug("Segment: Title")
|
||||||
|
title := e.getTitleTemplateText()
|
||||||
|
consoleTitleTiming := &SegmentTiming{
|
||||||
|
name: "ConsoleTitle",
|
||||||
|
nameLength: 12,
|
||||||
|
active: len(e.Config.ConsoleTitleTemplate) > 0,
|
||||||
|
text: title,
|
||||||
|
duration: time.Since(titleStartTime),
|
||||||
|
}
|
||||||
|
largestSegmentNameLength := consoleTitleTiming.nameLength
|
||||||
|
var segmentTimings []*SegmentTiming
|
||||||
|
segmentTimings = append(segmentTimings, consoleTitleTiming)
|
||||||
|
// cache a pointer to the color cycle
|
||||||
|
cycle = &e.Config.Cycle
|
||||||
|
// loop each segments of each blocks
|
||||||
|
for _, block := range e.Config.Blocks {
|
||||||
|
block.Init(e.Env, e.Writer)
|
||||||
|
longestSegmentName, timings := block.Debug()
|
||||||
|
segmentTimings = append(segmentTimings, timings...)
|
||||||
|
if longestSegmentName > largestSegmentNameLength {
|
||||||
|
largestSegmentNameLength = longestSegmentName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 22 is the color for false/true and 7 is the reset color
|
||||||
|
largestSegmentNameLength += 22 + 7
|
||||||
|
for _, segment := range segmentTimings {
|
||||||
|
duration := segment.duration.Milliseconds()
|
||||||
|
var active log.Text
|
||||||
|
if segment.active {
|
||||||
|
active = log.Text("true").Yellow()
|
||||||
|
} else {
|
||||||
|
active = log.Text("false").Purple()
|
||||||
|
}
|
||||||
|
segmentName := fmt.Sprintf("%s(%s)", segment.name, active.Plain())
|
||||||
|
e.write(fmt.Sprintf("%-*s - %3d ms - %s\n", largestSegmentNameLength, segmentName, duration, segment.text))
|
||||||
|
}
|
||||||
|
e.write(fmt.Sprintf("\n%s %s\n", log.Text("Run duration:").Green().Bold().Plain(), time.Since(startTime)))
|
||||||
|
e.write(fmt.Sprintf("\n%s %s\n", log.Text("Cache path:").Green().Bold().Plain(), e.Env.CachePath()))
|
||||||
|
e.write(fmt.Sprintf("\n%s %s\n", log.Text("Config path:").Green().Bold().Plain(), e.Env.Flags().Config))
|
||||||
|
e.write(log.Text("\nLogs:\n\n").Green().Bold().Plain().String())
|
||||||
|
e.write(e.Env.Logs())
|
||||||
|
return e.string()
|
||||||
|
}
|
|
@ -1,12 +1,9 @@
|
||||||
package engine
|
package engine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/jandedobbeleer/oh-my-posh/src/ansi"
|
"github.com/jandedobbeleer/oh-my-posh/src/ansi"
|
||||||
"github.com/jandedobbeleer/oh-my-posh/src/log"
|
|
||||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||||
"github.com/jandedobbeleer/oh-my-posh/src/shell"
|
"github.com/jandedobbeleer/oh-my-posh/src/shell"
|
||||||
"github.com/jandedobbeleer/oh-my-posh/src/template"
|
"github.com/jandedobbeleer/oh-my-posh/src/template"
|
||||||
|
@ -61,29 +58,7 @@ func (e *Engine) canWriteRightBlock(rprompt bool) bool {
|
||||||
return canWrite
|
return canWrite
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Engine) PrintPrimary() string {
|
func (e *Engine) pwd() {
|
||||||
// cache a pointer to the color cycle
|
|
||||||
cycle = &e.Config.Cycle
|
|
||||||
for i, block := range e.Config.Blocks {
|
|
||||||
var cancelNewline bool
|
|
||||||
if i == 0 {
|
|
||||||
row, _ := e.Env.CursorPosition()
|
|
||||||
cancelNewline = e.Env.Flags().Cleared || e.Env.Flags().PromptCount == 1 || row == 1
|
|
||||||
}
|
|
||||||
e.renderBlock(block, cancelNewline)
|
|
||||||
}
|
|
||||||
if len(e.Config.ConsoleTitleTemplate) > 0 {
|
|
||||||
title := e.getTitleTemplateText()
|
|
||||||
e.write(e.Writer.FormatTitle(title))
|
|
||||||
}
|
|
||||||
if e.Config.FinalSpace {
|
|
||||||
e.write(" ")
|
|
||||||
}
|
|
||||||
e.printPWD()
|
|
||||||
return e.print()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Engine) printPWD() {
|
|
||||||
// only print when supported
|
// only print when supported
|
||||||
sh := e.Env.Shell()
|
sh := e.Env.Shell()
|
||||||
if sh == shell.ELVISH || sh == shell.XONSH {
|
if sh == shell.ELVISH || sh == shell.XONSH {
|
||||||
|
@ -250,267 +225,3 @@ func (e *Engine) renderBlock(block *Block, cancelNewline bool) {
|
||||||
e.rprompt, e.rpromptLength = block.RenderSegments()
|
e.rprompt, e.rpromptLength = block.RenderSegments()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// debug will loop through your config file and output the timings for each segments
|
|
||||||
func (e *Engine) PrintDebug(startTime time.Time, version string) string {
|
|
||||||
e.write(fmt.Sprintf("\n%s %s\n", log.Text("Version:").Green().Bold().Plain(), version))
|
|
||||||
sh := e.Env.Shell()
|
|
||||||
shellVersion := e.Env.Getenv("POSH_SHELL_VERSION")
|
|
||||||
if len(shellVersion) != 0 {
|
|
||||||
sh += fmt.Sprintf(" (%s)", shellVersion)
|
|
||||||
}
|
|
||||||
e.write(fmt.Sprintf("\n%s %s\n", log.Text("Shell:").Green().Bold().Plain(), sh))
|
|
||||||
e.write(log.Text("\nSegments:\n\n").Green().Bold().Plain().String())
|
|
||||||
// console title timing
|
|
||||||
titleStartTime := time.Now()
|
|
||||||
e.Env.Debug("Segment: Title")
|
|
||||||
title := e.getTitleTemplateText()
|
|
||||||
consoleTitleTiming := &SegmentTiming{
|
|
||||||
name: "ConsoleTitle",
|
|
||||||
nameLength: 12,
|
|
||||||
active: len(e.Config.ConsoleTitleTemplate) > 0,
|
|
||||||
text: title,
|
|
||||||
duration: time.Since(titleStartTime),
|
|
||||||
}
|
|
||||||
largestSegmentNameLength := consoleTitleTiming.nameLength
|
|
||||||
var segmentTimings []*SegmentTiming
|
|
||||||
segmentTimings = append(segmentTimings, consoleTitleTiming)
|
|
||||||
// cache a pointer to the color cycle
|
|
||||||
cycle = &e.Config.Cycle
|
|
||||||
// loop each segments of each blocks
|
|
||||||
for _, block := range e.Config.Blocks {
|
|
||||||
block.Init(e.Env, e.Writer)
|
|
||||||
longestSegmentName, timings := block.Debug()
|
|
||||||
segmentTimings = append(segmentTimings, timings...)
|
|
||||||
if longestSegmentName > largestSegmentNameLength {
|
|
||||||
largestSegmentNameLength = longestSegmentName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 22 is the color for false/true and 7 is the reset color
|
|
||||||
largestSegmentNameLength += 22 + 7
|
|
||||||
for _, segment := range segmentTimings {
|
|
||||||
duration := segment.duration.Milliseconds()
|
|
||||||
var active log.Text
|
|
||||||
if segment.active {
|
|
||||||
active = log.Text("true").Yellow()
|
|
||||||
} else {
|
|
||||||
active = log.Text("false").Purple()
|
|
||||||
}
|
|
||||||
segmentName := fmt.Sprintf("%s(%s)", segment.name, active.Plain())
|
|
||||||
e.write(fmt.Sprintf("%-*s - %3d ms - %s\n", largestSegmentNameLength, segmentName, duration, segment.text))
|
|
||||||
}
|
|
||||||
e.write(fmt.Sprintf("\n%s %s\n", log.Text("Run duration:").Green().Bold().Plain(), time.Since(startTime)))
|
|
||||||
e.write(fmt.Sprintf("\n%s %s\n", log.Text("Cache path:").Green().Bold().Plain(), e.Env.CachePath()))
|
|
||||||
e.write(fmt.Sprintf("\n%s %s\n", log.Text("Config path:").Green().Bold().Plain(), e.Env.Flags().Config))
|
|
||||||
e.write(log.Text("\nLogs:\n\n").Green().Bold().Plain().String())
|
|
||||||
e.write(e.Env.Logs())
|
|
||||||
return e.string()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Engine) print() string {
|
|
||||||
switch e.Env.Shell() {
|
|
||||||
case shell.ZSH:
|
|
||||||
if !e.Env.Flags().Eval {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
// Warp doesn't support RPROMPT so we need to write it manually
|
|
||||||
if e.isWarp() {
|
|
||||||
e.write(e.Writer.SaveCursorPosition())
|
|
||||||
e.write(e.Writer.CarriageForward())
|
|
||||||
e.write(e.Writer.GetCursorForRightWrite(e.rpromptLength, 0))
|
|
||||||
e.write(e.rprompt)
|
|
||||||
e.write(e.Writer.RestoreCursorPosition())
|
|
||||||
// escape double quotes contained in the prompt
|
|
||||||
prompt := fmt.Sprintf("PS1=\"%s\"", strings.ReplaceAll(e.string(), `"`, `\"`))
|
|
||||||
return prompt
|
|
||||||
}
|
|
||||||
// escape double quotes contained in the prompt
|
|
||||||
prompt := fmt.Sprintf("PS1=\"%s\"", strings.ReplaceAll(e.string(), `"`, `\"`))
|
|
||||||
prompt += fmt.Sprintf("\nRPROMPT=\"%s\"", e.rprompt)
|
|
||||||
return prompt
|
|
||||||
case shell.PWSH, shell.PWSH5, shell.GENERIC, shell.NU:
|
|
||||||
if !e.canWriteRightBlock(true) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
e.write(e.Writer.SaveCursorPosition())
|
|
||||||
e.write(e.Writer.CarriageForward())
|
|
||||||
e.write(e.Writer.GetCursorForRightWrite(e.rpromptLength, 0))
|
|
||||||
e.write(e.rprompt)
|
|
||||||
e.write(e.Writer.RestoreCursorPosition())
|
|
||||||
case shell.BASH:
|
|
||||||
if !e.canWriteRightBlock(true) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
// in bash, the entire rprompt needs to be escaped for the prompt to be interpreted correctly
|
|
||||||
// see https://github.com/jandedobbeleer/oh-my-posh/pull/2398
|
|
||||||
writer := &ansi.Writer{
|
|
||||||
TrueColor: e.Env.Flags().TrueColor,
|
|
||||||
}
|
|
||||||
writer.Init(shell.GENERIC)
|
|
||||||
prompt := writer.SaveCursorPosition()
|
|
||||||
prompt += writer.CarriageForward()
|
|
||||||
prompt += writer.GetCursorForRightWrite(e.rpromptLength, 0)
|
|
||||||
prompt += e.rprompt
|
|
||||||
prompt += writer.RestoreCursorPosition()
|
|
||||||
prompt = e.Writer.FormatText(prompt)
|
|
||||||
e.write(prompt)
|
|
||||||
}
|
|
||||||
|
|
||||||
return e.string()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Engine) PrintTooltip(tip string) string {
|
|
||||||
tip = strings.Trim(tip, " ")
|
|
||||||
var tooltip *Segment
|
|
||||||
for _, tp := range e.Config.Tooltips {
|
|
||||||
if !tp.shouldInvokeWithTip(tip) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
tooltip = tp
|
|
||||||
}
|
|
||||||
if tooltip == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
if err := tooltip.mapSegmentWithWriter(e.Env); err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
if !tooltip.writer.Enabled() {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
tooltip.Enabled = true
|
|
||||||
// little hack to reuse the current logic
|
|
||||||
block := &Block{
|
|
||||||
Alignment: Right,
|
|
||||||
Segments: []*Segment{tooltip},
|
|
||||||
}
|
|
||||||
switch e.Env.Shell() {
|
|
||||||
case shell.ZSH, shell.CMD, shell.FISH, shell.GENERIC:
|
|
||||||
block.Init(e.Env, e.Writer)
|
|
||||||
if !block.Enabled() {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
text, _ := block.RenderSegments()
|
|
||||||
return text
|
|
||||||
case shell.PWSH, shell.PWSH5:
|
|
||||||
block.InitPlain(e.Env, e.Config)
|
|
||||||
if !block.Enabled() {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
text, length := block.RenderSegments()
|
|
||||||
// clear from cursor to the end of the line in case a previous tooltip is cut off and partially preserved,
|
|
||||||
// if the new one is shorter
|
|
||||||
e.write(e.Writer.ClearAfter())
|
|
||||||
e.write(e.Writer.CarriageForward())
|
|
||||||
e.write(e.Writer.GetCursorForRightWrite(length, 0))
|
|
||||||
e.write(text)
|
|
||||||
return e.string()
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
type ExtraPromptType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
Transient ExtraPromptType = iota
|
|
||||||
Valid
|
|
||||||
Error
|
|
||||||
Secondary
|
|
||||||
Debug
|
|
||||||
)
|
|
||||||
|
|
||||||
func (e *Engine) PrintExtraPrompt(promptType ExtraPromptType) string {
|
|
||||||
// populate env with latest context
|
|
||||||
e.Env.LoadTemplateCache()
|
|
||||||
var prompt *Segment
|
|
||||||
switch promptType {
|
|
||||||
case Debug:
|
|
||||||
prompt = e.Config.DebugPrompt
|
|
||||||
case Transient:
|
|
||||||
prompt = e.Config.TransientPrompt
|
|
||||||
case Valid:
|
|
||||||
prompt = e.Config.ValidLine
|
|
||||||
case Error:
|
|
||||||
prompt = e.Config.ErrorLine
|
|
||||||
case Secondary:
|
|
||||||
prompt = e.Config.SecondaryPrompt
|
|
||||||
}
|
|
||||||
if prompt == nil {
|
|
||||||
prompt = &Segment{}
|
|
||||||
}
|
|
||||||
getTemplate := func(template string) string {
|
|
||||||
if len(template) != 0 {
|
|
||||||
return template
|
|
||||||
}
|
|
||||||
switch promptType { //nolint: exhaustive
|
|
||||||
case Debug:
|
|
||||||
return "[DBG]: "
|
|
||||||
case Transient:
|
|
||||||
return "{{ .Shell }}> "
|
|
||||||
case Secondary:
|
|
||||||
return "> "
|
|
||||||
default:
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tmpl := &template.Text{
|
|
||||||
Template: getTemplate(prompt.Template),
|
|
||||||
Env: e.Env,
|
|
||||||
}
|
|
||||||
promptText, err := tmpl.Render()
|
|
||||||
if err != nil {
|
|
||||||
promptText = err.Error()
|
|
||||||
}
|
|
||||||
foreground := prompt.ForegroundTemplates.FirstMatch(nil, e.Env, prompt.Foreground)
|
|
||||||
background := prompt.BackgroundTemplates.FirstMatch(nil, e.Env, prompt.Background)
|
|
||||||
e.Writer.SetColors(background, foreground)
|
|
||||||
e.Writer.Write(background, foreground, promptText)
|
|
||||||
str, length := e.Writer.String()
|
|
||||||
if promptType == Transient {
|
|
||||||
if padText, OK := e.shouldFill(prompt.Filler, length); OK {
|
|
||||||
str += padText
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch e.Env.Shell() {
|
|
||||||
case shell.ZSH:
|
|
||||||
// escape double quotes contained in the prompt
|
|
||||||
if promptType == Transient {
|
|
||||||
prompt := fmt.Sprintf("PS1=\"%s\"", strings.ReplaceAll(str, "\"", "\"\""))
|
|
||||||
// empty RPROMPT
|
|
||||||
prompt += "\nRPROMPT=\"\""
|
|
||||||
return prompt
|
|
||||||
}
|
|
||||||
return str
|
|
||||||
case shell.PWSH, shell.PWSH5:
|
|
||||||
// Return the string and empty our buffer
|
|
||||||
// clear the line afterwards to prevent text from being written on the same line
|
|
||||||
// see https://github.com/JanDeDobbeleer/oh-my-posh/issues/3628
|
|
||||||
return str + e.Writer.ClearAfter()
|
|
||||||
case shell.CMD, shell.BASH, shell.FISH, shell.NU, shell.GENERIC:
|
|
||||||
// Return the string and empty our buffer
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Engine) PrintRPrompt() string {
|
|
||||||
filterRPromptBlock := func(blocks []*Block) *Block {
|
|
||||||
for _, block := range blocks {
|
|
||||||
if block.Type == RPrompt {
|
|
||||||
return block
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
block := filterRPromptBlock(e.Config.Blocks)
|
|
||||||
if block == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
block.Init(e.Env, e.Writer)
|
|
||||||
if !block.Enabled() {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
text, length := block.RenderSegments()
|
|
||||||
e.rpromptLength = length
|
|
||||||
return text
|
|
||||||
}
|
|
||||||
|
|
|
@ -81,8 +81,8 @@ func TestPrintPWD(t *testing.T) {
|
||||||
},
|
},
|
||||||
Writer: writer,
|
Writer: writer,
|
||||||
}
|
}
|
||||||
engine.printPWD()
|
engine.pwd()
|
||||||
got := engine.print()
|
got := engine.string()
|
||||||
assert.Equal(t, tc.Expected, got, tc.Case)
|
assert.Equal(t, tc.Expected, got, tc.Case)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -114,7 +114,7 @@ func engineRender() {
|
||||||
Writer: writer,
|
Writer: writer,
|
||||||
}
|
}
|
||||||
|
|
||||||
engine.PrintPrimary()
|
engine.Primary()
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkEngineRenderPalette(b *testing.B) {
|
func BenchmarkEngineRenderPalette(b *testing.B) {
|
||||||
|
|
200
src/engine/prompt.go
Normal file
200
src/engine/prompt.go
Normal file
|
@ -0,0 +1,200 @@
|
||||||
|
package engine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/jandedobbeleer/oh-my-posh/src/ansi"
|
||||||
|
"github.com/jandedobbeleer/oh-my-posh/src/shell"
|
||||||
|
"github.com/jandedobbeleer/oh-my-posh/src/template"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ExtraPromptType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
Transient ExtraPromptType = iota
|
||||||
|
Valid
|
||||||
|
Error
|
||||||
|
Secondary
|
||||||
|
Debug
|
||||||
|
)
|
||||||
|
|
||||||
|
func (e *Engine) Primary() string {
|
||||||
|
// cache a pointer to the color cycle
|
||||||
|
cycle = &e.Config.Cycle
|
||||||
|
for i, block := range e.Config.Blocks {
|
||||||
|
var cancelNewline bool
|
||||||
|
if i == 0 {
|
||||||
|
row, _ := e.Env.CursorPosition()
|
||||||
|
cancelNewline = e.Env.Flags().Cleared || e.Env.Flags().PromptCount == 1 || row == 1
|
||||||
|
}
|
||||||
|
e.renderBlock(block, cancelNewline)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(e.Config.ConsoleTitleTemplate) > 0 {
|
||||||
|
title := e.getTitleTemplateText()
|
||||||
|
e.write(e.Writer.FormatTitle(title))
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.Config.FinalSpace {
|
||||||
|
e.write(" ")
|
||||||
|
}
|
||||||
|
|
||||||
|
e.pwd()
|
||||||
|
|
||||||
|
switch e.Env.Shell() {
|
||||||
|
case shell.ZSH:
|
||||||
|
if !e.Env.Flags().Eval {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// Warp doesn't support RPROMPT so we need to write it manually
|
||||||
|
if e.isWarp() {
|
||||||
|
e.write(e.Writer.SaveCursorPosition())
|
||||||
|
e.write(e.Writer.CarriageForward())
|
||||||
|
e.write(e.Writer.GetCursorForRightWrite(e.rpromptLength, 0))
|
||||||
|
e.write(e.rprompt)
|
||||||
|
e.write(e.Writer.RestoreCursorPosition())
|
||||||
|
// escape double quotes contained in the prompt
|
||||||
|
prompt := fmt.Sprintf("PS1=\"%s\"", strings.ReplaceAll(e.string(), `"`, `\"`))
|
||||||
|
return prompt
|
||||||
|
}
|
||||||
|
// escape double quotes contained in the prompt
|
||||||
|
prompt := fmt.Sprintf("PS1=\"%s\"", strings.ReplaceAll(e.string(), `"`, `\"`))
|
||||||
|
prompt += fmt.Sprintf("\nRPROMPT=\"%s\"", e.rprompt)
|
||||||
|
return prompt
|
||||||
|
case shell.PWSH, shell.PWSH5, shell.GENERIC, shell.NU:
|
||||||
|
if !e.canWriteRightBlock(true) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
e.write(e.Writer.SaveCursorPosition())
|
||||||
|
e.write(e.Writer.CarriageForward())
|
||||||
|
e.write(e.Writer.GetCursorForRightWrite(e.rpromptLength, 0))
|
||||||
|
e.write(e.rprompt)
|
||||||
|
e.write(e.Writer.RestoreCursorPosition())
|
||||||
|
case shell.BASH:
|
||||||
|
if !e.canWriteRightBlock(true) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// in bash, the entire rprompt needs to be escaped for the prompt to be interpreted correctly
|
||||||
|
// see https://github.com/jandedobbeleer/oh-my-posh/pull/2398
|
||||||
|
writer := &ansi.Writer{
|
||||||
|
TrueColor: e.Env.Flags().TrueColor,
|
||||||
|
}
|
||||||
|
writer.Init(shell.GENERIC)
|
||||||
|
prompt := writer.SaveCursorPosition()
|
||||||
|
prompt += writer.CarriageForward()
|
||||||
|
prompt += writer.GetCursorForRightWrite(e.rpromptLength, 0)
|
||||||
|
prompt += e.rprompt
|
||||||
|
prompt += writer.RestoreCursorPosition()
|
||||||
|
prompt = e.Writer.FormatText(prompt)
|
||||||
|
e.write(prompt)
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.string()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Engine) ExtraPrompt(promptType ExtraPromptType) string {
|
||||||
|
// populate env with latest context
|
||||||
|
e.Env.LoadTemplateCache()
|
||||||
|
var prompt *Segment
|
||||||
|
switch promptType {
|
||||||
|
case Debug:
|
||||||
|
prompt = e.Config.DebugPrompt
|
||||||
|
case Transient:
|
||||||
|
prompt = e.Config.TransientPrompt
|
||||||
|
case Valid:
|
||||||
|
prompt = e.Config.ValidLine
|
||||||
|
case Error:
|
||||||
|
prompt = e.Config.ErrorLine
|
||||||
|
case Secondary:
|
||||||
|
prompt = e.Config.SecondaryPrompt
|
||||||
|
}
|
||||||
|
|
||||||
|
if prompt == nil {
|
||||||
|
prompt = &Segment{}
|
||||||
|
}
|
||||||
|
|
||||||
|
getTemplate := func(template string) string {
|
||||||
|
if len(template) != 0 {
|
||||||
|
return template
|
||||||
|
}
|
||||||
|
switch promptType { //nolint: exhaustive
|
||||||
|
case Debug:
|
||||||
|
return "[DBG]: "
|
||||||
|
case Transient:
|
||||||
|
return "{{ .Shell }}> "
|
||||||
|
case Secondary:
|
||||||
|
return "> "
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpl := &template.Text{
|
||||||
|
Template: getTemplate(prompt.Template),
|
||||||
|
Env: e.Env,
|
||||||
|
}
|
||||||
|
promptText, err := tmpl.Render()
|
||||||
|
if err != nil {
|
||||||
|
promptText = err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
foreground := prompt.ForegroundTemplates.FirstMatch(nil, e.Env, prompt.Foreground)
|
||||||
|
background := prompt.BackgroundTemplates.FirstMatch(nil, e.Env, prompt.Background)
|
||||||
|
e.Writer.SetColors(background, foreground)
|
||||||
|
e.Writer.Write(background, foreground, promptText)
|
||||||
|
|
||||||
|
str, length := e.Writer.String()
|
||||||
|
if promptType == Transient {
|
||||||
|
if padText, OK := e.shouldFill(prompt.Filler, length); OK {
|
||||||
|
str += padText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch e.Env.Shell() {
|
||||||
|
case shell.ZSH:
|
||||||
|
// escape double quotes contained in the prompt
|
||||||
|
if promptType == Transient {
|
||||||
|
prompt := fmt.Sprintf("PS1=\"%s\"", strings.ReplaceAll(str, "\"", "\"\""))
|
||||||
|
// empty RPROMPT
|
||||||
|
prompt += "\nRPROMPT=\"\""
|
||||||
|
return prompt
|
||||||
|
}
|
||||||
|
return str
|
||||||
|
case shell.PWSH, shell.PWSH5:
|
||||||
|
// Return the string and empty our buffer
|
||||||
|
// clear the line afterwards to prevent text from being written on the same line
|
||||||
|
// see https://github.com/JanDeDobbeleer/oh-my-posh/issues/3628
|
||||||
|
return str + e.Writer.ClearAfter()
|
||||||
|
case shell.CMD, shell.BASH, shell.FISH, shell.NU, shell.GENERIC:
|
||||||
|
// Return the string and empty our buffer
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Engine) RPrompt() string {
|
||||||
|
filterRPromptBlock := func(blocks []*Block) *Block {
|
||||||
|
for _, block := range blocks {
|
||||||
|
if block.Type == RPrompt {
|
||||||
|
return block
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
block := filterRPromptBlock(e.Config.Blocks)
|
||||||
|
if block == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
block.Init(e.Env, e.Writer)
|
||||||
|
if !block.Enabled() {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
text, length := block.RenderSegments()
|
||||||
|
e.rpromptLength = length
|
||||||
|
return text
|
||||||
|
}
|
63
src/engine/tooltip.go
Normal file
63
src/engine/tooltip.go
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
package engine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/jandedobbeleer/oh-my-posh/src/shell"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (e *Engine) Tooltip(tip string) string {
|
||||||
|
tip = strings.Trim(tip, " ")
|
||||||
|
var tooltip *Segment
|
||||||
|
for _, tp := range e.Config.Tooltips {
|
||||||
|
if !tp.shouldInvokeWithTip(tip) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
tooltip = tp
|
||||||
|
}
|
||||||
|
|
||||||
|
if tooltip == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tooltip.mapSegmentWithWriter(e.Env); err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if !tooltip.writer.Enabled() {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
tooltip.Enabled = true
|
||||||
|
|
||||||
|
// little hack to reuse the current logic
|
||||||
|
block := &Block{
|
||||||
|
Alignment: Right,
|
||||||
|
Segments: []*Segment{tooltip},
|
||||||
|
}
|
||||||
|
|
||||||
|
switch e.Env.Shell() {
|
||||||
|
case shell.ZSH, shell.CMD, shell.FISH, shell.GENERIC:
|
||||||
|
block.Init(e.Env, e.Writer)
|
||||||
|
if !block.Enabled() {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
text, _ := block.RenderSegments()
|
||||||
|
return text
|
||||||
|
case shell.PWSH, shell.PWSH5:
|
||||||
|
block.InitPlain(e.Env, e.Config)
|
||||||
|
if !block.Enabled() {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
text, length := block.RenderSegments()
|
||||||
|
// clear from cursor to the end of the line in case a previous tooltip
|
||||||
|
// is cut off and partially preserved, if the new one is shorter
|
||||||
|
e.write(e.Writer.ClearAfter())
|
||||||
|
e.write(e.Writer.CarriageForward())
|
||||||
|
e.write(e.Writer.GetCursorForRightWrite(length, 0))
|
||||||
|
e.write(text)
|
||||||
|
return e.string()
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
Loading…
Reference in a new issue