Refactoring an Emacs theme

I’m gradually working on a custom theme for emacs, and I wanted to make it easier to update and read. (It’s not quite ready to share, but I look forward to doing just that when it is.) Surprising no-one, the theme format on emacs is not incredibly … “clean.” It’s basically a command that passes in a huge list of lists (of lists… this is lisp, after all). That command is custom-theme-set-faces.

 'my-cool-theme                         ;theme name

FACE is the symbol for a particular element, for example default for default text or error which is used for error messages. Easy. SPEC, however is a bit more complicated. SPEC is a list of DISPLAY ATTS pairs, each list of attributes pertaining to its paired display. If this were CSS, I think it would be similar to including the @media selector for each rule.

(DISPLAY can simply be default to apply to all displays, but I haven’t seen this used much and am trying to follow suit.)

So now we have this.

 'my-cool-theme                         ;theme name

ATTS is property list, where you can specify the width, height, etc. for the font face, on that display.

So plugging in some actual values, we have the following:

 'my-cool-theme                         ;theme name
   ;; error face, using default DISPLAY (simpler)
   (error ( (default (:foreground "red" :background "black")) ) )

   ;; default face using DISPLAY specified by a a list of conditions (less simpler)
   ;; In this case, a color terminal that supports at least 89 colors.
   (default ( ( ((class color) (min-colors 89)) (:foreground "white" :background "black")) ) )

I was working with a bunch of lines that basically looked like the second case. There was some substitution, but it was still painful to see all that repetition.

    ((class '((class color) (min-colors 89))))
  ;; ...
  `(default ((,class (:foreground "white" :background "black"))))
  ;; ...

I wanted to remove the DISPLAY component completely and just add it later using code. This would mean a cleaner and easier to maintain theme. The excellent emacs-doom-themes has a very elegant solution, but I didn’t quite understand all the elisp and thought I could roll a simpler version myself as a good learning experience.

Lots of fumbling around elisp ensued, but I now have something I’m happy with.

;; the simple-faces are bound to a list `two-fifteen-simple-faces'
(default (:background ,bg1 :foreground ,fg1))
(error   (:foreground "red" :background "black"))
;; ...

;; Add the display to the simple-faces
(while two-fifteen-simple-faces
      ((simple-face (car two-fifteen-simple-faces))
       (fname (car simple-face))
       (fattrs (cadr simple-face)))
    (setq two-fifteen-simple-faces (cdr two-fifteen-simple-faces))
    (setq built-faces (append
                       `((,fname ((,class ,fattrs))))))))

;; apply custom-theme-set-faces to built-faces
(apply #'custom-theme-set-faces 'two-fifteen built-faces)

Next up is making a palette with re-usable colours, which I’ve started w/the bg1 and fg1.

Here are some functions and modes I’m finding extremely helpful when working on the theme:

  • describe-face : Describe the face at the point (cursor)
  • list-faces-display : List faces in use on display
  • rainbow-mode : Display colour values using the actual colour
  • list-colors-display : visually browse colours names and codes
  • align-regexp : Align attribute blocks of the theme to make it easier to read
  • sort-lines : Sort sections alphabetically
  • Iedit and narrow-to-region : Refactoring
  • Keyboard macros, OF COURSE. : start ( C-x ( ), stop ( C-x ) ), run ( C-x e )

There are moments where I do something and a big fat grin creeps onto my face, appreciating what I just did using Emacs. You know what I’m talking about! 🙂

Oh right, here’s a picture of what the theme currently looks like. It’s called two-fifteen. 2019-08-05_emacs_two_fifteen_theme.png

Leave a comment

Your email address will not be published. Required fields are marked *