[gnu.emacs] Vi word hopping in emacs

tbray@watsol.waterloo.edu (Tim Bray) (07/31/89)

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))))

julian@uhccux.uhcc.hawaii.edu (Julian Cowley) (07/31/89)

In article <15516@watdragon.waterloo.edu> tbray@watsol.waterloo.edu (Tim Bray) writes:
>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).

You're not the only person who's wanted to change the behavior
of Emacs' word movement functions!  Here's my own crack at it,
but I've never considered posting it before for fear of
insulting hardcore Emacsers (I've also got the start of a
WordStar mode on the backburner).  Note that the function
vi-forward-word completely replaces the forward-word function,
so that kill-word and other Lisp editing functions work in the
same manner as vi.  The only functions that aren't affected are
those which call forward-word (really scan_words) in C (abbrev
mode, case fiddling commands, etc.).

This version is not entirely like vi, for it skips characters
until the syntax of the current character changes, and then
skips any remaining whitespace.  This was close enough to vi for
my tastes.  It also has the advantage of being modifiable through
the syntax table.

--------------- cut here ---------------
;; redefine forward-word to act more like vi

(defun vi-forward-word (count)
  "Move point forward ARG words in a manner similar to vi
(backward if ARG is negative).  Normally returns t.
If an edge of the buffer is reached, point is left there
and nil is returned."
  (interactive "p")
  (while (> count 0)
    (skip-syntax-forward (char-syntax (following-char)))
    (skip-syntax-forward ? )
    (setq count (1- count)))
  (while (< count 0)
    (skip-syntax-backward ? )
    (skip-syntax-backward (char-syntax (preceding-char)))
    (setq count (1+ count)))
  (null (or (eobp)
	    (bobp))))

(defun skip-syntax-forward (syntax)
  "Move point forward, stopping after a character does not match SYNTAX
or the end of the buffer is reached."
  (while (and (not (eobp))
	      (eq (char-syntax (following-char))
		  syntax))
    (forward-char)))

(defun skip-syntax-backward (syntax)
  "Move point backward, stopping after a character does not match SYNTAX
or the beginning of the buffer is reached."
  (while (and (not (bobp))
	      (eq (char-syntax (preceding-char))
		  syntax))
    (backward-char)))

(fset 'original-forward-word (symbol-function 'forward-word))
(fset 'forward-word 'vi-forward-word)
--------------- cut here ---------------

julian@uhccux.uhcc.hawaii.edu
uunet!nosc!humu!uhccux!julian
julian@uhccux.bitnet
University of Hawaii at Manoa

boris@prodigal.psych.rochester.edu (Boris Goldowsky) (08/01/89)

In article <15516@watdragon.waterloo.edu> tbray@watsol.waterloo.edu (Tim Bray) writes:
>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.

But we already have syntax tables which specify which characters are
part of words.  Couldn't this behavior be more easily gotten with
modify-syntax-entry?

Bng

rms@ai.mit.edu (08/03/89)

    heh! So, what's one bug more, or one bug less in vi anyway? ;-)

    oooops..I forgot, Tim, consider that a feature! :-)

Please learn not to send messages like this to info-gnu-emacs, or to
any other large mailing list which exists for a practical purpose.
You wasted the time of a large number of people.

    which is one phenomenol reason why one does not want to learn emacs.

For some users, learning Emacs is clearly not worth while.  For everyone
else, it is a matter of taste.

Arguments about taste are a waste of time.  Don't start them here!