Irreal: Writing Blogs in HTML

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.

-1:-- Writing Blogs in HTML (Post Irreal)--L0--C0--2025-02-20T17:34:44.000Z

Benjamin Slade: Lambda Calculus and Lisp, part 2 (recursion excursion)

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.…

[McCarthy 1978a:1901]

…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.…

[McCarthy 1978a:1791]

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

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):

1. S  NP VP
2. NP  (Det) N
3. N  (AdjP)* N
4. VP  V
5. VP  V NP (NP)
6. VP  V CP
7. CP  (Comp) S
...
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:

S
NP VP
N VP
N V CP
N V Comp S
N V Comp NP VP
N V Comp N VP
N V Comp N V CP
N V Comp N V Comp S
N V Comp N V Comp NP VP
N V Comp N V Comp N VP
N V Comp N V Comp N V CP
N V Comp N V Comp N V Comp S
N V Comp N V Comp N V Comp S
N V Comp N V Comp N V Comp NP VP
N V Comp N V Comp N V Comp N VP
N V Comp N V Comp N V Comp N V CP
N V Comp N V Comp N V Comp N V Comp S
...
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:

  1. Fib(0) = 0 as base case 1.
  2. Fib(1) = 1 as base case 2.
  3. 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; -*-
(defun fib1 (n a b)
  "Calculate the first `n' Fibonacci numbers, recursively."
  (if (< n 1)
      a
    (cons a
          (fib1 (1- n) b (+ a b)))))
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:

