Coffee time. I woke up wondering about .org-id-locations. You know, that Emacs file that keeps track of all of your org-mode IDs. How does it know where are the headers in the files it refers to? Is there a size limit? Can I use a different file (or files) if I want to for the hell of it?

What?? Don’t look at me like that. I often think about Emacs just before I fall asleep and when I wake up. Don’t you?

A hand is holding a coffee cup with the Emacs logo near a window, c clouldy rainy day outside
-1:--  (Post TAONAW - Emacs and Org Mode)--L0--C0--2025-05-22T15:10:59.000Z

Irreal: Batsov Tries Vim (Again)

When the Eagles disbanded after a particularly contentious Long Beach concert where Glenn Frey and Don Felder almost came to blows, Don Henley quipped that the band would get back together when Hell freezes over. Fourteen years later the band did reunite and released their new album, Hell Freezes Over.

I was reminded of that when I read Bozhidar Batsov’s latest post on Rediscovering Vim. Most people don’t know that, like me, Batsov used Vim early in his career. He apparently didn’t stay with Vim too long because of its really hideous extension language. I was less interested in customizing my editor back then and stayed with Vim for many years.

Batsov isn’t, of course, abandoning Emacs. He just wants to “rediscover” Vim and see what he can learn from it. He started out with Neovim, which, apparently, most developers in the Vi camp are using now. But he dropped back to Vim so that he could develop his own configuration just as he had with Emacs.

Batsov likes the way Vim has the notion of “text object” and a uniform way of dealing with those objects. As I’ve said before, that’s what makes it so easy to learn and use the Vim key sequences. I don’t really miss Vim but if I did, it would be that aspect that I’d miss the most.

As I’ve also said before, I have significant muscle memory problems trying to use more than a single editor so we here at the Irreal bunker won’t be recreating Batsov’s experiment. And, really, muscle memory notwithstanding, I have no desire to. I’m perfectly happy with Emacs and indulge what little desire I have to tinker with editors to playing with my Emacs configuration.

-1:-- Batsov Tries Vim (Again) (Post Irreal)--L0--C0--2025-05-22T15:01:02.000Z

Christian Tietze: Insert Cc and Bcc Mail Headers Conditionally in Emacs message-mode

In my Emacs email setup (notmuch.el using message-mode), I had this for the longest time:

(setq message-default-mail-headers "Cc: \nBcc: \n")

It would insert Cc and Bcc headers into email composition buffers so that I could quickly edit these fields.

However, the message-default-mail-headers insertion is done unconditionally, so when replying to mail with people receiving a carbon copy, I would end up with two such header lines:

To: ada@example.com
Cc: bob@example.com
Cc:
Bcc:

The empty Cc/Bcc lines usually don’t cause trouble, unless there’s already a Cc/Bcc line – then some mail servers reject the email. Spam protection or something. It was not supposed to be this way, and multiple Cc header lines were permitted:

This specification permits multiple occurrences of most fields. —RFC 822, Section 4.1, 1982

See?

But here we are, post-2001, when RFC 2822 deviates with min/max occurrences. I didn’t know! So how do we get there?

Looking at the variable’s origin in code, it says this variable is intended to ease migration from mail-mode. That alerted me to look just above that declaration and find message-default-headers, which accepts a function.

The message-mode manual on “Message Headers” doesn’t offer any more detail on that, but my experiments show that when message-default-headers is called, the mail composition buffer only contains From, To, Subject, and that’s it. No -- to separate header from content, no signature, yet. So I can check whether Cc/Bcc header lines exist:

(defun ct/message-insert-headers () 
  "Inserts CC/BCC headers, but only if they aren't already present."
  (let* ((headers (buffer-substring-no-properties (point-min) (point-max)))
         (additions (list
                     (unless (string-match "^Cc:" headers) "Cc: \n")
                     (unless (string-match "^Bcc:" headers) "Bcc: \n"))))
    (string-join additions)))
    
(setopt message-default-headers #'ct/message-insert-headers)

Well, “insert” is a lie, the function returns the headers, but for the intended use it’s fine with me.

I can perceive a brief flash of changes when composing emails: the buffer appears, then something happens and is inserted immediately. That didn’t happen before – but the call site looks like it would run this (new to me) function before inserting the (previously used) string message-default-mail-headers. So that’s a mystery I can live with.

During all of this, I noticed that message.el is nested in the gnus/ directory. I didn’t realize that this originated in the Gnus newsreader project.


Hire me for freelance macOS/iOS work and consulting.

Buy my apps.

Receive new posts via email.

-1:-- Insert Cc and Bcc Mail Headers Conditionally in Emacs message-mode (Post Christian Tietze)--L0--C0--2025-05-22T06:15:00.000Z

Irreal: Emacs Window Navigation

There’s been a spate of posts lately about navigating among windows in Emacs and The Emacs Cat decided to jump in with a description of their own procedures. I like those procedures because they are essentially the same as mine.

Like The Emacs Cat, I use abo-abo’s ace-window to switch between windows and I don’t understand why anyone would use anything else. The idea is that when it’s invoked, ace-window will label each window with a number and you can then switch to any of the windows by typing requisite number. It’s easy and fast and after several years I haven’t found anything better.

Unlike The Cat, I just bind ace-window to Ctrl+x o. Most of the time it acts exactly like other-window because I typically have two windows open and in that case, ace-winodw simply gives focus to the other window. On those occasions when I have more than two windows open, moving to the correct window is only one additional keystroke. At one time, I also bound F8 to ace-window so I could switch windows even faster but I found I never used it.

I also use

:config (custom-set-faces
     '(aw-leading-char-face
       ((t (:inherit ace-jump-face-foreground :height 3.0)))))

in my use-package definition for ace-window so that the numbers on the windows are bigger.

Take it from The Emacs Cat and me. You really should be using ace-window to handle window navigation. It’s the best there is.

-1:-- Emacs Window Navigation (Post Irreal)--L0--C0--2025-05-21T15:07:02.000Z

Protesilaos Stavrou: Emacs: live package maintenance (denote, doric-themes, etc.) today 2025-05-21 at 11:00 Europe/Athens time

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

At 11:00 AM Europe/Athens, I will start streaming my work on some of the Emacs packages I have authored and maintain. I will engage with the chat, if there are any comments, but my focus will be on the code.

The packages I plan to cover are denote, denote-org, denote-sequence, doric-themes. Depending on how it goes, I will work on more of my packages.

The video will be recorded and can be watched later.

All my Emacs-related work: https://protesilaos.com/emacs.

-1:-- Emacs: live package maintenance (denote, doric-themes, etc.) today 2025-05-21 at 11:00 Europe/Athens time (Post Protesilaos Stavrou)--L0--C0--2025-05-21T00:00:00.000Z

Amin Bandali: Don't "buy" e-books from Oxford University Press

Last month I watched the book talk Music Copyright, Creativity, and Culture by Jennifer Jenkins with James Boyle facilitating the discussion, co-hosted by the Internet Archive and the Authors Alliance:

Looking to get a copy of the book, I found the book’s page on the publisher’s website, Oxford University Press. Seeing it available as an e-book, I opted to go with that as a more eco-friendly option and to save some physical space. I worked my way through the checkout and payment steps, under the impression that I would be purchasing a copy of the book that I could download and do with as I wished. The use of the words “buy” and “purchase” throughout the book page on the publisher’s website certainly did not suggest otherwise.

Screenshot from book page on OUP

In hindsight, there were red flags I failed to notice at the time, such as confusing and seemingly redundant, if not contradictory, information on the book page:

“Downloaded copy on your device does not expire.”

Um, okay? I’d sure hope and expect as much about any file I download.

“Includes 4 years of Bookshelf Online.”

Whatever — as long as I could download, store, and use the book offline I’d be happy.

It’s only upon hovering the small and generic, if not misleading, “E-book purchasing help” link that one would be presented with this vaguely informative eyebrow-raising sentence:

E-book purchase

E-books are granted under the terms of a single-user, non-transferable license, and may be accessed online from any location.

“E-books are granted” (??) is news to me. I thought I would have rightful access to something I bought and paid for, rather than being “granted” (read “allowed”) access to it by and through some overlord. Oh but of course, we live in a time where vendors get to redefine well-established words like “purchase” and “buy” on page N of their terms and conditions.

I obviously did not see that “E-book purchasing help” before giving Oxford University Press my money: being a tech-savvy person, I didn’t think I needed any help “purchasing” an e-book.

Everything became clear shortly after I completed the “purchase” and was redirected to VitalSource to access the book: the VitalSource “Bookshelf” user interface offered no way to download a copy of the book I thought I bought and paid for. It is instead a glorified pile of proprietary JavaScript DRM (Digital Restrictions Management) that wraps around the underlying representation of the book in VitalSource’s possession. The only other option for accessing the book would be through VitalSource’s proprietary application available only for certain versions of certain proprietary operating systems.

