Recently:

Irreal: Speeding Up Magit On macOS

If you’re using Magit on macOS, here’s a tip that will either improve your life considerably or not affect you at all. The tip is from Greg Newman who says that it reduced his commit time from about 4 seconds to less than a second.

The secret? It turns out to be simply specifying a pointer to the git executable like this:

(use-package magit
  :bind ("C-x g" . magit-status)
  :custom
  (magit-git-executable "/opt/homebrew/bin/git"))

Of course, after the excitement dies down, you start thinking, “Wait! What? That can’t be right.” Well, what can’t be right is that it’s taking 3 seconds to find the git executable by searching the PATH variable. What is happening then?

It turns out that Apple provides a copy of git that it puts in /usr/bin/ but if you’re using Homebrew and loaded git with it, the executable lives in another place. In Newman’s case, that place is /opt/homebrew/bin/. In my case, it’s in another place but the same thing happens: there are two versions of git in your file system and unless you’ve performed surgery on your PATH variable, Emacs will probably find the older version provided by Apple.

What’s really going on here, it seems, is that without some further action on your part, Emacs—or more precisely, Magit— may be finding the older, presumably slower, version of git. You might wonder why someone would load git from Homebrew. As I recall, the version of git provided by Apple wasn’t quite working correctly and, in any event, you might want the latest version with all its improvements and bug fixes.

Regardless, if Magit seems slow you should:

  1. Make sure you have the latest version of git loaded from Homebrew or elsewhere
  2. Ensure that Magit finds that git either by adjusting your PATH variable or setting an explicit path as Newman does.
-1:-- Speeding Up Magit On macOS (Post Irreal)--L0--C0--2025-07-06T15:55:56.000Z

Installed Emacs again on Pop OS (I wiped it after my GPU issues), and now I realize I’ve had this weird issue in org-mode, where images are displayed in landscape mode. Some of my images are rotated 90 degrees in the wrong direction. Anyone familiar with this, what causes it, and how to fix it?

-1:--  (Post TAONAW - Emacs and Org Mode)--L0--C0--2025-07-06T11:59:46.000Z

Mario Jason Braganza: Emacs Package Updation Checklist


I’ve never updated my Emacs packages until recently, because Emacs is where all my writing happens, and so I’m justifiably paranoid.
But then some packages stopped working, due to various circumstances1 and an update solved it.

So I’ve decided to update my packages once a quarter, so that I don’t lose days yak shaving when something goes wrong and I handle breakage on my terms and not the machine’s.

As far as package management goes, I want to keep things simple.
In fact, I still haven’t graduated to use-package or straight.el because my package needs are few and conservative2. And so, while there are automatic update options out there, I’ll just stick to updating them manually, every quarter.

Ergo, this is the checklist I’ll use next time onwards …

  1. Stop emacs user service, systemctl --user stop emacs
  2. Backup emacs folder in ~/.config
  3. Start emacs manually (not the service)
  4. M-x package-refresh-contents
  5. M-x package-upgrade-all
  6. Problems? Quit emacs Revert backup folder
  7. In the end, start emacs user sevice, systemctl --user start emacs

There’s an Org mode task, scheduled quarterly, so that I won’t forget.


Feedback on this post?
Mail me at feedback at this domain or continue the discourse here.

P.S. Subscribe to my mailing list!
Forward these posts and letters to your friends and get them to subscribe!
P.P.S. Feed my insatiable reading habit.



  1. While I don’t want updated packages, I do want updated Emacs and that broke stuff 😂 ↩︎

  2. The biggest change I forsee, is if Jetbrains ever turn evil and I have to move off their editors and subsequently need to use Emacs as an IDE ↩︎

-1:-- Emacs Package Updation Checklist (Post Mario Jason Braganza)--L0--C0--2025-07-06T03:06:44.000Z

Irreal: Simply Annotate

James Dyer has announced a new package called Simply Annotate. It is, as the name suggests, a package to make annotations to a file with the feature that the original file is not modified. There are, of course, as Dyer says plenty of packages that do this sort of thing but he wanted a light weight package that specifically met his needs and preferences.

Dyer has a list of those preferences, which you can read at his post or you can take a look at the user manual. Regardless, it appears to have some nice features. Dyer has an animation that shows how the system works. The package is available on Melpa or directly from its GitHub page. There’s plenty of information on Dyer’s post, which appears to be essentially the same as the GitHub README.

I’ve tried very hard in the past to warm up to applications like this. After all, they make perfect sense: you can make personal notes concerning the file content without altering the file itself. I spent a certain amount of time making such notes but then never—ever—actually read them. I reluctantly concluded that such things were not for me. But they may be just right for you so you should definitely take a look.

-1:-- Simply Annotate (Post Irreal)--L0--C0--2025-07-05T15:49:51.000Z

Protesilaos Stavrou: Emacs: doric-themes version 0.2.0

These are my minimalist themes. They use few colours and will appear mostly monochromatic in many contexts. Styles involve the careful use of typography, such as italics and bold italics.

If you want maximalist themes in terms of colour, check my ef-themes package. For something in-between, which I would consider the best “default theme” for a text editor, opt for my modus-themes.

Below are the release notes.


Version 0.2.0 on 2025-07-05

This version introduces several refinements to the individual themes comprising the Doric themes collection. Changes pertain to the intensity of colours in use. While each tweak is small, their cumulative effect contributes to a more consistent design.

Users who are interested in the contrast ratio values and relevant colour distance can refer to the contrasts.org file that is part of the project’s Git repository: https://github.com/protesilaos/doric-themes.

I extended support for several faces or face groups including:

  • ace-window
  • dictionary
  • embark-keybinding
  • man
  • markdown-metadata-key-face
  • package-mark-delete-line
  • package-mark-install-line
  • read-multiple-choice-face
  • rcirc
  • spacious-padding
  • textsec-suspicious
  • which-key-key-face
  • woman

Some other faces that were already covered are revised in the interest of theme-wide consistency. Again, changes are subtle.

The colour of the directory icons in all-the-icons and nerd-icons packages is toned down a little bit to avoid exaggerations.

Several spacing-sensitive faces, like Org tables and code blocks, unconditionally inherit the fixed-pitch face. This means that, in principle, they are rendered in a monospaced font even when the user activates variable-pitch-mode or sets the default face to a proportionately spaced typeface. Users who need to specify the applicable font family can either use the following, mutatis mutandis, or take a look at my fontaine package:

