GNU Emacs configuration
This page is the successor to my old Emacs literate configuration file. I no longer use Org to maintain my Emacs setup. Instead, everything is done directly with Emacs Lisp. The purpose of this document is to describe my setup and list the contents of all the relevant files I am loading. Last revised and exported on 2023-08-05 10:26:38 +0300.
Table of Contents
1. About this page
Herein you will find documentation about my files for Emacs. This is not a literate configuration: it is ordinary Emacs Lisp code. I am writing about it in this separate file to explain how my setup is organised.
- Website: https://protesilaos.com/emacs/dotemacs
- Git repo on SourceHut: https://git.sr.ht/~protesilaos/dotfiles
- Mirrors:
- Mailing list: https://lists.sr.ht/~protesilaos/dotfiles
- Backronym: Do Observe, Transpose, Examine, or Mirror All Configurations, Stranger (dotemacs); Dotfiles Operate Transparently For the Included Linux and Emacs Setups (dotfiles).
2. Details of my Emacs build
I track the trunk of emacs.git, as I am the maintainer of several
Emacs packages and a contributor to Emacs core. Users of Arch Linux
can refer to this PKGBUILD
I maintain for my purposes:
- Git repo on SourceHut: https://git.sr.ht/~protesilaos/emacs-arch-linux-pkgbuild
- Mailing list: https://lists.sr.ht/~protesilaos/general-issues
- Backronym for “PKGBUILD … of Emacs”: Package Knowhow Germane to Building Unapologetically Individuated Local Design … of Emacs.
3. Anatomy of my Emacs configuration
3.1. Overview of my Emacs configuration
early-init.el
- Optimisations for starting up Emacs and setting the basics.
init.el
- Defines foundational blocks of my system and loads the individual configuration modules.
prot-emacs-modules
directory- Includes all my configuration modules. These simply tweak packages and are not meant to define extra functionality.
prot-lisp
directory- Custom packages
3.2. The anatomy of my Emacs in detail
I have built my setup from scratch and am observing best practices
with regard to how Emacs expects things to run. I do not use the
Emacs daemon, as I have encountered instabilities with it. Instead, I
run a single instance of Emacs and then configure it to act as the
server. This means that I can still connect to the running session
via emacsclient
, which is useful when I want to evaluate Elisp code
from outside of Emacs (e.g. with the delight
shell script that
switches the entire “environment” theme of my tiling window
manager—see my dotfiles for the technicalities).
With those granted, here is an overview of how files are organised and what they do:
- The
early-init.el
file - This is the first file that Emacs reads
when starting up. It should contain code that does not depend on
any package or the proportions of the Emacs frame. My
early-init.el
is the place where I:- Set up the parameters of the initial and all future frames.
- Optimise the early initialisation phase to speed up startup time.
- Define functions that test whether my environment is using a light or dark theme. Those are used by my Emacs theme-related configurations, to load an appropriate theme.
- Prevent the initial flash of light if my environment theme is dark when I launch Emacs.
- The
init.el
file - This is the main configuration file that Emacs
uses. Mine defines some user options that are intended for use in
the
prot-emacs-pre-custom.el
file (more below) and then goes on to handle the substantive parts of my configuration. Concretely:- Make the
custom-file
disposable because I consider persistent configurations outside my hand-written code to be highly problematic. - Register my
prot-lisp/
andprot-emacs-modules/
in the Emacsload-path
. Read further below for what these directories contain. - Load all the modules of my setup in the appropriate order.
- Arrange my package archives and ensures that the packages I
develop are drawn from the
elpa-devel
archive. Read here for all my packages: https://protesilaos.com/emacs. I install my packages fromelpa-devel
just to be sure that their installation works properly. - Define the Lisp Macros that are used throughout my setup, such as
prot-emacs-package
andprot-emacs-keybind
. Why notuse-package
, especially now that it is built into Emacs? Because (i) I do not like many aspects of its behaviour, such as key bindings “magically” delaying the load of a package, and (ii) I have no use for most of its functionality. Writing my own Lisp macros helps me practice my coding skills and get exactly what I want.
- Make the
- The
prot-emacs-modules
directory This is where I store all the individual components of my Emacs setup. The directory is a subdirectory of
~/.emacs.d/
. All files are prefixed withprot-emacs-
, followed by a word that broadly describes their scope of application, such asprot-emacs-font
,prot-emacs-window
… Each module consists of ordinary Elisp and a final call toprovide
the set of configurations as a feature that can then be loaded viarequire
from theinit.el
. What Emacs calls a “feature” is, in essence a variable whose value is the entirety of the file that has aprovide
call to it. Features are symbols that are named after the file name minus its file type extension:prot-emacs-font
is the feature provided byprot-emacs-font.el
.Modules are intended only for configuration purposes. They do not define any major variables/functions.
- The
prot-lisp
directory - As with the aforementioned modules,
this directory is a subdirectory of
~/.emacs.d/
. This is where I keep all my custom code that individual modules can use. The contents of this directory can be understood as “packages” and, in fact, many of my actual packages started out asprot-lisp
experiments. Each file is written in accordance with the conventions on Emacs packaging, even though they are only intended for use in my setup and are not polished to the level of my actual public-facing packages (meaning the ones listed here: https://protesilaos.com/emacs). - The
prot-emacs-pre-custom
file - It is evaluated before the
modules are loaded. It is intended for users of my configuration
who want to:
- Specify their preferred theme family by setting the user option
prot-emacs-load-theme-family
. - Choose a completion framework of their choice by configuring
prot-emacs-completion-ui
. - Opt to omit packages from being loaded by the
init.el
and its modules, by defining the value ofprot-emacs-omit-packages
.
- Specify their preferred theme family by setting the user option
- The
prot-emacs-post-custom
file - Like the above, this file is meant for users of my setup. It is evaluated after the rest of my setup is loaded. Here they can include whatever code they want.
- The
prot-emacs.org
file - The source of what you are currently reading. It is not pertinent to the configuration of Emacs. All it does is document the rest of my code.
4. Contents of all files in my setup
The following sections produce the contents of all the files that form part of my Emacs setup. ⚠️ This is a work-in-progress: not all files are included herein, though the plan is to add everything.
4.1. early-init.el
As explained earlier, this is the first file that my Emacs reads at startup (Anatomy of my Emacs configuration).
;;; early-init.el --- Early Init File -*- lexical-binding: t -*- ;; Copyright (c) 2020-2023 Protesilaos Stavrou <info@protesilaos.com> ;; Author: Protesilaos Stavrou <info@protesilaos.com> ;; URL: https://protesilaos.com/emacs/dotemacs ;; Version: 0.1.0 ;; Package-Requires: ((emacs "30.1")) ;; This file is NOT part of GNU Emacs. ;; This file is free software: you can redistribute it and/or modify it ;; under the terms of the GNU General Public License as published by the ;; Free Software Foundation, either version 3 of the License, or (at ;; your option) any later version. ;; ;; This file is distributed in the hope that it will be useful, but ;; WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;; General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with this file. If not, see <https://www.gnu.org/licenses/>. ;;; Commentary: ;; See my dotfiles: https://git.sr.ht/~protesilaos/dotfiles ;;; Code: (defvar prot-emacs-tiling-window-manager-regexp "\\(?:\\(?:bsp\\|herbstluft\\)wm\\)" "Regular expression to match desired tiling window managers. See definition of `prot-emacs-with-desktop-session'.") (defmacro prot-emacs-with-desktop-session (&rest body) "Expand BODY if desktop session is not a tiling window manager. See `prot-emacs-tiling-window-manager-regexp' for what constitutes a matching tiling window manager." (declare (indent 0)) `(when-let* ((session (getenv "DESKTOP_SESSION")) ((not (string-match-p session prot-emacs-tiling-window-manager-regexp)))) ,@body)) (defun prot-emacs-add-to-list (list element) "Add to symbol of LIST the given ELEMENT. Simplified version of `add-to-list'." (set list (cons element (symbol-value list)))) (prot-emacs-with-desktop-session (mapc (lambda (var) (prot-emacs-add-to-list var '(width . (text-pixels . 1200))) (prot-emacs-add-to-list var '(height . (text-pixels . 900))) (prot-emacs-add-to-list var '(scroll-bar-width . 12))) '(default-frame-alist initial-frame-alist))) (setq frame-resize-pixelwise t frame-inhibit-implied-resize t use-dialog-box t ; only for mouse events, which I seldom use use-file-dialog nil inhibit-splash-screen t inhibit-startup-screen t inhibit-x-resources t inhibit-startup-echo-area-message user-login-name ; read the docstring inhibit-startup-buffer-menu t) ;; I do not use those graphical elements by default, but I do enable ;; them from time-to-time for testing purposes or to demonstrate ;; something. (menu-bar-mode -1) (scroll-bar-mode -1) (tool-bar-mode -1) ;; Temporarily increase the garbage collection threshold. These ;; changes help shave off about half a second of startup time. (defvar prot-emacs--gc-cons-threshold gc-cons-threshold) (setq gc-cons-threshold most-positive-fixnum) ;; Same idea as above for the `file-name-handler-alist'. (defvar prot-emacs--file-name-handler-alist file-name-handler-alist) (setq file-name-handler-alist nil) ;; Same idea as above for the `vc-handled-backends'. (defvar prot-emacs--vc-handled-backends vc-handled-backends) (setq vc-handled-backends nil) (add-hook 'emacs-startup-hook (lambda () (setq gc-cons-threshold prot-emacs--gc-cons-threshold file-name-handler-alist prot-emacs--file-name-handler-alist vc-handled-backends prot-emacs--vc-handled-backends))) ;; Initialise installed packages at this early stage, by using the ;; available cache. I had tried a setup with this set to nil in the ;; early-init.el, but (i) it ended up being slower and (ii) various ;; package commands, like `describe-package', did not have an index of ;; packages to work with, requiring a `package-refresh-contents'. (setq package-enable-at-startup t) ;;;; General theme code (defun prot-emacs-theme-gsettings-dark-p () "Return non-nil if gsettings (GNOME) has a dark theme. Return nil if the DESKTOP_SESSION is either bspwm or herbstluftwm, per the configuration of my dotfiles. Also check the `delight' shell script." (prot-emacs-with-desktop-session (string-match-p "dark" (shell-command-to-string "gsettings get org.gnome.desktop.interface color-scheme")))) (defun prot-emacs-theme-twm-dark-p () "Return non-nil if my custom setup has a dark theme. I place a file in ~/.config/prot-xtwm-active-theme which contains a single word describing my system-wide theme. This is part of my dotfiles. Check my `delight' shell script for more." (when-let ((file "~/.config/prot-xtwm-active-theme") ((file-exists-p file))) (string-match-p "dark" (with-temp-buffer (insert-file-contents file) (buffer-substring-no-properties (point-min) (point-max)))))) (defun prot-emacs-theme-environment-dark-p () "Return non-nil if environment theme is dark." (or (prot-emacs-theme-twm-dark-p) (prot-emacs-theme-gsettings-dark-p))) (defun prot-emacs-re-enable-frame-theme (_frame) "Re-enable active theme, if any, upon FRAME creation. Add this to `after-make-frame-functions' so that new frames do not retain the generic background set by the function `prot-emacs-avoid-initial-flash-of-light'." (when-let ((theme (car custom-enabled-themes))) (enable-theme theme))) ;; NOTE 2023-02-05: The reason the following works is because (i) the ;; `mode-line-format' is specified again and (ii) the ;; `prot-emacs-theme-gsettings-dark-p' will load a dark theme. (defun prot-emacs-avoid-initial-flash-of-light () "Avoid flash of light when starting Emacs, if needed. New frames are instructed to call `prot-emacs-re-enable-frame-theme'." (when (prot-emacs-theme-environment-dark-p) (setq mode-line-format nil) (set-face-attribute 'default nil :background "#000000" :foreground "#ffffff") (set-face-attribute 'mode-line nil :background "#000000" :foreground "#ffffff" :box 'unspecified) (add-hook 'after-make-frame-functions #'prot-emacs-re-enable-frame-theme))) (prot-emacs-avoid-initial-flash-of-light) (add-hook 'after-init-hook (lambda () (set-frame-name "home"))) ;;; early-init.el ends here
4.2. init.el
This is where I define the Lisp macros used in my setup and load all the invidiual modules.
;;; init.el --- Personal configuration file -*- lexical-binding: t -*- ;; Copyright (c) 2019-2023 Protesilaos Stavrou <info@protesilaos.com> ;; Author: Protesilaos Stavrou <info@protesilaos.com> ;; URL: https://protesilaos.com/emacs/dotemacs ;; Version: 0.1.0 ;; Package-Requires: ((emacs "30.1")) ;; This file is NOT part of GNU Emacs. ;; This file is free software: you can redistribute it and/or modify it ;; under the terms of the GNU General Public License as published by the ;; Free Software Foundation, either version 3 of the License, or (at ;; your option) any later version. ;; ;; This file is distributed in the hope that it will be useful, but ;; WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;; General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with this file. If not, see <https://www.gnu.org/licenses/>. ;;; Commentary: ;; See my dotfiles: <https://git.sr.ht/~protesilaos/dotfiles> ;;; Code: (defgroup prot-emacs nil "User options for my dotemacs." :group 'file) ;; For those who use my dotfiles and need an easy way to write their ;; own extras on top of what I already load: search below for the files ;; prot-emacs-pre-custom.el and prot-emacs-post-custom.el (defcustom prot-emacs-load-theme-family 'modus "Set of themes to load. Valid values are the symbols `ef', `modus', and `standard', which reference the `ef-themes', `modus-themes', and `standard-themes', respectively. A nil value does not load any of the above (use Emacs without a theme). This user option must be set in the `prot-emacs-pre-custom.el' file. If that file exists in the Emacs directory, it is loaded before all other modules of my setup." :group 'prot-emacs :type '(choice :tag "Set of themes to load" :value modus (const :tag "The `ef-themes' module" ef) (const :tag "The `modus-themes' module" modus) (const :tag "The `standard-themes' module" standard) (const :tag "Do not load a theme module" nil))) (defcustom prot-emacs-completion-ui 'vertico "Choose minibuffer completion UI between `mct' or `vertico'." :group 'prot-emacs :type '(choice :tag "Minibuffer user interface" (const :tag "The `mct' module" mct) (const :tag "The `vertico' module" vertico))) (defcustom prot-emacs-omit-packages nil "List of package names to not load. This instructs the relevant macros to not `require' the given package. In the case of `prot-emacs-elpa-package', the package will not be installed if it is not already available on the system. This user option must be set in the `prot-emacs-pre-custom.el' file. If that file exists in the Emacs directory, it is loaded before all other modules of my setup." :group 'prot-emacs :type '(repeat symbol)) ;; Some basic settings (setq frame-title-format '("%b")) (setq ring-bell-function 'ignore) (setq use-short-answers t) (setq native-comp-async-report-warnings-errors 'silent) ; Emacs 28 with native compilation (setq native-compile-prune-cache t) ; Emacs 29 (setq make-backup-files nil) (setq backup-inhibited nil) ; Not sure if needed, given `make-backup-files' (setq create-lockfiles nil) ;; Disable the damn thing by making it disposable. (setq custom-file (make-temp-file "emacs-custom-")) ;; There is also the greek-postfix style. This is for inserting ;; accents. I am used to the standard use of a prefix. (setq default-input-method "greek") ;; Enable these (dolist (c '(narrow-to-region narrow-to-page upcase-region downcase-region)) (put c 'disabled nil)) ;; And disable these (dolist (c '(eshell project-eshell overwrite-mode iconify-frame diary)) (put c 'disabled t)) ;; Always start with *scratch* (setq initial-buffer-choice t) ;;;; Packages (dolist (path '("prot-lisp" "prot-emacs-modules")) (add-to-list 'load-path (locate-user-emacs-file path))) (require 'package) (with-eval-after-load 'project-vc (setq package-vc-register-as-project nil)) ; Emacs 30 (add-hook 'package-menu-mode-hook #'hl-line-mode) ;; Also read: <https://protesilaos.com/codelog/2022-05-13-emacs-elpa-devel/> (setq package-archives '(("gnu-elpa" . "https://elpa.gnu.org/packages/") ("gnu-elpa-devel" . "https://elpa.gnu.org/devel/") ("nongnu" . "https://elpa.nongnu.org/nongnu/") ("melpa" . "https://melpa.org/packages/"))) ;; Highest number gets priority (what is not mentioned has priority 0) (setq package-archive-priorities '(("gnu-elpa" . 3) ("melpa" . 2) ("nongnu" . 1))) ;; I want to use my own packages from specific repositories. All ;; others will rely on `package-archive-priorities'. I do this to ;; test that the packaged version works as intended. (defvar prot-emacs-my-packages '(agitate altcaps beframe cursory denote dired-preview ef-themes fontaine lin logos mct modus-themes notmuch-indicator pulsar spacious-padding standard-themes substitute sxhkdrc-mode tmr) "List of symbols representing the packages I develop/maintain.") (setq package-pinned-packages `(,@(mapcar (lambda (package) (cons package "gnu-elpa-devel")) prot-emacs-my-packages))) (setq custom-safe-themes t) (defun prot-emacs-package-install (package &optional method) "Install PACKAGE with optional METHOD. If METHOD is nil or the `builtin' symbol, PACKAGE is not installed as it is considered part of Emacs. If METHOD is a string, it must be a URL pointing to the version controlled repository of PACKAGE. Installation is done with `package-vc-install'. If METHOD is a quoted list, it must have a form accepted by `package-vc-install' such as: \\='(denote :url \"https://git.sr.ht/~protesilaos/denote\" :branch \"main\") If METHOD is any other non-nil value, install PACKAGE using `package-install'." (unless (or (eq method 'builtin) (null method)) (unless (package-installed-p package) (when (or (stringp method) (listp method)) (package-vc-install method)) (unless package-archive-contents (package-refresh-contents)) (package-install package)))) (defvar prot-emacs-loaded-packages nil) (defmacro prot-emacs-package (package &rest body) "Require PACKAGE with BODY configurations. PACKAGE is an unquoted symbol that is passed to `require'. It thus conforms with `featurep'. BODY consists of ordinary Lisp expressions. There are, nevertheless, two unquoted plists that are treated specially: 1. (:install METHOD) 2. (:delay NUMBER) These plists can be anywhere in BODY and are not part of its final expansion. The :install property is the argument passed to `prot-emacs-package-install' and has the meaning of METHOD described therein. The :delay property makes the evaluation of PACKAGE with the expanded BODY happen with `run-with-timer'. Also see `prot-emacs-configure'." (declare (indent 1)) (unless (memq package prot-emacs-omit-packages) (let (install delay) (dolist (element body) (when (plistp element) (pcase (car element) (:install (setq install (cdr element) body (delq element body))) (:delay (setq delay (cadr element) body (delq element body)))))) (let ((common `(,(when install `(prot-emacs-package-install ',package ,@install)) (require ',package) (add-to-list 'prot-emacs-loaded-packages ',package) ,@body ;; (message "Prot Emacs loaded package: %s" ',package) ))) (cond ((featurep package) `(progn ,@body)) (delay `(run-with-timer ,delay nil (lambda () ,@(delq nil common)))) (t `(progn ,@(delq nil common)))))))) ;; Samples of `prot-emacs-package' (expand them with `pp-macroexpand-last-sexp'). ;; (prot-emacs-package denote ;; (setq denote-directory "path/to/dir") ;; (define-key global-map (kbd "C-c n") #'denote) ;; (:install '(denote . (:url "https://git.sr.ht/~protesilaos/denote" :branch "main"))) ;; (:delay 5) ;; (setq denote-file-type nil)) ;; ;; (prot-emacs-package denote ;; (setq denote-directory "path/to/dir") ;; (define-key global-map (kbd "C-c n") #'denote) ;; (:install "https://git.sr.ht/~protesilaos/denote") ;; (:delay 5) ;; (setq denote-file-type nil)) ;; ;; (prot-emacs-package denote ;; (:delay 5) ;; (setq denote-directory "path/to/dir") ;; (define-key global-map (kbd "C-c n") #'denote) ;; (:install "https://git.sr.ht/~protesilaos/denote") ;; (setq denote-file-type nil)) ;; ;; (prot-emacs-package denote ;; (:install "https://git.sr.ht/~protesilaos/denote") ;; (:delay 5) ;; (setq denote-directory "path/to/dir") ;; (define-key global-map (kbd "C-c n") #'denote) ;; (setq denote-file-type nil)) ;; ;; (prot-emacs-package denote ;; (:delay 5) ;; (setq denote-directory "path/to/dir") ;; (define-key global-map (kbd "C-c n") #'denote) ;; (setq denote-file-type nil)) ;; ;; (prot-emacs-package denote ;; (setq denote-directory "path/to/dir") ;; (define-key global-map (kbd "C-c n") #'denote) ;; (setq denote-file-type nil)) (defmacro prot-emacs-configure (&rest body) "Evaluate BODY as a `progn'. BODY consists of ordinary Lisp expressions. The sole exception is an unquoted plist of the form (:delay NUMBER) which evaluates BODY with NUMBER seconds of `run-with-timer'. Note that `prot-emacs-configure' does not try to autoload anything. Use it only for forms that evaluate regardless. Also see `prot-emacs-package'." (declare (indent 0)) (let (delay) (dolist (element body) (when (plistp element) (pcase (car element) (:delay (setq delay (cadr element) body (delq element body)))))) (if delay `(run-with-timer ,delay nil (lambda () ,@body)) `(progn ,@body)))) (defmacro prot-emacs-keybind (keymap &rest definitions) "Expand key binding DEFINITIONS for the given KEYMAP. DEFINITIONS is a sequence of string and command pairs." (declare (indent 1)) (unless (zerop (% (length definitions) 2)) (error "Uneven number of key+command pairs")) (let ((keys (seq-filter #'stringp definitions)) ;; We do accept nil as a definition: it unsets the given key. (commands (seq-remove #'stringp definitions))) `(when-let (((keymapp ,keymap)) (map ,keymap)) ,@(mapcar (lambda (pair) (unless (and (null (car pair)) (null (cdr pair))) `(define-key map (kbd ,(car pair)) ,(cdr pair)))) (cl-mapcar #'cons keys commands))))) ;; Sample of `prot-emacs-keybind' ;; (prot-emacs-keybind global-map ;; "C-z" nil ;; "C-x b" #'switch-to-buffer ;; "C-x C-c" nil ;; "C-x k" #'kill-buffer) (defmacro prot-emacs-abbrev (table &rest definitions) "Expand abbrev DEFINITIONS for the given TABLE. DEFINITIONS is a sequence of string pairs mapping the abbreviation to its expansion." (declare (indent 1)) (unless (zerop (% (length definitions) 2)) (error "Uneven number of key+command pairs")) `(when-let (((abbrev-table-p ,table)) (table ,table)) ,@(mapcar (lambda (pair) (when-let ((abbrev (car pair)) (expansion (cadr pair))) `(define-abbrev table ,abbrev ,expansion))) (seq-split definitions 2)))) (defun prot-emacs-return-loaded-packages () "Return a list of all loaded packages. Here packages include both `prot-emacs-loaded-packages' and `package-activated-list'. The latter only covers what is found in the `package-archives', whereas the former is for anything that is expanded with the `prot-emacs-package' macro." (delete-dups (append prot-emacs-loaded-packages package-activated-list))) (defvar prot-emacs-package-form-regexp "^(\\(prot-emacs-package\\|prot-emacs-keybind\\|prot-emacs-abbrev\\|require\\) +'?\\([0-9a-zA-Z-]+\\)" "Regexp to add packages to `lisp-imenu-generic-expression'.") (eval-after-load 'lisp-mode `(add-to-list 'lisp-imenu-generic-expression (list "Packages" ,prot-emacs-package-form-regexp 2))) (defconst prot-emacs-font-lock-keywords '(("(\\(prot-emacs-package\\)\\_>[ \t']*\\(\\(?:\\sw\\|\\s_\\)+\\)?" (2 font-lock-constant-face nil t)) ("(\\(prot-emacs-\\(keybind\\|abbrev\\)\\)\\_>[ \t']*\\(\\(\\sw\\|\\s_\\)+\\)?" (3 font-lock-variable-name-face nil t)))) (font-lock-add-keywords 'emacs-lisp-mode prot-emacs-font-lock-keywords) ;; For those who use my dotfiles and need an easy way to write their ;; own extras on top of what I already load. The file must exist at ;; ~/.emacs.d/prot-emacs-pre-custom.el ;; ;; The purpose of this file is for the user to define their ;; preferences BEFORE loading any of the modules. For example, the ;; user option `prot-emacs-omit-packages' lets the user specify which ;; packages not to load. Search for all `defcustom' forms in this ;; file for other obvious customisations. (load (locate-user-emacs-file "prot-emacs-pre-custom.el") :no-error :no-message) (require 'prot-emacs-essentials) (pcase prot-emacs-load-theme-family ('ef (require 'prot-emacs-ef-themes)) ('modus (require 'prot-emacs-modus-themes)) ('standard (require 'prot-emacs-standard-themes))) (require 'prot-emacs-theme-extras) (require 'prot-emacs-font) (require 'prot-emacs-modeline) (require 'prot-emacs-completion-common) (pcase prot-emacs-completion-ui ('mct (require 'prot-emacs-completion-mct)) ('vertico (require 'prot-emacs-completion-vertico))) (require 'prot-emacs-search) (require 'prot-emacs-dired) (require 'prot-emacs-window) (require 'prot-emacs-git) ; git, diff, and related (require 'prot-emacs-write) ; denote, logos, etc. (require 'prot-emacs-org) ; org, calendar, appt (require 'prot-emacs-langs) (require 'prot-emacs-email) (when (executable-find "notmuch") (require 'prot-emacs-email-notmuch)) (require 'prot-emacs-web) ; eww, elfeed, rcirc (setq safe-local-variable-values '((org-hide-leading-stars . t) (org-hide-macro-markers . t))) ;; For those who use my dotfiles and need an easy way to write their ;; own extras on top of what I already load. The file must exist at ;; ~/.emacs.d/user-emacs.el OR ~/.emacs.d/prot-emacs-post-custom.el ;; ;; The purpose of the "post customisations" is to make tweaks to what ;; I already define, such as to change the default theme. See above ;; for the `prot-emacs-pre-custom.el' to make changes BEFORE loading ;; any of my other configurations. (load (locate-user-emacs-file "prot-emacs-post-custom.el") :no-error :no-message) ;;; init.el ends here