Split core-packages into two (packages & modules)

+ Move doom-initialize et co into core.el
+ Lazy load core-packages
+ load! has been moved into core-lib
+ Added FILE! and DIR! macros
+ Fix package! not returning correct value when package is disabled
+ Remove :disabled support for def-package-hook! officially
This commit is contained in:
Henrik Lissner 2018-06-11 23:18:15 +02:00
parent f70c9ebb71
commit 0741c8851a
No known key found for this signature in database
GPG key ID: 5F6C0EA160557395
5 changed files with 681 additions and 654 deletions

View file

@ -550,3 +550,19 @@ calls."
(print! (bold (green "Finished!"))) (print! (bold (green "Finished!")))
(if success (doom-delete-autoloads-file doom-package-autoload-file)) (if success (doom-delete-autoloads-file doom-package-autoload-file))
success))))) success)))))
;;
;; Make package.el cooperate with Doom
;;
;; Updates QUELPA after deleting a package
;;;###autoload
(advice-add #'package-delete :after #'doom*package-delete)
;; Replace with Doom variants
;;;###autoload
(advice-add #'package-autoremove :override #'doom//packages-autoremove)
;;;###autoload
(advice-add #'package-install-selected-packages :override #'doom//packages-install)

View file

@ -65,6 +65,16 @@ This is used by `associate!', `file-exists-p!' and `project-file-exists-p!'."
collect hook collect hook
else collect (intern (format "%s-hook" (symbol-name hook))))) else collect (intern (format "%s-hook" (symbol-name hook)))))
(defun doom--assert-stage-p (stage macro)
(cl-assert (eq stage doom--stage)
nil
"Found %s call in non-%s.el file (%s)"
macro (symbol-name stage)
(let ((path (FILE!)))
(if (file-in-directory-p path doom-emacs-dir)
(file-relative-name path doom-emacs-dir)
(abbreviate-file-name path)))))
;; ;;
;; Functions ;; Functions
@ -178,6 +188,18 @@ MATCH is a string regexp. Only entries that match it will be included."
;; Macros ;; Macros
;; ;;
(defmacro FILE! ()
"TODO"
`(cond ((bound-and-true-p byte-compile-current-file))
((stringp (car-safe current-load-list)) (car current-load-list))
(load-file-name)
(buffer-file-name)))
(defmacro DIR! ()
"TODO"
`(let ((file (FILE!)))
(and file (file-name-directory file))))
(defmacro λ! (&rest body) (defmacro λ! (&rest body)
"A shortcut for inline interactive lambdas." "A shortcut for inline interactive lambdas."
(declare (doc-string 1)) (declare (doc-string 1))
@ -190,7 +212,7 @@ MATCH is a string regexp. Only entries that match it will be included."
compilation. This will no-op on features that have been disabled by the user." compilation. This will no-op on features that have been disabled by the user."
(declare (indent defun) (debug t)) (declare (indent defun) (debug t))
(unless (and (symbolp targets) (unless (and (symbolp targets)
(memq targets doom-disabled-packages)) (memq targets (bound-and-true-p doom-disabled-packages)))
(list (if (or (not (bound-and-true-p byte-compile-current-file)) (list (if (or (not (bound-and-true-p byte-compile-current-file))
(dolist (next (doom-enlist targets)) (dolist (next (doom-enlist targets))
(if (symbolp next) (if (symbolp next)
@ -422,5 +444,23 @@ KEY is a key string or vector. It is *not* piped through `kbd'."
(push `(define-key map ,key ,def) forms))) (push `(define-key map ,key ,def) forms)))
(nreverse forms))))) (nreverse forms)))))
(defmacro load! (filename &optional path noerror)
"Load a file relative to the current executing file (`load-file-name').
FILENAME is either a file path string or a form that should evaluate to such a
string at run time. PATH is where to look for the file (a string representing a
directory path). If omitted, the lookup is relative to either `load-file-name',
`byte-compile-current-file' or `buffer-file-name' (checked in that order).
If NOERROR is non-nil, don't throw an error if the file doesn't exist."
(unless path
(setq path (or (DIR!)
(error "Could not detect path to look for '%s' in"
filename))))
`(load ,(if path
`(expand-file-name ,filename ,path)
filename)
,noerror ,(not doom-debug-mode)))
(provide 'core-lib) (provide 'core-lib)
;;; core-lib.el ends here ;;; core-lib.el ends here

384
core/core-modules.el Normal file
View file

@ -0,0 +1,384 @@
;;; core-modules.el --- module & package management system -*- lexical-binding: t; -*-
(defvar doom-init-modules-p nil
"Non-nil if `doom-initialize-modules' has run.")
(defvar doom-modules ()
"A hash table of enabled modules. Set by `doom-initialize-modules'.")
(defvar doom-modules-dirs
(list (expand-file-name "modules/" doom-private-dir) doom-modules-dir)
"A list of module root directories. Order determines priority.")
(defvar doom--current-module nil)
;;
;; Bootstrap API
;;
(defun doom-initialize-modules (&optional force-p)
"Loads the init.el in `doom-private-dir' and sets up hooks for a healthy
session of Dooming. Will noop if used more than once, unless FORCE-P is
non-nil."
(when (or force-p (not doom-init-modules-p))
;; Set `doom-init-modules-p' early, so `doom-pre-init-hook' won't infinitely
;; recurse by accident if any of them need `doom-initialize-modules'.
(setq doom-init-modules-p t)
(when doom-private-dir
(load (expand-file-name "init" doom-private-dir)
'noerror 'nomessage))))
(defun doom-initialize-autoloads (file)
"Tries to load FILE (an autoloads file). Return t on success, nil otherwise."
(condition-case-unless-debug e
(load (file-name-sans-extension file) 'noerror 'nomessage)
('error
(message "Autoload error: %s -> %s"
(car e) (error-message-string e)))))
;;
;; Module API
;;
(defun doom-module-p (category module)
"Returns t if CATEGORY MODULE is enabled (ie. present in `doom-modules')."
(and (hash-table-p doom-modules)
(gethash (cons category module) doom-modules)
t))
(defun doom-module-get (category module &optional property)
"Returns the plist for CATEGORY MODULE. Gets PROPERTY, specifically, if set."
(when-let* ((plist (gethash (cons category module) doom-modules)))
(if property
(plist-get plist property)
plist)))
(defun doom-module-put (category module property value &rest rest)
"Set a PROPERTY for CATEGORY MODULE to VALUE. PLIST should be additional pairs
of PROPERTY and VALUEs."
(when-let* ((plist (doom-module-get category module)))
(plist-put plist property value)
(when rest
(when (cl-oddp (length rest))
(signal 'wrong-number-of-arguments (list (length rest))))
(while rest
(plist-put rest (pop rest) (pop rest))))
(puthash (cons category module) plist doom-modules)))
(defun doom-module-set (category module &rest plist)
"Enables a module by adding it to `doom-modules'.
CATEGORY is a keyword, module is a symbol, PLIST is a plist that accepts the
following properties:
:flags [SYMBOL LIST] list of enabled category flags
:path [STRING] path to category root directory
Example:
(doom-module-set :lang 'haskell :flags '(+intero))"
(when plist
(let ((old-plist (doom-module-get category module)))
(unless (plist-member plist :flags)
(plist-put plist :flags (plist-get old-plist :flags)))
(unless (plist-member plist :path)
(plist-put plist :path (or (plist-get old-plist :path)
(doom-module-locate-path category module))))))
(let ((key (cons category module)))
(puthash key plist doom-modules)))
(defun doom-module-path (category module &optional file)
"Like `expand-file-name', but expands FILE relative to CATEGORY (keywordp) and
MODULE (symbol).
If the category isn't enabled this will always return nil. For finding disabled
modules use `doom-module-locate-path'."
(let ((path (doom-module-get category module :path)))
(if file (expand-file-name file path)
path)))
(defun doom-module-locate-path (category &optional module file)
"Searches `doom-modules-dirs' to find the path to a module.
CATEGORY is a keyword (e.g. :lang) and MODULE is a symbol (e.g. 'python). FILE
is a string that will be appended to the resulting path. If no path exists, this
returns nil, otherwise an absolute path.
This doesn't require modules to be enabled. For enabled modules us
`doom-module-path'."
(when (keywordp category)
(setq category (substring (symbol-name category) 1)))
(when (and module (symbolp module))
(setq module (symbol-name module)))
(cl-loop for default-directory in doom-modules-dirs
for path = (concat category "/" module "/" file)
if (file-exists-p path)
return (expand-file-name path)))
(defun doom-module-from-path (&optional path)
"Returns a cons cell (CATEGORY . MODULE) derived from PATH (a file path)."
(or doom--current-module
(when path
(save-match-data
(setq path (file-truename path))
(when (string-match "/modules/\\([^/]+\\)/\\([^/]+\\)/.*$" path)
(when-let* ((module (match-string 1 path))
(submodule (match-string 2 path)))
(cons (intern (concat ":" module))
(intern submodule))))))))
(defun doom-module-load-path ()
"Return a list of absolute file paths to activated modules."
(append (cl-loop for plist being the hash-values of (doom-modules)
collect (plist-get plist :path))
(list doom-private-dir)))
(defun doom-modules (&optional refresh-p)
"Minimally initialize `doom-modules' (a hash table) and return it."
(or (unless refresh-p doom-modules)
(let ((noninteractive t)
doom-init-modules-p)
(message "Initializing modules")
(doom-initialize-modules t)
doom-modules)))
;;
;; Use-package modifications
;;
(autoload 'use-package "use-package-core" nil nil t)
;; Adds the :after-call custom keyword to `use-package' (and consequently,
;; `def-package!'). :after-call takes a symbol or list of symbols. These symbols
;; can be functions to hook variables.
;;
;; (use-package X :after-call find-file-hook)
;;
;; This will load X on the first invokation of `find-file-hook' (then it will
;; remove itself from the hook/function).
(defvar doom--deferred-packages-alist ())
(after! use-package-core
(add-to-list 'use-package-deferring-keywords :after-call nil #'eq)
(setq use-package-keywords
(use-package-list-insert :after-call use-package-keywords :after))
(defalias 'use-package-normalize/:after-call
'use-package-normalize-symlist)
(defun use-package-handler/:after-call (name-symbol _keyword hooks rest state)
(let ((fn (intern (format "doom|transient-hook--load-%s" name-symbol)))
(hooks (delete-dups hooks)))
(if (plist-get state :demand)
(use-package-process-keywords name rest state)
(use-package-concat
`((fset ',fn
(lambda (&rest _)
(require ',name-symbol)
(dolist (hook (cdr (assq ',name-symbol doom--deferred-packages-alist)))
(if (functionp hook)
(advice-remove hook #',fn)
(remove-hook hook #',fn)))
(map-delete doom--deferred-packages-alist ',name-symbol)
(fmakunbound ',fn))))
(cl-mapcan (lambda (hook)
(if (functionp hook)
`((advice-add #',hook :before #',fn))
`((add-hook ',hook #',fn))))
hooks)
`((map-put doom--deferred-packages-alist
',name-symbol
'(,@hooks ,@(cdr (assq name-symbol doom--deferred-packages-alist)))))
(use-package-process-keywords name rest state))))))
;;
;; Module config macros
;;
(defmacro doom! (&rest modules)
"Bootstraps DOOM Emacs and its modules.
The bootstrap process involves making sure the essential directories exist, core
packages are installed, `doom-autoload-file' is loaded, `doom-packages-file'
cache exists (and is loaded) and, finally, loads your private init.el (which
should contain your `doom!' block).
If the cache exists, much of this function isn't run, which substantially
reduces startup time.
The overall load order of Doom is as follows:
~/.emacs.d/init.el
~/.emacs.d/core/core.el
`doom-pre-init-hook'
~/.doom.d/init.el
Module init.el files
`doom-init-hook'
Module config.el files
~/.doom.d/config.el
`after-init-hook'
`emacs-startup-hook'
`doom-post-init-hook' (at end of `emacs-startup-hook')
Module load order is determined by your `doom!' block. See `doom-modules-dirs'
for a list of all recognized module trees. Order defines precedence (from most
to least)."
(let ((doom-modules
(make-hash-table :test #'equal
:size (if modules (length modules) 100)
:rehash-threshold 1.0))
category
init-forms config-forms)
(dolist (m modules)
(cond ((keywordp m) (setq category m))
((not category) (error "No module category specified for %s" m))
((let* ((module (if (listp m) (car m) m))
(flags (if (listp m) (cdr m)))
(path (doom-module-locate-path category module)))
(if (not path)
(message "Couldn't find the %s %s module" category module)
(let ((key (cons category module)))
(doom-module-set category module :flags flags :path path)
(push `(let ((doom--current-module ',key))
(load! "init" ,path t))
init-forms)
(push `(let ((doom--current-module ',key))
(load! "config" ,path t))
config-forms)))))))
`(let (file-name-handler-alist)
(setq doom-modules ',doom-modules)
,@(nreverse init-forms)
(run-hooks 'doom-init-hook)
(unless noninteractive
(let ((doom--stage 'config))
,@(nreverse config-forms)
(when doom-private-dir
(load ,(expand-file-name "config" doom-private-dir)
t (not doom-debug-mode))))))))
(defmacro def-package! (name &rest plist)
"A thin wrapper around `use-package'."
;; Ignore package if NAME is in `doom-disabled-packages'
(when (and (memq name (bound-and-true-p doom-disabled-packages))
(not (memq :disabled plist)))
(setq plist `(:disabled t ,@plist)))
;; If byte-compiling, ignore this package if it doesn't meet the condition.
;; This avoids false-positive load errors.
(unless (and (bound-and-true-p byte-compile-current-file)
(or (and (plist-member plist :if) (not (eval (plist-get plist :if) t)))
(and (plist-member plist :when) (not (eval (plist-get plist :when) t)))
(and (plist-member plist :unless) (eval (plist-get plist :unless) t))))
`(use-package ,name ,@plist)))
(defmacro def-package-hook! (package when &rest body)
"Reconfigures a package's `def-package!' block.
Only use this macro in a module's init.el file.
Under the hood, this uses use-package's `use-package-inject-hooks'.
PACKAGE is a symbol; the package's name.
WHEN should be one of the following:
:pre-init :post-init :pre-config :post-config
WARNING: If :pre-init or :pre-config hooks return nil, the original
`def-package!''s :init/:config block (respectively) is overwritten, so remember
to have them return non-nil (or exploit that to overwrite Doom's config)."
(declare (indent defun))
(doom--assert-stage-p 'init #'package!)
(unless (memq when '(:pre-init :post-init :pre-config :post-config))
(error "'%s' isn't a valid hook for def-package-hook!" when))
`(progn
(setq use-package-inject-hooks t)
(add-hook!
',(intern (format "use-package--%s--%s-hook"
package
(substring (symbol-name when) 1)))
,@body)))
(defmacro require! (category module &rest plist)
"Loads the module specified by CATEGORY (a keyword) and MODULE (a symbol)."
(let ((doom-modules (copy-hash-table doom-modules)))
(apply #'doom-module-set category module
(mapcar #'eval plist))
(let ((module-path (doom-module-locate-path category module)))
(if (directory-name-p module-path)
`(condition-case-unless-debug ex
(let ((doom--current-module ',(cons category module)))
(load! "init" ,module-path :noerror)
(let ((doom--stage 'config))
(load! "config" ,module-path :noerror)))
('error
(lwarn 'doom-modules :error
"%s in '%s %s' -> %s"
(car ex) ,category ',module
(error-message-string ex))))
(warn 'doom-modules :warning "Couldn't find module '%s %s'"
category module)))))
(defmacro featurep! (module &optional submodule flag)
"Returns t if MODULE SUBMODULE is enabled. If FLAG is provided, returns t if
MODULE SUBMODULE has FLAG enabled.
(featurep! :config default)
Module FLAGs are set in your config's `doom!' block, typically in
~/.emacs.d/init.el. Like so:
:config (default +flag1 -flag2)
When this macro is used from inside a module, MODULE and SUBMODULE can be
omitted. eg. (featurep! +flag1)"
(unless submodule
(let* ((path (FILE!))
(module-pair (doom-module-from-path path)))
(unless module-pair
(error "featurep! couldn't detect what module its in! (in %s)" path))
(setq flag module
module (car module-pair)
submodule (cdr module-pair))))
(if flag
(and (memq flag (doom-module-get module submodule :flags)) t)
(doom-module-p module submodule)))
;;
;; Cross-module configuration
;;
;; I needed a way to reliably cross-configure modules without littering my
;; modules with `after!' blocks or testing whether they were enabled, so I wrote
;; `set!'. If a setting doesn't exist at runtime, the `set!' call is ignored and
;; its arguments are left unevaluated (and entirely omitted when byte-compiled).
(defmacro def-setting! (keyword arglist &optional docstring &rest forms)
"Define a setting. Like `defmacro', this should return a form to be executed
when called with `set!'. FORMS are not evaluated until `set!' calls it.
See `doom/describe-setting' for a list of available settings.
Do not use this for configuring Doom core."
(declare (indent defun) (doc-string 3))
(or (keywordp keyword)
(signal 'wrong-type-argument (list 'keywordp keyword)))
`(fset ',(intern (format "doom--set%s" keyword))
(lambda ,arglist ,docstring ,@forms)))
(defmacro set! (keyword &rest values)
"Set an option defined by `def-setting!'. Skip if doesn't exist. See
`doom/describe-setting' for a list of available settings.
VALUES doesn't get evaluated if the KEYWORD setting doesn't exist."
(declare (indent defun))
(let ((fn (intern-soft (format "doom--set%s" keyword))))
(if (and fn (fboundp fn))
(apply fn values)
(when doom-debug-mode
(message "No setting found for %s" keyword)
nil))))
(provide 'core-modules)
;;; core-modules.el ends here

View file

@ -1,4 +1,4 @@
;;; core-packages.el --- package management system -*- lexical-binding: t; -*- ;;; core/core-packages.el -*- lexical-binding: t; -*-
;; Emacs package management is opinionated, and so am I. I've bound together ;; Emacs package management is opinionated, and so am I. I've bound together
;; `use-package', `quelpa' and package.el to create my own, rolling-release, ;; `use-package', `quelpa' and package.el to create my own, rolling-release,
@ -39,22 +39,6 @@
;; ;;
;; See core/autoload/packages.el for more functions. ;; See core/autoload/packages.el for more functions.
(defvar doom-init-p nil
"Non-nil if `doom-initialize' has run.")
(defvar doom-init-modules-p nil
"Non-nil if `doom-initialize-modules' has run.")
(defvar doom-init-time nil
"The time it took, in seconds, for DOOM Emacs to initialize.")
(defvar doom-modules ()
"A hash table of enabled modules. Set by `doom-initialize-modules'.")
(defvar doom-modules-dirs
(list (expand-file-name "modules/" doom-private-dir) doom-modules-dir)
"A list of module root directories. Order determines priority.")
(defvar doom-packages () (defvar doom-packages ()
"A list of enabled packages. Each element is a sublist, whose CAR is the "A list of enabled packages. Each element is a sublist, whose CAR is the
package's name as a symbol, and whose CDR is the plist supplied to its package's name as a symbol, and whose CDR is the plist supplied to its
@ -67,30 +51,7 @@ missing) and shouldn't be deleted.")
(defvar doom-disabled-packages () (defvar doom-disabled-packages ()
"A list of packages that should be ignored by `def-package!'.") "A list of packages that should be ignored by `def-package!'.")
(defvar doom-site-load-path load-path (setq package--init-file-ensured t
"The starting load-path, before it is altered by `doom-initialize'.")
(defvar doom-autoload-file (concat doom-local-dir "autoloads.el")
"Where `doom//reload-doom-autoloads' will generate its core autoloads file.")
(defvar doom-package-autoload-file (concat doom-local-dir "autoloads.pkg.el")
"Where `doom//reload-package-autoloads' will generate its package.el autoloads
file.")
(defvar doom-reload-hook nil
"A list of hooks to run when `doom//reload-load-path' is called.")
(defvar doom-emacs-changed-p nil
"If non-nil, the running version of Emacs is different from the first time
Doom was setup, which can cause problems.")
(defvar doom--current-module nil)
(defvar doom--refreshed-p nil)
(defvar doom--stage 'init)
;;
(setq autoload-compute-prefixes nil
package--init-file-ensured t
package-user-dir (expand-file-name "elpa" doom-packages-dir) package-user-dir (expand-file-name "elpa" doom-packages-dir)
package-enable-at-startup nil package-enable-at-startup nil
package-archives package-archives
@ -100,27 +61,13 @@ Doom was setup, which can cause problems.")
;; I omit Marmalade because its packages are manually submitted rather ;; I omit Marmalade because its packages are manually submitted rather
;; than pulled, so packages are often out of date with upstream. ;; than pulled, so packages are often out of date with upstream.
;; security settings
gnutls-verify-error (not (getenv "INSECURE")) ; you shouldn't use this
tls-checktrust gnutls-verify-error
tls-program (list "gnutls-cli --x509cafile %t -p %p %h"
;; compatibility fallbacks
"gnutls-cli -p %p %h"
"openssl s_client -connect %h:%p -no_ssl2 -no_ssl3 -ign_eof")
use-package-verbose doom-debug-mode
use-package-minimum-reported-time (if doom-debug-mode 0 0.1)
;; Don't track MELPA, we'll use package.el for that ;; Don't track MELPA, we'll use package.el for that
quelpa-checkout-melpa-p nil quelpa-checkout-melpa-p nil
quelpa-update-melpa-p nil quelpa-update-melpa-p nil
quelpa-melpa-recipe-stores nil quelpa-melpa-recipe-stores nil
quelpa-self-upgrade-p nil quelpa-self-upgrade-p nil
quelpa-verbose doom-debug-mode quelpa-verbose doom-debug-mode
quelpa-dir (expand-file-name "quelpa" doom-packages-dir) quelpa-dir (expand-file-name "quelpa" doom-packages-dir))
byte-compile-verbose doom-debug-mode
byte-compile-warnings '(not free-vars unresolved noruntime lexical make-local))
;; accommodate INSECURE setting ;; accommodate INSECURE setting
(unless gnutls-verify-error (unless gnutls-verify-error
@ -129,199 +76,9 @@ Doom was setup, which can cause problems.")
;; ;;
;; Helpers 'n hooks ;; Bootstrapper
;; ;;
(defun doom--assert-stage-p (stage macro)
(cl-assert (eq stage doom--stage)
nil
"Found %s call in non-%s.el file (%s)"
macro (symbol-name stage)
(if (file-in-directory-p load-file-name doom-emacs-dir)
(file-relative-name load-file-name doom-emacs-dir)
(abbreviate-file-name load-file-name))))
(defun doom|display-benchmark (&optional return-p)
"Display a benchmark, showing number of packages and modules, and how quickly
they were loaded at startup.
If RETURN-P, return the message as a string instead of displaying it."
(funcall (if return-p #'format #'message)
"Doom loaded %s packages across %d modules in %.03fs"
(length package-activated-list)
(if doom-modules (hash-table-count doom-modules) 0)
(or doom-init-time
(setq doom-init-time (float-time (time-subtract (current-time) before-init-time))))))
(defun doom|post-init ()
"Run `doom-post-init-hook'. That's all."
(run-hooks 'doom-post-init-hook))
(defun doom|run-all-startup-hooks ()
"Run all startup Emacs hooks. Meant to be executed after starting Emacs with
-q or -Q, for example:
emacs -Q -l init.el -f doom|run-all-startup-hooks"
(run-hooks 'after-init-hook 'delayed-warnings-hook
'emacs-startup-hook 'term-setup-hook
'window-setup-hook))
;;
;; Bootstrap helpers
;;
(defvar doom--last-emacs-file (concat doom-local-dir "emacs-version.el"))
(defvar doom--last-emacs-version nil)
(defun doom-ensure-same-emacs-version-p ()
"Do an Emacs version check and set `doom-emacs-changed-p' if it has changed."
(if (load doom--last-emacs-file 'noerror 'nomessage 'nosuffix)
(setq doom-emacs-changed-p
(not (equal emacs-version doom--last-emacs-version)))
(with-temp-file doom--last-emacs-file
(princ `(setq doom--last-emacs-version ,(prin1-to-string emacs-version))
(current-buffer))))
(cond ((not doom-emacs-changed-p))
((y-or-n-p
(format
(concat "Your version of Emacs has changed from %s to %s, which may cause incompatibility\n"
"issues. Please run `bin/doom compile :plugins` afterwards to resolve any problems.\n\n"
"Continue?")
doom--last-emacs-version
emacs-version))
(delete-file doom--last-emacs-file))
(noninteractive (error "Aborting"))
((kill-emacs))))
(defun doom-ensure-packages-initialized (&optional force-p)
"Make sure package.el is initialized."
(when (or force-p (not (bound-and-true-p package--initialized)))
(require 'package)
(setq package-activated-list nil
package--initialized nil)
(let (byte-compile-warnings)
(condition-case _
(quiet! (package-initialize))
('error (package-refresh-contents)
(setq doom--refreshed-p t)
(package-initialize))))))
(defun doom-ensure-core-packages ()
"Make sure `doom-core-packages' are installed."
(when-let* ((core-packages (cl-remove-if #'package-installed-p doom-core-packages)))
(message "Installing core packages")
(unless doom--refreshed-p
(package-refresh-contents))
(dolist (package core-packages)
(let ((inhibit-message t))
(package-install package))
(if (package-installed-p package)
(message "✓ Installed %s" package)
(error "✕ Couldn't install %s" package)))
(message "Installing core packages...done")))
(defun doom-ensure-core-directories ()
"Make sure all Doom's essential local directories (in and including
`doom-local-dir') exist."
(dolist (dir (list doom-local-dir doom-etc-dir doom-cache-dir doom-packages-dir))
(unless (file-directory-p dir)
(make-directory dir t))))
(defun doom-delete-autoloads-file (file)
"Delete FILE (an autoloads file), and delete the accompanying *.elc file, if
it exists."
(cl-check-type file string)
(when (file-exists-p file)
(delete-file file)
(ignore-errors (delete-file (byte-compile-dest-file file)))
(message "Deleted old %s" (file-name-nondirectory file))))
;;
;; Bootstrap API
;;
(defun doom-initialize (&optional force-p)
"Bootstrap Doom, if it hasn't already (or if FORCE-P is non-nil).
The bootstrap process involves making sure 1) the essential directories exist,
2) the core packages are installed, 3) `doom-autoload-file' and
`doom-package-autoload-file' exist and have been loaded, and 4) Doom's core
files are loaded.
If the cache exists, much of this function isn't run, which substantially
reduces startup time.
The overall load order of Doom is as follows:
~/.emacs.d/init.el
~/.emacs.d/core/core.el
`doom-pre-init-hook'
~/.doom.d/init.el
Module init.el files
`doom-init-hook'
Module config.el files
~/.doom.d/config.el
`after-init-hook'
`emacs-startup-hook'
`doom-post-init-hook' (at end of `emacs-startup-hook')
Module load order is determined by your `doom!' block. See `doom-modules-dirs'
for a list of all recognized module trees. Order defines precedence (from most
to least)."
(when (or force-p (not doom-init-p))
;; Set this to prevent infinite recursive calls to `doom-initialize'
(setq doom-init-p t)
;; `doom-autoload-file' tells Emacs where to load all its autoloaded
;; functions from. This includes everything in core/autoload/*.el and all
;; the autoload files in your enabled modules.
(when (or force-p (not (doom-initialize-autoloads doom-autoload-file)))
(doom-ensure-core-directories)
(doom-ensure-same-emacs-version-p)
(doom-ensure-packages-initialized force-p)
(doom-ensure-core-packages)
;; Regenerate `doom-autoload-file', which tells Doom where to find all its
;; module autoloaded functions.
(unless (or force-p noninteractive)
(user-error "Your doom autoloads are missing! Run `bin/doom refresh' to regenerate them")))
;; Loads `doom-package-autoload-file', which caches `load-path',
;; `auto-mode-alist', `Info-directory-list', `doom-disabled-packages' and
;; `package-activated-list'. A big reduction in startup time.
(unless (or force-p
(doom-initialize-autoloads doom-package-autoload-file)
noninteractive)
(user-error "Your package autoloads are missing! Run `bin/doom refresh' to regenerate them")))
;; Initialize Doom core
(unless noninteractive
(add-hook! 'emacs-startup-hook
#'(doom|post-init doom|display-benchmark))
(require 'core-os)
(require 'core-ui)
(require 'core-editor)
(require 'core-projects)
(require 'core-keybinds)))
(defun doom-initialize-modules (&optional force-p)
"Loads the init.el in `doom-private-dir' and sets up hooks for a healthy
session of Dooming. Will noop if used more than once, unless FORCE-P is
non-nil."
(when (or force-p (not doom-init-modules-p))
;; Set `doom-init-modules-p' early, so `doom-pre-init-hook' won't infinitely
;; recurse by accident if any of them need `doom-initialize-modules'.
(setq doom-init-modules-p t)
(when doom-private-dir
(load (expand-file-name "init" doom-private-dir)
'noerror 'nomessage))))
(defun doom-initialize-autoloads (file)
"Tries to load FILE (an autoloads file). Return t on success, nil otherwise."
(condition-case-unless-debug e
(load (file-name-sans-extension file) 'noerror 'nomessage)
('error
(message "Autoload error: %s -> %s"
(car e) (error-message-string e)))))
(defun doom-initialize-packages (&optional force-p) (defun doom-initialize-packages (&optional force-p)
"Ensures that Doom's package management system, package.el and quelpa are "Ensures that Doom's package management system, package.el and quelpa are
initialized, and `doom-packages', `packages-alist' and `quelpa-cache' are initialized, and `doom-packages', `packages-alist' and `quelpa-cache' are
@ -384,334 +141,35 @@ them."
;; ;;
;; Module API ;; Package API
;; ;;
(defun doom-module-p (category module) (defun doom-ensure-packages-initialized (&optional force-p)
"Returns t if CATEGORY MODULE is enabled (ie. present in `doom-modules')." "Make sure package.el is initialized."
(and (hash-table-p doom-modules) (when (or force-p (not (bound-and-true-p package--initialized)))
(gethash (cons category module) doom-modules) (require 'package)
t)) (setq package-activated-list nil
package--initialized nil)
(let (byte-compile-warnings)
(condition-case _
(quiet! (package-initialize))
('error (package-refresh-contents)
(setq doom--refreshed-p t)
(package-initialize))))))
(defun doom-module-get (category module &optional property) (defun doom-ensure-core-packages ()
"Returns the plist for CATEGORY MODULE. Gets PROPERTY, specifically, if set." "Make sure `doom-core-packages' are installed."
(when-let* ((plist (gethash (cons category module) doom-modules))) (when-let* ((core-packages (cl-remove-if #'package-installed-p doom-core-packages)))
(if property (message "Installing core packages")
(plist-get plist property) (unless doom--refreshed-p
plist))) (package-refresh-contents))
(dolist (package core-packages)
(defun doom-module-put (category module property value &rest rest) (let ((inhibit-message t))
"Set a PROPERTY for CATEGORY MODULE to VALUE. PLIST should be additional pairs (package-install package))
of PROPERTY and VALUEs." (if (package-installed-p package)
(when-let* ((plist (doom-module-get category module))) (message "✓ Installed %s" package)
(plist-put plist property value) (error "✕ Couldn't install %s" package)))
(when rest (message "Installing core packages...done")))
(when (cl-oddp (length rest))
(signal 'wrong-number-of-arguments (list (length rest))))
(while rest
(plist-put rest (pop rest) (pop rest))))
(puthash (cons category module) plist doom-modules)))
(defun doom-module-set (category module &rest plist)
"Enables a module by adding it to `doom-modules'.
CATEGORY is a keyword, module is a symbol, PLIST is a plist that accepts the
following properties:
:flags [SYMBOL LIST] list of enabled category flags
:path [STRING] path to category root directory
Example:
(doom-module-set :lang 'haskell :flags '(+intero))"
(when plist
(let ((old-plist (doom-module-get category module)))
(unless (plist-member plist :flags)
(plist-put plist :flags (plist-get old-plist :flags)))
(unless (plist-member plist :path)
(plist-put plist :path (or (plist-get old-plist :path)
(doom-module-locate-path category module))))))
(let ((key (cons category module)))
(puthash key plist doom-modules)))
(defun doom-module-path (category module &optional file)
"Like `expand-file-name', but expands FILE relative to CATEGORY (keywordp) and
MODULE (symbol).
If the category isn't enabled this will always return nil. For finding disabled
modules use `doom-module-locate-path'."
(let ((path (doom-module-get category module :path)))
(if file (expand-file-name file path)
path)))
(defun doom-module-locate-path (category &optional module file)
"Searches `doom-modules-dirs' to find the path to a module.
CATEGORY is a keyword (e.g. :lang) and MODULE is a symbol (e.g. 'python). FILE
is a string that will be appended to the resulting path. If no path exists, this
returns nil, otherwise an absolute path.
This doesn't require modules to be enabled. For enabled modules us
`doom-module-path'."
(when (keywordp category)
(setq category (substring (symbol-name category) 1)))
(when (and module (symbolp module))
(setq module (symbol-name module)))
(cl-loop for default-directory in doom-modules-dirs
for path = (concat category "/" module "/" file)
if (file-exists-p path)
return (expand-file-name path)))
(defun doom-module-from-path (&optional path)
"Returns a cons cell (CATEGORY . MODULE) derived from PATH (a file path)."
(or doom--current-module
(when path
(save-match-data
(setq path (file-truename path))
(when (string-match "/modules/\\([^/]+\\)/\\([^/]+\\)/.*$" path)
(when-let* ((module (match-string 1 path))
(submodule (match-string 2 path)))
(cons (intern (concat ":" module))
(intern submodule))))))))
(defun doom-module-load-path ()
"Return a list of absolute file paths to activated modules."
(append (cl-loop for plist being the hash-values of (doom-modules)
collect (plist-get plist :path))
(list doom-private-dir)))
(defun doom-modules (&optional refresh-p)
"Minimally initialize `doom-modules' (a hash table) and return it."
(or (unless refresh-p doom-modules)
(let ((noninteractive t)
doom-init-modules-p)
(message "Initializing modules")
(doom-initialize-modules t)
doom-modules)))
;;
;; Use-package modifications
;;
(autoload 'use-package "use-package-core" nil nil t)
;; Adds the :after-call custom keyword to `use-package' (and consequently,
;; `def-package!'). :after-call takes a symbol ro list of symbols. These symbols
;; can be functions to hook variables.
;;
;; (use-package X :after-call find-file-hook)
;;
;; This will load X on the first invokation of `find-file-hook' (then it will
;; remove itself from the hook).
(defvar doom--deferred-packages-alist ())
(after! use-package-core
(add-to-list 'use-package-deferring-keywords :after-call nil #'eq)
(setq use-package-keywords
(use-package-list-insert :after-call use-package-keywords :after))
(defalias 'use-package-normalize/:after-call
'use-package-normalize-symlist)
(defun use-package-handler/:after-call (name-symbol _keyword hooks rest state)
(let ((fn (intern (format "doom|transient-hook--load-%s" name-symbol)))
(hooks (delete-dups hooks)))
(if (plist-get state :demand)
(use-package-process-keywords name rest state)
(use-package-concat
`((fset ',fn
(lambda (&rest _)
(require ',name-symbol)
(dolist (hook (cdr (assq ',name-symbol doom--deferred-packages-alist)))
(if (functionp hook)
(advice-remove hook #',fn)
(remove-hook hook #',fn)))
(map-delete doom--deferred-packages-alist ',name-symbol)
(fmakunbound ',fn))))
(cl-mapcan (lambda (hook)
(if (functionp hook)
`((advice-add #',hook :before #',fn))
`((add-hook ',hook #',fn))))
hooks)
`((map-put doom--deferred-packages-alist
',name-symbol
'(,@hooks ,@(cdr (assq name-symbol doom--deferred-packages-alist)))))
(use-package-process-keywords name rest state))))))
;;
;; Module config macros
;;
(defmacro doom! (&rest modules)
"Bootstraps DOOM Emacs and its modules.
The bootstrap process involves making sure the essential directories exist, core
packages are installed, `doom-autoload-file' is loaded, `doom-packages-file'
cache exists (and is loaded) and, finally, loads your private init.el (which
should contain your `doom!' block).
If the cache exists, much of this function isn't run, which substantially
reduces startup time.
The overall load order of Doom is as follows:
~/.emacs.d/init.el
~/.emacs.d/core/core.el
`doom-pre-init-hook'
~/.doom.d/init.el
Module init.el files
`doom-init-hook'
Module config.el files
~/.doom.d/config.el
`after-init-hook'
`emacs-startup-hook'
`doom-post-init-hook' (at end of `emacs-startup-hook')
Module load order is determined by your `doom!' block. See `doom-modules-dirs'
for a list of all recognized module trees. Order defines precedence (from most
to least)."
(let ((doom-modules
(make-hash-table :test #'equal
:size (if modules (length modules) 100)
:rehash-threshold 1.0))
category
init-forms config-forms)
(dolist (m modules)
(cond ((keywordp m) (setq category m))
((not category) (error "No module category specified for %s" m))
((let* ((module (if (listp m) (car m) m))
(flags (if (listp m) (cdr m)))
(path (doom-module-locate-path category module)))
(if (not path)
(message "Couldn't find the %s %s module" category module)
(let ((key (cons category module)))
(doom-module-set category module :flags flags :path path)
(push `(let ((doom--current-module ',key))
(load! "init" ,path t))
init-forms)
(push `(let ((doom--current-module ',key))
(load! "config" ,path t))
config-forms)))))))
`(let (file-name-handler-alist)
(setq doom-modules ',doom-modules)
,@(nreverse init-forms)
(run-hooks 'doom-init-hook)
(unless noninteractive
(let ((doom--stage 'config))
,@(nreverse config-forms)
(when doom-private-dir
(load ,(expand-file-name "config" doom-private-dir)
t (not doom-debug-mode))))))))
(defmacro def-package! (name &rest plist)
"A thin wrapper around `use-package'."
;; Ignore package if NAME is in `doom-disabled-packages'
(when (and (memq name doom-disabled-packages)
(not (memq :disabled plist)))
(setq plist `(:disabled t ,@plist)))
;; If byte-compiling, ignore this package if it doesn't meet the condition.
;; This avoids false-positive load errors.
(unless (and (bound-and-true-p byte-compile-current-file)
(or (and (plist-member plist :if) (not (eval (plist-get plist :if) t)))
(and (plist-member plist :when) (not (eval (plist-get plist :when) t)))
(and (plist-member plist :unless) (eval (plist-get plist :unless) t))))
`(use-package ,name ,@plist)))
(defmacro def-package-hook! (package when &rest body)
"Reconfigures a package's `def-package!' block.
Only use this macro in a module's init.el file.
Under the hood, this uses use-package's `use-package-inject-hooks'.
PACKAGE is a symbol; the package's name.
WHEN should be one of the following:
:pre-init :post-init :pre-config :post-config
WARNING: If :pre-init or :pre-config hooks return nil, the original
`def-package!''s :init/:config block (respectively) is overwritten, so remember
to have them return non-nil (or exploit that to overwrite Doom's config)."
(declare (indent defun))
(doom--assert-stage-p 'init #'package!)
(cond ((eq when :disable)
(message "Using :disable with `def-package-hook!' is deprecated. Use :disable in `package!' instead.")
(ignore (push package doom-disabled-packages)))
((memq when '(:pre-init :post-init :pre-config :post-config))
`(progn
(setq use-package-inject-hooks t)
(add-hook!
',(intern (format "use-package--%s--%s-hook"
package
(substring (symbol-name when) 1)))
,@body)))
((error "'%s' isn't a valid hook for def-package-hook!" when))))
(defmacro load! (filename &optional path noerror)
"Load a file relative to the current executing file (`load-file-name').
FILENAME is either a file path string or a form that should evaluate to such a
string at run time. PATH is where to look for the file (a string representing a
directory path). If omitted, the lookup is relative to either `load-file-name',
`byte-compile-current-file' or `buffer-file-name' (checked in that order).
If NOERROR is non-nil, don't throw an error if the file doesn't exist."
(unless path
(setq path (or (and (bound-and-true-p byte-compile-current-file)
(file-name-directory byte-compile-current-file))
(and load-file-name (file-name-directory load-file-name))
(and buffer-file-name (file-name-directory buffer-file-name))
(error "Could not detect path to look for '%s' in" filename))))
`(load ,(if path
`(expand-file-name ,filename ,path)
filename)
,noerror ,(not doom-debug-mode)))
(defmacro require! (category module &rest plist)
"Loads the module specified by CATEGORY (a keyword) and MODULE (a symbol)."
(let ((doom-modules (copy-hash-table doom-modules)))
(apply #'doom-module-set category module
(mapcar #'eval plist))
(let ((module-path (doom-module-locate-path category module)))
(if (directory-name-p module-path)
`(condition-case-unless-debug ex
(let ((doom--current-module ',(cons category module)))
(load! "init" ,module-path :noerror)
(let ((doom--stage 'config))
(load! "config" ,module-path :noerror)))
('error
(lwarn 'doom-modules :error
"%s in '%s %s' -> %s"
(car ex) ,category ',module
(error-message-string ex))))
(warn 'doom-modules :warning "Couldn't find module '%s %s'"
category module)))))
(defmacro featurep! (module &optional submodule flag)
"Returns t if MODULE SUBMODULE is enabled. If FLAG is provided, returns t if
MODULE SUBMODULE has FLAG enabled.
(featurep! :config default)
Module FLAGs are set in your config's `doom!' block, typically in
~/.emacs.d/init.el. Like so:
:config (default +flag1 -flag2)
When this macro is used from inside a module, MODULE and SUBMODULE can be
omitted. eg. (featurep! +flag1)"
(unless submodule
(let* ((path (or (bound-and-true-p byte-compile-current-file)
load-file-name))
(module-pair (doom-module-from-path path)))
(unless module-pair
(error "featurep! couldn't detect what module its in! (in %s)" path))
(setq flag module
module (car module-pair)
submodule (cdr module-pair))))
(if flag
(and (memq flag (doom-module-get module submodule :flags)) t)
(doom-module-p module submodule)))
;; ;;
@ -772,7 +230,7 @@ elsewhere."
,(when (and pkg-pin t) ,(when (and pkg-pin t)
`(map-put package-pinned-packages ',name ,pkg-pin)) `(map-put package-pinned-packages ',name ,pkg-pin))
(map-put doom-packages ',name ',plist) (map-put doom-packages ',name ',plist)
(not ,pkg-disable)))) (not (memq ',name doom-disabled-packages)))))
(defmacro packages! (&rest packages) (defmacro packages! (&rest packages)
"A convenience macro like `package!', but allows you to declare multiple "A convenience macro like `package!', but allows you to declare multiple
@ -804,53 +262,5 @@ loads MODULE SUBMODULE's packages.el file."
(doom-module-put ,module ',submodule :flags flags)) (doom-module-put ,module ',submodule :flags flags))
(load! "packages" ,(doom-module-locate-path module submodule) t))) (load! "packages" ,(doom-module-locate-path module submodule) t)))
;;
;; Make package.el cooperate with Doom
;;
;; Updates QUELPA after deleting a package
(advice-add #'package-delete :after #'doom*package-delete)
;; Replace with Doom variants
(advice-add #'package-autoremove :override #'doom//packages-autoremove)
(advice-add #'package-install-selected-packages :override #'doom//packages-install)
;;
;; Cross-module configuration
;;
;; I needed a way to reliably cross-configure modules without littering my
;; modules with `after!' blocks or testing whether they were enabled, so I wrote
;; `set!'. If a setting doesn't exist at runtime, the `set!' call is ignored and
;; its arguments are left unevaluated (and entirely omitted when byte-compiled).
(defmacro def-setting! (keyword arglist &optional docstring &rest forms)
"Define a setting. Like `defmacro', this should return a form to be executed
when called with `set!'. FORMS are not evaluated until `set!' calls it.
See `doom/describe-setting' for a list of available settings.
Do not use this for configuring Doom core."
(declare (indent defun) (doc-string 3))
(or (keywordp keyword)
(signal 'wrong-type-argument (list 'keywordp keyword)))
`(fset ',(intern (format "doom--set%s" keyword))
(lambda ,arglist ,docstring ,@forms)))
(defmacro set! (keyword &rest values)
"Set an option defined by `def-setting!'. Skip if doesn't exist. See
`doom/describe-setting' for a list of available settings.
VALUES doesn't get evaluated if the KEYWORD setting doesn't exist."
(declare (indent defun))
(let ((fn (intern-soft (format "doom--set%s" keyword))))
(if (and fn (fboundp fn))
(apply fn values)
(when doom-debug-mode
(message "No setting found for %s" keyword)
nil))))
(provide 'core-packages) (provide 'core-packages)
;;; core-packages.el ends here ;;; core-packages.el ends here

View file

@ -63,7 +63,39 @@ Use this for files that change often, like cache files.")
"Where your private customizations are placed. Must end in a slash. Respects "Where your private customizations are placed. Must end in a slash. Respects
XDG directory conventions if ~/.config/doom exists.") XDG directory conventions if ~/.config/doom exists.")
(defconst doom-autoload-file (concat doom-local-dir "autoloads.el")
"Where `doom//reload-doom-autoloads' will generate its core autoloads file.")
(defconst doom-package-autoload-file (concat doom-local-dir "autoloads.pkg.el")
"Where `doom//reload-package-autoloads' will generate its package.el autoloads
file.")
;;
;; State variables
;;
(defvar doom-init-p nil
"Non-nil if `doom-initialize' has run.")
(defvar doom-init-time nil
"The time it took, in seconds, for DOOM Emacs to initialize.")
(defvar doom-emacs-changed-p nil
"If non-nil, the running version of Emacs is different from the first time
Doom was setup, which can cause problems.")
(defvar doom-site-load-path load-path
"The starting load-path, before it is altered by `doom-initialize'.")
(defvar doom--refreshed-p nil)
(defvar doom--stage 'init)
;;
;; Doom hooks ;; Doom hooks
;;
(defvar doom-init-hook nil (defvar doom-init-hook nil
"Hooks run after all init.el files are loaded, including your private and all "Hooks run after all init.el files are loaded, including your private and all
module init.el files, but before their config.el files are loaded.") module init.el files, but before their config.el files are loaded.")
@ -73,8 +105,40 @@ module init.el files, but before their config.el files are loaded.")
`emacs-startup-hook', as late as possible. Guaranteed to run after everything `emacs-startup-hook', as late as possible. Guaranteed to run after everything
else (except for `window-setup-hook').") else (except for `window-setup-hook').")
(defvar doom-reload-hook nil
"A list of hooks to run when `doom//reload-load-path' is called.")
;;
;; Optimize startup
;;
(defvar doom--file-name-handler-alist file-name-handler-alist)
(unless (or after-init-time noninteractive)
;; A big contributor to long startup times is the garbage collector, so we up
;; its memory threshold, temporarily and reset it later in `doom|finalize'.
(setq gc-cons-threshold 402653184
gc-cons-percentage 1.0
;; consulted on every `require', `load' and various file reading
;; functions. You get a minor speed up by nooping this.
file-name-handler-alist nil))
(defun doom|finalize ()
"Resets garbage collection settings to reasonable defaults (if you don't do
this, you'll get stuttering and random freezes) and resets
`file-name-handler-alist'."
(setq file-name-handler-alist doom--file-name-handler-alist
gc-cons-threshold 16777216
gc-cons-percentage 0.2))
(add-hook 'emacs-startup-hook #'doom|finalize)
(add-hook 'doom-reload-hook #'doom|finalize)
;;
;; Emacs core configuration
;;
;;;
;; UTF-8 as the default coding system ;; UTF-8 as the default coding system
(when (fboundp 'set-charset-priority) (when (fboundp 'set-charset-priority)
(set-charset-priority 'unicode)) ; pretty (set-charset-priority 'unicode)) ; pretty
@ -88,10 +152,11 @@ else (except for `window-setup-hook').")
(setq-default (setq-default
ad-redefinition-action 'accept ; silence advised function warnings ad-redefinition-action 'accept ; silence advised function warnings
apropos-do-all t ; make `apropos' more useful apropos-do-all t ; make `apropos' more useful
auto-mode-case-fold nil
autoload-compute-prefixes nil
debug-on-error doom-debug-mode debug-on-error doom-debug-mode
ffap-machine-p-known 'reject ; don't ping things that look like domain names ffap-machine-p-known 'reject ; don't ping things that look like domain names
idle-update-delay 2 ; update ui less often idle-update-delay 2 ; update ui less often
auto-mode-case-fold nil
;; be quiet at startup; don't load or display anything unnecessary ;; be quiet at startup; don't load or display anything unnecessary
inhibit-startup-message t inhibit-startup-message t
inhibit-startup-echo-area-message user-login-name inhibit-startup-echo-area-message user-login-name
@ -105,6 +170,19 @@ else (except for `window-setup-hook').")
create-lockfiles nil create-lockfiles nil
history-length 500 history-length 500
make-backup-files nil ; don't create backup~ files make-backup-files nil ; don't create backup~ files
;; `use-package'
use-package-verbose doom-debug-mode
use-package-minimum-reported-time (if doom-debug-mode 0 0.1)
;; byte compilation
byte-compile-verbose doom-debug-mode
byte-compile-warnings '(not free-vars unresolved noruntime lexical make-local)
;; security
gnutls-verify-error (not (getenv "INSECURE")) ; you shouldn't use this
tls-checktrust gnutls-verify-error
tls-program (list "gnutls-cli --x509cafile %t -p %p %h"
;; compatibility fallbacks
"gnutls-cli -p %p %h"
"openssl s_client -connect %h:%p -no_ssl2 -no_ssl3 -ign_eof")
;; files ;; files
abbrev-file-name (concat doom-local-dir "abbrev.el") abbrev-file-name (concat doom-local-dir "abbrev.el")
auto-save-list-file-name (concat doom-cache-dir "autosave") auto-save-list-file-name (concat doom-cache-dir "autosave")
@ -121,11 +199,6 @@ else (except for `window-setup-hook').")
url-cache-directory (concat doom-cache-dir "url/") url-cache-directory (concat doom-cache-dir "url/")
url-configuration-directory (concat doom-etc-dir "url/")) url-configuration-directory (concat doom-etc-dir "url/"))
;;
;; Emacs fixes/hacks
;;
(defvar doom-auto-minor-mode-alist '() (defvar doom-auto-minor-mode-alist '()
"Alist mapping filename patterns to corresponding minor mode functions, like "Alist mapping filename patterns to corresponding minor mode functions, like
`auto-mode-alist'. All elements of this alist are checked, meaning you can `auto-mode-alist'. All elements of this alist are checked, meaning you can
@ -167,29 +240,129 @@ with functions that require it (like modeline segments)."
;; ;;
;; Optimize startup ;; Bootstrap helpers
;; ;;
(defvar doom--file-name-handler-alist file-name-handler-alist) (defvar doom--last-emacs-file (concat doom-local-dir "emacs-version.el"))
(unless (or after-init-time noninteractive) (defvar doom--last-emacs-version nil)
;; A big contributor to long startup times is the garbage collector, so we up
;; its memory threshold, temporarily and reset it later in `doom|finalize'.
(setq gc-cons-threshold 402653184
gc-cons-percentage 1.0
;; consulted on every `require', `load' and various file reading
;; functions. You get a minor speed up by nooping this.
file-name-handler-alist nil))
(defun doom|finalize () (defun doom-ensure-same-emacs-version-p ()
"Resets garbage collection settings to reasonable defaults (if you don't do "Do an Emacs version check and set `doom-emacs-changed-p' if it has changed."
this, you'll get stuttering and random freezes) and resets (if (load doom--last-emacs-file 'noerror 'nomessage 'nosuffix)
`file-name-handler-alist'." (setq doom-emacs-changed-p
(setq file-name-handler-alist doom--file-name-handler-alist (not (equal emacs-version doom--last-emacs-version)))
gc-cons-threshold 16777216 (with-temp-file doom--last-emacs-file
gc-cons-percentage 0.2)) (princ `(setq doom--last-emacs-version ,(prin1-to-string emacs-version))
(current-buffer))))
(cond ((not doom-emacs-changed-p))
((y-or-n-p
(format
(concat "Your version of Emacs has changed from %s to %s, which may cause incompatibility\n"
"issues. Please run `bin/doom compile :plugins` afterwards to resolve any problems.\n\n"
"Continue?")
doom--last-emacs-version
emacs-version))
(delete-file doom--last-emacs-file))
(noninteractive (error "Aborting"))
((kill-emacs))))
(add-hook 'emacs-startup-hook #'doom|finalize) (defun doom-ensure-core-directories ()
(add-hook 'doom-reload-hook #'doom|finalize) "Make sure all Doom's essential local directories (in and including
`doom-local-dir') exist."
(dolist (dir (list doom-local-dir doom-etc-dir doom-cache-dir doom-packages-dir))
(unless (file-directory-p dir)
(make-directory dir t))))
(defun doom|display-benchmark (&optional return-p)
"Display a benchmark, showing number of packages and modules, and how quickly
they were loaded at startup.
If RETURN-P, return the message as a string instead of displaying it."
(funcall (if return-p #'format #'message)
"Doom loaded %s packages across %d modules in %.03fs"
(length package-activated-list)
(if doom-modules (hash-table-count doom-modules) 0)
(or doom-init-time
(setq doom-init-time (float-time (time-subtract (current-time) before-init-time))))))
(defun doom|post-init ()
"Run `doom-post-init-hook'. That's all."
(run-hooks 'doom-post-init-hook))
(defun doom|run-all-startup-hooks ()
"Run all startup Emacs hooks. Meant to be executed after starting Emacs with
-q or -Q, for example:
emacs -Q -l init.el -f doom|run-all-startup-hooks"
(run-hooks 'after-init-hook 'delayed-warnings-hook
'emacs-startup-hook 'term-setup-hook
'window-setup-hook))
;;
;; Bootstrap functions
;;
(defun doom-initialize (&optional force-p)
"Bootstrap Doom, if it hasn't already (or if FORCE-P is non-nil).
The bootstrap process involves making sure 1) the essential directories exist,
2) the core packages are installed, 3) `doom-autoload-file' and
`doom-package-autoload-file' exist and have been loaded, and 4) Doom's core
files are loaded.
If the cache exists, much of this function isn't run, which substantially
reduces startup time.
The overall load order of Doom is as follows:
~/.emacs.d/init.el
~/.emacs.d/core/core.el
~/.doom.d/init.el
Module init.el files
`doom-init-hook'
Module config.el files
~/.doom.d/config.el
`after-init-hook'
`emacs-startup-hook'
`doom-post-init-hook' (at end of `emacs-startup-hook')
Module load order is determined by your `doom!' block. See `doom-modules-dirs'
for a list of all recognized module trees. Order defines precedence (from most
to least)."
(when (or force-p (not doom-init-p))
;; Set this to prevent infinite recursive calls to `doom-initialize'
(setq doom-init-p t)
;; `doom-autoload-file' tells Emacs where to load all its autoloaded
;; functions from. This includes everything in core/autoload/*.el and all
;; the autoload files in your enabled modules.
(when (or force-p (not (doom-initialize-autoloads doom-autoload-file)))
(doom-ensure-core-directories)
(doom-ensure-same-emacs-version-p)
;; Ensure packages are set up and initialized
(require 'core-packages)
(doom-ensure-packages-initialized force-p)
(doom-ensure-core-packages)
;; Regenerate `doom-autoload-file', which tells Doom where to find all its
;; module autoloaded functions.
(unless (or force-p noninteractive)
(user-error "Your doom autoloads are missing! Run `bin/doom refresh' to regenerate them")))
;; Loads `doom-package-autoload-file', which caches `load-path',
;; `auto-mode-alist', `Info-directory-list', `doom-disabled-packages' and
;; `package-activated-list'. A big reduction in startup time.
(unless (or force-p
(doom-initialize-autoloads doom-package-autoload-file)
noninteractive)
(user-error "Your package autoloads are missing! Run `bin/doom refresh' to regenerate them")))
;; Initialize Doom core
(unless noninteractive
(add-hook! 'emacs-startup-hook
#'(doom|post-init doom|display-benchmark))
(require 'core-os)
(require 'core-ui)
(require 'core-editor)
(require 'core-projects)
(require 'core-keybinds)))
;; ;;
@ -198,14 +371,18 @@ this, you'll get stuttering and random freezes) and resets
(add-to-list 'load-path doom-core-dir) (add-to-list 'load-path doom-core-dir)
(require 'core-lib)
(require 'core-packages)
(load custom-file t t t) (load custom-file t t t)
(require 'core-lib)
(require 'core-modules)
(when noninteractive
(require 'core-dispatcher))
(doom-initialize noninteractive) (doom-initialize noninteractive)
(if noninteractive (unless noninteractive
(require 'core-dispatcher)
(doom-initialize-modules)) (doom-initialize-modules))
(after! package
(require 'core-packages))
(provide 'core) (provide 'core)
;;; core.el ends here ;;; core.el ends here