mirror of
git://git.code.sf.net/p/zsh/code
synced 2025-10-27 04:40:59 +01:00
Merge of 22606: word-context style for word matching.
This commit is contained in:
parent
6c39ff7b81
commit
2e89ebbdc7
3 changed files with 284 additions and 3 deletions
48
Functions/Zle/match-word-context
Normal file
48
Functions/Zle/match-word-context
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
# See if we can extend the word context to something more specific.
|
||||
# curcontext must be set to the base context by this point; it
|
||||
# will be appended to directly.
|
||||
|
||||
emulate -L zsh
|
||||
setopt extendedglob
|
||||
|
||||
local -a worcon bufwords
|
||||
local pat tag lastword word
|
||||
integer iword
|
||||
|
||||
zstyle -a $curcontext word-context worcon || return 0
|
||||
|
||||
if (( ${#worcon} % 2 )); then
|
||||
zle -M "Bad word-context style in context $curcontext"
|
||||
return
|
||||
fi
|
||||
|
||||
bufwords=(${(z)LBUFFER})
|
||||
iword=${#bufwords}
|
||||
lastword=${bufwords[-1]}
|
||||
bufwords=(${(z)BUFFER})
|
||||
|
||||
if [[ $lastword = ${bufwords[iword]} ]]; then
|
||||
# If the word immediately left of the cursor is complete,
|
||||
# we're not on it. Either we're on unquoted whitespace, or
|
||||
# the start of a new word. Test the latter.
|
||||
if [[ -z $RBUFFER ]]; then
|
||||
# Nothing there, so not in a word.
|
||||
word=''
|
||||
elif [[ $RBUFFER[1] = [[:space:]] ]]; then
|
||||
# Whitespace, so not in a word.
|
||||
word=' '
|
||||
else
|
||||
# We want the next word along.
|
||||
word=${bufwords[iword+1]}
|
||||
fi
|
||||
else
|
||||
# We're on a word.
|
||||
word=${bufwords[iword]}
|
||||
fi
|
||||
|
||||
for pat tag in "${worcon[@]}"; do
|
||||
if [[ $word = ${~pat} ]]; then
|
||||
curcontext+=":$tag"
|
||||
return
|
||||
fi
|
||||
done
|
||||
203
Functions/Zle/match-words-by-style
Normal file
203
Functions/Zle/match-words-by-style
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
# Match words by the style given below. The matching depends on the
|
||||
# cursor position. The matched_words array is set to the matched portions
|
||||
# separately. These look like:
|
||||
# <stuff-at-start> <word-before-cursor> <whitespace-before-cursor>
|
||||
# <whitespace-after-cursor> <word-after-cursor> <whitespace-after-word>
|
||||
# <stuff-at-end>
|
||||
# where the cursor position is always after the third item and `after'
|
||||
# is to be interpreted as `after or on'. Some
|
||||
# of the array elements will be empty; this depends on the style.
|
||||
# For example
|
||||
# foo bar rod stick
|
||||
# ^
|
||||
# with the cursor where indicated will with typical settings produce the
|
||||
# elements `foo ', `bar', ` ', ` ', `rod', ` ' and `stick'.
|
||||
#
|
||||
# The style word-style can be set to indicate what a word is.
|
||||
# The three possibilities are:
|
||||
#
|
||||
# shell Words are shell words, i.e. elements of a command line.
|
||||
# whitespace Words are space delimited words; only space or tab characters
|
||||
# are considered to terminated a word.
|
||||
# normal (the default): the usual zle logic is applied, with all
|
||||
# alphanumeric characters plus any characters in $WORDCHARS
|
||||
# considered parts of a word. The style word-chars overrides
|
||||
# the parameter. (Any currently undefined value will be
|
||||
# treated as `normal', but this should not be relied upon.)
|
||||
# specified Similar to normal, except that only the words given
|
||||
# in the string (and not also alphanumeric characters)
|
||||
# are to be considered parts of words.
|
||||
# unspecified The negation of `specified': the characters given
|
||||
# are those that aren't to be considered parts of a word.
|
||||
# They should probably include white space.
|
||||
#
|
||||
# In the case of the `normal' or `(un)specified', more control on the
|
||||
# behaviour can be obtained by setting the style `word-chars' for the
|
||||
# current context. The value is used to override $WORDCHARS locally.
|
||||
# Hence,
|
||||
# zstyle ':zle:transpose-words*' word-style normal
|
||||
# zstyle ':zle:transpose-words*' word-chars ''
|
||||
# will force bash-style word recognition, i.e only alphanumeric characters
|
||||
# are considered parts of a word. It is up to the function which calls
|
||||
# match-words-by-style to set the context in the variable curcontext,
|
||||
# else a default context will be used (not recommended).
|
||||
#
|
||||
# You can override the use of word-chars with the style word-class.
|
||||
# This specifies the same information, but as a character class.
|
||||
# The surrounding square brackets shouldn't be given, but anything
|
||||
# which can appear inside is allowed. For example,
|
||||
# zstyle ':zle:*' word-class '-:[:alnum:]'
|
||||
# is valid. Note the usual care with `]' , `^' and `-' must be taken if
|
||||
# they need to appear as individual characters rather than for grouping.
|
||||
#
|
||||
# The final style is `skip-chars'. This is an integer; that many
|
||||
# characters counting the one under the cursor will be treated as
|
||||
# whitespace regardless and added to the front of the fourth element of
|
||||
# matched_words. The default is zero, i.e. the character under the cursor
|
||||
# will appear in <whitespace-after-cursor> if it is whitespace, else in
|
||||
# <word-after-cursor>. This style is mostly useful for forcing
|
||||
# transposition to ignore the current character.
|
||||
#
|
||||
# The values of the styles can be overridden by options to the function:
|
||||
# -w <word-style>
|
||||
# -s <skip-chars>
|
||||
# -c <word-class>
|
||||
# -C <word-chars>
|
||||
|
||||
emulate -L zsh
|
||||
setopt extendedglob
|
||||
|
||||
local wordstyle spacepat wordpat1 wordpat2 opt charskip wordchars wordclass
|
||||
local match mbegin mend pat1 pat2 word1 word2 ws1 ws2 ws3 skip
|
||||
local nwords MATCH MBEGIN MEND
|
||||
|
||||
local curcontext=${curcontext:-:zle:match-words-by-style}
|
||||
|
||||
autoload -U match-word-context
|
||||
match-word-context
|
||||
|
||||
while getopts "w:s:c:C:" opt; do
|
||||
case $opt in
|
||||
(w)
|
||||
wordstyle=$OPTARG
|
||||
;;
|
||||
|
||||
(s)
|
||||
skip=$OPTARG
|
||||
;;
|
||||
|
||||
(c)
|
||||
wordclass=$OPTARG
|
||||
;;
|
||||
|
||||
(C)
|
||||
wordchars=$OPTARG
|
||||
;;
|
||||
|
||||
(*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
[[ -z $wordstyle ]] && zstyle -s $curcontext word-style wordstyle
|
||||
[[ -z $skip ]] && zstyle -s $curcontext skip-chars skip
|
||||
[[ -z $skip ]] && skip=0
|
||||
|
||||
case $wordstyle in
|
||||
(shell) local bufwords
|
||||
# This splits the line into words as the shell understands them.
|
||||
bufwords=(${(z)LBUFFER})
|
||||
nwords=${#bufwords}
|
||||
wordpat1="${(q)bufwords[-1]}"
|
||||
|
||||
# Take substring of RBUFFER to skip over $skip characters
|
||||
# from the cursor position.
|
||||
bufwords=(${(z)RBUFFER[1+$skip,-1]})
|
||||
wordpat2="${(q)bufwords[1]}"
|
||||
spacepat='[[:space:]]#'
|
||||
|
||||
# Assume the words are at the top level, i.e. if we are inside
|
||||
# 'something with spaces' then we need to ignore the embedded
|
||||
# spaces and consider the whole word.
|
||||
bufwords=(${(z)BUFFER})
|
||||
if (( ${#bufwords[$nwords]} > ${#wordpat1} )); then
|
||||
# Yes, we're in the middle of a shell word.
|
||||
# Find out what's in front.
|
||||
eval pat1='${LBUFFER%%(#b)('${wordpat1}')('${spacepat}')}'
|
||||
# Now everything from ${#pat1}+1 is wordy
|
||||
wordpat1=${LBUFFER[${#pat1}+1,-1]}
|
||||
wordpat2=${RBUFFER[1,${#bufwords[$nwords]}-${#wordpat1}+1]}
|
||||
|
||||
wordpat1=${(q)wordpat1}
|
||||
wordpat2=${(q)wordpat2}
|
||||
fi
|
||||
;;
|
||||
(*space) spacepat='[[:space:]]#'
|
||||
wordpat1='[^[:space:]]##'
|
||||
wordpat2=$wordpat1
|
||||
;;
|
||||
(*) local wc
|
||||
# See if there is a character class.
|
||||
wc=$wordclass
|
||||
if [[ -n $wc ]] || zstyle -s $curcontext word-class wc; then
|
||||
# Treat as a character class: do minimal quoting.
|
||||
wc=${wc//(#m)[\'\"\`\$\(\)\^]/\\$MATCH}
|
||||
else
|
||||
# See if there is a local version of $WORDCHARS.
|
||||
wc=$wordchars
|
||||
if [[ -z $wc ]]; then
|
||||
zstyle -s $curcontext word-chars wc ||
|
||||
wc=$WORDCHARS
|
||||
fi
|
||||
if [[ $wc = (#b)(?*)-(*) ]]; then
|
||||
# We need to bring any `-' to the front to avoid confusing
|
||||
# character classes... we get away with `]' since in zsh
|
||||
# this isn't a pattern character if it's quoted.
|
||||
wc=-$match[1]$match[2]
|
||||
fi
|
||||
wc="${(q)wc}"
|
||||
fi
|
||||
# Quote $wc where necessary, because we don't want those
|
||||
# characters to be considered as pattern characters later on.
|
||||
if [[ $wordstyle = *specified ]]; then
|
||||
if [[ $wordstyle != un* ]]; then
|
||||
# The given set of characters are the word characters, nothing else
|
||||
wordpat1="[${wc}]##"
|
||||
# anything else is a space.
|
||||
spacepat="[^${wc}]#"
|
||||
else
|
||||
# The other way round.
|
||||
wordpat1="[^${wc}]##"
|
||||
spacepat="[${wc}]#"
|
||||
fi
|
||||
else
|
||||
# Normal: similar, but add alphanumerics.
|
||||
wordpat1="[${wc}[:alnum:]]##"
|
||||
spacepat="[^${wc}[:alnum:]]#"
|
||||
fi
|
||||
wordpat2=$wordpat1
|
||||
;;
|
||||
esac
|
||||
|
||||
# The eval makes any special characters in the parameters active.
|
||||
# In particular, we need the surrounding `[' s to be `real'.
|
||||
# This is why we quoted the wordpats in the `shell' option, where
|
||||
# they have to be treated as literal strings at this point.
|
||||
match=()
|
||||
eval pat1='${LBUFFER%%(#b)('${wordpat1}')('${spacepat}')}'
|
||||
word1=$match[1]
|
||||
ws1=$match[2]
|
||||
|
||||
match=()
|
||||
charskip=
|
||||
repeat $skip charskip+=\?
|
||||
|
||||
eval pat2='${RBUFFER##(#b)('${charskip}${spacepat}')('\
|
||||
${wordpat2}')('${spacepat}')}'
|
||||
|
||||
ws2=$match[1]
|
||||
word2=$match[2]
|
||||
ws3=$match[3]
|
||||
|
||||
matched_words=("$pat1" "$word1" "$ws1" "$ws2" "$word2" "$ws3" "$pat2")
|
||||
Loading…
Add table
Add a link
Reference in a new issue