Irreal: Emacs Development: Ongoing And Good

One of the things that we Emacers hear with tiring frequency is that Emacs is moribund, old technology loved only by graybeards stuck in the last century. In fact, as actual users know, Emacs is undergoing constant development and is often the first to offer new features that the other editors copy. The most salient examples are Org mode and Magit but there are plenty of more recent examples.

But don’t take my word for it. Over at the Emacs subreddit, gonstrider has a short post relating his experience with all of this. He’s been using an unnamed editor—you’ll have no problem figuring out which one—that keeps having weekly updates that seem to make things worse each time. So he jumped in the deep end and downloaded and built Master (Emacs 32) fully expecting to find lots of broken things.

It didn’t turn out that way. He was, in fact, surprised to discover that almost everything worked well. The only problem he had was evil on Windows and he solved that by simply upgrading evil. As for the rest, he found that Emacs was super-responsive and all the functionality that seemed laggy in that other editor was snappy in Emacs.

There are two lessons here. The first is that Emacs, far from being moribund, is undergoing continuing development and introducing new features. The second point is that Emacs is, unlike some of its competitors, continuing to produce upgrades that simply work and don’t make their users crazy.

Very shortly, we should see the release of Emacs 31 but work on Emacs 32 is already ongoing and amazingly stable for what is, after all, alpha software. The next time someone tells you that Emacs is old and moribund, refer them to gonstrider’s post.

-1:-- Emacs Development: Ongoing And Good (Post Irreal)--L0--C0--2026-08-12T14:38:54.000Z

TAONAW - Emacs and Org Mode: Back to Solarized

Years ago, when I started the old blog1, I discovered Solarized.

I liked it. The colors were beautiful and worked for me, but eventually I switched to the Gotham theme in Emacs, which, if you take a look, is not much different — just a bit darker. I stayed with this theme for a long time, tweaking it a bit after a trip to New Mexico.

When I received my Mac and started working on it at home, I enjoyed its automatic switching between dark and light themes. At the same time, Prot started coming out with more of his excellent Ef Themes, and I adopted ef-reverie for my light theme and ef-night for my dark one (once again, if you look at the images, you’d see they’re not far off from Solarized, especially my choice of a light theme).

This has been the state of affairs until the beginning of this week, when I “found” Solarized again. I discovered Prot himself was heavily influenced by Solarized, and he discussed it several times on his blog. Here’s part of his background story for his project Prot16:

My starting point for developing a colour scheme was Ethan Schoonover’s Solarized. A true masterpiece. I thus settled on a method of using a 16-colour palette that consists of 8 base values and 8 accents. As with Solarized, the themes would have to be able to transition from ’light’ to ‘dark’ environments with minor alteration to the mapping of colours (though not the values themselves).

Prot’s quest is an interesting one. Like me, he enjoyed Solarized, but eventually decided the colors were not contrasting enough for his taste. His reaction was to create his well-known Modus themes, which, along with his Ef-themes and Prot16, set new theming standards for Emacs.

Meanwhile, I have had the other issue since childhood:

I’ve never liked harsh contrast for this reason. As a matter of fact, a more “gentle” contrasting theme is one of the reasons I like working in Emacs. My website and my wiki are a result of these preferences. The black text on the beige background is comfortable on my eyes. Enough contrast to read, but not enough to burn text into my vision, as many dark modes out there do.

While Prot’s ef-themes offer softer contrast (I didn’t check, but it does seem so visually) which I find excellent, I decided to try out bbatsov’s Solarized for Emacs, which I didn’t know about in the past when I first picked up this theme for Emacs — the official Emacs port is not as good in my opinion.

