doomemacs/core/autoload/modules.el

289 lines
14 KiB
EmacsLisp

;;; core/autoload/modules.el -*- lexical-binding: t; -*-
;;;###autoload
(defun doom//reload ()
"Reload your private Doom config. Experimental!"
(interactive)
(let ((load-prefer-newer t))
(message "Reloading your private config...")
(setq doom-modules (make-hash-table :test #'equal :size 100 :rehash-threshold 1.0))
(doom-initialize t)
(doom//reload-autoloads))
(message "✓ Done!"))
;;;###autoload
(defun doom//reload-load-path ()
"Reload `load-path' and recompile files (if necessary).
Use this when `load-path' is out of sync with your plugins. This should only
happen if you manually modify/update/install packages from outside Emacs, while
an Emacs session is running.
This isn't necessary if you use Doom's package management commands because they
call `doom//reload-load-path' remotely (through emacsclient)."
(interactive)
(unless doom--inhibit-reload
(when (file-exists-p doom-packages-file)
(delete-file doom-packages-file))
(cond ((and noninteractive (not (daemonp)))
(require 'server)
(when (server-running-p)
(message "Reloading active Emacs session...")
(server-eval-at server-name '(doom//reload-load-path))))
(t
(doom-initialize t)
(message "%d packages reloaded" (length package-alist))
(run-hooks 'doom-reload-hook)))))
(defvar generated-autoload-load-name)
;;;###autoload
(defun doom//reload-autoloads (&optional force)
"Refreshes the autoloads.el file, specified by `doom-autoload-file'.
It scans and reads core/autoload/*.el, modules/*/*/autoload.el and
modules/*/*/autoload/*.el, and generates an autoloads file at the path specified
by `doom-autoload-file'. This file tells Emacs where to find lazy-loaded
functions.
This should be run whenever init.el or an autoload file is modified. Running
'make autoloads' from the commandline executes this command."
(interactive)
;; This function must not use autoloaded functions or external dependencies.
;; It must assume nothing is set up!
(let ((default-directory doom-emacs-dir)
(targets
(file-expand-wildcards
(expand-file-name "autoload/*.el" doom-core-dir)))
(generate-autoload-section-continuation "")
(generate-autoload-section-header "")
(generate-autoload-section-trailer "")
(doom--stage 'autoloads)
outdated)
(doom-initialize)
(dolist (path (doom-module-load-path))
(let ((auto-dir (expand-file-name "autoload" path))
(auto-file (expand-file-name "autoload.el" path)))
(when (file-exists-p auto-file)
(push auto-file targets))
(when (file-directory-p auto-dir)
(dolist (file (doom-files-under auto-dir :match "\\.el$"))
(push file targets)))))
(when (file-exists-p doom-autoload-file)
(delete-file doom-autoload-file)
(ignore-errors (delete-file (byte-compile-dest-file doom-autoload-file)))
(message "Deleted old autoloads.el"))
(message "Generating new autoloads.el")
(dolist (file (mapcar #'file-truename (reverse targets)))
(let ((generated-autoload-load-name (file-name-sans-extension file)))
(message
(cond ((not (doom-file-cookie-p file))
"⚠ Ignoring %s")
((update-file-autoloads file nil doom-autoload-file)
"✕ Nothing in %s")
("✓ Scanned %s"))
(if (file-in-directory-p file default-directory)
(file-relative-name file)
(abbreviate-file-name file)))))
(make-directory (file-name-directory doom-autoload-file) t)
(let ((buf (find-file-noselect doom-autoload-file t))
(load-path (append doom-psuedo-module-dirs
doom-modules-dirs
load-path))
case-fold-search)
;; FIXME Make me faster
(unwind-protect
(with-current-buffer buf
(goto-char (point-min))
(insert ";;; -*- lexical-binding:t -*-\n"
";; This file is autogenerated by `doom//reload-autoloads', DO NOT EDIT !!\n\n")
;; Replace autoload paths (only for module autoloads) with
;; absolute paths for faster resolution during load and simpler
;; `load-path'
(save-excursion
(let (cache)
(while (re-search-forward "^\\s-*(autoload\\s-+'[^ ]+\\s-+\"\\([^\"]*\\)\"" nil t)
(let ((path (match-string 1)))
(replace-match
(or (cdr (assoc path cache))
(when-let* ((libpath (locate-library path))
(libpath (file-name-sans-extension libpath)))
(push (cons path (abbreviate-file-name libpath)) cache)
libpath)
(progn
(warn "Couldn't find absolute path for: %s" path)
path))
t t nil 1))))
(message "✓ Autoload paths expanded"))
;; insert package autoloads
(save-excursion
(dolist (spec package-alist)
(let ((pkg (car spec)))
(unless (memq pkg doom-autoload-excluded-packages)
(let ((file
(abbreviate-file-name
(concat (package--autoloads-file-name (cadr spec)) ".el"))))
(insert "(let ((load-file-name " (prin1-to-string file) "))\n")
(insert-file-contents file)
(while (re-search-forward "^\\(?:;;\\(.*\n\\)\\|\n\\)" nil t)
(unless (nth 8 (syntax-ppss))
(replace-match "" t t)))
(unless (bolp) (insert "\n"))
(insert ")\n")))))
(message "✓ Package autoloads included"))
;; Remove `load-path' and `auto-mode-alist' modifications (most
;; of them, at least); they are cached elsewhere, so these are
;; unnecessary overhead.
(while (re-search-forward (concat "^\\s-*(\\(add-to-list\\s-+'\\(?:load-path\\|auto-mode-alist\\)\\)")
nil t)
(beginning-of-line)
(skip-chars-forward " \t")
(kill-sexp))
(message "✓ load-path/auto-mode-alist entries removed")
;; Remove byte-compile inhibiting file variables so we can
;; byte-compile the file.
(when (re-search-forward "^;; no-byte-compile: t\n$" nil t)
(replace-match "" t t))
(save-buffer)
;; Byte compile it to give the file a chance to reveal errors.
(condition-case-unless-debug ex
(quiet! (byte-compile-file doom-autoload-file 'load))
('error
(delete-file doom-autoload-file)
(message "Deleting autoloads file!")
(error "Error in autoloads.el: %s -- %s"
(car ex) (error-message-string ex))))
(message "Done!"))
(kill-buffer buf)))))
;;;###autoload
(defun doom//byte-compile (&optional modules recompile-p)
"Byte compiles your emacs configuration.
init.el is always byte-compiled by this.
If MODULES is specified (a list of module strings, e.g. \"lang/php\"), those are
byte-compiled. Otherwise, all enabled modules are byte-compiled, including Doom
core. It always ignores unit tests and files with `no-byte-compile' enabled.
Doom was designed to benefit from byte-compilation, but the process may take a
while. Also, while your config files are byte-compiled, changes to them will not
take effect! Use `doom//clean-byte-compiled-files' or `make clean' to remove
these files.
If RECOMPILE-P is non-nil, only recompile out-of-date files."
(interactive
(list nil current-prefix-arg))
(let ((default-directory doom-emacs-dir)
(recompile-p (or recompile-p (and (member "-r" (cdr argv)) t)))
(argv (delete "-r" argv)))
(unless recompile-p
(doom//clean-byte-compiled-files))
(let ((total-ok 0)
(total-fail 0)
(total-noop 0)
(modules (or modules (cdr argv)))
compile-plugins
compile-targets)
;; Ensure that Doom has been fully loaded, some of its state may be
;; pertinent to files compiled later.
(let ((doom--stage 'compile)
noninteractive)
;; Core libraries aren't fully loaded in a noninteractive session, so we
;; pretend to be interactive and reinitialize
(doom-initialize))
;; Assemble el files we want to compile; taking into account that MODULES
;; may be a list of MODULE/SUBMODULE strings from the command line.
(setq
modules (or modules (append (list doom-core-dir) (doom-module-load-path)))
compile-targets
(cl-loop for target in modules
if (equal target ":core")
nconc (nreverse (doom-files-under doom-core-dir :match "\\.el$"))
and collect (expand-file-name "init.el" doom-private-dir)
if (equal target ":private")
nconc (nreverse (doom-files-under doom-private-dir :match "\\.el$"))
if (equal target ":plugins")
do (setq compile-plugins t)
else if (file-directory-p target)
nconc (nreverse (doom-files-under target :match "\\.el$"))
else if (cl-member target doom-psuedo-module-dirs :test #'file-in-directory-p)
nconc (nreverse (doom-files-under it :match "\\.el$"))
else if (string-match "^\\([^/]+\\)/\\([^/]+\\)$" target)
nconc (nreverse (doom-files-under
(doom-module-locate-path
(intern (format ":%s" (match-string 1 target)))
(intern (match-string 2 target)))
:match "\\.el$"))
else if (file-exists-p target)
collect target
finally do (setq argv nil)))
(if compile-plugins
(byte-recompile-directory package-user-dir 0 t)
(if (not compile-targets)
(message "No targets to compile")
(condition-case ex
(let ((use-package-expand-minimally t))
(push (expand-file-name "init.el" doom-emacs-dir) compile-targets)
(dolist (target (cl-delete-duplicates (mapcar #'file-truename compile-targets) :test #'equal))
(when (or (not recompile-p)
(let ((elc-file (byte-compile-dest-file target)))
(and (file-exists-p elc-file)
(file-newer-than-file-p target elc-file))))
(let ((result (cond ((string-match-p "/\\(packages\\|doctor\\)\\.el$" target)
'no-byte-compile)
((doom-file-cookie-p target)
(byte-compile-file target))
('no-byte-compile)))
(short-name (if (file-in-directory-p target doom-emacs-dir)
(file-relative-name target doom-emacs-dir)
(abbreviate-file-name target))))
(cl-incf
(cond ((eq result 'no-byte-compile)
(message! (dark (white "⚠ Ignored %s" short-name)))
total-noop)
((null result)
(message! (red "✕ Failed to compile %s" short-name))
total-fail)
(t
(message! (green "✓ Compiled %s" short-name))
(quiet! (load target t t))
total-ok))))))
(message!
(bold
(color (if (= total-fail 0) 'green 'red)
"%s %d/%d file(s) (%d ignored)"
(if recompile-p "Recompiled" "Compiled")
total-ok (- (length compile-targets) total-noop)
total-noop))))
(error
(message! (red "\n%%s\n\n%%s\n\n%%s")
"There were breaking errors."
(error-message-string ex)
"Reverting changes...")
(quiet! (doom//clean-byte-compiled-files))
(message! (green "Finished (nothing was byte-compiled)")))))))))
;;;###autoload
(defun doom//clean-byte-compiled-files ()
"Delete all the compiled elc files in your Emacs configuration. This excludes
compiled packages.'"
(interactive)
(cl-loop with default-directory = doom-emacs-dir
for path in (append (doom-files-in doom-emacs-dir :match "\\.elc$")
(doom-files-in doom-psuedo-module-dirs :match "\\.elc$" :full t)
(doom-files-under doom-core-dir :match "\\.elc$")
(doom-files-under doom-modules-dirs :match "\\.elc$"))
for truepath = (file-truename path)
do (delete-file path)
and do
(message "✓ Deleted %s"
(if (file-in-directory-p truepath default-directory)
(file-relative-name truepath)
(abbreviate-file-name path)))
finally do (message "Everything is clean")))