4

I use Emacs mostly for writing text not code. This involves a great deal of selecting, deleting and editing, as one would expect. By default if I move forward by a word in Emacs, Emacs ignores basic punctuation such as . , ; ? ! ' ". This is especially problematic for me when I want to copy or delete text because it involves using both M-f and then C-f. What I would like is for Emacs to move forward to the empty space AFTER a punctuation mark, so that the punctuation mark after a word becomes part of the selection. If one moves forward by sentences, Emacs does this naturally, but not when one moves by words, and by implication by phrases.

For example, in the sentence,

Bob went to the store, in a joyful, happy mood.

If I wanted to delete the words "to the store," (including the comma) I would like to simply use the mark and press M-f 3 times without also having to then press C-f.

I have tried some solutions but none of them really worked for me, including,

  • evil-mode (I like Vim's behaviour when it comes to moving by words, but do not wish to change my whole workflow to evil)
  • forward-same-syntax (movements are too refined)
  • the package syntax-subword (changes the Emacs default too radically)

Please note that my default file mode is org-mode, not text-mode.

  • why not evil-mode? – Toothrot Nov 18 at 12:11
  • 1
    How about using the subword.el built-in library? You can define your own functions and assign them the subword-forward-function and subword-backward-function variables. I wrote my own forward/backward function several years ago, and I've modified it since then .... here is a link to the old version .... stackoverflow.com/questions/18675201/… See also the function forward-symbol -- there are other functions in subr.el for movement that you may wish to have a look at. – lawlist Nov 18 at 15:31
4

Two rules to take into account when solving your problem:

  1. Do not persistently mess up the syntax table if you do not see through all the consequences. It is used for many tasks.
  2. Do not advice forward-word to solve your problem. It is also used for many tasks you do not aprehend at the first look.

But, what you can do is rebinding the keys for forward-char and backward-char. The following code defines a new minor mode forward-w.-mode and switches it automatically on in new org-mode buffers. In forward-w.-mode the keys for forward-word and backward-word are rebound to forward-w. and backward-w.. These functions skip punctuation chars after running forward-word/backward-char. The mode uses as much as possible from forward-char and backward-char to keep stuff like subword-mode and text field motion working.

(defun forward-w. (&optional arg)
  "Move point forward ARG words (backward if ARG is negative).
In constrast to `forward-word' also skip attached to words punctuation."
  (interactive "P")
  (cl-flet
      ((move-f (_forward-word _skip-syntax-forward _char-after)
           (let (c)
         (funcall _forward-word)
         (while
             (and
              (setq c (funcall _char-after))
              (eq (char-syntax c) ?.)
              (progn
            (funcall _skip-syntax-forward ".")
            (when (and
                   (setq c (funcall _char-after))
                   (memq (char-syntax c) '(?_ ?w)))
              (funcall _forward-word))
            (and
             (setq c (funcall _char-after))
             (memq (char-syntax c) '(?_ ?w ?.)))))))))
    (cl-macrolet
    ((move (forward after)
           `(move-f
         ,@(mapcar (lambda (stuff)
                 (symbol-function (intern-soft (apply #'concat stuff))))
               `((,forward "-word")
                 ("skip-syntax-" ,forward)
                 ("char-" ,after))))))
      (when (null arg)
    (setq arg 1))
      (cond
       ((eq arg 1)
    (move "forward" "after"))
       ((eq arg -1)
    (move "backward" "before"))
       ((> arg 1)
    (dotimes (i arg)
      (forward-w. 1)))
       ((< arg -1)
    (dotimes (i (- arg))
      (forward-w. -1)))))))

(defun backward-w. (&optional arg)
  "Do the same as `forward-w.' with negated ARG."
  (interactive "P")
  (unless arg
    (setq arg 1))
  (forward-w. (- arg)))

(define-minor-mode forward-w.-mode
  "Skip punctuation with `forward-word' and `backward-word'."
  nil
  " w."
  (let ((map (make-sparse-keymap)))
    (define-key map [remap forward-word] #'forward-w.)
    (define-key map [remap backward-word] #'backward-w.)
    map))

(add-hook 'org-mode-hook #'forward-w.-mode)
2

For the specific example you cite, I would execute M-z ,, where M-z is bound to zap-to-char. I don't know how well this would generalize to your daily usage though.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.