Xref: utzoo gnu.emacs:1332 alt.religion.computers:427
Path: utzoo!utgpu!watmath!watdragon!watsol!tbray
From: tbray@watsol.waterloo.edu (Tim Bray)
Newsgroups: gnu.emacs,alt.religion.computers
Subject: Vi word hopping in emacs
Message-ID: <15516@watdragon.waterloo.edu>
Date: 30 Jul 89 17:30:23 GMT
Sender: daemon@watdragon.waterloo.edu
Reply-To: tbray@watsol.waterloo.edu (Tim Bray)
Organization: U. of Waterloo, Ontario
Lines: 57

I was editing and editing and editing this huge file, and got really mad
at Emacs' idea of where the next word was.  For this file at least, the
vi word model was appropriate.

So this is emacs, oughta be able to whip up an appropriate forward-word
and backward-word, right?  Gack.  A couple hours later, here it is.
Most of that time was spent figuring out just what vi actually *does*
(no source code).  Did people know that in vi, if a line ends with some
blanks, and you go to those blanks, and press 'w', it just beeps at
you?!?!?  Go find me the Next WORD, you braindead piece of junk!  This
package does not duplicate that silliness.  Also, it does not share vi's
ideas about the role of '_' in words, but that could be fixed.  It
actually might be a good idea to change this idea automatically when in
C-mode.  Actually the package should use a global variable called
in-word-chars or some such.
-------------------------------------------------------------------
;; vi-style word hopping
(defvar WordHop-vi-mode 't "*Use vi-style word movement if true")

(defun next-word (count) 
  "Like forward-word, but also do vi-style"
  (interactive "p")
  (if WordHop-vi-mode
      (while (not (zerop count))
	(word-hop-forward)
	(setq count (1- count)))
    (forward-word count)))

(defun prev-word (count) 
  "Like backword-word, but also do vi-style"
  (interactive "p")
  (if WordHop-vi-mode
      (while (not (zerop count))
	(word-hop-backward)
	(setq count (1- count)))
    (backward-word count)))

(defun word-hop-forward () "Hop one word, vi style"
  (cond
   ;; This behaviour is *incredibly* kludgy. Differ from vi in '_' handling
   ((looking-at "\n") (forward-char 1) (re-search-forward "[ \t]*"))
   ((looking-at "[ \t]") (re-search-forward "[ \t]*"))
   ((looking-at "[a-zA-Z0-9]")
    (re-search-forward "[a-zA-Z0-9]*")
    (if (looking-at "[ \t\n]") (word-hop-forward)))
   (t (re-search-forward "[a-zA-Z0-9 \t\n]")
      (backward-char 1)
      (if (looking-at "[ \t\n]") (word-hop-forward)))))

(defun word-hop-backward () "Back one word, vi style"
  (backward-char 1)
  (if (looking-at "[ \t\n]") (re-search-backward "[^ \t\n]"))
  (cond
   ((looking-at "\n")) ;; just leave it
   ((looking-at "[a-zA-Z0-9]")
    (re-search-backward "[^a-zA-Z0-9]") (forward-char 1))
   (t (re-search-backward "[a-zA-Z0-9 \t]") (forward-char 1))))