<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://emacsredux.com/atom.xml" rel="self" type="application/atom+xml" /><link href="https://emacsredux.com/" rel="alternate" type="text/html" /><updated>2026-04-07T06:55:18+00:00</updated><id>https://emacsredux.com/atom.xml</id><title type="html">Emacs Redux</title><subtitle>Return to the Essence of Text Editing</subtitle><author><name>Bozhidar Batsov</name></author><entry><title type="html">Stealing from the Best Emacs Configs</title><link href="https://emacsredux.com/blog/2026/04/07/stealing-from-the-best-emacs-configs/" rel="alternate" type="text/html" title="Stealing from the Best Emacs Configs" /><published>2026-04-07T06:00:00+00:00</published><updated>2026-04-07T06:00:00+00:00</updated><id>https://emacsredux.com/blog/2026/04/07/stealing-from-the-best-emacs-configs</id><content type="html" xml:base="https://emacsredux.com/blog/2026/04/07/stealing-from-the-best-emacs-configs/"><![CDATA[<blockquote>
  <p>Good artists borrow, great artists steal.</p>

  <p>– Pablo Picasso</p>
</blockquote>

<p>After spending the past couple of weeks updating
<a href="https://github.com/bbatsov/prelude">Prelude</a> and my <a href="https://github.com/bbatsov/emacs.d">personal Emacs
config</a>, I figured it wouldn’t
hurt to see what the competition has been up to. I hadn’t done a
proper survey of other people’s configs in years, and the Emacs
landscape has changed quite a bit since the last time I looked.</p>

<p>So I went through <a href="https://github.com/doomemacs/doomemacs">Doom Emacs</a>,
<a href="https://github.com/purcell/emacs.d">Purcell’s emacs.d</a>, <a href="https://github.com/seagle0128/.emacs.d">Centaur
Emacs</a>, <a href="https://github.com/protesilaos/dotfiles">Prot’s
dotfiles</a>, and a handful of
others. Here are some of the most interesting things I found – settings
and tricks that I either didn’t know about or had forgotten about
entirely.</p>

<!--more-->

<h2 id="performance-tweaks">Performance Tweaks</h2>

<h3 id="disable-bidirectional-text-scanning-doom-emacs">Disable Bidirectional Text Scanning (Doom Emacs)</h3>

<p>If you don’t edit right-to-left languages (Arabic, Hebrew, etc.),
Emacs is doing a bunch of work on every redisplay cycle for nothing.
These settings tell Emacs to assume left-to-right text everywhere and
skip the bidirectional parenthesis algorithm:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nv">setq-default</span> <span class="nv">bidi-display-reordering</span> <span class="ss">'left-to-right</span>
              <span class="nv">bidi-paragraph-direction</span> <span class="ss">'left-to-right</span><span class="p">)</span>
<span class="p">(</span><span class="k">setq</span> <span class="nv">bidi-inhibit-bpa</span> <span class="no">t</span><span class="p">)</span>
</code></pre></div></div>

<p>The difference is hard to measure in small buffers, but in large files
(think multi-thousand-line JSON or log files) it adds up. Doom enables
this unconditionally and I’ve never seen anyone complain about it.</p>

<h3 id="skip-fontification-during-input-doom-emacs">Skip Fontification During Input (Doom Emacs)</h3>

<p>Emacs normally fontifies (syntax-highlights) text even while you’re
actively typing. This can cause micro-stutters, especially in
tree-sitter modes or large buffers. One setting fixes it:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">setq</span> <span class="nv">redisplay-skip-fontification-on-input</span> <span class="no">t</span><span class="p">)</span>
</code></pre></div></div>

<p>Emacs will defer fontification until you stop typing. In practice you
never notice the delay – the highlighting catches up instantly – but
scrolling and typing may feel smoother.</p>

<h3 id="increase-process-output-buffer-for-lsp-doom-purcell-centaur">Increase Process Output Buffer for LSP (Doom, Purcell, Centaur)</h3>

<p>The default <code class="language-plaintext highlighter-rouge">read-process-output-max</code> is 64KB, which is still quite
conservative. Modern LSP servers like <code class="language-plaintext highlighter-rouge">rust-analyzer</code> or <code class="language-plaintext highlighter-rouge">clangd</code> routinely
send multi-megabyte responses. Bumping this reduces the number of
read calls Emacs has to make:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">setq</span> <span class="nv">read-process-output-max</span> <span class="p">(</span><span class="nb">*</span> <span class="mi">4</span> <span class="mi">1024</span> <span class="mi">1024</span><span class="p">))</span> <span class="c1">; 4MB</span>
</code></pre></div></div>

<p>If you use eglot (or lsp-mode), this is basically free performance.
Three of the most popular configs out there all set it – that should
tell you something.</p>

<p><strong>Note:</strong> I’m really surprised I didn’t discover this one sooner.
Probably that’s because I rarely work on big projects these days.</p>

<h3 id="dont-render-cursors-in-non-focused-windows-doom-emacs">Don’t Render Cursors in Non-Focused Windows (Doom Emacs)</h3>

<p>If you have several windows visible, Emacs draws a cursor in each of
them – even the ones you’re not working in. It also highlights
selections in non-focused windows. Two settings to stop that:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nv">setq-default</span> <span class="nv">cursor-in-non-selected-windows</span> <span class="no">nil</span><span class="p">)</span>
<span class="p">(</span><span class="k">setq</span> <span class="nv">highlight-nonselected-windows</span> <span class="no">nil</span><span class="p">)</span>
</code></pre></div></div>

<p>This is mostly a visual preference (I don’t mind the phantom cursors
but I know some people find them distracting), but it also reduces rendering work.</p>

<p>All four of these performance settings are safe to add unconditionally
– they have no downsides for the vast majority of users.</p>

<h2 id="kill-ring-emacss-clipboard-history-and-clipboard">Kill Ring (Emacs’s Clipboard History) and Clipboard</h2>

<h3 id="save-the-clipboard-before-killing-purcell-prot-centaur">Save the Clipboard Before Killing (Purcell, Prot, Centaur)</h3>

<p>Here’s a scenario: you copy a URL from your browser, switch to Emacs,
kill a line with <code class="language-plaintext highlighter-rouge">C-k</code>, and then try to yank the URL you copied earlier with <code class="language-plaintext highlighter-rouge">C-y</code>.
Gone. The kill replaced it on the clipboard.</p>

<p>This setting makes Emacs save the existing clipboard content into the
kill ring <em>before</em> overwriting it:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">setq</span> <span class="nv">save-interprogram-paste-before-kill</span> <span class="no">t</span><span class="p">)</span>
</code></pre></div></div>

<p>Now <code class="language-plaintext highlighter-rouge">C-y</code> gets the kill, and <code class="language-plaintext highlighter-rouge">M-y</code> gets you back to the URL. Such a
small thing, but it eliminates a genuinely annoying problem.</p>

<h3 id="no-duplicates-in-the-kill-ring-doom-prot">No Duplicates in the Kill Ring (Doom, Prot)</h3>

<p>Kill the same line three times and you get three identical entries in
the kill ring, wasting slots. This deduplicates them:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">setq</span> <span class="nv">kill-do-not-save-duplicates</span> <span class="no">t</span><span class="p">)</span>
</code></pre></div></div>

<h3 id="persist-the-kill-ring-across-sessions-doom-prot">Persist the Kill Ring Across Sessions (Doom, Prot)</h3>

<p>Most configs that use <code class="language-plaintext highlighter-rouge">savehist-mode</code> only persist search rings. But
<code class="language-plaintext highlighter-rouge">savehist</code> can save any variable – including the kill ring. Add it
and you get clipboard history that survives restarts:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">setq</span> <span class="nv">savehist-additional-variables</span>
      <span class="o">'</span><span class="p">(</span><span class="nv">search-ring</span> <span class="nv">regexp-search-ring</span> <span class="nv">kill-ring</span><span class="p">))</span>
</code></pre></div></div>

