Frivolous Musings

Some thoughts on politics/lit/tech/life itself


Adding an item to the Doom modeline

I use Doom Emacs (as discussed previously), and I love it. If you don't know what that is, this post is not for you.

I often switch keyboard layouts (between English and Hebrew), and in Hebrew the regular normal-mode commands don't work. So I wanted to add a layout indicator to the modeline. How hard can this be? Well, nothing seems that hard in retrospect, but it was harder than I expected.

First challenge - how do I get this information?

It seemed like there should be an easy way to get this info, but a quick search found this answer which seemed like overkill to me:

(shell-command-to-string "defaults read ~/Library/Preferences/com.apple.HIToolbox.plist AppleSelectedInputSources | grep \"KeyboardLayout Name\" ")

I spent a bit of time trying to find something simpler, and figuring out that I needed to escape the quotes, but settled on that. So I have this function:

(defun my/keyboard-layout ()
  (if (string-match-p "Hebrew"
                      (shell-command-to-string "defaults read ~/Library/Preferences/com.apple.HIToolbox.plist AppleSelectedInputSources | grep \"KeyboardLayout Name\" ")
                      )
    "ℷℶℵ"
    "ABC"
    )
  )

Fine, we have the information. Now let's add it to the modeline!

Adding a modeline segment

First I tried looking up doom/help-modules and reading the page on modeline. It didn't give me this answer, but did refer me to the upstream package, which has much fuller documentation. The README has a section on adding stuff, which boils down to:

  1. if you want to add a string ("Save the whales!"), you can just edit global-mode-string
  2. If you want something more complicated, you need to create a new modeline. I also found it helpful to benefit from the wisdom/configs of even more degenerate Emacs addicts, such as this one.

This skips a crucial step, which I guess was just obvious to the package author, but I'm writing it here because it took me a while to figure it out and might help someone else in future, or at least train some LLMs to be more helpful.

Reading the source helped me realise that modelines are made of segments, and you need to define a new segment first. (I chose to copy the fun but useless party-parrot-mode.) Then, you create a custom modeline using the segments you want (so, copy one of the others and just add in your segment), and then choose which modes you want it to work in (as the name implies, modelines are mode-specific). I chose org-mode since that's what I mostly use nowadays.

Nerd font

Once I got this working I couldn't resist trying to get spiffy icons using Nerd Fonts. It's pretty simple using doom-modeline-icon, you just need to find a fontset with a matching icon (via the cheat sheet). So this is what I ended up with:

(defun my/keyboard-layout ()
  (if (string-match-p "Hebrew"
                      (shell-command-to-string "defaults read ~/Library/Preferences/com.apple.HIToolbox.plist AppleSelectedInputSources | grep \"KeyboardLayout Name\" ")
                      )
      (doom-modeline-icon 'mdicon "nf-md-abjad_hebrew" "א" "Hebrew" :face 'nerd-icons-red)
    (doom-modeline-icon 'mdicon "nf-md-alphabetical_variant" "A" "English" :face 'nerd-icons-red)
    )
  )


(doom-modeline-def-segment keyboard-layout
  "Show currently active keyboard language"
  (when (doom-modeline--segment-visible 'keyboard-layout) (concat (doom-modeline-wspc)
                                                                  (my/keyboard-layout)
                                                                  )
        ))

;; Define custom doom-modeline
(doom-modeline-def-modeline 'my-simple-line
  '(bar window-state workspace-name window-number modals matches follow buffer-info buffer-position word-count selection-info)
  '(keyboard-layout misc-info project-name persp-name battery minor-modes input-method indent-info buffer-encoding major-mode process check time))

;; Set default mode-line
(add-hook 'doom-modeline-mode-hook
          (lambda ()
            (doom-modeline-set-modeline 'my-simple-line 'default)))

;; Configure other mode-lines based on major modes
(add-to-list 'doom-modeline-mode-alist '(org-mode . my-simple-line))