mirror of
https://gitlab.com/dwt1/dotfiles.git
synced 2026-04-24 12:00:24 +10:00
Updating Doom Emacs.
This commit is contained in:
@@ -1,27 +1,25 @@
|
||||
;;; core/core-packages.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; Emacs package management is opinionated, and so is Doom. Doom uses `straight'
|
||||
;; to create a declarative, lazy-loaded and optionally rolling-release package
|
||||
;; to create a declarative, lazy-loaded and (nominally) reproducible package
|
||||
;; management system. We use `straight' over `package' because the latter is
|
||||
;; tempermental. ELPA sources suffer downtime occasionally, and often fail at
|
||||
;; building some packages when GNU Tar is unavailable (e.g. MacOS users start
|
||||
;; with BSD tar). There are also known gnutls errors in the current stable
|
||||
;; release of Emacs (26.x) which bork TLS handshakes with ELPA repos (mainly
|
||||
;; gnu.elpa.org). See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=3434.
|
||||
;; tempermental. ELPA sources suffer downtime occasionally and often fail to
|
||||
;; build packages when GNU Tar is unavailable (e.g. MacOS users start with BSD
|
||||
;; tar). Known gnutls errors plague the current stable release of Emacs (26.x)
|
||||
;; which bork TLS handshakes with ELPA repos (mainly gnu.elpa.org). See
|
||||
;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=3434.
|
||||
;;
|
||||
;; What's worse, you can only get the latest version of packages through ELPA.
|
||||
;; In an ecosystem that is constantly changing, this is more frustrating than
|
||||
;; convenient. Straight (and Doom) can do rolling release, but it is optional
|
||||
;; (and will eventually be opt-in).
|
||||
;; convenient. Straight (and Doom) can do rolling release, but it is opt-in.
|
||||
;;
|
||||
;; ANyhow, interacting with this package management system is done through the
|
||||
;; bin/doom script included with Doom Emacs. You'll find more about it by
|
||||
;; running 'doom help' (I highly recommend you add it to your PATH), but here
|
||||
;; are the highlights:
|
||||
;; Interacting with this package management system is done through Doom's
|
||||
;; bin/doom script. Find out more about it by running 'doom help' (I highly
|
||||
;; recommend you add the script to your PATH). Here are some highlights:
|
||||
;;
|
||||
;; + `bin/doom install`: a wizard that guides you through setting up Doom and
|
||||
;; your private config for the first time.
|
||||
;; + `bin/doom refresh`: your go-to command for making sure Doom is in optimal
|
||||
;; + `bin/doom sync`: your go-to command for making sure Doom is in optimal
|
||||
;; condition. It ensures all unneeded packages are removed, all needed ones
|
||||
;; are installed, and all metadata associated with them is generated.
|
||||
;; + `bin/doom upgrade`: upgrades Doom Emacs and your packages to the latest
|
||||
@@ -32,48 +30,31 @@
|
||||
;; `doom-core-dir'. These contain `package!' declarations that tell DOOM what
|
||||
;; plugins to install and where from.
|
||||
;;
|
||||
;; All that said, you can still use package.el's commands, but 'bin/doom
|
||||
;; refresh' will purge ELPA packages.
|
||||
|
||||
(defvar doom-init-packages-p nil
|
||||
"If non-nil, Doom's package management system has been initialized.")
|
||||
;; All that said, you can still use package.el's commands, but 'bin/doom sync'
|
||||
;; will purge ELPA packages.
|
||||
|
||||
(defvar doom-packages ()
|
||||
"A list of enabled packages. Each element is a sublist, whose CAR is the
|
||||
package's name as a symbol, and whose CDR is the plist supplied to its
|
||||
`package!' declaration. Set by `doom-initialize-packages'.")
|
||||
|
||||
(defvar doom-core-packages '(straight use-package async gcmh)
|
||||
"A list of packages that must be installed (and will be auto-installed if
|
||||
missing) and shouldn't be deleted.")
|
||||
|
||||
(defvar doom-core-package-sources
|
||||
'((org-elpa :local-repo nil)
|
||||
(melpa
|
||||
:type git :host github
|
||||
:repo "melpa/melpa"
|
||||
:no-build t)
|
||||
(gnu-elpa-mirror
|
||||
:type git :host github
|
||||
:repo "emacs-straight/gnu-elpa-mirror"
|
||||
:no-build t)
|
||||
(emacsmirror-mirror
|
||||
:type git :host github
|
||||
:repo "emacs-straight/emacsmirror-mirror"
|
||||
:no-build t))
|
||||
"A list of recipes for straight's recipe repos.")
|
||||
|
||||
(defvar doom-disabled-packages ()
|
||||
"A list of packages that should be ignored by `use-package!' and `after!'.")
|
||||
|
||||
(defvar doom-packages-file "packages"
|
||||
"The basename of packages file for modules.
|
||||
|
||||
Package files are read whenever Doom's package manager wants a manifest of all
|
||||
desired packages. They are rarely read in interactive sessions (unless the user
|
||||
uses a straight or package.el command directly).")
|
||||
|
||||
|
||||
;;
|
||||
;;; Package managers
|
||||
;;; package.el
|
||||
|
||||
;; Ensure that, if we do need package.el, it is configured correctly. You really
|
||||
;; shouldn't be using it, but it may be convenient for quick package testing.
|
||||
(setq package--init-file-ensured t
|
||||
package-enable-at-startup nil
|
||||
(setq package-enable-at-startup nil
|
||||
package-user-dir (concat doom-local-dir "elpa/")
|
||||
package-gnupghome-dir (expand-file-name "gpg" package-user-dir)
|
||||
;; I omit Marmalade because its packages are manually submitted rather
|
||||
@@ -84,175 +65,363 @@ missing) and shouldn't be deleted.")
|
||||
("melpa" . ,(concat proto "://melpa.org/packages/"))
|
||||
("org" . ,(concat proto "://orgmode.org/elpa/")))))
|
||||
|
||||
;; Don't save `package-selected-packages' to `custom-file'
|
||||
(defadvice! doom--package-inhibit-custom-file-a (&optional value)
|
||||
:override #'package--save-selected-packages
|
||||
(if value (setq package-selected-packages value)))
|
||||
;; package.el has no business modifying the user's init.el
|
||||
(advice-add #'package--ensure-init-file :override #'ignore)
|
||||
|
||||
;; Refresh package.el the first time you call `package-install'
|
||||
;; Refresh package.el the first time you call `package-install', so it can still
|
||||
;; be used (e.g. to temporarily test packages). Remember to run 'doom sync' to
|
||||
;; purge them; they can conflict with packages installed via straight!
|
||||
(add-transient-hook! 'package-install (package-refresh-contents))
|
||||
|
||||
;;; straight
|
||||
|
||||
;;
|
||||
;;; Straight
|
||||
|
||||
(setq straight-base-dir doom-local-dir
|
||||
straight-repository-branch "develop"
|
||||
straight-cache-autoloads nil ; we already do this, and better.
|
||||
;; Doom doesn't encourage you to modify packages in place. Disabling this
|
||||
;; makes 'doom refresh' instant (once everything set up), which is much
|
||||
;; nicer UX than the several seconds modification checks.
|
||||
;; makes 'doom sync' instant (once everything set up), which is much nicer
|
||||
;; UX than the several seconds modification checks.
|
||||
straight-check-for-modifications nil
|
||||
;; We handle package.el ourselves (and a little more comprehensively)
|
||||
straight-enable-package-integration nil
|
||||
;; Before switching to straight, `doom-local-dir' would average out at
|
||||
;; around 100mb with half Doom's modules at ~230 packages. Afterwards, at
|
||||
;; around 1gb. With shallow cloning, that is reduced to ~400mb. This
|
||||
;; imposes an issue with packages that require their git history for
|
||||
;; certain things to work (like magit and org), but we can deal with that
|
||||
;; when we cross that bridge.
|
||||
;; around 1gb. With shallow cloning, that is reduced to ~400mb. This has
|
||||
;; no affect on packages that are pinned, however (run 'doom purge' to
|
||||
;; compact those after-the-fact). Some packages break when shallow cloned
|
||||
;; (like magit and org), but we'll deal with that elsewhere.
|
||||
straight-vc-git-default-clone-depth 1
|
||||
;; Straight's own emacsmirror mirror 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)
|
||||
;; we don't have to deal with them at all.
|
||||
autoload-compute-prefixes nil
|
||||
;; We handle it ourselves
|
||||
straight-fix-org nil)
|
||||
|
||||
(defun doom--finalize-straight ()
|
||||
(mapc #'funcall (delq nil (mapcar #'cdr straight--transaction-alist)))
|
||||
(setq straight--transaction-alist nil))
|
||||
|
||||
;;; Getting straight to behave in batch mode
|
||||
(when noninteractive
|
||||
;; HACK Remove dired & magit options from prompt, since they're inaccessible
|
||||
;; in noninteractive sessions.
|
||||
(advice-add #'straight-vc-git--popup-raw :override #'straight--popup-raw))
|
||||
|
||||
;; HACK Replace GUI popup prompts (which hang indefinitely in tty Emacs) with
|
||||
;; simple prompts.
|
||||
(defadvice! doom--straight-fallback-to-y-or-n-prompt-a (orig-fn &optional prompt)
|
||||
:around #'straight-are-you-sure
|
||||
(if noninteractive
|
||||
(y-or-n-p (format! "%s" (or prompt "")))
|
||||
(funcall orig-fn prompt)))
|
||||
|
||||
(defadvice! doom--straight-fallback-to-tty-prompt-a (orig-fn prompt actions)
|
||||
"Modifies straight to prompt on the terminal when in noninteractive sessions."
|
||||
:around #'straight--popup-raw
|
||||
(if (not noninteractive)
|
||||
(funcall orig-fn prompt actions)
|
||||
;; We can't intercept C-g, so no point displaying any options for this key
|
||||
;; Just use C-c
|
||||
(delq! "C-g" actions 'assoc)
|
||||
;; HACK These are associated with opening dired or magit, which isn't
|
||||
;; possible in tty Emacs, so...
|
||||
(delq! "e" actions 'assoc)
|
||||
(delq! "g" actions 'assoc)
|
||||
(let ((options (list (lambda ()
|
||||
(let ((doom-format-indent 0))
|
||||
(terpri)
|
||||
(print! (error "Aborted")))
|
||||
(kill-emacs)))))
|
||||
(print! (start "%s") (red prompt))
|
||||
(terpri)
|
||||
(print-group!
|
||||
(print-group!
|
||||
(print! " 1) Abort")
|
||||
(dolist (action actions)
|
||||
(cl-destructuring-bind (_key desc func) action
|
||||
(when desc
|
||||
(push func options)
|
||||
(print! "%2s) %s" (length options) desc)))))
|
||||
(terpri)
|
||||
(let ((options (nreverse options))
|
||||
answer fn)
|
||||
(while
|
||||
(not
|
||||
(setq
|
||||
fn (ignore-errors
|
||||
(nth (1- (setq answer
|
||||
(read-number
|
||||
(format! "How to proceed? (%s) "
|
||||
(mapconcat #'number-to-string
|
||||
(number-sequence 1 (length options))
|
||||
", ")))))
|
||||
options))))
|
||||
(print! (warn "%s is not a valid answer, try again.") answer))
|
||||
(funcall fn))))))
|
||||
(defadvice! doom--read-pinned-packages-a (orig-fn &rest args)
|
||||
"Read `:pin's in `doom-packages' on top of straight's lockfiles."
|
||||
:around #'straight--lockfile-read-all
|
||||
(append (apply orig-fn args) ; lockfiles still take priority
|
||||
(doom-package-pinned-list)))
|
||||
|
||||
|
||||
;;
|
||||
;;; Bootstrapper
|
||||
;;; Bootstrappers
|
||||
|
||||
(defun doom--ensure-straight (recipe pin)
|
||||
(let ((repo-dir (doom-path straight-base-dir "straight/repos/straight.el"))
|
||||
(repo-url (concat "http" (if gnutls-verify-error "s")
|
||||
"://github.com/"
|
||||
(or (plist-get recipe :repo) "raxod502/straight.el")))
|
||||
(branch (or (plist-get recipe :branch) straight-repository-branch))
|
||||
(call (if doom-debug-p #'doom-exec-process #'doom-call-process)))
|
||||
(unless (file-directory-p repo-dir)
|
||||
(message "Installing straight...")
|
||||
(cond
|
||||
((eq straight-vc-git-default-clone-depth 'full)
|
||||
(funcall call "git" "clone" "--origin" "origin" repo-url repo-dir))
|
||||
((null pin)
|
||||
(funcall call "git" "clone" "--origin" "origin" repo-url repo-dir
|
||||
"--depth" (number-to-string straight-vc-git-default-clone-depth)
|
||||
"--branch" straight-repository-branch))
|
||||
((integerp straight-vc-git-default-clone-depth)
|
||||
(make-directory repo-dir t)
|
||||
(let ((default-directory repo-dir))
|
||||
(funcall call "git" "init")
|
||||
(funcall call "git" "checkout" "-b" straight-repository-branch)
|
||||
(funcall call "git" "remote" "add" "origin" repo-url)
|
||||
(funcall call "git" "fetch" "origin" pin
|
||||
"--depth" (number-to-string straight-vc-git-default-clone-depth))
|
||||
(funcall call "git" "checkout" "--detach" pin)))))
|
||||
(require 'straight (concat repo-dir "/straight.el"))
|
||||
(doom-log "Initializing recipes")
|
||||
(with-temp-buffer
|
||||
(insert-file-contents (doom-path repo-dir "bootstrap.el"))
|
||||
;; Don't install straight for us -- we've already done that -- only set
|
||||
;; up its recipe repos for us.
|
||||
(eval-region (search-forward "(require 'straight)")
|
||||
(point-max)))))
|
||||
|
||||
(defun doom--ensure-core-packages (packages)
|
||||
(doom-log "Installing core packages")
|
||||
(dolist (package packages)
|
||||
(let ((name (car package)))
|
||||
(when-let (recipe (plist-get (cdr package) :recipe))
|
||||
(straight-override-recipe (cons name recipe)))
|
||||
(straight-use-package name))))
|
||||
|
||||
(defun doom-initialize-core-packages (&optional force-p)
|
||||
"Ensure `straight' is installed and was compiled with this version of Emacs."
|
||||
(when (or force-p (null (bound-and-true-p straight-recipe-repositories)))
|
||||
(doom-log "Initializing straight")
|
||||
(let ((packages (doom-package-list nil 'core)))
|
||||
(cl-destructuring-bind (&key recipe pin &allow-other-keys)
|
||||
(alist-get 'straight packages)
|
||||
(doom--ensure-straight recipe pin))
|
||||
(doom--ensure-core-packages packages))))
|
||||
|
||||
(defun doom-initialize-packages (&optional force-p)
|
||||
"Ensures that Doom's package system and straight.el are initialized.
|
||||
"Process all packages, essential and otherwise, if they haven't already been.
|
||||
|
||||
If FORCE-P is non-nil, do it anyway.
|
||||
|
||||
This ensure `doom-packages' is populated, if isn't aren't already. Use this
|
||||
before any of straight's or Doom's package management's API to ensure all the
|
||||
necessary package metadata is initialized and available for them."
|
||||
(unless doom-init-packages-p
|
||||
(setq force-p t))
|
||||
This ensures `doom-packages' is populated and `straight' recipes are properly
|
||||
processed."
|
||||
(doom-initialize-core-packages force-p)
|
||||
(when (or force-p (not (bound-and-true-p package--initialized)))
|
||||
(doom-log "Initializing package.el")
|
||||
(require 'package)
|
||||
(package-initialize))
|
||||
(when (or force-p (not doom-packages))
|
||||
(doom-log "Initializing straight")
|
||||
(setq doom-init-packages-p t)
|
||||
(unless (fboundp 'straight--reset-caches)
|
||||
(doom-ensure-straight)
|
||||
(require 'straight))
|
||||
(straight--reset-caches)
|
||||
(setq straight-recipe-repositories nil)
|
||||
(mapc #'straight-use-recipes doom-core-package-sources)
|
||||
(straight-register-package
|
||||
`(straight :type git :host github
|
||||
:repo ,(format "%s/straight.el" straight-repository-user)
|
||||
:files ("straight*.el")
|
||||
:branch ,straight-repository-branch
|
||||
:no-byte-compile t))
|
||||
(mapc #'straight-use-package doom-core-packages)
|
||||
(doom-log "Initializing doom-packages")
|
||||
(setq doom-disabled-packages nil
|
||||
doom-packages (doom-package-list))
|
||||
(cl-loop for (pkg . plist) in doom-packages
|
||||
if (plist-get plist :disable)
|
||||
do (cl-pushnew pkg doom-disabled-packages)
|
||||
else if (not (plist-get plist :ignore))
|
||||
do (with-demoted-errors "Package error: %s"
|
||||
(straight-register-package
|
||||
(if-let (recipe (plist-get plist :recipe))
|
||||
(cons pkg recipe)
|
||||
pkg))))
|
||||
(unless doom-interactive-mode
|
||||
(add-hook 'kill-emacs-hook #'doom--finalize-straight))))
|
||||
(package-initialize)
|
||||
(unless package--initialized
|
||||
(error "Failed to initialize package.el")))
|
||||
(when (or force-p (null doom-packages))
|
||||
(doom-log "Initializing straight.el")
|
||||
(or (setq doom-disabled-packages nil
|
||||
doom-packages (doom-package-list))
|
||||
(error "Failed to read any packages"))
|
||||
(dolist (package doom-packages)
|
||||
(cl-destructuring-bind
|
||||
(name &key recipe disable ignore &allow-other-keys) package
|
||||
(unless ignore
|
||||
(if disable
|
||||
(cl-pushnew name doom-disabled-packages)
|
||||
(when recipe
|
||||
(straight-override-recipe (cons name recipe)))
|
||||
(straight-register-package name)))))))
|
||||
|
||||
(defun doom-ensure-straight ()
|
||||
"Ensure `straight' is installed and was compiled with this version of Emacs."
|
||||
(defvar bootstrap-version)
|
||||
(let* (;; 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 straight-base-dir)
|
||||
(bootstrap-file (doom-path straight-base-dir "straight/repos/straight.el/straight.el"))
|
||||
(bootstrap-version 5))
|
||||
(make-directory (doom-path straight-base-dir "straight/build") 'parents)
|
||||
(unless (featurep 'straight)
|
||||
(unless (or (require 'straight nil t)
|
||||
(file-readable-p bootstrap-file))
|
||||
(with-current-buffer
|
||||
(url-retrieve-synchronously
|
||||
(format "https://raw.githubusercontent.com/raxod502/straight.el/%s/install.el"
|
||||
straight-repository-branch)
|
||||
'silent 'inhibit-cookies)
|
||||
(goto-char (point-max))
|
||||
(eval-print-last-sexp)))
|
||||
(load bootstrap-file nil t))))
|
||||
|
||||
;;
|
||||
;;; Package management API
|
||||
|
||||
(defun doom-package-get (package &optional prop nil-value)
|
||||
"Returns PACKAGE's `package!' recipe from `doom-packages'."
|
||||
(let ((plist (cdr (assq package doom-packages))))
|
||||
(if prop
|
||||
(if (plist-member plist prop)
|
||||
(plist-get plist prop)
|
||||
nil-value)
|
||||
plist)))
|
||||
|
||||
(defun doom-package-set (package prop value)
|
||||
"Set PROPERTY in PACKAGE's recipe to VALUE."
|
||||
(setf (alist-get package doom-packages)
|
||||
(plist-put (alist-get package doom-packages)
|
||||
prop value)))
|
||||
|
||||
(defun doom-package-recipe (package &optional prop nil-value)
|
||||
"Returns the `straight' recipe PACKAGE was registered with."
|
||||
(let ((plist (gethash (symbol-name package) straight--recipe-cache)))
|
||||
(if prop
|
||||
(if (plist-member plist prop)
|
||||
(plist-get plist prop)
|
||||
nil-value)
|
||||
plist)))
|
||||
|
||||
(defun doom-package-recipe-repo (package)
|
||||
"Resolve and return PACKAGE's (symbol) local-repo property."
|
||||
(if-let* ((recipe (cdr (straight-recipes-retrieve package)))
|
||||
(repo (straight-vc-local-repo-name recipe)))
|
||||
repo
|
||||
(symbol-name package)))
|
||||
|
||||
(defun doom-package-build-recipe (package &optional prop nil-value)
|
||||
"Returns the `straight' recipe PACKAGE was installed with."
|
||||
(let ((plist (nth 2 (gethash (symbol-name package) straight--build-cache))))
|
||||
(if prop
|
||||
(if (plist-member plist prop)
|
||||
(plist-get plist prop)
|
||||
nil-value)
|
||||
plist)))
|
||||
|
||||
(defun doom-package-build-time (package)
|
||||
"TODO"
|
||||
(car (gethash (symbol-name package) straight--build-cache)))
|
||||
|
||||
(defun doom-package-dependencies (package &optional recursive _noerror)
|
||||
"Return a list of dependencies for a package."
|
||||
(let ((deps (nth 1 (gethash (symbol-name package) straight--build-cache))))
|
||||
(if recursive
|
||||
(append deps (mapcan (lambda (dep) (doom-package-dependencies dep t t))
|
||||
(copy-sequence deps)))
|
||||
(copy-sequence deps))))
|
||||
|
||||
(defun doom-package-depending-on (package &optional noerror)
|
||||
"Return a list of packages that depend on the package named NAME."
|
||||
(cl-check-type name symbol)
|
||||
;; can't get dependencies for built-in packages
|
||||
(unless (or (doom-package-build-recipe name)
|
||||
noerror)
|
||||
(error "Couldn't find %s, is it installed?" name))
|
||||
(cl-loop for pkg in (hash-table-keys straight--build-cache)
|
||||
for deps = (doom-package-dependencies pkg)
|
||||
if (memq package deps)
|
||||
collect pkg
|
||||
and append (doom-package-depending-on pkg t)))
|
||||
|
||||
|
||||
;;; Predicate functions
|
||||
(defun doom-package-built-in-p (package)
|
||||
"Return non-nil if PACKAGE (a symbol) is built-in."
|
||||
(eq (doom-package-build-recipe package :type)
|
||||
'built-in))
|
||||
|
||||
(defun doom-package-installed-p (package)
|
||||
"Return non-nil if PACKAGE (a symbol) is installed."
|
||||
(file-directory-p (straight--build-dir (symbol-name package))))
|
||||
|
||||
(defun doom-package-is-type-p (package type)
|
||||
"TODO"
|
||||
(memq type (doom-enlist (doom-package-get package :type))))
|
||||
|
||||
(defun doom-package-in-module-p (package category &optional module)
|
||||
"Return non-nil if PACKAGE was installed by the user's private config."
|
||||
(when-let (modules (doom-package-get package :modules))
|
||||
(or (and (not module) (assq :private modules))
|
||||
(member (cons category module) modules))))
|
||||
|
||||
(defun doom-package-backend (package)
|
||||
"Return 'straight, 'builtin, 'elpa or 'other, depending on how PACKAGE is
|
||||
installed."
|
||||
(cond ((gethash (symbol-name package) straight--build-cache)
|
||||
'straight)
|
||||
((or (doom-package-built-in-p package)
|
||||
(assq package package--builtins))
|
||||
'builtin)
|
||||
((assq package package-alist)
|
||||
'elpa)
|
||||
((locate-library (symbol-name package))
|
||||
'other)))
|
||||
|
||||
(defun doom-package-changed-recipe-p (name)
|
||||
"Return t if a package named NAME (a symbol) has a different recipe than it
|
||||
was installed with."
|
||||
(cl-check-type name symbol)
|
||||
;; TODO
|
||||
;; (when (doom-package-installed-p name)
|
||||
;; (when-let* ((doom-recipe (assq name doom-packages))
|
||||
;; (install-recipe (doom-package-recipe)))
|
||||
;; (not (equal (cdr quelpa-recipe)
|
||||
;; (cdr (plist-get (cdr doom-recipe) :recipe))))))
|
||||
)
|
||||
|
||||
|
||||
;;; Package getters
|
||||
(defun doom--read-packages (file &optional noeval noerror)
|
||||
(condition-case-unless-debug e
|
||||
(with-temp-buffer ; prevent buffer-local state from propagating
|
||||
(if (not noeval)
|
||||
(load file noerror 'nomessage 'nosuffix)
|
||||
(when (file-exists-p file)
|
||||
(insert-file-contents file)
|
||||
(let (emacs-lisp-mode) (emacs-lisp-mode))
|
||||
;; Scrape `package!' blocks from FILE for a comprehensive listing of
|
||||
;; packages used by this module.
|
||||
(while (search-forward "(package!" nil t)
|
||||
(let ((ppss (save-excursion (syntax-ppss))))
|
||||
;; Don't collect packages in comments or strings
|
||||
(unless (or (nth 3 ppss)
|
||||
(nth 4 ppss))
|
||||
(goto-char (match-beginning 0))
|
||||
(cl-destructuring-bind (_ name . plist)
|
||||
(read (current-buffer))
|
||||
(push (cons
|
||||
name (plist-put
|
||||
plist :modules
|
||||
(list (doom-module-from-path file))))
|
||||
doom-packages))))))))
|
||||
(error
|
||||
(signal 'doom-package-error
|
||||
(list (doom-module-from-path file)
|
||||
file e)))))
|
||||
|
||||
(defun doom-package-list (&optional all-p core-only-p)
|
||||
"Retrieve a list of explicitly declared packages from enabled modules.
|
||||
|
||||
If ALL-P, gather packages unconditionally across all modules, including disabled
|
||||
ones."
|
||||
(let ((packages-file (concat doom-packages-file ".el"))
|
||||
doom-packages)
|
||||
(doom--read-packages
|
||||
(doom-path doom-core-dir packages-file) all-p 'noerror)
|
||||
(unless core-only-p
|
||||
(let ((private-packages (doom-path doom-private-dir packages-file))
|
||||
(doom-modules (doom-module-list)))
|
||||
(if all-p
|
||||
(mapc #'doom--read-packages
|
||||
(doom-files-in doom-modules-dir
|
||||
:depth 2
|
||||
:match "/packages\\.el$"))
|
||||
;; We load the private packages file twice to ensure disabled packages
|
||||
;; are seen ASAP, and a second time to ensure privately overridden
|
||||
;; packages are properly overwritten.
|
||||
(doom--read-packages private-packages nil 'noerror)
|
||||
(cl-loop for key being the hash-keys of doom-modules
|
||||
for path = (doom-module-path (car key) (cdr key) packages-file)
|
||||
for doom--current-module = key
|
||||
do (doom--read-packages path nil 'noerror)))
|
||||
(doom--read-packages private-packages all-p 'noerror)))
|
||||
(cl-remove-if-not
|
||||
(if core-only-p
|
||||
(lambda (pkg) (eq (plist-get (cdr pkg) :type) 'core))
|
||||
#'identity)
|
||||
(nreverse doom-packages))))
|
||||
|
||||
(defun doom-package-pinned-list ()
|
||||
"Return an alist mapping package names (strings) to pinned commits (strings)."
|
||||
(let (alist)
|
||||
(dolist (package doom-packages alist)
|
||||
(cl-destructuring-bind (name &key disable ignore pin unpin &allow-other-keys)
|
||||
package
|
||||
(when (and (not ignore)
|
||||
(not disable)
|
||||
(or pin unpin))
|
||||
(setf (alist-get (doom-package-recipe-repo name) alist
|
||||
nil 'remove #'equal)
|
||||
(unless unpin pin)))))))
|
||||
|
||||
(defun doom-package-unpinned-list ()
|
||||
"Return an alist mapping package names (strings) to pinned commits (strings)."
|
||||
(let (alist)
|
||||
(dolist (package doom-packages alist)
|
||||
(cl-destructuring-bind
|
||||
(_ &key recipe disable ignore pin unpin &allow-other-keys)
|
||||
package
|
||||
(when (and (not ignore)
|
||||
(not disable)
|
||||
(or unpin
|
||||
(and (plist-member recipe :pin)
|
||||
(null pin))))
|
||||
(cl-pushnew (doom-package-recipe-repo (car package)) alist
|
||||
:test #'equal))))))
|
||||
|
||||
(defun doom-package-recipe-list ()
|
||||
"Return straight recipes for non-builtin packages with a local-repo."
|
||||
(let (recipes)
|
||||
(dolist (recipe (hash-table-values straight--recipe-cache))
|
||||
(cl-destructuring-bind (&key local-repo type &allow-other-keys)
|
||||
recipe
|
||||
(unless (or (null local-repo)
|
||||
(eq type 'built-in))
|
||||
(push recipe recipes))))
|
||||
(nreverse recipes)))
|
||||
|
||||
(defun doom-package-state-list ()
|
||||
"TODO"
|
||||
(let (alist)
|
||||
(dolist (recipe (hash-table-values straight--repo-cache) alist)
|
||||
(cl-destructuring-bind (&key local-repo type &allow-other-keys)
|
||||
recipe
|
||||
(when (and local-repo (not (eq type 'built-in)))
|
||||
(setf (alist-get local-repo alist nil nil #'equal)
|
||||
(straight-vc-get-commit type local-repo)))))))
|
||||
|
||||
|
||||
;;
|
||||
;;; Module package macros
|
||||
|
||||
(cl-defmacro package!
|
||||
(name &rest plist &key built-in recipe ignore _disable _freeze)
|
||||
(name &rest plist &key built-in recipe ignore _type _pin _disable)
|
||||
"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
|
||||
@@ -263,67 +432,134 @@ Only use this macro in a module's packages.el file.
|
||||
|
||||
Accepts the following properties:
|
||||
|
||||
:type core|local|built-in|virtual
|
||||
Specifies what kind of package this is. Can be a symbol or a list thereof.
|
||||
`core' = this is a protected package and cannot be disabled!
|
||||
`local' = this package is being modified in-place. This package's repo is
|
||||
unshallowed and will be skipped when you update packages.
|
||||
`built-in' = this package is already built-in (otherwise, will be
|
||||
installed)
|
||||
`virtual' = this package is not tracked by Doom's package manager. It won't
|
||||
be installed or uninstalled. Use this to pin 2nd order dependencies.
|
||||
:recipe RECIPE
|
||||
Takes a MELPA-style recipe (see `quelpa-recipe' in `quelpa' for an example);
|
||||
for packages to be installed from external sources.
|
||||
Specifies a straight.el recipe to allow you to acquire packages from external
|
||||
sources. See https://github.com/raxod502/straight.el#the-recipe-format for
|
||||
details on this recipe.
|
||||
:disable BOOL
|
||||
Do not install or update this package AND disable all of its `use-package!'
|
||||
blocks.
|
||||
and `after!' blocks.
|
||||
:ignore FORM
|
||||
Do not install this package.
|
||||
:freeze FORM
|
||||
Do not update this package if FORM is non-nil.
|
||||
:built-in BOOL
|
||||
Same as :ignore if the package is a built-in Emacs package. If set to
|
||||
'prefer, will use built-in package if it is present.
|
||||
:pin STR|nil
|
||||
Pin this package to commit hash STR. Setting this to nil will unpin this
|
||||
package if previously pinned.
|
||||
:built-in BOOL|'prefer
|
||||
Same as :ignore if the package is a built-in Emacs package. This is more to
|
||||
inform help commands like `doom/help-packages' that this is a built-in
|
||||
package. If set to 'prefer, the package will not be installed if it is
|
||||
already provided by Emacs.
|
||||
|
||||
Returns t if package is successfully registered, and nil if it was disabled
|
||||
elsewhere."
|
||||
(declare (indent defun))
|
||||
(when (and recipe (keywordp (car-safe recipe)))
|
||||
(plist-put! plist :recipe `(quote ,recipe)))
|
||||
;; :built-in t is basically an alias for :ignore (locate-library NAME)
|
||||
(when built-in
|
||||
(when (and (not ignore) (equal built-in '(quote prefer)))
|
||||
(when (and (not ignore)
|
||||
(equal built-in '(quote prefer)))
|
||||
(setq built-in `(locate-library ,(symbol-name name) nil doom--initial-load-path)))
|
||||
(plist-delete! plist :built-in)
|
||||
(plist-put! plist :ignore built-in))
|
||||
`(let* ((name ',name)
|
||||
(plist (cdr (assq name doom-packages))))
|
||||
;; Record what module this declaration was found in
|
||||
(let ((module-list (plist-get plist :modules))
|
||||
(module ',(doom-module-from-path)))
|
||||
(unless (member module module-list)
|
||||
(plist-put! plist :modules
|
||||
(append module-list
|
||||
(list module)
|
||||
(when (file-in-directory-p ,(dir!) doom-private-dir)
|
||||
'((:private . modules)))
|
||||
nil))))
|
||||
|
||||
;; Merge given plist with pre-existing one
|
||||
(doplist! ((prop val) (list ,@plist) plist)
|
||||
(unless (null val)
|
||||
(plist-put! plist prop val)))
|
||||
|
||||
;; Some basic key validation; error if you're not using a valid key
|
||||
;; Some basic key validation; throws an error on invalid properties
|
||||
(condition-case e
|
||||
(cl-destructuring-bind
|
||||
(&key _local-repo _files _flavor _no-build
|
||||
_type _repo _host _branch _remote _nonrecursive _fork _depth)
|
||||
(plist-get plist :recipe))
|
||||
(when-let (recipe (plist-get plist :recipe))
|
||||
(cl-destructuring-bind
|
||||
(&key local-repo _files _flavor
|
||||
_no-build _no-byte-compile _no-autoloads
|
||||
_type _repo _host _branch _remote _nonrecursive _fork _depth)
|
||||
recipe
|
||||
;; Expand :local-repo from current directory
|
||||
(when local-repo
|
||||
(plist-put!
|
||||
plist :recipe
|
||||
(plist-put recipe :local-repo
|
||||
(let ((local-path (expand-file-name local-repo ,(dir!))))
|
||||
(if (file-directory-p local-path)
|
||||
local-path
|
||||
local-repo)))))))
|
||||
(error
|
||||
(signal 'doom-package-error
|
||||
(cons ,(symbol-name name)
|
||||
(error-message-string e)))))
|
||||
|
||||
;; This is the only side-effect of this macro!
|
||||
(setf (alist-get name doom-packages) plist)
|
||||
(if (not (plist-get plist :disable)) t
|
||||
(doom-log "Disabling package %S" name)
|
||||
(cl-pushnew name doom-disabled-packages)
|
||||
nil)))
|
||||
(unless (plist-get plist :disable)
|
||||
(with-no-warnings
|
||||
(cons name plist)))))
|
||||
|
||||
(defmacro disable-packages! (&rest packages)
|
||||
"A convenience macro for disabling packages in bulk.
|
||||
Only use this macro in a module's (or your private) packages.el file."
|
||||
(macroexp-progn
|
||||
(cl-loop for p in packages
|
||||
collect `(package! ,p :disable t))))
|
||||
(mapcar (lambda (p) `(package! ,p :disable t))
|
||||
packages)))
|
||||
|
||||
(defmacro unpin! (&rest targets)
|
||||
"Unpin packages in TARGETS.
|
||||
|
||||
This unpins packages, so that 'doom upgrade' downloads their latest version. It
|
||||
can be used one of five ways:
|
||||
|
||||
+ To disable pinning wholesale: (unpin! t)
|
||||
+ To unpin individual packages: (unpin! packageA packageB ...)
|
||||
+ To unpin all packages in a group of modules: (unpin! :lang :tools ...)
|
||||
+ To unpin packages in individual modules:
|
||||
(unpin! (:lang python javascript) (:tools docker))
|
||||
|
||||
Or any combination of the above.
|
||||
|
||||
This macro should only be used from the user's private packages.el. No module
|
||||
should use it!"
|
||||
(if (memq t targets)
|
||||
`(mapc (doom-rpartial #'doom-package-set :unpin t)
|
||||
(mapcar #'car doom-packages))
|
||||
(macroexp-progn
|
||||
(mapcar
|
||||
(lambda (target)
|
||||
(when target
|
||||
`(doom-package-set ',target :unpin t)))
|
||||
(cl-loop for target in targets
|
||||
if (or (keywordp target) (listp target))
|
||||
append
|
||||
(cl-loop with (category . modules) = (doom-enlist target)
|
||||
for (name . plist) in doom-packages
|
||||
for pkg-modules = (plist-get plist :modules)
|
||||
if (and (assq category pkg-modules)
|
||||
(or (null modules)
|
||||
(cl-loop for module in modules
|
||||
if (member (cons category module) pkg-modules)
|
||||
return t))
|
||||
name)
|
||||
collect it)
|
||||
else if (symbolp target)
|
||||
collect target)))))
|
||||
|
||||
(provide 'core-packages)
|
||||
;;; core-packages.el ends here
|
||||
|
||||
Reference in New Issue
Block a user