diff --git a/core/core-lib.el b/core/core-lib.el index 3fbec99fd..4bc25c25d 100644 --- a/core/core-lib.el +++ b/core/core-lib.el @@ -1,5 +1,8 @@ ;;; core-lib.el -*- lexical-binding: t; -*- +(require 'cl-lib) + + ;; ;;; Helpers @@ -112,6 +115,87 @@ at the values with which this function was called." if (lookup-key keymap keys) return it))) +(defun doom-load-envvars-file (file &optional noerror) + "Read and set envvars from FILE. +If NOERROR is non-nil, don't throw an error if the file doesn't exist or is +unreadable. Returns the names of envvars that were changed." + (if (null (file-exists-p file)) + (unless noerror + (signal 'file-error (list "No envvar file exists" file))) + (with-temp-buffer + (insert-file-contents file) + (when-let (env (read (current-buffer))) + (setq-default + process-environment + (append env (default-value 'process-environment)) + exec-path + (append (split-string (getenv "PATH") path-separator t) + (list exec-directory)) + shell-file-name + (or (getenv "SHELL") + (default-value 'shell-file-name))) + env)))) + +(defun doom-run-hook (hook) + "Run HOOK (a hook function) with better error handling. +Meant to be used with `run-hook-wrapped'." + (condition-case-unless-debug e + (funcall hook) + (error + (signal 'doom-hook-error (list hook e)))) + ;; return nil so `run-hook-wrapped' won't short circuit + nil) + +(defun doom-run-hooks (&rest hooks) + "Run HOOKS (a list of hook variable symbols) with better error handling. +Is used as advice to replace `run-hooks'." + (dolist (hook hooks) + (condition-case-unless-debug e + (run-hook-wrapped hook #'doom-run-hook) + (doom-hook-error + (unless debug-on-error + (lwarn hook :error "Error running hook %S because: %s" + (if (symbolp (cadr e)) + (symbol-name (cadr e)) + (cadr e)) + (caddr e))) + (signal 'doom-hook-error (cons hook (cdr e))))))) + +(defun doom-run-hook-on (hook-var trigger-hooks) + "Configure HOOK-VAR to be invoked exactly once when any of the TRIGGER-HOOKS +are invoked *after* Emacs has initialized (to reduce false positives). Once +HOOK-VAR is triggered, it is reset to nil. + +HOOK-VAR is a quoted hook. +TRIGGER-HOOK is a list of quoted hooks and/or sharp-quoted functions." + (dolist (hook trigger-hooks) + (let ((fn (intern (format "%s-init-on-%s-h" hook-var hook)))) + (fset + fn (lambda (&rest _) + ;; Only trigger this after Emacs has initialized. + (when (and after-init-time + (or (daemonp) + ;; In some cases, hooks may be lexically unset to + ;; inhibit them during expensive batch operations on + ;; buffers (such as when processing buffers + ;; internally). In these cases we should assume this + ;; hook wasn't invoked interactively. + (and (boundp hook) + (symbol-value hook)))) + (doom-run-hooks hook-var) + (set hook-var nil)))) + (cond ((daemonp) + ;; In a daemon session we don't need all these lazy loading + ;; shenanigans. Just load everything immediately. + (add-hook 'after-init-hook fn 'append)) + ((eq hook 'find-file-hook) + ;; Advise `after-find-file' instead of using `find-file-hook' + ;; because the latter is triggered too late (after the file has + ;; opened and modes are all set up). + (advice-add 'after-find-file :before fn '((depth . -101)))) + ((add-hook hook fn -101))) + fn))) + ;; ;;; Sugars diff --git a/core/core.el b/core/core.el index 2e3a9924e..47227d5b4 100644 --- a/core/core.el +++ b/core/core.el @@ -482,12 +482,7 @@ If this is a daemon session, load them all immediately instead." ;; -;;; Bootstrap helpers - -(defun doom-finish-init-h () - "Set `doom-init-time'." - (setq doom-init-time - (float-time (time-subtract (current-time) before-init-time)))) +;;; Bootstrapper (defun doom-display-benchmark-h (&optional return-p) "Display a benchmark including number of packages and modules loaded. @@ -497,92 +492,9 @@ If RETURN-P, return the message as a string instead of displaying it." "Doom loaded %d packages across %d modules in %.03fs" (- (length load-path) (length (get 'load-path 'initial-value))) (if doom-modules (hash-table-count doom-modules) 0) - (or doom-init-time (doom-finish-init-h)))) - -(defun doom-load-envvars-file (file &optional noerror) - "Read and set envvars from FILE. -If NOERROR is non-nil, don't throw an error if the file doesn't exist or is -unreadable. Returns the names of envvars that were changed." - (if (null (file-exists-p file)) - (unless noerror - (signal 'file-error (list "No envvar file exists" file))) - (with-temp-buffer - (insert-file-contents file) - (when-let (env (read (current-buffer))) - (setq-default - process-environment - (append env (default-value 'process-environment)) - exec-path - (append (split-string (getenv "PATH") path-separator t) - (list exec-directory)) - shell-file-name - (or (getenv "SHELL") - (default-value 'shell-file-name))) - env)))) - -(defun doom-run-hook (hook) - "Run HOOK (a hook function) with better error handling. -Meant to be used with `run-hook-wrapped'." - (condition-case-unless-debug e - (funcall hook) - (error - (signal 'doom-hook-error (list hook e)))) - ;; return nil so `run-hook-wrapped' won't short circuit - nil) - -(defun doom-run-hooks (&rest hooks) - "Run HOOKS (a list of hook variable symbols) with better error handling. -Is used as advice to replace `run-hooks'." - (dolist (hook hooks) - (condition-case-unless-debug e - (run-hook-wrapped hook #'doom-run-hook) - (doom-hook-error - (unless debug-on-error - (lwarn hook :error "Error running hook %S because: %s" - (if (symbolp (cadr e)) - (symbol-name (cadr e)) - (cadr e)) - (caddr e))) - (signal 'doom-hook-error (cons hook (cdr e))))))) - -(defun doom-run-hook-on (hook-var trigger-hooks) - "Configure HOOK-VAR to be invoked exactly once when any of the TRIGGER-HOOKS -are invoked *after* Emacs has initialized (to reduce false positives). Once -HOOK-VAR is triggered, it is reset to nil. - -HOOK-VAR is a quoted hook. -TRIGGER-HOOK is a list of quoted hooks and/or sharp-quoted functions." - (dolist (hook trigger-hooks) - (let ((fn (intern (format "%s-init-on-%s-h" hook-var hook)))) - (fset - fn (lambda (&rest _) - ;; Only trigger this after Emacs has initialized. - (when (and after-init-time - (or (daemonp) - ;; In some cases, hooks may be lexically unset to - ;; inhibit them during expensive batch operations on - ;; buffers (such as when processing buffers - ;; internally). In these cases we should assume this - ;; hook wasn't invoked interactively. - (and (boundp hook) - (symbol-value hook)))) - (doom-run-hooks hook-var) - (set hook-var nil)))) - (cond ((daemonp) - ;; In a daemon session we don't need all these lazy loading - ;; shenanigans. Just load everything immediately. - (add-hook 'after-init-hook fn 'append)) - ((eq hook 'find-file-hook) - ;; Advise `after-find-file' instead of using `find-file-hook' - ;; because the latter is triggered too late (after the file has - ;; opened and modes are all set up). - (advice-add 'after-find-file :before fn '((depth . -101)))) - ((add-hook hook fn -101))) - fn))) - - -;; -;;; Bootstrapper + (or doom-init-time + (setq doom-init-time + (float-time (time-subtract (current-time) before-init-time)))))) (defun doom-initialize (&optional force-p) "Bootstrap Doom, if it hasn't already (or if FORCE-P is non-nil). @@ -598,11 +510,11 @@ The overall load order of Doom is as follows: ~/.emacs.d/core/core.el ~/.doom.d/init.el Module init.el files - `doom-before-init-modules-hook' - Module config.el files - ~/.doom.d/config.el `doom-init-modules-hook' - `doom-after-init-modules-hook' (alias for `after-init-hook') + Module config.el files + `doom-configure-modules-hook' + ~/.doom.d/config.el + `after-init-hook' `emacs-startup-hook' `doom-init-ui-hook' `window-setup-hook' @@ -616,7 +528,7 @@ to least)." ;; Reset as much state as possible, so `doom-initialize' can be treated like ;; a reset function. e.g. when reloading the config. - (dolist (var '(exec-path load-path process-environment)) + (dolist (var '(exec-path load-path)) (set-default var (get var 'initial-value))) ;; Doom caches a lot of information in `doom-autoloads-file'. Module and @@ -645,10 +557,11 @@ to least)." ;; 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. - (if (and (or (display-graphic-p) - (daemonp)) - doom-env-file) - (doom-load-envvars-file doom-env-file 'noerror)) + (when (and (or (display-graphic-p) + (daemonp)) + doom-env-file) + (setq-default process-environment (get 'process-environment 'initial-value)) + (doom-load-envvars-file doom-env-file 'noerror)) ;; Loads `use-package' and all the helper macros modules (and users) can use ;; to configure their packages. @@ -661,17 +574,17 @@ to least)." (eval-after-load 'package '(require 'core-packages)) (eval-after-load 'straight '(doom-initialize-packages)) - ;; Bootstrap the interactive session - (add-hook 'after-change-major-mode-hook #'doom-run-local-var-hooks-maybe-h 100) - (add-hook 'hack-local-variables-hook #'doom-run-local-var-hooks-h) - (add-hook 'emacs-startup-hook #'doom-load-packages-incrementally-h) - (add-hook 'window-setup-hook #'doom-display-benchmark-h) - (doom-run-hook-on 'doom-first-buffer-hook '(find-file-hook doom-switch-buffer-hook)) - (doom-run-hook-on 'doom-first-file-hook '(find-file-hook dired-initial-position-hook)) - (doom-run-hook-on 'doom-first-input-hook '(pre-command-hook)) + (unless noninteractive + ;; Bootstrap the interactive session + (add-hook 'after-change-major-mode-hook #'doom-run-local-var-hooks-maybe-h 100) + (add-hook 'hack-local-variables-hook #'doom-run-local-var-hooks-h) + (add-hook 'emacs-startup-hook #'doom-load-packages-incrementally-h) + (add-hook 'window-setup-hook #'doom-display-benchmark-h) + (doom-run-hook-on 'doom-first-buffer-hook '(find-file-hook doom-switch-buffer-hook)) + (doom-run-hook-on 'doom-first-file-hook '(find-file-hook dired-initial-position-hook)) + (doom-run-hook-on 'doom-first-input-hook '(pre-command-hook)) - ;; Bootstrap our GC manager - (add-hook 'doom-first-buffer-hook #'gcmh-mode)) + (add-hook 'doom-first-buffer-hook #'gcmh-mode))) doom-init-p)