Repository: junegunn/fzf (language: go)

Issue:
Completion in zsh is always based on first command
<!-- ISSUES NOT FOLLOWING THIS TEMPLATE WILL BE CLOSED AND DELETED -->

<!-- Check all that apply [x] -->

- [x] I have read through the manual page (`man fzf`)
- [x] I have the latest version of fzf
- [x] I have searched through the existing issues

## Info

- OS
    - [x] Linux
    - [ ] Mac OS X
    - [ ] Windows
    - [ ] Etc.
- Shell
    - [ ] bash
    - [x] zsh
    - [ ] fish
    
## Problem / Steps to reproduce

In bash, you can invoke different completions in one line, e.g.:

    ssh **<Tab>; ls **<Tab>; unalias **<Tab>;

But in zsh, the ssh menu would be displayed everytime.


Discussion / hints:
see my comments on how I solved partitioning the command line to get command word.  Probably too complicated for fzf.  Maybe there is an easier way.

Fix patch:
diff --git a/shell/completion.zsh b/shell/completion.zsh
index 217404fc040..a6d686e98fd 100644
--- a/shell/completion.zsh
+++ b/shell/completion.zsh
@@ -120,25 +120,19 @@ __fzf_comprun() {
   fi
 }
 
-# Extract the name of the command. e.g. foo=1 bar baz**<tab>
+# Extract the name of the command. e.g. ls; foo=1 ssh **<tab>
 __fzf_extract_command() {
-  local token tokens
-  tokens=(${(z)1})
-  for token in $tokens; do
-    token=${(Q)token}
-    if [[ "$token" =~ [[:alnum:]] && ! "$token" =~ "=" ]]; then
-      echo "$token"
-      return
-    fi
-  done
-  echo "${tokens[1]}"
+  setopt localoptions noksh_arrays
+  # Control completion with the "compstate" parameter, insert and list noting
+  compstate[insert]=
+  compstate[list]=
+  cmd_word="${words[1]}"
 }
 
 __fzf_generic_path_completion() {
-  local base lbuf cmd compgen fzf_opts suffix tail dir leftover matches
+  local base lbuf compgen fzf_opts suffix tail dir leftover matches
   base=$1
   lbuf=$2
-  cmd=$(__fzf_extract_command "$lbuf")
   compgen=$3
   fzf_opts=$4
   suffix=$5
@@ -161,7 +155,7 @@ __fzf_generic_path_completion() {
         FZF_DEFAULT_OPTS=$(__fzf_defaults "--reverse --scheme=path" "${FZF_COMPLETION_OPTS-}")
         unset FZF_DEFAULT_COMMAND FZF_DEFAULT_OPTS_FILE
         if declare -f "$compgen" > /dev/null; then
-          eval "$compgen $(printf %q "$dir")" | __fzf_comprun "$cmd" ${(Q)${(Z+n+)fzf_opts}} -q "$leftover"
+          eval "$compgen $(printf %q "$dir")" | __fzf_comprun "$cmd_word" ${(Q)${(Z+n+)fzf_opts}} -q "$leftover"
         else
           if [[ $compgen =~ dir ]]; then
             walker=dir,follow
@@ -170,7 +164,7 @@ __fzf_generic_path_completion() {
             walker=file,dir,follow,hidden
             rest=${FZF_COMPLETION_PATH_OPTS-}
           fi
-          __fzf_comprun "$cmd" ${(Q)${(Z+n+)fzf_opts}} -q "$leftover" --walker "$walker" --walker-root="$dir" ${(Q)${(Z+n+)rest}} < /dev/tty
+          __fzf_comprun "$cmd_word" ${(Q)${(Z+n+)fzf_opts}} -q "$leftover" --walker "$walker" --walker-root="$dir" ${(Q)${(Z+n+)rest}} < /dev/tty
         fi | while read -r item; do
           item="${item%$suffix}$suffix"
           echo -n -E "${(q)item} "
@@ -227,10 +221,9 @@ _fzf_complete() {
     rest=("$@")
   fi
 
-  local fifo lbuf cmd matches post
+  local fifo lbuf matches post
   fifo="${TMPDIR:-/tmp}/fzf-complete-fifo-$$"
   lbuf=${rest[0]}
-  cmd=$(__fzf_extract_command "$lbuf")
   post="${funcstack[1]}_post"
   type $post > /dev/null 2>&1 || post=cat
 
@@ -238,7 +231,7 @@ _fzf_complete() {
   matches=$(
     FZF_DEFAULT_OPTS=$(__fzf_defaults "--reverse" "${FZF_COMPLETION_OPTS-} $str_arg") \
     FZF_DEFAULT_OPTS_FILE='' \
-      __fzf_comprun "$cmd" "${args[@]}" -q "${(Q)prefix}" < "$fifo" | $post | tr '\n' ' ')
+      __fzf_comprun "$cmd_word" "${args[@]}" -q "${(Q)prefix}" < "$fifo" | $post | tr '\n' ' ')
   if [ -n "$matches" ]; then
     LBUFFER="$lbuf$matches"
   fi
@@ -310,9 +303,16 @@ _fzf_complete_kill_post() {
 }
 
 fzf-completion() {
-  local tokens cmd prefix trigger tail matches lbuf d_cmds
+  trap 'unset cmd_word' EXIT
+  local tokens prefix trigger tail matches lbuf d_cmds
   setopt localoptions noshwordsplit noksh_arrays noposixbuiltins
 
+  # Check if at least one completion system (old or new) is active
+  if ! zmodload -F zsh/parameter p:functions 2>/dev/null || ! (( $+functions[compdef] )); then
+    if ! zmodload -e zsh/compctl; then
+      zmodload -i zsh/compctl
+    fi
+  fi
   # http://zsh.sourceforge.net/FAQ/zshfaq03.html
   # http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion-Flags
   tokens=(${(z)LBUFFER})
@@ -321,8 +321,6 @@ fzf-completion() {
     return
   fi
 
-  cmd=$(__fzf_extract_command "$LBUFFER")
-
   # Explicitly allow for empty trigger.
   trigger=${FZF_COMPLETION_TRIGGER-'**'}
   [ -z "$trigger" -a ${LBUFFER[-1]} = ' ' ] && tokens+=("")
@@ -340,16 +338,20 @@ fzf-completion() {
   if [ ${#tokens} -gt 1 -a "$tail" = "$trigger" ]; then
     d_cmds=(${=FZF_COMPLETION_DIR_COMMANDS-cd pushd rmdir})
 
+    # Make the 'cmd_word' global
+    zle __fzf_extract_command || :
+    [[ -z "$cmd_word" ]] && return
+
     [ -z "$trigger"      ] && prefix=${tokens[-1]} || prefix=${tokens[-1]:0:-${#trigger}}
     if [[ $prefix = *'$('* ]] || [[ $prefix = *'<('* ]] || [[ $prefix = *'>('* ]] || [[ $prefix = *':='* ]] || [[ $prefix = *'`'* ]]; then
       return
     fi
     [ -n "${tokens[-1]}" ] && lbuf=${lbuf:0:-${#tokens[-1]}}
 
-    if eval "type _fzf_complete_${cmd} > /dev/null"; then
-      prefix="$prefix" eval _fzf_complete_${cmd} ${(q)lbuf}
+    if eval "type _fzf_complete_${cmd_word} > /dev/null"; then
+      prefix="$prefix" eval _fzf_complete_${cmd_word} ${(q)lbuf}
       zle reset-prompt
-    elif [ ${d_cmds[(i)$cmd]} -le ${#d_cmds} ]; then
+    elif [ ${d_cmds[(i)$cmd_word]} -le ${#d_cmds} ]; then
       _fzf_dir_completion "$prefix" "$lbuf"
     else
       _fzf_path_completion "$prefix" "$lbuf"
@@ -366,6 +368,9 @@ fzf-completion() {
   unset binding
 }
 
+# Completion widget to gain access to the 'words' array (man zshcompwid)
+zle     -C   __fzf_extract_command .complete-word __fzf_extract_command
+# Normal widget
 zle     -N   fzf-completion
 bindkey '^I' fzf-completion
 fi
diff --git a/shell/key-bindings.zsh b/shell/key-bindings.zsh
index 3015f12d13f..c8c2076705c 100644
--- a/shell/key-bindings.zsh
+++ b/shell/key-bindings.zsh
@@ -111,7 +111,7 @@ fzf-history-widget() {
   # Ensure the module is loaded if not already, and the required features, such
   # as the associative 'history' array, which maps event numbers to full history
   # lines, are set. Also, make sure Perl is installed for multi-line output.
-  if zmodload -F zsh/parameter p:{commands,history} 2>/dev/null && (( ${#commands[perl]} )); then
+  if zmodload -F zsh/parameter p:{commands,history} 2>/dev/null && (( $+commands[perl] )); then
     # Import commands from other shells if SHARE_HISTORY is enabled, as the
     # 'history' array only updates after executing a non-empty command.
     selected="$(


Test patch: