diff --git a/Makefile b/Makefile
index 4b33a0ca0..5bc6eb0f5 100644
--- a/Makefile
+++ b/Makefile
@@ -25,8 +25,7 @@ cp: compile-plugins
re: recompile
d: doctor
-quickstart: deprecated
- @$(DOOM) quickstart
+quickstart: install
## Package management
@@ -49,7 +48,7 @@ compile-core: deprecated
compile-private: deprecated
@$(DOOM) compile :private
compile-plugins: deprecated
- @$(DOOM) compile :plugins
+ @$(DOOM) build
recompile: deprecated
@$(DOOM) recompile
clean: deprecated
diff --git a/README.md b/README.md
index dd499db2e..39b1af1ca 100644
--- a/README.md
+++ b/README.md
@@ -34,7 +34,7 @@
**Quick start**
```bash
git clone https://github.com/hlissner/doom-emacs ~/.emacs.d
-~/.emacs.d/bin/doom quickstart
+~/.emacs.d/bin/doom install
```
**Table of Contents**
diff --git a/bin/doom-doctor b/bin/doom-doctor
index 119d4b741..1ed40ae87 100755
--- a/bin/doom-doctor
+++ b/bin/doom-doctor
@@ -27,6 +27,7 @@
(when (getenv "DEBUG")
(setq debug-on-error t))
+(require 'subr-x)
(require 'pp)
(load (expand-file-name "core/autoload/format" user-emacs-directory) nil t)
@@ -147,7 +148,7 @@
emacs-version)
(explain! "Byte-code compiled in one version of Emacs may not work in another version."
"It is recommended that you reinstall your plugins or recompile them with"
- "`bin/doom compile :plugins'.")))
+ "`bin/doom rebuild'.")))
(section! "Checking for Emacs config conflicts...")
(when (file-exists-p "~/.emacs")
@@ -182,109 +183,7 @@
;; on windows?
(when (memq system-type '(windows-nt ms-dos cygwin))
(warn! "Warning: Windows detected")
- (explain! "DOOM was designed for MacOS and Linux. Expect a bumpy ride!"))
-
- ;; gnutls-cli & openssl
- (section! "Checking gnutls/openssl...")
- (cond ((executable-find "gnutls-cli"))
- ((executable-find "openssl")
- (let* ((output (sh "openssl ciphers -v"))
- (protocols
- (let (protos)
- (mapcar (lambda (row)
- (add-to-list 'protos (cadr (split-string row " " t))))
- (split-string (sh "openssl ciphers -v") "\n"))
- (delq nil protos))))
- (unless (or (member "TLSv1.1" protocols)
- (member "TLSv1.2" protocols))
- (let ((version (cadr (split-string (sh "openssl version") " " t))))
- (warn! "Warning: couldn't find gnutls-cli, and OpenSSL is out-of-date (v%s)" version)
- (explain!
- "This may not affect your Emacs experience, but there are security "
- "vulnerabilities in the SSL2/3 & TLS1.0 protocols. You should use "
- "TLS 1.1+, which wasn't introduced until OpenSSL v1.0.1.\n\n"
-
- "Please consider updating (or install gnutls-cli, which is preferred).")))))
- (t
- (error! "Important: couldn't find either gnutls-cli nor openssl")
- (explain!
- "You may not be able to install/update packages because Emacs won't be able to "
- "verify HTTPS ELPA sources. Install gnutls-cli or openssl v1.0.0+. If for some "
- "reason you can't, you can bypass this verification with the INSECURE flag:\n\n"
-
- " INSECURE=1 make install\n\n"
-
- "Or change `package-archives' to use non-https sources.\n\n"
-
- "But remember that you're leaving your security in the hands of your "
- "network, provider, government, neckbearded mother-in-laws, geeky roommates, "
- "or just about anyone who knows more about computers than you do!")))
-
- ;; are certificates validated properly?
- (section! "Testing your root certificates...")
- (cond ((not (ignore-errors (gnutls-available-p)))
- (warn! "Warning: Emacs wasn't installed with gnutls support")
- (explain!
- "This may cause 'pecular error' errors with the Doom doctor, and is likely to "
- "interfere with package management. Your mileage may vary."
- (when (eq system-type 'darwin)
- (concat "\nMacOS users are advised to install Emacs via homebrew with one of the following:\n"
- " brew install emacs --with-gnutls"
- " or"
- " brew tap d12frosted/emacs-plus"
- " brew install emacs-plus"))))
-
- ((not (fboundp 'url-retrieve-synchronously))
- (error! "Can't find url-retrieve-synchronously function. Are you sure you're on Emacs 24+?"))
-
- ((or (executable-find "gnutls-cli")
- (executable-find "openssl"))
- (let ((tls-checktrust t)
- (gnutls-verify-error t))
- (dolist (url '("https://elpa.gnu.org" "https://melpa.org"))
- (pcase (condition-case-unless-debug e
- (unless (let ((inhibit-message t)) (url-retrieve-synchronously url))
- 'empty)
- ('timed-out 'timeout)
- ('error e))
- (`nil nil)
- (`empty (error! "Couldn't reach %s" url))
- (`timeout (error! "Timed out trying to contact %s" ex))
- (it
- (error! "Failed to validate %s" url)
- (explain! (pp-to-string it)))))
- (dolist (url '("https://self-signed.badssl.com"
- "https://wrong.host.badssl.com/"))
- (pcase (condition-case-unless-debug e
- (if (let ((inhibit-message t)) (url-retrieve-synchronously url))
- t
- 'empty)
- ('timed-out 'timeout)
- ('error))
- (`nil nil)
- (`empty (error! "Couldn't reach %s" url))
- (`timeout (error! "Timed out trying to contact %s" ex))
- (_
- (error! "Validated %s (this shouldn't happen!)" url)))))))
-
- ;; which variant of tar is on your system? bsd or gnu tar?
- (section! "Checking for GNU/BSD tar...")
- (let ((tar-bin (or (executable-find "gtar")
- (executable-find "tar"))))
- (if tar-bin
- (unless (string-match-p "(GNU tar)" (sh "%s --version" tar-bin))
- (warn! "Warning: BSD tar detected")
- (explain!
- "QUELPA (through package-build) uses the system tar to build plugins, but it "
- "expects GNU tar. BSD tar *could* cause errors during package installation or "
- "updating from non-ELPA sources."
- (when (eq system-type 'darwin)
- (concat "\nMacOS users can install gnu-tar via homebrew:\n"
- " brew install gnu-tar"))))
- (error! "Important: Couldn't find tar")
- (explain!
- "This is required by package.el and QUELPA to build packages and will "
- "prevent you from installing & updating packages."))))
+ (explain! "DOOM was designed for MacOS and Linux. Expect a bumpy ride!")))
;;
@@ -292,12 +191,11 @@
(condition-case-unless-debug ex
(let ((after-init-time (current-time))
- (doom-message-backend 'ansi)
- noninteractive)
+ (doom-format-backend 'ansi))
(section! "Checking DOOM Emacs...")
(load (concat user-emacs-directory "core/core.el") nil t)
(unless (file-directory-p doom-private-dir)
- (error "No DOOMDIR was found, did you run `doom quickstart` yet?"))
+ (error "No DOOMDIR was found, did you run `doom install` yet?"))
(let ((indent 2))
;; Make sure everything is loaded
@@ -317,6 +215,7 @@
(success! "Initialized %d modules" (hash-table-count doom-modules))
(warn! "Failed to load any modules. Do you have an private init.el?"))
+ (doom-ensure-straight)
(doom-initialize-packages)
(success! "Initialized %d packages" (length doom-packages))
@@ -343,11 +242,10 @@
doom-disabled-packages)
(load packages-file 'noerror 'nomessage)
(mapcar #'car doom-packages))
- for name = (doom-package-true-name name)
- unless (or (doom-package-prop name :disable)
- (eval (doom-package-prop name :ignore))
- (package-built-in-p name)
- (package-installed-p name))
+ unless (or (doom-package-get name :disable)
+ (eval (doom-package-get name :ignore))
+ (doom-package-built-in-p name)
+ (doom-package-installed-p name))
do (error! "%s is not installed" name))
(load doctor-file 'noerror 'nomessage))
(file-missing (error! "%s" (error-message-string ex)))
diff --git a/core/autoload/cli.el b/core/autoload/cli.el
index 693a31250..9eb3f8406 100644
--- a/core/autoload/cli.el
+++ b/core/autoload/cli.el
@@ -1,15 +1,18 @@
;;; core/autoload/cli.el -*- lexical-binding: t; -*-
+;; Externs
+(defvar evil-collection-mode-list)
+
(require 'core-cli)
;;;###autoload
-(defun doom-cli-run (command &rest _args)
+(defun doom--cli-run (command &rest _args)
(when (featurep 'general)
(general-auto-unbind-keys))
(let* ((evil-collection-mode-list nil)
(default-directory doom-emacs-dir)
(buf (get-buffer-create " *bin/doom*"))
- (doom-message-backend 'ansi)
+ (doom-format-backend 'ansi)
(ignore-window-parameters t)
(noninteractive t)
(standard-output
@@ -39,21 +42,21 @@
"TODO"
(interactive "P")
(let ((doom-auto-accept yes))
- (doom-cli-run "autoloads")))
+ (doom--cli-run "autoloads")))
;;;###autoload
(defun doom//update (&optional yes)
"TODO"
(interactive "P")
(let ((doom-auto-accept yes))
- (doom-cli-run "update")))
+ (doom--cli-run "update")))
;;;###autoload
(defun doom//upgrade (&optional yes)
"TODO"
(interactive "P")
(let ((doom-auto-accept yes))
- (doom-cli-run "upgrade"))
+ (doom--cli-run "upgrade"))
(when (y-or-n-p "You must restart Emacs for the upgrade to take effect. Restart?")
(doom/restart-and-restore)))
@@ -62,18 +65,18 @@
"TODO"
(interactive "P")
(let ((doom-auto-accept yes))
- (doom-cli-run "install")))
+ (doom--cli-run "install")))
;;;###autoload
(defun doom//autoremove (&optional yes)
"TODO"
(interactive "P")
(let ((doom-auto-accept yes))
- (doom-cli-run "autoremove")))
+ (doom--cli-run "autoremove")))
;;;###autoload
(defun doom//refresh (&optional yes)
"TODO"
(interactive "P")
(let ((doom-auto-accept yes))
- (doom-cli-run "refresh")))
+ (doom--cli-run "refresh")))
diff --git a/core/autoload/debug.el b/core/autoload/debug.el
index 953af45cf..10dd3662d 100644
--- a/core/autoload/debug.el
+++ b/core/autoload/debug.el
@@ -16,65 +16,54 @@ ready to be pasted in a bug report on github."
(require 'vc-git)
(let ((default-directory doom-emacs-dir)
(doom-modules (doom-modules)))
- (format
- (concat "- OS: %s (%s)\n"
- "- Shell: %s\n"
- "- Emacs: %s (%s)\n"
- "- Doom: %s (%s)\n"
- "- Graphic display: %s (daemon: %s)\n"
- "- System features: %s\n"
- "- Details:\n"
- " ```elisp\n"
- " env bootstrapper: %s\n"
- " elc count: %s\n"
- " uname -a: %s\n"
- " modules: %s\n"
- " packages: %s\n"
- " exec-path: %s\n"
- " ```")
- system-type system-configuration
- shell-file-name
- emacs-version (format-time-string "%b %d, %Y" emacs-build-time)
- doom-version
- (or (string-trim (shell-command-to-string "git log -1 --format=\"%D %h %ci\""))
- "n/a")
- (display-graphic-p) (daemonp)
- (bound-and-true-p system-configuration-features)
- (cond ((file-exists-p doom-env-file) 'envvar-file)
- ((featurep 'exec-path-from-shell) 'exec-path-from-shell))
- ;; details
- (length (doom-files-in `(,@doom-modules-dirs
- ,doom-core-dir
- ,doom-private-dir)
- :type 'files :match "\\.elc$" :sort nil))
- (if IS-WINDOWS
- "n/a"
- (with-temp-buffer
- (unless (zerop (call-process "uname" nil t nil "-msrv"))
- (insert (format "%s" system-type)))
- (string-trim (buffer-string))))
- (or (cl-loop with cat = nil
- for key being the hash-keys of doom-modules
- if (or (not cat) (not (eq cat (car key))))
- do (setq cat (car key))
- and collect cat
- and collect (cdr key)
- else collect
- (let ((flags (doom-module-get cat (cdr key) :flags)))
- (if flags
- `(,(cdr key) ,@flags)
- (cdr key))))
- "n/a")
- (or (ignore-errors
- (require 'use-package)
- (cl-loop for (name . plist) in (doom-find-packages :private t)
- if (use-package-plist-delete (copy-sequence plist) :modules)
- collect (format "%s" (cons name it))
- else
- collect (symbol-name name)))
- "n/a")
- ;; abbreviate $HOME to hide username
- (mapcar #'abbreviate-file-name exec-path))))
+ (cl-letf
+ (((symbol-function 'sh)
+ (lambda (format)
+ (string-trim
+ (shell-command-to-string format)))))
+ `((emacs
+ (version . ,emacs-version)
+ (features ,@system-configuration-features)
+ (build . ,(format-time-string "%b %d, %Y" emacs-build-time))
+ (buildopts ,system-configuration-options))
+ (doom
+ (version . ,doom-version)
+ (build . ,(sh "git log -1 --format=\"%D %h %ci\"")))
+ (system
+ (type . ,system-type)
+ (config . ,system-configuration)
+ (shell . ,shell-file-name)
+ (uname . ,(if IS-WINDOWS
+ "n/a"
+ (sh "uname -msrv")))
+ (path . ,(mapcar #'abbreviate-file-name exec-path)))
+ (config
+ (envfile . ,(cond ((file-exists-p doom-env-file) 'envvar-file)
+ ((featurep 'exec-path-from-shell) 'exec-path-from-shell)))
+ (elc-files . ,(length (doom-files-in `(,@doom-modules-dirs
+ ,doom-core-dir
+ ,doom-private-dir)
+ :type 'files :match "\\.elc$" :sort nil)))
+ (modules ,@(or (cl-loop with cat = nil
+ for key being the hash-keys of doom-modules
+ if (or (not cat) (not (eq cat (car key))))
+ do (setq cat (car key))
+ and collect cat
+ and collect (cdr key)
+ else collect
+ (let ((flags (doom-module-get cat (cdr key) :flags)))
+ (if flags
+ `(,(cdr key) ,@flags)
+ (cdr key))))
+ '("n/a")))
+ (packages ,@(or (ignore-errors
+ (require 'use-package)
+ (cl-loop for (name . plist) in (doom-find-packages :private t)
+ if (use-package-plist-delete (copy-sequence plist) :modules)
+ collect (format "%s" (cons name it))
+ else
+ collect (symbol-name name)))
+ '("n/a"))))))))
;;
@@ -86,24 +75,55 @@ ready to be pasted in a bug report on github."
branch and commit."
(interactive)
(require 'vc-git)
- (print! "Doom v%s (Emacs v%s)\nBranch: %s\nCommit: %s"
+ (print! "Doom v%s (Emacs v%s)\nBranch: %s\nCommit: %s\nBuild date: %s"
doom-version
emacs-version
(or (vc-git--symbolic-ref doom-core-dir)
"n/a")
(or (vc-git-working-revision doom-core-dir)
+ "n/a")
+ (or (string-trim (shell-command-to-string "git log -1 --format=%ci"))
"n/a")))
;;;###autoload
-(defun doom/info ()
+(defun doom/info (&optional raw)
"Collects some debug information about your Emacs session, formats it into
markdown and copies it to your clipboard, ready to be pasted into bug reports!"
- (interactive)
- (message "Generating Doom info...")
- (if noninteractive
- (print! (doom-info))
- (kill-new (doom-info))
- (message "Done! Copied to clipboard.")))
+ (interactive "P")
+ (let ((buffer (get-buffer-create "*doom-info*"))
+ (info (doom-info)))
+ (with-current-buffer buffer
+ (unless (or noninteractive
+ (eq major-mode 'markdown-mode)
+ (not (fboundp 'markdown-mode)))
+ (markdown-mode))
+ (erase-buffer)
+ (if raw
+ (progn
+ (save-excursion
+ (pp info (current-buffer)))
+ (when (re-search-forward "(modules " nil t)
+ (goto-char (match-beginning 0))
+ (cl-destructuring-bind (beg . end)
+ (bounds-of-thing-at-point 'sexp)
+ (let ((sexp (prin1-to-string (sexp-at-point))))
+ (delete-region beg end)
+ (insert sexp)))))
+ (insert "\n\n```\n")
+ (dolist (group info)
+ (insert! "%-8s%-10s %s\n"
+ ((car group)
+ (caadr group)
+ (cdadr group)))
+ (dolist (spec (cddr group))
+ (insert! (indent 8 "%-10s %s\n")
+ ((car spec) (cdr spec)))))
+ (insert "```\n "))
+ (if noninteractive
+ (print! (buffer-string))
+ (switch-to-buffer buffer)
+ (kill-new (buffer-string))
+ (print! (green "Copied markdown to clipboard"))))))
;;;###autoload
(defun doom/am-i-secure ()
@@ -144,11 +164,11 @@ markdown and copies it to your clipboard, ready to be pasted into bug reports!"
(macroexp-progn
(append `((setq noninteractive nil
doom-debug-mode t
+ load-path ',load-path
package--init-file-ensured t
package-user-dir ,package-user-dir
package-archives ',package-archives
- user-emacs-directory ,doom-emacs-dir
- doom--modules-cache nil)
+ user-emacs-directory ,doom-emacs-dir)
(with-eval-after-load 'undo-tree
;; undo-tree throws errors because `buffer-undo-tree' isn't
;; corrrectly initialized
diff --git a/core/autoload/help.el b/core/autoload/help.el
index 9a204ebb3..d7bac09d6 100644
--- a/core/autoload/help.el
+++ b/core/autoload/help.el
@@ -365,10 +365,14 @@ current file is in, or d) the module associated with the current major mode (see
(recenter)
(message "Couldn't find the config block"))))))))
+(defvar doom--help-packages-list nil)
(defun doom--help-packages-list (&optional refresh)
(or (unless refresh
- (doom-cache-get 'help-packages))
- (doom-cache-set 'help-packages (doom-package-list 'all))))
+ doom--help-packages-list)
+ (setq doom--help-packages-list
+ (append (cl-loop for package in doom-core-packages
+ collect (list package :modules '((:core internal))))
+ (doom-package-list 'all)))))
(defun doom--help-package-configs (package)
;; TODO Add git checks, in case ~/.emacs.d isn't a git repo
diff --git a/core/autoload/hydras.el b/core/autoload/hydras.el
index 68f6b4360..6ab2dd7a4 100644
--- a/core/autoload/hydras.el
+++ b/core/autoload/hydras.el
@@ -1,4 +1,4 @@
-;;; core/autoload/hydras.el -*- lexical-binding: t; -*-
+;;; core/autoload/hydras.el -*- lexical-binding: t; no-byte-compile: t; -*-
;;;###autoload (autoload 'doom-text-zoom-hydra/body "core/autoload/hydras" nil t)
(defhydra doom-text-zoom-hydra (:hint t :color red)
diff --git a/core/autoload/packages.el b/core/autoload/packages.el
index 4da43c8d8..c26673ac2 100644
--- a/core/autoload/packages.el
+++ b/core/autoload/packages.el
@@ -1,128 +1,67 @@
;;; core/autoload/packages.el -*- lexical-binding: t; -*-
(require 'core-packages)
-(load! "cache") ; in case autoloads haven't been generated yet
-
-
-(defun doom--packages-choose (prompt)
- (let ((table (cl-loop for pkg in package-alist
- unless (doom-package-built-in-p (cdr pkg))
- collect (cons (package-desc-full-name (cdr pkg))
- (cdr pkg)))))
- (cdr (assoc (completing-read prompt
- (mapcar #'car table)
- nil t)
- table))))
-
-(defun doom--refresh-pkg-cache ()
- "Clear the cache for `doom-refresh-packages-maybe'."
- (setq doom--refreshed-p nil)
- (doom-cache-set 'last-pkg-refresh nil))
-
-;;;###autoload
-(defun doom-refresh-packages-maybe (&optional force-p)
- "Refresh ELPA packages, if it hasn't been refreshed recently."
- (when force-p
- (doom--refresh-pkg-cache))
- (unless (or (doom-cache-get 'last-pkg-refresh)
- doom--refreshed-p)
- (condition-case e
- (progn
- (message "Refreshing package archives")
- (package-refresh-contents)
- (doom-cache-set 'last-pkg-refresh t 1200))
- ((debug error)
- (doom--refresh-pkg-cache)
- (signal 'doom-error e)))))
;;
;;; Package metadata
;;;###autoload
-(defun doom-package-plist (package)
+(defun doom-package-get (package &optional prop nil-value)
"Returns PACKAGE's `package!' recipe from `doom-packages'."
- (cdr (assq package doom-packages)))
+ (let ((plist (cdr (assq package doom-packages))))
+ (if prop
+ (if (plist-member plist prop)
+ (plist-get plist prop)
+ nil-value)
+ plist)))
;;;###autoload
-(defun doom-package-desc (package)
- "Returns PACKAGE's desc struct from `package-alist'."
- (cadr (assq (or (car (doom-package-prop package :recipe))
- package)
- package-alist)))
+(defun doom-package-recipe (package &optional prop nil-value)
+ "Returns the `straight' recipe PACKAGE was registered with."
+ (let ((plist (gethash (symbol-name package) straight--recipe-cache)))
+ (if prop
+ (if (plist-member plist prop)
+ (plist-get plist prop)
+ nil-value)
+ plist)))
;;;###autoload
-(defun doom-package-true-name (package)
- "Return PACKAGE's true name.
-
-It is possible for quelpa packages to be given a psuedonym (the first argument
-of `package!'). Its real name is the car of package's :recipe. e.g.
-
- (package! X :recipe (Y :fetcher github :repo \"abc/def\"))
-
-X's real name is Y."
- (let ((sym (car (doom-package-prop package :recipe))))
- (or (and (symbolp sym)
- (not (keywordp sym))
- sym)
- package)))
+(defun doom-package-build-recipe (package &optional prop nil-value)
+ "Returns the `straight' recipe PACKAGE was installed with."
+ (let ((plist (nth 2 (gethash (symbol-name package) straight--build-cache))))
+ (if prop
+ (if (plist-member plist prop)
+ (plist-get plist prop)
+ nil-value)
+ plist)))
;;;###autoload
-(defun doom-package-psuedo-name (package)
+(defun doom-package-build-time (package)
"TODO"
- (or (cl-loop for (package . plist) in doom-packages
- for recipe-name = (car (plist-get plist :recipe))
- if (eq recipe-name package)
- return recipe-name)
- package))
+ (car (gethash (symbol-name package) straight--build-cache)))
;;;###autoload
-(defun doom-package-backend (package &optional noerror)
- "Return backend that PACKAGE was installed with.
+(defun doom-package-dependencies (package &optional recursive noerror)
+ "Return a list of dependencies for a package."
+ (let ((deps (nth 1 (gethash (symbol-name package) straight--build-cache))))
+ (if recursive
+ (nconc deps (mapcan (lambda (dep) (doom-package-dependencies dep t t))
+ deps))
+ deps)))
-Can either be elpa, quelpa or emacs (built-in). Throws an error if NOERROR is
-nil and the package isn't installed.
-
-See `doom-package-recipe-backend' to get the backend PACKAGE is registered with
-\(as opposed to what it is was installed with)."
- (cl-check-type package symbol)
- (let ((package-truename (doom-package-true-name package)))
- (cond ((assq package-truename quelpa-cache) 'quelpa)
- ((assq package-truename package-alist) 'elpa)
- ((doom-package-built-in-p package) 'emacs)
- ((not noerror) (error "%s package is not installed" package)))))
-
-;;;###autoload
-(defun doom-package-recipe-backend (package &optional noerror)
- "Return backend that PACKAGE is registered with.
-
-See `doom-package-backend' to get backend for currently installed package."
- (cl-check-type package symbol)
- (cond ((not (doom-package-registered-p package))
- (unless noerror
- (error "%s package is not registered" package)))
- ((let ((builtin (eval (doom-package-prop package :built-in) t)))
- (or (and (eq builtin 'prefer)
- (locate-library (symbol-name package) nil doom-site-load-path))
- (eq builtin 't)))
- 'emacs)
- ((doom-package-prop package :recipe)
- 'quelpa)
- ('elpa)))
-
-;;;###autoload
-(defun doom-package-prop (package prop &optional nil-value)
- "Return PROPerty in PACKAGE's plist.
-
-Otherwise returns NIL-VALUE if package isn't registered or PROP doesn't
-exist/isn't specified."
- (cl-check-type package symbol)
- (cl-check-type prop keyword)
- (if-let (plist (doom-package-plist package))
- (if (plist-member plist prop)
- (plist-get plist prop)
- nil-value)
- nil-value))
+(defun doom-package-depending-on (package &optional noerror)
+ "Return a list of packages that depend on the package named NAME."
+ (cl-check-type name symbol)
+ ;; can't get dependencies for built-in packages
+ (unless (or (doom-package-build-recipe name)
+ noerror)
+ (error "Couldn't find %s, is it installed?" name))
+ (cl-loop for pkg in (hash-table-keys straight--build-cache)
+ for deps = (doom-package-dependencies pkg)
+ if (memq package deps)
+ collect pkg
+ and append (doom-package-depending-on pkg t)))
;;
@@ -131,99 +70,52 @@ exist/isn't specified."
;;;###autoload
(defun doom-package-built-in-p (package)
"Return non-nil if PACKAGE (a symbol) is built-in."
- (unless (doom-package-installed-p package)
- (or (package-built-in-p (doom-package-true-name package))
- (locate-library (symbol-name package) nil doom-site-load-path))))
+ (eq (doom-package-build-recipe package :type)
+ 'built-in))
;;;###autoload
(defun doom-package-installed-p (package)
"Return non-nil if PACKAGE (a symbol) is installed."
- (when-let (desc (doom-package-desc package))
- (and (package-installed-p desc)
- (file-directory-p (package-desc-dir desc)))))
+ (file-directory-p (straight--build-dir (symbol-name package))))
;;;###autoload
(defun doom-package-registered-p (package)
"Return non-nil if PACKAGE (a symbol) has been registered with `package!'.
Excludes packages that have a non-nil :built-in property."
- (let ((package (or (cl-loop for (pkg . plist) in doom-packages
- for newname = (car (plist-get plist :recipe))
- if (and (symbolp newname)
- (eq newname package))
- return pkg)
- package)))
- (when-let (plist (doom-package-plist package))
- (not (eval (plist-get plist :ignore))))))
+ (when-let (plist (doom-package-get package))
+ (not (eval (plist-get plist :ignore) t))))
;;;###autoload
(defun doom-package-private-p (package)
"Return non-nil if PACKAGE was installed by the user's private config."
- (doom-package-prop package :private))
+ (doom-package-get package :private))
;;;###autoload
(defun doom-package-protected-p (package)
"Return non-nil if PACKAGE is protected.
A protected package cannot be deleted and will be auto-installed if missing."
- (memq (doom-package-true-name package) doom-core-packages))
+ (memq package doom-core-packages))
;;;###autoload
(defun doom-package-core-p (package)
"Return non-nil if PACKAGE is a core Doom package."
(or (doom-package-protected-p package)
- (assq :core (doom-package-prop package :modules))))
-
-;;;###autoload
-(defun doom-package-different-backend-p (package)
- "Return t if a PACKAGE (a symbol) has a new backend than what it was installed
-with. Returns nil otherwise, or if package isn't installed."
- (cl-check-type package symbol)
- (and (doom-package-installed-p package)
- (not (doom-get-depending-on package)) ; not a dependency
- (not (eq (doom-package-backend package 'noerror)
- (doom-package-recipe-backend package 'noerror)))))
+ (assq :core (doom-package-get package :modules))))
;;;###autoload
(defun doom-package-different-recipe-p (name)
"Return t if a package named NAME (a symbol) has a different recipe than it
was installed with."
(cl-check-type name symbol)
- (when (doom-package-installed-p name)
- (let ((package-truename (doom-package-true-name name)))
- (when-let* ((quelpa-recipe (assq package-truename quelpa-cache))
- (doom-recipe (assq package-truename doom-packages)))
- (not (equal (cdr quelpa-recipe)
- (cdr (plist-get (cdr doom-recipe) :recipe))))))))
-
-(defvar quelpa-upgrade-p)
-;;;###autoload
-(defun doom-package-outdated-p (name)
- "Determine whether NAME (a symbol) is outdated or not.
-
-If outdated, returns a list, whose car is NAME, and cdr the current version list
-and latest version list of the package."
- (cl-check-type name symbol)
- (when-let (desc (doom-package-desc name))
- (let* ((old-version (package-desc-version desc))
- (new-version
- (pcase (doom-package-backend name)
- (`quelpa
- (let ((recipe (doom-package-prop name :recipe))
- (dir (expand-file-name (symbol-name name) quelpa-build-dir))
- (inhibit-message (not doom-debug-mode))
- (quelpa-upgrade-p t))
- (if-let (ver (quelpa-checkout recipe dir))
- (version-to-list ver)
- old-version)))
- (`elpa
- (let ((desc (cadr (assq name package-archive-contents))))
- (when (package-desc-p desc)
- (package-desc-version desc)))))))
- (unless (and (listp old-version) (listp new-version))
- (error "Couldn't get version for %s" name))
- (when (version-list-< old-version new-version)
- (list name old-version new-version)))))
+ ;; TODO
+ ;; (when (doom-package-installed-p name)
+ ;; (when-let* ((doom-recipe (assq name doom-packages))
+ ;; (install-recipe (doom-package-recipe)))
+ ;; (not (equal (cdr quelpa-recipe)
+ ;; (cdr (plist-get (cdr doom-recipe) :recipe))))))
+ )
;;
@@ -307,32 +199,37 @@ files."
collect (cons sym plist)
and if (and deps (not (doom-package-built-in-p sym)))
nconc
- (cl-loop for pkg in (doom-get-dependencies-for sym 'recursive 'noerror)
+ (cl-loop for pkg in (doom-package-dependencies sym 'recursive 'noerror)
if (or (eq installed 'any)
(if installed
(doom-package-installed-p pkg)
(not (doom-package-installed-p pkg))))
collect (cons pkg (cdr (assq pkg doom-packages)))))))
-(defun doom--read-module-packages-file (file &optional raw noerror)
+(defun doom--read-module-packages-file (file &optional eval noerror)
(with-temp-buffer ; prevent buffer-local settings from propagating
(condition-case e
- (if (not raw)
+ (if (not eval)
(load file noerror t t)
(when (file-readable-p file)
(insert-file-contents file)
+ (delay-mode-hooks (emacs-lisp-mode))
(while (re-search-forward "(package! " nil t)
(save-excursion
(goto-char (match-beginning 0))
- (cl-destructuring-bind (name . plist) (cdr (sexp-at-point))
- (push (cons name
- (plist-put plist :modules
- (cond ((file-in-directory-p file doom-private-dir)
- '((:private)))
- ((file-in-directory-p file doom-core-dir)
- '((:core)))
- ((doom-module-from-path file)))))
- doom-packages))))))
+ (unless (string-match-p
+ "^.*;" (buffer-substring-no-properties
+ (line-beginning-position)
+ (point)))
+ (cl-destructuring-bind (name . plist) (cdr (sexp-at-point))
+ (push (cons name
+ (plist-put plist :modules
+ (cond ((file-in-directory-p file doom-private-dir)
+ '((:private)))
+ ((file-in-directory-p file doom-core-dir)
+ '((:core)))
+ ((doom-module-from-path file)))))
+ doom-packages)))))))
((debug error)
(signal 'doom-package-error
(list (or (doom-module-from-path file)
@@ -350,15 +247,16 @@ ones."
(let ((noninteractive t)
(doom-modules (doom-modules))
doom-packages
- doom-disabled-packages
- package-pinned-packages)
- (doom--read-module-packages-file (expand-file-name "packages.el" doom-core-dir) all-p)
+ doom-disabled-packages)
+ (doom--read-module-packages-file
+ (expand-file-name "packages.el" doom-core-dir)
+ all-p t)
(let ((private-packages (expand-file-name "packages.el" doom-private-dir)))
(unless all-p
;; We load the private packages file twice to ensure disabled packages
;; are seen ASAP, and a second time to ensure privately overridden
;; packages are properly overwritten.
- (doom--read-module-packages-file private-packages nil t))
+ (doom--read-module-packages-file private-packages t t))
(if all-p
(mapc #'doom--read-module-packages-file
(doom-files-in doom-modules-dir
@@ -371,192 +269,11 @@ ones."
for doom--current-module = key
do (doom--read-module-packages-file path nil t)))
(doom--read-module-packages-file private-packages all-p t))
- (append (cl-loop for package in doom-core-packages
- collect (list package :modules '((:core internal))))
- (nreverse doom-packages))))
-
-;;;###autoload
-(defun doom-get-package-alist ()
- "Returns a list of all desired packages, their dependencies and their desc
-objects, in the order of their `package! blocks.'"
- (cl-remove-duplicates
- (cl-loop for name in (mapcar #'car doom-packages)
- if (assq name package-alist)
- nconc (cl-loop for dep in (package--get-deps name)
- if (assq dep package-alist)
- collect (cons dep (cadr it)))
- and collect (cons name (cadr it)))
- :key #'car
- :from-end t))
-
-;;;###autoload
-(defun doom-get-depending-on (name &optional noerror)
- "Return a list of packages that depend on the package named NAME."
- (cl-check-type name symbol)
- (setq name (or (car (doom-package-prop name :recipe)) name))
- (unless (doom-package-built-in-p name)
- (if-let (desc (cadr (assq name package-alist)))
- (mapcar #'package-desc-name (package--used-elsewhere-p desc nil t))
- (unless noerror
- (error "Couldn't find %s, is it installed?" name)))))
-
-;;;###autoload
-(defun doom-get-dependencies-for (name &optional recursive noerror)
- "Return a list of dependencies for a package."
- (cl-check-type name symbol)
- ;; can't get dependencies for built-in packages
- (unless (doom-package-built-in-p name)
- (if-let (desc (doom-package-desc name))
- (let* ((deps (mapcar #'car (package-desc-reqs desc)))
- (deps (cl-remove-if #'doom-package-built-in-p deps)))
- (if recursive
- (nconc deps (mapcan (lambda (dep) (doom-get-dependencies-for dep t t))
- deps))
- deps))
- (unless noerror
- (error "Couldn't find %s, is it installed?" name)))))
-
-;;;###autoload
-(defun doom-get-outdated-packages (&optional include-frozen-p)
- "Return a list of packages that are out of date. Each element is a list,
-containing (PACKAGE-SYMBOL OLD-VERSION-LIST NEW-VERSION-LIST).
-
-If INCLUDE-FROZEN-P is non-nil, check frozen packages as well.
-
-Used by `doom-packages-update'."
- (doom-refresh-packages-maybe doom-debug-mode)
- (cl-loop for package in (mapcar #'car package-alist)
- when (and (or (not (eval (doom-package-prop package :freeze)))
- include-frozen-p)
- (not (eval (doom-package-prop package :ignore)))
- (not (doom-package-different-backend-p package))
- (doom-package-outdated-p package))
- collect it))
-
-;;;###autoload
-(defun doom-get-orphaned-packages ()
- "Return a list of symbols representing packages that are no longer needed or
-depended on.
-
-Used by `doom-packages-autoremove'."
- (let ((package-selected-packages
- (mapcar #'car (doom-find-packages :ignored nil :disabled nil))))
- (append (cl-remove-if #'doom-package-registered-p (package--removable-packages))
- (cl-loop for pkg in package-selected-packages
- if (and (doom-package-different-backend-p pkg)
- (not (doom-package-built-in-p pkg)))
- collect pkg))))
-
-;;;###autoload
-(defun doom-get-missing-packages ()
- "Return a list of requested packages that aren't installed or built-in, but
-are enabled (with a `package!' directive). Each element is a list whose CAR is
-the package symbol, and whose CDR is a plist taken from that package's
-`package!' declaration.
-
-Used by `doom-packages-install'."
- (cl-loop for (name . plist)
- in (doom-find-packages :ignored nil
- :disabled nil
- :deps t)
- if (and (equal (plist-get plist :pin)
- (ignore-errors
- (package-desc-archive
- (cadr (assq name package-alist)))))
- (or (not (doom-package-installed-p name))
- (doom-package-different-backend-p name)
- (doom-package-different-recipe-p name)))
- collect (cons name plist)))
+ (nreverse doom-packages)))
;;
-;; Main functions
-
-(defun doom--delete-package-files (name-or-desc)
- (let ((pkg-build-dir
- (if (package-desc-p name-or-desc)
- (package-desc-dir name-or-desc)
- (expand-file-name (symbol-name name-or-desc) quelpa-build-dir))))
- (when (file-directory-p pkg-build-dir)
- (delete-directory pkg-build-dir t))))
-
-;;;###autoload
-(defun doom-install-package (name &optional plist)
- "Installs package NAME with optional quelpa RECIPE (see `quelpa-recipe' for an
-example; the package name can be omitted)."
- (cl-check-type name symbol)
- (when (and (doom-package-installed-p name)
- (not (doom-package-built-in-p name)))
- (if (or (doom-package-different-backend-p name)
- (doom-package-different-recipe-p name))
- (doom-delete-package name t)
- (user-error "%s is already installed" name)))
- (let* ((inhibit-message (not doom-debug-mode))
- (plist (or plist (doom-package-plist name))))
- (if-let (recipe (plist-get plist :recipe))
- (condition-case e
- (let (quelpa-upgrade-p)
- (quelpa recipe))
- ((debug error)
- (doom--delete-package-files name)
- (signal (car e) (cdr e))))
- (package-install name))
- (if (not (doom-package-installed-p name))
- (doom--delete-package-files name)
- (add-to-list 'package-selected-packages name nil 'eq)
- (setf (alist-get name doom-packages) plist)
- name)))
-
-;;;###autoload
-(defun doom-update-package (name &optional force-p)
- "Updates package NAME (a symbol) if it is out of date, using quelpa or
-package.el as appropriate."
- (cl-check-type name symbol)
- (unless (doom-package-installed-p name)
- (error "%s isn't installed" name))
- (when (doom-package-different-backend-p name)
- (user-error "%s's backend has changed and must be uninstalled first" name))
- (when (or force-p (doom-package-outdated-p name))
- (let ((inhibit-message (not doom-debug-mode))
- (desc (doom-package-desc name)))
- (pcase (doom-package-backend name)
- (`quelpa
- (let ((name (doom-package-true-name name)))
- (condition-case e
- (let ((quelpa-upgrade-p t))
- (quelpa (assq name quelpa-cache)))
- ((debug error)
- (doom--delete-package-files name)
- (signal (car e) (cdr e))))))
- (`elpa
- (let* ((archive (cadr (assq name package-archive-contents)))
- (packages
- (if (package-desc-p archive)
- (package-compute-transaction (list archive) (package-desc-reqs archive))
- (package-compute-transaction () (list (list archive))))))
- (package-download-transaction packages))))
- (unless (doom-package-outdated-p name)
- (doom--delete-package-files desc)
- t))))
-
-;;;###autoload
-(defun doom-delete-package (name &optional force-p)
- "Uninstalls package NAME if it exists, and clears it from `quelpa-cache'."
- (cl-check-type name symbol)
- (unless (doom-package-installed-p name)
- (user-error "%s isn't installed" name))
- (let ((inhibit-message (not doom-debug-mode))
- (name (doom-package-true-name name)))
- (when-let (spec (assq name quelpa-cache))
- (delq! spec quelpa-cache)
- (quelpa-save-cache))
- (package-delete (doom-package-desc name) force-p)
- (doom--delete-package-files name)
- (not (doom-package-installed-p name))))
-
-
-;;
-;; Interactive commands
+;;; Main functions
;;;###autoload
(defun doom/reload-packages ()
@@ -565,61 +282,3 @@ package.el as appropriate."
(message "Reloading packages")
(doom-initialize-packages t)
(message "Reloading packages...DONE"))
-
-;;;###autoload
-(defun doom/update-package (pkg)
- "Prompts the user with a list of outdated packages and updates the selected
-package. Use this interactively. Use `doom-update-package' for direct
-calls."
- (declare (interactive-only t))
- (interactive
- (let* ((packages (doom-get-outdated-packages))
- (selection (if packages
- (completing-read "Update package: "
- (mapcar #'car packages)
- nil t)
- (user-error "All packages are up to date")))
- (name (car (assoc (intern selection) package-alist))))
- (unless name
- (user-error "'%s' is already up-to-date" selection))
- (list (assq name packages))))
- (cl-destructuring-bind (package old-version new-version) pkg
- (if-let (desc (doom-package-outdated-p package))
- (let ((old-v-str (package-version-join old-version))
- (new-v-str (package-version-join new-version)))
- (if (y-or-n-p (format "%s will be updated from %s to %s. Update?"
- package old-v-str new-v-str))
- (message "%s %s (%s => %s)"
- (if (doom-update-package package t) "Updated" "Failed to update")
- package old-v-str new-v-str)
- (message "Aborted")))
- (message "%s is up-to-date" package))))
-
-
-;;
-;; Advice
-
-;;;###autoload
-(defun doom*package-delete (desc &rest _)
- "Update `quelpa-cache' upon a successful `package-delete'."
- (let ((name (package-desc-name desc)))
- (unless (doom-package-installed-p name)
- (when-let (spec (assq name quelpa-cache))
- (setq quelpa-cache (delq spec quelpa-cache))
- (quelpa-save-cache)
- (doom--delete-package-files name)))))
-
-
-;;
-;; 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//autoremove)
-
-;;;###autoload
-(advice-add #'package-install-selected-packages :override #'doom//install)
diff --git a/core/cli/autoloads.el b/core/cli/autoloads.el
index e16b74ebe..2f4bbf98a 100644
--- a/core/cli/autoloads.el
+++ b/core/cli/autoloads.el
@@ -1,7 +1,6 @@
;;; core/cli/autoloads.el -*- lexical-binding: t; -*-
-(dispatcher! (autoloads a)
- (doom-reload-autoloads nil 'force)
+(def-command! (autoloads a) ()
"Regenerates Doom's autoloads files.
It scans and reads autoload cookies (;;;###autoload) in core/autoload/*.el,
@@ -10,7 +9,8 @@ byte-compiles `doom-autoload-file', as well as `doom-package-autoload-file'
(created from the concatenated autoloads files of all installed packages).
It also caches `load-path', `Info-directory-list', `doom-disabled-packages',
-`package-activated-list' and `auto-mode-alist'.")
+`package-activated-list' and `auto-mode-alist'."
+ (doom-reload-autoloads nil 'force))
;; external variables
(defvar autoload-timestamps)
@@ -21,55 +21,45 @@ It also caches `load-path', `Info-directory-list', `doom-disabled-packages',
;;
;;; Helpers
-(defvar doom-autoload-excluded-packages '(marshal gh)
- "Packages that have silly or destructive autoload files that try to load
-everyone in the universe and their dog, causing errors that make babies cry. No
-one wants that.")
-
(defun doom-delete-autoloads-file (file)
"Delete FILE (an autoloads file) and accompanying *.elc file, if any."
(cl-check-type file string)
(when (file-exists-p file)
- (when-let (buf (find-buffer-visiting doom-autoload-file))
+ (when-let (buf (find-buffer-visiting file))
(with-current-buffer buf
(set-buffer-modified-p nil))
(kill-buffer buf))
(delete-file file)
(ignore-errors (delete-file (byte-compile-dest-file file)))
- (message "Deleted old %s" (file-name-nondirectory file))))
+ t))
(defun doom--warn-refresh-session ()
- (print! (bold (green "\nFinished!")))
- (message "If you have a running Emacs Session, you will need to restart it or")
- (message "reload Doom for changes to take effect:\n")
+ (message "Restart or reload Doom Emacs for changes to take effect:\n")
(message " M-x doom/restart-and-restore")
(message " M-x doom/restart")
(message " M-x doom/reload"))
(defun doom--reload-files (&rest files)
- (if (not noninteractive)
- (dolist (file files)
- (load-file (byte-compile-dest-file file)))
- (add-hook 'kill-emacs-hook #'doom--warn-refresh-session)))
+ (if noninteractive
+ (add-hook 'doom-cli-post-execute-hook #'doom--warn-refresh-session)
+ (dolist (file files)
+ (load-file (byte-compile-dest-file file)))))
(defun doom--byte-compile-file (file)
- (let ((short-name (file-name-nondirectory file))
- (byte-compile-dynamic-docstrings t))
+ (let ((byte-compile-warnings (if doom-debug-mode byte-compile-warnings)))
(condition-case e
(when (byte-compile-file file)
;; Give autoloads file a chance to report error
(load (if doom-debug-mode
file
(byte-compile-dest-file file))
- nil t)
- (unless noninteractive
- (message "Finished compiling %s" short-name)))
+ nil t))
((debug error)
(let ((backup-file (concat file ".bk")))
- (message "Copied backup to %s" backup-file)
+ (print! (warn "- Copied backup to %s") (relpath backup-file))
(copy-file file backup-file 'overwrite))
(doom-delete-autoloads-file file)
- (signal 'doom-autoload-error (list short-name e))))))
+ (signal 'doom-autoload-error (list file e))))))
(defun doom-reload-autoloads (&optional file force-p)
"Reloads FILE (an autoload file), if it needs reloading.
@@ -82,71 +72,133 @@ even if it doesn't need reloading!"
(signal 'wrong-type-argument (list 'stringp file)))
(if (stringp file)
(cond ((file-equal-p file doom-autoload-file)
- (doom-reload-doom-autoloads force-p))
+ (doom-reload-core-autoloads force-p))
((file-equal-p file doom-package-autoload-file)
(doom-reload-package-autoloads force-p))
((error "Invalid autoloads file: %s" file)))
- (doom-reload-doom-autoloads force-p)
+ (doom-reload-core-autoloads force-p)
(doom-reload-package-autoloads force-p)))
;;
;;; Doom autoloads
-(defun doom--file-cookie-p (file)
- "Returns the return value of the ;;;###if predicate form in FILE."
- (with-temp-buffer
- (insert-file-contents-literally file nil 0 256)
- (if (and (re-search-forward "^;;;###if " nil t)
- (<= (line-number-at-pos) 3))
- (let ((load-file-name file))
- (eval (sexp-at-point)))
- t)))
-
(defun doom--generate-header (func)
(goto-char (point-min))
- (insert ";; -*- lexical-binding:t -*-\n"
+ (insert ";; -*- lexical-binding:t; byte-compile-dynamic-docstrings: t; -*-\n"
";; This file is autogenerated by `" (symbol-name func) "', DO NOT EDIT !!\n\n"))
(defun doom--generate-autoloads (targets)
- (require 'autoload)
- (dolist (file targets)
- (let* ((file (file-truename file))
- (generated-autoload-file doom-autoload-file)
- (generated-autoload-load-name (file-name-sans-extension file))
- (noninteractive (not doom-debug-mode))
- autoload-timestamps)
- (print!
- (cond ((not (doom--file-cookie-p file))
- "⚠ Ignoring %s")
- ((autoload-generate-file-autoloads file (current-buffer))
- (yellow "✕ Nothing in %s"))
- ((green "✓ Scanned %s")))
- (if (file-in-directory-p file default-directory)
- (file-relative-name file)
- (abbreviate-file-name file))))))
+ (let ((n 0))
+ (dolist (file targets)
+ (insert
+ (with-temp-buffer
+ (cond ((not (doom-file-cookie-p file))
+ (print! (debug "Ignoring %s") (relpath file)))
-(defun doom--expand-autoloads ()
+ ((let ((generated-autoload-load-name (file-name-sans-extension file)))
+ (require 'autoload)
+ (autoload-generate-file-autoloads file (current-buffer)))
+ (print! (debug "Nothing in %s") (relpath file)))
+
+ ((cl-incf n)
+ (print! (debug "Scanning %s...") (relpath file))))
+ (buffer-string))))
+ (print! (class (if (> n 0) 'success 'info)
+ "Scanned %d file(s)")
+ n)))
+
+(defun doom--expand-autoload-paths (&optional allow-internal-paths)
(let ((load-path
;; NOTE With `doom-private-dir' in `load-path', Doom autoloads files
;; will be unable to declare autoloads for the built-in autoload.el
;; Emacs package, should $DOOMDIR/autoload.el exist. Not sure why
;; they'd want to though, so it's an acceptable compromise.
- (append (list doom-private-dir doom-emacs-dir)
+ (append (list doom-private-dir)
doom-modules-dirs
- load-path))
- cache)
- (while (re-search-forward "^\\s-*(autoload\\s-+'[^ ]+\\s-+\"\\([^\"]*\\)\"" nil t)
+ (straight--directory-files (straight--build-dir) nil t)
+ load-path)))
+ (defvar doom--autoloads-path-cache nil)
+ (while (re-search-forward "^\\s-*(\\(?:custom-\\)?autoload\\s-+'[^ ]+\\s-+\"\\([^\"]*\\)\"" nil t)
(let ((path (match-string 1)))
(replace-match
- (or (cdr (assoc path cache))
- (when-let* ((libpath (locate-library path))
- (libpath (file-name-sans-extension libpath)))
- (push (cons path (abbreviate-file-name libpath)) cache)
+ (or (cdr (assoc path doom--autoloads-path-cache))
+ (when-let* ((libpath (or (and allow-internal-paths
+ (locate-library path nil (cons doom-emacs-dir doom-modules-dirs)))
+ (locate-library path)))
+ (libpath (file-name-sans-extension libpath))
+ (libpath (abbreviate-file-name libpath)))
+ (push (cons path libpath) doom--autoloads-path-cache)
libpath)
path)
t t nil 1)))))
+(defun doom--generate-autodefs-1 (path &optional member-p)
+ (let (forms)
+ (while (re-search-forward "^;;;###autodef *\\([^\n]+\\)?\n" nil t)
+ (let* ((sexp (sexp-at-point))
+ (alt-sexp (match-string 1))
+ (type (car sexp))
+ (name (doom-unquote (cadr sexp)))
+ (origin (cond ((doom-module-from-path path))
+ ((file-in-directory-p path doom-private-dir)
+ `(:private . ,(intern (file-name-base path))))
+ ((file-in-directory-p path doom-emacs-dir)
+ `(:core . ,(intern (file-name-base path)))))))
+ (cond
+ ((and (not member-p)
+ alt-sexp)
+ (push (read alt-sexp) forms))
+
+ ((memq type '(defun defmacro cl-defun cl-defmacro))
+ (cl-destructuring-bind (_ _name arglist &rest body) sexp
+ (let ((docstring (if (stringp (car body))
+ (pop body)
+ "No documentation.")))
+ (appendq!
+ forms
+ (list (if member-p
+ (make-autoload sexp (abbreviate-file-name (file-name-sans-extension path)))
+ (setq docstring (format "THIS FUNCTION DOES NOTHING BECAUSE %s IS DISABLED\n\n%s"
+ origin docstring))
+ (condition-case-unless-debug e
+ (if alt-sexp
+ (read alt-sexp)
+ (append (list (pcase type
+ (`defun 'defmacro)
+ (`cl-defun `cl-defmacro)
+ (_ type))
+ name arglist docstring)
+ (cl-loop for arg in arglist
+ if (and (symbolp arg)
+ (not (keywordp arg))
+ (not (memq arg cl--lambda-list-keywords)))
+ collect arg into syms
+ else if (listp arg)
+ collect (car arg) into syms
+ finally return (if syms `((ignore ,@syms))))))
+ ('error
+ (print! "- Ignoring autodef %s (%s)" name e)
+ nil)))
+ `(put ',name 'doom-module ',origin))))))
+
+ ((eq type 'defalias)
+ (cl-destructuring-bind (_type name target &optional docstring) sexp
+ (let ((name (doom-unquote name))
+ (target (doom-unquote target)))
+ (unless member-p
+ (setq target #'ignore
+ docstring
+ (format "THIS FUNCTION DOES NOTHING BECAUSE %s IS DISABLED\n\n%s"
+ origin docstring)))
+ (appendq!
+ forms
+ `((put ',name 'doom-module ',origin)
+ (defalias ',name #',target ,docstring))))))
+
+ (member-p (push sexp forms)))))
+ forms))
+
(defun doom--generate-autodefs (targets enabled-targets)
(goto-char (point-max))
(search-backward ";;;***" nil t)
@@ -155,83 +207,17 @@ even if it doesn't need reloading!"
(insert
(with-temp-buffer
(insert-file-contents path)
- (let ((member-p (or (member path enabled-targets)
- (file-in-directory-p path doom-core-dir)))
- forms)
- (while (re-search-forward "^;;;###autodef *\\([^\n]+\\)?\n" nil t)
- (let* ((sexp (sexp-at-point))
- (alt-sexp (match-string 1))
- (type (car sexp))
- (name (doom-unquote (cadr sexp)))
- (origin (cond ((doom-module-from-path path))
- ((file-in-directory-p path doom-private-dir)
- `(:private . ,(intern (file-name-base path))))
- ((file-in-directory-p path doom-emacs-dir)
- `(:core . ,(intern (file-name-base path))))))
- (doom-file-form
- `(put ',name 'doom-file ,(abbreviate-file-name path))))
- (cond ((and (not member-p) alt-sexp)
- (push (read alt-sexp) forms))
-
- ((memq type '(defun defmacro cl-defun cl-defmacro))
- (cl-destructuring-bind (_ name arglist &rest body) sexp
- (let ((docstring (if (stringp (car body))
- (pop body)
- "No documentation.")))
- (push (if member-p
- (make-autoload sexp (abbreviate-file-name (file-name-sans-extension path)))
- (push doom-file-form forms)
- (setq docstring (format "THIS FUNCTION DOES NOTHING BECAUSE %s IS DISABLED\n\n%s"
- origin docstring))
- (condition-case-unless-debug e
- (if alt-sexp
- (read alt-sexp)
- (append (list (pcase type
- (`defun 'defmacro)
- (`cl-defun `cl-defmacro)
- (_ type))
- name arglist docstring)
- (cl-loop for arg in arglist
- if (and (symbolp arg)
- (not (keywordp arg))
- (not (memq arg cl--lambda-list-keywords)))
- collect arg into syms
- else if (listp arg)
- collect (car arg) into syms
- finally return (if syms `((ignore ,@syms))))))
- ('error
- (message "Ignoring autodef %s (%s)"
- name e)
- nil)))
- forms)
- (push `(put ',name 'doom-module ',origin) forms))))
-
- ((eq type 'defalias)
- (cl-destructuring-bind (_type name target &optional docstring) sexp
- (let ((name (doom-unquote name))
- (target (doom-unquote target)))
- (unless member-p
- (setq docstring (format "THIS FUNCTION DOES NOTHING BECAUSE %s IS DISABLED\n\n%s"
- origin docstring))
- (setq target #'ignore))
- (push doom-file-form forms)
- (push `(put ',name 'doom-module ',origin) forms)
- (push `(defalias ',name #',target ,docstring)
- forms))))
-
- (member-p
- (push sexp forms)))))
- (if forms
- (concat (mapconcat #'prin1-to-string (nreverse forms) "\n")
- "\n")
- ""))))))
+ (if-let (forms (doom--generate-autodefs-1 path (member path enabled-targets)))
+ (concat (mapconcat #'prin1-to-string (nreverse forms) "\n")
+ "\n")
+ "")))))
(defun doom--cleanup-autoloads ()
(goto-char (point-min))
(when (re-search-forward "^;;\\(;[^\n]*\\| no-byte-compile: t\\)\n" nil t)
(replace-match "" t t)))
-(defun doom-reload-doom-autoloads (&optional force-p)
+(defun doom-reload-core-autoloads (&optional force-p)
"Refreshes `doom-autoload-file', if necessary (or if FORCE-P is non-nil).
It scans and reads autoload cookies (;;;###autoload) in core/autoload/*.el,
@@ -241,62 +227,78 @@ modules/*/*/autoload.el and modules/*/*/autoload/*.el, and generates
Run this whenever your `doom!' block, or a module autoload file, is modified."
(let* ((default-directory doom-emacs-dir)
(doom-modules (doom-modules))
- (abbreviated-home-dir (if IS-WINDOWS "\\`'" abbreviated-home-dir))
- (targets
- (file-expand-wildcards
- (expand-file-name "autoload/*.el" doom-core-dir)))
- (enabled-targets (copy-sequence targets))
- case-fold-search)
- (dolist (path (doom-module-load-path t))
- (let* ((auto-dir (expand-file-name "autoload" path))
- (auto-file (expand-file-name "autoload.el" path))
- (module (doom-module-from-path auto-file))
- (module-p (or (doom-module-p (car module) (cdr module))
- (file-equal-p path doom-private-dir))))
- (when (file-exists-p auto-file)
- (push auto-file targets)
- (if module-p (push auto-file enabled-targets)))
- (dolist (file (doom-files-in auto-dir :match "\\.el$" :full t :sort nil))
- (push file targets)
- (if module-p (push file enabled-targets)))))
- (if (and (not force-p)
- (not doom-emacs-changed-p)
- (file-exists-p doom-autoload-file)
- (not (file-newer-than-file-p (expand-file-name "init.el" doom-private-dir)
- doom-autoload-file))
- (not (cl-loop for file in targets
- if (file-newer-than-file-p file doom-autoload-file)
- return t)))
- (progn (print! (green "Doom core autoloads is up-to-date"))
- (doom-initialize-autoloads doom-autoload-file)
- nil)
- (doom-delete-autoloads-file doom-autoload-file)
- (message "Generating new autoloads.el")
- (make-directory (file-name-directory doom-autoload-file) t)
- (with-temp-file doom-autoload-file
- (doom--generate-header 'doom-reload-doom-autoloads)
- (prin1 `(setq doom--modules-cache ',doom-modules) (current-buffer))
- (save-excursion
- (doom--generate-autoloads (reverse enabled-targets)))
- ;; Replace autoload paths (only for module autoloads) with absolute
- ;; paths for faster resolution during load and simpler `load-path'
- (save-excursion
- (doom--expand-autoloads)
- (print! (green "✓ Expanded module autoload paths")))
- ;; Generates stub definitions for functions/macros defined in disabled
- ;; modules, so that you will never get a void-function when you use
- ;; them.
- (save-excursion
- (doom--generate-autodefs (reverse targets) enabled-targets)
- (print! (green "✓ Generated autodefs")))
- ;; Remove byte-compile-inhibiting file variables so we can byte-compile
- ;; the file, and autoload comments.
- (doom--cleanup-autoloads)
- (print! (green "✓ Clean up autoloads")))
- ;; Byte compile it to give the file a chance to reveal errors.
- (doom--byte-compile-file doom-autoload-file)
- (doom--reload-files doom-autoload-file)
- t)))
+
+ ;; The following bindings are in `package-generate-autoloads'.
+ ;; Presumably for a good reason, so I just copied them
+ (noninteractive t)
+ (backup-inhibited t)
+ (version-control 'never)
+ (case-fold-search nil) ; reduce magit
+ (autoload-timestamps nil)
+
+ ;; Where we'll store the files we'll scan for autoloads. This should
+ ;; contain *all* autoload files, even in disabled modules, so we can
+ ;; scan those for autodefs. We start with the core libraries.
+ (targets (doom-glob doom-core-dir "autoload/*.el"))
+ ;; A subset of `targets' in enabled modules
+ (active-targets (copy-sequence targets)))
+
+ (dolist (path (doom-module-load-path 'all-p))
+ (when-let* ((files (cons (doom-glob path "autoload.el")
+ (doom-files-in (doom-path path "autoload")
+ :match "\\.el$")))
+ (files (delq nil files)))
+ (appendq! targets files)
+ (when (or (doom-module-from-path path 'enabled-only)
+ (file-equal-p path doom-private-dir))
+ (appendq! active-targets files))))
+
+ (print! (start "Checking core autoloads file"))
+ (print-group!
+ (if (and (not force-p)
+ (file-exists-p doom-autoload-file)
+ (not (file-newer-than-file-p doom-emacs-dir doom-autoload-file))
+ (not (cl-loop for dir
+ in (append (doom-glob doom-private-dir "init.el*")
+ targets)
+ if (file-newer-than-file-p dir doom-autoload-file)
+ return t)))
+ (ignore
+ (print! (success "Skipping core autoloads, they are up-to-date"))
+ (doom-initialize-autoloads doom-autoload-file))
+ (print! (start "Regenerating core autoloads file"))
+
+ (if (doom-delete-autoloads-file doom-autoload-file)
+ (print! (success "Deleted old %s") (filename doom-autoload-file))
+ (make-directory (file-name-directory doom-autoload-file) t))
+
+ (with-temp-file doom-autoload-file
+ (doom--generate-header 'doom-reload-core-autoloads)
+ (save-excursion
+ (doom--generate-autoloads active-targets)
+ (print! (success "Generated new autoloads.el")))
+ ;; Replace autoload paths (only for module autoloads) with absolute
+ ;; paths for faster resolution during load and simpler `load-path'
+ (save-excursion
+ (doom--expand-autoload-paths 'allow-internal-paths)
+ (print! (success "Expanded module autoload paths")))
+ ;; Generates stub definitions for functions/macros defined in disabled
+ ;; modules, so that you will never get a void-function when you use
+ ;; them.
+ (save-excursion
+ (doom--generate-autodefs targets (reverse active-targets))
+ (print! (success "Generated autodefs")))
+ ;; Remove byte-compile-inhibiting file variables so we can byte-compile
+ ;; the file, and autoload comments.
+ (doom--cleanup-autoloads)
+ (print! (success "Clean up autoloads")))
+ ;; Byte compile it to give the file a chance to reveal errors (and buy us a
+ ;; few marginal performance boosts)
+ (print! "> Byte-compiling %s..." (relpath doom-autoload-file))
+ (when (doom--byte-compile-file doom-autoload-file)
+ (print! (success "Finished compiling %s") (relpath doom-autoload-file)))
+ (doom--reload-files doom-autoload-file))
+ t)))
;;
@@ -304,33 +306,49 @@ Run this whenever your `doom!' block, or a module autoload file, is modified."
(defun doom--generate-package-autoloads ()
"Concatenates package autoload files, let-binds `load-file-name' around
-them,and remove unnecessary `provide' statements or blank links.
-
-Skips over packages in `doom-autoload-excluded-packages'."
- (dolist (spec (doom-get-package-alist))
- (if-let* ((pkg (car spec))
- (desc (cdr spec)))
- (unless (memq pkg doom-autoload-excluded-packages)
- (let ((file (concat (package--autoloads-file-name desc) ".el")))
- (when (file-exists-p file)
- (insert "(let ((load-file-name " (prin1-to-string (abbreviate-file-name file)) "))\n")
- (insert-file-contents file)
- (while (re-search-forward "^\\(?:;;\\(.*\n\\)\\|\n\\|(provide '[^\n]+\\)" nil t)
- (unless (nth 8 (syntax-ppss))
- (replace-match "" t t)))
- (unless (bolp) (insert "\n"))
- (insert ")\n"))))
- (message "Couldn't find package desc for %s" (car spec)))))
+them,and remove unnecessary `provide' statements or blank links."
+ (dolist (pkg (straight--directory-files (straight--build-dir)))
+ (let ((file (straight--autoloads-file pkg)))
+ (when (file-exists-p file)
+ (insert-file-contents file)
+ (when (save-excursion
+ (and (re-search-forward "\\_" nil t)
+ (not (nth 8 (syntax-ppss)))))
+ ;; Set `load-file-name' so that the contents of autoloads
+ ;; files can pretend they're in the file they're expected to
+ ;; be in, rather than `doom-package-autoload-file'.
+ (insert (format "(setq load-file-name %S)\n" (abbreviate-file-name file))))
+ (while (re-search-forward "^\\(?:;;\\(.*\n\\)\\|\n\\|(provide '[^\n]+\\)" nil t)
+ (unless (nth 8 (syntax-ppss))
+ (replace-match "" t t)))
+ (unless (bolp) (insert "\n"))))))
(defun doom--generate-var-cache ()
"Print a `setq' form for expensive-to-initialize variables, so we can cache
them in Doom's autoloads file."
(doom-initialize-packages)
(prin1 `(setq load-path ',load-path
- auto-mode-alist ',auto-mode-alist
+ auto-mode-alist
+ ',(let ((alist (copy-sequence auto-mode-alist))
+ newalist
+ last-group
+ last-mode
+ it)
+ (while (setq it (pop alist))
+ (cl-destructuring-bind (re . mode) it
+ (unless (eq mode last-mode)
+ (when last-mode
+ (push (cons (if (cdr last-group)
+ (concat "\\(?:" (string-join last-group "\\)\\|\\(?:") "\\)")
+ (car last-group))
+ last-mode)
+ newalist))
+ (setq last-mode mode
+ last-group nil))
+ (push re last-group)))
+ (nreverse newalist))
Info-directory-list ',Info-directory-list
- doom-disabled-packages ',(mapcar #'car (doom-find-packages :disabled t))
- package-activated-list ',package-activated-list)
+ doom-disabled-packages ',doom-disabled-packages)
(current-buffer)))
(defun doom--cleanup-package-autoloads ()
@@ -351,34 +369,63 @@ Will do nothing if none of your installed packages have been modified. If
FORCE-P (universal argument) is non-nil, regenerate it anyway.
This should be run whenever your `doom!' block or update your packages."
- (let ((abbreviated-home-dir (if IS-WINDOWS "\\`'" abbreviated-home-dir)))
- (if (and (not force-p)
- (not doom-emacs-changed-p)
- (file-exists-p doom-package-autoload-file)
- (not (file-newer-than-file-p doom-packages-dir doom-package-autoload-file))
- (not (ignore-errors
- (cl-loop for key being the hash-keys of (doom-modules)
- for path = (doom-module-path (car key) (cdr key) "packages.el")
- if (file-newer-than-file-p path doom-package-autoload-file)
- return t))))
- (ignore (print! (green "Doom package autoloads is up-to-date"))
- (doom-initialize-autoloads doom-package-autoload-file))
- (let (case-fold-search)
- (doom-delete-autoloads-file doom-package-autoload-file)
- (with-temp-file doom-package-autoload-file
- (doom--generate-header 'doom-reload-package-autoloads)
- (save-excursion
- ;; Cache important and expensive-to-initialize state here.
- (doom--generate-var-cache)
- (print! (green "✓ Cached package state"))
- ;; Concatenate the autoloads of all installed packages.
- (doom--generate-package-autoloads)
- (print! (green "✓ Package autoloads included")))
- ;; Remove `load-path' and `auto-mode-alist' modifications (most of them,
- ;; at least); they are cached later, so all those membership checks are
- ;; unnecessary overhead.
- (doom--cleanup-package-autoloads)
- (print! (green "✓ Removed load-path/auto-mode-alist entries"))))
- (doom--byte-compile-file doom-package-autoload-file)
- (doom--reload-files doom-package-autoload-file)
- t)))
+ (print! (start "Checking package autoloads file"))
+ (print-group!
+ (if (and (not force-p)
+ (file-exists-p doom-package-autoload-file)
+ (not (file-newer-than-file-p doom-elpa-dir doom-package-autoload-file))
+ (not (ignore-errors
+ (cl-loop for dir in (straight--directory-files (straight--repos-dir))
+ if (cl-find-if (lambda (dir) (file-newer-than-file-p dir doom-package-autoload-file))
+ (glob! (straight--repos-dir dir) "*.el"))
+ return t)))
+ (not (ignore-errors
+ (cl-loop for key being the hash-keys of (doom-modules)
+ for path = (doom-module-path (car key) (cdr key) "packages.el")
+ if (file-newer-than-file-p path doom-package-autoload-file)
+ return t))))
+ (ignore
+ (print! (success "Skipping package autoloads, they are up-to-date"))
+ (doom-initialize-autoloads doom-package-autoload-file))
+ (let (;; The following bindings are in `package-generate-autoloads'.
+ ;; Presumably for a good reason, so I just copied them
+ (noninteractive t)
+ (backup-inhibited t)
+ (version-control 'never)
+ (case-fold-search nil) ; reduce magit
+ (autoload-timestamps nil))
+ (print! (start "Regenerating package autoloads file"))
+
+ (if (doom-delete-autoloads-file doom-package-autoload-file)
+ (print! (success "Deleted old %s") (filename doom-package-autoload-file))
+ (make-directory (file-name-directory doom-autoload-file) t))
+
+ (with-temp-file doom-package-autoload-file
+ (doom--generate-header 'doom-reload-package-autoloads)
+
+ (save-excursion
+ ;; Cache important and expensive-to-initialize state here.
+ (doom--generate-var-cache)
+ (print! (success "Cached package state"))
+ ;; Concatenate the autoloads of all installed packages.
+ (doom--generate-package-autoloads)
+ (print! (success "Package autoloads included")))
+
+ ;; Replace autoload paths (only for module autoloads) with absolute
+ ;; paths for faster resolution during load and simpler `load-path'
+ (save-excursion
+ (doom--expand-autoload-paths)
+ (print! (success "Expanded module autoload paths")))
+
+ ;; Remove `load-path' and `auto-mode-alist' modifications (most of them,
+ ;; at least); they are cached later, so all those membership checks are
+ ;; unnecessary overhead.
+ (doom--cleanup-package-autoloads)
+ (print! (success "Removed load-path/auto-mode-alist entries")))
+ ;; Byte compile it to give the file a chance to reveal errors (and buy us a
+ ;; few marginal performance boosts)
+ (print! (start "Byte-compiling %s...") (relpath doom-package-autoload-file))
+ (when (doom--byte-compile-file doom-package-autoload-file)
+ (print! (success "Finished compiling %s") (relpath doom-package-autoload-file)))
+ (doom--reload-files doom-package-autoload-file)))
+ t))
diff --git a/core/cli/byte-compile.el b/core/cli/byte-compile.el
index 4d7533011..9de5cfa4a 100644
--- a/core/cli/byte-compile.el
+++ b/core/cli/byte-compile.el
@@ -1,21 +1,24 @@
;;; core/cli/byte-compile.el -*- lexical-binding: t; -*-
-(dispatcher! (compile c) (doom-byte-compile args)
+(def-command! (compile c) (&rest targets)
"Byte-compiles your config or selected modules.
compile [TARGETS...]
compile :core :private lang/python
compile feature lang
-Accepts :core, :private and :plugins as special arguments, indicating you want
-to byte-compile Doom's core files, your private config or your ELPA plugins,
-respectively.")
+Accepts :core and :private as special arguments, which target Doom's core files
+and your private config files, respectively. To recompile your packages, use
+'doom rebuild' instead."
+ (doom-byte-compile targets))
-(dispatcher! (recompile rc) (doom-byte-compile args 'recompile)
- "Re-byte-compiles outdated *.elc files.")
+(def-command! (recompile rc) (&rest targets)
+ "Re-byte-compiles outdated *.elc files."
+ (doom-byte-compile targets 'recompile))
-(dispatcher! clean (doom-clean-byte-compiled-files)
- "Delete all *.elc files.")
+(def-command! clean ()
+ "Delete all *.elc files."
+ (doom-clean-byte-compiled-files))
;;
@@ -25,9 +28,10 @@ respectively.")
(let ((filename (file-name-nondirectory path)))
(or (string-prefix-p "." filename)
(string-prefix-p "test-" filename)
- (not (equal (file-name-extension path) "el")))))
+ (not (equal (file-name-extension path) "el"))
+ (member filename (list "packages.el" "doctor.el")))))
-(defun doom-byte-compile (&optional modules recompile-p)
+(cl-defun doom-byte-compile (&optional modules recompile-p)
"Byte compiles your emacs configuration.
init.el is always byte-compiled by this.
@@ -45,35 +49,35 @@ byte-compilation.
If RECOMPILE-P is non-nil, only recompile out-of-date files."
(let ((default-directory doom-emacs-dir)
- (total-ok 0)
- (total-fail 0)
- (total-noop 0)
- compile-plugins-p
+ (doom-modules (doom-modules))
+ (byte-compile-verbose doom-debug-mode)
+ (byte-compile-warnings '(not free-vars unresolved noruntime lexical make-local))
+
+ ;; In case it is changed during compile-time
+ (auto-mode-alist auto-mode-alist)
+ (noninteractive t)
+
targets)
- (dolist (module (delete-dups modules) (nreverse targets))
- (pcase module
- (":core" (push doom-core-dir targets))
- (":private" (push doom-private-dir targets))
- (":plugins"
- (cl-loop for (_name . desc) in (doom-get-package-alist)
- do (package--compile desc))
- (setq compile-plugins-p t
- modules (delete ":plugins" modules)))
- ((pred file-directory-p)
- (push module targets))
- ((pred (string-match "^\\([^/]+\\)/\\([^/]+\\)$"))
- (push (doom-module-locate-path
- (doom-keyword-intern (match-string 1 module))
- (intern (match-string 2 module)))
- targets))))
- (cl-block 'byte-compile
- ;; If we're just here to byte-compile our plugins, we're done!
- (and (not modules)
- compile-plugins-p
- (cl-return-from 'byte-compile t))
- (unless (or (equal modules '(":core"))
- recompile-p)
- (unless (or doom-auto-accept
+
+ (let (target-dirs)
+ (dolist (module (delete-dups modules))
+ (pcase module
+ (":core"
+ (push (doom-glob doom-emacs-dir "init.el") targets)
+ (push doom-core-dir target-dirs))
+ (":private"
+ (push doom-private-dir target-dirs))
+ ((pred file-directory-p)
+ (push module target-dirs))
+ ((pred (string-match "^\\([^/]+\\)/\\([^/]+\\)$"))
+ (push (doom-module-locate-path
+ (doom-keyword-intern (match-string 1 module))
+ (intern (match-string 2 module)))
+ target-dirs))))
+
+ (and (or (null modules) (member ":private" modules))
+ (not recompile-p)
+ (not (or doom-auto-accept
(y-or-n-p
(concat "Warning: byte compiling is for advanced users. It will interfere with your\n"
"efforts to debug issues. It is not recommended you do it if you frequently\n"
@@ -82,109 +86,113 @@ If RECOMPILE-P is non-nil, only recompile out-of-date files."
"Doom core files, as these don't change often.\n\n"
"If you have issues, please make sure byte-compilation isn't the cause by using\n"
"`bin/doom clean` to clear out your *.elc files.\n\n"
- "Byte-compile anyway?")))
- (message "Aborting.")
- (cl-return-from 'byte-compile)))
- (when (and (not recompile-p)
- (or (null modules)
- (equal modules '(":core"))))
- (doom-clean-byte-compiled-files))
- (let (doom-emacs-changed-p
- noninteractive)
- ;; But first we must be sure that Doom and your private config have been
- ;; fully loaded. Which usually aren't so in an noninteractive session.
- (unless (and (doom-initialize-autoloads doom-autoload-file)
- (doom-initialize-autoloads doom-package-autoload-file))
- (doom-reload-autoloads))
- (doom-initialize)
- (doom-initialize-modules 'force))
- ;; If no targets were supplied, then we use your module list.
- (unless modules
- (let ((doom-modules-dirs (delete (expand-file-name "modules/" doom-private-dir)
- doom-modules-dirs)))
- (setq targets
- (append (list doom-core-dir)
- (delete doom-private-dir (doom-module-load-path))))))
- ;; Assemble el files we want to compile; taking into account that
- ;; MODULES may be a list of MODULE/SUBMODULE strings from the command
- ;; line.
- (let ((target-files (doom-files-in targets :filter #'doom--byte-compile-ignore-file-p :sort nil)))
- (when (or (not modules)
- (member ":core" modules))
- (push (expand-file-name "init.el" doom-emacs-dir)
- target-files))
- (unless target-files
- (if targets
- (message "Couldn't find any valid targets")
- (message "No targets to %scompile" (if recompile-p "re" "")))
- (cl-return-from 'byte-compile))
- (require 'use-package)
- (condition-case e
- (let ((use-package-defaults use-package-defaults)
- (use-package-expand-minimally t)
- (load-path load-path)
- kill-emacs-hook kill-buffer-query-functions)
- ;; Prevent packages from being loaded at compile time if they
- ;; don't meet their own predicates.
- (push (list :no-require t
- (lambda (_name args)
- (or (when-let (pred (or (plist-get args :if)
- (plist-get args :when)))
- (not (eval pred t)))
- (when-let (pred (plist-get args :unless))
- (eval pred t)))))
- use-package-defaults)
- (dolist (target (cl-delete-duplicates (mapcar #'file-truename target-files) :test #'equal))
- (if (or (not recompile-p)
- (let ((elc-file (byte-compile-dest-file target)))
- (and (file-exists-p elc-file)
- (file-newer-than-file-p target elc-file))))
- (let ((result (if (or (string-match-p "/\\(?:packages\\|doctor\\)\\.el$" target)
- (not (doom--file-cookie-p target)))
- 'no-byte-compile
- (byte-compile-file target)))
- (short-name (if (file-in-directory-p target doom-emacs-dir)
- (file-relative-name target doom-emacs-dir)
- (abbreviate-file-name target))))
- (cl-incf
- (cond ((eq result 'no-byte-compile)
- (print! (dark (white "⚠ Ignored %s")) short-name)
- total-noop)
- ((null result)
- (print! (red "✕ Failed to compile %s") short-name)
- total-fail)
- (t
- (print! (green "✓ Compiled %s") short-name)
- (load target t t)
- total-ok))))
- (cl-incf total-noop)))
- (print! (bold (color (if (= total-fail 0) 'green 'red)
- "%s %d/%d file(s) (%d ignored)"))
- (if recompile-p "Recompiled" "Compiled")
- total-ok (- (length target-files) total-noop)
- total-noop)
- (or (= total-fail 0)
- (error "Failed to compile some files")))
- ((debug error)
- (print! (red "\nThere were breaking errors.\n\n%s")
- "Reverting changes...")
- (signal 'doom-error (list 'byte-compile e))))))))
+ "Byte-compile anyway?"))))
+ (user-error "Aborting"))
+
+ ;; But first we must be sure that Doom and your private config have been
+ ;; fully loaded. Which usually aren't so in an noninteractive session.
+ (doom-initialize-autoloads doom-autoload-file)
+ (doom-initialize-autoloads doom-package-autoload-file)
+
+ ;;
+ (unless target-dirs
+ (push (doom-glob doom-emacs-dir "init.el") targets)
+ ;; If no targets were supplied, then we use your module list.
+ (appendq! target-dirs
+ (list doom-core-dir)
+ ;; Omit `doom-private-dir', which is always first
+ (cl-remove-if-not (lambda (path) (file-in-directory-p path doom-emacs-dir))
+ (cdr (doom-module-load-path)))))
+
+ ;; Assemble el files we want to compile; taking into account that MODULES
+ ;; may be a list of MODULE/SUBMODULE strings from the command line.
+ (appendq! targets
+ (doom-files-in target-dirs
+ :match "\\.el$"
+ :filter #'doom--byte-compile-ignore-file-p)))
+
+ (unless targets
+ (print!
+ (if targets
+ (warn "Couldn't find any valid targets")
+ (info "No targets to %scompile" (if recompile-p "re" ""))))
+ (cl-return nil))
+
+ (print!
+ (info (if recompile-p
+ "Recompiling stale elc files..."
+ "Byte-compiling your config (may take a while)...")))
+ (print-group!
+ (require 'use-package)
+ (condition-case e
+ (let ((total-ok 0)
+ (total-fail 0)
+ (total-noop 0)
+ (use-package-defaults use-package-defaults)
+ (use-package-expand-minimally t)
+ kill-emacs-hook kill-buffer-query-functions)
+ ;; Prevent packages from being loaded at compile time if they
+ ;; don't meet their own predicates.
+ (push (list :no-require t
+ (lambda (_name args)
+ (or (when-let (pred (or (plist-get args :if)
+ (plist-get args :when)))
+ (not (eval pred t)))
+ (when-let (pred (plist-get args :unless))
+ (eval pred t)))))
+ use-package-defaults)
+
+ (unless recompile-p
+ (doom-clean-byte-compiled-files))
+
+ (dolist (target (delete-dups targets))
+ (cl-incf
+ (if (not (or (not recompile-p)
+ (let ((elc-file (byte-compile-dest-file target)))
+ (and (file-exists-p elc-file)
+ (file-newer-than-file-p target elc-file)))))
+ total-noop
+ (pcase (if (doom-file-cookie-p target)
+ (byte-compile-file target)
+ 'no-byte-compile)
+ (`no-byte-compile
+ (print! (info "Ignored %s") (relpath target))
+ total-noop)
+ (`nil
+ (print! (error "Failed to compile %s") (relpath target))
+ total-fail)
+ (_
+ (print! (success "Compiled %s") (relpath target))
+ (load target t t)
+ total-ok)))))
+ (print! (class (if (= total-fail 0) 'success 'error)
+ "%s %d/%d file(s) (%d ignored)")
+ (if recompile-p "Recompiled" "Compiled")
+ total-ok (- (length targets) total-noop)
+ total-noop)
+ t)
+ ((debug error)
+ (print! (error "\nThere were breaking errors.\n\n%s")
+ "Reverting changes...")
+ (signal 'doom-error (list 'byte-compile e)))))))
(defun doom-clean-byte-compiled-files ()
"Delete all the compiled elc files in your Emacs configuration and private
module. This does not include your byte-compiled, third party packages.'"
- (cl-loop with default-directory = doom-emacs-dir
- for path
- in (append (doom-files-in doom-emacs-dir :match "\\.elc$" :depth 0 :sort nil)
- (doom-files-in doom-private-dir :match "\\.elc$" :depth 1 :sort nil)
- (doom-files-in doom-core-dir :match "\\.elc$" :sort nil)
- (doom-files-in doom-modules-dirs :match "\\.elc$" :depth 4 :sort nil))
- for truepath = (file-truename path)
- if (file-exists-p path)
- do (delete-file path)
- and do
- (print! (green "✓ Deleted %s")
- (if (file-in-directory-p truepath default-directory)
- (file-relative-name truepath)
- (abbreviate-file-name truepath)))
- finally do (print! (bold (green "Everything is clean")))))
+ (print! (start "Cleaning .elc files"))
+ (print-group!
+ (cl-loop with default-directory = doom-emacs-dir
+ with success = nil
+ for path
+ in (append (doom-glob doom-emacs-dir "*.elc")
+ (doom-files-in doom-private-dir :match "\\.elc$" :depth 1 :sort nil)
+ (doom-files-in doom-core-dir :match "\\.elc$" :sort nil)
+ (doom-files-in doom-modules-dirs :match "\\.elc$" :depth 4 :sort nil))
+ if (file-exists-p path)
+ do (delete-file path)
+ and do (print! (success "Deleted %s") (relpath path))
+ and do (setq success t)
+ finally do
+ (print! (if success
+ (success "All elc files deleted")
+ (info "No elc files to clean"))))))
diff --git a/core/cli/debug.el b/core/cli/debug.el
index a66a52507..c8c7b4648 100644
--- a/core/cli/debug.el
+++ b/core/cli/debug.el
@@ -1,7 +1,26 @@
;;; core/cli/debug.el -*- lexical-binding: t; -*-
-(dispatcher! info (doom/info)
- "Output system info in markdown for bug reports.")
+(load! "autoload/debug" doom-core-dir)
-(dispatcher! (version v) (doom/version)
- "Reports the version of Doom and Emacs.")
+
+;;
+;;; Commands
+
+(def-command! info (&optional format)
+ "Output system info in markdown for bug reports."
+ (pcase format
+ ("json"
+ (require 'json)
+ (with-temp-buffer
+ (insert (json-encode (doom-info)))
+ (json-pretty-print-buffer)
+ (print! (buffer-string))))
+ ((or "md" "markdown")
+ (doom/info))
+ (_ (doom/info 'raw)))
+ nil)
+
+(def-command! (version v) ()
+ "Reports the version of Doom and Emacs."
+ (doom/version)
+ nil)
diff --git a/core/cli/env.el b/core/cli/env.el
index 185ff7e1c..f8a0cf85a 100644
--- a/core/cli/env.el
+++ b/core/cli/env.el
@@ -1,25 +1,6 @@
;;; core/cli/env.el -*- lexical-binding: t; -*-
-(dispatcher! env
- (let ((env-file (abbreviate-file-name doom-env-file)))
- (pcase (car args)
- ((or "refresh" "re")
- (doom-reload-env-file 'force))
- ((or "enable" "auto")
- (setenv "DOOMENV" "1")
- (print! (green "Enabling auto-reload of %S") env-file)
- (doom-reload-env-file 'force)
- (print! (green "Done! `doom refresh' will now refresh your envvar file.")))
- ("clear"
- (setenv "DOOMENV" nil)
- (unless (file-exists-p env-file)
- (user-error "%S does not exist to be cleared" env-file))
- (delete-file env-file)
- (print! (green "Disabled envvar file by deleting %S") env-file))
- (_
- (print! "%s\n\n%s"
- (bold (red "No valid subcommand provided."))
- "See `doom help env` to see available commands."))))
+(def-command! env (&optional command)
"Manages your envvars file.
env [SUBCOMMAND]
@@ -38,7 +19,18 @@ on Linux).
To generate a file, run `doom env refresh`. If you'd like this file to be
auto-reloaded when running `doom refresh`, run `doom env enable` instead (only
-needs to be run once).")
+needs to be run once)."
+ (let ((default-directory doom-emacs-dir))
+ (pcase command
+ ("clear"
+ (unless (file-exists-p doom-env-file)
+ (user-error! "%S does not exist to be cleared"
+ (relpath doom-env-file)))
+ (delete-file doom-env-file)
+ (print! (success "Successfully deleted %S")
+ (relpath doom-env-file)))
+ (_
+ (doom-reload-env-file 'force)))))
;;
@@ -86,12 +78,12 @@ default, on Linux, this is '$SHELL -ic /usr/bin/env'. Variables in
`doom-env-ignored-vars' are removed."
(when (or force-p (not (file-exists-p doom-env-file)))
(with-temp-file doom-env-file
- (message "%s envvars file at %S"
+ (print! (start "%s envvars file at %S")
(if (file-exists-p doom-env-file)
"Regenerating"
"Generating")
- (abbreviate-file-name doom-env-file))
- (let ((process-environment doom-site-process-environment))
+ (relpath doom-env-file doom-emacs-dir))
+ (let ((process-environment doom--initial-process-environment))
(insert
(concat
"# -*- mode: dotenv -*-\n"
@@ -111,25 +103,31 @@ default, on Linux, this is '$SHELL -ic /usr/bin/env'. Variables in
"# To auto-regenerate this file when `doom reload` is run, use `doom env auto' or\n"
"# set DOOMENV=1 in your shell environment/config.\n"
"# ---------------------------------------------------------------------------\n\n"))
- (let ((shell-command-switch doom-env-switches))
- (message "Scraping env from '%s %s %s'"
- shell-file-name
- shell-command-switch
- doom-env-executable)
+ (let ((shell-command-switch doom-env-switches)
+ (error-buffer (get-buffer-create "*env errors*")))
+ (print! (info "Scraping shell environment with '%s %s %s'")
+ (filename shell-file-name)
+ shell-command-switch
+ (filename doom-env-executable))
(save-excursion
- (insert (shell-command-to-string doom-env-executable)))
- ;; Remove undesireable variables
- (while (re-search-forward "\n\\([^= \n]+\\)=" nil t)
- (save-excursion
- (let* ((valend (or (save-match-data
- (when (re-search-forward "^\\([^= ]+\\)=" nil t)
- (line-beginning-position)))
- (point-max)))
- (var (match-string 1))
- (value (buffer-substring-no-properties (point) (1- valend))))
- (when (cl-loop for regexp in doom-env-ignored-vars
- if (string-match-p regexp var)
- return t)
- (message "Ignoring %s" var)
- (delete-region (match-beginning 0) (1- valend))))))
- (print! (green "Envvar successfully generated")))))))
+ (shell-command doom-env-executable (current-buffer) error-buffer))
+ (print-group!
+ (let ((errors (with-current-buffer error-buffer (buffer-string))))
+ (unless (string-empty-p errors)
+ (print! (info "Error output:\n\n%s") (indent 4 errors))))
+ ;; Remove undesireable variables
+ (while (re-search-forward "\n\\([^= \n]+\\)=" nil t)
+ (save-excursion
+ (let* ((valend (or (save-match-data
+ (when (re-search-forward "^\\([^= ]+\\)=" nil t)
+ (line-beginning-position)))
+ (point-max)))
+ (var (match-string 1)))
+ (when (cl-loop for regexp in doom-env-ignored-vars
+ if (string-match-p regexp var)
+ return t)
+ (print! (info "Ignoring %s") var)
+ (delete-region (match-beginning 0) (1- valend)))))))
+ (print! (success "Successfully generated %S")
+ (relpath doom-env-file doom-emacs-dir))
+ t)))))
diff --git a/core/cli/packages.el b/core/cli/packages.el
index 661186a50..78c635eb0 100644
--- a/core/cli/packages.el
+++ b/core/cli/packages.el
@@ -1,55 +1,47 @@
;; -*- no-byte-compile: t; -*-
;;; core/cli/packages.el
-;;
-;;; Helpers
-
-(defmacro doom--condition-case! (&rest body)
- `(condition-case-unless-debug e
- (progn ,@body)
- ('user-error
- (print! (bold (red " NOTICE: %s")) e))
- ('file-error
- (print! " %s\n %s"
- (bold (red "FILE ERROR: %s" (error-message-string e)))
- "Trying again...")
- (quiet! (doom-refresh-packages-maybe t))
- ,@body)
- ('error
- (print! (bold (red " %s %s\n %s"))
- "FATAL ERROR: " e
- "Run again with the -d flag for details"))))
-
-(defsubst doom--ensure-autoloads-while (fn)
- (doom-reload-doom-autoloads)
- (when (funcall fn doom-auto-accept)
- (doom-reload-package-autoloads)))
+(defmacro doom--ensure-autoloads-while (&rest body)
+ `(progn
+ (doom-reload-core-autoloads)
+ (when (progn ,@body)
+ (doom-reload-package-autoloads 'force-p))
+ t))
;;
;;; Dispatchers
-(dispatcher! (install i)
- (doom--ensure-autoloads-while #'doom-packages-install)
- "Installs wanted packages that aren't installed.
-
-Package management in Doom is declarative. A `package!' declaration in an
-enabled module or your private packages.el marks a package as 'wanted'.")
-
-(dispatcher! (update u)
- (doom--ensure-autoloads-while #'doom-packages-update)
+(def-command! (update u) ()
"Updates packages.
This excludes packages whose `package!' declaration contains a non-nil :freeze
-or :ignore property.")
+or :ignore property."
+ (doom--ensure-autoloads-while
+ (straight-check-all)
+ (when (doom-packages-update doom-auto-accept)
+ (doom-packages-rebuild doom-auto-accept)
+ t)))
-(dispatcher! (autoremove r)
- (doom--ensure-autoloads-while #'doom-packages-autoremove)
- "Removes packages that are no longer needed.
+(def-command! (rebuild b) ()
+ "Rebuilds all installed packages.
-This includes packages installed with 'M-x package-install' without an
-accompanying `package!' declaration in an enabled module's packages.el file or
-your private one.")
+This ensures that all needed files are symlinked from their package repo and
+their elisp files are byte-compiled."
+ (doom--ensure-autoloads-while
+ (doom-packages-rebuild doom-auto-accept (member "all" args))))
+
+(def-command! (purge p) ()
+ "Deletes any unused packages and package repos.
+
+You should run this once in a while, as repos tend to build up over time."
+ (doom--ensure-autoloads-while
+ (straight-check-all)
+ (doom-packages-purge doom-auto-accept)))
+
+;; (def-command! rollback () ; TODO rollback
+;; ""
+;; (user-error "Not implemented yet, sorry!"))
;;
@@ -63,153 +55,212 @@ declaration) or dependency thereof that hasn't already been.
Unless AUTO-ACCEPT-P is non-nil, this function will prompt for confirmation with
a list of packages that will be installed."
- (print! "Looking for packages to install...")
- (let ((packages (doom-get-missing-packages)))
- (cond ((not packages)
- (print! (green "No packages to install!"))
- nil)
+ (print! "> Installing & building packages...")
+ (print-group!
+ (let ((n 0))
+ (dolist (package (hash-table-keys straight--recipe-cache))
+ (straight--with-plist (gethash package straight--recipe-cache)
+ (local-repo)
+ (let ((existed-p (file-directory-p (straight--repos-dir package))))
+ (condition-case-unless-debug e
+ (and (straight-use-package (intern package) nil nil " ")
+ (not existed-p)
+ (file-directory-p (straight--repos-dir package))
+ (cl-incf n))
+ (error
+ (signal 'doom-package-error
+ (list e (straight--process-get-output))))))))
+ (if (= n 0)
+ (ignore (print! (success "No packages need to be installed")))
+ (print! (success "Installed & built %d packages") n)
+ t))))
- ((not (or auto-accept-p
- (y-or-n-p
- (format "%s packages will be installed:\n\n%s\n\nProceed?"
- (length packages)
- (mapconcat
- (lambda (pkg)
- (format "+ %s (%s)"
- (car pkg)
- (cond ((doom-package-different-recipe-p (car pkg))
- "new recipe")
- ((doom-package-different-backend-p (car pkg))
- (format "%s -> %s"
- (doom-package-backend (car pkg) 'noerror)
- (doom-package-recipe-backend (car pkg) 'noerror)))
- ((plist-get (cdr pkg) :recipe)
- "quelpa")
- ("elpa"))))
- (cl-sort (cl-copy-list packages) #'string-lessp
- :key #'car)
- "\n")))))
- (user-error "Aborted!"))
- ((let (success)
- (doom-refresh-packages-maybe doom-debug-mode)
- (dolist (pkg packages)
- (print! "Installing %s" (car pkg))
- (doom--condition-case!
- (let ((result
- (or (and (doom-package-installed-p (car pkg))
- (not (doom-package-different-backend-p (car pkg)))
- (not (doom-package-different-recipe-p (car pkg)))
- 'already-installed)
- (and (doom-install-package (car pkg) (cdr pkg))
- (setq success t)
- 'success)
- 'failure))
- (pin-label
- (and (plist-member (cdr pkg) :pin)
- (format " [pinned: %s]" (plist-get (cdr pkg) :pin)))))
- (print! "%s%s"
- (pcase result
- (`already-installed (dark (white "⚠ ALREADY INSTALLED")))
- (`success (green "✓ DONE"))
- (`failure (red "✕ FAILED")))
- (or pin-label "")))))
- (print! (bold (green "Finished!")))
- (when success
- (set-file-times doom-packages-dir)
- (doom-delete-autoloads-file doom-package-autoload-file))
- success)))))
+(defun doom-packages-rebuild (&optional auto-accept-p all)
+ "(Re)build all packages."
+ (print! (start "(Re)building %spackages...") (if all "all " ""))
+ (print-group!
+ (let ((n 0))
+ (if all
+ (let ((straight--packages-to-rebuild :all)
+ (straight--packages-not-to-rebuild (make-hash-table :test #'equal)))
+ (dolist (package (hash-table-keys straight--recipe-cache))
+ (straight-use-package
+ (intern package) nil (lambda (_) (cl-incf n) nil) " ")))
+ (let ((straight-check-for-modifications '(find-when-checking)))
+ (straight-check-all)
+ (dolist (recipe (hash-table-values straight--recipe-cache))
+ (straight--with-plist recipe (package local-repo no-build)
+ (unless (or no-build (null local-repo))
+ ;; REVIEW We do these modification checks manually because
+ ;; Straight's checks seem to miss stale elc files. Need
+ ;; more tests to confirm this.
+ (when (or (gethash package straight--cached-package-modifications)
+ (file-newer-than-file-p (straight--repos-dir local-repo)
+ (straight--build-dir package))
+ (cl-loop for file
+ in (doom-files-in (straight--build-dir package)
+ :match "\\.el$"
+ :full t)
+ for elc-file = (byte-compile-dest-file file)
+ if (and (file-exists-p elc-file)
+ (file-newer-than-file-p file elc-file))
+ return t))
+ (print! (info "Rebuilding %s") package)
+ ;; REVIEW `straight-rebuild-package' alone wasn't enough. Why?
+ (delete-directory (straight--build-dir package) 'recursive)
+ (straight-rebuild-package package)
+ (cl-incf n)))))))
+ (if (= n 0)
+ (ignore (print! (success "No packages need rebuilding")))
+ (print! (success "Rebuilt %d package(s)" n))
+ t))))
+
(defun doom-packages-update (&optional auto-accept-p)
"Updates packages.
Unless AUTO-ACCEPT-P is non-nil, this function will prompt for confirmation with
a list of packages that will be updated."
- (print! "Looking for outdated packages...")
- (let ((packages (cl-sort (cl-copy-list (doom-get-outdated-packages)) #'string-lessp
- :key #'car)))
- (cond ((not packages)
- (print! (green "Everything is up-to-date"))
- nil)
+ (print! (start "Scanning for outdated packages (this may take a while)..."))
+ (print-group!
+ ;; REVIEW Does this fail gracefully enough? Is it error tolerant?
+ ;; TODO Add version-lock checks; don't want to spend all this effort on
+ ;; packages that shouldn't be updated
+ (condition-case e
+ (let (futures)
+ (dolist (group (seq-partition (hash-table-values straight--repo-cache) 8))
+ (push (async-start
+ `(lambda ()
+ (setq load-path ',load-path
+ doom-modules ',doom-modules)
+ (load ,(concat doom-core-dir "core.el"))
+ (let (packages)
+ (when (require 'straight nil t)
+ (dolist (recipe ',group)
+ (straight--with-plist recipe (package local-repo)
+ (when (and local-repo (straight--repository-is-available-p recipe))
+ (straight-fetch-package package)
+ ;; REVIEW Isn't there a better way to get this information? Maybe with `vc'?
+ (let* ((default-directory (straight--repos-dir local-repo))
+ (n (string-to-number
+ (shell-command-to-string "git rev-list --right-only --count HEAD..@'{u}'")))
+ (pretime
+ (string-to-number
+ (shell-command-to-string "git log -1 --format=%at HEAD")))
+ (time
+ (string-to-number
+ (shell-command-to-string "git log -1 --format=%at FETCH_HEAD"))))
+ (when (> n 0)
+ (push (list n pretime time recipe)
+ packages)))))))
+ (nreverse packages))))
+ futures))
+ (let ((total (length futures))
+ (futures (nreverse futures))
+ (specs '(t)))
+ (while futures
+ (while (not (async-ready (car futures)))
+ (sleep-for 2)
+ (print! "."))
+ (nconc specs (async-get (pop futures))))
+ (terpri)
+ (if-let (specs (delq nil (cdr specs)))
+ (if (or auto-accept-p
+ (y-or-n-p
+ (format! "%s\n\nThere %s %d package%s available to update. Update them?"
+ (mapconcat
+ (lambda (spec)
+ (cl-destructuring-bind (n pretime time recipe) spec
+ (straight--with-plist recipe (package)
+ (format! "+ %-33s %s commit(s) behind %s -> %s"
+ (yellow package) (yellow n)
+ (format-time-string "%Y%m%d" pretime)
+ (format-time-string "%Y%m%d" time)))))
+ specs
+ "\n")
+ (if (cdr specs) "are" "is")
+ (length specs)
+ (if (cdr specs) "s" ""))))
+ (terpri)
+ (dolist (spec specs t)
+ (cl-destructuring-bind (n pretime time recipe) spec
+ (straight--with-plist recipe (local-repo package)
+ (let ((default-directory (straight--repos-dir local-repo)))
+ (print! (start "Updating %S") package)
+ ;; HACK `straight' doesn't assume it would ever be used
+ ;; non-interactively, but here we are. If the repo is
+ ;; dirty, the command will lock up, waiting for
+ ;; interaction that will never come, so discard all local
+ ;; changes. Doom doesn't want you modifying those anyway.
+ (and (straight--get-call "git" "reset" "--hard")
+ (straight--get-call "git" "clean" "-ffd"))
+ (straight-merge-package package)
+ ;; HACK `straight-rebuild-package' doesn't pick up that
+ ;; this package has changed, so we do it manually. Is
+ ;; there a better way?
+ (run-hook-with-args 'straight-use-package-pre-build-functions package)
+ (straight--build-package recipe " "))
+ (with-current-buffer (straight--process-get-buffer)
+ (with-silent-modifications
+ (erase-buffer))))))
+ (print! (info "Aborted update"))
+ nil)
+ (print! (success "No packages to update"))
+ nil)))
+ (error
+ (message "Output:\n%s" (straight--process-get-output))
+ (signal (car e) (error-message-string e))))))
- ((not (or auto-accept-p
- (y-or-n-p
- (format "%s packages will be updated:\n\n%s\n\nProceed?"
- (length packages)
- (let ((max-len
- (or (car (sort (mapcar (lambda (it) (length (symbol-name (car it)))) packages)
- #'>))
- 10)))
- (mapconcat
- (lambda (pkg)
- (format (format "+ %%-%ds (%%s) %%-%ds -> %%s"
- (+ max-len 2) 14)
- (symbol-name (car pkg))
- (doom-package-backend (car pkg))
- (package-version-join (cadr pkg))
- (package-version-join (cl-caddr pkg))))
- packages
- "\n"))))))
- (user-error "Aborted!"))
- ((let (success)
- (dolist (pkg packages)
- (print! "Updating %s" (car pkg))
- (doom--condition-case!
- (print!
- (let ((result (doom-update-package (car pkg) t)))
- (when result (setq success t))
- (color (if result 'green 'red)
- (if result "✓ DONE" "✕ FAILED"))))))
- (print! (bold (green "Finished!")))
- (when success
- (set-file-times doom-packages-dir)
- (doom-delete-autoloads-file doom-package-autoload-file))
- success)))))
+(defun doom--packages-to-purge ()
+ (let (builds repos)
+ (dolist (name (straight--directory-files (straight--repos-dir)))
+ (unless (straight--checkhash name straight--repo-cache)
+ (push name repos)))
+ (dolist (name (straight--directory-files (straight--build-dir)))
+ (unless (gethash name straight--profile-cache)
+ (push name builds)))
+ (straight-prune-build-cache)
+ (list builds repos)))
-(defun doom-packages-autoremove (&optional auto-accept-p)
- "Auto-removes orphaned packages.
+(defun doom-packages-purge (&optional auto-accept-p)
+ "Auto-removes orphaned packages and repos.
An orphaned package is a package that isn't a primary package (i.e. doesn't have
a `package!' declaration) or isn't depended on by another primary package.
Unless AUTO-ACCEPT-P is non-nil, this function will prompt for confirmation with
a list of packages that will be removed."
- (print! "Looking for orphaned packages...")
- (let ((packages (doom-get-orphaned-packages)))
- (cond ((not packages)
- (print! (green "No unused packages to remove"))
- nil)
-
- ((not
- (or auto-accept-p
- (y-or-n-p
- (format "%s packages will be deleted:\n\n%s\n\nProceed?"
- (length packages)
- (mapconcat
- (lambda (sym)
- (let ((old-backend (doom-package-backend sym 'noerror))
- (new-backend (doom-package-recipe-backend sym 'noerror)))
- (format "+ %s (%s)" sym
- (cond ((null new-backend)
- "removed")
- ((eq old-backend new-backend)
- (symbol-name new-backend))
- ((format "%s -> %s" old-backend new-backend))))))
- (sort (cl-copy-list packages) #'string-lessp)
- "\n")))))
- (user-error "Aborted!"))
-
- ((let (success)
- (dolist (pkg packages)
- (doom--condition-case!
- (let ((result (doom-delete-package pkg t)))
- (if result (setq success t))
- (print! (color (if result 'green 'red) "%s %s")
- (if result "✓ Removed" "✕ Failed to remove")
- pkg))))
- (print! (bold (green "Finished!")))
- (when success
- (set-file-times doom-packages-dir)
- (doom-delete-autoloads-file doom-package-autoload-file))
- success)))))
+ (print! (start "Searching for orphaned packages..."))
+ (cl-destructuring-bind (builds repos) (doom--packages-to-purge)
+ (unless (bound-and-true-p package--initialized)
+ (package-initialize))
+ (print-group!
+ (let ((packages (append builds (mapcar #'car package-alist) nil)))
+ (if (not packages)
+ (ignore (print! (success "No orphaned packages to purge")))
+ (or auto-accept-p
+ (y-or-n-p
+ (format! "\n%s\n\n%d packages are orphaned. Purge them (for the Emperor)?"
+ (mapconcat (lambda (pkgs)
+ (mapconcat (lambda (p) (format " + %-20.20s" p))
+ pkgs
+ ""))
+ (seq-partition (cl-sort (copy-sequence packages) #'string-lessp)
+ 3)
+ "\n")
+ (length packages)))
+ (user-error "Aborted"))
+ (let ((n 0))
+ (dolist (dir (append (mapcar #'straight--repos-dir repos)
+ (mapcar #'straight--build-dir builds)))
+ (print! (info "Deleting %S") (relpath dir (straight--dir)))
+ (delete-directory dir 'recursive)
+ (unless (file-directory-p dir)
+ (cl-incf n)))
+ (straight-prune-build-cache)
+ (when (file-directory-p package-user-dir)
+ (delete-directory package-user-dir t)
+ t)
+ (> n 0)))))))
diff --git a/core/cli/patch-macos.el b/core/cli/patch-macos.el
index 8e8bdfb3d..99dff3f75 100644
--- a/core/cli/patch-macos.el
+++ b/core/cli/patch-macos.el
@@ -1,9 +1,6 @@
;;; core/cli/patch-macos.el -*- lexical-binding: t; -*-
-(dispatcher! (patch-macos)
- (doom-patch-macos (or (member "--undo" args)
- (member "-u" args))
- (doom--find-emacsapp-path))
+(def-command! patch-macos ()
"Patches Emacs.app to respect your shell environment.
WARNING: This command is deprecated. Use 'doom env' instead.
@@ -30,7 +27,11 @@ It can be undone with the --undo or -u options.
Alternatively, you can install the exec-path-from-shell Emacs plugin, which will
scrape your shell environment remotely, at startup. However, this can be slow
-depending on your shell configuration and isn't always reliable.")
+depending on your shell configuration and isn't always reliable."
+ :hidden t
+ (doom-patch-macos (or (member "--undo" args)
+ (member "-u" args))
+ (doom--find-emacsapp-path)))
;;
diff --git a/core/cli/quickstart.el b/core/cli/quickstart.el
index 1e061653a..740371644 100644
--- a/core/cli/quickstart.el
+++ b/core/cli/quickstart.el
@@ -1,7 +1,12 @@
;;; core/cli/quickstart.el -*- lexical-binding: t; -*-
-(dispatcher! (quickstart qs) (apply #'doom-quickstart args)
- "Guides you through setting up Doom for first time use.
+(def-command! quickstart (&rest args) ; DEPRECATED
+ ""
+ :hidden t
+ (apply #'doom-cli-install args))
+
+(def-command! (install i) ()
+ "A wizard for installing Doom for the first time.
This command does the following:
@@ -17,89 +22,80 @@ This command is idempotent and safe to reuse.
The location of DOOMDIR can be changed with the -p option, or by setting the
DOOMDIR environment variable. e.g.
- doom -p ~/.config/doom quickstart
- DOOMDIR=~/.config/doom doom quickstart
+ doom -p ~/.config/doom install
+ DOOMDIR=~/.config/doom doom install
-Quickstart understands the following switches:
+install understands the following switches:
--no-config Don't create DOOMDIR or dummy files therein
--no-install Don't auto-install packages
--no-env Don't generate an envvars file (see `doom help env`)
- --no-fonts Don't install (or prompt to install) all-the-icons fonts")
-
-
-;;
-;; Library
-
-(defun doom-quickstart (&rest args)
- "Quickly deploy a private module and setup Doom.
-
-This deploys a barebones config to `doom-private-dir', installs all missing
-packages, prompts to install all-the-icons fonts, generates an env file and
-regenerates the autoloads file."
- ;; Create `doom-private-dir'
- (let ((short-private-dir (abbreviate-file-name doom-private-dir)))
+ --no-fonts Don't install (or prompt to install) all-the-icons fonts"
+ (print! (green "Installing Doom Emacs!\n"))
+ (let ((default-directory (doom-dir "~")))
+ ;; Create `doom-private-dir'
(if (member "--no-config" args)
- (print! (yellow "Not copying private config template, as requested"))
- (print! "Creating %s" short-private-dir)
- (make-directory doom-private-dir t)
- (print! (green "Done!"))
+ (print! (warn "Not copying private config template, as requested"))
+ (print! "> Creating %s" (relpath doom-private-dir))
+ (make-directory doom-private-dir 'parents)
+ (print! (success "Created %s") (relpath doom-private-dir))
;; Create init.el, config.el & packages.el
- (dolist (file (list (cons "init.el"
- (lambda ()
- (insert-file-contents (expand-file-name "init.example.el" doom-emacs-dir))))
- (cons "config.el"
- (lambda ()
- (insert (format ";;; %sconfig.el -*- lexical-binding: t; -*-\n\n"
- short-private-dir)
- ";; Place your private configuration here\n")))
- (cons "packages.el"
- (lambda ()
- (insert (format ";; -*- no-byte-compile: t; -*-\n;;; %spackages.el\n\n"
- short-private-dir)
- ";;; Examples:\n"
- ";; (package! some-package)\n"
- ";; (package! another-package :recipe (:fetcher github :repo \"username/repo\"))\n"
- ";; (package! builtin-package :disable t)\n")))))
- (cl-destructuring-bind (path . fn) file
- (if (file-exists-p! path doom-private-dir)
- (print! "%s already exists, skipping" path)
- (print! "Creating %s%s" short-private-dir path)
- (with-temp-file (expand-file-name path doom-private-dir)
- (funcall fn))
- (print! (green "Done!")))))))
+ (mapc (lambda (file)
+ (cl-destructuring-bind (filename . fn) file
+ (if (file-exists-p! filename doom-private-dir)
+ (print! (warn "%s already exists, skipping") filename)
+ (print! (info "Creating %s%s") (relpath doom-private-dir) filename)
+ (with-temp-file (doom-path doom-private-dir filename)
+ (funcall fn))
+ (print! (success "Done!")))))
+ '(("init.el" .
+ (lambda ()
+ (insert-file-contents (doom-dir "init.example.el"))))
+ ("config.el" .
+ (lambda ()
+ (insert! ";;; %sconfig.el -*- lexical-binding: t; -*-\n\n"
+ ";; Place your private configuration here\n"
+ ((relpath doom-private-dir)))))
+ ("packages.el" .
+ (lambda ()
+ (insert! ";; -*- no-byte-compile: t; -*-\n;;; %spackages.el\n\n"
+ ";;; Examples:\n"
+ ";; (package! some-package)\n"
+ ";; (package! another-package :recipe (:fetcher github :repo \"username/repo\"))\n"
+ ";; (package! builtin-package :disable t)\n"
+ ((relpath doom-private-dir))))))))
- ;; In case no init.el was present the first time `doom-initialize-modules' was
- ;; called in core.el (e.g. on first install)
- (doom-initialize-packages 'force-p)
+ ;; In case no init.el was present the first time `doom-initialize-modules' was
+ ;; called in core.el (e.g. on first install)
+ (doom-initialize-packages 'force-p)
- ;; Ask if Emacs.app should be patched
- (if (member "--no-env" args)
- (print! (yellow "Not generating envvars file, as requested"))
- (when (or doom-auto-accept
- (y-or-n-p "Generate an env file? (see `doom help env` for details)"))
- (doom-reload-env-file 'force-p)))
+ ;; Ask if Emacs.app should be patched
+ (if (member "--no-env" args)
+ (print! (warn "- Not generating envvars file, as requested"))
+ (when (or doom-auto-accept
+ (y-or-n-p "Generate an env file? (see `doom help env` for details)"))
+ (doom-reload-env-file 'force-p)))
- ;; Install Doom packages
- (if (member "--no-install" args)
- (print! (yellow "Not installing plugins, as requested"))
- (print! "Installing plugins")
- (doom-packages-install doom-auto-accept))
+ ;; Install Doom packages
+ (if (member "--no-install" args)
+ (print! (warn "- Not installing plugins, as requested"))
+ (print! "Installing plugins")
+ (doom-packages-install doom-auto-accept))
- (print! "Regenerating autoloads files")
- (doom-reload-autoloads nil 'force-p)
+ (print! "Regenerating autoloads files")
+ (doom-reload-autoloads nil 'force-p)
- (if (member "--no-fonts" args)
- (print! (yellow "Not installing fonts, as requested"))
- (when (or doom-auto-accept
- (y-or-n-p "Download and install all-the-icon's fonts?"))
- (require 'all-the-icons)
- (let ((window-system (cond (IS-MAC 'ns)
- (IS-LINUX 'x))))
- (all-the-icons-install-fonts 'yes))))
+ (if (member "--no-fonts" args)
+ (print! (warn "- Not installing fonts, as requested"))
+ (when (or doom-auto-accept
+ (y-or-n-p "Download and install all-the-icon's fonts?"))
+ (require 'all-the-icons)
+ (let ((window-system (cond (IS-MAC 'ns)
+ (IS-LINUX 'x))))
+ (all-the-icons-install-fonts 'yes))))
- (print! (bold (green "\nFinished! Doom is ready to go!\n")))
- (with-temp-buffer
- (doom-template-insert "QUICKSTART_INTRO")
- (print! (buffer-string))))
+ (print! (success "\nFinished! Doom is ready to go!\n"))
+ (with-temp-buffer
+ (doom-template-insert "QUICKSTART_INTRO")
+ (print! (buffer-string)))))
diff --git a/core/cli/test.el b/core/cli/test.el
index f67953fbc..eb0e79709 100644
--- a/core/cli/test.el
+++ b/core/cli/test.el
@@ -1,67 +1,73 @@
;;; core/cli/test.el -*- lexical-binding: t; -*-
-(dispatcher! test (doom-run-tests args)
+(def-command! test ()
"Run Doom unit tests.")
;;
-;; Library
+;;; Library
-(defun doom-run-tests (&optional modules)
- "Run all loaded tests, specified by MODULES (a list of module cons cells) or
-command line args following a double dash (each arg should be in the
-'module/submodule' format).
+(defun doom-run-tests ()
+ "Discover and load test files, then run all defined suites.
-If neither is available, run all tests in all enabled modules."
- ;; Core libraries aren't fully loaded in a noninteractive session, so we
- ;; reload it with `noninteractive' set to nil to force them to.
- (let* ((noninteractive t)
- (doom-modules (doom-modules)))
- (quiet! (doom-reload-autoloads))
- (let ((target-paths
- ;; Convert targets into a list of string paths, pointing to the root
- ;; directory of modules
- (cond ((stringp (car modules)) ; command line
- (save-match-data
- (cl-loop for arg in modules
- if (string= arg ":core") collect doom-core-dir
- else if (string-match-p "/" arg)
- nconc (mapcar (apply-partially #'expand-file-name arg)
- doom-modules-dirs)
- else
- nconc (cl-loop for dir in doom-modules-dirs
- for path = (expand-file-name arg dir)
- if (file-directory-p path)
- nconc (doom-files-in path :type 'dirs :depth 1 :full t :sort nil))
- finally do (setq argv nil))))
-
- (modules ; cons-cells given to MODULES
- (cl-loop for (module . submodule) in modules
- if (doom-module-locate-path module submodule)
- collect it))
-
- ((append (list doom-core-dir)
- (doom-module-load-path))))))
- ;; Load all the unit test files...
- (require 'buttercup)
- (mapc (lambda (file) (load file :noerror (not doom-debug-mode)))
- (doom-files-in (mapcar (apply-partially #'expand-file-name "test/")
- target-paths)
- :match "\\.el$" :full t))
- ;; ... then run them
- (when doom-debug-mode
- (setq buttercup-stack-frame-style 'pretty))
- (let ((split-width-threshold 0)
- (split-height-threshold 0)
- (window-min-width 0)
- (window-min-height 0))
- (buttercup-run)))))
+Takes directories as command line arguments, defaulting to the
+current directory."
+ (let ((dirs nil)
+ (patterns nil)
+ (args command-line-args-left)
+ (doom-modules (doom-modules)))
+ (doom-initialize-autoloads doom-autoload-file)
+ (doom-initialize-autoloads doom-package-autoload-file)
+ (while args
+ (cond
+ ;; ((member (car args) '("--traceback"))
+ ;; (when (not (cdr args))
+ ;; (error "Option requires argument: %s" (car args)))
+ ;; ;; Make sure it's a valid style by trying to format a dummy
+ ;; ;; frame with it
+ ;; (buttercup--format-stack-frame '(t myfun 1 2) (intern (cadr args)))
+ ;; (setq buttercup-stack-frame-style (intern (cadr args)))
+ ;; (setq args (cddr args)))
+ ;; ((member (car args) '("-p" "--pattern"))
+ ;; (when (not (cdr args))
+ ;; (error "Option requires argument: %s" (car args)))
+ ;; (push (cadr args) patterns)
+ ;; (setq args (cddr args)))
+ ;; ((member (car args) '("-c" "--no-color"))
+ ;; (setq buttercup-color nil)
+ ;; (setq args (cdr args)))
+ (t
+ (push (car args) dirs)
+ (setq args (cdr args)))))
+ (setq command-line-args-left nil)
+ (dolist (dir (or dirs '(".")))
+ (setq dir (if (string= dir "core")
+ doom-core-dir
+ (expand-file-name dir doom-modules-dir)))
+ (let ((test-dir (expand-file-name "test" dir)))
+ (when (or (string= dir doom-core-dir)
+ (cl-destructuring-bind (category . module)
+ (or (doom-module-from-path dir)
+ (cons nil nil))
+ (and category module (doom-module-p category module))))
+ (dolist (file (nreverse (doom-glob test-dir "test-*.el")))
+ (when (doom-file-cookie-p file)
+ (load file nil t))))))
+ (when patterns
+ (dolist (spec (buttercup--specs buttercup-suites))
+ (let ((spec-full-name (buttercup-spec-full-name spec)))
+ (unless (cl-dolist (p patterns)
+ (when (string-match p spec-full-name)
+ (cl-return t)))
+ (setf (buttercup-spec-function spec)
+ (lambda () (signal 'buttercup-pending "SKIPPED")))))))
+ (buttercup-run)))
;;
;; Test library
-(defmacro insert! (&rest text)
+(defmacro insert!! (&rest text)
"Insert TEXT in buffer, then move cursor to last {0} marker."
`(progn
(insert ,@text)
diff --git a/core/cli/upgrade.el b/core/cli/upgrade.el
index b7664ceb5..7f42d532d 100644
--- a/core/cli/upgrade.el
+++ b/core/cli/upgrade.el
@@ -1,7 +1,7 @@
;;; core/cli/upgrade.el -*- lexical-binding: t; -*-
-(dispatcher! (upgrade up) (doom-upgrade)
- "Checks out the latest Doom on this branch.
+(def-command! (upgrade up) ()
+ "Updates Doom and packages.
Doing so is equivalent to:
@@ -9,7 +9,8 @@ Doing so is equivalent to:
git pull
bin/doom clean
bin/doom refresh
- bin/doom update")
+ bin/doom update"
+ (doom-upgrade))
;;
@@ -75,7 +76,7 @@ Doing so is equivalent to:
(buffer-string)))
(unless (equal (vc-git-working-revision doom-emacs-dir) rev)
(error "Failed to checkout latest commit.\n\n%s" (buffer-string))))
- (doom-refresh 'force-p)
+ (doom-cli-refresh 'force-p)
(when (doom-packages-update doom-auto-accept)
(doom-reload-package-autoloads))
(message "Done! Please restart Emacs for changes to take effect")))
diff --git a/core/core-cli.el b/core/core-cli.el
index 7ca20ba45..dc12704a4 100644
--- a/core/core-cli.el
+++ b/core/core-cli.el
@@ -1,54 +1,82 @@
;;; -*- lexical-binding: t; no-byte-compile: t; -*-
-;; Eagerly load these libraries because this module may be loaded in a session
-;; that hasn't been fully initialized (where autoloads files haven't been
-;; generated or `load-path' populated).
-(load! "autoload/debug")
-(load! "autoload/files")
-(load! "autoload/message")
-(load! "autoload/packages")
-
-
-;;
-;; Dispatcher API
-
(defvar doom-auto-accept (getenv "YES")
"If non-nil, Doom will auto-accept any confirmation prompts during batch
commands like `doom-packages-install', `doom-packages-update' and
`doom-packages-autoremove'.")
-(defconst doom--dispatch-command-alist ())
-(defconst doom--dispatch-alias-alist ())
+(defvar doom-cli-pre-execute-hook nil
+ "TODO")
+(defvar doom-cli-post-execute-hook nil
+ "TODO")
+
+(defvar doom--cli-commands (make-hash-table :test 'equal))
+(defvar doom--cli-groups (make-hash-table :test 'equal))
+(defvar doom--cli-group nil)
+
+
+;;
+;;; Dispatcher API
+
+(defun doom-file-cookie-p (file)
+ (with-temp-buffer
+ (insert-file-contents-literally file nil 0 256)
+ (if (and (re-search-forward "^;;;###if " nil t)
+ (<= (line-number-at-pos) 3))
+ (let ((load-file-name file))
+ (eval (sexp-at-point) t))
+ t)))
+
+(defun doom--dispatch-command (command)
+ (when (symbolp command)
+ (setq command (symbol-name command)))
+ (cl-check-type command string)
+ (intern-soft
+ (format "doom-cli-%s"
+ (if (gethash command doom--cli-commands)
+ command
+ (cl-loop for key
+ being the hash-keys in doom--cli-commands
+ for aliases = (plist-get (gethash key doom--cli-commands) :aliases)
+ if (member command aliases)
+ return key)))))
(defun doom--dispatch-format (desc &optional short)
(with-temp-buffer
(let ((fill-column 72))
- (insert desc)
- (goto-char (point-min))
- (while (re-search-forward "\n\n[^ \n]" nil t)
- (fill-paragraph)))
+ (save-excursion
+ (insert desc)
+ (while (re-search-backward "\n\n[^ \n]" nil t)
+ (fill-paragraph))))
(if (not short)
(buffer-string)
- (goto-char (point-min))
- (buffer-substring-no-properties
- (line-beginning-position)
- (line-end-position)))))
+ (buffer-substring (line-beginning-position)
+ (line-end-position)))))
-(defun doom--dispatch-help (&optional command desc &rest args)
- "Display help documentation for a dispatcher command. If COMMAND and DESC are
+(defun doom--dispatch-help-1 (command)
+ (cl-destructuring-bind (&key aliases _group)
+ (gethash command doom--cli-commands)
+ (print! "%-11s\t%s\t%s"
+ command (if aliases (string-join aliases ",") "")
+ (doom--dispatch-format
+ (documentation (doom--dispatch-command command))
+ t))))
+
+(defun doom--dispatch-help (&optional fn &rest args)
+ "Display help documentation for a dispatcher command. If fn and DESC are
omitted, show all available commands, their aliases and brief descriptions."
- (if command
- (princ (doom--dispatch-format desc))
- (print! (bold "%-10s\t%s\t%s" "Command:" "Alias" "Description"))
- (dolist (spec (cl-sort doom--dispatch-command-alist #'string-lessp
- :key #'car))
- (cl-destructuring-bind (command &key desc _body) spec
- (let ((aliases (cl-loop for (alias . cmd) in doom--dispatch-alias-alist
- if (eq cmd command)
- collect (symbol-name alias))))
- (print! " %-10s\t%s\t%s"
- command (if aliases (string-join aliases ",") "")
- (doom--dispatch-format desc t)))))))
+ (if fn
+ (princ (documentation fn))
+ (print! (bold "%-11s\t%s\t%s" "Command:" "Alias" "Description"))
+ (print-group!
+ (dolist (group (seq-group-by (lambda (key) (plist-get (gethash key doom--cli-commands) :group))
+ (hash-table-keys doom--cli-commands)))
+ (if (null (car group))
+ (mapc #'doom--dispatch-help-1 (cdr group))
+ (print! "%-30s\t%s" (bold (car group)) (gethash (car group) doom--cli-groups))
+ (print-group!
+ (mapc #'doom--dispatch-help-1 (cdr group))))
+ (terpri)))))
(defun doom-dispatch (cmd args &optional show-help)
"Parses ARGS and invokes a dispatcher.
@@ -59,17 +87,31 @@ If SHOW-HELP is non-nil, show the documentation for said dispatcher."
(when args
(setq cmd (car args)
args (cdr args))))
- (cl-destructuring-bind (command &key desc body)
- (let ((sym (intern cmd)))
- (or (assq sym doom--dispatch-command-alist)
- (assq (cdr (assq sym doom--dispatch-alias-alist))
- doom--dispatch-command-alist)
- (user-error "Invalid command: %s" sym)))
+ (let ((fn (doom--dispatch-command cmd)))
+ (unless (fboundp fn)
+ (user-error "%s is not any command *I* know!" fn))
(if show-help
- (apply #'doom--dispatch-help command desc args)
- (funcall body args))))
+ (doom--dispatch-help fn args)
+ (let ((start-time (current-time)))
+ (run-hooks 'doom-cli-pre-execute-hook)
+ (when-let (ret (apply fn args))
+ (print!
+ "\n%s"
+ (success "Finished! (%.4fs)"
+ (float-time
+ (time-subtract (current-time)
+ start-time))))
+ (run-hooks 'doom-cli-post-execute-hook)
+ ret)))))
-(defmacro dispatcher! (command form &optional docstring)
+(defmacro def-command-group! (name docstring &rest body)
+ "TODO"
+ (declare (indent defun) (doc-string 2))
+ `(let ((doom--cli-group ,name))
+ (puthash doom--cli-group ,docstring doom--cli-groups)
+ ,@body))
+
+(defmacro def-command! (names arglist docstring &rest body)
"Define a dispatcher command. COMMAND is a symbol or a list of symbols
representing the aliases for this command. DESC is a string description. The
first line should be short (under 60 letters), as it will be displayed for
@@ -77,79 +119,38 @@ bin/doom help.
BODY will be run when this dispatcher is called."
(declare (indent defun) (doc-string 3))
- (cl-destructuring-bind (cmd &rest aliases)
- (doom-enlist command)
+ (let* ((names (mapcar #'symbol-name (doom-enlist names)))
+ (fn (intern (format "doom-cli-%s" (car names))))
+ (plist (cl-loop while (keywordp (car body))
+ collect (pop body)
+ collect (pop body))))
(macroexp-progn
- (append
- (when aliases
- `((dolist (alias ',aliases)
- (setf (alist-get alias doom--dispatch-alias-alist) ',cmd))))
- `((setf (alist-get ',cmd doom--dispatch-command-alist)
- (list :desc ,docstring
- :body (lambda (args) (ignore args) ,form))))))))
+ (reverse
+ `((unless ,(plist-get plist :hidden)
+ (let ((plist ',plist))
+ (setq plist (plist-put plist :aliases ',(cdr names)))
+ (unless (or (plist-member plist :group)
+ (null doom--cli-group))
+ (plist-put plist :group doom--cli-group))
+ (puthash ,(car names) plist doom--cli-commands)))
+ (defun ,fn ,arglist
+ ,docstring
+ ,@body))))))
;;
-;; Dummy dispatch commands
+;;; Dispatch commands
-;; These are handled by bin/doom, except we still want 'help CMD' to print out
-;; documentation for them, so...
-
-(dispatcher! run :noop
- "Run Doom Emacs from bin/doom's parent directory.
-
-All arguments are passed on to Emacs (except for -p and -e).
-
- doom run
- doom run -nw init.el
-
-WARNING: this command exists for convenience and testing. Doom will suffer
-additional overhead by being started this way. For the best performance, it is
-best to run Doom out of ~/.emacs.d and ~/.doom.d.")
-
-(dispatcher! (doctor doc) :noop
- "Checks for issues with your environment & Doom config.
-
-Use the doctor to diagnose common problems or list missing dependencies in
-enabled modules.")
-
-(dispatcher! (help h) :noop
- "Look up additional information about a command.")
+;; Eagerly load these libraries because this module may be loaded in a session
+;; that hasn't been fully initialized (where autoloads files haven't been
+;; generated or `load-path' populated).
+(load! "autoload/format")
+(load! "autoload/packages")
-;;
-;; Real dispatch commands
-
-(load! "cli/autoloads")
-(load! "cli/byte-compile")
-(load! "cli/debug")
-(load! "cli/env")
-(load! "cli/packages")
-(load! "cli/patch-macos")
-(load! "cli/quickstart")
-(load! "cli/upgrade")
-(load! "cli/test")
-
-
-;;
-(defun doom-refresh (&optional force-p)
- "Ensure Doom is in a working state by checking autoloads and packages, and
-recompiling any changed compiled files. This is the shotgun solution to most
-problems with doom."
- (when (getenv "DOOMENV")
- (doom-reload-env-file 'force))
- (doom-reload-doom-autoloads force-p)
- (unwind-protect
- (progn
- (ignore-errors
- (doom-packages-autoremove doom-auto-accept))
- (ignore-errors
- (doom-packages-install doom-auto-accept)))
- (doom-reload-package-autoloads force-p)
- (doom-byte-compile nil 'recompile)))
-
-(dispatcher! (refresh re) (doom-refresh 'force)
- "Refresh Doom.
+;; Load all of our subcommands
+(def-command! (refresh re) (&optional force-p)
+ "Ensure Doom is properly set up.
This is the equivalent of running autoremove, install, autoloads, then
recompile. Run this whenever you:
@@ -161,7 +162,68 @@ recompile. Run this whenever you:
It will ensure that unneeded packages are removed, all needed packages are
installed, autoloads files are up-to-date and no byte-compiled files have gone
-stale.")
+stale."
+ (print! (green "Initiating a refresh of Doom Emacs...\n"))
+ (let (success)
+ (when (file-exists-p doom-env-file)
+ (doom-reload-env-file 'force))
+ (doom-reload-core-autoloads force-p)
+ (unwind-protect
+ (progn
+ (and (doom-packages-install doom-auto-accept)
+ (setq success t))
+ (and (doom-packages-rebuild doom-auto-accept)
+ (setq success t))
+ (and (doom-packages-purge doom-auto-accept)
+ (setq success t)))
+ (doom-reload-package-autoloads (or success force-p))
+ (doom-byte-compile nil 'recompile))
+ t))
+
+
+;; Load all of our subcommands
+(load! "cli/quickstart")
+
+(def-command-group! "Diagnostics"
+ "For troubleshooting and diagnostics"
+ (def-command! (doctor doc) ()
+ "Checks for issues with your environment & Doom config.
+
+Use the doctor to diagnose common problems or list missing dependencies in
+enabled modules.")
+
+ (load! "cli/debug")
+ (load! "cli/test"))
+
+(def-command-group! "Maintenance"
+ "For managing your config and packages"
+ (load! "cli/env")
+ (load! "cli/upgrade")
+ (load! "cli/packages")
+ (load! "cli/autoloads")
+ (load! "cli/patch-macos"))
+
+(def-command-group! "Byte compilation"
+ "For byte-compiling Doom and your config"
+ (load! "cli/byte-compile"))
+
+(def-command-group! "Utilities"
+ "Conveniences for interacting with Doom externally"
+ (def-command! run ()
+ "Run Doom Emacs from bin/doom's parent directory.
+
+All arguments are passed on to Emacs (except for -p and -e).
+
+ doom run
+ doom run -nw init.el
+
+WARNING: this command exists for convenience and testing. Doom will suffer
+additional overhead by being started this way. For the best performance, it is
+best to run Doom out of ~/.emacs.d and ~/.doom.d.")
+
+ ;; (load! "cli/batch")
+ ;; (load! "cli/org")
+ )
(provide 'core-cli)
;;; core-cli.el ends here
diff --git a/core/core-modules.el b/core/core-modules.el
index 2453f861e..b9c368385 100644
--- a/core/core-modules.el
+++ b/core/core-modules.el
@@ -11,9 +11,6 @@
doom-modules-dir)
"A list of module root directories. Order determines priority.")
-(defvar doom-inhibit-module-warnings (not noninteractive)
- "If non-nil, don't emit deprecated or missing module warnings at startup.")
-
(defconst doom-obsolete-modules
'((:feature (version-control (:emacs vc) (:ui vc-gutter))
(spellcheck (:tools flyspell))
@@ -50,14 +47,10 @@ syntax-checker modules obsolete. e.g. If :feature version-control is found in
your `doom!' block, a warning is emitted before replacing it with :emacs vc and
:ui vc-gutter.")
-(defvar doom--current-module nil)
-(defvar doom--current-flags nil)
-(defvar doom--modules-cache ())
+(defvar doom-inhibit-module-warnings (not noninteractive)
+ "If non-nil, don't emit deprecated or missing module warnings at startup.")
-
-;;
;;; Custom hooks
-
(defvar doom-before-init-modules-hook nil
"A list of hooks to run before Doom's modules' config.el files are loaded, but
after their init.el files are loaded.")
@@ -68,8 +61,8 @@ before the user's private module.")
(defvaralias 'doom-after-init-modules-hook 'after-init-hook)
-(define-obsolete-variable-alias 'doom-post-init-hook 'doom-init-modules-hook "2.1.0")
-(define-obsolete-variable-alias 'doom-init-hook 'doom-before-init-modules-hook "2.1.0")
+(defvar doom--current-module nil)
+(defvar doom--current-flags nil)
;;
@@ -79,27 +72,26 @@ before the user's private module.")
"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 (and (or force-p
- (not doom-init-modules-p))
- (not (setq doom-modules nil))
- (load! "init" doom-private-dir t))
- (setq doom-init-modules-p t)
- (unless (hash-table-p doom-modules)
- (setq doom-modules (make-hash-table :test 'equal)))
- (maphash (lambda (key plist)
- (let ((doom--current-module key)
- (doom--current-flags (plist-get plist :flags)))
- (load! "init" (plist-get plist :path) t)))
- doom-modules)
- (run-hook-wrapped 'doom-before-init-modules-hook #'doom-try-run-hook)
- (unless noninteractive
- (maphash (lambda (key plist)
- (let ((doom--current-module key)
- (doom--current-flags (plist-get plist :flags)))
- (load! "config" (plist-get plist :path) t)))
- doom-modules)
- (run-hook-wrapped 'doom-init-modules-hook #'doom-try-run-hook)
- (load! "config" doom-private-dir t))))
+ (when (or force-p (not doom-init-modules-p))
+ (setq doom-init-modules-p t
+ doom-modules nil)
+ (when (load! "init" doom-private-dir t)
+ (when doom-modules
+ (maphash (lambda (key plist)
+ (let ((doom--current-module key)
+ (doom--current-flags (plist-get plist :flags)))
+ (load! "init" (plist-get plist :path) t)))
+ doom-modules))
+ (run-hook-wrapped 'doom-before-init-modules-hook #'doom-try-run-hook)
+ (unless noninteractive
+ (when doom-modules
+ (maphash (lambda (key plist)
+ (let ((doom--current-module key)
+ (doom--current-flags (plist-get plist :flags)))
+ (load! "config" (plist-get plist :path) t)))
+ doom-modules))
+ (run-hook-wrapped 'doom-init-modules-hook #'doom-try-run-hook)
+ (load! "config" doom-private-dir t)))))
;;
@@ -108,12 +100,11 @@ non-nil."
(defun doom-module-p (category module &optional flag)
"Returns t if CATEGORY MODULE is enabled (ie. present in `doom-modules')."
(declare (pure t) (side-effect-free t))
- (when (hash-table-p doom-modules)
- (let ((plist (gethash (cons category module) doom-modules)))
- (and plist
- (or (null flag)
- (memq flag (plist-get plist :flags)))
- t))))
+ (let ((plist (gethash (cons category module) doom-modules)))
+ (and plist
+ (or (null flag)
+ (memq flag (plist-get plist :flags)))
+ t)))
(defun doom-module-get (category module &optional property)
"Returns the plist for CATEGORY MODULE. Gets PROPERTY, specifically, if set."
@@ -128,7 +119,7 @@ non-nil."
of PROPERTY and VALUEs.
\(fn CATEGORY MODULE PROPERTY VALUE &rest [PROPERTY VALUE [...]])"
- (if-let* ((old-plist (doom-module-get category module)))
+ (if-let (old-plist (doom-module-get category module))
(progn
(when plist
(when (cl-oddp (length plist))
@@ -159,9 +150,10 @@ 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))
- file-name-handler-alist)
- (if file (expand-file-name file path)
+ (let ((path (doom-module-get category module :path)))
+ (if file
+ (let (file-name-handler-alist)
+ (expand-file-name file path))
path)))
(defun doom-module-locate-path (category &optional module file)
@@ -183,37 +175,42 @@ This doesn't require modules to be enabled. For enabled modules us
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)."
+(defun doom-module-from-path (&optional path enabled-only)
+ "Returns a cons cell (CATEGORY . MODULE) derived from PATH (a file path).
+If ENABLED-ONLY, return nil if the containing module isn't enabled."
(or doom--current-module
- (let* (file-name-handler-alist
- (path (or path (FILE!))))
+ (let* ((file-name-handler-alist nil)
+ (path (file-truename (or path (file!)))))
(save-match-data
- (setq path (file-truename path))
(when (string-match "/modules/\\([^/]+\\)/\\([^/]+\\)\\(?:/.*\\)?$" path)
- (when-let* ((category (match-string 1 path))
- (module (match-string 2 path)))
- (cons (doom-keyword-intern category)
- (intern module))))))))
+ (when-let* ((category (doom-keyword-intern (match-string 1 path)))
+ (module (intern (match-string 2 path))))
+ (and (or (null enabled-only)
+ (doom-module-p category module))
+ (cons category module))))))))
-(defun doom-module-load-path (&optional all-p)
- "Return an unsorted list of absolute file paths to activated modules.
+(defun doom-module-load-path (&optional module-dirs)
+ "Return a list of file paths to activated modules.
-If ALL-P is non-nil, return paths of possible modules, activated or otherwise."
+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
+those directories."
(declare (pure t) (side-effect-free t))
- (append (if all-p
- (doom-files-in doom-modules-dirs
+ (append (list doom-private-dir)
+ (if module-dirs
+ (doom-files-in (if (listp module-dirs)
+ module-dirs
+ doom-modules-dirs)
:type 'dirs
:mindepth 1
- :depth 1
- :full t
- :sort nil)
+ :depth 1)
(cl-loop for plist being the hash-values of (doom-modules)
collect (plist-get plist :path)))
- (list doom-private-dir)))
+ nil))
(defun doom-modules (&optional refresh-p)
- "Minimally initialize `doom-modules' (a hash table) and return it."
+ "Minimally initialize `doom-modules' (a hash table) and return it.
+This value is cached. If REFRESH-P, then don't use the cached value."
(or (unless refresh-p doom-modules)
(let ((noninteractive t)
doom-modules
@@ -343,45 +340,43 @@ The overall load order of Doom is as follows:
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)."
- (if doom--modules-cache
- (progn
- (setq doom-modules doom--modules-cache)
- (doom-log "Using `doom-modules' cache"))
- (unless doom-modules
- (setq doom-modules
- (make-hash-table :test 'equal
- :size (if modules (length modules) 150)
- :rehash-threshold 1.0)))
- (let ((inhibit-message doom-inhibit-module-warnings)
- category m)
- (while modules
- (setq m (pop modules))
- (cond ((keywordp m) (setq category m))
- ((not category) (error "No module category specified for %s" m))
- ((catch 'doom-modules
- (let* ((module (if (listp m) (car m) m))
- (flags (if (listp m) (cdr m))))
- (when-let* ((obsolete (assq category doom-obsolete-modules))
- (new (assq module obsolete)))
- (let ((newkeys (cdr new)))
- (if (null newkeys)
- (message "WARNING %s module was removed" key)
- (if (cdr newkeys)
- (message "WARNING %s module was removed and split into the %s modules"
- (list category module) (mapconcat #'prin1-to-string newkeys ", "))
- (message "WARNING %s module was moved to %s"
- (list category module) (car newkeys)))
- (push category modules)
- (dolist (key newkeys)
- (push (if flags
- (nconc (cdr key) flags)
- (cdr key))
- modules)
- (push (car key) modules))
- (throw 'doom-modules t))))
- (if-let (path (doom-module-locate-path category module))
- (doom-module-set category module :flags flags :path path)
- (message "WARNING Couldn't find the %s %s module" category module)))))))))
+ (unless (keywordp (car modules))
+ (setq modules (eval modules t)))
+ (unless doom-modules
+ (setq doom-modules
+ (make-hash-table :test 'equal
+ :size (if modules (length modules) 150)
+ :rehash-threshold 1.0)))
+ (let ((inhibit-message doom-inhibit-module-warnings)
+ category m)
+ (while modules
+ (setq m (pop modules))
+ (cond ((keywordp m) (setq category m))
+ ((not category) (error "No module category specified for %s" m))
+ ((catch 'doom-modules
+ (let* ((module (if (listp m) (car m) m))
+ (flags (if (listp m) (cdr m))))
+ (when-let* ((obsolete (assq category doom-obsolete-modules))
+ (new (assq module obsolete)))
+ (let ((newkeys (cdr new)))
+ (if (null newkeys)
+ (message "WARNING %s module was removed" key)
+ (if (cdr newkeys)
+ (message "WARNING %s module was removed and split into the %s modules"
+ (list category module) (mapconcat #'prin1-to-string newkeys ", "))
+ (message "WARNING %s module was moved to %s"
+ (list category module) (car newkeys)))
+ (push category modules)
+ (dolist (key newkeys)
+ (push (if flags
+ (nconc (cdr key) flags)
+ (cdr key))
+ modules)
+ (push (car key) modules))
+ (throw 'doom-modules t))))
+ (if-let (path (doom-module-locate-path category module))
+ (doom-module-set category module :flags flags :path path)
+ (message "WARNING Couldn't find the %s %s module" category module))))))))
(when noninteractive
(setq doom-inhibit-module-warnings t))
`(setq doom-modules ',doom-modules))
@@ -505,9 +500,9 @@ CATEGORY and MODULE can be omitted When this macro is used from inside a module
(doom--current-flags (memq category doom--current-flags))
((let ((module-pair
(or doom--current-module
- (doom-module-from-path (FILE!)))))
+ (doom-module-from-path (file!)))))
(unless module-pair
- (error "featurep! call couldn't auto-detect what module its in (from %s)" (FILE!)))
+ (error "featurep! call couldn't auto-detect what module its in (from %s)" (file!)))
(memq category (doom-module-get (car module-pair) (cdr module-pair) :flags)))))
t))
diff --git a/core/core-packages.el b/core/core-packages.el
index 8346326a2..d7cda7fbf 100644
--- a/core/core-packages.el
+++ b/core/core-packages.el
@@ -1,5 +1,7 @@
;;; core/core-packages.el -*- lexical-binding: t; -*-
+(require 'core-modules)
+
;; 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,
;; lazily-loaded package management system for Emacs.
@@ -32,123 +34,164 @@
;;
;; See core/autoload/packages.el for more functions.
+(defvar doom-init-packages-p nil
+ "If non-nil, Doom's package management system has been initialized.")
+
(defvar doom-packages ()
"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!' declaration. Set by `doom-initialize-packages'.")
-(defvar doom-core-packages
- '(persistent-soft use-package quelpa async)
+(defvar doom-core-packages '(straight use-package async)
"A list of packages that must be installed (and will be auto-installed if
missing) and shouldn't be deleted.")
+(defvar doom-core-package-sources
+ '((org-elpa :local-repo nil)
+ (melpa
+ :type git :host github
+ :repo "melpa/melpa"
+ :no-build t)
+ (gnu-elpa-mirror
+ :type git :host github
+ :repo "emacs-straight/gnu-elpa-mirror"
+ :no-build t)
+ (emacsmirror-mirror
+ :type git :host github
+ :repo "emacs-straight/emacsmirror-mirror"
+ :no-build t))
+ "A list of recipes for straight's recipe repos.")
+
(defvar doom-disabled-packages ()
"A list of packages that should be ignored by `def-package!' and `after!'.")
-;;; package.el
+
+;;
+;;; Package managers
+
+;; Ensure that, if we do need package.el, it is configured correctly. You really
+;; shouldn't be using it, but it may be convenient for quick package testing.
(setq package--init-file-ensured t
- package-user-dir (expand-file-name "elpa" doom-packages-dir)
- package-gnupghome-dir (expand-file-name "gpg" doom-packages-dir)
package-enable-at-startup nil
+ package-user-dir doom-elpa-dir
+ package-gnupghome-dir (expand-file-name "gpg" doom-elpa-dir)
;; I omit Marmalade because its packages are manually submitted rather
;; than pulled, so packages are often out of date with upstream.
package-archives
- `(("gnu" . "https://elpa.gnu.org/packages/")
- ("melpa" . "https://melpa.org/packages/")
- ("org" . "https://orgmode.org/elpa/")))
+ (let ((proto (if gnutls-verify-error "http" "https")))
+ `(("gnu" . ,(concat proto "://elpa.gnu.org/packages/"))
+ ("melpa" . ,(concat proto "://melpa.org/packages/"))
+ ("org" . ,(concat proto "://orgmode.org/elpa/")))))
;; Don't save `package-selected-packages' to `custom-file'
-(advice-add #'package--save-selected-packages :override
- (lambda (&optional value) (if value (setq package-selected-packages value))))
+(def-advice! doom--package-inhibit-custom-file-a (&optional value)
+ :override #'package--save-selected-packages
+ (if value (setq package-selected-packages value)))
-(when (or (not gnutls-verify-error)
- (not (ignore-errors (gnutls-available-p))))
- (dolist (archive package-archives)
- (setcdr archive (replace-regexp-in-string "^https://" "http://" (cdr archive) t nil))))
+;;; straight
+(setq straight-cache-autoloads nil ; we already do this, and better.
+ ;; Doom doesn't encourage you to modify packages in place. Disabling this
+ ;; makes 'doom refresh' instant (once everything set up), which is much
+ ;; nicer UX than the several seconds modification checks add.
+ straight-check-for-modifications nil
+ ;; We do this ourselves, and a little more comprehensively.
+ straight-enable-package-integration nil
+ ;; Before switching to straight, `doom-local-dir' would average out at
+ ;; around 100mb. Afterwards, at around 1gb. With shallow cloning, that is
+ ;; reduced to ~400mb. This imposes an isuse with packages that require
+ ;; their git history for certain things to work (like magit and org), but
+ ;; we're prepared for that.
+ straight-vc-git-default-clone-depth 1
+ ;; Straight's own emacsmirror mirro is a little smaller and faster.
+ straight-recipes-emacsmirror-use-mirror t
+ ;; Prefix declarations are unneeded bulk added to our autoloads file. Best
+ ;; we just don't have to deal with them at all.
+ autoload-compute-prefixes nil)
-;;; quelpa
-(setq quelpa-dir (expand-file-name "quelpa" doom-packages-dir)
- quelpa-verbose doom-debug-mode
-
- ;; Don't track MELPA, we'll use package.el for that
- quelpa-checkout-melpa-p nil
- quelpa-update-melpa-p nil
- quelpa-melpa-recipe-stores nil
- quelpa-self-upgrade-p nil)
+;; Straight is hardcoded to operate out of ~/.emacs.d/straight. Not on my watch!
+(def-advice! doom--straight-use-local-dir-a (orig-fn &rest args)
+ :around #'straight--emacs-dir
+ (let ((user-emacs-directory doom-local-dir))
+ (apply orig-fn args)))
;;
;;; Bootstrapper
(defun doom-initialize-packages (&optional force-p)
- "Ensures that Doom's package management system, package.el and quelpa are
-initialized, and `doom-packages', `packages-alist' and `quelpa-cache' are
-populated, if they aren't already.
+ "Ensures that Doom's package system and straight.el are initialized.
If FORCE-P is non-nil, do it anyway.
-If FORCE-P is 'internal, only (re)populate `doom-packages'.
-Use this before any of package.el, quelpa or Doom's package management's API to
-ensure all the necessary package metadata is initialized and available for
-them."
- (let ((load-prefer-newer t)) ; reduce stale code issues
- ;; package.el and quelpa handle themselves if their state changes during the
- ;; current session, but if you change a packages.el file in a module,
- ;; there's no non-trivial way to detect that, so to reload only
- ;; `doom-packages' pass 'internal as FORCE-P or use `doom/reload-packages'.
- (unless (eq force-p 'internal)
- ;; `package-alist'
- (when (or force-p (not (bound-and-true-p package-alist)))
- (doom-ensure-packages-initialized 'force)
- (setq load-path (cl-delete-if-not #'file-directory-p load-path)))
- ;; `quelpa-cache'
- (when (or force-p (not (bound-and-true-p quelpa-cache)))
- ;; ensure un-byte-compiled version of quelpa is loaded
- (unless (featurep 'quelpa)
- (load (locate-library "quelpa.el") nil t t))
- (setq quelpa-initialized-p nil)
- (or (quelpa-setup-p)
- (error "Could not initialize quelpa"))))
- ;; `doom-packages'
- (when (or force-p (not doom-packages))
- (setq doom-packages (doom-package-list)))))
+This ensure `doom-packages' is populated, if isn't aren't already. Use this
+before any of straight's or Doom's package management's API to ensure all the
+necessary package metadata is initialized and available for them."
+ (when (or force-p (not doom-init-packages-p))
+ (setq doom-init-packages-p t)
+ (straight--reset-caches)
+ (mapc #'straight-use-recipes doom-core-package-sources)
+ (straight-register-package
+ `(straight :type git :host github
+ :repo ,(format "%s/straight.el" straight-repository-user)
+ :files ("straight*.el")
+ :branch ,straight-repository-branch))
+ (mapc #'straight-use-package doom-core-packages)
+ (when noninteractive
+ (add-hook 'kill-emacs-hook #'straight--transaction-finalize))
+ (dolist (package (straight--directory-files (straight--build-dir)))
+ (add-to-list 'load-path (directory-file-name (straight--build-dir package)))))
+ (when (or force-p (not doom-packages))
+ (setq doom-disabled-packages nil
+ doom-packages (doom-package-list))
+ (cl-loop for (pkg . plist) in doom-packages
+ for ignored = (eval (plist-get plist :ignore) t)
+ for disabled = (eval (plist-get plist :disable) t)
+ if disabled
+ do (add-to-list 'doom-disabled-packages pkg)
+ else if (not ignored)
+ do (with-demoted-errors "Package error: %s"
+ (straight-register-package
+ (if-let (recipe (plist-get plist :recipe))
+ `(,pkg ,@recipe)
+ pkg))))))
+
+(defun doom-ensure-straight ()
+ "Ensure `straight' is installed and was compiled with this version of Emacs."
+ (defvar bootstrap-version)
+ (let* ((straight-dir (expand-file-name "straight/" doom-local-dir))
+ (bootstrap-file (expand-file-name "repos/straight.el/straight.el" straight-dir))
+ (bootstrap-version 5)
+ ;; Force straight to install into ~/.emacs.d/.local/straight instead of
+ ;; ~/.emacs.d/straight by pretending `doom-local-dir' is our .emacs.d.
+ (user-emacs-directory doom-local-dir))
+ (cl-block 'straight
+ ;; Straight will throw `emacs-version-changed' if it's loaded with a
+ ;; version of Emacs that doesn't match the one it was compiled with.
+ ;; Getting this error isn't very good UX...
+ (catch 'emacs-version-changed
+ (unless (require 'straight nil t)
+ (unless (file-exists-p bootstrap-file)
+ (with-current-buffer
+ (url-retrieve-synchronously
+ "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el"
+ 'silent 'inhibit-cookies)
+ (goto-char (point-max))
+ (eval-print-last-sexp)))
+ (load bootstrap-file nil 'nomessage))
+ (cl-return-from 'straight t))
+ ;; ...so we transform it into a more graceful error message:
+ (with-temp-buffer
+ (insert-file-contents-literally (expand-file-name "build-cache.el" straight-dir))
+ (let ((_ (read (current-buffer)))
+ (last-emacs-version (read (current-buffer))))
+ (user-error "Your version of Emacs has changed (from %S to %S). You must rebuild your packages with 'doom rebuild'."
+ emacs-version last-emacs-version))))))
;;
-;;; Package API
+;;; Module package macros
-(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 _
- (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")))
-
-
-;;
-;; Module package macros
-
-(cl-defmacro package! (name &rest plist &key built-in recipe pin disable ignore _freeze)
+(cl-defmacro package! (name &rest plist &key built-in _recipe disable ignore _freeze)
"Declares a package and how to install it (if applicable).
This macro is declarative and does not load nor install packages. It is used to
@@ -162,9 +205,6 @@ Accepts the following properties:
:recipe RECIPE
Takes a MELPA-style recipe (see `quelpa-recipe' in `quelpa' for an example);
for packages to be installed from external sources.
- :pin ARCHIVE-NAME
- Instructs ELPA to only look for this package in ARCHIVE-NAME. e.g. \"org\".
- Ignored if RECIPE is present.
:disable BOOL
Do not install or update this package AND disable all of its `def-package!'
blocks.
@@ -180,14 +220,9 @@ Returns t if package is successfully registered, and nil if it was disabled
elsewhere."
(declare (indent defun))
(let ((old-plist (cdr (assq name doom-packages))))
- (when recipe
- (when (cl-evenp (length recipe))
- (setq plist (plist-put plist :recipe (cons name recipe))))
- (setq pin nil
- plist (plist-put plist :pin nil)))
(let ((module-list (plist-get old-plist :modules))
(module (or doom--current-module
- (let ((file (FILE!)))
+ (let ((file (file!)))
(cond ((file-in-directory-p file doom-private-dir)
(list :private))
((file-in-directory-p file doom-core-dir)
@@ -199,7 +234,7 @@ elsewhere."
(when built-in
(doom-log "Ignoring built-in package %S" name)
(when (equal built-in '(quote prefer))
- (setq built-in `(locate-library ,(symbol-name name) nil doom-site-load-path))))
+ (setq built-in `(locate-library ,(symbol-name name) nil doom--initial-load-path))))
(setq plist (plist-put plist :ignore (or built-in ignore)))
(while plist
(unless (null (cadr plist))
@@ -207,22 +242,20 @@ elsewhere."
(pop plist)
(pop plist))
(setq plist old-plist)
+ ;; TODO Add `straight-use-package-pre-build-function' support
(macroexp-progn
- (append (when pin
- (doom-log "Pinning package '%s' to '%s'" name pin)
- `((setf (alist-get ',name package-pinned-packages) ,pin)))
- `((setf (alist-get ',name doom-packages) ',plist))
+ (append `((setf (alist-get ',name doom-packages) ',plist))
(when disable
- (doom-log "Disabling package '%s'" name)
- `((add-to-list 'doom-disabled-packages ',name nil 'eq)
+ `((doom-log "Disabling package %S" ',name)
+ (add-to-list 'doom-disabled-packages ',name nil 'eq)
nil))))))
(defmacro disable-packages! (&rest packages)
"A convenience macro for disabling packages in bulk.
Only use this macro in a module's (or your private) packages.el file."
(macroexp-progn
- (cl-loop for pkg in packages
- collect (macroexpand `(package! ,pkg :disable t)))))
+ (cl-loop for p in packages
+ collect `(package! ,p :disable t))))
(provide 'core-packages)
;;; core-packages.el ends here
diff --git a/core/core.el b/core/core.el
index 80ed6d957..9f24e28bb 100644
--- a/core/core.el
+++ b/core/core.el
@@ -56,7 +56,7 @@ dependencies or long-term shared data. Must end with a slash.")
Use this for files that change often, like cache files. Must end with a slash.")
-(defvar doom-packages-dir (concat doom-local-dir "packages/")
+(defvar doom-elpa-dir (concat doom-local-dir "elpa/")
"Where package.el and quelpa plugins (and their caches) are stored.
Must end with a slash.")
@@ -78,13 +78,13 @@ Defaults to ~/.config/doom, ~/.doom.d or the value of the DOOMDIR envvar;
whichever is found first. Must end in a slash.")
(defvar doom-autoload-file (concat doom-local-dir "autoloads.el")
- "Where `doom-reload-doom-autoloads' stores its core autoloads.
+ "Where `doom-reload-core-autoloads' stores its core autoloads.
This file is responsible for informing Emacs where to find all of Doom's
autoloaded core functions (in core/autoload/*.el).")
(defvar doom-package-autoload-file (concat doom-local-dir "autoloads.pkg.el")
- "Where `doom-reload-package-autoloads' stores its package.el autoloads.
+ "Where `doom-reload-package-autoloads' stores its package autoloads.
This file is compiled from the autoloads files of all installed packages
combined.")
@@ -404,35 +404,6 @@ Meant to be used with `run-hook-wrapped'."
;; return nil so `run-hook-wrapped' won't short circuit
nil)
-(defun doom-ensure-same-emacs-version-p ()
- "Check if the running version of Emacs has changed and set
-`doom-emacs-changed-p' if it has."
- (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. If you run into errors, run `bin/doom compile :plugins` or reinstall your\n"
- "plugins to resolve them.\n\n"
- "Continue?")
- doom--last-emacs-version
- emacs-version))
- (delete-file doom--last-emacs-file))
- (noninteractive (error "Aborting"))
- ((kill-emacs))))
-
-(defun doom-ensure-core-directories-exist ()
- "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-h (&optional return-p)
"Display a benchmark, showing number of packages and modules, and how quickly
they were loaded at startup.
@@ -461,7 +432,9 @@ If RETURN-P, return the message as a string instead of displaying it."
"Tries to load FILE (an autoloads file). Return t on success, throws an error
in interactive sessions, nil otherwise (but logs a warning)."
(condition-case e
- (load (file-name-sans-extension file) 'noerror 'nomessage)
+ (let (command-switch-alist)
+ (load (if noninteractive file (file-name-sans-extension file))
+ 'noerror 'nomessage))
((debug error)
(if noninteractive
(message "Autoload file warning: %s -> %s" (car e) (error-message-string e))
@@ -470,7 +443,7 @@ in interactive sessions, nil otherwise (but logs a warning)."
(defun doom-load-env-vars (file)
"Read and set envvars in FILE."
(if (not (file-readable-p file))
- (doom-log "Couldn't read %S envvar file" file)
+ (signal 'file-error (list "Couldn't read envvar file" file))
(with-temp-buffer
(insert-file-contents file)
(search-forward "\n\n" nil t)
@@ -527,37 +500,45 @@ to least)."
load-path doom--initial-load-path
process-environment doom--initial-process-environment)
- ;; `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-exist)
- (doom-ensure-same-emacs-version-p)
+ ;; `doom-autoload-file' tells Emacs where to load all its functions from.
+ ;; This includes everything in core/autoload/*.el and autoload files in
+ ;; enabled modules.
+ (when (or (not (doom-initialize-autoloads doom-autoload-file))
+ force-p)
+ (dolist (dir (list doom-local-dir doom-etc-dir doom-cache-dir doom-elpa-dir))
+ (unless (file-directory-p dir)
+ (make-directory dir 'parents-too)))
+ ;; Ensure the package management system (and straight) are ready for
+ ;; action (and all core packages/repos are installed)
(require 'core-packages)
- (doom-ensure-packages-initialized force-p)
- (doom-ensure-core-packages)
+ (doom-ensure-straight)
+ (doom-initialize-packages force-p)
(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 loads a concatenated package
- ;; autoloads file and caches `load-path', `auto-mode-alist',
- ;; `Info-directory-list', `doom-disabled-packages' and
- ;; `package-activated-list'. A big reduction in startup time.
- (let (command-switch-alist)
- (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")))
+ ;; autoloads file which caches `load-path', `auto-mode-alist',
+ ;; `Info-directory-list', and `doom-disabled-packages'. 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"))
;; Load shell environment
- (unless noninteractive
+ (when (and (not noninteractive)
+ (file-exists-p doom-env-file))
(doom-load-env-vars doom-env-file)))
;; In case we want to use package.el's API
(with-eval-after-load 'package
- (require 'core-packages)))
+ (require 'core-packages))
+ ;; Or straight interactively
+ (with-eval-after-load 'straight
+ (require 'core-packages)
+ (doom-initialize-packages)))
;;
diff --git a/core/packages.el b/core/packages.el
index a1c5c6d60..e88c26bee 100644
--- a/core/packages.el
+++ b/core/packages.el
@@ -40,8 +40,5 @@
(package! which-key)
(package! hydra)
-;; core-packages.el
-(package! gnu-elpa-keyring-update)
-
;; autoload/debug.el
(package! esup)
diff --git a/init.example.el b/init.example.el
index ea853ade2..73482f49f 100644
--- a/init.example.el
+++ b/init.example.el
@@ -1,9 +1,9 @@
;;; init.el -*- lexical-binding: t; -*-
-;; Copy this file to ~/.doom.d/init.el or ~/.config/doom/init.el ('doom
-;; quickstart' will do this for you). The `doom!' block below controls what
-;; modules are enabled and in what order they will be loaded. Remember to run
-;; 'doom refresh' after modifying it.
+;; Copy this file to ~/.doom.d/init.el or ~/.config/doom/init.el ('doom install'
+;; will do this for you). The `doom!' block below controls what modules are
+;; enabled and in what order they will be loaded. Remember to run 'doom refresh'
+;; after modifying it.
;;
;; More information about these modules (and what flags they support) can be
;; found in modules/README.org.
diff --git a/modules/completion/helm/packages.el b/modules/completion/helm/packages.el
index 1e8d9d178..2021ecf93 100644
--- a/modules/completion/helm/packages.el
+++ b/modules/completion/helm/packages.el
@@ -5,7 +5,7 @@
(package! helm-ag)
(package! helm-c-yasnippet)
(package! helm-company)
-(package! helm-describe-modes :recipe (:fetcher github :repo "emacs-helm/helm-describe-modes"))
+(package! helm-describe-modes :recipe (:host github :repo "emacs-helm/helm-describe-modes"))
(package! helm-projectile)
(package! swiper-helm)
(when (featurep! +fuzzy)
diff --git a/modules/editor/evil/packages.el b/modules/editor/evil/packages.el
index 4c345d5e8..3e0e1f8ef 100644
--- a/modules/editor/evil/packages.el
+++ b/modules/editor/evil/packages.el
@@ -9,14 +9,13 @@
(package! evil-escape)
(package! evil-exchange)
(package! evil-indent-plus)
-(package! evil-numbers :recipe (:fetcher github :repo "janpath/evil-numbers"))
+(package! evil-numbers :recipe (:host github :repo "janpath/evil-numbers"))
(package! evil-textobj-anyblock)
(package! evil-snipe)
(package! evil-surround)
(package! evil-visualstar)
(package! exato)
-
;;
(when (featurep! +everywhere)
;; `evil-collection-neotree' uses the `neotree-make-executor' macro, but this
diff --git a/modules/editor/rotate-text/packages.el b/modules/editor/rotate-text/packages.el
index 955e531ed..5d6486109 100644
--- a/modules/editor/rotate-text/packages.el
+++ b/modules/editor/rotate-text/packages.el
@@ -1,4 +1,4 @@
;; -*- no-byte-compile: t; -*-
;;; editor/rotate-text/packages.el
-(package! rotate-text :recipe (:fetcher github :repo "debug-ito/rotate-text.el"))
+(package! rotate-text :recipe (:host github :repo "debug-ito/rotate-text.el"))
diff --git a/modules/editor/snippets/packages.el b/modules/editor/snippets/packages.el
index 64123c6d3..f0274ca26 100644
--- a/modules/editor/snippets/packages.el
+++ b/modules/editor/snippets/packages.el
@@ -5,6 +5,6 @@
(package! auto-yasnippet)
(package! doom-snippets
- :recipe (:fetcher github
+ :recipe (:host github
:repo "hlissner/doom-snippets"
:files ("*.el" "snippets")))
diff --git a/modules/lang/cc/packages.el b/modules/lang/cc/packages.el
index 5a73da2f9..49f47c91b 100644
--- a/modules/lang/cc/packages.el
+++ b/modules/lang/cc/packages.el
@@ -10,7 +10,7 @@
(when (package! glsl-mode)
(when (featurep! :completion company)
- (package! company-glsl :recipe (:fetcher github :repo "Kaali/company-glsl"))))
+ (package! company-glsl :recipe (:host github :repo "Kaali/company-glsl"))))
(if (featurep! +lsp)
(package! ccls)
diff --git a/modules/lang/ocaml/packages.el b/modules/lang/ocaml/packages.el
index 9a5a84a40..28c8f99bc 100644
--- a/modules/lang/ocaml/packages.el
+++ b/modules/lang/ocaml/packages.el
@@ -16,18 +16,8 @@
;; by default quelpa generated a version 0pre0.20180929.192844, which got
;; parsed into (0 -1 0 ...), which when compared with version nil (0) in
;; package-installed-p always yielded false
- (package! ocamlformat :recipe (:fetcher github :repo "ocaml-ppx/ocamlformat" :files ("emacs/*.el"))))
+ (package! ocamlformat :recipe
+ (:host github :repo "ocaml-ppx/ocamlformat" :files ("emacs/*.el"))))
-(package! dune :recipe (:fetcher github :repo "ocaml/dune" :files ("editor-integration/emacs/*.el")))
-
-
-;; (defvar +ocaml-elisp-dir
-;; (when (executable-find "opam")
-;; (let ((opam-share (ignore-errors (car (process-lines "opam" "config" "var" "share" "--safe")))))
-;; (when (and opam-share (file-directory-p opam-share))
-;; (expand-file-name "emacs/site-lisp" opam-share)))))
-;;
-;; (defmacro localpackage! (name)
-;; `(package! ,name :recipe (:fetcher file :path ,+ocaml-elisp-dir)))
-;;
-;; (localpackage! opam-site-lisp)
+(package! dune :recipe
+ (:host github :repo "ocaml/dune" :files ("editor-integration/emacs/*.el")))
diff --git a/modules/lang/org/config.el b/modules/lang/org/config.el
index 160f5bd7b..e809cda2f 100644
--- a/modules/lang/org/config.el
+++ b/modules/lang/org/config.el
@@ -882,3 +882,12 @@ compelling reason, so..."
(org-clock-load))
:config
(add-hook 'kill-emacs-hook #'org-clock-save)))
+
+
+;; HACK A necessary hack because org requires a compilation step after being
+;; cloned, and during that compilation a org-version.el is generated with these
+;; two functions, which return the output of a 'git describe ...' call in the
+;; repo's root. Of course, this command won't work in a sparse clone, and more
+;; than that, initiating these compilation step is a hassle, so...
+(defun org-release () "")
+(defun org-git-version () "")
diff --git a/modules/lang/org/packages.el b/modules/lang/org/packages.el
index 2697d0896..3a0bfcff0 100644
--- a/modules/lang/org/packages.el
+++ b/modules/lang/org/packages.el
@@ -1,15 +1,8 @@
;; -*- no-byte-compile: t; -*-
;;; lang/org/packages.el
-;; Prevent built-in Org from playing into the byte-compilation of
-;; `org-plus-contrib'.
-(when-let (orglib (locate-library "org" nil doom-site-load-path))
- (setq load-path (delete (substring (file-name-directory orglib) 0 -1)
- load-path)))
-(package! org-plus-contrib) ; install cutting-edge version of org-mode
-(package! org :ignore t) ; ignore org on ELPA, if possible
-
-(package! org-bullets :recipe (:fetcher github :repo "Kaligule/org-bullets"))
+(package! org-plus-contrib) ; install cutting-edge version of org-mode
+(package! org-bullets :recipe (:host github :repo "Kaligule/org-bullets"))
(package! toc-org)
(when (featurep! :editor evil)
(package! evil-org))
@@ -17,7 +10,7 @@
(package! org-pdfview))
(package! htmlize)
(package! ox-clip)
-(package! org-yt :recipe (:fetcher github :repo "TobiasZawada/org-yt"))
+(package! org-yt :recipe (:host github :repo "TobiasZawada/org-yt"))
;;; Babel
(package! ob-async)
@@ -28,7 +21,7 @@
(when (featurep! :lang nim)
(package! ob-nim))
(when (featurep! :lang racket)
- (package! ob-racket :recipe (:fetcher github :repo "DEADB17/ob-racket")))
+ (package! ob-racket :recipe (:host github :repo "DEADB17/ob-racket")))
(when (featurep! :lang rest)
(package! ob-restclient))
(when (featurep! :lang rust)
@@ -37,18 +30,14 @@
;;; Modules
(when (featurep! +dragndrop)
(package! org-download))
-
(when (featurep! +gnuplot)
(package! gnuplot)
(package! gnuplot-mode))
-
(when (featurep! +ipython)
(package! ob-ipython))
-
(when (featurep! +pandoc)
(package! ox-pandoc))
-
(when (featurep! +present)
- (package! centered-window :recipe (:fetcher github :repo "anler/centered-window-mode"))
+ (package! centered-window :recipe (:host github :repo "anler/centered-window-mode"))
(package! org-tree-slide)
(package! ox-reveal))
diff --git a/modules/lang/php/packages.el b/modules/lang/php/packages.el
index 39f874bb5..bcb87db05 100644
--- a/modules/lang/php/packages.el
+++ b/modules/lang/php/packages.el
@@ -2,13 +2,13 @@
;;; lang/php/packages.el
(package! php-boris)
-(package! php-extras :recipe (:fetcher github :repo "arnested/php-extras"))
+(package! php-extras :recipe (:host github :repo "arnested/php-extras"))
(package! php-mode)
(package! php-refactor-mode)
(package! phpunit)
(when (featurep! +hack)
- (package! hack-mode :recipe (:fetcher github :repo "hhvm/hack-mode")))
+ (package! hack-mode :recipe (:host github :repo "hhvm/hack-mode")))
(unless (featurep! +lsp)
(package! phpactor))
diff --git a/modules/lang/terra/packages.el b/modules/lang/terra/packages.el
index 90115f396..4a1a1485b 100644
--- a/modules/lang/terra/packages.el
+++ b/modules/lang/terra/packages.el
@@ -1,7 +1,8 @@
;; -*- no-byte-compile: t; -*-
;;; lang/lua/packages.el
-(package! terra-mode :recipe (:fetcher github :repo "StanfordLegion/terra-mode"))
+(package! terra-mode
+ :recipe (:host github :repo "StanfordLegion/terra-mode"))
(when (featurep! :completion company)
(package! company-lua))
diff --git a/modules/tools/magit/config.el b/modules/tools/magit/config.el
index 16c549113..e2e36bac3 100644
--- a/modules/tools/magit/config.el
+++ b/modules/tools/magit/config.el
@@ -17,6 +17,11 @@ It is passed a user and repository name.")
(setq transient-levels-file (concat doom-etc-dir "transient/levels")
transient-values-file (concat doom-etc-dir "transient/values")
transient-history-file (concat doom-etc-dir "transient/history"))
+
+ ;; HACK Magit complains loudly when it can't determine its own version, which
+ ;; is the case when magit is built through straight. The warning is
+ ;; harmless, however, so we just need it to shut up.
+ (advice-add #'magit-version :around #'ignore)
:config
(setq transient-default-level 5
magit-revision-show-gravatars '("^Author: " . "^Commit: ")
diff --git a/modules/tools/pass/packages.el b/modules/tools/pass/packages.el
index cb4e52e36..a65e113a8 100644
--- a/modules/tools/pass/packages.el
+++ b/modules/tools/pass/packages.el
@@ -7,8 +7,8 @@
;; an older version of `auto-source-pass' is built into Emacs 26+, so we must
;; install the new version directly from the source and with a psuedonym.
-(package! auth-source-pass-new
- :recipe (auth-source-pass :fetcher github :repo "DamienCassou/auth-password-store"))
+(package! auth-source-pass
+ :recipe (:host github :repo "DamienCassou/auth-password-store"))
(when (featurep! :completion ivy)
(package! ivy-pass))