(fib1 10 0 1)  ; (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 . (fib1 2 1 1))  ;; [= (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).

So now we have:

(0 1 . (fib1 1 1 2))  ;; [= (cons 0 (cons 1 (fib1 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:

(0 1 1 . (fib1 0 2 3))  ;; [= (cons 0 (cons 1 (cons 1 (fib1 0 2 3))))]

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:

(0 1 1 . 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:

Debugger entered--Lisp error: (excessive-lisp-nesting 1622)
  cl-print--cons-tail(excessive-lisp-nesting (1601) #<buffer  *temp*>)
  #f(compiled-function (object stream) #<bytecode 0x491d55561699a09>)(#0 #<buffer  *temp*>)
  apply(#f(compiled-function (object stream) #<bytecode 0x491d55561699a09>) (#0 #<buffer  *temp*>))
  #f(compiled-function (&rest args) #<bytecode 0x1c31d892b8046a8b>)()
  #f(compiled-function (cl--cnm object stream) #<bytecode 0x7c361f66f109692>)(#f(compiled-function (&rest args) #<bytecode 0x1c31d892b8046a8b>) #0 #<buffer  *temp*>)
  apply(#f(compiled-function (cl--cnm object stream) #<bytecode 0x7c361f66f109692>) #f(compiled-function (&rest args) #<bytecode 0x1c31d892b8046a8b>) (#0 #<buffer  *temp*>))
  #f(compiled-function (object stream) #<bytecode 0x1f277a9a6dc403fa>)(#0 #<buffer  *temp*>)
  apply(#f(compiled-function (object stream) #<bytecode 0x1f277a9a6dc403fa>) #0 #<buffer  *temp*>)
  cl-print-object(#0 #<buffer  *temp*>)
  cl-prin1(#0 #<buffer  *temp*>)
  backtrace--print(#0 #<buffer  *temp*>)
  cl-print-to-string-with-limit(backtrace--print #0 5000)
  backtrace--print-to-string(#0 nil)
  backtrace-print-to-string(#0)
  debugger--insert-header((error #0 :backtrace-base eval-expression--debug))
  #f(compiled-function () #<bytecode 0x299e53d67d1ac62>)()
  backtrace-print()
  debugger-setup-buffer((error #0 :backtrace-base eval-expression--debug))
  debug(error #0 :backtrace-base eval-expression--debug)
  eval-expression--debug(#0)
  (if (< n 1) a (cons a (fib1 (1- n) b (+ a b))))
  fib1(0 259396630450514843945535792456880074043523940756078363514486570322782139633750401577338505233670220572153381665 419712564636128966418863068957011388899128076671547993021605479585858227224221424221791102364954108601240491394)
  (cons a (fib1 (1- n) b (+ a b)))
  (if (< n 1) a (cons a (fib1 (1- n) b (+ a b))))
  fib1(1 160315934185614122473327276500131314855604135915469629507118909263076087590471022644452597131283888029087109729 259396630450514843945535792456880074043523940756078363514486570322782139633750401577338505233670220572153381665)
  (cons a (fib1 (1- n) b (+ a b)))
  (if (< n 1) a (cons a (fib1 (1- n) b (+ a b))))
  fib1(2 99080696264900721472208515956748759187919804840608734007367661059706052043279378932885908102386332543066271936 160315934185614122473327276500131314855604135915469629507118909263076087590471022644452597131283888029087109729)
....
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…

[Steele 1977:1556]

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; -*-
(defun fib2 (n a b)
  "Calculate the first `n' Fibonacci numbers,
in a loop."
  (let ((result nil))
    (dotimes (c n)
      (setq result (cons a result))
      (setq tempa b)
      (setq b (+ a b))
      (setq a tempa))
    (nreverse result)))

(fib2 10 0 1) ; (0 1 1 2 3 5 8 13 21 34)

(setq bigfib-50000 (fib2 50000 0 1)) ; 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:

(defun-tco foo (...)
  (...)
  (foo (...)))

would be re-written as:

(defun foo (...)
   (flet (foo-thunk (...)
               (...)
               (lambda () (foo-thunk (...))))
     (let ((result (apply foo-thunk (...))))
       (while (functionp result)
         (setq result (funcall result)))
       result)))

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; -*-
(defun fib3 (n)
  "Calculate the first `n' Fibonacci numbers,
recursively, with limited tail-call optimisation
through `cl-labels'?!"
  (cl-labels ((fib* (n a b)
                (if (< n 1)
                    a
                  (cons a
                        (fib* (1- n)
                              b
                              (+ a b))))))
    (fib* n 0 1)))

(setq bigfib3 (fib3 397)) ; 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; -*-
(defun fib4 (n a b accum)
  "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 (cons a accum))
         (accum-lng (length accum)))
    (if (< n accum-lng)
        (nreverse accum)
      (fib4 n b (+ b a) accum))))

(setq bigfib4-529 (fib4 529 0 1 nil)) ; last good
(setq bigfib4-530 (fib4 530 0 1 nil)) ; 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; -*-
(defun fib5 (n)
  "Calculate the first `n' Fibonacci numbers, recursively,
using both cl-labels and the accumulator trick."
  (cl-labels ((fib* (a b accum)
                   (let* ((accum (cons a accum))
                         (accum-lng (length accum)))
                     (if (< n accum-lng)
                         (nreverse accum)
                         (fib* b (+ b a) accum)))))
            (fib* 0 1 nil)))

(setq bigfib5-10000 (fib5 10000)) ; ok
(setq bigfib5-50000 (fib5 50000)) ; 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; -*-
(defun fib6 (n)
  "Return a list of the first `n' Fibonacci numbers,
implemented as stream of (delayed evaluation) conses."
  (cl-labels ((fibonacci-populate (a b)
                (stream-cons a (fibonacci-populate b (+ a b)))))
    (let ((fibonacci-stream
           (fibonacci-populate 0 1))
          (fibs nil))
      (dotimes (c n)
        (setq fibs (cons (stream-pop fibonacci-stream) fibs)))
      (nreverse fibs))))

(setq fib6-10k (fib6 10000)) ; ok
(setq fib6-50k (fib6 50000)) ; little slow, but works
(setq fib6-100k (fib6 100000)) ; 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

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.

Figure 3: a close-up of a section of Michał “phoe” Herda’s hand-illuminated Y Combinator Codex showing part of a Fibonacci defun

Figure 3: a close-up of a section of Michał “phoe” Herda’s hand-illuminated Y Combinator Codex showing part of a Fibonacci defun

And, the Y Combinator ties in with all sorts of other curious things. Paradoxes, types, calligraphy.


  1. 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 ↩︎

  2. “t if and only if p and q or if r then s and not not not not not not t” ↩︎

  3. 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] ↩︎

  4. 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. ↩︎

  5. See further here, for instance, for more about tail calls and tail call optimisation. ↩︎

  6. 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] ↩︎

  7. 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”

    Figure 4: close-up of Duane Bibby’s cover illustration for “The Little Schemer”

    ↩︎

  8. One of my emacs packages, Equake, actually used to use tco.el (until commit #0ab0801 [2020-08-24 Mon]). ↩︎

-1:-- Lambda Calculus and Lisp, part 2 (recursion excursion) (Post Benjamin Slade)--L0--C0--2025-02-20T07:26:00.000Z

Alvaro Ramirez: Keychron K3 Pro: F1-F12 as default macOS keys

20 February 2025 Keychron K3 Pro: F1-F12 as default macOS keys

keyboard.jpg

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.

settings.png

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.

save.png

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.

The diff looks a little something like this:

--- k3_pro_ansi_white(before).json	2025-02-20 09:44:03
+++ k3_pro_ansi_white(after).json	2025-02-20 09:43:59
@@ -5,18 +5,18 @@
   "layers": [
     [
       "KC_ESC",
-      "KC_BRID",
-      "KC_BRIU",
-      "CUSTOM(4)",
-      "CUSTOM(5)",
-      "BL_DEC",
-      "BL_INC",
-      "KC_MPRV",
-      "KC_MPLY",
-      "KC_MNXT",
-      "KC_MUTE",
-      "KC_VOLD",
-      "KC_VOLU",
+      "KC_F1",
+      "KC_F2",
+      "KC_F3",
+      "KC_F4",
+      "KC_F5",
+      "KC_F6",
+      "KC_F7",
+      "KC_F8",
+      "KC_F9",
+      "KC_F10",
+      "KC_F11",
+      "KC_F12",
       "CUSTOM(8)",
       "KC_DEL",
       "BL_STEP",
@@ -103,18 +103,18 @@
     ],
     [
       "KC_TRNS",
-      "KC_F1",
-      "KC_F2",
-      "KC_F3",
-      "KC_F4",
-      "KC_F5",
-      "KC_F6",
-      "KC_F7",
-      "KC_F8",
-      "KC_F9",
-      "KC_F10",
-      "KC_F11",
-      "KC_F12",
+      "KC_BRID",
+      "KC_BRIU",
+      "CUSTOM(4)",
+      "CUSTOM(5)",
+      "BL_DEC",
+      "BL_INC",
+      "KC_MPRV",
+      "KC_MPLY",
+      "KC_MNXT",
+      "KC_MUTE",
+      "KC_VOLD",
+      "KC_VOLU",
       "KC_TRNS",
       "KC_TRNS",
       "BL_TOGG",

Similarly, here's side-by-side look via Emacs ediff:

after-1.png

after-2.png

Load your modified layout

Now that we have k3_pro_ansi_white(after).json with our changes, all that's left is loading through https://usevia.app. You are now done.

load.png

While F1-F12 keys should now be available by default. To access your macOS special keys use the fn key.

Enjoy your F1-F12 default keys!

-1:-- Keychron K3 Pro: F1-F12 as default macOS keys (Post Alvaro Ramirez)--L0--C0--2025-02-20T00:00:00.000Z

Protesilaos Stavrou: Emacs: fontaine version 3.0.0

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.

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:

Fontaine no longer has frame-specific effects

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).

-1:-- Emacs: fontaine version 3.0.0 (Post Protesilaos Stavrou)--L0--C0--2025-02-20T00:00:00.000Z

Matt Maguire: Org Mode Beamer Presentations

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:

-1:-- Org Mode Beamer Presentations (Post Matt Maguire)--L0--C0--2025-02-20T00:00:00.000Z

Irreal: Emoji Picker

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.

-1:-- Emoji Picker (Post Irreal)--L0--C0--2025-02-19T18:27:37.000Z

Norm: XPath server 0.0.2

-1:-- XPath server 0.0.2 (Post Norm)--L0--C0--2025-02-19T17:47:13.000Z

Lars Ingebrigtsen: Inserting Images Automatically in ewp

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…

-1:-- Inserting Images Automatically in ewp (Post Lars Ingebrigtsen)--L0--C0--2025-02-19T01:54:51.000Z

Peter Povinec: Speed Dialing Your Favorite Files

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.

;;
;; Speed Dialing Favorite Files
;;
(defvar /speed-dial-list
  '(("⓵-todo" . "~/todo.org")
    ("⓶-emacs" . "~/para/areas/emacs.org")
    ("⓷-family" . "~/para/areas/family.org")
    ("⓸-house" . "~/para/areas/house.org")
    ("⓹-garden" . "~/para/areas/garden.org")
    ("⓺-42" . "~/para/areas/42.org")
    ("⓻-init" . "~/.emacs.d/init.el")
    ("⓼-O1" . "~/para/projects/proj1.org")
    ("⓽-O2" . "~/para/projects/proj2.org")
    ("⓾-O3" . "~/para/projects/proj3.org"))
  "List of speed-dial entries as (LABEL . FILENAME).")

;; Global keybindings for speed dialing using '<Super>' + digit
(let ((i 1))
  (dolist (entry /speed-dial-list)
    (keymap-global-set (format "s-%d" (mod i 10))
                       `(lambda() (interactive) (find-file-existing ,(cdr entry))))
    (setq i (1+ i))))

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.

speed_dialing_screenshot.png

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!

Discuss this post on Reddit.

-1:-- Speed Dialing Your Favorite Files (Post Peter Povinec)--L0--C0--2025-02-18T18:46:00.000Z

Charles Choi: macOS Native Emoji Picking in Emacs from the Edit Menu

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.

1
2
3
4
5
(easy-menu-add-item global-map '(menu-bar edit)
                    ["Emoji & Symbols"
                     ns-do-show-character-palette
                     :help "Show macOS Character Palette."
                     :visible (eq window-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.

References

-1:-- macOS Native Emoji Picking in Emacs from the Edit Menu (Post Charles Choi)--L0--C0--2025-02-18T18:30:00.000Z

Irreal: Searching For The Thing At Point

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+s Meta+. 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.

-1:-- Searching For The Thing At Point (Post Irreal)--L0--C0--2025-02-18T16:54:28.000Z

Protesilaos Stavrou: Emacs: dired-preview version 0.4.0

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.

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).

Thanks to artelse for discussing this with me in issue 20: https://github.com/protesilaos/dired-preview/issues/20.

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.

-1:-- Emacs: dired-preview version 0.4.0 (Post Protesilaos Stavrou)--L0--C0--2025-02-18T00:00:00.000Z

Lars Ingebrigtsen: HTML, but not too HTML

When writing blog posts, I use ewp, an Emacs package to administrate WordPress. It offers an editing mode based on the revolutionary idea of just writing HTML.

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.

-1:-- HTML, but not too HTML (Post Lars Ingebrigtsen)--L0--C0--2025-02-17T18:23:46.000Z

Sacha Chua: 2025-02-17 Emacs news

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

View org source for this post
-1:-- 2025-02-17 Emacs news (Post Sacha Chua)--L0--C0--2025-02-17T15:54:07.000Z

Irreal: Recurring Checklists

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.

-1:-- Recurring Checklists (Post Irreal)--L0--C0--2025-02-17T15:45:05.000Z

Org Mode requests: [FR] org-read-date-style

[FR] org-read-date-style
-1:-- [FR] org-read-date-style (Post Org Mode requests)--L0--C0--2025-02-17T11:10:21.000Z

Marcin Borkowski: isearch-forward-thing-at-point

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!
-1:-- isearch-forward-thing-at-point (Post Marcin Borkowski)--L0--C0--2025-02-17T05:57:04.000Z

200ok: Shell Smith: A Clojure Library for Building CLI Tools with Elegant Configuration Management

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)
  • Environment variables (matching pattern <UPPERCASE_NAME>_*)
  • Command-line arguments

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.


If you liked this post, please consider supporting our Free and Open Source software work – you can sponsor us on Github and Patreon or star our FLOSS repositories.

-1:-- Shell Smith: A Clojure Library for Building CLI Tools with Elegant Configuration Management (Post 200ok)--L0--C0--2025-02-17T00:00:00.000Z

Norm: XPath server

-1:-- XPath server (Post Norm)--L0--C0--2025-02-16T18:26:10.000Z

Irreal: Sometimes The Current Flows Towards Emacs

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.

-1:-- Sometimes The Current Flows Towards Emacs (Post Irreal)--L0--C0--2025-02-16T16:07:18.000Z

Mark Tomczak: Unstick Terminals in Emacs

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.
-1:-- Unstick Terminals in Emacs (Post Mark Tomczak)--L0--C0--2025-02-16T00:28:27.000Z

Irreal: Using Emacs For Research Notes

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.

-1:-- Using Emacs For Research Notes (Post Irreal)--L0--C0--2025-02-15T17:14:40.000Z

Irreal: A Followup To Why You Can’t Use Emacs More

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.

-1:-- A Followup To Why You Can’t Use Emacs More (Post Irreal)--L0--C0--2025-02-14T16:45:42.000Z

Anand Tamariya: Emacs Font is wider

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
  

Emacs definition is larger than the standard definition. The DTP point is defined as 1⁄72 of an inch. 

Round-off error

Emacs definition:

/* 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.



-1:-- Emacs Font is wider (Post Anand Tamariya)--L0--C0--2025-02-14T03:05:00.000Z

Irreal: Reasons You Can’t Use Emacs More

Over at The Art Of Not Asking Why, JTR has some nice things to say about Irreal but that’s not what I want to talk about. Rather, I like to address the point of his post, which is how difficult it can be to use Emacs as much as he’d like.

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.

-1:-- Reasons You Can’t Use Emacs More (Post Irreal)--L0--C0--2025-02-13T16:56:34.000Z

Emacs Redux: Customizing Color Themes

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:

(custom-set-faces
 '(region ((t (:inherit nil :background "RoyalBlue4"))))
 '(highlight ((t (:inherit region :background "dark olive green"))))
 '(hl-line ((t (:inherit highlight)))))

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:

(custom-theme-set-faces
 'zenburn
 '(region ((t (:inherit nil :background "RoyalBlue4"))))
 '(highlight ((t (:inherit region :background "dark olive green"))))
 '(hl-line ((t (:inherit highlight)))))

Looking good!

Note: custom-set-faces works by calling custom-theme-set-faces for the user theme, a special theme referring to settings made via Customize.

Finally, you can just set a specific face using set-face-attribute like this:

(set-face-attribute 'font-lock-builtin-face nil :weight 'bold)

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-package catppuccin-theme
  :config
  ;; or 'latte, 'macchiato, or 'mocha
  (setq catppuccin-flavor 'macchiato)
  (load-theme 'catppuccin t)
  (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 (:inherit font-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:

(use-package zenburn-theme
  :preface
  (setq my/zenburn-colors-alist
        '((fg . "#DCDCCC") (bg . "#1C1C1C") (cyan . "#93E0E3")))
  :custom-face
  (region ((t (:background ,(alist-get my/zenburn-colors-alist 'cyan)))))
  :config
  (load-theme 'zenburn t))

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!

  1. Remember that Emacs allows you load multiple themes with them stacking one upon another. 

-1:-- Customizing Color Themes (Post Emacs Redux)--L0--C0--2025-02-13T08:23:00.000Z

Protesilaos Stavrou: Aporetic fonts version 1.1.0

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.

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. ]

-1:-- Aporetic fonts version 1.1.0 (Post Protesilaos Stavrou)--L0--C0--2025-02-12T00:00:00.000Z

Irreal: Elisp Abstraction

Over at the Emacs subreddit, AbstProcDo proposes an interesting idea: some Elisp constructs are very intuitive and natural compared to other languages. He uses the example of dolist to process a list versus the Python way.

(dolist (item '(1 2 3 4))
  (print item))

versus

for item in ['a', 'b', 'c', 'd']:
    print(item)

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.

-1:-- Elisp Abstraction (Post Irreal)--L0--C0--2025-02-11T17:18:15.000Z

TAONAW - Emacs and Org Mode: More Emacs would be nice, but...

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.

-1:-- More Emacs would be nice, but... (Post TAONAW - Emacs and Org Mode)--L0--C0--2025-02-11T13:54:42.000Z

Arthur A. Gleckler: LLM Unlock

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-shell README.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:

(require 'use-package)

(use-package chatgpt-shell
  :ensure t
  :bind (("C-c i" . chatgpt-shell-prompt-compose)
         ("C-c I" . chatgpt-shell))
  :init
  (setq chatgpt-shell-openai-key
        (lambda ()
          (with-temp-buffer
            (insert-file-contents
             (substitute-in-file-name "$r/openai.gpg"))
            (buffer-string)))))

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:

(require 'use-package)

(use-package chatgpt-shell
  :ensure t
  :bind (("C-c i" . chatgpt-shell-prompt-compose)
         ("C-c I" . chatgpt-shell))
  :custom
  ((chatgpt-shell-openai-key
    (lambda () (auth-source-pass-get 'secret "api-keys/openai.com")))))

I recommend it highly.

-1:-- LLM Unlock (Post Arthur A. Gleckler)--L0--C0--2025-02-11T12:00:00.000Z

Protesilaos Stavrou: Emacs: I am in the process of splitting Denote into many packages

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:

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.

For the time being, the technical discussion is done in issue 543: https://github.com/protesilaos/denote/issues/543. The code which will eventually be merged into the main branch resides in the reorganise-denote branch: https://github.com/protesilaos/denote/tree/reorganise-denote.

For the longer-term benefit of the project

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.

-1:-- Emacs: I am in the process of splitting Denote into many packages (Post Protesilaos Stavrou)--L0--C0--2025-02-11T00:00:00.000Z

Sacha Chua: 2025-02-10 Emacs news

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

View org source for this post
-1:-- 2025-02-10 Emacs news (Post Sacha Chua)--L0--C0--2025-02-10T18:51:27.000Z

Vineet Naik: Recurring checklists using org mode in emacs

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:

(use-package org-contrib
  :ensure t
  :config
  (require 'org-checklist))

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:

  1. all checkboxes get unchecked,
  2. the time when the state was changed to DONE gets recorded,
  3. 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.

-1:-- Recurring checklists using org mode in emacs (Post Vineet Naik)--L0--C0--2025-02-10T18:30:00.000Z

Marcin Borkowski: Running one own's commands

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.
-1:-- Running one own's commands (Post Marcin Borkowski)--L0--C0--2025-02-10T17:28:29.000Z

Irreal: Projectile Caching Improvements

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.

-1:-- Projectile Caching Improvements (Post Irreal)--L0--C0--2025-02-10T15:46:16.000Z

Eric MacAdie: 2024-02 Austin Emacs Meetup

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
-1:-- 2024-02 Austin Emacs Meetup (Post Eric MacAdie)--L0--C0--2025-02-10T00:29:10.000Z

Tory Anderson: snippets that defy orgmode tangling

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.
-1:-- snippets that defy orgmode tangling (Post Tory Anderson)--L0--C0--2025-02-09T00:00:00.000Z

Chris Maiorana: From Emacs To Microsoft Word (And Beyond, Really)

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.

Packages used

To fully replicate the functionality in my video on this topic you will need the vertico and orderless completion packages for minibuffer completion magic.

Custom Export Function

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.

(defun csm/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.

-1:-- From Emacs To Microsoft Word (And Beyond, Really) (Post Chris Maiorana)--L0--C0--2025-02-08T13:10:58.000Z

Emacs APAC: Announcing Emacs Asia-Pacific (APAC) virtual meetup, Saturday, February 22, 2025

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:

-1:-- Announcing Emacs Asia-Pacific (APAC) virtual meetup, Saturday, February 22, 2025 (Post Emacs APAC)--L0--C0--2025-02-08T00:01:04.000Z

James Dyer: Ollama Buddy - Local LLM Integration for Emacs

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:

https://github.com/captainflasmr/ollama-buddy


Overview

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 defcustom ollama-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 @ | --------------------

Features

  • Interactive menu-driven interface
  • Dedicated chat buffer with streaming responses
  • Easy model switching
  • Quick actions for common tasks:
    • Code refactoring
    • Git commit message generation
    • Code description
    • Text proofreading
    • Text summarization
  • Cute ASCII art separators for chat messages
  • Region-based interaction with any buffer

Whats New

<2025-02-07 Fri>

Added query finished message.

<2025-02-06 Thu>

  • Initial release
  • Basic chat functionality
  • Menu-driven interface
  • Region-based interactions
  • Model switching support

Prerequisites

  • Ollama installed and running locally
  • Emacs 27.1 or later
  • curl command-line tool

Installation

Manual Installation

Clone this repository:

git clone https://github.com/captainflasmr/ollama-buddy.git

Add to your init.el:

(add-to-list 'load-path "path/to/ollama-buddy")
(require 'ollama-buddy)

MELPA (Coming Soon)

(use-package ollama-buddy
  :ensure t
  :bind ("C-c l" . ollama-buddy-menu))

Usage

  1. Start your Ollama server locally
  2. Use M-x ollama-buddy-menu or the default keybinding C-c l to open the menu
  3. Select your preferred model using the [m] option
  4. Select text in any buffer
  5. Choose an action from the menu:
Key Action Description
o Open chat buffer Opens the main chat interface
m Swap model Switch between available Ollama models
h Help assistant Display help message
l Send region Send selected text directly to model
r Refactor code Get code refactoring suggestions
g Git commit message Generate commit message for changes
d Describe code Get code explanation
p Proofread text Check text for improvements
z Make concise Reduce wordiness while preserving meaning
c Custom Prompt Enter bespoke prompt through the minibuffer
x Kill request Cancel current Ollama request
q Quit Exit the menu

Key Bindings

Key Description
C-c l Open ollama-buddy menu

Customization

Custom variable Description
ollama-buddy-menu-columns Number of columns to display in the Ollama Buddy menu.
ollama-buddy-menu-items Menu items definition for Ollama Buddy.
ollama-buddy-current-model Default Ollama model to use.
ollama-buddy-separator-1 Separator used for Ollama LLM output, variant 1.
ollama-buddy-separator-2 Separator used for Ollama LLM output, variant 2.

Customize the package to the default startup using:

(setq ollama-buddy-current-model "qwen-4q:latest")

;; Change number of menu columns
(setq ollama-buddy-menu-columns 4)

;; Customize separators
(setq ollama-buddy-separator-1 "Your custom separator here")
(setq ollama-buddy-separator-2 "Another custom separator")

Available customization options:

Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Commit your changes
  4. Open a pull request

License

MIT License

Acknowledgments

  • Ollama for making local LLM inference accessible
  • Emacs community for continuous inspiration

Issues

Report issues on the GitHub Issues page

Alternative LLM based packages

To the best of my knowledge, there are currently a few Emacs packages related to Ollama, though the ecosystem is still relatively young:

  1. llm.el (by Jacob Hacker)

    • A more general LLM interface package that supports Ollama as one of its backends
    • GitHub: https://github.com/ahyatt/llm
    • Provides a more abstracted approach to interacting with language models
    • Supports multiple backends including Ollama, OpenAI, and others
  2. gptel (by Karthik Chikmagalur)

    • While primarily designed for ChatGPT and other online services, it has experimental Ollama support
    • GitHub: https://github.com/karthink/gptel
    • Offers a more integrated chat buffer experience
    • Has some basic Ollama integration, though it’s not the primary focus
  3. chatgpt-shell (by xenodium)

  4. ellama

    • TODO

Alternative package comparison

Let’s compare ollama-buddy to the existing solutions:

  1. llm.el

    • Pros:

      • Provides a generic LLM interface
      • Supports multiple backends
      • More abstracted and potentially more extensible
    • Cons:

      • Less Ollama-specific
      • More complex configuration
      • Might have overhead from supporting multiple backends

    ollama-buddy is more:

    • Directly focused on Ollama
    • Lightweight and Ollama-native
    • Provides a more interactive, menu-driven approach
    • Simpler to set up for Ollama specifically
  2. gptel

    • Pros:

      • Sophisticated chat buffer interface
      • Active development
      • Good overall UX
    • Cons:

      • Primarily designed for online services
      • Ollama support is experimental
      • More complex architecture

    ollama-buddy differentiates by:

    • Being purpose-built for Ollama
    • Offering a more flexible, function-oriented approach
    • Providing a quick, lightweight interaction model
    • Having a minimal, focused design
  3. chatgpt-shell

    • Pros:

      • Mature shell-based interaction model
      • Rich interaction capabilities
    • Cons:

      • Not truly Ollama-native
      • Primarily focused on online services
      • More complex setup

    ollama-buddy stands out by:

    • Being specifically designed for Ollama
    • Offering a simpler, more direct interaction model
    • Providing a quick menu-based interface
    • Having minimal dependencies
  4. ellama

    • TODO
-1:-- Ollama Buddy - Local LLM Integration for Emacs (Post James Dyer)--L0--C0--2025-02-07T09:40:00.000Z

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