2022-09-12 18:05:14 +02:00
|
|
|
;;; early-init.el --- Doom's universal bootstrapper -*- lexical-binding: t -*-
|
2022-06-18 15:04:12 +02:00
|
|
|
;;; Commentary:
|
|
|
|
;;
|
2022-09-12 18:05:14 +02:00
|
|
|
;; early-init.el was introduced in Emacs 27.1 and is loaded before init.el, and
|
|
|
|
;; before Emacs initializes its UI or package.el, and before site files are
|
|
|
|
;; loaded. This is good place for startup optimizating, because only here can
|
|
|
|
;; you *prevent* things from loading, rather than turn them off after-the-fact.
|
|
|
|
;; As such, Doom does all its initializing here.
|
2022-06-18 15:04:12 +02:00
|
|
|
;;
|
2022-09-12 18:05:14 +02:00
|
|
|
;; This file is Doom's "universal bootstrapper" for both interactive and
|
|
|
|
;; non-interactive sessions. It's also the heart of its profile bootloader,
|
|
|
|
;; which allows you to switch between Emacs configs on demand using
|
|
|
|
;; `--init-directory DIR' (which was backported from Emacs 29) or `--profile
|
|
|
|
;; NAME` (more about profiles at `https://docs.doomemacs.org/-/developers' or
|
|
|
|
;; docs/developers.org).
|
2022-07-27 12:06:07 +02:00
|
|
|
;;
|
2022-09-12 18:05:14 +02:00
|
|
|
;; In summary, this file is responsible for:
|
|
|
|
;; - Setting up some universal startup optimizations.
|
|
|
|
;; - Determining where `user-emacs-directory' is from one of:
|
|
|
|
;; - `--init-directory DIR' (backported from 29)
|
|
|
|
;; - `--profile PROFILENAME'
|
|
|
|
;; - Do one of the following:
|
|
|
|
;; - Load `doom' and one of `doom-start' or `doom-cli'.
|
|
|
|
;; - Or (if the user is trying to load a non-Doom config) load
|
|
|
|
;; `user-emacs-directory'/early-init.el.
|
2022-06-18 15:04:12 +02:00
|
|
|
;;
|
|
|
|
;;; Code:
|
2018-03-28 00:41:57 -04:00
|
|
|
|
2022-09-06 21:01:46 +02:00
|
|
|
;; PERF: Garbage collection is a big contributor to startup times. This fends it
|
2022-09-12 18:05:14 +02:00
|
|
|
;; off, but will be reset later by `gcmh-mode'. Not resetting it later will
|
2022-09-06 21:01:46 +02:00
|
|
|
;; cause stuttering/freezes.
|
2019-07-21 03:01:15 +02:00
|
|
|
(setq gc-cons-threshold most-positive-fixnum)
|
2018-09-19 00:25:17 +01:00
|
|
|
|
2022-09-13 13:08:37 +02:00
|
|
|
;; PERF: Don't use precious startup time checking mtime on elisp bytecode.
|
|
|
|
;; Ensuring correctness is 'doom sync's job, not the interactive session's.
|
|
|
|
;; Still, stale byte-code will cause *heavy* losses in startup efficiency.
|
|
|
|
(setq load-prefer-newer noninteractive)
|
2022-06-18 15:04:12 +02:00
|
|
|
|
2022-07-27 11:15:30 +02:00
|
|
|
|
2022-07-27 12:05:56 +02:00
|
|
|
;;
|
|
|
|
;;; Bootstrap
|
2022-07-27 11:15:30 +02:00
|
|
|
|
2022-09-13 13:08:37 +02:00
|
|
|
(or
|
|
|
|
;; PERF: `file-name-handler-alist' is consulted often. Unsetting it offers a
|
|
|
|
;; notable saving in startup time.
|
|
|
|
(let (file-name-handler-alist)
|
|
|
|
;; FEAT: First, we process --init-directory and --profile to detect what
|
|
|
|
;; `user-emacs-directory' to load from. I avoid using
|
|
|
|
;; `command-switch-alist' to process --profile and --init-directory because
|
|
|
|
;; it is processed too late to change `user-emacs-directory' in time.
|
|
|
|
|
|
|
|
;; REVIEW: Backported from Emacs 29. Remove when 28 support is dropped.
|
|
|
|
(let ((initdir (or (cadr (member "--init-directory" command-line-args))
|
|
|
|
(getenv-internal "EMACSDIR"))))
|
|
|
|
(when initdir
|
|
|
|
;; FIX: Discard the switch to prevent "invalid option" errors later.
|
|
|
|
(push (cons "--init-directory" (lambda (_) (pop argv))) command-switch-alist)
|
|
|
|
(setq user-emacs-directory (expand-file-name initdir))))
|
|
|
|
;; Initialize a known profile, if requested.
|
|
|
|
(let ((profile (or (cadr (member "--profile" command-line-args))
|
|
|
|
(getenv-internal "DOOMPROFILE"))))
|
|
|
|
(when profile
|
|
|
|
;; FIX: Discard the switch to prevent "invalid option" errors later.
|
|
|
|
(push (cons "--profile" (lambda (_) (pop argv))) command-switch-alist)
|
|
|
|
;; While processing the requested profile, Doom loosely expects
|
|
|
|
;; `user-emacs-directory' to be changed. If it doesn't, then you're using
|
|
|
|
;; profiles.el as a glorified, runtime dir-locals.el (which is fine, if
|
|
|
|
;; intended).
|
|
|
|
(catch 'found
|
|
|
|
(let ((profiles-file (expand-file-name "profiles.el" user-emacs-directory)))
|
|
|
|
(when (file-exists-p profiles-file)
|
|
|
|
(with-temp-buffer
|
|
|
|
(let ((coding-system-for-read 'utf-8-auto))
|
|
|
|
(insert-file-contents profiles-file))
|
|
|
|
(condition-case-unless-debug e
|
|
|
|
(let ((profile-data (cdr (assq (intern profile) (read (current-buffer))))))
|
|
|
|
(dolist (var profile-data (if profile-data (throw 'found t)))
|
|
|
|
(if (eq (car var) 'env)
|
|
|
|
(dolist (env (cdr var)) (setenv (car env) (cdr env)))
|
|
|
|
(set (car var) (cdr var)))))
|
|
|
|
(error (error "Failed to parse profiles.el: %s" (error-message-string e))))))
|
|
|
|
;; If the requested profile isn't in profiles.el, then see if
|
|
|
|
;; $EMACSDIR/profiles/$DOOMPROFILE exists. These are implicit
|
|
|
|
;; profiles, where `emacs --profile foo` will be equivalent to `emacs
|
|
|
|
;; --init-directory $EMACSDIR/profile/foo', if that directory exists.
|
|
|
|
(let ((profile-dir
|
|
|
|
(expand-file-name
|
|
|
|
profile (or (getenv-internal "DOOMPROFILESDIR")
|
|
|
|
(expand-file-name "profiles/" user-emacs-directory)))))
|
|
|
|
(when (file-directory-p profile-dir)
|
|
|
|
(setq user-emacs-directory profile-dir)
|
|
|
|
(throw 'found t)))
|
|
|
|
(user-error "No %S profile found" profile)))
|
|
|
|
(when init-file-debug
|
|
|
|
(message "Selected profile: %s" profile))
|
|
|
|
;; Ensure the selected profile persists through the session
|
|
|
|
(setenv "DOOMPROFILE" profile)))
|
|
|
|
|
|
|
|
;; PERF: When `load'ing or `require'ing files, each permutation of
|
|
|
|
;; `load-suffixes' and `load-file-rep-suffixes' (then `load-suffixes' +
|
|
|
|
;; `load-file-rep-suffixes') is used to locate the file. Each permutation
|
|
|
|
;; is a file op, which is normally very fast, but they can add up over the
|
|
|
|
;; hundreds/thousands of files Emacs needs to load.
|
|
|
|
;;
|
|
|
|
;; To reduce that burden -- and since Doom doesn't load any dynamic modules
|
|
|
|
;; -- I remove `.so' from `load-suffixes' and pass the `must-suffix' arg to
|
|
|
|
;; `load'. See the docs of `load' for details.
|
|
|
|
(or (let ((load-suffixes '(".elc" ".el")))
|
|
|
|
;; Load the heart of Doom Emacs.
|
|
|
|
(if (load (expand-file-name "lisp/doom" user-emacs-directory)
|
|
|
|
'noerror 'nomessage nil 'must-suffix)
|
|
|
|
;; ...and prepare for the rest of the session.
|
|
|
|
(if noninteractive
|
|
|
|
(doom-require 'doom-cli)
|
|
|
|
;; HACK: This advice hijacks Emacs' initfile resolver to replace
|
|
|
|
;; $EMACSDIR/init.el (and ~/.emacs or ~/_emacs) with a
|
|
|
|
;; a Doom-provided init file. Later, this file will be
|
|
|
|
;; generated by 'doom sync' for the active Doom profile;
|
|
|
|
;; `doom-start' is its stand-in until that's implemented.
|
|
|
|
;;
|
|
|
|
;; This effort spares Emacs the overhead of searching for
|
|
|
|
;; initfiles we don't care about, enables savvier hackers to
|
|
|
|
;; use $EMACSDIR as their $DOOMDIR, and gives us an opportunity
|
|
|
|
;; to fall back to a "safe mode", so we can present a more
|
|
|
|
;; user-friendly failure state.
|
|
|
|
(define-advice startup--load-user-init-file (:filter-args (args) init-doom)
|
|
|
|
"Initialize Doom Emacs in an interactive session."
|
|
|
|
(list (lambda ()
|
|
|
|
(file-name-concat doom-core-dir "doom-start"))
|
|
|
|
(lambda ()
|
|
|
|
(file-name-concat doom-profiles-dir "safe-mode" "init.el"))
|
|
|
|
(caddr args))))))
|
|
|
|
;; Failing that, assume we're loading a non-Doom config and prepare.
|
|
|
|
(ignore
|
|
|
|
(setq early-init-file (expand-file-name "early-init" user-emacs-directory)
|
|
|
|
;; I make no assumptions about the config we're about to load, so
|
|
|
|
;; to limit side-effects, undo any leftover optimizations:
|
|
|
|
load-prefer-newer t))))
|
|
|
|
|
|
|
|
;; Then continue on to the config/profile we want to load.
|
|
|
|
(load early-init-file 'noerror 'nomessage nil 'must-suffix))
|
2022-06-18 15:04:12 +02:00
|
|
|
|
|
|
|
;;; early-init.el ends here
|