This org-mode file contains Patrick M. Niedzielski’s Emacs configuration, written in a Literate Programming style. The configuration targets GNU Emacs git-master and, although cross-platform, does not support any other version of GNU Emacs or any other Emacs editor (such as XEmacs).
This document is the result of declaring =.emacs= bankruptcy during 2016. At the time, my Emacs configuration was too large and unwieldy to maintain. It had become difficult to do anything but add more configuration to the end of the file; any modifications to existing configuration ran a very high risk of breaking the entire configuration file. The result was a long time lost to debugging my text editor, which could have been more productively used on my own projects. When this became too unbearable, I started anew.
To make sure that I wouldn’t have to do this again, I decided on two major pillars of my configuration file. First, and in fact primarily, I would write it in a Literate Programming style. A big part of the problem in my prior configuration was not knowing the thought process that went into old configurations. Towards the end of my old configuration file’s life, some of this was solved by good git commit messages, but there was enough cruft that make this not a good solution. Literate Programming allows me to write prose to describe my intention with a particular piece of configuration, and to link together different parts of configuration that need to be separate in Emacs Lisp, but which make sense together in my prose. Second, I would use John Wiegley’s use-package macro, which would make each piece of the configuration code itself declarative. This would free me from the minutiae of Emacs’s autoload functionality and such.
Through the years, my primary use of Emacs has shifted. In 2016, I was primarily writing C++ and Perl code. Now, though, I use Emacs for reading and writing academic works, and the little code I do write is in Haskell. I have also needed to use this configuration file different Debian machines (usually my personal machines) and Windows boxes (usually work machines). Furthermore, as I’ve learned more Emacs, the way I have used it has changed dramatically. I think it’s a testament to the above two decisions that my configuration is still easy to use and maintain, despite the quite major shifts in what it’s been intended to do.
Until 2021, my configuration was private, so I did not need to worry about whether to keep secrets like passwords in the file or not. Now, I am migrating the configuration to a public git repository, so others can see what sorts of things I have in my configuration. I have needed to do this carefully, so in the meantime this repository may not have everything that my active configuration has. The ultimate goal is to migrate entirely over to this configuration.
Copyright © 2016-2022, Patrick M. Niedzielski.
The following license applies both to this Org file and to the corresponding Emacs configuration file that can be generated from this file (as well as any other derivative works).
;; This program 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 program 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 program. If not, see
;; <http://www.gnu.org/licenses/>.
This Emacs configuration is based on Org Babel, which allows source
code blocks to live inside an org-mode document; the org-mode document
can then be woven into an output document format (such as HTML or
LaTeX) that describes the source code and tangled into an output
source file (in this case, Emacs Lisp) that can be run. Our actual
init.el
file is a simple stub that tangles this org-mode file and
then executes it.
The tangled file should have the license text and a note reminding us to modify the org-mode file instead of the tangled source file.
;;; configuration.el --- Emacs configuration file
;; This source file has been generated from an org-mode file.
;; Modifications should be performed on the org-mode file!
<<license>>
Although load times don’t matter too much for us, since we use an
Emacs daemon, we can still save a bit of time on startup by compiling
the elisp we produce. One way to do this is to tangle and compile
this org file every time we save it. We’ll have to be careful,
because if ever the .elc
file and the .org
file get out of sync,
we’ll have some hard-to-track-down bugs.
In order to tangle and compile the org file every time we save it, we
define a hook that runs every time a file is saved. If the file is
our configuration org file, we tangle it and compile it. In our
init.el
file, we load the .elc
file if it exists, and otherwise we
tangle and load the .org
file.
(defun pmn/tangle-dotfiles ()
"If the current file is this file, the code blocks are tangled."
(letrec ((org-file (expand-file-name "configuration.org" user-emacs-directory))
(el-file (concat (file-name-sans-extension org-file) ".el")))
(when (equal (buffer-file-name) org-file)
(org-babel-tangle nil el-file)
(byte-compile-file el-file))))
(add-hook 'after-save-hook #'pmn/tangle-dotfiles)
In this section, we set up the backbone of our configuration: Emacs’s
built-in package.el
, and the use-package
macro.
Most of our packages we install using package.el
, which is bundled
with Emacs 24 and later. There are four major package repositories
for Emacs packages; we use the first three listed here.
- ELPA
- The repository for packages with FSF-copyright assignment.
- Non-GNU ELPA
- The repository for GNU-sanctioned packages without copyright assignment.
- MELPA
- Contains the largest selection of pcakages and is built directly from source in the package’s VCS.
- Marmalade
- Packages are uploaded by users and so tend to be at stable (albeit old versions)
We do not use Marmalade because it often has the same packages as MELPA, only with older versions. This can sometimes lead to conflicts that we want to avoid. So, we can set up the three package repositories we plan to use.
(require 'package)
(setq package-archives '(("gnu" . "http://elpa.gnu.org/packages/")
("nongnu" . "http://elpa.gnu.org/nongnu/")
("melpa" . "https://melpa.org/packages/")))
Then, we tell Emacs to prefer ELPA over Non-GNU ELPA over MELPA. I have a lot more trust in ELPA than I do in MELPA, since the former requires explicit versioned uploads. If it is ever an issue, I can pin a specific package from MELPA. So far, though, that has never come up.
(setq package-archive-priorities '(("gnu" . 30)
("nongnu" . 20)
("melpa" . 10)))
As of [2021-09-23 ĵaŭ], I’m trying out the Non-GNU ELPA repository. I’m not sure if this will cause conflicts with MELPA or not, but for now I’m prefering to use the official non-GNU package repository where possible.
It’s very useful to keep all our installed packages in a single place,
separate from any local Lisp code we have. This gives us the option
of deleting the installed packages whenever we want to reset our Emacs
state, and keeps our Emacs configuration directory tidy. Let’s put
them in the elpa
directory under our Emacs configuration directory.
(setq package-user-dir (concat user-emacs-directory "elpa"))
It’s important for our configuration that packages are not initialized
until we have set up all the use-package
invocations that declare
what packages we want to use. To do this, we tell package.el
not to
activate any packages early on.
(setq package-enable-at-startup nil)
Finally, we start up package.el
.
(package-initialize)
Now that package.el
is set up and ready to use, we’re ready to
configure use-package
, which we use to automatically install the
packages we want and to track dependencies between them.
use-package
provides us with a macro that centralizes all the
configuration for each package, and lets us state the conditions under
which we can load the package.
Before using use-package
, though, we need to make sure it’s
installed, or install it if it’s not already! We need to bootstrap by
using package.el
to fetch and install use-package
if it’s not
locally installed. While we’re at it, we pick up the package
diminish
, which lets us control what packages are shown in the
modeline, and bind-key
, which lets us bind keys more easily. Both
of these are integrated nicely into use-package
.
(unless (package-installed-p 'use-package)
(package-refresh-contents)
(package-install 'use-package)
(package-install 'diminish))
<<use-package-config>>
(require 'use-package)
(require 'use-package-ensure)
(require 'diminish)
(require 'bind-key)
Because we can’t use use-package
itself to configure use-package
,
we’ll have a handful of loose configured options set up here at the
start. We chose to be a bit more verbose in what we output, so that
we can keep track of what is going on in the systemd journal. We also
make sure download packages by default if they aren’t already
installed (known in use-package
-speak as ensuring). Finally, we
turn off the default of adding a -hook
suffix to symbols that appear
in the :hook
section of a use-package
declaration; the default
means we can’t configure abnormal hooks in the same way as normal
hooks.
(setq use-package-verbose t
use-package-expand-minimally nil
use-package-compute-statistics t
use-package-always-ensure t
use-package-hook-name-suffix nil)
Finally, to avoid unnecessary garbage collection during start up, we’ll raise the (rather low) garbage collection and process buffering thresholds. ([2021-09-07 mar]: This seems out of place here, and I didn’t document why I chose this location in the configuration to put these two statements. It might make sense to break it out later, or at least move it somewhere else.)
(setq gc-cons-threshold (* 50 1000 1000))
(setq read-process-output-max (* 1024 1024)) ; 1mb
There are still a few packages that are not on MELPA, which I have
installed locally (as it used to be, before package.el
). I put
these packages in a subdirectory of my Emacs directory,
.config/emacs/lisp/
. For each local package we load outside of
MELPA, we will need to add a directory to the load-path
variable.
To make this easy, I add a variable called pn/local-lisp-directory
that points to the right place.
(setq pn/local-lisp-directory (concat user-emacs-directory "lisp/"))
Starting with version 28, Emacs includes functionality to compile Lisp down to native code. While there has been byte-code compilation for a long time, this still goes through the Lisp interpreter, which introduces some delay—and since Emacs is mostly single-threaded, we want to limit this delay as much as possible, so we can interact with Emacs with less frustration. Native compilation (formerly GccEmacs), lets you precompile Lisp code so it can run natively, and includes all the optimizations of the GCC backend. This has had a noticeable impact on the snappiness of my Emacs, especially when using larger packages.
Luckily, we don’t need to do much to make Emacs use native compilation. However, there is one quite unfortunate default: when packages are being asynchronously natively compiled, every warning during compilation pops up a buffer that takes away input focus from what I was doing, and sometimes screws up a carefully crafted window layout. I don’t usually care about compilation warnings in code I didn’t write, and there seems to be quite a lot of that in this transition phase to native-comp Nirvana, so I ignore warnings during these async compilations:
(setq warning-minimum-level :error)
This has made asynchronous native compilation so much smoother, and this has the benefit of showing me only actual errors that I should be concerned with.
I usually run Emacs as a server on my systems, with emacsclients connecting to the server. Let’s make sure to enable this functionality.
(require 'server)
Emacs knows about the XDG Basedirs Specification, but unfortunately the library is not loaded by default.
(use-package xdg)
Finally, I have a lot of modifications to my PATH
variable and
others that I want to import into Emacs, regardless of whether it was
started with systemd or not. To do this, we can use the
exec-path-from-shell
package, and only initialize it when Emacs is
started as a daemon (non-interactively, so not from a shell).
(use-package exec-path-from-shell
:config (when (daemonp)
(exec-path-from-shell-initialize)))
This section describes some configuration options that are globally important, but don’t really fit anywhere else.
Set my name and (public, personal) email address for whenever Emacs needs it.
(setq user-full-name "Patrick M. Niedzielski"
user-mail-address "[email protected]")
By default, Emacs modifies our init.el
file to save customizations
made with the Customize mode. I don’t want to mess up my init.el
file, so we keep these customizations in a different file. I’ll first
tell Emacs where that file is, and then I’ll load any customizations
we had.
(setq custom-file (concat user-emacs-directory "custom.el"))
(load custom-file)
I want to use Unicode by default, and UTF-8 is best on Unix (and everywhere).
(set-language-environment "UTF-8")
(setq locale-coding-system 'utf-8)
(prefer-coding-system 'utf-8)
Emacs places backups in the same directory as the normal file by default. This is almost certainly not what we want, so we put them in a separate directory under our user directory.
(setq backup-directory-alist
`((".*" . ,(concat user-emacs-directory "backups")))
auto-save-file-name-transforms
`((".*" ,(concat user-emacs-directory "backups") t)))
EasyPGP, which is bundled with Emacs, lets us easily encrypt and
decrypt files with GPG. This is more or less transparent: you can
open a PGP encrypted file, edit the buffer as if it’s normal, and save
it back, encrypting it again. One thing to note is that, because
EasyPGP is bundled with Emacs, we don’t want to download it from the
package manager, so we are sure to set :ensure nil
.
(use-package epa-file
:ensure nil
:config (epa-file-enable))
Similarly, we want to use auto-compression-mode to allow us to
automatically compress and decompress files with gzip
or bzip
. I
don’t know which package to use to store this configuration, so for
the moment I’ll just keep it loose.
(auto-compression-mode 1)
(setq dired-use-gzip-instead-of-compress t)
There are a lot of keybindings in Emacs, and there’s no way I can
remember them all. Frequently I remember a prefix of a long
keybinding, and then forget the remainder of the keybinding. The
which-key
package is a surprisingly nice solution to this, and made
me realize how many keybindings I will never quite remember. It
provides a minor mode that pops up a nice buffer listing all
keybinding continuations a short while after typing an incomplete
keybinding. This means I can type the start of a keybinding, wait a
second, and see all possible completions to that keybinding. This
moreover incentivizes me to keep my own keybindings nice and logically
organized.
(use-package which-key
:diminish which-key-mode
:config
(which-key-mode))
~free-keys~ allows me to see which keys are not bound in a particular buffer. This is in some sense the opposite of ~which-key~, and is helpful for seeing which keybindings I have available for binding. I need to configure this to also show me keybinding opportunities with the Hyper key, under which I store some of my keybindings.
(use-package free-keys
:commands free-keys
:custom (free-keys-modifier . ("" "C" "M" "C-M" "H" "C-H")))
I use pass to manage my passwords, because in part because of how easy
it is to synchronize and update the passwords across devices with Git.
Emacs has nice integration with it as well. The password-store
package provides a programmatic interface to my pass database, which
lets me keep passwords out of this (public) configuration.
(use-package password-store)
The benefits in movement that Emacs gives are probably its killer feature as a text editor for me. While some people really customize their editor a lot for keybindings and replacing functionality, I try to use stock keybindings and built-in packages as much as possible. That said, each configuration I have here is one that made my life significantly better than before, so I don’t feel bad about moving away from the stock functionality here.
There’s some basic movement functionality that I use quite frequently when I’m writing documents: moving by paragraph and moving by page. Because my keyboard has a hyper key, let’s bind more convenient movement keys to them:
(use-package lisp
:ensure nil
:bind (("H-f" . forward-paragraph)
("H-b" . backward-paragraph)
("C-H-f" . forward-page)
("C-H-b" . backward-page)))
~expand-region~ is a useful little package that expands the region by
semantic units: that is for prose, it selects first a word, then a
sentence, then a paragraph, and so forth; for code, it selects first a
token, then an sexpr, then a statement, and so forth. While there is
M-@
(mark-word
) and others, which accomplish this more
immediately, having an interactive command has proven useful to me as
well. Furthermore, there is no mark-sentence
command, which I find
very useful in editing prose, and expand-region
makes this two
keystrokes.
(use-package expand-region
:bind ("C-=" . er/expand-region))
I used to be a big user of Emacs rectangle commands, especially for
inserting the same text (frequently spaces) on multiple lines. With
~cua-mode~’s visible rectangle highlighting, this was a very nice
workflow, using only built-in functionality. While I still use the
rectangle commands for some purposes, their primary utility for me was
replaced by the amazing multiple-cursors
package, which lets you
insert what seem like multiple points, and do the same edit command at
each point. There are many modes of interacting with this, most of
which I haven’t explored in depth, but the one that’s by far the most
command and useful is the mc/edit-lines
command, which I have bound
to C-c m c
. This inserts a cursor on each line in a region: the
mark is changed to a cursor, and every other line gets a cursor of its
own at the same column as the point.
(use-package multiple-cursors
:bind (("C-c m c" . mc/edit-lines)))
multiple-cursors
has some downsides, though, especially in modes
that override self-insert-command
for various keys. Then, the mode
will ask if you’re sure you want to do the “unsafe” command at each
cursor, and that sometimes screws up what the command was actually
meant to do. This is annoying, and I would eventually like to figure
out if there’s a fix.
Every now and again I see reference to a package called ~iedit~, which
seems to be in the same vein as this package. I had heard that
multiple-cursors
had superseded it, looking on [2021-09-08 mer] I
found that it’s still updated. It might be worth looking into that as
a replacement to multiple-cursors
, if I ever find the time (and,
significantly, if it doesn’t have the same annoyance as I describe
above).
Even though I consider movement the killer feature of Emacs as an editor, the actual editing functionality of Emacs is also very useful. You’ll find that some of these functions are also in other editors, sometimes even better, but when composed with the effortless ability to move about the buffer as you please, they provide for a beautiful editing experience, both for composing prose and developing software.
Before we get started, though, there are some basic, global settings that I think Emacs got wrong. I want to set these to sane values before anything else.
First, we need to never use tabs. In general, tabs are evil for indentation, but the way Emacs uses them (using tabs just as a replacement for every 8 consecutive spaces, not using them semantically) is even worse. We turn tabs indent off by default. There are very few times when we’ll need them anyway, and it can be turned back on locally to a project, a mode, or a buffer.
(setq-default indent-tabs-mode nil)
Similarly, in UNIX, all files should end with a newline. Emacs can
control this via the variable require-final-newline
. While we can
tell Emacs to automatically add a newline on saving, on visiting, or
both, I feel a bit worried about this happening without my knowledge.
Although most often git will let me know that the final line was
modified, I’m not always in a git repo, or I may absentmindedly miss
that in the diff. As an extra line of defense, I tell Emacs to ask me
on any buffer that doesn’t have a final newline whether to add one or
not when I save that buffer. This way, I’m in full control.
(setq-default require-final-newline 'ask)
Another global truism is that lines should never have trailing whitespace. This usually does nothing, and again, we can always turn it off in those specific modes or buffers that require it (or, alternatively, when we’re working with poorly crafted source files already that have needless amounts of trailing whitespace—a red flag, if ever there was one).
However, it’s an unfortunate fact that certain automatically generated
Emacs buffers having rampant trailing whitespace (a red flag, if ever
there was one), including completing-read
in the minibuffer. While
we could create a list of modes to turn this setting off, for the
specific problem of special Emacs buffers with trailing whitespace, it
appears the best cut is between buffers I can edit and /buffers I
cannot/—or in other words programming and writing buffers on one hand,
and other buffers on the other. What we do, then, is turn
show-trailing-whitespace
on only in text-mode
and prog-mode
.
(add-hook 'text-mode-hook #'(lambda () (setq show-trailing-whitespace t)))
(add-hook 'prog-mode-hook #'(lambda () (setq show-trailing-whitespace t)))
I spend most of my time in Emacs nowadays reading and writing prose, so the most important configurations in this document relate to reading and writing.
The sections that follow are mostly centered around text-mode
and
modes that derive from it.
text-mode
is probably my most-used major mode, directly and via its
derivative modes.
One of the most useful aspects of text-mode
is its understanding of
prose structure. The following keybindings (cognate with the line
movement keybindings) skip around the buffer on a sentence-by-sentence
basis:
M-a
(backward-sentence
)M-e
(forward-sentence
)M-k
(kill-sentence
)C-x <DEL>
(backward-kill-sentence
)
See the Sentences section of the Emacs manual for more information.
By default, though, these commands determine sentence boundaries using punctuation followed by two spaces. In fact, this is how I type myself, so this default works well for prose I write. I seem to be in the minority, though, and whenever I’m working with text written by someone else, it gets very annoying when the sentence commands don’t see any sentence boundaries. This is worse than the alternative, where too many false positives are given for possible sentences. We could tell Emacs to need only a single space for separating sentences, as below:
(add-hook 'text-mode-hook
(lambda () (setq sentence-end-double-space nil)))
However, there is a problem with this: it deletes the double spaces in
my own documents when I reflow paragraphs. Yuck. For the moment, I
don’t have a good solution to this. I think I’d rather get annoyed
when working with the anemic text documents that lack double spacing,
more than have Emacs muck up my own documents. Maybe someday, I’ll
write a bit of code to automatically detect whether to set
sentence-end-double-space
on a buffer-by-buffer basis, à la this
solution by Marcin Borkowski. I like the DWIMness of it, but there
are enough open threads to this solution that, again, I think I would
find it more annoying than helpful.
Most of the time, I want my paragraphs in plain text formats to be
filled, the Emacs jargon for having hard line-breaks before a
certain column. It’s mostly easy to hit the M-q
and refill the
current paragraph as I type, but it’s even easier to let Emacs
automatically break the line at the right point. To do this, I add a
hook to text-mode
:
(add-hook 'text-mode-hook #'auto-fill-mode)
~typo.el~ is a package that contains two minor modes, typo-mode
and
typo-global-mode
. The former is what we’re interested in: when
enabled, ASCII typographic characters are replaced with Unicode
characters while typing. This is very useful when editing documents,
especially now that we’re in a post-ASCII age. typo-global-mode
is
also useful: it enables a C-c 8
hierarchy to mirror the built-in
C-x 8
hierarchy, which allows us to access much of the same
functionality in program modes as we do in text modes, when needed.
(use-package typo
:hook (text-mode-hook . typo-mode)
:config (typo-global-mode 1)
(setq-default typo-language "English"))
typo.el
supports converting quotes to their language-specific
surface realizations: for English, that looks like “this”, whereas for
Esperanto, that looks like „this“. It would be great to automatically
detect which to use based on the ispell dictionary, but for the moment
I use English as a default, and manually change the quote style when
needed.
Indeed, there is similar functionality built-in to Emacs in the form
of ~electric-quote-mode~, but it only replaces quotation marks, and
only to their English typographic equivalent. I find myself using
dashes and ellipses quite often, and as well it isn’t infrequent that
I edit texts in other languages, with different typographic
traditions. typo.el
works out of the box.
I am terrible at spelling—much more terrible than a recovering Indo-Europeanist should be. Flyspell marks my spelling errors on-the-fly, underlining in red words that aren’t in my system’s English dictionary. This does yield a significant number of false positives, but it’s good enough to catch most of my spelling mistakes.
We turn on flyspell in modes that are derived from text-mode
, and we
turn on flyspell only in comments for modes that are derived from
prog-mode
.
(use-package flyspell
:diminish flyspell
:hook ((text-mode–hook . flyspell-mode)
(prog-mode-hook . flyspell-prog-mode)))
My systems tend to have Esperanto as their default language (for
displaying the interface), but most of the text I write is in English
(obviously). The auto-dictionary
package detects which language the
text I’m writing is in and sets the spell-check dictionary to that
language. We’ll turn this on whenever we have flyspell on.
(use-package auto-dictionary
:after flyspell
:hook (flyspell-mode-hook . auto-dictionary-mode))
In addition to spell checking, it’s very useful to be able to lookup
the definitions of words in some text. Luckily, Emacs has a built-in
package to search through RFC 2229 DICT servers, called
dictionary.el
. By default, this will first query a locally
installed DICT server, and if that fails, it will query the default
dict.org, which aggregates a handful of free sources, including
WordNet.
Ideally for me, either the OED or Wiktionary would have DICT interfaces. The former has amazing entries, and the latter has English definitions for words in many foreign languages. Unfortunately, I haven’t been able to find a working gateway for either of them—the only gateway that I could find seems to be inaccessible. Similarly, the only local dictionaries I can find on Debian are of lower quality than the ones on dict.org. Perhaps someday I will install a local dictionary server, but for the moment I will rely on an internet connection.
What this means is that we need to tell dictionary.el
to just use
dict.org rather than first trying localhost, and warning us that there
is no DICT server running on the machine. At the same time, we set up
a keybinding that is very similar to the keybinding for correcting
spelling, but prefixed with C-c
.
(use-package dictionary
:custom
(dictionary-server "dict.org"
"Don’t use localhost dictionary preferentially")
:bind
("C-c M-TAB" . dictionary-search))
It’s hard to know where to put my Org configuration, because of how deeply Org has inserted its tendrils into everything I do. But, I suppose, at its heart, Org mode is a markup language and an Emacs package built on top of that language. Furthermore, I think the unifying theme of Org mode is one of writing plain text; even without the (very useful) Emacs functions built on top of the Org markup, they are enabled almost entirely by the plain text, freely modifiable, and human-readable nature of Org mode.
(use-package org
:mode "\\.org'"
:init
(setq org-catch-invisible-edits 'smart
org-src-window-setup 'other-window
org-indirect-buffers-display 'other-window
org-src-fontify-natively t
org-highlight-latex-and-related '(native script entities)
org-todo-keywords
'((sequence "TODO(t)" "NEXT(n)" "|" "DONE(d)")
(sequence "DELEGATED(e@/!)" "WAITING(w@/!" "HOLD(h@/!)" "|" "CANCELED(c@/!)" "MEETING(m)"))
org-todo-keyword-faces '(("TODO" :foreground "red" :weight bold)
("NEXT" :foreground "DeepSkyBlue2" :weight bold)
("DONE" :foreground "forest green" :weight bold)
("WAITING" :foreground "orange" :weight bold)
("DELEGATED" :foreground "orange" :weight bold)
("HOLD" :foreground "magenta" :weight bold)
("CANCELED" :foreground "forest green" :weight bold)
("MEETING" :foreground "red" :weight bold)))
<<org-latex-export>>
:config
<<org-clock-config>>
:bind (("C-c a" . org-agenda)
("C-c l" . org-store-link)
("C-c !" . org-time-stamp-inactive)
(:map org-mode-map
("C-c n n" . org-id-get-create))))
Let’s take these configurations in turn.
Note as of [2022-05-06 ven 21:53]: I haven’t used this in quite a while, but because I’m slowly moving over my org configuration into a git repository, I’m keeping it here for the moment.
Org mode includes clocking functionality that tracks how much time you spend working on a particular project in your org agenda. I think the primary use case of this is for contractors who bill by the hour; having a detailed list of hours spent on each task is simply necessary for such a job. I am not at all in that position. However, some time during the early part of 2021, I experimented with tracking my tasks for a week, to see how much time I spent on different parts of a paper I was working on. I found this very informative, but moreover, I found that having something how much time I work on each task made me focus on each one a bit more, rather than flitting to and fro between tasks—a bad habit I’ve picked up over the course of the COVID-19 pandemic. I thought this might help me better estimate how much time I need to spend on something to complete it, but it didn’t help this at all. It seems to be a purely psychological trick, and part of me hopes I’ll be able to grow out of it.
And so, I am by no means religious about this, and it strikes me that giving this any more than the minimum amount of thought would be counterproductive rather than helpful. Luckily, the configuration for this is dead simple. We teach Emacs to remember both our time history and the time of any currently running clock across restarts and also set up hooks that actually perform the persistence.
(setq org-clock-persist t)
(org-clock-persistence-insinuate) ;weird name, org-mode
At this point, work tracking works very simply. All the relevant
keybindings live under the C-c C-x
prefix (along with seemingly half
of org’s keybindings). These are the ones I use:
C-c C-x C-i
- Start a work clock on the heading under point.
C-u C-c C-x C-i
- Start a work clock on a recent task.
C-c C-x C-o
- Stop a work clock.
The Pomodoro Technique divides tasks into roughly 25 minute blocks (“pomodoros”), with 5 minute breaks in between. I prefer to work in longer blocks when I can, without distraction, but when I don’t have the motivation or interest to maintain that level of focus, dividing work into shorter blocks of time can do wonders. It’s a mental trick, and should be reserved for those times when you need mental tricks to get something done. It won’t magically make you more productive.
If you want to do this, just use a phone timer or such. I don’t think it’s worth integrating this into Emacs specifically except if you use work timing as above. And even then, it is only a minor convenience—otherwise there’s really no point. But, if you do use work tracking in org mode, you can use the org-pomodoro package, whose singular benefit is to start a work clock at the beginning of a pomodoro, and stop the work clock at the end of the pomodoro.
The one configuration we make is to send Pomodoro alerts as system alerts. Otherwise, I am liable to miss them.
(use-package org-pomodoro
:after alert
:commands (org-pomodoro) ;only load when we call this
:config
(add-to-list 'alert-user-configuration
'(((:category . "org-pomodoro")) libnotify nil)))
I didn’t used to have much use for Org mode exporting: my main use of Org mode was for writing, reading, and using the package landscape built-up around Org mode. However, I’ve found more and more that exporting has a place in my workflow.
For example, although most of my writing for publication is in LaTeX, I’m trying out doing more and more handouts in Org mode, and then exporting to LaTeX. This is still somewhat of a manual process, wherein I check and manually modify the LaTeX document, copying in some of the preamble commands I need outside of Org mode. As I get more confident with the Org export functionality, I will start to put more of this preamble content in configuration here.
Also, with org-roam-ui, I’m consuming a lot more Org content in exported HTML format as well. It’s becoming more and more important to have export set up well, in multiple different formats.
Let’s get started with our export configuration.
(setq org-latex-compiler "lualatex")
As of [2021-07-04 dim], I’m trying out the new version of org-roam. The basic concepts behind org-roam have changed pretty dramatically, as well as its interface, so the configuration for this is very different. At first, I needed to use Quelpa to download this version, but it has since become the default version on MELPA.
By default, org-roam sets up no global keybindings, but because it’s
such an important part of my workflow, I choose to set up some of my
own. They will live under the C-c n
prefix.
("C-c n l" . org-roam-buffer-toggle)
("C-c n f" . org-roam-node-find)
("C-c n c" . org-roam-capture)
("C-c n t" . org-roam-tag-add)
("C-c n a" . org-roam-alias-add)
("C-c n r" . org-roam-ref-add)
("C-c n d T" . org-roam-dailies-capture-today)
("C-c n d D" . org-roam-dailies-capture-date)
("C-c n d t" . org-roam-dailies-goto-today)
("C-c n d d" . org-roam-dailies-goto-date)
Furthermore, when we’re in an org-mode buffer, we might want to insert some links to org-roam notes, so we add some mode-local keybindings:
(:map org-mode-map
("C-c n i" . org-roam-node-insert)
("C-c n I" . org-roam-node-insert-immediate))
(use-package org-roam
:after org
:custom
(org-roam-directory "~/Dokumentoj/org/notes/")
(org-roam-dailies-directory "daily/")
:bind
<<org-roam-keybinds>>
<<org-roam-mode-keybinds>>
:init
(setq org-roam-v2-ack t) ; Don’t display a warning every time we load org-roam
:config
(setq org-roam-capture-templates
'(("d" "default" plain "%?"
:if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org"
"#+title: ${title}\n#+date: %T\n")
:unnarrowed t)
("p" "person" plain "%?"
:if-new (file+head "${slug}.org"
"#+title: ${title}\n#+date: %T\n#+filetags: person\n")
:unnarrowed t))
org-roam-dailies-capture-templates
'(("d" "default" entry
"* %?\n%U\n"
:if-new (file+head "%<%Y-%m-%d>.org"
"#+title: %<%Y-%m-%d>\n"))
("e" "event" entry
"* %?\n%T\n"
:if-new (file+head "%<%Y-%m-%d>.org"
"#+title: %<%Y-%m-%d>\n"))))
(setq org-roam-mode-section-functions
(list #'org-roam-backlinks-section
#'org-roam-reflinks-section
#'org-roam-unlinked-references-section))
(org-roam-db-autosync-enable))
(use-package org-roam-protocol :ensure nil :after org-roam :demand t)
Next, we can set up org-roam-ui, which replaces the old org-roam-server. This gives us an interactive view of the connections between our org-roam nodes, which gives a bit more of a global view of my repository than the default org-roam commands are really able to. Although until recently this was not on MELPA, as of [2021-11-14 dim], I can use normal package downloads to get this package.
(use-package org-roam-ui
:after (org-roam websocket simple-httpd)
:config
(setq org-roam-ui-sync-theme t
org-roam-ui-retitle-ref-nodes t
org-roam-ui-follow t
org-roam-ui-update-on-save t
org-roam-ui-open-on-start t))
Markdown support isn’t included by default in Emacs, and Emacs doesn’t
recognize files with the .markdown
and .md
extensions. We use
markdown-mode by Jason Blevins and associate these extensions with it.
(use-package markdown-mode
:mode "\\.md\\'"
:custom
(markdown-asymmetric-header t
"Only put header markup at the start of the line.")
(markdown-enable-highlighting-syntax t
"Use ==this== for highlighter support.")
(markdown-enable-html t
"Font lock for HTML tags and attributes.")
(markdown-enable-math t
"Font lock for inline LaTeX mathematics.")
(markdown-fontify-code-blocks-natively t
"Use the right major mode for font locking in source blocks.")
(markdown-bold-underscore nil
"Use **this** instead of __this__ for boldface.")
(markdown-italic-underscore t
"Use _this_ instead of *this* for italics.")
(markdown-list-indent-width 2
"Indent by two spaces for a list."))
Most of the configuration for this mode is in either font-locking or
in using some of the convenience features the mode gives. Most of my
consumption of Markdown documents is in reading the unrendered markup
code, so I care a lot about making it look nice, and making it easier
to read. This is despite the fact that I write in org-mode
more
frequently these days (even though I think Markdown is an
easier-to-read markup language), so I don’t always remember the
specifics of using Markdown mode. That said, I want to make it as
easy to write beautiful looking Markdown documents that are as
easy-to-read as possible, and these configurations help enable that.
Most of the keybindings for Markdown mode are under the C-c C-s
namespace. The ones I (should) use most often are:
Action | Keybinding |
---|---|
Insert heading | C-c C-s H |
Bold | C-c C-s b |
Italic | C-c C-s i |
Inline code | C-c C-s c |
Blockquote | C-c C-s q |
Code block | C-c C-s C |
Edit code block | C-c ' |
Insert link | C-c C-l |
Insert image | C-c C-i |
Follow link | C-c C-o |
Insert footnote | C-c C-s f |
Jump between reference and definition | C-c C-d |
New list item | M-RET |
Promote/demote list item | C-c <left>=/=<right> |
Horizontal rule | C-c C-s - |
These were distilled from the Guide to Markdown Mode.
For historical reasons, a lot of the Emacs programming functionality
is hacked up around regular expressions, which aren’t powerful enough
to know much about the underlying structure inherent in the code. One
of the ways around this is to use tree-sitter
, an external project
that parses code from a large number of programming languages into
trees. Emacs can then read these parses and more correctly highlight,
navigate, and modify our code buffers.
To do this, we need to enable integration between tree-sitter
and
Emacs. Currently, this requires an external package, which we have to
install separately, and which integrates highlighting, navigation, and
modification with the results of =tree-sitter=’s parsing.
(use-package tree-sitter
:hook (tree-sitter-after-on-hook . tree-sitter-hl-mode)
:config (global-tree-sitter-mode))
(use-package tree-sitter-langs)
While Tree Sitter adds syntactic information to Emacs’s editing capabilities, the Language Server Protocol (LSP) adds semantic information to it. LSP is another client-server protocol, where Emacs is the client, and each programming language project has its own server that Emacs starts. This server uses information derived from linters, or the compiler toolchain, or other semantic analysis software, to enable easy refactoring, code navigation, and completion, among many other things. In short, it gives Emacs many of the tools of modern IDEs, without needing to implement them separately for each Emacs major mode.
As is to be expected, there are two different packages for Emacs that implement an LSP client.
- eglot
- Of the two, this feels the most “Emacs” to me. It’s lightweight, and it doesn’t radically change the interaction with Emacs, which I like—in its own words, it “stays out of your way”. Furthermore, it integrates with many built-in packages, like xref, eldoc, and Flymake.
- lsp-mode
- This is the batteries-included LSP client out of the two. It feels like it integrates with every package ever, especially ones that I don’t use. It can be a bit heavyweight, and some of the UI elements can feel a bit intrusive when I don’t want them.
For a long time I used lsp-mode, because I wasn’t able to get eglot working. However, as I started documenting this section of my configuration file on [2022-03-07 lun], I decided to give eglot another try.
Eglot works by starting a server using the current buffer major mode,
scoped local to the project (using project.el
). Information from
the server is exposed mostly through other packages, mirroring the
built-in functionality for writing Emacs LISP code in particular.
- finding symbols can be done with
xref
, - diagnostics can be given using
flymake
, - symbol documentation can be given using
eldoc
and optionallymarkdown
, - completion is taken care of by
completion-at-point
andcompany
, and - code snippets can be inserted by
yasnappet
.
LSP does provide some additional functionality that isn’t exactly replicated by existing Emacs packages, such as
eglot-rename
to rename symbols across the entire project,eglot-format
to automatically format a piece of code, andeglot-code-actions
(and some predefined shortcutseglot-code-action-X
for action X) to modify the code on a language-by-language basis.
Configuring eglot is trivial:
(use-package eglot)
Perhaps I may eventually set up keybindings for some of those actions, but I don’t use them frequently enough at the moment to worry about that.
The language I spend most of my time in as of [2022-03-07 lun] is Haskell. Although I’m a systems programmer at heart, Haskell lets me write code for my research that is very close to the mathematical formalisms I’m working with.
First of all, the biggest configuration we can do is turn on LSP with eglot. Otherwise, Haskell Mode comes with quite a number of minor modes we can turn on, many of which are useful. We set up our mode hook to turn on the ones we want.
(use-package haskell-mode
:hook (haskell-mode-hook . eglot-ensure)
(haskell-mode-hook . haskell-unicode-input-method-enable)
(haskell-mode-hook . haskell-indentation-mode)
(haskell-mode-hook . haskell-decl-scan-mode)
(haskell-mode-hook . interactive-haskell-mode))
There are some keybindings in Haskell Mode that are very useful, but that I have a particularly bad time remembering:
M-x haskell-mode-format-imports
(C-c C-,
) sorts and aligns the imports at the top of a file.
The time I spend in Emacs now is dominated by reading documents of various kinds: academic articles, books, conference proceedings, and so forth. Unfortunately, this is exactly a place where the built-in functionality of Emacs does not shine (which is not the biggest surprise, as it is at its heart a text editor). The configurations in this section to me represent the most disheartening, in that they move me the furthest away from what Emacs out-of-the-box is designed to do.
While Emacs does come with a built-in document reading package, called DocView, it is a very unpleasant experience to use it. It can read a wide variety of different document types (including Microsoft Word documents), but where possible, I would like to use better tools.
PDF Tools is a significantly better tool to read PDFs in Emacs. It functions as a drop-in replacement for DocView, so any package that opens a PDF can open it in PDF Tools without issue, and some of the basic keybindings for navigation are shared with DocView, making the switch to (or in my case, from) DocView more pleasant. Furthermore, it is fully maintained (writing as of [2021-10-28 ĵaŭ]), and hopefully more features and bugfixes will be on the way.
The biggest downside of PDF Tools is that it relies on a server
program called epdfinfo
that communicates with Emacs and provides it
with enough information to handle the complex searching and annotation
functionality that the Emacs frontend provides. This server program
must be compiled before the package can be used, which relies on the
system having a compiler and development tooling, as well as the
required libraries for PDF. We will set it up to build everything we
need when we load a PDF for the first time.
(use-package pdf-tools
:mode "\\.pdf\\'"
:magic ("%PDF" . pdf-view-mode)
:config
(require 'pdf-tools)
(require 'pdf-view)
(require 'pdf-misc)
(require 'pdf-occur)
(require 'pdf-util)
(require 'pdf-annot)
(require 'pdf-info)
(require 'pdf-isearch)
(require 'pdf-history)
(require 'pdf-links)
(setq pdf-view-continuous nil)
:functions
(pdf-tools-disable-cursor pdf-tools-advice-evil-refresh-cursor))
Being able to read PDFs is not enough. I also need to be able to organize my corner of the literature, so I can effectively search through it, take notes and process it, and then cite it in my own writing. There are many tools to do this, even within Emacs, but I would like a system that isn’t exclusive to Emacs: I should be able to easily and effectively add entries and read documents without Emacs (or without any other particular program).
The natural solution is a plaintext BibLaTeX file and a directory of PDF files. I could have some hierarchy to these PDF files, but if my BibLaTeX file is good enough, it can serve as an index to them, much as notmuch is an index to my mail. To that end, I choose a directory ~Biblioteko~ under my home directory, which stores a BibLaTeX file ~biblioteko.bib~ and a loose collection of PDFs, each named after their BibLaTeX key. This makes it easy to store in a git annex repository, and thus to sync the library across each computer. For managing this directory and the files within, I have settled on two packages: Ebib, for editing and organizing the collection, and ivy-bibtex, for searching through and citing entries.
Because our whole enterprise is based on Bib(La)TeX, we need to make
sure we set up Emacs’s BibTeX mode up properly. The most import thing
is for me is to make sure it automatically generates keys in format I
find most useful. While some people may find this key format verbose,
I find that it makes the backing source code for these citing
documents easier to read directly. While the code to make this happen
is a bit verbose, my end goal is to get keys like
chomsky1957syntactic
for Chomsky’s (1957) Syntactic Structures.
(use-package bibtex
:ensure nil
:config
(setq bibtex-autokey-names 1
bibtex-autokey-names-stretch 1
bibtex-autokey-additional-names "etal"
bibtex-autokey-name-separator ""
bibtex-autokey-name-year-separator ""
bibtex-autokey-year-length 4
bibtex-autokey-year-title-separator ""
bibtex-autokey-titleword-first-ignore '("the" "a" "if" "and" "an")
bibtex-autokey-titleword-length 30
bibtex-autokey-titlewords 1
bibtex-autokey-use-crossref t
bibtex-autokey-edit-before-use t
bibtex-autokey-before-presentation-function #'downcase))
Org-cite is a relatively recent addition to org-mode, which lets me include bibliography citations in my org files. Although most serious things I write in LaTeX, setting this up is still very useful for my org-roam notes.
For the moment, I do very little configuration of this, just setting where my bibliography is:
(use-package oc ; org-cite is oc.el :(
:ensure nil
:after org
:custom (org-cite-global-bibliography '("~/Biblioteko/biblioteko.bib")))
Ebib is a very nice replacement for software programs like Mendeley, Zotero, and JabRef. Each of these had some downside:
- Mendeley started encrypting its local database of PDFs after Zotero wrote an importer for Mendeley users. How is this okay?
- Zotero felt very heavyweight when I tried it briefly, and integrating it with BibLaTeX was a pain. Furthermore, its apparent killer feature, the ability to download bibliographic information, neither was unique to it nor even gave particularly high quality results.
- JabRef was nice, but it’s still a quite heavy Java program for a task so fundamentally simple, hogging more RAM than it has any right to.
Ebib has served as a good replacement for each of these programs. Because it’s in Emacs, of course, it integrates nicely with other packages, but it also has some nice features of its own: main and dependent databases, a visual editor for BibLaTeX fields, and integration with LaTeX and Org mode for citations and notes. This makes my workflow quite a bit easier, and makes adding and editing my PDF database a breeze. Moreover, since its backend is just a BibLaTeX file, I don’t need to worry about interfacing with other authors who use different programs, or importing my database from one system to another. It should just work.
The ebib manual is a good read to understand all that this package can do. My usage at the moment is quite simple, but like much of Emacs, it’s very easy to grow into the rest of the functionality, as it were.
(use-package ebib
:after xdg
:bind ("<f7>" . ebib)
:custom ((ebib-bibtex-dialect 'biblatex)
(ebib-preload-bib-files '("biblioteko.bib"))
(ebib-bib-search-dirs '("~/Biblioteko"))
(ebib-file-search-dirs '("~/Biblioteko"))
(ebib-reading-list-file "~/Dokumentoj/org/reading.org")
(ebib-use-timestamp t)
(ebib-use-timestamp-format "%Y-%m-%d")
(ebib-import-directory (or (xdg-user-dir "DOWNLOAD") "~/Downloads"))
(ebib-layout 'window)
(ebib-file-associations '())))
For working with my different writing and programming projects, I have some external Emacs packages to make things easier.
Version control is a big part of my project management, and I almost
exclusively use git as version control. While git has a very nice
data model, its user interface is somewhat lacking. Luckily, the
Emacs ecosystem provides us with a much nicer interface to git. In
fact, I would go as far as to say that Magit is the best git
porcelain, by far. For ease of access, I have the main Magit command,
magit-status
, bound to C-c g
.
(use-package magit
:bind ("C-c g" . magit-status)
:config (setq magit-repository-directories '(("~" . 3)))
:commands (magit-status magit-blame magit-log-buffer-file magit-log-all))
We want Magit to search for all repositories nested three directories
under home. ([2022-04-04 lun 21:13] At some point, I will look into
how this relates to Emacs’s own built-in project search; it’s not
clear to me that I really need this configuration when I use
project.el
.)
(use-package magit-filenotify :after magit)
(use-package forge :after magit)
We also want to integrate Magit into Emacs’s project functionality:
(use-package magit-extras
:ensure nil)
(use-package git-commit
:custom ((git-commit-major-mode . 'markdown-mode)))
(use-package magit-imerge
:after magit)
Emacs’s built-in way of alerting you of something is by appending to
the Messages
buffer, which also shows up in the minibuffer. This is
often hard to see, and it doesn’t interface nicely with other
notifications my system gives me, using libnotify
. John Wiegley’s
alert
package gives us a consistent interface to any number of
notification backends; this works on Windows, OSX, and UNIX-y systems,
and can be easily extended. For the moment, since the only systems I
use Emacs on are graphical Linux systems, I unconditionally configure
alert
to use a backend that forwards to libnotify
. On GNOME
Shell, this makes a little notification pop up at the top of the
screen, with a customizable title, icon, and actions.
(use-package alert
:config
(setq alert-default-style 'notifications))
Any package that uses alert
will now show us notifications in GNOME
Shell.
I use org-agenda
to manage my TODOs and schedule, so many of the
more important parts of my configuration for what I do day-to-day are
here. First, we want to let Emacs know where I keep my TODO files.
(setq org-agenda-files '("~/Dokumentoj/org/"
"~/Dokumentoj/org/notes/daily/"))
Not everything in Emacs land knows about org-agenda
. The older,
built-in package diary.el
includes things like BBDB anniversaries,
sunrise/sunset times, and more. In order to include these in the
org-agenda
, we need to set the following variable:
(setq org-agenda-include-diary t)
There is an alternative way to do the above, which is apparently somewhat faster: you can add specific diary expressions into your org file, and add only those diary entries (anniversaries, holidays, etc.) that you need. I haven’t seen any issues with the above, but I will want to keep this in mind just in case.
(setq org-agenda-category-icon-alist nil)
(setq org-agenda-use-time-grid t)
I also use the LOCATION
property quite frequently in my tasks, and I
would like to see those in my agenda along with the time. Do do this,
we reach for the org-agenda-property
package, which is a very small
addition to org-agenda, but gives this information to be very easily.
(use-package org-agenda-property
:custom (org-agenda-property-list '("LOCATION")))
I would like to put local sunrise and sunset times in my Org Agenda,
as events at the correct time of day. This can be done using the
solar.el
functionality present within Emacs, and using Emacs diary
expressions.
First, we need to figure out where we are currently located. Emacs uses a handful of variables to figure this out:
(setq calendar-latitude 42.36164
calendar-longitude -71.090255
calendar-location-name "Cambridge, MA")
For the moment, I have this hard-coded, but really I should be using GeoClue to figure out where the system is.
Next, I make two functions that I can call in an org file with diary expressions, which return a string with the sunrise and sunset, respectively:
(defun pn/diary-sunrise ()
"Local time of sunrise as a diary entry.
Accurate to a few seconds."
;; To be called from diary-list-sexp-entries, where DATE is bound.
(with-no-warnings (defvar date))
(or (and calendar-latitude calendar-longitude calendar-time-zone)
(solar-setup))
(let ((l (solar-sunrise-sunset date)))
(if (car l)
(format "🌅 Sunrise %s at %s"
(apply #'solar-time-string (car l))
(eval calendar-location-name))
"No sunrise")))
(defun pn/diary-sunset ()
"Local time of sunset as a diary entry.
Accurate to a few seconds."
;; To be called from diary-list-sexp-entries, where DATE is bound.
(with-no-warnings (defvar date))
(or (and calendar-latitude calendar-longitude calendar-time-zone)
(solar-setup))
(let ((l (solar-sunrise-sunset date)))
(if (cadr l)
(format "🌅 Sunset %s (%s hrs daylight)"
(apply #'solar-time-string (cadr l))
(nth 2 l))
"No sunset")))
Plain text is incredibly versatile as a means for interacting with computers, and Emacs is incredibly versatile as a means for interacting with plain text. A substantial amount of my time is spent in Emacs, and that includes working with networked applications. This section details my configuration for several important such applications, all entirely in plain text!
My email is synced locally using IMAP and SMTP, so I always have a copy of my email archives. Because these archives are all stored in Maildirs, I can read them, search them, and interact with them in my choice of mail clients; if any of them ever fails me, it’s little effort to use another one temporarily. My preferred mail clients have all been applications within Emacs, though. Though early on I used the builtin GNUS, I found it too heavyweight and confusing to use daily. For a while, I used mu4e, which I loved, but it physically moves mail messages within my maildirs when I retag messages. This always worried me, as I do not want to lose any mail accidentally.
So instead, I’ve settled on notmuch for indexing my mail, and the
included notmuch Emacs interface for reading my mail. Notmuch is
blazingly fast, integrates with other mail clients, like mutt
, and
also comes with a nice command line interface for working with my
large mail archive. Moreover, and most importantly, it maintains a
separate index of my email archive, so it doesn’t touch the ground
truth: the emails themselves.
Because the notmuch binary and the notmuch Emacs interface are tightly coupled, we don’t want to search for the latest notmuch package on MELPA or such. Instead, we just load the package that is already installed on the system.
(use-package notmuch
:ensure nil
:bind (("<f6>" . notmuch))
:init
(setenv "EMAIL_QUEUE_QUIET" "true")
(setq sendmail-program "/home/pniedzielski/.local/bin/msmtpq"
message-send-mail-function 'message-send-mail-with-sendmail
;; mail-from-style 'angles
mail-host-address "pniedzielski.net"
mail-specify-envelope-from t
;; needed for debian’s message.el, cf. README.Debian.gz
message-sendmail-f-is-evil nil
mail-envelope-from 'header
message-sendmail-envelope-from 'header
message-citation-line-format "%f skribis:\n"
message-citation-line-function 'message-insert-formatted-citation-line
notmuch-fcc-dirs '(("[email protected]" . "personal/Sent")
("[email protected]" . "mit/Sent"))
mml-secure-openpgp-sign-with-sender t)
:custom
(notumch-search-oldest-first nil
"Give me the most recent mail at the top."))
RSS has been and remains one of the best ways of consuming syndicated articles on the web. Although its heyday seems to have gone (being too commonly replaced by walled gardens and email newsletters), there still are plenty of RSS feeds out there, as well as services that convert public data into RSS feeds.
I use FreshRSS on my personal server to collect and synchronize my feeds across all my devices (personal computers, web, and mobile phone), and so I need an Emacs package that supports one of FreshRSS’s APIs. Luckily, the Emacs feedreader elfeed, when augmented with a separate extension named elfeed-protocol, is able to communicate with my FreshRSS server, synchronizing its local database with the online server.
(use-package elfeed
:config (setq elfeed-use-curl t
elfeed-db-directory (concat user-emacs-directory "elfeed"))
(elfeed-set-timeout 36000)
:bind (:map elfeed-search-mode-map
("m" . elfeed-toggle-star)
("M" . elfeed-toggle-star)))
(use-package elfeed-goodies
:config (elfeed-goodies/setup))
(use-package elfeed-protocol
:defer nil
:init
(defun pn/elfeed-refresh ()
"Refresh elfeed feeds from Fever API."
(interactive)
(save-mark-and-excursion
(push-mark (point))
(push-mark (point-max) nil t)
(goto-char (point-min))
(cl-loop for entry in (elfeed-search-selected)
do (elfeed-untag-1 entry 'unread))
(elfeed-search-update--force)
(elfeed-protocol-fever-reinit "https://[email protected]")))
:config
(setq elfeed-feeds
`(("fever+https://[email protected]"
:api-url "https://rss.pniedzielski.net/api/fever.php"
:password ,(password-store-get "api/rss.pniedzielski.net")))
elfeed-protocol-enabled-protocols '(fever))
(elfeed-protocol-enable)
:bind (:map elfeed-search-mode-map
("G" . pn/elfeed-refresh)))
I use John Wiegley’s ledger program to manage my finances. Ledger is a plain text-based double-entry accounting system that lets me keep track of exactly where every cent goes. Luckily, ledger has an Emacs mode as well, which helps in managing my budget.
(use-package ledger-mode)
For quite a long time, I used Ido for completing-read
, and it worked
great. It was nice, it was lightweight, and it was easy to setup.
Especially compared to the only alternative at the time—Helm—it didn’t
take up much screen real estate and never got in my way. However,
Ido was very limited with what it could do, and (through probably no
fault of its own) was not well-integrated with third-party packages.
When I stared playing with helm-bibtex/ivy-bibtex, it finally got me
to switch to Ivy, the lighter-weight of those two alternatives.
Nonetheless, although Ivy is not as heavy, it is not especially Emacs-y. I’ve never fully understood the division of labor between Ivy and its related package Counsel, and the Ivy-ecosystem package that seems to be touted as its killer feature, Swiper, seems worse than isearch+occur. For this reason, I’m taking a look at the new kid on the block, Vertico ([2022-04-03 dim]).
As I understand it, Vertico is designed to do one thing well: be a
completing-read
implementation. It outsources many of the things in
Ivy and Counsel to other packages to deal with. In that way, I always
found that ecosystem more confusing, but I think that’s because it’s
exposing inherent complexity that that was always behind the scenes in
Ivy. This should also make it easier for me to modify exactly how my
searches work, like giving me the ability to add tags back to the
org-roam search very easily.
Because I found this so unintuitive though, this is how I understand the ecosystem:
- selectrum
- This was the predecessor to Vertico, and it
implements
completing-read
, i.e., reading a string from the minibuffer and providing completion. It was meant to be a lighter version of Ivy, and to integrate more nicely with the default Emacscompleting-read
framework. - vertico
- Vertico provides an implementation of Emacs’s
completing-read
, allowing you to read a string from the minibuffer and providing completion from a list of candidates. Like selectrum before it, it slots into Emacs’s existingcompleting-read
framework. - consult
- Consult enhances many of the commands in Emacs that use
completing-read
, and adds a few morecompleting-read
commands as well. It’s analogous to Counsel in the Ivy world. - embark
- Once you’ve found what you’re looking for with
completing-read
, Embark lets you run other commands on it than the one you invoked. For instance, if you search apropos for a function, you can instead insert the function name. It’s similar toivy-hydra
. - marginalia
- Marginalia adds nice annotations to
completing-read
, likeivy-rich
. - orderless
- This adds another filtering mechanism to
completing-read
, which is based on substring matching with multiple space-deliminated terms.
For the moment, I’m starting small: I’m only using Vertico, so I can get used to it, and Marginalia, since the annotations are very, very useful to me. In a way, this feels like using Ido mode again, although it has better integration across-the-board than Ido did when I last used it.
(use-package vertico :demand
:diminish vertico-mode
:custom
(vertico-resize t "Grow and shrink the Vertico minibuffer")
:config
(vertico-mode))
Vertico has a handful of extensions that are included along with it. One very nice one makes editing text in the minibuffer more like Ido, which is something that I greatly missed when I switched to Ivy.
(use-package vertico-directory
:after vertico
:ensure nil ; included with vertico
;; Make vertico editing more like Ido.
:bind (:map vertico-map
("RET" . vertico-directory-enter)
("DEL" . vertico-directory-delete-char)
("M-DEL" . vertico-directory-delete-word))
;; Tidy shadowed names.
:hook (rfn-eshadow-update-overlay-hook . vertico-directory-tidy))
Vertico has some issues with org-refile, if I use a completion style
that isn’t just ~basic~—or more precisely, org-refile has some issues
with other completion styles. I never tried to fix this with Ido or
Ivy, so when I started using org
more, I went from flex
completion
back to basic
. flex
completion is nice, so at some point I will
use one of the two solutions documented in the Vertico manual.
The Marginalia package adds nice annotations to completing-read
, in
the manner of ivy-rich
. This is something I really liked about the
Ivy ecosystem, and I’m glad to have it still with Vertico.
This configuration is mostly taken from the Marginalia README, but
with one change: The README says to start marginalia-mode
in the
:init
section of my use-package
declaration:
;; Must be in the :init section of use-package such that the mode gets
;; enabled right away. Note that this forces loading the package.
This doesn’t comport with what the ~use-package~ manual says, namely
that :init
is run before the package is loaded! We need to load
Marginalia before we can turn on the mode. I’m not sure why that
configuration seems to work (does it have something to do with
autoloading?), but the correct way to override the lazy loading from
:bind
is using ~:demand~.
(use-package marginalia :demand
:diminish marginalia-mode
:bind (:map minibuffer-local-map
("M-A" . marginalia-cycle))
:config (marginalia-mode))
I haven’t found a use yet for marginalia-cycle
, but having it bound
to M-A
as the documentation suggests doesn’t seem to hurt anything.
Vertico and other packages that only replace the completing-read mechanism can only do so much to the interface of commonly-used commands, as it is limited by the completing-read interface. While this interface is very flexible, we can do even better if we override those commonly-used commands with versions that do more with the information from completing-read.
Consult is one such package, the equivalent in the Vertico/Selectrum ecosystem of Ivy’s Counsel. Consult provides versions of many built-in Emacs commands, but which provide a richer interface, including real-time previews, in-minibuffer formatting, and completion narrowing.
(use-package consult
:bind (;; C-c bindings (mode-specific-map)
("C-c h" . consult-history)
("C-c m" . consult-mode-command)
("C-c k" . consult-kmacro)
;; C-x bindings (ctl-x-map)
("C-x M-:" . consult-complex-command) ;; orig. repeat-complex-command
("C-x b" . consult-buffer) ;; orig. switch-to-buffer
("C-x 4 b" . consult-buffer-other-window) ;; orig. switch-to-buffer-other-window
("C-x 5 b" . consult-buffer-other-frame) ;; orig. switch-to-buffer-other-frame
("C-x r b" . consult-bookmark) ;; orig. bookmark-jump
("C-x p b" . consult-project-buffer) ;; orig. project-switch-to-buffer
;; Custom M-# bindings for fast register access
("M-#" . consult-register-load)
("M-'" . consult-register-store) ;; orig. abbrev-prefix-mark (unrelated)
("C-M-#" . consult-register)
;; Other custom bindings
("M-y" . consult-yank-pop) ;; orig. yank-pop
("<help> a" . consult-apropos) ;; orig. apropos-command
;; M-g bindings (goto-map)
("M-g e" . consult-compile-error)
("M-g f" . consult-flymake) ;; Alternative: consult-flycheck
("M-g g" . consult-goto-line) ;; orig. goto-line
("M-g M-g" . consult-goto-line) ;; orig. goto-line
("M-g o" . consult-outline) ;; Alternative: consult-org-heading
("M-g m" . consult-mark)
("M-g k" . consult-global-mark)
("M-g i" . consult-imenu)
("M-g I" . consult-imenu-multi)
;; M-s bindings (search-map)
("M-s d" . consult-find)
("M-s D" . consult-locate)
("M-s g" . consult-grep)
("M-s G" . consult-git-grep)
("M-s r" . consult-ripgrep)
("M-s l" . consult-line)
("M-s L" . consult-line-multi)
("M-s m" . consult-multi-occur)
("M-s k" . consult-keep-lines)
("M-s u" . consult-focus-lines)
;; Isearch integration
("M-s e" . consult-isearch-history)
:map isearch-mode-map
("M-e" . consult-isearch-history) ;; orig. isearch-edit-string
("M-s e" . consult-isearch-history) ;; orig. isearch-edit-string
("M-s l" . consult-line) ;; needed by consult-line to detect isearch
("M-s L" . consult-line-multi) ;; needed by consult-line to detect isearch
;; Minibuffer history
:map minibuffer-local-map
("M-s" . consult-history) ;; orig. next-matching-history-element
("M-r" . consult-history)) ;; orig. previous-matching-history-element
;; Enable automatic preview at point in the *Completions* buffer.
;; This is relevant when you use the default completion UI.
:hook (completion-list-mode . consult-preview-at-point-mode)
;; The :init configuration is always executed (Not lazy)
:init
;; Optionally configure the register formatting. This improves the
;; register preview for `consult-register', `consult-register-load',
;; `consult-register-store' and the Emacs built-ins.
(setq register-preview-delay 0.5
register-preview-function #'consult-register-format)
;; Optionally configure the register formatting. This improves the register
;; preview for `consult-register', `consult-register-load',
;; `consult-register-store' and the Emacs built-ins.
(setq register-preview-delay 0.5
register-preview-function #'consult-register-format)
;; Optionally tweak the register preview window.
;; This adds thin lines, sorting and hides the mode line of the window.
(advice-add #'register-preview :override #'consult-register-window)
;; Optionally replace `completing-read-multiple' with an enhanced version.
(advice-add #'completing-read-multiple :override #'consult-completing-read-multiple)
;; Use Consult to select xref locations with preview
(setq xref-show-xrefs-function #'consult-xref
xref-show-definitions-function #'consult-xref)
;; Configure other variables and modes in the :config section,
;; after lazily loading the package.
:config
;; Optionally configure preview. The default value
;; is 'any, such that any key triggers the preview.
;; (setq consult-preview-key 'any)
;; (setq consult-preview-key (kbd "M-."))
;; (setq consult-preview-key (list (kbd "<S-down>") (kbd "<S-up>")))
;; For some commands and buffer sources it is useful to configure the
;; :preview-key on a per-command basis using the `consult-customize' macro.
(consult-customize
consult-theme
:preview-key '(:debounce 0.2 any)
consult-ripgrep consult-git-grep consult-grep
consult-bookmark consult-recent-file consult-xref
consult--source-bookmark consult--source-recent-file
consult--source-project-recent-file
:preview-key (kbd "M-."))
;; Optionally configure the narrowing key.
;; Both < and C-+ work reasonably well.
(setq consult-narrow-key "<") ;; (kbd "C-+")
;; Optionally make narrowing help available in the minibuffer.
;; You may want to use `embark-prefix-help-command' or which-key instead.
;; (define-key consult-narrow-map (vconcat consult-narrow-key "?") #'consult-narrow-help)
;; By default `consult-project-function' uses `project-root' from project.el.
;; Optionally configure a different project root function.
;; There are multiple reasonable alternatives to chose from.
;;;; 1. project.el (the default)
;; (setq consult-project-function #'consult--default-project--function)
;;;; 2. projectile.el (projectile-project-root)
;; (autoload 'projectile-project-root "projectile")
;; (setq consult-project-function (lambda (_) (projectile-project-root)))
;;;; 3. vc.el (vc-root-dir)
;; (setq consult-project-function (lambda (_) (vc-root-dir)))
;;;; 4. locate-dominating-file
;; (setq consult-project-function (lambda (_) (locate-dominating-file "." ".git")))
)
I used to use Amx, and before that Smex, as an enhanced
execute-extended-command
, which prioritized commands by use and
showing keyboard shortcuts along with the command name. Now that I’m
using Vertico, which (along with its ancillary packages) provides most
of the functionality in Amx, it doesn’t make sense to use Amx anymore.
The only missing functionality is saving the command history across
sessions of Emacs. Luckily, the built-in savehist
minor mode
replicates this functionality. We just need to load it and turn it
on, and it will periodically save our command history to a file,
preserving it when Emacs is shutdown.
(use-package savehist
:config (savehist-mode +1))
Although minibuffer completion is by far the most important sort of completion to my Emacs usage, I do make use of completion at point fairly frequently as well, especially when programming. However, even moreso than the default completing read, I find the default completion at point to be very annoying to use whenever there are more than a small number of options. For this reason, I have always replaced completion at point with a 3rd party package that display a popup for completion candidates, rather than opening a side window to display them. For the longest time, I used company-mode, which does work very well, but has started to feel very big and bolted-on, especially as Emacs’s built-in completion framework has matured. I’m now trying corfu, which performs the same basic UI function as company-mode, but which relies only on the Emacs completion functionality to propose candidates.
(use-package corfu
:custom
(tab-always-indent 'complete
"On TAB, first indent the line, then complete.")
:config
(global-corfu-mode))
Unlike company-mode, corfu does not provide any backends for collecting completion candidates, instead relying on Emacs’s built-in completion at point functions to do that job. This is, by and large, fine, because the completion at point backends have gotten much better in recent versions of Emacs. Furthermore, more and more modes and packages, notably including Eglot, have targetted completion at point functions to provide completion candidates. If I ever need something like the addition backends that Company provides, I can always try the Cape package, which provides similar backends as completion at point functions.
One of the nice things about corfu is how extensible it is. This comes in several forms, such as the pluggable completion at point backends above, but even its UI can be modified. There are two packages that I’m trying with corfu, because I used their equivalents with company:
- corfu-doc provides inline documentation for candidates that
include it, making it easier to select between similar
alternatives when programming.
(use-package corfu-doc :after corfu :hook (corfu-mode . corfu-doc-mode) :bind (:map corfu-map ("M-p" . corfu-doc-scroll-down) ("M-n" . corfu-doc-scroll-up) ("M-d" . corfu-doc-toggle)))
- kind-icon uses the
:company-kind
property to add icons or visual text prefixes to completion candidates, making it easier to filter completion candidates at a glance.(use-package kind-icon :after corfu :custom (kind-icon-default-face 'corfu-default "Use the same font as corfu for icons") :config (add-to-list 'corfu-margin-formatters #'kind-icon-margin-formatter))
This is honestly the least important part of my configuration. Fonts and pretty are nice, and customizing the display to save a bit of space is useful, but setting up my packages and configuration as above is so much more important to me.
One important aspect of this configuration is that I try my hardest for it to work both with X11 GUI clients and with terminal clients. I use terminal clients significantly more often than I think most Emacs GUI users do, and I don’t want my theming to make terminal clients unusable.
There are certain GUI defaults in Emacs that I’m not a big fan of, because they either take up space or distract me. Foremost among these is the toolbar, which I see very little use for: it only has the commonly used buffer and file operations whose keybindings I have no trouble remembering. Tool tips and blinking cursors are similar—they only distract me when I’m trying to do something. Let’s turn these off:
(dolist (mode
'(tool-bar-mode
tooltip-mode
blink-cursor-mode
menu-bar-mode
scroll-bar-mode))
(funcall mode 0))
The final two things we turn off here are ones that I do think have
great utility, and that I turn on manually every once in a while.
First, menu-bar-mode
is great, especially when learning a new major
mode or package. While my eventual goal is to learn the keybindings
or command names, the GUI menu is a great way to discover the
functionality of the package. For that reason, I turn it on manually
when I’m training myself on a new package. By default, though, I keep
it off. Second, scroll-bar-mode
is quite nice for seeing roughly
where you are in a large buffer, without taking up much space. For
some of this, I can use the line number on the modeline, coupled with
count-words
to see how many lines are in the (maybe narrowed)
buffer. But, it can still be a nice visual reminder. On those
occasions, I turn the scroll bar back on manually. Otherwise, though,
I keep it off, to take up less space.
My desktop environment uses the mouse to select which X11 window has keyboard input focus. I’d like this to carry over into Emacs, where the mouse also selects which Emacs window has focus. This isn’t my primary means of moving between windows (see the Windmove section for details), but when I’m working with other programs that use the mouse, this makes life a bit easier.
(setq-default mouse-autoselect-window t)
We also don’t want to see a startup screen. Instead, I’d rather be taken directly to scratch—I know how to find the GNU Manifesto on my own.
(setq-default inhibit-startup-message t)
We do, though, want the line and column numbers to be displayed on the modeline.
(line-number-mode 1)
(column-number-mode 1)
Finally, let’s make sure the margins are there, but aren’t bigger than necessary.
(setq left-margin-width 1
right-margin-width 1)
The font I’m currently using is Cascadia Code (which, luckily, is
packaged in Debian as fonts-cascadia-code
). I find this font is
very easy to read for long periods of time, and at different sizes; I
value that more than its looks or its ligatures.
(set-face-attribute 'default nil :font "Cascadia Code PL")
It can be hard to tell which window has input focus at-a-glance. I don’t find the visual clues of the mode-line sufficient to help me know where I’m typing. The ~dimmer.el~ package tones down the colors of windows that are not in focus, making it easier to see which window is in focus.
(use-package dimmer
:config <<dimmer-config-fraction>>
<<dimmer-config-packages>>
(dimmer-mode))
The default dimming fraction (20%) isn’t quite enough to make it easy to see which window is in focus, so I increase this to 40%.
(setq dimmer-fraction 0.4)
By default, dimmer.el
doesn’t dim the minibuffer and echo areas.
There are some packages, though, that use multiple windows in their
normal interface. Among these are Magit, Org Mode, and Which Key?.
(dimmer-configure-magit)
(dimmer-configure-org)
(dimmer-configure-which-key)
For a full list of supported packages, see the Configuration section
of the dimmer.el
documentation.
I use Emacs’s page functionality quite a lot, so it’s a nice quality
of life improvement to have the ugly ^L
characters displayed as
lines stretching across the window. There are a few packages that can
do this: the big ones seem to be Steve Purcell’s page-break-lines and
Vasilij Schneidermann’s form-feed. For whatever reason, I chose the
latter, and I haven’t had any problems with it.
Let’s set up form-feed to prettify our page breaks globally across all Emacs modes and buffers.
(use-package form-feed
:config (global-form-feed-mode))
In most cases, having icons in a text editor serve a similar purpose
as syntax-highlighting: used sparingly, they can help understand new
information at a glance. The all-the-icons
package gives us icons
for most file/buffer types, using icon fonts. In my view, it would be
better to use the built-in Emacs SVG support for this purpose, but I
don’t know of a package that does this.
(use-package all-the-icons)
I enable icons in three different places (which, of course, means I need three extra packages…): in dired, in abuser, and in file completion. In each of these cases, the icons are complementary to the filenames themselves. That is to say, if I know what the file I’m looking for is named, I don’t need to worry about the icons, or if I know what type of file I’m looking for, I can filter out everything else; if, on the other hand, I’m looking at a cluttered directory for the first time, or otherwise don’t know what exactly I’m searching for, icons give a good easy overview.
First, set up the dired icons, by running a hook when dired is loaded.
(use-package all-the-icons-dired :demand
:after (dired all-the-icons)
:diminish all-the-icons-dired-mode
:hook (dired-mode-hook . all-the-icons-dired-mode))
Next, set up the ibuffer icons. Of course, it could’t be parallel to the above, so we need to enable a minor mode.
(use-package all-the-icons-ibuffer :demand
:after (ibuffer all-the-icons)
:diminish all-the-icons-ibuffer-mode
:config (all-the-icons-ibuffer-mode 1))
Third and finally, set up icons in filename completion.
(use-package all-the-icons-completion :demand
:after (marginalia all-the-icons)
:diminish all-the-icons-completion-mode
:config (all-the-icons-completion-mode))
More and more terminal programs have been using ANSI color codes, or
their extensions, such as XTERM-256, to provide richer formatting of
their human-readable outputs. However, by default, Emacs does not
render these correctly (namely, as colors) in compilation-mode
or
Eshell—the two places where I am most likely to encounter has become
more of an issue as I write more Haskell code, as the commonly used
Tasty testing framework by default prints test results with color,
making it easier to see at a glance which tests have passed and which
tests have failed. This is decidedly a good thing, but when I run the
tests in compilation-mode
, I see the raw escape codes.
There are two solutions to this problem: the first is the built-in
ansi-color.el
, which filters a buffer or region for ANSI color codes
and sets text properties accordingly. This is fairly easy to set up,
but requires us to configure each of the places we want to use
it individually:
(use-package ansi-color
:ensure nil ;built-in
:defer nil
:hook (
<<ansi-color-compilation-hook>>
<<ansi-color-comint-hook>>
<<ansi-color-eshell-hook>>))
Modes that are derived from compilation-mode
accomplish this by
adding a filter function to compilation-filter-hook
:
(compilation-filter-hook . ansi-color-compilation-filter)
Similarly, for comint
modes (like shell-mode
), we add a filter
function to a hook. This hook, however, is an abnormal hook, which
takes an argument, and we need to use the function
ansi-color-process-output
:
(comint-preoutput-filter-functions . ansi-color-process-output)
The process for configuring Eshell is the same:
(eshell-preoutput-filter-functions . ansi-color-process-output)
The second solution for this problem is the xterm-color package, which supports more control codes than the basic ANSI ones. At the moment I don’t need to use it, but it’s worth keeping in mind if I find I need to commonly use an application that uses XTERM 256 or Truecolor output.
Nowadays, Emacs has built-in support for custom themes. It used to be
that you needed to manually set colors, or load a package
color-theme
to provide a theme, as follows:
(use-package color-theme-modern)
Because support for custom themes is built-in, though, we don’t need to do this anymore.
Normally, I use the tangotango theme, which is a bit old, and doesn’t support everything, but is high contrast where it matters and easy-to-read.
(use-package tangotango-theme
:defer nil
:config
(load-theme 'tangotango t))
However, sometimes a dark theme does not work well: for instance, on a laptop screen, the glare can make the dark theme hard-to-read. In this case, it’s useful to have alternative light themes available. Let’s load some alternative themes up that I can manually engage.
(use-package color-theme-modern)
(This package contains all the color themes that the old color-theme
package contains, but updated for the new, built-in custom theme
mechanism.