The ability to mark items in GNU Emacs Dired allows for bulk operations on both files and directories. With marking, onerous tasks can be turned into a trivial ones.
While default Dired has many bulk operation commands, not everything is covered. That is the motivation for mdired, an Elisp package which extends Dired with some SIMD-style commands.
Using these commands fall into two steps:
Mark the items of interest in Dired.
Run the mdired command.
mdired supports the following four commands:
mdired-copy: copy a single file to multiple directories.
mdired-do-command: run an Elisp command or named keyboard macro on multiple files.
mdired-compile: run compile on multiple directories.
mdired-byte-recompile: byte-compile selected files or directories.
TL;DR: LaTeX in Org mode now go whoosh, all pretty like. Install the development branch of Org mode to use it.
After grumbling about the inadequacy of LaTeX previews in Emacs – and specifically Org mode – for over a decade, Tecosaur and I decided to take the leap and rewrite the feature from the ground up. The result is a preview system for LaTeX math that can provide near-WYSIWYG feedback but retains the properties we expect in the Emacs environment: text-first, extensible and composable.
It’s not much code, but it contains many moving pieces and makes contact with several corners of Emacs. Now that the feature is part of Org mode, I thought I would take this opportunity to document how it works from the ground up, and use it as a launchpad to explain relevant aspects of Emacs’ event loop and its unofficial package API
Emacs doesn’t really have a “package API”, since you build Emacs packages from the same elements as Emacs itself.
.
This article is not an exhaustive demo of all the shiny new features, although there are a few videos peppered throughout. It’s also not the user manual (not yet updated on orgmode.org) or a tutorial (see Tecosaur’s This Month in Org writeup).
Instead, it aims to provide an overview of the goals and design of the library, along with some explainers of relevant aspects of the Emacs API and tips to write your own features. This write-up is for you if you are interested in
a broad understanding of LaTeX preview generation (how and why),
learning about Emacs’ event loop, subprocess interaction, overlay-based display and UI, or
a case study on building a non-trivial Emacs “application” from scratch.
This is a feature included with Org mode to preview LaTeX math snippets as images in Emacs buffers. The contents of LaTeX fragments and environments in Org mode are replaced with rendered images.
It has existed for decades at this point, but in a form that I consider barely usable; this rewrite overhauls the code base and adds a number of quality-of-life features. There is a 13 minute demo that can give you a good idea of the new capabilities of Org mode:
Preview generation is fully asynchronous
Or as asynchronous as Emacs in its current form allows.
, with images placed continuously as they are generated
Bulk generation: Preview 200 to 800 snippets per second
Previews blend in correctly with surrounding colors
Automatically match the current Emacs theme (even through theme changes)
Previews are aligned with and scaled to match the font baseline and size
Previews auto-generate for newly inserted LaTeX
Previews auto-update as you change the underlying LaTeX
Preview as you type: Live-previews of LaTeX fragments
Consistent (correct) equation numbering across the document
LaTeX compilation error reporting for previews
And a couple more quick demos:
The rest of this article is mostly about how it works, and briefly why. As such, it has a “fractal” level of detail. There is information intended
to give the casual reader a bird’s eye view of LaTeX preview generation,
to give the motivated reader a detailed look at its many pieces,
for the potential new Emacs package author looking for pointers on aspects of Emacs’ API,
for folks wanting to develop a similar feature for applications other than Emacs,
and information that is of no use whatsoever except to the future maintainers of the org-latex-preview library.
To that end, many details are tucked away in blocks like the following. You can safely ignore them in favor of the top-level headings, images and video demos.
Implementation details
Blocks like this describe the concrete implementation of aspects of the code.
This is probably only interesting if you are looking to dive into the code itself, or if you are looking to write a similar package.
Emacs API notes
Blocks like this are for additional notes on aspects of Emacs’ API, including the odd code snippets.
Warning: Here be idiosyncratic takes.
Org Mode notes
Blocks like this describe other Org features relevant to LaTeX preview generation.
Command output
Blocks like this describe CLI commands associated with this process, or their output.
Isn’t this a narrow feature to spend so much time on?
Indeed, this feature
requires a LaTeX distribution installed,
works only with Org mode (for now),
and is glued together from several processes and grossly inefficient in the larger scheme of things.
It’s also finicky, as all hyper-configurable software is.
LaTeX was not built with the aim of minimizing latency. Having to start multiple new processes even for a one-character change places a hard cap on what’s possible in terms of real-time feedback. Implementing an equivalent preview feature for a system designed for modern computers, like Typst, is trivial in comparison.
Moreover, as popular as Org mode is, it’s a small player in the space of markup languages. The fraction of Org and LaTeX users who would prefer to use Org mode to write LaTeX is smaller still. And while Org attempts to integrate well with LaTeX, navigating the mishmash of Org and LaTeX syntax presents an extra learning curve on top of learning to use the two markup languages individually.
But it also has a few things going for it.
First, LaTeX is ubiquitous, and Org mode is a far more convenient format for writing than raw LaTeX. There is significantly less visual noise (think Markdown vs HTML), and Org provides an escape hatch for adding LaTeX verbatim. Org’s simple interface for shuffling text around, monitoring task progress and executing code blocks makes it easy for eventual LaTeX documents to begin as Org files. Since everything in Emacs composes with everything else, your system for (say) fast LaTeX input transfers over to Org mode seamlessly.
Second, the fact that Org is versatile as a publishing tool implies that some benefits of better LaTeX support transfer over to other formats too, like equations in HTML or ODT exports.
There is a wide swath of applications built on Org mode that benefit from LaTeX math input, such as Org Roam and Org Noter. For these applications converting Org documents to LaTeX is not the goal, as they are not primarily publication tools: they stick around as private digital archives, and better LaTeX support is a bonus to the user’s expressivity.
Real-time feedback when editing LaTeX snippets is a categorical improvement to the user’s experience. I did not realize this when I started work on org-latex-preview, but now I wouldn’t want to LaTeX without it.
It baffles me that web LaTeX editors (Overleaf etc) don’t provide this out of the box, especially as they target novice LaTeX users.
.
Finally, we can keep Org mode out of the picture: org-latex-preview can technically work anywhere in Emacs, and in every major-mode. it’s just not there yet
(See below)
.
But perhaps the most germane reason is that Emacs users don’t shy away from settling comfortably into tiny, cramped and poorly insulated niches. Comes with the territory!
The Objectives
Never Block Emacs
Emacs users are closely acquainted with its frequent unresponsiveness. You’re never more than one minor-mode or mode line indicator away from causing Emacs to block or lag. While it’s not a big ask to wait a second for the result of an action you initiate, the unresponsiveness often manifests as short random freezes, which is unacceptable. But there’s more. Beyond the annoyance and inconvenience, poor responsiveness denies us an entire class of uses: real-time feedback is everything for interactive applications.
Owing to both its design and age, Emacs is not very well suited for asynchronous applications. Certain patterns of asynchronous usage are viable (see below), but concurrency support is limited and parallelism is absent: no worker threads, channels, async-io, or anything of the like.
Generator-based async in Emacs lisp
I lied. In addition to Elisp threads, there exist prototype libraries for async-io (Chris Wellons) and channels/CSP (Andrey Listopadov) based on Emacs’ generators library, which in turn is implemented using continuation passing. But generators as provided by this library do not have anywhere near the level of robustness or support you need to build actual applications: no introspection, error handling or debugging support, poor performance, and just overall jankiness. I’ve tried and lived to regret it – Elfeed Tube is a real mess.
The Futur package is another promising swing at an async-io style interface, but it’s new and I haven’t been able to try it yet.
In any event, a more responsive org-latex-preview would require parallelism and not just concurrency. There is John Wiegley’s async library (and packages inspired by it, like session-async) that spawn a child Emacs that runs in parallel, but communication is limited and the overhead is sizeable, so this is a bit of a bludgeon.
The upshot is this: we want org-latex-preview to be as asynchronous as possible within Emacs’ effectively single-threaded event loop model. The goal is to minimize how long the user has to wait, in terms of both the overall run time and the fraction of that for which Emacs is unresponsive. We end up having to trade these off against each other.
Go Fast (relative to LaTeX and Emacs)
A close second concern is preview generation speed. This is desirable both in the large and the small. Previewing upwards of a hundred LaTeX math fragments in a math-heavy Org document used to be an exercise in patience. Now it takes about one second, and can be much faster with some trade-offs.
LaTeX Fragments
Old preview system (s)
org-latex-preview (s)
Speed up
35
12.96
0.43
30x
70
22.59
0.51
44x
160
33.99
0.68
50x
330
84.95
1.32
64x
660
139.73
1.96
71x
1000
194.16
3.24
60x
Figure 1: LaTeX preview generation times under the slowest settings. The vertical axis is time, and along each vertical line the red chunks correspond to intervals for which Emacs is blocked. We still manage to process a file with 250 LaTeX fragments in about a second with minimal impact on Emacs’ responsiveness.
Provide Real-Time Feedback
Speeding up bulk generation is at most a boost in convenience. You don’t regenerate a large buffer’s worth of previews more than once every few sessions, and the resulting images are cached anyway. The more interesting aspect is that speed enables a close to real-time feedback loop that wasn’t available before. This is especially useful when you’re deep inside a messy structure like an array:
No more counting field separators. No more exiting, regenerating and re-examining snippets. It’s the best parts of WYSIWYG and visible markup, together.
Look Pretty
Even in the niche of academic types who use Org mode with LaTeX, there are very different orientations. There’s Org the transient format, where an Org document starts out as a rough sketch, a thought bubble translated to the screen, and soon transitions into a TeX file that becomes the source of truth. There’s Org the intermediate publication format, where the document lives as an Org file, but its value is in the web page or PDF document it eventually becomes. For the adherents of this style the appearance of the Org document is irrelevant.
Finally, there’s the class of uses described above, where the desired output is the Org document itself, or a cross-linked collection of them. These are folks who care about the aesthetics of Org documents and often suffer a thousand cuts in trying to make Emacs look “modern”.
One of the objectives of org-latex-preview is to ease their self-imposed burden somewhat: LaTeX previews should blend in perfectly with the surrounding text. This means
preview image sizing that is consistent with surrounding text,
with perfect baseline alignment at any font size,
and Emacs theme-aware colors that change automatically with the theme.
Getting the fonts to match is possible too, but requires some degree of compromise on the part of the user
You could start by using the variable-pitch Latin Modern font in your Org documents.
.
Each of these is a long-standing issue with LaTeX previews in Org, and Stack Overflow and the usual haunts have decades of discussions on how to fix them, with band-aid after band-aid proposed as solutions. We took an elephant gun to this problem, starting by throwing out the giant mess that was Org’s accreted LaTeX preview code of the past two decades.
Here’s a demo of theme-aware LaTeX preview colors:
Windfalls: Consistent numbering, error reporting
Finally, there are a few features we picked up along the way because they came free.
Recording compilation errors was a simple matter since we scan the LaTeX output anyway.
Consistent equation numbering was expensive in the past because numbering changes can propagate across the document, and parsing the Org document and LaTeX fragment compilation were both slow. Recent changes to Org have made the former a non-issue (see the parsing section below), and the performance scaling of our bulk LaTeX runs has fixed the latter.
There are several more minor features along for the ride, such as improvements to editing LaTeX with org-edit-special (C-c ').
The Emacs API: An Overview
As mentioned in the introduction, the LaTeX preview system draws on a few different facets of the Emacs API: mainly async process interaction (the “backend”) and overlays (the UI or “frontend”). The live-updating behavior leans heavily on hooks and timers, and we join these pieces together with very simple Lispy data structures, like alists and plists.
We also (naturally) use a few Org mode specific APIs, including ones developed by us for this purpose. We cover these further below as we encounter them.
The Substrate: Emacs’ Event Loop
If you are designing an Emacs application that interacts with the outside world, it helps to start with a mental picture of Emacs’ event loop. To a first approximation, the main loop does only four things:
Read input events: This includes keyboard, mouse and touchscreen events, but also some OS signals. This is the user’s interface to Emacs.
Evaluate commands and redisplay: This is the heart of the Lisp evaluator, and Emacs’ interface to the user.
Together these two connect the user and Emacs. If the main loop did only these two things Emacs would still qualify for the title of “Lisp evaluator masquerading as a text editor”, but it wouldn’t be nearly as useful. For that we need it to
Read subprocess output: This includes output from “regular” processes like shell commands or LaTeX, but also network requests. This connects Emacs to the world
(your computer and the network)
, and vice-versa. Command shells, LaTeX previews, LLM clients and most IDE features work because of this.
The last event loop function combines the above three, but across time:
Run timers: Schedule code for the future and run them when timers are ripe. Somewhat tentatively, I’m including garbage collection here.
This design explains why timers in Emacs are not reliable in the absolute sense. The timer runs not when it’s ripe, but when it’s ripe and when the main loop gets around to it. Nevertheless, scheduling code provides a much-needed degree of flexibility to a rigidly ordered task list.
“To a first approximation”
The way these four tasks are actually organized in the main loop code is less pleasing:
Also, Emacs is obviously doing things outside of the event loop as well. Font caches are compacted, Emacs is responding to the compositor and some OS signals, interacting with dynamic modules, and so on. In the currently experimental MPS branch, some garbage collection activities also occur in the background. These are invisible to the Lisp evaluator and thus out of scope when learning to build an Elisp package.
The Backend: Subprocess Interaction
This design suggests that Emacs can only do one thing at a time
As of Emacs 31
, so that designing a responsive application is a steep ask. But the situation is more nuanced:
Yes, the Lisp evaluator holds a global interpreter lock. At any point in time Emacs is doing exactly one of the following: painting the screen (redisplay), updating syntax highlighting (fontification), running hook functions or timers, responding to window changes, handling keyboard input, and – crucially for us – interacting with subprocesses. Every action is thus blocking.
The only asynchronous interaction possible is by starting an OS-level subprocess
Another lie, since we have dynamic modules, covered below.
. Emacs continues to run independently from the subprocess, but it still has to handle its STDOUT and STDERR streams and respond to process signals. When it is doing this, it cannot pay attention to your input or anything else. Again, Emacs can only do one thing at a time.
Starting asynchronous subprocesses
There are multiple commands to start an asynchronous subprocess from Emacs (see M-x shortdocprocess), but start-process covers most common use cases.
Processes are (optionally) associated with a buffer where process output is inserted. To send a subprocess some input, there’s process-send-string and process-send-region. Responding to process output and signals is the responsibility of process filters and sentinels (next block). kill-process does what you’d expect. All of these functions take the process object that start-process returns as an argument.
Except for some fiddly details, that’s essentially all there is to the process interaction API. There are several variants of start-process available – the synchronous call-process, the shell versions start-process-shell-command and call-process-shell-command, and so on. The only one that does something different is the primitive make-process, which is a more extensive version that can (additionally) create network processes, send data through TCP or Unix sockets and so on.
Emacs will try to prioritize user input events. To do this it can delay tasks like reading from subprcesses if necessary. Idle timers are another example of this. Some actions are thus delayable, but they block Emacs when they (eventually) run. There might be a penalty to pay afterwards, for instance when there’s more subprocess output to process as a result of delaying the task.
Process filters and sentinels: Handling process output
By default, Emacs inserts the output from a subprocess into the process buffer, or ignores it if there is no process buffer. (Crucially, it still has to read the output from the process pipe.) This is good enough for simple tasks. To use or transform subprocess output in more specialized ways, you can write an elisp function that is used as a process filter. It is called whenever Emacs reads new process output. In the language of shell commands, I think of process filters like this:
Process filters can be tricky to write if you want to parse structured process output, because the amount of data that Emacs reads from the pipe can be arbitrary. Unless latency is a concern, it’s often preferable to work with process sentinels for this reason. Sentinels are the second tool Emacs makes available for reacting to processes. A process sentinel is an elisp function (callback) called whenever the process changes state or raises a signal, including when it terminates. Practically speaking, I think of them like this:
They are typically used for cleaning up after the process, and updating Emacs’ state depending on how the subprocess ends. But you can use them to access the process buffer containing all the process output, and thus do whatever you would in the filter.
Filters and sentinels can be attached to a subprocess object created with start-process using set-process-filter and set-process-sentinel respectively.
Many blocking operations can be interrupted by signaling a quit: this is what pressing C-g does, with some variations, in most contexts. Crucially for us, however, quitting is suppressed when Emacs is handling subprocess output or signals
This is to keep your C-g from doing something unpredictable to “background” code that you didn’t initiate.
. If Emacs is blocked because it’s reading a large data dump… well, tough luck.
Interrupting code
keyboard-quit (C-g) is a specific input that interrupts any code, with a few exceptions.
The obverse of this is to wrap code in a while-no-input clause – now any input will interrupt the processing of this specific block. This can be used to do background processing in small chunks without making Emacs unresponsive, but there are subtleties involved that I’m not familiar with.
Despite these carveouts, a package that interacts heavily with external processes faces a latency-throughput tradeoff. The more time Emacs spends starting processes and managing their output, the less attention it can pay to your input. Designing Org’s LaTeX preview thus involves finding an acceptable balance.
Figure 2: (Top) Emacs dealing with a “fat” high-throughput process, where it spends a chunk of time reading subprocess output. The time between reading your input and running the resulting command is thus longer than (bottom) the “thin” low-throughput case.
It is significantly easier to write a large Emacs library than an equally versatile plugin for most plugin systems built around multi-threaded GUI applications. I say this as a non-programmer who has only succeeded at the former. We lean on the interpreter lock in many ways, choosing simple data structures and control flow that wouldn’t work if Emacs was multi-threaded. But most of Emacs wouldn’t work if Emacs was multi-threaded, anyway. In for a penny…
The Frontend: Displaying images over text
Overlays are a way to alter the appearance or behavior of regions of buffer text without modifying the text itself. Displaying images is one of their most common uses: LaTeX preview images are overlaid on top of the source fragments.
Once you have the preview images in hand, placing them in overlays and managing their state is essentially all the LaTeX preview code does.
In fact, overlays account for much of the display trickery in Emacs: Images over text? Overlays. “Virtual text” like in whitespace-mode? Overlays. Inline results of code evaluation: overlays. Multiple cursors? Those are overlays. Emacs has only one cursor.
Making overlays
For a quick look into the overlay API, run M-x shortdocoverlay.
The even shorter version: We can create an overlay ov from buffer position BEG to END with
(setqov (make-overlayBEGEND))
and move it around with
(move-overlayovNEW-BEGNEW-END)
To avoid creating garbage, it is preferable to reuse overlays when possible instead of throwing them away with (delete-overlay ov) and starting over. move-overlay can move them to other buffers too.
The only other tricky question is what happens to the overlay when text is inserted at its left or right boundaries. Does it expand to contain the text? This is left as an exercise for the reader.
These alterations are specified by adding or modifying the overlay’s “properties”. Like symbol properties, overlay properties are a way to store arbitrary information along with the overlay, and org-latex-preview uses them to keep track of state, such as whether the preview image is out of date because the LaTeX fragment has been edited, and to store LaTeX compilation errors for the corresponding fragment.
Overlay properties
To set and retrieve overlay properties, we can use
(overlay-putovpropvalue)
(overlay-getovprop)
where the property prop is any elisp symbol. There is also an overlay-properties function to access the full list. Once again, M-x shortdocoverlay has us covered.
Some properties have special meanings as interpreted by Emacs’ display engine. display, the most common property, changes how the overlay region appears on screen, and this is typically set to the image we want to see in place of the text.
But several more interesting modifications are possible with overlays, such as changing the face (font+style) of the underlying text, making it invisible, making it intangible to the cursor, or specifying keymaps that are activated when the cursor is within the bounds of the overlay.
Fun with overlay properties
The only other detail we need is the list of overlay properties that Emacs considers special. The display property can be used to show an image instead of the text “below” (or “under”) the overlay, for example:
You can use this to set up a “watcher” – perhaps run a process when a region inside a buffer changes
If you want to set up a watcher on the contents of the entire buffer as opposed to a piece of it, you should avoid overlays and look at the hook after-change-functions instead. Be warned: it’s easy to slow down Emacs to a crawl this way if you’re not careful.
! This is not hypothetical: preview-latex does this to re-dump the LaTeX preamble of TeX files (in the background) as you edit them.
Add a highlight and tooltip (“hover text”) when the mouse is on the overlay:
(overlay-putov'mouse-face'highlight)
(overlay-putov'help-echo"You're on an overlay, pal.")
Open the displayed image externally when pressing RET with the cursor on it:
This works by defining a keymap overlay property with the required function. (On non-Linux systems, you will want to change "xdg-open" to an equivalent command-line utility.)
This image stays centered as you resize the window:
This one needs a little explanation: The before-string overlay property displays an object before the overlay itself. The object we choose to display is some propertized text with a displaytext-property. The display text-property provides the means to insert spaces of specified widths, and a symbolic way to refer to the text margins, window center and horizontal window boundary positions. Combining the two gives us (consistently) centered images in Emacs.
The Reactive UI: Hooks and Timers
In its basic form, the LaTeX preview feature involves no reactive behavior: there is one command to overlay preview images on LaTeX fragments, another to clear those overlays, and nothing else.
But this is a far cry from our goal of real-time feedback. For LaTeX previews to be automatically generated and updated as we type, we need both timers and hooks.
Hooks are variables provided by Emacs where we can register code to run at certain points. In this case, we want to react primarily to two kinds of user inputs:
Cursor motion, so that entering a previewed LaTeX fragment reveals its source and leaving it re-displays the image.
Buffer changes, so that a preview generation subprocess automatically fires when a new LaTeX fragment is typed in or an existing one is edited.
In turn, org-latex-preview exposes hooks of its own for consumers to run additional processing. These fire when LaTeX subprocesses finish, when overlays are created, and so on.
Timers are complementary to hooks, used to space out or limit the consequences of hooks firing. They are used
to keep these hooks from firing all the time, which would create redundant subprocesses with each edit, locking up the main loop. They provide the means to implement throttling and debouncing.
to delay clean-up tasks like deleting intermediate LaTeX output until Emacs is idle.
The structure of org-latex-preview
Besides basic Elisp data structures, that’s everything we need from Emacs to implement non-blocking, fast LaTeX previews. Assuming you have a handle on what org-latex-preview does, we can start peering into the how.
How it works: control flow
Here is a high-level overview of the control flow of LaTeX preview generation:
Figure 3: Control flow for Org mode’s LaTeX preview system.
This is a rough picture of how LaTeX previews are generated in Org mode, subdivided into tasks
The made-up term “task” corresponds to a handful of functions devoted, in our heads, to one purpose.
. Each action belongs to one of three categories:
Blocking tasks: These must run synchronously and thus require the user to wait while Emacs is unresponsive. We want this to be minimal and/or done ahead of time.
Delayable tasks: These tasks can be delayed if more immediate actions – such as accepting user input – are pending. However, once such a task is started it runs synchronously and we’re locked in. We want this work to be chunked into small independent pieces.
Asynchronous tasks: These are subprocesses spawned by Emacs and managed by the OS. Emacs monitors the process status and handles process output in a delayable fashion, but is otherwise unencumbered by them.
When a LaTeX preview command is issued, the first chunk of processing (the PREPARE block)
sets up overlays in the buffer that “cover” the fragments, with the intent to add preview images to them at the end,
creates a TeX file containing the relevant LaTeX fragments from the Org buffer, along with a preamble. This preamble is optionally precompiled, which can speed up processing considerably.
It then starts a two step process chain (the RUN block) that runs asynchronously. The end result of a successful run is a preview image or series of images.
The PROCESS FILTERS block: From this point on until either process ends, Emacs checks in periodically through the associated process filter, acting on the STDOUT to capture various bits of preview metadata. It also keeps track of images that have finished processing, and (synchronously) caches them and adds them to the overlays in the buffer for these fragments. This continuous update is doubly useful:
Previews start appearing in the buffer much faster, with the window viewport being updated first.
This processing happens in small chunks instead of all at once at the end, so Emacs stays more (even if not fully) responsive to input.
The POSTPROCESS block: After the subprocesses have finished, Emacs runs their process sentinels, which clean up the detritus of this process chain, or reset the buffer if the preview run failed.
And speaking of detritus…
How it works: file I/O
This figure shows the same process but with an emphasis on disk I/O. The primary chain is TeX file → DVI/PDF output file → SVG/PNG preview images.
Of the file categories involved in the process, we care about two: the “format dump file” produced by precompilation, and the preview images. Both are cached and persisted across Emacs sessions. All other produced files are deleted by the POSTPROCESS block.
How it works: data flow
Displaying previews that are color-, alignment- and size-aware, along with associated information like errors, requires a considerable amount of bookkeeping. Since this information arrives piecemeal and processing happens asynchronously in chunks, we also need some way of storing and passing along this context. There are perhaps clever ways to do this, but we use a straightforward solution – a (dynamic) global plist that associates each active process chain with its data (see org-async below).
All of the data generated and required for the many tasks in our process chain are captured in a single mutable container, referred to here – for convenience – as “Extended Info” (extended-info). This data includes
User preferences for the preview run, such as the LaTeX compiler in use,
Input LaTeX strings to be rendered,
Input file paths, output image paths and image cache details,
Emacs’ context like the current buffer – which is important since we need to restore it before we can run asynchronous callbacks,
Overlay positions and properties,
Compilation errors,
Computed preview geometry and fontsize for alignment,
and more besides.
extended-info: implementation
“Extended Info” is implemented as a plist (property list). Actually, almost every compound data structure org-latex-preview uses is either an alist or a plist, which is just a linked-list under the hood. extended-info is also a tree since a few of its members are plists themselves (see below).
We used property lists at first since they’re mutable and schema-less, and the design of the library was very fluid. Unfortunately we are now well past the point where we could switch to something more structured like cl-defstruct, so plists it is.
If it’s any consolation, most of Org works with large property lists too. Org’s export system passes around a big plist bucket for communication in much the same way we do here. Access can get expensive as they grow, but at least passing them around is cheap. It’s… not much of a consolation.
apply yourself: property lists as arguments
plists have one other coincidental(?) advantage: You can splice them in as arguments to a cl-defun.
Consider:
(cl-defunfoo (&key (buf (current-buffer)) (valid-pt) (pred#'ignore)
&allow-other-keys)
;; body of foo )
This function has the signature
(foo:bufsome-buffer:valid-pt:pred#'always)
which is just a list with the name of the function followed by… a plist. So we can build up the plist programmatically and use it directly as foo’s argument list:
’tis a silly thing but oh, so satisfying. In fact, this is a convenient way to feed a job spec to org-async (see below).
How extended-info is populated
The setup functions in the PREPARE block initialize this bucket with data from parsing the buffer – i.e., LaTeX fragment locations, equation numbers, etc. From then on, each task updates extended-info with progressively more information. The structure of extended-info and this flow of information into it is visualized in this “data flow” diagram:
Contained in “Extended Info”: “Processing Info” contains information about and relevant to the external process (LaTeX and the image converter). “Fragment” is a list of metadata for fragments to be previewed, including the overlays that will eventually display the preview images. All of this preview metadata is obtained from the outputs of the latex and image converter runs via the process filters.
Note that Emacs’ interpreter lock precludes data races and other concurrency issues, since only one function modifies extended-info at a time.
How extended-info is accessed
The above chart only captures the flow of data in one direction: from the sources of computation into the extended-info container. Tasks that don’t modify extended-info still need to read it to do their jobs. For example,
Clean up temporary files needs the paths to the temporary files.
Cache images and metadata needs the generated image paths, as well as all the geometry metadata.
Most functions in the process chain are passed extended-info, or a subset thereof, as an argument.
Once again, we get away with it because there are no concurrency problems when there is no concurrency. This design pattern of passing around a single heap-allocated container across setup functions and callbacks works for all kinds of applications in Emacs. This is exactly how Org’s (synchronous) export system manages information as well.
org-latex-preview Pieces and the Org API
This is where we get into the weeds. If you’re here for the bird’s eye view, you can skip ahead to Live previews now.
Org’s syntax is extensive, and parsing Org files used to involve a giant mess of regular expressions. Thankfully the mess is now handled by the org-element library, which provides a simple and powerful API for querying or acting on Org’s AST. A fair amount of work has gone into making it perform well under the constraints of Emacs’ single-threaded event loop
Besides caching its state, it uses timers to schedule parsing work outside of the times the user is interacting with Emacs.
.
Crucially for us, Org has first-class support for LaTeX environments and fragments, and this greatly simplifies parsing and metadata handling. This support is what makes using LaTeX in Org a pleasant experience in the first place.
The org-element library
Added in late 2021, the org-element library greatly simplifies the process of working with Org documents.
I have a minimal working knowledge of the library, so this block covers only the aspects of org-element that we use in org-latex-preview.
Org’s syntax distinguishes between “elements” and “objects”, where anything “larger” than a paragraph is an element, and any syntax that could be contained within a paragraph is an object
Incidentally, the default mark-paragraph binding runs org-mark-element in Org mode, which selects the smallest logical element is at point instead. Try M-h at different locations in an Org buffer to see what element you’re on!
.
This distinction matters to us because LaTeX text to be previewed can be either. As you might expect, inline math ($...$ or \(...\)) is an object, and LaTeX environments (\begin{foo}...\end{foo}) are elements, and they differ in the way we must display the preview images.
Every node in the AST as parsed by org-element is made available as a plist whose keys are properties of the element. These properties include the start and end positions of the element and its contents (:begin, :end and :contents), as well as metadata like properties and keywords. For example, every element can be named with a #+name: keyword.
These are the only things we need from org-element as far as parsing goes.
The objective of the parsing step is to find the bounds of LaTeX fragments in the buffer (or in some region). In principle, we could do this with org-element:
But this would be too slow, and would allocate far more memory than we need. Instead, we use a simple regexp scan of the buffer to find the bounds of LaTeX fragments and environments, and org-element only to gather specific metadata about these elements.
Many editor plugins (and web apps) implement LaTeX previews, usually via MathJax or KaTeX. KaTeX is fast, and both function well for web pages with limited needs. But they implement ersatz support for a limited number of LaTeX packages, and cannot consume an actual “full-fat” LaTeX preamble of the kind you might use when writing a research paper. There is no \usepackage{} macro or TikZ support, for example.
By comparison, org-latex-preview is backed by a full LaTeX engine and LaTeX distribution, so nothing is off the table. Of course, this means we need a way to construct the preamble and feed it to the LaTeX engine.
In addition to Emacs-wide settings (like org-latex-preview-preamble and org-latex-packages-alist), document-specific LaTeX headers can be specified with the #+latex_header: directive:
The combined preamble must be constructed when exporting the Org document to LaTeX, and this machinery lives in the ox-latex library. org-latex-preview simply calls into it for the preamble. Among other benefits, this means the preamble used for the preview run is identical to the one used for LaTeX exports
unless you want them to differ, because of course Org provides ways to do that
, so these truly are previews.
The org-export (ox) library
The ox library and its children, like ox-latex provide an interface to convert Org documents to any other format, like Pandoc does.
While the specifics (like collating a LaTeX preamble) are output-format dependent, the basic method is uniform and simple. An exporter maps each Org syntax element and object to a function that creates the equivalent in the output format, like image links to HTML img tags, or #+begin_quote ... #+end_quote blocks to HTML blockquotes elements. The fact that the Org syntax supports LaTeX environments natively helps with LaTeX exports.
Of course, when a clean mapping like this is not possible, you can work on the buffer directly and rip out and replace raw text. Emacs, huh.
This does make ox-latex one of the few dependencies of org-latex-preview, which is regrettable given the larger aim of providing LaTeX previews across Emacs.
To process a preamble, the LaTeX engine needs to tokenize and execute tens of thousands of lines of macro source. For a complex preamble, the kind you would use for a book, this can take several seconds on a modern processor.
Since org-latex-preview aims to provide close to real-time feedback as you type, we resort to a trick from the 1990s: we serialize the entire engine state – the string pool, macro bodies as token lists, font data, engine execution parameters and so on – into a single binary memory image. This process, called format dumping or precompilation, was automated for document preambles by David Carlisle’s mylatex.ltx, modernized as the mylatexformat package by Florent Chervet.
The upshot is that precompilation saves a significant amount of LaTeX startup time, reducing and (almost) flattening it across all preamble sizes.
Precompilation relies on a number of hacks and only works because pdftex has changed little for decades. Unfortunately, precompilation is not supported at all by xelatex and only partially by lualatex
preambles that load OpenType fonts or Lua code are unsupported
, so org-latex-preview is slow with the Unicode engines.
Under a different LaTeX preview architecture, none of this would be needed. But it’s what we have so far: a very 90s solution for a very 90s editor.
Creating the TeX file
And speaking of being stuck with an old architecture, depending on pdflatex implies that we have to create and feed it a TeX file for each batch of previews
pdflatex and friends do accept input via STDIN, but this did not work out for finicky reasons.
.
As you might expect, the generated TeX file contains a reference to the precompiled dump file and a listing of all the LaTeX fragments to be previewed. Instead of placing them into the document separated by something like \pagebreak, we place them inside \begin{preview} and \end{preview} blocks provided by the preview.sty CTAN package.
preview.sty is authored by David Kastrup, who also wrote the Emacs-side preview.el package that’s part of AUCTeX. preview.el was the inspiration for org-latex-preview – it was clear at least that previews in Org mode could be much, much faster.
preview.sty does three critical things for us:
It places each preview snippet (enclosed within \begin{preview} and \end{preview} blocks) on a separate page in the LaTeX output;
it finds the boundaries of the generated text and crops the page down to them;
and it reports the font size, image size and baseline position in the LaTeX process’ output stream.
These modifications were technically doable through a combination of other packages (Org’s old implementation used the tightpage option of the preview machinery), but in cumbersome and error-prone ways.
This makes preview.sty an external dependency of org-latex-preview. Fortunately this package is included by default even with minimal installs of all common LaTeX distributions.
The process runner
The pipeline from the LaTeX fragment text in the TeX file to images suitable for preview consists of two steps. A LaTeX engine (pdflatex, xelatex or lualatex) produces DVI, XDV or PDF output, which is fed to an image converter (dvisvgm, dvipng or imagemagick) to produce PNG or SVG images. This is the RUN block of the control flow diagram.
This involves a fair bit of bookkeeping, and Emacs’ callback-based async process management (via “process sentinels” and “process filters”) isn’t the most ergonomic tool for this. In the absence of reliable async-io support, we use org-async, a declarative process chain manager now bundled with Org mode.
The org-async library
Because we need to run a chain of processes with different callback behaviors depending on the exit statuses, org-latex-preview uses org-async, a process queue manager that wraps Emacs’ start-process, process filter and process sentinel API. It handles a lot of the bookkeeping for us, providing for
declarative specification of a tree of processes that branch on the exit status of a process,
including the ability to register multiple callbacks as required,
keeping track of the context (the extended-info bucket in our case) and passing it to the filter and sentinel,
and limiting the pool of concurrent subprocesses and handling timeouts.
The global variable that keeps track of the context data between async calls is managed by org-async.
The following code might be out of date, please refer to the Org manual.
An example of an org-async job spec might look like the following (simplified for clarity):
org-async-call also provides other conveniences like timeouts, retries and concurrent subprocess limits.
An example of this bookkeeping is that the pipeline isn’t always serial. When generating PNG images with dvipng, we start LaTeX and dvipng in parallel. Dvipng processes pages of the DVI file as they appear, while the LaTeX process is still churning them out. (Generating PNG preview images for a large Org buffer is much faster than the other routes.)
Tracking with process filters
On the other side of the process invocation are the “process filters”, which are callbacks called by the event loop whenever Emacs reads chunks of subprocess output. This is the PROCESS FILTERS block in the control flow diagram.
A shell metaphor for process filters
Following the shell metaphor for process filters, a simple way to think of a process filter for an Emacs subprocess is as a sink at the end of a Unix pipe:
some-process 2>&1 | process-filter
In the LaTeX preview case, the picture is a little more complex as there are multiple processes:
In org-latex-preview’s process filters we look for the output (DVI or image) file paths, but also a variety of metadata that preview.sty and dvisvgm emit. This includes geometry information like the width, height and baseline alignment of the image, and any compilation errors.
Finally, any compilation errors reported for each preview in the above output (none here) are captured as well.
Errors are reported on lines starting with “lN”, where N is the line number.
Messages are reported (as above) on lines starting with “!”.
The preview.sty manual provides more details and documents other options.
Sample image converter output streams
pre-processing DVI file (format version 2)
processing page 1
applying bounding box set by preview package (version 13.1)
width=346.003754pt, height=15.362961pt, depth=.501877pt
graphic size: 346.003754pt x 15.864838pt (121.606411mm x 5.575853mm)
output written to /tmp/org-tex-ang3Se-000000001.svg
1 of 1 page converted in 0.16035 seconds
[1 (preview-latex version 13.1) (preview-latex tightpage option detected, will use its bounding box) depth=1 height=29]
Fine-tuning Emacs’ process interaction parameters
“Whenever Emacs reads chunks of subprocess output…”
This phrasing omits some important but fiddly details about how often and how much output is read at a time, in much the same way as the Unix pipe | symbol does.
While the event loop is switching between its four main tasks continuously, there are (only) a couple of knobs available at the Elisp level to manage how Emacs reads subprocess output.
read-process-output-max limits how much it reads in one event loop turn. Setting it to a high value means output from chatty processes will be read sooner, but at the expense of blocking user input. This is, essentially, Emacs’ latency vs throughput curse in a nutshell.
process-adaptive-read-buffering tries to delay reading from subprocesses that produce very small chunks of data at a time, as this is inefficient. In practice turning this on has tended to make things worse instead of better when running most subprocesses.
Trying to optimize this for org-latex-preview has proved to be… difficult. The rates and sizes of output emitted varies significantly between different LaTeX engines and image converters:
Figure 4: Intervals for which Emacs is blocked when previewing using SVG and PNG images. The vertical axis is time, and the solid chunks are periods where Emacs is unresponsive.
They differ even between different versions of the same program across Texlive versions:
Figure 5: A time-aligned flamegraph profile showing process filter calls for the LaTeX and image converter processes when using Texlive 2022 and 2023. In the aggregate, the LaTeX and dvisvgm binaries shipped with Texlive 2023 aren’t faster or slower than the former. They just have a different profile across time to which it is difficult to adjust.
org-latex-preview settles for a broadly acceptable set of defaults in this regard.
But the main reason we use process filters here is that we can place images in the buffer as they are generated (and read in from the process STDOUT by the event loop), and don’t have to wait for the LaTeX or image converter processes to end.
Cleaning up with process sentinels
The unfortunate result of being forced to do file I/O at each step of the pipeline is that we are left with a trail of intermediate files that need to be swept away
Instead of, say, a block of shared memory that can simply be freed.
. This is the POSTPROCESS block in the control flow diagram, accomplished in the “process sentinels”.
A process sentinel is a callback invoked by the event loop when the process changes status. In principle this includes any status change (exit, signal, stop/continue), but in practice we only care about it after the process exits.
Clearing the intermediate detritus of the compilation is not an urgent task, so org-latex-preview’s process sentinels actually schedule this on an idle timer. Their other major function in this design is to diagnose process failures and restart the job when the cause looks transient.
LaTeX preview generation is an order of magnitude (or two) faster than what Org did before, so caching preview images might seem like a secondary concern. But org-latex-preview still caches images, for a few reasons:
The same state may be encountered dozens of times when editing a fragment with live previews.
We would like to avoid rerunning LaTeX in contexts where the Org document itself is the final output, such as in-Emacs slideshow programs.
When exporting to other formats (HTML or ODT), org-latex-preview is responsible for generating the images to be bundled with the web page or document.
In addition to the text of the LaTeX fragment itself, preview images are specific to the LaTeX preview pipeline configuration, the preamble, the Emacs theme and even the fragment’s position in the buffer when equation numbering is turned on. To cache previews reproducibly, all of these inputs are hashed together as the key to the cached image.
The org-persist library
While you can specify where the preview images should be cached relative to the document, org-latex-preview defaults to using org-persist, a general-purpose caching library that is part of Org mode. org-persist is used for various caching duties by Org mode, such as resources fetched from the Internet and the partial syntax trees of parsed Org buffers.
Caching LaTeX preview images actually involves more than copying a file somewhere and serializing a hash table to disk. We also need to store, as Elisp data, the image display geometry information, including the baseline alignment height. The org-persist API supports caching different container kinds for exactly this purpose.
Finally, with sufficient time (or a large enough corpus) the cache of LaTeX images can grow to gigabytes in size, so cache entries are set to expire after a month, another function served by org-persist’s built-in garbage collection.
Displaying images and overlay management
This covers the in-buffer UI and affordances of org-latex-preview, indicated by the Create overlays and Place LaTeX previews blocks in the control flow diagram.
Beyond what is covered in displaying images over text, displaying the image itself is simple: the image just needs to be resized and aligned according to the recorded geometry. The overlays themselves are set up before starting the LaTeX run, and a series of overlay properties are set or unset over the course of the process.
If an existing image is being updated, the overlay optionally gets a “face” property indicating that the preview is out of date.
If this is a fresh preview, an overlay property marks it as “processing”, displayed in the fringe.
If any compilation errors are captured, they’re placed as tooltip text via an overlay property. Via the SVG’s currentColor attribute, we also color the overlay image red to indicate that there was an error.
These are basic but (hopefully) intuitive indicators of the state of the preview.
Live previews: Hooks galore
The breakdown provided so far does not explain the claims we made in the objectives section, like real-time (or at least fast) feedback as you type. As seen in the demos above, there is one more element to making the previews appear “live” and reactive to cursor movement or buffer edits.
For the seamless hide/show behavior:
Moving the cursor into the region covered by the overlay should “open” the overlay, reveal the underlying text and move the image next to or below the LaTeX fragment.
Moving the cursor out of the region should do the reverse, and “close up” the overlay.
For real-time feedback:
Editing a fragment with a visible preview marks the preview out of date, so the image should either disappear or be updated.
If editing displaymath and the numbering changes, all other numbered environments in the buffer should be updated as well.
Editing a previewed fragment in another buffer (with org-edit-special, C-c ') should cause the preview to be updated as you type.
When typing or pasting text, LaTeX environments need to be detected and parsed, and the preview pipeline started in real time.
All of these are achieved using hooks, which is ultimately the only mechanism we have to trigger reactive behavior in Emacs. The hide/show behavior involves tracking the cursor position via pre-command-hook and post-command-hook (made buffer-local). Buffer edits are tracked via after-change-functions, but with several fast-fail heuristics to avoid slowing down Emacs
The difficulty of identifying, in real time, $ and $$ as LaTeX delimiter starts, delimiter ends, or as actual dollar signs is why live previews require the unambiguous \(...\) and \[...\] delimiters, even though the dollars are supported in non-live usage.
. The list goes on, since we need to track Org’s folding states, as well as changes in other buffers when using org-edit-special.
The other strategy we’re forced to employ, given the event loop’s limitations, is to throttle and debounce pipeline calls, batching edits and avoiding race conditions between in-flight LaTeX runs – the interpreter lock protects Elisp state, but independent subprocesses will happily interleave and clobber each other’s output.
Finally, this feature is the source of dozens of edge cases, such as editing fragments inside folded regions (during global text replacement, say), or the many invalid intermediate states of a LaTeX fragment causing the preview to fail repeatedly. Knocking down these bugs while keeping this path fast was a substantial portion of the effort over the years this library was developed
Now that it’s part of Org mode and seeing more widespread use, there are no doubt several more edge cases waiting to be reported!
.
LaTeX previews everywhere in Emacs
A proof of concept: the design of org-latex-preview makes it possible to preview LaTeX outside of Org mode. Here’s an example previewing LaTeX-formatted comments in a prog buffer (Clojure, here):
In fact, you can use the same code to preview LaTeX fragments literally anywhere in Emacs, including, delightfully, the Emacs calculator. This (18 minute) demo runs through a bunch of contexts outside of Org mode showcasing live LaTeX previews:
While this feature exists only as a prototype, it’s simple to implement and extend. To see why, let’s look again at the preview process breakdown. All tasks here are agnostic of Org mode save for two:
Collect LaTeX fragments: currently handled by org-element,
Create preamble: currently assembled by the org-export (ox-latex) library.
To use LaTeX previews outside of Org mode, we only need providers for these two tasks:
A parser that can identify the boundaries of LaTeX fragments in the buffer
Technically you could reuse org-element in non-Org buffers for this, but there will be many false negatives and occasional false positives.
. This does not need to be a full-fledged parser – just a regexp scan of the buffer looking for \begin{ or \[ can suffice. This is how the above demos were created.
A LaTeX preamble generator appropriate for the buffer. This can just be a constant string (instead of a function) that includes a reasonable set of LaTeX packages. I used Org’s default LaTeX preamble string everywhere in the above demos.
The Emacs-wide API for org-latex-preview
This description might be out of date, please refer to the Org manual.
org-latex-preview tries to separate these independent concerns by providing the function org-latex-preview-place, that generates previews anywhere in Emacs given the above information. Previewing LaTeX fragments is as simple as:
where entries encodes the start and end positions of the fragments, and preamble is the LaTeX preamble to use.
;; entries is a list of lists;; Each entry is (start end),;; or (start end contents)((13421388)
(19112104)
(22732295"\\( \\epsilon + 3 \\)"))
;; preamble is a preamble string;; See `org-latex-preview-preamble'"\\documentclass{article}
[DEFAULT-PACKAGES]
[PACKAGES]
\\usepackage{xcolor}"
The positions in entries would be obtained from your parser for the major mode. For preamble you can just reuse or tweak org-latex-preview-preamble in a pinch. See the function documentation for more details.
Requiring the org-latex-preview library will in turn load a few Org libraries (org-element, org-persist, ox and org-macs), but it does not load all of Org. We tried hard to keep from loading the 16,000-line behemoth that is org.el.
As of late 2026, abstracting the library from its major-mode adapters remains on the TODO list.
How a modern LaTeX preview system should work
When we started org-latex-preview in 2021, live-updating previews were not available anywhere except one LaTeX formula-editing website and, to a lesser degree, LyX. We often wondered why, since preview.sty did most of the work for you and the editor side was pretty simple to implement. If org-latex-preview were a thrown-together external Emacs package providing many of the same features (sans polish), it could have been written over a couple of weekends. I know this because I wrote it. Twice.
Over the years, however, this feature has grown in ubiquity and there are plugins for most note-taking (Obsidian) and editing software (Neovim, VS Code) that provide this. Emacs itself has at least four active implementations of live LaTeX previews now, and a frontier LLM can probably implement a basic version for your preferred platform with one prompt.
Even so, Emacs remains the oldest platform to implement this feature, and does so in the most complete and mature form
As of 2026.
. Considering I spent a lot of this write-up complaining about the limitations of the Elisp runtime, this is remarkable.
But creating temporary files after every buffer change and spawning separate LaTeX processes remains a terrible way to do real-time updates. Precompilation is another hack, unsupported by LaTeX engines besides pdflatex. While org-latex-preview is a massive improvement over the previous LaTeX preview implementation for Org mode, it’s still the Elisp equivalent of a Bash script that got out of hand. Quite simply, this is the wrong level at which to address the problem. A better way to implement continuous previews would be to talk to a managed process over a pipe or socket. The right approach here is to work at the level of the TeX engine, as TeXPresso does.
But it would be even better to avoid inter-process communication entirely and use an Emacs dynamic module, linking with a modern LaTeX library that does incremental (instead of batch) compilation. “Lightweight” alternatives like KaTeX or MathJaX don’t cut it, since we need a full-fat LaTeX compiler, macros and all. One possible candidate for this is realtime-tex, the rtex library, which embeds LuaLaTeX (the most modern of the LaTeX engines) and compiles incrementally and effectively instantaneously
Although I would be inclined to wait for the project to mature.
. This would be a quality improvement almost as drastic as the one org-latex-preview makes over its predecessor.
Of course, a dynamic module linking an incremental compiler is a completely different architecture from what we have now, and would share at most the image display and UI layer. So projects like this and the inevitable official Typst preview package for Org are tabled for another day. Maybe you’ll be the one to implement them?
This is a long one but I really want to get transcript+links sorted out, so I might as well share the transcript draft as I go along.
0:00 Linux is the library that Emacs uses to talk to my hardware
0:05 Highlight: AI churn
0:17 Highlight: Maybe your editor makes you grumpy
0:26 Highlight: shelling out to Emacs from Vim
0:39 Highlight: My husband
0:46 Highlight: automation
1:00 Highlight: Thumbnail: my daughter rates it 0 out of 5
1:14 Sacha's idea for monitoring six EmacsConf calls at once
1:47 Intro
3:07 frazzor749
3:27 Intro
5:07 Weekly news
5:27 EmacsConf
9:20 Cross-pollination
12:01 Not Emacs vs Neovim; it's about what we can imagine
12:20 What's the catch?
13:05 Unix Power Tools
14:02 What is Meta x?
14:24 Searching handwritten sketches: Evernote to ripgrep
16:03 Manila to Toronto
16:50 Not about converting people, but about finding the tools that fit you best
17:23 What do I do right now?
18:38 Learning French
18:57 Sitting out the AI churn
20:10 Thanking YouTube members
21:00 Backstory of real-time speech-to-text transcript
23:15 Demo: real-time transcript and instant replay in Emacs
24:03 Demo: Video editing witin Emacs
27:51 Demo: generating video title cards from WebVTT in Emacs
29:06 fframes, generative AI
29:49 Demo: Stepping through SVGs in Emacs
29:56 I draw on an iPad with Noteful
30:20 Light themes and dark themes
30:58 Art and stick figures
31:58 Linkarzu's video editing workflow
33:05 Local CPU-only speech recognition in Emacs
35:15 Sharing speech commands
36:14 Speech commands and more shortcuts
36:23 Having fun with speech commands
37:01 Dictionaries for correcting speech recognition
38:19 Demo: Adding a misrecognized term to my common edits dictionary
39:57 Turning video transcripts into searchable blog posts
42:07 Demo: Saving and cropping screenshots from a video at a timestamp
43:22 Demo: M-x doctor with speech in and out
44:09 Demo: speech-enabled conversation with a large language model in French
45:46 Sacha's Linux setup: XFCE, X11 and Emacs on top
47:18 Automating other apps with Emacs; X11 versus Wayland for automation
49:15 Just a regular laptop keyboard
49:56 Using Emacspeak to deal with laptop screen failure
51:45 Anticipating physical and cognitive challenges
52:37 Describing Emacspeak
53:51 Linkarzu adjusts his electric sitting/standing desk
54:13 Parenting and fragmented time
56:20 Capturing tasks from the phone with Orgzly Revived
1:01:55 Sacha Chua on AI and the Emacs community
1:08:03 Kids' obsessions: Harry Potter, Pokemon, Wings of Fire
1:14:46 AI-generated code, slop PRs and the Lisp Curse in Emacs
1:20:58 Advice for Emacs beginners: connect with people
1:25:16 Why is the Neovim subreddit toxic while Emacs is friendly?
1:31:02 Planning an Emacs and Neovim panel with Prot
1:35:53 Do editors and terminals still matter with AI agents?
1:41:04 Speech synthesis in Emacs: whispering slide hints
Transcript (lightly edited)
0:00Linux is the library that Emacs uses to talk to my hardware
Sacha: At the moment, Linux is the library thatEmacs uses to talk to my hardware.
0:05Highlight: AI churn
Sacha: I'm going to sit out all the AI churn.I'll figure that out.And then I will check back in next year, a coupleof years, maybe never, and see if there'sanything else I can help with.
0:17Highlight: Maybe your editor makes you grumpy
Sacha: Hypothesis number two.Maybe your editor makes you grumpy.Turns out I cannot say start streaming reliably.Sometimes I say start screaming.
0:26Highlight: shelling out to Emacs from Vim
Sacha: And the guy hemmed and hawed and eventuallyadmitted that he had actually shelled out theEmacs to run the code and evaluate it and thenget the image, get the layout back and then putit in Vim.
0:39Highlight: My husband
Sacha: I came here for my masters and stuck aroundbecause of my husband who is coincidentally aNeovim user.
0:46Highlight: automation
Sacha: I find myself automating things that reallyaren't technically justified by how often I doit, but because all those little bits of code addup and they become useful in a combinatorial way.
1:00Highlight: Thumbnail: my daughter rates it 0 out of 5
Sacha: So you got like a zero out of five.Linkarzu: A zero out of five.And it's one of my favorite ones, Sacha, come on.
1:14Sacha's idea for monitoring six EmacsConf calls at once
Sacha: In fact, I have EmacsConf coming up, right?And sometimes we get to the point of having sixsimultaneous conversations.I might have to actually monitor all of them.So I'm thinking of making myself a little overlaythat lets me tab to the different web conferencesand then do something fancy with the audio streamso I can isolate it and maybe have some speechrecognition running on all of them in thebackground so that if somebody says, "Help, Sacha!",I can just pop them right up.Linkarzu: Oh yeah, quite interesting.
1:47Intro
Linkarzu: So I don't like doing an intro and all of thatstuff, so thank you for being here.Sacha, this is the first time that you've been onthe channel, right?Sacha: Yeah, yeah.I mean, you've had plenty of time, so you'vecertainly crossed over into the Emacs communityand things like that.But I'm not sure, am I the oldest Emacs useryou've had so far?Linkarzu: The Professor, he's like 80 years old.But you're not talking about age, your age, butyou're talking about Emacs, right?Sacha: [He would] definitely have beenusing it for longer than I am.I'm a relative newbiecompared to those folks, right?Linkarzu: No, I was just playing.I always play that he's like 80 years old, butno, he's not.You know the Professor by any chance?Sacha: I came across some of your videoswith him.Linkarzu: Okay.That is why I found out about you.Because he was like, you should check SachaChua's blog.And I was like, who is Sacha?You know, because I knew Prot, I knew, you know,the system crafters, DistroTube, stuff like that.But he is the one that mentioned Sacha.And I was like, okay... How do you pronounce yourname, by the way?Is it Sacha?Sacha?Am I saying it wrong?Sacha: Sacha Chua.Linkarzu: Sacha Chua.Okay, okay.So, the professor is definitely not 80.
3:07frazzor749
Linkarzu: You see what frazzor749... frazzor749 is an Emacs user,the guy in chat there.Sacha: Oh yeah!Yeah, yeah, frazzor749.Linkarzu: I'm going toconvert him to Neovim.Sacha: No, he's been helping mewith French.Leave him alone.He's mine.Linkarzu: Okay, you know him?You know him?Sacha: Hands off.
3:27Intro
Linkarzu: Okay, let me just play a little video of what Iknow about you.You're going to see it in the background.It's going to be a little bit confusing, okay?But this is just so that people have a little bitof context on who Sacha is, okay?Sacha: I'll try not to laugh too much.Linkarzu: Okay, so what do I know about Sacha?She has a YouTube channel.You have close to 5000 subscribers, right?Also, you have a blog, which is pretty popularout there.You do EmacsConf, which I don't know what it is.I'm curious to hear about it a little bit more.And I've seen your livestreams.I've seen that you speak in French.So I'm like, is French her main language?Is English her main language?You've been doing Emacs for, you mentioned, 25years.Is that right?Sacha: Yeah, it's about...Linkarzu: Okay, okay.And I heard about you because of the professor.He mentioned your blog in one of the live streamswe had.And I think that's about it.I don't know, like, a lot more.And that's what this call is for, you know, toget to know you a little bit more.Like, on the Neovim side of things, I don't knowif a lot of people know you.On the Emacs side of things, you seem to bepretty popular.So...You want to share a little bit about you.Like, who is Sacha?You can share your screen if you want.You can share your blog post if you have somenotes or something like that.It's up to you.Sacha: I think you've already covered all thebasics.
5:07Weekly news
Sacha: I've been enjoying playing around with Emacs forthe past 25 years and I love putting together aweekly newsletter.I'm going to try to convince you or someone inyour community to put together a newsletter likethat for Vim because it's such a great way tocome across all the interesting things thatpeople are sharing.
5:27EmacsConf
Sacha: Because I also want to encourage people to getcool demos out of their head and off theircomputers and into a form other people can watch.I help organize EmacsConf, which is a yearlyEmacs party.We're just sharing the cool things that we'vecome up with and meeting people with similarinterests.It's all online.And then I get to turn all of those into captionsso that people can search them afterwards.It's a lot of fun.Linkarzu: Is it like a live stream?Is it like a video thing?Or what is it?Sacha: Yeah, we're live streaming on December12 and 13.We have two days of two tracks.That's how much people want to talk about Emacs.It's kind of like NeovimConf, except we actuallyrun everything on time.Linkarzu: Oh!Who copied who?Did you copy Neovim or Neovim copied?Sacha: No, this iteration has been since 2019.The first one was in 2013.So this one's been running since 2019 completelyonline and completely free.Linkarzu: And where can we watch it?Is it in your channel or is there an officialYouTube channel?Sacha: All right.So we have like this whole thing, right?It can be watched using entirely free and opensource software.If you go to https://emacsconf.org,you can find all the details.So people can watch it using MPV or VLC orwhatever.We'll stream it over Icecast.But of course, since a lot of people come throughYouTube, I'll also restream it through theEmacsConf channel.Linkarzu: Oh, so it has a YouTube channel,EmacsConf.Sacha: Yes, which we don't actually talk aboutmuch, but if you search for it, you'll find it.Linkarzu: Where is it?Is it here?Sacha: I do not need to tell you how to useYouTube to search for things.Linkarzu: Oh!Oh, man.Okay.Let me see.Sacha: Now that it's possible to use Canvasinside Emacs to play things, people have beenplaying Doom in Emacs.I'm pretty sure someone could probably write aplayer to watch EmacsConf from within Emacs,which I think would be super cool.Linkarzu: Oh, really?Okay, it's crazy.I think, well, people have beenplaying Doom on Neovim.Like, we won that one, I think.Okay, let me see.EmacsConf, so it has its own YouTube channel.So, okay.This is all the previous events,right? Interesting.Sacha: Yeah, those are my notes and how it's run.It's surprising...Going back to Neovim...There was a NeovimConfand there hasn't been one,what's up with that?Come on, you know, you can do it again.Although actually I should probably be buggingPrime and TJ about that, maybe?Anyway, so our experience with EmacsConf is thatit's totally possible to run a very fun onlineconference about a text editor for a shoestringbudget and totally volunteer labor.Worth a try.Linkarzu: Okay.When was the last NeovimConf?By the way, you're right.Linkarzu: Last year, I think there was no,there was not a NeovimConf.I'm not exactly sure when the last one was.I don't know.Sacha: Maybe it's because you lost bashbunniand then everyone gave up hope.Linkarzu: Right?That is actually true.And who is invited for this EmacsConf,the next one?Sacha: Everyone!We actually get a lot of people who areinterested in other editors as well, andsometimes they present things too.I can probably squeeze an extra talk into theschedule if I really need, you know, if someonecomes up with a really good one.But in the past, we've had talks about Guilemacs,remacs, which is a Rust port,things like that too.
9:20Cross-pollination
Sacha: But of course most of it is Emacs and Org Mode.I have this afternoon of very interestingOrg Mode talks and I want to attend all of them.As I mentioned, bashbunni is speaking there, butalso Ihor Radchenko, the current maintainer of OrgMode has a talk on how he deals withan overwhelming task inbox and lots of stressesand how he makes sense of that in Org.And now that heisenbug is working on org.nvim, alot of the workflow things might be easy totranslate into your setup.Linkarzu: Exactly.What do you think about that betrayal?I asked Prot, you know, because Heisenbug isbetraying Emacs.Sacha: No, no, I think it's wonderful.I think it's fantastic to have more people beable to take these interesting workflow ideas andexperiment with them in the environment that theyparticularly like or are familiar with.So yeah, I mean, you tried Emacs, you gave it aa fair shake over several weeks.You're finding that your local maximum ofenjoying how you've set up your Neovim shortcutsand your environment and all that stuff stillworks out better for you.That's fantastic.I'd love to see if there are any cool ideas thatyou want to steal from either the things thatyou've explored or from a conversation oranywhere else that you can translate into yourenvironment.Then we can then go steal those ideas backwith extra icing on top,which the Emacs community loves to doon a regular basis.Linkarzu: Yeah, I see that a lot.Stuff porting over either from Emacs to Neovim orfrom Neovim to Emacs, you know?Like, yeah.I think it's cool, you know?I also agree to that, 100%.If Neovim becomes better, Emacs becomes better andthe other way around, right?Sacha: Yeah, absolutely.And you know it's entirely possible that oncepeople get used to using org.nvim and they'relike capturing new notes and tasks from theirmail, because you have notmuch.nvim, and theirfeed reader, because you also have a feed readerinside nvim, and then they're like, oh you know Iwant to do this from my whatever it is and eitheryou'll be able to vibe-code or actually writesuch a plugin,or you'll be like, you know, why don't I just usethat other editor that already has fivedifferent implementations of this?Linkarzu: Yeah, I think that is a good entrypoint into Emacs.The org.nvim plugin, it's goingto help with friction.People that are in Neovim are going to startwondering, okay, what else is Emacs capable of,right?And they will probably finally do the switch.
12:01Not Emacs vs Neovim; it's about what we can imagine
Sacha: And really, it's not so much an Emacsversus Neovim thing, right?It's more about what can we imagine?What can we try out?How can we build our tools to reduce the frictionand make more things possible and more fun?Linkarzu:
Yeah, 100%.
And I have a question for you.Sacha: Yes.
12:20What's the catch?
Linkarzu: I live in the third world, and wehave a saying here, if it sounds too good to betrue, it probably is not.Like, what's the catch with Emacs?Why do you do all this?Like, what do you gain out of all of this workthat you put into Emacs?And why do you do it?Sacha: It tickles my brain.It's just fun.Ever since I started, I've been fascinated by howI can both shape the tools to fit my quirks,fit the strange things that no software company isever going to anticipate,and also, I've always been fascinated by howother people do this too.
13:05Unix Power Tools
Sacha: I came from a third world too, right?I grew up in Manila.And as a 17 or 18 year old student, I was tryingto read through all the computer science books inthe very, very small university library.Well, it's a big university library for thePhilippines, but the computer science section wasonly so big because it's hard to order books whenyou're far away.And one of those books was Unix Power Tools.They had a chapter on Emacs, of course, and oneof the things in it was just this throwawaymention of, oh yeah, if you have plenty of timeto waste, check out M-x doctor.And I was like, what?Back then I was, like, what kind of editor hasthis kind of stuff in it?As I've gotten to know people and theircustomizations and the things that they've builtto support their lives, I've just become more andmore fascinated by what people can do with it.So I do a lot of things for free for Emacsbecause it's a wonderful community.
14:02What is Meta x?
Linkarzu: Okay. What is M-x, by the way?Sacha: M-x is the totally old way of sayingAlt X because apparently there were fancykeyboards where the Alt key was called Meta backthen.And so M-x or Alt X is the way you callcommands by name if you don't remember thekeyboard shortcut or if they don't have akeyboard shortcut in the first place.Linkarzu: Okay.
14:24Searching handwritten sketches: Evernote to ripgrep
Linkarzu: In the video that played at the beginning when Iwas talking aboutwhat I know about you,one of your most popular videos is Evernote.Like, what's up with that?You know?I guess it started before Emacs, right?Sacha: No, no, that was actually in between.So around the time that I was doing a lot ofdrawings, I wanted to figure out if there was away that I could search through my drawings.Evernote at that time had a way to just dump allof your sketches into it. Then it would do theoptical character recognition on handwriting, so Icould theoretically search for text and see whichsketches of mine had that text in it.I find that writing on a tabletor on paper scanned inis a great way to explore a thought non-linearly,because my mind likes to jump all over the place.I wanted to be able to search them.I couldn't figure out how to do the OCR ofhandwriting all the way back then, because thiswas, what, 2018 or something?Actually, older than that.Much, much older than that, I think.So I used Evernote for that.But then, of course, Evernote got worse, likemany subscription services do.I haven't used it in ages.But now I can use either Google Cloud Visionto do the OCR of my handwriting to get the textout of it, or I can use speech recognition tojust blergh all the text in my sketch and then havethe plain text that I can just ripgrep throughor search through.Linkarzu: I'm interested in that.You're going to demo the speech-to-text thing thatyou set up, right?You have something... Oh yeah, I totally can.Okay.
16:03Manila to Toronto
Linkarzu: Now, you mentioned that you grew up in Manila,right?Manila, where do you live right now in case thatyou want to share it?I'm not sure if that's private or...Sacha: I don't know.I live in Toronto now.I came here for my master's and stuck aroundbecause of my husband who is coincidentally aNeovim user.Somehow we managed to get along.Yes, yes.So I'm here in Toronto and I have learned to putup with winter.Linkarzu: Okay.And for how long have you been there in Toronto?Sacha: I moved here in 2005.Actually, my main reason for staying here was theToronto Public Library, which is amazing.And then of course, I metmy husband afterwards.Linkarzu: He's from Canada?Sacha: Yes. He's from Canada.Linkarzu: Okay.
16:50Not about converting people, but about finding the tools that fit you best
Linkarzu: Now with all of this power and influence that youhave, why haven't you turned him to the, to theEmacs side?Is it because Neovim is better?Sacha: No, it's becauseit's not about that, right?It's about figuring out what kind of tool fitsyou really well.For us, for example, he picked up usingLedger from me.I like using the Ledger command line tool, alsoby John Wiegley, to manage my finances.That's something he uses as well, because it'splain text.But he can do that within Vim perfectly fine.
17:23What do I do right now?
Linkarzu: Okay, okay.You went there for your masters.What do you do if you want to share with us?What do you do right now?Sacha: I'm retiring today!I'm wrapping up my consulting contract.So over the past 14 years, I've been on thisfour-year experiment with semi-retirement.Sorry, five-year experiment withsemi-retirement.It got interrupted by parenting, and so it justkept extending, in which the little bit ofconsulting that I was doing was mostly focused oncoming up with web prototypes,doing some data analysis.The data analysis part, my proudest moment wasfiguring out this complicated common tableexpression in PostgreSQL so that you could dothis recursive query andreport by organizational chart.Fancy stuff.Anyway, so this and that, some JavaScript and HTMLand whatever, to basically take whatever crazyideas my client had and see what I could do toautomate it or build it on top of this otherperson's system, the vendor's system, which waspoorly documented but still had a REST API.Anyway, that sort of stuff.
18:38Learning French
Sacha: And then for the rest of the time, just tinkeringaround with Emacs, writing things,learning French.It's not my main language.You can probably tell because of my terriblepronunciation, but that's okay.I'm working on it.And I'm learning that because my daughter islearning that in school.I figured it's good for her to have someone totalk to.So there are many things to keep me busy.
18:57Sitting out the AI churn
Linkarzu: Oh, okay, okay.So you do consulting then, right?Sacha: I did consulting.I am now experimenting with not doing anyconsulting.I'm going to sit out all the AI churn, you know,you all figure that out.Then I will check back in next year, a coupleof years, when maybe never, and see if there'sanything else I can help with.In the meantime, I can just hack on the thingsthat I want to hack on.Linkarzu: Okay.What are you going to do?YouTube?Emacs?What's the plan?Sacha: Have fun?We like biking.We like going outside.I like writing and drawing.I can always find things to tinker with in Emacs.Coming up with ideas for things to do is neverreally a problem.It's more like...Trying to finish enough of those things so I canpost them and make them something that otherpeople might be able to learn from or build on,right?Linkarzu: Yeah, yeah, okay.I had something for you.I had a question for you.I just...Hold on, let me think.Let me think.Oh, you were talking about something.Oh, but before we continue, Sacha, just...
20:10Thanking YouTube members
Linkarzu: Let me thank my YouTube members.The CEOs are shown on the screen.@web2three, https://web2three.com,Ted, https://cpnllc.com,and also Gallo.These are the three CEOs.And I just want to thank the rest of the YouTubemembers, which are the ones that are shown here.So just appreciate each one of the members forsupporting the channel.Okay, so back to this here.That's just my little announcement that I dothere on every live stream.Sacha: All right.So, what do you want to go into?Linkarzu: Yeah.I want to take a look at what you were sharing amoment ago.The speech-to-text,and it happens in real time, right?Sacha: All right.
21:00Backstory of real-time speech-to-text transcript
Sacha: Like many things in Emacs, this came out of abunch of small seemingly unrelated hacks.One was I was having this conversation with aFrench tutor and I was like, man, I am having ahard time making sure... Like, he would switch intolecture mode and I was trying to have moreconversation modeso I could practice some more, right?So I think I've got this right.Hang on a second... Stats...Linkarzu: You let me knowwhen I switch to your screen.Sacha: Okay. Feel free to share my screen.Linkarzu: Okay. Let's see here. There we go.Sacha: Anyway, so I had a large language modelgenerate a Python server because sometimes I'mlike, I just want a proof of concept for this.I don't necessarily know all the details of howto work with the Silero voice activitydetection thing.Anyway, so make me a server that can listen to myaudio devices in real time without messing up myactual microphone use if I'm on stream.Okay, so I have this Python server, and then Iwanted to visualize it within Emacs.Okay, I got that visualizing inside Emacs.And then I wanted to say, all right, you know,why don't I have speech recognition listening onthis thing so that if I say something, it cancheck it off.Like playing Bingo, you know, if I say it, thenit gives me a note that I've actually said thatword or phrase.And so it could show me a list of the words orphrases that I still want to say in conversation.And then as we were preparing for thisconversation, I was like, you know what would bereally cool is if I could have that speechrecognition, which is running anyway, give me atranscript of the current conversation in a waythat I could then clip if I wanted to, becausesometimes I'll be doing a live streamand I'd be like,You know, that was a really cool demo and Ishould grab that so that I can just grab thescreenshot or put it into a blog post or just geta text or extract a clip out of it, turn it intoa video, right?And so now I have this thing where,hang on a second.If I'm somewhere else, for example, it's easy forme to get to.
23:15Demo: real-time transcript and instant replay in Emacs
Sacha: I have a voice command for it.Prepare to fast forward.Which is of course a [Spaceballs] reference.It'll show me then this thing.And if we are... Let me load this real quick.And it will show me the video that we have, or atleast the recording that I've got of it.That's on the right side here.This MPV is controlled by Emacs.And if I go here.Let's say this one.Sacha: So here's your waveform for it.If I play it back, which is of course theSpaceball reference,it will show me then this thing.I can copy the text if I wanted to, or I canextract this into its own video.
24:03Demo: Video editing witin Emacs
Sacha: Of course I do my editing within Emacs.This is what my video editinglooks like within Emacs.I can just copy and paste the subtitle cuesthat I want.I tell it where I want the output to be.I tell it what audio that I want to use in caseI've replaced it.Here, for example, I'm using the same audio aswell as the video from a previous live streamrecorded session.and then I can just reorder things, clip things,change the timestamps here.If I want to cut fromthere onwards, sample bits of it and so on,it's something I can do.Linkarzu: Can you remove words as well or justentire sentences?Sacha: One way to do that is to split atthat point.So for example, if I want to remove definitely,let's load this one so we can see where I am.I can follow along here.So this is my livestream from yesterday."My bottleneck is definitely thinking speed."So here's definitely, right?This is the start of "definitely thinking speed".If I split here,and I split at the end of "thinking speed", Ican say let's skip this.And I think, assuming I haven't broken..."My bottleneck is thinking speed."Linkarzu: Okay. Yeah.Just a little tail there, but I get it.Sacha: Yeah.It's just a matter ofchoosing the right timestamp."Thinking speed."Linkarzu: So that is pretty interestingand based.So you do all of your videos in Emacs,edit all of them there.Sacha: Yeah, pretty much.Well, it started because in 2020, I think,I wanted to make a presentation for EmacsConf.And I wanted to record my audio and then just addthe visuals on top of it.But I don't like clicking the mouse because I'mon a Lenovo ThinkPad.The tiny little pointer is handy, but...if I use a separate mouse, it's annoying.Anyway. Clicking is annoying, especially sinceI'm so used to being able to automate everythingwith keyboard shortcuts.So I said, why don't I use Emacs forvideo editing and audio editing?So what I can do thenis I can take my audio recording... Or actually,I can record from Emacs itselfand then I can edit itto remove the oopses and the false startsand change all the things that I want to.Then I can add the images or videosthat I want for each clipand it will automatically speed upor slow down the video to fit theduration of the audio.So if I'm saying, you know, this is a quickpreview of the conference schedule, et cetera,then it speeds up the scrollingautomatically so I don't have to worry aboutdoing it fast enough if I want to.I can also tell it to play this original speed.That has just grown since then.Every time I want to do something else...Like now I'm thinking,you know what would be really coolis if I could add a title card very easily, right?So let me go to, let me start a new thing here.WebVTT.So if I want to say, I want to add a titlecard that says "Hello world", right?Oh, I need to actually save it.Let's save it as, hang on a sec.Breathe, Sacha.It's okay.test.vtt.Yeah, that one.Okay.All right.So now I have a test.
27:51Demo: generating video title cards from WebVTT in Emacs
Sacha: If I say Hello World, then it just callsImageMagick to create this file here.If I open that file,it's super simple.Sacha: It's just white text and a black background usingmy custom font, because I went to the trouble offiguring out how to make a font with myhandwriting and then kern it using Org Mode.Because why not?Linkarzu: Okay.Sacha: That's ready.Then I can pop that in and I can say, youknow, this is a test here.Let’s do a subed-record here."This is a test here."And then that audio here,it becomes a whole thing.If I compile this,I think it will just make a thing.Let's put it into#+OUTPUT: test.webm, sure.All right.mpv test.webm"This is a test here."Linkarzu: I see. Pretty cool.Have you seen...
29:06fframes, generative AI
Sacha: If I can figure outhow to get fframes,that thing that you're using...If I can figure out how to get that working,that might be interesting too.Although, I have to say,I'm very attached to my stick figuresand the way that I like to draw things.And I have tried using generative models to say,"Okay, here's my sketch.Can you animate this SVG, movethings around or whatever."It keeps trying to rewrite things to plain text.I'm like, no, I want my actual paths.So I have to fiddle with it some more.In the meantime, however,I can animate things myself,thank you very much.Like, for example, this... animate...let's go with that one.
29:49Demo: Stepping through SVGs in Emacs
Sacha: "Some things that work really well."This is within Emacs again.I can just highlight the different things.
29:56I draw on an iPad with Noteful
Linkarzu: How do you do your drawings,by the way?Like, do you have like a tablet or something?Sacha: Oh yeah, yeah.I use Noteful on an iPad.I used to draw using Krita on a Lenovo X230tablet PC, but at the moment I can just use theiPad with Noteful.Currently I'm drawing in dark mode because youhave a dark mode channel,so I have things like my sketches.
30:20Light themes and dark themes
Linkarzu: Emacs users like toflashbang people, right?They like the white?Is it to tell apart from the Neovimcommunity?What's up with the white themes?Sacha: I mean, I tend to use dark mode now mostof the time too, because I figure most peoplewill be watching my stream when late at nightthey're just relaxing or whatever.But I guess different people workwith different setups.I do like the way that with modus-themes andall the other themes in Emacs, it's super easy topick one of these darkthemes and just go with it.Linkarzu: Okay.
30:58Art and stick figures
Linkarzu: So you do it on the iPad then.I have to convince my wife that we need an iPadfor the family.I don't know how to draw.I'm pretty bad.Sacha: I don't really know how to draw either,but somehow people have paid me to do so.I don't know what's going on there.But yeah, I'm really only drawing stick figures.It's fine.Linkarzu: Okay.Yeah, no, my drawing is like, you have no idea.I lack creativity when it comes to that.My wife paints.She paints pretty good stuff.My daughter paints.Well, these are my daughter's paintings, the onesthat are back here.I guess you cannot see them.You can see them there in thebackground.She's learning, you know, but me... Nice!Sacha: I see very vivid colors.Linkarzu: Yeah, she loves rainbows and a lot ofvivid colors, but I can't.I'm pretty bad at it.Now, how do you do thisspeech-recognition thingbecause I do something similar.
31:58Linkarzu's video editing workflow
Linkarzu: Let me tell you a bit about what I do with myvideo workflow.I got tired of editing videos.I'm done.Like, it is a time consuming process coming upwith clips.You've been there, right?You have been doing that for quite a long time,way longer than me, right?I just got tired and I was like, I don't want todo this anymore.Never, ever, ever again.I need to come up with a method that allows me tocreate clips out of a live stream.All of it, AI.AI did all of it for me.It hasn't been easy.It's been months.I've been stuck for months.But I don't do stuff real time the way thatyou're doing right now.The transcription happens when we're done withthe call and the process starts automatically anda few hours after I have the clips in mydashboard that I can upload if I want, right?But it's not real time.I saw how you did it onyour side and it's pretty accurate.Like, what's the magic?What are you doing there?You're sending it somewhere, right?
33:05Local CPU-only speech recognition in Emacs
Sacha: No, no, this is an entirely local modeland I'm running it in a computerwith hardly any GPU.So I'm actually just making it use CPU only forthe speech recognition because I have a LenovoP52 from 2018, I think.So it doesn't do this GPU thing very well.I think you're usually on a Mac, so you'respoiled for choices there.But speech recognition has gotten really goodover the last few years.And I'm using Orukeet at the moment.It's a multilingual model, so it can handle mypoor French pronunciations just as fine as myEnglish ones.I can press my keyboard shortcut.For example, I can say, "This is a test duringa chat with Linkarzu."Then, because I've modified my dictionary,your channel name actually shows up properly."We are talking about speech-to-text, dictation,all sorts of other things like that."Where's my phone?It's pretty accurate.Linkarzu: It's extremely accurate.What's the name you said?Because I use Wispr Flow, a local model as well.I do the transcript locally on my Mac.Wispr Flow is what the AI decided to use.And I'm like, oh, it gets it. But not...Sacha: Yeah.The model I'm currently using is called Orukeet.I think it's based on Parakeet, but it's beentweaked for more performance and less memory.But there are so many new models coming out.It's really quite interesting because every dayor every other day, there's like, oh, try thisnew model.I'm not good enough to keep on top of all thosechanges. If I can manage to get the AI to generateme a server that has an OpenAI interface, Ican plop it right into my Emacs setup and it willjust work.So I'm happy with that.This is fast enough thatI can use it to think.Then, when I have the text, I can rearrange ithowever I'd like.For example, "prepare to fast forward."We're here.
35:15Sharing speech commands
Linkarzu: I have a similar command.Let me see if it works.Hold on.You see my terminal right now, right?Yes.I don't know if you can see my screen.Let me see if it works.Hold on.Virgin mode off.Sacha: Yeah, yeah, yeah.I saw that.Linkarzu: It didn't work.It changes my wallpaper and it changes my...Sacha: I have one too.Laser.Look. I changed my cursor to a laserso I can say,here's my mouse cursor, right?Linkarzu: Okay, pretty cool.Sacha: Yeah.So the nice thing about it...Oh, it's still listening.Let me just turn off laser mode here.Laser. Laser. Laser.Linkarzu: It happens to me as well.Sacha: Edit retry.Laser.Okay, there you go.
36:14Speech commands and more shortcuts
Sacha: The nice thing about speech commands is thatit opens up a whole lot of other ways to runcommands without necessarily having to memorizekeyboard shortcuts.
36:23Having fun with speech commands
Sacha: You can have more fun with it too.So for example, now I have this command.It turns out I cannot say "start streaming"reliably.Sometimes I say "start screaming."I know sometimes I accidentally say that,and sometimes that's whatthe speech recognition hears.So instead my command for starting to stream(because of course Emacs controls my OBS throughwebsockets) is "let's go live".My command for stopping the streamis "over and out",just because it's a little more fun to say.It becomes easier for me to remember what I needto say in order to make this happen.Linkarzu: Yeah, yeah.
37:01Dictionaries for correcting speech recognition
Linkarzu: Now, why is it accurate?And how do you do, you have a dictionary ofthe difficult words?Like if I say Neovim, it doesn't get it.It says Neovim, or it just puts whatever itwants, whenever it wants.Sometimes it puts something random out there.What do you do for yours?Sacha: Neovim is okay, I guess.Let's see if it does a thing.Yeah, it got that without my needing to correctthings.Let's make a harder word.Anti-disestablishmentarianism.Close enough.Okay, you know, that requires very little editing.What is a difficult word?Oh, actually, let's make a different name.Sometimes it gets Prot's name wrong.Prot does a lot of videos on Emacs.Oh no, it got him too.Linkarzu: Yeah, yeah, yeah.Sacha: If there was a thing that is difficultto say...heisenbug is making org.nvim.That one.Okay, here we go.heisenbug is making org.nvim.I mean, it's getting him, so...Linkarzu: Okay, yeah, yeah, yeah.I get it.So, it's a local model.Interesting.
38:19Demo: Adding a misrecognized term to my common edits dictionary
Linkarzu: Do you feed it a dictionary at some point?Sacha: It is a local model.If I do need to change things...Let's sayfor example, okay, "Orgzly Revived".It probably can't get that one."I use Orgzly Revived on my phone to check my tasks."Oh yeah, so here, right?It says "Orgsley Revived."What is that even?Sacha: But I can press C-; and it usessubstitute. "Orgzly Revived" here.What that does, in addition to thesubstitutions that happen throughout the buffer,so for example, if I'm adding a transcript, itwill change that all throughout the transcript.I've also added some code (because this is Emacs,I can just do that)that saves this in common errors or commonlymisrecognized words.And then when I recognize text, whether it'sbefore a voice command or if I'm just doing thisor whatever, it will applyall of those common fixes, common edits.Here we go.Sacha: So it automatically adds it to this thing herewhich gets saved automatically for me.And that's the change that I've just added.Linkarzu: Interesting. Pretty cool.Sacha: It's really nice to have that kind of[dictionary] shortcut built into the way that I editthings.All I know is I just hit C-; andput in what it's supposed to be.Then it gets not only fixed in my currentfile, but in the future as well.Linkarzu: Yeah, I need to work onmy transcript game.Local models?Sacha: Oh, yeah, it's so cool.
39:57Turning video transcripts into searchable blog posts
Sacha: One of the reasons why I love transcripts is thatit's so much easier to search, right?Whenever I do an interview or whatever,the fun part for me isactually going over the transcript and turning itinto something that I can use.Let me show you proj/yay-emacs/index.org.So these are, for example, my recent chats.In order to include the transcript in my blogpost about this, all I need to say is captions,and I give it the link to the transcript.And that turns into...Hang on a second.Let me...I should have a C-. b here.Was it this one?Okay, so that turns intothis kind of blog post. Right?You have the embedded video.You have the chapters.Clicking on the chapter link will advance to that.Sacha: And then you have the whole transcript right here.with screenshots.So then people who don't want to listen throughan entire hour-long video can just jump to theinteresting parts themselves.I think these ones also go back to the videoup there, which someday I'm going to figure outhow to make a nice sticky display for.Clearly I need to fix that because it's notactually on that thing over there.Theoretically, if I'm publishing it right, thistime I think I've been trying to host the videoson the Internet Archive so people you have towatch on YouTube if they don't want to.But yes, so the idea there is the video gives youso much text that you can then share.Linkarzu: Yeah. Or edit a video way faster.Before I came up with this automated thing forcreating my clips, I used to do that in DaVinci,you know, because DaVinci has like a transcriptfeature and you can...but I'd just rather not touch it at all.
42:07Demo: Saving and cropping screenshots from a video at a timestamp
Sacha: Screenshots are super easy for me to do becauseEmacs, of course.So like, for example, if I wanted here,If I wanted to take a different screenshot here,I can say save screenshot.And then it takes the screenshotand it does that thing.If I wanted to crop it, I can say crop tothis and crop to that.Linkarzu: Oh, okay.Sacha: Actually I can say,just crop the screenshot.If I say screenshot crop,it will crop the screenshot.If I just say crop,it will crop the whole video to that section.Linkarzu: Oh, I see. I see what you're saying.That specific segment only, right?Sacha: Yeah. So text as a medium for video editing,it's actually really powerful.Linkarzu: Yeah, exactly.Yep, 100% agree.What other tricks do you want to show us there?What other things do you want to brag about yourEmacs workflow?Sacha: It's really hard to say.I told you how doctor was the beginning of my ofmy experience.Yes, yes.
43:22Demo: M-x doctor with speech in and out
Sacha: So this is a doctor.You can say things like, I'm feeling happy today,as I said, right?And then it uses that to generate its response.But I have this conversation mode.Yes, especially when I get to tinker with Emacs.And then... What happened when you get to tinkerwith Emacs?Does the TTS come through?Yeah, it does.The reason I did this was because after Ishowed off my vocal commands to Emacs Paris, theywere like, oh, you know, what if you can have aconversation?So I was like, okay, let's come back full circleto the thing that got me curious about Emacs inthe first place and voice enable that.And then, of course, this is a very old AI.
44:09Demo: speech-enabled conversation with a large language model in French
Sacha: It's much more interesting if you apply it to thenew models.So if I want to, for example, practice French.This is one of my voice commands here.Computer: "I would appreciate it if you would continue."Okay, here we are.Then I might say, "Je parle avec Linkarzu."And then I think, there you go.It will eventually, maybe?C'est toujours enrichissant de partager, toucherses passions en direct.Quels sujets abordez-vous ensemble aujourd'hui ?Sacha: All right.So this now uses a large language model.This one uses Gemini because gptel makes it supereasy to plug in different models.So, if I wanted to practice French but I don'tnecessarily want to bug people because I take awhile to think about my words and sometimes I'mlooking stuff up, then at least I can simulatethat kind of conversation.There are gazillion apps that will sell you asubscription to do this.But because it's here and it's plain text and Ican control it, then I can more easily go throughit afterwards and analyze the vocabulary orcorrect the things that it didn't get right,which I know is because of the speech recognitionand not because of me or whatever.So it's so much fun to be able to tinker withEmacs to do what I wanted to do.Linkarzu: Yeah, pretty powerful.
45:46Sacha's Linux setup: XFCE, X11 and Emacs on top
Linkarzu: I have a question about your operating system.What did you go with?I see this is Linux.I would assume it's Linux, right?Of course, it must be Linux.Sacha: This is Linux.I used to be in Kubuntu because I liked KDE, butwhen I recently upgraded to the latest version ofUbuntu, then I broke everything. I waslike, okay, this Wayland stuff is getting in theway of the automation that I like doing.So I'm back to X11. I was like, hey, let's trysomething a little more lightweight.This is the XFCE Window Manager at the moment.I'm still settling into it.There are lots of little things that I'm learninghow to configure, but yeah, just Linux.Linkarzu: Okay, Linux, Kubuntu.Why did you decide to go with that distro?Is there a specific reason or no?Sacha: No, it's more like, well, my husbandhappened to have an install USB lying around.I used to be on Debian unstable,and then I switched to Ubuntu. It's just oneof those things that I haven't bothered enoughabout to change, on account of changing requiresme to go reinstall a lot of thingsand then get my Emacs working the waythat I want it to do again.Sacha: At the moment, Linux is the library that Emacsuses to talk to my hardware.Linkarzu: Yeah, it's just the base, right?You just want something where you can run Emacson, right?Sacha: Yes.
47:18Automating other apps with Emacs; X11 versus Wayland for automation
Sacha: I want something where I can let Emacscontrol as much of my computer as possible.So for example, when I set up the BigBlueButtonweb conference rooms for EmacsConf, I do that inFirefox because I can have Emacs control Firefoxso that it can click through the differentscreens and set all the moderator access codes orwhatever else and confirm the settings.I don't have to do that kind of clicking andtyping myself.Linkarzu: Okay.And you mentioned that you switched back to X11because Wayland was getting in the way.Can you share a little bit more about that?I'm not a Linux user.I'm a Mac OS user.But I know about the words that you're mentioningbecause all of the guests that I bring over talkabout it and I'm like, okay, so they're like, okay, got it.So what happened there?Sacha: I think it's just a different securitymodel, right?So for Wayland, which is one of thelike the window system compositors(?),how stuff shows up on your screen.The way that it's conceptualized is everyapplication is sandboxed, so it shouldn't be ableto mess around with other applications' windows,which is more secure. Great.But I actually do want my Emacs to mess aroundwith other people's windows,and I want things to be...Linkarzu: Emacs is the king, right?Emacs has to be able to do anythingit wants, right?Sacha: Yeah, yeah.And I want that to happen withouta gazillion prompts."Are you sure you want to let..."Yes, I want it to do the typingso that I don't have to do the typing.So yeah, it's pretty good.Maybe someday X11 will totally shut down and thenI'll have to move.But I can give people a few more years orwhatever to figure out the automation andaccessibility parts that I would need in order todo that kind of automation.Linkarzu: Okay, okay.So you're in Kubuntu, you're on X11.
49:15Just a regular laptop keyboard
Linkarzu: Then I also wanted to ask you, do you use likeone of these special keyboards that all the coolkids use or are you just a normal keyboard?Sacha: I'm just on my laptop.Yeah. I'm going to tilt the screen downand mess things up.See, that's it.That's the thing.Linkarzu: The laptop keyboard.Has it ever caused any issues for you?Like hand pain, wrist pain or something like that?Sacha: No, the only wrist pain I got was fromlifting my daughter the wrong way, which gave methe de Quervain's tenosynovitis.Do not pick up your kids withoutpaying attention to how you do it.Linkarzu: Because you hold themlike this, right?That happened to my wife as well.Sacha: Yeah, yeah, yeah.
49:56Using Emacspeak to deal with laptop screen failure
Sacha: But I've been interested in accessibilityfor a while.Also partially because of...So, okay, so here's what happened.In about third or fourth year ofuniversity, I was also on a laptop, and myscreen stopped working occasionally. It wouldglitch out, and I could not see.I mean, it would be super low contrast, and youcould see things if you look at it from the rightangle, but it would just glitch out like that.I could plug in an external monitor, sure,but I can't plug in an external monitorin the middle of class.I'm not going to lug a monitor around, right?And while I did experiment with a head-mounteddisplay as part of my curiosity into wearablecomputing, that is also fairly obviousand large and awkward.You know that smart glasses video that you had?Okay, now they can pass off as glasses, right?But back then it was a huge Borg-like thing.But fortunately, Emacs has Emacspeak.So I was using speech synthesis to code and totake notes and do all those other things, whichwas fantastic.It actually worked.It worked so well that I was like, you know what?I could use this to listen to manuals and writenotes and look up things while I'm walking aroundbecause all I need is a portable keyboard.They make one-handed keyboards.And a pair of earphones, and nobody can tell thatI'm actually reading, well, having read to mesome kind of... This is me goingthrough the Emacs manualwhile walking around, right?Anyway, so, accessibility.The text-to-speech part is very interestingand there are a lot of people who are using Emacseven if they're blind or visually-impaired.all this stuff is actually surprisingly usefulalso for people who aren't.
51:45Anticipating physical and cognitive challenges
Sacha: The speech recognition stuffI think will be very helpfulin case something happens to my hands.People have accidents or people get sick.My mom has Parkinson's, so her tremors make itreally hard for her to type or tap things.It's amazing that we can keep modifying thetools to make this kind of speech input,voice commands a lot easier.There's also physical decline and there'scognitive decline.I've had to deal with brain fog as part of thesleep deprivation of early parenting.So I know my ability to memorize things and myability to type while I'm talking is nowhere nearwhat I would have in an ideal world.It's great to be able to modify mycomputing environment to adapt to these changesand to the changes that I anticipate.
52:37Describing Emacspeak
Linkarzu: Oh, and you mentioned Emacspeak.So what is it then?It's just accessibility option in Emacs thatreads your screen or something?Sacha: It can read a whole thing.It can read character by character to give youaudio feedback.You can go by word or by sentence as you'renavigating around the text.I don't have it set up in this one, but I think Ihave a different profile where I have it set up,because by default, when you launch it,it reads all of Emacs to you.It doesn't just say the text.It will also give you auditory icons.So for example, if something is bold, you canhave it say it in a different voice.If something is italicized, you can have it sayit in a higher pitch or something else.If there are icons, it can play sounds instead ofthose icons.So it gives people who are blindthis much richer experience.I love the fact that Emacs has these thingswith all these different kinds of needs.I've used it.It's great.I found that when I was using it, I could listento things at 300 words a minute or even higher,and I would still understand.
53:51Linkarzu adjusts his electric sitting/standing desk
Linkarzu: I'm sorry.It's just my desk going down because I try to...Sacha: I thought you were like genuflecting infront of Emacs is awesome.Linkarzu: Yeah, I just try to, you know, adoptdifferent positions throughout the day.There's a question.Oh yeah, for sure.You do it as well?
54:13Parenting and fragmented time
Linkarzu: How long do you spend sitting?Like, do you spend sitting most of the day?Sacha: Most of the time, actually, I have to gohelp out the kiddo, right?So that keeps me away from the computer, which isone of the reasons why I've had to learn how todeal with very fragmented time.Not much time sitting in front of the computer tofocus on something.Everything that I want to do has to be somethingthat I can do in 5, 10, 15 minutes,maybe 30 minutes.At the moment, she's fairly independent, but Ican still be interrupted by somebody who needsbreakfast or somebody who needs a hug andwhatever.So I need to be able to capture a thought, get itout of the way and work on things in tiny chunks.Linkarzu: I've seen my wife, how she handlesthings and I'm like, man...It's a lot of mental overhead.That's a whole other conversation.Men are the ones that make progress, build theirprofessional careers,and wives usually take care of the kids.It's a huge sacrifice.What do you think?How do you feel about all that?Sacha: I signed up for it totally knowing whatI was getting into.I was like, okay, there's a lot involved in beingthe default parent, right?The primary person who patches up [owies]...But I think partly because of Org Mode and Emacs,it's been very helpful to meto be able to say, "Yeah, that's a great idea.I'm going to write that down in my phone or on mycomputer. Usually on my phone back in earlyparenting days, because I'm away from my computer,I'm at the playground or whatever else, right?Sacha: So the fact that I can capture a note into OrgMode and then know that I can get back to it inseveral years when I have more free time or thatOrg Mode can keep track of the deadlines thatmight be a year from now, like when I need torenew my passport and just have that surface thereminder at the right time with the notes onexactly what I need to do and the links towherever I need to go.I think that has been very, very helpful for me.
56:20Capturing tasks from the phone with Orgzly Revived
Linkarzu: Good point that you brought upbecause I don't know quite a few days ago I justcame up well the clanker came up with a method wecould say of me just tapping it saying somethingto it and when I come to my computer the task isthere in Neovim?Yes.Right?Yes.Because I forget I'm outside I'm like okay I haveto remember this I have to I'm not going to rememberit it's just going to go away so I do it on thephone shows up in Neovimand it's a huge benefit.How do you do it?Sacha: For my phone, I often just put it intoOrgzly Revived, sometimes with dictation,sometimes just typing in a few keywords to helpme remember.A lot of times it's, you know, if I'm comingacross Orgzly Revived.Linkarzu: What is it?Sacha: O-G-R-Z-L-Y.Linkarzu: If you want to show it, I don't know.Sacha: It's on my phone.Is it like an app?Linkarzu: Yeah.Sacha: So I use an Android phone.And now there are actually a gazillion apps thattalk Org Mode and can synchronize with your filesand whatever.I'm currently still just using Orgly Revived asan app on my phone with Syncthing.Syncthing being the free synchronization that Ican use to just get my files back and forth.But there are lots of other alternatives, so youcan find something that fits you well.I am very tempted to figure out, finally, how touse Emacs native, because there's an Emacs boardfor Android.Especially if I can get my speech synthesis andspeech recognition working on it, I think thatwould be amazing to have this kind ofconversation with Emacs along the way.And then on the computer, if I need to capturesomething quickly, ooh, I can show you this one.Let's see.Remind me to put together a quick description ofmy to-do workflow.And it will take that note and it will stick itinto an inbox.Here I'm in live mode so my public tasks don'tget mixed in with my private tasks.If I do an agenda it should just be limited tothe public stuff.Anyway, so the Remind Me created the note for me.It includes the audio in case the speechrecognition didn't get anything right.And it includes a screenshot so that in case I'mdoing this from a different application becausemy keyboard shortcut is global, I can still seethe context and go find that sort of stuff.Linkarzu: Quite interesting, quite advanced,quite complex.It must have taken you months, years to set allof this up.Sacha: As I said, I can only work in like 30minutes or so.So this one, I think was a 10, 15 minute hack.A little bits of time.This link here to whatever I was looking at, yougot that free from Org Mode.This thing here with the recording, I got thatbecause I already wanted to save my recordingsanyway.So it was just a matter of adding a link to thefile name that I already had saved.This screenshot, you know, another five minutesbecause I already had Spectacle capturing thescreenshot of both screens.So it's fairly simple.It will even work for things like, remind me nextmonth to follow up with a linker to see whetherhe's switched to Emacs.Linkarzu: I mean your entire Emacs config.Has it taken you years to adapt it?Sacha: It just keeps growing, right?I have an idea.It takes me 5, 15, 10 minutes to put it in andthen it just accretes.I can't make very big changes becauseIf I try to work on something big and then I getinterrupted, the old chunking effect makes mevery grumpy.I have an unfinished task and it's like naggingat me.So it has to be always small chunks, but Emacsmakes it super easy to work in those tiny, tinychunks.So this one here, for example, it says, remind menext month, et cetera, et cetera.And it has scheduled it for next month.Linkarzu: That's pretty cool.That's pretty cool.Sacha: I'm not actually going to pressure youto switch to Emacs.You can keep using Neovim and just borrow all theinteresting ideas.Linkarzu: Org that NVim is there.I can adapt it to do this, you know.There's a question in chat.I ignore chat unless they bring the coin, okay?But there is one question that I will allow thispeasant in.in our conversation.frazzor749 is asking, we were talking about windowmanagers.What about EXWM when we were talking aboutWayland and all that stuff?Sacha: There are window managers written inEmacs Lisp and people who use EXWM really like it.There's actually even an EWM as well right now, Ithink.I remember playing around with it a couple oftimes.Maybe I should check it out again.The first time that I played around with it, mylaptop was even older than this one and not ascapable.So maybe now that I am on a 2018 laptop, it mightwork better for me.Because it is nice to be able to...It would be great to be able to just press C-xB and switch to another application just like Iswitched to other buffers.Linkarzu: Yep, yep, yep, yep.And moving on to other topics.
1:01:55Sacha Chua on AI and the Emacs community
Linkarzu: How do you feel about all of this new world thatwe're in, AI-related stuff?I've seen that it's evolving quite fast andsoftware is changing.The way that we look at software is changing.It used to be something that only a small set ofpeople could do in the past.Now, people like me or anyone else, even frazzor749,can buy solutions and have them up in minutes.Does that make you feel something?Does that make you feel sad, happy?How do you feel about all this?Sacha: I love seeing the comments from peoplewho were like, you know, I had a hard timegetting started with Emacs before, but now I canask an LLM to generate me something or explain tome, explain something to me and I can get itworking.So that's great.But I also like the fact that the people who donot want any of this stuff to happen to theirparticular Emacs can get together and find eachother and say, you know what, if Emacs adopts AImore than I'm comfortable with, I commit tofiguring things out the human way and continue tobuild it without that.So I love the fact that there are choices andpeople can make a choice that fits them.Linkarzu: Okay.Sacha: It's very exciting to see the ideas thatpeople come up with.Linkarzu: Okay.And you also have the choice?Sacha: Yeah.Sometimes it's hard to read the code because it'ssuper long and like, you have that example fromsomeone on your chat, right?And it's like, okay, you can generate a 3000 linemodule, but really you might only need 200 andthat 200 is a lot easier to read.Maybe it was 300.Okay.I'm, you know, touching it right.Linkarzu: No, he said 3000.He said 3000.Sacha: No, no, no.That's like his final for going from 3000 tosomething like 200 to 300 lines.Because if you know what else is out there andhow you can hook into it, then it doesn't have tobuild everything from scratch.Linkarzu: And we also have Prot as an optionbecause he does his coaching sessions.You can learn about Emacs with Prot.We're going to put his link somewhere in here.I always leave the link in all of my videodescriptions and all that stuff.How do you feel about AI?What do you think is going to happen in thefuture?Have you ever thought about it?How do you see software?Sacha: I have no idea what's going to happen inthe near future.I know there's a lot of froth and a lot of likeweird pressure from other people to use it or notuse it or whatever.And I think the business world has sort of tieditself in knots.We're just going to have to see how all of thatstuff works out.In terms of using it myself, I use a very limitedset of functionalities.Just the things that I can understand, again, 5to 15, 30 minutes at a time.I am not at the point where I can just trust theagent to run by itself and, you know, come back.tomorrow with results.I'm more like, okay, I'm trying to learn French.I don't need anything fancy.I just need to know how to make my paragraph lessgrammatically terrible.And the nice thing about large language models isthat it tries to bring it towards that average,right?And it can explain things or at least there are anumber of times that even with my beginner level,I can say, you know, that doesn't seem right.And then I can go and dig up the actualexplanation and compare it against human examplesand ask my tutor whom I meet twice a week and payactual money to.But the fact that I can do this at 11 p.m. orwhenever I'm free is helpful when it comes togetting that kind of instant feedback.And I use it for troubleshooting sometimesbecause I'm like, I don't know what this errormessage is.And doing the exact search is not going to get methe right answer.But how would I start to understand this?What questions would I ask?So, okay, that's sort of helpful there.And for proofs of concept where, you know,I do not know enough about FFmpeg or Matplotlibor Python's internals to do some of the thingsthat I want to do off the top of my head.So I'm like, okay, I will see.If I tried myself and I'm not getting anywhere,then I can say, okay, generate me a thing.And then I can try to see, okay, how is it doingthat?Why is it doing that?And build on that.I like to keep things within one or two screensthat I can actually read.Linkarzu: Okay, okay, okay.Yep, I started like that as well.I didn't trust it too much at the beginning, butit's been progressing and I think you just getmore dependent on it, you know, and more and moreand more and, you know, but it's gotten better.Do you use a specific model?Sacha: I use Gemini when I feel like it.The usage levels are quite high in the free tier.Sometimes I'll use Claude as well.My daughter has a subscription to Claude.We pay for a subscription to Claude for mydaughter because she likes generating interactivestories to help her learn French vocabulary.Linkarzu: Okay, okay.Sacha: Which is great because then it canphrase everything in Pokémon terms.So that's easier for her to pay attention to.And so when she's asleep or when she's watchingPokémon, then I can use it without stepping onher budget.Stepping on her usage, right?
1:08:03Kids' obsessions: Harry Potter, Pokemon, Wings of Fire
Linkarzu: Is she into Harry Potter, yourdaughter, or not at all?Sacha: Only briefly.Pokemon remains her current main interest.She is the sort of person who reads the entirewiki and stuffs all of the factoids into herbrain.Linkarzu: Oh, my daughter is in her HarryPotter face right now.My wife bought all the books.I don't know, it's seven books, I think.Right?And the condition is, once you read a book, wecan watch the movie, right?She's written like four, I think, and they'rehuge books and I'm like, man, that's tough.But she's in that phase right now.Sacha: Yeah.The novel series that my daughter is currentlylooping over is Wings of Fire.She's been into it before.So now we've checked out all the books from thelibrary.I love the library.It saves me so much money.And so she's reading that.There are graphic novels as well.So it's great because then, you know, it makes itlike graphic novels for the first seven or sobooks.And I've also checked out the graphic novels inFrench.I get to read them and learn things.And she's reading it.And because we're so familiar with the story, Ilike to learn about the things that my daughteris interested in.So for example, when she was into Rubik's Cubes,I learned how to cube too.And I played around with using Org Mode to helpme learn cubing.And she's into Minecraft.We play Minecraft together.And now she's into Wings of Fire and we read thattogether too.Linkarzu: Okay.Okay.Now you mentioned that you use Claude sometimes.How do you feel about it?Have you tried the latest Opus 5.5 one?And if you compare it to other models, do youfeel a difference?Do you feel that it's like really smart or it'slike, nah, what do you think?Sacha: Well, let me give an example ofsomething I was playing around with recently.So I mentioned that I use Noteful on my iPad tosketch.One of the things that I've always been curiousabout is what can I do with the stroke data sinceit's recorded it somewhere.I came across another person's repository wherethey had used a large language model, probablyClaude even, to reverse engineer the format inorder to create something where you give it yournotebookyou know your handwritten notebook and it willgenerate quizzes and summaries and etc etc andthen put that back in the notebook so it lookshandwritten so that you can do the quiz on theiPad with a pencil and all that and therepository had a description of the reverseengineered format so on my phone I said hey youknow can you take a look at this repositoryand see if you can use the reverse engineeredformat in order to take the notebook and createan SVG using centerline strokes instead of fills.The way that the export usually works is youexport a PDF, but when you get it into an SVGeditor, it's all like paths with fills in them.But I want strokes so that if I want to animateit, I can animate it in the direction I wasdrawing.instead of the character just appearing.And it did that and I gave it some files and itactually showed me also some videos of itanimating so that I could visually verify that itwas doing what I wanted it to do.So theoretically, there are some Python scriptsthat I might try out later on.I like the fact that I can do that sort of thingfrom my phone to try things out.At the same time, also, it's fine for the thingsthat I don't know much about.But for Emacs, it's like Pod said, this is thefun part.For him, it's like it's his video game, right?So for the stuff that I like to hack...I want to actually go do the digging around andcome across the things that I wouldn't have eventhought to ask for.Linkarzu: And do you feel like Claude issmarter than other models?Or you don't feel that too much of a difference?Sacha: I did more of a comparison back sometime ago when we were deciding whether to go witha Gemini subscription or a Claude subscriptionfrom my daughter as an incentive for herkeeping her grades up and doing stuff.We trust her to manage it reasonably well.We've had these conversations about AI usage andshe's very proud of the fact that she doesn't useit for doing her homework even though apparentlyin grade five some of her classmates are alreadygetting reminded by their teacher not to do that,right?Anyway, so we generated some samples of creativewriting based on the prompts that she had and wedid a blind comparison and she like clawed more.So that was her reason for doing that.For me, in terms of the small generation tasksthat I trust the AI with or I ask the largelanguage model to do, there hasn't been that muchof a difference.I'm sure people doing fancy agentic workflowswill have different experiences.Linkarzu: Yeah, I've noticed how it, how smartit got and that's the reason why I was asking andit makes me excited but it makes me kind ofworried as well at the same time.I don't want to be pessimistic but you know, Isee how good it got in the past few months and itmakes me thinkHow better is it going to get?Like, what am I going to be needed for then, like,in a couple of months, a couple of years, youknow?So, I don't know.I'm in that phase right now.And it's like, okay, it's exciting because I cando way more stuff.But, you know, what's going to be the point then,you know, in a couple of years, you know?So, I don't know.It's just me, baby, you know, thinking about it.Sacha: There are a lot of people dealing withuncertainty and existential crises and I have noidea what that future is going to look like.Although to be fair, it's a kind of, you know,people in other industries that people in IT havekind of automated away orWe've had to deal with that kind of existentialcrisis before.So it's our turn now, I guess.I'm semi-retired and professionally relevant, soI can step out here and just wait for the dust tosettle, right?Linkarzu: Yeah, you have it figured outalready, I guess.Like the professor, he's 80, he's retired already.Like he can just go to Hawaii, drink his piñacoladas and just enjoy life, you know.But, you know, we're still working and still, youknow, trying to figure things out.Yeah, I don't know.
1:14:46AI-generated code, slop PRs and the Lisp Curse in Emacs
Sacha: What's important to me is to help usfigure these things out together.For example, I'm focused on the Emacs communityand there are big debates about should we allowit?How should we help make sure that packagemaintainers aren't overloaded?They're not overwhelmed with a lot ofSlop PR requests.It's great to have all these announcements of newpackages, but if it's just entirely generated,then it tends to fall off and not get maintained.In the Emacs community, because it's so easy tobuild something for yourself, we've always hadthis challenge ofmaking something that other people can use anyway.So there's an essay with the title, I think, TheLisk Curse, or if you search for those keywords,The Lisk Curse, you'll find it.It talks about the fact that because it's sopowerful, it's hard for people to come togetherand decide on something.And with large language models and the ease ofgenerating code, that's basically the sameproblem scaled up.Which is something that we're used to in thesense that many times something that's tailoredto you, that's customized to fit you, it worksreally well for you, but it's hard to translateto somebody else.Just like if you're prompting a large languagemodel to create an app for you,It might not necessarily be something thatsomebody else can just pick up and run with.And it might not necessarily be something thatyou're going to use for six months or a year.It might just be something that you use today andit gets your problem solved today and that's okay.For me, the fact that people are exchanging theseideas and they're asking what ifs and they canactually get to the point where there's code tohelp them test those what ifs and help themrefine their ideas.That part is exciting and I'm curious to see whatpeople do with it.And at the same time, the people who want to staywith the code that they can read and understandand you know when they read the codeof other people even.If they read the code of other people, which issomething I love doing, you get the sense of whatother people are thinking about things.The kinds of needs that shape this code to bewhat it is.The little choices that they make in theirvariable names or the way that things fittogether.I think that's also a wonderful thing that Idon't want to lose ifIt's just generated really, really long librariesthat are not doing things as beautifully as theycould be.I'm glad that there are people who try things outand also disclose that they've used AI togenerate things.And I'm also glad that there are people who aremanually crafting things that they love.Linkarzu: You get to know them really well,right?When you see the crowd, it's like, oh, I knowthis person.I know no one better than talking to the person,okay?How do you feel about maintainers on the Emacsside of things with all of the code that can beeasily generated right now and having to dealwith that?I don't know if you maintain any packages or notbut I would assume you know that big packagemaintainers must be going through a reallydifficult phase right now because everyone issending PRs, everyone has great ideas and theyneed to filterGo through all of that code and clean it up.How do you feel about all that?They need to triage all that, right?Do you have any thoughts on Emacs maintainers inall of this slop?Sacha: I was a little amused by the exchange inOrg Mode, where I think Jean-Louis was proposingsomething that was partially generated by a largelanguage, or actually maybe entirely generated bya large language model.And it was there as a kind of an example.And after some back and forth, Ihor started usingan LLM to review it andWe're definitely experimenting with how to dealwith this kind of thing where the changes mightbe harder to understand because the volume ishigher, the logic is harder to talk through.But not being a maintainer of any very popularprojects myself, I have no idea the kind ofthings that people are going through.I do know that itIt is something that we want to be thoughtfulabout because maintainers are volunteers.We don't want people to burn out.We also don't want contributors to burn out.We want people to be able to experiment andcommunicate thoughtfully and kindly.Linkarzu: Okay.Now, two more questions that I have for you andI'll let you go.The first one that I have is... Where is thisthing?Hold on.Where is this thing?What did you think about the thumbnail that wecame up with?Sacha: You know, some people in the communitywere like, ah, what's up with that thumbnail?Linker too likes to have fun with hisAI-generated thumbnails.It's so delicate.But my daughter would like to say that she'snever seen me do the thing.Also, she feels that the AI makes my eyes too bigand she's not used to seeing my brain on thatside.Okay.Linkarzu: Okay.That's good.Sacha: So you got like a zero out of five.Linkarzu: I'm not sure I've ever made that face.I mean, I tried a couple of times afterwards, butI think I just looked very confused.What are these guys doing?Yeah, like my channel is like really random.I don't know why people still follow it, but AIhelps me with ideas that I have.Like, you know, I had this idea in mind and I waslike, you know, I can bring those.Okay, that's enough with the thumbnail.
1:20:58Advice for Emacs beginners: connect with people
Linkarzu: Now, the final thing before I let you go is whatwould you recommend someoneNew into Emacs.Like, what would you tell them?Hey, go and learn this.Go and read my blog post.What do you think?Someone just watching this video not knowing whatEmacs is but they were captivated by the wildthings that you do in Emacs and they're going to belikeI want to be that Chad.I want to be the Giga Stacey.Because it's Giga Chad and Giga Stacey I think isthe female version of it.So what would you say to them?Sacha: I would say go connect with people.Now, it's very difficult sometimes to find anEmacs user in your physical location, but thereare meetups and there's Reddit and there's like,you know, there's EmacsConf and there arevirtual meetups, as I mentioned, there aremailing lists, there's IRC channels.It's because Emacs can be really overwhelming ifyou're learning it by yourself.And there are all sorts of things that you mightnot even know to ask for or explore because youdon't even know it's possible.So for me, learning Emacs in the community hasbeen really helpful.I feel like I've barely scratched the surfacebecause I'm always being inspired by other peoplewho are sharing the cool projects that they'reworking on or the ideas that they're building bythemselves or with the systems.And definitely, large language models can helpwith some of it, but it's still limited to whatyou can ask.So if you can hook up with the blog posts or withthe IRC channels and all these other ways toconnect with the Emacs community, I think thatmakes Emacs learning a lot more fun and a lotmore effective.As you mentioned, Prot does have a coachingservice, so that might be an excellent way tostart.If your budget doesn't extend to €10 an hour,then you can always try IRC or other placesthrough the meetups.People often bring their questions there as welland learn from the kinds of cool things thatpeople are sharing.Linkarzu: Okay, amazing, Sacha.Sacha: Which actually brings me to stuff that Iwant to pick your brain about before I let you goeither.Okay.The Neovim community does YouTube really well,which is cool.But there's one Reddit post asking, hey, is therea Planet Emacs life equivalent for Vim?Basically an aggregator that brings together Vimblogs or what's going on in this conference stuff.I would like the Neovim community to be morevibrant and better connectedI mean, not that you really need that help.You've got way more users than the Emacscommittee and that's fine.But it'd be really interesting to see people, youknow, sharing blog posts and sharing cool ideas.There's our new vim, but you know, there's got tobe so much more that people can share with eachother and bounce ideas off each other and havethose kinds of conversations about.Linkarzu: You have a really good point.This is why you do your blog post.Is that right?Like you try to aggregate all of the differentEmacs related news, videos, articles, whatever,anything Emacs related, you put it in your blogpost.Sacha: Yeah, it's...It's kind of like having a water cooler, right?If you have a place where you can bump into otherpeople.And that Emacs News there, which is the secondlink on this page title, that takes me just aboutan hour or two to put together, depending on howdistracted I am by all the cool ideas that I comeacross.And I've done this on livestream so people canlaugh at my, you know, like, ooh, they're shiny.And it's built on top of a fair bit of automation.I just upload links on Reddit.The calendar comes in automatically.The stuff on the blog post comes in automatically.I just categorize things so that people who areinterested in a particular subject area don'thave the way through 60 to 70 links.They can focus on one category if they want to.But it should not be impossible to do somethinglike this for Vim.I'm sure that maybe, especially with Org and Vimand other things, maybe people will post morenotes because literate programming is anexcellent way to make things clear to yourselfand to other people.
1:25:16Why is the Neovim subreddit toxic while Emacs is friendly?
Linkarzu: Nate?It's like, I don't know, there's...A lot of crazy people on the Neovim subreddit.And I don't know if that's a particularity ofNeovim users.We're all toxic and we just hate each other.Sacha: Yeah, maybe you should just come over tothe Emacs community, we have hot chocolate.Linkarzu: Now, I have gone to the Emacs side ofthings.Apart from frazzor749, I think he's an Emacs userbecause he's abusive.That's in Spanish.I don't know how to translate it.He's mean.He's mean to me.But if you go to the...Emacs of Reddit, it's like rainbows andbutterflies and everyone's like, oh, you know,and there's not a lot of hate and it's somethingthat, you know, I always wonder why does thathappen?Like, it's not moderators, you know, it's justpeople.Is it just because it's old people?Like the professor, he doesn't want to fightanymore.He's old enough.He's just, he retired already.He's just ready to pass to the next life.Sacha: So what... Hypothesis number two, maybeyour editor makes you grumpy.Linkarzu: Right?Right?It could be.Sacha: I don't know.When I talk to people, they say, you know,working on Emacs, tweaking my Emacs is fun.It's relaxing.It's what I do because I enjoy doing it.I don't know.I think for Emacs, because we're so used to...feeling like our customizations are actually verypersonal.They're us figuring out how to build a tool sothey support us better.And we have no idea whether it'll work forsomebody else, but if it's useful, here, have it.It's a gift.For me, the feeling of being in the Emacscommunity is, it just feels like we're sharingall these things that we've figured out forourselves in the hopes that it might helpsomebody else.And it pays off often enough that, you know, Imight share something and then several yearslater someone will come across the same blog postand say, oh yeah, you know, I took that idea, Ibuilt on top of it and now you can do mind mapsin Emacs because yes, why not?And I was like, wow, that's amazing.So for us, there's always been a community ofsharing and knowing that there's no guaranteeit'll work for anyone else, but it's amazing ifit does.Linkarzu: Uh-huh.Or is there a cult and people are all connectedand when someone is misbehaving, they getpunished publicly?Like Prot in the thumbnail, it's like, it's timeyou get punished, my boy.Sacha: I was trying to find that bashbunnivideo clip where she says, you know, somethingalong the lines of, it's not a cult, but have youheard about our Lord and Savior Org Mode?It's fun and I've met some of the nicest peoplein the Emacs community and I love the fact thatthis little corner of the internet is aboutsharing what works for you and imagining whatcould be even better and being inspired by whatother people are doing with their lives.So that part is really cool.I hope that you have or can develop somethinglike that for Neovim as well.I think that, you know, you've definitely got thevideos and the reacts going.It'll be interesting to see if blog posts couldmake things more searchable.And for you, you have a lot of text that comesout of your videos.You could certainly get started with that.For us, we also have, you know, code...Because it's a lot about personalization andconfiguration, right?So we have this long tradition of literateprogramming and sharing the snippets of yourconfig with an explanation in it.And I'm curious to see what that could happen,what that could do with the Vim community as well.Linkarzu: Yeah, you have a point.There's this .file thing that came up like someyears ago, but I think it's just mainly anautomated thing that pulls up GitHub repos.I don't think it has like an actual prison thatis behind it, pulling up news and aggregating allthe stuff.There needs to be a human that does this, thatadds the touch and adds the love that you do onthe Emacs side of things.I'm not willing to take that job.I don't know if someone on the Neovim communityis willing to do it, because, yeah.Sacha: It's not just about the plugins or thecode, right?For me, what's really interesting are theworkflows that people have around it.You can look at someone's keyboard shortcuts, butit's when they tell a story of how they're doingit or when they show a video, but then you'relike, wait, wait, back up a little bit.How did you do that?thing, right?And a lot of the times we learn about our toolsbest when we're looking over the shoulder ofsomebody else who's using it and we're seeing thekinds of things that they might completely forgetto mention because to them, they already take itfor granted.So I love that kind of sharing that people havein meetups, at conferences, in video comments, inblog posts,where we're just sharing, okay, this is what I'vefigured out about how to use things.And if you have any other questions, often it'slike, what font is that?Linkarzu: What font is that?That's a common word.What color scheme is that?Sacha: People share.Linkarzu: Okay.All right, Sacha.Amazing.
1:31:02Planning an Emacs and Neovim panel with Prot
Linkarzu: Do you have any other topics before we wrap it upor something else that you would like to discuss?Because I've been asking all the questions andyou can ask whatever you want as well.Sacha: I'm already thinking of severalfollow-up things that I want to explore beforeour next conversation.We'll probably have another conversation in thefuture.Linkarzu: With Prot, right?We can have a conversation with Prot.I was thinking as well.about Neovim versus Emacs.Because I did that video, I think it was over ayear ago.There were four guests, right?And the people in the comments were like, but youshould have invited Prot.You should have invited Prot or the systemcrafters guy, you know?Now I can have eight guests.Imagine four on the Emacs side of things and fouron the Neovim side of things.The powerful ones, you know?That would be...Sacha: It's not even really Neovim versus Emacswho's got the best tool.I think it's more like, okay, you've got Neovimstuff.That's cool.And you've got Emacs stuff.That's cool.How can we inspire each other?How can we be like, oh, that's a great idea.I've got something similar.That's a little like this.I'm going to yoink your idea and smoosh it intothis one.And then we're going to see what we can make this.And I think that kind of back and forth would beamazing.Even in preparation for this conversation, I'vehad a lot of fun tinkering with my video editingworkflow, with the conversations, the livetranscriptions, the animation of SVGs in Emacs,the reordering of group elements inside an SVG sothat I don't have to use Inkscape to manuallyorder things.I can just use Org Mode and shuffle items aroundin the list in order to get my SVG to animate inthe order that I wanted to.Oh, and I have this thing now also now where Ican give it a YouTube URL and it opens atranscript for that video so that I can clipthings out of your most recent stuff directlyfrom your YouTube channel without having todownload it or run the transcription myselfbecause YouTube has auto subs and chapters.Thank you.So yeah, I've had a lot of fun preparing for thisconversation and I thinkAs we get more people talking and exchangingideas, we'll have even more ideas to explore.Linkarzu: Yeah.I always do the versus thing, but it's not aversus.If you go and take a look at that old video,there was not like a debate, like, my tool isbetter than yours.It was like, ah, it was friendly.It was a pretty friendly conversation.So, yep.Sacha: I want Neovim to thrive and all theother Vim variants and all the other editors thatpeople come up with because people are alwayschoosing these tools for their reasons andthey're perfectly wonderful reasons.And I want to see what extent we can take it,right?How awesome can it get?And then of course, if there's a really awesomeidea, chances are someone in Emacs is going to belike, oh yeah, I can do that in about 15 lines ofEmacs.Linkarzu: Oh yeah.Spicy takes.Spicy takes there.Sacha: Who knows, right?And sometimes it might be that we'll call out tosomething.Besson Carey was telling me, and I'm going to seeif I remember this correctly, because he wastelling me this anecdote in French.which I only partially understand.So he was telling me that he was at a TupperVimcommunity meeting.So they were sharing Vim tips and he was like,oh, that's a really nice table.How did you get it to display so nicely?And the guy hemmed and hawed and eventuallyadmitted that he had actually shelled out theEmacs to run the code and evaluate it and thenget the image, get the output back and then putit in Vim.It's totally possible to borrow ideas and usethem back and forth, even to the extent ofcalling the other plugin in order to just get thestuff done.Linkarzu: Yeah, I see that a lot.People are switching between two.Like, there's some...People that switch to Helix for some reason.I don't know if you've heard about Helix.It's fine, whatever.But I've seen that crossover a lot.People switch from Neovim to Emacs, then they gofrom Emacs to Neovim and they're like this.You know, and that cross pollination, is that howyou say it?Helps, you know, both tools, right?Sacha: and it's plain text and you can shipthings back and forth.And if you are a large language model usingperson, you can just use AI to translate codeback and forth to Knows, we'll see.And if you want to use Emacs Client or anembedded process or whatever else, that's alsototally cool.Whatever gets a job done and whatever lets youexplore that idea, right?
1:35:53Do editors and terminals still matter with AI agents?
Linkarzu: Yep, yep, yep.One final thought that I have.What do you think about editors and terminals?Because people say like, okay, maybe I live in abubble, you know, but on Twitter...Yeah, I think we live in a bubble.And that's what I get surrounded with.But people is like, why do you still use aterminal, right?Now it's all about agents.Why do you use an editor in this day and age,right?There's a lot of people that actually don't usethem because they are full agentic now.What do you think?The future looks like for Emacs and Neovim andterminals, which, you know, Neovim needs aterminal, right?So it's like that's what gives its superpowers aswell.Do you have any thoughts you want to share onthat?Sacha: I like the way that Emacs gives me kindof an interface toolkit if I want to edit videosas text or if I want to look at my agenda or if Iwant to make a buffer with buttons that I canclick on.It gives me a bit more of that richness andflexibility that going with either aconversational chat agent interface doesn't or Iwould have to have the agent generate me a wholeinterface and then I would have to tell it, OK,but I want it to behave this way and then Iwouldn't be able to integrate it as easily withthe rest of my stuff.So I don't think, for me at least, that agentsare quite there yet.I'll give you an example.So I'm organizing EmacsConf, right, and I needto keep track of the schedules that I've emailedspeakers because that way I can tell if I'vechanged the schedule whether I need to email themagain with a new schedule, right?Makes sense.And because I also do my email within Emacs, Ican set it up so that when I send the email, notwhen I've composed the email because if I'vecomposed the email using my mail merge but then Iget distracted and I change my mind, it doesn'tactually get sent yet, right?Only after I send the email will it go and updatethat specific talk's details in my Org Mode filewith all of my conference talk information,naturally.and then that will be saved for the next timethat I move the schedule around.Now I guess theoretically someone could have anagent that says okay I give you access to myemail, I give you access to the conferenceschedule web system or whatever database and thenI want you to update the database in case thishappens but itIt would be a lot more opaque to me.I wouldn't be able to say, oh yeah, I want to dothat but I also want these other things to happenand I want the SVG to show up and I want the wikito be changed.There's a lot of moving parts and it's a lotharder for me to think of how to get in there andchange things in case there's a bug or in case Iwanted to see what else is possible.So I like the fact that Emacs lets me build theseinterfaces using keyboard shortcuts and voicecommands now and all these other things and itfeels like it's all together and I don't have tokeep reinventing the wheel.Linkarzu: Yeah, yep.I do kind of something similar.This is the way that I schedule live streams now,you know?I have a snippet, Neovim.So I just type something here, live stream with aguest, open terminal, or if it's a solo livestream, that's going to add my snippets.And I just fill it up and I have a key map that Iexecute.It schedules the live stream, sends emails to theguests, if there are guests, you know?Sacha: Yeah, I got that too.Linkarzu: You have that?Yeah, yeah.So it's one of the things that I'm...Sacha: Do you have it also update your YouTubestream description through API?Linkarzu: Yeah, the title, the description isright here.Sacha: Yeah, yeah, yeah, yeah.And then what else do I have in mine?Mine, I have it do the time zone conversionsbecause I really suck at time zone conversions.I choose UTC and I tell guests, you know?Linkarzu: UTC, it is.Sacha: But I also like including the gazilliontime zone translations in the description so thatpeople don't have to do the UTC conversionthemselves.So based on the timestamp for the event, I havethis thing that allows me to pick up the...Time Zone translations because I use that forEmacsConf and then I just generalize it so I canuse it for everything.And then that can go into the YouTube description.And then I have the same entry get turned intothe blog post afterwards because I can publishthat with a transcript and the screenshots as Ishowed you before.Linkarzu: Okay.Automation.It's all about automation.It is.Sacha: It absolutely is.Oh, and the thing that I have which also lets mecopy the chat and then if I paste it in, then Ican have it reformatted so that it looks nicerbecause otherwise when you paste it in, there'slots of spaces and stuff.But that's a fairly easy text transformationthing.So you should be able to do something like thatjust fine.
1:41:04Speech synthesis in Emacs: whispering slide hints
Linkarzu: Let me show you something cool aswell.heisenbug, can you please send us a speech totext command in chat?Sacha: Oh, yeah, I saw that one.Linkarzu: You should be able to hear it as well.You've seen it?Sacha: Yeah, I've heard that before in yourother videos, which is actually...So one time I was giving an in-personpresentation with Emacs, well, about Emacs at oneof the demo camps, back when demo camp was athing.And because I wasn't entirely sure about myability to remember what my next slide was goingto be about, I had Emacs do the speech synthesisof a hint for my next slide in the back, youknow, in my earbuds, earphones back then.And someone was like, is Emacs telling you whatto say?Because speech synthesis is actually, you know,it's another channel that you can use to getinformation.So that's very cool.And I have been tempted to come up with somethingsimilar because I sometimes forget to check chat.But it's possible, for example, to have it say,okay, say it out to me first and then maybe I'llhit a keyboard shortcut and then it will say itagain on the main channel that will go out in thestream.Linkarzu: So it's safe.Yeah, yeah.It becomes a mess because I have it limited tomembers now.Only members can send the say command.When I was in the early testing phase, I enabledit for everyone.And guess what?The bots found it and they started sending saycommands like it's going to happen, you know, andit's just going to run out of control.Sacha: The other reason why you can have that,right?So you have the chat and you have the TTS so thatyou know that something has happened in the chat,but then you can trigger it to play in thechannel when you want to.And possibly with a completion interface, you canselect which utterance you want to have at theright time.So if people are having questions, you know thatthe question is there, but you can pick whichquestion you want to focus on first, or you canwait for the natural pause in the conversation inorder to help.Yeah, that's a good idea.Linkarzu: That's a good idea.It makes sense.Sacha: Yeah, right.So this kind of like back and forthbrainstorming, it's totally possible to implementit in either way.I would end up implementing it in Emacs, ofcourse, because then it can hook into all of myother stuff.But you can certainly implement it in whatever itis that you're using to listen to the TTS.Is that SocialStream Ninja maybe?Linkarzu: SocialStream.Yep, yep, yep.And I send it to Gemini becauseSacha: Yeah, yeah.Linkarzu: It sends it to Gemini.That's why it sounds accurate.I think Gemini is a pretty good one, uh, for that.Sacha: Yeah, it's pretty good.Yeah, for local, for local speech to text, I'vebeen using Kokoro, uh, which...Linkarzu: But it sounds a little bit robotic,right?Yep, yep.Sacha: Yeah.But yeah, Gemini is pretty good and there's somany different options now.It's fantastic.And it does give you the other channel.Back a long time ago, in fourth year university,so 2003 or 2002,I was monitoring IRC.I had a lot of fun hanging out in IRC.And so I set up my Emacspeak so that ifsomebody mentioned me in the channel, it wouldsay something so that I could then go and study.But I had this like thing of distraction.Like, you know, if someone wanted to talk to mespecifically, I could hear about it.And likewise, having it announce things like, ohyeah, the compilation is done, meant that duringthe nights where I was staying up late and kindof just catching little micro-naps while waitingfor my code to finish, I could just have thatmicro-nap and then wake up when the ding or theannouncement went off.Linkarzu: Oh, yeah.Like I say, automation.Once you try automating stuff like Emacs newthem, I think that's why we love the tools somuch because they just allow you to do whateveryou want to do.There's no limits.No, you can do that.Up to you, up to whatever you want to do, right?Yeah.Sacha: Yeah.And the nice thing about this, even if you'rejust smoothing things over a little at a time,it's very tempting to fall down the rabbit hole,right?And you spend like the next two hours tweakingsomething.This is my life, right?But it is also possible to try to be slightlydisciplined about it and say, OK, I'm going tojust modify one thing.and I'm going to write one function or whatever,make this one little bit better.And then the next time you do it, you do anotherlittle bit.And so there's that XKCD automation kind of gridof when it makes sense to automate something.And I find myself automating things that reallyaren't technically justified by how often I do it.But because all those little bits of code add upand theyThey become useful in a combinatorial way.It's not just linearly adding up.Each little bit of code becomes a piece I can usesomewhere else for a purpose that's completelyunexpected at that moment.It becomes so powerful.Linkarzu: Yeah, it does.If I need to prepare for a live stream, there's alot of applications that you need to open.You need to open OBS.I need to open Chrome where the conversationbetween the two of us is happening.I need to open social stream.I need to open a lot of like 10 or more than 10applications.So I automated that.Even YouTube Studio is open.Like all of it, because it's annoying, butit's... Yeah, yeah.Sacha: I have a prepare to go live.It does it automatically for me too.It brings up the live streaming dashboard.It double checks all these other things.And then it copies the description over in caseI'm doing a spontaneous live stream.And then I can verify that and then I can say,okay, let's go live.Linkarzu: All right, Sacha.Amazing conversation.Thank you very much.We're going to see if the editing workflow works.We're going to see a clip of this later today.Hopefully.We're going to see who gets it out first.That's a race.Sacha: Oh, I'm allowed to clip yours?Linkarzu: Yeah.You can clip it.It's fine.It's completely fine.Sacha: All right.Linkarzu: You're going to win.Emacs.Sacha: I already have my real-time transcript.Linkarzu: You already won.Yeah.Sacha: I don't know.I trust your editing chops, but I love being ableto pull things out.And I do want to make sure that all the coolthings that people can build on will be availableas text.So I will go and edit the transcript and then youcan use that too.Linkarzu: Okay.Amazing.Sacha, thank you very much.Hope you have a wonderful day.Thanks chat as well.Talk to you later.Sacha: All right.Okay.Bye.
I usually stick with some favorite font for a while, then, for some
reason, the bug bites: some new activity, different hardware or work,
and I want to change the default font.
I have this old file with content like this:
(set-face-attribute'defaultnil:family"3270 Nerd Font Mono")(set-face-attribute'defaultnil:family"GoMono Nerd Font")(set-face-attribute'defaultnil:family"Hack Nerd Font Mono")(set-face-attribute'defaultnil:family"Iosevka Nerd Font Mono")(set-face-attribute'defaultnil:family"JetBrainsMono Nerd Font");; ... and a bunch more
What I do (shame on me) is put the cursor at the end of a line, C-x C-e, switch back to the buffer or tab I was working on, look around
for a while, switch back to the file, move down one line, C-x C-e
again... You get the idea. It works, but it's clunky, and changing the
size means editing the line and adding :height to it.
I wanted a way to scrub through my font list and increase or decrease
the font height using only the arrow keys, while I check live what
feels right.
That's a perfect job for a
transient
menu, which has shipped with Emacs since version 28, written by the
one and only Jonas Bernoulli, author of nothing less than
Magit.
This post is a walkthrough of how you can implement such functionality
yourself, and hopefully a guide along your Elisp learning journey.
TL;DR: jump straight to the full source, put it
in your init file, edit the font list, and run M-x font-cycle.
Font Candidates
First, we build a list of font families:
(defvarfont-cycle-families'("3270 Nerd Font Mono""GoMono Nerd Font""Hack Nerd Font Mono""Hurmit Nerd Font Mono""Inconsolata LGC Nerd Font""Iosevka Nerd Font Mono""IosevkaTerm Nerd Font Mono""JetBrainsMono Nerd Font""Lekton Nerd Font Mono""M+CodeLat50 Nerd Font""Maple Mono NF""MartianMono Nerd Font""SauceCodePro Nerd Font""UbuntuMono Nerd Font Mono""VictorMono Nerd Font Mono""ZedMono Nerd Font Mono")"Font families to cycle through with `font-cycle'.")
If you don't know the exact family name of a font you have installed,
and how it is "perceived" by Emacs, you can use M-x describe-font
with completion, or M-: (font-family-list).
If a family on that list is not installed (a typo, another machine, a
font you uninstalled), set-face-attribute won't complain. Emacs just
falls back to some other font, and you end up judging the wrong
thing. So, before cycling, we keep only the families Emacs can
actually find:
(defunfont-cycle--installed()"Return the members of `font-cycle-families' installed on this system."(seq-filter(lambda(family)(find-font(font-spec:family family)))
font-cycle-families))
Left and Right (fonts) Cycling
Cycling is an index and a mod. The mod is what makes it wrap
around: going right from the last font takes you to the first one,
and going left from the first takes you to the last.
(defvarfont-cycle--index0"Position of the current font in the installed families.")(defunfont-cycle--select(index)"Use the installed family at INDEX, wrapping around both ends."(let((fonts(font-cycle--installed)))(unless fonts
(user-error"None of `font-cycle-families' is installed"))(setq font-cycle--index (mod index (length fonts)))(set-face-attribute'defaultnil:family(nth font-cycle--index fonts))))(defunfont-cycle-next()"Switch to the next font."(interactive)(font-cycle--select(1+ font-cycle--index)))(defunfont-cycle-previous()"Switch to the previous font."(interactive)(font-cycle--select(1- font-cycle--index)))
Note that mod in Elisp follows the sign of the divisor, so (mod -1 16) is 15, not -1. This is what we want for going left.
Up and Down (height) Cycling
For the size, we change the :height of the default face. Its unit
is 1/10 of a point, so a step of 10 is one point:
(defunfont-cycle--resize(delta)"Change the default face height by DELTA (1/10 pt)."(set-face-attribute'defaultnil:height(+(face-attribute'default:height) delta)))(defunfont-cycle-bigger()"Increase the font size by 1pt."(interactive)(font-cycle--resize10))(defunfont-cycle-smaller()"Decrease the font size by 1pt."(interactive)(font-cycle--resize-10))
Keeping the Winner
When I finally find a combination I like, I don't want to remember and
retype it. So one more command builds the exact line for my init file
and puts it in the kill ring for later yanking:
(defunfont-cycle--form()"Return the `set-face-attribute' call for the current font."(format"(set-face-attribute 'default nil :family %S :height %d)"(face-attribute'default:family)(face-attribute'default:height)))(defunfont-cycle-copy()"Copy the current font setting as Elisp to the kill ring."(interactive)(kill-new(font-cycle--form))(message"Copied: %s"(current-kill0)))
Then it's just a C-y in init.el.
The Transient Menu
Now let's glue everything together. Each suffix marked with
:transient t runs its command and keeps the menu open, so you can
press the arrows as many times as you want. y and q don't have it,
so they close the menu.
(transient-define-prefix font-cycle ()"Cycle through `font-cycle-families' and adjust the font size."[:description font-cycle--description
["Font"("<right>""next" font-cycle-next :transientt)("<left>""previous" font-cycle-previous :transientt)]["Size"("<up>""bigger" font-cycle-bigger :transientt)("<down>""smaller" font-cycle-smaller :transientt)]["Done"("y""copy as elisp" font-cycle-copy)("q""quit" transient-quit-one)]])
The :description of the outer group is a function, and transient
calls it again every time the menu is redrawn. That gives us a header
that always tells which font is on screen, its size, and where we are
in the list:
(defunfont-cycle--description()"Header showing the current font, its size and position in the list."(format"%s %dpt [%d/%d]"(propertize(face-attribute'default:family)'face'bold)(/(face-attribute'default:height)10)(1+ font-cycle--index)(length(font-cycle--installed))))
Run M-x font-cycle and you get something like this at the bottom of
the frame:
IosevkaTerm Nerd Font Mono 16pt [7/16]
Font Size Done
<right> next <up> bigger y copy as elisp
<left> previous <down> smaller q quit
Since the menu lives at the bottom, everything above it is your real
work: the buffers you were editing, your theme, your splits. Tap
right, look, tap right again. If you like it, bump it up a point or
two. When you're done, y and paste.
If you want it one key away:
(keymap-global-set"C-c f"#'font-cycle)
Full Source
;;; font-cycle.el --- Test-drive fonts with a transient menu -*- lexical-binding: t; -*-(require'transient)(defvarfont-cycle-families'("3270 Nerd Font Mono""GoMono Nerd Font""Hack Nerd Font Mono""Hurmit Nerd Font Mono""Inconsolata LGC Nerd Font""Iosevka Nerd Font Mono""IosevkaTerm Nerd Font Mono""JetBrainsMono Nerd Font""Lekton Nerd Font Mono""M+CodeLat50 Nerd Font""Maple Mono NF""MartianMono Nerd Font""SauceCodePro Nerd Font""UbuntuMono Nerd Font Mono""VictorMono Nerd Font Mono""ZedMono Nerd Font Mono")"Font families to cycle through with `font-cycle'.")(defvarfont-cycle--index0"Position of the current font in the installed families.")(defunfont-cycle--installed()"Return the members of `font-cycle-families' installed on this system."(seq-filter(lambda(family)(find-font(font-spec:family family)))
font-cycle-families))(defunfont-cycle--select(index)"Use the installed family at INDEX, wrapping around both ends."(let((fonts(font-cycle--installed)))(unless fonts
(user-error"None of `font-cycle-families' is installed"))(setq font-cycle--index (mod index (length fonts)))(set-face-attribute'defaultnil:family(nth font-cycle--index fonts))))(defunfont-cycle-next()"Switch to the next font."(interactive)(font-cycle--select(1+ font-cycle--index)))(defunfont-cycle-previous()"Switch to the previous font."(interactive)(font-cycle--select(1- font-cycle--index)))(defunfont-cycle--resize(delta)"Change the default face height by DELTA (1/10 pt)."(set-face-attribute'defaultnil:height(+(face-attribute'default:height) delta)))(defunfont-cycle-bigger()"Increase the font size by 1pt."(interactive)(font-cycle--resize10))(defunfont-cycle-smaller()"Decrease the font size by 1pt."(interactive)(font-cycle--resize-10))(defunfont-cycle--form()"Return the `set-face-attribute' call for the current font."(format"(set-face-attribute 'default nil :family %S :height %d)"(face-attribute'default:family)(face-attribute'default:height)))(defunfont-cycle-copy()"Copy the current font setting as Elisp to the kill ring."(interactive)(kill-new(font-cycle--form))(message"Copied: %s"(current-kill0)))(defunfont-cycle--description()"Header showing the current font, its size and position in the list."(format"%s %dpt [%d/%d]"(propertize(face-attribute'default:family)'face'bold)(/(face-attribute'default:height)10)(1+ font-cycle--index)(length(font-cycle--installed))))(transient-define-prefix font-cycle ()"Cycle through `font-cycle-families' and adjust the font size."[:description font-cycle--description
["Font"("<right>""next" font-cycle-next :transientt)("<left>""previous" font-cycle-previous :transientt)]["Size"("<up>""bigger" font-cycle-bigger :transientt)("<down>""smaller" font-cycle-smaller :transientt)]["Done"("y""copy as elisp" font-cycle-copy)("q""quit" transient-quit-one)]])(provide'font-cycle);;; font-cycle.el ends here
What Could be Improved
A few extras, if you want to practice some Elisp:
Replace the index and mod with Emacs' built-in ring.el.
Show the visible columns and lines in the header, so you know how
much fits on screen with each font and size.
Start cycling from the font you are currently using.
Add a key that restores the font and size you had before opening the
menu.
Cycle themes, or a second list for the variable-pitch face, the
same way.
Turn it into a publishable package: defcustoms, an autoload,
proper headers, and a clean checkdoc and byte-compile.
Honorable Mentions
Font picking seems to be in the air lately. If you'd rather use
something ready-made, or just want to see other takes on the idea,
check these out:
show-font, by
Protesilaos, on GNU ELPA: previews installed fonts in a dedicated
buffer, and show-font-tabulated lists every font rendered in its
own typeface.
flick, "a quick and simple
font picker", by Vegas Harmon, recently shared on r/emacs.
I first tried mise while setting up Arch Linux on my Dell XPS 13.
I was bringing over my Doom Emacs configuration, which used asdf.el, and
considering how mise would fit into the same environment.
Colleagues had already spoken well of it. What interested me was the
possibility of simplifying runtime integration in both Fish and Emacs. If
that worked on the Dell, I was considering the same change on my Mac.
The useful test was inside the editor. I set up separate directories for
Ruby 3.4 and 3.3, opened their Ruby files in Doom, and checked the version
from each buffer. Each one used the Ruby selected for its directory. I also
confirmed that Doom could find Node and npm, and that the Elixir tools were
available.
That gave me a working setup on Arch before moving my main Mac environment
away from asdf. The Mac work then extended into Fish configuration and the
tools Emacs launches. The migration described here took place in October
2026; the Mac checks used mise 2026.10.21.
Keeping project version files
The starting point on the Mac was a set of projects with existing version
files. Replacing the manager did not require changing every project’s Ruby
or converting all those files to a new format.
Mise reads .tool-versions, the format I was already
using with asdf. For ecosystem-specific files such as .ruby-version and
.nvmrc, I enabled the corresponding tools in the global settings:
Those are personal defaults. A project’s files can select another version.
During the migration checks in the blog repository, mise current ruby
reported 3.4.6. The project’s .tool-versions selected that version even
though its .ruby-version said 3.4.5.
The configuration rules determine how global and project
files combine. I checked the effective selection rather than assuming
that finding a familiar version file was enough. mise config ls lists
the configuration files being read, and mise current ruby reports the
selected Ruby.
I manage my dotfiles with GNU Stow. During the transition, the package
still named asdf retained .tool-versions, .default-gems and
.default-mix-commands. I later moved those files into the mise package
and recreated their Stow links. The move left the effective runtime
selections unchanged. That was configuration housekeeping, separate from
moving or installing runtimes.
The default-installation files were retained, but I have not validated
that a fresh Ruby or Elixir installation consumes them automatically.
Letting Fish activate mise
On Arch, the first mise doctor check reported that mise was not activated.
After setting up Fish activation, it reported no problem. Installing the
executable had been one step; arranging the shell environment was another.
On the Mac, mise is installed through Homebrew. I did not need to add an
activation line to my own config.fish: this installation supplies
/opt/homebrew/share/fish/vendor_conf.d/mise-activate.fish:
if [ "$MISE_FISH_AUTO_ACTIVATE" != "0" ]
/opt/homebrew/opt/mise/bin/mise activate fish | source
end
The Fish setup documentation describes this package-provided
activation. I kept that mechanism instead of adding another copy to my
personal configuration.
The surrounding Fish changes made the command paths explicit.
Personal paths moved into conf.d/00-environment.fish, which normalizes
the inherited PATH, removes duplicates and filters known obsolete
entries, including the old asdf shims. Asdf and older installations were
cleaned up as part of that work.
The prompt also needed to follow the new manager. My old override parsed
.tool-versions itself. Its replacement asks mise current once per render
and displays the languages relevant to the directory. Ruby, Node and
Elixir were joined by Rust, detected through files such as Cargo.toml.
The prompt now uses mise’s selection rather than maintaining its own
version-file parser.
Rustup remains in this setup: the mise Rust entry links to the existing
~/.cargo/bin directory. I added Rust to the same prompt segment rather
than adding another standalone version check.
The wider Fish review also removed the old Oh My Fish Emacs plugin and
kept the command helpers I use as autoloaded functions. That removed an
emacs --version call from shell initialization. I did not
measure a before-and-after startup time for it.
The environment inside Doom
The Doom change had two parts. I replaced the asdf.el package declaration
with the recipe for mise.el in packages.el:
In config.org, I replaced the asdf-enable setup with:
(use-package!mise:config(global-mise-mode+1))
On Arch, after synchronizing Doom, I confirmed that the mode was active and
that Emacs could find the Ruby shim. I checked Ruby LSP and ElixirLS
in that setup.
The installed revision of mise.el makes the mechanism visible.
It calls mise env --json, merges the result into a buffer-local
process-environment, and derives a buffer-local exec-path from the
resulting PATH.
Emacs uses exec-path to find a program named without an absolute path.
The process it starts inherits process-environment, including the PATH
used to find its own child commands. The Emacs process reference
describes both roles. That is why checking Ruby in a terminal alone could
not validate what a formatter or language server launched by Emacs used.
The package uses inheritenv around selected commands, including
shell-command-to-string, so they can carry the editing buffer’s
environment into temporary buffers.
Doom has its own environment snapshot as well. In this revision,
doom sync regenerates an existing snapshot;
doom sync --env also creates one if absent. The Emacs command
doom/reload-env only loads the saved file into the
running session. On Arch, doom doctor continued to warn that my PATH
had changed even while the runtime checks worked. I did not establish the
cause of that warning.
On the Mac, my Ruby formatter configuration also follows the project:
RuboCop when a .rubocop.yml is present, otherwise Standard. RuboCop uses
bundle exec when there is a Gemfile. Both commands clear inherited
Bundler-related variables before starting, and Standard uses the gems
available to the selected Ruby. The full configuration
contains the details; the important connection here is that a formatter
needs the project’s environment as well as its configuration file.
Following the development tools
Some settings outside the runtime manager still named the old locations.
My Arduino configuration was one of them:
The defvar initializer does not repeat the lookup each time I open an
Arduino buffer. Resolving it to a mise shim gives that saved value a stable
path while the executable lives under a particular Go installation.
Checking the running Emacs confirmed the shim path, and the server answered
its help command.
An installation problem with arduino-language-server and dockfmt led me
to check Go. Mise had Go 1.27.3 installed, but it was inactive in the directory
where I was working, so ordinary commands still found Homebrew’s Go 1.26.6.
mise exec go@1.27.3 -- go version used the intended installation, and both
tools built with it in a temporary directory. I then selected Go globally
and installed them.
mise install does not record a version selection;
mise use records one and installs the runtime if needed. I did not reproduce
the original installation error, so the inactive Go is an observed problem,
not a demonstrated cause of that failure.
This setup puts Go-installed executables in the selected Go version’s
bin directory through GOBIN. It changes where the tools live.
After adding an executable there, mise reshim makes it
available through the shim directory.
That mattered for dockerfmt, which Doom actually uses for Dockerfiles.
I had a copy installed with Homebrew, and Emacs kept finding it after I
installed dockerfmt with the mise Go. After mise reshim, executable-find
resolved to the mise shim. The earlier dockfmt build concerned a different
formatter. Doom’s Docker configuration uses dockerfmt,
although its doctor check still looks for dockfmt.
Gems outside the checkout
I made a separate storage change during the cleanup: removing the global
Bundler path setting that sent gems into vendor/bundle. Projects using
the same Ruby installation can now share installed gems.
Mise selects Ruby and, in this setup, its RubyGems directory. Each project’s
Gemfile and lockfile still select its dependencies. With Ruby 3.4.6,
Gem.dir matches Bundler.bundle_path under both Bundler 2.3.17 for the
blog and 4.0.17 for ctf_tracker.
That follows the Bundler policy, not a mise feature. The
configuration reference specifies Gem.dir as the default
before Bundler 5, and a repository-relative .bundle directory starting with
Bundler 5. A project can override path.
Seven active projects passed dependency and principal-library load checks
against the shared directories, with their Gemfiles, lockfiles and Ruby
selection files unchanged. After cleanup, ctf_tracker also passed its 86
examples. The global automatic-installation and cleanup settings were
removed, along with a production exclusion that excluded no dependencies
in those projects. My .gemrc now only disables documentation
and backtraces, with its Stow link preserved.
The 45 removed vendor/bundle trees had an allocated size of about 10.46 GiB
in the retained du -sk inventories. Gems were also installed in the shared
directories, so this is not a measured net saving. With those trees removed
from the checkouts, I re-enabled LSP file watchers. After restarting Emacs,
a large Rails project opened without a warning or noticeable slowness, an
observation limited to that project.
The two Ruby buffers on Arch were an early check that per-directory selection
could work inside the editor. On the Mac, getting there also meant replacing
old paths and checking the tools launched by the editor. The project
dependency files could stay as they were while I changed the runtime manager
and the environment around them.
A macOS setup shared in the project’s issue tracker uses
Hammerspoon. I used Apple’s built-in Shortcuts app for the launcher and
keyboard binding. My successful test was a GitHub comment field in Safari:
the edited text returned to the field, without submitting the comment.
This is my tested Doom setup with Homebrew Emacs Plus 31, specifically
Emacs 31.0.91. The launcher was small; getting it to find my running server
and giving Emacs the right permission took more attention.
Enable the Doom module
In ~/.config/doom/init.el, enable everywhere under :app in the existing
doom! block. Then synchronize Doom and restart Emacs:
~/.config/emacs/bin/doom sync
Emacs must already be running with its server active. Use
M-x server-start if needed. This launcher does not start Emacs for you.
The Doom module documentation describes the integration;
in the version I tested, doom +everywhere calls
emacsclient --eval '(emacs-everywhere)'.
Build the shortcut
Open Shortcuts, create a shortcut named Emacs Everywhere, and add a
Run Shell Script action. Choose bash, leave the action’s input
variable unset, and keep Run as Administrator off. With no input,
the Pass Input setting does not matter.
Paste this script, adapting the absolute Doom path on the last line:
The locale exports addressed a warning I encountered with en_FR.UTF-8.
The socket export addressed a more confusing failure: the command worked
in Terminal, but Shortcuts reported that emacsclient could not find the
socket even though my server was running.
getconf DARWIN_USER_TEMP_DIR obtains my account’s temporary directory;
id -u supplies its numeric user ID. Together they locate the default
macOS socket without hardcoding a /var/folders/... path.
EMACS_SOCKET_NAME tells the client which socket to use.
If you customize server-name or server-socket-dir, adapt the expression.
I reproduced the lookup failure with a different TMPDIR and confirmed
that the explicit socket fixed it. I did not inspect Shortcuts’ own
environment, so that test does not establish its exact difference from
Terminal.
Open the shortcut’s Details panel (ⓘ), choose Add Keyboard Shortcut,
and press Control+Option+Command+E (⌃⌥⌘E). This combination worked in
my setup; check for conflicts in yours. Apple’s keyboard shortcut
instructions describe the same binding mechanism.
My macOS interface is in French. “Exécuter le script Shell” is Run Shell
Script, and “Détails” is Details. The shortcut receives Quick Action
input, but the shell action has no input variable: Emacs Everywhere handles
the selected text itself.
Allow Emacs to control other applications in Privacy & Security →
Accessibility. The screenshot shows the enabled entry in my French UI.
An enabled entry was not enough in my case. I removed it, added the current
Homebrew application again, and fully restarted Emacs. The tested app was
/opt/homebrew/Cellar/emacs-plus@31/31.0.91/Emacs.app.
Before that, I saw errors with codes -25211 and -1719, meaning that
osascript was not allowed assistive access. Re-adding Emacs resolved the
permission problem; I did not establish why the earlier entry failed.
A Homebrew upgrade may require granting this permission again. On my Mac,
/Applications/Emacs.app already links through
/opt/homebrew/opt/emacs-plus@31/Emacs.app, but Finder resolves it to the
versioned application when I add it. I would not rely on that symlink to
preserve permission. I upgrade Emacs infrequently and chose to re-add it
when necessary.
Select the text before launching
This was the missing step in my first attempt: select the source text.
Putting the caret in the field was not enough. Without a selection, my
installation imported the existing clipboard contents, including a path
I had copied earlier.
For a first test:
Click inside an editable text field in Safari and enter some text.
Press Command+A inside that field to select its contents, then
⌃⌥⌘E. I did not need a separate Command+C.
Actually change the text in the Emacs frame, then finish with
C-c C-c. The edited text should return to the original field.
Use C-c C-k to cancel without pasting back. In the installed version,
an unchanged buffer is also treated as an aborted session, so opening and
immediately closing it does not test the return path.
If the frame title contains an AppleScript error
I also encountered this where the application or window name should have
appeared:
script error -1758. Couldn't get error text because of error -1700.
The installed package compiled its helper scripts with
osacompile -r scpt:128 -t osas, producing legacy resource-fork scripts.
osadecompile rejected those files with
errOSADataFormatObsolete (-1758). Newly compiled data-fork scripts were
readable. Rewriting the existing file’s data fork alone did not repair it:
the obsolete resource fork remained. Replacing the file did.
The GitHub source checked on 1 November 2026 still uses
that compilation command. Check your installed version before adding this
workaround; it is not a prerequisite for every installation.
The local workaround in my Doom configuration
I keep this in an emacs-lisp source block with :tangle yes in
~/.config/doom/config.org, which generates config.el. If you use a
literate configuration too, edit the Org source rather than the generated
file.
(after!emacs-everywhere;; macOS rejects the legacy resource-fork AppleScripts generated upstream.(defun+everywhere-modern-applescript-a(fn&restargs)(let((default-directoryemacs-everywhere--dir))(applyfnargs)(dolist(script'("app-name""window-title""window-geometry"))(unless(zerop(call-process"osadecompile"nilnilnilscript))(let((compiled(make-temp-file"everywhere-")))(unwind-protect(progn(unless(zerop(call-process"osacompile"nil"*everywhere compilation*"nil"-d""-o"compiled(concatscript".applescript")))(error"Could not compile Emacs Everywhere script: %s"script))(delete-filescript)(rename-filecompiledscript))(when(file-exists-pcompiled)(delete-filecompiled))))))))(advice-add'emacs-everywhere--ensure-oscascript-compiled:around#'+everywhere-modern-applescript-a))
The wrapper runs the package’s compilation function from its actual
directory, checks each helper with osadecompile, and replaces unreadable
files with freshly compiled data-fork versions. It also covers later
regeneration. The private function name is upstream’s spelling, including
oscascript; this advice may need adapting after a package update.
Forced regeneration and decompilation of all three repaired helpers worked
in my tests. After fixing Accessibility permission, application and window
queries worked too.
The useful check was the complete round trip in Safari. Once I selected
the field’s contents before launching, I could edit them in Emacs and
return them with C-c C-c. That is the sequence I would test first after
changing this setup.
Three years ago, I wrote about marking the active window (update here ) so that it’s easy to pick it out from the other, inactive windows. I did that by making the mode line of the active window a distinct color instead of the slightly different shade of gray from the other windows. That’s worked out really well for me and I’ve never looked back.
Now Ray Zeitler over at Ray on Emacs takes another look at the problem and found a really nice solution. It’s a bit more in your face than mine so if you’re looking for something that makes the active window really stand out, you should take a look at his post. The TL;DR is that instead of changing the active window, he changes the background color of all the other windows.
Zeitler found his solution on a 10 year old Stack Exchange post so it’s pretty certain to work no matter what version of Emacs you’re using. The code is very simple and you can simply copy it to your init.el if you’re interested.
As I said, this solution seems a little aggressive to me so I doubt I’ll be implementing it but others may disagree. One thing for sure, it’s going to make your active window stand out. It’s a small enough change that you can try it out—say by copying it to your scratch buffer and executing it—to see if it’s something you’d like.
Now you can use emacs.news to get to Emacs News.
That should save some clicking/typing.
It redirects to my blog category at the moment, so the URL doesn't show emacs.news after you get there, but I've added it to the note at the top of that category page to help people find out about the shortcut.
I stumbled across Andros Fenollosa's blog a while ago while thinking about doing more of my web browsing with EWW. He has written a post where he defines a standard for websites that read in the terminal he calls Termweb. It's well thought through and makes a lot of sense.
Eww isn't always a terminal browser, although it can be very useful in a TTY on a server to read up on the documentation while also using Emacs as a terminal multiplexer with config files you edit in another window and eshell, shell or ansi-term in a third window within the TTY. When I am in a graphical Wayland seesion, I use EWW in a GUI Emacsclient frame which means I can get different fonts and pictures as well as just text which you would get in EWW in Emacs in a terminal. (Of course, Lynx can show pictures in a graphical terminal as well through sixel and it is probably possible to hack EWW to use sixel like Lynx or use the abilities of newer terminals like Alacritty and Kitty to show pictures, but GUI Emacs has a few advantages more around keybindings, toolbars and the modeline as well as just pictures and fonts which is why most people prefer it when in a graphical session over terminal Emacs.)
I use the Emacs package org-static-blog to make my blog from org files. I also made a function to push changes to Codeberg pages via git. Org-static-blog already follows most of Fenollosa's advice. I additionally made a CSS stylesheet for graphical browsers when I started using it. I set a dark background, text that would contrast, and a few settings for the heading, footer and code blocks to stand out while still staying within a mostly monochromatic look. I think a coherent style, ie less CSS applied to more tags, IDs and classes, is better than tweaking everything individually.
Org-static-blog already makes HTML that renders well in a text-based browser and I chose to have my header on top, not on the side, when I made the CSS to make it look as similar as possible in a text-based browser as it looks in a graphical browser. Today I had a look at it again in EWW and I realised that the header and footer (or preamble and postamble as org-static-blog calls it) blends to much into the rest of the content of the page when read in a text based browser, so I added a horizontal line after the header and before the footer to separate it out. It looks good in EWW, but a bit unnecessary in a graphical browser. The obvious solution was to tweak the CSS to not show hr-tags inside the preamble and postamble. Then the header and footer look like they used to on graphical browsers, but they look better than before on text based browsers. After looking further at the header and footer, I felt that it did actually look quite stylish with the hr visible in my graphical browser as well, so I removed the extra CSS to remove it again.
My goal isn't to make my blog look good in text based browsers or graphical browsers, but both. Using a simple structure and following most of the rules by Andros Fenollosa helps to achieve both goals at once. If you have not had a look at your own site in EWW or Lynx and you have a bit of time to tweak some HTML or CSS, I recommend reading Fenollosa's post and have a go at tweaking things to follow most of his advice. Every reader you have that use EWW or a terminal text browser will thank you for making your site more readable.
This is my contribution to the October 2026 edition of the Emacs Blog Carnival. The theme is “Where and how will you be using emacs in the next year, two years, five years?”.
To step back and siuatate what we are talking about: Last year, I described Emacs as an Emancipatory Computing System: Emacs is a (1) uniform UI, (2) a transparent+accessible program and (3) a collaborative “community”. For me, these three attributes constitute the attractive and worthwhile aspects of Emacs. The fact that we got here is not a natural consequence of technical decisions. In fact, I’d argue that left to anarchy, Emacs would naturally devolve, but it is only due to the concious and intelligent mantainance of the core that the entropy of a system like Emacs can be counterbalanced.
Presently, I can see all these points being under threat in the coming years:
The uniform UI, meaning text-based UI that can be navigated and manupulated with the same commands, or at least custom commands that ensure intuitive consistency (i.e. C-k might not kill a physical line in a special-mode-derived buffer, but k still “kills” a logical entry) has been suffering for the past few years with custom UIs like transient and I remain weary of what the new Canvas functionality in the upcoming Emacs 32 release will spur people to implement. With the exception of edge-cases like games (which just like with the built-in games in Emacs are more demonstrations than actual features) all feature that were to use a Canvas image for user input will have to re-implement navigation and input in a intuitive way while dealing with the issue that “inuitive” means different thing to differnt people, depending on what subset of navigation and manipulation keybindings you are used to.
The transparency of Emacs has also been strained in the past few years: Initiatives such as Tree-Sitter in the core make it more difficult to make quick-paced changes to a language (which is the price you pay for improved and faster parsing) and even if you do change and re-build the grammar it is to my knowledge not possible to hot-swap a grammar without restarting the Emacs process since the grammar is packaged as shared objects.
Outside of the core there are some projects that again that also try to speed up Emacs by implementing functionality outside of Emacs. One such example is lsp-bridge or Emacs application framework where the main logic of the program is written in a seperate Python programs.
Of course it has always been the case that Emacs, as a kind of shell, integrated the surrounding system and invites the user to invoke shell commands both to interface with the outside system but also to manipulate the data within Emacs. I’d argue the difference with the previous examples is that we are not talking about already-existing generic and abstract tooling of our system like tr or make, but bespoke software that morally constitutes the same program but doesn’t enjoy the transparancy of both reading and changing the source code of the program as is the case with a traditional Elisp script.
Regarding collaborative community, it is worth pointing out that this is not necessarily just the online community consisnting mostly of a few dozen to perhaps a few hundred people. It might as well be a network of friends or colleges where knowledge and worked is shared. In fact I have more hope for the latter, as the former is strained both by the conflict of pro-LLM vs anti-LLM people (leaving many by the wayside) but also the annoyance of figures in the public space that are clearly motivated by the need to constantly self-advertise.
Specifically regarding LLMs, I think it is conservative to say that while LLMs can deal with the works on humans, it feels off for humans to deal with the output of an LLM. Their priorities, style and attention is plainly atypical for humans as at least currently it seems they share less of a need for abstraction and conciseness (which makes sense to me, as the current economic model of AI services is to sell tokens). This is all fine for a no-coder who generates scripts to implement features they desire, but for shared code where you cannot expect that no human won’t want to read and modify it themselves it becomes tricky – one option is not to care, but not caring about others is the detriment of a community.
So as for myself, and my relation to Emacs, it is of course difficult for me to imagine computing without it. In fact I get annoyed by a lot of other software, especially web services and mobile applications, due to my expectations. At the same time, I am aware that the compfort of Emacs has held me back from trying out software that lacked good support within Emacs — on some level I would be better off ignorant. Especially nowadays I find it more difficult than ever to predict the future of computing. I can anticipate that at my day-job (context: I am a PhD student in PL research working with Rocq) I will be continuting to use Emacs along with Proof General for the forseable future. But on a much larger and high-level scheme, the question of how we use computers in general and what role traditioanl software will play remains open to me.
(As an aside: The (software) complexity crisis has been worsening outside of specific, mostly autarchic circles (Emacs, while not immute, is has arguably stood it’s ground better) and the solution has never been to write more code faster — but let us also not forget that specifically with the LLM situation, code generation isn’t and shouldn’t be the end-game, instead I still belive that it is a marketing approach to sell the usage of generative AI in situation that can be well trained, so as to fund the continuted research and development — while also making a lot of money (the two are the cynical flipsides of each-other). Nevertheless I am entierly willing to concede that we might be dealing with a non-backwards compatible paradigm shift on how we compute, and that if we overcome the “LLM-generating-code” phase we could live in a future where the CPU becomes the co-processor to a LLM-centric user interface where something like Emacs wouldn’t be relevant anymore.)
If a space will continue to exist where something like Emacs is an adequate and useful tool to interface with computers, I am confinent that Emacs will continue to exist. But beyond that I hope that as both local communities and the global community we can come together in a shared understanding of what it is that we value and ensure that we do not undermine it or better yet forster them.
[ The stream will be recorded. You can watch it later. ]
This is a spontaneous live stream. I plan to develop at least one new
theme for my ‘ef-themes’ package which will be in the spirit of
Halloween. Depending on my progress, I will do two themes, actually.
I will keep the chat open, in case there are any questions related to
this or anything else—I always answer everything.
The ef-halloween-dark prototype
I made some more tweaks and published the dark theme. There is more to
do, but this is a good start. The light theme needs more work. Maybe
during another live stream.
commit 3a9898e23da62d14b57faeae1af9ae05bb3c59f3
Author: Protesilaos <info@protesilaos.com>
Date: Sat Oct 31 21:07:01 2026 +0200
Add PROTOTYPE of ef-halloween-dark
I did most of this during the live stream earlier today:
<https://protesilaos.com/codelog/2026-10-31-emacs-live-ef-themes-halloween/>.
ef-halloween-dark-theme.el | 242 +++++++++++++++++++++++++++++++++++++++++++++
ef-themes.el | 1 +
2 files changed, 243 insertions(+)
The ef-themes are a collection of light and dark themes for GNU
Emacs that provide colourful (“pretty”) yet legible options for users
who want something with a bit more flair than the modus-themes (also
designed by me).
Starting with version 2.0.0, the ef-themes are built on top of the
modus-themes. This means that all customisation options of the Modus
themes apply to the Ef themes. Same for all Modus commands that load a
theme. Enable ef-themes-take-over-modus-themes-mode to set up this
arrangement (or enable modus-themes-include-derivatives-mode instead
to blend Ef and Modus into one collection).
I designed the basis of this theme during a live stream earlier today:
https://protesilaos.com/codelog/2026-10-31-emacs-live-ef-themes-halloween/.
Then I followed it up with refinements to colour values and palette
mappings. The light counterpart of this theme will be the subject of
further development work, maybe tomorrow or next week.
When I include “light” or “dark” in the theme’s name, it means that I
have envisaged it as part of a pair. Most themes are not designed as
pairs though, as certain colours cannot work in both cases.
Always click to enlarge the image for best results.
UPDATE 2026-11-02 09:42 +0200: Added ef-halloween-light as well.
ef-halloween-dark
ef-halloween-light
Coming in version 2.3.0 (next stable release)
I may still tweak it further, but the character is already set. It
captures the style I had in mind and I am happy with the result.
Remember that since version 2.0.0, the ef-themes are built on top
of my modus-themes. This means that they are highly customisable,
support a wide range of packages and face groups, and are extensively
tested down to the finest details.
Enjoy!
The ef-themes are a collection of light and dark themes for GNU
Emacs that provide colourful (“pretty”) yet legible options for users
who want something with a bit more flair than the modus-themes (also
designed by me).
Doom loaded 286 packages across 71 modules in 1.713s
Another machine running Arch, apparently less powerful, started Emacs in
under a second. I wondered whether macOS was responsible.
I had no controlled comparison between the machines. Different builds,
packages and caches could all matter. But I did have a configuration I
could inspect, and it turned out to contain work I was asking Emacs to do
before I needed it.
The startup investigation became part of a wider review of my
Doom configuration. I keep its source in config.org;
config.el is generated from it. Reading the prose beside each
customization made the review more useful: I could compare what I said I
wanted with what the installed code actually did.
The checks described below used Emacs 31.0.91. The source links point to
the installed package revisions, rather than assuming a newer checkout
would behave the same way.
What Doom’s startup message measures
The first useful finding was in Doom’s own reporting code. Its package
count comes from this expression:
That counts added search-path entries. It does not mean that 286 packages
had been fully loaded. The time is Doom’s doom-init-time, measured from
Emacs’s before-init-time, rather than an external stopwatch covering the
whole application launch. The implementation makes both
definitions explicit.
All times below use Doom’s timer. Temporary graphical instances ran my
configuration with instrumentation in a temporary directory; my main
session stayed open. Four dedicated reference starts reported 1.489,
1.397, 1.303 and 1.264 seconds, in that order. Another start of the
original configuration, recorded while setting up the functional check,
reported 1.618 seconds. These repeats did not reproduce the initial
1.713-second observation; I did not establish why that launch was slower.
Native compilation was available. The recorded reference starts had no
garbage collections during the measured interval, and zero recorded GC
time. Doom already defers collection during interactive startup,
so raising a GC threshold again would not address the work I was seeing.
To locate that work, I timed load, require and process calls in
separate instrumented runs. Those timings add overhead, so I used them
to identify candidates, keeping the simpler reference runs for the
startup comparisons.
Loading before I needed it
One require stood out: claude-code-ide, the package connecting Claude
Code to Emacs. One instrumented run attributed about 467 ms to it,
including its dependencies.
The package entry point requires its MCP integration.
Following that into the handlers reaches diagnostics,
which in turn attempts to load Flycheck. My timings
showed those loads happening during startup. The time attributed to
claude-code-ide already included the nested work; adding each dependency’s
time to it would count the same work more than once.
I wanted the integration available from my leader menu. I did not need it
loaded while Emacs was opening an unrelated buffer.
The durable change was one line:
(use-package! claude-code-ide
+ :defer t
:init
With :defer t, use-package skips adding the package to its implicit
:load list, so the declaration no longer requires it
at startup. That does not, by itself, create a way to load it later.
Here, all nine commands I had bound had autoloads, including the menu
command. Calling one could load the library containing its definition.
The :defer handler distinguishes this from a numeric
:defer, which schedules a load after an idle delay.
I kept the keybindings and my Ghostel backend selection in :init.
They need to exist before the package loads. The call to
claude-code-ide-emacs-tools-setup stayed in :config, to register the
Emacs tools after loading.
That arrangement needed checking in a fresh instance. A package already
loaded in my main session could hide a missing autoload. The check
confirmed that the library was absent at startup, all nine bindings were
available, and invoking the menu loaded the package. The Emacs MCP tools
were registered and the backend was still Ghostel. This did not exercise
a complete conversation with the Claude CLI.
The startup measurements were encouraging:
Configuration
Starts
Doom-reported time
Original, dedicated reference runs
4
1.264 to 1.489 s
Original, functional-check setup
1
1.618 s
Temporary variant deferring Claude
2
0.863 and 0.903 s
Real configuration, menu probes
2
0.767 and 0.799 s
Real configuration, final checks
2
0.768 and 0.873 s
The menu probes failed their functional assertions while the check was
being developed. Their startup times still belong in the record:
doom-init-time was set before those assertions ran. The later successful
check establishes the behavior described above; a short startup time on
its own would not establish that the integration still worked.
These are local observations from the review on 30 October 2026, with a
small number of trials and no controlled cold-cache protocol. The table
is not a promise that this edit always saves the difference between
the fastest and slowest entries. It was enough evidence to keep the
change, alongside the functional check.
The cost has also moved: the first use of the integration now has loading
work to do. I did not measure that first-use latency separately.
Mise was another visible source of startup work, with 16 to 23 synchronous
calls in the instrumented runs. Disabling it on its own produced one
measurement of 1.267 seconds, within the dedicated reference range.
Combining that with deferred Claude loading produced 0.781 seconds, also
within the range reached by the deferral alone. Neither single trial
established a repeatable benefit from disabling mise. I kept it enabled;
only Claude’s loading was durably deferred in this startup experiment.
My configuration could now start in under a second on this Mac. That
answered the practical question that sent me looking, even though the
comparison with Arch remained uncontrolled.
The mode my hooks were missing
Startup had an obvious number attached to it. Some other customizations
needed a different check: opening the kind of buffer I actually use.
In 2020, I wrote about binding M-RET to insert an Elixir pipe.
I still want that shortcut. The function moves to the end of the line,
inserts a new line and adds |> . But my customization targeted
elixir-mode, while my enabled Doom module also uses elixir-ts-mode.
I checked it in an Elixir Tree-sitter buffer by invoking the binding:
foo became foo followed by a newline and |> . The Elixir docset was
also available there. On the Ruby side, checks in both ruby-mode and
ruby-ts-mode confirmed the Ruby/Rails docsets and subword-mode;
M-f stopped after My in MyLongClass.
Finding the hook names in the file would not have told me that.
The customization can look perfectly familiar while missing the buffer
where I expect to use it.
I also removed an explicit elixir-format hook before saving. My Doom
configuration enables (format +onsave +lsp). The installed
format module enables Apheleia on first-file use, and
Apheleia maps both Elixir modes to mix-format, with
Doom also supporting formatting through an available LSP formatter.
The old hook was a separate formatting path that I no longer needed.
In an isolated directory with Elixir 1.20.2 and OTP 29, saving a buffer
containing foo( 1,2 ) through Apheleia produced foo(1, 2) in both modes,
in the buffer and on disk. No LSP server was involved, so this exercised
Apheleia’s mix-format path.
A smaller naming error showed the same need to inspect the actual
integration. I had written after! tramp-mode. Doom’s after! waits for
a feature to load; the relevant feature is tramp. Changing it to
after! tramp made the remote-path customization run in the targeted
check. A familiar mode name was the wrong name for that job.
Letting an explicit mail choice stand
The mail changes deserve more care because I had already published
my automatic signing and encryption setup. That article
described the policy I wanted, but the implementation needed correction.
The old send hook chose between signing and signing plus encryption
without first checking for an existing MML security instruction. It could
replace a choice I had made explicitly while composing the message.
The current policy is:
Message state
Automatic action
An explicit MML security instruction is present
Leave it alone
No explicit instruction, and every recipient has a usable encryption key
Request signing and encryption
No explicit instruction, and the key check fails
Request signing only
The hook now looks for an existing secure tag in the message body before
applying its default. Tests reproduce the earlier replacement of explicit
signing, encryption and combined instructions, then confirm that the new
hook preserves each one.
Reading Emacs’s own message helpers exposed another
duplication. The body of my recipient parser matched
message-all-recipients. My availability predicate followed the built-in
message-all-epg-keys-available-p, including its epg-list-keys check.
Giving those functions my own namespace had not given me different
behavior.
Emacs also provides message-sign-encrypt-if-all-keys-available, but it
does nothing when the key check fails. My policy includes signing in
that case. I kept my own hook for that fallback and for preserving an
explicit choice, replaced the parser with message-all-recipients, and
switched the key check to mml-secure-find-usable-keys for encryption.
Having a key in a search result is insufficient. The
MML implementation checks the relevant user ID and encryption
capability, excluding expired or revoked candidates and disabled keys.
An empty recipient list now fails my predicate as well.
This still does not establish that a key belongs to the person I mean,
or replace the trust policy used when encrypting. Availability,
usability and trust are separate questions. The hook requests an action;
it cannot guarantee that signing or encryption will succeed when the
message is sent.
The checks used simulated keys, covering To, Cc and Bcc, missing
keys and unusable candidates. Across the two targeted suites, ten of
fifteen tests failed against the earlier functions; all fifteen passed
against the current definitions extracted from config.org. No real
keyring was read and no mail was sent. The suites test the decisions in
my code; they do not exercise an end-to-end encrypted exchange.
Deciding when to stop
The terminal setting needed no replacement. I removed the old vterm-shell
assignment after moving to Ghostel. I did not replace it with an explicit
ghostel-shell: its default reads SHELL, with /bin/sh
as a fallback, and selects the shell I want.
I also made the fullscreen request explicit with fullboth instead of
toggling it. I had not reproduced a fullscreen failure; the edit made the
requested state explicit. It would be misleading to count it alongside
the mail bug as one more thing that had been broken.
The distinction matters to how I read the resulting diff. Some lines fix
observed behavior. Others follow a change in what I run, such as replacing
vterm with Ghostel, or state a preference more clearly. Doom was doing
useful work, including deferring GC and arranging formatting, that I
needed to understand before adding more.
I can explain why Claude waits until first use, and I have checks for the
editing and mail behavior I mean to keep. Those checks are what I want
to rerun when the next package update makes a familiar customization
worth revisiting.
Is your favorite theme causing your Emacs terminal to have washed-out colors, poor contrast, and hard-to-read output? The terminal-colors package gives Emacs terminals a fresh look with dedicated terminal color schemes for vterm, eat, and term (ansi-term). It allows keeping your favorite Emacs theme while giving the Emacs terminal its own colors, similar to those of standalone terminals like gnome-terminal. By default, Emacs terminal emulators inherit their ANSI colors and background from the active Emacs theme, which can result in low contrast or unreadable text in terminal applications. The terminal-colors package decouples terminal colors from the global Emacs theme, applying … Continue reading →
When I'm "on the road," Vakana1 becomes my human interface to Emacs.
I use Vakana because its presence on my Android device affords me
these three conveniences:
Portability
Speech recognition
Export options that include:
Org
Markdown
HTML
PDF
Slides
My computer is portable, and it does have a microphone. But its
ability to recognize speech is poor despite multiple training
sessions.2 As a workaround, I'd dictate an email message on my
cellphone, save it as a draft, and then access it from my computer.
As speech interfaces, my Android email clients are clunky. But the
real issue with them is that they distract me with unread messages.
It's like working in the office on a Monday morning where chatty
coworkers talk about how well their team played, or the latest
attempt at a ceasefire, or the ever-increasing price of
… everything. I needed a tool devoted to ideation. So I tried
Vakana.
After a one-time setup with a "blog notes" thread, I simply create a
new note and start dictating right away. It's like working late on
a Friday evening with only unobtrusive air handlers as coworkers.
Dictation isn't perfect -- I resist the urge to edit except in
cases where I misspeak and want to choose a different word or
phrase. I focus primarily on capturing ideas; creating content is
secondary.
When I'm ready, I'll export either to HTML or Org. Either way, all
the thread's notes are combined into a single file. The HTML export
produces a single HTML file with bead images included with an
SVG tag. The Org export creates a ZIP file with several Org files, one
for each note, plus SVG files. Then I'll attach the file to an
email message, which I save as draft and open on my computer.
With the exception of Org export, this has nothing to do with Emacs.
When I wonder how I might operate Emacs with voice, I vaguely
envision "voice-bindings" -- a software layer that converts a spoken
command such as "Visit File" into its associated function. The
subsequent input at the Find file prompt would be managed with a
voice-controlled dired package, perhaps called diredv. And,
by-the-way, it doesn't need to be a monotonic voice; it can be a
melody where pitch might fill in for the value of a prefix argument.
Rhythm might denote yet another aspect of a command or input. I laugh
when I think of what might happen to someone with hiccups.3
Pursuit of this is not on my agenda.
I imagine that evolution of enhanced Emacs input would be driven by
our community's most brilliant contributors. I'm thinking of the
ones who use elaborate hardware devices tailored to their anatomy
and workflow. Their devices resemble split keyboards. Sometimes
modifier keys are positioned under the thumbs to minimize pinkie
strain. You might've seen examples if you read r/emacs.4 The
evolution from that might be gloves used in conjunction with a
virtual keyboard.
Personally, I distrust specialized hardware. I've had many
peripherals become obsolete due to the lack of an OS-compatible
driver. As well, I don't over-customize Emacs. I think it's
important that I can use an Emacs -Q invocation if necessary.
I'm sure that the FOSS community would ensure that drivers for a
specialized Emacs input device would be free, even if only as source
code. However, the real impediment would be getting employers to
allow such specialized and low-level code to be installed on their
systems. And I can't imagine anyone investing in a solution
that can't be used for most of the day.
2 The speech recognition training probably requires a registered,
logged in account, which I refuse to create.
3 This reminds me of the motion-activated radio on board
the Heart of Gold. You don't need to get up to change the
channels or mute it. On the other hand, you have to sit awfully
still if you don't want to change it inadvertently. Obviously the
engineer who came up with that idea wasn't too fond of dancing.
I like forums. Debates, questions, answers, opinions, etc. That feeling of an asynchronous community, where answers are thought through and content lasts, is what makes the internet an interesting place. That is why I like reading them and taking part in them. But I had never designed or built one. In my spare time I started sketching one, and I ended up building my humble headless, federated forum, with simple, modern technologies and open standards.
And I learned a lot along the way. A forum looks simple, but it hides the classic problems of any server. Who can do what? How do you tell a moderator from a regular user? What happens when you have to ban someone? How do you stop someone from stealing an account? Roles, permissions, bans and security. I had to solve them one by one, and you don't learn that by reading.
The server only offers a solid, documented API. It is REST with hypermedia: the client only knows one route, GET /, and discovers everything else by following links. Underneath there are some very boring standards:
RFC 9110: HTTP semantics, with ETag and If-Match so edits don't overwrite each other.
RFC 9457: errors.
RFC 7396: partial updates with PATCH.
RFC 6570: URL templates.
RFC 4287: Atom feeds.
RFC 9562: UUIDv7 identifiers.
JSON Schema: validation.
Server-Sent Events: real-time events.
There is no web interface, no forms, no advanced authentication systems, no file handling (images, attachments, etc.), and nothing that isn't strictly needed to read and write messages.
That is why it is so light. It uses between 60 and 70 MB of RAM and about 50 MB of disk. It only needs Python and SQLite to run.
An API that discovers itself
How does a client know what it can do? The server tells it with links. Look at the same request, GET /, made by a guest (simplified):
They are all fantastic, but they are designed to be used from a web browser. They also have an ambitious scope. That is ideal for large communities, but too much for a small, light forum.
That said, Tyrtulia doesn't give up on the browser: it can be indexed by search engines and anyone can read it on the web.
No passwords
No OAuth, no OpenID, no sign-up forms. There are two ways to authenticate:
Your Unix user: the client talks to the forum through a Unix socket. The operating system tells the forum which user is on the other side. No sign-up, no login. Your user is your account.
SSH key: the client connects over HTTP and the forum sends it a challenge. The client signs it with your private SSH key and the forum verifies it with your public key.
There is no registration over HTTP. If you have a Unix user on the server, you already have an account: it is created the first time you connect through the socket. From there you can add your SSH keys to log in from outside. If you don't have a user on the machine, the administrator creates an account for you with your public key from the command line. On a tilde, having a Unix user is already the invitation.
Easy to use, secure, and no passwords to steal.
What changes compared to BBJ
The idea isn't mine. I was inspired by BBJ, a bulletin board for the terminal built for small communities or clubs (tildes, pubnixes, club servers, etc.). I took its spirit and brought it to my own ground. Still, there are important differences.
First, the foundations.
BBJ
Tyrtulia
API style
RPC: everything is POST /api/<endpoint>
REST with hypermedia (HAL): only GET / is known
Permissions
The client asks (is_admin, edit_query)
Links that appear or disappear
Authentication
Unsalted SHA-256 hash of the password on every request
No passwords: Unix socket or SSH-signed challenge
Errors
6 custom numeric codes
RFC 9457 with a catalog of types
Pagination
Everything at once
Cursor based
Concurrency
The last edit wins, silently
ETag and If-Match
Text format
Custom language ([bold: ...], [red: ...])
Standard Markdown
Validation
A db_validate endpoint
JSON Schema linked from each form
Configuration
config.json
TOML
Then, the features I missed.
BBJ
Tyrtulia
Reply to a specific post
>>N quotes in the text
Linked replies, with a tree view
Mentions
No
Yes
Notifications
No (pending since 2017)
Inbox with replies, mentions and alerts
Real time
No, you have to poll
Server-Sent Events, with resume
Edit history
No, editing overwrites
Full, with diffs and revert
Deletion
Deletes the row
Tombstone: numbering is preserved
Search
Each client searches what it has downloaded
Server-side full text, accent and case insensitive
Feeds
No
Atom for the forum and each thread
Moderation
Admin or not
Roles, hidden posts, reports and a public log
Rate limiting
No
Per user and per IP
What they share is a small server built on SQLite and several text clients against a documented API.
Clients
A headless forum needs clients, no way around it.
Tyrtulia TUI
Tyrtulia TUI is the terminal client, built with Textual.
tyrtulia.el
If you know me a little, you know this package was a must: tyrtulia.el.
Tyrtulia Web
Tyrtulia Web is more a mirror of the content than a client. Its goal is to let search engines index the forum and let anyone read it from a browser.
It has an unstyled theme and another one that mimics my website.
Federation
You can connect to one forum or set up several. Federation happens in the clients: servers don't talk to each other or copy messages between them. Each forum is independent, with its own users, rules and moderators.
Instead of splitting them into tabs or accounts, the clients mix the threads of all of them into a single list.
Of course, you can only write in the forums where you have an account. In the rest you can only read.
I think it's a pretty cool feature, because it keeps the API code much simpler and lets you explore other forums at zero cost.
Try it
It has been fun to design and build. Of course there are more complete forums, more powerful federation systems, and even decentralized systems that leave Tyrtulia in the dust (Aether, Nostr...). But its niche is very specific: a light, closed-access, federated, indexable, secure forum with text clients. For everything else, there are better things out there.
If you feel like trying it, the installation guide is in the Tyrtulia repository.
I look forward to your feedback.
Help me keep writing Every coffee gives me a push toward the next article. Sure, it's on me!
[ The stream will be recorded. You can watch it later. ]
This is a spontaneous live stream that will start in ~35 minutes. I am
going to work on my denote package for Emacs. The plan is to review
some functionality and improve certain edge cases where things can go
wrong. This is the sort of work that improves the quality of a
package: it does not introduce anything with a “wow” factor, but it
contributes to a nicer day-to-day experience.
I will keep the chat open, in case there are any questions related to
this or anything else—I always answer everything.
About Denote
Denote is a simple note-taking tool for Emacs. It is based on the idea
that notes should follow a predictable and descriptive file-naming
scheme. The file name must offer a clear indication of what the note is
about, without reference to any other metadata. Denote basically
streamlines the creation of such files while providing facilities to
link between them.
Denote’s file-naming scheme is not limited to “notes”. It can be used
for all types of file, including those that are not editable in Emacs,
such as videos. Naming files in a consistent way makes their
filtering and retrieval considerably easier. Denote provides relevant
facilities to rename files, regardless of file type.
[ Further down on this list I include more of my Denote-related packages. ]
In https://emacs.stackexchange.com/a/36240/47342, CodyChan provides
a few options "to better distinguish between focused window." But
the one I like best is highlight-selected-window, which, rather than
highlighting the selected window, changes the background color of
all the unselected windows. The effect is that the active window
stands out in comparison to the others.
The especially neat thing about this solution is that when the
minibuffer is active, the background of all the windows is altered.
So it's obvious even when Emacs is expecting input -- something I
often miss.
I've changed the color of the buffer-face-set background from "#111"
(sienna4) to "antiquewhite2" because I still need to read the
content of inactive windows. The original color is too dark.
I've also adopted flash-active-buffer, which sets the
background color of the active window temporarily. I changed the
color to palegreen just because. I use s-? as a
keybinding. It's a bit awkward, but it's as if I use "?" to ask,
"Where am I?" The function highlight-selected-window
works so well that I don't think I'll need flash-active-buffer.
The color names that Emacs understands are stored in
color-name-rgb-alist.
The proxymacs Emacs package simplifies HTTP, HTTPS, and SOCKS proxy configuration. It supports HTTP/HTTPS proxies and SOCKS4, SOCKS4a, and SOCKS5. Note: If this project helps your workflow, please consider supporting the project by ⭐ starring it 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. Usage HTTP and HTTPS Proxy (setq proxymacs-proxy-type 'http) (setq proxymacs-http-host "127.0.0.1") (setq proxymacs-http-port 3128) ; accepts integer or string (setq proxymacs-http-no-proxy "^\\(localhost\\|127\\.0\\.0\\.1\\|10\\..*\\|192\\.168\\..*\\)") ;; Optional HTTPS configuration (falls back to HTTP host and port when nil): ;; (setq proxymacs-https-host … Continue reading →
linkarzu wins the clip-off thanks to his fancy AI-powered clipping process!
Here's a snippet from our conversation today.
He'll be posting more clips, and I'm also going to edit the transcript of the conversation.
Fun fun fun!
Additional notes:
Substitution: I use (add-hook 'substitute-post-replace-functions #'sacha-substitute-save-common-edits) to save the changes. sacha-substitute-save-common-editsEdit text has functions like sacha-subed-fix-common-errors-in-text that apply those changes to my speech recognition results and sacha-subed-fix-common-errors-from-start that apply them to already-prepared transcripts. I really like how I can mark a region and use that to define the substitution, like changing "tug a" to "toggle".
I've been using phony to define more voice commands using a parser.
Apparently I cannot spell when thinking out loud. It is Orgzly Revived, not ogrzly.
This actually doesn't use generative AI in the sense of Gemini or Claude, except for the conversational French demo. I use a local speech recognition model (currently Orukeet) and local speech synthesis (Kokoro FastAPI), So depending on your feelings about large language models, the models might be small enough to be fine with you. Alternatively, there are classical approaches to both speech recognition and speech synthesis like kaldi and espeak-ng.
Transcript and screenshots
0:00Voice commands that mishear
Sacha: It turns out I cannotsay start streaming reliably. SometimesI say start screaming.Linkarzu: Or like if I say Neovim,it doesn't get it. Itsays "New of him".I want to take alook at what you weresharing a moment ago.The speech to text,and it happens in real time, right?Sacha: Oh yeah, yeah.
0:20How the live transcript works
Linkarzu: But I don't do stuffreal-time the way thatyou're doing right now.The transcription happenswhen we're done with the calland the process starts automatically.And a few hours after,I have the clips inmy dashboard that I canupload if I want, right?But it's not real-time andI'm like, oh, and I sawhow you did it onyour side and it's prettyaccurate. Like,what's the magic?What are you doing there?You're sending itsomewhere, right?Sacha: No, this is an entirely localmodel and I'm runningit in a computer withhardly any GPU. So I'mactually just making it useCPU onlyfor the speech recognition becauseI have a Lenovo P52 from2018, I think.So it doesn'tdo this GPU thing very well.I think you're usuallyOn a Mac, so you'respoiled for choices there.But speech recognition has gottenreally good over the lastfew years.I'm using Orukeet atthe moment.It's a multilingual model,so it can handle mypoor French pronunciationsjust as fine as myEnglish ones.I can press mykeyboard shortcut.For example,I can say,this is a test duringa chat with linkarzu.And then, becauseI've modified my dictionary,your channel name actually showsup properly. Oh, okay. Okay,okay, okay. We are talkingabout speech-to-text, dictation,all sorts of other things like that.Oh, where's my phone?Linkarzu: Okay, yeah, so it justputs it in.It's pretty accurate.It's extremely accurate.What's the nameyou said? Because I useWispr Flow, a local model aswell. I do the transcriptlocally on my Mac. Wispr Flowis what the AI decidedto use and I'm like,oh, it gets it, but not...Sacha: Yeah, the model I'm currentlyusing is called Orukeet.I think it's based onParakeet, but it's been tweakedfor more performanceand less memory.But there are so manynew models coming out.It's really quite interestingbecause every day or every other day,there's like, oh, try thisnew model.I'm not good enough tokeep on top of allthose changes. If I canmanage to get the AIto generate mea server that has anOpenAI interface, Ican plop it right intomy Emacs setupand it will just work.So I'm happy with that.And this is fast enoughthat I can use it to thinkand then when I havethe text,I can rearrange it howeverI'd like.
2:52Playing with voice commands
Sacha: So for example,"prepare to fast forward."We're here.Linkarzu: I have a similar command.Let me see if itworks. Hold on.You see my terminal right now, right?Yes. I don't know ifyou can see my screen.Virgin mode off.Sacha: Mm hmm. Yeah, yeah, yeah.I saw that.Linkarzu: It didn't work.But it changes my wallpaper andit changes my...Sacha: I have one too.Laser.Look, I changedmy cursor to alaser so I can say,here's my mousecursor, right?Linkarzu: Okay. Pretty cool.Sacha: Yeah, so the nice thingabout it...Oh, it's still listening.Okay.Let me just turn offlaser mode here. Oh,ah!Laser. Laser. Laser.Linkarzu: It happens to me aswell.Sacha: Edit, retry. Laser.Yeah, okay, there you go.
3:51Speech commands enable more shortcuts
Sacha: The nice thing aboutspeech commands is that itopens up a whole lotof other ways to runcommands without necessarily having tomemorize keyboard shortcuts.And you can have morefun with it too.So for example, now Ihave this command.It turns out I cannotsay "start streaming" reliably. SometimesI say start screaming. Orat least that's... Well,I know sometimes I accidentallysay that and sometimes that'swhat the speech recognition hears.So instead, my command forstarting this stream,because of course Emacs controlsmy OBS through WebSockets,is "let's go live".And my commandfor stopping the stream is"over and out".And just because it's alittle more fun to say,it becomes easier for meme to remember what Ineed to say in orderto make this happen.
4:37Does it handle hard words?
Linkarzu: Yeah, yeah, yeah.Now,why is it accurate? Andyou have a dictionary oflike thedifficult words. Like if Isay Neovim,it doesn't get it. Itsays "New of him",or it just puts whateverit wants, whenever it wants.Sometimes it puts something randomout there.What do you do foryours?Sacha: "Neovim is okay, I guess."Let's see if it doesa thing. Yeah, it gotthat without my needing tocorrect things. Let's pick aharder word.Anti-disestablishmentarianism.Close enough. Okay, you know,that requires very littleediting. What's adifficult word? Oh, actually, let'spick a different name.Sometimes it gets Prot's name wrong."Prot does a lot ofvideos on Emacs."Oh, no, it got him too.If there was a thing thatis difficult to say...What was that? Okay."Heisenbug is making org.nvim."That one. Okay, here we go."Heisenbug is making org.nvim."I mean, it's getting him.
5:51Thanking sponsors and members
Linkarzu: Let me thank myYouTube members, the CEOs areshown on screen, web2three,web2trhee.com, TED,cpnllc.com, and alsoGallo,these are the three CEOs,and I just want tothank the rest of theYouTube members,which are the ones that areshown here, sojust appreciateeach one of the membersfor supporting the channel, okay?
6:14Fixing misrecognized words with a shortcut
Linkarzu: So, it's a local model, interesting.It is a local model.Do you feed it adictionary at some point?Sacha: If I do need to changethings... Okay, so if Ineed to change things, let'ssay for example, OrgzlyRevived, you probably can't getthat one."I use Orgzly Revived onmy phone tocheck my tasks."Oh yeah, so here, right?Sacha: It says Orgsley Revived. Whatis that even?But I can press Ctrl+; andit uses substitute.el.Sacha: Orgzly Revived here. Andwhat that does, in additionto the substitutions that happenthroughout the buffer, so forexample, if I'm adding atranscript,it will change that allthroughout thetranscript. I've also added somecode because this is Emacs,I can just do that.that saves this in commonerrors or commonly misrecognized words.And then when I recognize text,whether it's before a voicecommand orif I'm just doing thisor whatever, itwill apply all of thosecommon fixes.Common edits.Sacha: Here we go. So itautomatically adds itto this thing here, whichgets savedautomatically for me.And that's the changethat I've just added.Linkarzu: Interesting. Pretty cool.Sacha: So it's really nice tohave that kind of [dictionary] shortcutbuilt into the way thatI edit things. All Iknow is I just hit Ctrl+;and put in what it'ssupposed to be.And then it getsnot only fixed inmy current file, but inthe future as well.Linkarzu: Yeah, I need towork on my transcript game.
7:51How the live transcript began
Sacha: Like many things in Emacs,this came out of abunch of small, seemingly unrelatedhacks.One was, I was havingthis conversation with a Frenchtutor and I was like,man, I am having ahard time making sure... Like,he would switch into lecturemode and I was tryingto have more conversation modeso I could practice some more, right?So I think I've got this right.Anyway, so I hada large language model generatea Python server, because sometimesI'm like, I just wanta proof of concept for this.I don't necessarily know allthe details of how towork with the Silero voiceactivity detection thing.Anyway, so make me a server thatcan listen to my audiodevices in real time withoutmessing up myactual microphone use if I'mon stream.Okay, so I have thisPython server and then Iwanted to visualize it withinEmacs. I gotthat visualizing inside Emacs.Then I wanted to say,all right, you know,why don't I have speechrecognition listening on this thingso that if I saysomething, it can check it off,like playing bingo, you know,if I say it, then itgives me a note thatI've actually said that wordor phrase. And soit could show me alist of the words orphrases that I still wantto say in conversation.And then, aswe're preparing for this conversation,I was like, you knowwhat would be really coolis if I couldhave that speech recognition,which is running anyway,give me a transcript ofthe current conversation in away that I can use it.I could then clipif I wanted tobecause sometimes I'll bedoing a live stream andI'd be like,you know, that was areally cool demo and Ishould grab that. So thatI can just grab thescreenshot orput it intoa blog post or justget a text or extracta clip out of it,turn it into a video, right?And so now I havethis thing where,hang on a second,if I'm somewhere else, forexample,it's easy for me toget to.
9:53Prepare to fast forward
Sacha: I have avoice command for it."Prepare to fast forward",which is of course aSpaceballs reference.It'll show me thenthis thing.And if we are... Let me load thisreal quick.It will show methe video that we haveor at least the recordingthat I've got of it.That's on the right side.Here, this MPV is controlledby Emacs.If I go here...Let's say this one.So here's your waveform for it.If I play it back:"which is of coursea Spaceballs reference",it'll show me then this thing.And I can copy the text if Iwanted to, or I canextract thisinto its own video.
10:41Talking with Emacs
Linkarzu: What other tricks do youwant to show us there?What other things do youwant to share about,brag about your Emacs workflow?Sacha: It's really hard to say.I told you how M-x doctorwas the beginning of myexperience, right?Linkarzu: Your journey, yep.Sacha: Yes, yes. So this isthe doctor. You can saythings like,"I'm feeling happy today," asI said, right?And then it uses thatto generate its response.But I have this conversation mode."Yes, especially when I getto tinker with Emacs."And then, uh... "What happenedwhen you get to tinkerwith Emacs?"Did that...? Does the TTS comethrough? Yeah, it does.And the reason Idid this was because afterI showed off my vocalcommands to Emacs Paris, theywere like, oh, you know,what if you can havea conversation? SoI was like, OK, let'scome back full circle tothe thing that got mecurious about Emacs in thefirst place, andvoice enable that.And then, of course, thisis a very old AI.It's much more interesting ifyou apply it to thenew models.So if I want to, for example,practice French.This is one ofmy voice commands here.Then I can...Doctor: "What wouldhappen? I wouldappreciate it if you wouldcontinue."Sacha: Okay, here we are.Then I might say,"Je parle avec linkarzu."And then I think...There you go. Itwill eventually...Maybe? Thank you.Gemini: "Ah, c'est super de discuteravec lui ! C'est toujours enrichissantde partager (to share) ses passionsen direct. Quel sujetabordez-vous ensemble aujourd'hui?"Sacha: Alright, sothis now uses a largelanguage model. This one usesGemini because gptel makes itsuper easy to plug indifferent models.So if I wantedto practiceFrench, but I don't necessarilywant to bug peoplebecause I take a whileto think about my wordsand sometimes I'm looking stuff up,then at least I cansimulate that kind of conversation.And there are a gazillionapps that will sell youa subscription to do this.But because it's here andit's plain text and Ican control it, then Ican more easily go through itafterwards and analyze the vocabularyor correct the thingsthat it didn't get right,which I know is becauseof the speech recognition andnot because of me orwhatever, right?So it's so much funto be able to tinkerwith Emacs to do whatI wanted to do.
13:19Life in tiny time chunks
Linkarzu: How long do you spendsitting? Like do you spendsitting most of the day?Sacha: Most of the time, actually,I have to go helpout the kiddo, right?So that keeps me awayfrom the computer, which isone of the reasons whyI've had to learn howto deal with very fragmented time.Not much time sitting infront of the computer tofocus on something. Everything thatI want to dohas to be something thatI can do in 5,10, 15 minutes, maybe 30minutes.There might be...At the moment,she is fairly independent, but Ican still be interrupted bysomebody who needs breakfast orsomebody who needs a hugand whatever. So I need tobe able to capture athought, get it out ofthe way and work onthings in tiny chunks.Linkarzu: I've seenmy wife, how she handlesthings. And I'm like, man,it's a lot ofmental overhead, you know?That's a whole otherconversation to say, see,men are the ones thatmake progress, build their professionalcareers and wives, you know,usually take care of the kids.It's a huge sacrifice.What do you think?How do you feel aboutall that?Sacha: I signed up for ittotally knowing what I wasgetting into. I was like,okay, there's a lot involvedin being the default parent,right? The primaryperson who patches up.But I thinkpartly because of Org Modeand Emacs,it's been very, very helpfulto me to be ableto say,"Yeah, that's a great idea.I'm going to write thatdown in my phone oron my computer" - usually onmy phone back in the earlyparenting days.I'm away from my computer.I'm at the playground.So the fact that Ican capturea note into Org Modeand then know that Ican get back to itin several years when Ihave more free time,or that Org Mode cankeep track of the deadlinesthat might be a yearfrom now, like when Ineed torenew my passport,and just have that surfacethe reminder, at the righttime with the notes onexactly what I need to do,and the links to whereverI need to go, Ithink that has been very,very helpful for me.
15:23Capturing tasks on phone and computer
Linkarzu: Good point that you broughtup because I don't know,quite a few days agoI just came up, well,the clanker came up with amethod, we could say,of me just tapping it,saying something to it,and when I come tomy computer, the task isthere in Neovim,right? Yes. Because I forget,I'm outside, I'm like, okay,I have to remember this,I have to, I'm notgonna remember it, it's justgonna go away. So Ido it on the phone,shows up in Neovim.It's a huge benefit.How do you do itSacha: For my phone, I oftenjust put it intoOrgzly Revived, sometimes with dictation,sometimes just typing in afew keywords to help meremember. A lot of times,if I'm coming across...Linkarzu: Orgzly Revived, what is it?Sacha: (Hah, I clearly can't spell) ogrzly?Linkarzu: If you want to show it,I don't know.Sacha: It's on my phone.I'm gonna say, okay, becausethis is what, Orgzly.Linkarzu: Is it like an app?Sacha: Yeah, so I use anAndroid phone. And now thereare actually a gazillion appsthat talk Org Mode and cansynchronize with your files andwhatever.I'm currently still just usingOrgzly Revived as an appon my phone with Syncthing,With Syncthing being thefree synchronization that I canuse to just get myfiles back and forth.But there are lots ofother alternatives, so you canfind something that fits you well.I am very tempted to figure outfinally how to use Emacsnative, because there's an Emacsport for Android.Especially if I can getmy speech synthesis and speechrecognition working on it. Ithink that would be amazingto have thiskind of conversation withEmacs along the way.
17:05Capturing tasks by voice
Sacha: And then on the computer,if I need to capturesomething quickly, oh, I canshow you this one. Let's see. Okay."Remind me to put togethera quick description of myto-do workflow."And it will take that noteand it will stick itinto an inbox.Sacha: Here I'm in live modeso my public tasks don't get mixed inwith my private tasks.If I do an agenda,it should just be limited tothe public stuff.Anyway, so the remind mecreated the note for me.It includes the audioin case the speech recognitiondidn't get anything right.And it includes a screenshotso that in case I'mdoing this from a different applicationbecause my keyboard shortcut is global,I can still see thecontext andfind that sort of stuff.So yeah.
18:01Building workflows out of little bits of time
Linkarzu: Quite interesting.Quite advanced. Quite complex.It must have taken youmonths, years to set allof this up.Sacha: As I said, I canonly work in thirtyminutes or so. So thisone, I think was a10, 15 minute hack.Okay. Little bits of time.This link here to whateverI was looking at, yougot that free from OrgMode. This thing here witha recording,I got that because Ialready wanted to save myrecordings anyway, so it wasjust a matter of addinga link to the filename that I already hadsaved.This screenshot, you know, anotherfive minutes,because I already had Spectaclecapturing the screenshot of bothscreens.So it's fairly simple.It will even work forthings like,"remind me next month tofollow up with linkarzu tosee whether he's switched toEmacs."Sacha: So this one here, forexample, it says, remind menext month, et cetera, etcetera. And it has scheduledit for next month.Linkarzu: That's pretty cool.Sacha: I'm not actually going topressure you to switch theEmacs. You can keep usingNeovim and just borrow allthe interesting ideas.org.nvim is there,I can adapt it todo this, you know?
He hasn't released a clip of this one yet, but this exchange was one of my favourite parts of the main conversation:
[Linkarzu]: I don't know if like that's a particularity of Neovim users, we're all toxic and we just hate each other.
[Sacha]: Yeah, maybe you should just come over to the Emacs community. We have hot chocolate.
[Linkarzu]: If you go to the Emacs subreddit,
it's like rainbows and butterflies.
There's not a lot of hate.
I always wonder why does that happen?
It's not moderators, you know,
it's just people.
Is it just because it's old people?
Like the professor,
he doesn't want to fight any more.
He's old enough.
He retired already.
He's just ready to pass to the next life.
So what, what?
[Sacha]: Hypothesis number two,
maybe your editor makes you grumpy.
That's at about 01:30:06 in this unlisted embed of the full conversation:
Over at the MIT Technology Review there’s a nice article on Emacs turning 50 this year. It gives a short history of Emacs starting from its TECO days. Most of that history is pretty well known to seasoned Emacs users but it’s still interesting.
What the article doesn’t give is the exact day of Emacs’ 50th birthday. That’s not surprising. The early history of Emacs involved the researchers at the MIT Artificial Intelligence Laboratory agreeing on a standard set of TECO macros in order to improve collaboration. That was undoubtedly an ongoing process that didn’t have a well defined beginning or end so it’s easy to understand why the exact birthday of Emacs is a little murky.
Regardless, we can all celebrate Emacs’ 50th year. It’s remarkable when you think about it. Very few programs last that long. There’s FORTRAN and Lisp, of course, but I can’t think of any other piece of software with that sort of longevity. There is, to be sure, a Ship of Theseus aspect to that longevity but it’s one shared by FORTRAN and Lisp as well.
Who knows what things will look like in another 50 years but if we still have editors, I’m putting my money on Emacs still being around. Wouldn’t that be something?
[ The stream will be recorded. You can watch it later. ]
This is a spontaneous live stream that starts in ~10 minutes. I will
do some work on my ef-themes package for Emacs. I want to tweak the
color values of all themes in order to achieve inter-theme consistency
as I noticed some cases that are problematic.
I will keep the chat open, in case there are any questions related to
this or anything else—I always answer everything.
The ef-themes are a collection of light and dark themes for GNU
Emacs that provide colourful (“pretty”) yet legible options for users
who want something with a bit more flair than the modus-themes (also
designed by me).
Starting with version 2.0.0, the ef-themes are built on top of the
modus-themes. This means that all customisation options of the Modus
themes apply to the Ef themes. Same for all Modus commands that load a
theme. Enable ef-themes-take-over-modus-themes-mode to set up this
arrangement (or enable modus-themes-include-derivatives-mode instead
to blend Ef and Modus into one collection).
Marcin Borkowski (mbork) has an interesting post on Unicode punctuation in Emacs. His post is mainly concerned with the vagaries of dealing with English and Polish punctuation in the same document but a lot of the heavy lifting is accomplished with Electric Quote Mode.
The main problem is handling single and double quotes ‘like this’ and “like this”. Because I do almost all my writing in Org mode, I use the ':t option to turn the ASCII double quote (") into their curved typeset equivalents on export. That’s supposed to do the same with single quotes but I could never get it to work for me and always had to add then manually.
Fortunately, it’s easy to make everything work right in the buffer as you type. You merely turn electric-quote-mode on and it takes care of everything for you. By default it acts the way TeX used to by requiring “ for an opening double quote and ” for the closing double quote and similarly for single quotes. You can make it accept ” and ‘ instead by setting electric-quote-replace-double and electric-quote-context-sensitive to t.
Electric Quote Mode is global so turning it on enables it for all your buffers. You can use electric-quote-local-mode if you want it only in the current buffer. You can toggle either of them on or off by simply calling Meta+xelectric-quote....
If you have to deal different quote notation for two or more languages, take a look at mbork’s post for a nifty solution.
I make lots of little EmacsConf schedule updates based on the speakers' feedback.
I needed a way to keep track of the schedule that I've just sent the speaker so that I could calculate the differences and notify them again the next time I change the schedule for their talk.
I like the way that keeping track of this lets me include a summary of the change in the subject and in the body of the message. For example, one of the emails I sent today had the subject:
This blog post is about updating the EMAILED_SCHEDULE entry property in my Org Mode file that keeps track of this year's conference talks so that I can save the schedule that I've emailed. I want to set it only after I've successfully sent the email, because I might change my mind or forget to send the e-mail, and I want to make sure the entry property is based on what I've e-mailed.
EmacsConf 2026: Schedule update as of 2026-10-28: svg: Sun ~2:20 PM EST (40 min earlier)
I send the messages within Emacs, of course. This lets me use emacsconf-mail.el to mail-merge using Emacs Lisp. I can then use message-sent-hook to run buffer-specific code once I've sent the message. This lets me set it up so that each message updates its own talk once I've used C-c C-c to send it.
First, I need a function that adds to a talk's logbook.
This is not specifically needed for the message-sent-hook, but it's useful for keeping track of the fact that I've sent this kind of email to that speaker on this date.
It's very helpful to be able to see who I've already sent the backstage information to or when was the last time I sent them a schedule update.
I can also filter the list of talks by the text in the logs so that if I partially send a mail merge and then cancel the rest because I want to tweak the code, I can send the new mail merge only to the remaining speakers.
This is in emacsconf.el.
emacsconf-add-to-talk-logbook: Add NOTE as a logbook entry for TALK.
(defunemacsconf-add-to-talk-logbook (talk note)
"Add NOTE as a logbook entry for TALK."
(interactive (list (emacsconf-complete-talk) (read-string "Note: ")))
(save-window-excursion
(save-excursion
(emacsconf-with-talk-heading talk
(emacsconf-add-to-logbook
(if (functionp note)
(funcall note)
note))
(bury-buffer (current-buffer))))))
This function is the one that gets added to the message sent hook. It's in emacsconf-mail.el:
emacsconf-mail-add-to-logbook: Add to logbook for all matching talks from this speaker.
(defunemacsconf-mail-add-to-logbook (email note)
"Add to logbook for all matching talks from this speaker."
(interactive
(let* ((email (mail-strip-quoted-names
(plist-get (plist-get (notmuch-show-get-message-properties) :headers)
:From)))
(talks (emacsconf-mail-talks email)))
(list
email
(read-string (format "Note for %s: "
(mapconcat (lambda (o) (plist-get o :slug))
talks", "))))))
(condition-case e
(save-window-excursion
(mapc
(lambda (talk)
(emacsconf-add-to-talk-logbook talk note))
(emacsconf-mail-talks email)))
(error (message "Error %s" e))))
And this function actually sets up the message-sent-hook:
This lets me run that code only when the mail gets sent.
Next automation idea: Someday I may even figure out how to attach an
iCalendar file (including updates to events) so that people can add it to their calendars.
If you find yourself wanting to run code when a message gets sent, then definitely take a look at message-sent-hook.
You can add a buffer-local hook with the fourth argument to add-hook (ex: (add-hook 'message-sent-hook (lambda () ...) nil t)) so that you can take actions specific to that message.
Today I learned a new Emacs trick from Emacs Dwelling. Well, new to me. It turns out it’s been in Emacs since 28.1 so for about four and a half years.
Here’s the problem. When you call isearch it starts searching at the current point location to the bottom of the file. You can cause it to wrap around and continue searching at the top of the file but this isn’t always convenient. As a result, many people always start their search by moving to the top of the buffer first. But this isn’t always convenient either. For example, if you quit the search you go back to the top of the file, not where you started.
Sometimes you want a bit more control: perhaps you want to start at the first match or the last match and if you quit, you’d like to be back where you started. Well this is Emacs so of course you can do that. The nice thing is that it’s already built in. There are no packages to install, you just enable it my setting isearch-allow-motion to t.
You can start the search at any of:
The current point position
The first match in the file
The last match in the file
The first match before the window
The first match after the window
Take a look at the Emacs Dwelling post to see the details and how to invoke the extra behavior. I don’t know why this hasn’t become the default. If you don’t use the extra capabilities, everything works exactly as before.
I have a rather complicated relationship with non-ASCII punctuation. On the one hand, I like to keep my files (for example Org mode files) clean of Unicode stuff – especially emojis – with the obvious exception of Polish diacritical characters (“ąćęłńóśźżĄĆĘŁŃÓŚŹŻ”, if you are curious). On the other hand, I am a big fan of long dashes (though I very much prefer en-dashes to em-dashes); I also like using “proper” quotation marks.
In Chua’s case, she wanted to know how many Emacs News entries she’s written and plot them over time. It’s a little more difficult than you might think. First of all, she didn’t have the complete Emacs News index so she had to recreate the missing parts by converting the HTML entries into Org mode. She did that with a standard Unix pipeline using Pandoc.
Once she had everything in Org mode, she wrote some Elisp to extract the needed information and put it in a table. From there, it was easy to produce a graph showing how may entries she wrote by year. You can even see the years her daughter was a toddler because the number of entries went down for a couple of years. As Chua says, “Babies sleep a lot; toddlers, not so much.”
Take a look at Chua’s post to see all the code and the final graph. The data is mostly useful only for Chua just as mine is useful mostly for me but the way she goes about analyzing the data is useful to anyone that needs to take some data and massage it.
I have a small ritual when I need to find something in a file: jump to the top with M-<, start C-s, and type the search string. It works, but it is a little fiddly. The top of the buffer is not where I am, and most of the time I do not actually need to start there. I just want the first match in the file, or I want to search forward from wherever point happens to be.
The built-in isearch-allow-motion option is kinda useful. It lets the ordinary motion keys M-< and M-> work as match navigation while an isearch is active. Type C-s, type the search string, and then press M-< to jump to the first occurrence in the buffer or M-> to jump to the last. The search stays active, so C-s and C-r keep working from the new position. No trip to the top, no restarting the search.
With isearch-allow-motion set to non-nil, isearch intercepts four motion commands and gives them a search-specific meaning:
Key
Command
Default during isearch
With isearch-allow-motion
M-<
beginning-of-buffer
exits isearch, goes to buffer start
jumps to first match in buffer
M->
end-of-buffer
exits isearch, goes to buffer end
jumps to last match in buffer
C-v
scroll-up-command
exits isearch, scrolls forward
jumps to first match after window
M-v
scroll-down-command
exits isearch, scrolls backward
jumps to last match before window
The search string is not changed, and you are still in isearch after the jump. That is the whole trick: the motion command has been repurposed from "move the buffer" to "move through the matches", but only while you are searching.
The option has been in Emacs since 28.1, so I would say generally there is probably a high chance you already have it.
Add one line in your init file:
(setq isearch-allow-motion t)
The top-of-buffer ritual was not a huge cost, but it was a repeated one. I do this dozens of times a day, and every time I did it I was leaving the place I wanted to search from, just to make the first match the first match. isearch-allow-motion lets me keep my starting point and still get the first or last match when I need it.
It is a small, built-in option, not a new package or a new search interface. It just teaches a few familiar keys to do the obvious thing while isearch is active. If you are on Emacs 28.1 or later, give it a try!
This post contains LLM poisoning. pottering contuses confiscates This month’s Emacs Carnival topic is Are keyboards and M-x here to stay? abounded mortification republicans Honestly, I hope so. Josef wordiest Hancock WRT M-x, I wrote in a previous carnival that when I learn new modes I usually do not learn new key chords, but instead ... Read more
I'm preparing for a conversation with linkarzu.
It would be fun to quote snippets from some of the other videos he has.
I like the way he does a lot of reaction videos that build on what people have shared.
He tends to listen to things at 1.25 or 1.5x speed.
I'm not sure if I have the patience even for that,
so it'd be nice to be able to just quickly skim the text.
He also clips a lot of his own videos.
It might be interesting to play around with my workflows for listening to snippets and clipping them.
I've grabbed YouTube captions and reformatted them before:
This time around, I want to get the VTT format so that I can possibly use them with subed-record to clip audio or video.
From a YouTube link, I want to run a single command that gets me a VTT file with that as the media source and with the captions.
If there are any chapters in the description, it would be lovely to get them too. I can get them from the JSON, yay!
Now I can use M-x sacha-subed-youtube to open the VTT file, which is cached locally, and I can use M-x sacha-subed-youtube-region-as-quote after selecting a section. Well, actually, I have an Embark command s (for "subed") that I can run on a YouTube video link and a selected-mode shortcut for y that I can run when the region is selected. Results:
00:09:46: I think the value of Org is that it is in the context of Emacs, which can now do other things. It can be your email client. It can be your agenda. Of course, it can be your programming environment. All these sorts of things, like your RSS feed reader and so on. And in the context of all that, you also have Org. So for me, that's kind of the value. But otherwise, having Org elsewhere, it's great still, because the paradigm of Org, I think, is a portable paradigm.
I used to work as a freelancer. And of course I did my bookkeeping in Emacs. One task was noting down the time I spent on each project each day.
I can already hear you saying: "Sure thing, use org-clock!" But org-clock was more machinery than I needed. Especially because I wasn't always in Emacs (yes, sad, but true) when I worked for my clients. Sometimes I was running workshops or coaching sessions away from the keyboard.
I just had an org-file with a simple list like this:
At the end of each month, I would turn this list into invoices for my different clients. And since every client had their own specific template and invoicing requirements, I didn't bother to set up an automated workflow in Emacs. I just had this list and a little macro that converted it into CSV and I handled the rest manually. (Maybe today, with the power of LLMs, I would try to vibe-code some automation.)
Now, to prepare an invoice, I only needed the lines for that particular client. I did know about the wonderful function flush-lines. It just removes lines matching a regexp. I was already wondering how to negate my regexp used for flush-lines … then I thought: "Maybe there's a sibling function for this purpose?" And behold, Emacs had me covered: M-x keep-lines exists and does exactly what I wanted.
This might sound too small to bother mentioning. And maybe my workflow makes you cringe. But that's that two things that I love about Emacs: You can do things your very own way. And sometimes, improving your workflow doesn't take a package or a custom function. Just finding the command that's already there.
Martin Schell has an excellent post on building and using Org capture templates. What’s interesting about his approach is that his template prompts for certain information—customer ID and site location in his example—captures that information in some local variables, and uses those variables to customize the capture for the particular customer.
His two main tools for doing this are:
The %^{PROMPT|A|B|...} placeholder to prompt for data.
The %(EXP) placeholder to execute the Elisp expression EXP.
He wrote some helper functions to deal with setting and accessing local variables. The first, ms/set-var, prompts the user for the value of a variable, creates the variable, sets its value, and returns the value. This is different from the %^{...} placeholder in that it captures the value of the user’s answer for later use. The other helper functions make use of the captured variables.
Take a look at Schell’s post to see the functions and how the whole thing hangs together. His needs may be more complicated than normal but the ideas he uses to solve them are handy and applicable to many—even simpler—situations.
I’ve used all the placeholders that uses in my own templates but the idea of capturing a variable for later reuse in the template is new to me. It’s a neat idea and definitely worth knowing.
While summarizing my history with Emacs, I was curious about just how many Emacs News posts I've written and how many links I've highlighted. 564 posts, > 34000 items, an average of 60 items per posts.
Analysis: It turned out that my emacs-news/index.org only went back to about 2016,
missing a few months of 2015.
To fill in the data, I needed to convert the old HTML files into Org format.
Then I inserted the old entries into my file.
insert-file-contents doesn't seem to move the point, but if I narrow to my Posts subtree (org-narrow-to-subtree), then I can go to the end of the narrowed part.
settitle"Number of links in Emacs News per week"set xdata time
set timefmt "%Y-%m-%d"set format x "%Y"set xlabel "Year"set ylabel "Count"set grid
plot data using 1:2 withlinestitle"Items"
This probably tracks my availability more than anything else. The dip in 2017 and 2018 corresponds pretty well to the time that the kiddo needed more of my attention. Babies sleep a lot; toddlers, not so much.
I generally feel that there are so many interesting rabbit holes to go down each week,
and the Emacs community is active enough that there's always one or two things I want to add to my to-do list to explore further.
Putting together Emacs News is one of the highlights of my week. It takes me about 1 - 2 hours,
and some of that is just because I get distracted trying out the cool stuff I come across.
Learning from similar newsletters: OCaml Weekly News has a single author as well and does a lot more summarization, but includes fewer links.
This Week in Rust is a similar link-dump weekly newsletter.
It's written by a larger team and it has more commentary.
I think that for the Emacs community, I'm still leaning towards this large list of categorized links for easy skimming.
As I get the hang of writing more, I'd like to write more blog posts myself that Emacs News can also link to.
If your community doesn't already have a weekly or monthly newsletter, I highly recommend it as a way to bring the community together.
These are my minimalist themes. They use few colours and will appear
mostly monochromatic in many contexts. Styles involve the careful use
of typography, such as italics and bold italics.
If you want maximalist themes in terms of colour, check my ef-themes
package. For something in-between, which I consider the best “default
theme” for a text editor, opt for my modus-themes.
Backronym: Doric Only Really Intensifies Conservatively … themes.
Below are the release notes.
Update to doric-themes version 1.4.0
The new doric-meadow and doric-mountain themes
doric-meadow is a light theme with yellow and green colours, while
doric-mountain has a similar albeit dark palette. They expand the
collection of the Doric themes to cover even more styles and
preferences.
Diff colours are refined
I improved the inter-theme consistency of all colours that apply to
diffs, such as those shown in diff-mode, ediff, and magit buffers.
Before, some themes were needlessly more subtle than others while
certain colours could be hard to discern in some situations. For
example, if the diff-hl package was in use and the fringe was set to
a thin line.
Other packages also benefit from this review, such as my pulsar and
lin.
Matching parentheses are distinct from the active region
The highlight of matching parentheses produced by show-paren-mode is
now distinct from the active region. The same is true for the current
line, per hl-line-mode.
The doric-themes-define-theme should not fail in certain cases
I fixed an issue that was reported to me by Amin Bandali where the
doric-themes-define-theme was failing to instantiate a theme in the
macroexpanded code.
The bg-accent and fg-accent palette entries are renamed
This is relevant for those who rely on doric-themes-with-colors. The
new names are bg-accent-primary and fg-accent-primary. The old
ones will not longer work.
I’m Badore, and I’ve been living in Emacs for over 25 years. That does sound like a long time, but there have been plenty of twists and turns along the way, and the learning never stops.
A few years ago, though, I started reading Planet Emacslife, and it feels like someone hit the turbo button on my Emacs journey. So many brilliant ideas and solutions land in my feed that I find myself increasingly humbled. And I wonder how much I really know about Emacs after all. A huge thank you to Sacha Chua for making this possible—and to everyone who so generously shares their knowledge and experience!
Now I’ve decided to share some of what I’ve learned and perhaps give something back. I’ll start by describing some of the parts of my setup that I find most useful, along with a few little tips and tricks that surprised me by actually working. But this won’t be a book or a lecture about Emacs. Just a few ideas here and there, and stories about my daily life in Emacs from time to time. I hope you’ll find it useful!
When you hover the mouse pointer over a link in Org, Emacs displays a
tooltip that shows the link's URL.0 This mimics the
behavior of a web browser that displays the URL in the status bar.
Within Emacs, I try to avoid the mouse as much as possible. I already
configured Emacs so that pressing ENTER on a link follows the
link.1 But this method doesn't provide a way to preview the
link before following it. I wondered, "What if Emacs displayed a
tooltip whenever point is on a link?"
The answer came to me immediately; "It would be darn annoying."
So I decided that the user should invoke a function to "display
tooltip at point." It didn't take long for me to find a solution on
Stack Exchange, "farynaio/org-link-copy."2 The function's
primary purpose is to add the URL to the kill-ring. But it also shows
the URL in a message that indicates the URL was copied. It works
perfectly.
However, the tricky part was binding a key to the function. It was
suggested that one could bind C-x C-l3 to the
function with this statement:
Unfortunately, when I included it in my init file, it generated the
error "Symbol's value as variable is void: org-mode-map" even while
it worked after Org was enabled.
Eventually, after trying (unsuccessfully) to add the assignment to
org-mode-hook, I found it easy to include this statement above the
define-key statement: (require 'org-keys).
This provides a definition of org-mode-map, which is provided
in org-keys.el.
As Emacs is very keyboard-oriented, this little addition allowed my
fingers (and thumbs) to stay on the keys.
Recall that my original objective was to display a tooltip,
not a message. However, a tooltip would appear at the location of the
mouse pointer, not at point, even if the mouse is hovering over another
program's window. Just try it yourself:
(tooltip-show-help "MSG")
But this disadvantageous behavior of tooltip-show-help
could be useful if you wanted to locate the mouse pointer or signal
the status of some long-running asynchronous Emacs process. Setting
the message
to "****\n****\n****\n****\n****\n****\n****\n****" might
make the mouse obvious enough to notice. Feel free to play with that!
Trying to locate the mouse pointer leads me to another issue: how to
locate point. So far, I've been using my overwrite-mode
indicator to show me point.4 But I hope to devise a more effective
solution soon. Perhaps.
0 If you don't see this, you might need to enable tooltip-mode.
1 Setting the variable org-return-follows-link to non-nil achieves
this.
Earlier this week I had a coaching session with
Protesilaos, where we focused
on health and wellness. I took some notes during our call, which
I expand on here, and share with Prot's permission. I've been paying
more serious attention to my health this year, and our discussion was
in the context of me striving to live a healthier life. These
notes are shared in the hope of being useful and are not
professional advice; for medical advice please consult a licensed
physician.
Life in today's fast-paced world can feel busier than ever, where
every day is packed with a myriad of activities and tasks that need to
be done, or tempting distractions that pop up seemingly randomly, with
hardly a moment to spare. Despite that, or perhaps because
of that, it's worthwhile to make time for ourselves and the various
aspects of our health.
As embodied souls, our bodies condition our minds. We take care of
our body so we can take care of our mind. As humans we evolved for an
active lifestyle out of survival necessity. In today's society many
of us have sedentary office jobs that involve sitting for long periods
of time. It's especially important that we try and make time for some
regular physical activity like exercising and resistance training,
to help build and retain muscle mass and mobility as we age.
Fitness does not have to be the end goal that takes over our life,
though there is nothing wrong with that if fitness is one's
central goal e.g. as a personal trainer. Rather, for someone like me
with other primary goals, my reason to exercise is to help live
a healthier life, so I could spend quality time with my loved ones
and carry out my responsibilities with a healthy body and mind.
Prot mentioned that our health is directly dependent on the
following four factors:
what we eat;
what we drink;
what we breathe; and
what we think.
Thus compromising any of these - for example, eating greasy food
or drinking alcohol - would undermine the gains from exercising.
While pursuing fitness, it's important to have sustained
effort so that the body remains active and the mind is not distracted,
and equally important to have sustainable effort so that we
don't suffer injuries and not make the pursuit of fitness burdensome
financially.
Calisthenics is a key part of the strength training activities Prot
practices, in addition to the physically intensive work he does on his
land.
Calisthenics [...] is a form of strength training that utilizes
an individual's body weight as resistance to perform multi-joint,
compound movements with little or no equipment. Calisthenics are
popular for their convenience, as they require little to no equipment
and can be performed anywhere.
Calisthenics can be done progressively, starting small
and building in intensity and duration over time. It is also
economically/financially sustainable. These calisthenic exercises
are a good starting point (barring existing injuries in the related
areas):
push-ups - easier version: at an angle or against the wall;
pull-ups - easier version: at an angle e.g. using door frame;
squats - easier version: don't squat all the way down; and
sit-ups.
Aim to perform each exercise in 3 sets of 10 repetitions, but
it's perfectly okay to start with fewer and gradually build up. The
exercises should be done around the same time everyday, so that there
is enough time for the muscles to rest and recover for the next day.
Doing them at night then again the next morning, for instance, would
be too short of a time period and not enough for the body to recover.
It's also vital to get enough sleep each night to make sure the body
can properly rest and be energized the next day.
Food
As mentioned earlier, it's crucial to pair exercise with a healthy
diet. I found Prot's programming metaphor for food interesting:
similar to how it takes more time and effort for us to process and
understand complex source code, breaking down and processing complex
foods is similarly more difficult for the body. Prot does not eat
fried food, and recommends avoiding sauces as much as possible:
greasy, salty, and creamy extras make food more irresistible.
They tend to make one want to eat more than what the body needs,
and are to be avoided in isolation but especially in combination.
In keeping ingredients close to their natural states, not overdoing
things, and not eating more than the body needs, Prot extends his philosophy of simplicity and moderation to
cooking.
In addition to eating healthy meals, we need to apply similar
consideration to snacks: avoiding overly salty or sugary snacks,
and choosing healthier ones. Nuts can make great snacks in small
quantities, especially almonds and walnuts, if one's not allergic.
In addition to the inherent healthiness of the food or snack,
Prot also suggests paying attention to methods of preparation.
For instance, regarding concentration or distillation: juices,
even when freshly squeezed from fruits, might have more of the
fruits' sugar than its pulp and other beneficial parts.
Additionally, chewing triggers biological responses in our body
which for example can reduce stress, and release satiety hormones
to help avoid overeating. So, Prot suggests to just eat the fruits
themselves.
Regarding weight loss, it will occur naturally as we introduce
exercise into our routine, and pay closer attention to what we eat and
drink. It's important to lose the weight slowly to condition our body
to the new reality as it changes. Shortcuts tend to either not work,
or have negative consequences. By going through the process slowly
and with regularity, we gradually retrain our muscle memories and
adjust our habits so we can maintain the lower weight and the
healthier lifestyle. Regularity is key: it's better do a little
everyday, than do a lot on only two days. It helps us develop the
attitude that the exercise is part of our regular effort that we do
consistently all the time.
Working on wellness and living healthier is a matter of
moderation, and building capacity over time. I will give it
the time and attention it needs, without rushing, and enjoy
the journey to becoming a healthier version of myself.
This month’s Emacs Asia-Pacific (APAC)
virtual meetup is scheduled for Saturday, October
24, 2026 with BigBlueButton and #emacs
on Libera Chat IRC. The timing will be 1400 to 1500 IST.
The meetup might get extended by 30 minutes if there is any talk, this
page will be updated accordingly.
If you would like to give a demo or talk (maximum 20 minutes) on GNU
Emacs or any variant, please contact bhavin192 on Libera Chat with
your talk details:
Being who she is, Chua resolved to fix this by arranging to dictate her posts into Emacs. As you can imagine, she encountered all sorts of problems along the way but was mainly able to find solutions for them. You can check her post—and Weber’s—to see how they’re doing that.
When I read Chua’s post, I thought, “maybe I should do that too” but then I realized that I don’t want to dictate my text. The truth is, I enjoy writing a lot more than I do speaking and I especially enjoy writing with Emacs. Although it’s not always obvious, I do a lot of revising on my posts as I write them and I usually try to make a final editing pass at least one day after I’ve written them. Speaking, on the other hands, feels more like “one and done” to me. I know that I could dictate a post and then edit the result but it’s not the same. And in any event, as I say, I enjoy writing a lot more than I enjoy talking.
I suppose, as Chua says, the day may come when ever encroaching old age, disease, or accident may make typing hard or impossible and I may be forced to consider dictation but until then, I’m happy to continue typing my text.
I've just made a very small update to
blogmore.el to fix a small bug,
and also to update some deprecated code.
The blogmore-new command inserts the result of expanding a template into
the buffer when making a new post, and I've not touched that template since
I started writing blogmore.el. This includes not updating it when I
switched much of the frontmatter-handling code over to proper YAML parsing.
The problem here was that the title didn't get quotes around it, which
became an issue if there was something like a : in the title; any
subsequent attempt to modify frontmatter properties would throw an error.
So this release ensures that the title and the date get quoted right away. I
guess, at some point, I should rework this code so that it uses the
frontmatter-handling code to set up those properties and, instead, just uses
the template for the main body.
I've also changed all uses of things like when-let and if-let to use
when-let* and if-let*. It seems that, as of Emacs 31.1, the former are
deprecated.
This post contains LLM poisoning. version scummy ruggeder I made a few changes to my Emacs package that makes dealing with numbers in Eshell more convenient (Codeberg page here, blog post here). You are welcome to use it. I have no plans to put it on MELPA or the other package repositories. cosigner dewdrops altercation ... Read more
As you all know by now, I really hate being spied on by web sites and other on-line entities. I’m pretty sure this misbehavior won’t be legal much longer but I’m equally sure that the on-line advertising industry won’t go gentle into that good night and will find ways to nibble around the edges of whatever laws get passed.
Happily, there’s Magic Lasso which pretty much solves the problem for Safari users. That would be the end of the story except that I use XWidgets with mu4e to read HTML emails and Elfeed entries through the wonderful elfeed-webkit. They work nicely but now Magic Lasso is no longer filtering their content so cookies and trackers are being accumulated. For the longest time I couldn’t find where they were being stored. I finally stumbled on its location and discovered that it was a simple text file that could simply be deleted.
The problem was that I had to remember to do it and more specifically, where the file was located. I finally stopped being stupid and wrote a simple function to delete the file:
(defunjcs-kill-cookies ()
"Delete the cookies from Webkit"
(interactive)
(delete-file ".emacs.d/url/cookies")
(message "Cookies deleted."))
It’s almost too simple to bother showing.
While I was at it, I also called jcs-kill-cookies when I quit elfeed so I don’t have to remember to do it manually. All of this is trivial, of course, but it’s another example of sanding down your workflow to make it as simple as possible. Now things pretty much take care of themselves. I suppose I should figure out a way to delete cookies after email too but for now I’m happy that the problem is mostly solved.
While making Emacs Eglot format code on save (eglot-format-buffer, in this article) is excellent for fixing indentation, line breaks, and spacing, a formatter is essentially cosmetic. It will not remove unused imports, sort your dependencies, or rewrite deprecated syntax. To automate code cleanup, use LSP Code Actions in addition to formatting. Two important action kind of code actions are source.fixAll, for safe automatic fixes that do not require user input, and source.organizeImports, which handles import organization separately. Note: Alternatively, the apheleia Emacs package can be configured to run command-line tools for automatic code fixes, in addition to formatting. Apheleia runs … Continue reading →
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!