It’s a pretty nice system but after using it for a while, Prot realized that when he was dealing with large directories a bit of initial filtering of the most recently modified files would be useful. He solved that by adapting his previous code to do the filtering. He does it by taking the list of files from his first function and running them through seq-filter to get the files he’s interested in. Again, all the code is in his post but Prot says he won’t be updating the post so you should check out his configuration for the latest version.
If you frequently generate Dired listings and would prefer to deal with a flat list of the files without worrying about what directory they’re in, take a look at Prot’s post for an excellent solution.
Denote-journal, from the prolific Prot who also made Denote, was something I wanted to try for a while. I didn’t really have a good reason to, because I’ve been using Journelly for a long time, and on my Mac or Linux desktop, I’d call a capture template that would append to the file, adding my additional entries.
But I always had one major issue with journaling on my iPhone: privacy. My iPhone is owned and regulated (with a system profile) by my workplace. So even if I’m fine writing personal notes on my Mac (I’m not, I’m iffy about it as well, I don’t trust Apple to respect my privacy much more than I trust Google), at the end of the day, these notes also sync to my iPhone.
On a day-to-day basis, for quick thoughts and work notes, Journelly and my iPhone are great, but when it comes to writing longer notes about my future plans, how I spent the weekend with my partners, and basically anything else that involves people whose privacy I respect, I always self-censor.
For this reason, I came up with a way to create private notes with Denote only on Linux. These notes only live on my Linux Desktop, with a few of those syncing to my Android1 (again, not great, but at least it’s not work-managed).
I always feel more like myself on Linux because I am more myself - whether it’s customizing my shortcuts and workflow exactly how I like it, or if it’s the built-in privacy that can be further fortified and inspected. Journaling on Linux, without the world’s biggest nanny peeping over my shoulder, is where I really open up and write my most personal thoughts.
A couple of times I looked at my Journelly notes, those that I wrote on the Mac where I could write at length and use the full power of Emacs, and compared them to my older journal on Linux. Well, there is no comparison. And while creating a private note in Denote in Linux and linking it back to the original works2, it introduces friction that hinders the flow of my thoughts.
So I looked into denote-journal, realized it’s very easy to use, and gave it a try one day, and since then:
Essentially, it’s what I used to do when I started using Journelly: refile my Journelly headers into my journal files when I get the chance. Or, as a matter of fact, I use org-refile-copy, which does exactly what it says, because I want to keep the original in Journelly. Since I now use individual journal files instead of a big file split into weeks (as I did in the past), this process is even easier. The only catch is the images.
Images in Journelly are saved in its /Journelly.org.assets folder, where my journal files can’t see them directly; and even if it did, these images are too big and cause freezes, and also oriented the wrong way, so they need at least a minimal treatment. For these reasons, I attach each image to the correct header in the journal daily file which I moved over from Journelly.
These journal notes are fantastic. I have my quick notes from the day available, but when I want to extend, all I do now is just write a new entry for that day (which looks just like the Journelly entries) and write to my heart’s content. There’s also a bonus: denote-journal allows me to make up for days if I didn’t create a “meta journal note” for the day (so far this happened only once) directly from the calendar, so if I miss a day, I go to the calendar, point at the missing day, and use denote-journal-new-or-existing-entry to take care of things. If I already have an entry, it jumps to it; if I don’t, it creates one.
Between my journal on Linux, the emails I write to other bloggers, my blog posts, and the occasional instructions I write in Denote, I think I write more than I did in my entire life. I’m thinking I need to start capturing it in some sort of book, though I have no idea what it will be about and how to edit my writings in a way that makes sense. This is only a vague concept at the moment.
Footnotes
1 : I keep skirting around this issue so I’ll just mention it quickly: I used GrapheneOS in the past, and it’s great for these kinds of things, but GrapheneOS protects your phone to an extent that certain apps don’t work.
2 : I’ve been doing this often enough that I have a whole tag in Denote called “supplemental” with additional thoughts and notes. In Journelly, I was just linking to those, and writing something like “I have more to say about this” and this would include a Denote link to the Linux-only note.
I've released blogmore.el v5.3.0.
This is a pretty small release but adds a command I realised I'd forgotten
to add a couple of releases ago.
Now that BlogMore has the concept of a post
series, and now that blogmore.el lets you add and remove a series from a
post, it makes sense that I'd want to link to a series in a post from time
to time, like I can and do with categories and tags.
So v5.3.0 adds a blogmore-link-series command. It can also be found in the
transient menu.
Emacs features have a discoverability problem, and we’re chipping away at it one
demo at a time. The years since I wrote the last one of these have yielded
several more surprising and useful finds, so it’s time again for another
“batteries included” report.
Note
This is the third in a series of articles highlighting useful but lesser-known
features included in Emacs.
“Lesser-known” is a subjective judgment. Roughly, it means that at the time of
writing, I have seen these features mentioned fewer than five times – and often
never – in the past two decades of dipping in and out of online Emacs
discourse. Some of the features covered in past entries are well known and
often recommended today. I claim no credit.
If you’re a new Emacs user, don’t start here. This is not a getting-started
guide. You will be better served by grokking basic Emacs concepts and sticking
to the most widely recommended packages. Once you’ve experienced the Emacs
equivalents of thoughts like “Why didn’t anyone think to put wheels on luggage
until 1990?”, this series might be more helpful.
My rule of thumb is that if you aren’t yet aware of undo-in-region, there is
much low hanging fruit for the picking, and you can come back to this article
after that supply has run out!
.
Veteran Emacs users tend to use some relatively niche Emacs features, but in my
experience it’s always a different subset for each user. So if you’ve been
around the block a few times, I promise there will still be surprises below for
you as well!
Same rules as before:
No packages, stock Emacs only
No steep learning curves. Learn each feature in under five minutes or bust.
No gimmicks. No doctor, tetris, snake, dunnet, zone,
butterfly… yes, we know about dissociated-press. Let’s move on.
Just the deltas. No commonly mentioned packages like Flymake, doc-view,
outline-minor-mode, gnus or eww. Nothing that Emacs brings up automatically or
a nonspecific Google search gets you.
Assume a modern Emacs, 28.1+.
Also, if you’re new to Emacs and still reading:
Emacs jargon
Modern parlance
M-x
Alt + x
C-x
Ctrl + x
Frame
Emacs window
Window
split/pane
Buffer
Contiguous chunk of text/data
Point
Cursor position in buffer
Active Region
Text selection
Region
Text selection (not highlighted)
Face
Font, color and display properties
I’m Sorry.
Okay? Let’s go:
Dictionary on hover (M-x dictionary-tooltip-mode)
Turn on dictionary-tooltip-mode to see word meanings in tooltips when you
hover over them:
Of course, tooltip-mode will need to be enabled as well, but that’s the
default.
If you have local dictionaries set up, it will try those first. Note that
Emacs’ dictionary can look up contemporary jargon and lingo too, usually via
Wiktionary:
find-file and dired with wildcards
A surprisingly little known utility of two of the most used Emacs commands: you
can use wildcards when using both find-file and dired interactively.
When finding files with find-file (C-x C-f), open multiple files at once
with a wildcard like *foo*.txt.
When opening a directory with Dired, produce a custom listing of specific
files by specifying a filename wildcard.
Here’s a demo where both features are used to clean up some (very) old TeX
compilation artifacts and then open a bunch of LaTeX files at once:
Play by play
Run Dired with a “two-level” wildcard */*_region_*: look for all files with
"_region_" in their name, but only in sub-directories.
Dired produces a listing of these files. (These are temporary files created by
AucTeX.)
Select them all (with dired-toggle-marks, bound to t) and delete them.
Run find-file with a wildcard, opening all TeX files in sub-directories.
Check the list of buffers to see that several TeX files have been opened.
(The command used to see the list of open buffers is consult-buffer, and the
completions are displayed by Corfu.)
The fact that this is possible when calling them programmatically is evident
from their function signatures. But realizing that this capability is also
available during interactive use requires reading through the full docstring,
and no one has the time for that!
In practice the Dired wildcard capability is superseded by a modern workflow
like consult-find exported as a Dired buffer by embark-export, but this
works out of the box.
List all URIs with M-x ffap-menu
Alternatives: fancy-urls-menu, my/search-occur-browse-url and co
You might be familiar with Emacs’ “find-file-at-point” feature, M-x ffap, that
checks if the cursor is on a valid file path and offers to open it.
This is accompanied by ffap-menu, a less well known but equally handy command.
ffap-menu scans the whole buffer for anything that looks like a file path or
URL and presents you with all of them:
Since it offers a completing-read interface, this opens up a small universe of
possibilities: you can export the list of (possibly filtered) completions into a
buffer, copy or open all or any subset of them, or otherwise act on them right
away with Embark.
Addendum: Listing propertized links
Many Emacs applications (like EWW) include URLs as text properties and not
plain-text links, and ffap-menu misses them. Inspired by ffap-menu, I use a
home-brew version that fetches such links as well.
Play by play
Start with EWW showing a Wikipedia page, with imenu on the left.
Call my/search-occur-browse-url, a custom command inspired by ffap-menu
Scroll through the list of page links, and scroll through the page itself.
The enhanced version:
Searching for all URLs in the buffer
(defunmy/search-occur-browse-url (&optionaluse-generic-p)
"Point browser at a URL in the buffer using completion.
Which web browser to use depends on the value of the variable
`browse-url-browser-function'.
Also see `my/search-occur-url'." (interactive"P")
(let ((matchnil)
(match-datanil)
(context (lambda (beg&optionalshrp)
(let* ((before (string-replace"\n""" (buffer-substring-no-propertiesbeg (max (line-beginning-position) (-beg30)))))
(link (string-replace"\n""" (buffer-substring-no-propertiesbeg (point))))
(after (buffer-substring-no-properties (point) (min (line-end-position) (+ (point) 30)))))
(concat (propertize" "'display'(space:align-to65))
(propertize (concat"…"before) 'face'shadow)
(ifshrp (propertizelink'face'(:inheritshadow:weightbold:underlinet))
link)
(propertize (concatafter"…") 'face'shadow))))))
(save-excursion (goto-char (point-min))
(while (search-forward-regexpmy/search-url-regexpnilt)
(push (cons (match-string-no-properties0)
(funcallcontext (match-beginning0)))
match-data))
(goto-char (point-min))
(while (setqmatch (text-property-search-forward'shr-urlnilnil))
(push (cons (prop-match-valuematch)
(funcallcontext (prop-match-beginningmatch) 'shrp))
match-data)))
(let* ((completion-extra-properties`(:annotation-function,(lambda (cand) (concat" " (cdr (assoccandmatch-data))))))
(url (completing-read"Browse URL: "match-datanilt)))
(ifuse-generic-p (browse-url-genericurl)
(browse-urlurl)))))
Compare windows (M-x compare-windows)
There are more commands for comparing buffers and files in Emacs than you can
shake a stick at: there’s diff, diff-buffers, diff-backup,
diff-buffer-with-file, dired-diff, vc-diff, and a whole constellation of
ediff-, ediff-merge- and ediff-directories- commands. I lost count at
around twenty two, and can’t remember most of them.
But my favorite diff command is the lightweight compare-windows, which does
something very obvious and simple in a context-agnostic way.
It compares the text of two windows starting from their respective cursor
positions, and stops at and reports the next mismatch. The two windows are the
active one and whatever other-window would select. Obviously less powerful,
but so much easier and faster to run than Ediff
Have you tried ediff-regions-linewise? Setting this up is a four step
process, involving selecting buffers, marking regions and calling
exit-recursive-edit repeatedly, an advanced command that most Emacs users
should never encounter!
or diff:
Play by play
Move the cursor to the beginnings of the text to compare in two windows.
M-x compare-windows
That’s it. It moves the cursors to the first mismatch and reports it.
compare-windows is only concerned with the actual text in the two windows, and
not the provenance of this text. The buffer type, modification state, file,
version-control status – all irrelevant! You can even compare a
chunk of text in a buffer against another chunk a little further down in the
same buffer by displaying it in both windows. In a silly yet effective way, it
can even compare directory contents, including file attributes:
Play by play
Two directories containing some similar-looking files.
Place the cursors on the same file in both windows.
M-x compare-windows
The cursors stop at the first reported mismatch, which is a file modification
time here.
And yes, you can call it with a prefix argument to ignore whitespace differences.
compare-windows is what you use when you find yourself playing
spot-the-difference between two views of any kind. It is my most used “diff”
command.
Compare directories with Dired (M-x dired-compare-directories)
But speaking of comparing directories, Dired does (of course) provide a less
hacky way to do that. M-x dired-compare-directories in Dired prompts for a
directory to compare with, and marks all files whose names differ in both Dired
listings. That covers the most common use case, and might be everything you
need.
But we already did that with the rudimentary compare-windows.
dired-compare-directories is an actual file-level comparison, so you can
provide custom matching predicates involving any file attribute, like
modification times or sizes. For instance,
you can mark the more recently modified version of a file with (> mtime2 mtime1),
or mark files with the same name but different sizes with (/= size1 size2)
In this example, dired-compare-directories has marked (i) files that are not
common to the two listings and (ii) files with differing modification times:
An Ediff for every season
If you want something more interactive/prescribed there is also an
ediff-directories, because there is an Ediff command for every occasion.
While we’re on the topic of spotting differences, highlight-changes-mode is a
handy way to emphasize changes to the file, and a “live” alternative to diff
commands like diff-buffer-with-file:
Play by play
Run the below code block syncing highlight-changes-mode with save-buffer.
Now changes are highlighted until the next save.
Make some changes. Notice that added/changed text is colored differently.
Save the buffer, clearing the highlights in the process.
Repeat the last two steps a couple of times.
Visualization with highlight-changes is determined only by the mode itself, and
changes are highlighted from the time the mode is turned on until it’s turned
off. In general, this is not what we want. What we would like instead is to
highlight unsaved changes
There is M-x highlight-compare-with-file, but this is non-ergonomic enough to
the point of being unusable.
. We could do this with some finesse, or just throw in a couple of hooks:
Now all changes in text-mode buffers are automatically highlighted.
highlight-unsaved as a standalone feature
The highlight-changes visualization can be customized to be more subtle, but you
probably don’t want it turned on all the time nevertheless. The above hook
logic can easily be turned into a minor-mode in its own right:
(require'hilit-chg)
(defunhighlight-changes-mode-turn-off ()
(andhighlight-changes-mode (highlight-changes-mode-1)))
(define-minor-modehighlight-unsaved-mode"Highlight all changes until the buffer is saved.":lighter"H" (cond ((not (buffer-file-name))
(user-error"Highlight-until-save-mode is only meant for use in file-visiting buffers"))
(highlight-until-save-mode (highlight-changes-mode1)
(add-hook'after-save-hook#'highlight-changes-mode-turn-onnilt)
(add-hook'before-save-hook#'highlight-changes-mode-turn-offnilt))
(t (highlight-changes-mode-1)
(remove-hook'after-save-hook#'highlight-changes-mode-turn-ont)
(remove-hook'before-save-hook#'highlight-changes-mode-turn-offt))))
Finally, highlight-changes-mode provides an auxiliary capability: you can jump
to the next and previous change in the buffer with
highlight-changes-next-change and highlight-changes-previous-change. Since
this is an independent consequence of change tracking you can use just this
navigation and turn off the change visualization with M-x highlight-changes-remove-highlight.
Actually useful file backups (vc-diff variants)
One last excursion to close out the theme of spotting and diffing changes.
This will require a tangent through the topic of Emacs backup files and is
pushing both the five minute limit and the idea of a built-in, so please bear
with me.
Let’s back up
By default, Emacs makes a periodic backup of any file you edit and save.
This backup system is usually mentioned only in the context of being something
annoying you should disable (via make-backup-files). If you want actual
backups you could just use version control, right?
If you have security concerns with sensitive files being copied to elsewhere on
disk, I sympathize. But otherwise, I think this is largely an ergonomics issue.
Emacs litters your working directory with the backups, and
doesn’t make it easy to peruse and work with the backup files.
Changing the former is a user option
For example, see backup-directory-alist, kept-old-versions and
kept-new-versions.
, but the latter is entirely the case of a missing user interface.
The external package backup-walker provides this “time-machine” interface,
along with a couple of others. But there is a simpler, satisfying fix available
that simultaneously solves another problem.
VC (until V don’t)
Emacs’ built-in VC package offers an interface for viewing past versions of
version-controlled files:
vc-diff (C-x v =)
Diffs the file against its immediate previous
version, or against a prescribed version when called with a prefix argument.
vc-ediff
Runs Ediff against the file’s previous version, or against a
prescribed version.
vc-revision-other-window (C-x v ~)
Displays a previous version (immediate or
specified) of the file next to this one.
This is a handy interface
and not git-specific, unlike magit’s versions of these commands.
, but of course they do nothing in files that aren’t version controlled.
In the spirit of getting the most out of every fiber of muscle memory, we can
extend the vc- interface for the purpose of inspecting backups as well.
The bridge
We can overload all three VC commands so they always do something useful in a
file:
If the file is unsaved, vc-diff (vc-ediff) generates a diff of (runs Ediff
on) the buffer against the file.
If the file is version controlled, run vc-diff (vc-ediff,
vc-revision-other-window) as usual.
If the file is not version controlled, diff against (Ediff, show) the latest
numbered backup, or a prescribed numbered backup when called with a prefix
argument.
This forces functions into a single consistent mental model:
Compare against the previous version, for whatever “previous” means in this
context.
As a bonus, we are also free to forget about a few different diff commands that
have been subsumed here, such as diff-buffer-with-file and ediff-current-file.
Augmenting vc-* commands
(defunmy/read-backup-file-name (file)
(if-let* ((backup-files (file-backup-file-namesfile)))
(completing-read"Backup version: "backup-filesnilt)
(user-error"No backup files available for file %s" (buffer-file-name))))
(defunmy/vc-diff (&optionalarg)
"Compare current buffer with its file, or file with backup or revision.
With prefix ARG, compare the file with a selected backup when the file
is not under version control." (interactive"P")
(if (buffer-modified-p)
(diff-buffer-with-file (current-buffer))
(condition-caseerrdata (call-interactively#'vc-diff)
(error (if (string-match-p"not under version control" (cadrerrdata))
(ifarg (diff (my/read-backup-file-name (buffer-file-name))
(buffer-file-name))
(diff-backup (buffer-file-name)))
(apply#'signalerrdata))))))
(defunmy/vc-ediff (&optionalarg)
"Run Ediff on the current buffer, file, or backup.
With prefix ARG, compare the file with a selected backup when the file
is not under version control." (interactive"P")
(if (buffer-modified-p)
(call-interactively#'ediff-current-file)
(condition-caseerrdata (call-interactively#'vc-ediff)
(error (if (string-match-p"not under version control" (cadrerrdata))
(ifarg (ediff-files (my/read-backup-file-name (buffer-file-name))
(buffer-file-name))
(ediff-backup (buffer-file-name)))
(apply#'signalerrdata))))))
(defunmy/vc-revision-other-window (&optionalarg)
"Visit the current file's past revision or backup in another window.
With prefix ARG, visit a selected backup when the file is not under
version control." (interactive"P")
(condition-caseerrdata (call-interactively#'vc-revision-other-window)
(error (if (string-match-p"not under version control" (cadrerrdata))
(ifarg (find-file-other-window (my/read-backup-file-name (buffer-file-name)))
(if-let* ((backup (file-newest-backup (buffer-file-name))))
(find-file-other-windowbackup)
(user-error"No backup files available for %s" (buffer-file-name))))
(apply#'signalerrdata)))))
See scroll all windows for a demonstration of the
generalized vc-diff working with backup files.
The apropos family
If you only use one help keybinding, it should be C-h k, describe-key, since
the very fact that every key press invokes a first-class function that you can
live-inspect and mess with can be a revelation.
If you learn two, there is a strong case for apropos being the second. It
bridges the gap between not knowing what to search for and getting a full
picture of how things are laid out. It’s a foot-in-the-door command.
But you already know apropos. What’s less evident is that apropos is a
whole family of commands that do increasingly specialized but useful look-ups
It’s ironic that the extended apropos family is itself not very discoverable.
.
Bind them all under C-h a, replacing apropos:
(defvar-keymaphelp-apropos-map:doc"Keymap for apropos subcommands.""a"#'apropos"l"#'apropos-library"f"#'apropos-function"x"#'apropos-command"v"#'apropos-variable"V"#'apropos-local-variable"u"#'apropos-user-option"d"#'apropos-documentation"C-f"#'customize-apropos-faces"g"#'customize-apropos-groups"o"#'customize-apropos-options"c"#'customize-apropos"i"#'info-apropos)
(keymap-sethelp-map"a"help-apropos-map)
prefix-help-command
You don’t need to remember any of these! If you don’t already use a prompter like
which-key, you can press C-h after the prefix C-h a to bring up a listing of the
available commands.
My favorite of these is customize-apropos: it produces a bespoke customization
buffer for perusing or changing all options matching the thing you searched for:
Play by play
Invoke the apropos map prefix (C-h a)
Press C-h to see available commands under this prefix. I used Embark for
this feature, but you should see a list of available commands no matter what.
Pick customize-apropos and search for “async”
It produces a customize buffer with all options, faces and groups matching “async”.
One of the most useful things you can do in Emacs, if you don’t like what a
keybinding does (or if you’re simply curious), is to jump to the definition of
the command it calls to see how to modify its behavior live. Normally this is a
multi-step process:
Find the command invoked by the key: describe-key or C-h k + your key
sequence.
Jump to its definition by pressing s (for “source”).
Get hacking or reading.
find-function-on-key obviates step 2, and takes you from keybinding to the
source. Bind it to a key and you’re off to the races.
C-h M-k for me, as it’s a variation of describe-key:
(keymap-sethelp-map"M-k"#'find-function-on-key)
Going from a keybinding to the source of the function in one step is a minor
shortcut for the common route, but it’s magical the first time you try it.
No video demo, because the effect is so instantaneous a video would be both (i)
confusing and (ii) underwhelming!
copy-from-above-command and duplicate-dwim
Emacs recently added some missing editing commands that have been part of most
users’ tool-belts for decades. The two most useful of these are for duplicating
text with the cursor as the destination and source. Respectively,
copy-from-above-command copies text from the first non-blank line above the
current one, similar to Vim’s C-y.
duplicate-dwim copies text on the current line (or active region) below the
current one, similar to Vim’s yy<N>p1.
In typical Emacs fashion, slight tweaks can make these commands work how your
brain does:
copy-from-above-command copies as many characters from the above line as the
prefix argument. I typically want to copy the whole line, so I change the
prefix argument interpretation to “copy the above line and comment it out”, a
very common action when experimenting with code or prose:
Note that the original prefix argument behavior still works, you can copy a
fixed number of characters from above with C-<N>.
duplicate-dwim has a choice to make about where to place the cursor after
the duplication: does the user mean to continue working with the duplicated
text or the original?
You can make that choice for yourself by setting a user option. I prefer to
move the cursor and region to the duplicated text:
Turn keystrokes into macros (M-x kmacro-edit-lossage)
Three facts about Emacs keyboard macros:
They are far more powerful than many users realize
See Mickey Peterson’s excellent article Keyboard Macros are Misunderstood
. We tend to associate macros with text transformations, but they capture
and playback any sequence of actions in Emacs, including mouse clicks. And
since this is Emacs, actions don’t have to correspond to text editing at all.
All the short video demos in this article were copied across Dired buffers,
processed with FFmpeg, renamed and inserted into the draft using a single
keyboard macro.
They require a lot of premeditation and focus to use. Unfortunately
“thinking in macros” is cognitively taxing. Any error or non-generalizable
movement can scupper the whole attempt. The multiple-cursors package and
others like it present alternative interfaces to keyboard macros that lower
the mental strain.
But they still don’t solve the “foresight” problem: I need to know before the
fact that a sequence of actions will need to be repeatable, and start a
recording. Coupled with the fact that you’re unlikely to get a complex
sequence right the first time, we’re back to the previous problem again.
Vim’s . (dot) command is a solution here, since Vim is effectively
always recording a macro of your edits, and Emacs’ dot-mode package emulates this
with some success. But these are still limited to buffer edits, and not full
fledged keyboard macros.
Well. Emacs provides the confusingly named kmacro-edit-lossage command that
addresses this foresight problem, albeit in a manual way. At any time, you can
view your “lossage”, a record of the last 300 or so key-presses with the
view-lossage (C-h l) command.
kmacro-edit-lossage takes this further, and lets you create a macro from your
key-press history at any time. The lossage is truly editable, you can and will
want to insert new commands into the lossage when creating a macro. An example
of creating a macro from a keystroke sequence that is setting up a window split:
“Oh, I need to do the complex thing I just did 200 times”
In practice, I edit macros I’ve already defined with edit-kbd-macro (C-x C-k e) more than I fashion new ones out of the lossage, but on the infrequent
occasions that call for kmacro-edit-lossage, it’s a real lifesaver
The editing process generally requires a generous sprinkling of
kbd-macro-query calls into the lossage to be truly generalizable.
.
subword-mode, superword-mode and word syntax
Emacs offers word-based navigation and editing commands (forward-word,
forward-to-word, kill-word…) and major-mode-specific syntax tables,
leaving the question of “what is a word?” up to you.
subword-mode and superword-mode are two different answers to this
question. With subword-mode turned on, each component of a CamelCase symbol
counts as a word. As the documentation helpfully illustrates:
When superword-mode is turned on, snake_case symbols like this_is_a_symbol
counts as one word
The string-inflection package provides a command to easily cycle between these
styles: this_is_a_symbol –> this-is-a-symbol –> This_Is_A_Symbol –>
thisIsASymbol –> ThisIsASymbol.
. In practice, this is less crucial than subword-mode, since acting on
symbols is already well supported in Emacs via the *-sexp commands.
More generally, it can be worth taking a few minutes to modify the syntax table
of a major mode to fix annoyances you might be experiencing with structural
navigation. In Lisp-y contexts, my most useful change is to make “:” be
considered part of a word, so that I can backward-kill-word through keywords
like :foo:
See describe-syntax (C-h s) and modify-syntax-entry for how to specify the
syntax of characters.
Manipulate image display
Almost everywhere that Emacs displays an image, you can manipulate the display
by placing the cursor on the image and pressing i. Here is an example with
images displayed in Elfeed (an RSS feed reader) and in Org mode:
Play by play
Preview an Org mode link to an image with org-link-preview
Zoom in with i +.
Subsequent zooms and rotation don’t require the i prefix because I use repeat-mode.
Do the same to the image displayed in the Elfeed entry buffer.
I used the keyboard in this demo, but you could just use the C-<wheel>
shortcut familiar from browsers and other applications.
The most useful bindings are i + and i - to zoom, and maybe i r to rotate the
image by 90 degrees. But you can do other things like cropping the image with i c – see
M-x describe-keymap⮐ image-map.
If you use repeat-mode, you don’t need the i prefix after the first invocation
either, you can repeat with just +, - or r.
This functionality is provided via a keymap placed over images, and nothing
needs to be turned on for this. Note that only the image display is modified,
not the image on disk.
In web-pages and rendered-HTML buffers, there is one more useful command:
pressing z (shr-zoom-image) will split the image into horizontal strips
across several lines, and cycle through different image sizes. It’s an odd
command, probably intended to mitigate Emacs’ display engine limitations when
dealing with large images. But mitigate it does. Useful if you visit websites
with huge images in Emacs.
Make all text visible (M-x visible-mode)
Emacs can make buffer text selectively invisible. Marking text as invisible is
the basis of all “folding” behavior – think magit-section buffers, Outline
mode, Org mode and so on. Every mode that provides folding also provides
keybindings to toggle the fold state, and pressing TAB usually works.
Usually.
In practice, these keybindings tend to be all over the place. For when you
can’t be bothered to learn mode-specific conventions because you don’t use the
mode enough
Or because the interface is bonkers, like the default outline-minor-mode keybindings.
and just want to see all hidden text, you’ve got visible-mode.
Play by play
In a buffer with outline-minor-mode enabled, run visible-mode. All text
is revealed.
Run visible-mode again to restore the previous text invisibilty state.
Switch to a magit buffer and run visible-mode for similar effects.
visible-mode is somewhat low-level, it simply disables text invisibility
across the buffer until you call it again. So if the buffer is presenting a
“reactive” UI where (un)folding text has dynamic effects, things can appear
broken until you disable visible-mode. As such, it’s intended as a temporary
measure or a debugging tool, but since it always works it’s my go-to button
for showing all buffer text uniformly with one command.
Ignore visible text (isearch-toggle-invisible)
And speaking of visible-mode, some Emacs commands like Isearch ignore
text invisibility out of the box, making it easy to search across the actual
document text
As usual, the full behavior is more complicated. Isearch also restores the
invisibility when you move out of an invisible region that was temporarily
revealed.
.
But this behavior also has a downside. When the buffer as presented is intended
as a guide, automatically revealing invisible text breaks our assumptions about
what Isearch will do. This is a problem when using Isearch as a navigation (and
not a search) tool.
You can toggle searching invisible text when using Isearch with
isearch-toggle-invisible, bound to M-s i when searching:
Play by play
With the intent to jump to one of the last headings in this Org document, Isearch for “zero”.
The search skips to a match in a folded region instead.
Cancel the match with isearch-abort (C-g)
Start Isearch again, and run isearch-toggle-invisible (M-s i)
Search for “zero”, jumping only to matches in the visible text
Exit Isearch at the desired match.
This keybinding is not arbitrary – all the Isearch behavior toggles are under
the M-s keymap, mirroring Isearch’s default binding of C-s. (But that’s a
whole article unto itself.)
Ruler (M-x ruler-mode)
At some point in its eventful past, Emacs was intended to possess WYSIWYG
word-processing features. This is not surprising, it’s difficult to find
computing applications that Emacs doesn’t implement in its own janky, special
way.
One of the byproducts of this ambition is that there are some semi-buried
WYSIWYG features lingering around. The center- commands are one such feature,
centering lines, paragraphs and regions relative to fill-column. Useful for
fancy comments in code… and not much else, unless you like to print from Emacs
buffers.
But customizable display margin and fringe widths are a welcome addition, as a
lot of functionality can be stuffed into this screen estate. The only problem
with specifying widths like margins is actually doing it.
You might think that the handily named set-left-margin and set-right-margin
commands do this, but they actually work like the center commands, indenting
the actual buffer text.
It’s surprisingly messy. There is no direct command for this, and setting the
display margins does not take effect until the window is displayed again.
ruler-mode has you covered:
Play by Play
Turn on ruler-mode.
Hover over the header-line for a tooltip with instructions.
Use S-<mouse-1> and S-<mouse-3> to set the left and right margin for the buffer.
Drag <mouse-2> to set the fill-column.
Fill a paragraph to demonstrate the changed fill-column.
Restore the margins and turn off ruler-mode.
As a bonus you can set the fill-column too.
You can also set the goal column, but that’ll have to wait.
Of course, you could use the visual-fill-column or olivetti packages for
this, but if you like to change the margins instead of toggling between preset
widths, ruler-mode is arguably even more user-friendly.
Refill text (M-x refill-mode)
On the theme of text widths, Emacs provides a handy series of fill- commands
and an auto-fill-mode for filling text as you type.
The fact that auto-fill-mode features prominently in Emacs’ tutorial,
among the first things you’re expected to learn, suggests that Emacs takes
text filling very seriously, considering it a crucial text-editing feature.
Personally, the only people I know devoting any of their attention to text
widths on computer screens are professional typesetters… and some Emacs users.
If you’re reading this you might be one of them, so I ask you: isn’t it odd that
auto-fill-mode is not actually automatic? It only wraps the line you’re on,
leaving any earlier misalignment in the paragraph (caused by pasting text from
elsewhere, say) to be fixed manually.
refill-mode is Emacs’ actual automatic text-filling feature. It ensures that
your document stays wrapped at the fill-column:
M-x refill-mode and you’re set.
Scroll all windows (M-x scroll-all-mode)
There are two commonly recommended scroll-related commands that new Emacs users
find surprising, in the sense that they make you wonder why other software
doesn’t have them.
The first is scroll-other-window, for scrolling the window that isn’t selected
without having to switch to it first. This is very handy when the next window
contains material that’s a reference for our work in this one. The second is
follow-mode (covered in an earlier installment), giving you a contiguous view
of a single buffer across multiple windows.
scroll-all-mode is almost as useful, but lesser known than these two. This
mode scrolls all windows on the frame simultaneously: very handy when you’re
looking at buffers that need to be “synced” in some way. A common use for me is
eyeballing two versions of a file without having to get locked into an Ediff
session:
Play by play
Open a specific previous backup of the current file with
vc-revision-other-window (actually my spin on it, covered above)
Turn on scroll-all-mode
Scroll the window as usual. All windows scroll simultaneously.
It’s just M-x scroll-all-mode.
Bonus: scrolling other windows and master-mode
While we’re on the topic of scrolling other windows, a common question is what
happens if you have more than two windows on screen, and the window you want to
scroll is not the “next-window” that Emacs picks
This tip is recycled from The Emacs Window Management Almanac, but I wouldn’t
blame you for missing it in the 15,000 word sea of blather.
.
One solution is the built-in master-mode, where you can pre-designate (or
live-designate) buffers that should be scrollable from other buffers.
But a more immediately useful method is to set the strategy used to find the
window to scroll. One option is
(setqother-window-scroll-default#'get-lru-window)
which will always scroll the least-recently-used window. This is useful if the
window you want to scroll contains reference material that you won’t be editing,
so the window will rarely be selected.
Alternatively, you might have two windows (of many), both of which see frequent
edits. You’d then use the most-recently-used window as the other window to scroll:
(setqother-window-scroll-default (lambda ()
(or (get-mru-windownilnil'not-this-one-dummy)
(next-window) ;fall back to next window (next-windownilnil'visible))))
Some combination of these should make scroll-other-windows always
do-what-I-mean.
Refuse to terminate (M-x emacs-lock-mode)
If you try to quit Emacs with unsaved files, Emacs refuses until you answer the
question of what to do about each of them. Annoying perhaps, but a useful
check.
emacs-lock-mode extends this idea and hands you the controls. Call it in any
buffer to “lock” it.
Until the lock is disengaged, the buffer will refuse to be killed, throwing up a
message instead:
Buffer "*scratch*" is locked and cannot be killed
and Emacs will refuse to exit:
Emacs cannot exit because buffer "*scratch*" is locked
This is handy for non-file-visiting buffers containing information you don’t
want to accidentally lose, or just as a reminder that a task in that buffer is
pending, and you shouldn’t throw away the context.
In a post Org-capture world, the former is rarely an issue, but locking is still
useful in shell and compilation buffers, websites or other special applications
that contain output or state you don’t want to lose.
Undelete frames (M-x undelete-frame-mode and M-x undelete-frame)
If you accidentally close an Emacs frame with a carefully curated workspace,
M-x undelete-frame has your back. Uh, if you’ve turned on
undelete-frame-mode, that is.
It does exactly what the more widely recommended (also built-in) winner and
tab-bar-history packages do, but for frames instead of windows. Turn on
undelete-frame-mode with your Emacs and don’t worry about closing frames
again. It can restore up to the last 16 deleted frames.
The leftovers
That’s twenty Emacs features I collided with in the last six years that have
survived contact with the reality of using Emacs in 2026. Several more Emacs
libraries I discovered by fat-fingering the keyboard ended up being more
interesting as archaeological artifacts than as reliable solutions to common
user needs. allout-mode is Org mode from a parallel universe, an outline
manager with features like Org’s speed-keys and even per-subtree encryption.
shadowfile is implementing unison from inside Emacs, with questionable
utility. double-mode is a key-translation-based input-method for typing
non-keyboard characters that predates quail. The bs library was someone’s
attempt at a smarter list-buffers command, but ibuffer blew everything else
out of the water so there’s no reason to use it.
Other ostensibly handy features, like wrapping regions with delimiters using
electric-pair-mode, didn’t make the cut because the ratio of finickiness to
utility is too high. It’s better to just use an external package like
wrap-region, smartparens or embrace for this.
Then there’s the constellation of included Org and Org-adjacent libraries (like
appt) that add interesting but obscure features to Org mode. But that’s a
whole story by itself, best covered in a dedicated article.
Finally, a lot of my serendipitous finds were libraries new and old that
are primarily of interest to Elisp developers. The thunk library is an
example of this. These too deserve their own write-up.
Still, I hope you found at least a couple of useful tips among the batteries
that passed testing. The lisp directory that ships with Emacs isn’t that big,
but somehow this barrel never runs dry. I don’t doubt that more batteries remain
to be found, even if it takes a few more years of typos to stumble onto them.
Until then, happy Emacsing!
A mini rant: This is your cue to tell me that yyp
is not a Vim command but a shining example of its command composition
language, where yy and p do different things. And mine to tell you that no
Vim user, including you or me, thinks about an exceedingly common action like
yyp this way in practice. Your fingers memorized the sequence years ago and
carry it out before you can muster the wherewithal to consider that you are
composing a yank and a paste. It’s simply not a high-level cognitive action, so
this is a distinction without a difference. ↩︎
By pure coincidence, it's six years ago
tomorrow that I
finally, after years of running Emacs with a bright
white background, moved to using a dark theme. It took a little bit of
getting used to but eventually I got very comfortable with it, and since
then have run everything I can in a dark mode too.
On occasion, in the last year or so, I've had this urge to move to something
darker. Also, in part, it's an urge to change things up a little. I felt it
was time for a refresh of how my Emacs looks. I've tried a few themes, but
none have ever stuck. When trying them I've run into various issues:
It just didn't look nice at all
Too many other things I use in Emacs didn't get themed
It looked like there was going to be too much work to do to really theme
things well
However, yesterday evening, after making an effort to simplify my mode
line, I was determined to find a
darker theme that I would be happy with. I think I finally managed!
I've settled on modus-vivendi from modus
themes. Out of the box it felt
right, and from what I can see in the documentation there's an amazing
amount of customisation you can do. The key point there too is the
documentation; there's so much of it, it's incredibly comprehensive.
For example: the default choice for the mode line is to have an unsubtle
border around it -- presumably to create a good contrast. I found that far
too distracting and was wondering what I could do about it. I didn't have to
wonder long, the documentation addresses exactly that
situation.
Another downside I ran into is that the colours that were showing in the
mode line, when I switched to mood-line yesterday, were gone. I spent a
short amount of time last night, and a good hour or so this morning, trying
to wrangle mood-line into something I liked, but I just couldn't get
anything sensible going. Eventually I cracked, fired up
Antigravity, prompted it with:
I am using mood-line for my mode line -- see
init.d/packages.d/melpa/mood-line.el and
https://github.com/emacsmirror/mood-line
I am using https://protesilaos.com/emacs/modus-themes as my theme
I would like to have finer control over the parts of the mode line I've
configured. For example, I'd like the buffer name to stand out in an
informative colour, but one that is part of the modus theme's colour
scheme.
Don't make changes yet, but help me understand how I should do this in a
maintainable way.
and then spent about 20 minutes going back and forth, refining what I
wanted; this got me a
result
I'm happy with from a visual point of view. I still need to fully review the
code and the approach it took, but it isn't too far removed from what I'd
been trying myself.
Overall I'm pleased with the result, and this is the longest I've stuck with
a new theme (at this point I'm probably about 4 or 5 hours into working in
it). I think that says something significant. I can see myself still wanting
to tweak some aspects of it though. For example, the left-hand fringe
doesn't feel quite right, in a way I can't quite put my finger on. While I
want it to stand out from the main editing area, it feels... disconnected in
some way. Also the background colour of the mode line still feels like it
doesn't quite blend how I'd like.
Now to see if this lasts...
Seriously, just the once, but that happened. I took that as a sign
from the Lisp gods that I was doing something sinful. ↩
When we call dired from Lisp, we can pass it a list of files instead
of a directory. This gives us a fully fledged Dired buffer for those
files. My most common use-case is to produce flat listing, so that I
do not have to go searching in exactly which directory some file is
(e.g. in the Downloads folder there is some zip archive that I
downloaded with a bunch of files in a complex structure).
A flat Dired listing
For a while now I have been using my own command to create a Dired
buffer from the current directory (which can always be updated on
demand with M-x cd). It is prot-dired-search-flat-list. Here is
the code:
(defvarprot-dired-regexp-historynil"Minibuffer history of `prot-dired-regexp-prompt'.")(defunprot-dired-regexp-prompt()(let((default(carprot-dired-regexp-history)))(read-regexp(format-prompt"Files matching REGEXP"default)default'prot-dired-regexp-history)))(defunprot-dired--get-files(regexp)"Return files matching REGEXP, recursively from `default-directory'."(directory-files-recursivelydefault-directoryregexpnil));;;###autoload(defunprot-dired-search-flat-list(regexp)"Return a Dired buffer for files matching REGEXP.
Perform the search recursively from the current directory."(interactive(list(prot-dired-regexp-prompt)))(if-let*((files(prot-dired--get-filesregexp))(relative-paths(mapcar#'file-relative-namefiles)))(dired(cons(format"prot-flat-dired for `%s'"regexp)relative-paths))(error"No files matching `%s'"regexp)))
I could modify prot-dired-search-flat-list to also prompt for a
directory, though I optimise for the common workflow of operating from
where I am (and I generally do not like overloading the C-u with
special cases that I will never remember—a new command with a name I
can search for is better).
Flat listing limited to last modified since DAYS
Yesterday I had the need to browse a massive directory, but only
wanted to get a couple of files out of it. I realised that I had to
filter my last modified, so I extended my above use-case with the new
command prot-dired-search-flat-list-since-days. Here is what I came
up with:
(defvarprot-dired-days-prompt-historynil"Minibuffer history for `prot-dired-days-prompt'.")(defunprot-dired-days-prompt()"Prompt for days and return them as a number."(let*((first(carprot-dired-days-prompt-history))(default(when(stringpfirst)(string-to-numberfirst))))(read-number"Number of days: "default'prot-dired-days-prompt-history)))(defunprot-dired--get-last-modified(filesdays)"Return list of FILES last modified since DAYS."(seq-filter(lambda(file)(and-let*((attributes(file-attributesfile))(last-modified(nth5attributes))(last-modified-seconds(time-to-secondslast-modified))(current-time(current-time))(current-time-seconds(time-to-secondscurrent-time))(delta-seconds(*days246060))(oldest-seconds(-current-time-secondsdelta-seconds))(_(>=last-modified-secondsoldest-seconds)))))files));;;###autoload(defunprot-dired-search-flat-list-since-days(regexpdays)"Return Dired buffer with files matching REGEXP up to DAYS since last modification.
Perform the search recursively from the current directory."(interactive(list(prot-dired-regexp-prompt)(prot-dired-days-prompt)))(if-let*((files(prot-dired--get-filesregexp)))(if-let*((files-filtered(prot-dired--get-last-modifiedfilesdays))(relative-paths(mapcar#'file-relative-namefiles-filtered)))(dired(cons(format"prot-flat-dired since %d days for `%s'"daysregexp)relative-paths))(error"No files last modified within the last %d days"days))(error"No files matching `%s'"regexp)))
Note that I always design my minibuffer prompts to have their own
history, because then I only get relevant entries when I press M-p
(previous-history-element) and M-n (next-history-element) at the
prompt (and the built-in savehist-mode takes care to persist those).
Everything is part of my Emacs configuration: https://protesilaos.com/emacs/dotemacs.
I will not be updating this article, so make sure to check for any
further refinements there.
Every so often I get the urge to change how Emacs looks. Ever since I
finally fell to the dark
side, my Emacs
has stayed looking pretty much the same. I like how it looks, but I do keep
having this urge to find a darker theme, and to also make things just a wee
bit more minimal.
At one point I was very much about, and in favour of, having as much
information as possible in the mode
line.
Eventually I realised I didn't use that much and tried to declutter
somewhat, mostly cleaning up minor mode information with
diminish.
Even then though, I had this feeling that there was still more information
in the mode line than I really needed.
So, just now, as an experiment, I've decided to start fairly clean. I've
dropped powerline and instead
decided to have a play with
mood-line. Rather than use one
of its pre-configured formats, I've had a go at rolling my own:
For any given buffer the mode line display is now:
The status of the buffer
The name of the buffer
The major mode of the buffer
The git status for what I'm working on
The cursor position
Honestly, I'm struggling to think of anything else I really need to see.
Sure, I can imagine there's the odd minor mode I might need to know about,
but generally I either have them enabled all the time anyway, or it's
something so obvious that I know when it's not enabled.
I'm going to run with this for a while now and see how I feel. I can sense
that I might want to tweak a couple of things (at the moment the left-hand
side will move when I change the unsaved status of the buffer; on the right
there's nothing that tells me that this file I'm editing right now is new to
the repo and not part of it yet), but this basic configuration feels clean
and right.
Meanwhile... the search for a theme that is
darker and I actually prefer over the sanityinc-tomorrow
themes
continues. I fear this is going to be a lot harder.
Bozhidar Batsov has a post announcing a couple of new Emacs modes for AsciiDoc. In the usual Batsov way, he’s significantly improved the support for AsciiDoc in Emacs. He loves AsciiDoc but found it hard to use in Emacs because the support was so bad.
I’ve never used AsciiDoc so I don’t know why Batsov prefers it to Org mode. Org mode can be and has been used to produce first class technical documentation. If needed, it can bring to bear all the power of LaTeX to produce a polished product.
As far as I can see, the only reason not to use Org mode is because you’re not an Emacs user or are collaborating on a document with someone who isn’t. It offers everything you’re apt to need short of bringing in a heavyweight dedicated document preparation system. I’m sure Irreal readers not as ignorant as I about the situation can tell us why we should prefer AsciiDoc. Our lines are open.
If you are an AsciiDoc user, Batsov’s two new modes are sure to be a blessing. The update to adoc-mode is designed to work with all versions of Emacs and updates the regex-based font-locking to the current AsciiDoc grammar. It’s perfect for an AsciiDoc user who’s running an older version of Emacs. Asciidoc-mode is the more modern solution using tree-sitter to handle things like font-locking but it only runs on Emacs 30.1+.
Take a look at Batsov’s post for the details on the two modes. And if you know why an Org mode user should care, leave a comment.
Another quick update to
blogmore.el, again to fix an issue
I've run into with the new frontmatter-handling code. This time it's to
address an actual crash that could happen if a property was available but
empty. For example, if a post had frontmatter that looked like this:
And I then went to use blogmore-add-tag, I'd get a crash saying:
Wrong type argument: sequencep, :null
The reason being that tags was being parsed with a value of :null,
rather than (as before) having a value of nil (which of course meant I had
a nice empty list to do things with). It was an easy enough fix.
At this point I think I've managed to shake out any serious issues with the
proper YAML-parsing approach to frontmatter, as I've used it to write a
handful of posts now.
A quick little update to
blogmore.el to fix a couple of
issues introduced by the new YAML-parsing approach to reading frontmatter;
both pretty much stemming from how falsy values are handled.
Simply put, both boolean false values, and also empty values (something
that could commonly happen with tags and series) would end up showing up
in the frontmatter as null. This release handles that situation.
Also, under the hood, I cleaned up some repeated boilerplate related to how
the cached dump calls to BlogMore took place.
The code for categories, tags and series data was almost exactly the same,
save for the actual name of the thing being dumped. So I turned it all into
a macro:
(defmacroblogmore--cache-dump(dump-name)"Generate a function to get DUMP-NAME from BlogMore, with caching."(let((cache-name(intern(format"blogmore--current-%s-cache"dump-name)))(getter-name(intern(format"blogmore--current-%s"dump-name))))`(progn(defvar,cache-namenil,(format"Cache for the list of %s from existing posts."dump-name))(defun,getter-name(),(format"Get a list of %s from existing posts."dump-name)(or,cache-name(setq,cache-name(blogmore--list-of,(symbol-namedump-name))))))))
and now the defvar that creates the variable that holds the cache, and the
defun that creates the getter function for the data, are reduced to this
for all three collections of values:
Sure, I probably could have done all of this in a single global, a central
getter function, and a hash
table,
but the macro approach feels so much more elegant, and more... lispy.
And it’s understandable — things are getting worse and worse all the time, and anybody who is running a web site (that has interesting information) is under constant attack from badly programmed AI scrapers.
But where does that leave us li’l smol peeps who are just scrapin’ a li’l data for ourselves so that we don’t have to type as much?
I’ve got two small use cases that have been torpedoed by this arms race lately — I use the imdb search to find the data on movies I’ve ripped from blu rays that I’ve bought. And I use the Goodreads search when I’m entering (manually) e-books that I’ve bought into the Emacs package for that. (Physical books have ISBNs printed in bar code form, so I can use various APIs for that and don’t need to resort to anything as tawdry as web scraping.)
These are just minor convenience things I’ve gotten used to over the years, so I could give them up… or I could go raging, raging against the dying of the open web.
First try to fetch the URL using the normal, fast method.
If this fails, use Selenium headless. This involves spinning up a web browser and then dumping the resulting DOM.
If this fails, spin up Selenium and a web browser window. This will allow the user to click around a bit, answering any challenges.
In 2) and 3), fetch-dom will save and reuse cookies, so that
hopefully 3) doesn’t happen as much, and 1) and 2) will be successful
more often.
So this requires a Python/Selenium installation that works, and
Chromium installed.
fetch-dom is synchronous by default, but is asynchronous if you give it the :callback keyword parameter.
This seems to work for my use cases — things usually work automatically, but once in a while it pops up a browser window, and I click a bit, and then things work headlessly for a while again.
I was asked recently, "Do you use Org Mode protocol ... for browser
to Emacs interaction? If so, were there any complications to set it up
on Windows? Is there like bookmarklets for capturing or doing
things?"1
At the time I wasn't doing anything too fancy to incorporate Web
Content into Org. I'd copy the URL from Vivaldi's address bar to the
clipboard and (if applicable) the Web Content I'm interested
in.2 Then
in Org, I'd yank the Web Content (if applicable), mark it and then do
org-insert-link (C-c C-l) to turn it into a hyperlink. The marked
text becomes what you see in the document. But I usually just specify
the link Description simply as "LINK." In fact I created a macro
(bound to the Insert key) to do this.
But I've started to use eww as my web browser for this. If I write
something that I need to verify, I'll mark it and invoke
eww-search-words (M-s M-w), which brings me to a DuckDuckGo page of
search results. If I follow a search result that I like, I'll mark a
small section and invoke org-store-link (C-c l) and create the link
with org-insert-link, with the marked content as the Description.
Thus org-store-link captures both the location and the context in one
magical swoop. Here's an animated GIF that illustrates the process.
Note that the video shows me capturing and referring to part of Sacha
Chua's website that shows some neat solutions to launching a web
browser from Emacs.3
If you follow writers at all, you’ll have noticed that many prefer to write their prose in long hand even if they began their writing career with a computer. Tess Gerretsen and Neal Stephenson are two examples that come to mind. Both are serious, established writers who have no need for performative resistance to computers. Indeed, they both use computers to produce the final manuscript. They say they write in longhand to slow things down or to avoid premature editing.
I understand their urge but could never work that way. Writing by hand is excruciatingly slow and even painful to me. I didn’t start writing a lot until I got a computer and could do it without so much effort and discomfort. A middle ground is what’s often called a “writerdeck”. The idea is that it’s a device that’s completely dedicated to writing and is as simple as possible.
Chris Maiorana wrote an article that argued a pad and pen is the ultimate writedeck. I reject that for the reasons I stated above. Maiorana recognizes that often a computer is a good idea and has an article that describes building his own writerdeck. He wanted something free or cheap so he used a Raspberry Pi 4 that he had lying around, put Linux and Emacs on it, and restricted it to text mode. Later he added a few utilities such as Git but it remains a text only machine dedicated to writing with Emacs. Maiorana considers it the “maximum digital minimalism” but I’m more inclined to think of it as a minimal acceptable writing environment.
I don’t do social media other than Irreal, if you consider that social media, so I have no difficulty concentrating on my writing while using my everyday laptop. Not everybody is like that, of course, so if you find it’s hard to resist doom scrolling while you’re supposed to be writing, you may want to take a look at Maiorana’s post on his writerdeck.
"...how can people keep up with what you're learning?"1
I've always kept a low profile on the Internet. It can be a
scary place! But I've been asked to show glimpses of my online
work to the people I was meeting in real life.
I agreed to chat with Sacha Chua about Emacs and Life
(but not the Universe -- that'll be next time). Emacs is worth
revealing oneself for. If people really do want to keep up
with what I'm learning, they can visit here.
My init file2 shows only a small aspect of how I have Emacs
configured. You'll want to see an example of my org file(s),
my diary file, and the Lisp I have tucked away under my home
directory.
Aside from Emacs, my workflow relies on Vivaldi and LibreOffice, so
I'll try to show what I'm doing with them, as well. Perhaps I
can provide screen recordings, too!
"Sometimes it just takes somebody saying, your stuff is
interesting. I'm telling you, your stuff is interesting."3
Rendering an Emacs status bar as an SVG image, like with my svg-line package (demo), really is several times heavier than the native text engine. The skeptics were right. But it doesn't matter in practice: even a fully-repainted 3000-pixel ultrawide bar holds ~77 fps, and a thin inline animation rasterizing a fresh frame every tick runs at 130–520 fps. To put that into perspective, this is still well above the 60 fps that high-frame-rate games target. More than enough for a text editor if you ask me!
I got two real take-aways from this benchmark. First, the rendering cost of SVG scales with the SVG's pixel area, not with what's drawn on it (text and icons cost the same as you'll see). Second, in any real Emacs config the bottleneck is the content query (I'm looking at you, git), not the renderer (I'm looking at you, SVG, with doe eyes).
2. Why Benchmark This?
The skepticism is reasonable. Emacs is optimized for a small number of cached images, while a status bar that changes on every redisplay can generate a new image many, many times per second. Intuitively, that should be slow.
But other Emacs SVG projects show smooth 60 fps animation, as one redditor pointed out, and anecdotally svg-line felt fast on my end.
I felt compelled to measure it, so I did, with a self-contained harness (to borrow the redditor's term) that runs in bare emacs -Q.
3. Results: SVG vs Native Text Engine
You can run the benchmark yourself, in a bare emacs -Q or your own Emacsen. My setup is Emacs 31.0.50 (native-compiled), librsvg (linked into my build), on a 2025 M5 Mac running Tahoe 26.1.
Three renderers draw the same thing at three widths (a narrow split, a normal window, and an ultrawide monitor): an SVG bar of text, an SVG bar of Nerd-Font icon glyphs, and (as the baseline) the native text engine drawing the same characters.
3.1. SVG Rendering Text
SVG / text
mean ms
min ms
max ms
mean fps
min fps
narrow 800px, 65c
3.44
3.09
3.96
290
253
medium 1800px,149c
7.49
6.98
7.91
134
126
large 3000px,249c
12.91
12.39
13.37
77
75
3.2. SVG Rendering Icons
SVG / icons
mean ms
min ms
max ms
mean fps
min fps
narrow 800px, 65c
3.60
3.37
3.80
278
263
medium 1800px,149c
7.85
7.47
8.53
127
117
large 3000px,249c
13.31
12.67
14.10
75
71
3.3. Built-in Rendering Text
BUILT-IN / text
mean ms
min ms
max ms
mean fps
min fps
narrow 800px, 65c
0.16
0.13
0.23
6377
4378
medium 1800px,149c
0.21
0.19
0.37
4789
2686
large 3000px,249c
0.26
0.24
0.33
3842
3046
3.4. Analysis
SVGs perform at all widths. Even the large 3000-pixel bar (an ultrawide-monitor width), repainted every frame, stays above 60 fps: ~77 fps for text and ~75 fps for icons, and the worst trial across the run is still 71 fps.
Text and icons cost essentially the same (290 vs 278, 134 vs 127, 77 vs 75). The cost is dominated by rasterizing the SVG as a whole, so what is painted on it doesn't seem to matter. The relevant correlate is the SVG's pixel area.
The native engine is ~20–50× faster. The skeptics are right that SVG is heavier because a monolithic image is re-rasterized in full whenever any pixel changes, while the text engine updates incrementally from cached glyphs, and the gap widens with width for that reason. But!!! This doesn't matter in practice. Even in the worst case, SVG never drops below the gaming gold standard, which is more than enough for a text editor.
4. A Live Inline Animation
Why talk frame rates without an animation? Here, three purple icons (Emacs, Org, Image) glide back and forth along a thin white bar, measured across three widths.
line animation
mean ms
min ms
max ms
mean fps
min fps
small 300x56px
1.94
1.77
2.26
517
443
medium 700x56px
4.03
3.75
4.55
248
220
large 1400x56px
7.45
7.04
7.77
134
129
An inline animation rasterizing a fresh SVG every frame runs at 130–520 fps, and the worst trial is still ~129 fps.
NOTE: My screen recording can't convey the real frame rate because my screen-capture tool is capped at 60 fps.
Run the demo yourself (below) to see the true smoothness.
5. How I Measured It
The harness renders a status-bar-shaped image (or, for the baseline, native text) into a buffer, re-renders it every frame, forces a redisplay, and times the wall-clock per frame in milliseconds (which the tables also report as fps).
(defunsvg-bench--time(render-fn reps)"Render REPS frames, forcing redisplay, and return ms per frame."(let((gc-cons-threshold most-positive-fixnum))(clear-image-cache)(funcall render-fn 0)(redisplay t); warm-up, untimed
(let((t0 (float-time)))(dotimes(i reps)(funcall render-fn (1+ i))(redisplay t))(/ (* 1e3 (- (float-time) t0)) reps))))
Some details on what makes this demo reflect practical use of SVG in Emacs:
I ran it in a real GUI frame. If you ran this with --batch or emacs -nw there's no display and rasterization is skipped.
I set gc-cons-threshold during timing and I discard a warm-up frame (because the first render loads librsvg).
I force a miss in Emacs's image cache. Emacs caches each rasterized image by its full data string, so if a frame's string repeats, the cache serves the bitmap and the benchmark measures a cache hit rather than a real render. Rotating through a small pool of content (say ten icons, or a short alphabet) repeats every few frames. So each frame in my benchmark carries a 2px per-frame colour marker (a tiny non-text rectangle) that makes the whole-image data string unique and guarantees a genuine re-rasterization.
6. In Practice, the Bottleneck Is Not the SVG
The benchmarks above use synthetic content precisely so they measure the rendering, not the performance of any indicator you might put in a status bar.
In a real configuration, a single synchronous git query in the version-control segment runs ~7.6 ms, which is on the order of painting the entire SVG bar, and far more than rendering the one segment it occupies. A native mode line showing the same version-control state would pay this cost too.
When an Emacs status bar feels slow, an I/O-bound content function is almost certainly the reason.
7. Conclusion
SVG status bars are several times more expensive than the native text engine — and also far faster than 60 fps, even at ultrawide widths. The native engine keeps an order of magnitude in reserve, but SVG has plenty for the job, and in practice the slow part of a status bar is the content it queries, not the engine that paints it.
8. Run it on Your End
The harness is one self-contained file, svg-bench.el, in my Emacs configuration repository (built-in svg and cl-lib only). In a graphical Emacs:
emacs -Q svg-bench/svg-bench.el
Then M-x eval-buffer, and run any of the demos — svg-perf-text, svg-perf-icons, builtin-perf-text, svg-perf-animation. The full report comes from svg-perf-benchmark (the three status-bar tables) and svg-perf-animation-benchmark (the animation table).
Regular readers know I have a soft spot for
AsciiDoc – I’ve written about it more than once, and
it’s the markup behind the documentation of most of my bigger OSS projects
(CIDER, nREPL, Projectile, RuboCop). It hits a sweet spot Markdown never quite
reaches: rich enough for proper technical writing (admonitions, includes, real
tables, cross-references), without dragging in the full ceremony of something
like DocBook.
There was just one problem, and it nagged at me for years: editing AsciiDoc in
Emacs was never much fun.
Background
For years the only real option was adoc-mode, and it was showing its age. The
font-locking was uneven, it predated modern Asciidoctor syntax, and it lagged
far behind what markdown-mode (and org-mode) offered. So I’d catch myself
reaching for Markdown in situations where AsciiDoc was clearly the better
tool – not because Markdown was better, but because markdown-mode was. That
always bugged me.
Eventually the itch got bad enough that I did something about it. Back in 2023 I
took over maintenance of adoc-mode,
which had been dormant for years, and set myself a clear goal: get it to feature
parity with markdown-mode. More recently I also started a brand-new
asciidoc-mode built on top of
tree-sitter. The target for both is the same – AsciiDoc support in Emacs that’s
on par with Markdown, and ideally not far off from Org.
Less is more, unless it’s modes
Why two modes? They scratch slightly different itches:
adoc-mode is the classic, regexp-based mode. It works on any reasonably
modern Emacs, has no external dependencies, and now ships a pile of
interactive features (markup commands, list editing, image preview, tempo
templates).
asciidoc-mode is the newer, tree-sitter-based mode. It leans on a real
grammar for robust, accurate highlighting and good performance in large
documents. It’s leaner by design, and it needs Emacs 30.1+ with tree-sitter.1
If you want the kitchen sink on any Emacs, reach for adoc-mode. If you’re on a
recent Emacs and want highlighting backed by an actual parser, try
asciidoc-mode. Pick whichever fits your setup – both are actively maintained.
Recent developments
I’ve spent a good chunk of the past month bringing both modes to a place I’m
genuinely happy with, and they’re now mostly feature-complete as far as I’m
concerned. Some highlights:
Modern AsciiDoc, not legacy AsciiDoc.py. Both modes now track the modern
Asciidoctor spec – something I’d
wanted to do for a while.
+text+ and ++text++ are treated as passthroughs rather
than monospace, curved-quote syntax ("`text`") is recognized, the block
ID shorthand [#id] is supported, checklists are highlighted, and the menus
and templates dropped a bunch of deprecated AsciiDoc.py leftovers.
More uniform font-locking. I went through the faces in both modes and
aligned them with markdown-mode and org-mode: bold renders as plain bold,
emphasis as plain italic, structural markup (delimiters, list markers) fades
into the background, and dedicated semantic faces cover metadata, URLs,
footnotes, highlights, and roles. Switching between Markdown and AsciiDoc no
longer feels jarring.
Navigation that actually works. Clickable cross-references and links, an
xref backend over anchors, context-aware completion, and – for adoc-mode
– cross-file Antora cross-reference resolution.
Tooling integration. Asciidoctor-backed preview/export and a Flymake
checker that reports parser errors inline.
Native code-block fontification.[source,LANG] blocks in asciidoc-mode
are highlighted using the language’s own major mode, just like in
markdown-mode and org-mode. adoc-mode had added support for this a while
back.
Getting asciidoc-mode where I wanted it also meant sending a few fixes
upstream to the
tree-sitter-asciidoc
grammar it builds on. A grammar-backed mode is only as good as its grammar, so
some of the work happened a layer below Emacs. If you’re curious about that side
of things, I wrote up the broader experience in
building Emacs major modes with Tree-sitter.
Give it a try
If you’ve been avoiding AsciiDoc in Emacs because the tooling wasn’t there – I
get it, that was me too. But the situation is genuinely different now. Grab
adoc-mode or
asciidoc-mode, point it at one of
your .adoc files, and see how it feels.
And please share your feedback. Both projects are at the stage where real-world
usage surfaces the rough edges I can’t see myself. Bug reports, ideas, and PRs
are all very welcome on the respective issue trackers.
Together we can make AsciiDoc a first-class citizen of the Emacs ecosystem!2
That’s all from me, folks! Keep hacking!
Also, I really wanted to name something asciidoc-mode. ↩︎
Afterwards we’ll aim for world domination, of course. ↩︎
I'm still chipping away at the backlog that built up while I was away, but if there's anything in particular you'd like me to look at, feel free to ping.
Luckily, agent-shell is built on ACP, which sidesteps the problem. When a vendor changes course, you can swap providers and keep using your preferred tool. No need to reshape that hard-earned muscle memory.
On that note, the list of agents supported by agent-shell continues to grow.
Supported agents
Here's a list of the latest agents now supported by agent-shell.
Speaking of vendor-neutral tools being more important than ever, there are a couple of ways to help keep agent-shell going. Some cost money, others just a click. All are appreciated ;)
Sponsorships for agent-shell longevity
agent-shell has been attracting quite a few users. It's nice to hear folks are using agent-shell on a daily basis. They are often relieved agent-shell exists as an alternative to AI-tools commonly mandated at work. Those tools have well-funded engineering teams behind them, while agent-shell is just me, an indie dev ;) Time spent on agent-shell is time away from work that pays the bills, so if it's useful to you, please consider sponsoring the project.
Every individual sponsorship genuinely helps keep the project going. And if your employer benefits from your agent-shell use, they're typically in a position to contribute at a scale individuals can't, so nudge them to chip in too.
Hey, I'm looking at you, folks at Google, GitHub, GitLab, NVIDIA, Oracle, Red Hat, Yelp, Venmo, ARM, Spotify, Augment Code, Hinge, Mercury, Nubank, Veeva… Some of you are using agent-shell. Nudge your employer ;)
Speaking of GitHub stars, agent-shell is now my most popular Emacs package, recently overtaking chatgpt-shell.
New markdown renderer
agent-shell now ships with a brand new, more performant inline markdown renderer. This is the biggest internal change in some time. Enabled by default via agent-shell-markdown-render-function (moving away from the overlay-based renderer in shell-maker).
Access/navigate table content
Table content is now accessible. Point can land on any cell, which wasn't possible with the previous overlay implementation. In addition, tables are now also navigable: TAB and S-TAB move between cells.
Improved source block support (enabled by default)
Source-block syntax highlighting is now on by default. The per-snippet copy button is now keyboard-accessible too (previously mouse-click only, due to the overlay implementation).
New blockquotes support
Blockquotes now render in both shell and viewport. More importantly, you can select text in either a viewport page (or the shell itself), press r (for reply) and the selection becomes a blockquote in a fresh prompt.
Viewport
Shell
Session restoration
Session restoration got a meaningful overhaul (#605 by @nhojb), now exposed via agent-shell-session-restore-verbosity, with four levels:
minimal (default): title only, so restore is fast and quiet (needs session/resume support).
last: render the last prompt turn (needs session/load support).
first-last: render the first and last prompt turns (needs session/load support).
full: replay the whole conversation (needs session/load support).
Feature availability is agent-specific, requiring either session/resume or session/load request support. agent-shell degrades as needed, ultimately falling back to creating a new session.
Relatedly, agent-shell-session-strategy now defaults to 'prompt, and 'new-deferred has been retired.
Session forking
You can now fork the current session, starting a new shell that shares the conversation history so far and diverges from there. Invoke via M-x agent-shell-fork.
Restart/reload session
You can now restart the current shell anew (drop history) via M-x agent-shell-restart or reload (keep history) via agent-shell-reload.
Downloads/Temp shells
The new agent-shell-new-downloads-shell and agent-shell-new-temp-shell commands create agents anchored at either ~/Downloads or a temp directory. Both are also reachable via C-u M-x agent-shell.
TRAMP support
acp.el #20 by @martenlienen landed support for ACP connections over TRAMP, now making it possible to drive remote agents from agent-shell. Pair it with agent-shell-tramp for the user-facing integration.
Viewport improvements
Viewport interaction (setq agent-shell-prefer-viewport-interaction t) continues to be my primary way to interact with agents. It is focused (see only the latest interaction), fast (single-key bindings: y = yes, c = continue, m = more…), and offers a richer editing experience (dedicated prompt-crafting buffer).
The viewport is just a viewport to shell content. You can have your cake and eat it too, by jumping to the related shell buffer if needed.
Queueing
From a viewport, you can press r to reply to the latest agent response. In the past, you could only reply to idle agents. You can now press r to reply to busy agents too, automatically queuing requests on submission.
List editing mode
A new (basic) agent-shell-list-edit-mode lets you edit list-style content inside the viewport.
Improved buffer selection
Some commands prompt you to pick one of your active shell buffers (e.g. M-x agent-shell-send-region-to). The picker now shows extra context for each buffer to help you choose.
The same mechanism is now used by the new M-x agent-shell-switch-buffer command. More on the underlying API later.
M-x agent-shell-ui-toggle-fragment toggles the fragment at or near point (DWIM).
M-x agent-shell-ui-toggle-all-fragments cycles globally between all-expanded and all-collapsed.
Together they replace the previous agent-shell-ui-toggle-fragment-at-point, which is now an internal primitive.
c joins the viewport reply gang
You can now press c from a viewport to quickly send a "continue" request, joining the rest of the single-key reply shortcuts:
c: replies "continue" (new)
y: replies "yes"
m: replies "more"
a: replies "again"
1 … 9: replies with the corresponding numbered choice
r: opens the reply compose buffer
R: same as r, with the agent response quoted
More compact status rendering
Tool call status is now rendered as a compact icon-based label by default (agent-shell--inverse-icon-status-kind-label). agent-shell-styles.el ships several alternatives, picked via agent-shell-status-kind-label-function.
You can now set a default model and session mode for Codex via agent-shell-openai-default-model-id and agent-shell-openai-default-session-mode-id (#405 by @robjgray). Both must match an ID from Codex's "Available models" / "Available modes" listings.
Emacs 31 fixes
Headers had a few rendering hiccups on Emacs 31. These are now fixed (#588 and #590 by @nhojb, #463 by @ftlio). Warnings from deprecated when-let usage were also cleared.
Wayland clipboard support
wl-paste is now a supported clipboard handler for pasting images on Wayland (#461 by @martenlienen).
Windows clipboard support
Similarly, pasting clipboard images now works on Windows via PowerShell (#572 by @repelliuss).
Header improvements
The graphical header got minor tweaks here and there. For example, thought level is now displayed in the header. It can be changed via M-x agent-shell-viewport-set-session-thought-level as well as menus (#601 by @martenlienen).
Session config options
agent-shell now supports ACP session config options (#553 by @greggroth and #613 by @catern). Bind C-c C-s (or call agent-shell-set-session-config-option) to pick from the options the agent advertises. Broadcasted as config-option-update and available externally via agent-shell-subscribe-to.
Interrupt confirmation
You can now skip interrupt confirmations by unsetting agent-shell-confirm-interrupt (#424 by @emil-e).
Public functions
Resume sessions by ID
You can now resume an existing session by its ID via M-x agent-shell-resume-session (#332). Primarily useful for external integrations.
Outgoing request decorator
You can now use agent-shell-outgoing-request-decorator to tag or transform outgoing requests.
Subscribe to idle events
agent-shell-subscribe-to now broadcasts idle events (#509 by @arthurgleckler).
Getting shell buffers
agent-shell-shell-buffer returns the underlying shell buffer for the current context.
Last vs interaction at point
agent-shell-goto-last-interaction jumps to the latest prompt/response pair, while agent-shell-interaction-at-point returns the interaction at point as data.
Shell status
agent-shell-status returns 'busy, 'blocked, or 'ready status for any shell buffer.
Session title
The agent-supplied session title is now exposed via the session-title-changed event (delivered via agent-shell-subscribe-to) (#559 by @smagnuso). Can be handy for buffer names, bookmarks, or recent listings.
New third-party packages
The agent-shell family of third-party packages keeps growing. Recent additions:
#548: Copied highlighted text from agent output includes trailing backtick
#563: "Cannot modify map in-place" when starting agents
#577: agent-shell-get-config does not work in an agent-shell-command-prefix function
#587: Long region preview's "Expand…" button is sent literally to the agent
#617: OpenCode: consent prompt shows empty input when request_permission arrives before tool_call_update populates rawInput
Lots of polish
Beyond what's showcased, I've poured much love and effort into polishing the agent-shell experience. Interested in the nitty-gritty? Have a look through my regular commits.
Make the work sustainable
If agent-shell is useful to you, please consider sponsoring the project. I'm now back to working on agent-shell daily.
LLM tokens aren't free, and neither is the time dedicated to building this stuff (especially as an indie dev). I also have bills to pay ;)
Unless I can make this work sustainable, I will have to shift my focus to work on something else that is.
Ian Pan has curated a nice collection of Emacs caught in the wild. By “in the wild” I mean “in popular culture”: things like movies and comics. Most of us have seen the familiar scene from Tron but Pan has a bunch of others.
One example is the famous scene from Silicon Valley in which Richie and Winnie start arguing about Tabs versus Spaces and end up discovering that one is a Vim user and the other an Emacs user. I’ve seen that scene many times but didn’t realize where it came from so I learned something new from his post.
The other surprising thing—to me—is that Emacs also makes appearances in comics. What’s interesting is that you rarely see any editor other than Emacs or Vi(m) mentioned outside geek circles. Perhaps the current cohort of hackers will change that and we’ll start seeing references to VS Code. Or maybe not. VS Code does have a lot of users but I haven’t seen the same culture grow up around it. We’ll see.
However, as a digital minimalist, and one who writes at a high enough volume, a writerdeck starts to make a lot of sense. So I thought I’d try to make one.
But, I wanted it to be a “low buy” or “no buy” situation. Luckily, I had a Raspberry Pi 4 lying around unused, so I snapped it up. I installed a lightweight, minimalist distribution of Debian called DietPi and installed Emacs with no GUI. (Of course, I also installed git and a lot of other stuff by now, but I am keeping it GUI-free.)
We’re talking MAXIMUM digital minimalism here.
I am using tmux as a sort of window manager, spawning sessions and windows and switching between them as needed. I’ve also opted to run Emacs as a daemon and connect to it with emacsclient, so no matter what session or window I’m in, I’m attached to the same running Emacs state. This means I can view any and all available buffers and have Claude edit Emacs lisp files and see live results without having to reload Emacs.
For aesthetic reasons, I’ve opted for an “amber CRT” color scheme with the available 16-color palette in the text interface. This makes my workstation feel like a DOS-era word processor.
I love this so much it’s hard to contain my excitement.
I plan on making a more in-depth video on this topic later to actually demonstrate the system live and show some of the config-mongering I went through to get it just right.
I’ve really enjoyed the results as I’ve grown accustomed to a purely text-based (no mouse!) workflow. The only downside is that it reverses the classic Emacs paradigm of having Emacs colonize my desktop. Instead, Emacs is a guest in tmux. C’est la vie.
When I released blogmore.el v4.7.0
yesterday I finished off by saying that it was my intention, at some point,
to rework the frontmatter-handling code so that it did proper YAML parsing.
As often happens with these sorts of things, "some point" ended up being
that evening.
I've rewritten everything to do with handling properties in the frontmatter
so that it now uses yaml.el. This has a
number of knock-on effects. The first and most obvious effect is that
anything that is a list/array in the frontmatter is actually properly
treated as a list. A good example here is tags. Now you can have your tags
look like:
and blogmore.el will still handle things fine. The same holds for series
too.
It should be noted, however, that because I'm now using actual YAML
serialisation code, most other forms of a list will all end up being
transformed into this kind:
and you make an edit to the tags via blogmore.el, it will end up as the
version enclosed in []. BlogMore itself
supports all three versions so this works fine.
There is a breaking change here too, which in part explains the reason I
bumped the version to 5.0.0: because series can now be treated as a list
I've removed the blogmore-set-series command and instead replaced it with
blogmore-add-series and blogmore-remove-series. Both can of course be
found in the transient menu.
Another big change in this release is the way that existing values are
loaded up from your blog. Previously, when you went to add a category, tag
or series, blogmore.el would use
ripgrep (or a combination of find
and grep if rg wasn't available) to pull out values to help populate a
completion list. This worked fine as long as a) the frontmatter property was
all on one line and b) the body of a post didn't contain something that
looked like a frontmatter property. With this release of blogmore.el I've
dropped this approach in favour of calling blogmore itself and using the
dump command to get
the actual lists of categories, tags and series.
This does mean that BlogMore needs to be installed in a location where
blogmore.el can see it, and to help with this I've added a new defcustom
called blogmore-command. By default this is set to call whatever version
of blogmore can be found in your exec-path; if this results in
unexpected behaviour you can set blogmore-command to point to a specific
copy of blogmore.
There is, however, a small downside to this beneficial1 approach: calling
on blogmore and parsing all posts to get the values is generally going to
be slower. With this in mind I've built in a cache for these values. The
first time you load up the categories, tags or series, the values are held
on to so that subsequent prompts are instantaneous (meaning there is no
further call to blogmore). To ensure this doesn't confuse things, when you
switch blog (blogmore-work-on) the caches are cleared. In the unlikely
event that there is a problem with this approach, I've also added a
blogmore-clear-caches command to force the clearing of the caches.
There are some other small QoL changes under the hood and also to the
interface. I've moved some things around in the transient menu, and also
ensured that a couple of options are better-disabled depending on the
context.
All of this makes the package even more robust. Something that started as a
quick hack back in March has turned
into a tool I heavily lean on. Hopefully, for anyone who might happen to use
BlogMore and GNU Emacs, it'll be a useful daily-driver for them too.
The benefits being: only values in frontmatter appear, inconsistent
casing is cleaned up, etc. ↩
My ongoing journey with vc-dir and vc-mode led me into wanting to delete a stash (not to pop). There are "z" keybindings in vc-dir:
z c vc-git-stash
z p vc-git-stash-pop
z s vc-git-stash-snapshot
but what about delete?, and are there any others?
In vc-dir you get a rather nice little Stash section tacked on at the bottom listing all your stashes:
I genuinely assumed I was missing something obvious, so I did the sensible Emacs thing and hit C-h m to describe the mode, scrolled through the whole keymap, and, nothing about stashes at all.
Well how about C-h b (describe-bindings)?, initially nothing!, however I stumbled on a solution, if the point is on a stash name, the following is exposed!
C-k vc-git-stash-delete-at-point
RET vc-git-stash-show-at-point
= vc-git-stash-show-at-point
A vc-git-stash-apply-at-point
C vc-git-stash
P vc-git-stash-pop-at-point
S vc-git-stash-snapshot
Surprisingly difficult to find.
and C-k does obviously seem a natural fit to delete a stash, but what wasn't obvious was the fact that the point had to be on the stash name.
I couldn't quite understand why these keybindings were so difficult to find, so I went digging into vc-git.el and there it was, the bindings are not part of vc-dir-mode-map at all, they live in their own little keymap:
And the crucial detail, this keymap is not installed as the major mode map, it is slapped directly onto the stash lines as a keymap text property when those lines are rendered. So the bindings are only live when point is literally sitting on a stash, which is rather elegant actually, but it does mean describe-mode never sees them, because C-h m only reports the major mode's own keymap, and a text-property keymap is invisible to it. That explained my confusion completely, I was not going mad after all!
And the Emacs manual, well I did have a little look but found nothing. It seems these keybindings are almost impossible to find, but now I have found them this blog post should help me to remember.
So, lesson learned, and a genuinely useful one I think, C-h m is not the whole story. When a mode renders interactive regions, buttons, clickable lines, embedded widgets, those often carry their own text-property keymaps that describe-mode will never show you, and the move in those situations is C-h k or C-h b with point actually on the thing.
Anyway, C-k on a stash line to drop it, that is the headline, write it on a sticky note, you will want it eventually!, if you are a vc-mode user of course, I suspect that magit has this very discoverable already through the stash menu.
Quick announcement: this blog now lives on the small net too. If you have a
Gemini client around, point it at:
gemini://gemini.rahuljuliato.com
You'll find the same posts there, rendered as plain gemtext.
Wait, Gemini?
Gemini (the protocol, in case the name now
reminds you of other things) is a small internet protocol that sits somewhere
between Gopher and the early web. It works like this: the client sends a single
request, receives a single response, and the connection is done, always over
TLS. Pages are written in a tiny markup format called gemtext, which gives you
headings, links, lists, preformatted blocks, and that's about it. A link is
always its own line, so pages end up reading like a well-organized text file.
The whole spec is short enough to read in an afternoon, and people have written
servers and clients for it in pretty much every language you can think of. The
community around it is usually called the "small net" or "smolnet": personal
capsules (that's what sites are called over there), gemlogs, and a general
feeling of the web before it became an application platform.
I've been lurking in Geminispace for a while and finally decided this blog
should have a capsule of its own. The mirror is generated from the same
Markdown sources as the web version, so both stay in sync.
Browsing it from Emacs
This site has always been totally browsable on EWW, I wanted that by design,
the web version degrades gracefully to plain HTML and reads just fine in a text
buffer. But EWW speaks HTTP only. For Gemini (and Gopher, and Finger), the
package you want is elpher. It's available
on NonGNU ELPA, so a plain M-x package-install RET elpher RET gets you there. Then:
M-x elpher-go RET gemini://gemini.rahuljuliato.com RET
It renders gemtext beautifully in a buffer, history and bookmarks included, and
it integrates gracefully with EWW: bump into an http:// link inside elpher
and it opens in EWW, follow a gemini:// link from EWW and elpher picks it up.
EWW for the big web, elpher for the small one, and you never leave Emacs.
Other clients
The official project site keeps a list of clients for every taste, from TUI
ones like amfora to full GUI browsers like Lagrange:
DC Toedt is a lawyer and professor of practice who uses Emacs and Org Mode. He wanted a small Emacs Lisp function to convert Org Mode syntax to Markdown and copy it to the clipboard to make it easier to copy the materials he's writing for a course on contract drafting. This seems to be a common need, and here are several other approaches:
You can, of course, set a new one too, but the idea here (as with categories
and tags) is that you can quickly find back an existing series and add the
current post to it.
Also, as with tags, the expectation is that either a single series is being
used, or if more than one series is in play for a post they'll be listed as
a comma-separated list. The issue here is that while BlogMore supports this:
series:-Some series of posts-Some other series of posts-Yet another series of posts
the frontmatter-handling code in blogmore.el isn't very sophisticated at
all; it doesn't actually handle it as actual YAML, instead just treating it
as a set of key/value pairs separated by a colon.
At some point soon I want to give blogmore.el a revamp and base all of the
frontmatter-handling code on something like
yaml.el. I did do some experimenting
last night to drop it in and handle proper lists. It worked well enough, but
I abandoned the work as I realised I really wanted to start again from
scratch and build blogmore.el from the bottom up using that package.
protesilaos: Looking forward to this! Talk to you soon.
gr1maldi: Yo, and stuff.🙂
charliemcmackin4859: I just checked, there are 11 items in my kill-ring at the moment. Some of them are several lines long
charliemcmackin4859: narrowing to defun feels easier than trying to mark the function before search-and-replacing… I love narrowing
charliemcmackin4859: …and I love that you can make those marking tricks part of a keyboard macro
hmelman: both local and global marks are useful to navigate through
pratikmishra4073: just tried indirect buffer. didn't know it existed
hmelman: and the fact that things like xref or imenu push the last location before jumping, means popping the mark is an easy way to go back from various navigation mechanisms
matthewjorgensen9115: thinking about isearrch wrap around search losing your place. either have scrroll to posisiotn to know the direction and how far it was away, or have mode line flash to say it moved from last to first
hmelman: Don't need flymake for that just M-g M-n (next-error) which works with compile, grep, occur (I think).
hmelman: One I keep forgetting about, you mentioned using sexp commands in other languages, works in prose too. C-M-u (backwards-up-list) moves you out of a "quote" or (paren) and then C-M-e to the end of it.
matthewjorgensen9115: follow mode alllows the smae file with multiple frames, like a book. follow mode can work with 2 3 or more windows
matthewjorgensen9115: also works with centered cursor mode which will keep the cusor possion in the middle of the frame
RandCode: Damn I am late, hello everyone! 🙂
matthewjorgensen9115: when highlighting parts of the manual how are you doing this? in bookmarks, bookmark region, using org-remark? other ways?
RandCode: Does emacs have a grammar checker like harper's lsp?
greggr0th: What are your favorite completion plugins?
RandCode: Also, looking fresh Prot ;)
protesilaos: Thanks!
hmelman: dabbrev is underrated. You don't need to pre-define them, just type the start of a word/symbol and type M-/ and it will search the buffer for something starting with what's left of point and expand it
RandCode: There is so much to learn that I feel like I have been missing out on all of emacs somehow lol
RandCode: Since you mentioned built-in completion framewworks, how does the built-in (vertical) completions compare to corfu btw?
charliemcmackin4859: not grammar, but if you have a dictionary server running (like dicod on linux) emacs can be made to communicate with it to give definitions at point
RandCode: Ohh, that is a cool! ^
matthewjorgensen9115: virtico multiform mode allows custom settings command, for example for files I use grid and alphabetical, but vertico recommendations normally. This allows more value of completion knowledge
RandCode: Thank you so mcuh for this wonderful stream everyone!!!
I will join Sacha’s live stream this Thursday to talk about
underappreciated features that are built into Emacs. There are a lot
of nice things that are available out-of-the-box (plus many packages
that build on top of them). I am looking forward to it!
If you have a numeric keypad1, try this in the *scratch* buffer:
enable numlock and press the zero on the numeric keyboard
(Num-0). Then press the zero located between the alpha keys and the
function keys (0). Okay, you see 00, no big deal.
Why do I bring this up? Emacs interprets Num-0 keypress as <kp-0> while it interprets 0 as
0. This means that you could bind Num-0 to a function without
affecting the other 0. You could configure Num-0 to insert "zero" by
entering and evaluating this in the *scratch* buffer:
That's a trivial example, of course. But it implies that you can
have ten more keys to play with. Similarly, the arithmetic
operator keys on the numeric keyboard differ from the "regular
ones" that are grouped with the alpha keys. They can be bound
to functions, referenced
as <kp-add>, <kp-subtract>, <kp-multiply>, <kp-divide>. But
these also accept the standard C- M- S- modifier keys, which
gives you twelve more possibilities. This is true also for the
Insert key <kp-insert> and the Delete key <kp-delete> that
double as Num-0 and Num-., respectively.
I have a "Calc" key above the numeric keypad. Unfortunately I
can't seem to use it for anything in Emacs -- it opens the OS's
calc.exe program regardless of what modifiers I use with it. This is
a shame because it would be the ideal mapping for M-x calc. Perhaps
this can be altered in BIOS.
But I do use my Win key as a modifier (sometimes). And I've heard
that the Caps Lock key can be repurposed. These are topics for
other posts.
Daniel Mendler, who took over as maintainer of the world’s best RSS reader, Elfeed, has announced the release of Elfeed 4.0.0. You can read the Change File to see what’s new but the most important point for me is that the maintainership of Elfeed has successfully transitioned from Chris Wellons to Mendler.
When Wellons announced his retirement from the Emacs ecosphere, I, and I’m sure others, had a moment of apprehension. We all wondered if anyone could fill Wellons’ large shoes. I’m happy to say that Mendler has shown himself up to the task and is doing a great job moving Elfeed forward.
As I’ve said before, I consider Elfeed an almost perfect RSS reader. It’s easy to configure it to operate the way you need it to. One of Mendler’s changes that I really like is the ability to pop into EWW if you need to. I use it from the default Elfeed text buffer but also from the WebKit mediated display if there are too many ads and blinkenlights on the default page. As I’ve said before, I have a growing appreciation of EWW and am a bit surprised to find it becoming one of my go to tools.
The important point, though, is that if you’re an Emacs user who has an RSS feed, you should definitely try out Elfeed. You won’t, believe me, want to go back.
Emacs provides four useful status bars (mode-line, header-line, tab-bar, and tab-line), but each imposes different, inconsistent limits on multi-line layout, alignment, icons, and interactivity. svg-line (see code on GitHub) solves this by rendering them as SVG images, and normalizes a rich feature set across all status bars with a consistent configuration. svg-line works by defining a small rendering engine built on Emacs's native SVG support. Configuring status bars is easy: you simply write one :content function and call svg-line-activate. You can see my custom configuration of mode-line, header-line, tab-line, and tab-bar in my Emacs config.
Figure 1: Every *-line in this frame is one SVG image drawn by svg-line.
2. About
Emacs gives us four status bars, the mode-line, the header-line, the tab-bar, and the tab-line (*-lines for short). These are useful for providing a dynamic 'heads-up display', for important context, like what buffer you're in, the active major mode, and really any arbitrary thing you can define.
I'm a heavy user of the *-lines in Emacs, and I have them all enabled, but the issue that has plagued me is that, natively, each one behaves differently and each has unique limitations. For example, multi-line status (necessary on my small laptop) is possible, but only in the tab-bar. Right alignment is possible in the tab-bar, but only in the last line, and this alignment feature is only available in the tab-bar. I can display icons from all-the-icons in the mode-line and header-line, but not the tab-bar or tab-line. Etc….
What I really want is consistent behaviour and configuration across all these status bars, and I want the multi-line, alignment, and icons features available in all of them. It turns out that SVG (scaled vector graphics) is the key to solving this.
Inspired by Nicolas Rougier's dual-header gist, I built svg-line, which provides this experience by utilizing Emacs's built-in SVG rendering support. At first, this approach seemed like a hack, or abuse of the *-lines, or neglect of the built-in status bar behaviour. But I kept it and created a package because I was literally shocked how well this works and how native this feels (see the screenshot and video above).
Note that even if you only use the mode-line, svg-line is still useful — likely more so, since a single status bar has to render all your indicators on its own.
3.svg-line's Features
Multi-line everywhere, with per-row left/center/right alignment.
A tab-line that wraps overflowing tabs onto new rows instead of hiding them, including with file-type glyphs, a current-tab highlight, and an unsaved tint.
Clickable anything. Any segment can carry a left-click action, a right-click menu, and hover help with a highlight. This works uniformly across all four bars, including the otherwise-uncooperative tab-bar.
Icons as text. Using Nerd Fonts and an icon is just a character that flows with everything else. SVG rendering also enables a full-height "masthead" glyph option on status bars that can span multiple lines.
Dynamic and animated indicators: a buffer-position pie, progress bars, active vs. inactive styling per window.
It respects text scale. The bars track text-scale, re-rendering crisply instead of blurring.
A meta feature is that the configuration surface is uniform across all status bars, which is a pleasant improvement over the diverse configuration strategies for the native APIs.
4. Why SVG Works
When using svg-line, each line becomes one SVG image, and SVG images are more featureful than the native text engine:
It can be any height. Multi-row bars are now possible in every *-line.
Everything is placed at exact pixel coordinates. Left, right, and center alignment work identically on every row.
It draws whatever you want. Text, yes, but also wrapped tab flows, geometric progress bars and pies, and (with a Nerd Font) icon glyphs inline with the text, the same on all four lines. Anything you can render in an SVG (just about anything) is fair game.
The engine remembers where it drew. It can detect the mouse against those placements, so clicks, right-click menus, and hover all work on any element of any line.
5. Configuration
Configuring svg-line is deliberately simple. You write a :content function that returns rows, supply it to svg-line-define, and call svg-line-activate on the defined line. This configuration pattern is identical for all four bars. The engine has two layouts: lines (the default — rows of segments, used for the mode-line, header-line, and tab-bar) and wrap (a flow that wraps, used for the tab-line).
5.1. Mode-line
5.1.1. Simple mode-line
The smallest useful line is a single row: a label on the left, the cursor position on the right.
This trivial example clarifies the pattern: define then activate:
:content is the only required key: a function returning a list of rows. Each row is a (LEFT . RIGHT) cons, and each side is a list of segments — here just plain strings.
with no :background, :foreground, or :active, the line picks sensible defaults and is always drawn as active.
svg-line-activate enables it, and svg-line-deactivate / svg-line-toggle disable it, restoring the native mode-line untouched.
5.1.2. Rich mode-line
Here's a more complicated mode-line configuration that demonstrates svg-line's feature scope. It defines two rows, three-way alignment, a masthead icon, a custom segment, a clickable button, dynamic theme colours, and active/inactive styling:
;; A custom segment is just a zero-argument function returning a string.
;; This one shows how far point sits through the buffer, as a percentage.
(defunmy/buffer-percent()(format " %d%%"(/ (* 100 (point))(max 1 (point-max)))))(svg-line-define 'my-mode-line
:target 'mode-line
:active #'mode-line-window-selected-p
:icon(lambda()(nerd-icons-icon-for-mode major-mode)):background(lambda()(face-background 'mode-line nil t)):foreground(lambda()(face-foreground 'default nil t)):content(lambda()(list
;; row 1 — three independently-aligned segments on one row
(list :left(list (buffer-name)):center(list (symbol-name major-mode)):right(list (format-time-string "%H:%M")));; row 2 — custom segment + position on the left, a button on the right
(cons (list #'my/buffer-percent (format-mode-line " %l:%c"))(list (svg-line-seg "save":id 'ml-save
:help"buffer actions":action #'save-buffer
:action-help"save":menu '(("Revert" . revert-buffer)("Kill" . kill-current-buffer))))))))(svg-line-activate 'my-mode-line)
Line by line:
my/buffer-percent — any zero-argument function can be a segment; this one returns a string.
:active #'mode-line-window-selected-p — a predicate; when it's false (an unfocused window) the engine applies the :inactive-* colours instead.
:icon — a full-height "masthead" glyph drawn once on the left edge, spanning both rows. This is a function, so it tracks the current buffer's mode.
:background / :foreground — literal colours, or (as here) zero-argument functions evaluated on every render, so the bar follows your theme automatically.
row 1 — a :left/:center/:right plist puts three independently-aligned segments on a single row.
row 2 — a plain (LEFT . RIGHT) cons. Its left side mixes the custom function with an ordinary %l:%c string.
svg-line-seg — turns a segment into a button: left-click runs :action, right-click opens the :menu, and :help shows on hover in the echo area.
5.2. Tab-line
The tab-line is where the wrap layout is most useful: instead of scrolling overflow off the edge, it flows tabs onto subsequent rows.
:layout 'wrap — switches from rows of segments to a wrapping flow; overflowing tabs land on a new row rather than scrolling out of sight.
each item is (LABEL . STATE), where :current and :modified in the state plist drive the per-tab highlight and unsaved tint.
:current-background / :modified-foreground — the same value-or-function styling as the lines layout, just with current- and modified-tab variants.
6. Acknowledgement
Credit where it's due: this started as an experiment off Nicolas Rougier's work. His SVG explorations and that dual-header gist demonstrated that this was possible, and showed me how well this approach works.
JTR over at The Art Of Not Asking Why describes a problem—and its resolution—that I didn’t know existed. The TL;DR is that photographs taken with an iPhone are always captured in landscape mode but Apple’s software detects portrait photos and corrects the orientation on the fly.
The fix is pretty easy. He uses the mogrify tool of ImageMagick to auto-orient the photos based on their EXIF data. Since he’s viewing these photos in Org mode he wants an easy way of making the conversion from within Emacs. That’s a perfect job for Álvaro Ramírez’s dwim-shell-command, which JTR uses.
Take a look at the code and see how simple it is. Even if you aren’t an Elisp expert, it’s easy to do things like this with dwim-shell-command right from Emacs.
Org-mode has a capture system. You can be in any file in Emacs and invoke the capture system. Press a few keys and add text to a org heading in any file that you have set up a capture for.
It is easy to set it up. Firstly you assign a keyboard command to the capture system. In my case,
:bind ("C-c c" . org-capture)
Org-capture
You get this.
Journal Entry
You press a key. Let’s say you want to add a journal entry. Press j and get this.
Type what you want to say in the buffer and when done, press ⌃C ⌃C. This saves the additions to the journal and closes the buffer. Leaving you in the buffer you were in before you invoked the capture function.
This is the code-snippet which does the heavy lifting. "* %<%R: >\n%? \n"
The explanation:
*: Creates a level-1 heading.
%<%R: >: This is a time format code. It inserts the current time in 24-hour format (e.g., 21:45: ).
\n: Inserts a line break (a new line).
%?: This is where your flashing cursor will start, you can begin typing your journal entry without having to move the cursor yourself.
If you set up a capture system, you can easily add content to files without having to open them.
It is one of the useful features of Org-mode.
Tweaking the Capture System for Bookmarks
I paid for lifetime access for pinboard.in. But I get the feeling that this project is on maintenance mode and the developer has moved on. It still works but I want to stop relying on it and move my archived bookmarks to a plain-text Org file. I started a bookmarks.org file with this structure:
The capture would put the entries under the Bookmark header and I would refile the URL’s into appropriate sections periodically. Worked for a while. I learned more about capture templates and realized that I could lessen my work by filing the URL’s into their appropriate sections at the time of capture.
Org-capture
I didn’t like that. I wanted to be able to file the URL under it’s relevant heading when I captured it Now when I invoke capture I get this.
Bookmark Choices
Press b and I get this.
I can press the relevant key for the section I want and then paste in the URL. It gets inserted to the appropriate section.
Makes it easy.
This is an excerpt from the relevant section in my init.el.
This feature is one of the many that makes me love Org-mode.
Tweaking Org-mode
I am used to outlining programs. I have been using them for a long time. There is muscle memory build up over the years and I wanted to bring those into Org-mode. I am sure that the OG’s of Emacs are used to the Org-mode keybindings but I wanted to feel more comfortable using it. That comfort would increase if I used the keybindings I am used to.
New Org Header Outside/Inside
I wanted the new heading either to be a sub-heading within the current heading which conceptually is a daughter to me. Or the heading to be on the same level as the current heading I am under, conceptually a sibling. The third alternative, the new heading would be mom. That is a heading level higher than the one I was on. I wanted to be able to press a keyboard command and be at the start of the text of the new heading, mom, sibling or daughter. This piece of code does that:
(defun my/get-org-level-at-point ()
"Find the level of the headline the cursor is currently inside of by searching backward."
(save-excursion
(beginning-of-line)
;; 1. Check if the CURRENT line is a headline
(if (looking-at "^\\*+")
(length (match-string 0))
;; 2. If not, search backward for the nearest headline
(if (re-search-backward "^\\*+" (point-min) t)
(length (match-string 0))
0))))
(defun my/org-insert-child-headline ()
"Insert a new headline that is a child (Level + 1) at the end of the current subtree."
(interactive)
(let ((level (my/get-org-level-at-point)))
(if (> level 0)
(progn
(org-end-of-subtree)
(newline)
(insert (make-string (+ level 1) ?*) " "))
(user-error "Not inside an Org headline"))))
(defun my/org-insert-sibling-headline ()
"Insert a new headline at the same level at the end of the current subtree."
(interactive)
(let ((level (my/get-org-level-at-point)))
(if (> level 0)
(progn
(org-end-of-subtree)
(newline)
(insert (make-string level ?*) " "))
(user-error "Not inside an Org headline"))))
(defun my/org-insert-promoted-headline ()
"Insert a new headline one level higher (Level - 1) at the end of the current subtree."
(interactive)
(let ((level (my/get-org-level-at-point)))
(if (> level 0)
(let ((new-level (max 1 (- level 1))))
(org-end-of-subtree)
(newline)
(insert (make-string new-level ?*) " "))
(user-error "Not inside an Org headline"))))
Tweaking Some More
I am fond of the keybinding M-<right> and M-<left> as navigation keys in Emacs. In Org-mode it is bound to org-metaright and org-metaleft. That didn’t sound good to me. I was used to C-s-<right> and C-s-<left> for this function. Similarly M-<up> and M-<down> didn’t make sense to me. Too many years of using C-s-<up> and C-s-<down> for this function. So I changed them.
And I added keybindings for the functions I talk about in the previous section.
I needed to replace a page in a PDF with another PDF. This was a bit
of an annoying process on my iPad involving copying and pasting pages
in Noteful and then re-exporting them as a PDF, but it was easy to do
in Emacs thanks to pdf-tools and PDFtk.
;;;###autoload
(defunsacha-pdf-view-replace-current-page-with-file (file)
"Replace the current page in PDF View with FILE.Requires pdftk."
(interactive"FFile to insert: ")
(let ((temp-file (concat (make-temp-name "pdf-view") ".pdf")))
(call-process
"pdftk"
nil nil nil
(concat "A=" (expand-file-name (buffer-file-name)))
(concat "B=" (expand-file-name file))
"cat"
(format "A%d-%d"
1
(1- (pdf-view-current-page)))
"B"
(format "A%d-end"
(1+ (pdf-view-current-page)))
"output"
temp-file)
(rename-file temp-file (buffer-file-name) t)))
Sebastián Monía1 offered some alternatives to
the exeln function that I posted on June
4.2
First, he pointed out that there's no need to
bind asynch with the let function. Instead he
suggested that I use (when arg "&") to append the
ampersand when there's a prefix argument.
Thus, shell-command can be called this way:
Second, call async-shell-command rather
than shell-command with the "&" as suggested
in shell-command's doc string, which says, "You can
also use async-shell-command that automatically adds
'&'."3
Third, use thing-at-point from the built-in
thingatpt.el library to return the line at point rather
than calling buffer-substring.
(defunexeln (arg)
"Execute current line as a shell command.With prefix ARG, run asynchronously."
(interactive"P")
(funcall (if arg
#'async-shell-command
#'shell-command)
(thing-at-point 'line t)))
And although he didn't mention it explicitly, he reworded the doc
string -- it blends in seamlessly with the native functions' doc strings.
I set out to contribute to the Emacs community, but I'm getting so
much more out of it than I expected! Thank you again, Sebastián Monía!
Sean Whitton writes to tell us that the first Emacs 31 pretest, emacs.31.0.90, is available for download. It’s been almost two years since the first Emacs 30 pretest came out so we’re pretty much on schedule for our yearly major release.
As always, Irreal wants to give a huge shout out of thanks to all the developers who work so hard to keep the Emacs development bandwagon rolling. They do all this for free so we each owe them a word of thanks and, as I always say, the drinks are on us if we find ourselves in the same bar as one of them.
Whitton’s post has all the details concerning downloading and verifying the latest tarball. If you don’t mind living on the edge, give it a try and report any problems.
A few weeks ago I was on vacation. Of course I brought back quite a few pictures with me. When I got back, I sat to select some of them to make a gallery for my family and friends. I started with copying all pictures from two mobile phones and a camera to my laptop. Then I wanted to copy a few carefully chosen images to one directory. To my surprise, Emacs’ Image mode does not have a command to copy the currently viewed image to another directory.
Spam is an eternal problem, and while SpamAssassin is pretty good (I get around 700 mails per day, of which 670 are rejected as spam), there’s still some stuff that persistently gets through. A goodly portion of that is traditional random spam from random accounts, and there’s not much to be done about that, but there’s a recent trend of legitimate-looking things coming from persistent accounts — like BackerHome and BackerHive.
I’m guessing that eventually these will get so bad reputations that they end up on RBLs and will disappear into the mists, but meanwhile… I just thought that there should be a mechanism in Gnus to split these message to the spam group — without having to do much work.
Tada!
It’s a small package, and you can find it on Microsoft Github. It’s basically a tiny minor mode that adds a couple of commands, so you just hit z, check the entry (or not) and then C-c C-c.
(There’s also a command to edit the split data if things go off the rails, or just because you want to.)
B t to check the split trace, as normal.
So there you go. May your inboxes be slightly less spammy.
(And it’s fun doing some Gnus work again for the first time in years… I’ve forgotten what all the functions are called, though, so I had to use Claude to remind me. *gulp*)
The following exeln function passes the current line to the system
shell, which executes it, thus EXEcuting LiNe.
On Windows x86, my GNU Emacs has shell-file-name set to
cmdproxy.exe, which is provided by x86_64-w64-mingw32, with which
Emacs was compiled.
shell is invoked synchronously by default; a prefix argument can
change this to asynchronous.
In the video below, point is on a line of text that would print the
contents of hello_whirled.txt when entered at the Windows command
prompt (or DOS prompt). I press C-c x, which is bound
to exeln. The command is executed and the output is shown in *Shell
Command Output*, the default buffer used for synchronous output from
shell-command.
(defunexeln (arg)
"Execute the current line as a shell command.With prefix arg, operate asynchronously, same as calling(shell-command COMMAND) where COMMAND ends in '&'.Otherwise, execute synchronously."
(interactive"P")
(let (asynch)
(if arg
(setq asynch "&"))
(shell-command (concat
(buffer-substring
(line-beginning-position) (line-end-position))
asynch))))
“No-code automation” has been in Emacs far longer than its usage as a term of art. Better known as keyboard macros, this feature has allowed Emacs users to achieve their bespoke needs for decades, all without needing to know a whit about Lisp. I can testify to this personally: though I’ve been using Emacs since the early 90’s, only in the past three years has writing Elisp played any part in it. For most of my Emacs journey, I got by with just making keyboard macros.
If you’re unfamiliar with them, here are two links you should get to know:
My addition to the knowledge of those posts is to boost this fact: mouse events can also be captured by a keyboard macro. If you know where a command is in a menu, you can also record clicking on it. I think the ability to combine both keyboard and mouse events in a keyboard macro is compelling, so much so that I decided to emphasize this in Anju.
The recent Anju v1.5.0 release added the menu “Tools › Macro Recorder” to the menu bar as shown below.
With this, mouse enthusiasts can create and run keyboard macros without need to recall keybindings or deal with inconsistent command naming, as observed in Petersen’s post.
One thing that I TILed about keyboard macros in building this menu was the command list-keyboard-macros (see (emacs) Kmacro menu). This command is available in the “Macro Recorder” menu as the item “List macros”. This lets you manage multiple keyboard macros that have been defined during a session.
James Cherti has a new version (Release 1.0.4) of his inhibit-mouse package available. As the name suggests, it disables the mouse while you’re using Emacs. I’ve written about inhibit-mouse before. As I said in my previous post my first thought was that I don’t need this because I’ve already trained myself not to use the mouse while I’m in Emacs.
The thing is, though, that I mainly use my laptop—a MacBook Pro—which has a trackpad for mouse operations. Since I often use my laptop on my lap while sitting on the couch, it’s easy to inadvertently touch the trackpad with my palm and change focus from one window to another. Turning off the mouse would eliminate that from happening. Still, I do use my trackpad to scroll Web pages and emails that are displayed with WebKit because there’s no other decent way of doing so. Happily, inhibit-mouse is very configurable and you can, in particular, disable it’s use in any major modes you like. That may solve my WebKit problem.
Inhibit-mouse is a reimagining of Steve Purcell’s disable-mouse package. Cherti claims inhibit-mouse has some advantages—such as being more efficient—over disable-mouse. Take a look at the repository to see what they are.
If you’re looking for a way to banish the mouse from your Emacs experience, this seems like a good way of doing so. You can even leave a couple of escape hatches if you need to.
Ben Zanin (@gnomon@mastodon.social) - Mastodon: Robertson screwdriver owner, believer in the value of personal-scale computing and skeptic of the value of computing scales any larger than that
Sacha: I forgot to ask you how your lastname is pronounced.Ben: Oh, Zanin.Sacha: This is Emacs chat number 25 and here I am withBen Zanin.You have a math background and now you're in IT.Where did you encounter Emacs in the journey?Ben: So the reason I have a mathbackground is that I wanted to get intoprogramming language design and at the time sothat led me over to lambda calculus and that kindof thing and at the time it was at the time I wasgetting into university in like 2005-2006It was impossible to get into the computer programs,but I could get into math.I figured that would move me in the direction that I wantedto go in, so I did.Got to third year, and then every professorwho taught any of the courses I cared aboutall left the faculty at the same time.Sacha: Oh no!Ben: Yeah. Anyhow, that's a different story.But yeah, I got into Emacs because in the processof learning about Lambda Calculus, the Lispweenies found me.And yeah, I spent a bunch of time learning aboutthat.And it wasn't until like 10 years after I gotinto Common Lisp and Scheme that I actually gotinto Emacs at all.Sacha: That's interesting.Usually people, like, if they're doing Lispythings, they get into Emacs right away becauseLisp.But okay, so 10 years after.You're already well-used to parentheses at that point.Ben: Yes.But I guess it wasn't until about ten years ago,like around 2015 or so,that I started really getting into Emacs.I would still consider myself to be a beginner on that front.Sacha: Oh yeah? So what got you to get into it more?Ben: I found that I was looking for more structureddata management processes.Basically, I was looking for personal informationmanagement.And I kept bouncing off Emacs because I wanted toget into those tasks, but Emacs was such a richlearning process that I kept getting sidetracked,so I kept bouncing off of it.
2:19Ben got into Emacs because of twittering-mode
Ben: But I have to admit that one of the things thatoriginally actually got me into Emacs wastwittering mode.Oh yeah?Because I was looking for a tool that would letme, we'll probably get into this later in thechat, but I was looking for a tool that would letme keep a larger window of context about theconversations that I've been in.The Twitter apps and the Twitter websiteimplemented a really narrow window.I wanted to be able to search back a coupleof weeks for stuff that I had seen or talkedabout previously.Twittering-mode gave me the ability to keepdays or weeks of context in a single bufferthat I could then immediately search.So I didn't actually get into Emacs as a text editor,I got into it as an application platform,and then gradually learned the rest of it.This is actually fantastic. So I kind of stuck around after that.Sacha: That is the first time I've heard of someonecoming to Emacs because of Twitter.Ben: No, it's deeply embarrassing.Sacha: No, it's wonderful.I love that there's just so manyreasons why people come and the fact that youcould find something that would let you takethose notes and see that context and reply to it,and even, as I see in your config, work aroundslow keyboard response issues.Ben: And also the fact that that's stillin the config despite Twitter itself...I think I abandoned it in 2017, but it takes me alittle while to rip things out of my config.Sacha: Yeah, I see you're a lot moreactive in Mastodon these days. I see yourMonsterdon posts scrolled by my feed every so often.Yeah, so we dove right into that because a lotof the stuff in your config is interesting andyour workflows too.My idea for these Emacs chats is toshow people the kind of stuff that isn'timmediately obvious to newbies when they'relooking at someone's config.Because a lot of the stuff is new workflows andhow the keyboard shortcuts work together and howthis stuff fits into your life.That's not immediately obvious from the code.I've taken a look at your config, but beforewe jump into the gazillion things I wanted to askyou about, are there things that you particularlylove about your Emacs workflow?
4:30Emacs as a media playback platform with Versuri and Mpdel
Ben: I'm probably going to geta little bit of deserved flak for this, but Iuse Emacs a lot as a media playback platform.I spend a lot of time using Emacs to listen tomusic through MPV.And I've got some integration with Versuri, whichlets me...I think you've seen those customizationswhere what I've done is I've kind of duct-tapedthe two together.Versuri is an Emacs package for being able toquickly search through lyrics for songs.What I've done is I've bound Versuri to thepoint whereI've pulled up a page of lyrics, I now have ahotkey that bounces me over to a search of my MPDlibrary for where that song is, and vice versa.I've got MPD rigged up so that when I've got asong currently playing, I can bounce intoVerisuri to display the lyrics for thatparticular song.Something you probably have not seen in my configyet because I haven't finished it yet...What I'm working on is actually integratingOrg Mode with MPD, because I'd really like to be able tostreamline my process where occasionally when I'mlistening to something, I'll take notes eitherabout the lyrics or about the song. I'd liketo be able to link from Org Mode directly toeither that song, that album, or that particulartimecode.I don't have that yet, but I'm working on it.I think I'll probably lean on it quite a bit onceI have it.Sacha: Yeah, yeah.I saw your tweet from May, I think, where you'retalking about coming up with thisway of taking notes.think if you come up with maybe a customOrg Mode link type that can store the annotationand then let you go to it, I think that would bereally interesting.Especially if you figure out, okay, like, are youtaking notes in the album?Are you taking notes on the song?Are you taking notes on this moment in the song?That could all be very cool.Ben: Actually, do you mind if I share my screen?Sacha: Please.Ben: So one of the things I wanted tokind of note was... Let me... Oh yeah, this isactually going to be further embarrassing.I'm a terminal Emacs user.Sacha: I saw.Can you increase the font size, though?Ben: Absolutely, yeah.Sacha: Resizing your terminal so it's smallermakes everything look bigger...Ben: Is that better?Sacha: More.Some of us are older.Thank you.Ben: One of the things that's kind of unusual aboutmy MPD configuration is I've got a satellite config.Sorry, the line wrap is a little bit off here.What this means is that I have...the household file server maintains the MPDlibrary, but I've got multiple playback nodesaround the house. What that lets me do is...Sacha: You can play stuff on this...Ben: So what this means is that I'm controlling the...well, in this case, that's a littleMeLE Quieter3C that I've got in the living roomthat's kind of like thethe home media server, the home playback machine.Fundamentally, I'm an ops guy, I'm a sys admin,and I've got, unfortunately, a fleet of machines around the house.What this lets me do is easily control eachone of them.Instead of each one maintaining its ownlibrary of music files, the file server itselfindexes them, which means that library updatesare almost instantaneous, instead of having toread over the network to pull metadata out ofeach individual file.So having a satellite setup lets me...The real benefit is that MPD tracks albums,directories, and files as URLs.That means that I have a single globalnamespace of URLs for all of the media that'scurrently in the library, which means that I canannotate from any machine, but have it mean one thing.It makes it convenient for navigation, but italso makes it convenient for keeping notes, whichI'm currently doing manually and working on thoseOrg link types to be able to make it a little bitmore smooth than it currently is.Sacha: So you've got your central store of music files.You've got URLs for them so you can talk about them.That's just one identifier per song.You've got multiple speakers that you can useyour completing interface to say, okay, I want toplay this over there.You have Emacs controlling all of that.
9:13Emacs on Android with Termux
Sacha: Now, is that Emacs on your laptop and you justtake it around and you say, okay, I'm going tosit in the living room for a little bit orwhatever and play there.Or do you... Ah, there you go.That's what I was going to ask.All right.So Emacs on your phone.The Android port or Termux?Ben: I have both installed.I think you actually saw this a few monthsago when I was running performance comparisonsbetween the Termux version of Emacs and thenative port, and found that the native portgenerally ran anything CPU-intensiveroughly twice as fast.But I still use the Termux version because it's...I'm not sure if you played around with theAndroid terminal virtualization layer at all, butit's not great.I really appreciate the integration thatTermux gives me with Android features.For example, being able to query the sensors,such as GPS or temperature sensors, and I canactually pull those in from the Emacs side.And that's something I kind of want to get into.Sacha: What do you think of using thetemperature sensors for?What can Emacs do with that?Ben: It's not so much the fact that Iuse the temperature sensors as that when I pullthe sensors, that's the most remarkable valuethat I get out of it.But one of the interesting things is thebarometric pressure sensor, which gives meheight, which matters because when I'm out andabout and if I stop to record an entry in myjournal, for example, I'll just pull the sensorsand dump them into the header in the entry that I'm writing.The barometric sensor, I'll get the GPS, sothat'll tell me what the actual height is at thatlocation on the earth. The barometric sensoralso gives me a little bit of insight into the weather.So it's not so much the temperature sensoris what my temperature is, but just like ifit's a hot day or not.Sacha: I am very curious about that setup.So if you happen to share your, I don't know, Orgcapture for Termux that includes all these things...You don't have to show me the actual stuff, butyou know, at some point I think other people willbe curious about what kind of benefits they canget from running Emacs on their phones.Ben: So, I mean, I guess I'll...One of the benefits that I get is the ability tokeep shopping.So I'm a little bit of a militant cyclist.One of the things that I often do is leaveBen: notes about things that I need to take care of inparticular areas of the city.The nice thing is that I can just drop themdirectly in commits right as I'm going.So I often have a long list of updates that arefrom Emacs on my phone, just because it's convenient.All right.There we go for Monsterdon, in fact.Sacha: Nice, nice.Then that'sSyncthing or whatever else to just get it copied backto your laptop or just on your phone?Ben: Straight Magit mode.Sacha: Oh, yeah?Ben: That's one of the things that Ifound was a little bit clunkier when I wasrunning virtualized Emacs under the Androidemulator and also the native Android port ofEmacs.They are a little bit more troublesome to getMagit to work.Because I rely on that for syncing back andforth from my phone,that's one of the reasons why I stuck with Termux.Sacha: Very cool, very cool.I like Termux's little bar of extrakeyboard keys that you can have, so you can havea regular keyboard and then you can just haveyour Controls and your Alts and whatever on thatlittle extra bar.Termux is quite interesting.Ben: I think I'm probably pushing itharder than it is meant to be pushed.Sacha: That's the fun of it.I don't exactly know how everything will shakeout, but probably with Google trying to lock downthe developer ecosystemin a few months, right?They're saying, oh, you know, it's got to be ADBin order to get these unsigned apps on.We'll have to see how it all shakes out.But I'm hoping Termux can survive because I likethat one too.Ben: Yeah, me too.Sacha: Okay, so you've got interestingmusic, an interesting music setup with lyrics andplayback and all that stuff.You've got your phone, which also runs Emacs andfrom which you can, you've also set it up so youcan control your music from your phone?Ben: Yes.Sacha: Okay.
13:44Keyboards and other devices
Sacha: When I was reading through your toots, I noticedyou like to play around with other keyboards andother devices like the 8BitDo.Do you have any of that talking to Emacs too?Ben: [Keychron B11 Pro is] the keyboard I'm currently using.I got this so I could keep it in my purse andjust like walk around with it, but it turns outit's super comfortable and I'm accidentally usingit as a daily driver.Sacha: All right.Name-drop the keyboard for all the people wholike the device recommendations.Ben: It's a Keychron B11 Pro.It runs ZMK.ZMK, I suppose.And if you've seen me interacting withPete Johanson on Mastodon, he's the lead developer ofthat firmware package.It's a little bit like QMK, except thatfundamentally QMK is a polling architecture, andit works really well for very restrictedmicrocontrollers.ZMK is interrupt-based, which means it istremendously more efficient in terms of power draw.So if you want a Bluetooth keyboard, you shouldprobably run ZMK on it.Anyhow, yeah, it's fantastic.And I do have a bit of a keyboard problem where Itend to...Sacha: Many Emacs people have keyboard problems.Ben: Right? I feel like I'm in good company right now.Sacha: Yeah, yeah, yeah.So, devices, yep, gotcha.And this has become your main keyboard even forregular computing?Ben: In the last couple of weeks, yeah.It's super comfortable.Because it's so flat, I can get away withouthaving to use palm rests to actually keep in adecent posture,because of the curvature of it..Like, it's an Alice layout.The key travel isn't too bad.It's actually quite comfortable.I found that I needed a little bit ofacclimatization to get used to split keyboards.This one has roughly the spread of a split,but it seems to be easier for my muscle memory.So yeah, I didn't intend to leave it on my desk,but it stuck and has kind of stayed there for now.Sacha: Does it have any special ergonomicsfor all the modifiers that we like to do?Or do you just use Caps as Control or whatever else?Ben: Actually, I don't use Caps asControl and I actually really need to get intoremapping that.ZMK has a very rich remapping story, as I expectyou to imagine, from a custom firmware.The macros are intense, and you can getreally deep in customization.I have done none of that on this keyboardyet, because it's only, I think, a couple ofmonths old at this point.Sacha: You've been settling in.As you said, this has been yourmain keyboard for a little while.Charlie Baker says, "yeah, I love the split keyboard.Recently bought one myself.I was getting so much neck pain, you know,shoulder and neck pain from so many hours keepinghands close together."But the split lets his shoulders relax.So probably you're getting the same, like, ha, my arms.
16:44Benefits of a split keyboard
Ben: The two things that I first noticedwhen I moved to a split keyboard was one, mywrists started feeling a lot nicer and two, I wasimmediately able to bench press like 20 poundsmore the next week.It was amazing how much of a difference it madeand it showed up in the data.Sacha: Wow.There was a weightlifting talk at EmacsConf last year, I think.So there's a surprising overlap between thepeople who are checking their weightlifting statsand the people who are into Emacs.Naturally, with Org Mode.There you go.Ben: Let's not look at those numbers too closely.I'm not too proud of a bunch of them.But yes, I find that Org capture templates let mekeep on top of that and not really think aboutit, which is great because you don't want tothink about it.You want to gather the data and think about itseparately so that you canstay emotionally divested from what the numbers mean.Sacha: I know.I'm totally happy to dig into the Org capture,especially if you do any graphing.People always love graphing their progress, right?But if you're like, I don't really want to showmy numbers at the moment, that is alsounderstandable and okay.Ben: I don't, I'm afraid. Sorry.Sacha: That's okay.Ben: It took me a couple years to getused to being on camera at all.I don't really like my appearance very much.I'm working on that, as you can see.I've had to work on that to be able to work remotely.It's a work in progress.I'm getting through it.Sacha: You know us. We're very much focused on text.In fact, you even use Emacs in the terminal.That's very text-y.
18:22Meeting workflow
Sacha: One of the interesting things actually that Iwanted to ask you about since you mentionedmeetings is you probably also have the role ofdesignated note-taker.You mentioned in one of your toots that you takenotes and people are like, what are you doing that in?How are you taking your notes?And it's Emacs.Ben: Yes, so actually that's one of thethings I should lead off with as an apology.I probably won't be able to sharemany of my capture templates because I don't usethem in my personal life very much.They're mostly on my work machine. I'vegot capture templates for impromptu meetingswhere it's just like an exploratory, we have aproblem we need to solve, we're kind of talkingthrough a discussion,a separate capture template for meetings withagendas to make sure that we stay on them andthat we log decisions,and a separate set of templates for... So I havea team of direct reports and they each havedifferent cares and different topics that we'reworking to develop in their professional careers.I find that it's really useful to be able tokeep some continuity between our conversations.It also makes it very easy to export thatentire dataset, share it with them to make surethat they can consult it at the same time.If I got anything wrong, I can fold theircorrections in.But all of those templates, unfortunately, are onmy work machine.Sacha: We'll just sketch out the generalidea in abstract details and then people who wantto implement it for themselves can fill in the blanks.For example, when you're having a meeting withyour direct reports, are the tasks related tothem in an Org agenda?Are you using dynamic blocks?Is it tracked somewhere else or is it in Org?Ben: It's straight in Org mode.So most of the touch points that we have are moreabout professional development and their caresand concerns, rather than specific deliverables.Usually deliverables we manage at a team level.But if there are specific things that individualsare working on, they will be in to-dos undertheir subheadings in a way that show up on theagenda if we have assigned deadlines for them.But generally, if there's a deadline on a task,it's becauseI'm doing something and they need to track it, orthey're doing something and I need to track it.If I'm the only guy using Emacs, then OrgMode won't work for that,so there aren't a lot of those.But if it's just something that I need to checkup on to make sure thatsomething that... Like they've got planned leave forexample, or they have a cousin's wedding thatthey need to go off to, or something that I justneed to keep in my brain that's not necessarilydeliverable, then it would beappropriate to keep that in a place where it'llonly show up on my agenda.So that's the kind of decision making that Iapply there.
21:11Narrowing
Sacha: And you mentioned you have asubheading, I guess a subheading per person orsort of... Or do you use tags to keep trackof something that might touch several people?Ben: Usually a section per person, andthat's more of a convenience than anything else.That lets me narrow to just that subheading andshare my screen during the meetings.Then I don't have to worry about accidentallyleaking anybody else's information into thescreen share or anything like that.Obviously we let off with conversations aboutthem being all right with that.But the ability to very clearly and simplydelineate"This domain of my notes pertains to this person"and being able to be confident and share thatconfidence that there will be no leakage meansthat we can explore topics and talk about them ina way that might otherwise be a little bit morerestrictive.So being able to do that simply, and being able toestablish a very clear delineation around whoseinformation belongs where and where it should beshared turns out to be pretty valuable.Sacha: Yeah, and that's a technique Ithink that especially people who are new to Emacsand who aren't used to narrowing and wideningmight not know how to use effectively.I think narrow is even one of the commands that'sdisabled by default.You've got to say, yeah, you know, I'm not scared.I know what to do.Ben: Yeah, it's kind of bizarre to methat it is, but yeah.Sacha: I can imagine people accidentallytriggering it and they're like, oh no, the restof my file is missing.But if you know how to narrow to a region, andOrg even has those commands to easily narrow to a subtree...I think it's even part of the default speedcommand so you can trigger it right from a heading.But it's great for that kind of restriction.
22:58There's even an internal Slack channel about Emacs at Ben's company
Sacha: Okay, so the reason why I was asking about thatis because some people are working with peoplewho are not using Emacs, so it's very interestingto see what the kinds of... It's very rare forpeople to work with other people who actuallyuse... Are there any other Emacs people in yourcompany, for example?Ben: There's an entire Emacs Slack topicabout that, yeah.It's kind of great.Sacha: What is that like to have co-workers who do Emacs?Because this is a rare experience.Ben: It's super cool.It's also neat to see the very, very differentways that people use it.But yeah, oftentimes people will talk aboutproblems that they have with particular internaltools, and somebody's like, oh yeah, go check outthis repo on GitLab that I published last weekwhere I got sick of it and decided to solve thatproblem categorically.It's actually super cool.And some of the folks at...Generally, I tried not to mention my currentemployer at any point.Obviously, it's not secret.It's in the init file.But yeah, I don't tend to get into that deeply.But some of the other people who work at mycompany, they maintain venerable Emacs packages.So it's awesome to be able to go and talk withsome of the folks whoI've been working on the code that I've beenusing for a decade and we are now coworkers.It's lovely.Sacha: Oh, that's so nice.I'm glad you have that kind of little communityin there.Ben: Yeah. Bunch of nerds.Sacha: So I suspect some people will be looking up yourcompany after this.I think it's also mentioned in LinkedIn and sortof like, okay, let's see if there's any jobopenings.Ben: If it's mentioned on LinkedIn, thatis somebody else.I haven't logged into LinkedIn in 15 years and Inever will.
24:50Ben keeps Org capture templates as individual files and adapts them to different meeting flows
Sacha: We do have a question from Shae.Shae is asking, how do you make a capturetemplate to stay on agenda and record decisions?You got any tips for that?I'm guessing this is more of a human thing ratherthan an Org thing, but maybe you have some ideason how you keep something on track.Ben: So I'll actually push back on the"It's a human thing rather than an Org thing".I strongly believe that one of the real valuesof Org Mode is that it's a very plastic system.Fundamentally, I think that it is a human systembecause it allows you to express exactly thosekinds of trade-offs in a really, really fluid way.I love the personal information management thatPalm Pilots provided, for example,but it was pretty restrictive.One of the things that I love about Org Modeis that if you find that a particular person hasa particular conversation style, it's really easyto modify your Org Mode capture templates to beable to capture that particular flow that youhave with that person.Or with a particular group of a recurringmeeting, for example, that tends to flow in aparticular way.Super easy to update the templates that I use tocapture those particular meetings to make iteasier to match the general conversation flowwith the notes that I will be taking about it.Sacha: Wow, that sounds prettysophisticated.Ben: Well, I mean, that's the thing.It's not sophisticated.It tracks the human process very easily.So it's really lightweight.So you don't have to have a lot of sophisticationto make it very, very useful.You just have to have a feedback loop that youcan tune every time with one or two minutes of effort.And that's one of the reasons why I depend onMagit mode so much is that it lets me trackthose things.But yeah, to answer Shae's question about how doI track those particular things.So if it's a meeting where one of my direct reportshas a particular set of topics that wetend to investigate, like somebody who'sconcentrated on career growth.So we're talking about the areas in which we wantto see development.I'll absolutely update the capture templates forthat particular person to say, all right, here'swhat we talked about last time.Here are the things that were done between ourlast two conversations about how we move in that direction.Were they successful or not?Did it require a lot of effort?So was it something that more effort should beput into to be able to drive down the work over time,to be able to produce those results?But yeah, that goes into the templatesand that goes into the cadence of conversations that wetalked about in Capture.Sacha: So that's really interesting.You modify the capture templates with thenotes that you want to have easily available thenext time you chat with them.Ben: Exactly, yeah.Sacha: The capture templates are still defined as asetq somewhere in your thing, or are yougoing into the customized interface?Ben: So I actually keep the capture modetemplates as raw files that are referred to, andthat makes it easier to version them in a waythat like, I mean, sure, like my Emacs init file,if you look at the Git history of it, it has awhole bunch of different...Sacha: I had not considered having capturetemplates as files files.It worked out really well.Ben: So you can see that... It's a littlebit hard to see with the font this small, but youcan see that I've got a lot of changes to, forexample, my init file, but keeping them inindividual capture mode template files makes it alittle bit easier to just look at the history ofthat particular file and see why particularchanges were made.Sacha: I'm going to try that.I think that's a great idea because it allows youto be a lot more granular about the notes.
28:45Personal-scale software and the journey
Sacha: I saw in that very brief flash of your changelog message that you like to writefairly detailed commit messages that talk aboutwhy a change was made instead of just "a newfunction, new command,"very terse updates that I sometimes just try toget away with.Tell us more about that, because I think you'vehad a couple of toots about reading source codeand reading commits.Ben: Yeah.So I guess one of the things that I most appreciate aboutsoftware in general, and specifically personalsoftware, like personal-scale software that'sbeen written by an amount of people that youcould fit into a room to have a conversationabout it...One of the things that I most appreciate aboutthat is that almost all software written that wayis fundamentally a diary about the way that aperson learned how to solve a set of problemsthat they might not have known about when theydecided to at the beginning.It's this fascinating process oftracking somebody's voyage through the problemlandscape as they discover other people who havetouched on topics that are tangential to theproblem being solved by that software package.It's such a personal and fascinating experienceto see somebody go from "I know just enough abouta problem to be able to decide that I want tobuild a machine that works on it" to "I havethis deeper understanding of how the problemactually exists in the broader set of things thatpeople care about,oriented along the axis of what machinery cando to help solve parts of that problem."The commit logs for a program arelike a map through that territory.It's wonderful to read.It gives you a lot of insight into the persondoing the navigation.When you see 16 commits on December 24th of 2023,like, that person probably had a littlebit of time to work on it.Then, when you see a bunch of commits that are5 p.m. Monday to Friday, like, okay, this isprobably done in a particular way.It's this deeply personal process ofseeing how problems are learned about.I just really appreciate that.Sacha: I think what you're saying aboutpersonal-scale software and getting asense of people's journey as they learn tounderstand a problem and as they start toprototype a solution... Because you never quite comeacross the right solution the first time around.You're figuring things out,You're borrowing things from other people.I think that will resonate a lot with lots ofpeople in the Emacs community, because Emacs useis so personal, as you mentioned when you werelooking at the Slack channel.I was wondering, in your personal practice, asyou figure things out, what kinds of things help you?Is it mostly a matter of actually sitting downand taking the time to write the literateprogramming stuff around the code blocks or thecommit messages,or are there other tools or techniques that helpyou do that?Are there tools or techniques that help me writedown your journey along the way as you thinkabout the code?What's your practice?For example, when you come across something thatyou want to figure out, how do you go about doing it?Ben: So it depends on how I'mapproaching the project.One of the neat things about software is thatit's a document and a device at the same time.You can care about it because of the thingthat it does, or you can care about it because ofthe information that it captures.If I'm digging into a program because it doesa thing that I want to learn, then I'll payattention to the device aspect of it.That usually means that I'll start with itsown documentation.If it has a man page, I'll read that.If it has user docs, I'll read those.Usually, I'll start keeping notes inan Org Mode file, because it's easy to hyperlinkback to it and add my own annotations in parallel to theactual authoritative source.If I'm reading software primarily as a document,then I'll start by reading the source code andthe commit logs, usually the most recent ones,and then I'll jump back to the beginning of theproject and read upwards at the beginning, andthen fill in the blanks iteratively, if it's aproject that's small enough or has little enoughhistory that I can do that.Again, I'll usually start an Org Mode filespecific to that particular exploration that willlet me organize my notes as I go.I really do rely on the external brain that I cankeep in an Org Mode file.I tend to have a pretty decent long-term memory,but being able to bridge the gap betweenshort-term memory and long-term memory is super,super useful.Leaving myself hints that let me dredge thingsback out of long-term andreload it into the hot cache of short-term really,really helps out, because that lets me maintainthat flow state or get back into that flow state.When you've got like the whole program, all thecontext that you've absorbed up until now and theideas of the areas that you want toinvestigate,being able to hot-reload all of that and thenpick up where I left off is super useful.But the meat brain can't do it.I need the machine brain too.
34:07vc-git-grep for finding notes again
Sacha: Tell me about this dredging up process.Are you a lots of little files person?A couple of large files?A project file,there's an Org file in each project that you look up?How do you find things, especially if you don'tremember the exact words?Ben: So it's usually like an Org Modeper category, and they get consolidated.Let me shrink this down a little bit.So usually it's an Org Mode category per file.If there's something super specific, then usuallyit'll live out in its own place until Iget around to...Sacha: I love that you have an Org Filespecifically for Mug Cake.Ben: But that's the thing.It'll live off in its own thing until it getsintegrated into the list where the long-termstuff lives.I promise this is pertinent toyour actual question.Sacha: No, no, this is very pertinent.Ben: Usually, lots of littlefiles while I'm still working on something,and then as that something becomes part of my larger lifeand has links to other things,Ben: then it'll kind of get centralized into one of the Org Modefiles that has a broader topic purpose basically.Sacha: If it's very long-term and you can't rememberthe exact words to find something, how do you generally findfind your notes?Ben: That way.Sacha: You start grepping various keywords and try tofind it.Ben: Yeah.So one of the things that I don't yet have turnedon, and actually you inspired me to look intothis, was so... Actually, do we have enough time?Yeah, okay.I'm just looking at the clock.One of the things that I wanted to get into waslong-term searching and searchability.Yeah.So I use SQLite's indexing for a lot of things,and I really appreciate its porter stemmingbecause it lets you search for stemmed words,which are kind of approximate matches, which isuseful, but less useful than vector databases.You actually put a little bit of work intoindexing topics based on vector similarity.Sacha: Which I haven't gotten back to, soI'm glad to see you managed toexperiment with it,see what you come up with.Ben: I don't have anything to showyet, except that it's an interesting topic.Because I'm word-oriented, I will often rememberindividual keywords, or I'll have enough patiencethat I can sift through a list of potentialkeywords as I'm grepping for stuff.
36:55Add keywords to make things easier to find again
Ben: If I find that my first three or fourattempts at searching for a keyword don't findthe topic that I'm looking for, once I doeventually dig up the topic, I will add thosekeywords that I was searching for just so I canfind it next time, expecting that future me willprobably behave more or less like current me.But yeah, being able to use a vectordatabase to search for headings that includerelated topics instead of related words...I'd really like to get there.I'm not there yet,and I think that's going to be interesting and useful.I also think it's going to be challenging torepresent Org Mode's hierarchical structureto restrict the trainingof particular vector setsto be able to cast an increasingly wide netfor where I want the searches to run.I don't know how that's going to be solved yet,but I'm going to be looking into it.
37:47Keybindings and terminals; wezterm
Sacha: @PuercoPop has a question regardingterminal Emacs."Do you need to change any of the keybindings toavoid clashes with the terminal?"Some keybindings don't work onterminals, or some terminals already have thesekeybindings set.Ben: So I have good news and bad news there.The good news is that... One of the terminalsthat I'm using right now on most of my systems isWezterm.It's really good.I started off with [meant to say Kitty], and I kind of bouncedoff that project because of some of the behaviorof the lead developers.I won't get too deep into it, but what I found isthat Wezterm...It gives me very little friction that way.It doesn't have a lot of its own keybindings,and that's a relief.It means that I really don't have to... I don'tthink I've had to put any time into customizingWezterm to get out of the way of Emacs or Screen.I should mention, I am running Emacs withinScreen,so that introduces another layer.C-a a, for example, is
beginning-of-linefor me, even though C-a itself is thedefault Emacs keybinding.But yeah, I think that I ended up choosingterminal software specifically on that merit, sothat it doesn't get in the way.That's actually one of the things that Termux is...The default Google keyboard keeps stealing key bindings.It'll update, and all of a sudden, I can't use aparticular Alt key combination anymore, becausenow it invokes a Google keyboard shortcut forpulling things out of the Android clipboard, forexample, which is really kind of annoying.But yeah, in terms of terminals on full-fledgedmachines, I generally don't have that particularproblem.Except for... What was the name of it?It was... It was a zap-to on-the-screen packagethat alphapapa wrote, I believe, that I...Actually, let me just look it up.Yeah, Avy.Sacha: Oh yeah?Ben: So I wanted to start picking thisup in 2021, and yeah, control colon doesn't workin terminals.So the mode itself is fantastic, and when I'musing it in a GUI Emacs, it's very useful, and Irarely use it now specifically because of thatparticular terminal problem.That is self-inflicted.That is not a problem with the package.That is a me problem.But yeah, I think this is the notable one that Ibounced off of.Sacha: Yeah, just trying to find theright, you know, a key binding that will pass.Because you can't get used to key binding in onesystem and then not have it available in othersystems.It's just going to mess with your brain.So yeah, Ray points out, yeah, Google stealsControl Shift K, which he uses to kill tabs tothe right.So it's like, oh.Ben: Infuriating.Sacha: Yeah, yeah.And of course, none of the standard keyboardshave a super key on the thing, so you can't justtack on another modifier that'll get through.Ben: That's actually one of the reasonsthat I picked up this particular keyboard isBrian Carlson.Another Torontonian.He works on Git LFS and some other stuff.He was specifically looking for a keyboard thathad a super key that he could remap.So four keys to the right of space, which thisone has.And because it's ZMK, it's remappable.I think this might be suitable for his purpose,except that he wants a QMK keyboard, specificallybecause the development tools for QMK arepackaged in W, and that's a plus for him.Anyhow, yes, I agree.Like, not having a super and a hyper key on akeyboardSacha: feels like like one of your fingersthat are cramped up we need more modifiers well Imean you know pipe organTell us about that.Ben: What is this?Oh yeah, foot pedals.Yeah, and I actually do have these wired into alittle Atmel microcontroller running QMK.I honestly tried the foot pedal thing for alittle while.It was hilarious.I did not find it useful.Sacha: I don't have the hand-eye-footcoordination to do that thing at the same time.Ben: Maybe if I had learned piano as akid, it would have been good.But yeah, I don't seem to be able to do it.But I gave it the old college try and it was fun.
42:38Timers: tea-timer, tmr
Sacha: Okay, I had a couple of things Iwanted to ask about in your config.You have two types of timers.You have tea-timer and you have Prot's TMR.Do you use them for different purposes?What's going on here?Ben: So I'm trying them out.I tried TTimer first because it was the first onethat I found, and it was useful.I was actually using it in meetings because Iwould, when I was trying to keep us on topic, Iwould use that to remind us that, oh, we've gotfour other topics we need to get through.We're trying to give this one five minutes.We're running up on four.And it was just because it was super low overhead.It was easy to pick up.It was easy to have it displayed on my screen.And it didn't feel like I was...Running a chess timer on everyone.It's very subtle in the mode line.It was useful.But what I found was that when I was...Running multiple timers simultaneously.TMR, just the fact that you can list all thetimers and manage them in a separate buffer.It's richer and I appreciated some of itsdocumentation more.I haven't fully switched over to it yet.I'm still in exploratory mode.In fact, this is probably like... So I actuallyhave a bunch more packages installed than I'mcurrently using.So I'm going to shrink this down a little bitjust so that it doesn't line wrap.
43:57Different stages of package use
Ben: But yeah, so I've got, I think about 140 packageslocally cloned.I've got, as you can see in my init,about half of that actually installed.There are various phases. I've got a clone, but I don'thave it in my init. Or I have it in my init, butthere's another package that offerssimilar functionality. Or I'm all-in on this particularchoice and I'm removing the other ones.It's a gradual kind of progression.Sacha: Yeah, we're all auditioningpackages to see if they fit in our workflow andall that stuff.I was curious also about your elfeed things.You have a few things related to elfeed.Actually, do you want to talk about the thing youjust highlighted first?Ben: No, we'll get back to it in a sec.I'm keeping an eye on the clock.Sacha: But yeah, elfeed.
44:47Elfeed
Sacha: Yeah, you've got a couple of interesting thingsin your elfeed setup, including picking a searchfrom a bookmark or other shortcuts like that.Ben: So I have discovered in myconversations with the new maintainers for elfeedthat my elfeed habits are very deviant.So let me actually start off by going through...You can see that like the top of my elfeed list iskind of in one format and down at the bottom, theones that are closer to present day are organizedin a different way.Sacha: You've got tags now.Ben: So my feed list is, I mean, itstarted in Google Reader.It's kind of been forward ported since then.It's long.Sacha: I love the comments.Ben: But one of the consequences ofhaving a long feed list is that I have anenormous elfeed database.And I use it for completely different purposes.So for example, I'm going to show off that littledefault search.I keep an eye on projects that have releasesthat aren't packaged by Debian, for example.So I've got this little ATS Mini.basically a small AM FM radio that happens to runon ESP32 and the firmware for this communitydeveloped and it's kind of interesting but likethat's not packaged by Debian so I pay attentionto or I subscribe to the release feed for thatparticular project.I do keep in keep an eye on the Linux kernelespecially these days becauseThe gallop of security vulnerabilities coming outof mythos analyses right now means that I have topay more attention than usual to fresh updates.So generally I run Debian stable, but I do runkernels from backports and I want to know whenthose are available.MeshTastic.I was paying more attention to this a couple ofweeks ago.I'm not going to reach that right now.But yeah, the local MeshTastic community inToronto is plagued by a couple of griefers thatmake it a lot less fun.So I'm paying a bit less attention to that rightnow.But yeah, so like one of the bookmarks that Ihave is software currency.Another one is, for example, the YouTube channelsthat I subscribe to.Thank goodness for Elf YouTube.It is so nice to be able to get the transcriptsfor things directly in there.Sacha: There's even a sponsor blockintegration so you can skip all the promotionsand stuff.Ben: Yeah, I mean, so again, my use ofYouTube is also deviant in that I pretty muchstrictly subscribe to RSS feeds and they'redownloaded onto the file server.So when a new recording comes out, it getsdropped there.I'll watch it.And then I've got a little widget that scansthrough my Kodi database for watched files.Once YouTube files show up in the watch list,they're automatically purged.And it means I just never see an ad.It's not just satisfying.I don't think that I would be able to payattention if I was being constantly interruptedbecause of the way that my brain works.But yeah, so being able to quickly search forYouTube is one thing.
48:36Spoilers
Ben: I do have articles that I've marked as spoilers for mediathat I haven't watched or read yet.Maybe I go to an extreme degree, but I'll try toavoid previews for new movies, for example.I do want to actually go back to them later on.Marking articles as having spoilers that I wantto revisit later on is an easy way of not havingto worry about it anymore.For a while, back in the days when I waspaying more attention to... Enter the Dragon wasthe sequel to... What was that TV show?George R.R. Martin... Seven seasons and it went badly off therails in season six.Sacha: Game of Thrones.Ben: Thank you.So when I was watching that for a while, severalof the sites that I was reading would haveindividual episode updates.I really appreciated having a list of Elfeed keywordsthat I could automatically apply those tags to.I wouldn't even see the headlines because itwas problematic enough.I've become less...Sacha: Nice.Ben: I've been putting less effort intomaintaining that now, but the infrastructure isstill there, and I can lean on it if I need to.
49:53Comparing different news sources
Ben: One of the things that I don't have set up right nowis a quick search for the municipal topics that I cover.I tend to subscribe to a lot of news because Ifound that, for example, it's interesting to seehow the Toronto Star covers stuff differentlyfrom other local newspapers.It's useful to be able to quickly pull upthe five or six publications that covernews in the same way and then look through theannotations for them or add the annotationsmyself. I can see that this particular storywas covered by this journalist in thispublication in this way, and then it lets mecross-reference so that I can pay attention totopics that are under-covered or publicationsthat habitually leave out particular aspects ofnews stories.It lets me pay more attention tothe trends of publications as well as to thestories that they're covering.The consequence of that is that my Elfeeddatabase is 4.5 gigs.It's common for me... I don't know ifthat actually shows.It's common for me to have like 65,000 articlesin like the six month horizon for the default Elfeedsearches.That means that searches and redraws areexpensive, even on a relatively performant machine.You've probably seen some of the workaroundsthat I've got.This little bookmark selector is one of thoseworkarounds.Elfeed's interactive search: super, super useful.But if you're trying to do it like this...Actually, it's not so bad.Sacha: I love this.When you see someone using Elfeed for 65,000items, you get a sense of what it can be usedfor when you're really stressing it.Ben: And the organization of the bookmarks that I havemeans that I do have one river of newsof all of the things that I want to pay attention to.But it's seven or eight pretty divergent topicswith not a lot of overlap.It's neat to be able tothink of Elfeed as the thing where newsinformation comes in. The individualbookmarks are the topics that are groupedtogether that I actually care about and want toread about in concert with each other.If I specifically narrow it down to just Emacsstuff, one of the things that I can easily do isadd a bookmark for that search. If I name it...Now that I've got that bookmark saved...Sacha: And then it's like, bookmark annotation.Ben: Right. It ends up in my dot file as a git commit.
53:13Bookmark naming conventions
Ben: Now that that bookmark exists, simply because ofthe naming (elfeed-search-...), now it shows up incompletion.Sacha: Precisely this sort of stuff.I am under-utilizing bookmarks, I think.
53:24Naming conventions simplify building new workflows
Sacha: I love this idea of using bookmarkswith a naming conventionand then writing Emacs Lispto take advantage of that naming conventionto make that easy to jump to, easy to use in other things.Ben: Yeah, it means I don't really have...When I notice that there's a thing I don't have,it diminishes the activation energy forbuilding a new workflow.It means that I don't have to put a lot ofthought into it.I can quickly do it.If it turns out to not be useful, I canquickly get rid of it.Sacha: Very cool.
53:50elfeed-curate for annotations
Sacha: I had one more thing that I wanted to ask youabout elfeed that I want to squeeze in,in the five minutes that I have before the kiddo comes outfor lunch break.This elfeed-curate that you're using, this is thefirst time I've come across it in the config.Are you sharing your notes or your selectionswith other people, or just for yourself?Ben: No. It's just for myself.What I'm trying to replicate is,back in Google Reader days,I really appreciated how when you added an annotationto an article that you were reading,by default, that annotation would be viewable byother people in your social graph.That was super interesting.Sometimes you could also add personal-only annotations.Actually, I don't think that's the keyword that it uses.I think it’s just +ann.Sacha: Also, because you have an Emacskeyword on it, so I don't know whether you haveany annotated Emacs ones.Ben: Yeah.Well, I mean, generally, if I've added anannotation, it will be.So, for example, here.Right? Super useful.Well, super useful for me, anyhow.If I find that there's an annotation that is more...This is inflammatory.It doesn't need to go out on the web.Sacha: Sorry, did you need me to kill the stream?I have a 10-second window.Ben: No, it's all good.I'm not worried about sharing this, but it's notsomething I've posted.What the nice thing is, is that if it issomething that I want to share, it's really,really easy to just drop this into Mastodon mode.Sacha: Very cool.Ben: That's one of the ways that I use it.Because the annotations are just Org Modefiles, you can add links to other things and itbecomes just as useful as anything else.
55:46mytoots archives Mastodon toots
Sacha: You're saying this can flowinto Mastodon and from there, once you toot it...I think you're using mytootsthat you mentioned in your config forarchiving it?Or searching, yeah, okay.Oh, except it's currently not working.Ben: Yeah, I don't have it loaded right now.So mytoots, it just loads the backup ofyour export archive.So it loads the...What's the most recent one?So yeah, it was your outbox JSON file, which isthe most recent one here.I've got an Org Mode reminder to download itroughly quarterly.My outbox is about 100 megs right now.Compared to 4.5 gigs for elfeed,it's not so big.That's not going to redraw properly.I was just going to show it. My residentEmacs memory size right now is about like five orsix gigs.It's a little bit embarrassing.By comparison, 100 megs of JSON in RAM is not sobad, but having an instant search foreverything that you posted and being able tobounce back from your local archive to theconversation thread that's live and see anythingnew that comes in...Again, it lets me exercise that outboard brainkind of idea.So yeah, mytoots is super useful.Sacha: I like it a lot.
57:06Mentoring offer
Sacha: The last thing that I want to askbefore the kiddo (because I have twominutes) is that you have a mentoring offerpinned in your Mastodon, too.Have people taken you up on it, especially ifthey've taken you up on Emacs?Is that ongoing or forgotten about or whatever?Ben: Not forgotten about.Very much real.Still open.I haven't refreshed that offer recently, but it'sstill pinned for a reason.I think maybe a dozen or so folks haveasked for that.9 or 10 success stories, I think.Nobody's asked for Emacs tutorialship yet.That's fine.Again, I love Emacs.I use it.I don't often recommend it.But if anybody was curious about that, I would bevery open to the idea.Sacha: All right, so if people want toalso develop a very interesting elfeed setup,they know who to talk to.Ben: Please, I would love that conversation.Sacha: Thank you so much.Yes, go ahead.
58:02A local instance of public-inbox can let you use Gnus to read mailing lists quickly
Ben: Oh yeah, there's just one lastthing I want to mention, and I realize we'reright up against time.I use Gnus. I use mu4e for email,but I use Gnus for reading mailing lists.I just wanted to strongly recommend toanybody who is thinking about it... This is amazing.You gotta do this.public-inbox lets you keep a complete localarchive of the entire mailing list.Searches are instant.Tracking threads is instant.Check it out.It's fantastic.Sacha: That's great becauseEmacs-devel has a gazillion threads. How do youeven keep track of all this stuff? But if you'vegot Gnus and if you've got this set up,then you're no longer dependent on the goodgraces of Gmane being around as an NNTP tomailing list gateway.@JonKishore11 wants to know if you have aYouTube channel. I'm guessing you don't yet.Ben: I don't really like my face very much.Sacha: Ping Ben on Mastodon and ask about this mentoringthing and then share your notes so we can alllearn from it.Thank you so much for today.I look forward to chatting with you more through toots.What is the verb, you know, in the fediverse?All right.Thank you so much.Sacha:
sachactube: only slightly panicking behind the scenes 🙂 Nudged Ben by Mastodon in case we got our wires crossed
sachactube: yay, he's here, just getting sound sorted
symbiopoyesis: Good morning
x_goose_x: hey gnomonnnnnn
CharlieBaker707: yay! love the split keyboard. recently bought one myself. i was getting shoulder and neck pain from so many hours keeping by hands close together, but the split let's my shoulder's relax.
ShaeErisson: How do you make a capture template to stay on-agenda and record decisions? Any tips?
PuercoPop: Regarding terminal emacs, did they need to adapt any keybindings to avoid clashses with the terminal keybindings?
AyanRaza-n1x: is that the creator of emacs?
Ray-On-Emacs: Yes! Google steals C-S-k, which I use to kill tabs to the right on Vivaldi!
CharlieBaker707: elfeed is like a distraction alleviation machine. I agree with Ben, I don't think I could consume any of this information outside of Emacs. Ads and even varying formats distract me too much.
JonKishore11: do he have YT
sachactube: Thanks for hanging out, everyone! =)
gnomon027: Thanks so much for running this whole series, @sachactube !!
Here’s a niche problem: When viewing iPhone-captured photos in org-mode on Linux, they always appear in landscape orientation, even if you took them in portrait.
The reason is that the sensor of the camera on the phone is physically embedded in landscape mode, so all photos are in landscape mode; when you hold the phone in portrait mode (which is how you hold it most of the time), the phone detects that and implements a fix in the EXIF data file. Essentially, a software fix to a hardware design issue.
Since most photos I take are usually in portrait orientation, it means I need to twist my neck and view images at a 90-degree angle when I look at my Journelly entries.
The source of the problem seems to stem from how org-mode interprets EXIF data in the photo: it doesn’t. It relies on other parts of Emacs, which in turn rely on parts of the OS, to do the job. On Linux (at least on Kubuntu, which is what I use these days), those parts don’t handle EXIF orientation information. Why and how, I am not sure, it’s more digging than I have the time for right now… but anyway - there’s a simple fix.
This operator reads and resets the EXIF image profile setting ‘Orientation’ and then performs the appropriate 90 degree rotation on the image to orient the image, for correct viewing.
To execute on an image file: mogrify -auto-orient <file>.
And because I use Emacs, of course there’s a dwim-shell-command solution:
(defun jtr/dwim-image-auto-orient ()
"Auto-orient images based on EXIF data using mogrify." (interactive)
(dwim-shell-command-on-marked-files
"Auto-orient images""mogrify -auto-orient '<<f>>'" :utils "mogrify" :silent-success t))
That last part,:silent-success, closes the empty buffer that pops up after successful execution, as mogrify doesn’t really produce an output window. So, it will just bring us back to Dired.
Two years ago I wrote a super simple todo package for Emacs to see whether I could get over my tendency to 1) procrastinate a lot and 2) being annoyed at my tendency to procrastinate.
Reader, you won’t believe the answer: Yes and no. Or rather, no and yes.
That is, I still procrastinate a lot (but less than before), but I’m way less annoyed about it.
As you can clearly see from the Emacs mode line above, I’ve got 27 “new” things on the todo list, 4 things that are “in progress” and… tada! 616 things that have been done!
It cannot be! That’s about one thing per day! (Although it’s more like no things most days and then eight things on a random day.)
I’ve had a look at other todo systems, but like I said two years ago, they seem to be specifically made to enable further procrastination: You can tinker endlessly with priorities, deadlines and arranging things in hierarchies. So you can spend an hour working on your todo list and then feel you’ve really made some progress. Which is a total lie, of course.
And open sores todo lists encourage further meta-tweaking — altering the todo software itself, so that you can add even more features. I’m proud to say that after starting to use it two years ago, I haven’t touched the anddo code even once! Behold the Microsoft Github repo!
So it’s just a list of tasks, and the ones that are new or in progress are shown. You can edit them and add notes, and you can change status, but that’s it. No dates, no priorities, no nothing. Just do it.
This system has really made me actually take care of a lot of annoyances and stuff, but even more importantly: Instead of looking at the lamp that’s sitting on the hall table every day and going “oh yeah, I should hang that on the wall somewhere; I must remember that”, I just put it on the todo list… and then my brain stops doing that every time I see the lamp on the hall table.
The system works!
(And then I might even hang it on the wall one day when I’m in the mood to do something, and I pull up the todo list… I might.)
This is the sixth post in my series on Emacs completion. The first argued that Incremental Completing Read (ICR) is a structural property of an interface, not an interface itself; the next four made it concrete with the VOMPECCC framework, my Spotify client (spot), a produce picker, and a VOMPECCC showcase. This one coins a term for a special case, Incremental Suggesting Read (ISR), where the candidate set produced by incrementally typed input is a suggestion, rather than a literal completion of that input. The ability to generate inferred matches in addition to literal matches vastly expands the scope of what a 'completion' system can do. Two conceptual sources supply the suggestions: 1) semantic retrieval and 2) generative synthesis.
This post is more speculative than useful, so carry that pinch of salt with you as you watch the video or read this post.
2. From completing to suggesting icrisrdefinition
The initial post in this series broke down the 3 key words in ICR: read (type input), completing (match input against a candidate set), and incremental (recompute matches on every keystroke).
ISR is perfectly analgous, except the materialized candidate set is not a list of literal match candidates, but rather a list of inferred suggestions.
Put another way, in completing read, the candidates lexically correspond to your input. In suggesting read, a model infers candidates from your input.
In this post and video above, I discuss two sources of ISR:
Semantic retrieval: the candidates are generated from concrete, existing data (notes, mail, code), and ISR ranks them by meaning, or semantic proximity to the input. A well established demo of this in Emacs is John Kitchin's "semantic completing read", which ranks Org files by embedding similarity to what your input 'means'.
Generative synthesis: the candidate is written on demand (by an LLM) and is effectively non-existent prior to your input, which can appropriately be called a 'prompt' in these cases.
3. The same machinery, a different source substratethesis
Because Emacs built-ins (and of course VOMPECCC) are so strong, they can be used to support ISR use-cases. Everything we demonstrated in prior posts is relevant here, most of all the consult package.
The key enabler of ISR is the fact that completing-read's candidate collection can be a function: Emacs passes your input to a function, and that function returns the inferred suggestions. That's powerful in the abstract, and concretely it enables our semantic retreival and generative synthesis use cases.
Because Emacs clearly already supports this abstraction, everything downstream of the source (everything we've demonstrated in this series up until this point) is re-usable. In line with a common theme in Emacs, our precious, hard-earned muscle memory transfers perfectly from ICR to ISR.
consult is especially enabling of ISR because it is functionally optimized to supply candidates from a backend rather than a materialized list. We've already shown how we can hand consult a function (that hits an API) and consult supplies asynchronous fetching, keystroke debouncing, live preview, grouping, narrowing, and (optionally) Embark actions.
There are already public examples of ISR in action:
In the generative syntehsis case, an integration between 2 packages provides a beautful example. Armin's consult-omni ships with a consult interface to karthink's gptel in gptel source. This integration with gptel's suggesting backend is defined the same way as it would be with a completing backend, whether that's ripgrep or a search API. Another generative synthesis example would be completing for the possible LLM expansions of inline code or prose (think GitHub Copilot's Emacs extensions).
In the semantic retreival case, I implemented an example borrowing from John Kitchin's org-db-v3 for the embeddings.
The point of this section is that the existing ICR machinery (especially consult in this case) can be re-used for ISR. Below you can see how I reuse it. Pay special attention to consult--read and consult--dynamic-collection in my chiply-isr-semantic-read command:
(defun chiply-isr--collection (input)
(mapcar (lambda (r)
(cons (format "%-22s %s"
(file-name-nondirectory (alist-get 'filename r))
(alist-get 'chunk_text r))
(alist-get 'filename r)))
(chiply-isr--search input))) ; embedding search over my sample corpus
;; consult drives that function as a live, per-keystroke source.
(defun chiply-isr-semantic-read ()
(interactive)
(find-file
(consult--read
(consult--dynamic-collection #'chiply-isr--collection
:min-input 3 :debounce 0.3)
:prompt "Suggesting read (by meaning): "
:lookup #'consult--lookup-cdr
:require-match t :sort nil)))
The suggesting--collection function is the only thing that differs from a completing character-matching source. The full chiply-isr module lives in my Emacs config, but I built it for this demo, so I don't recommend you use it.
4. What makes suggestion special hcicognition
The primer praised ICR for its cued recall, an alleviation of the burden on recall, with a small tax on recognition. With ICR, you supply a fragment (almost an 'intuition' considering Orderless's flexible matching capabilities), then pay the incredibly cheap cognitive cost of recognizing your target from a winnowed candidate set.
ISR moves further from recall, and opens the door to speculation. In semantic retrieval you supply only a gesture at meaning, and in generative synthesis you supply a question. In this way, I think about the goal of ISR as exploration and discovery, and the goal of ICR as arriving at a destination.
In my opinion, ISR's real value props are 1) reaching candidates that literal matching can't (semantic retreival), and 2) providing detailed answers to open questions and possible expansions (generative synthesis).
5. Notes on the demo
The demos from the companion video, and where their code lives in my Emacs config:
The text editor I'd used for 20 years on an MS-DOS platform would
change the shape of the cursor to distinguish between insert mode and
overwrite mode. A filled rectangle (or box, in Emacs terms) indicated
overwrite mode; an underscore (hbar), insert mode. It's a nice way to
make overwrite mode obvious.
The video starts out with Emacs in Insert Mode. Then Overwrite
Mode is activated, which changes cursor-type from hbar to box. The
word "Overwrite" replaces "Insert" to demonstrate the effect.
Anyway, here's a function to implement that behavior in Emacs:
(defunzeit-change-cursor-shape ()
"Set cursor shape depending on overwrite mode.When overwrite-mode is nil (off), set cursor-type to hbar.Otherwise, set it to box."
(interactive)
(if (not overwrite-mode)
(setq cursor-type 'hbar)
(setq cursor-type 'box)))
(add-hook 'overwrite-mode-hook 'zeit-change-cursor-shape)
2026-06-02 Add add-hook as suggested by @Howitzer105mm@pdx.social
The point of this post is to pass on the announcement that sdcv-quick has been updated. If you do any writing at all you need sdcv and Webster’s 1923. Having the OED is a bonus. Happily, it’s easy to have it all in Emacs with sdcv.
Things have slowed on the Emacs front. I am not tweaking the init file every day and trying to get things just right. It is a good state to be in. There are no pain-points in the use of Emacs which has been bugging me and after all this time, it is a relief. Don’t get me wrong. I love tweaking. I love discovering new packages and trying them out. That is fun for me. But the basic configuration is stable, I understand it, I am trying to incorporate it into my muscle memory and things are peaceful.
That doesn’t mean I haven’t discovered a few nice things from the last time I wrote about Emacs. Here we go.
You run the anddo command and then you can manage your task list with anddo. It is an useful addition, which is well implemented. I love it.
;; Bound to M-o 8
"8" '(anddo :which-key "anddo")
Substitute
I have been trying out multiple-cursors and for some reason I never seem to use it. I have tried to make the keyboard commands easier. I have included it in the general.el setup, but at the end of it, I never seem to use it.
My config for substitute (directly copied from Prot’s suggestions):
(use-package substitute
:ensure t
:config
;; Set this to nil if you do not like visual feedback on the matching
;; target. Default is t.
;; (setq substitute-highlight nil)
;; Set this to t if you want to always treat the letter casing
;; literally. Otherwise each command accepts a `C-u' prefix
;; argument to do this on-demand.
(setq substitute-fixed-letter-case t)
;; If you want a message reporting the matches that changed in the
;; given context. We don't do it by default.
(add-hook 'substitute-post-replace-functions #'substitute-report-operation)
;; Use C-c s as a prefix for all Substitute commands.
(define-key global-map (kbd "C-c s") #'substitute-prefix-map)
(define-key substitute-prefix-map (kbd "b") #'substitute-target-in-buffer)
(define-key substitute-prefix-map (kbd "d") #'substitute-target-in-defun)
(define-key substitute-prefix-map (kbd "r") #'substitute-target-above-point)
(define-key substitute-prefix-map (kbd "s") #'substitute-target-below-point))
#+end_src
And a general.el keybinding:
;; Bound to M-o 3
"3" '(substitute-prefix-map :which-key "substitute")
Emacs and the Sunk Cost Fallacy. Elaborated on the notion and came up with the assertion that this is not sunk cost fallacy at all. It is sunk cost. Sunk cost fallacy is a different thing altogether. The key statement to me was “A person commits the sunk cost fallacy when deciding to continue a project by considering the irrecoverable investment rather than the future utility of the project.”
I loved this analysis but let me move away from the description of this phenomenon and explain my reality when it comes to this feeling.
I use Emacs and Org-mode for my writing. For the blog I use Markdown. All of my writing gets done in Emacs. I get distracted by the new shiny once in a while. I tell myself that I am curious about new solutions because it gives me things to write about for the blog. I am not sure that I buy that rationale. Yes, it does give me new things to write about. Sometimes the products I write about become a critical part of the workflow but most of the time I go back to whatever solution I was using previously. For the last couple of years, the most used application on my computer has been Emacs. I write. Every day. In text files. Emacs is the center of all of that activity. I try out every text editor that gets released or that I come across. Look at it for a while and return to Emacs. Why?
I am familiar with Emacs. If you break down the cognitive load of writing into two components: the content you are writing and the load coming from the act of dealing with the application you are writing in, the Emacs load is minimal. I know the program enough to not have to worry about it. Low cognitive load on that front. That is the ideal state and new applications don’t reach that state without considerable effort.
The nature of Emacs. If I don’t like how Emacs does something, I know enough to be able to hack a solution together which makes Emacs behave the way I want it to. The ability to customize Emacs is one of the killer features which keeps me using it. I must admit that I am a novice in this regard. I am learning and trying to get better.
I have spent an ungodly number of hours figuring out how to use Emacs. That knowledge is paying off in the increased comfort I have with the program. Picking up another text editor and learning how to use it is a chore which I don’t need to do. It is not a fear of throwing away all the time I invested in Emacs, it is the ridiculousness of learning how to use a new program when I am perfectly happy being in Emacs. In other words, I don’t see the upside.
I am not doing a cost-benefit analysis of what it would mean to switch from Emacs to something else. Why would I? I am happy here.
I was talking to a non-technical new Mac user who asked for advice on text editors and knowledge management. I told him to look at some alternatives, Bear, Ulysses, Scrivener, iA Writer, Obsidian, VSCodium and BBEdit. Pick one and dive into it. Delete all the others from the machine and use that one program for the next two years. Two years of regular use is sufficient to understand BBEdit, VSCodium and Scrivener. Obsidian would probably take six months. The rest of the programs need a week of use. But stick to that solution. Use it all the time. The maxim is “The best editor is the one you know.” The ideal state is when the cognitive load of using the program is near zero.
If I was talking to a minimally technical Mac user, I would suggest they use Emacs. No question. Start the journey today.
That is my take on the sunk-cost fallacy and Emacs usage.
I find myself agreeing with the author. I did go through a switch from using babel and Org-mode to manage my config file to using a plain elisp file several months ago. That was the main change. I can’t see myself throwing away a couple of years work to start from scratch, that sounds crazy to me. I do make changes and add/delete things depending on my usage. That is a process which might go on for ever. That is okay. It means that I am learning new things or finding different ways to do the same things. I try out something new, if it works, I keep it. If it doesn’t I go back to what I was using before or come up with a different way of doing it. That is what makes the journey interesting and involved.
If you use both Scrim and Emacs on macOS, you can “Open in Emacs” a file or directory from Finder via the “Quick Actions” context menu. This is implemented as a macOS Shortcut, shared as an iCloud link:
I find macOS drag-and-drop operations from Finder to Emacs (in particular, the NS variant) to be frustratingly inconsistent.
Dragging a file or folder from Finder to a non-Dired buffer will visit it.
Not always! This behavior can be mode-specific.
Dragging a file or folder from Finder to a Dired buffer will copy it.
Dragging a file or folder from Finder to the Emacs icon in the Dock will visit it in a new frame.
Add to that, drag-and-drop isn’t graceful with a long traversal path on a large screen.
Ultimately, what I want is a single consistent action to “Open in Emacs” a file or directory from Finder. This post describes a solution for achieving this using the Scrim and macOS Shortcuts apps.
Background
Scrim
Scrim is an Org protocol proxy app that I had published on the App Store last year. Implementing Scrim was largely an exercise in reverse-engineering emacsclient to be a macOS app. This is because macOS apps these days (macOS 10.14+) are packaged to execute as a hardened runtime (aka “sandboxed”). Among the many things a hardened runtime restricts is the ability for an app to exec another program outside of its sandbox (like emacsclient).
In lieu of that restriction, macOS provides different APIs to let apps work with other apps, among them Services (more below), Intents (a whole different topic worthy of another post), and custom URL schemes.
A custom URL scheme is what Scrim provides to allow other apps to do emacsclient-like things like opening a file. By design, Scrim does not surface all the capabilities of emacsclient such as arbitrary Elisp execution. The custom URL scheme itself is scrim://.
Currently the scrim:// URL scheme supports the following two actions:
Visit a file or directory (e.g. scrim://open?file=~/.profile)
Open an Info node (e.g. scrim://info?node=(eshell) Top)
For this post, we will concern ourselves with (1) visiting (opening) a file or directory.
Note that on a system where a custom URL scheme is installed, any app on that system can open said custom URL scheme.
Services
The macOS Services API allows an application to access the functionality of another.
Apple uses this API to provide tools to help orchestrate workflows between different applications, namely AppleScript, Automator, and Shortcuts. Note that as of this writing, AppleScript and Automator are in maintenance mode, and Shortcuts is intended to supersede Automator. Once a workflow is defined, it is usually accessed via a context menu sub-menu labeled “Quick Actions” or “Services”.
“Open in Emacs” Shortcut
With the above background out of the way, let's turn our attention to the Shortcut itself.
The shortcut invokes a Python script which forms the scrim://open?file custom URL and then opens it with the open command line utility.
When installed, the menu item "Open in Emacs" will appear in the "Quick Actions" sub-menu when the context menu is raised on an Finder item.
“scrim link” Shortcut
What if you want just the Scrim URL to open a file or directory instead? The shortcut "scrim link" shown above provides that, putting the URL into the system clipboard to paste into another native macOS app like Notes or TextEdit. Such apps that recognize custom URL schemes will treat the URL as a “live” link.
While the behavior of "Open in Emacs" seems modest, a lot of things need to happen under the hood to orchestrate the different apps required to make it happen on macOS. But taking a step back, I'd observe that this is not unique to just macOS alone. Other contemporary operating systems are following suit as seen with Snap (Ubuntu) and Flatpak for Linux, and UWP for Windows. Such isolating practices by OS vendors are not going away.
Running counter to this is the spate of interest in orchestrating apps and/or services together using AI tools, whose approach so far is to bypass all hardened runtime boundaries.
Then there is comparison to the Emacs ecosystem, where orchestration of apps (or rather modes) is routinely done via Elisp. By implementation circumstance there are effectively no concerns for security boundaries within the internals of Emacs.
There's a lot of ground to ruminate on, in particular software malleability and orchestration, which I intend to do with future posts on this blog.
This is an edited transcript of my chat with Matei Candea, an anthropologist who is curious about the Emacs community and AI. Sharing it here with permission so that it becomes a thing I can refer to and in case it sparks further conversations. AI is a bit of a contentious topic, so I hope people will be patient and kind as we figure things out!
NOTE Matei is an anthropologist; ethnographic researchWhat I actually do for work is to do ethnographic research,to interview people.I've written a lot about scientific communities.For instance, I've written articles onbehavioral scientists who work with animalsand how they think about knowledgeand technology and stuff.Completely independently of that,I kind of got into Emacsand got really excited.About four years later, I was, like,wait a minute, why don't I do an ethnographyof Emacs as a community?Sacha: Really cool people.Matei: Right? Really cool people.
Curious about Emacs as a community in the time of AI
Matei: I think what I'm really saying isEmacs as a community in the time of AI and how that'sshifting or not shifting how people are using it,and what it does. I've spoken to Prot onMonday. That was the first interview I did, and wehad a great chat. I basicallyasked him how he got into Emacs and whatit meant to him and what his relationship is tothe community and stuff,and then a bit about AI and then a bit about whathe feels are the interactions between the two.Matei: That's, broadly speaking, what I would be interested in doing with you.Matei: If you think there's a broader conversation,we could live stream and have an actualchat about how people use Emacs.By the way, I'm very happy also to tell youwhere my own trajectory was that I got into Emacsweirdly and randomly about a year before ChatGPTreally hit the mainstream.The thing that you read by me was written becauseme and Ella together weretrying to figure out Cambridge's response to AIas a university.Like, what are we going to do about it?If I'm going to betalking about that, I need to know how it works.But I don't want to use AI in my own actual workor in my teaching, because I think it's a bit dodgy.I don't really like it.Why don't I just do it with this kind of sideproject I've got, which is learning Emacs, right?And the weird paradoxical thing was thatI now basically kind of live in Emacs.My email is mu4e.If you saw my screen now, the notes are basicallya narrowed Org buffer with questions.Everything's email.But I don't think I could have got there thatfast if it hadn't been for the fact that Istarted asking ChatGPT, like, "Oh, this isn'tworking.Can you just write me a defun that does this?"I'm not completely vibe coding.I'm trying to learn Elisp at the same time, butI'm in this weird position where...Anyway, this is why for me it raised these questions of:what does learning Emacs in the time of AI mean?As you can probably gather from the manifesto,I'm not pushing it at all.I'm really ambivalent about the use of AI.I find myself doing it and kind of sort ofworrying about doing it.Would you be happy to do an interview like I didwith Prot?Sacha: Yeah, we can certainly do that.In addition to whatever I can share from mypersonal experiences, I think your interest inunderstanding and describing the community andthe culture and how it's interacting with this AIthing, I think it'll offer a perspective that isdifferent from what you usually see,because Emacs users have had this long traditionof fiddling with things and making it reallymalleable and fitting it to them and figuringthis out in dialogue.It's figuring out in dialogue with themselves asthey figure out their workflows, with thesoftware as they learn from the code, with otherpeople, with resources on the internet not necessarilyattached to specific people.That's got a really long history.It's really interesting to see how AI bothhas plus sides and minus sides in this whole mix.It definitely, I think, will offer someinsight that you won't hear with the frothy AI hypethat other communities have.It's all very interesting.Matei: Amazing.Let's start with a general kind ofinterview thing.
How did I first get into Emacs
Matei:How did you first get into Emacs?Sacha: I was going through all the booksin my university library aboutcomputer science.One of them was Unix Power Tools.I was like, there's this chapter on Emacs,and it mentions Tetris and other things.What is going on here?I tried it.It was great.I liked it.Then in fourth year or so, my screen stoppedworking.I didn't want to replace it.But there was Emacspeak.I was amazed.Lots of people hadput together Emacspeak so I could use the computerwith a broken screen.I could still read it periodically,if I tilted it and kind of looked at the lowcontrast thing… The speech synthesisworked just fine.I'm going to program this way.Sacha: I'll plug into a monitor when I'm back in my room.But if I'm out and about, I have this other wayto do it.Something that maybe most programs would not haveanticipated, but because somebody had built it for themselves,it was something I could use.Before I got into Emacsspeak, Igot into Planner Mode because I was a universitystudent and I was taking notes.Planner Mode was an easy way for me to keeptrack of tasks.It was more flexible than other to-do managers.Sacha: It's one of the packages that was popular before Org Mode.I started using that to write my blog.Blogs had just been invented around then.I was figuring out, how do I export RSSout of this?I was able to customize it to do that.I liked it so muchI emailed John Wiegley, who had created Planner Mode.I said, hey, I can help you fix bugs.He said, great, you're the new maintainer now.Which was actually very good for me because I wasa university student in the Philippines, andPhilippines, and normally we don't get to work on anythingreally cool.Suddenly I was in this global community of people.There was a mailing list.People would send in questions or feature requests.I would share the things that I was working on.They were very, very patient with me.Like that one time, one of my changesaccidentally deleted somebody's notes and theywere still nice to me afterwards.The community has always been part of how Iexperience Emacs.Learning in public has also always been partof how I've been figuring out what I can do withit and changing it to fit my needs,as very idiosyncratic as they are sometimes...It has also always been part of my experience ofEmacs.Matei: When did this start?Sacha: Very shortly after I started Emacs, I startedblogging with it.My first blog post from that is 2001.Matei: Right.You were studying computer science?Sacha: I was studying computer science, yes.Matei: Are you a computer scientist now?What do you do when you're not doing Emacs?Sacha: Most of the time, I'm still focusedon full-time parenting, which is why I'm goingover to the freezer now to remember to put yogurtin the freezer.I do a tiny, tiny, tiny bit of consulting, butconsulting, but for the last 10 years or so, I've just beenfocused on parenting.Playing with Emacs and being in touch with theEmacs community has been one of the ways thatI've kind of kept sane.I've enjoyed the intellectual puzzles of:I have this thing that I want to do,how do I do it with codein ways that I can fit into five minutes here,ten minutes there of my life.Matei: Do you do any other coding or just Elisp?Sacha: JavaScript, Python on occasion.Some of my consulting involves making littleJavaScript prototypes for ideas that my clienthas, but it's really just an hour a week, maybe less.But for fun, I still write a bit of JavaScript and Python.Emacs Lisp, however, is what I usually writebecause it's so much easier to do things whenyou've got the full editor with you.Matei: Yes, that makes sense.I've got a million questions, but I'm going totry and do them in order.You've said a little bit about this already, but
What do you love about Emacs?
Matei:what do you love about Emacs?Sacha: You can come up with a crazy ideaand you can actually make it happen.So, for example, I've been doing alot of conversations,interviewing people or working with my sister's interviews.I always like turning these into textbecause text is a lot more searchable.Chapters and things like that too, right,so that people can jump to just the part they'reinterested in.I don't know how other tools do it, but I lovethe fact that I can modify Org Mode so that I cancapture timestamps. Wall-clock time is easier forme to work with.I can say, okay, while I'm typing, I just usean abbreviation to put in the timestamp that'sthe current time and my rough notes.I have another piece of code that translatesthat into offsets from the start of the videobased on YouTube's live stream or the file nameof the video.Then I can paste that into the subtitle fileso that it automatically puts the chapters inroughly the right places.As I come up with little workflow ideas, I canactually implement them.
Community
Sacha: I also love the community of it.Looking through the blog posts or as Iput together Emacs News every week, there'salways all these interesting examples from peoplewho are asking the same questions aboutabout "What is it I want to do?" and "How can I doit 0.5% better?"They write these little functions.I'm like, oh, that is a fantastic idea.I get to absorb that into my life.Because I'm seeing it in the context oftheir blog post or their video, you geta glimpse of other people's lives as expressed through code,because all of the code is very personal.That is one of the things that is goodabout the fact that people are using AI sometimesto generate this code.They can make things thatthings that punch above their weight.A newcomer to Emacs can havecustomized functions that let you fullyappreciate its power.But on the other hand, if the AI is justgenerating this code, you don't get a sense oflike, where's the blog post this is coming from?Or who would I talk to to keep up withother crazy ideas they come up with?You're limited to just your ideas.Then there's the whole thing aboutlicense-washing.Most of the people release their code under GPLbecause it's Emacs,but the large language models never mention that.They never say, you also have the right to go andshare this and modify this and build on top of itand contribute back to the community.Anyway, it doesn't feel right, the code.It doesn't quite get the conventions and theidioms yet.So the things that I love about Emacs aregenerally the fact that it can fit me like aglove and it's got this community of people whoare also exploring what is possible as crazy asideas sometimes get.There's always some way to hack it in.
Do you know how big the community is?
Matei:
Do you know how big the community is?
Sacha: I have no idea.We generally feel like it's a lot smallerthan VS Code and probably a lot smaller than Vim.It depends, of course, on if you're talking aboutpercentage, it depends also on...There's a lot of Clojure developers using it,because it's the standard Clojure way of doingthings, but there are probably a lot fewer Javaor JavaScript people using itbecause a lot of people are in VS Code instead.I used to do Google Analytics tracking on mywebsite, but I stripped all of that out becausecookies and tracking and all of that.When people ask me how many people read this stuff,I have no idea, but I do know that every time Ilook for Emacs News, I'm delighted by the breadththat I come across.To me, it feels like there's a thrivingcommunity that's large enough for my interests.Matei: Cool.You're the second person I'veactually spoken to. The first person was Protesilaos.I'm struck by the fact that from a sample oftwo, I've got two people who are not based in theUS, who are super international, and also who arenot developers.Sacha: That is a fantastic thing about it.I love that we have researchers and sourdoughbakers and knitters.Of course, the programming part is still there,but a lot of people end up getting into some kindof programming because of Emacs.Emacs is the only thing they ever code, and theydon't even think of it as coding.It's just like, I do this, but Iwanted to be able to do this, so I learned how todo Org Mode and source blocks, and that's all Ican do, but it's great.I think that's really interesting becausewhen you talk to people about their originstories with Emacs…Sure, of course, you have the pockets of peoplewho are like, I'm a computer sciencestudent and my professor said use this, so I'musing this, and so forth.But then you get these random high school musicstudents who are like, oh, yeah, I just saw thisvideo and I thought it looked really cool, so Itaught myself how to do that.I don't know anybody else who uses it in reallife, but I like it.Musicians using it live to do performance...Where are these people coming from? But they come across it,and it just strikes a chord with them, deep intheir souls. It appeals to a certaintinkerer type, I guess.They just continue with it. They get stuck.Sometimes they leave and they come back, and allthat stuff…But the breadth is one of my favorite things about Emacs.Matei: Do you think that most of thepeople in the community are probably developers?Because when you were saying the community, youcompared it to Vim and VS Code, which is to thinkof it really as an IDE kind of thing.Sacha: That's usually what people talkabout, right?Because usually when people are thinking, howpopular is this, they're stacking it up against developer toolsbecause those are the surveys that the development websites do.Stack Overflow or State of Clojure or whatever.They'll ask people, “What editor do you use?”But given Emacs' surprisingpopularity among people who are, for example,diagnosed with ADHD and find that Org Mode is theonly way they can manage their brains…Matei: Is that a thing?That's really interesting.Sacha: In a number of Reddit threads that I've seen,people are like, yeah, I'm not a programmer, butOrg Mode is the only way that I've figured outhow to manage my brain.Or people will come to Emacs from something elsespecifically for Org Mode because of the way that itcan help them manage their tasks or agenda,because they can sculpt it to fit what theirspecific workflow could be.It's amazing.Of course, we've got the writersand the researchers who are like,"I love publishing beautifully typesetthings, but I don't like working with LaTeX allthat much, so let me just figure out the template once."Matei: Yeah, totally.I really came to Emacs because Iwas looking for an outliner.I'd been writing in Markdownfor a while. I was really getting sick of the heavyWord stuff. And I was, like, Org Mode, omg, it's amazing!Then from there, I was bitten.
Do you have any frustrations with Emacs?
Matei:
Do you have any frustrations with Emacs?
Sacha: I would like to have more time inthe day to fiddle with things.In terms of the balance between fiddling with myconfig and doing the thing that I want to do, ifI sandwich it so that I do my 5 to 15 minutesof Emacs fiddling at the start, then I'mmotivated to go through the task because I wantto test that my improvement works.Then it becomes a good balance for me.I don't spend all the time feeling like I'myak shaving, and I don't spend the time strugglingwith workflow because I didn't take the time toautomate it.Sacha: I would like to have more time,because I always come up withmore ideas in the middle of something."I know this is possible.I just have to sit down and do it, and it'll be great.But okay, I have to wait till my next 5 to 15minute window where I can fiddle with it again."The other thing that I've been trying tofigure out is: how do you help people develop thatintuition for how to do things, how to make Emacsdo things?We see a lot of people come into thecommunity. They might get stuck on some things.The tutorial is very useful, but it can beoverwhelming.The whole Emacs thing can be very overwhelmingfor people.How you help people getthrough that part is something that's of greatinterest to me.Bringing it back to AI and largelanguage models, the fact that people cansometimes have a conversation with this endlesslypatient tutor where they might be too embarrassedto ask their questions on a mailing list or aforum, I think that's fantastic.But also, going to your manifesto's points aboutlearning by doing and education and the eurekamoment, we also don't want this quick and easyhelp to rob people of the understanding that theyget from looking at it and tweaking the code orlearning how to read through the source codethemselves.There's just so much there that I would hate forpeople to just get stuck in the “please generatethis code for me" level rather than be able tolearn this is how I start learning from otherpeople's source code so that I can come up withmore ideas.Matei: That's right.That's also what I think basically.Here's an interesting question.
Would you ever leave Emacs?
Matei:
Would you ever leave Emacs?
Sacha: I cannot imagine an editor at themoment that would let me get away with nearlyhalf of the things that I do, but maybe even less.Right now, I've got so many odd littlecustomizations for it.For example, on my phone, I'll use Orgzly Revivedto capture a quick note so that I can go backinto Emacs later and do it.But even though I'm comfortable programming inJavaScript and Python, and there are lots oftools available there, the interactive interfacepart of things is something thatI don't see any other program give me the samekind of platform of support or building blocks toplay with.Who knows? If some day, this thing manages tosupport all of my hacks built on hacks and givesme that same kind of feedback loop, but it's alsomultithreaded and graphical and whatever,I might give it a try. But at the moment since Ican get away with so much in Emacs and I knowthat people behind the scenes are working onadding even more to it, it's okay,long term. It's been around for 40 years. It'll bearound for... Probably it'll outlive me.I don't have to worry too much about giving up onit.
How important for you is the free software bit of Emacs?
Matei:
How important for you is the free software bit of Emacs?
I was on Mac when I got into Emacs. I went toGNU Emacs to download it and it said,we made this available to people onproprietary systems in order to teach you to freeyourself.I was like, huh?I downloaded it andI'm now running Arch Linux.It definitely worked.Richard Stallman has downloaded himself into mybrain.How much is the free software bit ofit important to you in using Emacs?Sacha: I'm not a purist.I will happily be the interface using thenon-free things.For example, when we were doing EmacsConf, thefirst few years before Whisper was around, I wasthe one doing like, okay, fine, YouTube has thissubtitling thing that we can grab the stuff from.Yeah, it's a non-free service, but I willhappily take advantage of it in order to make theinformation more free, and things like that.I use both free and non-free things, but I lovethe single-minded focus that a lot of people haveon freedom and making sure that other peopleenjoy these rights.For example, in the Emacscommunity, a surprising number refuse touse JavaScript because a lot of JavaScript isnon-free software.I want to make sure that my website stillmakes sense without JavaScript.EmacsConf, there are ways to participate evenparticipate even without JavaScript. You can use MPV to watchthe stream.It's all free software.You can use IRC to chat.All that stuff is very important to people, andthat's great.I love the fact that for a lot of people,they really care about making sure other peoplecan continue to enjoy these freedoms to modifythings and to build on it.Every so often someone comes into the Emacscommunity and they're like, oh yeah, I want tomake money making packages here.I'm going to put my package behind a paywall.You've got to send me a donation in order to use it.Then they get smacked down so hard.Usually the way it works is someone will then,you know, take a look at their README and say,okay, that looks vibe-coded. I can do itfaster and I'll do it for free.That's the usual response to this stuff.Yeah, here's the thing that you're trying tosell, but it's free.Matei: So that never works. I wasstruck by this. It seems to be so absolutelyimmune to takeover by proprietary stuff.Sacha: I mean, it's a startuphustle mentality in other communities, but inEmacs, it does not fly. Mostly because people are, like,are, like, I know the tools you're using, I can do thatbetter myself.There are people who do get sustained bydonations from Emacs community members, but itgenerally is more of a "I appreciateyour work and I will send you this voluntarydonation" instead of your paywalling your stuffbehind this thing, which feels very much againstthe ethos of the Emacs community.It's been interesting to see theAI hustle "software as a service or product type" thingtry to infiltrate the Emacs community, and they arehaving none of it.Matei: Interesting.Why do you think it's so resilient to that?Sacha: Because we've had such a longtradition of sharing things for free, building ontop of things that people have freely shared: notjust like free as in beer, but free as in you've gotthe source code, you've got all the rights to dowhatever you want with it, including for free.That's baked into the community.Any time someone comes in and tries to say,oh yeah, I've got this commercial packaging ofEmacs, it's all rights reserved, peopleare like, yeah, there's probably a GPLviolation right there, so let's go.Matei: Cool.
How do you explain your passion for Emacs to non-Emacs users?
Matei:How do you explain your passion for Emacs to non-Emacs users?Sacha: I don't usually.I love the fact that I can tinker with it, right?If it clicks for people, it clicks.But if it doesn't click for people and they don'tnecessarily want or need that, then it's okay forthem to use something else.I love the fact that people are using or evenshifting to other editors.For example, we've had a couple of peopleannounce that they're leaving Emacs recentlybecause vibe coding has made it possible for themto build native applications and they don't haveto build it on top of Emacs anymore.They can finally get their Vim config set up theway that they wanted to because the LLM cangenerate that stuff for them.Whereas in Emacs, it would have been a lot easierto write it themselves, but now they can do itwith VS Code or whatever.It's great because the more people areexperimenting with interesting ideas, evenoutside Emacs, the more we get to steal thoseideas and then bring them back.You see a lot of this sometimes.You see people re-implementing cool ideas fromother editors or other tools.To me, it's totally okay if other people usesomething else, especially if they tell me thecool stuff that they think only that editor can do.Because I'm like, that soundslike an interesting feature.Do tell me more.There was an interesting talk by Jeremy Friesenin either last EmacsConf or the onebefore that, about mentoring and how he's nolonger trying to push people to useEmacs.He wants to share the general workflowpractices he's using.If he's pair programmingSacha: with someone, he might say, how do youjump to a specific function definition?They might show him something, or they mightrealize that's a thing.I can go look in my editor how to do that.He might show, this is how I do it.That's the general idea.Sometimes when people start talking workflow, thentalking workflow, thentalking workflow, then people who are not using Emacs will go, "Thatlooks really cool.How do I do that?" Thenthat?" Then you send them down the path of:get it installed, go through the tutorial, thatsort of stuff.But it always helps to have that specific reason,the thing that they want to be able to do.For me, for example, I love the way that Org Modelets me have my notes and the code and the links.It's all one big thing.I don't have to think about, oh, okay, I haveto do everything in Python because that's whatJupyter does.I can do some of it in Emacs Lisp, and I can dosome of it in shell scripts, and I can do some ofit in JavaScript or Python.It's like all this big messOrg Babel kind of thing.Yeah, because your brain might not be in tune withall those different languages, but it works for me.If other people see that and they say, Iwant to do that too, then that's when you helpthem get into Emacs.But aside from that, I don't talk to people inelevators and say, have you heard the good news?Matei: I was wondering evenmore broadly than kind of people who are alreadycoding with a different editor.To tell you a story...My cousin is also an anthropologist. He's ananthropologist in France. I've known for yearsthat he was into Linux and free software andstuff. When I got into Emacs, he said, you knowI've been doing Emacs for 10 years.I was like, what?How?What?And he'd never told me.I realize now, having been doing Emacs forfour years, I can't talk to my colleagues andfriends about it because theylook over my shoulder and it'slike, what are you doing?This looks like it's from the 1980s.Even trying to explain to people whatEmacs is... I don't mean coders, I just mean people.My cousin said, yeah, Italk to people about free software all the time.I've never talked to anyone about Emacs.It's just so weird.Sacha: I think that's why thecommunity is so important, right?I aggregate a lot of blogson Planet Emacslife so people can bump into each other.There are a lot of meetups, some of which we hoston BigBlueButton... There are meetups, bythe way.If you check under Emacs News, there's actually avery active London meetup.Matei: I haven't yet.
To what extent do people meet in person with Emacs?
Matei:To what extent do people meet in person with Emacs?Sacha: Apparently, a lot of people meet inperson whenever they're lucky enough to get asense that there are actually other people intheir general geographic location who areinterested in this.But there are also a lot of people who meet online.Org Meetup has a meetup every month that has about20 people in it.Sacha: Emacs Berlin has a meetup that's hybrid, andso it's both in person and online.There's Emacs Asia Pacific.There's a whole list of meetups in Emacs News,which is that newsletter that I do every week.I list upcoming events, and there's also a linkthere to the calendar as well as to the usergroups page which lists by region.There are a lot of people getting together aboutEmacs because a lot of times,you learn about Emacs by looking over someone'sshoulder, physical or virtual, right?This is how you learn about things that you wouldnot have even thought of asking an AI about.They're doing ademonstration or they're doing a video, andyou're like, what is that thing that you just did?They had no plans to talk about it because it'sjust something they take for granted.It's a keyword shortcut or a command.It's just part of the workflow.They don't think about it anymore.Or it's even as simple as"What's that theme?What's that font?"Because people can see it, can see somebodydoing stuff with Emacs, they get inspired tolearn more and to adopt that into their workflow.That is one of the things that I love abouthow people learn Emacs.It's very convivial, right?Matei: Yeah.
Learning in public
Matei: You said the phrase earlier: learning in public.In one sense, that sounds scary. Learningin public, making mistakes inpublic and stuff. You said it as a reallygood thing. Tell me more about learning inpublic.Sacha: My favorite kinds of blog posts isSacha: when I'm proud of myself for figuringout something clever.Like, okay, here's this functionfunction to do this thing. I had to figureit out.It was hard.It took like a day or two to do it.Then someone comes by in the comments and says,oh yeah, that's built in.Matei: Yeah, I've been there.Sacha: "You just change this variable."It happens so often.The reason is because Emacs is so big, right?There are variables and functions that I would not think ofcoming across.Maybe I'm not using the right words to search forthem, or whatever.If you add to that the entire packageecosystem and as well as the things that are notpeople's packaged code, snippetsin people's config and whatnot...Chances are someone has come across the sameproblem that I'm thinking about and has come upwith a more elegant solution for it.If I'm not using the same words, I might notfind it.One of the things that I like about largelanguage models is that even if I use my words,sometimes it will suggest something that doesthat translation, right?It's an approximate search.But even if I don't have that, if I'm writingabout something, then I have that opportunity forsomebody to say, oh yeah, you should check this out.Or several years later, someone might also say,that is exactly what I was trying to do.I'm taking your code.I've built something on top of it to make it even better.For me, writing about what I'm learningSacha: with Emacsis a great way to learn even more from thecommunity.I keep trying to convince people, yes, please,even if you're a beginner, write about whatyou're learning, because it's a great way tocrystallize that knowledge for yourself,become part of the community and part of theconversations, and learn about things that youwould not have thought of asking about.Matei: Well, I'm following your example.I'm trying to write my config in Org Babel at themoment, partly as a way to say, wait aminute, what is this thing?How does it work?It's so useful.But one thing I was wondering, and it's partlyalso just a practical question,
Disclaimers
Matei: I've never tried to contribute or to postMatei: anything on anything, partly because I worry that my stuff is crap.Sacha: If you put a disclaimer,that way they know they're reading it for theidea, but not necessarily the Emacs Lisp style.That's fine with me too.There are a lot of people who are like, you know,it's got too many emojis in it,I'm not going to read that.I'm going to focus my time reading something elsethat's been handcrafted and all that stuff.That's fine too.There's room for all sorts of people and allsorts of approaches to this.Sometimes even just the idea of somethingis already valuable, thatsomebody thought of saying, hey, my workflowwould be better if it could just do this.If there's a screenshot, even better, right?You can see how it works.Screenshot or video or animated GIF.Because then they can go and write the code thatthey would have to do anyway.Because of course, they've got their ownpersonalized setup.You know, the code that you write will not meshperfectly with their particular setup.There's this whole… There's this Lisp curseessay that's sort of related to…Matei: I was going to ask you about that.Sacha: We've all got our ecosystems of ourown code and absorbing something into it issometimes hard.But if you start with even just the idea thatsomebody else has written about, whether or notyou take their actual code for it or use theircode as a building block, that is already usefuland interesting.Again, you don't have to be Bozhidar Batsov orOmar Antolin to be able to contribute at thatlevel.Even at the beginner level, you could just belike, I just need to do this thing andit's driving me crazy to do it manually all the time.Then I'm like, you can do that non-manually?Oh yeah, we should do that.Matei: Cool.Just to come back to the question abouttalking to other people about Emacs, do you evertalk to people who are not programmers?
Do you ever talk to family and friends about Emacs?
Matei:Do you ever talk to family and friends about Emacs?Do you ever have to explain what this thing isthat you're doing or do you just not?Sacha: Well, my kiddo is 10, and she'slike, can you set me up a kid Emacs?Because she sees me like... Yes!Clearly something of great interest to me.I said, maybe.She does a little bit of vibe coding with Claude asshe generates interactive stories.She was trying to trackdown a syntax error at some point.I was like, can I just install Emacs on yourcomputer so I can do... And she said no.My husband uses Vim.Sacha: Although he did get very interested inOrg Mode at some point, so he found theappropriate Vim plug-in for it.That was amusing.I don't talk topeople about editor choices.I just do the stuff that I do. When I write about it,sometimes people will come across it, again,coming from completely different backgrounds.They'll be like, oh yeah, I also need to edit transcripts.What is this Emacs thing?And I'm like, well, it's a very long road, butit's a lot of fun and it's worth it.If you do want to get into it, here's some waysto get started.I don't know.But you can look at the videos first to seewhether it might be something that resonates with you.Matei: Yeah.No, I'm the same.I'm very cautious.I've seen that.The learning curve thing is so cool.My kids are like, yourcomputer used to be so pretty when it was a Macand now it just looks really ugly.I'm like, oh, if you knew.It's so much more beautiful now, but never mind.Cool.We've talked a lot about AI actually already.
Do you ever use AI in chatbots for anything else?
Matei:Do you ever use AI in chatbots for anything else?Sacha: Well, I'm learning French at the moment.In this case, the kind of the regression tothe mean that AI does is very useful for mebecause I need to know, what is the common word choice here?How do I get the grammar to do the thing?I don't really want to spend an hour of arelatively expensive tutor's time picking apartmy subject-verb agreement or my nounsagreeing in plurality with the verbs and stufflike that.It's reasonably acceptable to use large languagemodels for language feedback.That makes sense.In terms of coding,I'm not there yet.Quite a few people are very enthusiastic about it.Even in Emacs, some people are like, "I don't writemy code anymore.I just vibe the whole thing."I love the way that it gets a lot of peopleto make things that they would not otherwise havethe time or effort or experience to do,but on the other hand also, it hallucinatesa lot of things. It gets me excited:oh there's a variable orfunction specifically for this? No! It doesn'texist. I can make it exist, so it's a little lessfrustrating for me, because I can say, you know,that does make sense. I can write that. I can fillin the blanks for it.But 9 times out of 10, I'll be like, no, no, go backand do the proper search.One out of 10 times, it'll tell me, oh yeah,there is this function and it will exist,exist, then I'm like, okay, great,I want to use that, because I wouldn't haveotherwise come across it.But I cannot use it to generate a lot of codebecause I get this urge to just rewrite things tofit the way I want.I just use it like… it suggests ideas.It acts kind of like a search engine that getsthings wrong most of the time.I'll just take theinteresting parts of that and do it myself.Aside from that, I haven't really dug into it tothe extent that other people have.I am happy to take a step back and see how thisall shakes out because with the shake-up inpricing and all the externalized costs that areslowly being factored in, I'm not going to builda house of cards on it.Matei: Yeah, that's very wise, I think.How do you feel aboutthe fact that these models have been trained onall these free conversations?They just suck up allthis stuff that people have been doing for 40 years.Is that a problem in and of itself?Sacha: It's interesting in the particularcase of Emacs.As I mentioned, the vast majority of Emacs Lispis released under either the GPL or the MITlicense or even public domain because people inEmacs really care about sharing stuff and theywant other people to do it.It's not like, oh, we've got this proprietarycode and it's been stolen away from us, it'sus, it's not available for other people.The fact that we're treating AI-generatedcode as non-copyrightable, it's okay that it'ssort of out there.It would be nice to be able to say, hey, thisstuff is GPL, so if you're going to build on it,please share it under the same licenses.But in terms of the way that many people use itfor personal configuration and learning,I'm okay with that.I know that other people in the community havestronger stances, and that's also okay.Because there's no attribution, there's no linkback to the person.The licensing doesn't require [lots of] attribution.You don't have to say, oh yeah, this configwas inspired by these people and at these links.You don't have to do that,but it would be nice to be able to follow thoselinks back to the people.That would be nice.The ability for more people to learn from thisstuff is good.If we can encourage them to share whatthey're figuring out with other people, that'salso good.Matei: So is the problem less about kindof taking intellectual property and more about
Not breaking connections to people
Matei:breaking connections to people or like breakingthese traceable connections to other people inthe community?Sacha: That's the part that I'm interestedin and care about, because I feel thecommunity experience of Emacs is very interesting.All the other stuff, there are peoplewho are far smarter than me and have focused on...This is above my pay grade, right?Actually working out intellectual property,what that means.A lot of people think about copyright andcopyleft and that stuff.I will leave that to them to sort all of theethics after that one.I just care about making sure people can feellike they're learning, feel like they're welcome,and can find the ways forward both withassistance of large language models if they wantto, but also connecting with real people who theycan learn from too.Matei: Yeah, super.I think that's sort of the questions I had,really.I'm sureI'm going to have a million otherquestions.I might email you back about this.Did you have any questions for me?
Education and ethics and eureka
Sacha: I love now knowing that you werewriting your manifesto with that experience ofbeing an Emacs user in mind, because the way thatthe education and ethics and eureka was like,that actually lines up precisely with the Emacscommunity and what it's like and what we careabout.I would love to explore this in futureconversations and see how we can help peoplenavigate this time.There's a lot of froth about AI, and the businessworld is losing their heads over thiscollectively.The programmers in industry either find ituseful but also, in general, seem to havea fairly worse experience.This is not where we should be using this.This is not how this is supposed to be turningout.It should not be leading to more unhappiness, butit is.It would beinteresting to sort out both in the societylevel, but also in the individual level, as peoplemake their own choices aboutwhat to use and how much to use it for, and alsothe impact, even if they're not making thosechoices themselves.I think the general sense now, for at least Emacsand Org Mode, is "we're not going to acceptLLM-generated contributions because we've got tohave a person who can stand behind the code."We so far have been safe from theinundation of generated pull requests that areplaguing other open source projects.It's definitely something to watch out for.But there is some tension.People are proud of their vibe-coded projects,but on the other hand, people are like, well, ittakes 5 minutes or 15 minutes to generate this,and because it's not really maintainable, peoplewill lose interest in it after their 15 minutesof fame on Reddit with their nice screenshots andall that.It's not going to keep moving forward.Matei: Is that kind of like a…version of the Curse of Lisp written large? Everyone'sjust going to write their own programsat home and no one's going to be talking to eachother anymore.Sacha: It is very similar to that. that.It can be a problem.It can be an opportunity.It's not one or the other yet.We're figuring out as a community and asindividuals how to navigate this.We have this long history of people not actuallybeing able to adoptto adopt someone else's code off the shelf.It's amazing when someone actually puts togethera package that can cut across a large variety ofuse cases.It takes a lot of work to get there, but thingslike Magit and Org Mode, how do these thingshappen?Yeah, it's fantastic.I love the fact that we can look at thingsSacha: like consult and vertico...The fact that they can work for a lot of peopleis amazing.It's actually pretty rare in the Emacs community.But for the most part, we are in our littlefiefdoms and we have to make an effort to do thatkind of connection.Whether or not the other person is usingvibe-coded code doesn't matter that much.There's still that barrier. Higher barrier ifyou're dealing with vibe code because they don'tunderstand it and you don't understand it and thecode is hard to read.The ideas can be transmitted over blog posts and videos.But at the same time, the fact that more peoplelike you can use this to start to experience thepower of Emacs, the customizability of it, andcan then go on to imagine, hey, is this whatsoftware could be?Can it be personal?Can it be malleable?Can I say,"No company is going to anticipate this particularneed, but I can make it for myself."?I think that's really worth it.If the tools will help us get there, and ifwe can find our own balance of ethics that areokay with this... Some people might say, no,definitely not for me, even if it gives me the power.Some people were like, I just want to getthis stuff working.That's cool, too.We get to see how that all works out.Matei: It's interesting.I've written this paper for which Igave a talk in Oxford a couple of weeks agoabout this, really for anthropologists. It'santhropologists. It's very interesting that a lot of thethings we were talking about today, Ithought that might be the case on some ofthese things.It's partly thinking about theway in which AI, ChatGPT, whatever, kind ofinterferes, becomes like a broker between thecommunity and the individual.So the good side of it is thatyou're never going tobe told to go and read the manual, right?It's always going to say, "Yeah, sure,that's great." But the bad thing is,you're never going to go and read the manual.That's the problem, right?But what I said at the end of itand I don't know whether this resonatesat all, but I said now that I'm becoming awarethat this is a problem,the paradox that I got intoEmacs for the communityand yet, in a way, I'm being movedaway from the community.Increasingly, now,I will ask not "write this code for me"but "explain to me why this code doesn't work" or"explain to me why my problem could be donedifferently," and even more than that, not even"explain to me this" but "suggest to me how I couldpost this on a forum."I'm a bit worried about posting on a forum incase someone turns around and says, that's stupid.Claude or someone can say,if you write it like that, some people might findit interesting.Does that feel like a different kind of use of AI maybe?Sacha: It does, and I encourage the morereflective use of it.For example, you might say, instead ofgenerating this code, you might say, can you helpme figure out what it is that I actually want tohave in my workflow?Can you ask me questions to help me figure outhow to do this or how to break it down intosmaller tasks?Then that might be a more useful way of doing it.Sometimes people respond better when something isasking them questions.That is possibly an interesting use of AI.Matei: Amazing.Sacha, thank you so much for your time.
Future conversations
Matei: Having had this conversation, do you think there's matter herefor some kind of live stream or something, maybewith other people who want to talk about this stuff?Sacha: In fact, if you wanted to take thisrecording and plop it somewhere public, I amtotally fine with that.Learning out loud is how we have theseconversations grow, right?The conversation is like this brain dump of ideas, and if wewant to start unpacking those ideas and exploringthem through all the multifaceted perspectivesthat we have in the Emacs community, otheranthropologists or people who are interested inthe philosophy of it, there's people whohave so much deep experience in things that Ihave no idea.I would love for them to be able to say,let's take this facet of thisconversation and build on it and explore that one.I am totally okay with both sharing thisconversation, if you want to, as well as havingother conversations that other people might beable to ripple out from.Matei: Fantastic.I mentioned to Protesilaos that we're going tohave this chat, and he said, you know, if you wantto at some point organize a discussion over thiskind of stuff, he'd be very happy to be involved.Sacha: I've been experimenting with making myself askpeople for help.Prot has coaching sessions.If our schedules can line up, then I can schedulea three-way conversation.It can be live and we canbuild on the ideas that you might haveor follow-up questions that you might have,and then we can see if other people do as well.So that could be good.I'm looking forward to hearing about your insights.I would love to see where this goes.I think the Emacs community is definitely worth studying.I think that there are insights here that youmight not otherwise come across in morespecialized, more focused... Like, just developers orwhatever, or more focused on closed source.There's something interesting about this mix ofEmacs and AI and plain text and all that stuff.I would love to see where this goes.Matei: Amazing.Thank you very much.Sacha: All right.Bye.Matei: Bye.
A couple of weeks ago, I wrote about using EWW to avoid paywalls and my experiments to see if it really worked. As I reported, it did in a surprising number of cases. But as I also wrote, I’m not that interested in avoiding paywalls so I didn’t expect to get much out of the experiment other than satisfying my idle curiosity.
It turns out though that I discovered using EWW could be very restful. As I wrote in that last post,
The other thing I learned is that reading content with EWW can be a very restful experience. No ads, no blinkenlights, and sometimes, no paywall either. I didn’t try running EWW with eww-readable but that would probably make the experience even more restful.
My normal procedure is to read my feeds with Elfeed—the world’s best RSS reader—and to display the original Web page in the same buffer with elfeed-webkit. That gives me a browser like view of the page. The problem is, my ad blocker doesn’t work with elfeed-webkit so I get all the trash and abuse one gets with a default page load1.
During my experiment I discovered that calling EWW when a site gets out of control is an excellent solution. The latest version of elfeed makes this easy so if I site becomes too obnoxious, I simply switch to EWW and invoke eww-readable with R to provide a quiet and restful wall of text (complete with the accompanying pictures).
It’s amazing how much junk we just take for granted when all we want to do is read an article or post. EWW takes away a lot of the sting. No, it’s still not going to replace the browser but it’s an excellent companion to it.
Hello and welcome to my May 2026 free software activities report.
A lot's been going on in my life offline so I took a bit of a hiatus
from doing these reports, but I've had a fairly productive month of
May so I thought it'd be nice to do another one for this month.
ffs-0.2.2: I finally polished and published my ffs package for
GNU Emacs on GNU ELPA. Many thanks to Protesilaos for rounds of
code review and feedback for improving and polishing the package
in preparation for submission to GNU ELPA.
bug#81101: Trying to visit https://www.emacswiki.org in EWW
I noticed it fails with a Somebody wants you to give them money
error due to the anti-bot challenge being served with a HTTP 402
(Payment Required) response. So I landed a patch 12eec781ed6 to
no longer do that. Thanks to Emacs comaintainer Sean Whitton
for reviewing and approving my proposed patch.
bug#81107: I noticed that in EWW, unlike <input type="submit">
HTML buttons, <button> elements were not tab-stoppable, leading
to poorer usability and accessibility. So I landed a patch
ec3d662de0b to fix that. Thanks to Emacs comaintainer Eli
Zaretskii for reviewing, providing feedback, and accepting my
proposed change.
Emacs Chat with Sacha Chua: I joined Sacha for a new episode of
her Emacs Chat podcast, where we talked about Emacs and life.
I gave a quick tour of my Emacs configuration, discussing at
length my configurations for EXWM (Emacs X Window Manager) among
other topics like Emacs's facility for visually indicating buffer
boundaries in the fringe by setting indicate-buffer-boundaries
and my convenience configuration macros.
maintainers@: I started the next long-overdue round of emails to GNU
package maintainers to confirm the contact information we have on
file for them and get a brief status update about their packages.
Emails are sent in small batches to keep the workload of handling
the responses manageable for assistant GNUisances.
GNU Spotlight: I prepared and sent the May GNU Spotlight to the FSF
campaigns team for publication on the FSF's community blog and the
monthly Free Software Supporter newsletter.
Debian
I've begun the work toward updating the Jami package in Debian
unstable again, which means I need to package new releases of its
direct and indirect dependencies. For OpenDHT, I need to update
RESTinio, and to do that I first need to package expected-lite and
sobjectizer for Debian:
#1120837: ITP: expected-lite – expected objects for C++11 and later
#1137609: ITP: sobjectizer – C++ implementation of Actor,
Publish-Subscribe, and CSP models
I've been working on packaging both and hope to have them uploaded to
the archive in the next days and weeks.
I just completed Ziglings! It's wicked fun!
Zig is a programming language and Ziglings is a collection of fun exercises to help you learn it. I heard about if from this interview with Andrew Kelley. He is the creator of Zig (but not the creator of Ziglings, that's Dave Gauer).
Zig is still under development (it has yet to release version 1.0), so it's changing a lot and Ziglings is changing right along with it.
I recently installed Harper on my Linux Desktop to work with Emacs, but since I’m running Kubuntu, I ran into difficulties. In short, there’s no Flatpak or Apt option when it comes to Harper.
After a few interesting changes to the way I journal in Emacs (this is something I hope to discuss soon), I decided to go for the full version, and that meant installing Rust and Cargo.
This was another “programming quest” I didn’t know how to start in the past. I used Claude.ai to guide me, but as usual, I asked a million questions about everything, so I can explain it again here (this is my test to myself). So if you’re new to all of this like I am, take the explanations with a grain of salt, and if you’re an experienced developer who understands Rust (and curl, for that matter) feel free to reach out and educate me further.
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
The above is a bit more complex than what’s in Cargo’s documentation, but based on a quick search, it is what’s directly recommended in rustup, which is where you install Rust. The idea is the same as other curl installations, with a few more options for added security and to ensure we’re getting what we really want:
Run curl, but restrict it only to https (no http):
curl --proto '=https'
Somewhat redundant: curl will usually refuse anything lower than 1.2 by default. This forces TLS 1.2 as the minimum. This is good practice and also what they tell us to use, so why not:
--tlsv1.2
options for silent mode s (so don’t show us progress and status), but show us if we get errors S, and if we get a 404 error or similar, just stop silently f (otherwise it will pipe it into the sh command at the end):
-sSf
Then we have the URL to download from:
https://sh.rustup.rs
and finally we pipe it | into a shell sh command so it runs as a script as intended here. If you go to the above URL directly, it will download a shell script - so this is how we get it and run it in one go:
| sh
Because we’re about to run commands for Rust, it’s a good idea to add it to our source environment, the same as editing ~/.bashrc manually and adding . "$HOME/.cargo/env". Without it, we’ll have to specify where Cargo is installed for the next commands
source ~/.bashrc
At first, I installed what was available on crates.io. Crates, as I learned, is the official repository for Cargo, our “app store” for Rust, (or Elpa for Emacs). The individual packages are called “crates”. Makes sense now, but before it all looked like a bunch of command-line voodoo to me.
However, apperently what’s available on Crates is not up to snuff. The official repository for Harper is at https://github.com/Automattic/harper, and it specifies version 2.3.1, whereas the one available in Crates is 2.0.0. We are still using cargo (it’s the “installer” for Rust), but specify to get what we need directly from there:
The git option tells Cargo we’re installing directly with git, which is what we’re doing here; the locked option is specified in Harper’s documentation, and upon some research, I learned this forces the exact dependency versions specified in Cargo.lock. Without it, cargo might choose newer dependency versions that were not tested or are not specified in the documentation.
Finally, in Emacs, we want to tell eglot where to find Harper:
In my case, since I use the same config on my Mac, I want this to run only on Linux. On my Mac, Harper is installed without all these shenanigans directly from Homebrew, which also keeps it up to date. This is added to the same config block I specified in my earlier post. It now looks like this:
(with-eval-after-load 'eglot (add-to-list 'eglot-server-programs'(org-mode . ("harper-ls""--stdio"))))
(setq-default eglot-workspace-configuration
'(:harper-ls (:dialect"American":linters (:LongSentences:json-false:AvoidCurses:json-false))))
;; Besides choosing American as the language, I also want to ignore long sentences (the main issue is that it hides other errors nested in those), and I also want Harper not to tell me when it thinks something is offensive. I'm a big boy/an old fart. The full list of these options is in https://writewithharper.com/docs/rules. It needs to be nested inside the :linters option. (when (eq system-type 'gnu/linux)
(add-to-list 'exec-path (expand-file-name "~/.cargo/bin")))
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!