Completing avfs fake directories in zsh

autocompleteavfsfilenameszsh

How can I tweak zsh's completion system to complete “fake” files in some circumstances?

More precisely, the AVFS filesystem exposes archives as directories by creating a “fake directory” next to every archive. By default, it replicates the whole directory hierarchy under its mount point ~/.avfs. Furthermore, under ~/.avfs, for every archive file such as /tmp/foo.zip, besides the archive file itself ~/.avfs/tmp/foo.zip, there is a directory called ~/.avfs/tmp/foo.zip# which exposes the content of the archive. However this extra directory does not appear in the listing of ~/.avfs/tmp: it only exists when explicitly requested. (This is similar to how some automounters operate.)

mountavfs
cd ~/.avfs/tmp/foo.zip\#

When I type a command like the one above, foo.zip# doesn't appear as a completion under ~/.avfs/tmp/, since there is no such directory. How can I tell zsh that whenever there is a file whose full path $arc matches ~/.avfs/**/*.(tgz|zip)¹, it should pretend that there is a directory called ${arc}#?

(Note that I want the completion to work with every call to _files or _dirs, not just for the cd command. I'd prefer not to have to duplicate the code of the _files function, if possible.)

¹ and all the rest of the extensions

Best Answer

A fake answer appears insufficient for this fake question; the zsh builtin support for automount directories operates on directories (fake-files dir:names), and not on file patterns therein. This is handy to add specific named files to a directory, which may suit automount or sshfs or NetApp .snapshot type setups where the directory-to-be-mounted is a known static name, ALAS.

autoload -U compinit
compinit
zstyle ':completion:*' fake-files $HOME/.avfs:'ALAS POOR YORICK'

zshall(1) says the "names" are strings, and experiments indicate metacharacters (e.g. # or \# or '*(e:"echo hi":)') "do not work" in various ways, so there is no way I know of to sneak a glob onto the names portion of the fake-files statement. (Delving through Src/Zle/computil.c might reveal exactly what the names can be, but that would be more work.) (Also, using recursive globs in the directory position to name archive files did not fly, but again it will take C diving to see how much fake-files lets you get away with.)

With compdef, one can list the .avfs directory completions:

compdef '_files -g $HOME/.avfs/\*' -p \^
function andthehash {
  reply=($REPLY $REPLY\#)
}
compdef "_files -g $HOME/.avfs/'*(+andthehash)'" -p \^

Except this fails, as it will only show the # files when it does not matter, and thus cannot complete on them. (There are productive uses for this form, though not for this case.)

Using zstyle gets you closer, with the following .zshrc:

autoload -U compinit
compinit

function UnderAVFS {
  local pdir
  [[ -z $1 ]] && return 1
  pdir=$1:a
  [[ ! -d $pdir ]] && pdir=$pdir:h
  [[ ! -d $pdir || $pdir == . ]] && return 1
  while [[ $pdir != / ]]; do
    [[ $pdir == $HOME/.avfs ]] && return 0
    pdir=$pdir:h
  done
  return 1
}

function AVFSExtras {
  if UnderAVFS $REPLY; then
    reply=($REPLY)
    # TODO embiggen list of archive suffixes
    [[ $REPLY == *.(bz2|tbz2) ]] && reply+=($REPLY\#)
  fi
}

# Try to match avfs, otherwise the default pattern. Probably could greatly
# benefit from caching, to avoid hits on UnderAVFS function.
zstyle ':completion:*' file-patterns '*(+AVFSExtras):avfs-files' '%p:all-files'

However, it can only complete up to the \#/ bit (on a debian squeeze virt with a default avfs setup), but not into the virtual filesystem for an archive, so additional work will be necessary to tab complete into the archives. I'm guessing via _call_program ... ls or something along those lines for completions at the \# bit, unless there's a more elegant way to con zsh into believing that directories that do not exist do.

Bad news! The completion does not make it through _path_files for the fake directories; I suspect this is zsh globbing related. Both zsh 4.something on and zsh 5.0.8 exhibit the problem. So I suspect that a complete fix would require patches to _path_files, or to write something different that otherwise completes on these fake dirs. A completion debugging trace can be generated by typing in ls /root/.avfs/root/sometar.gz\#/ and then at the end of that typing control-x and then ?, assuming bindkey -e and that _complete_debug is bound to said keycombo, if someone wants to delve through why this is failing.

Related Question