summaryrefslogtreecommitdiffstats
path: root/Completion
diff options
context:
space:
mode:
authorTanaka Akira <akr@users.sourceforge.net>1999-04-15 18:20:19 +0000
committerTanaka Akira <akr@users.sourceforge.net>1999-04-15 18:20:19 +0000
commit04a89199d02a3ee6c4b3d89a6c782bdb0a4f1bc8 (patch)
tree2215f99f95d55660fc939a029bf965c454d080b5 /Completion
parentzsh-3.1.5-pws-11 (diff)
downloadzsh-04a89199d02a3ee6c4b3d89a6c782bdb0a4f1bc8.tar
zsh-04a89199d02a3ee6c4b3d89a6c782bdb0a4f1bc8.tar.gz
zsh-04a89199d02a3ee6c4b3d89a6c782bdb0a4f1bc8.tar.bz2
zsh-04a89199d02a3ee6c4b3d89a6c782bdb0a4f1bc8.tar.lz
zsh-04a89199d02a3ee6c4b3d89a6c782bdb0a4f1bc8.tar.xz
zsh-04a89199d02a3ee6c4b3d89a6c782bdb0a4f1bc8.tar.zst
zsh-04a89199d02a3ee6c4b3d89a6c782bdb0a4f1bc8.zip
zsh-3.1.5-pws-12zsh-3.1.5-pws-12
Diffstat (limited to 'Completion')
-rw-r--r--Completion/.distfiles3
-rw-r--r--Completion/Base/.distfiles6
-rw-r--r--Completion/Base/_command_names10
-rw-r--r--Completion/Base/_condition8
-rw-r--r--Completion/Base/_default2
-rw-r--r--Completion/Base/_long_options309
-rw-r--r--Completion/Base/_match_pattern6
-rw-r--r--Completion/Base/_subscript9
-rw-r--r--Completion/Builtins/.distfiles7
-rw-r--r--Completion/Builtins/_bindkey11
-rw-r--r--Completion/Builtins/_cd13
-rw-r--r--Completion/Builtins/_disable12
-rw-r--r--Completion/Builtins/_enable12
-rw-r--r--Completion/Builtins/_kill8
-rw-r--r--Completion/Builtins/_setopt9
-rw-r--r--Completion/Builtins/_unhash12
-rw-r--r--Completion/Builtins/_unsetopt9
-rw-r--r--Completion/Builtins/_wait8
-rw-r--r--Completion/Builtins/_zftp3
-rw-r--r--Completion/Commands/.distfiles3
-rw-r--r--Completion/Core/.distfiles5
-rw-r--r--Completion/Core/_compalso6
-rw-r--r--Completion/Core/_files11
-rw-r--r--Completion/Core/_main_complete114
-rw-r--r--Completion/Core/_normal16
-rw-r--r--Completion/Core/_path_files141
-rw-r--r--Completion/Core/_sep_parts171
-rw-r--r--Completion/Core/compdump2
-rw-r--r--Completion/Core/compinit48
-rw-r--r--Completion/README14
-rw-r--r--Completion/User/.distfiles6
-rw-r--r--Completion/User/_a2ps53
-rw-r--r--Completion/User/_configure35
-rw-r--r--Completion/User/_dd2
-rw-r--r--Completion/User/_find8
-rw-r--r--Completion/User/_man2
-rw-r--r--Completion/User/_mh14
-rw-r--r--Completion/User/_rcs1
-rw-r--r--Completion/User/_strip1
-rw-r--r--Completion/User/_tar162
-rw-r--r--Completion/User/_tar_archive20
41 files changed, 1039 insertions, 253 deletions
diff --git a/Completion/.distfiles b/Completion/.distfiles
new file mode 100644
index 000000000..c50107c61
--- /dev/null
+++ b/Completion/.distfiles
@@ -0,0 +1,3 @@
+DISTFILES_SRC='
+ .distfiles README
+'
diff --git a/Completion/Base/.distfiles b/Completion/Base/.distfiles
new file mode 100644
index 000000000..7e7635fa6
--- /dev/null
+++ b/Completion/Base/.distfiles
@@ -0,0 +1,6 @@
+DISTFILES_SRC='
+ .distfiles
+ _brace_parameter _command_names _condition _default _equal
+ _long_options _match_pattern _match_pattern.orig _match_test _parameter
+ _precommand _redirect _subscript _tilde _vars
+'
diff --git a/Completion/Base/_command_names b/Completion/Base/_command_names
index eab314dfa..f21af674c 100644
--- a/Completion/Base/_command_names
+++ b/Completion/Base/_command_names
@@ -1,7 +1,11 @@
#defcomp -command-
-local nm=$compstate[nmatches]
+local nm=$compstate[nmatches] ret=1
-compgen -c
+compgen -c && ret=0
-[[ nm -eq compstate[nmatches] ]] && _path_files -/g "*(*)"
+if [[ nm -eq compstate[nmatches] ]]; then
+ _path_files -/g "*(*)"
+else
+ return ret
+fi
diff --git a/Completion/Base/_condition b/Completion/Base/_condition
index fb6b98b1b..db1adfd9a 100644
--- a/Completion/Base/_condition
+++ b/Completion/Base/_condition
@@ -7,6 +7,10 @@ if [[ "$prev" = -o ]]; then
elif [[ "$prev" = -([no]t|ef) ]]; then
_files
else
- _files
- compgen -v
+ local ret=1
+
+ _files && ret=0
+ compgen -v && ret=0
+
+ return ret
fi
diff --git a/Completion/Base/_default b/Completion/Base/_default
index 569bd6382..9ea1a09db 100644
--- a/Completion/Base/_default
+++ b/Completion/Base/_default
@@ -9,6 +9,6 @@
# and insert the line `[[ compstate[nmatches] -eq 0 ]] || return' after
# `compcall'.
-compcall || return
+compcall || return 0
_files
diff --git a/Completion/Base/_long_options b/Completion/Base/_long_options
new file mode 100644
index 000000000..a5d92632c
--- /dev/null
+++ b/Completion/Base/_long_options
@@ -0,0 +1,309 @@
+#autoload
+
+# This function tries to automatically complete long option names. For
+# this it invokes the command from the line with the `--help' option
+# and then parses the output to find possible option names. For
+# options that get an argument after a `=', the function also tries to
+# automatically find out what should be complete as the argument.
+# The possible completions for option-arguments can be described with
+# the arguments to this function. This is done by giving pairs of
+# patterns and actions as consecutive arguments. The actions specify
+# what should be done to complete arguemts of those options that match
+# the pattern. The action may be a list of words in brackets or in
+# parentheses, separated by spaces. A list in brackets denotes
+# possible values for an optional argument, a list in parentheses
+# gives words to complete for mandatory arguments. If the action does
+# not start with a bracket or parentheses, it should be the name of a
+# command (probably with arguments) that should be invoked to complete
+# after the equal sign. E.g.:
+#
+# _long_options '*\*' '(yes no)' \
+# '*=FILE*' '_files' \
+# '*=DIR*' '_files -/'
+#
+# This makes `yes' and `no' be completed as the argument of options
+# whose description ends in a star, file names for options that
+# contain the substring `=FILE' in the description, and paths for
+# options whose description contains `=DIR'. Note the last two
+# patterns are not needed since this function always completes files
+# for option descriptions containing `=FILE' and paths for option
+# descriptions that contain `=DIR' or `=PATH'. These builtin patterns
+# can be overridden by patterns given as arguments, though.
+#
+# This function also accepts the `-X', `-J', and `-V' options which
+# are given to `compadd'. Finally, it accepts the option `-t'. If this
+# is given, completion is only done on words starting with two hyphens.
+
+local opt expl group test i name action ret=1 tmp suffix
+
+setopt extendedglob
+
+# Get the options.
+
+group=()
+expl=()
+if [[ $1 = -*~--* ]]; then
+ while getopts "J:V:X:t" opt; do
+ case "$opt" in
+ [JV]) group=("-$opt" "$OPTARG");;
+ X) expl=(-X "$OPTARG");;
+ t) test=yes;;
+ esac
+ done
+ shift OPTIND-1
+fi
+
+# Test if we are completing after `--' if we were asked to do so.
+
+[[ -n "$test" && "$PREFIX" != --* ]] && return 1
+
+# We cache the information about options and the command name, see if
+# we can use the cache.
+
+if [[ "$words[1]" = (.|..)/* ]]; then
+ tmp="$PWD/$words[1]"
+else
+ tmp="$words[1]"
+fi
+
+if [[ "$tmp" != $_lo_cache_cmd ]]; then
+
+ # No, store the new command name and clear the old parameters.
+
+ _lo_cache_cmd="$tmp"
+ (( $+_lo_cache_actions )) && unset "$_lo_cache_names[@]" _lo_cache_actions _lo_cache_names
+
+ local opts pattern anum=1 tmpo str
+
+ # Now get the long option names by calling the command with `--help'.
+ # The parameter expansion trickery first gets the lines as separate
+ # array elements. Then we select all lines whose first non-blank
+ # character is a hyphen. Since some commands document more than one
+ # option per line, separated by commas, we convert commas int
+ # newlines and then split the result again at newlines after joining
+ # the old array elements with newlines between them. Then we select
+ # those elements that start with two hyphens, remove anything up to
+ # those hyphens and anything from the space or comma after the
+ # option up to the end. Finally all elements with option strings
+ # that contain uppercase letters are removed.
+
+ opts=("--${(@)^${(@)${(@)${(@M)${(@ps:\n:j:\n:)${(@)${(@M)${(@f)$("$words[1]" --help)}:#[ ]#-*}//,/
+}}:#[ ]#--*}#*--}%%[, ]*}:#(*-[A-Z]*|)}")
+
+ # The interpretation of the options is completely table driven. We
+ # use the positional parameters we were given and a few standard
+ # ones. Then we loop through this table.
+
+ set -- "$@" '*=FILE*' '_files' '*=(DIR|PATH)*' '_files -/' '*' ''
+
+ while [[ $# -gt 1 ]]; do
+
+ # First, we get the pattern and the action to use and take them
+ # from the positional parameters.
+
+ pattern="$1"
+ action="$2"
+ shift 2
+
+ # We get all options matching the pattern and take them from the
+ # list we have built. If no option matches the pattern, we
+ # continue with the next.
+
+ tmp=("${(@M)opts:##$~pattern}")
+ opts=("${(@)opts:##$~pattern}")
+
+ (( $#tmp )) || continue
+
+ # Now we collect the options for the pattern in an array. We also
+ # check if the options take an argument after a `=', and if this
+ # argument is optional. The name of the array built contains
+ # `_arg_' for mandatory arguments, `_optarg_' for optional
+ # arguments, and `_simple_' for options that don't get an
+ # argument. In `_lo_cache_names' we save the names of these
+ # arrays and in `_lo_cache_actions' the associated actions.
+
+ # If the action is a list of words in brackets, this denotes
+ # options that get an optional argument. If the action is a list
+ # of words in parentheses, the option has to get an argument.
+ # In both cases we just build the array name to use.
+
+ if [[ "$action[1]" = '[' ]]; then
+ name="_lo_cache_optarg_$anum"
+ elif [[ "$action[1]" = '(' ]]; then
+ name="_lo_cache_arg_$anum"
+ else
+
+ # If there are option strings with a `[=', we take make these
+ # get an optional argument...
+
+ tmpo=("${(@M)tmp:#*\[\=*}")
+ if (( $#tmpo )); then
+
+ # ...by removing them from the option list and storing them in
+ # an array.
+
+ tmp=("${(@)tmp:#*\[\=*}")
+ tmpo=("${(@)${(@)tmpo%%\=*}//[^a-z0-9-]}")
+ _lo_cache_names[anum]="_lo_cache_optarg_$anum"
+ _lo_cache_actions[anum]="$action"
+ eval "_lo_cache_optarg_${anum}=(\"\$tmpo[@]\")"
+ (( anum++ ))
+ fi
+
+ # Now we do the same for option strings containing `=', these
+ # are options getting an argument.
+
+ tmpo=("${(@M)tmp:#*\=*}")
+ if (( $#tmpo )); then
+ tmp=("${(@)tmp:#*\=*}")
+ tmpo=("${(@)${(@)tmpo%%\=*}//[^a-z0-9-]}")
+ _lo_cache_names[anum]="_lo_cache_arg_$anum"
+ _lo_cache_actions[anum]="$action"
+ eval "_lo_cache_arg_${anum}=(\"\$tmpo[@]\")"
+ (( anum++ ))
+ fi
+
+ # The name for the options without arguments, if any.
+
+ name="_lo_cache_simple_$anum"
+ fi
+ # Now filter out any option strings we don't like and stuff them
+ # in an array, if there are still some.
+
+ tmp=("${(@)${(@)tmp%%\=*}//[^a-z0-9-]}")
+ if (( $#tmp )); then
+ _lo_cache_names[anum]="$name"
+ _lo_cache_actions[anum]="$action"
+ eval "${name}=(\"\$tmp[@]\")"
+ (( anum++ ))
+ fi
+ done
+fi
+
+# We get the string from the line and and see if it already contains a
+# equal sign.
+
+str="$PREFIX$SUFFIX"
+
+if [[ "$str" = *\=* ]]; then
+
+ # It contains a `=', now we ignore anything up to it, but first save
+ # the old contents of the special parameters we change.
+
+ local oipre opre osuf pre parto parta pat patflags anum=1
+
+ oipre="$IPREFIX"
+ opre="$PREFIX"
+ osuf="$SUFFIX"
+
+ pre="${str%%\=*}"
+ IPREFIX="${IPREFIX}${pre}="
+ PREFIX="${str#*\=}"
+ SUFFIX=""
+
+ # We will check if the arrays contain an option matching what's on
+ # the line. To do this good, we build a pattern.
+
+ [[ -n "$_comp_correct" && $#pre -le _comp_correct ]] && return 1
+
+ pat="${pre}*"
+ patflags=''
+ _match_pattern _long_options pat patflags
+ [[ -n "$_comp_correct" ]] && patflags="$patflags(#a$_comp_correct)"
+
+ # Then we walk through the array names. For each array we test if it
+ # contains the option string. If so, we `invoke' the action stored
+ # with the name. If the action is a list of words, we just add them,
+ # otherwise we invoke the command or function named.
+
+ for name in "$_lo_cache_names[@]"; do
+ action="$_lo_cache_actions[anum]"
+ if (( ${(@)${(@P)name}[(I)$pre]} )); then
+ if [[ "$action[1]" = (\[|\() ]]; then
+ compadd - ${=action[2,-2]}
+ elif (( $#action )); then
+ $=action
+ fi
+
+ # We found the option string, return.
+
+ return
+ fi
+
+ # The array did not contain the full option string, see if it
+ # contains a string matching the string from the line.
+ # If there is one, we store the option string in `parto' and the
+ # element from `_lo_actions' in `parta'. If we find more than one
+ # such option or if we already had one, we set `parto' to `-'.
+
+ tmp=("${(@M)${(@P)name}:#${~pat}}")
+ if [[ $#tmp -eq 1 ]]; then
+ if [[ -z "$parto" ]]; then
+ parto="$tmp[1]"
+ parta="$action"
+ else
+ parto=-
+ fi
+ elif (( $#tmp )); then
+ parto=-
+ fi
+ (( anum++ ))
+ done
+
+ # If we found only one matching option, we accept it and immediatly
+ # try to complete the string after the `='.
+
+ if [[ -n "$parto" && "$parto" != - ]]; then
+ IPREFIX="${parto}="
+
+ if (( $#parta )); then
+ if [[ "$parta[1]" = (\[|\() ]]; then
+ compadd - ${=parta[2,-2]}
+ else
+ $=parta
+ fi
+ else
+ compadd -S '' - "$PREFIX"
+ fi
+ return
+ fi
+
+ # The option string was not found, restore the special parameters.
+
+ IPREFIX="$oipre"
+ PREFIX="$opre"
+ SUFFIX="$osuf"
+fi
+
+# The string on the line did not contain a `=', or we couldn't
+# complete the option string since there were more than one matching
+# what's on the line. So we just ad the option string as possible
+# matches, giving the string from the `=' on as a suffix.
+
+if [[ "$str" = *\=* ]]; then
+ str="=${str#*\=}"
+ PREFIX="${PREFIX%%\=*}"
+ suffix=()
+else
+ str=""
+ suffix=('-S=')
+fi
+
+anum=1
+for name in "$_lo_cache_names[@]"; do
+ action="$_lo_cache_actions[anum]"
+
+ if [[ "$name" = *_optarg_* ]]; then
+ compadd -M 'r:|-=* r:|=*' -Qq "$suffix[@]" -s "$str" - \
+ "${(@P)name}" && ret=0
+ elif [[ "$name" = *_arg_* ]]; then
+ compadd -M 'r:|-=* r:|=*' -Q "$suffix[@]" -s "$str" - \
+ "${(@P)name}" && ret=0
+ elif [[ -z "$str" ]]; then
+ compadd -M 'r:|-=* r:|=*' -Q - \
+ "${(@P)name}" && ret=0
+ fi
+ (( anum++ ))
+done
+
+return ret
diff --git a/Completion/Base/_match_pattern b/Completion/Base/_match_pattern
index 3df115d5b..91930a571 100644
--- a/Completion/Base/_match_pattern
+++ b/Completion/Base/_match_pattern
@@ -28,4 +28,10 @@
# like the `r:|[.-]=* r:|=*'. To make this work, the function `_match_test'
# would have to be changed to `(( compstate[matcher] <= 2 ))'
#
+# When automatic correction is used (see the file `_main_complete'), you
+# probably don't want to set matching flags here as that may make the
+# results slightly unpredictable. For this, change the line above to:
+#
+# [[ compstate[matcher] -lt 0 ]] && eval "${3}='(#l)'"
+#
# The default implementation of this function is empty.
diff --git a/Completion/Base/_subscript b/Completion/Base/_subscript
index d50fd8335..670739a9a 100644
--- a/Completion/Base/_subscript
+++ b/Completion/Base/_subscript
@@ -1,5 +1,10 @@
#defcomp -subscript-
-_compalso -math-
+local ret=1
+
+_compalso -math- && ret=0
+
[[ ${(Pt)${compstate[parameter]}} = assoc* ]] &&
- compgen -k "( ${(kP)${compstate[parameter]}} )"
+ compgen -k "( ${(kP)${compstate[parameter]}} )" && ret=0
+
+return ret
diff --git a/Completion/Builtins/.distfiles b/Completion/Builtins/.distfiles
new file mode 100644
index 000000000..97906e91f
--- /dev/null
+++ b/Completion/Builtins/.distfiles
@@ -0,0 +1,7 @@
+DISTFILES_SRC='
+ .distfiles
+ _aliases _arrays _autoload _bg_jobs _bindkey _builtin _cd _command
+ _dirs _disable _echotc _enable _fc _functions _hash _jobs _kill
+ _limits _sched _set _setopt _source _trap _unhash _unsetopt _vars_eq
+ _wait _which _zftp _zle _zmodload
+'
diff --git a/Completion/Builtins/_bindkey b/Completion/Builtins/_bindkey
index d3d019492..6fca200ba 100644
--- a/Completion/Builtins/_bindkey
+++ b/Completion/Builtins/_bindkey
@@ -1,7 +1,14 @@
#defcomp bindkey
-if [[ "$words[2]" = -*[DAN]* || "$words[CURRENT-1] = -*M ]]; then
+# Normally, this completes names of zle widgets, whether the builtin ones
+# or ones defined by the user. Note that a - allows a wildcard before it,
+# so h-b-s-b will complete to history-beginning-search-backward. You
+# can alter this by removing the -M ... from the second compgen.
+#
+# Where appropriate, will complete keymaps instead of widgets.
+
+if [[ "$words[2]" = -*[DAN]* || "$words[CURRENT-1]" = -*M ]]; then
compgen -s '$(bindkey -l)'
else
- compgen -b
+ compgen -b -M 'r:|-=* r:|=*'
fi
diff --git a/Completion/Builtins/_cd b/Completion/Builtins/_cd
index 65ce7f293..9a58effe0 100644
--- a/Completion/Builtins/_cd
+++ b/Completion/Builtins/_cd
@@ -24,7 +24,7 @@ if [[ -position 3 ]]; then
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
+ (( ! $#rep )) || compadd $rep
elif [[ $words[1] = pu* && $PREFIX = [-+]* ]]; then
# pushd: just complete the numbers, but show the full directory list with
# numbers.
@@ -34,7 +34,8 @@ elif [[ $words[1] = pu* && $PREFIX = [-+]* ]]; then
# lazy to type pushd.
IPREFIX=$PREFIX[1]
PREFIX=$PREFIX[2,-1]
- local list lines
+ 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
@@ -52,9 +53,11 @@ elif [[ $words[1] = pu* && $PREFIX = [-+]* ]]; then
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
+ compgen -y '$lines' -Q -k list && ret=0
+ [[ -z $compstate[list] ]] && compstate[list]=list && ret=0
+ [[ -n $compstate[insert] ]] && compstat[insert]=menu && ret=0
+
+ return ret
elif [[ $PREFIX != (\~|/|./|../)* && $#cdpath -ne 0 ]]; then
_path_files -W cdpath -/
else
diff --git a/Completion/Builtins/_disable b/Completion/Builtins/_disable
index e3edafe2b..27db5c18f 100644
--- a/Completion/Builtins/_disable
+++ b/Completion/Builtins/_disable
@@ -1,8 +1,10 @@
#defcomp disable
-local prev="$words[CURRENT-1]"
+local prev="$words[CURRENT-1]" ret=1
-[[ "$prev" = -*a* ]] && compgen -ea
-[[ "$prev" = -*f* ]] && compgen -eF
-[[ "$prev" = -*r* ]] && compgen -ew
-[[ "$prev" != -* ]] && compgen -eB
+[[ "$prev" = -*a* ]] && compgen -ea && ret=0
+[[ "$prev" = -*f* ]] && compgen -eF && ret=0
+[[ "$prev" = -*r* ]] && compgen -ew && ret=0
+[[ "$prev" != -* ]] && compgen -eB && ret=0
+
+return ret
diff --git a/Completion/Builtins/_enable b/Completion/Builtins/_enable
index 111d1ae26..1baa09ed6 100644
--- a/Completion/Builtins/_enable
+++ b/Completion/Builtins/_enable
@@ -1,8 +1,10 @@
#defcomp enable
-local prev="$words[CURRENT-1]"
+local prev="$words[CURRENT-1]" ret=1
-[[ "$prev" = -*a* ]] && compgen -da
-[[ "$prev" = -*f* ]] && compgen -dF
-[[ "$prev" = -*r* ]] && compgen -dw
-[[ "$prev" != -* ]] && compgen -dB
+[[ "$prev" = -*a* ]] && compgen -da && ret=0
+[[ "$prev" = -*f* ]] && compgen -dF && ret=0
+[[ "$prev" = -*r* ]] && compgen -dw && ret=0
+[[ "$prev" != -* ]] && compgen -dB && ret=0
+
+return ret
diff --git a/Completion/Builtins/_kill b/Completion/Builtins/_kill
index c1afa78cb..36a23ccb2 100644
--- a/Completion/Builtins/_kill
+++ b/Completion/Builtins/_kill
@@ -5,7 +5,11 @@ local list
if [[ -iprefix '-' ]]; then
compgen -k "($signals[1,-3])"
else
- compgen -P '%' -j
+ local ret=1
+
+ compgen -P '%' -j && ret=0
list=("$(ps 2>/dev/null)")
- compgen -y '$list' -s '`ps 2>/dev/null | tail +2 | cut -c1-5`'
+ compgen -y '$list' -s '`ps 2>/dev/null | tail +2 | cut -c1-5`' && ret=0
+
+ return ret
fi
diff --git a/Completion/Builtins/_setopt b/Completion/Builtins/_setopt
index 98800152f..b458cb2b0 100644
--- a/Completion/Builtins/_setopt
+++ b/Completion/Builtins/_setopt
@@ -1,8 +1,11 @@
#defcomp setopt
-local nm=$compstate[nmatches]
+local nm=$compstate[nmatches] ret=1
compgen -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' \
- -s '$({ unsetopt kshoptionprint; unsetopt } 2>/dev/null)'
+ -s '$({ unsetopt kshoptionprint; unsetopt } 2>/dev/null)' && ret=0
+
[[ compstate[nmatches] -eq nm ]] &&
- compgen -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' -o
+ compgen -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' -o && ret=0
+
+return ret
diff --git a/Completion/Builtins/_unhash b/Completion/Builtins/_unhash
index 63d61c991..a9050cb49 100644
--- a/Completion/Builtins/_unhash
+++ b/Completion/Builtins/_unhash
@@ -1,8 +1,10 @@
#defcomp unhash
-local fl="$words[2]"
+local fl="$words[2]" ret=1
-[[ "$fl" = -*d* ]] && compgen -n
-[[ "$fl" = -*a* ]] && compgen -a
-[[ "$fl" = -*f* ]] && compgen -F
-[[ "$fl" != -* ]] && compgen -m
+[[ "$fl" = -*d* ]] && compgen -n && ret=0
+[[ "$fl" = -*a* ]] && compgen -a && ret=0
+[[ "$fl" = -*f* ]] && compgen -F && ret=0
+[[ "$fl" != -* ]] && compgen -m && ret=0
+
+return ret
diff --git a/Completion/Builtins/_unsetopt b/Completion/Builtins/_unsetopt
index a5c85b1ef..1194e28a7 100644
--- a/Completion/Builtins/_unsetopt
+++ b/Completion/Builtins/_unsetopt
@@ -1,8 +1,11 @@
#defcomp unsetopt
-local nm=$compstate[nmatches]
+local nm=$compstate[nmatches] ret=1
compgen -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' \
- -s '$({ unsetopt kshoptionprint; setopt } 2>/dev/null)'
+ -s '$({ unsetopt kshoptionprint; setopt } 2>/dev/null)' && ret=0
+
[[ compstate[nmatches] -eq nm ]] &&
- compgen -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' -o
+ compgen -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' -o && ret=0
+
+return ret
diff --git a/Completion/Builtins/_wait b/Completion/Builtins/_wait
index 6e3a4c3c9..9281a5cc2 100644
--- a/Completion/Builtins/_wait
+++ b/Completion/Builtins/_wait
@@ -1,7 +1,9 @@
#defcomp wait
-local list
+local list ret=1
-compgen -P '%' -j
+compgen -P '%' -j && ret=0
list=("$(ps 2>/dev/null)")
-compgen -y '$list' -s '`ps 2>/dev/null | tail +2 | cut -c1-5`'
+compgen -y '$list' -s '`ps 2>/dev/null | tail +2 | cut -c1-5`' && ret=0
+
+return ret
diff --git a/Completion/Builtins/_zftp b/Completion/Builtins/_zftp
index e93021acf..178d9d9e3 100644
--- a/Completion/Builtins/_zftp
+++ b/Completion/Builtins/_zftp
@@ -45,6 +45,7 @@ case $subcom in
*)
# dunno... try ordinary completion after all.
- unset _compskip
+ unset _compskip
+ return 1
;;
esac
diff --git a/Completion/Commands/.distfiles b/Completion/Commands/.distfiles
new file mode 100644
index 000000000..f79d69704
--- /dev/null
+++ b/Completion/Commands/.distfiles
@@ -0,0 +1,3 @@
+DISTFILES_SRC='
+ .distfiles _correct_filename _most_recent_file
+'
diff --git a/Completion/Core/.distfiles b/Completion/Core/.distfiles
new file mode 100644
index 000000000..ddf2a707e
--- /dev/null
+++ b/Completion/Core/.distfiles
@@ -0,0 +1,5 @@
+DISTFILES_SRC='
+ .distfiles
+ _compalso _files _main_complete _multi_parts _normal _path_files
+ _sep_parts compdump compinit
+'
diff --git a/Completion/Core/_compalso b/Completion/Core/_compalso
index 6ff6cf0bf..52fb08f05 100644
--- a/Completion/Core/_compalso
+++ b/Completion/Core/_compalso
@@ -7,7 +7,9 @@
# `_compalso -math-' to get the completions that would be generated for a
# mathematical context.
-local tmp
+local tmp ret=1
tmp="$_comps[$1]"
-[[ -z "$tmp" ]] || "$tmp"
+[[ -z "$tmp" ]] || "$tmp" && ret=0
+
+return ret
diff --git a/Completion/Core/_files b/Completion/Core/_files
index 471824bfe..506ddbc8e 100644
--- a/Completion/Core/_files
+++ b/Completion/Core/_files
@@ -3,10 +3,9 @@
# Utility function for completing files of a given type or any file.
# In many cases you will want to call this one instead of _path_files().
-local nm=$compstate[nmatches] ret
+local nm=$compstate[nmatches] ret=1
-_path_files "$@"
-ret=$?
+_path_files "$@" && ret=0
if [[ $# -ne 0 && compstate[nmatches] -eq nm ]]; then
local opt opts
@@ -23,7 +22,7 @@ if [[ $# -ne 0 && compstate[nmatches] -eq nm ]]; then
[[ "$opt" = [PSWFJVX] ]] && opts=("$opts[@]" "-$opt" "$OPTARG")
done
- _path_files "$opts[@]"
-else
- return $ret
+ _path_files "$opts[@]" && ret=0
fi
+
+return ret
diff --git a/Completion/Core/_main_complete b/Completion/Core/_main_complete
index 34c5a3d3c..3571b712c 100644
--- a/Completion/Core/_main_complete
+++ b/Completion/Core/_main_complete
@@ -3,36 +3,47 @@
# The main loop of the completion code. This is what is called when
# completion is attempted from the command line.
#
-# This code will automatically try to correct the string on the
-# line based on the strings generated for the context if the
-# parameter `COMPCORRECT' is set and normal completion didn't yield
-# any matches. These corrected strings will be shown in a list and
-# one can cycle through them as in a menucompletion. To use this
-# feature, `COMPCORRECT' should be set to a number, specifying the
+# This code will automatically try to correct the string on the line
+# based on the strings generated for the context if
+# `compconfig[correct]' is set and normal completion didn't yield any
+# matches. These corrected strings will be shown in a list and one can
+#cycle through them as in a menucompletion. To use this feature,
+#`compconfig[correct]' should be set to a number, specifying the
# maximum number of errors that should be accepted. If the string also
# contains a `n' or `N', the code will use the numeric argument as the
# maximum number of errors if a numeric argument was given. If no
# numeric argument was given, the number from the value of
-# `COMPCORRECT' will be used. E.g. with `COMPCORRECT=2n' two errors
-# will be accepted, but if the user gives another number with the
-# numeric argument, this will be prefered. Also, with `COMPCORRECT=0n',
-# normally no automatic correction will be tried, but if a numeric
-# argument is given, automatic correction will be used. Once the
-# number of errors to accept is determined, the code will repeatedly
-# try to generate matches by allowing one error, two errors, and so
-# on.
-# If the parameter `CCORIG' is set (independent of the value), the
-# line will first be left unchanged and consecutive TABs cycle through
-# the list.
-# When using automatic correction, one can also set the parameter
-# `CCPROMPT' to a string that will be shown when multiple
-# correction results are displayed and the code starts cycling
-# through them (this string is used with the `-X' option and thus may
-# contain the control sequences `%n', `%B',...).
+# `compconfig[correct]' will be used. E.g. with `compconfig[correct]=2n'
+# two errors will be accepted, but if the user gives another number
+# with the numeric argument, this will be prefered. Also, with
+# `compconfig[correct]=0n',normally no automatic correction will be
+# tried, but if a numeric argument is given, automatic correction will
+# be used. Once the number of errors to accept is determined, the code
+# will repeatedly try to generate matches by allowing one error, two
+# errors, and so on. Independent of the number of errors the user
+# wants to accept, the code will allow only fewer errors than there
+# are characters in the string from the line.
+# The value of `compconfig[correct_orig]' is used to determine if the
+# original string should be included in the list (and thus be
+# presented to the user when cycling through the corrections). If it
+# is set to any non-empty value, the original string will be
+# offered. If it contains the sub-string `last', the original string
+# will apear as the last string when cycling through the corrections,
+# otherwise it will appear as the first one (so that the command line
+# does not change immediatly). Also, if the value of
+# `compconfig[correct_orig]' contains the sub-string `always', the
+# original string will always be included, whereas normally it is
+# included only if more than one possible correction was generated.
+# Finally, `compconfig[correct_prompt]' may be set to a string that
+# should be printed before the list of corrected strings when cycling
+# through them. This string may contain the control sequences `%n',
+# `%B', etc. known from the `-X' option of `compctl'. Also, the
+# sequence `%e' will be replaced by the number of errors accepted to
+# generate the corrected strings.
-local comp name _comp_correct comax
+local comp name _comp_correct _correct_prompt comax
-setopt localoptions nullglob rcexpandparam globdots
+setopt localoptions nullglob rcexpandparam
unsetopt markdirs globsubst shwordsplit nounset ksharrays
# Special completion contexts after `~' and `='.
@@ -91,25 +102,30 @@ while true; do
# Use automatic correction?
- if (( $+COMPCORRECT )); then
+ if (( $+compconfig[correct] )); then
# Do we have matches?
if (( compstate[nmatches] )); then
# Yes, were they added using correction? (More than one match?)
- if [[ -n "$_comp_correct" && compstate[nmatches] -gt 1 ]]; then
+ if [[ -n "$_comp_correct" &&
+ ( "$compconfig[correct_orig]" = *always* ||
+ compstate[nmatches] -gt 1 ) ]]; then
- # If we got more than one string from correction, we add the
- # original string as a possible match, let it not be shown in
- # the list, and probably display the `CCPROMPT'.
-
- (( $+CCORIG )) && builtin compadd -nQ - "$PREFIX$SUFFIX"
+ if [[ "$compconfig[correct_orig]" = *last* ]]; then
+ builtin compadd -V _correct_orig -nQ - "$PREFIX$SUFFIX"
+ elif [[ -n "$compconfig[correct_orig]" ]]; then
+ builtin compadd -nQ - "$PREFIX$SUFFIX"
+ fi
# If you always want to see the list of possible corrections,
# set `compstate[list]=list' here.
+
+ compstate[force_list]=list
fi
# Since we have matches, we don't want to try again.
+
break
fi
@@ -117,25 +133,33 @@ while true; do
if [[ -n "$_comp_correct" ]]; then
- # Yes, give up if we reached the maximum number of tries,
- # otherwise increment our counter.
+ # Yes, give up if we reached the maximum number of tries or the
+ # string from the line is too short, otherwise increment our
+ # counter.
- [[ _comp_correct -eq comax ]] && break
+ [[ _comp_correct -eq comax ||
+ "${#${:-$PREFIX$SUFFIX}}" -le _comp_correct+1 ]] && break
(( _comp_correct++ ))
+ _correct_prompt="${compconfig[correct_prompt]//\%e/$_comp_correct}"
+
elif [[ compstate[matcher] -eq compstate[total_matchers] ]]; then
+ # We don't try correction if the string is too short.
+
+ [[ "${#${:-$PREFIX$SUFFIX}}" -le 1 ]] && return
+
# No matches and no correction tried yet, but we just tried the
# last global match specification, so let's see if we should use
# correction now. First, get the maximum number of errors.
- if [[ "$COMPCORRECT" = *[nN]* && NUMERIC -ne 1 ]]; then
+ if [[ "$compconfig[correct]" = *[nN]* && NUMERIC -ne 1 ]]; then
# Prefer the numeric argument if that has a sensible value.
comax="$NUMERIC"
else
- comax="${COMPCORRECT//[^0-9]}"
+ comax="${compconfig[correct]//[^0-9]}"
fi
- # If the number of errors to accept is to small, give up.
+ # If the number of errors to accept is too small, give up.
[[ "$comax" -lt 1 ]] && break
@@ -145,25 +169,31 @@ while true; do
# ignored prefix).
compadd() {
+ [[ "$*" != *-([a-zA-Z/]#|)U* &&
+ "${#${:-$PREFIX$SUFFIX}}" -le _comp_correct ]] && return
+
if [[ "$PREFIX" = \~*/* ]]; then
PREFIX="${PREFIX%%/*}/(#a${_comp_correct})${PREFIX#*/}"
else
PREFIX="(#a${_comp_correct})$PREFIX"
fi
- if (( $+CCPROMPT )); then
- builtin compadd -X "$CCPROMPT" -J _correct "$@"
+ if [[ -n "$_correct_prompt" ]]; then
+ builtin compadd -X "$_correct_prompt" -J _correct "$@"
else
builtin compadd -J _correct "$@"
fi
}
compgen() {
+ [[ "$*" != *-([a-zA-Z/]#|)U* &&
+ "${#${:-$PREFIX$SUFFIX}}" -le _comp_correct ]] && return
+
if [[ "$PREFIX" = \~*/* ]]; then
PREFIX="${PREFIX%%/*}/(#a${_comp_correct})${PREFIX#*/}"
else
PREFIX="(#a${_comp_correct})$PREFIX"
fi
- if (( $+CCPROMPT )); then
- builtin compgen "$@" -X "$CCPROMPT" -J _correct
+ if [[ -n "$_correct_prompt" ]]; then
+ builtin compgen "$@" -X "$_correct_prompt" -J _correct
else
builtin compgen "$@" -J _correct
fi
@@ -179,6 +209,8 @@ while true; do
_comp_correct=1
compstate[matcher]=-1
+ _correct_prompt="${compconfig[correct_prompt]//\%e/$_comp_correct}"
+
# We also need to set `extendedglob' and to make the completion
# code behave as if globcomplete were set.
diff --git a/Completion/Core/_normal b/Completion/Core/_normal
index f56849194..98337eae5 100644
--- a/Completion/Core/_normal
+++ b/Completion/Core/_normal
@@ -1,6 +1,6 @@
#autoload
-local comp command cmd1 cmd2 pat val name i ret
+local comp command cmd1 cmd2 pat val name i ret=1
# Completing in command position? If not we set up `cmd1' and `cmd2' as
# two strings we have search in the completion definition arrays (e.g.
@@ -9,8 +9,9 @@ local comp command cmd1 cmd2 pat val name i ret
command="$words[1]"
if [[ CURRENT -eq 1 ]]; then
comp="$_comps[-command-]"
- [[ -z "$comp" ]] || "$comp"
- return
+ [[ -z "$comp" ]] || "$comp" && ret=0
+
+ return ret
elif [[ "$command[1]" == '=' ]]; then
eval cmd1\=$command
cmd2="$command[2,-1]"
@@ -28,11 +29,10 @@ for i in "$_patcomps[@]"; do
pat="${i% *}"
val="${i#* }"
if [[ "$cmd1" == $~pat || "$cmd2" == $~pat ]]; then
- "$val"
- ret=$?
+ "$val" && ret=0
if (( $+_compskip )); then
unset _compskip
- return $ret
+ return ret
fi
fi
done
@@ -53,4 +53,6 @@ if [[ -z "$comp" ]]; then
name=-default-
comp="$_comps[-default-]"
fi
-[[ -z "$comp" ]] || "$comp"
+[[ -z "$comp" ]] || "$comp" && ret=0
+
+return ret
diff --git a/Completion/Core/_path_files b/Completion/Core/_path_files
index 3c03c0c61..535ba537c 100644
--- a/Completion/Core/_path_files
+++ b/Completion/Core/_path_files
@@ -2,13 +2,15 @@
# Utility function for in-path completion.
# Supported arguments are: `-f', `-/', `-g <patterns>', `-J <group>',
-# `-V <group>', `-W paths', `-X explanation', and `-F <ignore>'. All but
-# the last have the same syntax and meaning as for `compgen'. The
-# `-F <ignore>' option may be used to give a list of suffixes either by
-# giving the name of an array or literally by giving them in a string
-# surrounded by parentheses. Files with one of the suffixes thus given
-# are treated like files with one of the suffixes in the `fignore' array
-# in normal completion.
+# `-V <group>', `-W paths', `-X explanation', `-P prefix', `-S suffix',
+# `-q', `-r remove-chars', `-R remove-func', and `-F <ignore>'. All but
+# the last have the same syntax and meaning as for `compgen' or
+# `compadd', respectively. The `-F <ignore>' option may be used to give
+# a list of suffixes either by giving the name of an array or
+# literally by giving them in a string surrounded by
+# parentheses. Files with one of the suffixes thus given are treated
+# like files with one of the suffixes in the `fignore' array in normal
+# completion.
#
# This function uses the helper functions `_match_test' and `_match_pattern'.
@@ -20,9 +22,10 @@ _match_test _path_files || return 1
local nm prepaths str linepath realpath donepath patstr prepath testpath rest
local tmp1 collect tmp2 suffixes i ignore matchflags opt group sopt pats gopt
-local addpfx addsfx expl orig ostr nm=$compstate[nmatches]
+local addpfx addsfx expl orig ostr nm=$compstate[nmatches] menu remsfx patlast
+local origflags mflags
-setopt localoptions nullglob rcexpandparam globdots extendedglob
+setopt localoptions nullglob rcexpandparam extendedglob
unsetopt markdirs globsubst shwordsplit nounset
prepaths=('')
@@ -33,16 +36,21 @@ gopt=''
pats=()
addpfx=()
addsfx=()
+remsfx=()
expl=()
# Get the options.
-while getopts "P:S:W:F:J:V:X:f/g:" opt; do
+while getopts "P:S:qr:R:W:F:J:V:X:f/g:" opt; do
case "$opt" in
P) addpfx=(-P "$OPTARG")
;;
S) addsfx=(-S "$OPTARG")
;;
+ q) tmp1=yes
+ ;;
+ [rR]) remsfx=("-$opt" "$OPTARG")
+ ;;
W) tmp1="$OPTARG"
if [[ "$tmp1[1]" = '(' ]]; then
prepaths=( ${^=tmp1[2,-2]}/ )
@@ -76,6 +84,8 @@ while getopts "P:S:W:F:J:V:X:f/g:" opt; do
esac
done
+[[ -n "$tmp1" && $#addsfx -ne 0 ]] && addsfx[1]=-qS
+
# If we were given no file selection option, we behave as if we were given
# a `-f'.
@@ -89,19 +99,25 @@ if [[ "$sopt" = - ]]; then
fi
# str holds the whole string from the command line with a `*' between
-# the prefix and the suffix.
+# the prefix and the suffix. Then we see if we will do menucompletion.
-if [[ -o globcomplete ]]; then
+if [[ $#compstate[pattern_match] -ne 0 ]]; then
str="${PREFIX}*${SUFFIX}"
else
str="${PREFIX:q}*${SUFFIX:q}"
+ [[ "$str" = \\\~* ]] && str="$str[2,-1]"
fi
orig="${PREFIX}${SUFFIX}"
+[[ $compstate[insert] = *menu || -n "$_comp_correct" ||
+ ( $#compstate[pattern_match] -ne 0 &&
+ "$orig" != "${orig:q}" ) ]] && menu=yes
+
+
# We will first try normal completion called with `compgen', but only if we
-# weren't given a `-F' option.
+# weren't given a `-F', `-r', or `-R' option.
-if (( ! $#ignore )); then
+if [[ $#ignore -eq 0 && $#remsfx -eq 0 && -z "$_comp_correct" ]]; then
# First build an array containing the `-W' option, if there is any and we
# want to use it. We don't want to use it if the string from the command line
# is a absolute path or relative to the current directory.
@@ -124,11 +140,11 @@ if (( ! $#ignore )); then
# If this generated any matches, we don't want to do in-path completion.
[[ compstate[nmatches] -eq nm ]] || return 0
+fi
- # No `-F' option, so we want to use `fignore'.
+# No `-F' option, so we want to use `fignore'.
- ignore=(-F fignore)
-fi
+(( $#ignore )) || ignore=(-F fignore)
# Now let's have a closer look at the string to complete.
@@ -176,6 +192,7 @@ fi
patstr="$str"
matchflags=""
_match_pattern _path_files patstr matchflags
+origflags="$matchflags"
[[ -n "$_comp_correct" ]] && matchflags="$matchflags(#a$_comp_correct)"
# We almost expect the pattern to have changed `..' into `*.*.', `/.' into
@@ -185,13 +202,40 @@ _match_pattern _path_files patstr matchflags
patstr="$patstr:gs-/-*/-:gs/*.*./../:gs-/*.-/.-:gs/**/*/:gs-.*/-./-"
+# We take the last pathname component from the pattern and store it in
+# `patlast', replacing `*'s in it with patterns that match any character
+# but not slashes. Later we will generate matches using `patstr' with the
+# patterns we were given (like `*.c') appended to it, producing all matching
+# files. These filenames are then compared to `patlast' and all names not
+# matching that will be removed. All this is needed to be able to correctly
+# support `completeinword' as otherwise we would have something like `a*x'
+# from the line (the `*' was inserted above) and appending the `-g' pattern
+# `*.tex' would yield `a*x*.tex' which is not what we want.
+
+if [[ "$patstr" = */* ]]; then
+ if [[ -n "$_comp_correct" && "${#orig##*/}" -le _comp_correct ]]; then
+ patlast="*/${origflags}${${patstr##*/}//\*/[^/]#}"
+ else
+ patlast="*/${matchflags}${${patstr##*/}//\*/[^/]#}"
+ fi
+ patstr="${patstr%/*}/"
+else
+ if [[ -n "$_comp_correct" && "$#orig" -le _comp_correct ]]; then
+ patlast="${origflags}${patstr//\*/[^/]#}"
+ else
+ patlast="${matchflags}${patstr//\*/[^/]#}"
+ fi
+ patstr=""
+fi
+
# First we skip over all pathname components in `str' which really exist in
# the file-system, so that `/usr/lib/l<TAB>' doesn't offer you `lib' and
# `lib5'. Pathname components skipped this way are taken from `orig' and added
# to `donepath'.
while [[ "$orig" = */* ]] do
- tmp1=( ${~matchflags}$realpath$donepath${orig%%/*}/${~patstr#*/}$^pats )
+ tmp1=( $realpath$donepath${orig%%/*}/${~matchflags}${~patstr#*/}$^pats )
+ tmp1=("${(@M)tmp1:#$~patlast}")
[[ $#tmp1 -gt 0 && -e "$realpath$donepath${orig%%/*}" ]] || break
donepath="$donepath${orig%%/*}/"
orig="${orig#*/}"
@@ -217,8 +261,13 @@ for prepath in "$prepaths[@]"; do
# we get the globbing matches for the pathname component currently
# handled.
+ if [[ -n "$_comp_correct" && "${#ostr%%/*}" -le _comp_correct ]]; then
+ mflags="$origflags"
+ else
+ mflags="$matchflags"
+ fi
rest="${str#*/}"
- tmp1="${prepath}${realpath}${testpath}${~matchflags}${str%%/*}(-/)"
+ tmp1="${prepath}${realpath}${testpath}${~mflags}${str%%/*}(-/)"
tmp1=( $~tmp1 )
if [[ $#tmp1 -eq 0 ]]; then
@@ -240,12 +289,19 @@ for prepath in "$prepaths[@]"; do
suffixes=( $rest$^pats )
suffixes=( "${(@)suffixes:gs.**.*.}" )
+ if [[ -n "$_comp_correct" && "${#ostr#*/}" -le _comp_correct ]]; then
+ mflags="$origflags"
+ else
+ mflags="$matchflags"
+ fi
+
# In the loop the prefixes from the `tmp1' array produced above and
# the suffixes we just built are used to produce possible matches
# via globbing.
for i in "$tmp1[@]" ; do
- tmp2=( ${~i}/${~matchflags}${~suffixes} )
+ tmp2=( ${~i}/${~mflags}${~suffixes} )
+ tmp2=("${(@M)tmp2:#$~patlast}")
[[ $#tmp2 -ne 0 ]] && collect=( $collect $i )
done
@@ -255,9 +311,6 @@ for prepath in "$prepaths[@]"; do
# next `-W' path.
if [[ $#collect -eq 0 ]]; then
- compadd -QU "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" \
- -i "$IPREFIX" -p "${linepath:q}${testpath:q}" -S "/${ostr#*/}" \
- -W "$tmp1" -f "$ignore[@]" - "${(@)tmp1:q}"
continue 2
elif [[ $#collect -ne 1 ]]; then
# If we have more than one possible match, this means that the
@@ -270,6 +323,7 @@ for prepath in "$prepaths[@]"; do
# Now produce all matching pathnames in `collect'.
collect=( ${~collect}/${~matchflags}${~suffixes} )
+ collect=("${(@M)collect:#$~patlast}")
# And then remove the common path prefix from all these matches.
@@ -282,15 +336,16 @@ for prepath in "$prepaths[@]"; do
# these are file names and that `fignore' should be used as usual
# (the `-f' and `-F' options).
- if [[ $compstate[insert] = *menu ]]; then
+ if [[ -n "$menu" ]]; then
compadd -QU "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" \
- -i "$IPREFIX" -p "${linepath:q}${testpath:q}" -S "/${ostr#*/}" \
+ -i "$IPREFIX" -p "$linepath${testpath:q}" \
+ -s "/${ostr#*/}" \
-W "$tmp1" -f "$ignore[@]" - "${(@)${(@)collect%%/*}:q}"
else
for i in $collect; do
- compadd -U "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" \
- -i "$IPREFIX" -p "$linepath$testpath" -s "/${i#*/}" \
- -M 'r:|/=*' -W "$tmp1" -f "$ignore[@]" - "${i%%/*}"
+ compadd -QU "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" \
+ -i "$IPREFIX" -p "$linepath${testpath:q}" -s "/${${i#*/}:q}" \
+ -M 'r:|/=*' -W "$tmp1" -f "$ignore[@]" - "${${i%%/*}:q}"
done
fi
@@ -299,12 +354,27 @@ for prepath in "$prepaths[@]"; do
continue 2
fi
+
# We reach this point if only one of the path prefixes in `tmp1'
# has a existing path-suffix matching the string from the line.
# In this case we accept this match and continue with the next
# path-name component.
tmp1=( "$collect[1]" )
+ elif [[ -n "$_comp_correct" && "$mflags" = "$matchflags" ]]; then
+
+ # If we got only one match with auto-correction and if we get none
+ # without correction, stop now.
+
+ tmp2="${prepath}${realpath}${testpath}${~origflags}${str%%/*}(-/)"
+ tmp2=( $~tmp2 )
+
+ if [[ $#tmp1 -ne $#tmp2 ]]; then
+ compadd -QU "$addpfx[@]" -S '' "$group[@]" "$expl[@]" \
+ -i "$IPREFIX" -p "$linepath${testpath:q}" -s "/${ostr#*/}" \
+ - "${${tmp1#${prepath}${realpath}${testpath}}:q}"
+ continue 2
+ fi
fi
# This is also reached if the first globbing produced only one match
# in this case we just continue with the next pathname component, too.
@@ -320,10 +390,17 @@ for prepath in "$prepaths[@]"; do
# no path suffix, the `-W' we are currently handling, all the matches we
# can produce in this directory, if any.
+ if [[ -n "$_comp_correct" && "${#ostr#*/}" -le _comp_correct ]]; then
+ mflags="$origflags"
+ else
+ mflags="$matchflags"
+ fi
tmp1="$prepath$realpath$testpath"
suffixes=( $str$^pats )
suffixes=( "${(@)suffixes:gs.**.*.}" )
tmp2=( ${~tmp1}${~matchflags}${~suffixes} )
+ tmp2=("${(@M)tmp2:#$~patlast}")
+
if [[ $#tmp2 -eq 0 ]]; then
# No match, insert the expanded path and add the original tail.
@@ -334,11 +411,11 @@ for prepath in "$prepaths[@]"; do
[[ "$linepath$testpath$ostr" = "$PREFIX$SUFFIX" ]] && return 1
compadd -QU -S '' "$group[@]" "$expl[@]" \
- -i "$IPREFIX" -f - "${linepath:q}${testpath:q}$ostr"
+ -i "$IPREFIX" -f - "$linepath${testpath:q}$ostr"
else
- compadd -U "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" \
- -i "$IPREFIX" -p "$linepath$testpath" -f "$ignore[@]" \
- -W "$prepath$realpath$testpath" - "${(@)tmp2#$tmp1}"
+ compadd -QU "$addpfx[@]" "$addsfx[@]" "$remsfx[@]" "$group[@]" "$expl[@]" \
+ -i "$IPREFIX" -p "$linepath${testpath:q}" -f "$ignore[@]" \
+ -W "$prepath$realpath$testpath" - "${(@)${(@)tmp2#$tmp1}:q}"
fi
done
diff --git a/Completion/Core/_sep_parts b/Completion/Core/_sep_parts
new file mode 100644
index 000000000..c1cda2b9a
--- /dev/null
+++ b/Completion/Core/_sep_parts
@@ -0,0 +1,171 @@
+#autoload
+
+# This function can be used to separately complete parts of strings
+# where each part may be one of a set of matches and different parts
+# have different sets.
+# Arguments are alternatingly arrays and separator strings. Arrays may
+# be given by name or literally as words separated by white space in
+# parentheses, e.g.:
+#
+# _sep_parts '(foo bar)' @ hosts
+#
+# This will make this function complete the strings in the array
+# `friends'. If the string on the line contains a `@', the substring
+# after it will be completed from the array `hosts'. Of course more
+# arrays may be given, each preceded by another separator string.
+#
+# This function understands the `-J group', `-V group', and
+# `-X explanation' options.
+#
+# This function does part of the matching itself and calls the functions
+# `_match_test' and `_match_pattern' for this.
+
+local str arr sep test testarr tmparr prefix suffixes matchers autosuffix
+local matchflags opt group expl nm=$compstate[nmatches]
+
+# Test if we should use this function for the global matcher in use.
+
+_match_test _sep_parts || return 1
+
+# Get the options.
+
+group=()
+expl=()
+while getopts "J:V:X:" opt; do
+ case "$opt" in
+ [JV]) group=("-$opt" "$OPTARG");;
+ X) expl=(-X "$OPTARG");;
+ esac
+done
+shift OPTIND-1
+
+# Get the string from the line.
+
+str="$PREFIX$SUFFIX"
+[[ $#compstate[pattern_match] -ne 0 ]] || str="$str:q"
+prefix=""
+
+# Walk through the arguments to find the longest unambiguous prefix.
+
+while [[ $# -gt 1 ]]; do
+ # Get the next array and separator.
+ arr="$1"
+ sep="$2"
+
+ if [[ "$arr[1]" == '(' ]]; then
+ tmparr=( ${=arr[2,-2]} )
+ arr=tmparr
+ fi
+ # Is the separator on the line?
+ [[ "$str" != *${sep}* ]] && break
+
+ # Build a pattern matching the possible matches and get all these
+ # matches in an array.
+
+ test="${str%%${sep}*}"
+ [[ -n "$_comp_correct" && $#test -le _comp_correct ]] && return 1
+
+ matchflags=""
+ _match_pattern _sep_parts test matchflags
+ [[ -n "$_comp_correct" ]] && matchflags="$matchflags(#a$_comp_correct)"
+
+ test="${matchflags}${test}"
+ testarr=( "${(@M)${(@P)arr}:#${~test}*}" )
+ testarr=( "${(@)testarr:#}" )
+
+ # If there are no matches we give up. If there is more than one
+ # match, this is the part we will complete.
+ (( $#testarr )) || return 1
+ [[ $#testarr -gt 1 ]] && break
+
+ # Only one match, add it to the prefix and skip over it in `str',
+ # continuing with the next array and separator.
+ prefix="${prefix}${testarr[1]}${sep}"
+ str="${str#*${sep}}"
+ shift 2
+done
+
+# Get the array to work upon.
+arr="$1"
+if [[ "$arr[1]" == '(' ]]; then
+ tmparr=( ${=arr[2,-2]} )
+ arr=tmparr
+fi
+if [[ $# -le 1 || "$str" != *${2}* ]]; then
+ # No more separators, build the matches.
+
+ test="$str"
+ [[ -n "$_comp_correct" && $#test -le _comp_correct ]] && return 1
+
+ matchflags=""
+ _match_pattern _sep_parts test matchflags
+ [[ -n "$_comp_correct" ]] && matchflags="$matchflags(#a$_comp_correct)"
+
+ test="${matchflags}${test}"
+ testarr=( "${(@M)${(@P)arr}:#${~test}*}" )
+ testarr=( "${(@)testarr:#}" )
+fi
+
+[[ $#testarr -eq 0 || ${#testarr[1]} -eq 0 ]] && return 1
+
+# Now we build the suffixes to give to the completion code.
+shift
+matchers=()
+suffixes=("")
+autosuffix=()
+
+while [[ $# -gt 0 && "$str" == *${1}* ]]; do
+ # Remove anything up to the the suffix.
+ str="${str#*${1}}"
+
+ # Again, we get the string from the line up to the next separator
+ # and build a pattern from it.
+ if [[ $# -gt 2 ]]; then
+ test="${str%%${3}*}"
+ else
+ test="$str"
+ fi
+
+ [[ -n "$_comp_correct" && $#test -le _comp_correct ]] && return 1
+
+ matchflags=""
+ _match_pattern _sep_parts test matchflags
+ [[ -n "$_comp_correct" ]] && matchflags="$matchflags(#a$_comp_correct)"
+ test="${matchflags}${test}"
+
+ # We incrementally add suffixes by appending to them the seperators
+ # and the strings from the next array that match the pattern we built.
+
+ arr="$2"
+ if [[ "$arr[1]" == '(' ]]; then
+ tmparr=( ${=arr[2,-2]} )
+ arr=tmparr
+ fi
+ tmparr=( "${(@M)${(@P)arr}:#${~test}*}" )
+ tmparr=( "${(@)tmparr:#}" )
+ suffixes=("${(@)^suffixes[@]}${1}${(@)^tmparr}")
+
+ # We want the completion code to generate the most specific suffix
+ # for us, so we collect matching specifications that allow partial
+ # word matching before the separators on the fly.
+ matchers=("$matchers[@]" "r:|${1:q}=*")
+ shift 2
+done
+
+# If we were given at least one more separator we make the completion
+# code offer it by appending it as a autoremovable suffix.
+(( $# )) && autosuffix=(-qS "$1")
+
+# If we have collected matching specifications, we build an array
+# from it that can be used as arguments to `compadd'.
+[[ $#matchers -gt 0 ]] && matchers=(-M "$matchers")
+
+# Add the matches for each of the suffixes.
+for i in "$suffixes[@]"; do
+ compadd -U "$group[@]" "$expl[@]" "$matchers[@]" "$autosuffix[@]" \
+ -i "$IPREFIX" -p "$prefix" -s "$i" - "$testarr[@]"
+done
+
+# This sets the return value to indicate that we added matches (or not).
+
+[[ nm -ne compstate[nmatches] ]]
diff --git a/Completion/Core/compdump b/Completion/Core/compdump
index 8be096f50..5fdee8c7a 100644
--- a/Completion/Core/compdump
+++ b/Completion/Core/compdump
@@ -15,7 +15,7 @@
# Print the number of files used for completion. This is used in compinit
# to see if auto-dump should re-dump the dump-file.
-_d_file=${COMPDUMP-${0:h}/compinit.dump}
+_d_file=${compconfig[dump_file]-${0:h}/compinit.dump}
typeset -U _d_files
_d_files=( ${^~fpath}/_*~*~(N:t) )
diff --git a/Completion/Core/compinit b/Completion/Core/compinit
index ec5867838..31d011565 100644
--- a/Completion/Core/compinit
+++ b/Completion/Core/compinit
@@ -37,18 +37,24 @@
# Note that no white space is allowed between the `#' and the rest of
# the string.
#
-# See the file `compdump' for how to speed up initialiation.
+# Functions that are used to generate matches should return zero if they
+# were able to add matches and non-zero otherwise.
+#
+# See the file `compdump' for how to speed up initialisation.
#
# If you are using global matching specifications with `compctl -M ...'
# have a look at the files `_match_test' and `_match_pattern'. To make
# all the example functions use matching as specified with `-M' these
# need some editing.
-#
+
# If we got the `-d'-flag, we will automatically dump the new state (at
# the end).
+# If we were given an argument, this will be taken as the name of the
+# file in which to store the dump.
if [[ "$1" = -d ]]; then
_i_autodump=1
+ shift
else
_i_autodump=0
fi
@@ -59,6 +65,18 @@ fi
typeset -A _comps
_patcomps=()
+# This is the associative array used for configuration.
+
+typeset -A compconfig
+
+# Standard initialisation for `compconfig'.
+
+(( $# )) && compconfig[dump_file]="$1"
+[[ -z "$compconfig[dump_file]" ]] && compconfig[dump_file]="$0.dump"
+
+compconfig[correct_prompt]='correct to:'
+
+
# This function is used to register or delete completion functions. For
# registering completion functions, it is invoked with the name of the
# function as it's first argument (after the options). The other
@@ -197,11 +215,25 @@ compdef() {
fi
}
-# Now we automatically make the definition files autoloaded.
+# Functional interface to configuration. This takes its arguments
+# and sets the according values in `compconfig'.
+# Arguments may be `foo=bar' to set key `foo' to `bar' or `baz' to
+# set key `baz' to the empty string.
-# First we get the name of a dump file if this will be used.
+compconf() {
+ local i name
-: ${COMPDUMP:=$0.dump}
+ for i; do
+ if [[ "$i" = *\=* ]]; then
+ name="${i%%\=*}"
+ compconfig[$name]="${i#*\=}"
+ else
+ compconfig[$i]=''
+ fi
+ done
+}
+
+# Now we automatically make the definition files autoloaded.
if [[ ! -o extendedglob ]]; then
_i_noextglob=yes
@@ -215,10 +247,10 @@ _i_done=''
# If we have a dump file, load it.
-if [[ -f "$COMPDUMP" ]]; then
- read -rA _i_line < "$COMPDUMP"
+if [[ -f "$compconfig[dump_file]" ]]; then
+ read -rA _i_line < "$compconfig[dump_file]"
if [[ _i_autodump -eq 1 && $_i_line[2] -eq $#_i_files ]]; then
- builtin . "$COMPDUMP"
+ builtin . "$compconfig[dump_file]"
_i_done=yes
fi
unset _i_line
diff --git a/Completion/README b/Completion/README
index 43ffcbcc2..f95e67699 100644
--- a/Completion/README
+++ b/Completion/README
@@ -9,9 +9,12 @@ Core/compinit. It is recommnded that you use the -d option, which outputs
a file containing the necessary variables, bindkeys etc., making later
loading much faster. For example,
[[ -f ~/completion/compinit ]] && . ~/completion/compinit -d
+The name of the file to use may be given as an extra argument.
+
This will rebind any keys which do completion to use the new system.
For more detailed instructions, including how to add new completions, see
-the top of Core/compinit.
+the top of Core/compinit. For information about how to configure the code,
+see the comment at the top of Core/_main_complete.
The subdirectories contain:
@@ -27,7 +30,7 @@ Core:
This dumps the completions status for faster initialisation. The
easiest way of doing this is to use the -d option to compinit rather
than calling compdump directly.
- _comp_parts
+ _sep_parts
Utility used for completing words with multiple separate parts, such as
`<user>@<host>'
_multi_parts
@@ -67,10 +70,15 @@ Base:
This handles completion of command arguments when no special function
exists. Usually this means completing files, but you can modify this
as you wish.
+ _long_options
+ This handles options beginning with `--', as in many GNU commands.
+ The command must accept the --help option to list the possible options.
+ __long_options can also take arguments to help it decide what to
+ complete as the value of the option.
_match_pattern
_match_test
These are used by Base/_path_files (and hence also Base/_files)
- and Base/_comp_parts for file completion with control over
+ and Base/_sep_parts for file completion with control over
matching (whether to complete case-insensitively, or to allow
insertion before `.', etc.) See _match_test for instructions.
Note _path_files expects these files to be present.
diff --git a/Completion/User/.distfiles b/Completion/User/.distfiles
new file mode 100644
index 000000000..ee0017035
--- /dev/null
+++ b/Completion/User/.distfiles
@@ -0,0 +1,6 @@
+DISTFILES_SRC='
+ .distfiles
+ _a2ps _compress _configure _dd _dvi _find _gunzip _gzip _hosts
+ _make _man _mh _pdf _ps _rcs _rlogin _strip _stty _tar _tar_archive
+ _tex _uncompress _x_options _xfig
+'
diff --git a/Completion/User/_a2ps b/Completion/User/_a2ps
index 600b58872..9923ae20d 100644
--- a/Completion/User/_a2ps
+++ b/Completion/User/_a2ps
@@ -1,22 +1,39 @@
#defcomp a2ps
-if [[ "$PREFIX[1,2]" = -- ]]; then
- _comp_parts '(--borders --compact --truncate-lines --interpret
- --print-anyway --delegate)' '=' '(yes no)'
- _comp_parts '(--major)' '=' '(rows columns)'
- _comp_parts '(--end-of-line)' '=' '(r n nr rn any)'
+# This is for the GNU version of a2ps.
- compgen -S= -k '(--medium --columns --rows --line-numbers
- --font-size --lines-per-page --chars-per-line
- --tabsize --non-printable-format --encoding
- --title --stdin --prologue --highlight-level
- --strip-level --output --version-control --suffix
- --printer --copies --sides --page-prefeed
- --no-page-prefeed)'
- compgen -qS= -k '(--margin --header --underlay --left-title
- --right-title --left-footer --footer --right-footer
- --pages --pretty-print)'
- compgen -k '(--landscape --portrait --catman --no-header)'
-else
- _files -F fignore -g "*~*.ps"
+if [[ "$words[1]" != "$_a2ps_cache_cmd" ]]; then
+ local descr
+
+ _a2ps_cache_cmd="$words[1]"
+
+ descr=( "${(@)${(f@)$($words[1] --list=features)//
+ / }:#}" )
+
+ _a2ps_cache_values=(
+ "${descr[(r)Known style sheets*]#* }"
+ "${descr[(r)Known encodings*]#* }"
+ "${descr[(r)Known media*]#* }"
+ "${descr[(r)Known prologues*]#* }"
+ "${descr[(r)Known PostScript Printer Descriptions*]#* }"
+ "${descr[(r)Known output destination*]#* }"
+ "${descr[(r)Known user options*]#* }"
+ "${descr[(r)Known Variables*]#* }"
+ )
fi
+
+_long_options -t '*\*' '(yes no)' \
+ '*=DIRECTION' '(rows columns)' \
+ '*=TYPE' '(r n nr rn any)' \
+ '--highlight-level*' '(none normal heavy)' \
+ '--version-control*' '(none off t numbered nil
+ existing never simple)' \
+ '--pretty-print*' "[${_a2ps_cache_values[1]}]" \
+ '--encoding*' "(${_a2ps_cache_values[2]})" \
+ '--medium*' "[${_a2ps_cache_values[3]}]" \
+ '--prologue*' "[${_a2ps_cache_values[4]}]" \
+ '--ppd*' "[${_a2ps_cache_values[5]}]" \
+ '--printer*' "[${_a2ps_cache_values[6]}]" \
+ '--user-option*' "[${_a2ps_cache_values[7]}]" \
+ '--variable*' "[${_a2ps_cache_values[8]}]" ||
+ _files -F fignore -g '*~*.(ps|PS|eps|EPS)'
diff --git a/Completion/User/_configure b/Completion/User/_configure
index 050701fac..7559852e8 100644
--- a/Completion/User/_configure
+++ b/Completion/User/_configure
@@ -1,35 +1,4 @@
#defcomp configure
-setopt localoptions extendedglob
-
-if [[ $PREFIX = *=* ]]; then
- # Complete filenames after e.g. --prefix=
- IPREFIX=${PREFIX%%=*}=
- PREFIX=${PREFIX#*=}
- compgen -f
-else
- # Generate a list of options from configure --help
- local -a pars
- local i
- pars=($($words[1] --help | awk '$1 ~ /--[a-z]*.*/ {print $1}'))
- for i in $pars
- do
- case $i in
- (--(((en|dis)able-FEATURE)|(with(out|)-PACKAGE))*)
- : Skip standard help output
- ;;
- --enable)
- : Skip standard help output
- ;;
- --*\[=* )
- compadd -M 'r:|-=* r:|=*' -q -S = -- ${i%%\[=*}
- ;;
- --*=* )
- compadd -M 'r:|-=* r:|=*' -S = -- ${i%%=*}
- ;;
- * )
- compadd -M 'r:|-=* r:|=*' -- $i
- ;;
- esac
- done
-fi
+_long_options '*=(E|)PREFIX*' '_files -/' \
+ '*=PROGRAM*' '_command_names'
diff --git a/Completion/User/_dd b/Completion/User/_dd
index 86a47b1ab..63ae40f50 100644
--- a/Completion/User/_dd
+++ b/Completion/User/_dd
@@ -5,7 +5,7 @@ if [[ -iprefix conv= ]]; then
# test alone will have that effect.
[[ -string , ]]
compgen -S, -q \
- -k '(ascii ebcdic ibm block unblock lcase ucase swab noerror sync)'
+ -k '(ascii ebcdic ibm block unblock lcase ucase swab noerror sync)'
elif [[ -iprefix 'if=' || -iprefix 'of=' ]]; then
_files
else
diff --git a/Completion/User/_find b/Completion/User/_find
index 8fcdafb83..cb637fc26 100644
--- a/Completion/User/_find
+++ b/Completion/User/_find
@@ -10,8 +10,12 @@ elif [[ -iprefix - ]]; then
{i,}{l,}name {no,}{user,group} path perm regex size true uid used \
exec {f,}print{f,0,} ok prune ls'
elif [[ -position 2 ]]; then
- compgen -g '. ..'
- _files -g '(-/)'
+ local ret=1
+
+ compgen -g '. ..' && ret=0
+ _files -g '(-/)' && ret=0
+
+ return ret
elif [[ "$prev" = -((a|c|)newer|fprint(|0|f)) ]]; then
_files
elif [[ "$prev" = -fstype ]]; then
diff --git a/Completion/User/_man b/Completion/User/_man
index 67d59f24a..cd1badc4d 100644
--- a/Completion/User/_man
+++ b/Completion/User/_man
@@ -1,7 +1,9 @@
#defcomp man
+
setopt localoptions rcexpandparam
local rep
+
if [[ $words[2] = (<->*|ln) ]]; then
rep=( $manpath/(man|cat)${words[2]}/$PREFIX*$SUFFIX.<->*(N:t:r) )
else
diff --git a/Completion/User/_mh b/Completion/User/_mh
index 7e8575123..c6c018220 100644
--- a/Completion/User/_mh
+++ b/Completion/User/_mh
@@ -4,6 +4,7 @@
# Alter the following two to your own mh directory and the directory
# where standard mh library files live. (It works anyway, but this
# will save a little time.)
+
local mymhdir=~/Mail
local mhlib=/usr/lib/mh
@@ -25,6 +26,7 @@ if [[ -iprefix - ]]; then
elif [[ -iprefix '+' || -iprefix '@' || "$prev" = -draftfolder ]]; then
# Complete folder names.
local mhpath
+
if [[ $IPREFIX != '@' ]]; then
[[ $IPREFIX = '+' ]] || IPREFIX=+
mhpath=$mymhdir
@@ -53,7 +55,8 @@ elif [[ "$prev" = -[rw]cache ]]; then
compadd public private never ask
else
# Generate sequences.
- local foldnam folddir f
+ local foldnam folddir f ret
+
for f in $argv; do
[[ $f = [@+]* ]] && foldnam=$f
done
@@ -66,7 +69,10 @@ else
# leaving foldnam empty works here
fi
- compgen -s '$(mark $foldnam 2>/dev/null | awk -F: '\''{ print $1 }'\'')'
- compadd reply next cur prev first last all unseen
- compgen -W folddir -g '<->'
+ compgen -s '$(mark $foldnam 2>/dev/null | awk -F: '\''{ print $1 }'\'')' &&
+ ret=0
+ compadd reply next cur prev first last all unseen && ret=0
+ compgen -W folddir -g '<->' && ret=0
+
+ return ret
fi
diff --git a/Completion/User/_rcs b/Completion/User/_rcs
index 5a751605c..5e53cb4da 100644
--- a/Completion/User/_rcs
+++ b/Completion/User/_rcs
@@ -6,6 +6,7 @@ local nm=$compstate[nmatches]
if [[ $compstate[nmatches] -eq nm && -d RCS && $words[1] != ci ]]; then
local rep
+
rep=(RCS/$PREFIX*$SUFFIX,v(:t:s/\,v//))
(( $#rep )) && compadd - $rep
fi
diff --git a/Completion/User/_strip b/Completion/User/_strip
index 6962ac455..541c901ad 100644
--- a/Completion/User/_strip
+++ b/Completion/User/_strip
@@ -1,2 +1,3 @@
#defcomp strip
+
_files -g '*(*)'
diff --git a/Completion/User/_tar b/Completion/User/_tar
index 84c490f1e..d11ee76c8 100644
--- a/Completion/User/_tar
+++ b/Completion/User/_tar
@@ -1,69 +1,125 @@
#defcomp tar
# Tar completion. Features:
-# - Assumes tar commands are in second position, tar archive is in third
-# e.g. tar xvzf zsh-3.0.5.tar.gz ...
-# Could search better. Send me the patch.
+# - Tries to collect tar commands from second position, single letter
+# option, and long options.
# - `tar' can be called anything, will use the correct name
-# - Preferentially completes *.tar and *.TAR files in third position
-# - unless z or Z appears in the commands, in which case prefer *.tar.gz
-# and similar (GNU tar).
-# - From fourth position on, if command is x or t, completes files inside
-# archive. This is supposed to look pretty much as if the files are
-# in an ordinary directory hierarchy. Handles extraction from compressed
-# archives (GNU tar).
+# - Uses the function `_tar_archive' to complete archive files.
+# - Tries to find out if compressed archives should be used.
+# - Completes files inside archive. This is supposed to look pretty
+# much as if the files are in an ordinary directory hierarchy.
+# Handles extraction from compressed archives (GNU tar).
# - Anywhere -- appears, gets a list of long options to complete from
-# tar itself (GNU tar); this needs perl. If you have GNU tar but not
-# perl: your system manager is weird.
+# tar itself (GNU tar)
# - Things like --directory=... are also completed correctly.
emulate -LR zsh
setopt extendedglob
-local nm=$NMATCHES tcmd="$words[2]" tf="$words[3]"
+local _tar_cmd tf tmp del
-if [[ $PREFIX = *=* ]]; then
- # For GNU tar arguments like --directory=
- IPREFIX=${PREFIX%%\=*}=
- PREFIX=${PREFIX#*=}
- if [[ $IPREFIX = --directory* ]]; then
- _path_files -/
- else
- _files
+# First we collect in `_tar_cmd' single letter options describing what
+# should be done with the archive and if it is compressed. This
+# collected from options arguments that start with only one hyphen,
+# from some of the possible long options, and from the second word if
+# that does not start with a hyphen.
+
+tmp=("${(@M)words:#-[^-]*}")
+_tar_cmd="${(j::)tmp#-}"
+
+(( $words[(I)--(un|)gzip] )) && _tar_cmd="z$_tar_cmd"
+(( $words[(I)--(un|)compress] )) && _tar_cmd="Z$_tar_cmd"
+(( $words[(I)--list] )) && _tar_cmd="t$_tar_cmd"
+(( $words[(I)--(extract|get)] )) && _tar_cmd="x$_tar_cmd"
+(( $words[(I)--create] )) && _tar_cmd="c$_tar_cmd"
+
+# Other ways of finding out what we're doing: first
+# look in the first argument if it's not an option
+if [[ "$words[2]" = *[txcdruA]*~-* ]]; then
+ _tar_cmd="$words[2]$_tar_cmd"
+elif [[ $_tar_cmd != *[txcdruA]* && CURRENT -gt 2 ]]; then
+ # look for more obscure long options: these aren't all handled.
+ (( $words[(I)--(diff|compare)] )) && _tar_cmd="d$_tar_cmd"
+ (( $words[(I)--append] )) && _tar_cmd="r$_tar_cmd"
+ (( $words[(I)--update] )) && _tar_cmd="u$_tar_cmd"
+ (( $words[(I)--(con|)catenate] )) && _tar_cmd="A$_tar_cmd"
+ (( $words[(I)--delete] )) && del=1
+fi
+
+# Next, we try to find the archive name and store it in `tf'. The name
+# is searched after a `--file=' long option, in the third word if the
+# second one didn't start with a hyphen but contained a `f', and after
+# an option argument starting with only one hyphen and containing a `f'.
+
+tmp="$words[(I)--file=*]"
+if (( tmp )); then
+ tf="${words[tmp][8,-1]}"
+ _tar_cmd="f$_tar_cmd"
+elif [[ "$words[2]" != -* && "$words[2]" = *f* ]]; then
+ tf="$words[3]"
+ _tar_cmd="f$_tar_cmd"
+else
+ tmp="${words[(I)-*f*~--*]}"
+ if (( tmp )); then
+ tf="$words[tmp+1]"
+ _tar_cmd="f$_tar_cmd"
fi
-elif [[ $PREFIX = --* ]]; then
- # gnu tar, generate completions from --help
- # ones followed by = get that as a suffix
- local -a ownlist eqlist
- local comp
- $words[1] --help |
- perl -ne 'while (/--[^[\s,='\'']+=?/g) { print "$&\n"; }' |
- while read comp; do
- if [[ $comp = *= ]]; then
- eqlist[$#eqlist+1]=${comp%=}
- else
- ownlist[$#ownlist+1]=$comp
- fi
- done
- compgen -S '=' -k eqlist
- compgen -k ownlist
-elif [[ "$tcmd" = *[tx]*f* && $CURRENT -ge 4 ]] then
- # Listing or extracting a particular file. We run `tar t...'
- # on the file, keeping the list of filenames cached, plus the
- # name of the tarfile so we know if it changes.
+fi
+
+# Now we complete...
+
+if [[ "$PREFIX" = --* ]]; then
+
+ # ...long options after `--'.
+
+ _long_options '--owner*' "_tilde" \
+ '*=(PROG|COMMAND)*' "_command_names" \
+ '*=ARCHIVE*' "_tar_archive" \
+ '*=CONTROL*' "[t numbered nil existing never simple]"
+
+elif [[ ( CURRENT -gt 2 && "$words[CURRENT-1]" = -*f* &&
+ "$words[CURRENT-1]" != --* ) ||
+ ( CURRENT -eq 3 && "$words[2]" = *f* && "$words[2]" != -* ) ]]; then
+
+ # ...archive files if we think they are wanted here.
+
+ _tar_archive
+
+elif [[ ( "$_tar_cmd" = *[xt]* || -n $del ) && -n "$tf" ]]; then
+
+ # ...and files from the archive if we found an archive name and tar
+ # commands. We run `tar t...' on the file, keeping the list of
+ # filenames cached, plus the name of the tarfile so we know if it
+ # changes.
+
local largs=-tf
- [[ $words[2] = *z* ]] && largs=-tzf
- [[ $words[2] = *Z* ]] && largs=-tZf
- if [[ $tf != $tar_cache_name ]]; then
- tar_cache_list=("${(@f)$($words[1] $largs $tf)}")
- tar_cache_name=$tf
+ if [[ $_tar_cmd = *z* ]]; then
+ largs=-tzf
+ elif [[ $_tar_cmd = *Z* ]]; then
+ largs=-tZf
+ else
+ # Some random compression program e.g. bzip2
+ tmp="${words[(r)--use-comp*]}"
+ [[ -n $tmp ]] && largs=($tmp -tf)
+ fi
+
+ if [[ $tf != $_tar_cache_name ]]; then
+ _tar_cache_list=("${(@f)$($words[1] $largs $tf)}")
+ _tar_cache_name=$tf
+ fi
+
+ _multi_parts / _tar_cache_list
+else
+
+ # See if we should use a path prefix. We have to use eval as the dir can
+ # be any unevaluated thing which appears on the command line, including a
+ # parameter.
+ tmp=${words[(r)--dir[a-z]#=*]}
+ if [[ -n $tmp ]]; then
+ eval "tmp=(${tmp#*=})"
+ _path_files -W tmp
+ else
+ _files
fi
- _multi_parts / tar_cache_list
-elif [[ "$tcmd" = *c*f* && $CURRENT -ge 4 ]] then
- _files
-elif [[ "$tcmd" = *[zZ]*f* && $CURRENT -eq 3 ]] then
- _files -g '*.((tar|TAR).(gz|Z)|.tgz)'
-elif [[ "$tcmd" = *f* && $CURRENT -eq 3 ]] then
- _files -g '*.(tar|TAR)'
fi
diff --git a/Completion/User/_tar_archive b/Completion/User/_tar_archive
new file mode 100644
index 000000000..58e436c4d
--- /dev/null
+++ b/Completion/User/_tar_archive
@@ -0,0 +1,20 @@
+#autoload
+
+# This is used to generate filenames usable as a tar archive. This may
+# get one argument, a collection of tar option characters that may be
+# used to find out what kind of filename is needed. If no argument is
+# given but the parameter `_tar_cmd' is set, that is used.
+# If your version of `tar' supports this you may want to complete
+# things like `host:file' or `user@host:file' here.
+
+[[ $# -eq 0 && $+_tar_cmd -ne 0 ]] && set "$_tar_cmd"
+
+if [[ "$1" = *[tx]* ]]; then
+ if [[ "$1" = *[zZ]* ]]; then
+ _files -g '*.((tar|TAR).(gz|GZ|Z)|.tgz)'
+ else
+ _files -g '*.(tar|TAR)'
+ fi
+else
+ _files
+fi