Philip Kaludercic: A retrospective on “setup”
-1:-- A retrospective on “setup” (Post Philip Kaludercic)--L0--C0--2026-02-07T17:25:23.000Z
-1:-- A retrospective on “setup” (Post Philip Kaludercic)--L0--C0--2026-02-07T17:25:23.000Z
Jacob Boxerman has a nice video and related blog post about his approach to configuring Emacs in a declarative and reproducible way. By “reproducible” he means that if he installs his init.el on a new system, he gets the exact same Emacs environment. That doesn’t mean just things like colors and key bindings but also that the same versions of the same packages are loaded and available.
By “declarative” he means, essentially, that he uses use-package and straight.el to specify and configure his packages in a declarative way. The goal is that the order of the declarations doesn’t matter and if one declaration s deleted, no other package is affected.
The video is a tour through a sample configuration based on Boxerman’s that shows how he accomplishes these things. The declarative part is mostly handled by use-package while the reproducible part depends mostly on straight’s ability to lock a file to a specific version. Take a look at the video and associated blog post for the details.
If, like me, you don’t have multiple machines that you require to provide exactly the same Emacs environment, you can probably omit straight.el but the idea of putting all your configuration in use-package blocks is a good one. It goes a long way towards ensuring that changes in one area won’t affect things in another.
Boxerman describes an excellent way of keeping your Emacs configuration under control and avoiding problems when—inevitably—you have to make changes. The video is only 8 minutes, 23 seconds so it’s easy to find time for it. If you find his ideas useful, take a look at his post, which explores them in a bit more detail.
-1:-- Declarative, Reproducible Emacs (Post Irreal)--L0--C0--2026-02-07T15:55:27.000Z
Just a quickie today. Jack Baty has a short post on how he enables Org capture from anywhere on his system. He does pretty much what I do except that I use yequake instead of his custom script.
Yequake probably handles the edge cases but either way you simply evoke Emacs and ask it to run a bit of custom Elisp. If you aren’t doing something like this, you should take a look at Baty’s post or Alphapappa’s yequake. No matter where you are, you can bring up your capture menu and bring notes or a browser link into Emacs. I use it many times a day, especially for capturing items that I might want to write about in Irreal.
This is just another example of how Emacs can ease your workflow. You simply pop up an Emacs frame, capture you note, and deal with it later. It’s about as easy a way of capturing a quick note without breaking you out of the flow as you can get. If Emacs is at the center of your workflow, you should definitely check out Baty’s post or Alphapappa’s yequake.
-1:-- Org Capture From Anywhere (Post Irreal)--L0--C0--2026-02-06T15:06:30.000Z
Contributing to Git Projects with Magit: PRs, Patches & Agit workflow
Quick video guide on contributing to git projects using Magit. Showing an overview of the 3 most commonly used workflows
Pull Requests
Agit Workflow
Git patches
Video notes:
I’m creating this as a tutorial video on how to contribute to bebliotheca, a digital recreation of the Library of Constantinople to host texts that would have been found there during the Greco-Roman Empire.
The steps demonstrated here are applicable to any project.
This is a video guide on contributing to git projects, using Emacs and
Magit (the magical Git client). Magit provides the best
experience out of the box for a text based interface to git, which
makes magit an emacs’ killer feature.
In this video we will start from an example Emacs configuration, adding a theme and installing Magit. Afterwards we will see how to contribute to git projects in 3 ways:
codebergI personally use
vc, a built-in module. But that’s because I enjoy tinkering and writing my own scripts for it. Magit does by default a lot more that even my ~300 line vc config can do.
You will need the following software installed:
Additionally, ensure you have a pair of SSH keys already generated and set up for your forge.
Example init.el file that I will be using:
init.el, depending on your system:
~/.emacs.d/init.elC:\Users\<YourUsername>\.emacs.d\init.el
;; Init packages
(package-initialize)
;;;; Do not install keycast as well, I'm only using it to share my
;;;; keystrokes for the video.
;; (use-package keycast
;; :ensure t)
;; Set and load custom.el
(setf custom-file (locate-user-emacs-file "custom.el"))
(load custom-file 'noerror)
;; Completions
(fido-vertical-mode 1)
(electric-pair-mode 1)
;;; Theming ;;;
(column-number-mode)
(global-display-line-numbers-mode 1)
(tool-bar-mode -1)
(tooltip-mode -1)
(menu-bar-mode -1)
(blink-cursor-mode -1)
(scroll-bar-mode -1)
(set-fringe-mode -1)
;; Add transparency
(add-to-list 'default-frame-alist '(alpha-background . 85))
;; ef-themes package provides a great theme collection, made by Protesilaos.
(use-package ef-themes
:ensure t)
(defun thanos/load-theme (&optional theme)
"Disable current theme and load a new THEME.
Emacs can load multiple themes at once. With this function we make sure we only load one at a time"
(interactive)
(let ((theme (or theme (intern (completing-read "Theme: " (custom-available-themes))))))
(disable-theme (car custom-enabled-themes))
(load-theme theme t)))
(thanos/load-theme 'ef-dark)
(use-package magit
:bind ("C-x g" . magit)
;; Hook flyspell for autocorrect during commit messages.
:hook (git-commit-mode . flyspell-mode))
;; Agitjo extends Magit with a new menu for AGit-Flow operations.
(use-package agitjo
:vc (:url "https://codeberg.org/halvin/agitjo")
:ensure t)
If you’d rather not use a Web UI, but have to use Pull Requests, consider using forge.
Note that the agit workflow is not supported by GitHub at the time of making this video. To use agit workflow, you will need to utilize a forgejo instance such as Codeberg.
#-t
$ git format-patch -{NUM} # where NUM the number of commits.
C-x C-m.-1:-- (Video) Contributing to Git Projects with Magit: PRs, Patches & Agit workflow (Post Thanos Apollo)--L0--C0--2026-02-05T22:00:00.000Z
Denote includes the option of adding different directories, so this now makes sense:
(setq denote-directory
(if (eq system-type 'gnu/linux) ; If I'm using Linux, include the private folder.
(list (expand-file-name "~/Sync/Notes")
(expand-file-name "~/Documents/private"))
(list (expand-file-name "~/Sync/Notes")))) ; If I'm using anything else (macOS) just Notes.
(setq denote-excluded-directories-regexp "data") ; the data folder (with attachments) is excluded.
I have a private folder for Denote notes on Linux only. I used to only sync my Sync/Notes folder, which includes my informational notes and blog posts, but that means I’m missing out on Denote’s abilities in my private folder, which is slowly increasing in size. This solves this problem.
There’s no need to list the data folder (which includes my attachments, mostly in file formats that are not Denote’s format), so that’s why the exclusion is there.
-1:-- (Post TAONAW - Emacs and Org Mode)--L0--C0--2026-02-05T21:06:20.000Z
I should be taking more photos. One roll of film for the entire month of January is embarrassing. I have a new Leica Q2 to play with, and I’ve taken maybe 20 images with it so far. What’s up with that?
I’m bored with all the performative quitting going on. What do you want, a medal?
Later: I don’t think Dave cared for my comment about being bored. Fair enough. Sorry, Dave, you’re excluded of course :). I wasn’t “sub-posting” you or anyone else in particular. And I’m not bored with people quitting things that don’t line up with their ethics or anything else. That’s excellent. I’m bored with people talking about it in that particularly smug tone that social media brings out in people. Frankly, I’m so angry about just about everything happening righ now I can hardly think. I’ll exercise my privilege by being bored by the tiresome discourse around so much of it.
Come to think of it, I’m bored by just about everything. I seem to have only two reactions to everything: anger or boredom.
Took the M3 loaded with a roll of HP5 over to my grandson’s this morning. Shot the roll at ISO1600. Nothing very good to show for it, but at least I did something.
-1:-- Thursday, February 05, 2026 (Post Jack Baty)--L0--C0--2026-02-05T11:00:24.000Z
I like being able to create new Org mode tasks from anywhere, via a simple keyboard shortcut. This probably doesn’t justify a whole post, but here’s how I did it.
Create a script at ~/.local/bin/orgcapture.sh
Here’s the script:
#!/bin/bash
emacsclient -c -F '((name . "capture") (width . 80) (height . 34))' -e '(progn (org-capture) (delete-other-windows))'
In my Emacs config, I have a hook that tidies up the new frame:
(defun my/org-capture-finalize-hook ()
"Close frame after org-capture if it was opened for capture."
(when (and (> (length (frame-list)) 1) ; More than one frame
(frame-parameter nil 'client)) ; Frame created by emacsclient
(delete-frame)))
(add-hook 'org-capture-after-finalize-hook 'my/org-capture-finalize-hook)
In KDE’s Settings, I added a new command in the Shortcuts settings that point to that script. I assigned it to F3.
Now I hit F3 any time and a small capture buffer pops up in a new frame. That’s it. If there’s a better method for doing this, let me know.
-1:-- Global org-capture shortcut in KDE (Post Jack Baty)--L0--C0--2026-02-05T10:29:52.000Z
I have added two new themes to my minimalist doric-themes package.
The collection is growing to cover styles that range from austere to
playful.
doric-jade is a light theme with an emphais on green. doric-copper
is a dark theme with mostly orange accents and hints of patina. Both
themes retain the Doric quality of using few colours while relying on
typography to establish rhythm and structure.
Below are their samples.
[ Or just check all the pictures: https://protesilaos.com/emacs/doric-themes-pictures. ]
doric-themes version 0.7.0Both doric-jade and doric-copper are in-development. I have
already worked on their finer point, but may still make some tweaks
before publishing them as part of the next stable version of the
doric-themes.
The Doric themes use few colours and will appear monochromatic in many contexts. They are my most minimalist themes. Styles involve the careful use of typographic features and subtleties in colour gradients to establish a consistent rhythm. Legibility is still high.
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.
doric-themes-1:-- Emacs: new Doric themes ‘doric-jade’ and ‘doric-copper’ (Post Protesilaos Stavrou)--L0--C0--2026-02-05T00:00:00.000Z
I’ve had a little free time recently (figuring out this baby stuff!) and thought I would spend time revisiting and refining my AI assistant ollama-buddy
I’ve been playing around with agentic coding and keeping up-to-date on the rapid development of the Emacs AI package landscape and I think I have refined in my own mind my idea of what I would like to see in an Emacs AI assistant.
The headline change regarding the latest release of ollama-buddy is GitHub Copilot integration; the rest of the work is about smoothing the UI and simplifying day-to-day use.
What’s new - the Copilot addition (v1.2)
(use-package ollama-buddy
:bind
("C-c o" . ollama-buddy-menu)
("C-c O" . ollama-buddy-transient-menu-wrapper)
:config
(require 'ollama-buddy-copilot nil t))
Other notable updates in this release series
-1:-- Spent a bit of free time polishing ollama-buddy - github Copilot is now onboard! (Post James Dyer)--L0--C0--2026-02-04T10:40:00.000Z
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”.
beframeBelow are the release notes
This version fixes two bugs and makes other minor tweaks.
The first bug pertains to the performance of the command
beframe-switch-buffer or the command switch-to-buffer when
beframe-mode is enabled: they were really slow when the list of
buffers was long. Now they are always fast. Thanks to Alexandre
Rousseau for reporting the problem in issue 17:
https://github.com/protesilaos/beframe/issues/17.
The second bug is more subtle. It is about persisting the completion
metadata category value in all prompts that read a buffer, when
beframe-mode is enabled. This change means that users who configure
the user option completion-category-overrides will not get the
expected results in buffer prompts affected by Beframe.
Thanks to Stefan Monnier for commenting on the initial implementation,
specifically telling me that let binding the metadata can affect
nested minibuffers, which we do not want. This was done on the
emacs-devel mailing list: https://lists.gnu.org/archive/html/emacs-devel/2025-12/msg00264.html.
-1:-- Emacs: beframe version 1.5.0 (Post Protesilaos Stavrou)--L0--C0--2026-02-04T00:00:00.000Z
My laptop has been running Fedora’s KDE spin. I first tried the stock Fedora with Gnome, but since my desktop was running KDE, I figured I should standardize on that.
Standardization is boring. :)
I installed Fedora Workstation this morning. I’d taken notes from the last time, and those helped speed things along. Still, it was 2 hours from installer to a fully functional system. I’m typing this post in Emacs and will deploy using Hugo shortly.
For the record, here are my raw notes from the installation. I need to work on the order in which I do things, but this wasn’t bad.
- sudo dnf install syncthing
- Settings
- Trackpad -> Disable tap to click
- Install Gnome Tweaks
- set caps lock to control
- Emacs Input on
- sudo dnf install -y stow just fzf zoxide ripgrep
- Install Extension Manager (Flatpak)
- Installed Dash to Dock extension (via Extension Manager)
- Installed Clipboard Indicator extension (via Extension Manager)
- Installed starship curl -sS https://starship.rs/install.sh | sh
- stow bash
- stow pandoc
- sudo dnf install pandoc
- sudo dnf install texlive-scheme-full
- sudo dnf install neovim
- sudo dnf install rust cargo # (for eza, since eza is no longer in fedora repos)
- Build and install eza
git clone https://github.com/eza-community/eza.git
cd eza/
cargo install --path .
- Add berkely-mono to ~/.local/share/fonts/berkeley-mono
- sudo dnf copr enable dejan/lazygit
- sudo dnf install lazygit
- Install Signal (Flatpak)
- stow auth
- stow git
- stow gnupg
- stow jrnl
- stow pass
- stow ssh
- stow aerc
- sudo dnf install aerc
- sudo dnf install emacs
- Configure emacs
- git clone https://github.com/jamescherti/minimal-emacs.d.git ~/.config/emacs
- git clone [my dotemacs repo] to ~/.config/emacs-mine
- cp ~/.config/emacs-mine/pre-early-init.el ~/.config/emacs/
- ln -s ~/Sync/emacs/manual-packages ~/.config/emacs-mine/
- sudo dnf install fastfetch
- Add "Start Syncthing" to Startup Applications in Gnome Tweaks app
- Install FireCode Nerd Font
- ...a nearly infinite number of little tweaks that I didn't record.
Since the laptop is meant to be a sort of satellite computer orbiting my desktop Mac Mini, I don’t need everything installed. The above covers just the basics.
One day I should write a script that takes care of this for me.
-1:-- Installing Fedora Workstation on the laptop (Post Jack Baty)--L0--C0--2026-02-03T17:01:27.000Z
A couple of weeks ago, I wrote about how Punchagan over at NOETIC NOUGHT started saving his Elfeed database after he lost it in a system crash. At the time, Punchagan didn’t know exactly what happened, only that the system crashed and he lost his Elfeed database. He wasn’t even sure that the two events were related, only that they occurred at the same time. His solution to the problem was to set up periodic saving of his Elfeed data to git.
A little later he had some time to troubleshoot the problem and discovered what happened. The Elfeed database is basically a hash table and saving it is simply a matter of dumping the binary to a file. This happens inside a with-temp-file macro, which is much like the more familiar with-temp-buffer macro except that it saves the results to a file at the end. The saving is done with write-region, which first truncates any existing file.
What happened, apparently, is that the system crashed between the truncation of the old file and writing the new file. Sadly there’s not much that can be done at the user level other than saving the file periodically.
I liked this post because it shows how easy it is to simply follow the code—which is, of course, available whenever you’re running Emacs—to see what’s happening. Emacs, to be sure, has more sophisticated tools for debugging but often simply looking at the code will reveal what’s going on. Punchagan’s post reminds me of Sacha Chua’s post on figuring out how to edit an SVG file and its source at the same time. She figured out how to do this the same way as Punchagan solved his problem: by reading the source code to see where the undesirable behavior was happening.
-1:-- How Punchagan Lost His Elfeed Database (Post Irreal)--L0--C0--2026-02-03T15:29:45.000Z
Links from reddit.com/r/emacs, r/orgmode, r/spacemacs, Mastodon #emacs, Bluesky #emacs, Hacker News, lobste.rs, programming.dev, lemmy.world, lemmy.ml, planet.emacslife.com, YouTube, the Emacs NEWS file, Emacs Calendar, and emacs-devel. Thanks to Andrés Ramírez for emacs-devel links. Do you have an Emacs-related link or announcement? Please e-mail me at sacha@sachachua.com. Thank you!
You can comment on Mastodon or e-mail me at sacha@sachachua.com.
-1:-- 2026-02-02 Emacs news (Post Sacha Chua)--L0--C0--2026-02-02T20:03:41.000Z
JR over at The Art Of Not Asking Why has a useful post on exporting an Org document to Docx. I’ve written about this before [1, 2, 3] and you might wonder why. After all, you can simply export directly from Org to ODT and get a Word compatible document.
The problem occurs when you’re working in an environment that requires a specific style implemented with a Word style sheet. The native Org exporter doesn’t support this so the usual solution is to use Pandoc, which does support a Word style sheet.
JTR’s post has a step-by-step recipe for making the conversion and setting up a Word style sheet using a reference document with the desired settings. He even covers the difficult areas of images and tables, which, of course, require further machinations.
None of this would be necessary if Word and its siblings would use plain text instead of an arcane, opaque file structure to store the document. After all, it’s certainly possible as Org—or, if you require even more complicated output, TeX/LaTeX—show. But, of course, that would stand in the way of editor lock in.
Regardless, if you have to produce Word documents but prefer to write in Org, take a look at JTR’s post. He shows you how to produce a good looking Word document from an easy to write Org file. He even shows how to automate the process using Álvaro Ramírez’s dwim-shell-command.
-1:-- Exporting From Org To Docx (Post Irreal)--L0--C0--2026-02-02T16:15:51.000Z
-1:-- git-link (Post Marcin Borkowski)--L0--C0--2026-02-02T06:00:38.000Z
When it comes to sharing written documents with my co-workers, I usually need to use Word or PDFs. Org-mode includes an export option to odt files, which does the job in a pinch, but it’s a bit harder to customize, especially if I want to preserve certain formatting elements. In the past, I used to create ODT files, import them into Word, make whatever changes I needed, and save them in a docx file. The problem there, besides the manual work, is consistency. If I want to make sure I use the same style for fonts, header sizes, and table formats, things get annoying fast.
About a year ago, I discovered that Pandoc can use an external Word document (docx) as a style template. As long as I have this file accessible, every org file I convert to docx will automatically (and beautifully, I may add) retain the exact style changes I need. This, of course, can be automated later inside Emacs, so the export process is basically seamless.
The problem lies in the fact that you need to work with Word and understand how its styles. Readers of this blog know I love to complain about Microsoft’s UI and its lack of consistency, and the instructions here are one fine example in my opinion. Be it as it may, I will try to keep the snarky comments in check.
The first thing to do is get Pandoc to create its default style reference document as a .docx. This can be done with pandoc -o custom-reference.docx --print-default-data-file reference.docx.
What you’re telling Pandoc here is to output (-o) a file named custom-reference.docx (you can call it whatever you’d like, as long as it’s a docx, since you want to work with Word). What to output goes into this file? The default reference document.
Once you do that, you will have a custom-reference.docx in the folder you ran the command from.
Now it’s time to work inside Word.
What you will see is a rather plain file listing a bunch of styles and their name. First, you will see “Title,” and it will be nice and big because it’s using the Title style in Word. Heading 1 will use the Heading 1 style, Body Text (further down) will use the Body Text style, and so on.
What you want to do is to access these styles in Word. You will be modifying them to fit to your liking.
To do that, search for the Styles Pane. In my case, Office 365 for macOS, I can find it in the ribbon under Home, but I wouldn’t be surprised if that’s a little different if you’re using Windows, or a different version of Word.
With the Style Pane open, you will see a long list of the available styles. You can navigate this way, but I find it’s easier to just look for them in the document to the left and select them; it will then show under Current style: to the right.
Now that you have a style selected, select the name of that style from the Style Pane and choose Modify Style…1. You will see the options available to you, such as the font name, text color, alignment, etc.
Make sure that you do not change the Name: of the style! You’re modifying existing default styles that Pandoc recognizes; if you create a new one (by giving it a new name), Pandoc will not know what to do with it and will ignore it. Remember, you are modifying a reference sheet of existing styles, not creating new styles. Likewise, this means that you have no reason to change anything in the document itself (the content) - Pandoc doesn’t care about that and will ignore it. You’re only touching the options in the Modify Style… window.
That’s basically all there is to it. As you will see soon, some items can get a bit more complicated as they are not available in the same way. Save the files when you’re done modifying the styles. You will probably keep going back to this document as you export docx files, tweaking things until you like them.
The next step is to tell Pandoc to convert an existing org file to a docx file using the reference sheet we just worked on. To do that, use the following command:
pandoc -s <input_file.org> -o <output_file.docx> --reference-doc custom-reference.docx.
The idea is the same as before, but we use a stationary option, -s (the reason for that is that we’re telling Pandoc to use a “full” version of a document, needed with the reference doc option, otherwise it can’t use our reference document).
Then, we’re pointing Pandoc to our org file and using the output, -o, as the doc file we want, then finally using the reference-doc option by utilizing our reference file, custom-reference.docx (unless you change the name to something else). That’s it, Pandoc will create the file in the folder you are in.
For me, things get a bit more complicated. My technical documents often include screenshots, and I need those embedded in the resulting Word docx file.
The above command will embed images in the Word document, as long as org-mode can find and display your images (using the inlineimages option in org-mode). My org-mode technical notes are all written with Denote these days, which keeps everything organized, including the images, which are inside the default data subfolder. If your workflow is different, be mindful of your path to the images; you’d probably want to use an absolute path in org-mode (use C-u C-u C-c C-l when linking an image instead of the usual C-u C-c C-l for this). There’s also the option to extract media in Pandoc (see the –extract-media option). In my opinion, it’s simpler and cleaner to dedicate a folder for your org documents, completed with a subfolder for attachments.
Another issue that stumbled me was the tables.
Pandoc’s default style for tables is minimal - without borders. This might look nice if you want to hide your tables, but if you need a table with borders and perhaps some shading, you’ll run into issues.
The problem is with Word. The style for the table is not included in the Style Pane. In order to change the style for your table in the reference doc, you need to locate Table Design in your ribbon. Notice that if you work on a small screen (like in my recording), you might have to expand your options to find it. Then, you will have to extend the pane further and locate the Modify Table Style below. Once again, this is true for my case, Office 365 on macOS. If you’re using a different Office version or on Windows, this menu might be located elsewhere.
Once you have Modify Table Style open, you can change the borders and shading to your heart’s content, but as before, make sure the style name remains Table. If you create a new style, Pandoc will ignore it.
Under the name of the style, in the Modify Style or Modify Table Style window, you will see the Style based on option. You can change this option and browse through different colors and fonts to find something you like, and then customize further instead of starting from scratch. As long as you don’t change the name of the style, you’re OK. I found this useful when working with tables, as I was looking for a table with shading on every other row.
Speaking of tables, the border button is the little square icon in the middle. I spent a few moments trying to find it.
If you’re an Emacs user, you like shortcuts, and you like to use shell commands from within Emacs. If you haven’t yet, check out dwim-shell-command.
Here’s how I have it set up. All I have to do is mark the org file(s) I want to convert to a docx, and that’s it. It doesn’t matter if I want to export only one file or fifty; it’s the same ease of use.
(defun jtr/dwim-shell-command-pandoc-org-to-docx ()
"uses pandoc to convert an org file to docx using a template docx file. The docx template file is stored in my Notes sync folder"
(interactive)
(dwim-shell-command-on-marked-files
"converting from org to docx"
"pandoc -s '<<f>>' -o '<<fne>>.docx' --reference-doc ~/Sync/Notes/Info/custom-reference.docx"
:utils "pandoc"))
1 : Or, another way to work is to use to select the text you want to modify, then go to Format and select Style from there; you will need to then choose Modify in the window that shows.
-1:-- Org files to beatiful docx files with Pandoc (Post TAONAW - Emacs and Org Mode)--L0--C0--2026-02-01T14:55:30.000Z
Welcome to 2026, and a new year of Emacs Blog Carnival post!
To start the Gregorian calendar year fresh, the blogging/writing/thinking prompt of this month is:
This year, I’ll …
What will you do differently in Emacs this year? Or will you just get started? Why?
What are you excited about to explore and tinker with? What do you want to perfect?
Are there any large projects in front of you that you’ll emacs1 with confidence?
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.
Comment below or DM/email me with your submission! I’ll collect submissions up to, and including, February 1st (Central European Time), so that every time zone has had a chance to meet the January 31st deadline.
Ordered by submission date:
George Jones: This Year, I’ll – comes with a handy Emacs verb definition:
Emacso, -are, -avi, -atus (verb, 1st conjugation)
emacso, emacsas, emacsat, emacsamus, emacsatis, emacsant
- Cotidie, cum discipulis meis, emacso ad software melius discendum. (Every day, with my students, I emacs to learn better software.)
I’m trying to establish a verb here. ↩
Hire me for freelance macOS/iOS work and consulting.
Buy my apps.
Receive new posts via email.
-1:-- Emacs Carnival 2026-01: “This Year, I’ll ...” (Post Christian Tietze)--L0--C0--2026-02-01T10:41:27.000Z
I've been hoping to write a GUI for my Emacs clone for a while now. And I'm putting together a list of Emacs' display features to keep compatible with when designing the GUI. Unsurprisingly, this list just keeps growing, and, nah,
Waiting to feel ready to do the thing is not doing the thing. 1
So, I really need to start writing the shittiest Emacs GUI ever.
Read more… (10 min remaining to read)
-1:-- This year, I will write a shitty GUI for my Emacs clone (Post Kana)--L0--C0--2026-01-31T21:45:00.000Z
: Fix flycheck-checkers.
After learning about French spellcheck and grammar checking from Emacs expliqué à mes enfants, I added flycheck-grammalecte to my config. Nudged by @lann@mastodon.zaclys.com, I finally got around to figuring out why my setup sometimes worked and sometimes didn't. When I checked flycheck-verify-setup, I noticed that grammalecte kept getting disabled. A little digging around showed me that it was getting disabled because of too many errors. That was because it was trying to work on my whole file instead of just the portion that I narrowed to with org-narrow-to-subtree (ooh, just noticed an org-toggle-narrow-to-subtree command). I like having all of my French journal entries in one file because I can use consult-line (which I've bound to M-g l) to quickly look up examples of where else I've used a word. So I needed to define a checker that runs only on the narrowed part of the buffer.
(defun my-flycheck-grammalecte-buffer (checker callback)
(let* ((temp-file-name (make-temp-file "grammalecte"))
(output-buffer (get-buffer-create temp-file-name))
(buffer (current-buffer))
(cmdline (delq nil `("python3"
,(expand-file-name "flycheck_grammalecte.py"
grammalecte--site-directory)
,(unless flycheck-grammalecte-report-spellcheck "-S")
,(unless flycheck-grammalecte-report-grammar "-G")
,(unless flycheck-grammalecte-report-apos "-A")
,(unless flycheck-grammalecte-report-nbsp "-N")
,(unless flycheck-grammalecte-report-esp "-W")
,(unless flycheck-grammalecte-report-typo "-T")
(option-list "-f" flycheck-grammalecte-filters)
(eval (flycheck-grammalecte--prepare-arg-list
"-f" flycheck-grammalecte-filters-by-mode))
(eval (flycheck-grammalecte--prepare-arg-list
"-b" flycheck-grammalecte-borders-by-mode))
,temp-file-name)))
(args (mapcan (lambda (arg) (flycheck-substitute-argument arg checker)) cmdline))
(command (flycheck--wrap-command (car args) (cdr args))))
(write-region (buffer-string) nil temp-file-name)
(make-process :name "grammalecte"
:buffer output-buffer
:command command
:sentinel
(lambda (process status)
(let ((errors (with-current-buffer (process-buffer process)
(message "%s" (buffer-string))
(flycheck-parse-with-patterns
(buffer-string)
checker
(current-buffer)))))
(delete-file temp-file-name)
(kill-buffer output-buffer)
;; offset
(funcall
callback
'finished
(let ((offset (save-excursion (goto-char (point-min))
(line-number-at-pos nil t))))
(mapcar
(lambda (err)
(let ((new-err (copy-flycheck-error err)))
(setf (cl-struct-slot-value 'flycheck-error 'buffer new-err)
buffer)
(setf (cl-struct-slot-value 'flycheck-error 'line new-err)
(+ (flycheck-error-line new-err)
offset -1))
(setf (cl-struct-slot-value 'flycheck-error '-end-line new-err)
(+ (flycheck-error-end-line new-err)
offset -1))
new-err))
errors))))))))
(defun my-flycheck-grammalecte-setup ()
"Build the flycheck checker, matching your taste."
(interactive)
(unless (grammalecte--version)
(advice-add 'grammalecte-download-grammalecte :after-while
#'flycheck-grammalecte--retry-setup))
(grammalecte--augment-pythonpath-if-needed)
(flycheck-define-generic-checker 'my-grammalecte-narrowed
"Report Grammalecte errors, but only for the narrowed section."
:start #'my-flycheck-grammalecte-buffer
:modes flycheck-grammalecte-enabled-modes
:predicate (lambda ()
(if (functionp flycheck-grammalecte-predicate)
(funcall flycheck-grammalecte-predicate)
t))
:enabled #'grammalecte--version
:verify #'flycheck-grammalecte--verify-setup)
(setf (flycheck-checker-get 'my-grammalecte-narrowed 'error-patterns)
(seq-map (lambda (p)
(cons (flycheck-rx-to-string `(and ,@(cdr p))
'no-group)
(car p)))
flycheck-grammalecte--error-patterns))
(add-to-list 'flycheck-checkers 'my-grammalecte-narrowed)
(flycheck-grammalecte--patch-flycheck-mode-map))
After I use my-flycheck-grammalecte-setup, I can use flycheck-select-checker to select my-grammalecte-narrowed and then use flycheck-buffer to run it. Then it will underline all the number/gender agreement issues I usually have. It's nice that I can practise editing my text with this script before I run the text through an LLM (also via flycheck) for feedback on wording.
You can e-mail me at sacha@sachachua.com.
-1:-- Emacs and French: Focus flycheck-grammalecte on the narrowed part of the buffer (Post Sacha Chua)--L0--C0--2026-01-31T03:22:49.000Z
A bit of a teaser:
This bit of dwim-shell-command magic works nicely:
(defun jtr/dwim-shell-command-pandoc-org-to-docx ()
"uses pandoc to convert an org file to docx using a template docx file. The docx template file is stored in my synced notes folder"
(interactive)
(dwim-shell-command-on-marked-files
"converting from org to docx"
"pandoc -s '<<f>>' -o '<<fne>>.docx' --reference-doc ~/Sync/Notes/custom-reference.docx"
:utils "pandoc"))
-1:-- (Post TAONAW - Emacs and Org Mode)--L0--C0--2026-01-31T00:35:29.000Z
For the Emacs Carnival theme for February, let's learn more about completion together. There are all sorts of cheesy puns one can make about completion and Emacs and Valentine's Day, like "You complete me," but beyond the jokes, it's actually a really good topic to help us work with Emacs more efficiently.
From Christian Tietze:
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.
You can get a sense of previous Emacs Carnivals by checking out the previous ones:
| Month | Host | Topic |
|---|---|---|
| June 2025 | ctietze | "Take Two" |
| July | gnewman | "Writing Experience" |
| August | takeonrules | "Your Elevator Pitch for Emacs" |
| September | rodiongoritskov | "Obscure packages" |
| October | AndyDrop | "Maintenance, server or home or garden" |
| November | donaldh | "An ode to org-babel" |
| December | GeorgeJones | "The People of Emacs" |
| January 2026 | ctietze | "This year, I'll…" |
You don't have to be an expert in order to post. In fact, this is a great way for all of us (beginners and otherwise) to focus on a topic together. Let's treat it like a kind of book club where we can share our notes as we learn.
Completion can make it faster to enter text and to reduce errors. You can use it to find Emacs commands even if you don't know their full names or keyboard shortcuts. You can use it to expand abbreviations or even fix the typos you usually make. You can use it when you code and when you write. I've heard some people define common abbreviations across different programming languages so they don't have to remember the differences between syntaxes, and minibuffer-completion-based interfaces like consult-ripgrep let you flip through search results astoundingly quickly.
Let's start by talking about two types of completion:
minibuffer completion, which happens in the small window at the bottom of the screen whenever you use M-x, find a file, etc. This is where you can type a little and then find matching options so that you don't have to remember the full names of commands or files. For lots of tips, check out Understanding Minibuffer Completion - Mastering Emacs.
For example, here's my minibuffer for M-x using vertico for the display and marginalia for annotations on the side:
in-buffer completion, like when you expand an abbreviation, insert a snippet, or fill in the rest of a variable name.
Here are some ideas for things to explore. Pick an idea or come up with your own and write a post sharing what you're figuring out!
S-M-x (execute-extended-command-for-buffer - available with Emacs 28.1 or higher), which suggests commands relevant to the current mode?M-p to go back through your history? (Did you know you can interactively search through that history with C-s and C-r?)M-n to go into the future history?M-x (execute-extended-command) and M-y (yank-pop) soo much nicer!For example, this month, I want to…
I'll publish my notes on my blog and I'll add them to this post as well. I'd love to check out your notes too!
Please e-mail me at sacha@sachachua.com or DM me via Mastodon with a link to your post(s) by February 28 so that I can add them to this post. I'm happy to link to multiple posts. For example, here are some things you might like to write about:
Looking forward to hearing from you!
You can comment on Mastodon or e-mail me at sacha@sachachua.com.
-1:-- Emacs Carnival February 2026: Completion (Post Sacha Chua)--L0--C0--2026-01-30T16:41:18.000Z
As you all know, I am always writing about how much I like and use Journelly. One of the things that I always say is that since Journelly saves it data as an Org mode file—or, if you prefer, as a Markdown file—the file is essentially a database that can be queried and processed to produce other files.
Álvaro Ramírez has a very interesting post that describes one such workflow. Much like I might do, Ramírez adds an entry in his Journelly when he comes across some data about a movie he might want to watch. It may be an IMDB entry, a Reddit post, or even just something someone told him so that all he has is the movie or director name. The common denominator is that he adds a tag such as #film or #watch to mark those entries having to do with movies he should watch. Journelly can, of course, search on the tags but Ramírez has a better way.
First he extracts all the entries having an appropriate tag into a watchlist.org file. That gives him a file with all the movies he might want to watch. He uses this and the Claude Code agent to look up each entry in IMDB and to retrieve all the metadata for each movie from IMDB and put it in a db.org file. Finally, he uses the db.org file to generate HTML so that he has a browsable file showing each movie along with its poster.
Take a look at his post for the details and to see the final results. As Ramírez says,
At the center of all it all my beloved org syntax. Thanks to plain text formats, we can easily peek at them, query them, poke at them, tweak them, and bend til our heart’s content. It’s just so versatile and now we can throw them at LLMs, too.
Almost none of this is something you’d expect a text editor to do but the Combination of Emacs and Journelly provides a way of moving from free form capture entries to a polished, browsable file.
-1:-- Extracting Data From Journelly Entries (Post Irreal)--L0--C0--2026-01-30T15:33:40.000Z
My Linux machine crashed, while I was in the middle of a video call. I restarted quickly, and continued the discussion. Later, when I was trying to sync Elfeed updates onto the Elfeed Offline app on my phone, I found it acting weird. All the bazillion things needed to get it working were in place, but it was still complaining about the Emacs Elfeed server not being accessible. I tried opening Elfeed inside Emacs and failed! The index file in Elfeed’s DB was empty! Gone! Poof!
Frustrating, but I didn’t have time to look into what happened. I would’ve been furious if I had been using Elfeed for longer and had a lot more metadata saved. But, it was only a few weeks of lost metadata - posts I read, starred, etc. I quickly setup a Git based backup to prevent future losses and moved on.
Later, I found time to dig into what might have happened…
Elfeed stores all the metadata for all the posts in an index file with a
content addressed store of the contents of each of the posts. The index file is
simply a dump of the hash-table containing the metadata for the subscribed
feeds, their entries and metadata like tags, read/unread status, etc.
The DB save happens in elfeed-db-save, which simply dumps the hash-table to
disk inside a call to the with-temp-file macro.
(defun elfeed-db-save ()
; <snip>
(with-temp-file (expand-file-name "index" elfeed-db-directory)
; ...
(princ (format ";;; Elfeed Database Index (version %s)\n\n"
elfeed-db-version))
; ...
(prin1 elfeed-db)
;...
))
with-temp-file, as its documentation says, lets you create a new buffer,
evaluate the body there, and write the buffer to file. For a moment, I
thought there was some temporary file involved, but nope! I guess the name
comes as an extension from with-temp-buffer which does create a temporary
buffer where the body of the macro gets evaluated. Stripped to its core, this
function is:
`(let ((,temp-file ,file)
(,temp-buffer (generate-new-buffer " *temp file*" t)))
(prog1
(with-current-buffer ,temp-buffer
,@body)
(with-current-buffer ,temp-buffer
(write-region nil nil ,temp-file nil 0))))
write-region and O_TRUNCSo, write-region is the workhorse which writes the contents of the temporary
buffer to disk. It’s a roughly 300 line long C function that essentially opens
the file with the flags O_WRONLY | O_CREAT | O_TRUNC (in this case) and then
does something to write the contents to the file, etc. Honestly, I didn’t look
at anything else too carefully after I spotted the O_TRUNC.
The man page of open explains O_TRUNC as follows:
O_TRUNC :: If the file already exists and is a regular file and the access mode allows writing (i.e., is O_RDWR or O_WRONLY) it will be truncated to length 0.
Voilà!
The write is not atomic. The file first gets truncated to length 0, and then we hope that the new contents get correctly written before something goes wrong.
It now makes sense why the file got truncated to 0 bytes. The crash happened
after the open, but before the write. Somehow the crash happened in this
short window, and poof!
Temporary files with ~ in their file extensions have definitely annoyed me in
the past when Emacs created them where I didn’t want them. So, I do know that
Emacs has back-up mechanisms out of the box. But, it turns out that the backups
occur in code paths that are more interactive, like save-buffer,
write-file, etc. And not via the programmatic APIs like write-region or the
higher level with-temp-file. save-buffer calls backup-buffer before
writing, but write-region is much more low-level and doesn’t deal with
backups.
My “fix” for this is to backup the Elfeed data in a git repository to be able to recover from any such corruptions of data, which I already wrote about here.
I know that any code that uses with-temp-file (or write-region) could be
affected by this, and the right fix for this may be to write to a temporary
file and rename it. Maybe next time I lose data I’ll actually fix it properly.
-1:-- Why my Elfeed index was 0 bytes (Post punchagan)--L0--C0--2026-01-30T11:28:00.000Z
I can queue multiple transcriptions with whisper.el so that they get processed sequentially with backup audio. It catches up when I pause to think. Now I want to use Silero voice activity detection to do that kind of segmentation for me automatically.
First, I need a Python server that can print out events when it notices the start or stop of a speech segment. If I print out the timestamps, I might be able to cross-reference it someday with interestingthings. For now, even just paying attention to the end of a segment is enough for what I want to do.
Python script for printing out eventsimport sounddevice as sd
import numpy as np
import torch
import sys
from datetime import datetime, timedelta
SILENCE_DURATION = 500
SAMPLING_RATE = 16000
CHUNK_SIZE = 512
model, utils = torch.hub.load(repo_or_dir='snakers4/silero-vad',
model='silero_vad',
force_reload=False)
(get_speech_timestamps, save_audio, read_audio, VADIterator, collect_chunks) = utils
vad_iterator = VADIterator(model, threshold=0.5, min_silence_duration_ms=SILENCE_DURATION)
stream_start_time = None
def format_iso_with_offset(offset_seconds):
if stream_start_time is None:
return "PENDING"
event_time = stream_start_time + timedelta(seconds=offset_seconds)
return event_time.astimezone().isoformat(timespec='milliseconds')
def audio_callback(indata, frames, time, status):
global stream_start_time
if status:
print(status, file=sys.stderr)
if stream_start_time is None:
stream_start_time = datetime.now()
tensor_input = torch.from_numpy(indata.copy()).flatten()
speech_dict = vad_iterator(tensor_input, return_seconds=True)
if speech_dict:
if "start" in speech_dict:
print(f"START {format_iso_with_offset(speech_dict['start'])}", flush=True)
if "end" in speech_dict:
print(f"END {format_iso_with_offset(speech_dict['end'])}", flush=True)
try:
with sd.InputStream(samplerate=SAMPLING_RATE,
channels=1,
callback=audio_callback,
blocksize=CHUNK_SIZE):
while True:
pass
except KeyboardInterrupt:
print("\nStopping...")
Then I can start this process from Emacs:
(defvar my-vad-events-process nil)
(defvar my-vad-events-dir "~/proj/speech/")
(defvar my-vad-events-command `(,(expand-file-name ".venv/bin/python" my-vad-events-dir)
"vad-events.py"))
(defun my-vad-events-filter (proc string)
(when (and (string-match "^END" string)
(process-live-p whisper--recording-process))
(message "Noticed speech turn: %s" string)
(my-whisper-continue)))
(defun my-vad-events-ensure ()
"Start the process if it's not already running."
(interactive)
(unless (process-live-p my-vad-events-process)
(let ((default-directory my-vad-events-dir)
(process-environment
(cons
(format
"PULSE_PROP=node.description='%s' media.name='%s' node.name='%s'"
"vad" "vad" "vad")
process-environment)))
(setq my-vad-events-process
(make-process
:name "vad-events"
:command my-vad-events-command
:buffer (get-buffer-create "*vad-events*")
:stderr (get-buffer-create "*vad-events-err*")
:filter #'my-vad-events-filter)))))
Because I added Pulse properties to the process environment, I can easily use epwgraph to rewire the input so that it gets the input from my VirtualMicSink instead of the default system audio device. (Someday I'll figure out how to specify that as the input automatically.)
Now I can press my shortcut for my-whisper-continue to start the process. As I keep talking, it will continue to record. When I pause for more than a second between sentences, then it will send that chunk to the server for transcription without me having to press another button, while still listening for more speech.
How is this different from the streaming approach that many real-time speech recognition services offer? I think this gives me a bit more visibility into and control of the process. For my personal use, I don't need to have everything processed as quickly as possible, and I'm not trying to replicate live captions. I just want to be able to look back over the last five minutes to try to remember what I was talking about. I usually have a lot of quiet time as I think through my next steps, and it's fine to have it catch up then. I also like that I can save time-stamped audio files for later processing, divided according to the speech segments. Those might be a little bit easier to work with when I get around to compositing them into a video.
You can e-mail me at sacha@sachachua.com.
-1:-- Using Silero voice activity detection to automatically queue multiple transcriptions with natrys/whisper.el (Post Sacha Chua)--L0--C0--2026-01-29T16:24:46.000Z
-1:-- ultisnips-mode.el: An Emacs major mode for editing Ultisnips snippet files (*.snippets files) (Post James Cherti)--L0--C0--2026-01-29T15:48:37.000Z
Well, not really prodigal, but the return—nonetheless—of a son assumed lost. Michał Sapka has a blog post about his return to Emacs. A while ago, Sapka left Emacs for Vim and the shell. He liked them but realized that Emacs really is different.
The way it’s different is telling. Although you can do anything in Vim and shell, Emacs is different. As Sapka puts it,
It’s not that nothing stops you from connecting Mastodon.el, Magit and Mu4e, it’s that it’s natural.
Because all of Emacs is exposed to the user, it’s easy to modify it to fit your workflow and string together disparate applications in ways that their authors never intended. Sapka makes the same point that Irreal and others have made: it’s not that Emacs has an extension language, it’s that Emacs’ source code is modifiable on the fly from within the application itself at run time. It’s a whole different thing.
Sapka admits that there are problems but says that they are solvable. Depending on your work environment, solving them may be more or less difficult but they remain solvable.
In any event, Sapka has returned to Emacs because, in the end, nothing else provides the same power and flexibility. He ends his post by noting that he’s—sort of—combined the power of Emacs and Vim by adopting Evil mode.
-1:-- Return Of The Prodigal Son (Post Irreal)--L0--C0--2026-01-29T15:29:40.000Z
I've been somewhat chaotically "bookmarking" things to watch for some time. The info typically goes straight to an Emacs org file primarily via Journelly.
I'm fairly forgiving in my input form. I often link to Reddit discussions, IMDB/Letterboxd links, or at times simply write movie or director names. The only real requirement is to sprinkle some hashtags (#film or #series or #watch) for retrieval.
While this flexibility is important (makes it more likely for me to actually write), it's not super practical to browse through this mixed structure when looking for something to watch. Yesterday, I finally did something about that.
Here's what my save-to-Journelly flow may look like for a Reddit link:

All entries are saved to Journelly.org, a plain text org file (Markdown is supported too), so I first extracted all relevant entries (containing #film or #series or #watch). A matching entry looks a little something like this:
* [2026-01-29 Thu 09:52] @ Home
:PROPERTIES:
:LATITUDE: 51.5007
:LONGITUDE: -0.1246
:WEATHER_TEMPERATURE: 4.2°C
:WEATHER_CONDITION: Cloudy
:WEATHER_SYMBOL: cloud
:END:
#film #hongkong #watch
https://www.reddit.com/r/movies/comments/1j85b52/just_rewatched_kung_fu_hustle_still_felt_great
Having extracted non-personal items from Journelly.org, I created a git repo with just one file: watchlist.org including entries like the one above.
With personal/private details out of the way, I decided to let the LLM robots loose. That is, hand watchlist.org over to a Claude Code agent, via Emacs agent-sell to organize my chaos.
First I asked to normalize data by extracting all film/tv info from each entry, visiting Reddit/IMDB/Letterboxd links if needed, which yielded normalized.org:
Kung Fu Hustle
https://www.imdb.com/title/tt0373074
Next I asked to generate metadata for me (using the fields I wanted from IMDB), stored in org drawers, saved to db.org:
* Kung Fu Hustle
:PROPERTIES:
:TYPE: film
:YEAR: 2004
:IMDB: https://www.imdb.com/title/tt0373074/
:IMDB_RATING: 7.7
:COUNTRY: Hong Kong
:DIRECTOR: Stephen Chow
:GENRE: action, comedy, fantasy
:RUNTIME: 99
:ADDED: [2026-01-28]
:THUMBNAIL: file:thumbnails/tt0373074.png
:IMDB_THUMBNAIL: https://m.media-amazon.com/images/M/MV5BNGU2OWVlM2ItZGZlOC00Yzk2LWE1NzEtMDYwMzE4YTE5MzQ2XkEyXkFqcGc@._V1_FMjpg_UY1024_.jpg
:END:
Finally, I asked to generate HTML from db.org so I can render and easily browse.
<div>
<a href="https://www.imdb.com/title/tt0373074/"><img src="https://m.media-amazon.com/images/M/MV5BNGU2OWVlM2ItZGZlOC00Yzk2LWE1NzEtMDYwMzE4YTE5MzQ2XkEyXkFqcGc@._V1_FMjpg_UY1024_.jpg" alt="Kung Fu Hustle" width="150" height="225"></a>
<p><strong>Kung Fu Hustle (2004)</strong></p>
<p>action · comedy · fantasy</p>
<p>Hong Kong · Stephen Chow</p>
</div>
For each transformation, Claude Code generated a python script. I've yet to decide what to do with all the resulting source code. Do I clean it up? Rewrite it?
For now, I'm happy with the results of the experiment. I managed to organize some of my chaos! In some ways, this isn't too different from me writing a quick/hacky/ugly script, when it's acceptable to do so.
At the center of it all, is my beloved org syntax. Thanks to plain text formats, we can easily peek at them, query them, poke at them, tweak them, and bend til our heart's content. It's just so versatile, and now we can throw them at LLMs too.
Oh, and if you're curious about that watch list, here you go. Do you have some movie/show suggestions for me?
-1:-- Film/TV bookmarks (chaos resolved) (Post Alvaro Ramirez)--L0--C0--2026-01-29T00:00:00.000Z
OK, so (not that) long story short: I'm back inside Emacs.
I deeply enjoyed the Vim side-quest, so I can now say that I use both: Vim and Emacs
, but it's time to return to the main storyline.
See, Emacs is just so different from anything out there, that nothing comes close.
Vim may be an outstanding editor, and Shell gives me everything... but it's not the same.
The thing with precompiled software is that they are black boxes, and modifying them is a huge pain in the ass.
In Emacs, on the other hand, all the interesting parts are there for you to tinker.
It's not that nothing stops you from connecting Mastodon.el, Magit and Mu4e, it's that it's natural.
After using the standard shell I noticed that I don't care about Emacs, but what is stands for: the way software should have always been.
I want to have my entire computing experience in an open, interconnected system. What I don't want is some pesky product manager telling me how I should use their software, or fighting with my fork. Neither do I want to rent server space to run a glorified web page. Extensions are half-step; the entire program should be available for me to modify.
Now, there are problems - but none unsolvable. I am forced to do Java in day-job really soon, but we're living in the XXI century! Even such legacy language has some some tooling now. LSP works for autocomplete (not yet with our repo, but I'll fix it somehow) and refactorings are supported. Debugging is available via JDB and some Microsoft thingy (none of which work with our repo, but I'll fix it somehow). This means I will most likely not need to wash myself after work just to get the stench of IntelliJ out of me.
There are also pluses. I forced myself to do work notes, just to use org-mode... finally. This already reaps benefits, as it is miles better than a dozen Slack messages, Confluence, Microslop GitHub, and some memory. I am not the most organized person I've known, but I've heard legends of people less organized.
Happy LISPing to me! I'll need to write that html-to-gemtext converter. And yes - I am fully evil-mode now.
-1:-- RTE - Return to Emacs (Post Michal Sapka)--L0--C0--2026-01-28T20:36:26.000Z
Inspired by the Emacs Carnival theme for January, this year, I will:
The more I do in Emacs, the more I think of ideas for improvement…
En françaisSur le thème du Carnaval d'Emacs pour janvier - Cette année, je vais :
Plus j'en fais dans Emacs, plus je pense à des idées d'amélioration…
Thanks to Christian Tietze for hosting!
You can comment on Mastodon or e-mail me at sacha@sachachua.com.
-1:-- Emacs: This year, I will... / Cette année, je vais... (Post Sacha Chua)--L0--C0--2026-01-28T16:28:40.000Z
Having another look at my AI assistant - ollama-buddy, its been a while and it seems ollama has moved on since I started creating this package last year, so I have developed a new roadmap and the first step is to add ollama cloud models!
Here are some references to the project, including a youtube channel where I upload ollama-buddy demonstrations:
https://melpa.org/#/ollama-buddy
https://github.com/captainflasmr/ollama-buddy
Here is the changelog for the cloud model implementation:
Added Ollama Cloud Models support
ollama-buddy-cloud-signin to automatically open browser for authenticationC-u C-c m or transient menu “Model > Cloud” to select cloud models-1:-- Ollama buddy now supports cloud models! (Post James Dyer)--L0--C0--2026-01-28T08:29:00.000Z
John Gruber, over at Daring Fireball has a post, Untitled Document Syndrome, that makes two points of interest to Irreal readers. The first involves the difficulty of initially saving what he calls “untitled documents”. He’s writing about macOS, of course, but even through I’ve used one Mac or another for almost two decades, I had a hard time figuring out what he was talking about. I guess it’s this: in some Apple text apps you can start typing without specifying a document name or a file path for where it should be saved. That means that when you first save the file you have to negotiate the file save dialog to supply this information.
Since I write everything in Emacs, this never happens to me. Part of starting a new file is executing a find-file which specifies its name and file system location. Even then, I just specify it in the minibuffer; there’s no annoying open dialog to deal with. The first time I save it is like any other time1: a simple Ctrl+x Ctrl+s.
The point of all this is that the difficulty of the initial save means that people will put it off and therefore suffer disaster when the app or system crashes. They may even lose hours of work. Happily, that first save is easy in Emacs so there’s no reason—not even a weak one like the save dialog—for putting it off.
Even if you do put it off, Emacs, like Gruber’s editor BBEdit, has you covered. It does periodic automatic backups so you never lose more than a little work.
The second point is related and concerns note taking apps. Like most of us—at least us oldtimers—Gruber stated taking notes by creating a file for each one with his editor. As we’ve all discovered, that doesn’t work all that well so he moved to Yojimbo and discovered that he was taking many more notes. As you all know, I solved the same problem with Journelly. I use it as my memo book and typically make about 10 entries per day. People think of Journelly as integrated with Emacs but it can also save its data in Markdown so it’s perfectly usable on any system and editor as long as you’re using an iPhone.
It’s amusing how 40 year old technology is still more convenient and easier to use than “modern” systems with dialog boxes for everything.
Yes, one can just open a buffer, set the appropriate mode and start writing. When it comes time to save it, you do have specify a name and location but, of course, no one works this way.
-1:-- Gruber On File Saving Ease (Post Irreal)--L0--C0--2026-01-27T16:22:14.000Z
As much as I enjoy using Eshell, it is not without its annoyances. In this case, I’m writing about the common shell convention of using !n to recall a previously entered command.
In Eshell, entering the history command will show you all of your previously entered commands alongside a number n.
The problem is in attempting to use !n to recall a specific command in Eshell as one would do in bash or zsh. By default it will not work as described in Info (eshell) History without configuring eshell-expand-input-functions first. (My current configuration is GNU Emacs 30.2.)
To get !n to work requires adding the function eshell-expand-history-references to the customizable variable eshell-expand-input-functions.
The following configuration Elisp will achieve this.
1 2 | |
A nice feature of eshell-expand-history-references is that it will expand using the content of an actual command instead of a number reference. So !foo will invoke the last command that you entered the string “foo” in.
It’s not clear to me whether coupling nth history support to eshell-expand-history-references is intentional or a bug. If the former, then I’d consider this yet another example of staying “on brand” with Emacs’ tendency to ship with questionable defaults. That shade aside, Eshell is still an amazing package that I could not imagine using Emacs without.
-1:-- Getting Eshell nth History to Work (Post Charles Choi)--L0--C0--2026-01-27T06:15:00.000Z
I’ve discovered, over the years, that setting a clear intention is closely correlated to a day well spent. I know that, when I decide what I want to do early in the morning—before being overwhelmed by email, Slack, Zulip, Discord, and all the other little attention grabbers—that I have a better chance of doing something I care about.
It seems like, in 2026, slow, intentional work is en vogue, with books like Burkeman’s “Meditations for Mortals” and Cal Newport’s “Slow Productivity” doing the rounds. As my friends know, I am fascinated (and inspired) by the Stoics and enjoy listening to the various teachers on Waking Up, many of whom echo the same idea: that mindful, focused effort is vastly more satisfying than responding to whatever seems urgent.
How we set intention is personal, and recommending any specific system feels trite. However, I have found that, for me, planning a rough schedule for the day—often called time-blocking—is a great way to think about what I want to do, how much of it I want to do, and when I want to do it.
Trying to follow such a schedule brings home many realities quickly:
I use org-mode to outline my day, and have capture templates for each
week and each day. For this year, I have a file 2026.org in which I
capture all those entries.
The day planner template is most relevant here:
** %u
- [ ] Scan [[mu4e:query:maildir:/INBOX flag:flagged][pinned emails]] for new tasks (max 10 mins)
- [ ] Scan [[https://github.com/notifications][GitHub]] for new tasks (max 10 mins)
*** Plan
****
**** Lunch + walk <%<%Y-%m-%d %a 13:00-13:45>>
****
**** Tidy up <%<%Y-%m-%d %a 16:45-17:00>>
*** Notes
It is then hooked up to my org-capture-templates.
I use org-indent mode, so all those heading stars collapse.
The part I’d like to discuss in this post is the “Plan” section, where I outline my work for the day.
The time stamps in org-mode are neat: you’ll see the lunch entry
goes from 13:00 to 13:45 (and this is how it is stored on disk), but
when modifying it in org-mode it is displayed for editing in the
minibuffer as 13:00+0:45. This makes it very easy to adjust the
duration of an event. Note that the day timestamp (%u) is
inactive,
whereas the time blocks are active, so that only the latter appear in
today’s Agenda view.
The above template is sufficient for doing time blocking in org-mode. However, we can reduce some friction in how we use it by adding some utility functions for common activities.
Here are three such functions that help me to craft and modify my daily plan smoothly:
Quickly navigate to today’s plan
This command is invoked inside 2026.org, and navigates to the current week, then day, then to the end of the “Plan” entry.
(defun stefanv/org-jump-to-today-plan ()
"Jump to the end of the 'Plan' heading under today's date headline."
(interactive)
(let* ((today (regexp-quote (format-time-string (org-time-stamp-format nil t))))
(today-re (concat "^\\*+ .*?" today)))
(goto-char (point-min))
(if (not (re-search-forward today-re nil t))
(message "Heading matching %s not found." today)
(org-reveal)
(save-restriction
(org-narrow-to-subtree)
(goto-char (point-min))
;; Matches 'Plan' heading and optional trailing tags
(if (re-search-forward "^\\*+ Plan\\(?:[ \t]*\\(:[[:alnum:]_@:]+:\\)\\)?[ \t]*$" nil t)
(org-end-of-subtree)
(widen)
(message "No exact 'Plan' heading found under today's date."))))))
It specifically searches for a * Plan heading, so if you modify the
template be sure to update the regexp accordingly.
Add a new planner entry, which follows the previous
This function takes the end time of the previous entry, and uses it as the start time for a new entry.
It tries to be somewhat clever at guessing your intention:
(defun stefanv/org-plan-next ()
"Create a new day plan entry following on the current or previous lines's active timestamp."
(interactive)
(let* ((ts-found (save-excursion
(end-of-line)
(when (re-search-backward org-ts-regexp0 nil t)
(let ((ctx (org-element-context)))
(when (eq (org-element-type ctx) 'timestamp) ctx)))))
(is-blank (string-blank-p (buffer-substring (line-beginning-position) (line-end-position))))
(is-header (org-at-heading-p))
;; Check if the found timestamp is actually on the current line
(on-current-line (and ts-found
(>= (org-element-property :begin ts-found)
(line-beginning-position)))))
(if (not ts-found)
(user-error "No timestamp found above point")
(cond
;; Already has TS OR is plain text -> new heading below
((or on-current-line (and (not is-blank) (not is-header)))
(end-of-line)
(org-insert-heading)
(save-excursion (stefanv/insert-formatted-org-ts ts-found)))
;; Current line is blank -> turn into heading here
(is-blank
(org-insert-heading)
(save-excursion (stefanv/insert-formatted-org-ts ts-found)))
;; Current line is a header (no TS) -> append TS and return cursor
(t
(save-excursion
(end-of-line)
(stefanv/insert-formatted-org-ts ts-found)))))))
(defun stefanv/insert-formatted-org-ts (ts)
"Helper to insert formatted timestamp with exactly one preceding space."
(just-one-space)
(insert (format "<%s %02d:%02d>"
(org-format-timestamp ts "%Y-%m-%d %a")
(or (org-element-property :hour-end ts)
(org-element-property :hour-start ts) 0)
(or (org-element-property :minute-end ts)
(org-element-property :minute-start ts) 0))))
If you use it often, you can bind the function to a key:
(keymap-set org-mode-map "C-c C-x p" #'stefanv/org-plan-next)
Move all timestamps in a region forward by N minutes.
Sometimes, a day runs away from us, or we get carried away and miss the end of a timeblock. At that point, you’d likely want to shift your entire day forward. The following function lets you select several planner items and shift them all by N minutes (positive or negative). If no region is selected, operate on the current line.
There’s some fancy footwork to ensure that the surrounding org buffer
is re-rendered, especially when using
org-modern.
(defun stefanv/org-shift-timestamps-in-region (minutes)
"Shift all active timestamps in the region (or current line) forward by MINUTES minutes."
(interactive "nMinutes to shift: ")
(let* ((region-active (use-region-p))
(beg (if region-active (region-beginning) (line-beginning-position)))
(end (copy-marker (if region-active (region-end) (line-end-position))))
;; Regex for active timestamps: matches < followed by anything until >
(active-ts-re "<\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}.*?\\)>"))
;; Allow undoing everything in one step
(atomic-change-group
(save-excursion
(goto-char beg)
;; Shift active timestamps
(while (re-search-forward active-ts-re end t)
(save-excursion
(goto-char (match-beginning 0))
(org-timestamp-change minutes 'minute)
(when (fboundp 'org-element-cache-refresh)
(org-element-cache-refresh (point)))))
;; Re-align tags for any headlines in the range
(goto-char beg)
(while (re-search-forward org-outline-regexp-bol end t)
(org-align-tags))))
;; Refresh
(font-lock-flush beg end)
(set-marker end nil)
(message "Shifted active timestamps by %s minutes." minutes)))
I’ve presented three utility functions that help me quickly plan and organize my day. I’d love to hear from you how you plan yours!
-1:-- org-mode Day Planning with Time Blocks (Post Stefan van der Walt)--L0--C0--2026-01-27T00:00:00.000Z
The ef-themes are a collection of light and dark themes for GNU
Emacs that provide colourful (“pretty”) yet legible options for users
who want something with a bit more flair than the modus-themes (also
designed by me).
ef-themesBelow are the release notes.
This version introduces two carefully designed, legible and colourful
themes: ef-orange (light) and ef-fig (dark). Both draw inspiration
from the fruits they are name after.
The convenience commands ef-themes-select-dark and
ef-themes-select-light use the minibuffer to select+load an Ef theme
that is either dark or light, respectively. The more general command
is ef-themes-select.
All screen shots of the themes are updated to reflect their current status: https://protesilaos.com/emacs/ef-themes-pictures.
Remember that since version 2.0.0 the ef-themes are built on top
of my modus-themes. This means that most of the changes happen to
Modus and are inherited by Ef.
-1:-- Emacs: ef-themes version 2.1.0 (Post Protesilaos Stavrou)--L0--C0--2026-01-27T00:00:00.000Z
I’ve been an Emacs user for more than 25 years, and I’d say I’ve got a reasonable handle on customizing it. I can writeelisp, and I’ve got a couple decades of accumulated tweaking cruft on display in my dotfilesrepo. I’ve writtenadvice, I bind custom functions to keys, andI’m actually writing this right now with a customized mode for Hugomarkdown that I ginned up when Iwas getting annoyed by the way fill-paragraph worked inside Hugo shortcodes.
The reason Emacs is so attractive to those of us who use it is its infinite customizibility. There’s really not muchyou can’t alter or intercept or rewrite with enough effort, and over time your editor will come to fit you like awell-worn boot.
However, there are plenty of minor annoyances that I just live with. No one’s built the exact package that I want, andlearning enough to do it myself would be shaving just oneyak too many. So I do things by hand,or use the mouse instead of having a shortcut that jumps to whatever-it-is, or I deal with the hard-to-read log outputinstead of nice syntax highlighting.
LLMs, it turns out, are pretty good at elisp. There is a ton of open-source training data and gooddocumentation. So far I’ve successfully had Gemini or Claude build me:
go-test-mode.Unfortunately my biggest successes have all been for work, done on work machines with AI paid for by work, so I can’tpublish them here (at some point I’ll go through channels and see if I can open source some of it), but here’s someredacted logs that show the pretty formatting I was able to achieve:

And here’s a slightly-mangled version of the code that produces it. Note most of the comments and doc comes straightfrom Gemini:
(defgroup work-log-faces nil "Faces for Work integration test logs.");; Define faces(defface work-log-timestamp '((t :inherit font-lock-comment-face :foreground "#6c757d")) "Face for timestamps." :group 'work-log-faces)(defface work-log-level '((t :inherit font-lock-keyword-face :weight bold)) "Face for log levels (INFO, WARN)." :group 'work-log-faces)(defface work-log-message '((t :inherit font-lock-string-face :foreground "#e0e0e0")) "Face for the log message text." :group 'work-log-faces)(defface work-log-json '((t :inherit font-lock-constant-face :height 0.9)) "Face for the JSON payload." :group 'work-log-faces);; Define the matching rules(defvar work-log-font-lock-keywords '(("^\\([0-9T:.-]+\\)[ \t]+\\([A-Z]+\\)[ \t]+\\(.*?\\)[ \t]+\\({.*}\\)?$" (1 'work-log-timestamp t) (2 'work-log-level) (3 'work-log-message) (4 'work-log-json))) "Regex keywords to highlight structured logs.");; 1. Define the control flag (default to nil)(defvar -work-test-highlighting-active nil "If non-nil, apply custom log highlighting in go-test-mode.");; 2. Define the function that checks the flag(defun my:work-apply-logs-highlight () "Apply highlighting only if the Work test flag is active." (when -work-test-highlighting-active (font-lock-add-keywords nil work-log-font-lock-keywords t)));; 3. Add it to the hook;; Note: go-test usually runs in `go-test-mode` or `compilation-mode`(add-hook 'go-test-mode-hook #'my:work-apply-logs-highlight)So far I’ve used two primary interfaces to LLMs: Claude Code, especially Claude-code-ide forEmacs, and the Gemini web interface. For Claude I’ve been using Opus4.5, for Gemini 3 either Thinking or Pro. Keep in mind that most of the time my company is paying for tokens, so I’mnot being particularly cost-conscious (sorry, bean-counters). I haven’t done any serious comparisons of models andinterface because…all the modern models work pretty well for this sort of thing.
For both syntax highlighting and the backtraces, I was able to simply paste in a sample of the input and describe whatI’d like, and it was able to give me some elisp I could drop into my .emacs.d.
The ts grammar and mode needed a bit more back-and-forth and some actual debugging (by both myself and Gemini). I thinkif I had less elisp experience I wouldn’t have been able to vibecode the whole solution, but I’ve never really written ats grammar or a major mode before that was more than a tweak or two, and it got me 80% of what I needed.
The code that Gemini wrote for the font-locking wasn’t revolutionary. It ended up just writing a regex, some faces, andcalling font-lock-add-keywords. But I’ve barely messed withfont-lock or face definitions myself before and I didn’t know any of the relevant functions off the top of my head. Icould have figured it out, but it wouldn’t have been worth my time. The LLM shaved most of the yak for me.
So next time you’re annoyed by a missing motion command, or ugly output, or you wish you had a command that worked justa bit differently, ask Claude/Gemini/your favorite LLM to fix it for you. Not only will you fix your problem, but ifyou take just a bit of time to review the output (and you’re not gonna just blindly throw LLM code in your .emacs right?RIGHT?), you may learn about a subsystem you didn’t know before. I’m sure you can do this with Neovim or, to a lesserextent, VSCode or other editors that have reasonable extension points, so go fix them too. Your yaks will thank you come summer.
-1:-- Yak Power-Shears: LLMs are pretty good at Emacs (Post Evan Moses)--L0--C0--2026-01-27T00:00:00.000Z
New packages:
Links from reddit.com/r/emacs, r/orgmode, r/spacemacs, Mastodon #emacs, Bluesky #emacs, Hacker News, lobste.rs, programming.dev, lemmy.world, lemmy.ml, planet.emacslife.com, YouTube, the Emacs NEWS file, Emacs Calendar, and emacs-devel. Thanks to Andrés Ramírez for emacs-devel links. Do you have an Emacs-related link or announcement? Please e-mail me at sacha@sachachua.com. Thank you!
You can e-mail me at sacha@sachachua.com.
-1:-- 2026-01-26 Emacs news (Post Sacha Chua)--L0--C0--2026-01-26T22:20:00.000Z
A couple of weeks ago I read a lovely story in The New Yorker by Calvin Tompkins about starting a journal in his hundredth year. Tompkins was born the same year that The New Yorker was founded and, ironically, spent most of his working life writing for the magazine. Last year—his hundredth—he surprised himself by deciding to start a journal as a sort of countdown to his hundredth birthday. The story is mostly entries from the journal but I found them fascinating. Perhaps you will too. If nothing else, you’ll get a first hand glimpse of what old age really looks like. The TL;DR is that there’s good news and bad news.
In any event—although I am nowhere near my hundredth year—I was inspired to start my own journal. Of course, it was going to be written with Emacs but the question is how. There is the builtin journal app as well as some third party packages but I chose simplicity. I didn’t need anything special or complex with arcane functions so I just added an Org capture template to create a file+datetree file in Org mode. Here’s the whole thing:
("J" "Personal Journal" entry (file+datetree "~/org/personal-journal.org") "* %<%R: >%? \n%t" :empty-lines-before 1)
I have some startup options in the file itself to set visual-line-mode, use a variable pitch mode, and couple of other things: the same setup that I use for my blog posts. All this is simple and familiar. There’s nothing new for me to learn since it’s basically the same setup that I use everyday to write my Irreal posts.
The takeaway is that if you’d like to start a journal, it’s really easy. Just use a simple Org file with a corresponding capture template.
-1:-- Starting A Journal (Post Irreal)--L0--C0--2026-01-26T15:55:58.000Z
Here is just a quick one, I was working with an org-mode table for tracking work weeks and needed to auto-populate a Date column where each row increments by exactly one week. The table structure looked like this:
The first row has a base date (2026-01-05), and I wanted subsequent rows to automatically calculate as weekly increments: 2026-01-12, 2026-01-19, and so on.
Initially, I tried several approaches that seemed logical but encountered #ERROR results and eventually settled on a working solution which is to hardcode the base date directly in the formula:
#+TBLFM: $3='(format-time-string "%Y-%m-%d" (time-add (date-to-time "2026-01-05") (* (- @# 2) 7 24 3600)))
which gave:
and I can now of course extend the table for all the weeks in the year and I don’t have to fill in manually any more!
Here’s how it works:
(date-to-time "2026-01-05") - Convert the hardcoded base date to Emacs time format(- @# 2) - Calculate the offset from the base row(* (- @# 2) 7 24 3600) - Convert the offset to seconds (weeks × days × hours × seconds)time-add - Add the offset to the base dateformat-time-string "%Y-%m-%d" - Format back to ISO date string-1:-- Auto-Populating Weekly Dates in Org-Mode Tables (Post James Dyer)--L0--C0--2026-01-26T10:13:00.000Z
To ensure that my skepticism of LLMs did not blind me to progress, and to keep my finger on the pulse, I’ve been incorporating them into my Emacs config, starting with gptel and, more recently, macher. In December, I took another look at Gemini, and noticed a significant improvement over Flash 2.5 with the arrival of Flash 3 Preview.
In this post, I share the configuration I currently use, and mention some open questions I still hope to address.
gptel is a beautifully clean and simple interface for LLM chat.
Despite its simplicity, it actually has fairly advanced functionality,
which includes MCP integration, introspection of queries, multi-modal
input (such as images), and tool usage (extending LLMs with access to
elisp functions of your choosing).
macher runs on top of gptel. It provides a “pseudo-agentic”
workflow, in which you provide the LLM context about your current
project, such as the list of files. It also defines several tools that
the LLM can use, such as the ability to read files. You can use
macher to generate a patch, which you then review, apply the
parts you like, or ask macher for further improvements.
The macher installation simply follows the recommendations in its README:
(use-package macher
:custom
;; better folding and source block highlighting of the reasoning process
(macher-action-buffer-ui 'org)
:config
(macher-install)
(macher-enable))
And here is the basic gptel configuration, ensuring that macher is pre-loaded:
(use-package gptel
:commands (gptel gptel-mode)
:config
(require 'macher))
Now the model definition for Gemini, to access the latest 3.0 models:
;; https://ai.google.dev/gemini-api/docs/models
(setq
gptel-model 'gemini-3-flash-preview
gptel-backend (gptel-make-gemini "Gemini"
:key "<Get your key at aistudio.google.com>"
:stream t
:models '(gemini-3-flash-preview
gemini-3-pro-preview
gemini-2.5-flash
gemini-2.5-pro)))
Note how you list the models you want to choose from, and then pick the default.
You will need an API key, which you can generate at
https://aistudio.google.com. I’m pretty sure you also need to
activate billing to access gemini-3-flash-preview, but let me know
if you succeed without doing that. My total cost, after playing around
with it a bunch this week, was ~3 USD.
If your Emacs config lives in a public repository or on an untrusted
machine, you’ll want to be careful in adding the token to it. gptel
automatically uses tokens listed in ~/.authinfo.
See Mastering Emacs
for more on
auth-source.
EDIT: I’ve since noticed that, while adding the search tool this way works, it also messes up macher. So, don’t try this yet until #750 is resolved.
The above configuration works fine, but I noticed that some answers were outdated. Gemini supports “grounding” via Google Search. For example, instead of telling you that TypeScript 5.3 is the newest release (which it was, at the time of its training), it can use Google Search to verify that, in fact, the latest version is 5.9.
gptel#750 tracks the issue, but in the meantime you can manually adjust the query sent to Google, and tell it to enable Grounding with Google Search:
;; Enable Google Search capability for gemini
(cl-defmethod gptel--request-data :around ((backend gptel-gemini) prompts)
"Add search ability to Gemini requests."
(plist-put (cl-call-next-method) :tools (append '(:google_search ()))))
This is obviously just a temporary hack, until #750 can be resolved.
Grounding with Search may incur additional costs, as per the Gemini API docs:
5,000 prompts per month (free), then (Coming soon**) $14 / 1,000 search queries
To generate a patch on your current project, you must do two things:
Select project: Tell macher which project you’re working
on. The easiest way to do this is to open a gptel buffer (which you
can give any name you like, e.g. gemini:add-tests) and then M-x cd (change directory) into your project.
I haven’t checked, but I suspect this requires the project to be under version control.
Activate macher: As part of your query, mention @macher
anywhere to enable it. E.g., @macher add unit test scaffolding. C-c <enter> executes the query.
If you type @macher and it does not get highlighted with a little
box surrounding the text, then macher isn’t correctly
installed. Try running M-: (macher-install) manually.
You can add additional context to a query (e.g., a programming
guidance document outside of the project) using gptel-add (this
is standard gptel functionality).
While the query is executing, it displays a folded reasoning block. You can type TAB to unfold it, and watch the various steps the LLM takes. After a while, a patch will be generated in a separate window.
You can apply the entire patch using diff-apply-buffer, or single
parts of the patch with diff-apply-hunk.
With my configuration, diff-apply-buffer works fine as long as only
existing files are modified. When new files need to be created, it
misunderstands the file prefix in the patch, and fails to
auto-complete its name. I filed an issue to discuss that with the
macher author at
macher#45, and
hopefully can post an update soon.
LLMs recently seem to have undergone a phase change in coding ability. Of course, even in this phase any code generated needs to be very carefully reviewed and tested. Andrej Karpathy’s tweet earlier today summarizes where we stand quite well (I recommend reading the top comments too).
E.g., when talking about the new capability, he also warns:
The nearest neighbor really is some kind of a junior engineer. Its ideas about what experiments to run […] have been surprisingly bad, but its execution on ideas I’ve given it have been surprisingly good. — https://x.com/karpathy/status/2015888674739912910
The mistakes have changed a lot - they are not simple syntax errors anymore, they are subtle conceptual errors that a slightly sloppy, hasty junior dev might do. The most common category is that the models make wrong assumptions on your behalf and just run along with them without checking. They also don’t manage their confusion, they don’t seek clarifications, they don’t surface inconsistencies, they don’t present tradeoffs, they don’t push back when they should, and they are still a little too sycophantic. — https://x.com/karpathy/status/2015883857489522876
So, while we should tread with care, there are exciting avenues to explore. I’ve been developing open source computational libraries for a long time, and as our ecosystem grows I notice myself spending more and more time on maintenance, and less and less time on reading papers and developing new features. My hope is that LLMs will free our small communities—who have very limited time and resources—from repetitive, tiring maintenance tasks, so we may return to what we love to do: crafting intuitive APIs around innovative algorithms that, in turn, enable real-world science.
-1:-- Using Gemini for Code Patches in Emacs (Post Stefan van der Walt)--L0--C0--2026-01-26T00:00:00.000Z
Does Emacs run slower on macOS than it does on a similar machine running Linux? I don’t know. I’ve been running Emacs almost exclusively on macOS for somewhere around 17 years so its performance on macOS, whatever it is, seems normal to me.
Over at the Emacs reddit, staff-engineer says that he runs it on both macOS and on Linux. The Apple machine is, in fact, more powerful than the Linux one but is nevertheless less performant when running Emacs and staff-engineer wonders why.
He received several excellent answers that are definitely worth reading but I especially like the one from totbwf who explains that a large part of the problem is that macOS uses a tiny size for its pty buffers and that this can significantly slow down Emacs when it calls an external process using a process-connection-type of pty. His solution to this is to simply advise functions calling start-process to use pipes instead of ptys. He says that this can speed up these actions by a factor of 10. For Magit, he says that you can get the same result by simply setting magit-process-connection-type to nil.
Even if you don’t want to add a lot of advises to your configuration, it may be worthwhile making this one simple change to Magit if you use it a lot.
To be honest, I have no idea if these fixes will work or not but they’re easy to try if you feel Emacs is too slow on macOS. If changing the connection type helps, I would think that someone would fix it in Emacs core for macOS installations. It doesn’t sound like it would be too hard but one of the problems is that there are few Apple users helping with development.
-1:-- Emacs Performance on macOS (Post Irreal)--L0--C0--2026-01-25T15:12:23.000Z
When I mentioned Denote again a couple of days ago, Omar commented that whenever someone mentions using Denote or org-roam, he’s curious to know if that person tried vanilla org-mode first, without additional packages.
I get it. After all, I’ve been resisting the whole Zettelkasten bandwagon and org-roam with it for a while, keeping the same opinion about org-roam Omar seems to have about Denote (I’m speculating here, of course). In general, I don’t like the idea of Doom Emacs or Spacemacs for the same reasons. Denote, for some reason, never registered this way. I liked it from day one.
As I was answering his question and he asked for more details, I thought it might make a good post. So here we are.
Not a whole lot changed for me since I first looked into Denote. I still mostly use it for my blog posts (like the one you’re reading right now) and my info notes, which mostly contain technical how-tos to myself and to a lesser degree, some records and memos, like a list of equipment in my home or who’s who at work. At this point, I also have a third, personal folder with notes only on my Linux computer as well, which mostly contain “supplemental” notes as I call them to journal entries I want to expand on in private (I use my work iPhone with Journelly, which does a fantastic job, but I don’t trust Apple or any cloud service for that matter with my private stuff).
It has been slow progress, but I’ve improved my usability with Denote, which I recently enhanced further after having the pleasure of talking to Prot 1:1 in one of his online tutoring sessions (always a pleasure, and highly recommended if you ask me).
The main philosophy behind Denote is something I’ve been in agreement with for a long time, before I started using it, or Emacs, or even got familiar with Linux: the idea of how to date and rename files. I’ve never agreed with the American way of writing dates, and I always prefer the ISO format: yyyymmdd followed by hhmm. To me, this is how things should be organized. So when Denote came around with Prot explaining that this is how he sees files should be organized - date, followed by keywords, and then a title - it just clicked.
You can extend this file-naming scheme to all your personal files if you’d like (at least all the files you visit with Dired), and I’m in the process of doing exactly that. Because the renaming function is built into Denote, it’s easy to stay concise and avoid mistakes (for example, renaming one file 20260124 something, and then another 2026124, emitting the 0 for January). While it’s true you could easily do that without Denote, to me, this core piece was like a sign that said, “Hey, look at this, this guy is building a package that is based on something that makes sense, check it out.”
Tags are another important part of Denote. Org-mode has tags built into headers, which works, but the headers, to me, are a bit too specific to need them. For the most part, I can get the level of information I need through the header itself, and I use tags to associate a certain header with a person - but even that is not useful, as I often work with too many people for the tags to really mean anything. Meanwhile, it’s the files themselves that need tags: certain workflows are similar, but are tagged to work for different operating systems (say, how to set my work VPN on Linux vs on my Mac), or maybe I have a naming convention for different departments at work, and I want to tag the relevant departments.
I didn’t always break my different workflows and notes into files. In the past, I had one org file as a wiki: a single file with many headers explaining workflows and procedures. I often had errors there. It was one big file that I often opened and edited, so I had syncing issues, headers that got mixed up, etc. The other problem I had with the wiki was logical: at times, a certain subheader would fit under two different headers, and I would have a hard time deciding where to place something. Headers work as categories, while tags work as ,well, tags. If we take the VPN example from before, I could put my VPN instructions under “networking,” “security,” or “remote work,” but not all. I don’t have this problem with Denote.
Another, more recent skill I started using with Denote is its metadata and search features. Meta notes work as an index of other files, where I can have one file with a dynamic list (updated whenever the file is loaded) of live links to other files with the same tag or other regex I use. Meanwhile, searching with Denote using denote-grep (something I learned recently) shows a list of all the files matching my search in collapsible form, making it easy to find what I need. I can use one of many denote-query forms to filter those results even further, and of course, there’s denote-consult, which, well, if you know consult, you know what it does, and it’s excellent.
Finding stuff is a central issue for anyone keeping notes, no matter what system they use. Denote supplies me with the best tools I’ve seen for this job. It’s true that many of these tools are already available in Emacs and in org-mode. Denote doesn’t alter these existing options; rather, it builds on them. In fact, that’s a big part of Denote: the way its commands are constructed borrows from what Emacs already does, so if you’re familiar with one, you intuitively know what the same thing would do inside Denote.
Another example of the above is the org files you create with Denote. You can use Denote directly with a capture template, and each file is created with file tags that make sense if you use org-mode: #-title, #+date and #+filetags, which denote changes automatically as you rename files with it. These work together with additional options I include in my notes, such as #+STARTUP: inlineimages. In a Markdown export, such as this very post you’re reading now, Emacs knows to ignore these org-mode only options, so I don’t need to clean them out when I’m ready to post. It’s a nice touch that already exists in vanilla org-mode, but with the added benefits of Denote, like having my post tagged, dated, and easily found in a search alongside the rest of my Denote files in the future.
There are definitely more features of Denote I don’t use, or use enough (blacklinks, which I believe are more of a recent introduction, is one of them), but as I stated above, this is a process. I learn more every week, and as my notes collection increases, so does my understanding of how to use Denote in a way that works for me.
-1:-- Why I use Denote? (Post TAONAW - Emacs and Org Mode)--L0--C0--2026-01-25T14:12:09.000Z
Since the switching to eglot I've ended up making a few related changes.
Since eglot it's written to work with other packages in core, which means it
integrates with flymake. The switch comprised
:ensure nil to make sure elpaca knows there's nothing to download.flymake-mode to prog-mode-hook.The two functions for toggling showing diagnostics look like this
(defun mes/toggle-flymake-buffer-diagnostics ()
(interactive)
(if-let* ((window (get-buffer-window (flymake--diagnostics-buffer-name))))
(save-selected-window (quit-window nil window))
(flymake-show-buffer-diagnostics)))
(defun mes/toggle-flymake-project-diagnostics ()
(interactive)
(if-let* ((window (get-buffer-window (flymake--project-diagnostics-buffer (projectile-project-root)))))
(save-selected-window (quit-window nil window))
(flymake-show-project-diagnostics)))
And the changed keybindings are
| flycheck | flymake |
|---|---|
| flycheck-next-error | flymake-goto-next-error |
| flycheck-previous-error | flymake-goto-prev-error |
| mes/toggle-flycheck-error-list | mes/toggle-flymake-buffer-diagnostics |
| mes/toggle-flycheck-projectile-error-list | mes/toggle-flymake-project-diagnostics |
with-eval-after-load instead of :after eglot
When it comes to use-package I keep on being surprised, and after the switch
to elpaca I've found some new surprises. One of them was that using :after
eglot like this
(use-package haskell-ng-mode
:afer eglot
:ensure (:type git
:repo "git@gitlab.com:magus/haskell-ng-mode.git"
:branch "main")
:init
(add-to-list 'major-mode-remap-alist '(haskell-mode . haskell-ng-mode))
(add-to-list 'eglot-server-programs '(haskell-ng-mode "haskell-language-server-wrapper" "--lsp"))
(setq-default eglot-workspace-configuration
(plist-put eglot-workspace-configuration
:haskell
'(:formattingProvider "fourmolu"
:plugin (:stan (:global-on :json-false)))))
...
:hook
(haskell-ng-mode . eglot-ensure)
...)
would delay initialisation until after eglot had been loaded. However, it
turned out that nothing in :init ... seemed to run and upon opening a haskell file
no mode was loaded.
After a bit of thinking and tinkering I got it working by removing :after
eglot and using with-eval-after-load
(use-package haskell-ng-mode
:ensure (:type git
:repo "git@gitlab.com:magus/haskell-ng-mode.git"
:branch "main")
:init
(add-to-list 'major-mode-remap-alist '(haskell-mode . haskell-ng-mode))
(with-eval-after-load 'eglot
(add-to-list 'eglot-server-programs '(haskell-ng-mode "haskell-language-server-wrapper" "--lsp"))
(setq-default eglot-workspace-configuration
(plist-put eglot-workspace-configuration
:haskell
'(:formattingProvider "fourmolu"
:plugin (:stan (:global-on :json-false))))))
...
:hook
(haskell-ng-mode . eglot-ensure)
...)
That change worked for haskell, and it seemed to work for python too, but after a little while I realised that python needed a bit more attention.
The python setup looked like this
(use-package python
:init
(add-to-list 'major-mode-remap-alist '(python-mode . python-ts-mode))
(with-eval-after-load 'eglot
(assoc-delete-all '(python-mode python-ts-mode) eglot-server-programs)
(add-to-list 'eglot-server-programs
`((python-mode python-ts-mode) . ,(eglot-alternatives
'(("rass" "python") "pylsp")))))
...
:hook (python-ts-mode . eglot-ensure)
...)
and it worked all right, but then I visited the package (using elpaca-visit)
and realised that the downloaded package was all of emacs. That's a bit of
overkill, I'd say.
However, adding :ensure nil didn't have the expected effect of just using the
version that's in core. Instead the whole configuration seemed to never take
effect and again I was back to the situation where I had to jump to
python-ts-mode manually.
The documentation for use-package says that :init is for
Code to run before PACKAGE-NAME has been loaded.
but I'm guessing "before" isn't quite before enough. Then I noticed :preface
with the description
Code to be run before everything except
:disabled; this can be used to define functions for use in:if, or that should be seen by the byte-compiler.
and yes, "before everything" is early enough. The final python configuration looks like this
(use-package python
:ensure nil
:preface
(add-to-list 'major-mode-remap-alist '(python-mode . python-ts-mode))
:init
(with-eval-after-load 'eglot
(assoc-delete-all '(python-mode python-ts-mode) eglot-server-programs)
(add-to-list 'eglot-server-programs
`((python-mode python-ts-mode) . ,(eglot-alternatives
'(("rass" "python") "pylsp")))))
...
:hook (python-ts-mode . eglot-ensure)
...)
I'm still not sure I have the correct intuition about how to use use-package,
but hopefully it's more correct now than before. I have a growing suspicion
that use-package changes behaviour based on the package manager I use. Or
maybe it's just that some package managers make use-package more forgiving of
bad use.
-1:-- More on the switch to eglot (Post Magnus)--L0--C0--2026-01-25T13:18:00.000Z
TMR provides facilities for setting timers using a convenient notation. Lots of commands are available to operate on timers, while there also exists a tabulated view to display all timers in a nice grid.
tmrBelow are the release notes.
This version adds some user options and new features to an already stable package.
The command tmr-toggle-pause will prompt for a running timer and
pause it.
Users who have something like the following in their configuration,
have access to tmr-toggle-pause under the P key:
;; All TMR commands are behind this prefix key. So `tmr-toggle-pause' is C-c t P.
(define-key global-map (kbd "C-c t") #'tmr-prefix-map)
In the buffer produced by the command tmr-tabulated-view the pause
functionality applies to the timer at point. The tmr-toggle-pause is
invoked with the P key.
An extra column in the tabulated view shows whether a timer is paused or not. Here is an example:
Start End Duration Remaining Paused? Acknowledge? Description
08:49:41 09:19:46 30m 29m 17s Yes Work on TMR for 30 minutes
08:49:31 08:54:31 5m 3m 53s Prepare tea
08:49:21 08:59:21 10m 8m 42s Yes Edit the description with this one instead
When a timer is set to be acknowledged (i.e. the user must confirm
that they saw it elapse) it prompts for confirmation. The default
input text that confirms the acknowledgement is ack. This is now
subject to configuration via the user option tmr-acknowledge-timer-text.
The buffer produced by tmr-tabulated-view is set to automatically
refresh every 5 seconds by default. In previous versions this was every
1 second. The new user option tmr-tabulated-refresh-interval can be
set to a number of seconds or nil. In the latter case, the automatic
refresh is disabled.
tmr-tabulated.elIts code was merged into tmr.el on 2024-10-30 and all it was doing
thenceforth is issue a warning to those who would require the
tmr-tabulated feature. Now (require 'tmr-tabulated) produces an
error.
-1:-- Emacs: tmr version 1.3.0 (Post Protesilaos Stavrou)--L0--C0--2026-01-25T00:00:00.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!