doomemacs/lisp/cli/autoloads.el
Henrik Lissner b914830403
refactor!: complete profile gen and init systems
BREAKING CHANGE: This commit makes three breaking changes:

- Doom now fully and dynamically generates (and byte-compiles) your
  profile and its init files, which includes your autoloads, loading
  your init files and modules, and then some. This replaces
  doom-initialize-modules, doom-initialize-core-modules, and
  doom-module-loader, which have been removed. This has also improved
  startup time by a bit, but if you use these functions in your CLIs,
  for instance, this will be a breaking change.
- `doom sync` is now required for Doom to see your profiles (and must be
  run whenever you change them, or when you up/downgrade Emacs across
  major versions).
- $DOOMDIR/init.el is now read much earlier than it used to be. Before
  any of doom-{ui,keybinds,editor,projects}, before any autoloads are
  loaded, and before your load-path has been populated with your
  packages. It now runs in the context of early-init.el, giving users
  freer range over what they can affect, but a more minimalistic
  environment to do it in.

  If you must have some logic run when all that is set up, add it to one
  of the module hooks added in e08f68b or 283308a.

This also poses a significant change to Doom's load order (see the
commentary change in lib/doom.el), along with the following (non
breaking) changes:

1. Adds a new `doom profiles sync` command. This will forcibly resync
   your profiles, while `doom sync` will only do so if your profiles
   have changed.
2. Doom now fully and dynamically generates (and byte-compiles) your
   user-init-file, which includes loading all your init files, modules,
   and custom-file. This replaces the job of doom-initialize-modules,
   doom-initialize-core-modules, and doom-module-loader, which have been
   removed. This has also improved startup time by a bit.
3. Defines new doom-state-dir variable, though not used yet (saving that
   and the other breaking changes for the 3.0 release).
4. Redesigns profile directory variables (doom-profile-*-dir) to prepare
   for future XDG-compliance.
5. Removed unused/unimportant profile variables in doom.el.
6. Added lisp/doom-profiles.el. It's hardly feature complete, but it's
   enough to power the system as it is now.
7. Updates the "load order" commentary in doom.el to reflect these
   changes.
2022-09-16 01:14:23 +02:00

194 lines
8.3 KiB
EmacsLisp

