diff --git a/bin/doom b/bin/doom index 46a06bf8d..9da3f4c96 100755 --- a/bin/doom +++ b/bin/doom @@ -5,25 +5,25 @@ :; case "$VERSION" in *\ 2[0-5].[0-9]) echo "Detected Emacs $VERSION"; echo "Doom only supports Emacs 26.1 and newer"; echo; exit 2 ;; esac :; DOOMBASE="$(dirname "$0")/.." :; [ "$1" = -d ] || [ "$1" = --debug ] && { shift; export DEBUG=1; } -:; [ "$1" = run ] && { cd "$DOOMBASE"; shift; exec $EMACS -q --no-splash -l bin/doom "$@"; exit 0; } +:; [ "$1" = run ] && { cd "$DOOMBASE"; shift; exec $EMACS -q --no-splash -l init.el "$@"; exit 0; } :; exec $EMACS --no-site-file --script "$0" -- "$@" :; exit 0 -(let* ((load-prefer-newer t) - (loaddir (file-name-directory (file-truename load-file-name))) +(let* ((loaddir (file-name-directory (file-truename load-file-name))) (emacsdir (getenv "EMACSDIR")) (user-emacs-directory (abbreviate-file-name (or emacsdir (expand-file-name "../" loaddir))))) + ;; + (load (expand-file-name "core/core.el" user-emacs-directory) nil t) + ;; HACK Load `cl' and site files manually so we can stop them from polluting ;; CLI logs with deprecation and file load messages. - (let ((inhibit-message t)) - (when (> emacs-major-version 26) - (require 'cl)) - (load "site-start" t t)) + (quiet! (when (> emacs-major-version 26) + (require 'cl)) + (load "site-start" t t)) - (push (expand-file-name "core" user-emacs-directory) load-path) - (require 'core) + (doom-log "Initializing Doom CLI") (require 'core-cli) (defcli! :main @@ -45,10 +45,10 @@ with a different private module." (setq user-emacs-directory (file-name-as-directory emacsdir)) (print! (info "EMACSDIR=%s") localdir)) (when doomdir - (setenv "DOOMDIR" doomdir) + (setenv "DOOMDIR" (file-name-as-directory doomdir)) (print! (info "DOOMDIR=%s") localdir)) (when localdir - (setenv "DOOMLOCALDIR" localdir) + (setenv "DOOMLOCALDIR" (file-name-as-directory localdir)) (print! (info "DOOMLOCALDIR=%s") localdir)) (when debug-p (setenv "DEBUG" "1") @@ -75,54 +75,50 @@ with a different private module." (when (or emacsdir doomdir localdir) (load! "core/core.el" user-emacs-directory)) - (cond ((not noninteractive) - (print! "Doom launched out of %s (test mode)" (path user-emacs-directory)) - (load! "init.el" user-emacs-directory) - (doom-run-all-startup-hooks-h)) - - ((null command) - (doom-cli-execute "help")) - - ((condition-case e - (let ((start-time (current-time))) - (and (doom-cli-execute command args) - (print! (success "Finished! (%.4fs)") - (float-time - (time-subtract (current-time) - start-time))))) - (user-error - (print! (error "%s\n") (error-message-string e)) - (print! (yellow "See 'doom help %s' for documentation on this command.") (car args)) - (error "")) ; Ensure non-zero exit code - ((debug error) - (print! (error "There was an unexpected error:")) - (print-group! - (print! "%s %s" (bold "Type:") (car e)) - (print! (bold "Message:")) - (print-group! - (print! "%s" (get (car e) 'error-message))) - (print! (bold "Data:")) - (print-group! - (if (cdr e) - (dolist (item (cdr e)) - (print! "%S" item)) - (print! "n/a"))) - (when (featurep 'straight) - (when (string-match-p (regexp-quote straight-process-buffer) - (error-message-string e)) - (print! (bold "Straight output:")) - (print-group! (print! "%s" (straight--process-get-output)))))) - (unless debug-on-error - (terpri) - (print! - (concat "Run the command again with the -d (or --debug) switch to enable debug\n" - "mode and (hopefully) generate a backtrace from this error:\n" - "\n %s\n\n" - "If you file a bug report, please include it!") - (string-join (append (list (file-name-nondirectory load-file-name) "-d" command) - args) - " ")) - (error ""))))))) ; Ensure non-zero exit code + (let (print-level print-gensym print-length) + (condition-case e + (if (null command) + (doom-cli-execute "help") + (let ((start-time (current-time))) + (and (doom-cli-execute command args) + (print! (success "Finished! (%.4fs)") + (float-time + (time-subtract (current-time) + start-time)))))) + (user-error + (print! (error "%s\n") (error-message-string e)) + (print! (yellow "See 'doom help %s' for documentation on this command.") (car args)) + (error "")) ; Ensure non-zero exit code + ((debug error) + (print! (error "There was an unexpected error:")) + (print-group! + (print! "%s %s" (bold "Type:") (car e)) + (print! (bold "Message:")) + (print-group! + (print! "%s" (get (car e) 'error-message))) + (print! (bold "Data:")) + (print-group! + (if (cdr e) + (dolist (item (cdr e)) + (print! "%S" item)) + (print! "n/a"))) + (when (and (bound-and-true-p straight-process-buffer) + (string-match-p (regexp-quote straight-process-buffer) + (error-message-string e))) + (print! (bold "Straight output:")) + (print-group! (print! "%s" (straight--process-get-output))))) + (unless debug-on-error + (terpri) + (print! + (concat "Run the command again with the -d (or --debug) switch to enable debug\n" + "mode and (hopefully) generate a backtrace from this error:\n" + "\n %s\n\n" + "If you file a bug report, please include it!") + (string-join (append (list (file-name-nondirectory load-file-name) "-d" command) + args) + " ")) + ;; Ensure non-zero exit code + (error "")))))) (doom-cli-execute :main (cdr (member "--" argv))) (setq argv nil)) diff --git a/core/autoload/config.el b/core/autoload/config.el index cacd0d32d..02d78d725 100644 --- a/core/autoload/config.el +++ b/core/autoload/config.el @@ -55,14 +55,19 @@ And jumps to your `doom!' block." (defmacro doom--if-compile (command on-success &optional on-failure) (declare (indent 2)) - `(with-current-buffer (compile ,command) - (add-hook - 'compilation-finish-functions - (lambda (_buf status) - (if (equal status "finished\n") - ,on-success - ,on-failure)) - nil 'local))) + (let ((windowsym (make-symbol "doom-sync-window"))) + `(with-current-buffer (compile ,command t) + (let ((,windowsym (get-buffer-window (current-buffer)))) + (select-window ,windowsym) + (add-hook + 'compilation-finish-functions + (lambda (_buf status) + (if (equal status "finished\n") + (progn + (delete-window ,windowsym) + ,on-success) + ,on-failure)) + nil 'local))))) ;;;###autoload (defun doom/reload () @@ -89,7 +94,7 @@ Runs `doom-reload-hook' afterwards." (doom-initialize-modules 'force) (general-auto-unbind-keys t))) (run-hook-wrapped 'doom-reload-hook #'doom-try-run-hook) - (print! (success "Config successfully reloaded!"))) + (message "Config successfully reloaded!")) (user-error "Failed to reload your config"))) ;;;###autoload diff --git a/core/autoload/debug.el b/core/autoload/debug.el index c6d58e8f9..ed1683be7 100644 --- a/core/autoload/debug.el +++ b/core/autoload/debug.el @@ -1,5 +1,36 @@ ;;; core/autoload/debug.el -*- lexical-binding: t; -*- +;; +;;; Doom's debug mode + +;;;###autoload +(defvar doom-debug-variables + '(doom-debug-mode + init-file-debug + debug-on-error + garbage-collection-messages + use-package-verbose + jka-compr-verbose + lsp-log-io + gcmh-verbose + magit-refresh-verbose + url-debug) + "A list of variable to toggle on `doom/toggle-debug-mode'.") + +;;;###autoload +(defun doom/toggle-debug-mode (&optional arg) + "Toggle `debug-on-error' and `doom-debug-mode' for verbose logging." + (interactive (list (or current-prefix-arg 'toggle))) + (let ((value + (cond ((eq arg 'toggle) (not doom-debug-mode)) + ((> (prefix-numeric-value arg) 0))))) + (mapc (doom-rpartial #'set value) doom-debug-variables) + (message "Debug mode %s" (if value "on" "off")))) + + +;; +;;; Hooks + ;;;###autoload (defun doom-run-all-startup-hooks-h () "Run all startup Emacs hooks. Meant to be executed after starting Emacs with @@ -17,18 +48,12 @@ ;; ;;; Helpers -(defun doom-template-insert (template) - "TODO" - (let ((file (expand-file-name (format "templates/%s" template) doom-core-dir))) - (when (file-exists-p file) - (insert-file-contents file)))) - (defsubst doom--collect-forms-in (file form) (when (file-readable-p file) (let (forms) (with-temp-buffer (insert-file-contents file) - (delay-mode-hooks (emacs-lisp-mode)) + (let (emacs-lisp-mode) (emacs-lisp-mode)) (while (re-search-forward (format "(%s " (regexp-quote form)) nil t) (let ((ppss (syntax-ppss))) (unless (or (nth 4 ppss) @@ -45,7 +70,7 @@ ready to be pasted in a bug report on github." (require 'vc-git) (require 'core-packages) (let ((default-directory doom-emacs-dir) - (doom-modules (doom-modules))) + (doom-modules (doom-module-list))) (letf! (defun sh (&rest args) (cdr (apply #'doom-call-process args))) `((emacs (version . ,emacs-version) @@ -243,48 +268,62 @@ Some items are not supported by the `nsm.el' module." (file (make-temp-file "doom-sandbox-"))) (require 'package) (with-temp-file file - (insert - (prin1-to-string - (macroexp-progn - (append `((setq noninteractive nil - init-file-debug t - load-path ',load-path - package--init-file-ensured t - package-user-dir ,package-user-dir - package-archives ',package-archives - user-emacs-directory ,doom-emacs-dir) - (with-eval-after-load 'undo-tree - ;; undo-tree throws errors because `buffer-undo-tree' isn't - ;; corrrectly initialized - (setq-default buffer-undo-tree (make-undo-tree)))) - (pcase mode - (`vanilla-doom+ ; Doom core + modules - private config - `((load-file ,(expand-file-name "core.el" doom-core-dir)) - (doom-initialize) - (doom-initialize-core) - (add-hook 'window-setup-hook #'doom-display-benchmark-h) - (setq doom-modules ',doom-modules) - (maphash (lambda (key plist) - (let ((doom--current-module key) - (doom--current-flags (plist-get plist :flags))) - (load! "init" (doom-module-locate-path (car key) (cdr key)) t))) - doom-modules) - (maphash (lambda (key plist) - (let ((doom--current-module key) - (doom--current-flags (plist-get plist :flags))) - (load! "config" (doom-module-locate-path (car key) (cdr key)) t))) - doom-modules) - (run-hook-wrapped 'doom-init-modules-hook #'doom-try-run-hook) - (doom-run-all-startup-hooks-h))) - (`vanilla-doom ; only Doom core - `((load-file ,(expand-file-name "core.el" doom-core-dir)) - (doom-initialize) - (doom-initialize-core) - (doom-run-all-startup-hooks-h))) - (`vanilla ; nothing loaded - `((package-initialize))))))) - "\n(unwind-protect (progn\n" contents "\n)\n" - (format "(delete-file %S))" file))) + (prin1 `(progn + (setq noninteractive nil + process-environment ',doom--initial-process-environment + exec-path ',doom--initial-exec-path + init-file-debug t + load-path ',load-path + package--init-file-ensured t + package-user-dir ,package-user-dir + package-archives ',package-archives + user-emacs-directory ,doom-emacs-dir) + (with-eval-after-load 'undo-tree + ;; undo-tree throws errors because `buffer-undo-tree' isn't + ;; correctly initialized + (setq-default buffer-undo-tree (make-undo-tree))) + (ignore-errors + (delete-directory ,(expand-file-name "auto-save-list" doom-emacs-dir) 'parents))) + (current-buffer)) + (prin1 `(unwind-protect + (defun --run-- () ,(read (concat "(progn\n" contents "\n)"))) + (delete-file ,file)) + (current-buffer)) + (prin1 (pcase mode + (`vanilla-doom+ ; Doom core + modules - private config + `(progn + (load-file ,(expand-file-name "core.el" doom-core-dir)) + (setq doom-modules-dirs (list doom-modules-dir)) + (let ((doom-init-modules-p t)) + (doom-initialize) + (doom-initialize-core-modules)) + (setq doom-modules ',doom-modules) + (--run--) + (maphash (lambda (key plist) + (let ((doom--current-module key) + (doom--current-flags (plist-get plist :flags))) + (load! "init" (doom-module-locate-path (car key) (cdr key)) t))) + doom-modules) + (maphash (lambda (key plist) + (let ((doom--current-module key) + (doom--current-flags (plist-get plist :flags))) + (load! "config" (doom-module-locate-path (car key) (cdr key)) t))) + doom-modules) + (run-hook-wrapped 'doom-init-modules-hook #'doom-try-run-hook) + (doom-run-all-startup-hooks-h))) + (`vanilla-doom ; only Doom core + `(progn + (load-file ,(expand-file-name "core.el" doom-core-dir)) + (let ((doom-init-modules-p t)) + (doom-initialize) + (doom-initialize-core-modules)) + (--run--) + (doom-run-all-startup-hooks-h))) + (`vanilla ; nothing loaded + `(progn + (package-initialize) + (--run--)))) + (current-buffer))) (let ((args (if (eq mode 'doom) (list "-l" file) (list "-Q" "-l" file)))) @@ -343,7 +382,7 @@ to reproduce bugs and determine if Doom is to blame." (doom-sandbox-emacs-lisp-mode) (setq-local default-directory doom-emacs-dir) (unless (buffer-live-p exists) - (doom-template-insert "VANILLA_SANDBOX") + (insert-file-contents (doom-glob doom-core-dir "templates/VANILLA_SANDBOX")) (let ((contents (substitute-command-keys (buffer-string)))) (erase-buffer) (insert contents "\n"))) @@ -378,20 +417,3 @@ will be automatically appended to the result." (profiler-report) (profiler-stop)) (setq doom--profiler (not doom--profiler))) - -;;;###autoload -(defun doom/toggle-debug-mode (&optional arg) - "Toggle `debug-on-error' and `doom-debug-mode' for verbose logging." - (interactive (list (or current-prefix-arg 'toggle))) - (let ((value - (cond ((eq arg 'toggle) (not doom-debug-mode)) - ((> (prefix-numeric-value arg) 0))))) - (setq doom-debug-mode value - debug-on-error value - garbage-collection-messages value - use-package-verbose value - jka-compr-verbose value - lsp-log-io value - gcmh-verbose value - magit-refresh-verbose value) - (message "Debug mode %s" (if value "on" "off")))) diff --git a/core/autoload/help.el b/core/autoload/help.el index 060731b18..92f83f5d0 100644 --- a/core/autoload/help.el +++ b/core/autoload/help.el @@ -427,8 +427,8 @@ If prefix arg is present, refresh the cache." (let ((guess (or (function-called-at-point) (symbol-at-point)))) (require 'finder-inf nil t) - (require 'core-packages) - (doom-initialize-packages) + (require 'package) + (require 'straight) (let ((packages (delete-dups (append (mapcar #'car package-alist) (mapcar #'car package--builtins) @@ -503,7 +503,7 @@ If prefix arg is present, refresh the cache." (modules (if (gethash (symbol-name package) straight--build-cache) (doom-package-get package :modules) - (plist-get (cdr (assq package (doom-packages-list 'all))) + (plist-get (cdr (assq package (doom-package-list 'all))) :modules))) (package--print-help-section "Modules") (insert "Declared by the following Doom modules:\n") diff --git a/core/autoload/packages.el b/core/autoload/packages.el index 349f34805..7b677e129 100644 --- a/core/autoload/packages.el +++ b/core/autoload/packages.el @@ -1,276 +1,5 @@ ;;; core/autoload/packages.el -*- lexical-binding: t; -*- -;; -;;; Package metadata - -;;;###autoload -(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))) - -;;;###autoload -(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))) - -;;;###autoload -(defun doom-package-recipe (package &optional prop nil-value) - "Returns the `straight' recipe PACKAGE was registered with." - (let ((plist (gethash (symbol-name package) straight--recipe-cache))) - (if prop - (if (plist-member plist prop) - (plist-get plist prop) - nil-value) - plist))) - -;;;###autoload -(defun doom-package-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))) - -;;;###autoload -(defun doom-package-build-recipe (package &optional prop nil-value) - "Returns the `straight' recipe PACKAGE was installed with." - (let ((plist (nth 2 (gethash (symbol-name package) straight--build-cache)))) - (if prop - (if (plist-member plist prop) - (plist-get plist prop) - nil-value) - plist))) - -;;;###autoload -(defun doom-package-build-time (package) - "TODO" - (car (gethash (symbol-name package) straight--build-cache))) - -;;;###autoload -(defun doom-package-dependencies (package &optional recursive _noerror) - "Return a list of dependencies for a package." - (let ((deps (nth 1 (gethash (symbol-name package) straight--build-cache)))) - (if recursive - (nconc deps (mapcan (lambda (dep) (doom-package-dependencies dep t t)) - deps)) - deps))) - -;;;###autoload -(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 - -;;;###autoload -(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)) - -;;;###autoload -(defun doom-package-installed-p (package) - "Return non-nil if PACKAGE (a symbol) is installed." - (file-directory-p (straight--build-dir (symbol-name package)))) - -;;;###autoload -(defun doom-package-registered-p (package) - "Return non-nil if PACKAGE (a symbol) has been registered with `package!'. - -Excludes packages that have a non-nil :built-in property." - (when-let (plist (doom-package-get package)) - (not (plist-get plist :ignore)))) - -;;;###autoload -(defun doom-package-private-p (package) - "Return non-nil if PACKAGE was installed by the user's private config." - (assq :private (doom-package-get package :modules))) - -;;;###autoload -(defun doom-package-protected-p (package) - "Return non-nil if PACKAGE is protected. - -A protected package cannot be deleted and will be auto-installed if missing." - (memq package doom-core-packages)) - -;;;###autoload -(defun doom-package-core-p (package) - "Return non-nil if PACKAGE is a core Doom package." - (or (doom-package-protected-p package) - (assq :core (doom-package-get package :modules)))) - -;;;###autoload -(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))) - -;;;###autoload -(defun doom-package-different-recipe-p (name) - "Return t if a package named NAME (a symbol) has a different recipe than it -was installed with." - (cl-check-type name symbol) - ;; 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 list getters - -(defun doom--read-module-packages-file (file &optional noeval noerror) - (with-temp-buffer ; prevent buffer-local settings from propagating - (condition-case e - (if (not noeval) - (load file noerror t t) - (when (file-readable-p file) - (insert-file-contents file) - (delay-mode-hooks (emacs-lisp-mode)) - (while (search-forward "(package! " nil t) - (save-excursion - (goto-char (match-beginning 0)) - (unless (let ((ppss (syntax-ppss))) - (or (nth 3 ppss) - (nth 4 ppss))) - (cl-destructuring-bind (name . plist) - (cdr (sexp-at-point)) - (push (cons - name (plist-put - plist :modules - (list (doom-module-from-path file)))) - doom-packages))))))) - ((debug error) - (signal 'doom-package-error - (list (doom-module-from-path file) - file - e)))))) - -;;;###autoload -(defun doom-package-list (&optional all-p) - "Retrieve a list of explicitly declared packages from enabled modules. - -This excludes core packages listed in `doom-core-packages'. - -If ALL-P, gather packages unconditionally across all modules, including disabled -ones." - (let ((doom-interactive-mode t) - (doom-modules (doom-modules)) - doom-packages) - (doom--read-module-packages-file - (doom-path doom-core-dir "packages.el") all-p t) - (let ((private-packages (doom-path doom-private-dir "packages.el"))) - (unless all-p - ;; We load the private packages file twice to ensure disabled packages - ;; are seen ASAP, and a second time to ensure privately overridden - ;; packages are properly overwritten. - (doom--read-module-packages-file private-packages nil t)) - (if all-p - (mapc #'doom--read-module-packages-file - (doom-files-in doom-modules-dir - :depth 2 - :match "/packages\\.el$")) - (cl-loop for key being the hash-keys of doom-modules - for path = (doom-module-path (car key) (cdr key) "packages.el") - for doom--current-module = key - do (doom--read-module-packages-file path nil t))) - (doom--read-module-packages-file private-packages all-p t)) - (nreverse doom-packages))) - -;;;###autoload -(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))))))) - -;;;###autoload -(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)))))) - -;;;###autoload -(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 no-build &allow-other-keys) - recipe - (unless (or (null local-repo) - (eq type 'built-in) - no-build) - (push recipe recipes)))) - (nreverse recipes))) - -;;;###autoload -(defmacro doom-with-package-recipes (recipes binds &rest body) - "TODO" - (declare (indent 2)) - (let ((recipe-var (make-symbol "recipe")) - (recipes-var (make-symbol "recipes"))) - `(let* ((,recipes-var ,recipes) - (built ()) - (straight-use-package-pre-build-functions - (cons (lambda (pkg) (cl-pushnew pkg built :test #'equal)) - straight-use-package-pre-build-functions))) - (dolist (,recipe-var ,recipes-var) - (cl-block nil - (straight--with-plist (append (list :recipe ,recipe-var) ,recipe-var) - ,(doom-enlist binds) - ,@body))) - (nreverse built)))) - - -;; -;;; Main functions - ;;;###autoload (defun doom/reload-packages () "Reload `doom-packages', `package' and `quelpa'." @@ -280,47 +9,185 @@ ones." (doom-initialize-packages t) (message "Reloading packages...DONE")) -;;;###autoload -(defun doom/update-pinned-package-form (&optional select) - "Inserts or updates a `:pin' for the `package!' statement at point. +;; +;;; Bump commands + +(defun doom--package-full-recipe (package plist) + (doom-plist-merge + (plist-get plist :recipe) + (or (cdr (straight-recipes-retrieve package)) + (plist-get (cdr (assq package doom-packages)) + :recipe)))) + +(defun doom--package-to-bump-string (package plist) + "Return a PACKAGE and its PLIST in 'username/repo@commit' format." + (format "%s@%s" + (plist-get (doom--package-full-recipe package plist) :repo) + (substring-no-properties (plist-get plist :pin) 0 7))) + +(defun doom--package-at-point (&optional point) + "Return the package and plist from the (package! PACKAGE PLIST...) at point." + (save-match-data + (save-excursion + (and point (goto-char point)) + (if (eq (sexp-at-point) 'package!) + (progn + (backward-sexp) + (backward-char) + t) + (while (and (search-backward "(package!" nil t) + (doom-point-in-string-or-comment-p)))) + (unless (or (doom-point-in-string-or-comment-p) + (not (eq (car-safe (sexp-at-point)) 'package!))) + (cl-destructuring-bind (beg . end) + (bounds-of-thing-at-point 'sexp) + (let ((package (let (doom-packages) + (eval (sexp-at-point) t)))) + (list :beg beg + :end end + :package (car package) + :plist (cdr package)))))))) + +;;;###autoload +(defun doom/bumpify-package-at-point () + "Convert `package!' call at point to a bump string." + (interactive) + (cl-destructuring-bind (&key package plist beg end) + (doom--package-at-point) + (when-let (str (doom--package-to-bump-string package plist)) + (goto-char beg) + (delete-region beg end) + (insert str)))) + +;;;###autoload +(defun doom/bumpify-packages-in-buffer () + "Convert all `package!' calls in buffer into bump strings." + (interactive) + (save-excursion + (goto-char (point-min)) + (while (search-forward "(package!" nil t) + (unless (doom-point-in-string-or-comment-p) + (doom/bumpify-package-at-point))))) + +;;;###autoload +(defun doom/bump-package-at-point (&optional select) + "Inserts or updates a `:pin' for the `package!' statement at point. +Grabs the latest commit id of the package using 'git'." + (interactive "P") + (doom-initialize-packages) + (cl-destructuring-bind (&key package plist beg end) + (or (doom--package-at-point) + (user-error "Not on a `package!' call")) + (let* ((recipe (doom--package-full-recipe package plist)) + (branch (or (plist-get recipe :branch) + straight-vc-git-default-branch)) + (oldid (or (plist-get plist :pin) + (doom-package-get package :pin))) + id url) + (when (or (eq (plist-get plist :built-in) t) + (and (eq (plist-get plist :built-in) 'prefer) + (locate-library (symbol-name package)))) + (user-error "%s: skipping built-in package" package)) + (setq url (straight-vc-git--destructure recipe (upstream-repo upstream-host) + (straight-vc-git--encode-url upstream-repo upstream-host)) + id (when url + (cdr (doom-call-process + "git" "ls-remote" url + (unless select + (or branch straight-vc-git-default-branch)))))) + (unless id + (user-error "%s: no id from %s" package url)) + (let* ((id (car (split-string + (if select + (completing-read "Commit: " (split-string id "\n" t)) + id))))) + (when (and oldid (equal oldid id)) + (user-error "%s: no update necessary" package)) + (save-excursion + (if (re-search-forward ":pin +\"\\([^\"]+\\)\"" end t) + (replace-match id t t nil 1) + (thing-at-point--end-of-sexp) + (backward-char) + (insert " :pin " (prin1-to-string id)))) + (message "Updated %S: %s -> %s" + package + (substring oldid 0 10) + (substring id 0 10)))))) + +;;;###autoload +(defun doom/bump-packages-in-buffer (&optional select) + "Inserts or updates a `:pin' for the `package!' statement at point. Grabs the latest commit id of the package using 'git'." (interactive "P") - ;; REVIEW Better error handling - ;; TODO Insert a new `package!' if no `package!' at poin - (require 'straight) - (ignore-errors - (while (and (atom (sexp-at-point)) - (not (bolp))) - (forward-sexp -1))) (save-excursion - (if (not (eq (sexp-at-point) 'package!)) - (user-error "Not on a `package!' call") - (backward-char) - (let* ((recipe (cdr (sexp-at-point))) - (package (car recipe)) - (oldid (doom-package-get package :pin)) - (id - (cdr (doom-call-process - "git" "ls-remote" - (straight-vc-git--destructure - (doom-plist-merge - (plist-get (cdr recipe) :recipe) - (or (cdr (straight-recipes-retrieve package)) - (plist-get (cdr (assq package doom-packages)) :recipe))) - (upstream-repo upstream-host) - (straight-vc-git--encode-url upstream-repo upstream-host)))))) - (unless id - (user-error "No id for %S package" package)) - (let* ((id (if select - (car (split-string (completing-read "Commit: " (split-string id "\n" t)))) - (car (split-string id)))) - (id (substring id 0 10))) - (if (and oldid (string-match-p (concat "^" oldid) id)) - (user-error "No update necessary") - (if (re-search-forward ":pin +\"\\([^\"]+\\)\"" (cdr (bounds-of-thing-at-point 'sexp)) t) - (replace-match id t t nil 1) - (thing-at-point--end-of-sexp) - (backward-char) - (insert " :pin " (prin1-to-string id))) - (message "Updated %S: %s -> %s" package oldid id))))))) + (goto-char (point-min)) + (doom-initialize-packages) + (let (packages) + (while (search-forward "(package! " nil t) + (unless (let ((ppss (syntax-ppss))) + (or (nth 4 ppss) + (nth 3 ppss) + (save-excursion + (and (goto-char (match-beginning 0)) + (not (plist-member (sexp-at-point) :pin)))))) + (condition-case e + (push (doom/bump-package-at-point) packages) + (user-error (message "%s" (error-message-string e)))))) + (if packages + (message "Updated %d packages\n- %s" (length packages) (string-join packages "\n- ")) + (message "No packages to update"))))) + +;;;###autoload +(defun doom/bump-module (category &optional module select) + "Bump packages in CATEGORY MODULE. +If SELECT (prefix arg) is non-nil, prompt you to choose a specific commit for +each package." + (interactive + (append + (mapcar #'intern + (split-string + (completing-read + "Bump module: " + (let ((modules (doom-module-list 'all))) + (mapcar (lambda (m) + (if (listp m) + (format "%s %s" (car m) (cdr m)) + (format "%s" m))) + (append (list ":private") + (delete-dups (mapcar #'car modules)) + modules))) + nil t nil nil) + " " t)) + (list current-prefix-arg))) + (if-let (packages-file (if (eq category :private) + (doom-glob doom-private-dir "packages.el") + (doom-module-locate-path category module "packages.el"))) + (with-current-buffer + (or (get-file-buffer packages-file) + (find-file-noselect packages-file)) + (doom/bump-packages-in-buffer select)) + (user-error "Module %s %s has no packages.el file"))) + +;;;###autoload +(defun doom/bump-package (package) + "Bump PACKAGE in all modules that install it." + (interactive + (list (completing-read "Bump package: " + (mapcar #'car (doom-package-list 'all))))) + + (let* ((packages (doom-package-list 'all)) + (modules (plist-get (alist-get package packages) :modules))) + (unless modules + (user-error "This package isn't installed by any Doom module")) + (dolist (module modules) + (when-let (packages-file (doom-module-locate-path (car module) (cdr module))) + (doom/bump-module (car module) (cdr module)))))) + + +;; +;;; Bump commits + +;;;###autoload +(defun doom/commit-bumps () + (interactive)) diff --git a/core/autoload/plist.el b/core/autoload/plist.el index 87f897598..fc291dee7 100644 --- a/core/autoload/plist.el +++ b/core/autoload/plist.el @@ -35,24 +35,6 @@ list, the pair is destructured into (CAR . CDR)." "Delete PROP from PLIST in-place." `(setq ,plist (doom-plist-delete ,plist ,prop))) -;;;###autoload -(defmacro with-plist! (plist props &rest body) - "With props bound from PLIST to PROPS, evaluate BODY. - -PROPS is a list of symbols. Each one is converted to a keyword and then its -value is looked up in the PLIST and bound to the symbol for the duration of -BODY." - (declare (indent 2)) - (let ((plist-sym (make-symbol "plist"))) - `(let* ((,plist-sym ,plist) - ,@(cl-loop for prop in props - collect - `(,prop - (plist-get - ,plist-sym - ,(doom-keyword-intern (symbol-name prop)))))) - ,@body))) - ;; ;;; Library diff --git a/core/autoload/cli.el b/core/autoload/process.el similarity index 88% rename from core/autoload/cli.el rename to core/autoload/process.el index f0582ef1c..66d5d6a8c 100644 --- a/core/autoload/cli.el +++ b/core/autoload/process.el @@ -1,7 +1,4 @@ -;;; core/autoload/cli.el -*- lexical-binding: t; -*- - -;; -;;; Library +;;; core/autoload/process.el -*- lexical-binding: t; -*- ;;;###autoload (defun doom-call-process (command &rest args) @@ -10,7 +7,7 @@ Returns (STATUS . OUTPUT) when it is done, where STATUS is the returned error code of the process and OUTPUT is its stdout output." (with-temp-buffer - (cons (or (apply #'call-process command nil t nil args) + (cons (or (apply #'call-process command nil t nil (remq nil args)) -1) (string-trim (buffer-string))))) @@ -28,7 +25,7 @@ Warning: freezes indefinitely on any stdin prompt." (cons (let ((process (make-process :name "doom-sh" :buffer (current-buffer) - :command (cons command args) + :command (cons command (remq nil args)) :connection-type 'pipe)) done-p) (set-process-filter diff --git a/core/autoload/sessions.el b/core/autoload/sessions.el index de08be93d..ece9fe2f9 100644 --- a/core/autoload/sessions.el +++ b/core/autoload/sessions.el @@ -71,6 +71,9 @@ "TODO" (add-hook 'window-setup-hook #'doom-load-session 'append)) +;;;###autoload +(add-to-list 'command-switch-alist (cons "--restore" #'doom-restore-session-handler)) + ;; ;;; Commands diff --git a/core/cli/byte-compile.el b/core/cli/byte-compile.el index 49121e0ff..b8831cd7c 100644 --- a/core/cli/byte-compile.el +++ b/core/cli/byte-compile.el @@ -2,7 +2,9 @@ (defcli! (compile c) ((recompile-p ["-r" "--recompile"]) - &rest targets) + (core-p ["-c" "--core"]) + (private-p ["-p" "--private"]) + (verbose-p ["-v" "--verbose"])) "Byte-compiles your config or selected modules. compile [TARGETS...] @@ -11,8 +13,23 @@ Accepts :core and :private as special arguments, which target Doom's core files and your private config files, respectively. To recompile your packages, use -'doom rebuild' instead." - (doom-cli-byte-compile targets recompile-p)) +'doom build' instead." + (doom-cli-byte-compile + (if (or core-p private-p) + (append (when core-p + (list (doom-glob doom-emacs-dir "init.el") + doom-core-dir)) + (when private-p + (list doom-private-dir))) + (append (list (doom-glob doom-emacs-dir "init.el") + doom-core-dir) + (cl-remove-if-not + ;; Only compile Doom's modules + (lambda (path) (file-in-directory-p path doom-emacs-dir)) + ;; Omit `doom-private-dir', which is always first + (cdr (doom-module-load-path))))) + recompile-p + verbose-p)) (defcli! clean () "Delete all *.elc files." @@ -25,17 +42,20 @@ and your private config files, respectively. To recompile your packages, use (defun doom--byte-compile-ignore-file-p (path) (let ((filename (file-name-nondirectory path))) - (or (string-prefix-p "." filename) + (or (not (equal (file-name-extension path) "el")) + (member filename (list "packages.el" "doctor.el")) + (string-prefix-p "." filename) (string-prefix-p "test-" filename) - (string-suffix-p ".example.el" filename) - (not (equal (file-name-extension path) "el")) - (member filename (list "packages.el" "doctor.el"))))) + (string-prefix-p "flycheck_" filename) + (string-suffix-p ".example.el" filename)))) -(cl-defun doom-cli-byte-compile (&optional modules recompile-p) +(cl-defun doom-cli-byte-compile (&optional targets recompile-p verbose-p) "Byte compiles your emacs configuration. init.el is always byte-compiled by this. +If TARGETS is specified, as a list of direcotries + 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. @@ -48,138 +68,109 @@ Use `doom-clean-byte-compiled-files' or `make clean' to reverse byte-compilation. If RECOMPILE-P is non-nil, only recompile out-of-date files." - (let ((default-directory doom-emacs-dir) - (doom-modules (doom-modules)) - (byte-compile-verbose doom-debug-mode) - (byte-compile-warnings '(not free-vars unresolved noruntime lexical make-local)) - - ;; In case it is changed during compile-time - (auto-mode-alist auto-mode-alist) - (noninteractive t) - - targets) - - (let (target-dirs) - (dolist (module (delete-dups modules)) - (pcase module - (":core" - (push (doom-glob doom-emacs-dir "init.el") targets) - (push doom-core-dir target-dirs)) - (":private" - (push doom-private-dir target-dirs)) - ((pred file-directory-p) - (push module target-dirs)) - ((pred (string-match "^\\([^/]+\\)/\\([^/]+\\)$")) - (push (doom-module-locate-path - (doom-keyword-intern (match-string 1 module)) - (intern (match-string 2 module))) - target-dirs)) - (_ (user-error "%S is not a valid target" module)))) - - (and (or (null modules) (member ":private" modules)) - (not recompile-p) - (not (or doom-auto-accept - (y-or-n-p - (concat "Warning: byte compiling is for advanced users. It will interfere with your\n" - "efforts to debug issues. It is not recommended you do it if you frequently\n" - "tinker with your Emacs config.\n\n" - "Alternatively, use `bin/doom compile :core` instead to byte-compile only the\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" - "`bin/doom clean` to clear out your *.elc files.\n\n" - "Byte-compile anyway?")))) - (user-error "Aborting")) + (let* ((default-directory doom-emacs-dir) + (targets (nreverse (delete-dups targets))) + ;; In case it is changed during compile-time + (auto-mode-alist auto-mode-alist) + kill-emacs-hook kill-buffer-query-functions) + (let ((after-load-functions + (if (null targets) + after-load-functions + ;; Assemble el files we want to compile, and preserve in the order + ;; they are loaded in, so we don't run into any scary catch-22s + ;; while byte-compiling, like missing macros. + (cons (let ((target-dirs (cl-remove-if-not #'file-directory-p targets))) + (lambda (path) + (and (not (doom--byte-compile-ignore-file-p path)) + (cl-find-if (doom-partial #'file-in-directory-p path) + target-dirs) + (cl-pushnew path targets)))) + after-load-functions)))) + (doom-log "Reloading Doom in preparation for byte-compilation") ;; But first we must be sure that Doom and your private config have been ;; fully loaded. Which usually aren't so in an noninteractive session. - (let ((doom-interactive-mode 'byte-compile)) - (doom-initialize) - (doom-initialize-packages) - (doom-initialize-core)) + (let ((load-prefer-newer t) + (noninteractive t) + doom-interactive-mode) + (doom-initialize 'force) + (quiet! (doom-initialize-packages)))) - ;; - (unless target-dirs - (push (doom-glob doom-emacs-dir "init.el") targets) - ;; If no targets were supplied, then we use your module list. - (appendq! target-dirs - (list doom-core-dir) - (nreverse - (cl-remove-if-not - (lambda (path) (file-in-directory-p path doom-emacs-dir)) - ;; Omit `doom-private-dir', which is always first - (cdr (doom-module-load-path)))))) + (if (null targets) + (print! (info "No targets to %scompile" (if recompile-p "re" ""))) + (print! (start "%scompiling your config...") + (if recompile-p "Re" "Byte-")) - ;; Assemble el files we want to compile; taking into account that MODULES - ;; may be a list of MODULE/SUBMODULE strings from the command line. - (appendq! targets - (doom-files-in target-dirs - :match "\\.el$" - :filter #'doom--byte-compile-ignore-file-p))) + (dolist (dir + (cl-remove-if-not #'file-directory-p targets) + (setq targets (cl-remove-if #'file-directory-p targets))) + (prependq! targets + (doom-files-in + dir :match "\\.el" :filter #'doom--byte-compile-ignore-file-p))) - (unless targets - (print! - (if targets - (warn "Couldn't find any valid targets") - (info "No targets to %scompile" (if recompile-p "re" "")))) - (cl-return nil)) - - (print! - (start (if recompile-p - "Recompiling stale elc files..." - "Byte-compiling your config (may take a while)..."))) - (print-group! - (require 'use-package) - (condition-case e - (let ((total-ok 0) - (total-fail 0) - (total-noop 0) - (use-package-defaults use-package-defaults) - (use-package-expand-minimally t) - kill-emacs-hook kill-buffer-query-functions) - ;; Prevent packages from being loaded at compile time if they - ;; don't meet their own predicates. - (push (list :no-require t - (lambda (_name args) - (or (when-let (pred (or (plist-get args :if) - (plist-get args :when))) - (not (eval pred t))) - (when-let (pred (plist-get args :unless)) - (eval pred t))))) - use-package-defaults) - - (unless recompile-p - (doom-clean-byte-compiled-files)) - - (dolist (target (delete-dups (delq nil targets))) - (cl-incf - (if (not (or (not recompile-p) - (let ((elc-file (byte-compile-dest-file target))) - (and (file-exists-p elc-file) - (file-newer-than-file-p target elc-file))))) - total-noop - (pcase (if (doom-file-cookie-p target "if" t) - (byte-compile-file target) - 'no-byte-compile) - (`no-byte-compile - (print! (info "Ignored %s") (relpath target)) - total-noop) - (`nil - (print! (error "Failed to compile %s") (relpath target)) - total-fail) - (_ - (print! (success "Compiled %s") (relpath target)) - (load target t t) - total-ok))))) - (print! (class (if (= total-fail 0) 'success 'error) - "%s %d/%d file(s) (%d ignored)") - (if recompile-p "Recompiled" "Compiled") - total-ok (- (length targets) total-noop) - total-noop) - t) - ((debug error) - (print! (error "\nThere were breaking errors.\n\n%s") - "Reverting changes...") - (signal 'doom-error (list 'byte-compile e))))))) + (print-group! + (require 'use-package) + (condition-case-unless-debug e + (let* ((total-ok 0) + (total-fail 0) + (total-noop 0) + (byte-compile-verbose nil) + (byte-compile-warnings '(not free-vars unresolved noruntime lexical make-local)) + (byte-compile-dynamic-docstrings t) + (use-package-compute-statistics nil) + (use-package-defaults use-package-defaults) + (use-package-expand-minimally t) + (targets (delete-dups targets)) + (modules (seq-group-by (lambda (t) (doom-module-from-path t)) targets)) + (total-files (length targets)) + (total-modules (length modules)) + (i 0) + last-module) + ;; Prevent packages from being loaded at compile time if they + ;; don't meet their own predicates. + (push (list :no-require t + (lambda (_name args) + (or (when-let (pred (or (plist-get args :if) + (plist-get args :when))) + (not (eval pred t))) + (when-let (pred (plist-get args :unless)) + (eval pred t))))) + use-package-defaults) + (dolist (module-files modules) + (cl-incf i) + (dolist (target (cdr module-files)) + (let ((elc-file (byte-compile-dest-file target))) + (cl-incf + (if (and recompile-p (not (file-newer-than-file-p target elc-file))) + total-noop + (pcase (if (not (doom-file-cookie-p target "if" t)) + 'no-byte-compile + (unless (equal last-module (car module-files)) + (print! (success "(% 3d/%d) Compiling %s %s module...") + i total-modules (caar module-files) (cdar module-files)) + (setq last-module (car module-files))) + (if verbose-p + (byte-compile-file target) + (quiet! (byte-compile-file target)))) + (`no-byte-compile + (print! (debug "(% 3d/%d) Ignored %s") + i total-modules (relpath target)) + total-noop) + (`nil + (print! (error "(% 3d/%d) Failed to compile %s") + i total-modules (relpath target)) + total-fail) + (_ total-ok))))))) + (print! (class (if (= total-fail 0) 'success 'warn) + "%s %d/%d file(s) (%d ignored)") + (if recompile-p "Recompiled" "Byte-compiled") + total-ok total-files + total-noop) + (= total-fail 0)) + ((debug error) + (print! (error "There were breaking errors.\n\n%s") + "Reverting changes...") + (signal 'doom-error (list 'byte-compile e)))))))) (defun doom-clean-byte-compiled-files () "Delete all the compiled elc files in your Emacs configuration and private @@ -188,7 +179,8 @@ module. This does not include your byte-compiled, third party packages.'" (print! (start "Cleaning .elc files")) (print-group! (cl-loop with default-directory = doom-emacs-dir - with success = nil + with success = 0 + with esc = (if doom-debug-mode "" "\033[1A") for path in (append (doom-glob doom-emacs-dir "*.elc") (doom-files-in doom-private-dir :match "\\.elc$" :depth 1) @@ -196,10 +188,10 @@ module. This does not include your byte-compiled, third party packages.'" (doom-files-in doom-modules-dirs :match "\\.elc$" :depth 4)) if (file-exists-p path) do (delete-file path) - and do (print! (success "Deleted %s") (relpath path)) - and do (setq success t) + and do (print! (success "\033[KDeleted %s%s") (relpath path) esc) + and do (cl-incf success) finally do - (print! (if success - (success "All elc files deleted") - (info "No elc files to clean")))) + (print! (if (> success 0) + (success "\033[K%d elc files deleted" success) + (info "\033[KNo elc files to clean")))) t)) diff --git a/core/cli/debug.el b/core/cli/debug.el index d36f73dc6..1faa6956e 100644 --- a/core/cli/debug.el +++ b/core/cli/debug.el @@ -1,10 +1,12 @@ ;;; core/cli/debug.el -*- lexical-binding: t; -*- +(load! "autoload/debug" doom-core-dir) + ;; ;;; Commands (defcli! info - ((format ["--json" "--md" "--lisp"] "What format to dump info into")) + ((format ["--json" "--md" "--lisp"] "What format to dump info into")) "Output system info in markdown for bug reports." (pcase format ("--json" @@ -24,6 +26,5 @@ (defcli! (version v) () "Show version information for Doom & Emacs." - :bare t (doom/version) nil) diff --git a/core/cli/doctor.el b/core/cli/doctor.el index 22e690e2e..a394d2965 100644 --- a/core/cli/doctor.el +++ b/core/cli/doctor.el @@ -81,7 +81,6 @@ in." (print-group! (let ((doom-interactive-mode 'doctor)) (doom-initialize 'force) - (doom-initialize-core) (doom-initialize-modules)) (print! (success "Initialized Doom Emacs %s") doom-version) diff --git a/core/cli/env.el b/core/cli/env.el index d80414fed..5b2188385 100644 --- a/core/cli/env.el +++ b/core/cli/env.el @@ -1,8 +1,9 @@ ;;; core/cli/env.el -*- lexical-binding: t; -*- (defcli! env - ((clear-p ["-c" "--clear"] "Clear and delete your envvar file") - (outputfile ["-o" PATH] + ((allow ["-a" "--allow"] "An envvar whitelist regexp") + (clear-p ["-c" "--clear"] "Clear and delete your envvar file") + (outputfile ["-o" PATH] "Generate the envvar file at PATH. Envvar files that aren't in `doom-env-file' won't be loaded automatically at startup. You will need to load them manually from your private config with the `doom-load-envvars-file' @@ -58,35 +59,25 @@ Why this over exec-path-from-shell? (defvar doom-env-ignored-vars '("^DBUS_SESSION_BUS_ADDRESS$" - "^GPG_AGENT_INFO$" - "^\\(SSH\\|GPG\\)_TTY$" - "^HOME$" - "^PS1$" - "^PWD$" - "^R?PROMPT$" - "^SSH_AGENT_PID$" - "^SSH_AUTH_SOCK$" - "^TERM$" + "^GPG_AGENT_INFO$" "^\\(SSH\\|GPG\\)_TTY$" + "^SSH_\\(AUTH_SOCK\\|AGENT_PID\\)$" + "^HOME$" "^PWD$" "^PS1$" "^R?PROMPT$" "^TERM$" ;; Doom envvars - "^DEBUG$" - "^INSECURE$" - "^YES$" - "^__") + "^DEBUG$" "^INSECURE$" "^YES$" "^__") "Environment variables to not save in `doom-env-file'. Each string is a regexp, matched against variable names to omit from `doom-env-file'.") -(defun doom-cli-reload-env-file (&optional force-p env-file) +(defun doom-cli-reload-env-file (&optional force-p env-file whitelist) "Generates `doom-env-file', if it doesn't exist (or if FORCE-P). This scrapes the variables from your shell environment by running `doom-env-executable' through `shell-file-name' with `doom-env-switches'. By default, on Linux, this is '$SHELL -ic /usr/bin/env'. Variables in `doom-env-ignored-vars' are removed." - (let ((env-file (if env-file - (expand-file-name env-file) - doom-env-file))) + (let ((env-file (if env-file (expand-file-name env-file) doom-env-file)) + (process-environment doom--initial-process-environment)) (when (or force-p (not (file-exists-p env-file))) (with-temp-file env-file (setq-local coding-system-for-write 'utf-8) @@ -95,38 +86,38 @@ default, on Linux, this is '$SHELL -ic /usr/bin/env'. Variables in "Regenerating" "Generating") (path env-file)) - (let ((process-environment doom--initial-process-environment)) - (print! (info "Scraping shell environment")) - (print-group! - (when doom-interactive-mode - (user-error "'doom env' must be run on the command line, not an interactive session")) - (goto-char (point-min)) - (insert - (concat - "# -*- mode: sh -*-\n" - "# ---------------------------------------------------------------------------\n" - "# This file was auto-generated by `doom env'. It contains a list of environment\n" - "# variables scraped from your default shell (excluding variables blacklisted\n" - "# in doom-env-ignored-vars).\n" - "#\n" - (if (file-equal-p env-file doom-env-file) - (concat "# It is NOT safe to edit this file. Changes will be overwritten next time you\n" - "# run 'doom sync'. To create a safe-to-edit envvar file use:\n#\n" - "# doom env -o ~/.doom.d/myenv\n#\n" - "# And load it with (doom-load-envvars-file \"~/.doom.d/myenv\").\n") - (concat "# This file is safe to edit by hand, but remember to preserve the null bytes at\n" - "# the end of each line! needs to be loaded manually with:\n#\n" - "# (doom-load-envvars-file \"path/to/this/file\")\n#\n" - "# Use 'doom env -o path/to/this/file' to regenerate it.")) - "# ---------------------------------------------------------------------------\n\0\n")) - ;; We assume that this noninteractive session was spawned from the - ;; user's interactive shell, therefore we just dump - ;; `process-environment' to a file. - (dolist (env process-environment) - (if (cl-find-if (doom-rpartial #'string-match-p (car (split-string env "="))) - doom-env-ignored-vars) - (print! (info "Ignoring %s") env) - (insert env "\0\n"))) - (print! (success "Successfully generated %S") - (path env-file)) - t)))))) + (print-group! + (when doom-interactive-mode + (user-error "'doom env' must be run on the command line, not an interactive session")) + (goto-char (point-min)) + (insert + (concat + "# -*- mode: sh -*-\n" + "# ---------------------------------------------------------------------------\n" + "# This file was auto-generated by `doom env'. It contains a list of environment\n" + "# variables scraped from your default shell (excluding variables blacklisted\n" + "# in doom-env-ignored-vars).\n" + "#\n" + (if (file-equal-p env-file doom-env-file) + (concat "# It is NOT safe to edit this file. Changes will be overwritten next time you\n" + "# run 'doom sync'. To create a safe-to-edit envvar file use:\n#\n" + "# doom env -o ~/.doom.d/myenv\n#\n" + "# And load it with (doom-load-envvars-file \"~/.doom.d/myenv\").\n") + (concat "# This file is safe to edit by hand, but remember to preserve the null bytes at\n" + "# the end of each line! needs to be loaded manually with:\n#\n" + "# (doom-load-envvars-file \"path/to/this/file\")\n#\n" + "# Use 'doom env -o path/to/this/file' to regenerate it.")) + "# ---------------------------------------------------------------------------\n\0\n")) + ;; We assume that this noninteractive session was spawned from the + ;; user's interactive shell, therefore we just dump + ;; `process-environment' to a file. + (dolist (env process-environment) + (if (and (cl-find-if (doom-rpartial #'string-match-p (car (split-string env "="))) + doom-env-ignored-vars) + (not (cl-find-if (doom-rpartial #'string-match-p (car (split-string env "="))) + whitelist))) + (print! (info "Ignoring %s") env) + (insert env "\0\n"))) + (print! (success "Successfully generated %S") + (path env-file)) + t))))) diff --git a/core/cli/install.el b/core/cli/install.el index 719ca07b0..8b0d81f32 100644 --- a/core/cli/install.el +++ b/core/cli/install.el @@ -24,7 +24,6 @@ DOOMDIR environment variable. e.g. doom -p ~/.config/doom install DOOMDIR=~/.config/doom doom install" - :bare t (print! (green "Installing Doom Emacs!\n")) (let ((default-directory (doom-path "~"))) ;; Create `doom-private-dir' @@ -59,8 +58,7 @@ DOOMDIR environment variable. e.g. ;; In case no init.el was present the first time `doom-initialize-modules' was ;; called in core.el (e.g. on first install) - (doom-initialize 'force 'noerror) - (doom-initialize-modules) + (doom-initialize-modules 'force) ;; Ask if user would like an envvar file generated (if noenv-p @@ -79,28 +77,28 @@ DOOMDIR environment variable. e.g. (print! "Regenerating autoloads files") (doom-cli-reload-autoloads) - + (cond (nofonts-p) - (IS-WINDOWS - (print! (warn "Doom cannot install all-the-icons' fonts on Windows!\n")) - (print-group! - (print! - (concat "You'll have to do so manually:\n\n" - " 1. Launch Doom Emacs\n" - " 2. Execute 'M-x all-the-icons-install-fonts' to download the fonts\n" - " 3. Open the download location in windows explorer\n" - " 4. Open each font file to install them")))) - ((or doom-auto-accept - (y-or-n-p "Download and install all-the-icon's fonts?")) - (require 'all-the-icons) - (let ((window-system (cond (IS-MAC 'ns) - (IS-LINUX 'x)))) - (all-the-icons-install-fonts 'yes)))) + (IS-WINDOWS + (print! (warn "Doom cannot install all-the-icons' fonts on Windows!\n")) + (print-group! + (print! + (concat "You'll have to do so manually:\n\n" + " 1. Launch Doom Emacs\n" + " 2. Execute 'M-x all-the-icons-install-fonts' to download the fonts\n" + " 3. Open the download location in windows explorer\n" + " 4. Open each font file to install them")))) + ((or doom-auto-accept + (y-or-n-p "Download and install all-the-icon's fonts?")) + (require 'all-the-icons) + (let ((window-system (cond (IS-MAC 'ns) + (IS-LINUX 'x)))) + (all-the-icons-install-fonts 'yes)))) (when (file-exists-p "~/.emacs") (print! (warn "A ~/.emacs file was detected. This conflicts with Doom and should be deleted!"))) (print! (success "\nFinished! Doom is ready to go!\n")) (with-temp-buffer - (doom-template-insert "QUICKSTART_INTRO") - (print! (buffer-string))))) + (insert (doom-glob doom-core-dir "templates/QUICKSTART_INTRO")) + (print! "%s" (buffer-string))))) diff --git a/core/cli/packages.el b/core/cli/packages.el index 71b7b583e..77e1ca362 100644 --- a/core/cli/packages.el +++ b/core/cli/packages.el @@ -2,7 +2,7 @@ ;;; core/cli/packages.el (defcli! (update u) - ((discard-p ["--discard"] "All local changes to packages are discarded")) + ((discard-p ["--discard"] "All local changes to packages are discarded")) "Updates packages. This works by fetching all installed package repos and checking the distance @@ -74,23 +74,77 @@ list remains lean." "-n" "25" end-ref (concat "^" (regexp-quote start-ref))) (straight--process-get-output))) +(defun doom--barf-if-incomplete-packages () + (let ((straight-safe-mode t)) + (condition-case _ (straight-check-all) + (error (user-error "Package state is incomplete. Run 'doom sync' first"))))) + +(defmacro doom--with-package-recipes (recipes binds &rest body) + (declare (indent 2)) + (let ((recipe-var (make-symbol "recipe")) + (recipes-var (make-symbol "recipes"))) + `(let* ((,recipes-var ,recipes) + (built ()) + (straight-use-package-pre-build-functions + (cons (lambda (pkg &rest _) (cl-pushnew pkg built :test #'equal)) + straight-use-package-pre-build-functions))) + (dolist (,recipe-var ,recipes-var (nreverse built)) + (cl-block nil + (straight--with-plist (append (list :recipe ,recipe-var) ,recipe-var) + ,(doom-enlist binds) + ,@body)))))) + +(defvar doom--cli-updated-recipes nil) +(defun doom--cli-recipes-update () + "Updates straight and recipe repos." + (unless doom--cli-updated-recipes + (straight--make-build-cache-available) + (print! (start "Updating recipe repos...")) + (print-group! + (doom--with-package-recipes + (delq + nil (mapcar (doom-rpartial #'gethash straight--repo-cache) + (mapcar #'symbol-name straight-recipe-repositories))) + (recipe package type local-repo) + (let ((esc (unless doom-debug-mode "\033[1A")) + (ref (straight-vc-get-commit type local-repo)) + newref) + (print! (start "\033[KUpdating recipes for %s...%s") package esc) + (when (straight-vc-fetch-from-remote recipe) + (setq output (straight--process-get-output)) + (straight-merge-package package) + (or (equal ref (setq newref (straight-vc-get-commit type local-repo))) + (print! (success "\033[K%s updated (%s -> %s)") + package + (doom--abbrev-commit ref) + (doom--abbrev-commit newref))))))) + (setq straight--recipe-lookup-cache (make-hash-table :test #'eq) + doom--cli-updated-recipes t))) + + (defun doom-cli-packages-install () "Installs missing packages. This function will install any primary package (i.e. a package with a `package!' declaration) or dependency thereof that hasn't already been." - (straight--transaction-finalize) + (doom-initialize-packages) (print! (start "Installing packages...")) (let ((pinned (doom-package-pinned-list))) (print-group! (if-let (built - (doom-with-package-recipes (doom-package-recipe-list) + (doom--with-package-recipes (doom-package-recipe-list) (recipe package type local-repo) + (unless (file-directory-p (straight--repos-dir local-repo)) + (doom--cli-recipes-update)) (condition-case-unless-debug e - (straight-use-package (intern package)) + (let ((straight-use-package-pre-build-functions + (cons (lambda (pkg &rest _) + (when-let (commit (cdr (assoc pkg pinned))) + (print! (info "Checked out %s") commit))) + straight-use-package-pre-build-functions))) + (straight-use-package (intern package))) (error - (signal 'doom-package-error - (list package e (straight--process-get-output))))))) + (signal 'doom-package-error (list package e)))))) (print! (success "Installed %d packages") (length built)) (print! (info "No packages need to be installed")) @@ -99,7 +153,7 @@ declaration) or dependency thereof that hasn't already been." (defun doom-cli-packages-build (&optional force-p) "(Re)build all packages." - (straight--transaction-finalize) + (doom-initialize-packages) (print! (start "(Re)building %spackages...") (if force-p "all " "")) (print-group! (let ((straight-check-for-modifications @@ -118,7 +172,7 @@ declaration) or dependency thereof that hasn't already been." (unless force-p (straight--make-build-cache-available)) (if-let (built - (doom-with-package-recipes recipes (package local-repo) + (doom--with-package-recipes recipes (package local-repo) (unless force-p ;; Ensure packages with outdated files/bytecode are rebuilt (let ((build-dir (straight--build-dir package)) @@ -143,18 +197,21 @@ declaration) or dependency thereof that hasn't already been." (defun doom-cli-packages-update () "Updates packages." - (straight--transaction-finalize) - (print! (start "Updating packages (this may take a while)...")) + (doom-initialize-packages) + (doom--barf-if-incomplete-packages) (let* ((repo-dir (straight--repos-dir)) (pinned (doom-package-pinned-list)) + (recipes (doom-package-recipe-list)) (packages-to-rebuild (make-hash-table :test 'equal)) (repos-to-rebuild (make-hash-table :test 'equal)) - (recipes (doom-package-recipe-list)) (total (length recipes)) (esc (unless doom-debug-mode "\033[1A")) (i 0) errors) - (doom-with-package-recipes recipes (recipe package type local-repo) + (when recipes + (doom--cli-recipes-update)) + (print! (start "Updating packages (this may take a while)...")) + (doom--with-package-recipes recipes (recipe package type local-repo) (cl-incf i) (print-group! (unless (straight--repository-is-available-p recipe) @@ -173,7 +230,9 @@ declaration) or dependency thereof that hasn't already been." (error "%S is not a valid repository" package))) (condition-case-unless-debug e (let ((ref (straight-vc-get-commit type local-repo)) - (target-ref (cdr (assoc local-repo pinned))) + (target-ref + (cdr (or (assoc local-repo pinned) + (assoc package pinned)))) output) (or (cond ((not (stringp target-ref)) @@ -201,9 +260,9 @@ declaration) or dependency thereof that hasn't already been." (doom--same-commit-p target-ref (straight-vc-get-commit type local-repo))) ((print! (start "\033[K(%d/%d) Re-cloning %s...") i total local-repo esc) - (let ((repo (straight--repos-dir local-repo))) - (ignore-errors - (delete-directory repo 'recursive)) + (let ((repo (straight--repos-dir local-repo)) + (straight-vc-git-default-clone-depth 'full)) + (delete-directory repo 'recursive) (print-group! (straight-use-package (intern package) nil 'no-build)) (prog1 (file-directory-p repo) @@ -219,7 +278,7 @@ declaration) or dependency thereof that hasn't already been." (puthash package t packages-to-rebuild) (unless (string-empty-p output) (print! (start "\033[K(%d/%d) Updating %s...") i total local-repo) - (print-group! (print! (indent 2 output)))) + (print-group! (print! "%s" (indent 2 output)))) (print! (success "\033[K(%d/%d) %s updated (%s -> %s)") i total local-repo (doom--abbrev-commit ref) @@ -227,24 +286,19 @@ declaration) or dependency thereof that hasn't already been." (user-error (signal 'user-error (error-message-string e))) (error - (print! (warn "\033[K(%d/%d) Encountered error with %s" i total package)) - (print-group! - (print! (error "%s") e) - (print-group! (print! (info "%s" (straight--process-get-output))))) - (push package errors)))))) - (princ "\033[K") - (when errors - (print! (error "Encountered %d error(s), the offending packages: %s") - (length errors) (string-join errors ", "))) - (if (hash-table-empty-p packages-to-rebuild) - (ignore (print! (success "All %d packages are up-to-date") total)) - (let ((default-directory (straight--build-dir))) - (mapc (doom-rpartial #'delete-directory 'recursive) - (hash-table-keys packages-to-rebuild))) - (print! (success "Updated %d package(s)") - (hash-table-count packages-to-rebuild)) - (doom-cli-packages-build) - t))) + (signal 'doom-package-error (list package e))))))) + (print-group! + (princ "\033[K") + (if (hash-table-empty-p packages-to-rebuild) + (ignore (print! (success "All %d packages are up-to-date") total)) + (straight--transaction-finalize) + (let ((default-directory (straight--build-dir))) + (mapc (doom-rpartial #'delete-directory 'recursive) + (hash-table-keys packages-to-rebuild))) + (print! (success "Updated %d package(s)") + (hash-table-count packages-to-rebuild)) + (doom-cli-packages-build) + t)))) ;;; PURGE (for the emperor) @@ -258,8 +312,8 @@ declaration) or dependency thereof that hasn't already been." (defun doom--cli-packages-purge-builds (builds) (if (not builds) - (progn (print! (info "No builds to purge")) - 0) + (prog1 0 + (print! (info "No builds to purge"))) (print! (start "Purging straight builds..." (length builds))) (print-group! (length @@ -287,8 +341,8 @@ declaration) or dependency thereof that hasn't already been." (defun doom--cli-packages-regraft-repos (repos) (if (not repos) - (progn (print! (info "No repos to regraft")) - 0) + (prog1 0 + (print! (info "No repos to regraft"))) (print! (start "Regrafting %d repos..." (length repos))) (let ((before-size (doom-directory-size (straight--repos-dir)))) (print-group! @@ -302,8 +356,7 @@ declaration) or dependency thereof that hasn't already been." (defun doom--cli-packages-purge-repo (repo) (let ((repo-dir (straight--repos-dir repo))) (delete-directory repo-dir 'recursive) - (ignore-errors - (delete-file (straight--modified-file repo))) + (delete-file (straight--modified-file repo)) (if (file-directory-p repo-dir) (ignore (print! (error "Failed to purge repos/%s" repo))) (print! (success "Purged repos/%s" repo)) @@ -311,8 +364,8 @@ declaration) or dependency thereof that hasn't already been." (defun doom--cli-packages-purge-repos (repos) (if (not repos) - (progn (print! (info "No repos to purge")) - 0) + (prog1 0 + (print! (info "No repos to purge"))) (print! (start "Purging straight repositories...")) (print-group! (length @@ -322,8 +375,8 @@ declaration) or dependency thereof that hasn't already been." (require 'core-packages) (let ((dirs (doom-files-in package-user-dir :type t :depth 0))) (if (not dirs) - (progn (print! (info "No ELPA packages to purge")) - 0) + (prog1 0 + (print! (info "No ELPA packages to purge"))) (print! (start "Purging ELPA packages...")) (dolist (path dirs (length dirs)) (condition-case e @@ -346,16 +399,18 @@ a `package!' declaration) or isn't depended on by another primary package. If BUILDS-P, include straight package builds. If REPOS-P, include straight repos. If ELPA-P, include packages installed with package.el (M-x package-install)." + (doom-initialize-packages) + (doom--barf-if-incomplete-packages) (print! (start "Purging orphaned packages (for the emperor)...")) (cl-destructuring-bind (&optional builds-to-purge repos-to-purge repos-to-regraft) (let ((rdirs (straight--directory-files (straight--repos-dir) nil nil 'sort)) (bdirs (straight--directory-files (straight--build-dir) nil nil 'sort))) - (list (cl-remove-if (doom-rpartial #'gethash straight--profile-cache) - bdirs) - (cl-remove-if (doom-rpartial #'straight--checkhash straight--repo-cache) - rdirs) - (cl-remove-if-not (doom-rpartial #'straight--checkhash straight--repo-cache) - rdirs))) + (list (seq-remove (doom-rpartial #'gethash straight--profile-cache) + bdirs) + (seq-remove (doom-rpartial #'straight--checkhash straight--repo-cache) + rdirs) + (seq-filter (doom-rpartial #'straight--checkhash straight--repo-cache) + rdirs))) (let (success) (print-group! (if (not builds-p) diff --git a/core/cli/upgrade.el b/core/cli/upgrade.el index 5e327f525..14d3854ce 100644 --- a/core/cli/upgrade.el +++ b/core/cli/upgrade.el @@ -1,8 +1,8 @@ ;;; core/cli/upgrade.el -*- lexical-binding: t; -*- (defcli! (upgrade up) - ((force-p ["-f" "--force"] "Discard local changes to Doom and packages, and upgrade anyway") - (packages-only-p ["-p" "--packages"] "Only upgrade packages, not Doom")) + ((force-p ["-f" "--force"] "Discard local changes to Doom and packages, and upgrade anyway") + (packages-only-p ["-p" "--packages"] "Only upgrade packages, not Doom")) "Updates Doom and packages. This requires that ~/.emacs.d is a git repo, and is the equivalent of the @@ -19,7 +19,7 @@ following shell commands: nil (list (unless packages-only-p (doom-cli-upgrade doom-auto-accept doom-auto-discard)) - (doom-cli-execute "refresh") + (doom-cli-execute "sync") (when (doom-cli-packages-update) (doom-cli-reload-package-autoloads) t))) @@ -110,6 +110,23 @@ following shell commands: (equal (vc-git--rev-parse "HEAD") new-rev)) (print! (info "%s") (cdr result)) (error "Failed to check out %s" (substring new-rev 0 10))) + + ;; Reload Doom's CLI & libraries, in case there were any + ;; upstream changes. Major changes will still break, however + (condition-case e + (progn + (mapc (doom-rpartial #'unload-feature t) + '(core core-lib + core-cli + core-modules + core-packages)) + (require 'core) + (setq doom-init-p nil + doom-init-modules-p nil) + (doom-initialize)) + (error + (signal 'doom-error (list "Could not upgrade Doom without issues")))) + (print! (success "Finished upgrading Doom Emacs"))) t))))) (ignore-errors diff --git a/core/core-cli.el b/core/core-cli.el index 852f6fd43..2aa215247 100644 --- a/core/core-cli.el +++ b/core/core-cli.el @@ -1,5 +1,24 @@ ;;; -*- lexical-binding: t; no-byte-compile: t; -*- +(require 'seq) + +(load! "autoload/process") +(load! "autoload/plist") +(load! "autoload/files") +(load! "autoload/format") + +;; Create all our core directories to quell file errors +(mapc (doom-rpartial #'make-directory 'parents) + (list doom-local-dir + doom-etc-dir + doom-cache-dir)) + +;; Ensure straight and the bare minimum is ready to go +(require 'core-modules) +(require 'core-packages) +(doom-initialize-core-packages) + + ;; ;;; Variables @@ -11,7 +30,6 @@ commands like `doom-cli-packages-install', `doom-cli-packages-update' and (defvar doom-auto-discard (getenv "FORCE") "If non-nil, discard all local changes while updating.") -(defvar doom--cli-p nil) (defvar doom--cli-commands (make-hash-table :test 'equal)) (defvar doom--cli-groups (make-hash-table :test 'equal)) (defvar doom--cli-group nil) @@ -54,9 +72,10 @@ commands like `doom-cli-packages-install', `doom-cli-packages-update' and (defun doom--cli-process (cli args) (let* ((args (copy-sequence args)) (arglist (copy-sequence (doom-cli-arglist cli))) - (expected (or (cl-position-if (doom-rpartial #'memq cl--lambda-list-keywords) - arglist) - (length arglist))) + (expected + (or (cl-position-if (doom-rpartial #'memq cl--lambda-list-keywords) + arglist) + (length arglist))) (got 0) restvar rest @@ -137,7 +156,7 @@ Executes a cli defined with `defcli!' with the name or alias specified by COMMAND, and passes ARGS to it." (if-let (cli (doom-cli-get command)) (funcall (doom-cli-fn cli) - (doom--cli-process cli args)) + (doom--cli-process cli (remq nil args))) (user-error "Couldn't find any %S command" command))) (defmacro defcli! (name speclist &optional docstring &rest body) @@ -186,10 +205,6 @@ BODY will be run when this dispatcher is called." for optsym = (if (listp opt) (car opt) opt) unless (memq optsym cl--lambda-list-keywords) collect (list optsym `(cdr (assq ',optsym --alist--)))) - ,@(unless (plist-get plist :bare) - '((unless doom-init-p - (doom-initialize 'force 'noerror) - (doom-initialize-modules)))) ,@body))) doom--cli-commands) (when aliases @@ -208,10 +223,25 @@ BODY will be run when this dispatcher is called." ;;; Straight hacks (defvar doom--cli-straight-discard-options - '("^Delete remote \"[^\"]+\", re-create it with correct " - "^Reset branch " - "^Abort merge$" - "^Discard changes$")) + '((" has diverged from branch " + . "^Reset [^ ]+ to branch") + (" but recipe specifies a URL of " + . "^Delete remote \"[^\"]+\", re-create it with correct ") + (" has a merge conflict:" + . "^Abort merge$") + (" has a dirty worktree:" + . "^Discard changes$") + ("^In repository " + . "^Reset branch ")) + "A list of regexps, mapped to regexps. + +Their CAR is tested against the prompt, and CDR is tested against the presented +option, and is used by `straight-vc-git--popup-raw' to select which option to +recommend. + +It may not be obvious to users what they should do for some straight prompts, +so Doom will recommend the one that reverts a package back to its (or target) +original state.") ;; HACK Remove dired & magit options from prompt, since they're inaccessible in ;; noninteractive sessions. @@ -222,63 +252,69 @@ BODY will be run when this dispatcher is called." (defadvice! doom--straight-fallback-to-y-or-n-prompt-a (orig-fn &optional prompt) :around #'straight-are-you-sure (or doom-auto-accept - (if noninteractive - (y-or-n-p (format! "%s" (or prompt ""))) - (funcall orig-fn prompt)))) + (if doom-interactive-mode + (funcall orig-fn prompt) + (y-or-n-p (format! "%s" (or prompt "")))))) + +(defun doom--straight-recommended-option-p (prompt option) + (cl-loop for (prompt-re . opt-re) in doom--cli-straight-discard-options + if (string-match-p prompt-re prompt) + return (string-match-p opt-re option))) (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) + (if doom-interactive-mode (funcall orig-fn prompt actions) - ;; We can't intercept C-g, so no point displaying any options for this key - ;; when C-c is the proper way to abort batch Emacs. - (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) - (if doom-auto-discard - (cl-loop with doom-auto-accept = t - for (_key desc func) in actions - when desc - when (cl-find-if (doom-rpartial #'string-match-p desc) - doom--cli-straight-discard-options) - return (funcall func)) - (print! (start "%s") (red prompt)) - (print-group! - (terpri) - (let (options) - (print-group! - (print! " 1) Abort") - (cl-loop for (_key desc func) in actions + (let ((doom--cli-straight-discard-options doom--cli-straight-discard-options)) + ;; We can't intercept C-g, so no point displaying any options for this key + ;; when C-c is the proper way to abort batch Emacs. + (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) + (if doom-auto-discard + (cl-loop with doom-auto-accept = t + for (_key desc func) in actions when desc - do (push func options) - and do - (print! "%2s) %s" (1+ (length options)) - (if (cl-find-if (doom-rpartial #'string-match-p desc) - doom--cli-straight-discard-options) - (green (concat desc " (Recommended)")) - desc)))) + when (doom--straight-recommended-option-p prompt desc) + return (funcall func)) + (print! (start "%s") (red prompt)) + (print-group! (terpri) - (let* ((options - (cons (lambda () - (let ((doom-format-indent 0)) - (terpri) - (print! (warn "Aborted"))) - (kill-emacs 1)) - (nreverse options))) - (prompt - (format! "How to proceed? (%s) " - (mapconcat #'number-to-string - (number-sequence 1 (length options)) - ", "))) - answer fn) - (while (null (nth (setq answer (1- (read-number prompt))) - options)) - (print! (warn "%s is not a valid answer, try again.") - answer)) - (funcall (nth answer options)))))))) + (let (options) + (print-group! + (print! " 1) Abort") + (cl-loop for (_key desc func) in actions + when desc + do (push func options) + and do + (print! "%2s) %s" (1+ (length options)) + (if (doom--straight-recommended-option-p prompt desc) + (progn + (setq doom--cli-straight-discard-options nil) + (green (concat desc " (Recommended)"))) + desc)))) + (terpri) + (let* ((options + (cons (lambda () + (let ((doom-format-indent 0)) + (terpri) + (print! (warn "Aborted"))) + (kill-emacs 1)) + (nreverse options))) + (prompt + (format! "How to proceed? (%s) " + (mapconcat #'number-to-string + (number-sequence 1 (length options)) + ", "))) + answer fn) + (while (null (nth (setq answer (1- (read-number prompt))) + options)) + (print! (warn "%s is not a valid answer, try again.") + answer)) + (funcall (nth answer options))))))))) (defadvice! doom--straight-respect-print-indent-a (args) "Indent straight progress messages to respect `doom-format-indent', so we @@ -297,34 +333,18 @@ everywhere we use it (and internally)." interactive))) -;; -;;; Dependencies - -(require 'seq) - -;; Eagerly load these libraries because we may be in a session that hasn't been -;; fully initialized (e.g. where autoloads files haven't been generated or -;; `load-path' populated). -(load! "autoload/cli") -(load! "autoload/debug") -(load! "autoload/files") -(load! "autoload/format") -(load! "autoload/plist") - - ;; ;;; CLI Commands (load! "cli/help") (load! "cli/install") -(defcligroup! "Maintenance" - "For managing your config and packages" - (defcli! (sync s refresh re) - ((if-necessary-p ["-n" "--if-necessary"] "Only regenerate autoloads files if necessary") - (inhibit-envvar-p ["-e"] "Don't regenerate the envvar file") +(defcli! (sync s refresh re) + ((inhibit-envvar-p ["-e"] "Don't regenerate the envvar file") + (inhibit-elc-p ["-c"] "Don't recompile config") + (update-p ["-u"] "...") (prune-p ["-p" "--prune"] "Purge orphaned packages & regraft repos")) - "Synchronize your config with Doom Emacs. + "Synchronize your config with Doom Emacs. This is the equivalent of running autoremove, install, autoloads, then recompile. Run this whenever you: @@ -337,48 +357,44 @@ recompile. Run this whenever you: It will ensure that unneeded packages are removed, all needed packages are installed, autoloads files are up-to-date and no byte-compiled files have gone stale." - :bare t - (let (success) - ;; Ensures that no pre-existing state pollutes the generation of the new - ;; autoloads files. - (dolist (file (list doom-autoload-file doom-package-autoload-file)) - (delete-file file) - (delete-file (byte-compile-dest-file file))) + (print! (start "Synchronizing your config with Doom Emacs...")) + (print-group! + (and (not inhibit-envvar-p) + (file-exists-p doom-env-file) + (doom-cli-reload-env-file 'force)) + (doom-cli-reload-core-autoloads) + (doom-cli-packages-install) + (doom-cli-packages-build) + (when update-p + (doom-cli-packages-update)) + (doom-cli-packages-purge prune-p 'builds-p prune-p prune-p) + (doom-cli-reload-package-autoloads) + t)) - (doom-initialize 'force 'noerror) - (doom-initialize-modules) - - (print! (start "Synchronizing your config with Doom Emacs...")) - (print-group! - (when (and (not inhibit-envvar-p) - (file-exists-p doom-env-file)) - (doom-cli-reload-env-file 'force)) - - (doom-cli-reload-core-autoloads) - (doom-cli-packages-install) - (doom-cli-packages-build) - (doom-cli-packages-purge prune-p 'builds-p prune-p prune-p) - (doom-cli-reload-package-autoloads) - t))) - - (load! "cli/env") - (load! "cli/upgrade") - (load! "cli/packages") - (load! "cli/autoloads")) +(load! "cli/env") +(load! "cli/upgrade") +(load! "cli/packages") +(load! "cli/autoloads") (defcligroup! "Diagnostics" "For troubleshooting and diagnostics" (load! "cli/doctor") (load! "cli/debug") - (load! "cli/test")) + + ;; Our tests are broken at the moment. Working on fixing them, but for now we + ;; disable them: + ;; (load! "cli/test") + ) + (defcligroup! "Compilation" "For compiling Doom and your config" (load! "cli/byte-compile")) + (defcligroup! "Utilities" "Conveniences for interacting with Doom externally" - (defcli! run () + (defcli! run (&rest args) "Run Doom Emacs from bin/doom's parent directory. All arguments are passed on to Emacs. @@ -388,11 +404,16 @@ All arguments are passed on to Emacs. 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.") +best to run Doom out of ~/.emacs.d and ~/.doom.d.")) - ;; (load! "cli/batch") - ;; (load! "cli/org") - ) + +;; +;;; Load user config + +(condition-case-unless-debug e + (load! "init" doom-private-dir t) + (error + (signal 'doom-private-error (list "init.el" e)))) (provide 'core-cli) ;;; core-cli.el ends here diff --git a/core/core-editor.el b/core/core-editor.el index 6b76ffa5b..e05ac3204 100644 --- a/core/core-editor.el +++ b/core/core-editor.el @@ -79,8 +79,8 @@ possible." t)))))) ;; Don't autosave files or create lock/history/backup files. We don't want -;; copies of potentially sensitive material floating around, and we'll rely on -;; git and our own good fortune instead. Fingers crossed! +;; copies of potentially sensitive material floating around or polluting our +;; filesystem. We rely on git and our own good fortune instead. Fingers crossed! (setq auto-save-default nil create-lockfiles nil make-backup-files nil @@ -216,7 +216,7 @@ possible." (use-package! recentf ;; Keep track of recently opened files :defer-incrementally easymenu tree-widget timer - :after-call after-find-file + :hook (doom-first-file . recentf-mode) :commands recentf-open-files :config (defun doom--recent-file-truename (file) @@ -246,22 +246,20 @@ possible." "Add dired directory to recentf file list." (recentf-add-file default-directory))) - (when doom-interactive-mode - (add-hook 'kill-emacs-hook #'recentf-cleanup) - (quiet! (recentf-mode +1)))) + (add-hook 'kill-emacs-hook #'recentf-cleanup) + (advice-add #'recentf-load-list :around #'doom-shut-up-a)) (use-package! savehist ;; persist variables across sessions :defer-incrementally custom - :after-call post-command-hook + :hook (doom-first-input . savehist-mode) + :init + (setq savehist-file (concat doom-cache-dir "savehist")) :config - (setq savehist-file (concat doom-cache-dir "savehist") - savehist-save-minibuffer-history t + (setq savehist-save-minibuffer-history t savehist-autosave-interval nil ; save on kill only savehist-additional-variables '(kill-ring search-ring regexp-search-ring)) - (savehist-mode +1) - (add-hook! 'kill-emacs-hook (defun doom-unpropertize-kill-ring-h () "Remove text properties from `kill-ring' for a smaller savehist file." @@ -273,11 +271,11 @@ possible." (use-package! saveplace ;; persistent point location in buffers - :after-call after-find-file dired-initial-position-hook - :config + :hook (doom-first-file . save-place-mode) + :init (setq save-place-file (concat doom-cache-dir "saveplace") save-place-limit 100) - + :config (defadvice! doom--recenter-on-load-saveplace-a (&rest _) "Recenter on cursor when loading a saved place." :after-while #'save-place-find-file-hook @@ -293,9 +291,7 @@ possible." `pp' can be expensive for longer lists, and there's no reason to prettify cache files, so we replace calls to `pp' with the much faster `prin1'." :around #'save-place-alist-to-file - (letf! ((#'pp #'prin1)) (funcall orig-fn))) - - (save-place-mode +1)) + (letf! ((#'pp #'prin1)) (funcall orig-fn)))) (use-package! server @@ -314,7 +310,8 @@ files, so we replace calls to `pp' with the much faster `prin1'." ;;; Packages (use-package! better-jumper - :after-call pre-command-hook + :hook (doom-first-input . better-jumper-mode) + :hook (better-jumper-post-jump . recenter) :preface ;; REVIEW Suppress byte-compiler warning spawning a *Compile-Log* buffer at ;; startup. This can be removed once gilbertw1/better-jumper#2 is merged. @@ -324,17 +321,16 @@ files, so we replace calls to `pp' with the much faster `prin1'." (global-set-key [remap evil-jump-backward] #'better-jumper-jump-backward) (global-set-key [remap xref-pop-marker-stack] #'better-jumper-jump-backward) :config - (better-jumper-mode +1) - (add-hook 'better-jumper-post-jump-hook #'recenter) + (advice-add #'dired-find-file :before #'doom-set-jump-maybe-a) - (defadvice! doom-set-jump-a (orig-fn &rest args) + (defun doom-set-jump-a (orig-fn &rest args) "Set a jump point and ensure ORIG-FN doesn't set any new jump points." (better-jumper-set-jump (if (markerp (car args)) (car args))) (let ((evil--jumps-jumping t) (better-jumper--jumping t)) (apply orig-fn args))) - (defadvice! doom-set-jump-maybe-a (orig-fn &rest args) + (defun doom-set-jump-maybe-a (orig-fn &rest args) "Set a jump point if ORIG-FN returns non-nil." (let ((origin (point-marker)) (result @@ -441,7 +437,7 @@ files, so we replace calls to `pp' with the much faster `prin1'." (use-package! smartparens ;; Auto-close delimiters and blocks as you type. It's more powerful than that, ;; but that is all Doom uses it for. - :after-call doom-switch-buffer-hook after-find-file + :hook (doom-first-buffer . smartparens-global-mode) :commands sp-pair sp-local-pair sp-with-modes sp-point-in-comment sp-point-in-string :config ;; smartparens recognizes `slime-mrepl-mode', but not `sly-mrepl-mode', so... @@ -501,16 +497,12 @@ files, so we replace calls to `pp' with the much faster `prin1'." (defun doom-disable-smartparens-mode-maybe-h () (when smartparens-mode (setq-local doom-buffer-smartparens-mode t) - (turn-off-smartparens-mode)))) - - (smartparens-global-mode +1)) + (turn-off-smartparens-mode))))) (use-package! so-long - :after-call after-find-file + :hook (doom-first-file . global-so-long-mode) :config - (when doom-interactive-mode - (global-so-long-mode +1)) (setq so-long-threshold 400) ; reduce false positives w/ larger threshold ;; Don't disable syntax highlighting and line numbers, or make the buffer ;; read-only, in `so-long-minor-mode', so we can have a basic editing @@ -530,6 +522,7 @@ files, so we replace calls to `pp' with the much faster `prin1'." (appendq! so-long-minor-modes '(flycheck-mode flyspell-mode + spell-fu-mode eldoc-mode smartparens-mode highlight-numbers-mode @@ -560,8 +553,7 @@ files, so we replace calls to `pp' with the much faster `prin1'." (use-package! ws-butler ;; a less intrusive `delete-trailing-whitespaces' on save - :after-call after-find-file - :config (ws-butler-global-mode +1)) + :hook (doom-first-buffer . ws-butler-global-mode)) (provide 'core-editor) ;;; core-editor.el ends here diff --git a/core/core-keybinds.el b/core/core-keybinds.el index be5a93ce5..abe650a8f 100644 --- a/core/core-keybinds.el +++ b/core/core-keybinds.el @@ -179,8 +179,7 @@ localleader prefix." ;;; Packages (use-package! which-key - :defer 1 - :after-call pre-command-hook + :hook (doom-first-input . which-key-mode) :init (setq which-key-sort-order #'which-key-prefix-then-key-order which-key-sort-uppercase-first nil @@ -195,9 +194,7 @@ localleader prefix." (setq-hook! 'which-key-init-buffer-hook line-spacing 3) (which-key-add-key-based-replacements doom-leader-key "") - (which-key-add-key-based-replacements doom-localleader-key "") - - (which-key-mode +1)) + (which-key-add-key-based-replacements doom-localleader-key "")) ;; diff --git a/core/core-lib.el b/core/core-lib.el index 5f5d311e2..1e3dc6244 100644 --- a/core/core-lib.el +++ b/core/core-lib.el @@ -467,11 +467,19 @@ advised)." (put ',fn 'permanent-local-hook t) (add-hook sym #',fn ,append)))))) +(defmacro add-hook-trigger! (hook-var &rest targets) + "TODO" + `(let ((fn (intern (format "%s-h" ,hook-var)))) + (fset fn (lambda (&rest _) (run-hooks ,hook-var) (set ,hook-var nil))) + (put ,hook-var 'permanent-local t) + (dolist (on (list ,@targets)) + (if (functionp on) + (advice-add on :before fn) + (add-hook on fn))))) + (defmacro add-hook! (hooks &rest rest) "A convenience macro for adding N functions to M hooks. -If N and M = 1, there's no benefit to using this macro over `add-hook'. - This macro accepts, in order: 1. The mode(s) or hook(s) to add to. This is either an unquoted mode, an diff --git a/core/core-modules.el b/core/core-modules.el index b50fdd33c..db7104c25 100644 --- a/core/core-modules.el +++ b/core/core-modules.el @@ -71,6 +71,13 @@ before the user's private module.") ;; ;;; Bootstrap API +(defun doom-initialize-core-modules () + "Load Doom's core files for an interactive session." + (require 'core-keybinds) + (require 'core-ui) + (require 'core-projects) + (require 'core-editor)) + (defun doom-initialize-modules (&optional force-p) "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 @@ -78,16 +85,18 @@ non-nil." (when (or force-p (not doom-init-modules-p)) (setq doom-init-modules-p t doom-modules nil) - (load custom-file 'noerror 'nomessage) - (when (load! "init" doom-private-dir t) - (when doom-modules - (maphash (lambda (key plist) - (let ((doom--current-module key) - (doom--current-flags (plist-get plist :flags))) - (load! "init" (plist-get plist :path) t))) - doom-modules)) - (run-hook-wrapped 'doom-before-init-modules-hook #'doom-try-run-hook) - (when doom-interactive-mode + (with-temp-buffer + (doom-log "Initializing core modules") + (doom-initialize-core-modules) + (when-let (init-p (load! "init" doom-private-dir t)) + (doom-log "Initializing user config") + (when doom-modules + (maphash (lambda (key plist) + (let ((doom--current-module key) + (doom--current-flags (plist-get plist :flags))) + (load! "init" (plist-get plist :path) t))) + doom-modules)) + (run-hook-wrapped 'doom-before-init-modules-hook #'doom-try-run-hook) (when doom-modules (maphash (lambda (key plist) (let ((doom--current-module key) @@ -95,7 +104,8 @@ non-nil." (load! "config" (plist-get plist :path) t))) doom-modules)) (run-hook-wrapped 'doom-init-modules-hook #'doom-try-run-hook) - (load! "config" doom-private-dir t))))) + (load! "config" doom-private-dir t) + (load custom-file 'noerror (not doom-debug-mode)))))) ;; @@ -122,15 +132,17 @@ non-nil." of PROPERTY and VALUEs. \(fn CATEGORY MODULE PROPERTY VALUE &rest [PROPERTY VALUE [...]])" - (if-let (old-plist (doom-module-get category module)) - (progn - (when plist - (when (cl-oddp (length plist)) - (signal 'wrong-number-of-arguments (list (length plist)))) - (while plist - (plist-put old-plist (pop plist) (pop plist)))) - (puthash (cons category module) old-plist doom-modules)) - (puthash (cons category module) plist doom-modules))) + (puthash (cons category module) + (if-let (old-plist (doom-module-get category module)) + (if (null plist) + old-plist + (when (cl-oddp (length plist)) + (signal 'wrong-number-of-arguments (list (length plist)))) + (while plist + (plist-put old-plist (pop plist) (pop plist))) + old-plist) + plist) + doom-modules)) (defun doom-module-set (category module &rest plist) "Enables a module by adding it to `doom-modules'. @@ -223,18 +235,21 @@ those directories. The first returned path is always `doom-private-dir'." collect (plist-get plist :path))) nil)) -(defun doom-modules (&optional refresh-p) +(defun doom-module-list (&optional all-p) "Minimally initialize `doom-modules' (a hash table) and return it. This value is cached. If REFRESH-P, then don't use the cached value." - (or (unless refresh-p doom-modules) - (let (doom-interactive-mode - doom-modules - doom-init-modules-p) - (load! "init" doom-private-dir t) - (or doom-modules - (make-hash-table :test 'equal - :size 20 - :rehash-threshold 1.0))))) + (if all-p + (cl-loop for path in (cdr (doom-module-load-path 'all)) + collect (doom-module-from-path path)) + (or doom-modules + (let (doom-interactive-mode + doom-modules + doom-init-modules-p) + (load! "init" doom-private-dir t) + (or doom-modules + (make-hash-table :test 'equal + :size 20 + :rehash-threshold 1.0)))))) ;; @@ -375,8 +390,8 @@ for a list of all recognized module trees. Order defines precedence (from most to least)." `(let ((modules ,@(if (keywordp (car modules)) - (list (list 'quote modules)) - modules))) + (list (list 'quote modules)) + modules))) (unless doom-modules (setq doom-modules (make-hash-table :test 'equal diff --git a/core/core-packages.el b/core/core-packages.el index 1ad951a37..bbcbfd1de 100644 --- a/core/core-packages.el +++ b/core/core-packages.el @@ -30,43 +30,20 @@ ;; `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) - "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!'.") ;; -;;; 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. @@ -94,7 +71,7 @@ missing) and shouldn't be deleted.") ;;; Straight (setq straight-base-dir doom-local-dir - straight-repository-branch "master" + 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 sync' instant (once everything set up), which is much nicer @@ -123,81 +100,305 @@ missing) and shouldn't be deleted.") ;; -;;; Bootstrapper +;;; Bootstrappers + +(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") + (cl-destructuring-bind (&key pin recipe &allow-other-keys) + (let ((doom-packages (doom-package-list nil 'core-only))) + (doom-package-get 'straight)) + (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-mode #'doom-exec-process #'doom-call-process))) + (if (not (file-directory-p repo-dir)) + (message "Installing straight...") + ;; TODO Rethink this clumsy workaround + (let ((default-directory repo-dir)) + (unless (equal (cdr (doom-call-process "git" "rev-parse" "HEAD")) pin) + (delete-directory repo-dir 'recursive) + (message "Updating straight...")))) + (unless (file-directory-p repo-dir) + (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-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) - (doom-ensure-straight) - (mapc #'straight-use-package doom-core-packages) - (doom-log "Initializing doom-packages") - (setq doom-disabled-packages nil - doom-pinned-packages nil - doom-packages (doom-package-list)) + (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) - (let ((name (car package))) - (with-plist! (cdr package) (recipe modules disable ignore pin) - (if ignore - (doom-log "Ignoring package %S" name) - (if (not disable) - (with-demoted-errors "Package error: %s" - (when recipe - (straight-override-recipe (cons name recipe))) - (straight-register-package name)) - (doom-log "Disabling package %S" name) + (cl-destructuring-bind + (name &key recipe disable ignore &allow-other-keys) package + (unless ignore + (if disable (cl-pushnew name doom-disabled-packages) - ;; Warn about disabled core packages - (when (cl-find :core modules :key #'car) - (print! (warn "%s\n%s") - (format "You've disabled %S" name) - (indent 2 (concat "This is a core package. Disabling it will cause errors, as Doom assumes\n" - "core packages are always available. Disable their minor-modes or hooks instead."))))))))))) + (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." - (unless (fboundp 'straight--reset-caches) - (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) - (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)) - (require 'straight)) - (straight--reset-caches) - (setq straight-recipe-repositories nil - straight-recipe-overrides 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))) + +;; +;;; 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) + (goto-char (match-beginning 0)) + (let ((ppss (save-excursion (syntax-ppss)))) + ;; Don't collect packages in comments or strings + (or (nth 3 ppss) + (nth 4 ppss) + (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 ((doom-interactive-mode t) + doom-packages) + (doom--read-packages + (doom-path doom-core-dir "packages.el") all-p 'noerror) + (unless core-only-p + (let ((private-packages (doom-path doom-private-dir "packages.el")) + (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.el") + for doom--current-module = key + do (doom--read-packages path nil 'noerror))) + (doom--read-packages private-packages all-p 'noerror))) + (reverse 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 no-build &allow-other-keys) + recipe + (unless (or (null local-repo) + (eq type 'built-in) + no-build) + (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))))))) ;; @@ -254,6 +455,8 @@ elsewhere." (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) @@ -282,8 +485,9 @@ elsewhere." (error-message-string e))))) ;; This is the only side-effect of this macro! (setf (alist-get name doom-packages) plist) - (with-no-warnings - (not (plist-get plist :disable))))) + (unless (plist-get plist :disable) + (with-no-warnings + (cons name plist))))) (defmacro disable-packages! (&rest packages) "A convenience macro for disabling packages in bulk. @@ -311,24 +515,26 @@ should use it!" (if (memq t targets) `(mapc (doom-rpartial #'doom-package-set :unpin t) (mapcar #'car doom-packages)) - (let (forms) - (dolist (target targets) - (cl-check-type target (or symbol keyword list)) - (cond - ((symbolp target) - (push `(doom-package-set ',target :unpin t) forms)) - ((or (keywordp target) - (listp target)) - (cl-destructuring-bind (category . modules) (doom-enlist target) - (dolist (pkg doom-packages) - (let ((pkg-modules (plist-get (cdr pkg) :modules))) - (and (assq category pkg-modules) - (or (null modules) - (cl-loop for module in modules - if (member (cons category module) pkg-modules) - return t)) - (push `(doom-package-set ',(car pkg) :unpin t) forms)))))))) - (macroexp-progn forms)))) + (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 diff --git a/core/core-projects.el b/core/core-projects.el index 14ba77ac4..dff3aec43 100644 --- a/core/core-projects.el +++ b/core/core-projects.el @@ -20,7 +20,6 @@ Emacs.") ;;; Packages (use-package! projectile - :after-call after-find-file dired-before-readin-hook minibuffer-setup-hook :commands (projectile-project-root projectile-project-name projectile-project-p diff --git a/core/core-ui.el b/core/core-ui.el index 9b590b668..ea788cd97 100644 --- a/core/core-ui.el +++ b/core/core-ui.el @@ -443,9 +443,10 @@ windows, switch to `doom-fallback-buffer'. Otherwise, delegate to original (use-package! winner ;; undo/redo changes to Emacs' window layout - :after-call after-find-file doom-switch-window-hook + :hook (window-configuration-change . winner-mode) :preface (defvar winner-dont-bind-my-keys t) ; I'll bind keys myself - :config (winner-mode +1) + :config + (remove-hook 'window-configuration-change #'winner-mode) (appendq! winner-boring-buffers '("*Compile-Log*" "*inferior-lisp*" "*Fuzzy Completions*" "*Apropos*" "*Help*" "*cvs*" "*Buffer List*" "*Ibuffer*" @@ -454,13 +455,12 @@ windows, switch to `doom-fallback-buffer'. Otherwise, delegate to original (use-package! paren ;; highlight matching delimiters - :after-call after-find-file doom-switch-buffer-hook + :hook (doom-first-buffer . show-paren-mode) :config (setq show-paren-delay 0.1 show-paren-highlight-openparen t show-paren-when-point-inside-paren t - show-paren-when-point-in-periphery t) - (show-paren-mode +1)) + show-paren-when-point-in-periphery t)) ;;;###package whitespace diff --git a/core/core.el b/core/core.el index f4d5aa9c6..aea4ac635 100644 --- a/core/core.el +++ b/core/core.el @@ -1,8 +1,9 @@ ;;; core.el --- the heart of the beast -*- lexical-binding: t; -*- -(when (version< emacs-version "26.1") - (error "Detected Emacs %s. Doom only supports Emacs 26.1 and higher" - emacs-version)) +(eval-when-compile + (when (< emacs-major-version 26) + (error "Detected Emacs v%s. Doom only supports Emacs 26 and newer" + emacs-version))) (defconst doom-version "2.0.9" "Current version of Doom Emacs.") @@ -195,7 +196,7 @@ users).") ;; Emacs "updates" its ui more often than it needs to, so we slow it down ;; slightly from 0.5s: -(setq idle-update-delay 1) +(setq idle-update-delay 1.0) ;; Emacs is essentially one huge security vulnerability, what with all the ;; dependencies it pulls in from all corners of the globe. Let's try to be at @@ -306,15 +307,13 @@ users).") ;; Adopt a sneaky garbage collection strategy of waiting until idle time to ;; collect; staving off the collector while the user is working. -(when doom-interactive-mode - (setq gcmh-idle-delay 5 - gcmh-high-cons-threshold 16777216 ; 16mb - gcmh-verbose doom-debug-mode - gc-cons-percentage 0.6) - (add-transient-hook! 'pre-command-hook (gcmh-mode +1)) - (with-eval-after-load 'gcmh - (setq gc-cons-percentage 0.1) - (add-hook 'focus-out-hook #'gcmh-idle-garbage-collect))) +(setq gcmh-idle-delay 5 + gcmh-high-cons-threshold 16777216 ; 16mb + gcmh-verbose doom-debug-mode + gc-cons-percentage 0.6) +(with-eval-after-load 'gcmh + ;; But restore this later, otherwise we risk freezing and stuttering! + (setq gc-cons-percentage 0.1)) ;; HACK `tty-run-terminal-initialization' is *tremendously* slow for some ;; reason. Disabling it completely could have many side-effects, so we @@ -337,18 +336,14 @@ users).") "Run MODE-local-vars-hook after local variables are initialized." (run-hook-wrapped (intern-soft (format "%s-local-vars-hook" major-mode)) #'doom-try-run-hook)) -(add-hook 'hack-local-variables-hook #'doom-run-local-var-hooks-h) ;; If the user has disabled `enable-local-variables', then ;; `hack-local-variables-hook' is never triggered, so we trigger it at the end ;; of `after-change-major-mode-hook': -(defun doom-run-local-var-hooks-if-necessary-h () +(defun doom-run-local-var-hooks-maybe-h () "Run `doom-run-local-var-hooks-h' if `enable-local-variables' is disabled." (unless enable-local-variables (doom-run-local-var-hooks-h))) -(add-hook 'after-change-major-mode-hook - #'doom-run-local-var-hooks-if-necessary-h - 'append) ;; @@ -427,7 +422,18 @@ If this is a daemon session, load them all immediately instead." nil #'doom-load-packages-incrementally (cdr doom-incremental-packages) t)))) -(add-hook 'emacs-startup-hook #'doom-load-packages-incrementally-h) + +;; +;;; Custom hooks + +(defvar doom-first-input-hook nil + "Transient hooks run before the first user input.") + +(defvar doom-first-file-hook nil + "Transient hooks run before the first interactively opened file.") + +(defvar doom-first-buffer-hook nil + "Transient hooks run before the first interactively opened buffer.") ;; @@ -445,7 +451,7 @@ If RETURN-P, return the message as a string instead of displaying it." (setq doom-init-time (float-time (time-subtract (current-time) before-init-time)))))) -(defun doom-initialize (&optional force-p noerror) +(defun doom-initialize (&optional force-p) "Bootstrap Doom, if it hasn't already (or if FORCE-P is non-nil). The bootstrap process ensures that the essential directories exist, all core @@ -473,6 +479,7 @@ for a list of all recognized module trees. Order defines precedence (from most to least)." (when (or force-p (not doom-init-p)) (setq doom-init-p t) + (doom-log "Initializing Doom") ;; Reset as much state as possible, so `doom-initialize' can be treated like ;; a reset function. e.g. when reloading the config. @@ -480,70 +487,51 @@ to least)." load-path doom--initial-load-path process-environment doom--initial-process-environment) - ;; Load shell environment, optionally generated from 'doom env'. No need to - ;; do so if we're in terminal Emacs, because Emacs will correctly inherit + (or (and + ;; `doom-autoload-file' tells Emacs where to load all its functions + ;; from. This includes everything in core/autoload/*.el and + ;; autoload files in enabled modules. + (or (doom-load-autoloads-file doom-autoload-file) + (ignore (warn "Doom's core autoloads file is missing"))) + ;; Loads `doom-package-autoload-file', which loads a concatenated + ;; package autoloads file which caches `load-path', + ;; `auto-mode-alist', `Info-directory-list', and + ;; `doom-disabled-packages'. A big reduction in startup time. + (or (doom-load-autoloads-file doom-package-autoload-file) + (ignore (warn "Doom's package autoloads file is missing")))) + (signal 'doom-error + (list "Doom is in an incomplete state" + "run 'bin/doom sync' on the command line to repair it"))) + + ;; Load shell environment, optionally generated from 'doom env'. No need + ;; to do so if we're in terminal Emacs, where Emacs correctly inherits ;; your shell environment there. - (when (and (or (display-graphic-p) - (daemonp)) - (file-exists-p doom-env-file)) - (doom-load-envvars-file doom-env-file)) + (and (or (display-graphic-p) + (daemonp)) + (doom-load-envvars-file doom-env-file 'noerror)) (require 'core-modules) - (let (;; `doom-autoload-file' tells Emacs where to load all its functions - ;; from. This includes everything in core/autoload/*.el and autoload - ;; files in enabled modules. - (core-autoloads-p (doom-load-autoloads-file doom-autoload-file noerror)) - ;; Loads `doom-package-autoload-file', which loads a concatenated - ;; package autoloads file which caches `load-path', `auto-mode-alist', - ;; `Info-directory-list', and `doom-disabled-packages'. A big - ;; reduction in startup time. - (pkg-autoloads-p (doom-load-autoloads-file doom-package-autoload-file noerror))) + (autoload 'doom-initialize-packages "core-packages") + (autoload 'doom-initialize-core-packages "core-packages") - (if (and core-autoloads-p pkg-autoloads-p (not force-p)) - ;; In case we want to use package.el or straight via M-x - (progn - (with-eval-after-load 'package - (require 'core-packages)) - (with-eval-after-load 'straight - (require 'core-packages) - (doom-initialize-packages))) + ;; In case we want to use package.el or straight via M-x + (with-eval-after-load 'package (require 'core-packages)) + (with-eval-after-load 'straight (doom-initialize-packages)) - ;; Eagerly load these libraries because we may be in a session that hasn't been - ;; fully initialized (e.g. where autoloads files haven't been generated or - ;; `load-path' populated). - (mapc (doom-rpartial #'load nil (not doom-debug-mode) 'nosuffix) - (file-expand-wildcards (concat doom-core-dir "autoload/*.el"))) + ;; Bootstrap interactive session + (add-hook! 'window-setup-hook + (add-hook 'hack-local-variables-hook #'doom-run-local-var-hooks-h) + (add-hook 'after-change-major-mode-hook #'doom-run-local-var-hooks-maybe-h 'append) + (add-hook 'doom-first-input-hook #'gcmh-mode) + (add-hook-trigger! 'doom-first-input-hook 'pre-command-hook) + (add-hook-trigger! 'doom-first-file-hook 'after-find-hook 'dired-initial-position-hook) + (add-hook-trigger! 'doom-first-buffer-hook 'after-find-hook 'doom-switch-buffer-hook)) + (add-hook 'emacs-startup-hook #'doom-load-packages-incrementally-h) + (add-hook 'window-setup-hook #'doom-display-benchmark-h 'append) - ;; Create all our core directories to quell file errors - (mapc (doom-rpartial #'make-directory 'parents) - (list doom-local-dir - doom-etc-dir - doom-cache-dir)) + (doom-initialize-modules force-p)) - ;; Ensure the package management system (and straight) are ready for - ;; action (and all core packages/repos are installed) - (require 'core-packages) - (doom-initialize-packages force-p)) - - (unless (or (and core-autoloads-p pkg-autoloads-p) - noerror) - (unless core-autoloads-p - (warn "Your Doom core autoloads file is missing")) - (unless pkg-autoloads-p - (warn "Your package autoloads file is missing")) - (signal 'doom-autoload-error (list "Run `bin/doom refresh' to generate them"))) - - (when doom-interactive-mode - (add-hook 'window-setup-hook #'doom-display-benchmark-h 'append) - (add-to-list 'command-switch-alist (cons "--restore" #'doom-restore-session-handler)))) - t)) - -(defun doom-initialize-core () - "Load Doom's core files for an interactive session." - (require 'core-keybinds) - (require 'core-ui) - (require 'core-projects) - (require 'core-editor)) + doom-init-p) (provide 'core) ;;; core.el ends here diff --git a/core/packages.el b/core/packages.el index 986d1459a..fba82b326 100644 --- a/core/packages.el +++ b/core/packages.el @@ -5,6 +5,22 @@ (package! auto-minor-mode :pin "17cfa1b54800fdef2975c0c0531dad34846a5065") (package! gcmh :pin "b1bde5089169a74f62033d027e06e98cbeedd43f") +;; core-packages.el +(package! straight + ;; :type 'core + :recipe `(:host github + :repo "raxod502/straight.el" + :branch ,straight-repository-branch + :local-repo "straight.el" + :files ("straight*.el") + :no-build t) + :pin "f606bfaf9330cfb1ef6971bb1f6ac4e2c39a39c2") + +;; core-modules.el +(package! use-package + ;; :type 'core + :pin "c873d5529c9c80cb58222f22873a4f081c307cb2") + ;; core-ui.el (package! all-the-icons :pin "d6cb6d4a779eaa3570d8e451fd4d38b2b4554860") (package! hide-mode-line :pin "88888825b5b27b300683e662fa3be88d954b1cea") diff --git a/init.el b/init.el index 13ed45647..78a37fc54 100644 --- a/init.el +++ b/init.el @@ -47,7 +47,3 @@ ;; And let 'er rip! (doom-initialize) -(if noninteractive - (doom-initialize-packages) - (doom-initialize-core) - (doom-initialize-modules)) diff --git a/modules/checkers/syntax/config.el b/modules/checkers/syntax/config.el index f6c52c0a1..9d9ab1049 100644 --- a/modules/checkers/syntax/config.el +++ b/modules/checkers/syntax/config.el @@ -5,7 +5,7 @@ (use-package! flycheck :commands flycheck-list-errors flycheck-buffer - :after-call doom-switch-buffer-hook after-find-file + :hook (doom-first-buffer . global-flycheck-mode) :config (setq flycheck-emacs-lisp-load-path 'inherit) @@ -39,9 +39,7 @@ :n "j" #'flycheck-error-list-next-error :n "k" #'flycheck-error-list-previous-error :n "RET" #'flycheck-error-list-goto-error - :n [return] #'flycheck-error-list-goto-error) - - (global-flycheck-mode +1)) + :n [return] #'flycheck-error-list-goto-error)) (use-package! flycheck-popup-tip diff --git a/modules/completion/helm/config.el b/modules/completion/helm/config.el index 66e8d7b9a..24d3175c9 100644 --- a/modules/completion/helm/config.el +++ b/modules/completion/helm/config.el @@ -23,8 +23,7 @@ be negative.") ;;; Packages (use-package! helm-mode - :defer t - :after-call pre-command-hook + :hook (doom-first-input . helm-mode) :init (map! [remap apropos] #'helm-apropos [remap find-library] #'helm-locate-library @@ -43,7 +42,6 @@ be negative.") [remap recentf-open-files] #'helm-recentf [remap yank-pop] #'helm-show-kill-ring) :config - (helm-mode +1) ;; helm is too heavy for `find-file-at-point' (add-to-list 'helm-completing-read-handlers-alist (cons #'find-file-at-point nil))) diff --git a/modules/completion/ido/config.el b/modules/completion/ido/config.el index 22de041ae..5f0cb880b 100644 --- a/modules/completion/ido/config.el +++ b/modules/completion/ido/config.el @@ -8,12 +8,12 @@ ;;; Packages (use-package! ido - :after-call pre-command-hook + :hook (doom-first-input . ido-mode) :hook (ido-mode . ido-everywhere) :hook (ido-mode . ido-ubiquitous-mode) :preface ;; HACK `ido' is a really old package. It defines `ido-mode' manually and - ;; doesn't define a hook, so we define one for it.") + ;; doesn't define a hook, so we define one for it, so we can use it! (defadvice! +ido-run-hooks-a (&rest _) :after #'ido-mode (run-hooks 'ido-mode-hook)) @@ -42,9 +42,7 @@ ;; Go to $HOME with ~ "~" (λ! (if (looking-back "/" (point-min)) (insert "~/") - (call-interactively #'self-insert-command)))) - - (ido-mode +1)) + (call-interactively #'self-insert-command))))) (use-package! ido-vertical-mode diff --git a/modules/completion/ivy/config.el b/modules/completion/ivy/config.el index f9dcab6c6..874181aa3 100644 --- a/modules/completion/ivy/config.el +++ b/modules/completion/ivy/config.el @@ -19,7 +19,7 @@ results buffer.") ;;; Packages (use-package! ivy - :after-call pre-command-hook + :hook (doom-first-input . ivy-mode) :init (let ((standard-search-fn (if (featurep! +prescient) @@ -98,12 +98,10 @@ evil-ex-specific constructs, so we disable it solely in evil-ex." (apply orig-fn args))) (define-key! ivy-minibuffer-map - "C-c C-e" #'+ivy/woccur [remap doom/delete-backward-word] #'ivy-backward-kill-word + "C-c C-e" #'+ivy/woccur "C-o" #'ivy-dispatching-done - "M-o" #'hydra-ivy/body) - - (ivy-mode +1)) + "M-o" #'hydra-ivy/body)) (use-package! ivy-rich diff --git a/modules/lang/org/config.el b/modules/lang/org/config.el index 7d34f4001..ef7b512bc 100644 --- a/modules/lang/org/config.el +++ b/modules/lang/org/config.el @@ -432,7 +432,19 @@ relative to `org-directory', unless it is an absolute path." (setq org-pandoc-options '((standalone . t) (mathjax . t) - (variable . "revealjs-url=https://revealjs.com"))))) + (variable . "revealjs-url=https://revealjs.com")))) + + (defadvice! +org--fix-async-export-a (orig-fn &rest args) + :around #'org-export-to-file + (if (not org-export-in-background) + (apply orig-fn args) + (setq org-export-async-init-file (make-temp-file "doom-org-async-export")) + (with-temp-file org-export-async-init-file + (prin1 `(progn (setq org-export-async-debug ,debug-on-error + load-path ',load-path) + (load ,user-init-file nil t)) + (current-buffer))) + (apply orig-fn args)))) (defun +org-init-habit-h ()