about summary refs log tree commit diff
path: root/Completion/Builtins/_cd
blob: 9ac29b8f85b0e02ba1694dc575693d4225f527d9 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#compdef cd pushd

# Handling of cd.
#  - Normally just completes directories.  Uses cdpath if that's set
#    and the string doesn't begin with ~, /, ./ or ../.
#  - In the second argument to cd for the form `cd old new', completes
#    possible `new' strings by examining `old' and $PWD.
#  - After - or +, completes numbers, but the listing
#    gives you the list of directories to complete.  This turns on
#    menu-completion and lists the possibilities automatically, otherwise
#    it's not a lot of use.  If you don't type the + or - it will
#    complete directories as normal.

emulate -L zsh
setopt extendedglob nonomatch

if [[ CURRENT -eq 3 ]]; 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
  rep=(${~PWD/$words[2]/*}~$PWD(-/N))
  # Now remove all the common parts of $PWD and the completions from this
  rep=(${${rep#${PWD%%$words[2]*}}%${PWD#*$words[2]}})
  (( ! $#rep )) || compadd $rep
elif [[ $PREFIX = [-+]* ]]; then
  # pushd: just complete the numbers, but show the full directory list with
  # numbers.
  # For - we do the same thing, but reverse the numbering (other
  # way round if pushdminus is set).
  # The test is for pu* because I have an alias pu since I'm too
  # lazy to type pushd.
  IPREFIX=$PREFIX[1]
  PREFIX=$PREFIX[2,-1]
  local list lines ret=1

  # get the list of directories with their canonical number
  lines="$(dirs -v)"
  # turn the lines into an array, removing the current directory
  list=(${${(f)lines}##0*})
  if [[ ( $IPREFIX = - && ! -o pushdminus ) ||
        ( $IPREFIX = + && -o pushdminus ) ]]; then
    # reverse the numbering: it counts the last one as -0, which
    # is a little strange.
    integer tot i
    for (( i = 1, tot = $#list-1; tot >= 0; i++, tot-- )); do
      list[$i]="$tot${list[$i]##[0-9]#}"
    done
  fi
  # make sure -y treats this as a single string
  lines="${(F)list}"
  # get the array of numbers only
  list=(${list%%[ 	]*})
  compgen -y '$lines' -Q -k list && ret=0
  [[ -z $compstate[list] ]] && compstate[list]=list && ret=0
  [[ -n $compstate[insert] ]] && compstate[insert]=menu && ret=0

  return ret
elif [[ $PREFIX != (\~|/|./|../)* && $#cdpath -ne 0 ]]; then
  local tdir tdir2
  # With cdablevars, we can convert foo/bar/... to ~foo/bar/... if
  # there is no directory foo.  In that case we could also complete
  # variable names, but it hardly seems worth it.
  # Note we need a tilde because cdablevars also allows user home
  # directories, hence we also need nonomatch to suppress error messages.
  if [[ -o cdablevars && ! -d ${tdir::=${PREFIX%%/*}} &&
    -d ${~tdir2::="~$tdir"} ]]; then
      PREFIX="~$PREFIX"
      _path_files -/
  else
    _path_files -W "(. $cdpath)" -/
  fi
else
  _path_files -/
fi