Skip to content

pniedzielski/emacs.d

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

99 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Emacs Configuration File

Org-mode Emacs Configuration

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.

License

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/>.

How This Works

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>>

Tangle this file

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)

Package Installation and Dependencies

In this section, we set up the backbone of our configuration: Emacs’s built-in package.el, and the use-package macro.

package.el Configuration

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)

use-package Configuration

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

Local Lisp Packages

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/"))

Native Compilation

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.

Emacs Server

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)

XDG Directories

Emacs knows about the XDG Basedirs Specification, but unfortunately the library is not loaded by default.

(use-package xdg)

exec-path-from-shell

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)))

Global Configuration

This section describes some configuration options that are globally important, but don’t really fit anywhere else.

User Configuration

Set my name and (public, personal) email address for whenever Emacs needs it.

(setq user-full-name    "Patrick M. Niedzielski"
      user-mail-address "patrick@pniedzielski.net")

Custom File

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)

Language Settings

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)

Backups

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

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))

Auto Compression

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)

Which Key?

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

~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")))

Passwords

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)

Movement

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.

Keybindings

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

~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))

Multiple Cursors

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).

Editing

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)))

Writing

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

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.

Filling Paragraphs

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 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.

Spell Checking

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))

Dictionary

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))

Org Mode

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.

Work Tracking

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.
Pomodoro Technique

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)))

Exporting

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")

Org-roam

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

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:

ActionKeybinding
Insert headingC-c C-s H
BoldC-c C-s b
ItalicC-c C-s i
Inline codeC-c C-s c
BlockquoteC-c C-s q
Code blockC-c C-s C
Edit code blockC-c '
Insert linkC-c C-l
Insert imageC-c C-i
Follow linkC-c C-o
Insert footnoteC-c C-s f
Jump between reference and definitionC-c C-d
New list itemM-RET
Promote/demote list itemC-c <left>=​/​=<right>
Horizontal ruleC-c C-s -

These were distilled from the Guide to Markdown Mode.

Programming

Tree Sitter

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)

LSP

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 optionally markdown,
  • completion is taken care of by completion-at-point and company, 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, and
  • eglot-code-actions (and some predefined shortcuts eglot-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.

Haskell Mode

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.

Reading

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.

PDF Tools

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))

Bibliography

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.

BibTeX mode

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

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

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    '())))

Projects

For working with my different writing and programming projects, I have some external Emacs packages to make things easier.

Magit

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)

Git Commit

(use-package git-commit
  :custom ((git-commit-major-mode . 'markdown-mode)))

Magit Imerge

(use-package magit-imerge
  :after magit)

Calendar

Alerts

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.

Org Agenda

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")))

Sunrise and Sunset

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")))

Internet

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!

Email

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 '(("patrick@pniedzielski.net" . "personal/Sent")
                           ("pnski@mit.edu" . "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

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://pniedzielski@rss.pniedzielski.net")))
  :config
  (setq elfeed-feeds
        `(("fever+https://pniedzielski@rss.pniedzielski.net"
           :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)))

Finances

Ledger Mode

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)

Completion

Vertico

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 Emacs completing-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 existing completing-read framework.
consult
Consult enhances many of the commands in Emacs that use completing-read, and adds a few more completing-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 to ivy-hydra.
marginalia
Marginalia adds nice annotations to completing-read, like ivy-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.

Completion Annotations

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.

Smarter Completion

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")))
  )

Minibuffer History

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))

Completion at Point

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:

  1. 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)))
        
  2. 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))
        

Visual Theming

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.

GUI

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)

Fonts and Ligatures

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")

Dimming Unused Windows

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.

Pretty Pages

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))

Icons

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))

ANSI Coloring

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.

Custom Theme

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.

About

My Emacs Configuration, finally git-ified

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published