Subject: Re: bug with camel case and delete-whole-word-match function
From: Peter Stephenson <[email-masked]>
To: [email-masked]
Date: Tue, 05 Jul 2016 11:19:00 +0100
List-Id: Zsh Workers List <zsh-workers.zsh.org>

On Tue, 05 Jul 2016 09:08:46 +0000 (UTC)
Oliver Kiddle <[email-masked]> wrote:
> I took a look at the word style widgets in the contributed functions
> with a view to perhaps adding a vim style text object based on them.
> 
> delete-whole-word-match appears to be the most useful example because
> it operates on a whole word rather than just forward of
> backward. Unfortunately, it doesn't seem to work too well for the
> subword (camel case) word style.
> 
> The presence of white space before the cursor position is used as an
> indication that the cursor is on the first character of the word but
> with camel case, there isn't necessarily any whitespace.
>
> On the first character of a camel case word, both the current and
> previous word are deleted.
>
> On the last letter, it deletes up to the next real whitespace.

These are related, but the first one's much harder to fix.

If you have "ThisIsSomeWords" and you're on the "e" then the relevant
bits of the split are "Som" (word before cursor) and "e" (word after
cursor), with whitespace before and after cursor empty.  The function
didn't recognise that "me" should be considered a word segment because
you had started the word at "So"; the reason it only happened on the
last character was because it previously assumed that must be the start
of a new word and this didn't match because "Words" followed
immediately.  That's easy enough to fix or at least improve --- detect
what we've been told is the start of a word is a character that normally
wouldn't be in subword mode when there's no white space around.

However, if you're on the "S", you get "Is" before and "Some" after.
Again there's no white space, so there's nothing to indicate to the
calling function that these are two separate words rather than bits of
the same word.  So I think we'd need to add some extra signalling from
match-words-by-style to indicate "I'm at a word start" whether or not
there's white space, which needs some thinking about.

diff --git a/Functions/Zle/match-words-by-style b/Functions/Zle/match-words-by-style
index 54e019d..1a3e78c 100644
--- a/Functions/Zle/match-words-by-style
+++ b/Functions/Zle/match-words-by-style
@@ -202,7 +202,7 @@ if [[ $wordstyle = *subword* ]]; then
   # followed by a lower case letter, or an upper case letter at
   # the start of a group of upper case letters.  To make
   # it easier to be consistent, we just use anything that
-  # isn't an upper case characer instead of a lower case
+  # isn't an upper case character instead of a lower case
   # character.
   # Here the initial "*" will match greedily, so we get the
   # last such match, as we want.
@@ -237,6 +237,12 @@ if [[ $wordstyle = *subword* ]]; then
 	  -n $match[2] ]]; then
     # Yes, so the last one is new word boundary.
     (( epos = ${#match[1]} - 1 ))
+    # Otherwise, are we in the middle of a word?
+    # In other, er, words, we've got something on the left with no
+    # white space following and something that doesn't start a word here.
+  elif [[ -n $word1 && -z $ws1 && -z $ws2 && \
+    $word2 = (#b)([^${~subwordrange}]##)* ]]; then
+    (( epos = ${#match[1]} ))
     # Otherwise, do we have upper followed by non-upper not
     # at the start?  Ignore the initial character, we already
     # know it's a word boundary so it can be an upper case character