From b53d756bc2bcec7d9428db0716131b38e7359138 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 14 Jan 2022 12:14:48 +0530 Subject: [PATCH] Shell integration: Fix bash integration not working when PROMPT_COMMAND is used to change the prompt variables Fixes #4476 --- docs/changelog.rst | 3 ++ shell-integration/bash/kitty.bash | 83 +++++++++++++++++++++++++------ 2 files changed, 72 insertions(+), 14 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 7dd8e57de..d5a9ad18e 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -79,6 +79,9 @@ Detailed list of changes - macOS: Allow opening text files, images and directories with kitty when launched using "Open with" in Finder (:iss:`4460`) +- Shell integration: Fix bash integration not working when ``PROMPT_COMMAND`` + is used to change the prompt variables (:iss:`4476`) + - Allow using templates with text formatting for :opt:`tab_activity_symbol` (:pull:`4507`) diff --git a/shell-integration/bash/kitty.bash b/shell-integration/bash/kitty.bash index f7cdd76c0..38843604a 100644 --- a/shell-integration/bash/kitty.bash +++ b/shell-integration/bash/kitty.bash @@ -3,7 +3,7 @@ _ksi_main() { if [[ $- != *i* ]] ; then return; fi # check in interactive mode if [[ -z "$KITTY_SHELL_INTEGRATION" ]]; then return; fi - declare -A _ksi_prompt=( [cursor]='y' [title]='y' [mark]='y' [complete]='y' ) + declare -g -A _ksi_prompt=( [cursor]='y' [title]='y' [mark]='y' [complete]='y' [ps0]='' [ps1]='' [ps2]='' ) set -f for i in ${KITTY_SHELL_INTEGRATION[@]}; do set +f @@ -23,30 +23,58 @@ _ksi_main() { # " } + _ksi_set_mark() { + _ksi_prompt["${1}_mark"]="\[\e]133;k;${1}_kitty\a\]" + } + + _ksi_set_mark start + _ksi_set_mark end + _ksi_set_mark start_secondary + _ksi_set_mark end_secondary + unset -f _ksi_set_mark + _ksi_prompt[secondary_prompt]="\n${_ksi_prompt[start_secondary_mark]}\[\e]133;A;k=s\a\]${_ksi_prompt[end_secondary_mark]}" + + _ksi_prompt_command() { + # we first remove any previously added kitty code from the prompt variables and then add + # it back, to ensure we have only a single instance + if [[ -n "${_ksi_prompt[ps0]}" ]]; then + PS0=${PS0//\\\[\\e\]133;k;start_kitty\\a\\\]*end_kitty\\a\\\]} + PS0="${_ksi_prompt[ps0]}$PS0" + fi + if [[ -n "${_ksi_prompt[ps1]}" ]]; then + PS1=${PS1//\\\[\\e\]133;k;start_kitty\\a\\\]*end_kitty\\a\\\]} + PS1="${_ksi_prompt[ps1]}$PS1" + if [[ "${_ksi_prompt[mark]}" == "y" ]]; then + # bash does not redraw the leading lines in a multiline prompt so + # mark them as secondary prompts + PS1=${PS1//\\\[\\e\]133;k;start_secondary_kitty\\a\\\]*end_secondary_kitty\\a\\\]} + PS1=${PS1//"\n"/${_ksi_prompt[secondary_prompt]}} + fi + fi + if [[ -n "${_ksi_prompt[ps2]}" ]]; then + PS2=${PS2//\\\[\\e\]133;k;start_kitty\\a\\\]*end_kitty\\a\\\]} + PS2="${_ksi_prompt[ps2]}$PS2" + fi + } + if [[ "${_ksi_prompt[cursor]}" == "y" ]]; then - PS1="\[\e[5 q\]$PS1" # blinking bar cursor - PS0="\[\e[1 q\]$PS0" # blinking block cursor + _ksi_prompt[ps1]+="\[\e[5 q\]" # blinking bar cursor + _ksi_prompt[ps0]+="\[\e[5 q\]" # blinking block cursor fi if [[ "${_ksi_prompt[title]}" == "y" ]]; then # see https://www.gnu.org/software/bash/manual/html_node/Controlling-the-Prompt.html#Controlling-the-Prompt - PS1="\[\e]2;\w\a\]$PS1" + _ksi_prompt[ps1]+="\[\e]2;\w\a\]" if [[ "$HISTCONTROL" == *"ignoreboth"* ]] || [[ "$HISTCONTROL" == *"ignorespace"* ]]; then _ksi_debug_print "ignoreboth or ignorespace present in bash HISTCONTROL setting, showing running command in window title will not be robust" fi - local orig_ps0="$PS0" - PS0='$(printf "\e]2;%s\a" "$(HISTTIMEFORMAT= history 1 | sed -e "s/^[ ]*[0-9]*[ ]*//")")' - PS0+="$orig_ps0" + _ksi_prompt[ps0]+='$(printf "\e]2;%s\a" "$(HISTTIMEFORMAT= history 1 | sed -e "s/^[ ]*[0-9]*[ ]*//")")' fi if [[ "${_ksi_prompt[mark]}" == "y" ]]; then - # bash does not redraw the leading lines in a multiline prompt so - # mark them as secondary prompts - local secondary_prompt="\n\[\e]133;A;k=s\a\]" - PS1=${PS1//"\n"/"$secondary_prompt"} - PS1="\[\e]133;A\a\]$PS1" - PS2="\[\e]133;A;k=s\a\]$PS2" - PS0="\[\e]133;C\a\]$PS0" + _ksi_prompt[ps1]+="\[\e]133;A\a\]" + _ksi_prompt[ps2]+="\[\e]133;A;k=s\a\]" + _ksi_prompt[ps0]+="\[\e]133;C\a\]" fi if [[ "${_ksi_prompt[complete]}" == "y" ]]; then @@ -62,5 +90,32 @@ _ksi_main() { } complete -o nospace -F _ksi_completions kitty fi + + # wrap our prompt additions in markers we can use to remove them using + # bash's anemic pattern substitution + if [[ -n "${_ksi_prompt[ps0]}" ]]; then + _ksi_prompt[ps0]="${_ksi_prompt[start_mark]}${_ksi_prompt[ps0]}${_ksi_prompt[end_mark]}" + fi + if [[ -n "${_ksi_prompt[ps1]}" ]]; then + _ksi_prompt[ps1]="${_ksi_prompt[start_mark]}${_ksi_prompt[ps1]}${_ksi_prompt[end_mark]}" + fi + if [[ -n "${_ksi_prompt[ps2]}" ]]; then + _ksi_prompt[ps2]="${_ksi_prompt[start_mark]}${_ksi_prompt[ps2]}${_ksi_prompt[end_mark]}" + fi + unset _ksi_prompt[start_mark] + unset _ksi_prompt[end_mark] + unset _ksi_prompt[start_secondary_mark] + unset _ksi_prompt[end_secondary_mark] + + # install our prompt command, using an array if it is unset or already an array, + # otherwise append a string + if [[ -z "${PROMPT_COMMAND}" ]]; then + PROMPT_COMMAND=([0]="_ksi_prompt_command") + elif [[ $(declare -p PROMPT_COMMAND 2> /dev/null) =~ 'declare -a PROMPT_COMMAND' ]]; then + PROMPT_COMMAND+=("_ksi_prompt_command") + else + PROMPT_COMMAND+="; _ksi_prompt_command" + fi } _ksi_main +unset -f _ksi_main