diff --git a/autosuggestions.zsh b/autosuggestions.zsh index fb5ffe6..e82dfd3 100644 --- a/autosuggestions.zsh +++ b/autosuggestions.zsh @@ -8,30 +8,12 @@ # ``` zmodload zsh/net/socket -AUTOSUGGEST_SERVER_SCRIPT="${0:a:h}/completion-server.zsh" +source "${0:a:h}/completion-client.zsh" -# function { -# [[ -n $ZLE_DISABLE_AUTOSUGGEST ]] && return -# setopt local_options no_hup - -# local server_dir="/tmp/zsh-autosuggest-$USER" -# local pid_file="$server_dir/pid" -# local socket_path="$server_dir/socket" - -# if ! [[ -S $socket_path && -r $pid_file ]] || ! kill -0 $(<$pid_file) &>/dev/null; then -# # start server -# zsh $AUTOSUGGEST_SERVER_SCRIPT $server_dir $pid_file $socket_path &! -# fi - -# integer remaining_tries=10 -# # wait until the process is listening -# while ! [[ -d $server_dir && -r $pid_file ]] || ! kill -0 $(<$pid_file) &> /dev/null; do -# (( --remaining_tries )) || break -# sleep 0.3 -# done - -# ZLE_AUTOSUGGEST_SOCKET=$socket_path -# } +function { + [[ -n $ZLE_DISABLE_AUTOSUGGEST ]] && return + autosuggest-ensure-server +} ZLE_AUTOSUGGEST_PAUSE_WIDGETS=( vi-cmd-mode vi-backward-char backward-char backward-word beginning-of-line @@ -169,25 +151,14 @@ paused-autosuggest-self-insert() { fi } -autosuggest-first-completion() { - zsocket $ZLE_AUTOSUGGEST_SOCKET &>/dev/null || return 1 - local connection=$REPLY - local completion - print -u $connection $LBUFFER - while read -u $connection completion; do - RBUFFER=" $completion" - break - done - exec {connection}>&- +autosuggest-get-completion() { + local suggestion=$(autosuggest-first-completion $LBUFFER) + RBUFFER="$suggestion" } show-suggestion() { [[ -n $ZLE_DISABLE_AUTOSUGGEST || $LBUFFER == '' ]] && return - # TODO need a way to reset HISTNO so .history-beginning-search-backward - # will always retrieve the last matching history entry - # unset HISTNO - zle .history-beginning-search-backward || autosuggest-first-completion\ - || RBUFFER='' + zle .history-beginning-search-backward || autosuggest-get-completion highlight-suggested-text } diff --git a/completion-client.zsh b/completion-client.zsh index 9439bd9..8edef1a 100755 --- a/completion-client.zsh +++ b/completion-client.zsh @@ -1,25 +1,36 @@ #!/usr/bin/env zsh -# Helper script for debugging the completion server zmodload zsh/net/socket -setopt no_hup + AUTOSUGGEST_SERVER_SCRIPT="${0:a:h}/completion-server.zsh" -server_dir="/tmp/zsh-autosuggest-$USER" -pid_file="$server_dir/pid" -socket_path="$server_dir/socket" +autosuggest-ensure-server() { + setopt local_options no_hup + local server_dir="/tmp/zsh-autosuggest-$USER" + local pid_file="$server_dir/pid" + local socket_path="$server_dir/socket" -[[ -S $socket_path && -r $pid_file ]] && kill -0 $(<$pid_file) &> /dev/null ||\ - zsh $AUTOSUGGEST_SERVER_SCRIPT $server_dir $pid_file $socket_path &! + [[ -S $socket_path && -r $pid_file ]] && \ + kill -0 $(<$pid_file) &> /dev/null || \ + zsh $AUTOSUGGEST_SERVER_SCRIPT $server_dir $pid_file $socket_path &! -# wait until the process is listening -while ! [[ -d $server_dir && -r $pid_file ]] || ! kill -0 $(<$pid_file) &> /dev/null; do - sleep 0.3 -done + integer remaining_tries=10 + # wait until the process is listening + while ! [[ -d $server_dir && -r $pid_file ]] ||\ + ! kill -0 $(<$pid_file) &> /dev/null && (( --remaining_tries )); do + sleep 0.3 + done + ZLE_AUTOSUGGEST_SOCKET=$socket_path +} -zsocket $socket_path -connection=$REPLY -print -u $connection vi -while read -u $connection completion; do - print $completion -done -exec {connection}>&- + +autosuggest-first-completion() { + zsocket $ZLE_AUTOSUGGEST_SOCKET &>/dev/null || return 1 + local connection=$REPLY + local completion + print -u $connection $1 + while read -u $connection completion; do + print ${completion} + done + # close fd + exec {connection}>&- +} diff --git a/completion-server-init.zsh b/completion-server-init.zsh index f5c59da..33939ac 100644 --- a/completion-server-init.zsh +++ b/completion-server-init.zsh @@ -1,6 +1,7 @@ # Based on: # https://github.com/Valodim/zsh-capture-completion/blob/master/.zshrc +ZLE_DISABLE_AUTOSUGGEST=1 # no prompt! PROMPT= @@ -38,6 +39,10 @@ zstyle ':completion:*' insert-tab false zstyle ':completion:*' list-separator '' # dont use matchers zstyle -d ':completion:*' matcher-list +# dont format +zstyle -d ':completion:*' format +# no color formatting +zstyle -d ':completion:*' list-colors # we use zparseopts zmodload zsh/zutil @@ -96,7 +101,6 @@ compadd () { # this is the point where we have all matches in $__hits and all # descriptions in $__dscr! - __hits=(${(O)__hits}) # display all matches local dsuf dscr for i in {1..$#__hits}; do diff --git a/completion-server.zsh b/completion-server.zsh index cb1a4fb..15d0856 100755 --- a/completion-server.zsh +++ b/completion-server.zsh @@ -2,7 +2,9 @@ # Based on: # https://github.com/Valodim/zsh-capture-completion/blob/master/capture.zsh +# close stdio exec &> /dev/null +exec < /dev/null zmodload zsh/zpty zmodload zsh/net/socket @@ -13,37 +15,30 @@ zpty z ZLE_DISABLE_AUTOSUGGEST=1 zsh -i # Source the init script zpty -w z "source '${0:a:h}/completion-server-init.zsh'" +# read all completions and return the longest match read-to-null() { - connection=$1 - integer consumed=0 while zpty -r z chunk; do [[ $chunk == *$'\0'* ]] && break - (( consumed++ )) && continue - if [[ -n $connection ]]; then - print -n -u $connection $chunk - else - print -n $chunk &> /dev/null - fi + print -n $chunk done } # wait for ok from shell -read-to-null +read-to-null &> /dev/null -# listen on an unix domain socket +# listen on a socket for completion requests server_dir=$1 pid_file=$2 socket_path=$3 cleanup() { - rm -f $socket_path - rm -f $pid_file + rm -f $socket_path $pid_file } trap cleanup TERM INT HUP EXIT -mkdir $server_dir &> /dev/null +mkdir -m 700 $server_dir &> /dev/null while ! zsocket -l $socket_path; do if [[ ! -r $pid_file ]] || ! kill -0 $(<$pid_file) &> /dev/null; then @@ -61,10 +56,26 @@ while zsocket -a $server &> /dev/null; do connection=$REPLY # connection accepted, read the request and send response while read -u $connection prefix &> /dev/null; do + # send the prefix to be completed followed by a TAB to force + # completion zpty -w -n z $prefix$'\t' zpty -r z chunk &> /dev/null # read empty line before completions - read-to-null $connection + local current='' + # read completions one by one, storing the longest match + read-to-null | while read line; do + (( $#line > $#current )) && current=$line + done + # send the longest completion back to the client, strip the last + # non-printable character + if (( $#current )); then + local last_word=${${(z)prefix}[-1]} + print -u $connection ${current:$#last_word:-1} + else + print -u $connection '' + fi + # close fd exec {connection}>&- + # clear input buffer zpty -w z $'\n' done done