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
  zstat -A data -L +link $link_path
  if [[ -z $target ]]; then
    print -rl 1>&2 "$link_path: not a symbolic link"
    return 2
  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

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?

  • You can define your own completion with vared with compdef myfunction -vared-. See zsh.org/mla/users/2007/msg01187.html for an example. Mar 24, 2014 at 12:22
  • Gilles, I tried your function and completion works fine for me inside it. Can you give a test case for which it fails? Jun 6, 2020 at 8:58
  • 1
    @MarlonRichert Completion is relative to the current directory instead of being relative to the directory containing the symlink. To reproduce: ln -s tmq link; mkdir tmp; cd tmp; lned ../link then press BackSpace and Tab. Completion offers nothing (because you're in an empty directory), but tmp is a valid target for the link. Jun 6, 2020 at 19:16

1 Answer 1


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

local curcontext=lned:::

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

_files -W $link_path:h/

Then add this to your ~/.zshrc:

zstyle ':completion:lned:*' completer _lned

Now it works as expected.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.