Irreal: Cybernetic Productivity and Emacs

Victor Dorneanu has an interesting post on Cybernetic Productivity and Emacs. Like Dorneanu, I was unfamiliar with the term but it has, apparently, been around since the early 2000s. It’s a strategy for dealing with your workload in the digital age. The TL;DR is summed up by its four principals:

  1. Automate and Speedup shallow tasks.
  2. Keep the information you need at your fingertips.
  3. Remove friction from communication.
  4. Simplify the extraction of actionable information from raw data

Dorneanu notes that he learned all this from Cal Newport, whose work he follows fairly closely. Newport, though, says the cybernetic productivity approach doesn’t work because, if I understand his argument, we’re too overwhelmed with work to have time to follow the principals.

Dorneanu disagrees and offers his use of Emacs as a counterargument. His post details how he used the four principals—except, maybe, the communication part—to write and publish his current post. The important part is that he did it all within the unified environment of Emacs. He lets Emacs handle the routine parts of publishing a post and uses Elfeed to easily retrieve the necessary required information.

Finally, he notes that the recent integration of Emacs with the various AI/ML services provides a very nice and efficient way of sifting through data looking for the wheat among the chaff. I’d judge that more promise than fact right now but I do take his point.

-1:-- Cybernetic Productivity and Emacs (Post jcs)--L0--C0--September 28, 2023 04:02 PM

Jeremy Friesen: Emacs Function to Calculate an Errant's Movement Dice

Summary: In this post, I share Errant’s Chase procedure. I then write out the mathematics of calculating the movement dice. And from there provide an Emacs function for calculating movement dice. The chase subsystem is too interesting to ignore the movement dice. The underlying question is “should we care about the movement dice in combat?” I think so, because those dice add to the unpredictability of the game.


, I released Errant Encumberance, Speed, and Movement Dice Calculator. I also updated my script to reflect the same logic.

I was reading Errant 📖 and got curious about the mathematic behind the fiddly Movement Dice. A character’s Movement Dice are derived from their physique, skill, and number of item Slots carried.

The Movement Dice is a proud and odd little rule that impacts movement in combat as well as chases. My conjecture is that the chase mechanic encourages you to throw down your backpack and run; multiple dice are advantageous for escaping.

Of important note, the only time a character’s movement dice matter is during a chase and during combat. Exploration is not based on the movement dice.

Errant’s Chase Procedure

First let’s look at the Chases section of Errant :

In the case where they are being chased through a dungeon or similarly defined area, or for a short pursuit, the hunt can play out using standard initiative turn rules. However, for longer pursuits, and ones that may take place in broadly abstracted spaces like the wilderness or in cities, the following chase procedure can be used.

In a chase, generally, the participants can be tracked in terms of what side they’re on (i.e. pursuers and fugitives), but some chases may involve multiple parties or characters that need to be tracked separately.

Each initiative turn, the character with the lowest spd or mv on each side makes a movement roll. If the characters are on mounts or vehicles, use the spd of the mount or vehicle.

If the fugitives roll two 4’s, then they escape and the chase ends. If the pursuers roll two 4’s, they have caught the fugitives. In case of a tie, both sides make a movement roll as a tiebreaker.

If either side rolls doubles that are not 4’s, then characters on that side may make melee attack roll, perform a sorcery or miracle, or any other actions they wish.

If any of the results on both side’s movement dice match each other, characters may make ranged attack rolls against the other side. So if the fugitives rolled a 3 and a 4, and the pursuers rolled a 2 and a 3, the 3’s match, and so characters on each side may make ranged attack rolls against characters on the other side.

Dropping items during a chase is a free action. Dropping something the pursuers are interested in (food, money, etc.) may force a morale roll to see if they continue the chase.

Characters on either side may choose to sprint, rolling double their normal amount of movement dice, but they must make a phys check with a dv equal to their encumbrance to do so. If they fail, they may not make a movement roll this initiative turn.

Characters on either side can choose to split off from their group; they will make movement rolls separately.

At the end of the initiative turn, if the chase has not yet ended, the side that rolled the lowest on their movement roll rolls a d10 for a chase development that affects them.

Ava Islam, Errant

We have some high stakes dice rolls where having more movement dice increases your chances of ending the chase or taking meaningful actions during the chase. There’s a press your luck element in choosing to sprint (and garner potentially more dice). There’s also the option to split from the slower characters.

All modeling the absolute chaos of a good old fashioned rout.

On to the Mathematic of It All

With that in mind, we dive into the fiddly bits. We use two attributes (e.g. physique and skill) plus the more fluid number of carried Slots.

Errant encourages you to calculate your Spd while carrying your backpack and when not carrying your backpack. The book wants to make it clear just how much that backpack is costing you during a chase.

A character’s ENC (or Encumbrance) is:

  • When slots > physique, then 4 + slots - physique
  • Else (4 × slots) ÷ physique rounded down.

A character’s SPD (or Speed) is skill - encumberance. And their movement die is speed + 4 rounded down.

Emacs Lisp Function

The following function prompts for information and provides movement dice while carrying your backpack and when not carrying your backpack. I suppose it would also be useful to see the effect of discarding things carried in your hands. Because when you’re running maybe discarding that greatsword is a better plan than carrying the dead weight.

(defun jf/gaming/errant/movement-dice (prefix)
  "Calculate an Errant's movement dice."
  (interactive "P")
  (if prefix
      (shell-command (concat "open ~/git/takeonrules.source/static/errant/index.html"))
    (let* ((range '("4" "5" "6" "7" "8" "9" "10" "11" "12" "13" "14" "15" "16" "17" "18" "19" "20"))
           (slot-range '("0" "0.25" "0.5" "0.75" "1" "1.25" "1.5" "1.75" "2" "2.25" "2.5" "2.75"
                         "3" "3.25" "3.5" "3.75" "4" "4.25" "4.5" "4.75" "5" "5.25" "5.5" "5.75"
                         "6" "6.25" "6.5" "6.75" "7" "7.25" "7.5" "7.75" "8" "8.25" "8.5" "8.75"
                         "9" "9.25" "9.5" "9.75" "10" "10.25" "10.5" "10.75" "11" "11.25" "11.5" "11.75"
                         "12" "12.25" "12.5" "12.75" "13" "13.25" "13.5" "13.75" "14"
                         "14.25" "14.5" "14.75" "15" "15.25" "15.5" "15.75" "16" "16.25" "16.5" "16.75" "17"
                         "17.25" "17.5" "17.75" "18" "18.25" "18.5" "18.75" "19" "19.25" "19.5" "19.75"
                         "20" "20.25" "20.5" "20.75" "21" "21.25" "21.5" "21.75" "22"
                         "22.25" "22.5" "22.75" "23" "23.25" "23.5" "23.75" "24"))
	   (phys (string-to-number (completing-read "Physique: " range nil t)))
	   (skil (string-to-number (completing-read "Skill: " range nil t)))
	   (slots-hand (string-to-number (completing-read "Slots in hand: " (subseq slot-range 0 9) nil t)))
	   (slots-handy (string-to-number (completing-read "Slots in handy: " (subseq slot-range 0 17) nil t)))
	   (slots-worn (string-to-number (completing-read "Slots in worn: " slot-range nil t)))
	   (slots-pack (string-to-number (completing-read "Slots in pack: " slot-range nil t)))
           (text (format "Errant Movement\n- Physique: %s · Skill: %s\n- Slots Hand: %s · Handy: %s · Worn: %s · Pack: %s"
			 phys skil slots-hand slots-handy slots-worn slots-pack)))
      (dolist (label-slots (list (cons "in hand, handy, worn, pack"
				       (+ slots-hand slots-handy slots-worn slots-pack))
				 (cons "in hand, handy, worn"
				       (+ slots-hand slots-handy slots-worn))
				 (cons "handy, worn"
				       (+ slots-handy slots-worn))
				 (cons "worn" slots-worn)
				 (cons "naked and free" 0)))
        (let* ((slots (cdr label-slots))
               (label (car label-slots))
               (enc (if (>= phys slots)
		        (floor (* 4 slots) phys)
		      (+ 4 (- (floor slots) phys))))
               (spd (if (>= skil enc) (- skil enc) 0))
               (md (floor spd 4))
               (md-text (if (= 0 md) "0" (format "%sd4" md))))
          (setq-local text (format "%s\n- %s\n  ENC: %s · SPD: %s· MD: %s"
				   text label enc spd md-text))))
      (kill-new text)
      (message text))))


I like the chase procedure; and the fiddly bits make the procedure possible. Also given that combat might lead to chase, having a clear understanding of a character’s movement dice helps folks think about the viability of fleeing.

If I don’t want to worry about it, a quick and dirty method could be:

  • Pick the higher of physique or skill and subtract the slots.
  • Select the lowest sided-die from the dice chain that has sides greater than the above amount.
  • Roll that die and multiply by 10 feet.

Or just move 10 feet per point of difference.

-1:-- Emacs Function to Calculate an Errant's Movement Dice (Post Jeremy Friesen ( 27, 2023 09:43 PM

Irreal: Using Gnuplot With Org Mode Tables

James Dyer has an interesting post on using Gnuplot to plot data from Org tables. Gnuplot is my go to utility for plotting data but its syntax is, to put it mildly, convoluted and since I don’t use it all that often, it’s always a struggle to get a working graph. Dyer’s method seems less complicated—at least for simple cases—so it’s worth taking note of how he did it.

This post is mainly a note to myself but other occasional Gnuplot users who want to produce graphs from the data in an Org table may find it useful too. The TL;DR is that you specify a #+PLOT: line for the table that lists the parameters for the graph you wish to plot.

As Dyer illustrates, you can also easily produce a bar plot within the table itself with the orgtbl-ascii-draw command. It’s use is pretty much self explanatory from Dyer’s post but it’s also documented at the #+PLOT link.

Dyer’s post is short but has a surprisingly useful kernel. It’s definitely worth taking a look at it if you ever need to plot data contained in an Org table.

-1:-- Using Gnuplot With Org Mode Tables (Post jcs)--L0--C0--September 27, 2023 04:29 PM

James Dyer: Plotting Other Org Tables

I’m currently in the process of learning how to create graphical plots from org tables using gnuplot. I’ve noticed that it’s generally more straightforward to extract x-axis data from an org table column with incrementing numbers, as opposed to relying on gnuplot to potentially sort out data from an existing column (which may not be plot-friendly)

Generally my existing org tables do not have such an incrementing integer column, so how do I quickly create and populate such a column?, well actually it is super easy, barely an inconvenience!

For example take the following table that represents a little flutter on the horses, how would I go about plotting the Total column and therefore clearly visualise the downward sloping line 😀

| Date   | Horse          | Stk |   Ret |  Prof |  Total |
|        |                |     |       |     0 |  306.9 |
| 27 May | aggagio        |  15 |  7.68 | -7.32 | 299.58 |
|        | red derek      |  15 |     0 |   -15 | 284.58 |
| 03 Jun | dear my friend |  10 |     0 |   -10 | 274.58 |
|        | the foxes      |  10 |    32 |    22 | 296.58 |
|        | sprewell       |  10 |    16 |     6 | 302.58 |
|        | dancing poet   |  15 | 16.73 |  1.73 | 304.31 |

Firstly simply M-S-<right> to insert a table column:

|   | Date   | Horse          | Stk |   Ret |  Prof |  Total |
|   |        |                |     |       |     0 |  306.9 |
|   | 27 May | aggagio        |  15 |  7.68 | -7.32 | 299.58 |
|   |        | red derek      |  15 |     0 |   -15 | 284.58 |
|   | 03 Jun | dear my friend |  10 |     0 |   -10 | 274.58 |
|   |        | the foxes      |  10 |    32 |    22 | 296.58 |
|   |        | sprewell       |  10 |    16 |     6 | 302.58 |
|   |        | dancing poet   |  15 | 16.73 |  1.73 | 304.31 |

Now move the cursor to the starting point and put in an initial integer:

|   | Date   | Horse          | Stk |   Ret |  Prof |  Total |
| 1 |        |                |     |       |     0 |  306.9 |
|   | 27 May | aggagio        |  15 |  7.68 | -7.32 | 299.58 |
|   |        | red derek      |  15 |     0 |   -15 | 284.58 |
|   | 03 Jun | dear my friend |  10 |     0 |   -10 | 274.58 |
|   |        | the foxes      |  10 |    32 |    22 | 296.58 |
|   |        | sprewell       |  10 |    16 |     6 | 302.58 |
|   |        | dancing poet   |  15 | 16.73 |  1.73 | 304.31 |

Now just simply S-Enter (org-table-copy-down) which will fill an incremented number downwards as far as you want:

|   | Date   | Horse          | Stk |   Ret |  Prof |  Total |
| 1 |        |                |     |       |     0 |  306.9 |
| 2 | 27 May | aggagio        |  15 |  7.68 | -7.32 | 299.58 |
| 3 |        | red derek      |  15 |     0 |   -15 | 284.58 |
| 4 | 03 Jun | dear my friend |  10 |     0 |   -10 | 274.58 |
| 5 |        | the foxes      |  10 |    32 |    22 | 296.58 |
| 6 |        | sprewell       |  10 |    16 |     6 | 302.58 |
| 7 |        | dancing poet   |  15 | 16.73 |  1.73 | 304.31 |

Now I can use a #+PLOT header to reference the first column for the x axis:

#+PLOT: title:"Betting" ind:1 deps:(7) type:2d with:lines set:"yrange [250:350]"
|   | Date   | Horse          | Stk |   Ret |  Prof |  Total |
| 1 |        |                |     |       |     0 |  306.9 |
| 2 | 27 May | aggagio        |  15 |  7.68 | -7.32 | 299.58 |
| 3 |        | red derek      |  15 |     0 |   -15 | 284.58 |
| 4 | 03 Jun | dear my friend |  10 |     0 |   -10 | 274.58 |
| 5 |        | the foxes      |  10 |    32 |    22 | 296.58 |
| 6 |        | sprewell       |  10 |    16 |     6 | 302.58 |
| 7 |        | dancing poet   |  15 | 16.73 |  1.73 | 304.31 |

which will produce the following plot:

Not such a downward spiral as I first thought!

-1:-- Plotting Other Org Tables (Post James Dyer)--L0--C0--September 26, 2023 08:30 PM

Sacha Chua: #EmacsConf backstage: adding a talk to the wiki

The EmacsConf 2023 call for participation has finished, hooray! We've sent out acceptances and added talks to the wiki. We experimented with early acceptances this year, which was really nice because it gives people quick feedback and allows people to get started on their videos early. That meant that I needed to be able to easily add talks to the wiki throughout the call for participation. We use templates and an Ikiwiki directive to make it easier to consistently format talk pages. This post covers adding a talk to the wiki, and these functions are in the emacsconf-el repository.


Amin Bandali picked Ikiwiki for the wiki for I think it's because Ikiwiki works with plain text in a Git repository, which fits nicely with our workflow. I can use Emacs Lisp to generate files that are included in other files, and I can also use Emacs Lisp to generate the starting point for files that may be manually edited later on.

We organize conference pages by year. Under the directory for this year (2023/), we have:

