fix(cli): preserve literal theme browser output

Keep backslash sequences literal and enable prompt-percent rendering independently of the parent shell options. Add PTY coverage for both fixes and verify EXIT-hook preservation through the public CLI.
This commit is contained in:
Robby Russell
2026-09-09 06:13:21 -07:00
parent 3bf157dc77
commit 76439de772
2 changed files with 59 additions and 3 deletions
+2 -2
View File
@@ -2,7 +2,7 @@
# two-line data handoff, while all terminal I/O goes through a private tty fd.
function _omz_theme_browser() (
emulate -L zsh
setopt extendedglob
setopt extendedglob promptpercent
# Displayed names, filter text, and worker output are data, not prompt code.
unsetopt promptsubst
zmodload zsh/terminfo && zmodload zsh/zpty && zmodload zsh/system && zmodload zsh/datetime || return 1
@@ -46,7 +46,7 @@ function _omz_theme_browser() (
done
formatted+=$text
echoti cup $row 0 >&$tty
print -Pn -u $tty -- "%${width}<..<${formatted}%<<%f%k%b"
print -Prn -u $tty -- "%${width}<..<${formatted}%<<%f%k%b"
(( row++ ))
}
+57 -1
View File
@@ -11,6 +11,7 @@ if [[ $1 == --child ]]; then
source "$ZSH/lib/cli.zsh"
# Match normal OMZ sessions without sourcing any user startup/configuration.
setopt promptsubst
[[ -z $BROWSER_TEST_NO_PROMPT_PERCENT ]] || unsetopt promptpercent
print -r -- "LAZY:$+functions[_omz_theme_browser]:$+functions[_omz_theme_preview]"
if [[ $2 == completion ]]; then
# Capture the real completion function's candidates without loading the
@@ -57,8 +58,18 @@ if [[ $1 == --child ]]; then
# An interactive script otherwise aborts on SIGINT instead of returning to
# the next prompt as an interactive command loop would.
trap ':' INT
case $BROWSER_TEST_EXIT_TRAP in
function) function TRAPEXIT { print -r -- EXIT_HOOK; } ;;
string) trap 'print -r -- EXIT_HOOK' EXIT ;;
esac
typeset original_exit_trap=${functions[TRAPEXIT]-}
omz theme browse "$2"
result=$?
if [[ -n $BROWSER_TEST_EXIT_TRAP ]]; then
[[ ${functions[TRAPEXIT]-} == "$original_exit_trap" ]] && print -r -- EXIT_FUNCTION_UNCHANGED
trap
trap - EXIT
fi
typeset restored_tty=$(stty -a)
restored_tty=${${restored_tty//-pendin/}//pendin/}
if [[ $restored_tty == "$original_tty" ]]; then
@@ -114,6 +125,8 @@ typeset preview_probe='$(print HIT > $TEST_SCRATCH/preview-executed)'
# Emit literal preview output, rather than asking the worker to evaluate a
# prompt expression. Only a second evaluation by the browser can execute it.
print -rl -- "print -r -- ${(q)preview_probe}" 'PROMPT="literal-preview-ready"' > "$ZSH_CUSTOM/literal-browser.zsh-theme"
typeset backslash_probe='literal:\e[2J\n\c:end'
print -rl -- "print -r -- ${(q)backslash_probe}" 'PROMPT="backslash-preview-ready"' > "$ZSH_CUSTOM/backslash-browser.zsh-theme"
typeset buffer='' transcript='' chunk='' label=''
typeset -F browser_finished_at=0
@@ -265,6 +278,49 @@ function test_save {
finish '0:set:zz-omz-test-02:zz-omz-test-02'
}
function test_backslashes {
start 'literal backslashes in preview and filter' backslash-browser '> backslash-browser'
await "$backslash_probe"
await backslash-preview-ready
send $'\x15'"$backslash_probe"
await "Filter: $backslash_probe"
await 'No matching themes.'
send $'\e'
finish '0::parent-theme'
}
function test_promptpercent {
local -x BROWSER_TEST_NO_PROMPT_PERCENT=1
start 'renderer enables promptpercent without changing parent options' zz-omz-test-01 '> zz-omz-test-01'
await FIXTURE_PREVIEW
check test "${transcript#*'%<<'}" = "$transcript"
send $'\e'
finish '0::parent-theme'
}
function test_exittrap {
local -x BROWSER_TEST_EXIT_TRAP
local action
for BROWSER_TEST_EXIT_TRAP in function string; do
for action in use set cancel; do
start "inherited $BROWSER_TEST_EXIT_TRAP EXIT trap: $action" zz-omz-test-01 '> zz-omz-test-01'
await FIXTURE_PREVIEW
if [[ $action == cancel ]]; then
send $'\e'
finish '0::parent-theme'
else
send $'\r'; await '[u] Use in session'
send "${action[1]}"
finish "0:$action:zz-omz-test-01:zz-omz-test-01"
fi
check test "${transcript#*EXIT_FUNCTION_UNCHANGED}" != "$transcript"
if [[ $BROWSER_TEST_EXIT_TRAP == string ]]; then
check test "${transcript#*"trap -- 'print -r -- EXIT_HOOK' EXIT"}" != "$transcript"
fi
done
done
}
function test_interrupt {
start 'Ctrl-C restores terminal' zz-omz-test-01 '> zz-omz-test-01'
send $'\x03'
@@ -322,7 +378,7 @@ function test_slow {
# Keep independent regressions running after a failed UI session.
unsetopt err_exit
typeset -i failures=0
for scenario in rejection completion navigation promptsubst use save interrupt resize slow-navigation slow-escape slow-interrupt; do
for scenario in rejection completion navigation promptsubst backslashes promptpercent exittrap use save interrupt resize slow-navigation slow-escape slow-interrupt; do
[[ -z $1 || $scenario == "$1" ]] || continue
(
setopt err_exit