Org-Mode Emphasis Keymap with Mnemonics
In Emacs Org-Mode, the default key binding to “highlight” or “format” the selection is C-c C-x C-f. This will then ask for the org-mode syntax thingie to use, i.e. */_~=+
. Literally, that’s the prompt.
So you have to know which character to use to embolden text. That’s *
. But which one is code? It’s ~
. And =
is for verbatim text, while +
is for strike-through.
This is defined in the variable org-emphasis-alist
, and it’s default value is:
(("*" bold)
("/" italic)
("_" underline)
("=" org-verbatim verbatim)
("~" org-code verbatim)
("+"
(:strike-through t)))
If you don’t know what any of these characters do, but if you know how to browse the Emacs help to get to this piece of information, you can learn the formatting syntax this way.
I don’t like that. If I already know the character to type, I may just as well type it. There are plenty of options to have some characters surround the selected text, so that you can select text first, then hit *, and have the selection be emboldened. That’s not what I believe a formatting shortcut is useful for.
As an actual convenience for users, UX and all, I believe “b” for “bold” and such would be much better to get started!
That’s also closer to common shortcuts in rich text editors, so that may be a bonus for new users, too. And me, who never knows whether to type =
or ~
for inline code.
So here’s how you’d do that in Emacs nowadays: with a keymap.
(defvar-keymap ct/org-emphasis-map
:doc "Keymap for quickly applying Org emphasis rules."
:name "[b]old [i]talic [u]nderscore [v]erbatim [c]ode [s]trike-though"
"b" (lambda () (interactive) (ct/org-emphasize-below-point ?*))
"i" (lambda () (interactive) (ct/org-emphasize-below-point ?/))
"u" (lambda () (interactive) (ct/org-emphasize-below-point ?_))
"v" (lambda () (interactive) (ct/org-emphasize-below-point ?=))
"c" (lambda () (interactive) (ct/org-emphasize-below-point ?~))
"s" (lambda () (interactive) (ct/org-emphasize-below-point ?+)))
This adds all mnemonics (the initial letters of the styles) to the keymap and executes a function with the corresponding org-mode formatting character as argument.
The function I call is ct/org-emphasize-below-point
because that’s my personal way to auto emphasize the closest thing:
(defun ct/org-emphasize-below-point (&optional char)
"Emphasisez region with CHAR.
If there's no region, marks the closest s-expression, first.
Opposed to word boundaries, sexp's work with `subword-mode' enabled."
(interactive)
(unless (region-active-p)
(backward-sexp)
(mark-sexp))
(org-emphasize char))
subword-mode
makes by-word movement commands stop at capital letters in CamelCasedWords, which I find extremely useful in almost all circumstances. Except highlighting the word before the insertion point, because then I want the whole word word, not sub-word. So that’s what my function is for.
If you’re not using any of that, call (org-emphasize ?*)
etc. instead in the lambdas.
So I don’t know any arguments in defense of the */_=~+
-based dispatcher, but I can come up with two arguments in favor of biuvcs
:
- Ergonomics: It requires only letters that you can press without modifiers, while all but
=
and/
of the original 6 require shift to access on QWERTY. - Discoverability. Bold is
b
, italics isi
. If you know what the style is called in English, you know which letter to press. If you forget, you can re-learn. The original 6 you had to learn and remember to use, which reduces the utility oforg-emphasize
because typing the character immediately works almost just as well. - Bonus: Composability That’s not actually an argument for the letters I picked, but this approach uses a keymap to do its job instead of a one-off, bespoke function. You can use a keymap in GUI menus, or plug it into an Embark prefix key inside an org document.
Finally, as a hat-tip to markdown-mode
, I bound this to C-c C-s:
(define-key org-mode-map (kbd "C-c C-s") ct/org-emphasis-map)
The Markdown mode’s style helper formats the prompt much more nicely, though; here, look:
I love this attention to detail, but I was too lazy for that; the function that produces the propertized (i.e.: styled) string for the modeline is this:
(defun markdown--style-map-prompt ()
"Return a formatted prompt for Markdown markup insertion."
(when markdown-enable-prefix-prompts
(concat
"Markdown: "
(propertize "bold" 'face 'markdown-bold-face) ", "
(propertize "italic" 'face 'markdown-italic-face) ", "
(propertize "code" 'face 'markdown-inline-code-face) ", "
(propertize "C = GFM code" 'face 'markdown-code-face) ", "
(propertize "pre" 'face 'markdown-pre-face) ", "
(propertize "footnote" 'face 'markdown-footnote-text-face) ", "
(propertize "F = foldable" 'face 'markdown-bold-face) ", "
(propertize "q = blockquote" 'face 'markdown-blockquote-face) ", "
(propertize "h & 1-6 = heading" 'face 'markdown-header-face) ", "
(propertize "- = hr" 'face 'markdown-hr-face) ", "
"C-h = more")))
That could be adapted to my barebones modeline string that just says
"[b]old [i]talic [u]nderscore [v]erbatim [c]ode [s]trike-though"
I leave this as an exercise for the reader.
…
Really, please implement this, and share. I want it, but I don’t want to spend that time today :)