doomemacs/core/core.el
Itai Y. Efrat 34fae1c01c Remove remaining Emacs27+ checks
- Remove remaining `EMACS27+` checks, since the whole codebase is
  assumed to run at version 27 or above now
- Remove `EMACS27+` definition since it's no longer needed
2021-07-10 16:03:41 +03:00

678 lines
28 KiB
EmacsLisp

;;; core.el --- the heart of the beast -*- lexical-binding: t; -*-
;;
;;; Initialize internal state
(defconst doom-version "3.0.0-alpha"
"Current version of Doom Emacs.")
(defvar doom-init-p nil
"Non-nil if Doom has been initialized.")
(defvar doom-init-time nil
"The time it took, in seconds, for Doom Emacs to initialize.")
(defvar doom-debug-p (or (getenv-internal "DEBUG") init-file-debug)
"If non-nil, Doom will log more.
Use `doom-debug-mode' to toggle it. The --debug-init flag and setting the DEBUG
envvar will enable this at startup.")
(defconst doom-interactive-p (not noninteractive)
"If non-nil, Emacs is in interactive mode.")
(defconst EMACS28+ (> emacs-major-version 27))
(defconst IS-MAC (eq system-type 'darwin))
(defconst IS-LINUX (eq system-type 'gnu/linux))
(defconst IS-WINDOWS (memq system-type '(cygwin windows-nt ms-dos)))
(defconst IS-BSD (or IS-MAC (eq system-type 'berkeley-unix)))
;; Ensure Doom's core libraries are visible for loading
(add-to-list 'load-path (file-name-directory load-file-name))
;; Remember these variables' initial values, so we can safely reset them at a
;; later time, or consult them without fear of contamination.
(dolist (var '(exec-path load-path process-environment))
(unless (get var 'initial-value)
(put var 'initial-value (default-value var))))
;; Unix tools look for HOME, but this is normally not defined on Windows.
(when (and IS-WINDOWS (null (getenv-internal "HOME")))
(setenv "HOME" (getenv "USERPROFILE"))
(setq abbreviated-home-dir nil))
;; Contrary to what many Emacs users have in their configs, you don't need more
;; than this to make UTF-8 the default coding system:
(set-language-environment "UTF-8")
;; ...but the clipboard's on Windows could be in another encoding (likely
;; utf-16), so let Emacs/the OS decide what to use there.
(unless IS-WINDOWS
(setq selection-coding-system 'utf-8)) ; with sugar on top
;;; Custom error types
(define-error 'doom-error "Error in Doom Emacs core")
(define-error 'doom-hook-error "Error in a Doom startup hook" 'doom-error)
(define-error 'doom-autoload-error "Error in Doom's autoloads file" 'doom-error)
(define-error 'doom-module-error "Error in a Doom module" 'doom-error)
(define-error 'doom-private-error "Error in private config" 'doom-error)
(define-error 'doom-package-error "Error with packages" 'doom-error)
;;
;;; Directory variables
(defconst doom-emacs-dir user-emacs-directory
"The path to the currently loaded .emacs.d directory. Must end with a slash.")
(defconst doom-core-dir (concat doom-emacs-dir "core/")
"The root directory of Doom's core files. Must end with a slash.")
(defconst doom-modules-dir (concat doom-emacs-dir "modules/")
"The root directory for Doom's modules. Must end with a slash.")
(defconst doom-local-dir
(let ((localdir (getenv-internal "DOOMLOCALDIR")))
(if localdir
(expand-file-name (file-name-as-directory localdir))
(concat doom-emacs-dir ".local/")))
"Root directory for local storage.
Use this as a storage location for this system's installation of Doom Emacs.
These files should not be shared across systems. By default, it is used by
`doom-etc-dir' and `doom-cache-dir'. Must end with a slash.")
(defconst doom-etc-dir (concat doom-local-dir "etc/")
"Directory for non-volatile local storage.
Use this for files that don't change much, like server binaries, external
dependencies or long-term shared data. Must end with a slash.")
(defconst doom-cache-dir (concat doom-local-dir "cache/")
"Directory for volatile local storage.
Use this for files that change often, like cache files. Must end with a slash.")
(defconst doom-docs-dir (concat doom-emacs-dir "docs/")
"Where Doom's documentation files are stored. Must end with a slash.")
(defconst doom-private-dir
(let ((doomdir (getenv-internal "DOOMDIR")))
(if doomdir
(expand-file-name (file-name-as-directory doomdir))
(or (let ((xdgdir
(expand-file-name "doom/"
(or (getenv-internal "XDG_CONFIG_HOME")
"~/.config"))))
(if (file-directory-p xdgdir) xdgdir))
"~/.doom.d/")))
"Where your private configuration is placed.
Defaults to ~/.config/doom, ~/.doom.d or the value of the DOOMDIR envvar;
whichever is found first. Must end in a slash.")
(defconst doom-autoloads-file
(concat doom-local-dir "autoloads." emacs-version ".el")
"Where `doom-reload-core-autoloads' stores its core autoloads.
This file is responsible for informing Emacs where to find all of Doom's
autoloaded core functions (in core/autoload/*.el).")
(defconst doom-env-file (concat doom-local-dir "env")
"The location of your envvar file, generated by `doom env`.
This file contains environment variables scraped from your shell environment,
which is loaded at startup (if it exists). This is helpful if Emacs can't
\(easily) be launched from the correct shell session (particularly for MacOS
users).")
;;
;;; Custom hooks
(defvar doom-first-input-hook nil
"Transient hooks run before the first user input.")
(put 'doom-first-input-hook 'permanent-local t)
(defvar doom-first-file-hook nil
"Transient hooks run before the first interactively opened file.")
(put 'doom-first-file-hook 'permanent-local t)
(defvar doom-first-buffer-hook nil
"Transient hooks run before the first interactively opened buffer.")
(put 'doom-first-buffer-hook 'permanent-local t)
(defvar doom-after-reload-hook nil
"A list of hooks to run after `doom/reload' has reloaded Doom.")
(defvar doom-before-reload-hook nil
"A list of hooks to run before `doom/reload' has reloaded Doom.")
;;
;;; Native Compilation support (http://akrl.sdf.org/gccemacs.html)
;; REVIEW Remove after a month
(when EMACS28+
(mapc (lambda (varset)
(unless (boundp (car varset))
(defvaralias (car varset) (cdr varset))))
'((native-comp-deferred-compilation . comp-deferred-compilation)
(native-comp-deferred-compilation-deny-list . comp-deferred-compilation-deny-list)
(native-comp-eln-load-path . comp-eln-load-path)
(native-comp-warning-on-missing-source . comp-warning-on-missing-source)
(native-comp-driver-options . comp-native-driver-options)
(native-comp-async-query-on-exit . comp-async-query-on-exit)
(native-comp-async-report-warnings-errors . comp-async-report-warnings-errors)
(native-comp-async-env-modifier-form . comp-async-env-modifier-form)
(native-comp-async-all-done-hook . comp-async-all-done-hook)
(native-comp-async-cu-done-functions . comp-async-cu-done-functions)
(native-comp-async-jobs-number . comp-async-jobs-number)
(native-comp-never-optimize-functions . comp-never-optimize-functions)
(native-comp-bootstrap-deny-list . comp-bootstrap-deny-list)
(native-comp-always-compile . comp-always-compile)
(native-comp-verbose . comp-verbose)
(native-comp-debug . comp-debug)
(native-comp-speed . comp-speed))))
;; Don't store eln files in ~/.emacs.d/eln-cache (they are likely to be purged
;; when upgrading Doom).
(when (boundp 'native-comp-eln-load-path)
(add-to-list 'native-comp-eln-load-path (concat doom-cache-dir "eln/")))
(with-eval-after-load 'comp
;; HACK Disable native-compilation for some troublesome packages
(mapc (apply-partially #'add-to-list 'native-comp-deferred-compilation-deny-list)
(let ((local-dir-re (concat "\\`" (regexp-quote doom-local-dir))))
(list (concat "\\`" (regexp-quote doom-autoloads-file) "\\'")
(concat local-dir-re ".*/evil-collection-vterm\\.el\\'")
(concat local-dir-re ".*/with-editor\\.el\\'")
;; https://github.com/nnicandro/emacs-jupyter/issues/297
(concat local-dir-re ".*/jupyter-channel\\.el\\'")))))
;;
;;; Core libraries
;; Just the... bear necessities~
(require 'subr-x)
(require 'cl-lib)
(require 'core-lib)
;;
;;; A quieter startup
;; Disable warnings from legacy advice system. They aren't useful, and what can
;; we do about them, besides changing packages upstream?
(setq ad-redefinition-action 'accept)
;; Reduce debug output, well, unless we've asked for it.
(setq debug-on-error doom-debug-p
jka-compr-verbose doom-debug-p)
;; Get rid of "For information about GNU Emacs..." message at startup, unless
;; we're in a daemon session where it'll say "Starting Emacs daemon." instead,
;; which isn't so bad.
(unless (daemonp)
(advice-add #'display-startup-echo-area-message :override #'ignore))
;; Reduce *Message* noise at startup. An empty scratch buffer (or the dashboard)
;; is more than enough.
(setq inhibit-startup-message t
inhibit-startup-echo-area-message user-login-name
inhibit-default-init t
;; Shave seconds off startup time by starting the scratch buffer in
;; `fundamental-mode', rather than, say, `org-mode' or `text-mode', which
;; pull in a ton of packages. `doom/open-scratch-buffer' provides a better
;; scratch buffer anyway.
initial-major-mode 'fundamental-mode
initial-scratch-message nil)
;;
;;; Don't litter `doom-emacs-dir'
;; We avoid `no-littering' because it's a mote too opinionated for our needs.
(setq async-byte-compile-log-file (concat doom-etc-dir "async-bytecomp.log")
custom-file (concat doom-private-dir "custom.el")
desktop-dirname (concat doom-etc-dir "desktop")
desktop-base-file-name "autosave"
desktop-base-lock-name "autosave-lock"
pcache-directory (concat doom-cache-dir "pcache/")
request-storage-directory (concat doom-cache-dir "request")
shared-game-score-directory (concat doom-etc-dir "shared-game-score/"))
(defadvice! doom--write-to-etc-dir-a (orig-fn &rest args)
"Resolve Emacs storage directory to `doom-etc-dir', to keep local files from
polluting `doom-emacs-dir'."
:around #'locate-user-emacs-file
(let ((user-emacs-directory doom-etc-dir))
(apply orig-fn args)))
(defadvice! doom--write-enabled-commands-to-doomdir-a (orig-fn &rest args)
"When enabling a disabled command, the `put' call is written to
~/.emacs.d/init.el, which causes issues for Doom, so write it to the user's
config.el instead."
:around #'en/disable-command
(let ((user-init-file custom-file))
(apply orig-fn args)))
;;
;;; Optimizations
;; A second, case-insensitive pass over `auto-mode-alist' is time wasted, and
;; indicates misconfiguration (don't rely on case insensitivity for file names).
(setq auto-mode-case-fold nil)
;; Disable bidirectional text rendering for a modest performance boost. I've set
;; this to `nil' in the past, but the `bidi-display-reordering's docs say that
;; is an undefined state and suggest this to be just as good:
(setq-default bidi-display-reordering 'left-to-right
bidi-paragraph-direction 'left-to-right)
;; Disabling the BPA makes redisplay faster, but might produce incorrect display
;; reordering of bidirectional text with embedded parentheses and other bracket
;; characters whose 'paired-bracket' Unicode property is non-nil.
(setq bidi-inhibit-bpa t) ; Emacs 27 only
;; Reduce rendering/line scan work for Emacs by not rendering cursors or regions
;; in non-focused windows.
(setq-default cursor-in-non-selected-windows nil)
(setq highlight-nonselected-windows nil)
;; More performant rapid scrolling over unfontified regions. May cause brief
;; spells of inaccurate syntax highlighting right after scrolling, which should
;; quickly self-correct.
(setq fast-but-imprecise-scrolling t)
;; Don't ping things that look like domain names.
(setq ffap-machine-p-known 'reject)
;; Resizing the Emacs frame can be a terribly expensive part of changing the
;; font. By inhibiting this, we halve startup times, particularly when we use
;; fonts that are larger than the system default (which would resize the frame).
(setq frame-inhibit-implied-resize t)
;; The GC introduces annoying pauses and stuttering into our Emacs experience,
;; so we use `gcmh' to stave off the GC while we're using Emacs, and provoke it
;; when it's idle.
(setq gcmh-idle-delay 5 ; default is 15s
gcmh-high-cons-threshold (* 16 1024 1024) ; 16mb
gcmh-verbose doom-debug-p)
;; Emacs "updates" its ui more often than it needs to, so slow it down slightly
(setq idle-update-delay 1.0) ; default is 0.5
;; Font compacting can be terribly expensive, especially for rendering icon
;; fonts on Windows. Whether disabling it has a notable affect on Linux and Mac
;; hasn't been determined, but do it there anyway, just in case. This increases
;; memory usage, however!
(setq inhibit-compacting-font-caches t)
;; Increase how much is read from processes in a single chunk (default is 4kb).
;; This is further increased elsewhere, where needed (like our LSP module).
(setq read-process-output-max (* 64 1024)) ; 64kb
;; Introduced in Emacs HEAD (b2f8c9f), this inhibits fontification while
;; receiving input, which should help a little with scrolling performance.
(setq redisplay-skip-fontification-on-input t)
;; Performance on Windows is considerably worse than elsewhere. We'll need
;; everything we can get.
(when IS-WINDOWS
(setq w32-get-true-file-attributes nil ; decrease file IO workload
w32-pipe-read-delay 0 ; faster IPC
w32-pipe-buffer-size (* 64 1024))) ; read more at a time (was 4K)
;; Remove command line options that aren't relevant to our current OS; means
;; slightly less to process at startup.
(unless IS-MAC (setq command-line-ns-option-alist nil))
(unless IS-LINUX (setq command-line-x-option-alist nil))
;; HACK `tty-run-terminal-initialization' is *tremendously* slow for some
;; reason; inexplicably doubling startup time for terminal Emacs. Keeping
;; it disabled will have nasty side-effects, so we simply delay it instead,
;; and invoke it later, at which point it runs quickly; how mysterious!
(unless (daemonp)
(advice-add #'tty-run-terminal-initialization :override #'ignore)
(add-hook! 'window-setup-hook
(defun doom-init-tty-h ()
(advice-remove #'tty-run-terminal-initialization #'ignore)
(tty-run-terminal-initialization (selected-frame) nil t))))
;;
;;; Security
;; 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
;; least a little more discerning.
(setq gnutls-verify-error (not (getenv-internal "INSECURE"))
gnutls-algorithm-priority
(when (boundp 'libgnutls-version)
(concat "SECURE128:+SECURE192:-VERS-ALL"
(if (and (not IS-WINDOWS)
(>= libgnutls-version 30605))
":+VERS-TLS1.3")
":+VERS-TLS1.2"))
;; `gnutls-min-prime-bits' is set based on recommendations from
;; https://www.keylength.com/en/4/
gnutls-min-prime-bits 3072
tls-checktrust gnutls-verify-error
;; Emacs is built with `gnutls' by default, so `tls-program' would not be
;; used in that case. Otherwise, people have reasons to not go with
;; `gnutls', we use `openssl' instead. For more details, see
;; https://redd.it/8sykl1
tls-program '("openssl s_client -connect %h:%p -CAfile %t -nbio -no_ssl3 -no_tls1 -no_tls1_1 -ign_eof"
"gnutls-cli -p %p --dh-bits=3072 --ocsp --x509cafile=%t \
--strict-tofu --priority='SECURE192:+SECURE128:-VERS-ALL:+VERS-TLS1.2:+VERS-TLS1.3' %h"
;; compatibility fallbacks
"gnutls-cli -p %p %h"))
;; Emacs stores `authinfo' in $HOME and in plain-text. Let's not do that, mkay?
;; This file stores usernames, passwords, and other such treasures for the
;; aspiring malicious third party.
(setq auth-sources (list (concat doom-etc-dir "authinfo.gpg")
"~/.authinfo.gpg"))
;;
;;; MODE-local-vars-hook
;; File+dir local variables are initialized after the major mode and its hooks
;; have run. If you want hook functions to be aware of these customizations, add
;; them to MODE-local-vars-hook instead.
(defvar doom-inhibit-local-var-hooks nil)
(defun doom-run-local-var-hooks-h ()
"Run MODE-local-vars-hook after local variables are initialized."
(unless doom-inhibit-local-var-hooks
(setq-local doom-inhibit-local-var-hooks t)
(doom-run-hooks (intern-soft (format "%s-local-vars-hook" major-mode)))))
;; 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-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)))
;;
;;; Incremental lazy-loading
(defvar doom-incremental-packages '(t)
"A list of packages to load incrementally after startup. Any large packages
here may cause noticeable pauses, so it's recommended you break them up into
sub-packages. For example, `org' is comprised of many packages, and can be
broken up into:
(doom-load-packages-incrementally
'(calendar find-func format-spec org-macs org-compat
org-faces org-entities org-list org-pcomplete org-src
org-footnote org-macro ob org org-clock org-agenda
org-capture))
This is already done by the lang/org module, however.
If you want to disable incremental loading altogether, either remove
`doom-load-packages-incrementally-h' from `emacs-startup-hook' or set
`doom-incremental-first-idle-timer' to nil. Incremental loading does not occur
in daemon sessions (they are loaded immediately at startup).")
(defvar doom-incremental-first-idle-timer 2.0
"How long (in idle seconds) until incremental loading starts.
Set this to nil to disable incremental loading.")
(defvar doom-incremental-idle-timer 0.75
"How long (in idle seconds) in between incrementally loading packages.")
(defvar doom-incremental-load-immediately (daemonp)
"If non-nil, load all incrementally deferred packages immediately at startup.")
(defun doom-load-packages-incrementally (packages &optional now)
"Registers PACKAGES to be loaded incrementally.
If NOW is non-nil, load PACKAGES incrementally, in `doom-incremental-idle-timer'
intervals."
(if (not now)
(appendq! doom-incremental-packages packages)
(while packages
(let* ((gc-cons-threshold most-positive-fixnum)
(req (pop packages)))
(unless (featurep req)
(doom-log "Incrementally loading %s" req)
(condition-case-unless-debug e
(or (while-no-input
;; If `default-directory' is a directory that doesn't exist
;; or is unreadable, Emacs throws up file-missing errors, so
;; we set it to a directory we know exists and is readable.
(let ((default-directory doom-emacs-dir)
(inhibit-message t)
file-name-handler-alist)
(require req nil t))
t)
(push req packages))
(error
(message "Failed to load %S package incrementally, because: %s"
req e)))
(if (not packages)
(doom-log "Finished incremental loading")
(run-with-idle-timer doom-incremental-idle-timer
nil #'doom-load-packages-incrementally
packages t)
(setq packages nil)))))))
(defun doom-load-packages-incrementally-h ()
"Begin incrementally loading packages in `doom-incremental-packages'.
If this is a daemon session, load them all immediately instead."
(if doom-incremental-load-immediately
(mapc #'require (cdr doom-incremental-packages))
(when (numberp doom-incremental-first-idle-timer)
(run-with-idle-timer doom-incremental-first-idle-timer
nil #'doom-load-packages-incrementally
(cdr doom-incremental-packages) t))))
;;
;;; Bootstrap helpers
(defun doom-finish-init-h ()
"Set `doom-init-time'."
(setq doom-init-time
(float-time (time-subtract (current-time) before-init-time))))
(defun doom-display-benchmark-h (&optional return-p)
"Display a benchmark including number of packages and modules loaded.
If RETURN-P, return the message as a string instead of displaying it."
(funcall (if return-p #'format #'message)
"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'."
(doom-log "Running doom hook: %s" hook)
(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
(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 everything Doom needs to run is set up;
essential directories exist, core packages are installed, `doom-autoloads-file'
is loaded (failing if it isn't), that all the needed hooks are in place, and
that `core-packages' will load when `package' or `straight' is used.
The overall load order of Doom is as follows:
~/.emacs.d/init.el
~/.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')
`emacs-startup-hook'
`doom-init-ui-hook'
`window-setup-hook'
Module load order is determined by your `doom!' block. See `doom-modules-dirs'
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.
(dolist (var '(exec-path load-path process-environment))
(set-default var (get var 'initial-value)))
;; Doom caches a lot of information in `doom-autoloads-file'. Module and
;; package autoloads, autodefs like `set-company-backend!', and variables
;; like `doom-modules', `doom-disabled-packages', `load-path',
;; `auto-mode-alist', and `Info-directory-list'. etc. Compiling them into
;; one place is a big reduction in startup time.
(condition-case-unless-debug e
;; Avoid `file-name-sans-extension' for premature optimization reasons.
;; `string-remove-suffix' is cheaper because it performs no file sanity
;; checks; just plain ol' string manipulation.
(load (string-remove-suffix ".el" doom-autoloads-file) nil 'nomessage)
(file-missing
;; If the autoloads file fails to load then the user forgot to sync, or
;; aborted a doom command midway!
(if (locate-file doom-autoloads-file load-path)
;; Something inside the autoloads file is triggering this error;
;; forward it to the caller!
(signal 'doom-autoload-error e)
(signal 'doom-error
(list "Doom is in an incomplete state"
"run 'doom sync' on the command line to repair it")))))
(if doom-debug-p (doom-debug-mode +1))
;; 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))
;; Loads `use-package' and all the helper macros modules (and users) can use
;; to configure their packages.
(require 'core-modules)
;; There's a chance the user will later use package.el or straight in this
;; interactive session. If they do, make sure they're properly initialized
;; when they do.
(autoload 'doom-initialize-packages "core-packages")
(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))
;; Bootstrap our GC manager
(add-hook 'doom-first-buffer-hook #'gcmh-mode))
doom-init-p)
(provide 'core)
;;; core.el ends here