Skip to content

Commit 2cee9b9

Browse files
committed
Suppress PROMPT_COMMAND hooks from inside saved strings
We try to remove the existing hooks before the re-adjustment of PROMPT_COMMAND, but it might not work when another framework saves the original value of `PROMPT_COMMAND` in another variable and tries to call it from inside their `PROMPT_COMMAND` hook. For example, Starship does that [1]. In such a case, our hooks would be called several times unexpectedly. To avoid this situation, we process our hooks only when the hooks are called at the top level. [1] https://github.com/starship/starship/blob/3d474684149e0a7959fb986f8cea1d28b4c69d87/src/init/starship.bash#L97
1 parent 15d1880 commit 2cee9b9

File tree

1 file changed

+24
-5
lines changed

1 file changed

+24
-5
lines changed

bash-preexec.sh

+24-5
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,14 @@ __bp_remove_command_from_prompt_command() {
200200
# It sets a variable to indicate that the prompt was just displayed,
201201
# to allow the DEBUG trap to know that the next command is likely interactive.
202202
__bp_interactive_mode() {
203+
if [[ "${1-}" != "force" && ! "${BATS_VERSION-}" ]] && (( ${#FUNCNAME[*]} > 1 )); then
204+
# When this function is not called from the top level, the current
205+
# function call is probably performed via PROMPT_COMMAND saved by
206+
# another framework (e.g., starship). In this case, we do not want to
207+
# turn on the "interactive mode" here.
208+
return 0
209+
fi
210+
203211
__bp_preexec_interactive_mode="on";
204212
}
205213

@@ -223,7 +231,15 @@ __bp_precmd_invoke_cmd() {
223231

224232
# Check and adjust PROMPT_COMMAND to make sure that PROMPT_COMMAND has the
225233
# form "__bp_precmd_invoke_cmd; ...; __bp_interactive_mode"
226-
__bp_install_prompt_command
234+
if ! __bp_install_prompt_command && [[ ! "${BATS_VERSION-}" ]] && (( ${#FUNCNAME[*]} > 1 )); then
235+
# When PROMPT_COMMAND is already properly set up but this function is
236+
# not called from the top level, the current function call is probably
237+
# performed via PROMPT_COMMAND saved by another framework (e.g.,
238+
# starship). In this case, we do not need to invoke precmd because it
239+
# is supposed to be already processed by the top-level
240+
# __bp_precmd_invoke_cmd.
241+
: return 0
242+
fi
227243

228244
local __bp_inside_precmd=1
229245

@@ -395,7 +411,7 @@ __bp_install() {
395411
# Remove setting our trap install string and sanitize the existing prompt command string
396412
__bp_remove_command_from_prompt_command "$__bp_install_string"
397413

398-
__bp_install_prompt_command
414+
__bp_install_prompt_command || true
399415

400416
# Add two functions to our arrays for convenience
401417
# of definition.
@@ -404,12 +420,14 @@ __bp_install() {
404420

405421
# Invoke our two functions manually that were added to $PROMPT_COMMAND
406422
__bp_precmd_invoke_cmd
407-
__bp_interactive_mode
423+
__bp_interactive_mode force
408424
}
409425

410426

411427
# Encloses PROMPT_COMMAND hooks within __bp_precmd_invoke_cmd and
412-
# __bp_interactive_mode.
428+
# __bp_interactive_mode. If all the PROMPT_COMMAND hooks are already surrounded
429+
# by __bp_precmd_invoke_cmd and __bp_interactive_mode, the function exits with
430+
# status 1.
413431
__bp_install_prompt_command() {
414432
local prompt_command="${PROMPT_COMMAND:-}"
415433
if __bp_use_array_prompt_command; then
@@ -420,7 +438,7 @@ __bp_install_prompt_command() {
420438

421439
# Exit if we already have a properly set-up hooks in PROMPT_COMMAND
422440
if [[ "$prompt_command" == __bp_precmd_invoke_cmd$'\n'*$'\n'__bp_interactive_mode ]]; then
423-
return 0
441+
return 1
424442
fi
425443

426444
__bp_remove_command_from_prompt_command __bp_precmd_invoke_cmd
@@ -436,6 +454,7 @@ __bp_install_prompt_command() {
436454
# shellcheck disable=SC2179 # PROMPT_COMMAND is not an array in bash <= 5.0
437455
PROMPT_COMMAND+=$'\n__bp_interactive_mode'
438456
fi
457+
return 0
439458
}
440459

441460

0 commit comments

Comments
 (0)