In zsh, the cd command has a two-argument form: cd OLD NEW changes to ${PWD/OLD/NEW}. With the new-style completion system, zsh is capable of completing NEW: the second argument is completed based on what OLD can be substituted with to obtain an existing directory. But the first argument is only completed to existing directories.

How can I get zsh to offer completions that are possible values for OLD, in addition to completing existing directories?

For example, if the current directory is /path/to/foo and there are also directories /also/to/foo and /path/to/foo/prime, then cd pTab completes p to prime. If I intend to run cd path also then I'd like zsh to also offer path as a completion. How?

Using already-typed values of the second argument to limit the possibilities for the first argument would be a plus, but completing the first argument independently would be fine too.

  • Would your 2-argument completion example be: cd p also <Tab>, or cd p also <left arrow x 5> <Tab>?
    – Jeff Schaller
    May 11, 2016 at 0:41
  • @JeffSchaller The second-argument-sensitive example would be cd p also<Left*5><Tab>: if I press <Tab> while the cursor is in also, I expect also to be completed, not p. May 11, 2016 at 7:29
  • If you type cd t<tab> should it offer th/to/foo/prime and to/foo/prime? Or should it just limit itself to directory boundaries?
    – Barmar
    May 11, 2016 at 18:30
  • @Barmar I think limiting to directory boundaries would be the most convenient. May 11, 2016 at 18:51
  • INteresting question...
    – wogsland
    Mar 19, 2017 at 15:27

1 Answer 1


I guess you could add the components of $PWD to the cd completion list, though this appears to require fiddling with _cd; that is, a customized version of _cd must appear first in $fpath.

% cd && mkdir zcomp
% cp $fpath[-1]/_cd zcomp
% fpath=(~/zcomp $fapth)

Then up at the top of ~/zcomp/_cd add a function

_our_pwd() {
  _values ourpwd ${(ps:/:)PWD}

and then just before the _alternative line add what that returns to the list of alternatives

  alt=("$service-options:$service option:_cd_options" "$alt[@]")

alt=(ourpwd:pwd:_our_pwd "$alt[@]")

_alternative "$alt[@]" && ret=0

return ret

though this will always add the pwd components to cd completions:

% cd
Users    jdoe    Applications/  Desktop/  Documents/  Downloads/  Library/

with additional logic you could only add the $PWD components when there is already a second argument present instead of always.

However! This always messes up the cd completion and requires that we monkey patch the upstream _cd completion. Another option would be to create a new name for the function provided by the two-arg cd, perhaps called cdsub, and only have the completion of PWD components appear for that. Add this to ~/.zshrc

function cdsub { builtin cd "$@" }

And then a gutted _cd completion for _cdsub to be placed somewhere in $fpath:

#compdef cdsub
# Modified version of _cd from ZSH 5.3.1 with specific support for the
# `cd old new` form whereby PWD elements are provided for completion.

_cd_options() {
  _arguments -s \
  '-q[quiet, no output or use of hooks]' \
  '-s[refuse to use paths with symlinks]' \
  '(-P)-L[retain symbolic links ignoring CHASE_LINKS]' \
  '(-L)-P[resolve symbolic links as CHASE_LINKS]'

setopt localoptions nonomatch

local expl ret=1 curarg
integer argstart=2 noopts

if (( CURRENT > 1 )); then
  # if not in command position, may have options.
  # Careful: -<-> is not an option.
  while [[ $words[$argstart] = -* && argstart -lt CURRENT ]]; do
    [[ $curarg = -<-> ]] && break
    (( argstart++ ))
    [[ $curarg = -- ]] && noopts=1 && break

if [[ CURRENT -eq $((argstart+1)) ]]; then
  # cd old new: look for old in $PWD and see what can replace it
  local rep
  # Get possible completions using word in position 2
  # Now remove all the common parts of $PWD and the completions from this
  (( $#rep )) && _wanted -C replacement strings expl replacement compadd -a rep
  _values ourpwd ${(ps:/:)PWD} && ret=0
  return ret

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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