diff options
author | Koichi Murase <myoga.murase@gmail.com> | 2024-01-24 02:53:47 +0900 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-01-23 17:53:47 +0000 |
commit | 067bda66e261fcc739eb235d390df1501e4209da (patch) | |
tree | d93ec0fb684ba7bd87c7cd89315f06a3cd342a19 | |
parent | 2bd7114cea89a471dcf6f4c279048db4be5d7731 (diff) |
fix(bash): strip control chars generated by `\[\]` in PS1 with bash-preexec (#1620)
* fix(bash): strip control chars generated by \[\] in PS1 with bash-preexec
Fixes https://github.com/atuinsh/atuin/issues/1617
* perf(bash): count newlines using Bash built-in features
-rw-r--r-- | atuin/src/shell/atuin.bash | 49 |
1 files changed, 32 insertions, 17 deletions
diff --git a/atuin/src/shell/atuin.bash b/atuin/src/shell/atuin.bash index 490b7f71..bf5f4481 100644 --- a/atuin/src/shell/atuin.bash +++ b/atuin/src/shell/atuin.bash @@ -71,28 +71,45 @@ __atuin_set_ret_value() { return ${1:+"$1"} } -# The expansion ${PS1@P} is available in bash >= 4.4. +# The shell function `__atuin_evaluate_prompt` evaluates prompt sequences in +# $PS1. We switch the implementation of the shell function +# `__atuin_evaluate_prompt` based on the Bash version because the expansion +# ${PS1@P} is only available in bash >= 4.4. if ((BASH_VERSINFO[0] >= 5 || BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] >= 4)); then - __atuin_use_prompt_expansion=true + __atuin_evaluate_prompt() { + __atuin_set_ret_value "${__bp_last_ret_value-}" "${__bp_last_argument_prev_command-}" + __atuin_prompt=${PS1@P} + + # Note: Strip the control characters ^A (\001) and ^B (\002), which + # Bash internally uses to enclose the escape sequences. They are + # produced by '\[' and '\]', respectively, in $PS1 and used to tell + # Bash that the strings inbetween do not contribute to the prompt + # width. After the prompt width calculation, Bash strips those control + # characters before outputting it to the terminal. We here strip these + # characters following Bash's behavior. + __atuin_prompt=${__atuin_prompt//[$'\001\002']} + + # Count the number of newlines contained in $__atuin_prompt + __atuin_prompt_offset=${__atuin_prompt//[!$'\n']} + __atuin_prompt_offset=${#__atuin_prompt_offset} + } else - __atuin_use_prompt_expansion=false + __atuin_evaluate_prompt() { + __atuin_prompt='$ ' + __atuin_prompt_offset=0 + } fi __atuin_accept_line() { local __atuin_command=$1 # Reprint the prompt, accounting for multiple lines - if [[ $__atuin_use_prompt_expansion == true ]]; then - local __atuin_prompt=${PS1@P} - local __atuin_prompt_offset - __atuin_prompt_offset=$(printf '%s' "$__atuin_prompt" | wc -l) - if ((__atuin_prompt_offset > 0)); then - tput cuu "$__atuin_prompt_offset" - fi - printf '%s\n' "$__atuin_prompt$__atuin_command" - else - printf '%s\n' "\$ $__atuin_command" + local __atuin_prompt __atuin_prompt_offset + __atuin_evaluate_prompt + if ((__atuin_prompt_offset > 0)); then + tput cuu "$__atuin_prompt_offset" fi + printf '%s\n' "$__atuin_prompt$__atuin_command" # Add it to the bash history history -s "$__atuin_command" @@ -140,10 +157,8 @@ __atuin_accept_line() { # Bash will redraw only the line with the prompt after we finish, # so to work for a multiline prompt we need to print it ourselves, # then go to the beginning of the last line. - if [[ $__atuin_use_prompt_expansion == true ]]; then - __atuin_set_ret_value "${__bp_last_ret_value-}" "${__bp_last_argument_prev_command-}" - printf '%s\r' "${PS1@P}" - fi + __atuin_evaluate_prompt + printf '%s\r' "$__atuin_prompt" } __atuin_history() { |