At this point, the only method I could think of to try and obtain a copy of the book that I could read without subjecting myself to the shackles of DRM or proprietary software was trying to print the book to PDF. Given that VitalSource’s DRM interface is a proprietary wrapper around VitalSource’s likely ePub-based underlying representation (guessing from the presence of epubcfi in the URL of their book renderer page), the book pages are not exposed all at once, practically forcing one to use the interface’s Print function to get all the pages in one go. After waiting what felt like an eternity for the website to prepare a printable version of the book, I was presented with this abomination (click image for sample in original PDF form):

Sample print output from VitalSource

That is a sample of the output generated by the interface’s Print function: an utterly useless, inferior copy of the book that has giant watermarks on every single page, with the only selectable text in the whole book being the repugnant threat at the top of each page — the actual body text of the book is converted to low-resolution, blurry images, and is therefore neither selectable nor searchable.

Going forward, I will NEVER “purchase” anything from Oxford University Press (and most definitely not from VitalSource), so long as they have no problem “selling” [access to] DRM-infested copies of books with no way to download a usable copy of what I paid for.

The key takeaway for me from this whole experience is that due to the sad and sorry status quo of our current times, this kind of insulting (mal)treatment of users is all but common, and really can happen to any one of us. Therefore it is all the more important for us to band together in protest of this, rather than dividing and isolating ourselves through misguided better-than-thou sentiments toward each other.

For Music Copyright, Creativity, and Culture, I ordered and a few days later received a paper copy from the local bookstore. It’s a copy I truly own, and can read whenever, wherever, and however I please.

Take care, and so long for now.

References and related links:

-1:-- Don't "buy" e-books from Oxford University Press (Post Amin Bandali)--L0--C0--2025-05-20T23:34:49.000Z

Bozhidar Batsov: Rediscovering Vim

Most people who know me and follow my open-source work are likely going to be surprised that I’m writing an article about Vim. After all, I’ve been a devout member of the Church of Emacs for 20 years now, and I’ve spent a lot of time preaching the gospel of Emacs and building extensions for Emacs.

I think a lot fewer people know that early on in my career I was using Vim briefly, before switching to Emacs in 2005. In the beginning of this year I felt it was a good time to revisit Vim and (neovim) and see how they’ve evolved compared to my beloved Emacs. It’s never a bad idea to keep an eye on the competition - they are always a great source of “inspiration”.

The main reason why I abandoned Vim back in the day is quite simple - I really dislike Vim script. It was a horrible language back then and I think it’s still a horrible language today.1 I’ll admit that the rise of neovim (and it’s switch to Lua) was probably the main reason why the thought of revisiting Vim after so many years even crossed my mind. There’s also the fact that it remains the only other editor (besides Emacs), that’s essentially building material for those of us crazy enough to want to build their own editor. Most people don’t care about that, but for me that’s very precious.

Starting with Vim is always intimidating, even if you kind of know the basics. The configuration options are countless and the Vim plugin ecosystem is huge. :help is your friend!

Initially, I focused on neovim for a couple of reasons:

  • Lua for the configuration and the plugins
  • Built-in support for things like LSP and Tree-sitter
  • Many distros and a ton of “modern” plugins

I spent a lot of time with LazyVim (and a bit with Kickstart.nvim) and while I was mostly OK with them, I didn’t like the process of starting with a huge configuration and working from there to make it my own. So, after a bit of soul-searching I’ve decided to start at the very beginning with Vim 9 and a blank .vimrc and work my way up from there, same as I did with Emacs 20 years ago.

For a while I didn’t use any plugins at all, apart from a handful of plugins bundled with Vim. Eventually I had to install a few plugins as Vim is so spartan out-of-the-box, compared to most other editors. I was reminded that you need plugins for basic things like:

  • VCS
  • commenting out stuff
  • surrounding stuff in paired delimiters
  • working with paired delimiters in general (e.g. auto-insert/skip closing quotation marks or brackets)

Frankly, I’m a bit shocked how those things never made their way to Vim proper, but I guess there’s some reason for this. Emacs is often considered to be a super-conservative and slow-moving editor, but it almost feels progressive compared to Vim.2

I was also a bit confused by the native Vim 8 package system3, as it’s little more then some predefined locations and structure for the Vim packages. I had expected something closer to Emacs’s package.el. Anyways vim-plug is pretty good and there are a ton of other package managers out there.

Some other pet peeves I have with Vim are:

  • seems there’s no way to configure which types of split commands like :help will use (on widescreen displays I always prefer vertical splits)
  • lots of commands that shell out essentially take you out of the editor and you have to press some key (e.g. Enter) to proceed back
  • configuration options with weird names like “wildmenu” - good luck figuring out what this is without consulting the help!

So, what am I doing with Vim these days? I’m working my way through the classic book “Practical Vim” and gradually building up my modest .vimrc. I’m trying to use Vim for as many tasks as I can (e.g. writing this blog article), but admittedly every time I need to do something more complex I switch back to Emacs. Knowing myself and my habit of always diving deeper than it usually makes sense I’ll likely play a bit with the dreaded Vim script and Vim9 script before revisiting neovim eventually.

The “real” Vim seems to be in a pretty weird place right now:

  • Vim9 is here, but few people are interested in using it, as Vim9 won’t be supported on neovim for obvious reasons
  • As neovim has much better LSP support, and Tree-sitter support the majority of the developers using Vim have switched to neovim already
  • Vim’s future seems quite uncertain after the passing of its author and primary maintainer

Still, for me it’s always fun to learn something new and to challenge myself to work in a different manner. I’ve never really bought the claims that Vim’s modal editing model is better than the competition, but I can’t deny it has a certain charm to it and it often forces you to think about achieving the desired end result with as little keystrokes as possible. I also like the concept of TextObjects (e.g. something between parentheses or a tag) and having an uniform language for interacting with them. neovim really takes this to the next level by adding Tree-sitter objects to the mix. (e.g. blocks, functions, classes, etc)

I’m already at the point where I feel pretty comfortable with the basics and I probably know about Vim than I ever did. Still, I definitely not as productive as I want to be and I often have to pause and think how to adjust my work habits to match the Vim way. One of the areas that I struggle the most with is that it’s very hard to work on multiple projects with the same Vim instance. I know that’s not how most people use Vim, but it’s something I’m quite used to and it’s taking some time to adjust. I’m also struggling with the weird (and very central) file plugin and figuring out how to tweak various settings for different file types. I know that’s subjective, but right now Emacs’s notion of major and minor modes seems pretty fantastic to me in comparison. Oh, well… perhaps I’ll see the light one day!

What’s the endgame with Vim for me? I don’t see myself switching to Vim (or neovim) as my primary editor, but I can imagine adopting evil-mode in Emacs if I start seeing some tangible benefits from Vim’s way. Even before I began my experiments with Vim, I’d occasionally use evil-mode in Emacs when I primarily had to read something as opposed to write it - pressing fewer modifier keys is always nice. The other potential benefit from my foray in the Vim that I envision is that editors like VS Code and Zed typically have a lot better Vim emulation than Emacs, as there are so much more Vim users out there. Recently my F# exploits forced me to spend a bit of time in VS Code and dealing with its native keybindings was a lot of pain for me.

Down the road I plan to write a couple of follow-up articles on things like:

  • my Vim configuration
  • comparisons between Vim and Emacs today
  • some tips and tricks

Perhaps I’ll even write a guide to Vim for Emacs users, as I really needed one when I was starting out.

If you have any tips for aspiring Vim users - please share those in the comments. I’m also happy to get some advice from seasoned experts and to peruse their .vimrc or init.lua.

That’s all I have for you today. Now I have to figure out how to commit this article with Fugitive and exit Vim. Wish me luck!

  1. I have to admit that I can tolerate vim9 script. 

  2. There was a push in Emacs in recent years to modernize the out-of-the-box experience. 

  3. See :h packages for more details. In a nutshell it’s a built-in alternative of Pathogen. 

-1:-- Rediscovering Vim (Post Bozhidar Batsov)--L0--C0--2025-05-20T15:09:00.000Z

Irreal: Journelly 1.1

After the official release of Journelly 1.0, Álvaro Ramírez has not been sitting on his laurels. He has a long list of feature requests from his beta testers and new users and has already released Journelly 1.1 that implements many of them. You can check his announcement for the full list but they’re mostly quality of life enhancements. For example, there’s now a hashtag picker that comes up when you push the # button. He’s also added support for 10 additional languages. There’s more—take a look at his post for the details.

One interesting aspect of his announcement is that he writes about a post from Ellane telling of her experience with Journelly. She makes two very interesting points. The first is that if you sync your Journelly files to the iCloud, you can control click on the directory to keep it always downloaded. That means you always have access to it even if you are offline.

