Acronym minor mode for Emacs

| categories: video, emacs, tooltip | tags:

Three letter acronyms (TLA) are pretty common, as are other kinds of acronyms, e.g. ferromagnetic (FM), anti-ferromagnetic (AFM), National Security Agency (NSA), even Escape-Meta-Alt-Control-Shift (EMACS) etc… in technical documents. As you get away from the definition, it can get hard to remember what they are, so here we develop a minor mode that will put a tooltip over acronyms that hopefully shows what they mean.

You can see this in action here: https://www.youtube.com/watch?v=2G2isMO6E2c

When we turn the mode on, it will scan the buffer looking for an acronym pattern, deduce its likely meaning, and put tooltips on every subsequent use of the acronym. The pattern we will look for is a sequence of uppercase letters surrounded by parentheses. We will assume that if we find N uppercase letters, that the previous N words contain the definition of the acronym. This is pretty approximate, but it is not likely to be that wrong. Then, we will use button-lock to put the tooltips on all subsequent instances of acronyms. We don't want flyspell interfering with the tooltips, so we remove the overlays if they are there.

Unlike previous examples where we just use button-lock, here we wrap the feature into a minor mode that you can turn on and off. Note, you cannot add new acronyms and have them have tooltips. You have to refresh the buttons.

Here is the minor mode code. We use the interesting rx package to build the regular expression. It is more verbose, but a little easier to read than a straight regexp like (concat "\\<" (match-string 1) "\\>") in my opinion.

(make-variable-buffer-local
  (defvar *acronym-buttons* '() "list of acronym buttons"))

(require 'rx)

(defun highlight-acronyms ()
  (save-excursion
    (let ((case-fold-search nil))
      (goto-char (point-min))
      (while (re-search-forward "(\\([A-Z]+\\))" nil t)
        (when flyspell-mode
          (flyspell-delete-region-overlays (match-beginning 1)
                                           (match-end 1)))
        (let* ((acronym (match-string 1))
               (p (point))
               (definition (save-excursion
                             (goto-char (match-beginning 1))
                             (backward-word (length acronym))
                             (buffer-substring (point) p))))
          (add-to-list '*acronym-buttons*
                       (button-lock-set-button
                        (rx word-start (eval (match-string 1)) word-end)
                        nil
                        :help-echo definition)))))))


(defun remove-acronym-buttons ()
  (dolist (button *acronym-buttons*)
      (button-lock-unset-button button))
  (setq *acronym-buttons* '()))


(defun refresh-acronyms ()
  "Refresh acronym tooltips in buffer."
  (interactive)
  (remove-acronym-buttons)
  (highlight-acronyms))


;;;###autoload
(define-minor-mode acronym-mode
  "Put definitions on acronyms."
  :lighter " AM"
  (if acronym-mode
      (highlight-acronyms)
    (remove-acronym-buttons)))


(provide 'acronym-mode)
acronym-mode

There it is. Now any time we have an acronym like EMACS we can mouse over it, or type C-h . on the acronym to see how it was previously defined. If you don't like it, you can turn it off!

Copyright (C) 2015 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.10

Discuss on Twitter