T34: prompts and commands carry OSC 133 marks: the tap reads every OSC body and hands 133's marks to the session, which keeps its last command's status; zsh, bash, fish and PowerShell mark what the shell leaves unmarked, and the user's prompt still sees the status
This commit is contained in:
18 files changed
+359
-88
No files matched your search
@@ -1,4 +1,4 @@
|
||||
# ELYTerminal: load the usual startup files, report folder and title.
|
||||
# ELYTerminal: load the usual startup files, report folder and title, mark prompts and commands.
|
||||
if [ -n "${ELY_BASH_LOGIN-}" ]; then
|
||||
builtin unset ELY_BASH_LOGIN
|
||||
[ -r /etc/profile ] && builtin source /etc/profile
|
||||
@@ -12,8 +12,29 @@ if [ -n "${ELY_BASH_LOGIN-}" ]; then
|
||||
else
|
||||
[ -r ~/.bashrc ] && builtin source ~/.bashrc
|
||||
fi
|
||||
_ely_ran=
|
||||
_ely_armed=
|
||||
_ely_ps0=
|
||||
if (( BASH_VERSINFO[0] * 100 + BASH_VERSINFO[1] >= 404 )); then
|
||||
# PS0 runs in the shell, so its subscript can note that a command ran.
|
||||
_ely_ps0='${_ely_note[_ely_ran=1]-}\e]133;C\a'
|
||||
elif [ -z "$(builtin trap -p DEBUG)" ]; then
|
||||
_ely_debug() {
|
||||
[ -n "$_ely_armed" ] || builtin return 0
|
||||
case $BASH_COMMAND in _ely_*) builtin return 0 ;; esac
|
||||
_ely_armed=
|
||||
_ely_ran=1
|
||||
builtin printf '\e]133;C\a'
|
||||
}
|
||||
builtin trap '_ely_debug' DEBUG
|
||||
fi
|
||||
_ely_prompt() {
|
||||
builtin local ret=$? LC_ALL=C char url= i title
|
||||
_ely_armed=
|
||||
if [ -n "$_ely_ran" ]; then
|
||||
builtin printf '\e]133;D;%s\a' "$ret"
|
||||
_ely_ran=
|
||||
fi
|
||||
for (( i = 0; i < ${#PWD}; i++ )); do
|
||||
char=${PWD:i:1}
|
||||
case $char in
|
||||
@@ -25,4 +46,12 @@ _ely_prompt() {
|
||||
builtin printf '\e]7;file://%s\a\e]2;%s\a' "$url" "${title//[[:cntrl:]]/}"
|
||||
builtin return $ret
|
||||
}
|
||||
PROMPT_COMMAND="_ely_prompt${PROMPT_COMMAND:+;$PROMPT_COMMAND}"
|
||||
# Last, after anything that sets PS1 or PS0.
|
||||
_ely_mark() {
|
||||
[[ $PS1 == *'\e]133;A'* ]] || PS1='\[\e]133;A\a\]'"$PS1"'\[\e]133;B\a\]'
|
||||
if [ -n "$_ely_ps0" ] && builtin shopt -q promptvars && [[ ${PS0-} != *"$_ely_ps0"* ]]; then
|
||||
PS0=$_ely_ps0${PS0-}
|
||||
fi
|
||||
_ely_armed=1
|
||||
}
|
||||
PROMPT_COMMAND="_ely_prompt${PROMPT_COMMAND:+;$PROMPT_COMMAND};_ely_mark"
|
||||
@@ -1,4 +1,4 @@
|
||||
# ELYTerminal: restore XDG_DATA_DIRS, report the folder; fish_title keeps the title.
|
||||
# ELYTerminal: restore XDG_DATA_DIRS, report the folder, mark what this fish leaves unmarked; fish_title keeps the title.
|
||||
if test -n "$ELY_FISH_XDG_DATA_DIRS"
|
||||
set -gx XDG_DATA_DIRS $ELY_FISH_XDG_DATA_DIRS
|
||||
else
|
||||
@@ -9,4 +9,33 @@ if status is-interactive
|
||||
function _ely_prompt --on-event fish_prompt
|
||||
printf '\e]7;file://%s\a' (string escape --style=url -- $PWD)
|
||||
end
|
||||
# fish 4.3 marks its own prompts and commands; 4.0 all but the prompt's end; older ones none.
|
||||
set -l v (string split ' ' -- (string replace -r '^(\d+)\.(\d+).*' '$1 $2' -- $version))
|
||||
if test $v[1] -lt 4
|
||||
set -g _ely_marks A B
|
||||
function _ely_preexec --on-event fish_preexec
|
||||
printf '\e]133;C\a'
|
||||
end
|
||||
function _ely_postexec --on-event fish_postexec
|
||||
printf '\e]133;D;%s\a' $status
|
||||
end
|
||||
else if test $v[1] -eq 4 -a $v[2] -lt 3
|
||||
set -g _ely_marks B
|
||||
end
|
||||
if set -q _ely_marks
|
||||
# The user's prompt loads late and may change; the marks go around the one shown.
|
||||
function _ely_wrap --on-event fish_prompt
|
||||
if functions -q _ely_user_prompt; and string match -q '*_ely_user_prompt*' -- (functions fish_prompt)
|
||||
return
|
||||
end
|
||||
functions -q _ely_user_prompt; and functions -e _ely_user_prompt
|
||||
functions -c fish_prompt _ely_user_prompt
|
||||
function fish_prompt
|
||||
# First, so the user's prompt sees the command's status.
|
||||
set -l text (_ely_user_prompt | string collect -N)
|
||||
contains A $_ely_marks; and printf '\e]133;A\a'
|
||||
printf '%s\e]133;B\a' "$text"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,6 +1,23 @@
|
||||
# ELYTerminal: wrap the prompt to report folder and title.
|
||||
# ELYTerminal: wrap the prompt to report folder and title and to mark prompts and commands.
|
||||
$global:ElyPrompt = $function:prompt
|
||||
$global:ElyRan = $false
|
||||
function global:prompt {
|
||||
# First: every statement sets $? again.
|
||||
$ok = $?
|
||||
$esc, $bel = [char]27, [char]7
|
||||
if ($global:ElyRan) {
|
||||
$code = 1
|
||||
if ($ok) { $code = 0 }
|
||||
elseif ($Error.Count -eq 0 -or [object]::ReferenceEquals($Error[0], $global:ElyError)) {
|
||||
# No new error: a native program failed. Strict mode refuses an unset variable.
|
||||
$code = Get-Variable LASTEXITCODE -Scope Global -ValueOnly -ErrorAction Ignore
|
||||
}
|
||||
[Console]::Write("$esc]133;D;$code$bel")
|
||||
$global:ElyRan = $false
|
||||
}
|
||||
[Console]::Write("$esc]133;A$bel")
|
||||
# The user's prompt sees a failed command's $? still.
|
||||
if (-not $ok) { Write-Error '' -ErrorAction Ignore }
|
||||
$text = & $global:ElyPrompt
|
||||
$folder = $executionContext.SessionState.Path.CurrentLocation
|
||||
if ($folder.Provider.Name -eq 'FileSystem') {
|
||||
@@ -8,8 +25,20 @@ function global:prompt {
|
||||
$path = ($parts | ForEach-Object { [Uri]::EscapeDataString($_) }) -join '/'
|
||||
if (-not $path.StartsWith('/')) { $path = "/$path" }
|
||||
$leaf = (Split-Path -Leaf $folder.ProviderPath) -replace '\p{Cc}', ''
|
||||
$esc, $bel = [char]27, [char]7
|
||||
[Console]::Write("$esc]7;file://$path$bel$esc]2;$leaf$bel")
|
||||
}
|
||||
$text
|
||||
"$text$esc]133;B$bel"
|
||||
}
|
||||
# A command starts once PSReadLine hands over a line that is not blank.
|
||||
if (Test-Path Function:\PSConsoleHostReadLine) {
|
||||
$global:ElyReadLine = $function:PSConsoleHostReadLine
|
||||
function global:PSConsoleHostReadLine {
|
||||
$line = & $global:ElyReadLine
|
||||
if (-not [string]::IsNullOrWhiteSpace($line)) {
|
||||
$global:ElyRan = $true
|
||||
$global:ElyError = if ($Error.Count) { $Error[0] }
|
||||
[Console]::Write("$([char]27)]133;C$([char]7)")
|
||||
}
|
||||
$line
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
# ELYTerminal: restore ZDOTDIR, load the user's .zshenv, report folder and title.
|
||||
# ELYTerminal: restore ZDOTDIR, load the user's .zshenv, report folder and title, mark prompts and commands.
|
||||
if [[ -n "${ELY_ZSH_ZDOTDIR+set}" ]]; then
|
||||
ZDOTDIR=$ELY_ZSH_ZDOTDIR
|
||||
builtin unset ELY_ZSH_ZDOTDIR
|
||||
@@ -7,8 +7,13 @@ else
|
||||
fi
|
||||
[[ -r "${ZDOTDIR:-$HOME}/.zshenv" ]] && builtin source "${ZDOTDIR:-$HOME}/.zshenv"
|
||||
if [[ -o interactive ]]; then
|
||||
_ely_ran=
|
||||
_ely_precmd() {
|
||||
builtin local ret=$? LC_ALL=C char url=
|
||||
if [[ -n $_ely_ran ]]; then
|
||||
builtin printf '\e]133;D;%s\a' $ret
|
||||
_ely_ran=
|
||||
fi
|
||||
for char in ${(s::)PWD}; do
|
||||
case $char in
|
||||
[[:cntrl:]]|%) builtin printf -v char '%%%02X' "'$char" ;;
|
||||
@@ -16,12 +21,21 @@ if [[ -o interactive ]]; then
|
||||
url+=$char
|
||||
done
|
||||
builtin printf '\e]7;file://%s\a\e]2;%s\a' "$url" "${${${PWD:t}:-/}//[[:cntrl:]]/}"
|
||||
# Themes set PS1 in later hooks; from the next prompt the marks go on after them.
|
||||
precmd_functions=(${precmd_functions:#_ely_mark} _ely_mark)
|
||||
builtin return ret
|
||||
}
|
||||
_ely_mark() {
|
||||
if [[ -o prompt_percent && $PS1 != *$'\e]133;A'* ]]; then
|
||||
PS1=$'%{\e]133;A\a%}'$PS1$'%{\e]133;B\a%}'
|
||||
fi
|
||||
}
|
||||
_ely_preexec() {
|
||||
builtin printf '\e]2;%s\a' "${1//[[:cntrl:]]/}"
|
||||
_ely_ran=1
|
||||
builtin printf '\e]133;C\a\e]2;%s\a' "${1//[[:cntrl:]]/}"
|
||||
}
|
||||
builtin autoload -Uz add-zsh-hook
|
||||
add-zsh-hook precmd _ely_precmd
|
||||
add-zsh-hook precmd _ely_mark
|
||||
add-zsh-hook preexec _ely_preexec
|
||||
fi
|
||||
Reference in new issue
Block a user