refactor(init): enable features automatically

This commit is contained in:
Jan De Dobbeleer 2024-07-24 13:16:03 +02:00 committed by Jan De Dobbeleer
parent ef16b1cd3a
commit 4a1aa2a7b4
40 changed files with 921 additions and 479 deletions

View file

@ -22,6 +22,7 @@ type Template struct {
WSL bool
PromptCount int
SHLVL int
Jobs int
Segments *maps.Concurrent
SegmentsCache maps.Simple

View file

@ -6,7 +6,6 @@ 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"
"github.com/jandedobbeleer/oh-my-posh/src/terminal"
"github.com/jandedobbeleer/oh-my-posh/src/upgrade"
"github.com/spf13/cobra"
@ -68,35 +67,20 @@ func runInit(shellName string) {
cfg := config.Load(env)
shell.Transient = cfg.TransientPrompt != nil
shell.ErrorLine = cfg.ErrorLine != nil || cfg.ValidLine != nil
shell.Tooltips = len(cfg.Tooltips) > 0
shell.ShellIntegration = cfg.ShellIntegration
shell.PromptMark = shellName == shell.FISH && cfg.ITermFeatures != nil && cfg.ITermFeatures.Contains(terminal.PromptMark)
shell.AutoUpgrade = cfg.AutoUpgrade
for i, block := range cfg.Blocks {
// only fetch cursor position when relevant
if cfg.EnableCursorPositioning && (i == 0 && block.Newline) {
shell.CursorPositioning = true
}
if block.Type == config.RPrompt {
shell.RPrompt = true
}
}
// TODO: this can be removed I think
// allow overriding the upgrade notice from the config
if cfg.DisableNotice || cfg.AutoUpgrade {
env.Cache().Set(upgrade.CACHEKEY, "disabled", -1)
}
feats := cfg.Features()
if printOutput {
init := shell.PrintInit(env)
init := shell.PrintInit(env, feats)
fmt.Print(init)
return
}
init := shell.Init(env)
init := shell.Init(env, feats)
fmt.Print(init)
}

View file

@ -20,6 +20,7 @@ var (
eval bool
cleared bool
cached bool
jobCount int
command string
shellVersion string
@ -68,6 +69,7 @@ var printCmd = &cobra.Command{
Cached: cached,
NoExitCode: noStatus,
Column: column,
JobCount: jobCount,
}
eng := prompt.New(flags)
@ -112,6 +114,7 @@ func init() {
printCmd.Flags().BoolVar(&cleared, "cleared", false, "do we have a clear terminal or not")
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")
// Deprecated flags, keep to not break CLI integration
printCmd.Flags().IntVarP(&status, "error", "e", 0, "last exit code")

View file

@ -3,6 +3,8 @@ package config
import (
"github.com/jandedobbeleer/oh-my-posh/src/color"
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
"github.com/jandedobbeleer/oh-my-posh/src/segments"
"github.com/jandedobbeleer/oh-my-posh/src/shell"
"github.com/jandedobbeleer/oh-my-posh/src/template"
"github.com/jandedobbeleer/oh-my-posh/src/terminal"
)
@ -74,3 +76,56 @@ func (cfg *Config) getPalette() color.Palette {
}
return cfg.Palette
}
func (cfg *Config) Features() shell.Features {
var feats shell.Features
if cfg.TransientPrompt != nil {
feats = append(feats, shell.Transient)
}
if cfg.ShellIntegration {
feats = append(feats, shell.FTCSMarks)
}
if !cfg.AutoUpgrade && !cfg.DisableNotice {
feats = append(feats, shell.Notice)
}
if cfg.AutoUpgrade {
feats = append(feats, shell.Upgrade)
}
if cfg.ErrorLine != nil || cfg.ValidLine != nil {
feats = append(feats, shell.LineError)
}
if len(cfg.Tooltips) > 0 {
feats = append(feats, shell.Tooltips)
}
if cfg.env.Shell() == shell.FISH && cfg.ITermFeatures != nil && cfg.ITermFeatures.Contains(terminal.PromptMark) {
feats = append(feats, shell.PromptMark)
}
for i, block := range cfg.Blocks {
if (i == 0 && block.Newline) && cfg.EnableCursorPositioning {
feats = append(feats, shell.CursorPositioning)
}
if block.Type == RPrompt {
feats = append(feats, shell.RPrompt)
}
for _, segment := range block.Segments {
if segment.Type == AZ {
source := segment.Properties.GetString(segments.Source, segments.FirstMatch)
if source == segments.Pwsh || source == segments.FirstMatch {
feats = append(feats, shell.Azure)
}
}
}
}
return feats
}

View file

@ -63,7 +63,7 @@ func loadConfig(env runtime.Environment) *Config {
data, err := stdOS.ReadFile(configFile)
if err != nil {
env.DebugF("error reading config file: %s", err)
env.Error(err)
return Default(env, true)
}
@ -87,7 +87,7 @@ func loadConfig(env runtime.Environment) *Config {
}
if err != nil {
env.DebugF("error decoding config file: %s", err)
env.Error(err)
return Default(env, true)
}

View file

@ -25,6 +25,7 @@ func (e *Engine) ExtraPrompt(promptType ExtraPromptType) string {
// populate env with latest context
e.Env.LoadTemplateCache()
var prompt *config.Segment
switch promptType {
case Debug:
prompt = e.Config.DebugPrompt

View file

@ -63,6 +63,7 @@ type Flags struct {
Cached bool
NoExitCode bool
Column int
JobCount int
}
type CommandError struct {
@ -276,11 +277,14 @@ func (term *Terminal) resolveConfigPath() {
configFile = filepath.Join(term.Home(), configFile)
}
if !filepath.IsAbs(configFile) {
configFile = filepath.Join(term.Pwd(), configFile)
abs, err := filepath.Abs(configFile)
if err != nil {
term.Error(err)
term.CmdFlags.Config = filepath.Clean(configFile)
return
}
term.CmdFlags.Config = filepath.Clean(configFile)
term.CmdFlags.Config = abs
}
func (term *Terminal) Trace(start time.Time, args ...string) {
@ -782,6 +786,7 @@ func (term *Terminal) TemplateCache() *cache.Template {
tmplCache.PromptCount = term.CmdFlags.PromptCount
tmplCache.Env = make(map[string]string)
tmplCache.Var = make(map[string]any)
tmplCache.Jobs = term.CmdFlags.JobCount
if term.Var != nil {
tmplCache.Var = term.Var

View file

@ -21,9 +21,9 @@ type Az struct {
const (
Source properties.Property = "source"
pwsh = "pwsh"
cli = "cli"
firstMatch = "first_match"
Pwsh = "pwsh"
Cli = "cli"
FirstMatch = "first_match"
azureEnv = "POSH_AZURE_SUBSCRIPTION"
)
@ -82,13 +82,13 @@ func (a *Az) Init(props properties.Properties, env runtime.Environment) {
}
func (a *Az) Enabled() bool {
source := a.props.GetString(Source, firstMatch)
source := a.props.GetString(Source, FirstMatch)
switch source {
case firstMatch:
case FirstMatch:
return a.getCLISubscription() || a.getModuleSubscription()
case pwsh:
case Pwsh:
return a.getModuleSubscription()
case cli:
case Cli:
return a.getCLISubscription()
}
return false

View file

@ -74,14 +74,14 @@ func TestAzSegment(t *testing.T) {
ExpectedString: "AzureCliCloud",
Template: "{{ .EnvironmentName }}",
HasCLI: true,
Source: cli,
Source: Cli,
},
{
Case: "Az CLI Profile only - disabled",
ExpectedEnabled: false,
Template: "{{ .EnvironmentName }}",
HasCLI: false,
Source: cli,
Source: Cli,
},
{
Case: "PowerShell Profile only",
@ -89,13 +89,13 @@ func TestAzSegment(t *testing.T) {
ExpectedString: "AzurePoshCloud",
Template: "{{ .EnvironmentName }}",
HasPowerShell: true,
Source: pwsh,
Source: Pwsh,
},
{
Case: "Az CLI Profile only - disabled",
ExpectedEnabled: false,
Template: "{{ .EnvironmentName }}",
Source: pwsh,
Source: Pwsh,
},
{
Case: "Az CLI account type",
@ -103,7 +103,7 @@ func TestAzSegment(t *testing.T) {
ExpectedString: "user",
Template: "{{ .User.Type }}",
HasCLI: true,
Source: cli,
Source: Cli,
},
}
@ -136,7 +136,7 @@ func TestAzSegment(t *testing.T) {
}
if tc.Source == "" {
tc.Source = firstMatch
tc.Source = FirstMatch
}
az := &Az{

78
src/shell/bash.go Normal file
View file

@ -0,0 +1,78 @@
package shell
import (
_ "embed"
"fmt"
"strings"
)
//go:embed scripts/omp.bash
var bashInit string
const (
unixUpgrade = "$_omp_executable upgrade"
unixNotice = "$_omp_executable notice"
)
func (f Feature) Bash() Code {
switch f {
case CursorPositioning:
return "_omp_cursor_positioning=1"
case FTCSMarks:
return "_omp_ftcs_marks=1"
case Upgrade:
return unixUpgrade
case Notice:
return unixNotice
case PromptMark, RPrompt, PoshGit, Azure, LineError, Jobs, Tooltips, Transient:
fallthrough
default:
return ""
}
}
func quotePosixStr(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)
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()
}

19
src/shell/bash_test.go Normal file
View file

@ -0,0 +1,19 @@
package shell
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestBashFeatures(t *testing.T) {
got := allFeatures.Lines(BASH).String("// these are the features")
want := `// these are the features
_omp_ftcs_marks=1
$_omp_executable upgrade
$_omp_executable notice
_omp_cursor_positioning=1`
assert.Equal(t, want, got)
}

38
src/shell/cmd.go Normal file
View file

@ -0,0 +1,38 @@
package shell
import (
_ "embed"
"fmt"
"strings"
)
//go:embed scripts/omp.lua
var cmdInit string
func (f Feature) Cmd() Code {
switch f {
case Transient:
return "transient_enabled = true"
case RPrompt:
return "rprompt_enabled = true"
case Tooltips:
return "enable_tooltips()"
case Upgrade:
return "os.execute(string.format('%s upgrade', omp_exe()))"
case Notice:
return "os.execute(string.format('%s notice', omp_exe()))"
case PromptMark, PoshGit, Azure, LineError, Jobs, FTCSMarks, CursorPositioning:
fallthrough
default:
return ""
}
}
func quoteLuaStr(str string) string {
if len(str) == 0 {
return "''"
}
return fmt.Sprintf("'%s'", strings.NewReplacer(`\`, `\\`, `'`, `\'`).Replace(str))
}

20
src/shell/cmd_test.go Normal file
View file

@ -0,0 +1,20 @@
package shell
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestCmdFeatures(t *testing.T) {
got := allFeatures.Lines(CMD).String("// these are the features")
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()))
rprompt_enabled = true`
assert.Equal(t, want, got)
}

29
src/shell/code.go Normal file
View file

@ -0,0 +1,29 @@
package shell
import "strings"
type Code string
func (c Code) Indent(spaces int) Code {
return Code(strings.Repeat(" ", spaces) + string(c))
}
type Lines []Code
func (l Lines) String(script string) string {
var builder strings.Builder
builder.WriteString(script)
builder.WriteString("\n")
for i, line := range l {
builder.WriteString(string(line))
// add newline if not last line
if i < len(l)-1 {
builder.WriteString("\n")
}
}
return builder.String()
}

21
src/shell/elvish.go Normal file
View file

@ -0,0 +1,21 @@
package shell
import (
_ "embed"
)
//go:embed scripts/omp.elv
var elvishInit string
func (f Feature) Elvish() Code {
switch f {
case Upgrade:
return unixUpgrade
case Notice:
return unixNotice
case PromptMark, RPrompt, PoshGit, Azure, LineError, Jobs, CursorPositioning, Tooltips, Transient, FTCSMarks:
fallthrough
default:
return ""
}
}

17
src/shell/elvish_test.go Normal file
View file

@ -0,0 +1,17 @@
package shell
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestElvishFeatures(t *testing.T) {
got := allFeatures.Lines(ELVISH).String("// these are the features")
want := `// these are the features
$_omp_executable upgrade
$_omp_executable notice`
assert.Equal(t, want, got)
}

55
src/shell/features.go Normal file
View file

@ -0,0 +1,55 @@
package shell
type Feature byte
const (
Jobs Feature = iota
Azure
PoshGit
LineError
Tooltips
Transient
FTCSMarks
Upgrade
Notice
PromptMark
RPrompt
CursorPositioning
)
type Features []Feature
func (f Features) Lines(shell string) Lines {
var lines Lines
for _, feature := range f {
var code Code
switch shell {
case PWSH, PWSH5:
code = feature.Pwsh()
case ZSH:
code = feature.Zsh()
case BASH:
code = feature.Bash()
case ELVISH:
code = feature.Elvish()
case TCSH:
code = feature.Tcsh()
case FISH:
code = feature.Fish()
case CMD:
code = feature.Cmd()
case NU:
code = feature.Nu()
case XONSH:
code = feature.Xonsh()
}
if len(code) > 0 {
lines = append(lines, code)
}
}
return lines
}

61
src/shell/fish.go Normal file
View file

@ -0,0 +1,61 @@
package shell
import (
_ "embed"
"fmt"
"strings"
)
//go:embed scripts/omp.fish
var fishInit string
func (f Feature) Fish() Code {
switch f {
case Transient:
return "set --global _omp_transient_prompt 1"
case FTCSMarks:
return "set --global _omp_ftcs_marks 1"
case PromptMark:
return "set --global _omp_prompt_mark 1"
case Tooltips:
return "enable_poshtooltips"
case Upgrade:
return unixUpgrade
case Notice:
return unixNotice
case RPrompt, PoshGit, Azure, LineError, Jobs, CursorPositioning:
fallthrough
default:
return ""
}
}
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()
}

21
src/shell/fish_test.go Normal file
View file

@ -0,0 +1,21 @@
package shell
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestFishFeatures(t *testing.T) {
got := allFeatures.Lines(FISH).String("// these are the features")
want := `// these are the features
enable_poshtooltips
set --global _omp_transient_prompt 1
set --global _omp_ftcs_marks 1
$_omp_executable upgrade
$_omp_executable notice
set --global _omp_prompt_mark 1`
assert.Equal(t, want, got)
}

View file

@ -1,171 +1,38 @@
package shell
import (
_ "embed"
"path/filepath"
"strconv"
"fmt"
"os"
"strings"
"github.com/jandedobbeleer/oh-my-posh/src/color"
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
"github.com/jandedobbeleer/oh-my-posh/src/template"
"github.com/jandedobbeleer/oh-my-posh/src/upgrade"
)
//go:embed scripts/omp.ps1
var pwshInit string
//go:embed scripts/omp.fish
var fishInit string
//go:embed scripts/omp.bash
var bashInit string
//go:embed scripts/omp.zsh
var zshInit string
//go:embed scripts/omp.lua
var cmdInit string
//go:embed scripts/omp.nu
var nuInit string
//go:embed scripts/omp.tcsh
var tcshInit string
//go:embed scripts/omp.elv
var elvishInit string
//go:embed scripts/omp.py
var xonshInit string
const (
noExe = "echo \"Unable to find Oh My Posh executable\""
)
var (
Transient bool
ErrorLine bool
Tooltips bool
ShellIntegration bool
RPrompt bool
CursorPositioning bool
PromptMark bool
AutoUpgrade bool
)
func getExecutablePath(env runtime.Environment) (string, error) {
executable, err := os.Executable()
if err != nil {
return "", err
}
if env.Flags().Strict {
return runtime.Base(env, executable), nil
}
// On Windows, it fails when the excutable is called in MSYS2 for example
// which uses unix style paths to resolve the executable's location.
// PowerShell knows how to resolve both, so we can swap this without any issue.
if env.GOOS() == runtime.WINDOWS {
executable = strings.ReplaceAll(executable, "\\", "/")
}
return executable, nil
}
func quotePwshStr(str string) string {
return fmt.Sprintf("'%s'", strings.ReplaceAll(str, "'", "''"))
}
func quotePosixStr(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)
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()
}
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()
}
func quoteLuaStr(str string) string {
if len(str) == 0 {
return "''"
}
return fmt.Sprintf("'%s'", strings.NewReplacer(`\`, `\\`, `'`, `\'`).Replace(str))
}
func quoteNuStr(str string) string {
if len(str) == 0 {
return "''"
}
return fmt.Sprintf(`"%s"`, strings.NewReplacer(`\`, `\\`, `"`, `\"`).Replace(str))
}
func Init(env runtime.Environment) string {
func Init(env runtime.Environment, feats Features) string {
shell := env.Flags().Shell
switch shell {
@ -197,44 +64,25 @@ func Init(env runtime.Environment) string {
return fmt.Sprintf(command, executable, shell, config, additionalParams)
case ZSH, BASH, FISH, CMD, TCSH, XONSH:
return PrintInit(env)
return PrintInit(env, feats)
case NU:
createNuInit(env)
createNuInit(env, feats)
return ""
default:
return fmt.Sprintf("echo \"No initialization script available for %s\"", shell)
}
}
func PrintInit(env runtime.Environment) string {
func PrintInit(env runtime.Environment, features Features) string {
executable, err := getExecutablePath(env)
if err != nil {
return noExe
}
toggleSetting := func(setting bool) string {
if env.Flags().Manual {
return "false"
}
return strconv.FormatBool(setting)
}
promptMark := func() string {
if PromptMark {
return "iterm2_prompt_mark"
}
return ""
}
shell := env.Flags().Shell
configFile := env.Flags().Config
var (
script, notice string
hasNotice bool
)
var script string
switch shell {
case PWSH, PWSH5:
@ -273,57 +121,11 @@ func PrintInit(env runtime.Environment) string {
return fmt.Sprintf("echo \"No initialization script available for %s\"", shell)
}
// only run this for shells that support
// injecting the notice directly
if shell != PWSH && shell != PWSH5 {
notice, hasNotice = upgrade.Notice(env, false)
}
return strings.NewReplacer(
init := strings.NewReplacer(
"::OMP::", executable,
"::CONFIG::", configFile,
"::SHELL::", shell,
"::TRANSIENT::", toggleSetting(Transient),
"::ERROR_LINE::", toggleSetting(ErrorLine),
"::TOOLTIPS::", toggleSetting(Tooltips),
"::FTCS_MARKS::", toggleSetting(ShellIntegration),
"::RPROMPT::", strconv.FormatBool(RPrompt),
"::CURSOR::", strconv.FormatBool(CursorPositioning),
"::UPGRADE::", strconv.FormatBool(hasNotice),
"::UPGRADENOTICE::", notice,
"::AUTOUPGRADE::", strconv.FormatBool(AutoUpgrade),
"::PROMPT_MARK::", promptMark(),
).Replace(script)
}
func createNuInit(env runtime.Environment) {
initPath := filepath.Join(env.Home(), ".oh-my-posh.nu")
f, err := os.OpenFile(initPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
if err != nil {
return
}
_, err = f.WriteString(PrintInit(env))
if err != nil {
return
}
_ = f.Close()
}
func ConsoleBackgroundColor(env runtime.Environment, backgroundColorTemplate color.Ansi) color.Ansi {
if backgroundColorTemplate.IsEmpty() {
return backgroundColorTemplate
}
tmpl := &template.Text{
Template: string(backgroundColorTemplate),
Context: nil,
Env: env,
}
text, err := tmpl.Render()
if err != nil {
return color.Transparent
}
return color.Ansi(text)
return features.Lines(shell).String(init)
}

52
src/shell/nu.go Normal file
View file

@ -0,0 +1,52 @@
package shell
import (
_ "embed"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
)
//go:embed scripts/omp.nu
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
case Upgrade:
return "^$_omp_executable upgrade"
case Notice:
return "^$_omp_executable notice"
case PromptMark, RPrompt, PoshGit, Azure, LineError, Jobs, Tooltips, FTCSMarks, CursorPositioning:
fallthrough
default:
return ""
}
}
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)
if err != nil {
return
}
_, err = f.WriteString(PrintInit(env, features))
if err != nil {
return
}
_ = f.Close()
}

19
src/shell/nu_test.go Normal file
View file

@ -0,0 +1,19 @@
package shell
import (
"testing"
"github.com/stretchr/testify/assert"
)
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)" }
^$_omp_executable upgrade
^$_omp_executable notice`
assert.Equal(t, want, got)
}

42
src/shell/pwsh.go Normal file
View file

@ -0,0 +1,42 @@
package shell
import (
_ "embed"
"fmt"
"strings"
)
//go:embed scripts/omp.ps1
var pwshInit string
func (f Feature) Pwsh() Code {
switch f {
case Tooltips:
return "Enable-PoshTooltips"
case LineError:
return "Enable-PoshLineError"
case Transient:
return "Enable-PoshTransientPrompt"
case Jobs:
return "$global:_ompJobCount = $true"
case Azure:
return "$global:_ompAzure = $true"
case PoshGit:
return "$global:_ompPoshGit = $true"
case FTCSMarks:
return "$global:_ompFTCSMarks = $true"
case Upgrade:
return "& $global:_ompExecutable upgrade"
case Notice:
return "& $global:_ompExecutable notice"
case PromptMark, RPrompt, CursorPositioning:
fallthrough
default:
return ""
}
}
func quotePwshStr(str string) string {
return fmt.Sprintf("'%s'", strings.ReplaceAll(str, "'", "''"))
}

26
src/shell/pwsh_test.go Normal file
View file

@ -0,0 +1,26 @@
package shell
import (
"testing"
"github.com/stretchr/testify/assert"
)
var allFeatures = Features{Tooltips, LineError, Transient, Jobs, Azure, PoshGit, FTCSMarks, Upgrade, Notice, PromptMark, RPrompt, CursorPositioning}
func TestPwshFeatures(t *testing.T) {
got := allFeatures.Lines(PWSH).String("")
want := `
Enable-PoshTooltips
Enable-PoshLineError
Enable-PoshTransientPrompt
$global:_ompJobCount = $true
$global:_ompAzure = $true
$global:_ompPoshGit = $true
$global:_ompFTCSMarks = $true
& $global:_ompExecutable upgrade
& $global:_ompExecutable notice`
assert.Equal(t, want, got)
}

View file

@ -4,22 +4,28 @@ export POWERLINE_COMMAND="oh-my-posh"
export POSH_PID=$$
export CONDA_PROMPT_MODIFIER=false
# global variables
_omp_start_time=""
_omp_stack_count=0
_omp_elapsed=-1
_omp_no_exit_code="true"
_omp_status_cache=0
_omp_pipestatus_cache=0
_omp_executable=::OMP::
# switches to enable/disable features
_omp_cursor_positioning=0
_omp_ftcs_marks=0
# start timer on command start
PS0='${_omp_start_time:0:$((_omp_start_time="$(_omp_start_timer)",0))}$(_omp_ftcs_command_start)'
# set secondary prompt
PS2="$(::OMP:: print secondary --config="$POSH_THEME" --shell=bash --shell-version="$BASH_VERSION")"
PS2="$(${_omp_executable} print secondary --config="$POSH_THEME" --shell=bash --shell-version="$BASH_VERSION")"
function _omp_set_cursor_position() {
# not supported in Midnight Commander
# see https://github.com/JanDeDobbeleer/oh-my-posh/issues/3415
if [[ "::CURSOR::" != "true" ]] || [[ -v MC_SID ]]; then
if [[ $_omp_cursor_positioning == 0 ]] || [[ -v MC_SID ]]; then
return
fi
@ -37,11 +43,11 @@ function _omp_set_cursor_position() {
}
function _omp_start_timer() {
::OMP:: get millis
$_omp_executable get millis
}
function _omp_ftcs_command_start() {
if [[ "::FTCS_MARKS::" == "true" ]]; then
if [[ $_omp_ftcs_marks == 0 ]]; then
printf "\e]133;C\a"
fi
}
@ -62,7 +68,7 @@ function _omp_hook() {
_omp_stack_count=$((${#DIRSTACK[@]} - 1))
if [[ "$_omp_start_time" ]]; then
local omp_now=$(::OMP:: get millis --shell=bash)
local omp_now=$(${_omp_executable} get millis --shell=bash)
_omp_elapsed=$((omp_now - _omp_start_time))
_omp_start_time=""
_omp_no_exit_code="false"
@ -75,19 +81,11 @@ function _omp_hook() {
set_poshcontext
_omp_set_cursor_position
PS1="$(::OMP:: print primary --config="$POSH_THEME" --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')"
PS1="$(${_omp_executable} print primary --config="$POSH_THEME" --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')"
return $_omp_status_cache
}
if [[ "$TERM" != "linux" ]] && [[ -x "$(command -v ::OMP::)" ]] && ! [[ "$PROMPT_COMMAND" =~ "_omp_hook" ]]; then
if [[ "$TERM" != "linux" ]] && [[ -x "$(command -v $_omp_executable)" ]] && ! [[ "$PROMPT_COMMAND" =~ "_omp_hook" ]]; then
PROMPT_COMMAND="_omp_hook; $PROMPT_COMMAND"
fi
if [[ "::UPGRADE::" == "true" ]]; then
echo "::UPGRADENOTICE::"
fi
if [[ "::AUTOUPGRADE::" == "true" ]]; then
::OMP:: upgrade
fi

View file

@ -4,6 +4,7 @@ set-env POSH_SHELL_VERSION (elvish --version)
set-env POWERLINE_COMMAND 'oh-my-posh'
var error-code = 0
var _omp_executable = ::OMP::
fn posh-after-command-hook {|m|
var error = $m[error]
@ -23,18 +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:: print primary --config=$E:POSH_THEME --shell=elvish --execution-time=$cmd-duration --status=$error-code --pwd=$pwd --shell-version=$E:POSH_SHELL_VERSION
$_omp_executable print primary --config=$E:POSH_THEME --shell=elvish --execution-time=$cmd-duration --status=$error-code --pwd=$pwd --shell-version=$E:POSH_SHELL_VERSION
}
set edit:rprompt = {
var cmd-duration = (printf "%.0f" (* $edit:command-duration 1000))
::OMP:: print right --config=$E:POSH_THEME --shell=elvish --execution-time=$cmd-duration --status=$error-code --pwd=$pwd --shell-version=$E:POSH_SHELL_VERSION
}
if (eq '::UPGRADE::' 'true') {
echo '::UPGRADENOTICE::'
}
if (eq '::AUTOUPGRADE::' 'true') {
::OMP:: upgrade
$_omp_executable print right --config=$E:POSH_THEME --shell=elvish --execution-time=$cmd-duration --status=$error-code --pwd=$pwd --shell-version=$E:POSH_SHELL_VERSION
}

View file

@ -7,6 +7,11 @@ set --global _omp_tooltip_command ''
set --global _omp_current_rprompt ''
set --global _omp_transient false
set --global _omp_executable ::OMP::
set --global _omp_ftcs_marks 0
set --global _omp_transient_prompt 0
set --global _omp_prompt_mark 0
# We use this to avoid unnecessary CLI calls for prompt repaint.
set --global _omp_new_prompt true
@ -25,7 +30,7 @@ function fish_prompt
# see https://github.com/fish-shell/fish-shell/issues/8418
printf \e\[0J
if test "$_omp_transient" = true
::OMP:: print transient --config $POSH_THEME --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_executable print transient --config $POSH_THEME --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
return
end
if test "$_omp_new_prompt" = false
@ -67,10 +72,12 @@ function fish_prompt
set omp_cleared true
end
::PROMPT_MARK::
if test $_omp_prompt_mark = 1
iterm2_prompt_mark
end
# The prompt is saved for possible reuse, typically a repaint after clearing the screen buffer.
set --global _omp_current_prompt (::OMP:: print primary --config $POSH_THEME --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_executable print primary --config $POSH_THEME --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)
echo -n "$_omp_current_prompt"
end
@ -79,13 +86,16 @@ function fish_right_prompt
set _omp_transient false
return
end
# Repaint an existing right prompt.
if test "$_omp_new_prompt" = false
echo -n "$_omp_current_rprompt"
return
end
set _omp_new_prompt false
set --global _omp_current_rprompt (::OMP:: print right --config $POSH_THEME --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_executable print right --config $POSH_THEME --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 '')
echo -n "$_omp_current_rprompt"
end
@ -96,7 +106,7 @@ function _omp_postexec --on-event fish_postexec
end
function _omp_preexec --on-event fish_preexec
if test "::FTCS_MARKS::" = true
if test $_omp_ftcs_marks = 1
echo -ne "\e]133;C\a"
end
end
@ -107,16 +117,19 @@ if test -n (bind \r --user 2>/dev/null | string match -e _omp_enter_key_handler)
bind -e \r -M insert
bind -e \r -M visual
end
if test -n (bind \n --user 2>/dev/null | string match -e _omp_enter_key_handler)
bind -e \n -M default
bind -e \n -M insert
bind -e \n -M visual
end
if test -n (bind \cc --user 2>/dev/null | string match -e _omp_ctrl_c_key_handler)
bind -e \cc -M default
bind -e \cc -M insert
bind -e \cc -M visual
end
if test -n (bind \x20 --user 2>/dev/null | string match -e _omp_space_key_handler)
bind -e \x20 -M default
bind -e \x20 -M insert
@ -127,23 +140,28 @@ end
function _omp_space_key_handler
commandline --function expand-abbr
commandline --insert ' '
# Get the first word of command line as tip.
set --local tooltip_command (commandline --current-buffer | string trim -l | string split --allow-empty -f1 ' ' | string collect)
# Ignore an empty/repeated tooltip command.
if test -z "$tooltip_command" || test "$tooltip_command" = "$_omp_tooltip_command"
return
end
set _omp_tooltip_command $tooltip_command
set --local tooltip_prompt (::OMP:: print tooltip --config $POSH_THEME --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_executable print tooltip --config $POSH_THEME --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 '')
if test -z "$tooltip_prompt"
return
end
# Save the tooltip prompt to avoid unnecessary CLI calls.
set _omp_current_rprompt $tooltip_prompt
commandline --function repaint
end
if test "::TOOLTIPS::" = true
function enable_poshtooltips
bind \x20 _omp_space_key_handler -M default
bind \x20 _omp_space_key_handler -M insert
end
@ -155,14 +173,17 @@ function _omp_enter_key_handler
commandline --function accept-autosuggestion
return
end
if commandline --is-valid || test -z (commandline --current-buffer | string trim -l | string collect)
set _omp_new_prompt true
set _omp_tooltip_command ''
if test "::TRANSIENT::" = true
if test $_omp_transient_prompt = 1
set _omp_transient true
commandline --function repaint
end
end
commandline --function execute
end
@ -170,13 +191,16 @@ function _omp_ctrl_c_key_handler
if test -z (commandline --current-buffer | string collect)
return
end
# Render a transient prompt on Ctrl-C with non-empty command line buffer.
set _omp_new_prompt true
set _omp_tooltip_command ''
if test "::TRANSIENT::" = true
if test $_omp_transient_prompt = 1
set _omp_transient true
commandline --function repaint
end
commandline --function cancel-commandline
commandline --function repaint
end
@ -192,17 +216,6 @@ bind \cc _omp_ctrl_c_key_handler -M insert
bind \cc _omp_ctrl_c_key_handler -M visual
# legacy functions
function enable_poshtooltips
return
end
function enable_poshtransientprompt
return
end
if test "::UPGRADE::" = true
echo "::UPGRADENOTICE::"
end
if test "::AUTOUPGRADE::" = true
::OMP:: upgrade
end

View file

@ -39,8 +39,8 @@ end
local endedit_time = 0
local last_duration = 0
local tooltips_enabled = ::TOOLTIPS::
local rprompt_enabled = ::RPROMPT::
local rprompt_enabled = false
local transient_enabled = false
local no_exit_code = true
local cached_prompt = {}
@ -260,20 +260,29 @@ function p:filter(prompt)
return cached_prompt.left
end
function p:rightfilter(prompt)
-- Return cached tooltip if available, otherwise return cached rprompt.
-- Returning false as the second return value halts further prompt
-- filtering, to keep other things from overriding what we generated.
return (cached_prompt.tooltip or cached_prompt.right), false
end
function p:transientfilter(prompt)
if not transient_enabled then
return nil
end
local prompt_exe = string.format('%s print transient --shell=cmd --config=%s %s %s', omp_exe(), omp_config(), error_level_option(), no_exit_code_option())
prompt = run_posh_command(prompt_exe)
if prompt == "" then
prompt = nil
end
return prompt
end
function p:transientrightfilter(prompt)
return "", false
end
@ -323,11 +332,10 @@ function ohmyposh_space(rl_buffer)
end
end
if tooltips_enabled and rl.setbinding then
local function enable_tooltips()
if not rl.setbinding then
return
end
rl.setbinding(' ', [["luafunc:ohmyposh_space"]], 'emacs')
end
if '::AUTOUPGRADE::' == 'true' then
local prompt_exe = string.format('%s upgrade', omp_exe())
os.execute(prompt_exe)
end

View file

@ -9,6 +9,8 @@ $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.
@ -21,7 +23,7 @@ def posh_width [] {
}
# PROMPTS
$env.PROMPT_MULTILINE_INDICATOR = (^::OMP:: print secondary $"--config=($env.POSH_THEME)" --shell=nu $"--shell-version=($env.POSH_SHELL_VERSION)")
$env.PROMPT_MULTILINE_INDICATOR = (^$_omp_executable print secondary $"--config=($env.POSH_THEME)" --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
@ -35,23 +37,9 @@ $env.PROMPT_COMMAND = { ||
do --env $env.SET_POSHCONTEXT
}
^::OMP:: print primary $"--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)" $"--cleared=($clear)"
^$_omp_executable print primary $"--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)" $"--cleared=($clear)"
}
$env.PROMPT_COMMAND_RIGHT = { ||
^::OMP:: print right $"--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)"
}
if "::TRANSIENT::" == "true" {
$env.TRANSIENT_PROMPT_COMMAND = { ||
^::OMP:: 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)"
}
}
if "::UPGRADE::" == "true" {
echo "::UPGRADENOTICE::"
}
if "::AUTOUPGRADE::" == "true" {
^::OMP:: upgrade
^$_omp_executable print right $"--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)"
}

View file

@ -13,6 +13,13 @@ function global:Get-PoshStackCount {
return 0
}
# global enablers
$global:_ompJobCount = $false
$global:_ompFTCSMarks = $false
$global:_ompPoshGit = $false
$global:_ompAzure = $false
$global:_ompExecutable = ::OMP::
New-Module -Name "oh-my-posh-core" -ScriptBlock {
# Check `ConstrainedLanguage` mode.
$script:ConstrainedLanguageMode = $ExecutionContext.SessionState.LanguageMode -eq "ConstrainedLanguage"
@ -25,11 +32,12 @@ New-Module -Name "oh-my-posh-core" -ScriptBlock {
$script:NoExitCode = $true
$script:ErrorCode = 0
$script:ExecutionTime = 0
$script:OMPExecutable = ::OMP::
$script:ShellName = "::SHELL::"
$script:PSVersion = $PSVersionTable.PSVersion.ToString()
$script:TransientPrompt = $false
$script:TooltipCommand = ''
$script:JobCount = 0
$env:POWERLINE_COMMAND = "oh-my-posh"
$env:POSH_SHELL_VERSION = $script:PSVersion
$env:POSH_PID = $PID
@ -40,25 +48,6 @@ New-Module -Name "oh-my-posh-core" -ScriptBlock {
$env:POSH_THEME = (Resolve-Path -Path ::CONFIG::).ProviderPath
}
# specific module support (disabled by default)
if (($null -eq $env:POSH_GIT_ENABLED) -or ($null -eq (Get-Module 'posh-git'))) {
$env:POSH_GIT_ENABLED = $false
}
if (($null -eq $env:POSH_AZURE_ENABLED) -or ($null -eq (Get-Module 'az'))) {
$env:POSH_AZURE_ENABLED = $false
}
function Initialize-ModuleSupport {
if ($env:POSH_GIT_ENABLED -eq $true) {
# We need to set the status so posh-git can facilitate autocomplete
$global:GitStatus = Get-GitStatus
$env:POSH_GIT_STATUS = $global:GitStatus | ConvertTo-Json
}
if ($env:POSH_AZURE_ENABLED -eq $true) {
$env:POSH_AZURE_SUBSCRIPTION = Get-AzContext | ConvertTo-Json
}
}
function Start-Utf8Process {
param(
[string]$FileName,
@ -94,6 +83,7 @@ New-Module -Name "oh-my-posh-core" -ScriptBlock {
}
$StartInfo.Arguments = $escapedArgs -join ' '
}
$StartInfo.StandardErrorEncoding = $StartInfo.StandardOutputEncoding = [System.Text.Encoding]::UTF8
$StartInfo.RedirectStandardError = $StartInfo.RedirectStandardInput = $StartInfo.RedirectStandardOutput = $true
$StartInfo.UseShellExecute = $false
@ -106,6 +96,7 @@ New-Module -Name "oh-my-posh-core" -ScriptBlock {
}
$StartInfo.WorkingDirectory = $PWD.ProviderPath
}
$StartInfo.CreateNoWindow = $true
[void]$Process.Start()
@ -118,6 +109,7 @@ New-Module -Name "oh-my-posh-core" -ScriptBlock {
if ($stderr) {
$Host.UI.WriteErrorLine($stderr)
}
$stdoutTask.Result
}
@ -140,8 +132,11 @@ New-Module -Name "oh-my-posh-core" -ScriptBlock {
$terminalWidth
}
if (!$script:ConstrainedLanguageMode) {
if ('::TOOLTIPS::' -eq 'true') {
function Enable-PoshTooltips {
if ($script:ConstrainedLanguageMode) {
return
}
Set-PSReadLineKeyHandler -Key Spacebar -BriefDescription 'OhMyPoshSpaceKeyHandler' -ScriptBlock {
param([ConsoleKeyInfo]$key)
[Microsoft.PowerShell.PSConsoleReadLine]::SelfInsert($key)
@ -150,6 +145,7 @@ New-Module -Name "oh-my-posh-core" -ScriptBlock {
[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$command, [ref]$null)
# Get the first word of command line as tip.
$command = $command.TrimStart().Split(' ', 2) | Select-Object -First 1
# Ignore an empty/repeated tooltip command.
if (!$command -or ($command -eq $script:TooltipCommand)) {
return
@ -161,23 +157,7 @@ New-Module -Name "oh-my-posh-core" -ScriptBlock {
$cleanPSWD = Get-CleanPSWD
$stackCount = global:Get-PoshStackCount
$arguments = @(
"print"
"tooltip"
"--status=$script:ErrorCode"
"--shell=$script:ShellName"
"--pswd=$cleanPSWD"
"--execution-time=$script:ExecutionTime"
"--stack-count=$stackCount"
"--config=$env:POSH_THEME"
"--command=$command"
"--shell-version=$script:PSVersion"
"--column=$column"
"--terminal-width=$terminalWidth"
"--no-status=$script:NoExitCode"
)
$standardOut = (Start-Utf8Process $script:OMPExecutable $arguments) -join ''
$standardOut = (Start-Utf8Process $global:_ompExecutable @("print", "tooltip", "--status=$script:ErrorCode", "--shell=$script:ShellName", "--pswd=$cleanPSWD", "--execution-time=$script:ExecutionTime", "--stack-count=$stackCount", "--config=$env:POSH_THEME", "--command=$command", "--shell-version=$script:PSVersion", "--column=$column", "--terminal-width=$terminalWidth", "--no-status=$script:NoExitCode", "--job-count=$script:JobCount")) -join ''
if (!$standardOut) {
return
}
@ -204,6 +184,11 @@ New-Module -Name "oh-my-posh-core" -ScriptBlock {
}
}
function Enable-PoshTransientPrompt {
if ($script:ConstrainedLanguageMode) {
return
}
Set-PSReadLineKeyHandler -Key Enter -BriefDescription 'OhMyPoshEnterKeyHandler' -ScriptBlock {
try {
$parseErrors = $null
@ -212,19 +197,18 @@ New-Module -Name "oh-my-posh-core" -ScriptBlock {
if ($executingCommand) {
$script:newPrompt = $true
$script:TooltipCommand = ''
if ('::TRANSIENT::' -eq 'true') {
Set-TransientPrompt
}
}
}
finally {
[Microsoft.PowerShell.PSConsoleReadLine]::AcceptLine()
if (('::FTCS_MARKS::' -eq 'true') -and $executingCommand) {
if ($global:_ompFTCSMarks -and $executingCommand) {
# Write FTCS_COMMAND_EXECUTED after accepting the input - it should still happen before execution
Write-Host "$([char]0x1b)]133;C`a" -NoNewline
}
}
}
Set-PSReadLineKeyHandler -Key Ctrl+c -BriefDescription 'OhMyPoshCtrlCKeyHandler' -ScriptBlock {
try {
$start = $null
@ -233,20 +217,18 @@ New-Module -Name "oh-my-posh-core" -ScriptBlock {
if ($start -eq -1) {
$script:newPrompt = $true
$script:TooltipCommand = ''
if ('::TRANSIENT::' -eq 'true') {
Set-TransientPrompt
}
}
}
finally {
[Microsoft.PowerShell.PSConsoleReadLine]::CopyOrCancelLine()
}
}
}
if ("::ERROR_LINE::" -eq "true") {
$validLine = (Start-Utf8Process $script:OMPExecutable @("print", "valid", "--config=$env:POSH_THEME", "--shell=$script:ShellName")) -join "`n"
$errorLine = (Start-Utf8Process $script:OMPExecutable @("print", "error", "--config=$env:POSH_THEME", "--shell=$script:ShellName")) -join "`n"
function Enable-PoshLineError {
$validLine = (Start-Utf8Process $global:_ompExecutable @("print", "valid", "--config=$env:POSH_THEME", "--shell=$script:ShellName")) -join "`n"
$errorLine = (Start-Utf8Process $global:_ompExecutable @("print", "error", "--config=$env:POSH_THEME", "--shell=$script:ShellName")) -join "`n"
Set-PSReadLineOption -PromptText $validLine, $errorLine
}
@ -285,7 +267,7 @@ New-Module -Name "oh-my-posh-core" -ScriptBlock {
$Format = 'json'
)
$configString = Start-Utf8Process $script:OMPExecutable @("config", "export", "--config=$env:POSH_THEME", "--format=$Format")
$configString = Start-Utf8Process $global:_ompExecutable @("config", "export", "--config=$env:POSH_THEME", "--format=$Format")
# if no path, copy to clipboard by default
if ($FilePath) {
# https://stackoverflow.com/questions/3038337/powershell-resolve-path-that-might-not-exist
@ -305,15 +287,18 @@ New-Module -Name "oh-my-posh-core" -ScriptBlock {
[Parameter(ValuefromPipeline = $True)]
[string]$name
)
$esc = [char]27
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 "$esc]8;;file://$uri$esc\$name$esc]8;;$esc\"
}
@ -353,7 +338,7 @@ New-Module -Name "oh-my-posh-core" -ScriptBlock {
$cleanPSWD = Get-CleanPSWD
$themes | ForEach-Object -Process {
Write-Host "Theme: $(Get-FileHyperlink -uri $_.FullName -Name ($_.BaseName -replace '\.omp$', ''))`n"
Start-Utf8Process $script:OMPExecutable @("print", "primary", "--config=$($_.FullName)", "--pswd=$cleanPSWD", "--shell=$script:ShellName")
Start-Utf8Process $global:_ompExecutable @("print", "primary", "--config=$($_.FullName)", "--pswd=$cleanPSWD", "--shell=$script:ShellName")
Write-Host "`n"
}
}
@ -374,6 +359,7 @@ Example:
$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
@ -382,18 +368,35 @@ Example:
$script:PromptType = "debug"
return
}
$script:PromptType = "primary"
Initialize-ModuleSupport
if ($global:_ompJobCount) {
$script:JobCount = (Get-Job -State Running).Count
}
if ($global:_ompAzure) {
$env:POSH_AZURE_SUBSCRIPTION = Get-AzContext | ConvertTo-Json
}
if ($global:_ompPoshGit) {
$global:GitStatus = Get-GitStatus
$env:POSH_GIT_STATUS = $global:GitStatus | ConvertTo-Json
}
Set-PoshContext
}
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
@ -401,16 +404,19 @@ Example:
$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
@ -432,35 +438,19 @@ Example:
Set-PoshPromptType
if ($script:PromptType -ne 'transient') {
Update-PoshErrorCode
}
$cleanPSWD = Get-CleanPSWD
$stackCount = global:Get-PoshStackCount
Set-PoshContext
$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
$arguments = @(
"print"
$script:PromptType
"--status=$script:ErrorCode"
"--pswd=$cleanPSWD"
"--execution-time=$script:ExecutionTime"
"--stack-count=$stackCount"
"--config=$env:POSH_THEME"
"--shell-version=$script:PSVersion"
"--terminal-width=$terminalWidth"
"--shell=$script:ShellName"
"--no-status=$script:NoExitCode"
)
$standardOut = Start-Utf8Process $script:OMPExecutable $arguments
$standardOut = Start-Utf8Process $global:_ompExecutable @("print", $script:PromptType, "--status=$script:ErrorCode", "--pswd=$cleanPSWD", "--execution-time=$script:ExecutionTime", "--stack-count=$stackCount", "--config=$env:POSH_THEME", "--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)
@ -477,33 +467,32 @@ Example:
$Function:prompt = $promptFunction
# set secondary prompt
Set-PSReadLineOption -ContinuationPrompt ((Start-Utf8Process $script:OMPExecutable @("print", "secondary", "--config=$env:POSH_THEME", "--shell=$script:ShellName")) -join "`n")
# legacy functions
function Enable-PoshTooltips {}
function Enable-PoshTransientPrompt {}
function Enable-PoshLineError {}
Set-PSReadLineOption -ContinuationPrompt ((Start-Utf8Process $global:_ompExecutable @("print", "secondary", "--config=$env:POSH_THEME", "--shell=$script:ShellName")) -join "`n")
# perform cleanup on removal so a new initialization in current session works
if (!$script:ConstrainedLanguageMode) {
$ExecutionContext.SessionState.Module.OnRemove += {
Remove-Item Function:Get-PoshStackCount
$Function:prompt = $script:OriginalPromptFunction
(Get-PSReadLineOption).ContinuationPrompt = $script:OriginalContinuationPrompt
(Get-PSReadLineOption).PromptText = $script:OriginalPromptText
if ((Get-PSReadLineKeyHandler Spacebar).Function -eq 'OhMyPoshSpaceKeyHandler') {
Remove-PSReadLineKeyHandler Spacebar
}
if ((Get-PSReadLineKeyHandler Enter).Function -eq 'OhMyPoshEnterKeyHandler') {
Set-PSReadLineKeyHandler Enter -Function AcceptLine
}
if ((Get-PSReadLineKeyHandler Ctrl+c).Function -eq 'OhMyPoshCtrlCKeyHandler') {
Set-PSReadLineKeyHandler Ctrl+c -Function CopyOrCancelLine
}
}
}
$notice = Start-Utf8Process $script:OMPExecutable @("notice")
$notice = Start-Utf8Process $global:_ompExecutable @("notice")
if ($notice) {
Write-Host $notice -NoNewline
}
@ -519,8 +508,3 @@ Example:
"prompt"
)
} | Import-Module -Global
if ("::AUTOUPGRADE::" -eq "true") {
& ::OMP:: upgrade
}

View file

@ -4,6 +4,7 @@ $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
@ -13,20 +14,12 @@ def get_command_context():
def posh_primary():
status, duration = get_command_context()
return $(::OMP:: print primary --config=@($POSH_THEME) --shell=xonsh --status=@(status) --execution-time=@(duration) --shell-version=@($POSH_SHELL_VERSION))
return $(@($POSH_EXECUTABLE) print primary --config=@($POSH_THEME) --shell=xonsh --status=@(status) --execution-time=@(duration) --shell-version=@($POSH_SHELL_VERSION))
def posh_right():
status, duration = get_command_context()
return $(::OMP:: print right --config=@($POSH_THEME) --shell=xonsh --status=@(status) --execution-time=@(duration) --shell-version=@($POSH_SHELL_VERSION))
return $(@($POSH_EXECUTABLE) print right --config=@($POSH_THEME) --shell=xonsh --status=@(status) --execution-time=@(duration) --shell-version=@($POSH_SHELL_VERSION))
$PROMPT = posh_primary
$RIGHT_PROMPT = posh_right
notice = """::UPGRADENOTICE::"""
if "::UPGRADE::" == "true":
print(notice)
if "::AUTOUPGRADE::" == "true":
::OMP:: upgrade

View file

@ -1,15 +1,14 @@
setenv POWERLINE_COMMAND "oh-my-posh";
setenv POSH_THEME "::CONFIG::";
setenv POSH_THEME ::CONFIG::;
setenv POSH_SHELL_VERSION "";
set POSH_COMMAND = ::OMP::;
set USER_PRECMD = "`alias precmd`";
set USER_POSTCMD = "`alias postcmd`";
set POSH_PRECMD = 'set POSH_CMD_STATUS = $status;set POSH_PATH = ::OMP::;set POSH_END_TIME = `$POSH_PATH get millis`;set POSH_DURATION = 0;if ( $POSH_START_TIME != -1 ) @ POSH_DURATION = $POSH_END_TIME - $POSH_START_TIME;set prompt = "`$POSH_PATH print primary --shell=tcsh --config=$POSH_THEME --status=$POSH_CMD_STATUS --execution-time=$POSH_DURATION`";set POSH_START_TIME = -1';
set POSH_POSTCMD = 'set POSH_START_TIME = `::OMP:: get millis`';
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 --config=$POSH_THEME --status=$POSH_CMD_STATUS --execution-time=$POSH_DURATION`";set POSH_START_TIME = -1';
set POSH_POSTCMD = 'set POSH_START_TIME = `$POSH_COMMAND get millis`';
alias precmd "$POSH_PRECMD;$USER_PRECMD";
alias postcmd "$POSH_POSTCMD;$USER_POSTCMD";
set POSH_START_TIME = `::OMP:: get millis`;
if ("::UPGRADE::" == "true") echo "::UPGRADENOTICE::";
if ("::AUTOUPGRADE::" == "true") ::OMP:: upgrade;
set POSH_START_TIME = `$POSH_COMMAND get millis`;

View file

@ -6,13 +6,19 @@ export CONDA_PROMPT_MODIFIER=false
export POSH_PROMPT_COUNT=0
export ZLE_RPROMPT_INDENT=0
_omp_executable=::OMP::
# switches to enable/disable features
_omp_cursor_positioning=0
_omp_ftcs_marks=0
# set secondary prompt
PS2="$(::OMP:: print secondary --config="$POSH_THEME" --shell=zsh)"
PS2="$(${_omp_executable} print secondary --config="$POSH_THEME" --shell=zsh)"
function _omp_set_cursor_position() {
# not supported in Midnight Commander
# see https://github.com/JanDeDobbeleer/oh-my-posh/issues/3415
if [[ "::CURSOR::" != "true" ]] || [[ -v MC_SID ]]; then
if [[ $_omp_cursor_positioning == 0 ]] || [[ -v MC_SID ]]; then
return
fi
@ -37,11 +43,11 @@ function set_poshcontext() {
}
function _omp_preexec() {
if [[ "::FTCS_MARKS::" = "true" ]]; then
if [[ $_omp_ftcs_marks == 0 ]]; then
printf "\033]133;C\007"
fi
_omp_start_time=$(::OMP:: get millis)
_omp_start_time=$(${_omp_executable} get millis)
}
function _omp_precmd() {
@ -52,7 +58,7 @@ function _omp_precmd() {
_omp_no_exit_code="true"
if [ $_omp_start_time ]; then
local omp_now=$(::OMP:: get millis --shell=zsh)
local omp_now=$(${_omp_executable} get millis --shell=zsh)
_omp_elapsed=$(($omp_now - $_omp_start_time))
_omp_no_exit_code="false"
fi
@ -67,7 +73,7 @@ function _omp_precmd() {
set_poshcontext
_omp_set_cursor_position
eval "$(::OMP:: print primary --config="$POSH_THEME" --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_executable} print primary --config="$POSH_THEME" --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
}
@ -110,7 +116,7 @@ function _omp_render_tooltip() {
fi
_omp_tooltip_command="$tooltip_command"
local tooltip=$(::OMP:: print tooltip --config="$POSH_THEME" --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_executable} print tooltip --config="$POSH_THEME" --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")
if [[ -z "$tooltip" ]]; then
return
fi
@ -129,7 +135,7 @@ function _omp_zle-line-init() {
(( $+zle_bracketed_paste )) && print -r -n - $zle_bracketed_paste[2]
_omp_tooltip_command=''
eval "$(::OMP:: print transient --config="$POSH_THEME" --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_executable} print transient --config="$POSH_THEME" --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")"
zle .reset-prompt
# Exit the shell if we receive EOT.
@ -187,19 +193,3 @@ function _omp_create_widget() {
# legacy functions
function enable_poshtooltips() {}
function enable_poshtransientprompt() {}
if [[ "::TOOLTIPS::" = "true" ]]; then
_omp_create_widget self-insert _omp_render_tooltip
fi
if [[ "::TRANSIENT::" = "true" ]]; then
_omp_create_widget zle-line-init _omp_zle-line-init
fi
if [[ "::UPGRADE::" = "true" ]]; then
echo "::UPGRADENOTICE::"
fi
if [[ "::AUTOUPGRADE::" = "true" ]]; then
::OMP:: upgrade
fi

21
src/shell/tcsh.go Normal file
View file

@ -0,0 +1,21 @@
package shell
import (
_ "embed"
)
//go:embed scripts/omp.tcsh
var tcshInit string
func (f Feature) Tcsh() Code {
switch f {
case Upgrade:
return "$POSH_COMMAND upgrade;"
case Notice:
return "$POSH_COMMAND notice;"
case PromptMark, RPrompt, PoshGit, Azure, LineError, Jobs, Tooltips, Transient, FTCSMarks, CursorPositioning:
fallthrough
default:
return ""
}
}

17
src/shell/tcsh_test.go Normal file
View file

@ -0,0 +1,17 @@
package shell
import (
"testing"
"github.com/stretchr/testify/assert"
)
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;`
assert.Equal(t, want, got)
}

21
src/shell/xonsh.go Normal file
View file

@ -0,0 +1,21 @@
package shell
import (
_ "embed"
)
//go:embed scripts/omp.py
var xonshInit string
func (f Feature) Xonsh() Code {
switch f {
case Upgrade:
return "@($POSH_EXECUTABLE) upgrade"
case Notice:
return "@($POSH_EXECUTABLE) notice"
case PromptMark, RPrompt, PoshGit, Azure, LineError, Jobs, Tooltips, Transient, CursorPositioning, FTCSMarks:
fallthrough
default:
return ""
}
}

17
src/shell/xonsh_test.go Normal file
View file

@ -0,0 +1,17 @@
package shell
import (
"testing"
"github.com/stretchr/testify/assert"
)
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`
assert.Equal(t, want, got)
}

29
src/shell/zsh.go Normal file
View file

@ -0,0 +1,29 @@
package shell
import (
_ "embed"
)
//go:embed scripts/omp.zsh
var zshInit string
func (f Feature) Zsh() Code {
switch f {
case CursorPositioning:
return "_omp_cursor_positioning=1"
case Tooltips:
return "_omp_create_widget self-insert _omp_render_tooltip"
case Transient:
return "_omp_create_widget zle-line-init _omp_zle-line-init"
case FTCSMarks:
return "_omp_ftcs_marks=1"
case Upgrade:
return unixUpgrade
case Notice:
return unixNotice
case PromptMark, RPrompt, PoshGit, Azure, LineError, Jobs:
fallthrough
default:
return ""
}
}

21
src/shell/zsh_test.go Normal file
View file

@ -0,0 +1,21 @@
package shell
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestZshFeatures(t *testing.T) {
got := allFeatures.Lines(ZSH).String("// these are the features")
want := `// these are the features
enable_poshtooltips
_omp_create_widget zle-line-init _omp_zle-line-init
_omp_ftcs_marks=1
$_omp_executable upgrade
$_omp_executable notice
_omp_cursor_positioning=1`
assert.Equal(t, want, got)
}

View file

@ -43,6 +43,7 @@ var (
"Templates",
"Var",
"Data",
"Jobs",
}
shell string