From bec79a3d4cb47335dd5dcb768951fd60d840b30d Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Mon, 14 May 2018 18:40:35 +0200 Subject: [PATCH] Major refactor of Doom bootstrap process + New `input` and `buffer` support for :defer in def-package! can now defer packages until the first command invoked after startup or first interactive buffer switch, respectively + Exploit these new :defer techniques to lazy-load many core packages, netting Doom a 20-30% decrease in startup time + Various userland macros (like package!, def-package-hook!, packages!, and disable-packages!) will now throw an error if used incorrectly (i.e. outside of their intended files; e.g. package! should be used in packages.el files) + Removed support for multiple/nested doom! calls. There should only be THE ONE in ~/.doom.d/init.el (or ~/.config/doom/init.el) + Fix an issue where load-path and auto-mode-list modifications would not persist because doom-packages-file was cached too late. + Added package-activated-list to cached variables in doom-packages-file, thus we no longer need custom-file. + Load Doom core files from doom-initialize. Now doom-initialize can be called from state-dependent non-interactive functions, instead of reloading core/core.el, which was clumsy + Removed the doom-post-init-hook hook. There was no reason for it to exist when doom-init-hook can simply be appended to --- core/core-editor.el | 67 ++++++---- core/core-keybinds.el | 1 + core/core-packages.el | 294 ++++++++++++++++++++++++------------------ core/core-projects.el | 2 +- core/core-ui.el | 28 ++-- core/core.el | 105 +++++---------- 6 files changed, 268 insertions(+), 229 deletions(-) diff --git a/core/core-editor.el b/core/core-editor.el index e7c04bd1c..cb12e8514 100644 --- a/core/core-editor.el +++ b/core/core-editor.el @@ -59,37 +59,48 @@ fundamental-mode) for performance sake." (fundamental-mode)))) (add-hook 'find-file-hook #'doom|check-large-file) -(push '("/LICENSE$" . text-mode) auto-mode-alist) - ;; ;; Built-in plugins ;; +(electric-indent-mode -1) ; enabled by default in Emacs 25+. No thanks. + (add-hook 'after-save-hook #'executable-make-buffer-file-executable-if-script-p) ;; revert buffers for changed files -(global-auto-revert-mode 1) -(setq auto-revert-verbose nil) +(def-package! autorevert + :defer buffer + :config + (setq auto-revert-verbose nil) + (global-auto-revert-mode +1)) -;; enabled by default in Emacs 25+. No thanks. -(electric-indent-mode -1) +;; persist variables across sessions +(def-package! savehist + :defer (input . 1) + :config + (setq savehist-file (concat doom-cache-dir "savehist") + 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)) -;; savehist / saveplace -(setq savehist-file (concat doom-cache-dir "savehist") - savehist-save-minibuffer-history t - savehist-autosave-interval nil ; save on kill only - savehist-additional-variables '(kill-ring search-ring regexp-search-ring) - save-place-file (concat doom-cache-dir "saveplace")) -(add-hook! 'doom-init-hook #'(savehist-mode save-place-mode)) -(defun doom*recenter-on-load-saveplace (&rest _) - "Recenter on cursor when loading a saved place." - (if buffer-file-name (ignore-errors (recenter)))) -(advice-add #'save-place-find-file-hook :after-while #'doom*recenter-on-load-saveplace) +;; persistent point location in buffers +(def-package! saveplace + :defer buffer + :config + (setq save-place-file (concat doom-cache-dir "saveplace")) + (defun doom*recenter-on-load-saveplace (&rest _) + "Recenter on cursor when loading a saved place." + (if buffer-file-name (ignore-errors (recenter)))) + (advice-add #'save-place-find-file-hook + :after-while #'doom*recenter-on-load-saveplace) + (save-place-mode +1)) ;; Keep track of recently opened files (def-package! recentf - :hook (doom-init . recentf-mode) + :defer (input . 1) + :commands recentf-open-files :config (setq recentf-save-file (concat doom-cache-dir "recentf") recentf-auto-cleanup 60 @@ -101,7 +112,15 @@ fundamental-mode) for performance sake." "^/tmp/" "^/ssh:" "\\.?ido\\.last$" "\\.revive$" "/TAGS$" "^/var/folders/.+$" ;; ignore private DOOM temp files (but not all of them) - (concat "^" (file-truename doom-local-dir))))) + (concat "^" (file-truename doom-local-dir)))) + (recentf-mode +1)) + +(def-package! server + :when (display-graphic-p) + :defer 2 + :config + (unless (server-running-p) + (server-start))) ;; @@ -110,10 +129,9 @@ fundamental-mode) for performance sake." ;; Auto-close delimiters and blocks as you type (def-package! smartparens + :defer (buffer . 2) :config - (smartparens-global-mode +1) (require 'smartparens-config) - (setq sp-highlight-pair-overlay nil sp-cancel-autoskip-on-backward-movement nil sp-show-pair-delay 0 @@ -124,12 +142,15 @@ fundamental-mode) for performance sake." (add-hook 'evil-replace-state-exit-hook #'turn-on-smartparens-mode) (sp-local-pair '(xml-mode nxml-mode php-mode) "" - :post-handlers '(("| " "SPC")))) + :post-handlers '(("| " "SPC"))) + + (smartparens-global-mode +1)) ;; Branching undo (def-package! undo-tree - :hook (doom-init . global-undo-tree-mode) + :defer input :config + (global-undo-tree-mode +1) ;; persistent undo history is known to cause undo history corruption, which ;; can be very destructive! So disable it! (setq undo-tree-auto-save-history nil diff --git a/core/core-keybinds.el b/core/core-keybinds.el index b13063b76..0fca8ca65 100644 --- a/core/core-keybinds.el +++ b/core/core-keybinds.el @@ -60,6 +60,7 @@ If any hook returns non-nil, all hooks after it are ignored.") (def-package! hydra + :commands (defhydra defhydradio) :init ;; In case I later need to wrap defhydra in any special functionality. (defalias 'def-hydra! 'defhydra) diff --git a/core/core-packages.el b/core/core-packages.el index 54a8f8547..562d6d25d 100644 --- a/core/core-packages.el +++ b/core/core-packages.el @@ -73,6 +73,17 @@ missing) and shouldn't be deleted.") (defvar doom-disabled-packages () "A list of packages that should be ignored by `def-package!'.") +(defvar doom-deferred-packages + '((input) + (buffer)) + "A alist of packages that have been deferred. The CAR is the type of deferral +for the package, the CDR is the list of packages. + + input will be loaded on the first action the user invokes + after startup. + buffer will be loaded on the first new buffer to be opened + interactively.") + (defvar doom-reload-hook nil "A list of hooks to run when `doom/reload-load-path' is called.") @@ -87,10 +98,8 @@ missing) and shouldn't be deleted.") and `auto-mode-alist'.") (defvar doom--current-module nil) -(defvar doom--init-cache-p nil) -(defvar doom--initializing nil) (defvar doom--refreshed-p nil) -(defvar generated-autoload-load-name) +(defvar doom--stage nil) ;; (setq autoload-compute-prefixes nil @@ -134,37 +143,57 @@ and `auto-mode-alist'.") ;; -;; Startup benchmark +;; Helpers 'n hooks ;; -(defun doom-packages--benchmark () - (format "Doom loaded %s packages across %d modules in %.03fs" - ;; Certainly imprecise, especially where custom additions to - ;; load-path are concerned, but I don't mind a [small] margin of - ;; error in the plugin count in exchange for faster startup. - (- (length load-path) (length doom-site-load-path)) - (hash-table-count doom-modules) - (or doom-init-time - (setq doom-init-time (float-time (time-subtract (current-time) before-init-time)))))) +(defun doom--assert-stage-p (stage macro) + (cl-assert (eq stage doom--stage) + nil + "Found %s call in non-%s.el file (%s)" + macro (symbol-name stage) + (if (file-in-directory-p load-file-name doom-emacs-dir) + (file-relative-name load-file-name doom-emacs-dir) + (abbreviate-file-name load-file-name)))) + +(defun doom|refresh-cache () + "Refresh `doom-packages-file', which caches `load-path', +`Info-directory-list', `doom-disabled-packages', `auto-mode-alist' and +`package-activated-list'." + (doom-initialize-packages 'internal) + (let ((coding-system-for-write 'emacs-internal)) + (with-temp-file doom-packages-file + (insert ";;; -*- lexical-binding:t -*-\n" + ";; This file was autogenerated by `doom|refresh-cache', DO NOT EDIT!\n") + (prin1 `(setq load-path ',load-path + Info-directory-list ',Info-directory-list + auto-mode-alist ',auto-mode-alist + doom-disabled-packages ',doom-disabled-packages + package-activated-list ',package-activated-list) + (current-buffer))))) + +(defun doom|display-benchmark (&optional return-p) + "Display a benchmark, showing number of packages and modules, and how quickly +they were loaded at startup. + +If RETURN-P, return the message as a string instead of displaying it." + (funcall (if return-p #'format #'message) + "Doom loaded %s packages across %d modules in %.03fs" + ;; Certainly imprecise, especially where custom additions to + ;; load-path are concerned, but I don't mind a [small] margin of + ;; error in the plugin count in exchange for faster startup. + (- (length load-path) (length doom-site-load-path)) + (hash-table-count doom-modules) + (or doom-init-time + (setq doom-init-time (float-time (time-subtract (current-time) before-init-time)))))) + +(add-hook 'emacs-startup-hook #'doom|display-benchmark) +(add-hook 'doom-reload-hook #'doom|display-benchmark) ;; ;; Bootstrap API ;; -(defun doom--refresh-cache () - "TODO" - (when doom--init-cache-p - (doom-initialize-packages 'internal) - (unless noninteractive - (with-temp-buffer - (prin1 `(setq load-path ',load-path - Info-directory-list ',Info-directory-list - doom-disabled-packages ',doom-disabled-packages) - (current-buffer)) - (write-file doom-packages-file)) - (setq doom--init-cache-p nil)))) - (defun doom-initialize (&optional force-p) "Bootstrap the bare essentials to get Doom running, if it hasn't already. If FORCE-P is non-nil, do it anyway. @@ -180,12 +209,14 @@ FORCE-P is non-nil, do it anyway. (require 'cl-lib) (require 'map)) (when (or force-p (not doom-init-p)) - (unless (load doom-autoload-file t t t) + ;; autoloads file + (unless (load doom-autoload-file 'noerror 'nomessage 'nosuffix) (unless noninteractive (error "No autoloads file! Run make autoloads"))) - (when (and noninteractive (file-exists-p doom-packages-file)) + ;; packages.el cache + (when (and force-p (file-exists-p doom-packages-file)) (delete-file doom-packages-file)) - (when (or force-p (not (load doom-packages-file t t t))) + (unless (load doom-packages-file 'noerror 'nomessage 'nosuffix) ;; Ensure core folders exist, otherwise we get errors (dolist (dir (list doom-local-dir doom-etc-dir doom-cache-dir doom-packages-dir)) (unless (file-directory-p dir) @@ -195,7 +226,8 @@ FORCE-P is non-nil, do it anyway. (setq package-activated-list nil package--initialized nil) (let (byte-compile-warnings) - (condition-case _ (package-initialize) + (condition-case _ + (package-initialize) ('error (package-refresh-contents) (setq doom--refreshed-p t) (package-initialize)))) @@ -213,8 +245,23 @@ FORCE-P is non-nil, do it anyway. (error "✕ Couldn't install %s" package))) (message "Installing core packages...done"))) (cl-pushnew doom-core-dir load-path :test #'string=) - (setq doom--init-cache-p t)) - (setq doom-init-p t))) + (add-hook 'doom-internal-init-hook #'doom|refresh-cache)) + (when doom-debug-mode + (message "Doom initialized"))) + ;; initialize Doom core + (require 'core-lib) + (require 'core-os) + (unless noninteractive + (require 'core-ui) + (require 'core-editor) + (require 'core-projects) + (require 'core-keybinds)) + ;; load input-deferred packages on first `pre-command-hook' + (add-transient-hook! 'pre-command-hook + (mapc #'require (cdr (assq 'input doom-deferred-packages)))) + (add-transient-hook! 'doom-after-switch-buffer-hook + (when (get-buffer-window) + (mapc #'require (cdr (assq 'buffer doom-deferred-packages)))))) (defun doom-initialize-autoloads () "Ensures that `doom-autoload-file' exists and is loaded. Otherwise run @@ -222,14 +269,6 @@ FORCE-P is non-nil, do it anyway. (unless (file-exists-p doom-autoload-file) (quiet! (doom//reload-autoloads)))) -(defun doom-initialize-modules () - "Bootstraps all enabled modules by loading their config.el files." - (maphash (lambda (key plist) - (let ((doom--current-module key)) - (load (expand-file-name "config" (plist-get plist :path)) - 'noerror (not doom-debug-mode)))) - doom-modules)) - (defun doom-initialize-packages (&optional force-p) "Ensures that `doom-packages', `packages-alist' and `quelpa-cache' are populated. @@ -242,51 +281,57 @@ Use this before any of package.el, quelpa or Doom's package management's API to ensure all the necessary package metadata is initialized and available for them." (with-temp-buffer ; prevent buffer-local settings from propagating - (cl-flet - ((_load - (file &optional noerror interactive) - (condition-case-unless-debug ex - (let ((load-prefer-newer t) - (noninteractive (not interactive))) - (load file noerror 'nomessage 'nosuffix)) - ('error - (lwarn 'doom-initialize-packages :warning - "%s in %s: %s" - (car ex) - (file-relative-name file doom-emacs-dir) - (error-message-string ex)))))) + (let ((load-prefer-newer t)) ;; package.el and quelpa handle themselves if their state changes during ;; the current session, but if you change an packages.el file in a module, ;; there's no non-trivial way to detect that, so we give you a way to - ;; reload only doom-packages. + ;; reload only doom-packages (by passing 'internal as FORCE-P). ;; `doom-packages' (when (or force-p (not doom-packages)) + (unless (eq force-p 'internal) + ;; `package-alist' + (when (or force-p (not (bound-and-true-p package-alist))) + (setq load-path doom-site-load-path) + (require 'package) + (setq package-activated-list nil + package--initialized nil) + (let (byte-compile-warnings) + (condition-case _ + (package-initialize) + ('error (package-refresh-contents) + (setq doom--refreshed-p t) + (package-initialize))))) + + ;; `quelpa-cache' + (when (or force-p (not (bound-and-true-p quelpa-cache))) + (require 'quelpa) + (setq quelpa-initialized-p nil) + (or (quelpa-setup-p) + (error "Could not initialize quelpa")))) + (setq doom-packages nil) - (_load (expand-file-name "packages.el" doom-core-dir)) - (cl-loop for key being the hash-keys of doom-modules - for path = (doom-module-expand-file (car key) (cdr key) "packages.el") - if (file-exists-p path) - do (let ((doom--current-module key)) (_load path))) - (cl-loop for dir in doom-psuedo-module-dirs - for path = (expand-file-name "packages.el" dir) - if (file-exists-p path) - do (_load path))) - - (unless (eq force-p 'internal) - ;; `package-alist' - (when (or force-p (not (bound-and-true-p package-alist))) - (setq load-path doom-site-load-path) - (require 'package) - (setq package-activated-list nil) - (let (byte-compile-warnings) - (package-initialize))) - - ;; `quelpa-cache' - (when (or force-p (not (bound-and-true-p quelpa-cache))) - (require 'quelpa) - (setq quelpa-initialized-p nil) - (or (quelpa-setup-p) - (error "Could not initialize quelpa"))))))) + (cl-flet + ((_load + (file &optional noerror interactive) + (condition-case-unless-debug ex + (let ((noninteractive (not interactive))) + (load file noerror 'nomessage 'nosuffix)) + ('error + (lwarn 'doom-initialize-packages :warning + "%s in %s: %s" + (car ex) + (file-relative-name file doom-emacs-dir) + (error-message-string ex)))))) + (let ((doom--stage 'packages)) + (_load (expand-file-name "packages.el" doom-core-dir)) + (cl-loop for key being the hash-keys of doom-modules + for path = (doom-module-expand-file (car key) (cdr key) "packages.el") + if (file-exists-p path) + do (let ((doom--current-module key)) (_load path))) + (cl-loop for dir in doom-psuedo-module-dirs + for path = (expand-file-name "packages.el" dir) + if (file-exists-p path) + do (_load path)))))))) ;; @@ -387,28 +432,34 @@ added, if the file exists." "Bootstraps DOOM Emacs and its modules. MODULES is an malformed plist of modules to load." - (let (load-forms module file-name-handler-alist) - (dolist (m modules) - (cond ((keywordp m) (setq module m)) - ((not module) (error "No namespace specified in `doom!' for %s" m)) - ((let ((submodule (if (listp m) (car m) m)) - (flags (if (listp m) (cdr m)))) - (let ((path (doom-module-find-path module submodule))) - (if (not path) - (when doom-debug-mode - (message "Couldn't find the %s %s module" module submodule)) - (doom-module-set module submodule :flags flags :path path) - (push `(let ((doom--current-module ',(cons module submodule))) - (load! init ,path t)) - load-forms))))))) + (let (init-forms config-forms file-name-handler-alist) + (let (module) + (dolist (m modules) + (cond ((keywordp m) (setq module m)) + ((not module) (error "No namespace specified in `doom!' for %s" m)) + ((let ((submodule (if (listp m) (car m) m)) + (flags (if (listp m) (cdr m)))) + (let ((path (doom-module-find-path module submodule))) + (if (not path) + (when doom-debug-mode + (message "Couldn't find the %s %s module" module submodule)) + (doom-module-set module submodule :flags flags :path path) + (push `(let ((doom--current-module ',(cons module submodule))) + (load! init ,path t)) + init-forms) + (push `(let ((doom--current-module ',(cons module submodule))) + (load! config ,path t)) + config-forms)))))))) `(let (file-name-handler-alist) (setq doom-modules ',doom-modules) - (let ((doom--initializing t)) - ,@(nreverse load-forms)) - ,(unless doom--initializing - '(unless noninteractive - (doom--refresh-cache) - (doom-initialize-modules)))))) + (let ((doom--stage 'init)) + ,@(nreverse init-forms)) + (unless noninteractive + (run-hooks 'doom-internal-init-hook) + (let ((doom--stage 'config)) + ,@(nreverse config-forms) + (when doom-private-dir + (load ,(concat doom-private-dir "config") t t))))))) (defmacro def-package! (name &rest plist) "A thin wrapper around `use-package'." @@ -419,10 +470,17 @@ MODULES is an malformed plist of modules to load." ;; If byte-compiling, ignore this package if it doesn't meet the condition. ;; This avoids false-positive load errors. (unless (and (bound-and-true-p byte-compile-current-file) - (or (and (plist-member plist :if) (not (eval (plist-get plist :if)))) - (and (plist-member plist :when) (not (eval (plist-get plist :when)))) - (and (plist-member plist :unless) (eval (plist-get plist :unless))))) - `(use-package ,name ,@plist))) + (or (and (plist-member plist :if) (not (eval (plist-get plist :if) t))) + (and (plist-member plist :when) (not (eval (plist-get plist :when) t))) + (and (plist-member plist :unless) (eval (plist-get plist :unless) t)))) + `(progn + ,(when-let* ((defer (plist-get plist :defer)) + (type (or (car-safe defer) defer))) + (setq plist (plist-put plist :defer (or (cdr-safe defer) t))) + (when (and (not doom-init-p) + (assq type doom-deferred-packages)) + `(push ',name (cdr (assq ',type doom-deferred-packages))))) + (use-package ,name ,@plist)))) (defmacro def-package-hook! (package when &rest body) "Reconfigures a package's `def-package!' block. @@ -439,6 +497,7 @@ WARNING: If :pre-init or :pre-config hooks return nil, the original `def-package!''s :init/:config block (respectively) is overwritten, so remember to have them return non-nil (or exploit that to overwrite Doom's config)." (declare (indent defun)) + (doom--assert-stage-p 'init #'package!) (cond ((eq when :disable) (message "Using :disable with `def-package-hook!' is deprecated. Use :disable in `package!' instead.") (ignore (push package doom-disabled-packages))) @@ -533,16 +592,6 @@ omitted. eg. (featurep! +flag1)" ;; Module package macros ;; -(defun doom--assert-file-p (file-name macro) - (cl-assert (string= (file-name-nondirectory load-file-name) file-name) - nil - "Found %s call in non-%s file (%s)" - macro - file-name - (if (file-in-directory-p load-file-name doom-emacs-dir) - (file-relative-name load-file-name doom-emacs-dir) - (abbreviate-file-name load-file-name)))) - (defmacro package! (name &rest plist) "Declares a package and how to install it (if applicable). @@ -568,7 +617,7 @@ Accepts the following properties: :freeze FORM Do not update this package if FORM is non-nil." (declare (indent defun)) - (doom--assert-file-p "packages.el" #'package!) + (doom--assert-stage-p 'packages #'package!) (cond ((memq name doom-disabled-packages) nil) ((let ((disable (plist-get plist :disable))) (and disable (eval disable))) @@ -599,7 +648,7 @@ Accepts the following properties: packages at once. Only use this macro in a module's packages.el file." - (doom--assert-file-p "packages.el" #'packages!) + (doom--assert-stage-p 'packages #'packages!) `(progn ,@(cl-loop for desc in packages collect `(package! ,@desc)))) (defmacro disable-packages! (&rest packages) @@ -607,7 +656,7 @@ Only use this macro in a module's packages.el file." packages at once. Only use this macro in a module's packages.el file." - (doom--assert-file-p "packages.el" #'disable-packages!) + (doom--assert-stage-p 'packages #'disable-packages!) `(setq doom-disabled-packages (append ',packages doom-disabled-packages))) (defmacro depends-on! (module submodule &optional flags) @@ -617,7 +666,7 @@ Only use this macro in a module's packages.el file. MODULE is a keyword, and SUBMODULE is a symbol. Under the hood, this simply loads MODULE SUBMODULE's packages.el file." - (doom--assert-file-p "packages.el" #'depends-on!) + (doom--assert-stage-p 'packages #'depends-on!) `(let ((doom-modules ,doom-modules) (flags ,flags)) (when flags @@ -680,6 +729,7 @@ call `doom//reload-load-path' remotely (through emacsclient)." (message "%d packages reloaded" (length package-alist)) (run-hooks 'doom-reload-hook)))) +(defvar generated-autoload-load-name) (defun doom//reload-autoloads () "Refreshes the autoloads.el file, specified by `doom-autoload-file'. @@ -805,12 +855,11 @@ If RECOMPILE-P is non-nil, only recompile out-of-date files." ;; pertinent to files compiled later. (let (noninteractive) ;; Core libraries aren't fully loaded in a noninteractive session, so - ;; we reload it with `noninteractive' set to nil to force them to. - (load (expand-file-name "core.el" doom-core-dir) nil t t) + ;; we pretend to be interactive and reinitialize + (doom-initialize) ;; In case autoloads.el hasn't been properly generated at this point. - (dolist (file (file-expand-wildcards (expand-file-name "autoload/*.el" doom-core-dir))) - (load file t t t))) - (doom-initialize-modules) + (unless (file-exists-p doom-autoload-file) + (mapc #'load (file-expand-wildcards (expand-file-name "autoload/*.el" doom-core-dir))))) ;; Assemble el files we want to compile; taking into account that ;; MODULES may be a list of MODULE/SUBMODULE strings from the command ;; line. @@ -819,6 +868,7 @@ If RECOMPILE-P is non-nil, only recompile out-of-date files." in (or modules (append (list doom-core-dir) (doom-module-load-path))) if (equal target "core") nconc (nreverse (doom-packages--files doom-core-dir "\\.el$")) + and collect (expand-file-name "init.el" doom-private-dir) else if (file-directory-p target) nconc (nreverse (doom-packages--files target "\\.el$")) else if (cl-member target doom-psuedo-module-dirs :test #'file-in-directory-p) diff --git a/core/core-projects.el b/core/core-projects.el index f81118fc6..497b6c5b6 100644 --- a/core/core-projects.el +++ b/core/core-projects.el @@ -12,9 +12,9 @@ projectile-ignored-projects '("~/" "/tmp")) :config - (projectile-mode +1) (add-hook 'dired-before-readin-hook #'projectile-track-known-projects-find-file-hook) (add-hook 'find-file-hook #'doom|autoload-project-mode) + (projectile-mode +1) ;; a more generic project root file (push ".project" projectile-project-root-files-bottom-up) diff --git a/core/core-ui.el b/core/core-ui.el index 9b267e203..118a50a23 100644 --- a/core/core-ui.el +++ b/core/core-ui.el @@ -280,15 +280,19 @@ DEFAULT is non-nil, set the default mode-line for all buffers." (add-hook 'isearch-mode-end-hook #'doom|enable-ui-keystrokes) ;; undo/redo changes to Emacs' window layout -(defvar winner-dont-bind-my-keys t) ; I'll bind keys myself -(autoload 'winner-mode "winner" nil t) -(add-hook 'doom-init-ui-hook #'winner-mode) +(def-package! winner + :defer buffer + :preface (defvar winner-dont-bind-my-keys t) ; I'll bind keys myself + :config (winner-mode +1)) ;; highlight matching delimiters -(setq show-paren-delay 0.1 - show-paren-highlight-openparen t - show-paren-when-point-inside-paren t) -(add-hook 'doom-init-ui-hook #'show-paren-mode) +(def-package! paren + :defer input + :config + (setq show-paren-delay 0.1 + show-paren-highlight-openparen t + show-paren-when-point-inside-paren t) + (show-paren-mode +1)) ;;; More reliable inter-window border ;; The native border "consumes" a pixel of the fringe on righter-most splits, @@ -352,10 +356,12 @@ from the default." (prog1 (apply orig-fn args) (run-hooks 'doom-after-switch-buffer-hook))) -(advice-add #'select-frame :around #'doom*switch-frame-hooks) -(advice-add #'select-window :around #'doom*switch-window-hooks) -(advice-add #'switch-to-buffer :around #'doom*switch-buffer-hooks) -(advice-add #'display-buffer :around #'doom*switch-buffer-hooks) +(defun doom|init-custom-hooks () + (advice-add #'select-frame :around #'doom*switch-frame-hooks) + (advice-add #'select-window :around #'doom*switch-window-hooks) + (advice-add #'switch-to-buffer :around #'doom*switch-buffer-hooks) + (advice-add #'display-buffer :around #'doom*switch-buffer-hooks)) +(add-hook 'doom-init-hook #'doom|init-custom-hooks) (defun doom*load-theme-hooks (theme &rest _) (setq doom-theme theme) diff --git a/core/core.el b/core/core.el index 0b7dc3c40..afd720562 100644 --- a/core/core.el +++ b/core/core.el @@ -82,6 +82,7 @@ XDG directory conventions if ~/.config/doom exists.") abbrev-file-name (concat doom-local-dir "abbrev.el") auto-save-list-file-name (concat doom-cache-dir "autosave") backup-directory-alist (list (cons "." (concat doom-cache-dir "backup/"))) + custom-file (concat doom-etc-dir "custom.el") pcache-directory (concat doom-cache-dir "pcache/") mc/list-file (concat doom-etc-dir "mc-lists.el") server-auth-dir (concat doom-cache-dir "server/") @@ -92,10 +93,6 @@ XDG directory conventions if ~/.config/doom exists.") url-cache-directory (concat doom-cache-dir "url/") url-configuration-directory (concat doom-etc-dir "url/")) -;; move custom defs out of init.el -(setq custom-file (concat doom-etc-dir "custom.el")) -(load custom-file t t t) - ;; be quiet at startup; don't load or display anything unnecessary (unless noninteractive (advice-add #'display-startup-echo-area-message :override #'ignore) @@ -108,56 +105,17 @@ XDG directory conventions if ~/.config/doom exists.") ;; Custom init hooks; clearer than `after-init-hook', `emacs-startup-hook', and ;; `window-setup-hook'. (defvar doom-init-hook nil - "A list of hooks run when DOOM is initialized, before `doom-post-init-hook'. -Use this for essential functionality.") + "A list of hooks run when DOOM is initialized.") -(defvar doom-post-init-hook nil - "A list of hooks run after DOOM initialization is complete, and after -`doom-init-hook'. Use this for extra, non-essential functionality.") - -(defun doom-try-run-hook (fn hook) - "Runs a hook wrapped in a `condition-case-unless-debug' block; its objective -is to include more information in the error message, without sacrificing your -ability to invoke the debugger in debug mode." - (condition-case-unless-debug ex - (if noninteractive - (quiet! (funcall fn)) - (funcall fn)) - ('error - (lwarn hook :error - "%s in '%s' -> %s" - (car ex) fn (error-message-string ex)))) - nil) - -(defun doom|after-init () - "Run `doom-init-hook' and `doom-post-init-hook', start the Emacs server, and -display the loading benchmark." - (unless noninteractive - (load (concat doom-private-dir "config") t t)) - (dolist (hook '(doom-init-hook doom-post-init-hook)) - (run-hook-wrapped hook #'doom-try-run-hook hook)) - (unless noninteractive - (when (display-graphic-p) - (require 'server) - (unless (server-running-p) - (server-start))) - (message "%s" (doom-packages--benchmark)))) - -(defun doom|finalize () - "Resets garbage collection settings to reasonable defaults (if you don't do -this, you'll get stuttering and random freezes), and resets -`file-name-handler-alist'." - (setq gc-cons-threshold 16777216 - gc-cons-percentage 0.1 - file-name-handler-alist doom--file-name-handler-alist) - t) +(defvar doom-internal-init-hook nil + "Hooks run after Doom has loaded all init.el files, and is ready to load +modules.") ;; ;; Emacs fixes/hacks ;; -;; Automatic minor modes (defvar doom-auto-minor-mode-alist '() "Alist mapping filename patterns to corresponding minor mode functions, like `auto-mode-alist'. All elements of this alist are checked, meaning you can @@ -195,36 +153,39 @@ with functions that require it (like modeline segments)." (advice-add #'make-indirect-buffer :around #'doom*set-indirect-buffer-filename) -;;; -;; Initialize -(eval-and-compile - (defvar doom--file-name-handler-alist file-name-handler-alist) - (unless (or after-init-time noninteractive) - ;; A big contributor to long startup times is the garbage collector, so we - ;; up its memory threshold, temporarily and reset it later in - ;; `doom|finalize'. - (setq gc-cons-threshold 402653184 - gc-cons-percentage 0.6 - file-name-handler-alist nil)) +;; +;; Bootstrap +;; - (when doom-private-dir - (load (concat doom-private-dir "early-init") t t)) +(defvar doom--file-name-handler-alist file-name-handler-alist) +(unless (or after-init-time noninteractive) + ;; A big contributor to long startup times is the garbage collector, so we up + ;; its memory threshold, temporarily and reset it later in `doom|finalize'. + (setq gc-cons-threshold 402653184 + gc-cons-percentage 1.0 + ;; consulted on every `require', `load' and various file reading + ;; functions. You get a minor speed up by nooping this. + file-name-handler-alist nil)) - (require 'core-packages (concat doom-core-dir "core-packages")) - (doom-initialize noninteractive) - (load! core-lib) - (load! core-os) ; consistent behavior across OSes +(defun doom|finalize () + "Resets garbage collection settings to reasonable defaults (if you don't do +this, you'll get stuttering and random freezes) and resets +`file-name-handler-alist'." (unless noninteractive - (load! core-ui) ; draw me like one of your French editors - (load! core-editor) ; baseline configuration for text editing - (load! core-projects) ; making Emacs project-aware - (load! core-keybinds)) ; centralized keybind system + which-key + (run-hooks 'doom-init-hook)) + (setq doom-init-p t + file-name-handler-alist doom--file-name-handler-alist + gc-cons-threshold 16777216 + gc-cons-percentage 0.15)) - (add-hook! '(emacs-startup-hook doom-reload-hook) #'doom|finalize) - (add-hook 'emacs-startup-hook #'doom|after-init) +;; +(require 'core-packages (concat doom-core-dir "core-packages")) +(doom-initialize noninteractive) - (when doom-private-dir - (load (concat doom-private-dir "init") t t))) +(add-hook! '(emacs-startup-hook doom-reload-hook) + #'doom|finalize) +(when doom-private-dir + (load (concat doom-private-dir "init") t t)) (provide 'core) ;;; core.el ends here