Delete the Next Word or the Current Selection in Emacs with the Same Shortcut

When you delete a character or the current selection, you usually hit C-d (Control + d) in Emacs.

I got into the habit of revising drafts by deleting whole words, and the M-d (Meta + d) key combination is super useful for that. It also works into the other direction with backspace. It’s essentially the same effect of +backspace everywhere else in macOS.

When I delete words with these shortcuts for a while, I get into the habit of reaching for that key combo a lot. Deleting and rewriting words is often times faster than carefully positioning the insertion point inside an existing word to fix it, especially if you’re accustomed to 10-finger-typing.

Problems arose whenever I wanted to delete a selection, though: when I want to select a couple of words, I enter the “selection mode” and then expand the selection forward a couple of words using M-f (Meta + f, for “forward”); it’s like +rightarrow on macOS. With the thumb already on the Meta key, I usually just leave it there, press down, and hit “d” to delete – but by default, emacs would delete the next word after the end of the selection instead of the selection itself. This bummed me every single time.

So here’s a fix.

The default M-d key binding calls the interactive function kill-word:

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

It kills (aka deletes and puts into the kill-ring/clipboard) everything from the current point up to the next word, determined by forward-word. The actual call forwards the numerical argument from the key binding, so you can press C-u 10 M-d and delete the next 10 words this way; that’s what the progn call and stuff is for.

Now I want to add an option to delete the current selection if there is any; if not, proceed with the default behavior. My simple wrapper is this:

(defun ct/kill-word (arg)
  "Kill characters forward until encountering the end of a word, or the current selection."
  (interactive "p")
  (if (use-region-p)
      (delete-active-region 'kill)
      (kill-word arg)))
(global-set-key (kbd "M-d") 'ct/kill-word)
  • use-region-p is a test if there is currently any selection active;
  • delete-active-region deletes the selected range

If you pass any non-nil argument to delete-active-region, you toggle a flag where the removed content is put into the kill ring (aka clipboard) instead of just being discarded. The value could be anything, really anything: t for “true”, the numerical value 1, an empty list (), a non-empty list with the names of your favorite pop songs from the 80s, or any string, like 'kill, which I find makes this flag very readable. (I hate to guess what positional arguments do.)

There you go, problem fixed. Happy (manu)script editing.