From 2b05f5ed2d44799e1d71ce3f186bcb5b07abd277 Mon Sep 17 00:00:00 2001 From: Eric Freese Date: Wed, 17 Apr 2019 23:28:20 -0600 Subject: [PATCH 1/9] Add a comment explaining post-completion hook --- src/strategies/completion.zsh | 3 +++ zsh-autosuggestions.zsh | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/strategies/completion.zsh b/src/strategies/completion.zsh index bf4ed9a..c2d52f0 100644 --- a/src/strategies/completion.zsh +++ b/src/strategies/completion.zsh @@ -14,6 +14,9 @@ _zsh_autosuggest_capture_postcompletion() { } _zsh_autosuggest_capture_completion_widget() { + # Add a post-completion hook to be called after all completions have been + # gathered. The hook can modify compstate to affect what is done with the + # gathered completions. local -a +h comppostfuncs comppostfuncs=(_zsh_autosuggest_capture_postcompletion) diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh index f2fe337..449d68b 100644 --- a/zsh-autosuggestions.zsh +++ b/zsh-autosuggestions.zsh @@ -496,6 +496,9 @@ _zsh_autosuggest_capture_postcompletion() { } _zsh_autosuggest_capture_completion_widget() { + # Add a post-completion hook to be called after all completions have been + # gathered. The hook can modify compstate to affect what is done with the + # gathered completions. local -a +h comppostfuncs comppostfuncs=(_zsh_autosuggest_capture_postcompletion) From 7e048c3f53ad6e56148efd98f752f15654637774 Mon Sep 17 00:00:00 2001 From: Eric Freese Date: Wed, 17 Apr 2019 23:25:43 -0600 Subject: [PATCH 2/9] Fix error that was hidden in zpty Without the quotes, this was blowing up with: _zsh_autosuggest_capture_postcompletion:unset:5: not enough arguments --- src/strategies/completion.zsh | 2 +- zsh-autosuggestions.zsh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/strategies/completion.zsh b/src/strategies/completion.zsh index c2d52f0..b280608 100644 --- a/src/strategies/completion.zsh +++ b/src/strategies/completion.zsh @@ -10,7 +10,7 @@ _zsh_autosuggest_capture_postcompletion() { compstate[insert]=1 # Don't list completions - unset compstate[list] + unset 'compstate[list]' } _zsh_autosuggest_capture_completion_widget() { diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh index 449d68b..2d9c751 100644 --- a/zsh-autosuggestions.zsh +++ b/zsh-autosuggestions.zsh @@ -492,7 +492,7 @@ _zsh_autosuggest_capture_postcompletion() { compstate[insert]=1 # Don't list completions - unset compstate[list] + unset 'compstate[list]' } _zsh_autosuggest_capture_completion_widget() { From fdf4502c5c84459d9343b4299006ea063f130ef1 Mon Sep 17 00:00:00 2001 From: Eric Freese Date: Wed, 17 Apr 2019 23:28:51 -0600 Subject: [PATCH 3/9] We need to autoload is-at-least for it to be available This error was hidden inside the zpty --- src/strategies/completion.zsh | 2 ++ zsh-autosuggestions.zsh | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/strategies/completion.zsh b/src/strategies/completion.zsh index b280608..f869aa2 100644 --- a/src/strategies/completion.zsh +++ b/src/strategies/completion.zsh @@ -35,6 +35,8 @@ _zsh_autosuggest_capture_completion_widget() { zle -N autosuggest-capture-completion _zsh_autosuggest_capture_completion_widget _zsh_autosuggest_capture_setup() { + autoload -Uz is-at-least + # There is a bug in zpty module in older zsh versions by which a # zpty that exits will kill all zpty processes that were forked # before it. Here we set up a zsh exit hook to SIGKILL the zpty diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh index 2d9c751..ffc0a1a 100644 --- a/zsh-autosuggestions.zsh +++ b/zsh-autosuggestions.zsh @@ -517,6 +517,8 @@ _zsh_autosuggest_capture_completion_widget() { zle -N autosuggest-capture-completion _zsh_autosuggest_capture_completion_widget _zsh_autosuggest_capture_setup() { + autoload -Uz is-at-least + # There is a bug in zpty module in older zsh versions by which a # zpty that exits will kill all zpty processes that were forked # before it. Here we set up a zsh exit hook to SIGKILL the zpty From e9ce05c4c1aaff9aef12ddc375e718fd4e41d9c9 Mon Sep 17 00:00:00 2001 From: Eric Freese Date: Fri, 14 Jun 2019 21:27:21 -0600 Subject: [PATCH 4/9] Ensure that `kill` succeeds even in older buggy versions of zsh See https://unix.stackexchange.com/a/477647/156673 --- src/strategies/completion.zsh | 8 ++++++-- zsh-autosuggestions.zsh | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/strategies/completion.zsh b/src/strategies/completion.zsh index f869aa2..246f0bd 100644 --- a/src/strategies/completion.zsh +++ b/src/strategies/completion.zsh @@ -44,8 +44,12 @@ _zsh_autosuggest_capture_setup() { # zpty processes. if ! is-at-least 5.4; then zshexit() { - kill -KILL $$ - sleep 1 # Block for long enough for the signal to come through + # The zsh builtin `kill` fails sometimes in older versions + # https://unix.stackexchange.com/a/477647/156673 + kill -KILL $$ 2>&- || command kill -KILL $$ + + # Block for long enough for the signal to come through + sleep 1 } fi diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh index ffc0a1a..753228e 100644 --- a/zsh-autosuggestions.zsh +++ b/zsh-autosuggestions.zsh @@ -526,8 +526,12 @@ _zsh_autosuggest_capture_setup() { # zpty processes. if ! is-at-least 5.4; then zshexit() { - kill -KILL $$ - sleep 1 # Block for long enough for the signal to come through + # The zsh builtin `kill` fails sometimes in older versions + # https://unix.stackexchange.com/a/477647/156673 + kill -KILL $$ 2>&- || command kill -KILL $$ + + # Block for long enough for the signal to come through + sleep 1 } fi From c04e015d13fdee2903af9740e46540fa7c9295c4 Mon Sep 17 00:00:00 2001 From: Eric Freese Date: Wed, 17 Apr 2019 23:29:52 -0600 Subject: [PATCH 5/9] Add to explanation of extra stuff after `zpty -r` --- src/strategies/completion.zsh | 6 ++++-- zsh-autosuggestions.zsh | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/strategies/completion.zsh b/src/strategies/completion.zsh index 246f0bd..8dc1f9d 100644 --- a/src/strategies/completion.zsh +++ b/src/strategies/completion.zsh @@ -108,8 +108,10 @@ _zsh_autosuggest_strategy_completion() { # content between the first two null bytes. zpty -r $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME line '*'$'\0''*'$'\0' - # On older versions of zsh, we sometimes get extra bytes after the - # second null byte, so trim those off the end + # Extract the suggestion from between the null bytes. On older + # versions of zsh (older than 5.3), we sometimes get extra bytes after + # the second null byte, so trim those off the end. + # See http://www.zsh.org/mla/workers/2015/msg03290.html suggestion="${${${(M)line:#*$'\0'*$'\0'*}#*$'\0'}%%$'\0'*}" } always { # Destroy the pty diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh index 753228e..cef55f8 100644 --- a/zsh-autosuggestions.zsh +++ b/zsh-autosuggestions.zsh @@ -590,8 +590,10 @@ _zsh_autosuggest_strategy_completion() { # content between the first two null bytes. zpty -r $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME line '*'$'\0''*'$'\0' - # On older versions of zsh, we sometimes get extra bytes after the - # second null byte, so trim those off the end + # Extract the suggestion from between the null bytes. On older + # versions of zsh (older than 5.3), we sometimes get extra bytes after + # the second null byte, so trim those off the end. + # See http://www.zsh.org/mla/workers/2015/msg03290.html suggestion="${${${(M)line:#*$'\0'*$'\0'*}#*$'\0'}%%$'\0'*}" } always { # Destroy the pty From d94475ca1b6a39cf92a3957d3b437903c15c4c93 Mon Sep 17 00:00:00 2001 From: Eric Freese Date: Fri, 14 Jun 2019 22:08:15 -0600 Subject: [PATCH 6/9] Simplify logic to extract suggestion from between null bytes Just remove up to and including the first null byte and after and including the last null byte. I also looked into using `${${(0)line}[2]}`, but it fails when `$line` starts with a null byte, since the first split string will be empty and thus not included in the resulting array. --- src/strategies/completion.zsh | 2 +- zsh-autosuggestions.zsh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/strategies/completion.zsh b/src/strategies/completion.zsh index 8dc1f9d..06edc20 100644 --- a/src/strategies/completion.zsh +++ b/src/strategies/completion.zsh @@ -112,7 +112,7 @@ _zsh_autosuggest_strategy_completion() { # versions of zsh (older than 5.3), we sometimes get extra bytes after # the second null byte, so trim those off the end. # See http://www.zsh.org/mla/workers/2015/msg03290.html - suggestion="${${${(M)line:#*$'\0'*$'\0'*}#*$'\0'}%%$'\0'*}" + suggestion="${${line#*$'\0'}%$'\0'*}" } always { # Destroy the pty zpty -d $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh index cef55f8..e7d9861 100644 --- a/zsh-autosuggestions.zsh +++ b/zsh-autosuggestions.zsh @@ -594,7 +594,7 @@ _zsh_autosuggest_strategy_completion() { # versions of zsh (older than 5.3), we sometimes get extra bytes after # the second null byte, so trim those off the end. # See http://www.zsh.org/mla/workers/2015/msg03290.html - suggestion="${${${(M)line:#*$'\0'*$'\0'*}#*$'\0'}%%$'\0'*}" + suggestion="${${line#*$'\0'}%$'\0'*}" } always { # Destroy the pty zpty -d $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME From e263845beda6b261f3654bcc2ef3ca91b7aace15 Mon Sep 17 00:00:00 2001 From: Eric Freese Date: Wed, 17 Apr 2019 23:27:26 -0600 Subject: [PATCH 7/9] Add an extra completion to better exercise choosing first --- spec/strategies/completion_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/strategies/completion_spec.rb b/spec/strategies/completion_spec.rb index bd2c72d..1b4002b 100644 --- a/spec/strategies/completion_spec.rb +++ b/spec/strategies/completion_spec.rb @@ -4,7 +4,7 @@ describe 'the `completion` suggestion strategy' do -> do session. run_command('autoload compinit && compinit'). - run_command('_foo() { compadd bar }'). + run_command('_foo() { compadd bar; compadd bat }'). run_command('compdef _foo baz') end end From 68343c8de405da28a8bfa9baaa2cf0bc4b94f706 Mon Sep 17 00:00:00 2001 From: Eric Freese Date: Mon, 15 Apr 2019 14:12:55 -0600 Subject: [PATCH 8/9] Allow suggestions to suggest trailing newlines Command substitution via $() trims trailing newlines so the old approach to reading everything from the fd was preventing suggestions from ending with newlines. Found the read solution here: https://stackoverflow.com/a/15184414/154703 --- src/async.zsh | 5 ++++- zsh-autosuggestions.zsh | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/async.zsh b/src/async.zsh index b038cb0..d7e9fe8 100644 --- a/src/async.zsh +++ b/src/async.zsh @@ -56,9 +56,12 @@ _zsh_autosuggest_async_request() { _zsh_autosuggest_async_response() { emulate -L zsh + local suggestion + if [[ -z "$2" || "$2" == "hup" ]]; then # Read everything from the fd and give it as a suggestion - zle autosuggest-suggest -- "$(cat <&$1)" + IFS='' read -rd '' -u $1 suggestion + zle autosuggest-suggest -- "$suggestion" # Close the fd exec {1}<&- diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh index e7d9861..11ff7bd 100644 --- a/zsh-autosuggestions.zsh +++ b/zsh-autosuggestions.zsh @@ -769,9 +769,12 @@ _zsh_autosuggest_async_request() { _zsh_autosuggest_async_response() { emulate -L zsh + local suggestion + if [[ -z "$2" || "$2" == "hup" ]]; then # Read everything from the fd and give it as a suggestion - zle autosuggest-suggest -- "$(cat <&$1)" + IFS='' read -rd '' -u $1 suggestion + zle autosuggest-suggest -- "$suggestion" # Close the fd exec {1}<&- From f543ba08c33453c30d36195ce6fc91b6514369c9 Mon Sep 17 00:00:00 2001 From: Eric Freese Date: Wed, 17 Apr 2019 23:30:48 -0600 Subject: [PATCH 9/9] Fix cr/lf handling in completion strategy --- spec/strategies/completion_spec.rb | 12 ++++++++++++ src/strategies/completion.zsh | 10 ++++++++++ zsh-autosuggestions.zsh | 10 ++++++++++ 3 files changed, 32 insertions(+) diff --git a/spec/strategies/completion_spec.rb b/spec/strategies/completion_spec.rb index 1b4002b..e8cc8ce 100644 --- a/spec/strategies/completion_spec.rb +++ b/spec/strategies/completion_spec.rb @@ -14,6 +14,12 @@ describe 'the `completion` suggestion strategy' do wait_for { session.content }.to eq('baz bar') end + it 'does not add extra carriage returns when prefix has a line feed' do + skip '`stty` does not work inside zpty below zsh version 5.0.3' if session.zsh_version < Gem::Version.new('5.0.3') + session.send_string('baz \\').send_keys('C-v', 'C-j') + wait_for { session.content }.to eq("baz \\\nbar") + end + context 'when async mode is enabled' do let(:options) { ['ZSH_AUTOSUGGEST_USE_ASYNC=true', 'ZSH_AUTOSUGGEST_STRATEGY=completion'] } @@ -21,6 +27,12 @@ describe 'the `completion` suggestion strategy' do session.send_string('baz ') wait_for { session.content }.to eq('baz bar') end + + it 'does not add extra carriage returns when prefix has a line feed' do + skip '`stty` does not work inside zpty below zsh version 5.0.3' if session.zsh_version < Gem::Version.new('5.0.3') + session.send_string('baz \\').send_keys('C-v', 'C-j') + wait_for { session.content }.to eq("baz \\\nbar") + end end end diff --git a/src/strategies/completion.zsh b/src/strategies/completion.zsh index 06edc20..4c60e90 100644 --- a/src/strategies/completion.zsh +++ b/src/strategies/completion.zsh @@ -28,6 +28,16 @@ _zsh_autosuggest_capture_completion_widget() { # after autosuggestions is initialized. zle -- ${(k)widgets[(r)completion:.complete-word:_main_complete]} + if is-at-least 5.0.3; then + # Don't do any cr/lf transformations. We need to do this immediately before + # output because if we do it in setup, onlcr will be re-enabled when we enter + # vared in the async code path. There is a bug in zpty module in older versions + # where the tty is not properly attached to the pty slave, resulting in stty + # getting stopped with a SIGTTOU. See zsh-workers thread 31660 and upstream + # commit f75904a38 + stty -onlcr -ocrnl -F /dev/tty + fi + # The completion has been added, print the buffer as the suggestion echo -nE - $'\0'$BUFFER$'\0' } diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh index 11ff7bd..5f2401d 100644 --- a/zsh-autosuggestions.zsh +++ b/zsh-autosuggestions.zsh @@ -510,6 +510,16 @@ _zsh_autosuggest_capture_completion_widget() { # after autosuggestions is initialized. zle -- ${(k)widgets[(r)completion:.complete-word:_main_complete]} + if is-at-least 5.0.3; then + # Don't do any cr/lf transformations. We need to do this immediately before + # output because if we do it in setup, onlcr will be re-enabled when we enter + # vared in the async code path. There is a bug in zpty module in older versions + # where the tty is not properly attached to the pty slave, resulting in stty + # getting stopped with a SIGTTOU. See zsh-workers thread 31660 and upstream + # commit f75904a38 + stty -onlcr -ocrnl -F /dev/tty + fi + # The completion has been added, print the buffer as the suggestion echo -nE - $'\0'$BUFFER$'\0' }