I’ve learned a couple of things I wasn’t aware of. For starters, you can have many variations of this theme, even though the main 16 colors are still preserved. It makes sense when you think about it: one person can choose Solarized’s red (#dc322f in hex) as an emphasis, while another can select its yellow (#b58900) or orange (#cb4b16), for example. Webpages and Emacs take it a step further, as certain texts can be bold or italicized, with solid underlines or dashed ones, etc. All of those changes take place without touching the colors themselves.

Solarized also helps me with a certain itch I’ve had for a couple of months: a dark theme for my blog. I know most of you read my posts in your RSS reader, hopefully with a theme you enjoy, so this won’t matter much. For me, how my site looks and is styled is important, and getting there is fun.


  1. This post is here, on the new blog, because I migrated it from the old. The date on it is accurate however, and it’s the first post I have saved in my old archive. The same is true about the New Mexico post. ↩︎

-1:-- Back to Solarized (Post TAONAW - Emacs and Org Mode)--L0--C0--2026-08-12T14:09:15.000Z

Ashish Panigrahi: Reviewing finished tasks from last week in org-agenda and emacs

Being a PhD student within a research group entails some structure to tracking your progress as you proceed with research. Within the current team that I'm in, we have weekly group meetings where each member presents the tasks that they've completed during the previous week and discusses any issues they might've faced (if any), to our supervisor.

Usually before the meeting, I jot down things that I've finished with pen and paper. But since I'm in the spirit of integrating my workflow with emacs and orgmode, I thought why don't I try implementing this part of my routine also into orgmode.

Enter org-agenda

All my todo items are tracked with org-agenda where I capture every task I need to finish with org-capture1.

I have a custom org-agenda view just for viewing my daily and weekly TODO items, events/meetings scheduled at a certain time and tasks that have a deadline associated with them. Suffice to say, this workflow has replaced all my needs for having a calendar over the cloud, be it Google, Outlook, what have you2. The only downside that I haven't figured out (yet) is to incorporate calendar invites that I receive via email, into my org-agenda automatically instead of manually capturing an event into the agenda view.

With this custom agenda view, as I finish tasks and mark them DONE, they disappear from the current view. This is by design. I figured I could just review my completed tasks by visiting the relevant files (tasks.org, meetings.org, etc. in my case). But who wants to sift through all the completed tasks and filter them manually to only note down the tasks completed during the previous week? Why not automate this with the power of emacs?

Elisp snippet that implements this weekly review

The idea is fairly simple. I'd like to look at the tasks that I've completed since the last group meeting. This assumes I review these tasks on the day of the group meeting (which I do currently3).

We simply then instruct orgmode to filter out the DONE items from the last 7 days. The org-agenda-custom-commands variable is what we'll use to define this custom org-agenda view.

(setq org-agenda-custom-commands
      '(("w" "Finished tasks from last week" tags "CLOSED>=\"<-7d>\""
         ((org-agenda-overriding-header "Finished tasks from last week")
         (org-agenda-archives-mode t)
         (org-agenda-tag-filter-preset '("-emacs" "-personal" "-email"))
         (org-agenda-prefix-format '((tags . " ")))
         (org-agenda-remove-tags nil)))))

Let's go over the various config options one by one:

  • The first line defines the keybinding in the primary org-agenda dashboard and the associated title for the keybinding.
  • tags "CLOSED>=\"<-7d>\"" is pretty self-explanatory. We filter for items that were completed in the last 7 days.
  • org-agenda-overriding-header: Simply defines the title of the agenda view.
  • org-agenda-archives-mode: This is set to t (True) meaning to also look at the archived entries from last week. I typically archive my entries frequently so this is helpful.
  • org-agenda-tag-filter-preset: I don't want personal tasks appearing in this view, so I remove any items that have the relevant tags.
  • org-agenda-prefix-format: By default, the agenda view contains information about which file the entries belong to (in my case, it's tasks.org). This appears as tasks: per entry in the agenda which unnecessarily clutters the view.
  • org-agenda-remove-tags: I don't want to remove the tags of the various entries (they are useful to me) that appear in the right margin. Hence this variable is set to nil (False).

To invoke the view, we can simply run M-x org-agenda and hit w to get into the view. My org-agenda dashboard looks like this:

My org-agenda dashboard

All the above keys are present in the default view, except my custom views (depicted by j and w keys).

Invoking custom org-agenda views (slightly) faster

For my workflow, I mostly deal with just the two org-agenda views (represented by j and w). It's a good idea to have a global keybinding to quickly invoke these views. Let's define the function and keybinding for it.

(defun pani/done-items-prev-week-org-agenda ()
  "Custom function to immediately jump to my custom org-agenda view."
  (interactive)
  (org-agenda nil "w"))

To invoke the function, we define the keybinding like via a use-package macro:

(use-package org-agenda
  :ensure nil
  ;; Don't need to go through org-agenda template for custom agenda
  :bind (:map global-map
	      ("C-c w" . pani/done-items-prev-week-org-agenda)))

I can then just hit CTRL c w to bring up this new agenda view.

That's it! This should make things easier for me during our group's weekly meetings.

Special thanks to Marci and my sister, Alaka Panigrahi for pointing out grammatical errors and typos.
  1. More on this in a future blog-post.

  2. This is fully offline. Between my personal laptop and office PC, I simply use syncthing to sync all my orgmode files via peer-to-peer.

  3. This is perhaps not the best approach but is a good enough starting point for me.

-1:-- Reviewing finished tasks from last week in org-agenda and emacs (Post Ashish Panigrahi)--L0--C0--2026-08-12T00:00:00.000Z

Irreal: Markup Editing In Org Mode

Marcin Borkowski (mbork) does a lot of writing and, of course, uses Org mode for most of it. The hard part, he says, is not the writing but the editing. In particular, he’s talking about the emphasis markup. If he wants to write a word or region in bold, that’s easy. He merely starts and end it text with a star. Making a word or region bold after the fact—in the editing phase—is more difficult. You have to locate the beginning of the text, add a star, move to the end of the text, and add another star.

Mbork, of course, soon discovered the solution: org-emphjasize. All you need to do is highlight the text and specify the markup character. The problem is that the binding for org-emphasize, Ctrl+c Ctrl+x Ctrl+f, is difficult to type. Mbork solved that writing a function to call org-emphasize with an argument * to cover his most common use. But then he bound it to Ctrl+c Ctrl+x Ctrl+8, which strikes me as just as bad, although you don’t have to specify the *.

My solution for this is to use surround.el that I wrote about here and Bozhidar Batsov wrote about here. I have it bound to Hyper+ so it’s easy to call. I suppose you could even do as mbork did and write a specialized function for a heavily applied use case.

One thing for sure, mbork is right about markup being harder to deal with when editing.

-1:-- Markup Editing In Org Mode (Post Irreal)--L0--C0--2026-08-11T14:43:02.000Z

Raymond Zeitler: How Much Management Does Knowledge Need?

The topic of this month's Emacs Carnival interests me, even though I "dump ... notes into a directory and simply grep for information ...."0 Org does not work the way my brain works,1 or rather, my brain doesn't work the way Org works.

I do use Org. At first I created a few Org files (work.org and home.org, and maybe todo.org) thinking I could rigidly dedicate all my topics to them.

Over time I had about dozen files, one for nearly every major facet of my life until I gave up. Why? Because it really doesn't matter where I put Org TODOs and their notes. When I need to locate something, Org provides a way to search entries for a regular expression, which is what grep does, sans all that Unixy CLI stuff.

So faced with the task of paying one of my wife's medical bills, I could agonize over whether to put the task into wife.org or bills.org or medical.org.2 Or I could just:

  • establish the task somewhere,
  • act on it
  • link to a receipt
  • mark it done3

I created a special template for org-capture, which places these one-off tasks into todo.org. And it clocks into the task, too.

A topic with even greater complexity is "education." I'm a lifelong learner; my edu.org file has (only) 29 headings, all of which have at least a few subheadings. If I learn about an Emacs feature, I might want to write about it, so I'll put a task into blog.org that links to it.

Occasionally a post will span multiple topics in edu.org, topics such as Emacs, Vivaldi, LibreOffice, HTML or LaTeX. In a year from now, which file should I look for it in? The keystroke C-c a s comes to the rescue.4 I might search for "Sacha" because all I remember is that I discovered an Emacs feature on Sacha's website.5 Incidentally, I get 13 hits when I search for "Sacha," one of which was the one I had in mind.

The question I think all Emacs users face is, "At what point does tweaking Emacs (and self-quantifying tools in general) tip the scales from aiding our progress to impeding it?" I dread going down a Knowledge Management rabbit hole,6 but I'm sure I'll enjoy the leap.

0 Charlie Holland's Search for Knowledge

1 A tree works like your brain.

2 Don't even get me started on insurance.

3 All of this occurs while clocked in to the task.

4 In this case, C-c a is bound to org-agenda.

5 Sacha Chua's blog

6 Can one gain knowledge of Knowledge Management Systems without having a Knowledge Management System in which to record that knowledge? That may be a Zen koan for someone.

-1:-- How Much Management Does Knowledge Need? (Post Raymond Zeitler)--L0--C0--2026-08-10T22:19:40.478Z

Marcin Borkowski: Emphasizing a region in Org mode

One of the most basic features of Org mode is its markup. It is basically the Markdown of the Emacs world (although some people, like Karl Voit, claim that it’s superior to Markdown), and it lets do basic things like italic, bold or verbatim very easily – at least if you’re writing text. I, on the other hand, happen to edit text pretty often. This is very different from writing. When I write, typing a slash before and after something I want to emphasize is easy. When I edit and just want to make some text bold, putting stars around it is much less convenient.
-1:-- Emphasizing a region in Org mode (Post Marcin Borkowski)--L0--C0--2026-08-10T20:35:30.000Z

Irreal: Conditional Abbrevs

Protesilaos Stavrou (Prot) has another nice video on something that I was vaguely aware of but had never used: conditional abbreviations. As most Emacers know, you can define abbreviations that will expand into another, usually longer or more complicated, string. It can save a lot of time and you can set them to fire automatically or when Tab is pressed right after the abbreviation.

What’s less well known is that these abbreviations can have properties and one of those properties is to specify a function that will tell Emacs whether or not it should expand the abbreviation. Prot gives a few examples of this. In one example, he wants “Welcome” to expand to a French greeting but only if he is using one of the French input methods. Another example expands an abbreviation only if he’s in a certain directory or one of its subdirectories. A third example expands an abbreviation only if a certain minor mode is active.

Those examples might seem like they’d be complicated but they are, in fact, simple, almost trivial. In the call to define-abbrev, you simply give the name of the deciding function in the :enable-function clause. Check out Prot’s video for the details.

I use several abbreviation systems—including abbrev—but this capability in abbrev is especially nice. It’s easy to use and you can make the firing condition as simple or complex as you need it to be.

Prot’s video is 14 minutes, 33 seconds so it should be easy to find time for it. As with all of Prot’s videos, it’s worth the time.

-1:-- Conditional Abbrevs (Post Irreal)--L0--C0--2026-08-10T14:45:51.000Z

Sacha Chua: 2026-08-10 Emacs news

The Navigation category has a couple of interesting window configuration tips this week in case you prefer to work with just one window or have multiple windows with some of them dedicated to specific buffers.

Links from reddit.com/r/emacs, r/orgmode, r/spacemacs, Mastodon #emacs, Bluesky #emacs, Hacker News, lobste.rs, programming.dev, lemmy.world, lemmy.ml, planet.emacslife.com, YouTube, the Emacs NEWS file, Emacs Calendar, and emacs-devel. Thanks to Andrés Ramírez for emacs-devel links. Do you have an Emacs-related link or announcement? Please e-mail me at sacha@sachachua.com. Thank you!

View Org source for this post

You can e-mail me at sacha@sachachua.com.

-1:-- 2026-08-10 Emacs news (Post Sacha Chua)--L0--C0--2026-08-10T13:49:50.000Z

Andros Fenollosa: Web Design for Terminal Browsers

This is a purely-for-fun exercise: a guide to web design for terminal browsers. Starting from the text browsers people actually use, I am going to pull out a set of rules in case you ever decide to build a site meant to read and look its best in a terminal. It is not an alternative to Gemini, Gopher or a smallweb attempt, but a set of best practices so your site reads well in a terminal browser, and even so you can build Web Apps for the terminal.

Five engines, one test

The first step is to install the five text engines people actually use and feed them the same test pages, isolating one feature at a time.

  • w3m (0.5.6): the most capable with tables, and it even draws images in terminals that allow it.
  • lynx (2.9.3): the patriarch, the lowest common denominator since 1992.
  • links (2.30): fast, with a little bit of CSS.
  • elinks (0.20.0): the cousin with bigger ambitions, it even has an optional CSS engine.
  • EWW: the Emacs one.

Now we look for the compatibility crossovers. You test HTML and CSS features and see how each engine interprets them.

The results:

Feature EWW w3m lynx links elinks
JavaScript No No No No No
<style> / <link> sheets No No No No Partial
display:none inline Hides Shows Shows Hides Shows
display:none by class Shows Shows Shows Shows Shows
inline color Yes (with contrast) Depends on the terminal Depends on the terminal Depends on the terminal Depends on the terminal
text-align No No No No Yes
Data tables Yes Yes Yes (no borders) Yes Yes
colspan / rowspan Yes Yes Flattens Yes Yes
Graphical images Yes In the terminal No No No
alt of a broken image Yes Yes Yes Yes Yes
srcset Yes (picks resolution) alt/src alt/src alt/src alt/src
data: URI in an image Yes alt alt alt alt
GET/POST forms Yes Yes Yes Yes Yes
Collapsible <details> No No No No No
Spacing of <article>, <section>... No No No No No
<base href> Yes Yes Yes Yes Yes
<title>, <pre>, <hr>, lists Yes Yes Yes Yes Yes

From this we draw some general conclusions:

  • JavaScript does not exist. In any of them. It is not slow or partial. There simply is no interpreter.
  • CSS is almost a mirage. Stylesheets, whether <style> or <link>, are ignored in all of them except elinks, which applies a few things like text-align. Four out of five engines do not read your CSS. Design as if it were not there.
  • display:none is a trap. If you hide something with a class (class="hidden" and .hidden{display:none} in your sheet), all five engines show it. Every one. Because they do not read the sheet: do not hide anything important with CSS. If it should not be seen, do not put it in the HTML.
  • Images are text. Only EWW (and w3m in some terminals) actually draws the image. In the rest, the image is its alt attribute. All five fall back to alt when the image is broken, and an image with no alt leaves you a [hero] with the file name, or nothing at all. alt is not accessibility for others, it is your content.
  • <details> does not fold. None of the five make it interactive. The summary and the body always show, one after the other.
  • HTML5 semantic tags are invisible. article, section, nav, header, footer, main, aside: they are transparent containers. They do not add a single line break. Their value is semantic, not visual.

With that, we can lay down a few design lines and best practices.

8 rules for publishing to the terminal

1. DOM order rules

There is no float, no flex, no grid, no order. Whatever you put first in the HTML comes first on screen. So place the content right after opening the <body> and send the long navigation and the footer to the end. A reader who opens your article does not want to tab through thirty menu links before reaching the first sentence.

2. Mark structure with tags, not styles

Real headings <h1>..<h6> for the hierarchy, never a <div class="big-title">. Lists with <ul>/<ol>, definitions with <dl>. Quotes with <blockquote>, which all of them indent. Code with <pre> and <code>. Each engine gives them its own treatment: use them for what they mean.

3. Your page must read with CSS turned off

This is the touchstone. If you disable CSS and your page becomes unreadable, it is not the terminal browser's problem, it is your HTML's problem. Spacing (margin, padding, line-height) does not exist: the separation comes from paragraphs. Structure with real <p>, not with loose <br>.

4. Do not convey information with color alone

A "required field in red" or a "green = correct" evaporate. Inline color is the most fragile thing in the table: it depends on the terminal and its configuration, and in many cases it does not even show. Always pair color with text or a symbol. An "Error:" in front, an asterisk, anything.

5. Tables for data only, never for layout

EWW and w3m draw a surprisingly good ASCII grid, colspan and rowspan included. But a layout table produces an absurd, unreadable grid. Watch the width: if the columns add up to more than the terminal's, the experience degrades. Fewer columns and short cells win.

6. Images with a descriptive alt and srcset

alt is what you see in four out of five engines. Make it a sentence, not an image1.png. And if the image is pure decoration, give it an explicit alt="": that way the reader ignores it instead of reading you the file name. A missing alt and an empty one are not the same thing. Offer srcset with several resolutions, and the engine that can show images will pick the right one. And do not rely on an image to communicate anything critical, because in most engines they do not load at all.

7. Real forms

No JavaScript submissions. A <form> with its action and its method (GET or POST), and an <input type="submit"> or a <button>. Put a name on every field: the engines collect by name, and a field without one is lost. Associate a <label> with each one.

8. Headers that do count

<title> always, descriptive: lynx and links center it at the top, and all of them use it to identify the page. <meta charset> in UTF-8 as an encoding fallback. <base> if you use relative links, which all of them respect. And serve over HTTPS, since several engines flag the certificate status.

TerminalSpeed Insights

A guide you cannot run is a list of good intentions. So I wrote a prototype. It is a Python script that reads an HTML file, or a URL, and gives you back a readability score with the specific warnings. The number is a heuristic, not a science. I hand out the points by eye, an error weighs more than a warning and that is that. What really matters is the list of warnings, not the scoreboard.

You can download it from its repository, TerminalSpeed Insights, and run it against any page:

python3 terminalspeed.py https://your-site.dev/

Running it over a few popular sites, the ones leaning more on content than design, I get some numbers:

Site Score
andros.dev (this blog) 100
text.npr.org (NPR's text-only version) 100
motherfuckingwebsite.com 100
emacswiki.org 94
suckless.org 94
gnu.org 88
man pages on man7.org 88
Wikipedia (an article) 46
Hacker News 55

At the top are the wikis, the docs, the text sites and the ones that fly the minimalism flag. At the bottom, curiously, two of the sites most loved by people who read in the terminal. It is no coincidence: the ones that score high serve the content first and lean on the tags, not on CSS. EmacsWiki, for instance, is almost plain HTML, and that is why it reads on anything that can show text.

Still, these are numbers not every browser shares. Open Hacker News in w3m and you will see the front page perfectly readable, with its numbered list of headlines, despite that 55. w3m draws tables so well that it survives. The validator is not wrong to penalize it, it flags real friction, but friction is not always a death sentence. Take the score as a guide, not a verdict: a 100 almost guarantees it reads well, a low number tells you where to look.

Not just documents: a Terminal Web App

So far I have talked about documents: articles, cards, docs pages. We still have to squeeze the forms: GET and POST work in all five engines. And a form that works is, no more and no less, an application.

Let me show you with a coffee shop:

<form action="/order" method="post">
  <p><label>Your name <input type="text" name="name"></label></p>
  <p><label>Quantity <input type="number" name="qty" value="1"></label></p>
  <p><label><input type="checkbox" name="no_milk" value="yes"> No milk</label></p>
  <p><button type="submit">Order coffee</button></p>
</form>

And this is how it looks in EWW, the Emacs browser. The fields are edited with the keyboard; here I have already filled in the name, the quantity and checked "No milk":

Coffee order form in EWW, the Emacs browser, with the name "Bob", a quantity of 2 and the "No milk" checkbox ticked, and an "Order coffee" button

You hit submit and the <form> does a POST. The server responds with a redirect (the good old Post/Redirect/Get pattern) and the browser paints the confirmation, all without leaving the keyboard or touching the mouse:

Order confirmation in EWW, the Emacs browser: "Order confirmed. Coming right up: 2 coffees without milk for Bob. Total: 2.40 EUR" with a link to place another order

Look at the detail: heading in bold, the link colored, the quantity and the no-milk option collected correctly. It is a stateful app, interactive, with no JavaScript and without a single kilobyte of framework. And yes, it scores 100 with the validator.

It is a real application: it lives in the text browser, but it looks just as good in Chrome or Firefox, because underneath there is only HTML. You get a terminal interface without touching ncurses or any TUI library. Your toolkit is HTML, your renderer is the browser and your logic lives on the server. The same app, a language you already know, and it works everywhere.

Conclusion

It has been a fun investigation. Almost nobody designs their sites for the terminal, it is a minority and, even so, it is an audience that exists. And there is no shortage of reasons: these are visitors who want accessibility or speed, who are on low resolutions or who work with pipes. More than once I have found myself doing quick searches in EWW, or reading an article there for the visual comfort.

You can take it as a curious article or as another approach to web design. I, at least, will start looking at my pages with different eyes.

I hope you enjoyed this other point of view on web design.


Help me keep writing Every coffee gives me a push toward the next article. Sure, it's on me!

Send an email to comment+article-70d817e8@andros.dev to leave a comment. The subject will be ignored.

-1:-- Web Design for Terminal Browsers (Post Andros Fenollosa)--L0--C0--2026-08-10T13:47:15.000Z

James Cherti: single-window.el – Always Open Emacs Buffers in the Current Active Window

Build Status License

The single-window package forces Emacs to open buffers in the current active window.

It keeps your carefully arranged layouts intact, reduces visual clutter, and provides a much more predictable workflow. It handles edge cases by configuring modes like org-mode (src blocks and agenda) to respect the current window.

If this project helps your workflow, please consider supporting it by ⭐ starring single-window on GitHub and sharing it on your website, blog, Mastodon, Reddit, X, LinkedIn, or other social media platforms so other Emacs users can discover its benefits.

Installation and Usage

Emacs: use-package and straight (Emacs version < 30)

To install single-window with straight.el:

  1. It if hasn’t already been done, add the straight.el bootstrap code to your init file.
  2. Add the following code to the Emacs init file:
(use-package single-window
  :straight (single-window
             :type git
             :host github
             :repo "jamescherti/single-window.el")
  :config
  (single-window-mode 1))

Alternative installation: use-package and :vc (Built-in feature in Emacs version >= 30)

To install single-window with use-package and :vc (Emacs >= 30):

(use-package single-window
  :vc (:url "https://github.com/jamescherti/single-window.el"
       :rev :newest)
  :config
  (single-window-mode 1))

Alternative installation: Doom Emacs

Here is how to install single-window on Doom Emacs:

  1. Add to the ~/.doom.d/packages.el file:
(package! single-window
  :recipe
  (:host github :repo "jamescherti/single-window.el"))
  1. Add to ~/.doom.d/config.el:
(after! single-window
  (single-window-mode 1))
  1. Run the doom sync command:
doom sync

Author and License

The single-window Emacs package has been written by James Cherti and is distributed under terms of the GNU General Public License version 3, or, at your choice, any later version.

Copyright (C) 2026 James Cherti

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program.

See also

Links

Other Emacs packages by the same author:

  • compile-angel.el: Speed up Emacs! This package guarantees that all .el files are both byte-compiled and native-compiled, which significantly speeds up Emacs.
  • outline-indent.el: An Emacs package that provides a minor mode that enables code folding and outlining based on indentation levels for various indentation-based text files, such as YAML, Python, and other indented text files.
  • easysession.el: Easysession is lightweight Emacs session manager that can persist and restore file editing buffers, indirect buffers/clones, Dired buffers, the tab-bar, and the Emacs frames (with or without the Emacs frames size, width, and height).
  • vim-tab-bar.el: Make the Emacs tab-bar Look Like Vim’s Tab Bar.
  • elispcomp: A command line tool that allows compiling Elisp code directly from the terminal or from a shell script. It facilitates the generation of optimized .elc (byte-compiled) and .eln (native-compiled) files.
  • tomorrow-night-deepblue-theme.el: The Tomorrow Night Deepblue Emacs theme is a beautiful deep blue variant of the Tomorrow Night theme, which is renowned for its elegant color palette that is pleasing to the eyes. It features a deep blue background color that creates a calming atmosphere. The theme is also a great choice for those who miss the blue themes that were trendy a few years ago.
  • Ultyas: A command-line tool designed to simplify the process of converting code snippets from UltiSnips to YASnippet format.
  • dir-config.el: Automatically find and evaluate .dir-config.el Elisp files to configure directory-specific settings.
  • flymake-bashate.el: A package that provides a Flymake backend for the bashate Bash script style checker.
  • flymake-ansible-lint.el: An Emacs package that offers a Flymake backend for ansible-lint.
  • inhibit-mouse.el: A package that disables mouse input in Emacs, offering a simpler and faster alternative to the disable-mouse package.
  • quick-sdcv.el: This package enables Emacs to function as an offline dictionary by using the sdcv command-line tool directly within Emacs.
  • enhanced-evil-paredit.el: An Emacs package that prevents parenthesis imbalance when using evil-mode with paredit. It intercepts evil-mode commands such as delete, change, and paste, blocking their execution if they would break the parenthetical structure.
  • stripspace.el: Ensure Emacs Automatically removes trailing whitespace before saving a buffer, with an option to preserve the cursor column.
  • persist-text-scale.el: Ensure that all adjustments made with text-scale-increase and text-scale-decrease are persisted and restored across sessions.
  • pathaction.el: Execute the pathaction command-line tool from Emacs. The pathaction command-line tool enables the execution of specific commands on targeted files or directories. Its key advantage lies in its flexibility, allowing users to handle various types of files simply by passing the file or directory as an argument to the pathaction tool. The tool uses a .pathaction.yaml rule-set file to determine which command to execute. Additionally, Jinja2 templating can be employed in the rule-set file to further customize the commands.
  • kirigami.el: The kirigami Emacs package offers a unified interface for opening and closing folds across a diverse set of major and minor modes in Emacs, including outline-mode, outline-minor-mode, outline-indent-minor-mode, org-mode, markdown-mode, vdiff-mode, vdiff-3way-mode, hs-minor-mode, hide-ifdef-mode, origami-mode, yafolding-mode, folding-mode, and treesit-fold-mode. With Kirigami, folding key bindings only need to be configured once. After that, the same keys work consistently across all supported major and minor modes, providing a unified and predictable folding experience.
  • buffer-guardian.el: Automatically saves Emacs buffers without requiring manual intervention. By default, it triggers a save when the user switches to another buffer, switches to another window or frame, Emacs loses focus, or the minibuffer is opened. Beyond standard file buffers, buffer-guardian also manages specialized editing buffers such as org-src and edit-indirect. Additional features, disabled by default, include periodic or idle-time saving of all buffers, automatic exclusion of remote, nonexistent, or large files, and support for custom exclusion rules via regular expressions or predicate functions.
-1:-- single-window.el – Always Open Emacs Buffers in the Current Active Window (Post James Cherti)--L0--C0--2026-08-10T12:17:11.000Z

TAONAW - Emacs and Org Mode: Emacs Config Gems - Part 4

Part four of Emacs Config Gems is about basic file operations and save functions. I realized as I was working on this that I have the frame configuration here, which is the wrong place for it. I will change it after posting. Alright, here we go.


When deleting a file in Emacs, we should use the system’s trash (or recycle bin) instead of deleting the file permanently, giving us a chance to restore the file in case we’ve made a mistake.

In addition, since we have this turned on, we can also tell Emacs to always delete non-empty directories (because they’ll go to the trash) instead of prompting. It’s just faster.

(setq delete-by-moving-to-trash t)
(setq dired-recursive-deletes 'always)

Let’s set the frame Emacs starts with to be a bit bigger. In Emacs, these measurements are in lines and columns (character width), so we’re basically saying: “make the default frame 100 characters wide and 55 lines high.”1

(setq default-frame-alist
   '((width . 100)
     (height . 55))
    )

We might want to move Emacs’ backup files and temp files (those are the ones with ~ and #)2 to a different dedicated folder (emacs-bks) instead of the main Emacs folder. Since Emacs won’t just create the directory for us, let’s ask it to do so3. It won’t throw an error if we already have it.

(make-directory "~/emacs-bks" t)
(setq backup-directory-alist
      `((".*" . "~/emacs-bks")))
(setq auto-save-file-name-transforms
      `((".*" "~/emacs-bks" t)))

Now, let’s turn on the option to automatically save files we are working on inside Emacs, and set how long Emacs needs to be idle before auto-save kicks in. In my case, 300 seconds (= 5 minutes) is good enough for idle time. Importantly, I want to emphasize again, idle time. It will not auto-save the file as long as you type, so if you type non-stop for an hour and suddenly lose power, this will not save you, since Emacs didn’t get the chance to be idle for 5 minutes (or even 30 seconds, which is the default). For this, we have auto-save-interval, which auto-saves every 300 characters (globally in Emacs) by default. In my case, I’m lowering it here to 100, since I write plenty of quick notes (using org-add-note, C-c C-z), and it’s common for me to keep using Emacs between those without hitting the 300 mark. Both of those are described in the manual under auto-saving.

(auto-save-visited-mode t)
(setq auto-save-visited-interval 300)
(setq auto-save-interval 100)

Auto-revert checks the buffer we’re visiting against the saved file itself. If the buffer has no changes, but the saved file does have changes, it loads those changes into the buffer silently. However, if the buffer has changes (meaning, we’re writing something into the file and we haven’t saved yet), it won’t do a thing and won’t say anything. Since my particular setup means that all of my org files are inside a folder synced by Syncthing, this is ideal: if I save changes to a file on my Mac, when I switch to my Linux desktop I’ll have the file and the buffer visiting it refreshed and ready to go, provided of course that Syncthing is working and the sync is complete4.

(global-auto-revert-mode t)

Activate recentf mode, so Emacs has a list of the recent files we worked on last time, up to 30 by my config. 10 is too little; 50 starts to feel more like “everything I opened this month,” which defeats the purpose. Useful when we don’t want to navigate to the same file each time, and we don’t want to have a bookmark. Note: since I’m using consult, recent files are integrated and show under the file header in the consult minibuffer, and can be quickly viewed with f while visiting the consult minibuffer.

(recentf-mode t)
(setq recentf-max-saved-items 30)

  1. There are a couple of more interesting parameters (options) to choose from. For example, alpha is a thing, if you like your windows to be translucent (and you can set it so that the frame in focus has a different degree of alpha than those out of focus). I played with it a bit, but eventually found it to be too distracting. ↩︎

  2. But what are those anyway, and why do we need them? Let’s talk about auto-save first. It’s been there since the beginning. In the early 80s (and even before), when the idea of computers always being on and connected to some virtual cloud was science fiction, people lost their data quite often, either by hardware failure, power failures, and/or user errors, like hanging up the phone in the middle of a data transfer (these were the days of dial-up modems). Auto-saving a file was a feature that was a solution to a very common problem, and we can see it in computer magazines from the era (you can see those in the internet archive, look at page 70 in this magazine for example). As for backups, it’s a similar idea, but the purpose is slightly different. Think of backup literally means “back up.” The idea here is to restore the file from before you saved, backing up (as in going back) to what it was before you touched it. This idea is more closely associated with file-versioning, which Emacs also has, but that’s a slightly different story and it’s turned off by default. To differentiate the auto-save from backup files: In Emacs, auto-saved files are the ones marked with hashtags and the backup files are the ones with a trailing tilde: #autosave-file# and last-save-file~ ↩︎

  3. There are a bunch of these helpful OS-file level functions available to us from inside Emacs. They are scattered in the Lisp manual in the file section, depending what you’re looking for. If you want a quick cheat-sheet of sorts with examples, Xah Lee has something a bit old, but still relevant↩︎

  4. There are other functions around revert, particularly auto-revert-avoid-polling, which is off by default. it’s a bit confusing: auto-revert-avoid-polling means, avoid automatically polling for reverting. By default, this is nil (false) which means polling is on: Emacs does its own polling to check if a file was changed every 5 seconds by default (this is determined by auto-revert-interval). If this is flipped to true, Emacs does not check the file, and instead relies on the OS file notification system (inotify in Linux, FSEvents on macOS). This is good if we want to save battery and CPU cycles: our system checks if files were changed all the time, so Emacs just relies on that. In my case, with Syncthing, this is a bad idea because the OS may or may not catch changes done by Syncthing. My Desktop is always connected to power and my Mac is usually docked so leaving it as default makes sense. Check out the manual for auto-saving↩︎

-1:-- Emacs Config Gems - Part 4 (Post TAONAW - Emacs and Org Mode)--L0--C0--2026-08-10T00:01:32.000Z

Raymond Zeitler: Vakana -- Sneak a Peek

I installed Donovan Ratefison's brand-new Vakana app1 on my Android device this morning. After following the tutorial, I exported the thread as an Org-mode-compatible package in a ZIP and emailed it to myself in order to transfer it to my desktop.

After opening the Org file that Vakana generated, I exported the entire buffer to HTML. The result is shown below. The only change I wanted to make was to scale the bead of each heading 25%.2

It's fortuitous that Donovan Ratefison released Vakana in a month whose Emacs Carnival theme is "The Search for Knowledge," because Vakana provides a unique way to manage one's knowledge. I'll need to become proficient in it before adding it to my workflow. So my eventual Emacs Carnival submission will be more pedestrian.


1 Learn about Vakana here or download here.

2 The images used for beads are SVG files. However this blogging platform does not support that file type, so I converted them to GIF for this post. This should explain why they don't look awesome.

Your first thread

Your first thread
A 90-second tour. Walk it bead by bead, then make it yours — delete it, or reload it anytime from Settings → Pro Tips.

1. The thread

2. Start here · a 90-second tour

[2026-08-09 Sun]

First Bead, Marked 0
Welcome — and if this is all new, that’s perfectly fine. We’ll go gently.

Vakana is the Malagasy word for beads — there’s no other word for them in the language, and it’s where this app takes its name. The idea is old and simple: you turn a moment into a small bead — a little coloured token — and beads you keep together form a thread you can walk back along, like beads on a string. (These six notes are already a thread — “Your first thread.”)

That’s the whole shape of it. What a bead means is always yours — the app never decides that for you.

This is a short, hands-on tour: five steps, each one a thing you try yourself.

Next → 1/5 · Tap the bead.

3. 1/5 · Tap the bead

[2026-08-08 Sat]

Second Bead, Marked 1
,*Tap the bead* at the top of this note — the composer opens. That’s where you pick a bead’s shape, pattern, mood-colour and a tiny glyph (this one’s is the number 1️⃣).

Eight shapes, eight patterns, eight moods — small enough to learn, wide enough for anything. There’s no legend: a circle isn’t “an encounter” unless you decide it is.

How does the user navigate the beads? Now I see "< Prev" and "Next >" so it's obvious.

Next → 2/5 · Open the thread.

4. 2/5 · Open the thread

[2026-08-07 Fri]

Third Bead, Marked 2
You’re on a thread right now — a chain of beads strung together on purpose. Open “Your first thread” (tap its chip here, or find it in the Threads view) and you’ll see all six beads in a row you can walk, bead by bead.

The app also keeps a quiet thread for every day you write, under Days in the Threads view — built for you, no effort.

Next → 3/5 · Comment.

5. 3/5 · Comment — swipe me right

[2026-08-06 Thu]

Some thoughts arrive after the moment. Instead of rewriting a note, leave a comment in its margin.

Try it: tap the 💬 at the top of this note — or, back in your list, swipe this row to the right. Either opens the margin, where you can jot a thought or even set it to remind you later.

(The next bead, 4️⃣, already has a comment — spot the 💬 on its row.)

Next → 4/5 · Long-press the bead.

5.1. Comments

5.1.1. Comment — [2026-08-09 Sun 18:50]

The tutorial shows an icon for comment as a white-filled rectangular balloon with three dots. However the actual icon is a balloon that's an empty circle

6. 4/5 · Long-press a bead in the list

[2026-08-05 Wed]

Fifth Bead, Marked 4
Beads repeat, and you’ll want to find a bead’s kin. This is a thing you do from your list, not from inside a note — so head back to the list first.

There, press and hold any bead: the list instantly filters to every bead like it. (Long-press is the move most people miss.)

Want finer control? Tap the funnel at the top of the list to filter by shape, mood, pattern or glyph, then gather the results into a brand-new thread.

Next → 5/5 · Make it yours.

6.1. Comments

6.1.1. Comment — [2026-08-09 Sun 18:22]

I keep coming back to this one — long-pressing a bead is the move most people miss.

6.1.2. Comment — [2026-08-09 Sun 19:14]

The alarm clock icon next to the note field is for a reminder, not a timestamp. It puts it on the next business day at 9:00 a.m., perhaps. I pressed it at 3:00 p.m. on Sunday August 9th, and then entered the reminder for the next day at 9:00 a.m..

7. 5/5 · Close it, then make it yours

[2026-08-04 Tue]

Sixth Bead, Marked 5
This thread is closed — notice the clasp. Closing marks a loop that finished; you can reopen it anytime.

A finished thread can leave the app: open it and Export to turn it into a web page or a slide deck.

That’s the whole grammar — bead, thread, comment, close, export. Now make it yours:

Delete this tour — swipe any note left, or remove it in one tap from Settings → Pro Tips → Interactive tutorial. • Want it back later? Reload from the same place.

Now write your first real moment, and bead it however you like.

Created: 2026-08-09 Sun 16:42

Validate

-1:-- Vakana -- Sneak a Peek (Post Raymond Zeitler)--L0--C0--2026-08-09T23:56:55.107Z

Chris Maiorana: Using ffap to jump to file includes in Emacs

Lord knows I have trouble finding files sometimes. Over the years I’ve tried lots of schemes in Org Mode and elsewhere for the sole purpose of keeping track of stray items. Using includes in Org Mode has been helpful, but I didn’t have a way of jumping to those included files for quick access. Until now! Read on.

In this article, we’ll look at:

Table of Contents

Let’s get into it.

FFAP – Find file at point

For a refresher on Org includes for those who don’t know, I’d advise clicking on that link above or watching my recent video on this topic. Basically, includes let you include file contents from elsewhere in your system into a working document.

This can be useful for gathering individual files, like sections of a book, into a master document, or including bits of code from live files as examples.

Using ffap to jump to files

However, you may have noticed, these include lines don’t resolve as hyperlinks. So you cannot simply click an included file and jump to it.

This is where you can make use of the ffap (find file at point) command. It’s an interactive function that does exactly what it says: finds the file at your point.

Emascs ffap demo
The ~ffap~ command will present your file to you if the path resolves cleanly.

If the filepath under your point resolves cleanly to a file, absolutely or relatively, you will have the option to open that file presented to you in the minibuffer.

File paths passing and failing

Here’s a rundown of how you can write out your “include” statements in various filepath styles and get the result you want. This example assumes your working directory has an Org master file, and a relative subdirectory or “chapters”. The last instance fails because there would be no such subdirectory at the root level.

Path Result
title_page.org PASS
~/Desktop/ffap-demo/chapters/chapter1.org PASS
chapters/chapter1.org PASS
/chapters/chapter1.org FAIL

If you enjoyed this little article, you may want to check out the following.

As always, thanks for reading, see you next time.

The post Using ffap to jump to file includes in Emacs appeared first on Chris Maiorana.

-1:-- Using ffap to jump to file includes in Emacs (Post Chris Maiorana)--L0--C0--2026-08-09T19:05:34.000Z

Irreal: Deskstop Save Without The Restore

Srijan Choudhary likes desktop-save-mode but he wants a slightly different behavior. He likes having his session configuration saved but he usually doesn’t want it restored. He wants to restore it only when he closed it by mistake or forget do something in the last session. Then, he wants to restore it by hand.

He could, of course, do this by disabling desktop-save-mode and calling the save and restore functions manually. The problem with that solution is that like most of us Choudhary would forget to do the save.

But this is Emacs so Choudhary was able to roll his own solution. That solution does the automatic save, as usual, but the restore function is disabled at system startup. Instead, the last save is added to a list of the last 5 saves. When he wants to restore a session, he’s presented with the list and can choose the save that he wants.

This is another great example of how Emacs lets you have it your way. His code is a little complex but there are a lot of desktop save peculiarities to account for. If you’re like me, you probably aren’t interested in this capability but that’s the point: even if you’re the only one who wants some functionality, Emacs will let you have it. This capability is something no developer is likely to anticipate so it provides another example of the superiority of Emacs’ method over the typical extension system provided by other editors.

Update [2026-08-10 Mon 10:52]: Added link to Choudhary’s post.

-1:-- Deskstop Save Without The Restore (Post Irreal)--L0--C0--2026-08-09T15:13:09.000Z

Lars Ingebrigtsen: Prehistoric Blogging Evidence

I’ve been cleaning out the loft, and I found this in a box. What kind of exciting pictures could lurk within!

Heh heh.

Right… during the 90s, while doing Gnus development, I sold a whole bunch of merchandise. These must be the mugs for Pterodactyl Gnus?

Man, so many shots, and almost all of them out of focus. I’m a master photographer, me.

Hey, that one’s in focus… kinda impressive height, eh?

Right, I didn’t “blog” about this until 2001, and by that time I’d bought a digital camera.

And I’ve still got a couple of mugs, almost three decades later. Quality!

The first batch had non-metallic non-dishwasher-safe printing (it turned out), so those degraded fast, but the second batch, with silver inks, is still going strong.

(Here’s a link to the other Gnus stuff I did back then… The 90s were fun.)

-1:-- Prehistoric Blogging Evidence (Post Lars Ingebrigtsen)--L0--C0--2026-08-09T14:42:25.000Z

Sacha Chua: Replace YouTube captions from Emacs Lisp

I want to be able to easily update YouTube captions from my VTT files. Adding a comment like this:

NOTE
#+YOUTUBE_URL: https://youtu.be/sxqsIgXYkVw
#+LANGUAGE: en

to the first subtitle in my VTT file lets me programmatically replace the captions for the specified language without needing to click through the YouTube interface, saving me at least 9 clicks, a slight delay, and the selection of the file. Now I can just bind it to C-c C-c when I'm working on correcting or translating captions.

;;;###autoload
(defun sacha-youtube-replace-captions (url language vtt-file)
  "Replace captions for URL in LANGUAGE with VTT-FILE."
  (interactive
   (let ((params (and (derived-mode-p 'subed-vtt-mode)
                      (sacha-subed-record-youtube-params))))
     (list
      (or (plist-get params :url)
          (read-string "YouTube URL: "))
      (or (plist-get params :language)
          (read-string "Language (ex: fr): "))
      (if (derived-mode-p 'subed-vtt-mode)
          (buffer-file-name)
        (read-file-name "VTT file: ")))))
  (let* ((video-id (sacha-org-yt-id url))
         (existing-captions (sacha-youtube-get-captions video-id))
         (boundary "---------------------------sacha_yt_caption_boundary"))
    (dolist (item existing-captions)
      (let ((snippet (alist-get 'snippet item)))
        (when (string= (alist-get 'language snippet) language)
          (sacha-youtube-delete-caption (alist-get 'id item)))))
    (let* ((metadata (json-encode `((snippet . ((videoId . ,video-id)
                                                (language . ,language)
                                                (name . ,language))))))
           (text (with-temp-buffer
                   (insert-file-contents vtt-file)
                   (buffer-string)))
           (body (concat "--" boundary "\r\n"
                         "Content-Type: application/json; charset=UTF-8\r\n\r\n"
                         metadata "\r\n"
                         "--" boundary "\r\n"
                         "Content-Type: text/vtt\r\n\r\n"
                         (encode-coding-string text 'utf-8) "\r\n"
                         "--" boundary "--\r\n"))
           (response (request-response-data
                      (request "https://www.googleapis.com/upload/youtube/v3/captions?uploadType=multipart&part=snippet"
                        :type "POST"
                        :headers `(("Authorization" . ,(format "Bearer %s" (sacha-google-access-token)))
                                   ("Content-Type" . ,(format "multipart/related; boundary=%s" boundary))
                                   ("Accept" . "application/json"))
                        :data body
                        :sync t
                        :parser #'json-read))))
      (message "Uploaded.")
      response)))

(defun sacha-youtube-get-captions (video-id)
  "Return the list of existing caption tracks for VIDEO-ID."
  (let ((response (request-response-data
                   (request (format "https://www.googleapis.com/youtube/v3/captions?part=snippet&videoId=%s" video-id)
                            :headers `(("Authorization" . ,(format "Bearer %s" (sacha-google-access-token))))
                            :sync t
                            :parser #'json-read))))
    (alist-get 'items response)))

(defun sacha-youtube-delete-caption (caption-id)
  "Delete the caption track identified by CAPTION-ID."
  (request (format "https://www.googleapis.com/youtube/v3/captions?id=%s" caption-id)
           :type "DELETE"
           :headers `(("Authorization" . ,(format "Bearer %s" (sacha-google-access-token))))
           :sync t))

(defun sacha-subed-record-youtube-params ()
  "Return directives related to YouTube."
  (save-excursion
    (goto-char (point-min))
    (unless (subed-subtitle-msecs-start) (subed-forward-subtitle-start-pos))
    (list
     :url (subed-record-get-directive "#+YOUTUBE_URL")
     :language (or (subed-record-get-directive "#+LANGUAGE")
                   (progn (goto-char (point-min))
                          (when (re-search-forward "^Language: \\(.+\\)\n" (or (save-excursion (re-search-forward "\n\n" nil t))
                                                                               (point-max)))
                            (match-string 1)))))))

The code uses some functions defined elsewhere in my configuration:

This is part of my Emacs configuration.
View Org source for this post

You can e-mail me at sacha@sachachua.com.

-1:-- Replace YouTube captions from Emacs Lisp (Post Sacha Chua)--L0--C0--2026-08-09T14:11:30.000Z

Donovan R.: 🚀 Vakana.mg mobile app is now available

The Vakana.mg wordmark

After months of cooking, I am happy to announce that the mobile app is now
available for download at https://vakana.mg.

Some highlights

  1. Fully offline. No ads, no data held hostage. Your data, your rules.
  2. Flexible exports: Export your threads and beads to HTML (from
    templates), Markdown format (e.g., Obsidian), or rich Org files for Emacs
    users.
  3. Built-in inspiration: Includes a list of use cases and possibilities.
    Find one that fits you.

Feel free to explore and share with friends.

The Vakana.mg app lock screen with the mala logo Your beads of life, laid out as a journal timeline The calendar view, a book of days Threads of life, grouped into collections A thread rendered as a looping bead chain Export a thread to Org, Markdown, HTML, PDF, or Slides
-1:-- 🚀 Vakana.mg mobile app is now available (Post Donovan R.)--L0--C0--2026-08-09T12:37:59.000Z

Protesilaos: Emacs: conditionally expand abbrev-mode definitions

Raw link: https://www.youtube.com/watch?v=QzhU6fklc4o

In this ~15-minute video I show how to add conditions to abbrev-mode abbreviations that you define. The idea is to have those abbrevs expand only in certain cases. I use three examples, which I think cover the common needs.

Code samples

(defun my-french-p ()
  "Return non-nil if `current-input-method' is French."
  (and current-input-method
       (string-match-p "french" current-input-method)))

(define-abbrev global-abbrev-table "welcome" "je t'en prie" nil :enable-function #'my-french-p)


(defvar my-notes-directory (expand-file-name "~/.emacs.d/abbrev-test/")
  "Directory where my notes are.")

(defun my-notes-directory-p ()
  "Return non-nil if we are in `my-notes-directory' or its subdirectories."
  (let ((current-directory (expand-file-name default-directory)))
    (string-prefix-p my-notes-directory current-directory)))

(define-abbrev global-abbrev-table "prot" "p’rǒt" nil :enable-function #'my-notes-directory-p)


(defun my-notes-keycast-mode-p ()
  "Return non-nil if `keycast-mode-line-mode' is enabled."
  (bound-and-true-p keycast-mode-line-mode))

(define-abbrev global-abbrev-table "prot" "Protesilaos, also known as Prot" nil :enable-function #'my-notes-keycast-mode-p)
-1:-- Emacs: conditionally expand abbrev-mode definitions (Post Protesilaos)--L0--C0--2026-08-09T00:00:00.000Z

Irreal: A Customized Agenda View

Ashish Panigrahi has an interesting post on customizing his agenda view. He’s a graduate student, apparently in the sciences, and has to keep track of his tasks and their statuses. The natural solution for us Emacsers is to use Org mode and the agenda view to track these things.

There’s nothing exciting about that, of course, but what makes his post interesting is that he shows how to create a custom agenda view. It’s easy to make a custom view and I have a lot of them but they’re pretty simple. Panigrahi views are more complex and he demonstrates how to specify more intricate criteria for the view.

For his main view, he wants to show tasks that he’s completed since his last group meeting. That means he wants to filter for tasks completed in the last 7 days that aren’t personal items. He also wants to keep tags, so he makes sure they aren’t removed.

Finally, since he often consults this view, he has a function that calls it with a simple Ctrl+c w rather than having to go through the agenda dispatch menu.

As I said, you probably won’t care about his particular view but it’s instructive to see how he defined it as a jumping off place for your own views.

-1:-- A Customized Agenda View (Post Irreal)--L0--C0--2026-08-08T14:46:47.000Z

Irreal: Emacs 31 And The Speedbar

Protesilaos Stavrou (Prot) has another excellent video. This time it’s on it’s on the Emacs speedbar and its Emacs 31 upgrade. The TL;DR is that as of Emacs 31, you can configure the speedbar to be displayed in a separate window instead of a separate frame. The video is 15 minutes, 39 seconds so it should be easy to find time for it.

The placement of the speedbar matters a lot to Prot. He found the speedbar to be unusable when it was displayed in a separate frame because it was difficult to deal with the two related frames. It’s absolutely impossible for me to deal with because I run Emacs as a full screen app so the speedbar is in another workspace and I have to switch to that workspace to see it.

Now that the speedbar can be displayed in the same frame, Prot finds that it can be useful when dealing with large projects with many files. His video is mostly concerned with showing how the speedbar works and what you can do with it.

Lots of people love speedbar-like applications and swear by them. I, however, have never been able to warm up to them. These days, I don’t work with large projects and perhaps I’d feel differently if I did but mostly they just seem like noise to me. Even Prot says he doesn’t use speedbar for his smaller personal projects but that it can be useful for larger, multi-file projects.

My recommendation is that if you don’t already strong opinions on the matter, give the speedbar a try when Emacs 31 comes out and see if it works for you. There are plenty of informed opinions on both sides of the issue so there’s no right answer, just what works for you. As always, Emacs will let you have it your way.

-1:-- Emacs 31 And The Speedbar (Post Irreal)--L0--C0--2026-08-07T14:27:46.000Z

Dave's blog: Run the file in Emacs’ dired

I started going through the The Rust Programming Language to learn rust. I’m using the rustic package to edit files, run cargo, etc.

Having run cargo build in the sample hello_cargo project, I wanted to run the executable file from within Dired. I realized there’s no obvious way to do this in Dired or Dired-X, and that’s probably a good thing. If it was easy I can imagine all sorts of havoc with people accidentally, or intentionally, runnning files out of Dired. Too easy.

But it occurred to me that bash has command to run the next word in the command line as a command. I tried ! command in Dired, but since the directory isn’t in the path, it told me

/bin/bash: line 1: hello_cargo: command not found

Okay, update PATH before running command: ! PATH=.:$PATH command. This actually works, but decades of training give me a queasy feeling about adding . to PATH. How about $PWD instead? ! PATH=$PWD:$PATH command. Yep, that works too.

But of course, with rustic, I can use C-c C-p r to run cargo run to run the command. But trying to run the file on the line in Dired was an interesting diversion!

-1:-- Run the file in Emacs’ dired (Post Dave's blog)--L0--C0--2026-08-07T00:00:00.000Z

Sacha Chua: Emacs Chat avec Richard Bonichon en français

Prot et moi avons parlé en direct avec Richard Bonichon (emacs.d, GitHub) d'Emacs, d'OCaml, de ses flux de travail, de sa configuration et d'autres sujets en français.

J'ai raté le début de la diffusion en direct. Je suis vraiment désolée ! Heureusement, j'ai enregistré toute la conversation localement. Voici la vidéo complète :

J'ai essayé de corriger la transcription (voici le fichier VTT), mais je crois qu'il y a encore beaucoup d'erreurs, donc n'hésitez pas à m'envoyer un message.

Merci beaucoup à Richard, à Prot, et à tous les auditeurs ! À la semaine prochaine pour le 13 août : Emacs Chat avec Fabrice Niessen !

Si vous voulez parler d'Emacs avec Prot et moi en français, n'hésitez pas à me contacter!

La transcription un peu corrigée

Details

Prot: Comment on dit « chat » en français ? Parce que je n'ai pas chat.

Richard: Non, on dit chat.

Prot: Ah, on dit chat. D'accord. C'est facile.

Sacha: Bonjour à tous et à toutes et bienvenue dans ce tout premier épisode d'Emacs Chat en français. J'ai hâte de converser avec notre invité, Richard Bonichon, pour parler d'Emacs, d'Org Mode et d'autres sujets. Comme dans l'épisode d'Emacs Chat en anglais, j'espère que nous pourrons explorer les choses qui ne sont pas évidentes, rien que sa configuration ou son code, comment utiliser et combiner les fonctions dans vos flux de travail les raccourcis clavier le plus utiles, les astuces. J'espère aussi que nous pourrons tous rencontre les personnes passionnées qui font vivre la communauté Emacs. Bonjour Richard, merci beaucoup d'avoir accepté mon invitation.

Richard: Le plaisir, c'est pour moi, en fait, de vous rencontrer tous les deux, en réalité. J'avoue que j'ai accepté en me disant, en voyant tes efforts, Sacha, pour écrire en français et apprendre le français. Et si je pouvais t'aider un tout petit peu avec tout ce que tu donnes à la communauté Emacs, et donc à moi, en t'aidant en français, alors c'est un plaisir de le faire.

Sacha: Et merci aussi à Prot de nous rejoindre pour m'aider à garder une conversation fluide et m'épauler si jamais je me retrouve bloquée par la barrière de la langue. Je vais vous présenter mes excuses d'avance. Je ne suis qu'une débutante en français qui a envie de vidéos intermédiaires sur Emacs, que je puisse écouter mille et une fois. Donc voilà mon plan diabolique. En français, salut Prot!

Prot: Salut Sacha, salut Richard. Allons-y, on va parler.

Sacha: Vous êtes prêts ? Oui. C'est parti. Tout d'abord, Richard, peut-être vous présenter ?

Richard: Oui, alors moi, je suis Richard Bonichon, donc je suis français et je suis un utilisateur d'Emacs depuis bien longtemps. Je peux raconter un peu l'histoire, comment ça est arrivé. Ça a commencé en école d'ingénieur. Donc en France, les études... Pas souvent en dehors de l'université, donc par des écoles dédiées, des écoles d'ingénieurs. Et là, on devait choisir. Et à l'époque, on nous avait encouragé à choisir entre Vi et Emacs sous Solaris. Et plus exactement, c'était l'époque, il fallait prendre X et Emacs. Fin des années 90, début des années 2000, il y avait encore ces deux choix. Moi, j'ai fait des études d'informatique. Aujourd'hui, je suis ingénieur, programmeur, ingénieur logiciel après avoir été chercheur pendant très longtemps dans le domaine des méthodes formelles, des langages de programmation. Et mon langage de programmation préféré, c'est OCaml. Et dans la communauté OCaml, l'éditeur, jusqu'à maintenant ça change un peu, l'éditeur de prédilection pendant très longtemps, c'était Emacs. Parce que c'est lui qui avait le meilleur support pour le langage à travers le mode Tuareg et le mode Caml. Et donc, quand j'apprenais au niveau académique dans mon domaine, tous mes encadrants utilisaient Emacs pour coder en Caml. C'est là que le choix définitif s'est fait et ça n'a fait que s'empirer entre guillemets. Après, j'ai commencé à creuser moi-même et en particulier... Grâce à toi, Sacha, grâce à la newsletter hebdomadaire que tu fournis, parce que ça permet de perdre un temps fou dans tout ce que font les gens sur Emacs et d'aller voir comment ça se fait, ce qu'il y a de nouveau, essayer un nouveau mode ou un nouveau environnement tout le temps. Et j'ai aussi passé le virus après à mes étudiants, donc les encourager à utiliser Emacs et tout ce qu'il y avait dans Emacs, en particulier Org Mode et Magit, qui sont deux points d'entrée super importants et incroyables en termes de qualité pour utiliser Emacs. Et donc, il y en a d'autres qui l'utilisent. Mais je suis un maximaliste, moi, plutôt. On pourra en reparler.

Sacha: Tu utilises Emacs pour ton travail, ton enseignement à l'université avec tes étudiants.

Richard: Oui, quand j'enseignais, j'ai montré tout le temps Emacs. Quand je montrais du code, c'était dans Emacs. Quand je composais mes slides, ma présentation, c'était avec Emacs, avec Beamer et LaTeX. Et puis Org Mode, via LaTeX et Beamer. En fait, je l'utilise. J'ai trois fenêtres en général dans mon OS. J'ai Emacs, un navigateur. Et peut-être un terminal, mais les terminaux maintenant passent essentiellement dans Emacs à travers les différents modes qu'on a, enfin les différentes capacités, que ce soit Eat, Vterm ou Eshell.

Sacha: En lisant ta configuration, j'ai noté que tu as beaucoup de fonctions personnalisées pour Eshell. Donc, tu as bien utilisé ?

Richard: Oui, alors je les ai, comme une bonne partie de ma configuration, je les ai un peu volés des gens. Et notamment, je pense que pour Eshell, ça vient de deux personnes en particulier. Howard Abrams, qui à un moment avait une série de posts incroyables sur l'utilisation d'Org pour documenter et réexécuter tout ce qu'il faisait en DevOps, et notamment parler d'Eshell. Et probablement de John Wiegley, si je ne me trompe pas. Donc, j'ai récupéré des aliases qu'ils avaient et en fait, j'ai toujours cette envie de l'utiliser et à chaque fois, je retombe sur un autre. Mais il est toujours là dans un coin et il est assez personnalisé mais son usage est minimal, j'avoue.

Sacha: Mais tu as une vie de commande très complexe.

Richard: Oui, mais ça c'est pareil. C'est comme beaucoup de trucs, tu sais. C'est l'avantage des communautés ouvertes, c'est qu'on prend quelque chose et puis on le modifie et ça devient le nôtre. Et c'est ça qui est super, en fait, dans Emacs en particulier, mais dans les communautés ouvertes et open source en général, c'est d'avoir accès à toutes ces ressources et souvent d'avoir des gens qui les partagent gratuitement. C'est incroyable quand même ça.

Prot: Et c'est l'esprit d'Emacs comme ça, de prendre quelque chose et de modifier pour faire ce que tu veux.

Richard: Exactement. Ah bah oui, ça c'est clairement... Mais ça, ça me parle beaucoup en plus en tant que programmeur. C'est vraiment adapté à... Alors j'ai essayé d'autres IDE de programmation dans les environnements de développement. Le seul qui trouve un peu grâce à mes yeux, c'est le concurrent principal d'Emacs, dans ma tête en tout cas, c'est la famille Vim, parce que je comprends aussi, mais je me sens à chaque fois extrêmement gêné, restreint et contraint dans un idéal, même dans Visual Code ou ce genre de choses, je me sens... Mal à l'aise. Comment dire ? Je ne sais pas. Il y a trop de choses et on me contraint trop à faire les choses de la manière dont on les a pensées et pas autrement. Et je ne comprends pas comment les adapter en plus. Je sais comment adapter les choses. Ce sont que des fonctions. Donc ça, ça me parle en tant que programmeur fonctionnel. Emacs Lisp, c'est très proche de ma manière de penser. J'ai une formation aussi de mathématicien, bien sûr. Ça me parle très fort. C'est un langage qui est très, très proche dans beaucoup d'aspects de OCaml. Il est à la fois fonctionnel, mais impur. On peut faire beaucoup d'effets, de bords, comme on dit, de changement de structure de données. Et il n'y a pas le typage. Mais c'est autre chose, un autre débat.

Prot: Et tu préfères les modèles OCaml comme ça ou les modèles d'Emacs?

Richard: Alors, sincèrement, le langage où je me sens le mieux quand je programme, où je me sens bien, c'est OCaml. Clairement, ça fait 20 ans, plus de 20 ans que j'utilise et ça me fait plaisir. Je pense que vous connaissez ça tous les deux, mais il y a des moments où on a du plaisir à être dans un outil ou dans un monde qu'on connait. Et OCaml, je connais sur le bout des doigts quoi je connais par cœur. Je prèfère, j'aime moi le typage. Moi, je suis un programmeur qui assume qu'il va faire des erreurs. Le type m'aide en enlever une partie à réfléchir et à structurer ma pensée. C'est un avis comme un autre, mais pour un éditeur, par contre, le modèle d'Emacs Lisp a quand même des avantages. Je ne sais pas comment ça s'est passé, mais il se trouve que c'est quand même bien, les deux se complètent bien. Le langage dans un éditeur, ce langage-là dans un éditeur, la famille Lisp, c'est pas mal.

Sacha: En lisant ta configuration, j'ai aussi noté que tu affiches les informations du mode line dans le header line. Est-ce que c'est plus pratique pour toi?

Richard: Oui, en fait, j'ai tendance à regarder en haut, dans mon buffer. Et j'avais vu, je pense que j'ai pris ce truc-là d'un autre francophone. Donc j'ai oublié le prénom, c'est Rougier. Et c'est de là que j'ai volé cette idée. Je suis un grand voleur, vous savez.

Sacha: Tout le monde.

Richard: Oui, on récupère les idées. Et c'est resté. Voilà, c'est quelque chose que j'ai mis en place et c'est resté. Ça me plaît bien.

Prot: Voler à volonté.

Richard: Ouais, ouais. Et en fait, c'est bien que vous m'ayez un peu forcé à mettre ma config pour ce qu'elle a d'intéressant en public. Comme ça, les autres vont pouvoir voler s'ils veulent. C'est très bien comme ça.

Sacha: Je vois que tu as tant de bibliothèques. Il y en a une qui prépare un rapport sur tes journées de travail. Il y a une autre qui extrait des offres d'emploi sur une page. Un troisième analyse les données Garmin en utilisant l'IA pour générer un rapport au format Org Mode. Tu peux nous en dire plus ? Quelle est ta fonctionnalité préférée ?

Richard: Ah, de celle-là ? Alors, c'est le mode Garmin. C'est le dernier que j'ai mis en place. Donc, moi, je fais pas mal de courses à pied. J'aime bien ça. Et puis, ça me permet de... Moi, c'est là où je fais de la méditation, la course à pied. Je vais courir et puis je pense... Vous savez, quand on est sous la douche ou quelque chose comme ça, on a des idées qui arrivent, la course à pied, c'est à se faire la même chose et puis ça maintient en forme. C'est pas plus mal de prendre un peu soin de sa santé quand on peut. Donc j'ai beaucoup de données Garmin et ça commençait à m'embêter de devoir aller sur le site de Garmin et puis pas adapter à ce que je veux faire. Encore une fois, c'est toujours la même idée et que mes données ne soient que là parce que c'est mes données. Le premier réflexe pour moi, c'est comment je peux les visualiser d'une manière cool sous Emacs. Pour moi, Org Mode, forcément, il faut un truc textuel, donc sous Emacs et Org Mode, parce que pour moi, c'est lié. Et oui, j'ai fait ça à l'aide avec Claude, évidemment, comme beaucoup de gens à l'heure actuelle. Et c'est ça aussi qui est cool dans Emacs, c'est qu'on a pu analyser toute la base de code d'Elisp. Il sait faire ça raisonnablement bien. Oui, il y a plusieurs petits outils. Il y a un bout de code Rust pour extraire en mode JSON les données du format fit de Garmin parce qu'il y a une bonne bibliothèque en Rust tout simplement. Puis je fais du Rust en ce moment, donc voilà. C'est pratique. C'est un langage que j'aime bien. Donc ça, ça fait du JSON. Et après, une fois qu'on a du JSON, on est bien dans Emacs. On n'a plus besoin... J'ai décidé de ne pas tout faire en Elisp. La fonction d'extraction du feed ne paraissait pas... J'ai essayé de demander beaucoup plus de codes que nécessaire pour mon usage. Après, j'ai mis en place une pagination. Je savais que je voulais utiliser le mode tabulated pour les mettre en format colonne. Dans le passé, j'avais déjà utilisé le docker mode. pour gérer plusieurs dockers. Et j'ai trouvé ça génial d'être dans Emacs et de gérer ces dockers, avoir une interface assez simple et d'avoir des raccourcis pour les lancer, les arrêter. Et je me dis, mais c'est ça qu'il me faut. Moi, par moi, une page, je peux lancer l'analyse, parce que j'utilise aussi Claude pour essayer d'analyser mes entraînements, etc. Et les analyser en Org Mode et les avoir, en fait, et avoir le journal de course à pied. Automatiquement généré, l'app en Elisp. Je pense que ça, je l'utilise tous les jours maintenant. Enfin, tous les jours, je vais courir en tout cas. Et les autres, c'est sympa aussi. Alors, les rapports, c'est parce qu'à une époque, on demandait encore hebdomadaire sur mon activité.

Sacha: J'ai oublié de presser le bouton go live.

Prot: Ah non !

Sacha: Je dois présenter toutes mes excuses... J'ai totalement oublié le bouton...

Prot: Pas grave, pas de souci, pas de problème.

Sacha: On recommence. Comment en français on dit que la première edition, c'est pour essuyer les plâtres. Quand on refait un mur, il faut que nettoye. [??]

Prot: Ouais, ouais, très bien.

Richard: On pourra recommencer.

Sacha: Donc, je ne répète pas mon introduction. Tu racontes une histoire fascinante de ses données de Garmin et...

Richard: Je peux recommencer, on recommence. Pas de souci, je peux raconter. On peut commencer directement là-dessus. J'y vais ? La dernière chose pour laquelle j'ai choisi Emacs et que j'aime bien, c'est pour créer un mode pour extraire, pour gérer les données de ma montre Garmin. J'ai un côté maximaliste, donc j'envisage Emacs comme l'endroit où je vais gérer mes données et je vais les visualiser et qui va orchestrer. C'est vraiment mon OS en réalité. Quand nos copains qui sont plutôt adeptes de Vim parlent d'Emacs comme un super OS, j'utilise vraiment comme ça. En fait, c'est l'orchestrateur et c'est là que je vois mes données. Et pour Garmin, j'ai pensé immédiatement à Emacs et derrière à Org Mode pour les voir en textuel. C'est un orchestrateur entre un petit programme Rust qui fait le traitement de la donnée brute en format fit de Garmin, qu'il extrait en JSON, et c'est ça qui est après analysé en Emacs. Retravailler pour avoir une visualisation Org Mode avec un tableau kilomètre par kilomètre, les allures, la fréquence cardiaque, etc. Même un petit... Je crois qu'il y a un petit bout qui est censé faire des graphes aussi. Je ne l'utilise pas trop avec Gnuplot. Je ne sais pas si j'ai bien demandé, si ça marche. Je n'ai pas trop testé. Mais l'idée, c'est vraiment d'avoir tout ce que je trouve intéressant de chez Garmin dans un fichier Org Mode. Donc avec la capacité de le voir localement. Via Emacs. Quand je raconte ce genre de choses à mes étudiants ou mes amis, ils me disent toujours, je ne suis pas trop étonné de tes choix. Quand on me connaît, que je fasse des choses et que ça passe par Org Mode, les gens commencent à ne plus être trop étonnés. J'adore cette idée-là. En fait, c'est tellement... Chouette. J'aime bien l'idée que tout soit en texte, qu'on ait tout autour une gamme d'outils, que ce soit pour faire comme Prot avec Denote ou ce genre de choses. On a des outils autour qui permettent d'utiliser ce format qui a déjà été super bien pensé et qui avait déjà beaucoup de capacités, l'agenda, etc. Moi, j'y pense toujours. Je l'utilise aussi, l'agenda. Je diverge un peu. Désolé, je reviens au mode Garmin. Donc, j'ai une page par mois avec toutes mes sorties, savoir si elles ont été analysées. Je peux lancer l'analyse par IA aussi de ma progression ou de ma régression. Ça arrive aussi. De ce que je devrais faire, de ce qu'ils pensent que je devrais faire. Alors, petit secret, j'arrive quand même assez systématiquement à convaincre l'IA que c'est bien ce que je fais et qu'elle a tort. Elle arrive toujours à se faire convaincre que ça a du sens ce que je fais. Donc l'orchestrateur, une page par mois dans le mode tabulated pour avoir des petites visualisations aussi, ça a été analysé. Je peux marquer, j'ai rajouté, je ne sais pas si je l'ai poussé, j'ai rajouté des marqueurs. Ça, c'est une séance de référence. Si, par exemple, il y avait un test ou une allure particulière qui est importante dans l'entraînement, je peux marquer cette chose-là. Donc, il y a une petite base de données. Il y a des métadatas en format .el, je crois, essentiellement une S-expression, pour rajouter des données perso au-dessus des données extraites de Garmin en plus. Donc qui est lu par Emacs puisque les données sont dans un répertoire et il y a ces données là aussi. Une mini base de données qui est juste en fait une... Pas une, c'est juste une donnée Lisp, quoi. Ça, c'est un exemple. Des trucs qui sont super chouettes en Lisp, c'est qu'on prend un fichier, on a juste la S-expression, il n'y a pas de différence, les données, le programme. Ça, c'est un truc pratique pour travailler sur les données ou les choses.

Sacha: C'est le pouvoir de Lisp.

Richard: Exactement, mais c'est pour ça que je dis que je pense que c'est un cas d'usage où Lisp est particulièrement bien adapté. Je vois les langages de programmation comme ayant tous des capacités quand même plus ou moins particulières. Je n'utiliserais pas, par exemple, même si on a discuté où OCaml était mon langage préféré, je ne l'utiliserais pas pour un Pour un tas de choses. Il y a des langages qui sont plus adaptés. Même si je veux faire des scripts, par exemple, un peu à jeter, j'aurais quand même tendance à faire du Python, personnellement, malgré tout. Même si c'est possible, il n'y a pas tout le reste. Il manque des choses.

Sacha: Et parce que tu utilises Emacs pour créer une interface personnalisée avec le Tabulated et d'autres modules, tu peux créer ton interface dans tout ton flux de travail et tu peux aussi annoter tes données avec Org Mode.

Richard: Exactement. Et je peux les rechercher après avec les tags, comme ça se fait, ou avec les outils standards, Unix, Grep, RG, ce que vous préférez. Tout ça, c'est très bien intégré dans Emacs, donc il n'y a pas besoin de sortir de son monde.

Prot: Est-ce que tu utilises l'agenda aussi pour chercher ces données ?

Richard: Ah, pour chercher ces données ? Non. L'agenda, je suis tombé, j'ai beaucoup limité son usage. J'ai restreint sur quelques fichiers simplement. Peut-être à tort, mais justement, je suis prêt à changer s'il y a des usages que je n'utilise pas trop. Ça fait partie des fichiers exclus parce que je commence à avoir beaucoup de fichiers de notes diverses et variées sur des sujets. Et les séries d'entraînement, elles sont classées dans un sous-répertoire de ces notes. qui sont pour l'instant indexés avec org-roam, mais j'utilise de plus en plus RG pour chercher dedans parce que des fois je cherche du contenu, pas les tags ou le titre. De toute façon, ce n'est pas indexé dans la base SQLite de org-roam.

Prot: D'accord.

Sacha: Tu utilises Org Super Agenda. Oui. En disant ta configuration d'Org Mode, je suis très curieuse parce que tu as une vue d'agenda qui a 27 sections. Oui. Vraiment, ton Super Zaen View.

Richard: C'est peut-être une forme de paresse, mais j'aime bien avoir toute l'information en une fois et donc j'ai pas mal de filtres sur... Est-ce que ça s'agit d'une entreprise ? Est-ce que c'est pour l'entreprise ? Est-ce que c'est pour la maison ? Est-ce que c'est pour le travail mais pas pour l'entreprise ? Donc, à la fin, on arrive avec beaucoup de catégories. Et vous savez, c'est des choses qui ne font que s'accumuler. Donc, c'est une fonction monotone croissante. J'en ajoute, mais j'en enlève jamais. Ça manque de garbage collection. Il faudrait peut-être que je fasse une passe. Pour enlever des choses, il y a effectivement des choses sur dans combien de jours qui filtrent sur la deadline, le schedule. Il y a des choses qui filtrent sur la priorité. Il y a des choses qui filtrent sur le tag. Là, ça me convient à peu près. Du coup, je n'ai pas trop réfléchi à faire mieux, mais c'est possible que ce soit un peu trop fort.

Sacha: Je pense que tu aimes distinguer les données par couleur. Tu as configuré Dired, Elfeed, les priorités d'Org Mode pour colorier les entrées selon le format ou par les étiquettes. Qu'est-ce que ça donne? Peux-tu nous montrer peut-être sur Elfeed?

Richard: Oui. Attends, je vais... Et puis c'est là que ça va être intéressant parce que j'utilise Wayland. Alors c'est toujours le suspense sur le partage d'écran.

Sacha: Pas de souci.

Richard: Mais pas de souci. J'ai pas de souci à partager mon écran. On va voir. User operating system settings. Ok. Il va partager l'écran. Je vais essayer de nous mettre sur un autre. On va se mettre là. Voilà le flux. En fait, les couleurs, elles sont assez... C'est faiblement visible là, comme c'est très grand. Donc c'est surtout des soulignés que j'utilise, des petites couleurs. Je ne suis pas sûr d'y prêter tant attention que ça. Au final. Mais oui, c'est resté. C'est assez pareil. C'est pris du créateur d'Elfeed. J'avais pris de là. Je trouve que c'est ça. Moi, j'aime bien ce que ça donne en termes visuels. Je ne vais pas le changer. Donc là j'utilise un des modes fait par Prot justement. Un des modes de couleurs. Merci. Je suis tombé. Celui-là il me plaît parce qu'il est hyper lisible. Vous voyez moi je suis fond blanc. Je suis dans l'équipe fond blanc.

Prot: Oui, comme moi.

Richard: Ça ne me pose pas de problème. J'ai l'écran, la luminosité diminue un peu avec la fin de journée, ça suffit. Ça suffit pour moi, mais ça dépend des gens. Donc voilà, ça donne ça. Il n'y a pas... Donc tu es là, tu vois, Sacha.

Sacha: Mon journal !

Prot: Et il y a les OCaml News aussi.

Richard: Oui, il y a l'OCaml News.

Prot: Sacha Chua de la communauté OCaml.

Richard: Exactement. En fait, j'ai trois weekly. Il y a trois trucs que je lis systématiquement. Sacha Weekly, Sacha Emacs News, This Week in Rust, et le Weekly News OCaml. Je trouve ces trois blogs où le ratio signal-bruit, comme on dit, est très très élevé. J'en retire beaucoup de choses intéressantes, de curiosités, de choses à aller voir. Et ça, moi, ça m'enthousiasme à chaque fois que j'ai des choses à aller voir. Ah, il y a encore un truc que je ne connais pas. Alors c'est un peu angoissant, on est toujours derrière, on a toujours des choses à faire. Mais c'est quand même plus sympa de voir ce que les gens ont trouvé de nouveau. Que ce soit dans un langage de programmation ou dans Emacs. C'est les trois newsletters que j'aime bien lire. Je sais que ça sera facile, c'est-à-dire que je ne vais pas devoir me concentrer des heures. Par exemple, il y en a où c'est des maths, il faut pouvoir se poser pour lire. Pour lire le post, il faut réfléchir. Et sur les newsletters weekly dont j'ai parlé, c'est hebdomadaire, on dit en français, pas weekly. C'est des choses que je lis avec plaisir.

Sacha: En lisant aussi ta configuration, j'ai noté que tu as des fonctions qui répliquent la fonctionnalité d'org-capture, comme quand tu crées une note sur le rendez-vous ou tu saisis une tâche. Quelle différence... Je consulte mes notes. Pourquoi tu utilises ta propre fonction pour faire ça ?

Richard: C'est une excellente question et je ne suis même pas sûr de quoi tu parles, alors je vais aller voir.

Sacha: Par exemple, tu as une fonction qui te demande sur une tâche et après ça, tu le remplace dans un titre dans ta boîte de réception. Je pense... J'oublie.

Richard: On va aller voir. Dis-moi le nom si tu as la note. Dis-moi... Dis-moi...

Sacha: Je cherche pendant que vous conversez.

Prot: D'accord. Peut-être que c'est plus facile d'avoir une fonction personnalisée.

Richard: Je ne sais pas de quoi exactement on parle.

Prot: Moi aussi, je n'en suis pas sûr.

Richard: Et c'est possible, Sacha, je t'avoue, c'est possible que ce soit quelque chose que je n'utilise plus. C'est possible. Dans ma configuration Emacs, C'est un peu le même problème que le nombre de mes sections dans Super Agenda. Il y a des choses qui arrivent et il y a rarement des choses qui partent. Ça s'ajoute, ça s'ajoute et il manque la personne qui va faire le nettoyage. Je ne sais pas où elle est cette personne.

Sacha: Je l'ai trouvé. Je l'ai trouvé. La fonction rb/org-add-task.

Richard: Ah, bah, j'utilise rb/org. Est-ce qu'elle est cette fonction ?

Sacha: org-add-task.

Richard: Ivy?

Sacha: rb/org-add-task. Pour ajouter une tâche avec un lien...

Richard: Ah oui, c'est... Oui. Je crois que je ne sais pas faire avec Capture. En fait, je pense que je n'ai pas réfléchi à faire avec Capture. Simplement, c'est quand je suis en train de faire quelque chose dans un autre buffer, ça me permet d'avoir un raccourci. Que je ne saurais peut-être pas faire avec Capture, je t'avoue. Oui, tu peux. Rajouter une tâche, mais peut-être qu'il me manque juste la curiosité ou le bon raccourci dans Capture pour faire ça. Je n'ai pas trop réfléchi et on va voir. Oui ? Non ? C'est bien. Bon, je t'avoue, il n'y a pas de raison fondamentale.

Sacha: Je suis seulement curieuse. Tes bibliothèques contiennent un mélange amusant de fonctions qui portent un docstring qui ne sont que « docstrings » entre guillemets, et d'autres fonctions qui ont des docstrings plus longues, plus détaillées. Je pense que tu utilises l'IA pour écrire ça.

Richard: Alors, il y a deux, oui. Dernièrement, c'est l'IA qui fait mes docstrings. Ça, c'est vrai. Dans le passé, ça existait déjà, cependant. Il y avait des fonctions que j'écrivais en me disant que je vais la documenter correctement. Et d'autres, où j'appliquais le fameux paradigme du programmeur, je la documenterais plus tard. Et plus tard, ce n'est pas encore aujourd'hui, visiblement. Ça, vous l'avez jamais. Oui, c'est ça.

Sacha: Moi aussi, je réponds toujours à mes documentations. Prot est bien documenté.

Prot: Non, non, moi je le documente, oui.

Richard: Mais parce que je pense que l'avantage de Prot, je pense qu'il a bien... C'est que lui, il fait vraiment des choses pour être publiées et pour être utilisées. Donc ça donne quand même un coup de pouce à produire de la documentation. Alors que moi, c'est vrai que mon optique, c'est souvent produire des choses pour moi-même, pas trop les... Donc, c'est un peu... On a tous rencontré des ingénieurs ou des programmateurs, la documentation, mais c'est le code. Elle est là, la documentation, c'est le code.

Sacha: Si je n'écris pas [ma] documentation, j'ai totalement oublié la fonction. Après ça, je ne comprends pas.

Richard: Sacha, je suis d'accord avec toi. Mais de là à ce que ça produise une action réelle, c'est là où le problème philosophique arrive. C'est qu'il faut arriver à produire l'action derrière. Mais c'est vrai, on oublie. D'ailleurs, il y a des fonctions dont tu me parles que j'ai oubliées. Celles-là déjà, je les ai quasiment oubliées, celles dont tu viens de me parler. Alors que c'est bien. C'est essentiellement quand je regarde du code pour me mettre une note en disant là il faut que je change quelque chose. Et ça va dans la to-do list. Et ça a un lien vers le code source et le contexte. Et si c'est possible de faire dans capture, je vais regarder. Je vais demander à... Ouais, je vais demander à l'IA, je pense. J'allais dire, soit je vais prendre le temps de le faire, moi, parce que c'est sympa aussi, c'est un peu de l'artisanat. Mais c'est vrai que ça va vite avec l'IA.

Sacha: J'aimerais que tout a beaucoup de priorités.

Richard: Oui, oui. Trop. Trop. Moi-même, je ne suis pas sûr de leur sémantique. Oui, c'est ça. Je crois que j'utilise essentiellement ABC. Ça, je comprends. On parlait des différentes sections. Les autres, oui. C'est essentiellement... En fait, ça va... Ceux qui sont plus tard, essentiellement un jour ça va devenir Someday. Et après ça va faire Cancelled, je pense. Soyons réalistes.

Sacha: Moi aussi, mes fichiers Org Mode sont remplis de tâches annulées.

Richard: Par exemple, je vois ici la YubiKey GPG Stuff. Ça, je sais déjà que ça n'arrivera jamais puisque je commence à passer à Age. Donc, ça ne va pas arriver. Ça n'arrivera jamais. Et ça, je n'ai plus la machine. Par exemple, on peut l'enlever. C'est bien, on va faire ça en direct.

Prot: Voilà la progression.

Sacha: Oui, progress. Tu as une fonction pour suivre ton plan d'études?

Richard: Ah oui, ça c'est pareil. J'avais créé, alors je ne sais même plus, mais c'est vrai, j'ai un répertoire. Alors, parce que comme tout le monde, des fois je cherche, soit je suis curieux de quelque chose. Et donc j'ai utilisé l'IA pour faire ça, pour me générer, c'est assez fort pour faire ça, générer des plans d'études, des sections, et on peut travailler avec elle aussi pour faire des interrogations. C'est pas mal. Et donc, ouais, c'est Study Plan, Study Next Plan. Oui, j'ai plusieurs. Plusieurs plans pour différents domaines de l'informatique.

Sacha: C'est très petit. Pouvez-vous?

Richard: On peut se tutoyer, comme on dit en français. Il y a des trucs en méthode formelle que je n'ai pas accompagné, comme l'arrivée de Lean4. J'aimerais bien prendre un peu de temps pour jouer avec. Il y a deux ou trois fonctions qui permettent de marquer, de passer à l'étape d'après avec Org Mode et un peu de glue autour pour voir quel fichier est en todo. On arrive à faire des étapes. Et c'est bien que tu me le rappelles parce que j'ai du retard là-dessus. Je ne suis pas en avance sur mon study plan, sur mon plan d'étude.

Sacha: C'est un bon plan. Tu utilises l'IA pour générer des plans. Tu as une fonction qui avance à la prochaine étape.

Richard: C'est ça. Donc il y a un format quand même. Le format n'est pas très compliqué. Week, l'index et puis des sujets qui sont marqués. Donc l'index permet de savoir ce qu'il y a à faire et d'avoir une vue d'ensemble des sujets. Donc si je travaille avec l'IA, je lui demande quand même d'aller suivre certaines choses. Par expérience, je sais que c'est un sujet sur lequel on peut passer toute une vie. On délimite quand même. J'ai utilisé l'IA aussi pour faire des interviews croisées sur des sujets. Vous savez les interviews techniques en informatique sont particulièrement pénibles. Donc pour faire une simulation d'interview technique. Avec l'IA qui fait une impersonnation de quelqu'un qui serait un expert en méthode formelle et qui travaillerait sur tel sujet et qui vous pose des questions. Comme si c'était une interview pour être... Pour être embauché pour travailler dans une entreprise.

Sacha: Tu le fais dans un fichier Org Mode ?

Richard: Je garde la trace dans un fichier Org Mode. Je garde la trace d'exécution dans un fichier Org Mode pour pouvoir repasser dessus et réétudier. C'est un peu des techniques de mémorisation classiques. C'est-à-dire, on fait une première stimulation, puis on revient un peu après pour rafraîchir des choses qu'on a travaillées. La première stimulation, c'est aussi, oups, ça, je ne connais pas du tout, je vais devoir aller chercher. Ce qu'on fait dans ce cadre-là, parce qu'en fait, je ne le connais pas. Je ne sais pas. J'ai une idée, peut-être. Ou peut-être mon idée est fausse, d'ailleurs. Il faut la corriger. Donc ça, c'est la première stimulation. C'est face à la question, se mettre face à son ignorance. Confronté à son ignorance. On peut soit demander à l'IA de nous aider un peu, soit d'aller chercher soi-même selon le temps qu'on a et selon ce qu'on juge le plus efficace. Mais après, il faut repasser dessus, sinon on oublie. C'est juste une bonne sensation. Mais après, ça ne sert pas à grand-chose en termes d'apprentissage.

Sacha: Il y a une question associée au chat sur la gestion des idées. « Ma question est la suivante. Je pense que c'est une rabbit hole au lieu de se focaliser sur les choses les plus importantes, au lieu de créer des projets et se concentrer sur les dot files, etc. » Je pense que tout le monde a du mal avec la gestion d'attention.

Richard: Oui, alors... En fait, un des problèmes, c'est tes notes hebdomadaires, Sacha.

Sacha: Pour moi aussi !

Richard: Elle donne tellement d'ouverture. C'est super ce que fait personne. Je vais faire pareil. C'est intéressant. Et on se retrouve deux heures plus tard. On a juste avancé sa configuration Emacs. Mais ce n'est pas grave. J'ai pris mon parti. Ça fait partie des activités que je fais dans la semaine. Ça fait partie des choses. Je pense, en réalité, je crois que nos emplois du temps sont moins pleins que ce qu'on croit qu'ils sont. Et moi, j'admis que Emacs, ça faisait partie des outils que j'aimais utiliser et comme tous les outils qu'on aime utiliser, il faut les entretenir. Donc, passer un peu de temps sur l'entretien et regarder des choses parce que c'est agréable. Voilà, donc j'ai admis qu'il y aurait du temps passé sur la configuration, et c'est pas grave parce que c'est un outil, il y a plein de gens, Emacs c'est un outil pour une vie, pour moi. Donc si on passe une heure par semaine, c'est pas grave, dans 20 ans, si on est encore là, ça sera quelque chose qui va être, qui aura un bénéfice si ce n'est juste pour la connaissance, juste pour le plaisir. C'est pas grave. Moi, c'est un truc que j'aime bien. On perd du temps entre guillemets, mais c'est pas vrai. Quand on fait des choses qu'on aime bien, on perd pas du temps, je pense.

Prot: Oui, oui, oui.

Sacha: Bien évidemment, tu utilises Emacs pendant plus de 20 ans.

Richard: Voilà, donc du coup, à force, ce n'est pas grave. Il y a des choses qui sont là depuis 20 ans. J'ai pris du temps pour les configurer, mais la configuration n'a pratiquement pas changé ou elle a évolué un petit peu. Donc, ce n'est pas grave. J'ai perdu une heure il y a 20 ans. Ce n'est pas grave. Ce n'est pas beaucoup de temps dans une vie.

Prot: Et tu as gagné les automatismes que tu préfères.

Richard: Ben oui, et ça je m'en rends compte quand je passe sur d'autres outils surtout. Que ça marche pas, C-x, CTRL F, ça marche pas, CTRL H, F, ça marche pas. Les raccourcis sont pas corrects. Je rigole bien sûr, mais c'est un peu ça. J'ai tellement d'habitude que c'est une forme d'obstruction au changement. On est d'accord, force d'avoir. Mais bon... Quand je regarde avec un pas de côté, il y a très peu d'outils qui ont la pérennité d'Emacs ou de Vim d'ailleurs. Donc c'est des réflexes qui valent le coup d'apprendre que ce soit pour l'un ou pour l'autre. Ce sont des choses qui seront probablement encore là où il y aura des héritiers très similaires dans 15 ans, 20 ans. Moi je parie qu'il y aura toujours un Emacs ou une forme d'Emacs et il y aura toujours un héritier de Vim quel qu'il soit dans 20 ans et c'est bien possible que Visual Code ait été remplacé par un autre éditeur plus à la mode. J'en ai déjà vu passer quelques-uns qui étaient très à la mode à un moment ou à un autre. Emacs a l'avantage d'être jamais à la mode. Donc, en fait, si on admet que ce n'est pas grave de ne pas être à la mode, on a un outil qui sera présent aussi parce qu'il y a... Grâce au travail de plein de gens et aux heures de travail de plein de gens dans la communauté, parce que ce n'est pas magique, donc voilà... On aura un outil qui va rester et qui sera toujours... De toute façon, on aura toujours une fonctionnalité ou une autre qu'on pourra continuer à utiliser. Même si on utilise Emacs juste pour avoir une interface Git qui soit super chouette, Ça reste assez chouette, ça suffit. Ou juste pour Org Mode, ça suffit aussi. Effectivement, je suis maximaliste, j'essaie de l'utiliser pour presque tout. Mais je pense que ça convient à plein d'usages.

Sacha: Tes étudiants supportent l'utilisation d'Emacs ? Ils continuent ?

Richard: Pardon?

Sacha: Tes étudiants. s'est passé à...

Richarde: Mon étudiant? Ah, mon étudiant, non, il continue avec Emacs, bien sûr. Les étudiants que j'ai eus, ils ont tous utilisé Emacs et il y en a plusieurs qui ont adhéré, qui ont beaucoup aimé Org Mode en particulier parce que souvent, je leur propose ça. Notamment dans les travails de master ou de thèse, il y a quand même beaucoup de lecture. Le rôle de l'encadrant, c'est de faire des recommandations. Vous savez comment est la vie, que ce soit en tant que parent ou en tant qu'encadrant, on fait des recommandations et les enfants ou les étudiants ne les suivent pas. Ils font leur propre expérience et après, ils s'aperçoivent qu'il y avait quand même des bonnes idées. C'est à peu près ça le mécanisme, comment ça fonctionne. Mais ils essayent. Et puis moi, je peux en parler pendant des heures et puis je suis enthousiaste sur ces outils-là parce que c'est pérenne, parce que c'est ouvert, parce que c'est facile de donner le format et on peut le transformer facilement grâce à tous les outils qui existent dans le monde open source. Donc, on part d'Emacs et d'un format textuel. Pour moi, vraiment Org Mode, c'est le plus adapté quand on est dans Emacs. Parce que le mode est phénoménal, même par rapport au support Markdown qu'on a, et par ses capacités qui sont supérieures. Mais après, on peut communiquer avec les gens facilement, produire à un PDF de ses notes Org Mode pour communiquer avec quelqu'un qui n'utiliserait pas Emacs, même pas en tant que visualiseur PDF. Donc, c'est facile. C'est très malléable. En fait, c'est vraiment là qu'on en revient. C'est très malléable comme outil. C'est fait pour être retransformé, pris en main. En fait, ça convient bien aux gens qui ont cette philosophie, en particulier les programmeurs. Beaucoup de programmeurs, ça leur parle. Mais pas que. Prot, à la base, il n'est pas programmeur, par exemple.

Prot: Oui, oui, c'est vrai.

Richard: Bon, et maintenant il l'est, mais de facto. Mais bon, voilà. Il continue, oui, ça continue. Mais même des gens, en fait, beaucoup de gens l'utilisaient dans mon entourage juste pour écrire les articles. En fait, c'est pour ça, initialement, en tant qu'étudiant, c'est pour ça que j'avais utilisé Emacs plutôt que Vim, c'était que c'était plus naturel de taper dans Emacs. C'est-à-dire, quand on tape une lettre, on obtient la lettre. Il n'y a pas de contrainte à passer dans un mode ou un autre. La notion de modal dans Emacs est un peu différente, un peu sous-jacente, mais elle existe. Donc, c'est un peu pour ça que j'étais parti sur Emacs à la base. Le choix, on conviendra que cette raison n'est pas extraordinaire. C'est une raison de facilité initiale. Et puis, du coup, je suis toujours dedans. Comme quoi.

Sacha: Mais les raccourcis clavier !

Prot: Dès que c'est la facilité avec Emacs.

Richard: Non, mais c'est vrai qu'il y a toujours cette difficulté de comprendre le mode. J'étais vraiment dans le Vi, donc il fallait vraiment taper sur Escape pour pouvoir taper une lettre, et puis pour bouger, après il fallait retaper sur Escape, etc. Bon, j'ai un peu absorbé ce genre de choses maintenant, puisqu'on a quand même des interfaces, enfin l'interface de Magit en particulier, c'est plein d'autres. Même les miennes, elles sont un peu modales. On arrive dans un buffer, on a des raccourcis clavier dédiés sur une lettre ou une autre. Bon, c'est clairement la même logique. Ça a du bon. C'est vrai qu'il y a des gens qui argumentent du fait que Emacs est modal carrément. Je n'irai peut-être pas jusque là, mais la différence n'est pas si grande que ça. Il y a une différence fondamentale d'esprit et de contraintes, mais ça c'est comme dans le design de quoi que ce soit. Il y a une vision initiale, puis après on s'aperçoit qu'en pratique, les différences sont beaucoup plus fines.

Prot: Et pour toi, comment on dit que le texte est malléable, c'est la malléabilité d'Emacs qui est l'essence de ces différences ?

Richard: Pour moi, oui. Le choix initial qui est de se dire je vais avoir un interpréteur d'un langage. Et en fait, Emacs pour moi, je le vois vraiment comme on a essentiellement un langage interprété. Ça me parle beaucoup. Et au-dessus, il se trouve qu'on a rajouté toute une bibliothèque super chouette de fonctions pour afficher du texte. Il y a une bibliothèque en plus. C'est comme si on prenait un langage de programmation standard et qu'on avait prévu déjà son utilisation au sein d'un éditeur de texte. Donc on a toujours la capacité inhérente au langage de programmation et en particulier, dans le cas du Lisp, la malléabilité. et l'expressivité. En plus, on a toute la bibliothèque et là on a tous les modes en plus, donc ça c'est encore d'autres choses, mais je le vois vraiment comme ça. Ça vient d'un langage de programmation, donc d'un monde que je connais. Programmation fonctionnelle dit quasiment un interpréteur pour pouvoir jouer avec le langage. Et au-dessus de ça, on a pensé l'affichage et la fonction d'éditeur, mais au-dessus de ce langage de programmation. Moi, j'aime bien ce côté langage de programmation qui est dans Emacs, qui est dans la nature d'Emacs.

Sacha: J'adore combiner les fonctions des bibliothèques différentes pour faire une interface personnalisée.

Richard: Oui, c'est ça, c'est ça. Et puis on continue en fait. Et c'est vraiment l'esprit, enfin pour moi c'est vraiment l'esprit open source, mais même la programmation fonctionnelle. On prend, on fait des, on a des briques de base et on les assemble. On fait des briques un peu plus abstraites et puis on... Et dans Emacs, force est constante. Moi, je trouve que les briques de base sont pas mal pensées. Est-ce que c'est vraiment... Je pense que c'est un peu organique. Un choix d'une essence initiale qui a tout bien décidé du début. Mais on se trouve qu'on arrive à un équilibre qui, moi, me satisfait. Et je ne cherche pas à convaincre le reste du monde qu'ils ont tort. Moi, ça me va qu'Emacs ne soit pas l'éditeur à la mode, comme j'ai dit tout à l'heure. Je ne cherche pas à convaincre le reste du monde que c'est ça qu'il faut faire. J'incite fortement mes étudiants quand j'en ai à l'utiliser. Mais sinon, ce n'est pas grave. Ça reste un outil et c'est pareil, les outils, il faut les adapter. L'open source permet ça et je pense que c'est aussi un message important.

Sacha: As-tu des astuces moins connues sur l'utilisation d'Emacs ?

Richard: Moins connues ? Je ne sais pas. Comme je vous ai dit, j'emprunte beaucoup de choses dans ce que je vois. La dernière chose que j'ai commencé à utiliser et que j'aime bien, c'est Embark. Mais je pense que c'est suffisamment connu. Pour ne pas dire que c'est une astuce inconnue que je commence à utiliser. Parce qu'effectivement, on a ce problème de se rappeler de ce qui existe, ce qu'on a déjà fait. Et je trouve que Embark m'aide un peu à ça. On a un raccourci de base dans un contexte. Et on va afficher les choses qui existent dans ce contexte qui pourraient être intéressantes à utiliser, qu'on a oublié parce que nos fichiers de configuration sont trop longs ou il y a plein de modes ou des choses comme ça. Et ça m'aide dans ça, ou pour rajouter facilement des fonctionnalités dans des modes qui ne sont pas les miens. Là, dans Magit, par exemple, au-dessus de Forge, j'ai associé dans Embark la possibilité d'ouvrir via peer review. La pull request qui était ouverte et pouvoir faire la revue de code dans Emacs, évidemment. Aussi. En se passant de GitHub.

Sacha: Ma fille ne s'est pas encore réveillée. Donc si vous êtes disponible, nous pouvons continuer. Si vous avez d'autres obligations, pas de souci. On continue?

Prot: Moi, je peux continuer.

Richard: Moi aussi. En plus, on a un quart d'heure à rattraper au moins. À cause de notre erreur initiale assumant collectivement.

Sacha: Oui, non, non, non. J'ai enregistré toutes les sessions, donc je peux publier l'enregistrement.

Richard: D'accord. Très bien. Non, pas de souci, avec plaisir. Moi, j'aime bien. Moi, juste d'être là avec vous deux, ça me va, et de parler d'Emacs. Comme je vous ai dit, je peux en parler pendant des heures. Je pense que certains de mes étudiants en avaient peut-être marre, mais osaient pas me le dire. Mais oui, on peut continuer.

Sacha: Je n'ose pas toujours aller aux réunions virtuelles d'Emacs Paris. Comment ça va? Il y a beaucoup de présentations en français ?

Richard: Alors moi, je n'y suis jamais allé. En plus, tu m'as parlé de virtuel, donc ça n'a pas d'impact, mais je n'habite pas à Paris. Mais effectivement, c'est une bonne remarque. Peut-être que je devrais le faire. Il faut voir les horaires parce que je suis un peu comme toi. J'ai des contraintes familiales. J'ai aussi une fille. Donc, il y a des horaires qui sont impossibles à l'heure actuelle. Et c'est bien, en fait. C'est comme ça. C'est la vie.

Sacha: C'est la vie.

Richard: Non, mais c'est bien. En fait, c'est bien. C'est bien. Malgré ma passion d'Emacs, on peut faire un petit pas de côté, avoir un enfant, moi, ça m'a amené à reconsidérer beaucoup de choses. C'est normal, oui. Et d'ailleurs, juste en passant, moi, ça m'intéresse ce que tu racontes sur la parentalité, Sacha, dans tes billets de blog. C'est un truc que je suis. Ça n'a rien à voir avec Emacs, mais un peu, parce qu'à travers toi, c'est un des sujets que j'aime bien suivre et qui m'interpelle depuis qu'il y a un enfant, essentiellement. C'est comme ça qu'on s'y intéresse, je crois.

Sacha: Ma fille a 10 ans, donc je me sens toujours une débutante avec tous les sujets de parentalité... c'est difficile.

Richard: Mais je pense que c'est la même attitude face à Emacs. Il faut avoir... On est des débutants, on continue à apprendre. C'est un environnement où il y a tellement de choses à apprendre. On continue. Ce qui est bien, c'est que ça peut satisfaire les gens qui sont curieux et qui sont quand même aussi à l'aise avec le fait qu'ils ne connaîtront jamais tout. Ce n'est pas grave. Moi, ça me nourrit comme ça. La parentalité, c'est un peu la même chose. On ne connaîtra jamais tout. Sauf que ce qu'on fait a quand même plus d'impact que dans Emacs. On est d'accord. C'est quand même un peu différent.

Sacha: Quelles sont tes prochaines étapes sur ton bricolage d'Emacs ?

Richard: Alors probablement, visiblement, ce qu'on a discuté là, je suis conscient qu'il y a un peu de nettoyage à faire. Peut-être d'aller voir un peu plus dans le détail ce qui existe dans Org-capture pour simplifier certaines choses. On a tous un peu, enfin les programmeurs, on a tendance aussi à, comme on dit en français, à réinventer la roue. On aime bien faire les choses de notre manière parce qu'elle est mieux. Parce que c'est la nôtre, en fait. On la comprend plus facilement, tout simplement. Donc, probablement, allez voir s'il n'y a pas d'autres choses. Je vais continuer à travailler sur mon interface Garmin pour que ça soit... Plus intégrer pour ma visualisation, donc vérifier les courbes, je pense que je vais regarder. C'est peut-être intégrer d'autres données physiologiques. Ce qui est pas mal, c'est que si on a, enfin moi c'est pas mon cas, mais si on a plusieurs, il n'y a pas de mot français, trackers, de données, on peut les intégrer au même endroit dans Emacs, évidemment. En ayant la bonne interface, la bonne glue, la bonne colle pour intégrer les données. Et puis je vais continuer, dès que j'ai une cascade, une séquence de choses répétées, à essayer de les intégrer sous forme de fonction dans Emacs. qui fait toujours dans cette vision d'orchestration. Je pense que je vais continuer parce que je l'ai fait. Par exemple, j'ai des fonctions pour générer des invoices parce que ça m'est venu assez naturellement d'avoir ça dans Emacs. parce que j'ai un esprit comme ça, mais avec du LaTeX, bien sûr, parce que ça, c'est mon background académique c'est parce que je connais les packages. C'est pareil quand on connaît un environnement, hein, quand on a un marteau, on a l'impression que tout est un clou, comme on dit, hein. C'est un peu ça. Je vais continuer ça. Dès qu'il y a quelque chose qui est un peu répétitif et qui est un peu ennuyeux à faire, je pense que je vais l'intégrer dans Emacs. Parce que l'outil me le permet très facilement. C'est facile de lire les sorties d'outils Unix, c'est facile de les combiner avec la programmation fonctionnelle, tout se passe bien. On les remet en forme, on échange les données. Et ça, Lisp est chouette pour ça. Pour manipuler les données, c'est vachement bien. Pour moi, c'est encore plus simple que d'autres visions de la programmation. Je pense que ces deux aspects-là, simplifier les tâches répétitives, donc ça, je continue en particulier, même à les faire avec des timers, parce qu'il y a des choses que je n'ai pas envie de réfléchir. Il y a des choses auxquelles je réfléchis, comme d'aller récupérer... Donc, par exemple, toutes les pull requests qui ont reçu des commentaires. Voilà, mettre dans Emacs et me faire une petite note Org Mode avec les liens qui vont bien pour pouvoir... Ça fait une forme de... C'est des mini to-do list pour la journée, quoi. Qu'est-ce que je dois faire dans ma journée de programmeur d'un peu urgent ? Au lieu d'aller manuellement dans les dépôts Git et aller voir s'il y a eu... Moi ça me va quand je ne vais pas sur les interfaces des navigateurs, j'aime bien. Je préfère rester dans la manipulation de texte et dans l'abstraction. J'aime bien les outils locaux et Emacs me permet de faire comme si c'était local.

Sacha: J'ai lu ta fonction pour générer ton rapport pour les stand-up qui ressemblait à tous les pull requests et d'autres données.

Richard: C'est ça, oui. On me demandait de faire ces rapports-là. Ce n'est pas un truc d'humain, ça. C'est un truc de machine. Ce n'est pas important. Donc, c'était des demandes. Quand on a des demandes administratives qu'on trouve à la limite de l'absurde ou qu'on ne considère pas comme importantes, c'est bien que la machine vous aide. Emacs me permet ça, mais tous les outils de script le permettent. J'aime bien l'avoir dans Emacs.

Sacha: Tu as d'autres collègues qui utilisent Emacs ? Peut-être qui utilisent tes scripts ?

Richard: Mes scripts, c'est arrivé par le passé. Des choses que j'avais faites, j'avais distribué à des étudiants justement en Caml. J'avais fait des petites choses pour simplifier, pour pouvoir résumer vite. En Caml, on a deux types de fichiers. On doit décrire dans un fichier l'implémentation et dans un autre fichier ce qui est accessible comme API. Il y a une forme de duplication qui n'est pas très satisfaisante en tant qu'être humain. Donc, j'ai fait un script pour extraire l'information nécessaire du fichier où on avait l'implémentation vers le fichier d'interface. Je l'ai diffusé aux étudiants pour qu'ils puissent le réutiliser, pour que ça leur simplifie le développement. J'ai peut-être deux ou trois autres choses qui ne viennent pas à l'esprit, mais c'est assez limité. Et j'ai un collègue qui utilise Emacs. Alors on n'est pas beaucoup, je suis dans une toute petite entreprise, on est six, donc on est un tiers d'utilisateurs Emacs. Un tiers, c'est énorme sur une entreprise. Le reste, ils font tous du visual code, plus exactement du... Comment s'appelle ça ? Cursor.

Prot: Ah, Cursor, oui.

Richard: Personne n'est parfait.

Prot: C'est la mode, oui.

Richard: Mais là encore, en fait, Emacs est parfait pour l'IA. Mais c'est normal. Lisp, c'est un langage d'IA à la base.

Prot: C'est normal.

Richard: Donc, Emacs était en avance. On ne le savait pas. L'IA manipule presque tout de format textuel. Donc, c'est parfait pour Unix en général et Emacs en particulier. Moi, j'utilise Claude Code et j'ai fait une petite interface aussi pour savoir toutes les instances de Claude qui tournent dans quel répertoire avec quel pour pouvoir les ouvrir et aller les voir ou les tuer etc. Donc encore une fois, j'ai réutilisè Tabulated pour faire ça, parce que effectivement j'étais un peu contraint par Claude Code. Moi, j'utilise qui marche très bien, mais ça permet qu'une seule instance à un seul buffer, donc j'ai un peu généralisé pour avoir un Claude Multicode ou quelque chose comme ça. Ça permet d'utiliser plein d'instances qui les renomment automatiquement, on peut les renommer. J'ai un peu mon cockpit dans Emacs, mon dashboard textuel. C'est l'idée générale actuellement.

Sacha: J'ai un peu peur de permettre à l'IA d'utiliser mon Emacs directement. C'est un peu dangereux, je pense.

Richard: Non, oui. Il y a d'autres soucis, mais on est incité. Bon, ça simplifie certaines choses, mais ça en complique d'autres. On pourrait en parler, mais ce n'est pas le sujet d'aujourd'hui. Ce n'est pas le sujet d'aujourd'hui, mais ça aide. En fait, moi, ça m'a enlevé un frein pour ma création d'outils personnalisés dans Emacs quand même. Est-ce que j'avais toujours une espèce de frein qui me disait, oh là là, mais il va y avoir tout ça à faire. Ça va prendre beaucoup de temps. Plein de choses, j'arrive à conceptualiser, mais les détails, je ne les connais pas. Donc l'IA me permet de remplir facilement les détails en donnant le concept de ce que je veux et d'aller un peu regarder parfois, mais... Mais c'est pas tout le temps. Parfois, mais pas tout le temps. Là, il y a plusieurs choses. J'ai fait beaucoup de travail avec. Mais je reste souvent sur ma faim, comme on dit. Sceptique quant aux résultats obtenus. C'est assez facile de... Je trouve que je me suis déjà convaincu de certaines choses en travaillant avec IA, en revenant le jour d'après en disant mais c'est pas bon du tout ça. A mon avis, c'est même totalement faux. Et de retravailler un client en disant ah oui, effectivement, c'est totalement faux ce que j'avais dit. C'est vraiment le problème de travailler avec ça. Tout le monde qui a un peu de recul le sait. Ces outils ont ce problème-là. Il y a ça aussi. Tu ne l'as peut-être pas vu. Je ne sais pas si je l'ai poussé.

Sacha: Il y a un commentaire dans le chat. Aya a dit, dans mon école d'ingé, tout le monde utilise Vim ou Neovim. Emacs est considéré comme sauvage un peu.

Richard: Et c'est déjà... Moi, je trouve ça impressionnant que tout le monde utilise Vim ou Neovim. En réalité, en 2026, qu'il y ait tant d'utilisateurs que ça, de ces éditeurs qui sont...

Sacha: Très limités. C'est de papi, c'est des grands-pères de l'informatique.

Prot: Oui, oui.

Richarde: Mais en fait, quand êtes pensée... Dont la vision est a quelque chose de singulier et c'est pour ça qu'ils perdurent. C'est pas juste parce qu'il y a un peu d'habitude probablement, mais c'est pas que ça. C'est qu'ils parlent, je pense, à un certain type d'audience. Il y a vraiment des idées, je pense, qui conviennent à un paquet de gens, des ingénieurs en particulier. Je vous avoue que si moi, on m'imposait un autre outil, je serais très triste. Ça serait difficile. En fait, c'est un peu ce que je considère comme une nécessité au métier d'ingénieur ou de programmeur, c'est d'avoir de l'autonomie dans ses choix, techniques en particulier. Et un des choix techniques, c'est son interface de programmation. C'est pour ça que je ne vais pas essayer de convaincre à tout prix que quelqu'un qui utilise Visual Code a tort. Il n'a pas tort, il utilise l'outil qui est adapté pour lui. Je peux lui montrer Emacs, on peut rigoler en lui disant qu'il a tort, mais je n'irai pas jusqu'à le considérer vraiment. Mais j'étais étonné qu'on soit deux à utiliser Emacs. En Rust, je pense que c'est un environnement qui est moins courant. Les communautés de programmation ont aussi des habitudes par rapport aux éditeurs. Et en Caml, Emacs... avait au passé une majorité. Je crois qu'il y a beaucoup d'utilisateurs de Neovim ou de Vim maintenant. Notamment depuis qu'on s'est abstrait que le support du langage est passé du côté LSP. Finalement, l'éditeur n'est devenu pas très important pour avoir la qualité de support. Du coup, c'est un bon argument pour que je garde Emacs. Comme ça, j'ai mes raccourcis clavier, j'ai un environnement hyper dépouillé parce que moi j'aime bien avoir quelque chose de très lisible et très flat. Pas de truc qui clignote ou de machin. Bon, vous pouvez voir que j'utilise un window manager aussi très tiling, donc j'ai vraiment un truc un peu spartiate. Et j'ai utilisé plusieurs fois Mac OS. A chaque fois, ça s'est terminé pareil. Cet environnement ne me laisse pas faire les choses comme je veux. J'en ai marre, je repars sur Linux.

Sacha: Tu es très particulière sur tes outils.

Richard: Oui, c'est-à-dire que j'aime bien ne pas me sentir restreint. Mais dans la vie, c'est pareil. J'aime bien la forme de liberté que procure L'open source, encore une fois. Sous Linux, si je veux avoir un environnement à la macOS, je peux aussi. Si c'est ça qui me convient comme utilisateur. Et c'est ça qui est important, moi. Qu'on me laisse le choix. Et moi, ce dont j'ai besoin, c'est vraiment un truc simple. Ou l'écran, ou mon interface avec le monde, c'est Emacs. C'est particulier, mais ici, dans ce petit monde où on est là, tous les trois et les auditeurs, je pense qu'on est plusieurs à avoir un peu ce parti pris. Même quand j'étais sur Mac OS, c'était... Mon principal problème c'était que je trouvais désagréable, c'était compliqué pour moi de switcher entre Emacs et le navigateur. Je trouvais ça désagréable, la façon de le faire sur macOS. Là, essentiellement, j'ai deux écrans virtuels différents et il y en a un qu'avec un navigateur. Donc, je change de l'un à l'autre et un par raccourci clavier. C'est sans doute possible sous macOS. Je le crois volontiers, mais je ne suis pas arrivé à quelque chose où je me sens libre de le faire.

Sacha: En lisant ta configuration, j'ai noté que tu utilises EWW. Oui, un peu, oui.

Richard: Dès que c'est possible. C'est-à-dire pas tout le temps. En 2026. Oui, ça m'arrive assez souvent, via Elfeed en particulier. Parce qu'il y a un nombre de blogs qui redirigent uniquement où on est obligé d'aller sur la page. Donc beaucoup d'entrees... En fait, ça suffit pour lire et c'est ça me va. Et donc j'utilise en premier lieu EWW dans ce cas-là. Sinon, c'est... Malheureusement, je n'arrive pas à en faire un outil majeur au quotidien. Les interfaces web que j'utilise ont souvent besoin de JavaScript en 2026. Malheureusement, je ne vais pas pousser la liaison Firefox. J'utilise Firefox essentiellement, mais la liaison Firefox et Emacs, je ne vais pas pousser cet aspect-là.

Sacha: Je donne l'exception SpookFox pour créer un lien entre le navigateur Firefox et Emacs.

Richard: Comment tu dis?

Sacha: SpookFox. SpookFox me donne le pouvoir d'exécuter le JavaScript à partir d'Emacs Lisp. pour retrouver des...

Richard: Je vais regarder...

Prot: Oui.

Richard: Tu vas me faire perdre du temps encore.

Sacha: Pardon, pardon. pour passer à une autre page ou à retrouver des données ou à toutes les choses.

Richard: Je pense que c'est exactement ce qu'il me faut. Je ne sais pas qui a fait ça, mais merci à cette personne.

Sacha: Quand je prépare le bulletin d'information Emacs News, je l'utilise pour [extraire] les liens sur Reddit. Oui, oui, oui. Au lieu de copier manuellement les liens moi-même, je juste le sélectionne dans une liste de vertico.

Richard: Ça c'est bien, ça. C'est un usage que j'ai régulièrement de récupérer le lien qui est dans mon navigateur pour l'intégrer dans une note ou quelconque. Effectivement, ça va être bien ça, je pense. On apprend tous les jours, c'est là qu'on mesure le degré de notre ignorance.

Sacha: C'est la raison que j'ai envie de plus de vidéos et de ressources intermédiaires sur Emacs parce que tout le monde a des choses qu'on peut apprendre. Dans Emacs... Toutes les vidéos en ce moment sont pour les débutants, mais si tu prends juste un peu plus d'étapes, tu peux découvrir beaucoup de sujets d'apprendre.

Richard: Oui, c'est vrai. On apprend tout le temps. Mais même quand on est expert, je pense que... Même les experts apprennent parce qu'ils sont experts dans un sous-domaine. Ils ne connaissent pas tout. Dans la communauté Emacs, il y a plein de gens qui connaissent des choses, qui ont fait des choses très particulières. Même eux, je pense, peuvent apprendre d'autres qui ont fait d'autres parties plus particulières.

Prot: Mais ça doit être un peu plus difficile de faire les vidéos sur ce sujet-là. Parce que... Qu'est-ce que tu vas dire ? Les préférences sont particulières. Peut-être c'est plus difficile de dire, oh voilà, voici quelque chose pour tout le monde. Tu vas dire, voici quelque chose pour moi.

Richard: Ça marche. Ça peut servir de point... En fait, ça a une autre utilité. Déjà, se rendre compte que c'est possible. Parce que parfois, on se dit juste, on n'a pas eu l'idée. Ah, mais on peut faire ça. Et comment on le fait? Il a utilisé ça, ça et ça dans les mains. Ah, mais je ne savais pas que c'était comme ça qu'on pouvait faire. Et donc, à partir de ça, on désassemble un peu les briques et on les réassemble pour nous différemment. C'est d'autres utilités, je pense.

Prot: Mais peut-être il y a les séances en direct qui ont comme ça. Je ne sais pas, oui. Je n'ai pas cherché.

Richard: Moi non plus.

Sacha: Je suis étonnée que cette conversation marche, même si je suis débutante, même si Prot est un peu rouillé.

Richard: Bon non, tu es pas... Alors, pour avoir une conversation d'une heure comme ça, tu n'es plus débutante. Non, il n'y a aucun souci à te comprendre en français. J'ai l'impression que tu n'as pas eu beaucoup de soucis de compréhension. C'est très facile de te suivre. Moi, j'ai appris une autre langue un peu comme toi. Dans mon passé, je suis allé au Brésil. J'ai habité deux ans là-bas et je ne parlais pas portugais. Et j'étais prof de l'université. Donc, j'ai appris comme ça, en donnant des cours. Alors, un peu en apprenant des cours, bien sûr, de base. Mais après, toute l'oralité, la façon de parler, la facilité de parler et le vocabulaire, c'est en travaillant sur les cours. En parlant, comme tu es en train de le faire, là. Et maintenant, les gens ne savent pas si... Beaucoup de gens ont des doutes jusqu'à un certain point. Ils arrivent à voir que je ne suis pas brésilien. Mais ça marche. Non, mais ton français est très bon. Il n'y a aucun souci.

Sacha: Tu disais que tu avais peut-être un peu peur sur ce... Peut-être qu'un jour, nous pourrons avoir des Emacs en portugais. Je ne sais pas. Je ne parle pas portugais.

Prot: L'année prochaine.

Richard: En grec, d'abord.

Prot: Le final boss.

Sacha: Mais je pense qu'avec l'aide de l'IA, c'est plus facile d'apprendre la langue à ce moment.

Richard: C'est vrai que c'est un usage tout à fait pertinent aussi, de ces outils. Je t'avoue, par exemple, l'usage de l'interview dont je t'avais parlé, je n'y avais pas pensé. Mais en fait, c'est très pertinent de l'utiliser comme adversaire d'une certaine façon. Dans un jeu, il y a beaucoup d'ingénieurs aussi qui créent leur logiciel maintenant, un peu aussi pour maîtriser ce qu'ils font de façon conversationnelle avec l'IA. Donc en remettant, en disant, en lui faisant poser des questions, en créant bric à bric, au lieu de lui dire fais ça, d'aller prendre un café, c'est bien aussi, parfois, mais ils le font pas à pas, comme un outil pour délimiter, parce que c'est vrai que Parfois, les solutions sont intéressantes, dirons-nous. Ça peut être assez intéressant. J'ai déjà eu le cas, mais beaucoup d'autres, c'est quand on lui dit « fais passer tel test », tu enlèves tout le code du test et puis on met juste « assert true ». Voilà, très bien. Ça passe.

Sacha: J'ai envie d'interface vocale et conversationnelle avec Emacs. Je pense que c'est très puissant.

Richard: Alors, j'y ai pensé, mais je ne suis pas là. Mais effectivement, j'ai des collègues qui utilisent l'IA et qui lui parlent directement pour être encore plus efficace. Mais est-ce que c'est... Je me pose à chaque fois la question si c'est... Est-ce que c'est vraiment le but de ce qu'on veut faire personnellement, comme être humain, quoi ? Est-ce que c'est vraiment ça, être le plus efficace possible ? Là, par exemple, on n'est pas efficace, mais c'est très bien. Mais ça a quand même quelque chose de pertinent.

Sacha: Et il y a des commentaires au chat. J'ai totalement oublié de lire à voix haute. Une conversation sur la gestion des fenêtres, sur l'apprentissage de langue avec les algorithmes de répétition espacée. Et aussi les amis qui utilisent Helix [plutôt] que Emacs ou aussi Zed parce qu'il est plus [à la] mode.

Richard: Surtout si on aime Rust. J'ai essayé, mais c'est un peu comme tous les outils. J'ai peut-être pas assez de patience. J'ai passé déjà mon temps d'apprentissage dans Emacs et j'avoue être un peu réticent, comme on dit.

Sacha: Oui, tu as beaucoup d'habitude d'Emacs et c'est difficile de passer à d'autres éditeurs.

Richard: Oui, et puis surtout, comme on disait tout à l'heure, maintenant, il y a des couches d'abstraction. Il y a LSP, TreeSitter, qui en fait rendent le choix de l'éditeur un peu... Plus proche d'un choix tout à fait personnel. Et pas de capacité en fait. Les capacités sont les mêmes, elles sont produites par l'adaptateur LSP ou l'adaptateur tree-sitter. Du coup, il n'y a plus de frein à utiliser celui qui nous plaît le plus. Parce qu'on a des habitudes pour tout un tas de mauvaises raisons. Effectivement, comme j'utilise plein d'autres choses dans Emacs, pas juste la capacité d'éditeur de programmation parce que c'est ça qu'on a moins en fait dans Visual Code ou même peut-être dans Z, que je ne connais pas très bien. C'est le fait que ça soit plus qu'un éditeur en réalité. C'est ce qu'il y a de plus que ça. Ça fait l'éditeur de code. Oui, mais tellement plus. C'est vraiment proche d'un OS d'une certaine façon. Beaucoup de choses. C'est une couche d'abstraction lispienne au-dessus d'un OS avec un... Un tiling window manager pas terrible. Et voilà. Et l'édition quoi. Mais l'édition, en fait c'est l'édition de texte. Donc ça marche. Pour écrire des papiers, c'est super, c'est très bien aussi. C'est comme ça que j'ai beaucoup utilisé pendant très longtemps pour écrire mes papiers scientifiques. Comme en informatique, on utilise LaTeX. Pareil, c'est souvent Emacs ou un autre éditeur, peu importe finalement. Parce que derrière, on a une espèce de makefile ou un truc. Je suis assez vieux pour utiliser des makefiles, moi. Tout s'emboîte assez bien dans Emacs. Il est né en même temps que beaucoup d'outils donc finalement il arrive à interagir très bien avec ces outils. C'est surtout ça que je pense qu'il importe, qu'il est bien. Mais probablement les autres éditeurs y arrivent très bien aussi. Mais on n'a tous que 24 heures dans la journée. On choisit de perdre son temps sur la note hebdomadaire de Sacha et du coup on ne peut pas aller faire du visual code. Voilà.

Sacha: Donc c'est ma faute.

Richard: C'est ça. Non mais ça serait autre chose, tu sais, la procrastination avec... Elle est illimitée. Quand il y a des choses qu'on n'a pas envie de faire, on trouve toujours quelque chose de plus important à faire.

Sacha: Je ne sais pas comment je peux créer une traduction de notre conversation. Je pense que j'ai du mal à la corriger parce que c'est difficile en français. Mais j'essaie. Oui, oui, oui, oui. J'adore utiliser WhisperX pour transcrire mes autres conversations en français parce que même si elle demande beaucoup de corrections, c'est plus utile parce que je ne sais pas les mots, je ne sais pas... Ah! Il y a un... Un petit remarque. « Est-ce que vous êtes averti qu'on peut utiliser Emacs, Vim, etc. avec Termux dans votre poche avec le smartphone et synchroniser votre travail avec SyncThing? » J'utilise Orgzly Revived avec SyncThing. Tu utilises Emacs sur ton téléphone?

Richard: Je déteste utiliser mon téléphone. On ne voit rien. C'est désagréable. On n'a pas de clavier. C'est horrible. Je n'aime pas utiliser mon téléphone. Je ne suis pas une personne. Je suis trop vieux, peut-être. Mais non, c'est trop petit. On ne voit pas assez de contexte. Je ne sais pas. Ça ne me convient pas, personnellement. J'essaie. Je fais Orgzly et Syncthing. Mais en réalité... Ce qui me convient mieux, c'est de prendre des notes à la main quelque part et d'aller les rentrer après. Et en plus, je crois que ça me détend un peu plus de prendre la note à la main. Je la retiens mieux. Des fois, je n'ai même pas besoin de passer par l'étape intermédiaire de la rentrée. Je trouve que je les oublie beaucoup plus facilement si je les note sur le téléphone ou sur un device de façon électronique. Si je les note à la main, du coup, Il y a quelqu'un qui est un peu connu, qui est dans la réflexion de comment on travaille, qui s'appelle Cal Newport. Lui, il fait beaucoup de notes à la main quand même dans son travail, mais il est chercheur, c'est pareil. C'est un peu une apologie aussi de la lenteur d'une certaine façon pour réfléchir à ce qu'on fait. J'ai mis en place Syncthing et mon Android n'arrête pas de le tuer parce qu'il ne fait rien. On va lui dire que ça fait je ne sais pas combien de temps que vous ne l'avez pas utilisé donc je vais arrêter cette permission et cette autre permission. Je suis au courant de tout ça. Je vais plutôt passer du temps à faire ma config Emacs. On va faire des interfaces pour gérer les finances dans Emacs. Et puis d'autres outils qui existent. Et puis on agrège tout ça avec un makefile ou un just file. Et on obtient des interfaces assez sympas. Et ça suffit pour une personne en fait. On n'a pas tant d'opérations. Quand on réfléchit, on n'a pas tant d'opérations que ça, le texte. Je pense que c'est un peu le constat de Prot sur Denote par rapport à l'utilisation d'une base de données, c'est que... On produit pas tant d'informations que ça en fait. Pour qu'on ait besoin d'avoir des bases de données en plus au-dessus du file system et que les outils qui se cherchent directement dans le texte nous amènent en fait très très loin par rapport à la qualité et le contenu d'informations qu'on produit. Donc le téléphone, si je pouvais m'en passer. Si je pouvais me passer du smartphone, je le ferais. Mais il y a beaucoup de banques qui ne veulent pas qu'on se passe d'un smartphone et beaucoup d'autres intermédiaires qui veulent vraiment qu'on ait leur application sur notre téléphone. Mais si je pouvais avoir que le téléphone, franchement, je pense que je le ferais maintenant. Chapeau ! Je suis à ce point-là. Je suis comme tout le monde, je perds du temps sur mon téléphone. Je me rends compte qu'il va toujours vouloir écouter des trucs. A raté d'autres choses du coup. A raté ce que je suis en train de faire. A raté ce que me dit ma famille. Peut-être qu'il y a une petite remise en question personnelle sur moi, sur le multitasking, sur la multitâche. Je pense que je vois un peu tout ça. Moi, je trouve ça chouette quand même au niveau technique de se dire on peut le faire et c'est possible. Mais je pense que j'ai un peu passé cette étape-là où je me dis je vais franchir pour dire je vais le mettre en place sur mon téléphone et je vais prendre le temps de le faire. Je pense que je ne ferai plus ça.

Sacha: Tu peux consacrer ton temps sur ton ordinateur pour le travail et après ça, c'est fini.

Richard: Oui, mais pas que. Enfin, même pour d'autres choses. Il n'y a pas que du travail. Mais effectivement, je préfère utiliser l'ordinateur comme interface numérique. C'est trop limité le téléphone pour moi. Et encore une fois, c'est un environnement qui est de plus en plus contraint. Si on veut pas... C'est pas si simple d'utiliser Lineage OS parce qu'il y a des applications qui vont peut-être plus fonctionner. Notamment les banques, par exemple, il y en a peut-être qui vont plus fonctionner parce qu'on n'a pas le bon OS. C'est un peu le même truc dont on parlait. Je me sens contraint sur le téléphone et c'est le but. Un certain nombre d'entités de récupérer, à travers cette contrainte, des données. Il est là, mon téléphone, il n'est pas loin. Tu sais la justification facile pour cette chose-là, c'est « mais si jamais on m'appelle pour ma fille ?

Prot: » Oui, oui, oui.

Richard: Moi, je suis né à une époque, on est assez vieux pour se souvenir qu'à une époque, on n'avait pas de téléphone portable. Et on arrivait quand même à appeler les gens. J'en déduis rien de particulier, mais je pense qu'on est capable de s'organiser raisonnablement ou de limiter nos usages, parce que ce n'est pas inutile, je ne dirais pas jusque-là. Je pense qu'on l'utilise trop. Moi, je l'utilise trop. Résumons ça. Moi, je l'utilise trop et ça me fatigue.

Sacha: Quelle est-ce ta fille ?

Richard: Elle a 6 ans.

Sacha: Ah ! Très petite. Elle est jeune.

Richard: Elle est jeune, mais c'est quand même déjà très différent. Et on a passé les étapes les plus J'ai envie de me dire qu'on a passé les étapes les plus compliquées de la gestion par rapport à l'organisation de la journée. Et la façon d'interagir, parce que là, maintenant, elle est à un stade où la discussion est totalement possible. Avant, c'était moins facile quand ils sont tout petits. En fait, j'ai deux filles. J'en ai une qui habite avec moi maintenant et l'autre qui est très grande, qui a habité avec sa maman, qui a 18 ans. Elle fait sa vie, elle est indépendante à 18 ans. Il n'y a plus besoin au quotidien de choses.

Sacha: Donc, quand ta fille demande ton attention, tu dois payer toute l'attention.

Richard: Oui, et parfois elle nous fait remarquer qu'on est sur le téléphone. Et en général, ça nous suffit, dis-nous parce que c'est valable pour moi et mon épouse, mais ça nous suffit pour le poser. En fait, on a un endroit où on le laisse charger qui est un peu éloigné de quelque part dans notre salon. On a un endroit pour les charger et ce n'est pas dans la chambre, c'est un peu loin. Du coup, ils sont souvent là, quand même. Souvent là, et puis retournés, comme ça, ils sonnent pas. Moi, mon téléphone, il sonne jamais, de toute façon. Il est en vibreur, déjà, c'est pour te dire à quel point j'aime cet outil. Il est en vibreur, donc il sonne pas. J'aime pas les gadgets qui réclament mon attention. Les pop-ups, les machins, ça me stresse. Même quand je suis dans une gare à Paris, par exemple, je le sais, il y a des vidéos qui tournent, ça m'agresse. Tout ça, toutes les notifications, les choses, tout ça. C'est pour ça que j'aime bien Emacs, parce qu'on peut en avoir si on veut, mais par défaut, on n'a rien. En fait, ce qui est bien avec Emacs, c'est que par défaut, il n'y a rien.

Sacha: À l'exception de Visible Bell, qui peut configurer à Visible Bell ou à d'autres options.

Richard: Et pour avoir quelque chose, il faut travailler et faire ses choix. Lire, suivre des gens, c'est [??]. Ça c'est chouette. Moi, ça me va bien et j'aime bien le fait qu'il y a ait rien que on pousse pas à la perception ou à la prise d'attention. C'est hyper néfaste en fait. On le sait sur l'attention, sur la production intellectuelle. Mais Emacs a cette qualité-là où... C'est un outil de travail, vraiment pour du travail. C'est-à-dire un travail profond où on réfléchit à ce qu'on fait, c'est difficile, ça prend du temps. Et c'est même pas tant la rapidité de taper au clavier qui est importante parce que ce qui prend du temps, c'est avoir la bonne idée. Donc après, on peut taper vite ou lentement. Finalement, c'est presque insignifiant par rapport à la durée de réflexion et de la production de la chose. Donc, je pense que c'est le genre d'environnement un peu zen que j'aime bien, moi.

Sacha: Emacs, l'éditeur zen !

Richard: Non, mais c'est vrai. Puis l'image du gnu avec la flûte, franchement. Même Vi est comme ça. C'est des outils, c'est des choses, des outils. Il n'y a pas de superflu. On rajoute ce qu'on veut par-dessus et ça peut devenir un monstre. ou un truc très très limité. Moi j'aime bien ces outils qu'on peut configurer en fonction du temps et du travail qu'on y accorde. Et puis j'aime bien Lisp honnêtement. C'est pas OCaml mais c'est pas loin quoi. C'est dans mon top 4 avec Haskell. Ah ça serait Rust maintenant.

Prot: Ah, très bien.

Richard: J'aime bien. Rust, c'est comme langage de programmation, j'aime bien. J'en fais. J'avoue que c'est... Ils ont trouvé un bon équilibre dans les idées. Ils ont pris des bonnes idées là où elles existent depuis longtemps. Mais au moins, ils ont réussi à les faire passer dans le mainstream et ça, c'est important. Ça prend du temps. Que les idées percolent, comme on dit, de là où elles sont produites, qui est souvent dans le milieu académique, jusqu'à un usage, disons, industriel ou généralisé. C'est vrai pour tout un tas d'idées intellectuelles. Ce n'est pas vrai que pour l'informatique, en particulier. Je ne sais plus ce qu'on m'avait dit quand j'étais jeune étudiant en thèse. C'est quelque chose comme 40 ans, 50 ans. Par exemple, le typage, les algorithmes de typage où on n'a pas besoin d'annoter un programme, c'est les années 70. L'algorithme, ça fait 55 ans qu'on sait faire ça. De là à le voir arriver dans un langage où les gens se disent « Ah, c'est super, je n'ai pas besoin de le faire ». Quand les langages que les gens utilisent au quotidien, ça met du temps. C'est même pas totalement présent partout facilement. J'ai toujours connu ça en OCaml. Quand on écrit une fonction, le style traditionnel d'écrire une fonction d'implémentation, on n'annote pas les types. Parce qu'on sait qu'ils vont être inférés, ils vont être calculés. Par contre, on les met dans ce qui est l'interface avec l'utilisateur parce que ça fait partie de la documentation et du contrat avec l'utilisateur. Tu dois envoyer tel type de données pour que ça soit correct. Donc, les idées mettent du temps à transiter. Enfin, c'est comme ça. Être accepté, en fait, il y a toujours une forme de résistance dans les idées pour qu'elles arrivent. Je ne sais pas pourquoi on est là, d'ailleurs, dans cette conversation, mais c'est intéressant. Nous sommes loin d'Emacs, mais c'est quand même... Enfin, la vision qui a amené à la création de Lisp. Lisp, c'est vraiment un langage zen au niveau de la syntaxe, même si les gens rigolent du nombre de parenthèses. Mais au niveau de la syntaxe, c'est d'une élégance minimaliste incroyable. Il y a un côté esthétique plaisant dans cette façon de voir les choses moi je trouve. C'est pour ça que j'aime bien aussi produire un peu des Maxlis parce que je ne sais pas, il y a un truc, un côté satisfaisant intellectuellement et même il y a une forme d'art. Il y a la simplification. L'élégance. Oui, l'élégance, la simplification du tout jusqu'au côté minimaliste. Alors moi, en fait, j'aime bien ça parce que quand j'ai étudié aussi des choses comme le lambda calcul ou des choses un peu à la limite de la logique et de la philosophie. La frontière est très ténue en fait, en pratique. Et une des questions que les gens se posent, c'est de combien d'opérateurs on a besoin pour calculer tout ce qui est calculable ? Bah, en fait... Donc après, c'est pas très pratique de programmer avec un opérateur. Ah donc être humain quoi. Nos interfaces, on a besoin que ça soit un peu plus riche. Mais c'est voilà, tout le reste est comme on dit en anglais du sucre syntaxique.

Prot: Hein.

Sacha: Oui, je pense que je préfère la simplicité d'utilisation du juste parenthèse au lieu de beaucoup d'autres syntaxes.

Richard: Je suis d'accord. J'aime bien ça. Après, c'est un choix de conception et de design. On a toujours un équilibre entre la facilité d'exprimer l'idée dans le langage programmation ou pas. En fait... Donc, l'interface humaine, et puis la derrière, la beauté ou la simplicité d'implémentation pour la machine. C'est pour ça peut-être que les gens aiment bien l'IA, parce que c'est facile, le langage, c'est le langage naturel. En informatique, on sait depuis un certain temps, en philosophie aussi. Il est ambigu et du coup, ce n'est peut-être pas terrible si on veut spécifier des choses pour une machine. C'est pour ça que les langages de programmation existent. C'est pour qu'ils aient une sémantique bien définie. C'est une partie des choses que j'ai étudiées. La sémantique, c'est important. Et la sémantique du langage naturel.

Prot: Non, non, c'est beaucoup plus difficile.

Richard: Elle est ambiguë et c'est bien parce que c'est pour ça qu'il existe la littérature et la poésie et un tas de choses. C'est comme ça que ça existe. Sinon, si c'était non ambigu, ce ne serait peut-être pas génial ce genre. Il n'y aurait pas de blague non plus. On ne ferait pas de blague. De jeu de mots. Moi, je ne crois pas trop. J'ai un petit avis sur l'IA. Ce que j'ai étudié, c'était en particulier la spécification des langages de programmation parce que dans les méthodes formelles, un des domaines d'application, c'est qu'on a un programme et on veut vérifier qu'il fait bien ce qu'on est censé faire. Déjà, on l'a écrit en code, en langage en C, peu importe, mais on a un autre langage mathématique qui décrit ce qu'il est censé faire, donc spécifier. Et j'ai l'impression qu'on va en arriver là, on va en arriver là en IA, mais c'est une thèse qui est défendue aussi par d'autres gens. C'est pas la mienne en fait. La vraie bridge, c'est le vrai pont à bâtir, c'est comment je spécifie ce que je veux de façon non ambiguë pour vérifier que l'IA fait ce que je voulais qu'elle fasse. Mais on va arriver à un langage de programmation en fait. Je ne sais pas lequel, mais en tout état de cause, c'est ça la solution, c'est les spécifications. Peut-être un langage, ce sera les maths. Ce n'est pas toutes les personnes qui aiment l'utiliser ce langage-là.

Sacha: Je préfère [l'algorithme déterministe], parce que les résultats imprévisibles de l'IA sont un peu difficiles de gérer.

Richard:

Oui, oui, je suis 100% d'accord sur ça.

C'est là où les différentes visions qui disent, mais maintenant, avant on avait l'assembleur, on avait les langages haut niveau, maintenant on a l'IA qui est au-dessus. Non, non, c'est pas du tout la même chose. Il n'y a pas une suite de séquences bien définie qu'on peut même démontrer, parce qu'on a des compilateurs prouvés. Toutes les étapes sont démontrées correctes par rapport à ce qu'on pensait qu'elles devaient faire. On sait faire ça, mais avec l'IA, on ne sait pas faire ça. On ne saura jamais avec cette technique actuelle. C'est impossible, ce n'est pas prévu. On se probabilise, donc c'est intrinsèquement impossible. Mais c'est un outil intéressant malgré tout, pour tout un tas de choses.

Sacha: Nous conversons à 12 heures.

Prot: Il n'y a pas que lui.

Sacha: J'ai promis à ma fille que je prends une sieste avant qu'elle se réveille pour éviter tomber à la fatigue. Oui, oui, oui. La fatigue. Donc, je pense que je dois conclure la discussion.

Richard: Pas de souci, Sacha.

Sacha: Mais je veux le continuer toujours.

Prot: Je crois que ce point est bon.

Richard: Non, c'est y a pas de souci. C'est un plaisir et il y a des cont moi. Ma fille se lève jamais tard donc tu vois... Moi, à 7h30 en général, elle est réveillée. J'ai pas de matinée. Elle est pas assez vieille pour faire des grasses matinées comme on dit.

Sacha: Donc, merci beaucoup. Un grand merci.

Richard: De rien.

Sacha: Merci à vous deux, Richard et Prot. Et merci aussi, chers auditeurs, pour contribuer à vos questions, vos commentaires. Je pense que je peux publier l'enregistrement de toute la session, toute la discussion, si ça marche, si ça fonctionne.

Richard: On y croit, on y croit. On croise les doigts. On croise les doigts. Oui, oui, oui.

Sacha: À plus tard. Merci beaucoup.

Richard: Merci à tous les deux, Sacha et Prot.

Prot: Merci, merci, Sacha. À bientôt, à bientôt. Au revoir.

Richard: Au revoir.

Chat

  • sachactube:​​ J'ai totalement oublié de commencer la diffusion en direct ! Je vais publier l'enregstrement.
  • alainlamourec6332:​ ​ca rigole
  • syshYarak:​ ​bonjour j'ai une petite question pour vous j'ai deja travaillé pour emacs , avec la version préconfigurés spacemacs , orgmod etc ….. je préfère édité avec vim juste pour ne pas utiliser la souris,
  • syshYarak:​ ​ma question c'est la suivante, je pense que c'est une rabbit hole au lieu de focaliser sur les choses les plus importantes, au lieu de créer des projets etc on se concentre sur les dotfiles etc …..
  • phyzixlab:​ ​Sacre bleu
  • protesilaos:​ ​Salut les gens!
  • alainlamourec6332:​ ​Salut Prot
  • JonKishore11:​​ Vous ne connaissez pas l'anglais ?
  • sachactube​​C'est: en français aujourd'hui, pour le pratiquer ! =) 8:28 AM@syshYarak​

​mr si tu veux sauver un ancien utilisateur d'emacs on me proposant un M2 ou une thèse , car je suis ingénieur au Maroc , prot je ne t'ai pas reconnu t'avais de Long cheveux,

  • protesilaos:​ ​Oui, maintenant mes cheveux sont courts. C'est une catastrophe, haha!
  • syshYarak:​ ​j'avais l'habitude de suivre tes cours de politique européenne prot. from emacs to politics
  • syshYarak:​ ​lisp programmation fonctionnel ?
  • ahyass​​oui: c'est du fonctionnel 8:42 AM@ahyass​

​dans mon école d'ingé tout le monde utilise vim/neovim, emacs est considérer comme sauvage un peu

  • ahyass:​ ​j'ai plus d'amis qui utilisent helix que emacs, même zed est plus populaire j'ai l'impression
  • syshYarak:​ Gestionnaire de fenêtres genre i3wn ou …?
  • ahyass:​ ​niri est d'après mon expérience le meilleur window manager
  • protesilaos:​ ​moi j'utilise herbstluftwm
  • syshYarak:​ ​i3wm on Windows et zellij come Terminal session manager au lieu de tmux , et glazewm dans ma windows machine
  • syshYarak:​ ​j'essaie de laisser les choses simples, car l'optimisation prématuré et la source des pbs.
  • syshYarak:​ ​next step learning french with emacs , en intégrant des algorithmes de répétition espacé
  • protesilaos:​ ​en ce sujet là, il y a un paquet nouveau qui s'appelle "srs.el"
  • protesilaos:​ ​je croix que "package" est "paquet", mais je ne suis pas sur
  • syshYarak:​ ​oui c'est ça prot , de toute façon je t'ai compris t'inquiète
  • syshYarak:​ ​j'ai une petite remarque est ce que vous êtes averti qu'on peut utiliser emacs vim etc avec termux dans votre poche avec le SMARTPHONE et synchroniser votre travail avec synchthing???
  • syshYarak:​ ​non , la confite en elle même tu peux la synchroniser et compilé sur termux ,
  • syshYarak:​ ​non mais plus tôt une intégration d'ai au niveau du kernel pour ce qui est tout tâche, en ce qui concerne ce que vous voulez spécifiquement pour vous , un entraînement SUBJECTIVE de chaque individu
  • syshYarak:​ ​sera envisageable pour réduire les erreurs pour chaque personne pour reprendre au prof
View Org source for this post

You can e-mail me at sacha@sachachua.com.

-1:-- Emacs Chat avec Richard Bonichon en français (Post Sacha Chua)--L0--C0--2026-08-06T19:30:46.000Z

Raymond Zeitler: Dedicate an Emacs Window to Its Buffer

Before I start writing in Org, I dedicate the window. This "protects" the content, preventing it from being replaced by something else. It ensures I can stay focused on my writing no matter what other buffers Emacs opens.

My usual workflow is to display help on the functions I write about. Emacs opens the help buffer in another window. That's good. But then if I click on the link to the source code that help references, that file will open in my writing window, and I lose focus. Dedicating the window prevents this.

I could dedicate a window manually by invoking toggle-window-dedicated (bound to C-x w d) every time I start writing. But because I clock in to anything I'm serious about, I let org-clock-in-hook to do it for me as shown below. Note that the hook calls set-window-dedicated-p rather than toggle-window-dedicated because the latter will un-dedicate a window that's already dedicated. I could configure org-clock-out-hook to un-dedicate the window; I'm debating whether it's necessary.

(add-hook 'org-clock-in-hook (lambda () (set-window-dedicated-p nil t)))

Interestingly, while I was writing this, JTR published his "Gems" article in which he writes about setting help-window-keep-selected to true "to keep help in its own dedicated window, so it won’t open in a separate window once we follow a link."1 Perhaps that’s his approach to the same issue.


1 Emacs Config Gems - Part 3

-1:-- Dedicate an Emacs Window to Its Buffer (Post Raymond Zeitler)--L0--C0--2026-08-06T17:46:17.556Z

Irreal: Displaying The Active Clock

Over at Ray on Emacs, Raymond Zeitler has an interesting post on displaying the Org clocked in status. The default is to display it in the mode line but Zeitler wanted to display it in the title bar too. It turns out you can turn it off, display it in the mode line, display it in the title bar, or display it in both the mode line and title bar. These choices are controlled by the org-clock-clocked-in-display variable.

If you regularly clock in your tasks, having flexibility in where the status is displayed could be useful. The problem is I couldn’t find any reference to this variable in the help system or in timeclock.el, which handles the seemingly related timeclock-mode-line-display.

I updated my packages to make sure I had the latest Org Mode (I’m on Org 9.8.8) but I got the same results. I’m guessing that Zeitler is using one of the Emacs 31 prereleases, which implements this. If you’re interested in this feature and are on Emacs 31Try it on your system and see what happens. If it works for you, leave a comment.

-1:-- Displaying The Active Clock (Post Irreal)--L0--C0--2026-08-06T14:28:40.000Z

Srijan Choudhary: Emacs: desktop-save-mode without the automatic restore

Emacs has a built-in way to save and restore sessions: the desktop.el library.

As the Emacs manual says, putting this in the init file enables automatic save and restore of sessions:

(desktop-save-mode 1)

But, I wanted a different behavior:

  • Automatically save the session
  • But don't restore it automatically

I wanted to only use the session feature to restore sessions that I closed by mistake, or where I forgot to finish something before quitting Emacs. By default, I wanted a fresh session every time.

But this also means that some session history has to be maintained; otherwise, a fresh session will start to write to the desktop file and then I will not be able to recover the old session.

By default, the way to do this is to not set desktop-save-mode, but manually call desktop-save when needed, and then call desktop-read when needed. But, if I forget to save, I cannot restore.

This elisp is what I came up with to make it work the way I want:

(defvar my/desktop-snapshot-keep 5
  "How many previous sessions to keep desktop snapshots for.")

(defun my/desktop--snapshots ()
  (nreverse (directory-files desktop-dirname t
                             (concat "\\`" (regexp-quote desktop-base-file-name)
                                     "\\.[0-9]\\{8\\}T[0-9]\\{6\\}\\'"))))

(defun my/desktop--take-snapshot ()
  (let ((live (desktop-full-file-name)))
    (when (file-exists-p live)
      (let ((dest (concat live (format-time-string
                                ".%Y%m%dT%H%M%S"
                                (file-attribute-modification-time
                                 (file-attributes live))))))
        (unless (file-exists-p dest)
          (copy-file live dest t t)))))
  (dolist (old (nthcdr my/desktop-snapshot-keep (my/desktop--snapshots)))
    (delete-file old)))

(defun my/desktop--label (file)
  (format "%s   %d buffers"
          (format-time-string "%Y-%m-%d %H:%M:%S"
                              (file-attribute-modification-time
                               (file-attributes file)))
          (with-temp-buffer
            (insert-file-contents file)
            (how-many "^(desktop-\\(?:create-buffer\\|append-buffer-args\\) "
                      (point-min) (point-max)))))

(add-hook 'emacs-startup-hook
          (lambda ()
            (require 'desktop)
            (setq desktop-dirname (expand-file-name user-emacs-directory))
            (my/desktop--take-snapshot)
            (desktop--get-file-modtime)
            (let ((owner (desktop-owner)))
              (unless (and owner (desktop--emacs-pid-running-p owner))
                (desktop-claim-lock)))
            (desktop-save-mode 1)))

(defun my/desktop-restore (file)
  "Restore the desktop snapshot FILE, prompting for one interactively."
  (interactive
   (progn
     (require 'desktop)
     (let* ((snapshots (or (my/desktop--snapshots)
                           (user-error "No desktop snapshots in %s"
                                       desktop-dirname)))
            (choices (mapcar (lambda (f) (cons (my/desktop--label f) f))
                             snapshots)))
       (list (cdr (assoc (completing-read "Restore desktop session: "
                                          choices nil t)
                         choices))))))
  (desktop-release-lock)
  (let ((desktop-base-file-name (file-name-nondirectory file)))
    (desktop-read (file-name-directory file)))
  (desktop--get-file-modtime))

Note that desktop-save-mode must not be enabled before after-init-hook fires, because that's where desktop.el decides to automatically restore. So, I enable it in emacs-startup-hook, which runs later. Other things inside my emacs-startup-hook lambda are workarounds for how desktop.el works, so they can be fragile if there are major changes to it.

The lock check avoids a stale lock from a crashed Emacs silently disabling auto-save, and naming snapshots after the desktop file's mtime means restarting without saving anything doesn't use up one of the five slots.

Here's what it looks like when I call my/desktop-restore:

Emacs minibuffer showing a list of sessions to choose for restoring. Each entry shows the timestamp of last snapshot and number of buffers in that session.

Of course, this can be made better by showing more information per session (like number of projects, names of last edited files or last visited buffers). Vertico/marginalia can show it nicely. Also maybe a section in emacs-dashboard to show recent sessions and click/enter to restore. Something for another day.

-1:-- Emacs: desktop-save-mode without the automatic restore (Post Srijan Choudhary)--L0--C0--2026-08-06T08:10:00.000Z

Raymond Zeitler: Display Active Clock in Emacs Frame Title

I thought it would be helpful to modify the title of the Emacs frame to display the Org task I'm clocked in to. It turns out to be a lot easier than it sounds -- just change one Org variable in Customize.

The variable is org-clock-clocked-in-display. Its default value is mode-line. This means that the active clock displays only in the mode line. But it can have one of three other values: nil, frame title, both.

I've set it to both. Now the clocked task displays in both the mode line and the frame title. It shows the clock duration in square brackets followed by the task heading in parentheses, as shown below.

Screenshot of a small part of author's Emacs frame.  It shows the title as [1:00] (Pay Medical Bills).  Below that is the menu and the first three lines of author's Org file.

This simple update has improved my productivity significantly. The prominent display of the clocked task reminds me of what I started to do before I went down yet another rabbit. Not that rabbit holes are all bad -- I was in a rabbit hole yesterday when I found org-clock-clocked-in-display.

Try it out!

-1:-- Display Active Clock in Emacs Frame Title (Post Raymond Zeitler)--L0--C0--2026-08-05T16:20:25.067Z

TAONAW - Emacs and Org Mode: Emacs Config Gems - Part 3

My actual configs start with pretty usual stuff you’d see across many other Emacs configs. As I was going through these however, I started to wonder how things work and why they ended up the way we have them today. In turn, that led to reading about the intention behind those, which led to more options I didn’t think about. Also, it’s kind of fun. I’ve made two more passes for this part alone in the last couple of weeks, and I could probably go for a third. When reading this post, if you want the good stuff (in my opinion), get lost in the footnotes as I did. There’s a lot to explore. I just scratched the surface.

Load Melpa

Melpa is where many Emacs packages live. The story behind it (from what I can tell) revolves around one of Emacs’ maintainers, Bozhidar Batsov, who pointed to Melpa at some point in 2012, when Emacs’s then go-to third-party package repository went offline. He is not its creator — Melpa has been around before this post — but this seems to be the turning point where Emacs package maintainers started to really use it. I don’t have good coder/repository maintainer knowledge, but from what I read, it’s built on the same idea as Homebrew (for Mac), which was familiar to many of the Emacs folks at the time.

So let’s get Melpa:

(require 'package)
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)

UI and Fundamental Emacs tweaks:

Most Emacs folks with a config file like this like to kill the toolbar, scrollbar, and menu bar. I find that the menu bar makes Emacs look more like the other programs, especially in macOS1. Besides, it’s nice to look every now and then and be reminded of good Emacs kung-fu I forgot exists (org-sort, I’m sorry my friend, I’ll keep saying hi more often).

This is why the menu-bar is commented out in my config; I want it, but I also want to remember I could turn it off if I wanted:

(tool-bar-mode -1)
(scroll-bar-mode -1)
;; (menu-bar-mode -1) 

By default, help commands2 display the help buffer in a new window without selecting it. I find this annoying: if I bring up the help window, it’s usually because I want to scroll down to find something or copy (yank) something. On the other hand, if I just want to skim quickly in the help buffer, it’s helpful that the marker is on it for a quick q for quit.

In addition, we want to be able to navigate help menus as Emacs intended if we follow the links in a help buffer. help-window-keep-selected allows us to keep help in its own dedicated window, so it won’t open in a separate window once we follow a link. Going back to the original is the same as we do in other browser-like buffers in Emacs, with l (like in Eww, for example). Likewise, we can go forward with r. I should also expand here on various help options; they definitely deserve a mention, but this is another huge topic (you can get an idea from the footer) that will take a couple of weeks. I will come back to this someday.

(setq help-window-select t)
(setq help-window-keep-selected t)

Lisp is full of parentheses, and it’s easy to lose track. Turning show-paren-mode on means that standing on an open or closed parentheses highlights its matching counterpart, which is handy.

In addition, while we’re here: when we stand on an expression that is only showing a portion of an expression (meaning, we need to scroll down to see the rest) Emacs can highlight the expression for us to make sure we don’t get confused — this is the mixed option below for show-paren-style. If you want to see the whole expression highlighted every time you’re on it, there’s the option expression, which I’m leaving here for reference for myself. For now, I think it’s too much visual noise, especially since we’re using org-edit-special, which also highlights what we’re working on.

  (show-paren-mode t)
  (setq show-paren-style 'mixed)
;; (setq show-paren-style 'expression)

And now, a true Emacs classic. I don’t think I’ve seen a config without it: shortening “yes” or “no,” answer to “y” or “n” like any other program known to mankind. use-short-answers was introduced in Emacs 28.1, which is a more graceful way to do this, intended exactly for this purpose. Before that, back when I started to use Emacs, it was done with (fset 'yes-or-no-p 'y-or-n-p). If you have an older Emacs version, you’d need it.

;;  (fset 'yes-or-no-p 'y-or-n-p)
(setq use-short-answers t)

This one was annoying until I learned this option exists from someone else’s config years ago: “Emacs, please stop asking me if I want to kill process when exiting Emacs (Shell, etc.), just do it!” Yep.

(setq confirm-kill-processes nil)

Emacs has two annoying keyboard shortcuts that I should have disabled as soon as I started using it: C-x C-c (exit Emacs) and C-z (minimize Emacs). Both of them are too easy to press by mistake, especially C-z, which is undo in many other programs. If I had a penny for each time I minimized Emacs by mistake… When we want to minimize Emacs (and why would you want to do that? Emacs deserves a prominent space on your screen), we can just do it by clicking the minimize window itself, shamefully using our mouse, as we do with other apps. And Exiting Emacs? With a shortcut that seems like it fits somewhere in org-mode? No thanks. Let’s disable those:

(global-unset-key (kbd "C-x C-c"))
(global-unset-key (kbd "C-z"))

Turn off the annoying error beep, which I first discovered in SUSE Linux by turning on a visual warning (a flash) instead. This will flash the top and bottom lines of our window instead of going “beep!”

(setq visible-bell t)

Turn on visual line mode so text lines “wrap” inside the frame and don’t continue beyond the window’s edge. This is essential; I can’t read in Emacs without it. And a newcomer I discovered recently: since Emacs 30, we also have global-visual-wrap-prefix-mode, which preserves indentation on wrapped lines. For example, in a list in org-mode (made of dashes one under the other), a long line of text that wraps will align under its starting point (dash) in the list instead of jumping back to column 0 (the start of the window, to the most left). This has been a huge pet peeve. Finally!

(global-visual-line-mode t)
(global-visual-wrap-prefix-mode t)

Winner mode (included in Emacs) is sort of “undo” for changes in windows’ layout in Emacs. C-c ← goes back to our previous setting, and C-c → will “redo” the layout we just left. I use it all the time as in “oops, I closed the wrong window”3.

(winner-mode t)

Saw this over at https://emacsredux.com/blog/2026/04/07/stealing-from-the-best-emacs-configs/. This is a nice little trick, and it requires a bit of reading to understand what exactly it does: preventing Emacs from deleting whatever we have in the system’s clipboard when we kill a line, so it’s still in the kill ring. Here’s an example to help you grasp the idea and why it’s useful:

  1. We’re visiting a website with our default browser and copy its URL because we want to write about it in Emacs
  2. We go to Emacs, and we kill the line we’re on because we need some space
  3. Crap! Now we lost our paste in the clipboard, and we have to go back to the browser and grab that URL again!
  4. Wait, which tab was it? Did we close it? Should we look in our browsing history? Ugh!

Not anymore! With this little guy, when we kill the line (step 2 above), Emacs “injects” it into the kill ring. Now, when we yank with C-y, we will still get the last line we killed, yes, butlook up the kill ring with M-y and voilà! Your URL is there. No need to go find that URL again:

(setq save-interprogram-paste-before-kill t)

You know how for years Emacs “jumped” when you scrolled down large embedded images in your org buffers? Let’s turn on pixel-scroll-precision-mode for pixel scrolling instead of the default line scrolling to fix this. It’s pretty much what it reads: the default old way would “scroll” (it’s actually not scrolling at all) by a line of text at a time. Since our image of, say, 600px in height is treated as a single line of text, it will jump to the end of it. With this option turned on, Emacs would read the pixels from your mouse wheels as you scroll, and we get the smooth scrolling we’re used to.

Interesting to know: moving up and down the line without a mouse (arrows, or C-p and C-n) is moving by text lines, utilizing auto-windows-vscroll and line-move, which basically does in Emacs what arrow movement seems to be doing in modern applications: these apply this principle from the other direction — they natively work with pixels, so they treat these arrow movements as a fixed number of pixel movements, which is approximately a line of text. So, when you move up and down a large image (or say a PDF) in a browser, the browser thinks “ok, the user is moving up one line of text, which is.. hm.. let’s see… ah, it says it’s exactly 40 pixels, so let me show that”.

(pixel-scroll-precision-mode t)

test break

“Emacs is the editor of tinkerers and artisans; those who are eternally dissatisfied with all other tools because of their adamantine rigidity. Crafting, or shaping, your tools to meet your exacting needs is what Emacs excels at. Because of that, Emacs is – much to the chagrin of everyone who picks it up for the first time – squarely aimed at people who already know Emacs.”

In fact, if you’re curious about help and Emacs help, I suggest you head over there now and read that post. It expands on how to approach Emacs help by how to think Emacs, which is something that experienced Emacs users struggle to explain to newcomers.

As for the history of help in Emacs, I think it’s pretty safe to say it’s been there since the start, or at least since Emacs became intended for public use, sometime around the 1980s. By that point, most of the core help parts we know today (the tutorial, C-h t; the Emacs Manual, C-h R; as well as the framework itself) were already in place. The help system was there before GNU Emacs, back in the days of TECO Emacs that RMS worked on at MIT in the 1970s. You could browse the original code from then (preserved by MIT) and see how Stallman explained what certain commands do in emacs.doc inside that repository. Another interesting find about Emacs itself (and the help system that comes with it) is the EMACS paper written by RMS back in 1981. For the help system, take a look at page 17: “6. Self-Documentation and Extensibility” and the following chapter in page 18: “7. History”. Fascinating stuff.


  1. And here, dear reader, I fell down a deep rabbit hole, one that pushed this post a couple of days. There are two parts to this. The Emacs part, which I will explore at some point in the near future, has to do with Emacs’ menu itself, which is controlled by easymenu.el. This is an old Emacs package that’s been around since 1994, written by RMS himself — kind of. It was first created by Per Abrahamsen (who seems to be a bit of a mystery, but that’s digging for another day). In turn, this package was taken from lmenu.el, which was part of yet a different Emacs fork at the time. In a regular Emacs style, the menu bar can be completely reconstructed and changed — and that’s something I want to explore soon as a way to recall useful functions I keep forgetting exist (like having an “organize” menu with something like “org-sort” under it). This will be a fun project of its own for another day. Meanwhile, the other, bigger rabbit hole is the story of the GUI menu itself as we have it today. Apple has a big role here, back in the days before Steve Jobs left Apple and worked on the Lisa. This seems to be the first usage of a graphical interface in personal computers, and the menu, which was taken from Xerox, was a big part of it (there’s a short YouTube video there that would give you a quick brief of what happened). This means that I am right on keeping the menu in theme with the rest of the Mac apps, based on history! ↩︎

  2. Emacs’ built-in help deserves its own series of posts. Looking at it now, from the eyes of someone who used it for a couple of years, I find Micky’s description satisfying, reflecting what I felt at the time: ↩︎

  3. I forget how exactly, but some research into winner-mode history (turns out it’s been around since 1997 — also, a lot of goodies here to look into!) led me down the path of tabs in Emacs, tab-bar in particular. I didn’t think much about tabs in Emacs, but since I’ve been using Kubuntu for a while and experimented with its Workspaces, I understand the concept better. I looked into BSAG’s post, and it looks like you can hide the tabs while still displaying them on the mode line, and of course, you don’t have to use the mouse. The concept of having a whole workplace completed with its unique window arrangement available with a single keyboard shortcut is alluring. Now, instead of playing around with where and how I want my windows to display, I can just save (or re-create) those, and I always have a “work” environment vs a “personal” environment within the same Emacs frame. This can be a good organizational feature. I’m going to dig more into this one. ↩︎

-1:-- Emacs Config Gems - Part 3 (Post TAONAW - Emacs and Org Mode)--L0--C0--2026-08-05T13:06:09.000Z

Protesilaos: Emacs: the ‘speedbar’ built-in file explorer (Emacs 31)

Raw link: https://www.youtube.com/watch?v=Crn6x3RmmyQ

In this video I demonstrate the built-in ‘speedbar’ command. This is the file explorer that Emacs ships with. Starting with Emacs 31, the ‘speedbar’ can be configured to appear on the side of the frame. Whereas the default is to show up in a separate frame. To me, the default is impractical, whereas the new option turns ‘speedbar’ into a very useful piece of functionality.

-1:-- Emacs: the ‘speedbar’ built-in file explorer (Emacs 31) (Post Protesilaos)--L0--C0--2026-08-05T00:00:00.000Z

Charlie Holland: Gödel, Escher, Elisp: The Beauty of Macros

1. TLDR

escher-drawing-hands.jpg

If you are an Emacs user with a keen eye, you will have noticed that in Emacs Lisp, code is data. After all, 'Lisp' is shorthand for 'List Processing'. One of Elisp's most beautiful features is the fortuitous blur between the thing that is processing the list (the program) and the list itself (the data). The macro in Elisp is a utility that exploits this blur and allows you to leverage this dualism between program and data in many useful and fascinating ways.

In this post, I want to swoon about macros, explain what "homoiconic" actually means, demonstrate their ubiquity in Elisp, depict their beauty on a detour through Hofstadter's strange loops and Escher's prints, and finally show off some tooling (macroexpand, emacs-lisp-macroexpand, macrostep) that enhances both comprehension and appreciation of macros.

Here is the Escher imagery we'll be leaning on along the way:

2. Programs as Data, Data as Programs   emacs elisp lisp

The kernel of Lisp has a crystalline purity that not only appeals to the esthetic sense, but also makes Lisp a far more flexible language than most others.

— Douglas Hofstadter

An important word for this post is homoiconic. A language is homoiconic when its programs are written in the language's own data structures.

Few languages are homoiconic, and the Lisp family wears the property most proudly, with Emacs Lisp (Elisp) being the dialect many of us are most familiar with. In Elisp, source code is lists, symbols, strings, and numbers. Code looks exactly the same as lists you build with cons and take apart with car and cdr.

The distinction between program and data is exhibited by a specific, special character, the glorious ':

;; a program: evaluates to 3
(+ 1 2)

;; data: a list of three elements — a symbol and two numbers
'(+ 1 2)

The quote tells the evaluator not to run the form that follows, but to treat it as plain data (a list).

To emphasize that program and data are equivalent in Elisp, running eval on the quoted list (as in (eval '(+ 1 2))) will turn it into a program, where the function is addition, and its arguments are the numbers 1 and 2.

That dualism lies at the heart of the language. Any piece of code is one character away from being a value you can inspect, transform, and rebuild; and any suitably-shaped value is one function call away from being a program.

So in Elisp, we say Program = Data, even though that's a little too simplistic, because we saw how correctly the Lisp interpreter deciphers when a list is being represented as a program versus when it is being represented as data…. The point is the list: that's the unifying form. Maybe more appropriately, we can say the program and data take the same form, or as previously mentioned, Elisp's programs are written in Elisp's own data structures.

This post was motivated by a simultaneous obsession with Douglas Hofstadter's writing and Elisp macros, so be prepared for many depictive metaphors from one of Hofstadter's favourite artists, M.C. Escher. Here's the first:

Escher drew this kind of dualism as a woodcut. The ants of Möbius Strip II appear to march on both sides of a strip. The image is provocative enough at first glance, but I invite you to follow any one of them around and discover that the two sides are one continuous surface. Program and data are the two sides of Elisp's homoiconic Möbius.

escher-mobius-strip-ii.jpg

Figure 1: M.C. Escher, Möbius Strip II (1963). Two sides, one surface. © The M.C. Escher Company.

In most languages, metaprogramming lives in a separate layer with its own representation of code like templates, reflection APIs, token streams, quasi-quoted ASTs. Some of those layers are crude and some are genuinely sophisticated, but each is a wall between code and data. In Elisp there was never a wall to tunnel through. Elisp enables metaprogramming, but it's the same language, and the same data structures, all the way down.

3. What a Macro Actually Is   emacs elisp macros

Consider a regular function in Elisp. A function receives values and computes a value at runtime.

On the other hand, a macro receives code (the raw, unevaluated forms typed at its call site) and returns new code, which is then evaluated in its place. Macros run at expansion time, before your program does. I like to think of macros as little programs that write other programs given the arbitrary forms they can accept. The complexity of that form -> program projection is essentially infinite, or at least bounded by what you can express in Elisp, which is very likely bounded by your imagination.

The macro's form -> program toolkit is quasiquotation: backquote ` builds a code template, comma , inserts a computed piece, and ,@ splices in a list. Here's the smallest real macro I can write, a reimplementation of unless:

(defmacro my-unless (condition &rest body)
  "Run BODY unless CONDITION is non-nil."
  (declare (indent 1))
  `(if ,condition nil ,@body))

We can actually ask Emacs what this macro will get expanded to. The first code block is the call to macroexpand-1, the second is the expansion it returns:

(macroexpand-1
 '(my-unless (file-exists-p "~/notes")
    (make-directory "~/notes")
    (message "created it")))
(if (file-exists-p "~/notes") nil
  (make-directory "~/notes") (message "created it"))

To anticipate a common question: why couldn't my-unless be a function? Function arguments are evaluated eagerly, before the function ever sees them. A function version would evaluate (make-directory "~/notes") while its arguments were being prepared, before the condition could ever be consulted. So, when ~/notes already exists, instead of doing nothing it would signal a file-already-exists error, and the message would never run at all. In other words, the function receives the results of the body, but the point of using a macro here is to decide whether the body runs at all. A macro receives the body as inert data, so control flow itself is up for grabs. In this way, you are extending what the language can express.

4. You've Been Using Macros All Along   emacs elisp macros

Macros may seem specialist or eccentric…. I hope this surprises the Elispiens who are reading this! It certainly surprised me!

  • when and unless are macros over if.
  • dolist and dotimes are macros over while.
  • push, pop, and setf are macros that rewrite themselves into the right mutation for the place you provide them with.
  • with-current-buffer, with-temp-buffer, and ignore-errors are macros that wrap your code in the correct save-and-restore ceremony so you never have to type it.
  • Even defun is a macro!

The most justifiably famous macro in any Emacs config is use-package:

(use-package magit
  :bind ("C-c g" . magit-status)
  :hook (git-commit-mode . flyspell-mode))

:bind and :hook aren't Elisp, but rather keywords in a small configuration language, and the use-package macro is its 'compiler', expanding the declaration into the require calls, keymap bindings, hooks, and autoload deferrals that would otherwise need to be written out by hand in their full, verbose form.

The define-minor-mode macro is similar in this way. One declaration expands into a variable, an interactive toggle command, keymap wiring, and documentation. This is what is meant by macros letting you grow a language toward the problem. With macro use, your config can read more like a declarative description of what you want, because someone built a macro for that (in use-package's case, shout out to John Wiegley).

The most shocking instance in my deep dive was defun. Evaluate (macrop 'defun) and the result is t, indicating that, yes, defun is a macro (again, the first code block is the call to macroexpand-1, the second is the expansion it returns):

(macroexpand-1 '(defun greet (name) "Say hi." (message "Hi, %s" name)))
(defalias 'greet #'(lambda (name) "Say hi." (message "Hi, %s" name)))

Defining a function turns out to mean this:

  • build an anonymous function
  • alias a symbol to it

More surprises….

Did you know that lambda itself is also a macro (albeit a delightfully small one that expands into a function-quoted version of itself)? Surely not my beloved defcustom? Yes, my fellow Emacsapien, that is also a macro.

(macroexpand-1
 '(defcustom chiply/favorite-lithograph "Drawing Hands"
    "Which Escher lithograph to contemplate while macroexpanding."
    :type 'string
    :group 'chiply))
(custom-declare-variable
 'chiply/favorite-lithograph '"Drawing Hands"
 "Which Escher lithograph to contemplate while macroexpanding."
 :type 'string :group 'chiply)

(If you try this in *scratch* or ielm you'll see something gnarlier: the default value comes back wrapped in (funcall #'(lambda () ...)). Under lexical binding; which is the default in *scratch*, ielm, and M-: since Emacs 27; defcustom wraps the default in a closure so Custom can re-evaluate it later. Org evaluated this block with dynamic binding, hence the simpler form above. Either way the point stands: defcustom is a macro, and one expansion away from a plain function call. Just know that macro's expansion can depend on the environment it expands in.)

cl-loop, that entire iteration mini-language, much overused by yours truly? That's also a macro!

If you want to see all the macros, just run this.

(let (names)
  (mapatoms (lambda (s) (when (macrop s) (push (symbol-name s) names))))
  (with-temp-buffer
    (setq fill-column 72)
    (insert (mapconcat #'identity (sort names #'string<) " "))
    (fill-region (point-min) (point-max))
    (buffer-string)))

It seems like all the code you are writing is somehow being compiled in place to other code, so where does this end? It ends at the special formsif, let, setq, while, quote, save-excursion, condition-case, and their friends implemented in C. When you macroexpand any Elisp program all the way down, what remains is composed of special forms, plain function calls, and the variables and constants they operate on. The functions do the work (about fifteen hundred are C primitives like car and cons; the rest are written in Elisp). The special forms decide how evaluation flows. And every piece of syntax above that floor (when, dolist, setf, use-package, even defun) is macros written in Elisp. The foundation is C, but the architecture is built out of the tower's own bricks.

The distinction between special form and macro deserves closer inspection, because at the call site a macro and a special form are indistinguishable. Neither special forms nor macros evaluate their arguments the normal way, which is why when and if feel similar. The difference is clear when you introspect. when is a macro, so it is obliged to explain itself: macroexpand turns it into if. In contrast to when, if explains nothing about itself — it is an evaluation rule, wired into the interpreter's C. A macro must always expand away, whereas a special form is where expanding stops. Put another way, a macro is a special form you're allowed to write yourself, on the condition that it has to compile down to the real forms. The real ones number exactly twenty-two in the Emacs I'm writing this in (swap special-form-p for macrop into the census above to meet them), and cond, and, and or are among them.

Escher cut his Tower of Babel in 1928, and its subject is a construction project failing, because the builders stopped sharing a language. Elisp's tower stands for precisely the opposite reason: from use-package at the summit down to the special forms at the footing, every floor is written in the same tongue.

escher-tower-of-babel.jpg

Figure 2: M.C. Escher, Tower of Babel (1928). Babel's construction stalled when its builders' languages diverged; Elisp's tower keeps rising because every floor expresses the same language (Elisp). © The M.C. Escher Company.

5. Rolling Your Own   emacs elisp macros config

I think this is a common use case. Let's say you keep writing a command that sets a variable and reports what happened. Maybe you have one for debug-on-error, one for truncate-lines, etc…. The pattern of thought is: "give me a command that toggles this variable." Capturing the pattern in a macro is useful in this case:

(defmacro deftoggle (var)
  "Define a command `chiply/toggle-VAR' that toggles the variable VAR."
  `(defun ,(intern (format "chiply/toggle-%s" var)) ()
     ,(format "Toggle the variable `%s'." var)
     (interactive)
     (setq ,var (not ,var))
     (message "%s is now %s" ',var (if ,var "on" "off"))))

One line per toggle, forever after:

(deftoggle debug-on-error)
(deftoggle truncate-lines)

Expanding the first one shows what you actually wrote:

(macroexpand-1 '(deftoggle debug-on-error))
(defun chiply/toggle-debug-on-error nil
  "Toggle the variable `debug-on-error'." (interactive)
  (setq debug-on-error (not debug-on-error))
  (message "%s is now %s" 'debug-on-error
           (if debug-on-error "on" "off")))

Our single line macro invocation does a lot:

  • It interned a new symbolM-x chiply/toggle-debug-on-error now exists as a command.
  • It wrote a docstring, computed at expansion time, that shows up properly in C-h f.
  • It emitted an interactive declaration.

Now, a fair objection: a plain function could have produced all three of these effects at runtime - defalias interns a symbol, accepts a docstring, and wraps an (interactive) lambda. What a function can't give you is the call site and the timing. You'd write (make-toggle 'debug-on-error), a quoted symbol handed to runtime machinery, and the byte compiler would never see the definition. The macro receives the bare name and leaves a real defun in the expanded source, where C-h f, the byte compiler, and every other source tool can find it!

You haven't merely written a helper, but more importantly, you've added a new defining form to the language, a small sibling of defun and defvar that communicates in the diction of your problem domain. That's another beauty of Elisp macros: the abstraction is expressed and interpreted at the same level as the primitives it imitates.

6. Strange Loops and Drawing Hands   emacs elisp hofstadter geb

I've been recently obsessed with the writing of Douglas Hofstadter, and he spent three of his Scientific American columns in 1983 teaching Lisp (the first, "Lisp: Atoms and Lists", survives online), later collected in Metamagical Themas. He opens with a mission statement:

Why is most AI work done in Lisp? There are many reasons, most of which are somewhat technical, but one of the best is quite simple: Lisp is crisp. Or as Marilyn Monroe said in The Seven-Year Itch, "I think it's just elegant!"

— Douglas Hofstadter, "Lisp: Atoms and Lists" (1983)

It's no coincidence that the author of the great book about self-reference (Gödel, Escher, Bach) fell for this language: Lisp is probably the most GEB-shaped artifact in computing.

The engine of GEB is Gödel numbering: encoding statements about arithmetic as arithmetic, painstakingly numbering every symbol until number theory could be made to talk about itself. (For a gentle tour of how the proof uses it, see Quanta's explainer; for this post, the gist is enough.)

It took a stroke of genius to build that bridge, because sentences and numbers live in different worlds. In Lisp, it seems, the bridge comes built-in, as the sentence already is the data structure. Hofstadter saw the temptation, and near the end of GEB he stages this exact argument, letting the Crab assume the burden of proof, in the book's most enchanting 'fugue':

Well, in the programming language LISP, you can talk about your own programs directly, instead of indirectly, because programs and data have exactly the same form. Gödel should have just thought up LISP, and then—

— the Crab, in Gödel, Escher, Bach (20th-anniversary ed.), p. 738

The Crab is making this post's argument: programs and data have exactly the same form, and quote is precisely the formalized quotation he goes on to wish Gödel had invented. But!

But the Author (Hofstadter) interrupts him:

Author: …no reference is truly direct — every reference depends on SOME kind of coding scheme. It's just a question of how implicit it is. Therefore, no self-reference is direct, not even in LISP.

Hofstadter is right, of course. Look under a quoted form and there is still a code: reader syntax, interned symbols, and cons cells laid out in memory. Lisp didn't abolish Gödel's bridge, but arguably simplified it for the programming use case. It built the bridge so well, and sank it so deep beneath the syntax, that you can cross it naively.

A strange loop is Hofstadter's coinage, and GEB defines it in its opening pages:

"The 'Strange Loop' phenomenon occurs whenever, by moving upwards (or downwards) through the levels of some hierarchical system, we unexpectedly find ourselves right back where we started."

Escher's Drawing Hands is his canonical image of this phenomenon. In this hand-drawn drawing of drawing hands, each hand draws the hand that is drawing it, and is both sketcher and sketch at once. Elisp hides the same lithograph in its bootstrap. defmacro, the form you use to create macros, is itself a macro. The hand that draws hands is drawn; the macro that defines macros is a macro.

escher-drawing-hands.jpg

Figure 3: M.C. Escher, Drawing Hands (1948). Each hand draws the hand that draws it. (macrop 'defmacro)t. © The M.C. Escher Company.

If the Drawing Hands image has you thinking about macros, know that the loops nest. A macro can expand into code that contains more macro calls — remember that defun hiding inside deftoggle? Take it one story higher:

(defmacro deftoggles (&rest vars)
  "Define a toggle command for each variable in VARS."
  `(progn ,@(mapcar (lambda (v) `(deftoggle ,v)) vars)))

(deftoggles debug-on-error truncate-lines)
;; ⇒ (progn (deftoggle debug-on-error) (deftoggle truncate-lines))
;; ⇒ ... (defun chiply/toggle-debug-on-error () ...)
;; ⇒ ... (defalias 'chiply/toggle-debug-on-error #'(lambda () ...))

A program writing a program writing a program writing a program. Hofstadter's running metaphor for the Lisp interpreter is a genie granting wishes, and even while introducing the language's basics, having just shown the reader that Lisp statements are themselves lists, he spots exactly this loop:

…the Lisp genie, by manipulating lists and atoms, can actually construct new wishes by itself. Thus the object of a wish can be the construction — and subsequent evaluation — of a new wish!

— Douglas Hofstadter, "Lisp: Atoms and Lists" (1983)

A macro is precisely that: a wish whose object is a new wish.

This kind of macro expansion makes me think of Escher's Print Gallery, where a young man stands in a gallery looking at a print of a seaport, and the print swells outward until it contains the gallery, and the young man, inside it. Each level of a macro expansion is a picture that turns out to contain the room you were standing in.

Escher famously couldn't finish this paradox. At the center of the lithograph, where the loop closes on itself, he left a blank patch and signed his name. The Elisp tower has its blank patch too. When you expand all the way down, you bottom out at the special forms, where the language stops being written in itself and things move over to C.

escher-print-gallery.jpg

Figure 4: M.C. Escher, Print Gallery (1956). The print contains the gallery that contains its viewer; at the center, where the loop closes, Escher left a blank patch and his signature. © The M.C. Escher Company.

The program–data dualism has its own lithograph: Reptiles, where a lizard crawls out of a flat sketchbook drawing, climbs up over a book and a dodecahedron as a living, three-dimensional creature, and then climbs back into the page to become a drawing again. That is quote and eval exactly. A quoted form is the lizard on paper (inert, flat, safe to handle), whereas eval is when it climbs off the page and comes to life. Macros do their work on the paper lizards, rearranging drawings that will shortly be alive, their hearts beating in the Lisp interpreter.

escher-reptiles.jpg

Figure 5: M.C. Escher, Reptiles (1943). Off the page, around the desk, back onto the page. This is eval and quote as lithograph. © The M.C. Escher Company.

There's one more Hofstadter obsession that Lisp exhibits. GEB's deepest question is how meaning condenses out of meaningless symbol-shuffling, layer by layer. Lisp is unabashed about the importance of symbols here because its atoms are literally called symbols. Symbols are Elisp's first-class objects you can pass around, compare, and define (deftoggle interned one for you). And each floor of the expansion tower speaks its own language: the use-package form speaks configuration, its expansion speaks hooks and keymaps, and the floors below speak control flow (special forms again), until meaning has condensed all the way into machine operations. No floor is the "real" one. Instead, the whole thing is a tangled hierarchy that you can inhabit. With Emacs, you can ride up and down at will. Here's how.

7. Seeing Through the Magic   emacs elisp tooling

If macros were opaque, all of this would be unsettling, because you could create arbitrarily abstracted code-expanding-code that defies introspection. What keeps macros honest is that Emacs will show you the expansion at every level, as long as you know the utilities needed to make it do that. There's a passage late in GEB, where Hofstadter is explaining why introspection can't reach our own machinery, that makes the stakes of that vivid:

We feel self-programmed. Indeed, we couldn't feel any other way, for we are shielded from the lower levels, the neural tangle. Our thoughts seem to run about in their own space, creating new thoughts and modifying old ones, and we never notice any neurons helping us out! But that is to be expected. We can't.

An analogous double-entendre can happen with LISP programs that are designed to reach in and change their own structure. If you look at them on the LISP level, you will say that they change themselves; but if you shift levels, and think of LISP programs as data to the LISP interpreter (see chapter X), then in fact the sole program that is running is the interpreter, and the changes being made are merely changes in the pieces of data. The LISP interpreter itself is shielded from changes.

— Douglas Hofstadter, Gödel, Escher, Bach (20th-anniversary ed.), p. 692

escher-hand-with-reflecting-sphere.jpg

Figure 6: M.C. Escher, Hand with Reflecting Sphere (1935). The observer holds the sphere that contains the observer: introspection with the shield lifted. © The M.C. Escher Company.

The second paragraph is again evocative of Print Gallery's blank patch. However wildly your macros rewrite the language, the machinery below them is never touched. The first paragraph highlights a special way in which your editor (Emacs) is better off than your brain. We feel self-programmed but are shielded from our own neural tangle; we cannot watch our thoughts being implemented. In Emacs, the shield is optional.

Escher made a self-portrait of that privilege: Hand with Reflecting Sphere, the artist holding the mirror in which the artist, the room, and the holding hand are all visible at once. Every level of the expansion is there to be seen. In Emacs, these are the tools you can use to introspect macros and detangle the tangled hierarchy:

  • The functions. macroexpand-1 performs exactly one step of expansion — when becomes if, and stops. macroexpand keeps expanding the top-level form until it isn't a macro call anymore. macroexpand-all recurses into subforms too, grinding everything down to special forms and function calls. Evaluate them in *scratch* or ielm, wrapped in pp for readable output.
  • In place, in your buffer. M-x pp-macroexpand-last-sexp with point after a form pops the pretty-printed expansion into a separate buffer — the low-commitment option. M-x emacs-lisp-macroexpand with point before a form is the committed one: it replaces the form in your buffer with its expansion, properly indented. It's unbound by default and undo restores the original, so it's a safe and weirdly satisfying way to peel a layer off right where you're working.
  • Interactively: macrostep. macrostep (on MELPA) makes macro debugging feel like using a debugger. M-x macrostep-expand on a macro call shows the expansion inline, as an overlay. Press e to expand the next macro call inside the expansion, c to collapse a level, q to collapse everything and leave. Macro-generated symbols are highlighted, so you can see exactly which code came from your template and which came from the call site. When a macro you're writing misbehaves, stepping through its expansion layer by layer, in place, is usually all the debugging you need.

And if you want to know what running macrostep feels like, Escher printed that too. Metamorphosis II is a single strip, nearly four metres long, that begins with the word metamorphose, dissolves it into a checkerboard, the checkerboard into lizards, the lizards into honeycomb, bees, fish, birds, a town on the Mediterranean, a chessboard, and finally, at the far end, the word it began with. Stepping through an expansion is walking that strip: deftoggles to deftoggle to defun to defalias. Meaning gets transformed one panel at a time, and the first and final forms are both Elisp.

escher-metamorphosis-ii.jpg

Figure 7: M.C. Escher, Metamorphosis II (1939–1940), shown in four stacked rows. One form becomes another by lawful local steps, like a macro expansion laid out lengthwise. © The M.C. Escher Company.

8. With Great Power   emacs elisp macros

Hofstadter, watching definitions build on definitions in that same column, issues a warning: "The whole thing snowballs rather miraculously, and you can quickly become overwhelmed by the power you wield."

He's right, and power over syntax cuts both ways. A macro nobody else can read is a private language. A macro that evaluates its arguments twice, or accidentally captures a variable the caller was using (the classic fix is generating fresh symbols with gensym), fails in ways plain functions don't.

Escher drew this failure mode. A buggy macro is Escher's Belvedere, where every line of the expansion is locally reasonable, but where the whole is impossible. (Notice the boy on the bench in the foreground, calmly studying the impossible cube in his hands. That's you, mid-macroexpand.)

escher-belvedere.jpg

Figure 8: M.C. Escher, Belvedere (1958). Joints are locally sound, but the building is globally impossible. The classic shape of a macro bug. © The M.C. Escher Company.

And for the macro that expands into a call to itself with no base case, Escher supplied his classic staircase illusion, where the monks of Ascending and Descending climb a loop that rises forever and never exits.

escher-ascending-descending.jpg

Figure 9: M.C. Escher, Ascending and Descending (1960). A staircase that rises forever: the macro that expands into itself. © The M.C. Escher Company.

The Emacs community's rules of thumb are worth keeping in mind. Reach for a function first, and use a macro when you need to do something that a function can't, like controlling evaluation, establishing bindings, or defining new things. And keep expansions boring. The cleverness belongs in the macro's template, not its output.

I hope you notice these are the responsibilities of a language designer, because that's what a macro makes you.

9. Bending the Metal   emacs elisp lisp

Because programs are data in Elisp, the language can be reshaped in the language. The boundary between writing a program and designing a language dissolves.

Escher's Waterfall is a portrait of such a machine. The water falls, turns the wheel, and sets off along an aqueduct, where, three bends later, it pours over the top of its own fall again. The loop powers itself. That is what a self-extending language looks like from the outside: Elisp, extended by macros, written in Elisp.

escher-waterfall.jpg

Figure 10: M.C. Escher, Waterfall (1961). Every stretch of the channel runs downhill, and the water returns to the top of its own fall: a loop that powers itself. © The M.C. Escher Company.

-1:-- Gödel, Escher, Elisp: The Beauty of Macros (Post Charlie Holland)--L0--C0--2026-08-04T14:08:50.000Z

Andros Fenollosa: EWW, the Emacs browser you underestimate

The first time I opened a web page inside Emacs I thought it was a trick, a hack, a toy... studying it in depth I saw I was wrong. EWW is a browser written 100% in Emacs Lisp, with no external engine behind it (no WebKit, Blink or Gecko). It is absurdly lightweight, intelligently designed and, almost without meaning to, it hands you a platform to do scraping or automate web tasks in a few lines. A tool with enormous potential that many people don't know about, even within the Emacs community itself.

And the best part is that you already have it installed. It ships with Emacs by default. To launch it just run M-x eww and type a URL or a search term (it will open DuckDuckGo).

Is it a replacement for Chrome or Firefox? No, and it doesn't try to be. It plays in a different league.

Two pieces: EWW and SHR

What we call "the Emacs browser" is really two pieces working together.

  • EWW (eww.el) is the browser layer: URLs, history, bookmarks, forms, cookies, downloads and sessions.
  • SHR, or Simple HTML Renderer (shr.el), is the engine that turns HTML into text inside a buffer. And EWW is not the only one using it: Gnus for mail, elfeed for feeds, and quite a few other packages share it too.

Here's the key: SHR doesn't draw a page, it translates it. It takes the HTML and paints it as Emacs text, with its faces and its properties. What does it understand along the way? Quite a bit more than you'd imagine:

  • Rich text: b, i, em, strong, u, s, code, tt, mark, ins, del, sup, sub, abbr, bdo/bdi.
  • Structure: h1..h6, p, div, blockquote, pre, hr, ul/ol/li, dl/dt/dd.
  • Links and tables.
  • Images: it understands data: URIs (base64), srcset (it picks the resolution), cid: (mail), scaling with shr-max-image-proportion, animation and zoom. And if a src is broken, it falls back to its alt text, as it should.
  • MathML: it keeps the TeX annotation, it does not render the formula.

Forms are a curious case: SHR doesn't add them, EWW layers them on top via shr-external-rendering-functions. Thanks to that you can submit GET and POST forms, and even multipart/form-data to upload files.

Two things are missing from the list: JavaScript and CSS.

What EWW doesn't do (and why that's fine)

EWW is not meant to run modern web applications. Its limitations aren't an oversight, they are the reason it's so fast and so lightweight. But you'd better be clear about them before you get frustrated.

No JavaScript. This rules out, in one stroke, any SPA (React, Vue, Angular), infinite scroll, content that arrives via fetch or XHR, and most of today's web. If a page needs JS to paint itself, in EWW you'll see little or nothing.

No CSS. The code itself confesses it in its header: "It does not do CSS, JavaScript or anything advanced". In practice:

  • <style> sheets and <link rel=stylesheet> are ignored completely. Only the inline style attribute is read, and only if it contains color, display (specifically none) or border-collapse. Everything else (font-size, margin, padding, float, flex, grid, text-align...) is thrown in the bin.
  • Colors require (display-color-cells) >= 88. And the contrast system is surprisingly serious: it converts to CIE Lab and uses CIE DE2000 distance to make sure the text is readable.
  • There are no class or id selectors, no cascade, no specificity. Nothing.

The parser is not HTML5-conformant. It uses libxml2, which is tolerant but does not follow the HTML5 parsing algorithm to the letter. Manual patches are applied to plug the holes.

EWW is not for SPAs, online banking, JS dashboards, dynamic forms, anything that throws a "enable JavaScript to continue" at you, embedded video or audio, or layouts that are only legible thanks to CSS. It's not its turf, and forcing it is a waste of time.

Scraping out of the box

EWW leans on libxml-parse-html-region, which gives you back the DOM as an S-expression. And Emacs includes dom.el to walk it. That makes scraping trivial, and you don't even need to open EWW.

Look at this script. It extracts the headlines from the Hacker News front page:

(require 'dom)
(require 'url)
(require 'cl-lib)

(defun demo-scrape (url)
  "Download URL and return the Hacker News headlines as a list of conses.
Each element is (TITLE . HREF)."
  (with-current-buffer (url-retrieve-synchronously url t t 30)
    (goto-char (point-min))
    ;; Skip HTTP headers until the first blank line.
    (re-search-forward "\r?\n\r?\n" nil t)
    (let* ((dom (libxml-parse-html-region (point) (point-max)))
           ;; On HN each headline is <span class="titleline"><a>...</a>.
           (titles (dom-by-class dom "titleline")))
      (mapcar (lambda (node)
                (let ((a (dom-child-by-tag node 'a)))
                  (cons (string-trim (dom-texts a))   ; link text
                        (dom-attr a 'href))))          ; destination
              titles))))

(defun demo-scrape-hn ()
  "Download the Hacker News front page and show the headlines in a buffer."
  (interactive)
  (let ((items (demo-scrape "https://news.ycombinator.com/")))
    (with-output-to-temp-buffer "*HN headlines*"
      (princ (format "Headlines found: %d\n\n" (length items)))
      (cl-loop for (title . href) in items
               for i from 1
               do (princ (format "%2d. %s\n    %s\n\n" i title href))))))

;; Evaluating the buffer (M-x eval-buffer) runs it directly:
(demo-scrape-hn)

Evaluate it with M-x eval-buffer and a temporary buffer will pop up with the list of headlines and their links. No external libraries, nothing to install.

This is possible because dom.el gives you a handful of functions that do the heavy lifting: dom-by-tag, dom-by-class, dom-by-id, dom-child-by-tag, dom-attr, dom-text and dom-texts. And if instead of the raw HTML you want the already-rendered text (for example, to index the "readable" version of a page), you can run the DOM through shr-insert-document in a temporary buffer and keep buffer-string. This is, literally, the foundation on which elfeed or mu4e are built.

Designing "for EWW" is designing well

Let me switch perspective. So far we've talked about EWW as a reader. But what if you're the one publishing? What if you want your site to look flawless in there?

The good news is that designing for EWW isn't learning some weird dialect. It's going back to the principles of semantic HTML, the ones that should never have been abandoned. EWW renders HTML as a structured document, not as a canvas painted with CSS. Follow these ideas and, as a bonus, your site will be more accessible everywhere.

DOM order is on-screen order

SHR walks the tree in document order and inserts the text just as it finds it. There is no CSS reordering: float, flex, grid, order and position don't exist. Whatever you put first in the HTML appears first.

So place the main content as early as possible in the DOM, or at least right after opening the <body>. Long <nav> blocks and footers go at the end.

Mark up structure with tags, not with styles

SHR gives its own faces to h1..h6, b/strong, i/em, u, code, pre, blockquote, lists, mark and del/ins. Use them for what they mean, not for how they look:

  • Real headings <h1>..<h6> for hierarchy. Never a <div class="big">.
  • <ul>/<ol>/<li> for lists and <dl>/<dt>/<dd> for definitions.
  • <blockquote> for quotes (it indents) and <pre> for preformatted blocks or ASCII art (it disables reflow).
  • <code> for inline code (fixed-width face).

Watch out for the HTML5 semantic elements (article, section, nav, header, footer, main, aside, figure): they have no render of their own. They're treated as transparent containers and only contribute their textual content. They're fine for giving meaning to the document, but don't expect them to "show".

Don't rely on CSS for anything essential

You already know: only the inline style attribute is read, and only color, background-color, display and border-collapse. The rest is ignored. From that come three rules worth tattooing on yourself:

  • Your page must be legible with CSS completely disabled. If it isn't, that's not an EWW problem, it's an HTML problem.
  • Don't hide content with a display:none class. SHR won't apply it and that content will show up anyway. If you really need to hide something it would have to be style="display:none" inline, but that's bad practice. Better not to put that content in at all.
  • Don't convey information with color alone. Even though inline color works, it demands enough contrast (that strict CIE DE2000 filter) and on poor terminals it isn't applied at all. A "required field in red" or a "green = correct" vanishes. Always back it up with text or symbols.

And a consequence that sneaks in: spacing (margin, padding, line-height) doesn't exist. Separation comes from paragraph breaks. Structure with real <p> tags, not with stray <br> or empty divs.

Tables: for data only, never for layout

SHR draws <table> as a surprisingly good ASCII grid: it measures columns, distributes widths and even emulates colspan and rowspan. But it has its rules:

  • Use them only for real data. A layout table produces an absurd, illegible grid.
  • Watch the width. If the sum of the columns exceeds the frame, EWW just turns on truncate-lines and the experience degrades. Fewer columns and short cells fare much better.
  • Images inside cells aren't embedded in the cell (a buffer limitation): they're inserted after the table. If order matters, avoid images in there.

Images with alt and srcset

Yes, Emacs shows images in graphical buffers. But don't get cocky:

  • Always give a descriptive alt. It's what shows if the image is blocked, broken or if the user browses without images. In many EWW flows, the alt is the content.
  • srcset is supported: EWW picks the right resolution for the frame width, so offer it several.
  • Images load asynchronously over a placeholder, they don't block the text render. Don't rely on an image to communicate anything critical.
  • data: URIs (base64 included) work, handy for small embedded icons.

Forms that actually work

Forms are fine if they're pure HTML with action and method (GET or POST, including multipart/form-data for files):

  • No JavaScript submissions (onclick, fetch). Use a real <form> with its <input type="submit"> or <button>.
  • Put name on every field and value for the defaults. EWW collects by name.
  • The recognized text types (text, password, email, number, date, color and textarea itself...) are painted as editable fields. The rest degrade to text. checkbox, radio and select work.
  • Associate a <label> with each field: keyboard navigation will thank you.

Help the readable mode

If you want your article to look perfect with eww-readable, know your enemy. Its heuristic scores each node by word count, penalizes links (it subtracts their words), rewards images, and keeps the node with more than 100 words and the highest score.

The practical takeaway? Wrap the body of the article in a single container with lots of continuous text and don't chop it up into a thousand tiny divs full of links. An <article> or a <main> with long paragraphs wins. A tangle of <div> with navigation loses.

Headers and metadata that do count

Four small details that make a difference:

  • <title>: shown in EWW's header line. Always set it and make it descriptive.
  • <meta charset>: EWW uses it as an encoding fallback. Declare UTF-8.
  • <base>: respected, useful for resolving relative links.
  • HTTPS with a valid certificate: EWW colors the header-line title according to the TLS status. Serve your site over HTTPS.

So, what is it good for?

EWW is an excellent HTML document reader and a deliberately incomplete web browser. And once you accept that duality, it fits like a glove in a handful of scenarios:

  • Reading without leaving Emacs: documentation, blogs, articles, wikis, HTML man pages... with all your usual keys and with isearch.
  • Focused reading: the readable mode, with its word-density scoring, leaves the text and nothing else. No noise.
  • Low bandwidth and zero distractions: no ads, no JS, no pop-ups, no telemetry. Accessibility out of the box.
  • Feeds and mail with HTML: remember that SHR is what Gnus, elfeed and mu4e use underneath.
  • A single flow: search and open links from Emacs itself, with bookmarks, history, multiple buffers or tabs and sessions.
  • Scriptable: you parse the DOM from libxml-parse-html-region directly and automate whatever you want.

Its philosophy is the opposite of a modern browser. Instead of emulating a graphical rendering engine, it translates semantic HTML into Emacs text. Anything that is a "document" works very well and very fast. Anything that is a "web application" doesn't work at all. And that's the beauty of it: it won't pass the Acid3 test, but not out of deficiency, out of design.


Help me keep writing Every coffee gives me a push toward the next article. Sure, it's on me!

Send an email to comment+article-e0e00b4b@andros.dev to leave a comment. The subject will be ignored.

-1:-- EWW, the Emacs browser you underestimate (Post Andros Fenollosa)--L0--C0--2026-08-04T13:29:43.000Z

The Emacs Cat: Debugging in Emacs

Introduction

In my years of using Emacs, I’ve noticed a common pattern: developers who are highly proficient at editing source code in Emacs switch to a “normal IDE” (as they call it)—such as CLion or Visual Studio—when they need to debug their code. They usually explain this by saying that Emacs feels a bit weird for the task.

However, I’d much rather not leave Emacs if something can be done inside it.

In this post, I’ll try to show that debugging in Emacs is both simple and effective, especially after a few small adjustments to the default settings.

Debugging in Emacs revolves around two key components:

  1. GDB (GNU Debugger), the most popular and powerful debugger for C, C++, and other languages.
  2. GUD (Grand Unified Debugger), Emacs’ built-in interface since at least v19 that turns GDB into a well-integrated debugging environment inside Emacs, allowing you to debug your code without leaving the editor.

A Sample Debug Session

Below is an example of a typical debug session of mine.

In Emacs, I open a source code file (timepoint.c in this example) and run M-x gdb. GDB then prompts in the minibuffer for the path to the corresponding executable, as shown below.

Loading a program to debug. Click or tap to view the full-size picture.

After you enter the path to the executable—assuming it was built with the -g flag (i.e., compiled with debug information)—and press Enter , GDB prints some initial output and may ask whether it should automatically download missing debugging information from trusted online servers.

Downloading missing debugging information. Click or tap to view the full-size picture.

Because I don’t intend to debug system or third-party libraries, I simply press n to skip this step, which can occasionally freeze Emacs for a while.

The GUD dashboard appears. It usually consists of 6 windows.

  • Debugger, the GUD interaction buffer (or GDB shell);
  • C/*, the source code buffer;
  • Frames, the stack buffer, a list of stack frames of the program being debugged;
  • Locals, the local variables/registers buffer;
  • Inferior I/O, the input/output buffer (usually associated with stdin/stdout);
  • Breakpoints, the breakpoints/threads list.

GUD dashboard. Click or tap to view the full-size picture.

In my usual workflow, I type break main (or simply b main) in the GUD interaction buffer (Debugger) to set a breakpoint at the start of my program.

Setting initial breakpoint. Click or tap to view the full-size picture.

The new breakpoint shows up right away: as a red fringe marker next to the line in the source buffer (color varies by theme) and as an entry in the breakpoints list.

Now you can type run in the GUD interaction buffer. Execution will then stop at the first breakpoint (line 20 in this example).

GUD Keybindings

Frankly, I find the default GUD keybindings somewhat complex. For example, C-x C-a C-n is the globally defined key chord for gud-next — one of the most frequently used debugging commands. Having to type three keys while holding the Ctrl key feels excessive to me, so I prefer a single keystroke for this operation.

So, in my ~/.emacs, I’ve rebound the most frequently used GUD commands to F6 and its derivatives.

(global-set-key [(f6)] #'gud-next) ;; Execute the next single line.
(global-set-key [(control f6)] #'gud-step) ;; Enter the called function.
(global-set-key [(shift f6)] #'gud-cont) ;; Continue execution until hitting a breakpoint.
(global-set-key [(control shift f6)] #'gud-print) ;; Evaluate the expression at point.

I’ve kept the default keybindings for the less frequently used commands, such as setting a breakpoint (gud-break, C-x C-a C-b), continuing execution to the current line (gud-until, C-x C-a C-u), and so on.

Additionally, I’ve added a new keybinding for gud-watch (C-x C-a C-w), which watches the expression at point. This command has no default keybinding in GUD, at least in Emacs 29.3.

(global-set-key (kbd "C-x C-a C-w") #'gud-watch) ;; Watch the expression at point.

Debugging

Let’s continue with our sample debugging session.

We stopped at line 20, the first breakpoint in my program. Now I want to step through the function print_timepoint line by line. In the source code buffer, I move the cursor to line 9 and set a new breakpoint with C-x C-a C-b (gud-break). Then I continue execution with Shift + F6 (gud-cont) until the new breakpoint is hit.

After stepping through the function line by line with F6 (gud-next) until the end (line 16), I get the following output:

Debugging a function. Click or tap to view the full-size picture.

In the Locals buffer, you can see all local variables and their current values. The Inferior I/O buffer shows the program output. Everything looks correct: the tpoint variable has the value 0, which corresponds to the beginning of the Unix epoch (January 1, 1970).

Indeed, we could have inspected the print_timepoint function by simply stepping into it (gud-step, Ctrl + F6) directly from the first line of main, where the initial breakpoint was set. I used this example to demonstrate more GUD commands in action.

Now we can quit the debugging session by typing quit (or simply q) in the GUD interaction buffer (Debugger).

Of course, there’s no way to cover all the functions and commands available in GDB/Emacs GUD in one short post but I hope it provides a helpful introduction to using Emacs GUD for debugging your code.

You can find more information in the Running Debuggers Under Emacs manual; the GDB Cheat Sheet (PDF) is also worth having on hand.

Happy debugging in Emacs!

— The Emacs Cat.

-1:-- Debugging in Emacs (Post The Emacs Cat)--L0--C0--2026-08-04T08:47:40.000Z

Charles Choi: Announcing now-playing.el, an Emacs interface for the macOS Music app

For me, listening to music whenever I’m working on a computer is a commonplace, daresay essential activity. Much of my work is done using Emacs, so the idea of having it control music playback to mitigate an app switch seemed reasonable. Such was the motivation for my latest project, now-playing.el, a Transient interface for the macOS Music (nee iTunes) app.

img

now-playing.el takes inspiration from the many mini-client interfaces provided by Apple to control the Music app. The idea was to have a compact and simple interface:

  • Show what is currently playing (song, artist, album)
  • Offer basic controls (play/pause, next/previous track, volume control)
  • Optionally log what was played.

now-playing.el takes advantage of AppleScript support in the Music app, providing the needed controllability and observability of it from Emacs. The sequence diagram below illustrates how these programs relate to each other by showing a request/response transaction between Emacs and the Music app using the osascript command line tool.

img

If Emacs is built with NextStep (NS) support, then Emacs can send a direct AppleScript message to the Music app via ns-do-applescript to avoid making a shell call to osascript.

img

State synchronization for now-playing.el is fairly simple, with client updates done in a pull fashion. More details about what now-playing.el can do can be found in the now-playing.el User Guide.

Closing Thoughts

It is not coincidental that writing this mini-client for the Music app works into my recent explorations and musings on malleable computing with Emacs. As of this writing, now-playing.el weighs in at 326 lines of code (cloc measured), taking full advantage of Elisp libraries to construct the following client subsystems:

  • UI: Transient for menu, cus-edit.el for customization UI
  • Client Edge: shell-command-to-string to osascript, ns-do-applescript
  • Local Database: Elisp variables

now-playing.el explores integration with AppleScript, itself a mechanism to allow for orchestration between Cocoa applications. Expect some future posts documenting my explorations in this area, particularly with the more contemporary Apple Shortcuts and Intents.

now-playing.el v1.0 is now available on MELPA.

-1:-- Announcing now-playing.el, an Emacs interface for the macOS Music app (Post Charles Choi)--L0--C0--2026-08-03T17:55:00.000Z

Irreal: The Em Dash And Emacs

Irreal, as you all know, is an em-dash friendly site. Irreal has always liked them and now we like them even more because it puts a thumb in the eye of those mentally challenged folks who claim, erroneously, that “real people” don’t use em-dashes and their presence in a document is a sure sign of AI generated text.

It turns out that JTR over at The Art Of Not Asking Why is also an em-dash aficionado and has been struggling with how to enter them, especially when using Emacs. His final solution involves expand-abbrev but seems to me overly complicated. He types --- then immediately calls expand-abbrev by typing Ctrl+x `.

We here at Irreal have the same problem, of course, but solved it in what seems to me a much simpler way:

(defun jcs-insert-em-dash ()
  "Insert an em-dash at point"
  (interactive)
  (insert 8212))

and

(global-set-key (kbd "H--") #'jcs-insert-em-dash)

I have Hyper bound to the right ⌘ Cmd key so it’s really easy to invoke it: thumb on Hyper and index finger on - and it inserts an em-dash directly into the source exactly as JTR wanted.

I don’t claim my solution is better than JTR’s, only that it suits me better. It’s just another example of Emacs letting you have it your way.

-1:-- The Em Dash And Emacs (Post Irreal)--L0--C0--2026-08-03T14:37:58.000Z

Still working on Emacs Config Gems part 3. Fell into a deep rabbit hole and got lost for a couple of days, then climbed out just to discover another one I have to explore next. I wanted to crunch the first few tweaks of my Emacs config in one post, but there’s too much to explore. I’m getting there!

-1:--  (Post TAONAW - Emacs and Org Mode)--L0--C0--2026-08-03T12:21:03.000Z

Sacha Chua: 2026-08-03 Emacs news

: Updated pkal's link to Structured Changelogs for ELPA packages (request for feedback)

I particularly enjoyed the tweaks for EWW (Emacs Web Wowser) in this edition. Could be fun to take whatever page you're looking at in a different application and open it within Emacs, with syntax highlighting and maybe even the ability to copy or execute bits of code more easily. Could be handy during your search for knowledge, which is incidentally the Emacs Carnival theme for August. Enjoy!

Links from reddit.com/r/emacs, r/orgmode, r/spacemacs, Mastodon #emacs, Bluesky #emacs, Hacker News, lobste.rs, programming.dev, lemmy.world, lemmy.ml, planet.emacslife.com, YouTube, the Emacs NEWS file, Emacs Calendar, and emacs-devel. Thanks to Andrés Ramírez for emacs-devel links. Do you have an Emacs-related link or announcement? Please e-mail me at sacha@sachachua.com. Thank you!

View Org source for this post

You can e-mail me at sacha@sachachua.com.

-1:-- 2026-08-03 Emacs news (Post Sacha Chua)--L0--C0--2026-08-03T10:57:58.000Z

TAONAW - Emacs and Org Mode: Em Dashes in org-mode and the Nuances of Abbrev

Since I confessed my enjoyment of em dash in my writing last week, I started using them more freely. On a Mac, it’s Option Shift Hyphen, as Sasha would tell you. In Linux, it’s a bit more involved: Ctrl Shift U to bring up a search for Unicode number reference, and then the number for em dash: 2014.

This is all good and well, but what about Emacs, where I write most of my text?

Emacs comes with abbrev-mode, a powerful text-expanding option that automatically converts something like “om” to “org-mode”, or even “fox” to “The quick brown fox jumps over the lazy dog” if I wanted to. But the em dash is a bit more tricky. I could, if I wanted, create an abbreviation like “em” to simply expand to “—”. But this doesn’t make much sense.

When converting org-mode to markdown (as I do when I post something), the built-in solution for Emacs’ markdown exporter is to convert --- (three dashes) to , em dash — but this presents two annoyances. First, the built-in exporter would convert those to their character reference, as we just saw: &#2014;. I explained this more in-depth previously. Second, even after I fixed this issue by using pandoc, I still see --- in org-mode, not —. This looks like the kind of thing abbrev-mode was made for, right?

But it’s a bit more complicated. abbrev-mode triggers only when you type a space after a letter. So, “em” to — would work, but - to — would not, because a dash is not part of a word1.

The solution was simpler than I thought: expand-abbrev, which “Expands the abbrev before point, if there is an abbrev there. Effective when explicitly called even when ‘abbrev-mode’ is nil.” In other words, it calls expand-abbrev without needing to trigger it the usual way. We simply call the abbrev replacement directly.

Now whenever I want an em dash, I type --- first, and then I make sure the marker is right after the last dash, and call expand-abbrev, which in my config is tied to C-x ‘. I then get an em dash in Emacs, and the exporter in Emacs (or pandoc, in my case) leaves it alone.


  1. Prot has a related, interesting video about expanding abbrevs with non-word-like characters, which I already use. The problem here is that there’s nothing “word-like” in —. We can work with something like “!word” or “word!” because the mechanism that catches abbrevs will recognize a word-like character before or after the “!” (using regex, as explained in the post) and trigger it, but in the case we have above, there’s nothing that looks like a word to trigger it at all, so it does not work. ↩︎

-1:-- Em Dashes in org-mode and the Nuances of Abbrev (Post TAONAW - Emacs and Org Mode)--L0--C0--2026-08-02T14:07:04.000Z

Raymond Zeitler: One Space or Two?

HTML enforces the use of a single space to separate sentences. If you want to add another space, you need to use &nbsp;. I still remember how annoyed I was about that. Back in the century in which I took my technical writing course, two spaces were required. Of course, we weren't using computers very often; hand-written sentences needed to appear more separated than the words within.

I'm definitely not here to convince anyone to switch. Instead, I just want to share that I paid close attention to this detail when I was formulating my style guide for this blog. Should I continue with my out-of-date two-space habit? Or should I adopt the current convention?

I turned to Emacs itself for guidance. It turns out that the individuals who wrote Emacs comments, docstrings and help text used two spaces. Although many times they started each sentence on a new line.

It turns out that the content here mostly is one-space. I start out using two spaces in Org where most of this originates. But when I export to HTML, that second space vaporizes. That would seem like a bug to me.

The only place where you'll see evidence of my two-space style is in the comments and docstrings of my source code.

Am I the only person who nit picks about such details?


Thanks to Irreal for highlighting a customization from macosguru

-1:-- One Space or Two? (Post Raymond Zeitler)--L0--C0--2026-08-01T23:47:27.512Z

Marcin Borkowski: A fancier splash screen in terminal Emacs

I’ve been using Emacs in a terminal for some time now (an experience worth a blog post another day). One thing that bothered me a little was that the “splash screen” (what Emacs displays at the start) does not show the familiar Emacs logo. I decided to fix this.
-1:-- A fancier splash screen in terminal Emacs (Post Marcin Borkowski)--L0--C0--2026-08-01T05:13:48.000Z

Protesilaos: Emacs live with @linkarzu on his workflow, Org capture, and projects (2026-08-01 22:20 Europe/Athens)

Raw link: https://www.youtube.com/watch?v=WQbXKvBT8HY

In about 40 minutes from now I will do a live stream with Christian Arzu from the @linkarzu channel on YouTube. The video will be recorded.

The topic of our meeting revolves around how Christian uses NeoVim and Kitty to handle sessions and write fleeting thoughts. Then we will try to do something along those lines using built-in Emacs functionality.

Christian did a video a couple of days ago about this topic and here is my extensive commentary on it, with points that are useful in general: https://protesilaos.com/codelog/2026-07-31-re-linkarzu-what-i-need-from-emacs/.

-1:-- Emacs live with @linkarzu on his workflow, Org capture, and projects (2026-08-01 22:20 Europe/Athens) (Post Protesilaos)--L0--C0--2026-08-01T00:00:00.000Z

Ashish Panigrahi: Setting up offline email for Microsoft O365 with notmuch and emacs

Having to deal with Microsoft's outlook has been a growing pain with my computing needs. Of course, this is pertinent only for work email. For personal email, I use Migadu which has good standards for email unlike Microsoft's.

At work, I can only use either the official Outlook webmail or Thunderbird since those are the only clients that are whitelisted by the IT department at my university. Thunderbird is slightly better (it has a few problems of its own but I digress) but I would like to have an offline copy of all my emails, fully searchable with an indexed database provided by notmuch, which uses the excellent Xapian library for indexing. Notmuch is shipped as an emacs package in the form of notmuch.el, which is exactly what I like given that I'm moving most of my computing needs into emacs1.

I've taken inspiration from Fergus' excellent blogpost which implements the same in neomutt. The initial setup process is identical to Fergus' blogpost.

It should be noted that notmuch is only a mail indexer. It does not fetch mail, nor does it send mail. Those are handled by isync and msmtp, which are commandline programs.

Access authentication with O365

Since Thunderbird is still allowed to access O365's authentication, we will use the client ID provided from it (this is publicly available from Thunderbird's source code)2. For authentication for IMAP and SMTP, we will use XOAUTH, which is a simple authentication and security layer (SASL) more suited for email clients.

Tools like isync rely on SASL for authentication, for which we'll require an XOAUTH SASL plugin: https://github.com/moriyoshi/cyrus-sasl-xoauth2.

If you're on Arch, then this plugin is installable from the AUR:

paru -S cyrus-sasl-xoauth2-git

If not, then we can build it from source:

git clone https://github.com/moriyoshi/cyrus-sasl-xoauth2
cd cyrus-sasl-xoauth2/
./autogen.sh   # checks for requirements
./configure    # configure (here we use the defaults)
make           # compile
sudo make install     # install for root user

Token generation for OAuth

Microsoft has conditionally allowed API access to clients and the only way to get access for any client is to register the application in the Microsot Application portal, which is something only my org's IT department can do3. Fortunately, we can use Thunderbird's credentials to generate an access token for our purposes.

Another issue that comes up is the finite lifetime of these access tokens (typically an hour or two), which is cumbersome to generate each time one needs to download email. Fortunately, mutt comes with mutt_oauth2.py, a script to help in the automatic renewal of this token. This script helps in decrypting the token file and renewing it everytime it is invoked.

Fetching the initial token is fairly simple but before we do it, there are certain changes that we need to do to mutt_oauth2.py.

Look into the ENCRYPTION_PIPE variable (line 48) and make sure to specify your gpg key. If you don't have one, then generate one:

gpg --full-generate-key

Select the default options (ed25519 cypher if it allows) and make sure that the expiration of the key is set to 0 (no expiration). This makes your life easier but of course for security reasons, this is not a good practice.

Bring up your public key:

gpg --list-keys

Copy the second line (after the pub line) and paste it into mutt_oauth2.py

ENCRYPTION_PIPE = ['gpg', '--encrypt', '--recipient', 'YOUR-GPG-KEY']

After this, look at line 79. This includes a client_id for microsoft. For Thunderbird, the client id is 9e5f94bc-e8a4-4e73-b8be-63364c29d753. Leave the client_secret value as is. It should look like so:

'client_id': '9e5f94bc-e8a4-4e73-b8be-63364c29d753',
'client_secret': '',

Now we are ready to generate the access token. Simply run:

./mutt_oauth2.py OUTPUT_TOKEN_FILE --verbose --authorize

A series of prompts should appear. Select microsoft, localhostauthcode (or authcode), followed by your email address, after which you should automatically be redirected to a browser for adding your credentials for authentication. Doing so, should reveal the access token in your terminal. No need to copy it, it is present in your OUTPUT_TOKEN_FILE which is encrypted. To obtain the decrypted token, we run the script again with just the file name as an argument

./mutt_oauth2.py OUTPUT_TOKEN_FILE

A nice thing is that running this script with the token file also checks for expiry and renews it if necessary. This can be done multiple times without worry.

UPDATE (2026-06-19): About 20 days after I set this up, for whatever reason the token file expired on my personal laptop. I'm not entirely sure what happened but the fix for now was to regenerate the token again. I'll need to debug the exact cause to prevent it from happening again.

UPDATE (2026-07-29): It looks like this problem only appears on my personal laptop as it's not an authorized device and as such, 14 days is the maximum duration before which the token needs to be reauthorized. My office PC is within this "authorized" devices whitelist and thus doesn't need reauthentication after the initial setup.

Offline email setup

Now that we are done with the difficult part, we move onto setting up isync (the binary is called mbsync) which is available in most distributions. This program basically downloads your emails offline and synchronizes it with your email's remote IMAP servers. It supports multiple accounts too. The program is configurable with a ~/.mbsyncrc file4. I highly recommend reading the manual for it via man mbsync. Here's a starting template:

# -- Global defaults
# These will be applied to all accounts

# Create new mail in either location, so if the remote or local has mail
# and the other does not, then create it
Create Both
# never remote mail from either side
Remove None
# Remove messages marked for deletion from the local side on the
# remote. Do not ever delete local if marked for deletion in the
# remote. Prevents admins from deleting one's local mail
Expunge Far
Sync All
SyncState *
CopyArrivalDate yes
# No maximum number of messages
MaxMessages 0

# -- First email account
IMAPAccount university
Host outlook.office365.com
Port 993
User my-username@university.edu
AuthMechs XOAUTH2
PassCmd "~/.local/bin/mutt_oauth2.py ~/.local/bin/TOKEN"
# Use TLS
TLSType IMAPS
SystemCertificates yes
Timeout 10

# A store defines a collection of mailboxes, so we associate the
# remote IMAP mailbox with the account we just configured
IMAPStore university-remote
# Associate the store with our account
Account university

# Where are we going to keep the mail associated with this account
MaildirStore university-local
SubFolders Verbatim
Path ~/.local/share/mail/university/
Inbox ~/.local/share/mail/university/INBOX

# Channel to synchronize everything except "Sent" email
Channel uni-main
Far :university-remote:
Near :university-local:
# Which directories to synchronize
Patterns INBOX "Deleted Items" "Drafts"

# Channel to synchronize "Sent" email
Channel uni-sent
Far :university-remote:"Sent Items"
Near :university-local:Sent

# Group the two channels and sync everything with `mbsync NAME`
Group university
Channel uni-main
Channel uni-sent

# -- Second email account
IMAPAccount personal
...

Once you have everything configured, we can start downloading all our email by running:

mbsync university

The first time mbsync is run, it will take a fair amount of time, since you'll be downloading a lot of email (assuming you've been using your account for a long time). The subsequent runs would be just incremental downloads, so it's much faster.

After navigating to your local Maildir directory (~/.local/share/mail/university in my case), you should see all your emails downloaded and categorized into inbox, drafts, sent, and deleted-items folders.

A way to automate this to run a script periodically. I use systemd-timers where I have a script which is periodically run every 15 mins and notifies me if there's new email (I'll cover this later in the blogpost).

Sending mail via SMTP

Sending email is done via msmtp. It is configured by editing ~/.msmtprc5.

defaults
auth on
tls on
tls_trust_file system
logfile ~/.cache/msmtp.log
timeout 10

# -- First account
account university
tls_starttls on
host smtp.office365.com
port 587
auth xoauth2
user my-username@university.edu
passwordeval "~/.local/bin/mutt_oauth2.py ~/.local/bin/TOKEN"
from my-username@university.edu

# -- Second account
account personal

This is fairly self-explanatory. One thing to note is that tls_starttls should be on for this to work. I'm not very familiar with email security protocols like TLS and STARTTLS but from my understanding, STARTTLS is an automatic upgrade (if supported) from the more insecure TLS protocol. TLS however needs to be enabled for any email service, otherwise it won't work.

Now, we can test our configuration by sending email directly from the commandline. Save the contents of a file called example-mail as follows:

From: my-username@university.edu
To: user@example.com
Subject: Hello World

This is a test for msmtp.

And send the email by running:

cat example-mail | msmtp -t -a university

Indexing email offline with notmuch

We have receiving and sending email configured. How about reading and "using" email via an email client. In more technical terms, an email client is called a mail user agent (MUA), which is typically seen in email headers. This includes clients like Thunderbird, Apple mail, neomutt, etc. We will configure notmuch which indexes email and provides an interface for emacs natively with notmuch.el.

Notmuch is entirely tag based. Basic tags exist like unread, inbox, draft, sent but you can customize it with more tags to filter out emails from a particular email address, from a particular range of date, etc. Tags are heavily used and it's a different paradigm of handling email compared to your typical webmail interface. I won't go over the details of tagging in notmuch here for the sake of brevity (to be covered in another blogpost).

When first setting up notmuch, simply run:

notmuch setup

This will prompt you for basic information like your name, email address, Maildir directory, etc. Once this is done, notmuch needs to index your Maildir. Run the following:

notmuch new

This should be fairly fast, even if your Maildir is big. Notmuch is quick like that.

Now you can search an email entry via notmuch search. For example, I can search emails from a particular sender like so:

notmuch search 'from:friend@university.edu'

I would again advise you to go through the man pages (man notmuch-search) for more details.

Emacs setup for notmuch

Simply running M-x notmuch should give you a hello screen, where you can then select tags like inbox, unread to view their respective email. I have a fairly customized config for notmuch.el but below I provide a fairly good starting point for a more minimal notmuch interface6:

;; Notmuch for email
(use-package notmuch
  :load-path "/usr/share/emacs/site-lisp/"
  :ensure nil
  :defer t
  :commands (notmuch notmuch-mua-new-mail)
  :init
  ;; Search
  (setq notmuch-search-oldest-first nil)
  :config
  ; General UI
  (setq notmuch-show-logo nil
	notmuch-column-control 1.0
	notmuch-hello-auto-refresh t
	notmuch-hello-recent-searches-max 20
	notmuch-hello-thousands-separator ""
	notmuch-hello-sections '(notmuch-hello-insert-saved-searches)
	notmuch-show-all-tags-list t)

  ; Search
  (setq notmuch-search-result-format
        '(("date" . "%12s  ")
          ("count" . "%-7s  ")
          ("authors" . "%-20s  ")
          ("subject" . "%-80s  ")
          ("tags" . "(%s)")))
  (setq notmuch-tree-result-format
        '(("date" . "%12s  ")
          ("authors" . "%-20s  ")
          ((("tree" . "%s")
            ("subject" . "%s"))
           . " %-80s  ")
          ("tags" . "(%s)")))
  (setq notmuch-show-empty-saved-searches t)

  ; Tags
  (setq notmuch-archive-tags nil ; I don't archive email
	notmuch-message-replied-tags '("+replied")
	notmuch-message-forwarded-tags '("+forwarded")
	notmuch-show-mark-read-tags '("-unread")
	notmuch-draft-tags '("+draft")
	notmuch-draft-folder "university/Drafts"
	notmuch-draft-save-plaintext 'ask)

  ; Email composition
  (setq notmuch-mua-compose-in 'new-window)
  (setq notmuch-mua-hidden-headers nil)
  (setq notmuch-address-command 'internal)
  (setq notmuch-address-use-company nil)
  (setq notmuch-always-prompt-for-sender t)
  (setq notmuch-mua-cite-function
	'message-cite-original-without-signature)
  (setq notmuch-mua-user-agent-function nil)

  (setq notmuch-show-relative-dates t)
  (setq notmuch-show-all-multipart/alternative-parts nil)
  (setq notmuch-show-indent-messages-width 0)
  (setq notmuch-show-indent-multipart nil)
  (setq notmuch-show-part-button-default-action 'notmuch-show-view-part)
  (setq notmuch-wash-wrap-lines-length 120)
  (setq notmuch-unthreaded-show-out nil)
  (setq notmuch-message-headers '("To" "Cc" "Subject" "Date"))
  (setq notmuch-message-headers-visible t)

  :bind
  ( :map global-map
    ("C-c m m" . notmuch)
    ("C-x m" . notmuch-mua-new-mail) ; override `compose-mail'
    :map notmuch-search-mode-map
    ("/" . notmuch-search-filter) ; alias for l
    ("r" . notmuch-search-reply-to-thread) ; easier to reply to all by default
    ("R" . notmuch-search-reply-to-thread-sender)
    :map notmuch-show-mode-map
    ("r" . notmuch-show-reply) ; easier to reply to all by default
    ("R" . notmuch-show-reply-sender)
    :map notmuch-hello-mode-map
    ("J" . notmuch-jump-search)))

You're free to look at my init.el for my own customization for notmuch.el.

Automating email synchronization with systemd timers

Let's automate the email sync to happen every 15 mins (or whatever frequency you prefer). First we create a shell-script to download email via mbsync and also index the email with notmuch. Let's name it it syncmail.sh and place it in $HOME/.local/bin/.

#!/bin/sh
set -eu

# Set environment variable for notmuch config file
export NOTMUCH_CONFIG="$HOME/.config/notmuch/notmuch-config"

mbsync university

before=$(notmuch count --lastmod '*' | cut -f3)
notmuch new > /dev/null

query="lastmod:$((before + 1)).. and path:university/**"
count=$(notmuch count "$query")

if [ "$count" -gt 0 ]; then
    body=$(notmuch search --format=json --limit=5 "$query" \
	       | jq -r '.[] | "\(.authors): \(.subject)"')
    notify-send "New mail: $count" "$body"
fi

In addition to downloading email and indexing, the script also notifies through notify-send if there's new email along with a sender and subject in the notification. Make sure to make the script executable with

chmod +x ./syncmail.sh

Then we write the systemd script to run this script periodically. First we create a syncmail.service file in $HOME/.config/systemd/user/:

[Unit]
Description=Sync mail with mbsync and index with notmuch

[Service]
Type=oneshot
ExecStart=%h/.local/bin/syncmail.sh

Then we create a timer file (make sure it is named syncmail.timer) for actually running the script every n minutes.

[Unit]
Description=Run syncmail every 15 minutes

[Timer]
OnBootSec=2m
OnUnitActiveSec=15m
Persistent=true

[Install]
WantedBy=timers.target

The advantage over cronjobs is that if your system is asleep or shutdown, then booting up accomodates accordingly and runs the script after m minutes (2 minutes in my case).

Finally we enable the script with systemctl:

systemctl --user daemon-reload
systemctl --user enable --now syncmail.timer

Concluding remarks

Now you should have a working setup for using offline email for Microsoft's O365. This is fairly minimal but if you'd like to have encrypted emails, addressbooks, etc., please take a look at the original blogpost from Fergus.

A basic emailing etiquette I like to follow is to use plaintext as opposed to html. Read more on why this is better.

Special thanks to Marci for pointing out grammatical errors and typos.
  1. It's like the old adage from Vi advocates, "Emacs is a great operating system, lacking only a decent text editor". Although the latter part I would disagree with.

  2. I just found out that Mozilla has made it very convoluted to find the source code and build Thunderbird locally. It uses mercurial instead of Git (in this day and age?). Relevant docs are here.

  3. Even after asking countless times, they've rejected my request to include notmuch/emacs into the allowed clients list. Ah well, here we are.

  4. Personally, I dislike that my $HOME directory gets cluttered with config files that don't respect the XDG specifications. I set it to $HOME/.config/mbsync/mbsyncrc by exporting the variable MBSYNCRC.

  5. Same issue with this. I fix it to be at $HOME/.config/msmtp/config.

  6. The default interface is too noisy for me, so I disable a lot of the bells and whistles.

-1:-- Setting up offline email for Microsoft O365 with notmuch and emacs (Post Ashish Panigrahi)--L0--C0--2026-08-01T00:00:00.000Z

Protesilaos: Reply to @linkarzu about ‘What I Need From Emacs’

Christian Arzu from the @linkarzu channel on YouTube has just published a ~16-minute video with the title What I Need From Emacs: A Video for Prot.

I am happy he has done this and am eager to discuss those concepts in further depth. In this article I will comment on the specific points Christian raises as well as on the overarching theme of his experiment with Emacs.

Emacs has “sessions” out-of-the-box

Christian has experienced friction with using Emacs. Much of it revolves around the notion of “sessions” that Kitty provides. At least for the way Christian uses sessions, Emacs has the equivalent concept of “projects”. Each project is a version controlled directory (Git in this case, though Emacs supports many others as well).

To tell Emacs where your Git repositories are, so that it can record all of them as known projects, do M-x project-remember-projects-under and select the directory at the follow-up prompt.

To switch between projects, use M-x project-switch-project. At this prompt, you use search to narrow the results and pick one among your existing projects. There is also an option to open a specific directory that is not yet a known project, which will thenceforth be remembered as a project if it is under version control (there are other options on how projects are identified, but the presence of version control system is the default check).

Projects also become known if you invoke one of the project-specific commands from inside the given Git repository. For example, if you call the command project-find-file, it will do what it is supposed to do (more below) in the current repository and store that repository as one your projects for future use.

Quickly find a file anywhere in the project

The find-file command (C-x C-f by default) is about manually navigating to a path. Christian finds this cumbersome and indeed it is if you are thinking in terms of selecting a file related to the current project.

The command project-find-file consolidates all the files into a flat list from where you can type a few characters to narrow the results and pick the file you need.

Similarly, project-find-dir lets you pick any directory in the current project, which is especially useful if you need to do something with many files (e.g. rename a bunch of files or change their permissions).

This principle extends to all other relevant commands. For example, project-shell opens a shell buffer at the root of the current project. If you are using something like the ghostel package (https://github.com/dakra/ghostel), it provides the ghostel-project command to open a fully fledged terminal emulator (i.e. not just a “shell”) in this project’s root directory.

There are many other nice things that can be done on a per-project basis, such as to run the relevant compile command, switch to the project’s buffers, perform a project-wide grep, and more.

And, generally, many packages will build on top of projects, such as the popular consult package (https://github.com/minad/consult) with some of its commands like consult-find and consult-grep (or consult-ripgrep).

The built-in projects can be combined with other features

These Emacs project capabilities can easily be combined with other features that Emacs also supports out-of-the-box.

For example, you can set things up so that when you switch to a new project it appears on another tab (displayed at the top of the Emacs frame) or, indeed, to switch to another frame, within which there can be tabs.

To augment projects, you can combine them with mechanisms that restore buffers and/or window/tab/frame arrangements after you restart Emacs. For example, the bufferlo package (https://github.com/florommel/bufferlo) covers this space.

Piecing together the system

Depending on one’s needs, there is a lot that can be done to not only recreate those sessions that Christian mentions but to do arbitrary things on top, with the help of packages as well as custom Elisp (and writing your own Elisp is very much in the spirit of extending Emacs to do exactly what makes sense to you).

Though here we get into the area of workflow and the overall approach. I think it is safer to try things one at a time from the ground up, so that you can understand how the emergent system is pieced together. This allows you to better reason about what you have and what you need. Plus, it empowers you to troubleshoot things because you already have a sense of what is there.

For example, you start with the projects as they are done in the generic Emacs. When you find something that does not quite work, you read the documentation to see if there is some user option to configure things to your liking. Then you can search for packages that augment the experience, based on your specific requirements and expectations.

Remember that Kitty sessions build on top of existing experience

Taking a step back, consider that what Kitty provides is not something you get up-and-running with as a new user of terminal emulators.

When you are a beginner with terminal emulators, you barely understand how things work and how you are supposed to interact with the tool. Over time you learn all the nice things about the shell and eventually you incorporate custom scripts and extensions that augment your experience (like fuzzy finding, multiplexing, storing/restoring configurations).

To remind yourself how austere the default experience is, launch an unmodified instance of XTerm. It will bring to the foreground the fact that what Christian is doing with his Kitty setup is many levels above what a standard terminal-based workflow looks like.

And the key here is that each level corresponds to a corpus of knowledge that took form through experience. In other words, it was done over time.

The same is true for Emacs and everything else: you cannot speedrun experience.

Capturing thoughts very quickly

Christian demonstrates how he quickly creates a daily note to record a reminder or fleeting thought. The equivalent built-in facility in Emacs is org-capture: it can do a lot of things, but to cover specifically what Christian wants, there is an org-capture-templates configuration that records an entry under a specific tree of date headings. Because there can be many such templates, the user can define any variations they need to match their specific requirements.

If the user prefers that those fleeting thoughts are always captured in standalone files, one per day, then the popular org-roam package can do that through what it calls “dailies” (one note per day). There are other ways to do the same thing, including with core Org, but org-roam streamlines this method.

org-roam also covers another need Christian has, which is about linking files together and finding the backlinks to them (again, there are many ways to achieve this, but covering all the options is not the topic here).

You do not necessarily need Emacs

What I say to others and will write for Christian as well is always have a good idea “why” you want something before committing to it. This is how you find the motivation to stick to a project despite the eventual setbacks. Without the “why” it is practically guaranteed that you will quit early.

NeoVim, Kitty, and friends can combine to provide practically all the features that Emacs has. Users may then argue over the relative pros and cons of each approach, which I personally consider a distraction because what matters is to have a computing environment that gets the job done. The gist, however, is that if NeoVim+extras works for you, then you do not really gain anything from switching to Emacs: it is a lateral step at best.

More importantly, if your current setup covers all your needs, you do not have a compelling reason and thus the drive to persistently try Emacs. It is the same for me: Emacs covers my needs, so I do not feel any enthusiasm—or, indeed, curiosity—to spend time with other options. Maybe those other options are excellent and maybe they can be configured to be superior to my Emacs setup. But what I have fits my workflow, so I do not have the emotional push to look elsewhere.

Without the “why”, it will be practically impossible to stick with any experiment: it will always make sense to revert to the state you are familiar with.

Careful with shortcuts in life

Allow me to elaborate on my claim that you cannot speedrun experience. I will use an example from football (soccer), as I played it at a high level and it may be a more relatable story anyway.

You watch the game and are excited to walk on the pitch right away to score some goals. It looks cool and feels amazing! However, you have not practised the fundamentals, like controlling the ball while running and having situational awareness. You believe you can figure those out while playing the actual match. As such, you overestimate your abilities and set yourself up for failure.

Then, things get real all of a sudden. Within the first minute of the match, you come up against someone like me, who makes no discounts and takes no nonsense. I will not let you ever get near that ball: I will be first on the challenge because I know how to position myself and, even if I am not first, I will regain possession of the ball with ease because I have done it a zillion times.

You will never get anywhere near scoring a goal and you will likely not get the chance to be involved in the flow of even smaller patterns of play like an exchange of passes. Whenever you are close to touching the ball, I will be there breathing down your neck, hounding you everywhere you go. Such is the gulf in experience that I will make your life miserable on that pitch.

In short, your initial enthusiasm will quickly turn into frustration because there will be no gameplay whatsoever for you. And the worst part is that it will only get harder from there, as I will also be competitive on top of being annoyingly better, with tough challenges, crunching tackles, constant shittalking to add insult to injury, and the overall aggression that shows a clear willingness to escalate further.

This is not about me: I am using myself as a proxy for the experienced player who grants you the honour of a true challenge. I am doing it to make it feel real and thus to emphasise the frustrating parts because this is exactly how life works when you are not prepared.

The morale of this story is that working on the fundamentals guarantees a better long-term experience and, above all, is a process you should not skip.

-1:-- Reply to @linkarzu about ‘What I Need From Emacs’ (Post Protesilaos)--L0--C0--2026-07-31T00:00:00.000Z

Protesilaos: Emacs: tmr version 1.4.0

TMR provides facilities for setting timers using a convenient notation. Lots of commands are available to operate on timers, while there also exists a tabulated view to display all timers in a nice grid.

Below are the release notes.


Version 1.4.0 on 2026-07-31

TMR is an efficient package that has been stable for years. This new version introduces some nice new features as well as several smaller internal refinements.

Create a timer that repeats

The command tmr-repeat prompts for a duration, like the standard tmr command but also asks for a number of times to repeat the given timer. When called with a prefix argument (C-u with default keybindings), tmr-repeat will also ask for a description for the new timer and whether it should be acknowledged or not after all repetitions have elapsed.

Thanks to Óscar Fuentes for contributing the original implementation of tmr-repeat (with many extra changes/features by me). This was done in pull request 16: https://github.com/protesilaos/tmr/pull/16. Óscar has assigned copyright to the Free Software Foundation.

Modify the repeat count of an existing timer

The command tmr-edit-repeat-count prompts for an existing timer and updates its repeat count. If the repeat count is set to 0, then the timer is no longer considered repeatable.

As with all commands that operate on existing timers, tmr-edit-repeat-count automatically applies to the timer at point when called from inside the tabulated list of timers (per tmr-tabulated-view).

Modifying a timer’s repeat count does not start a new timer. It simply updates the relevant data. Use the command tmr-clone to start a new timer using the data of an existing timer.

The tabulated view has more columns and all of them can be adjusted

The tabulated list of timers, which is typically produced by the command tmr-tabulated-view, now has extra columns for the data that pertains to repeatable timers.

Users who want to control which columns are displayed, and in what order, can modify the user option tmr-tabulated-columns. Its documentation covers the technicalities. The default value is to display all columns.

Completion metadata for the prompt that selects a timer

Advanced users can now modify all the completion-metadata associated with the prompt that reads/selects one among the existing timer objects (the function is tmr-read-timer). The relevant variable is tmr-completion-metadata. I am not providing this as a user option, because regular users will anyway not be able to do much with it as it involves custom functions.

Timers in the completion prompt are sorted by end time

The prompt for selecting a timer now sorts candidates by their end time. The closest end time appears at the top.

This affects all commands that operate on a timer, such as tmr-edit-repeat-count, tmr-cancel, and more.

In the past, there was no sorting whatsoever. Users who prefer that style can add the following to their configuration:

(setq tmr-completion-metadata
      (list
       (cons 'category 'tmr-timer)
       (cons 'display-sort-function #'identity) ; THIS IS THE RELEVANT PART
       (cons 'annotation-function #'tmr-completion-annotate)))

Miscellaneous

  • The manual is easier to read. I have rewritten it to group commands and user options under appropriate headings.

  • Lots of internal refinements to the code. I tweaks things to make the code easier to maintain and contribute to.

  • Thanks to Pavlo Lysov for adding a small example to the manual about using TMR hooks to send a custom notification on the macOS. This was done in pull request 25: https://github.com/protesilaos/tmr/pull/25. Pavlo does not need to assign copyright to the Free Software Foundation.

  • Thanks to Carlos Pajuelo Rojo for refining an earlier version of the function that produces a message about the repeat count of a given timer (I have since made lots of changes on this and other fronts). Carlos’ contribution is in pull request 20: https://github.com/protesilaos/tmr/pull/20. The change is small, meaning that Carlos does not need to assign copyright to the Free Software Foundation.

  • Thanks to f6p for use the appropriate face for the paused indicator in the tabulated view. This was done in pull request 17: https://github.com/protesilaos/tmr/pull/17. The change is small, meaning that the author does not need to assign copyright to the Free Software Foundation.

-1:-- Emacs: tmr version 1.4.0 (Post Protesilaos)--L0--C0--2026-07-31T00:00:00.000Z

Rahul Juliato: The Completions Buffer Is Now Enough

My last post was a tour of Emacs 31, which has since moved into pretest. Sean Whitton announced 31.0.91, the second pretest, on July 23rd, and that's what I'm writing this on. One of the sections in that post was a short list of new completion knobs, three lines of config I mentioned and moved on from. Those three lines turned into a month-long experiment, so they get their own post.

I know how that title reads, so let me put it in context before you close the tab. It comes from someone who ran company, moved to Corfu, and then left Corfu behind for icomplete, which has been my weapon of choice for years now, for both the minibuffer and in-buffer completion.

And I didn't just use icomplete. I sent patches to it. bug#75784, which brought vertical in-buffer behavior and prefix indicators to icomplete, is mine, and I wrote about the road to it here and here. So when I say I turned icomplete off for a month, understand that I was arguing with my own past self, and that the title isn't me saying the last few years were wasted. It's me saying the floor moved.

What made me try was Emacs 31 itself. The *Completions* buffer improved nicely while I wasn't looking, and I wanted to know whether the UI I'd been layering on top of it was still earning its place.

While I was drafting this, Protesilaos published a video on the same territory. I haven't watched it all the way through yet, and I'm publishing anyway, because what follows is my month with this setup rather than a summary of his. His videos have shaped a lot of how I think about Emacs, so go watch it too. I'll probably end up fixing some of my own setup after I do.

Everything below runs on stock Emacs 31. Save the snippets into a test.el and launch with:

emacs -Q --load test.el

The full playground file (search for test.el) is at the bottom of the post if you'd rather skip ahead.

Two completions, one buffer

Emacs completes in two places, and it's worth keeping them apart in your head:

✔️ Minibuffer completion. M-x, C-x C-f, C-x b, every completing-read prompt in every package you have. You type in the minibuffer, candidates come from a completion table.

✔️ In-buffer completion. completion-at-point, the thing bound to M-TAB, and to TAB if you set tab-always-indent to complete. You type in a normal buffer and the candidates come from completion-at-point-functions: elisp symbols, file names, whatever your LSP server offers through Eglot.

Corfu and company handle the second case. Vertico, ivy and friends handle the first. You probably run one of each. Emacs covers both with the same *Completions* buffer, which surprised me, and it means one block of config does the whole job.

The base config

This is what I run:

(defun my/minibuffer-truncate-lines ()
  "Keep minibuffer lines unwrapped."
  (setq truncate-lines t))

(use-package minibuffer
  :ensure nil
  :bind ( :map minibuffer-visible-completions-up-down-map
		  ("C-n" . minibuffer-next-completion)
		  ("C-p" . minibuffer-previous-completion))
  :hook ((minibuffer-setup . cursor-intangible-mode)
		 (minibuffer-setup . my/minibuffer-truncate-lines))
  :custom
  (tab-always-indent 'complete)
  (completion-auto-help t)
  (completion-auto-select t)
  (completion-eager-update t)
  (completion-eager-display t)
  (minibuffer-visible-completions 'up-down)
  (completion-ignore-case t)
  (completion-show-help nil)
  (completion-styles '(partial-completion flex initials))
  (completions-format 'one-column)
  (completions-max-height 10)
  (completions-sort 'historical)
  (enable-recursive-minibuffers t)
  (read-buffer-completion-ignore-case t)
  (read-file-name-completion-ignore-case t)
  (minibuffer-prompt-properties
   '(read-only t intangible t cursor-intangible t face minibuffer-prompt))
  (minibuffer-depth-indicate-mode t)
  (minibuffer-electric-default-mode t))

Two of those carry the post, and both are new in Emacs 31:

:custom
;; ...
(completion-eager-update t)
(completion-eager-display t)
;; ...

completion-eager-display decides whether *Completions* shows up on its own. completion-eager-update decides whether it refreshes as you type. Both default to 'auto in Emacs 31, which means "only if the completion table asks for it," and almost nothing asks. Set both to t and you stop summoning the buffer with TAB. It's already open.

Run emacs -Q, hit M-x, type wind, and you get nothing but your own text in the minibuffer. Load the config above, do the same, and 34 candidates appear the moment you stop typing. Keep typing move-del and the list narrows to 5 without you touching a key that isn't a letter.

completions-format 'one-column and completions-max-height 10 are what turn that list into a vertical one. The default is 'horizontal, which packs candidates across the window like ls output. One column, capped at ten lines, reads as a dropdown.

completions-sort 'historical sorts alphabetically and then floats whatever you picked recently to the top. It's the smallest of these settings and the one I'd miss first.

That exact moment, below: four letters typed into M-x, no TAB pressed, 34 candidates already sitting there in one column.

completions demo 01

completion-auto-select t moves point into the *Completions* window when TAB pops it up, so you can navigate the list without a second key to get there. completion-show-help nil drops the two-line "Click or type RET on a completion to select it" banner from the top of the buffer, which I've read enough times for one lifetime.

enable-recursive-minibuffers plus minibuffer-depth-indicate-mode belong together: the first lets you start a minibuffer command from inside another one, the second puts a [2] in the prompt so you know how deep you are. minibuffer-electric-default-mode hides the (default foo) part of a prompt as soon as you type, and brings it back if you erase.

The minibuffer-prompt-properties line keeps point out of the read-only prompt text, and cursor-intangible-mode on minibuffer-setup-hook is what enforces it. truncate-lines in the minibuffer keeps long candidates on one line instead of ballooning the minibuffer to N rows.

One of those settings needs a keybinding to go with it. With minibuffer-visible-completions set to 'up-down, the arrow keys move point inside *Completions* while you're still typing in the minibuffer, and RET takes the highlighted candidate instead of your literal input. Left and right still move in the minibuffer, which is why I use 'up-down rather than t.

By the way, I tend to avoid arrow keys, M-<some-arrow-direction> doesn't feel natural in a completion list for me. That's why I set C-n and C-p like this:

:bind (:map minibuffer-visible-completions-up-down-map
	   ("C-n" . minibuffer-next-completion)
	   ("C-p" . minibuffer-previous-completion))

Below, the same list after a couple of C-n. The highlight has walked down to windmove-delete-default-keybindings while the cursor is still in the minibuffer, where I'm still typing.

completions demo 02

The same buffer, in your code

This setting is the one that made the experiment worth running:

:custom
;; ...
(tab-always-indent 'complete)
;; ...

TAB in a code buffer now indents the line if it needs indenting, then completes if it doesn't. Open *scratch*, type (window-lay, hit TAB. Emacs fills in the common prefix, window-layout-. Hit TAB again and *Completions* opens with the five commands that match, with point already inside it thanks to completion-auto-select. C-n to the one you want, RET, done.

The same keys and config as the minibuffer half, in a code buffer now.

After the second TAB, below: the buffer holds (window-layout-, the five commands sit underneath, and the first is already selected.

completions demo 03

*Completions* is a real window, so it takes room in your frame instead of floating over it. After a month I've stopped noticing, and I got something back for it: the candidate list is an ordinary buffer, so I can search it, scroll it, and yank out of it.

Where LSP completion goes wrong

One place did annoy me enough to write code.

My completion styles are:

:custom
;; ...
(completion-styles '(partial-completion flex initials))
;; ...

flex is the fuzzy one: it matches an in-order subset of what you typed, so faL finds faLinkedin and faList without you spelling out the middle. Exactly what I want from an LSP buffer, where I half-remember a name and want to see the family.

But flex does two jobs, and only one of them is filtering. Its try-completion also merges the surviving candidates and inserts the result. With tab-always-indent set to complete, that merge happens on TAB, before you ever see the list.

The case that cost me an afternoon comes from this blog's own source. components/footer.tsx imports a handful of FontAwesome icons. I wanted faLinkedin, so I typed faL and pressed TAB once:

;; I typed:            faL
;; TAB left me with:   faEnvelopeSquare

And no list is shown, faEnvelopeSquare matches faL under flex because the l sits inside "Envelope", and that's where the merge landed. TAB typed sixteen characters I didn't ask for, hid the candidates I did want, and left me deleting.

The frame below shows the whole thing. The echo area tells me Complete, but not unique, which is Emacs admitting it guessed and still showing me none of the alternatives:

completions demo 04a

That's the gap I kept hitting between the default UI and what icomplete had trained me to expect. icomplete shows me candidates on TAB. Here it types at me.

Eglot ran into this too and works around it with its own eglot--dumb-flex style, which skips the merge. That fixes the typing and loses the scoring, so candidates come back in whatever order the server sent them.

I wanted both: flex's filtering and relevance sorting, and no insertion until I pick something. So I wrapped it.

flex-noinsert

(defun my/flex-noinsert-try-completion (string table pred point)
  "Flex `try-completion' that never auto-extends the input on TAB.

The stock `flex' completion style does two jobs: it filters
candidates by fuzzy (subsequence) match, and its `try-completion'
merges the surviving candidates, inserting their common expansion
into the buffer.  With `tab-always-indent' set to `complete' that
merge means TAB silently types a candidate (often a far, wrong one)
*before* the *Completions* list is shown.  Eglot's own
`eglot--dumb-flex' avoids the merge but gives no relevance sorting.

This wrapper keeps flex's filtering and scoring (so prefix matches
sort first, fuzzy ones last) but suppresses the merge:

  - no candidates           -> nil   (no match)
  - exactly one candidate   -> complete it fully (TAB still finishes
							   a unique completion)
  - two or more candidates  -> return STRING unchanged, so TAB only
							   pops the *Completions* list and lets
							   you pick, inserting nothing.

STRING, TABLE, PRED and POINT are the usual `try-completion' args."
  (let ((all (completion-flex-all-completions string table pred point)))
	(cond
	 ((null all) nil)
	 ((= (safe-length all) 1)
	  (let ((sole (car all)))
		(if (string= sole string) t (cons sole (length sole)))))
	 (t (cons string point)))))

;; Register the `flex-noinsert' style: same filtering/sorting as
;; `flex', but with the wrapper above as its try function.
(add-to-list 'completion-styles-alist
			 '(flex-noinsert
			   my/flex-noinsert-try-completion
			   completion-flex-all-completions
			   "Flex matching that never extends input on TAB."))

;; Reuse flex's metadata tweak so *Completions* sorts by flex score.
(put 'flex-noinsert 'completion--adjust-metadata
	 'completion--flex-adjust-metadata)

A completion style in Emacs is four things in a list: a name, a try-completion function, an all-completions function, and a doc string. I reuse completion-flex-all-completions unchanged, so filtering and scoring are stock flex, and I swap in a try-completion that refuses to merge. The completion--adjust-metadata property is what keeps *Completions* sorted by flex score rather than alphabetically.

Then aim it at Eglot only:

:custom
;; ...
(completion-category-overrides '((eglot-capf (styles flex-noinsert))))
;; ...

Minibuffer completion keeps stock flex, where the merge behavior is fine because you can see what it did. Eglot's eglot-capf category gets the no-insert variant.

Now in the same situation as before, trying to complete faL with TAB. The buffer keeps saying faL, and *Completions* opens instead.

It opens with 2664 candidates, which sounds absurd until you look at what's on top: faL itself, then faLeaf, faLess, faLine. Flex matches loosely and scores tightly, so I get a huge tail under a head that's already correct. The trade only works if nothing gets typed into my buffer first.

One more letter sharpens it. faLi cuts the list to 859, and the visible window holds the family I was after:

;; faLi + TAB
;;
;; *Completions*
;;   faLine    @fortawesome/free-brands-svg-icons
;;   faLink    @fortawesome/free-solid-svg-icons
;;   faList    @fortawesome/free-solid-svg-icons
;;   faLinux   @fortawesome/free-brands-svg-icons

Each one appears twice, once for the package and once for the deep import path, and Eglot shows me which module it would pull from. C-n and RET, or keep typing. Unique completions still finish on the first TAB, because that's the one case where the merge can't be wrong.

Here is faL with the fix in place. Emacs wrote nothing into the buffer, and the long tail sits under a head that starts where I was aiming:

completions demo 04b

And faLi, where the whole visible window is the answer:

completions demo 04c

Living with it

A month in and:

✔️ The minibuffer half is a straight swap. Take the drawing away and the functionality is exactly what I already had with icomplete-vertical-mode. M-x, C-x b, C-x C-f: same filtering, same narrowing as I type, same keys under the same fingers. My hands and eyes resynced in a day or two, which is the part nobody can argue you into. I'm not tolerating this until I go back to icomplete. It's an alternative with a different UI and the same behavior, and I'm running it. The list being a real buffer is a bonus on top.

✔️ The in-buffer half surprised me. I assumed I'd miss a floating popup within a week. It took a couple of days to stop thinking about it, and splits are what sold me. Complete inside the left window of a C-x 3 and *Completions* opens in that window, under the code I'm completing. The right split never moves. The list arrives where I'm already looking, and the rest of the frame stays where I left it.

A two-window frame below, avatar.tsx on the left and footer.tsx on the right. I completed on the left, and that's the only half that changed:

completions demo 05

✔️ flex-noinsert is doing real work. Without it I'd have given up on the default and gone back inside a week.

✔️ The scoring is where the defaults still trail. A dedicated fuzzy matcher does better when I type three letters from the middle of a name. flex gets close without getting there.

IMPORTANT: I haven't deleted icomplete from my config, and I'm not telling you to delete yours. And if you enjoy Vertico, Corfu, company or anything else in that family, please keep using it. Those are excellent projects, with real care put into how they look and feel, and which one you run is a personal preference, not a correctness contest. Nothing here is an argument against them.

I wanted to show how good, and how complete, the default completion has become. That's all. It's really cool now, and it's sitting in the editor you already have.

What changed for me is that I now know what the default does. When I go back to icomplete I'll be choosing it rather than inheriting it.

If you run Emacs Solo, you don't have to take my word for any of it, because the switch is already wired up:

(setq emacs-solo-enable-icomplete nil)  ; plain *Completions*
(setq emacs-solo-enable-icomplete t)    ; icomplete, the default

It's a defcustom, so M-x customize-variable RET emacs-solo-enable-icomplete works too. Flip it, restart, and live with the other side for a week. Emacs Solo adjusts the related settings for you: with icomplete off it sets completion-eager-display to t rather than 'auto, which is the setting that makes *Completions* show up on its own.

Give each side long enough that you stop noticing it. After one day you'll only know which one feels unfamiliar, and that's a different question from which one you want.

The full test.el

Save this, run emacs -Q --load test.el, and poke at it. Emacs 31 or newer, since completion-eager-update, completion-eager-display and minibuffer-visible-completions-up-down-map are all new here.

;;; test.el --- default completions playground -*- lexical-binding: t -*-

(defun my/minibuffer-truncate-lines ()
  "Keep minibuffer lines unwrapped."
  (setq truncate-lines t))

(defun my/flex-noinsert-try-completion (string table pred point)
  "Flex `try-completion' that never auto-extends the input on TAB.

Keeps flex's filtering and scoring but suppresses the merge:

  - no candidates           -> nil   (no match)
  - exactly one candidate   -> complete it fully
  - two or more candidates  -> return STRING unchanged, so TAB only
							   pops the *Completions* list.

STRING, TABLE, PRED and POINT are the usual `try-completion' args."
  (let ((all (completion-flex-all-completions string table pred point)))
	(cond
	 ((null all) nil)
	 ((= (safe-length all) 1)
	  (let ((sole (car all)))
		(if (string= sole string) t (cons sole (length sole)))))
	 (t (cons string point)))))

(use-package minibuffer
  :ensure nil
  :bind ( :map minibuffer-visible-completions-up-down-map
		  ("C-n" . minibuffer-next-completion)
		  ("C-p" . minibuffer-previous-completion))
  :hook ((minibuffer-setup . cursor-intangible-mode)
		 (minibuffer-setup . my/minibuffer-truncate-lines))
  :custom
  (tab-always-indent 'complete)
  (completion-auto-help t)
  (completion-auto-select t)
  (completion-eager-update t)
  (completion-eager-display t)
  (minibuffer-visible-completions 'up-down)
  (completion-ignore-case t)
  (completion-show-help nil)
  (completion-styles '(partial-completion flex initials))
  (completion-category-overrides '((eglot-capf (styles flex-noinsert))))
  (completions-format 'one-column)
  (completions-max-height 10)
  (completions-sort 'historical)
  (enable-recursive-minibuffers t)
  (read-buffer-completion-ignore-case t)
  (read-file-name-completion-ignore-case t)
  (minibuffer-prompt-properties
   '(read-only t intangible t cursor-intangible t face minibuffer-prompt))
  (minibuffer-depth-indicate-mode t)
  (minibuffer-electric-default-mode t)
  :config
  (add-to-list 'completion-styles-alist
			   '(flex-noinsert
				 my/flex-noinsert-try-completion
				 completion-flex-all-completions
				 "Flex matching that never extends input on TAB."))
  (put 'flex-noinsert 'completion--adjust-metadata
	   'completion--flex-adjust-metadata))

;;; test.el ends here

Things to try once it's loaded:

✔️ M-x and type a few letters, then C-n and C-p through the list.

✔️ C-x C-f and walk a directory tree with the same keys.

✔️ In *scratch*, type (window-lay and hit TAB twice.

✔️ C-x C-f into a project with an LSP server, M-x eglot, and type a partial method name followed by TAB to watch flex-noinsert behave.

And C-x C-f doing the same job on a directory, below:

completions demo 06

All of it lives in Emacs Solo too, my no-external-packages config, if you'd rather read it in context.

Now, go watch Prot's take after this one.

Happy hacking!

-1:-- The Completions Buffer Is Now Enough (Post Rahul Juliato)--L0--C0--2026-07-30T23:00:00.000Z

Irreal: Turning Two Spaces Into A New Sentence

In the Apple ecosphere—especially in iOS—a convenient short cut is to type two spaces to end the current sentence and start a new one. Specifically, two spaces will delete the spaces, insert a period and a space, and capitalize the next letter. There’s probably something similar in the Android world; it seems like a natural thing to do.

Over at Bicycle For Your Mind, macosxguru has a post that describes three of the recent changes he’s made to his Emacs environment. Two of them—showing tabs and newlines explicitly, and fiddling with his mode line probably won’t interest most Irreal readers. The third is importing the two space shortcut into Emacs.

Emacs, of course, doesn’t have that capability but macosxguru added it with a bit of Elisp which he binds to the Space key with the text-mode hook. It looks for two consecutive spaces and repaces them with a period and space. The tricky part is capitalizing the next letter. He does that by reading the next character explicitly and capitalizing it if it’s a letter. He also checks for some special keys (like an arrow key) and “unreads” them so that Emacs can treat them normally. All the code is in the post so see it for the details if you’re interested.

I’m ambivalent about this change. On the one hand, I use it all the time on my iPhone and iPad but that’s because typing is such a pain on virtual keyboards. On a regular keyboard, I’m pretty sure that I could type the period, space, and next letter just as fast. The shift for the next letter might slow things down a bit but not by much. I haven’t decided yet whether I will adopt it but inertia will probably cause me not to.

-1:-- Turning Two Spaces Into A New Sentence (Post Irreal)--L0--C0--2026-07-30T14:46:59.000Z

Please note that planet.emacslife.com aggregates blogs, and blog authors might mention or link to nonfree things. To add a feed to this page, please e-mail the RSS or ATOM feed URL to sacha@sachachua.com . Thank you!