From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 18678 invoked by alias); 13 Jun 2016 01:44:41 -0000 Mailing-List: contact zsh-workers-help@zsh.org; run by ezmlm Precedence: bulk X-No-Archive: yes List-Id: Zsh Workers List List-Post: List-Help: X-Seq: 38670 Received: (qmail 14213 invoked from network); 13 Jun 2016 01:44:39 -0000 X-Spam-Checker-Version: SpamAssassin 3.4.1 (2015-04-28) on f.primenet.com.au X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,T_DKIM_INVALID autolearn=ham autolearn_force=no version=3.4.1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=brasslantern-com.20150623.gappssmtp.com; s=20150623; h=from:message-id:date:to:subject:mime-version; bh=+zCrLCmXjT2AIRNIQBD5df9APzYjN2Ifmcu5PsAIgrw=; b=lTXY43pUC0OwW+NRDAtiA7qxvfCQXRVm6+bBavAh+kipqor+VHuQvRqKsaTQyiBVUl 4WtwwSzRhbdde0r7halg1XMuhl/lCayGk848yJ93XP5YaI1CE0OGGUDhTwzt+l2B0jBU LvS9GX0Pbfl91nrengjtnTAuuui0u3w7fBgRE0FfUrsqEK97Rq0eJDMkxW/mgCgvrrlw tXyxDnai+yCWK/pSh0APKUhkm7uE00RbRj8nlYSEjNFrlbrpy95F4NXr8x6HkUZNKfJU VxJaWTwuXB081x6D4A14aJcGCpv/StkX3dw2VWVdgR0R5gnvQ/GElnNUEE3kD4jDGiZr mXVg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:message-id:date:to:subject:mime-version; bh=+zCrLCmXjT2AIRNIQBD5df9APzYjN2Ifmcu5PsAIgrw=; b=ih/CI7y/QUQ33j3WHM/QNdjJjdXkoK8oQu+MfqIvvt9aITdreIbGnOUtrS7lJt58Mq vJCrR0X2ZVQMOjhpPeBEqpY4PJoEdVenaKMeGTYD3VcI46z1WyPqHlfC1UKOSj2vcuWm NV7fn1G9ormxYzZkHxH92gYOYLG1GflP9HcrsTnGF/oJL1/H0V8uJhuhy3Zi9RuO9a+U SfzssD2qi5nDpFZOoqNkllMbwwT+jSN3VA8TGQvrsuXax7rdO9PtFo2nF/8I6PTFfb0X o8kVQ0Y6Ki+8Ng+85sxgydywtCaqylL3a59NMfNJThqnuFE8J9SeShKZ5OYoxsvM11B7 Qv7A== X-Gm-Message-State: ALyK8tIW3PEKJOOMMxPsnwol+LLY6SaVSUaot42vP3G+V4+UWqrcMeEIvPrTqpTduI36qA== X-Received: by 10.66.152.164 with SMTP id uz4mr18845246pab.9.1465782276456; Sun, 12 Jun 2016 18:44:36 -0700 (PDT) From: Bart Schaefer Message-Id: <160612184453.ZM11316@torch.brasslantern.com> Date: Sun, 12 Jun 2016 18:44:53 -0700 X-Mailer: OpenZMail Classic (0.9.2 24April2005) To: zsh-workers@zsh.org Subject: [PATCH] add-zle-hook-widget MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii The idea and ultimate implementation are the same as in my "crude sketch" but the function has been rewritten to parallel add-zsh-hook very closely. This means among other things that you can only add one hook widget per call to add-zle-hook-widget. Also as briefly discussed, I removed add-zle-hook-function entirely and made it a requirement that all the hooks be actual widgets. However, as a parallel to add-zsh-hook, the given name is both defined as a widget and marked as an autoload function if the widget doesn't yet exist. There are probably still bugs in there somewhere, but all worked as I expected in a few simple tests. I have not made much attempt to make this backward-compatible with past zsh versions, but I think the only conflict is with the arraname+=(value list) appending syntax. Patch includes repairs to the add-zsh-hook documentation. diff --git a/Doc/Zsh/contrib.yo b/Doc/Zsh/contrib.yo index 923bb29..dd643a1 100644 --- a/Doc/Zsh/contrib.yo +++ b/Doc/Zsh/contrib.yo @@ -292,11 +292,11 @@ cindex(hook function utility) startitem() findex(add-zsh-hook) -item(tt(add-zsh-hook) [ tt(-dD) ] [ tt(-Uzk) ] var(hook) var(function))( +item(tt(add-zsh-hook) [ tt(-L) | tt(-dD) ] [ tt(-Uzk) ] var(hook) var(function))( Several functions are special to the shell, as described in the section ifnzman(Special Functions, noderef(Functions))\ ifzman(SPECIAL FUNCTIONS, see zmanref(zshmisc)), -in that they are automatic called at a specific point during shell execution. +in that they are automatically called at specific points during shell execution. Each has an associated array consisting of names of functions to be called at the same point; these are so-called `hook functions'. The shell function tt(add-zsh-hook) provides a simple way of adding or @@ -312,6 +312,9 @@ var(function) is name of an ordinary shell function. If no options are given this will be added to the array of functions to be executed in the given context. +If the option tt(-L) is given, the current values for the hook arrays +are listed with tt(typeset). + If the option tt(-d) is given, the var(function) is removed from the array of functions to be executed. @@ -323,6 +326,55 @@ The options tt(-U), tt(-z) and tt(-k) are passed as arguments to tt(autoload) for var(function). For functions contributed with zsh, the options tt(-Uz) are appropriate. ) +findex(add-zle-hook-widget) +item(tt(add-zle-hook-widget) [ tt(-L) | tt(-dD) ] [ tt(-Uzk) ] var(hook) var(widgetname))( +Several widget names are special to the line editor, as described in the section +ifnzman(Special Widgets, noderef(Zle Widgets))\ +ifzman(Special Widgets, see zmanref(zshzle)), +in that they are automatically called at specific points during editing. +Unlike function hooks, these do not use a predefined array of other names +to call at the same point; the shell function tt(add-zle-hook-widget) +maintains a similar array and arranges for the special widget to invoke +those additional widgets. + +var(hook) is one of tt(isearch-exit), tt(isearch-update), +tt(line-pre-redraw), tt(line-init), tt(line-finish), tt(history-line-set), +or tt(keymap-select), corresponding to each of the special widgets +tt(zle-isearch-exit), etc. + +var(widgetname) is the name of a ZLE widget. If no options are given this +is added to the array of widgets to be invoked in the given hook context. +Note that the hooks are called as widgets, that is, with +`tt(zle var(widgetname) -Nw)' rather than as a function call. + +The arrays of var(widgetname) are maintained in several tt(zstyle) +contexts, one for each var(hook) context, with a style of `tt(widgets)'. +If the tt(-L) option is given, this set of styles is listed with +`tt(zstyle -L)'. These styles may be updated directly with tt(zstyle) +commands, but the special widgets that refer to the styles are created +only if tt(add-zle-hook-widget) is called to add at least one widget. + +If the option tt(-d) is given, the var(widgename) is removed from +the array of widgets to be executed. + +If the option tt(-D) is given, the var(widgetname) is treated as a pattern +and any matching names of widgets are removed from the array. + +If var(widgetname) does not name an existing widget when added to the +array, it is assumed that a shell function also named var(widgetname) is +meant to provide the implementation of the widget. This name is therefore +marked for autoloading, and the options tt(-U), tt(-z) and tt(-k) are +passed as arguments to tt(autoload) as with tt(add-zsh-hook). The +widget is also created with `tt(zle -N var(widgetname))' to cause the +corresponding function to be loaded the first time the hook is called. + +In addition, var(widgetname) may be of the form var(index)tt(:)var(name). +In this case var(index) is an integer which determines the order in +which the widget var(name) will be called relative to other widgets in +the array. Widgets having the same var(index) are called in unspecified +order, and all widgets declared with an index are called before any +widgets that have no index. +) enditem() texinode(Recent Directories)(Other Directory Functions)(Utilities)(User Contributions) diff --git a/Functions/Zle/add-zle-hook-widget b/Functions/Zle/add-zle-hook-widget new file mode 100644 index 0000000..eeb0191 --- /dev/null +++ b/Functions/Zle/add-zle-hook-widget @@ -0,0 +1,140 @@ +# 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 not +# required). +# +# WIDGET may be of the form INDEX:NAME in which case the INDEX determines +# the order in which the widget executes relative to other hook widgets. +# +# With -d, remove the widget from the hook instead; delete the hook +# variable if it is empty. +# +# -D behaves like -d, but pattern characters are active in the +# widget name, 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. + +emulate -L zsh + +# Setup - create the base functions for hook widgets that call the others + +zmodload zsh/parameter || { + print -u2 "Need parameter module for zle hooks" + return 1 +} + +local -a hooktypes=( isearch-exit isearch-update + line-pre-redraw line-init line-finish + history-line-set keymap-select ) +# Stash in zstyle to make it global +zstyle zle-hook types $hooktypes + +for hook in $hooktypes +do + function zle-$hook { + local -a hook_widgets + local hook + # Values of these styles look like number:name + # and we run them in number order + # $funcstack is more reliable than $0 + # Also, ksh_arrays is annoying + emulate zsh -c 'zstyle -a $funcstack[2] widgets hook_widgets' + for hook in "${@${(@on)hook_widgets}#*:}" + do + zle "$hook" -Nw || return + done + return 0 + } +done + +# 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: $0 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 )) + + if (( list )); then + zstyle -L "zle-(${1:-${(@j:|:)hooktypes}})" widgets + return $? + elif (( help || $# != 2 || ${hooktypes[(I)${1#zle-}]} == 0 )); then + print -u$(( 2 - help )) $usage + return $(( 1 - help )) + fi + + local -aU extant_hooks + local hook="zle-${1#zle-}" + 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 + zstyle -g extant_hooks "$hook" widgets + extant_hooks+=("$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" + fi + fi +} + +# Handle zsh autoloading conventions +if [[ "$zsh_eval_context" = *loadautofunc && ! -o kshautoload ]]; then + add-zle-hook-widget "$@" +fi