bash: fix OSC 133;D status always zero (#14250)

The Bash 4.4+ prompt hook saves the command status before doing its own
work and passes it to __ghostty_precmd. The function ignored that
argument and instead read the hook invocation status, causing command
end markers to report zero.

Also, existing scalar PROMPT_COMMAND entries can overwrite the last
command's status before Ghostty's appended hook runs. Capture and
restore the status before those commands, then consume the saved value
in the final hook.

Fixes #14247
This commit is contained in:
Mitchell Hashimoto
2026-09-16 08:30:09 -07:00
committed by GitHub

View File

@@ -134,7 +134,7 @@ _ghostty_executing=""
_ghostty_last_reported_cwd=""
function __ghostty_precmd() {
local ret="$?"
local ret="${1:-$?}"
if test "$_ghostty_executing" != "0"; then
_GHOSTTY_SAVE_PS1="$PS1"
_GHOSTTY_SAVE_PS2="$PS2"
@@ -223,8 +223,15 @@ if (( BASH_VERSINFO[0] > 4 || (BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] >= 4) )
[[ -n "$cmd" ]] && __ghostty_preexec "$cmd"
}
# Bash 5.1+ restores the command status for each PROMPT_COMMAND array entry.
# Scalar values need to save and restore it before existing commands run.
__ghostty_restore_status() {
builtin return "$1"
}
__ghostty_hook() {
builtin local ret=$?
builtin local ret="${__ghostty_status:-$?}"
builtin unset __ghostty_status
__ghostty_precmd "$ret"
# Append preexec hook to PS0 if not already present.
@@ -240,13 +247,14 @@ if (( BASH_VERSINFO[0] > 4 || (BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] >= 4) )
fi
}
# Append our hook to PROMPT_COMMAND, preserving its existing type.
# Append our hook to PROMPT_COMMAND, preserving its existing type. Array
# entries receive their command's status directly, while scalar values
# need a leading status capture before existing commands run.
#
# The 2>/dev/null suppresses "command not found" in subshells that inherit
# PROMPT_COMMAND without the function definition. This also silences any
# errors from inside __ghostty_hook itself, but those are all terminal escape
# sequences and non-actionable.
#
# PROMPT_COMMAND without the function definitions. This also silences any
# errors from inside our hooks, but those are all terminal escape sequences
# and non-actionable.
# shellcheck disable=SC2128,SC2178,SC2179
if [[ ";${PROMPT_COMMAND[*]:-};" != *";__ghostty_hook 2>/dev/null;"* ]]; then
if [[ -z "${PROMPT_COMMAND[*]}" ]]; then
@@ -255,9 +263,10 @@ if (( BASH_VERSINFO[0] > 4 || (BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] >= 4) )
else
PROMPT_COMMAND="__ghostty_hook 2>/dev/null"
fi
elif [[ $(builtin declare -p PROMPT_COMMAND 2>/dev/null) == "declare -a "* ]]; then
elif [[ $(builtin declare -p PROMPT_COMMAND 2>/dev/null) == "declare -a"* ]]; then
PROMPT_COMMAND+=("__ghostty_hook 2>/dev/null")
else
PROMPT_COMMAND='__ghostty_status=$?;if builtin declare -F __ghostty_restore_status >/dev/null;then __ghostty_restore_status "$__ghostty_status";else (exit "$__ghostty_status");fi;'"${PROMPT_COMMAND}"
[[ "${PROMPT_COMMAND}" =~ (\;[[:space:]]*|$'\n')$ ]] || PROMPT_COMMAND+=";"
PROMPT_COMMAND+="__ghostty_hook 2>/dev/null"
fi