(set-face-attribute 'default nil :family "Aporetic Sans Mono" :height 160)
(set-face-attribute 'variable-pitch nil :family "Aporetic Sans" :height 1.0)
(set-face-attribute 'fixed-pitch nil :family "Aporetic Sans Mono" :height 1.0)

Other theme packages of mine have an option for “mixed fonts” (like modus-themes-mixed-fonts), but I think I will not be providing options for the Doric themes. At least not for the time being while I explore the design space for minimalist themes.

-1:-- Emacs: doric-themes version 0.2.0 (Post Protesilaos Stavrou)--L0--C0--2025-07-05T00:00:00.000Z

James Dyer: New Package! - Simply Annotate: A Lightweight Annotation System

Yes thats right, yet another annotation system!, in this case it is a lightweight annotation system for Emacs that allows you to add persistent notes to any text file without modifying the original content.

https://melpa.org/#/simply-annotate https://github.com/captainflasmr/simply-annotate

Still not convinced it is any different to any other note taking package in Emacs given that previous generic sparse description? Well I shall do my best to explain my own note taking ideas. Of course this is also just a pet project to continue to learn elisp, so there is that too :)

I developed this package to fill my own perceived gap in the existing Emacs note-taking ecosystem. Although annotate.el comes close to what I needed, I wanted a simplified version and something with a few workflow adjustments that better suit my note taking preferences. The result is a lightweight alternative that handles annotations the way I have always wanted them to work.

The main functional aspects that I felt were important to me that generally differ from other existing Emacs annotation packages are:

  • Header-line status display with keybinding reminders
  • Simple intuitive workflow (hopefully)
  • Multiple display styles (highlight, fringe indicators, or both)
  • Annotation editing via a dedicated buffer
  • Lightweight (if possible <1000 lines - [UPDATE], no its not possible, lets try <2000 lines!)
  • Multi-purpose key to show/edit/toggle annotations
  • Dedicated annotation buffer that can pop in and out
  • Quickly step through annotations with M-n and M-p
  • Buffer summary of annotations in grep-mode format
  • Quickly browse through all annotated files using completing-read
  • Smart action command that adapts based on context
  • Threading conversations for each annotation

See https://github.com/captainflasmr/simply-annotate/blob/main/docs/simply-annotate.org for the manual!

Quick Demo Video

Open this file and open the simply-annotate.el file

  • highlight some regions and put in some annotation comments

  • step through annotation comments

  • show summary

  • delete some annotations

Go to the directory level and now browse the 2 files

Whats New

<2025-07-03 Thu> 0.6.0

A little refactoring and tidying up.

<2025-06-29 Sun> 0.5.1

  • Raw sexp editing: Edit annotation data structures directly as Elisp for complete control
  • Advanced editing capabilities: Full access to thread metadata, status, priority, and comments

Updated Key Bindings:

  • M-s e: Edit annotation as raw Elisp sexp

<2025-06-20 Fri> 0.5.0

  • Threading system: Add replies to annotations for conversations
  • Status management: Track progress (open, in-progress, resolved, closed)
  • Priority levels: Set importance (low, normal, high, critical)
  • Multi-author support: Configure team members for collaboration
  • Tag system: Organize with hashtags (#review, #bug, #question)
  • Org-mode export: Convert threads to structured TODO items
  • Enhanced display: Thread info in headers and lists
  • Author management: Flexible prompting modes and author changes

New Key Bindings:

  • M-s r: Add reply to annotation
  • M-s s: Set status
  • M-s p: Set priority
  • M-s t: Add tag
  • M-s a: Change author
  • M-s o: Export to org-mode

<2025-06-20 Fri> 0.0.1

  • Basic annotation functionality
  • Persistent storage
  • Navigation commands
  • Org-mode export
  • Customizable highlighting

Installation

MELPA (Recommended)

(use-package simply-annotate
  :bind ("C-c A" . simply-annotate-mode))

Manual Installation

  1. Download simply-annotate.el
  2. Place it in your Emacs load-path
  3. Add to your configuration:
(require 'simply-annotate)
(global-set-key (kbd "C-c A") simply-annotate-mode)

Quick Start

  1. Open any file
  2. Enable annotation mode: M-x simply-annotate-mode
  3. Select/mark text and press M-s j to create your first annotation
  4. Create some more annotations
  5. Navigate with M-n (next) and M-p (previous)
  6. Add replies: Press M-s r on any annotation to reply
  7. Set status: Press M-s s to track progress (open/resolved/etc.)
  8. Advanced editing: Press M-s e to edit annotation data structure directly

Usage

Enabling Annotation Mode

M-x simply-annotate-mode

Or bind to a convenient key:

(global-set-key (kbd "C-c A") 'simply-annotate-mode)

Creating Annotations

Simple

The M-s j command (simply-annotate-smart-action) is context-aware:

  1. With region selected: Creates new annotation or edits existing one
  2. On annotated text: Toggles annotation buffer visibility
  3. With prefix (C-u M-s j): Forces edit mode on existing annotation
  4. Elsewhere: Creates annotation for current line
  5. Enter your annotation text in the dedicated buffer
  6. Save with C-c C-c

Advanced Editing

Raw Sexp Editing

For complete control over annotation data structures:

  1. Place cursor on any annotation
  2. Press M-s e to open the raw sexp editor
  3. Edit the Elisp data structure directly:
    • Modify thread metadata (status, priority, tags)
    • Edit comment text and timestamps
    • Add/remove/reorder comments
    • Change author information
  4. Save with C-c C-c or cancel with C-c C-k

Example sexp structure:

((id . "thread-123456")
 (created . "2025-06-29T10:30:00")
 (status . "open")
 (priority . "high")
 (tags . ("bug" "critical"))
 (comments . (((author . "John Doe")
               (timestamp . "2025-06-29T10:30:00")
               (text . "Found a critical bug here")
               (type . "comment"))
              ((author . "Jane Smith")
               (timestamp . "2025-06-29T11:15:00")
               (text . "I can reproduce this issue")
               (type . "reply")))))

Threading & Collaboration

Adding Replies

  1. Place cursor on any annotation
  2. Press M-s r to add a reply
  3. Enter your response
  4. The annotation becomes a threaded conversation

Status Management

  • Press M-s s to set status: open, in-progress, resolved, closed
  • Press M-s p to set priority: low, normal, high, critical
  • Press M-s t to add tags like #review, #bug, #question

Author Management

Configure for single-user or team workflows:

;; Single user (default behavior)
(setq simply-annotate-prompt-for-author nil)

;; Team collaboration
(setq simply-annotate-author-list '("John Doe" "Jane Smith" "Bob Wilson"))
(setq simply-annotate-prompt-for-author 'threads-only)  ; Prompt only for replies
(setq simply-annotate-remember-author-per-file t)       ; Remember per file

Available prompting modes:

  • nil: Never prompt (single-user mode)
  • 'first-only: Prompt once per session
  • 'always: Prompt for every annotation
  • 'threads-only: Prompt only for thread replies (great for reviews)

Author Commands

  • M-s a: Change author of existing annotation/comment

Display Styles

Simply Annotate supports three display styles:

  • Highlight: Traditional background highlighting (default)
  • Fringe: Shows indicators in the left fringe
  • Both: Combines highlighting with fringe indicators

Change styles with M-s ] or customize simply-annotate-display-style.

Viewing Annotations

When simply-annotate-mode is active:

  • Annotated text is displayed according to your chosen style
  • The header line shows annotation count, status info, and available commands
  • * Thread info*: Header shows [OPEN/HIGH:3] for status, priority, and comment count
  • Moving to annotated text shows annotation details in the header
  • Press M-s j on annotated text to view/edit in detail

Navigation

Key Binding Action
M-n Jump to next annotation
M-p Jump to previous annotation
M-s j Smart action (context-aware)

Managing Annotations

Creating

  • Select/mark some text
  • Press M-s j to open the annotation buffer
  • Make your changes
  • Save with C-c C-c

Editing

Standard Editing:

  • Place cursor on annotated text
  • Press C-u M-s j to open the annotation buffer
  • Make your changes
  • Save with C-c C-c

Advanced Sexp Editing:

  • Place cursor on annotated text
  • Press M-s e to open the raw sexp editor
  • Edit the complete data structure
  • Save with C-c C-c or cancel with C-c C-k

Deleting

  • Place cursor on annotated text
  • Press M-s - to remove the annotation

Listing All Annotations

  • Press M-s l to open a grep-mode buffer showing all annotations in the current file
  • Enhanced display: Shows thread status, priority, comment counts, and author info
  • Click on line numbers, press Enter or n/p keys to jump directly to annotations
  • Perfect for getting an overview of all your notes and their status

Cross-file Overview

  • Press M-s 0 to browse annotations across all files
  • Select a file from the completion list
  • Statistics: Shows annotation counts and status summaries per file
  • View all annotations for that file in grep-mode format
  • Source file is presented along with grep-mode list of annotations

Org-mode Integration

Export your annotation threads to org-mode files for further processing:

  • Press M-s o to export current buffer annotations to an org file
  • Each thread becomes a TODO item with proper metadata
  • Replies become sub-entries
  • Status, priority, tags, and timestamps are preserved

Tips and Tricks

Workflow Suggestions

Enable the mode globally if you wish for all files!

(use-package simply-annotate
  :hook
  (find-file-hook . simply-annotate-mode)
  :bind
  ("C-c A" . simply-annotate-mode)
  ("C-c 0" . simply-annotate-show-all))

Smart Action Usage Patterns

  • Quick annotation: No selection, M-s j to annotate current line
  • Edit existing: C-u M-s j on annotated text to force edit mode
  • Toggle view: M-s j on annotated text to show/hide annotation buffer
  • Region annotation: Select text, M-s j to create detailed annotation

Advanced Editing Tips

  • Bulk operations: Use M-s e to edit multiple comments at once in sexp mode
  • Data migration: Copy annotation structures between files using sexp editing
  • Precision control: Manually adjust timestamps, IDs, or metadata via sexp editing
  • Complex threading: Create sophisticated reply structures that aren’t possible through the UI

Display Style Tips

  • Use fringe mode for code files to minimize visual distraction
  • Use highlight mode for documents where you want emphasis
  • Use both mode for critical files requiring maximum attention
  • Change styles on-the-fly with M-s ] based on current task

Performance Notes

  • Annotations are loaded on-demand per buffer
  • Large numbers of annotations (100+) may slightly impact performance
  • Fringe mode generally has better performance than highlight mode
  • Threading: Complex threads (10+ replies) may slow annotation buffer rendering
  • Sexp editing: Large annotation structures may take a moment to format and parse
-1:-- New Package! - Simply Annotate: A Lightweight Annotation System (Post James Dyer)--L0--C0--2025-07-04T21:43:00.000Z

Irreal: Emacs Users: How Are Your Hands?

Over at the Emacs subreddit, SecretTraining4082 asks long term vanilla Emacs users how their hands are holding up. You’d be forgiven if you think the answer is “terrible”. After all, it’s received wisdom that Emacs’ default key bindings are particularly hard on your hands and inevitably lead to Emacs pinkie or some other form of RSI. Certainly there are serious and well informed people like Eric Fraga who say that RSI caused them to move to Evil mode.

I’ve always taken these people at their word, of course, but, even more, have generalized their experience to the larger Emacs user population, even though I have never suffered from any RSI problems despite years of Emacs use and even more years of constant keyboard use.

I was, therefore, a bit surprised at the answers to SecretTraining4082’s question. Almost every responder said that they didn’t suffer any significant RSI problems despite years of vanilla Emacs use. Many of them attributed this to turning the Caps Lock key into another Ctrl key. I’ve been doing this for as long as I can remember—long before I was an Emacs user—so perhaps that helps explain my lack of RSI problems.

Some of the commenters recommended the usual things like split/ergonomic keyboards but it’s surprising how many said they have been using vanilla Emacs for 30 or 40 years without any problems and without doing anything special other than making Caps Lock into another Ctrl key.

There are, for sure, people who do suffer RSI problems from the default Emacs keybindings but I was surprised at the number of people who said they didn’t. It appears that the idea that Emacs keybindings inevitably lead to RSI may be another trope used by Emacs haters to justify their animus.

-1:-- Emacs Users: How Are Your Hands? (Post Irreal)--L0--C0--2025-07-04T19:32:44.000Z

Discovered Harper in this blog post but not sure how to get it to work. Installed with homebrew, evaluated the default lisp they have on their site and… ok, now what? I don’t see any Harper-related commands. Has anyone figured it out?

Short version: Harper is a frontend to Server Language Protocol (SLP). Emacs has a built-in SLP called Eglot. This means you use Eglot on Emacs to run Harper, so the interactive (M-x) command is Eglot (and it helps if you spell it correctly 🤦‍♂️). The E-Lisp configuration they have on their website (above) works fine, provided Eglot is configured to run and you have Harper installed.

I will expand later after I experiment some more.

-1:--  (Post TAONAW - Emacs and Org Mode)--L0--C0--2025-07-04T17:35:38.000Z

Irreal: Emacs Second Try

After yesterdays post about Charles Choi’s second take on Eshell, I discovered that Choi’s post was part of a collection by Christian Tietze on a second take on various Emacs topics. Tietze himself had a post on Emacs on Second Try.

His story is not at all like mine. He first use of Emacs was under duress as part of a university course. He used it because it was required but left it behind as soon as he could. He was pretty happy using it with the text based terminals at school but once he got his first Mac he abandoned it completely.

Over the years, he learned about things like the package system and compiling from within Emacs. He was also attracted to the configurability of Emacs. He liked some of the Xah Lee’s bindings and was able to incorporate them into his Emacs configuration.

Eventually, of course, Tietze became a convert. His story and mine are alike in one respect: we both initially resisted Emacs and spent some time overcoming that resistance. And, of course, we both became committed Emacs users.

It is, really, a common story. Many of today’s zealous Emacs users took some time to adopt it. For some, like me, it took several tries before it took.

-1:-- Emacs Second Try (Post Irreal)--L0--C0--2025-07-03T15:26:04.000Z

Greg Newman: TIL Speed up Magit on MacOS

I've been using Magit in Emacs for many years and on larger (and some smaller) projects there would usually be a 3 to 5 second delay with magit-status and commits. Today I've been refactoring my config and fixing annoyances and learned that setting the git executable explicitly fixed that issue for me - from about 4 seconds down to less than a second.

(use-package magit
  :bind ("C-x g" . magit-status)
  :custom
  (magit-git-executable "/opt/homebrew/bin/git"))
-1:-- TIL Speed up Magit on MacOS (Post Greg Newman)--L0--C0--2025-07-03T00:00:00.000Z

Charles Choi: Capturing an Org note via macOS Shortcuts

With a new update of Scrim v1.1.0 on the App Store, it seems an opportune time to show a nice macOS Shortcut integration with it.

The use case is this: You're on your Mac, working on something else other than Emacs (it happens!) and you want to make a quick note in an Org file without having to context switch to Emacs.

You can accomplish this with a macOS Shortcut. Shortcuts is an Apple tool that lets you orchestrate different apps to achieve a custom workflow.

Shown below is the shortcut named “Make Note Entry” to accomplish the above use case. It will:

  1. Prompt the user to enter text.
  2. Run a Python script on that text and construct an Org protocol capture request with it using the template named "note".
  3. Issue (open) the Org protocol request.

If the shortcut is configured so that “Pin in Menu Bar” is turned on, you will see some variant of its entry in the Shortcuts bar icon menu.

Running the shortcut “Make Note Entry” will prompt you with a dialog where you can enter your note:

Pressing the “Done” button will result in the entry being inserted at the top of the file “~/org/notes.org” as shown below. Note however that Emacs is not raised with this shortcut. The screenshot below is only shown to illustrate that the entry is there.

Installation

Want to try this out? Here’s what to do:

1. Understand Org protocol

If you don’t already then read up about it at org-protocol.el – Trigger actions in Emacs via a custom URL scheme.

2. Have Scrim installed and configured

Learn more about Scrim at http://yummymelon.com/scrim.

3. Add the capture template “note”

On the Emacs side, add the following template entry to org-capture-templates. This template named “note” defines an entry with a heading that has an inactive Org timestamp (“%U”) and the body containing the entered text (“%i”). The destination of this note entry is in the file “~/org/notes.org”

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
("note"
 "Note (Org Protocol)"
 entry
 (file "~/org/notes.org")
 (function (lambda ()
             (string-join
              '("* %U"
                "%i")
              "\n")))
 :prepend t
 :immediate-finish t
 :empty-lines-after 1)

Observe that this template uses the keys :immediate-finish to not make the entry interactive and :prepend to insert the entry at the top of the file.

4. Add the Shortcut “Make Note Entry”

Download the shortcut file Make Note Entry.shortcut and double-click on it to install. It should now be in your Shortcuts collection.

5. Run “Make Note Entry”

On the first run of this shortcut, you will be prompted twice to give it permissions: 1) to run the shortcut and 2) to open Scrim. Inspect that its contents correspond with its screenshot above and grant them.

Closing Thoughts

Readers are invited to take these ideas and customize them for their own purposes.

-1:-- Capturing an Org note via macOS Shortcuts (Post Charles Choi)--L0--C0--2025-07-02T21:30:00.000Z

Irreal: Charles Choi’s Second Take On Eshell

If you’re a regular Irreal reader, you’ll be familiar with Charles Choi. He often has useful takes on Emacs matters and, of course, he’s the author of the excellent Casual Suite that helps you use all the arcane features of many familiar Emacs commands.

Early on in his Emacs career, Choi tried Eshell but it didn’t take. That’s because, he says, he was expecting it to act like a normal shell, which it most certainly doesn’t. Now Choi has a post that offers a second take on Eshell. It’s a take informed by Choi’s many years of using Emacs and learning to appreciate Eshell.

Choi says that you should begin by thinking of Eshell as offering an Emacs REPL that can easily reference the command line utilities traditionally accessed through a conventional shell. He gives several examples of how that works.

He also mentions something that I’ve noticed too. He now does most of the things he used to do in a conventional shell through Emacs functionality like Dired and Magit. He has a table in his post that compares various shell tasks with their corresponding Eshell equivalents.

I try to use Eshell whenever I need a shell but I haven’t internalized the Eshell way as Choi has. Reading his post has inspired me to do better and try to use Eshell more often. One way to do that is to use Eshell instead of using a key shortcut or Meta+x to invoke Emacs functions such as Dired.

If you’d like to improve your Emacs game, take a look at Choi’s post. There’s a lot of meat in it.

-1:-- Charles Choi’s Second Take On Eshell (Post Irreal)--L0--C0--2025-07-02T15:17:20.000Z

Christian Tietze: Emacs on Second Try

In 2018, Sascha Fast of zettelkasten.de was on a minimalist task management journey and dug into Emacs org-mode, and pressured me for days (we were sharing an apartment back then) to give it a shot, because it surely would be my jam.

While I knew Emacs existed, I didn’t feel compelled to even try it.

I did use Emacs as part of a compulsory University course on UNIX and terminal program basics. We went through the Emacs tutorial on our own terms there. I picked up a couple of shortcuts that turned out to be available system-wide on Macs, too, like Ctrl-D to delete forward, for example, and I used that a lot on laptop keyboards that only come with a dedicated backspace key. But using Emacs for anything outside that course didn’t feel too appealing. I did use it for assignments, because I liked it somewhat better than the other options for text editing on our computer terminals at Uni.

Still, I was very happy with TextMate and other “Mac-assed Mac apps”, so I didn’t bother using the software we had to use from our University terminals for more than absolutely necessary, even though it was fun to hack around in such a bare-bones environment. – Oh, if only I knew how not-bare-bones Emacs in particular was! Nobody told me about the package system at University! For example, I used Emacs to edit source files, then Ctrl-Z to move it to the background, compile from the shell, then move Emacs to the front again. I could’ve done all of this and more from within Emacs, and I didn’t know. So that was my first Emacs experience as a terminal-based text editor that had some weird but learn-able text editing shortcuts. (If only I knew …)

When I got my first Mac, I already had a laundry list of must-have apps. That was in 2008. And during that time, OmniFocus came out (work-in-progress page from 2007), and I just needed to have that app. I used it as my project and task management tool for years, with short stints into trying out Things actu. So in 2018, I had all my stuff in OmniFocus: private tasks, programming projects everything. I didn’t want to switch. It took Sascha a lot of convincing until I caved because of that. But I gave it a try eventually the more features and tweaks and packages he brought up. A digital nerd cave was promised, and boy did it deliver.

I found Emacs Lisp weird, but the malleability of the system sounded just too appealing. I was amazed that plain text UI’s could be ab-used to render so many details. Eventually, I managed to make Emacs look okay (I’m a software developer, so my standards for ‘okay’ are quite low to be productive). It eventually didn’t feel like a downgrade. OmniFocus handled copy-pasting of images and other files as task attachments nicely, and org-mode …. well, I gave up on using attachments and inline images/screenshots as much early on to focus on other things, and 7 years later still haven’t bothered to look into this for most of my .org files.

What I did adopt early on was a modal key binding by Xah Lee. All web searches for Emacs Lisp tips went to his site (or Sacha Chua’s), and Xah clearly is into ergonomics, so I gave it a shot and loved it.

Especially the B key in command mode, bound to xah-toggle-letter-case. It cycles the region or the word next to the point between capitalizing the first letter, lowercase, and all caps. I used this a lot when proof-reading and editing book manuscripts and blog posts in our collaboration at zettelkasten.de. Really, it’s not funny how often I pressed this key.

The Xah Fly Keys command mode (aka default/non-insert mode) also comes with very nice text movement keys without having to press any modifiers. It’s easier to visualize when you check out the key binding graphic:

You end up with arrow keys on I, J, K, L, on QWERTY. And then above the left arrow (J) and the right arrow (L), you have U and O bound to move by word boundary backward and forward, respectively. And to the outside, left and right of J and L, you have H and : to move to beginning and end of line, respectively.

It’s like an arrow key cluster, but enlarged.

When proof-reading, this and the case toggle key binding did a lot of the heavy lifting. It’s much nicer to have all the movement keys next to the right-hand home row than having to hold the Opt key and use arrow keys to move by word boundary on Mac. Much, much nicer.

This sold me to the idea that if I’m spending such an ungodly amount of time in a text editor anyway, I should be able to do these kinds of customizations to make it fit my work.

Eventually, I uninstalled TextMate (after more than a decade of use) and OmniFocus.

When I did, I also went down other rabbit holes to move email, version control, shell interaction, … into Emacs. Because I could, so why wouldn’t I …?


Hire me for freelance macOS/iOS work and consulting.

Buy my apps.

Receive new posts via email.

-1:-- Emacs on Second Try (Post Christian Tietze)--L0--C0--2025-07-02T07:04:59.000Z

Christian Tietze: Emacs Carnival 2025-06: Take Two

To kickstart the Emacs Carnival, and as a hat tip to our recent inspiration, this month’s topic is borrowed from the June IndieWeb Blog Carnival: the topic is “Take Two”, hosted by Nick Simon:

Ever wish for a do-over? “Take two!” (or three, four, etc.) might be shouted by a film director or audio engineer looking to get a somewhat different outcome from a group of actors or musical performers. Would you like a second shot at something that didn’t land?

This is a perfect fit for Emacs users:

We usually don’t nail “it” on our first try – init.el bancruptcy; refactoring hacky Emacs Lisp code; leaving Emacs only to come back englightened much later; running two Emacsens in parallel. There are plenty of possible second takes when it comes to Emacs!

So for this month, our first community blog carnival, I want you to:

  • meditate on “Emacs” and “take two”,
  • blog about it,
  • then send me a link to your blog post.

That’s it! I’ll aggregate submissions for all of June in this post.

Don’t have a blog, yet?

Well, just start one already! :)

It’s the future of the internet!

Blogging is the future, and the future is now!

What’s a Blog Carnival, Again?

This is our first Emacs Carnival, so you may not know the format. A blog carnival is a fun way to tie together a community with shared writing prompts, and marvel at all the creative interpretations of the topic of the month. I’ve provided a couple of interpretations above, but you may think of something else entirely. That’s amazing, roll with it, that’s what makes this fun!

Thanks to Sacha Chua for her post “Working on the plumbing of a small web community” that was part of the May 2025 IndieWeb Carnival, and got me thinking why there’s no Emacs Carnival, yet, and for her encouragement to kickstart this.

For future Carnivals, check out the “Carnival” page on EmacsWiki . It includes instructions, and is our community space to coordinate participants and topic ideas.

Submissions

Comment below or DM/email me with your submission! I’ll collect submissions up to, and including, July 1st (Central European Time), so that every time zone has had a chance to meet the June 30th deadline :)

