mirror of
https://github.com/JanDeDobbeleer/oh-my-posh.git
synced 2024-12-28 04:19:41 -08:00
feat: allow segment to override global properties
This commit is contained in:
parent
b2e1b041e3
commit
287f183244
|
@ -3,6 +3,7 @@ package template
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
|
@ -14,6 +15,29 @@ const (
|
||||||
// Errors to show when the template handling fails
|
// Errors to show when the template handling fails
|
||||||
InvalidTemplate = "invalid template text"
|
InvalidTemplate = "invalid template text"
|
||||||
IncorrectTemplate = "unable to create text based on template"
|
IncorrectTemplate = "unable to create text based on template"
|
||||||
|
|
||||||
|
globalRef = ".$"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
knownVariables = []string{
|
||||||
|
"Root",
|
||||||
|
"PWD",
|
||||||
|
"Folder",
|
||||||
|
"Shell",
|
||||||
|
"ShellVersion",
|
||||||
|
"UserName",
|
||||||
|
"HostName",
|
||||||
|
"Env",
|
||||||
|
"Data",
|
||||||
|
"Code",
|
||||||
|
"OS",
|
||||||
|
"WSL",
|
||||||
|
"Segments",
|
||||||
|
"Templates",
|
||||||
|
"PromptCount",
|
||||||
|
"Var",
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
type Text struct {
|
type Text struct {
|
||||||
|
@ -73,26 +97,7 @@ func (t *Text) Render() (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Text) cleanTemplate() {
|
func (t *Text) cleanTemplate() {
|
||||||
knownVariables := []string{
|
isKnownVariable := func(variable string) bool {
|
||||||
"Root",
|
|
||||||
"PWD",
|
|
||||||
"Folder",
|
|
||||||
"Shell",
|
|
||||||
"ShellVersion",
|
|
||||||
"UserName",
|
|
||||||
"HostName",
|
|
||||||
"Env",
|
|
||||||
"Data",
|
|
||||||
"Code",
|
|
||||||
"OS",
|
|
||||||
"WSL",
|
|
||||||
"Segments",
|
|
||||||
"Templates",
|
|
||||||
"PromptCount",
|
|
||||||
"Var",
|
|
||||||
}
|
|
||||||
|
|
||||||
knownVariable := func(variable string) bool {
|
|
||||||
variable = strings.TrimPrefix(variable, ".")
|
variable = strings.TrimPrefix(variable, ".")
|
||||||
splitted := strings.Split(variable, ".")
|
splitted := strings.Split(variable, ".")
|
||||||
if len(splitted) == 0 {
|
if len(splitted) == 0 {
|
||||||
|
@ -111,6 +116,9 @@ func (t *Text) cleanTemplate() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fields := make(fields)
|
||||||
|
fields.init(t.Context)
|
||||||
|
|
||||||
var result, property string
|
var result, property string
|
||||||
var inProperty, inTemplate bool
|
var inProperty, inTemplate bool
|
||||||
for i, char := range t.Template {
|
for i, char := range t.Template {
|
||||||
|
@ -149,9 +157,17 @@ func (t *Text) cleanTemplate() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// end of a variable, needs to be appended
|
// end of a variable, needs to be appended
|
||||||
if !knownVariable(property) {
|
if !isKnownVariable(property) {
|
||||||
result += ".Data" + property
|
result += ".Data" + property
|
||||||
} else {
|
} else {
|
||||||
|
// check if we have the same property in Data
|
||||||
|
// and replace it with the Data property so it
|
||||||
|
// can take precedence
|
||||||
|
if fields.hasField(property) {
|
||||||
|
property = ".Data" + property
|
||||||
|
}
|
||||||
|
// remove the global reference so we can use it directly
|
||||||
|
property = strings.TrimPrefix(property, globalRef)
|
||||||
result += property
|
result += property
|
||||||
}
|
}
|
||||||
property = ""
|
property = ""
|
||||||
|
@ -169,3 +185,34 @@ func (t *Text) cleanTemplate() {
|
||||||
// return the result and remaining unresolved property
|
// return the result and remaining unresolved property
|
||||||
t.Template = result + property
|
t.Template = result + property
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type fields map[string]bool
|
||||||
|
|
||||||
|
func (f *fields) init(data interface{}) {
|
||||||
|
if data == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val := reflect.TypeOf(data)
|
||||||
|
switch val.Kind() { //nolint:exhaustive
|
||||||
|
case reflect.Struct:
|
||||||
|
fieldsNum := val.NumField()
|
||||||
|
for i := 0; i < fieldsNum; i++ {
|
||||||
|
(*f)[val.Field(i).Name] = true
|
||||||
|
}
|
||||||
|
case reflect.Map:
|
||||||
|
m, ok := data.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for key := range m {
|
||||||
|
(*f)[key] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f fields) hasField(field string) bool {
|
||||||
|
field = strings.TrimPrefix(field, ".")
|
||||||
|
_, ok := f[field]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
|
@ -209,11 +209,40 @@ func TestRenderTemplateEnvVar(t *testing.T) {
|
||||||
{Case: "no env var", Expected: "hello world", Template: "{{.Text}} world", Context: struct{ Text string }{Text: "hello"}},
|
{Case: "no env var", Expected: "hello world", Template: "{{.Text}} world", Context: struct{ Text string }{Text: "hello"}},
|
||||||
{Case: "map", Expected: "hello world", Template: "{{.Text}} world", Context: map[string]interface{}{"Text": "hello"}},
|
{Case: "map", Expected: "hello world", Template: "{{.Text}} world", Context: map[string]interface{}{"Text": "hello"}},
|
||||||
{Case: "empty map", Expected: " world", Template: "{{.Text}} world", Context: map[string]string{}},
|
{Case: "empty map", Expected: " world", Template: "{{.Text}} world", Context: map[string]string{}},
|
||||||
|
{
|
||||||
|
Case: "Struct with duplicate property",
|
||||||
|
Expected: "posh",
|
||||||
|
Template: "{{ .OS }}",
|
||||||
|
Context: struct{ OS string }{OS: "posh"},
|
||||||
|
Env: map[string]string{"HELLO": "hello"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Case: "Struct with duplicate property, but global override",
|
||||||
|
Expected: "darwin",
|
||||||
|
Template: "{{ .$.OS }}",
|
||||||
|
Context: struct{ OS string }{OS: "posh"},
|
||||||
|
Env: map[string]string{"HELLO": "hello"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Case: "Map with duplicate property",
|
||||||
|
Expected: "posh",
|
||||||
|
Template: "{{ .OS }}",
|
||||||
|
Context: map[string]interface{}{"OS": "posh"},
|
||||||
|
Env: map[string]string{"HELLO": "hello"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Case: "Non-supported map",
|
||||||
|
Expected: "darwin",
|
||||||
|
Template: "{{ .OS }}",
|
||||||
|
Context: map[int]interface{}{},
|
||||||
|
Env: map[string]string{"HELLO": "hello"},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tc := range cases {
|
for _, tc := range cases {
|
||||||
env := &mock.MockedEnvironment{}
|
env := &mock.MockedEnvironment{}
|
||||||
env.On("TemplateCache").Return(&platform.TemplateCache{
|
env.On("TemplateCache").Return(&platform.TemplateCache{
|
||||||
Env: tc.Env,
|
Env: tc.Env,
|
||||||
|
OS: "darwin",
|
||||||
})
|
})
|
||||||
env.On("Error", mock2.Anything)
|
env.On("Error", mock2.Anything)
|
||||||
tmpl := &Text{
|
tmpl := &Text{
|
||||||
|
|
|
@ -13,6 +13,10 @@ offers a few standard properties to work with.
|
||||||
|
|
||||||
## Global properties
|
## Global properties
|
||||||
|
|
||||||
|
These properties can be used anywhere, in any segment. If a segment contains a property with the same name,
|
||||||
|
the segment property value will be used instead. In case you want to use the global property, you can prefix
|
||||||
|
it with `.$` to reference it directly.
|
||||||
|
|
||||||
| Name | Type | Description |
|
| Name | Type | Description |
|
||||||
| --------------- | --------- | ----------------------------------------------------------------- |
|
| --------------- | --------- | ----------------------------------------------------------------- |
|
||||||
| `.Root` | `boolean` | is the current user root/admin or not |
|
| `.Root` | `boolean` | is the current user root/admin or not |
|
||||||
|
|
Loading…
Reference in a new issue