fix(shell): avoid unnecessary CLI calls for prompt repaint

This commit is contained in:
L. Yeung 2024-07-10 20:28:08 +08:00 committed by Jan De Dobbeleer
parent dbc5da5800
commit 66f1347357
5 changed files with 280 additions and 246 deletions

View file

@ -172,11 +172,11 @@ func (e *Engine) renderBlock(block *config.Block, cancelNewline bool) bool {
defer e.patchPowerShellBleed() defer e.patchPowerShellBleed()
// This is deprecated but we leave it in to not break configs // This is deprecated but we leave it in to not break configs
// It is encouraged to used "newline": true on block level // It is encouraged to use "newline": true on block level
// rather than the standalone the linebreak block // rather than the standalone linebreak block
if block.Type == config.LineBreak { if block.Type == config.LineBreak {
// do not print a newline to avoid a leading space // do not print a newline to avoid a leading space
// when we're printin the first primary prompt in // when we're printing the first primary prompt in
// the shell // the shell
if !cancelNewline { if !cancelNewline {
e.writeNewline() e.writeNewline()
@ -191,7 +191,7 @@ func (e *Engine) renderBlock(block *config.Block, cancelNewline bool) bool {
} }
// do not print a newline to avoid a leading space // do not print a newline to avoid a leading space
// when we're printin the first primary prompt in // when we're printing the first primary prompt in
// the shell // the shell
if block.Newline && !cancelNewline { if block.Newline && !cancelNewline {
e.writeNewline() e.writeNewline()

View file

@ -1,6 +1,7 @@
package prompt package prompt
import ( import (
"slices"
"strings" "strings"
"github.com/jandedobbeleer/oh-my-posh/src/config" "github.com/jandedobbeleer/oh-my-posh/src/config"
@ -9,6 +10,18 @@ import (
) )
func (e *Engine) Tooltip(tip string) string { func (e *Engine) Tooltip(tip string) string {
supportedShells := []string{
shell.ZSH,
shell.CMD,
shell.FISH,
shell.PWSH,
shell.PWSH5,
shell.GENERIC,
}
if !slices.Contains(supportedShells, e.Env.Shell()) {
return ""
}
tip = strings.Trim(tip, " ") tip = strings.Trim(tip, " ")
tooltips := make([]*config.Segment, 0, 1) tooltips := make([]*config.Segment, 0, 1)
@ -35,40 +48,29 @@ func (e *Engine) Tooltip(tip string) string {
Alignment: config.Right, Alignment: config.Right,
Segments: tooltips, Segments: tooltips,
} }
block.Init(e.Env)
if !block.Enabled() {
return ""
}
text, length := e.renderBlockSegments(block)
switch e.Env.Shell() { switch e.Env.Shell() {
case shell.ZSH, shell.CMD, shell.FISH, shell.GENERIC:
block.Init(e.Env)
if !block.Enabled() {
return ""
}
text, _ := e.renderBlockSegments(block)
return text
case shell.PWSH, shell.PWSH5: case shell.PWSH, shell.PWSH5:
if !block.Enabled() { e.rprompt = text
e.rpromptLength = length
e.currentLineLength = e.Env.Flags().Column
space, ok := e.canWriteRightBlock(true)
if !ok {
return "" return ""
} }
e.write(terminal.SaveCursorPosition())
consoleWidth, err := e.Env.TerminalWidth()
if err != nil || consoleWidth == 0 {
return ""
}
text, length := e.renderBlockSegments(block)
space := consoleWidth - e.Env.Flags().Column - length
if space <= 0 {
return ""
}
// clear from cursor to the end of the line in case a previous tooltip
// is cut off and partially preserved, if the new one is shorter
e.write(terminal.ClearAfter())
e.write(strings.Repeat(" ", space)) e.write(strings.Repeat(" ", space))
e.write(text) e.write(text)
e.write(terminal.RestoreCursorPosition())
return e.string() return e.string()
default:
return text
} }
return ""
} }
func (e *Engine) shouldInvokeWithTip(segment *config.Segment, tip string) bool { func (e *Engine) shouldInvokeWithTip(segment *config.Segment, tip string) bool {

View file

@ -1,17 +1,22 @@
set --export POSH_THEME ::CONFIG:: set --export POSH_THEME ::CONFIG::
set --export POSH_SHELL_VERSION $FISH_VERSION set --export POSH_SHELL_VERSION $FISH_VERSION
set --global POWERLINE_COMMAND "oh-my-posh" set --global POWERLINE_COMMAND oh-my-posh
set --global POSH_PID $fish_pid set --global POSH_PID $fish_pid
set --global CONDA_PROMPT_MODIFIER false set --global CONDA_PROMPT_MODIFIER false
set --global omp_tooltip_prompt "" set --global omp_tooltip_command ''
set --global has_omp_tooltip false set --global omp_current_rprompt ''
set --global omp_transient 0 set --global omp_transient false
# We use this to avoid unnecessary CLI calls for prompt repaint.
set --global omp_new_prompt true
# template function for context loading # template function for context loading
function set_poshcontext function set_poshcontext
return return
end 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 function fish_prompt
set --local omp_status_cache_temp $status set --local omp_status_cache_temp $status
set --local omp_pipestatus_cache_temp $pipestatus set --local omp_pipestatus_cache_temp $pipestatus
@ -19,11 +24,14 @@ function fish_prompt
# commandline --function repaint does not do this # commandline --function repaint does not do this
# see https://github.com/fish-shell/fish-shell/issues/8418 # see https://github.com/fish-shell/fish-shell/issues/8418
printf \e\[0J printf \e\[0J
if test "$omp_transient" = "1" 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:: 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 return
end
if test "$omp_new_prompt" = false
echo -n "$omp_current_prompt"
return
end end
set --global omp_status_cache $omp_status_cache_temp set --global omp_status_cache $omp_status_cache_temp
set --global omp_pipestatus_cache $omp_pipestatus_cache_temp set --global omp_pipestatus_cache $omp_pipestatus_cache_temp
set --global omp_stack_count (count $dirstack) set --global omp_stack_count (count $dirstack)
@ -31,22 +39,22 @@ function fish_prompt
set --global omp_no_exit_code false set --global omp_no_exit_code false
# check if variable set, < 3.2 case # check if variable set, < 3.2 case
if set --query omp_lastcommand; and test "$omp_lastcommand" = "" if set --query omp_lastcommand; and test -z "$omp_lastcommand"
set omp_duration 0 set omp_duration 0
set omp_no_exit_code true set omp_no_exit_code true
end end
# works with fish >=3.2 # works with fish >=3.2
if set --query omp_last_status_generation; and test "$omp_last_status_generation" = "$status_generation" if set --query omp_last_status_generation; and test "$omp_last_status_generation" = "$status_generation"
set omp_duration 0 set omp_duration 0
set omp_no_exit_code true set omp_no_exit_code true
else if test -z "$omp_last_status_generation" else if test -z "$omp_last_status_generation"
# first execution - $status_generation is 0, $omp_last_status_generation is empty # first execution - $status_generation is 0, $omp_last_status_generation is empty
set omp_no_exit_code true set omp_no_exit_code true
end end
if set --query status_generation if set --query status_generation
set --global --export omp_last_status_generation $status_generation set --global omp_last_status_generation $status_generation
end end
set_poshcontext set_poshcontext
@ -55,112 +63,146 @@ function fish_prompt
set --local omp_cleared false set --local omp_cleared false
set --local last_command (history search --max 1) set --local last_command (history search --max 1)
if test "$last_command" = "clear" if test "$last_command" = clear
set omp_cleared true set omp_cleared true
end end
::PROMPT_MARK:: ::PROMPT_MARK::
::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 # 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)
echo -n "$omp_current_prompt"
end end
function fish_right_prompt function fish_right_prompt
if test "$omp_transient" = "1" if test "$omp_transient" = true
echo -n "" set omp_transient false
set omp_transient 0 return
set has_omp_tooltip false
return
end end
if test -n "$omp_tooltip_prompt" # Repaint an existing right prompt.
echo -n $omp_tooltip_prompt if test "$omp_new_prompt" = false
set omp_tooltip_prompt "" echo -n "$omp_current_rprompt"
set has_omp_tooltip true return
return
end end
set has_omp_tooltip false set omp_new_prompt false
::OMP:: print right --config $POSH_THEME --shell fish --status $omp_status_cache --execution-time $omp_duration --stack-count $omp_stack_count --shell-version $FISH_VERSION 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 '')
echo -n "$omp_current_rprompt"
end end
function postexec_omp --on-event fish_postexec function postexec_omp --on-event fish_postexec
# works with fish <3.2 # works with fish <3.2
# pre and postexec not fired for empty command in fish >=3.2 # pre and postexec not fired for empty command in fish >=3.2
set --global --export omp_lastcommand $argv set --global omp_lastcommand $argv
end
# fix tooltip not resetting on SIGINT (ctrl+c)
function sigint_omp --on-signal INT
commandline --function repaint
end end
function preexec_omp --on-event fish_preexec function preexec_omp --on-event fish_preexec
if "::FTCS_MARKS::" = "true" if test "::FTCS_MARKS::" = true
echo -ne "\e]133;C\a" echo -ne "\e]133;C\a"
end end
end end
# perform cleanup so a new initialization in current session works # perform cleanup so a new initialization in current session works
if test "$(string match -e '_render_transient' $(bind \r --user 2>/dev/null))" != '' if test -n (bind \r --user 2>/dev/null | string match -e _omp_enter_key_handler)
bind -e \r bind -e \r -M default
bind -e \r -M insert
bind -e \r -M visual
end end
if test "$(string match -e '_render_tooltip' $(bind \x20 --user 2>/dev/null))" != '' if test -n (bind \n --user 2>/dev/null | string match -e _omp_enter_key_handler)
bind -e \x20 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
end end
# tooltip # tooltip
function _render_tooltip function _omp_space_key_handler
commandline --function expand-abbr commandline --function expand-abbr
commandline --insert " " commandline --insert ' '
# get the first word of command line as tip # Get the first word of command line as tip.
set omp_tooltip_command (commandline --current-buffer | string trim -l | string split --allow-empty -f1 ' ' | string collect) set --local tooltip_command (commandline --current-buffer | string trim -l | string split --allow-empty -f1 ' ' | string collect)
if not test -n "$omp_tooltip_command" # Ignore an empty/repeated tooltip command.
return if test -z "$tooltip_command" || test "$tooltip_command" = "$omp_tooltip_command"
end return
set omp_tooltip_prompt (::OMP:: print tooltip --config $POSH_THEME --shell fish --status $omp_status_cache --shell-version $FISH_VERSION --command $omp_tooltip_command)
if not test -n "$omp_tooltip_prompt"
if test "$has_omp_tooltip" = "true"
commandline --function repaint
end end
return set omp_tooltip_command $tooltip_command
end 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 '')
commandline --function repaint 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 end
if test "::TOOLTIPS::" = "true" if test "::TOOLTIPS::" = true
bind \x20 _render_tooltip -M default bind \x20 _omp_space_key_handler -M default
bind \x20 _render_tooltip -M insert bind \x20 _omp_space_key_handler -M insert
end end
# transient prompt # transient prompt
function _render_transient function _omp_enter_key_handler
if commandline --paging-mode if commandline --paging-mode
commandline --function accept-autosuggestion commandline --function accept-autosuggestion
return return
end end
set omp_transient 1 if commandline --is-valid || test -z (commandline --current-buffer | string trim -l | string collect)
commandline --function repaint set omp_new_prompt true
commandline --function execute set omp_tooltip_command ''
if test "::TRANSIENT::" = true
set omp_transient true
commandline --function repaint
end
end
commandline --function execute
end end
if test "::TRANSIENT::" = "true" function _omp_ctrl_c_key_handler
bind \r _render_transient -M default if test -z (commandline --current-buffer | string collect)
bind \r _render_transient -M insert return
bind \r _render_transient -M visual 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
set omp_transient true
commandline --function repaint
end
commandline --function cancel-commandline
commandline --function repaint
end end
bind \r _omp_enter_key_handler -M default
bind \r _omp_enter_key_handler -M insert
bind \r _omp_enter_key_handler -M visual
bind \n _omp_enter_key_handler -M default
bind \n _omp_enter_key_handler -M insert
bind \n _omp_enter_key_handler -M visual
bind \cc _omp_ctrl_c_key_handler -M default
bind \cc _omp_ctrl_c_key_handler -M insert
bind \cc _omp_ctrl_c_key_handler -M visual
# legacy functions # legacy functions
function enable_poshtooltips function enable_poshtooltips
return return
end end
function enable_poshtransientprompt function enable_poshtransientprompt
return return
end end
if test "::UPGRADE::" = "true" if test "::UPGRADE::" = true
echo "::UPGRADENOTICE::" echo "::UPGRADENOTICE::"
end end
if test "::AUTOUPGRADE::" = "true" if test "::AUTOUPGRADE::" = true
::OMP:: upgrade ::OMP:: upgrade
end end

View file

@ -14,20 +14,29 @@ function global:Get-PoshStackCount {
} }
New-Module -Name "oh-my-posh-core" -ScriptBlock { New-Module -Name "oh-my-posh-core" -ScriptBlock {
# Check `ConstrainedLanguage` mode.
$script:ConstrainedLanguageMode = $ExecutionContext.SessionState.LanguageMode -eq "ConstrainedLanguage"
# Prompt related backup.
$script:OriginalPromptFunction = $Function:prompt
$script:OriginalContinuationPrompt = (Get-PSReadLineOption).ContinuationPrompt
$script:OriginalPromptText = (Get-PSReadLineOption).PromptText
$script:NoExitCode = $true
$script:ErrorCode = 0 $script:ErrorCode = 0
$script:ExecutionTime = 0 $script:ExecutionTime = 0
$script:OMPExecutable = ::OMP:: $script:OMPExecutable = ::OMP::
$script:ShellName = "::SHELL::" $script:ShellName = "::SHELL::"
$script:PSVersion = $PSVersionTable.PSVersion.ToString() $script:PSVersion = $PSVersionTable.PSVersion.ToString()
$script:TransientPrompt = $false $script:TransientPrompt = $false
$script:ToolTipCommand = "" $script:TooltipCommand = ''
$env:POWERLINE_COMMAND = "oh-my-posh" $env:POWERLINE_COMMAND = "oh-my-posh"
$env:POSH_SHELL_VERSION = $script:PSVersion $env:POSH_SHELL_VERSION = $script:PSVersion
$env:POSH_PID = $PID $env:POSH_PID = $PID
$env:CONDA_PROMPT_MODIFIER = $false $env:CONDA_PROMPT_MODIFIER = $false
# set the default theme # set the default theme
if ((::CONFIG:: -ne '') -and (Test-Path -LiteralPath ::CONFIG::)) { if (::CONFIG:: -and (Test-Path -LiteralPath ::CONFIG::)) {
$env:POSH_THEME = (Resolve-Path -Path ::CONFIG::).ProviderPath $env:POSH_THEME = (Resolve-Path -Path ::CONFIG::).ProviderPath
} }
@ -56,7 +65,7 @@ New-Module -Name "oh-my-posh-core" -ScriptBlock {
[string[]]$Arguments = @() [string[]]$Arguments = @()
) )
if ($ExecutionContext.SessionState.LanguageMode -eq "ConstrainedLanguage") { if ($script:ConstrainedLanguageMode) {
$standardOut = Invoke-Expression "& `$FileName `$Arguments 2>&1" $standardOut = Invoke-Expression "& `$FileName `$Arguments 2>&1"
$standardOut -join "`n" $standardOut -join "`n"
return return
@ -99,12 +108,14 @@ New-Module -Name "oh-my-posh-core" -ScriptBlock {
} }
$StartInfo.CreateNoWindow = $true $StartInfo.CreateNoWindow = $true
[void]$Process.Start() [void]$Process.Start()
# we do this to remove a deadlock potential on Windows
# Remove deadlock potential on Windows.
$stdoutTask = $Process.StandardOutput.ReadToEndAsync() $stdoutTask = $Process.StandardOutput.ReadToEndAsync()
$stderrTask = $Process.StandardError.ReadToEndAsync() $stderrTask = $Process.StandardError.ReadToEndAsync()
$Process.WaitForExit() $Process.WaitForExit()
$stderr = $stderrTask.Result.Trim() $stderr = $stderrTask.Result.Trim()
if ($stderr -ne '') { if ($stderr) {
$Host.UI.WriteErrorLine($stderr) $Host.UI.WriteErrorLine($stderr)
} }
$stdoutTask.Result $stdoutTask.Result
@ -120,77 +131,79 @@ New-Module -Name "oh-my-posh-core" -ScriptBlock {
return $pswd return $pswd
} }
if (("::TOOLTIPS::" -eq "true") -and ($ExecutionContext.SessionState.LanguageMode -ne "ConstrainedLanguage")) { function Get-TerminalWidth {
Set-PSReadLineKeyHandler -Key Spacebar -BriefDescription 'OhMyPoshSpaceKeyHandler' -ScriptBlock { $terminalWidth = $Host.UI.RawUI.WindowSize.Width
[Microsoft.PowerShell.PSConsoleReadLine]::Insert(' ') # Set a sane default when the value can't be retrieved.
$command = $null if (-not $terminalWidth) {
[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$command, [ref]$null) return 0
$command = $command.TrimStart().Split(" ", 2) | Select-Object -First 1
# ignore an empty/repeated tip
if ($command -eq '' -or $command -eq $script:ToolTipCommand) {
return
}
$position = $host.UI.RawUI.CursorPosition
$terminalWidth = $Host.UI.RawUI.WindowSize.Width
$cleanPSWD = Get-CleanPSWD
$standardOut = @(Start-Utf8Process $script:OMPExecutable @("print", "tooltip", "--status=$script:ErrorCode", "--shell=$script:ShellName", "--pswd=$cleanPSWD", "--config=$env:POSH_THEME", "--command=$command", "--shell-version=$script:PSVersion", "--column=$($position.X)", "--terminal-width=$terminalWidth"))
# ignore an empty tooltip
if ($standardOut -eq '') {
return
}
Write-Host $standardOut -NoNewline
$host.UI.RawUI.CursorPosition = $position
# cache the tip command
$script:ToolTipCommand = $command
# we need this workaround to prevent the text after cursor from disappearing when the tooltip is rendered
[Microsoft.PowerShell.PSConsoleReadLine]::Insert(' ')
[Microsoft.PowerShell.PSConsoleReadLine]::Undo()
} }
$terminalWidth
} }
function Set-TransientPrompt { if (!$script:ConstrainedLanguageMode) {
$previousOutputEncoding = [Console]::OutputEncoding if ('::TOOLTIPS::' -eq 'true') {
$executingCommand = $false Set-PSReadLineKeyHandler -Key Spacebar -BriefDescription 'OhMyPoshSpaceKeyHandler' -ScriptBlock {
param([ConsoleKeyInfo]$key)
[Microsoft.PowerShell.PSConsoleReadLine]::SelfInsert($key)
try {
$command = ''
[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
}
$script:TooltipCommand = $command
$column = $Host.UI.RawUI.CursorPosition.X
$terminalWidth = Get-TerminalWidth
$cleanPSWD = Get-CleanPSWD
$stackCount = global:Get-PoshStackCount
$standardOut = (Start-Utf8Process $script: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")) -join ''
if (!$standardOut) {
return
}
Write-Host $standardOut -NoNewline
try { # Workaround to prevent the text after cursor from disappearing when the tooltip is printed.
$parseErrors = $null [Microsoft.PowerShell.PSConsoleReadLine]::Insert(' ')
[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$null, [ref]$null, [ref]$parseErrors, [ref]$null) [Microsoft.PowerShell.PSConsoleReadLine]::Undo()
if ($parseErrors.Count -eq 0) { }
$executingCommand = $true finally {}
}
}
function Set-TransientPrompt {
$previousOutputEncoding = [Console]::OutputEncoding
try {
$script:TransientPrompt = $true $script:TransientPrompt = $true
[Console]::OutputEncoding = [Text.Encoding]::UTF8 [Console]::OutputEncoding = [Text.Encoding]::UTF8
[Microsoft.PowerShell.PSConsoleReadLine]::InvokePrompt() [Microsoft.PowerShell.PSConsoleReadLine]::InvokePrompt()
} }
} finally {
finally { [Console]::OutputEncoding = $previousOutputEncoding
# If PSReadline is set to display suggestion list, this workaround is needed to clear the buffer below
# before accepting the current commandline. The max amount of items in the list is 10, so 12 lines
# are cleared (10 + 1 more for the prompt + 1 more for current commandline).
if ((Get-PSReadLineOption).PredictionViewStyle -eq 'ListView') {
$terminalHeight = $Host.UI.RawUI.WindowSize.Height
# only do this on an valid value
if ([int]$terminalHeight -gt 0) {
[Microsoft.PowerShell.PSConsoleReadLine]::Insert("`n" * [System.Math]::Min($terminalHeight - $Host.UI.RawUI.CursorPosition.Y - 1, 12))
[Microsoft.PowerShell.PSConsoleReadLine]::Undo()
}
} }
[Console]::OutputEncoding = $previousOutputEncoding
} }
return $executingCommand
}
if (("::TRANSIENT::" -eq "true") -and ($ExecutionContext.SessionState.LanguageMode -ne "ConstrainedLanguage")) {
Set-PSReadLineKeyHandler -Key Enter -BriefDescription 'OhMyPoshEnterKeyHandler' -ScriptBlock { Set-PSReadLineKeyHandler -Key Enter -BriefDescription 'OhMyPoshEnterKeyHandler' -ScriptBlock {
try { try {
$executingCommand = Set-TransientPrompt $parseErrors = $null
[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$null, [ref]$null, [ref]$parseErrors, [ref]$null)
$executingCommand = $parseErrors.Count -eq 0
if ($executingCommand) {
$script:TooltipCommand = ''
if ('::TRANSIENT::' -eq 'true') {
Set-TransientPrompt
}
}
}
finally {
[Microsoft.PowerShell.PSConsoleReadLine]::AcceptLine() [Microsoft.PowerShell.PSConsoleReadLine]::AcceptLine()
# Write FTCS_COMMAND_EXECUTED after accepting the input - it should still happen before execution if (('::FTCS_MARKS::' -eq 'true') -and $executingCommand) {
if (("::FTCS_MARKS::" -eq "true") -and $executingCommand) { # Write FTCS_COMMAND_EXECUTED after accepting the input - it should still happen before execution
Write-Host "$([char]0x1b)]133;C`a" -NoNewline Write-Host "$([char]0x1b)]133;C`a" -NoNewline
} }
} }
finally {}
} }
Set-PSReadLineKeyHandler -Key Ctrl+c -BriefDescription 'OhMyPoshCtrlCKeyHandler' -ScriptBlock { Set-PSReadLineKeyHandler -Key Ctrl+c -BriefDescription 'OhMyPoshCtrlCKeyHandler' -ScriptBlock {
try { try {
@ -198,37 +211,21 @@ New-Module -Name "oh-my-posh-core" -ScriptBlock {
[Microsoft.PowerShell.PSConsoleReadLine]::GetSelectionState([ref]$start, [ref]$null) [Microsoft.PowerShell.PSConsoleReadLine]::GetSelectionState([ref]$start, [ref]$null)
# only render a transient prompt when no text is selected # only render a transient prompt when no text is selected
if ($start -eq -1) { if ($start -eq -1) {
Set-TransientPrompt $script:TooltipCommand = ''
if ('::TRANSIENT::' -eq 'true') {
Set-TransientPrompt
}
} }
} }
finally {} finally {
[Microsoft.PowerShell.PSConsoleReadLine]::CopyOrCancelLine()
[Microsoft.PowerShell.PSConsoleReadLine]::CopyOrCancelLine()
}
}
if (("::FTCS_MARKS::" -eq "true") -and ("::TRANSIENT::" -ne "true") -and ($ExecutionContext.SessionState.LanguageMode -ne "ConstrainedLanguage")) {
Set-PSReadLineKeyHandler -Key Enter -BriefDescription 'OhMyPoshEnterKeyHandler' -ScriptBlock {
$executingCommand = $false
try {
$parseErrors = $null
[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$null, [ref]$null, [ref]$parseErrors, [ref]$null)
$executingCommand = $parseErrors.Count -eq 0
}
finally {}
[Microsoft.PowerShell.PSConsoleReadLine]::AcceptLine()
# Write FTCS_COMMAND_EXECUTED after accepting the input - it should still happen before execution
if ($executingCommand) {
Write-Host "$([char]0x1b)]133;C`a" -NoNewline
} }
} }
} }
if ("::ERROR_LINE::" -eq "true") { if ("::ERROR_LINE::" -eq "true") {
$validLine = @(Start-Utf8Process $script:OMPExecutable @("print", "valid", "--config=$env:POSH_THEME", "--shell=$script:ShellName")) -join "`n" $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" $errorLine = (Start-Utf8Process $script:OMPExecutable @("print", "error", "--config=$env:POSH_THEME", "--shell=$script:ShellName")) -join "`n"
Set-PSReadLineOption -PromptText $validLine, $errorLine Set-PSReadLineOption -PromptText $validLine, $errorLine
} }
@ -267,9 +264,9 @@ New-Module -Name "oh-my-posh-core" -ScriptBlock {
$Format = 'json' $Format = 'json'
) )
$configString = @(Start-Utf8Process $script:OMPExecutable @("config", "export", "--config=$env:POSH_THEME", "--format=$Format")) $configString = Start-Utf8Process $script:OMPExecutable @("config", "export", "--config=$env:POSH_THEME", "--format=$Format")
# if no path, copy to clipboard by default # if no path, copy to clipboard by default
if ('' -ne $FilePath) { if ($FilePath) {
# https://stackoverflow.com/questions/3038337/powershell-resolve-path-that-might-not-exist # https://stackoverflow.com/questions/3038337/powershell-resolve-path-that-might-not-exist
$FilePath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($FilePath) $FilePath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($FilePath)
[IO.File]::WriteAllLines($FilePath, $configString) [IO.File]::WriteAllLines($FilePath, $configString)
@ -288,7 +285,7 @@ New-Module -Name "oh-my-posh-core" -ScriptBlock {
[string]$name [string]$name
) )
$esc = [char]27 $esc = [char]27
if ("" -eq $name) { if (!$name) {
# if name not set, uri is used as the name of the hyperlink # if name not set, uri is used as the name of the hyperlink
$name = $uri $name = $uri
} }
@ -335,7 +332,7 @@ New-Module -Name "oh-my-posh-core" -ScriptBlock {
$cleanPSWD = Get-CleanPSWD $cleanPSWD = Get-CleanPSWD
$themes | ForEach-Object -Process { $themes | ForEach-Object -Process {
Write-Host "Theme: $(Get-FileHyperlink -uri $_.FullName -Name ($_.BaseName -replace '\.omp$', ''))`n" 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 $script:OMPExecutable @("print", "primary", "--config=$($_.FullName)", "--pswd=$cleanPSWD", "--shell=$script:ShellName")
Write-Host "`n" Write-Host "`n"
} }
} }
@ -400,7 +397,7 @@ Example:
} }
} }
function prompt { $promptFunction = {
# store the orignal last command execution status # store the orignal last command execution status
if ($global:NVS_ORIGINAL_LASTEXECUTIONSTATUS -is [bool]) { if ($global:NVS_ORIGINAL_LASTEXECUTIONSTATUS -is [bool]) {
# make it compatible with NVS auto-switching, if enabled # make it compatible with NVS auto-switching, if enabled
@ -419,41 +416,30 @@ Example:
$cleanPSWD = Get-CleanPSWD $cleanPSWD = Get-CleanPSWD
$stackCount = global:Get-PoshStackCount $stackCount = global:Get-PoshStackCount
Set-PoshContext Set-PoshContext
$terminalWidth = $Host.UI.RawUI.WindowSize.Width $terminalWidth = Get-TerminalWidth
# set a sane default when the value can't be retrieved
if (-not $terminalWidth) {
$terminalWidth = 0
}
# in some cases we have an empty $script:NoExitCode
# this is a workaround to make sure we always have a value
# see https://github.com/JanDeDobbeleer/oh-my-posh/issues/4128
if ($null -eq $script:NoExitCode) {
$script:NoExitCode = $true
}
# set the cursor positions, they are zero based so align with other platforms # 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_LINE = $Host.UI.RawUI.CursorPosition.Y + 1
$env:POSH_CURSOR_COLUMN = $Host.UI.RawUI.CursorPosition.X + 1 $env:POSH_CURSOR_COLUMN = $Host.UI.RawUI.CursorPosition.X + 1
$standardOut = @(Start-Utf8Process $script: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")) $standardOut = Start-Utf8Process $script: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")
# make sure PSReadLine knows if we have a multiline prompt # make sure PSReadLine knows if we have a multiline prompt
Set-PSReadLineOption -ExtraPromptLineCount (($standardOut | Measure-Object -Line).Lines - 1) Set-PSReadLineOption -ExtraPromptLineCount (($standardOut | Measure-Object -Line).Lines - 1)
# the output can be multiline, joining these ensures proper rendering by adding line breaks with `n
# The output can be multi-line, joining them ensures proper rendering.
$standardOut -join "`n" $standardOut -join "`n"
# remove any posh-git status # remove any posh-git status
$env:POSH_GIT_STATUS = $null $env:POSH_GIT_STATUS = $null
# remove cached tip command
$script:ToolTipCommand = ""
# restore the orignal last exit code # restore the orignal last exit code
$global:LASTEXITCODE = $script:OriginalLastExitCode $global:LASTEXITCODE = $script:OriginalLastExitCode
} }
$Function:prompt = $promptFunction
# set secondary prompt # set secondary prompt
Set-PSReadLineOption -ContinuationPrompt (@(Start-Utf8Process $script:OMPExecutable @("print", "secondary", "--config=$env:POSH_THEME", "--shell=$script:ShellName")) -join "`n") Set-PSReadLineOption -ContinuationPrompt ((Start-Utf8Process $script:OMPExecutable @("print", "secondary", "--config=$env:POSH_THEME", "--shell=$script:ShellName")) -join "`n")
# legacy functions # legacy functions
function Enable-PoshTooltips {} function Enable-PoshTooltips {}
@ -461,21 +447,25 @@ Example:
function Enable-PoshLineError {} function Enable-PoshLineError {}
# perform cleanup on removal so a new initialization in current session works # perform cleanup on removal so a new initialization in current session works
if ($ExecutionContext.SessionState.LanguageMode -ne "ConstrainedLanguage") { if (!$script:ConstrainedLanguageMode) {
$ExecutionContext.SessionState.Module.OnRemove += { $ExecutionContext.SessionState.Module.OnRemove += {
if ((Get-PSReadLineKeyHandler -Key Spacebar).Function -eq 'OhMyPoshSpaceKeyHandler') { Remove-Item Function:Get-PoshStackCount
Remove-PSReadLineKeyHandler -Key Spacebar $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 -Key Enter).Function -eq 'OhMyPoshEnterKeyHandler') { if ((Get-PSReadLineKeyHandler Enter).Function -eq 'OhMyPoshEnterKeyHandler') {
Set-PSReadLineKeyHandler -Key Enter -Function AcceptLine Set-PSReadLineKeyHandler Enter -Function AcceptLine
} }
if ((Get-PSReadLineKeyHandler -Key Ctrl+c).Function -eq 'OhMyPoshCtrlCKeyHandler') { if ((Get-PSReadLineKeyHandler Ctrl+c).Function -eq 'OhMyPoshCtrlCKeyHandler') {
Set-PSReadLineKeyHandler -Key Ctrl+c -Function CopyOrCancelLine Set-PSReadLineKeyHandler Ctrl+c -Function CopyOrCancelLine
} }
} }
} }
$notice = @(Start-Utf8Process $script:OMPExecutable @("notice")) $notice = Start-Utf8Process $script:OMPExecutable @("notice")
if ($notice) { if ($notice) {
Write-Host $notice -NoNewline Write-Host $notice -NoNewline
} }
@ -488,7 +478,6 @@ Example:
"Export-PoshTheme" "Export-PoshTheme"
"Get-PoshThemes" "Get-PoshThemes"
"Start-Utf8Process" "Start-Utf8Process"
"prompt"
) )
} | Import-Module -Global } | Import-Module -Global

