1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
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 (( ${+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]:-0}+1
else
return 0
fi
extant_hooks+=("${i}:${fn}")
zstyle -- "$hook" widgets "${extant_hooks[@]}"
if (( ! ${+widgets[$fn]} )); then
autoload "${autoopts[@]}" -- "$fn"
zle -N -- "$fn"
fi
if (( ! ${+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.
|