Merge branch 'main' into patch-1

This commit is contained in:
Gary Sassano 2024-09-19 09:45:35 +02:00 committed by GitHub
commit dee457683f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
50 changed files with 1051 additions and 887 deletions

View file

@ -32,3 +32,17 @@ trim_trailing_whitespace = false
[*.{ps1,psd1,psm1}]
indent_size = 4
charset = utf-8-bom
; Lua
[*.lua]
line_space_after_comment = max(2)
line_space_after_do_statement = max(2)
line_space_after_expression_statement = max(2)
line_space_after_for_statement = max(2)
line_space_after_function_statement = fixed(2)
line_space_after_if_statement = max(2)
line_space_after_local_or_assign_statement = max(2)
line_space_after_repeat_statement = max(2)
line_space_after_while_statement = max(2)
max_line_length = unset
quote_style = single

View file

@ -44,7 +44,7 @@ jobs:
- name: Set default Kubernetes namespace
run: |
kubectl config set-context posh --namespace demo
- uses: azure/login@6c251865b4e6290e7b78be643ea2d005bc51f69a
- uses: azure/login@a65d910e8af852a8061c627c456678983e180302
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Build oh-my-posh 🔧

6
src/cache/cache.go vendored
View file

@ -10,7 +10,7 @@ import (
)
type Cache interface {
Init(home string)
Init(filePath string, persist bool)
Close()
// Gets the value for a given key.
// Returns the value and a boolean indicating if the key was found.
@ -27,9 +27,9 @@ const (
FileName = "omp.cache"
)
var SessionFileName = fmt.Sprintf("%s.%s", FileName, pid())
var SessionFileName = fmt.Sprintf("%s.%s", FileName, sessionID())
func pid() string {
func sessionID() string {
pid := os.Getenv("POSH_PID")
if len(pid) == 0 {
log.Debug("POSH_PID not set, using process pid")

8
src/cache/file.go vendored
View file

@ -13,13 +13,15 @@ type File struct {
cache *maps.Concurrent
cacheFilePath string
dirty bool
persist bool
}
func (fc *File) Init(cacheFilePath string) {
func (fc *File) Init(cacheFilePath string, persist bool) {
defer log.Trace(time.Now(), cacheFilePath)
fc.cache = maps.NewConcurrent()
fc.cacheFilePath = cacheFilePath
fc.persist = persist
log.Debug("loading cache file:", fc.cacheFilePath)
@ -47,14 +49,14 @@ func (fc *File) Init(cacheFilePath string) {
}
func (fc *File) Close() {
if !fc.dirty {
if !fc.persist || !fc.dirty {
return
}
cache := fc.cache.ToSimple()
if dump, err := json.MarshalIndent(cache, "", " "); err == nil {
_ = os.WriteFile(fc.cacheFilePath, dump, 0644)
_ = os.WriteFile(fc.cacheFilePath, dump, 0o644)
}
}

View file

@ -2,17 +2,18 @@ package mock
import mock "github.com/stretchr/testify/mock"
// Cache is an autogenerated mock type for the cache type
type Cache struct {
mock.Mock
}
// close provides a mock function with given fields:
func (_m *Cache) Init(filePath string, persist bool) {
_m.Called(filePath, persist)
}
func (_m *Cache) Close() {
_m.Called()
}
// get provides a mock function with given fields: key
func (_m *Cache) Get(key string) (string, bool) {
ret := _m.Called(key)
@ -33,17 +34,10 @@ func (_m *Cache) Get(key string) (string, bool) {
return r0, r1
}
// init provides a mock function with given fields: home
func (_m *Cache) Init(home string) {
_m.Called(home)
}
// set provides a mock function with given fields: key, value, ttl
func (_m *Cache) Set(key, value string, ttl int) {
_m.Called(key, value, ttl)
}
// delete provides a mock function with given fields: key
func (_m *Cache) Delete(key string) {
_m.Called(key)
}

View file

@ -15,56 +15,68 @@ import (
)
// debugCmd represents the prompt command
var debugCmd = &cobra.Command{
Use: "debug [bash|zsh|fish|powershell|pwsh|cmd|nu|tcsh|elvish|xonsh]",
Short: "Print the prompt in debug mode",
Long: "Print the prompt in debug mode.",
ValidArgs: supportedShells,
Args: NoArgsOrOneValidArg,
Run: func(cmd *cobra.Command, args []string) {
startTime := time.Now()
if len(args) == 0 {
_ = cmd.Help()
return
}
env := &runtime.Terminal{
CmdFlags: &runtime.Flags{
Config: configFlag,
Debug: true,
PWD: pwd,
Shell: args[0],
Plain: plain,
},
}
env.Init()
defer env.Close()
cfg := config.Load(env)
// add variables to the environment
env.Var = cfg.Var
terminal.Init(shell.GENERIC)
terminal.BackgroundColor = cfg.TerminalBackground.ResolveTemplate(env)
terminal.Colors = cfg.MakeColors()
terminal.Plain = plain
eng := &prompt.Engine{
Config: cfg,
Env: env,
Plain: plain,
}
fmt.Print(eng.PrintDebug(startTime, build.Version))
},
}
var debugCmd = createDebugCmd()
func init() {
debugCmd.Flags().StringVar(&pwd, "pwd", "", "current working directory")
debugCmd.Flags().StringVar(&shellName, "shell", "", "the shell to print for")
debugCmd.Flags().BoolVarP(&plain, "plain", "p", false, "plain text output (no ANSI)")
RootCmd.AddCommand(debugCmd)
}
func createDebugCmd() *cobra.Command {
debugCmd := &cobra.Command{
Use: "debug [bash|zsh|fish|powershell|pwsh|cmd|nu|tcsh|elvish|xonsh]",
Short: "Print the prompt in debug mode",
Long: "Print the prompt in debug mode.",
ValidArgs: supportedShells,
Args: NoArgsOrOneValidArg,
Run: func(cmd *cobra.Command, args []string) {
startTime := time.Now()
if len(args) == 0 {
_ = cmd.Help()
return
}
env := &runtime.Terminal{
CmdFlags: &runtime.Flags{
Config: configFlag,
Debug: true,
PWD: pwd,
Shell: args[0],
Plain: plain,
},
}
env.Init()
defer env.Close()
cfg := config.Load(env)
// add variables to the environment
env.Var = cfg.Var
terminal.Init(shell.GENERIC)
terminal.BackgroundColor = cfg.TerminalBackground.ResolveTemplate(env)
terminal.Colors = cfg.MakeColors()
terminal.Plain = plain
eng := &prompt.Engine{
Config: cfg,
Env: env,
Plain: plain,
}
fmt.Print(eng.PrintDebug(startTime, build.Version))
},
}
debugCmd.Flags().StringVar(&pwd, "pwd", "", "current working directory")
debugCmd.Flags().BoolVarP(&plain, "plain", "p", false, "plain text output (no ANSI)")
// Deprecated flags, should be kept to avoid breaking CLI integration.
debugCmd.Flags().StringVar(&shellName, "shell", "", "the shell to print for")
// Hide flags that are deprecated or for internal use only.
_ = debugCmd.Flags().MarkHidden("shell")
return debugCmd
}

View file

@ -30,7 +30,15 @@ var (
"xonsh",
}
initCmd = &cobra.Command{
initCmd = createInitCmd()
)
func init() {
RootCmd.AddCommand(initCmd)
}
func createInitCmd() *cobra.Command {
initCmd := &cobra.Command{
Use: "init [bash|zsh|fish|powershell|pwsh|cmd|nu|tcsh|elvish|xonsh]",
Short: "Initialize your shell and config",
Long: `Initialize your shell and config.
@ -46,15 +54,20 @@ See the documentation to initialize your shell: https://ohmyposh.dev/docs/instal
runInit(args[0])
},
}
)
func init() {
initCmd.Flags().BoolVarP(&printOutput, "print", "p", false, "print the init script")
initCmd.Flags().BoolVarP(&strict, "strict", "s", false, "run in strict mode")
initCmd.Flags().BoolVarP(&manual, "manual", "m", false, "enable/disable manual mode")
initCmd.Flags().BoolVar(&debug, "debug", false, "enable/disable debug mode")
// Deprecated flags, should be kept to avoid breaking CLI integration.
initCmd.Flags().BoolVarP(&manual, "manual", "m", false, "enable/disable manual mode")
// Hide flags that are deprecated or for internal use only.
_ = initCmd.Flags().MarkHidden("manual")
_ = initCmd.MarkPersistentFlagRequired("config")
RootCmd.AddCommand(initCmd)
return initCmd
}
func runInit(shellName string) {
@ -68,7 +81,6 @@ func runInit(shellName string) {
Shell: shellName,
Config: configFlag,
Strict: strict,
Manual: manual,
Debug: debug,
},
}

View file

@ -21,6 +21,7 @@ var (
cleared bool
cached bool
jobCount int
saveCache bool
command string
shellVersion string
@ -30,74 +31,81 @@ var (
)
// printCmd represents the prompt command
var printCmd = &cobra.Command{
Use: "print [debug|primary|secondary|transient|right|tooltip|valid|error]",
Short: "Print the prompt/context",
Long: "Print one of the prompts based on the location/use-case.",
ValidArgs: []string{
"debug",
"primary",
"secondary",
"transient",
"right",
"tooltip",
"valid",
"error",
},
Args: NoArgsOrOneValidArg,
Run: func(cmd *cobra.Command, args []string) {
if len(args) == 0 {
_ = cmd.Help()
return
}
flags := &runtime.Flags{
Config: configFlag,
PWD: pwd,
PSWD: pswd,
ErrorCode: status,
PipeStatus: pipestatus,
ExecutionTime: timing,
StackCount: stackCount,
TerminalWidth: terminalWidth,
Eval: eval,
Shell: shellName,
ShellVersion: shellVersion,
Plain: plain,
Primary: args[0] == "primary",
Cleared: cleared,
NoExitCode: noStatus,
Column: column,
JobCount: jobCount,
}
eng := prompt.New(flags)
defer eng.Env.Close()
switch args[0] {
case "debug":
fmt.Print(eng.ExtraPrompt(prompt.Debug))
case "primary":
fmt.Print(eng.Primary())
case "secondary":
fmt.Print(eng.ExtraPrompt(prompt.Secondary))
case "transient":
fmt.Print(eng.ExtraPrompt(prompt.Transient))
case "right":
fmt.Print(eng.RPrompt())
case "tooltip":
fmt.Print(eng.Tooltip(command))
case "valid":
fmt.Print(eng.ExtraPrompt(prompt.Valid))
case "error":
fmt.Print(eng.ExtraPrompt(prompt.Error))
default:
_ = cmd.Help()
}
},
}
var printCmd = createPrintCmd()
func init() {
RootCmd.AddCommand(printCmd)
}
func createPrintCmd() *cobra.Command {
printCmd := &cobra.Command{
Use: "print [debug|primary|secondary|transient|right|tooltip|valid|error]",
Short: "Print the prompt/context",
Long: "Print one of the prompts based on the location/use-case.",
ValidArgs: []string{
"debug",
"primary",
"secondary",
"transient",
"right",
"tooltip",
"valid",
"error",
},
Args: NoArgsOrOneValidArg,
Run: func(cmd *cobra.Command, args []string) {
if len(args) == 0 {
_ = cmd.Help()
return
}
flags := &runtime.Flags{
Config: configFlag,
PWD: pwd,
PSWD: pswd,
ErrorCode: status,
PipeStatus: pipestatus,
ExecutionTime: timing,
StackCount: stackCount,
TerminalWidth: terminalWidth,
Eval: eval,
Shell: shellName,
ShellVersion: shellVersion,
Plain: plain,
Primary: args[0] == "primary",
Cleared: cleared,
NoExitCode: noStatus,
Column: column,
JobCount: jobCount,
SaveCache: saveCache,
}
eng := prompt.New(flags)
defer eng.Env.Close()
switch args[0] {
case "debug":
fmt.Print(eng.ExtraPrompt(prompt.Debug))
case "primary":
fmt.Print(eng.Primary())
case "secondary":
fmt.Print(eng.ExtraPrompt(prompt.Secondary))
case "transient":
fmt.Print(eng.ExtraPrompt(prompt.Transient))
case "right":
fmt.Print(eng.RPrompt())
case "tooltip":
fmt.Print(eng.Tooltip(command))
case "valid":
fmt.Print(eng.ExtraPrompt(prompt.Valid))
case "error":
fmt.Print(eng.ExtraPrompt(prompt.Error))
default:
_ = cmd.Help()
}
},
}
printCmd.Flags().StringVar(&pwd, "pwd", "", "current working directory")
printCmd.Flags().StringVar(&pswd, "pswd", "", "current working directory (according to pwsh)")
printCmd.Flags().StringVar(&shellName, "shell", "", "the shell to print for")
@ -114,11 +122,18 @@ func init() {
printCmd.Flags().BoolVar(&eval, "eval", false, "output the prompt for eval")
printCmd.Flags().IntVar(&column, "column", 0, "the column position of the cursor")
printCmd.Flags().IntVar(&jobCount, "job-count", 0, "number of background jobs")
printCmd.Flags().BoolVar(&saveCache, "save-cache", false, "save updated cache to file")
// Deprecated flags, keep to not break CLI integration
// Deprecated flags, should be kept to avoid breaking CLI integration.
printCmd.Flags().IntVarP(&status, "error", "e", 0, "last exit code")
printCmd.Flags().BoolVar(&noStatus, "no-exit-code", false, "no valid exit code (cancelled or no command yet)")
printCmd.Flags().BoolVar(&cached, "cached", false, "use a cached prompt")
RootCmd.AddCommand(printCmd)
// Hide flags that are deprecated or for internal use only.
_ = printCmd.Flags().MarkHidden("error")
_ = printCmd.Flags().MarkHidden("no-exit-code")
_ = printCmd.Flags().MarkHidden("cached")
_ = printCmd.Flags().MarkHidden("save-cache")
return printCmd
}

View file

@ -6,10 +6,11 @@ import (
// promptCmd represents the prompt command
var promptCmd = &cobra.Command{
Use: "prompt",
Short: "Set up the prompt for your shell (deprecated)",
Long: `Set up the prompt for your shell. (deprecated)`,
Args: cobra.NoArgs,
Use: "prompt",
Short: "Set up the prompt for your shell (deprecated)",
Long: `Set up the prompt for your shell. (deprecated)`,
Hidden: true,
Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, _ []string) {
_ = cmd.Help()
},
@ -17,8 +18,8 @@ var promptCmd = &cobra.Command{
func init() {
// legacy support
promptCmd.AddCommand(initCmd)
promptCmd.AddCommand(debugCmd)
promptCmd.AddCommand(printCmd)
promptCmd.AddCommand(createInitCmd())
promptCmd.AddCommand(createDebugCmd())
promptCmd.AddCommand(createPrintCmd())
RootCmd.AddCommand(promptCmd)
}

View file

@ -49,7 +49,13 @@ var (
func init() {
RootCmd.PersistentFlags().StringVarP(&configFlag, "config", "c", "", "config file path")
RootCmd.Flags().BoolVarP(&initialize, "init", "i", false, "init (deprecated)")
RootCmd.Flags().BoolVar(&displayVersion, "version", false, "version")
RootCmd.Flags().StringVarP(&shellName, "shell", "s", "", "shell (deprecated)")
// Deprecated flags, should be kept to avoid breaking CLI integration.
RootCmd.Flags().BoolVarP(&initialize, "init", "i", false, "init")
RootCmd.Flags().StringVarP(&shellName, "shell", "s", "", "shell")
// Hide flags that are deprecated or for internal use only.
_ = RootCmd.Flags().MarkHidden("init")
_ = RootCmd.Flags().MarkHidden("shell")
}

View file

@ -108,17 +108,19 @@ func (e *Engine) pwd() {
}
func (e *Engine) getNewline() string {
// WARP terminal will remove \n from the prompt, so we hack a newline in.
// For Elvish, we do this to prevent cutting off a right-aligned block.
if e.isWarp() || e.Env.Shell() == shell.ELVISH {
if e.Plain || e.Env.Flags().Debug {
return "\n"
}
// Warp terminal will remove a newline character ('\n') from the prompt, so we hack it in.
// For Elvish on Windows, we do this to prevent cutting off a right-aligned block.
if e.isWarp() || (e.Env.Shell() == shell.ELVISH && e.Env.GOOS() == runtime.WINDOWS) {
return terminal.LineBreak()
}
// TCSH needs a space before the LITERAL newline character or it will not render correctly
// don't ask why, it be like that sometimes.
// https://unix.stackexchange.com/questions/99101/properly-defining-a-multi-line-prompt-in-tcsh#comment1342462_322189
// To avoid improper translations in Tcsh, we use an invisible word joiner character (U+2060) to separate a newline from possible preceding escape sequences.
if e.Env.Shell() == shell.TCSH {
return ` \n`
return "\u2060\\n"
}
return "\n"
@ -506,6 +508,17 @@ func (e *Engine) adjustTrailingDiamondColorOverrides() {
}
}
func (e *Engine) rectifyTerminalWidth(diff int) {
// Since the terminal width may not be given by the CLI flag, we should always call this here.
_, err := e.Env.TerminalWidth()
if err != nil {
// Skip when we're unable to determine the terminal width.
return
}
e.Env.Flags().TerminalWidth += diff
}
// New returns a prompt engine initialized with the
// given configuration options, and is ready to print any
// of the prompt components.
@ -532,9 +545,16 @@ func New(flags *runtime.Flags) *Engine {
}
switch env.Shell() {
case shell.ELVISH:
// In Elvish, a prompt line will always wrap when the cursor reaches the rightmost cell on the terminal screen.
// We have to reduce the terminal width by 1 so a right-aligned block will not be broken.
case shell.XONSH:
// In Xonsh, the behavior of wrapping at the end of a prompt line is inconsistent across platforms.
// On Windows, it wraps before the rightmost cell on the terminal screen, that is, the rightmost cell is never available for a prompt line.
if eng.Env.GOOS() == runtime.WINDOWS {
eng.rectifyTerminalWidth(-1)
}
case shell.TCSH, shell.ELVISH:
// In Tcsh, newlines in a prompt are badly translated.
// No silver bullet here. We have to reduce the terminal width by 1 so a right-aligned block will not be broken.
// In Elvish, the behavior is similar to that in Xonsh, but we do this for all platforms.
eng.rectifyTerminalWidth(-1)
case shell.PWSH, shell.PWSH5:
// when in PowerShell, and force patching the bleed bug
@ -548,14 +568,3 @@ func New(flags *runtime.Flags) *Engine {
return eng
}
func (e *Engine) rectifyTerminalWidth(diff int) {
// Since the terminal width may not be given by the CLI flag, we should always call this here.
_, err := e.Env.TerminalWidth()
if err != nil {
// Skip when we're unable to determine the terminal width.
return
}
e.Env.Flags().TerminalWidth += diff
}

View file

@ -101,23 +101,22 @@ func (e *Engine) ExtraPrompt(promptType ExtraPromptType) string {
switch e.Env.Shell() {
case shell.ZSH:
if promptType == Transient {
if !e.Env.Flags().Eval {
break
}
prompt := fmt.Sprintf("PS1=%s", shell.QuotePosixStr(str))
// empty RPROMPT
prompt += "\nRPROMPT=''"
return prompt
}
return str
case shell.PWSH, shell.PWSH5:
if promptType == Transient {
// clear the line afterwards to prevent text from being written on the same line
// see https://github.com/JanDeDobbeleer/oh-my-posh/issues/3628
return str + terminal.ClearAfter()
}
return str
case shell.CMD, shell.BASH, shell.FISH, shell.NU, shell.GENERIC:
return str
}
return ""
return str
}

View file

@ -2,6 +2,8 @@ package prompt
import (
"github.com/jandedobbeleer/oh-my-posh/src/config"
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
"github.com/jandedobbeleer/oh-my-posh/src/shell"
)
func (e *Engine) RPrompt() string {
@ -29,5 +31,10 @@ func (e *Engine) RPrompt() string {
text, length := e.renderBlockSegments(rprompt)
e.rpromptLength = length
if e.Env.Shell() == shell.ELVISH && e.Env.GOOS() != runtime.WINDOWS {
// Workaround to align with a right-aligned block on non-Windows systems.
text += " "
}
return text
}

View file

@ -68,7 +68,6 @@ type Environment interface {
Connection(connectionType ConnectionType) (*Connection, error)
TemplateCache() *cache.Template
LoadTemplateCache()
SetPromptCount()
CursorPosition() (row, col int)
SystemInfo() (*SystemInfo, error)
Debug(message string)
@ -92,7 +91,6 @@ type Flags struct {
TerminalWidth int
Strict bool
Debug bool
Manual bool
Plain bool
Primary bool
HasTransient bool
@ -101,6 +99,7 @@ type Flags struct {
NoExitCode bool
Column int
JobCount int
SaveCache bool
}
type CommandError struct {

View file

@ -281,10 +281,6 @@ func (env *Environment) DirIsWritable(path string) bool {
return args.Bool(0)
}
func (env *Environment) SetPromptCount() {
_ = env.Called()
}
func (env *Environment) CursorPosition() (int, int) {
args := env.Called()
return args.Int(0), args.Int(1)

View file

@ -67,12 +67,13 @@ func (term *Terminal) Init() {
initCache := func(fileName string) *cache.File {
cache := &cache.File{}
cache.Init(filepath.Join(term.CachePath(), fileName))
cache.Init(filepath.Join(term.CachePath(), fileName), term.CmdFlags.SaveCache)
return cache
}
term.deviceCache = initCache(cache.FileName)
term.sessionCache = initCache(cache.SessionFileName)
term.setPromptCount()
term.ResolveConfigPath()
@ -81,8 +82,6 @@ func (term *Terminal) Init() {
}
term.tmplCache = &cache.Template{}
term.SetPromptCount()
}
func (term *Terminal) ResolveConfigPath() {
@ -737,27 +736,20 @@ func dirMatchesOneOf(dir, home, goos string, regexes []string) bool {
return false
}
func (term *Terminal) SetPromptCount() {
func (term *Terminal) setPromptCount() {
defer term.Trace(time.Now())
countStr := os.Getenv("POSH_PROMPT_COUNT")
if len(countStr) > 0 {
// this counter is incremented by the shell
count, err := strconv.Atoi(countStr)
if err == nil {
term.CmdFlags.PromptCount = count
return
}
}
var count int
if val, found := term.Session().Get(cache.PROMPTCOUNTCACHE); found {
count, _ = strconv.Atoi(val)
}
// only write to cache if we're the primary prompt
// Only update the count if we're generating a primary prompt.
if term.CmdFlags.Primary {
count++
term.Session().Set(cache.PROMPTCOUNTCACHE, strconv.Itoa(count), 1440)
}
term.CmdFlags.PromptCount = count
}

View file

@ -2,7 +2,6 @@ package shell
import (
_ "embed"
"fmt"
"strings"
)
@ -13,13 +12,13 @@ var bashInit string
func (f Feature) Bash() Code {
switch f {
case CursorPositioning:
return "_omp_cursor_positioning=1"
return unixCursorPositioning
case FTCSMarks:
return "_omp_ftcs_marks=1"
return unixFTCSMarks
case Upgrade:
return `"$_omp_executable" upgrade`
return unixUpgrade
case Notice:
return `"$_omp_executable" notice`
return unixNotice
case PromptMark, RPrompt, PoshGit, Azure, LineError, Jobs, Tooltips, Transient:
fallthrough
default:
@ -32,42 +31,5 @@ func QuotePosixStr(str string) string {
return "''"
}
needQuoting := false
var b strings.Builder
for _, r := range str {
normal := false
switch r {
case '!', ';', '"', '(', ')', '[', ']', '{', '}', '$', '|', '&', '>', '<', '`', ' ', '#', '~', '*', '?', '=':
b.WriteRune(r)
case '\\', '\'':
b.WriteByte('\\')
b.WriteRune(r)
case '\a':
b.WriteString(`\a`)
case '\b':
b.WriteString(`\b`)
case '\f':
b.WriteString(`\f`)
case '\n':
b.WriteString(`\n`)
case '\r':
b.WriteString(`\r`)
case '\t':
b.WriteString(`\t`)
case '\v':
b.WriteString(`\v`)
default:
b.WriteRune(r)
normal = true
}
if !normal {
needQuoting = true
}
}
// the quoting form $'...' is used for a string contains any special characters
if needQuoting {
return fmt.Sprintf("$'%s'", b.String())
}
return b.String()
return fmt.Sprintf("$'%s'", strings.NewReplacer(`\`, `\\`, "'", `\'`).Replace(str))
}

View file

@ -1,6 +1,7 @@
package shell
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
@ -17,3 +18,17 @@ _omp_cursor_positioning=1`
assert.Equal(t, want, got)
}
func TestQuotePosixStr(t *testing.T) {
tests := []struct {
str string
expected string
}{
{str: "", expected: "''"},
{str: `/tmp/"omp's dir"/oh-my-posh`, expected: `$'/tmp/"omp\'s dir"/oh-my-posh'`},
{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, QuotePosixStr(tc.str), fmt.Sprintf("QuotePosixStr: %s", tc.str))
}
}

View file

@ -2,8 +2,6 @@ package shell
import (
_ "embed"
"fmt"
"strings"
)
@ -16,23 +14,32 @@ func (f Feature) Cmd() Code {
return "transient_enabled = true"
case RPrompt:
return "rprompt_enabled = true"
case FTCSMarks:
return "ftcs_marks_enabled = true"
case Tooltips:
return "enable_tooltips()"
case Upgrade:
return "os.execute(string.format('%s upgrade', omp_exe()))"
return `os.execute(string.format('"%s" upgrade', omp_executable))`
case Notice:
return "os.execute(string.format('%s notice', omp_exe()))"
case PromptMark, PoshGit, Azure, LineError, Jobs, FTCSMarks, CursorPositioning:
return `os.execute(string.format('"%s" notice', omp_executable))`
case PromptMark, PoshGit, Azure, LineError, Jobs, CursorPositioning:
fallthrough
default:
return ""
}
}
func quoteLuaStr(str string) string {
func escapeLuaStr(str string) string {
if len(str) == 0 {
return "''"
return str
}
return fmt.Sprintf("'%s'", strings.NewReplacer(`\`, `\\`, `'`, `\'`).Replace(str))
// We only replace a minimal set of special characters with corresponding escape sequences, without adding surrounding quotes.
// That way the result can be later quoted with either single or double quotes in a Lua script.
return strings.NewReplacer(
`\`, `\\`,
"'", `\'`,
`"`, `\"`,
"\n", `\n`,
"\r", `\r`,
).Replace(str)
}

View file

@ -1,6 +1,7 @@
package shell
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
@ -12,9 +13,24 @@ func TestCmdFeatures(t *testing.T) {
want := `// these are the features
enable_tooltips()
transient_enabled = true
os.execute(string.format('%s upgrade', omp_exe()))
os.execute(string.format('%s notice', omp_exe()))
ftcs_marks_enabled = true
os.execute(string.format('"%s" upgrade', omp_executable))
os.execute(string.format('"%s" notice', omp_executable))
rprompt_enabled = true`
assert.Equal(t, want, got)
}
func TestEscapeLuaStr(t *testing.T) {
tests := []struct {
str string
expected string
}{
{str: "", expected: ""},
{str: `/tmp/"omp's dir"/oh-my-posh`, expected: `/tmp/\"omp\'s dir\"/oh-my-posh`},
{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, escapeLuaStr(tc.str), fmt.Sprintf("escapeLuaStr: %s", tc.str))
}
}

View file

@ -4,6 +4,13 @@ import "strings"
type Code string
const (
unixFTCSMarks Code = "_omp_ftcs_marks=1"
unixCursorPositioning Code = "_omp_cursor_positioning=1"
unixUpgrade Code = `"$_omp_executable" upgrade`
unixNotice Code = `"$_omp_executable" notice`
)
func (c Code) Indent(spaces int) Code {
return Code(strings.Repeat(" ", spaces) + string(c))
}

View file

@ -10,9 +10,9 @@ var elvishInit string
func (f Feature) Elvish() Code {
switch f {
case Upgrade:
return unixUpgrade
return "$_omp_executable upgrade"
case Notice:
return unixNotice
return "$_omp_executable notice"
case PromptMark, RPrompt, PoshGit, Azure, LineError, Jobs, CursorPositioning, Tooltips, Transient, FTCSMarks:
fallthrough
default:

View file

@ -2,7 +2,6 @@ package shell
import (
_ "embed"
"fmt"
"strings"
)
@ -35,27 +34,6 @@ func quoteFishStr(str string) string {
if len(str) == 0 {
return "''"
}
needQuoting := false
var b strings.Builder
for _, r := range str {
normal := false
switch r {
case ';', '"', '(', ')', '[', ']', '{', '}', '$', '|', '&', '>', '<', ' ', '#', '~', '*', '?', '=':
b.WriteRune(r)
case '\\', '\'':
b.WriteByte('\\')
b.WriteRune(r)
default:
b.WriteRune(r)
normal = true
}
if !normal {
needQuoting = true
}
}
// single quotes are used when the string contains any special characters
if needQuoting {
return fmt.Sprintf("'%s'", b.String())
}
return b.String()
return fmt.Sprintf("'%s'", strings.NewReplacer(`\`, `\\`, "'", `\'`).Replace(str))
}

View file

@ -1,6 +1,7 @@
package shell
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
@ -13,9 +14,23 @@ func TestFishFeatures(t *testing.T) {
enable_poshtooltips
set --global _omp_transient_prompt 1
set --global _omp_ftcs_marks 1
$_omp_executable upgrade
$_omp_executable notice
"$_omp_executable" upgrade
"$_omp_executable" notice
set --global _omp_prompt_mark 1`
assert.Equal(t, want, got)
}
func TestQuoteFishStr(t *testing.T) {
tests := []struct {
str string
expected string
}{
{str: "", expected: "''"},
{str: `/tmp/"omp's dir"/oh-my-posh`, expected: `'/tmp/"omp\'s dir"/oh-my-posh'`},
{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, quoteFishStr(tc.str), fmt.Sprintf("quoteFishStr: %s", tc.str))
}
}

View file

@ -73,6 +73,9 @@ func GetFormats(shell string) *Formats {
ITermPromptMark: "%{$(iterm2_prompt_mark)%}",
ITermCurrentDir: "%%{\x1b]1337;CurrentDir=%s\x07%%}",
ITermRemoteHost: "%%{\x1b]1337;RemoteHost=%s@%s\x07%%}",
EscapeSequences: map[rune]string{
'%': "%%",
},
}
default:
formats = &Formats{
@ -98,10 +101,9 @@ func GetFormats(shell string) *Formats {
}
}
if shell == ZSH {
formats.EscapeSequences = map[rune]string{
'%': "%%",
}
if shell == TCSH {
formats.EscapeSequences['\\'] = `\\`
formats.EscapeSequences['!'] = `\!`
}
return formats

View file

@ -49,10 +49,6 @@ func Init(env runtime.Environment, feats Features) string {
additionalParams += " --strict"
}
if env.Flags().Manual {
additionalParams += " --manual"
}
var command, config string
switch shell {
@ -72,7 +68,7 @@ func Init(env runtime.Environment, feats Features) string {
createNuInit(env, feats)
return ""
default:
return fmt.Sprintf("echo \"No initialization script available for %s\"", shell)
return fmt.Sprintf(`echo "%s is not supported by Oh My Posh"`, shell)
}
}
@ -105,22 +101,24 @@ func PrintInit(env runtime.Environment, features Features, startTime *time.Time)
configFile = quoteFishStr(configFile)
script = fishInit
case CMD:
executable = quoteLuaStr(executable)
configFile = quoteLuaStr(configFile)
executable = escapeLuaStr(executable)
configFile = escapeLuaStr(configFile)
script = cmdInit
case NU:
executable = quoteNuStr(executable)
configFile = quoteNuStr(configFile)
script = nuInit
case TCSH:
executable = QuotePosixStr(executable)
configFile = QuotePosixStr(configFile)
executable = quoteCshStr(executable)
configFile = quoteCshStr(configFile)
script = tcshInit
case ELVISH:
executable = quotePwshOrElvishStr(executable)
configFile = quotePwshOrElvishStr(configFile)
script = elvishInit
case XONSH:
executable = quotePythonStr(executable)
configFile = quotePythonStr(configFile)
script = xonshInit
default:
return fmt.Sprintf("echo \"No initialization script available for %s\"", shell)

View file

@ -1,88 +0,0 @@
package shell
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
)
func TestQuotePwshOrElvishStr(t *testing.T) {
tests := []struct {
str string
expected string
}{
{str: ``, expected: `''`},
{str: `/tmp/oh-my-posh`, expected: `'/tmp/oh-my-posh'`},
{str: `/tmp/omp's dir/oh-my-posh`, expected: `'/tmp/omp''s dir/oh-my-posh'`},
{str: `C:\tmp\oh-my-posh.exe`, expected: `'C:\tmp\oh-my-posh.exe'`},
{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, quotePwshOrElvishStr(tc.str), fmt.Sprintf("quotePwshStr: %s", tc.str))
}
}
func TestQuotePosixStr(t *testing.T) {
tests := []struct {
str string
expected string
}{
{str: ``, expected: `''`},
{str: `/tmp/oh-my-posh`, expected: `/tmp/oh-my-posh`},
{str: `/tmp/omp's dir/oh-my-posh`, expected: `$'/tmp/omp\'s dir/oh-my-posh'`},
{str: `C:/tmp/oh-my-posh.exe`, expected: `C:/tmp/oh-my-posh.exe`},
{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, QuotePosixStr(tc.str), fmt.Sprintf("quotePosixStr: %s", tc.str))
}
}
func TestQuoteFishStr(t *testing.T) {
tests := []struct {
str string
expected string
}{
{str: ``, expected: `''`},
{str: `/tmp/oh-my-posh`, expected: `/tmp/oh-my-posh`},
{str: `/tmp/omp's dir/oh-my-posh`, expected: `'/tmp/omp\'s dir/oh-my-posh'`},
{str: `C:/tmp/oh-my-posh.exe`, expected: `C:/tmp/oh-my-posh.exe`},
{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, quoteFishStr(tc.str), fmt.Sprintf("quoteFishStr: %s", tc.str))
}
}
func TestQuoteLuaStr(t *testing.T) {
tests := []struct {
str string
expected string
}{
{str: ``, expected: `''`},
{str: `/tmp/oh-my-posh`, expected: `'/tmp/oh-my-posh'`},
{str: `/tmp/omp's dir/oh-my-posh`, expected: `'/tmp/omp\'s dir/oh-my-posh'`},
{str: `C:/tmp/oh-my-posh.exe`, expected: `'C:/tmp/oh-my-posh.exe'`},
{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, quoteLuaStr(tc.str), fmt.Sprintf("quoteLuaStr: %s", tc.str))
}
}
func TestQuoteNuStr(t *testing.T) {
tests := []struct {
str string
expected string
}{
{str: ``, expected: `''`},
{str: `/tmp/oh-my-posh`, expected: `"/tmp/oh-my-posh"`},
{str: `/tmp/omp's dir/oh-my-posh`, expected: `"/tmp/omp's dir/oh-my-posh"`},
{str: `C:/tmp/oh-my-posh.exe`, expected: `"C:/tmp/oh-my-posh.exe"`},
{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, quoteNuStr(tc.str), fmt.Sprintf("quoteNuStr: %s", tc.str))
}
}

View file

@ -2,7 +2,6 @@ package shell
import (
_ "embed"
"fmt"
"os"
"path/filepath"
@ -17,7 +16,7 @@ var nuInit string
func (f Feature) Nu() Code {
switch f {
case Transient:
return `$env.TRANSIENT_PROMPT_COMMAND = { ^$_omp_executable print transient $"--config=($env.POSH_THEME)" --shell=nu $"--shell-version=($env.POSH_SHELL_VERSION)" $"--execution-time=(posh_cmd_duration)" $"--status=($env.LAST_EXIT_CODE)" $"--terminal-width=(posh_width)" }` //nolint: lll
return `$env.TRANSIENT_PROMPT_COMMAND = {|| _omp_get_prompt transient }`
case Upgrade:
return "^$_omp_executable upgrade"
case Notice:
@ -33,12 +32,13 @@ func quoteNuStr(str string) string {
if len(str) == 0 {
return "''"
}
return fmt.Sprintf(`"%s"`, strings.NewReplacer(`\`, `\\`, `"`, `\"`).Replace(str))
}
func createNuInit(env runtime.Environment, features Features) {
initPath := filepath.Join(env.Home(), ".oh-my-posh.nu")
f, err := os.OpenFile(initPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
f, err := os.OpenFile(initPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o755)
if err != nil {
return
}

View file

@ -1,6 +1,7 @@
package shell
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
@ -9,11 +10,24 @@ import (
func TestNuFeatures(t *testing.T) {
got := allFeatures.Lines(NU).String("// these are the features")
//nolint: lll
want := `// these are the features
$env.TRANSIENT_PROMPT_COMMAND = { ^$_omp_executable print transient $"--config=($env.POSH_THEME)" --shell=nu $"--shell-version=($env.POSH_SHELL_VERSION)" $"--execution-time=(posh_cmd_duration)" $"--status=($env.LAST_EXIT_CODE)" $"--terminal-width=(posh_width)" }
$env.TRANSIENT_PROMPT_COMMAND = {|| _omp_get_prompt transient }
^$_omp_executable upgrade
^$_omp_executable notice`
assert.Equal(t, want, got)
}
func TestQuoteNuStr(t *testing.T) {
tests := []struct {
str string
expected string
}{
{str: "", expected: "''"},
{str: `/tmp/"omp's dir"/oh-my-posh`, expected: `"/tmp/\"omp's dir\"/oh-my-posh"`},
{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, quoteNuStr(tc.str), fmt.Sprintf("quoteNuStr: %s", tc.str))
}
}

View file

@ -2,7 +2,6 @@ package shell
import (
_ "embed"
"fmt"
"strings"
)
@ -38,5 +37,9 @@ func (f Feature) Pwsh() Code {
}
func quotePwshOrElvishStr(str string) string {
if len(str) == 0 {
return "''"
}
return fmt.Sprintf("'%s'", strings.ReplaceAll(str, "'", "''"))
}

View file

@ -1,6 +1,7 @@
package shell
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
@ -24,3 +25,17 @@ $global:_ompFTCSMarks = $true
assert.Equal(t, want, got)
}
func TestQuotePwshOrElvishStr(t *testing.T) {
tests := []struct {
str string
expected string
}{
{str: "", expected: "''"},
{str: `/tmp/"omp's dir"/oh-my-posh`, expected: `'/tmp/"omp''s dir"/oh-my-posh'`},
{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, quotePwshOrElvishStr(tc.str), fmt.Sprintf("quotePwshOrElvishStr: %s", tc.str))
}
}

View file

@ -1,6 +1,6 @@
export POSH_THEME=::CONFIG::
export POSH_SHELL_VERSION=$BASH_VERSION
export POWERLINE_COMMAND="oh-my-posh"
export POWERLINE_COMMAND='oh-my-posh'
export POSH_PID=$$
export CONDA_PROMPT_MODIFIER=false
export OSTYPE=$OSTYPE
@ -10,12 +10,12 @@ if [[ $OSTYPE =~ ^(msys|cygwin) ]]; then
fi
# global variables
_omp_start_time=""
_omp_start_time=''
_omp_stack_count=0
_omp_elapsed=-1
_omp_no_exit_code="true"
_omp_status_cache=0
_omp_pipestatus_cache=0
_omp_execution_time=-1
_omp_no_status=true
_omp_status=0
_omp_pipestatus=0
_omp_executable=::OMP::
# switches to enable/disable features
@ -26,7 +26,11 @@ _omp_ftcs_marks=0
PS0='${_omp_start_time:0:$((_omp_start_time="$(_omp_start_timer)",0))}$(_omp_ftcs_command_start)'
# set secondary prompt
_omp_secondary_prompt=$("$_omp_executable" print secondary --shell=bash --shell-version="$BASH_VERSION")
_omp_secondary_prompt=$(
"$_omp_executable" print secondary \
--shell=bash \
--shell-version="$BASH_VERSION"
)
function _omp_set_cursor_position() {
# not supported in Midnight Commander
@ -54,7 +58,7 @@ function _omp_start_timer() {
function _omp_ftcs_command_start() {
if [[ $_omp_ftcs_marks == 1 ]]; then
printf "\e]133;C\a"
printf '\e]133;C\a'
fi
}
@ -63,26 +67,37 @@ function set_poshcontext() {
return
}
function _omp_print_primary() {
# Avoid unexpected expansions.
function _omp_get_primary() {
# Avoid unexpected expansions when we're generating the prompt below.
shopt -u promptvars
trap 'shopt -s promptvars' RETURN
local prompt
if shopt -oq posix; then
# Disable in POSIX mode.
prompt='[NOTICE: Oh My Posh prompt is not supported in POSIX mode]\n\u@\h:\w\$ '
else
prompt=$("$_omp_executable" print primary --shell=bash --shell-version="$BASH_VERSION" --status="$_omp_status_cache" --pipestatus="${_omp_pipestatus_cache[*]}" --execution-time="$_omp_elapsed" --stack-count="$_omp_stack_count" --no-status="$_omp_no_exit_code" --terminal-width="${COLUMNS-0}" | tr -d '\0')
prompt=$(
"$_omp_executable" print primary \
--save-cache \
--shell=bash \
--shell-version="$BASH_VERSION" \
--status="$_omp_status" \
--pipestatus="${_omp_pipestatus[*]}" \
--no-status="$_omp_no_status" \
--execution-time="$_omp_execution_time" \
--stack-count="$_omp_stack_count" \
--terminal-width="${COLUMNS-0}" |
tr -d '\0'
)
fi
echo "${prompt@P}"
# Allow command substitution in PS0.
shopt -s promptvars
}
function _omp_print_secondary() {
# Avoid unexpected expansions.
function _omp_get_secondary() {
# Avoid unexpected expansions when we're generating the prompt below.
shopt -u promptvars
trap 'shopt -s promptvars' RETURN
if shopt -oq posix; then
# Disable in POSIX mode.
@ -90,38 +105,39 @@ function _omp_print_secondary() {
else
echo "${_omp_secondary_prompt@P}"
fi
# Allow command substitution in PS0.
shopt -s promptvars
}
function _omp_hook() {
_omp_status_cache=$? _omp_pipestatus_cache=("${PIPESTATUS[@]}")
_omp_status=$? _omp_pipestatus=("${PIPESTATUS[@]}")
if [[ ${#BP_PIPESTATUS[@]} -ge ${#_omp_pipestatus_cache[@]} ]]; then
_omp_pipestatus_cache=("${BP_PIPESTATUS[@]}")
if [[ ${#BP_PIPESTATUS[@]} -ge ${#_omp_pipestatus[@]} ]]; then
_omp_pipestatus=("${BP_PIPESTATUS[@]}")
fi
_omp_stack_count=$((${#DIRSTACK[@]} - 1))
_omp_execution_time=-1
if [[ $_omp_start_time ]]; then
local omp_now=$("$_omp_executable" get millis --shell=bash)
_omp_elapsed=$((omp_now - _omp_start_time))
_omp_start_time=""
_omp_no_exit_code="false"
local omp_now=$("$_omp_executable" get millis)
_omp_execution_time=$((omp_now - _omp_start_time))
_omp_no_status=false
fi
_omp_start_time=''
if [[ ${_omp_pipestatus_cache[-1]} != "$_omp_status_cache" ]]; then
_omp_pipestatus_cache=("$_omp_status_cache")
if [[ ${_omp_pipestatus[-1]} != "$_omp_status" ]]; then
_omp_pipestatus=("$_omp_status")
fi
set_poshcontext
_omp_set_cursor_position
PS1='$(_omp_print_primary)'
PS2='$(_omp_print_secondary)'
PS1='$(_omp_get_primary)'
PS2='$(_omp_get_secondary)'
return $_omp_status_cache
# Ensure that command substitution works in a prompt string.
shopt -s promptvars
return $_omp_status
}
function _omp_install_hook() {
@ -129,7 +145,7 @@ function _omp_install_hook() {
local cmd
for cmd in "${PROMPT_COMMAND[@]}"; do
if [[ $cmd = "_omp_hook" ]]; then
if [[ $cmd = _omp_hook ]]; then
return
fi
done

View file

@ -1,33 +1,55 @@
set-env POSH_PID (to-string (randint 10000000000000 10000000000000000))
set-env POSH_THEME ::CONFIG::
set-env POSH_SHELL_VERSION (elvish --version)
set-env POWERLINE_COMMAND 'oh-my-posh'
set-env POSH_SHELL_VERSION $version
set-env POWERLINE_COMMAND oh-my-posh
var _omp_error_code = 0
var _omp_executable = ::OMP::
var _omp_executable = (external ::OMP::)
var _omp_status = 0
var _omp_no_status = 1
var _omp_execution_time = -1
var _omp_terminal_width = ($_omp_executable get width)
fn _omp-after-readline-hook {|_|
set _omp_execution_time = -1
# Getting the terminal width can fail inside a prompt function, so we do this here.
set _omp_terminal_width = ($_omp_executable get width)
}
fn _omp-after-command-hook {|m|
# The command execution time should not be available in the first prompt.
if (== $_omp_no_status 0) {
set _omp_execution_time = (printf %.0f (* $m[duration] 1000))
}
set _omp_no_status = 0
fn posh-after-command-hook {|m|
var error = $m[error]
if (is $error $nil) {
set _omp_error_code = 0
set _omp_status = 0
} else {
try {
set _omp_error_code = $error[reason][exit-status]
set _omp_status = $error[reason][exit-status]
} catch {
# built-in commands don't have a status code.
set _omp_error_code = 1
set _omp_status = 1
}
}
}
set edit:after-command = [ $@edit:after-command $posh-after-command-hook~ ]
set edit:prompt = {
var cmd-duration = (printf "%.0f" (* $edit:command-duration 1000))
(external $_omp_executable) print primary --shell=elvish --execution-time=$cmd-duration --status=$_omp_error_code --pwd=$pwd --shell-version=$E:POSH_SHELL_VERSION
fn _omp_get_prompt {|type @arguments|
$_omp_executable print $type ^
--save-cache ^
--shell=elvish ^
--shell-version=$E:POSH_SHELL_VERSION ^
--status=$_omp_status ^
--no-status=$_omp_no_status ^
--execution-time=$_omp_execution_time ^
--terminal-width=$_omp_terminal_width ^
$@arguments
}
set edit:rprompt = {
var cmd-duration = (printf "%.0f" (* $edit:command-duration 1000))
(external $_omp_executable) print right --shell=elvish --execution-time=$cmd-duration --status=$_omp_error_code --pwd=$pwd --shell-version=$E:POSH_SHELL_VERSION
}
set edit:after-readline = [ $@edit:after-readline $_omp-after-readline-hook~ ]
set edit:after-command = [ $@edit:after-command $_omp-after-command-hook~ ]
set edit:prompt = {|| _omp_get_prompt primary }
set edit:rprompt = {|| _omp_get_prompt right }

View file

@ -24,42 +24,58 @@ function set_poshcontext
return
end
function _omp_get_prompt
if test (count $argv) -eq 0
return
end
$_omp_executable print $argv[1] \
--save-cache \
--shell=fish \
--shell-version=$FISH_VERSION \
--status=$_omp_status \
--pipestatus="$_omp_pipestatus" \
--no-status=$_omp_no_status \
--execution-time=$_omp_execution_time \
--stack-count=$_omp_stack_count \
$argv[2..]
end
# NOTE: Input function calls via `commandline --function` are put into a queue and will not be executed until an outer regular function returns. See https://fishshell.com/docs/current/cmds/commandline.html.
function fish_prompt
set --local omp_status_cache_temp $status
set --local omp_pipestatus_cache_temp $pipestatus
set --local omp_status_temp $status
set --local omp_pipestatus_temp $pipestatus
# clear from cursor to end of screen as
# commandline --function repaint does not do this
# see https://github.com/fish-shell/fish-shell/issues/8418
printf \e\[0J
if test "$_omp_transient" = 1
$_omp_executable print transient --shell fish --status $_omp_status_cache --pipestatus="$_omp_pipestatus_cache" --execution-time $_omp_duration --stack-count $_omp_stack_count --shell-version $FISH_VERSION --no-status=$_omp_no_exit_code
_omp_get_prompt transient
return
end
if test "$_omp_new_prompt" = 0
echo -n "$_omp_current_prompt"
return
end
set --global _omp_status_cache $omp_status_cache_temp
set --global _omp_pipestatus_cache $omp_pipestatus_cache_temp
set --global _omp_status $omp_status_temp
set --global _omp_pipestatus $omp_pipestatus_temp
set --global _omp_no_status false
set --global _omp_execution_time "$CMD_DURATION$cmd_duration"
set --global _omp_stack_count (count $dirstack)
set --global _omp_duration "$CMD_DURATION$cmd_duration"
set --global _omp_no_exit_code false
# check if variable set, < 3.2 case
if set --query _omp_last_command && test -z "$_omp_last_command"
set _omp_duration 0
set _omp_no_exit_code true
set _omp_execution_time 0
set _omp_no_status true
end
# works with fish >=3.2
if set --query _omp_last_status_generation && test "$_omp_last_status_generation" = "$status_generation"
set _omp_duration 0
set _omp_no_exit_code true
set _omp_execution_time 0
set _omp_no_status true
else if test -z "$_omp_last_status_generation"
# first execution - $status_generation is 0, $_omp_last_status_generation is empty
set _omp_no_exit_code true
set _omp_no_status true
end
if set --query status_generation
@ -81,7 +97,8 @@ function fish_prompt
end
# The prompt is saved for possible reuse, typically a repaint after clearing the screen buffer.
set --global _omp_current_prompt ($_omp_executable print primary --shell fish --status $_omp_status_cache --pipestatus="$_omp_pipestatus_cache" --execution-time $_omp_duration --stack-count $_omp_stack_count --shell-version $FISH_VERSION --cleared=$omp_cleared --no-status=$_omp_no_exit_code | string collect)
set --global _omp_current_prompt (_omp_get_prompt primary --cleared=$omp_cleared | string join \n | string collect)
echo -n "$_omp_current_prompt"
end
@ -98,7 +115,7 @@ function fish_right_prompt
end
set _omp_new_prompt 0
set --global _omp_current_rprompt ($_omp_executable print right --shell fish --status $_omp_status_cache --pipestatus="$_omp_pipestatus_cache" --execution-time $_omp_duration --stack-count $_omp_stack_count --shell-version $FISH_VERSION --no-status=$_omp_no_exit_code | string join '')
set --global _omp_current_rprompt (_omp_get_prompt right | string join '')
echo -n "$_omp_current_rprompt"
end
@ -154,7 +171,7 @@ function _omp_space_key_handler
end
set _omp_tooltip_command $tooltip_command
set --local tooltip_prompt ($_omp_executable print tooltip --shell fish --status $_omp_status_cache --pipestatus="$_omp_pipestatus_cache" --execution-time $_omp_duration --stack-count $_omp_stack_count --shell-version $FISH_VERSION --command $_omp_tooltip_command --no-status=$_omp_no_exit_code | string join '')
set --local tooltip_prompt (_omp_get_prompt tooltip --command=$_omp_tooltip_command | string join '')
if test -z "$tooltip_prompt"
return

View file

@ -1,34 +1,24 @@
-- Upgrade notice
local notice = [[::UPGRADENOTICE::]]
if '::UPGRADE::' == 'true' then
print(notice)
end
---@diagnostic disable: undefined-global
---@diagnostic disable: undefined-field
---@diagnostic disable: lowercase-global
-- Cache PID
os.setenv("POSH_PID", os.getpid())
os.setenv('POSH_PID', os.getpid())
-- Helper functions
local function get_priority_number(name, default)
local value = os.getenv(name)
if os.envmap ~= nil and type(os.envmap) == 'table' then
local t = os.envmap[name]
value = (t ~= nil and type(t) == 'string') and t or value
end
if type(default) == 'number' then
value = tonumber(value)
if value == nil then
return default
else
return value
end
else
return default
end
local value = os.getenv(name)
if value == nil and os.envmap ~= nil and type(os.envmap) == 'table' then
value = os.envmap[name]
end
local num = tonumber(value)
if num ~= nil then
return num
end
return default
end
os.setenv("POSH_CURSOR_LINE", console.getnumlines())
-- Environment variables
local function environment_onbeginedit()
@ -41,6 +31,7 @@ local endedit_time = 0
local last_duration = 0
local rprompt_enabled = false
local transient_enabled = false
local ftcs_marks_enabled = false
local no_exit_code = true
local cached_prompt = {}
@ -57,7 +48,7 @@ local function cache_onbeginedit()
local old_cache = cached_prompt
-- Start a new table for the new edit/prompt session.
cached_prompt = { cwd=cwd }
cached_prompt = { cwd = cwd }
-- Copy the cached left/right prompt strings if the cwd hasn't changed.
-- IMPORTANT OPTIMIZATION: This keeps the prompt highly responsive, except
@ -68,33 +59,30 @@ local function cache_onbeginedit()
end
end
-- Executable
local omp_executable = '::OMP::'
-- Configuration
local function omp_exe()
return '"'..::OMP::..'"'
end
os.setenv("POSH_THEME", ::CONFIG::)
os.setenv("POSH_SHELL_VERSION", string.format('clink v%s.%s.%s.%s', clink.version_major, clink.version_minor, clink.version_patch, clink.version_commit))
os.setenv('POSH_THEME', '::CONFIG::')
os.setenv('POSH_SHELL_VERSION', string.format('clink v%s.%s.%s.%s', clink.version_major, clink.version_minor, clink.version_patch, clink.version_commit))
-- Execution helpers
local function can_async()
if (clink.version_encoded or 0) >= 10030001 then
return settings.get("prompt.async")
return settings.get('prompt.async')
end
end
local function run_posh_command(command)
command = '"'..command..'"'
local _, ismain = coroutine.running()
local output
if ismain then
output = io.popen(command):read("*a")
else
output = io.popenyield(command):read("*a")
command = string.format('""%s" %s"', omp_executable, command)
local _, is_main = coroutine.running()
if is_main then
return io.popen(command):read('*a')
end
return output
return io.popenyield(command):read('*a')
end
-- Duration functions
@ -105,10 +93,8 @@ local function os_clock_millis()
-- OMP to get the time in milliseconds.
if (clink.version_encoded or 0) >= 10020030 then
return math.floor(os.clock() * 1000)
else
local prompt_exe = string.format('%s get millis --shell=cmd', omp_exe())
return run_posh_command(prompt_exe)
end
return run_posh_command('get millis')
end
local function duration_onbeginedit()
@ -125,7 +111,7 @@ end
local function duration_onendedit(input)
endedit_time = 0
-- For an empty command, the execution time should not be evaluated.
if string.gsub(input, "^%s*(.-)%s*$", "%1") ~= "" then
if string.gsub(input, '^%s*(.-)%s*$', '%1') ~= '' then
endedit_time = os_clock_millis()
end
end
@ -134,43 +120,48 @@ end
local function execution_time_option()
if last_duration ~= nil then
return "--execution-time "..last_duration
return '--execution-time=' .. last_duration
end
return ""
return ''
end
local function error_level_option()
if os.geterrorlevel ~= nil and settings.get("cmd.get_errorlevel") then
return "--status "..os.geterrorlevel()
local function status_option()
if os.geterrorlevel ~= nil and settings.get('cmd.get_errorlevel') then
return '--status=' .. os.geterrorlevel()
end
return ""
return ''
end
local function no_exit_code_option()
local function no_status_option()
if no_exit_code then
return "--no-status"
return '--no-status'
end
return ""
return ''
end
local function get_posh_prompt(rprompt)
local prompt = "primary"
if rprompt then
prompt = "right"
end
local prompt_exe = string.format('%s print %s --shell=cmd %s %s %s', omp_exe(), prompt, execution_time_option(), error_level_option(), no_exit_code_option())
return run_posh_command(prompt_exe)
local function get_posh_prompt(prompt_type, ...)
os.setenv('POSH_CURSOR_LINE', console.getnumlines())
local command = table.concat({
'print',
prompt_type,
'--save-cache',
'--shell=cmd',
status_option(),
no_status_option(),
execution_time_option(),
...
}, ' ')
return run_posh_command(command)
end
local function set_posh_tooltip(tip_command)
if tip_command ~= "" and tip_command ~= cached_prompt.tip_command then
if tip_command ~= '' and tip_command ~= cached_prompt.tip_command then
-- Escape special characters properly, if any.
local escaped_tip_command = string.gsub(tip_command, '(\\+)"', '%1%1"'):gsub('(\\+)$', '%1%1'):gsub('"', '\\"'):gsub('([&<>%(%)@%^|])', '^%1')
local prompt_exe = string.format('%s print tooltip --shell=cmd %s --command="%s"', omp_exe(), error_level_option(), escaped_tip_command)
local tooltip = run_posh_command(prompt_exe)
local escaped_tip_command = string.gsub(tip_command, '(\\+)"', '%1%1"'):gsub('(\\+)$', '%1%1'):gsub('"', '\\"'):gsub('([&<>%(%)@|%^])', '^%1'):gsub('%%', '%%%%')
local command_option = string.format('--command "%s"', escaped_tip_command)
local tooltip = get_posh_prompt('tooltip', command_option)
-- Do not cache an empty tooltip.
if tooltip == "" then
if tooltip == '' then
return
end
cached_prompt.tip_command = tip_command
@ -185,25 +176,10 @@ local function display_cached_prompt()
cached_prompt.only_use_cache = nil
end
local function async_collect_posh_prompts()
-- Generate the left prompt.
cached_prompt.left = get_posh_prompt(false)
-- Generate the right prompt, if needed.
if rprompt_enabled then
display_cached_prompt() -- Show left side; don't wait for right side.
cached_prompt.right = get_posh_prompt(true)
end
end
local function command_executed_mark(input)
if string.gsub(input, "^%s*(.-)%s*$", "%1") ~= "" then
no_exit_code = false
else
no_exit_code = true
end
if "::FTCS_MARKS::" == "true" then
clink.print("\x1b]133;C\007", NONL)
no_exit_code = string.gsub(input, '^%s*(.-)%s*$', '%1') == ''
if ftcs_marks_enabled then
clink.print('\x1b]133;C\007', NONL)
end
end
@ -216,7 +192,7 @@ function p:filter(prompt)
-- Get a left prompt immediately if nothing is available yet.
if not cached_prompt.left then
cached_prompt.left = get_posh_prompt(false)
cached_prompt.left = get_posh_prompt('primary')
need_left = false
end
@ -228,10 +204,10 @@ function p:filter(prompt)
-- function was defined. That way if a new prompt starts (which
-- discards the old coroutine) and a new coroutine starts, the old
-- coroutine won't stomp on the new cached_prompt table.
clink.promptcoroutine(function ()
clink.promptcoroutine(function()
-- Generate left prompt, if needed.
if need_left then
cached_prompt.left = get_posh_prompt(false)
cached_prompt.left = get_posh_prompt('primary')
end
-- Generate right prompt, if needed.
if rprompt_enabled then
@ -239,17 +215,17 @@ function p:filter(prompt)
-- Show left side while right side is being generated.
display_cached_prompt()
end
cached_prompt.right = get_posh_prompt(true)
cached_prompt.right = get_posh_prompt('right')
else
cached_prompt.right = nil
end
end)
else
if need_left then
cached_prompt.left = get_posh_prompt(false)
cached_prompt.left = get_posh_prompt('primary')
end
if rprompt_enabled then
cached_prompt.right = get_posh_prompt(true)
cached_prompt.right = get_posh_prompt('right')
end
end
end
@ -269,10 +245,9 @@ function p:transientfilter(prompt)
return nil
end
local prompt_exe = string.format('%s print transient --shell=cmd %s %s', omp_exe(), error_level_option(), no_exit_code_option())
prompt = run_posh_command(prompt_exe)
prompt = get_posh_prompt('transient')
if prompt == "" then
if prompt == '' then
prompt = nil
end
@ -280,7 +255,7 @@ function p:transientfilter(prompt)
end
function p:transientrightfilter(prompt)
return "", false
return '', false
end
-- Event handlers
@ -303,12 +278,12 @@ end
-- Tooltips
function ohmyposh_space(rl_buffer)
function _omp_space_keybinding(rl_buffer)
-- Insert space first, in case it might affect the tip word, e.g. it could
-- split "gitcommit" into "git commit".
rl_buffer:insert(" ")
rl_buffer:insert(' ')
-- Get the first word of command line as tip.
local tip_command = rl_buffer:getbuffer():gsub("^%s*([^%s]*).*$", "%1")
local tip_command = rl_buffer:getbuffer():gsub('^%s*(.-)%s*$', '%1')
-- Generate a tooltip asynchronously (via coroutine) if available, otherwise
-- generate a tooltip immediately.
@ -318,7 +293,7 @@ function ohmyposh_space(rl_buffer)
elseif cached_prompt.coroutine then
-- No action needed; a tooltip coroutine is already running.
else
cached_prompt.coroutine = coroutine.create(function ()
cached_prompt.coroutine = coroutine.create(function()
set_posh_tooltip(tip_command)
if cached_prompt.coroutine == coroutine.running() then
cached_prompt.coroutine = nil
@ -333,5 +308,5 @@ local function enable_tooltips()
return
end
rl.setbinding(' ', [["luafunc:ohmyposh_space"]], 'emacs')
rl.setbinding(' ', [["luafunc:_omp_space_keybinding"]], 'emacs')
end

View file

@ -4,28 +4,48 @@ if ($env.config? | is-not-empty) {
}
$env.POWERLINE_COMMAND = 'oh-my-posh'
$env.POSH_THEME = ::CONFIG::
$env.POSH_THEME = (echo ::CONFIG::)
$env.PROMPT_INDICATOR = ""
$env.POSH_PID = (random uuid)
$env.POSH_SHELL_VERSION = (version | get version)
let _omp_executable: string = ::OMP::
def posh_cmd_duration [] {
# We have to do this because the initial value of `$env.CMD_DURATION_MS` is always `0823`,
# which is an official setting.
# See https://github.com/nushell/nushell/discussions/6402#discussioncomment-3466687.
if $env.CMD_DURATION_MS == "0823" { 0 } else { $env.CMD_DURATION_MS }
}
def posh_width [] {
(term size).columns | into string
}
let _omp_executable: string = (echo ::OMP::)
# PROMPTS
$env.PROMPT_MULTILINE_INDICATOR = (^$_omp_executable print secondary --shell=nu $"--shell-version=($env.POSH_SHELL_VERSION)")
$env.PROMPT_COMMAND = { ||
def --wrapped _omp_get_prompt [
type: string,
...args: string
] {
mut execution_time = -1
mut no_status = true
# We have to do this because the initial value of `$env.CMD_DURATION_MS` is always `0823`, which is an official setting.
# See https://github.com/nushell/nushell/discussions/6402#discussioncomment-3466687.
if $env.CMD_DURATION_MS != '0823' {
$execution_time = $env.CMD_DURATION_MS
$no_status = false
}
(
^$_omp_executable print $type
--save-cache
--shell=nu
$"--shell-version=($env.POSH_SHELL_VERSION)"
$"--status=($env.LAST_EXIT_CODE)"
$"--no-status=($no_status)"
$"--execution-time=($execution_time)"
$"--terminal-width=((term size).columns)"
...$args
)
}
$env.PROMPT_MULTILINE_INDICATOR = (
^$_omp_executable print secondary
--shell=nu
$"--shell-version=($env.POSH_SHELL_VERSION)"
)
$env.PROMPT_COMMAND = {||
# hack to set the cursor line to 1 when the user clears the screen
# this obviously isn't bulletproof, but it's a start
mut clear = false
@ -37,9 +57,7 @@ $env.PROMPT_COMMAND = { ||
do --env $env.SET_POSHCONTEXT
}
^$_omp_executable print primary --shell=nu $"--shell-version=($env.POSH_SHELL_VERSION)" $"--execution-time=(posh_cmd_duration)" $"--status=($env.LAST_EXIT_CODE)" $"--terminal-width=(posh_width)" $"--cleared=($clear)"
_omp_get_prompt primary $"--cleared=($clear)"
}
$env.PROMPT_COMMAND_RIGHT = { ||
^$_omp_executable print right --shell=nu $"--shell-version=($env.POSH_SHELL_VERSION)" $"--execution-time=(posh_cmd_duration)" $"--status=($env.LAST_EXIT_CODE)" $"--terminal-width=(posh_width)"
}
$env.PROMPT_COMMAND_RIGHT = {|| _omp_get_prompt right }

View file

@ -48,28 +48,24 @@ New-Module -Name "oh-my-posh-core" -ScriptBlock {
$env:POSH_THEME = (Resolve-Path -Path ::CONFIG::).ProviderPath
}
function Start-Utf8Process {
param(
[string]$FileName,
[string[]]$Arguments = @()
)
function Invoke-Utf8Posh {
param([string[]]$Arguments = @())
if ($script:ConstrainedLanguageMode) {
$standardOut = Invoke-Expression "& `$FileName `$Arguments 2>&1"
$standardOut -join "`n"
$output = Invoke-Expression "& `$global:_ompExecutable `$Arguments 2>&1"
$output -join "`n"
return
}
$Process = New-Object System.Diagnostics.Process
$StartInfo = $Process.StartInfo
$StartInfo.FileName = $FileName
$StartInfo.FileName = $global:_ompExecutable
if ($StartInfo.ArgumentList.Add) {
# ArgumentList is supported in PowerShell 6.1 and later (built on .NET Core 2.1+)
# ref-1: https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.processstartinfo.argumentlist?view=net-6.0
# ref-2: https://docs.microsoft.com/en-us/powershell/scripting/whats-new/differences-from-windows-powershell?view=powershell-7.2#net-framework-vs-net-core
$Arguments | ForEach-Object -Process { $StartInfo.ArgumentList.Add($_) }
}
else {
} else {
# escape arguments manually in lower versions, refer to https://docs.microsoft.com/en-us/previous-versions/17w5ykft(v=vs.85)
$escapedArgs = $Arguments | ForEach-Object {
# escape N consecutive backslash(es), which are followed by a double quote, to 2N consecutive ones
@ -113,8 +109,6 @@ New-Module -Name "oh-my-posh-core" -ScriptBlock {
$stdoutTask.Result
}
function Set-PoshContext([bool]$originalStatus) {}
function Get-NonFSWD {
# We only need to return a non-filesystem working directory.
if ($PWD.Provider.Name -ne 'FileSystem') {
@ -131,6 +125,192 @@ New-Module -Name "oh-my-posh-core" -ScriptBlock {
$terminalWidth
}
function Get-FileHyperlink {
param(
[Parameter(Mandatory, ValuefromPipeline = $True)]
[string]$Uri,
[Parameter(ValuefromPipeline = $True)]
[string]$Name
)
if (!$Name) {
# if name not set, uri is used as the name of the hyperlink
$Name = $Uri
}
if ($null -ne $env:WSL_DISTRO_NAME) {
# wsl conversion if needed
$Uri = &wslpath -m $Uri
}
# return an ANSI formatted hyperlink
return "`e]8;;file://$Uri`e\$Name`e]8;;`e\"
}
function Set-TransientPrompt {
$previousOutputEncoding = [Console]::OutputEncoding
try {
$script:TransientPrompt = $true
[Console]::OutputEncoding = [Text.Encoding]::UTF8
[Microsoft.PowerShell.PSConsoleReadLine]::InvokePrompt()
} finally {
[Console]::OutputEncoding = $previousOutputEncoding
}
}
function Set-PoshPromptType {
if ($script:TransientPrompt -eq $true) {
$script:PromptType = "transient"
$script:TransientPrompt = $false
return
}
# for details about the trick to detect a debugging context, see these comments:
# 1) https://github.com/JanDeDobbeleer/oh-my-posh/issues/2483#issuecomment-1175761456
# 2) https://github.com/JanDeDobbeleer/oh-my-posh/issues/2502#issuecomment-1179968052
# 3) https://github.com/JanDeDobbeleer/oh-my-posh/issues/5153
if ($Host.Runspace.Debugger.InBreakpoint) {
$script:PromptType = "debug"
return
}
$script:PromptType = "primary"
if ($global:_ompJobCount) {
$script:JobCount = (Get-Job -State Running).Count
}
if ($global:_ompAzure) {
try {
$env:POSH_AZURE_SUBSCRIPTION = Get-AzContext | ConvertTo-Json
} catch {}
}
if ($global:_ompPoshGit) {
try {
$global:GitStatus = Get-GitStatus
$env:POSH_GIT_STATUS = $global:GitStatus | ConvertTo-Json
} catch {}
}
}
function Update-PoshErrorCode {
$lastHistory = Get-History -ErrorAction Ignore -Count 1
# error code should be updated only when a non-empty command is run
if (($null -eq $lastHistory) -or ($script:LastHistoryId -eq $lastHistory.Id)) {
$script:ExecutionTime = 0
$script:NoExitCode = $true
return
}
$script:NoExitCode = $false
$script:LastHistoryId = $lastHistory.Id
$script:ExecutionTime = ($lastHistory.EndExecutionTime - $lastHistory.StartExecutionTime).TotalMilliseconds
if ($script:OriginalLastExecutionStatus) {
$script:ErrorCode = 0
return
}
$invocationInfo = try {
# retrieve info of the most recent error
$global:Error[0] | Where-Object { $_ -ne $null } | Select-Object -ExpandProperty InvocationInfo
} catch { $null }
# check if the last command caused the last error
if ($null -ne $invocationInfo -and $lastHistory.CommandLine -eq $invocationInfo.Line) {
$script:ErrorCode = 1
return
}
if ($script:OriginalLastExitCode -is [int] -and $script:OriginalLastExitCode -ne 0) {
# native app exit code
$script:ErrorCode = $script:OriginalLastExitCode
return
}
}
function Get-PoshPrompt {
param(
[string]$Type,
[string[]]$Arguments
)
$nonFSWD = Get-NonFSWD
$stackCount = Get-PoshStackCount
$terminalWidth = Get-TerminalWidth
Invoke-Utf8Posh @(
"print", $Type
"--save-cache"
"--shell=$script:ShellName"
"--shell-version=$script:PSVersion"
"--status=$script:ErrorCode"
"--no-status=$script:NoExitCode"
"--execution-time=$script:ExecutionTime"
"--pswd=$nonFSWD"
"--stack-count=$stackCount"
"--terminal-width=$terminalWidth"
"--job-count=$script:JobCount"
if ($Arguments) { $Arguments }
)
}
$promptFunction = {
# store the orignal last command execution status
if ($global:NVS_ORIGINAL_LASTEXECUTIONSTATUS -is [bool]) {
# make it compatible with NVS auto-switching, if enabled
$script:OriginalLastExecutionStatus = $global:NVS_ORIGINAL_LASTEXECUTIONSTATUS
} else {
$script:OriginalLastExecutionStatus = $?
}
# store the orignal last exit code
$script:OriginalLastExitCode = $global:LASTEXITCODE
Set-PoshPromptType
if ($script:PromptType -ne 'transient') {
Update-PoshErrorCode
}
Set-PoshContext $script:ErrorCode
# set the cursor positions, they are zero based so align with other platforms
$env:POSH_CURSOR_LINE = $Host.UI.RawUI.CursorPosition.Y + 1
$env:POSH_CURSOR_COLUMN = $Host.UI.RawUI.CursorPosition.X + 1
$output = Get-PoshPrompt $script:PromptType
# make sure PSReadLine knows if we have a multiline prompt
Set-PSReadLineOption -ExtraPromptLineCount (($output | Measure-Object -Line).Lines - 1)
# The output can be multi-line, joining them ensures proper rendering.
$output = $output -join "`n"
if ($script:PromptType -eq 'transient') {
# Workaround to prevent a command from eating the tail of a transient prompt, when we're at the end of the line.
$command = ''
[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$command, [ref]$null)
if ($command) {
$output += " `b`b"
}
}
$output
# remove any posh-git status
$env:POSH_GIT_STATUS = $null
# restore the orignal last exit code
$global:LASTEXITCODE = $script:OriginalLastExitCode
}
$Function:prompt = $promptFunction
# set secondary prompt
Set-PSReadLineOption -ContinuationPrompt ((Invoke-Utf8Posh @("print", "secondary", "--shell=$script:ShellName")) -join "`n")
### Exported Functions ###
function Set-PoshContext([bool]$originalStatus) {}
function Enable-PoshTooltips {
if ($script:ConstrainedLanguageMode) {
return
@ -151,35 +331,21 @@ New-Module -Name "oh-my-posh-core" -ScriptBlock {
}
$script:TooltipCommand = $command
$column = $Host.UI.RawUI.CursorPosition.X
$terminalWidth = Get-TerminalWidth
$nonFSWD = Get-NonFSWD
$stackCount = global:Get-PoshStackCount
$standardOut = (Start-Utf8Process $global:_ompExecutable @("print", "tooltip", "--status=$script:ErrorCode", "--shell=$script:ShellName", "--pswd=$nonFSWD", "--execution-time=$script:ExecutionTime", "--stack-count=$stackCount", "--command=$command", "--shell-version=$script:PSVersion", "--column=$column", "--terminal-width=$terminalWidth", "--no-status=$script:NoExitCode", "--job-count=$script:JobCount")) -join ''
if (!$standardOut) {
$output = (Get-PoshPrompt "tooltip" @(
"--column=$($Host.UI.RawUI.CursorPosition.X)"
"--command=$command"
)) -join ''
if (!$output) {
return
}
Write-Host $standardOut -NoNewline
Write-Host $output -NoNewline
# Workaround to prevent the text after cursor from disappearing when the tooltip is printed.
[Microsoft.PowerShell.PSConsoleReadLine]::Insert(' ')
[Microsoft.PowerShell.PSConsoleReadLine]::Undo()
}
finally {}
}
}
function Set-TransientPrompt {
$previousOutputEncoding = [Console]::OutputEncoding
try {
$script:TransientPrompt = $true
[Console]::OutputEncoding = [Text.Encoding]::UTF8
[Microsoft.PowerShell.PSConsoleReadLine]::InvokePrompt()
}
finally {
[Console]::OutputEncoding = $previousOutputEncoding
} finally {}
}
}
@ -197,8 +363,7 @@ New-Module -Name "oh-my-posh-core" -ScriptBlock {
$script:TooltipCommand = ''
Set-TransientPrompt
}
}
finally {
} finally {
[Microsoft.PowerShell.PSConsoleReadLine]::AcceptLine()
if ($global:_ompFTCSMarks -and $executingCommand) {
# Write FTCS_COMMAND_EXECUTED after accepting the input - it should still happen before execution
@ -216,16 +381,15 @@ New-Module -Name "oh-my-posh-core" -ScriptBlock {
$script:TooltipCommand = ''
Set-TransientPrompt
}
}
finally {
} finally {
[Microsoft.PowerShell.PSConsoleReadLine]::CopyOrCancelLine()
}
}
}
function Enable-PoshLineError {
$validLine = (Start-Utf8Process $global:_ompExecutable @("print", "valid", "--shell=$script:ShellName")) -join "`n"
$errorLine = (Start-Utf8Process $global:_ompExecutable @("print", "error", "--shell=$script:ShellName")) -join "`n"
$validLine = (Invoke-Utf8Posh @("print", "valid", "--shell=$script:ShellName")) -join "`n"
$errorLine = (Invoke-Utf8Posh @("print", "error", "--shell=$script:ShellName")) -join "`n"
Set-PSReadLineOption -PromptText $validLine, $errorLine
}
@ -264,40 +428,24 @@ New-Module -Name "oh-my-posh-core" -ScriptBlock {
$Format = 'json'
)
$configString = Start-Utf8Process $global:_ompExecutable @("config", "export", "--format=$Format")
# if no path, copy to clipboard by default
if ($FilePath) {
# https://stackoverflow.com/questions/3038337/powershell-resolve-path-that-might-not-exist
$FilePath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($FilePath)
[IO.File]::WriteAllLines($FilePath, $configString)
}
else {
Set-Clipboard $configString
Write-Output "Theme copied to clipboard"
}
}
function Get-FileHyperlink {
param(
[Parameter(Mandatory, ValuefromPipeline = $True)]
[string]$uri,
[Parameter(ValuefromPipeline = $True)]
[string]$name
$output = Invoke-Utf8Posh @(
"config", "export"
"--format=$Format"
"--output=$FilePath"
)
$esc = [char]27
if (!$name) {
# if name not set, uri is used as the name of the hyperlink
$name = $uri
if (!$output) {
Write-Host "Theme exported to $(Get-FileHyperlink $FilePath)."
return
}
if ($null -ne $env:WSL_DISTRO_NAME) {
# wsl conversion if needed
$uri = &wslpath -m $uri
}
# return an ANSI formatted hyperlink
return "$esc]8;;file://$uri$esc\$name$esc]8;;$esc\"
# When no path is provided, copy the output to clipboard.
Set-Clipboard $output
Write-Host 'Theme copied to clipboard.'
}
function Get-PoshThemes {
@ -329,19 +477,28 @@ New-Module -Name "oh-my-posh-core" -ScriptBlock {
Write-Host $logo
$themes = Get-ChildItem -Path "$Path/*" -Include '*.omp.json' | Sort-Object Name
if ($List -eq $true) {
$themes | Select-Object @{ Name = 'hyperlink'; Expression = { Get-FileHyperlink -uri $_.FullName } } | Format-Table -HideTableHeaders
}
else {
$themes | Select-Object @{ Name = 'hyperlink'; Expression = { Get-FileHyperlink -Uri $_.FullName } } | Format-Table -HideTableHeaders
} else {
$nonFSWD = Get-NonFSWD
$stackCount = Get-PoshStackCount
$terminalWidth = Get-TerminalWidth
$themes | ForEach-Object -Process {
Write-Host "Theme: $(Get-FileHyperlink -uri $_.FullName -Name ($_.BaseName -replace '\.omp$', ''))`n"
Start-Utf8Process $global:_ompExecutable @("print", "primary", "--config=$($_.FullName)", "--pswd=$nonFSWD", "--shell=$script:ShellName")
Write-Host "Theme: $(Get-FileHyperlink -Uri $_.FullName -Name ($_.BaseName -replace '\.omp$', ''))`n"
Invoke-Utf8Posh @(
"print", "primary"
"--config=$($_.FullName)"
"--shell=$script:ShellName"
"--shell-version=$script:PSVersion"
"--pswd=$nonFSWD"
"--stack-count=$stackCount"
"--terminal-width=$terminalWidth"
)
Write-Host "`n"
}
}
Write-Host @"
Themes location: $(Get-FileHyperlink -uri "$Path")
Themes location: $(Get-FileHyperlink -Uri "$Path")
To change your theme, adjust the init script in $PROFILE.
Example:
@ -350,139 +507,6 @@ Example:
"@
}
function Set-PoshPromptType {
if ($script:TransientPrompt -eq $true) {
$script:PromptType = "transient"
$script:TransientPrompt = $false
return
}
# for details about the trick to detect a debugging context, see these comments:
# 1) https://github.com/JanDeDobbeleer/oh-my-posh/issues/2483#issuecomment-1175761456
# 2) https://github.com/JanDeDobbeleer/oh-my-posh/issues/2502#issuecomment-1179968052
# 3) https://github.com/JanDeDobbeleer/oh-my-posh/issues/5153
if ($Host.Runspace.Debugger.InBreakpoint) {
$script:PromptType = "debug"
return
}
$script:PromptType = "primary"
if ($global:_ompJobCount) {
$script:JobCount = (Get-Job -State Running).Count
}
if ($global:_ompAzure) {
try {
$env:POSH_AZURE_SUBSCRIPTION = Get-AzContext | ConvertTo-Json
}
catch {}
}
if ($global:_ompPoshGit) {
try {
$global:GitStatus = Get-GitStatus
$env:POSH_GIT_STATUS = $global:GitStatus | ConvertTo-Json
}
catch {}
}
}
function Update-PoshErrorCode {
$lastHistory = Get-History -ErrorAction Ignore -Count 1
# error code should be updated only when a non-empty command is run
if (($null -eq $lastHistory) -or ($script:LastHistoryId -eq $lastHistory.Id)) {
$script:ExecutionTime = 0
$script:NoExitCode = $true
return
}
$script:NoExitCode = $false
$script:LastHistoryId = $lastHistory.Id
$script:ExecutionTime = ($lastHistory.EndExecutionTime - $lastHistory.StartExecutionTime).TotalMilliseconds
if ($script:OriginalLastExecutionStatus) {
$script:ErrorCode = 0
return
}
$invocationInfo = try {
# retrieve info of the most recent error
$global:Error[0] | Where-Object { $_ -ne $null } | Select-Object -ExpandProperty InvocationInfo
}
catch { $null }
# check if the last command caused the last error
if ($null -ne $invocationInfo -and $lastHistory.CommandLine -eq $invocationInfo.Line) {
$script:ErrorCode = 1
return
}
if ($script:OriginalLastExitCode -is [int] -and $script:OriginalLastExitCode -ne 0) {
# native app exit code
$script:ErrorCode = $script:OriginalLastExitCode
return
}
}
$promptFunction = {
# store the orignal last command execution status
if ($global:NVS_ORIGINAL_LASTEXECUTIONSTATUS -is [bool]) {
# make it compatible with NVS auto-switching, if enabled
$script:OriginalLastExecutionStatus = $global:NVS_ORIGINAL_LASTEXECUTIONSTATUS
}
else {
$script:OriginalLastExecutionStatus = $?
}
# store the orignal last exit code
$script:OriginalLastExitCode = $global:LASTEXITCODE
Set-PoshPromptType
if ($script:PromptType -ne 'transient') {
Update-PoshErrorCode
}
Set-PoshContext $script:ErrorCode
$nonFSWD = Get-NonFSWD
$stackCount = global:Get-PoshStackCount
$terminalWidth = Get-TerminalWidth
# set the cursor positions, they are zero based so align with other platforms
$env:POSH_CURSOR_LINE = $Host.UI.RawUI.CursorPosition.Y + 1
$env:POSH_CURSOR_COLUMN = $Host.UI.RawUI.CursorPosition.X + 1
$standardOut = Start-Utf8Process $global:_ompExecutable @("print", $script:PromptType, "--status=$script:ErrorCode", "--pswd=$nonFSWD", "--execution-time=$script:ExecutionTime", "--stack-count=$stackCount", "--shell-version=$script:PSVersion", "--terminal-width=$terminalWidth", "--shell=$script:ShellName", "--no-status=$script:NoExitCode", "--job-count=$script:JobCount")
# make sure PSReadLine knows if we have a multiline prompt
Set-PSReadLineOption -ExtraPromptLineCount (($standardOut | Measure-Object -Line).Lines - 1)
# The output can be multi-line, joining them ensures proper rendering.
$output = $standardOut -join "`n"
if ($script:PromptType -eq 'transient') {
# Workaround to prevent a command from eating the tail of a transient prompt, when we're at the end of the line.
$command = ''
[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$command, [ref]$null)
if ($command) {
$output += " `b`b"
}
}
$output
# remove any posh-git status
$env:POSH_GIT_STATUS = $null
# restore the orignal last exit code
$global:LASTEXITCODE = $script:OriginalLastExitCode
}
$Function:prompt = $promptFunction
# set secondary prompt
Set-PSReadLineOption -ContinuationPrompt ((Start-Utf8Process $global:_ompExecutable @("print", "secondary", "--shell=$script:ShellName")) -join "`n")
# perform cleanup on removal so a new initialization in current session works
if (!$script:ConstrainedLanguageMode) {
$ExecutionContext.SessionState.Module.OnRemove += {
@ -506,11 +530,6 @@ Example:
}
}
$notice = Start-Utf8Process $global:_ompExecutable @("notice")
if ($notice) {
Write-Host $notice -NoNewline
}
Export-ModuleMember -Function @(
"Set-PoshContext"
"Enable-PoshTooltips"
@ -518,7 +537,6 @@ Example:
"Enable-PoshLineError"
"Export-PoshTheme"
"Get-PoshThemes"
"Start-Utf8Process"
"prompt"
)
} | Import-Module -Global

View file

@ -1,25 +0,0 @@
import uuid
$POWERLINE_COMMAND = "oh-my-posh"
$POSH_THEME = r"::CONFIG::"
$POSH_PID = uuid.uuid4().hex
$POSH_SHELL_VERSION = $XONSH_VERSION
$POSH_EXECUTABLE = r"::OMP::"
def get_command_context():
last_cmd = __xonsh__.history[-1] if __xonsh__.history else None
status = last_cmd.rtn if last_cmd else 0
duration = round((last_cmd.ts[1] - last_cmd.ts[0]) * 1000) if last_cmd else 0
return status, duration
def posh_primary():
status, duration = get_command_context()
return $(@($POSH_EXECUTABLE) print primary --shell=xonsh --status=@(status) --execution-time=@(duration) --shell-version=@($POSH_SHELL_VERSION))
def posh_right():
status, duration = get_command_context()
return $(@($POSH_EXECUTABLE) print right --shell=xonsh --status=@(status) --execution-time=@(duration) --shell-version=@($POSH_SHELL_VERSION))
$PROMPT = posh_primary
$RIGHT_PROMPT = posh_right

View file

@ -1,20 +1,34 @@
setenv POWERLINE_COMMAND "oh-my-posh";
setenv POSH_THEME ::CONFIG::;
setenv POSH_SHELL_VERSION "";
setenv POSH_SHELL_VERSION "$tcsh";
setenv POSH_PID $$;
setenv OSTYPE "$OSTYPE";
if ("$OSTYPE" =~ {msys,cygwin}*) then
setenv POSH_PID "`cat /proc/$$/winpid`";
endif
if ( "$OSTYPE" =~ {msys,cygwin}* ) setenv POSH_PID "`cat /proc/$$/winpid`";
set POSH_COMMAND = ::OMP::;
set USER_PRECMD = "`alias precmd`";
set USER_POSTCMD = "`alias postcmd`";
set POSH_PRECMD = 'set POSH_CMD_STATUS = $status;set POSH_END_TIME = `$POSH_COMMAND get millis`;set POSH_DURATION = 0;if ( $POSH_START_TIME != -1 ) @ POSH_DURATION = $POSH_END_TIME - $POSH_START_TIME;set prompt = "`$POSH_COMMAND print primary --shell=tcsh --status=$POSH_CMD_STATUS --execution-time=$POSH_DURATION`";set POSH_START_TIME = -1';
set POSH_POSTCMD = 'set POSH_START_TIME = `$POSH_COMMAND get millis`';
if ( ! $?_omp_enabled ) alias precmd '
set _omp_status = $status;
set _omp_execution_time = -1;
set _omp_last_cmd = `echo $_:q`;
if ( $#_omp_last_cmd && $?_omp_cmd_executed ) @ _omp_execution_time = `"$_omp_executable" get millis` - $_omp_start_time;
unset _omp_last_cmd;
unset _omp_cmd_executed;
@ _omp_stack_count = $#dirstack - 1;
set prompt = "`$_omp_executable:q print primary
--save-cache
--shell=tcsh
--shell-version=$tcsh
--status=$_omp_status
--execution-time=$_omp_execution_time
--stack-count=$_omp_stack_count`";
'"`alias precmd`";
alias precmd "$POSH_PRECMD;$USER_PRECMD";
alias postcmd "$POSH_POSTCMD;$USER_POSTCMD";
if ( ! $?_omp_enabled ) alias postcmd '
set _omp_start_time = `"$_omp_executable" get millis`;
set _omp_cmd_executed;
'"`alias postcmd`";
set POSH_START_TIME = `$POSH_COMMAND get millis`;
set _omp_enabled;
set _omp_executable = ::OMP::;
set _omp_execution_time = -1;
set _omp_start_time = -1;

49
src/shell/scripts/omp.xsh Normal file
View file

@ -0,0 +1,49 @@
import uuid
$POWERLINE_COMMAND = "oh-my-posh"
$POSH_THEME = ::CONFIG::
$POSH_PID = uuid.uuid4().hex
$POSH_SHELL_VERSION = $XONSH_VERSION
_omp_executable = ::OMP::
_omp_history_length = 0
def _omp_get_context():
global _omp_history_length
status = 0
duration = -1
if __xonsh__.history:
last_cmd = __xonsh__.history[-1]
if last_cmd:
status = last_cmd.rtn
history_length = len(__xonsh__.history)
if history_length != _omp_history_length:
_omp_history_length = history_length
duration = round((last_cmd.ts[1] - last_cmd.ts[0]) * 1000)
return status, duration
def _omp_get_prompt(type: str, *args: str):
status, duration = _omp_get_context()
return $(
@(_omp_executable) print @(type) \
--save-cache \
--shell=xonsh \
--shell-version=$XONSH_VERSION \
--status=@(status) \
--execution-time=@(duration) \
@(args)
)
def _omp_get_primary():
return _omp_get_prompt('primary')
def _omp_get_right():
return _omp_get_prompt('right')
$PROMPT = _omp_get_primary
# When the primary prompt has multiple lines, the right prompt is always displayed on the first line, which is inconsistent with other supported shells.
# The behavior is controlled by Xonsh, and there is no way to change it.
$RIGHT_PROMPT = _omp_get_right

View file

@ -1,9 +1,8 @@
export POSH_THEME=::CONFIG::
export POSH_SHELL_VERSION=$ZSH_VERSION
export POSH_PID=$$
export POWERLINE_COMMAND="oh-my-posh"
export POWERLINE_COMMAND='oh-my-posh'
export CONDA_PROMPT_MODIFIER=false
export POSH_PROMPT_COUNT=0
export ZLE_RPROMPT_INDENT=0
export OSTYPE=$OSTYPE
@ -12,6 +11,7 @@ if [[ $OSTYPE =~ ^(msys|cygwin) ]]; then
fi
_omp_executable=::OMP::
_omp_tooltip_command=''
# switches to enable/disable features
_omp_cursor_positioning=0
@ -31,7 +31,7 @@ function _omp_set_cursor_position() {
stty raw -echo min 0
local pos
echo -en "\033[6n" >/dev/tty
echo -en '\033[6n' >/dev/tty
read -r -d R pos
pos=${pos:2} # strip off the esc-[
local parts=(${(s:;:)pos})
@ -49,32 +49,30 @@ function set_poshcontext() {
function _omp_preexec() {
if [[ $_omp_ftcs_marks == 0 ]]; then
printf "\033]133;C\007"
printf '\033]133;C\007'
fi
_omp_start_time=$($_omp_executable get millis)
}
function _omp_precmd() {
_omp_status_cache=$?
_omp_pipestatus_cache=(${pipestatus[@]})
_omp_status=$?
_omp_pipestatus=(${pipestatus[@]})
_omp_stack_count=${#dirstack[@]}
_omp_elapsed=-1
_omp_no_exit_code="true"
_omp_execution_time=-1
_omp_no_status=true
_omp_tooltip_command=''
if [ $_omp_start_time ]; then
local omp_now=$($_omp_executable get millis --shell=zsh)
_omp_elapsed=$(($omp_now - $_omp_start_time))
_omp_no_exit_code="false"
local omp_now=$($_omp_executable get millis)
_omp_execution_time=$(($omp_now - $_omp_start_time))
_omp_no_status=false
fi
if [[ ${_omp_pipestatus_cache[-1]} != "$_omp_status_cache" ]]; then
_omp_pipestatus_cache=("$_omp_status_cache")
if [[ ${_omp_pipestatus[-1]} != "$_omp_status" ]]; then
_omp_pipestatus=("$_omp_status")
fi
count=$((POSH_PROMPT_COUNT + 1))
export POSH_PROMPT_COUNT=$count
set_poshcontext
_omp_set_cursor_position
@ -86,8 +84,8 @@ function _omp_precmd() {
setopt PROMPT_PERCENT
PS2=$_omp_secondary_prompt
eval "$(_omp_get_prompt primary --eval)"
eval "$($_omp_executable print primary --status="$_omp_status_cache" --pipestatus="${_omp_pipestatus_cache[*]}" --execution-time="$_omp_elapsed" --stack-count="$_omp_stack_count" --eval --shell=zsh --shell-version="$ZSH_VERSION" --no-status="$_omp_no_exit_code")"
unset _omp_start_time
}
@ -116,6 +114,21 @@ function _omp_cleanup() {
_omp_cleanup
unset -f _omp_cleanup
function _omp_get_prompt() {
local type=$1
local args=("${@[2,-1]}")
$_omp_executable print $type \
--save-cache \
--shell=zsh \
--shell-version=$ZSH_VERSION \
--status=$_omp_status \
--pipestatus="${_omp_pipestatus[*]}" \
--no-status=$_omp_no_status \
--execution-time=$_omp_execution_time \
--stack-count=$_omp_stack_count \
${args[@]}
}
function _omp_render_tooltip() {
if [[ $KEYS != ' ' ]]; then
return
@ -130,7 +143,7 @@ function _omp_render_tooltip() {
fi
_omp_tooltip_command="$tooltip_command"
local tooltip=$($_omp_executable print tooltip --status="$_omp_status_cache" --pipestatus="${_omp_pipestatus_cache[*]}" --execution-time="$_omp_elapsed" --stack-count="$_omp_stack_count" --command="$tooltip_command" --shell=zsh --shell-version="$ZSH_VERSION" --no-status="$_omp_no_exit_code")
local tooltip=$(_omp_get_prompt tooltip --command="$tooltip_command")
if [[ -z $tooltip ]]; then
return
fi
@ -148,23 +161,21 @@ function _omp_zle-line-init() {
local -i ret=$?
(( $+zle_bracketed_paste )) && print -r -n - $zle_bracketed_paste[2]
_omp_tooltip_command=''
eval "$($_omp_executable print transient --status="$_omp_status_cache" --pipestatus="${_omp_pipestatus_cache[*]}" --execution-time="$_omp_elapsed" --stack-count="$_omp_stack_count" --eval --shell=zsh --shell-version="$ZSH_VERSION" --no-status="$_omp_no_exit_code")"
eval "$(_omp_get_prompt transient --eval)"
zle .reset-prompt
# Exit the shell if we receive EOT.
if [[ $ret == 0 && $KEYS == $'\4' ]]; then
exit
fi
if ((ret)); then
# TODO (fix): this is not equal to sending a SIGINT, since the status code ($?) is set to 1 instead of 130.
zle .send-break
else
# Enter
zle .accept-line
fi
return ret
# Exit the shell if we receive EOT.
if [[ $KEYS == $'\4' ]]; then
exit
fi
zle .accept-line
return $ret
}
# Helper function for calling a widget before the specified OMP function.
@ -205,7 +216,7 @@ function _omp_create_widget() {
}
function enable_poshtooltips() {
local widget=${$(bindkey " "):2}
local widget=${$(bindkey ' '):2}
if [[ -z $widget ]]; then
widget=self-insert

View file

@ -2,6 +2,8 @@ package shell
import (
_ "embed"
"fmt"
"strings"
)
//go:embed scripts/omp.tcsh
@ -10,12 +12,23 @@ var tcshInit string
func (f Feature) Tcsh() Code {
switch f {
case Upgrade:
return "$POSH_COMMAND upgrade;"
return `"$_omp_executable" upgrade;`
case Notice:
return "$POSH_COMMAND notice;"
return `"$_omp_executable" notice;`
case PromptMark, RPrompt, PoshGit, Azure, LineError, Jobs, Tooltips, Transient, FTCSMarks, CursorPositioning:
fallthrough
default:
return ""
}
}
func quoteCshStr(str string) string {
if len(str) == 0 {
return "''"
}
// An non-working edge case: there is no way to preserve a newline ('\n') in command substitution.
// Therefore, we can only get a limited string without newlines for "eval".
return fmt.Sprintf("'%s'", strings.NewReplacer("'", `'"'"'`,
"!", `\!`).Replace(str))
}

View file

@ -1,6 +1,7 @@
package shell
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
@ -10,8 +11,22 @@ func TestTcshFeatures(t *testing.T) {
got := allFeatures.Lines(TCSH).String("// these are the features")
want := `// these are the features
$POSH_COMMAND upgrade;
$POSH_COMMAND notice;`
"$_omp_executable" upgrade;
"$_omp_executable" notice;`
assert.Equal(t, want, got)
}
func TestQuoteCshStr(t *testing.T) {
tests := []struct {
str string
expected string
}{
{str: "", expected: "''"},
{str: `/tmp/"omp's dir"!/oh-my-posh`, expected: `'/tmp/"omp'"'"'s dir"\!/oh-my-posh'`},
{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, quoteCshStr(tc.str), fmt.Sprintf("quoteCshStr: %s", tc.str))
}
}

View file

@ -2,20 +2,34 @@ package shell
import (
_ "embed"
"fmt"
"strings"
)
//go:embed scripts/omp.py
//go:embed scripts/omp.xsh
var xonshInit string
func (f Feature) Xonsh() Code {
switch f {
case Upgrade:
return "@($POSH_EXECUTABLE) upgrade"
return "@(_omp_executable) upgrade"
case Notice:
return "@($POSH_EXECUTABLE) notice"
return "@(_omp_executable) notice"
case PromptMark, RPrompt, PoshGit, Azure, LineError, Jobs, Tooltips, Transient, CursorPositioning, FTCSMarks:
fallthrough
default:
return ""
}
}
func quotePythonStr(str string) string {
if len(str) == 0 {
return "''"
}
return fmt.Sprintf("'%s'", strings.NewReplacer(
"'", `'"'"'`,
`\`, `\\`,
"\n", `\n`,
).Replace(str))
}

View file

@ -1,6 +1,7 @@
package shell
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
@ -10,8 +11,22 @@ func TestXonshFeatures(t *testing.T) {
got := allFeatures.Lines(XONSH).String("// these are the features")
want := `// these are the features
@($POSH_EXECUTABLE) upgrade
@($POSH_EXECUTABLE) notice`
@(_omp_executable) upgrade
@(_omp_executable) notice`
assert.Equal(t, want, got)
}
func TestQuotePythonStr(t *testing.T) {
tests := []struct {
str string
expected string
}{
{str: "", expected: "''"},
{str: `/tmp/"omp's dir"/oh-my-posh`, expected: `'/tmp/"omp'"'"'s dir"/oh-my-posh'`},
{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, quotePythonStr(tc.str), fmt.Sprintf("quotePythonStr: %s", tc.str))
}
}

View file

@ -7,21 +7,16 @@ import (
//go:embed scripts/omp.zsh
var zshInit string
const (
unixUpgrade = "$_omp_executable upgrade"
unixNotice = "$_omp_executable notice"
)
func (f Feature) Zsh() Code {
switch f {
case CursorPositioning:
return "_omp_cursor_positioning=1"
return unixCursorPositioning
case Tooltips:
return "enable_poshtooltips"
case Transient:
return "_omp_create_widget zle-line-init _omp_zle-line-init"
case FTCSMarks:
return "_omp_ftcs_marks=1"
return unixFTCSMarks
case Upgrade:
return unixUpgrade
case Notice:

View file

@ -13,8 +13,8 @@ func TestZshFeatures(t *testing.T) {
enable_poshtooltips
_omp_create_widget zle-line-init _omp_zle-line-init
_omp_ftcs_marks=1
$_omp_executable upgrade
$_omp_executable notice
"$_omp_executable" upgrade
"$_omp_executable" notice
_omp_cursor_positioning=1`
assert.Equal(t, want, got)

View file

@ -183,34 +183,29 @@ func ClearAfter() string {
}
func FormatTitle(title string) string {
switch Shell {
// These shells don't support setting the console title.
if Shell == shell.ELVISH || Shell == shell.XONSH {
case shell.ELVISH, shell.XONSH, shell.TCSH:
return ""
}
case shell.BASH, shell.ZSH:
title = trimAnsi(title)
s := new(strings.Builder)
title = trimAnsi(title)
// We have to do this to prevent the shell from misidentifying escape sequences.
for _, char := range title {
escaped, shouldEscape := formats.EscapeSequences[char]
if shouldEscape {
s.WriteString(escaped)
continue
}
if Plain {
return title
}
if Shell != shell.BASH && Shell != shell.ZSH {
return fmt.Sprintf(formats.Title, title)
}
// We have to do this to prevent Bash/Zsh from misidentifying escape sequences.
s := new(strings.Builder)
for _, char := range title {
escaped, shouldEscape := formats.EscapeSequences[char]
if shouldEscape {
s.WriteString(escaped)
continue
s.WriteRune(char)
}
s.WriteRune(char)
return fmt.Sprintf(formats.Title, s.String())
default:
return fmt.Sprintf(formats.Title, trimAnsi(title))
}
return fmt.Sprintf(formats.Title, s.String())
}
func EscapeText(text string) string {

View file

@ -188,10 +188,10 @@ to only require remote scripts to be signed using `Set-ExecutionPolicy RemoteSig
Adjust the Oh My Posh init line in `~/.tcshrc` by adding the `--config` flag with the location of your configuration.
```bash
eval `oh-my-posh init tcsh --config ~/jandedobbeleer.omp.json`
eval "`oh-my-posh init tcsh --config ~/jandedobbeleer.omp.json`"
```
Once added, reload your profile for the changes to take effect.
Note that the surrounding quotes are required. Once added, reload your profile for the changes to take effect.
```bash
exec tcsh

View file

@ -183,10 +183,10 @@ Once added, reload your profile for the changes to take effect.
Add the following at the end of `~/.tcshrc`:
```bash
eval `oh-my-posh init tcsh`
eval "`oh-my-posh init tcsh`"
```
Once added, reload your profile for the changes to take effect.
Note that the surrounding quotes are required. Once added, reload your profile for the changes to take effect.
```bash
exec tcsh