## Adding emacs command key-bindings and help functionality to org-mode

| categories: | tags: | View Comments

The documentation of functions in emacs allows you to put some light markup into function doc strings that will render as the key sequence required to run the command when you look up the help on the function. I would like to have something like that in org-mode. You can look up the key-binding to a command like this:

(substitute-command-keys "\\[org-agenda]")
C-c a

We are going to explore a way to recognize the syntax shown above, change its appearance to alert us that we are looking at an emacs command, add a tooltip, and make it clickable to open the documentation, and s (super) clickable to find the function code. Font lock is the tool we will use for this. Basically, we need a regular expression to match the syntax, and a function to find the next instance, and put some properties on the matched text.

I made a video (https://www.youtube.com/watch?v=VLUMW0sR4Vk ) showing what this post is all about. It isn't easy to see in the post ☺.

Here we use the rx' library to build up a regular expression for this. It is a bit easier to document than a raw regexp. Since we are matching \ in the pattern, there are some obligatory escaping \ characters in there too. All we need is to integrate this into font-lock. We define a function that will move the point to the end of the next match, and put properties on the match. We will go ahead and make the text clickable so we can access documentation and code easily. The tooltip will show the key-binding to run the command.

(require 'rx)

(defvar elisp-symbol-keybinding-re
(rx
;; opening \\[
(eval "\\[")
;; one or more characters that are not ]
(group (one-or-more (not (any "]"))))
;; The closing ]
"]")
"Regexp for an elisp command keybinding syntax. \\[some-command]
Regexp group 1 matches some-command'.")

(defun match-next-keybinding (&optional limit)
"Move point to the end of the next expression matching
elisp-symbol-keybinding-re', and put properties on the match
that shows the key sequence. Non-bound commands are not
fontified."
(when (and (re-search-forward
elisp-symbol-keybinding-re
limit t)
(fboundp (intern (match-string 1))))
(let* ((beg (match-beginning 0))
(end (match-end 0))
(s (match-string 0))
(command (match-string 1))
(describe-func (lambda ()
"Run describe-function' on the command."
(interactive)
(describe-function (intern ,command))))
(find-func (lambda ()
"Run find-function' on the command."
(interactive)
(find-function (intern ,command))))
(map (make-sparse-keymap)))

;; this is what gets run when you click on it.
(define-key map [mouse-1] describe-func)
(define-key map [s-mouse-1] find-func)
;; Here we define the text properties
beg end
(local-map ,map
mouse-face highlight
help-echo ,(format
"%s\n\nClick for documentation.\ns-mouse-1 to find function."
(substitute-command-keys s))
keybinding t)))))

Let's go ahead and make syntax for some-command' too. This one seems simple enough we just write a regexp for it.

(defun match-next-emacs-command (&optional limit)
"Move point to the end of the next expression matching
this-syntax', and put a tooltip on the match
that shows the key sequence. Works on commands and variables."
(when (and (re-search-forward
"\$$[^']+\$$'"
limit t)
(or (boundp (intern (match-string 1)))
(fboundp (intern (match-string 1)))))
(let* ((beg (match-beginning 0))
(end (match-end 0))
(s (match-string 0))
(command (match-string 1))
(describe-func
(lambda ()
"Run describe-function/variable' on the command."
(interactive)
(cond ((fboundp (intern ,command))
(describe-function (intern ,command)))
((boundp (intern ,command))
(describe-variable (intern ,command))))))
(find-func (lambda ()
"Run find-function' on the command."
(interactive)
(find-function (intern ,command))))
(map (make-sparse-keymap)))

;; this is what gets run when you click on it.
(define-key map [mouse-1] describe-func)
(define-key map [s-mouse-1] find-func)
;; Here we define the text properties
beg end
(local-map ,map
mouse-face highlight
help-echo ,(format
"%s\n\nClick for documentation.%s"
(if (fboundp (intern command))
(substitute-command-keys (format "\\[%s]" command))
"Variable")
(if (fboundp (intern command))
"\ns-mouse-1 to find function."
""))
keybinding t)))))

Now we need a way to turn them on and off. We do that here with a minor mode.

(define-minor-mode emacs-keybinding-command-tooltip-mode
"Fontify on emacs keybinding syntax. Adds a tooltip for
keybinding, and make the command clickable to get to the
documentation."
:lighter " KB"
(if emacs-keybinding-command-tooltip-mode
;; turn them on
nil
'((match-next-keybinding 1 font-lock-constant-face)
(match-next-emacs-command 1 font-lock-constant-face)))
;; turn them off
(font-lock-remove-keywords
nil
'((match-next-keybinding 1 font-lock-constant-face)
(match-next-emacs-command 1 font-lock-constant-face))))
(font-lock-fontify-buffer))

Here we turn it on:

(emacs-keybinding-command-tooltip-mode -1)

Here are some sample uses. You can use \\[org-toggle-latex-overlays] to toggle latex overlays.

You can use \\[org-ref-helm-insert-cite-link] to insert citations.

That more or less does it! I don't know if this is the canonical way to do this, but it works nicely here. You can also use overlays, but I found them a little confusing because they are not editable, and you have to toggle the minor mode to see them. Here we have unobtrusive tooltips. One downside is these won't export in any fashion in org-mode since it is not part of the syntax. It might be a good idea to adjust font-lock-extra-managed-props' for this

It works for this syntax too: helm', which is also commonly used in doc strings. This should be pretty handy in org-mode documents about Emacs!