From a6b74f190f59bc9fd6c2deb8e092e04d3ca76e2d Mon Sep 17 00:00:00 2001 From: Roman Perepelitsa Date: Wed, 5 Jan 2022 09:20:33 +0100 Subject: [PATCH 1/4] Revert "Get _ksi_debug_print working again" This reverts commit ff63e58f95833e10146568df914438ff4d4f8e12. --- shell-integration/zsh/kitty-integration | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/shell-integration/zsh/kitty-integration b/shell-integration/zsh/kitty-integration index 70d7d0641..8e47cfb53 100644 --- a/shell-integration/zsh/kitty-integration +++ b/shell-integration/zsh/kitty-integration @@ -32,13 +32,10 @@ builtin emulate -L zsh -o no_warn_create_global # 2: none of the above. builtin typeset -gi _ksi_state -# Asks kitty to print $@ to its STDOUT. This is for debugging. +# Asks kitty to print $@ to its stdout. This is for debugging. _ksi_debug_print() { - builtin local data saved - saved="$IFS" - IFS=" " - data=$(command base64 <<< "$*") - IFS="$saved" + builtin local data + data=$(command base64 <<<"${(j: :}@}") || builtin return builtin printf '\eP@kitty-print|%s\e\\' "${data//$'\n'}" } From 4decac26f6aa0496650646bd853b5083b2b713c1 Mon Sep 17 00:00:00 2001 From: Roman Perepelitsa Date: Wed, 5 Jan 2022 09:22:00 +0100 Subject: [PATCH 2/4] Fix a typo in _ksi_debug_print --- shell-integration/zsh/kitty-integration | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell-integration/zsh/kitty-integration b/shell-integration/zsh/kitty-integration index 8e47cfb53..05a1dd947 100644 --- a/shell-integration/zsh/kitty-integration +++ b/shell-integration/zsh/kitty-integration @@ -35,7 +35,7 @@ builtin typeset -gi _ksi_state # Asks kitty to print $@ to its stdout. This is for debugging. _ksi_debug_print() { builtin local data - data=$(command base64 <<<"${(j: :}@}") || builtin return + data=$(command base64 <<<"${(j: :)@}") || builtin return builtin printf '\eP@kitty-print|%s\e\\' "${data//$'\n'}" } From 8009b85073442acf9b932c5155d2bf7415a2119f Mon Sep 17 00:00:00 2001 From: Roman Perepelitsa Date: Wed, 5 Jan 2022 09:55:19 +0100 Subject: [PATCH 3/4] Make writing to the TTY more robust in zsh integration See https://github.com/kovidgoyal/kitty/issues/4440. --- shell-integration/zsh/kitty-integration | 74 +++++++++++++++++++------ 1 file changed, 58 insertions(+), 16 deletions(-) diff --git a/shell-integration/zsh/kitty-integration b/shell-integration/zsh/kitty-integration index 05a1dd947..5195f1791 100644 --- a/shell-integration/zsh/kitty-integration +++ b/shell-integration/zsh/kitty-integration @@ -32,11 +32,42 @@ builtin emulate -L zsh -o no_warn_create_global # 2: none of the above. builtin typeset -gi _ksi_state -# Asks kitty to print $@ to its stdout. This is for debugging. +# Attempt to create a writable file descriptor to the TTY so that we can print +# to the TTY later even when STDOUT is redirected. This code is fairly subtle. +# +# - It's tempting to do `[[ -t 1 ]] && exec {_ksi_state}>&1` but we cannot do this +# because it'll create a file descriptor >= 10 without O_CLOEXEC. This file +# descriptor will leak to child processes. +# - If we do `exec {3}>&1`, the file descriptor won't leak to the child processes +# but it'll still leak if the current process is replaced with another. In +# addition, it'll break user code that relies on fd 3 being available. +# - Zsh doesn't expose dup3, which would have allowed us to copy STDOUT with +# O_CLOEXEC. The only way to create a file descriptor with O_CLOEXEC is via +# sysopen. +# - `zmodload zsh/system` and `sysopen -o cloexec -wu _ksi_fd -- /dev/tty` can +# fail with an error message to STDERR (the latter can happen even if /dev/tty +# is writable), hence the redirection of STDERR. We do it for the whole block +# for performance reasons (redirections are slow). +# - We must open the file descriptor right here rather than in _ksi_deferred_init +# because there are broken zsh plugins out there that run `exec {fd}< <(cmd)` +# and then close the file descriptor more than once while suppressing errors. +# This could end up closing our file descriptor if we opened it in +# _ksi_deferred_init. +typeset -gi _ksi_fd +{ + zmodload zsh/system && (( $+builtins[sysopen] )) && { + { [[ -w $TTY ]] && sysopen -o cloexec -wu _ksi_fd -- $TTY } || + { [[ -w /dev/tty ]] && sysopen -o cloexec -wu _ksi_fd -- /dev/tty } + } +} 2>/dev/null || (( _ksi_fd = 1 )) + +# Asks kitty to print $@ to its STDOUT. This is for debugging. _ksi_debug_print() { builtin local data data=$(command base64 <<<"${(j: :)@}") || builtin return - builtin printf '\eP@kitty-print|%s\e\\' "${data//$'\n'}" + # Removing all spaces rather than just \n allows this code to + # work on broken systems where base64 outputs \r\n. + builtin print -nu "$_ksi_fd" '\eP@kitty-print|'"${data//[[:space:]]}"'\e\\' } # We defer initialization until precmd for several reasons: @@ -95,8 +126,10 @@ _ksi_deferred_init() { # themselves with a blinking block cursor within fzf. _ksi_zle_line_init _ksi_zle_line_finish _ksi_zle_keymap_select() { case ${KEYMAP-} in - vicmd|visual) builtin print -n '\e[1 q';; # blinking block cursor - *) builtin print -n '\e[5 q';; # blinking bar cursor + # Blinking block cursor. + vicmd|visual) builtin print -nu "$_ksi_fd" '\e[1 q';; + # Blinking bar cursor. + *) builtin print -nu "$_ksi_fd" '\e[5 q';; esac } fi @@ -122,11 +155,11 @@ _ksi_deferred_init() { if (( _ksi_state == 1 )); then # The last written OSC 133 C has not been closed with D yet. # Close it and supply status. - builtin printf '\e]133;D;%s\a' $cmd_status + builtin print -nu $_ksi_fd '\e]133;D;'$cmd_status'\a' (( _ksi_state = 2 )) elif (( _ksi_state == 2 )); then # There might be an unclosed OSC 133 C. Close that. - builtin print -n '\e]133;D\a' + builtin print -nu $_ksi_fd '\e]133;D\a' fi fi @@ -148,7 +181,11 @@ _ksi_deferred_init() { # - False positive (with prompt_percent): PS1="%(?.$mark1.)" # - False negative (with prompt_subst): PS1='$mark1' [[ $PS1 == *$mark1* ]] || PS1=${mark1}${PS1} - [[ $PS2 == *$mark2* ]] || PS2=${mark2}${PS2} + # The following line is commented out because currently kitty doesn't + # use B prompt marking, so there is no need to pay performance penalty + # for it. + # + # [[ $PS2 == *$mark2* ]] || PS2=${mark2}${PS2} (( _ksi_state = 2 )) else # If our precmd hook is not the last, we cannot rely on prompt @@ -164,7 +201,7 @@ _ksi_deferred_init() { # already have a mark, so the following reset-prompt will write # it. If it doesn't, there is nothing we can do. if ! builtin zle; then - builtin print -rn -- $mark1[3,-3] + builtin print -rnu $_ksi_fd -- $mark1[3,-3] (( _ksi_state = 2 )) fi fi @@ -172,7 +209,7 @@ _ksi_deferred_init() { # Without prompt_percent we cannot patch prompt. Just print the # mark, except when we are invoked from zle. In the latter case we # cannot do anything. - builtin print -rn -- $mark1[3,-3] + builtin print -rnu $_ksi_fd -- $mark1[3,-3] (( _ksi_state = 2 )) fi } @@ -187,14 +224,19 @@ _ksi_deferred_init() { # top. We cannot force prompt_subst on the user though, so we would # still need this code for the no_prompt_subst case. PS1=${PS1//$'%{\e]133;A\a%}'} - PS2=${PS2//$'%{\e]133;A;k=s\a%}'} + + # The following line is commented out because currently kitty doesn't + # use B prompt marking, so there is no need to pay performance penalty + # for it. + # + # PS2=${PS2//$'%{\e]133;A;k=s\a%}'} # This will work incorrectly in the presence of a preexec hook that # prints. For example, if MichaelAquilina/zsh-you-should-use installs # its preexec hook before us, we'll incorrectly mark its output as # belonging to the command (as if the user typed it into zle) rather # than command output. - builtin print -n '\e]133;C\a' + builtin print -nu $_ksi_fd '\e]133;C\a' (( _ksi_state = 1 )) } @@ -202,7 +244,7 @@ _ksi_deferred_init() { # and hooking zle widgets in ZSH is a total minefield, see https://github.com/kovidgoyal/kitty/issues/4428 # so we can at least tell users to use no-cursor and with that avoid hooking ZLE widgets at all # functions[_ksi_zle_line_init]+=' - # builtin print -n "\\e]133;B\\a"' + # builtin print -nu "$_ksi_fd" "\\e]133;B\\a"' fi # Enable terminal title changes. @@ -213,10 +255,10 @@ _ksi_deferred_init() { # We use (V) in preexec to convert control characters to something visible # (LF becomes \n, etc.). This isn't necessary in precmd because (%) does it # for us. - functions[_ksi_precmd]+=' - builtin printf "\\e]2;%s\\a" "${(%):-%(4~|…/%3~|%~)}"' - functions[_ksi_preexec]+=' - builtin printf "\\e]2;%s\\a" "${(V)1}"' + functions[_ksi_precmd]+=" + builtin print -rnu $_ksi_fd \$'\\e]2;'\"\${(%):-%(4~|…/%3~|%~)}\"\$'\\a'" + functions[_ksi_preexec]+=" + builtin print -rnu $_ksi_fd \$'\\e]2;'\"\${(V)1}\"\$'\\a'" fi # Some zsh users manually run `source ~/.zshrc` in order to apply rc file From 4f06ce9d72efd397f630364ac8e02e51bffa4f8a Mon Sep 17 00:00:00 2001 From: Roman Perepelitsa Date: Wed, 5 Jan 2022 16:36:00 +0100 Subject: [PATCH 4/4] Once again start embedding marks in PS2 on zsh --- shell-integration/zsh/kitty-integration | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/shell-integration/zsh/kitty-integration b/shell-integration/zsh/kitty-integration index 5195f1791..59c8137e0 100644 --- a/shell-integration/zsh/kitty-integration +++ b/shell-integration/zsh/kitty-integration @@ -181,11 +181,7 @@ _ksi_deferred_init() { # - False positive (with prompt_percent): PS1="%(?.$mark1.)" # - False negative (with prompt_subst): PS1='$mark1' [[ $PS1 == *$mark1* ]] || PS1=${mark1}${PS1} - # The following line is commented out because currently kitty doesn't - # use B prompt marking, so there is no need to pay performance penalty - # for it. - # - # [[ $PS2 == *$mark2* ]] || PS2=${mark2}${PS2} + [[ $PS2 == *$mark2* ]] || PS2=${mark2}${PS2} (( _ksi_state = 2 )) else # If our precmd hook is not the last, we cannot rely on prompt @@ -224,12 +220,7 @@ _ksi_deferred_init() { # top. We cannot force prompt_subst on the user though, so we would # still need this code for the no_prompt_subst case. PS1=${PS1//$'%{\e]133;A\a%}'} - - # The following line is commented out because currently kitty doesn't - # use B prompt marking, so there is no need to pay performance penalty - # for it. - # - # PS2=${PS2//$'%{\e]133;A;k=s\a%}'} + PS2=${PS2//$'%{\e]133;A;k=s\a%}'} # This will work incorrectly in the presence of a preexec hook that # prints. For example, if MichaelAquilina/zsh-you-should-use installs