Bonus: Submit your indie blog post to Nick as well (he really said so)!

These have been submitted as of 2025-06-30:

  1. Kemal: Announcing Brainiac Project
  2. Jeremy Friesen: “Take Two”
  3. Carlos Pajuelo Rojo: “Take two on Emacs (Toma dos de Emacs)”
  4. Mike Hostetler: Emacs: Take 2
  5. Sacha Chua: Thinking about time travel with the Emacs text editor, Org Mode, and backups
  6. Shom: Undo finally clicked with vundo
  7. Eugene Andrienko: My Emacs configuration (common parts)
  8. Ross A. Baker: Take Two: Coming Home to Emacs
  9. Greg Newman: Emacs Take Two (Eglot)
  10. Charles Choi: Take Two: Eshell
  11. Dibyashanu Pati: Take Two - A new Start
  12. Rodion Goritskov: Take two to blogging with Emacs
  13. My own post, Emacs on Second Try

Thanks everyone for participating! July is being hosted by Greg Newman: Writing Experience. You can also host of a future carnival yourself, don’t be shy :)


Hire me for freelance macOS/iOS work and consulting.

Buy my apps.

Receive new posts via email.

-1:-- Emacs Carnival 2025-06: Take Two (Post Christian Tietze)--L0--C0--2025-07-02T06:59:25.000Z

Jakub Nowak: Mixing Code Styles with org-babel

I've really started making good use of org-babel recently as part of my job, and org-babel-tangle has been particularly invaluable. I never honestly expected to get any use out of org-babel outside of taking lecture notes or working through textbooks. Don't get me wrong, it's a very useful tool - and I want to explain how I'm using it now that I've found a really solid use-case for it.

If you're unfamiliar, org-babel is a literate programming extension for org-mode. On a surface level, it works similar to a Python notebook. It allows you to specify what major mode to treat a code block as and execute code directly from within your org-mode file. It also allows you to rip code out of code-blocks and place it into another file. So, for example, I can have two code-blocks at different points in my org file, both with a :tangle file.cpp header, and have them both be inserted (in order) into file.cpp. This feature is often used for literate configs within Emacs - where one org file contains all the configuration code, with suitable annotations.

I don't use a literate config (currently). Actually, when it comes to Lisp I'm generally against being too literate in your code, either in code comments or literal literate programming. There's a quote out of Riastradh's Lisp Style Rules that I generally adhere to:

Write comments only where the code is incapable of explaining itself. Prefer self-explanatory code over explanatory comments. Avoid `literate programming' like the plague.

Rationale: If the code is often incapable of explaining itself, then perhaps it should be written in a more expressive language. This may mean using a different programming language altogether, or, since we are talking about Lisp, it may mean simply building a combinator language or a macro language for the purpose. `Literate programming' is the logical conclusion of languages incapable of explaining themselves; it is a direct concession of the inexpressiveness of the computer language implementing the program, to the extent that the only way a human can understand the program is by having it rewritten in a human language.

In Lisp, via macros, we usually have the luxury of completely changing the "language" (not literally) of the code we write, which means that code can, in my opinion, actually be self-documenting. Whenever I feel the impulse to annotate something with a comment, I try to take time to reflect on whether that line could instead be written in a better way that requires less external explanation. There are exceptions, like marking where a section of some logic begins and ends, but that's my general philosophy. It's also why I like Racket so much - being able to switch as required between effectively any syntax you want for a particular problem is very useful for maintaining readability in my opinion. Or just because I want to write something using APL syntax.

However, at work, I don't have the luxury of writing in Lisp. Currently I'm doing data engineering with dbt, and I'm mostly writing in SQL (with Jinja macros sprinkled on top). There's also been a lot of hubbub around code style recently, for a number of reasons I won't get into, and now we are recommended to use sqlfluff to fix up formatting on SQL files. So, my two issues here are:

  1. SQL can be very opaque without sufficient commentary (something that is very true for a lot of older dbt models in this codebase).
  2. I do not like the standard sqlfluff formatting rules. This is really just a personal grip, but having functions and keywords be CAPITALISED has always seemed a bit pointless to me, as though it's a relic from a time before syntax highlighting.
  3. Bonus: dbt tests are often undocumented and (this is a problem with a lot of test suites, not just dbt) are abstracted away from the context of the code they're testing.

org-babel helps me fix all of these issues in one fell swoop. Better yet, it lets me do so without ever bothering other people working in the repository with org-mode or Emacs.

Let's tackle the first issue: opaqueness and literate programming. There is more to this than just explaining that "Oh, line X does Y and Z". The business context of a particular decision can also be very helpful to keep track of, and so is the story or task in Jira that a model or code block is a part of. While the former of these two could be done with just code comments, the latter benefits greatly from org's structural editing. For example, if a bunch of user stories are related and should be (per the system design) in one folder in the tree, we can structure the file like so (DE- is the user story prefix in Jira):

* DE-1: Building some pipeline
** Model 1
** Model 2

Even better, some models are made up of a massive chain of SQL CTEs. We can add those to our structure too:

* DE-1: Building some pipeline
** Model 1
*** CTE 1
*** CTE 2

For each model, we can specify the file we want the model to untangle to via header properties for each code-block.

* DE-1: Building some pipeline
** Model 1
:PROPERTIES:
:header-args:sql: :tangle model_1.sql :comments no :padline no
:header-args:yaml: :tangle _model_1.yml :comments no :padline no
:END:

*** CTE 1
*** CTE 2

This way, every SQL code-block within Model 1 automatically gets tangled into the right file, and the YAML (which is used for test specification in dbt) goes to its own file. I can write as much as I want around a code block, and it will also be ignored during the tangle. But, if I want a specific piece of text to be inserted as a comment in the output file (which I might want to do, since the output is going into version control and no one but me will ever see the 'master file'), I can do that by adding :comments org to the header of the code-block.

OK, so literate programming is solved. Let's look at the bonus issue next since that's still more in the realm of literate programming as a whole. One benefit of this setup is that I can write my YAML tests directly next to the actual SQL source code. For example:

#+begin_src sql
  select something from somewhere
#+end_src

:testing:
This goes to the yaml.
#+begin_src yaml
  - name: some_column
    data_Type: INTEGER
    tests:
      - not_null
#+end_src
:end:

This of course requires setting up the boilerplate for the YAML file, which I do in a separate code-block before the first CTE section. I also use this preface to specify the dbt materialization and table alias, as well as add a general comment as to the purpose of the model.

If you're familiar with YAML, you will know that indentation is used to determine scope, and tangling org files tends to mess up the whitespace rules. The solution here is to set the org-src-preserve-indentation variable to t - but we want this to be on a per-file basis. And we have some other Elisp that we want to run, so that we can fix the second issue of code style. I have an Elisp code block that looks like this at the start of the org file:

#+name: startup
#+begin_src elisp :results silent
  (setq org-src-preserve-identation t)

  (defun sql-format ()
    "Run sqlfluff on the current file."
    (call-process-shell-command (concat "sqlfluff fix --dialect snowflake" buffer-file-name)
                            nil
                            nil))

  (add-hook 'org-babel-post-tangle-hook (lambda () (when (string-match-p ".sql\\" (buffer-file-name))
                                                   (beginning-of-buffer)
                                                   (flush-lines "-- :end:")
                                           (flush-lines "^$")
                                           (save-buffer)
                                           (sql-format))))
#+end_src

The sql-format function just runs sqlfluff over the file of the current buffer, our team is using Snowflake so that's the dialect I've selected. I want a hook to run this function on the output whenever I tangle an SQL file, so that's what the add-hook block is doing. The reason for the two flush-lines calls is to get rid of any :end: comments (which come up when I want a comment after a :testing: section) and to remove any empty lines (since sqlfluff will handle padding on it's own). This means that I can write the SQL however I like, and then not care about formatting the output. In fact, until I send the PR for review, I never even have to read the actual output of the master file. I can just work in it the whole time.

We can run this code-block from within org-mode with C-c C-c, but it's a bit of a chore to do this each time I enter the file. There is a better way: with local variables I can execute this code-block when I enter the file. This is also the reason for #+name: startup at the top:

# Local Variables:
# org-confirm-babel-evaluate: nil
# eval: (progn (org-babel-goto-named-src-block "startup") (org-babel-execute-src-block))
# End:

Chuck this at the end of the org file, and now every time you open it that startup block will run.

That's basically it. There's a few caveats: obviously, once this gets merged into the version control then any changes won't synchronise with the org file. This could be fixed with link comments and org-babel-detangle, but that goes against the making sure no one else in the team ever needs to interact with org. This workflow is supposed to be for myself only, and not interrupt anyone else in their reading or writing of the code. This does mean that once it starts going through review, and especially once any code is actually merged, the master file is essentially useless as it won't stay up to date. But that's OK, because usually any changes that are made after merge are minor, and I can still keep the org file updated with any information that shouldn't be a file comment. Or, I can export the whole thing as a word document to describe the implementation in plain English to the reviewer. Not sure if that will ever come up, but it's nice to have the option.

-1:-- Mixing Code Styles with org-babel (Post Jakub Nowak)--L0--C0--2025-07-02T00:00:00.000Z

Irreal: A Journey To Vanilla Emacs

There are many, many posts on Irreal about the journey that people make from the hinterlands to Emacs. This post is about the journey from uber-distributions like Spacemacs and Doom to plain old vanilla Emacs. Irreal doesn’t hold any position, political or otherwise, on what distribution you should use. We’re content to let each user find his or her own Nirvana.