;;; lisp/cli/autoloads.el -*- lexical-binding: t; -*-
(defvar doom-autoloads-excluded-packages ()
"Which packages to exclude from Doom's autoloads files.
Use this for packages with problematic autoloads; e.g. they autoload too much or
hoist buggy forms into autoloads.")
(defvar doom-autoloads-excluded-files ()
"List of regexps whose matching files won't be indexed for autoloads.")
(defvar doom-autoloads-cached-vars
'(doom-modules
doom-disabled-packages
native-comp-deferred-compilation-deny-list
load-path
auto-mode-alist
interpreter-mode-alist
Info-directory-list)
"A list of variables to be cached in `doom-autoloads-file'.")
(defvar doom-autoloads-files ()
"A list of additional files or file globs to scan for autoloads.")
;;
;;; Library
(defun doom-autoloads--write (file &rest forms)
(make-directory (file-name-directory file) 'parents)
(condition-case-unless-debug e
(with-temp-file file
(setq-local coding-system-for-write 'utf-8)
(let ((standard-output (current-buffer))
(print-quoted t)
(print-level nil)
(print-length nil))
(insert ";; -*- lexical-binding: t; coding: utf-8; no-native-compile: t -*-\n"
";; This file is autogenerated by 'doom sync', DO NOT EDIT IT!!\n")
(dolist (form (delq nil forms))
(mapc #'prin1 form))
t))
(error (delete-file file)
(signal 'doom-autoload-error (list file e)))))
(defun doom-autoloads--compile-file (file)
(condition-case-unless-debug e
(let ((byte-compile-warnings (if init-file-debug byte-compile-warnings)))
(and (byte-compile-file file)
(load (byte-compile-dest-file file) nil t)))
(error
(delete-file (byte-compile-dest-file file))
(signal 'doom-autoload-error (list file e)))))
(defun doom-autoloads--cleanup-form (form &optional expand)
(let ((func (car-safe form)))
(cond ((memq func '(provide custom-autoload))
nil)
((and (eq func 'add-to-list)
(memq (doom-unquote (cadr form))
doom-autoloads-cached-vars))
nil)
((not (eq func 'autoload))
form)
((and expand (not (file-name-absolute-p (nth 2 form))))
(defvar doom--autoloads-path-cache nil)
(setf (nth 2 form)
(let ((path (nth 2 form)))
(or (cdr (assoc path doom--autoloads-path-cache))
(when-let* ((libpath (locate-library path))
(libpath (file-name-sans-extension libpath))
(libpath (abbreviate-file-name libpath)))
(push (cons path libpath) doom--autoloads-path-cache)
libpath)
path)))
form)
(form))))
(defun doom-autoloads--scan-autodefs (file buffer module &optional module-enabled-p)
(with-temp-buffer
(insert-file-contents file)
(while (re-search-forward "^;;;###autodef *\\([^\n]+\\)?\n" nil t)
(let* ((standard-output buffer)
(form (read (current-buffer)))
(altform (match-string 1))
(definer (car-safe form))
(symbol (doom-unquote (cadr form))))
(cond ((and (not module-enabled-p) altform)
(print (read altform)))
((memq definer '(defun defmacro cl-defun cl-defmacro))
(print
(if module-enabled-p
(make-autoload form file)
(seq-let (_ _ arglist &rest body) form
(if altform
(read altform)
(append
(list (pcase definer
(`defun 'defmacro)
(`cl-defun `cl-defmacro)
(_ type))
symbol arglist
(format "THIS FUNCTION DOES NOTHING BECAUSE %s IS DISABLED\n\n%s"
module (if (stringp (car body))
(pop body)
"No documentation.")))
(cl-loop for arg in arglist
if (symbolp arg)
if (not (keywordp arg))
if (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)))))))))
(print `(put ',symbol 'doom-module ',module)))
((eq definer 'defalias)
(seq-let (_ _ target docstring) form
(unless module-enabled-p
(setq target #'ignore
docstring
(format "THIS FUNCTION DOES NOTHING BECAUSE %s IS DISABLED\n\n%s"
module docstring)))
(print `(put ',symbol 'doom-module ',module))
(print `(defalias ',symbol #',(doom-unquote target) ,docstring))))
(module-enabled-p (print form)))))))
(defvar autoload-timestamps)
(defvar generated-autoload-load-name)
(defun doom-autoloads--scan-file (file)
(let* (;; Prevent `autoload-find-file' from firing file hooks, e.g. adding
;; to recentf.
find-file-hook
write-file-functions
;; Prevent a possible source of crashes when there's a syntax error in
;; the autoloads file.
debug-on-error
;; Non-nil interferes with autoload generation in Emacs < 29. See
;; radian-software/straight.el#904.
(left-margin 0)
;; The following bindings are in `package-generate-autoloads'.
;; Presumably for a good reason, so I just copied them.
(backup-inhibited t)
(version-control 'never)
case-fold-search ; reduce magic
autoload-timestamps ; reduce noise in generated files
;; So `autoload-generate-file-autoloads' knows where to write it
(generated-autoload-load-name (file-name-sans-extension file))
(target-buffer (current-buffer))
(module (doom-module-from-path file))
(module-enabled-p (and (doom-module-p (car module) (cdr module))
(doom-file-cookie-p file "if" t))))
(save-excursion
(when module-enabled-p
(quiet! (autoload-generate-file-autoloads file target-buffer)))
(doom-autoloads--scan-autodefs
file target-buffer module module-enabled-p))))
(defun doom-autoloads--scan (files &optional exclude literal)
"Scan and return all autoloaded forms in FILES.
Autoloads will be generated from autoload cookies in FILES (except those that
match one of the regexps in EXCLUDE -- a list of strings). If LITERAL is
non-nil, treat FILES as pre-generated autoload files instead."
(require 'autoload)
(let (autoloads)
(dolist (file files (nreverse (delq nil autoloads)))
(when (and (not (seq-find (doom-rpartial #'string-match-p file) exclude))
(file-readable-p file))
(doom-log "loaddefs:scan: %s" file)
(setq file (file-truename file))
(with-temp-buffer
(if literal
(insert-file-contents file)
(doom-autoloads--scan-file file))
(save-excursion
(while (re-search-forward "\\_<load-file-name\\_>" nil t)
;; `load-file-name' is meaningless in a concatenated
;; mega-autoloads file, but also essential in isolation, so we
;; replace references to it with the file they came from.
(let ((ppss (save-excursion (syntax-ppss))))
(or (nth 3 ppss)
(nth 4 ppss)
(replace-match (prin1-to-string file) t t)))))
(let ((load-file-name file)
(load-path
(append (list doom-user-dir)
doom-modules-dirs
load-path)))
(condition-case _
(while t
(push (doom-autoloads--cleanup-form (read (current-buffer))
(not literal))
autoloads))
(end-of-file))))))))