Her second point is more important. I, and many others commenting on Journelly, have stressed that one of its strengths is its integration with Org mode. Ellane says that even if you don’t know what Org is, Journelly is an extraordinarily useful app and well worth having. I made the same point while commenting on someone else’s post but haven’t stressed it enough. My use of Journelly as a memo book would work just as well even if I weren’t an Org/Emacs user. Of course, the fact that I am an Org user makes it even better.

Let me say again that I really like this app. I’ve been using it all day, every day and its ease of use and flexibility have improved my note taking. The other day I was on a web page that I wanted to save to Journelly. I was about to capture the URL and paste it into Journelly when I noticed that Journelly was in the share menu. I just clicked on it and the entry was automatically stored into Journelly for me. What could be easier? And did I mention that I can read and edit the file in Emacs?

Update [2025-05-20 Tue 19:35]: Ramírez reminds me that when you use the share button to add a web page to Journelly, you can highlight text on the web page and that will be added to the Journelly entry along with the page’s URL.

-1:-- Journelly 1.1 (Post Irreal)--L0--C0--2025-05-20T14:53:32.000Z

Sacha Chua: 2025-05-19 Emacs news

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

View org source for this post

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

-1:-- 2025-05-19 Emacs news (Post Sacha Chua)--L0--C0--2025-05-19T23:00:57.000Z

Corwin Brust: Fork of restclient guarding uses of eval

Another IRC user, technomancy, created a fork of restclient today after noticing some evals.

Here's the new repository: https://git.sr.ht/~technomancy/restclient.el

The delta is small, simply guarding these behind options (which can be setup per "trusted" file/buffer). I'm sure he'd be interested in your further thoughts if you happen to be an IRC user; I'm not sure how much work he plans to put into this other than immediately offering "a simple fix" believing the package might be somewhat undermaintained currently.

-1:-- Fork of restclient guarding uses of eval (Post Corwin Brust)--L0--C0--2025-05-19T19:07:59.000Z

Marcin Borkowski: Customization variables which require some computation after being set

Today I’d like to learn – and then show – how to define a user option in Emacs so that setting it will trigger evaluating some Elisp code. Here is my use-case. I want the option to be a timestamp. The most convenient way to store it is as Unix time (number of seconds since the epoch), since the code using it will perform comparisons between that timestamp and other times. On the other hand, the most convenient way to set it is as ISO-8601 timestamp, which is inifinitely more human-readable than the Unix time.
-1:-- Customization variables which require some computation after being set (Post Marcin Borkowski)--L0--C0--2025-05-19T17:38:05.000Z

Irreal: A Bespoke Emacs Keyboard In Software

In response to my post on the best emacs keyboards, Paul Jorgensen has a very interesting post of how he leverages software to turn the standard Apple keyboards into Emacs specific keyboards and make them better suited to his needs outside of Emacs as well.

Oddly, he doesn’t have RSI problems so his changes are simply for keyboard efficiency. His post is interesting because it shows how far you can get with a standard, builtin keyboard through software.

Jorgensen uses a combination of Emacs configuration and Karabiner-Elements to customize his keyboard layout. It’s nowhere near what I’d want but that’s not the point. You can use his strategy to configure your keyboard to your requirements.

The lesson from all this is that you don’t (necessarily) need a special keyboard to make it amenable to Emacs or whatever else you’re using. Happily, I don’t require much more than mapping caps lock to Ctrl, choosing a key for Hyper, and choosing a key for Super. All that’s easily done from within Emacs and standard macOS.

All of this is significant because although there are now plenty of excellent third party keyboards available, many—or most—of us are using laptops as our main computers and often don’t have the option of using third party keyboards. Being able to use software to reconfigure our standard keyboards to our liking is a huge win.

If this sort of thing interests you, take a look at Jorgensen’s post. As I said, you don’t have to make the same choices that he did. His process will allow you to make just about any configuration possible.

-1:-- A Bespoke Emacs Keyboard In Software (Post Irreal)--L0--C0--2025-05-19T14:32:11.000Z

Alvaro Ramirez: Journelly 1.1 released

19 May 2025 Journelly 1.1 released

hello.png

download-on-app-store.png

Journelly 1.1 available on the App Store

What is Journelly?

Journelly feels like tweeting but for your eyes only.

A fresh take on frictionless note-taking for iOS, powered by Org plain text.

  • Save cooking recipes, movies, music, restaurants, coffee shops…
  • Jot down your thoughts.
  • Save your favorite quotes.
  • Use it as a journal, memo book, or notes.
  • Write your shopping lists.
  • Document your travels.
  • Lots more…

Check out journelly.com for details.

sideways.jpg

What's new?

Journelly v1.1 is the first release since launching. It adds support for 10 new languages and delivers the first round of feature requests and bug fixes.

New features

  • New languages:
    • Danish
    • Dutch
    • Finnish
    • French
    • German
    • Italian
    • Japanese
    • Norwegian
    • Spanish
    • Swedish
  • Easily add hashtags using the new picker (most requested feature).
  • Hashtags are now highlighted in the editor.
  • Automatically capture selected text in Safari.
  • Paste images directly from the clipboard.
  • Tap on email addresses to compose a new message.
  • New context menu options:
    • Set location as Home.
    • Open location in Maps.
    • Copy text.
  • iPad keyboard shortcuts
    • ⌘-N Create a new Entry
    • ⌘-S Save the current Entry
    • ↑/↓ Select entry in list
    • ↵ Edit selected entry
  • Uses the full date format based on your locale.

Fixes

  • Prevents the Esc key from discarding unsaved changes.
  • Resolves incorrect link icon colors in Light Mode.
  • The About screen is now available on fresh installs.
  • Fixes issue where the navigation bar became inaccessible when viewing markup.
  • Locations are now only clickable when valid coordinates are available.

A happy Journelly user

Just as I'm getting ready to announce Journelly's 1.1 release, Ellane (from ellanew.com) shared a wonderful blog post on her experience using version 1.0:

Journelly is the Org App You’ll Love (Even if You Don’t Do Org).

I'm particulary excited to hear from Ellane given her Plain Text; Paper, Less philosophy.

"It’s the perfect mix of simplicity and low-tech plain text wizardry"

"It takes a very particular set of features for a new app to impress me enough to hit the purchase button as fast as I did with Journelly."

"Journelly is the first Org-powered app I’ve seen that lays out the welcome mat for people who don’t even know what Org is, never mind how to use it."

Ellane / ellanew.com

As an org mode enthusiast myself, I'm delighted to hear Journelly is paving a gentle road for org newcomers.

Ellane's post also has a great list of features requests. Lucky for me, I can report at least two of them are covered by today's release:

  • The new hashtag picker.
  • Pasting images from the clipboard.

Be sure to check out Ellane's post, as she covers many details I'm not mentioning here. But lemme share one last tip I learned from her post today…

Ellane's iCloud tip

Today I learned something new from Ellane’s post: you can Control-click the Journelly iCloud Drive folder on your Mac and select "Keep Downloaded" to ensure your notes are always available offline. Super handy, specially for those of us using Emacs on macOS.

Markdown enthusiast? Enquire within

Currently, Journelly stores entries in Org plain text format, but Markdown support is on the way. Interested in Markdown? Please reach out. The more requests I receive, the sooner it will land.

On the topic of Markdown: I also run lmno.lol, a Markdown-powered blogging service. Simple and focused, without the frustrating parts of the modern web. Custom domains are welcome too! My xenodium.com blog runs off lmno.lol.

-1:-- Journelly 1.1 released (Post Alvaro Ramirez)--L0--C0--2025-05-19T13:40:34.000Z

Alvaro Ramirez: LLM text chat is everywhere. Who’s optimizing UX?

18 May 2025 LLM text chat is everywhere. Who’s optimizing UX?

When it comes to programming LLM tools, I've seen modes of interaction in the form of code completion, patch application, improvement suggestions, and text chat amongst others. Text chat is everywhere.

In the context of text chat UX, I haven't really come across huge differentiators across offerings. That's not to say they don't exist. The landscape moves fast and there are far too many products out there for me to check out, thus my question to you is…

Who's optimizing or innovating LLM text chat UX?

While there are plenty of new agents and model capabilities that are interesting in their own right, in the context of chat UX, I'm more interested in finding your favorite UX features. What are they? What do you love about them? Why? Do they feel like they reached or are close to reaching an optimal experience? On the other hand, what do you find that's rough about them? Tiny friction here and there? I'd love to know: Mastodon / Twitter / Reddit / Bluesky.

Chat as a shell, is it keyboard optimized?

