0

I'm trying to make fzfs autocompletion invoked on cat **<TAB> return only files (directories does not make sense for cat). According to docs it's the matter of defining _fzf_complete_cat(), so my first try was

_fzf_complete_cat() {
  _fzf_complete -- "$@" < <(
    fd --type f --hidden --follow --no-ignore --exclude .git 
  )
}

however, there's a major flaw: this completion does not take into account the path's part before **, e.g. if I am in ~/subdir and type cat ~/another/**<TAB> it will not recurse from there, but rather from ~/subdir.

I understand the trick is around these lines from fzf/shell/completion.zsh, namely, the way _fzf_[path|dir]_completion() handles it. So I tried smth like that (essentially, repeating the logic of the completion.zsh):

_custom_fzf_compgen_path() {
  fd --hidden --follow --no-ignore --exclude ".git" . "$1"
}

_custom_fzf_path_completion() {
  __fzf_generic_path_completion "$1" "$2" _fzf_compgen_path \
    "-m" "" " "
}

_fzf_complete_cat() {
  _custom_fzf_path_completion "$prefix" "$1"
}

and it almost works, messing up in the very end with zle reset-prompt from here.

TL;DR: I need **<TAB> for cat command work similarly to _fzf_[path|dir]_completion() but return only files.

Appreciate any ideas! Thanks!

agronskiy
  • 300
  • 4
  • 11
  • 1
    [This answer](https://stackoverflow.com/a/64416946/14467607) of mine might help you (particularly if combined with [this answer](https://stackoverflow.com/a/67019648/14467607)). – mattb Dec 07 '21 at 23:27
  • Thank you! I wanted to do that in a way alighned with the how fzf invokes tab completion for relative paths (it extracts base) so I ended up tweaking fzf's. – agronskiy Dec 08 '21 at 10:51

1 Answers1

1

I ended up going down the initial path of tweaking _fzf_complete_cat(). It uses a wonderful function __fzf_generic_path_completion() which takes care of splitting the path yped before **<TAB> (e.g. cat ~/some_other_dir/**<TAB>) and finding the base of it. Problem lied around extra zle reset-prompt in here, so I had to create my own __fzf_generic_path_completion() without it.

Full working solution

__custom_fzf_generic_path_completion() {
  local base lbuf cmd compgen fzf_opts suffix tail dir leftover matches
  base=$1
  lbuf=$2
  cmd=$(__fzf_extract_command "$lbuf")
  compgen=$3
  fzf_opts=$4
  suffix=$5
  tail=$6

  setopt localoptions nonomatch
  eval "base=$base"
  [[ $base = *"/"* ]] && dir="$base"
  while [ 1 ]; do
    if [[ -z "$dir" || -d ${dir} ]]; then
      leftover=${base/#"$dir"}
      leftover=${leftover/#\/}
      [ -z "$dir" ] && dir='.'
      [ "$dir" != "/" ] && dir="${dir/%\//}"
      matches=$(eval "$compgen $(printf %q "$dir")" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore $FZF_DEFAULT_OPTS $FZF_COMPLETION_OPTS" __fzf_comprun "$cmd" ${(Q)${(Z+n+)fzf_opts}} -q "$leftover" | while read item; do
        echo -n "${(q)item}$suffix "
      done)
      matches=${matches% }
      if [ -n "$matches" ]; then
        LBUFFER="$lbuf$matches$tail"
      fi
      # REMOVED HERE zle reset-prompt
      break
    fi
    dir=$(dirname "$dir")
    dir=${dir%/}/
  done
}

_custom_fzf_compgen_path() {
  # Add here your favorite command to search for files. $1 is the starting
  # point.
  fd --type f --hidden --follow --no-ignore --exclude ".git" . "$1"
}

_custom_fzf_path_completion() {
  __custom_fzf_generic_path_completion "$1" "$2" _custom_fzf_compgen_path \
    "-m" "" " "
}

_fzf_complete_cat() {
  # $prefix is the variable set by fzf here https://github.com/junegunn/fzf/blob/master/shell/completion.zsh#L302
  _custom_fzf_path_completion "$prefix" "$1"
}
agronskiy
  • 300
  • 4
  • 11