mirror of
https://github.com/JanDeDobbeleer/oh-my-posh.git
synced 2024-11-09 20:44:03 -08:00
refactor: move engine to module
This commit is contained in:
parent
c0f4b6d6f0
commit
fee29f4b2e
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -9,8 +9,6 @@
|
|||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
# Initialization scripts generated by https://github.com/kevinburke/go-bindata
|
||||
init.go
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package engine
|
||||
|
||||
import (
|
||||
"oh-my-posh/color"
|
||||
|
@ -56,7 +56,7 @@ func (b *Block) initPlain(env environment.Environment, config *Config) {
|
|||
b.ansi.Init(plain)
|
||||
b.writer = &color.AnsiWriter{
|
||||
Ansi: b.ansi,
|
||||
TerminalBackground: getConsoleBackgroundColor(env, config.TerminalBackground),
|
||||
TerminalBackground: GetConsoleBackgroundColor(env, config.TerminalBackground),
|
||||
AnsiColors: config.MakeColors(env),
|
||||
}
|
||||
b.env = env
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package engine
|
||||
|
||||
import (
|
||||
"testing"
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package engine
|
||||
|
||||
import (
|
||||
// "encoding/json"
|
||||
|
@ -108,7 +108,7 @@ func loadConfig(env environment.Environment) (*Config, error) {
|
|||
return &cfg, nil
|
||||
}
|
||||
|
||||
func exportConfig(configFile, format string) string {
|
||||
func ExportConfig(configFile, format string) string {
|
||||
if len(format) == 0 {
|
||||
format = config.JSON
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package engine
|
||||
|
||||
import (
|
||||
"oh-my-posh/segments"
|
||||
|
@ -11,7 +11,7 @@ import (
|
|||
|
||||
func TestSettingsExportJSON(t *testing.T) {
|
||||
defer testClearDefaultConfig()
|
||||
content := exportConfig("../themes/jandedobbeleer.omp.json", "json")
|
||||
content := ExportConfig("../themes/jandedobbeleer.omp.json", "json")
|
||||
assert.NotContains(t, content, "\\u003ctransparent\\u003e")
|
||||
assert.Contains(t, content, "<transparent>")
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package engine
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
@ -10,40 +10,40 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
type engine struct {
|
||||
config *Config
|
||||
env environment.Environment
|
||||
writer color.Writer
|
||||
ansi *color.Ansi
|
||||
consoleTitle *console.Title
|
||||
plain bool
|
||||
type Engine struct {
|
||||
Config *Config
|
||||
Env environment.Environment
|
||||
Writer color.Writer
|
||||
Ansi *color.Ansi
|
||||
ConsoleTitle *console.Title
|
||||
Plain bool
|
||||
|
||||
console strings.Builder
|
||||
rprompt string
|
||||
}
|
||||
|
||||
func (e *engine) write(text string) {
|
||||
func (e *Engine) write(text string) {
|
||||
e.console.WriteString(text)
|
||||
}
|
||||
|
||||
func (e *engine) writeANSI(text string) {
|
||||
if e.plain {
|
||||
func (e *Engine) writeANSI(text string) {
|
||||
if e.Plain {
|
||||
return
|
||||
}
|
||||
e.console.WriteString(text)
|
||||
}
|
||||
|
||||
func (e *engine) string() string {
|
||||
func (e *Engine) string() string {
|
||||
return e.console.String()
|
||||
}
|
||||
|
||||
func (e *engine) canWriteRPrompt() bool {
|
||||
func (e *Engine) canWriteRPrompt() bool {
|
||||
prompt := e.string()
|
||||
consoleWidth, err := e.env.TerminalWidth()
|
||||
consoleWidth, err := e.Env.TerminalWidth()
|
||||
if err != nil || consoleWidth == 0 {
|
||||
return true
|
||||
}
|
||||
promptWidth := e.ansi.LenWithoutANSI(prompt)
|
||||
promptWidth := e.Ansi.LenWithoutANSI(prompt)
|
||||
availableSpace := consoleWidth - promptWidth
|
||||
// spanning multiple lines
|
||||
if availableSpace < 0 {
|
||||
|
@ -51,37 +51,37 @@ func (e *engine) canWriteRPrompt() bool {
|
|||
availableSpace = consoleWidth - overflow
|
||||
}
|
||||
promptBreathingRoom := 30
|
||||
canWrite := (availableSpace - e.ansi.LenWithoutANSI(e.rprompt)) >= promptBreathingRoom
|
||||
canWrite := (availableSpace - e.Ansi.LenWithoutANSI(e.rprompt)) >= promptBreathingRoom
|
||||
return canWrite
|
||||
}
|
||||
|
||||
func (e *engine) render() string {
|
||||
for _, block := range e.config.Blocks {
|
||||
func (e *Engine) Render() string {
|
||||
for _, block := range e.Config.Blocks {
|
||||
e.renderBlock(block)
|
||||
}
|
||||
if e.config.ConsoleTitle {
|
||||
e.writeANSI(e.consoleTitle.GetTitle())
|
||||
if e.Config.ConsoleTitle {
|
||||
e.writeANSI(e.ConsoleTitle.GetTitle())
|
||||
}
|
||||
e.writeANSI(e.ansi.ColorReset())
|
||||
if e.config.FinalSpace {
|
||||
e.writeANSI(e.Ansi.ColorReset())
|
||||
if e.Config.FinalSpace {
|
||||
e.write(" ")
|
||||
}
|
||||
|
||||
if !e.config.OSC99 {
|
||||
if !e.Config.OSC99 {
|
||||
return e.print()
|
||||
}
|
||||
cwd := e.env.Pwd()
|
||||
e.writeANSI(e.ansi.ConsolePwd(cwd))
|
||||
cwd := e.Env.Pwd()
|
||||
e.writeANSI(e.Ansi.ConsolePwd(cwd))
|
||||
return e.print()
|
||||
}
|
||||
|
||||
func (e *engine) renderBlock(block *Block) {
|
||||
func (e *Engine) renderBlock(block *Block) {
|
||||
// when in bash, for rprompt blocks we need to write plain
|
||||
// and wrap in escaped mode or the prompt will not render correctly
|
||||
if block.Type == RPrompt && e.env.Shell() == bash {
|
||||
block.initPlain(e.env, e.config)
|
||||
if block.Type == RPrompt && e.Env.Shell() == bash {
|
||||
block.initPlain(e.Env, e.Config)
|
||||
} else {
|
||||
block.init(e.env, e.writer, e.ansi)
|
||||
block.init(e.Env, e.Writer, e.Ansi)
|
||||
}
|
||||
block.setStringValues()
|
||||
if !block.enabled() {
|
||||
|
@ -98,21 +98,21 @@ func (e *engine) renderBlock(block *Block) {
|
|||
e.write("\n")
|
||||
case Prompt:
|
||||
if block.VerticalOffset != 0 {
|
||||
e.writeANSI(e.ansi.ChangeLine(block.VerticalOffset))
|
||||
e.writeANSI(e.Ansi.ChangeLine(block.VerticalOffset))
|
||||
}
|
||||
switch block.Alignment {
|
||||
case Right:
|
||||
e.writeANSI(e.ansi.CarriageForward())
|
||||
e.writeANSI(e.Ansi.CarriageForward())
|
||||
blockText := block.renderSegments()
|
||||
e.writeANSI(e.ansi.GetCursorForRightWrite(blockText, block.HorizontalOffset))
|
||||
e.writeANSI(e.Ansi.GetCursorForRightWrite(blockText, block.HorizontalOffset))
|
||||
e.write(blockText)
|
||||
case Left:
|
||||
e.write(block.renderSegments())
|
||||
}
|
||||
case RPrompt:
|
||||
blockText := block.renderSegments()
|
||||
if e.env.Shell() == bash {
|
||||
blockText = e.ansi.FormatText(blockText)
|
||||
if e.Env.Shell() == bash {
|
||||
blockText = e.Ansi.FormatText(blockText)
|
||||
}
|
||||
e.rprompt = blockText
|
||||
}
|
||||
|
@ -120,33 +120,33 @@ func (e *engine) renderBlock(block *Block) {
|
|||
// If this doesn't happen, the portion after the prompt gets colored in the background
|
||||
// color of the line above the new input line. Clearing the line fixes this,
|
||||
// but can hopefully one day be removed when this is resolved natively.
|
||||
if e.env.Shell() == pwsh || e.env.Shell() == powershell5 {
|
||||
e.writeANSI(e.ansi.ClearAfter())
|
||||
if e.Env.Shell() == pwsh || e.Env.Shell() == powershell5 {
|
||||
e.writeANSI(e.Ansi.ClearAfter())
|
||||
}
|
||||
}
|
||||
|
||||
// debug will loop through your config file and output the timings for each segments
|
||||
func (e *engine) debug() string {
|
||||
func (e *Engine) Debug(version string) string {
|
||||
var segmentTimings []*SegmentTiming
|
||||
largestSegmentNameLength := 0
|
||||
e.write(fmt.Sprintf("\n\x1b[1mVersion:\x1b[0m %s\n", Version))
|
||||
e.write(fmt.Sprintf("\n\x1b[1mVersion:\x1b[0m %s\n", version))
|
||||
e.write("\n\x1b[1mSegments:\x1b[0m\n\n")
|
||||
// console title timing
|
||||
start := time.Now()
|
||||
consoleTitle := e.consoleTitle.GetTitle()
|
||||
consoleTitle := e.ConsoleTitle.GetTitle()
|
||||
duration := time.Since(start)
|
||||
segmentTiming := &SegmentTiming{
|
||||
name: "ConsoleTitle",
|
||||
nameLength: 12,
|
||||
enabled: e.config.ConsoleTitle,
|
||||
enabled: e.Config.ConsoleTitle,
|
||||
stringValue: consoleTitle,
|
||||
enabledDuration: 0,
|
||||
stringDuration: duration,
|
||||
}
|
||||
segmentTimings = append(segmentTimings, segmentTiming)
|
||||
// loop each segments of each blocks
|
||||
for _, block := range e.config.Blocks {
|
||||
block.init(e.env, e.writer, e.ansi)
|
||||
for _, block := range e.Config.Blocks {
|
||||
block.init(e.Env, e.Writer, e.Ansi)
|
||||
longestSegmentName, timings := block.debug()
|
||||
segmentTimings = append(segmentTimings, timings...)
|
||||
if longestSegmentName > largestSegmentNameLength {
|
||||
|
@ -165,16 +165,16 @@ func (e *engine) debug() string {
|
|||
e.write(fmt.Sprintf("%-*s - %3d ms - %s\n", largestSegmentNameLength, segmentName, duration, segment.stringValue))
|
||||
}
|
||||
e.write(fmt.Sprintf("\n\x1b[1mRun duration:\x1b[0m %s\n", time.Since(start)))
|
||||
e.write(fmt.Sprintf("\n\x1b[1mCache path:\x1b[0m %s\n", e.env.CachePath()))
|
||||
e.write(fmt.Sprintf("\n\x1b[1mCache path:\x1b[0m %s\n", e.Env.CachePath()))
|
||||
e.write("\n\x1b[1mLogs:\x1b[0m\n\n")
|
||||
e.write(e.env.Logs())
|
||||
e.write(e.Env.Logs())
|
||||
return e.string()
|
||||
}
|
||||
|
||||
func (e *engine) print() string {
|
||||
switch e.env.Shell() {
|
||||
func (e *Engine) print() string {
|
||||
switch e.Env.Shell() {
|
||||
case zsh:
|
||||
if !*e.env.Args().Eval {
|
||||
if !*e.Env.Args().Eval {
|
||||
break
|
||||
}
|
||||
// escape double quotes contained in the prompt
|
||||
|
@ -182,22 +182,22 @@ func (e *engine) print() string {
|
|||
prompt += fmt.Sprintf("\nRPROMPT=\"%s\"", e.rprompt)
|
||||
return prompt
|
||||
case pwsh, powershell5, bash, plain:
|
||||
if e.rprompt == "" || !e.canWriteRPrompt() || e.plain {
|
||||
if e.rprompt == "" || !e.canWriteRPrompt() || e.Plain {
|
||||
break
|
||||
}
|
||||
e.write(e.ansi.SaveCursorPosition())
|
||||
e.write(e.ansi.CarriageForward())
|
||||
e.write(e.ansi.GetCursorForRightWrite(e.rprompt, 0))
|
||||
e.write(e.Ansi.SaveCursorPosition())
|
||||
e.write(e.Ansi.CarriageForward())
|
||||
e.write(e.Ansi.GetCursorForRightWrite(e.rprompt, 0))
|
||||
e.write(e.rprompt)
|
||||
e.write(e.ansi.RestoreCursorPosition())
|
||||
e.write(e.Ansi.RestoreCursorPosition())
|
||||
}
|
||||
return e.string()
|
||||
}
|
||||
|
||||
func (e *engine) renderTooltip(tip string) string {
|
||||
func (e *Engine) RenderTooltip(tip string) string {
|
||||
tip = strings.Trim(tip, " ")
|
||||
var tooltip *Segment
|
||||
for _, tp := range e.config.Tooltips {
|
||||
for _, tp := range e.Config.Tooltips {
|
||||
if !tp.shouldInvokeWithTip(tip) {
|
||||
continue
|
||||
}
|
||||
|
@ -206,7 +206,7 @@ func (e *engine) renderTooltip(tip string) string {
|
|||
if tooltip == nil {
|
||||
return ""
|
||||
}
|
||||
if err := tooltip.mapSegmentWithWriter(e.env); err != nil {
|
||||
if err := tooltip.mapSegmentWithWriter(e.Env); err != nil {
|
||||
return ""
|
||||
}
|
||||
if !tooltip.enabled() {
|
||||
|
@ -218,53 +218,53 @@ func (e *engine) renderTooltip(tip string) string {
|
|||
Alignment: Right,
|
||||
Segments: []*Segment{tooltip},
|
||||
}
|
||||
switch e.env.Shell() {
|
||||
switch e.Env.Shell() {
|
||||
case zsh, winCMD:
|
||||
block.init(e.env, e.writer, e.ansi)
|
||||
block.init(e.Env, e.Writer, e.Ansi)
|
||||
return block.renderSegments()
|
||||
case pwsh, powershell5:
|
||||
block.initPlain(e.env, e.config)
|
||||
block.initPlain(e.Env, e.Config)
|
||||
tooltipText := block.renderSegments()
|
||||
e.write(e.ansi.ClearAfter())
|
||||
e.write(e.ansi.CarriageForward())
|
||||
e.write(e.ansi.GetCursorForRightWrite(tooltipText, 0))
|
||||
e.write(e.Ansi.ClearAfter())
|
||||
e.write(e.Ansi.CarriageForward())
|
||||
e.write(e.Ansi.GetCursorForRightWrite(tooltipText, 0))
|
||||
e.write(tooltipText)
|
||||
return e.string()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (e *engine) renderTransientPrompt() string {
|
||||
if e.config.TransientPrompt == nil {
|
||||
func (e *Engine) RenderTransientPrompt() string {
|
||||
if e.Config.TransientPrompt == nil {
|
||||
return ""
|
||||
}
|
||||
promptTemplate := e.config.TransientPrompt.Template
|
||||
promptTemplate := e.Config.TransientPrompt.Template
|
||||
if len(promptTemplate) == 0 {
|
||||
promptTemplate = "{{ .Shell }}> "
|
||||
}
|
||||
tmpl := &template.Text{
|
||||
Template: promptTemplate,
|
||||
Env: e.env,
|
||||
Env: e.Env,
|
||||
}
|
||||
prompt, err := tmpl.Render()
|
||||
if err != nil {
|
||||
prompt = err.Error()
|
||||
}
|
||||
e.writer.SetColors(e.config.TransientPrompt.Background, e.config.TransientPrompt.Foreground)
|
||||
e.writer.Write(e.config.TransientPrompt.Background, e.config.TransientPrompt.Foreground, prompt)
|
||||
switch e.env.Shell() {
|
||||
e.Writer.SetColors(e.Config.TransientPrompt.Background, e.Config.TransientPrompt.Foreground)
|
||||
e.Writer.Write(e.Config.TransientPrompt.Background, e.Config.TransientPrompt.Foreground, prompt)
|
||||
switch e.Env.Shell() {
|
||||
case zsh:
|
||||
// escape double quotes contained in the prompt
|
||||
prompt := fmt.Sprintf("PS1=\"%s\"", strings.ReplaceAll(e.writer.String(), "\"", "\"\""))
|
||||
prompt := fmt.Sprintf("PS1=\"%s\"", strings.ReplaceAll(e.Writer.String(), "\"", "\"\""))
|
||||
prompt += "\nRPROMPT=\"\""
|
||||
return prompt
|
||||
case pwsh, powershell5, winCMD:
|
||||
return e.writer.String()
|
||||
return e.Writer.String()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (e *engine) renderRPrompt() string {
|
||||
func (e *Engine) RenderRPrompt() string {
|
||||
filterRPromptBlock := func(blocks []*Block) *Block {
|
||||
for _, block := range blocks {
|
||||
if block.Type == RPrompt {
|
||||
|
@ -273,11 +273,11 @@ func (e *engine) renderRPrompt() string {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
block := filterRPromptBlock(e.config.Blocks)
|
||||
block := filterRPromptBlock(e.Config.Blocks)
|
||||
if block == nil {
|
||||
return ""
|
||||
}
|
||||
block.init(e.env, e.writer, e.ansi)
|
||||
block.init(e.Env, e.Writer, e.Ansi)
|
||||
block.setStringValues()
|
||||
if !block.enabled() {
|
||||
return ""
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package engine
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
@ -37,9 +37,9 @@ func TestCanWriteRPrompt(t *testing.T) {
|
|||
env.On("TerminalWidth").Return(tc.TerminalWidth, tc.TerminalWidthError)
|
||||
ansi := &color.Ansi{}
|
||||
ansi.Init(plain)
|
||||
engine := &engine{
|
||||
env: env,
|
||||
ansi: ansi,
|
||||
engine := &Engine{
|
||||
Env: env,
|
||||
Ansi: ansi,
|
||||
}
|
||||
engine.rprompt = strings.Repeat("x", tc.RPromptLength)
|
||||
engine.console.WriteString(strings.Repeat("x", tc.PromptLength))
|
||||
|
@ -101,7 +101,7 @@ func engineRender(configPath string) error {
|
|||
writerColors := cfg.MakeColors(env)
|
||||
writer := &color.AnsiWriter{
|
||||
Ansi: ansi,
|
||||
TerminalBackground: getConsoleBackgroundColor(env, cfg.TerminalBackground),
|
||||
TerminalBackground: GetConsoleBackgroundColor(env, cfg.TerminalBackground),
|
||||
AnsiColors: writerColors,
|
||||
}
|
||||
consoleTitle := &console.Title{
|
||||
|
@ -110,16 +110,16 @@ func engineRender(configPath string) error {
|
|||
Style: cfg.ConsoleTitleStyle,
|
||||
Template: cfg.ConsoleTitleTemplate,
|
||||
}
|
||||
engine := &engine{
|
||||
config: cfg,
|
||||
env: env,
|
||||
writer: writer,
|
||||
consoleTitle: consoleTitle,
|
||||
ansi: ansi,
|
||||
plain: *args.Plain,
|
||||
engine := &Engine{
|
||||
Config: cfg,
|
||||
Env: env,
|
||||
Writer: writer,
|
||||
ConsoleTitle: consoleTitle,
|
||||
Ansi: ansi,
|
||||
Plain: *args.Plain,
|
||||
}
|
||||
|
||||
engine.render()
|
||||
engine.Render()
|
||||
|
||||
return nil
|
||||
}
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
// https://github.com/homeport/termshot
|
||||
|
||||
package main
|
||||
package engine
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
|
@ -97,10 +97,14 @@ func NewRGBColor(ansiColor string) *RGB {
|
|||
}
|
||||
|
||||
type ImageRenderer struct {
|
||||
ansiString string
|
||||
author string
|
||||
ansi *color.Ansi
|
||||
bgColor string
|
||||
AnsiString string
|
||||
Author string
|
||||
CursorPadding int
|
||||
RPromptOffset int
|
||||
BgColor string
|
||||
Ansi *color.Ansi
|
||||
|
||||
path string
|
||||
|
||||
factor float64
|
||||
|
||||
|
@ -128,11 +132,12 @@ type ImageRenderer struct {
|
|||
backgroundColor *RGB
|
||||
foregroundColor *RGB
|
||||
ansiSequenceRegexMap map[string]string
|
||||
rPromptOffset int
|
||||
cursorPadding int
|
||||
}
|
||||
|
||||
func (ir *ImageRenderer) init() {
|
||||
func (ir *ImageRenderer) Init(config string) {
|
||||
match := regex.FindNamedRegexMatch(`.*(\/|\\)(?P<STR>.+).omp.(json|yaml|toml)`, config)
|
||||
ir.path = fmt.Sprintf("%s.png", match[str])
|
||||
|
||||
f := 2.0
|
||||
|
||||
ir.cleanContent()
|
||||
|
@ -244,8 +249,8 @@ func (ir *ImageRenderer) runeAdditionalWidth(r rune) int {
|
|||
|
||||
func (ir *ImageRenderer) calculateWidth() int {
|
||||
longest := 0
|
||||
for _, line := range strings.Split(ir.ansiString, "\n") {
|
||||
length := ir.ansi.LenWithoutANSI(line)
|
||||
for _, line := range strings.Split(ir.AnsiString, "\n") {
|
||||
length := ir.Ansi.LenWithoutANSI(line)
|
||||
for _, char := range line {
|
||||
length += ir.runeAdditionalWidth(char)
|
||||
}
|
||||
|
@ -258,27 +263,27 @@ func (ir *ImageRenderer) calculateWidth() int {
|
|||
|
||||
func (ir *ImageRenderer) cleanContent() {
|
||||
rPromptAnsi := "\x1b7\x1b[1000C"
|
||||
hasRPrompt := strings.Contains(ir.ansiString, rPromptAnsi)
|
||||
hasRPrompt := strings.Contains(ir.AnsiString, rPromptAnsi)
|
||||
// clean abundance of empty lines
|
||||
ir.ansiString = strings.Trim(ir.ansiString, "\n")
|
||||
ir.ansiString = "\n" + ir.ansiString
|
||||
ir.AnsiString = strings.Trim(ir.AnsiString, "\n")
|
||||
ir.AnsiString = "\n" + ir.AnsiString
|
||||
// clean string before render
|
||||
ir.ansiString = strings.ReplaceAll(ir.ansiString, "\x1b[m", "\x1b[0m")
|
||||
ir.ansiString = strings.ReplaceAll(ir.ansiString, "\x1b[K", "")
|
||||
ir.ansiString = strings.ReplaceAll(ir.ansiString, "\x1b[1F", "")
|
||||
ir.ansiString = strings.ReplaceAll(ir.ansiString, "\x1b8", "")
|
||||
ir.ansiString = strings.ReplaceAll(ir.ansiString, "\u2800", " ")
|
||||
ir.AnsiString = strings.ReplaceAll(ir.AnsiString, "\x1b[m", "\x1b[0m")
|
||||
ir.AnsiString = strings.ReplaceAll(ir.AnsiString, "\x1b[K", "")
|
||||
ir.AnsiString = strings.ReplaceAll(ir.AnsiString, "\x1b[1F", "")
|
||||
ir.AnsiString = strings.ReplaceAll(ir.AnsiString, "\x1b8", "")
|
||||
ir.AnsiString = strings.ReplaceAll(ir.AnsiString, "\u2800", " ")
|
||||
// replace rprompt with adding and mark right aligned blocks with a pointer
|
||||
ir.ansiString = strings.ReplaceAll(ir.ansiString, rPromptAnsi, fmt.Sprintf("_%s", strings.Repeat(" ", ir.cursorPadding)))
|
||||
ir.ansiString = strings.ReplaceAll(ir.ansiString, "\x1b[1000C", strings.Repeat(" ", ir.rPromptOffset))
|
||||
ir.AnsiString = strings.ReplaceAll(ir.AnsiString, rPromptAnsi, fmt.Sprintf("_%s", strings.Repeat(" ", ir.CursorPadding)))
|
||||
ir.AnsiString = strings.ReplaceAll(ir.AnsiString, "\x1b[1000C", strings.Repeat(" ", ir.RPromptOffset))
|
||||
if !hasRPrompt {
|
||||
ir.ansiString += fmt.Sprintf("_%s", strings.Repeat(" ", ir.cursorPadding))
|
||||
ir.AnsiString += fmt.Sprintf("_%s", strings.Repeat(" ", ir.CursorPadding))
|
||||
}
|
||||
// add watermarks
|
||||
ir.ansiString += "\n\n\x1b[1mhttps://ohmyposh.dev\x1b[22m"
|
||||
if len(ir.author) > 0 {
|
||||
createdBy := fmt.Sprintf(" by \x1b[1m%s\x1b[22m", ir.author)
|
||||
ir.ansiString += createdBy
|
||||
ir.AnsiString += "\n\n\x1b[1mhttps://ohmyposh.dev\x1b[22m"
|
||||
if len(ir.Author) > 0 {
|
||||
createdBy := fmt.Sprintf(" by \x1b[1m%s\x1b[22m", ir.Author)
|
||||
ir.AnsiString += createdBy
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -290,11 +295,11 @@ func (ir *ImageRenderer) measureContent() (width, height float64) {
|
|||
advance := tmpDrawer.MeasureString(strings.Repeat(" ", linewidth))
|
||||
width = float64(advance >> 6)
|
||||
// height, lines times font height and line spacing
|
||||
height = float64(len(strings.Split(ir.ansiString, "\n"))) * ir.fontHeight() * ir.lineSpacing
|
||||
height = float64(len(strings.Split(ir.AnsiString, "\n"))) * ir.fontHeight() * ir.lineSpacing
|
||||
return width, height
|
||||
}
|
||||
|
||||
func (ir *ImageRenderer) SavePNG(path string) error {
|
||||
func (ir *ImageRenderer) SavePNG() error {
|
||||
var f = func(value float64) float64 { return ir.factor * value }
|
||||
|
||||
var (
|
||||
|
@ -345,7 +350,7 @@ func (ir *ImageRenderer) SavePNG(path string) error {
|
|||
// Draw rounded rectangle with outline and three button to produce the
|
||||
// impression of a window with controls and a content area
|
||||
dc.DrawRoundedRectangle(xOffset, yOffset, width-2*marginX, height-2*marginY, corner)
|
||||
dc.SetHexColor(ir.bgColor)
|
||||
dc.SetHexColor(ir.BgColor)
|
||||
dc.Fill()
|
||||
|
||||
dc.DrawRoundedRectangle(xOffset, yOffset, width-2*marginX, height-2*marginY, corner)
|
||||
|
@ -362,16 +367,16 @@ func (ir *ImageRenderer) SavePNG(path string) error {
|
|||
// Apply the actual text into the prepared content area of the window
|
||||
var x, y float64 = xOffset + paddingX, yOffset + paddingY + titleOffset + ir.fontHeight()
|
||||
|
||||
for len(ir.ansiString) != 0 {
|
||||
for len(ir.AnsiString) != 0 {
|
||||
if !ir.shouldPrint() {
|
||||
continue
|
||||
}
|
||||
runes := []rune(ir.ansiString)
|
||||
runes := []rune(ir.AnsiString)
|
||||
if len(runes) == 0 {
|
||||
continue
|
||||
}
|
||||
str := string(runes[0:1])
|
||||
ir.ansiString = string(runes[1:])
|
||||
ir.AnsiString = string(runes[1:])
|
||||
switch ir.style {
|
||||
case bold:
|
||||
dc.SetFontFace(ir.bold)
|
||||
|
@ -419,16 +424,16 @@ func (ir *ImageRenderer) SavePNG(path string) error {
|
|||
x += w
|
||||
}
|
||||
|
||||
return dc.SavePNG(path)
|
||||
return dc.SavePNG(ir.path)
|
||||
}
|
||||
|
||||
func (ir *ImageRenderer) shouldPrint() bool {
|
||||
for sequence, re := range ir.ansiSequenceRegexMap {
|
||||
match := regex.FindNamedRegexMatch(re, ir.ansiString)
|
||||
match := regex.FindNamedRegexMatch(re, ir.AnsiString)
|
||||
if len(match) == 0 {
|
||||
continue
|
||||
}
|
||||
ir.ansiString = strings.TrimPrefix(ir.ansiString, match[str])
|
||||
ir.AnsiString = strings.TrimPrefix(ir.AnsiString, match[str])
|
||||
switch sequence {
|
||||
case invertedColor:
|
||||
ir.foregroundColor = ir.defaultBackgroundColor
|
||||
|
@ -463,7 +468,7 @@ func (ir *ImageRenderer) shouldPrint() bool {
|
|||
ir.setBase16Color(match[fg])
|
||||
return false
|
||||
case link:
|
||||
ir.ansiString = match[url] + ir.ansiString
|
||||
ir.AnsiString = match[url] + ir.AnsiString
|
||||
}
|
||||
}
|
||||
return true
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package engine
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
|
@ -10,7 +10,7 @@ import (
|
|||
)
|
||||
|
||||
func runImageTest(content string) error {
|
||||
poshImagePath := "ohmyposh.png"
|
||||
poshImagePath := "jandedobbeleer.png"
|
||||
file, err := ioutil.TempFile("", poshImagePath)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -19,11 +19,11 @@ func runImageTest(content string) error {
|
|||
ansi := &color.Ansi{}
|
||||
ansi.Init(plain)
|
||||
image := &ImageRenderer{
|
||||
ansiString: content,
|
||||
ansi: ansi,
|
||||
AnsiString: content,
|
||||
Ansi: ansi,
|
||||
}
|
||||
image.init()
|
||||
err = image.SavePNG(poshImagePath)
|
||||
image.Init("~/jandedobbeleer.omp.json")
|
||||
err = image.SavePNG()
|
||||
return err
|
||||
}
|
||||
|
112
src/engine/init.go
Normal file
112
src/engine/init.go
Normal file
|
@ -0,0 +1,112 @@
|
|||
package engine
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
|
||||
"fmt"
|
||||
"oh-my-posh/environment"
|
||||
"oh-my-posh/template"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//go:embed init/omp.ps1
|
||||
var pwshInit string
|
||||
|
||||
//go:embed init/omp.fish
|
||||
var fishInit string
|
||||
|
||||
//go:embed init/omp.bash
|
||||
var bashInit string
|
||||
|
||||
//go:embed init/omp.zsh
|
||||
var zshInit string
|
||||
|
||||
//go:embed init/omp.lua
|
||||
var cmdInit string
|
||||
|
||||
const (
|
||||
noExe = "echo \"Unable to find Oh My Posh executable\""
|
||||
|
||||
zsh = "zsh"
|
||||
bash = "bash"
|
||||
pwsh = "pwsh"
|
||||
fish = "fish"
|
||||
powershell5 = "powershell"
|
||||
winCMD = "cmd"
|
||||
plain = "shell"
|
||||
)
|
||||
|
||||
func getExecutablePath(shell string) (string, error) {
|
||||
executable, err := os.Executable()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// On Windows, it fails when the excutable is called in MSYS2 for example
|
||||
// which uses unix style paths to resolve the executable's location.
|
||||
// PowerShell knows how to resolve both, so we can swap this without any issue.
|
||||
executable = strings.ReplaceAll(executable, "\\", "/")
|
||||
switch shell {
|
||||
case bash, zsh:
|
||||
return strings.ReplaceAll(executable, " ", "\\ "), nil
|
||||
}
|
||||
return executable, nil
|
||||
}
|
||||
|
||||
func InitShell(shell, configFile string) string {
|
||||
executable, err := getExecutablePath(shell)
|
||||
if err != nil {
|
||||
return noExe
|
||||
}
|
||||
switch shell {
|
||||
case pwsh, powershell5:
|
||||
return fmt.Sprintf("(@(&\"%s\" --print-init --shell=%s --config=\"%s\") -join \"`n\") | Invoke-Expression", executable, shell, configFile)
|
||||
case zsh, bash, fish, winCMD:
|
||||
return PrintShellInit(shell, configFile)
|
||||
default:
|
||||
return fmt.Sprintf("echo \"No initialization script available for %s\"", shell)
|
||||
}
|
||||
}
|
||||
|
||||
func PrintShellInit(shell, configFile string) string {
|
||||
executable, err := getExecutablePath(shell)
|
||||
if err != nil {
|
||||
return noExe
|
||||
}
|
||||
switch shell {
|
||||
case pwsh, powershell5:
|
||||
return getShellInitScript(executable, configFile, pwshInit)
|
||||
case zsh:
|
||||
return getShellInitScript(executable, configFile, zshInit)
|
||||
case bash:
|
||||
return getShellInitScript(executable, configFile, bashInit)
|
||||
case fish:
|
||||
return getShellInitScript(executable, configFile, fishInit)
|
||||
case winCMD:
|
||||
return getShellInitScript(executable, configFile, cmdInit)
|
||||
default:
|
||||
return fmt.Sprintf("echo \"No initialization script available for %s\"", shell)
|
||||
}
|
||||
}
|
||||
|
||||
func getShellInitScript(executable, configFile, script string) string {
|
||||
script = strings.ReplaceAll(script, "::OMP::", executable)
|
||||
script = strings.ReplaceAll(script, "::CONFIG::", configFile)
|
||||
return script
|
||||
}
|
||||
|
||||
func GetConsoleBackgroundColor(env environment.Environment, backgroundColorTemplate string) string {
|
||||
if len(backgroundColorTemplate) == 0 {
|
||||
return backgroundColorTemplate
|
||||
}
|
||||
tmpl := &template.Text{
|
||||
Template: backgroundColorTemplate,
|
||||
Context: nil,
|
||||
Env: env,
|
||||
}
|
||||
text, err := tmpl.Render()
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
return text
|
||||
}
|
31
src/engine/init_test.go
Normal file
31
src/engine/init_test.go
Normal file
|
@ -0,0 +1,31 @@
|
|||
package engine
|
||||
|
||||
import (
|
||||
"oh-my-posh/environment"
|
||||
"oh-my-posh/mock"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestConsoleBackgroundColorTemplate(t *testing.T) {
|
||||
cases := []struct {
|
||||
Case string
|
||||
Expected string
|
||||
Term string
|
||||
}{
|
||||
{Case: "Inside vscode", Expected: "#123456", Term: "vscode"},
|
||||
{Case: "Outside vscode", Expected: "", Term: "windowsterminal"},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
env := new(mock.MockedEnvironment)
|
||||
env.On("TemplateCache").Return(&environment.TemplateCache{
|
||||
Env: map[string]string{
|
||||
"TERM_PROGRAM": tc.Term,
|
||||
},
|
||||
})
|
||||
color := GetConsoleBackgroundColor(env, "{{ if eq \"vscode\" .Env.TERM_PROGRAM }}#123456{{end}}")
|
||||
assert.Equal(t, tc.Expected, color, tc.Case)
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package engine
|
||||
|
||||
import (
|
||||
"errors"
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package engine
|
||||
|
||||
import (
|
||||
"encoding/json"
|
160
src/main.go
160
src/main.go
|
@ -1,16 +1,12 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"flag"
|
||||
"fmt"
|
||||
"oh-my-posh/color"
|
||||
"oh-my-posh/console"
|
||||
"oh-my-posh/engine"
|
||||
"oh-my-posh/environment"
|
||||
"oh-my-posh/regex"
|
||||
"oh-my-posh/template"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gookit/config/v2"
|
||||
|
@ -19,33 +15,6 @@ import (
|
|||
// Version number of oh-my-posh
|
||||
var Version = "development"
|
||||
|
||||
//go:embed init/omp.ps1
|
||||
var pwshInit string
|
||||
|
||||
//go:embed init/omp.fish
|
||||
var fishInit string
|
||||
|
||||
//go:embed init/omp.bash
|
||||
var bashInit string
|
||||
|
||||
//go:embed init/omp.zsh
|
||||
var zshInit string
|
||||
|
||||
//go:embed init/omp.lua
|
||||
var cmdInit string
|
||||
|
||||
const (
|
||||
noExe = "echo \"Unable to find Oh My Posh executable\""
|
||||
|
||||
zsh = "zsh"
|
||||
bash = "bash"
|
||||
pwsh = "pwsh"
|
||||
fish = "fish"
|
||||
powershell5 = "powershell"
|
||||
winCMD = "cmd"
|
||||
plain = "shell"
|
||||
)
|
||||
|
||||
func main() {
|
||||
args := &environment.Args{
|
||||
ErrorCode: flag.Int(
|
||||
|
@ -174,20 +143,20 @@ func main() {
|
|||
return
|
||||
}
|
||||
if *args.Init {
|
||||
init := initShell(*args.Shell, *args.Config)
|
||||
init := engine.InitShell(*args.Shell, *args.Config)
|
||||
fmt.Print(init)
|
||||
return
|
||||
}
|
||||
if *args.PrintInit {
|
||||
init := printShellInit(*args.Shell, *args.Config)
|
||||
init := engine.PrintShellInit(*args.Shell, *args.Config)
|
||||
fmt.Print(init)
|
||||
return
|
||||
}
|
||||
if *args.PrintConfig {
|
||||
fmt.Print(exportConfig(*args.Config, *args.ConfigFormat))
|
||||
fmt.Print(engine.ExportConfig(*args.Config, *args.ConfigFormat))
|
||||
return
|
||||
}
|
||||
cfg := GetConfig(env)
|
||||
cfg := engine.GetConfig(env)
|
||||
ansi := &color.Ansi{}
|
||||
ansi.Init(env.Shell())
|
||||
var writer color.Writer
|
||||
|
@ -197,7 +166,7 @@ func main() {
|
|||
writerColors := cfg.MakeColors(env)
|
||||
writer = &color.AnsiWriter{
|
||||
Ansi: ansi,
|
||||
TerminalBackground: getConsoleBackgroundColor(env, cfg.TerminalBackground),
|
||||
TerminalBackground: engine.GetConsoleBackgroundColor(env, cfg.TerminalBackground),
|
||||
AnsiColors: writerColors,
|
||||
}
|
||||
}
|
||||
|
@ -207,121 +176,46 @@ func main() {
|
|||
Template: cfg.ConsoleTitleTemplate,
|
||||
Style: cfg.ConsoleTitleStyle,
|
||||
}
|
||||
engine := &engine{
|
||||
config: cfg,
|
||||
env: env,
|
||||
writer: writer,
|
||||
consoleTitle: consoleTitle,
|
||||
ansi: ansi,
|
||||
plain: *args.Plain,
|
||||
eng := &engine.Engine{
|
||||
Config: cfg,
|
||||
Env: env,
|
||||
Writer: writer,
|
||||
ConsoleTitle: consoleTitle,
|
||||
Ansi: ansi,
|
||||
Plain: *args.Plain,
|
||||
}
|
||||
if *args.Debug {
|
||||
fmt.Print(engine.debug())
|
||||
fmt.Print(eng.Debug(Version))
|
||||
return
|
||||
}
|
||||
if *args.PrintTransient {
|
||||
fmt.Print(engine.renderTransientPrompt())
|
||||
fmt.Print(eng.RenderTransientPrompt())
|
||||
return
|
||||
}
|
||||
if len(*args.Command) != 0 {
|
||||
fmt.Print(engine.renderTooltip(*args.Command))
|
||||
fmt.Print(eng.RenderTooltip(*args.Command))
|
||||
return
|
||||
}
|
||||
if *args.RPrompt {
|
||||
fmt.Print(engine.renderRPrompt())
|
||||
fmt.Print(eng.RenderRPrompt())
|
||||
return
|
||||
}
|
||||
prompt := engine.render()
|
||||
prompt := eng.Render()
|
||||
if !*args.ExportPNG {
|
||||
fmt.Print(prompt)
|
||||
return
|
||||
}
|
||||
imageCreator := &ImageRenderer{
|
||||
ansiString: prompt,
|
||||
author: *args.Author,
|
||||
cursorPadding: *args.CursorPadding,
|
||||
rPromptOffset: *args.RPromptOffset,
|
||||
bgColor: *args.BGColor,
|
||||
ansi: ansi,
|
||||
imageCreator := &engine.ImageRenderer{
|
||||
AnsiString: prompt,
|
||||
Author: *args.Author,
|
||||
CursorPadding: *args.CursorPadding,
|
||||
RPromptOffset: *args.RPromptOffset,
|
||||
BgColor: *args.BGColor,
|
||||
Ansi: ansi,
|
||||
}
|
||||
imageCreator.init()
|
||||
match := regex.FindNamedRegexMatch(`.*(\/|\\)(?P<STR>.+).omp.(json|yaml|toml)`, *args.Config)
|
||||
err := imageCreator.SavePNG(fmt.Sprintf("%s.png", match[str]))
|
||||
imageCreator.Init(*args.Config)
|
||||
err := imageCreator.SavePNG()
|
||||
if err != nil {
|
||||
fmt.Print(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func getExecutablePath(shell string) (string, error) {
|
||||
executable, err := os.Executable()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// On Windows, it fails when the excutable is called in MSYS2 for example
|
||||
// which uses unix style paths to resolve the executable's location.
|
||||
// PowerShell knows how to resolve both, so we can swap this without any issue.
|
||||
executable = strings.ReplaceAll(executable, "\\", "/")
|
||||
switch shell {
|
||||
case bash, zsh:
|
||||
return strings.ReplaceAll(executable, " ", "\\ "), nil
|
||||
}
|
||||
return executable, nil
|
||||
}
|
||||
|
||||
func initShell(shell, configFile string) string {
|
||||
executable, err := getExecutablePath(shell)
|
||||
if err != nil {
|
||||
return noExe
|
||||
}
|
||||
switch shell {
|
||||
case pwsh, powershell5:
|
||||
return fmt.Sprintf("(@(&\"%s\" --print-init --shell=%s --config=\"%s\") -join \"`n\") | Invoke-Expression", executable, shell, configFile)
|
||||
case zsh, bash, fish, winCMD:
|
||||
return printShellInit(shell, configFile)
|
||||
default:
|
||||
return fmt.Sprintf("echo \"No initialization script available for %s\"", shell)
|
||||
}
|
||||
}
|
||||
|
||||
func printShellInit(shell, configFile string) string {
|
||||
executable, err := getExecutablePath(shell)
|
||||
if err != nil {
|
||||
return noExe
|
||||
}
|
||||
switch shell {
|
||||
case pwsh, powershell5:
|
||||
return getShellInitScript(executable, configFile, pwshInit)
|
||||
case zsh:
|
||||
return getShellInitScript(executable, configFile, zshInit)
|
||||
case bash:
|
||||
return getShellInitScript(executable, configFile, bashInit)
|
||||
case fish:
|
||||
return getShellInitScript(executable, configFile, fishInit)
|
||||
case winCMD:
|
||||
return getShellInitScript(executable, configFile, cmdInit)
|
||||
default:
|
||||
return fmt.Sprintf("echo \"No initialization script available for %s\"", shell)
|
||||
}
|
||||
}
|
||||
|
||||
func getShellInitScript(executable, configFile, script string) string {
|
||||
script = strings.ReplaceAll(script, "::OMP::", executable)
|
||||
script = strings.ReplaceAll(script, "::CONFIG::", configFile)
|
||||
return script
|
||||
}
|
||||
|
||||
func getConsoleBackgroundColor(env environment.Environment, backgroundColorTemplate string) string {
|
||||
if len(backgroundColorTemplate) == 0 {
|
||||
return backgroundColorTemplate
|
||||
}
|
||||
tmpl := &template.Text{
|
||||
Template: backgroundColorTemplate,
|
||||
Context: nil,
|
||||
Env: env,
|
||||
}
|
||||
text, err := tmpl.Render()
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
|
|
@ -8,28 +8,6 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestConsoleBackgroundColorTemplate(t *testing.T) {
|
||||
cases := []struct {
|
||||
Case string
|
||||
Expected string
|
||||
Term string
|
||||
}{
|
||||
{Case: "Inside vscode", Expected: "#123456", Term: "vscode"},
|
||||
{Case: "Outside vscode", Expected: "", Term: "windowsterminal"},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
env := new(mock.MockedEnvironment)
|
||||
env.On("TemplateCache").Return(&environment.TemplateCache{
|
||||
Env: map[string]string{
|
||||
"TERM_PROGRAM": tc.Term,
|
||||
},
|
||||
})
|
||||
color := getConsoleBackgroundColor(env, "{{ if eq \"vscode\" .Env.TERM_PROGRAM }}#123456{{end}}")
|
||||
assert.Equal(t, tc.Expected, color, tc.Case)
|
||||
}
|
||||
}
|
||||
|
||||
// This can only be tested here due to circular dependencies
|
||||
// Which might be an indaction of a fault architecture but
|
||||
// I honestly could not figure out how to do this better
|
||||
|
|
Loading…
Reference in a new issue