How to Make an Emacs Minor Mode
An Emacs buffer always has one major mode and zero or more minor modes. Major modes tend to be significant efforts, especially when it comes to automatic indentation. In contrast, minor modes are often simple, perhaps only overlaying a small keymap for additional functionality. Creating a new minor mode is really easy, it’s just a matter of understanding Emacs’ conventions.
Mode names should end in
-mode and the command for toggling the mode
should be the same name. They keymap for the mode should be called
-map and the mode’s toggle hook should be called
-hook. Keep all of this in mind when picking a name for your
There are a number of other tedious issues that need to be taken into
account when manually building a minor mode. The good news is that no
one needs to worry about most of it! Lisp has macros for cutting down
on boilerplate code and so there’s a macro for this very purpose:
define-minor-mode. Here’s all it takes to make a new minor mode,
(define-minor-mode foo-mode "Get your foos in the right places.")
This creates a command
foo-mode for toggling the minor mode and a
foo-mode-hook. There’s a strange caveat about the hook:
it’s not immediately declared as a variable. My guess is that this is
some archaic optimization which now exists as bad design. The hook
add-hook will create this variable lazily when needed and
run-hooks will ignore hook variables that don’t yet
exist, so it doesn’t get tripped up by this situation. So despite its
strange initial absence, the new minor mode will use this hook as
soon as functions are added to it.
Minor Mode Options
This mode doesn’t do anything yet. It doesn’t have its own keymap
and it doesn’t even show up in the modeline. It’s just a toggle and a
hook that’s run when the toggle is used. To add more to the mode,
define-minor-mode accepts a number of keywords. Here are the
:lighter– the name, a string, to show in the modeline
:keymap– the mode’s keymap
:global– specifies if the minor mode is global
:lighter option has one caveat: it’s concatenated to the rest of
the modeline without any delimiter. This means it needs to be prefixed
with a space. I think this is mistake, but we’re stuck with it
probably forever. Otherwise this string should be kept short: there’s
generally not much room on the modeline.
(define-minor-mode foo-mode "Get your foos in the right places." :lighter " foo")
New, empty keymaps are created with
(make-sparse-keymap). The latter is more efficient when the map will
contain a small number of keybindings, as is the case with most minor
modes. The fact that these separate functions exist is probably
another outdated, premature optimization. To avoid confusing others, I
recommend you use the one that matches your intended usage.
The keymap can be provided directly to
:keymap and it will be bound
foo-mode-map automatically. I could just put an empty keymap here
and define keys separately outside the
declaration, but I like the idea of creating the whole map in one
(defun insert-foo () (interactive) (insert "foo")) (define-minor-mode foo-mode "Get your foos in the right places." :lighter " foo" :keymap (let ((map (make-sparse-keymap))) (define-key map (kbd "C-c f") 'insert-foo) map))
:global option means the minor mode is not local to a buffer,
it’s present everywhere. As far as I know, the only global minor mode
I’ve ever used is YASnippet.
Minor Mode Body
The rest of
define-minor-mode is a body for arbitrary Lisp, like a
defun. It’s run every time the mode is toggled off or on, so it’s
like a built-in hook function. Use it to do any sort of special setup
or teardown, such hooking or unhooking Emacs’ hooks. A likely thing to
be done in here is specifying buffer-local variables.
Any time the Emacs interpreter is evaluating an expression there’s always a current buffer acting as context. Many functions that operate on buffers don’t actually accept a buffer as an argument. Instead they operate on the current buffer. Furthermore, some variables are buffer-local: the binding is dynamic over the current buffer. This is useful for maintaining state relevant only to a particular buffer.
Side note: the
with-current-buffer macro is used to specify a
different current buffer for a body of code. It can be used to access
other buffer’s local variables. Similarly,
a brand new buffer, uses it as the current buffer for its body, and
then destroys the buffer.
For example, let’s say I want to keep track of how many times
foo-mode inserted “foo” into the current buffer.
(defvar foo-count 0 "Number of foos inserted into the current buffer.") (defun insert-foo () (interactive) (setq foo-count (1+ foo-count)) (insert "foo")) (define-minor-mode foo-mode "Get your foos in the right places." :lighter " foo" :keymap (let ((map (make-sparse-keymap))) (define-key map (kbd "C-c f") 'insert-foo) map) (make-local-variable 'foo-count))
The built-in function
make-local-variable creates a new buffer-local
version of a global variable in the current buffer. Here, the
foo-count will be initialized with the value 0 from the
global variable but all reassignments will only be visible in the
However, in this case it may be better to use
make-variable-buffer-local on the global variable and skip the
make-local-variable. The main reason is that I don’t want
insert-foo to clobber the global variable if it happens to be used
in a buffer that doesn’t have the minor mode enabled.
(make-variable-buffer-local (defvar foo-count 0 "Number of foos inserted into the current buffer."))
A big advantage is that this buffer-local intention for the variable is documented globally. This message will appear in the variable’s documentation.
Automatically becomes buffer-local when set in any fashion.
Which method you use is up to your personal preference. The Emacs documentation encourages the former but I think the latter is nicer in many situations.
Automatically Enabling the Minor Mode
Some minor modes don’t have any particular major mode association and the user will toggle it at will. Some minor modes only make sense when used with particular major mode and it might make sense to automatically enable along with that mode. This is done by hooking that major mode’s hook. So long as the mode follows Emacs’ conventions as mentioned at the top, this hook should be easy to find.
(add-hook 'text-mode-hook 'foo-mode)
foo-mode will automatically be activated in all
Here’s the final code for our minor mode, saved to
has one keybinding and it’s easily open for users to define more keys
foo-mode-map. It also automatically activates when the user is
editing a plain text file.
(make-variable-buffer-local (defvar foo-count 0 "Number of foos inserted into the current buffer.")) (defun insert-foo () (interactive) (setq foo-count (1+ foo-count)) (insert "foo")) ;;;###autoload (define-minor-mode foo-mode "Get your foos in the right places." :lighter " foo" :keymap (let ((map (make-sparse-keymap))) (define-key map (kbd "C-c f") 'insert-foo) map)) ;;;###autoload (add-hook 'text-mode-hook 'foo-mode) (provide 'foo-mode)
I added some autoload declarations and a
provide in case this mode
is ever distributed or used as a package. If an autoloads script is
generated for this minor mode, a temporary function called
will be defined whose sole purpose is to load the real
and then call
foo-mode again with its new definition, which was
loaded overtop the temporary definition.
The autoloads script also adds this temporary
foo-mode function to
text-mode-hook. If a
text-mode buffer is created, the hook
foo-mode which will load
foo-mode to its real definition, then activate
The point of autoloads is to defer loading code until it’s needed. You may notice this as a short delay the first time you activate a mode after starting Emacs. This is what keeps Emacs’ start time reasonable despite having millions of lines of Elisp virtually loaded at startup.