<p>One thing to watch out for: the kill ring can accumulate text
properties (fonts, overlays, etc.) that bloat the savehist file. Doom
strips them before saving:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nv">add-hook</span> <span class="ss">'savehist-save-hook</span>
          <span class="p">(</span><span class="k">lambda</span> <span class="p">()</span>
            <span class="p">(</span><span class="k">setq</span> <span class="nv">kill-ring</span>
                  <span class="p">(</span><span class="nb">mapcar</span> <span class="nf">#'</span><span class="nv">substring-no-properties</span>
                          <span class="p">(</span><span class="nv">cl-remove-if-not</span> <span class="nf">#'</span><span class="nb">stringp</span> <span class="nv">kill-ring</span><span class="p">)))))</span>
</code></pre></div></div>

<p>Probably overkill for most people, but it’s good to be aware of if
your savehist file starts growing suspiciously large.</p>

<h2 id="editing">Editing</h2>

<h3 id="auto-chmod-scripts-on-save-multiple-configs">Auto-Chmod Scripts on Save (Multiple Configs)</h3>

<p>If you create a file that starts with <code class="language-plaintext highlighter-rouge">#!</code> (a shebang line), it should
be executable. But you always forget to <code class="language-plaintext highlighter-rouge">chmod +x</code> it, run the script,
get “Permission denied”, curse, go back, chmod, try again. This hook
does it automatically:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nv">add-hook</span> <span class="ss">'after-save-hook</span>
          <span class="nf">#'</span><span class="nv">executable-make-buffer-file-executable-if-script-p</span><span class="p">)</span>
</code></pre></div></div>

<p>Save a file with a shebang, and Emacs <code class="language-plaintext highlighter-rouge">chmod +x</code>es it for you. One
of those things that should arguably be a default.</p>

<p><strong>Note:</strong> Okay, I have to admit I’ve always known this one, but seeing
it in so many configs made me want to include it here.</p>

<h3 id="sane-syntax-in-re-builder-multiple-configs">Sane Syntax in re-builder (Multiple Configs)</h3>

<p><code class="language-plaintext highlighter-rouge">re-builder</code> (<code class="language-plaintext highlighter-rouge">M-x re-builder</code>) is an interactive tool for developing
regexps – you type a pattern and see matches highlighted live in the
target buffer. The problem is the default syntax: <code class="language-plaintext highlighter-rouge">read</code>. In read
syntax, you have to double-escape everything, so a word boundary is
<code class="language-plaintext highlighter-rouge">\\&lt;</code> and a group is <code class="language-plaintext highlighter-rouge">\\(...\\)</code>. It’s the regexp equivalent of
trying to type with oven mitts on.</p>

<p>Switch to string syntax and things look like normal Emacs regexps:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">setq</span> <span class="nv">reb-re-syntax</span> <span class="ss">'string</span><span class="p">)</span>
</code></pre></div></div>

<p>Now <code class="language-plaintext highlighter-rouge">\&lt;</code> is <code class="language-plaintext highlighter-rouge">\&lt;</code> and <code class="language-plaintext highlighter-rouge">\(foo\)</code> is <code class="language-plaintext highlighter-rouge">\(foo\)</code>. Much less painful.</p>

<p><strong>See also:</strong> If you want live feedback on the regexp <em>structure</em> as
you type it (color-coded groups, character classes, etc.), check out
<a href="/blog/2026/04/06/minibuffer-regexp-mode/">minibuffer-regexp-mode</a>
– a new built-in mode in Emacs 30.</p>

<h3 id="prevent-ffap-from-pinging-hostnames-centaur-emacs">Prevent ffap from Pinging Hostnames (Centaur Emacs)</h3>

<p>Ever had Emacs freeze for a few seconds when you ran
<code class="language-plaintext highlighter-rouge">find-file-at-point</code> (or a command that uses it internally)? If the
text under point looks like a hostname – say, <code class="language-plaintext highlighter-rouge">something.com</code> in a
comment – ffap tries to ping it to check if it’s reachable. On a
slow or firewalled network, that’s a multi-second hang.</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">setq</span> <span class="nv">ffap-machine-p-known</span> <span class="ss">'reject</span><span class="p">)</span>
</code></pre></div></div>

<p>This tells ffap to never try network lookups. If you actually want to
open a remote file, you can type the path explicitly.</p>

<h2 id="windows">Windows</h2>

<h3 id="proportional-window-resizing-purcell-prot">Proportional Window Resizing (Purcell, Prot)</h3>

<p>When you split a window with <code class="language-plaintext highlighter-rouge">C-x 2</code> or <code class="language-plaintext highlighter-rouge">C-x 3</code>, Emacs halves the
current window. If you already have a multi-window layout, this can
produce one awkwardly tiny window while others stay large. With this
setting, <em>all</em> windows in the frame resize proportionally:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">setq</span> <span class="nv">window-combination-resize</span> <span class="no">t</span><span class="p">)</span>
</code></pre></div></div>

<p>The difference is subtle but makes multi-window layouts feel more
balanced without manual resizing.</p>

<h3 id="reversible-c-x-1-purcell">Reversible <code class="language-plaintext highlighter-rouge">C-x 1</code> (Purcell)</h3>

<p><code class="language-plaintext highlighter-rouge">C-x 1</code> (<code class="language-plaintext highlighter-rouge">delete-other-windows</code>) is the nuclear option – it nukes
your entire window layout to focus on one buffer. Then you spend the
next minute recreating the layout you just destroyed.</p>

<p>With <code class="language-plaintext highlighter-rouge">winner-mode</code> and a small wrapper, you can make <code class="language-plaintext highlighter-rouge">C-x 1</code>
toggle: press it once to go single-window, press it again to restore
the previous layout:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nv">winner-mode</span> <span class="mi">+1</span><span class="p">)</span>

<span class="p">(</span><span class="nb">defun</span> <span class="nv">toggle-delete-other-windows</span> <span class="p">()</span>
  <span class="s">"Delete other windows in frame if any, or restore previous window config."</span>
  <span class="p">(</span><span class="nv">interactive</span><span class="p">)</span>
  <span class="p">(</span><span class="k">if</span> <span class="p">(</span><span class="nb">and</span> <span class="nv">winner-mode</span>
           <span class="p">(</span><span class="nb">equal</span> <span class="p">(</span><span class="nv">selected-window</span><span class="p">)</span> <span class="p">(</span><span class="nv">next-window</span><span class="p">)))</span>
      <span class="p">(</span><span class="nv">winner-undo</span><span class="p">)</span>
    <span class="p">(</span><span class="nv">delete-other-windows</span><span class="p">)))</span>

<span class="p">(</span><span class="nv">global-set-key</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">"C-x 1"</span><span class="p">)</span> <span class="nf">#'</span><span class="nv">toggle-delete-other-windows</span><span class="p">)</span>
</code></pre></div></div>

<p>Just drop this into your config as-is – it’s self-contained. This is
one of those tricks where once you have it, you can’t imagine going
back.</p>

<h2 id="misc">Misc</h2>

<h3 id="faster-mark-popping-purcell-centaur-prot">Faster Mark Popping (Purcell, Centaur, Prot)</h3>

<p>The mark ring is one of Emacs’s most underused navigation features.
Every time you jump somewhere – <code class="language-plaintext highlighter-rouge">isearch</code>, <code class="language-plaintext highlighter-rouge">M-&lt;</code>, <code class="language-plaintext highlighter-rouge">M-&gt;</code>,
<code class="language-plaintext highlighter-rouge">goto-line</code>, <code class="language-plaintext highlighter-rouge">imenu</code>, and many more – Emacs pushes your old position
onto the mark ring. <code class="language-plaintext highlighter-rouge">C-u C-SPC</code> pops it, jumping you back.</p>

<p>The annoyance: you need <code class="language-plaintext highlighter-rouge">C-u C-SPC</code> every single time. With this
setting, after the first <code class="language-plaintext highlighter-rouge">C-u C-SPC</code> you can keep pressing just
<code class="language-plaintext highlighter-rouge">C-SPC</code> to continue popping:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">setq</span> <span class="nv">set-mark-command-repeat-pop</span> <span class="no">t</span><span class="p">)</span>
</code></pre></div></div>

<p>This pairs beautifully with <code class="language-plaintext highlighter-rouge">repeat-mode</code> if you have it enabled
(and you should – see <a href="/blog/2026/04/04/repeat-mode/">my earlier post on repeat-mode</a>).</p>

<h3 id="recenter-after-save-place-restores-position-doom-emacs">Recenter After save-place Restores Position (Doom Emacs)</h3>

<p><code class="language-plaintext highlighter-rouge">save-place-mode</code> is great – it remembers where you were in each file
and jumps back there when you reopen it. The problem is that it can
leave your cursor on the last visible line of the window, which is
disorienting. This advice recenters the view after the jump:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nv">advice-add</span> <span class="ss">'save-place-find-file-hook</span> <span class="ss">:after</span>
            <span class="p">(</span><span class="k">lambda</span> <span class="p">(</span><span class="k">&amp;rest</span> <span class="nv">_</span><span class="p">)</span>
              <span class="p">(</span><span class="nb">when</span> <span class="nv">buffer-file-name</span> <span class="p">(</span><span class="nb">ignore-errors</span> <span class="p">(</span><span class="nv">recenter</span><span class="p">)))))</span>
</code></pre></div></div>

<p>Small thing, but it makes reopening files feel much more natural.</p>

<h3 id="auto-select-help-windows-prot">Auto-Select Help Windows (Prot)</h3>

<p>When you press <code class="language-plaintext highlighter-rouge">C-h f</code> or <code class="language-plaintext highlighter-rouge">C-h v</code>, Emacs opens the help buffer but
leaves your cursor in the original window. You almost always want to
read the help right away, so you end up pressing <code class="language-plaintext highlighter-rouge">C-x o</code> every
single time. This fixes it:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">setq</span> <span class="nv">help-window-select</span> <span class="no">t</span><span class="p">)</span>
</code></pre></div></div>

<p><strong>Bonus:</strong> Many of the configs I surveyed also use built-in lazy
isearch counting (showing “match N of M” in the minibuffer) instead
of third-party packages like <code class="language-plaintext highlighter-rouge">anzu</code>. I recently wrote about that in
<a href="/blog/2026/03/15/isearch-lazy-count/">a dedicated post</a>.</p>

<hr />

<p>The funny thing about all of this is how much overlap there is between
configs. Half of these tricks appear in three or four of the configs I
surveyed. At this point I’m convinced there are about 200 essential
Emacs settings floating around in the collective unconscious, and
every serious config independently converges on roughly the same
subset. Picasso was right – we all steal from each other, and the
kill ring makes it embarrassingly easy. <code class="language-plaintext highlighter-rouge">M-w</code> and move on.</p>

<p>That’s all I have for you today! Keep hacking!</p>]]></content><author><name>Bozhidar Batsov</name></author><category term="Configuration" /><category term="Productivity" /><summary type="html"><![CDATA[Good artists borrow, great artists steal. – Pablo Picasso After spending the past couple of weeks updating Prelude and my personal Emacs config, I figured it wouldn’t hurt to see what the competition has been up to. I hadn’t done a proper survey of other people’s configs in years, and the Emacs landscape has changed quite a bit since the last time I looked. So I went through Doom Emacs, Purcell’s emacs.d, Centaur Emacs, Prot’s dotfiles, and a handful of others. Here are some of the most interesting things I found – settings and tricks that I either didn’t know about or had forgotten about entirely.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://emacsredux.com/assets/og-image.png" /><media:content medium="image" url="https://emacsredux.com/assets/og-image.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">The Many Faces of flet: cl-flet, cl-labels, and cl-letf</title><link href="https://emacsredux.com/blog/2026/04/06/the-many-faces-of-flet/" rel="alternate" type="text/html" title="The Many Faces of flet: cl-flet, cl-labels, and cl-letf" /><published>2026-04-06T12:00:00+00:00</published><updated>2026-04-06T12:00:00+00:00</updated><id>https://emacsredux.com/blog/2026/04/06/the-many-faces-of-flet</id><content type="html" xml:base="https://emacsredux.com/blog/2026/04/06/the-many-faces-of-flet/"><![CDATA[<p>Way back in 2013 I wrote about <a href="/blog/2013/09/05/a-proper-replacement-for-flet/">the deprecation of
<code class="language-plaintext highlighter-rouge">flet</code></a> and how
<code class="language-plaintext highlighter-rouge">noflet</code> could fill the gap. Thirteen years later, it’s probably time
for a proper overview of what replaced <code class="language-plaintext highlighter-rouge">flet</code> in <code class="language-plaintext highlighter-rouge">cl-lib</code> and when to
use each option.</p>

<p>Emacs Lisp doesn’t have a built-in way to define local functions (the
way <code class="language-plaintext highlighter-rouge">let</code> defines local variables), so <code class="language-plaintext highlighter-rouge">cl-lib</code> provides several macros
for this. If you’ve ever been confused by <code class="language-plaintext highlighter-rouge">cl-flet</code>, <code class="language-plaintext highlighter-rouge">cl-labels</code>, and
<code class="language-plaintext highlighter-rouge">cl-letf</code> – you’re not alone. The naming doesn’t make the distinctions
obvious, and the documentation is a bit dry. Let’s try to fix that.</p>

<!--more-->

<h2 id="a-bit-of-history">A Bit of History</h2>

<p>The original <code class="language-plaintext highlighter-rouge">flet</code> (from the old <code class="language-plaintext highlighter-rouge">cl</code> package) let you temporarily
override a function’s definition. It worked by swapping out the
function’s <code class="language-plaintext highlighter-rouge">symbol-function</code> cell and restoring it when the body
finished – essentially a dynamic <code class="language-plaintext highlighter-rouge">let</code> for functions:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">;; Old-style flet (deprecated since Emacs 24.3)</span>
<span class="p">(</span><span class="k">flet</span> <span class="p">((</span><span class="nv">some-function</span> <span class="p">()</span> <span class="s">"overridden result"</span><span class="p">))</span>
  <span class="c1">;; Everything here, including called functions, sees the override</span>
  <span class="p">(</span><span class="nv">some-function</span><span class="p">))</span>
</code></pre></div></div>

<p>This was very handy for testing (stubbing impure functions), but it
conflated two different things into one macro:</p>

<ol>
  <li>Defining local helper functions (a lexical concept)</li>
  <li>Temporarily overriding a global function (a dynamic concept)</li>
</ol>

<p>When <code class="language-plaintext highlighter-rouge">cl</code> was reorganized into <code class="language-plaintext highlighter-rouge">cl-lib</code> in Emacs 24.3, <code class="language-plaintext highlighter-rouge">flet</code> was
split into separate macros for each use case. This also brought the
lexical variants in line with Common Lisp semantics, where <code class="language-plaintext highlighter-rouge">flet</code> and
<code class="language-plaintext highlighter-rouge">labels</code> are lexically scoped.</p>

<h2 id="the-three-replacements">The Three Replacements</h2>

<h3 id="cl-flet-local-functions-no-recursion">cl-flet: Local Functions (No Recursion)</h3>

<p><code class="language-plaintext highlighter-rouge">cl-flet</code> binds function names lexically within its body. The key
thing to understand is that the binding is only visible in the body
forms – not inside the function’s own definition, and not to any
functions you call:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nv">cl-flet</span> <span class="p">((</span><span class="nv">double</span> <span class="p">(</span><span class="nv">n</span><span class="p">)</span> <span class="p">(</span><span class="nb">*</span> <span class="nv">n</span> <span class="mi">2</span><span class="p">)))</span>
  <span class="p">(</span><span class="nv">double</span> <span class="mi">21</span><span class="p">))</span> <span class="c1">; =&gt; 42</span>
</code></pre></div></div>

<p>Because it’s lexical, <code class="language-plaintext highlighter-rouge">cl-flet</code> cannot override functions seen by
other code:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nb">defun</span> <span class="nv">my-helper</span> <span class="p">()</span> <span class="p">(</span><span class="nb">+</span> <span class="mi">1</span> <span class="mi">2</span><span class="p">))</span>
<span class="p">(</span><span class="nb">defun</span> <span class="nv">my-caller</span> <span class="p">()</span> <span class="p">(</span><span class="nv">my-helper</span><span class="p">))</span>

<span class="p">(</span><span class="nv">cl-flet</span> <span class="p">((</span><span class="nv">my-helper</span> <span class="p">()</span> <span class="mi">999</span><span class="p">))</span>
  <span class="p">(</span><span class="nv">my-caller</span><span class="p">))</span>  <span class="c1">; =&gt; 3, NOT 999!</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">my-caller</code> still sees the original <code class="language-plaintext highlighter-rouge">my-helper</code>. This is the
fundamental difference from the old <code class="language-plaintext highlighter-rouge">flet</code>.</p>

<p>There’s also <code class="language-plaintext highlighter-rouge">cl-flet*</code>, which is to <code class="language-plaintext highlighter-rouge">cl-flet</code> what <code class="language-plaintext highlighter-rouge">let*</code> is to
<code class="language-plaintext highlighter-rouge">let</code> – each binding can reference the ones before it.</p>

<p>Use <code class="language-plaintext highlighter-rouge">cl-flet</code> when you just need a simple local helper and don’t need
recursion. Think of it as <code class="language-plaintext highlighter-rouge">let</code> for functions.</p>

<h3 id="cl-labels-local-functions-with-recursion">cl-labels: Local Functions (With Recursion)</h3>

<p><code class="language-plaintext highlighter-rouge">cl-labels</code> is like <code class="language-plaintext highlighter-rouge">cl-flet</code>, but the function <em>is</em> visible inside
its own body and inside the bodies of sibling bindings. This makes
recursion and mutual recursion possible:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nv">cl-labels</span> <span class="p">((</span><span class="nv">factorial</span> <span class="p">(</span><span class="nv">n</span><span class="p">)</span>
              <span class="p">(</span><span class="k">if</span> <span class="p">(</span><span class="nb">&lt;=</span> <span class="nv">n</span> <span class="mi">1</span><span class="p">)</span> <span class="mi">1</span>
                <span class="p">(</span><span class="nb">*</span> <span class="nv">n</span> <span class="p">(</span><span class="nv">factorial</span> <span class="p">(</span><span class="nb">-</span> <span class="nv">n</span> <span class="mi">1</span><span class="p">))))))</span>
  <span class="p">(</span><span class="nv">factorial</span> <span class="mi">10</span><span class="p">))</span> <span class="c1">; =&gt; 3628800</span>
</code></pre></div></div>

<p>This would blow up with <code class="language-plaintext highlighter-rouge">cl-flet</code> because <code class="language-plaintext highlighter-rouge">factorial</code> wouldn’t be
defined inside its own body.</p>

<p>Mutual recursion works too:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nv">cl-labels</span> <span class="p">((</span><span class="nv">my-even-p</span> <span class="p">(</span><span class="nv">n</span><span class="p">)</span> <span class="p">(</span><span class="k">if</span> <span class="p">(</span><span class="nb">=</span> <span class="nv">n</span> <span class="mi">0</span><span class="p">)</span> <span class="no">t</span> <span class="p">(</span><span class="nv">my-odd-p</span> <span class="p">(</span><span class="nb">-</span> <span class="nv">n</span> <span class="mi">1</span><span class="p">))))</span>
            <span class="p">(</span><span class="nv">my-odd-p</span> <span class="p">(</span><span class="nv">n</span><span class="p">)</span> <span class="p">(</span><span class="k">if</span> <span class="p">(</span><span class="nb">=</span> <span class="nv">n</span> <span class="mi">0</span><span class="p">)</span> <span class="no">nil</span> <span class="p">(</span><span class="nv">my-even-p</span> <span class="p">(</span><span class="nb">-</span> <span class="nv">n</span> <span class="mi">1</span><span class="p">)))))</span>
  <span class="p">(</span><span class="nb">list</span> <span class="p">(</span><span class="nv">my-even-p</span> <span class="mi">4</span><span class="p">)</span> <span class="p">(</span><span class="nv">my-odd-p</span> <span class="mi">3</span><span class="p">)))</span> <span class="c1">; =&gt; (t t)</span>
</code></pre></div></div>

<p>Use <code class="language-plaintext highlighter-rouge">cl-labels</code> when your local functions need to call themselves or
each other.</p>

<p><strong>Note:</strong> <code class="language-plaintext highlighter-rouge">cl-labels</code> requires <code class="language-plaintext highlighter-rouge">lexical-binding</code> to be <code class="language-plaintext highlighter-rouge">t</code> in the file (which
it really should be for any modern Emacs Lisp code).</p>

<h3 id="cl-letf-temporary-global-override">cl-letf: Temporary Global Override</h3>

<p>This is the one that actually replaces the old <code class="language-plaintext highlighter-rouge">flet</code>’s dynamic
behavior. <code class="language-plaintext highlighter-rouge">cl-letf</code> temporarily rebinds a generalized place (anything
<code class="language-plaintext highlighter-rouge">setf</code> can handle) and restores it on exit:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nb">defun</span> <span class="nv">my-helper</span> <span class="p">()</span> <span class="p">(</span><span class="nb">+</span> <span class="mi">1</span> <span class="mi">2</span><span class="p">))</span>
<span class="p">(</span><span class="nb">defun</span> <span class="nv">my-caller</span> <span class="p">()</span> <span class="p">(</span><span class="nv">my-helper</span><span class="p">))</span>