Back in 2023, I released the first version of chatgpt-shell. To me, LLM chats felt like the perfect candidates to be implemented as shells. After all, aren't they just REPLs of sorts?

  1. Reads user input
  2. Evaluates the input
  3. Prints the result
  4. Loops back to read more input

While this mode of LLM interaction served me well for some time, I couldn't shake the feeling there were tiny improvements to be made to shed a little friction here and there.

TAB navigation

Not all LLM output is equal. I want to quickly jump to more interesting items like code blocks or links (via keyboard of course). Sure, I can search to navigate around, but don't we have better patterns already? Don't we often just TAB our way around apps and web pages?

With that, I added TAB and Shift-TAB navigation to chatgpt-shell.

Editable vs read-only (why not both?)

Most LLM text chat interfaces I've come across are made up of two components: the input text-box and the history of input requests along with their corresponding LLM outputs. While using the text-box, keyboard shortcuts are somewhat limited to modifier key shortcuts. I'd love to have a richer menu of options available or ways to quickly ask for things, without explicitly having to request a different interaction mode nor a menu of sorts. Circumstances aren't that different in a shell when you have to switch between character and line mode. In a way, the clunkiness intensifies when you'd like to input multi-line text through your shell. You better watch out for that muscle memory and avoid pressing enter prematurely while you intended to add a newline… Too late. Your incomplete request is already on its way.

With all this in mind, it's easy to dismiss shell foundations given their quirks. The thing is, we don't have to throw the baby out with the bathwater. What we need is a veneer of sorts, automatically switching between edit and view mode just when you need it.

Reducing history noise

While I want to have access to my LLM chat history (ie. the context), I'm hardly ever interested in seeing anything but the last LLM response. An always-present history feels like constant noise to me. If I want to see the history, I'd like to actively ask for it. Remember that veneer of sorts? Well, can't it act as viewport too? …showing me only the very last response. Want access to previous entries? Well, can act as a pager too. One interaction per page (request/response), but if I really want to open the history floodgates, just give me access to the "raw" shell… and so I started experimenting with pagers of sorts.

One chat interface to rule them all

With my initial chatgpt-shell implementation, I envisioned multi-model chat support would be possible by isolating shell logic into a separate package ( shell-maker) and let folks build whichever LLM chat they'd like (adding support for their favorite model).

While new shells started popping up here and there, I didn't foresee minor shell UX differences affecting general user experience. Learning the quirks of each new shell felt like unnecessary friction in developing muscle memory. I also became dependent on chatgpt-shell features, which I often missed when using some of the other shells. In the end, I bit the bullet and made chatgpt-shell go multi-model.

Tying it all together

TAB navigation, a smart veneer, a viewport, paging, optional access to chat history, a transient menu, a single interface driving different models, and a bunch of other tweaks currently make up chatgpt-shell's compose experience, available via Emacs's M-x chatgpt-shell-prompt-compose command or my preferred key binding: C-c C-e.

Today, I bring another tweak. Compose buffers get a brand new header presenting all relevant shell details including model and paging information.

compose-header.gif

The new compose header is now available in the latest MELPA package version. Invoke M-x chatgpt-shell-prompt-compose and off you go.

Back to the original question…

What are some of the text chat UX features you love? Bonus points if they are keyboard driven. I'd love to hear: Mastodon / Twitter / Reddit / Bluesky.

-1:-- LLM text chat is everywhere. Who’s optimizing UX? (Post Alvaro Ramirez)--L0--C0--2025-05-18T19:55:32.000Z

Irreal: Emacs 30.2 Pretest 1

Eli Zaretskii has announced that the first pretest for Emacs 30.2 is now available for testing. That’s great news. It’s nice to see that development is proceeding apace with the planned bug releases between the—more or less—yearly major releases.

As I always say, if you don’t mind living on the edge a tiny bit, download and test this new release. And as I also always say, thanks to Eli and all the other developers who give so much to our community. They’re real heroes and deserve the gratitude of us all.

-1:-- Emacs 30.2 Pretest 1 (Post Irreal)--L0--C0--2025-05-18T15:27:23.000Z

Org Mode requests: [RFC] Should we allow search options for all links types? (was: [PATCH] lisp/ox.el: Fix export of id links with search string)

[RFC] Should we allow search options for all links types? (was: [PATCH] lisp/ox.el: Fix export of id links with search string)
-1:-- [RFC] Should we allow search options for all links types? (was: [PATCH] lisp/ox.el: Fix export of id links with search string) (Post Org Mode requests)--L0--C0--2025-05-17T17:15:34.000Z

Irreal: An Org Mode Capture Template For Journelly

One of the killer features of Journelly for many of us is that Journelly’s data is stored as Org Mode markup and that this data is easily synced to the iCloud so that it can be shared among all your devices, including a Mac. That means that you can display and edit the Journelly data just like any other Org file.

It also means that you can make an Org Mode template to add entries to the file right from your Mac. When you do, the entries will show up in the Journelly app just as you’d expect. That’s significant because it means you can take Journelly notes from any of your devices.

Early on, while the app was still in beta, Jack Baty wrote about a template he was using for just this purpose. Ramírez, of course, had his own version. A limitation of both versions was that they didn’t capture the location and weather information. Baty remarked that it shouldn’t be too hard to add that and I remember thinking it was something I should do as soon as I got a chance.

It turns out that it’s a bit harder than I thought. Ramírez has a new post that shows how to do it. Part of the problem is that access to the weather APIs on the Mac require an API key and there’s apparently no easily accessible API for location so Ramírez had to use third party apps for both of these. Even so, it didn’t take much code and now he has a template that captures data and displays it in Journelly just as if it had been entered there in the first place.

If you’d like to try this out, the code is available on GitHub here.

-1:-- An Org Mode Capture Template For Journelly (Post Irreal)--L0--C0--2025-05-17T16:08:24.000Z

Alvaro Ramirez: A richer Journelly org capture template

16 May 2025 A richer Journelly org capture template

In addition to including user content, Journelly entries typically bundle a few extra details like timestamp, location, and weather information, which look a little something like this:

rice-guys.jpg

Behind the scenes, Journelly entries follow a fairly simple org structure:

* [2025-04-23 Wed 13:24] @ Waterside House
:PROPERTIES:
:LATITUDE: 51.518714352892665
:LONGITUDE: -0.17575820941499262
:WEATHER_TEMPERATURE: 11.4°C
:WEATHER_CONDITION: Mostly Cloudy
:WEATHER_SYMBOL: cloud
:END:
Try out Rice Guys #food #london on Wednesdays in Paddington

[[file:Journelly.org.assets/images/C5890C25-5575-4F52-80D9-CE0087E9986C.jpg]]

While out and capturing entries from my iPhone, I rely on Journelly to leverages iOS location and weather APIs to include relevant information. On the other hand, when capturing from my Macbook, I rely on a basic Emacs org capture template (very similar to Jack Baty's):

(setq org-capture-templates
      '(("j"  "Journelly" entry (file  "path/to/Journelly.org")
          "* %U @ Home\n%?"  :prepend t)))

These templates yield straightforward entries like:

* [2025-05-16 Fri 12:42] @ Home
A simple entry from my Macbook.

I've been using this capture template for some time. It does a fine job, though you'd notice location and weather info aren't captured. No biggie, since the location of my laptop isn't typically relevant, but hey today seemed like a perfect day to get nerd snipped by @natharari.

And so, off I went, to look for a utility to capture location from the command line. I found CoreLocationCLI, which leverages the equivalent macOS location APIs. As a bonus, the project seemed active (modified only a few days ago).

Installing CoreLocationCLI via Homebrew was a breeze:

brew install corelocationcli

The first time you run corelocationcli, you'll get an message like:

"CoreLocationCLI" can't be opened because it is from an unidentified developer...

You'll need to follow CoreLocationCLI's instructions:

To approve the process and allow CoreLocationCLI to run, go to System Settings ➡️ Privacy & Security ➡️ General, and look in the bottom right corner for a button to click.

After approving the process, I ran into a snag:

$ CoreLocationCLI
CoreLocationCLI: ❌ The operation couldn’t be completed. (kCLErrorDomain error 0.)

Lucky for me, the README had the solution:

Note for Mac users: make sure Wi-Fi is turned on. Otherwise you will see kCLErrorDomain error 0.

Oddly, my WiFi was turned on, so I went ahead and toggled it. Success:

$ CoreLocationCLI
51.51871 -0.17575

We can start by wrapping this command-line utility to return coordinates along with reverse geolocation (ie. description):

