💥 Replace package.el/quelpa with straight #374

There are a few kinks to iron out, but for the most part it's done. Doom
Emacs, powered by straight. Goodbye gnutls and elpa/quelpa issues.

This update doesn't come with rollback or lockfile support yet, but I
will eventually include one with Doom, and packages will be (by default,
anyway) updated in sync with Doom.

Relevant threads: #1577 #1566 #1473
This commit is contained in:
Henrik Lissner 2019-07-21 15:39:45 +02:00
parent 492f2dea1e
commit b90dede1ab
No known key found for this signature in database
GPG key ID: 5F6C0EA160557395
35 changed files with 1542 additions and 1771 deletions

View file

@ -25,8 +25,7 @@ cp: compile-plugins
re: recompile re: recompile
d: doctor d: doctor
quickstart: deprecated quickstart: install
@$(DOOM) quickstart
## Package management ## Package management
@ -49,7 +48,7 @@ compile-core: deprecated
compile-private: deprecated compile-private: deprecated
@$(DOOM) compile :private @$(DOOM) compile :private
compile-plugins: deprecated compile-plugins: deprecated
@$(DOOM) compile :plugins @$(DOOM) build
recompile: deprecated recompile: deprecated
@$(DOOM) recompile @$(DOOM) recompile
clean: deprecated clean: deprecated

View file

@ -34,7 +34,7 @@
**Quick start** **Quick start**
```bash ```bash
git clone https://github.com/hlissner/doom-emacs ~/.emacs.d git clone https://github.com/hlissner/doom-emacs ~/.emacs.d
~/.emacs.d/bin/doom quickstart ~/.emacs.d/bin/doom install
``` ```
**Table of Contents** **Table of Contents**

View file

@ -27,6 +27,7 @@
(when (getenv "DEBUG") (when (getenv "DEBUG")
(setq debug-on-error t)) (setq debug-on-error t))
(require 'subr-x)
(require 'pp) (require 'pp)
(load (expand-file-name "core/autoload/format" user-emacs-directory) nil t) (load (expand-file-name "core/autoload/format" user-emacs-directory) nil t)
@ -147,7 +148,7 @@
emacs-version) emacs-version)
(explain! "Byte-code compiled in one version of Emacs may not work in another 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" "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...") (section! "Checking for Emacs config conflicts...")
(when (file-exists-p "~/.emacs") (when (file-exists-p "~/.emacs")
@ -182,109 +183,7 @@
;; on windows? ;; on windows?
(when (memq system-type '(windows-nt ms-dos cygwin)) (when (memq system-type '(windows-nt ms-dos cygwin))
(warn! "Warning: Windows detected") (warn! "Warning: Windows detected")
(explain! "DOOM was designed for MacOS and Linux. Expect a bumpy ride!")) (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."))))
;; ;;
@ -292,12 +191,11 @@
(condition-case-unless-debug ex (condition-case-unless-debug ex
(let ((after-init-time (current-time)) (let ((after-init-time (current-time))
(doom-message-backend 'ansi) (doom-format-backend 'ansi))
noninteractive)
(section! "Checking DOOM Emacs...") (section! "Checking DOOM Emacs...")
(load (concat user-emacs-directory "core/core.el") nil t) (load (concat user-emacs-directory "core/core.el") nil t)
(unless (file-directory-p doom-private-dir) (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)) (let ((indent 2))
;; Make sure everything is loaded ;; Make sure everything is loaded
@ -317,6 +215,7 @@
(success! "Initialized %d modules" (hash-table-count doom-modules)) (success! "Initialized %d modules" (hash-table-count doom-modules))
(warn! "Failed to load any modules. Do you have an private init.el?")) (warn! "Failed to load any modules. Do you have an private init.el?"))
(doom-ensure-straight)
(doom-initialize-packages) (doom-initialize-packages)
(success! "Initialized %d packages" (length doom-packages)) (success! "Initialized %d packages" (length doom-packages))
@ -343,11 +242,10 @@
doom-disabled-packages) doom-disabled-packages)
(load packages-file 'noerror 'nomessage) (load packages-file 'noerror 'nomessage)
(mapcar #'car doom-packages)) (mapcar #'car doom-packages))
for name = (doom-package-true-name name) unless (or (doom-package-get name :disable)
unless (or (doom-package-prop name :disable) (eval (doom-package-get name :ignore))
(eval (doom-package-prop name :ignore)) (doom-package-built-in-p name)
(package-built-in-p name) (doom-package-installed-p name))
(package-installed-p name))
do (error! "%s is not installed" name)) do (error! "%s is not installed" name))
(load doctor-file 'noerror 'nomessage)) (load doctor-file 'noerror 'nomessage))
(file-missing (error! "%s" (error-message-string ex))) (file-missing (error! "%s" (error-message-string ex)))

View file

@ -1,15 +1,18 @@
;;; core/autoload/cli.el -*- lexical-binding: t; -*- ;;; core/autoload/cli.el -*- lexical-binding: t; -*-
;; Externs
(defvar evil-collection-mode-list)
(require 'core-cli) (require 'core-cli)
;;;###autoload ;;;###autoload
(defun doom-cli-run (command &rest _args) (defun doom--cli-run (command &rest _args)
(when (featurep 'general) (when (featurep 'general)
(general-auto-unbind-keys)) (general-auto-unbind-keys))
(let* ((evil-collection-mode-list nil) (let* ((evil-collection-mode-list nil)
(default-directory doom-emacs-dir) (default-directory doom-emacs-dir)
(buf (get-buffer-create " *bin/doom*")) (buf (get-buffer-create " *bin/doom*"))
(doom-message-backend 'ansi) (doom-format-backend 'ansi)
(ignore-window-parameters t) (ignore-window-parameters t)
(noninteractive t) (noninteractive t)
(standard-output (standard-output
@ -39,21 +42,21 @@
"TODO" "TODO"
(interactive "P") (interactive "P")
(let ((doom-auto-accept yes)) (let ((doom-auto-accept yes))
(doom-cli-run "autoloads"))) (doom--cli-run "autoloads")))
;;;###autoload ;;;###autoload
(defun doom//update (&optional yes) (defun doom//update (&optional yes)
"TODO" "TODO"
(interactive "P") (interactive "P")
(let ((doom-auto-accept yes)) (let ((doom-auto-accept yes))
(doom-cli-run "update"))) (doom--cli-run "update")))
;;;###autoload ;;;###autoload
(defun doom//upgrade (&optional yes) (defun doom//upgrade (&optional yes)
"TODO" "TODO"
(interactive "P") (interactive "P")
(let ((doom-auto-accept yes)) (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?") (when (y-or-n-p "You must restart Emacs for the upgrade to take effect. Restart?")
(doom/restart-and-restore))) (doom/restart-and-restore)))
@ -62,18 +65,18 @@
"TODO" "TODO"
(interactive "P") (interactive "P")
(let ((doom-auto-accept yes)) (let ((doom-auto-accept yes))
(doom-cli-run "install"))) (doom--cli-run "install")))
;;;###autoload ;;;###autoload
(defun doom//autoremove (&optional yes) (defun doom//autoremove (&optional yes)
"TODO" "TODO"
(interactive "P") (interactive "P")
(let ((doom-auto-accept yes)) (let ((doom-auto-accept yes))
(doom-cli-run "autoremove"))) (doom--cli-run "autoremove")))
;;;###autoload ;;;###autoload
(defun doom//refresh (&optional yes) (defun doom//refresh (&optional yes)
"TODO" "TODO"
(interactive "P") (interactive "P")
(let ((doom-auto-accept yes)) (let ((doom-auto-accept yes))
(doom-cli-run "refresh"))) (doom--cli-run "refresh")))

View file