talk descriptions that can be manually edited
the description for the talk. Includes ../info/, ../info/, and ../info/
automatically-generated files that are included before and after the talk description, and navigation links between talks
navigation links
navigation links
navigation links
list of talks
public planning notebook

The filenames and URLs for each talk are based on the ID for a talk. I store that in the SLUG property to make it easy to differentiate from CUSTOM_ID, since CUSTOM_ID is useful for lots of other things in Org Mode. I usually assign the slugs when I add the talks to our private file, although sometimes people suggest a specific ID.


Publishing to wiki pages and replying to e-mails are easier if I can substitute text into readable templates. There are a number of templating functions for Emacs Lisp, like the built-in tempo.el or s-lex-format from s.el. I ended up writing something that works with plists instead, since we use property lists (plists) all over the emacsconf-el library.

emacsconf-replace-plist-in-string: Replace ${keyword} from ATTRS in STRING.
(defun emacsconf-replace-plist-in-string (attrs string)
  "Replace ${keyword} from ATTRS in STRING."
  (let ((a attrs) name val)
    (while a
      (setq name (pop a) val (pop a))
      (when (stringp val)
        (setq string
              (replace-regexp-in-string (regexp-quote (concat "${" (substring (symbol-name name) 1) "}"))
                                        (or val "")
                                        string t t))))

It is also handy to be able to add text around another string only if the string is non-nil, and to provide a different string to use if it isn't specified..

emacsconf-surround: Concat BEFORE, TEXT, and AFTER if TEXT is specified, or return ALTERNATIVE.
(defun emacsconf-surround (before text after &optional alternative)
  "Concat BEFORE, TEXT, and AFTER if TEXT is specified, or return ALTERNATIVE."
  (if (and text (not (string= text "")))
      (concat (or before "") text (or after ""))

Getting the talk information

To get the data to fill in the template, we can run a bunch of different functions. This lets us add or remove functions when we need to. We pass the previous result to the next function in order to accumulate properties or modify them. The result is a property list for the current talk.

emacsconf-get-talk-info-for-subtree: Run ‘emacsconf-talk-info-functions’ to extract the info for this entry.
(defun emacsconf-get-talk-info-for-subtree ()
  "Run `emacsconf-talk-info-functions' to extract the info for this entry."
  (seq-reduce (lambda (prev val) (save-excursion (save-restriction (funcall val prev))))

emacsconf-talk-info-functions: Functions to collect information.
(defvar emacsconf-talk-info-functions
  "Functions to collect information.")

Getting the talk abstract

I add a *** Talk abstract subheading to the talk and put the rest of the submission under a *** Talk details subheading. This allows me to extract the text of the Talk abstract heading (or whatever matches emacsconf-abstract-heading-regexp, which is set to "abstract".).

emacsconf-get-subtree-entry: Return the text for the subtree matching HEADING-REGEXP.
(defun emacsconf-get-subtree-entry (heading-regexp)
  "Return the text for the subtree matching HEADING-REGEXP."
   (delq nil
          (lambda ()
            (when (string-match heading-regexp (org-entry-get (point) "ITEM"))
          nil 'tree))))

emacsconf-get-talk-abstract-from-subtree: Add the abstract from a subheading.
(defun emacsconf-get-talk-abstract-from-subtree (o)
  "Add the abstract from a subheading.
The subheading should match `emacsconf-abstract-heading-regexp'."
  (plist-put o :abstract (substring-no-properties (or (emacsconf-get-subtree-entry "abstract") ""))))

I include emacsconf-get-talk-abstract-from-subtree in emacsconf-talk-info-functions so that it retrieves that information when I call emacsconf-get-talk-info-for-subtree.

Publishing the talk page

We add accepted talks to the wiki so that people can see what kinds of talks will be at EmacsConf 2023. To add the talk to the wiki, I use emacsconf-publish-add-talk. It'll create the talk page without overwriting anything that's already there and redo the automatically-generated info pages that provide navigation, status, and so on.

emacsconf-publish-add-talk: Add the current talk to the wiki.
(defun emacsconf-publish-add-talk ()
  "Add the current talk to the wiki."
  (emacsconf-publish-talk-page (emacsconf-get-talk-info-for-subtree))
  (magit-status-setup-buffer emacsconf-directory))

The talk page includes the description and other resources.

emacsconf-publish-talk-page: Draft the talk page for O unless the page already exists or FORCE is non-nil.
(defun emacsconf-publish-talk-page (o &optional force)
  "Draft the talk page for O unless the page already exists or FORCE is non-nil."
  (interactive (list (emacsconf-get-talk-info-for-subtree)
                     (> (prefix-numeric-value current-prefix-arg) 1)))
  (let ((filename (expand-file-name (format "" (plist-get o :slug))
                                    (expand-file-name "talks" (expand-file-name emacsconf-year emacsconf-directory)))))
    (unless (file-directory-p (expand-file-name "talks" (expand-file-name emacsconf-year emacsconf-directory)))
      (mkdir (expand-file-name "talks" (expand-file-name emacsconf-year emacsconf-directory))))
    (when (or force (null (file-exists-p filename)))
      (with-temp-file filename
           (append o (list
                      :speaker-info (emacsconf-publish-format-speaker-info o)
                      :meta "!meta"
                      :categories (if (plist-get o :categories)
                                      (mapconcat (lambda (o) (format "[[!taglink %s]]" o))
                                                 (plist-get o :categories)
                                                 " ")
          "[[${meta} title=\"${title}\"]]
[[${meta} copyright=\"Copyright &copy; ${year} ${speakers}\"]]
[[!inline pages=\"internal(${year}/info/${slug}-nav)\" raw=\"yes\"]]

<!-- Initially generated with emacsconf-publish-talk-page and then left alone for manual editing -->
<!-- You can manually edit this file to update the abstract, add links, etc. --->\n

# ${title}

[[!inline pages=\"internal(${year}/info/${slug}-before)\" raw=\"yes\"]]


[[!inline pages=\"internal(${year}/info/${slug}-after)\" raw=\"yes\"]]

[[!inline pages=\"internal(${year}/info/${slug}-nav)\" raw=\"yes\"]]


Ikiwiki uses Markdown, so we can take advantage of Org's Markdown export.

emacsconf-convert-talk-abstract-to-markdown: Set the :abstract-md property to a Markdown version of the abstract.
(defun emacsconf-convert-talk-abstract-to-markdown (o)
  "Set the :abstract-md property to a Markdown version of the abstract."
  (plist-put o :abstract-md (org-export-string-as (or (plist-get o :abstract) "") 'md t)))

Publishing the list of talks

The list of talks at is grouped by track.

2023-09-26_14-00-19.png Figure 1: List of talks

emacsconf-publish-schedule: Generate the schedule or program.
(defun emacsconf-publish-schedule (&optional info)
  "Generate the schedule or program."
  (setq info (or info (emacsconf-publish-prepare-for-display info)))
  (with-temp-file (expand-file-name ""
                                    (expand-file-name emacsconf-year emacsconf-directory))
     (if (member emacsconf-publishing-phase '(cfp program))
         (let ((sorted (emacsconf-publish-prepare-for-display (or info (emacsconf-get-talk-info)))))
            (lambda (track)
               "Jump to: "
               ;; links to other tracks
               (string-join (seq-keep (lambda (track-link)
                                        (unless (string= (plist-get track-link :id)
                                                         (plist-get track :id))
                                          (format "<a href=\"#%s\">%s</a>"
                                                  (plist-get track-link :id)
                                                  (plist-get track-link :name))))
                            " | ")
               (let ((track-talks (seq-filter (lambda (o) (string= (plist-get o :track)
                                                                   (plist-get track :name)))
                  "<h1 id=\"%s\" class=\"sched-track %s\">%s (%d talks)</h1>\n%s"
                  (plist-get track :id)
                  (plist-get track :name)
                  (plist-get track :name)
                  (length track-talks)
                  (emacsconf-publish-format-main-schedule track-talks)))))
            emacsconf-tracks "\n\n"))
       (emacsconf-publish-format-interleaved-schedule info))))
  (when (member emacsconf-publishing-phase '(cfp program))
    (with-temp-file (expand-file-name
                     (expand-file-name emacsconf-year emacsconf-directory))
       "[[!sidebar content=\"\"]]\n\n" 
       "This is a *DRAFT* schedule.\n"
       (let ((emacsconf-publishing-phase 'schedule))
         (emacsconf-publish-format-interleaved-schedule info))))))

The emacsconf-format-main-schedule function displays the information for the talks in each track. It's pretty straightforward, but I put it in a function because I call it from a number of places.

emacsconf-publish-format-main-schedule: Include the schedule information for INFO.
(defun emacsconf-publish-format-main-schedule (info)
  "Include the schedule information for INFO."
  (mapconcat #'emacsconf-publish-sched-directive info "\n"))

We define an Ikiwiki sched directive that conditionally displays things depending on what we specify, so it's easy to add more information during the schedule or conference phase. This is templates/ in the EmacsConf wiki git repository:

<div data-start="<TMPL_VAR startutc>" data-end="<TMPL_VAR endutc>" class="sched-entry <TMPL_IF track>track-<TMPL_VAR track></TMPL_IF track>">
<div class="sched-meta">
<TMPL_IF start>
<span class="sched-time"><span class="sched-start"><TMPL_VAR start></span>
<TMPL_IF end> - <span class="sched-end"><TMPL_VAR end></span></TMPL_IF end>
</span></TMPL_IF start>
<TMPL_IF track> <span class="sched-track <TMPL_VAR track>"><TMPL_IF watch><a href="<TMPL_VAR watch>"></TMPL_IF><TMPL_VAR track><TMPL_IF watch></a></TMPL_IF></span></TMPL_IF track>
<TMPL_IF pad> <span class="sched-pad"><a href="<TMPL_VAR pad>">Etherpad</a></TMPL_IF pad>
<TMPL_IF q-and-a> <span class="sched-q-and-a">Q&amp;A: <TMPL_VAR q-and-a></span> </TMPL_IF q-and-a>
<div class="sched-title"><a href="<TMPL_VAR url>"><TMPL_VAR title></a></div>
<div class="sched-speakers"><TMPL_VAR speakers> <TMPL_IF note>- <TMPL_VAR note></TMPL_IF note></div>
<TMPL_IF resources>
<ul class="resources">
<TMPL_VAR resources>
</TMPL_IF resources>
<TMPL_IF time><span class="sched-duration><TMPL_VAR time></span> minutes</TMPL_IF time>
<TMPL_IF slug> <span class="sched-slug">id:<TMPL_VAR slug></span></TMPL_IF slug>

This Emacs Lisp function converts a talk into that directive.

emacsconf-publish-sched-directive: Format the schedule directive with info for O.
(defun emacsconf-publish-sched-directive (o)
  "Format the schedule directive with info for O."
  (format "[[!template id=sched%s]]"
          (let ((result "")
                (attrs (append
                        (pcase emacsconf-publishing-phase
                            :time (plist-get o :time)))
                          ((or 'schedule 'conference)
                            :status (pcase (plist-get o :status)
                                      ("CAPTIONED" "captioned")
                                      ("PREREC_RECEIVED" "received")
                                      ("DONE" "done")
                                      ("STARTED" "now playing")
                                      (_ nil))
                            :time (plist-get o :time)
                            :q-and-a (plist-get o :qa-link) 
                            :pad (and emacsconf-publish-include-pads (plist-get o :pad-url))
                            :startutc (format-time-string "%FT%T%z" (plist-get o :start-time) t)
                            :endutc (format-time-string "%FT%T%z" (plist-get o :end-time) t)
                            :start (format-time-string "%-l:%M" (plist-get o :start-time) emacsconf-timezone)
                            :end (format-time-string "%-l:%M" (plist-get o :end-time) emacsconf-timezone)))
                            :pad nil
                            :channel nil
                            :resources (mapconcat (lambda (s) (concat "<li>" s "</li>"))
                                                   (append o
                                                           (list :base-url (format "%s%s/" emacsconf-media-base-url emacsconf-year)))
                                                   (append emacsconf-main-extensions (list "--answers.webm" "--answers.opus" "--answers.vtt")))
                         :title (plist-get o :title)
                         :url (concat "/" (plist-get o :url))
                         :speakers (plist-get o :speakers)
                         :track (if (member emacsconf-publishing-phase '(schedule conference)) (plist-get o :track))
                         :watch (plist-get o :watch-url)
                         :slug (plist-get o :slug)
                          (delq nil
                                 (when (plist-get o :captions-edited)
                                 (when (and (plist-get o :public)
                                            (or (plist-get o :toobnix-url)
                                                (plist-get o :video-file)))
                                   "video posted")))
                          ", ")
            (while attrs
              (let ((field (pop attrs))
                    (val (pop attrs)))
                (when val
                  (setq result (concat result " " (substring (symbol-name field) 1) "=\"\"\"" val "\"\"\"")))))

Publishing auto-generated navigation

It's nice to be able to navigate between talks without going back to the schedule page each time. This is handled by just keeping two extra copies of the list: one with the first talk popped off, and one with an extra element added to the beginning. Then we can use the heads of those lists for next/previous links.

2023-09-26_14-05-51.png Figure 2: Navigation

emacsconf-publish-nav-pages: Generate links to the next and previous talks.
(defun emacsconf-publish-nav-pages (&optional talks)
  "Generate links to the next and previous talks.
During the schedule and conference phase, the talks are sorted by time.
Otherwise, they're sorted by track and then schedule."
  (interactive (list (emacsconf-publish-prepare-for-display (or emacsconf-schedule-draft (emacsconf-get-talk-info)))))
  (let* ((next-talks (cdr talks))
         (prev-talks (cons nil talks))
         (label (if (member emacsconf-publishing-phase '(schedule conference))
    (unless (file-directory-p (expand-file-name "info" (expand-file-name emacsconf-year emacsconf-directory)))
      (mkdir (expand-file-name "info" (expand-file-name emacsconf-year emacsconf-directory))))
    (while talks
      (let* ((o (pop talks))
             (next-talk (emacsconf-format-talk-link (pop next-talks)))
             (prev-talk (emacsconf-format-talk-link (pop prev-talks))))
        (with-temp-file (expand-file-name (format "" (plist-get o :slug))
                                          (expand-file-name "info" (expand-file-name emacsconf-year emacsconf-directory)))
          (insert (concat "\n<div class=\"talk-nav\">
Back to the [[talks]]  \n"
                          (if prev-talk (format "Previous by %s: %s  \n" label prev-talk) "")
                          (if next-talk (format "Next by %s: %s  \n" label next-talk) "")
                          (if (plist-get o :track) ; tagging doesn't work here because ikiwiki will list the nav page
                              (format "Track: <span class=\"sched-track %s\">%s</span>  \n" (plist-get o :track) (plist-get o :track))

Before the talk description

We include some details about the schedule in the talk page, before the description.

2023-09-26_14-09-07.png Figure 3: Description

emacsconf-publish-before-page: Generate the page that has the info included before the abstract.
(defun emacsconf-publish-before-page (talk &optional info)
  "Generate the page that has the info included before the abstract.
This includes the intro note, the schedule, and talk resources."
  (interactive (list (emacsconf-complete-talk-info)))
  (setq info (or info (emacsconf-publish-prepare-for-display (emacsconf-get-talk-info))))
  (with-temp-file (expand-file-name (format "" (plist-get talk :slug))
                                    (expand-file-name "info" (expand-file-name emacsconf-year emacsconf-directory)))
    (insert "<!-- Automatically generated by emacsconf-publish-before-page -->\n")
    (insert (emacsconf-surround "" (plist-get talk :intro-note) "\n\n" ""))
    (let ((is-live (emacsconf-talk-live-p talk)))
      (when is-live (emacsconf-publish-captions-in-wiki talk))
      (when (member emacsconf-publishing-phase '(schedule conference))
        (insert (emacsconf-publish-format-talk-schedule-image talk info)))
      (insert (emacsconf-publish-format-talk-schedule-info talk) "\n\n")
       (if (plist-get talk :public) (emacsconf-wiki-talk-resources talk) "")
       "\n# Description\n"))
    (insert "<!-- End of emacsconf-publish-before-page -->")))

emacsconf-publish-format-talk-schedule-info: Format schedule information for O.
(defun emacsconf-publish-format-talk-schedule-info (o)
  "Format schedule information for O."
  (let ((friendly (concat "/" emacsconf-year "/talks/" (plist-get o :slug) ))
        (timestamp (org-timestamp-from-string (plist-get o :scheduled))))
     (append o
              (concat (or (plist-get o :video-time)
                          (plist-get o :time))
                      "-min talk"
                      (if (plist-get o :q-and-a)
                          (format " followed by %s Q&A%s"
                                  (plist-get o :q-and-a)
                                  (if (eq emacsconf-publishing-phase 'conference)
                                      (format " (%s)"
                                              (if (string-match "live" (plist-get o :q-and-a))
                                                  (if (eq 'after (emacsconf-bbb-status o))
                                                    (format "<>" (plist-get o :slug)))
                                                (emacsconf-publish-webchat-link o)))
              (if emacsconf-publish-include-pads
                  (format "Etherpad: <>  \n" emacsconf-year (plist-get o :slug))
              (format "Discuss on IRC: [#%s](%s)  \n" (plist-get o :channel)
                      (plist-get o :webchat-url))
              (if (member emacsconf-publishing-phase '(cfp program schedule conference)) (format "Status: %s  \n" (plist-get o :status-label)) "")
              (if (and (member emacsconf-publishing-phase '(schedule conference))
                       (not (emacsconf-talk-all-done-p o))
                       (not (string= (plist-get o :status) "CANCELLED")))
                  (let ((start (org-timestamp-to-time (org-timestamp-split-range timestamp)))
                        (end (org-timestamp-to-time (org-timestamp-split-range timestamp t))))
                     "<div>Times in different timezones:</div><div class=\"times\" start=\"%s\" end=\"%s\"><div class=\"conf-time\">%s</div><div class=\"others\"><div>which is the same as:</div>%s</div></div><div><a href=\"/%s/watch/%s/\">Find out how to watch and participate</a></div>"
                     (format-time-string "%Y-%m-%dT%H:%M:%SZ" start t)
                     (format-time-string "%Y-%m-%dT%H:%M:%SZ" end t)
                     (emacsconf-timezone-string o emacsconf-timezone)
                     (string-join (emacsconf-timezone-strings
                                   (seq-remove (lambda (zone) (string= emacsconf-timezone zone))
                                               emacsconf-timezones)) "<br />")
                     (plist-get (emacsconf-get-track (plist-get o :track)) :id)))
      "[[!toc  ]]
Format: ${format}  
      (if (plist-get o :alternate-apac)
          (format "[[!inline pages=\"internal(%s/inline-alternate)\" raw=\"yes\"]]  \n" emacsconf-year)

After the talk description

After the talk description, we include a footer that makes it easier for people to e-mail questions using either the PUBLIC_EMAIL property of the talk or the emacsconf-org-private e-mail address.


emacsconf-publish-format-email-questions-and-comments: Invite people to e-mail either the public contact for TALK or the private list.
(defun emacsconf-publish-format-email-questions-and-comments (talk)
  "Invite people to e-mail either the public contact for TALK or the private list."
  (format "Questions or comments? Please e-mail %s"
          (emacsconf-publish-format-public-email talk
                                          (and (string= (plist-get talk :public-email) "t")
                                               (plist-get talk :email))
                                          (plist-get talk :public-email)

emacsconf-publish-after-page: Generate the page with info included after the abstract.
(defun emacsconf-publish-after-page (talk &optional info)
  "Generate the page with info included after the abstract.
This includes captions, contact, and an invitation to participate."
  (interactive (list (emacsconf-complete-talk-info)))
  ;; Contact information
  (with-temp-file (expand-file-name (format "" (plist-get talk :slug))
                                    (expand-file-name "info" (expand-file-name emacsconf-year emacsconf-directory)))
     "<!-- Automatically generated by emacsconf-publish-after-page -->\n"
     ;; main transcript
     (if (plist-get talk :public) (emacsconf-publish-format-captions talk) "")
     (emacsconf-publish-format-email-questions-and-comments talk) "\n"
     (if (eq emacsconf-publishing-phase 'cfp)
         (format "\n----\nGot an idea for an EmacsConf talk or session? We'd love to hear from you! Check out the [[Call for Participation|/%s/cfp]] for details.\n" emacsconf-year)
     "\n\n<!-- End of emacsconf-publish-after-page -->\n")))

Whenever the schedule changes

This function makes it easier to regenerate all those dynamic pages that need to be updated whenever the schedule changes.

emacsconf-publish-info-pages: Populate year/info/*-nav, -before, and -after files.
(defun emacsconf-publish-info-pages (&optional info)
  "Populate year/info/*-nav, -before, and -after files."
  (interactive (list nil))
  (setq info (or info (emacsconf-publish-prepare-for-display info)))
    (emacsconf-publish-nav-pages info)
    (emacsconf-publish-schedule info)
    (mapc (lambda (o)
            (emacsconf-publish-before-page o info)
            (emacsconf-publish-after-page o info))


So once the review period has passed and we're ready to accept the talk, I change the status to WAITING_FOR_PREREC and find room for it in the schedule. Then I use emacsconf-publish-add-talk to add the talk description to the wiki. I review the files it generated, tweak hyperlinks as needed, add the pages to the Git repository, and push the commit to the server. If I rearrange talks or change times, I just need to run emacsconf-publish-info-pages and all the dynamically-generated pages will be updated.

-1:-- #EmacsConf backstage: adding a talk to the wiki (Post Sacha Chua)--L0--C0--September 26, 2023 06:12 PM

Irreal: Periodic Reminder: Emacs Bindings in macOS

Once again, as is occasionally the happens, I’ve stumbled across an Emacs user who discovered that some Emacs keybindings work in macOS. This user, sudeenhux, realized that he could navigate in the Apple Mail app with the normal Emacs bindings. He had no idea what was going on and took to reddit to ask what he had done to enable this. When reddit told him this was built in to macOS and worked with all macOS applications, he was overjoyed.

The thing is, of course, that what sudeenhux discovered is only half the story. As I’ve reported many times before—most recently here—macOS lets you configure your own bindings for its navigation commands. That would be a pain if you had to do it piecemeal but the way it works is that you put the bindings in a file and everything happens automatically. Even better, someone has already done the hard part and produced the file so all you have to do is download it and install it in the proper place. All the details are in the above link.

As a bonus, I recently got tired of not having Ctrl+Delete work in all macOS apps so I added an entry to enable it. That shows how easy it is to add your own bindings, assuming, of course, that macOS has a command that supports it.

I’m always surprised that so many people don’t know this so I like to remind everyone once in a while. Some of you may be tired of hearing about it but to those who don’t already know, it’s a revelation.

-1:-- Periodic Reminder: Emacs Bindings in macOS (Post jcs)--L0--C0--September 26, 2023 03:53 PM

T. V. Raman: Together: The Old And New Work Much Better!

Together: The Old And New Are Much Better!

1. Executive Summary

Emacs has had long-standing features like outline-mode and outline-minor-mode that often get forgotten in the face of newer affordances like org-mode. At the same time, Emacs continues to acquire new features — in this article, I am specifically alluding to repeat-mode. The combinatorial explosion that is the result of the various ways of bringing these together is mind-boggling — and often the result is that one fails to take advantage of what has become possible. The result is that one ends up sticking to one's long-established habits at the risk of failing to significantly enhance one's productivity.

2. Background

  • Structured navigation is a significant productivity enhancer — especially in my case since I rely exclusively on an auditory interface.
  • The above applies to both textual documents and programming source.
  • Starting all the way back in 1991, I started using folding-mode to organize code into hierarchical containers that could be expanded and collapsed; and this is what makes Emacs' eco-system amazing; folding-mode still works and is available on melpa in 2023.
  • About a year ago one of the Emacs maintainers (Stefan Mounier) helped me update portions of Emacspeak, and in the process pointed out that I could just use outline-minor-mode to expand and collapse sections of code in .el files.
  • At the time I filed this away for later use — I was still reluctant to abandon the 30+ year investment in folding-mode.
  • About a year ago, I discovered repeat-mode in Emacs and started leveraging it for everything — including outline-mode and org-mode amongst others.
  • Despite the years of investment in folding-mode, it had one drawback; keeping the fold-marks (special comments) in sync was always a bit of a hastle.
    • Bringing The Old And New Together

      This week I brought all of the above context together to:

  • Cut over the Emacspeak codebase to stop using folding-mode.
  • Turning the fold-marks to comments that outline-minor-mode understood was a trivial application of my typo.plPerl script.
  • I had already set up Emacspeak to use repeat-mode for the various outline modes.
  • Another annoyance with outline that I had fixed over 20+ years ago was to pick an easier to press prefix-key for outline; I use C-o.

3. The Resulting Experience

  • I can now skim the Emacspeak sources (as well as the Emacs sources of course) with very few keystrokes.
  • Example: Pressing C-o C-n navigates by section headings; when skimming, I only need to press C-o the first time thanks to repeat-mode.
  • I also bound j and k in the outline-mode keymaps to avoid having to chord when skimming — j is easier to press than C-n.

4. For The Future

  • Would be nice to enhance outline-minor-mode to understand sectioning comments in other programming languages.
    • The annoyance with the default (and unusable) prefix key for the outline modes needs to fix in Emacs core.
-1:-- Together: The Old And New Work Much Better! (Post T. V. Raman ( 26, 2023 03:34 PM

Sacha Chua: Org protocol: following Org links from outside Emacs

_xor had an interesting idea: can we use org-protocol to link to things inside Emacs, so that we can have a webpage with bookmarks into our Org files? Here's a quick hack that reuses org-store-link and org-link-open.

(defun org-protocol-open-link (info)
  "Process an org-protocol://open style url with INFO."
  (org-link-open (car (org-element-parse-secondary-string (plist-get info :link) '(link)))))

(defun org-protocol-copy-open-link (arg)
  (interactive "P")
  (kill-new (concat "org-protocol://open?link=" (url-hexify-string (org-store-link arg)))))

(with-eval-after-load 'org
  (add-to-list 'org-protocol-protocol-alist
               '("org-open" :protocol "open" :function org-protocol-open-link)))

To make exporting and following easier, we also need a little code to handle org-protocol links inside Org.

(defun org-protocol-follow (path &rest _)
  "Follow the org-protocol link for PATH."
  (org-protocol-check-filename-for-protocol (concat "org-protocol:" path) nil nil))

(defun org-protocol-export (path desc format info)
  "Export an org-protocol link."
  (setq path (concat "org-protocol:" path))
  (setq desc (or desc path))
  (pcase format
    (`html (format "<a href=\"%s\">%s</a>" path desc))
    (`11ty (format "<a href=\"%s\">%s</a>" path desc))
    (`latex (org-latex-link path desc info))
    (`ascii (org-ascii-link path desc info))
    (`md (org-md-link path desc info))
    (_ path)))

(with-eval-after-load 'org
  (org-link-set-parameters "org-protocol"
                           :follow #'org-protocol-follow
                           :export #'org-protocol-export))

Now I can use org-protocol-copy-open-link to copy a link to the current location, and I can put it into my Org files.

Example bare link to the Org manual, which will work only if you have open in the org-protocol-protocol-alist:


With a description:

Org manual - Protocols

This is part of my Emacs configuration.
-1:-- Org protocol: following Org links from outside Emacs (Post Sacha Chua)--L0--C0--September 26, 2023 01:42 PM

Irreal: Navigating In View Mode

Emacs view mode is one of things that might seem superfluous at first glance. After all, you can always just visit a file and use the normal navigation commands to view the file. But there’s a lot to be said for a read-only file viewer—think less, more, or other pager utilities—that let’s you page through the file without worrying about accidentally introducing a change.

The normal view mode protocol is to use Space and Delete to scroll forward and backward like Ctrl+v and Meta+v do. It’s a nice and convenient way of viewing a file that you don’t want to edit.

Charles Choi agrees with all that but wants a richer protocol. For instance, if he’s in an Org file, he’d like the movement to be forward and backward to the next heading. Once you make that leap, you realize that other file types could also benefit from specialized navigation commands. He has a post that explores the idea of file type naviation in view mode.

He presents some code to do this for several file types and it would be no problem to add other file types as well. Even if you don’t know Elisp, it would be easy to follow the cases he presents to add your own.

View mode is an underused feature of Emacs and Choi’s enhancements make it even better. Take a look at his post and see if you don’t agree.

-1:-- Navigating In View Mode (Post jcs)--L0--C0--September 25, 2023 04:30 PM

Sacha Chua: 2023-09-25 Emacs news

Links from, r/orgmode, r/spacemacs, r/planetemacs, Hacker News,, kbin,,, lemmy,, 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 Thank you!

-1:-- 2023-09-25 Emacs news (Post Sacha Chua)--L0--C0--September 25, 2023 12:40 PM

Ryan Rix: I'm now running my Matrix Synapse instance on The Wobserver Nix

I'm now running my Matrix Synapse instance on The Wobserver Nix

After last week's embarassingly-handled WebP 0-day, I realized my Synapse instance was sorely out of date and now had a Pegasus-class vulnerability on it. Unfortunately, the dockerfile I had been using to manage that service on my Wobscale server was out of date and didn't build with more recent versions of Synapse. Rather than using the upstream Debian-based Dockerfile, I was using one prepared by my dear friend iliana, one which she stopped using quite a while ago and I was maintaining myself. Welp nyaa~.

After briefly considering migrating to spantaleev/matrix-docker-ansible-deploy, and doing some math on exactly how much data a federating synapse node passes in a week or a month, I decided I would move the Synapse install on to my home network with my Wobscale 1U acting as a reverse proxy to my homelab machine over Tailscale.

And so on Friday afternoon I decided to wreck my sleep schedule and migrate across.

In I'm now running my Matrix Synapse instance on my Wobserver, I've written at length about this migration process, but I will spare the RSS feed the gruesome details. Click through if you're curious about moving a functioning Synapse instance to a NixOS machine with "only" 24 hours of downtime.

Synapse was one of the last services running on my Seattle 1U server; it was originally deployed back in like 2017 and has served me well but it's a geriatric Fedora Linux install that is now only running my Wallabag server on it. Once I migrate that to The Wobserver in my living room, I'll be able to turn this host down and ask ili for a small VM that can do the edge networking and be managed by my NixOS ecosystem instead of hand-tuned nginx configurations. That'll be nice.

This simultaneously took more and less work than I expected it to, and it's certainly not a perfect migration, but it is nice to be done with. It took about 22 hours of downtime all said and done, including some time spent sleeping while the thing was semi-functional.

This migration was a huge fnord for months where I would say "i should update my synapse, ugh, i should migrate my synapse to nixos, ugh, i should move to conduit, ugh i should just sign up for and never touch synapse again" every time my disk would fill up and i would have to do some stupid bullshit to clean it up enough to run VACUUM FULL on the synapse DB. It's still 65 fucking GB of old events I never want to see, and I recently learned why: "unfortunately the matrix-synapse delete room API does not remove anything from stategroupsstate. This is similar to the way that the matrix-synapse message retention policies also do not remove anything from stategroupsstate." this kills me, and this is probably why my next step will be to set up matrix-synapse-diskspace-janitor.

-1:-- I'm now running my Matrix Synapse instance on The
Wobserver Nix (Post)--L0--C0--September 25, 2023 05:05 AM

Tory Anderson: Emacs Tip: org-clone-subtree-with-time-shift with negative shifts

org-clone-subtree-with-time-shift with negative shifts This post is at the weekly Emacs Tips Reddit thread1. I use org-clone-subtree-with-time-shift (C-c C-x c on an org heading) sometimes when I need richer agenda entries than using a repeater like <2023-09-25 Mon +1w>. This allows for better notes upon individual dates, as well as more nuanced control like “except those three Mondays”. This morning I accidentally started my clones a couple weeks in the future, meaning I missed today and next week.
-1:-- Emacs Tip: org-clone-subtree-with-time-shift with negative shifts (Post)--L0--C0--September 25, 2023 12:00 AM

Protesilaos Stavrou: Emacs: live stream today (2023-09-25) at 14:30 Europe/Athens time

Raw link:

The video will be recorded and can be watched later. I will do some work on my Denote package and maybe tackle other Emacs-related issues. I plan to make this about 2 hours long, though we will see in practice.

About Denote

Denote is a simple note-taking tool for Emacs. It is based on the idea that notes should follow a predictable and descriptive file-naming scheme. The file name must offer a clear indication of what the note is about, without reference to any other metadata. Denote basically streamlines the creation of such files while providing facilities to link between them.

Denote’s file-naming scheme is not limited to “notes”. It can be used for all types of file, including those that are not editable in Emacs, such as videos. Naming files in a consistent way makes their filtering and retrieval considerably easier. Denote provides relevant facilities to rename files, regardless of file type.

  • Package name (GNU ELPA): denote
  • Official manual:
  • Change log:
  • Git repo on SourceHut:
    • Mirrors:
      • GitHub:
      • GitLab:
  • Mailing list:
  • Video demo:
  • Backronyms: Denote Everything Neatly; Omit The Excesses. Don’t Ever Note Only The Epiphenomenal.
-1:-- Emacs: live stream today (2023-09-25) at 14:30 Europe/Athens time (Post)--L0--C0--September 25, 2023 12:00 AM

Magnus: Defining a formatter for Cabal files

For Haskell code I can use lsp-format-buffer and lsp-format-region to keep my file looking nice, but I've never found a function for doing the same for Cabal files. There's a nice command line tool, cabal-fmt, for doing it, but it means having to jump to a terminal. It would of course be nicer to satisfy my needs for aesthetics directly from Emacs. A few times I've thought of writing the function myself, I mean how hard can it be? But then I've forgotten about it until then next time I'm editing a Cabal file.

A few days ago I noticed emacs-reformatter popping up in my feeds. That removed all reasons to procrastinate. It turned out to be very easy to set up.

The package doesn't have a recipe for straight.el so it needs a :straight section. Also, the naming of the file in the package doesn't fit the package name, hence the slightly different name in the use-package declaration:1

(use-package reformatter
  :straight (:host github
             :repo "purcell/emacs-reformatter"))

Now the formatter can be defined

(reformatter-define cabal-format
  :program "cabal-fmt"
  :args '("/dev/stdin"))

in order to create functions for formatting, cabal-format-buffer and cabal-format-region, as well as a minor mode for formatting on saving a Cabal file.



I'm sure it's possible to use :files to deal with this, but I'm not sure how and my naive guess failed. It's OK to be like this until I figure it out properly.

-1:-- Defining a formatter for Cabal files (Post)--L0--C0--September 24, 2023 08:20 AM

Protesilaos Stavrou: Emacs: mct version 1.0.0

Enhancements for the default minibuffer completion UI of Emacs. In essence, MCT is (i) a very thin layer of interactivity on top of the out-of-the-box completion experience, and (ii) glue code that combines built-in functionalities to make the default completion framework work like that of more featureful third-party options.

Below are the release notes.

Resumption of MCT development

In April 2022, I announced that I was discontinuing the development of my mct package. At the time, Emacs 29 was gaining new MCT-like capabilities and I thought we would quickly reach a point where my package would be superseded by built-in functionality. The article I published at the time:

About a year later and after receiving questions about MCT, I decided to restart its development. This was done in light of the realisation that the built-in Emacs functionality was still not as opinionated as MCT. There are good reasons for this state of affairs, due to the legacy of this important User Interface element and Emacs’ policy to not break stuff willy nilly. Still, the fact remains that MCT can fit in a very narrow niche for those who (i) like the built-in completions and (ii) appreciate a few extra niceties. What I wrote in March, 2023:

What does MCT offer that the built-in Emacs UI does not? In short:

  • MCT provides a facility for “live completions”, to automatically update the *Completions* buffer given certain conditions. A number of user options control the specifics.

  • There are user options for a passlist and blocklist, which determine what should automatically display the *Completions* buffer and be live updated. The passlist and the blocklist can target individual commands, such as find-file, as well as completion categories like buffer. The manual includes a section with several known completion categories.

To be clear: MCT builds on top of the built-in functionality and should not compete with it. Depending on my availability, I will try to prepare patches for emacs.git to see whether at least some features can be added directly to mnibuffer.el or related.

MCT supports Emacs 29 or higher

MCT is highly opinionated about how the completions should work. This applies to the presentation of the completion candidates as well as the behaviour of commands that cycle between the minibuffer and the *Completions*, treating the two as a contiguous space. In previous versions of Emacs, MCT could not work exactly as intended due to limitations in the underlying framework. For example, the variable completions-format gained the one-column value only in Emacs 28: Emacs 27 supported grid views which are not intuitive as a vertical list for up-down cycling between the candidates.

To make things easier to maintain, MCT only works with Emacs 29 or higher. The ~1 year hiatus has hopefully given users enough time to assess their options.

Deprecation of mct-region-mode

For a while, MCT supported in-buffer completion via a minor mode that would add all the needed functionality. This was always problematic due to underlying constrains and is thus no longer supported. MCT is designed to work exclusively with the minibuffer, where the behaviour is more reliable.

Nevertheless, users can still get an MCT-like experience with these settings, which affect the default UI (modify as you see fit):

;; Define the small wrapper functions
(defun my-mct-next-line-or-completion (n)
  "Select next completion or move to next line N times.
Select the next completion if `completion-in-region-mode' is
active and the Completions window is on display."
  (interactive "p")
  (if (and completion-in-region-mode (mct--get-completion-window))
      (minibuffer-next-completion n)
    (next-line n)))

(defun my-mct-previous-line-or-completion (n)
  "Select previous completion or move to previous line N times.
Select the previous completion if `completion-in-region-mode' is
active and the Completions window is on display."
  (interactive "p")
  (if (and completion-in-region-mode (mct--get-completion-window))
      (minibuffer-previous-completion n)
    (previous-line n)))

(defun my-mct-return-or-choose-completion (n)
  "Choose current completion or create N newlines.
Choose the current completion if `completion-in-region-mode' is
active and the Completions window is on display."
  (interactive "p")
  (if (and completion-in-region-mode (mct--get-completion-window))
    (newline n :interactive)))

;; Get the key bindings
(let ((map completion-in-region-mode-map))
  (define-key map (kbd "C-n") #'my-mct-next-line-or-completion)
  (define-key map (kbd "C-p") #'my-mct-previous-line-or-completion)
  (define-key map (kbd "RET") #'my-mct-return-or-choose-completion))

;; Tweak the appearance
(setq completions-format 'one-column)
(setq completion-show-help nil)
(setq completion-auto-help t)

;; Optionally, tweak the appearance further
(setq completions-detailed t)
(setq completion-show-inline-help nil)
(setq completions-max-height 6)
(setq completions-highlight-face 'completions-highlight)

The mct-minibuffer-mode is renamed to mct-mode

The mct-mode was the original name, which was later given the “minibuffer” specifier to disambiguate it from the aforementioned mct-region-mode. With the latter gone, this qualification is no longer pertinent and the original name can be restored.

The completing-read-multiple indicator has been removed

Previous versions of MCT would prepend a [CRM] tag to the minibuffer prompt of commands powered by completing-read-multiple. While this is a nice usability enhancement, it is not specific to MCT and thus should not be part of mct.el. Use this in your init file instead:

;; Add prompt indicator to `completing-read-multiple'.  We display
;; [`completing-read-multiple': <separator>], e.g.,
;; [`completing-read-multiple': ,] if the separator is a comma.  This
;; is adapted from the README of the `vertico' package by Daniel
;; Mendler.  I made some small tweaks to propertize the segments of
;; the prompt.
(defun crm-indicator (args)
  (cons (format "[`crm-separator': %s]  %s"
                  "\\`\\[.*?]\\*\\|\\[.*?]\\*\\'" ""
                 'face 'error)
                (car args))
        (cdr args)))

(advice-add #'completing-read-multiple :filter-args #'crm-indicator)

No more IDO-like file navigation

Older versions of MCT had a command for file navigation that would delete the whole directory component before point, effectively going back up one directory. While the functionality can be useful, it is not integral to the MCT experience and thus should not belong in mct.el. Add this to your own configuration file instead:

;; Adaptation of `icomplete-fido-backward-updir'.
(defun my-backward-updir ()
  "Delete char before point or go up a directory."
  (interactive nil mct-mode)
   ((and (eq (char-before) ?/)
         (eq (mct--completion-category) 'file))
    (when (string-equal (minibuffer-contents) "~/")
      (insert (expand-file-name "~/"))
      (goto-char (line-end-position)))
      (goto-char (1- (point)))
      (when (search-backward "/" (minibuffer-prompt-end) t)
        (delete-region (1+ (point)) (point-max)))))
   (t (call-interactively 'backward-delete-char))))

(define-key minibuffer-local-filename-completion-map (kbd "DEL") #'my-backward-updir)

Lots of changes under the hood

I do not intend to refashion MCT. It works the way it was originally intended to. What I did is to streamline the code for compatibility with Emacs 29 and tweak the custom commands to preserve the desired cyclic behaviour between the minibuffer and the *Completions*.

Experiments such as integration with the avy package or the ability to type-to-complete in the *Completions* buffer are abandoned.

Do not expect radical changes henceforth. I shall monitor and/or contribute to developments in core Emacs and am happy to forever archive MCT if/when the default completion UI gains the capabilities that, I think, make the user experience a little bit easier.

-1:-- Emacs: mct version 1.0.0 (Post)--L0--C0--September 24, 2023 12:00 AM

John D. Cook: Navigating a LaTeX file

I like generating long LaTeX documents from org-mode because, for one thing, org-mode has nice section folding. But not everyone I work with uses Emacs, so its better to work in LaTeX directly rather than have Emacs generate LaTeX.

AUCTeX has section folding for LaTeX documents, though so far I’ve only has limited success at getting it to work. However, RefTeX worked right out of the box.

If you enter reftex-mode ctrlc = then RefTeX will open a table of contents window.

RefTeX screen shot

Scrolling through the table of contents window scrolls through the body of the document. This isn’t exactly section folding, but it serves a similar purpose.

RefTeX ships with Emacs, so there’s probably no need to install it, but the mode is not enabled by default.

The post Navigating a LaTeX file first appeared on John D. Cook.
-1:-- Navigating a LaTeX file (Post John)--L0--C0--September 23, 2023 07:18 PM

Irreal: Batch Mode

If you’re an even moderately experienced Emacs user, you probably know about batch mode. You can write a file with some Emacs commands and run it either by calling Emacs on the command line with a --script parameter pointing to the file or by adding at #! line to the top of the script and running it by calling the script directly.

Emacs Elements has a short video on batch mode that describes a use case I hadn’t seen before. The video is only 5 minutes, 45 seconds so you can watch it to get the details but the TL;DR is that you can use batch mode to check a changed init.el file for errors.

The idea is that you use batch mode to load the modified init.el file. If there are any errors, you get notified but your active Emacs instance is still operable. It’s preferable to just restarting Emacs because if there’s a disabling error, you still have a working Emacs to correct it. Sure, you could always restart Emacs with a -q or -Q option but then you don’t really have a fully operational Emacs to work with. That happens to me a lot when I have an error in my init.el. I can get by, of course, but it’s a pain. With Emacs Elements’ method you still have your familiar and comfortable working environment available.

-1:-- Batch Mode (Post jcs)--L0--C0--September 23, 2023 03:40 PM

Alex Schroeder: 2021-11-06 How to join Gemini space

2021-11-06 How to join Gemini space

I’m hungry, and my wife is hungry, but I want to write this page, and my wife is reading about mutations and genomes and what not, so we keep postponing it. It’s now 21:36.

If you’re interested in #Gemini, a (small) hypertext world outside the world wide web, there are a few options out there. The Transjovian Capsules is a project I host that works well with Emacs (you know me) and Lagrange (a Gemini client by @jk).

Here’s how you would get started:

  • Transjovian Capsules (edit: no longer active 😢)

These instructions available on the web, of course, since there needs to be a way to cross over…

I’m still writing the documentation, so if you get stuck, mail me, and we’ll fix it, together. 🥳🚀🚀

#Gemini #Emacs


(Please contact me if you want to remove your comment.)

I was taking screenshots of my phone and wondered how to bring down the file size for uploading. This is what I ended up doing (using fish).

for f in image*.png
    convert -strip -geometry 400 -quality 50% "$f" "small-"(basename "$f" .png)".jpg"

This turns files such as image2.png (397KiB) into small-image2.jpg (22K).

– Alex 2021-11-06 22:46 UTC

It feels strange to be writing on the Intetebi capsule, knowing that you can only read it via Gemini. Oh well.

– Alex 2021-11-17 22:16 UTC

-1:-- 2021-11-06 How to join Gemini space (Post)--L0--C0--September 22, 2023 10:38 PM

Irreal: Live In Emacs Or Die

Today, we have a bit of humor that we can all relate to. Reddit has a post announcing the Web browser NYXT inspired by Emacs. I don’t know anything about NXYT but I did love this comment from noooit:

byu/evlogii from discussion

Those of us who live in Emacs will relate. The rest of the universe will wonder what in the world we’re talking about.

UPDATE [2023-09-23 Sat 15:04]: NXYT → NYXT.

-1:-- Live In Emacs Or Die (Post jcs)--L0--C0--September 22, 2023 04:06 PM

James Dyer: Plotting Org Table Weight Loss Using gnuplot

Now I have a weight loss org table defined and therefore an easy way to track the total amount of weight loss each week I had an idea for an improvement with some form of a satisfying graphical representation.

I have been aware of gnuplot for while now and I think this is an opportunity to give it a try through org-mode

The first step is a simple (use-package gnuplot)

here is the original table:

| date             | weight | pounds | loss | total |
| <2023-08-18 Fri> |   15:5 |    215 |      |       |
| <2023-08-25 Fri> |   14:9 |    205 |  -10 |   -10 |
| <2023-09-01 Fri> |   14:2 |    198 |   -7 |   -17 |
| <2023-09-08 Fri> |  13:11 |    193 |   -5 |   -22 |
#+TBLFM: $3='(convert-weight $2)::@3$4..@>$4=$3-@-1$3::@3$5..@>$5=vsum(@$4..@3$4)

and with a little investigation and trial and error I came up with:

#+PLOT: title:"Weight Loss" ind:1 deps:(4) type:2d with:lines set:"yrange [150:220]"
|   | date             |   stn | pnd | lss | tot | bar            |
| 0 | <2023-08-18 Fri> |  15:5 | 215 |     |     | WWWWWWWWWWWWWH |
| 1 | <2023-08-25 Fri> |  14:9 | 205 | -10 | -10 | WWWWWWWWWWWV   |
| 2 | <2023-09-01 Fri> |  14:2 | 198 |  -7 | -17 | WWWWWWWWWW;    |
| 3 | <2023-09-08 Fri> | 13:11 | 193 |  -5 | -22 | WWWWWWWWW:     |
| 4 | <2023-09-15 Fri> | 13:10 | 192 |  -1 | -23 | WWWWWWWWW      |
| 5 | <2023-09-22 Fri> |  13:9 | 191 |  -1 | -24 | WWWWWWWWV      |
#+TBLFM: $4='(convert-weight $3)::@3$5..@>$5=$4-@-1$4::@3$6..@>$6=vsum(@$5..@3$5)::$7='(orgtbl-ascii-draw $4 150 220 15)

The first thing to point out is that as I was learning more about org tables I came across the orgtbl-ascii-draw function which visually is quite self explanatory as shown in the bar column. The main arguments are the x axis range (150-220) and the span in characters (15)

Now on to the main plotting change and that is defining a #+PLOT header setting out all the parameters I want fed into gnuplot from the org table.

Again most of the defines are self explanatory and the only issue I ran into was trying to get plot lines to be rendered based off the original first date column. The histograms display did work and correctly listed each week with the corresponding bar value but lines only displayed a single vertical line at a 2023 value.

I am guessing in the case of a line display the data is plotted in a more non discrete manner and the date was parsed as best it could and just plucked out the first part of the date string which was the year. I decided not to investigate this and instead simply added another column with a simple numbering scheme and set my x axis off of that. I only want to see a plot over a number of weeks so I don’t care about the display of the date.

So now lets generate the gnuplot! which can be accomplished by running C-c " g (org-plot/gnuplot):

-1:-- Plotting Org Table Weight Loss Using gnuplot (Post James Dyer)--L0--C0--September 22, 2023 12:40 PM

Protesilaos Stavrou: Emacs: aLtCaPs version 1.2.0

The altcaps package is a small, focused-in-scope tool that helps users communicate mockery or sarcasm effectively. It does this by alternating the letter casing of characters in the words it affects.

Below are the release notes.

Breaking change to the value of altcaps-force-character-casing

This user option enforces the specified letter casing for the given character. The value is an alist. In previous versions, the car of each cell was a character type, whereas now it is a string type. Concretely, the old value was expressed like this:

;; Old value
(setq altcaps-force-character-casing
      '((?i . downcase)
        (?l . upcase)))

It becomes:

;; New value
(setq altcaps-force-character-casing
      '(("i" . downcase)
        ("l" . upcase)))

At least based on my correspondence, strings are easier for users. The notation for characters causes confusion.

The public altcaps-transform function

This is the function that performs the alternating letter casing, while also respecting the user option altcaps-force-character-casing. The function is more efficient now. Use it in Lisp with a single string argument, like this:

(altcaps-transform "Your wish is my command")
;; => yOuR wIsH iS mY cOmMaNd

The above return value is consistent with the default settings. With altcaps-force-character-casing bound, we can affect the output thus:

(setq altcaps-force-character-casing
      '(("i" . downcase)
        ("m" . upcase)))

(altcaps-transform "Your wish is my command")
;; => yOuR wiSh iS My CoMMaNd

Characters without casing no longer matter

Before, the algorithm was toggling the letter casing of virtually every character. This means that a string like "a.c" was wrongly treated as a sequence of three characters with letter casing, so the program was trying to do this:

a => downcase
. => upcase
c => downcase

Whereas now, the transformation skips characters without letter casing:

a => downcase
. => i Am ThE iNtElLiGeNtSiA nOw
c => upcase

The altcaps-replace is superseded by altcaps-replace-region

The altcaps-replace was not sufficiently abstract, making the code a bit repetitive. The new altcaps-replace-region is efficient in that regard.

The arity of the two functions is different: altcaps-replace was accepting one required argument plus an optional one, while altcaps-replace-region takes three arguments at all times. Please consult its doc string before adapting it to your code.

-1:-- Emacs: aLtCaPs version 1.2.0 (Post)--L0--C0--September 22, 2023 12:00 AM

Alex Schroeder: Orks, Höllenhunde, Balrog

Orks, Höllenhunde, Balrog

Ich leite im Moment Spiele in zwei Megadungeons (Stonehell und Arden Vul) und eine Wildnis (Hinter den Riesigen Riesen).

Auf der Hexkarte hat es natürlich auch unterirdische Höhlen und Kammern, Hallen und Höhlen – aber ich halte diese extra einfacher als einen Megadungeon. Wenn ich das nicht mache, kann es sein, dass es niemanden interessiert, oder es interessiert so brennend, dass sich niemand mehr für die Wildnis interessiert.

Es geht also darum, “kleine” Dungeons zu machen. Grundsätzlich hat es hier nicht sehr viel Info. Zwei drei Monstersachen, Schätze, ein paar Sätze, mehr nicht. Für den oberen Teil hatte ich folgende Beschreibung:

0608: On one of the rock faces you can still see the markings of the old dwarf forge Great Hammer. The ruin is abandoned and dead. If you explore the ruins, you will soon find the balor The Wounded Sceptre of the Gods who caused its downfall (HD 9+1 AC 4 1d6/2d6 F9 MV 12 ML 11; flying; aura of fire (anybody in melee takes automatic 1d6 fire damage); if the first attack with the flaming whip hits, the second attack with the flaming sword is at +4; only harmed by magic or magic weapons; immune to fire).

The orcs of the Filth-Spitting tribe serve The Wounded Sceptre of the Gods. 30 orcs led by Maufimbul (HD 7) live here (HD 1 AC 6 1d6 F1 MV 12 ML 8 XP 100). […]. The ancient spear Fur Hunter +1/+3 vs. mammals, a relic of the Old Lizard Wars, blessed by Set. Maufimbul is a famous engineer in the Ash and Steel school of Machinery.

Also here is cautious Grishg (HD 3). […] He likes to keep a low profile. He joined Maufimbul when things got hot up in the north.

Shagdog, last of the Fingerslayers has joined Grishg.

The orcs have built their settlement in the main hall, along the walls and overlooking the stone garden and its dwarf statues and the artificial pools. The round huts are protected by 2 boars (HD 3+1 AC 6 1d8 F1 MV 15 ML 9 XP 300). Every room has multiple little painted wooden statues depicting animals and plants from the surface world: goats, yaks, trees, mushrooms.

Gestern wusste ich: Am Abend habe ich drei Spieler mit jede Menge Unterstützung, die den Orks ihre Schätze abnehmen wollen. Die Elfe Jada hat zwei Kampfmagier der 5. Stufe auf Riesenskorpionen, die je einen Feuerball und zwei magische Geschosse mit je drei Pfeilen verschiessen können, plus eine weitere Elfe und einen weiteren Dieb. Der Mörder Hammond Vex hat einen Elfenumhang und Elfenstiefel, kann also fast perfekt schleichen, hat einen bezauberten Waldschratt und drei bezauberte Riesenaffen im Gefolge. Der Zwerg Unn hat eine Diebin im Gefolge. Am Schluss waren es 13 Leute + 2 Riesenskorpione der Kampfmagier + Unns Reitschwein.

Bezüglich der Anzahl Gefolgsleute halte ich es so: keine Rekursion. Die Gefolgsleute haben also nicht selber wiederum Gefolgsleute. Jeder kann ein Pferd oder sonst ein Reittier haben, aber wenn das Reittier besondere Befehle befolgen oder kämpfen kann, dann zählt es zum Gefolge. Das Charisma der Spielfiguren bestimmt, wie gross das Gefolge jeweils sein darf. Im Fall von Jada ist es so, dass das Gefolge auf 5 Personen, Reittiere und sonstige Begleiter beschränkt ist: die Kampfmagier Gwena und Sida, die Diebin Lilly, die Elfe Laetitia, und der Nekromant Sherwin. Dieser beschwört mit einem Spruch später noch einen Ghul, aber dieser gilt als Sprucheffekt, nicht als Gefolgsperson. Somit ist klar, auf den Riesenskorpionen kann man reiten aber sie kämpfen nicht. Falls sie kämpfen würden, dann wie freie Monster: sie greifen an, wen sie wollen. Das heisst, die Spielleitung bestimmt ihre Ziele durch würfeln.

Dann habe ich mir die Werte der Monster rauskopiert. Wie stark sind sie, wie viele gibt es, was würden sie zaubern, und so weiter. Hier ist mir persönlich wichtig, dass es mehr als nur 1–2 Monsterarten gibt und sie nicht zu kompliziert zu führen sind. Deswegen ist mein Balor Dämon auch einfacher als ein regulärer Dämon aus den offiziellen Regelwerken. Die sind mir viel zu kompliziert mit all ihren Fähigkeiten. Da gebe ich ihnen lieber noch ein paar Höllenhunde mit und gut ist.

Dann habe ich mir eine Liste von Orten aufgeschrieben. Dabei lasse ich mich von der ursprünglichen Beschreibung inspirieren. Was haben wir denn dort? Eine Haupthalle, einen Steingarten, Statuen von Zwergen, irgendwelche Wasserbecken.

In einer gewissen Weise kann man sich das als “point crawl” vorstellen: Ich bennen die Knoten und die Verbindungen sind eher allgemein gehalten. Das sind einfach breite oder schmale Tunnels, enge oder steile Treppen, Schächte, und so weiter.

Mit dieser Information male ich mir eine Skizze der Karte. Die ist mit der Füllfeder gekritzelt, ohne ästhetische Ansprüche. Ich nehme die Liste der Orte als Inspiration, vergesse ein paar davon (die Wasserbecken und die Statuen), zeichne noch ein paar zusätzliche Orte ein (die Schornsteine, die Schlackenhalde).

Auf der Karte notiere ich mir gewisse taktische Situationen. Wie sieht das Tor aus? Wie wird die lange Treppe verteidigt? Auf die Schützengalerie bin ich besonders stolz.


  • einen Überblick über die Spielfiguren und ihr Gefolge
  • die Monster und ihre Werte, vielleicht eine Tabelle für Zufallsbegegnungen
  • eine Liste der wichtigen Orte
  • eine Skizze der Umgebung

Ein Foto von meinem Notizbuch. Links Monsterwerte und Orte, rechts eine Karte mit Beschriftung.

Jetzt kann das Abenteuer beginnen! Im Spielbericht sieht das dann so aus:

Hammond Vex, Pfote, Tick, Trick, Track, Jada, Gwena, Sida, Lilly, Laetitia, Sherwin, Unn und Nela ziehen in den Riesigen Riesen gegen die Orks von Grosshammer… Die beiden Kampfmagier reiten auf ihren Riesenskorpionen, Unn reitet auf seinem Eber…

Am Fuss der Berge trifft Hammond Vex auf zwei Waldschratte, die sich als Spione zur Verfügung stellen. Ihr erster Auftrag: Verhindern, dass Ork Patrouillen der Gruppe nach Grosshammer nachziehen. Haltet den Weg zurück frei!

Bald windet sich der Weg in die Höhe, auf den kleinen Vorplatz von Grosshammer. Hinter den Toren hat es vier Orks, die relativ kurz angebunden sind und schnell schiessen. Doch die Magier schiessen mit magischen Geschossen zurück und der letzte wird bezaubert und öffnet das Tor. Dieser Ork wird geschickt, die Wachen auf der Schützengalerie zu holen, doch scheint die Sache auf zu fliegen: ein Horn ertönt. Die Gruppe macht sich bereit, will die anstürmenden Orks in dem etwa 20m langen Gang vom Torhaus mit Zauberei töten.

Bald hört man die Orks singen, Trommeln schlagen, pfeifen, und man hört den Balrog, der seine Höllenhunde in den Gang treibt. Ein Kampfmagier schiesst einen Feuerball in den Gang – doch die Hunde sind immun! Ein Gemetzel beginnt im Torhaus. Als der letzte Höllenhund erschlagen ist, stürmt, der Balrog durch den Gang, fängt Feuer, die Peitsche brennt, das Schwert brennt, alle im Nahkampf mit den Höllenhunden werden von Flammen umhüllt, das Stechen geht weiter – und dann geschieht das unglaubliche: Hammond Vex ersticht den Balrog, den Herrscher der alten Zwergenbinge Grosshammer.

Mit Schwert und Peitsche des Balrogs macht die Gruppe sich auf den Rückweg. Sie sind geschwächt. Und wer weiss, was die 70 Orks noch machen.

Ich bin zufrieden!

Die Beschreibung der Hexkarte wird angepasst, der Balor entfernt, die Anzahl Orks verringert, dann um 1W8 Orks aus der Umgebung wieder erhöht, und jetzt sollte ich wohl überlegen, wie es das nächste Mal weiter geht. Haben die Orks eine Chance, jemanden in der Nähe um Hilfe zu bitten?


-1:-- Orks, Höllenhunde, Balrog (Post)--L0--C0--September 21, 2023 03:21 PM

Irreal: Kitchen Sink

It’s often said of Emacs that it includes everything including the kitchen sink. Indeed, there’s even an Emacs logo reflecting this sensibility. I don’t know if it was ever an official logo but its use is widespread. Still, it would be easy to dismiss the notion as a bit of Emacs fanboy hyperbole.

Gopar has a short video that provides compelling evidence that Emacs does, indeed, include the kitchen sink and everything else. The video demonstrates two builtin functions. To say that they are obscure and little used would be an award winning understatement.

The first is a function that translates to and from Morse code. Just in case you want to know what “Emacs is amazing” looks in More code, Emacs has you covered:

./--/.-/-.-./... ../... .-/--/.-/--../../-./--.

The second functionality is to render text in the phonetic alphabet. I’m familiar with this from my pilot days, where it can increase clarity in radio communications, but didn’t expect to find it in Emacs core. If you want to know how to spell Emacs in the phonetic alphabet, here you go:


Emacs calls this the “NATO alphabet”. I’ve never heard it called that before but whatever you call it, Emacs can render text into it and, of course, go in the other direction as well.

Again, these functions are builtin, not packages. They make the current agonizing over whether or not to add actually useful functionality to Emacs core look positively silly.

-1:-- Kitchen Sink (Post jcs)--L0--C0--September 21, 2023 03:18 PM

Tory Anderson: using regexp find and replace to reformat my todo list

Intro I had a series of todo entries like this *** TODO Rename legacy/authtoken.clj and sibling tests and wanted them to have the issue number at the end of the todo title, which helps with our git branch naming praxis. Emacs regular expression (slightly improved) anzu-query-replace-regexp1 with the following: ^\(\*.*\) \(.*github.*\)\([0-9]+\)\s-+ → \1 #\3 \2\3 Where the linebreaks are entered literally via C-q C-j. The end result was what I wanted:
-1:-- using regexp find and replace to reformat my todo list (Post)--L0--C0--September 21, 2023 12:00 AM

Andrey Listopadov: Why Kakoune

Recently I’ve stumbled upon a video about Kakoune, a code editor: Idiot user tries to use Kakoune (for notes? Also Helix?). Funnily enough, I was mentioned in this video, which was a surprise, and made me laugh for quite a while:

Let’s go back to the official plugins page. This guy has made a bunch of plugins. Who is he? How is he able to make such good use of Kakoune? Oh, he’s an Emacs user! Of course!

Yeah, I am.

Even though the video is about Kakoune, the author’s main focus is note-taking and the oddities that come with this process when using a code editor to edit text. Kakoune advertises itself as a code editor for the most part, and I have to agree. As far as I know, Maxime Coste (@mawww), the creator of Kakoune, made it because of the desire for a better programming experience. As a result, a small and contained code editor was made. And I can appreciate a desire of the system one can fully grasp and understand.

Kakoune is relatively small, compared to Emacs and Vim that is. It doesn’t feature a scripting language, instead relying on shelling out if you need any programmable features. It’s a clever trick, and the editor exposes its internal state as a set of shell variables, so you still can do interactive things based on your workflow. And I did a lot of this back in the day when I used Kakoune.

I have mentioned Kakoune in this blog previously, but it was rather sparse. The reason for that is stated in the video pretty accurately - I use Emacs and not Kakoune, so there’s little to no reason for me to write about it besides occasional praise or comparisons. I mention it on my about page, and in various text-editor-related posts, but that’s it. I don’t participate in the Kakoune community anymore, and no longer actively maintain my packages, as I no longer use Kakoune.

But I still need to address this point of the video - I wasn’t an Emacs user when I started with Kakoune. Before Kakoune I was a Vim user! And transition from Vim to Kakoune was caused by several factors, one of which is again stated in the video:

…and people have written so many damn Vim plugins over the years that if you have a need it’s already been addressed like three or four different ways. So with Vim you could just piece together your ideal text editor like LEGO bricks…

And that’s exactly my problem with Vim - too many ways to do the same thing. At the time, I was working with SoC in C and started using the Ale plugin for asynchronous linting of the project, as the synchronous linting was quite slow. This was before LSP inception - just look at how many tools Ale supports. As far as I remember LSP was added to Ale much later, when competing plugins showed up.

Competing plugins.

There’s nothing bad with competition on its own, and in Emacs, this is also present, with many plugins, but in Vim’s case, I feel that people created most of the plugins purely because of the NIH1 syndrome. I lost count of how many plugins provided autocomplete interfaces, there were tons of list-narrowing frameworks, plugin managers, and snippet managers, and everything was poorly integrated with each other. I remember that some autocomplete plugins did not integrate well or at all with snippets, some synchronous completion providers were not supported by asynchronous completion frameworks, and so on. Again, this was before LSP came to the scene and basically became a standard for these features. So perhaps the situation is a bit better today, but I still have doubts. NeoVim people are going crazy over Lua API, writing their configs in Fennel, and making new Lua-based plugins that may or may not be compatible with the rest of the ecosystem.

Fact is, you can piece your dream text editor like LEGO bricks, just beware that some of these bricks are actually Duplo blocks, some are COBI bricks, lots and lots are probably LELE, and some are even freakin’ OLEG. Well, at least that was the situation when I used Vim, I gave up on updating my config around 2018 and made the switch to Kakoune. By that time I already made three plugins for Vim because I was unsatisfied with existing ones, but they were crappy too. Integrating these plugins into different other plugins was a huge pain. So I made the switch. You can trace the history from that point if you’re interested in my Kakoune journey (why would you be though).

Obviously, I missed a lot of features from Vim, and Kakoune actually has an entry on their wiki on how to migrate from Vim. Unfortunately, though, the suggestions were either too hardcore-minimalist or uncooperative. For example, Vim’s smarttab feature didn’t exist, and expandtab was suggested to be done via hooks. Not that it was wrong, but it was suggested when people asked about a very specific feature of Vim, and these did not provide the same feature as in Vim. So I started writing plugins.

However, Kakoune didn’t have conventional plugins at all at that time. Well, there was a section with plugins on the official page, but there was no real ecosystem. There was nothing such as Vimplug if you will.

Installing plugins meant you had to manually copy files around, or load them pathogen-style, but the process wasn’t convenient or easy to automate in my opinion. Updating plugins installed in this way was problematic too. This motivated me to make plug.kak. And then I started experimenting more and more with other interesting plugins.

But, around the same time I switched to Kakoune, I briefly tried Emacs. In reality, I tried Emacs like 4 times at that point, the earliest one dates back to around 2010. All four times I did not succeed, but something gravitated me to it for some reason. This last one actually was a reason why I made some plugins like langmap.kak or kaktree, which resemble what I saw in Emacs at that point. Many plugins were inspired by Vim, like smarttab.kak, powerline.kak, fzf.kak, tagbar.kak, equivalents to which I daily used in Vim before. And at that time, Kakoune really did everything I needed and was a very capable code editor.

But I still wanted something more. So why I made the switch to Emacs - but for a bit different reasons.

First of all, I started enjoying writing more prose instead of just writing code. And if you’ve watched the video I linked above, the author similarly wants a text editor, not a code editor. There’s another video on their channel about note-taking, featuring a lot of programs made specifically for this task, and it features a text editor section at the end in which the author talks about Emacs and Vim, briefly touching Kakoune and Helix. What they’re saying about Emacs is also very similar to what I’ve experienced, although I didn’t use an Emacs distribution, I started with vanilla2.

Org Mode.

I don’t know how to explain this to a non-Emacs, non-Org person, but every time someone asks the “Why Emacs?” question, Org Mode is somewhere at the top of the answers. And I never understood that, until I tried for myself. And boy are they right. But before I understood that, there was a long period of adoption. I still used Kakoune, but more and more I was shifted towards Emacs - it slowly consumed me.

A big part of that was that I started writing in Lisps. Emacs is the king when it comes to Lisp editing. Plugins for various Schemes, that I used to do tasks from the SICP book were amazing, and Kakoune :repl command paled in comparison. Though I can’t blame anyone here - Emacs is a lisp machine on its own, it’s bound to have great lisp editing experience.

Though lisp wasn’t my primary language back then, more like a novelty. I used Rust and considered switching jobs from a C engineer to a Rust back-end developer. Rust seemed both a perspective and a safe enough bet for the foreseeable future. Who knew how the tables would turn?!

Kakoune actually was great as a Rust IDE of sorts. The kak-lsp plugin was on it, written in Rust it supported Rust well. And it helped me at work with C too. That was 2019, the year I started using Emacs for real.

That year, I made my first, kinda big post, in which I realized that I wanted to write more. Ironically, it was a post about Emacs on the Kakoune forum. It was even written partly in Emacs and partly in Kakoune - I was comparing editors at that time, much like the author of already mentioned videos. But this day signified that I was ready to fully migrate to Emacs - my config was more or less ready for work at that point. Emacs seemed better at writing, although I was missing cool Kakoune features, such as multiple selections, a lot. I started writing this blog in 2020, and it was done in Emacs from the get-go. Not so long after that, I moved to Emacs completely.

I used Kakoune for 1.9835616438356165 years (first commit on Jul 24 2018, last commit on Jul 17 2020).

But this post actually is called “Why Kakoune” and not “Why I switched to Emacs”, so let’s address that!

Why Kakoune

What an awfully long preamble. If you read that, you have my thanks. If not - fair enough.

I think it’s kinda weird to read reasoning on why someone should use Kakoune from someone who’s not using Kakoune right now and hasn’t for another three years already. But, as far as I can see, not much has changed in Kakoune since! Which, actually, is great - I can actually just check out to a commit previous to the one I deleted my Kakoune config in the dotfiles repository, and run it. A fresh clone of Kakoune’s latest stable release builds in just two minutes on my machine, and loads my old configuration without too many errors:

At this point of the post I wanted to write about stability, but as it seems, the situation isn’t that great.

  • Some plugins simply no longer exist.
  • Some defaults were changed in 2022, making keys behave differently (can be turned back via a remap)
  • Some changes were made to how Kakscript is interpreted.
  • Most of my plugins broke (but that’s on me).
  • There’s possibly more, but I’m out of the loop.

What’s hasn’t changed is that people still stumble on the autoload directory after all these years. Because there’s no plugin manager in Kakoune, it relies on storing scripts you want to load automatically during startup in the ~/.config/kak/autoload directory. This, however, for some weird reason, disables loading a system-wide Kakoune autoload directory, and Kakoune simply stops loading all of its inbuilt features that are shipped as .kak files. I also experienced this problem, and it was one of the main reasons for making plug.kak. So, if anything above seems too weird, perhaps Kakoune is not for you.

But, given all that, Kakoune hasn’t changed that drastically over the three years I haven’t used it, and that’s a good thing. Even now, I can still edit files in it pretty comfortably after my brain does the switch from Emacs keybindings to a modal model. For the most part, that is, some habits are hard.

But one thing, that I think can be a main reason why people should try Kakoune, in my opinion, is its POSIX integration. Back in the day, I really liked this idea, can’t say so today3, but, it’s still a good reason why Kakoune is interesting.

I mentioned that I made plugins for Kakoune, and because of that I now know POSIX sh pretty well. Not that I need this knowledge that often, but when I do, I’m glad Kakoune taught me well. The same goes for other POSIX tools - Kakoune basically forces you to learn your shell stuff, because there’s no other way to be productive in Kakoune. Everything is done via shelling out to use some tool like fmt, grep, find, etc.

And when shell tools are not enough you can always call different programming languages from the shell. For example, some of my plugins are written in Perl of all languages. And while I can’t say that I’m proud of that, or that I know Perl that well, I can still say that Kakoune made me learn Perl, well, to some degree. Also, Awk. And I still occasionally use both when I need to send a code snippet to my colleague so that they can send me some filtered logs instead of full logs. Because that’s what these tools excel at, and learning how to use them from within an editor really makes it apparent how they can be useful. So Kakoune really helps you learn your standard tools, and some extra things too.

Another thing I think can be said is that Kakoune really makes you learn and understand regular expressions. When I started using Kakoune, I once told my friend that I started using an editor that is built around using regular expressions for text manipulation. They were quite skeptical, because I didn’t know regular expressions back then, and they had some experience and said that it’s a terrible idea to use them at all. But turns out, that regular expressions are actually easy to learn, and Kakoune really helps with that, because you’re constantly creating multiple selections, selections in selections, and filtering selections - all done with regexes. So, if you think that regexes are hard and you’ll never learn them (and you’re a Vim user by chance), give Kakoune a try.

And finally, Kakoune is just fun! Especially if you’re a seasoned Vim user, the inverted paradigm of object-verb really messes with your brain. I think Kakoune features a really unique editing model, where it doesn’t need any separate mode for selecting text - all motions do it automatically. When I started, I adjusted to the object verb paradigm pretty quickly, it’s very natural to how things are done - in real life, we usually don’t think upfront what we want to clean and then how many of what was that, ah yeah the shelves. We think that these shelves are dusty and we need to clean them. I should probably do it right now.

Anyway, a TL;DR for this could as well have been:

Kakoune gives you:

  • Small and understandable core.
  • Proficiency with POSIX tools,
    • and maybe even some programming languages other than sh.
  • Structural regular expressions as a central way of text manipulation.
    • With multiple selections created via regular expressions, acting upon regular expressions.
  • Fresh take on the modal editing paradigm.

So, yeah, Kakoune definitively deserves your attention, if you’re into experiments with your workflow. I, certainly, am. At least, I was, now I do everything from Emacs.

  1. Not Invented Here. ↩︎

  2. Though, I tried Spacemacs for a very brief period, mostly to see what’s there, and what can I have in Emacs. ↩︎

  3. Emacs, after some time, makes it obvious that you don’t need shells for most of the tasks, if not for all. ↩︎

-1:-- Why Kakoune (Post)--L0--C0--September 20, 2023 07:59 PM

Irreal: Zamansky: Learning Elisp #10

Mike Zamansky has popped in with another Learning Elisp video before he departs on his vacation. He didn’t want to start the next project and leave it hanging while he was away so this video covers some preliminaries. In particular, he covers some Elisp builtin data structures. That includes lists, pairs, vectors, hash tables, and association lists.

Vectors and hash tables have direct analogs in other languages and are probably familiar to all Irreal readers. Lists are simply linked lists and although they don’t have a builtin analog in most other languages, the idea is a common and familiar.

Zamansky explains how lists and pairs (single, unlinked cons cells) are implemented and then goes on to discuss the data structure that he’s planning to use in the next project: association lists. They’re sort of an intermediate structure, a bit like hash tables but lighter weight and not as fast. They’re the ideal key/value lookup method for small tables. It’s \(O(n)\) instead of the \(O(1)\) that hash tables provide but it’s perfect for relatively small lookup tables.

The next project is going to be about inserting emoji into a buffer and Zamansky is planning on using an association list to map the emoji name to the actual symbol. We’ll have to wait until Zamansky gets back from Europe for that but we can consider this video a teaser.

The video is 16 minutes, 16 seconds long so plan accordingly.

-1:-- Zamansky: Learning Elisp #10 (Post jcs)--L0--C0--September 20, 2023 04:07 PM

Alex Schroeder: Web mentions vs. referrals

Web mentions vs. referrals

Thinking about web mentions because of a post by @hbuchel I just read. I remember having added them to my wiki so that it would basically just post a comment. Then I started seeing spam, so I started checking whether the source actually linked to the page it claimed to link to. Then I realized that practically nobody was doing it. I had one person posting articles on their block and using web mention to ping my site. I guess they used a Wordpress plugin. Sadly, that plugin pinged my site no matter how insignificant the link. A few times the web mention didn’t really add value and eventually I disabled it. 😿

What seemed more useful, eventually, was using referrals. Browsers still send them along even though it’s a privacy issue, and I used reused the ideas I had already developed: see if the URL is readable, does it actually contain a link to my site, plus a blocklist that removes search engines, some pattern matching to try and get canonical URLs (with and without www subdomain, with and without https scheme, with and without certain path info and the like – a lot of Blogspot specific trickery), and on and on… so it was still a lot of work. Ugh!

But the signal was more interesting, I felt: a referral usually meant an actual person followed a link because it seemed interesting. I discovered Reddit threads and blogs linking to my pages. There was joy on a regular basis.

The only stain on the endeavour was that I still feel like this is a privacy violation. The signal I am using shouldn’t even be there. Plus the canonicalisation of URLs was annoying.

I don’t think I will be adding either of the two to this site.

The last time I thought about it:


-1:-- Web mentions vs. referrals (Post)--L0--C0--September 20, 2023 02:39 PM

Alex Schroeder: Generating forward indexes for Oddµ

Generating forward indexes for Oddµ

I find myself working on forward indexes and feeds for Oddµ. Or perhaps I should say: outside of Oddµ. I’m still holding off because it seems to be a very particular way of thinking about a wiki. My problem is that I use the wiki like a blog or Zettelkasten and therefore a feed is appropriate for my wiki.

So the first question is: Was I wrong when I wondered about feeds?

A feed is a dubious addition to a digital garden or a Zettelkasten. Yes, the ideas can be put in a chronological order, but “following along” is perhaps not the most efficient way of following along. – To have a feed or not

If the wiki is a site where visitors come to see what’s up, then the site is a publishing platform more in line with news. This points at the wiki duality: a digital garden where people work on pages like they would on papers to be published later or not at all, linked to from elsewhere; and a blog with updates of thoughts and projects. I find myself using the wiki like a blog with updates, not like a digital garden.

In order to use the wiki like a blog, the only thing that really seems to be missing is forward indexes. A forward index in this context is simply a list of links. On the front page, a list of links to “new” pages. On a topic page, a list of links to “new” pages for that topic.

For Oddmuse, I used “journal pages”. These pages transcluded the last ten appropriate pages, resulting in that wall of text vibe that I liked. This is also what many of the older blogs look like. If you wanted to find the older pages, you had to click through the pages, or you had to navigate an archive with dates. Neither of which was something I particularly enjoy.

What I did do a lot on my old wiki, however, was search for page titles and tags. Now, hopefully the search Oddµ has is good enough to offer that – we’ll see – but topic pages like RPG having a long list of links for easy browsing by page name seems like a good idea to me.

All of this to say that right now I have a Perl script that I call on the command-line, for now. I might call it as a CGI script one day, who knows.

For every file I give it as an argument, it does the following:

For every hash tag the file contains, it does the following:

  • add an item to appropriate feed, if it exists (not all hashtags need their own feed, I think)
  • add a link to the topic page, if it exists

To populate new topic pages with links to existing pages, I have used the following pipeline from hell: grep --ignore-case --files-with-matches '#' wiki/2023*.md|sort --reverse|perl -e 'use Encode; use Mojo::Util qw(url_escape); use File::Slurper qw(read_text); binmode(STDOUT,":utf8"); while(<>) { chomp; $d=read_text($_); ($t)=$d=~/^# (.*)/m; s/\.md//; s/^wiki\///; $s=$_; $s=~s/_/ /g; $n=url_escape($_); print "* [$t]($n)\n"}' … what can I say? I think it works. 🤪

#Oddµ #Perl

-1:-- Generating forward indexes for Oddµ (Post)--L0--C0--September 20, 2023 08:04 AM

Irreal: Emacs Writing Studio

As many of you know, I’m very interested in the use of Emacs for non-technical purposes. In particular, I interested in how prose writers and researchers in disciplines other than Computer Science and its close siblings use Emacs for their writing.

Peter Prevos has written a lot about using Emacs for writing and he’s gathered these posts together into an Emacs Writing Studio section on his blog. As Prevos puts it, “The Emacs Writing Studio configuration shows how to use Emacs to conduct research, write your prose and publish your text as an article, (e)book, or website.”

It’s an excellent resource conveniently broken into small easy-to-read articles. There are 28 articles—although some of them are only peripherally about writing. The other aspect of the Emacs Writing Studio is that it includes a downloadable configuration to get the n00b started using Emacs for writing.

In the reddit article about the Emacs Writing Studio, Alphapapa provides a link to his own collection of articles on using Emacs for writing that’s also worth taking a look at.

In his first article, Prevos notes that the right way to approach Emacs is to learn just enough to get started and then learn other details as they become necessary. As even moderately experienced Emacs users know, it’s a fool’s errand to try to learn everything about Emacs up front. Prevos suggests the same incremental approach that Irreal and others have recommended for Org mode.

If your principal activity is writing prose, you should take a look at Prevos’ site. It is, as I say, a nice resource.

UPDATE [2023-09-19 Tue 14:31]: Alphapappa → Alphapapa.

-1:-- Emacs Writing Studio (Post jcs)--L0--C0--September 19, 2023 03:40 PM

Alex Schroeder: 50% GDP destruction somewhere between 2070 and 2090

50% GDP destruction somewhere between 2070 and 2090

I’m still trying to process this. The situation might be so much worse – and I always thought I was a pessimist. This is from page 26 in “The Emperor’s New Climate Scenarios: Limitations and assumptions of commonly used climate-change scenarios in financial services” by Sandy Trust, Sanjay Joshi, Tim Lenton, Jack Oliver.

There is uncertainty around how much warming we will experience. As described in the previous section, atmospheric GHGs are now double their pre-industrial level, which is what ECS is calibrated to. A reminder that best estimate ECS = 3°C but there is an 18% chance that ECS>4.5°C.

Earth-system sensitivity is greater than ECS, as ECS assumes ice sheets and vegetation fixed, with a possibility that ESS is significantly greater than ECS. Conservatively, there is an argument for at least a 20% chance that we may be on a trajectory to 5°C or more of warming at current levels of GHGs.

The pace of warming is also uncertain. However, some scientists now estimate warming of 0.3 °C per decade or around 1°C every 30 years, which would imply warming of greater than 2°C by 2050 and 3°C by 2080. This is well within life expectancy for many in workplace schemes now and in range for the European Insurance and Occupational Pensions Authority (EIOPA) who have specified 80 years as long range for the Own Risk and Solvency Assessment (ORSA).

Put another way, at what point do we expect 50% GDP destruction – somewhere between 2070 and 2090 depending on how you parameterise the distribution. It is worth a moment of reflection to consider what sort of catastrophic chain of events would lead to this level of economic destruction.

The chances of me living up to 2070 are pretty slim but they are not zero. I’d be 97 and probably in need of care. I wonder how much care I can expect in a world that’s crashing like that.


-1:-- 50% GDP destruction somewhere between 2070 and 2090 (Post)--L0--C0--September 19, 2023 03:00 PM

Alex Schroeder: Linking fediverse accounts in Oddµ

Looking up

Linking fediverse accounts in Oddµ

I’m still enjoying working with Go. I’m still working on Oddµ. Here’s what’s been on my mind this morning: How do I link fediverse accounts to their profile pages? Example: @alex. There are two problems I can see:

  • international domain names
  • webfinger

Let’s take a look.

The first problem is the one that I am unable to crack. I think what would need to happen is for me to not look at bytes but do actual UTF-8 decoding. Right now, what I’m doing is this, once I know that we’re looking at an @: skip forward over a-z, A-Z, 0-9, . and - and exactly one other @ and then I skip backwards over . and that this is correct. In the case of the account name above, this makes sure that the . is not included. For international domains, that wouldn’t work. The bytes for schrö would include the bytes 0xc3 and 0xb6 to encode the ö in UTF-8.

I guess I could add “skip forward over any byte that’s above 127” to my list. This would work for schrö but it wouldn’t always end correctly. Does schrö… include the ellipsis? I’d say it does not. It is encoded by the bytes 0xe2, 0x80 and 0xa6 – all of which are bigger than 127. In other words, I need to decode the bytes and then figure out if the bytes are valid characters in a domain name.

This task is nearly impossible to solve. If you want to stare into the abyss, read RFC 5890 or RFC 5892 Appendix B. I think in the context of trusted (big ask!) wiki authors wanting link to a profile page would expect that any letters and digits plus the period and the hyphen are valid domain name constituents.

So what I really need is Unicode categories. I’m guessing a more-or-less valid domain name as entered by a nice person that is copying and pasting a valid account name from somewhere, the rules are: “letters, period and hyphen”? I know you can’t register some of these domains, since the allowed set can vary by registrar and those rules can change over time; and some of the characters are disallowed because they can be used to make phishing attacks; some characters may not be mixed for the same reason; but in our case, those cases don’t matter: if the resulting international resource identifier (IRI) doesn’t point to a valid profile, that’s not a problem.

But of course there are exceptions. 😭

ZERO-WIDTH NON-JOINER “may occur in a formally cursive script (such as Arabic)” … ZERO WIDTH JOINER “may occur in Indic scripts in a consonant-conjunct context” … and so on. It gets very specific! MIDDLE DOT “Between ‘l’ (U+006C) characters only, used to permit the Catalan character ela geminada to be expressed.” – RFC 5982 Appendix A.1

There really is no easy way out.

Perhaps all I can hope for is to improve this slowly, over time.

So the smallest next improvement should be the inclusion of all the Unicode letters. Let’s see how far that gets us.

Ah. Now I remember. I wanted to talk about two things.

The second thing I wanted to talk about is Webfinger. The problem is this: If you have an account like @alex then you can’t just turn that into It works most of the time, for sure. The popular servers implement that because Mastodon does it. The form is currently the redirect target of many servers. Sadly, there are servers that support only the first form and not the second, and there are servers that don’t accept either of them.

What you are supposed to do is slow and requires caching in some form. You are supposed to take the domain part of the account name, and do a webfinger query for the account resource. The response tells you where the actual profile page is. Looking at gets you the JSON document that tells you where the profile page resides:

I am loath to do these lookups because then we get into the topic of cache expiry and cache storage. I could just keep it in memory and rely on the occasional restarts to clear the cache as a first step. And then start to expire cache entries older than a few days at a later stage. I can’t say I like it.

I also don’t want to do this while rendering a page because the page loading will be slow every time you have a new account name. So what I’m doing right now is that I use the guess I mentioned above, an URI of the form – and I start a background process to do the lookup. The lookup then populates a map of accounts to URIs and overwrites the guess. A subsequent reload of the page then has the improved URI.

On a small site without a lot of traffic, I think it’s OK not to persist this map. Restarts automatically clear the map.

Let’s see if this works. This page has an account where the guess is wrong: doesn’t exist. But within a few seconds, this should be fixed.

-1:-- Linking fediverse accounts in Oddµ (Post)--L0--C0--September 19, 2023 02:13 PM

Protesilaos Stavrou: Emacs: modus-themes version 4.3.0

I just published the latest stable release of the Modus themes. The change log entry is reproduced further below. For any questions, feel welcome to contact me.

I will soon install the changes in emacs.git so please wait a little longer for the updates to trickle down to you.

All themes except the tritanopia ones have a new hover colour

The previous colour was not sufficiently distinct from what each theme defines for the bg-completion palette entry (preview a palette with M-x modus-themes-preview-colors or M-x modus-themes-preview-colors-current). This would make it hard to spot the difference while, for example, using vertico-mode in tandem with vertico-mouse-mode.

Same principle for the difference between the mouse hover and lazy isearch highlights (e.g. in Dired or Occur buffers).

Changing the hue here follows the same principle that underpinned the redesign of the grey backgrounds for version 4 of the project: depending on hardware capabilities, colour reproduction may not be optimal, so we need to be more considerate with the choice of colour values, erring on the side of caution.

The modus-operandi-tritanopia and modus-vivendi-tritanopia themes are not affected by this initiative, as they already used highly distinct hues.

Thanks to Daniel Mendler for bringing this matter to my attention and for testing the proposed alternatives. This was done via a private channel and the information is shared with permission. Daniel is the developer of vertico, among many other excellent packages:

Japanese holidays have the expected style

Japanese calendars style Saturdays uniquely and the Modus themes now do the same for those who use the japanese-holidays package. Saturdays show up in a blue colour (which changes to cyan for the modus-operandi-tritanopia, modus-vivendi-tritanopia themes).

Each theme’s palette has a new semantic colour mapping called date-holiday-other, just in case we ever encounter another scenario like this one (users can override any entry in the palette—consult the manual for the technicalities).

Thanks to Olaf Meeuwissen for bringing this package to my attention and showing me how traditional Japanese calendars style Saturdays. This was done in issue 311 on the GitLab mirror:

Each theme has semantic colour mappings for terminal emulators

These are used by ansi-term, vterm, and the like. The idea is to empower users to differentiate background and foreground values, should they ever encounter a need to do so (when in doubt, do nothing).

By convention, terminal emulators use the same value for both background and foreground, although this is not optimal with high contrast themes because what works as a foreground does not necessarily look nice as a background.

The default values of the new mappings retain the prior state, just to not break existing configurations. Consider this a tacit user option for those who really need it.

Thanks to Tony Zorman for reporting the problem that provided the impetus for this change:

All theme definitions conform with the latest standard for metadata

Themes are expected to declare their background type and affinity, such that the built-in command theme-choose-variant can do what it describes (switch between related themes). I was already doing this, though I had to make some adjustments. This is in response to Emacs bug#65468: Thanks to Mauro Aranda for bringing the matter to my attention.

Proper colours for the inline preview of the corfu top candidate

The corfu-candidate-overlay package is used in tandem with the corfu package to create an inline preview of the first matching candidate. Thanks to Nicolas Semrau for bringing this matter to my attention in issue 89 on the GitHub mirror:

Context indicators for the mode-line or header-line (breadcrumb.el)

I added support for the new breadcrumb package by João Távora. It displays information about where we are in the given file, such as under which Org heading or inside which function. The indicator can be shown on the mode-line or the header-line. Either way, it will now be legible and consistent with its surroundings.

The new family of nerd-icons is covered by the themes

This is a new set of packages: nerd-icons, nerd-icons-completion, ~nerd-icons-dired, nerd-icons-ibuffer. A popular package that uses the Nerd icons is doom-modeline, which the themes support as well.

All icons look as intended again

Some faces from the all-the-icons package were not configured because I accidentally changed their name from something like all-the-icons-red-alt to all-the-icons-red-warmer. I did that while renaming the colours defined in each theme’s palette, to make them more meaningful (“warmer red” can hint at something whereas “alt red” is more abstract).

Corrected the documentation about custom Org faces

The Modus themes manual shows how to configure arbitrary TODO keywords to inherit the style of an arbitrary face (get the list of faces present in your Emacs with M-x list-faces-display). The previous value I used was faulty. It now is as intended. Thanks to soaringbird for reporting the issue on the mailing list:

The colours used by avy are better for users with tritanopia

I changed the sequence of colours displayed by commands such as avy-goto-char-timer such that each individual background does not blend with the ones adjacent to it, while respecting the overall needs of a tritanopia-friendly design. I also tweaked the colour values to achieve the desired result. The backgrounds remain distinct from their context but now also work harmoniously together.

The bg-dim palette entry is marginally brighter in all Modus operandi variants

After extensive testing and side-by-side comparisons, I have concluded that the marginal increase in brightness improves the affected interfaces.

The bg-dim background is used, among others, in the header-line, the popup of the company and corfu packages, as well as the Org source blocks (when the user option modus-themes-org-blocks is configured appropriately).

The “intense” palette override preset has new colours for tables and prose metadata

This concerns the modus-themes-preset-overrides-intense (refer to its documentation on how to use it). The primary target of these changes is Org mode and the overall effect is subtle. The previous colours did not combine nicely with all structural elements. For example, Org clocktables would obscure timestamps by being the same colour as them, while the table formula would not stand out. These styles did not fit into the concept of “intense” colours.

The “warmer” palette override preset has more legible strings

The modus-themes-preset-overrides-warmer uses a more prominent warm green value for strings in programming modes. The effect is subtle, though it fits in better with the overall aesthetic of these palette overrides.

Org document keywords like #+author are optionally monospaced

When the user option modus-themes-mixed-fonts is non-nil, all Org document keywords will be rendered with the fixed-pitch face. This ensures consistency between keywords such as #+author and “meta lines” like #+texinfo. Thanks to user fluentpwn for the change: it is one line and thus the author does not need to assign copyright to the Free Software Foundation.

Git commit summary lines have a more precise style

This concerns the first line in a Git commit message, as seen while working with the magit package. Same principle for the log-edit buffer used by the built-in VC framework. Before, I was hardcoding a blue colour value, whereas now I apply the success face. The success face is designed to contrast with the warning face that is used to show overlong summaries. Furthermore, the added indirection makes it possible to particularise the colour value, which I do for the tritanopia themes that cannot use blue.


  • Removed explicit support for the built-in css-mode. Its default faces are decent. They inherit from standard font-lock faces that the themes already cover.

  • Recalibrated wordwise (“refined”) diffs for deuteranopia. The modus-operandi-deuteranopia and modus-vivendi-deuteranopia have a little bit more intense colour values applied to wordwise, else “refined”, diffs. These concern removed lines. The effect is visible while using magit or the built-in diff-mode.

  • Backported emacs.git commit 4cf33b6bd02b868ebbf112da7926d7c3c64517ce. It removed the space from the front matter of the file (i.e. the manual) because the Org export did not produce the right results, per Emacs bug#64548. Thanks to Stephen Berman for reporting the issue and making the requisite change.

  • Added support for the erts-mode. Thanks to Kevin Fleming for informing me about this built-in mode. This was done in issue 85 on the GitHub mirror:

  • Fixed a typo in the modus-themes-preset-overrides-intense doc string. Thanks to Nicolas Semrau for bringing this matter to my attention. It was done in issue 90 on the GitHub mirror:

  • Made all commands that prompt for a theme (modus-themes-select, modus-themes-preview-colors) apply the theme category to the available candidates. This allows the user to target said category to affect the relevant functions. For example, to set completion styles with completion-category-overrides or to define a custom annotation function with the marginalia package.

  • Added support for new appt-notification face (Emacs 30). Change upstream by me.

-1:-- Emacs: modus-themes version 4.3.0 (Post)--L0--C0--September 19, 2023 12:00 AM

Discovering Emacs podcast: Making Incremental Search Work for You in Emacs - EP3

-1:-- Making Incremental Search Work for You in Emacs - EP3 (Post Discovering Emacs)--L0--C0--September 18, 2023 10:55 PM

Marcin Borkowski: Making Anki flashcards from subtitles

Those of you who follow my blog know that one of my hobbies is translating subtitles. The main reason I do this is to watch stuff with my daughter, who doesn’t yet speak English fluently. Some time ago it dawned on me that I can use my translations twice. Not only can I watch films and tv series with her, but I can use them to help her learn English.
-1:-- Making Anki flashcards from subtitles (Post)--L0--C0--September 18, 2023 06:41 PM

Charles Choi: Enhancing Navigation in Emacs View Mode

For decades I’ve used view mode in Emacs akin to the command line tool less (or its older inspiration, more) where I page through a file using the spacebar in read-only fashion, and navigating through said file with basic motion commands. A recent video on motion from Emacs Elements has gotten me to indirectly reconsider how motion works in view mode which I’ll share in this post.

That video suggested to rebind the key sequences C-v and M-v to heading (or structural) navigation rather than typically scrolling up or down a screen. After trying it for both Org and Markdown modes, I became convinced. But when viewing said files using C-v, M-v to navigate structure, it seemed clumsy. Wasn’t there already a binding convention for quickly navigating structure in read-only buffers? Turns out there is. Many Emacs packages bind n and p to navigation in read-only buffers (e.g. dired, magit, Org Agenda). So I was led to do this with view mode.

Because view mode is a mode (albeit a minor one), it follows the conventions of having a mode map (view-mode-map) and a mode hook (view-mode-hook). The code below customizes view mode to support bindings of n and p that are specific to the major mode it is applied to. If no major mode is specified, then p and n are mapped to scroll-down-command and scroll-up-command respectively.

(require 'view)

 (lambda ()
   (cond ((derived-mode-p 'org-mode)
          (define-key view-mode-map (kbd "p") 'org-previous-visible-heading)
          (define-key view-mode-map (kbd "n") 'org-next-visible-heading))
         ((derived-mode-p 'markdown-mode)
          (define-key view-mode-map (kbd "p") 'markdown-outline-previous)
          (define-key view-mode-map (kbd "n") 'markdown-outline-next))
         ((derived-mode-p 'html-mode)
          (define-key view-mode-map (kbd "p") 'sgml-skip-tag-backward)
          (define-key view-mode-map (kbd "n") 'sgml-skip-tag-forward))
         ((derived-mode-p 'python-mode)
          (define-key view-mode-map (kbd "p") 'python-nav-backward-block)
          (define-key view-mode-map (kbd "n") 'python-nav-forward-block))
         ((derived-mode-p 'emacs-lisp-mode)
          (define-key view-mode-map (kbd "p") 'backward-sexp)
          (define-key view-mode-map (kbd "n") 'forward-sexp))
         ((derived-mode-p 'makefile-mode)
          (define-key view-mode-map (kbd "p") 'makefile-previous-dependency)
          (define-key view-mode-map (kbd "n") 'makefile-next-dependency))
         ((derived-mode-p 'c-mode)
          (define-key view-mode-map (kbd "p") 'c-beginning-of-defun)
          (define-key view-mode-map (kbd "n") 'c-end-of-defun))
          (define-key view-mode-map (kbd "p") 'scroll-down-command)
          (define-key view-mode-map (kbd "n") 'scroll-up-command)))))

The cond expression above can be extended to support more modes as desired.

Highlight Line Mode

With structural navigation in view mode, I find it more useful to highlight the line where the point is. hl-line-mode does just that. The following code shows how to turn on highlight line mode and to disable it upon exiting view mode to edit the file.

(add-hook 'view-mode-hook 'hl-line-mode)

(defun cc/view-exit ()
  "Advice function to disable highlighting upon exiting view-mode."
  (hl-line-mode -1))

(advice-add 'View-exit :after #'cc/view-exit)

Source for all code fragments above can be found on GitHub. All code tested with Emacs 28.2.

Closing Remarks

One might argue that the above behavior skirts around modal-style editing and navigation, but it’s really not my intention to go in that direction. That said, since using view mode means you are not editing a file, it seems sensible to take advantage of using single key bindings.

Having been around since Emacs version 16, view mode is both old and for me, overlooked with regards to thinking about customizing despite my frequent use of it. Using structural navigation in view mode has been surprisingly delightful. If this is new to you, I think you’ll find it the same.

-1:-- Enhancing Navigation in Emacs View Mode (Post Charles Choi)--L0--C0--September 18, 2023 06:00 PM

Mike Zamansky: Learning Elisp 10 - elisp data structures

I'm leaving our next elisp project for when I get back from my vacation but realized I could make a short video with some preliminaries - some elisp tools that we'll need that we can get out of the way.

Specifically, elisp's built in data structures.

We've already used the list data structure and in this video we go a bit more in depth.

Lists are great for, well, list processing but they're not great for random access. For that we can use vectors. Think of them like arrays in other languages but with a functional way of accessing:

 (setq v [10 20 30 40 50]) ;; define a vector
 (aref v 1) ;; returns the 20, the value in index 1
 (aset v 1 100) ;; stores 100 in index i
 ;; the vector will now have [10 100 30 40 50]

 (mapcar #'1+ v)
 ;; the above returns a new list with (2 101 31 41 51)

Note in that last example above we can use mapcar which we covered in an earlier video on vectors as well as lists but the return is a new list, not a vector.

We also cover elisp hash tables which are similar to python dictionaries. With them you can store and retrieve values based on keys (key value pairs). We're not actually going to use them in the next project so you can see the video for details.

The data structure we will be using are association lists. You can think of them as a lightweight hash table. Basically a list of pairs. A pair is a special form of a list that only has two items (well, not exactly but see the video for details). We write them by putting a period between the two elements - (1 . 2) - that's a pair with 1 as the first part and 2 as the second.

Here's an association list and how we use it:

 (setq a '((1 . "one value") (2 . "two value") (3 . "three value")))
 (assoc 2 a) ;; returns (2 . "two value")
 (cdr (assoc 2 a)) ;; returns "two value"
 (rest (assoc 2 a)) ;; also returns "two value"

In our next project, we'll use association lists to store the string names for emojis as the keys and the actual emojis for the values.

That's it for now. More in a few weeks.


The code for the series is still up here:

but this episode doesn't have any specific code.

The videos:

-1:-- Learning Elisp 10 - elisp data structures (Post)--L0--C0--September 18, 2023 05:57 PM

Sacha Chua: 2023-09-18 Emacs news

Links from, r/orgmode, r/spacemacs, r/planetemacs, Hacker News,, kbin,,, lemmy,, 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 Thank you!

-1:-- 2023-09-18 Emacs news (Post Sacha Chua)--L0--C0--September 18, 2023 01:02 PM

Eric MacAdie: 2023-09 Austin Emacs Meetup

There was another meeting a couple of weeks ago 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 of interest to ... Read more
-1:-- 2023-09 Austin Emacs Meetup (Post Eric MacAdie)--L0--C0--September 18, 2023 06:31 AM

Irreal: Fixing The Emacs/Gnupg Problem

For some time there’s been a problem between Gnupg 2.4.1 and Emacs. Although you can decrypt files with Emacs—your .authinfo.gpg file for example—you couldn’t edit and save an encrypted file: Emacs hangs. This turns out to be a mismatch between the way Emacs and Gnupg expect to communicate the password and apparently it’s a hard problem to resolve. Until one or both apps are fixed you need a workaround.

The workaround is simple: downgrade to Gnupg 2.4.0. That’s not hard, of course, if you compile Gnupg yourself, but is harder if you use Brew to install it. There’s no easy way to do that with the standard Brew commands and the process that I’ve seen previously seemed a bit complex.

Álvaro Ramírez to the rescue. He has a post that gives us a simple procedure for doing the downgrade. Basically, you just download the Brew recipe and then install it using standard Brew commands. I’ve already done that and it took me less than a minute so there’s no reason not to follow his prescription if you’re having the problem.

-1:-- Fixing The Emacs/Gnupg Problem (Post jcs)--L0--C0--September 17, 2023 04:09 PM

Manuel Uberti: Marilyn's avatar

“I guess there isn’t any Norma Jean, is there?”

Blonde (Andrew Dominik, 2022)

The first image of Marilyn Monroe I have ever seen is a photo of her, up on the wall of the living room in the house where I grew up. It has been there for as long as I can remember and it is still there today. I have never paid too much attention to it, to be honest, even when I understood who the charming lady was. It seemed like an icon from a past I did not feel really attached to. Recently, while reading Joyce Carol Oates’ Blonde and right before watching Andrew Dominik’s film of the same name, I have had the chance to look at the photo more closely. Once again, it eluded me. Where is Norma Jeane here?

Oates and Dominik ask themselves the same question and set out to explore what phrasing it means, way before searching for an answer. What Oates aptly discovers and what Dominik bravely takes away from her book is that in order to understand the woman behind Marilyn Monroe one has to start from her body and think of how she lived it. This could explain why Oates’ and Dominik’s works have been met with mixed reactions. There is no point in expecting the story of Marilyn Monroe or asking for another apologetic tale of belated repentance, just as it makes no sense to complain about how shattered one’s own mental picture of her will be after the last word and the last scene. Quite the contrary. The fragments Oates and Dominik disperse should be treasured as pieces of a bigger puzzle everyone is required to complete.

Marilyn Monroe (Cecil Beaton, 1956)

Indeed, Marilyn Monroe remains a mystery the solution of which cannot be found neither in Oates’ book nor in Dominik’s film. Still, the pages and the images provide a specific point of view, an angle from which one can observe Marilyn Monroe and try for themselves to find a way in her world of mirrors, illusions, fame and failure, tragedy and eternity. I have the utmost respect for a writer, a director or, generally speaking, an artist who provokes me or even infuriates me. As long as a reaction to what is placed in front of me is demanded, art is already doing the right thing regardless of any aesthetic consideration. I like to think I deserve a bit of credit from an artist, I want to believe they know they can rely on me doing my best to engage with their effort. Simply put, I may not share the artist knowledgeable background, but it does not mean that I should be treated as a goof.

This is why I felt so compelled by Oates and Dominik. They refuse to pamper their audience and keep pushing forward whatever they think worth of our attention. Take Oates, for instance. She offers a plethora of voices, never at peace with a simple opinion, deliberately refusing to add details for the sake of completeness. What if the reader cannot understand a specific passage because they are not familiar with Norma Jean’s youth? What if they cannot recognise names, places, events? Oates does not care. She points at the source material for her book in the very first pages, so the reader knows how to fill the gaps if so inclined. Eventually, Oates shapes facts, invents characters, exaggerates situations, and breaks conventional pictures of Marilyn Monroe. What we read is beyond the true-false dichotomy. Only Norma Jean painfully knows what is true and what is false about Marilyn Monroe.

Blonde (Andrew Dominik, 2022)

Dominik accepts Oates’ uncompromising imagination and dives as deep as possible in the horror of this story. The director plays to great extent with Norma Jean being an avatar of Marilyn Monroe. One would expect the exact opposite, but Dominik convincingly insists on the psychological power of Monroe, a white men product, over the fragile woman who was never able to overcome it. Monroe takes apart Norma Jean mind and body, destroys her private life every time she tries to get it back, and owns her to the end until exhaustion. Much like Oates, Dominik picks what makes sense to him from the book and, to a larger extent, from Monroe’s life. And just like the writer, the director twists specific moments and develops new scenarios with remarkable effect. Look, for example, at his shocking depiction of Norma Jean’s nightmares or marvel at the outstanding yet unsustainable last minute of the film.

Both authors have been criticised for their supposedly merciless treatment of a sacred icon like Marilyn Monroe. Again, destabilising common conscience and subverting given myths is precisely what art should do. Oates’ and Dominik’s works aim at the same target: where does entertainment end? More than twenty years separate the film from the book, but this question is as relevant today as it was back in the early 2000s.

-1:-- Marilyn's avatar (Post)--L0--C0--September 17, 2023 12:00 AM

Please note that 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 . Thank you!