refactor!: module API

BREAKING CHANGE: This backports some architectural choices from v3.0.
This changes Doom's module API, renaming some functions and removing
others, in order to facilitate some new features, prepare to move Doom's
modules into separate repos, and make way for two, much larger breaking
commits coming in the next few days.

This commit won't break anything for users unless they're tinkering with
Doom's internals/using its `doom-module-*` API directly. I am avoiding
broader backwards incompatibilities until the 3.0 release.

What's new:

- Negated flags. (modulep! :editor evil -everywhere) will return non-nil
  if :editor evil is active without its +everywhere flag.
- `modulep!` now takes multiple flags to simplify AND checks. E.g.

    (and (modulep! +foo)
         (modulep! +bar)
         (not (modulep! +baz)))

  Can now be expressed with:

    (modulep! +foo +bar -baz)
- Adds pcase matchers for `doom-module-context` and `doom-module`
  structs, making the following destructuring binds possible:

    (pcase-dolist ((doom-module group name flags features)
                   (hash-table-values doom-modules))
      ...)

  This will be used more in v3.0.
- Adds file cookie support to module init.el and config.el files.

Here's a summary of breaking changes made in this commit:

- `doom-module-context` was changed from a vector to a struct (record).
- `doom-modules` is now a table of `doom-module` structs, rather than
  free-form plists.
- The following macros have been renamed:
  - `doom-context-with` -> `with-doom-context`
  - `doom-module-context-with` -> `with-doom-module`
- The followings functions have been replaced/removed:
  - `doom-module-context`+`doom-module-context-get` -> `doom-module`
  - `doom-module-set` -> `doom-module--put`
  - `doom-module-p` -> `doom-module-active-p`
  - `doom-module-context-key` (is now a getter with the same name)
  - `doom-module-put` (removed)
  - `doom-module--context-field` (removed)
- The signatures for these functions have changed:
  - `doom-module-get CATEGORY &optional MODULE PROP` ->
    `doom-module-get (GROUP . MODULE) &optional PROP`
  - `doom-module-locate-path CATEGORY &optional MODULE FILE` ->
    `doom-module-locate-path (GROUP . MODULE) &optional FILE`
  - `doom-module-expand-path CATEGORY MODULE &optional FILE` ->
    `doom-module-expand-path (GROUP . MODULE) &optional FILE`
- Adds the following functions
  - `doom-module-exists-p`
  - `doom-module-key`
  - `doom-module->context`
  - `doom-module<-context`
- Removes the following variables
  - `doom-module--empty-context`

This commit results in a little redundancy, which I will address in
parts 2/3 and/or v3.0.
This commit is contained in:
Henrik Lissner
2024-10-19 15:29:56 -04:00
parent b9deb35aab
commit 15904349cf
24 changed files with 443 additions and 320 deletions

View File

@ -251,7 +251,7 @@ SEE ALSO:
;; Load $DOOMDIR/init.el, to read the user's `doom!' block and give users an ;; Load $DOOMDIR/init.el, to read the user's `doom!' block and give users an
;; opportunity to customize the CLI environment, if they like. Otherwise, they ;; opportunity to customize the CLI environment, if they like. Otherwise, they
;; can do so in .doomrc or .doomproject. ;; can do so in .doomrc or .doomproject.
(load! doom-module-init-file doom-user-dir t) (doom-modules-initialize)
;; There are a lot of CLIs, and some have expensive initialization, so best we ;; There are a lot of CLIs, and some have expensive initialization, so best we
;; load them lazily. ;; load them lazily.
@ -297,9 +297,9 @@ SEE ALSO:
(let ((cli-file "cli.el")) (let ((cli-file "cli.el"))
(defcli-group! "Module commands" (defcli-group! "Module commands"
(doom-context-with 'modules (with-doom-context 'modules
(dolist (key (doom-module-list)) (dolist (key (doom-module-list))
(when-let (path (doom-module-locate-path (car key) (cdr key) cli-file)) (when-let (path (doom-module-locate-path key cli-file))
(defcli-group! :prefix (if (cdr key) (format "+%s" (cdr key))) (defcli-group! :prefix (if (cdr key) (format "+%s" (cdr key)))
(doom-load (file-name-sans-extension path)))))))) (doom-load (file-name-sans-extension path))))))))

View File

