refactor: remove env from template/text

This commit is contained in:
Jan De Dobbeleer 2024-08-07 17:19:24 +02:00 committed by Jan De Dobbeleer
parent 701cd499df
commit cefe985bf7
35 changed files with 237 additions and 380 deletions

View file

@ -1,13 +1,10 @@
package cache
import (
"sync"
"github.com/jandedobbeleer/oh-my-posh/src/maps"
)
type Template struct {
Env map[string]string
SegmentsCache maps.Simple
Segments *maps.Concurrent
Var maps.Simple
@ -24,7 +21,6 @@ type Template struct {
PromptCount int
SHLVL int
Jobs int
sync.RWMutex
WSL bool
Root bool
Initialized bool

View file

@ -69,7 +69,7 @@ Exports the config to an image file using customized output options.`,
env.Var = cfg.Var
terminal.Init(shell.GENERIC)
terminal.BackgroundColor = cfg.TerminalBackground.ResolveTemplate(env)
terminal.BackgroundColor = cfg.TerminalBackground.ResolveTemplate()
terminal.Colors = cfg.MakeColors()
eng := &prompt.Engine{

View file

@ -55,7 +55,7 @@ func createDebugCmd() *cobra.Command {
env.Var = cfg.Var
terminal.Init(shell.GENERIC)
terminal.BackgroundColor = cfg.TerminalBackground.ResolveTemplate(env)
terminal.BackgroundColor = cfg.TerminalBackground.ResolveTemplate()
terminal.Colors = cfg.MakeColors()
terminal.Plain = plain

View file

@ -7,6 +7,7 @@ import (
"github.com/jandedobbeleer/oh-my-posh/src/config"
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
"github.com/jandedobbeleer/oh-my-posh/src/shell"
"github.com/jandedobbeleer/oh-my-posh/src/template"
"github.com/spf13/cobra"
)
@ -67,6 +68,8 @@ See the documentation to initialize your shell: https://ohmyposh.dev/docs/instal
env.Init()
defer env.Close()
template.Init(env)
cfg := config.Load(env)
feats := cfg.Features()

View file

@ -120,7 +120,7 @@ func (c Ansi) ToForeground() Ansi {
return c
}
func (c Ansi) ResolveTemplate(env runtime.Environment) Ansi {
func (c Ansi) ResolveTemplate() Ansi {
if c.IsEmpty() {
return c
}
@ -132,7 +132,6 @@ func (c Ansi) ResolveTemplate(env runtime.Environment) Ansi {
tmpl := &template.Text{
Template: string(c),
Context: nil,
Env: env,
}
text, err := tmpl.Render()

View file

@ -9,6 +9,7 @@ import (
cache_ "github.com/jandedobbeleer/oh-my-posh/src/cache/mock"
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
"github.com/jandedobbeleer/oh-my-posh/src/runtime/mock"
"github.com/jandedobbeleer/oh-my-posh/src/template"
testify_ "github.com/stretchr/testify/mock"
)
@ -81,15 +82,14 @@ func TestAnsiRender(t *testing.T) {
for _, tc := range cases {
env := new(mock.Environment)
env.On("DebugF", testify_.Anything, testify_.Anything).Return(nil)
env.On("TemplateCache").Return(&cache.Template{
Env: map[string]string{
"TERM_PROGRAM": tc.Term,
},
})
env.On("Flags").Return(&runtime.Flags{})
env.On("TemplateCache").Return(&cache.Template{})
env.On("Getenv", "TERM_PROGRAM").Return(tc.Term)
env.On("Shell").Return("foo")
template.Init(env)
ansi := Ansi("{{ if eq \"vscode\" .Env.TERM_PROGRAM }}#123456{{end}}")
got := ansi.ResolveTemplate(env)
got := ansi.ResolveTemplate()
assert.Equal(t, tc.Expected, got, tc.Case)
}

View file

@ -64,7 +64,6 @@ func (cfg *Config) getPalette() color.Palette {
tmpl := &template.Text{
Template: cfg.Palettes.Template,
Env: cfg.env,
}
key, err := tmpl.Render()

View file

@ -5,8 +5,8 @@ import (
"github.com/jandedobbeleer/oh-my-posh/src/cache"
"github.com/jandedobbeleer/oh-my-posh/src/color"
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
"github.com/jandedobbeleer/oh-my-posh/src/runtime/mock"
"github.com/jandedobbeleer/oh-my-posh/src/template"
"github.com/stretchr/testify/assert"
testify_ "github.com/stretchr/testify/mock"
@ -93,11 +93,12 @@ func TestGetPalette(t *testing.T) {
for _, tc := range cases {
env := &mock.Environment{}
env.On("TemplateCache").Return(&cache.Template{
Env: map[string]string{},
Shell: "bash",
})
env.On("DebugF", testify_.Anything, testify_.Anything).Return(nil)
env.On("Flags").Return(&runtime.Flags{})
env.On("Shell").Return("bash")
template.Init(env)
cfg := &Config{
env: env,

View file

@ -20,17 +20,19 @@ import (
// SegmentStyle the style of segment, for more information, see the constants
type SegmentStyle string
func (s *SegmentStyle) resolve(env runtime.Environment, context any) SegmentStyle {
func (s *SegmentStyle) resolve(context any) SegmentStyle {
txtTemplate := &template.Text{
Context: context,
Env: env,
}
txtTemplate.Template = string(*s)
value, err := txtTemplate.Render()
// default to Plain
if err != nil || len(value) == 0 {
return Plain
}
return SegmentStyle(value)
}
@ -218,7 +220,7 @@ func (segment *Segment) SetText() {
func (segment *Segment) string() string {
if !segment.Templates.Empty() {
templatesResult := segment.Templates.Resolve(segment.writer, segment.env, "", segment.TemplatesLogic)
templatesResult := segment.Templates.Resolve(segment.writer, "", segment.TemplatesLogic)
if len(segment.Template) == 0 {
return templatesResult
}
@ -231,7 +233,6 @@ func (segment *Segment) string() string {
tmpl := &template.Text{
Template: segment.Template,
Context: segment.writer,
Env: segment.env,
}
text, err := tmpl.Render()
@ -282,7 +283,7 @@ func (segment *Segment) cwdExcluded() bool {
func (segment *Segment) ResolveForeground() color.Ansi {
if len(segment.ForegroundTemplates) != 0 {
match := segment.ForegroundTemplates.FirstMatch(segment.writer, segment.env, segment.Foreground.String())
match := segment.ForegroundTemplates.FirstMatch(segment.writer, segment.Foreground.String())
segment.Foreground = color.Ansi(match)
}
@ -291,7 +292,7 @@ func (segment *Segment) ResolveForeground() color.Ansi {
func (segment *Segment) ResolveBackground() color.Ansi {
if len(segment.BackgroundTemplates) != 0 {
match := segment.BackgroundTemplates.FirstMatch(segment.writer, segment.env, segment.Background.String())
match := segment.BackgroundTemplates.FirstMatch(segment.writer, segment.Background.String())
segment.Background = color.Ansi(match)
}
@ -303,7 +304,7 @@ func (segment *Segment) ResolveStyle() SegmentStyle {
return segment.styleCache
}
segment.styleCache = segment.Style.resolve(segment.env, segment.writer)
segment.styleCache = segment.Style.resolve(segment.writer)
return segment.styleCache
}

View file

@ -4,7 +4,6 @@ import (
"encoding/json"
"testing"
"github.com/jandedobbeleer/oh-my-posh/src/cache"
"github.com/jandedobbeleer/oh-my-posh/src/color"
"github.com/jandedobbeleer/oh-my-posh/src/properties"
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
@ -12,7 +11,6 @@ import (
"github.com/jandedobbeleer/oh-my-posh/src/segments"
"github.com/stretchr/testify/assert"
testify_ "github.com/stretchr/testify/mock"
)
const (
@ -144,19 +142,11 @@ func TestGetColors(t *testing.T) {
},
}
for _, tc := range cases {
env := new(mock.Environment)
env.On("DebugF", testify_.Anything, testify_.Anything).Return(nil)
env.On("TemplateCache").Return(&cache.Template{
Env: make(map[string]string),
})
env.On("Flags").Return(&runtime.Flags{})
segment := &Segment{
writer: &segments.Aws{
Profile: tc.Profile,
Region: tc.Region,
},
env: env,
}
if tc.Background {

View file

@ -86,7 +86,6 @@ func (e *Engine) pwd() {
// Allow template logic to define when to enable the PWD (when supported)
tmpl := &template.Text{
Template: e.Config.PWD,
Env: e.Env,
}
pwdType, err := tmpl.Render()
@ -159,7 +158,6 @@ func (e *Engine) shouldFill(filler string, padLength int) (string, bool) {
func (e *Engine) getTitleTemplateText() string {
tmpl := &template.Text{
Template: e.Config.ConsoleTitleTemplate,
Env: e.Env,
}
if text, err := tmpl.Render(); err == nil {
return text
@ -520,11 +518,13 @@ func New(flags *runtime.Flags) *Engine {
env.Init()
cfg := config.Load(env)
template.Init(env)
env.Var = cfg.Var
flags.HasTransient = cfg.TransientPrompt != nil
terminal.Init(env.Shell())
terminal.BackgroundColor = cfg.TerminalBackground.ResolveTemplate(env)
terminal.BackgroundColor = cfg.TerminalBackground.ResolveTemplate()
terminal.Colors = cfg.MakeColors()
terminal.Plain = flags.Plain

View file

@ -9,6 +9,7 @@ import (
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
"github.com/jandedobbeleer/oh-my-posh/src/runtime/mock"
"github.com/jandedobbeleer/oh-my-posh/src/shell"
"github.com/jandedobbeleer/oh-my-posh/src/template"
"github.com/jandedobbeleer/oh-my-posh/src/terminal"
"github.com/stretchr/testify/assert"
@ -92,12 +93,11 @@ func TestPrintPWD(t *testing.T) {
env.On("Host").Return("host", nil)
env.On("DebugF", testify_.Anything, testify_.Anything).Return(nil)
env.On("TemplateCache").Return(&cache.Template{
Env: make(map[string]string),
Shell: "shell",
})
env.On("Flags").Return(&runtime.Flags{})
terminal.Init(shell.GENERIC)
template.Init(env)
engine := &Engine{
Env: env,
@ -127,7 +127,7 @@ func engineRender() {
cfg := config.Load(env)
terminal.Init(shell.GENERIC)
terminal.BackgroundColor = cfg.TerminalBackground.ResolveTemplate(env)
terminal.BackgroundColor = cfg.TerminalBackground.ResolveTemplate()
terminal.Colors = cfg.MakeColors()
engine := &Engine{
@ -185,11 +185,7 @@ func TestGetTitle(t *testing.T) {
env.On("Home").Return("/usr/home")
env.On("PathSeparator").Return(tc.PathSeparator)
env.On("DebugF", testify_.Anything, testify_.Anything).Return(nil)
env.On("Flags").Return(&runtime.Flags{})
env.On("TemplateCache").Return(&cache.Template{
Env: map[string]string{
"USERDOMAIN": "MyCompany",
},
Shell: tc.ShellName,
UserName: "MyUser",
Root: tc.Root,
@ -197,8 +193,11 @@ func TestGetTitle(t *testing.T) {
PWD: tc.Cwd,
Folder: "vagrant",
})
env.On("Getenv", "USERDOMAIN").Return("MyCompany")
env.On("Shell").Return(tc.ShellName)
terminal.Init(shell.GENERIC)
template.Init(env)
engine := &Engine{
Config: &config.Config{
@ -249,18 +248,17 @@ func TestGetConsoleTitleIfGethostnameReturnsError(t *testing.T) {
env.On("Pwd").Return(tc.Cwd)
env.On("Home").Return("/usr/home")
env.On("DebugF", testify_.Anything, testify_.Anything).Return(nil)
env.On("Flags").Return(&runtime.Flags{})
env.On("TemplateCache").Return(&cache.Template{
Env: map[string]string{
"USERDOMAIN": "MyCompany",
},
Shell: tc.ShellName,
UserName: "MyUser",
Root: tc.Root,
HostName: "",
})
env.On("Getenv", "USERDOMAIN").Return("MyCompany")
env.On("Shell").Return(tc.ShellName)
terminal.Init(shell.GENERIC)
template.Init(env)
engine := &Engine{
Config: &config.Config{

View file

@ -60,7 +60,6 @@ func (e *Engine) ExtraPrompt(promptType ExtraPromptType) string {
tmpl := &template.Text{
Template: getTemplate(prompt.Template),
Env: e.Env,
}
promptText, err := tmpl.Render()
@ -78,8 +77,8 @@ func (e *Engine) ExtraPrompt(promptType ExtraPromptType) string {
e.write(terminal.PromptStart())
}
foreground := color.Ansi(prompt.ForegroundTemplates.FirstMatch(nil, e.Env, string(prompt.Foreground)))
background := color.Ansi(prompt.BackgroundTemplates.FirstMatch(nil, e.Env, string(prompt.Background)))
foreground := color.Ansi(prompt.ForegroundTemplates.FirstMatch(nil, string(prompt.Foreground)))
background := color.Ansi(prompt.BackgroundTemplates.FirstMatch(nil, string(prompt.Background)))
terminal.SetColors(background, foreground)
terminal.Write(background, foreground, promptText)

View file

@ -641,8 +641,6 @@ func (term *Terminal) Logs() string {
func (term *Terminal) TemplateCache() *cache.Template {
defer term.Trace(time.Now())
tmplCache := term.tmplCache
tmplCache.Lock()
defer tmplCache.Unlock()
if tmplCache.Initialized {
return tmplCache
@ -655,7 +653,6 @@ func (term *Terminal) TemplateCache() *cache.Template {
tmplCache.WSL = term.IsWsl()
tmplCache.Segments = maps.NewConcurrent()
tmplCache.PromptCount = term.CmdFlags.PromptCount
tmplCache.Env = make(map[string]string)
tmplCache.Var = make(map[string]any)
tmplCache.Jobs = term.CmdFlags.JobCount
@ -663,17 +660,6 @@ func (term *Terminal) TemplateCache() *cache.Template {
tmplCache.Var = term.Var
}
const separator = "="
values := os.Environ()
term.DebugF("environment: %v", values)
for value := range values {
key, val, valid := strings.Cut(values[value], separator)
if !valid {
continue
}
tmplCache.Env[key] = val
}
pwd := term.Pwd()
tmplCache.PWD = ReplaceHomeDirPrefixWithTilde(term, pwd)

View file

@ -261,14 +261,16 @@ func (l *language) buildVersionURL() {
if len(versionURLTemplate) == 0 {
return
}
tmpl := &template.Text{
Template: versionURLTemplate,
Context: l.version,
Env: l.env,
}
url, err := tmpl.Render()
if err != nil {
return
}
l.version.URL = url
}

View file

@ -3,13 +3,11 @@ package segments
import (
"testing"
"github.com/jandedobbeleer/oh-my-posh/src/cache"
"github.com/jandedobbeleer/oh-my-posh/src/properties"
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
"github.com/jandedobbeleer/oh-my-posh/src/runtime/mock"
"github.com/stretchr/testify/assert"
testify_ "github.com/stretchr/testify/mock"
)
const (
@ -60,12 +58,6 @@ func bootStrapLanguageTest(args *languageArgs) *language {
env.On("Pwd").Return(cwd)
env.On("Home").Return(home)
env.On("DebugF", testify_.Anything, testify_.Anything).Return(nil)
env.On("Flags").Return(&runtime.Flags{})
env.On("TemplateCache").Return(&cache.Template{
Env: make(map[string]string),
})
if args.properties == nil {
args.properties = properties.Map{}
@ -549,11 +541,6 @@ func getMockedLanguageEnv(params *mockedLanguageParams) (*mock.Environment, prop
env.On("HasFiles", params.extension).Return(true)
env.On("Pwd").Return("/usr/home/project")
env.On("Home").Return("/usr/home")
env.On("TemplateCache").Return(&cache.Template{
Env: make(map[string]string),
})
env.On("DebugF", testify_.Anything, testify_.Anything).Return(nil)
env.On("Flags").Return(&runtime.Flags{})
props := properties.Map{
properties.FetchVersion: true,

View file

@ -4,7 +4,6 @@ import (
"path/filepath"
"testing"
"github.com/jandedobbeleer/oh-my-posh/src/cache"
"github.com/jandedobbeleer/oh-my-posh/src/runtime/mock"
"github.com/stretchr/testify/assert"
@ -26,19 +25,18 @@ func TestGetNodePackageVersion(t *testing.T) {
for _, tc := range cases {
var env = new(mock.Environment)
// mock getVersion methods
env.On("Pwd").Return("posh")
path := filepath.Join("posh", "node_modules", "nx")
env.On("HasFilesInDir", path, "package.json").Return(!tc.NoFiles)
env.On("FileContent", filepath.Join(path, "package.json")).Return(tc.PackageJSON)
env.On("TemplateCache").Return(&cache.Template{
Env: make(map[string]string),
})
got, err := getNodePackageVersion(env, "nx")
if tc.ShouldFail {
assert.Error(t, err, tc.Case)
return
}
assert.Nil(t, err, tc.Case)
assert.Equal(t, tc.Version, got, tc.Case)
}

View file

@ -90,7 +90,6 @@ func TestOSInfo(t *testing.T) {
env.On("GOOS").Return(tc.GOOS)
env.On("Platform").Return(tc.Platform)
env.On("TemplateCache").Return(&cache.Template{
Env: make(map[string]string),
WSL: tc.IsWSL,
})

View file

@ -247,7 +247,6 @@ func (pt *Path) getMaxWidth() int {
tmpl := &template.Text{
Template: width,
Context: pt,
Env: pt.env,
}
text, err := tmpl.Render()
@ -279,7 +278,6 @@ func (pt *Path) getFolderSeparator() string {
tmpl := &template.Text{
Template: separatorTemplate,
Context: pt,
Env: pt.env,
}
text, err := tmpl.Render()
@ -573,7 +571,6 @@ func (pt *Path) setMappedLocations() {
tmpl := &template.Text{
Template: key,
Context: pt,
Env: pt.env,
}
path, err := tmpl.Render()

View file

@ -33,25 +33,28 @@ func renderTemplateNoTrimSpace(env *mock.Environment, segmentTemplate string, co
break
}
}
if !found {
env.On("TemplateCache").Return(&cache.Template{
Env: make(map[string]string),
})
env.On("TemplateCache").Return(&cache.Template{})
}
env.On("Error", testify_.Anything)
env.On("Debug", testify_.Anything)
env.On("DebugF", testify_.Anything, testify_.Anything).Return(nil)
env.On("Flags").Return(&runtime.Flags{})
env.On("Shell").Return("foo")
template.Init(env)
tmpl := &template.Text{
Template: segmentTemplate,
Context: context,
Env: env,
}
text, err := tmpl.Render()
if err != nil {
return err.Error()
}
return text
}
@ -157,7 +160,9 @@ func TestParent(t *testing.T) {
FolderSeparatorIcon: tc.FolderSeparatorIcon,
},
}
path.setPaths()
got := path.Parent()
assert.EqualValues(t, tc.Expected, got, tc.Case)
}
@ -947,7 +952,7 @@ func TestFullPathCustomMappedLocations(t *testing.T) {
PathSeparator string
Expected string
}{
{Pwd: abcd, MappedLocations: map[string]string{"{{ .Env.HOME }}/d": "#"}, Expected: "#"},
{Pwd: homeDir + "/d", MappedLocations: map[string]string{"{{ .Env.HOME }}/d": "#"}, Expected: "#"},
{Pwd: abcd, MappedLocations: map[string]string{abcd: "#"}, Expected: "#"},
{Pwd: "\\a\\b\\c\\d", MappedLocations: map[string]string{"\\a\\b": "#"}, GOOS: runtime.WINDOWS, PathSeparator: "\\", Expected: "#\\c\\d"},
{Pwd: abcd, MappedLocations: map[string]string{"/a/b": "#"}, Expected: "#/c/d"},
@ -980,11 +985,10 @@ func TestFullPathCustomMappedLocations(t *testing.T) {
env.On("Flags").Return(args)
env.On("Shell").Return(shell.GENERIC)
env.On("DebugF", testify_.Anything, testify_.Anything).Return(nil)
env.On("TemplateCache").Return(&cache.Template{
Env: map[string]string{
"HOME": "/a/b/c",
},
})
env.On("TemplateCache").Return(&cache.Template{})
env.On("Getenv", "HOME").Return(homeDir)
template.Init(env)
path := &Path{
env: env,
@ -997,6 +1001,7 @@ func TestFullPathCustomMappedLocations(t *testing.T) {
path.setPaths()
path.setStyle()
got := renderTemplateNoTrimSpace(env, "{{ .Path }}", path)
assert.Equal(t, tc.Expected, got)
}
@ -1015,6 +1020,9 @@ func TestFolderPathCustomMappedLocations(t *testing.T) {
env.On("Flags").Return(args)
env.On("Shell").Return(shell.GENERIC)
env.On("DebugF", testify_.Anything, testify_.Anything).Return(nil)
template.Init(env)
path := &Path{
env: env,
props: properties.Map{
@ -1024,8 +1032,10 @@ func TestFolderPathCustomMappedLocations(t *testing.T) {
},
},
}
path.setPaths()
path.setStyle()
got := renderTemplateNoTrimSpace(env, "{{ .Path }}", path)
assert.Equal(t, "#", got)
}
@ -1404,6 +1414,9 @@ func TestGetPwd(t *testing.T) {
env.On("Flags").Return(args)
env.On("Shell").Return(shell.PWSH)
env.On("DebugF", testify_.Anything, testify_.Anything).Return(nil)
template.Init(env)
path := &Path{
env: env,
props: properties.Map{
@ -1413,6 +1426,7 @@ func TestGetPwd(t *testing.T) {
},
},
}
path.setPaths()
assert.Equal(t, tc.Expected, path.pwd)
}
@ -1437,7 +1451,9 @@ func TestGetFolderSeparator(t *testing.T) {
env.On("Error", testify_.Anything)
env.On("Debug", testify_.Anything)
env.On("DebugF", testify_.Anything, testify_.Anything).Return(nil)
env.On("Flags").Return(&runtime.Flags{})
env.On("Shell").Return(shell.GENERIC)
template.Init(env)
path := &Path{
env: env,
@ -1455,7 +1471,6 @@ func TestGetFolderSeparator(t *testing.T) {
}
env.On("TemplateCache").Return(&cache.Template{
Env: make(map[string]string),
Shell: "bash",
})
@ -1626,7 +1641,8 @@ func TestReplaceMappedLocations(t *testing.T) {
env.On("GOOS").Return(runtime.DARWIN)
env.On("Home").Return("/a/b/k")
env.On("DebugF", testify_.Anything, testify_.Anything).Return(nil)
env.On("Flags").Return(&runtime.Flags{})
template.Init(env)
path := &Path{
env: env,
@ -1750,13 +1766,11 @@ func TestGetMaxWidth(t *testing.T) {
env := new(mock.Environment)
env.On("DebugF", testify_.Anything, testify_.Anything).Return(nil)
env.On("Error", testify_.Anything).Return(nil)
env.On("Flags").Return(&runtime.Flags{})
env.On("TemplateCache").Return(&cache.Template{
Env: map[string]string{
"MAX_WIDTH": "120",
},
Shell: "bash",
})
env.On("TemplateCache").Return(&cache.Template{})
env.On("Getenv", "MAX_WIDTH").Return("120")
env.On("Shell").Return(shell.BASH)
template.Init(env)
path := &Path{
env: env,

View file

@ -117,20 +117,19 @@ func TestSessionSegmentTemplate(t *testing.T) {
env.On("User").Return(tc.UserName)
env.On("GOOS").Return("burp")
env.On("Host").Return(tc.ComputerName, nil)
var SSHSession string
if tc.SSHSession {
SSHSession = "zezzion"
}
env.On("Getenv", "SSH_CONNECTION").Return(SSHSession)
env.On("Getenv", "SSH_CLIENT").Return(SSHSession)
env.On("Getenv", "POSH_SESSION_DEFAULT_USER").Return(tc.DefaultUserName)
env.On("TemplateCache").Return(&cache.Template{
UserName: tc.UserName,
HostName: tc.ComputerName,
Env: map[string]string{
"SSH_CONNECTION": SSHSession,
"SSH_CLIENT": SSHSession,
"POSH_SESSION_DEFAULT_USER": tc.DefaultUserName,
},
Root: tc.Root,
})
@ -147,6 +146,7 @@ func TestSessionSegmentTemplate(t *testing.T) {
env: env,
props: properties.Map{},
}
_ = session.Enabled()
assert.Equal(t, tc.ExpectedString, renderTemplate(env, tc.Template, session), tc.Case)
}

View file

@ -49,7 +49,6 @@ func (s *Status) Init(props properties.Properties, env runtime.Environment) {
statusTemplate := s.props.GetString(StatusTemplate, "{{ .Code }}")
s.template = &template.Text{
Template: statusTemplate,
Env: s.env,
}
}

View file

@ -93,21 +93,13 @@ func TestFormatStatus(t *testing.T) {
}
for _, tc := range cases {
env := new(mock.Environment)
env.On("TemplateCache").Return(&cache.Template{
Code: 133,
})
env.On("Error", testify_.Anything).Return(nil)
env.On("DebugF", testify_.Anything, testify_.Anything).Return(nil)
env.On("Flags").Return(&runtime.Flags{})
props := properties.Map{
StatusTemplate: tc.Template,
StatusSeparator: tc.Separator,
}
s := &Status{}
s.Init(props, env)
s.Init(props, new(mock.Environment))
assert.Equal(t, tc.Expected, s.formatStatus(tc.Status, tc.PipeStatus), tc.Case)
}

View file

@ -30,15 +30,14 @@ func TestTextSegment(t *testing.T) {
env.On("PathSeparator").Return("/")
env.On("TemplateCache").Return(&cache.Template{
UserName: "Posh",
Env: map[string]string{
"HELLO": "hello",
"WORLD": "",
},
HostName: "MyHost",
Shell: "terminal",
Root: true,
Folder: "posh",
})
env.On("Getenv", "HELLO").Return("hello")
env.On("Getenv", "WORLD").Return("")
txt := &Text{
env: env,
}

View file

@ -61,7 +61,6 @@ func (w *Wakatime) getURL() (string, error) {
tmpl := &template.Text{
Template: url,
Context: w,
Env: w.env,
}
return tmpl.Render()
}

View file

@ -5,13 +5,10 @@ import (
"fmt"
"testing"
"github.com/jandedobbeleer/oh-my-posh/src/cache"
"github.com/jandedobbeleer/oh-my-posh/src/properties"
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
"github.com/jandedobbeleer/oh-my-posh/src/runtime/mock"
"github.com/stretchr/testify/assert"
testify_ "github.com/stretchr/testify/mock"
)
func TestWTTrackedTime(t *testing.T) {
@ -68,13 +65,6 @@ func TestWTTrackedTime(t *testing.T) {
env.On("HTTPRequest", FAKEAPIURL).Return([]byte(response), tc.Error)
env.On("DebugF", testify_.Anything, testify_.Anything).Return(nil)
env.On("Flags").Return(&runtime.Flags{})
env.On("TemplateCache").Return(&cache.Template{
Env: map[string]string{"HELLO": "hello"},
})
w := &Wakatime{
props: properties.Map{
URL: FAKEAPIURL,
@ -86,54 +76,3 @@ func TestWTTrackedTime(t *testing.T) {
assert.Equal(t, tc.Expected, renderTemplate(env, w.Template(), w), tc.Case+" - String")
}
}
func TestWTGetUrl(t *testing.T) {
cases := []struct {
Case string
Expected string
URL string
ShouldError bool
}{
{
Case: "no template",
Expected: "test",
URL: "test",
},
{
Case: "template",
URL: "{{ .Env.HELLO }} world",
Expected: "hello world",
},
{
Case: "error",
URL: "{{ .BURR }}",
ShouldError: true,
},
}
for _, tc := range cases {
env := &mock.Environment{}
env.On("Error", testify_.Anything)
env.On("DebugF", testify_.Anything, testify_.Anything).Return(nil)
env.On("TemplateCache").Return(&cache.Template{
Env: map[string]string{"HELLO": "hello"},
})
env.On("Flags").Return(&runtime.Flags{})
w := &Wakatime{
props: properties.Map{
URL: tc.URL,
},
env: env,
}
got, err := w.getURL()
if tc.ShouldError {
assert.Error(t, err, tc.Case)
continue
}
assert.Equal(t, tc.Expected, got, tc.Case)
}
}

View file

@ -4,7 +4,6 @@ import (
"testing"
"github.com/jandedobbeleer/oh-my-posh/src/cache"
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
"github.com/jandedobbeleer/oh-my-posh/src/runtime/mock"
"github.com/stretchr/testify/assert"
@ -25,16 +24,15 @@ func TestGlob(t *testing.T) {
env := &mock.Environment{}
env.On("DebugF", testify_.Anything, testify_.Anything).Return(nil)
env.On("TemplateCache").Return(&cache.Template{
Env: make(map[string]string),
})
env.On("Flags").Return(&runtime.Flags{})
env.On("TemplateCache").Return(&cache.Template{})
env.On("Shell").Return("foo")
Init(env)
for _, tc := range cases {
tmpl := &Text{
Template: tc.Template,
Context: nil,
Env: env,
}
text, err := tmpl.Render()

85
src/template/init.go Normal file
View file

@ -0,0 +1,85 @@
package template
import (
"bytes"
"sync"
"text/template"
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
)
const (
// Errors to show when the template handling fails
InvalidTemplate = "invalid template text"
IncorrectTemplate = "unable to create text based on template"
globalRef = ".$"
elvish = "elvish"
xonsh = "xonsh"
)
var (
shell string
tmplFunc *template.Template
contextPool sync.Pool
buffPool sync.Pool
env runtime.Environment
knownVariables []string
)
type buff bytes.Buffer
func (b *buff) release() {
(*bytes.Buffer)(b).Reset()
buffPool.Put(b)
}
func (b *buff) Write(p []byte) (n int, err error) {
return (*bytes.Buffer)(b).Write(p)
}
func (b *buff) String() string {
return (*bytes.Buffer)(b).String()
}
func Init(environment runtime.Environment) {
env = environment
shell = env.Shell()
tmplFunc = template.New("cache").Funcs(funcMap())
contextPool = sync.Pool{
New: func() any {
return &context{}
},
}
buffPool = sync.Pool{
New: func() any {
return &buff{}
},
}
knownVariables = []string{
"Root",
"PWD",
"AbsolutePWD",
"Folder",
"Shell",
"ShellVersion",
"UserName",
"HostName",
"Code",
"Env",
"OS",
"WSL",
"PromptCount",
"Segments",
"SHLVL",
"Templates",
"Var",
"Data",
"Jobs",
}
}

View file

@ -4,7 +4,6 @@ import (
"testing"
"github.com/jandedobbeleer/oh-my-posh/src/cache"
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
"github.com/jandedobbeleer/oh-my-posh/src/runtime/mock"
"github.com/stretchr/testify/assert"
@ -23,25 +22,26 @@ func TestUrl(t *testing.T) {
}
env := &mock.Environment{}
env.On("TemplateCache").Return(&cache.Template{
Env: make(map[string]string),
})
env.On("TemplateCache").Return(&cache.Template{})
env.On("Error", testify_.Anything)
env.On("Debug", testify_.Anything)
env.On("DebugF", testify_.Anything, testify_.Anything).Return(nil)
env.On("Flags").Return(&runtime.Flags{})
env.On("Shell").Return("foo")
Init(env)
for _, tc := range cases {
tmpl := &Text{
Template: tc.Template,
Context: nil,
Env: env,
}
text, err := tmpl.Render()
if tc.ShouldError {
assert.Error(t, err)
continue
}
assert.Equal(t, tc.Expected, text, tc.Case)
}
}
@ -55,18 +55,10 @@ func TestPath(t *testing.T) {
{Case: "valid path", Expected: "<LINK>file:/test/test<TEXT>link</TEXT></LINK>", Template: `{{ path "link" "/test/test" }}`},
}
env := &mock.Environment{}
env.On("DebugF", testify_.Anything, testify_.Anything).Return(nil)
env.On("TemplateCache").Return(&cache.Template{
Env: make(map[string]string),
})
env.On("Flags").Return(&runtime.Flags{})
for _, tc := range cases {
tmpl := &Text{
Template: tc.Template,
Context: nil,
Env: env,
}
text, _ := tmpl.Render()

View file

@ -2,8 +2,6 @@ package template
import (
"strings"
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
)
type Logic string
@ -19,25 +17,24 @@ func (l List) Empty() bool {
return len(l) == 0
}
func (l List) Resolve(context any, env runtime.Environment, defaultValue string, logic Logic) string {
func (l List) Resolve(context any, defaultValue string, logic Logic) string {
switch logic {
case FirstMatch:
return l.FirstMatch(context, env, defaultValue)
return l.FirstMatch(context, defaultValue)
case Join:
fallthrough
default:
return l.Join(context, env)
return l.Join(context)
}
}
func (l List) Join(context any, env runtime.Environment) string {
func (l List) Join(context any) string {
if len(l) == 0 {
return ""
}
txtTemplate := &Text{
Context: context,
Env: env,
}
var buffer strings.Builder
@ -54,14 +51,13 @@ func (l List) Join(context any, env runtime.Environment) string {
return buffer.String()
}
func (l List) FirstMatch(context any, env runtime.Environment, defaultValue string) string {
func (l List) FirstMatch(context any, defaultValue string) string {
if len(l) == 0 {
return defaultValue
}
txtTemplate := &Text{
Context: context,
Env: env,
}
for _, tmpl := range l {

View file

@ -3,12 +3,7 @@ package template
import (
"testing"
"github.com/jandedobbeleer/oh-my-posh/src/cache"
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
"github.com/jandedobbeleer/oh-my-posh/src/runtime/mock"
"github.com/stretchr/testify/assert"
testify_ "github.com/stretchr/testify/mock"
)
func TestHResult(t *testing.T) {
@ -22,20 +17,10 @@ func TestHResult(t *testing.T) {
{Case: "Not a number", Template: `{{ hresult "no number" }}`, ShouldError: true},
}
env := &mock.Environment{}
env.On("TemplateCache").Return(&cache.Template{
Env: make(map[string]string),
})
env.On("Error", testify_.Anything)
env.On("Debug", testify_.Anything)
env.On("DebugF", testify_.Anything, testify_.Anything).Return(nil)
env.On("Flags").Return(&runtime.Flags{})
for _, tc := range cases {
tmpl := &Text{
Template: tc.Template,
Context: nil,
Env: env,
}
text, err := tmpl.Render()

View file

@ -3,12 +3,7 @@ package template
import (
"testing"
"github.com/jandedobbeleer/oh-my-posh/src/cache"
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
"github.com/jandedobbeleer/oh-my-posh/src/runtime/mock"
"github.com/stretchr/testify/assert"
testify_ "github.com/stretchr/testify/mock"
)
func TestRoundSeconds(t *testing.T) {
@ -29,20 +24,10 @@ func TestRoundSeconds(t *testing.T) {
{Case: "error", Expected: "", Template: "{{ secondsRound foo }}", ShouldError: true},
}
env := &mock.Environment{}
env.On("TemplateCache").Return(&cache.Template{
Env: make(map[string]string),
})
env.On("Error", testify_.Anything)
env.On("Debug", testify_.Anything)
env.On("DebugF", testify_.Anything, testify_.Anything).Return(nil)
env.On("Flags").Return(&runtime.Flags{})
for _, tc := range cases {
tmpl := &Text{
Template: tc.Template,
Context: nil,
Env: env,
}
text, err := tmpl.Render()

View file

@ -3,12 +3,7 @@ package template
import (
"testing"
"github.com/jandedobbeleer/oh-my-posh/src/cache"
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
"github.com/jandedobbeleer/oh-my-posh/src/runtime/mock"
"github.com/stretchr/testify/assert"
testify_ "github.com/stretchr/testify/mock"
)
func TestTrunc(t *testing.T) {
@ -26,20 +21,10 @@ func TestTrunc(t *testing.T) {
{Case: "negative", Expected: "ld", Template: `{{ trunc -2 "Hello World" }}`},
}
env := &mock.Environment{}
env.On("TemplateCache").Return(&cache.Template{
Env: make(map[string]string),
})
env.On("Error", testify_.Anything)
env.On("Debug", testify_.Anything)
env.On("DebugF", testify_.Anything, testify_.Anything).Return(nil)
env.On("Flags").Return(&runtime.Flags{})
for _, tc := range cases {
tmpl := &Text{
Template: tc.Template,
Context: nil,
Env: env,
}
text, err := tmpl.Render()

View file

@ -1,98 +1,26 @@
package template
import (
"bytes"
"errors"
"fmt"
"reflect"
"strings"
"sync"
"text/template"
"github.com/jandedobbeleer/oh-my-posh/src/cache"
"github.com/jandedobbeleer/oh-my-posh/src/regex"
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
)
const (
// Errors to show when the template handling fails
InvalidTemplate = "invalid template text"
IncorrectTemplate = "unable to create text based on template"
globalRef = ".$"
elvish = "elvish"
xonsh = "xonsh"
)
var (
knownVariables = []string{
"Root",
"PWD",
"AbsolutePWD",
"PSWD",
"Folder",
"Shell",
"ShellVersion",
"UserName",
"HostName",
"Code",
"Env",
"OS",
"WSL",
"PromptCount",
"Segments",
"SHLVL",
"Templates",
"Var",
"Data",
"Jobs",
}
shell string
tmplFunc = template.New("cache").Funcs(funcMap())
contextPool = sync.Pool{
New: func() any {
return &context{}
},
}
buffPool = sync.Pool{
New: func() any {
return &buff{}
},
}
)
type buff bytes.Buffer
func (b *buff) release() {
(*bytes.Buffer)(b).Reset()
buffPool.Put(b)
}
func (b *buff) Write(p []byte) (n int, err error) {
return (*bytes.Buffer)(b).Write(p)
}
func (b *buff) String() string {
return (*bytes.Buffer)(b).String()
}
type Text struct {
Template string
Context any
Env runtime.Environment
Template string
}
type Data any
type context struct {
*cache.Template
// Simple container to hold ANY object
Data
Getenv func(string) string
cache.Template
initialized bool
}
@ -103,9 +31,8 @@ func (c *context) init(t *Text) {
return
}
if tmplCache := t.Env.TemplateCache(); tmplCache != nil {
c.Template = tmplCache
}
c.Getenv = env.Getenv
c.Template = *env.TemplateCache()
c.initialized = true
}
@ -116,19 +43,17 @@ func (c *context) release() {
}
func (t *Text) Render() (string, error) {
t.Env.DebugF("rendering template: %s", t.Template)
shell = t.Env.Flags().Shell
env.DebugF("rendering template: %s", t.Template)
if !strings.Contains(t.Template, "{{") || !strings.Contains(t.Template, "}}") {
return t.Template, nil
}
t.cleanTemplate()
t.patchTemplate()
tmpl, err := tmplFunc.Parse(t.Template)
if err != nil {
t.Env.Error(err)
env.Error(err)
return "", errors.New(InvalidTemplate)
}
@ -141,7 +66,7 @@ func (t *Text) Render() (string, error) {
err = tmpl.Execute(buffer, context)
if err != nil {
t.Env.Error(err)
env.Error(err)
msg := regex.FindNamedRegexMatch(`at (?P<MSG><.*)$`, err.Error())
if len(msg) == 0 {
return "", errors.New(IncorrectTemplate)
@ -158,7 +83,7 @@ func (t *Text) Render() (string, error) {
return text, nil
}
func (t *Text) cleanTemplate() {
func (t *Text) patchTemplate() {
isKnownVariable := func(variable string) bool {
variable = strings.TrimPrefix(variable, ".")
splitted := strings.Split(variable, ".")
@ -233,6 +158,11 @@ func (t *Text) cleanTemplate() {
// the list of segments so they can be accessed directly
property = strings.Replace(property, ".Segments", ".Segments.ToSimple", 1)
result += property
case strings.HasPrefix(property, ".Env."):
// we need to replace the property with the getEnv function
// so we can access the environment variables directly
property = strings.TrimPrefix(property, ".Env.")
result += fmt.Sprintf(`(call .Getenv "%s")`, property)
default:
// check if we have the same property in Data
// and replace it with the Data property so it
@ -240,6 +170,7 @@ func (t *Text) cleanTemplate() {
if fields.hasField(property) {
property = ".Data" + property
}
// remove the global reference so we can use it directly
property = strings.TrimPrefix(property, globalRef)
result += property

View file

@ -157,19 +157,10 @@ func TestRenderTemplate(t *testing.T) {
},
}
env := &mock.Environment{}
env.On("TemplateCache").Return(&cache.Template{
Env: make(map[string]string),
})
env.On("Error", testify_.Anything)
env.On("DebugF", testify_.Anything, testify_.Anything).Return(nil)
env.On("Flags").Return(&runtime.Flags{})
for _, tc := range cases {
tmpl := &Text{
Template: tc.Template,
Context: tc.Context,
Env: env,
}
text, err := tmpl.Render()
@ -247,18 +238,23 @@ func TestRenderTemplateEnvVar(t *testing.T) {
}
for _, tc := range cases {
env := &mock.Environment{}
env.On("TemplateCache").Return(&cache.Template{
Env: tc.Env,
OS: "darwin",
})
env.On("Error", testify_.Anything)
env.On("DebugF", testify_.Anything, testify_.Anything).Return(nil)
env.On("Flags").Return(&runtime.Flags{})
env.On("Shell").Return("foo")
env.On("TemplateCache").Return(&cache.Template{
OS: "darwin",
})
for k, v := range tc.Env {
env.On("Getenv", k).Return(v)
}
Init(env)
tmpl := &Text{
Template: tc.Template,
Context: tc.Context,
Env: env,
}
text, err := tmpl.Render()
@ -271,7 +267,7 @@ func TestRenderTemplateEnvVar(t *testing.T) {
}
}
func TestCleanTemplate(t *testing.T) {
func TestPatchTemplate(t *testing.T) {
cases := []struct {
Case string
Expected string
@ -294,27 +290,27 @@ func TestCleanTemplate(t *testing.T) {
},
{
Case: "Same prefix",
Expected: "{{ .Env.HELLO }} {{ .Data.World }} {{ .Data.WorldTrend }}",
Expected: "{{ (call .Getenv \"HELLO\") }} {{ .Data.World }} {{ .Data.WorldTrend }}",
Template: "{{ .Env.HELLO }} {{ .World }} {{ .WorldTrend }}",
},
{
Case: "Double use of property with different child",
Expected: "{{ .Env.HELLO }} {{ .Data.World.Trend }} {{ .Data.World.Hello }} {{ .Data.World }}",
Expected: "{{ (call .Getenv \"HELLO\") }} {{ .Data.World.Trend }} {{ .Data.World.Hello }} {{ .Data.World }}",
Template: "{{ .Env.HELLO }} {{ .World.Trend }} {{ .World.Hello }} {{ .World }}",
},
{
Case: "Hello world",
Expected: "{{.Env.HELLO}} {{.Data.World}}",
Expected: "{{(call .Getenv \"HELLO\")}} {{.Data.World}}",
Template: "{{.Env.HELLO}} {{.World}}",
},
{
Case: "Multiple vars",
Expected: "{{.Env.HELLO}} {{.Data.World}} {{.Data.World}}",
Expected: "{{(call .Getenv \"HELLO\")}} {{.Data.World}} {{.Data.World}}",
Template: "{{.Env.HELLO}} {{.World}} {{.World}}",
},
{
Case: "Multiple vars with spaces",
Expected: "{{ .Env.HELLO }} {{ .Data.World }} {{ .Data.World }}",
Expected: "{{ (call .Getenv \"HELLO\") }} {{ .Data.World }} {{ .Data.World }}",
Template: "{{ .Env.HELLO }} {{ .World }} {{ .World }}",
},
{
@ -343,12 +339,19 @@ func TestCleanTemplate(t *testing.T) {
Template: `{{.Segments.Git.Repo}}`,
},
}
env := &mock.Environment{}
env.On("Shell").Return("foo")
Init(env)
for _, tc := range cases {
tmpl := &Text{
Template: tc.Template,
Context: map[string]any{"OS": "posh"},
}
tmpl.cleanTemplate()
tmpl.patchTemplate()
assert.Equal(t, tc.Expected, tmpl.Template, tc.Case)
}
}
@ -368,16 +371,16 @@ func TestSegmentContains(t *testing.T) {
segments.Set("Git", "foo")
env.On("DebugF", testify_.Anything, testify_.Anything).Return(nil)
env.On("TemplateCache").Return(&cache.Template{
Env: make(map[string]string),
Segments: segments,
})
env.On("Flags").Return(&runtime.Flags{})
env.On("Shell").Return("foo")
Init(env)
for _, tc := range cases {
tmpl := &Text{
Template: tc.Template,
Context: nil,
Env: env,
}
text, _ := tmpl.Render()