1. Installation
This whole Emacs configuration, including the configuration file and the included packages is a Nix derivation. By installing Emacs through Nix, the editor, its packages and the configuration are bundled together in a single bundle. This allows for quick installs and reproducable builds.
As an example, to try out this Emacs configuration without affecting the rest of your system, run the following command. This downloads and compiles Emacs, including packages and the configuration, and starts the resulting Emacs.app.
nix run github:jeffkreeftmeijer/.emacs.d
1.1. Building Emacs from Git with Nix
Instead of using the stable Emacs from Nixpkgs, this configuration uses emacs-overlay to build Emacs from its master branch. The overlay updates daily, but this configuration only updates sporadically, when there’s reason to do so, to keep everything as stable as possible.
I’m inclined to use a stable version of Emacs instead of building from Git, as I’m not specifically looking to be on the bleeding edge. However, newly added features tend to pull me in.
Currently, there are two reasons I’m currently running on a prerelease version:
- Emacs master updated its included version of the modus-themes, including the tinted variants of modus-vivendi and modus-operandi, which are my preferred themes. Running Emacs master therefor requires one less dependency.
- Completion-preview.el, a fish-like completion-at-point package, was merged into master in e82d807a2845673e2d55a27915661b2f1374b89a.
To build a Nix derivation that intalls Emacs from Git using Emacs-overlay, import nixpkgs
, and then apply the overlay from a tarball.
Then, return pkgs.emacs-git
:
{ pkgs ? import <nixpkgs> { overlays = [ (import (builtins.fetchTarball { url = https://github.com/nix-community/emacs-overlay/archive/f7fcac1403356fd09e2320bc3d61ccefe36c1b91.tar.gz; })) ]; } }: pkgs.emacs-git
In this example, the version of emacs-overlay (and thus Emacs itself) is locked to a specific version.
To use the latest version, replace the revision hash with a branch name like master
.
Assuming the derivation is saved to a file named emacs-git.nix
, it can be built through nix build
:
1.2. Enabling XWidgets in Emacs on macOS with Nix
Nix disables the withXwidgets
option for Emacs on macOS, so simply enabling it won’t work yet:
{ pkgs ? import <nixpkgs> { overlays = [ (import (builtins.fetchTarball { url = https://github.com/nix-community/emacs-overlay/archive/f7fcac1403356fd09e2320bc3d61ccefe36c1b91.tar.gz; })) ]; } }: pkgs.emacs-git.overrideAttrs(old: { withXwidgets = true; })
In the meantime, circumvent Nix’s option by manually adding the build flag. As expected, enabling XWidgets also requires the WebKit framework as a build input:
{ pkgs ? import <nixpkgs> { overlays = [ (import (builtins.fetchTarball { url = https://github.com/nix-community/emacs-overlay/archive/f7fcac1403356fd09e2320bc3d61ccefe36c1b91.tar.gz; })) ]; } }: pkgs.emacs-git.overrideAttrs(old: { buildInputs = old.buildInputs ++ [ pkgs.darwin.apple_sdk.frameworks.WebKit ]; configureFlags = old.configureFlags ++ ["--with-xwidgets"]; })
1.3. Applying Emacs Plus patches
Emacs Plus is a Homebrew formula to build Emacs on macOS, which applies a couple of patches while building. First, download the patches for the correct Emacs version. In this case, get the patches for Emacs 30:
curl https://raw.githubusercontent.com/d12frosted/homebrew-emacs-plus/master/patches/emacs-30/system-appearance.patch -o patches/system-appearance.patch curl https://raw.githubusercontent.com/d12frosted/homebrew-emacs-plus/master/patches/emacs-30/round-undecorated-frame.patch -o patches/round-undecorated-frame.patch curl https://raw.githubusercontent.com/d12frosted/homebrew-emacs-plus/master/patches/emacs-30/poll.patch -o patches/poll.patch curl https://raw.githubusercontent.com/d12frosted/homebrew-emacs-plus/master/patches/emacs-28/fix-window-role.patch -o patches/fix-window-role.patch
Then, override the attributes in pkgs.emacs-git
when using emacs-overlay—or pkgs.emacs
when building Emacs from Nixpkgs—to add all path files to the package’s patches list:
{ pkgs ? import <nixpkgs> { overlays = [ (import (builtins.fetchTarball { url = https://github.com/nix-community/emacs-overlay/archive/f7fcac1403356fd09e2320bc3d61ccefe36c1b91.tar.gz; })) ]; } }: pkgs.emacs-git.overrideAttrs(old: { patches = old.patches ++ [ ./patches/system-appearance.patch ./patches/round-undecorated-frame.patch ./patches/poll.patch ./patches/fix-window-role.patch ]; })
Assuming the derivation is saved to a file named emacs-patched.nix
, it can be built through nix build
:
nix build --file emacs-patched.nix open /result/Applications/Emacs.app
1.4. Emacs with bundled configuration
The emacsWithPackagesFromUsePackage
function parses configuration files in search of packages to bundle with Emacs.
For example, to package Emacs with Evil and enable evil-mode
on startup, add a use-package
statement as the emacs configuration:
{ pkgs ? import <nixpkgs> { overlays = [ (import (builtins.fetchTarball { url = https://github.com/nix-community/emacs-overlay/archive/f7fcac1403356fd09e2320bc3d61ccefe36c1b91.tar.gz; })) ]; } }: pkgs.emacsWithPackagesFromUsePackage { package = pkgs.emacs-git; config = '' (use-package evil :ensure t :init (evil-mode 1)) ''; defaultInitFile = true; }
Assuming the derivation is saved to a file named emacs-enil.nix
, it can be built through nix build
:
nix build --file emacs-evil.nix open /result/Applications/Emacs.app
1.5. Configured Emacs
By combining the features in Emacs overlay, this configuration produces configured Emacs, a version of Emacs with macOS-specific patches applied, XWidgets enabled, packages installed and a full configuration loaded.
The included configuration file is default.el
, which is generated from the rest of this configuration.
{ pkgs ? import <nixpkgs> { overlays = [ (import (builtins.fetchTarball { url = https://github.com/nix-community/emacs-overlay/archive/f7fcac1403356fd09e2320bc3d61ccefe36c1b91.tar.gz; })) ]; } }: pkgs.emacsWithPackagesFromUsePackage { package = ( pkgs.emacs-git.overrideAttrs(old: { patches = old.patches ++ [ ./patches/system-appearance.patch ./patches/round-undecorated-frame.patch ./patches/poll.patch ./patches/fix-window-role.patch ]; buildInputs = old.buildInputs ++ [ pkgs.darwin.apple_sdk.frameworks.WebKit ]; configureFlags = old.configureFlags ++ ["--with-xwidgets"]; }) ); config = ./default.el; defaultInitFile = true; }
2. Appearance
2.1. Frames
Disable the scroll bar, the tool bar, and the menu bar:
(scroll-bar-mode -1) (tool-bar-mode -1) (menu-bar-mode -1)
2.2. Fonts
Use Iosevka as a monospace font (fixed in Emacs lingo), and Iosevka’s “Aile” variant as a (quasi-)proportional font (variable-pitch in Emacs lingo).
Both variants are used with their regular weights, expanded widths, and a height of 150 (15 points × 10):
(defun jk/set-face-font (face family) (set-face-attribute face nil :family family :weight 'regular :width 'expanded :height 150)) (jk/set-face-font 'default "Iosevka") (jk/set-face-font 'fixed-pitch "Iosevka") (jk/set-face-font 'variable-pitch "Iosevka Aile")
The face-font-family-alternatives
variable provides fallback fonts if the preferred fonts aren’t available.
This produces a font list akin to CSS font-families, starting with the preferred font and falling back to an option that is most likely to be available on any system.
Having a list of fallback fonts like this removes the need to explicitly depend on fonts being available.
This configuration falls back to Apple’s SF Mono and SF Pro if the Iosevka fonts aren’t available. Since the Apple fonts need to be downloaded explicitly, they aren’t more likely to be there than the Iosevka ones, but they’re included as they were the previous favorite.
If the SF fonts aren’t available, the fixed font falls back to Menlo before the default monospace font (which is most likely Courier). The variable pitch font falls back to SF Pro, Helvetica, and finally Arial:
(custom-set-variables '(face-font-family-alternatives '(("Iosevka" "SF Mono" "Menlo" "monospace") ("Iosevka Aile" "SF Pro" "Helvetica" "Arial"))))
2.3. Variable pitch
To use proportional fonts (as opposed to monospaced fonts) for non-code text, enable variable-pitch-mode
for selected modes.
While this mode is enabled, the default
font face inherits from variable-pitch
instead of fixed-pitch
.
An often-recommended approach is to hook into text-mode
, which is the major mode most text-based modes inherit from:
(add-hook 'text-mode-hook #'variable-pitch-mode))
Doing so automatically enables variable-pitch-mode
thenever text-mode
is enabled.
This works, but it’s a bit too eager for my liking.
The above configuration enables variable-pitch-mode
when editing Org files, but also when writing commit messages and editing YAML files.
I consider text in the latter two as code, so I’d prefer to have those displayed in a monospace font.
Instead of hooking into text-mode
, explicitly select the modes to use proportional fonts in Org and Markdown mode:
(add-hook 'org-mode-hook #'variable-pitch-mode) (add-hook 'markdown-mode-hook #'variable-pitch-mode)
2.4. Themes
The Modus themes are a set of beautiful and customizable themes, which are shipped with Emacs since version 28.
The modus themes consist of two types: Modus Operandi is a light theme, and Modus Vivendi is its dark counterpart. The tinted variants shift the background colors from white and black to a more pleasant light ochre and dark blue.
When using the version of the Modus themes that’s included in Emacs, the themes need to be explicitly required using require-theme
:
(require-theme 'modus-themes)
To select modus-operandi-tinted
as the default theme, load it with the load-theme
function:
(load-theme 'modus-operandi-tinted)
An interactive function named modus-themes-toggle
switches between the light and dark themes.
By default, the function switches between the non-tinted versions, but that can be overwritten to use the tinted versions through the modus-themes-to-toggle
variable:
(setq modus-themes-to-toggle '(modus-operandi-tinted modus-vivendi-tinted))
2.4.1. Switching between dark and light mode
Auto-dark automatically switches between dark and light themes based on the operating system’s appearance.
(auto-dark-mode 1)
It uses the wombat and leuven themes by default, but these are configured to use the modus themes with the auto-dark-light-theme
and auto-dark-dark-theme
variables.
(setq (auto-dark-light-theme 'modus-operandi-tinted)
(auto-dark-dark-theme 'modus-vivendi-tinted))
With auto-dark in place, Emacs’ theme can be switched by toggling the system-wide dark mode instead of using modus-themes-toggle
.
The jk/dark
and jk/light
functions run an apple script to turn dark mode on and off from Emacs:
(defun jk/dark () "Switch to macOS' dark appearance." (interactive) (do-applescript "tell application \"System Events\" tell appearance preferences set dark mode to true end tell end tell")) (defun jk/light () "Switch to macOS' light appearance." (interactive) (do-applescript "tell application \"System Events\" tell appearance preferences set dark mode to false end tell end tell"))
2.4.2. Customization
The Modus themes can optionally inherit from the fixed-pitch
face for some faces, which allows for turning on variable-pitch-mode
while keeping some text monospaced.
To turn it on, set modus-themes-mixed-fonts
, but make sure it’s set before loading one of the modus themes:
(setq modus-themes-mixed-fonts t)
The Modus themes come with the option to use italic and bold constructs, which is turned off by default. Enabling produces italic type for comments and contextual information, and bold type in syntax highlighting.
(setq
modus-themes-italic-constructs t
modus-themes-bold-constructs t)
Note that any configuration options to the themes themselves need to happen before the theme is loaded, or the theme needs to be reloaded through load-theme
after setting the customizations.
2.5. Layout
The spacious-padding package adds spacing around windows and frames, as well as padding the mode line.
Turn on spacious-padding-mode
to add spacing around windows and frames:
(spacious-padding-mode 1)
Turn on spacious-padding-subtile-mode-line
for a more subtile mode line:
(setq spacious-padding-subtle-mode-line t)
3. Modal editing
3.1. Evil mode
Emacs is the best Vim emulator, and Evil is the best Vim mode.
After installing Evil, turn on evil-mode
globally:
Instead of enabling Evil’s gloval evil-mode
hook, turn it on per buffer.
By hooking into both prog-mode and text-mode, Evil mode is only turned on for programming and text editing buffers.
(add-hook 'prog-mode-hook 'turn-on-evil-mode) (add-hook 'text-mode-hook 'turn-on-evil-mode)
3.2. Evil-commentary
Evil-commentary is an Evil port of vim-commentary which adds key bindings to call Emacs’ built in comment-or-uncomment-region
function.
Turn it on by calling evil-commentary-mode
:
(evil-commentary-mode 1)
3.3. Cursors
An example of an essential difference between Emacs and Vim is how they handle the location of the cursor (named point in Emacs). In Vim, the cursor is on a character, while Emacs’ point is before it. In Evil mode, the cursor changes between a box in “normal mode” to a bar in “insert mode”. Because Emacs is always in a kind of insert mode, make the cursor a bar:
(setq-default cursor-type 'bar)
4. Completion
4.1. Vertical completion
Vertico is a vertical completion library, based on Emacs’ default completion system.
(vertico-mode 1)
4.2. Contextual information
Marginalia adds extra contextual information to minibuffer completions.
For example, besides just showing command names when executing M-x
, the package adds a description of the command and the key binding.
(marginalia-mode 1)
4.4. Pattern matching
Orderless is a completion style that divides the search pattern in space-separated components, and matches regardless of their order.
After installing it, add it as a completion style by setting completion-styles
:
(setq completion-styles '(orderless basic))
4.5. Minibuffer actions
Embark adds actions to minibuffer results.
For example, when switching buffers with switch-to-buffer
or consult-buffer
, pressing C-.
opens Embark’s list of key bindings.
From there, you can act on results in the minibuffer.
In this exampke, pressing k
kills the currently selected buffer.
(global-set-key (kbd "C-.") 'embark-act)
4.6. Minibuffer history
Emacs’ savehist
feature saves minibuffer history to ~/emacs.d/history
.
The history is then used to order vertical completion suggestions.
(savehist-mode 1)
4.7. Completion at point
Emacs 30 includes completion-preview.el
, since e82d807a2845673e2d55a27915661b2f1374b89a, which adds grayed-out completion previews while typing, akin to the autocomplete in the Fish shell.
(global-completion-preview-mode 1)
5. Development
5.1. Major modes
5.1.1. Treesitter
The treesit-auto package automatically installs and uses the tree-sitter equivalent of installed major modes.
For example, it automatically installs and uses rust-ts-mode
when a Rust file is opened and rust-mode
is installed.
To turn it on globally, enable global-treesit-auto-mode
:
(global-treesit-auto-mode 1)
To automatically install missing major modes, enable treesit-auto-install
.
To have the package prompt before installing, set the variable to 'prompt
:
(custom-set-variables '(treesit-auto-install 'prompt))
5.1.2. Additional major modes
In addition to the list of already installed major modes, this configuration adds adds more when they’re needed1.
5.1.2.1. beancount-mode
Bencount-mode requires hooking up the mode manually, so enable it for each file with a .beancount
extension:
(use-package beancount :ensure t :mode ("\\.beancount\\'" . beancount-mode))
5.1.2.2. dockerfile-mode
(use-package dockerfile-mode :ensure t)
5.1.2.3. elixir-mode
(use-package elixir-mode :ensure t)
5.1.2.4. git-modes
(use-package git-modes :ensure t)
5.1.2.5. markdown-mode
There is currently no Emacs major mode for MDX, so enable Markdown-mode for files with a .mdx
extension:
(use-package markdown-mode :ensure t :mode ("\\.mdx\\'" . markdown-mode))
5.1.2.6. nix-mode
(use-package nix-mode :ensure t)
5.1.2.7. rust-mode
(use-package rust-mode :ensure t)
5.1.2.8. typescript-mode
(use-package typescript-mode :ensure t)
5.1.2.9. yaml-mode
(use-package yaml-mode :ensure t)
5.2. Environments
Programming environments set up with Nix and direnv alter the environment and available programs based on the current directory. To provide access to programs on a per-directory level, use the Emacs direnv package:
(direnv-mode 1)
5.3. Language servers
Eglot is Emacs’ built-in Language Server Protocol client.
Language servers are added through the eglot-server-programs
variable:
(add-to-list 'eglot-server-programs '((rust-ts-mode rust-mode) "rust-analyzer")) (add-to-list 'eglot-server-programs '((elixir-ts-mode elixir-mode) "elixir-ls")) (add-to-list 'eglot-server-programs '((nix-mode) "nixd"))
Start eglot automatically for Nix an Rust files:
(add-hook 'nix-mode #'eglot-ensure) (add-hook 'rust-mode #'eglot-ensure) (add-hook 'rust-ts-mode #'eglot-ensure)
5.3.1. Automatically format files on save in Eglot-enabled buffers
The eglot-format-buffer
function doesn’t check if Eglot is running in the current buffer.
This means hooking using it as a global after-save-hook
produces errors in the echo area whenever a file is saved while Eglot isn’t enabled:
(jsonrpc-error "No current JSON-RPC connection" (jsonrpc-error-code . -32603) (jsonrpc-error-message . "No current JSON-RPC connection"))
To remedy this, add a function that formats only when Eglot is enabled.
(defun jk/maybe-format-buffer () (when (bound-and-true-p eglot-managed-p) (eglot-format-buffer)))
This function is then added as a global after-save-hook
.
(add-hook 'after-save-hook 'jk/maybe-format-buffer)
Now, with the hook enabled, any Eglot-enabled buffer is formatted automatically on save.
6. Version control
Magit is a user interface for Git in Emacs. Even after years of using Git from the console, it’s the quickest way to use Git, and it’s one of the most sophisticated Emacs packages.
An interesting thing about Magit is that it doesn’t have many configuration options. It doesn’t need any, as it’s a great experience out of the box.
(use-package magit :ensure t)
7. Shell
7.1. Terminal emulation
Use Eat (Emulate A Terminal) as a terminal emulator.
If Eat prints “garbled” text, run M-x eat-compile-terminfo
, then restart the Eat buffer.
Aside from starting the terminal emulator with M-x eat
and M-x eat-project
, Eat adds terminal emulation to Eshell with eat-eshell-mode
.
This allows Eshell to run full screen terminal applications.
(eat-eshell-mode 1)
Because Eat now handles full screen terminal applications, Eshell no longer has to run programs in a term buffer.
Therefor, the eshell-visual-commands
list can be unset.
(setq eshell-visual-commands nil)
Now, an application like top
will run in the Eshell buffer without a separate term buffer having to be opened.
7.2. History
Atuin is a cross-shell utility that stores shell history in a SQLite database. The eshell-atuin package adds support for both reading from and writing to the history from Eshell.
(eshell-atuin-mode)
To read the history in Eshell, bind the <up>
key to eshell-atuin-history
, which opens the shell history in the minibuffer.
Also unset the <down>
key, which was bound to eshell-next-input
for cycling through history in reverse:
(keymap-set eshell-hist-mode-map "<up>" 'eshell-atuin-history) (keymap-unset eshell-hist-mode-map "<down>")
By default, eshell-atuin only shows commands that completed succesfully.
To show all commands, change the eshell-atuin-search-options
variable from ("--exit" "0")
to nil
:
(setq eshell-atuin-search-options nil)
Shell history completion is different from other kinds of completion for two reasons:
- Other completion options are presented in a list from top to bottom, with the search prompt at the top.
Because
eshell-atuin-history
is opened by pressing the<up>
key and history is searched backward, the list is reversed by usingvertico-reverse
. - The command history shouldn’t be ordered, as that’s already handled by Atuin.
Instead of ordering the list again, pass
identity
as thevertico-sort-function
.
Using vertico-multiform
, which is enabled through vertico-multiform-mode
, set the above options specifically for the eshell-atuin-history
function:
(vertico-multiform-mode 1)
(setq vertico-multiform-commands
'((eshell-atuin-history
reverse
(vertico-sort-function . identity))))
8. Dired
(dirvish-override-dired-mode)
9. Org
9.1. Note-taking
I’m trying out org-node, a just-released alternative to org-roam, my current note-taking solution. Currently, this configuration uses both packages.
9.1.1. Org-node
Org-node is not on any of the package repositories yet.
This configuration doesn’t ensure the package is there, so it’s assumed it’s installed manually.
I’ve installed org-node through package-vc-install
for now.
Enable org-node by calling org-node-enable
whenever an org-mode
is enabled:
(add-hook 'org-mode-hook #'org-node-enable))
9.1.2. Org-roam
Org-roam stores notes in org-roam-directory
, which is ~/org-roam
by default.
Use ~/notes
instead:
(setq org-roam-directory (file-truename "~/notes"))
9.1.3. Org-roam-ui
Org-roam-ui is a graphical frontend for Org-roam, which displays all nodes in a graph for browsing the directory of nodes and discovering possible missing links.
9.2. Task management
Beorg is an iOS app that takes Org mode to iOS.
It includes a list of tasks named inbox that’s synced via iCloud, meaning it can be added to the agenda through org-agenda-files
.
(setq org-agenda-files '("/Users/jeff/Library/Mobile\ Documents/iCloud\~com\~appsonthemove\~beorg/Documents/org/inbox.org"))
9.3. Modern defaults for Org exports
Org files can be can be exported to other formats, like HTML.
Due to backwards compatibility constraints, however, the produced documents have an xhtml-strict
doctype with syntax to match.
Luckily, Org’s exporters are endlessly configurable, and include support for more modern configurations.
9.3.1. Smart quotes
Automatically convert single and double quotes to their curly equivalents, depending on the document language.
(setq org-export-with-smart-quotes t)
9.3.2. Entities
Disable entities, like using “
instead of “ in HTML.
This option only works for entities included in the document, not the entities added through smart quotes.
(setq org-export-with-entities nil)
9.3.3. Headline levels
Instead of 3, set the maximum headline level to 5. This matches the HTML standard of having six headline levels, when counting the document title as the first, leaving five.
(setq org-export-headline-levels 5)
9.3.4. Table of contents and section numbers
Disable both the table of contents and section numbers, as they’re easily turned on when needed, not needed for most exports, and not present in the source documents.
(setq
org-export-with-toc nil
org-export-section-numbers nil)
9.3.5. HTML 5
Aside from replacing the doctype in the document, setting org-html-doctype
to html5 has modernizing effects on the output file.
For example, it uses the charset
attribute (as opposed to http-equiv
) to set the character set, it drops the XML declaration from the header of the document, it switches to the HTML5 validator for the footer (which is then disabled later), and disables HTML table attributes2.
Setting the doctype instantly transports the document from the start of the millenium to last decade.
To enable the HTML5 doctype , set the org-html-doctype
variable:
(setq org-html-doctype "html5")
9.3.7. Containers
Aside from the modern elements already enabled by the HTML5 doctype and org-html-html5-fancy
, Org allows for more customizations to its HTML exports.
Use org-html-container-element
and org-html-divs
to replace some of the standard <div>
elements with HTML 5 alternatives:
- Use the
<section>
element instead of the main section<div>
elements - Use the
<header>
element to wrap document preambles - Use the
<main>
element to wrap the document’s main section - Use the
<footer>
element to wrap document postambles
(setq org-html-container-element "section" org-html-divs '((preamble "header" "preamble") (content "main" "content") (postamble "footer" "postamble")))
9.3.8. Summary
To configure Org mode’s HTML exporter to output HTML 5 with modern elements, set the following configuration.
(setq org-export-with-smart-quotes t org-export-with-entities nil org-export-headline-levels 5 org-export-with-toc nil org-export-section-numbers nil org-html-doctype "html5" org-html-html5-fancy t org-html-container-element "section" org-html-divs '((preamble "header" "preamble") (content "main" "content") (postamble "footer" "postamble")))
When using use-package
for configuration, hook into the ox-org
package an use the :custom
keyword.
(use-package ox-org :custom org-export-with-smart-quotes t org-export-with-entities nil org-export-headline-levels 5 org-export-with-toc nil org-export-section-numbers nil org-html-doctype "html5" org-html-html5-fancy t org-html-container-element "section" org-html-divs '((preamble "header" "preamble") (content "main" "content") (postamble "footer" "postamble")))
9.4. Source code
One of Org’s most impressive features is source code evalutation through its Library of Babel. Babel can both evaluate (run a source code block from within an Org document) and extract (take a source code block from an Org document and place it in another file) code.
9.4.1. Evaluation
By default, Org only evaluates Emacs Lisp code, but other languages can be added via org-babel-load-languages
:
(setq
org-babel-load-languages '((emacs-lisp . t)
(shell . t)))
9.4.2. Extraction
Org extracts each code block that has a “tangle” attribute whenever the org-babel-tangle
function is evaluated.
It’s bound do C-c C-v t
by default.
However, it’s convenient to have code blocks tangled automatically when the source document is saved.
Automatic source code tangling can be enabled per-document by adding a document header line:
# -*- eval: (add-hook 'after-save-hook #'org-babel-tangle nil t); -*-
For documents where this header can’t be added, or situations where the header hasn’t been added yet, there’s a package named org-auto-tangle.
(add-hook 'org-mode-hook #'org-auto-tangle-mode)
The org-auto-tangle package automatically extracts code blocks for every document that has the option.
To turn it on for all Org documents regardless, set
org-auto-tangle-default
:
(setq org-auto-tangle-default t)
10. Email
Use notmuch.el to read email.
11. Enhancements
This section covers general enhancements to Emacs which don’t warrant their own section.
11.1. Backups
Emacs automatically generates backups for files not stored in version control.
Instead of storing them in the files’ directories, put everything in ~/.emacs.d/backups
:
(setq backup-directory-alist `(("." . "~/.emacs.d/backups")))
11.2. Key suggestions
With which-key, Emacs shows suggestions when pausing during an incomplete keypress, which is especially useful when trying to learn Emacs’ key bindings. By default, Emacs only shows the already-typed portion of the command, which doesn’t help to find the next key to press.
(which-key-mode 1)
11.3. Projects
By default, project.el
only takes projects into account that have a .git
directory.
Use project-x to allow for projects that are not under version control, and projects nested within other projects.
Project-x is not on any of the pacakge managers, so this configuration assumes it’s installed manually for now.
Also, this configuration re-sets project-find-functions
to try project-x-try-local
before project-try-vc
to make it work for projects nested within directories under version control.
(project-x-mode 1)
(setq project-find-functions '(project-x-try-local project-try-vc))
With project-x enabled, Emacs will recognise directories with a .project
file as project directories.3
11.4. Precise scrolling
Added in Emacs 29, pixel-scroll-precision-mode
enables smooth scrolling instead of scrolling line by line.
(pixel-scroll-precision-mode 1)
11.5. Indentation
Don’t use tabs for indentation.
(indent-tabs-mode 0)
12. Benchmarking
Use benchmark-init to benchmark Emacs’ initialization. Enable benchmark-init at the top of the configuration file, before any packages are loaded.
(use-package benchmark-init :ensure t :config (add-hook 'after-init-hook 'benchmark-init/deactivate))
After starting Emacs, the benchmarking results can be examined using the benchmark-init/show-durations-tree
function.
Aside from that, append the total duration to a file named ~/.emacs.d/benchmark.csv
for future reference.
(write-region (format "%s,%s\n" (string-trim (shell-command-to-string "git --git-dir ~/emacs-config/.git rev-parse HEAD")) (benchmark-init/node-duration-adjusted benchmark-init/durations-tree)) nil "~/.emacs.d/benchmark.csv" 'append)
- I’d rather not worry about installing major modes and use a package like vim-polyglot, but I haven’t been able to find an equivalent for Emacs. ↩︎
The easiest way to find out what each of these options does is to locate where the predicate functions are called in
↩︎ox-html.el
in Org’s source code. For example, to find out what changing the doctype to HTML5 does, search fororg-html-html5-p
.Apparently,
↩︎project.el
now supports identifying projects based on a special file in its directory root. Project-x should be obsolete for this purpose, but I haven’t figured it out yet.