mirror of
https://github.com/JanDeDobbeleer/oh-my-posh.git
synced 2025-03-05 20:49:04 -08:00
feat(template): add global Version property
This commit is contained in:
parent
85a86be703
commit
93649abc21
7
.vscode/launch.json
vendored
7
.vscode/launch.json
vendored
|
@ -10,10 +10,13 @@
|
||||||
"args": [
|
"args": [
|
||||||
"prompt",
|
"prompt",
|
||||||
"print",
|
"print",
|
||||||
"primary",
|
"right",
|
||||||
"--shell=pwsh",
|
"--shell=pwsh",
|
||||||
"--terminal-width=200"
|
"--terminal-width=200"
|
||||||
]
|
],
|
||||||
|
"env": {
|
||||||
|
"POSH_THEME": "C:\\Users\\jande\\.posh.omp.jsonc"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Tooltip",
|
"name": "Tooltip",
|
||||||
|
|
11
src/cache/template.go
vendored
11
src/cache/template.go
vendored
|
@ -8,19 +8,20 @@ type Template struct {
|
||||||
SegmentsCache maps.Simple
|
SegmentsCache maps.Simple
|
||||||
Segments *maps.Concurrent
|
Segments *maps.Concurrent
|
||||||
Var maps.Simple
|
Var maps.Simple
|
||||||
ShellVersion string
|
PWD string
|
||||||
AbsolutePWD string
|
Folder string
|
||||||
PSWD string
|
PSWD string
|
||||||
UserName string
|
UserName string
|
||||||
HostName string
|
HostName string
|
||||||
PWD string
|
ShellVersion string
|
||||||
Shell string
|
Shell string
|
||||||
Folder string
|
AbsolutePWD string
|
||||||
OS string
|
OS string
|
||||||
Code int
|
Version string
|
||||||
PromptCount int
|
PromptCount int
|
||||||
SHLVL int
|
SHLVL int
|
||||||
Jobs int
|
Jobs int
|
||||||
|
Code int
|
||||||
WSL bool
|
WSL bool
|
||||||
Root bool
|
Root bool
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,10 +7,9 @@ import (
|
||||||
|
|
||||||
type Battery struct {
|
type Battery struct {
|
||||||
base
|
base
|
||||||
|
|
||||||
*battery.Info
|
|
||||||
Error string
|
Error string
|
||||||
Icon string
|
Icon string
|
||||||
|
battery.Info
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -34,15 +33,16 @@ func (b *Battery) Enabled() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
info, err := b.env.BatteryState()
|
||||||
b.Info, err = b.env.BatteryState()
|
|
||||||
|
|
||||||
if !b.enabledWhileError(err) {
|
if !b.enabledWhileError(err) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
b.Info = *info
|
||||||
|
|
||||||
// case on computer without batteries(no error, empty array)
|
// case on computer without batteries(no error, empty array)
|
||||||
if err == nil && b.Info == nil {
|
if err == nil && b.Info.Percentage == 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/jandedobbeleer/oh-my-posh/src/build"
|
||||||
"github.com/jandedobbeleer/oh-my-posh/src/cache"
|
"github.com/jandedobbeleer/oh-my-posh/src/cache"
|
||||||
"github.com/jandedobbeleer/oh-my-posh/src/log"
|
"github.com/jandedobbeleer/oh-my-posh/src/log"
|
||||||
"github.com/jandedobbeleer/oh-my-posh/src/maps"
|
"github.com/jandedobbeleer/oh-my-posh/src/maps"
|
||||||
|
@ -36,6 +37,7 @@ func loadCache(vars maps.Simple) {
|
||||||
Cache.PromptCount = env.Flags().PromptCount
|
Cache.PromptCount = env.Flags().PromptCount
|
||||||
Cache.Var = make(map[string]any)
|
Cache.Var = make(map[string]any)
|
||||||
Cache.Jobs = env.Flags().JobCount
|
Cache.Jobs = env.Flags().JobCount
|
||||||
|
Cache.Version = build.Version
|
||||||
|
|
||||||
if vars != nil {
|
if vars != nil {
|
||||||
Cache.Var = vars
|
Cache.Var = vars
|
||||||
|
|
|
@ -19,14 +19,15 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
shell string
|
shell string
|
||||||
env runtime.Environment
|
env runtime.Environment
|
||||||
knownVariables []string
|
knownFields *maps.Concurrent
|
||||||
)
|
)
|
||||||
|
|
||||||
func Init(environment runtime.Environment, vars maps.Simple) {
|
func Init(environment runtime.Environment, vars maps.Simple) {
|
||||||
env = environment
|
env = environment
|
||||||
shell = env.Shell()
|
shell = env.Shell()
|
||||||
|
knownFields = maps.NewConcurrent()
|
||||||
|
|
||||||
renderPool = sync.Pool{
|
renderPool = sync.Pool{
|
||||||
New: func() any {
|
New: func() any {
|
||||||
|
@ -34,29 +35,6 @@ func Init(environment runtime.Environment, vars maps.Simple) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
knownVariables = []string{
|
|
||||||
"Root",
|
|
||||||
"PWD",
|
|
||||||
"AbsolutePWD",
|
|
||||||
"PSWD",
|
|
||||||
"Folder",
|
|
||||||
"Shell",
|
|
||||||
"ShellVersion",
|
|
||||||
"UserName",
|
|
||||||
"HostName",
|
|
||||||
"Code",
|
|
||||||
"Env",
|
|
||||||
"OS",
|
|
||||||
"WSL",
|
|
||||||
"PromptCount",
|
|
||||||
"Segments",
|
|
||||||
"SHLVL",
|
|
||||||
"Templates",
|
|
||||||
"Var",
|
|
||||||
"Data",
|
|
||||||
"Jobs",
|
|
||||||
}
|
|
||||||
|
|
||||||
if Cache != nil {
|
if Cache != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/jandedobbeleer/oh-my-posh/src/log"
|
"github.com/jandedobbeleer/oh-my-posh/src/log"
|
||||||
"github.com/jandedobbeleer/oh-my-posh/src/regex"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Text struct {
|
type Text struct {
|
||||||
|
@ -31,29 +30,6 @@ func (t *Text) Render() (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Text) patchTemplate() {
|
func (t *Text) patchTemplate() {
|
||||||
isKnownVariable := func(variable string) bool {
|
|
||||||
variable = strings.TrimPrefix(variable, ".")
|
|
||||||
splitted := strings.Split(variable, ".")
|
|
||||||
|
|
||||||
if len(splitted) == 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
variable = splitted[0]
|
|
||||||
// check if alphanumeric
|
|
||||||
if !regex.MatchString(`^[a-zA-Z0-9]+$`, variable) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, b := range knownVariables {
|
|
||||||
if variable == b {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
fields := make(fields)
|
fields := make(fields)
|
||||||
fields.init(t.Context)
|
fields.init(t.Context)
|
||||||
|
|
||||||
|
@ -70,10 +46,12 @@ func (t *Text) patchTemplate() {
|
||||||
inTemplate = false
|
inTemplate = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !inTemplate {
|
if !inTemplate {
|
||||||
result += string(char)
|
result += string(char)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
switch char {
|
switch char {
|
||||||
case '.':
|
case '.':
|
||||||
var lastChar rune
|
var lastChar rune
|
||||||
|
@ -96,9 +74,6 @@ func (t *Text) patchTemplate() {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case !isKnownVariable(property):
|
|
||||||
// end of a variable, needs to be appended
|
|
||||||
result += ".Data" + property
|
|
||||||
case strings.HasPrefix(property, ".Segments") && !strings.HasSuffix(property, ".Contains"):
|
case strings.HasPrefix(property, ".Segments") && !strings.HasSuffix(property, ".Contains"):
|
||||||
// as we can't provide a clean way to access the list
|
// as we can't provide a clean way to access the list
|
||||||
// of segments, we need to replace the property with
|
// of segments, we need to replace the property with
|
||||||
|
@ -149,10 +124,48 @@ func (f *fields) init(data any) {
|
||||||
val := reflect.TypeOf(data)
|
val := reflect.TypeOf(data)
|
||||||
switch val.Kind() { //nolint:exhaustive
|
switch val.Kind() { //nolint:exhaustive
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
|
name := val.Name()
|
||||||
|
|
||||||
|
// ignore the base struct
|
||||||
|
if name == "base" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if we already know the fields of this struct
|
||||||
|
if kf, OK := knownFields.Get(name); OK {
|
||||||
|
for key := range kf.(fields) {
|
||||||
|
(*f)[key] = true
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get struct fields and check embedded types
|
||||||
fieldsNum := val.NumField()
|
fieldsNum := val.NumField()
|
||||||
for i := 0; i < fieldsNum; i++ {
|
for i := 0; i < fieldsNum; i++ {
|
||||||
(*f)[val.Field(i).Name] = true
|
field := val.Field(i)
|
||||||
|
(*f)[field.Name] = true
|
||||||
|
|
||||||
|
// If this is an embedded field, get its methods too
|
||||||
|
if !field.Anonymous {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
embeddedType := field.Type
|
||||||
|
|
||||||
|
// Recursively check if the embedded type is also a struct
|
||||||
|
if embeddedType.Kind() == reflect.Struct {
|
||||||
|
f.init(reflect.New(embeddedType).Elem().Interface())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get pointer methods
|
||||||
|
ptrType := reflect.PointerTo(val)
|
||||||
|
methodsNum := ptrType.NumMethod()
|
||||||
|
for i := 0; i < methodsNum; i++ {
|
||||||
|
(*f)[ptrType.Method(i).Name] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
knownFields.Set(name, *f)
|
||||||
case reflect.Map:
|
case reflect.Map:
|
||||||
m, ok := data.(map[string]any)
|
m, ok := data.(map[string]any)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -168,6 +181,11 @@ func (f *fields) init(data any) {
|
||||||
|
|
||||||
func (f fields) hasField(field string) bool {
|
func (f fields) hasField(field string) bool {
|
||||||
field = strings.TrimPrefix(field, ".")
|
field = strings.TrimPrefix(field, ".")
|
||||||
|
|
||||||
|
// get the first part of the field
|
||||||
|
splitted := strings.Split(field, ".")
|
||||||
|
field = splitted[0]
|
||||||
|
|
||||||
_, ok := f[field]
|
_, ok := f[field]
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ func TestRenderTemplate(t *testing.T) {
|
||||||
type Me struct {
|
type Me struct {
|
||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
Context any
|
Context any
|
||||||
Case string
|
Case string
|
||||||
|
@ -161,6 +162,11 @@ func TestRenderTemplate(t *testing.T) {
|
||||||
Context: tc.Context,
|
Context: tc.Context,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
env := new(mock.Environment)
|
||||||
|
env.On("Shell").Return("foo")
|
||||||
|
Cache = new(cache.Template)
|
||||||
|
Init(env, nil)
|
||||||
|
|
||||||
text, err := tmpl.Render()
|
text, err := tmpl.Render()
|
||||||
if tc.ShouldError {
|
if tc.ShouldError {
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
|
@ -204,7 +210,7 @@ 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]any{"Text": "hello"}},
|
{Case: "map", Expected: "hello world", Template: "{{.Text}} world", Context: map[string]any{"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{}, ShouldError: true},
|
||||||
{
|
{
|
||||||
Case: "Struct with duplicate property",
|
Case: "Struct with duplicate property",
|
||||||
Expected: "posh",
|
Expected: "posh",
|
||||||
|
@ -335,15 +341,22 @@ func TestPatchTemplate(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
env := &mock.Environment{}
|
env := new(mock.Environment)
|
||||||
env.On("Shell").Return("foo")
|
env.On("Shell").Return("foo")
|
||||||
|
Cache = new(cache.Template)
|
||||||
Init(env, nil)
|
Init(env, nil)
|
||||||
|
|
||||||
for _, tc := range cases {
|
for _, tc := range cases {
|
||||||
tmpl := &Text{
|
tmpl := &Text{
|
||||||
Template: tc.Template,
|
Template: tc.Template,
|
||||||
Context: map[string]any{"OS": "posh"},
|
Context: map[string]any{
|
||||||
|
"OS": true,
|
||||||
|
"World": true,
|
||||||
|
"WorldTrend": "chaos",
|
||||||
|
"Working": true,
|
||||||
|
"Staging": true,
|
||||||
|
"CPU": true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpl.patchTemplate()
|
tmpl.patchTemplate()
|
||||||
|
@ -351,6 +364,27 @@ func TestPatchTemplate(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Foo struct{}
|
||||||
|
|
||||||
|
func (f *Foo) Hello() string {
|
||||||
|
return "hello"
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPatchTemplateStruct(t *testing.T) {
|
||||||
|
env := new(mock.Environment)
|
||||||
|
env.On("Shell").Return("foo")
|
||||||
|
Cache = new(cache.Template)
|
||||||
|
Init(env, nil)
|
||||||
|
|
||||||
|
tmpl := &Text{
|
||||||
|
Template: "{{ .Hello }}",
|
||||||
|
Context: Foo{},
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpl.patchTemplate()
|
||||||
|
assert.Equal(t, "{{ .Data.Hello }}", tmpl.Template)
|
||||||
|
}
|
||||||
|
|
||||||
func TestSegmentContains(t *testing.T) {
|
func TestSegmentContains(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
Case string
|
Case string
|
||||||
|
|
|
@ -35,6 +35,7 @@ it with `.$` to reference it directly.
|
||||||
| `.WSL` | `boolean` | in WSL yes/no |
|
| `.WSL` | `boolean` | in WSL yes/no |
|
||||||
| `.Templates` | `string` | the [templates][templates] result |
|
| `.Templates` | `string` | the [templates][templates] result |
|
||||||
| `.PromptCount` | `int` | the prompt counter, increments with 1 for every prompt invocation |
|
| `.PromptCount` | `int` | the prompt counter, increments with 1 for every prompt invocation |
|
||||||
|
| `.Version` | `string` | the Oh My Posh version |
|
||||||
|
|
||||||
## Environment variables
|
## Environment variables
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue