Zsh – How to Call vared and Complete Files from a Different Directory

autocompletecd-commandzsh

I have the following zsh function which lets me edit symbolic links using the vared ZLE builtin.

function lned {
  emulate zsh
  setopt err_return extended_glob local_options local_traps
  local -a data
  link_path=${1%%/##}
  zstat -A data -L +link $link_path
  target=$data[1]
  if [[ -z $target ]]; then
    print -rl 1>&2 "$link_path: not a symbolic link"
    return 2
  fi
  vared -p "${(q)1} -> " target
  if [[ -n $target && "$target" != "$data[1]" ]]; then
    # We can't use 'ln -sf' if $link_path is a symlink to a directory, so remove
    # it manually first.
    # Check that this is still a symlink, in case the file was changed while we
    # were editing (this leaves only a very small time interval where we might
    # remove a non-symlink).
    rm -- $link_path(@)
    ln -s -- $target $link_path
  fi
}

This is nice, but when I'm editing a relative symbolic link in a directory other than the current one, the file name completions are relative to the wrong directory. I want to fix that.

I could run pushd -- $link_path:h/ before calling vared and popd afterwards. However, this would not restore the current directory if the user interrupted the shell by pressing Ctrl+C at the wrong moment. This can be solved by setting traps exactly right, and maybe using cd rather than pushd, which is probably useful also to avoid having to detect whether the link is in the current directory (in which case pushd wouldn't push anything).

I'd like to minimize the disruption caused by the directory change, which means also locally turning off chpwd, restoring OLDPWD, and perhaps more which I'm not thinking of. Also, if I have no permission to cd back into the current directory, I'd be in a lurch — this is a very rare case but I'd prefer to support it.

It would be simpler if I could just set things up so that files are completed relative to a different directory during the vared call. How can I do that? If this is impossible, what's the best way to minimize the impact of a temporary directory change?

Best Answer

In your function lned, add the following before calling vared:

local curcontext=lned:::

Put the following in a file called _lned in your $fpath:

#autoload
_files -W $link_path:h/

Then add this to your ~/.zshrc:

zstyle ':completion:lned:*' completer _lned

Now it works as expected.

Related Question