2022-01-17 Making code snippets in Org-mode easier to type

From time to time people tend to discuss the Org markup – some people want to extend it in one way or another, some people want to make it more Markdown-ish. I tend to agree that the selection of the tilde (~) to denote code is a bit inferior than Markdown’s choice of the backtick (`). But keep in mind this: I don’t have a problem with looking at my Org file and seeing (lots of) tildes. After all, font-lock does a good job of telling me that this is code. I have a problem with typing these tildes, since they require me pressing the shift key – and if I have a lot of small code snippets, this is not very comfortable. Add to this the fact that I happen to write in Markdown pretty often (when I use Markdown-based tools, like Slack or Jira), and I started wondering: why couldn’t I press the backtick in Org-mode to type a tilde (and vice versa so that I don’t lose the ability to type a backtick)?

Now, this is probably easy to accomplish using xmodmap. But I am an Emacs user, so where’s the fun in that? Of course I don’t want to bind a command with some simplistic code like (insert "”)~ to the backtick, since I could lose things like tables aligning automatically or speed keys (which I wouldn’t regret since I don’t use them – but maybe I’ll will one day?). Well, by default both backtick and tilde are bound to org-self-insert-command in Org-mode. It would be best if my command could call that but trick it to think that another key was pressed. It turns out that this can be done by setting the variable last-command-event. (And I can’t help but mention that this variable and an example of its use are discussed in one of the chapters of my book on Elisp!) So, the most basic version of swapping the meaning of tilde and backtick may look like this:

(defun org-insert-backtick ()
  "Insert a backtick using `org-self-insert-command'."
  (interactive)
  (setq last-command-event ?`)
  (call-interactively #'org-self-insert-command))

(defun org-insert-tilde ()
  "Insert a tilde using `org-self-insert-command'."
  (interactive)
  (setq last-command-event ?~)
  (call-interactively #'org-self-insert-command))

(define-key org-mode-map (kbd "`") #'org-insert-tilde)
(define-key org-mode-map (kbd "~") #'org-insert-backtick)

Now, the two commands defined above are so similar that I could write a macro to define them:

(defmacro define-org-insert-whatever (name character)
  "Make the function `org-insert-NAME' insert CHARACTER."
  `(defun ,(intern (concat "org-insert-" name)) ()
     ,(format "Insert a %s using `org-self-insert-command'." name)
     (interactive)
     (setq last-command-event ,character)
     (call-interactively #'org-self-insert-command)))

This seems a good idea on paper, but has a significant drawback – when looking up a function generated this way (with C-h C-f), we don’t get the link to the source of the function. So in this case I prefer to type the defun​s by hand. Also, in a moment I’m going to change the definition of org-insert-backtick anyway.

I am not sure how long this contraption is going to survive in my init file, since this is pretty unorthodox and I may get tripped by this more than one time (muscle memory is a thing!). But I’m going to try to stick with it, because it’s cool, and I need the tilde character pretty often when writing this blog, for instance. Also, having coded the above, I could get really fancy and do this:

(defvar-local org-insert-tilde-language nil
  "Default language name in the current Org file.
If nil, `org-insert-tilde' after 2 tildes inserts an \"example\"
block.  If a string, it inserts a \"src\" block with the given
language name.")

(defun org-insert-tilde ()
  "Insert a tilde using `org-self-insert-command'."
  (interactive)
  (if (string= (buffer-substring-no-properties (- (point) 3) (point))
	       "\n~~")
      (progn (delete-char -2)
	     (if org-insert-tilde-language
		 (insert (format "#+begin_src %s\n#+end_src"
				 org-insert-tilde-language))
	       (insert "#+begin_example\n#+end_example"))
	     (forward-line -1)
	     (if (string= org-insert-tilde-language "")
		 (move-end-of-line nil)
	       (org-edit-special)))
    (setq last-command-event ?~)
    (call-interactively #'org-self-insert-command)))

How cool is that? Now when I press the backtick three times when at the beginning of the line (like what I’d do when typing in Markdown to create a code block), I get an example block (or a source code block in any language defined by the org-insert-tilde-language variable, which can be set as a buffer-local variable). If that variable is an empty string, I can fill in the language; otherwise, I can edit the example or source block straight away. Also, this variable is defined with defvar-local so that it is automatically buffer-local. (Also, you might want to set it up as a file variable.)

Now this is clearly not something for everyone. But note that – as usual – it is a perfect example of Emacs malleability. Coding this took me about 15 minutes, and even if it’s not perfect or could break in some circumstances (which can probably happen, given the level of complexity of Emacs and the plethora of its existing packages – I can’t possibly predict the potential interactions between them!), I could just dive back into the code and fix any issues when they appear.

And again, let me say that this only takes 15 minutes of coding when you are pretty well-versed with Emacs standard library. And becoming pretty well-versed with Emacs standard library is not that difficult – you just have to code in Elisp enough to learn it. (Well, there are some resources that can help with that… Just saying! ;-) )

CategoryEnglish, CategoryBlog, CategoryEmacs, CategoryOrgMode