View file

@ -20,7 +20,7 @@ function _set_posh_cursor_position() {
stty raw -echo min 0 stty raw -echo min 0
local pos local pos
echo -en "\033[6n" > /dev/tty echo -en "\033[6n" >/dev/tty
read -r -d R pos read -r -d R pos
pos=${pos:2} # strip off the esc-[ pos=${pos:2} # strip off the esc-[
local parts=(${(s:;:)pos}) local parts=(${(s:;:)pos})
@ -44,24 +44,24 @@ function prompt_ohmyposh_preexec() {
} }
function prompt_ohmyposh_precmd() { function prompt_ohmyposh_precmd() {
omp_last_error=$? omp_status_cache=$?
local pipeStatus=(${pipestatus[@]}) omp_pipestatus_cache=(${pipestatus[@]})
omp_stack_count=${#dirstack[@]} omp_stack_count=${#dirstack[@]}
omp_elapsed=-1 omp_elapsed=-1
no_exit_code="true" omp_no_exit_code="true"
if [ $omp_start_time ]; then if [ $omp_start_time ]; then
local omp_now=$(::OMP:: get millis --shell=zsh) local omp_now=$(::OMP:: get millis --shell=zsh)
omp_elapsed=$(($omp_now-$omp_start_time)) omp_elapsed=$(($omp_now - $omp_start_time))
no_exit_code="false" omp_no_exit_code="false"
fi fi
if [[ "${pipeStatus[-1]}" != "$omp_last_error" ]]; then if [[ "${omp_pipestatus_cache[-1]}" != "$omp_status_cache" ]]; then
pipeStatus=("$omp_last_error") omp_pipestatus_cache=("$omp_status_cache")
fi fi
count=$((POSH_PROMPT_COUNT+1)) count=$((POSH_PROMPT_COUNT + 1))
export POSH_PROMPT_COUNT=$count export POSH_PROMPT_COUNT=$count
set_poshcontext set_poshcontext
_set_posh_cursor_position _set_posh_cursor_position
eval "$(::OMP:: print primary --config="$POSH_THEME" --status="$omp_last_error" --pipestatus="${pipeStatus[*]}" --execution-time="$omp_elapsed" --stack-count="$omp_stack_count" --eval --shell=zsh --shell-version="$ZSH_VERSION" --no-status="$no_exit_code")" 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")"
unset omp_start_time unset omp_start_time
} }
@ -82,7 +82,7 @@ function _posh-tooltip() {
# https://github.com/zsh-users/zsh-autosuggestions - clear suggestion to avoid keeping it after the newly inserted space # https://github.com/zsh-users/zsh-autosuggestions - clear suggestion to avoid keeping it after the newly inserted space
if [[ "$(zle -lL autosuggest-clear)" ]]; then if [[ "$(zle -lL autosuggest-clear)" ]]; then
# only if suggestions not disabled (variable not set) # only if suggestions not disabled (variable not set)
if ! [[ -v _ZSH_AUTOSUGGEST_DISABLED ]]; then if [[ ! -v _ZSH_AUTOSUGGEST_DISABLED ]]; then
zle autosuggest-clear zle autosuggest-clear
fi fi
fi fi
@ -90,19 +90,19 @@ function _posh-tooltip() {
# https://github.com/zsh-users/zsh-autosuggestions - fetch new suggestion after the space # https://github.com/zsh-users/zsh-autosuggestions - fetch new suggestion after the space
if [[ "$(zle -lL autosuggest-fetch)" ]]; then if [[ "$(zle -lL autosuggest-fetch)" ]]; then
# only if suggestions not disabled (variable not set) # only if suggestions not disabled (variable not set)
if ! [[ -v _ZSH_AUTOSUGGEST_DISABLED ]]; then if [[ ! -v _ZSH_AUTOSUGGEST_DISABLED ]]; then
zle autosuggest-fetch zle autosuggest-fetch
fi fi
fi fi
# get the first word of command line as tip # Get the first word of command line as tip.
local tip=${${(MS)BUFFER##[[:graph:]]*}%%[[:space:]]*} local tooltip_command=${${(MS)BUFFER##[[:graph:]]*}%%[[:space:]]*}
# ignore an empty tip # Ignore an empty/repeated tooltip command.
if [[ -z "$tip" ]]; then if [[ -z "$tooltip_command" ]] || [[ "$tooltip_command" = "$omp_tooltip_command" ]]; then
return return
fi fi
local tooltip=$(::OMP:: print tooltip --config="$POSH_THEME" --shell=zsh --status="$omp_last_error" --command="$tip" --shell-version="$ZSH_VERSION") omp_tooltip_command="$tooltip_command"
# ignore an empty tooltip 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")
if [[ -z "$tooltip" ]]; then if [[ -z "$tooltip" ]]; then
return return
fi fi
@ -119,7 +119,8 @@ function _posh-zle-line-init() {
local -i ret=$? local -i ret=$?
(( $+zle_bracketed_paste )) && print -r -n - $zle_bracketed_paste[2] (( $+zle_bracketed_paste )) && print -r -n - $zle_bracketed_paste[2]
eval "$(::OMP:: print transient --status="$omp_last_error" --execution-time="$omp_elapsed" --stack-count="$omp_stack_count" --config="$POSH_THEME" --eval --shell=zsh --shell-version="$ZSH_VERSION" --no-status="$no_exit_code")" 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")"
zle .reset-prompt zle .reset-prompt
# Exit the shell if we receive EOT. # Exit the shell if we receive EOT.