Using Nerd Icons in Org Agenda

Org mode supports icons in its agenda views. The icons can be given as either file paths to images (like SVGs), as image data or as a display property. I use a Nerd Font along with the nerd-icons package in my Emacs config, so I thought I might as well enable icons in my org agenda views.

The nice thing about using nerd fonts is this works perfectly in text mode too (assuming you have a nerd font configured for your terminal emulator).

The code

Since the nerd icons are accessible through a few different sets, I first wrote a function to convert a “simple” alist icon specification into an alist org-mode expects:

1(defun gk-nerd-agenda-icons (fun prefix alist)
2  "Makes an org agenda alist"
3  (mapcar (pcase-lambda (`(,category . ,icon))
4            `(,category
5              (,(funcall fun (concat prefix icon) :height 1.0))))
6          alist))

I use this function like so to create my mapping from categories to icons:

 1(setq org-agenda-category-icon-alist
 2      (append
 3       (gk-nerd-agenda-icons #'nerd-icons-mdicon "nf-md-"
 4                             '(("Birthday" . "cake_variant")
 5                               ("Diary" . "book_clock")
 6                               ("Holiday" . "umbrella_beach")
 7                               ("Chore" . "broom")
 8                               ("Regular" . "autorenew")
 9                               ("Sprint" . "run_fast")
10                               ("Database" . "database")
11                               ("ELT" . "pipe")
12                               ("Devops" . "gitlab")
13                               ("Blog" . "fountain_pen_tip")
14                               ("FOSS" . "code_braces")
15                               ("Tool" . "tools")
16                               ("Todo" . "list_status")))
17       (gk-nerd-agenda-icons #'nerd-icons-sucicon "nf-custom-"
18                             '(("Emacs" . "emacs")
19                               ("Org" . "orgmode")))
20       '(("" '(space . (:width (11)))))))

The final entry is a default match and puts a space of 11 pixels when the category doesn’t match any entry in the list. You’ll have to play around with the number of pixels here as it depends on your font.

You can adjust the :height 1.0 part to make the icons bigger or smaller in a graphical emacs. You’ll have to experiment with this and it will depend on the font you use.

The final thing you probably need is a modification to org-agenda-prefix-format. The reason this is necessary is because some icons take up too much space and make the lines in the agenda overflow on the right. This will depend on your font also, but to fix overflowing lines, make sure your org-agenda-prefix-format entries include %-2i. This means org will include two characters for the icon in its calculation of line width.

1(setq org-agenda-prefix-format '((agenda . " %-2i %-12:c%?-12t% s")
2                                 (todo .   " %-2i %-12:c")
3                                 (tags .   " %-2i %-12:c")
4                                 (search . " %-2i %-12:c")))

You can, of course, remove the category text (%-12:c) completely now, if you wish.

Limitations

This is actually a bit of a hack as what org agenda is actually doing here is using our options as a display property passed to propertize. It works because a display property can be a string, which is just displayed in place of whatever is being “propertized”.

Unfortunately this means there are some limitations: you can’t apply other display properties, nor are recursive display properties supported (ie. using (propertize icon ...) as the display property). So there can be some alignment issues and you can’t change the colours of the icons.

Perhaps it’s possible to patch to org-mode to properly support propertized text as the icon. The difficulty might be making it backwards compatible with current behaviour.

Before I do that I’ll see if I actually enjoy using icons enough over the next few weeks…

Alternative approach

An equally hacky, but much easier, way is just setting the category in your org files to the nerd icon:

1* Database                                                            :@work:
2:PROPERTIES:
3:CATEGORY: 󰆼
4:END:

Then something like:

1(setq org-agenda-prefix-format '((agenda . " %-2c%?-12t% s")
2                                 (todo .   " %-2c")
3                                 (tags .   " %-2c")
4                                 (search . " %-2c")))

This means you can’t practically use the categories for filters and stuff, though.

Happy hacking!