24

I have a bit of a slow pinky. As my other fingers get faster and faster at typing, my pinky just can't keep up. This frequently leads me to type sentences whose first word starts with two uppercase letters. Here's a short example.

THere's nothing in there. DEfinitely not a body.

Usually what I do is hit M-b M-c (assuming I spot it immediately), but I'm getting a little tired of that.

How can I have Emacs fix this automatically for me?

That is, I'd like Emacs to detect when I type a word that starts with two uppercase letters followed by at least one lowercase letter, and fix that automatically.

0

3 Answers 3

21

Here's a function that will convert DOuble CApitals to Single Capitals. I'd originally suggested adding it to post-self-insert-hook, but below is an option for a glorified minor mode so that you only add to that hook when you really want it:

(defun dcaps-to-scaps ()
  "Convert word in DOuble CApitals to Single Capitals."
  (interactive)
  (and (= ?w (char-syntax (char-before)))
       (save-excursion
         (let ((end (point)))
           (and (if (called-interactively-p)
                    (skip-syntax-backward "w")
                  (= -3 (skip-syntax-backward "w")))
                (let (case-fold-search)
                  (looking-at "\\b[[:upper:]]\\{2\\}[[:lower:]]"))
                (capitalize-region (point) end))))))
(add-hook 'post-self-insert-hook #'dcaps-to-scaps nil 'local)

And the minor mode definition:

(define-minor-mode dubcaps-mode
  "Toggle `dubcaps-mode'.  Converts words in DOuble CApitals to
Single Capitals as you type."
  :init-value nil
  :lighter (" DC")
  (if dubcaps-mode
      (add-hook 'post-self-insert-hook #'dcaps-to-scaps nil 'local)
    (remove-hook 'post-self-insert-hook #'dcaps-to-scaps 'local)))

For what it's worth, using this version:

  • is simple: just turn it on/off manually or in a mode hook;
  • requires no changes to key bindings, so you're not losing any other functionality.

Even when you add it to post-self-insert-hook, the overhead is almost non-existent, at least according to some simple benchmarking. On my machine, here's what I get with 10,000 repetitions each of a ludicrously simple form and the dcaps-to-scaps function:

(benchmark-run-compiled 10000 (+ 1 1))          ; => .001 to .003 -ish
(benchmark-run-compiled 10000 (dcaps-to-scaps)) ; => .003 to .006 -ish

So, yes, it's slower than adding 1 + 1, but in absolute terms you'll never notice.

8
  • You could use looking-at-p, which does not set the match data at all (that's ok since you don't need or use it here).
    – YoungFrog
    Commented Jul 14, 2015 at 13:41
  • A few more remarks, mostly unimportant (but I prefer your answer so I want to contribute ;p) : the use of forward-word won't work well with subword-mode, using (char-syntax (char-before)) will (I guess) ignore any syntax class set with properties (alternate solution: (syntax-after (1- (point))), and (last but not least) the regexp won't find accented letters (e.g. "ÉMincer", in french)
    – YoungFrog
    Commented Jul 14, 2015 at 23:04
  • @YoungFrog: updated to deal with the forward-word issue, and changed the regexp to deal with accented capitals.
    – Dan
    Commented Jul 15, 2015 at 1:35
  • Is there a reason to prefer and over when, in particular in the first instance?
    – Clément
    Commented Jul 20, 2015 at 15:28
  • @Clément: and is short-circuited, so the logic works like when here. I'm not sure if there are best practices about using one versus the other, but it seems like it would make a good question on this site (I'd upvote, anyway).
    – Dan
    Commented Jul 23, 2015 at 12:16
8

My preference is to simply create a new function that does what the usual self-insert-command would do plus more.

Here are few reasons:

  • Finer control on which major modes should have this auto-correcting capability. For this use case, it could be text-only modes like org-mode, text-mode, etc.
  • For the type of correction requested in the question, user would typically hit SPC or RET or . key after the word. So using something like post-self-insert-hook might be an overkill, and we would do that extra processing each time we hit any key.

So the below proposed solution binds this function with just the SPC key in org-mode-map (ignoring the corner case where the word could be the last word in a line). If needed, the user can bind similar wrapper functions to more keys.

(defun space-plus-more ()
  (interactive)
  (save-excursion
    (backward-word 1)
    (let ((case-fold-search))
      (when (looking-at-p "[A-Z]\\{2\\}.*?[a-z]+.*?\\b")
        (capitalize-word 1))))
  (self-insert-command 1))

(define-key org-mode-map (kbd "<SPC>") #'space-plus-more)

This is an interesting elisp exercise :)

I personally wouldn't want to bind this to RET as then I would lose the default bindings in org-mode and probably other major modes too. But it was interesting to learn about elt and this-command-keys-vector.

(defun my/fix-double-caps ()
  (interactive)
  (save-excursion
    (backward-word 1)
    (let ((case-fold-search))
      (when (looking-at-p "[A-Z]\\{2\\}.*?[a-z]+.*?\\b")
        (capitalize-word 1))))
  (if (eq 13 (elt (this-command-keys-vector) 0)) ; detect RET
      (newline)
    (self-insert-command 1)))

(let ((maps-list (list org-mode-map
                       text-mode-map))
      (keys-list (list "<SPC>" "<RET>" ".")))
  (dolist (map maps-list)
    (dolist (key keys-list)
      (define-key map (kbd key) #'my/fix-double-caps))))
3
  • Oh yes, restricting it to text-derived modes is definitely a good idea. :)
    – Malabarba
    Commented Jul 14, 2015 at 14:40
  • @Malabarba don't you want this behaviour in strings for modes derived from prog-mode ?
    – YoungFrog
    Commented Jul 14, 2015 at 14:58
  • @YoungFrog sure, but then it would have to check that it's actually inside a string, otherwise it would just get in the way.
    – Malabarba
    Commented Jul 14, 2015 at 22:05
0

Maybe this answer does not provide solution that you're expecting (interactive correction of words as you type), I would like to share my ways to fight such problems.

First of all, I don't like things that silently change my text (capitalize, etc., what if you want to type the word IBuffer, I think such "corrector" is a wrong way), so I advise two things:

First, try to enable feature "Sticky Keys". It may seem weird at first but I use it all the time. This feature is available on OS/desktop environment level, it's not Emacs stuff. When this thing is enabled you first press ⇧ Shift and then you press another key you want to capitalize. This way your problem cannot even arise, only one letter is capitalized in this sequential approach! It also reduces work your hands need to do holding the ⇧ Shift key. I think it's easier to type now.

Second, now you still can use ⇧ Shift key normally (holding it) when your think it's necessary, but I would like to propose you an Emacs package called Fix Word. Even if you don't like "Sticky Keys", you can correct words into their proper forms easily, and you can correct several words in a row without useless movements of the cursor. Give it a try, I use it all the time. (It's still difficult to correct things if you entered several words and the word you need to capitalize is somewhere in the middle.)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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