Defining a new minor-mode in Emacs

Recently, I wanted to create my first little mode for Emacs which would allow me to add additional keybindings and commands to handle Kubernetes Secret definitions more efficiently. Turns out, while itโ€™s quite simple to create new major modes and also to activate it, with minor-modes the whole process is a bit different. The working result of this whole quest can be found as the k8s-secret-mode package on Github but here I just wanted to write all things I learnt on the way about working with minor-modes in general and creating a new one in particular.

Defining a minor-mode

Since this was my first attempt at doing something like that, I first needed to learn how to create a minor-mode. Turns out, there are already a couple of helpers available as part of the easy-mmode package, first of all the define-minor-mode macro:

(define-minor-mode k8s-secret-mode
  "Toggle k8s-secret-mode."
  
  ; Here I can set a custom keymap which should be enabled
  ; with this minor mode.
  :keymap k8s-secret-mode-map
  
  ; The text "k8s-secret" should be shown inside the
  ; minor-mode status line:
  :lighter " k8s-secret"
  
  ; Inside the mode I want to keep track if the buffer has
  ; already been decoded or not. I'm using a buffer-local
  ; variable for that which should be reset every time 
  ; the mode is enabled.
  (when k8s-secret-mode
    (setq k8s-secret--status nil)))

Detecting an active minor-mode

Next, I wanted to find out, if a certain minor mode is active or not. Sadly, there isn’t something like the major-mode variable. Instead, I opted to parse the minor mode out of the minor-mode-alist which holds all status-line activation functions for all known minor-modes:

(require 'dash)

(defun active-minor-modes ()
  "Returns a list of all active minor modes based on the state of the mode-line."
  (interactive)
  (let* ((svars (-map '-first-item minor-mode-alist))
         (actives (-filter 'symbol-value svars)))
    (progn
      (when (called-interactively-p 'any)
        (message "Active minor modes: %s" actives))
      actives)))

This is basically a list of cons-lists with the car being the status variable of a minor mode and the cdr the method or string that should be used for the status line. It’s not perfect but usually the minor-mode variable has the same name as the minor-mode itself, so it’s enough for my scenario.

How to automatically activate a minor-mode

While acting a major-mode usually happens through the auto-mode-alist and magic-mode-alist, something similar for minor-modes is not available in the standard distribution.

You could hook into things like the find-file-hook and implement a set-file-mode like function ourselves. Luckily, though, Joe Wreschnig has already done all the work for us with his auto-minor-mode package.

(require 'auto-minor-mode)
(setq auto-minor-mode-magic-alist
      (list '(k8s-secret-mode-magic-matches . 
              k8s-secret-mode)))

All the functions and matching taking place inside the auto-minor-mode-magic-alist happens in a narrowed context controlled through the magic-mode-regexp-match-limit variable. This defines that the matching is restricted to the first 4000 characters of the buffer. Since in my case the kind: Secret string might be also at the very end of a YAML file, I have to first widen and eventually re-narrow the buffer for reliable matching inside the k8s-secret-mode-magic-matches function.

That’s it!

That’s pretty much it ๐Ÿ™‚ Defining a custom (minor-)mode turned out to be much easier than I had anticipated thanks to the easy-mmode package!

Do you want to give me feedback about this article? Please send it to comments@zerokspot.com.