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
| | #autoload
# Intended to be called during accept-line or in zle-line-finish, but can
# be called by any widget to apply correction to all words in $BUFFER or
# used as a widget itself
# Compare modify-current-argument
setopt localoptions noksharrays multibyte norecexact
zmodload zsh/complist || return 1
local -a reply cmdline
local key REPLY REPLY2 MENUSELECT
integer pos posword poschar
unset MENUSELECT # Bug with no-select plus yes=2 below
local curcontext="${curcontext:-}"
local widget="correct-${${WIDGET/correct-all-words/}:-all-words}"
if [[ -z "$curcontext" ]]; then
curcontext="${widget}:::"
else
curcontext="${widget}:${curcontext#*:}"
fi
local mycontext="${curcontext}"
# This breaks out of read-from-minibuffer if only the original matches
local shown_original
zstyle -e ":completion:${widget}:*" original \
'if (( compstate[nmatches] == 0 )); \
then reply=(false);
elif [[ ${compstate[unambiguous]} = ${key} ]]; \
then shown_original=unambiguous; zle -U n; reply=(false); \
elif [[ -z ${shown_original} ]];
then shown_original=original; reply=(true);
else reply=(false); fi'
# Would be nice to make these conditional, but it's hard to test for
# these specifically if a more general context has the style defined
zstyle ":completion:${widget}:*" menu no-select yes=2
zstyle ":completion:${widget}:*" group-name ''
zstyle ":completion:${widget}:*" group-order original corrections
zstyle ":completion:${widget}:*" show-ambiguity true
zstyle ":completion:${widget}:*" show-completer true
zstyle ":completion:${widget}:*" accept-exact false
# Keep completion functions out of the results
zstyle ":completion:${widget}:*" ignored-patterns '_*'
# This shows earliest the words with the fewest necessary corrections
zstyle ":completion:${widget}:*" sort false
# Overload fake description to produce a prompt for multiple corrections
zstyle -e ":completion:${widget}:*:original" fake \
'if (( compstate[nmatches] > 1 )) && \
[[ -z ${shown_original} ]] ; \
then bindkey -M correctall y _correct_word; \
shown_original=fake-original; \
reply=("${key//:/\\:}:TAB to choose, ENTER to accept, n to skip"); \
fi'
zstyle -e ":completion:${widget}:*:corrections" fake \
'if [[ ${shown_original} = wanted ]] || \
( (( compstate[nmatches] > 0 )) && \
[[ -z ${shown_original} ]] ); \
then bindkey -M correctall y _correct_word; \
shown_original=fake-corrections; \
reply+=("${key//:/\\:}:TAB to choose, ENTER to accept, n to skip"); \
fi'
# There's no good semantics for 'y' when there are multiple possible
# corrections. Left as $'\t\n' it'll skip ahead after a correction is
# chosen from the list. Changed to .accept-line it becomes equivalent
# to 'n' if no choice has been made yet. The above makes it cycle the
# menu, but maybe it would be better just to have it do nothing?
autoload -Uz split-shell-arguments read-from-minibuffer mkshadow
split-shell-arguments
[[ ${#reply} -lt 2 ]] && return 1
(( posword = REPLY, poschar = REPLY2 ))
cmdline=("${reply[@]}") # In case something else uses $reply ...
bindkey -N correctall
bindkey -M correctall $'\t' _correct_word
bindkey -M correctall ' ' _correct_word
for key in n a e \! $'\n' $'\r'
do
bindkey -M correctall $key .accept-line
done
bindkey -M correctall u .undo
bindkey -M correctall '^_' .undo
bindkey -M correctall -s '^G' e
bindkey -M correctall -s '^U' a # Should copy from main XXX
bindkey -M correctall -s y $'\t\n'
# Work around a bug with recursive-edit from zle-line-finish
[[ $WIDGET = zle-line-finish ]] && zle recursive-edit # Fails
local lmini rmini
{
mkshadow -s all-words _original_file _correct_word
# Force unedited original to precede _path_files additions.
# Otherwise the "original" style above handles this.
function _original_file {
[[ ${key} = */* ]] && shown_original=wanted
return 1 # Force call to _correct
}
# This is to avoid having _correct_word stomp on $curcontext,
# plus break out of read-from-minibuffer when nothing matches
function _correct_word {
local shown_original ret REPLY
_main_complete _original_file _correct
ret=$?
[[ ${compstate[nmatches]} -eq 0 ]] && zle -U n
return ret
}
# "Real" words from reply[2], reply[4], etc., see split-shell-arguments
for pos in {2..${#cmdline}..2}
do
# Don't try to correct numbers and non-syntax punctuation
[[ ${cmdline[pos]} = *[A-Za-z]* ]] || continue
{
key=${cmdline[pos]}
curcontext="${mycontext}"
# Can't use -K KEYMAP because read-from-minibuffer always resets
bindkey -A main llatcerroc
bindkey -A correctall main
# lmini="${PREBUFFER}${(j::)cmdline[1,pos-1]}" # Too much?
lmini=${(j::)cmdline[1,pos-1]}
rmini=${(j::)cmdline[pos+1,-1]}
zle -U $'\t' # Start minibuffer in completion
read-from-minibuffer "Correct $key [nyae!]: " "${lmini}${key}" "${rmini}"
REPLY=${${REPLY#${lmini}}%${rmini}}
} always { bindkey -A llatcerroc main }
case ${KEYS} in
(a) zle send-break; break;;
(n) continue;;
(y|$'\n'|$'\r') cmdline[pos]=${REPLY:-${cmdline[pos]}};;
(e) [[ ${WIDGET} = zle-line-finish ]] && {
print -z "${(j::)cmdline}"
cmdline=()
(( posword = 0, poschar = 0 ))
}
;&
(*) break;;
esac
done
} always {
rmshadow
zle -R -c
}
BUFFER="${(j::)cmdline}"
CURSOR=$(( ${#${(j::)cmdline[1,posword-1]}} + poschar ))
[[ "${KEYS}" = \! ]] && zle .accept-line
|