Still, it’s true that many converts come first to Doom or Spacemacs because they find it a bit more familiar and comfortable. Plenty of those people find a permanent home in one of those distributions and live happily ever after. Others, though, still hear the siren song of traditional Emacs and eventually complete their journey by adopting vanilla Emacs.

Donovan Ratefison has a post that recounts his own journey. He has a nice metaphor involving drinking tea. When he first started drinking tea he liked it with lots of sugar. Eventually he discovered that the sugar was masking the true taste and aroma of the tea. The raw tea was, perhaps, an acquired taste but once acquired it opened up a whole new world or tastes and sensations to him.

So it was with Emacs. Ratefison tried both Spacemacs and Doom and liked them both but vanilla Emacs kept calling to him. He like may others decided that he wanted a minimal configuration where he understood everything and there were no packages that he didn’t actually need and use.

He includes a copy of his init.el and it is, indeed, simple. He loads only a few packages. He will probably find the need for additional packages as his Emacs use matures. That, at least, has been my experience but it seems that he has found, as with tea, that unsweetened Emacs is better.

-1:-- A Journey To Vanilla Emacs (Post Irreal)--L0--C0--2025-07-01T15:48:47.000Z

Anand Tamariya: Plan 9 : The Infinity Notebook

 

Plan 9 is an Operating System (OS) from Bell Labs which is a successor to UNIX. It builds on the learnings from the previous Operating systems. Network computing and graphics is an integral part of the OS rather than an afterthought. It is modular and portable - it comes with a cross compiler. It is available, under MIT License, which anybody can use, learn and tweak.

Features

  • Everything is a file.
  • Singular Grid: All the computers running Plan 9 OS and connected together act like a singular grid. So there's no need for remote access solutions like VNC or RDP. This cool video shows a small glimpse of the possibilities.
  • Process Isolation: Processes run within their own namespaces and are isolated from each other. So you can run rio (the window manager) within rio. Applications like Browsers don't need complicated sandboxing. And a crashing program is unlikely to bring down the OS.
  • No DLL Hell: Binaries are always statically linked. So there's no DLL hell and there's no need for application bundles like Snap, Flatpak and Appimage.
  • Plumber: This allows user to define custom rules for disparate application interactions. e.g. opening a browser for HTTP URL or a mail client for mailto URL or a text editor for a file URL.
  • Rune: Unicode (UTF) character handling (multi-lingual support) is easily done via Rune API.

 

Plan 9 Keybindings

Plan 9 OS user interface (UI) is mouse oriented. However, thanks to Common User Access (CUA) specification, we have come to expect keyboard shortcuts to work for certain repeated actions. e.g. Ctrl+x, Ctrl+c and Ctrl+v for cut, copy and paste respectively. This is an attempt to introduce the same in Plan 9.

 

Code (Work in Progress)


 References

  1. Typing Unicode Characters (see also man keyboard(6))
  2. Context Menu is Personal (GNU Emacs)
  3. Alternatives in GNU Emacs - File Explorer
     

 
-1:-- Plan 9 : The Infinity Notebook (Post Anand Tamariya)--L0--C0--2025-07-01T04:04:00.000Z

Sacha Chua: 2025-06-30 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. 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 comment on Mastodon or e-mail me at sacha@sachachua.com.

-1:-- 2025-06-30 Emacs news (Post Sacha Chua)--L0--C0--2025-06-30T19:26:59.000Z

Charles Choi: Take Two: Eshell

This is a contribution to the Emacs Carnival 2025-06: Take Two collection of posts on Christian Tietze’s blog.

My first take with Eshell many years back did not leave a good impression. My early expectations was that it should act like any other shell, only to be unpleasantly surprised by it. It took a long time for me to warm up to Eshell. Upon reflection, it was because I wasn’t ready for it.

Now Eshell is an inseparable part of my Emacs experience. Paradoxically though, I find little occasion to use Eshell in the same way I’ve used shells in the past. Much of what I used to use the shell for, I do today with Emacs modes instead.

It was not always this way. Like so many users who started Unix computing in the 80’s, I started with the command line (for myself ksh and later bash), studiously internalizing command line tools and their arcane syntaxes to support file management, running programs, and systems administration. Overwhelmingly though, it was file management and running programs that I did through the command line.

For file management these days, Dired is my interface of choice. It is simply the more elegant tool for the job.

Case in point: renaming a bunch of files in a directory to arbitrary names. With a shell, my standard approach would have been to run some variant of ls -1 > foo.sh, edit foo.sh to insert mv on each line to a renamed target file, chmod u+x foo.sh to be executable, run foo.sh, delete foo.sh and call it a day.

With Dired, the above steps are replaced as such: make the buffer writable (via Wdired). Change the target filenames to a desired result, then commit the changes. This is even easier if there is a pattern you can use a regexp for. Unsurprisingly, all the basic file management operations that you would want are supported by Dired. Add Casual Dired to help make these operations discoverable and you’ll soon consider how quaint using mv, cp, rm, and ls are.

Running a simple command on a file? Again, you can do that with Dired. Move the point to the Dired line containing the file. Type “!” and enter a command in the prompt. You can optionally reference the filename in the command with the “?” character if you want to redirect its output to a file (e.g. cmd ? > outfile).

Taking file management and running simple commands out of what you would use a shell for, what’s left over? Source code management (SCM)?

Like for many others, git is my goto tool for SCM. Much has been said about the terrible user experience of the git command line and for years I suffered with its design decisions. Thankfully for Emacs users, there is Magit and VC mode which both provide a saner experience for it.

Running a Makefile? Use Emacs compile, which provides command completion for the targets defined in the Makefile and error/warning navigation if needed. (Try out Casual Make for editing Makefiles in Emacs.)

Do you need to run a command with arcane arguments repeatedly? Again, Makefile + compile to the rescue where it can be used as a task runner for that command.

Need robust terminal emulation? Eshell will fail you here and you are likely better off using a dedicated terminal emulator app, separate from Emacs. That said, I’ve found the built-in term to be good enough for many curses-style utilities.

Mining data files with piped commands? Eshell is a power tool here: used wrongly and you could figuratively cut off a limb. But used cleverly, Eshell offers great value. More on this later.

So with all these use cases described, why even use Eshell?

Using Eshell becomes a win only if you know Elisp. With that, you can think of Eshell as an Elisp REPL that can secondarily act as a shell.

Let’s not mince words. For many users this is a high bar, as it takes into account a lot of prerequisite knowledge. That said, learning basic Elisp is trivial for readers already conversant with programming and manageable for those that aren’t. Personally, I was very late (decades!) in taking the time to learn Elisp because I didn’t see the reward in it and so, prioritized accordingly. That hesitancy turned out to be unfounded. Elisp is easy to learn and the reward (or punishment) for knowing it is having the ability to really use the full capabilities of Emacs. (Know Python? Here’s a cheatsheat for Elisp that I wrote, hop in, the water’s fine…)

Once gained, bringing Elisp knowledge to using Eshell gives you a command line super power: the ability to improvise shell commands with Elisp functions.

Consider the following Eshell example where we wish to apply a command on a set of png files in a directory. Let’s start with a simple example that simply echoes filenames with suffix .png.

$ for i in *.png { echo $i }

In Eshell, you can replace echo $i with an Elisp expression.

$ for i in *.png { (format "%s" i) }

You can also mix shell commands and Elisp via dollar ($) expansion. Note that $ expansion in Eshell is different from other shells. A deeper reading is on it is recommended to avoid confusion with what works in conventional shells.

$ for i in *.png { echo $(format "%s" i) }

If you have ImageMagick, installed, you can use the convert utility to change this to a jpeg file, using the Elisp file name components functions to do the work of getting a desired target name.

$ for i in *.png { convert $i $(file-name-with-extension i "jpg") }

Contrast this with the equivalent expression in Bash which I consider to have much more forgettable syntax.

$ for i in *.png; do convert $i ${i/.png/.jpg}; done

Another significance with mixing shell commands and Elisp together is the ability to incorporate Emacs buffers into an improvised workflow. Shell commands are typically designed to work with data organized as files. In contrast, Elisp (rather, Emacs) treats files as a persisted, secondary form of data. Instead, Buffers are the primary data type in Emacs.

Recall the Dired naming example above? Consider the added steps due to using a temporary file foo.sh compared to using a writeable Dired buffer.

Eshell shifts you into thinking differently about working with a prompt, where instead of thinking of output being only a file or (stdout, stderr), the output could also be an Emacs buffer.

With that change in thinking, Eshell is better thought of as a “prompt” interface for Emacs which in turn serves as the interface to all the command line utilities made available to it. In this light, a whole swath of common shell workflows get replaced by Emacs.

Workflow Shell Eshell
File management Orchestrate cd, ls, cp, rm, mv Run dired {optional path}
View a file Run more or less Run view-file
SCM with git Run git command(s) Run magit {optional path}
View man page Run man Run Emacs man
Run Makefile Run make Run make & to send output to a compile buffer
Remote login Run ssh Run Eshell Tramp
Edit a file Run $EDITOR Run find-file
Manage processes Run htop Run proced
Search for pattern in a file Run grep Run Eshell grep to send output to a compile buffer
Locate a file Run locate Run Eshell locate to send output to locate buffer

Suffice to say, Eshell reinforces and rewards keeping Emacs users only in Emacs.

Another common shell workflow is using grep with pipes (|) for data mining files. For example:

$ grep {pat1} {file} | grep -v {pat2} | grep {pat3} >outfile

Using this pattern indiscriminately in Eshell is a recipe for unpleasant surprise if the file to be mined is large. This is because Eshell will ingest said file into Emacs as part of the pipeline processing. This will be slow. To avoid this, you can prefix each | with an * as follows:

$ grep {pat1} {file} *| grep -v {pat2} *| grep {pat3} >outfile

Why store a result in a file (outfile) that you have to open to see it and may not care to keep around? Here Eshell gives you the ability to redirect to a buffer.

$ grep {pat1} {file} *| grep -v {pat2} *| grep {pat3} >#<buffer "*my grep results*">

Note that Eshell supports creating/overwriting (>) and appending (>>) for redirection. Use > with care when working with an existing buffer.

What about piping shell command output to an Elisp function? At the time of this writing, you really can’t though according to this Reddit thread, this may change in the future.

If you are used to workflows that bleed out megabytes of data to stdout, you really shouldn’t use Eshell. But with some cleverness, Eshell can help you get insights to your data faster by leveraging all the tools Emacs can offer for this (occur, highlighting, grep, etc.).

Closing Thoughts

IMHO, Eshell is best thought primarily as a prompt for Elisp/Emacs functionality with the secondary benefit of running command line utilities with a “shell-like” experience. It should not be thought of as a “drop-in” replacement for an actual terminal shell. If full terminal emulation is desired, my guidance is to run a dedicated terminal emulator outside of Emacs.

To get the full benefit of using Eshell, you really need to be conversant with Elisp. That said, Eshell is actually a great way to start learning Elisp as it provides you a prompt interface (REPL) in much the same way that other languages like Python, JavaScript, Ruby, Java, Swift, etc do.1 Especially if you are comfortable with programming, take a day to learn Elisp. You won’t regret it.

Bringing Eshell into my Emacs journey has been amazing and I’m happy to have given it a second take.

Thanks to all the contributors of Eshell.

Footnotes

1 To clarify, these languages adopted the idea of a REPL from Lisp development which Elisp is descended from.

-1:-- Take Two: Eshell (Post Charles Choi)--L0--C0--2025-06-30T16:30:00.000Z

Marcin Borkowski: Interacting with an external process via stdin and stdout

For a project I’ve been recently working on, I needed Emacs to interact with an external process, sending things to it via its stdin and receiving responses via its stdout. I never did anything like that before, so I started with checking my options.
-1:-- Interacting with an external process via stdin and stdout (Post Marcin Borkowski)--L0--C0--2025-06-30T15:46:19.000Z

Irreal: Fold And Focus Comes To Markdown

Flandrew likes Org mode but he wanted to improve his workflow with it a bit. So he wrote some code that he called Fold and Focus—Focused Navigation. The idea was that when in Org mode a single key would move to the next or previous subtree, position that subtree at the same place on the screen, and have everything else folded so that he could concentrate on the current subtree.

That worked out pretty well but after a while wanted to bring the same sort of functionality to Elisp buffers, which he did. Recently, while working in a Markdown buffer, he automatically tried to navigate in the same way but, of course, it didn’t work. So he did the natural thing and ported focused navigation to work in Markdown buffers.

I don’t use Markdown so I have no use for this functionality but it seems like a really useful addition to your toolkit if you do. Take a look at flandrew’s posts. He has animations that show how it works in all three environments. If the project seems interesting to you, take a look at the project page.

-1:-- Fold And Focus Comes To Markdown (Post Irreal)--L0--C0--2025-06-30T15:36:26.000Z

Greg Newman: Emacs Carnival 2025-07: Writing Experience

It is month two of the Emacs Carnival and I am hosting July with the topic of "Writing Experience".

This is such a wide ranging topic but one I know the Emacs community and beyond loves to discuss. It usually produces some interesting posts and workflows. Whether you are writing novels, zettlekasten or simply taking notes, what is your writing experience in Emacs. Denote, Howm, Orgmode, Org-Roam, Journal, it doesn't matter. How does your writing workflow in Emacs translate to your mobile devices? What are your pain points? What are your successes? Lay it all out there.

I'm hoping the July topic produces new ideas and inspiration. Have fun with it.

