Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: PATCH: enhanced word widgets

"Bart Schaefer" wrote:
> I want the definition of a "word" to be context-sensitive.  For example:
> If the cursor is within a shell word that contains a "/" character, then
> I want "ZLE words" to be pathname components (and transpose-words should
> transpose around the nearest "/" either under the cursor or to the left);
> but if the cursor is between two shell words, then I want "words" to be
> shell words, e.g., pathnames including the slashes.

OK, I think this does pretty much what you want.  (Luckily I was working
on a scatternet scheduling problem, so I was easily diverted.)  Zefram
is quite right but his point leads to a complete rewrite of the parser
rather than a quick ten-minute rewrite of the shell function...

No documentation yet and it won't be committed until we decide what the
neatest way of doing this is.

Set the style word-context to pairs of words: a pattern, and a
subcontext.  The function then tests against the patterns in order.

- If the cursor is in a command argument the pattern will be tested
  against the word (with any quotes stripped, so foo, \foo, 'foo' "foo"
  will all give you foo to match against --- I can remove this, but it
  seemed more convenient with it).
- If the cursor is in whitespace between words, it will be tested
  against the whitespace character under the cursor.
- If it is at the end of the buffer, it will be tested against the empty
- For reasons best known to the lexical analyser, the pattern is tested
  against `;' if you are at the end of a line when there are more to
  follow (unfortunately due to the unquoting this looks the same as a
  real semicolon in quotes).  I suppose I could sensibly trap this
  before the unquoting and turn it into a newline.

If a pattern matches, the corresponding subcontext is appended (with the
traditional colon) to the end of the current context.

I tried this out with the context array:

zstyle ':zle:*' word-context "[[:space:]]" whitespace "*/*" \
    filename "" end "*" other

(though all you need for the suggested use are the `filename' element
plus a suitable default and `other' is completely redundant (it's an
insignificant other)) and

zstyle ':zle:transpose-words:whitespace' word-style shell
zstyle ':zle:transpose-words:filename' word-style normal
zstyle ':zle:transpose-words:filename' word-chars ''

so if you are in a filename --- the character under the cursor is part
of a word which includes a slash --- you get bash-style behaviour, if
you are in whitespace, you get shell-word behaviour, and if you are in
any other word you get the default behaviour.

Index: Functions/Zle/match-words-by-style
RCS file: /cvsroot/zsh/zsh/Functions/Zle/match-words-by-style,v
retrieving revision 1.1
diff -u -r1.1 match-words-by-style
--- Functions/Zle/match-words-by-style	28 Mar 2003 11:34:30 -0000	1.1
+++ Functions/Zle/match-words-by-style	28 Mar 2003 18:46:56 -0000
@@ -62,12 +62,47 @@
 emulate -L zsh
 setopt extendedglob
-local wordstyle spacepat wordpat1 wordpat2 opt charskip
+local wordstyle spacepat wordpat1 wordpat2 charskip
 local match mbegin mend pat1 pat2 word1 word2 ws1 ws2 ws3 skip
+local MATCH MBEGIN MEND pattern subcontext
+local -a wordcontext
 if [[ -z $curcontext ]]; then
     local curcontext=:zle:match-words-by-style
+if zstyle -a $curcontext word-context wordcontext; then
+    # Pretend line so far is a complete set of words...
+    bufwords=(${(z)LBUFFER})
+    word1=${#bufwords}
+    # Word either before or including cursor
+    pat1=$bufwords[-1]
+    # If we get more words by adding in the cursor, we're at the
+    # start of the next word.
+    wordpat1="$LBUFFER$RBUFFER[1]"
+    bufwords=(${(z)wordpat1})
+    word2=${#bufwords}
+    pat2=$bufwords[-1]
+    bufwords=(${(z)BUFFER})
+    if (( word1 != word2 )); then
+	# At start of word2
+	wordpat2=${(Q)bufwords[$word2]}
+    elif [[ ${#bufwords[$word1]} > ${#pat1} ]]; then
+	# Word is longer when including whole buffer.
+	# How beautiful life is now you're in the word.
+	# Strip quotes in order to test actual argument
+	# (except we haven't done expansion on it).
+	wordpat2=${(Q)bufwords[$word1]}
+    else
+	# We are not in the word.  Just use character at cursor position.
+	wordpat2=$RBUFFER[1]
+    fi
+    for pattern subcontext in "${wordcontext[@]}"; do
+	if [[ $wordpat2 = ${~pattern} ]]; then
+	    curcontext+=":$subcontext"
+	    break
+	fi
+    done
 zstyle -s $curcontext word-style wordstyle

Peter Stephenson <pws@xxxxxxx>                  Software Engineer
CSR Ltd., Science Park, Milton Road,
Cambridge, CB4 0WH, UK                          Tel: +44 (0)1223 692070

The information transmitted is intended only for the person or
entity to which it is addressed and may contain confidential 
and/or privileged material. 
Any review, retransmission, dissemination or other use of, or
taking of any action in reliance upon, this information by 
persons or entities other than the intended recipient is 
If you received this in error, please contact the sender and 
delete the material from any computer.

Messages sorted by: Reverse Date, Date, Thread, Author