diff --git a/early-init.el b/early-init.el index f925597b0..d81d63c14 100644 --- a/early-init.el +++ b/early-init.el @@ -1,28 +1,33 @@ -;;; early-init.el -*- lexical-binding: t; -*- +;;; early-init.el --- Doom's universal bootstrapper -*- lexical-binding: t -*- ;;; Commentary: ;; -;; early-init.el was introduced in Emacs 27.1 and is loaded before init.el; -;; before Emacs initializes package.el and its UI; and before site files are -;; loaded. This is the best time to tweak Emacs (though any UI work will have to -;; be deferred), and will always be where Doom's dirtiest (and config-agnostic) -;; startup optimizations will live. +;; early-init.el was introduced in Emacs 27.1 and is loaded before init.el, and +;; before Emacs initializes its UI or package.el, and before site files are +;; loaded. This is good place for startup optimizating, because only here can +;; you *prevent* things from loading, rather than turn them off after-the-fact. +;; As such, Doom does all its initializing here. ;; -;; Doom uses this file as its universal bootstrapper, for both interactive and -;; non-interactive sessions. It's also the heart of its bootloader, which lets -;; you switch between Emacs configs on demand using `--init-directory DIR' -;; (which it has backported from Emacs 29) or `--profile NAME` (for more about -;; this, read the Profiles section in docs/developers.org or -;; `https://docs.doomemacs.org/developers'). +;; This file is Doom's "universal bootstrapper" for both interactive and +;; non-interactive sessions. It's also the heart of its profile bootloader, +;; which allows you to switch between Emacs configs on demand using +;; `--init-directory DIR' (which was backported from Emacs 29) or `--profile +;; NAME` (more about profiles at `https://docs.doomemacs.org/-/developers' or +;; docs/developers.org). ;; -;; If you're writing a Doom-based batch script, or using Doom's CLI framework, -;; load this file to initialize Doom and its CLI framework. Then you may -;; optionally load `doom-start' to initialize your interactive config on top of -;; it, if you need it. +;; In summary, this file is responsible for: +;; - Setting up some universal startup optimizations. +;; - Determining where `user-emacs-directory' is from one of: +;; - `--init-directory DIR' (backported from 29) +;; - `--profile PROFILENAME' +;; - Do one of the following: +;; - Load `doom' and one of `doom-start' or `doom-cli'. +;; - Or (if the user is trying to load a non-Doom config) load +;; `user-emacs-directory'/early-init.el. ;; ;;; Code: ;; PERF: Garbage collection is a big contributor to startup times. This fends it -;; off, then is reset later by enabling `gcmh-mode'. Not resetting it will +;; off, but will be reset later by `gcmh-mode'. Not resetting it later will ;; cause stuttering/freezes. (setq gc-cons-threshold most-positive-fixnum) diff --git a/lisp/cli/ci.el b/lisp/cli/ci.el index b8d33d747..b5757fa68 100644 --- a/lisp/cli/ci.el +++ b/lisp/cli/ci.el @@ -251,6 +251,7 @@ Note: warnings are not considered failures.") "Commands that automate development processes." :partial t) +;; TODO Move to 'doom install --git-hooks' (defcli! (ci deploy-hooks) ((force ("--force"))) "TODO" (let* ((repo-path (sh! "git" "rev-parse" "--show-toplevel")) @@ -283,6 +284,7 @@ Note: warnings are not considered failures.") (if overwrite-p "Overwrote" "Created") (path hook))))))) +;; TODO Move to 'doom lint commits' (defcli! (ci lint-commits) (from &optional to) "TODO" (with-temp-buffer @@ -305,10 +307,7 @@ Note: warnings are not considered failures.") commits)) commits)))) -;;; TODO -(defstub! (ci run-tests)) - -;;; doom ci hook +;; TODO Move to 'doom lint hook:commit-msg' (defcli! (ci hook commit-msg) (file) "Run git commit-msg hook. @@ -323,6 +322,7 @@ Lints the current commit message." (match-beginning 0) (point-max)))))))) +;; TODO Move to 'doom lint hook:pre-push' (defcli! (ci hook pre-push) (remote url) "Run git pre-push hook. diff --git a/lisp/doom-keybinds.el b/lisp/doom-keybinds.el index 09ab0090b..453449767 100644 --- a/lisp/doom-keybinds.el +++ b/lisp/doom-keybinds.el @@ -1,9 +1,12 @@ ;;; doom-keybinds.el -*- lexical-binding: t; -*- - +;;; Commentary: +;; ;; A centralized keybinds system, integrated with `which-key' to preview ;; available keybindings. All built into one powerful macro: `map!'. If evil is ;; never loaded, then evil bindings set with `map!' are ignored (i.e. omitted ;; entirely for performance reasons). +;; +;;; Code: (defvar doom-leader-key "SPC" "The leader prefix key for Evil users.") @@ -24,7 +27,7 @@ and Emacs states, and for non-evil users.") ;; -;;; Keybind settings +;;; Global keybind settings (cond (IS-MAC @@ -41,7 +44,14 @@ and Emacs states, and for non-evil users.") (setq w32-lwindow-modifier 'super w32-rwindow-modifier 'super))) -;; HACK Fixes Emacs' disturbing inability to distinguish C-i from TAB. +;; HACK: Emacs cannot distinguish between C-i from TAB. This is largely a +;; byproduct of its history in the terminal, which can't distinguish them +;; either, however, when GUIs came about Emacs greated separate input events +;; for more contentious keys like TAB and RET. Therefore [return] != RET, +;; [tab] != TAB, and [backspace] != DEL. +;; +;; In the same vein, this keybind adds a [C-i] event, so users can bind to it. +;; Otherwise, it falls back to regular C-i keybinds. (define-key key-translation-map [?\C-i] (cmd! (if (let ((keys (this-single-command-raw-keys))) (and keys @@ -116,8 +126,8 @@ all hooks after it are ignored.") (add-hook 'doom-after-init-modules-hook #'general-auto-unbind-keys)) -;; HACK `map!' uses this instead of `define-leader-key!' because it consumes -;; 20-30% more startup time, so we reimplement it ourselves. +;; HACK: `map!' uses this instead of `define-leader-key!' because it consumes +;; 20-30% more startup time, so we reimplement it ourselves. (defmacro doom--define-leader-key (&rest keys) (let (prefix forms wkforms) (while keys @@ -184,9 +194,10 @@ localleader prefix." :prefix doom-localleader-alt-key ,@args))) -;; We use a prefix commands instead of general's :prefix/:non-normal-prefix -;; properties because general is incredibly slow binding keys en mass with them -;; in conjunction with :states -- an effective doubling of Doom's startup time! +;; PERF: We use a prefix commands instead of general's +;; :prefix/:non-normal-prefix properties because general is incredibly slow +;; binding keys en mass with them in conjunction with :states -- an effective +;; doubling of Doom's startup time! (define-prefix-command 'doom/leader 'doom-leader-map) (define-key doom-leader-map [override-state] 'all) diff --git a/lisp/doom-modules.el b/lisp/doom-modules.el index 0f676bbbc..ce3c9bb9a 100644 --- a/lisp/doom-modules.el +++ b/lisp/doom-modules.el @@ -1,4 +1,9 @@ ;;; doom-modules.el --- module & package management system -*- lexical-binding: t; -*- +;;; Commentary: +;;; Code: + +;; +;;; Variables (defvar doom-init-modules-p nil "Non-nil if `doom-initialize-modules' has run.") diff --git a/lisp/doom-packages.el b/lisp/doom-packages.el index 857869441..012200dc9 100644 --- a/lisp/doom-packages.el +++ b/lisp/doom-packages.el @@ -1,7 +1,8 @@ ;;; lisp/doom-packages.el -*- lexical-binding: t; -*- - +;;; Commentary: +;; ;; Emacs package management is opinionated, and so is Doom. Doom uses `straight' -;; to create a declarative, lazy-loaded and (nominally) reproducible package +;; to create a declarative, lazy-loaded, and (nominally) reproducible package ;; management system. We use `straight' over `package' because the latter is ;; tempermental. ELPA sources suffer downtime occasionally and often fail to ;; build packages when GNU Tar is unavailable (e.g. MacOS users start with BSD @@ -17,21 +18,23 @@ ;; bin/doom script. Find out more about it by running 'doom help' (I highly ;; recommend you add the script to your PATH). Here are some highlights: ;; -;; + `bin/doom install`: a wizard that guides you through setting up Doom and -;; your private config for the first time. -;; + `bin/doom sync`: your go-to command for making sure Doom is in optimal +;; - `doom install`: a wizard that guides you through setting up Doom and your +;; private config for the first time. +;; - `doom sync`: your go-to command for making sure Doom is in optimal ;; condition. It ensures all unneeded packages are removed, all needed ones ;; are installed, and all metadata associated with them is generated. -;; + `bin/doom upgrade`: upgrades Doom Emacs and your packages to the latest +;; - `doom upgrade`: upgrades Doom Emacs and your packages to the latest ;; versions. There's also 'bin/doom sync -u' for updating only your packages. ;; ;; How this works is: the system reads packages.el files located in each -;; activated module, your private directory (`doom-user-dir'), and one in +;; activated module, your private config (`doom-user-dir'), and one in ;; `doom-core-dir'. These contain `package!' declarations that tell DOOM what -;; plugins to install and where from. +;; packages to install and where from. ;; -;; All that said, you can still use package.el's commands, but 'bin/doom sync' -;; will purge ELPA packages. +;; All that said, you can still use package.el's commands, but 'doom sync' will +;; purge ELPA packages. +;; +;;; Code: (defvar doom-packages () "A list of enabled packages. Each element is a sublist, whose CAR is the @@ -97,10 +100,10 @@ uses a straight or package.el command directly).") straight-vc-git-default-clone-depth '(1 single-branch)) (with-eval-after-load 'straight - ;; HACK Doom relies on deferred compilation, which spares the user 20-50min of - ;; compilation at install time, but subjects them to ~50% CPU activity when - ;; starting Emacs for the first time. To complete this, straight.el needs to - ;; be told not to do native-compilation, but it won't obey + ;; HACK: Doom relies on deferred compilation, which spares the user 20-50min + ;; of compilation at install time, but subjects them to ~50% CPU activity + ;; when starting Emacs for the first time. To complete this, straight.el + ;; needs to be told not to do native-compilation, but it won't obey ;; `straight-disable-native-compile'. ;; ;; It *will* obey `straight--native-comp-available', though. Trouble is: diff --git a/lisp/doom-start.el b/lisp/doom-start.el index 511394bf5..21d0d2720 100644 --- a/lisp/doom-start.el +++ b/lisp/doom-start.el @@ -1,4 +1,4 @@ -;;; lisp/doom-start.el --- bootstrapper for interactive sessions -*- lexical-binding: t; -*- +;;; lisp/doom-start.el --- bootstraps interactive sessions -*- lexical-binding: t; -*- ;;; Commentary: ;;; Code: @@ -6,7 +6,7 @@ ;; -;;; doom-first-*-hook +;;; Custom hooks (defvar doom-first-input-hook nil "Transient hooks run before the first user input.") @@ -22,8 +22,9 @@ ;; -;;; Runtime/startup optimizations +;;; Reasonable defaults for interactive sessions +;;; Runtime optimizations ;; A second, case-insensitive pass over `auto-mode-alist' is time wasted. (setq auto-mode-case-fold nil) @@ -51,11 +52,6 @@ ;; 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) - ;; 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 @@ -88,6 +84,23 @@ w32-pipe-read-delay 0 ; faster IPC w32-pipe-buffer-size (* 64 1024))) ; read more at a time (was 4K) +;; 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. However, if the idle delay is too long, we run the risk of +;; runaway memory usage in busy sessions. If it's too low, then we may as well +;; not be using gcmh at all. +(setq gcmh-idle-delay 'auto ; default is 15s + gcmh-auto-idle-delay-factor 10 + gcmh-high-cons-threshold (* 16 1024 1024)) ; 16mb +(add-hook 'doom-first-buffer-hook #'gcmh-mode) + + +;;; Startup optimizations +;; 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) + ;; Remove command line options that aren't relevant to our current OS; means ;; slightly less to process at startup. (eval-when! (not IS-MAC) (setq command-line-ns-option-alist nil)) @@ -105,10 +118,15 @@ (add-hook 'window-setup-hook #'doom-init-tty-h)) ;; Reduce *Message* noise at startup. An empty scratch buffer (or the dashboard) -;; is more than enough. +;; is more than enough, and faster to display. (setq inhibit-startup-screen t inhibit-startup-echo-area-message user-login-name inhibit-default-init t) +;; Get rid of "For information about GNU Emacs..." message at startup. It's +;; redundant with our dashboard and incurs a redraw. In daemon sessions it says +;; "Starting Emacs daemon" instead, which is fine. +(unless (daemonp) + (advice-add #'display-startup-echo-area-message :override #'ignore)) ;; Shave seconds off startup time by starting the scratch buffer in ;; `fundamental-mode', rather than, say, `org-mode' or `text-mode', which pull @@ -117,20 +135,8 @@ (setq initial-major-mode 'fundamental-mode initial-scratch-message nil) -;; 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. However, if the idle delay is too long, we run the risk of -;; runaway memory usage in busy sessions. If it's too low, then we may as well -;; not be using gcmh at all. -(setq gcmh-idle-delay 'auto ; default is 15s - gcmh-auto-idle-delay-factor 10 - gcmh-high-cons-threshold (* 16 1024 1024)) ; 16mb -(add-hook 'doom-first-buffer-hook #'gcmh-mode) - - -;; -;;; Reasonable defaults for interactive sessions +;;; Language ;; 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") @@ -142,12 +148,8 @@ (eval-when! IS-WINDOWS (setq selection-coding-system 'utf-8)) -;; Get rid of "For information about GNU Emacs..." message at startup. It's -;; redundant with our dashboard. However, in daemon sessions it says "Starting -;; Emacs daemon." instead, which is fine. -(unless (daemonp) - (advice-add #'display-startup-echo-area-message :override #'ignore)) +;;; User interface ;; GUIs are inconsistent across systems, will rarely match our active Emacs ;; theme, and impose their shortcut key paradigms suddenly. Let's just avoid ;; them altogether and have Emacs handle the prompting. @@ -162,6 +164,14 @@ (setq split-width-threshold 160 split-height-threshold nil) +;; Add support for additional file extensions. +(dolist (entry '(("/\\.doom\\(?:rc\\|project\\|module\\|profile\\)\\'" . emacs-lisp-mode) + ("/LICENSE\\'" . text-mode) + ("\\.log\\'" . text-mode) + ("rc\\'" . conf-mode) + ("\\.\\(?:hex\\|nes\\)\\'" . hexl-mode))) + (push entry auto-mode-alist)) + ;; ;;; MODE-local-vars-hook @@ -272,7 +282,7 @@ If this is a daemon session, load them all immediately instead." ;; -;;; Let 'er rip +;;; Benchmark (defvar doom-init-time nil "The time it took, in seconds, for Doom Emacs to initialize.") @@ -289,13 +299,6 @@ 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)))))) -;; Add support for additional file extensions. -(dolist (entry '(("/\\.doom\\(?:rc\\|project\\|module\\|profile\\)\\'" . emacs-lisp-mode) - ("/LICENSE\\'" . text-mode) - ("\\.log\\'" . text-mode) - ("rc\\'" . conf-mode) - ("\\.\\(?:hex\\|nes\\)\\'" . hexl-mode))) - (push entry auto-mode-alist)) ;; Doom caches a lot of information in `doom-autoloads-file'. Module and package ;; autoloads, autodefs like `set-company-backend!', and variables like diff --git a/lisp/doom.el b/lisp/doom.el index a226a3cca..2d91c0a9c 100644 --- a/lisp/doom.el +++ b/lisp/doom.el @@ -49,6 +49,9 @@ ;; ;;; Code: +;; Doom's minimum supported version of Emacs is 27.1. Its my goal to support one +;; major version below the stable release, for about a year or until stable is +;; ubiquitous (or at least easily accessible) across Linux distros. (when (< emacs-major-version 27) (user-error (concat @@ -147,10 +150,10 @@ ;; ;;; Cross-platform fixes +;; Fix $HOME on Windows, where it's not normally defined, because many unix +;; tools expect it. (when IS-WINDOWS (when-let (realhome - ;; Fix HOME on Windows, where it's not normally defined (though - ;; many unix tools expect it). (and (null (getenv-internal "HOME")) (getenv "USERPROFILE"))) (setenv "HOME" realhome) @@ -336,11 +339,11 @@ users).") ;; ;;; Don't litter `doom-emacs-dir'/$HOME -;; I change `user-emacs-directory' because many packages (even built-in ones) -;; abuse it to build paths for storage/cache files (instead of correctly using -;; `locate-user-emacs-file'). This change ensures that said data files are never -;; saved to the root of your emacs directory *and* saves us the trouble setting -;; a million directory/file variables. +;; HACK: I change `user-emacs-directory' because many packages (even built-in +;; ones) abuse it to build paths for storage/cache files (instead of correctly +;; using `locate-user-emacs-file'). This change ensures that said data files +;; are never saved to the root of your emacs directory *and* saves us the +;; trouble setting a million directory/file variables. (setq user-emacs-directory doom-cache-dir) ;; ...However, this may surprise packages (and users) that read @@ -365,7 +368,7 @@ users).") "~/.authinfo.gpg")) (define-advice en/disable-command (:around (fn &rest args) write-to-data-dir) - "Write saved safe-local-variables to `custom-file' instead. + "Save safe-local-variables to `custom-file' instead of `user-init-file'. Otherwise, `en/disable-command' (in novice.el.gz) is hardcoded to write them to `user-init-file')."