What’s a Blog Carnival, Again?

(Copied from Christian Tietze introductory post)

A blog carnival is a fun way to tie together a community with shared writing prompts, and marvel at all the creative interpretations of the topic of the month. I’ve provided a couple of interpretations above, but you may think of something else entirely. That’s amazing, roll with it, that’s what makes this fun!

For future Carnivals, check out the “Carnival” page on EmacsWiki. It includes instructions, and is our community space to coordinate participants and topic ideas.

We are still needing more hosts so please volunteer if you want to host one of the months by adding your name to the EmacsWiki page.

Submissions

I'll aggregate all the submissions for July in this post. I do not yet have a contact form on this new site so you can email me at greg@gregnewman.org or DM me on Mastodon with a link to your submission.

-1:-- Emacs Carnival 2025-07: Writing Experience (Post Greg Newman)--L0--C0--2025-06-30T00:00:00.000Z

Donovan R.: My 2025 Note-taking workflow

Taking digital notes is something I do a lot. Not because I particularly enjoy writing all the time or because I’m especially good at it, but because I want to understand myself better and gain a little more control over my life.
Taking notes is like building a time machine that tracks how my mind works.
I do it also because it’s fun to rediscover my old self through my notes.
Sometimes I made silly decisions, other times I appeared wiser than I do today.
Sometimes I find my belief swinging from one extreme to the opposite like a pendulum.

It’s June 2025, and I want to share with you what kind of notes I take and an overview of my current workflow for note-taking.

As for today, I’m using both Logseq and Emacs to take notes.

Logseq

Logseq still holds most of my old notes, and I rely heavily on it for capturing media-related content (videos, Excalidraw diagrams, and more).

What notes do I take in Logseq?

Quotes

I capture quotes from other people that resonates with me, as well as my own insights.
I use the Logesq banner plugin to randomly display these qutes in the banner.
I love this mechanism of resurfacing quotes.

Contacts and People info

When I interact with someone, I note the interaction in the journal page, using their name as a tag (which also creates a page in Logseq).
Later, I can fill in more details about the person, such as email addresses or phone numbers on their page.
From their page, I can quickly check every interaction in the backlink section.

Youtube links

I use the youtube timestamps when learning something new or bookmarking some interesting part of a video.
This allows me to quickly jump to the relevant part later.

Excalidraw

I use the build-in excalidraw to brainstorm or create presentations.
I maintain a vertical calendar in Excalidraw where I jot down some notes about what I might want to do each month.
I use the green color to separate what’s done from the rest.
I find this gives me a convenient overview of the year.

Presentation

Logseq is excellent at structuring ideas with bullet points.
The Bullet Threading plugin combined with the built-in zoom in / out feature of Logseq makes it a very good tool for presentation.

Emacs

I rely extensively on Org mode for note-taking on Emacs.

What notes do I take in Emacs?

Long-form notes

Insights, Technical documentation, Blog Posts, Journals, etc, when I’m in the mood for a long-form writing, I switch to Denote and start typing.
I don’t use many of Denote’s features. I just run M-x denote , fill in the title and tag, and start writing.
It’s simple and gets the job done!

Project

I have a folder named project where I put org file for each project I’m working on.
I don’t use a specific structure yet. Just bullet points for todos and scheduled items with notes for each project task.
The org files are linked to Org agenda for convenience.

Time tracker

Similarly, I have org files for tasks. For example work.org for work-related tasks.
I track the time I’m working on tasks using org-timer-set-timer combined with org-clock-in and org-clock-out.
And I use Org capture template for convenience.

Event Scheduling and Agenda

As above, I have an agenda.org file that is linked to my Org agenda for all my scheduled event.
Using of course org-schedule for scheduling a task.

Synchronization

Wether I’m using Logseq or Emacs, all my notes are linked to a private Git repository. This setup ensures that my data is always synchronized and backed up.
It’s simple and reliable.

In summary

This is my current workflow. Nothing set in stone, just how I do things right now.
Like everything else, it changes and evolves over time as I learn and grow.
Who know what it’ll look like in the next month or next year?
For now, this is what works for me.

-1:-- My 2025 Note-taking workflow (Post Donovan R.)--L0--C0--2025-06-29T19:40:48.000Z

Irreal: How To Stop Emacs From Asking For Aliases

Today (Saturday) appears to be a slow day on the technical Internet or perhaps everyone is hiding out from the AI apocalypse. Whatever the reason, I can’t find anything to write about that’s up to the refined standards of Irreal readers.

I offer, therefore, this short post on a niche problem that most of you probably don’t have but that may be useful to those that do. If you’re an Eshell user you may or may not know that if you repeatedly try to run a command that doesn’t exist, Eshell will ask you if you want to set an alias. Over at the Emacs subreddit, Both_Confidence_4147 complains about this and asks how to stop it.

Presumably, Eshell thinks you’re repeatedly mistyping the command name—as in the old time common error or typing “moer” for “more” and similar errors—and is offering you a way to avoid the error going forward. Eshell will ask you for an alias after 3 failed commands but, of course, this is configurable. You can also arrange to disable the prompt completely as explained in this comment to Both_Confidence_4147’s post.

Again, this is a niche problem unless, of course, you’re the own who’s experiencing it. If you are, now you know how to fix things.

-1:-- How To Stop Emacs From Asking For Aliases (Post Irreal)--L0--C0--2025-06-29T16:11:39.000Z

Jakub Nowak: Goodbye LanguageTool, Hello Harper

I found out about Harper the other day. Ever since I heard about it, I've been itching to set it up in my config and get rid of the horrible LanguageTool setup I had going. Now that I've got it working, I've gotta say I'm quite impressed. My previous LanguageTool setup was not smooth at all, and definitely not as fast as Harper is. I'm not sure how anyone else is doing spellchecking in Emacs (and particularly in org-mode) at the moment, but I recommend anyone using Emacs for most of their writing to give it a shot.

I won't embarrass myself with a comparison of my LanguageTool config. Just know that it was slow and terrible. Copied from the Harper docs, this is what my config looks like:

