mirror of
git://git.code.sf.net/p/zsh/code
synced 2025-09-03 10:21:46 +02:00
683 lines
15 KiB
Text
683 lines
15 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 a=''
|
|
|
|
if [[ "$1" = -a ]] then
|
|
shift
|
|
a=yes
|
|
fi
|
|
if [[ $# -eq 1 ]] then
|
|
comps[$1]="__$1"
|
|
[[ -z "$a" ]] || autoload "__$1"
|
|
else
|
|
v="$1"
|
|
shift
|
|
for i; do
|
|
comps[$i]="$v"
|
|
done
|
|
[[ -z "$a" ]] || autoload "$v"
|
|
fi
|
|
}
|
|
|
|
defpatcomp() {
|
|
if [[ "$1" = -a ]] then
|
|
shift
|
|
autoload "$1"
|
|
fi
|
|
if (( $+patcomps )) 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() {
|
|
if [[ ${(P)+${1}} -eq 1 ]] then
|
|
complist ${(@P)${1}}
|
|
else
|
|
"$@"
|
|
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
|
|
# We first try the `compctl's. This is without first (-T) and default (-D)
|
|
# completion. If you want them add `-T' and/or `-D' to this command.
|
|
# If this produces any matches, we don't try new style completion. If you
|
|
# want to have that tried anyway, remove the `[[ -nmatches ... ]] ...'
|
|
# below.
|
|
|
|
compcall
|
|
[[ -nmatches 0 ]] || return
|
|
|
|
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
|
|
}
|
|
|
|
# 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:.=,' \
|
|
# 'm:{a-z}={A-Z} l:|=* r:|=*'
|
|
# so you may want to modify this.
|
|
|
|
pfiles() {
|
|
local nm ostr opa pre epre a b c s rest ppres
|
|
|
|
setopt localoptions nullglob rcexpandparam globdots extendedglob
|
|
unsetopt markdirs globsubst shwordsplit nounset
|
|
|
|
if [[ "$1" = -W ]] then
|
|
a="$2"
|
|
if [[ "$a[1]" = '(' ]] then
|
|
ppres=( $a[2,-2]/ )
|
|
else
|
|
ppres=( ${(P)${a}} )
|
|
[[ $#ppres -eq 0 ]] && ppres=( $a/ )
|
|
fi
|
|
[[ $#ppres -eq 0 ]] && ppres=( '' )
|
|
shift 2
|
|
else
|
|
ppres=( '' )
|
|
fi
|
|
|
|
str="${PREFIX:q}*${SUFFIX:q}"
|
|
|
|
if [[ -z "$a[1]" || "$str[1]" = [~/] || "$str" = (.|..)/* ]] then
|
|
a=()
|
|
else
|
|
a=(-W "( $ppres )")
|
|
fi
|
|
nm=$NMATCHES
|
|
if [[ $# -eq 0 ]] then
|
|
complist "$a[@]" -f
|
|
elif [[ "$1" = -g ]] then
|
|
complist "$a[@]" -g "$argv[2,-1]"
|
|
shift
|
|
else
|
|
complist "$a[@]" $1
|
|
shift
|
|
fi
|
|
[[ -nmatches nm ]] || return
|
|
|
|
[[ -z "$1" ]] && 1='*'
|
|
|
|
if [[ "$str[1]" = \~ ]] then
|
|
pre="${str%%/*}/"
|
|
eval epre\=$pre
|
|
str="${str#*/}"
|
|
opa=''
|
|
ppres=( '' )
|
|
else
|
|
pre=''
|
|
epre=''
|
|
if [[ "$str[1]" = / ]] then
|
|
str="$str[2,-1]"
|
|
opa='/'
|
|
ppres=( '' )
|
|
else
|
|
[[ "$str" = (.|..)/* ]] && ppres=( '' )
|
|
opa=''
|
|
fi
|
|
fi
|
|
while [[ "$str" = */* ]] do
|
|
[[ -e "$epre$opa${str%%/*}" ]] || break
|
|
opa="$opa${str%%/*}/"
|
|
str="${str#*/}"
|
|
done
|
|
|
|
if [[ -matcher 1 ]] then
|
|
ostr="$str:gs/,/*,/:gs/_/*_/:gs./.*/.:gs/-/*[-_]/:gs/./*[.,]/:gs-*[.,]*[.,]*/-../-:gs.**.*."
|
|
else
|
|
ostr="${str%/*}/*${str##*/}*"
|
|
ostr="$ostr:gs./.*/.:gs.**.*."
|
|
fi
|
|
|
|
for ppre in "$ppres[@]"; do
|
|
str="$ostr"
|
|
pa="$opa"
|
|
while [[ "$str" = */* ]] do
|
|
rest="${str#*/}"
|
|
a="${ppre}${epre}${pa}(#l)${str%%/*}(-/)"
|
|
a=( $~a )
|
|
if [[ $#a -eq 0 ]] then
|
|
continue 2
|
|
elif [[ $#a -gt 1 ]] then
|
|
c=()
|
|
s=( $rest$@ )
|
|
s=( "${(@)s:gs.**.*.}" )
|
|
for i in $a; do
|
|
b=( $~i/(#l)$~s )
|
|
[[ $#b -ne 0 ]] && c=( $c $i )
|
|
done
|
|
if [[ $#c -eq 0 ]] then
|
|
continue 2
|
|
elif [[ $#c -ne 1 ]] then
|
|
a="$ppre$epre$pa"
|
|
c=( $~c/(#l)$~s )
|
|
c=( ${c#$a} )
|
|
for i in $c; do
|
|
compadd -p "$pre$pa" -W "$a" -s "/${i#*/}" -fF -- "${i%%/*}"
|
|
done
|
|
continue 2
|
|
fi
|
|
a=( "$c[1]" )
|
|
fi
|
|
a="$a[1]"
|
|
pa="$pa${a##*/}/"
|
|
str="$rest"
|
|
done
|
|
a="$ppre$epre$pa"
|
|
s=( $str$@ )
|
|
s=( "${(@)s:gs.**.*.}" )
|
|
b=( $~a(#l)$~s )
|
|
compadd -p "$pre$pa" -W "$ppre$epre$pa" -fF -- ${b#$a}
|
|
done
|
|
}
|
|
|
|
# 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=$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 __vars --math--
|
|
__vars=( -v )
|
|
|
|
defcomp __subscr --subscr--
|
|
__subscr() {
|
|
compalso --math-- "$@"
|
|
[[ ${(Pt)${COMMAND}} = assoc* ]] && complist -k "( ${(kP)${COMMAND}} )"
|
|
}
|
|
|
|
defcomp __cond --cond--
|
|
__cond() {
|
|
if [[ -current -1 -o ]] then
|
|
complist -o -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}'
|
|
elif [[ -current -1 -nt || -current -1 -ot || -current -1 -ef ]] then
|
|
files
|
|
else
|
|
files
|
|
complist -v
|
|
fi
|
|
}
|
|
|
|
# Do sub-completion for pre-command modifiers.
|
|
|
|
defcomp __precmd - nohup nice eval time rusage noglob nocorrect exec
|
|
__precmd() {
|
|
COMMAND="$1"
|
|
shift
|
|
(( CURRENT-- ))
|
|
if [[ CURRENT -eq 0 ]] then
|
|
CONTEXT=command
|
|
else
|
|
CONTEXT=argument
|
|
fi
|
|
compsub
|
|
}
|
|
|
|
defcomp builtin
|
|
__builtin() {
|
|
if [[ -position 2 -1 ]] then
|
|
compsub
|
|
else
|
|
complist -eB
|
|
fi
|
|
}
|
|
|
|
defcomp command
|
|
__command() {
|
|
if [[ -position 2 -1 ]] then
|
|
compsub
|
|
else
|
|
complist -em
|
|
fi
|
|
}
|
|
|
|
# Various completions...
|
|
|
|
defcomp __jobs fg jobs
|
|
__jobs=(-j -P '%')
|
|
|
|
defcomp __bjobs bg
|
|
__bjobs=(-z -P '%')
|
|
|
|
defcomp __arrays shift
|
|
__arrays=(-A)
|
|
|
|
defcomp __which which whence where type
|
|
__which=( -caF )
|
|
|
|
defcomp unhash
|
|
__unhash() {
|
|
[[ -mword 1 -*d* ]] && complist -n
|
|
[[ -mword 1 -*a* ]] && complist -a
|
|
[[ -mword 1 -*f* ]] && complist -F
|
|
[[ ! -mword 1 -* ]] && complist -m
|
|
}
|
|
|
|
defcomp hash
|
|
__hash() {
|
|
if [[ -mword 1 -*d* ]] then
|
|
if [[ -string 1 '=' ]] then
|
|
pfiles -g '*(-/)'
|
|
else
|
|
complist -n -q -S '='
|
|
fi
|
|
elif [[ -string 1 '=' ]] then
|
|
files -g '*(*)' '*(-/)'
|
|
else
|
|
complist -m -q -S '='
|
|
fi
|
|
}
|
|
|
|
defcomp __funcs unfunction
|
|
__funcs=(-F)
|
|
|
|
defcomp echotc
|
|
__echotc=(-k '(al dc dl do le up al bl cd ce cl cr dc dl do ho is le ma nd nl se so up)')
|
|
|
|
defcomp __aliases unalias
|
|
__aliases=(-a)
|
|
|
|
defcomp __vars getopts read unset vared
|
|
|
|
defcomp __varseq declare export integer local readonly typeset
|
|
__varseq=(-v -S '=')
|
|
|
|
defcomp disable
|
|
__disable() {
|
|
[[ -mcurrent -1 -*a* ]] && complist -ea
|
|
[[ -mcurrent -1 -*f* ]] && complist -eF
|
|
[[ -mcurrent -1 -*r* ]] && complist -ew
|
|
[[ ! -mcurrent -1 -* ]] && complist -eB
|
|
}
|
|
|
|
defcomp enable
|
|
__enable() {
|
|
[[ -mcurrent -1 -*a* ]] && complist -da
|
|
[[ -mcurrent -1 -*f* ]] && complist -dF
|
|
[[ -mcurrent -1 -*r* ]] && complist -dw
|
|
[[ ! -mcurrent -1 -* ]] && complist -dB
|
|
}
|
|
|
|
defcomp __limits limit unlimit
|
|
__limits=(-k "(${(j: :)${(f)$(limit)}%% *})")
|
|
|
|
defcomp source
|
|
__source() {
|
|
if [[ -position 2 -1 ]] then
|
|
compsub
|
|
else
|
|
files
|
|
fi
|
|
}
|
|
|
|
defcomp setopt
|
|
__setopt() {
|
|
local nm=$NMATCHES
|
|
|
|
complist -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' \
|
|
-s '$({ unsetopt kshoptionprint; unsetopt } 2>/dev/null)'
|
|
[[ -nmatches nm ]] && complist -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' -o
|
|
}
|
|
|
|
defcomp unsetopt
|
|
__unsetopt() {
|
|
local nm=$NMATCHES
|
|
|
|
complist -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' \
|
|
-s '$({ unsetopt kshoptionprint; setopt } 2>/dev/null)'
|
|
[[ -nmatches nm ]] && complist -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' -o
|
|
}
|
|
|
|
defcomp autoload
|
|
__autoload=(-s '${^fpath}/*(N:t)')
|
|
|
|
defcomp bindkey
|
|
__bindkey() {
|
|
if [[ -mword 1 -*[DAN]* || -mcurrent -1 -*M ]] then
|
|
complist -s '$(bindkey -l)'
|
|
else
|
|
complist -b
|
|
fi
|
|
}
|
|
|
|
defcomp fc
|
|
__fc() {
|
|
if [[ -mcurrent -1 -*e ]] then
|
|
complist -c
|
|
elif [[ -mcurrent -1 -[ARWI]## ]] then
|
|
files
|
|
fi
|
|
}
|
|
|
|
defcomp sched
|
|
__sched() {
|
|
[[ -position 2 -1 ]] && compsub
|
|
}
|
|
|
|
defcomp set
|
|
__set() {
|
|
if [[ -mcurrent -1 [-+]o ]] then
|
|
complist -o
|
|
elif [[ -current -1 -A ]] then
|
|
complist -A
|
|
fi
|
|
}
|
|
|
|
defcomp zle
|
|
__zle() {
|
|
if [[ -word 1 -N && -position 3 ]] then
|
|
complist -F
|
|
else
|
|
complist -b
|
|
fi
|
|
}
|
|
|
|
defcomp zmodload
|
|
__zmodload() {
|
|
if [[ -mword 1 -*(a*u|u*a)* || -mword 1 -*a* && -position 3 -1 ]] then
|
|
complist -B
|
|
elif [[ -mword 1 -*u* ]] then
|
|
complist -s '$(zmodload)'
|
|
else
|
|
complist -s '${^module_path}/*(N:t:r)'
|
|
fi
|
|
}
|
|
|
|
defcomp trap
|
|
__trap() {
|
|
if [[ -position 1 ]] then
|
|
complist -c
|
|
else
|
|
complist -k signals
|
|
fi
|
|
}
|
|
|
|
killfunc() {
|
|
reply=( "$(ps -x 2>/dev/null)" )
|
|
}
|
|
|
|
defcomp kill
|
|
__kill() {
|
|
if [[ -iprefix '-' ]] then
|
|
complist -k "($signals[1,-3])"
|
|
else
|
|
complist -P '%' -j
|
|
complist -y killfunc -s '`ps -x 2>/dev/null | tail +2 | cut -c1-5`'
|
|
fi
|
|
}
|
|
|
|
defcomp wait
|
|
__wait() {
|
|
complist -P '%' -j
|
|
complist -y killfunc -s '`ps -x 2>/dev/null | tail +2 | cut -c1-5`'
|
|
}
|
|
|
|
defcomp cd
|
|
__cd() {
|
|
files -W cdpath -g '*(-/)'
|
|
}
|
|
|
|
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 __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 pmake
|
|
__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 __dvi xdvi dvips dvibook dviconcat dvicopy dvidvi dviselect dvitodvi dvitype
|
|
__dvi() {
|
|
files -g '*.(dvi|DVI)'
|
|
}
|
|
|
|
defcomp __dirs rmdir df du dircmp
|
|
__dirs() {
|
|
files -/ '*(-/)'
|
|
}
|
|
|
|
defcomp __uncompress uncompress zmore
|
|
__uncompress() {
|
|
files -g '*.Z'
|
|
}
|
|
|
|
defcomp compress
|
|
__compress() {
|
|
files -g '*~*.Z'
|
|
}
|
|
|
|
defcomp __tex tex latex slitex
|
|
__tex() {
|
|
files -g '*.(tex|TEX|texinfo|texi)'
|
|
}
|
|
|
|
defcomp __pdf acroread
|
|
__pdf() {
|
|
files -g '*.(pdf|PDF)'
|
|
}
|
|
|
|
defcomp tar
|
|
__tar() {
|
|
local nm=$NMATCHES tf="$2"
|
|
compsave
|
|
|
|
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
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
# A simple pattern completion, just as an example.
|
|
|
|
defpatcomp __x_options '*/X11/*'
|
|
__x_options() {
|
|
complist -J options -k '(-display -name -xrm)'
|
|
}
|