From 0b7b8800a2478588bde408c92fcdfa0e43a5baf0 Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Sat, 29 Jul 2017 00:11:07 +0200 Subject: [PATCH] Add support for module flags in doom! macro #158 For example: (doom! :feature (version-control +git)) I leave it to modules to interpret these flags, and they can be detected through one of the following: + (featurep! :feature version-control +git) + (featurep! +git) -- syntactic sugar, only available from within modules. + (doom-module-flags :feature version-control) -- returns a list of flags for this module. Flags are also available from packages.el files. --- core/core-packages.el | 108 ++++++++++++++++++++++++++++++------------ 1 file changed, 78 insertions(+), 30 deletions(-) diff --git a/core/core-packages.el b/core/core-packages.el index 89601ab0e..6ed46a678 100644 --- a/core/core-packages.el +++ b/core/core-packages.el @@ -90,6 +90,7 @@ missing) and shouldn't be deleted.") "A backup of `load-path' before it was altered by `doom-initialize'. Used as a base by `doom!' and for calculating how many packages exist.") +(defvar doom--module nil) (defvar doom--refresh-p nil) (setq load-prefer-newer (or noninteractive doom-debug-mode) @@ -206,20 +207,20 @@ This aggressively reloads core autoload files." :error)))))) (when (or force-p (not doom-modules)) (setq doom-modules nil) - (funcall load-fn (expand-file-name "init.el" doom-emacs-dir)) - (funcall load-fn (doom-module-path :private user-login-name "init.el") t) + (let (noninteractive) + (funcall load-fn (expand-file-name "init.el" doom-emacs-dir)) + (funcall load-fn (doom-module-path :private user-login-name "init.el") t)) (when load-p - (cl-loop for file - in (append (nreverse (file-expand-wildcards (expand-file-name "core*.el" doom-core-dir))) - (file-expand-wildcards (expand-file-name "autoload/*.el" doom-core-dir)) - (doom--module-paths "config.el")) - do (funcall load-fn file t))) + (mapc load-fn (file-expand-wildcards (expand-file-name "autoload/*.el" doom-core-dir)))) (doom|finalize)) (when (or force-p (not doom-packages)) (setq doom-packages nil) (funcall load-fn (expand-file-name "packages.el" doom-core-dir)) - (dolist (file (doom--module-paths "packages.el")) - (funcall load-fn file t))))) + (cl-loop for (module . submodule) in (doom--module-pairs) + for path = (doom-module-path module submodule "packages.el") + do + (let ((doom--module (cons module submodule))) + (funcall load-fn path t)))))) (defun doom-initialize-modules (modules) "Adds MODULES to `doom-modules'. MODULES must be in mplist format. @@ -240,8 +241,10 @@ This aggressively reloads core autoload files." if (file-directory-p path) collect (intern (file-name-nondirectory path)) into paths finally return (cons mode paths)))) + ((listp m) + (doom-module-enable mode (car m) (cdr m))) (t - (doom--enable-module mode m)))))) + (doom-module-enable mode m)))))) (defun doom-module-path (module submodule &optional file) "Get the full path to a module: e.g. :lang emacs-lisp maps to @@ -253,10 +256,24 @@ This aggressively reloads core autoload files." (expand-file-name (concat module "/" submodule "/" file) doom-modules-dir)) +(defun doom-module-flags (module submodule) + "Returns a list of flags provided for MODULE SUBMODULE." + (and (hash-table-p doom-modules) + (gethash (cons module submodule) doom-modules))) + (defun doom-module-loaded-p (module submodule) "Returns t if MODULE->SUBMODULE is present in `doom-modules'." - (and doom-modules - (gethash (cons module submodule) doom-modules))) + (and (doom-module-flags module submodule) t)) + +(defun doom-module-enable (module submodule &optional flags) + "Adds MODULE and SUBMODULE to `doom-modules', overwriting it if it exists. + +MODULE is a keyword, SUBMODULE is a symbol. e.g. :lang 'emacs-lisp. + +Used by `require!' and `depends-on!'." + (puthash (cons module submodule) + (doom-enlist (or flags t)) + doom-modules)) (defun doom--module-pairs () "Returns `doom-modules' as a list of (MODULE . SUBMODULE) cons cells. The list @@ -276,14 +293,7 @@ added, if the file exists." (when (file-exists-p path) (push path paths)))))) -(defun doom--enable-module (module submodule &optional force-p) - "Adds MODULE and SUBMODULE to `doom-modules', if it isn't already there (or if -FORCE-P is non-nil). MODULE is a keyword, SUBMODULE is a symbol. e.g. :lang -'emacs-lisp. -Used by `require!' and `depends-on!'." - (unless (or force-p (doom-module-loaded-p module submodule)) - (puthash (cons module submodule) t doom-modules))) (defun doom--display-benchmark () (message "Loaded %s packages in %.03fs" @@ -307,14 +317,14 @@ MODULES is an malformed plist of modules to load." (doom-initialize-modules modules) (when (and user-login-name (not (doom-module-loaded-p :private (intern user-login-name)))) - (doom--enable-module :private user-login-name)) + (doom-module-enable :private user-login-name)) `(let (file-name-handler-alist) (setq doom-modules ',doom-modules) (unless noninteractive (load ,(doom-module-path :private user-login-name "init") t t) ,@(cl-loop for (module . submodule) in (doom--module-pairs) - collect `(require! ,module ,submodule t)) + collect `(require! ,module ,submodule nil t)) (when (display-graphic-p) (require 'server) @@ -362,6 +372,32 @@ to have them return non-nil (or exploit that to overwrite Doom's config)." (t (error "'%s' isn't a valid hook for def-package-hook!" when)))) +(defmacro def-feature! (feature) + "Defines (and conditionally loads) FEATURE. + +FEATURE is a symbol representing a file in the current module, denoted by a '+' +prefix. e.g. +git. Flags are set in `doom!'. + +For example: + + ;; init.el + (doom! :lang (haskell +intero)) + + ;; modules/lang/haskell/config.el + (def-feature! +intero) ;; + (def-feature! +dante) + +lang/haskell/+intero.el will be loaded. + +Flags can be detected with `featurep!'. e.g. '(featurep! :lang haskell +intero)' +Or more concisely (if from inside a module) '(featurep! +intero)'." + `(cond ((featurep! ,(car doom--module) ,(cdr doom--module) ,feature) + (load! ,feature)) + (doom-debug-mode + (lwarn 'doom-module-feature :warning + "Feature %s in '%s %s' is not enabled" + ',feature ,(car doom--module) ',(cdr doom--module))))) + (defmacro load! (filesym &optional path noerror) "Load a file relative to the current executing file (`load-file-name'). @@ -386,28 +422,40 @@ If NOERROR is non-nil, don't throw an error if the file doesn't exist." (error "Could not find %s" filename)) (let ((file (expand-file-name (concat filename ".el") path))) (if (file-exists-p file) - `(load ,(file-name-sans-extension file) ,noerror ,(not doom-debug-mode)) + `(load ,(file-name-sans-extension file) ,noerror + ,(not doom-debug-mode)) (unless noerror (error "Could not load! file %s" file)))))) -(defmacro require! (module submodule &optional reload-p) +(defmacro require! (module submodule &optional flags reload-p) "Loads the module specified by MODULE (a property) and SUBMODULE (a symbol). The module is only loaded once. If RELOAD-P is non-nil, load it again." (let ((loaded-p (doom-module-loaded-p module submodule))) (when (or reload-p (not loaded-p)) (unless loaded-p - (doom--enable-module module submodule t)) + (doom-module-enable module submodule flags)) `(condition-case-unless-debug ex - (load! config ,(doom-module-path module submodule) t) + (let ((doom--module ',(cons module submodule))) + (load! config ,(doom-module-path module submodule) t)) ('error (lwarn 'doom-modules :error "%s in '%s %s' -> %s" - (car ex) ,module ',submodule (error-message-string ex))))))) + (car ex) ,module ',submodule + (error-message-string ex))))))) -(defmacro featurep! (module submodule) - "Convenience macro wrapper for `doom-module-loaded-p'." - (doom-module-loaded-p module submodule)) +(defmacro featurep! (module &optional submodule flag) + "A convenience macro wrapper for `doom-module-loaded-p'. It is evaluated at +compile-time/macro-expansion time." + (unless submodule + (unless doom--module + (error "featurep! was used incorrectly (doom--module wasn't unset)")) + (setq flag module + module (car doom--module) + submodule (cdr doom--module))) + (if flag + (and (memq flag (doom-module-flags module submodule)) t) + (doom-module-loaded-p module submodule))) ;; @@ -461,7 +509,7 @@ Only use this macro in a module's packages.el file. MODULE is a keyword, and SUBMODULE is a symbol. Under the hood, this simply loads MODULE SUBMODULE's packages.el file." - (doom--enable-module module submodule) + (doom-module-enable module submodule) `(load! packages ,(doom-module-path module submodule) t))