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 SPEC) (FACE SPEC) ))
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
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.
(custom-theme-set-faces 'my-cool-theme ;theme name '( (FACE ((DISPLAY ATTS) (DISPLAY ATTS))) (FACE ((DISPLAY ATTS) (DISPLAY ATTS))) ))
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:
(custom-theme-set-faces '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.
(let ((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 (let* ((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 built-faces `((,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
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
- Keyboard macros, OF COURSE. : start (
C-x (), stop (
C-x )), run (
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.