Skip to content
Switch branches/tags
Go to file
Cannot retrieve contributors at this time
;;; doom-themes.el --- an opinionated pack of modern color-themes -*- lexical-binding: t; -*-
;; Copyright (C) 2016-2021 Henrik Lissner
;; Author: Henrik Lissner <>
;; Maintainer: Henrik Lissner <>
;; Maintainer: Emmanuel Bustos Torres <>
;; Created: May 22, 2016
;; Modified: May 30, 2021
;; Version: 2.2.1
;; Keywords: custom themes, faces
;; Homepage:
;; Package-Requires: ((emacs "25.1") (cl-lib "0.5"))
;; This file is not part of GNU Emacs.
;;; Commentary:
;; DOOM Themes is an opinionated UI plugin and pack of themes extracted from my
;; [emacs.d], inspired by some of my favorite color themes including:
;; Flagship themes
;; `doom-one'
;; `doom-one-light'
;; `doom-vibrant'
;; Additional themes
;; + `doom-acario-dark' (added by gagbo)
;; + `doom-acario-light' (added by gagbo)
;; + `doom-ayu-dark': (added by LoveSponge)
;; + `doom-ayu-light': (added by LoveSponge)
;; + `doom-city-lights' (added by fuxialexnder)
;; + `doom-challenger-deep' (added by fuxialexnder)
;; + `doom-dark+' (added by ema2159)
;; + `doom-dracula' (added by fuxialexnder)
;; + `doom-ephemeral' (added by karetsu)
;; + `doom-fairy-floss' (added by ema2159)
;; + `doom-flatwhite' (added by ShaneKilkelly)
;; + `doom-gruvbox' (added by JongW)
;; + `doom-gruxbox-light' (added by jsoa)
;; + `doom-henna' (added by jsoa)
;; + `doom-homage-white' (added by [mskorzhinskiy])
;; + `doom-homage-black': (added by [mskorzhinskiy])
;; + `doom-horizon' (added by karetsu)
;; + `doom-Iosvkem' (added by neutaaaaan)
;; + `doom-ir-black' (added by legendre6891)
;; + `doom-laserwave' (added by hyakt)
;; + `doom-material' (added by tam5)
;; + `doom-manegarm' (added by kenranunderscore)
;; + `doom-miramare' (added by sagittaros)
;; + `doom-molokai' (added by hlissner)
;; + `doom-monokai-classic' (added by ema2159)
;; + `doom-monokai-pro' (added by kadenbarlow)
;; + `doom-monokai-machine' (added by minikN)
;; + `doom-monokai-octagon' (added by minikN)
;; + `doom-monokai-ristretto' (added by minikN)
;; + `doom-monokai-spectrum' (added by minikN)
;; + `doom-moonlight' (added by Brettm12345)
;; + `doom-nord' (added by fuxialexnder)
;; + `doom-nord-light' (added by fuxialexnder)
;; + `doom-nova' (added by bigardone)
;; + `doom-oceanic-next' (added by juanwolf)
;; + `doom-old-hope' (added by teesloane)
;; + `doom-opera' (added by jwintz)
;; + `doom-opera-light' (added by jwintz)
;; + `doom-outrun' (added by ema2159)
;; + `doom-palenight' (added by Brettm12345)
;; + `doom-peacock' (added by teesloane)
;; + `doom-plain': (added by [mateossh])
;; + `doom-plain-dark': (added by [das-s])
;; + `doom-rouge' (added by JordanFaust)
;; + `doom-snazzy' (added by ar1a)
;; + `doom-solarized-dark' (added by ema2159)
;; + `doom-solarized-light' (added by fuxialexnder)
;; + `doom-sourcerer' (added by defphil)
;; + `doom-spacegrey' (added by teesloane)
;; + `doom-tomorrow-night' (added by emacswatcher)
;; + `doom-tomorrow-day' (added by emacswatcher)
;; + `doom-wilmersdorf' (added by ianpan870102)
;; + `doom-zenburn' (added by jsoa)
;; ## Install
;; `M-x package-install RET doom-themes`
;; A comprehensive configuration example:
;; (require 'doom-themes)
;; ;; Global settings (defaults)
;; (setq doom-themes-enable-bold t ; if nil, bold is universally disabled
;; doom-themes-enable-italic t) ; if nil, italics is universally disabled
;; ;; Load the theme (doom-one, doom-molokai, etc); keep in mind that each
;; ;; theme may have their own settings.
;; (load-theme 'doom-one t)
;; ;; Enable flashing mode-line on errors
;; (doom-themes-visual-bell-config)
;; ;; Enable custom neotree theme
;; (doom-themes-neotree-config) ; all-the-icons fonts must be installed!
;;; Code:
(require 'cl-lib)
(require 'doom-themes-base)
(defgroup doom-themes nil
"Options for doom-themes."
:group 'faces)
(defcustom doom-themes-padded-modeline nil
"Default value for padded-modeline setting for themes that support it."
:group 'doom-themes
:type '(choice integer boolean))
(defcustom doom-themes-enable-bold t
"If nil, bold will be disabled across all faces."
:group 'doom-themes
:type 'boolean)
(defcustom doom-themes-enable-italic t
"If nil, italics will be disabled across all faces."
:group 'doom-themes
:type 'boolean)
;;; API
(defvar doom-themes--colors nil)
(defvar doom--min-colors '(257 256 16))
(defvar doom--quoted-p nil)
(defvar doom-themes--faces nil)
(defun doom-themes--colors-p (item)
(declare (pure t) (side-effect-free t))
(when item
(cond ((listp item)
(let ((car (car item)))
(cond ((memq car '(quote doom-color)) nil)
((memq car '(backquote \`))
(let ((doom--quoted-p t))
(doom-themes--colors-p (cdr item))))
((eq car '\,)
(let (doom--quoted-p)
(doom-themes--colors-p (cdr item))))
((or (doom-themes--colors-p car)
(doom-themes--colors-p (cdr-safe item)))))))
((and (symbolp item)
(not (keywordp item))
(not doom--quoted-p)
(not (equal (substring (symbol-name item) 0 1) "-"))
(assq item doom-themes--colors))))))
(defun doom-themes--apply-faces (new-faces &optional default-faces)
(declare (pure t) (side-effect-free t))
(let ((default-faces (or default-faces doom-themes-base-faces))
(faces (make-hash-table :test #'eq :size (+ (length default-faces) (length new-faces))))
(directives (make-hash-table :test #'eq)))
(dolist (spec (append (mapcar #'copy-sequence default-faces) new-faces))
(if (listp (car spec))
(cl-destructuring-bind (face action &optional arg) (car spec)
(unless (assq face new-faces)
(puthash face (list action arg (cdr spec))
(puthash (car spec) (cdr spec) faces)))
(cl-loop for face being the hash-keys of directives
for (action target spec) = (gethash face directives)
unless (memq action '(&inherit &extend &override))
do (error "Invalid operation (%s) for '%s' face" action face)
if (eq (car spec) 'quote)
do (error "Can't extend literal face spec (for '%s')" face)
;; TODO Add &all/&light/&dark extension support
else if (memq (car spec) '(&all &light &dark))
do (error "Can't extend face with &all, &light or &dark specs (for '%s')" face)
else do
(puthash face
(let ((old-spec (gethash (or target face) faces))
(plist spec))
;; remove duplicates
(while (keywordp (car plist))
(setq old-spec (plist-put old-spec (car plist) (cadr plist))
plist (cddr plist)))
(let (results)
(maphash (lambda (face plist)
(when (keywordp (car plist))
;; TODO Clean up duplicates in &all/&light/&dark blocks
(dolist (prop (append (unless doom-themes-enable-bold '(:weight normal :bold nil))
(unless doom-themes-enable-italic '(:slant normal :italic nil))))
(when (and (plist-member plist prop)
(not (eq (plist-get plist prop) 'inherit)))
(plist-put plist prop
(if (memq prop '(:weight :slant))
(quote 'normal))))))
(push (cons face plist) results))
(nreverse results))))
(defun doom-themes--colorize (item type)
(declare (pure t) (side-effect-free t))
(when item
(let ((doom--quoted-p doom--quoted-p))
(cond ((listp item)
(cond ((memq (car item) '(quote doom-color))
((eq (car item) 'doom-ref)
(apply #'doom-ref (cdr item)) type))
((let* ((item (append item nil))
(car (car item))
(cond ((memq car '(backquote \`)) t)
((eq car '\,) nil)
(t doom--quoted-p))))
(cons car
for i in (cdr item)
collect (doom-themes--colorize i type)))))))
((and (symbolp item)
(not (keywordp item))
(not doom--quoted-p)
(not (equal (substring (symbol-name item) 0 1) "-"))
(assq item doom-themes--colors))
`(doom-color ',item ',type))
(defun doom-themes--build-face (face)
(declare (pure t) (side-effect-free t))
',(car face)
,(let ((face-body (cdr face)))
(cond ((keywordp (car face-body))
(let ((real-attrs face-body)
(if (doom-themes--colors-p real-attrs)
(dolist (cl doom--min-colors `(list ,@(nreverse defs)))
(push `(list '((class color) (min-colors ,cl))
(list ,@(doom-themes--colorize real-attrs cl)))
`(list (list 't (list ,@real-attrs))))))
((memq (car-safe (car face-body)) '(quote backquote \`))
(car face-body))
((let (all-attrs defs)
(dolist (attrs face-body `(list ,@(nreverse defs)))
(cond ((eq (car attrs) '&all)
(setq all-attrs (append all-attrs (cdr attrs))))
((memq (car attrs) '(&dark &light))
(let ((bg (if (eq (car attrs) '&dark) 'dark 'light))
(real-attrs (append all-attrs (cdr attrs) '())))
(cond ((doom-themes--colors-p real-attrs)
(dolist (cl doom--min-colors)
(push `(list '((class color) (min-colors ,cl) (background ,bg))
(list ,@(doom-themes--colorize real-attrs cl)))
((push `(list '((background ,bg)) (list ,@real-attrs))
;;; Color helper functions
;; Shamelessly *borrowed* from solarized
(defun doom-name-to-rgb (color)
"Retrieves the hexidecimal string repesented the named COLOR (e.g. \"red\")
for FRAME (defaults to the current frame)."
(cl-loop with div = (float (car (tty-color-standard-values "#ffffff")))
for x in (tty-color-standard-values (downcase color))
collect (/ x div)))
(defun doom-blend (color1 color2 alpha)
"Blend two colors (hexidecimal strings) together by a coefficient ALPHA (a
float between 0 and 1)"
(when (and color1 color2)
(cond ((and color1 color2 (symbolp color1) (symbolp color2))
(doom-blend (doom-color color1) (doom-color color2) alpha))
((or (listp color1) (listp color2))
(cl-loop for x in color1
when (if (listp color2) (pop color2) color2)
collect (doom-blend x it alpha)))
((and (string-prefix-p "#" color1) (string-prefix-p "#" color2))
(apply (lambda (r g b) (format "#%02x%02x%02x" (* r 255) (* g 255) (* b 255)))
(cl-loop for it in (doom-name-to-rgb color1)
for other in (doom-name-to-rgb color2)
collect (+ (* alpha it) (* other (- 1 alpha))))))
(defun doom-darken (color alpha)
"Darken a COLOR (a hexidecimal string) by a coefficient ALPHA (a float between
0 and 1)."
(cond ((and color (symbolp color))
(doom-darken (doom-color color) alpha))
((listp color)
(cl-loop for c in color collect (doom-darken c alpha)))
((doom-blend color "#000000" (- 1 alpha)))))
(defun doom-lighten (color alpha)
"Brighten a COLOR (a hexidecimal string) by a coefficient ALPHA (a float
between 0 and 1)."
(cond ((and color (symbolp color))
(doom-lighten (doom-color color) alpha))
((listp color)
(cl-loop for c in color collect (doom-lighten c alpha)))
((doom-blend color "#FFFFFF" (- 1 alpha)))))
(defun doom-color (name &optional type)
"Retrieve a specific color named NAME (a symbol) from the current theme."
(let ((colors (if (listp name)
(cdr-safe (assq name doom-themes--colors)))))
(and colors
(cond ((listp colors)
(let ((i (or (plist-get '(256 1 16 2 8 3) type) 0)))
(if (> i (1- (length colors)))
(car (last colors))
(nth i colors))))
(t colors)))))
(defun doom-ref (face prop &optional class)
(let ((spec (or (cdr (assq face doom-themes--faces))
(error "Couldn't find the '%s' face" face))))
(when (memq (car spec) '(quote backquote \`))
(user-error "Can't fetch the literal spec for '%s'" face))
(when class
(setq spec (cdr (assq class spec)))
(unless spec
(error "Couldn't find the '%s' class in the '%s' face"
class face)))
(unless (plist-member spec prop)
(error "Couldn't find the '%s' property in the '%s' face%s"
prop face (if class (format "'s '%s' class" class) "")))
(plist-get spec prop)))
;;; Defining themes
(defun doom-themes-prepare-facelist (custom-faces)
"Return an alist of face definitions for `custom-theme-set-faces'.
Faces in EXTRA-FACES override the default faces."
(declare (pure t) (side-effect-free t))
(setq doom-themes--faces (doom-themes--apply-faces custom-faces))
(mapcar #'doom-themes--build-face doom-themes--faces))
(defun doom-themes-prepare-varlist (vars)
"Return an alist of variable definitions for `custom-theme-set-variables'.
Variables in EXTRA-VARS override the default ones."
(declare (pure t) (side-effect-free t))
(cl-loop for (var val) in (append doom-themes-base-vars vars)
collect `(list ',var ,val)))
(defun doom-themes-set-faces (theme &rest faces)
"Customize THEME (a symbol) with FACES.
If THEME is nil, it applies to all themes you load. FACES is a list of Doom
theme face specs. These is a simplified spec. For example:
(doom-themes-set-faces 'user
'(default :background red :foreground blue)
'(doom-modeline-bar :background (if -modeline-bright modeline-bg highlight))
'(doom-modeline-buffer-file :inherit 'mode-line-buffer-id :weight 'bold)
'(doom-modeline-buffer-path :inherit 'mode-line-emphasis :weight 'bold)
'(doom-modeline-buffer-project-root :foreground green :weight 'bold))"
(declare (indent defun))
(apply #'custom-theme-set-faces
(or theme 'user)
`(let* ((bold ,doom-themes-enable-bold)
(italic ,doom-themes-enable-italic)
,@(cl-loop for (var . val) in doom-themes--colors
collect `(,var ',val)))
(list ,@(mapcar #'doom-themes--build-face faces))))))
(defmacro def-doom-theme (name docstring defs &optional extra-faces extra-vars)
"Define a DOOM theme, named NAME (a symbol)."
(declare (doc-string 2))
(let ((doom-themes--colors defs))
`(let* ((bold doom-themes-enable-bold)
(italic doom-themes-enable-italic)
(setq doom-themes--colors
(list ,@(cl-loop for (var val) in defs
collect `(cons ',var ,val))))
(deftheme ,name ,docstring)
',name ,@(doom-themes-prepare-facelist extra-faces))
',name ,@(doom-themes-prepare-varlist extra-vars))
(unless bold (set-face-bold 'bold nil))
(unless italic (set-face-italic 'italic nil))
(provide-theme ',name))))
(when (and (boundp 'custom-theme-load-path) load-file-name)
(let* ((base (file-name-directory load-file-name))
(dir (expand-file-name "themes/" base)))
(add-to-list 'custom-theme-load-path
(or (and (file-directory-p dir) dir)
(provide 'doom-themes)
;;; doom-themes.el ends here