macOS uses some common Emacs editing keystrokes by default, and that
You can do much better by adding other Emacs keystrokes to those that macOS will recognize.
Here’s one such recent reminder. Now Bozhidar Batsov has turned the tables and revealed that Emacs also recognizes many macOS commands1. If you look in ns-win.el you’ll see a rather long list of macOS bindings that Emacs will recognize. You can, for example, save a file with ⌘ Cmd+s or start an incremental search with ⌘ Cmd+f. There’s a surprisingly large number of other such bindings. Take a look at Batsov’s post or ns-win.el for the details.
I didn’t know about any of this except for using ⌘ Cmd+f for isearch. I stumbled on that because I rebound Ctrl+s to swiper-isearch but sometimes wanted to do a regular incremental search. Even then, it didn’t click that Emacs was emulating macOS keybindings.
Other than that—and examples like it that other users may need—I can’t see the point of using those bindings. It seems like just another way of confusing my muscle memory. Of course, as I’ve confessed before, I’m very bad at handling multiple sets of keybindings.
For me, it makes more sense to make Emacs keybinding available to macOS rather than the other way around. Of course, you may disagree and want to standardize on macOS bindings. The great thing is that, as usual, Emacs2 let’s us have it our way.
To kickstart the Emacs Carnival, and as a hat tip to our recent inspiration, this month’s topic is borrowed from the June IndieWeb Blog Carnival: the topic is “Take Two”, hosted by Nick Simon:
Ever wish for a do-over? “Take two!” (or three, four, etc.) might be shouted by a film director or audio engineer looking to get a somewhat different outcome from a group of actors or musical performers. Would you like a second shot at something that didn’t land?
This is a perfect fit for Emacs users:
We usually don’t nail “it” on our first try – init.el bancruptcy; refactoring hacky Emacs Lisp code; leaving Emacs only to come back englightened much later; running two Emacsens in parallel. There are plenty of possible second takes when it comes to Emacs!
So for this month, our first community blog carnival, I want you to:
mediate on “Emacs” and “take two”,
blog about it,
then send me a link to your blog post.
That’s it! I’ll aggregate submissions for all of June in this post.
Don’t have a blog, yet?
Well, just start one already! :)
It’s the future of the internet!
Blogging is the future, and the future is now!
What’s a Blog Carnival, Again?
This is our first Emacs Carnival, so you may not know the format. A blog carnival is a fun way to tie together a community with shared writing prompts, and marvel at all the creative interpretations of the topic of the month. I’ve provided a couple of interpretations above, but you may think of something else entirely. That’s amazing, roll with it, that’s what makes this fun!
Thanks to Sacha Chua for her post “Working on the plumbing of a small web community” that was part of the May 2025 IndieWeb Carnival, and got me thinking why there’s no Emacs Carnival, yet, and for her encouragement to kickstart this.
For future Carnivals, check out the “Carnival” page on EmacsWiki . It includes instructions, and is our community space to coordinate participants and topic ideas.
Submissions
Comment below or DM/email me with your submission! I’ll collect submissions up to, and including, July 1st (Central European Time), so that every time zone has had a chance to meet the June 30th deadline :)
Charles Choi has another great addition to his Casual Suite. This time he helps us with the common problem of figuring out what time it is in some other place. As Choi says, the Internet makes it common to communicate regularly with people from all over the world. Even the introverts at Irreal find themselves dealing with this.
Choi says that the most common way of dealing with the problem is with a clock app—which typically show times from all over the world—or just looking it up on the Internet. I do the latter. When I want to communicate with someone in, say, London, I simply search for something along the lines of “what time is it in London?”.
Choi notes that Emacs already has all the machinery to deal with this sort of thing. It just needs to be pulled together into a user accessible package. That’s what Casual Timezone does. It provides a myriad of ways to map the time at one place to the time at another.
At this point, the Casual Suite has so many useful apps that it’s probably best to simply install the whole suite even if there are some that you don’t use. Choi’s use of transient menus makes it easy to use them without having to deal with a bunch of bindings that you seldom use and probably won’t remember. Take a look at his post for an animation of Casual Timezone in action and see it it’s something that might fit your needs.
A couple of people wrote back to me regarding my last post about the challenges I have with Journelly (which, again, don’t really have anything to do with the app itself, but my workflow).
In an Email, HTH let me know that while Syncthing is not officially supported on iOS, there is an app that works and does the job just fine: SyncTrain. I tested it, and it successfully synced my files to my iPhone, iCloud not included. That’s an amazing find.
Meanwhile, I was thinking about how I can better streamline my process of refiling Journelly’s entries to my journal, where they are… archived. Duh! I can simply use org-mode to archive entries into my journal file. All I need to do is define the file I want them to archive into in the file options at the start of the file. Journelly even has an archive feature built in, but since I want to save my entries off the phone, it won’t work for me.
However, I realized I might not want to send my Journelly entries away. Journelly is convenient, and I use it constantly for notes. Having these available on my iPhone (where Journelly has an excellent search feature with tags) and on Emacs on the Mac at the same time is a boon to my productivity, not to mention, it just looks so nice on the phone.
So now I’m considering a different mental approach. Instead of refiling and moving entries away from Journelly, I’m going to try and expand on what I have there later. Some of this I already discussed before: things like meeting entries, for example, can be copied later to my dedicated meeting file, and activities can be copied to my event file, if I feel there’s more to add. The missing piece is expanding on “mind dumps” in Journelly that go into tangents. For that, I want to break the ideas into specific subjects with Denote later. For example, if I write an entry in the morning about Journelly and I’m realizing I’m rambling about the app and have ideas, I can later put these ideas into a “Journelly thoughts” note later with Denote, along with the appropriate keywords and attachments, as needed. This, I think, also covers the concern I have regarding privacy and iCloud, as the Journelly entry will just cover the initial nugget, where I will write some key points which I will later develop in a dedicated note off Apple’s servers.
This is all pretty new and raw in my head, so I’m going to try that out for the rest of the week and see how I feel.
While packages like orderless provide flexible “any word, any order” completion, sometimes you want something lightweight and easy to tweak (well I do anyway). In this post, I’ll show you how to implement a simple orderless-like completion style using only Emacs Lisp, and how to integrate it smoothly into your workflow.
Traditional completion in Emacs often matches prefixes or substrings, but sometimes you want to type a few key parts of a word, in any order, and jump straight to your target. That’s what orderless and similar completion styles allow. But what if you want to write your own, or experiment with the logic?, well I will show you how…
The user’s input (a string) is split into words on spaces, dashes, or commas. This produces a list of “search terms.” This means that, in the minibuffer, the word separator can be any of these characters. I was initially really faffing around and struggling to work out how to insert a space between words in the minibuffer, as it seems to perform some form of completion. However, I eventually figured out that M-SPC actually inserts a space, allowing you to separate words. I use fido-mode, so I’m not sure if this is the same for other minibuffer completion systems.
After initially adding in the comma separator however I found that I actually prefer it, it is easier to access and I don’t think any keywords, functions e.t.c will typically contain a comma?
Pattern Construction:
For each word, a regex pattern is constructed: \\b.*WORD.*.
This means: “find a word boundary, followed by any characters, then the word, then anything else.” This is a bit looser than strict word matching, and you can tune it.
Candidate Filtering:
We generate all possible completions with all-completions and then filter them down. For a candidate to match, all the search terms (words) must appear somewhere, in any order.
Case Sensitivity:
Matching respects completion-ignore-case, so your results will be case-insensitive if you want of course.
Registering and Using the Style
To make Emacs aware of your new completion style, add it to completion-styles-alist:
You might not want this style everywhere (which I suspect is likely). For example, in file completion you might prefer strict prefix matching. So, let’s activate it only in the minibuffer:
(defun setup-minibuffer-completion-styles ()
"Use orderless completion in minibuffer, regular completion elsewhere.";; For minibuffer: use orderless first, then fallback to flex and basic (setq-local completion-styles '(basic simple-orderless flex substring)))
;; Hook into minibuffer setup(add-hook 'minibuffer-setup-hook#'setup-minibuffer-completion-styles)
Tweaking and Extending
Pattern Tuning:
The regexes can be made stricter or looser (e.g., remove \\b for more “fuzzy” matching).
Word Separators:
You can split on other characters if your workflow uses different delimiters.
Order of Styles:
Adjust the order in completion-styles to prefer your custom style over others. I found that if the simple-orderless style was listed first, pressing Tab to bring up the completions buffer doesn’t work, which I like to use sometimes, so that is why basic is first.
Conclusion
With just a handful of lines, you can build your own orderless-like completion style, giving you full control and transparency. This is a great starting point for experimenting with more advanced completion logic, and a good illustration of the power of Emacs’ built-in completion framework!
Bozhidar Batsov is back with a quick tip that many of you will probably find useful. It’s an improvement to keyboard-quit or, as we all know and love it, Ctrl+g.
One of Batsov’s pet peeves is that keyboard-quit doesn’t function as expected when the minibuffer is active. Happily that and a few other infelicities are easily fixed as he shows in his post. The code that Batsov presents in his post is short. It’s more comment than code yet it does fix the problems that were annoying Batsov.
Here, from the code comments, is what his improved code does:
The DWIM behaviour of this command is as follows:
- When the region is active, disable it.
- When a minibuffer is open, but not focused, close the minibuffer.
- When the Completions buffer is selected, close it.
- In every other case use the regular `keyboard-quit'."
You don’t have to edit Emacs source code. You simply include his function in your init.el and remap Ctrl+g to it. It’s easy to try out his code and if you don’t like it, simply drop back to the default behavior by undoing the key sequence remapping.
I just published a video about a Reddit post in which an Obsidian user asks about Emacs: pros and cons, and “aha” moments.
I’m not big on software supremacy type arguments. Whatever tool you decide to use is the best tool for you. I don’t see the point in forever touting one tool over another.
However, I don’t want to downplay the philosophical side of the argument. People who spend time carefully plotting out software choices are no different than laborers in any other field who want to pick the best tool for whatever job they’re doing.
For whatever reason (and I would be curious to get reader comments on this), users of open source tools tend to be more philosophical, I’ve noticed, than users of Windows or Mac products. What I mean by that is, FOSS users tend to value choice, and with that choice there is a greater assumption that you can make wise choices and unwise choices.
Quick example. A Mac user who needs to do some design work has no choice: they have to shell out for a Photoshop license. (They could consider using something like GIMP, but for most users that option would be too beyond the pale, and they might fear the difficulty of integrating a non-standard open source tool within their existing workflow.)
On the other hand, someone who has grown accustomed to open source tools takes on a whole different set of questions. “Can I use GIMP for my project? Of course, but do I need to use it? Couldn’t I just assemble my graphical items into a script and pipe it into ffmpeg? What is the best tool for this particular job? What is my ideal result? Should I favor speed or accuracy? Or both?”
Anyway… back to the Obsidian question.
Obsidian is a fine tool. If anyone is purely interested in taking notes and does not use Emacs already, I’d recommend just sticking with Obsidian. It does exactly what you need it to do.
However, if you’re using Emacs already, and you’re comfortable with the interface, I couldn’t think of a good reason to use Obsidian, unless you want to show off a cool hyperlinked graph of your notes and tags.
A few of the other points I mentioned in the video:
Emacs provides a superior writing environment.
You can code your own note system with Emacs lisp.
Emacs’s longevity makes it a safer choice for long term projects.
Whatever tool you decide to use, it’s always best to first take a step back and decide what your ideal outcome would be. Then you can more quickly sample different tools and get an idea of which one will work best for you.
Tree-sitter has taken the world of programming by a storm.
Together with LSP, it’s probably the technology that has influenced the
most programming editors and IDEs in the past several years.
And now that Emacs 29+ comes with built-in Tree-sitter support
I’ve been spending a lot of quality time with it, working
on clojure-ts-mode and neocaml-mode.
There’s a lot I’d like to share with you about using Tree-sitter effectively, but
today I’ll focus on a different topic. When most people hear about Tree-sitter
they think of font-locking (syntax highlighting) and indentation powered by
the abstract syntax tree (AST), generated by a Tree-sitter grammar.
For a while I’ve also been thinking that the AST data can also be used
for simple, yet reasonably accurate, code completion. (within the context of
a single code buffer, that is) That’s definitely not nearly as powerful
of what you’d normally get from a dedicated tool (e.g. an LSP server), as
those usually have project-wide completion capabilities, but it’s pretty
sweet given that it’s trivial to implement and doesn’t require any
external dependencies.
Below, you’ll find a simple proof of concept for such a completion, in the context
of clojure-ts-mode:1
(defvarclojure-ts--completion-query-globals(treesit-query-compile'clojure`((source(list_lit((sym_lit)@sym(:match,clojure-ts--variable-definition-symbol-regexp@sym)):anchor[(comment)(meta_lit)(old_meta_lit)]:*:anchor((sym_lit)@var-candidate)))(source(list_lit((sym_lit)@sym(:match,clojure-ts--function-type-regexp@sym)):anchor[(comment)(meta_lit)(old_meta_lit)]:*:anchor((sym_lit)@fn-candidate))))))(defconstclojure-ts--completion-annotations(list'var-candidate" Global variable"'fn-candidate" Function"))(defunclojure-ts--completion-annotation-function(candidate)(thread-lastminibuffer-completion-table(alist-getcandidate)(plist-getclojure-ts--completion-annotations)))(defunclojure-ts-completion-at-point-function()(when-let*((bounds(bounds-of-thing-at-point'symbol))(source(treesit-buffer-root-node'clojure))(nodes(treesit-query-capturesourceclojure-ts--completion-query-globals)))(list(carbounds)(cdrbounds)(thread-lastnodes(seq-filter(lambda(item)(not(equal(caritem)'sym))))(seq-map(lambda(item)(cons(treesit-node-text(cdritem)t)(caritem))))):exclusive'no:annotation-function#'clojure-ts--completion-annotation-function)))
I hope you’ll agree that the code is both simple and easy to follow (especially
if you know a bit about Tree-sitter queries and Emacs’s completion APIs). The
meat of the example is clojure-ts--completion-annotation-function, the rest is
just completion scaffolding.
And the result looks like this:
Not too shabby for 30 lines of code, right? With a bit more efforts this can be made
smarter (e.g. to include local bindings as well), and potentially we can even be
consulting all open buffers running clojure-ts-mode to fetch completion data
from the as well. (although that’s probably an overkill)
Still, I think that’s an interesting use of Tree-sitter that some of you might
find useful. It seems that Nic Ferrier has been playing with this idea recently
as well - check out his recent video on the subject
here.
In time Tree-sitter will redefine how we’re building Emacs major modes and what they can do.2
It’s still early days and sky is the limit. Exciting times ahead!
That’s all I have for you today. Keep hacking!
P.S. I plan to write more on the topic of Tree-sitter and how to use it
in Emacs major modes, but in the mean time you might find some of my development notes
useful:
Today’s article is going to be a bit more weird than usual… mostly because I’ve
set to write about one topic, and ended up about writing something completely different
in the end… Here we go!
TL;DR Many common macOS keybindings (e.g. Command-s, Command-z,
Command-f, etc) work in Emacs. And, of course, it’s well known that
macOS uses by default Emacs-like (readline) keybindings everywhere. (e.g. C-a and C-e)
I’m guessing 99% of Emacs users know that the most common ways to start isearch
are with isearch-forward (C-s) and isearch-backward (C-r). That’s not
the full story, though! While working on my recent isearch article I noticed that
out-of-the-box there are two other keybindings for those commands:
s-f (isearch-forward)
s-F (isearch-backward)
Note:s in this context means Super, which is usually Win in Windows and Command
in macOS.
When I saw those I was like “hmm, seems someone wanted to make Emacs a bit more
approachable to macOS users coming other editors”. But here things got
interesting…
I tried to find out where those extra keybindings were defined, and
after a bit of digging I found them in the ns-win.el library1, which defines a
ton of macOS-specific keybindings:
;; Here are some Nextstep-like bindings for command key sequences.(define-keyglobal-map[?\s-,]'customize)(define-keyglobal-map[?\s-']'next-window-any-frame)(define-keyglobal-map[?\s-`]'other-frame)(define-keyglobal-map[?\s-~]'ns-prev-frame)(define-keyglobal-map[?\s--]'center-line)(define-keyglobal-map[?\s-:]'ispell)(define-keyglobal-map[?\s-?]'info)(define-keyglobal-map[?\s-^]'kill-some-buffers)(define-keyglobal-map[?\s-&]'kill-current-buffer)(define-keyglobal-map[?\s-C]'ns-popup-color-panel)(define-keyglobal-map[?\s-D]'dired)(define-keyglobal-map[?\s-E]'edit-abbrevs)(define-keyglobal-map[?\s-L]'shell-command)(define-keyglobal-map[?\s-M]'manual-entry)(define-keyglobal-map[?\s-S]'ns-write-file-using-panel)(define-keyglobal-map[?\s-a]'mark-whole-buffer)(define-keyglobal-map[?\s-c]'ns-copy-including-secondary)(define-keyglobal-map[?\s-d]'isearch-repeat-backward)(define-keyglobal-map[?\s-e]'isearch-yank-kill)(define-keyglobal-map[?\s-f]'isearch-forward)(define-keyesc-map[?\s-f]'isearch-forward-regexp)(define-keyminibuffer-local-isearch-map[?\s-f]'isearch-forward-exit-minibuffer)(define-keyisearch-mode-map[?\s-f]'isearch-repeat-forward)(define-keyglobal-map[?\s-F]'isearch-backward)(define-keyesc-map[?\s-F]'isearch-backward-regexp)(define-keyminibuffer-local-isearch-map[?\s-F]'isearch-reverse-exit-minibuffer)(define-keyisearch-mode-map[?\s-F]'isearch-repeat-backward)(define-keyglobal-map[?\s-g]'isearch-repeat-forward)(define-keyglobal-map[?\s-h]'ns-do-hide-emacs)(define-keyglobal-map[?\s-H]'ns-do-hide-others)(define-keyglobal-map[?\M-\s-h]'ns-do-hide-others)(define-keyglobal-map[?\s-j]'exchange-point-and-mark)(define-keyglobal-map[?\s-k]'kill-current-buffer)(define-keyglobal-map[?\s-l]'goto-line)(define-keyglobal-map[?\s-m]'iconify-frame)(define-keyglobal-map[?\s-n]'make-frame)(define-keyglobal-map[?\s-o]'ns-open-file-using-panel)(define-keyglobal-map[?\s-p]'ns-print-buffer)(define-keyglobal-map[?\s-q]'save-buffers-kill-emacs)(define-keyglobal-map[?\s-s]'save-buffer)(define-keyglobal-map[?\s-t]'menu-set-font)(define-keyglobal-map[?\s-u]'revert-buffer)(define-keyglobal-map[?\s-v]'yank)(define-keyglobal-map[?\s-w]'delete-frame)(define-keyglobal-map[?\s-x]'kill-region)(define-keyglobal-map[?\s-y]'ns-paste-secondary)(define-keyglobal-map[?\s-z]'undo)(define-keyglobal-map[?\s-+]'text-scale-adjust)(define-keyglobal-map[?\s-=]'text-scale-adjust)(define-keyglobal-map[?\s--]'text-scale-adjust)(define-keyglobal-map[?\s-0]'text-scale-adjust)(define-keyglobal-map[?\s-|]'shell-command-on-region)(define-keyglobal-map[s-kp-bar]'shell-command-on-region)(define-keyglobal-map[?\C-\s-]'ns-do-show-character-palette)(define-keyglobal-map[s-right]'move-end-of-line)(define-keyglobal-map[s-left]'move-beginning-of-line)(define-keyglobal-map[home]'beginning-of-buffer)(define-keyglobal-map[end]'end-of-buffer)(define-keyglobal-map[kp-home]'beginning-of-buffer)(define-keyglobal-map[kp-end]'end-of-buffer)(define-keyglobal-map[kp-prior]'scroll-down-command)(define-keyglobal-map[kp-next]'scroll-up-command);; Allow shift-clicks to work similarly to under Nextstep.(define-keyglobal-map[S-mouse-1]'mouse-save-then-kill)(global-unset-key[S-down-mouse-1]);; Special Nextstep-generated events are converted to function keys. Here;; are the bindings for them. Note, these keys are actually declared in;; x-setup-function-keys in common-win.(define-keyglobal-map[ns-power-off]'save-buffers-kill-emacs)(define-keyglobal-map[ns-open-file]'ns-find-file)(define-keyglobal-map[ns-open-temp-file][ns-open-file])(define-keyglobal-map[ns-open-file-line]'ns-open-file-select-line)(define-keyglobal-map[ns-spi-service-call]'ns-spi-service-call)(define-keyglobal-map[ns-new-frame]'make-frame)(define-keyglobal-map[ns-toggle-toolbar]'ns-toggle-toolbar)(define-keyglobal-map[ns-show-prefs]'
Some of them look quite convenient (easy to press), so I might add a few
to my daily work. I’m shocked I never trying any of the standard macOS
keybindings for things like adjusting text size in Emacs. Or perhaps I tried
them and then I forgot about them… :D
Still, even though I’m a macOS users (at least for the time being), I doubt I’ll end
up using many of them. The reason for this is that I learned Emacs on Linux
and I’m extremely used to the default keybindings. Between remembering all of those,
and trying to master Vim (as of late), it’s hard to teach this old dog any new tricks.
That being sad, I can imagine those keybindings being useful to many other people, especially
if they haven’t learned Emacs on Linux 20 years ago.
Tip: Do a M-x find-library RET ns-win to see what else the library has in store
for macOS users.
All of this is, of course, made possible by the fact that macOS relies heavily on the
Command key which normally isn’t used in Emacs at all. For similar reasons it’s
“easier” to copy/paste text from/in your shell on macOS, compared to Linux and Windows, as
keybindings like Command + c and Command + v are not used by any shell.
That’s all I have for you today! Keep hacking!
P.S. After writing this article I was really amused that I’ve been using macOS on and
off for over 10 years and I never bothered to try whether something like Command-s or
Command-z works in Emacs! Oh, well… habits!
Emacs stubbornly keeps refering to macOS by its ancient name NextStep in much of the code and its documentation. ↩
Lately, I've been messing with Codelist Tools, a Rust library for working with medical codelists. Among other things, it uses PyO3 to provide bindings that enable its use from Python. I had not tried this before. It's fun!
It occurred to me that it should also work from Hy, a Lisp dialect for Python. So, I gave it a try…and it does!
Running either of these produces
Over at the Emacs subreddit, EachDaySameAsLast asks the perennial question: why does Emacs take so long to load. Actually, his question is, “Why do people say Emacs takes so long to load?” He has, he says, been using Emacs since its TECO days and has never, even in the old days, experienced overly long load times. To be sure, his configuration file is relatively short—about 100 lines—but Emacs loads for him in under 2 seconds.
Of course, as many of us have been saying and saying, none of that matters. For almost every user, Emacs shouldn’t be started very often: once a day at most, once every week or month typically.
As usual, the interesting part of the post is the comments. Almost everyone agrees: the load times don’t matter but if you use use-package and the defer option judiciously, your load time can be small too.
I’m beginning to feel as if I’m codependent with those people claiming that Emacs takes too long to load. I should probably just stop writing about it. Of course, it won’t matter. People—many of whom don’t even use Emacs—will keep repeating it.
But let me just repeat, with Batsov, that Emacs load time doesn’t matter. It really doesn’t.
Because the Internet, it is a blessing of our time that interacting with people in different time zones is commonplace. In this, the question is often raised: “what time is it there?” This is routinely answered using a clock app or a website.
While Emacs has long had the ability to make time zone calculations, it seemed overdue to have tooling that takes advantage of it.
The following screencast shows Casual Timezone in action, in this case a meeting planner (command: casual-timezone-planner) that compares the hours with your local time zone with that of another.
Casual Timezone also lets you directly ask:
“What time is it over there?” (command: casual-timezone-local-time-to-remote)
“That time over there, what is it here?” (command: casual-timezone-remote-time-to-local)
Note that at current, Casual Timezone only supports Unix-variant systems as it relies on the tz database. It has been tested on macOS 15.5 and Ubuntu Linux 22.04. Sorry Windows users, but I’m open for a PR for Windows support if a workaround for zoneinfo is available.
Closing Thoughts
With experience, I’ve learned to be humble about working with time APIs. While I’ve done much to ensure the results are accurate, I would not be surprised if there are bugs, especially for a first release. Let me know if you find any.
On reflection, putting Casual Timezone together was relatively easy as it was largely an exercise in integrating different Elisp packages:
From Org, the command org-read-date provided the date picker UI.
Built-in completion provided the UI for selecting a time zone.
vtable provided the table interface for casual-timezone-planner.
The table interface was configured as a derived mode to provide mode-specific behavior.
The built-in time functions were used for time zone calculations and formatting.
Transient provided support for menus.
Gonna call this an Elisp code-reuse success story.
I’ll be pretty brief today. keyboard-quit (C-g) is one of the most
used commands, but unfortunately it’s not very smart. Most annoyingly,
it doesn’t work as expected when the minibuffer is active.
Fortunately, fixing such problems (and then some) is trivial in Emacs:
(defuner-keyboard-quit()"Smater version of the built-in `keyboard-quit'.
The generic `keyboard-quit' does not do the expected thing when
the minibuffer is open. Whereas we want it to close the
minibuffer, even without explicitly focusing it."(interactive)(if(active-minibuffer-window)(if(minibufferp)(minibuffer-keyboard-quit)(abort-recursive-edit))(keyboard-quit)))
I’d suggest to just remap keyboard-quit to our improved version:
There are other ways to tackle this particular issue, of course,
and different people might prefer an even more complicated
version of the smarter keyboard-quit or one that does fewer
things. One of my readers suggested in the comments a similar
solution using an advice:
(define-advicekeyboard-quit(:around(quit)quit-current-context)"Quit the current context.
When there is an active minibuffer and we are not inside it close
it. When we are inside the minibuffer use the regular
`minibuffer-keyboard-quit' which quits any active region before
exiting. When there is no minibuffer `keyboard-quit' unless we
are defining or executing a macro."(if(active-minibuffer-window)(if(minibufferp)(minibuffer-keyboard-quit)(abort-recursive-edit))(unless(ordefining-kbd-macroexecuting-kbd-macro)(funcall-interactivelyquit))))
This has the benefit of directly modifying the original command, so you don’t
really need to rebind anything. On the other hand - advices are arguably
a bit more complicated to understand and debug. Personally, I like
to replace functions in my own setup with versions that I prefer,
as I think this makes the modifications more obvious.
(defunprot/keyboard-quit-dwim()"Do-What-I-Mean behaviour for a general `keyboard-quit'.
The generic `keyboard-quit' does not do the expected thing when
the minibuffer is open. Whereas we want it to close the
minibuffer, even without explicitly focusing it.
The DWIM behaviour of this command is as follows:
- When the region is active, disable it.
- When a minibuffer is open, but not focused, close the minibuffer.
- When the Completions buffer is selected, close it.
- In every other case use the regular `keyboard-quit'."(interactive)(cond((region-active-p)(keyboard-quit))((derived-mode-p'completion-list-mode)(delete-completion-window))((>(minibuffer-depth)0)(abort-recursive-edit))(t(keyboard-quit))))
I know this version of the command is quite popular in the wild, as many
people follow Prot’s work, but looking at the code of the actual keyboard-quit
it seems to me that Prot’s version is more complicated than it needs to be:
;; This executes C-g typed while Emacs is waiting for a command.;; Quitting out of a program does not go through here;;; that happens in the maybe_quit function at the C code level.(defunkeyboard-quit()"Signal a `quit' condition.
During execution of Lisp code, this character causes a quit directly.
At top-level, as an editor command, this simply beeps."(interactive);; Avoid adding the region to the window selection.(setqsaved-region-selectionnil)(let(select-active-regions)(deactivate-mark))(if(fboundp'kmacro-keyboard-quit)(kmacro-keyboard-quit))(whencompletion-in-region-mode(completion-in-region-mode-1));; Force the next redisplay cycle to remove the "Def" indicator from;; all the mode lines.(ifdefining-kbd-macro(force-mode-line-updatet))(setqdefining-kbd-macronil)(let((debug-on-quitnil))(signal'quitnil)))
As you can see it already handles things like the selected region
and completion in region. But perhaps I’m missing what Prot was
trying to achieve with his version.
Which of the three approaches do you prefer?
How would you improve er-keyboard-quit-dwim further?
Journelly keeps getting updated with good features. One of the latest features I noticed (I’m not sure if it was part of the latest patch) is that locations tagged with entries can be revisited in iOS Maps from the app. This makes Journelly a good spot to save locations and integrate them with personal memories, to be revisited later on the map. I would like to use this (instead of, say, a saved of locations on Google Maps), but I can’t - for two reasons.
The first one, which I can work around, is that the list of entries on Journelly quickly becomes long, and finding where you were a couple of weeks ago requires some scrolling. While Journelly has a search option, I usually don’t remember the name of the place I’m searching for, which is why I’m searching for it in the first place. Still, I could probably look for who I was with at the time, or even better, use the tagging feature, which was introduced a couple of weeks ago, for, say, “#cafes” to filter down cafes only.
The other issue is more challenging: I don’t keep my notes in Journelly. I keep refiling my entries into my main journal file. That file, while still on my Mac, is not synced with iCloud. Call me paranoid, but I don’t trust Apple’s iCloud with my personal notes along with my pictures throughout the years. As far as I’m concerned, Apple just has a better PR department than Google and Microsoft, and they only care about their users' privacy as long as it’s what looks good in the news. Because of that, I am not comfortable with Journelly being my archive of notes. There could be other options besides iCloud, but as far as I know, they all involve a cloud company somewhere. On Android and macOS, I still use the excellent Syncthing, which doesn’t involve any cloud storage. However, Syncthing doesn’t work on iOS, so I’m out of luck.
Besides these two issues (which have nothing to do with the app), the app is terrific. It’s amazing how polished and responsive it is.
Joar Von Arndt has an interesting post on a subject that many Emacs users obsess about: how to bring Web browser functionality into Emacs. As, I’ve said many times, virtually all my tube time is either in Emacs or Safari. I would, of course, like to get that down to just Emacs. To be sure, I do use some other apps but my time with them is basically in the noise. Almost everything I do on the computer involves Emacs or the browser.
Von Arndt looks at eww, w3m, Xwidgets, and EAF. He discusses the pros and cons of each. He appears to think that eww—with some customization—is the best solution.
My solution is to use Xwidgets for rendering Email posts that need it and for my RSS feed via Elfeed. My email client, mu4e, makes it easy to switch between text and HTML rendered displays. It is, in a way, the best of both worlds. I can read most of my emails in plain text, as the elders decreed, but can switch to an HTML rendered display when I need to.
I use elfeed-webkit to display my RSS feed with Elfeed. It brings up each entry in a browser like display and, of course, can be easily be toggled on and off. It’s a bit fragile, as Von Arndt says, but it easier than invoking the brower for each entry.
The sad news is that there still isn’t a good solution but what solutions there are are getting better. Perhaps we will soon have a way of bringing the final major holdout into the Emacs fold.
The IndieWeb Carnival prompt for May is small web
communities. I've been exploring some thoughts on
how a little effort goes a long way to connecting
a community. Sometimes I think of it as working on
the plumbing so that ideas can flow more smoothly.
It feels a little different from the direct
contribution of knowledge or ideas. I also want to
connect with other people who do this kind of
thing.
Emacs is a text editor that has been around since
the 1970s. It's highly programmable, so people
have come up with all sorts of ways to modify it
to do what they want. It's not just for
programmers. My favourite examples include
novelists and bakers and musicians who use Emacs
in unexpected ways. Because Emacs is so flexible,
community is important. The source code and
documentation don't show all the possible
workflows. As people figure things out by
themselves and together, more possibilities open
up.
I love tweaking Emacs to help me with different
things I want to do, and I love learning about how
other people use it too. I've been sharing my
notes on Emacs on this blog since 2001 or so. In
2015, as I was getting ready to become a parent, I
knew I was going to have much less time and
focused attention, which meant less time playing
with Emacs. Fortunately, around that time, John
Wiegley (who was one of the maintainers of Emacs
at the time) suggested that it would be helpful if
I could keep an eye on community updates and
summarize them. This worked well with the
fragmentation of my time, since I could still
speed-read updates and roughly categorize them.
You don't have to fill the pipes all by yourself. Just help things flow.
I want to share some of the things we're doing in the Emacs community
so that I can convince you that building plumbing for your community can be fun, easy, and awesome.
This is great because enthusiasm spreads.
virtuous cycle
Other places: YouTube, Reddit, HN, lobste.rs, Mastodon, PeerTube, mailing lists….
Blog aggregator
Planet Emacs Life (uses Planet Venus) - update: [2025-05-31 Sat] I wrote my own RSS feed aggregator instead.
EmacsConf: < USD 50 hosting costs + donated server + volunteer time
Tips:
Make it fun for yourself.
Build processes and tools.
Let people help
2024-01-31-05
Some more notes on the regular flows built up by
this kind of community plumbing:
Daily: Lots of people post on reddit.com/r/emacs
and on Mastodon with the #emacs hashtag. I also
aggregate Emacs-related blog posts at
planet.emacslife.com, taking over from
planet.emacsen.org when Tess had DNS issues. There
are a number of active channels on YouTube and
occasionally some on PeerTube instances as well. I
don't need to do much work to keep this flowing,
just occasionally adding feeds to the aggregator
for planet.emacslife.com.
Weekly: I collect posts from different sources,
remove duplicates, combine links talking about the
same thing, categorize the links, put them roughly
in order, and post Emacs News to a website, an RSS
feed, and a mailing list. This takes me maybe 1.5
hours each week. It's one of the highlights of my
week. I get to learn about all sorts of cool
things.
Weekly seems like a good rhythm for me considering
how active the Emacs community is. Daily would be
too much time. Monthly would lead to either too
long of a post or too much lost in curation, and
the conversations would be delayed.
Sometimes I feel a twinge of envy when I check out
other people's newsletter posts with commentary or
screenshots or synthesis. (So cool!) But hey, I'm
still here posting Emacs News after almost ten
years, so that's something. =) A long list of
categorized links fits the time I've got and the
way my mind works, and other people can put their
own spin on things.
Monthly: There are a number of Emacs user
groups, both virtual and in-person. Quite a few of
them use Emacs News to get the discussion rolling
or fill in gaps in conversation, which is
wonderful.
Some meetups use meet.jit.si, Zoom, or Google
Meet, but some are more comfortable on a
self-hosted service using free software. I help by
running a BigBlueButton web conferencing server
that I can now automatically scale up and down on
a schedule, so the base cost is about 60 USD/year.
Scaling it up for each meetup costs about USD 0.43
for a 6-hour span. It's pretty automated now,
which is good because I tend to forget things that
are scheduled for specific dates. My schedule
still hasn't settled down enough for me to host
meetups, but I like to drop by once in a while.
Yearly:EmacsConf is the one big project I like
to work on. It's completely online. It's more of a
friendly get-together than a formal conference. I
have fun trying to fit as many proposed talks as
possible into the schedule. We nudge speakers to
send us recorded presentations of 5-20 minutes
(sometimes longer), although they can share live
if they want to. A number of volunteers help us
caption the videos. Each presentation is followed
by Q&A over web conference, text chat, and/or
collaborative document. Other volunteers handle
checking in speakers and hosting the Q&A sessions.
It's a lot of fun for surprisingly little money.
For the two-day conference itself, the website
hosting cost for EmacsConf 2024 was about USD 56
and our setup was able to handle 400 viewers
online (107 max simultaneous users in various web
conferences).
EmacsConf takes more time. For me, it's about 1.5
hours a day for 4 months, but I think mostly
that's because I have so much fun figuring out how
to automate things and because I help with the
captions. Lots of other people put time into
preparing presentations, hosting Q&A,
participating, etc. It's worth it, though.
I like doing this because it's a great excuse to
nudge people to get cool stuff out of their head
and into something they can share with other
people, and it helps people connect with other
people who are interested in the same things. Some
Q&A sessions have run for hours and turned into
ongoing collaborations. I like turning videos into
captions and searchable text because I still don't
have the time/patience to actually watch videos,
so it's nice to be able to search. And it's
wonderful gathering lots of people into the same
virtual room and seeing the kind of enthusiasm and
energy they share.
So yeah, community plumbing turns out to be pretty
enjoyable. If this resonates with you, maybe you
might want to see if your small web community
could use a blog aggregator or a newsletter.
Doesn't have to be anything fancy. You could start
with a list of interesting links you've come
across. I'm curious about what other people do in
their communities to get ideas flowing!
This is a small release that makes spacious-padding-mode work as
intended when used in tandem with the Emacs daemon and subsequent
calls to emacsclient -c. I made the function responsible for
triggering the “spacious padding” effects work with individual frames
and then I responded to issue 33 by Lou Woell about integrating that
with the server-after-make-frame-hook: https://github.com/protesilaos/spacious-padding/issues/33.
Additionally, the package now defines two faces that can be used to
configure the user option spacious-padding-subtle-mode-line (read
its documentation string for all the possible values it accepts).
Here is how they can be set (default value is nil):
Reload the spacious-padding-mode for changes to take effect.
When configured this way and with default styles they make the mode
line use a minimalist overline with no background colour. The active
mode line has a more noticeable border than the inactive ones. All my
themes are designed to support this aesthetic (though themes can style
those faces as they see fit).
About Spacious Padding
This package provides a global minor mode to increase the
spacing/padding of Emacs windows and frames. The idea is to make
editing and reading feel more comfortable. Enable the mode with M-x
spacious-padding-mode. Adjust the exact spacing values by modifying
the user option spacious-padding-widths.
Today, I was reading Why Use Structured Errors in Rust Applications? and one of the issues discussed is jumping away from the code you're working on to look at the definition of the error. On the one hand, rust-analyzer makes it really easy to jump to the error, but on the other hand they felt that they sometimes had to do so unnecessarily.
In the comments on lobste.rs, matklad mentioned that he recently adopted the habit of splitting the screen first and going to the definition in the split (in Emacs terms, in the other window).
Two weeks ago I wrote about using defcustom’s :get and :set keywords, allowing the user to set an option containing a Unix timestamp (that is, a number) using an ISO-8601 timestamp. Today I am going to use such an option.
Jeff Bradberry has an excellent post on adding timestamped notes to the LOGBOOK drawer in Org files. Most of us are used to using this drawer to record state changes in TODO notes. You can arrange to have the times of the state changes added to the LOGBOOK. You can also arrange to pop up a buffer so that you can add a note to the entry. I do this when the state changes to CANCELLED or WAITING. Sometimes you may want to add a note to a state change that doesn’t normally have one.
That turns out to be easy. You simple add the universal argument to the state change call. If you use the normal Ctrl+cCtrl+t binding, you would simply use Ctrl+uCtrl+cCtrl+t instead when you want to add a note.
Many of you probably already know all this but Bradberry thought it would nice to add arbitrary notes to the drawer as well. That turns out to be easy too. As usual, Emacs has us covered. You simply call org-add-note bound to Ctrl+cCtrl+z by default.
This post is the seventh in a series on building an Org-mode Workflow. The whole series is worthwhile and you should definitely take a look if you’re an Org user. There are links to the entire series in Bradberry’s post.
It seems as if everybody having even a passing acquaintance with Emacs is opining about Emacs load times. That includes Irreal, of course. Most of us take the position that it doesn’t really matter how long it takes Emacs to start because it’s something that happens only rarely.
Every serious person acknowledges that there are exceptions. A (very) few people have to restart Emacs often for various reasons but for the most part the majority of us have no reason to care. Of course, that doesn’t stop some people from caring. And caring a lot.
Over at the Programming subreddit, dormunis1 is proudly announcing that he’s gotten his Zsh load time to under 70 ms. This is down from the apparently normal load time of 0.45 seconds. If you don’t see the problem with this, you should seek help. These times are all below human reaction times so reducing them, while an interesting intellectual challenge, serves no real purpose.
As usual, a lot of the meat is in the comments. Most of the commenters aren’t sympathetic and agree that it’s an optimization without a point. To be sure, learning to do this sort of thing is useful for cases where it does matter. My objection—and that of the other commenters, apparently—is that reducing the load time of Zsh, Emacs, or any other user facing app, by a few milliseconds simply doesn’t matter.
My journey into using Hledger with Emacs to track my share investments continues. Last Time
I looked at a way to track shares using a plain text file. Since then I have made some tweaks to my workflow as I gain more experience:
naming conventions that support multiple stock brokers
booking brokerage fees in a better way to support the Australian tax system.
Naming convention
Over the past month I was not overly impressed with my existing stock broker, which led me to explore other stock brokers that may offer a more reliable service at a cheaper rate. It is expensive to transfer stocks over to a new broker, so I decided to cap further investment at my current broker and instead make any new purchases at a different broker. This meant that I would need an account structure that lets me easily identify which share lots were purchased through which broker.
I’ve been writing a lot lately about Álvaro Ramírez’s Journelly, an iOS app that I use constantly everyday. It’s a great app that I recommend for everyone. But Ramírez has been writing apps for Emacs and iOS for a long time. A previous iOS app of his that I used and liked—before Journelly largely replaced it—is scr that sort of implements an Emacs ∗scratch∗ buffer on your iPhone or iPad. I’ve also written extensively about his dwim-shell-command package that provides all sorts of command line shortcuts from within Emacs.
All of this is by way of an introduction to Ramírez’s recent post on tricks for using Emacs on macOS. It’s a long post with a plethora of strategies for using Emacs on Macs. He starts off by mentioning that he uses Emacs Plus—available from Homebrew—rather than compiling it from source, as he used to do. I still compile from source but on my next machine I may take the easy route and use Emacs Plus too.
Another thing he mentions is a point I often make: Although he was a GNU/Linux user for many years, he now works primary on macOS but this doesn’t matter because he does almost everything in Emacs or his browser so the he can get away with being pretty much OS agnostic. The true believers will gasp in horror but it’s a truth that many of us have discovered.
As for the tips, they are too numerous to list here. Most people probably won’t like all of them but I’d be surprised if anyone couldn’t find something useful in the post. For my part, I really liked his configuration for long press for accents and Plain Org for iOS. I expect to implement both of these in the near future.
If you use Emacs on macOS you should definitely take a look at Ramírez’s post. You’re sure to find something useful.
If you want to do statistical calculations with your computer, you probably want to use some sort of package to help you. When I was in school, we used Minitab (a "mini" version of OMNITAB— now there's a great name!), but I guess most statisticians used either S or SAS at the time. Later, R came along as an alternative to S.
I may want to use extendr to access a Rust library from R, so I thought I would start by installing R on my Debian desktop.
I like GitHub well enough; I link to it often and even have an account but I would never consider using it for anything but public files that I had replicated on my own machine. I’ve been over the reasons for that many times. The most important reason is that you should never commit your only copy of any data you care about to a third party computer. Once you do, a clock starts ticking down to the time that you will lose your data. You never know how much time is left on the clock but you can be sure that it will eventually reach 0.
The second reason is that any site that hosts a lot of data is an attractive target for criminals and your data could be compromised. Here’s the latest GitHub exploit. That’s why I say I would only use GitHub for public data.
All of this is by way of introduction to this post from Simon Willison. In it Willison posits that GitHub is (almost) the perfect notebook. What follows is a list of features that could easily be a description of Org mode. Read it and see if you don’t agree.
The only reason for Willison’s “almost” is the lack of synchronized offline support. I submit that Journelly goes a long ways towards meeting that concern for Org mode. I’ve previously described it as a front end for Org and although it doesn’t expose all your Org files to your remote device it gets you a long way.
Given all that, it’s clear to me that Emacs and Org mode is a much better notebook than GitHub could hope to be. Your data is safer both from criminals and from being disappeared for some reason by a third party provider. My only question is why do I have to keep repeating this.
Bank Buddy is an Emacs package that provides financial analysis and reporting capabilities for your bank statements. It processes CSV bank statement data, categorizes transactions using customizable patterns, and generates detailed reports in Org-mode format.
It does not depend on any external account system, and the analysis is handled by elisp. The only external tool that may be required is gnuplot to visualize the generated data.
Here is an example of the type of report that is generated (obviously using test data :))
Smart Transaction Categorization: Auto-categorizes transactions based on customizable regex patterns
Financial Reports: Generates detailed reports in Org-mode with:
Transaction summaries and overviews
Spending category analysis
Top merchant identification
Monthly spending patterns with visual representation
Recurring subscription detection
Interactive Category Management: Edit and refine categorization patterns directly from reports
Data Visualizations: Generates charts and graphs using external gnuplot scripts
No Reliance on External Accounting System - Analysis is all Emacs built-in
Asynchronous Processing: Efficiently handles large bank statements without blocking Emacs
Screenshots
Monthly Spending categories
Monthly Spending categories (stacked)
Quick Start
Export your bank statement as a CSV file
Edit CSV using csv-mode for all lines to the format DATE,DESCRIPTION,AMOUNT
Open CSV file
Run: M-x bank-buddy-generate
Open the generated report
Generate gnuplots
Since version 0.2.0, Bank Buddy generates external gnuplot scripts that are executed via call-process. You need to have gnuplot installed on your system.
To install gnuplot:
Linux: sudo apt install gnuplot (or equivalent for your distribution)
The generated reports will include links to both the gnuplot script files (.gp) and data files (.dat) for each visualization. This allows for:
Easy customization of plots by editing the gnuplot scripts
Regeneration of plots without reprocessing the CSV data
Better control over visualization settings
Usage Guide
Understanding CSV Format
Bank Buddy expects CSV files with at least the following columns:
Transaction date
Transaction description
Debit amount
Different banks format their CSV exports differently. You may need to preprocess your CSV to match this format, I would advise to use the package csv-mode, open up a csv file and C-c C-k you way to removed unwanted columns so all you have left are those described above.
To generate a financial report M-x bank-buddy-generate-report
You’ll be prompted to select an input CSV file and specify the output Org file.
The package processes the data asynchronously and a buffer will appear reporting on the analysis progress, so Emacs remains responsive even with large CSV files.
When processing is complete, you’ll be asked if you want to open the generated report.
Understanding the Report
A typical Bank Buddy report includes:
Summary Overview
Total transactions analyzed
Date range
Total, average daily, and weekly spending
Top Spending Categories
Ranked list of spending categories
Total amount, percentage, and monthly/yearly averages
Links to generated gnuplot scripts and data files
Monthly Spending Patterns
Month-by-month spending visualization
Category breakdown for each month
Highest and lowest spending months
Links to visualization files (*.gp, *.dat)
Monthly Category Breakdowns
Detailed charts for each month showing spending by category
Consistent color coding across months for easy comparison
Links to individual gnuplot scripts for customization
Top Merchants
Your highest-spending merchants
Total amount, percentage, and monthly/yearly averages
Links to generated visualization files
Recurring Subscriptions
Detected recurring payments
Estimated monthly cost
Frequency analysis (weekly, bi-weekly, monthly, annual)
Transaction Size Distribution
Analysis of transaction sizes (under £10, £10-50, £50-100, over £100)
Unmatched Transactions
List of transactions that didn’t match specific categories
Suggested patterns to add to your customization
Managing Transaction Categories
Bank Buddy comes with predefined category patterns, but you’ll likely want to customize these for your personal transactions. The package includes an interactive mode for managing categories.
When viewing a report, you can:
Navigate to an unmatched transaction (in the “Unmatched Transactions” section)
Press C-c C-a to add it to a category
Choose an existing category or create a new one
Optionally save the updated category definitions to your init file
Regenerate the report to see the changes
To manage existing categories or add new ones manually, customize bank-buddy-core-cat-list-defines.
Category Format
Categories are defined as patterns in the form:
(REGEX-PATTERN CATEGORY-CODE)
Where:
REGEX-PATTERN is a regular expression that matches transaction descriptions
CATEGORY-CODE is a short code representing the category (e.g., “fod” for food)
Review Unmatched Transactions
Navigate to the “Unmatched Transactions” section of the report.
Categorize Transactions
Place cursor on an unmatched transaction
C-c C-a to add it to a category
Choose an existing category or create a new one
Regenerate Report
C-c C-r to see your changes
Save Category Definitions
When prompted, choose to save your category definitions to your init file.
Customization
Core Settings
;; Exclude large transactions from analysis(setq bank-buddy-core-exclude-large-txns t)
(setq bank-buddy-core-large-txn-threshold 2000)
;; Number of occurrences to detect subscriptions(setq bank-buddy-core-subscription-min-occurrences 3)
;; Number of top items to display(setq bank-buddy-core-top-spending-categories 5)
(setq bank-buddy-core-top-merchants 5)
Customizing Category Patterns
You can customize the category patterns by setting bank-buddy-core-cat-list-defines:
(customize-set-variable 'bank-buddy-core-cat-list-defines'(("amazon\\|amz""amz")
("netflix\\|spotify""str")
("uber\\|lyft""txi")
("sainsburys\\|tesco\\|asda""fod")
;; Add your own patterns here (".*""o"))) ; Catch-all pattern should be last
Customizing Category Names
Category codes are mapped to human-readable names via bank-buddy-core-category-names:
(customize-set-variable 'bank-buddy-core-category-names'(("amz"."Amazon")
("str"."Streaming Services")
("txi"."Taxi & Rideshare")
("fod"."Groceries")
;; Add your own mappings here ("o"."Other")))
Customizing Subscription Detection
Define subscription patterns for better detection of recurring payments:
(customize-set-variable 'bank-buddy-core-subscription-patterns'(("NETFLIX"."Netflix")
("SPOTIFY"."Spotify")
("AMAZON PRIME"."Amazon Prime")
;; Add your own patterns here ))
Advanced Usage
Integration with Other Financial Tools
Bank Buddy reports are generated as Org-mode files, making them compatible with other Org-based tools:
Export to HTML, PDF, or other formats with Org export functions
Use org-capture to add notes to specific transactions or categories
Custom Visualization
Bank Buddy generates visualizations using external gnuplot scripts. You can customize these by:
Editing the generated .gp files in the report directory
Creating your own gnuplot scripts based on the generated .dat files
Running the scripts manually with gnuplot filename.gp
Keyboard Shortcuts
When viewing a report with bank-buddy-cat-mode enabled:
C-c C-a: Add the transaction at point to a category
C-c C-r: Regenerate the report with current category definitions
Caveats and Tips
CSV Format: Bank Buddy expects a CSV with date, description, and amount columns
Performance: For very large CSV files (10K+ rows), the async processing helps but may still take time
Categorization: Start with broad patterns and refine as needed
Visualization: Ensure gnuplot is installed on your system for chart generation
Saving Patterns: Always save your category patterns to persist between sessions
Comparison with Other Financial Packages
Several Emacs packages exist for financial management, but they serve different purposes. Here’s how Bank Buddy compares to other notable financial packages:
Ledger-mode
Ledger-mode is an Emacs interface to the command-line Ledger accounting system.
Key differences:
Ledger is a complete double-entry accounting system; Bank Buddy is focused on bank statement analysis
Ledger requires manual transaction entry or carefully formatted imports; Bank Buddy automates categorization
Ledger offers more comprehensive accounting features (accounts, assets, liabilities); Bank Buddy focuses on spending insights
Bank Buddy provides visual spending breakdowns and charts; Ledger focuses on accurate accounting
When to use Ledger: For complete personal finance tracking, investments, budgeting, and double-entry accounting.
When to use Bank Buddy: For quick analysis of bank statements and visualizing spending patterns.
HLedger-mode
HLedger-mode is an Emacs major mode for working with hledger, a plain-text accounting system similar to Ledger.
Key differences:
HLedger, like Ledger, is a full double-entry accounting system; Bank Buddy focuses on bank statement analysis
HLedger requires manual transaction entry or formatted imports; Bank Buddy automates categorization
HLedger offers comprehensive accounting features (multiple currencies, time reporting); Bank Buddy emphasizes spending insights
Bank Buddy provides visual spending breakdowns; HLedger focuses on textual reports and balances
When to use HLedger-mode: For detailed personal finance tracking, multi-currency support, and generating various financial reports.
When to use Bank Buddy: For quick analysis of bank statements and visualizing spending patterns without learning a full accounting system.
Elbank
Elbank is a personal finance reporting tool for Emacs that uses Weboob to fetch data from bank websites.
Key differences:
Elbank can automatically fetch transactions from supported banks; Bank Buddy works with CSV exports
Elbank focuses on reporting and visualization; Bank Buddy offers both analysis and reporting
Elbank requires Weboob setup and configuration; Bank Buddy works directly with CSV files
Bank Buddy provides customizable transaction categorization; Elbank may rely on bank-provided categories
When to use Elbank: For automated tracking of multiple bank accounts with direct data fetching and basic reporting.
When to use Bank Buddy: For detailed analysis and categorization of bank statements, especially when working with CSV exports or when bank integration isn’t available or desired.
Beancount-mode
Beancount-mode is an Emacs mode for Beancount, another plain-text accounting system.
Key differences:
Beancount, like Ledger, is a full double-entry accounting system
Beancount has stricter syntax requirements than Ledger
Bank Buddy offers automatic categorization and reporting, while Beancount requires manual entry
Beancount generates sophisticated reports, but requires more setup and knowledge
When to use Beancount: For precise, auditable personal accounting with strict validation.
When to use Bank Buddy: For simple spending analysis without learning accounting principles.
csv-mode and orgtbl-mode
Some users analyze financial CSV data using built-in Emacs packages like csv-mode combined with org-table functionality.
Key differences:
These are general-purpose tools that require manual customization for financial analysis
Bank Buddy provides specialized, financial-specific analysis and visualization
Bank Buddy automatically categorizes transactions based on patterns
Bank Buddy generates reports without manual processing
When to use csv/orgtbl-mode: For custom, one-off analysis of financial data.
When to use Bank Buddy: For consistent, repeatable analysis of bank statements.
Roadmap
TODO
DOING
DONE
Add Paypal break down csv
Highlight bank lines not matched for iterative tweaks
While GNU/Linux had been my operating system of choice for many years, these days I'm primarily on macOS. Lucky for me, I spend most of my time in Emacs itself (or a web browser), making the switch between operating systems a relatively painless task.
I build iOS and macOS apps for a living, so naturally I've accumulated a handful of macOS-Emacs integrations and tweaks over time. Below are some of my favorites.
Emacs Plus
For starters, I should mention I run Emacs on macOS via the excellent Emacs Plushomebrew recipe. These are the options I use:
You may have noticed the --with-no-frame-refocus Emacs Plus option. I didn't like Emacs refocusing other frames when closing one, so I sent a tiny patch over to Emacs Plus, which gave us that option.
I also prefer reusing existing frames whenever possible.
(setq ns-pop-up-frames nil)
Visual tweaks
Most of my visual tweaks have been documented in my Emacs eye candy post. For macOS-specific things, read on…
It's been a while since I've added this, though vaguely remember needing it to fix mode line rendering artifacts.
(setq ns-use-srgb-colorspace nil)
I like using a transparent title bar and these two settings gave me just that:
I want a menu bar like other macOS apps, so I enable with:
(use-package menu-bar
:config
(menu-bar-mode +1))
Emoji picker (a freebie!)
If you got a more recent Apple keyboard, you can press the 🌐 key to insert emojis from anywhere, including Emacs. If you haven't got this key, you can always M-x ns-do-show-character-palette, which launches the very same dialog.
If you prefer Apple's long-press approach to inserting accents or other special characters, I got an Emacs version of that.
Rotate macOS display
I wanted to rotate my monitor from the comfort of M-x, so I made Emacs do it.
Open with
While there are different flavors of "open with default macOS app" commands out there (ie. crux-open-with as part of Bozhidar Batsov's crux), I wanted one that let me choose a specific macOS app.
Open in Xcode (at line number)
Shifting from Emacs to Xcode via "Open with" is simple enough, but don't you want to also visit the very same line?
This is particulary handy if you do any sort of iOS/macOS development, enabling you to insert SF Symbols using your favorite completion framework. I happen to remain a faithful ivy user.
SF Symbols (for fun)
Speaking of enabling SF Symbol rendering, you can also use them to spiff your Emacs up. Check out Charles Choi's Calle 24 for a great-looking Emacs toolbar. Also, Christian Tietze shows how to use SF Symbols as Emacs tab numbers.
Having learned how simple it was to enable Objective-C babel support, I figured I could do something a little more creative with SwiftUI, so I published ob-swiftui on MELPA.
Changing macOS default apps
I found the nifty duti command-line tool to change default macOS applications super handy, but could never remember its name when I needed it. And so I decided to bring it into dwim-shell-command as part of my toolbox.
I got a bunch of handy helpers in dwim-shell-commands.el (specially all the image/video helpers via ffmpeg and imagemagick). Go check dwim-shell-commands.el. There's loads in there, but here are my macOS-specific commands:
I wanted a quick way to record or take screenshots of macOS windows, so I now have my lazy way, leveraging macosrec, a recording command line utility I built. Invoked via M-x of course.
Eglot (LSP) for iOS/macOS dev
If you want any sort of code completion for your macOS projects, you'd be happy to know that eglot works out of the box.
While I still purchase music via Apple's Music app, I now play directly from Emacs via Ready Player Mode. I'm fairly happy with this setup, having scratched that itch with my own package.
By the way, those buttons also leverage SF Symbols on macOS.
Reveal -> all <- in Finder
While there are plenty of solutions out there leveraging the open command line tool to reveal files in macOS's Finder, I wanted one that revealed multiple files in one go. For that, I leveraged the awesome emacs-swift-module, also by Valeriy Savchenko.
Use the macOS Trash
The macOS trash has saved my bacon in more than one occasion. Make Emacs aware of it. Also check out M-x dwim-shell-commands-macos-empty-trash.
Build your own macOS utils
While elisp wasn't in my top languages to learn back in the day, I sure am glad I finally bit the bullet and learned a thing or two. This opened many possibilities. I now see Emacs as a platform to build utilities and tools off of. A canvas of sorts, to be leveraged in and out of the editor.
While not exactly an Emacs tweak itself, I wanted to extend Emacs bindings into other macOS apps. In particular, I wanted more reliable Ctrl-n/p usage everywhere, which I achieved via Karabiner-Elements. I also mapped C-g to Esc, which really feels just great! I can now cancel things, dismiss menus, dialogs, etc. everywhere.
Org as lingua franca
With my Emacs usage growing over time, it was a matter of time until I discovered org mode. This blog is well over 11 years old now, yet still powered by the very same org file (beware, this file is big).
With my org usage growing, I felt like I was missing org support outside of Emacs. And so I started building iOS apps revolving around my Emacs usage.
Journelly (iOS)
Journelly is my latest iOS app, centered around note-taking and journaling. The app feels like tweeting, but for your eyes only of course. It's powered by org markup, which can be synced with Emacs via iCloud.
Flat Habits (iOS)
Org habits are handy for tracking daily habits. However, it wasn't super practical for me as I often wanted to check things off while on the go (away from Emacs). That led me to build Flat Habits.
Scratch (iOS)
While these days I'm using Journelly to jot down just about anything, before that, I built and used Scratch as scratch pad of sorts. No iCloud syncing, but needless to say, it's also powered by org markup.
Plain Org (iOS)
For more involved writing, nothing beats Emacs org mode. But what if I want quick access to my org files while on the go? Plain Org is my iOS solution for that.
Found this post useful?
I'll keep looking for other macOS-related tips and update this post in the future.
iLemming has an interesting post on Hacker News about why younger engineers don’t use Emacs and why they should. You may find the post a bit overwritten but I especially like the ending:
The specialized app user lives in rented apartments; the Emacs devotee walks through an ever-expanding mansion whose rooms rearrange themselves to their thoughts.
That’s a nice metaphor for the Emacs experience. Whatever you need it to be, Emacs will change itself to accommodate you. We usually talk about Emacs as being a clay that we can mold into whatever shape we desire but I like this description better.
Not everybody agrees. Over at the Emacs subreddit there’s a bit of pushback. That’s fine but some responses, like this one, just seem uninformed. You can tell that the commenter doesn’t really know what they’re talking about because they refer to Emacs as “eMacs”. It’s not a big thing but it’s a sure sign that the commenter isn’t really familiar with the subject matter.
All that aside, I really like the metaphor of Emacs being a mansion whose rooms rearrange themselves to suite your needs.
I just published the first stable version of my new minimalist themes
for Emascs. The Doric themes use few colours and will appear
monochromatic in many contexts. Styles involve the careful use of
typographic features and subtleties in colour gradients to establish a
consistent rhythm.
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.
Like many of us Emacsers, I do much (if not most) my computering in Emacs. This includes using EMMS as my main media player and Dired as my file manager. One thing I find myself doing pretty often is adding a bunch of subdirectories in my ~/music directory to my EMMS playlist. I usually used emms-add-directory-tree, but it is not really smooth if I want to apply it to many subdirectories. I thought, “there must be a function which adds the current item – or the marked items – to the playlist”.
I had a couple of drinks last night and opened my laptop and accidentally launched Obsidian and thought, “Oh, I remember. This is pretty cool! I should use this for everything.” so this morning I’m staring at Obsidian wondering what now?
But why not just bail on the Obsidian app and drop back into my beloved Emacs?
I’ve been asking myself that question all morning. Ostensibly, I simply felt like a change of venue this morning, and Obsidian seemed as good as any. But there’s something larger lurking under the surface.
Playing with my Emacs config is a fun hobby. I do it all the time. Like, all the time. Every moment using Emacs can feel like, “…but this would be better if I made this one change…” where “one change” is a keyboard binding or a different package or a new theme or some little behavioral tweak using some homemade lisp.
I’ve recently broken some stuff that used to work. And do I really like the way my Org Agenda looks? Emacs gives me all the rope I need, which is great. For many, it’s the perfect amount of rope. On days like today, though, it feels like too much rope.
I’m not planning to switch from Emacs to Obsidian. That would be silly. But I am taking a short break. A change of pace can be fun, and I’m in the mood for something fun and different. That’s my story, and I’m sticking to it.
For a while, I’ve wanted to create a simple note-taking system in Emacs. Inspired by the Denote package, I wanted to create something that
Used only native Emacs functionality (but sweetened by other packages)
Created uniform, timestamped notes
That’s it! Nothing too complicated.
Finally, I managed to get this done. But then, even after having the system, I wasn’t using it. Below, I’ll talk about why I wanted to create a system, why I didn’t use it, and how I started using it.
Why create a note system?
I have often found it difficult to organize the files on my computer. When I consider all the various types of files I write, it’s no wonder they can get confusing:
Notes about books I’m reading.
Random diary entries.
Ideas for future projects.
Emails to friends and coworkers.
Critiques for writing group.
Original fiction.
Blog posts.
Scripts for YouTube videos.
And more.
And these are just plain text files.
For a while, I managed to do quite while with three different directories:
stories
notes
videos
blog posts
So I created these folders and started storing files in them. Notice, these categories fail to capture a lot of the above use cases. I started using the “notes” directory for anything that was not a story, video, or blog post. Fair enough.
But soon, I was only using the stories folder and still saving random files to various other locations in my file system.
I think the reason for this was simple. There was still some overlap in categorization. For example, I’d start writing a blog post, then decide to turn it into a video script and scrap the post. This meant I had a video script in my blog post directory. Not a big deal, but sort of inefficient and lazy; and, down the road, this makes it more difficult to find something if you need to reference it later—as I often do.
So…Why have separate directories at all? I needed a singular destination for everything.
Everything is a note
I decided to stop with the multiple directories and just start putting everything in the notes directory. This opened up the whole system. Now, I had one singular destination to create new files, link them together as needed, and reference them later if necessary.
Of course, “notes” is an arbitrary destination. The directory could be called everything, deep_thoughts, files, etc.
Now, the next task was to create some functionality for managing these notes. The system relies heavily upon Org mode hyperlinks, so I needed some functions for managing and viewing linkages, in addition to the basic functions for creating new notes in a uniform fashion using timestamps as a unique identifier (to avoid duplicates).
I needed a few simple things, some functions to do the following:
Create new notes in the chosen directory.
Create a uniform note from an existing file located somewhere else in my file system (like, ~/Desktop, ~/Downloads, or ~/Pictures, etc.).
Create new note from highlighted text in buffer (like in Obsidian).
Mark files that have a backlink.
List files that link to current file (if any).
I now have the system written and documented, and I will be giving a demonstration in an upcoming video. The reason I chose to build my own system instead of using an existing package was two-fold:
I find it more difficult learning a program someone else wrote while simultaneously figuring out how I want to use it.
Minimalism. I think it’s always better to create a smaller set of functions based solely on your individual needs.
When it comes to documenting your important thoughts, creative insights, systems, and projects, you’ll want something robust—and there are plenty of great programs out there that can handle it—, but also something lightweight that you can tweak on the fly. The live text-editing environment of Emacs is ideal for meeting those particular demands.
This post contains LLM poisoning. lasagnas scintilla spicing There was another meeting this past week of EmacsATX, the Austin Emacs Meetup group. For this month we had no predetermined topic. However, as always, there were mentions of many modes, packages, technologies and websites, some of which I had never heard of before, and some of ... Read more
There have been quite a few updates recently. The main highlights include support for attachments, so you can push a file to the chat directly from dired for potential inclusion in your next query.
Vision support has been added for models that can handle it. If you supply the path to an image file in the chat, it will be processed. This means you can now, for example, extract text from images using models like o:gemma3:4b.
I’ve also introduced the ability to save user system prompts. If you have a favorite prompt, or have crafted one that works especially well for you, you can now save it by category and title in a simple Org format for later recall. Prompt recall now works the same way as Fabric patterns and Awesome ChatGPT prompts. This makes it much easier to display the currently used system prompt concisely in the status bar, as it will be based on the prompt title (and thus likely the role).
What else? Oh yes, I received a request for better context tracking. Now, when context is nearing full capacity, or has exceeded it, it will be indicated in the status bar!
That’s probably it for the major changes. There was also some refactoring, but you probably don’t care about that. Anyway, here is the full list of changes:
<2025-05-22 Thu> 0.12.0
Full system prompt in the status bar replaced with a more meaningful simple role title
Added system prompt metadata tracking with title, source, and timestamp registry
Implemented automatic title extraction and unified completing-read interface
Enhanced fabric/awesome prompt integration with proper metadata handling
Improved transient menu organization and org-mode formatting with folding
Added system prompt history display and better error handling for empty files
Transient menu has been simplified and reorganised
Previously, the header status bar would show truncated system prompt text like [You are a helpful assistant wh...], making it difficult to quickly identify which prompt was active. Now, the display shows meaningful role titles with source indicators:
[F:Code Reviewer] - Fabric pattern
[A:Linux Terminal] - Awesome ChatGPT prompt
[U:Writing Assistant] - User-defined prompt
The system now intelligently extracts titles from prompt content by recognizing common patterns like “You are a…”, “Act as…”, or “I want you to act as…”. When these patterns aren’t found, it generates a concise title from the first few words.
Behind the scenes, Ollama Buddy now maintains a registry of all system prompts with their titles, sources, and timestamps. This enables new features like system prompt history viewing and better organization across Fabric patterns, Awesome ChatGPT prompts, and user-defined prompts.
The result is a cleaner interface that makes it immediately clear which role your AI assistant is currently embodying, without cluttering the status bar with long, truncated text.
<2025-05-21 Wed> 0.11.1
Quite a bit of refactoring to generally make this project more maintainable and I have added a starter kit of user prompts.
Color System Reworking
Removed all model color-related functions and variables
Removed dependency on color.el
Replaced with highlight-regexp and hashing to ^font-lock faces, so now using a more native built-in solutions for model colouring rather than shoe-horning in overlays.
UI Improvements
Simplified the display system by leveraging Org mode
Added org-mode styling for output buffers
Added org-hide-emphasis-markers and org-hide-leading-stars settings
Changed formatting to use Org markup instead of text properties
Converted plain text headers to proper Org headings
Replaced color properties with Org emphasis (bold)
History Management Updates
Streamlined history editing functionality
Improved model-specific history editing
Refactored history display and navigation
System Prompts
Added library of system prompts in these categories:
analysis (3 prompts)
coding (5 prompts)
creative (3 prompts)
documentation (3 prompts)
emacs (10 prompts)
general (3 prompts)
technical (3 prompts)
writing (3 prompts)
<2025-05-19 Mon> 0.11.0
Added user system prompts management
You can now save, load and manage system prompts
Created new transient menu for user system prompts (C-c s)
Organized prompts by categories with org-mode format storage
Supported prompt editing, listing, creation and deletion
Updated key bindings to integrate with existing functionality
Added prompts directory customization with defaults
This feature makes it easier to save, organize, and reuse your favorite system prompts when working with Ollama language models.
System prompts are special instructions that guide the behavior of language models. By setting effective system prompts, you can:
Define the AI’s role (e.g., “You are a helpful programming assistant who explains code clearly”)
Establish response formats
Set the tone and style of responses
Provide background knowledge for specific domains
The new ollama-buddy-user-prompts module organizes your system prompts in a clean, category-based system:
Save your prompts - Store effective system prompts you’ve crafted for future use
Categorize - Prompts are organized by domains like “coding,” “writing,” “technical,” etc.
Quick access - Browse and load your prompt library with completion-based selection
Edit in org-mode - All prompts are stored as org files with proper metadata
Manage with ease - Create, edit, list, and delete prompts through a dedicated transient menu
The new functionality is accessible through the updated key binding C-c s, which opens a dedicated transient menu with these options:
Save current (S) - Save your active system prompt
Load prompt (L) - Choose a previously saved prompt
Create new (N) - Start fresh with a new prompt
List all Prompts (l) - View your entire prompt library
Edit prompt (e) - Modify an existing prompt
Delete prompt (d) - Remove prompts you no longer need
If you work frequently with Ollama models, you’ve likely discovered the power of well-crafted system prompts. They can dramatically improve the quality and consistency of responses. With this new management system, you can:
Build a personal library of effective prompts
Maintain context continuity across sessions
Share prompts with teammates
Refine your prompts over time
<2025-05-14 Wed> 0.10.0
Added file attachment system for including documents in conversations
Added file attachment support with configurable file size limits (10MB default) and supported file types
Implemented session persistence for attachments in save/load functionality
Added attachment context inclusion in prompts with proper token counting
Created comprehensive attachment management commands:
Attach files to conversations
Show current attachments in dedicated buffer
Detach specific files
Clear all attachments
Added Dired integration for bulk file attachment
Included attachment menu in transient interface (C-c 1)
Updated help text to document new attachment keybindings
Enhanced context calculation to include attachment token usage
You can now seamlessly include text files, code, documentation, and more directly in your conversations with local AI models!
Simply use C-c C-a from the chat buffer to attach any file to your current conversation.
The attached files become part of your conversation context, allowing the AI to reference, analyze, or work with their contents directly.
The transient menu has also been updated with a new Attachment Menu
*File Attachments*
a Attach file
w Show attachments
d Detach file
0 Clear all attachments
Your attachments aren’t just dumped into the conversation - they’re intelligently integrated:
Token counting now includes attachment content, so you always know how much context you’re using
Session persistence means your attachments are saved and restored when you save/load conversations
Updated status bar to show current/max context with fontification
I’ve added context window management and monitoring capabilities to Ollama Buddy!
This update helps you better understand and manage your model’s context usage, preventing errors and optimizing your conversations.
Enable it with the following:
(setq ollama-buddy-show-context-percentage t)
Usage
After implementing these changes:
Text mode: Shows 1024/4096 style display
Bar mode (default): Shows ███████░░░░ 2048 style display
Use C-c 8 to toggle between modes
The Text mode will change fontification based on your thresholds:
Normal: regular fontification
(85%+): underlined and bold
(100%+): inverse video and bold
The Bar mode will just fill up as normal
The progress bar will visually represent how much of the context window you’re using, making it easier to see at a glance when you’re approaching the limit.
Implementation Details
Context Size Detection
Determining a model’s context size proved more complex than expected. While experimenting with parsing model info JSON, I discovered that context size information can be scattered across different fields. Rather than implementing a complex JSON parser (which may come later), I chose a pragmatic approach:
I created a new defcustom variable ollama-buddy-fallback-context-sizes that includes hard-coded values for popular Ollama models. The fallback mechanism is deliberately simple: substring matching followed by a sensible default of 4096 tokens.
(defcustom ollama-buddy-fallback-context-sizes
'(("llama3.2:1b".2048)
("llama3:8b".4096)
("tinyllama".2048)
("phi3:3.8b".4096)
("gemma3:1b".4096)
("gemma3:4b".8192)
("llama3.2:3b".8192)
("llama3.2:8b".8192)
("llama3.2:70b".8192)
("starcoder2:3b".8192)
("starcoder2:7b".8192)
("starcoder2:15b".8192)
("mistral:7b".8192)
("mistral:8x7b".32768)
("codellama:7b".8192)
("codellama:13b".8192)
("codellama:34b".8192)
("qwen2.5-coder:7b".8192)
("qwen2.5-coder:3b".8192)
("qwen3:0.6b".4096)
("qwen3:1.7b".8192)
("qwen3:4b".8192)
("qwen3:8b".8192)
("deepseek-r1:7b".8192)
("deepseek-r1:1.5b".4096))
"Mapping of model names to their default context sizes.
Used as a fallback when context size can't be determined from the API." :type '(alist :key-type string :value-type integer)
:group 'ollama-buddy)
This approach may not be perfectly accurate for all models, but it’s sufficient for getting the core functionality working. More importantly, as a defcustom, users can easily customize these values for complete accuracy with their specific models. Users can also set context values within the chat buffer through C-c C (Show Context Information) for each individual model if desired.
This design choice allowed me to focus on the essential features without getting stuck on complex context retrieval logic.
One final thing!, if the num_ctx: Context window size in tokens is set, then that number will also be taken into consideration. An assumption will be made that the model is honouring the context size requested and will incorporated into the context calculations accordingly.
Token Estimation
For token counting, I’ve implemented a simple heuristic: each word (using string-split) is multiplied by 1.3. This follows commonly recommended approximations and works well enough in practice. While this isn’t currently configurable, I may add it as a customization option in the future.
How to Use Context Management in Practice
The C-c C (Show Context Information) command is central to this feature. Rather than continuously monitoring context size while you type (which would be computationally expensive and potentially distracting), I’ve designed the system to calculate context on-demand when you choose.
Typical Workflows
Scenario 1: Paste-and-Send Approach
Let’s say you want to paste a large block of text into the chat buffer. You can simply:
Paste your content
Press the send keybinding
If the context limit is exceeded, you’ll get a warning dialog asking whether to proceed anyway
Scenario 2: Preemptive Checking
For more control, you can check context usage before sending:
Paste your content
Run C-c C to see the current context breakdown
If the context looks too high, you have several options:
Trim your current prompt
Remove or simplify your system prompt
Edit conversation history using Ollama Buddy’s history modification features
Switch to a model with a larger context window
Scenario 3: Manage the Max History Length
Want tight control over context size without constantly monitoring the real-time display? Since conversation history is part of the context, you can simply limit ollama-buddy-max-history-length to control the total context size.
For example, when working with small context windows, set ollama-buddy-max-history-length to 1. This keeps only the last exchange (your prompt + model response), ensuring your context remains small and predictable, perfect for maintaining control without manual monitoring.
Scenario 4: Parameter num_ctx: Context window size in tokens
Simply set this parameter and off you go!
Current Status: Experimental
Given the potentially limiting nature of context management, I’ve set this feature to disabled by default.
But to enable set the following :
(setq ollama-buddy-show-context-percentage t)
This means:
Context checks won’t prevent sending prompts
Context usage won’t appear in the status line
However, calculations still run in the background, so C-c C (Show Context Information) remains functional
As the feature matures and proves its value, I may enable it by default. For now, consider it an experimental addition that users can opt into.
More Details
The status bar now displays your current context usage in real-time. You’ll see a fraction showing used tokens versus the model’s maximum context size (e.g., “2048/8192”). The display automatically updates as your conversation grows.
Context usage changes fontification to help you stay within limits:
Normal font: Normal usage (under 85%)
Bold and Underlined: Approaching limit (85-100%)
Inversed: At or exceeding limit (100%+)
Before sending prompts that exceed the context limit, Ollama Buddy now warns you and asks for confirmation. This prevents unexpected errors and helps you manage long conversations more effectively.
There are now three new interactive commands:
C-c $ - Set Model Context Size. Manually configure context sizes for custom or fine-tuned models.
C-c % - Toggle Context Display. Show or hide the context percentage in the status bar.
C-c C - Show Context Information. View a detailed breakdown of:
All model context sizes
Current token usage by category (history, system prompt, current prompt)
Percentage usage
The system estimates token counts for:
Conversation history: All previous messages
System prompts: Your custom instructions
Current input: The message you’re about to send
This gives you a complete picture of your context usage before hitting send.
The context monitoring is not enabled by default.
<2025-05-05 Mon> 0.9.44
Sorted model names alphabetically in intro message
Removed multishot writing to register name letters
For some reason, when I moved the .ollama folder to an external disk, the models returned with api/tags were inconsistent, which meant it broke consistent letter assignment. I’m not sure why this happened, but it is probably sensible to sort the models alphabetically anyway, as this has the benefit of naturally grouping together model families.
I also removed the multishot feature of writing to the associated model letter. Now that I have to accommodate more than 26 models, incorporating them into the single-letter Emacs register system is all but impossible. I suspect this feature was not much used, and if you think about it, it wouldn’t have worked anyway with multiple model shots, as the register letter associated with the model would just show the most recent response. Due to these factors, I think I should remove this feature. If someone wants it back, I will probably have to design a bespoke version fully incorporated into the ollama-buddy system, as I can’t think of any other Emacs mechanism that could accommodate this.
<2025-05-05 Mon> 0.9.43
Fix model reference error exceeding 26 models #15
Update ollama-buddy to handle more than 26 models by using prefixed combinations for model references beyond ‘z’. This prevents errors in create-intro-message when the local server hosts a large number of models.
<2025-05-03 Sat> 0.9.42
Added the following to recommended models:
qwen3:0.6b
qwen3:1.7b
qwen3:4b
qwen3:8b
and fixed pull model
<2025-05-02 Fri> 0.9.41
Refactored model prefixing again so that when using only ollama models no prefix is applied and is only applied when online LLMs are selected (for example claude, chatGPT e.t.c)
I think this makes more sense and is cleaner for I suspect the majority who may use this package are probably more interested in just using ollama models and the prefix will probably be a bit confusing.
This could be a bit of a breaking change once again I’m afraid for those ollama users that have switched and are now familiar with prefixing “o:”, sorry!
<2025-05-02 Fri> 0.9.40
Added vision support for those ollama models that can support it!
Image files are now detected within a prompt and then processed if a model can support vision processing. Here’s a quick overview of how it works:
Configuration: Users can configure the application to enable vision support and specify which models and image formats are supported. Vision support is enabled by default.
Image Detection: When a prompt is submitted, the system automatically detects any image files referenced in the prompt.
Vision Processing: If the model supports vision, the detected images are processed in relation to the defined prompt. Note that the detection of a model being vision capable is defined in ollama-buddy-vision-models and can be adjusted as required.
In addition, a menu item has been added to the custom ollama buddy menu :
[I] Analyze an Image
When selected, it will allow you to describe a chosen image. At some stage, I may allow integration into dired, which would be pretty neat. :)
Picture this: You're deep in a coding session with an LLM, and your AI
assistant suggests running some shell commands or manipulating files.
It's incredibly productive—until that nagging voice in your head
whispers, "What if this goes wrong?"
We've all been there. AI tools with filesystem and command execution
capabilities are absolute game-changers for productivity, but handing
over the keys to your entire system? That's a hard pass for any
security-conscious developer.
While there are several containerization solutions available (Docker,
Podman, LXC, etc.), this post focuses on Guix containers. The main
reason is that I'm already managing Emacs and its packages through the
Guix ecosystem.
The Perfect LLM Client for Emacs
For interacting with LLMs in Emacs, my go-to client is gptel. It's
elegant, fast, and integrates seamlessly with the Emacs ecosystem.
Since most things we do is produce and consume text, I already have
all my things (emails, slides, accounting, code, etc) in Emacs. That
already gives super powers. Adding gptel on top of it is..
magnificent.
Speaking of tools, I've configured a comprehensive set of AI tools for
gptel that cover my daily requirements. You can find all my tool
configurations and setup details in my Emacs configuration repository.
Enter the Guix Container Solution
With Guix and Emacs, we can have our cake and eat it too: Full AI tool
access in an isolated environment. If you're new to Guix containers,
the official Guix Cookbook documentation provides background on how
container isolation works using Guix.
Let's peek at the shell script that makes using such a
container/jail/chroot more convenient:
#!/bin/bash
# Default workspace directory
DEFAULT_WORKSPACE="$HOME/src"
# Use provided workspace directory or default
WORKSPACE_DIR="${1:-$DEFAULT_WORKSPACE}"
# Validate workspace directory exists
if [ ! -d "$WORKSPACE_DIR" ]; then
echo "Error: Workspace directory '$WORKSPACE_DIR' does not exist."
exit 1
fi
# Extract guix packages. This is required only, if you use guix for Emacs packages.
PACKAGES=$(sed -n '/^#+begin_src fundamental :noweb-ref packages/,/^#+end_src/p' ~/.emacs.d/configuration.org | \
grep -v '^#+' | \
grep -v '^$' | \
sed 's/^[ \t]*//;s/[ \t]*$//;s/"//g' | \
tr '\n' ' ')
# Add SSH agent support
export SSH_AUTH_SOCK="$SSH_AUTH_SOCK"
# Workaround, otherwise the home directory in Guix is group writeable
# which SSH does not approve of
export CONTAINER_HOME="/tmp/container-home"
mkdir -p "$CONTAINER_HOME"
chmod g-w /tmp/container-home
guix shell --container --no-cwd --network \
--share="$WORKSPACE_DIR"=/workspace \
--share="$CONTAINER_HOME"=/home/munen \
# This is required only, if you use guix for Emacs packages.
--share=$HOME/.guix-profile/share/emacs/site-lisp \
--share=$HOME/.gitconfig \
--share=$HOME/.emacs.d \
--share=$HOME/.local \
--share=$HOME/.ssh \
--share=/tmp/.X11-unix \
--share="$SSH_AUTH_SOCK" \
--preserve='^SSH_AUTH_SOCK$' \
# So you can start Emacs on your hosts X11
--preserve='^DISPLAY$' \
--preserve='^XAUTHORITY$' \
--expose="$XAUTHORITY" \
emacs-next bash findutils coreutils git curl nss-certs openssh \
$PACKAGES -- emacs /workspace
Notice how only specific directories are shared with --share. Your
workspace gets mounted as /workspace, but your system root?
Completely inaccessible.
The container launches directly into Emacs with Dired showing your
workspace directory.
The Result: Fearless AI Development
With this setup, you can confidently tell your AI assistant to:
Refactor entire codebases
Run experimental scripts
Install and test new tools
Even rm -rf to its heart's content
All while knowing your host system remains pristine and secure.
So fire up that container, and let your AI assistant run wild. Happy
hacking🙏
P.S.: While this guide focuses on Guix containers, the same security
principles apply whether you choose Docker, Podman, or other
containerization solutions. The key is consistent isolation.
Want to see more creative uses of free software? Follow along as we explore the endless possibilities when you own your tools.
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!