diff options
Diffstat (limited to 'Functions/Misc')
-rw-r--r-- | Functions/Misc/add-zle-hook-widget | 186 | ||||
-rw-r--r-- | Functions/Misc/add-zsh-hook | 2 | ||||
-rw-r--r-- | Functions/Misc/run-help-ip | 4 | ||||
-rw-r--r-- | Functions/Misc/zcalc | 345 | ||||
-rw-r--r-- | Functions/Misc/zed | 4 |
5 files changed, 445 insertions, 96 deletions
diff --git a/Functions/Misc/add-zle-hook-widget b/Functions/Misc/add-zle-hook-widget new file mode 100644 index 000000000..d8a3950fb --- /dev/null +++ b/Functions/Misc/add-zle-hook-widget @@ -0,0 +1,186 @@ +# Add to HOOK the given WIDGET +# +# HOOK is one of isearch-exit, isearch-update, line-pre-redraw, line-init, +# line-finish, history-line-set, keymap-select (the zle- prefix is allowed +# but not required). If a widget corresponding to HOOK already exists, it +# is preserved and called first in the new set of HOOK widgets. +# +# With -d, remove the WIDGET from the hook instead; deletes the hook +# linkage if it is empty. +# +# -D behaves like -d, but pattern characters are active in WIDGET, so +# any matching widget will be deleted from the hook. +# +# Without -d, if the WIDGET is not already defined, a function having the +# same name is marked for autoload; -U is passed down to autoload if that +# is given, as are -z and -k. (This is harmless if the function is +# already defined.) The WIDGET is then created with zle -N. +# +# The -L option lists the hooks and their associated widgets. + +# This is probably more safeguarding than necessary +zmodload -e zsh/zle || return 1 +{ zmodload zsh/parameter && zmodload zsh/zleparameter } || { + print -u2 "add-zle-hook-widget: Need parameter modules for zle hooks" + return 1 +} + +() { # Preserve caller global option settings + +emulate -L zsh + +# Setup - create the base functions for hook widgets that call the others + +local -a hooktypes=( zle-isearch-exit zle-isearch-update + zle-line-pre-redraw zle-line-init zle-line-finish + zle-history-line-set zle-keymap-select ) +# Stash in zstyle to make it global +zstyle zle-hook types ${hooktypes#zle-} + +# Relying on multifuncdef option here +function azhw:${^hooktypes} { + local -a hook_widgets + local hook + # Values of these styles look like number:name + # and we run them in number order + zstyle -a $WIDGET widgets hook_widgets + for hook in "${(@)${(@on)hook_widgets[@]}#<->:}"; do + if [[ "$hook" = user:* ]]; then + # Preserve $WIDGET within the renamed widget + zle "$hook" -N -- "$@" + else + zle "$hook" -Nw -- "$@" + fi || return + done + return 0 +} + +# Redefine ourself with the setup left out + +function add-zle-hook-widget { + local -a hooktypes + zstyle -a zle-hook types hooktypes + + # This part copied from add-zsh-hook + local usage="Usage: $funcstack[1] hook widgetname\nValid hooks are:\n $hooktypes" + + local opt + local -a autoopts + integer del list help + + while getopts "dDhLUzk" opt; do + case $opt in + (d) + del=1 + ;; + + (D) + del=2 + ;; + + (h) + help=1 + ;; + + (L) + list=1 + ;; + + ([Uzk]) + autoopts+=(-$opt) + ;; + + (*) + return 1 + ;; + esac + done + shift $(( OPTIND - 1 )) + + 1=${1#zle-} # Strip prefix not stored in zle-hook types style + + if (( list )); then + zstyle -L "zle-(${1:-${(@j:|:)hooktypes[@]}})" widgets + return $? + elif (( help || $# != 2 || ${hooktypes[(I)$1]} == 0 )); then + print -u$(( 2 - help )) $usage + return $(( 1 - help )) + fi + + local -aU extant_hooks + local hook="zle-$1" + local fn="$2" + + if (( del )); then + # delete, if hook is set + if zstyle -g extant_hooks "$hook" widgets; then + if (( del == 2 )); then + set -A extant_hooks ${extant_hooks[@]:#(<->:|)${~fn}} + else + set -A extant_hooks ${extant_hooks[@]:#(<->:|)$fn} + fi + # unset if no remaining entries + if (( ${#extant_hooks} )); then + zstyle "$hook" widgets "${extant_hooks[@]}" + else + zstyle -d "$hook" widgets + fi + fi + else + # Check whether attempting to add a widget named for the hook + if [[ "$fn" = "$hook" ]]; then + if [[ -n "${widgets[$fn]}" ]]; then + print -u2 "$funcstack[1]: Cannot hook $fn to itself" + return 1 + fi + # No point in building the array until another is added + autoload "${autoopts[@]}" -- "$fn" + zle -N "$fn" + return 0 + fi + integer i=${#options[ksharrays]}-2 + zstyle -g extant_hooks "$hook" widgets + # Check for an existing widget, add it as the first hook + if [[ ${widgets[$hook]} != "user:azhw:$hook" ]]; then + if [[ -n ${widgets[$hook]} ]]; then + zle -A "$hook" "${widgets[$hook]}" + extant_hooks=(0:"${widgets[$hook]}" "${extant_hooks[@]}") + fi + zle -N "$hook" azhw:"$hook" + fi + # Add new widget only if not already in the hook list + if [[ -z ${(M)extant_hooks[@]:#(<->:|)$fn} ]]; then + # no index and not already hooked + # assign largest existing index plus 1 + i=${${(On@)${(@M)extant_hooks[@]#<->:}%:}[i]}+1 + else + return 0 + fi + extant_hooks+=("${i}:${fn}") + zstyle -- "$hook" widgets "${extant_hooks[@]}" + if [[ -z "${widgets[$fn]}" ]]; then + autoload "${autoopts[@]}" -- "$fn" + zle -N -- "$fn" + fi + if [[ -z "${widgets[$hook]}" ]]; then + zle -N "$hook" azhw:"$hook" + fi + fi +} + +} "$@" # Resume caller global options + +# Handle zsh autoloading conventions: +# - "file" appears last in zsh_eval_context when "source"-ing +# - "evalautofunc" appears with kshautoload set or autoload -k +# - "loadautofunc" appears with kshautoload unset or autoload -z +# - use of autoload +X cannot reliably be detected, use best guess +case "$zsh_eval_context" in +*file) ;; +*evalautofunc) ;; +*loadautofunc) add-zle-hook-widget "$@";; +*) [[ -o kshautoload ]] || add-zle-hook-widget "$@";; +esac +# Note fallback here is equivalent to the usual best-guess used by +# functions written for zsh before $zsh_eval_context was available +# so this case-statement is backward-compatible. diff --git a/Functions/Misc/add-zsh-hook b/Functions/Misc/add-zsh-hook index fc39659ae..3bc952e2f 100644 --- a/Functions/Misc/add-zsh-hook +++ b/Functions/Misc/add-zsh-hook @@ -19,7 +19,7 @@ hooktypes=( chpwd precmd preexec periodic zshaddhistory zshexit zsh_directory_name ) -local usage="Usage: $0 hook function\nValid hooks are:\n $hooktypes" +local usage="Usage: add-zsh-hook hook function\nValid hooks are:\n $hooktypes" local opt local -a autoopts diff --git a/Functions/Misc/run-help-ip b/Functions/Misc/run-help-ip index 740af52b5..8807f9ef1 100644 --- a/Functions/Misc/run-help-ip +++ b/Functions/Misc/run-help-ip @@ -19,8 +19,8 @@ while [[ $# != 0 && $1 == -* ]]; do done case $1 in - (addr*) man ip-address ;; - (addrlabel) man ip-addrlabel ;; + (addrl*) man ip-addrlabel ;; + (a*) man ip-address ;; (l2*) man ip-l2tp ;; (li*) man ip-link ;; (ma*) man ip-maddress ;; diff --git a/Functions/Misc/zcalc b/Functions/Misc/zcalc index 857007a94..480373345 100644 --- a/Functions/Misc/zcalc +++ b/Functions/Misc/zcalc @@ -94,24 +94,53 @@ # sequentially just as if read automatically. emulate -L zsh -setopt extendedglob +setopt extendedglob typesetsilent + +zcalc_show_value() { + if [[ -n $_base ]]; then + print -- $(( $_base $1 )) + elif [[ $1 = *.* ]] || (( _outdigits )); then + # With normal output, ensure trailing "." doesn't get lost. + if [[ -z $_forms[_outform] || ($_outform -eq 1 && $1 = *.) ]]; then + print -- $(( $1 )) + else + printf "$_forms[_outform]\n" $_outdigits $1 + fi + else + printf "%d\n" $1 + fi +} # For testing in ZLE functions. local ZCALC_ACTIVE=1 # TODO: make local variables that shouldn't be visible in expressions # begin with _. -local line ans base defbase forms match mbegin mend psvar optlist opt arg +local _line ans _base _defbase _forms match mbegin mend +local psvar _optlist _opt _arg _tmp local compcontext="-zcalc-line-" -integer num outdigits outform=1 expression_mode -local -a expressions +integer _num _outdigits _outform=1 _expression_mode +integer _rpn_mode _matched _show_stack _i _n +integer _max_stack _push +local -a _expressions stack # We use our own history file with an automatic pop on exit. history -ap "${ZDOTDIR:-$HOME}/.zcalc_history" -forms=( '%2$g' '%.*g' '%.*f' '%.*E' '') +_forms=( '%2$g' '%.*g' '%.*f' '%.*E' '') -zmodload -i zsh/mathfunc 2>/dev/null +local _mathfuncs +if zmodload -i zsh/mathfunc 2>/dev/null; then + zmodload -P _mathfuncs -FL zsh/mathfunc + _mathfuncs="("${(j.|.)${_mathfuncs##f:}}")" +fi +local -A _userfuncs +for _line in ${(f)"$(functions -M)"}; do + match=(${=_line}) + # get minimum number of arguments + _userfuncs[${match[3]}]=${match[4]} +done +_line= autoload -Uz zmathfuncdef if (( ! ${+ZCALCPROMPT} )); then @@ -127,111 +156,119 @@ if [[ -f "${ZDOTDIR:-$HOME}/.zcalcrc" ]]; then fi # Process command line -while [[ -n $1 && $1 = -(|[#-]*|f|e) ]]; do - optlist=${1[2,-1]} +while [[ -n $1 && $1 = -(|[#-]*|f|e|r(<->|)) ]]; do + _optlist=${1[2,-1]} shift - [[ $optlist = (|-) ]] && break - while [[ -n $optlist ]]; do - opt=${optlist[1]} - optlist=${optlist[2,-1]} - case $opt in + [[ $_optlist = (|-) ]] && break + while [[ -n $_optlist ]]; do + _opt=${_optlist[1]} + _optlist=${_optlist[2,-1]} + case $_opt in ('#') # Default base - if [[ -n $optlist ]]; then - arg=$optlist - optlist= + if [[ -n $_optlist ]]; then + _arg=$_optlist + _optlist= elif [[ -n $1 ]]; then - arg=$1 + _arg=$1 shift else print -- "-# requires an argument" >&2 return 1 fi - if [[ $arg != (|\#)[[:digit:]]## ]]; then + if [[ $_arg != (|\#)[[:digit:]]## ]]; then print -- "-# requires a decimal number as an argument" >&2 return 1 fi - defbase="[#${arg}]" + _defbase="[#${_arg}]" ;; (f) # Force floating point operation setopt forcefloat ;; (e) # Arguments are expressions - (( expression_mode = 1 )); + (( _expression_mode = 1 )); + ;; + (r) # RPN mode. + (( _rpn_mode = 1 )) + ZCALC_ACTIVE=rpn + if [[ $_optlist = (#b)(<->)* ]]; then + (( _show_stack = ${match[1]} )) + _optlist=${_optlist[${#match[1]}+1,-2]} + fi ;; esac done done -if (( expression_mode )); then - expressions=("$@") +if (( _expression_mode )); then + _expressions=("$@") argv=() fi -for (( num = 1; num <= $#; num++ )); do +for (( _num = 1; _num <= $#; _num++ )); do # Make sure all arguments have been evaluated. # The `$' before the second argv forces string rather than numeric # substitution. - (( argv[$num] = $argv[$num] )) - print "$num> $argv[$num]" + (( argv[$_num] = $argv[$_num] )) + print "$_num> $argv[$_num]" done -psvar[1]=$num -local prev_line cont_prompt -while (( expression_mode )) || - vared -cehp "${cont_prompt}${ZCALCPROMPT}" line; do - if (( expression_mode )); then - (( ${#expressions} )) || break - line=$expressions[1] - shift expressions +psvar[1]=$_num +local _prev_line _cont_prompt +while (( _expression_mode )) || + vared -cehp "${_cont_prompt}${ZCALCPROMPT}" _line; do + if (( _expression_mode )); then + (( ${#_expressions} )) || break + _line=$_expressions[1] + shift _expressions fi - if [[ $line = (|*[^\\])('\\')#'\' ]]; then - prev_line+=$line[1,-2] - cont_prompt="..." - line= + if [[ $_line = (|*[^\\])('\\')#'\' ]]; then + _prev_line+=$_line[1,-2] + _cont_prompt="..." + _line= continue fi - line="$prev_line$line" - prev_line= - cont_prompt= + _line="$_prev_line$_line" + _prev_line= + _cont_prompt= # Test whether there are as many open as close - # parentheses in the line so far. - if [[ ${#line//[^\(]} -gt ${#line//[^\)]} ]]; then - prev_line+=$line - cont_prompt="..." - line= + # parentheses in the _line so far. + if [[ ${#_line//[^\(]} -gt ${#_line//[^\)]} ]]; then + _prev_line+=$_line + _cont_prompt="..." + _line= continue fi - [[ -z $line ]] && break + [[ -z $_line ]] && break # special cases # Set default base if `[#16]' or `[##16]' etc. on its own. # Unset it if `[#]' or `[##]'. - if [[ $line = (#b)[[:blank:]]#('[#'(\#|)((<->|)(|_|_<->))']')[[:blank:]]#(*) ]]; then + if [[ $_line = (#b)[[:blank:]]#('[#'(\#|)((<->|)(|_|_<->))']')[[:blank:]]#(*) ]]; then if [[ -z $match[6] ]]; then if [[ -z $match[3] ]]; then - defbase= + _defbase= else - defbase=$match[1] + _defbase=$match[1] fi - print -s -- $line - print -- $(( ${defbase} ans )) - line= + print -s -- $_line + print -- $(( ${_defbase} ans )) + _line= continue else - base=$match[1] + _base=$match[1] fi else - base=$defbase + _base=$_defbase fi - print -s -- $line + print -s -- $_line - line="${${line##[[:blank:]]#}%%[[:blank:]]#}" - case "$line" in + _line="${${_line##[[:blank:]]#}%%[[:blank:]]#}" + case "$_line" in # Escapes begin with a colon (:(\\|)\!*) # shell escape: handle completion's habit of quoting the ! - eval ${line##:(\\|)\![[:blank:]]#} - line= + eval ${_line##:(\\|)\![[:blank:]]#} + _line= continue ;; @@ -241,72 +278,196 @@ while (( expression_mode )) || ;; ((:|)norm) # restore output format to default - outform=1 + _outform=1 ;; ((:|)sci[[:blank:]]#(#b)(<->)(#B)) - outdigits=$match[1] - outform=2 + _outdigits=$match[1] + _outform=2 ;; ((:|)fix[[:blank:]]#(#b)(<->)(#B)) - outdigits=$match[1] - outform=3 + _outdigits=$match[1] + _outform=3 ;; ((:|)eng[[:blank:]]#(#b)(<->)(#B)) - outdigits=$match[1] - outform=4 + _outdigits=$match[1] + _outform=4 ;; (:raw) - outform=5 + _outform=5 ;; ((:|)local([[:blank:]]##*|)) - eval $line - line= + eval ${_line##:} + _line= continue ;; ((function|:f(unc(tion|)|))[[:blank:]]##(#b)([^[:blank:]]##)(|[[:blank:]]##([^[:blank:]]*))) zmathfuncdef $match[1] $match[3] - line= + _userfuncs[$match[1]]=${$(functions -Mm $match[1])[4]} + _line= continue ;; (:*) print "Unrecognised escape" - line= + _line= + continue + ;; + + (\$[[:IDENT:]]##) + # Display only, no calculation + _line=${_line##\$} + print -r -- ${(P)_line} + _line= continue ;; (*) - # Latest value is stored as a string, because it might be floating - # point or integer --- we don't know till after the evaluation, and - # arrays always store scalars anyway. - # - # Since it's a string, we'd better make sure we know which - # base it's in, so don't change that until we actually print it. - eval "ans=\$(( $line ))" - # on error $ans is not set; let user re-edit line - [[ -n $ans ]] || continue - argv[num++]=$ans - psvar[1]=$num + _line=${${_line##[[:blank:]]##}%%[[:blank:]]##} + if [[ _rpn_mode -ne 0 && $_line != '' ]]; then + _push=1 + _matched=1 + case $_line in + (\<[[:IDENT:]]##) + ans=${(P)${_line##\<}} + ;; + + (\=|pop|\>[[:IDENT:]]#) + if (( ${#stack} < 1 )); then + print -r -- "${_line}: not enough values on stack" >&2 + _line= + continue + fi + case $_line in + (=) + ans=${stack[1]} + ;; + (pop|\>) + _push=0 + shift stack + ;; + (\>[[:IDENT:]]##) + if [[ ${_line##\>} = (_*|stack|ans|PI|E) ]]; then + print "${_line##\>}: reserved variable" >&2 + _line= + continue + fi + local ${_line##\>} + (( ${_line##\>} = ${stack[1]} )) + _push=0 + shift stack + ;; + (*) + print "BUG in special RPN functions" >&2 + _line= + continue + ;; + esac + ;; + + (+|-|\^|\||\&|\*|/|\*\*|\>\>|\<\</) + # Operators with two arguments + if (( ${#stack} < 2 )); then + print -r -- "${_line}: not enough values on stack" >&2 + _line= + continue + fi + eval "(( ans = \${stack[2]} $_line \${stack[1]} ))" + shift 2 stack + ;; + + (ldexp|jn|yn|scalb|xy|\<\>) + # Functions with two arguments + if (( ${#stack} < 2 )); then + print -r -- "${_line}: not enough values on stack" >&2 + _line= + continue + fi + if [[ $_line = (xy|\<\>) ]]; then + _tmp=${stack[1]} + stack[1]=${stack[2]} + stack[2]=$_tmp + _push=0 + else + eval "(( ans = ${_line}(\${stack[2]},\${stack[1]}) ))" + shift 2 stack + fi + ;; + + (${~_mathfuncs}) + # Functions with a single argument. + # This is actually a superset, but we should have matched + # any that shouldn't be in it in previous cases. + if (( ${#stack} < 1 )); then + print -r -- "${_line}: not enough values on stack" >&2 + _line= + continue + fi + eval "(( ans = ${_line}(\${stack[1]}) ))" + shift stack + ;; + + (${(kj.|.)~_userfuncs}) + # Get minimum number of arguments to user function + _n=${_userfuncs[$_line]} + if (( ${#stack} < n_ )); then + print -r -- "${_line}: not enough values ($_n) on stack" >&2 + _line= + continue + fi + _line+="(" + # least recent elements on stack are earlier arguments + for (( _i = _n; _i > 0; _i-- )); do + _line+=${stack[_i]} + (( _i > 1 )) && _line+="," + done + _line+=")" + shift $_n stack + eval "(( ans = $_line ))" + ;; + + (*) + # Treat as expression evaluating to new value to go on stack. + _matched=0 + ;; + esac + else + _matched=0 + fi + if (( ! _matched )); then + # Latest value is stored` as a string, because it might be floating + # point or integer --- we don't know till after the evaluation, and + # arrays always store scalars anyway. + # + # Since it's a string, we'd better make sure we know which + # base it's in, so don't change that until we actually print it. + if ! eval "ans=\$(( $_line ))"; then + _line= + continue + fi + # on error $ans is not set; let user re-edit _line + [[ -n $ans ]] || continue + fi + argv[_num++]=$ans + psvar[1]=$_num + (( _push )) && stack=($ans $stack) ;; esac - if [[ -n $base ]]; then - print -- $(( $base $ans )) - elif [[ $ans = *.* ]] || (( outdigits )); then - if [[ -z $forms[outform] ]]; then - print -- $(( $ans )) - else - printf "$forms[outform]\n" $outdigits $ans - fi + if (( _show_stack )); then + (( _max_stack = (_show_stack > ${#stack}) ? ${#stack} : _show_stack )) + for (( _i = _max_stack; _i > 0; _i-- )); do + printf "%3d: " $_i + zcalc_show_value ${stack[_i]} + done else - printf "%d\n" $ans + zcalc_show_value $ans fi - line= + _line= done return 0 diff --git a/Functions/Misc/zed b/Functions/Misc/zed index eb8f557ea..77d392bc3 100644 --- a/Functions/Misc/zed +++ b/Functions/Misc/zed @@ -6,6 +6,8 @@ # Use ^X^W to save, ^C to abort. # Option -f: edit shell functions. (Also if called as fned.) +setopt localoptions noksharrays + local var opts zed_file_name # We do not want timeout while we are editing a file integer TMOUT=0 okargs=1 fun bind @@ -74,7 +76,7 @@ if ((fun)) then if [[ $var = *\#\ undefined* ]] then var="$(autoload +X $1; functions -- $1)" elif [[ -z $var ]] then - var="$1() { + var="${(q-)1} () { }" fi vared -M zed -m zed-vicmd var && eval function "$var" |