diff --git a/Completion/Base/Widget/_read_comp b/Completion/Base/Widget/_read_comp new file mode 100644 index 000000000..bc6e4c3c6 --- /dev/null +++ b/Completion/Base/Widget/_read_comp @@ -0,0 +1,151 @@ +#compdef -k complete-word \C-x\C-r + +# This allows an on-the-fly choice of completions. On typing the key +# sequence given above, you will be prompted for a string of arguments. If +# this string begins with `_', it will be taken as the name of a function to +# evaluate to generate the completions; unambiguous strings in the function +# name are automatically completed. +# +# Else it is taken to be a set of arguments for compadd to generate a list +# of choices. The possibilities are the same as the flags for generating +# completions given in the zshcompwid manual page. Note the arguments are +# verbatim: include minus signs, spaces, quotes, etc. +# +# On subsequent calls, the same completion will be re-performed. To +# force a new type of completion to be read, supply a numeric argument. +# +# For example, +# % bindkey | grep rever +# Completion: -b +# % bindkey | grep reverse-menu-complete _ +# +# Global variables used: +# _read_comp Last completion string read from user + +# emulate -L zsh +setopt localoptions extendedglob nobadpattern # xtrace promptsubst +# local PS4='%N:%i:$((#key))> ' + +if [[ ${+NUMERIC} = 0 && -n $_read_comp ]]; then + if [[ $_read_comp = _* ]]; then + eval $_read_comp + else + eval "compadd $_read_comp" + fi + return +fi + +_read_comp= + +local key search str str2 newch funcs funcs2 exact msg list +integer pos + +msg="Completion: " + +zle -R $msg + +if ! read -k key; then + zle -cR '' + return 1 +fi + +while [[ '#key' -ne 10 && '#key' -ne 13 ]]; do + if [[ '#key' -eq 0 && '#key' -eq 3 || '#key' -eq 7 ]]; then + zle -cR '' + return 1 + fi + if [[ ( '#key' -eq 8 || '#key' -eq 127 ) && -n $str ]]; then + # delete character + str="$str[1,-2]" + exact= + list=() + elif [[ '#key' -eq 21 ]]; then + # ^U: delete line + str= + exact= + list=() + elif [[ '#key' -eq 4 && $str = _[^\ ]# && $str != *' '* ]]; then + # ^D: list completions + list=(${$(whence -m "$str*" 2>/dev/null)%: function}) + elif [[ ( -n $exact && $key != ' ' ) || '#key & 127' -lt 32 ]]; then + # If we've got an exact function, only allow a space after it. + # Don't try to insert non-printing characters. + if [[ -n $ZBEEP ]]; then + print -nb $ZBEEP + elif [[ -o beep ]]; then + print -n "\a" + fi + list=() + else + str="$str$key" + if [[ $str = _[^\ ]# ]]; then + # Rudimentary completion for function names. + # Allow arguments, i.e. don't do this after we've got a space. + funcs=(${$(whence -m "$str*" 2>/dev/null)%: function}) + if [[ -o autolist && $#str -gt 1 ]]; then + list=($funcs) + else + list=() + fi + if (( $#funcs == 1 )); then + # Exact match; prompt the user for a newline to confirm + str=$funcs[1] + exact=" (Confirm)" + elif (( $#funcs == 0 )); then + # We can't call zle beep, because this isn't a zle widget. + if [[ -n $ZBEEP ]]; then + print -nb $ZBEEP + elif [[ -o beep ]]; then + print -n "\a" + fi + str="$str[1,-2]" + list=() + else + # Add characters to the string until a name doesn't + # match any more, then backtrack one character to get + # the longest unambiguous match. + str2=$str + pos=$#str2 + while true; do + (( pos++ )) + newch=${funcs[1][pos]} + [[ -z $newch ]] && break + str2=$str2$newch + funcs2=(${funcs##$str2*}) + (( $#funcs2 )) && break + str=$str2 + done + fi + else + exact= + fi + fi + if (( $#list )); then + zle -R "$msg$str$exact" $list + else + zle -cR "$msg$str$exact" + fi + if ! read -k key; then + zle -cR '' + return 1 + fi +done + +if [[ -z $str ]]; then + # string must be non-zero + return 1 +elif [[ $str = _* ]] && ! whence ${str%% *} >& /dev/null; then + # a function must be known to the shell + return 1 +else + # remember the string for re-use + _read_comp=$str +fi + +zle -cR '' + +if [[ $str = _* ]]; then + eval $str +else + eval "compadd $str" +fi