<span class="p">(</span><span class="nv">cl-letf</span> <span class="p">(((</span><span class="nb">symbol-function</span> <span class="ss">'my-helper</span><span class="p">)</span> <span class="p">(</span><span class="k">lambda</span> <span class="p">()</span> <span class="mi">999</span><span class="p">)))</span>
  <span class="p">(</span><span class="nv">my-caller</span><span class="p">))</span>  <span class="c1">; =&gt; 999</span>
</code></pre></div></div>

<p>Now <code class="language-plaintext highlighter-rouge">my-caller</code> <em>does</em> see the override, because <code class="language-plaintext highlighter-rouge">cl-letf</code> modifies the
actual <code class="language-plaintext highlighter-rouge">symbol-function</code> cell of <code class="language-plaintext highlighter-rouge">my-helper</code> – just like the old <code class="language-plaintext highlighter-rouge">flet</code> did.
The original definition is restored when the body exits, even on error.</p>

<p>The syntax is a bit verbose because <code class="language-plaintext highlighter-rouge">cl-letf</code> isn’t specific to
functions – it’s a general-purpose temporary binding macro for any
<code class="language-plaintext highlighter-rouge">setf</code>-able place. <code class="language-plaintext highlighter-rouge">(symbol-function 'name)</code> is a “generalized
variable” that refers to the function stored in a symbol’s function
cell – it’s just one of many places <code class="language-plaintext highlighter-rouge">cl-letf</code> can bind. For example:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">;; Temporarily silence messages</span>
<span class="p">(</span><span class="nv">cl-letf</span> <span class="p">(((</span><span class="nb">symbol-function</span> <span class="ss">'message</span><span class="p">)</span> <span class="nf">#'</span><span class="k">ignore</span><span class="p">))</span>
  <span class="p">(</span><span class="nv">do-something-noisy</span><span class="p">))</span>
</code></pre></div></div>

<p>Use <code class="language-plaintext highlighter-rouge">cl-letf</code> when you need the old dynamic <code class="language-plaintext highlighter-rouge">flet</code> behavior – typically
for testing (stubbing functions) or temporarily suppressing/redirecting
behavior.</p>

<h2 id="quick-reference">Quick Reference</h2>

<table>
  <thead>
    <tr>
      <th> </th>
      <th>Scope</th>
      <th>Recursion</th>
      <th>Overrides global?</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">cl-flet</code></td>
      <td>Lexical</td>
      <td>No</td>
      <td>No</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">cl-labels</code></td>
      <td>Lexical</td>
      <td>Yes</td>
      <td>No</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">cl-letf</code></td>
      <td>Dynamic</td>
      <td>N/A</td>
      <td>Yes</td>
    </tr>
  </tbody>
</table>

<p>In other words:</p>

<ul>
  <li>Default to <code class="language-plaintext highlighter-rouge">cl-flet</code> for local helpers. It’s the simplest and
most predictable.</li>
  <li>Reach for <code class="language-plaintext highlighter-rouge">cl-labels</code> when you need recursion or mutual
recursion in local functions.</li>
  <li>Use <code class="language-plaintext highlighter-rouge">cl-letf</code> only when you genuinely need dynamic override –
mainly in tests. Modifying global function cells is a sharp tool and
it’s not thread-safe, so keep it contained.</li>
</ul>

<hr />

<p>That’s all I have for you today. Keep hacking!</p>]]></content><author><name>Bozhidar Batsov</name></author><category term="Emacs Lisp" /><category term="cl-lib" /><summary type="html"><![CDATA[Way back in 2013 I wrote about the deprecation of flet and how noflet could fill the gap. Thirteen years later, it’s probably time for a proper overview of what replaced flet in cl-lib and when to use each option. Emacs Lisp doesn’t have a built-in way to define local functions (the way let defines local variables), so cl-lib provides several macros for this. If you’ve ever been confused by cl-flet, cl-labels, and cl-letf – you’re not alone. The naming doesn’t make the distinctions obvious, and the documentation is a bit dry. Let’s try to fix that.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://emacsredux.com/assets/og-image.png" /><media:content medium="image" url="https://emacsredux.com/assets/og-image.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Live Regexp Feedback with minibuffer-regexp-mode</title><link href="https://emacsredux.com/blog/2026/04/06/minibuffer-regexp-mode/" rel="alternate" type="text/html" title="Live Regexp Feedback with minibuffer-regexp-mode" /><published>2026-04-06T09:00:00+00:00</published><updated>2026-04-06T09:00:00+00:00</updated><id>https://emacsredux.com/blog/2026/04/06/minibuffer-regexp-mode</id><content type="html" xml:base="https://emacsredux.com/blog/2026/04/06/minibuffer-regexp-mode/"><![CDATA[<p>This is the third article in a small series inspired by my recent cleanup of
<a href="https://github.com/bbatsov/prelude">Prelude</a> and my <a href="https://github.com/bbatsov/emacs.d">personal Emacs
configuration</a>, following the ones on
<a href="/blog/2026/04/04/repeat-mode/">repeat-mode</a> and
<a href="/blog/2026/04/04/read-extended-command-predicate/">read-extended-command-predicate</a>.
I’ve been going through the Emacs 28-30 changelogs for features I had ignored
so far, and this one from Emacs 30 immediately caught my eye.</p>

<p>Writing Emacs regexps has always been a bit of a dark art. Between the
double-escaped backslashes and the various group syntaxes (<code class="language-plaintext highlighter-rouge">\(...\)</code>,
<code class="language-plaintext highlighter-rouge">\(?:...\)</code>, <code class="language-plaintext highlighter-rouge">\(?N:...\)</code>), it’s easy to lose track of what you’re
actually matching. You type something into <code class="language-plaintext highlighter-rouge">query-replace-regexp</code>,
press RET, and hope for the best.</p>

<p>Emacs 30 added <code class="language-plaintext highlighter-rouge">minibuffer-regexp-mode</code>, a minor mode that gives you
live visual feedback as you compose a regexp in the minibuffer:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nv">minibuffer-regexp-mode</span> <span class="mi">1</span><span class="p">)</span>
</code></pre></div></div>

<!--more-->

<p>When active, the mode highlights the structure of your regexp <em>as you
type it</em> in the minibuffer. Capture groups, character classes, and
other constructs get color-coded so you can see at a glance whether
your grouping is right.</p>

<p>I find this particularly useful when building a regexp with
multiple groups for <code class="language-plaintext highlighter-rouge">query-replace-regexp</code>, where you need to get the
group numbering right for the replacement string (e.g., <code class="language-plaintext highlighter-rouge">\1</code>, <code class="language-plaintext highlighter-rouge">\2</code>).
The visual feedback makes it obvious which group is which.</p>

<h2 id="how-does-this-compare-to-re-builder">How Does This Compare to re-builder?</h2>

<p>You might be wondering how this compares to <code class="language-plaintext highlighter-rouge">re-builder</code> (<code class="language-plaintext highlighter-rouge">M-x
re-builder</code>). They’re complementary, really. <code class="language-plaintext highlighter-rouge">re-builder</code> shows
matches <em>in the buffer</em> as you type a regexp in a dedicated editing
window – great for developing complex patterns against actual text.
<code class="language-plaintext highlighter-rouge">minibuffer-regexp-mode</code>, on the other hand, highlights the <em>regexp itself</em>
in the minibuffer. It kicks in automatically whenever you’re prompted
for a regexp (e.g., <code class="language-plaintext highlighter-rouge">isearch-forward-regexp</code>, <code class="language-plaintext highlighter-rouge">query-replace-regexp</code>,
<code class="language-plaintext highlighter-rouge">keep-lines</code>, etc.).</p>

<p>One helps you see what your regexp matches; the other helps you see
what your regexp <em>says</em>. I’d suggest using both.</p>

<p>That’s all I have for you today. Keep hacking!</p>]]></content><author><name>Bozhidar Batsov</name></author><category term="Emacs 30" /><category term="Regexp" /><summary type="html"><![CDATA[This is the third article in a small series inspired by my recent cleanup of Prelude and my personal Emacs configuration, following the ones on repeat-mode and read-extended-command-predicate. I’ve been going through the Emacs 28-30 changelogs for features I had ignored so far, and this one from Emacs 30 immediately caught my eye. Writing Emacs regexps has always been a bit of a dark art. Between the double-escaped backslashes and the various group syntaxes (\(...\), \(?:...\), \(?N:...\)), it’s easy to lose track of what you’re actually matching. You type something into query-replace-regexp, press RET, and hope for the best. Emacs 30 added minibuffer-regexp-mode, a minor mode that gives you live visual feedback as you compose a regexp in the minibuffer: (minibuffer-regexp-mode 1)]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://emacsredux.com/assets/og-image.png" /><media:content medium="image" url="https://emacsredux.com/assets/og-image.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Declutter M-x with read-extended-command-predicate</title><link href="https://emacsredux.com/blog/2026/04/04/read-extended-command-predicate/" rel="alternate" type="text/html" title="Declutter M-x with read-extended-command-predicate" /><published>2026-04-04T08:00:00+00:00</published><updated>2026-04-04T08:00:00+00:00</updated><id>https://emacsredux.com/blog/2026/04/04/read-extended-command-predicate</id><content type="html" xml:base="https://emacsredux.com/blog/2026/04/04/read-extended-command-predicate/"><![CDATA[<p>This is another article inspired by my recent cleanup of <a href="https://github.com/bbatsov/prelude">Prelude</a> and my
<a href="https://github.com/bbatsov/emacs.d">personal Emacs config</a>, following the one on
<a href="/blog/2026/04/04/repeat-mode/">repeat-mode</a>. I’ve been going through the Emacs 28-30
changelogs looking for features I had overlooked, and this small one from Emacs 28
turned out to be a real gem.</p>

<p>Ever noticed how <code class="language-plaintext highlighter-rouge">M-x</code> shows you <em>every</em> command, including ones that
make no sense in your current buffer? Org commands while editing Ruby,
Magit commands in a shell buffer, that sort of thing. It’s not a huge
deal if you know what you’re looking for, but it adds noise to the
candidate list – especially if you’re using a completion framework
like Vertico or Ivy that shows everything at a glance.</p>

<p>Emacs 28 added a simple way to fix this:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">setq</span> <span class="nv">read-extended-command-predicate</span>
      <span class="nf">#'</span><span class="nv">command-completion-default-include-p</span><span class="p">)</span>
</code></pre></div></div>

<!--more-->

<p>With this setting, <code class="language-plaintext highlighter-rouge">M-x</code> hides commands that declare themselves
inapplicable to the current major mode from the completion candidates.
So if you’re in a Python buffer, you won’t see <code class="language-plaintext highlighter-rouge">dired-do-rename</code> or
<code class="language-plaintext highlighter-rouge">clojure-align</code> cluttering your results.</p>

<p>How does the filtering actually work? <code class="language-plaintext highlighter-rouge">command-completion-default-include-p</code>
looks at the modes a command declares it belongs to (via the <code class="language-plaintext highlighter-rouge">interactive</code>
form) or checks its <code class="language-plaintext highlighter-rouge">completion-predicate</code> symbol property. If no modes are
declared and there’s no completion predicate, the command is included as
usual – so existing commands that haven’t been updated are not affected.</p>

<p>Emacs actually ships with three predicates you can choose from (plus <code class="language-plaintext highlighter-rouge">nil</code>
for no filtering):</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">command-completion-default-include-p</code> – the safe default. Excludes
commands tagged for other modes, includes everything else.</li>
  <li><code class="language-plaintext highlighter-rouge">command-completion-using-modes-and-keymaps-p</code> – more aggressive. Shows
commands tagged for the current mode <em>plus</em> any command that has a
keybinding in the buffer’s active keymaps. Also always includes
<code class="language-plaintext highlighter-rouge">customize-*</code> commands. Untagged commands without keybindings are hidden.</li>
  <li><code class="language-plaintext highlighter-rouge">command-completion-using-modes-p</code> – the strictest option. Only shows
commands explicitly tagged for the current mode. Untagged commands are
hidden too, so this can be quite aggressive.</li>
</ul>

<p>I’d recommend starting with <code class="language-plaintext highlighter-rouge">command-completion-default-include-p</code> since
it’s the most conservative – it won’t hide anything that hasn’t explicitly
opted in to the filtering.</p>

<p>Package authors can declare mode affiliation by adding a mode specification
to the <code class="language-plaintext highlighter-rouge">interactive</code> form:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nb">defun</span> <span class="nv">my-foo-command</span> <span class="p">()</span>
  <span class="s">"Do something useful in foo-mode."</span>
  <span class="p">(</span><span class="nv">interactive</span> <span class="no">nil</span> <span class="nv">foo-mode</span><span class="p">)</span>
  <span class="o">...</span><span class="p">)</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">nil</code> is the interactive spec (no arguments in this case), and <code class="language-plaintext highlighter-rouge">foo-mode</code>
tells Emacs this command is only relevant in <code class="language-plaintext highlighter-rouge">foo-mode</code> buffers. If a command
applies to multiple modes, just list them all:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nb">defun</span> <span class="nv">cider-eval-defun-at-point</span> <span class="p">()</span>
  <span class="s">"Evaluate the top-level form at point."</span>
  <span class="p">(</span><span class="nv">interactive</span> <span class="no">nil</span> <span class="nv">clojure-mode</span> <span class="nv">clojure-ts-mode</span><span class="p">)</span>
  <span class="o">...</span><span class="p">)</span>
</code></pre></div></div>

<p>This is handy for packages like CIDER that need to work in both the
classic <code class="language-plaintext highlighter-rouge">clojure-mode</code> and the newer Tree-sitter-based <code class="language-plaintext highlighter-rouge">clojure-ts-mode</code>.</p>

<p>As for how well this works in practice – many built-in commands
already declare their applicable modes, so you’ll see a noticeably
cleaner <code class="language-plaintext highlighter-rouge">M-x</code> right away. Third-party package adoption is growing
but uneven. Commands that haven’t been updated will simply continue
to show up everywhere, same as before – so there’s no downside to
enabling this.</p>

<h2 id="a-note-for-verticoorderless-users">A Note for Vertico/Orderless Users</h2>

<p>If you followed the <a href="https://github.com/minad/vertico#configuration">Vertico sample
configuration</a>,
you’ll find this setting already there – commented out. It was
shipped that way because it was new at the time and some users found
the disappearing commands surprising. It’s been stable for years now
and works great with Vertico, Orderless, and Marginalia. Just
uncomment it and enjoy a less noisy <code class="language-plaintext highlighter-rouge">M-x</code>.</p>

<p>Commands that are filtered out aren’t gone – the filtering only
affects completion candidates. If you type the full command name at
the <code class="language-plaintext highlighter-rouge">M-x</code> prompt it will still execute just fine.</p>

<p>That’s all I have for you today. Keep hacking!</p>]]></content><author><name>Bozhidar Batsov</name></author><category term="Emacs 28" /><category term="Productivity" /><summary type="html"><![CDATA[This is another article inspired by my recent cleanup of Prelude and my personal Emacs config, following the one on repeat-mode. I’ve been going through the Emacs 28-30 changelogs looking for features I had overlooked, and this small one from Emacs 28 turned out to be a real gem. Ever noticed how M-x shows you every command, including ones that make no sense in your current buffer? Org commands while editing Ruby, Magit commands in a shell buffer, that sort of thing. It’s not a huge deal if you know what you’re looking for, but it adds noise to the candidate list – especially if you’re using a completion framework like Vertico or Ivy that shows everything at a glance. Emacs 28 added a simple way to fix this: (setq read-extended-command-predicate #'command-completion-default-include-p)]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://emacsredux.com/assets/og-image.png" /><media:content medium="image" url="https://emacsredux.com/assets/og-image.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Repeat Mode: Stop Repeating Yourself</title><link href="https://emacsredux.com/blog/2026/04/04/repeat-mode/" rel="alternate" type="text/html" title="Repeat Mode: Stop Repeating Yourself" /><published>2026-04-04T07:00:00+00:00</published><updated>2026-04-04T07:00:00+00:00</updated><id>https://emacsredux.com/blog/2026/04/04/repeat-mode</id><content type="html" xml:base="https://emacsredux.com/blog/2026/04/04/repeat-mode/"><![CDATA[<p>I’ve been going through the Emacs 28-30 changelogs recently as part of a big
update to <a href="https://github.com/bbatsov/prelude">Prelude</a> and my <a href="https://github.com/bbatsov/emacs.d">personal config</a>, looking
for features I never got around to trying. <code class="language-plaintext highlighter-rouge">repeat-mode</code> is one I wish I’d adopted sooner.</p>

<p>How many times have you typed <code class="language-plaintext highlighter-rouge">C-x o C-x o C-x o</code> to cycle through a
few windows?  Or <code class="language-plaintext highlighter-rouge">C-x { C-x { C-x {</code> to keep shrinking one? All that
prefix repetition is pure friction.</p>

<p><code class="language-plaintext highlighter-rouge">repeat-mode</code> is a built-in minor mode (Emacs 28+) that lets you
drop the prefix after the first invocation and just keep pressing the
final key. Enable it with one line:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nv">repeat-mode</span> <span class="mi">1</span><span class="p">)</span>
</code></pre></div></div>

<!--more-->

<p>One important thing to understand – this doesn’t magically work for
every key sequence. A command is only “repeatable” if it has been
explicitly added to a repeat map. Emacs ships with repeat maps for a
bunch of common built-in commands, though, so you get a decent
experience out of the box. Here are some of the highlights:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">C-x o o o</code> – keep cycling windows</li>
  <li><code class="language-plaintext highlighter-rouge">C-x { { {</code> / <code class="language-plaintext highlighter-rouge">C-x } } }</code> – shrink/grow window horizontally</li>
  <li><code class="language-plaintext highlighter-rouge">C-x ^ ^ ^</code> – grow window vertically</li>
  <li><code class="language-plaintext highlighter-rouge">C-x u u u</code> – keep undoing</li>
  <li><code class="language-plaintext highlighter-rouge">C-x &lt;left&gt; &lt;left&gt;</code> / <code class="language-plaintext highlighter-rouge">C-x &lt;right&gt; &lt;right&gt;</code> – cycle through buffer history</li>
  <li><code class="language-plaintext highlighter-rouge">M-g n n n</code> / <code class="language-plaintext highlighter-rouge">M-g p p p</code> – jump through next-error results</li>
</ul>

<p>The transient state ends as soon as you press any key that isn’t part
of the repeat map.</p>

<p>If you’d prefer it to time out automatically, there’s a setting for that:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">setq</span> <span class="nv">repeat-exit-timeout</span> <span class="mi">5</span><span class="p">)</span> <span class="c1">;; exit after 5 seconds of inactivity</span>
</code></pre></div></div>

<h2 id="defining-your-own-repeat-maps">Defining Your Own Repeat Maps</h2>

<p>The real power comes from defining repeat maps for your own commands.
For instance, if you use <a href="https://github.com/casouri/expreg">expreg</a>
for expand-region, you can set things up so that <code class="language-plaintext highlighter-rouge">C-= = = = -</code>
expands three times then contracts once:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nb">defvar</span> <span class="nv">expreg-repeat-map</span>
  <span class="p">(</span><span class="k">let</span> <span class="p">((</span><span class="nb">map</span> <span class="p">(</span><span class="nv">make-sparse-keymap</span><span class="p">)))</span>
    <span class="p">(</span><span class="nv">define-key</span> <span class="nb">map</span> <span class="s">"="</span> <span class="nf">#'</span><span class="nv">expreg-expand</span><span class="p">)</span>
    <span class="p">(</span><span class="nv">define-key</span> <span class="nb">map</span> <span class="s">"-"</span> <span class="nf">#'</span><span class="nv">expreg-contract</span><span class="p">)</span>
    <span class="nb">map</span><span class="p">))</span>

<span class="p">(</span><span class="nv">put</span> <span class="ss">'expreg-expand</span> <span class="ss">'repeat-map</span> <span class="ss">'expreg-repeat-map</span><span class="p">)</span>
<span class="p">(</span><span class="nv">put</span> <span class="ss">'expreg-contract</span> <span class="ss">'repeat-map</span> <span class="ss">'expreg-repeat-map</span><span class="p">)</span>
</code></pre></div></div>

<p>The pattern is simple: create a keymap, then attach it to the relevant
commands via the <code class="language-plaintext highlighter-rouge">repeat-map</code> symbol property. Any command with that
property becomes “repeatable” after first invocation.</p>

<p>That’s all there is to it. One line to enable, and a lot less <code class="language-plaintext highlighter-rouge">C-x</code>
mashing in your future.</p>

<p>Are you using <code class="language-plaintext highlighter-rouge">repeat-mode</code>? Have you defined any custom repeat maps
that you find particularly useful? I’d love to hear about them in the
comments!</p>

<p>Keep hacking!</p>]]></content><author><name>Bozhidar Batsov</name></author><category term="Emacs 28" /><category term="Keybindings" /><summary type="html"><![CDATA[I’ve been going through the Emacs 28-30 changelogs recently as part of a big update to Prelude and my personal config, looking for features I never got around to trying. repeat-mode is one I wish I’d adopted sooner. How many times have you typed C-x o C-x o C-x o to cycle through a few windows? Or C-x { C-x { C-x { to keep shrinking one? All that prefix repetition is pure friction. repeat-mode is a built-in minor mode (Emacs 28+) that lets you drop the prefix after the first invocation and just keep pressing the final key. Enable it with one line: (repeat-mode 1)]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://emacsredux.com/assets/og-image.png" /><media:content medium="image" url="https://emacsredux.com/assets/og-image.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Creating Emacs Color Themes, Revisited</title><link href="https://emacsredux.com/blog/2026/03/30/creating-emacs-color-themes/" rel="alternate" type="text/html" title="Creating Emacs Color Themes, Revisited" /><published>2026-03-30T05:25:00+00:00</published><updated>2026-03-30T05:25:00+00:00</updated><id>https://emacsredux.com/blog/2026/03/30/creating-emacs-color-themes</id><content type="html" xml:base="https://emacsredux.com/blog/2026/03/30/creating-emacs-color-themes/"><![CDATA[<p>Creating Emacs color themes is a topic I hadn’t thought much about in recent
years. My first theme (<a href="https://github.com/bbatsov/zenburn-emacs">Zenburn</a>)
has been in maintenance mode for ages, and
<a href="https://github.com/bbatsov/solarized-emacs">Solarized</a> mostly runs
itself at this point. But working on my ports of
<a href="https://github.com/bbatsov/emacs-tokyo-themes">Tokyo (Night) Themes</a> and
<a href="https://github.com/bbatsov/batppuccin-emacs">Catppuccin (Batppuccin)</a> made me
re-examine the whole topic with fresh eyes. The biggest shift I’ve noticed is
that multi-variant themes (light/dark/high-contrast from a shared codebase) have
become the norm rather than the exception, and that pattern naturally leads to
reusable theming infrastructure.</p>

<p>The task has always been simultaneously easy and hard. Easy because <code class="language-plaintext highlighter-rouge">deftheme</code>
and <code class="language-plaintext highlighter-rouge">custom-theme-set-faces</code> are well-documented and do exactly what you’d
expect. Hard because the real challenge was never the mechanics – it’s knowing
<em>which</em> faces to theme and keeping your color choices consistent across hundreds
of them.</p>

<p><strong>Note:</strong> In Emacs, a <a href="https://www.gnu.org/software/emacs/manual/html_node/emacs/Faces.html">face</a>
is a named set of visual attributes – foreground color, background, bold,
italic, underline, etc. – that controls how a piece of text looks. Themes work
by setting faces to match a color palette. See also the Elisp manual’s section
on <a href="https://www.gnu.org/software/emacs/manual/html_node/elisp/Custom-Themes.html">custom themes</a>
for the full API.</p>

<!--more-->

<h2 id="the-classic-approach">The Classic Approach</h2>

<p>The traditional way to create an Emacs theme is to write a <code class="language-plaintext highlighter-rouge">deftheme</code> form,
then set faces one by one with <code class="language-plaintext highlighter-rouge">custom-theme-set-faces</code>:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nv">deftheme</span> <span class="nv">my-cool-theme</span> <span class="s">"A cool theme."</span><span class="p">)</span>

<span class="p">(</span><span class="nv">custom-theme-set-faces</span>
 <span class="ss">'my-cool-theme</span>
 <span class="c1">;; The `t` means "all display types" -- you can also specify different</span>
 <span class="c1">;; colors for different displays (GUI vs 256-color terminal, etc.)</span>
 <span class="o">'</span><span class="p">(</span><span class="nv">default</span> <span class="p">((</span><span class="no">t</span> <span class="p">(</span><span class="ss">:foreground</span> <span class="s">"#c0caf5"</span> <span class="ss">:background</span> <span class="s">"#1a1b26"</span><span class="p">))))</span>
 <span class="o">'</span><span class="p">(</span><span class="nv">font-lock-keyword-face</span> <span class="p">((</span><span class="no">t</span> <span class="p">(</span><span class="ss">:foreground</span> <span class="s">"#bb9af7"</span><span class="p">))))</span>
 <span class="o">'</span><span class="p">(</span><span class="nv">font-lock-string-face</span> <span class="p">((</span><span class="no">t</span> <span class="p">(</span><span class="ss">:foreground</span> <span class="s">"#9ece6a"</span><span class="p">))))</span>
 <span class="c1">;; ... 200+ more faces</span>
 <span class="p">)</span>

<span class="p">(</span><span class="nv">provide-theme</span> <span class="ss">'my-cool-theme</span><span class="p">)</span>
</code></pre></div></div>

<p>This works fine and gives you total control. Many excellent themes are built
exactly this way. In practice, a lot of new themes start their life as copies of
existing themes – mostly to avoid the leg-work of discovering which faces to
define. You grab a well-maintained theme, swap the colors, and you’re halfway
there.</p>

<p>That said, the approach has a couple of pain points:</p>

<ul>
  <li>You need to know what faces exist. Emacs has dozens of built-in faces, and
every popular package adds its own. Miss a few and your theme looks polished
in some buffers but broken in others. <code class="language-plaintext highlighter-rouge">list-faces-display</code> is your friend
here, but it only shows faces that are currently loaded.</li>
  <li>Consistency is on you. With hundreds of face definitions, it’s easy to use
slightly different shades for things that should look the same, or to pick a
color that clashes with your palette. Nothing enforces coherence – you have
to do that yourself.</li>
  <li>Maintaining multiple variants is tedious. Want a light and dark version?
You’re duplicating most of the face definitions with different colors.<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup></li>
</ul>

<p>One more gotcha: some packages use variables instead of faces for their colors
(e.g., <code class="language-plaintext highlighter-rouge">hl-todo-keyword-faces</code>, <code class="language-plaintext highlighter-rouge">ansi-color-names-vector</code>). You can set those
with <code class="language-plaintext highlighter-rouge">custom-theme-set-variables</code>, but you have to know they exist first. It’s
easy to think you’ve themed everything via faces and then discover a package
that hard-codes colors in a defcustom.</p>

<p>How big of a problem the face tracking is depends on your scope. If you only
care about built-in Emacs faces, it’s pretty manageable – that’s what most of
the bundled themes do (check <code class="language-plaintext highlighter-rouge">wombat</code>, <code class="language-plaintext highlighter-rouge">deeper-blue</code>, or <code class="language-plaintext highlighter-rouge">tango</code> – they
define faces almost exclusively for packages that ship with Emacs and don’t
touch third-party packages at all). But if you want your theme to look good in
<code class="language-plaintext highlighter-rouge">magit</code>, <code class="language-plaintext highlighter-rouge">corfu</code>, <code class="language-plaintext highlighter-rouge">vertico</code>, <code class="language-plaintext highlighter-rouge">transient</code>, and a dozen other popular packages,
you’re signing up for ongoing maintenance. A new version of <code class="language-plaintext highlighter-rouge">magit</code> adds a face
and suddenly your theme has gaps you didn’t know about.</p>

<p>I still do things this way for Tokyo Themes and Batppuccin, but the more themes
I maintain the more I wonder if that’s overkill.</p>

<h2 id="every-multi-variant-theme-is-a-mini-framework">Every Multi-Variant Theme Is a Mini Framework</h2>

<p>Here’s something worth pointing out: any theme that ships multiple variants is
already a framework of sorts, whether it calls itself one or not. The moment you
factor out the palette from the face definitions so that multiple variants can
share the same code, you’ve built the core of a theming engine.</p>

<p>Take Tokyo Themes as an example. There are four variants (night, storm, moon, day), but the face
definitions live in a single shared file (<code class="language-plaintext highlighter-rouge">tokyo-themes.el</code>). Each variant is a
thin wrapper – just a <code class="language-plaintext highlighter-rouge">deftheme</code>, a palette alist, and a call to the shared
<code class="language-plaintext highlighter-rouge">tokyo--apply-theme</code> function:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nb">require</span> <span class="ss">'tokyo-themes</span><span class="p">)</span>
<span class="p">(</span><span class="nv">deftheme</span> <span class="nv">tokyo-night</span> <span class="s">"A clean dark theme inspired by Tokyo city lights."</span><span class="p">)</span>
<span class="p">(</span><span class="nv">tokyo--apply-theme</span> <span class="ss">'tokyo-night</span> <span class="nv">tokyo-night-colors-alist</span><span class="p">)</span>
<span class="p">(</span><span class="nv">provide-theme</span> <span class="ss">'tokyo-night</span><span class="p">)</span>
</code></pre></div></div>

<p>That’s the entire theme file. The palette is defined elsewhere, and the face
logic is shared – which is exactly how you solve the variant duplication problem
mentioned earlier. In theory, anyone could define a new palette alist and call
<code class="language-plaintext highlighter-rouge">tokyo--apply-theme</code> to create a fifth variant. The infrastructure is already
there – it’s just not explicitly marketed as a “framework.”</p>

<p>This is exactly how the theming features of packages like Solarized and
Modus evolved. They started as regular themes, grew variants, factored out
the shared code, and eventually exposed that machinery to users.</p>

<h2 id="meta-themes">Meta-Themes</h2>

<p>Some theme packages went a step further and turned their internal infrastructure
into an explicit theming API.</p>

<h3 id="solarized">Solarized</h3>

<p><a href="https://github.com/bbatsov/solarized-emacs">solarized-emacs</a> started as a
straight port of Ethan Schoonover’s Solarized palette, but over time it grew
the ability to create entirely new themes from custom palettes. You can use
<code class="language-plaintext highlighter-rouge">solarized-create-theme-file-with-palette</code> to generate a new theme by supplying
just 10 colors (2 base + 8 accent) – it derives all the intermediate shades
and maps them to the full set of faces:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nv">solarized-create-theme-file-with-palette</span> <span class="ss">'dark</span> <span class="ss">'my-solarized-dark</span>
  <span class="o">'</span><span class="p">(</span><span class="s">"#002b36"</span> <span class="s">"#fdf6e3"</span>                         <span class="c1">;; base colors</span>
    <span class="s">"#b58900"</span> <span class="s">"#cb4b16"</span> <span class="s">"#dc322f"</span> <span class="s">"#d33682"</span>     <span class="c1">;; accents</span>
    <span class="s">"#6c71c4"</span> <span class="s">"#268bd2"</span> <span class="s">"#2aa198"</span> <span class="s">"#859900"</span><span class="p">))</span>
</code></pre></div></div>

<p>This is how Solarized’s own variants (dark, light, gruvbox, zenburn, etc.) are
built internally. I’ll admit, though, that I always found it a bit weird to ship
themes like Gruvbox and Zenburn under the Solarized umbrella. If you install
<code class="language-plaintext highlighter-rouge">solarized-emacs</code> and find a <code class="language-plaintext highlighter-rouge">solarized-gruvbox-dark</code> theme in the list, the
natural reaction is “wait, what does Gruvbox have to do with Solarized?” The
answer is “nothing, really – they just share the theming engine.” That makes
perfect sense once you understand the architecture, but I think it’s confusing
for newcomers. It was part of the reason I was never super excited about this
direction for <code class="language-plaintext highlighter-rouge">solarized-emacs</code>.</p>

<h3 id="modus-themes">Modus Themes</h3>

<p>The <a href="https://github.com/protesilaos/modus-themes">modus-themes</a> take a
different approach. Rather than generating new theme files, they offer deep
runtime customization through palette overrides:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">setq</span> <span class="nv">modus-themes-common-palette-overrides</span>
      <span class="o">'</span><span class="p">((</span><span class="nv">bg-main</span> <span class="s">"#1a1b26"</span><span class="p">)</span>
        <span class="p">(</span><span class="nv">fg-main</span> <span class="s">"#c0caf5"</span><span class="p">)</span>
        <span class="p">(</span><span class="kt">keyword</span> <span class="nv">magenta-warmer</span><span class="p">)))</span>
</code></pre></div></div>

<p>You can override any named color in the palette without touching the theme
source. The result feels like a different theme, but it’s still Modus under the
hood with all its accessibility guarantees. The overrides apply to whichever
Modus variant you load, and <code class="language-plaintext highlighter-rouge">modus-themes-toggle</code> switches between variants
while keeping your overrides intact. Protesilaos’s
<a href="https://github.com/protesilaos/ef-themes">ef-themes</a> share the same
architecture.</p>

<h2 id="theming-frameworks">Theming Frameworks</h2>

<p>If you want to create something brand new rather than customize an existing
theme family, there are a couple of frameworks designed for this.</p>

<h3 id="autothemer">Autothemer</h3>

<p><a href="https://github.com/jasonm23/autothemer">autothemer</a> provides a macro that
replaces the verbose <code class="language-plaintext highlighter-rouge">custom-theme-set-faces</code> boilerplate with a cleaner,
palette-driven approach:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nv">autothemer-deftheme</span>
 <span class="nv">my-theme</span> <span class="s">"A theme using autothemer."</span>
 <span class="c1">;; Display classes: 24-bit GUI, 256-color terminal, 16-color terminal</span>
 <span class="p">((((</span><span class="nc">class</span> <span class="nv">color</span><span class="p">)</span> <span class="p">(</span><span class="nv">min-colors</span> <span class="mi">16777216</span><span class="p">))</span> <span class="p">((</span><span class="nc">class</span> <span class="nv">color</span><span class="p">)</span> <span class="p">(</span><span class="nv">min-colors</span> <span class="mi">256</span><span class="p">))</span> <span class="no">t</span><span class="p">)</span>
  <span class="p">(</span><span class="nv">my-bg</span>    <span class="s">"#1a1b26"</span> <span class="s">"black"</span>   <span class="s">"black"</span><span class="p">)</span>
  <span class="p">(</span><span class="nv">my-fg</span>    <span class="s">"#c0caf5"</span> <span class="s">"white"</span>   <span class="s">"white"</span><span class="p">)</span>
  <span class="p">(</span><span class="nv">my-red</span>   <span class="s">"#f7768e"</span> <span class="s">"red"</span>     <span class="s">"red"</span><span class="p">)</span>
  <span class="p">(</span><span class="nv">my-green</span> <span class="s">"#9ece6a"</span> <span class="s">"green"</span>   <span class="s">"green"</span><span class="p">))</span>

 <span class="c1">;; Face specs -- just reference palette names, no display class noise</span>
 <span class="p">((</span><span class="nv">default</span>         <span class="p">(</span><span class="ss">:foreground</span> <span class="nv">my-fg</span> <span class="ss">:background</span> <span class="nv">my-bg</span><span class="p">))</span>
  <span class="p">(</span><span class="nv">font-lock-keyword-face</span> <span class="p">(</span><span class="ss">:foreground</span> <span class="nv">my-red</span><span class="p">))</span>
  <span class="p">(</span><span class="nv">font-lock-string-face</span>  <span class="p">(</span><span class="ss">:foreground</span> <span class="nv">my-green</span><span class="p">))))</span>
</code></pre></div></div>

<p>You define your palette once as named colors with fallback values for different
display capabilities (GUI frames and terminals support different color depths,
so themes need appropriate fallbacks for each). Then you reference those names in
face specs without worrying about display classes again. Autothemer also provides
some nice extras like SVG palette previews and helpers for discovering unthemed
faces.</p>

<h3 id="base16--tinted-theming">Base16 / Tinted Theming</h3>

<p><a href="https://github.com/tinted-theming/base16-emacs">base16-emacs</a> is part of the
larger <a href="https://github.com/tinted-theming">Tinted Theming</a> ecosystem. The idea
is that you define a scheme as 16 colors in a YAML file, and a builder
generates themes for Emacs (and every other editor/terminal) from a shared
template. You don’t write Elisp at all – you write YAML and run a build step.</p>

<p>This is great if you want one palette to rule all your tools, but you give up
fine-grained control over individual Emacs faces. The generated themes cover a
good set of faces, but they might not handle every niche package you use.</p>

<h2 id="from-scratch-vs-framework-pros-and-cons">From Scratch vs. Framework: Pros and Cons</h2>

<table>
  <thead>
    <tr>
      <th> </th>
      <th>From Scratch</th>
      <th>Meta-Theme / Framework</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Control</td>
      <td>Total – every face is yours</td>
      <td>Constrained by what the framework exposes</td>
    </tr>
    <tr>
      <td>Consistency</td>
      <td>You enforce it manually</td>
      <td>The framework helps (palette-driven)</td>
    </tr>
    <tr>
      <td>Coverage</td>
      <td>You add faces as you discover them</td>
      <td>Inherited from the base theme/template</td>
    </tr>
    <tr>
      <td>Maintenance</td>
      <td>You track upstream face changes</td>
      <td>Shifted to the meta-theme maintainers</td>
    </tr>
    <tr>
      <td>Multiple variants</td>
      <td>Duplicate or factor out yourself</td>
      <td>Built-in support</td>
    </tr>
    <tr>
      <td>Learning curve</td>
      <td>Just <code class="language-plaintext highlighter-rouge">deftheme</code></td>
      <td>Framework-specific API</td>
    </tr>
  </tbody>
</table>

<h2 id="when-to-use-what">When to Use What</h2>

<p>I guess relatively few people end up creating theme packages, but here’s a
bit of general advice for them.</p>

<p>If you want total control over every face and you’re willing to put in the
maintenance work, roll your own. This makes sense for themes with a strong
design vision where you want to make deliberate choices about every element.
It’s more work, but nothing stands between you and the result you want.</p>

<p>If you mostly like an existing theme but want different colors, customizing a
meta-theme (Modus, Solarized, ef-themes) is a good bet. You get battle-tested
face coverage for free, and the palette override approach means you can tweak
things without forking. Keep in mind, though, that the face coverage problem
doesn’t disappear – you’re just shifting it to the meta-theme maintainers. How
comprehensive and up-to-date things stay depends entirely on how diligent they
are.</p>

<p>If you’re creating something new but don’t want to deal with the boilerplate,
use a framework. Autothemer is the best fit if you want to stay in Elisp and
have fine control. Base16/Tinted Theming is the pick if you want one palette
definition across all your tools.</p>

<h2 id="parting-thoughts">Parting Thoughts</h2>

<p>I’m still a “classic” – I like rolling out my themes from scratch. There’s
something satisfying about hand-picking the color for every face. But I won’t
pretend it doesn’t get tedious, especially when you maintain several themes
across multiple variants. Every time a package adds new faces, that’s more
work for me.</p>

<p>If I were starting fresh today, I’d seriously consider Autothemer or building on
top of a meta-theme (extracted from my existing theme packages). The time you
save on maintenance is time you can spend on what actually matters – making
your theme look good.</p>

<p>On the topic of maintenance – one area where AI tools can actually help is
extracting the relevant faces from a list of packages you want to support.
Instead of loading each package, running <code class="language-plaintext highlighter-rouge">list-faces-display</code>, and eyeballing
what’s new, you can ask an LLM to scan the source and give you the face
definitions. It’s also handy for periodically syncing your theme against the
latest versions of those packages to catch newly added faces. Not glamorous
work, but exactly the kind of tedium that AI is good at.</p>

<p>That’s all I have for you today. Keep hacking!</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>Later on you’ll see that’s a pretty easy problem to address. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Bozhidar Batsov</name></author><category term="Themes" /><category term="Packages" /><summary type="html"><![CDATA[Creating Emacs color themes is a topic I hadn’t thought much about in recent years. My first theme (Zenburn) has been in maintenance mode for ages, and Solarized mostly runs itself at this point. But working on my ports of Tokyo (Night) Themes and Catppuccin (Batppuccin) made me re-examine the whole topic with fresh eyes. The biggest shift I’ve noticed is that multi-variant themes (light/dark/high-contrast from a shared codebase) have become the norm rather than the exception, and that pattern naturally leads to reusable theming infrastructure. The task has always been simultaneously easy and hard. Easy because deftheme and custom-theme-set-faces are well-documented and do exactly what you’d expect. Hard because the real challenge was never the mechanics – it’s knowing which faces to theme and keeping your color choices consistent across hundreds of them. Note: In Emacs, a face is a named set of visual attributes – foreground color, background, bold, italic, underline, etc. – that controls how a piece of text looks. Themes work by setting faces to match a color palette. See also the Elisp manual’s section on custom themes for the full API.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://emacsredux.com/assets/og-image.png" /><media:content medium="image" url="https://emacsredux.com/assets/og-image.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Automatic Light/Dark Theme Switching</title><link href="https://emacsredux.com/blog/2026/03/29/automatic-light-dark-theme-switching/" rel="alternate" type="text/html" title="Automatic Light/Dark Theme Switching" /><published>2026-03-29T08:30:00+00:00</published><updated>2026-03-29T08:30:00+00:00</updated><id>https://emacsredux.com/blog/2026/03/29/automatic-light-dark-theme-switching</id><content type="html" xml:base="https://emacsredux.com/blog/2026/03/29/automatic-light-dark-theme-switching/"><![CDATA[<p>Most theme families these days ship both light and dark variants. For example,
<a href="https://github.com/bbatsov/emacs-tokyo-themes">Tokyo Themes</a> has <code class="language-plaintext highlighter-rouge">tokyo-day</code>
(light) alongside <code class="language-plaintext highlighter-rouge">tokyo-night</code>, <code class="language-plaintext highlighter-rouge">tokyo-storm</code>, and <code class="language-plaintext highlighter-rouge">tokyo-moon</code> (all dark).
<a href="https://github.com/bbatsov/batppuccin-emacs">Batppuccin</a> has <code class="language-plaintext highlighter-rouge">batppuccin-latte</code>
(light) and <code class="language-plaintext highlighter-rouge">batppuccin-mocha</code>, <code class="language-plaintext highlighter-rouge">batppuccin-macchiato</code>, <code class="language-plaintext highlighter-rouge">batppuccin-frappe</code>
(dark). But switching between them manually gets old fast. Here are a few ways
to automate it.</p>

<!--more-->

<h2 id="following-the-os-appearance-macos--emacs-plus">Following the OS Appearance (macOS + Emacs Plus)</h2>

<p>If you’re using <a href="https://github.com/d12frosted/homebrew-emacs-plus">Emacs Plus</a>
on macOS (which many Mac users do), you get the hook
<code class="language-plaintext highlighter-rouge">ns-system-appearance-change-functions</code>. This is <strong>not</strong> part of core Emacs –
it’s a <a href="https://github.com/d12frosted/homebrew-emacs-plus/blob/master/patches/emacs-28/system-appearance.patch">patch</a>
that Emacs Plus applies on top of the NS build. The hook fires
whenever macOS switches between light and dark mode, passing the symbol <code class="language-plaintext highlighter-rouge">light</code>
or <code class="language-plaintext highlighter-rouge">dark</code> as an argument. All you need is:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nb">defun</span> <span class="nv">my-apply-theme</span> <span class="p">(</span><span class="nv">appearance</span><span class="p">)</span>
  <span class="s">"Load theme based on APPEARANCE (light or dark)."</span>
  <span class="p">(</span><span class="nb">mapc</span> <span class="nf">#'</span><span class="nv">disable-theme</span> <span class="nv">custom-enabled-themes</span><span class="p">)</span>
  <span class="p">(</span><span class="nv">pcase</span> <span class="nv">appearance</span>
    <span class="p">(</span><span class="ss">'light</span> <span class="p">(</span><span class="nv">load-theme</span> <span class="ss">'tokyo-day</span> <span class="no">t</span><span class="p">))</span>
    <span class="p">(</span><span class="ss">'dark</span> <span class="p">(</span><span class="nv">load-theme</span> <span class="ss">'tokyo-night</span> <span class="no">t</span><span class="p">))))</span>

<span class="p">(</span><span class="nv">add-hook</span> <span class="ss">'ns-system-appearance-change-functions</span> <span class="nf">#'</span><span class="nv">my-apply-theme</span><span class="p">)</span>
</code></pre></div></div>

<p>That’s it. When you flip the system appearance (or the OS does it automatically
based on time of day), Emacs follows along. The <code class="language-plaintext highlighter-rouge">mapc</code> line disables any
currently active themes first – without it, <code class="language-plaintext highlighter-rouge">load-theme</code> stacks the new theme
on top of the old one, which can cause weird color bleed between the two.</p>

<p>This approach is nice because it keeps Emacs in sync with every other app on
your system. If you’re on macOS with Emacs Plus, this is probably the simplest
option.</p>

<p><strong>Note:</strong> This only works with Emacs Plus’s patched NS build. If you’re using a
vanilla Emacs build, or you’re on Linux/Windows, read on.</p>

<h2 id="following-the-os-appearance-cross-platform">Following the OS Appearance (Cross-Platform)</h2>

<p>If you want OS appearance tracking without writing the hook yourself, or you
need it on a platform other than macOS, check out
<a href="https://github.com/LionyxML/auto-dark-emacs">auto-dark</a>. It detects OS-level
dark/light mode changes on macOS, Linux (via D-Bus/GNOME), Windows, and even
Android (Termux):</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nb">use-package</span> <span class="nv">auto-dark</span>
  <span class="ss">:ensure</span> <span class="no">t</span>
  <span class="ss">:config</span>
  <span class="p">(</span><span class="k">setq</span> <span class="nv">auto-dark-themes</span> <span class="o">'</span><span class="p">((</span><span class="nv">batppuccin-mocha</span><span class="p">)</span> <span class="p">(</span><span class="nv">batppuccin-latte</span><span class="p">)))</span>
  <span class="p">(</span><span class="nv">auto-dark-mode</span><span class="p">))</span>
</code></pre></div></div>

<p>The value is a list of two lists – dark theme(s) first, light theme(s) second.
The extra nesting is there because you can stack multiple themes per mode (e.g.,
a base theme plus an overlay). For a single theme per mode, the format above is
all you need. <code class="language-plaintext highlighter-rouge">auto-dark</code> polls the system appearance every few seconds and
switches accordingly. It also provides <code class="language-plaintext highlighter-rouge">auto-dark-dark-mode-hook</code> and
<code class="language-plaintext highlighter-rouge">auto-dark-light-mode-hook</code> if you want to run extra code on each switch.</p>

<h2 id="time-based-switching-with-circadianel">Time-Based Switching with circadian.el</h2>

<p>If you want theme switching based on time of day regardless of your OS, take a
look at <a href="https://github.com/guidoschmidt/circadian.el">circadian.el</a>. It can
switch themes at fixed times or based on your local sunrise/sunset:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nb">use-package</span> <span class="nv">circadian</span>
  <span class="ss">:ensure</span> <span class="no">t</span>
  <span class="ss">:config</span>
  <span class="p">(</span><span class="k">setq</span> <span class="nv">circadian-themes</span> <span class="o">'</span><span class="p">((</span><span class="ss">:sunrise</span> <span class="o">.</span> <span class="nv">batppuccin-latte</span><span class="p">)</span>
                            <span class="p">(</span><span class="ss">:sunset</span>  <span class="o">.</span> <span class="nv">batppuccin-mocha</span><span class="p">)))</span>
  <span class="p">(</span><span class="nv">circadian-setup</span><span class="p">))</span>
</code></pre></div></div>

<p>You can also use fixed hours if you prefer:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">setq</span> <span class="nv">circadian-themes</span> <span class="o">'</span><span class="p">((</span><span class="s">"8:00"</span>  <span class="o">.</span> <span class="nv">batppuccin-latte</span><span class="p">)</span>
                          <span class="p">(</span><span class="s">"20:00"</span> <span class="o">.</span> <span class="nv">batppuccin-mocha</span><span class="p">)))</span>
</code></pre></div></div>

<p>For sunrise/sunset to work, set <code class="language-plaintext highlighter-rouge">calendar-latitude</code> and <code class="language-plaintext highlighter-rouge">calendar-longitude</code> in
your config. <code class="language-plaintext highlighter-rouge">circadian.el</code> uses Emacs’s built-in solar calculations, so no
external services are needed.<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup></p>

<h2 id="rolling-your-own-with-run-at-time">Rolling Your Own with run-at-time</h2>

<p>If you don’t want an extra dependency, you can do something basic with
<code class="language-plaintext highlighter-rouge">run-at-time</code>:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nb">defun</span> <span class="nv">my-set-theme-for-time</span> <span class="p">()</span>
  <span class="s">"Switch theme based on current hour."</span>
  <span class="p">(</span><span class="k">let</span> <span class="p">((</span><span class="nv">hour</span> <span class="p">(</span><span class="nv">string-to-number</span> <span class="p">(</span><span class="nv">format-time-string</span> <span class="s">"%H"</span><span class="p">))))</span>
    <span class="p">(</span><span class="nb">mapc</span> <span class="nf">#'</span><span class="nv">disable-theme</span> <span class="nv">custom-enabled-themes</span><span class="p">)</span>
    <span class="p">(</span><span class="k">if</span> <span class="p">(</span><span class="nb">&lt;=</span> <span class="mi">8</span> <span class="nv">hour</span> <span class="mi">19</span><span class="p">)</span>
        <span class="p">(</span><span class="nv">load-theme</span> <span class="ss">'tokyo-day</span> <span class="no">t</span><span class="p">)</span>
      <span class="p">(</span><span class="nv">load-theme</span> <span class="ss">'tokyo-night</span> <span class="no">t</span><span class="p">))))</span>

<span class="c1">;; Run now and repeat every hour</span>
<span class="p">(</span><span class="nv">run-at-time</span> <span class="no">t</span> <span class="mi">3600</span> <span class="nf">#'</span><span class="nv">my-set-theme-for-time</span><span class="p">)</span>
</code></pre></div></div>

<p>It’s crude compared to <code class="language-plaintext highlighter-rouge">circadian.el</code>, but it works and you can tweak the
schedule however you like.</p>

<h2 id="which-one-should-you-pick">Which One Should You Pick?</h2>

<ul>
  <li><strong>macOS with Emacs Plus and want zero dependencies?</strong> The
<code class="language-plaintext highlighter-rouge">ns-system-appearance-change-functions</code> hook is all you need.</li>
  <li><strong>Want OS tracking on Linux/Windows too?</strong> <code class="language-plaintext highlighter-rouge">auto-dark</code> has you covered.</li>
  <li><strong>Prefer time-based switching?</strong> <code class="language-plaintext highlighter-rouge">circadian.el</code> is the polished option;
the DIY <code class="language-plaintext highlighter-rouge">run-at-time</code> approach works if you want to keep things minimal.</li>
</ul>

<p>What about me? Well, I’m on macOS these days and I do enable the auto-switch
between light/dark mode there. So, normally I’d pick the first option, but
there’s a small catch - I really dislike light themes for programming and I’m
using only dark variants day and night, so I don’t really need theme
auto-switching in Emacs.</p>

<p><strong>Note:</strong> One thing to keep in mind: if you’re using any of these, remove any static
<code class="language-plaintext highlighter-rouge">load-theme</code> call from your init file – let the auto-switching mechanism handle
theme loading, otherwise the two will fight on startup.</p>

<p>As usual, there’s no shortage of ways to solve this in Emacs. Are you doing
auto-switching of themes yourself? Which is your favorite approach?</p>

<p>That’s all I have for you today. Keep hacking!</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>Am I the only one impressed by the fact that the <code class="language-plaintext highlighter-rouge">calendar</code> package can calculate things like sunrise and sunset? <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Bozhidar Batsov</name></author><category term="macOS" /><category term="Packages" /><category term="Themes" /><summary type="html"><![CDATA[Most theme families these days ship both light and dark variants. For example, Tokyo Themes has tokyo-day (light) alongside tokyo-night, tokyo-storm, and tokyo-moon (all dark). Batppuccin has batppuccin-latte (light) and batppuccin-mocha, batppuccin-macchiato, batppuccin-frappe (dark). But switching between them manually gets old fast. Here are a few ways to automate it.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://emacsredux.com/assets/og-image.png" /><media:content medium="image" url="https://emacsredux.com/assets/og-image.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Paredit’s Keybinding Conflicts</title><link href="https://emacsredux.com/blog/2026/03/27/paredit-keybinding-conflicts/" rel="alternate" type="text/html" title="Paredit’s Keybinding Conflicts" /><published>2026-03-27T08:00:00+00:00</published><updated>2026-03-27T08:00:00+00:00</updated><id>https://emacsredux.com/blog/2026/03/27/paredit-keybinding-conflicts</id><content type="html" xml:base="https://emacsredux.com/blog/2026/03/27/paredit-keybinding-conflicts/"><![CDATA[<p>Today’s topic came up while I was going over the list of open Prelude issues
after doing the recent 2.0 release.</p>

<p><a href="https://paredit.org">Paredit</a> and
<a href="https://github.com/Fuco1/smartparens">smartparens</a> are structural
editing packages that keep your parentheses balanced and let you
manipulate s-expressions as units – essential tools for anyone writing
Lisp. Paredit has been around since 2005 and its keybindings have
become muscle memory for a generation of Lisp programmers (yours truly
included). Smartparens inherits the same keymap when used with
<code class="language-plaintext highlighter-rouge">sp-use-paredit-bindings</code>.</p>

<p>The problem is that some of those keybindings conflict with standard
Emacs key prefixes that didn’t exist when paredit was written – or
that have grown more important over time.</p>

<!--more-->

<h2 id="the-commands-and-their-conflicts">The Commands and Their Conflicts</h2>

<p>Before getting to solutions, let’s look at each problematic command –
what it does, where paredit puts it, and what it shadows.</p>

<h3 id="splice--m-s">Splice – <code class="language-plaintext highlighter-rouge">M-s</code></h3>

<p><code class="language-plaintext highlighter-rouge">paredit-splice-sexp</code> (or <code class="language-plaintext highlighter-rouge">sp-splice-sexp</code> in smartparens) removes
the enclosing delimiters around point, “splicing” the contents into
the parent expression:</p>

<div class="language-clojure highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">;; before (point on "b"):</span><span class="w">
</span><span class="p">(</span><span class="nf">a</span><span class="w"> </span><span class="p">(</span><span class="nf">b</span><span class="w"> </span><span class="n">c</span><span class="p">)</span><span class="w"> </span><span class="n">d</span><span class="p">)</span><span class="w">

</span><span class="c1">;; after splice:</span><span class="w">
</span><span class="p">(</span><span class="nf">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="n">d</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p>The conflict: Emacs uses <code class="language-plaintext highlighter-rouge">M-s</code> as the <code class="language-plaintext highlighter-rouge">search-map</code> prefix (since
Emacs 23). Paredit’s splice binding shadows <code class="language-plaintext highlighter-rouge">M-s o</code> (<code class="language-plaintext highlighter-rouge">occur</code>), <code class="language-plaintext highlighter-rouge">M-s .</code>
(<code class="language-plaintext highlighter-rouge">isearch-forward-symbol-at-point</code>), and any <code class="language-plaintext highlighter-rouge">M-s</code>-prefixed bindings
from packages like consult (<code class="language-plaintext highlighter-rouge">consult-line</code>, <code class="language-plaintext highlighter-rouge">consult-ripgrep</code>, etc.).
If you use a completion framework like Vertico + Consult, this one
really hurts.</p>

<h3 id="convolute--m-">Convolute – <code class="language-plaintext highlighter-rouge">M-?</code></h3>

<p><code class="language-plaintext highlighter-rouge">paredit-convolute-sexp</code> (or <code class="language-plaintext highlighter-rouge">sp-convolute-sexp</code>) swaps the nesting
of two enclosing forms. Specifically, it takes the head of the outer
form and moves it inside the inner one:</p>

<div class="language-clojure highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">;; before (point on "c"):</span><span class="w">
</span><span class="p">(</span><span class="nf">a</span><span class="w"> </span><span class="p">(</span><span class="nf">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="n">d</span><span class="p">))</span><span class="w">

</span><span class="c1">;; after convolute -- "a" moved from outer to inner:</span><span class="w">
</span><span class="p">(</span><span class="nf">b</span><span class="w"> </span><span class="p">(</span><span class="nf">a</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="n">d</span><span class="p">))</span><span class="w">
</span></code></pre></div></div>

<p>The conflict: Emacs uses <code class="language-plaintext highlighter-rouge">M-?</code> for <code class="language-plaintext highlighter-rouge">xref-find-references</code> (since
Emacs 25). If you use LSP (Eglot or lsp-mode), paredit’s convolute
binding shadows “find all references” – one of the most useful LSP
features.</p>

<h3 id="slurp--c-right">Slurp – <code class="language-plaintext highlighter-rouge">C-&lt;right&gt;</code></h3>

<p><code class="language-plaintext highlighter-rouge">paredit-forward-slurp-sexp</code> (or <code class="language-plaintext highlighter-rouge">sp-forward-slurp-sexp</code>) expands
the current sexp forward by pulling the next sibling inside the
closing delimiter:</p>

<div class="language-clojure highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">;; before:</span><span class="w">
</span><span class="p">(</span><span class="nf">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="n">c</span><span class="w">

</span><span class="c1">;; after slurp -- "c" pulled inside:</span><span class="w">
</span><span class="p">(</span><span class="nf">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<h3 id="barf--c-left">Barf – <code class="language-plaintext highlighter-rouge">C-&lt;left&gt;</code></h3>

<p><code class="language-plaintext highlighter-rouge">paredit-forward-barf-sexp</code> (or <code class="language-plaintext highlighter-rouge">sp-forward-barf-sexp</code>) is the
opposite – it pushes the last element out past the closing delimiter:</p>

<div class="language-clojure highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">;; before:</span><span class="w">
</span><span class="p">(</span><span class="nf">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="p">)</span><span class="w">

</span><span class="c1">;; after barf -- "c" pushed out:</span><span class="w">
</span><span class="p">(</span><span class="nf">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="n">c</span><span class="w">
</span></code></pre></div></div>

<p>The conflict for both: <code class="language-plaintext highlighter-rouge">C-&lt;right&gt;</code> and <code class="language-plaintext highlighter-rouge">C-&lt;left&gt;</code> override
<code class="language-plaintext highlighter-rouge">right-word</code> and <code class="language-plaintext highlighter-rouge">left-word</code>. Fine if you’re in a Lisp buffer and
know what you’re doing, but surprising if you expected word-level
movement.</p>

<h3 id="splice-killing-backward--m-up">Splice-killing-backward – <code class="language-plaintext highlighter-rouge">M-&lt;up&gt;</code></h3>

<p><code class="language-plaintext highlighter-rouge">paredit-splice-sexp-killing-backward</code> splices (removes delimiters)
and also kills everything <em>before</em> point within the sexp:</p>

<div class="language-clojure highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">;; before (point on "c"):</span><span class="w">
</span><span class="p">(</span><span class="nf">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="n">d</span><span class="p">)</span><span class="w">

</span><span class="c1">;; after splice-killing-backward -- "a b" killed, parens removed:</span><span class="w">
</span><span class="n">c</span><span class="w"> </span><span class="n">d</span><span class="w">
</span></code></pre></div></div>

<h3 id="splice-killing-forward--m-down">Splice-killing-forward – <code class="language-plaintext highlighter-rouge">M-&lt;down&gt;</code></h3>

<p><code class="language-plaintext highlighter-rouge">paredit-splice-sexp-killing-forward</code> does the same but kills
everything <em>after</em> point:</p>

<div class="language-clojure highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">;; before (point on "b"):</span><span class="w">
</span><span class="p">(</span><span class="nf">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="n">d</span><span class="p">)</span><span class="w">

</span><span class="c1">;; after splice-killing-forward -- "c d" killed, parens removed:</span><span class="w">
</span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w">
</span></code></pre></div></div>

<p>The conflict for both: <code class="language-plaintext highlighter-rouge">M-&lt;up&gt;</code> and <code class="language-plaintext highlighter-rouge">M-&lt;down&gt;</code> clash with
<code class="language-plaintext highlighter-rouge">org-metaup</code>/<code class="language-plaintext highlighter-rouge">org-metadown</code> in Org mode, paragraph movement in some
configs, and window manager shortcuts on some Linux desktops.</p>

<h2 id="what-to-do-about-it">What to Do About It</h2>

<p>The good news is that both Matus Goljer (a.k.a. Fuco1, the smartparens
author) and Magnar Sveen (a.k.a. Magnars, the author of
<code class="language-plaintext highlighter-rouge">expand-region</code>, <code class="language-plaintext highlighter-rouge">multiple-cursors</code> and many other popular packages)
have solved these conflicts in their own configs. Their approaches are
worth borrowing.</p>

<p>The examples below use smartparens. For paredit, replace
<code class="language-plaintext highlighter-rouge">smartparens-mode-map</code> with <code class="language-plaintext highlighter-rouge">paredit-mode-map</code> and <code class="language-plaintext highlighter-rouge">sp-*</code> commands
with their <code class="language-plaintext highlighter-rouge">paredit-*</code> equivalents.</p>

<h3 id="splice-m-s">Splice (<code class="language-plaintext highlighter-rouge">M-s</code>)</h3>

<p>Matus’s approach is to rebind to <code class="language-plaintext highlighter-rouge">M-D</code> (meta-shift-d). The mnemonic
is nice – <code class="language-plaintext highlighter-rouge">M-d</code> kills a word, <code class="language-plaintext highlighter-rouge">M-D</code> “kills the delimiters.” This is
probably the most widely copied alternative:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nv">define-key</span> <span class="nv">smartparens-mode-map</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">"M-s"</span><span class="p">)</span> <span class="no">nil</span><span class="p">)</span>
<span class="p">(</span><span class="nv">define-key</span> <span class="nv">smartparens-mode-map</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">"M-D"</span><span class="p">)</span> <span class="nf">#'</span><span class="nv">sp-splice-sexp</span><span class="p">)</span>
</code></pre></div></div>

<p>Magnar’s approach is to rebind to <code class="language-plaintext highlighter-rouge">s-s</code> (super-s). Clean if you’re on
macOS where Super is the Command key:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nv">define-key</span> <span class="nv">smartparens-mode-map</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">"M-s"</span><span class="p">)</span> <span class="no">nil</span><span class="p">)</span>
<span class="p">(</span><span class="nv">define-key</span> <span class="nv">smartparens-mode-map</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">"s-s"</span><span class="p">)</span> <span class="nf">#'</span><span class="nv">sp-splice-sexp</span><span class="p">)</span>
</code></pre></div></div>

<p>You can use both – <code class="language-plaintext highlighter-rouge">M-D</code> everywhere, <code class="language-plaintext highlighter-rouge">s-s</code> as a macOS bonus.</p>

<h3 id="convolute-m-">Convolute (<code class="language-plaintext highlighter-rouge">M-?</code>)</h3>

<p>Convolute-sexp is one of paredit’s more obscure commands. If you use
LSP or xref regularly, freeing <code class="language-plaintext highlighter-rouge">M-?</code> for <code class="language-plaintext highlighter-rouge">xref-find-references</code> is
a net win:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nv">define-key</span> <span class="nv">smartparens-mode-map</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">"M-?"</span><span class="p">)</span> <span class="no">nil</span><span class="p">)</span>
</code></pre></div></div>

<p>If you actually use convolute-sexp, rebind it to something under
a less contested prefix.</p>

<h3 id="slurpbarf-c-arrow">Slurp/barf (<code class="language-plaintext highlighter-rouge">C-&lt;arrow&gt;</code>)</h3>

<p>Magnar moves these to Super:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nv">define-key</span> <span class="nv">smartparens-mode-map</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">"C-&lt;right&gt;"</span><span class="p">)</span> <span class="no">nil</span><span class="p">)</span>
<span class="p">(</span><span class="nv">define-key</span> <span class="nv">smartparens-mode-map</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">"C-&lt;left&gt;"</span><span class="p">)</span> <span class="no">nil</span><span class="p">)</span>
<span class="p">(</span><span class="nv">define-key</span> <span class="nv">smartparens-mode-map</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">"s-&lt;right&gt;"</span><span class="p">)</span> <span class="nf">#'</span><span class="nv">sp-forward-slurp-sexp</span><span class="p">)</span>
<span class="p">(</span><span class="nv">define-key</span> <span class="nv">smartparens-mode-map</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">"s-&lt;left&gt;"</span><span class="p">)</span> <span class="nf">#'</span><span class="nv">sp-forward-barf-sexp</span><span class="p">)</span>
</code></pre></div></div>

<p>Matus keeps the <code class="language-plaintext highlighter-rouge">C-&lt;arrow&gt;</code> bindings (accepting the conflict). This
one’s really a matter of taste – if word-level movement with
<code class="language-plaintext highlighter-rouge">C-&lt;arrow&gt;</code> matters to you, move them. If you’re a Lisp programmer
who slurps more than they word-move, keep them.</p>

<h3 id="splice-killing-m-up--m-down">Splice-killing (<code class="language-plaintext highlighter-rouge">M-&lt;up&gt;</code> / <code class="language-plaintext highlighter-rouge">M-&lt;down&gt;</code>)</h3>

<p>Matus uses <code class="language-plaintext highlighter-rouge">C-M-&lt;backspace&gt;</code> and <code class="language-plaintext highlighter-rouge">C-M-&lt;delete&gt;</code>. Magnar uses
<code class="language-plaintext highlighter-rouge">s-&lt;up&gt;</code> and <code class="language-plaintext highlighter-rouge">s-&lt;down&gt;</code>. Both work well.</p>

<h2 id="the-smartparens-alternative">The Smartparens Alternative</h2>

<p>If you’re using smartparens (rather than paredit), there’s actually a
simpler option – just use smartparens’ own default keybinding set
instead of the paredit compatibility bindings. Set
<code class="language-plaintext highlighter-rouge">sp-base-key-bindings</code> to <code class="language-plaintext highlighter-rouge">'sp</code> (or just don’t set it at all) and
call <code class="language-plaintext highlighter-rouge">sp-use-smartparens-bindings</code> instead of
<code class="language-plaintext highlighter-rouge">sp-use-paredit-bindings</code>.</p>

<p>The default smartparens bindings already avoid most of the conflicts
above:</p>

<table>
  <thead>
    <tr>
      <th>Command</th>
      <th>Paredit binding</th>
      <th>Smartparens binding</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>splice</td>
      <td><code class="language-plaintext highlighter-rouge">M-s</code></td>
      <td><code class="language-plaintext highlighter-rouge">M-D</code></td>
    </tr>
    <tr>
      <td>convolute</td>
      <td><code class="language-plaintext highlighter-rouge">M-?</code></td>
      <td>(unbound)</td>
    </tr>
    <tr>
      <td>slurp</td>
      <td><code class="language-plaintext highlighter-rouge">C-&lt;right&gt;</code></td>
      <td><code class="language-plaintext highlighter-rouge">C-&lt;right&gt;</code></td>
    </tr>
    <tr>
      <td>barf</td>
      <td><code class="language-plaintext highlighter-rouge">C-&lt;left&gt;</code></td>
      <td><code class="language-plaintext highlighter-rouge">C-&lt;left&gt;</code></td>
    </tr>
    <tr>
      <td>splice-killing-backward</td>
      <td><code class="language-plaintext highlighter-rouge">M-&lt;up&gt;</code></td>
      <td><code class="language-plaintext highlighter-rouge">C-M-&lt;backspace&gt;</code></td>
    </tr>
    <tr>
      <td>splice-killing-forward</td>
      <td><code class="language-plaintext highlighter-rouge">M-&lt;down&gt;</code></td>
      <td><code class="language-plaintext highlighter-rouge">C-M-&lt;delete&gt;</code></td>
    </tr>
  </tbody>
</table>

<p>The two big wins are splice moving to <code class="language-plaintext highlighter-rouge">M-D</code> (freeing <code class="language-plaintext highlighter-rouge">search-map</code>)
and convolute not being bound at all (freeing <code class="language-plaintext highlighter-rouge">xref-find-references</code>).
The slurp/barf conflict with word movement remains, but that’s a
trade-off most Lisp programmers are happy to make.</p>

<h2 id="what-about-me">What about me?</h2>

<p>I don’t use most of the commands shadowed by Paredit, so I didn’t
even think about the conflicts much before today. Given that I’m a
macOS user these days I like Magnar’s approach to solving the
conflicts. But I’m also sooo used to pressing <code class="language-plaintext highlighter-rouge">M-s</code>… Decisions,
decisions…</p>

<p>I definitely think everyone should free up <code class="language-plaintext highlighter-rouge">M-?</code>, given the default is quite
important command. For me this was never much of a problem in the past (until
the LSP era) as I’ve always used Projectile’s wrappers around <code class="language-plaintext highlighter-rouge">xref</code> commands –
<code class="language-plaintext highlighter-rouge">projectile-find-references</code> (<code class="language-plaintext highlighter-rouge">s-p ?</code> or <code class="language-plaintext highlighter-rouge">C-c p ?</code>) instead of
<code class="language-plaintext highlighter-rouge">xref-find-references</code>, and <code class="language-plaintext highlighter-rouge">projectile-find-tag</code> (<code class="language-plaintext highlighter-rouge">s-p j</code> or <code class="language-plaintext highlighter-rouge">C-c p j</code>) instead
of <code class="language-plaintext highlighter-rouge">xref-find-definitions</code>. Projectile scopes these to the current project
automatically, which is what I usually want anyway.</p>

<p>I don’t really care about any commands with arrows in them, as I’m
using an HHKB keyboard and it’s not really fun to press arrows on
it…</p>

<h2 id="the-bottom-line">The Bottom Line</h2>

<p>Paredit’s defaults made perfect sense in 2005. Twenty years later,
Emacs has grown <code class="language-plaintext highlighter-rouge">search-map</code>, xref, and a whole ecosystem of packages
that expect those keys to be available. If you’ve been living with
these conflicts out of habit, take five minutes to rebind – your
future self will thank you.</p>

<p>That’s all I have for you today. Keep hacking!</p>]]></content><author><name>Bozhidar Batsov</name></author><category term="Paredit" /><category term="smartparens" /><summary type="html"><![CDATA[Today’s topic came up while I was going over the list of open Prelude issues after doing the recent 2.0 release. Paredit and smartparens are structural editing packages that keep your parentheses balanced and let you manipulate s-expressions as units – essential tools for anyone writing Lisp. Paredit has been around since 2005 and its keybindings have become muscle memory for a generation of Lisp programmers (yours truly included). Smartparens inherits the same keymap when used with sp-use-paredit-bindings. The problem is that some of those keybindings conflict with standard Emacs key prefixes that didn’t exist when paredit was written – or that have grown more important over time.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://emacsredux.com/assets/og-image.png" /><media:content medium="image" url="https://emacsredux.com/assets/og-image.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Emacs Prelude: Redux</title><link href="https://emacsredux.com/blog/2026/03/26/emacs-prelude-redux/" rel="alternate" type="text/html" title="Emacs Prelude: Redux" /><published>2026-03-26T11:50:00+00:00</published><updated>2026-03-26T11:50:00+00:00</updated><id>https://emacsredux.com/blog/2026/03/26/emacs-prelude-redux</id><content type="html" xml:base="https://emacsredux.com/blog/2026/03/26/emacs-prelude-redux/"><![CDATA[<blockquote>
  <p>Programmers know the benefits of everything and the tradeoffs of nothing.</p>

  <p>– Rich Hickey</p>
</blockquote>

<p>Earlier today I wrote about <a href="/blog/2026/03/26/happy-13th-birthday-emacs-redux/">Emacs Redux turning 13</a>. That felt like the perfect
occasion to also ship something I’ve been working towards for a while –
<a href="https://github.com/bbatsov/prelude">Emacs Prelude</a> 2.0.</p>

<!--more-->

<h2 id="a-long-time-coming">A Long Time Coming</h2>

<p>The last tagged Prelude release (1.1) happened all the way back in February 2021.
Five years without a release might sound alarming, but I’d argue it’s a
feature, not a bug. Prelude has always aimed to be a <em>foundation</em> – simple,
stable, easy to understand. I never wanted users to dread pulling upstream
because everything moved under their feet. If you look at some of the more
“sophisticated” Emacs distributions out there, the constant churn and complexity
can be genuinely overwhelming. That’s not the experience I want for Prelude
users.</p>

<p>That said, five years is a long time in Emacs land. Emacs 29 landed with
built-in tree-sitter, Eglot, and <code class="language-plaintext highlighter-rouge">use-package</code>. A bunch of third-party packages
that Prelude depended on became obsolete or unmaintained. It was time for a
proper update.</p>

<h2 id="whats-new">What’s New</h2>

<p>Prelude 2.0 is all about modernizing the distribution for the Emacs 29+ era.</p>

<h3 id="emacs-291-is-now-the-minimum-version">Emacs 29.1 is now the minimum version</h3>

<p>This was the big enabling change. Emacs 29 brought so many things that Prelude
previously had to install or polyfill – <code class="language-plaintext highlighter-rouge">use-package</code>, <code class="language-plaintext highlighter-rouge">display-line-numbers-mode</code>,
<code class="language-plaintext highlighter-rouge">isearch-lazy-count</code>, <code class="language-plaintext highlighter-rouge">use-short-answers</code>, tree-sitter, Eglot – that bumping
the minimum version let me drop a ton of compatibility code and third-party
dependencies in one go. Packages like <code class="language-plaintext highlighter-rouge">nlinum</code>, <code class="language-plaintext highlighter-rouge">anzu</code>, and <code class="language-plaintext highlighter-rouge">epl</code> are gone
entirely, replaced by their built-in equivalents.</p>

<h3 id="tree-sitter-support">Tree-sitter support</h3>

<p>Language modules now automatically use tree-sitter modes (e.g., <code class="language-plaintext highlighter-rouge">python-ts-mode</code>
instead of <code class="language-plaintext highlighter-rouge">python-mode</code>) when a grammar is available, with graceful fallback to
classic modes when it isn’t. This means better syntax highlighting and
structural editing with zero configuration – just install the grammar and
you’re done. Prelude currently supports tree-sitter remapping for C/C++, Go,
Python, JavaScript, TypeScript (including TSX), Ruby, Elixir, Shell, YAML, and
CSS. Some modules like <code class="language-plaintext highlighter-rouge">prelude-ocaml</code> (which uses <code class="language-plaintext highlighter-rouge">neocaml</code>) are tree-sitter-only
by design.</p>

<h3 id="built-in-lsp-via-eglot">Built-in LSP via Eglot</h3>

<p>Most language modules now come with LSP support out of the box, using Eglot as
the default client. No extra packages to install, no configuration to write –
just make sure you have the right language server on your <code class="language-plaintext highlighter-rouge">$PATH</code> and Prelude
handles the rest. Eglot keybindings live under the <code class="language-plaintext highlighter-rouge">C-c C-l</code> prefix (rename,
code actions, format, organize imports), consistent with what lsp-mode users
are used to. If you prefer lsp-mode, set <code class="language-plaintext highlighter-rouge">prelude-lsp-client</code> to <code class="language-plaintext highlighter-rouge">'lsp-mode</code>
in your personal config and Prelude will use it across all language modules
instead.</p>

<h3 id="modernized-language-modules">Modernized language modules</h3>

<p>Python, JavaScript, TypeScript, OCaml, Go, and others have been updated to use
modern tooling. <code class="language-plaintext highlighter-rouge">anaconda-mode</code> is replaced by LSP, <code class="language-plaintext highlighter-rouge">js2-mode</code> by <code class="language-plaintext highlighter-rouge">js-ts-mode</code>,
<code class="language-plaintext highlighter-rouge">tide</code> by <code class="language-plaintext highlighter-rouge">typescript-ts-mode</code>, <code class="language-plaintext highlighter-rouge">tuareg</code> by <code class="language-plaintext highlighter-rouge">neocaml</code>, <code class="language-plaintext highlighter-rouge">alchemist</code> and
<code class="language-plaintext highlighter-rouge">go-projectile</code> are gone (both unmaintained for years). The goal was to bring
every language module up to 2026 standards while keeping them short and
focused – most are still under 50 lines.</p>

<h3 id="faster-startup">Faster startup</h3>

<p>I still stand by my <a href="https://batsov.com/articles/2025/04/07/emacs-startup-time-does-not-matter/">older take that Emacs startup time doesn’t really
matter</a> –
you start Emacs once and it runs for days (or weeks, or months). But when the
fruit hangs this low, why not pick it? Interactive packages are now loaded lazily
via <code class="language-plaintext highlighter-rouge">use-package</code> <code class="language-plaintext highlighter-rouge">:defer</code>, and redundant <code class="language-plaintext highlighter-rouge">require</code> calls have been eliminated
throughout. The old <code class="language-plaintext highlighter-rouge">defadvice</code> calls have been replaced with modern
<code class="language-plaintext highlighter-rouge">define-advice</code> / <code class="language-plaintext highlighter-rouge">advice-add</code>, and a fair amount of dead code has been cleaned
up across the board. Nothing dramatic, but it all adds up to a noticeably
snappier startup for those who care about such things.</p>

<p>There’s a detailed <a href="https://github.com/bbatsov/prelude/blob/master/CHANGELOG.md">changelog</a>
if you want the full picture, and a
<a href="https://github.com/bbatsov/prelude#upgrading-to-prelude-20">migration guide</a> in the
README to help with the upgrade.</p>

<h2 id="the-docs-got-a-facelift">The Docs Got a Facelift</h2>

<p>The <a href="https://prelude.emacsredux.com">documentation site</a> has been updated and
now uses the Material for MkDocs theme, which is a lot nicer to read and
navigate than the old ReadTheDocs default. The content has been refreshed too,
with all modules now properly documented.</p>

<h2 id="whats-next">What’s Next</h2>

<p>There’s more I’d like to do. For instance, I haven’t yet pushed to convert
everything to use <code class="language-plaintext highlighter-rouge">use-package</code> idiomatically – some modules still use the
old <code class="language-plaintext highlighter-rouge">with-eval-after-load</code> / <code class="language-plaintext highlighter-rouge">add-hook</code> style. I’d also like to explore deeper
integration with <code class="language-plaintext highlighter-rouge">project.el</code> and perhaps revisit the module system itself. But
everything is in good shape overall, and I’d rather ship incremental
improvements than hold back a release for perfection.</p>

<h2 id="starter-kits-in-the-age-of-ai">Starter Kits in the Age of AI</h2>

<p>A fair question to ask in 2026 is whether Emacs distributions even matter
anymore. With tools like Claude Code, you can just ask an AI to set up Emacs
however you like – generate an <code class="language-plaintext highlighter-rouge">init.el</code> from scratch, configure LSP, pick a
completion framework, wire up keybindings. Why bother with a starter kit?</p>

<p>I think there are a few reasons Prelude (and projects like it) still matter.</p>

<p>First, AI coding agents are only as good as the code they’ve been trained on.
And right now, the Emacs ecosystem has a serious “popularity inertia” problem –
agents will almost always suggest the older, more established package over a
newer alternative, even when the newer one is clearly better. Ask an AI to set
up OCaml in Emacs and you’ll get <code class="language-plaintext highlighter-rouge">tuareg</code> + <code class="language-plaintext highlighter-rouge">merlin</code> every time, not <code class="language-plaintext highlighter-rouge">neocaml</code> +
<code class="language-plaintext highlighter-rouge">ocaml-eglot</code>. Ask for a completion framework and you’ll get <code class="language-plaintext highlighter-rouge">ivy</code> or <code class="language-plaintext highlighter-rouge">helm</code>,
not <code class="language-plaintext highlighter-rouge">vertico</code> + <code class="language-plaintext highlighter-rouge">marginalia</code>. The training data reflects the past, not the
present. Well-maintained distributions that track the state of the art serve as
a corrective – both for humans browsing GitHub and for the models trained on
it.</p>

<p>Second, there’s real value in curation. An AI can generate a config, but it
can’t tell you which packages play well together, which ones are unmaintained,
or which defaults will bite you six months from now. That kind of judgment comes
from experience, and it’s exactly what a good starter kit encodes.</p>

<p>And third, simplicity still wins. A generated config you don’t understand is
worse than a short, readable one you do. Prelude’s modules are deliberately
small and straightforward – they’re meant to be read, forked, and modified. I’d
rather give someone 20 lines of well-chosen defaults than a 200-line AI-generated
config full of cargo-culted settings.</p>

<p>I wrote more about this topic in <a href="https://batsov.com/articles/2026/03/09/emacs-and-vim-in-the-age-of-ai/">Emacs and Vim in the Age of
AI</a> if
you’re curious.</p>

<h2 id="prelude-and-emacs-redux">Prelude and Emacs Redux</h2>

<p>Emacs Prelude holds a special place in my heart. It was one of my first
open-source projects – I started it back in 2011, two years before this blog
even existed. When I launched Emacs Redux in 2013, many of my early posts were
essentially showcasing features and ideas from Prelude. The two projects grew
up together, and in many ways Prelude was the proving ground for the tips and
workflows that ended up here. It’s fitting that they celebrate together today.</p>

<h2 id="the-return-of-the-prelude">The Return of the Prelude</h2>

<blockquote>
  <p>Simplicity is a great virtue but it requires hard work to achieve it and
education to appreciate it. And to make matters worse: complexity sells better.</p>

  <p>– Edsger W. Dijkstra</p>
</blockquote>

<p>I’ve always believed that slow, deliberate change beats constant reinvention.
It’s not glamorous, it doesn’t generate hype, but it builds something you can
actually rely on. Prelude doesn’t try to be everything to everyone – it tries
to be a solid, understandable starting point that respects your time and
attention.</p>

<p>And here’s a fun bit of trivia to close on: 2026 happens to be the year Honda
brings back the <a href="https://en.wikipedia.org/wiki/Honda_Prelude">Prelude</a>. Very few
people know this, but I was actually considering buying a (pretty old) Honda
Prelude around the time I created Emacs Prelude back in 2011 – that’s where the
name came from! I never did buy the car, but the Emacs distribution turned out
to be a much better investment.<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> And now, 15 years later, both Preludes are making
a comeback. Sometimes things just come full circle.</p>

<p>That’s all I have for you today. Keep hacking!</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>More trivia for you - I did end up buying a BMW E39 in 2010 instead of the Prelude. I still own it and it just turned 26 earlier this month! <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Bozhidar Batsov</name></author><category term="Prelude" /><summary type="html"><![CDATA[Programmers know the benefits of everything and the tradeoffs of nothing. – Rich Hickey Earlier today I wrote about Emacs Redux turning 13. That felt like the perfect occasion to also ship something I’ve been working towards for a while – Emacs Prelude 2.0.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://emacsredux.com/assets/og-image.png" /><media:content medium="image" url="https://emacsredux.com/assets/og-image.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Happy 13th Birthday, Emacs Redux!</title><link href="https://emacsredux.com/blog/2026/03/26/happy-13th-birthday-emacs-redux/" rel="alternate" type="text/html" title="Happy 13th Birthday, Emacs Redux!" /><published>2026-03-26T08:30:00+00:00</published><updated>2026-03-26T08:30:00+00:00</updated><id>https://emacsredux.com/blog/2026/03/26/happy-13th-birthday-emacs-redux</id><content type="html" xml:base="https://emacsredux.com/blog/2026/03/26/happy-13th-birthday-emacs-redux/"><![CDATA[<blockquote>
  <p>13 is my lucky number, so I’m not going to worry about it.<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup></p>

  <p>– Taylor Swift</p>
</blockquote>

<p>Exactly 13 years ago today I published the <a href="/blog/2013/03/26/blastoff/">first Emacs Redux post</a> and kicked off what has become one of the longest
running projects in my life. Time flies!</p>

<!--more-->

<h2 id="some-numbers">Some Numbers</h2>

<p>Over the past 13 years I’ve written 228 articles here. That’s not a lot by
some standards, but I’m pretty happy with the consistency. There hasn’t been a
single year without at least one post – although 2017 came dangerously close
with just 2 articles (written on the 31st of December). Here’s the full breakdown:</p>

<table>
  <thead>
    <tr>
      <th>Year</th>
      <th>Posts</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>2013</td>
      <td>68</td>
    </tr>
    <tr>
      <td>2014</td>
      <td>27</td>
    </tr>
    <tr>
      <td>2015</td>
      <td>9</td>
    </tr>
    <tr>
      <td>2016</td>
      <td>11</td>
    </tr>
    <tr>
      <td>2017</td>
      <td>2</td>
    </tr>
    <tr>
      <td>2018</td>
      <td>6</td>
    </tr>
    <tr>
      <td>2019</td>
      <td>6</td>
    </tr>
    <tr>
      <td>2020</td>
      <td>23</td>
    </tr>
    <tr>
      <td>2021</td>
      <td>20</td>
    </tr>
    <tr>
      <td>2022</td>
      <td>8</td>
    </tr>
    <tr>
      <td>2023</td>
      <td>9</td>
    </tr>
    <tr>
      <td>2024</td>
      <td>5</td>
    </tr>
    <tr>
      <td>2025</td>
      <td>16</td>
    </tr>
    <tr>
      <td>2026</td>
      <td>18</td>
    </tr>
  </tbody>
</table>

<p>2013 was the clear winner – I was on fire after launching the blog, writing
almost 70 posts in a single year. I doubt I’ll ever match that pace again, but
you never know.</p>

<h2 id="the-octopress-dark-ages">The Octopress Dark Ages</h2>

<p>One thing that almost killed my blogging was
<a href="http://octopress.org/">Octopress</a>. When I started Emacs Redux it was the hot
blogging platform for programmers, but over time it became a real pain to work
with. At some point just getting the site to build locally felt like a
chore, and that friction killed my motivation to write. I wrote about
<a href="/blog/2018/11/06/back-in-black/">the migration</a> back in 2018, and
looking at it now I can’t help but smile at this bit:</p>

<blockquote>
  <p>I realized recently that it has been over 10 years since my first blog
post. […] One thing never really changed, though - the quality of my
writing. It was always abysmally bad…</p>
</blockquote>

<p>I also noted there that 2018 marked the blog’s 5th birthday, and that I had
failed to keep up the pace I originally set for myself. Some things don’t
change! But the migration to a plain <a href="https://jekyllrb.com/">Jekyll</a> setup with
no extra layers on top made a real difference – that’s still what I’m using
today, and it gets out of my way completely. The lesson? Keep your publishing
toolchain as simple as possible.</p>

<h2 id="the-editor-landscape">The Editor Landscape</h2>

<p>Lots of things have changed in the editor world over the past 13 years, but my
love for Emacs remains as strong as ever.</p>

<p>Last year I had a lot of fun <a href="https://batsov.com/articles/2025/05/20/rediscovering-vim/">rediscovering
Vim</a> and wrote a
whole series of “How to Vim” articles on
<a href="https://batsov.com">batsov.com</a>. I’ve also spent some time with Helix, Zed,
and even VS Code (mostly for F# development). Playing with all of these only
reinforced my conviction that Emacs is the One True Editor – or at the very
least, the right (most fun) editor for me. There’s nothing quite like it!</p>

<h2 id="recent-activity">Recent Activity</h2>

<p>Some of you might have noticed that Emacs Redux has been more active than usual
over the past few months. Two reasons for that:</p>

<ul>
  <li>I’ve been having a lot of fun working on
<a href="https://github.com/bbatsov/neocaml">neocaml</a>, and new projects always
generate a steady stream of interesting findings worth sharing. I’ve made it a
rule of mine to turn those into blog posts instead of letting them fade from
memory.<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup></li>
  <li>I wanted to celebrate this birthday in style, so I promised myself to push a
bit harder on the blogging front until today. No promises for the rest of the year, though!</li>
</ul>

<h2 id="thank-you">Thank You</h2>

<p>A big thank you to everyone who has been reading Emacs Redux over the past 13
years. And to all the people who have supported my Emacs open-source projects –
whether by contributing code, filing issues, writing docs, donations, or just
spreading the word – you have my gratitude. None of this would be as rewarding
without you!</p>

<h2 id="looking-ahead">Looking Ahead</h2>

<blockquote>
  <p>The best way to predict the future is to invent it.</p>

  <p>– Alan Kay</p>
</blockquote>

<p>We live in a world that’s changing fast, and the future is always uncertain even
in the best of times. But I hope that Emacs (and Emacs Redux) will be alive,
well and relevant for many years to come!</p>

<p>In Emacs we trust! Keep hacking!</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>Same here I guess, given I was born on the 13th. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p>I’ve also been gradually updating most of my Emacs packages, especially those that didn’t get much love in the last couple of years. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Bozhidar Batsov</name></author><category term="Meta" /><summary type="html"><![CDATA[13 is my lucky number, so I’m not going to worry about it.1 – Taylor Swift Exactly 13 years ago today I published the first Emacs Redux post and kicked off what has become one of the longest running projects in my life. Time flies! Same here I guess, given I was born on the 13th. &#8617;]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://emacsredux.com/assets/og-image.png" /><media:content medium="image" url="https://emacsredux.com/assets/og-image.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>