mirror of
https://github.com/JanDeDobbeleer/oh-my-posh.git
synced 2024-12-29 04:49:39 -08:00
parent
6980cb55e7
commit
5a57d6909d
|
@ -3,7 +3,6 @@ package template
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"oh-my-posh/environment"
|
"oh-my-posh/environment"
|
||||||
"oh-my-posh/regex"
|
"oh-my-posh/regex"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -66,21 +65,6 @@ func (t *Text) Render() (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Text) cleanTemplate() {
|
func (t *Text) cleanTemplate() {
|
||||||
unknownVariable := func(variable string, knownVariables *[]string) (string, bool) {
|
|
||||||
variable = strings.TrimPrefix(variable, ".")
|
|
||||||
splitted := strings.Split(variable, ".")
|
|
||||||
if len(splitted) == 0 {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
for _, b := range *knownVariables {
|
|
||||||
if b == splitted[0] {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*knownVariables = append(*knownVariables, splitted[0])
|
|
||||||
return splitted[0], true
|
|
||||||
}
|
|
||||||
|
|
||||||
knownVariables := []string{
|
knownVariables := []string{
|
||||||
"Root",
|
"Root",
|
||||||
"PWD",
|
"PWD",
|
||||||
|
@ -97,14 +81,72 @@ func (t *Text) cleanTemplate() {
|
||||||
"Segments",
|
"Segments",
|
||||||
"Templates",
|
"Templates",
|
||||||
}
|
}
|
||||||
matches := regex.FindAllNamedRegexMatch(`(?: |{|\()(?P<VAR>(\.[a-zA-Z_][a-zA-Z0-9]*)+)`, t.Template)
|
|
||||||
|
knownVariable := func(variable string) bool {
|
||||||
|
variable = strings.TrimPrefix(variable, ".")
|
||||||
|
splitted := strings.Split(variable, ".")
|
||||||
|
if len(splitted) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
variable = splitted[0]
|
||||||
|
for _, b := range knownVariables {
|
||||||
|
if variable == b {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
walkAndReplace := func(node string) string {
|
||||||
|
var result string
|
||||||
|
var property string
|
||||||
|
var inProperty bool
|
||||||
|
// var literal bool
|
||||||
|
for _, char := range node {
|
||||||
|
switch char {
|
||||||
|
case '.':
|
||||||
|
var lastChar rune
|
||||||
|
if len(result) > 0 {
|
||||||
|
lastChar = rune(result[len(result)-1])
|
||||||
|
}
|
||||||
|
// only replace if we're in a valid property start
|
||||||
|
// with a space, { or ( character
|
||||||
|
switch lastChar {
|
||||||
|
case ' ', '{', '(':
|
||||||
|
property += string(char)
|
||||||
|
inProperty = true
|
||||||
|
default:
|
||||||
|
result += string(char)
|
||||||
|
}
|
||||||
|
case ' ', '}', ')': // space or }
|
||||||
|
if !inProperty {
|
||||||
|
result += string(char)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// end of a variable, needs to be appended
|
||||||
|
if !knownVariable(property) {
|
||||||
|
result += ".Data" + property
|
||||||
|
} else {
|
||||||
|
result += property
|
||||||
|
}
|
||||||
|
property = ""
|
||||||
|
result += string(char)
|
||||||
|
inProperty = false
|
||||||
|
default:
|
||||||
|
if inProperty {
|
||||||
|
property += string(char)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result += string(char)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// matches := regex.FindAllNamedRegexMatch(`(?: |{|\()(?P<VAR>(\.[a-zA-Z_][a-zA-Z0-9]*)+)`, t.Template)
|
||||||
|
matches := regex.FindAllNamedRegexMatch(`(?P<NODE>{{[^{]+}})`, t.Template)
|
||||||
for _, match := range matches {
|
for _, match := range matches {
|
||||||
if variable, OK := unknownVariable(match["VAR"], &knownVariables); OK {
|
node := walkAndReplace(match["NODE"])
|
||||||
pattern := fmt.Sprintf(`\.%s\b`, variable)
|
t.Template = strings.Replace(t.Template, match["NODE"], node, 1)
|
||||||
dataVar := fmt.Sprintf(".Data.%s", variable)
|
|
||||||
t.Template = regex.ReplaceAllString(pattern, t.Template, dataVar)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// allow literal dots in template
|
|
||||||
t.Template = strings.ReplaceAll(t.Template, `\.`, ".")
|
|
||||||
}
|
|
||||||
|
|
|
@ -10,6 +10,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRenderTemplate(t *testing.T) {
|
func TestRenderTemplate(t *testing.T) {
|
||||||
|
type Me struct {
|
||||||
|
Name string
|
||||||
|
}
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
Case string
|
Case string
|
||||||
Expected string
|
Expected string
|
||||||
|
@ -17,11 +20,68 @@ func TestRenderTemplate(t *testing.T) {
|
||||||
ShouldError bool
|
ShouldError bool
|
||||||
Context interface{}
|
Context interface{}
|
||||||
}{
|
}{
|
||||||
{Case: "single property", Expected: "hello world", Template: "{{.Text}} world", Context: struct{ Text string }{Text: "hello"}},
|
{
|
||||||
{Case: "invalid property", ShouldError: true, Template: "{{.Durp}} world", Context: struct{ Text string }{Text: "hello"}},
|
Case: "Env like property name",
|
||||||
{Case: "invalid template", ShouldError: true, Template: "{{ if .Text }} world", Context: struct{ Text string }{Text: "hello"}},
|
Expected: "hello world",
|
||||||
{Case: "if statement true", Expected: "hello world", Template: "{{ if .Text }}{{.Text}} world{{end}}", Context: struct{ Text string }{Text: "hello"}},
|
Template: "{{.EnvLike}} {{.Text2}}",
|
||||||
{Case: "if statement false", Expected: "world", Template: "{{ if .Text }}{{.Text}} {{end}}world", Context: struct{ Text string }{Text: ""}},
|
Context: struct {
|
||||||
|
EnvLike string
|
||||||
|
Text2 string
|
||||||
|
}{
|
||||||
|
EnvLike: "hello",
|
||||||
|
Text2: "world",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Case: "single property with a dot literal",
|
||||||
|
Expected: "hello world",
|
||||||
|
Template: "{{ if eq .Text \".Net\" }}hello world{{ end }}",
|
||||||
|
Context: struct{ Text string }{Text: ".Net"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Case: "single property",
|
||||||
|
Expected: "hello world",
|
||||||
|
Template: "{{.Text}} world",
|
||||||
|
Context: struct{ Text string }{Text: "hello"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Case: "duplicate property",
|
||||||
|
Expected: "hello jan posh",
|
||||||
|
Template: "hello {{ .Me.Name }} {{ .Name }}",
|
||||||
|
Context: struct {
|
||||||
|
Name string
|
||||||
|
Me Me
|
||||||
|
}{
|
||||||
|
Name: "posh",
|
||||||
|
Me: Me{
|
||||||
|
Name: "jan",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Case: "invalid property",
|
||||||
|
ShouldError: true,
|
||||||
|
Template: "{{.Durp}} world",
|
||||||
|
Context: struct{ Text string }{Text: "hello"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Case: "invalid template",
|
||||||
|
ShouldError: true,
|
||||||
|
Template: "{{ if .Text }} world",
|
||||||
|
Context: struct{ Text string }{Text: "hello"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Case: "if statement true",
|
||||||
|
Expected: "hello world",
|
||||||
|
Template: "{{ if .Text }}{{.Text}} world{{end}}",
|
||||||
|
Context: struct{ Text string }{Text: "hello"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Case: "if statement false",
|
||||||
|
Expected: "world",
|
||||||
|
Template: "{{ if .Text }}{{.Text}} {{end}}world",
|
||||||
|
Context: struct{ Text string }{Text: ""},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Case: "if statement true with 2 properties",
|
Case: "if statement true with 2 properties",
|
||||||
Expected: "hello world",
|
Expected: "hello world",
|
||||||
|
@ -86,6 +146,8 @@ func TestRenderTemplate(t *testing.T) {
|
||||||
if tc.ShouldError {
|
if tc.ShouldError {
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
continue
|
continue
|
||||||
|
} else {
|
||||||
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
assert.Equal(t, tc.Expected, text, tc.Case)
|
assert.Equal(t, tc.Expected, text, tc.Case)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue