Skip to content

Commit 5b6b204

Browse files
committed
feat: allow Doom be used as a config bootloader
This allows users to use Doom core to switch between Emacs configs (they don't have to be Doom configs either). Taking after Chemacs, these configs (called "profiles") can be declared in $EMACSDIR/profiles.el or implicitly as directories under $EMACSDIR/profiles/ (symlinks work too). Launch a profile with `emacs --profile foo` or by setting $DOOMPROFILE: `DOOMPROFILE=foo emacs`. An example profiles.el looks like this: ((doomemacs (user-emacs-directory . "~/.config/emacs") (env ("DOOMDIR" . "~/.config/doom"))) (spacemacs (user-emacs-directory . "~/.config/spacemacs")) (prelude (user-emacs-directory . "~/.config/prelude")) (altdoom (user-emacs-directory . "~/.config/doomemacs") (env ("DOOMDIR" . "~/.config/doomprivate1"))) (altdoom2 (user-emacs-directory . "~/.config/doomemacs") (env ("DOOMDIR" . "~/.config/doomprivate2")))) Chemacs users will find the format of this file familiar; the biggest differences are: - Keys are symbols, not strings - There is no, special "default" profile. The fallback profile is the Doom Emacs config doing the bootloading, living in ~/.config/emacs or ~/.emacs.d. If you don't like that, set $DOOMPROFILE in your dotfiles to the name of another profile. WARNING: bin/doom does not understand --profile or $DOOMPROFILE yet. To sync a particular profile, you'll have to run its bin/doom directly, e.g. To sync the "global" doom: ~/.config/emacs/bin/doom sync To sync your "altdoom" (and "altdoom2") profiles: ~/.config/doomemacs/bin/doom sync
1 parent 5108ffc commit 5b6b204

File tree

1 file changed

+59
-23
lines changed

1 file changed

+59
-23
lines changed

early-init.el

Lines changed: 59 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -67,28 +67,64 @@
6767

6868

6969
;;
70-
;;; Bootstrap
70+
;;; Detect `user-emacs-directory'
71+
72+
;; Prevent recursive profile processing, in case you're loading a Doom profile.
73+
(unless (boundp 'doom-version)
74+
;; Not using `command-switch-alist' to process --profile and --init-directory
75+
;; was intentional. `command-switch-alist' is processed too late at startup to
76+
;; change `user-emacs-directory' in time.
7177

72-
(let (;; DEPRECATED Backported from 29. Remove when 27/28 support is removed.
73-
(initdir (cadr (member "--init-directory" command-line-args)))
78+
;; DEPRECATED Backported from 29. Remove this when 27/28 support is removed.
79+
(let ((initdir (or (cadr (member "--init-directory" command-line-args))
80+
(getenv-internal "EMACSDIR"))))
81+
(when initdir
82+
;; Discard the switch to prevent "invalid option" errors later.
83+
(add-to-list 'command-switch-alist (cons "--init-directory" (lambda (_) (pop argv))))
84+
(setq user-emacs-directory initdir)))
7485

75-
initfile)
86+
(let ((profile (or (cadr (member "--profile" command-line-args))
87+
(getenv-internal "DOOMPROFILE"))))
88+
(when profile
89+
;; Discard the switch to prevent "invalid option" errors later.
90+
(add-to-list 'command-switch-alist (cons "--profile" (lambda (_) (pop argv))))
91+
;; Then process the requested profile, which should set
92+
;; `user-emacs-directory'. If it doesn't, then you're essentially using
93+
;; profiles.el as a glorified, runtime dir-locals.el -- which is fine.
94+
(let ((profiles-dir (or (getenv-internal "DOOMPROFILESDIR")
95+
(expand-file-name "profiles/" user-emacs-directory)))
96+
(profiles-file (expand-file-name "profiles.el" user-emacs-directory)))
97+
(if (file-exists-p profiles-file)
98+
(with-temp-buffer
99+
(let ((coding-system-for-read 'utf-8-auto))
100+
(insert-file-contents profiles-file))
101+
(condition-case err
102+
(dolist (var (or (cdr (assq (intern profile) (read (current-buffer))))
103+
(user-error "No %S profile found" profile)))
104+
(if (eq var 'env)
105+
(dolist (env var) (setenv (car env) (cdr env)))
106+
(set (car var) (cdr var))))
107+
(error (error "Failed to parse profiles.el: %s" (error-message-string err)))))
108+
;; If the profile doesn't exist, then use anything in
109+
;; $EMACSDIR/profiles/* as implicit profiles. I.e. If a foo profile
110+
;; doesn't exist, `emacs --profile foo' is equivalent to `emacs
111+
;; --init-directory $EMACSDIR/profile/foo', if the directory exists.
112+
(let ((profile-dir (expand-file-name profile profiles-dir)))
113+
(if (file-directory-p profile-dir)
114+
(setq user-emacs-directory profile-dir)
115+
(user-error "No profiles.el to look up %S in" profile))))))))
76116

77-
;; But discard the switches later to prevent "invalid option" errors.
78-
(when initdir
79-
(add-to-list 'command-switch-alist (cons "--init-directory" (lambda (_) (pop argv)))))
80117

81-
;; Detect emacs directory
82-
(setq user-emacs-directory
83-
(cond (initdir (expand-file-name initdir))
84-
((getenv-internal "EMACSDIR"))
85-
(user-emacs-directory)))
118+
;;
119+
;;; Bootstrap
86120

121+
;; Let er rip
122+
(let (init-file)
87123
;; Load the heart of Doom Emacs
88124
(if (load (expand-file-name "core/core" user-emacs-directory) t t)
89125
;; ...and prepare it for an interactive session.
90-
(setq initfile (expand-file-name "core-start" doom-core-dir))
91-
;; ...but if that fails, then assume this isn't a Doom config.
126+
(setq init-file (expand-file-name "core-start" doom-core-dir))
127+
;; ...but if that fails, then this is likely not a Doom config.
92128
(setq early-init-file (expand-file-name "early-init" user-emacs-directory))
93129
(load early-init-file t t))
94130

@@ -99,19 +135,19 @@
99135
;; ~/.emacs and ~/_emacs. And skips ~/.emacs.d/init.el, which won't exist if
100136
;; you're using Doom (fyi: doom hackers or chemacs users could then use
101137
;; $EMACSDIR as their $DOOMDIR, if they wanted).
102-
;; - Later, 'doom sync' will dynamically generate its bootstrap file, which is
103-
;; important for Doom's soon-to-be profile system (which can replace Chemacs).
104-
;; Until then, we'll use core/core-start.el.
105-
;; - A "fallback" initfile can be trivially specified, in case the bootstrapper
106-
;; is missing (if the user hasn't run 'doom sync' or is a first-timer). This
107-
;; is an opportunity to display a "safe mode" environment that's less
108-
;; intimidating and more helpful than the broken state errors would've left
109-
;; Emacs in, otherwise.
138+
;; - Later, 'doom sync' will dynamically generate its bootstrap file, which
139+
;; will be important for Doom's profile system later. Until then, we'll use
140+
;; core/core-start.el.
141+
;; - A "fallback" initfile can be trivially specified, in case the
142+
;; bootstrapper is missing (if the user hasn't run 'doom sync' or is a
143+
;; first-timer). This is an opportunity to display a "safe mode" environment
144+
;; that's less intimidating and more helpful than the broken state errors
145+
;; would've left Emacs in, otherwise.
110146
;; - A generated config allows for a file IO optimized startup.
111147
(define-advice startup--load-user-init-file (:filter-args (args) init-doom)
112148
"Initialize Doom Emacs in an interactive session."
113149
(list (lambda ()
114-
(or initfile
150+
(or init-file
115151
(expand-file-name "init.el" user-emacs-directory)))
116152
nil ; TODO Replace with safe mode initfile
117153
(caddr args))))

0 commit comments

Comments
 (0)