From 0b7b8800a2478588bde408c92fcdfa0e43a5baf0 Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Sat, 29 Jul 2017 00:11:07 +0200 Subject: [PATCH 1/5] 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)) From ac05f9a7630031a7d76f480e32e4907adb436bac Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Sat, 29 Jul 2017 00:14:44 +0200 Subject: [PATCH 2/5] Remove wildcard support from doom! macro "Explicit is better than implicit." --- core/core-packages.el | 7 ------- 1 file changed, 7 deletions(-) diff --git a/core/core-packages.el b/core/core-packages.el index 6ed46a678..aad5ddaec 100644 --- a/core/core-packages.el +++ b/core/core-packages.el @@ -234,13 +234,6 @@ This aggressively reloads core autoload files." (setq mode m)) ((not mode) (error "No namespace specified on `doom!' for %s" m)) - ((eq m '*) - (doom-initialize-modules - (cl-loop with modpath = (expand-file-name (substring (symbol-name mode) 1) doom-modules-dir) - for path in (directory-files modpath t "^\\w") - 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 From 87ee1a06e3aeefb2d112775aceaa813b6ade37fc Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Sat, 29 Jul 2017 00:25:54 +0200 Subject: [PATCH 3/5] Remove def-feature! (keep things simple!) Removed syntactic sugar macro because it just hides obvious functionality behind magic. --- core/core-packages.el | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/core/core-packages.el b/core/core-packages.el index aad5ddaec..a7efe5f14 100644 --- a/core/core-packages.el +++ b/core/core-packages.el @@ -365,32 +365,6 @@ 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'). From 5849a1fe38ad38d97f843a8389c7beb9474c6262 Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Sat, 29 Jul 2017 00:27:20 +0200 Subject: [PATCH 4/5] Update :feature version-control to support module flags --- modules/feature/version-control/config.el | 9 +++++++-- modules/feature/version-control/packages.el | 13 +++++++------ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/modules/feature/version-control/config.el b/modules/feature/version-control/config.el index 98db7e1b3..7d9339355 100644 --- a/modules/feature/version-control/config.el +++ b/modules/feature/version-control/config.el @@ -1,12 +1,17 @@ ;;; feature/version-control/config.el -*- lexical-binding: t; -*- +(unless (featurep! -git) + (load! +git)) +;; TODO hg support +;; (unless (featurep! -hg) +;; (load! +hg)) + +;; (setq vc-make-backup-files nil) (defvar +vcs-auto-hydra-smerge t "When entering `smerge-mode' automatically open associated hydra.") -(load! +git) -;; (load! +hg) (after! vc-annotate (set! :popup diff --git a/modules/feature/version-control/packages.el b/modules/feature/version-control/packages.el index 43dfb68fe..25c162e88 100644 --- a/modules/feature/version-control/packages.el +++ b/modules/feature/version-control/packages.el @@ -5,11 +5,12 @@ ;; n/a ;;; +git -(package! git-gutter-fringe) -(package! git-link) -(package! git-timemachine) -(package! gitconfig-mode) -(package! gitignore-mode) -(package! magit) +(when (featurep! +git) + (package! git-gutter-fringe) + (package! git-link) + (package! git-timemachine) + (package! gitconfig-mode) + (package! gitignore-mode) + (package! magit)) ;;; TODO +hg From c021d347d31fa0337c05698513672fda9843b498 Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Sat, 29 Jul 2017 00:30:13 +0200 Subject: [PATCH 5/5] Refactor module pairs and paths helpers --- core/core-packages.el | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/core/core-packages.el b/core/core-packages.el index a7efe5f14..8d07bab58 100644 --- a/core/core-packages.el +++ b/core/core-packages.el @@ -272,19 +272,18 @@ Used by `require!' and `depends-on!'." "Returns `doom-modules' as a list of (MODULE . SUBMODULE) cons cells. The list is sorted by order of insertion unless ALL-P is non-nil. If ALL-P is non-nil, include all modules, enabled or otherwise." - (if (hash-table-p doom-modules) - (cl-loop for key being the hash-keys of doom-modules - collect (cons (car key) (cdr key))) - (error "doom-modules is uninitialized"))) + (unless (hash-table-p doom-modules) + (error "doom-modules is uninitialized")) + (cl-loop for key being the hash-keys of doom-modules + collect key)) (defun doom--module-paths (&optional append-file) "Returns a list of absolute file paths to activated modules, with APPEND-FILE added, if the file exists." - (let (paths) - (dolist (pair (doom--module-pairs) (nreverse paths)) - (let ((path (doom-module-path (car pair) (cdr pair) append-file))) - (when (file-exists-p path) - (push path paths)))))) + (cl-loop for (module . submodule) in (doom--module-pairs) + for path = (doom-module-path module submodule append-file) + if (file-exists-p path) + collect path)) @@ -380,7 +379,8 @@ If NOERROR is non-nil, don't throw an error if the file doesn't exist." (and load-file-name (file-name-directory load-file-name)) (and (bound-and-true-p byte-compile-current-file) (file-name-directory byte-compile-current-file)) - (and buffer-file-name (file-name-directory buffer-file-name)))) + (and buffer-file-name + (file-name-directory buffer-file-name)))) (filename (cond ((stringp filesym) filesym) ((symbolp filesym) (symbol-name filesym)) (t (error "load! expected a string or symbol, got %s (a %s)"