Xah Lee, 2008-10, 2009-07-31
Emacs has several user level commands for changing letter case. They are: upcase-word 【Alt+u】, downcase-word 【Alt+l】, capitalize-word 【Alt+c】.
There are also “region” versions for each: upcase-region 【Ctrl+x Ctrl+u】, downcase-region 【Ctrl+x Ctrl+l】, capitalize-region, and also upcase-initials-region. (Note: for elisp programing, there are also these functions: upcase, capitalize, downcase, upcase-initials.)
One problem with these commands is that you need to move your cursor to the beginning of the word first. For example, if you have the text “THat”, and your cursor is on the “a”, and you call downcase-word, but it doesn't do anything because it only start at the cursor point to end of word. It would be nice if it'll just automatically perform the operation on the whole word.
Another problem is that it does not consider the final result. For example, if you have “oncE upon a time …”, and you select the whole sentence and call upcase-initials-region, it becomes “OncE Upon A Time …”. Note the capital E is not automatically lowered. For elisp programing, the orthogonal precision is nice, but as user commands, it is better to change the whole sentence.
Also, these commands have a “-word” and “-region” variants, great for precision in elisp programing but not smart as user commands. It would be nice if emacs automatically choose the right command depending whether there is text selection. (emacs is this way because technically, a “region” always exists. The modern concept of text selection (with highlighting) didn't come to emacs until it had transient-mark-mode, along with the complexity of “mark-active”. Things would be much simpler if emacs adopted transient-mark-mode by default. For detail, see: Emacs: What's Region, Active Region, transient-mark-mode? )
The following code combines them into one user function and should fix these problems.
(defun toggle-letter-case () "Toggle the letter case of current word or text selection. Toggles from 3 cases: UPPER CASE, lower case, Title Case, in that cyclic order." (interactive) (let (pos1 pos2 (deactivate-mark nil) (case-fold-search nil)) (if (and transient-mark-mode mark-active) (setq pos1 (region-beginning) pos2 (region-end)) (setq pos1 (car (bounds-of-thing-at-point 'word)) pos2 (cdr (bounds-of-thing-at-point 'word)))) (when (not (eq last-command this-command)) (save-excursion (goto-char pos1) (cond ((looking-at "[[:lower:]][[:lower:]]") (put this-command 'state "all lower")) ((looking-at "[[:upper:]][[:upper:]]") (put this-command 'state "all caps") ) ((looking-at "[[:upper:]][[:lower:]]") (put this-command 'state "init caps") ) (t (put this-command 'state "all lower") ) ) ) ) (cond ((string= "all lower" (get this-command 'state)) (upcase-initials-region pos1 pos2) (put this-command 'state "init caps")) ((string= "init caps" (get this-command 'state)) (upcase-region pos1 pos2) (put this-command 'state "all caps")) ((string= "all caps" (get this-command 'state)) (downcase-region pos1 pos2) (put this-command 'state "all lower")) ) ) )
This toggle-letter-case command is implemented in ergoemacs and has a single keyboard shortcut.