(when (and (not (equal system-type 'windows-nt)) (locate-file "harper-ls" exec-path))
  (with-eval-after-load 'eglot
    (add-to-list 'eglot-server-programs
               '(org-mode . ("harper-ls" "--stdio"))))

  (setq-default eglot-workspace-configuration
              '(:harper-ls (:dialect "Australian")))

  (add-hook 'org-mode-hook 'eglot-ensure))

Some caveats: it doesn't look like Harper has support for org-mode specifically yet, but it seems to generally work OK despite that. I have however noticed that it doesn't lint property tags, so for example your #+title: won't be spellchecked. Another minorly annoying little niggle is that straight seems to misbehave with eglot, or eldoc more specifically, printing eldoc error: (invalid-function incf) in the minibuffer instead of an actual error message. I was using lsp-mode before this - although I'm beginning to like the eglot interface more and more as I write this, so I think I may swap over to it where possible - so I never realised this issue. Per that GitHub issue link, (use-package eldoc :straight (:type built-in)) seems to be the solution.

The only thing that's missing, and to be fair LanguageTool doesn't implement this either (in fact to my knowledge nothing implements this, I don't even think there's a Word plugin), is text style metrics analysis. Every so often I get the impulse to try to untangle what the source code for that website is doing, and then write a simple elisp wrapper for it, but I always get sidetracked before I start. Maybe now is the time.

Addendum

I've discovered another few quirks in setting this up (mostly to do with eglot and company), so I figured I'd go back to update this post and catalogue what I've had to do for future reference.

eglot overwrites company-backends by default. This can be disabled with (setq eglot-stay-out-of '(company)). Figuring this out also convinced me to finally fix company-ispell, which I could not get working last time I tried. To get it working with both Ispell and flyspell it seems to need to following code-block:

(if (file-exists-p "/usr/bin/hunspell")
    (progn
      (eval-after-load "ispell"
        '(progn
         (setq ispell-program-name "hunspell"
               ispell-dictionary   "en_AU"
               ispell-alternate-dictionary (file-truename (concat user-emacs-directory "en_AU.dict"))
               ispell-local-dictionary-alist '(("en_AU" "[[:alpha:]]" "[^[:alpha:]]" "[']" nil ("-d" "en_AU,en_AU-med") nil utf-8)))
         (defun ispell-get-coding-system () 'utf-8)))
      (eval-after-load "flyspell"
        '(progn
         (setq ispell-program-name "hunspell"
               ispell-dictionary   "en_AU"
               ispell-alternate-dictionary (file-truename (concat user-emacs-directory "en_AU.dict"))
               ispell-local-dictionary-alist '(("en_AU" "[[:alpha:]]" "[^[:alpha:]]" "[']" nil ("-d" "en_AU,en_AU-med") nil utf-8)))
         (defun ispell-get-coding-system () 'utf-8)))))

I'm using Hunspell here - if you don't also eval-after-load for flyspell, then ispell-program-name gets overwritten (in my case, to enchant-2, which wasn't working). The alternate dictionary needs to be created, Hunspell doesn't ship a plaintext dictionary. You can make it by going to /usr/share/hunspell/ and running unmunch en_AU.dic en_AU.aff >> /~//.emacs.d/en_AU.dict. You'll notice that I'm using file-truename to turn that user Emacs directory into an absolute path - for some reason, it wasn't working with just the relative path.

-1:-- Goodbye LanguageTool, Hello Harper (Post Jakub Nowak)--L0--C0--2025-06-29T00:00:00.000Z

Greg Newman: Emacs Take Two (Eglot)

This post is my contribution to the Emacs Carnival June 2025 topic "Take Two" set by Christian Tietze. This is also a cross post for the IndieWeb Carnival: Take Two which was the original inspiration behind the Emacs Carnival. You can get more information about the Emacs Carnival on the wiki.

I started using Emacs about 20 years ago. From the start I was heavily involved in the Emacs IRC channel and the Org mode mailing list. I contributed the refresh of the Org mode logo to Dr. Carsten Dominik and the community as a small thank you for such a great package. All my notes were stored in Org mode files, along with my task lists. For development I used Elpy for Python and Webmode for anything HTML and Javascript. Elpy was good enough for me back then. Webmode was just ok but had a lot of problems with javascript. I jumped over to LSP mode pretty early and that solved a lot of problems with Python but it was a pain to configure and broke at the most inconvenient times. I was frustrated a lot of the time I was trying to work.

Around 2015 I was introduced to PyCharm by a client. The debugger was fantastic and hooked me quickly. It replaced a lot of what I was using Emacs for. I still used Emacs but jumped back and forth between the two (a lot of things are just easier in Emacs). Around the same time I started working with React and I could never get anything working well in Emacs, but PyCharm just worked. So I stayed with PyCharm for many years (mostly). All my notes were still stored in Org mode files (and most still are). I still have notes in org files from nearly 20 years ago. I can't say that about any other notes apps.

When Eglot started gaining momentum I decided to see what it could do. I was pleasantly surprised but it did not fully bring me back. It was still easier to just use PyCharm. PyCharm is a great IDE but I find IDEs too opinionated. I always had the itch to return to Emacs for all my development needs. A year or two ago I decided to give Eglot another try. It had matured - or perhaps I had. I played around with the available options for LSP and found Pyright to be very good. I was able to get my Emacs config to provide me with most of what PyCharm was handling. When I finally upgraded Emacs from 29 to 30, I stopped using PyCharm. With Eglot, the current options for Language Servers, and Tree-sitter I now have a stable environment for Python/Django work as well as React. I'm now comfortable with my environment.

Currently, I'm using Eglot with Basedpyright Language Server and Ruff for linting. For React/Javascript I'm using the Typescript Language Server. Below is my current Eglot config. It has taken many iterations to get it where I'm happy with it. I still need to add JS linting to it but for now that's mostly covered in my pre-commit hooks.

  (use-package eglot
    :straight t
    :ensure t
    :defer t
    :custom
    ;; Optimize performance
    (eglot-send-changes-idle-time 0.5)
    (eglot-extend-to-xref t)
    ;; Configure language servers
    (eglot-server-programs
     '((python-ts-mode . ("basedpyright-langserver" "--stdio" 
                          :initializationOptions (:basedpyright (:plugins (
                             :ruff (:enabled t
                                   :lineLength 88
                                   :exclude ["E501"]  ; Disable line length warnings
                                   :select ["E", "F", "I", "UP"])  ; Enable specific rule families
                             :pycodestyle (:enabled nil)  ; Disable other linters since we're using ruff
                             :pyflakes (:enabled nil)
                             :pylint (:enabled nil)
                             :rope_completion (:enabled t)
                             :autopep8 (:enabled nil))))))
       ((js-ts-mode typescript-ts-mode tsx-ts-mode) .
        ("typescript-language-server" "--stdio"))))
    :config
    (setq-default
       eglot-workspace-configuration
       '(:basedpyright (
           :typeCheckingMode "off"
         )
         :basedpyright.analysis (
           :diagnosticSeverityOverrides (
             :reportUnusedCallResult "none"
           )
           :inlayHints (
             :callArgumentNames :json-false
           )
         )))
    :bind (:map eglot-mode-map
                ("C-c l a" . eglot-code-actions)
                ("C-c l r" . eglot-rename)
                ("C-c l f" . eglot-format)
                ("C-c l d" . eldoc)
                ("C-c l o" . eglot-code-action-organize-imports)
                ("C-c l h" . eglot-inlay-hints-mode)
                ("C-c l q" . eglot-shutdown-all))
    :hook ((python-ts-mode . eglot-ensure)
           (js-ts-mode . eglot-ensure)
           (typescript-ts-mode . eglot-ensure)
           (tsx-ts-mode . eglot-ensure)
           ;; Python-specific settings
           (python-ts-mode . (lambda ()
                               (setq-local indent-tabs-mode nil
                                           tab-width 4
                                           python-indent-offset 4)
                               (superword-mode 1)
                               (hs-minor-mode 1)
                               (set-fill-column 88)
                               (display-line-numbers-mode 1)))))
-1:-- Emacs Take Two (Eglot) (Post Greg Newman)--L0--C0--2025-06-29T00:00:00.000Z

Irreal: More Emacs Startup Time

Because I know you guys never stop jonesing for another episode of the endless debate on Emacs startup time, here’s yet another post to feed your addiction. Over at the Emacs subreddit, prothtuahel complains that Emacs is taking 3 seconds to load 412 packages1 when all he wanted to do was add a TODO to a file.

There’s so much to say. First, why are you restarting Emacs for each file you want to edit? I know it can be disorienting for new users but when you’re using Emacs, you should leave it running all the time and not restart it every time you want to edit a file.

Second, if you must restart it for each file, start Emacs in server mode and use emacsclient to invoke it for each editing session. Emacsclient will start instantly because Emacs is already waiting in the background. But really, see the first point.

Third, you’re complaining about taking 3 seconds to load 412 packages? That’s actually pretty fast, but see the first and second points.

The comments mostly concentrate on the three points above but, of course, someone points out that you can speed things up with lazy loading. To me, the most cogent argument is that who cares?. If, as you should, you only load Emacs a few times a month or year, who cares how long it takes to load? I always get pushback when I write that but unless you’re one of the few people with special needs that require starting Emacs often, it remains true.

Footnotes:

1

Sadly, once again reddit has chosen to delete the post because reasons but the headline and comments are still there. Someone really needs to sit the reddit mods down and have a polite but firm discussion.

-1:-- More Emacs Startup Time (Post Irreal)--L0--C0--2025-06-28T15:28:09.000Z

Peter Tillemans: Consolidating Secrets in Pass

Background

Like a lot of people I've had a long history managing passwords and secrets over the years. From a little black book, over an Excel sheet, using a GPG encoded secrets file (works really well with Emacs gpg support), 1password (till they racked up their prices), lastpass (till they got bought by the Evil LogMeIn Corp), KeepassXC and lately pass.

I was perfectly happy with KeepassXC for a very long time, except for the command line integration. So I kept ending up with passwords in .envrc files in folders and excluded in the global .gitignore to avoid too many red cheeks. While this does keep secrets out of harms way mostly, it kept nagging that I had them in plain text in those files. In theory there is keepassxc-cli to query the passwords from the command line, but let's say the experience does not spark joy. It has no easy way to cache the password between calls and it is optimized for interactive use. (AFAICT, just the giant size of the command to type gives me dread).

Some day I stumbled over pass and found that after setup I could just pass snamellit/website to get the password on stdout. I wrote about the setup and emacs integration in a previous post. Since it leverage gpg, password caching is handled by the gpg-agent and my .envrc files quickly were purged of blasphemous secrets, replace by pure bliss:

export MY_SECRET=$(pass my/secret)
export OTHER_SECRET=$(pass other/secret)

similarly in emacs I can consistently get my passwords and related info with:

(org-gcal-client-id (auth-source-pass-get 'secret "snamellit/org-gcal-client"))
(org-gcal-client-secret (auth-source-pass-get "id"
"snamellit/org-gcal-client"))

When needed the gpg-agent will launch the appropriate pin-entry program whether in terminal or in the GUI and the caching will not force me to login several times when entering the folder.

So I ended up with my interactive use covered by KeepassXC and automated use by pass.

However, after some time I ended up with hundreds of secrets in KeepassXC, hundreds in pass, it is not always clear whether use is interactive or automated so confusion and duplication starts and things become harder to manage. In addition KeepassXC was using historically Dropbox to make it available on all my devices, recently migrated to Nextcloud, which has issues with dealing with conflicts which occasionally bite me in the behind. On the other hand pass secrets are stored encrypted in git where conflict punch you in the face. I prefer the latter. And started contemplating whether to move everything to pass.

Thanks to the encouragement of SummerEmacs, one of the more enthusiastic SystemCrafters, ensuring the great experience in browsers and iOS mobile devices I had no more excuses to keep postponing it.

Preparation

I started out with keeping my pass passwords as part of my dotfiles. This was convenient when they were few. However this is weird so this will attract weirdness when configuring all integrations I'll need.

Also pass supports a git command to manage the password-store with git which is not really useful when it is part of something else. So the first order of the day is to move all secrets to a separate repository and update the dotfiles to check for presence and clone the repo if missing (and do a gently pull when it is). A quick visit to each of the machines in my machine park to apply this change. Everything still seems to be working.

Migration of the KeepassXC data

I used the pass-import tool which adds in import command to pass which supports a crazy amount of password managers, including keepassxc. For keepassxc it need the pykeepass. If you're running on Arch, everything is a yay -S away. However on Ubuntu and its derivatives it is the usual slog we start to get accustomed to. It's all in the pass-import README , note that on Ubunty the pykeepass library is available with apt install python3-pykeepass.

Once it is installed I tried a dry run (with the -d flag) to see if basic functionality is working

pass import -a -d keepassxc ~/Nextcloud/Apps/Keepassxc/Passwords.kdbx
Password for /home/pti/Nextcloud/Apps/Keepassxc/Passwords.kdbx:
  w  Data would be imported from keepassxc to pass
  .   Passwords imported from: /home/pti/Nextcloud/Apps/Keepassxc/Passwords.kdbx
  .   Passwords exported to: /home/pti/.password-store
  .   Number of password imported: 2035
  .   All data imported
  w  Weak password detected: eDGQqipE might be weak.  Score 2 (100000001 guesses).  This estimate is based on the sequence eDGQqipE(bruteforce)
  w  Weak password detected: eDGQqipE might be weak.  Score 2 (100000001 guesses).  This estimate is based on the sequence eDGQqipE(bruteforce)
  w  Weak password detected: eDGQqipE might be weak.  Score 2 (100000001 guesses).  This estimate is based on the sequence eDGQqipE(bruteforce)
...  large list of names of secrets

This asks for the password of the Keepass file and some remarks it has.

This all looks reasonable. So we can try the import. Since the password-store is a git repo no real damage can be done to it (as it is safely pushed somewhere else where the import tool cannot touch it) and any damage done can be reverted....

Now is a good time to check if the mooring lines of your laptop are properly secured as encrypting all the secrets will spin up the propellors if the number is large enough.

I run it again without the -d flag and after several minutes the noise dies down and I am left with a lot of additional folders in my ~/.password-store which match the grouping in KeepassXC. The files contain the secrets and the expected metadata. This looks good so I add/commit the things to complete the level.

Integration with iOS for my iPhone

Let's start with the most scary one : the iPhone.

Upon recommendation I had installed passforios which needs to be configured.

Configuring the host, repo and username to use for the git repository is straightforward enough.

I always use ssh to access my repos so we need to add an ssh keypair for this purpose. There is no support to generate key-pairs in passforios for reasons, so I have to do it externally and upload the key. A quick ssh-keygen , uploading the public key to the forge, allowing access to the repo and if I can get the private key on my phone we can access the repo.

passforios has a nice feature to load ascii armored keys via a QR code. A bit digging surfaced the asc-key-to-qr-code-gif tool which was made for this specific purpose. The ssh key is already in the appropriate format so this can be directly converted

./asc-to-gif.sh ~/.ssh/id-passforios ssh-pub.gif
display ssh-pub.gif

Then go to the repository settings, press the circled i on the SSH Key button, select the ASCII-Armor Key and click to scan the QR code. Point the camera to the QR code on the screen and it should appear in the key field in the app.

We have to repeat this 2 more times to get the private and public key for the password-store into the app. First exporting the keys

gpg --export -a 1234ABCD >gpg.pub
gpg --export-secret-key -a 1234ABCD >gpg.key

converting to a gif, displaying them and scanning them in *Settings -> PGP Key -> ASCII-Armor Key in the respective fields.

If, after synching, you go now to the Passwords you should be greeted with a listing of all folders and keys and the secrets should be visible if made visible by tapping the eye icon.

I needed to enable passforios as a source for autofill : Settings -> Passwords -> Autofill Passwords and slide the toggle for Pass. I also disabled the toggle for Strongbox which I was using for integration with the Keepass database.

Now I see the option to select the secrets from the passforios app. It does not narrow down to the right key, but that is a problem for future me.

Ok, the hard part is done. Or at least the most risky part, ... in my eyes... whatever. Moving on...

Integration with FireFox

Checking at the bottom of the pass website we find that passff is the good stuff for integration with FireFox. From previous adventures with KeepassXC and NativeMessaging I assumed there had to be a host part to be installed too.

Indeed we are directed to the passff-host github repo to get an install-script which generates the native messaging json for the different browsers and a small executable python script which contains remarkable clean and no-dependency code. Similarly the install script is straightforward. I do not understand why it support half a dozen browser, mostly chrome based as for the life of me I cannot find an extension which uses this host program. So either I need bigger glasses or there is some knowledge beyond my grasp.

Running the installer, installing the extension, restarting firefox for good luck and the extension appears and offers passwords on the sites I try.

Out of curiosity I check the configuration in ~.mozilla/native-messaging :

pti@tuxedo ~> ls .mozilla/native-messaging-hosts/
org.keepassxc.keepassxc_browser.json  passff.json  passff.py*
pti@tuxedo ~> cat .mozilla/native-messaging-hosts/passff.json
{
  "name": "passff",
  "description": "Host for communicating with zx2c4 pass",
  "path": "/home/pti/.mozilla/native-messaging-hosts/passff.py",
  "type": "stdio",
  "allowed_extensions": [ "passff@invicem.pro" ]
}
pti@tuxedo ~> cat .mozilla/native-messaging-hosts/passff.py
#!/usr/bin/python3
"""
    Host application of the browser extension PassFF
    that wraps around the zx2c4 pass script.
"""

import json
...

Nothing out of the ordinary, the passff.py python is the same as in the repo. My old keepassxc extension support is still there.

Firefox is installed natively on this machine, not with a flatpak which I assume will come with its own challenges.

Chromium Support

Time to tackle the Chrome family. Chrome is required to put food on the table so we have to get that going eventually. But Chrome is distributed as a flatpak (or a snap but I am NOT going to deal with that), and I can install Chromium natively, and apparently native installs are MUCH better supported than the versions in wrappers so let's start with that one first.

From the pass website we find that browserpass is the way to go for the chrome family. The browser extension installs from the usual places without drama and starts promptly complaining it cannot find the native host to talk to.

The native host in question is from the browsaerpass-native sister repo . As usual for all distro's there are packages ready to install but because Ubuntu-derivative I can compile from source. Downloading the source for version 3.1.0 from the releases page. Again this repo refers to all browsers including firefox although I cannot for the life of me find a Firefox Extension supporting this host app.

Then building and installing timelapse :

tar -xzvf ~/Downloads/browserpass-native-3.1.0.tar.gz
cd browserpass-native-3.1.0
ls
less README.md
PREFIX=/usr/local make configure
sudo make PREFIX=/usr/local install
which browserpass

which shows the executable lives at /usr/local/bin/browserpass and this totally went fine the first time (NOT!!!!).

The Makefile has support to install the magic json to enable native messaging for the different browsers.

PREFIX=/usr/local make hosts-chromium-user
PREFIX=/usr/local make hosts-chrome-user

The second invocation is a hail-mary because I already know the Chrome flatpak does not look in the same places and will require some additional finnagling

For now focus on Chromium and check if the configuration looks reasonable:

pti@tuxedo ~> cd .config/chromium/NativeMessagingHosts/
pti@tuxedo ~/.c/c/NativeMessagingHosts> ls
com.github.browserpass.native.json@
pti@tuxedo ~/.c/c/NativeMessagingHosts> cat com.github.browserpass.native.json
{
    "name": "com.github.browserpass.native",
    "description": "Browserpass native component for the Chromium extension",
    "path": "/usr/local/bin/browserpass",
    "type": "stdio",
    "allowed_origins": [
        "chrome-extension://naepdomgkenhinolocfifgehidddafch/",
        "chrome-extension://pjmbgaakjkbhpopmakjoedenlfdmcdgm/",
        "chrome-extension://klfoddkbhleoaabpmiigbmpbjfljimgb/"
    ]
}

Cool, the executable is looked at where it is installed (this is not obvious, don't ask how I know). The rest looks also like how these things should look. Let's try...

The extension settings page is no longer complaining the native host is missing and there are password entries visible. Checking with some website shows the password is injected. yay!.

Level complete, ready for the final boss.

Enabling Chrome Support, now with more Flatpak!

Ok, we have a working chromium support so repo access, host app, native host configuration et al are proven working. We can only focus on jumping over the Flatpak Firewall...

As a good cargo cultist I do a literature study and find that I should

  • find the config location of the flatpak app

  • use flatpak-spawn to spawn the native messaging host app

  • enable D-Bus Session socket access for chrome

  • Package up the calling of the host app in a single script to configure in the json.

    Not necessarily in that order....

For the permission to access D-Bus Session start up flatseal from flathub, navigate to com.google.Chrome and enable the D-Bus Session socket. This should be possible with some additional cursing in the manifest file of Chrome. I cannot find decent reference documentation in a reasonable time, so flatseal it is.

The configuration of the flatpak app is easy too, painful experience seared in my brain that flatpaks look in ~/.var/app/ folder so for Chrome this will be ~/.var/app/com.google.Chrome . From the hail-mary install for chrome done above I know that it just creates a symbolic link to /usr/local/lib/browserpass/hosts/chromium/com.github.browserpass.native so we can start from there. We will have to edit that so copy it. We also need a wrapper to call the native host app

cd ~/.var/app/com.google.Chrome/config/google-chrome/NativeMessagingHosts
cp /usr/local/lib/browserpass/hosts/chromium/com.github.browserpass.native
ec browserpass.sh

Add the content of the wrapper

#!/bin/sh
cd ~
/usr/bin/flatpak-spawn --host /usr/local/bin/browserpass 2>/tmp/browserpass-error.log

I added the optional redirect of stderr to an error logfile because from experience I know nothing ever goes wrong if you enable error reporting beforehand.

chmod +x browserpass.sh
pwd
pwd | wl-copy
ec com.github.browserpass.native.json

Installing the browserpass extension in Chrome after restarting it (I am not superstitious, just careful) and I can bask in the glory of seeing proposals for passwords when trying to log in. Most of the proposals are pretty garbage, but that is a problem for future me.

Conclusion

I have access to my password-store secrets on my phone, my browsers on laptop and desktop, and most importantly Emacs. Narrowing of the proposed secrets is, euhmmm, sub-optimal, but since it is sub-optimal in the same way on all platforms I assume that some TLC in the password-store and cleaning of the migrated secrets will fix that in time.

In the process I gained much more confidence in configuring flatpak apps. I can decommission the keepassxc system including dealing with the sync conflicts (which was admittedly super easy with the merge database feature in KeepassXC). I no longer have to deal with giving the KeepassXC window a place on the desktop and autostarting it.

I am a bit puzzled about the host-apps referring to supporting browsers for which no extensions are available. This probably might warrant some additional investigation.

Big step forward

-1:-- Consolidating Secrets in Pass (Post Peter Tillemans)--L0--C0--2025-06-28T11:15:00.000Z

Emacs APAC: Announcing Emacs Asia-Pacific (APAC) virtual meetup, Saturday, June 28, 2025

This month’s Emacs Asia-Pacific (APAC) virtual meetup is scheduled for Saturday, June 28, 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, June 28, 2025 (Post Emacs APAC)--L0--C0--2025-06-28T04:19:43.000Z

Protesilaos Stavrou: Emacs: beframe version 1.4.0

beframe enables a frame-oriented Emacs workflow where each frame has access only to the list of buffers visited therein. In the interest of brevity, we call buffers that belong to frames “beframed”.

Below are the release notes


Version 1.4.0 on 2025-06-28

This version adds some minor improvements to a stable package.

Per-frame Xref histories

The built-in Xref mechanism is typically used to navigate to the definition of the symbol at point in a programming major mode. The command xref-find-definitions (M-. by default) jumps to the source, while xref-go-back (M-, by default) goes back in the history of visited positions until it reaches the starting point.

When beframe-mode is enabled, each new frame has its own Xref history. This means that finding a definition in one frame does not interfere with the Xref history of another frame.

“Beframed” buffer prompt text is now optional

When beframe-mode is enabled, it sets the standard read-buffer-function to one that filters buffers by frame. Any command that uses that, such as switch-to-buffer (C-x b by default) is thus beframing its completion candidates.

Such prompts get a prefix to inform the user of their behaviour. By default this is [Beframed]: it is subject to the user option beframe-prompt-prefix. Users who do not wish to have any prefix can set this option to nil or an empty string.

When there is a string, it is styled with the face beframe-face-prompt-prefix.

-1:-- Emacs: beframe version 1.4.0 (Post Protesilaos Stavrou)--L0--C0--2025-06-28T00:00:00.000Z

James Dyer: Setting up a C# Language Server (csharp-ls) on an Air-Gapped Windows System

For a little while now, I have been developing C# applications at work and on Windows!, possibly the perfect storm for Emacs, but actually, it copes pretty well, especially with eglot and the LSP server OmniSharp.

But recently my work setup has become even more air-gapped, and unfortunately, I reached a point where I couldn’t get eglot and OmniSharp working together. I gave it a really good try, but I think the mishmash and dependency installation madness finally took a toll.

It has something to do with trying to find MSBuild.exe, but due to my potentially wonky installation, I couldn’t figure out how to detect it. If Visual Studio is installed on the system, there are no problems; however, offline installation, even of the Community edition, is not an easy task.

So, the solution? Well, I thought I would try another C# LSP in the form of csharp-ls. Its deliverable form is a NuGet package, which can be extracted, placed in the relevant location, and then you’re pretty much off and running!

So here is how I did it:

Step 1: Download csharp-ls nuget Components

On a machine with internet access (which fortunately I do have access to):

Download csharp-ls

  • Go to the csharp-ls NuGet page
  • Click “Download package” to get the .nupkg file
  • Rename the file from .nupkg to .zip
  • Extract the contents

Step 2: Install csharp-ls (Airgapped Machine)

  1. Create a directory in your Emacs installation:

          mkdir "c:\path\to\emacs\bin\csharp-ls"
    
  2. Copy the extracted csharp-ls contents to this directory. The key file you need is:

          tools\net9.0\any\CSharpLanguageServer.dll
    

Step 3: Configure Emacs

Add this configuration to your Emacs init file:

;; Configure csharp-ls as the language server for C#
(setq eglot-server-programs
      '((csharp-mode . ("dotnet" "c:/path/to/emacs/bin/csharp-ls/tools/net9.0/any/CSharpLanguageServer.dll"))))

Step 4: Set Up Your C# Project Structure

Basically defining a source code collection as a project in Emacs. This can be a solution file, a source code control directory or as I generally have set up, an empty .project file

Step 5: Set up paths

This may not be needed, but for my own sanity and just in case, absorb the new directory in to the PATH and exec-path variables.

(when (eq system-type 'windows-nt)
  (let ((xPaths
         `(
           ,(expand-file-name "~/bin/csharp-ls/tools/net9.0/any")
           )))
    (setenv "PATH" (concat
                    (mapconcat 'identity xPaths ";")))
    (setq exec-path (append (split-string winPaths ";") xPaths (list "." exec-directory)))))

So it works as I had it with OmniSharp and that really is all I can ask for, lets see how I get on!

-1:-- Setting up a C# Language Server (csharp-ls) on an Air-Gapped Windows System (Post James Dyer)--L0--C0--2025-06-27T21:27:00.000Z

Irreal: Why Did You Switch From Vim To Emacs?

Over at the Emacs subreddit, floofcode asks those who moved from Vim to Emacs to tell their stories. On the one hand, it’s pretty much what you’d expect. The majority of the commenters said it was either Org mode or Magit (or both) that convinced them to move.

The second most popular response was that Evil mode—with or without Doom or Spacemacs—was the selling point. Those users felt that they got all the familiar benefits of Vim along with the power of Emacs.

For me, the most interesting responses where from those users who said that they found Emacs to be faster and easier to use than Vim. That’s a bit of a surprise. It’s received wisdom that Vim is faster and easier to use than Emacs, yet these users found just the opposite.

Oddly, none of these answers apply to me. I migrated from Vim to Emacs before the time of Org mode or Magit. And, although I was a long time Vi/Vim user, I didn’t embrace Evil mode but jumped right into the conventional Emacs milieu.

So what was the attraction? At this point, I don’t really remember. I think it was about the time I started using Lisp and Scheme seriously so it seemed like the Lisp based Emacs was a natural choice. As I’ve written before, I’d made tentative attempts to move to Emacs before but one silly thing or another always stopped me. Suddenly, the universe and I were ready and I moved to Emacs without looking back.

-1:-- Why Did You Switch From Vim To Emacs? (Post Irreal)--L0--C0--2025-06-27T15:21:53.000Z

Charles Choi: Announcing Casual Man & Help

The penchant for contemporary software today to rely on cloud services becomes a malady whenever said services fail. The recent Google Cloud Platform failure two weeks ago was just one such event whose impact was widespread. By no means is this a novel thing as computer historians would rightfully attest. Complex, changing systems will inevitably fail. Sadly, much of the software in production today is not designed to fail gracefully.

Because the web, an overwhelming amount of computer documentation (reference manuals, user guides, tutorials, etc.) is now read from the Internet. This is great until it’s not. Software that packages documentation “local-first” relaxes having to be on-line to read it. So it is with Emacs and Unix-based systems with Man pages.

Observing the above, I announce two more Transient menus for Casual, one for the Unix Man page reader and the other for the built-in Emacs Help system.

By and large these menus are for navigation features but delving into both Man-mode and help-mode, I’ve come across some TILs:

  • Setting the variable Man-switches to the value “-a” will display all the man pages for a specified topic.
    • This is useful if you have multiple variants of the same utility installed. An example is ls where one variant could be GNU and the other BSD.
  • In Man-mode, if the point is on another command with a man page, pressing RET (man-follow) will open its man page in another window, regardless if it is a link.
  • From help-mode, the commands help-goto-info (binding ’i’) and help-goto-lispref-info (binding ’I’, Elisp only) will send you to an Info manual on the current help topic if it is available. (the missing link!)

For Man-mode, I’ve taken the liberty of making an occur-style command using a regexp that looks for a ’-’ or ’+’ command line option. It is bound to ’o’ in the menu casual-man-tmenu.

Both Casual Man & Help are part of the Casual v2.6.0 release on MELPA.

Links

Some earlier posts related to Emacs documentation:

-1:-- Announcing Casual Man & Help (Post Charles Choi)--L0--C0--2025-06-24T22:00:00.000Z

Amit Patel: Emacs: marking text

Although I primarily use Emacs, I love the idea of vim text objects, and wanted to incorporate them into my editing. The Kakoune introduction explains that vim uses verb first, noun second for its commands, whereas Kakoune uses noun first, verb second. I wrote a blog post about why I prefer noun-verb over verb-noun, not only in text editors but also in games and other applications.

I started cobbling together some commands in a hydra, trying to match vim keys when I could:

(defhydra hydra-mark (:body-pre (set-mark-command nil) :color red)
  "
_w_, _,w_: word, symbol    _i'_, _a'_: string  _i)_, _a)_: pair
_t_, _f_: to char (exclude/include)   _0_, _$_: begin/end of line
_;_: comment   _u_: url    _e_: email      _>_: web-mode block or tag
_S_: sexp      _d_: defun  _p_: paragraph  _s_: sentence
_h_, _j_, _k_, _l_: move   _H-._: off
  "
  ("t" mark-to-char-exclusive)
  ("f" mark-to-char-inclusive)
  ("0" move-beginning-of-line)
  ("$" move-end-of-line)
  ("w" er/mark-word)
  (",w" er/mark-symbol)
  ("i'" er/mark-inside-quotes)
  ("a'" er/mark-outside-quotes)
  ("i)" er/mark-inside-pairs)
  ("a)" er/mark-outside-pairs)
  ("i]" er/mark-inside-pairs)
  ("a]" er/mark-outside-pairs)
  ("j" next-line)
  ("k" previous-line)
  ("h" left-char)
  ("l" right-char)
  (";" er/mark-comment)
  ("u" er/mark-url)
  ("e" er/mark-email)
  ("d" er/mark-defun)
  ("S" mark-sexp)
  ("s" mark-end-of-sentence)
  ("p" mark-paragraph)
  (">" web-mode-mark-and-expand)
  ("H-." deactivate-mark :exit t)
   )
(defun my/hydra-mark ()
  (interactive)
  (set-mark-command nil)
  (hydra-mark/body))

(bind-key "H-." #'my/hydra-mark)
And some helper functions:
(defun move-to-char (arg char)
  (interactive (list (prefix-numeric-value current-prefix-arg)
                     (read-char "Move to char: " t)))
  (search-forward (char-to-string char) nil nil arg))

(defun mark-to-char-exclusive (arg char)
  "Mark up to but not including ARGth occurrence of CHAR."
  (interactive (list (prefix-numeric-value current-prefix-arg)
                     (read-char "Mark to char: " t)))
  (set-mark
   (save-excursion
     (move-to-char arg char)
     (backward-char)
     (point))))

(defun mark-to-char-inclusive (arg char)
  "Mark up to and including ARGth occurrence of CHAR."
  (interactive (list (prefix-numeric-value current-prefix-arg)
                     (read-char "Mark to char: " t)))
  (set-mark
   (save-excursion
     (move-to-char arg char)
     (point))))

(use-package expand-region) ;; for the er/* commands

I've been using this since 2017. It's now 2022. How did it go?

Old habits are hard to break. I used this for urls, words, strings, almost none of the others. It's easier for me to move the cursor manually than to learn the specific commands, unless the command is something I use often.

So I'm going to call this experiment complete. I learned that it it's not going to work for me. That's ok. I try lots of things and most don't work. Some do, which is why I keep trying.

I still think the idea is good but I have 30 years of emacs muscle memory to fight.

I considered switching to one of these:

  • mark-thing-at lets you define keys for marking each type of thin
  • objed lets you work on text objects, inspired by vim and kakoune
  • expand-region will guess the object instead of making me choose

I decided I'll remove my experiment code and try expand-region next. [update 2025-05: gave up on expand-region in general, as I only use mark word or url, so I wrote something specific to that.]

-1:-- Emacs: marking text (Post Amit Patel)--L0--C0--2025-06-24T21:48:08.272Z

Rahul Juliato: Setting up Emacs native tab-bar and tab-bar-groups for a tmux-like experience

Explore how to turn Emacs' native tab-bar and tab-bar-groups into a powerful, tmux-like window and session management experience—no external packages needed. Organize your workflows with tabs, group them by project or context, and navigate with ease inside your Emacs session, all while keeping tmux nearby for when it still shines.

Here I'm traversing an open session using this concept of organization by simply issuing C-TAB.

tab-bar-config-demo

If you prefer not to show the group name, want to display buffer names, use other custom decorations, jump right into your group, don’t worry, we’ll explore all these possibilities step by step.

Now, how do we achieve this! 🤩


Motivation

It’s no secret that many Emacs users take advantage of its excellent window management capabilities, like: splitting windows, saving layouts, undoing and redoing them and even use tab-bar as a sort of tmux-like workflow.

What I’m presenting here takes it a step further: bringing in a "split by session" feature, just like tmux UI. In other words, we’re expanding our window management arsenal with:

➖ Tabs, as in Emacs we call it tab-bar (not to be confused with the VSCode-like tab-line mode): which can hold splits of different buffers, either in the same file, different files, terminals, and everything else Emacs can print in a buffer.

➖ Tab Groups, which can hold groups of tabs, mimicking sessions as we call them in tmux, or perspectives if you know this concept from persp-mode or perspective-el, or even activities if you use Doom Emacs.

Also, did I mention we're going to do it without any external package dependencies?

With the provided configuration, we're going to organize our current running Emacs session in "two levels":

The 'tab-bar-groups'

This level holds the tab-group. This might contain a "topic" like "Chat", "Mail" or "News", or simply your project name like "My Project", or if you're working with multiple projects at the same time, one level that might be organized by "Your Workflow". And of course, you can have all of this at the same time, like:

tab-bar-groups

The 'tab-bars'

This level contains your tabs, which can hold all sorts of window arrangements (for the uninitiated, from Emacs's point of view, the OS- level 'window' that holds the Emacs application is called a 'frame', while 'windows' are the inner splits that hold our buffers).

tab-bar-groups-group


So, first things first. I'm reproducing here the steps to the final form I just showed. But of course, it is all customizable. Want to do another sort of decorations? Want to hide the group name? Want to show filenames? Want to navigate differently? Go for it! It is all transparent to you!

Variables configurations

This is personal taste, take a look at each variable's documentation and tweak it yourself, basically:

➖ I do not want the close button, nor the new button, as I seldom use mouse navigation.

➖ I do want tab-hints, which are numbers on each tab-name for better navigation. I do override the internal function, though, to get it "decorated" my way.

➖ I want a clean separator, so, a single space.

➖ We want the tab-group name shown, hence we add to tab-bar-format the tab-bar-format-tabs-groups option.

All of this can be defined with:

(setq tab-bar-close-button-show nil)
  (setq tab-bar-new-button-show nil)
  (setq tab-bar-tab-hints t)
  (setq tab-bar-auto-width nil)
  (setq tab-bar-separator " ")
  (setq tab-bar-format '(tab-bar-format-tabs-groups
					tab-bar-format-tabs tab-bar-separator
					tab-bar-format-add-tab))

A few (IMHO justified) overrides

Tab bar doesn't allow us many customizations. Fortunately, we can override a couple of functions as they're small and easy to keep up with. Of course, this is totally optional; I'm just trying to mimic a more tmux-like UI feel.

First, tab-bar-tab-name-format-hints: I want to put some arrows around the hints number, and NOT show the buffer name.

(defun tab-bar-tab-name-format-hints (name _tab i)
	  (if tab-bar-tab-hints (concat (format "»%d«" i) "") name))

Second, tab-bar-tab-group-format-default: By default, groups show the hint of the first tab under it. I want a clean group name, so:

(defun tab-bar-tab-group-format-default (tab _i &optional current-p)
	(propertize
	 (concat (funcall tab-bar-tab-group-function tab))
	 'face (if current-p 'tab-bar-tab-group-current 'tab-bar-tab-group-inactive)))

Nice QoL Utility functions

With the above config, we can already do something like C-x t G, setting a group name for your current tab and start organizing your life!

You could also have automatically groups created by setting tab-group in your display-buffer-alist, like:

(add-to-list 'display-buffer-alist
			   '("\\*scratch\\*"
				 (display-buffer-in-tab display-buffer-full-frame)
				 (tab-group . "[EMACS]")))

We're not focusing on automatically tab-grouping stuff in this post though.

Truth is, yes, I want groups for my News, Mail, Chat, but most of my work is done in the form of Projects.

And yes, I want these settings to be manually issued. I can recall the pain of having to sneak-peak another project utility function or doc, just to have my crazy custom persp-mode pulling a new persp and messing with everything.

Function to set tab to group based on project

So, I want a function that can "promote" my current tab to the group [ProjectName], creating it if there are none. Of course, if the current buffer is part of a project. This allows me to switch projects, open new splits, without automagic jumps.

Here we have a function to do so, and a suggested bind:

(defun emacs-solo/tab-group-from-project ()
	"Call `tab-group` with the current project name as the group."
	(interactive)
	(when-let* ((proj (project-current))
				(name (file-name-nondirectory
					   (directory-file-name (project-root proj)))))
	  (tab-group (format "[%s]" name))))

  (global-set-key (kbd "C-x t P") #'emacs-solo/tab-group-from-project)

So, recap: I can C-x t G and "add" my tab to a group, and now I can also simply C-x t P and "add" my tab to the project group.

😎 Workflow?

C-x t p p: starts a new tab selecting a project

➖ Select a file, dired or shell...

C-x t P: add your new tab to the project group, creating it

Want some more tabs?

C-x t 2 will automatically add tabs to your current group.

Isn't it nice? Now, you can feel the power in your hands, you open 10 projects, you create a bunch of groups for your inner Emacs is my OS workflow, how do you traverse all this madness?

Function to jump to group

I found my self abusing of the default C-TAB and C-S-TAB to quickly "jump" between closer tabs. Now, I wanna quickly check my Mail, I'd like something more "precise" jumping than eye balling everything.

This is were our second utility function comes to hand:

(defun emacs-solo/tab-switch-to-group ()
  "Prompt for a tab group and switch to its first tab.
Uses position instead of index field."
  (interactive)
  (let* ((tabs (funcall tab-bar-tabs-function)))
	(let* ((groups (delete-dups (mapcar (lambda (tab)
										  (funcall tab-bar-tab-group-function tab))
										tabs)))
		   (group (completing-read "Switch to group: " groups nil t)))
	  (let ((i 1) (found nil))
		(dolist (tab tabs)
		  (let ((tab-group (funcall tab-bar-tab-group-function tab)))
			(when (and (not found)
					   (string= tab-group group))
			  (setq found t)
			  (tab-bar-select-tab i)))
		  (setq i (1+ i)))))))
  (global-set-key (kbd "C-x t g") #'emacs-solo/tab-switch-to-group)

This allows us to "list all available groups", select and switch to the first tab of that group.

tab-bar-group-change

Packing the entire config

The code here presented by parts is now part of my emacs-solo config (hence the prefix on the function names), I usually keep my configuration somewhat organized by use-package blocks, they keep everything in the right place and I suggest you do the same. Also it is a lot faster to grab this code, copy and paste to your config and make it work!

(use-package tab-bar
  :ensure nil
  :defer t
  :custom
  (tab-bar-close-button-show nil)
  (tab-bar-new-button-show nil)
  (tab-bar-tab-hints t)
  (tab-bar-auto-width nil)
  (tab-bar-separator " ")
  (tab-bar-format '(tab-bar-format-tabs-groups
					Tab-bar-format-tabs tab-bar-separator
					tab-bar-format-add-tab))
  :init
  ;;; --- OPTIONAL INTERNAL FN OVERRIDES TO DECORATE NAMES
  (defun tab-bar-tab-name-format-hints (name _tab i)
	  (if tab-bar-tab-hints (concat (format "»%d«" i) "") name))

  (defun tab-bar-tab-group-format-default (tab _i &optional current-p)
	(propertize
	 (concat (funcall tab-bar-tab-group-function tab))
	 'face (if current-p 'tab-bar-tab-group-current 'tab-bar-tab-group-inactive)))


  ;;; --- UTILITIES FUNCTIONS
  (defun emacs-solo/tab-group-from-project ()
	"Call `tab-group` with the current project name as the group."
	(interactive)
	(when-let* ((proj (project-current))
				(name (file-name-nondirectory
					   (directory-file-name (project-root proj)))))
	  (tab-group (format "[%s]" name))))

  (defun emacs-solo/tab-switch-to-group ()
  "Prompt for a tab group and switch to its first tab.
Uses position instead of index field."
  (interactive)
  (let* ((tabs (funcall tab-bar-tabs-function)))
	(let* ((groups (delete-dups (mapcar (lambda (tab)
										  (funcall tab-bar-tab-group-function tab))
										tabs)))
		   (group (completing-read "Switch to group: " groups nil t)))
	  (let ((i 1) (found nil))
		(dolist (tab tabs)
		  (let ((tab-group (funcall tab-bar-tab-group-function tab)))
			(when (and (not found)
					   (string= tab-group group))
			  (setq found t)
			  (tab-bar-select-tab i)))
		  (setq i (1+ i)))))))

  ;;; --- EXTRA KEYBINDINGS
  (global-set-key (kbd "C-x t P") #'emacs-solo/tab-group-from-project)
  (global-set-key (kbd "C-x t g") #'emacs-solo/tab-switch-to-group)

  ;;; --- TURNS ON BY DEFAULT
  (tab-bar-mode 1))

Customizations on tab-bar-properties

You might want to customize the tab-bar line, what I am using in these screenshots is:

(custom-set-faces
  '(tab-bar
	((t (:background "#232635" :foreground "#A6Accd"))))
  '(tab-bar-tab
	((t (:background "#232635" :underline t))))
  '(tab-bar-tab-inactive
	((t ( ;; :background "#232635" ;; uncomment to use this
		  ;; :box (:line-width 1 :color "#676E95")
		  ))))
  '(tab-bar-tab-group-current
	((t (:background "#232635" :foreground "#A6Accd" :underline t))))
  '(tab-bar-tab-group-inactive
	((t (:background "#232635" :foreground "#777")))))

So, time to ditch tmux?

I wish...

This functionality is indeed very useful, the UI mimics tmux-like power. And if this is enough for you, go for it! Ditch tmux!

For my use cases, the sheer possibility of any of my emacs-lisp code locking the one and only Emacs process means my beautifully designed and crafted Emacs session is going bye-bye with it. And yes, while emacs --daemon and restarting clients helps a lot here, let’s not pretend Emacs never goes sideways.

There are still solid reasons to keep tmux around:

Fault tolerance. When you’re SSH’d into a remote machine and something crashes, tmux is still there, your shell lives on. Emacs tabs don’t protect you from network drops or X11/Wayland hiccups.

Shell multiplexing. Sometimes you just want 3 quick shells, nothing fancy, don’t even want to boot up Emacs. tmux wins here. Fast, lightweight, and scriptable. You just install tmux, no fancy config needed to 'just use it'.

System-level process separation. I like to keep long-running REPLs, tailing logs, or even a docker attach session in tmux. If Emacs dies, they don’t.

Startup time. Emacs with heavy configuration can still take a second to feel "fully alive". When I want to attach to a ready-to-go shell session instantly, tmux a is just faster.

Better separation. While the whole tab-bar and tab-group approach is super flexible, sometimes you just need the hard boundary of a terminal session completely isolated from the rest. There are things you do outside Emacs for good reason.

And let’s be honest, you don’t need to choose. These tools complement each other. What this configuration gives you is a powerful Emacs-as-an-OS experience, with clarity, agility, and a clean mental model. Use Emacs for your inner workflows, and tmux as your outer shell guardian.


Wrapping Up

With just a few lines of Elisp, no external packages, and some clever overriding, Emacs’ tab-bar and tab-bar-groups become serious productivity tools. If you’re someone juggling multiple projects, workflows, or simply enjoy clean organization inside your Emacs session, this setup gives you control and clarity.

While we might not throw tmux out of the toolbox just yet, we now have a native Emacs experience that feels modern, fast, and surprisingly intuitive. Use what’s best for your workflow, but know that Emacs is more than capable of stepping up its game.

So go ahead, give it a try, tweak it, theme it, and make Emacs your tmux... and more.

Happy hacking. ✨💻🤓🚀

-1:-- Setting up Emacs native tab-bar and tab-bar-groups for a tmux-like experience (Post Rahul Juliato)--L0--C0--2025-06-24T20:00:00.000Z

Sacha Chua: 2025-06-23 Emacs news

[2025-06-25 Wed]: Removed reposted video.

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 comment on Mastodon or e-mail me at sacha@sachachua.com.

-1:-- 2025-06-23 Emacs news (Post Sacha Chua)--L0--C0--2025-06-23T13:52:49.000Z

Marcin Borkowski: Making functions interactive

I have to admit that I probably have a bit of OCD, and when I finish my work, I like to put my Emacs in a sort of “clean slate” state. By that I don’t mean closing it or killing all buffers (although when I leave the office, I do kill all work-related buffers). Instead, I mean going back to displaying just one window with the *GNU Emacs* buffer (the one with the logo and links to the tutorial etc.) The problem is, I sometimes accidentally kill that buffer, and then I have no place to go back to;-). Well, some time ago it occurred to me that something in Emacs must create that buffer, and I can find that something and learn how to do that whenever I want.
-1:-- Making functions interactive (Post Marcin Borkowski)--L0--C0--2025-06-23T06:20:20.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!