1
0
Fork 0
mirror of git://git.code.sf.net/p/zsh/code synced 2025-09-02 22:11:54 +02:00
zsh/Misc/new-completion-examples
1999-04-15 18:05:38 +00:00

453 lines
10 KiB
Text

# Define a new widget behaving like `expand-or-complete' but calling the
# function `main-complete' to generate matches.
zle -c my-comp expand-or-complete main-complete
bindkey '\C-i' my-comp
# Below is a proposed main loop and the things it needs.
# One associative array for normal completions and one for patterns.
typeset -A comps
# These may be used to define completion handlers. First argument is the
# name of the function/variable containing the definition, the other
# arguments are the command names for which this definition should be used.
# With only one argument the function/variable-name __$1 is used.
defcomp() {
local v
if [[ $# -eq 1 ]] then
comps[$1]="__$1"
else
v="$1"
shift
for i; do
comps[$i]="$v"
done
fi
}
defpatcomp() {
if [[ ${+patcomps} == 1 ]] then
patcomps=("$patcomps[@]" "$2 $1" )
else
patcomps=( "$2 $1" )
fi
}
# These can be used to easily save and restore the state of the special
# variables used by the completion code.
alias compsave='local _opre _oipre _oargs _ocur;_opre="$PREFIX";_oipre="$IPREFIX";_oargs=( "$@" );_ocur="$CURRENT"'
alias compreset='PREFIX="$_opre";IPREFIX="$_oipre";argv=( "$_oargs[@]" );CURRENT="$_ocur"'
# This is an easy way to get completion for sub-commands.
alias compsub='do-complete "$@" || return 1'
# This searches $1 in the array for normal completions and calls the result.
compalso() {
1="$comps[$1]"
[[ -z "$1" ]] || call-complete "$@"
}
# This generates matches. The first argument is something we got from one
# of the associative arrays above. This is expected to be either the name
# of a variable in which case we use its value as arguments to complist,
# or it is the name of a function in which case we call this function with
# the arguments from the command line as its arguments.
call-complete() {
local var
eval var\=\$\{\+$1\}
if [[ "$var" == 0 ]] then
"$@"
else
eval complist \$\{${1}\[\@\]\}
fi
}
# The main loop of the competion code. This is what is called when TAB is
# pressed. The completion code gives us the special variables and the
# arguments from the command line are gives as positional parameters.
main-complete() {
emulate -R zsh
local comp
setopt localoptions nullglob rcexpandparam globdots
unsetopt markdirs globsubst shwordsplit nounset
# An entry for `--first--' is the replacement for `compctl -T'
# The `|| return 1' is used throughout: if a function producing matches
# returns non-zero this is interpreted as `do not try to produce more matches'
# (this is the replacement for `compctl -t').
comp="$comps[--first--]"
[[ -z "$comp" ]] || call-complete "$comp" "$@" || return 1
# For arguments we use the `do-complete' function below called via the
# convenience alias `compsub'.
if [[ $CONTEXT == argument || $CONTEXT == command ]] then
compsub
else
# Let's see if we have a special completion definition for the other
# possible contexts.
comp=''
case $CONTEXT in
redirect) comp="$comps[--redir--]";;
math) comp="$comps[--math--]";;
subscript) comp="$comps[--subscr--]";;
value) comp="$comps[--value--]";;
condition) comp="$comps[--cond--]";;
esac
# If not, we use default completion, if any.
[[ -z "$comp" ]] && comp="$comps[--default--]"
[[ -z "$comp" ]] || call-complete "$comp" "$@" || return 1
fi
}
# This does completion for a command (in command position and for the
# arguments).
do-complete() {
local comp cmd1 cmd2 pat val
# 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.
# a path and the last path name component).
if [[ $CONTEXT == command ]] then
comp="$comps[--command--]"
[[ -z "$comp" ]] || call-complete "$comp" "$@" || return 1
return 0
elif [[ "$COMMAND[1]" == '=' ]] then
eval cmd1\=$COMMAND
cmd2="$COMMAND[2,-1]"
elif [[ "$COMMAND" == */* ]] then
cmd1="$COMMAND"
cmd2="${COMMAND:t}"
else
cmd1="$COMMAND"
eval cmd2=$(whence -p $COMMAND)
fi
# See if there are any matching pattern completions.
for i in "$patcomps[@]"; do
pat="${i% *}"
val="${i#* }"
if [[ "$cmd1" == $~pat || "$cmd2" == $~pat ]] then
call-complete "$val" "$@" || return 1
fi
done
# Now look up the two names in the normal completion array.
comp="${comps[$cmd1]:-$comps[$cmd2]}"
# And generate the matches, probably using default completion.
[[ -z "$comp" ]] && comp="$comps[--default--]"
[[ -z "$comp" ]] || call-complete "$comp" "$@" || return 1
}
# Do sub-completion for pre-command modifiers.
defcomp __precmd - noglob nocorrect exec command builtin
__precmd() {
COMMAND="$1"
shift
(( CURRENT-- ))
if [[ CURRENT -eq 0 ]] then
CONTEXT=command
else
CONTEXT=argument
fi
compsub
}
# Utility function for in-path completion.
# First argument should be an complist-option (e.g. -f, -/, -g). The other
# arguments should be glob patterns, one per argument.
# E.g.: files -g '*.tex' '*.texi'
# This is intended as a replacement for `complist -f', `complist -/', and
# `complist -g ...' (but don't use it with other options).
# This function behaves as if you have a matcher definition like:
# compctl -M 'r:|[-.,_/]=* r:|=* m:{a-z}={A-Z} m:-=_ m:.=,'
# so you may want to modify this.
pfiles() {
local nm str pa pre epre a b c s rest
setopt localoptions nullglob rcexpandparam globdots extendedglob
unsetopt markdirs globsubst shwordsplit nounset
nm=$NMATCHES
if [[ $# -eq 0 ]] then
complist -f
elif [[ "$1" = -g ]] then
complist -g "$argv[2,-1]"
shift
else
complist $1
shift
fi
[[ -nmatches nm ]] || return
str="$PREFIX*$SUFFIX"
[[ -z "$1" ]] && 1='*'
if [[ $str[1] = \~ ]] then
pre="${str%%/*}/"
eval epre\=$pre
str="${str#*/}"
pa=''
else
pre=''
epre=''
if [[ $str[1] = / ]] then
str="$str[2,-1]"
pa='/'
else
pa=''
fi
fi
str="$str:gs/,/*,/:gs/_/*_/:gs./.*/.:gs/-/*[-_]/:gs/./*[.,]/:gs-*[.,]*[.,]*/-../-:gs.**.*."
while [[ "$str" = */* ]] do
rest="${str#*/}"
a="${epre}${pa}(#l)${str%%/*}(-/)"
a=( $~a )
if [[ $#a -eq 0 ]] then
return
elif [[ $#a -gt 1 ]] then
c=()
s=( $rest$@ )
s=( "${(@)s:gs.**.*.}" )
for i in $a; do
b=( $~i/(#l)$~s )
eval b\=\( \$\{b:/\*\(${(j:|:)fignore}\)\} \)
[[ $#b -ne 0 ]] && c=( $c $i )
done
if [[ $#c -eq 0 ]] then
return
elif [[ $#c -ne 1 ]] then
a="$epre$pa"
c=( $~c/(#l)$~s )
eval c\=\( \$\{c:/\*\(${(j:|:)fignore}\)\} \)
c=( ${c#$a} )
for i in $c; do
compadd -p "$pre$pa" -W "$a" -s "/${i#*/}" -f "${i%%/*}"
done
return
fi
a=( "$c[1]" )
fi
a="$a[1]"
pa="$pa${a##*/}/"
str="$rest"
done
a="$epre$pa"
s=( $str$@ )
s=( "${(@)s:gs.**.*.}" )
b=( $~a(#l)$~s )
eval b\=\( \$\{b:/\*\(${(j:|:)fignore}\)\} \)
compadd -p "$pre$pa" -W "$epre$pa" -f ${b#$a}
}
# Utility function for completing files of a given type or any file.
# In many cases you will want to call this one instead of pfiles().
files() {
local nm
nm=$NMATCHES
pfiles "$@"
[[ $# -ne 0 && -nmatches nm ]] && pfiles
}
# Simple default, command, and math completion defined with variables.
defcomp __default --default--
__default() {
files
}
defcomp __command --command--
__command=( -c )
defcomp __math --math--
__math=( -v )
defcomp __subscr --subscr--
__subscr() {
compalso --math-- "$@"
# ...probably other stuff
}
# A simple pattern completion, just as an example.
defpatcomp __x_options '*/X11/*'
__x_options() {
complist -J options -k '(-display -name -xrm)'
}
# A better example: completion for `find'.
defcomp find
__find() {
compsave
if [[ -mbetween -(ok|exec) \\\; ]] then
compsub
elif [[ -iprefix - ]] then
complist -s 'daystart {max,min,}depth follow noleaf version xdev \
{a,c,}newer {a,c,m}{min,time} empty false {fs,x,}type gid inum links \
{i,}{l,}name {no,}{user,group} path perm regex size true uid used \
exec {f,}print{f,0,} ok prune ls'
compreset
elif [[ -position 1 ]] then
complist -g '. ..'
files -g '(-/)'
elif [[ -mcurrent -1 -((a|c|)newer|fprint(|0|f)) ]] then
files
elif [[ -current -1 -fstype ]] then
complist -k '(ufs 4.2 4.3 nfs tmp mfs S51K S52K)'
elif [[ -current -1 -group ]] then
complist -k groups
elif [[ -current -1 -user ]] then
complist -u
fi
}
# Various completions...
defcomp __gunzip gunzip zcat
__gunzip() {
files -g '*.[gG][z]'
}
defcomp gzip
__gzip() {
files -g '*~*.[gG][zZ]'
}
defcomp xfig
__xfig() {
files -g '*.fig'
}
defcomp __make make gmake
__make() {
complist -s "\$(awk '/^[a-zA-Z0-9][^/ ]+:/ {print \$1}' FS=: [mM]akefile)"
}
defcomp __ps gs ghostview gview psnup psselect pswrap pstops pstruct lpr
__ps() {
files -g '*([pP][sS]|eps)'
}
defcomp __which which whence
__which=( -caF )
defcomp __rlogin rlogin rsh ssh
__rlogin() {
if [[ -position 1 ]] then
complist -k hosts
elif [[ -position 2 ]] then
complist -k '(-l)'
elif [[ -position 3 && -word 1 artus ]] then
complist -k '(puck root)'
fi
}
defcomp __dvi xdvi dvips dvibook dviconcat dvicopy dvidvi dviselect dvitodvi dvitype
__dvi() {
files -g '*.(dvi|DVI)'
}
defcomp __dirs rmdir df du dircmp cd
__dirs() {
files -/ '*(-/)'
}
defcomp __jobs fg bg jobs
__jobs=(-j -P '%?')
defcomp kill
__kill() {
if [[ -iprefix '-' ]] then
complist -k signals
else
complist -P '%?' -j
fi
}
defcomp __uncompress uncompress zmore
__uncompress() {
files -g '*.Z'
}
defcomp compress
__compress() {
files -g '*~*.Z'
}
defcomp __tex tex latex glatex slitex gslitex
__tex() {
files -g '*.(tex|TEX|texinfo|texi)'
}
defcomp __options setopt unsetopt
__options=(-M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' -o)
defcomp __funcs unfunction
__funcs=(-F)
defcomp __aliases unalias
__aliases=(-a)
defcomp __vars unset
__vars=(-v)
defcomp __enabled disable
__enabled=(-FBwa)
defcomp __disabled enable
__disabled=(-dFBwa)
defcomp __pdf acroread
__pdf() {
files -g '*.(pdf|PDF)'
}
defcomp tar
__tar() {
local nm tf
compsave
tf="$2"
nm=$NMATCHES
if [[ ( -mword 1 *t*f* || -mword 1 *x*f* ) && -position 3 100000 ]] then
complist -k "( $(tar tf $tf) )"
compreset
elif [[ -mword 1 *c*f* && -position 3 100000 ]] then
files
compreset
elif [[ -mcurrent -1 *f* && -position 2 ]] then
files -g '*.(tar|TAR)'
fi
}