@ -317,18 +317,18 @@ in."
(print! (start "Checking your enabled modules...")) (print! (start "Checking your enabled modules..."))
(advice-add #'require :around #'doom-shut-up-a) (advice-add #'require :around #'doom-shut-up-a)
(pcase-dolist (`(,group . ,name) (doom-module-list)) (pcase-dolist (`(,group . ,name) (doom-module-list))
(doom-context-with 'doctor (with-doom-context 'doctor
(let (doom-local-errors (let (doom-local-errors
doom-local-warnings) doom-local-warnings)
(let (doom-doctor--errors (let (doom-doctor--errors
doom-doctor--warnings) doom-doctor--warnings)
(condition-case-unless-debug ex (condition-case-unless-debug ex
(doom-module-context-with (cons group name) (with-doom-module (cons group name)
(let ((doctor-file (doom-module-expand-path group name "doctor.el")) (let ((doctor-file (doom-module-expand-path (cons group name) "doctor.el"))
(packages-file (doom-module-expand-path group name doom-module-packages-file))) (packages-file (doom-module-expand-path (cons group name) doom-module-packages-file)))
(when packages-file (when packages-file
(cl-loop with doom-output-indent = 6 (cl-loop with doom-output-indent = 6
for name in (doom-context-with 'packages for name in (with-doom-context 'packages
(let* (doom-packages (let* (doom-packages
doom-disabled-packages) doom-disabled-packages)
(load packages-file 'noerror 'nomessage) (load packages-file 'noerror 'nomessage)

View File

@ -1878,7 +1878,7 @@ errors to `doom-cli-error-file')."
(when doom-cli--context (when doom-cli--context
(error "Cannot nest `run!' calls")) (error "Cannot nest `run!' calls"))
(doom-run-hooks 'doom-after-init-hook) (doom-run-hooks 'doom-after-init-hook)
(doom-context-with 'cli (with-doom-context 'cli
(let* ((args (flatten-list args)) (let* ((args (flatten-list args))
(context (make-doom-cli-context :prefix prefix :whole args)) (context (make-doom-cli-context :prefix prefix :whole args))
(doom-cli--context context) (doom-cli--context context)

View File

@ -51,7 +51,7 @@
(unless absolute? (unless absolute?
(append (cons '* (remq t (reverse doom-context))) (append (cons '* (remq t (reverse doom-context)))
(if (bound-and-true-p doom-module-context) (if (bound-and-true-p doom-module-context)
(let ((key (doom-module-context-key))) (let ((key (doom-module-context-key doom-module-context)))
(delq nil (list (car key) (cdr key))))))) (delq nil (list (car key) (cdr key)))))))
":") ":")
args))) args)))
@ -617,6 +617,25 @@ See `general-key-dispatch' for what other arguments it accepts in BRANCHES."
(defalias 'λ! #'cmd!) (defalias 'λ! #'cmd!)
(defalias 'λ!! #'cmd!!) (defalias 'λ!! #'cmd!!)
(pcase-defmacro doom-struct (type &rest fields)
`(and (pred (cl-struct-p))
;; TODO: Support `&rest', `&key', and `&optional' in FIELDS
,@(mapcar
(lambda (field)
(let ((offset (cl-struct-slot-offset type field)))
`(app (lambda (it)
,(if offset
`(aref it ,offset)
`(,(intern (format "%s-%s" ',type ',field)) it)))
,field)))
fields)))
(pcase-defmacro doom-module-context (&rest fields)
`(doom-struct doom-module-context ,@fields))
(pcase-defmacro doom-module (&rest fields)
`(doom-struct doom-module ,@fields))
;;; Mutation ;;; Mutation
(defmacro appendq! (sym &rest lists) (defmacro appendq! (sym &rest lists)

View File

@ -5,8 +5,8 @@
;; ;;
;;; Variables ;;; Variables
(defvar doom-modules (make-hash-table :test 'equal) (defvar doom-modules nil
"A hash table of enabled modules. Set by `doom-initialize-modules'.") "A table of enabled modules and metadata. See `doom-modules-initialize'.")
(define-obsolete-variable-alias 'doom-modules-dirs 'doom-module-load-path "3.0.0") (define-obsolete-variable-alias 'doom-modules-dirs 'doom-module-load-path "3.0.0")
(defvar doom-module-load-path (defvar doom-module-load-path
@ -120,114 +120,190 @@ your `doom!' block, a warning is emitted before replacing it with :emacs vc and
:type 'hook) :type 'hook)
;;
;;; Types
(cl-defstruct doom-module
"TODO"
(index 0 :read-only t)
;; source
group
name
depth
flags
features
;; sources
path
;; disabled-p
;; frozen-p
;; layer-p
;; recipe
;; alist
;; package
;; if
)
(cl-defstruct doom-module-context
"Hot cache object for the containing Doom module."
index key path flags features)
;; ;;
;;; `doom-module-context' ;;; `doom-module-context'
(defvar doom-module--empty-context [nil nil nil nil nil nil nil]) (defvar doom-module-context (make-doom-module-context)
"A `doom-module-context' for the module associated with the current file.
(eval-and-compile Never set this variable directly, use `with-doom-module'.")
(put 'doom-module-context 'keys '(:index 0 :initdepth 1 :configdepth 2
:group 3 :name 4 :flags 5 :features 6)))
(defvar doom-module-context doom-module--empty-context
"A vector describing the module associated it with the active context.
Contains the following: [INDEX INITDEPTH CONFIGDEPTH :GROUP MODULE FLAGS FEATURES] (defmacro with-doom-module (key &rest body)
"Evaluate BODY with `doom-module-context' informed by KEY."
Do not directly set this variable, only let-bind it.")
;; DEPRECATED: Remove this when byte-compilation is introduced to Doom core.
(defmacro doom-module--context-field (field)
(plist-get (get 'doom-module-context 'keys) field))
(defun doom-module-context-get (field &optional context)
"Return the FIELD of CONTEXT.
FIELD should be one of `index', `initdepth', `configdepth', `group', `name',
`flags', or `features'. CONTEXT should be a `doom-module-context' vector. If
omitted, defaults to `doom-module-context'."
(aref (or context doom-module-context)
(plist-get (get 'doom-module-context 'keys)
field)))
(defun doom-module-context (group &optional name)
"Create a `doom-module-context' from a module by GROUP and NAME.
If NAME is omitted, GROUP is treated as a module key cons cell: (GROUP . NAME)."
(declare (side-effect-free t))
(let ((key (if name (cons group name) group)))
(or (get (or (car-safe key) key)
(cdr-safe key))
doom-module--empty-context)))
(defun doom-module-context-key (&optional context)
"Return the module of the active `doom-module-context' as a module key."
(declare (side-effect-free t))
(let ((context (or context doom-module-context)))
(cons (aref context (doom-module--context-field :group))
(aref context (doom-module--context-field :name)))))
(defmacro doom-module-context-with (module-key &rest body)
"Evaluate BODY with `doom-module-context' informed by MODULE-KEY."
(declare (indent 1)) (declare (indent 1))
`(let ((doom-module-context (doom-module-context ,module-key))) `(let ((doom-module-context
(let ((key ,key))
(cond ((null key) (make-doom-module-context))
((doom-module-context-p key) key)
((doom-module-p key) (doom-module->context key))
((doom-module (car key) (cdr key)))
((doom-module->context key))
((error "Invalid module: %S" key))))))
(doom-log ":context:module: =%s" doom-module-context) (doom-log ":context:module: =%s" doom-module-context)
,@body)) ,@body))
(defun doom-module<-context (context)
"Return a `doom-module' plist from CONTEXT."
(declare (side-effect-free t))
(doom-module-get (doom-module-context-key context)))
(defun doom-module->context (key)
"Change a `doom-module' into a `doom-module-context'."
(pcase-let
(((doom-module index path flags group name)
(if (doom-module-p key)
key (doom-module-get (doom-module-key key)))))
(make-doom-module-context
:index index
:key (cons group name)
:path path
:flags flags)))
(defun doom-module (group name &optional property)
"Return the `doom-module-context' for any active module by GROUP NAME.
Return its PROPERTY, if specified."
(declare (side-effect-free t))
(when-let ((context (get group name)))
(if property
(aref
context
(or (plist-get
(eval-when-compile
(cl-loop with i = 1
for info in (cdr (cl-struct-slot-info 'doom-module-context))
nconc (list (doom-keyword-intern (symbol-name (car info)))
(prog1 i (cl-incf i)))))
property)
(error "Unknown doom-module-context property: %s" property)))
context)))
;; ;;
;;; Module API ;;; Module API
(defun doom-module-p (category module &optional flag) (defun doom-modules-initialize (&optional force?)
"Returns t if CATEGORY MODULE is enabled (ie. present in `doom-modules')." "Initializes module metadata."
(when (or (null doom-modules) force?)
(setq doom-modules (make-hash-table :test 'equal))
;; Register Doom's two virtual module categories, representing Doom's core
;; and the user's config; which are always enabled.
(doom-module--put '(:core . nil) :path doom-core-dir :depth -110)
(doom-module--put '(:user . nil) :path doom-user-dir :depth '(-105 . 105))
;; DEPRECATED: I intend to phase out our internal usage of `use-package' and
;; move it to a :config use-package module. The macro is far too complex
;; and magical for our needs, but until this move is done, ':config
;; use-package' will remain a hardcoded module for backwards
;; compatibility.
(doom-module--put '(:config . use-package)
:path (doom-module-locate-path '(:config . use-package))
:depth -111)
;; Load $DOOMDIR/init.el, where the user's `doom!' lives, which will inform
;; us of all desired modules.
(doom-load (file-name-concat doom-user-dir doom-module-init-file)
'noerror)))
(defun doom-module-key (key)
"Normalize KEY into a (GROUP . MODULE) tuple representing a Doom module key."
(declare (pure t) (side-effect-free t)) (declare (pure t) (side-effect-free t))
(when-let (plist (gethash (cons category module) doom-modules)) (cond ((doom-module-p key)
(or (null flag) (cons (doom-module-group key) (doom-module-name key)))
(and (memq flag (plist-get plist :flags)) ((doom-module-context-p key)
t)))) (doom-module-context-key key))
((car-safe key)
(if (nlistp (cdr-safe key))
key
(cons (car key) (cadr key))))
((error "Invalid key: %S" key))))
(defun doom-module-depth (category module &optional initdepth?) (defun doom-module--has-flag-p (flags wanted-flags)
"Return the depth of CATEGORY MODULE. "Return t if the list of WANTED-FLAGS satisfies the list of FLAGS."
(declare (pure t) (side-effect-free error-free))
(cl-loop with flags = (ensure-list flags)
for flag in (ensure-list wanted-flags)
for flagstr = (symbol-name flag)
if (if (eq ?- (aref flagstr 0))
(memq (intern (concat "+" (substring flagstr 1)))
flags)
(not (memq flag flags)))
return nil
finally return t))
If INITDEPTH? is non-nil, use the CAR if a module was given two depths (see (defun doom-module--fold-flags (flags)
`doom-module-set')." "Returns a collapsed list of FLAGS (a list of +/- prefixed symbols).
(if-let (depth (doom-module-get category module :depth))
(or (if initdepth?
(car-safe depth)
(cdr-safe depth))
depth)
0))
(defun doom-module-get (category module &optional property) FLAGS is read in sequence, cancelling out negated flags and removing
"Returns the plist for CATEGORY MODULE. Gets PROPERTY, specifically, if set." duplicates."
(declare (pure t) (side-effect-free t)) (declare (pure t) (side-effect-free error-free))
(when-let (plist (gethash (cons category module) doom-modules)) (let (newflags)
(while flags
(let* ((flag (car flags))
(flagstr (symbol-name flag)))
(when-let ((sym (intern-soft
(concat (if (eq ?- (aref flagstr 0)) "+" "-")
(substring flagstr 1)))))
(setq newflags (delq sym newflags)))
(cl-pushnew flag newflags :test 'eq))
(setq flags (cdr flags)))
(nreverse newflags)))
(defun doom-module-get (key &optional property)
"Returns the plist for GROUP MODULE. Gets PROPERTY, specifically, if set."
(declare (side-effect-free t))
(when-let ((m (gethash key doom-modules)))
(if property (if property
(plist-get plist property) (aref
plist))) m (or (plist-get
(eval-when-compile
(cl-loop with i = 1
for info in (cdr (cl-struct-slot-info 'doom-module))
nconc (list (doom-keyword-intern (symbol-name (car info)))
(prog1 i (cl-incf i)))))
property)
(error "Unknown doom-module property: %s" property)))
m)))
(defun doom-module-put (category module &rest plist) (cl-defun doom-module--put ((group . name) &rest plist)
"Set a PROPERTY for CATEGORY MODULE to VALUE. PLIST should be additional pairs "Enable GROUP NAME and associate PLIST with it.
of PROPERTY and VALUEs.
\(fn CATEGORY MODULE PROPERTY VALUE &rest [PROPERTY VALUE [...]])" This enables the target module, where GROUP is a keyword, NAME is a symbol, and
(puthash (cons category module) PLIST is a property list accepting none, any, or all of the following
(if-let (old-plist (doom-module-get category module)) properties:
(if (null plist)
old-plist
(when (cl-oddp (length plist))
(signal 'wrong-number-of-arguments (list (length plist))))
(while plist
(plist-put old-plist (pop plist) (pop plist)))
old-plist)
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:
:group KEYWORD
Indicating the group this module is in. This doesn't have to match GROUP, as
it could indicate a module alias.
:name SYMBOL
Indicating the name of this module. This doesn't have to match NAME, as it
could indicate a module alias.
:path STRING :path STRING
Path to the directory where this module lives. Path to the directory where this module lives.
:depth INT|(INITDEPTH . CONFIGDEPTH) :depth INT|(INITDEPTH . CONFIGDEPTH)
@ -235,32 +311,66 @@ following properties:
order of the module's init.el, while CONFIGDEPTH determines the same for all order of the module's init.el, while CONFIGDEPTH determines the same for all
other config files (config.el, packages.el, doctor.el, etc). other config files (config.el, packages.el, doctor.el, etc).
:flags (SYMBOL...) :flags (SYMBOL...)
A list of activated flags for this module. A list of activated flags for this module. Will be collapsed into
pre-existing flags for the module.
:features (SYMBOL...) :features (SYMBOL...)
A list of active features, determined from module's metadata. NOT A list of active features, determined from the module's metadata. Will be
IMPLEMENTED YET. collapsed into any pre-existing features for the module. NOT IMPLEMENTED
YET.
If PLIST consists of a single nil, the module is purged from memory instead." \(fn (GROUP . NAME) &key GROUP NAME PATH DEPTH FLAGS FEATURES)"
(if (car plist) (let ((module
(let* ((depth (ensure-list (or (plist-get plist :depth) 0))) (make-doom-module
(idepth (or (cdr depth) (car depth))) :index (hash-table-count doom-modules)
(cdepth (car depth)) :group (or (plist-get plist :group) group)
(idx (hash-table-count doom-modules))) :name (or (plist-get plist :name) name)
;; PERF: Doom caches module index, flags, and features in symbol plists :path (plist-get plist :path)
;; for fast lookups in `modulep!' and elsewhere. plists are lighter :flags (plist-get plist :flags)
;; and faster than hash tables for datasets this size, and this :features () ; TODO
;; information is looked up *very* often. The structure of this cache :depth
;; should match `doom-module-context's. (if (not (plist-member plist :depth))
(put category module '(0 . 0)
(vector idx idepth cdepth (let ((depth (plist-get plist :depth)))
category module (cl-check-type depth (or integer cons))
(plist-get plist :flags) (cond ((integerp depth) (cons depth depth))
(plist-get plist :features))) ((consp depth) (cons (or (car depth) 0)
;; The hash table will always been Doom's formal storage for (or (cdr depth) 0)))
;; modules. ((error "Invalid DEPTH value: %S" depth))))))))
(puthash (cons category module) plist doom-modules)) (doom-log 2 "module-put: %s" module)
(remhash (cons category module) doom-modules) (prog1 (puthash (cons group name) module doom-modules)
(cl-remf (symbol-plist category) module))) ;; PERF: Doom caches module index, flags, and features in symbol plists
;; for fast lookups in `modulep!' and elsewhere. plists are lighter and
;; faster than hash tables for datasets this size, and this information
;; is looked up *very* often.
(put group name (doom-module->context module)))))
(defun doom-module-active-p (group module &optional flags)
"Return t if GROUP MODULE is active, and with FLAGS (if given)."
(declare (side-effect-free t))
(when-let ((val (doom-module-get (cons group module) (if flags :flags))))
(or (null flags)
(doom-module--has-flag-p flags val))))
(defun doom-module-exists-p (group module)
"Returns t if GROUP MODULE is present in any active source."
(declare (side-effect-free t))
(if (doom-module-get group module) t))
(cl-defun doom-module--depth< (keya keyb &optional initorder?)
"Return t if module with KEY-A comes before another with KEY-B.
If INITORDER? is non-nil, grab the car of the module's :depth, rather than it's
cdr. See `doom-module-put' for details about the :depth property."
(declare (pure t) (side-effect-free t))
(let* ((adepth (doom-module-get keya :depth))
(bdepth (doom-module-get keyb :depth))
(adepth (if initorder? (car adepth) (cdr adepth)))
(bdepth (if initorder? (car bdepth) (cdr bdepth))))
(if (or (null adepth) (null bdepth)
(= adepth bdepth))
(< (or (doom-module-get keya :index) 0)
(or (doom-module-get keyb :index) 0))
(< adepth bdepth))))
(defun doom-module-list (&optional paths-or-all initorder?) (defun doom-module-list (&optional paths-or-all initorder?)
"Return a list of (:group . name) module keys in order of their :depth. "Return a list of (:group . name) module keys in order of their :depth.
@ -272,8 +382,7 @@ underneath it. If non-nil, return the same, but search `doom-module-load-path'
:depth, followed by disabled modules in lexicographical order (unless a :depth :depth, followed by disabled modules in lexicographical order (unless a :depth
is specified in their .doommodule). is specified in their .doommodule).
If INITORDER? is non-nil, sort modules by their initdepth, rather than their If INITORDER? is non-nil, sort modules by the CAR of that module's :depth."
configdepth. See `doom-module-set' for details."
(sort (if paths-or-all (sort (if paths-or-all
(delete-dups (delete-dups
(append (seq-remove #'cdr (doom-module-list nil initorder?)) (append (seq-remove #'cdr (doom-module-list nil initorder?))
@ -285,72 +394,64 @@ configdepth. See `doom-module-set' for details."
:mindepth 1 :mindepth 1
:depth 1))) :depth 1)))
(hash-table-keys doom-modules)) (hash-table-keys doom-modules))
(let ((idx (if initorder? 1 2))) (doom-rpartial #'doom-module--depth< initorder?)))
(lambda! ((groupa . namea) (groupb . nameb))
(let ((a (get groupa namea))
(b (get groupb nameb)))
(or (null b)
(and
a (let ((adepth (aref a idx))
(bdepth (aref b idx)))
(if (= adepth bdepth)
(< (aref a 0) (aref b 0))
(< adepth bdepth))))))))))
(defun doom-module-expand-path (category module &optional file) (defun doom-module-expand-path (key &optional file)
"Expands a path to FILE relative to CATEGORY and MODULE. "Expands a path to FILE relative to KEY, a cons cell: (GROUP . NAME)
CATEGORY is a keyword. MODULE is a symbol. FILE is an optional string path. GROUP is a keyword. MODULE is a symbol. FILE is an optional string path.
If the category isn't enabled this returns nil. For finding disabled modules use If the group isn't enabled this returns nil. For finding disabled modules use
`doom-module-locate-path'." `doom-module-locate-path' instead."
(when-let (path (doom-module-get category module :path)) (when-let ((path (doom-module-get key :path)))
(if file (if file
(file-name-concat path file) (file-name-concat path file)
path))) path)))
(defun doom-module-locate-path (category &optional module file) (defun doom-module-locate-path (key &optional file)
"Searches `doom-module-load-path' to find the path to a module. "Searches `doom-module-load-path' to find the path to a module by KEY.
CATEGORY is a keyword (e.g. :lang) and MODULE is a symbol (e.g. 'python). FILE KEY is a cons cell (GROUP . NAME), where GROUP is a keyword (e.g. :lang) and
is a string that will be appended to the resulting path. If no path exists, this NAME is a symbol (e.g. \\='python). FILE is a string that will be appended to
returns nil, otherwise an absolute path." the resulting path. If said path doesn't exist, this returns nil, otherwise an
absolute path."
(let (file-name-handler-alist) (let (file-name-handler-alist)
(if-let (path (doom-module-expand-path category module file)) (if-let ((path (doom-module-expand-path key file)))
(if (or (null file) (if (or (null file)
(file-exists-p path)) (file-exists-p path))
path) path)
(let* ((category (doom-keyword-name category)) (cl-destructuring-bind (group . module) (doom-module-key key)
(module (if module (symbol-name module))) (let* ((group (doom-keyword-name group))
(path (file-name-concat category module file))) (module (if module (symbol-name module)))
(if file (path (file-name-concat group module file)))
;; PERF: locate-file-internal is a little faster for finding files, (if file
;; but its interface for finding directories is clumsy. ;; PERF: locate-file-internal is a little faster for finding files,
(locate-file-internal path doom-module-load-path '("" ".elc" ".el")) ;; but its interface for finding directories is clumsy.
(cl-loop for default-directory in doom-module-load-path (locate-file-internal path doom-module-load-path '("" ".elc" ".el"))
if (file-exists-p path) (cl-loop for default-directory in doom-module-load-path
return (expand-file-name path))))))) if (file-exists-p path)
return (expand-file-name path))))))))
(defun doom-module-locate-paths (module-list file) (defun doom-module-locate-paths (module-list file)
"Return all existing paths to FILE under each module in MODULE-LIST. "Return all existing paths to FILE under each module in MODULE-LIST.
MODULE-LIST is a list of cons cells (GROUP . NAME). See `doom-module-list' for MODULE-LIST is a list of cons cells (GROUP . NAME). See `doom-module-list' for
an example." an example."
(cl-loop for (group . name) in (or module-list (doom-module-list)) (cl-loop for key in (or module-list (doom-module-list))
if (doom-module-locate-path group name file) if (doom-module-locate-path key file)
collect it)) collect it))
(defun doom-module-from-path (path &optional enabled-only) (defun doom-module-from-path (path &optional enabled-only?)
"Returns a cons cell (CATEGORY . MODULE) derived from PATH (a file path). "Returns a cons cell (GROUP . NAME) derived from PATH (a file path).
If ENABLED-ONLY, return nil if the containing module isn't enabled." If ENABLED-ONLY?, return nil if the containing module isn't enabled."
(let* ((file-name-handler-alist nil) (let* ((file-name-handler-alist nil)
(path (expand-file-name path))) (path (expand-file-name path)))
(save-match-data (save-match-data
(cond ((string-match "/modules/\\([^/]+\\)/\\([^/]+\\)\\(?:/.*\\)?$" path) (cond ((string-match "/modules/\\([^/]+\\)/\\([^/]+\\)\\(?:/.*\\)?$" path)
(when-let* ((category (doom-keyword-intern (match-string 1 path))) (when-let* ((group (doom-keyword-intern (match-string 1 path)))
(module (intern (match-string 2 path)))) (name (intern (match-string 2 path))))
(and (or (null enabled-only) (and (or (null enabled-only?)
(doom-module-p category module)) (doom-module-active-p group name))
(cons category module)))) (cons group name))))
((string-match (concat "^" (regexp-quote doom-core-dir)) path) ((string-match (concat "^" (regexp-quote doom-core-dir)) path)
(cons :core nil)) (cons :core nil))
((string-match (concat "^" (regexp-quote doom-user-dir)) path) ((string-match (concat "^" (regexp-quote doom-user-dir)) path)
@ -363,9 +464,8 @@ The list is in no particular order and its file paths are absolute. If
MODULE-DIRS is non-nil, include all modules (even disabled ones) available in MODULE-DIRS is non-nil, include all modules (even disabled ones) available in
those directories." those directories."
(declare (pure t) (side-effect-free t)) (declare (pure t) (side-effect-free t))
(cl-loop with module-load-path = (or module-load-path doom-module-load-path) (mapcar #'doom-module-locate-path
for (cat . mod) in (doom-module-list module-load-path) (doom-module-list (or module-load-path doom-module-load-path))))
collect (doom-module-locate-path cat mod)))
(defun doom-module-mplist-map (fn mplist) (defun doom-module-mplist-map (fn mplist)
"Apply FN to each module in MPLIST." "Apply FN to each module in MPLIST."
@ -373,14 +473,14 @@ those directories."
(inhibit-message doom-inhibit-module-warnings) (inhibit-message doom-inhibit-module-warnings)
obsolete obsolete
results results
category m) group m)
(while mplist (while mplist
(setq m (pop mplist)) (setq m (pop mplist))
(cond ((keywordp m) (cond ((keywordp m)
(setq category m (setq group m
obsolete (assq m doom-obsolete-modules))) obsolete (assq m doom-obsolete-modules)))
((null category) ((null group)
(error "No module category specified for %s" m)) (error "No module group specified for %s" m))
((and (listp m) (keywordp (car m))) ((and (listp m) (keywordp (car m)))
(pcase (car m) (pcase (car m)
(:cond (:cond
@ -402,12 +502,12 @@ those directories."
(print! (warn "%s module was removed")) (print! (warn "%s module was removed"))
(if (cdr newkeys) (if (cdr newkeys)
(print! (warn "%s module was removed and split into the %s modules") (print! (warn "%s module was removed and split into the %s modules")
(list category module) (list group module)
(mapconcat #'prin1-to-string newkeys ", ")) (mapconcat #'prin1-to-string newkeys ", "))
(print! (warn "%s module was moved to %s") (print! (warn "%s module was moved to %s")
(list category module) (list group module)
(car newkeys))) (car newkeys)))
(push category mplist) (push group mplist)
(dolist (key newkeys) (dolist (key newkeys)
(push (if flags (push (if flags
(nconc (cdr key) flags) (nconc (cdr key) flags)
@ -415,7 +515,11 @@ those directories."
mplist) mplist)
(push (car key) mplist)) (push (car key) mplist))
(throw 'doom-modules t)))) (throw 'doom-modules t))))
(push (funcall fn category module :flags (if (listp m) (cdr m))) (doom-log "module: %s %s %s -> %s" group module (or flags "")
(doom-module-locate-path (cons group module)))
(push (funcall fn (cons group module)
:flags (if (listp m) (cdr m))
:path (doom-module-locate-path (cons group module)))
results)))))) results))))))
(when noninteractive (when noninteractive
(setq doom-inhibit-module-warnings t)) (setq doom-inhibit-module-warnings t))
@ -433,84 +537,66 @@ those directories."
"Bootstraps DOOM Emacs and its modules. "Bootstraps DOOM Emacs and its modules.
If the first item in MODULES doesn't satisfy `keywordp', MODULES is evaluated, If the first item in MODULES doesn't satisfy `keywordp', MODULES is evaluated,
otherwise, MODULES is a multiple-property list (a plist where each key can have otherwise, MODULES is a variadic-property list (a plist whose key may be
multiple, linear values). followed by one or more values).
The bootstrap process involves making sure the essential directories exist, core This macro does nothing in interactive sessions, but in noninteractive session
packages are installed, `doom-autoloads-file' is loaded, `doom-packages-file' iterates through MODULES, enabling and initializing them. The order of modules
cache exists (and is loaded) and, finally, loads your private init.el (which in these blocks dictates their load order (unless given an explicit :depth)."
should contain your `doom!' block).
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 noninteractive `(when noninteractive
(doom-module-mplist-map (doom-module-mplist-map
(lambda (category module &rest plist) #'doom-module--put
(let ((path (doom-module-locate-path category module)))
(unless path
(print! (warn "Failed to locate a '%s %s' module") category module))
(apply #'doom-module-set category module
:path path
plist)))
,@(if (keywordp (car modules)) ,@(if (keywordp (car modules))
(list (list 'quote modules)) (list (list 'quote modules))
modules)) modules))
doom-modules)) t))
;; DEPRECATED Remove in 3.0 ;; DEPRECATED Remove in 3.0
(define-obsolete-function-alias 'featurep! 'modulep! "3.0.0") (define-obsolete-function-alias 'featurep! 'modulep! "3.0.0")
(defmacro modulep! (category &optional module flag) (defmacro modulep! (group &optional module &rest flags)
"Return t if :CATEGORY MODULE (and +FLAGS) are enabled. "Return t if :GROUP MODULE (and +FLAGS) are enabled.
If FLAG is provided, returns t if CATEGORY MODULE has FLAG enabled. If FLAGS is provided, returns t if GROUP MODULE has all of FLAGS enabled.
(modulep! :config default +flag) (modulep! :config default +flag)
(modulep! :config default +flag1 +flag2 +flag3)
CATEGORY and MODULE may be omitted when this macro is used from a Doom module's GROUP and MODULE may be omitted when this macro is used from a Doom module's
source (except your DOOMDIR, which is a special module). Like so: source (except your $DOOMDIR, which is a special module). Like so:
(modulep! +flag3 +flag1 +flag2)
(modulep! +flag) (modulep! +flag)
FLAGS can be negated. E.g. This will return non-nil if ':tools lsp' is enabled
without `+eglot':
(modulep! :tools lsp -eglot)
To interpolate dynamic values, use comma:
(let ((flag '-eglot))
(modulep! :tools lsp ,flag))
For more about modules and flags, see `doom!'." For more about modules and flags, see `doom!'."
;; PERF: This macro bypasses the module API to spare startup their runtime (if (keywordp group)
;; cost, as `modulep!' gets called *a lot* during startup. In the future, (if flags
;; Doom will byte-compile its core files. At that time, we can use it again. `(doom-module--has-flag-p
(and (cond (flag (memq flag (aref (or (get category module) doom-module--empty-context) (doom-module (backquote ,group) (backquote ,module) :flags)
(doom-module--context-field :flags)))) (backquote ,flags))
(module (get category module)) `(and (get (backquote ,group) (backquote ,module)) t))
((aref doom-module-context 0) (let ((flags (delq nil (cons group (cons module flags)))))
(memq category (aref doom-module-context (if (doom-module-context-index doom-module-context)
(doom-module--context-field :flags)))) `(doom-module--has-flag-p
((let ((file ',(doom-module-context-flags doom-module-context)
;; This must be expanded at the call site, not in (backquote ,flags))
;; `modulep!'s definition, to get the file we want. `(let ((file (file!)))
(macroexpand '(file!)))) (if-let ((module (doom-module-from-path file)))
(if-let (module (doom-module-from-path file)) (doom-module--has-flag-p
(memq category (aref (or (get (car module) (cdr module)) (doom-module (car module) (cdr module) :flags)
doom-module--empty-context) (backquote ,flags))
(doom-module--context-field :flags))) (error "(modulep! %s) couldn't resolve current module from %s"
(error "(modulep! %s %s %s) couldn't figure out what module it was called from (in %s)" (backquote ,flags) (abbreviate-file-name file))))))))
category module flag file)))))
t))
;;
;;; Defaults
;; Register Doom's two virtual module categories, representing Doom's core and
;; the user's config; which are always enabled.
(doom-module-set :core nil :path doom-core-dir :depth -110)
(doom-module-set :user nil :path doom-user-dir :depth '(-105 . 105))
;; DEPRECATED: I intend to phase out our internal usage of `use-package' and
;; move it to a :config use-package module. The macro is far too complex and
;; magical for our needs, but until this move is done, ':config use-package'
;; will remain a hardcoded module for backwards compatibility.
(doom-module-set :config 'use-package
:path (doom-module-locate-path :config 'use-package)
:depth -111)
(provide 'doom-modules) (provide 'doom-modules)
;;; doom-modules.el ends here ;;; doom-modules.el ends here

View File

@ -431,13 +431,13 @@ installed."
(push (cons (push (cons
name (plist-put name (plist-put
plist :modules plist :modules
(list (doom-module-context-key)))) (list (doom-module-context-key doom-module-context))))
doom-packages))))))))) doom-packages)))))))))
(user-error (user-error
(user-error (error-message-string e))) (user-error (error-message-string e)))
(error (error
(signal 'doom-package-error (signal 'doom-package-error
(list (doom-module-context-key) (list (doom-module-context-key doom-module-context)
file e))))) file e)))))
(defun doom-package-list (&optional module-list) (defun doom-package-list (&optional module-list)
@ -454,11 +454,11 @@ also be a list of module keys."
doom-disabled-packages doom-disabled-packages
doom-packages) doom-packages)
(letf! (defun read-packages (key) (letf! (defun read-packages (key)
(doom-module-context-with key (with-doom-module key
(when-let (file (doom-module-locate-path (when-let (file (doom-module-locate-path
(car key) (cdr key) doom-module-packages-file)) key doom-module-packages-file))
(doom-packages--read file nil 'noerror)))) (doom-packages--read file nil 'noerror))))
(doom-context-with 'packages (with-doom-context 'packages
(let ((user? (assq :user module-list))) (let ((user? (assq :user module-list)))
(when user? (when user?
;; We load the private packages file twice to populate ;; We load the private packages file twice to populate

View File

@ -389,50 +389,54 @@ Defaults to the profile at `doom-profile-default'."
(let* ((init-modules-list (doom-module-list nil t)) (let* ((init-modules-list (doom-module-list nil t))
(config-modules-list (doom-module-list)) (config-modules-list (doom-module-list))
(pre-init-modules (pre-init-modules
(seq-filter (fn! (<= (doom-module-depth (car %) (cdr %) t) -100)) (seq-filter (fn! (<= (car (doom-module-get % :depth)) -100))
(remove '(:user) init-modules-list))) (remove '(:user . nil) init-modules-list)))
(init-modules (init-modules
(seq-filter (fn! (<= 0 (doom-module-depth (car %) (cdr %) t) 100)) (seq-filter (fn! (<= 0 (car (doom-module-get % :depth)) 100))
init-modules-list)) init-modules-list))
(config-modules (config-modules
(seq-filter (fn! (<= 0 (doom-module-depth (car %) (cdr %)) 100)) (seq-filter (fn! (<= 0 (cdr (doom-module-get % :depth)) 100))
config-modules-list)) config-modules-list))
(post-config-modules (post-config-modules
(seq-filter (fn! (>= (doom-module-depth (car %) (cdr %)) 100)) (seq-filter (fn! (>= (cdr (doom-module-get % :depth)) 100))
config-modules-list)) config-modules-list))
(init-file doom-module-init-file) (init-file doom-module-init-file)
(config-file doom-module-config-file)) (config-file doom-module-config-file))
(letf! ((defun module-loader (group name file &optional noerror) (letf! ((defun module-loader (key file)
(doom-module-context-with (cons group name) (let ((noextfile (file-name-sans-extension file)))
`(let ((doom-module-context ,doom-module-context)) `(with-doom-module ',key
(doom-load ,(pcase key
,(pcase (cons group name) ('(:core . nil)
('(:core . nil) `(doom-load
`(file-name-concat (file-name-concat
doom-core-dir ,(file-name-nondirectory (file-name-sans-extension file)))) doom-core-dir ,(file-name-nondirectory noextfile))
('(:user . nil) t))
`(file-name-concat ('(:user . nil)
doom-user-dir ,(file-name-nondirectory (file-name-sans-extension file)))) `(doom-load
(_ (abbreviate-file-name (file-name-sans-extension file)))) (file-name-concat
t)))) doom-user-dir ,(file-name-nondirectory noextfile))
(defun module-list-loader (modules file &optional noerror) t))
(cl-loop for (cat . mod) in modules (_
if (doom-module-locate-path cat mod file) (when (doom-file-cookie-p file "if" t)
collect (module-loader cat mod it noerror)))) `(doom-load ,(abbreviate-file-name noextfile) t)))))))
(defun module-list-loader (modules file)
(cl-loop for key in modules
if (doom-module-locate-path key file)
collect (module-loader key it))))
;; FIX: Same as above (see `doom-profile--generate-init-vars'). ;; FIX: Same as above (see `doom-profile--generate-init-vars').
`((if (or (doom-context-p 'init) `((if (or (doom-context-p 'init)
(doom-context-p 'reload)) (doom-context-p 'reload))
(doom-context-with 'modules (with-doom-context 'modules
(set 'doom-modules ',doom-modules) (set 'doom-modules ',doom-modules)
(set 'doom-disabled-packages ',doom-disabled-packages) (set 'doom-disabled-packages ',doom-disabled-packages)
;; Cache module state and flags in symbol plists for quick lookup by ;; Cache module state and flags in symbol plists for quick lookup
;; `modulep!' later. ;; by `modulep!' later.
,@(cl-loop ,@(cl-loop
for (category . modules) in (seq-group-by #'car config-modules-list) for (category . modules) in (seq-group-by #'car config-modules-list)
collect collect
`(setplist ',category `(setplist ',category
(quote ,(cl-loop for (_ . module) in modules (quote ,(cl-loop for (_ . module) in modules
nconc `(,module ,(get category module)))))) nconc `(,module ,(doom-module->context (cons category module)))))))
(let ((old-custom-file custom-file)) (let ((old-custom-file custom-file))
,@(module-list-loader pre-init-modules init-file) ,@(module-list-loader pre-init-modules init-file)
(doom-run-hooks 'doom-before-modules-init-hook) (doom-run-hooks 'doom-before-modules-init-hook)
@ -441,7 +445,7 @@ Defaults to the profile at `doom-profile-default'."
(doom-run-hooks 'doom-before-modules-config-hook) (doom-run-hooks 'doom-before-modules-config-hook)
,@(module-list-loader config-modules config-file) ,@(module-list-loader config-modules config-file)
(doom-run-hooks 'doom-after-modules-config-hook) (doom-run-hooks 'doom-after-modules-config-hook)
,@(module-list-loader post-config-modules config-file t) ,@(module-list-loader post-config-modules config-file)
(when (eq custom-file old-custom-file) (when (eq custom-file old-custom-file)
(doom-load custom-file 'noerror))))))))) (doom-load custom-file 'noerror)))))))))

View File

@ -601,7 +601,7 @@ wasn't active when this was called."
(doom-log ":context: -%s %s" context doom-context) (doom-log ":context: -%s %s" context doom-context)
(setq doom-context (delq context doom-context)))) (setq doom-context (delq context doom-context))))
(defmacro doom-context-with (contexts &rest body) (defmacro with-doom-context (contexts &rest body)
"Evaluate BODY with CONTEXTS added to `doom-context'." "Evaluate BODY with CONTEXTS added to `doom-context'."
(declare (indent 1)) (declare (indent 1))
`(let ((doom-context doom-context)) `(let ((doom-context doom-context))

View File

@ -148,9 +148,13 @@ hoist buggy forms into autoloads.")
;; So `autoload-generate-file-autoloads' knows where to write it ;; So `autoload-generate-file-autoloads' knows where to write it
(target-buffer (current-buffer)) (target-buffer (current-buffer))
(module (doom-module-from-path file)) (module (doom-module-from-path file))
(generated-autoload-load-name (abbreviate-file-name (file-name-sans-extension file))) (generated-autoload-load-name
(module-enabled-p (and (doom-module-p (car module) (cdr module)) (abbreviate-file-name (file-name-sans-extension file)))
(doom-file-cookie-p file "if" t)))) (module-enabled-p
(and (doom-module-active-p (car module) (cdr module))
(doom-file-cookie-p file "if" t)))
;; (load-path (cons doom-modules-dir load-path))
)
(save-excursion (save-excursion
(when module-enabled-p (when module-enabled-p
(quiet! (autoload-generate-file-autoloads file target-buffer))) (quiet! (autoload-generate-file-autoloads file target-buffer)))

View File

@ -104,7 +104,7 @@ Runs `doom-after-reload-hook' afterwards."
(interactive) (interactive)
(mapc #'require (cdr doom-incremental-packages)) (mapc #'require (cdr doom-incremental-packages))
(doom--if-compile doom-reload-command (doom--if-compile doom-reload-command
(doom-context-with '(reload modules) (with-doom-context '(reload modules)
(doom-run-hooks 'doom-before-reload-hook) (doom-run-hooks 'doom-before-reload-hook)
(doom-load (file-name-concat doom-user-dir doom-module-init-file) t) (doom-load (file-name-concat doom-user-dir doom-module-init-file) t)
(with-demoted-errors "PRIVATE CONFIG ERROR: %s" (with-demoted-errors "PRIVATE CONFIG ERROR: %s"
@ -129,7 +129,7 @@ line."
(interactive) (interactive)
(require 'doom-profiles) (require 'doom-profiles)
;; TODO: Make this more robust ;; TODO: Make this more robust
(doom-context-with 'reload (with-doom-context 'reload
(dolist (file (mapcar #'car doom-profile-generators)) (dolist (file (mapcar #'car doom-profile-generators))
(when (string-match-p "/[0-9]+-loaddefs[.-]" file) (when (string-match-p "/[0-9]+-loaddefs[.-]" file)
(load (doom-path doom-profile-dir doom-profile-init-dir-name file) (load (doom-path doom-profile-dir doom-profile-init-dir-name file)
@ -145,7 +145,7 @@ Doing so from within Emacs will taint your shell environment.
An envvar file contains a snapshot of your shell environment, which can be An envvar file contains a snapshot of your shell environment, which can be
imported into Emacs." imported into Emacs."
(interactive) (interactive)
(doom-context-with 'reload (with-doom-context 'reload
(let ((default-directory doom-emacs-dir)) (let ((default-directory doom-emacs-dir))
(with-temp-buffer (with-temp-buffer
(doom-load-envvars-file doom-env-file) (doom-load-envvars-file doom-env-file)

View File

@ -317,8 +317,8 @@ ready to be pasted in a bug report on github."
do (setq lastcat cat) do (setq lastcat cat)
and collect lastcat and collect lastcat
collect collect
(let* ((flags (doom-module-get lastcat mod :flags)) (let* ((flags (doom-module-get (cons lastcat mod) :flags))
(path (doom-module-get lastcat mod :path)) (path (doom-module-get (cons lastcat mod) :path))
(module (module
(append (append
(cond ((null path) (cond ((null path)

View File

@ -144,7 +144,7 @@ MATCH is a string regexp. Only entries that match it will be included."
result))) result)))
;;;###autoload ;;;###autoload
(defun doom-file-cookie-p (file &optional cookie null-value) (defun doom-file-cookie (file &optional cookie null-value)
"Returns the evaluated result of FORM in a ;;;###COOKIE FORM at the top of "Returns the evaluated result of FORM in a ;;;###COOKIE FORM at the top of
FILE. FILE.
@ -158,11 +158,24 @@ return NULL-VALUE."
(insert-file-contents file nil 0 256) (insert-file-contents file nil 0 256)
(if (re-search-forward (format "^;;;###%s " (regexp-quote (or cookie "if"))) (if (re-search-forward (format "^;;;###%s " (regexp-quote (or cookie "if")))
nil t) nil t)
(doom-module-context-with (doom-module-from-path file) (sexp-at-point)
(let ((load-file-name file))
(eval (sexp-at-point) t)))
null-value))) null-value)))
;;;###autoload
(defun doom-file-cookie-p (file &optional cookie null-value)
"Returns the evaluated result of FORM in a ;;;###COOKIE FORM at the top of
FILE.
If COOKIE doesn't exist, or cookie isn't within the first 256 bytes of FILE,
return NULL-VALUE."
(let ((sexp (doom-file-cookie file cookie null-value)))
(if (equal sexp null-value)
null-value
(with-temp-buffer
(with-doom-module (doom-module-from-path file)
(let ((load-file-name file))
(eval (doom-file-cookie file cookie null-value) t)))))))
;;;###autoload ;;;###autoload
(defmacro file-exists-p! (files &optional directory) (defmacro file-exists-p! (files &optional directory)
"Returns non-nil if the FILES in DIRECTORY all exist. "Returns non-nil if the FILES in DIRECTORY all exist.

View File

@ -344,10 +344,10 @@ without needing to check if they are available."
(defun doom--help-modules-list () (defun doom--help-modules-list ()
(cl-loop for (cat . mod) in (doom-module-list 'all) (cl-loop for (cat . mod) in (doom-module-list 'all)
for readme-path = (or (doom-module-locate-path cat mod "README.org") for readme-path = (or (doom-module-locate-path (cons cat mod) "README.org")
(doom-module-locate-path cat mod)) (doom-module-locate-path (cons cat mod)))
for format = (if mod (format "%s %s" cat mod) (format "%s" cat)) for format = (if mod (format "%s %s" cat mod) (format "%s" cat))
if (doom-module-p cat mod) if (doom-module-active-p cat mod)
collect (list format readme-path) collect (list format readme-path)
else if (and cat mod) else if (and cat mod)
collect (list (propertize format 'face 'font-lock-comment-face) collect (list (propertize format 'face 'font-lock-comment-face)
@ -630,8 +630,7 @@ If prefix arg is present, refresh the cache."
(:core doom-core-dir) (:core doom-core-dir)
(:user doom-user-dir) (:user doom-user-dir)
(category (category
(doom-module-locate-path category (doom-module-locate-path (cons category (cdr m))))))
(cdr m)))))
(readme-path (expand-file-name "README.org" module-path))) (readme-path (expand-file-name "README.org" module-path)))
(insert indent) (insert indent)
(doom--help-insert-button (doom--help-insert-button

View File

@ -49,8 +49,8 @@
(or buffer-file-name (or buffer-file-name
(bound-and-true-p org-src-source-file-name))) (bound-and-true-p org-src-source-file-name)))
(package (package
(doom-context-with 'packages (with-doom-context 'packages
(doom-module-context-with (doom-module-from-path buffer-file-name) (with-doom-module (doom-module-from-path buffer-file-name)
(eval (sexp-at-point) t))))) (eval (sexp-at-point) t)))))
(list :beg beg (list :beg beg
:end end :end end
@ -164,14 +164,14 @@ each package."
(list (intern (car module)) (list (intern (car module))
(ignore-errors (intern (cadr module))) (ignore-errors (intern (cadr module)))
current-prefix-arg))) current-prefix-arg)))
(mapc (lambda! ((cat . mod)) (mapc (lambda! (key)
(if-let (packages-file (doom-module-locate-path cat mod doom-module-packages-file)) (if-let (packages-file (doom-module-locate-path key doom-module-packages-file))
(with-current-buffer (with-current-buffer
(or (get-file-buffer packages-file) (or (get-file-buffer packages-file)
(find-file-noselect packages-file)) (find-file-noselect packages-file))
(doom/bump-packages-in-buffer select) (doom/bump-packages-in-buffer select)
(save-buffer)) (save-buffer))
(message "Module %s has no packages.el file" (cons cat mod)))) (message "Module %s has no packages.el file" key)))
(if module (if module
(list (cons category module)) (list (cons category module))
(cl-remove-if-not (lambda (m) (eq (car m) category)) (cl-remove-if-not (lambda (m) (eq (car m) category))
@ -188,7 +188,7 @@ each package."
(unless modules (unless modules
(user-error "This package isn't installed by any Doom module")) (user-error "This package isn't installed by any Doom module"))
(dolist (module modules) (dolist (module modules)
(when (doom-module-locate-path (car module) (cdr module) doom-module-packages-file) (when (doom-module-locate-path module doom-module-packages-file)
(doom/bump-module (car module) (cdr module)))))) (doom/bump-module (car module) (cdr module))))))

View File

@ -151,7 +151,7 @@ If DIR is not a project, it will be indexed (but not cached)."
;; Intentionally avoid `helm-projectile-find-file', because it runs ;; Intentionally avoid `helm-projectile-find-file', because it runs
;; asynchronously, and thus doesn't see the lexical ;; asynchronously, and thus doesn't see the lexical
;; `default-directory' ;; `default-directory'
(if (doom-module-p :completion 'ivy) (if (doom-module-active-p :completion 'ivy)
#'counsel-projectile-find-file #'counsel-projectile-find-file
#'projectile-find-file))) #'projectile-find-file)))
((and (bound-and-true-p ivy-mode) ((and (bound-and-true-p ivy-mode)
@ -175,9 +175,9 @@ If DIR is not a project, it will be indexed (but not cached)."
"Traverse a file structure starting linearly from DIR." "Traverse a file structure starting linearly from DIR."
(let ((default-directory (file-truename (expand-file-name dir)))) (let ((default-directory (file-truename (expand-file-name dir))))
(call-interactively (call-interactively
(cond ((doom-module-p :completion 'ivy) (cond ((doom-module-active-p :completion 'ivy)
#'counsel-find-file) #'counsel-find-file)
((doom-module-p :completion 'helm) ((doom-module-active-p :completion 'helm)
#'helm-find-files) #'helm-find-files)
(#'find-file))))) (#'find-file)))))

View File

@ -2,6 +2,6 @@
;;; completion/helm/doctor.el ;;; completion/helm/doctor.el
(dolist (module '(ivy ido vertico)) (dolist (module '(ivy ido vertico))
(when (doom-module-p :completion module) (when (doom-module-active-p :completion module)
(error! "This module is incompatible with :completion %s; disable one or the other" (error! "This module is incompatible with :completion %s; disable one or the other"
module))) module)))

View File

@ -2,6 +2,6 @@
;;; completion/ido/doctor.el ;;; completion/ido/doctor.el
(dolist (module '(helm ivy vertico)) (dolist (module '(helm ivy vertico))
(when (doom-module-p :completion module) (when (doom-module-active-p :completion module)
(error! "This module is incompatible with :completion %s; disable one or the other" (error! "This module is incompatible with :completion %s; disable one or the other"
module))) module)))

View File

@ -2,6 +2,6 @@
;;; completion/ivy/doctor.el ;;; completion/ivy/doctor.el
(dolist (module '(helm ido vertico)) (dolist (module '(helm ido vertico))
(when (doom-module-p :completion module) (when (doom-module-active-p :completion module)
(error! "This module is incompatible with :completion %s; disable one or the other" (error! "This module is incompatible with :completion %s; disable one or the other"
module))) module)))

View File

@ -2,7 +2,7 @@
;;; completion/vertico/doctor.el ;;; completion/vertico/doctor.el
(dolist (module '(ivy helm ido)) (dolist (module '(ivy helm ido))
(when (doom-module-p :completion module) (when (doom-module-active-p :completion module)
(error! "This module is incompatible with :completion %s; disable one or the other" (error! "This module is incompatible with :completion %s; disable one or the other"
module))) module)))

View File

@ -1,4 +1,5 @@
;;; editor/evil/init.el -*- lexical-binding: t; -*- ;;; editor/evil/init.el -*- lexical-binding: t; -*-
;;;###if (modulep! +everywhere)
(defvar evil-collection-key-blacklist) (defvar evil-collection-key-blacklist)
@ -22,9 +23,7 @@
;; disable modules, and to reduce the effort required to maintain our copy of ;; disable modules, and to reduce the effort required to maintain our copy of
;; `evil-collection-list' (now I can just copy it from time to time). ;; `evil-collection-list' (now I can just copy it from time to time).
(when (and (not noninteractive) (unless (or noninteractive (doom-context-p 'reload))
(not (doom-context-p 'reload))
(modulep! +everywhere))
(setq evil-collection-company-use-tng (modulep! :completion company +tng)) (setq evil-collection-company-use-tng (modulep! :completion company +tng))

View File

@ -13,11 +13,11 @@ to a pop up buffer."
(debug-on-error t)) (debug-on-error t))
(unwind-protect (unwind-protect
(condition-case-unless-debug e (condition-case-unless-debug e
(doom-module-context-with (with-doom-module
(doom-module-from-path (doom-module-from-path
(or (buffer-file-name (buffer-base-buffer)) (or (buffer-file-name (buffer-base-buffer))
default-directory)) default-directory))
(doom-context-with 'eval (with-doom-context 'eval
(eval-region beg end buffer load-read-function)) (eval-region beg end buffer load-read-function))
(with-current-buffer buffer (with-current-buffer buffer
(let ((pp-max-width nil)) (let ((pp-max-width nil))

View File

@ -157,12 +157,11 @@ exist, and `org-link' otherwise."
(cl-destructuring-bind (&key category module flag) (cl-destructuring-bind (&key category module flag)
(+org-link--read-module-spec module-path) (+org-link--read-module-spec module-path)
(when category (when category
(let ((doom-modules-dirs (list doom-modules-dir))) (if-let* ((path (doom-module-locate-path (cons category module)))
(if-let* ((path (doom-module-locate-path category module)) (path (or (car (doom-glob path "README.org"))
(path (or (car (doom-glob path "README.org")) path)))
path))) (find-file path)
(find-file path) (user-error "Can't find Doom module '%s'" module-path)))
(user-error "Can't find Doom module '%s'" module-path))))
(when flag (when flag
(goto-char (point-min)) (goto-char (point-min))
(when (and (re-search-forward "^\\*+ \\(?:TODO \\)?Module flags") (when (and (re-search-forward "^\\*+ \\(?:TODO \\)?Module flags")
@ -179,13 +178,13 @@ exist, and `org-link' otherwise."
(cl-destructuring-bind (&key category module flag) (cl-destructuring-bind (&key category module flag)
(+org-link--read-module-spec module-path) (+org-link--read-module-spec module-path)
(let ((overall-face (let ((overall-face
(if (and category (doom-module-locate-path category module)) (if (and category (doom-module-locate-path (cons category module)))
'((:underline nil) org-link org-block bold) '((:underline nil) org-link org-block bold)
'(shadow org-block bold))) '(shadow org-block bold)))
(icon-face (icon-face
(cond (cond
((doom-module-p category module flag) 'success) ((doom-module-active-p category module flag) 'success)
((and category (doom-module-locate-path category module)) 'warning) ((and category (doom-module-locate-path (cons category module))) 'warning)
(t 'error)))) (t 'error))))
(add-text-properties (add-text-properties
start end start end
@ -294,9 +293,9 @@ exist, and `org-link' otherwise."
(cl-destructuring-bind (&key category module flag) (cl-destructuring-bind (&key category module flag)
(+org-link--read-module-spec (org-element-property :path link)) (+org-link--read-module-spec (org-element-property :path link))
(cond (cond
((doom-module-p category module) ((doom-module-active-p category module)
(propertize "enabled" 'face 'success)) (propertize "enabled" 'face 'success))
((and category (doom-module-locate-path category module)) ((and category (doom-module-locate-path (cons category module)))
(propertize "disabled" 'face 'error)) (propertize "disabled" 'face 'error))
(t (propertize "unknown" 'face '(bold error))))))) (t (propertize "unknown" 'face '(bold error)))))))
("doom-executable" ("doom-executable"

View File

@ -1301,7 +1301,7 @@ between the two."
)) ))
;;; Custom org modules ;;; Custom org modules
(dolist (flag (doom-module-context-get :flags)) (dolist (flag (doom-module :lang 'org :flags))
(load! (concat "contrib/" (substring (symbol-name flag) 1)) nil t)) (load! (concat "contrib/" (substring (symbol-name flag) 1)) nil t))
;; Add our general hooks after the submodules, so that any hooks the ;; Add our general hooks after the submodules, so that any hooks the

View File

@ -119,7 +119,7 @@
:config :config
(pcase-dolist (`((,category . ,modules) :after ,after :require ,libs) (pcase-dolist (`((,category . ,modules) :after ,after :require ,libs)
+debugger--dap-alist) +debugger--dap-alist)
(when (doom-module-p category (car modules) (cadr modules)) (when (doom-module-active-p category (car modules) (cadr modules))
(dolist (lib (ensure-list after)) (dolist (lib (ensure-list after))
(with-eval-after-load lib (with-eval-after-load lib
(mapc #'require (ensure-list libs)))))) (mapc #'require (ensure-list libs))))))