mirror of
https://github.com/JanDeDobbeleer/oh-my-posh.git
synced 2024-11-09 20:44:03 -08:00
feat: expose environment info to all templates
This commit is contained in:
parent
5426567d77
commit
f512df208a
18
.vscode/launch.json
vendored
18
.vscode/launch.json
vendored
|
@ -2,7 +2,7 @@
|
|||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch Package",
|
||||
"name": "Debug Prompt",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "debug",
|
||||
|
@ -13,7 +13,7 @@
|
|||
]
|
||||
},
|
||||
{
|
||||
"name": "Launch Tooltip",
|
||||
"name": "Debug Tooltip",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "debug",
|
||||
|
@ -24,6 +24,18 @@
|
|||
"--shell=pwsh"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Debug Transient",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "debug",
|
||||
"program": "${workspaceRoot}/src",
|
||||
"args": [
|
||||
"--config=/Users/jan/.jandedobbeleer.omp.json",
|
||||
"--shell=pwsh",
|
||||
"--print-transient"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Launch tests",
|
||||
"type": "go",
|
||||
|
@ -84,7 +96,7 @@
|
|||
"type": "node",
|
||||
"request": "attach",
|
||||
"port": 9229,
|
||||
"preLaunchTask": "func: host start"
|
||||
"preDebugTask": "func: host start"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ Under the hood, this uses go's [text/template][go-text-template] feature extende
|
|||
offers a few standard properties to work with.
|
||||
|
||||
- `.Root`: `boolean` - is the current user root/admin or not
|
||||
- `.Path`: `string` - the current working directory
|
||||
- `.PWD`: `string` - the current working directory
|
||||
- `.Folder`: `string` - the current working folder
|
||||
- `.Shell`: `string` - the current shell name
|
||||
- `.User`: `string` - the current user name
|
||||
|
@ -53,9 +53,9 @@ the current working directory is `/usr/home/omp` and the shell is `zsh`.
|
|||
// when root == false: omp :: zsh
|
||||
// when root == true: omp :: root :: zsh
|
||||
"console_title_template": "{{.Folder}}", // outputs: omp
|
||||
"console_title_template": "{{.Shell}} in {{.Path}}", // outputs: zsh in /usr/home/omp
|
||||
"console_title_template": "{{.User}}@{{.Host}} {{.Shell}} in {{.Path}}", // outputs: MyUser@MyMachine zsh in /usr/home/omp
|
||||
"console_title_template": "{{.Env.USERDOMAIN}} {{.Shell}} in {{.Path}}", // outputs: MyCompany zsh in /usr/home/omp
|
||||
"console_title_template": "{{.Shell}} in {{.PWD}}", // outputs: zsh in /usr/home/omp
|
||||
"console_title_template": "{{.User}}@{{.Host}} {{.Shell}} in {{.PWD}}", // outputs: MyUser@MyMachine zsh in /usr/home/omp
|
||||
"console_title_template": "{{.Env.USERDOMAIN}} {{.Shell}} in {{.PWD}}", // outputs: MyCompany zsh in /usr/home/omp
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ performance - defaults to `false`
|
|||
|
||||
- `.Context`: `string` - the current kubectl context
|
||||
- `.Namespace`: `string` - the current kubectl context namespace
|
||||
- `.User`: `string` - the current kubectl context user
|
||||
- `.UserName`: `string` - the current kubectl context user
|
||||
- `.Cluster`: `string` - the current kubectl context cluster
|
||||
|
||||
## Tips
|
||||
|
|
|
@ -127,7 +127,6 @@ starts with a symbol or icon.
|
|||
|
||||
## Template Properties
|
||||
|
||||
- `.PWD`: `string` - the current directory (non styled)
|
||||
- `.Path`: `string` - the current directory (styled)
|
||||
- `.StackCount`: `int` - the stack count
|
||||
|
||||
|
|
|
@ -44,7 +44,10 @@ func (t *consoleTitle) getTemplateText() string {
|
|||
Template: t.config.ConsoleTitleTemplate,
|
||||
Env: t.env,
|
||||
}
|
||||
return template.renderPlainContextTemplate(nil)
|
||||
if text, err := template.render(); err == nil {
|
||||
return text
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (t *consoleTitle) getPwd() string {
|
||||
|
|
|
@ -22,7 +22,7 @@ func TestGetConsoleTitle(t *testing.T) {
|
|||
{Style: FullPath, Cwd: "/usr/home/jan", PathSeperator: "/", ShellName: "default", Expected: "\x1b]0;~/jan\a"},
|
||||
{
|
||||
Style: Template,
|
||||
Template: "{{.Env.USERDOMAIN}} :: {{.Path}}{{if .Root}} :: Admin{{end}} :: {{.Shell}}",
|
||||
Template: "{{.Env.USERDOMAIN}} :: {{.PWD}}{{if .Root}} :: Admin{{end}} :: {{.Shell}}",
|
||||
Cwd: "C:\\vagrant",
|
||||
PathSeperator: "\\",
|
||||
ShellName: "PowerShell",
|
||||
|
@ -62,6 +62,7 @@ func TestGetConsoleTitle(t *testing.T) {
|
|||
env.On("getenv", "USERDOMAIN").Return("MyCompany")
|
||||
env.On("getCurrentUser", nil).Return("MyUser")
|
||||
env.On("getHostName", nil).Return("MyHost", nil)
|
||||
env.onTemplate()
|
||||
ansi := &ansiUtils{}
|
||||
ansi.init(tc.ShellName)
|
||||
ct := &consoleTitle{
|
||||
|
@ -117,6 +118,7 @@ func TestGetConsoleTitleIfGethostnameReturnsError(t *testing.T) {
|
|||
env.On("getenv", "USERDOMAIN").Return("MyCompany")
|
||||
env.On("getCurrentUser", nil).Return("MyUser")
|
||||
env.On("getHostName", nil).Return("", fmt.Errorf("I have a bad feeling about this"))
|
||||
env.onTemplate()
|
||||
ansi := &ansiUtils{}
|
||||
ansi.init(tc.ShellName)
|
||||
ct := &consoleTitle{
|
||||
|
|
|
@ -241,7 +241,10 @@ func (e *engine) renderTransientPrompt() string {
|
|||
Template: promptTemplate,
|
||||
Env: e.env,
|
||||
}
|
||||
prompt := template.renderPlainContextTemplate(nil)
|
||||
prompt, err := template.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.getShellName() {
|
||||
|
|
|
@ -19,6 +19,7 @@ func TestConsoleBackgroundColorTemplate(t *testing.T) {
|
|||
for _, tc := range cases {
|
||||
env := new(MockedEnvironment)
|
||||
env.On("getenv", "TERM_PROGRAM").Return(tc.Term)
|
||||
env.onTemplate()
|
||||
color := getConsoleBackgroundColor(env, "{{ if eq \"vscode\" .Env.TERM_PROGRAM }}#123456{{end}}")
|
||||
assert.Equal(t, tc.Expected, color, tc.Case)
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ func TestAngularCliVersionDisplayed(t *testing.T) {
|
|||
env.On("hasFiles", params.extension).Return(true)
|
||||
env.On("hasFilesInDir", "/usr/home/dev/my-app/node_modules/@angular/core", "package.json").Return(true)
|
||||
env.On("getFileContent", "/usr/home/dev/my-app/node_modules/@angular/core/package.json").Return(ta.Version)
|
||||
|
||||
env.onTemplate()
|
||||
props := properties{}
|
||||
angular := &angular{}
|
||||
angular.init(props, env)
|
||||
|
|
|
@ -54,6 +54,7 @@ func TestAWSSegment(t *testing.T) {
|
|||
env.On("getenv", "AWS_CONFIG_FILE").Return(tc.ConfigFile)
|
||||
env.On("getFileContent", "/usr/home/.aws/config").Return("")
|
||||
env.On("homeDir", nil).Return("/usr/home")
|
||||
env.onTemplate()
|
||||
props := properties{
|
||||
DisplayDefault: tc.DisplayDefault,
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ func TestAzSegment(t *testing.T) {
|
|||
}
|
||||
env.On("getFileContent", filepath.Join(home, ".azure/azureProfile.json")).Return(azureProfile)
|
||||
env.On("getFileContent", filepath.Join(home, ".azure/AzureRmContext.json")).Return(azureRmContext)
|
||||
|
||||
env.onTemplate()
|
||||
props := properties{
|
||||
SegmentTemplate: tc.Template,
|
||||
}
|
||||
|
|
|
@ -151,7 +151,7 @@ func TestBrewfatherSegment(t *testing.T) {
|
|||
env.On("HTTPRequest", BFBatchURL).Return([]byte(tc.BatchJSONResponse), tc.Error)
|
||||
env.On("HTTPRequest", BFBatchReadingsURL).Return([]byte(tc.BatchReadingsJSONResponse), tc.Error)
|
||||
env.On("cache", nil).Return(cache)
|
||||
|
||||
env.onTemplate()
|
||||
if tc.Template != "" {
|
||||
props[SegmentTemplate] = tc.Template
|
||||
}
|
||||
|
|
|
@ -181,13 +181,13 @@ const (
|
|||
|
||||
func (e *exit) deprecatedString() string {
|
||||
colorBackground := e.props.getBool(ColorBackground, false)
|
||||
if e.Code != 0 && !colorBackground {
|
||||
if e.code != 0 && !colorBackground {
|
||||
e.props.set(ForegroundOverride, e.props.getColor(ErrorColor, e.props.getColor(ForegroundOverride, "")))
|
||||
}
|
||||
if e.Code != 0 && colorBackground {
|
||||
if e.code != 0 && colorBackground {
|
||||
e.props.set(BackgroundOverride, e.props.getColor(ErrorColor, e.props.getColor(BackgroundOverride, "")))
|
||||
}
|
||||
if e.Code == 0 {
|
||||
if e.code == 0 {
|
||||
return e.props.getString(SuccessIcon, "")
|
||||
}
|
||||
errorIcon := e.props.getString(ErrorIcon, "")
|
||||
|
@ -195,7 +195,7 @@ func (e *exit) deprecatedString() string {
|
|||
return errorIcon
|
||||
}
|
||||
if e.props.getBool(AlwaysNumeric, false) {
|
||||
return fmt.Sprintf("%s%d", errorIcon, e.Code)
|
||||
return fmt.Sprintf("%s%d", errorIcon, e.code)
|
||||
}
|
||||
return fmt.Sprintf("%s%s", errorIcon, e.Text)
|
||||
}
|
||||
|
|
|
@ -447,6 +447,7 @@ func TestBatterySegmentSingle(t *testing.T) {
|
|||
props[DisplayCharged] = false
|
||||
}
|
||||
env.On("getBatteryInfo", nil).Return(tc.Batteries, tc.Error)
|
||||
env.onTemplate()
|
||||
b := &batt{
|
||||
props: props,
|
||||
env: env,
|
||||
|
@ -756,6 +757,7 @@ func TestPythonVirtualEnv(t *testing.T) {
|
|||
env.On("getPathSeperator", nil).Return("")
|
||||
env.On("getcwd", nil).Return("/usr/home/project")
|
||||
env.On("homeDir", nil).Return("/usr/home")
|
||||
env.onTemplate()
|
||||
props := properties{
|
||||
FetchVersion: tc.FetchVersion,
|
||||
DisplayVirtualEnv: true,
|
||||
|
|
|
@ -28,6 +28,7 @@ func bootStrapDotnetTest(args *dotnetArgs) *dotnet {
|
|||
env.On("getPathSeperator", nil).Return("")
|
||||
env.On("getcwd", nil).Return("/usr/home/project")
|
||||
env.On("homeDir", nil).Return("/usr/home")
|
||||
env.onTemplate()
|
||||
props := properties{
|
||||
FetchVersion: args.displayVersion,
|
||||
UnsupportedDotnetVersionIcon: args.unsupportedIcon,
|
||||
|
|
|
@ -6,7 +6,7 @@ type exit struct {
|
|||
props Properties
|
||||
env Environment
|
||||
|
||||
Code int
|
||||
code int
|
||||
Text string
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,7 @@ func (e *exit) init(props Properties, env Environment) {
|
|||
}
|
||||
|
||||
func (e *exit) getFormattedText() string {
|
||||
e.Code = e.env.lastErrorCode()
|
||||
e.code = e.env.lastErrorCode()
|
||||
e.Text = e.getMeaningFromExitCode()
|
||||
segmentTemplate := e.props.getString(SegmentTemplate, "")
|
||||
if len(segmentTemplate) == 0 {
|
||||
|
@ -46,7 +46,7 @@ func (e *exit) getFormattedText() string {
|
|||
}
|
||||
|
||||
func (e *exit) getMeaningFromExitCode() string {
|
||||
switch e.Code {
|
||||
switch e.code {
|
||||
case 1:
|
||||
return "ERROR"
|
||||
case 2:
|
||||
|
@ -100,6 +100,6 @@ func (e *exit) getMeaningFromExitCode() string {
|
|||
case 128 + 22:
|
||||
return "SIGTTOU"
|
||||
default:
|
||||
return strconv.Itoa(e.Code)
|
||||
return strconv.Itoa(e.code)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ func TestGetMeaningFromExitCode(t *testing.T) {
|
|||
errorMap[7000] = "7000"
|
||||
for exitcode, want := range errorMap {
|
||||
e := &exit{}
|
||||
e.Code = exitcode
|
||||
e.code = exitcode
|
||||
assert.Equal(t, want, e.getMeaningFromExitCode())
|
||||
}
|
||||
}
|
||||
|
@ -77,6 +77,7 @@ func TestExitWriterTemplateString(t *testing.T) {
|
|||
for _, tc := range cases {
|
||||
env := new(MockedEnvironment)
|
||||
env.On("lastErrorCode", nil).Return(tc.ExitCode)
|
||||
env.onTemplate()
|
||||
props := properties{
|
||||
SegmentTemplate: tc.Template,
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ func getMockedLanguageEnv(params *mockedLanguageParams) (*MockedEnvironment, pro
|
|||
env.On("hasFiles", params.extension).Return(true)
|
||||
env.On("getcwd", nil).Return("/usr/home/project")
|
||||
env.On("homeDir", nil).Return("/usr/home")
|
||||
env.onTemplate()
|
||||
props := properties{
|
||||
FetchVersion: true,
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ func TestIpifySegment(t *testing.T) {
|
|||
}
|
||||
|
||||
env.On("HTTPRequest", IPIFYAPIURL).Return([]byte(tc.Response), tc.Error)
|
||||
env.onTemplate()
|
||||
|
||||
if tc.Template != "" {
|
||||
props[SegmentTemplate] = tc.Template
|
||||
|
|
|
@ -66,6 +66,7 @@ func TestJava(t *testing.T) {
|
|||
} else {
|
||||
env.On("getenv", "JAVA_HOME").Return("")
|
||||
}
|
||||
env.onTemplate()
|
||||
props := properties{
|
||||
FetchVersion: true,
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ type KubeConfig struct {
|
|||
|
||||
type KubeContext struct {
|
||||
Cluster string `yaml:"cluster"`
|
||||
User string `yaml:"user"`
|
||||
UserName string `yaml:"user"`
|
||||
Namespace string `yaml:"namespace"`
|
||||
}
|
||||
|
||||
|
@ -139,6 +139,6 @@ func (k *kubectl) setError(message string) {
|
|||
k.Context = message
|
||||
}
|
||||
k.Namespace = message
|
||||
k.User = message
|
||||
k.UserName = message
|
||||
k.Cluster = message
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const testKubectlAllInfoTemplate = "{{.Context}} :: {{.Namespace}} :: {{.User}} :: {{.Cluster}}"
|
||||
const testKubectlAllInfoTemplate = "{{.Context}} :: {{.Namespace}} :: {{.UserName}} :: {{.Cluster}}"
|
||||
|
||||
func TestKubectlSegment(t *testing.T) {
|
||||
standardTemplate := "{{.Context}}{{if .Namespace}} :: {{.Namespace}}{{end}}"
|
||||
|
@ -24,7 +24,7 @@ func TestKubectlSegment(t *testing.T) {
|
|||
ParseKubeConfig bool
|
||||
Context string
|
||||
Namespace string
|
||||
User string
|
||||
UserName string
|
||||
Cluster string
|
||||
KubectlErr bool
|
||||
ExpectedEnabled bool
|
||||
|
@ -47,7 +47,7 @@ func TestKubectlSegment(t *testing.T) {
|
|||
KubectlExists: true,
|
||||
Context: "aaa",
|
||||
Namespace: "bbb",
|
||||
User: "ccc",
|
||||
UserName: "ccc",
|
||||
Cluster: "ddd",
|
||||
ExpectedString: "aaa :: bbb :: ccc :: ddd",
|
||||
ExpectedEnabled: true,
|
||||
|
@ -110,7 +110,7 @@ func TestKubectlSegment(t *testing.T) {
|
|||
var kubeconfig string
|
||||
content, err := ioutil.ReadFile("./test/kubectl.yml")
|
||||
if err == nil {
|
||||
kubeconfig = fmt.Sprintf(string(content), tc.Cluster, tc.User, tc.Namespace, tc.Context)
|
||||
kubeconfig = fmt.Sprintf(string(content), tc.Cluster, tc.UserName, tc.Namespace, tc.Context)
|
||||
}
|
||||
var kubectlErr error
|
||||
if tc.KubectlErr {
|
||||
|
@ -125,6 +125,7 @@ func TestKubectlSegment(t *testing.T) {
|
|||
env.On("getFileContent", path).Return(content)
|
||||
}
|
||||
env.On("homeDir", nil).Return("testhome")
|
||||
env.onTemplate()
|
||||
|
||||
k := &kubectl{
|
||||
env: env,
|
||||
|
|
|
@ -50,6 +50,7 @@ func bootStrapLanguageTest(args *languageArgs) *language {
|
|||
}
|
||||
env.On("getcwd", nil).Return(cwd)
|
||||
env.On("homeDir", nil).Return(home)
|
||||
env.onTemplate()
|
||||
if args.properties == nil {
|
||||
args.properties = properties{}
|
||||
}
|
||||
|
|
|
@ -60,6 +60,7 @@ func TestNbgv(t *testing.T) {
|
|||
env := new(MockedEnvironment)
|
||||
env.On("hasCommand", "nbgv").Return(tc.HasNbgv)
|
||||
env.On("runCommand", "nbgv", []string{"get-version", "--format=json"}).Return(tc.Response, tc.Error)
|
||||
env.onTemplate()
|
||||
nbgv := &nbgv{
|
||||
env: env,
|
||||
props: properties{
|
||||
|
|
|
@ -141,6 +141,7 @@ func TestNSSegment(t *testing.T) {
|
|||
|
||||
env.On("HTTPRequest", FAKEAPIURL).Return([]byte(tc.JSONResponse), tc.Error)
|
||||
env.On("cache", nil).Return(cache)
|
||||
env.onTemplate()
|
||||
|
||||
if tc.Template != "" {
|
||||
props[SegmentTemplate] = tc.Template
|
||||
|
|
|
@ -59,6 +59,7 @@ func TestOWMSegmentSingle(t *testing.T) {
|
|||
}
|
||||
|
||||
env.On("HTTPRequest", OWMAPIURL).Return([]byte(tc.JSONResponse), tc.Error)
|
||||
env.onTemplate()
|
||||
|
||||
if tc.Template != "" {
|
||||
props[SegmentTemplate] = tc.Template
|
||||
|
@ -185,6 +186,7 @@ func TestOWMSegmentIcons(t *testing.T) {
|
|||
expectedString := fmt.Sprintf("%s (20°C)", tc.ExpectedIconString)
|
||||
|
||||
env.On("HTTPRequest", OWMAPIURL).Return([]byte(response), nil)
|
||||
env.onTemplate()
|
||||
|
||||
o := &owm{
|
||||
props: properties{
|
||||
|
@ -208,6 +210,7 @@ func TestOWMSegmentIcons(t *testing.T) {
|
|||
expectedString := fmt.Sprintf("[%s (20°C)](http://api.openweathermap.org/data/2.5/weather?q=AMSTERDAM,NL&units=metric&appid=key)", tc.ExpectedIconString)
|
||||
|
||||
env.On("HTTPRequest", OWMAPIURL).Return([]byte(response), nil)
|
||||
env.onTemplate()
|
||||
|
||||
o := &owm{
|
||||
props: properties{
|
||||
|
@ -243,6 +246,7 @@ func TestOWMSegmentFromCache(t *testing.T) {
|
|||
cache.On("get", "owm_url").Return("http://api.openweathermap.org/data/2.5/weather?q=AMSTERDAM,NL&units=metric&appid=key", true)
|
||||
cache.On("set").Return()
|
||||
env.On("cache", nil).Return(cache)
|
||||
env.onTemplate()
|
||||
|
||||
assert.Nil(t, o.setStatus())
|
||||
assert.Equal(t, expectedString, o.string())
|
||||
|
@ -269,6 +273,7 @@ func TestOWMSegmentFromCacheWithHyperlink(t *testing.T) {
|
|||
cache.On("get", "owm_url").Return("http://api.openweathermap.org/data/2.5/weather?q=AMSTERDAM,NL&units=metric&appid=key", true)
|
||||
cache.On("set").Return()
|
||||
env.On("cache", nil).Return(cache)
|
||||
env.onTemplate()
|
||||
|
||||
assert.Nil(t, o.setStatus())
|
||||
assert.Equal(t, expectedString, o.string())
|
||||
|
|
|
@ -11,7 +11,7 @@ type path struct {
|
|||
props Properties
|
||||
env Environment
|
||||
|
||||
PWD string
|
||||
pwd string
|
||||
Path string
|
||||
StackCount int
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ func (pt *path) enabled() bool {
|
|||
}
|
||||
|
||||
func (pt *path) string() string {
|
||||
pt.PWD = pt.env.getcwd()
|
||||
pt.pwd = pt.env.getcwd()
|
||||
switch style := pt.props.getString(Style, Agnoster); style {
|
||||
case Agnoster:
|
||||
pt.Path = pt.getAgnosterPath()
|
||||
|
@ -86,9 +86,9 @@ func (pt *path) string() string {
|
|||
if pt.props.getBool(EnableHyperlink, false) {
|
||||
// wsl check
|
||||
if pt.env.isWsl() {
|
||||
pt.PWD, _ = pt.env.runCommand("wslpath", "-m", pt.PWD)
|
||||
pt.pwd, _ = pt.env.runCommand("wslpath", "-m", pt.pwd)
|
||||
}
|
||||
pt.Path = fmt.Sprintf("[%s](file://%s)", pt.Path, pt.PWD)
|
||||
pt.Path = fmt.Sprintf("[%s](file://%s)", pt.Path, pt.pwd)
|
||||
}
|
||||
|
||||
pt.StackCount = pt.env.stackCount()
|
||||
|
|
|
@ -203,6 +203,25 @@ func (env *MockedEnvironment) getWifiNetwork() (*wifiInfo, error) {
|
|||
return args.Get(0).(*wifiInfo), args.Error(1)
|
||||
}
|
||||
|
||||
func (env *MockedEnvironment) onTemplate() {
|
||||
patchMethodIfNotSpecified := func(method string, returnArguments ...interface{}) {
|
||||
for _, call := range env.Mock.ExpectedCalls {
|
||||
if call.Method == method {
|
||||
return
|
||||
}
|
||||
}
|
||||
env.On(method, nil).Return(returnArguments...)
|
||||
}
|
||||
patchMethodIfNotSpecified("isRunningAsRoot", false)
|
||||
patchMethodIfNotSpecified("getcwd", "/usr/home/dev/my-app")
|
||||
patchMethodIfNotSpecified("homeDir", "/usr/home/dev")
|
||||
patchMethodIfNotSpecified("getPathSeperator", "/")
|
||||
patchMethodIfNotSpecified("getShellName", "pwsh")
|
||||
patchMethodIfNotSpecified("getCurrentUser", "dev")
|
||||
patchMethodIfNotSpecified("getHostName", "laptop", nil)
|
||||
patchMethodIfNotSpecified("lastErrorCode", 0)
|
||||
}
|
||||
|
||||
const (
|
||||
homeBill = "/home/bill"
|
||||
homeJan = "/usr/home/jan"
|
||||
|
@ -417,6 +436,7 @@ func TestAgnosterPathStyles(t *testing.T) {
|
|||
PSWD: &tc.Pswd,
|
||||
}
|
||||
env.On("getArgs", nil).Return(args)
|
||||
env.onTemplate()
|
||||
path := &path{
|
||||
env: env,
|
||||
props: properties{
|
||||
|
@ -537,6 +557,7 @@ func TestGetFullPath(t *testing.T) {
|
|||
PSWD: &tc.Pswd,
|
||||
}
|
||||
env.On("getArgs", nil).Return(args)
|
||||
env.onTemplate()
|
||||
if len(tc.Template) == 0 {
|
||||
tc.Template = "{{ if gt .StackCount 0 }}{{ .StackCount }} {{ end }}{{ .Path }}"
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ func TestPythonTemplate(t *testing.T) {
|
|||
env.On("getPathSeperator", nil).Return("")
|
||||
env.On("getcwd", nil).Return("/usr/home/project")
|
||||
env.On("homeDir", nil).Return("/usr/home")
|
||||
env.onTemplate()
|
||||
props := properties{
|
||||
FetchVersion: tc.FetchVersion,
|
||||
SegmentTemplate: tc.Template,
|
||||
|
|
|
@ -98,6 +98,7 @@ func TestRuby(t *testing.T) {
|
|||
env.On("hasFiles", "Gemfile").Return(tc.HasGemFile)
|
||||
env.On("getcwd", nil).Return("/usr/home/project")
|
||||
env.On("homeDir", nil).Return("/usr/home")
|
||||
env.onTemplate()
|
||||
props := properties{
|
||||
FetchVersion: tc.FetchVersion,
|
||||
}
|
||||
|
|
|
@ -107,6 +107,7 @@ func TestSessionSegmentTemplate(t *testing.T) {
|
|||
env.On("getenv", "SSH_CLIENT").Return(SSHSession)
|
||||
env.On("isRunningAsRoot", nil).Return(tc.Root)
|
||||
env.On("getenv", defaultUserEnvVar).Return(tc.DefaultUserName)
|
||||
env.onTemplate()
|
||||
session := &session{
|
||||
env: env,
|
||||
props: properties{
|
||||
|
|
|
@ -154,6 +154,7 @@ func TestStravaSegment(t *testing.T) {
|
|||
env.On("HTTPRequest", url).Return([]byte(tc.JSONResponse), tc.Error)
|
||||
env.On("HTTPRequest", tokenURL).Return([]byte(tc.TokenResponse), tc.Error)
|
||||
env.On("cache", nil).Return(cache)
|
||||
env.onTemplate()
|
||||
|
||||
if tc.Template != "" {
|
||||
props[SegmentTemplate] = tc.Template
|
||||
|
|
|
@ -34,7 +34,9 @@ func TestSysInfo(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
tc.SysInfo.env = new(MockedEnvironment)
|
||||
env := new(MockedEnvironment)
|
||||
env.onTemplate()
|
||||
tc.SysInfo.env = env
|
||||
tc.SysInfo.props = properties{
|
||||
Precision: tc.Precision,
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ func bootStrapTerraformTest(args *terraformArgs) *terraform {
|
|||
env.On("hasFolder", "/.terraform").Return(args.hasTfFolder)
|
||||
env.On("getcwd", nil).Return("")
|
||||
env.On("runCommand", "terraform", []string{"workspace", "show"}).Return(args.workspaceName, nil)
|
||||
env.onTemplate()
|
||||
k := &terraform{
|
||||
env: env,
|
||||
props: properties{},
|
||||
|
|
|
@ -18,8 +18,11 @@ func (t *text) enabled() bool {
|
|||
Context: t,
|
||||
Env: t.env,
|
||||
}
|
||||
t.content = template.renderPlainContextTemplate(nil)
|
||||
return len(t.content) > 0
|
||||
if text, err := template.render(); err == nil {
|
||||
t.content = text
|
||||
return len(t.content) > 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (t *text) string() string {
|
||||
|
|
|
@ -33,6 +33,7 @@ func TestTextSegment(t *testing.T) {
|
|||
env.On("getenv", "WORLD").Return("")
|
||||
env.On("getCurrentUser", nil).Return("Posh")
|
||||
env.On("getHostName", nil).Return("MyHost", nil)
|
||||
env.onTemplate()
|
||||
txt := &text{
|
||||
env: env,
|
||||
props: properties{
|
||||
|
|
|
@ -39,6 +39,7 @@ func TestTimeSegmentTemplate(t *testing.T) {
|
|||
|
||||
for _, tc := range cases {
|
||||
env := new(MockedEnvironment)
|
||||
env.onTemplate()
|
||||
tempus := &tempus{
|
||||
env: env,
|
||||
props: properties{
|
||||
|
|
|
@ -77,6 +77,7 @@ func TestWTTrackedTime(t *testing.T) {
|
|||
cache.On("get", FAKEAPIURL).Return(response, !tc.CacheFoundFail)
|
||||
cache.On("set", FAKEAPIURL, response, tc.CacheTimeout).Return()
|
||||
env.On("cache", nil).Return(cache)
|
||||
env.onTemplate()
|
||||
|
||||
w := &wakatime{
|
||||
props: properties{
|
||||
|
|
|
@ -45,6 +45,7 @@ func TestWiFiSegment(t *testing.T) {
|
|||
env.On("getPlatform", nil).Return(windowsPlatform)
|
||||
env.On("isWsl", nil).Return(false)
|
||||
env.On("getWifiNetwork", nil).Return(tc.Network, tc.WifiError)
|
||||
env.onTemplate()
|
||||
|
||||
w := &wifi{
|
||||
env: env,
|
||||
|
|
|
@ -73,6 +73,7 @@ func TestWinReg(t *testing.T) {
|
|||
env := new(MockedEnvironment)
|
||||
env.On("getRuntimeGOOS", nil).Return(windowsPlatform)
|
||||
env.On("getWindowsRegistryKeyValue", tc.Path).Return(tc.getWRKVOutput, tc.Err)
|
||||
env.onTemplate()
|
||||
r := &winreg{
|
||||
env: env,
|
||||
props: properties{
|
||||
|
|
125
src/template.go
125
src/template.go
|
@ -3,7 +3,7 @@ package main
|
|||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"reflect"
|
||||
"fmt"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
@ -12,6 +12,7 @@ const (
|
|||
// Errors to show when the template handling fails
|
||||
invalidTemplate = "invalid template text"
|
||||
incorrectTemplate = "unable to create text based on template"
|
||||
// nostruct = "unable to create map from non-struct type"
|
||||
|
||||
templateEnvRegex = `\.Env\.(?P<ENV>[^ \.}]*)`
|
||||
)
|
||||
|
@ -22,40 +23,58 @@ type textTemplate struct {
|
|||
Env Environment
|
||||
}
|
||||
|
||||
func (t *textTemplate) renderPlainContextTemplate(context map[string]interface{}) string {
|
||||
if context == nil {
|
||||
context = make(map[string]interface{})
|
||||
type Data interface{}
|
||||
|
||||
type Context struct {
|
||||
Root bool
|
||||
PWD string
|
||||
Folder string
|
||||
Shell string
|
||||
User string
|
||||
Host string
|
||||
Code int
|
||||
Env map[string]string
|
||||
|
||||
// Simple container to hold ANY object
|
||||
Data
|
||||
}
|
||||
|
||||
func (c *Context) init(t *textTemplate) {
|
||||
c.Data = t.Context
|
||||
if t.Env == nil {
|
||||
return
|
||||
}
|
||||
context["Root"] = t.Env.isRunningAsRoot()
|
||||
c.Root = t.Env.isRunningAsRoot()
|
||||
pwd := t.Env.getcwd()
|
||||
pwd = strings.Replace(pwd, t.Env.homeDir(), "~", 1)
|
||||
context["Path"] = pwd
|
||||
context["Folder"] = base(pwd, t.Env)
|
||||
context["Shell"] = t.Env.getShellName()
|
||||
context["User"] = t.Env.getCurrentUser()
|
||||
context["Host"] = ""
|
||||
c.PWD = pwd
|
||||
c.Folder = base(c.PWD, t.Env)
|
||||
c.Shell = t.Env.getShellName()
|
||||
c.User = t.Env.getCurrentUser()
|
||||
if host, err := t.Env.getHostName(); err == nil {
|
||||
context["Host"] = host
|
||||
c.Host = host
|
||||
}
|
||||
t.Context = context
|
||||
text, err := t.render()
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
c.Code = t.Env.lastErrorCode()
|
||||
if strings.Contains(t.Template, ".Env.") {
|
||||
c.Env = map[string]string{}
|
||||
matches := findAllNamedRegexMatch(templateEnvRegex, t.Template)
|
||||
for _, match := range matches {
|
||||
c.Env[match["ENV"]] = t.Env.getenv(match["ENV"])
|
||||
}
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
func (t *textTemplate) render() (string, error) {
|
||||
t.cleanTemplate()
|
||||
tmpl, err := template.New("title").Funcs(funcMap()).Parse(t.Template)
|
||||
if err != nil {
|
||||
return "", errors.New(invalidTemplate)
|
||||
}
|
||||
if strings.Contains(t.Template, ".Env.") {
|
||||
t.loadEnvVars()
|
||||
}
|
||||
context := &Context{}
|
||||
context.init(t)
|
||||
buffer := new(bytes.Buffer)
|
||||
defer buffer.Reset()
|
||||
err = tmpl.Execute(buffer, t.Context)
|
||||
err = tmpl.Execute(buffer, context)
|
||||
if err != nil {
|
||||
return "", errors.New(incorrectTemplate)
|
||||
}
|
||||
|
@ -66,56 +85,28 @@ func (t *textTemplate) render() (string, error) {
|
|||
return text, nil
|
||||
}
|
||||
|
||||
func (t *textTemplate) loadEnvVars() {
|
||||
context := make(map[string]interface{})
|
||||
switch v := t.Context.(type) {
|
||||
case map[string]interface{}:
|
||||
context = v
|
||||
default:
|
||||
// we currently only support structs
|
||||
if !t.isStruct() {
|
||||
break
|
||||
func (t *textTemplate) cleanTemplate() {
|
||||
unknownVariable := func(variable string, knownVariables *[]string) (string, bool) {
|
||||
variable = strings.TrimPrefix(variable, ".")
|
||||
splitted := strings.Split(variable, ".")
|
||||
if len(splitted) == 0 {
|
||||
return "", false
|
||||
}
|
||||
context = t.structToMap()
|
||||
for _, b := range *knownVariables {
|
||||
if b == splitted[0] {
|
||||
return "", false
|
||||
}
|
||||
}
|
||||
*knownVariables = append(*knownVariables, splitted[0])
|
||||
return splitted[0], true
|
||||
}
|
||||
envVars := map[string]string{}
|
||||
matches := findAllNamedRegexMatch(templateEnvRegex, t.Template)
|
||||
knownVariables := []string{"Root", "PWD", "Folder", "Shell", "User", "Host", "Env", "Data", "Code"}
|
||||
matches := findAllNamedRegexMatch(`(?: |{)(?P<var>(\.[a-zA-Z_][a-zA-Z0-9]*)+)`, t.Template)
|
||||
for _, match := range matches {
|
||||
envVars[match["ENV"]] = t.Env.getenv(match["ENV"])
|
||||
}
|
||||
context["Env"] = envVars
|
||||
t.Context = context
|
||||
}
|
||||
|
||||
func (t *textTemplate) isStruct() bool {
|
||||
v := reflect.TypeOf(t.Context)
|
||||
if v == nil {
|
||||
return false
|
||||
}
|
||||
if v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
if v.Kind() == reflect.Invalid {
|
||||
return false
|
||||
}
|
||||
return v.Kind() == reflect.Struct
|
||||
}
|
||||
|
||||
func (t *textTemplate) structToMap() map[string]interface{} {
|
||||
context := make(map[string]interface{})
|
||||
v := reflect.ValueOf(t.Context)
|
||||
if v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
strct := v.Type()
|
||||
for i := 0; i < strct.NumField(); i++ {
|
||||
sf := strct.Field(i)
|
||||
if !v.Field(i).CanInterface() {
|
||||
continue
|
||||
if variable, OK := unknownVariable(match["var"], &knownVariables); OK {
|
||||
pattern := fmt.Sprintf(`\.%s\b`, variable)
|
||||
dataVar := fmt.Sprintf(".Data.%s", variable)
|
||||
t.Template = replaceAllString(pattern, t.Template, dataVar)
|
||||
}
|
||||
name := sf.Name
|
||||
value := v.Field(i).Interface()
|
||||
context[name] = value
|
||||
}
|
||||
return context
|
||||
}
|
||||
|
|
|
@ -69,6 +69,7 @@ func TestRenderTemplate(t *testing.T) {
|
|||
}
|
||||
|
||||
env := &MockedEnvironment{}
|
||||
env.onTemplate()
|
||||
for _, tc := range cases {
|
||||
template := &textTemplate{
|
||||
Template: tc.Template,
|
||||
|
@ -93,6 +94,13 @@ func TestRenderTemplateEnvVar(t *testing.T) {
|
|||
Env map[string]string
|
||||
Context interface{}
|
||||
}{
|
||||
{
|
||||
Case: "nil struct with env var",
|
||||
ShouldError: true,
|
||||
Template: "{{.Env.HELLO }} world{{ .Text}}",
|
||||
Context: nil,
|
||||
Env: map[string]string{"HELLO": "hello"},
|
||||
},
|
||||
{
|
||||
Case: "map with env var",
|
||||
Expected: "hello world",
|
||||
|
@ -100,13 +108,6 @@ func TestRenderTemplateEnvVar(t *testing.T) {
|
|||
Context: map[string]interface{}{"World": "world"},
|
||||
Env: map[string]string{"HELLO": "hello"},
|
||||
},
|
||||
{
|
||||
Case: "nil struct with env var",
|
||||
Expected: "hello world",
|
||||
Template: "{{.Env.HELLO }} world{{ .Text}}",
|
||||
Context: nil,
|
||||
Env: map[string]string{"HELLO": "hello"},
|
||||
},
|
||||
{
|
||||
Case: "struct with env var",
|
||||
Expected: "hello world posh",
|
||||
|
@ -120,6 +121,7 @@ func TestRenderTemplateEnvVar(t *testing.T) {
|
|||
}
|
||||
for _, tc := range cases {
|
||||
env := &MockedEnvironment{}
|
||||
env.onTemplate()
|
||||
for name, value := range tc.Env {
|
||||
env.On("getenv", name).Return(value)
|
||||
}
|
||||
|
@ -136,3 +138,49 @@ func TestRenderTemplateEnvVar(t *testing.T) {
|
|||
assert.Equal(t, tc.Expected, text, tc.Case)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCleanTemplate(t *testing.T) {
|
||||
cases := []struct {
|
||||
Case string
|
||||
Expected string
|
||||
Template string
|
||||
}{
|
||||
{
|
||||
Case: "Variable",
|
||||
Expected: "{{range $cpu := .Data.CPU}}{{round $cpu.Mhz 2 }} {{end}}",
|
||||
Template: "{{range $cpu := .CPU}}{{round $cpu.Mhz 2 }} {{end}}",
|
||||
},
|
||||
{
|
||||
Case: "Same prefix",
|
||||
Expected: "{{ .Env.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 }}",
|
||||
Template: "{{ .Env.HELLO }} {{ .World.Trend }} {{ .World.Hello }} {{ .World }}",
|
||||
},
|
||||
{
|
||||
Case: "Hello world",
|
||||
Expected: "{{.Env.HELLO}} {{.Data.World}}",
|
||||
Template: "{{.Env.HELLO}} {{.World}}",
|
||||
},
|
||||
{
|
||||
Case: "Multiple vars",
|
||||
Expected: "{{.Env.HELLO}} {{.Data.World}} {{.Data.World}}",
|
||||
Template: "{{.Env.HELLO}} {{.World}} {{.World}}",
|
||||
},
|
||||
{
|
||||
Case: "Multiple vars with spaces",
|
||||
Expected: "{{ .Env.HELLO }} {{ .Data.World }} {{ .Data.World }}",
|
||||
Template: "{{ .Env.HELLO }} {{ .World }} {{ .World }}",
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
template := &textTemplate{
|
||||
Template: tc.Template,
|
||||
}
|
||||
template.cleanTemplate()
|
||||
assert.Equal(t, tc.Expected, template.Template, tc.Case)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue