mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-17 04:52:47 +00:00
bash: remove dependency on bash-preexec for bash 4.4+
bash-preexec implements support for its "precmd" and "preexec" hooks using a combination of PROMPT_COMMAND and a DEBUG trap. The latter is unfortunately quick slow (in a relative sense), and the overall system is a bit more generalized than what we need for our shell integration (e.g. supporting multiple function hooks, subshells, etc.). Bash 4.4 introduced the PS0 variable, which is expanded and displayed by interactive shells after reading a complete command but before executing it. This is all we need to implement our own shell integration hooks. In Bash 5.1, PROMPT_COMMAND can be an array variable, each element of which can contain a command to be executed like a string PROMPT_COMMAND variable. When adding our hook to PROMPT_COMMAND, we preserve its type (string or array) to be minimally intrusive. This also matches direnv's approach. Bash 5.3 introduced support for function substitution, which is an even more efficient way to run code from PS0, so we use that when available. Otherwise, we use the more traditional command substitution approach. Earlier versions of bash (such as 3.2, which still ships with macOS) continue to use the bash-preexec path. This gives us two code paths to maintain, but I think that's preferable to fully maintaining our own DEBUG trap-based system for older bash versions given that bash-preexec has proven to work reliably in those environments. We also wouldn't unlock any other user benefits aside from removing the bash-preexec script dependency. See: #3724, #7734
This commit is contained in:
@@ -184,9 +184,6 @@ if [[ "$GHOSTTY_SHELL_FEATURES" == *ssh-* ]]; then
|
||||
}
|
||||
fi
|
||||
|
||||
# Import bash-preexec, safe to do multiple times
|
||||
builtin source "$(dirname -- "${BASH_SOURCE[0]}")/bash-preexec.sh"
|
||||
|
||||
# This is set to 1 when we're executing a command so that we don't
|
||||
# send prompt marks multiple times.
|
||||
_ghostty_executing=""
|
||||
@@ -260,5 +257,43 @@ function __ghostty_preexec() {
|
||||
_ghostty_executing=1
|
||||
}
|
||||
|
||||
preexec_functions+=(__ghostty_preexec)
|
||||
precmd_functions+=(__ghostty_precmd)
|
||||
if (( BASH_VERSINFO[0] > 4 || (BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] >= 4) )); then
|
||||
__ghostty_preexec_hook() {
|
||||
builtin local cmd
|
||||
cmd=$(LC_ALL=C HISTTIMEFORMAT='' builtin history 1)
|
||||
cmd="${cmd#*[[:digit:]][* ] }" # remove leading history number
|
||||
[[ -n "$cmd" ]] && __ghostty_preexec "$cmd"
|
||||
}
|
||||
|
||||
# Use function substitution in 5.3+. Otherwise, use command substitution.
|
||||
# Any output (including escape sequences) goes to the terminal.
|
||||
if (( BASH_VERSINFO[0] > 5 || (BASH_VERSINFO[0] == 5 && BASH_VERSINFO[1] >= 3) )); then
|
||||
# shellcheck disable=SC2016
|
||||
builtin readonly __ghostty_ps0='${ __ghostty_preexec_hook; }'
|
||||
else
|
||||
# shellcheck disable=SC2016
|
||||
builtin readonly __ghostty_ps0='$(__ghostty_preexec_hook >/dev/tty)'
|
||||
fi
|
||||
|
||||
__ghostty_hook() {
|
||||
builtin local ret=$?
|
||||
__ghostty_precmd "$ret"
|
||||
PS0=$__ghostty_ps0
|
||||
}
|
||||
|
||||
# Append our hook to PROMPT_COMMAND, preserving its existing type.
|
||||
if [[ ";${PROMPT_COMMAND[*]:-};" != *";__ghostty_hook;"* ]]; then
|
||||
if [[ -z "${PROMPT_COMMAND[*]}" ]]; then
|
||||
PROMPT_COMMAND="__ghostty_hook"
|
||||
elif [[ $(builtin declare -p PROMPT_COMMAND 2>/dev/null) == "declare -a "* ]]; then
|
||||
PROMPT_COMMAND+=(__ghostty_hook)
|
||||
else
|
||||
# shellcheck disable=SC2179
|
||||
PROMPT_COMMAND+="; __ghostty_hook"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
builtin source "$(dirname -- "${BASH_SOURCE[0]}")/bash-preexec.sh"
|
||||
preexec_functions+=(__ghostty_preexec)
|
||||
precmd_functions+=(__ghostty_precmd)
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user