Lars Ingebrigtsen has an interesting post that discusses writing WordPress blog posts in HTML. Why would you want to do that? Ingebrigtsen’s answer is that the alternatives are harder to use. In particular he says that markup languages are as hard, or harder, to use than raw HTML.
Even though he’s an Emacs user, he frames the discussion in terms of Markdown instead of the much more capable Org mode. Ingebrigtsen makes the point that writing a Markdown link is pretty much the same as writing an HTML link but Emacs/Org-mode makes all this easy.
First of all, you can simply ask Org to insert a link and it will query you for the link and associated text and then format and insert the Org actual link for you. I can’t remember the last time I actually hand-formulated an Org link. But it gets better. In most cases I want to link to a Web page and I have a function that extracts the link and queries me for the associated text. That’s much easier than doing the same thing with raw HTML.
A lot of his post discusses the problems of dealing with images in posts. He uses ewp to deal with this and other matters of dealing with HTML. The Org mode solution is org2blog which seamlessly handles images and the formatting of an Org buffer into to a WordPress blog entry.
I don’t begrudge anyone the ability to write in raw HTML but I don’t think it’s true that it’s easier than writing in Org. On the other hand, one nice thing about ewp is that it can take a screenshot of everything that’s linked to so that it’s easy to see it by just hovering over the link. That helps deal with linkrot.
In any event, it’s an interesting post and worth taking a look at if you feel that you’d like to write in raw HTML.
From the previous entry in this series, one of the things of note in
discussing the nature of the connections between LISP and (the) lambda
calculus was John McCarthy’s concern about recursion and higher-order
functions.
A couple of excerpts from previous quotes from McCarthy on the subject
to set the stage:
…And so, the way in which to [be able to handle function passing/higher
order functions] was to borrow from Church’s Lambda Calculus, to
borrow the lambda definition. Now, having borrowed this notation, one
the myths concerning LISP that people think up or invent for
themselves becomes apparent, and that is that LISP is somehow a
realization of the lambda calculus, or that was the intention. The
truth is that I didn’t understand the lambda calculus, really. In
particular, I didn’t understand that you really could do conditional
expressions in recursion in some sense in the pure lambda calculus.…
…Writing eval required inventing a notation representing LISP
functions as LISP data, and such a notation was devised for the
purposes of the paper with no thought that it would be used to express
LISP programs in practice. Logical completeness required that the
notation used to express functions used as functional arguments be
extended to provide for recursive functions, and the LABEL notation
was invented by Nathaniel Rochester for that purpose. D.M.R. Park
pointed out that LABEL was logically unnecessary since the result
could be achieved using only LAMBDA — by a construction analogous to
Church’s Y-operator, albeit in a more complicated way.…
Examining Church’s Y Combinator will be something we return to
(probably in a number of posts), but I’ll defer discussion of it for
the moment.
For now, let’s consider recursion in lisps. We’ll be talking a lot of
recursion in Emacs Lisp today in fact.
Self-reference, self-embedding
Recursion is a concept or process depends on a simpler or previous
version of itself. It’s ubiquitous, including in the natural world:
Wikipedia notes that “Shapes that seem to have been created by
recursive processes sometimes appear in plants and animals, such as in
branching structures in which one large part branches out into two or
more similar smaller parts. One example is Romanesco broccoli.”
Figure 1: Romanesco broccoli (Brassica oleracea), from Wikipedia
Recursion is a thing I deal with a lot in my day job, as it is a
feature of natural language, especially syntax and semantics. To
provide a quick illustration — though this is not at all how modern
generative syntax is done anymore — consider phrase structure grammar
and phrase structure rules used by Noam Chomsky and his colleagues in
the 1950s.
Sentences (and linguistics objects generally) have formal structure,
and it is part of the productive/creative nature of language that we
might envision this structure as involving abstract structure rules
that can be expanded in different ways (and then have vocabulary
filled in).
A phrase structure rule will generally have the form A → [B C],
indicating that A may be rewritten or expanded into [B C]. (In the
following, I’ll use round brackets ()'s to denote optional
constituents.)
So, a subset of these rules for English might include something like
(note that some things have multiple rules that can apply to them):
Code Snippet 1:
a snippet of phrase structure grammar rules for English [Nb.: again, not prolog, but maybe the best fontlocking choice here]
(Where S is “sentence”; Det is a determiner (like “the”, “a”); NP is a
noun phrase; Nₙ is a noun head; AdjP is an adjective phrase; VP is
a verb phrase; V is a verb head; CP is a complementiser phrase; Comp
is a complementiser (like “that”).)
So we can rewrite S as NP VP (1) and then rewrite NP VP as Det N VP
(2, choosing an optional Det) and then Det N VP as Det N V (4) and then
insert lexical items of the appropriate category into the ‘heads’ (the
non-P elements). So we might choose “the” for Det and “cat” for N and
“purrs” for V, and get the sentence “the cat purrs”.
But note that some of the rules allow for expansion into elements that
contain expansions back into themselves. So rule (1) allows an S to
exapnd into NP VP and rule (6) allows for a VP to expand into V CP and rule
(7) allows for a CP to expand into an S. At which point we can apply
rule (1) again to expand the new S into NP VP, and then repeat this
process as many times as we like:
Code Snippet 2:
a recursive English sentence expansion [Nb.: again, not prolog, but maybe the best fontlocking choice here]
And it’s easy to imagine examples of what such a sentence could be
like, e.g., “John said that Sita said that Bill said that Mary said
that Ram said that Kim said that…". It won’t be infinitely long, but
there’s no particular theoretical bound on how long it could be
(memory processing and finite human lifespans will impose practical
limits, of course).
On the formal semantics side, things are similar: consider that
logical languages too (which are often used to formalise natural
language semantics) allow for recursion. Propositional logic
construction with Boolean operators too have no theoretical upper
limits: we can write (t ↔ (p ∧ (q ∨ (r → (s ∧ ¬¬¬¬¬¬t))))),2 and
there’s nothing which prevents composing this bit of formalism with
yet another bit, and so on.
And in mathematics and computer science, recursion is often a thing
which suggests itself.
For instance, though there are other ways of calculating it, the
Fibonacci sequence3, i.e., a sequence of numbers in which each
element is the sum of the two elements that precede it (e.g. 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144…).
A natural way of writing an equation to calculate these is something
like:
Fib(0) = 0 as base case 1.
Fib(1) = 1 as base case 2.
For all integers n > 1, Fib(n) = Fib(n − 1) + Fib(n − 2).
Rule 3 makes reference to itself. I.e., in order (by this method) to
calculate Fib(6), you have to calculate Fib(5), for which you have to
calculate Fib(4)), for which you have to calculate Fib(3), for which
you have to calculate Fib(2), which you can then base on rules (1) &
(2): you can add 0 and 1 (= Fib(0) and Fib(1)) together to get Fib(2),
and then you can calculate Fib(3) by adding Fib(1) and Fib(2) and so on.
Cursed recursion
In early Lisp(s), despite the concern around recursion, writing
recursive functions was/is not always pragmatically viable, because it
can lead to stack overflows (potential infinities are hard in
practice).
Scheme, a Lisp dialect originally created at MIT by Guy L. Steele, Jr.
and Gerald Jay Sussman, was the first Lisp to implement tail call
optimisation, which is a way of making significantly recursive
functions viable, making tail calls similar in memory requirement to
their equivalent loops.
Before talking (a little bit more) about tail recursion, let’s look at
concrete examples of a tail recursive function and its loop
equivalent. We’ve mentioned Fibonacci numbers already, so let’s try to
write functions calculate these (in Emacs Lisp).
Following the mathematical abstraction for the Fibonacci sequence
above, we could write a function like this:
;; -*- lexical-binding: t; -*-(defunfib1(nab)"Calculate the first `n' Fibonacci numbers, recursively."(if(<n1)a(consa(fib1(1-n)b(+ab)))))
Code Snippet 3:
a first go at a recursive elisp function for fibonacci numbers
I’ve tried to make this as simple as possible, we could make it nicer
(say, with a wrapper function or some some of flet) so that we didn’t
have to pass in the initial values. But, keeping it simple (for now):
fib1 is a function taking three arguments: n, the quantity of
Fibonacci numbers to return; a, the first number to start with; and b,
the second number to start with.
Following the schema above, we’re going to pass 0 for a and 1 for
b. Let’s get the first ten numbers of the sequence, and pass 10 for n:
(fib11001); (0 1 1 2 3 5 8 13 21 34 . 55)
I know, the output is a bit ugly because we’re just cons’ing the
results and so it’s not a proper list, but we’re keeping things
simple.
Let’s walk through how it works. The function first looks at n, if n
is less than 1, it returns a (whatever it is). If n isn’t less than 1,
we return the cons of a with the result of calling fib1 itself on “n
minus 1” b and “a plus b”.
So if we start with n=3, a=0, b=1, that is, evaluate (fib1 3 0 1), the
function would say, well, 3 isn’t less than 1, so I’m going to create
a cons with 0 and the result of calling (fib1 2 1 1) (2 = “n minus 1”,
because n is currently 3; 1 because b=1; and 1 because a + b = 0 + 1 =
1).
So at this point we have a cons that looks like this:
(0.(fib1211));; [= (cons 0 (fib1 2 1 1))]
When we evaluate (fib1 2 1 1), n is still not less than 1, so we’re
going to cons the current value of a (which is 1) with another call to
fib1: (fib1 1 1 2) (1 = “n minus 1”, because n is currently 2; 1
because b=1; and 2 because a + b = 1 + 1 = 2).
Now we have to evaluate (fib1 1 1 2). n is still not less than 1; so
we create another cons of 1 (as a is currently 1) with yet another
call of fib1: (fib1 0 2 3) (0 = “n minus 1”, because n is currently 1; 2
because b=2; and 3 because a + b = 1 + 2 = 3). And so now:
And, finally, evaluating (fib1 0 2 3), now n is less than one, so we
take the first branch of the conditional and just return a, which is
2. So the result of starting with (fib1 3 0 1) is:
(011.2);; [= (cons 0 (cons 1 (cons 1 2)))]
And you can try this with other values of n, e.g., try evaluating
(fib1 100 0 1) to get the first 100 members of the sequence.4
But, at least for me on Emacs 30.0.93, 529 is the limit. If we try
(fib1 520 0 1), the debugger pops up with a 1622 line long error,
which begins:
Code Snippet 4:
beginning of excessive-lisp-nesting error for our fib1 function
Because we’ve built up a long, deeply-embedded list of conses and
Emacs has a limit of how deep it’s willing/able to go.
In Scheme and elsewhere, self-recursive calls at the ends (“tails”) of
functions can be optimised to avoid these sorts of stack overflows
(excessive-lisp-nesting).5 Tail-call optimisation lets
procedure calls in tail positions be treated a specialised GOTO
statements, which can be efficiently processed:
…only in cases where structures are explicitly declared to be
dynamically referenced should the compiler be forced to leave them on
the stack in an otherwise tail-recursive situation. In general,
procedure calls may be usefully thought of as GOTO statements which
also pass parameters, and can be uniformly encoded as JUMP
instructions. This is a simple, universal technique, to be contrasted
with […] more powerful recursion-removal techniques…
But our fib1 function doesn’t do this, and we end up flooded the stack
with too many conses.
Back to loops
Recursive functions are perhaps most idiomatic in Scheme (among lisps,
I mean). Some implementations of Common Lisp can do tail-call
optimisation, but loops are perhaps more common, and certainly in
Emacs Lisp (for reasons you can see above), loops are usually what are
used. And so we can write a new Fibonacci function with a loop. It
won’t be nearly as pretty, but it’ll work better.
Here’s one possible implementation:
;; -*- lexical-binding: t; -*-(defunfib2(nab)"Calculate the first `n' Fibonacci numbers,
in a loop."(let((resultnil))(dotimes(cn)(setqresult(consaresult))(setqtempab)(setqb(+ab))(setqatempa))(nreverseresult)))(fib21001); (0 1 1 2 3 5 8 13 21 34)(setqbigfib-50000(fib25000001)); this will work - we can get 50,000 numbers
Code Snippet 5:
a second go at a fibonacci function, with looping
The result is prettier at least: a proper rather than an improper list
(because we started by cons'ing onto an empty list). Our fib2 function
itself isn’t as mathematically pleasing as our fib1 function, we end
up with a lot of setq's (the nreverse at the end reverses our list,
because the way we build up our list is by cons'ing the first results
first, so they end up at the end until we flip them with
nreverse). But it works well. If you try to (fib2 100000 0 1), it’ll
fail, but not because of stack overflow, just because we end up with
numbers that are too big for Emacs. But you can certain get the over
50,000 members of the Fibonacci sequence, which is much better than
fib1's limit of 529.
And dotimes is just one loop procedure available. (See cl-loop for a more
powerful one.)
Optimal Tail with Emacs
Ok, so, practically, we should probably generally prefer loops over
tail-recursive functions in Emacs. But, what if we just like the latter
more?7 Are there any other possibilities?
Wilfred Hughes has an emacs package tco.el which implements a
special macro for writing tail-recursive functions.8 It works by
replacing each self-call with a thunk, and wrapping the function body
in a loop that repeatedly evaluates the thunk. Thus a
function foo defined with the defun-tco macro:
And this delays evaluation in such a way as to avoid stack
overflows. Unfortunately, at least currently for me (Emacs 30.0.93
again), tco.el seems to have some issues.
In Emacs 28.1, cl-labels (one of the ways of sort of doing let's for
functions) gained some limited tail-call optimisation (as did
named-let, which uses cl-labels), but so far I haven’t found out how
to make it work in the way it seems like it should:
;; -*- lexical-binding: t; -*-(defunfib3(n)"Calculate the first `n' Fibonacci numbers,
recursively, with limited tail-call optimisation
through `cl-labels'?!"(cl-labels((fib*(nab)(if(<n1)a(consa(fib*(1-n)b(+ab))))))(fib*n01)))(setqbigfib3(fib3397)); 396 highest that works
Code Snippet 6:
a third go at a fibonacci function, with cl-labels
At least the way I’ve written it, it seems to suffer an overflow even
sooner (at 397 rather than 529 as for our fib1).
We could try to write an accumulator as a hack, where we try to do
ours conses one at a time and pass along the results, but this fares no
better than our fib1:
;; -*- lexical-binding: t; -*-(defunfib4(nabaccum)"Calculate the first `n' Fibonacci numbers, recursively,
but collect conses as we go and keep track of the length of
the `accum' cp. against `n'."(let*((accum(consaaccum))(accum-lng(lengthaccum)))(if(<naccum-lng)(nreverseaccum)(fib4nb(+ba)accum))))(setqbigfib4-529(fib452901nil)); last good(setqbigfib4-530(fib453001nil)); overflows
Code Snippet 7:
a fourth go at a fibonacci function, with an accumulator
If we combine cl-labels and the accumulator trick, however, we do seem
to be able to escape stack overflows:
;; -*- lexical-binding: t; -*-(defunfib5(n)"Calculate the first `n' Fibonacci numbers, recursively,
using both cl-labels and the accumulator trick."(cl-labels((fib*(abaccum)(let*((accum(consaaccum))(accum-lng(lengthaccum)))(if(<naccum-lng)(nreverseaccum)(fib*b(+ba)accum)))))(fib*01nil)))(setqbigfib5-10000(fib510000)); ok(setqbigfib5-50000(fib550000)); very slow, but ok
Code Snippet 8:
a fifth go at a fibonacci function, with cl-labels and an accumulator
Now we’re back in the realms of what our fib2 non-recursive loop-style
function could do. Although (setq bigfib5-50000 (fib5 50000))
calculates very slowly (worse than our looping fib2), so that’s not ideal.
Stream of Conses-ness
But here’s another possibility: Nicholas Pettton‘s stream package
for emacs, where “streams” are delayed evaluations of cons cells.
;; -*- lexical-binding: t; -*-(defunfib6(n)"Return a list of the first `n' Fibonacci numbers,
implemented as stream of (delayed evaluation) conses."(cl-labels((fibonacci-populate(ab)(stream-consa(fibonacci-populateb(+ab)))))(let((fibonacci-stream(fibonacci-populate01))(fibsnil))(dotimes(cn)(setqfibs(cons(stream-popfibonacci-stream)fibs)))(nreversefibs))))(setqfib6-10k(fib610000)); ok(setqfib6-50k(fib650000)); little slow, but works(setqfib6-100k(fib6100000)); little slow & overflow error
Code Snippet 9:
a sixth go at a fibonacci function, with delayed evaluation conses
This works well. (fib6 50000) still turns out to run a bit slower than
our (fib2 50000), so loops are still probably more efficient, but
streams are pretty interesting. They can can used to represent
infinite sequences. So here, above, fibonacci-stream (set by
(fibonacci-populate 0 1)) is actually an infinite stream of Fibonaccis
numbers, but lazily evaluated, so we just get the next one each time
we call stream-pop on our fibonacci-stream local variable. (What
happens is that stream-pop takes the car of fibonacci-stream,
evaluates and returns it, and then sets fibonacci-stream to be its cdr
(i.e., popping off and “discarding” the first element; which which
captured in our fibs collector.))
Cascades of Fibonacci numbers
Oh, incidentally and irrelevantly, if you inspect the contents of your
fib6-50k, it’s very aesthetically pleasing, a cascade of numbers:
Figure 2: fibonacci numbers burst forth from their seeds and spill out into the buffer
excessive-lisp-nesting: (overflow-error)
I had hoped to get to the Y Combinator today (and think I might have
suggested a promise of that), for that’s where things really get
interesting. And we need to get back to lambda calculus, of course.
But we may be near the limits of excessive lisp nesting ourselves
here.
However, the recursion discussion here has set the stage for the Y
Combinator, which we’ve already talked a couple of times, especially
in connection to John McCarthy’s claims about “not really
understanding” lambda calculus and the fact that these really centre
on his not seeing how one could get recursion without direct
Self-reference (and thus the need for LABEL) because of not knowing
about the Y Combinator.
And, the Y Combinator ties in with all sorts of other curious
things. Paradoxes, types, calligraphy.
McCarthy, John. 1978a. History of Lisp. In History of programming languages, ed. Richard L. Wexelblat, 173–185. New York:
Association for Computing Machinery. https://dl.acm.org/doi/10.1145/800025.1198360↩︎
“t if and only if p and q or if r then s and
not not not not not not t” ↩︎
A number of Indian philosophers, at least as far back as
Virahāṅka (ca. AD 600–800), gave formulations for what is usually
called the Fibonacci sequence. See Singh, P. (1985). The so-called
fibonacci numbers in ancient and medieval India. Historia Mathematica,
12(3), 229–244. https://doi.org/10.1016/0315-0860(85)90021-7 [pdf] ↩︎
You might actually want to do something like (setq my-fib1-100 (fib1 100 0 1)) to put the result into a variable, because
the echo area at the bottom of Emacs isn’t big enough for all of the
numbers. (Oh, to eval things in an Emacs buffer, put your cursor/point
at the end of the expression and press C-x C-e. But don’t do that for
this one. If you want Emacs to just stick the results in directly into
the buffer rather than echoing them, press C-u C-x C-e. But don’t do
that here either, because Emacs will still end up printing an ellipsis
because it thinks it’s too long.) And then press C-h v and type
my-fib1-100 and enter to see the result. ↩︎
See further here, for instance, for more about tail calls and
tail call optimisation. ↩︎
Steele, Guy L., Jr. 1977. Debunking the “expensive procedure
call” myth or, procedure call implementations considered harmful or,
LAMBDA: The Ultimate GOTO. ACM ‘77: Proceedings of the 1977 annual
conference, 153–162. [https://dl.acm.org/doi/10.1145/800179.810196] ↩︎
If you’ve read any of Paul Graham’s Common Lisp books
(e.g., On Lisp) or any of the Little Schemer books (the latter with
Duane Bibby‘s lovely artwork in them), you may be disposed towards
using recursion rather than loops.
Figure 4: close-up of Duane Bibby’s cover illustration for “The Little Schemer”
20 February 2025 Keychron K3 Pro: F1-F12 as default macOS keys
After resetting my Keychron K3 Pro, my F1 to F12 keys were no longer my default macOS keys. The entire row was defaulting to macOS's special keys (i.e. Mission Control, Launch Pad, Volume, etc). At first, I thought I may just need to revisit the macOS setting "Use F1, F2, etc keys as standard function keys", yet toggling the setting made no difference.
Turns out, I had remapped those keys long ago and simply forgot about it. Factory resetting my keyboard got rid of this customization. This post is a reminder for my future self, and anyone else looking to remap their F1-F12 keys.
Save your current layout
Via https://usevia.app, I saved my current keyboard layout (the out-of-box layout) and named it k3_pro_ansi_white(before).json.
Apply your changes
I made a second copy of the layout and named it k3_pro_ansi_white(after).json. In this new file, I located the two layers (first and second) and simply swapped the two row chunks using a text editor.
Fontaine allows the user to define detailed font configurations and set
them on demand. For example, one can have a regular-editing preset
and another for presentation-mode (these are arbitrary, user-defined
symbols): the former uses small fonts which are optimised for writing,
while the latter applies typefaces that are pleasant to read at
comfortable point sizes.
Backronym: Fonts, Ornaments, and Neat Typography Are Irrelevant in Non-graphical Emacs.
Below are the release notes.
Version 3.0.0 on 2025-02-20
This version changes the underlying implementation of Fontaine’s font
configuration presets. In principle, this should not have any effect
on how users experience the package, though there are some important
details that are different.
Fontaine is now a “theme”
Fontaine has always modified typography-related faces, such as
default, fixed-pitch, and variable-pitch, to apply the font
family, height, and weight specified by the user. In the past, this
was done in a way that could get overridden under certain conditions,
such as by loading a theme after setting a Fontaine preset configuration.
By making Fontaine a theme, we guarantee that its settings are not
undone. In practice, this means that users do not have to re-apply the
current preset after loading a theme. The function fontaine-apply-current-preset
is thus obsolete.
In Emacs, a “theme” is a bundle of configurations. Those typically
cover colours (such as with my modus-themes), but a theme can focus
on other settings as well. For example, the popular use-package is
internally done as a theme (check the value of custom-known-themes).
Fontaine is a theme in the same way use-package is, meaning that it
will (i) persist its effects, (ii) not show up in the
custom-enabled-themes and so not be affected by something like
(mapc #'disable-theme custom-enabled-themes),
and (iii) not be an option among those presented by load-theme.
There are no known bugs, though please contact me if you encounter a
scenario where Fontaine does not do the right thing. Thanks, in this
regard, to Haruko and Emily Hyland for reporting a couple of bugs:
In the past, users could apply a Fontaine preset to the current frame
without affecting other frames. While this could be useful in certain
situations, it was ultimately making the code more complex for
marginal gains. As part of the transition to a theme, which is anyway
global, I am removing everything related to frame-specific functionality.
Quality-of-life refinements
The fontaine-generic-face-families are used when necessary to
guard against nil values. Those font families are symbolic
references to whatever the operating system is configured to use
(e.g. on Linux this is handled by fontconfig).
If Fontaine is instructed to load an invalid preset, it displays a
warning and does nothing else. Before, it would produce an error,
which could prevent Emacs from starting up normally if this were to
happen at startup. A warning is enough to inform the user of what is
happening.
Same principle as above when Emacs is not ran in a graphical
interface. In text terminals, Fontaine cannot work because it is not
possible to have different font families, styles, and heights, than
those of the terminal (hence the backronym of FONTAINE “Fonts,
Ornaments, and Neat Typography Are Irrelevant in Non-graphical
Emacs”). Thanks to Jorge Gomez for the patch in pull request 13:
https://github.com/protesilaos/fontaine/pull/13. Further tweaks by
me.
The fontaine-toggle-preset command will produce an error if it
cannot find the preset it is supposed to switch to. The toggle is
between the last two loaded presets, as done by the command
fontaine-set-preset (the fontaine-mode takes care to persist the
relevant history).
Earlier I talked about using Org Mode to produce lesson worksheets
for my students. This year I am thinking about using powerpoint-style presentation slides instead of worksheets to encourage students to write more in their books. This naturally led me to look into how to do this with Org Mode.
A Simple Lesson
Making presentations in \(\LaTeX\) is normally done using the beamer package. It turns out that Org Mode has good support for beamer. An example of a simple lesson presentation org-mode file might look like this:
I have family visiting so this is another short post. Charles Choi has an interesting tip for Emacs emoji users on macOS. I rarely use emojis anywhere but in texts so I wasn’t aware of Emacs facilities for choosing emojis. It turns out that emoji-list will work on any Emacs installation but that there’s a better system on macOS. That’s ns-do-show-character-palette, which pops up the usual macOS emoji picker.
If you use it a lot, it makes sense to bind it to a key sequence shortcut. Choi prefers to do this sort of thing from the menu system but, sadly, ns-do-show-character-palette is not in the Edit menu by default. It is, however, easy to add it and Choi’s post shows us how to do that.
If you like to add emojis to your emails, say, you use something like Mu4e or one of the other Emacs Emacs clients, and you’re on macOS you will probably find Choi’s post useful.
I’ve had code for ages to handle images automatically in Emacs and the WordPress Emacs package, but it hasn’t been integrated well before, and it’s been a bit hacky, leaving temporary files behind and stuff.
So I’ve now finally cleaned it up, and here’s how it works in practice:
See? Blogging with images is easy in Emacs.
Now, for this to work, you need a camera that uploads snaps automatically, of course. I use a Sony a9, but there’s many options out there — or you can use a wifi SD card, like I talk about in that blog post, too.
Which reminds me: I was wondering whether the new Sony a1 II camera is good for my use case. The a9 is almost perfect, but since I’m shooting one-handed in low light conditions, it sounds like the a1 would be even perfecter. (That’s a word.)
Of course there are no real reviews for the thing I’m interested in: Does the wifi FTP functionality work as well on the new a1 as it does on my a9? But I found this:
Eeek! “Improved FTP capabilities and integration with Sony’s Creators’ Cloud for automatic file uploads to services like Adobe Lightroom or Google Drive” could either mean that the FTP works as before (i.e., it works really well locally, too), or it means that they only allow “improved” FTP towards “Sony’s Creators’ Cloud” or the other central storage solutions, which isn’t what I want at all. That’d be horrible. I hate improvements sooo much…
Anybody happen to know? Many other cameras also offer automatic upload functionality, but most of them also use some crappy cloud solution instead of local FTP — I’m running an FTP server on this laptop, and the camera uploads the images directly to the laptop. It’s fast and reliable.
I mean, the a9 works well, so I’m in no hurry to switch, but…
I may be dating myself, but I vividly remember setting up speed dials for my most frequently called numbers on my AT&T landline phone. In the early '90s, you could store a phone number in a numbered memory slot (referred to as "programming") and later dial your grandma, for example, by pressing SPD+2. Retro is in—so if you're too young to remember that and want to know more, just ask your favorite LLM chatbot to fill you in.
Speed-dialing as a user experience concept is widespread, although we don't normally call it that anymore. It is implemented as a feature that I use many times a day in my web browser. I use Safari on a Mac and typically keep many tabs open. I pin the first few to frequently visited URLs, like https://planet.emacslife.com. I can quickly switch to one of them using the keyboard shortcut CMD+1..9, always knowing which website I'll get. Other browsers offer similar functionality, though they may use different shortcuts, like CTRL+1..9.
The two apps I use most often on my Mac are Safari and Emacs, and I wondered, “Why don't I have a similar speed-dialing feature in Emacs?” It would be incredibly useful to switch instantly to my important files for reading or jotting down notes. I also like to optimize my keybindings, and consistency plays a big role in that—whether it’s adopting Emacs keybindings elsewhere or bringing external shortcuts into Emacs. It would be great to use the same CMD+1..9 shortcut to recreate this functionality in Emacs.
But doesn’t Emacs already have Tab Bar and Tab Line features? Maybe one of them (I can never remember which is which) could be adapted or enhanced to do what I want. Note, however, that I’m talking about speed dialing files, not tabs. I don’t want to select a tab or cycle through them—I want to jump directly to a specific buffer that’s visiting a specific file. Tabs feel a bit unnatural in Emacs; they make sense in browsers, but in Emacs, we typically work with buffers by name.
Direct addressing—using a name or a short index—is both powerful and highly efficient. Cycling is the least efficient method (looking at you, CMD+TAB). Completion is a middle ground—it requires extra keystrokes compared to direct addressing and is less predictable when the candidate list changes (in how many characters must be typed to get a single match). However, it’s essential when the list of candidates is long.
Direct Addressing > Completion > Cycling
In general, I prefer direct addressing whenever possible, completion when necessary, and cycling only as a last resort. Emacs' built-in bookmark-jump falls into the completion category. It would be my next choice if the number of my frequently used files was above ten.
Another reason I avoid using tabs for this in Emacs is that I don’t want to waste screen real estate on a tab bar if I don’t have to. My speed dials are mostly static—I may change them occasionally, but if I assign 1 to school.org and 2 to house.org, I want to stick with that. Thanks to muscle memory, I don’t need to see the list in front of me at all times. Plus, accidentally switching to the wrong frequently used file isn’t a big deal—I can quickly flip through a few of them to find what I need.
The beauty of Emacs is that I can create a Safari-like speed-dial experience with just a couple of elisp expressions in my init.el file.
As you can see, I use the <Super> key modifier to define bindings that match my Safari shortcuts, CMD+1..9. Note a little trick: using the mod function inside keymap-global-set to get s-0 to invoke the tenth speed-dial entry.
Currently, the speed-dial bindings simply call the find-file-existing function to switch to the corresponding buffer, opening the file if needed. But you can customize this further by using your own function for tailored behavior.
For example, you might use repeated presses of the same CMD+0..9 to change folding in an Org buffer, jump to a predefined heading, switch to a related buffer, or perform other context-specific actions.
Rather than visualizing the speed-dial entries as tabs, I found a way to display them without taking up valuable screen real estate. I simply splice the speed-dial labels into the Emacs frame title bar, which I don't really use for anything else. By default, it shows the current buffer name, but that information is also displayed in the mode line, which is where my eyes naturally go.
;; Inject my speed-dial list into the frame title
(setq frame-title-format (concat (mapconcat #'car /speed-dial-list " ")
" - %b"))
For my needs, displaying speed-dial entries in the Emacs frame title, followed by the current buffer name, works perfectly. My main Emacs frame is always wide enough to accommodate it. If I couldn’t use the frame title, I’d probably just open my init.el whenever I needed to check which speed-dial number maps to which file. But you might find an even better approach that works for you.
Enjoy the malleability of Emacs and the freedom it gives you!
The cross-platform Emoji picker (emoji-list) in Emacs is nice. But if you’re on macOS running the NS variant of Emacs, you can use the macOS-native picker which is better (😍). The command to raise this picker is ns-do-show-character-palette.
Unfortunately, this command is not configured to be in the main menu by default. The following Elisp initialization code will rectify that, placing the menu item “Emoji & Symbols” at the bottom of the Edit menu from the main menu. The :visible slot ensures that this will only take effect on NS Emacs.
12345
(easy-menu-add-itemglobal-map'(menu-baredit)["Emoji & Symbols"ns-do-show-character-palette:help"Show macOS Character Palette.":visible(eqwindow-system'ns)])
Note that users of the Mitsuharu Yamamoto fork of Emacs (aka Emacs Mac App) don’t have to do this as this behavior is already there. It used to be my daily driver but I switched over to the NS variant with the 29.4 release some time ago because the latter was actively maintained. I suspect (sadly 😞) that the Yamamoto fork is being left to bit-rot which is regrettable as that fork is the superior version when it comes to native macOS integration. With the upcoming Emacs 30 release, I think this will become even more pronounced. I'd be happy to be wrong on this though.
Just a quick post about a short note from Marcin Borkowski (mbork) concerning searching for the thing at point. Despite using Emacs for over 18 years I didn’t know about isearch-forward-thing-at-point.
As mbork says, it does just what it says. You invoke it with Meta+sMeta+. and it searches forward for whatever the point is on. This is something I want to do all the time and I was vaguely aware that it was possible but didn’t know exactly how to do it, so I’m really glad to see mbork’s post.
Finding out about this now serves to emphasize that learning Emacs really is a lifelong journey.
This is a simple package to automatically preview in a window the file
at point in Dired buffers. Preview windows are closed when they are no
longer relevant, while preview buffers are killed if they have not
been used for other purposes beside previewing. The package provides
several customisation options to control its behaviour.
Backronym: Directories Invariably Render Everything Decently …
preview; dired … PDFs Require Extra Viewing Instructions for Emacs
to Work.
Below are the release notes
Version 0.4.0 on 2025-02-18
This version contains several refinements and bug fixes.
Preview buffers have a mode line indicator
Preview buffers have a prefix to their name to make them stand out.
This is controlled by the user option dired-preview-buffer-name-indicator,
which is a string that defaults to [P].
Control how preview buffers are cleaned up
The way dired-preview works is to display a buffer and then keep a
list of preview buffers to economise on redisplaying it again. This
list of buffers is relevant for as long as we are in the Dired buffer,
otherwise all buffers therein are killed (buffers that were alive
before being previewed are not touched).
By default we delete from oldest to newest the accumulated buffers
when they exceed 10 in total. Though users can modify this behaviour
by editing the value of the new user option dired-preview-kill-buffers-method
(its doc string explains the technicalities).
The dired-preview-display-action-alist has a new optional function
The dired-preview-display-action-alist is the user option which
controls where the preview window is displayed. Its value can either
be the symbol of a function or a display-buffer-alist entry.
By default, we have a “do-what-I-mean” function that tries to find a
good placement for the window. The new dired-preview-display-action-alist-below
function has a straightforward behaviour: it always shows the preview
below the current window and it always makes the preview window 0.3
times the height of the Emacs frame.
Encypted files are no longer previewed
This is to ensure that potentially sensitive contents are not
displayed by accident, such as during a video call.
We no longer preview the “self” directory
We should not trigger a preview when the cursor is over the implicit
. directory, as that causes a recursion that breaks things. Thanks
to Inkbottle007 for reporting the bug in issue 23:
https://github.com/protesilaos/dired-preview/issues/23.
Miscellaneous
Fixed a scenario where we would try to delete the last available
window on the current frame. This should never happen. Thanks to
artelse for reporting a relevant bug in the discussion of issue 22:
https://github.com/protesilaos/dired-preview/issues/22.
Fixed a case when hexl-follow-ascii could fail to find an overlay
under certain conditions. This did not create any noticeable
problems, though having an error there would interfere with any
workflow that would rely on toggle-debug-on-error.
The preview window will automatically be closed if the user switches
outside the given Dired buffer. We now do not consider a change to
the minibuffer as being “outside” this context. This way, a quick
M-x to, say, enable a minor mode does not have any effect on the
window layout.
Suppressed the messaging facility of the underlying tracking of
preview buffers. Otherwise, Dired would notify us that the directory
has changed whenever we would preview a new one, which is
superfluous.
The body of the dired-preview-trigger function, which determines
whether a preview will be displayed, is encapsulated in a condition-case.
This helps capture errors and thus have a more predictable behaviour.
The dired-preview-display-action-alist has a more accurate
declaration which allows for its correct customisation inside the
Custom UI interface. In particular, it will behave the same way as
the display-buffer-alist, where relevant.
Everything is cyclical in computing, so people move between writing things in raw HTML and using arcane and unholy systems, mostly based on some Markdown dialect. I understand the frustrations: It feels like there should be something that’s less annoying than using some WYSIWYG tool that invariably freaks out and ruins your post, or typing all that annoying HTML yourself, or using Markdown and then having to have some kind of build step.
In my opinion, Markdown is fine for writing README files, but if you’re writing blog posts, it just gets in the way. A blog post is mainly just paragraphs like the one I’m typing (and you’re reading) now, which is just text with no markup. Or there’s some slight formatting for emphasis or the like, but honestly, there’s not much difference between the HTML and Markdown versions for that.
Markdown is nice for headings and code snippets, but doesn’t really offer much useful for blog posts. And the things that blog posts need, which is images/screenshots and links: Markdown doesn’t help you much there.
Is that really better than the HTML version? And what, then if you need more stuff in the link?
It just gets worse and worse — what if you need to put more data into the links? The nice thing about HTML is that it’s well-formed and not very hacky — the more cruft you add to the HTML, the more unreadable it gets — but linearly. Markdown makes the easy stuff trivial, and the difficult stuff worse. (Here’s there the Greek chorus of “but you can just write HTML in Markdown” comes in, but that’s worse than just writing HTML in the first place.)
So: I write HTML, and Emacs takes care of displaying the images I’m linking to, so a blog post looks like this while I’m writing:
(To digress: I’ve noted over the years the many, many posts on HackerNews about statically generated blogs, and people have more fun spending time tinkering with their setups than actually writing blog posts, and that’s fine. But I’ve noted that virtually none of these systems have a mechanism for dealing with images in a natural way — because that’s just kinda hard. The nearest you get is “then you just create an S3 bucket and put the image there, and then you go to the AWS console to get the URL, and then you paste that into the Markdown here. See? PROBLEM SOLVED!!!” That’s why blog posts from all these people (random example) are almost always just walls of text.)
Anyway, here’s my problem:
YIKES! WHAT THE… Yes, I hear you.
To protect myself a bit against link rot, ewp screenshots everything I link to automatically. So on the blog, you can just hover over a link to see (and read, if you want to) what I was linking to at the time, and that will survive as long as my blog survives (while most of the things I’m linking to disappear, apparently).
But that means that I have to stash that data somewhere, and I stashed it in the links, which means that the HTML then becomes unreadable.
This is Emacs, however. What about just hiding all that junk?
Yes, that’s the same paragraph with the links hidden. And if I want to edit the links themselves, I can just hit TAB on the bracket:
And TAB again to hide:
Note that the links and stuff are still present in the Emacs buffer, so the normal Emacs autosave functions work perfectly, and there’s no danger of losing any data.
Similarly, the image HTML in WordPress can be pretty messy:
Because images have extra classes with their IDs, and you can click on images to get the full sizes, so they’re (almost always) wrapped in an <a>. Now, when writing articles, Emacs displays the images instead of the HTML, so we don’t see all that cruft anyway, but when editing image heavy articles, it can take some time to fetch the images, and we don’t want to be staring at junk like that while waiting for the images to arrive.
So let’s hide them like this:
And TAB can be used to cycle through the three different forms:
I think that looks kinda pleasant to work with…
Anyway, I think that’s as far as I want to go with hiding the HTML-ness of things. I mean, the temptation here is to start going in a more WYSIWYG direction, and translating <b>…</b> into bold text and all that sort of stuff, but… I’m more comfortable just looking at the tags?
So there you go: In the “just write HTML/no don’t write HTML” wars, I’m on “just write HTML but have the editor hide some of the worst of the cruft” tip.
Vineet Naik has a post with a nice tip. He often has complex build processes and rather than automating them with a script, he prefers to use an Org checklist that specifies the steps. He likes to keep the checklist in the project directory and check off the build items as he performs them.
That seems pretty straightforward but there’s a catch. After he’s done the build and checked off all the items the list is no longer useful. He’d like to reset the checklist to “empty” but what’s the best way of doing that? My first reaction was to keep the checklist as a Yasnippet, delete the completed checklist, and use the snippet to create a new copy.
But, he says, what if he needs to make a change to the checklist during the build? Then he has to remember the change the snippet too. The thing about Org mode is that everything is just text so he solved his problem by doing a query-replace to change [x] to [ ]. That worked well so his next thought was to automate it by writing a bit of Elisp.
That’s when he discovered that Emacs already has a package for that. That’s the point of the tip. After loading the package you need simply add :RESET_CHECK_BOXES: t to the property drawer and Emacs will handle everything for you. When the status of the checkbox tree changes from TODO to DONE, all the checkbox items are cleared and the status is reset to TODO. All this happens automatically. That makes it perfect for Naik’s problem.
There’s actually a bit more to the solution so be sure to check out Naik’s post for the details.
One thing which I do very often is isearching for some term, like a function or variable name. Recently I learned that Emacs has a nice feature which helps with that. Pressing M-s M-. (or M-s .) invokes the isearch-forward-thing-at-point command, which does exactly what you would expect. Very useful! In fact, pressing M-s C-h reveals even more usefulness. Try it out for yourself!
Shell Smith is a Clojure library that simplifies the creation of
command-line interface (CLI) tools by providing a robust configuration
management system with smart defaults based on your namespace.
Key Features
Namespace-Aware Configuration: One of Shell Smith's most powerful features is its ability to automatically use the current namespace as the default name for:
Configuration file naming (<name>.yml)
Environment variable prefix pattern
When no explicit :name option is provided
Multi-source Configuration: Shell Smith manages configurations from:
Default values
YAML configuration files
Environment variables
Command-line arguments
How It Works
The library uses a config macro that merges configuration values from
different sources. Here's an example showing both explicit naming and
namespace-based defaults:
;; With explicit naming
(def config-explicit
(shell-smith/config usage
:defaults {:port 3000}
:name "mytool"))
;; Using namespace-based defaults
;; If your namespace is 'my.app.server'
;; - Will look for 'my.app.server.yml'
;; - Will match env vars starting with 'MY_APP_SERVER_'
(def config-from-ns
(shell-smith/config usage
:defaults {:port 3000}))
Configuration Resolution
The configuration sources are merged in this order (from lowest to
highest priority):
Default values
YAML configuration file (<name>.yml in current working directory)
For example, if your namespace is my.app.tool, Shell Smith will
automatically:
Look for my.app.tool.yml in the current directory
Match environment variables starting with MY_APP_TOOL_
This convention-over-configuration approach makes it incredibly easy
to organize your CLI tool's configuration while maintaining
flexibility when needed.
Everyday I see several articles from—usually—younger developers opining that Emacs is too old-fashioned, non-intuitive, and, really, just too hard to bother learning. And, it doesn’t look nice like VS Code and other modern editors.
Okay, they’re young and don’t know any better. Maybe they’ll learn as they go along. But there are also older developers who have been using Emacs for years who say they’ve been seduced by the bling, and lately, AI integration. They too are choosing to use editors other than Emacs.
One would think, from reading all those posts, that there’s a one way current flowing from Emacs towards those lesser other editors. But that’s not true. There are still people who get a hint of Emacs’ power and make the effort to learn it. Ivan Prikaznov is a case in point.
He came into an IntelliJ shop as a green engineer so, of course, he used the IntelliJ IDEA editor like all his colleagues. But then he stumbled on Vim and learned about efficient editing. Eventually, he changed jobs and found himself editing a large configuration file. He realized that he was “essentially executing an algorithm by hand”. At that point he grasped that what he needed was a programmable editor and he turned to Emacs.
He hasn’t looked back. Now he does everything in Emacs and the Borg has assimilated a new member. Or something. In any event, it’s nice to know that Emacs is also gaining converts. It may not be a rapidly as the “in” editors but we’ve always known that Emacs is for the discerning.
I’ve used multi-term for years in emacs as my primary terminal interface. It works great, but occasionally while ssh’ing to another machine and using the emacsclient -t interface (my common approach), the terminal gets stuck to a too-small size. This happens most commonly when my ssh session crashes and I have to start a new one.
It turns out the cause of this is that the terminal is taking its measurements from the other emacs window it’s bound to, and if you unbind it, it’ll be willing to take measurements from a new window.
Continuing with our “Emacs for all things” theme of the last couple of days, here’s another post that speaks to how Emacs can be used for many things and how seemingly difficult or even impossible tasks can be done by using the right packages.
The topic of the post is using Emacs for taking research notes. S4h4rJ says that he loves Emacs but that he finds it hard to use for taking research notes because it’s so hard to deal with figures (jpegs, etc.). They are, he says, hard to size and hard to place within the file.
That didn’t seem right to me and other researchers chimed in explaining how they handled the problems that S4h4rJ was complaining about. It’s surprising how often the answer is simply installing and using an existing Emacs package.
The TL;DR is that there are many researchers who use Emacs to take and organize their notes. They use things like org-download and org-roam to deal with problems specific to their workflows. Take a look at the comments to S4h4rJ’s post for the details.
The takeaway is lots of serious researchers are using Emacs everyday for taking and organizing their research notes. If you’re a researcher and want to use Emacs, there’s no reason you can’t. You just need to be aware of a few packages and techniques that ease the process. Read the comments to S4h4rJ’s post to get an idea of what’s available. Even if you’re not a researcher, you may find those packages useful for your own workflow.
One of the odd things about writing Irreal is that I never know which posts will be popular or at least provoke some engagement. Often, posts that I think are really interesting receive no comments and posts that I suspect will be of marginal interest strike a chord with readers.
Yesterday’s post, Reasons You Can’t Use Emacs More, is an example of the latter. I wrote it mostly because I was enraged by the idea of people who don’t use editors telling people who do which editors they can use. I didn’t expect most people to care but there are, it seems, a lot of our colleagues suffering from that and they are, likewise, enraged.
Of course, they’re hackers and often find ways of bypassing the nannies. But not all the problems are caused by the nannies. Often, the issue is finding some way of a way of performing a necessary task with Emacs. This usually arises when the “normal” app for performing some task won’t interoperate with Emacs.
Serendipitously, I found this Emacs subreddit post by arni_ca asking what sort of tasks people perform with Emacs. That seems only marginally related to JTR’s problems from yesterday’s post but when you read through the comments you find lots of ways people have found to do things in Emacs even if it doesn’t seem possible at first glance.
It is, really, an encouraging post because it shows that it’s very often possible to find some way of importing an important task into Emacs. The real problem is discovering those methods. Posts like arni_ca’s help but that still means reading through a lot of blogs and reddit posts to find them. Sacha’s Emacs News is a good place to start. It provides a weekly review of interesting Emacs news and helps keep you up to date with minimal effort.
Emacs Font is wider than other applications. Most people don't notice the difference. If you can perceive it, you are not hallucinating. This can be attributed to the following:
Points per inch
#ifndef HAVE_ANDROID /* Number of pt per inch (from the TeXbook). */ #define PT_PER_INCH 72.27 #else /* Android uses this value instead to compensate for different device dimensions. */ #define PT_PER_INCH 160.00 #endif
/* Return a pixel size (integer) corresponding to POINT size (double) on resolution DPI. */ #define POINT_TO_PIXEL(POINT, DPI) ((POINT) * (DPI) / PT_PER_INCH + 0.5)
/* Return a point size corresponding to POINT size (integer) on resolution DPI. Note that though point size is a double, we expect it to be rounded to an int, so we add 0.5 here. If the desired value is tenths of points (as in xfld specs), then the pixel size should be multiplied BEFORE the conversion to avoid magnifying the error. */ #define PIXEL_TO_POINT(PIXEL, DPI) ((PIXEL) * PT_PER_INCH / (DPI) + 0.5)
Rounding-off error is unavoidable. While looking for its source, look for how it's compensated in the application being compared.
There are two main issues. The first is work problems. Lots of companies make it difficult to use anything other than “approved applications”. These are almost always brain-dead Windows apps that don’t work all that well and certainly don’t interoperate with others apps.This sort of thing is usually driven by what my son calls the “Notwork Nazis”, his term for the network engineering folks having an obsession with making sure that not a single unauthorized activity takes place on “their” network.
These guys don’t—usually—care what you do on your own machine as long as it doesn’t impinge on the network. There are, sadly, more extreme cases. Consider this case of of a company so clueless and intent on controlling every aspect of their employees’ work environment that you can’t use Emacs at all because it’s “An old fashioned and slow text editor created by Canonical for use with the Ubuntu operating system.” These morons are actually scanning machines to make sure no unauthorized editors are being used.
There are many degrees of this dysfunction. If it’s only that you can’t access company Email through Emacs, that may be tolerable. If your management thinks Emacs was developed by Canonical, it’s probably time to find another job.
The second problem that JTR encounters is that Emacs doesn’t interoperate with some apps that are important in his workflow. He gives the example of Grammarly. Being a curmudgeon who doesn’t like being told what to do, I’m not a Grammarly user but I take JTR’s point.
These apps obviously have an API so they can interoperate with others apps but sometimes they’re loath to share them. I’m not sure why that’s so. Wouldn’t you want your app to work with as many other apps a possible?
In any event, it’s a sad truth that it’s not always possible to use Emacs as much as you’d like.
Every now and then you’d be trying out a new color theme, that you really like overall, but you’d
like to tweak a bit here and there to make it perfect. After all, that’s what Emacs
is all about - creating the perfect editor for yourself.
Sometimes you might be dealing with missing face definitions or configuration
options that you might want to submit upstream, but most of the time the changes
you’d like to see are probably quite subjective and belong in your personal
config. So, how do you make those changes?
There are 3 common ways to adjust font faces in Emacs and I’ll briefly cover all
of them. Option number 1 is the tried and true classic custom-set-faces:
That’s what gets generate if you’re adjusting faces with something like M-x customize-face.
The bad thing about this approach is that those customizations will active regardless of your
currently selected color theme and if you like to switch themes that’s not cool. Fortunately, it’s
easily to narrow customizations to a particular theme with custom-theme-set-faces:
I’d suggest perusing the documentation of set-face-attribute (e.g. with C-h f)
as it explains in great detail all the possible attributes you can configure
for a font face. The number of properties you can set is truly epic, but most of
the time you’ll need to tweak only a couple of them. (e.g. :foreground, :background, etc)
Technically speaking, you can go a step further than that and define your own theme
that extends the color theme you want to modify1, but that’s an overkill unless you
plan to distribute this theme as a package.
All the examples above are kind of random, so I’ll conclude here with some real modifications
I do in my config to the popular Catppuccin theme:
(use-packagecatppuccin-theme:config;; or 'latte, 'macchiato, or 'mocha(setqcatppuccin-flavor'macchiato)(load-theme'catppuccint)(custom-theme-set-faces'catppuccin;; by default the theme uses the same face as for comments, which is wrong IMO'(font-lock-doc-face((t(:foreground(catppuccin-color'green)))));; font-lock variable definitions like function definitions'(font-lock-variable-face((t(:inheritfont-lock-function-face))))))
The example above also shows how to access the colors from the palette of some color theme
outside of its definition. Usually themes provide some API like theme-name-color to
get able to get the color codes easily.
Funny enough, as I’m writing this I realized that use-package actually has built-in
support for customizing faces that I could have used instead. Here’s an example of that
in action:
This example also reminded me that I should expose the Zenburn colors via functions.
So, to summarize:
If you’re using use-package it’s probably best to use it’s :custom-face functionality.
The rest of the time you most likely need custom-theme-set-faces.
One thing is certain - with Emacs there always numerous ways to achieve something!
Note: To see the new font faces in action you’ll either have to restart Emacs or
evaluate Elisp code that sets them. (e.g. with C-x C-e)
One final tip - if you’re wondering what’s the face used by some text, the best
way to figure it out is with the M-x describe-char command. It will give you a ton of
information, including something like this near the end:
There are text properties here:
face (font-lock-keyword-face markdown-code-face)
font-lock-fontified t
font-lock-multiline t
fontified t
markdown-gfm-code (2617 3092)
I had placed my cursor over the word “use-package” in the code snippet above, while writing
this article in markdown-mode, therefore the faces font-lock-keyword-face (coming from elisp-mode)
and markdown-code-face (from markdown-mode).
Do you have any tips on customizing color themes that you’d like share?
That’s all I have for you today. Keep hacking!
Remember that Emacs allows you load multiple themes with them stacking one upon another. ↩
Customised build of the Iosevka typeface, with a consistent rounded
style and overrides for almost all individual glyphs in both upright
(roman) and slanted (italic) variants. This is the successor to my
now-discontinued “Iosevka Comfy” fonts.
Backronym: Aporetic’s Predecessor Objects’ Reserved Eponym Truly Included “Comfy”.
Below are the release notes.
This release includes two stylistic corrections that pertain to
Aporetic Serif and Aporetic Serif Mono.
The first change is to the slanted (italic) form of the letter t.
Before, it was mistakenly set to have a curved, upward-facing bottom
stroke, which would clash with the flat bottom of i and l. Now
the slanted t has a flat bottom as intended. The upright (roman)
variants are always flat in this regard.
The second change is to the letter m in both upright and slanted
forms. Before, the m would have a top left serif, as intended, but
not a bottom right tail. The tail is a feature of other glyphs that
need to have such a style, like a, h, n, u: it imposes a
proper rhythm together with the rest of the serif details. Now the m
has its missing bottom right tail, making everything consistent.
[ The m has a shorter middle leg in all the “mono” families” to
improve readability, especially at small point sizes. The
proportionately spaced fonts use a normal middle leg, as m is
naturally wider there and thus is already perfectly legible. The
other details are the same. ]
It’s a valid point but I think there are better examples to make it. How about
(dotimes (i 10)
(print i))
versus the same in C
for ( i = 0; i < 10; i++)
printf( "%i ", i)
The Elisp macro suggests that we want to perform its body for the values \(0 \cdots 9\). The C for loop construct is all about initializing, incrementing, and terminating the loop. Of course, the same can be said of the Lisp do construct.
There’s nothing wrong with either of these approaches, of course. I’ve written a lot more C than I have any type of Lisp and the semantics of the for loop are embedded in my brain. Still, AbstProcDo has a point. The Elisp does seem more natural.
It would be easy to make too much of the comparison and enlist it for use in some sort of language war but that’s not my intention. I merely think it’s a provocative idea and worth thinking about. There are, I’m sure, counter examples, and I’m sure we’ll be learning all about them from the comments.
Earlier this morning, when I was up between my sleep phases, I was looking for some Emacs content through irreal, one of the most prolific Emacs blogs out there. Irreal publishes a post every day, and these posts usually summarize and link other Emacs-related posts to other blogs. It’s easy to find blogs with good Emacs stuff and check their archives for even more Emacs. That’s the life of an Emacs user - learn it, tweak it, find another cool thing you haven’t thought about, learn it, tweak it…
I would like to have more Emacs in my life, but unfortunately, it’s not easy.
My blog archives are full of complaints about Microsoft products and web tools that I have to use because of work. I can’t use email in Emacs because logging into Office 365 for work is restricted, and no other apps but Outlook can access it (not even Apple Mail). ServiceNow, the platform we use for IT tickets, has an API, but it’s also heavily restricted, forcing us to use the browser. Communications and phone calls happen on Teams, another closed Office 365 application. It’s not even just Microsoft specifically, even though I like to blame them: it’s the cloud.
I work with different IT departments, engineers, and managers. Usually, when app X doesn’t answer certain needs, the solution is to find a new app, which in turn is also integrated in the cloud with its own restrictions. This happens so many times that we don’t get the chance to explore the depth of one app before there’s another one. Each person brings his own new favorite app to add to the party.
I’m guilty of this too, on a personal level. I love writing in Emacs, but my favorite writing companion, Grammarly, doesn’t work with Emacs (yes, I know there were some packages for it in the past; they were abandoned, and as far as I know, Grammarly doesn’t have a working API anymore). Micro.blog uses its own macOS app for writing content, which brings convenient integration to my other content (like my photos and saved bookmarks) that I don’t currently have in Emacs, so I just copy-paste my posts into it these days. Even good tools like being on my iPhone are not as fluid as Apple Reminders or Notes, and it’s just easier to start something there and have the discipline (this is the weak link) to bring it all back into Emacs later.
Still, despite all of that, I love working in Emacs. It brings me peace that no other app does at this point because it’s entirely mine. I know where everything is, I know how to tweak it (or I can learn how to), and I can access its org files everywhere, even if I don’t have Emacs installed. No other application organizes my life and projects so well and for so long, and I don’t see anything replacing it in the near future.
Xenodium's excellent chatgpt-shell package makes it easy to use ChatGPT and other LLMs from Emacs.
Having all of Emacs's editing power and programmability while working
with an LLM is a big win.
ChatGPT requires a license key, and chatgpt-shell needs access to it. It would be a bad idea to store the key in a plain text file, so I
looked for a way to encrypt it. The chatgpt-shellREADME.org documents how to use the pass password manager, but I hadn't used pass before, so I did something even simpler: I used Emacs's built-in
support for GPG. Maybe you'll find this approach useful, too.
I set up chatgpt-shell by adding this code to my Emacs init file:
Now, when chatgpt-shell starts, it reads ~/.emacs.d/openai.gpg, prompts for its password, and decrypts it. It uses the full contents
of the file as the API key.
To create your own openai.gpg file, just C-x C-f ~/.emacs.d/openai.gpg, enter the key, and save. Emacs will prompt you for a password, then
use GPG to encrypt the file.
Now that I've written this, I should probably set up pass.
Edit on Wed 12 Feb 2025: I tried pass, and it's great. It's well documented, easy to use, simple, and
replicates passwords between machines using Git. Here's my new setup
for chatgpt-shell:
I am reorganising the denote package to have a clear separation
between “core” and “extensions”. The idea is to decouple the two. The
denote package shall provide only the core functionality, while all
other features we already have will be available as standalone
packages.
The reason I am doing this is because the project has organically
grown over time to encompass lots of useful-yet-inessential
applications, such as Org dynamic blocks, journaling capabilities, and
sequence schemes, among others.
All those extras are nice to have, though they dilute the message
about what Denote is, making it seem far more complex than it actually
is. They are also held back by the minimalist outlook of the core:
they cannot be developed to their logical end, as any dependency they
incorporate becomes a dependency of the whole project, which makes no
sense (e.g. we can have a transient.el to interact with Denote
commands, but this is in no way essential, so why force it upon
everyone who downloads the denote package?).
The core
Denote essentially is a file-naming scheme. We create new files or
rename existing ones to have file names that are easy to retrieve with
even basic tools. This is my use-case and the reason I wrote Denote: I
name my videos, PDFs, pictures, and “notes” with the Denote
file-naming scheme, making it easy for me to find everything.
I think the Denote file-naming scheme is ingenious, though the real
value is in having a scheme—any scheme—to force consistency in how
you name things. Consistency begets predictability, which in turn
increases the likelihood of finding your data.
The other part of retrieving information is through links. Part of the
Denote file-naming scheme is the date+time, which is a unique
identifier. We can thus link to any file in the denote-directory
using its identifier. Once we have these “forward links”, we can
easily figure out what the “backward links” of a given file are, i.e.
which files link to the current one.
This is the core, plus a few other conveniences that I need not
enumerate herein.
The extensions
Anything that builds on the aforementioned is an “extension” and will
have its own Git repository as well as user manual. To this end, I
have already removed denote-sequence.el from the denote core and
made it its own entity:
Package name (GNU ELPA): denote-sequence (⚠️ Not available yet)
The plan is to do the same with the “Org extras”, such as with all the
Org dynamic blocks, the “Markdown extras”, the “journal extras”, and
the “silo extras”. Once all the packages are ready for widespread use,
I will add them to GNU ELPA. Until then, everything is a WORK-IN-PROGRESS.
There will be no reduction in the total set of features we provide.
This is only a matter of reorganising what we have, namely, to (i)
make it easier for new users to understand what Denote is, (ii) pick
only the extensions they require, (iii) make it possible to decentralise
the maintenance of the project should I ever need to step down (which
is not happening, but as a matter of principle).
Those granted, keep in mind that Denote is not a “second brain” and
will not make you smarter. It is a flexible and capable tool, truly
Emacs-y in its adaptability, that you can use as part of a workflow
that makes sense to you. Let us then decouple the core from its
extensions and continue to give users the best possible experience
with every piece of code and documentation that we write.
Based on my experience of building and maintaining professional and
personal/hobby projects, I've come to realize that I often tend to
prefer well documented checklists over automated scripts for recurring
workflows.
Let me be clear about what I mean by recurring checklists first. I
have a side project which is a web application that uses VueJS for the
frontend, Rust for backend, and
tapestry for generating SQL
files from jinja templates. It runs behind nginx and is managed using
systemd on a VM. As you can see, there are many steps involved in
building and deploying the app and for that I have a checklist in the
same repository that comprehensively documents every single step. Even
though I have previous experience of automating such workflows, I
refrain from doing it here, because every time I have to build and
deploy the app, I am happy that it's a checklist and not a script.
As I began writing this article, I thought about the reasons behind
such a preference, but that part itself got so big that I felt it
deserves to be a separate post. It's sitting in my drafts folder now
and I hope to publish it soon. Today, I'll stick to how I manage such
recurring checklists in emacs using org mode thanks to a hidden gem
from the org-contrib package.
Now org mode supports checklists out of the box. You just have to
create a plain list
under an outline entry and prefix it with a
checkbox i.e. [ ].
A build checklist for the above app would look something like this:
* Build- [X] Generate SQL files using tapestry
#+begin_src bash
cd <dir>
tapestry render
# etc.
#+end_src- [X] Build backend
#+begin_src bash
cargo build --release
# etc.
#+end_src- [ ] Buiild frontend
#+begin_src bash
npm run build
# etc.
#+end_src- [ ] Create a tarball
#+begin_src bash
# tar czf ..
#+end_src- [ ] Upload to s3
#+begin_src bash
# aws s3 sync ...
#+end_src
The only problem is that these tasks need to be performed repeatedly
i.e. every time I have to build and deploy the code. To address this,
the first thing I reached out to was
(ya)snippets. In past, I've
used snippets quite effectively for recurring activities. For example,
I had a snippet that expanded to a template org tree for taking notes
during a meeting. There was another similar one for taking interviews.
But in case of build/deploy workflows, the expanded checklist is
practically of no use once all the items are checked off. In case of
meeting notes or interview notes, the information captured in the
expanded org tree during the meeting/interview is usually worth
retaining for future reference. Another problem with snippets was that
while performing the tasks if the checklist had to be updated due to
any deviation, I had to remember to update the snippet as well.
The next thing I tried out was to directly store the expanded
checklist in the repo with all items unchecked. Org being just plain
text, I can simply use M-x query-replace to uncheck all items again
after executing the checklist. When this worked well for me, I thought
it might be a good idea to wrap this into an interactive elisp
function and bind it to some key.
Now emacs has a funny way of always being one step ahead of you!
Whenever you find yourself thinking "wouldn't it be great if emacs
could do this?" chances are it already can, or someone in the
community has already built a package for exactly that purpose. And
sure enough, there's
org-checklist.el
in org-contrib which does exactly what I want!
First you need to install the org-contrib package and require
org-checklist file in your init.el:
Then just set the property RESET_CHECK_BOXES to t in the org
tree. You may do this using C-c C-x p which will show a prompt for
property names and let you enter the value in the minibuffer. It will
also create the property tray if required.
Now my checklist looks something like this (individual tasks collapsed
for brevity),
* TODO Build SCHEDULED: <2025-01-30 Thu .+1d> :PROPERTIES: :RESET_CHECK_BOXES: t :LAST_REPEAT: [2025-01-29 Wed 11:29] :END:- State "DONE" from "TODO" [2025-01-29 Wed 11:29]
- [X] Generate SQL files using tapestry...
- [X] Build backend...
- [ ] Buiild frontend...
- [ ] Create a tarball...
- [ ] Upload to s3...
The org item is marked as TODO and a recurring schedule is set with
the .+1d cookie. When the state is changed to DONE, the following
things happen automatically:
all checkboxes get unchecked,
the time when the state was changed to DONE gets recorded,
the org item becomes TODO again and the scheduled date gets
shifted to the next day
I may not actually end up running the workflow on the next day, but
the .+1d cookie ensures that even if it's repeated next after say 4
days, it won't consider the task as overdue for the previous 3 days 1.
The changes to the org files are committed in git, but I make it a
point do so only after the above side effects have taken place
i.e. the org entry is in TODO state and all items are unchecked
. This way the diff only contains the time when the checklist was last
executed.
With this workflow, there are no additional org entries created with
duplicate data that I'd have to archive later. If I have to update the
checklist during execution, I can do it there itself and commit the
changes in git. But more than anything this workflow feels so much
natural and native to org mode.
Footnotes
1. Not sure if I'm making sense here! Repeater
cookies are explained with better examples in org mode
docs. ↩
One of the common complaints of Emacs users is “I defined this cool little command to make my life easier and then I forgot to use it”. Well, I found one way to help with that.
Bozhidar Batsov, the developer of projectile, has announced some updates to its caching behavior. The idea is that a projects files are cached so that they don’t have to be reindexed every time your bring up a project.
Projectile has always done this, of course, and it works well but Batsov has always had in mind some improvements and is now getting around ti implementing them. His post is interesting because it gives us a window into his thinking. It’s always instructive a have a good developer explain his choices and his thinking.
Take a look at his post to see what I mean. Batsov says that most people have probably abandoned projectile for project.el but he believes that projectile still has some things to offer. I’m not in a position to say but I am a fan of Batsov’s work, especially in this area.
This post contains LLM poisoning. 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 this may be ... Read more
Literate Programing and snippets The Problem: orgmode literate programing is incompatible with orgmode snippets Submitted to the orgmode mailing list1 and to reddit2.
I have enjoyed using Orgmode for literate programming3 for years. I have a script that runs whenever there is a change to the file(s), retangling every tangle src block in the whole file. Just now I’ve finally acted to encode my snippets in my literate setup. For anyone unaware, snippets (I use yasnippet4) are shortcut keystrokes that transform into more elaborate text when used.
You’ll notice Emacs inspires words like “magical,” “beautiful,” and “elegant” in descriptions of its interface and functionality. It’s not surprising when you consider how nicely it simplifies everyday work tasks. (Once you have dedicated the time necessary to progress past the dizzying learning curve, of course.)
In my case, I needed an easy way to export my Org Mode documents to the myriad formats and specifications of the writer’s market. Some custom functionality got it done, and I’m finally able to say, through trial and error, that my entire process for composing and submitting fiction (and, prospective publication, of course) is locked in.
Coincidentally, Neal Stephenson mentioned this same problem to Lex Fridman, and they made a clip of it. “The publisher put their foot down, and they want it in Word format now.”
Let’s get into this.
Exporting from Emacs to Word (Or, Wherever)
The theory is simple. I use LibreOffice Writer as a go-between for Emacs and other formats needed in the writer’s marketplace (mostly Microsoft Word or Rich Text Format). This way, you can compose in Emacs without having to reformat your document every time a certain market demands slightly different parameters.
Emacs --> LibreOffice template(s) --> Microsoft Word (.docx)
Using my csm/office-export function, I can have a virtually infinite amount of templates. If a particular publication demands a unique specification, I can generate a new template for it.
Here is the function I use for ODT export. I make no guarantees, of course, but it works well for me. If you want to try it out you can simply replace the dummy values with your own personal template “nice names” and file paths.
(defuncsm/office-export ()
"Export Org file to ODT with a user-selected template using nice names."
(interactive)
;; Ask if we should keep line breaks
(let* ((preserve-line-breaks (y-or-n-p "Do you want to preserve line breaks? "))
;; Ask if we should export just the body (no header, footer, title, or author)
(body-only (y-or-n-p "Body only?"))
(templates '(("Template 1" . "~/path/to/template1.ott")
("Template 2" . "~/path/to/template2.ott")
("Template 3" . "~/path/to/template3.ott")))
(nice-name (completing-read "Choose ODT template: " (mapcar 'car templates)))
(template-path (cdr (assoc nice-name templates))))
;; Use `let` to locally bind export options
(let ((org-export-preserve-breaks preserve-line-breaks)
(org-export-with-toc (not body-only))
(org-export-with-title (not body-only))
(org-export-with-author (not body-only))
(org-odt-styles-file template-path))
;; Export the current Org buffer to ODT
(org-odt-export-to-odt))))
Once I run the above function, it will prompt me with a few questions: Do I want to preserve line breaks? Yes or no? There are some publications that want hard line breaks, so I have this option available. Do I want to export the body only? Likewise, there are some publications that prefer “blind submissions” without the author’s name present anywhere in the manuscript. The “body only” option works well for this.
Then, the most important part, the function will cycle through my hard-coded templates, and I can select the one I want. That’s it.
Once you have your OpenOffice document (the .odt file), you can open it, make any last-minute tweaks, like adding a word count, etc., and then save it as a Microsoft Word (.docx) file.
If you don’t know how to make your own Open Office template, you can check out my older video on the subject. It’s pretty simple: just do a base export from Org Mode, customize it, and save it as a template (.ott) file.
This month’s Emacs Asia-Pacific (APAC)
virtual meetup is scheduled for Saturday, February
22, 2025 with BigBlueButton and #emacs
on Libera Chat IRC. The timing will be 1400 to 1500 IST.
The meetup might get extended by 30 minutes if there is any talk, this
page will be updated accordingly.
If you would like to give a demo or talk (maximum 20 minutes) on GNU
Emacs or any variant, please contact bhavin192 on Libera Chat with
your talk details:
I have been playing around with local LLMs recently through ollama and decided to create the basis for an Emacs package to focus on interfacing to ollama specifically. My idea here is to implement something very minimal and as light-weight as possible and that could be run out-of-the-box with no configuration (obviously the ollama server just needs to be running). I have a deeper dive into my overall design thoughts and decisions in the github README and there are some simple demos:
A friendly Emacs interface for interacting with Ollama models. This package provides a convenient way to integrate Ollama’s local LLM capabilities directly into your Emacs workflow with little or no configuration required.
The name is just something a little bit fun and it seems to always remind me of the “bathroom buddy” from the film Gremlins (although hopefully this will work better than that seemed to!)
Screenshots / Demos
Note that all the demos are in real time.
Switching to a better model
More conversational
Describing code with different models
Describing code with a more advanced model
The Menu
Summary of my design ethos
Focused Design Philosophy
Dedicated solely to Ollama integration (unlike general-purpose LLM packages)
Intentionally lightweight and minimal setup
Particularly suitable for air-gapped systems
Avoids complex backends and payload configurations
Interface Design Choices
Flexible, customizable menu through defcustom
Easy-to-extend command system via simple alist modifications
Region-based interaction model across all buffers
Buffer Implementation
Simple, editable chat buffer approach
Avoids complex modes or bespoke functionality
Trying to leverage standard Emacs text editing capabilities
User Experience
“AI assistant” style welcome interface
Zero-config startup possible
Built-in status monitoring and model listing
Simple tutorial-style introduction
Technical Simplicity
REST-based Ollama
Quickly switch between small local LLMs
Backwards compatibility with older Emacs versions
Minimal dependencies
Straightforward configuration options
Design ethos expanded / why create this package?
The Ollama Emacs package ecosystem is still emerging. Although there are some great implementations available, they tend to be LLM jack-of-all-trades, catering to various types of LLM integrations, including, of course, the major online offerings.
Recently, I have been experimenting with a local solution using ollama. While using ollama through the terminal interface with readline naturally leans toward Emacs keybindings, there are a few limitations:
Copy and paste do not use Emacs keybindings like readline navigation. This is due to the way key codes work in terminals, meaning that copying and pasting into Emacs would require using the mouse!
Searching through a terminal with something like Emacs isearch can vary depending on the terminal.
Workflow disruption occur when copying and pasting between Emacs and ollama.
There is no easy way to save a session.
It is not using Emacs!
I guess you can see where this is going. The question is: how do I integrate a basic query-response mechanism to ollama into Emacs? This is where existing LLM Emacs packages come in, however, I have always found them to be more geared towards online models with some packages offering experimental implementations of ollama integration. In my case, I often work on an air-gapped system where downloading or transferring packages is not straightforward. In such an environment, my only option for LLM interaction is ollama anyway. Given the limitations mentioned earlier of interacting with ollama through a terminal, why not create a dedicated ollama Emacs package that is very simple to set up, very lightweight and leverages Emacs’s editing capabilities to provide a basic query response interface to ollama?
I have found that setting up ollama within the current crop of LLM Emacs packages can be quite involved. I often struggle with the setup, I get there in the end, but it feels like there’s always a long list of payloads, backends, etc., to configure. But what if I just want to integrate Emacs with ollama? It has a RESTful interface, so could I create a package with minimal setup, allowing users to define a default model in their init file (or select one each time if they prefer)? It could also query the current set of loaded models through the ollama interface and provide a completing-read type of model selection, with potentially no model configuration needed!
Beyond just being lightweight and easy to configure, I also have another idea: a flexible menu system. For a while, I have been using a simple menu-based interface inspired by transient menus. However, I have chosen not to use transient because I want this package to be compatible with older Emacs versions. Additionally, I haven’t found a compelling use case for a complex transient menu and I prefer a simple, opaque top level menu.
To achieve this, I have decided to create a flexible defcustom menu system. Initially, it will be configured for some common actions, but users can easily modify it through the Emacs customization interface by updating a simple alist.
For example, to refactor code through an LLM, a prepended text string of something like “Refactor the following code:” is usually applied. To proofread text, “Proofread the following:” could be prepended to the body of the query. So, why not create a flexible menu where users can easily add their own commands? For instance, if someone wanted a command to uppercase some text (even though Emacs can already do this), they could simply add the following entry to the ollama-buddy-menu-items alist:
(?u. ("Upcase" (lambda () (ollama-buddy--send "convert the following to uppercase:"))))
Then the menu would present a menu item “Upcase” with a “u” selection, upcasing the selected region. You could go nuts with this, and in order to double down on the autogeneration of a menu concept, I have provided a defcustomollama-buddy-menu-columns variable so you can flatten out your auto-generated menu as much as you like!
This is getting rambly, but another key design consideration is how prompts should be handled and in fact how do I go about sending text from within Emacs?. Many implementations rely on a chat buffer as the single focal point, which seems natural to me, so I will follow a similar approach.
I’ve seen different ways of defining a prompt submission mechanism, some using <RET>, others using a dedicated keybinding like C-c <RET>, so, how should I define my prompting mechanism? I have a feeling this could get complicated, so lets use the KISS principle, also, how should text be sent from within Emacs buffers? My solution? simply mark the text and send it, not just from any Emacs buffer, but also within the chat window. It may seem slightly awkward at first (especially in the chat buffer, where you will have to create your prompt and then mark it), but it provides a clear delineation of text and ensures a consistent interface across Emacs. For example, using M-h to mark an element requires minimal effort and greatly simplifies the package implementation. This approach also allows users to use the scratch buffer for sending requests if so desired!
Many current implementations create a chat buffer with modes for local keybindings and other features. I have decided not to do this and instead, I will provide a simple editable buffer (ASCII text only) where all ollama interactions will reside. Users will be able to do anything in that buffer; there will be no bespoke Ollama/LLM functionality involved. It will simply be based on a special buffer and to save a session?, just use save-buffer to write it to a file, Emacs to the rescue again!
Regarding the minimal setup philosophy of this package, I also want to include a fun AI assistant-style experience. Nothing complicated, just a bit of logic to display welcome text, show the current ollama status, and list available models. The idea is that users should be able to jump in immediately. If they know how to install/start ollama, they can install the package without any configuration, run `M-x ollama-buddy-menu`, and open the chat. At that point, the “AI assistant” will display the current ollama status and provide a simple tutorial to help them get started.
The backend?, well I decided simply to use curl to stimulate the ollama RESTful API, so you will need curl to be installed.
I have other thoughts regarding the use of local LLMs versus online AI behemoths. The more I use ollama with Emacs through this package, the more I realize the potential of smaller, local LLMs. This package allows for quick switching between these models while maintaining a decent level of performance on a regular home computer. I could, for instance, load up qwen-coder for code-related queries (I have found the 7B Q4/5 versions to work particularly well) and switch to a more general model for other queries, such as llama or even deepseek-r1.
Phew! That turned into quite a ramble, maybe I should run this text through ollama-buddy for proofreading! :)
AI assistant
A simple text information screen will be presented on the first opening of the chat, or when requested through the menu system:
==================== n_____n ======================================== | o Y o | ==================== ╭──────────────────────────────────────╮
│ Welcome to │
│ OLLAMA BUDDY │
│ Your Friendly AI Assistant │
╰──────────────────────────────────────╯
Hi there!
ollama RUNNING
I'm here to help you with:
- Code refactoring and analysis
- Writing clear commit messages
- Proofreading and text improvements
- And much more!
Quick Start/Tips:
- Try typing a prompt in this buffer
- Select/mark all prompt text (select region) - M-x ollama-buddy-menu
- Select menu item
- Now wait for ollama to do its magic!
- You can switch models anytime with [m] - Use [x] to cancel a running request
- You can send to this chat from any buffer
-------------------- | @ Y @ | --------------------
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!