(defun  journelly-get-location ()
   "Get current location.

 Return in the form:

 `((lat . 51.51871)
   (lon . -0.17575)
   (description . \"Sunny House\"))

 Signals an error if the location cannot be retrieved."
  (unless (executable-find  "CoreLocationCLI")
    (error  "Needs CoreLocationCLI (try brew install corelocationcli)"))
  (with-temp-buffer
    (if-let ((exit-code (call-process  "CoreLocationCLI" nil t nil
                                       "--format"  "%latitude\t%longitude\t%thoroughfare"))
             (success (eq exit-code 0))
             (parts (split-string (buffer-string)  "\t")))
        `((lat . ,(string-to-number (nth 0 parts)))
          (lon . ,(string-to-number (nth 1 parts)))
          (description . ,(string-trim (nth 2 parts))))
      (error  "No location available"))))

A quick check shows it's working as expected.

(journelly-get-location)
'((lat . 51.51871)
  (lon . -0.17575)
  (description .  "Waterside House"))

Now that we're able to get the current location, we need a way to fetch weather info. I discarded using WeatherKit on macOS for its dependence on a developer account and obtaining an API key. No worries, I found the great MET Norway API which is freely available without the need for keys.

(defun  journelly-fetch-weather (lat lon)
   "Fetch weather data from MET Norway API for LAT and LON.

 Return the parsed JSON object."
  (let* ((url (format  "https://api.met.no/weatherapi/locationforecast/2.0/compact?lat=%s&lon=%s" lat lon))
         (args (list  "-s" url)))
    (with-temp-buffer
      (apply #'call-process  "curl" nil t nil args)
      (goto-char (point-min))
      (json-parse-buffer  :object-type 'alist))))

We can take it for a spin with:

(journelly-fetch-weather 51.51871 -0.17575)

We get a nice object with a chunky time series (cropped for readability):

((type .  "Feature")
 (geometry (type .  "Point") (coordinates . [-0.1758 51.5187 30]))
 (properties
  (meta (updated_at .  "2025-05-16T11:17:44Z")
        (units (air_pressure_at_sea_level .  "hPa")
               (air_temperature .  "celsius")
               (cloud_area_fraction .  "%")
               (precipitation_amount .  "mm") (relative_humidity .  "%")
               (wind_from_direction .  "degrees") (wind_speed .  "m/s")))
  (timeseries
   . [((time .  "2025-05-16T12:00:00Z")
       (data
        (instant
         (details (air_pressure_at_sea_level . 1025.6)
                  (air_temperature . 18.0) (cloud_area_fraction . 4.7)
                  (relative_humidity . 44.2)
                  (wind_from_direction . 17.6) (wind_speed . 3.6)))
        (next_12_hours (summary (symbol_code .  "fair_day")) (details))
        (next_1_hours (summary (symbol_code .  "clearsky_day"))
                      (details (precipitation_amount . 0.0)))
        (next_6_hours (summary (symbol_code .  "clearsky_day"))
                      (details (precipitation_amount . 0.0)))))

      ...


     ((time .  "2025-05-26T00:00:00Z")
       (data
        (instant
         (details (air_pressure_at_sea_level . 1007.3)
                  (air_temperature . 12.6)
                  (cloud_area_fraction . 28.1)
                  (relative_humidity . 91.3)
                  (wind_from_direction . 258.7) (wind_speed . 3.5)))))])))

Journelly entries need only a tiny subset of the returned object, so let's add a helper to extract and format as preferred.

(defun  journelly-fetch-weather-summary (lat lon)
   "Fetch weather data from MET Norway API for LAT and LON.

 Return in the form:

  '((temperature . \"16.9°C\")
    (symbol . \"cloudy\"))."
  (let* ((data (journelly-fetch-weather lat lon))
         (now (current-time))
         (entry (seq-find
                 (lambda (entry)
                   (let-alist entry
                     (time-less-p now (date-to-time .time))))
                 (let-alist data
                   .properties.timeseries)))
         (unit (let-alist data
                 .properties.meta.units.air_temperature)))
    (unless entry
      (error  "Couldn't fetch weather data"))
    (let-alist entry
      `((temperature . ,(format  "%.1f%s"
                                .data.instant.details.air_temperature
                                (cond
                                 ((string= unit  "celsius")  "°C")
                                 ((string= unit  "fahrenheit")  "°F")
                                 (t (concat  " " unit)))))
        (symbol . ,(alist-get 'symbol_code .data.next_1_hours.summary))))))

We can take it for a spin with:

(journelly-fetch-weather-summary 51.51871 -0.17575)

Nice! Look at that weather, it's a sign I should finish writing and go outside!

'((temperature .  "19.0°C")
  (symbol .  "clearsky_day"))

I really should go outside, but I'm just so close now… Or so I thought! That symbol (ie. "clearsky_day") isn't recognizable by Journelly, which relies on SF Symbols returned by WeatherKit. I need a mapping of sorts between these symbols. Gosh, I do need to go outside. Let's speed things along. This is a perfect task for a robot! Whipped chatgpt-shell out and asked the LLM robots to take on this grunt work, who gave me:

{
   "clearsky_day":  "sun.max",
   "clearsky_night":  "moon.stars",
   "clearsky_polartwilight":  "sun.horizon",
  ...
   "snowshowers_and_thunder_day":  "cloud.sun.bolt.snow",
   "snowshowers_and_thunder_night":  "cloud.moon.bolt.snow",
   "thunderstorm":  "cloud.bolt"
}

We're in elisp land so who wants json? Hey robot, I need an alist:

chatgpt-shell.png

Won't the LLM make mapping errors? Most certainly! But for now, I'm just getting a rough prototype and I need to get moving if I want to go outside!

We plug our mapping into an elisp function

(defun  journelly-resolve-metno-to-sf-symbol (symbol)
   "Resolve Met.no weather SYMBOL strings to a corresponding SF Symbols."
  (let ((symbols '(("clearsky_day" .  "sun.max")
                   ("clearsky_night" .  "moon.stars")
                   ("clearsky_polartwilight" .  "sun.horizon")
                   ...
                   ("snowshowers_and_thunder_day" .  "cloud.sun.bolt.snow")
                   ("snowshowers_and_thunder_night" .  "cloud.moon.bolt.snow")
                   ("thunderstorm" .  "cloud.bolt"))))
    (map-elt symbols symbol)))

Does it work? Kinda seems like it.

(journelly-resolve-metno-to-sf-symbol
 (map-elt (journelly-fetch-weather-summary 51.51871 -0.17575) 'symbol))
 "sun.max"

We got everything we need now, let's put the bits together:

(defun  journelly-generate-metadata ()
  (let* ((location (journelly-get-location))
         (weather (journelly-fetch-weather-summary
                   (map-elt location 'lat)
                   (map-elt location 'lon))))
    (format  "%s
 :PROPERTIES:
 :LATITUDE: %s
 :LONGITUDE: %s
 :WEATHER_TEMPERATURE: %s
 :WEATHER_SYMBOL: %s
 :END:"
            (or (map-elt location 'description)  "-")
            (map-elt location 'lat)
            (map-elt location 'lon)
            (alist-get 'temperature weather)
            (journelly-resolve-metno-to-sf-symbol
             (alist-get 'symbol weather)))))

Lovely, we now get the metadata we need in the expected format.

Waterside House
 :PROPERTIES:
 :LATITUDE: 51.51871
 :LONGITUDE: -0.17575
 :WEATHER_TEMPERATURE: 18.5°C
 :WEATHER_SYMBOL: sun.max
 :END:

Damn, the temperature is dropping. I really do need to go outside. So close now!

All we have to do is plug our journelly-generate-metadata function into our org template and… Bob's your uncle!

(setq org-capture-templates
      '(("j"  "Journelly" entry (file  "path/to/Journelly.org")
          "* %U @ %(journelly-generate-metadata)\n%?"  :prepend t)))

We can now invoke our trusty M-x org-capture and off we go…

capture.gif

journelly.jpeg

While the code currently lives in my Emacs config, it's available on GitHub. If you do take it for a spin, it may crash and burn. I blame the weather. In the UK, when sunny, you rush to go outside! 🌞🏃‍♂️💨

-1:-- A richer Journelly org capture template (Post Alvaro Ramirez)--L0--C0--2025-05-16T14:30:37.000Z

The Emacs Cat: Windows Navigation in Emacs

Last week there were some quite interesting posts about windows management in Emacs — Emacs’ windows navigations and some Emacs zen (a review by Irreal), Emacs window management tweaking, for instance.

In this regard, I’ve decided to share my configuration for the issue.

For switching between windows in Emacs I use the ace-window package by Oleh Krehel aka abo-abo, a renowned author of such gems as Hydra, avy, Swiper, and others. Most of these packages have found their place in my .emacs.

My configuration is quite simple.

(use-package avy :ensure t)
(use-package ace-window :ensure t
  :after avy
)

(global-set-key [(f2)]  #'ace-window)

The main function, ace-window is meant to replace other-window by assigning each window a short, unique label. When there are only two windows present, other-window is called… If there are more, each window will have its first label character highlighted. Once a unique label is typed, ace-window will switch to that window.

By default, ace-window uses numbers for window labels so the window labeling is intuitively ordered.

I’ve bound ace-window to <F2>. Why <F2>? For ergonomic reasons. On most keyboards, the numeric keys (1-2-3-…) are located right under the <F2> key. It would be easy to press <F2> then, for example, 2 using the same finger.

Window labels are in the top left corner of each window (shown in red). The frame is dimmed when activating `ace-window`.

The ace-window package has many other options; you can find more detailed information at the ace-window GitHub repository.

Happy emacsing!

— The Emacs Cat.

-1:-- Windows Navigation in Emacs (Post The Emacs Cat)--L0--C0--2025-05-16T09:11:27.000Z

Irreal: A Garbage Collection Strategy

One of the strengths—or weaknesses, depending on your point of view—of Lisp is garbage collection. That means that memory no longer in use is automatically collected and made available to the system without depending on the programmer to release it the way we do in, say, C. Of course, that comes at a cost. Garbage collection can block other operations while it’s running. That’s especially true in the single threaded Emacs.

One often sees complaints about this in the various forums. There’s all sort of advice about what to do. Some say you should set the garbage collection threshold (the amount of collectible memory) high so that garbage collection isn’t triggered as often. Of course, that means that it takes longer to collect the unused memory.

Another suggestion is to set the threshold low so that unused memory can be collected quickly. That means, though, that garbage collection is run more often. Depending on your workflow, either—or possibly both—of these strategies may work well for you.

There’s a third, arguably better, strategy: only garbage collect when the system is idle. That’s facile advice, of course, because it begs the question of how you know when the system is idle. There’s a useful heuristic. If the system has been idle for x seconds it will probably continue being idle long enough for a garbage collection cycle. The question then boils down to the proper value for x.

Jack Jamison believes x should be small and uses 1.2 seconds in his implementation of this strategy. He says that he’s had excellent results with that value. The older GCMH system uses 15 seconds.

I’ve been using GCMH was several years and can’t remember having any garbage collection issues since I started. GCHH lets you configure the idle time but the 15 second setting has been working well for me so I’m not inclined to mess with it.

In any event, if your Emacs is experiencing garbage collection delays it’s worth trying this strategy. If you want a simple prepackaged solution, GCMH is for you. If you want to keep everything in your init.el and avoid external packages, take a look at Jamison’s post to get started.

-1:-- A Garbage Collection Strategy (Post Irreal)--L0--C0--2025-05-15T15:43:32.000Z

Two Eamcs tweaks I forgot about (most of you will know these):

Use Consult’s recentf to see a list of files that were edited recently:

  (global-set-key (kbd "C-x C-r") 'consult-recent-file)

Consult’s org-agenda (jump to heading) is quicker than what I usually do, which is to list all ACTIVE keywords in emacs (this is how I list my projects), and then go down the tree to find the specific TODO header I want.

Since I often know what I’m looking for within active projects, I can use consult-org-agenda, which narrows headers dynamically as I search. Much faster and clutter-free:

  (global-set-key (kbd "C-c C-h") 'consult-org-agenda)
-1:--  (Post TAONAW - Emacs and Org Mode)--L0--C0--2025-05-15T14:16:02.000Z

Irreal: Unsetting Keybindings

Back before I became an Emacser, a friend who was an Emacser said that I shouldn’t worry about the Emacs keybindings because I could configure them to be whatever I want. I remember thinking that that would be disaster. After all, what would I do if I were using Emacs on someone else’s computer or even setting up a new computer.

Now, of course, I have a very individualistic configuration that makes working on someone else’s computer uncomfortable. Uncomfortable but not impossible. That’s because although I have all sorts of specialized editing commands, I’ve left the “vanilla” editing commands—cursor movements, file visiting and saving, and other standard operations—alone. That means that if I use someone else’s Emacs I can edit successfully, if not optimally, as long as the guest Emacs also didn’t mess with the standard bindings.

That’s why I’m not entirely sympathetic to The MKat’s post on how to unset default key bindings. She’s got a lot of great advice on how to delete keybindings and I agree that it’s useful information to have. My objection is to the notion that you should delete bindings that you don’t like.

Just yesterday, I discussed how modern keyboards aren’t optmal for Emacs so it’s natural to want to fix that somehow. Yesterday’s post suggests some ways of doing that but none of them involve changing default keybindings.

Sure, some folks have special needs that make one binding or another difficult but in general, I think it’s a bad idea to be messing with them. Of course, this is Emacs and everyone can adjust it to their liking. I’m not arguing with that. I’m only saying that if you stay with the defaults it will be easier for you to use an alien Emacs.

-1:-- Unsetting Keybindings (Post Irreal)--L0--C0--2025-05-14T16:33:44.000Z

Irreal: The Best Emacs Keyboards

Over at the Emacs subreddit, surveypoodle asks a perennial question: what are the best keyboards for Emacs? He points out that Emacs pinky seems to have appeared rather late in the editor’s history and speculates that that’s because the keyboards originally used with Emacs were better suited for it than modern keyboards that make many of the bindings awkward. He’s asking what modern keyboards are best for dealing with RSI.

If you’re interested in an answer to the question, see the comments to surveypoodle’s post. There are all sorts of suggestions. Reading through them, I realized that I didn’t care.

I used to yearn for a reincarnation of the Space Cadet keyboard—okay, I still have some vestigial urges—but I realized that these days I do virtually all my work on my laptop. I’m guessing the same is true many of us. Regardless, the use of a laptop pretty much makes the choice of a keyboard for you. Sure, you can use a third party keyboard but unless you’re always working at (the same) desk it’s just too much trouble. As I’ve said before, I do a lot of my work on the couch so an external keyboard isn’t a practical choice for me.

What to do? As much as it has become a cliche, Emacs pinky—or more generally Emacs induced RSI—is a real thing.The best solution I’ve found is some simple keyboard modifications. I’m using a MacBook Pro so keyboard firmware modifications aren’t an option. What I can do, though, is arrange for the modifier keys I use the most to be easy to reach. Two of those, Ctrl and Hyper are, configurable from within Emacs or macOS. The Meta key is bound to Alt by default so there’s nothing to do there. What all this means is that binding Ctrl to caps lock and Hyper to ⌘ Right Cmd, I can use my thumbs for Meta and Hyper and that Ctrl is an easy reach for my pinky.

I know that a lot of you are doing the same or similar things. If you’ve got a better solution, leave a comment.

-1:-- The Best Emacs Keyboards (Post Irreal)--L0--C0--2025-05-13T15:20:05.000Z

James Dyer: The Smallest of Productivity Gains by Instantly Opening Dired Files when isearching

If you’re an Emacs user (which I know you are), especially one who lives in dired-mode, you’re probably familiar with the quick power of isearch for finding files or directories. But if you’re like me, you might have noticed a tiny speed bump in the workflow: after finding a file or directory with isearch, you would typically have to hit <enter> to exit the search, and then <enter> again to open the entry. That’s two steps for something that feels like it should be one and this has been a very minor annoyance for me for a very long time now.

I had a little time to shave some more yak hair, so lets try and address this!

The solution I came up with was to add a bit of advice to isearch-exit, so now, when you’re in dired-mode and you use isearch to locate a file or directory, pressing <enter> will both exit the search and immediately open the file or directory, no need for a second confirmation.

Here’s the magic!

(defadvice isearch-exit (after dired-enter-directory-or-file activate)
  "In dired mode, enter directory or open file after isearch."
  (when (eq major-mode 'dired-mode)
    (let ((file (dired-get-file-for-visit)))
      (when file
        (dired-find-file)))))

The only thing I am a little worried about are the side effects, but I guess we shall see…

And another thing, now my dired navigation is one step smoother; how much time generally is it going to save me? Was it worth the effort?.

Let’s say, conservatively, that I have pressed that extra Enter 100 times each day. Each press takes, say, 150 ms, accounting for human reaction times, even though it isn’t really a reaction, more of a muscle memory response.

Let’s do the calculations! I have no idea why I’m doing this, but maybe I can validate the amount of time I spent on this for future time-saving gains. Maybe it will make me feel better about the countless hours of yak shaving I have done over many years (with Emacs, of course, although I do have a yak in the garden shed that really needs some attention!)

So, the savings are as follows:

  • Day: 15 seconds
  • Week: 105 seconds
  • Month: 7 minutes
  • Year: 1 hour 24 minutes

Well, the yearly total is probably about the amount of time I actually took to implement this, so that means in a year I will be making productivity gains! Sweeeeeeet!

-1:-- The Smallest of Productivity Gains by Instantly Opening Dired Files when isearching (Post James Dyer)--L0--C0--2025-05-13T08:20:00.000Z

Protesilaos Stavrou: Emacs: my new Doric themes

The doric-themes is my new in-development package for Emacs. It is a set of themes that conform with a minimalist aesthetic: they use few colours and appear monochromatic in many contexts. Below are the screen shots.

In terms of my overall theme work for Emacs, the doric-themes are the most minimalist, ef-themes the most maximalist, and the modus-themes remain what I consider the best “default theme” style. All of them are designed to be highly legible.

I plan to add doric-themes to GNU ELPA within the next days. For the time being, they are only available from source code: https://github.com/protesilaos/doric-themes.

Finally, the backronym for “Doric” is “Doric Only Really Intensifies Conservatively”.

Enjoy your new theme!

Samples

doric-light

doric-light theme sample

doric-marble

doric-marble theme sample

doric-earth

doric-earth theme sample

doric-wind

doric-wind theme sample

doric-dark

doric-dark theme sample

doric-obsidian

doric-obsidian theme sample

doric-fire

doric-fire theme sample

doric-water

doric-water theme sample

-1:-- Emacs: my new Doric themes (Post Protesilaos Stavrou)--L0--C0--2025-05-13T00:00:00.000Z

Marie K. Ekeberg: Emacs Quick Tip: Disable key bindings with global-unset-key

Do you also hate some of Emacs' default keybindings? Some of the default key bindings in Emacs don't really feel right for me, so I unset/remove them. Sometimes I override them with new operations. In this short article, I will show you how to yeet (remove) your unwanted keybindings.
-1:-- Emacs Quick Tip: Disable key bindings with global-unset-key (Post Marie K. Ekeberg)--L0--C0--2025-05-13T00:00:00.000Z

Sacha Chua: 2025-05-12 Emacs news

[2025-05-13 Tue]: Fixed link to The Emacs Widget toolkit. Thanks, MPL!

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

View org source for this post

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

-1:-- 2025-05-12 Emacs news (Post Sacha Chua)--L0--C0--2025-05-12T14:07:22.000Z

Marcin Borkowski: Coloring Git output in Magit

Today I have a very short and rather niche tip, but maybe someone will find it useful (as I did). Like many other people, I use Magit on a daily basis. One thing that bothered me a bit was the fact that when I pressed $ to see the output of the Git process, all colors were gone. This was a problem for me, since the pre-commit hook I use calls the TypeScript compiler to tell me about any potential problems with types in my code. By default, the output of tsc is colored with the ANSI color codes. While the coloring can be turned out, the setting to do that is rather crude (it also disables pretty-printing/formatting), and after all, these colors serve a purpose and are genuinely useful. I decided that before I turn the formatting off, it may be a good idea to check if Magit has an option to allow coloring the Git output using ANSI codes.
-1:-- Coloring Git output in Magit (Post Marcin Borkowski)--L0--C0--2025-05-12T06:23:14.000Z

Joar von Arndt: Web browsing in Emacs

Why would you want web browsing in a text editor?

Emacs has a tendency to try and absorb all user-facing interactions I have with a computer. Some people seem able to separate “Emacs stuff” — like programming or using org-mode for note taking — but Emacs’ extendability makes me want to use it for everything. For a long time I have only really needed four different tools to do any kind of work at a computer:

  1. GNU Emacs (with my personal config).
  2. Tailscale to connect to my other machines.
  3. Syncthing so that I have local access to my files1.
  4. A web browser (usually Firefox).

Quickly getting these four things up and running is usually my first priority when setting up a new machine, and they require different amounts of work. Critically Firefox requires a GUI environment in order to run, but it’s really quite unnecessary to get a desktop environment or tiling window manager up and running — and dealing with their conflicting keybinds — when all I’m going to be doing is working in Emacs perhaps 80% of the time, finding information or reading docs in Firefox 15% of the time, and using a stray terminal the remaining 5% (something I can easily replace with one of Emacs’ many terminal and shell solutions). This is why I like using exwm2. Rather than using Emacs’ window manager capabilities to manage all your X windows it allows you to stay continually within Emacs and just treat any stray non-Emacs GUI programs within the same system. The solution then seems to try and subsume web browsing into my preëxisting workflow. There are many solutions to this, but I prefer ones that work with text as the primary medium as that is what Emacs is best oriented to work with.

The Emacs Web Wowser (eww)

eww is probably the most well-known of Emacs’ web browsing solutions, chiefly because it comes included with it. It is also the one I have the most experience with. I first came across eww as a consequence of reading about all of the crazy features included in Emacs by default like Tetris and Gnus. This isn’t very surprising; web browsers are some of the largest ubiquitous user-facing applications, so the fact that Emacs ships with one as a tangential feature is very surprising. However, these visions of browsing the web are often quickly crushed when one uses eww for the first time. It does not load any CSS, so websites do not look like one is accustomed to, and a lot of javascript functionality is non-functional.

I expect this experience — or at least something like it — to be somewhat universal amongst those who have even tried using eww. The modern web is built around these technologies, and going without them might seem almost impossible. But there is a place for eww in a modern workflow. The strength of Emacs is its power for working with text, and most media I interact with in a web browser is text. Emacs then is really the perfect environment for this. Reading and rewriting the content of web pages side-by-side using the same key-chords and user-interface allows for minimal context friction. But there are serious downsides, primarily ugly formatted headers and images. Most of the time eww can figure out the main readable content using the eww-readable function, bound to R by default. It’s meant to strip out long “navigation menus and the like”. In my experience it is either hit or miss. For some web pages like wikipedia it seems to work well (but only for some pages), but for others like github it just strips out the entire page. Thankfully it is bound to a convenient key and its easy to switch between the two views quickly. Images are probably one of the first things that stops people from using eww, with large ones causing the site to jump around as they cover the whole screen and then disappear. I solved this using ultra-scroll, which provides scrolling smoother than you could ever imagine in Emacs. It also allows you to scroll intuitively over images, rendering them as you would expect in a traditional browser like Firefox. With a slow network connection eww can have some problems with blocking, but this can be remedied by setting eww-retrieve-command:

(setq eww-retrieve-command
      '("chromium" "--headless" "--dump-dom"))

eww is written by Lars Ingebrigtsen of gnus fame. It uses the shr (simple HTML Renderer) program written in elisp to render HTML. He originally wrote shr to read elfeed entries in Emacs and eww is merely the addition of browsing (or wowsing) capability built on top of it. shr’s elisp nature is why it can sometimes struggle with complicated site layouts, but in my experience it is usually fast enough, especially with native compilation.

Generally eww can be surprisingly powerful, but most of its power (like Emacs itself) comes from its tight integration with other tooling and extensibility with elisp, rather than any amazing workflow it has built for you in advance. There is very little to learn, but a lot to master.

w3m

w3m can some sense be seen as the “hardcore” version of text-based web browsing in Emacs. w3m relies on the eponymous TUI program to render web pages in a way similar to Lynx, allegedly the world’s oldest continually-maintained web browser. w3m even comes with lynx bindings by default, although you can also change them to the included “info-like” ones. It allows for tabs, bookmarks, and other features one would expect in a modern browser. It also relies on interpreting the raw HTML to format the page, rather than rendering CSS. Like eww it also does not support javascript. Like I alluded to earlier, this is both a blessing and a curse, and reading just straight text can be very nice, both from a therapeutic minimalist way and a way to minimize context-switching. It is also nice and async by default, since it uses an external binary to fetch websites, not that it takes very long to load to begin with since it’s not running any heavy javascript. One nice difference between w3m and eww is also that the arrow keys navigate to the next link in the page by default, and the text in the buffer will resize itself whenever the buffer is resized3, something that is very handy if you’re using a package like golden-ratio that resizes windows depending on focus.

Non-text based

While I prefer text-based browsers that fit more seamlessly into the rest of Emacs, there are other solutions for more traditional browser experiences.

Xwidgets

Since version 25 Emacs has had the ability to be compiled with xwidgets support, and to render complete web pages through webkit. To do this merely add the --with-xwidgets flag to Emacs when compiling, and then run M-x xwidget-webkit-browse-url to surf the web! Sadly this doesn’t work very well at the moment, as you’ll quickly realize if you were to do the above. There is apparently a bug in the xwidget library webkitgtk that causes Emacs to abort. Worse yet its status is wontfix since GNU Emacs seems to be the only program affected by it thanks to its old and strange ways of doing things. Upon crashing Emacs links to a 22 year old bug report that had its last message sent five years ago, so I’m not exactly holding out hope (but maybe a good project to work on for someone?). It seems to work with version 2.40 of webkitgtk so try and use that if you’re having issues. Sadly I have not gotten it to work but I keep seeing people mentioning xwidgets as an option so it might work on some machines.

EAF

Getting a non-text browser in the text-based environment of Emacs requires working around Emacs’ systems. EAF (Emacs Application Framework) does this through creating an exterior python system that then hooks into Emacs to run a number of programs, chief among them a webkit-based browser.

The largest problem I have with the EAF’s browser is not really with the browser itself, although it is a bit unstable, but instead with the EAF itself. Being part of this large group of packages that feel very external to Emacs they are troublesome to install and maintain as part of a workflow. Installing EAF (something you’re probably only doing for the browser) asks you individually if you’d like to install things like a git client, system monitor, file browser, terminal, multiple demos, and numerous other things. Some of these things are impressive and useful, but I’d rather explicitly say what I’d like to install instead of having a Y/N on each program. Imagine having to do that for every ELPA package when first launching Emacs! EAF is of course nowhere near that level, but it’s still a baffling design decision. It also makes it way less portable, since I can’t describe what EAF programs I want to install (although I can for which ones I want to use) in a declarative way. It also has a few issues with javascript that feel like deal-breakers for a graphical browser, I’d rather go all-the-way with a text-based browser like eww or use a fully functional graphical browser like firefox.

Footnotes:

1

I can use tailscale to edit the files remotely using TRAMP, but that is usually pretty slow since my home network is very unreliable. Much easier to just sync “asynchronously” when the network feels like it.

2

exwm has other issues, namely around multi-monitor setups. They make sense and are probably sacrifices I would have made had I created exwm, but I don’t really like working with frames instead of windows. It becomes another layer of friction, even if I’m still working within Emacs.

3

To try and replicate this behaviour in eww you have to run (eww-reload t), by default bound to C-u g, to refresh the buffer and reformat it. You can probably run it in a hook whenever you resize windows, but I don’t care to do that.

-1:-- Web browsing in Emacs (Post Joar von Arndt)--L0--C0--2025-05-11T22:00:00.000Z

Irreal: Window Placement When Using Dired

Courtesy of JTR from The Art Of Not Asking Why, here’s a handy tip for dealing with window placement when bringing up a Dired buffer. There are two problems.

The first doesn’t really concern window placement. Most often JTR wants to bring up a Dired buffer for whatever directory he’s already in. The easy way to do that is to use dired-jump (bound to Ctrl+x Ctrl+j) to open a buffer in the same directory as the current file.

The problem with that is that it opens the Dired buffer in the current window. JTR doesn’t want that. He wants to open Dired in some other window so that he can see his current file and the Dired buffer. The answer to that is simple: just ask Emacs to open Dired in another window. There’s a general protocol for doing that. Simply use Ctrl+x 4 Ctrl+J to call Dired. That will open Dired in the same directory as the current file but in another window.

The Ctrl+x 4 … works for a lot of commands. I use it to open files or buffers in another window several times a day. I used to consider it an esoteric maneuver but now I use it all the time. You can also open your windows in another frame by using 5 instead of 4.

The 4/5 protocol doesn’t work for all commands but it does for a lot of them. It’s a real time saver that I use repeatedly. It’s well worth internalizing.

-1:-- Window Placement When Using Dired (Post Irreal)--L0--C0--2025-05-11T15:35:39.000Z

Emacs APAC: Announcing Emacs Asia-Pacific (APAC) virtual meetup, Saturday, May 24, 2025

This month’s Emacs Asia-Pacific (APAC) virtual meetup is scheduled for Saturday, May 24, 2025 with BigBlueButton and #emacs on Libera Chat IRC. The timing will be 1400 to 1500 IST.

The meetup might get extended by 30 minutes if there is any talk, this page will be updated accordingly.

If you would like to give a demo or talk (maximum 20 minutes) on GNU Emacs or any variant, please contact bhavin192 on Libera Chat with your talk details:

-1:-- Announcing Emacs Asia-Pacific (APAC) virtual meetup, Saturday, May 24, 2025 (Post Emacs APAC)--L0--C0--2025-05-10T00:01:02.000Z

TAONAW - Emacs and Org Mode: Emacs' windows navigations and some Emacs zen

As I was looking more into widows management in Emacs, particularly viewing System Crafters' video about the topic which I mentioned in my previous post, I learned a couple of things (or maybe re-learned? I probably used to know about them at some point in the past).

One of my issues with windows in Emacs right now is that whenever I open Dired, it takes over my current window. I often just want to view my files in the directory my current buffer is in.

The answer is simple: instead of opening Dired (C-x d), just “jump” into Dired in another window, which is dired-jump-other-window or C-x 4 C-j. This improves my workflows twice: first, I usually want to open Dired in the directory the buffer I’m working in is, and second, I want it in another window. With this command, I don’t need to specify the directory; it just takes me where I want1.

Also, as I was writing this post, I realized another problem with Emacs windows placement was actually my problem all along (user error dance, anyone?): C-x b is bound to consult-buffer-other-window on my system, which does what it says, opening the buffer I want in the other window. I thought this was the command to open a buffer, forgetting that there’s such a thing as opening a buffer in the other window. Using the logic I just learned, it would probably make more sense to bind consult-buffer-other-window to C-x 4 b (for me, it runs the default Emacs switch-to-buffer-other-window, which I don’t use2) and then consult-buffer (which is not bound to anything for me) to be C-x b. This will disrupt my muscle memory for a while, but eventually it will lead to a better workflow.

Another nice thing I didn’t know and learned from watching this video is that I can scroll up and down in the other window while still keeping the focus on the current buffer with M-pgup and M-pgdn. This is nice if I want to read help documentation while working on something, reading definitions with Emacs' built-in dictionary, or maybe using Occur or something similar, where just reading for reference is enough.

Then we have a built-in way to move between windows that I was looking for a while: windmove. Turns out it’s been there since Emacs 21, which means that for me, it was there all this time, and I didn’t know about it. Windmove lets you switch between windows using the shift and arrow keys by default, so that S-up would move you one window up, while S-down will move you down. You need to enable it and then enable its default keybindings, or assign it alternative keybindings.

Looking at these key-bindings, I can see why it’s turned off by default. Shift plus an arrow key is used extensively in org-mode, for example, so you can’t use these keys. Using something else, say, C+M+left or C+M+right, is not good either because on Linux, this will “swish” your workplaces (“spaces” in macOS), while the meta key plus an arrow is also taken by something else in Emacs… you get the idea, finding the keys to use is getting a bit problematic. You need to figure out what you can give up, or find a creative shortcut that works for you specifically. However, this seems too useful to pass up, so I think I’m going to dedicate keys to it very soon.

The video also goes over windmove’s sibling, windmove-swap-states-[direction], which switches the windows in the direction you tell it. So if I am working on this post in a window that is above a list of links I’m using as reference in the window below in another buffer, I can use windmove-swap-states-down to switch. Now I will be working in the lower window, and the list of links will be in the window above. This is good if I want my list of files in Dired to the window to the right, but dired-jump-other-window opened it in the window below, for example. Nice, it saves a few C-x 0 and C-x 1 maneuvers.

Then there are the external packages, of course. ace window is probably the most popular one, and this video provides a brief demonstration of it. The idea is that it numbers the windows for you on the screen, and then you select a number to jump to a window. Another package I learned about is winum, which is built on top of this idea, but it displays the window number in the minibuffer of each window all the time, and then it’s a matter of C-x # to jump to that window. It integrates into Emacs better in my opinion, and if you use many windows, it probably makes sense. I usually have no more than four windows open at once, so using the built-in windmove is probably better.

Alright, I’m off to rebind some keys and do some tweaking! Then we will see how much I need to mess with Emacs’s built-in logic for windows placement. It might just make a little more sense now.

Footnotes

1: it’s building on dired-jump (C-x C-j), which opens dired in the directory the buffer I’m in. So, if I’m working on bills.org, which is under ~/Documents/personal/, dired-jump will open dired inside ~/Documents/personal/. The problem for me is that it will take over my current buffer (where I have bills.org open), which I don’t want; this is where dired-jump-other-window comes in: it will open Dired in a separate window.

2: this Emacs keys and windows management rabbit hole makes me re-appriciate Emacs’s default key binding. They’re there for a reason. They might not make sense at start… or maybe for several years, depending on how often and how much you use Emacs and what for, but now I see how C-x 4 is an entire family of key combinations tied to “other window” functions and then other key bindings start to make sense. For some of you veteran Emacs users, this might be obvious, but for me, that’s a slap on the forehead with a “Ohhh! So this is why this keybinding is like this!”

-1:-- Emacs' windows navigations and some Emacs zen (Post TAONAW - Emacs and Org Mode)--L0--C0--2025-05-09T15:52:38.000Z

Please note that planet.emacslife.com aggregates blogs, and blog authors might mention or link to nonfree things. To add a feed to this page, please e-mail the RSS or ATOM feed URL to sacha@sachachua.com . Thank you!