Repository: JanDeDobbeleer/oh-my-posh
Pull request #5505 (2024-08-22 17:55:07)

fix(elvish): improve robustness of initialization
### Prerequisites

- [x] I have read and understood the [contributing guide][CONTRIBUTING.md].
- [x] The commit message follows the [conventional commits][cc] guidelines.
- [ ] Tests for the changes have been added (for bug fixes / features).
- [ ] Docs have been added/updated (for bug fixes / features).

### Description

This PR is to improve the robustness of the OMP initialization for Elvish:

- When the strict mode is used (via the `--strict`/`-s` CLI flag), the builtin `external` function is always needed to construct a callable value since we only have the basename of the OMP executable in that case, otherwise an error will occur: `bad value: command must be callable or string containing slash`.
- Strings used as replacements for `::OMP::` and `::CONFIG::` placeholders, should always be quoted in case they contain special characters.

[CONTRIBUTING.md]: https://github.com/JanDeDobbeleer/oh-my-posh/blob/main/CONTRIBUTING.md
[cc]: https://www.conventionalcommits.org/en/v1.0.0/#summary


Patch:
diff --git a/src/shell/init.go b/src/shell/init.go
index 566f616a..1037c97f 100644
--- a/src/shell/init.go
+++ b/src/shell/init.go
@@ -54,16 +54,17 @@ func Init(env runtime.Environment, feats Features) string {
 		}
 
 		var command, config string
+
 		switch shell {
 		case PWSH, PWSH5:
 			command = "(@(& %s init %s --config=%s --print%s) -join \"`n\") | Invoke-Expression"
-			config = quotePwshStr(env.Flags().Config)
-			executable = quotePwshStr(executable)
 		case ELVISH:
-			command = "eval (%s init %s --config=%s --print%s | slurp)"
-			config = env.Flags().Config
+			command = "eval ((external %s) init %s --config=%s --print%s | slurp)"
 		}
 
+		config = quotePwshOrElvishStr(env.Flags().Config)
+		executable = quotePwshOrElvishStr(executable)
+
 		return fmt.Sprintf(command, executable, shell, config, additionalParams)
 	case ZSH, BASH, FISH, CMD, TCSH, XONSH:
 		return PrintInit(env, feats, nil)
@@ -88,8 +89,8 @@ func PrintInit(env runtime.Environment, features Features, startTime *time.Time)
 
 	switch shell {
 	case PWSH, PWSH5:
-		executable = quotePwshStr(executable)
-		configFile = quotePwshStr(configFile)
+		executable = quotePwshOrElvishStr(executable)
+		configFile = quotePwshOrElvishStr(configFile)
 		script = pwshInit
 	case ZSH:
 		executable = quotePosixStr(executable)
@@ -116,6 +117,8 @@ func PrintInit(env runtime.Environment, features Features, startTime *time.Time)
 		configFile = quotePosixStr(configFile)
 		script = tcshInit
 	case ELVISH:
+		executable = quotePwshOrElvishStr(executable)
+		configFile = quotePwshOrElvishStr(configFile)
 		script = elvishInit
 	case XONSH:
 		script = xonshInit
diff --git a/src/shell/pwsh.go b/src/shell/pwsh.go
index 6da220c1..13d578c8 100644
--- a/src/shell/pwsh.go
+++ b/src/shell/pwsh.go
@@ -37,6 +37,6 @@ func (f Feature) Pwsh() Code {
 	}
 }
 
-func quotePwshStr(str string) string {
+func quotePwshOrElvishStr(str string) string {
 	return fmt.Sprintf("'%s'", strings.ReplaceAll(str, "'", "''"))
 }
diff --git a/src/shell/scripts/omp.elv b/src/shell/scripts/omp.elv
index f6b94bfa..cba32dcf 100644
--- a/src/shell/scripts/omp.elv
+++ b/src/shell/scripts/omp.elv
@@ -1,21 +1,21 @@
 set-env POSH_PID (to-string (randint 10000000000000 10000000000000000))
-set-env POSH_THEME '::CONFIG::'
+set-env POSH_THEME ::CONFIG::
 set-env POSH_SHELL_VERSION (elvish --version)
 set-env POWERLINE_COMMAND 'oh-my-posh'
 
-var error-code = 0
+var _omp_error_code = 0
 var _omp_executable = ::OMP::
 
 fn posh-after-command-hook {|m|
     var error = $m[error]
     if (is $error $nil) {
-        set error-code = 0
+        set _omp_error_code = 0
     } else {
         try {
-            set error-code = $error[reason][exit-status]
+            set _omp_error_code = $error[reason][exit-status]
         } catch {
             # built-in commands don't have a status code.
-            set error-code = 1
+            set _omp_error_code = 1
         }
     }
 }
@@ -24,10 +24,10 @@ set edit:after-command = [ $@edit:after-command $posh-after-command-hook~ ]
 
 set edit:prompt = {
     var cmd-duration = (printf "%.0f" (* $edit:command-duration 1000))
-    $_omp_executable print primary --shell=elvish --execution-time=$cmd-duration --status=$error-code --pwd=$pwd --shell-version=$E:POSH_SHELL_VERSION
+    (external $_omp_executable) print primary --shell=elvish --execution-time=$cmd-duration --status=$_omp_error_code --pwd=$pwd --shell-version=$E:POSH_SHELL_VERSION
 }
 
 set edit:rprompt = {
     var cmd-duration = (printf "%.0f" (* $edit:command-duration 1000))
-    $_omp_executable print right --shell=elvish --execution-time=$cmd-duration --status=$error-code --pwd=$pwd --shell-version=$E:POSH_SHELL_VERSION
+    (external $_omp_executable) print right --shell=elvish --execution-time=$cmd-duration --status=$_omp_error_code --pwd=$pwd --shell-version=$E:POSH_SHELL_VERSION
 }


Test patch:
diff --git a/src/shell/init_test.go b/src/shell/init_test.go
index 141456a5..d9701dd5 100644
--- a/src/shell/init_test.go
+++ b/src/shell/init_test.go
@@ -7,7 +7,7 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
-func TestQuotePwshStr(t *testing.T) {
+func TestQuotePwshOrElvishStr(t *testing.T) {
 	tests := []struct {
 		str      string
 		expected string
@@ -19,7 +19,7 @@ func TestQuotePwshStr(t *testing.T) {
 		{str: `C:\tmp\omp's dir\oh-my-posh.exe`, expected: `'C:\tmp\omp''s dir\oh-my-posh.exe'`},
 	}
 	for _, tc := range tests {
-		assert.Equal(t, tc.expected, quotePwshStr(tc.str), fmt.Sprintf("quotePwshStr: %s", tc.str))
+		assert.Equal(t, tc.expected, quotePwshOrElvishStr(tc.str), fmt.Sprintf("quotePwshStr: %s", tc.str))
 	}
 }