Subject: "drop-in replacement" and transpose-words-match
From: Bart Schaefer <[email-masked]>
To: [email-masked]
Date: Mon, 11 Jan 2016 23:32:59 -0800
List-Id: Zsh Workers List <zsh-workers.zsh.org>

Of the eight match-words-by-style functions, transpose-words-match is the
only one that is not really a drop-in, because it adopts the paradigm of
match-words-by-style where a cursor in the middle of a word splits that
word (treating the LBUFFER part as "word before the cursor" and RBUFFER
part as "word after the cursor").

There are some useful things about this behavior, e.g., "skip-chars 1" 
where you can make "fooXbar" swap as "barXfoo", but there are also some
drawbacks -- cases where transpose-words would do something but -match
does nothing or does something very different.  So how about this:  if
the skip-chars style is set (even to zero), then keep the cursor where
it is and split the word, otherwise act like the builtin.  The subword
flavor of word-style can similarly be checked.

Also, it looks to me as though transpose-words-match assumes zsh emulation
but (unlike e.g. select-word-style) never declares it.  Also "skip" is a
local in match-words-by-style, it doesn't help to declare it here.

This still isn't perfect -- if you are on or immediately after a space
in the middle of a quoted string, for example, you'll swap around that
space.  I haven't decided on the best way to deal with that.  But this
works much better if you are at the end of a line and invoke transpose:
it swaps the two words to the left, like the builtin.

diff --git a/Functions/Zle/transpose-words-match b/Functions/Zle/transpose-words-match
index c1db310..4d2ac71 100644
--- a/Functions/Zle/transpose-words-match
+++ b/Functions/Zle/transpose-words-match
@@ -11,14 +11,23 @@
 # on X would be turned into `barXfoo' with the cursor still on the X,
 # regardless of what the character X is.
 
+emulate -L zsh
 autoload -Uz match-words-by-style
 
-local curcontext=":zle:$WIDGET" skip
+local curcontext=":zle:$WIDGET"
 local -a matched_words
 integer count=${NUMERIC:-1} neg
 
 (( count < 0 )) && (( count = -count, neg = 1 ))
 
+if [[ $WIDGET == transpose-words ]]; then
+  # default is to be a drop-in replacement, check styles for change
+  zstyle -m $curcontext skip-chars \* ||
+  zstyle -m $curcontext word-style '*subword*' ||
+  { [[ $LBUFFER[-1] != [[:space:]] && $RBUFFER[1] != [[:space:]] ||
+       -z ${RBUFFER//[[:space:]]/} ]] && zle backward-word }
+fi
+
 while (( count-- > 0 )); do
     match-words-by-style