diff --git a/src/async.zsh b/src/async.zsh index 3e61a0b..fd81bca 100644 --- a/src/async.zsh +++ b/src/async.zsh @@ -3,13 +3,8 @@ # Async # #--------------------------------------------------------------------# -_zsh_autosuggest_async_fetch_suggestion() { - # Send the prefix to the pty to fetch a suggestion - zpty -w -n $ZSH_AUTOSUGGEST_PTY_NAME "${1}"$'\0' -} - # Pty is spawned running this function -_zsh_autosuggest_async_suggestion_server() { +_zsh_autosuggest_async_server() { emulate -R zsh # Output only newlines (not carriage return + newline) @@ -18,40 +13,37 @@ _zsh_autosuggest_async_suggestion_server() { local strategy=$1 local last_pid - while IFS='' read -r -d $'\0' prefix; do + while IFS='' read -r -d $'\0' query; do # Kill last bg process kill -KILL $last_pid &>/dev/null # Run suggestion search in the background - (echo -n -E "$($strategy "$prefix")"$'\0') & + ( + local suggestion + _zsh_autosuggest_strategy_$ZSH_AUTOSUGGEST_STRATEGY "$query" + echo -n -E "$suggestion"$'\0' + ) & last_pid=$! done } +_zsh_autosuggest_async_request() { + # Send the query to the pty to fetch a suggestion + zpty -w -n $ZSH_AUTOSUGGEST_PTY_NAME "${1}"$'\0' +} + # Called when new data is ready to be read from the pty # First arg will be fd ready for reading # Second arg will be passed in case of error -_zsh_autosuggest_async_suggestion_ready() { +_zsh_autosuggest_async_response() { local suggestion zpty -rt $ZSH_AUTOSUGGEST_PTY_NAME suggestion '*'$'\0' 2>/dev/null - zle _autosuggest-show-suggestion "${suggestion%$'\0'}" + zle autosuggest-suggest "${suggestion%$'\0'}" } -# Recreate the pty to get a fresh list of history events -_zsh_autosuggest_async_recreate_pty() { - typeset -g _ZSH_AUTOSUGGEST_PTY_FD - - # Kill the old pty - if [ -n "$_ZSH_AUTOSUGGEST_PTY_FD" ]; then - # Remove the input handler - zle -F $_ZSH_AUTOSUGGEST_PTY_FD - - # Destroy the pty - zpty -d $ZSH_AUTOSUGGEST_PTY_NAME &>/dev/null - fi - +_zsh_autosuggest_async_pty_create() { # With newer versions of zsh, REPLY stores the fd to read from typeset -h REPLY @@ -63,7 +55,7 @@ _zsh_autosuggest_async_recreate_pty() { fi # Start a new pty running the server function - zpty -b $ZSH_AUTOSUGGEST_PTY_NAME "_zsh_autosuggest_async_suggestion_server _zsh_autosuggest_strategy_$ZSH_AUTOSUGGEST_STRATEGY" + zpty -b $ZSH_AUTOSUGGEST_PTY_NAME "_zsh_autosuggest_async_server _zsh_autosuggest_strategy_$ZSH_AUTOSUGGEST_STRATEGY" # Store the fd so we can remove the handler later if (( REPLY )); then @@ -73,5 +65,29 @@ _zsh_autosuggest_async_recreate_pty() { fi # Set up input handler from the pty - zle -F $_ZSH_AUTOSUGGEST_PTY_FD _zsh_autosuggest_async_suggestion_ready + zle -F $_ZSH_AUTOSUGGEST_PTY_FD _zsh_autosuggest_async_response +} + +_zsh_autosuggest_async_pty_destroy() { + if [ -n "$_ZSH_AUTOSUGGEST_PTY_FD" ]; then + # Remove the input handler + zle -F $_ZSH_AUTOSUGGEST_PTY_FD + + # Destroy the pty + zpty -d $ZSH_AUTOSUGGEST_PTY_NAME &>/dev/null + fi +} + +_zsh_autosuggest_async_pty_recreate() { + _zsh_autosuggest_async_pty_destroy + _zsh_autosuggest_async_pty_create +} + +_zsh_autosuggest_async_start() { + typeset -g _ZSH_AUTOSUGGEST_PTY_FD + + _zsh_autosuggest_async_pty_create + + # We recreate the pty to get a fresh list of history events + add-zsh-hook precmd _zsh_autosuggest_async_pty_recreate } diff --git a/src/config.zsh b/src/config.zsh index 68d3b32..29806ea 100644 --- a/src/config.zsh +++ b/src/config.zsh @@ -63,3 +63,6 @@ ZSH_AUTOSUGGEST_IGNORE_WIDGETS=( # Max size of buffer to trigger autosuggestion. Leave undefined for no upper bound. ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE= + +# Use asynchronous mode by default. Unset this variable to use sync mode. +ZSH_AUTOSUGGEST_USE_ASYNC= diff --git a/src/start.zsh b/src/start.zsh index 7a1725e..49af555 100644 --- a/src/start.zsh +++ b/src/start.zsh @@ -11,8 +11,10 @@ _zsh_autosuggest_start() { _zsh_autosuggest_check_deprecated_config _zsh_autosuggest_bind_widgets - _zsh_autosuggest_async_recreate_pty - add-zsh-hook precmd _zsh_autosuggest_async_recreate_pty + if [ -n "${ZSH_AUTOSUGGEST_USE_ASYNC+x}" ]; then + _zsh_autosuggest_async_start + fi } +# Start the autosuggestion widgets on the next precmd add-zsh-hook precmd _zsh_autosuggest_start diff --git a/src/strategies/default.zsh b/src/strategies/default.zsh index 4246b78..bbd5e59 100644 --- a/src/strategies/default.zsh +++ b/src/strategies/default.zsh @@ -15,6 +15,6 @@ _zsh_autosuggest_strategy_default() { local -a histkeys histkeys=(${(k)history[(r)$prefix*]}) - # Echo the value of the first key - echo -E "${history[$histkeys[1]]}" + # Give back the value of the first key + suggestion="${history[$histkeys[1]]}" } diff --git a/src/strategies/match_prev_cmd.zsh b/src/strategies/match_prev_cmd.zsh index 632a24b..ee26346 100644 --- a/src/strategies/match_prev_cmd.zsh +++ b/src/strategies/match_prev_cmd.zsh @@ -47,6 +47,6 @@ _zsh_autosuggest_strategy_match_prev_cmd() { fi done - # Echo the matched history entry - echo -E "$history[$histkey]" + # Give back the matched history entry + suggestion="$history[$histkey]" } diff --git a/src/widgets.zsh b/src/widgets.zsh index 66eef58..0f0520a 100644 --- a/src/widgets.zsh +++ b/src/widgets.zsh @@ -46,13 +46,35 @@ _zsh_autosuggest_modify() { # Get a new suggestion if the buffer is not empty after modification if [ $#BUFFER -gt 0 ]; then if [ -z "$ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE" -o $#BUFFER -lt "$ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE" ]; then - _zsh_autosuggest_async_fetch_suggestion "$BUFFER" + _zsh_autosuggest_fetch fi fi return $retval } +# Fetch a new suggestion based on what's currently in the buffer +_zsh_autosuggest_fetch() { + if zpty -t "$ZSH_AUTOSUGGEST_PTY_NAME" &>/dev/null; then + _zsh_autosuggest_async_request "$BUFFER" + else + local suggestion + _zsh_autosuggest_strategy_$ZSH_AUTOSUGGEST_STRATEGY "$BUFFER" + _zsh_autosuggest_suggest "$suggestion" + fi +} + +# Offer a suggestion +_zsh_autosuggest_suggest() { + local suggestion="$1" + + if [ -n "$suggestion" ]; then + POSTDISPLAY="${suggestion#$BUFFER}" + else + unset POSTDISPLAY + fi +} + # Accept the entire suggestion _zsh_autosuggest_accept() { local -i max_cursor_pos=$#BUFFER @@ -120,7 +142,7 @@ _zsh_autosuggest_partial_accept() { return $retval } -for action in clear modify accept partial_accept execute; do +for action in clear modify fetch suggest accept partial_accept execute; do eval "_zsh_autosuggest_widget_$action() { local -i retval @@ -131,28 +153,14 @@ for action in clear modify accept partial_accept execute; do _zsh_autosuggest_highlight_apply + zle -R + return \$retval }" done +zle -N autosuggest-fetch _zsh_autosuggest_widget_fetch +zle -N autosuggest-suggest _zsh_autosuggest_widget_suggest zle -N autosuggest-accept _zsh_autosuggest_widget_accept zle -N autosuggest-clear _zsh_autosuggest_widget_clear zle -N autosuggest-execute _zsh_autosuggest_widget_execute - -_zsh_autosuggest_show_suggestion() { - local suggestion="$1" - - _zsh_autosuggest_highlight_reset - - if [ -n "$suggestion" ]; then - POSTDISPLAY="${suggestion#$BUFFER}" - else - unset POSTDISPLAY - fi - - _zsh_autosuggest_highlight_apply - - zle -R -} - -zle -N _autosuggest-show-suggestion _zsh_autosuggest_show_suggestion diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh index d6b6377..5d56502 100644 --- a/zsh-autosuggestions.zsh +++ b/zsh-autosuggestions.zsh @@ -100,6 +100,9 @@ ZSH_AUTOSUGGEST_IGNORE_WIDGETS=( # Max size of buffer to trigger autosuggestion. Leave undefined for no upper bound. ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE= +# Use asynchronous mode by default. Unset this variable to use sync mode. +ZSH_AUTOSUGGEST_USE_ASYNC= + #--------------------------------------------------------------------# # Utility Functions # #--------------------------------------------------------------------# @@ -330,13 +333,35 @@ _zsh_autosuggest_modify() { # Get a new suggestion if the buffer is not empty after modification if [ $#BUFFER -gt 0 ]; then if [ -z "$ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE" -o $#BUFFER -lt "$ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE" ]; then - _zsh_autosuggest_async_fetch_suggestion "$BUFFER" + _zsh_autosuggest_fetch fi fi return $retval } +# Fetch a new suggestion based on what's currently in the buffer +_zsh_autosuggest_fetch() { + if zpty -t "$ZSH_AUTOSUGGEST_PTY_NAME" &>/dev/null; then + _zsh_autosuggest_async_request "$BUFFER" + else + local suggestion + _zsh_autosuggest_strategy_$ZSH_AUTOSUGGEST_STRATEGY "$BUFFER" + _zsh_autosuggest_suggest "$suggestion" + fi +} + +# Offer a suggestion +_zsh_autosuggest_suggest() { + local suggestion="$1" + + if [ -n "$suggestion" ]; then + POSTDISPLAY="${suggestion#$BUFFER}" + else + unset POSTDISPLAY + fi +} + # Accept the entire suggestion _zsh_autosuggest_accept() { local -i max_cursor_pos=$#BUFFER @@ -404,7 +429,7 @@ _zsh_autosuggest_partial_accept() { return $retval } -for action in clear modify accept partial_accept execute; do +for action in clear modify fetch suggest accept partial_accept execute; do eval "_zsh_autosuggest_widget_$action() { local -i retval @@ -415,32 +440,18 @@ for action in clear modify accept partial_accept execute; do _zsh_autosuggest_highlight_apply + zle -R + return \$retval }" done +zle -N autosuggest-fetch _zsh_autosuggest_widget_fetch +zle -N autosuggest-suggest _zsh_autosuggest_widget_suggest zle -N autosuggest-accept _zsh_autosuggest_widget_accept zle -N autosuggest-clear _zsh_autosuggest_widget_clear zle -N autosuggest-execute _zsh_autosuggest_widget_execute -_zsh_autosuggest_show_suggestion() { - local suggestion="$1" - - _zsh_autosuggest_highlight_reset - - if [ -n "$suggestion" ]; then - POSTDISPLAY="${suggestion#$BUFFER}" - else - unset POSTDISPLAY - fi - - _zsh_autosuggest_highlight_apply - - zle -R -} - -zle -N _autosuggest-show-suggestion _zsh_autosuggest_show_suggestion - #--------------------------------------------------------------------# # Default Suggestion Strategy # #--------------------------------------------------------------------# @@ -457,8 +468,8 @@ _zsh_autosuggest_strategy_default() { local -a histkeys histkeys=(${(k)history[(r)$prefix*]}) - # Echo the value of the first key - echo -E "${history[$histkeys[1]]}" + # Give back the value of the first key + suggestion="${history[$histkeys[1]]}" } #--------------------------------------------------------------------# @@ -509,21 +520,16 @@ _zsh_autosuggest_strategy_match_prev_cmd() { fi done - # Echo the matched history entry - echo -E "$history[$histkey]" + # Give back the matched history entry + suggestion="$history[$histkey]" } #--------------------------------------------------------------------# # Async # #--------------------------------------------------------------------# -_zsh_autosuggest_async_fetch_suggestion() { - # Send the prefix to the pty to fetch a suggestion - zpty -w -n $ZSH_AUTOSUGGEST_PTY_NAME "${1}"$'\0' -} - # Pty is spawned running this function -_zsh_autosuggest_async_suggestion_server() { +_zsh_autosuggest_async_server() { emulate -R zsh # Output only newlines (not carriage return + newline) @@ -532,40 +538,37 @@ _zsh_autosuggest_async_suggestion_server() { local strategy=$1 local last_pid - while IFS='' read -r -d $'\0' prefix; do + while IFS='' read -r -d $'\0' query; do # Kill last bg process kill -KILL $last_pid &>/dev/null # Run suggestion search in the background - (echo -n -E "$($strategy "$prefix")"$'\0') & + ( + local suggestion + _zsh_autosuggest_strategy_$ZSH_AUTOSUGGEST_STRATEGY "$query" + echo -n -E "$suggestion"$'\0' + ) & last_pid=$! done } +_zsh_autosuggest_async_request() { + # Send the query to the pty to fetch a suggestion + zpty -w -n $ZSH_AUTOSUGGEST_PTY_NAME "${1}"$'\0' +} + # Called when new data is ready to be read from the pty # First arg will be fd ready for reading # Second arg will be passed in case of error -_zsh_autosuggest_async_suggestion_ready() { +_zsh_autosuggest_async_response() { local suggestion zpty -rt $ZSH_AUTOSUGGEST_PTY_NAME suggestion '*'$'\0' 2>/dev/null - zle _autosuggest-show-suggestion "${suggestion%$'\0'}" + zle autosuggest-suggest "${suggestion%$'\0'}" } -# Recreate the pty to get a fresh list of history events -_zsh_autosuggest_async_recreate_pty() { - typeset -g _ZSH_AUTOSUGGEST_PTY_FD - - # Kill the old pty - if [ -n "$_ZSH_AUTOSUGGEST_PTY_FD" ]; then - # Remove the input handler - zle -F $_ZSH_AUTOSUGGEST_PTY_FD - - # Destroy the pty - zpty -d $ZSH_AUTOSUGGEST_PTY_NAME &>/dev/null - fi - +_zsh_autosuggest_async_pty_create() { # With newer versions of zsh, REPLY stores the fd to read from typeset -h REPLY @@ -577,7 +580,7 @@ _zsh_autosuggest_async_recreate_pty() { fi # Start a new pty running the server function - zpty -b $ZSH_AUTOSUGGEST_PTY_NAME "_zsh_autosuggest_async_suggestion_server _zsh_autosuggest_strategy_$ZSH_AUTOSUGGEST_STRATEGY" + zpty -b $ZSH_AUTOSUGGEST_PTY_NAME "_zsh_autosuggest_async_server _zsh_autosuggest_strategy_$ZSH_AUTOSUGGEST_STRATEGY" # Store the fd so we can remove the handler later if (( REPLY )); then @@ -587,7 +590,31 @@ _zsh_autosuggest_async_recreate_pty() { fi # Set up input handler from the pty - zle -F $_ZSH_AUTOSUGGEST_PTY_FD _zsh_autosuggest_async_suggestion_ready + zle -F $_ZSH_AUTOSUGGEST_PTY_FD _zsh_autosuggest_async_response +} + +_zsh_autosuggest_async_pty_destroy() { + if [ -n "$_ZSH_AUTOSUGGEST_PTY_FD" ]; then + # Remove the input handler + zle -F $_ZSH_AUTOSUGGEST_PTY_FD + + # Destroy the pty + zpty -d $ZSH_AUTOSUGGEST_PTY_NAME &>/dev/null + fi +} + +_zsh_autosuggest_async_pty_recreate() { + _zsh_autosuggest_async_pty_destroy + _zsh_autosuggest_async_pty_create +} + +_zsh_autosuggest_async_start() { + typeset -g _ZSH_AUTOSUGGEST_PTY_FD + + _zsh_autosuggest_async_pty_create + + # We recreate the pty to get a fresh list of history events + add-zsh-hook precmd _zsh_autosuggest_async_pty_recreate } #--------------------------------------------------------------------# @@ -602,8 +629,10 @@ _zsh_autosuggest_start() { _zsh_autosuggest_check_deprecated_config _zsh_autosuggest_bind_widgets - _zsh_autosuggest_async_recreate_pty - add-zsh-hook precmd _zsh_autosuggest_async_recreate_pty + if [ -n "${ZSH_AUTOSUGGEST_USE_ASYNC+x}" ]; then + _zsh_autosuggest_async_start + fi } +# Start the autosuggestion widgets on the next precmd add-zsh-hook precmd _zsh_autosuggest_start