about summary refs log tree commit diff
path: root/Completion/Builtins/_cd
diff options
context:
space:
mode:
Diffstat (limited to 'Completion/Builtins/_cd')
-rw-r--r--Completion/Builtins/_cd63
1 files changed, 61 insertions, 2 deletions
diff --git a/Completion/Builtins/_cd b/Completion/Builtins/_cd
index f3ce67ec7..65ce7f293 100644
--- a/Completion/Builtins/_cd
+++ b/Completion/Builtins/_cd
@@ -1,3 +1,62 @@
-#defcomp cd
+#defcomp cd pushd
 
-_files -W cdpath -g '*(-/)'
+# 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 pushd - or pushd +, 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.
+
+local pushdminus
+[[ -o pushdminus ]] && pushdminus=1
+
+emulate -LR zsh
+setopt extendedglob
+
+if [[ -position 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 [[ $words[1] = pu* && $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
+  # 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 = - && -z $pushdminus ) ||
+        ( $IPREFIX = + && -n $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
+  [[ -z $compstate[list] ]] && compstate[list]=list
+  [[ -n $compstate[insert] ]] && compstat[insert]=menu
+elif [[ $PREFIX != (\~|/|./|../)* && $#cdpath -ne 0 ]]; then
+  _path_files -W cdpath -/
+else
+  _path_files -/
+fi