@ -16,65 +16,54 @@ ready to be pasted in a bug report on github."
(require 'vc-git) (require 'vc-git)
(let ((default-directory doom-emacs-dir) (let ((default-directory doom-emacs-dir)
(doom-modules (doom-modules))) (doom-modules (doom-modules)))
(format (cl-letf
(concat "- OS: %s (%s)\n" (((symbol-function 'sh)
"- Shell: %s\n" (lambda (format)
"- Emacs: %s (%s)\n" (string-trim
"- Doom: %s (%s)\n" (shell-command-to-string format)))))
"- Graphic display: %s (daemon: %s)\n" `((emacs
"- System features: %s\n" (version . ,emacs-version)
"- Details:\n" (features ,@system-configuration-features)
" ```elisp\n" (build . ,(format-time-string "%b %d, %Y" emacs-build-time))
" env bootstrapper: %s\n" (buildopts ,system-configuration-options))
" elc count: %s\n" (doom
" uname -a: %s\n" (version . ,doom-version)
" modules: %s\n" (build . ,(sh "git log -1 --format=\"%D %h %ci\"")))
" packages: %s\n" (system
" exec-path: %s\n" (type . ,system-type)
" ```") (config . ,system-configuration)
system-type system-configuration (shell . ,shell-file-name)
shell-file-name (uname . ,(if IS-WINDOWS
emacs-version (format-time-string "%b %d, %Y" emacs-build-time) "n/a"
doom-version (sh "uname -msrv")))
(or (string-trim (shell-command-to-string "git log -1 --format=\"%D %h %ci\"")) (path . ,(mapcar #'abbreviate-file-name exec-path)))
"n/a") (config
(display-graphic-p) (daemonp) (envfile . ,(cond ((file-exists-p doom-env-file) 'envvar-file)
(bound-and-true-p system-configuration-features) ((featurep 'exec-path-from-shell) 'exec-path-from-shell)))
(cond ((file-exists-p doom-env-file) 'envvar-file) (elc-files . ,(length (doom-files-in `(,@doom-modules-dirs
((featurep 'exec-path-from-shell) 'exec-path-from-shell)) ,doom-core-dir
;; details ,doom-private-dir)
(length (doom-files-in `(,@doom-modules-dirs :type 'files :match "\\.elc$" :sort nil)))
,doom-core-dir (modules ,@(or (cl-loop with cat = nil
,doom-private-dir) for key being the hash-keys of doom-modules
:type 'files :match "\\.elc$" :sort nil)) if (or (not cat) (not (eq cat (car key))))
(if IS-WINDOWS do (setq cat (car key))
"n/a" and collect cat
(with-temp-buffer and collect (cdr key)
(unless (zerop (call-process "uname" nil t nil "-msrv")) else collect
(insert (format "%s" system-type))) (let ((flags (doom-module-get cat (cdr key) :flags)))
(string-trim (buffer-string)))) (if flags
(or (cl-loop with cat = nil `(,(cdr key) ,@flags)
for key being the hash-keys of doom-modules (cdr key))))
if (or (not cat) (not (eq cat (car key)))) '("n/a")))
do (setq cat (car key)) (packages ,@(or (ignore-errors
and collect cat (require 'use-package)
and collect (cdr key) (cl-loop for (name . plist) in (doom-find-packages :private t)
else collect if (use-package-plist-delete (copy-sequence plist) :modules)
(let ((flags (doom-module-get cat (cdr key) :flags))) collect (format "%s" (cons name it))
(if flags else
`(,(cdr key) ,@flags) collect (symbol-name name)))
(cdr key)))) '("n/a"))))))))
"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))))
;; ;;
@ -86,24 +75,55 @@ ready to be pasted in a bug report on github."
branch and commit." branch and commit."
(interactive) (interactive)
(require 'vc-git) (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 doom-version
emacs-version emacs-version
(or (vc-git--symbolic-ref doom-core-dir) (or (vc-git--symbolic-ref doom-core-dir)
"n/a") "n/a")
(or (vc-git-working-revision doom-core-dir) (or (vc-git-working-revision doom-core-dir)
"n/a")
(or (string-trim (shell-command-to-string "git log -1 --format=%ci"))
"n/a"))) "n/a")))
;;;###autoload ;;;###autoload
(defun doom/info () (defun doom/info (&optional raw)
"Collects some debug information about your Emacs session, formats it into "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!" markdown and copies it to your clipboard, ready to be pasted into bug reports!"
(interactive) (interactive "P")
(message "Generating Doom info...") (let ((buffer (get-buffer-create "*doom-info*"))
(if noninteractive (info (doom-info)))
(print! (doom-info)) (with-current-buffer buffer
(kill-new (doom-info)) (unless (or noninteractive
(message "Done! Copied to clipboard."))) (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 "<details>\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</details>"))
(if noninteractive
(print! (buffer-string))
(switch-to-buffer buffer)
(kill-new (buffer-string))
(print! (green "Copied markdown to clipboard"))))))
;;;###autoload ;;;###autoload
(defun doom/am-i-secure () (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 (macroexp-progn
(append `((setq noninteractive nil (append `((setq noninteractive nil
doom-debug-mode t doom-debug-mode t
load-path ',load-path
package--init-file-ensured t package--init-file-ensured t
package-user-dir ,package-user-dir package-user-dir ,package-user-dir
package-archives ',package-archives package-archives ',package-archives
user-emacs-directory ,doom-emacs-dir user-emacs-directory ,doom-emacs-dir)
doom--modules-cache nil)
(with-eval-after-load 'undo-tree (with-eval-after-load 'undo-tree
;; undo-tree throws errors because `buffer-undo-tree' isn't ;; undo-tree throws errors because `buffer-undo-tree' isn't
;; corrrectly initialized ;; corrrectly initialized

View file

@ -365,10 +365,14 @@ current file is in, or d) the module associated with the current major mode (see
(recenter) (recenter)
(message "Couldn't find the config block")))))))) (message "Couldn't find the config block"))))))))
(defvar doom--help-packages-list nil)
(defun doom--help-packages-list (&optional refresh) (defun doom--help-packages-list (&optional refresh)
(or (unless refresh (or (unless refresh
(doom-cache-get 'help-packages)) doom--help-packages-list)
(doom-cache-set 'help-packages (doom-package-list 'all)))) (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) (defun doom--help-package-configs (package)
;; TODO Add git checks, in case ~/.emacs.d isn't a git repo ;; TODO Add git checks, in case ~/.emacs.d isn't a git repo

View file

@ -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) ;;;###autoload (autoload 'doom-text-zoom-hydra/body "core/autoload/hydras" nil t)
(defhydra doom-text-zoom-hydra (:hint t :color red) (defhydra doom-text-zoom-hydra (:hint t :color red)

View file

@ -1,128 +1,67 @@
;;; core/autoload/packages.el -*- lexical-binding: t; -*- ;;; core/autoload/packages.el -*- lexical-binding: t; -*-
(require 'core-packages) (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 ;;; Package metadata
;;;###autoload ;;;###autoload
(defun doom-package-plist (package) (defun doom-package-get (package &optional prop nil-value)
"Returns PACKAGE's `package!' recipe from `doom-packages'." "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 ;;;###autoload
(defun doom-package-desc (package) (defun doom-package-recipe (package &optional prop nil-value)
"Returns PACKAGE's desc struct from `package-alist'." "Returns the `straight' recipe PACKAGE was registered with."
(cadr (assq (or (car (doom-package-prop package :recipe)) (let ((plist (gethash (symbol-name package) straight--recipe-cache)))
package) (if prop
package-alist))) (if (plist-member plist prop)
(plist-get plist prop)
nil-value)
plist)))
;;;###autoload ;;;###autoload
(defun doom-package-true-name (package) (defun doom-package-build-recipe (package &optional prop nil-value)
"Return PACKAGE's true name. "Returns the `straight' recipe PACKAGE was installed with."
(let ((plist (nth 2 (gethash (symbol-name package) straight--build-cache))))
It is possible for quelpa packages to be given a psuedonym (the first argument (if prop
of `package!'). Its real name is the car of package's :recipe. e.g. (if (plist-member plist prop)
(plist-get plist prop)
(package! X :recipe (Y :fetcher github :repo \"abc/def\")) nil-value)
plist)))
X's real name is Y."
(let ((sym (car (doom-package-prop package :recipe))))
(or (and (symbolp sym)
(not (keywordp sym))
sym)
package)))
;;;###autoload ;;;###autoload
(defun doom-package-psuedo-name (package) (defun doom-package-build-time (package)
"TODO" "TODO"
(or (cl-loop for (package . plist) in doom-packages (car (gethash (symbol-name package) straight--build-cache)))
for recipe-name = (car (plist-get plist :recipe))
if (eq recipe-name package)
return recipe-name)
package))
;;;###autoload ;;;###autoload
(defun doom-package-backend (package &optional noerror) (defun doom-package-dependencies (package &optional recursive noerror)
"Return backend that PACKAGE was installed with. "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 (defun doom-package-depending-on (package &optional noerror)
nil and the package isn't installed. "Return a list of packages that depend on the package named NAME."
(cl-check-type name symbol)
See `doom-package-recipe-backend' to get the backend PACKAGE is registered with ;; can't get dependencies for built-in packages
\(as opposed to what it is was installed with)." (unless (or (doom-package-build-recipe name)
(cl-check-type package symbol) noerror)
(let ((package-truename (doom-package-true-name package))) (error "Couldn't find %s, is it installed?" name))
(cond ((assq package-truename quelpa-cache) 'quelpa) (cl-loop for pkg in (hash-table-keys straight--build-cache)
((assq package-truename package-alist) 'elpa) for deps = (doom-package-dependencies pkg)
((doom-package-built-in-p package) 'emacs) if (memq package deps)
((not noerror) (error "%s package is not installed" package))))) collect pkg
and append (doom-package-depending-on pkg t)))
;;;###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))
;; ;;
@ -131,99 +70,52 @@ exist/isn't specified."
;;;###autoload ;;;###autoload
(defun doom-package-built-in-p (package) (defun doom-package-built-in-p (package)
"Return non-nil if PACKAGE (a symbol) is built-in." "Return non-nil if PACKAGE (a symbol) is built-in."
(unless (doom-package-installed-p package) (eq (doom-package-build-recipe package :type)
(or (package-built-in-p (doom-package-true-name package)) 'built-in))
(locate-library (symbol-name package) nil doom-site-load-path))))
;;;###autoload ;;;###autoload
(defun doom-package-installed-p (package) (defun doom-package-installed-p (package)
"Return non-nil if PACKAGE (a symbol) is installed." "Return non-nil if PACKAGE (a symbol) is installed."
(when-let (desc (doom-package-desc package)) (file-directory-p (straight--build-dir (symbol-name package))))
(and (package-installed-p desc)
(file-directory-p (package-desc-dir desc)))))
;;;###autoload ;;;###autoload
(defun doom-package-registered-p (package) (defun doom-package-registered-p (package)
"Return non-nil if PACKAGE (a symbol) has been registered with `package!'. "Return non-nil if PACKAGE (a symbol) has been registered with `package!'.
Excludes packages that have a non-nil :built-in property." Excludes packages that have a non-nil :built-in property."
(let ((package (or (cl-loop for (pkg . plist) in doom-packages (when-let (plist (doom-package-get package))
for newname = (car (plist-get plist :recipe)) (not (eval (plist-get plist :ignore) t))))
if (and (symbolp newname)
(eq newname package))
return pkg)
package)))
(when-let (plist (doom-package-plist package))
(not (eval (plist-get plist :ignore))))))
;;;###autoload ;;;###autoload
(defun doom-package-private-p (package) (defun doom-package-private-p (package)
"Return non-nil if PACKAGE was installed by the user's private config." "Return non-nil if PACKAGE was installed by the user's private config."
(doom-package-prop package :private)) (doom-package-get package :private))
;;;###autoload ;;;###autoload
(defun doom-package-protected-p (package) (defun doom-package-protected-p (package)
"Return non-nil if PACKAGE is protected. "Return non-nil if PACKAGE is protected.
A protected package cannot be deleted and will be auto-installed if missing." 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 ;;;###autoload
(defun doom-package-core-p (package) (defun doom-package-core-p (package)
"Return non-nil if PACKAGE is a core Doom package." "Return non-nil if PACKAGE is a core Doom package."
(or (doom-package-protected-p package) (or (doom-package-protected-p package)
(assq :core (doom-package-prop package :modules)))) (assq :core (doom-package-get 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)))))
;;;###autoload ;;;###autoload
(defun doom-package-different-recipe-p (name) (defun doom-package-different-recipe-p (name)
"Return t if a package named NAME (a symbol) has a different recipe than it "Return t if a package named NAME (a symbol) has a different recipe than it
was installed with." was installed with."
(cl-check-type name symbol) (cl-check-type name symbol)
(when (doom-package-installed-p name) ;; TODO
(let ((package-truename (doom-package-true-name name))) ;; (when (doom-package-installed-p name)
(when-let* ((quelpa-recipe (assq package-truename quelpa-cache)) ;; (when-let* ((doom-recipe (assq name doom-packages))
(doom-recipe (assq package-truename doom-packages))) ;; (install-recipe (doom-package-recipe)))
(not (equal (cdr quelpa-recipe) ;; (not (equal (cdr quelpa-recipe)
(cdr (plist-get (cdr doom-recipe) :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)))))
;; ;;
@ -307,32 +199,37 @@ files."
collect (cons sym plist) collect (cons sym plist)
and if (and deps (not (doom-package-built-in-p sym))) and if (and deps (not (doom-package-built-in-p sym)))
nconc 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 (or (eq installed 'any)
(if installed (if installed
(doom-package-installed-p pkg) (doom-package-installed-p pkg)
(not (doom-package-installed-p pkg)))) (not (doom-package-installed-p pkg))))
collect (cons pkg (cdr (assq pkg doom-packages))))))) 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 (with-temp-buffer ; prevent buffer-local settings from propagating
(condition-case e (condition-case e
(if (not raw) (if (not eval)
(load file noerror t t) (load file noerror t t)
(when (file-readable-p file) (when (file-readable-p file)
(insert-file-contents file) (insert-file-contents file)
(delay-mode-hooks (emacs-lisp-mode))
(while (re-search-forward "(package! " nil t) (while (re-search-forward "(package! " nil t)
(save-excursion (save-excursion
(goto-char (match-beginning 0)) (goto-char (match-beginning 0))
(cl-destructuring-bind (name . plist) (cdr (sexp-at-point)) (unless (string-match-p
(push (cons name "^.*;" (buffer-substring-no-properties
(plist-put plist :modules (line-beginning-position)
(cond ((file-in-directory-p file doom-private-dir) (point)))
'((:private))) (cl-destructuring-bind (name . plist) (cdr (sexp-at-point))
((file-in-directory-p file doom-core-dir) (push (cons name
'((:core))) (plist-put plist :modules
((doom-module-from-path file))))) (cond ((file-in-directory-p file doom-private-dir)
doom-packages)))))) '((:private)))
((file-in-directory-p file doom-core-dir)
'((:core)))
((doom-module-from-path file)))))
doom-packages)))))))
((debug error) ((debug error)
(signal 'doom-package-error (signal 'doom-package-error
(list (or (doom-module-from-path file) (list (or (doom-module-from-path file)
@ -350,15 +247,16 @@ ones."
(let ((noninteractive t) (let ((noninteractive t)
(doom-modules (doom-modules)) (doom-modules (doom-modules))
doom-packages doom-packages
doom-disabled-packages doom-disabled-packages)
package-pinned-packages) (doom--read-module-packages-file
(doom--read-module-packages-file (expand-file-name "packages.el" doom-core-dir) all-p) (expand-file-name "packages.el" doom-core-dir)
all-p t)
(let ((private-packages (expand-file-name "packages.el" doom-private-dir))) (let ((private-packages (expand-file-name "packages.el" doom-private-dir)))
(unless all-p (unless all-p
;; We load the private packages file twice to ensure disabled packages ;; We load the private packages file twice to ensure disabled packages
;; are seen ASAP, and a second time to ensure privately overridden ;; are seen ASAP, and a second time to ensure privately overridden
;; packages are properly overwritten. ;; 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 (if all-p
(mapc #'doom--read-module-packages-file (mapc #'doom--read-module-packages-file
(doom-files-in doom-modules-dir (doom-files-in doom-modules-dir
@ -371,192 +269,11 @@ ones."
for doom--current-module = key for doom--current-module = key
do (doom--read-module-packages-file path nil t))) do (doom--read-module-packages-file path nil t)))
(doom--read-module-packages-file private-packages all-p t)) (doom--read-module-packages-file private-packages all-p t))
(append (cl-loop for package in doom-core-packages (nreverse doom-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)))
;; ;;
;; Main functions ;;; 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
;;;###autoload ;;;###autoload
(defun doom/reload-packages () (defun doom/reload-packages ()
@ -565,61 +282,3 @@ package.el as appropriate."
(message "Reloading packages") (message "Reloading packages")
(doom-initialize-packages t) (doom-initialize-packages t)
(message "Reloading packages...DONE")) (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)

View file

@ -1,7 +1,6 @@
;;; core/cli/autoloads.el -*- lexical-binding: t; -*- ;;; core/cli/autoloads.el -*- lexical-binding: t; -*-
(dispatcher! (autoloads a) (def-command! (autoloads a) ()
(doom-reload-autoloads nil 'force)
"Regenerates Doom's autoloads files. "Regenerates Doom's autoloads files.
It scans and reads autoload cookies (;;;###autoload) in core/autoload/*.el, 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). (created from the concatenated autoloads files of all installed packages).
It also caches `load-path', `Info-directory-list', `doom-disabled-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 ;; external variables
(defvar autoload-timestamps) (defvar autoload-timestamps)
@ -21,55 +21,45 @@ It also caches `load-path', `Info-directory-list', `doom-disabled-packages',
;; ;;
;;; Helpers ;;; 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) (defun doom-delete-autoloads-file (file)
"Delete FILE (an autoloads file) and accompanying *.elc file, if any." "Delete FILE (an autoloads file) and accompanying *.elc file, if any."
(cl-check-type file string) (cl-check-type file string)
(when (file-exists-p file) (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 (with-current-buffer buf
(set-buffer-modified-p nil)) (set-buffer-modified-p nil))
(kill-buffer buf)) (kill-buffer buf))
(delete-file file) (delete-file file)
(ignore-errors (delete-file (byte-compile-dest-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 () (defun doom--warn-refresh-session ()
(print! (bold (green "\nFinished!"))) (message "Restart or reload Doom Emacs for changes to take effect:\n")
(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 " M-x doom/restart-and-restore") (message " M-x doom/restart-and-restore")
(message " M-x doom/restart") (message " M-x doom/restart")
(message " M-x doom/reload")) (message " M-x doom/reload"))
(defun doom--reload-files (&rest files) (defun doom--reload-files (&rest files)
(if (not noninteractive) (if noninteractive
(dolist (file files) (add-hook 'doom-cli-post-execute-hook #'doom--warn-refresh-session)
(load-file (byte-compile-dest-file file))) (dolist (file files)
(add-hook 'kill-emacs-hook #'doom--warn-refresh-session))) (load-file (byte-compile-dest-file file)))))
(defun doom--byte-compile-file (file) (defun doom--byte-compile-file (file)
(let ((short-name (file-name-nondirectory file)) (let ((byte-compile-warnings (if doom-debug-mode byte-compile-warnings)))
(byte-compile-dynamic-docstrings t))
(condition-case e (condition-case e
(when (byte-compile-file file) (when (byte-compile-file file)
;; Give autoloads file a chance to report error ;; Give autoloads file a chance to report error
(load (if doom-debug-mode (load (if doom-debug-mode
file file
(byte-compile-dest-file file)) (byte-compile-dest-file file))
nil t) nil t))
(unless noninteractive
(message "Finished compiling %s" short-name)))
((debug error) ((debug error)
(let ((backup-file (concat file ".bk"))) (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)) (copy-file file backup-file 'overwrite))
(doom-delete-autoloads-file file) (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) (defun doom-reload-autoloads (&optional file force-p)
"Reloads FILE (an autoload file), if it needs reloading. "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))) (signal 'wrong-type-argument (list 'stringp file)))
(if (stringp file) (if (stringp file)
(cond ((file-equal-p file doom-autoload-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) ((file-equal-p file doom-package-autoload-file)
(doom-reload-package-autoloads force-p)) (doom-reload-package-autoloads force-p))
((error "Invalid autoloads file: %s" file))) ((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-reload-package-autoloads force-p)))
;; ;;
;;; Doom autoloads ;;; 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) (defun doom--generate-header (func)
(goto-char (point-min)) (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")) ";; This file is autogenerated by `" (symbol-name func) "', DO NOT EDIT !!\n\n"))
(defun doom--generate-autoloads (targets) (defun doom--generate-autoloads (targets)
(require 'autoload) (let ((n 0))
(dolist (file targets) (dolist (file targets)
(let* ((file (file-truename file)) (insert
(generated-autoload-file doom-autoload-file) (with-temp-buffer
(generated-autoload-load-name (file-name-sans-extension file)) (cond ((not (doom-file-cookie-p file))
(noninteractive (not doom-debug-mode)) (print! (debug "Ignoring %s") (relpath file)))
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))))))
(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 (let ((load-path
;; NOTE With `doom-private-dir' in `load-path', Doom autoloads files ;; NOTE With `doom-private-dir' in `load-path', Doom autoloads files
;; will be unable to declare autoloads for the built-in autoload.el ;; will be unable to declare autoloads for the built-in autoload.el
;; Emacs package, should $DOOMDIR/autoload.el exist. Not sure why ;; Emacs package, should $DOOMDIR/autoload.el exist. Not sure why
;; they'd want to though, so it's an acceptable compromise. ;; 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 doom-modules-dirs
load-path)) (straight--directory-files (straight--build-dir) nil t)
cache) load-path)))
(while (re-search-forward "^\\s-*(autoload\\s-+'[^ ]+\\s-+\"\\([^\"]*\\)\"" nil t) (defvar doom--autoloads-path-cache nil)
(while (re-search-forward "^\\s-*(\\(?:custom-\\)?autoload\\s-+'[^ ]+\\s-+\"\\([^\"]*\\)\"" nil t)
(let ((path (match-string 1))) (let ((path (match-string 1)))
(replace-match (replace-match
(or (cdr (assoc path cache)) (or (cdr (assoc path doom--autoloads-path-cache))
(when-let* ((libpath (locate-library path)) (when-let* ((libpath (or (and allow-internal-paths
(libpath (file-name-sans-extension libpath))) (locate-library path nil (cons doom-emacs-dir doom-modules-dirs)))
(push (cons path (abbreviate-file-name libpath)) cache) (locate-library path)))
(libpath (file-name-sans-extension libpath))
(libpath (abbreviate-file-name libpath)))
(push (cons path libpath) doom--autoloads-path-cache)
libpath) libpath)
path) path)
t t nil 1))))) 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) (defun doom--generate-autodefs (targets enabled-targets)
(goto-char (point-max)) (goto-char (point-max))
(search-backward ";;;***" nil t) (search-backward ";;;***" nil t)
@ -155,83 +207,17 @@ even if it doesn't need reloading!"
(insert (insert
(with-temp-buffer (with-temp-buffer
(insert-file-contents path) (insert-file-contents path)
(let ((member-p (or (member path enabled-targets) (if-let (forms (doom--generate-autodefs-1 path (member path enabled-targets)))
(file-in-directory-p path doom-core-dir))) (concat (mapconcat #'prin1-to-string (nreverse forms) "\n")
forms) "\n")
(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")
""))))))
(defun doom--cleanup-autoloads () (defun doom--cleanup-autoloads ()
(goto-char (point-min)) (goto-char (point-min))
(when (re-search-forward "^;;\\(;[^\n]*\\| no-byte-compile: t\\)\n" nil t) (when (re-search-forward "^;;\\(;[^\n]*\\| no-byte-compile: t\\)\n" nil t)
(replace-match "" t 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). "Refreshes `doom-autoload-file', if necessary (or if FORCE-P is non-nil).
It scans and reads autoload cookies (;;;###autoload) in core/autoload/*.el, 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." Run this whenever your `doom!' block, or a module autoload file, is modified."
(let* ((default-directory doom-emacs-dir) (let* ((default-directory doom-emacs-dir)
(doom-modules (doom-modules)) (doom-modules (doom-modules))
(abbreviated-home-dir (if IS-WINDOWS "\\`'" abbreviated-home-dir))
(targets ;; The following bindings are in `package-generate-autoloads'.
(file-expand-wildcards ;; Presumably for a good reason, so I just copied them
(expand-file-name "autoload/*.el" doom-core-dir))) (noninteractive t)
(enabled-targets (copy-sequence targets)) (backup-inhibited t)
case-fold-search) (version-control 'never)
(dolist (path (doom-module-load-path t)) (case-fold-search nil) ; reduce magit
(let* ((auto-dir (expand-file-name "autoload" path)) (autoload-timestamps nil)
(auto-file (expand-file-name "autoload.el" path))
(module (doom-module-from-path auto-file)) ;; Where we'll store the files we'll scan for autoloads. This should
(module-p (or (doom-module-p (car module) (cdr module)) ;; contain *all* autoload files, even in disabled modules, so we can
(file-equal-p path doom-private-dir)))) ;; scan those for autodefs. We start with the core libraries.
(when (file-exists-p auto-file) (targets (doom-glob doom-core-dir "autoload/*.el"))
(push auto-file targets) ;; A subset of `targets' in enabled modules
(if module-p (push auto-file enabled-targets))) (active-targets (copy-sequence targets)))
(dolist (file (doom-files-in auto-dir :match "\\.el$" :full t :sort nil))
(push file targets) (dolist (path (doom-module-load-path 'all-p))
(if module-p (push file enabled-targets))))) (when-let* ((files (cons (doom-glob path "autoload.el")
(if (and (not force-p) (doom-files-in (doom-path path "autoload")
(not doom-emacs-changed-p) :match "\\.el$")))
(file-exists-p doom-autoload-file) (files (delq nil files)))
(not (file-newer-than-file-p (expand-file-name "init.el" doom-private-dir) (appendq! targets files)
doom-autoload-file)) (when (or (doom-module-from-path path 'enabled-only)
(not (cl-loop for file in targets (file-equal-p path doom-private-dir))
if (file-newer-than-file-p file doom-autoload-file) (appendq! active-targets files))))
return t)))
(progn (print! (green "Doom core autoloads is up-to-date")) (print! (start "Checking core autoloads file"))
(doom-initialize-autoloads doom-autoload-file) (print-group!
nil) (if (and (not force-p)
(doom-delete-autoloads-file doom-autoload-file) (file-exists-p doom-autoload-file)
(message "Generating new autoloads.el") (not (file-newer-than-file-p doom-emacs-dir doom-autoload-file))
(make-directory (file-name-directory doom-autoload-file) t) (not (cl-loop for dir
(with-temp-file doom-autoload-file in (append (doom-glob doom-private-dir "init.el*")
(doom--generate-header 'doom-reload-doom-autoloads) targets)
(prin1 `(setq doom--modules-cache ',doom-modules) (current-buffer)) if (file-newer-than-file-p dir doom-autoload-file)
(save-excursion return t)))
(doom--generate-autoloads (reverse enabled-targets))) (ignore
;; Replace autoload paths (only for module autoloads) with absolute (print! (success "Skipping core autoloads, they are up-to-date"))
;; paths for faster resolution during load and simpler `load-path' (doom-initialize-autoloads doom-autoload-file))
(save-excursion (print! (start "Regenerating core autoloads file"))
(doom--expand-autoloads)
(print! (green "✓ Expanded module autoload paths"))) (if (doom-delete-autoloads-file doom-autoload-file)
;; Generates stub definitions for functions/macros defined in disabled (print! (success "Deleted old %s") (filename doom-autoload-file))
;; modules, so that you will never get a void-function when you use (make-directory (file-name-directory doom-autoload-file) t))
;; them.
(save-excursion (with-temp-file doom-autoload-file
(doom--generate-autodefs (reverse targets) enabled-targets) (doom--generate-header 'doom-reload-core-autoloads)
(print! (green "✓ Generated autodefs"))) (save-excursion
;; Remove byte-compile-inhibiting file variables so we can byte-compile (doom--generate-autoloads active-targets)
;; the file, and autoload comments. (print! (success "Generated new autoloads.el")))
(doom--cleanup-autoloads) ;; Replace autoload paths (only for module autoloads) with absolute
(print! (green "✓ Clean up autoloads"))) ;; paths for faster resolution during load and simpler `load-path'
;; Byte compile it to give the file a chance to reveal errors. (save-excursion
(doom--byte-compile-file doom-autoload-file) (doom--expand-autoload-paths 'allow-internal-paths)
(doom--reload-files doom-autoload-file) (print! (success "Expanded module autoload paths")))
t))) ;; 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 () (defun doom--generate-package-autoloads ()
"Concatenates package autoload files, let-binds `load-file-name' around "Concatenates package autoload files, let-binds `load-file-name' around
them,and remove unnecessary `provide' statements or blank links. them,and remove unnecessary `provide' statements or blank links."
(dolist (pkg (straight--directory-files (straight--build-dir)))
Skips over packages in `doom-autoload-excluded-packages'." (let ((file (straight--autoloads-file pkg)))
(dolist (spec (doom-get-package-alist)) (when (file-exists-p file)
(if-let* ((pkg (car spec)) (insert-file-contents file)
(desc (cdr spec))) (when (save-excursion
(unless (memq pkg doom-autoload-excluded-packages) (and (re-search-forward "\\_<load-file-name\\_>" nil t)
(let ((file (concat (package--autoloads-file-name desc) ".el"))) (not (nth 8 (syntax-ppss)))))
(when (file-exists-p file) ;; Set `load-file-name' so that the contents of autoloads
(insert "(let ((load-file-name " (prin1-to-string (abbreviate-file-name file)) "))\n") ;; files can pretend they're in the file they're expected to
(insert-file-contents file) ;; be in, rather than `doom-package-autoload-file'.
(while (re-search-forward "^\\(?:;;\\(.*\n\\)\\|\n\\|(provide '[^\n]+\\)" nil t) (insert (format "(setq load-file-name %S)\n" (abbreviate-file-name file))))
(unless (nth 8 (syntax-ppss)) (while (re-search-forward "^\\(?:;;\\(.*\n\\)\\|\n\\|(provide '[^\n]+\\)" nil t)
(replace-match "" t t))) (unless (nth 8 (syntax-ppss))
(unless (bolp) (insert "\n")) (replace-match "" t t)))
(insert ")\n")))) (unless (bolp) (insert "\n"))))))
(message "Couldn't find package desc for %s" (car spec)))))
(defun doom--generate-var-cache () (defun doom--generate-var-cache ()
"Print a `setq' form for expensive-to-initialize variables, so we can cache "Print a `setq' form for expensive-to-initialize variables, so we can cache
them in Doom's autoloads file." them in Doom's autoloads file."
(doom-initialize-packages) (doom-initialize-packages)
(prin1 `(setq load-path ',load-path (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 Info-directory-list ',Info-directory-list
doom-disabled-packages ',(mapcar #'car (doom-find-packages :disabled t)) doom-disabled-packages ',doom-disabled-packages)
package-activated-list ',package-activated-list)
(current-buffer))) (current-buffer)))
(defun doom--cleanup-package-autoloads () (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. FORCE-P (universal argument) is non-nil, regenerate it anyway.
This should be run whenever your `doom!' block or update your packages." This should be run whenever your `doom!' block or update your packages."
(let ((abbreviated-home-dir (if IS-WINDOWS "\\`'" abbreviated-home-dir))) (print! (start "Checking package autoloads file"))
(if (and (not force-p) (print-group!
(not doom-emacs-changed-p) (if (and (not force-p)
(file-exists-p doom-package-autoload-file) (file-exists-p doom-package-autoload-file)
(not (file-newer-than-file-p doom-packages-dir doom-package-autoload-file)) (not (file-newer-than-file-p doom-elpa-dir doom-package-autoload-file))
(not (ignore-errors (not (ignore-errors
(cl-loop for key being the hash-keys of (doom-modules) (cl-loop for dir in (straight--directory-files (straight--repos-dir))
for path = (doom-module-path (car key) (cdr key) "packages.el") if (cl-find-if (lambda (dir) (file-newer-than-file-p dir doom-package-autoload-file))
if (file-newer-than-file-p path doom-package-autoload-file) (glob! (straight--repos-dir dir) "*.el"))
return t)))) return t)))
(ignore (print! (green "Doom package autoloads is up-to-date")) (not (ignore-errors
(doom-initialize-autoloads doom-package-autoload-file)) (cl-loop for key being the hash-keys of (doom-modules)
(let (case-fold-search) for path = (doom-module-path (car key) (cdr key) "packages.el")
(doom-delete-autoloads-file doom-package-autoload-file) if (file-newer-than-file-p path doom-package-autoload-file)
(with-temp-file doom-package-autoload-file return t))))
(doom--generate-header 'doom-reload-package-autoloads) (ignore
(save-excursion (print! (success "Skipping package autoloads, they are up-to-date"))
;; Cache important and expensive-to-initialize state here. (doom-initialize-autoloads doom-package-autoload-file))
(doom--generate-var-cache) (let (;; The following bindings are in `package-generate-autoloads'.
(print! (green "✓ Cached package state")) ;; Presumably for a good reason, so I just copied them
;; Concatenate the autoloads of all installed packages. (noninteractive t)
(doom--generate-package-autoloads) (backup-inhibited t)
(print! (green "✓ Package autoloads included"))) (version-control 'never)
;; Remove `load-path' and `auto-mode-alist' modifications (most of them, (case-fold-search nil) ; reduce magit
;; at least); they are cached later, so all those membership checks are (autoload-timestamps nil))
;; unnecessary overhead. (print! (start "Regenerating package autoloads file"))
(doom--cleanup-package-autoloads)
(print! (green "✓ Removed load-path/auto-mode-alist entries")))) (if (doom-delete-autoloads-file doom-package-autoload-file)
(doom--byte-compile-file doom-package-autoload-file) (print! (success "Deleted old %s") (filename doom-package-autoload-file))
(doom--reload-files doom-package-autoload-file) (make-directory (file-name-directory doom-autoload-file) t))
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))

View file

@ -1,21 +1,24 @@
;;; core/cli/byte-compile.el -*- lexical-binding: t; -*- ;;; 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. "Byte-compiles your config or selected modules.
compile [TARGETS...] compile [TARGETS...]
compile :core :private lang/python compile :core :private lang/python
compile feature lang compile feature lang
Accepts :core, :private and :plugins as special arguments, indicating you want Accepts :core and :private as special arguments, which target Doom's core files
to byte-compile Doom's core files, your private config or your ELPA plugins, and your private config files, respectively. To recompile your packages, use
respectively.") 'doom rebuild' instead."
(doom-byte-compile targets))
(dispatcher! (recompile rc) (doom-byte-compile args 'recompile) (def-command! (recompile rc) (&rest targets)
"Re-byte-compiles outdated *.elc files.") "Re-byte-compiles outdated *.elc files."
(doom-byte-compile targets 'recompile))
(dispatcher! clean (doom-clean-byte-compiled-files) (def-command! clean ()
"Delete all *.elc files.") "Delete all *.elc files."
(doom-clean-byte-compiled-files))
;; ;;
@ -25,9 +28,10 @@ respectively.")
(let ((filename (file-name-nondirectory path))) (let ((filename (file-name-nondirectory path)))
(or (string-prefix-p "." filename) (or (string-prefix-p "." filename)
(string-prefix-p "test-" 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. "Byte compiles your emacs configuration.
init.el is always byte-compiled by this. 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." If RECOMPILE-P is non-nil, only recompile out-of-date files."
(let ((default-directory doom-emacs-dir) (let ((default-directory doom-emacs-dir)
(total-ok 0) (doom-modules (doom-modules))
(total-fail 0) (byte-compile-verbose doom-debug-mode)
(total-noop 0) (byte-compile-warnings '(not free-vars unresolved noruntime lexical make-local))
compile-plugins-p
;; In case it is changed during compile-time
(auto-mode-alist auto-mode-alist)
(noninteractive t)
targets) targets)
(dolist (module (delete-dups modules) (nreverse targets))
(pcase module (let (target-dirs)
(":core" (push doom-core-dir targets)) (dolist (module (delete-dups modules))
(":private" (push doom-private-dir targets)) (pcase module
(":plugins" (":core"
(cl-loop for (_name . desc) in (doom-get-package-alist) (push (doom-glob doom-emacs-dir "init.el") targets)
do (package--compile desc)) (push doom-core-dir target-dirs))
(setq compile-plugins-p t (":private"
modules (delete ":plugins" modules))) (push doom-private-dir target-dirs))
((pred file-directory-p) ((pred file-directory-p)
(push module targets)) (push module target-dirs))
((pred (string-match "^\\([^/]+\\)/\\([^/]+\\)$")) ((pred (string-match "^\\([^/]+\\)/\\([^/]+\\)$"))
(push (doom-module-locate-path (push (doom-module-locate-path
(doom-keyword-intern (match-string 1 module)) (doom-keyword-intern (match-string 1 module))
(intern (match-string 2 module))) (intern (match-string 2 module)))
targets)))) target-dirs))))
(cl-block 'byte-compile
;; If we're just here to byte-compile our plugins, we're done! (and (or (null modules) (member ":private" modules))
(and (not modules) (not recompile-p)
compile-plugins-p (not (or doom-auto-accept
(cl-return-from 'byte-compile t))
(unless (or (equal modules '(":core"))
recompile-p)
(unless (or doom-auto-accept
(y-or-n-p (y-or-n-p
(concat "Warning: byte compiling is for advanced users. It will interfere with your\n" (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" "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" "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" "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" "`bin/doom clean` to clear out your *.elc files.\n\n"
"Byte-compile anyway?"))) "Byte-compile anyway?"))))
(message "Aborting.") (user-error "Aborting"))
(cl-return-from 'byte-compile)))
(when (and (not recompile-p) ;; But first we must be sure that Doom and your private config have been
(or (null modules) ;; fully loaded. Which usually aren't so in an noninteractive session.
(equal modules '(":core")))) (doom-initialize-autoloads doom-autoload-file)
(doom-clean-byte-compiled-files)) (doom-initialize-autoloads doom-package-autoload-file)
(let (doom-emacs-changed-p
noninteractive) ;;
;; But first we must be sure that Doom and your private config have been (unless target-dirs
;; fully loaded. Which usually aren't so in an noninteractive session. (push (doom-glob doom-emacs-dir "init.el") targets)
(unless (and (doom-initialize-autoloads doom-autoload-file) ;; If no targets were supplied, then we use your module list.
(doom-initialize-autoloads doom-package-autoload-file)) (appendq! target-dirs
(doom-reload-autoloads)) (list doom-core-dir)
(doom-initialize) ;; Omit `doom-private-dir', which is always first
(doom-initialize-modules 'force)) (cl-remove-if-not (lambda (path) (file-in-directory-p path doom-emacs-dir))
;; If no targets were supplied, then we use your module list. (cdr (doom-module-load-path)))))
(unless modules
(let ((doom-modules-dirs (delete (expand-file-name "modules/" doom-private-dir) ;; Assemble el files we want to compile; taking into account that MODULES
doom-modules-dirs))) ;; may be a list of MODULE/SUBMODULE strings from the command line.
(setq targets (appendq! targets
(append (list doom-core-dir) (doom-files-in target-dirs
(delete doom-private-dir (doom-module-load-path)))))) :match "\\.el$"
;; Assemble el files we want to compile; taking into account that :filter #'doom--byte-compile-ignore-file-p)))
;; MODULES may be a list of MODULE/SUBMODULE strings from the command
;; line. (unless targets
(let ((target-files (doom-files-in targets :filter #'doom--byte-compile-ignore-file-p :sort nil))) (print!
(when (or (not modules) (if targets
(member ":core" modules)) (warn "Couldn't find any valid targets")
(push (expand-file-name "init.el" doom-emacs-dir) (info "No targets to %scompile" (if recompile-p "re" ""))))
target-files)) (cl-return nil))
(unless target-files
(if targets (print!
(message "Couldn't find any valid targets") (info (if recompile-p
(message "No targets to %scompile" (if recompile-p "re" ""))) "Recompiling stale elc files..."
(cl-return-from 'byte-compile)) "Byte-compiling your config (may take a while)...")))
(require 'use-package) (print-group!
(condition-case e (require 'use-package)
(let ((use-package-defaults use-package-defaults) (condition-case e
(use-package-expand-minimally t) (let ((total-ok 0)
(load-path load-path) (total-fail 0)
kill-emacs-hook kill-buffer-query-functions) (total-noop 0)
;; Prevent packages from being loaded at compile time if they (use-package-defaults use-package-defaults)
;; don't meet their own predicates. (use-package-expand-minimally t)
(push (list :no-require t kill-emacs-hook kill-buffer-query-functions)
(lambda (_name args) ;; Prevent packages from being loaded at compile time if they
(or (when-let (pred (or (plist-get args :if) ;; don't meet their own predicates.
(plist-get args :when))) (push (list :no-require t
(not (eval pred t))) (lambda (_name args)
(when-let (pred (plist-get args :unless)) (or (when-let (pred (or (plist-get args :if)
(eval pred t))))) (plist-get args :when)))
use-package-defaults) (not (eval pred t)))
(dolist (target (cl-delete-duplicates (mapcar #'file-truename target-files) :test #'equal)) (when-let (pred (plist-get args :unless))
(if (or (not recompile-p) (eval pred t)))))
(let ((elc-file (byte-compile-dest-file target))) use-package-defaults)
(and (file-exists-p elc-file)
(file-newer-than-file-p target elc-file)))) (unless recompile-p
(let ((result (if (or (string-match-p "/\\(?:packages\\|doctor\\)\\.el$" target) (doom-clean-byte-compiled-files))
(not (doom--file-cookie-p target)))
'no-byte-compile (dolist (target (delete-dups targets))
(byte-compile-file target))) (cl-incf
(short-name (if (file-in-directory-p target doom-emacs-dir) (if (not (or (not recompile-p)
(file-relative-name target doom-emacs-dir) (let ((elc-file (byte-compile-dest-file target)))
(abbreviate-file-name target)))) (and (file-exists-p elc-file)
(cl-incf (file-newer-than-file-p target elc-file)))))
(cond ((eq result 'no-byte-compile) total-noop
(print! (dark (white "⚠ Ignored %s")) short-name) (pcase (if (doom-file-cookie-p target)
total-noop) (byte-compile-file target)
((null result) 'no-byte-compile)
(print! (red "✕ Failed to compile %s") short-name) (`no-byte-compile
total-fail) (print! (info "Ignored %s") (relpath target))
(t total-noop)
(print! (green "✓ Compiled %s") short-name) (`nil
(load target t t) (print! (error "Failed to compile %s") (relpath target))
total-ok)))) total-fail)
(cl-incf total-noop))) (_
(print! (bold (color (if (= total-fail 0) 'green 'red) (print! (success "Compiled %s") (relpath target))
"%s %d/%d file(s) (%d ignored)")) (load target t t)
(if recompile-p "Recompiled" "Compiled") total-ok)))))
total-ok (- (length target-files) total-noop) (print! (class (if (= total-fail 0) 'success 'error)
total-noop) "%s %d/%d file(s) (%d ignored)")
(or (= total-fail 0) (if recompile-p "Recompiled" "Compiled")
(error "Failed to compile some files"))) total-ok (- (length targets) total-noop)
((debug error) total-noop)
(print! (red "\nThere were breaking errors.\n\n%s") t)
"Reverting changes...") ((debug error)
(signal 'doom-error (list 'byte-compile e)))))))) (print! (error "\nThere were breaking errors.\n\n%s")
"Reverting changes...")
(signal 'doom-error (list 'byte-compile e)))))))
(defun doom-clean-byte-compiled-files () (defun doom-clean-byte-compiled-files ()
"Delete all the compiled elc files in your Emacs configuration and private "Delete all the compiled elc files in your Emacs configuration and private
module. This does not include your byte-compiled, third party packages.'" module. This does not include your byte-compiled, third party packages.'"
(cl-loop with default-directory = doom-emacs-dir (print! (start "Cleaning .elc files"))
for path (print-group!
in (append (doom-files-in doom-emacs-dir :match "\\.elc$" :depth 0 :sort nil) (cl-loop with default-directory = doom-emacs-dir
(doom-files-in doom-private-dir :match "\\.elc$" :depth 1 :sort nil) with success = nil
(doom-files-in doom-core-dir :match "\\.elc$" :sort nil) for path
(doom-files-in doom-modules-dirs :match "\\.elc$" :depth 4 :sort nil)) in (append (doom-glob doom-emacs-dir "*.elc")
for truepath = (file-truename path) (doom-files-in doom-private-dir :match "\\.elc$" :depth 1 :sort nil)
if (file-exists-p path) (doom-files-in doom-core-dir :match "\\.elc$" :sort nil)
do (delete-file path) (doom-files-in doom-modules-dirs :match "\\.elc$" :depth 4 :sort nil))
and do if (file-exists-p path)
(print! (green "✓ Deleted %s") do (delete-file path)
(if (file-in-directory-p truepath default-directory) and do (print! (success "Deleted %s") (relpath path))
(file-relative-name truepath) and do (setq success t)
(abbreviate-file-name truepath))) finally do
finally do (print! (bold (green "Everything is clean"))))) (print! (if success
(success "All elc files deleted")
(info "No elc files to clean"))))))

View file

@ -1,7 +1,26 @@
;;; core/cli/debug.el -*- lexical-binding: t; -*- ;;; core/cli/debug.el -*- lexical-binding: t; -*-
(dispatcher! info (doom/info) (load! "autoload/debug" doom-core-dir)
"Output system info in markdown for bug reports.")
(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)

View file

@ -1,25 +1,6 @@
;;; core/cli/env.el -*- lexical-binding: t; -*- ;;; core/cli/env.el -*- lexical-binding: t; -*-
(dispatcher! env (def-command! env (&optional command)
(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."))))
"Manages your envvars file. "Manages your envvars file.
env [SUBCOMMAND] env [SUBCOMMAND]
@ -38,7 +19,18 @@ on Linux).
To generate a file, run `doom env refresh`. If you'd like this file to be 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 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." `doom-env-ignored-vars' are removed."
(when (or force-p (not (file-exists-p doom-env-file))) (when (or force-p (not (file-exists-p doom-env-file)))
(with-temp-file 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) (if (file-exists-p doom-env-file)
"Regenerating" "Regenerating"
"Generating") "Generating")
(abbreviate-file-name doom-env-file)) (relpath doom-env-file doom-emacs-dir))
(let ((process-environment doom-site-process-environment)) (let ((process-environment doom--initial-process-environment))
(insert (insert
(concat (concat
"# -*- mode: dotenv -*-\n" "# -*- 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" "# 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" "# set DOOMENV=1 in your shell environment/config.\n"
"# ---------------------------------------------------------------------------\n\n")) "# ---------------------------------------------------------------------------\n\n"))
(let ((shell-command-switch doom-env-switches)) (let ((shell-command-switch doom-env-switches)
(message "Scraping env from '%s %s %s'" (error-buffer (get-buffer-create "*env errors*")))
shell-file-name (print! (info "Scraping shell environment with '%s %s %s'")
shell-command-switch (filename shell-file-name)
doom-env-executable) shell-command-switch
(filename doom-env-executable))
(save-excursion (save-excursion
(insert (shell-command-to-string doom-env-executable))) (shell-command doom-env-executable (current-buffer) error-buffer))
;; Remove undesireable variables (print-group!
(while (re-search-forward "\n\\([^= \n]+\\)=" nil t) (let ((errors (with-current-buffer error-buffer (buffer-string))))
(save-excursion (unless (string-empty-p errors)
(let* ((valend (or (save-match-data (print! (info "Error output:\n\n%s") (indent 4 errors))))
(when (re-search-forward "^\\([^= ]+\\)=" nil t) ;; Remove undesireable variables
(line-beginning-position))) (while (re-search-forward "\n\\([^= \n]+\\)=" nil t)
(point-max))) (save-excursion
(var (match-string 1)) (let* ((valend (or (save-match-data
(value (buffer-substring-no-properties (point) (1- valend)))) (when (re-search-forward "^\\([^= ]+\\)=" nil t)
(when (cl-loop for regexp in doom-env-ignored-vars (line-beginning-position)))
if (string-match-p regexp var) (point-max)))
return t) (var (match-string 1)))
(message "Ignoring %s" var) (when (cl-loop for regexp in doom-env-ignored-vars
(delete-region (match-beginning 0) (1- valend)))))) if (string-match-p regexp var)
(print! (green "Envvar successfully generated"))))))) 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)))))

View file

@ -1,55 +1,47 @@
;; -*- no-byte-compile: t; -*- ;; -*- no-byte-compile: t; -*-
;;; core/cli/packages.el ;;; core/cli/packages.el
;; (defmacro doom--ensure-autoloads-while (&rest body)
;;; Helpers `(progn
(doom-reload-core-autoloads)
(defmacro doom--condition-case! (&rest body) (when (progn ,@body)
`(condition-case-unless-debug e (doom-reload-package-autoloads 'force-p))
(progn ,@body) t))
('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)))
;; ;;
;;; Dispatchers ;;; Dispatchers
(dispatcher! (install i) (def-command! (update u) ()
(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)
"Updates packages. "Updates packages.
This excludes packages whose `package!' declaration contains a non-nil :freeze 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) (def-command! (rebuild b) ()
(doom--ensure-autoloads-while #'doom-packages-autoremove) "Rebuilds all installed packages.
"Removes packages that are no longer needed.
This includes packages installed with 'M-x package-install' without an This ensures that all needed files are symlinked from their package repo and
accompanying `package!' declaration in an enabled module's packages.el file or their elisp files are byte-compiled."
your private one.") (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
;; "<Not implemented yet>"
;; (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 Unless AUTO-ACCEPT-P is non-nil, this function will prompt for confirmation with
a list of packages that will be installed." a list of packages that will be installed."
(print! "Looking for packages to install...") (print! "> Installing & building packages...")
(let ((packages (doom-get-missing-packages))) (print-group!
(cond ((not packages) (let ((n 0))
(print! (green "No packages to install!")) (dolist (package (hash-table-keys straight--recipe-cache))
nil) (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) (defun doom-packages-rebuild (&optional auto-accept-p all)
(doom-refresh-packages-maybe doom-debug-mode) "(Re)build all packages."
(dolist (pkg packages) (print! (start "(Re)building %spackages...") (if all "all " ""))
(print! "Installing %s" (car pkg)) (print-group!
(doom--condition-case! (let ((n 0))
(let ((result (if all
(or (and (doom-package-installed-p (car pkg)) (let ((straight--packages-to-rebuild :all)
(not (doom-package-different-backend-p (car pkg))) (straight--packages-not-to-rebuild (make-hash-table :test #'equal)))
(not (doom-package-different-recipe-p (car pkg))) (dolist (package (hash-table-keys straight--recipe-cache))
'already-installed) (straight-use-package
(and (doom-install-package (car pkg) (cdr pkg)) (intern package) nil (lambda (_) (cl-incf n) nil) " ")))
(setq success t) (let ((straight-check-for-modifications '(find-when-checking)))
'success) (straight-check-all)
'failure)) (dolist (recipe (hash-table-values straight--recipe-cache))
(pin-label (straight--with-plist recipe (package local-repo no-build)
(and (plist-member (cdr pkg) :pin) (unless (or no-build (null local-repo))
(format " [pinned: %s]" (plist-get (cdr pkg) :pin))))) ;; REVIEW We do these modification checks manually because
(print! "%s%s" ;; Straight's checks seem to miss stale elc files. Need
(pcase result ;; more tests to confirm this.
(`already-installed (dark (white "⚠ ALREADY INSTALLED"))) (when (or (gethash package straight--cached-package-modifications)
(`success (green "✓ DONE")) (file-newer-than-file-p (straight--repos-dir local-repo)
(`failure (red "✕ FAILED"))) (straight--build-dir package))
(or pin-label ""))))) (cl-loop for file
(print! (bold (green "Finished!"))) in (doom-files-in (straight--build-dir package)
(when success :match "\\.el$"
(set-file-times doom-packages-dir) :full t)
(doom-delete-autoloads-file doom-package-autoload-file)) for elc-file = (byte-compile-dest-file file)
success))))) 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) (defun doom-packages-update (&optional auto-accept-p)
"Updates packages. "Updates packages.
Unless AUTO-ACCEPT-P is non-nil, this function will prompt for confirmation with Unless AUTO-ACCEPT-P is non-nil, this function will prompt for confirmation with
a list of packages that will be updated." a list of packages that will be updated."
(print! "Looking for outdated packages...") (print! (start "Scanning for outdated packages (this may take a while)..."))
(let ((packages (cl-sort (cl-copy-list (doom-get-outdated-packages)) #'string-lessp (print-group!
:key #'car))) ;; REVIEW Does this fail gracefully enough? Is it error tolerant?
(cond ((not packages) ;; TODO Add version-lock checks; don't want to spend all this effort on
(print! (green "Everything is up-to-date")) ;; packages that shouldn't be updated
nil) (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) (defun doom--packages-to-purge ()
(dolist (pkg packages) (let (builds repos)
(print! "Updating %s" (car pkg)) (dolist (name (straight--directory-files (straight--repos-dir)))
(doom--condition-case! (unless (straight--checkhash name straight--repo-cache)
(print! (push name repos)))
(let ((result (doom-update-package (car pkg) t))) (dolist (name (straight--directory-files (straight--build-dir)))
(when result (setq success t)) (unless (gethash name straight--profile-cache)
(color (if result 'green 'red) (push name builds)))
(if result "✓ DONE" "✕ FAILED")))))) (straight-prune-build-cache)
(print! (bold (green "Finished!"))) (list builds repos)))
(when success
(set-file-times doom-packages-dir)
(doom-delete-autoloads-file doom-package-autoload-file))
success)))))
(defun doom-packages-autoremove (&optional auto-accept-p) (defun doom-packages-purge (&optional auto-accept-p)
"Auto-removes orphaned packages. "Auto-removes orphaned packages and repos.
An orphaned package is a package that isn't a primary package (i.e. doesn't have 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. 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 Unless AUTO-ACCEPT-P is non-nil, this function will prompt for confirmation with
a list of packages that will be removed." a list of packages that will be removed."
(print! "Looking for orphaned packages...") (print! (start "Searching for orphaned packages..."))
(let ((packages (doom-get-orphaned-packages))) (cl-destructuring-bind (builds repos) (doom--packages-to-purge)
(cond ((not packages) (unless (bound-and-true-p package--initialized)
(print! (green "No unused packages to remove")) (package-initialize))
nil) (print-group!
(let ((packages (append builds (mapcar #'car package-alist) nil)))
((not (if (not packages)
(or auto-accept-p (ignore (print! (success "No orphaned packages to purge")))
(y-or-n-p (or auto-accept-p
(format "%s packages will be deleted:\n\n%s\n\nProceed?" (y-or-n-p
(length packages) (format! "\n%s\n\n%d packages are orphaned. Purge them (for the Emperor)?"
(mapconcat (mapconcat (lambda (pkgs)
(lambda (sym) (mapconcat (lambda (p) (format " + %-20.20s" p))
(let ((old-backend (doom-package-backend sym 'noerror)) pkgs
(new-backend (doom-package-recipe-backend sym 'noerror))) ""))
(format "+ %s (%s)" sym (seq-partition (cl-sort (copy-sequence packages) #'string-lessp)
(cond ((null new-backend) 3)
"removed") "\n")
((eq old-backend new-backend) (length packages)))
(symbol-name new-backend)) (user-error "Aborted"))
((format "%s -> %s" old-backend new-backend)))))) (let ((n 0))
(sort (cl-copy-list packages) #'string-lessp) (dolist (dir (append (mapcar #'straight--repos-dir repos)
"\n"))))) (mapcar #'straight--build-dir builds)))
(user-error "Aborted!")) (print! (info "Deleting %S") (relpath dir (straight--dir)))
(delete-directory dir 'recursive)
((let (success) (unless (file-directory-p dir)
(dolist (pkg packages) (cl-incf n)))
(doom--condition-case! (straight-prune-build-cache)
(let ((result (doom-delete-package pkg t))) (when (file-directory-p package-user-dir)
(if result (setq success t)) (delete-directory package-user-dir t)
(print! (color (if result 'green 'red) "%s %s") t)
(if result "✓ Removed" "✕ Failed to remove") (> n 0)))))))
pkg))))
(print! (bold (green "Finished!")))
(when success
(set-file-times doom-packages-dir)
(doom-delete-autoloads-file doom-package-autoload-file))
success)))))

View file

@ -1,9 +1,6 @@
;;; core/cli/patch-macos.el -*- lexical-binding: t; -*- ;;; core/cli/patch-macos.el -*- lexical-binding: t; -*-
(dispatcher! (patch-macos) (def-command! patch-macos ()
(doom-patch-macos (or (member "--undo" args)
(member "-u" args))
(doom--find-emacsapp-path))
"Patches Emacs.app to respect your shell environment. "Patches Emacs.app to respect your shell environment.
WARNING: This command is deprecated. Use 'doom env' instead. 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 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 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)))
;; ;;

View file

@ -1,7 +1,12 @@
;;; core/cli/quickstart.el -*- lexical-binding: t; -*- ;;; core/cli/quickstart.el -*- lexical-binding: t; -*-
(dispatcher! (quickstart qs) (apply #'doom-quickstart args) (def-command! quickstart (&rest args) ; DEPRECATED
"Guides you through setting up Doom for first time use. ""
: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: 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 The location of DOOMDIR can be changed with the -p option, or by setting the
DOOMDIR environment variable. e.g. DOOMDIR environment variable. e.g.
doom -p ~/.config/doom quickstart doom -p ~/.config/doom install
DOOMDIR=~/.config/doom doom quickstart 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-config Don't create DOOMDIR or dummy files therein
--no-install Don't auto-install packages --no-install Don't auto-install packages
--no-env Don't generate an envvars file (see `doom help env`) --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") --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'
;; 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)))
(if (member "--no-config" args) (if (member "--no-config" args)
(print! (yellow "Not copying private config template, as requested")) (print! (warn "Not copying private config template, as requested"))
(print! "Creating %s" short-private-dir) (print! "> Creating %s" (relpath doom-private-dir))
(make-directory doom-private-dir t) (make-directory doom-private-dir 'parents)
(print! (green "Done!")) (print! (success "Created %s") (relpath doom-private-dir))
;; Create init.el, config.el & packages.el ;; Create init.el, config.el & packages.el
(dolist (file (list (cons "init.el" (mapc (lambda (file)
(lambda () (cl-destructuring-bind (filename . fn) file
(insert-file-contents (expand-file-name "init.example.el" doom-emacs-dir)))) (if (file-exists-p! filename doom-private-dir)
(cons "config.el" (print! (warn "%s already exists, skipping") filename)
(lambda () (print! (info "Creating %s%s") (relpath doom-private-dir) filename)
(insert (format ";;; %sconfig.el -*- lexical-binding: t; -*-\n\n" (with-temp-file (doom-path doom-private-dir filename)
short-private-dir) (funcall fn))
";; Place your private configuration here\n"))) (print! (success "Done!")))))
(cons "packages.el" '(("init.el" .
(lambda () (lambda ()
(insert (format ";; -*- no-byte-compile: t; -*-\n;;; %spackages.el\n\n" (insert-file-contents (doom-dir "init.example.el"))))
short-private-dir) ("config.el" .
";;; Examples:\n" (lambda ()
";; (package! some-package)\n" (insert! ";;; %sconfig.el -*- lexical-binding: t; -*-\n\n"
";; (package! another-package :recipe (:fetcher github :repo \"username/repo\"))\n" ";; Place your private configuration here\n"
";; (package! builtin-package :disable t)\n"))))) ((relpath doom-private-dir)))))
(cl-destructuring-bind (path . fn) file ("packages.el" .
(if (file-exists-p! path doom-private-dir) (lambda ()
(print! "%s already exists, skipping" path) (insert! ";; -*- no-byte-compile: t; -*-\n;;; %spackages.el\n\n"
(print! "Creating %s%s" short-private-dir path) ";;; Examples:\n"
(with-temp-file (expand-file-name path doom-private-dir) ";; (package! some-package)\n"
(funcall fn)) ";; (package! another-package :recipe (:fetcher github :repo \"username/repo\"))\n"
(print! (green "Done!"))))))) ";; (package! builtin-package :disable t)\n"
((relpath doom-private-dir))))))))
;; In case no init.el was present the first time `doom-initialize-modules' was ;; In case no init.el was present the first time `doom-initialize-modules' was
;; called in core.el (e.g. on first install) ;; called in core.el (e.g. on first install)
(doom-initialize-packages 'force-p) (doom-initialize-packages 'force-p)
;; Ask if Emacs.app should be patched ;; Ask if Emacs.app should be patched
(if (member "--no-env" args) (if (member "--no-env" args)
(print! (yellow "Not generating envvars file, as requested")) (print! (warn "- Not generating envvars file, as requested"))
(when (or doom-auto-accept (when (or doom-auto-accept
(y-or-n-p "Generate an env file? (see `doom help env` for details)")) (y-or-n-p "Generate an env file? (see `doom help env` for details)"))
(doom-reload-env-file 'force-p))) (doom-reload-env-file 'force-p)))
;; Install Doom packages ;; Install Doom packages
(if (member "--no-install" args) (if (member "--no-install" args)
(print! (yellow "Not installing plugins, as requested")) (print! (warn "- Not installing plugins, as requested"))
(print! "Installing plugins") (print! "Installing plugins")
(doom-packages-install doom-auto-accept)) (doom-packages-install doom-auto-accept))
(print! "Regenerating autoloads files") (print! "Regenerating autoloads files")
(doom-reload-autoloads nil 'force-p) (doom-reload-autoloads nil 'force-p)
(if (member "--no-fonts" args) (if (member "--no-fonts" args)
(print! (yellow "Not installing fonts, as requested")) (print! (warn "- Not installing fonts, as requested"))
(when (or doom-auto-accept (when (or doom-auto-accept
(y-or-n-p "Download and install all-the-icon's fonts?")) (y-or-n-p "Download and install all-the-icon's fonts?"))
(require 'all-the-icons) (require 'all-the-icons)
(let ((window-system (cond (IS-MAC 'ns) (let ((window-system (cond (IS-MAC 'ns)
(IS-LINUX 'x)))) (IS-LINUX 'x))))
(all-the-icons-install-fonts 'yes)))) (all-the-icons-install-fonts 'yes))))
(print! (bold (green "\nFinished! Doom is ready to go!\n"))) (print! (success "\nFinished! Doom is ready to go!\n"))
(with-temp-buffer (with-temp-buffer
(doom-template-insert "QUICKSTART_INTRO") (doom-template-insert "QUICKSTART_INTRO")
(print! (buffer-string)))) (print! (buffer-string)))))

View file

@ -1,67 +1,73 @@
;;; core/cli/test.el -*- lexical-binding: t; -*- ;;; core/cli/test.el -*- lexical-binding: t; -*-
(dispatcher! test (doom-run-tests args) (def-command! test ()
"Run Doom unit tests.") "Run Doom unit tests.")
;; ;;
;; Library ;;; Library
(defun doom-run-tests (&optional modules) (defun doom-run-tests ()
"Run all loaded tests, specified by MODULES (a list of module cons cells) or "Discover and load test files, then run all defined suites.
command line args following a double dash (each arg should be in the
'module/submodule' format).
If neither is available, run all tests in all enabled modules." Takes directories as command line arguments, defaulting to the
;; Core libraries aren't fully loaded in a noninteractive session, so we current directory."
;; reload it with `noninteractive' set to nil to force them to. (let ((dirs nil)
(let* ((noninteractive t) (patterns nil)
(doom-modules (doom-modules))) (args command-line-args-left)
(quiet! (doom-reload-autoloads)) (doom-modules (doom-modules)))
(let ((target-paths (doom-initialize-autoloads doom-autoload-file)
;; Convert targets into a list of string paths, pointing to the root (doom-initialize-autoloads doom-package-autoload-file)
;; directory of modules (while args
(cond ((stringp (car modules)) ; command line (cond
(save-match-data ;; ((member (car args) '("--traceback"))
(cl-loop for arg in modules ;; (when (not (cdr args))
if (string= arg ":core") collect doom-core-dir ;; (error "Option requires argument: %s" (car args)))
else if (string-match-p "/" arg) ;; ;; Make sure it's a valid style by trying to format a dummy
nconc (mapcar (apply-partially #'expand-file-name arg) ;; ;; frame with it
doom-modules-dirs) ;; (buttercup--format-stack-frame '(t myfun 1 2) (intern (cadr args)))
else ;; (setq buttercup-stack-frame-style (intern (cadr args)))
nconc (cl-loop for dir in doom-modules-dirs ;; (setq args (cddr args)))
for path = (expand-file-name arg dir) ;; ((member (car args) '("-p" "--pattern"))
if (file-directory-p path) ;; (when (not (cdr args))
nconc (doom-files-in path :type 'dirs :depth 1 :full t :sort nil)) ;; (error "Option requires argument: %s" (car args)))
finally do (setq argv nil)))) ;; (push (cadr args) patterns)
;; (setq args (cddr args)))
(modules ; cons-cells given to MODULES ;; ((member (car args) '("-c" "--no-color"))
(cl-loop for (module . submodule) in modules ;; (setq buttercup-color nil)
if (doom-module-locate-path module submodule) ;; (setq args (cdr args)))
collect it)) (t
(push (car args) dirs)
((append (list doom-core-dir) (setq args (cdr args)))))
(doom-module-load-path)))))) (setq command-line-args-left nil)
;; Load all the unit test files... (dolist (dir (or dirs '(".")))
(require 'buttercup) (setq dir (if (string= dir "core")
(mapc (lambda (file) (load file :noerror (not doom-debug-mode))) doom-core-dir
(doom-files-in (mapcar (apply-partially #'expand-file-name "test/") (expand-file-name dir doom-modules-dir)))
target-paths) (let ((test-dir (expand-file-name "test" dir)))
:match "\\.el$" :full t)) (when (or (string= dir doom-core-dir)
;; ... then run them (cl-destructuring-bind (category . module)
(when doom-debug-mode (or (doom-module-from-path dir)
(setq buttercup-stack-frame-style 'pretty)) (cons nil nil))
(let ((split-width-threshold 0) (and category module (doom-module-p category module))))
(split-height-threshold 0) (dolist (file (nreverse (doom-glob test-dir "test-*.el")))
(window-min-width 0) (when (doom-file-cookie-p file)
(window-min-height 0)) (load file nil t))))))
(buttercup-run))))) (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 ;; Test library
(defmacro insert! (&rest text) (defmacro insert!! (&rest text)
"Insert TEXT in buffer, then move cursor to last {0} marker." "Insert TEXT in buffer, then move cursor to last {0} marker."
`(progn `(progn
(insert ,@text) (insert ,@text)

View file

@ -1,7 +1,7 @@
;;; core/cli/upgrade.el -*- lexical-binding: t; -*- ;;; core/cli/upgrade.el -*- lexical-binding: t; -*-
(dispatcher! (upgrade up) (doom-upgrade) (def-command! (upgrade up) ()
"Checks out the latest Doom on this branch. "Updates Doom and packages.
Doing so is equivalent to: Doing so is equivalent to:
@ -9,7 +9,8 @@ Doing so is equivalent to:
git pull git pull
bin/doom clean bin/doom clean
bin/doom refresh bin/doom refresh
bin/doom update") bin/doom update"
(doom-upgrade))
;; ;;
@ -75,7 +76,7 @@ Doing so is equivalent to:
(buffer-string))) (buffer-string)))
(unless (equal (vc-git-working-revision doom-emacs-dir) rev) (unless (equal (vc-git-working-revision doom-emacs-dir) rev)
(error "Failed to checkout latest commit.\n\n%s" (buffer-string)))) (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) (when (doom-packages-update doom-auto-accept)
(doom-reload-package-autoloads)) (doom-reload-package-autoloads))
(message "Done! Please restart Emacs for changes to take effect"))) (message "Done! Please restart Emacs for changes to take effect")))

View file

@ -1,54 +1,82 @@
;;; -*- lexical-binding: t; no-byte-compile: t; -*- ;;; -*- 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") (defvar doom-auto-accept (getenv "YES")
"If non-nil, Doom will auto-accept any confirmation prompts during batch "If non-nil, Doom will auto-accept any confirmation prompts during batch
commands like `doom-packages-install', `doom-packages-update' and commands like `doom-packages-install', `doom-packages-update' and
`doom-packages-autoremove'.") `doom-packages-autoremove'.")
(defconst doom--dispatch-command-alist ()) (defvar doom-cli-pre-execute-hook nil
(defconst doom--dispatch-alias-alist ()) "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) (defun doom--dispatch-format (desc &optional short)
(with-temp-buffer (with-temp-buffer
(let ((fill-column 72)) (let ((fill-column 72))
(insert desc) (save-excursion
(goto-char (point-min)) (insert desc)
(while (re-search-forward "\n\n[^ \n]" nil t) (while (re-search-backward "\n\n[^ \n]" nil t)
(fill-paragraph))) (fill-paragraph))))
(if (not short) (if (not short)
(buffer-string) (buffer-string)
(goto-char (point-min)) (buffer-substring (line-beginning-position)
(buffer-substring-no-properties (line-end-position)))))
(line-beginning-position)
(line-end-position)))))
(defun doom--dispatch-help (&optional command desc &rest args) (defun doom--dispatch-help-1 (command)
"Display help documentation for a dispatcher command. If COMMAND and DESC are (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." omitted, show all available commands, their aliases and brief descriptions."
(if command (if fn
(princ (doom--dispatch-format desc)) (princ (documentation fn))
(print! (bold "%-10s\t%s\t%s" "Command:" "Alias" "Description")) (print! (bold "%-11s\t%s\t%s" "Command:" "Alias" "Description"))
(dolist (spec (cl-sort doom--dispatch-command-alist #'string-lessp (print-group!
:key #'car)) (dolist (group (seq-group-by (lambda (key) (plist-get (gethash key doom--cli-commands) :group))
(cl-destructuring-bind (command &key desc _body) spec (hash-table-keys doom--cli-commands)))
(let ((aliases (cl-loop for (alias . cmd) in doom--dispatch-alias-alist (if (null (car group))
if (eq cmd command) (mapc #'doom--dispatch-help-1 (cdr group))
collect (symbol-name alias)))) (print! "%-30s\t%s" (bold (car group)) (gethash (car group) doom--cli-groups))
(print! " %-10s\t%s\t%s" (print-group!
command (if aliases (string-join aliases ",") "") (mapc #'doom--dispatch-help-1 (cdr group))))
(doom--dispatch-format desc t))))))) (terpri)))))
(defun doom-dispatch (cmd args &optional show-help) (defun doom-dispatch (cmd args &optional show-help)
"Parses ARGS and invokes a dispatcher. "Parses ARGS and invokes a dispatcher.
@ -59,17 +87,31 @@ If SHOW-HELP is non-nil, show the documentation for said dispatcher."
(when args (when args
(setq cmd (car args) (setq cmd (car args)
args (cdr args)))) args (cdr args))))
(cl-destructuring-bind (command &key desc body) (let ((fn (doom--dispatch-command cmd)))
(let ((sym (intern cmd))) (unless (fboundp fn)
(or (assq sym doom--dispatch-command-alist) (user-error "%s is not any command *I* know!" fn))
(assq (cdr (assq sym doom--dispatch-alias-alist))
doom--dispatch-command-alist)
(user-error "Invalid command: %s" sym)))
(if show-help (if show-help
(apply #'doom--dispatch-help command desc args) (doom--dispatch-help fn args)
(funcall body 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 "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 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 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." BODY will be run when this dispatcher is called."
(declare (indent defun) (doc-string 3)) (declare (indent defun) (doc-string 3))
(cl-destructuring-bind (cmd &rest aliases) (let* ((names (mapcar #'symbol-name (doom-enlist names)))
(doom-enlist command) (fn (intern (format "doom-cli-%s" (car names))))
(plist (cl-loop while (keywordp (car body))
collect (pop body)
collect (pop body))))
(macroexp-progn (macroexp-progn
(append (reverse
(when aliases `((unless ,(plist-get plist :hidden)
`((dolist (alias ',aliases) (let ((plist ',plist))
(setf (alist-get alias doom--dispatch-alias-alist) ',cmd)))) (setq plist (plist-put plist :aliases ',(cdr names)))
`((setf (alist-get ',cmd doom--dispatch-command-alist) (unless (or (plist-member plist :group)
(list :desc ,docstring (null doom--cli-group))
:body (lambda (args) (ignore args) ,form)))))))) (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 ;; Eagerly load these libraries because this module may be loaded in a session
;; documentation for them, so... ;; that hasn't been fully initialized (where autoloads files haven't been
;; generated or `load-path' populated).
(dispatcher! run :noop (load! "autoload/format")
"Run Doom Emacs from bin/doom's parent directory. (load! "autoload/packages")
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.")
;; ;; Load all of our subcommands
;; Real dispatch commands (def-command! (refresh re) (&optional force-p)
"Ensure Doom is properly set up.
(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.
This is the equivalent of running autoremove, install, autoloads, then This is the equivalent of running autoremove, install, autoloads, then
recompile. Run this whenever you: 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 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 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) (provide 'core-cli)
;;; core-cli.el ends here ;;; core-cli.el ends here

View file

@ -11,9 +11,6 @@
doom-modules-dir) doom-modules-dir)
"A list of module root directories. Order determines priority.") "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 (defconst doom-obsolete-modules
'((:feature (version-control (:emacs vc) (:ui vc-gutter)) '((:feature (version-control (:emacs vc) (:ui vc-gutter))
(spellcheck (:tools flyspell)) (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 your `doom!' block, a warning is emitted before replacing it with :emacs vc and
:ui vc-gutter.") :ui vc-gutter.")
(defvar doom--current-module nil) (defvar doom-inhibit-module-warnings (not noninteractive)
(defvar doom--current-flags nil) "If non-nil, don't emit deprecated or missing module warnings at startup.")
(defvar doom--modules-cache ())
;;
;;; Custom hooks ;;; Custom hooks
(defvar doom-before-init-modules-hook nil (defvar doom-before-init-modules-hook nil
"A list of hooks to run before Doom's modules' config.el files are loaded, but "A list of hooks to run before Doom's modules' config.el files are loaded, but
after their init.el files are loaded.") 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) (defvaralias 'doom-after-init-modules-hook 'after-init-hook)
(define-obsolete-variable-alias 'doom-post-init-hook 'doom-init-modules-hook "2.1.0") (defvar doom--current-module nil)
(define-obsolete-variable-alias 'doom-init-hook 'doom-before-init-modules-hook "2.1.0") (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 "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 session of Dooming. Will noop if used more than once, unless FORCE-P is
non-nil." non-nil."
(when (and (or force-p (when (or force-p (not doom-init-modules-p))
(not doom-init-modules-p)) (setq doom-init-modules-p t
(not (setq doom-modules nil)) doom-modules nil)
(load! "init" doom-private-dir t)) (when (load! "init" doom-private-dir t)
(setq doom-init-modules-p t) (when doom-modules
(unless (hash-table-p doom-modules) (maphash (lambda (key plist)
(setq doom-modules (make-hash-table :test 'equal))) (let ((doom--current-module key)
(maphash (lambda (key plist) (doom--current-flags (plist-get plist :flags)))
(let ((doom--current-module key) (load! "init" (plist-get plist :path) t)))
(doom--current-flags (plist-get plist :flags))) doom-modules))
(load! "init" (plist-get plist :path) t))) (run-hook-wrapped 'doom-before-init-modules-hook #'doom-try-run-hook)
doom-modules) (unless noninteractive
(run-hook-wrapped 'doom-before-init-modules-hook #'doom-try-run-hook) (when doom-modules
(unless noninteractive (maphash (lambda (key plist)
(maphash (lambda (key plist) (let ((doom--current-module key)
(let ((doom--current-module key) (doom--current-flags (plist-get plist :flags)))
(doom--current-flags (plist-get plist :flags))) (load! "config" (plist-get plist :path) t)))
(load! "config" (plist-get plist :path) t))) doom-modules))
doom-modules) (run-hook-wrapped 'doom-init-modules-hook #'doom-try-run-hook)
(run-hook-wrapped 'doom-init-modules-hook #'doom-try-run-hook) (load! "config" doom-private-dir t)))))
(load! "config" doom-private-dir t))))
;; ;;
@ -108,12 +100,11 @@ non-nil."
(defun doom-module-p (category module &optional flag) (defun doom-module-p (category module &optional flag)
"Returns t if CATEGORY MODULE is enabled (ie. present in `doom-modules')." "Returns t if CATEGORY MODULE is enabled (ie. present in `doom-modules')."
(declare (pure t) (side-effect-free t)) (declare (pure t) (side-effect-free t))
(when (hash-table-p doom-modules) (let ((plist (gethash (cons category module) doom-modules)))
(let ((plist (gethash (cons category module) doom-modules))) (and plist
(and plist (or (null flag)
(or (null flag) (memq flag (plist-get plist :flags)))
(memq flag (plist-get plist :flags))) t)))
t))))
(defun doom-module-get (category module &optional property) (defun doom-module-get (category module &optional property)
"Returns the plist for CATEGORY MODULE. Gets PROPERTY, specifically, if set." "Returns the plist for CATEGORY MODULE. Gets PROPERTY, specifically, if set."
@ -128,7 +119,7 @@ non-nil."
of PROPERTY and VALUEs. of PROPERTY and VALUEs.
\(fn CATEGORY MODULE PROPERTY VALUE &rest [PROPERTY VALUE [...]])" \(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 (progn
(when plist (when plist
(when (cl-oddp (length 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 If the category isn't enabled this will always return nil. For finding disabled
modules use `doom-module-locate-path'." modules use `doom-module-locate-path'."
(let ((path (doom-module-get category module :path)) (let ((path (doom-module-get category module :path)))
file-name-handler-alist) (if file
(if file (expand-file-name file path) (let (file-name-handler-alist)
(expand-file-name file path))
path))) path)))
(defun doom-module-locate-path (category &optional module file) (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) if (file-exists-p path)
return (expand-file-name path))) return (expand-file-name path)))
(defun doom-module-from-path (&optional path) (defun doom-module-from-path (&optional path enabled-only)
"Returns a cons cell (CATEGORY . MODULE) derived from PATH (a file path)." "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 (or doom--current-module
(let* (file-name-handler-alist (let* ((file-name-handler-alist nil)
(path (or path (FILE!)))) (path (file-truename (or path (file!)))))
(save-match-data (save-match-data
(setq path (file-truename path))
(when (string-match "/modules/\\([^/]+\\)/\\([^/]+\\)\\(?:/.*\\)?$" path) (when (string-match "/modules/\\([^/]+\\)/\\([^/]+\\)\\(?:/.*\\)?$" path)
(when-let* ((category (match-string 1 path)) (when-let* ((category (doom-keyword-intern (match-string 1 path)))
(module (match-string 2 path))) (module (intern (match-string 2 path))))
(cons (doom-keyword-intern category) (and (or (null enabled-only)
(intern module)))))))) (doom-module-p category module))
(cons category module))))))))
(defun doom-module-load-path (&optional all-p) (defun doom-module-load-path (&optional module-dirs)
"Return an unsorted list of absolute file paths to activated modules. "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)) (declare (pure t) (side-effect-free t))
(append (if all-p (append (list doom-private-dir)
(doom-files-in doom-modules-dirs (if module-dirs
(doom-files-in (if (listp module-dirs)
module-dirs
doom-modules-dirs)
:type 'dirs :type 'dirs
:mindepth 1 :mindepth 1
:depth 1 :depth 1)
:full t
:sort nil)
(cl-loop for plist being the hash-values of (doom-modules) (cl-loop for plist being the hash-values of (doom-modules)
collect (plist-get plist :path))) collect (plist-get plist :path)))
(list doom-private-dir))) nil))
(defun doom-modules (&optional refresh-p) (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) (or (unless refresh-p doom-modules)
(let ((noninteractive t) (let ((noninteractive t)
doom-modules 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' 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 for a list of all recognized module trees. Order defines precedence (from most
to least)." to least)."
(if doom--modules-cache (unless (keywordp (car modules))
(progn (setq modules (eval modules t)))
(setq doom-modules doom--modules-cache) (unless doom-modules
(doom-log "Using `doom-modules' cache")) (setq doom-modules
(unless doom-modules (make-hash-table :test 'equal
(setq doom-modules :size (if modules (length modules) 150)
(make-hash-table :test 'equal :rehash-threshold 1.0)))
:size (if modules (length modules) 150) (let ((inhibit-message doom-inhibit-module-warnings)
:rehash-threshold 1.0))) category m)
(let ((inhibit-message doom-inhibit-module-warnings) (while modules
category m) (setq m (pop modules))
(while modules (cond ((keywordp m) (setq category m))
(setq m (pop modules)) ((not category) (error "No module category specified for %s" m))
(cond ((keywordp m) (setq category m)) ((catch 'doom-modules
((not category) (error "No module category specified for %s" m)) (let* ((module (if (listp m) (car m) m))
((catch 'doom-modules (flags (if (listp m) (cdr m))))
(let* ((module (if (listp m) (car m) m)) (when-let* ((obsolete (assq category doom-obsolete-modules))
(flags (if (listp m) (cdr m)))) (new (assq module obsolete)))
(when-let* ((obsolete (assq category doom-obsolete-modules)) (let ((newkeys (cdr new)))
(new (assq module obsolete))) (if (null newkeys)
(let ((newkeys (cdr new))) (message "WARNING %s module was removed" key)
(if (null newkeys) (if (cdr newkeys)
(message "WARNING %s module was removed" key) (message "WARNING %s module was removed and split into the %s modules"
(if (cdr newkeys) (list category module) (mapconcat #'prin1-to-string newkeys ", "))
(message "WARNING %s module was removed and split into the %s modules" (message "WARNING %s module was moved to %s"
(list category module) (mapconcat #'prin1-to-string newkeys ", ")) (list category module) (car newkeys)))
(message "WARNING %s module was moved to %s" (push category modules)
(list category module) (car newkeys))) (dolist (key newkeys)
(push category modules) (push (if flags
(dolist (key newkeys) (nconc (cdr key) flags)
(push (if flags (cdr key))
(nconc (cdr key) flags) modules)
(cdr key)) (push (car key) modules))
modules) (throw 'doom-modules t))))
(push (car key) modules)) (if-let (path (doom-module-locate-path category module))
(throw 'doom-modules t)))) (doom-module-set category module :flags flags :path path)
(if-let (path (doom-module-locate-path category module)) (message "WARNING Couldn't find the %s %s module" category module))))))))
(doom-module-set category module :flags flags :path path)
(message "WARNING Couldn't find the %s %s module" category module)))))))))
(when noninteractive (when noninteractive
(setq doom-inhibit-module-warnings t)) (setq doom-inhibit-module-warnings t))
`(setq doom-modules ',doom-modules)) `(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)) (doom--current-flags (memq category doom--current-flags))
((let ((module-pair ((let ((module-pair
(or doom--current-module (or doom--current-module
(doom-module-from-path (FILE!))))) (doom-module-from-path (file!)))))
(unless module-pair (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))))) (memq category (doom-module-get (car module-pair) (cdr module-pair) :flags)))))
t)) t))

View file

@ -1,5 +1,7 @@
;;; core/core-packages.el -*- lexical-binding: t; -*- ;;; core/core-packages.el -*- lexical-binding: t; -*-
(require 'core-modules)
;; Emacs package management is opinionated, and so am I. I've bound together ;; Emacs package management is opinionated, and so am I. I've bound together
;; `use-package', `quelpa' and package.el to create my own, rolling-release, ;; `use-package', `quelpa' and package.el to create my own, rolling-release,
;; lazily-loaded package management system for Emacs. ;; lazily-loaded package management system for Emacs.
@ -32,123 +34,164 @@
;; ;;
;; See core/autoload/packages.el for more functions. ;; 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 () (defvar doom-packages ()
"A list of enabled packages. Each element is a sublist, whose CAR is the "A list of enabled packages. Each element is a sublist, whose CAR is the
package's name as a symbol, and whose CDR is the plist supplied to its package's name as a symbol, and whose CDR is the plist supplied to its
`package!' declaration. Set by `doom-initialize-packages'.") `package!' declaration. Set by `doom-initialize-packages'.")
(defvar doom-core-packages (defvar doom-core-packages '(straight use-package async)
'(persistent-soft use-package quelpa async)
"A list of packages that must be installed (and will be auto-installed if "A list of packages that must be installed (and will be auto-installed if
missing) and shouldn't be deleted.") 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 () (defvar doom-disabled-packages ()
"A list of packages that should be ignored by `def-package!' and `after!'.") "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 (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-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 ;; I omit Marmalade because its packages are manually submitted rather
;; than pulled, so packages are often out of date with upstream. ;; than pulled, so packages are often out of date with upstream.
package-archives package-archives
`(("gnu" . "https://elpa.gnu.org/packages/") (let ((proto (if gnutls-verify-error "http" "https")))
("melpa" . "https://melpa.org/packages/") `(("gnu" . ,(concat proto "://elpa.gnu.org/packages/"))
("org" . "https://orgmode.org/elpa/"))) ("melpa" . ,(concat proto "://melpa.org/packages/"))
("org" . ,(concat proto "://orgmode.org/elpa/")))))
;; Don't save `package-selected-packages' to `custom-file' ;; Don't save `package-selected-packages' to `custom-file'
(advice-add #'package--save-selected-packages :override (def-advice! doom--package-inhibit-custom-file-a (&optional value)
(lambda (&optional value) (if value (setq package-selected-packages value)))) :override #'package--save-selected-packages
(if value (setq package-selected-packages value)))
(when (or (not gnutls-verify-error) ;;; straight
(not (ignore-errors (gnutls-available-p)))) (setq straight-cache-autoloads nil ; we already do this, and better.
(dolist (archive package-archives) ;; Doom doesn't encourage you to modify packages in place. Disabling this
(setcdr archive (replace-regexp-in-string "^https://" "http://" (cdr archive) t nil)))) ;; 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 ;; Straight is hardcoded to operate out of ~/.emacs.d/straight. Not on my watch!
(setq quelpa-dir (expand-file-name "quelpa" doom-packages-dir) (def-advice! doom--straight-use-local-dir-a (orig-fn &rest args)
quelpa-verbose doom-debug-mode :around #'straight--emacs-dir
(let ((user-emacs-directory doom-local-dir))
;; Don't track MELPA, we'll use package.el for that (apply orig-fn args)))
quelpa-checkout-melpa-p nil
quelpa-update-melpa-p nil
quelpa-melpa-recipe-stores nil
quelpa-self-upgrade-p nil)
;; ;;
;;; Bootstrapper ;;; Bootstrapper
(defun doom-initialize-packages (&optional force-p) (defun doom-initialize-packages (&optional force-p)
"Ensures that Doom's package management system, package.el and quelpa are "Ensures that Doom's package system and straight.el are initialized.
initialized, and `doom-packages', `packages-alist' and `quelpa-cache' are
populated, if they aren't already.
If FORCE-P is non-nil, do it anyway. 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 This ensure `doom-packages' is populated, if isn't aren't already. Use this
ensure all the necessary package metadata is initialized and available for before any of straight's or Doom's package management's API to ensure all the
them." necessary package metadata is initialized and available for them."
(let ((load-prefer-newer t)) ; reduce stale code issues (when (or force-p (not doom-init-packages-p))
;; package.el and quelpa handle themselves if their state changes during the (setq doom-init-packages-p t)
;; current session, but if you change a packages.el file in a module, (straight--reset-caches)
;; there's no non-trivial way to detect that, so to reload only (mapc #'straight-use-recipes doom-core-package-sources)
;; `doom-packages' pass 'internal as FORCE-P or use `doom/reload-packages'. (straight-register-package
(unless (eq force-p 'internal) `(straight :type git :host github
;; `package-alist' :repo ,(format "%s/straight.el" straight-repository-user)
(when (or force-p (not (bound-and-true-p package-alist))) :files ("straight*.el")
(doom-ensure-packages-initialized 'force) :branch ,straight-repository-branch))
(setq load-path (cl-delete-if-not #'file-directory-p load-path))) (mapc #'straight-use-package doom-core-packages)
;; `quelpa-cache' (when noninteractive
(when (or force-p (not (bound-and-true-p quelpa-cache))) (add-hook 'kill-emacs-hook #'straight--transaction-finalize))
;; ensure un-byte-compiled version of quelpa is loaded (dolist (package (straight--directory-files (straight--build-dir)))
(unless (featurep 'quelpa) (add-to-list 'load-path (directory-file-name (straight--build-dir package)))))
(load (locate-library "quelpa.el") nil t t)) (when (or force-p (not doom-packages))
(setq quelpa-initialized-p nil) (setq doom-disabled-packages nil
(or (quelpa-setup-p) doom-packages (doom-package-list))
(error "Could not initialize quelpa")))) (cl-loop for (pkg . plist) in doom-packages
;; `doom-packages' for ignored = (eval (plist-get plist :ignore) t)
(when (or force-p (not doom-packages)) for disabled = (eval (plist-get plist :disable) t)
(setq doom-packages (doom-package-list))))) 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) (cl-defmacro package! (name &rest plist &key built-in _recipe disable ignore _freeze)
"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)
"Declares a package and how to install it (if applicable). "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 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 :recipe RECIPE
Takes a MELPA-style recipe (see `quelpa-recipe' in `quelpa' for an example); Takes a MELPA-style recipe (see `quelpa-recipe' in `quelpa' for an example);
for packages to be installed from external sources. 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 :disable BOOL
Do not install or update this package AND disable all of its `def-package!' Do not install or update this package AND disable all of its `def-package!'
blocks. blocks.
@ -180,14 +220,9 @@ Returns t if package is successfully registered, and nil if it was disabled
elsewhere." elsewhere."
(declare (indent defun)) (declare (indent defun))
(let ((old-plist (cdr (assq name doom-packages)))) (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)) (let ((module-list (plist-get old-plist :modules))
(module (or doom--current-module (module (or doom--current-module
(let ((file (FILE!))) (let ((file (file!)))
(cond ((file-in-directory-p file doom-private-dir) (cond ((file-in-directory-p file doom-private-dir)
(list :private)) (list :private))
((file-in-directory-p file doom-core-dir) ((file-in-directory-p file doom-core-dir)
@ -199,7 +234,7 @@ elsewhere."
(when built-in (when built-in
(doom-log "Ignoring built-in package %S" name) (doom-log "Ignoring built-in package %S" name)
(when (equal built-in '(quote prefer)) (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))) (setq plist (plist-put plist :ignore (or built-in ignore)))
(while plist (while plist
(unless (null (cadr plist)) (unless (null (cadr plist))
@ -207,22 +242,20 @@ elsewhere."
(pop plist) (pop plist)
(pop plist)) (pop plist))
(setq plist old-plist) (setq plist old-plist)
;; TODO Add `straight-use-package-pre-build-function' support
(macroexp-progn (macroexp-progn
(append (when pin (append `((setf (alist-get ',name doom-packages) ',plist))
(doom-log "Pinning package '%s' to '%s'" name pin)
`((setf (alist-get ',name package-pinned-packages) ,pin)))
`((setf (alist-get ',name doom-packages) ',plist))
(when disable (when disable
(doom-log "Disabling package '%s'" name) `((doom-log "Disabling package %S" ',name)
`((add-to-list 'doom-disabled-packages ',name nil 'eq) (add-to-list 'doom-disabled-packages ',name nil 'eq)
nil)))))) nil))))))
(defmacro disable-packages! (&rest packages) (defmacro disable-packages! (&rest packages)
"A convenience macro for disabling packages in bulk. "A convenience macro for disabling packages in bulk.
Only use this macro in a module's (or your private) packages.el file." Only use this macro in a module's (or your private) packages.el file."
(macroexp-progn (macroexp-progn
(cl-loop for pkg in packages (cl-loop for p in packages
collect (macroexpand `(package! ,pkg :disable t))))) collect `(package! ,p :disable t))))
(provide 'core-packages) (provide 'core-packages)
;;; core-packages.el ends here ;;; core-packages.el ends here

View file

@ -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.") 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. "Where package.el and quelpa plugins (and their caches) are stored.
Must end with a slash.") 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.") whichever is found first. Must end in a slash.")
(defvar doom-autoload-file (concat doom-local-dir "autoloads.el") (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 This file is responsible for informing Emacs where to find all of Doom's
autoloaded core functions (in core/autoload/*.el).") autoloaded core functions (in core/autoload/*.el).")
(defvar doom-package-autoload-file (concat doom-local-dir "autoloads.pkg.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 This file is compiled from the autoloads files of all installed packages
combined.") combined.")
@ -404,35 +404,6 @@ Meant to be used with `run-hook-wrapped'."
;; return nil so `run-hook-wrapped' won't short circuit ;; return nil so `run-hook-wrapped' won't short circuit
nil) 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) (defun doom-display-benchmark-h (&optional return-p)
"Display a benchmark, showing number of packages and modules, and how quickly "Display a benchmark, showing number of packages and modules, and how quickly
they were loaded at startup. 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 "Tries to load FILE (an autoloads file). Return t on success, throws an error
in interactive sessions, nil otherwise (but logs a warning)." in interactive sessions, nil otherwise (but logs a warning)."
(condition-case e (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) ((debug error)
(if noninteractive (if noninteractive
(message "Autoload file warning: %s -> %s" (car e) (error-message-string e)) (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) (defun doom-load-env-vars (file)
"Read and set envvars in FILE." "Read and set envvars in FILE."
(if (not (file-readable-p 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 (with-temp-buffer
(insert-file-contents file) (insert-file-contents file)
(search-forward "\n\n" nil t) (search-forward "\n\n" nil t)
@ -527,37 +500,45 @@ to least)."
load-path doom--initial-load-path load-path doom--initial-load-path
process-environment doom--initial-process-environment) process-environment doom--initial-process-environment)
;; `doom-autoload-file' tells Emacs where to load all its autoloaded ;; `doom-autoload-file' tells Emacs where to load all its functions from.
;; functions from. This includes everything in core/autoload/*.el and all ;; This includes everything in core/autoload/*.el and autoload files in
;; the autoload files in your enabled modules. ;; enabled modules.
(when (or force-p (not (doom-initialize-autoloads doom-autoload-file))) (when (or (not (doom-initialize-autoloads doom-autoload-file))
(doom-ensure-core-directories-exist) force-p)
(doom-ensure-same-emacs-version-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) (require 'core-packages)
(doom-ensure-packages-initialized force-p) (doom-ensure-straight)
(doom-ensure-core-packages) (doom-initialize-packages force-p)
(unless (or force-p noninteractive) (unless (or force-p noninteractive)
(user-error "Your doom autoloads are missing! Run `bin/doom refresh' to regenerate them"))) (user-error "Your doom autoloads are missing! Run `bin/doom refresh' to regenerate them")))
;; Loads `doom-package-autoload-file', which loads a concatenated package ;; Loads `doom-package-autoload-file', which loads a concatenated package
;; autoloads file and caches `load-path', `auto-mode-alist', ;; autoloads file which caches `load-path', `auto-mode-alist',
;; `Info-directory-list', `doom-disabled-packages' and ;; `Info-directory-list', and `doom-disabled-packages'. A big reduction in
;; `package-activated-list'. A big reduction in startup time. ;; startup time.
(let (command-switch-alist) (unless (or force-p
(unless (or force-p (doom-initialize-autoloads doom-package-autoload-file)
(doom-initialize-autoloads doom-package-autoload-file) noninteractive)
noninteractive) (user-error "Your package autoloads are missing! Run `bin/doom refresh' to regenerate them"))
(user-error "Your package autoloads are missing! Run `bin/doom refresh' to regenerate them")))
;; Load shell environment ;; Load shell environment
(unless noninteractive (when (and (not noninteractive)
(file-exists-p doom-env-file))
(doom-load-env-vars doom-env-file))) (doom-load-env-vars doom-env-file)))
;; In case we want to use package.el's API ;; In case we want to use package.el's API
(with-eval-after-load 'package (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)))
;; ;;

View file

@ -40,8 +40,5 @@
(package! which-key) (package! which-key)
(package! hydra) (package! hydra)
;; core-packages.el
(package! gnu-elpa-keyring-update)
;; autoload/debug.el ;; autoload/debug.el
(package! esup) (package! esup)

View file

@ -1,9 +1,9 @@
;;; init.el -*- lexical-binding: t; -*- ;;; init.el -*- lexical-binding: t; -*-
;; Copy this file to ~/.doom.d/init.el or ~/.config/doom/init.el ('doom ;; Copy this file to ~/.doom.d/init.el or ~/.config/doom/init.el ('doom install'
;; quickstart' will do this for you). The `doom!' block below controls what ;; will do this for you). The `doom!' block below controls what modules are
;; modules are enabled and in what order they will be loaded. Remember to run ;; enabled and in what order they will be loaded. Remember to run 'doom refresh'
;; 'doom refresh' after modifying it. ;; after modifying it.
;; ;;
;; More information about these modules (and what flags they support) can be ;; More information about these modules (and what flags they support) can be
;; found in modules/README.org. ;; found in modules/README.org.

View file

@ -5,7 +5,7 @@
(package! helm-ag) (package! helm-ag)
(package! helm-c-yasnippet) (package! helm-c-yasnippet)
(package! helm-company) (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! helm-projectile)
(package! swiper-helm) (package! swiper-helm)
(when (featurep! +fuzzy) (when (featurep! +fuzzy)

View file

@ -9,14 +9,13 @@
(package! evil-escape) (package! evil-escape)
(package! evil-exchange) (package! evil-exchange)
(package! evil-indent-plus) (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-textobj-anyblock)
(package! evil-snipe) (package! evil-snipe)
(package! evil-surround) (package! evil-surround)
(package! evil-visualstar) (package! evil-visualstar)
(package! exato) (package! exato)
;; ;;
(when (featurep! +everywhere) (when (featurep! +everywhere)
;; `evil-collection-neotree' uses the `neotree-make-executor' macro, but this ;; `evil-collection-neotree' uses the `neotree-make-executor' macro, but this

View file

@ -1,4 +1,4 @@
;; -*- no-byte-compile: t; -*- ;; -*- no-byte-compile: t; -*-
;;; editor/rotate-text/packages.el ;;; 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"))

View file

@ -5,6 +5,6 @@
(package! auto-yasnippet) (package! auto-yasnippet)
(package! doom-snippets (package! doom-snippets
:recipe (:fetcher github :recipe (:host github
:repo "hlissner/doom-snippets" :repo "hlissner/doom-snippets"
:files ("*.el" "snippets"))) :files ("*.el" "snippets")))

View file

@ -10,7 +10,7 @@
(when (package! glsl-mode) (when (package! glsl-mode)
(when (featurep! :completion company) (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) (if (featurep! +lsp)
(package! ccls) (package! ccls)

View file

@ -16,18 +16,8 @@
;; by default quelpa generated a version 0pre0.20180929.192844, which got ;; by default quelpa generated a version 0pre0.20180929.192844, which got
;; parsed into (0 -1 0 ...), which when compared with version nil (0) in ;; parsed into (0 -1 0 ...), which when compared with version nil (0) in
;; package-installed-p always yielded false ;; 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"))) (package! dune :recipe
(:host 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)

View file

@ -882,3 +882,12 @@ compelling reason, so..."
(org-clock-load)) (org-clock-load))
:config :config
(add-hook 'kill-emacs-hook #'org-clock-save))) (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 () "")

View file

@ -1,15 +1,8 @@
;; -*- no-byte-compile: t; -*- ;; -*- no-byte-compile: t; -*-
;;; lang/org/packages.el ;;; lang/org/packages.el
;; Prevent built-in Org from playing into the byte-compilation of (package! org-plus-contrib) ; install cutting-edge version of org-mode
;; `org-plus-contrib'. (package! org-bullets :recipe (:host github :repo "Kaligule/org-bullets"))
(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! toc-org) (package! toc-org)
(when (featurep! :editor evil) (when (featurep! :editor evil)
(package! evil-org)) (package! evil-org))
@ -17,7 +10,7 @@
(package! org-pdfview)) (package! org-pdfview))
(package! htmlize) (package! htmlize)
(package! ox-clip) (package! ox-clip)
(package! org-yt :recipe (:fetcher github :repo "TobiasZawada/org-yt")) (package! org-yt :recipe (:host github :repo "TobiasZawada/org-yt"))
;;; Babel ;;; Babel
(package! ob-async) (package! ob-async)
@ -28,7 +21,7 @@
(when (featurep! :lang nim) (when (featurep! :lang nim)
(package! ob-nim)) (package! ob-nim))
(when (featurep! :lang racket) (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) (when (featurep! :lang rest)
(package! ob-restclient)) (package! ob-restclient))
(when (featurep! :lang rust) (when (featurep! :lang rust)
@ -37,18 +30,14 @@
;;; Modules ;;; Modules
(when (featurep! +dragndrop) (when (featurep! +dragndrop)
(package! org-download)) (package! org-download))
(when (featurep! +gnuplot) (when (featurep! +gnuplot)
(package! gnuplot) (package! gnuplot)
(package! gnuplot-mode)) (package! gnuplot-mode))
(when (featurep! +ipython) (when (featurep! +ipython)
(package! ob-ipython)) (package! ob-ipython))
(when (featurep! +pandoc) (when (featurep! +pandoc)
(package! ox-pandoc)) (package! ox-pandoc))
(when (featurep! +present) (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! org-tree-slide)
(package! ox-reveal)) (package! ox-reveal))

View file

@ -2,13 +2,13 @@
;;; lang/php/packages.el ;;; lang/php/packages.el
(package! php-boris) (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-mode)
(package! php-refactor-mode) (package! php-refactor-mode)
(package! phpunit) (package! phpunit)
(when (featurep! +hack) (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) (unless (featurep! +lsp)
(package! phpactor)) (package! phpactor))

View file

@ -1,7 +1,8 @@
;; -*- no-byte-compile: t; -*- ;; -*- no-byte-compile: t; -*-
;;; lang/lua/packages.el ;;; 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) (when (featurep! :completion company)
(package! company-lua)) (package! company-lua))

View file

@ -17,6 +17,11 @@ It is passed a user and repository name.")
(setq transient-levels-file (concat doom-etc-dir "transient/levels") (setq transient-levels-file (concat doom-etc-dir "transient/levels")
transient-values-file (concat doom-etc-dir "transient/values") transient-values-file (concat doom-etc-dir "transient/values")
transient-history-file (concat doom-etc-dir "transient/history")) 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 :config
(setq transient-default-level 5 (setq transient-default-level 5
magit-revision-show-gravatars '("^Author: " . "^Commit: ") magit-revision-show-gravatars '("^Author: " . "^Commit: ")

View file

@ -7,8 +7,8 @@
;; an older version of `auto-source-pass' is built into Emacs 26+, so we must ;; 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. ;; install the new version directly from the source and with a psuedonym.
(package! auth-source-pass-new (package! auth-source-pass
:recipe (auth-source-pass :fetcher github :repo "DamienCassou/auth-password-store")) :recipe (:host github :repo "DamienCassou/auth-password-store"))
(when (featurep! :completion ivy) (when (featurep! :completion ivy)
(package! ivy-pass)) (package! ivy-pass))