21

As the title says, how does one change the behaviour of emacs forward-word function? For example, suppose [] is the cursor. Then:

my $abs_target_path[]= abs_path($target);
<M-f>
my $abs_target_path = abs[_]path($target);

I know I could just use M-f M-b but as far as I'm concerned, that shouldn't be necessary and I'd like to change it. In particular, I want two things:

  1. When I press M-f, I want to go to the first character of the next word regardless of whether the point is within a word, within a group of spaces or somewhere else.
  2. Customize word-characters on a mode-by-mode basis. After all, moving around in CPerl mode is different than, say, TeX mode.

So, in the above example, item 1 would have the cursor would move to the 'a' (and the point to it's left) after hitting M-f. Item 2 would allow me to define underscores and sigils as word characters.

gvkv
  • 1,894
  • 2
  • 22
  • 34
  • for the underscore part, see http://stackoverflow.com/questions/1545851/how-to-make-forward-word-backward-word-treat-underscore-as-part-of-a-word/1545934 – Bahbar Nov 20 '09 at 15:55

4 Answers4

34

Try:

(require 'misc)

Then use M-x forward-to-word and see if it does what you want. You can then rebind M-f, etc.

To make the _ not a word separator (i.e. make it a word constituent) for C & C++ mode, you would do this:

(modify-syntax-entry ?_ "w" c-mode-syntax-table)
(modify-syntax-entry ?_ "w" c++-mode-syntax-table)

For more information on syntax tables, read this wiki page. Syntax tables are generally named like tex-mode-syntax-table and cperl-mode-syntax-table.

ideasman42
  • 42,413
  • 44
  • 197
  • 320
scottfrazer
  • 17,079
  • 4
  • 51
  • 49
  • Is there a specific version of (x)Emacs in which `(require 'misc)` would move the `_` character to whitespace in the syntax table? I had to do `(modify-syntax-entry ?_ "-")` to get this to work. – sameers Sep 25 '15 at 21:54
  • I am now using GNU Emacs 24.5 on OSX, and I don't know if I had tried this earlier, but adding that `modify-syntax-entry` call to my `.emacs` doesn't set the table entry. It works when I type it in the mini-buffer. Any idea how to make it work in all modes? `(modify-syntax-entry ?_ "w" standard-syntax-table)` says that `standard-syntax-table` is a void-definition symbol. – sameers Mar 11 '16 at 23:02
  • This can lead to serious lag issues in `24.5.1`. – Zelphir Kaltstahl Feb 23 '17 at 19:26
3

See forward-same-syntax function also. probably this is you needed to base on.

Alex
  • 31
  • 2
  • Thanks a lot! I was not aware of this and it works great! I found this which bundles all together - move/kill forward/backward: https://github.com/nixme/.emacs.d/blob/master/core/init-keybindings.el – fikovnik Apr 06 '17 at 11:41
2

I have a minor mode that changes word-based commands to operate on syntax changes (and also CamelCaseSubwords). It may be a bit too fine-grained for some tastes, but I find I basically ever use single character movement anymore.

https://bitbucket.org/jpkotta/syntax-subword

jpkotta
  • 9,237
  • 3
  • 29
  • 34
0

I wanted to copy the behavior of my previous editor thus needed a bit more control, so here's my take on it:

(defun my-syntax-class (char)
  "Return ?s, ?w or ?p depending or whether CHAR is a white-space, word or punctuation character."
  (pcase (char-syntax char)
      (`?\s ?s)
      (`?w ?w)
      (`?_ ?w)
      (_ ?p)))

(defun my-forward-word (&optional arg)
  "Move point forward a word (simulate behavior of Far Manager's editor).
With prefix argument ARG, do it ARG times if positive, or move backwards ARG times if negative."
  (interactive "^p")
  (or arg (setq arg 1))
  (let* ((backward (< arg 0))
         (count (abs arg))
         (char-next
          (if backward 'char-before 'char-after))
         (skip-syntax
          (if backward 'skip-syntax-backward 'skip-syntax-forward))
         (skip-char
          (if backward 'backward-char 'forward-char))
         prev-char next-char)
    (while (> count 0)
      (setq next-char (funcall char-next))
      (loop
       (if (or                          ; skip one char at a time for whitespace,
            (eql next-char ?\n)         ; in order to stop on newlines
            (eql (char-syntax next-char) ?\s))
           (funcall skip-char)
         (funcall skip-syntax (char-to-string (char-syntax next-char))))
       (setq prev-char next-char)
       (setq next-char (funcall char-next))
       ;; (message (format "Prev: %c %c %c Next: %c %c %c"
       ;;                   prev-char (char-syntax prev-char) (my-syntax-class prev-char)
       ;;                   next-char (char-syntax next-char) (my-syntax-class next-char)))
       (when
           (or
            (eql prev-char ?\n)         ; stop on newlines
            (eql next-char ?\n)
            (and                        ; stop on word -> punctuation
             (eql (my-syntax-class prev-char) ?w)
             (eql (my-syntax-class next-char) ?p))
            (and                        ; stop on word -> whitespace
             this-command-keys-shift-translated ; when selecting
             (eql (my-syntax-class prev-char) ?w)
             (eql (my-syntax-class next-char) ?s))
            (and                        ; stop on whitespace -> non-whitespace
             (not backward)             ; when going forward
             (not this-command-keys-shift-translated) ; and not selecting
             (eql (my-syntax-class prev-char) ?s)
             (not (eql (my-syntax-class next-char) ?s)))
            (and                        ; stop on non-whitespace -> whitespace
             backward                   ; when going backward
             (not this-command-keys-shift-translated) ; and not selecting
             (not (eql (my-syntax-class prev-char) ?s))
             (eql (my-syntax-class next-char) ?s))
            )
         (return))
       )
      (setq count (1- count)))))

(defun delete-word (&optional arg)
  "Delete characters forward until encountering the end of a word.
With argument ARG, do this that many times."
  (interactive "p")
  (delete-region (point) (progn (my-forward-word arg) (point))))

(defun backward-delete-word (arg)
  "Delete characters backward until encountering the beginning of a word.
With argument ARG, do this that many times."
  (interactive "p")
  (delete-word (- arg)))

(defun my-backward-word (&optional arg)
  (interactive "^p")
  (or arg (setq arg 1))
  (my-forward-word (- arg)))

(global-set-key (kbd "C-<left>") 'my-backward-word)
(global-set-key (kbd "C-<right>") 'my-forward-word)
(global-set-key (kbd "C-<delete>") 'delete-word)
(global-set-key (kbd "C-<backspace>") 'backward-delete-word)
Vladimir Panteleev
  • 24,651
  • 6
  • 70
  • 114