Irreal: Setq And The Others

James Cherti has an interesting and useful post about the relative efficiency of setq, setq-default, setopt, and customize-set-variable. Cherti justifies this concern on their effect on Emacs startup time but according to well established Irreal dicta this doesn’t matter because we seldom restart Emacs so a few seconds more or less simply don’t matter.

Oddly, there are some people who pay no attention to Irreal dicta and who obsess about every millisecond—Cherti’s term—of Emacs startup time. For those people and those who worry about execution times in general, Cherti’s post is very useful and explains why some of those commands are faster than others.

The crux of the matter is that defcustom can include a setter method that can be arbitrarily complex. Cherti has a nice example of this that will help you understand what’s going on. Cherti says that very often there’s no reason to invoke that setter method during startup and that setq or setq-default is faster and just as useful. Commands like setopt and customize-set-variable always call the setter method so they may be slower. Cherti’s examples show how to avoid unnecessary bottlenecks.

Cherti also shows how even the :custom keyword in use-package can trigger the setter methods of those variables and gives some workarounds. The main problem is that it’s hard to know when you can avoid the setter method and when you can’t. Sometimes, you just have to read the source code to figure it out.

Obsessing about startup times aside, it’s nice to have an explanation of what these commands do and how their use might affect run time. Cherti’s post is relatively short and well worth a minute or two to read.

-1:-- Setq And The Others (Post Irreal)--L0--C0--2026-08-23T15:15:54.000Z

Sacha Chua: Emacs Chat 29: Abdallah Maouche (en français)

Nous allons parler en direct avec Abdallah Maouche (heiwiper) (heiwiper/emacs.d, Gitlab, Github) d'Emacs, de ses flux de travail, de sa configuration, et d'autres sujets en français. Je suis un peu intimidée, mais si vous posez vos questions dans le chat, je pense que nous pouvons survivre à mon faible niveau de français. =)

J'ai essayé de corriger la transcription, mais je crois qu'il y a encore beaucoup d'erreurs, donc n'hésitez pas à m'envoyer un message.

La transcription un peu corrigée

Details

0:01 Introduction

Sacha: Ok, vous ĂȘtes en direct.

Prot: On a le nouveau lien ou c'est le mĂȘme ?

Sacha: C'est le mĂȘme lien que Google Meet mais je vais copier le nouveau lien. C'est YouTube.com, sachactube... C'est difficile. Oui.

Prot: Oui.

Sacha: Je ne peux pas poster le chat. Donc, je vais poster sur les conférences. Comment dire? Ah! Ajoutez un commentaire. Et je vais aussi ouvrir. Oui. Donc, le chat est ici. Je pense.

Prot: Ah, take two. J'ai trouvé. Oui, oui.

Abdallah: Oui, j'ai trouvé moi aussi.

Sacha: Ça marche? Merci beaucoup pour votre patience. L'introduction ! Bonjour Ă  toutes et Ă  tous et bienvenue dans le troisiĂšme Ă©pisode d'Emacs Chat en français et le vingt-neuviĂšme Ă©pisode de la sĂ©rie. Notre invitĂ© aujourd'hui est Abdallah Maouche, qui s'est fait aussi appeler par son pseudo heiwiper. Il vit en AlgĂ©rie et le français n'est pas sa langue maternelle, mais il s'y est assez courageusement avec nous. Si on se retrouve totalement bloquĂ©, comme moi, justement, pas de souci, nous pouvons passer au franglais ou Ă  l'anglais. Ce n'est pas grave. Bonjour Abdallah et encore merci d'ĂȘtre lĂ .

Abdallah: Merci, merci beaucoup. Merci pour l'invitation. Ça me fait un grand plaisir de parler à vous deux, car je vous suis sur votre blog et sur la chaüne YouTube. Donc, j'ai trùs hñte de converser avec vous.

Sacha: Et merci aussi à Prot de se rejoindre à nous pour cette expérience. Nous faisons de notre mieux ensemble. Merci beaucoup.

Prot: Je t'en prie, je t'en prie. C'est parti. Oui, c'est magnifique, c'est magnifique.

3:13 Apprentissage

Sacha: Abdallah, contrairement aux invités précédents, tu utilises Emacs depuis seulement sept ans. Je plaisante. C'est déjà pas mal, c'est génial. Peux-tu me raconter ton parcours d'apprentissage d'Emacs moderne? Comment est-ce que tu l'as découvert?

Abdallah: Donc, ça a commencĂ© environ 2019-2020. À ce moment, j'ai dĂ©couvert Linux pour la premiĂšre fois et j'ai commencĂ© Ă  explorer les applications open source. C'Ă©tait quelque chose de nouveau pour moi. Donc, Ă  un certain niveau, j'avais besoin d'un texte Ă©diteur parce qu'en universitĂ©, on avait utilisĂ© des environnements de dĂ©veloppement spĂ©cialisĂ©s pour des langages spĂ©cifiques. Par exemple, il y avait Eclipse pour Java et je pense CodeBlock, c'Ă©tait pour C, C++. Et puis au master, il y avait tellement de langage que c'Ă©tait difficile de garder le mĂȘme flux de travail. Donc, je me suis rendu au rĂ©pertoire des applications de distribution Linux que j'utilisais Ă  l'Ă©poque. Et j'ai parcouru la liste et j'ai trouvĂ© plusieurs Ă©diteurs et parmi ces Ă©diteurs, bien sĂ»r Emacs. Je l'avais installĂ©, j'ai installĂ© tous les Ă©diteurs en fait. Mais le problĂšme c'est quand je l'ai testĂ©, ça n'a pas vraiment, ça m'a pas plu. C'Ă©tait un peu, l'interface graphique Ă©tait un peu, je ne sais pas comment dire, un peu moche. Oui donc, et aussi il y avait le logo Emacs avec le GNU et le E ça ressemblait Ă  le symbole Epsilon. Donc ça me rappelait un peu des maths et je voyais les keybindings avec les moins et tout. Je me suis dit c'est un Ă©diteur pour les mathĂ©maticiens. Donc j'ai installĂ© Atom et j'ai utilisĂ© Atom. AprĂšs quelques mois, je pense, j'ai vu une vidĂ©o Ă  propos de Spacemacs. Parce que j'apprenais Game Engine Godot. Donc j'ai vu dans une chaĂźne Spacemacs et c'Ă©tait trĂšs intĂ©ressant. Donc j'ai installĂ© et j'ai appris les keybindings de Vim parce que c'est plus recommandĂ© dans Spacemacs. Et je l'ai utilisĂ© pour une pĂ©riode et puis je pense que j'ai J'ai changĂ© la configuration pour utiliser les keybindings Emacs aprĂšs parce que l'intĂ©gration de Evil, l'intĂ©gration des Vim keybindings n'Ă©tait pas parfaite. Il y avait des trucs qui manquent et c'Ă©tait un peu frustrant. Donc, j'ai dĂ» switcher Ă  Emacs Keybindings. Et je pense, aprĂšs un certain temps, j'ai eu mal Ă  configurer Spacemacs. La communautĂ© Ă©tait un peu trop petite. Oui, quand je rencontre des problĂšmes et je vais dans le Reddit d'Emacs, je ne sais pas comment appliquer les solutions Ă  Spacemacs. Donc, je me suis dit, je vais devoir changer et utiliser Emacs, ce qu'on appelle Vanilla Emacs. Je pense que j'ai vu les vidĂ©os de David Wilson. Est-ce que vous le connaissez?

Sacha: Oui, bien sûr.

Abdallah: Oui, System Crafters. C'est sa chaßne YouTube. Et j'ai suivi son tutoriel et depuis ce moment, je configure mon Emacs from scratch depuis le début. Et maintenant, j'ai une configuration, celle que je vous ai envoyée. Je l'ai publiée sur mon GitHub. Et je l'utilise quotidiennement pour les trucs personnels et au travail aussi.

Sacha: Les kits de débutants Spacemacs réduisent les peurs pour toi. Et aprÚs que tu [t'es] habitué à Emacs, tu repasses vers Emacs.

Abdallah: Oui, exactement. Je pense que le point important à l'époque, c'était l'interface graphique. Spacemacs avait une interface déjà configurée et c'était un peu attractif quand on voit la config Spacemacs. C'est pour ça que j'ai utilisé Atom, parce que c'était un peu aussi... Il avait une interface qui m'a plu.

Prot: Je crois que c'est aussi plus facile de découvrir les fonctionnalités avec quelque chose comme Spacemacs. Oui, je pense... Oui, c'est difficile.

Abdallah: Oui, parce qu'il y a des... La configuration de Spacemacs, c'est comme si... La configuration de Emacs avec beaucoup d'abstractions. C'est comme si on ajoute des couches de programmation, couche de langage, je ne sais pas, Java ou bien quelque chose comme ça. Donc, on va ignorer le fait qu'il y a des serveurs LSP et tout. En fait, il faut juste spécifier quel serveur tu veux utiliser et Spacemacs va configurer tout. Donc, c'est une abstraction de la configuration Emacs. Mais ça reste en Emacs Lisp.

Sacha: Est-ce que c'est difficile de traduire ta configuration de Spacemacs Ă  ta propre configuration?

Abdallah: En fait, je ne connaissais pas assez Emacs Lisp, donc je n'ai pas essayé, mais ce n'était pas évident parce que Spacemacs, je pense que c'est C'est trÚs différent de la configuration Emacs. Je pense qu'il y a beaucoup d'abstractions. Ce n'est pas évident de transférer la configuration et la traduire.

Prot: Parce que si Spacemacs utilise [les macros Emacs Lisp], tu dois connaĂźtre les macros. Autrement, tu ne peux pas transfĂ©rer le mĂȘme code.

Abdallah: Oui, évidemment, vous avez raison. Oui, les macros, c'est un peu sensible.

Sacha: J'aime bien ta configuration. Ta façon d'utiliser des variables pour simplifier ta configuration d'org-capture est une bonne idée. Peux-tu rendre cette section de ta configuration pour nous la montrer?

Abdallah: Oui, bien sûr. Je vais juste partager mon écran.

Sacha: J'avais piqué l'idée. C'est plus simple que mienne.

Prot: Voler à volonté.

Abdallah: Oui, bien sûr. C'est disponible dans GitHub, donc je pense que c'est... Ok, donc là, j'ai essayé de créer une configuration spécifique pour cette démo, juste pour montrer les différents trucs d'Org Mode et tout. Donc, on va aller vers la configuration des Org Capture, c'est ça ?

Sacha: Oui, org-capture-templates. Oui, oui. Oui, oui, oui, j'adore. J'adore l'utilisation de variables pour simplifier la configuration.

Abdallah: Oui, ce qui me plaĂźt dans cette configuration, c'est que la partie oĂč je dĂ©finis le template, On dirait que c'est la note parce qu'il y a le titre et puis il y a des sauts de ligne et la concatĂ©nation ici, elle fait comme si c'est une note rĂ©elle. Et puis, la partie ici, ce n'est pas assez chargĂ©e parce que lĂ , je dĂ©finis juste la variable. Donc, je peux voir toutes mes captures dans une seule page. Je n'ai pas besoin de... Faire un scroll tout au long du fichier pour voir toutes les captures que j'ai dĂ©finies.

Sacha: Ta variable heiwiper/org-jira-task est définie dans un autre fichier, je pense?

Abdallah: Non, j'ai transfĂ©rĂ© toutes les parties intĂ©ressantes dans un seul fichier juste pour la dĂ©monstration. Donc, vous pouvez l'avoir ici. Donc, cette template, elle me permet de crĂ©er une tĂąche et aussi associer le lien Jira pour cette tĂąche parce qu'on utilise Jira au travail. Donc, je vais vous montrer un exemple. Par exemple, ici, je vais... Bon, j'ai exĂ©cutĂ© l'org-capture. Vous voyez ici, il y a plusieurs captures, ce qu'on a dĂ©fini ici. Et lĂ , il y a Jira Task. Donc, je vais cliquer le J. Et bon, vous pouvez ignorer ceci. On va voir le prompt. Donc ici, ce que je fais, c'est que je prends la tĂąche Jira que j'ai sur email ou bien sur le navigateur. Je copie le lien. Par exemple, on va faire un exemple. https://jira.com/... Et lĂ , d'habitude, les tĂąches Jira, c'est quelque chose comme ça. On va dire BG comme si c'Ă©tait un bug ou... et une rĂ©fĂ©rence. On va dire 1, 2, 3, 4. Et puis, je vais cliquer entrer. On suppose que celui-ci est le lien de la tĂąche. Et lĂ , je vais copier le titre de la tĂąche. LĂ , je vais juste Ă©crire quelque chose. Et puis lĂ , j'ai quelques tags. Ça va me servir plus tard pour organiser les tĂąches. Donc j'ai Bugfix, Feature, Refactor, Enhancement. Je vais sĂ©lectionner Bugfix. Et lĂ , vous pouvez voir la tĂąche finale. Il y a le lien de la tĂąche. On peut cliquer sur C-c C-o pour accĂ©der directement Ă  la tĂąche. Il y a aussi la description. Et ce que j'aime dans ce workflow, c'est que je peux Ă©crire Des choses ici Ă  propos de cette tĂąche. Par exemple, des fois, j'Ă©cris des commandes de shell pour exĂ©cuter un appel d'API ou quelque chose comme ça. Je vais Ă©crire tout ici. Par exemple, curl. Je ne sais pas. Donc, c'est ça. Et puis, on clique sur C-c C-c pour sauvegarder. Et Ă  ce point, j'ai sauvegardĂ© la tĂąche. Je vais vous montrer l'agenda que j'ai spĂ©cifique pour les tĂąches. Vous pouvez voir ici Tasks by Type et Tasks by Service. On va dire Tasks by Type. Voici la tĂąche que j'ai dĂ©finie. C'est bien catĂ©gorisĂ© sous la section Bug fix. Et vous pouvez aussi l'avoir ici parce que lĂ  j'aime dĂ©placer les tĂąches vers des fichiers spĂ©cifiques donc parfois je dĂ©place les tĂąches ici pour avoir des fichiers un peu organisĂ©s.

Sacha: Tu as un fichier trÚs organisé, je pense, parce que tu as aussi des comptes de tùches dans ta mode-line.

Abdallah: Ah oui. Oui, en fait, Ă  propos de ce compteur, c'est quelque chose que je suis en train d'expĂ©rimenter avec. Donc, j'utilise une mĂ©thode para, je ne sais pas si vous avez entendu parler, pour organiser un peu mes notes et mes tĂąches. Et j'aime savoir combien de notes j'ai sur le fichier inbox. Parce que la plupart du temps, les tĂąches, je les dĂ©place vers des autres fichiers parce que j'utilise l'application Orgzly. Donc, les tĂąches ne restent pas gĂ©nĂ©ralement dans ce fichier inbox. C'est plutĂŽt les notes. Bon, je peux vous montrer le fichier ici, c'est juste un exemple. Mais c'est plutĂŽt comme ça. Il y a des notes et des tĂąches. VoilĂ . Plus la tĂąche qu'on a créée maintenant. Et pour ĂȘtre honnĂȘte avec vous, j'ai jamais atteint le nombre zĂ©ro. D'habitude c'est 10, 20, mais ça n'augmente pas trop, parce que, gĂ©nĂ©ralement, c'est juste des notes. Ils sont disponibles au cas oĂč je veux chercher les notes. Je me sens pas la pression de les dĂ©placer. C'est just quand j'ai assez du temps J'ai le compteur devant moi et c'est bon, je vais devoir dĂ©placer quelques notes puisque j'ai un peu de temps.

Sacha: De mon cÎté, je pense que ma boßte de réception a grandi constamment, sans cesse. Est-ce que tu as déjà ton bullet journal?

Abdallah: Oui, je l'ai utilisĂ© il y a deux ans. J'ai aussi Ă©crit un blog post Ă  ce propos. Pour ĂȘtre honnĂȘte, c'Ă©tait une expĂ©rience, mais je pense que ce n'est pas pour moi parce que j'ai arrĂȘtĂ© aprĂšs un an car je pense que j'ai besoin des notifications. Ce que je faisais, en fait, c'est que je pouvais garder la routine de voir mon journal chaque jour. Mais le problĂšme, c'est que parfois, j'ai besoin de me rappeler que je dois faire quelque chose, par exemple, 12h30. Et je ne peux pas garder les tĂąches sur ma tĂȘte. Donc ce que je faisais, c'est chaque matin, je vois les tĂąches sur le bullet journal et je le dĂ©place. Je les Ă©cris dans mon tĂ©lĂ©phone, in to do list. Mais c'est un peu pourquoi ne pas utiliser le tĂ©lĂ©phone.

Prot: C'est double de travail.

Abdallah: Oui, double de travail. J'ai essayé de l'intégrer avec Emacs en utilisant Org Journal, mais Le problÚme, c'est que j'utilise pas beaucoup l'ordinateur, donc c'est plutÎt le téléphone. Donc la méthode Bullet Journal, c'était pas pour moi. Donc j'utilise maintenant Org Mode avec Orgzly et ça marche trÚs bien.

Sacha: Est-ce que tu as aussi de l'agenda d'Orgzly Revived ou juste pour saisir les tĂąches?

Abdallah: J'ai pas bien compris.

Sacha: Orgzly a aussi des agendas. Maintenant, ton agenda sur ton ordinateur est bien organisé. Il a beaucoup de sections. Mais sur ton téléphone, tu utilises aussi Orgzly pour voir ton agenda?

Abdallah: En fait, mon agenda personnel est celui par dĂ©faut du Org Mode parce que ma vie personnelle est assez simple. Donc j'utilise l'agenda par dĂ©faut. L'agenda que je vous ai montrĂ©, c'est pour le travail seulement. Et pour le travail, je ne garde pas les notes dans Oxley, c'est juste dans l'ordinateur de travail. Donc dans mon cas, je pense que Orgzly, il fait le travail. Donc j'utilise le mĂȘme agenda, c'est pour ça.

Prot: Et c'est plus facile comme ça, d'avoir l'agenda seulement à l'ordinateur. L'agenda plus organisé.

Abdallah: Oui, l'agenda de travail, c'est plus efficace.

Sacha: L'écran est plus grand.

Abdallah: Oui, et aussi pour les tùches en relation avec le travail, je n'ai pas besoin de mon téléphone pour les accéder. Quand je suis au travail, je vais voir sur mon PC, sinon ce n'est pas la peine.

Sacha: Utiliser Emacs pour d'autres intĂ©rĂȘts comme l'apprentissage d'une langue Ă©trangĂšre ou...

Abdallah: En fait, j'ai passĂ© par la phase de ce qu'on appelle Rabbit Hole, Emacs. Donc, je peux vous dire que j'utilisais Emacs pour plein de choses. J'ai mĂȘme utilisĂ© EXWM, le Window Manager. Oui. J'ai aussi utilisĂ© l'autre, je ne me rappelle pas comment il s'appelait. C'Ă©tait un Window Manager en Common Lisp, pas en Emacs Lisp. Je ne me rappelle pas de son nom, mais il est assez connu dans la communautĂ© Lisp. Donc oui, j'utilisais plein d'autres trucs, mais pas pour l'apprentissage de langue. Mais avec le temps, j'essaie d'ĂȘtre plus pragmatique. Donc j'utilise les outils adaptĂ©s quand je trouve qu'il y a des difficultĂ©s ou bien il y a des problĂšmes. Par exemple, je vais vous donner un exemple rĂ©el. Je voulais utiliser Emacs pour envoyer des requĂȘtes quand je fais du programmation pour tester des API et tout. Parce que c'est trĂšs efficace pour mon workflow. Je vais vous dire un exemple plus tard. Mais parfois, je rencontre des problĂšmes oĂč il y a certains types de requĂȘtes oĂč ils ne sont pas supportĂ©s par le package qu'offre Emacs. Donc je me trouve avoir switchĂ© Ă  un logiciel qui est adaptĂ© pour ce truc. Et donc dans ce cas, j'utilise plutĂŽt les logiciels adaptĂ©s. Et pour l'exemple que je voulais mentionner, c'est que j'utilisais des macros pour envoyer des requĂȘtes Ă  travers Org Mode. Et puis je rĂ©cupĂšre des donnĂ©es de cette requĂȘte et pour exĂ©cuter une autre requĂȘte, juste pour simuler un workflow d'un utilisateur rĂ©el. C'Ă©tait trĂšs utile parce que ça m'Ă©vite de cliquer plusieurs boutons dans plusieurs logiciels et j'exĂ©cute juste le macro Emacs et il fait tout et je vais juste voir le rĂ©sultat.

Prot: C'est mieux comme ça.

Abdallah: Oui, c'est bien mieux. Plus efficace. Oui, bien sûr.

Sacha: Et moins d'erreurs. Je déteste le clic qui est nécessaire pour le flux de travail dans d'autres applications.

01: Oui.

Sacha: Ton flux de travail. J'aime les petites conditions que tu utilises dans ta configuration, comme helm-chart-template-p dans magic-mode-alist ou org-refile-target-verify-functions pour filtrer les tùches et ton-before-save Hook qui reformat le tampon selon le mode majeur. Dans ta configuration, quelles sont tes modifications préférées?

Abdallah: En fait, j'ai du mal à répondre à cette question parce que c'est tout le fichier qui est configuré et ça fonctionne un peu interconnecté. Donc, je ne peux pas vraiment choisir une partie préférée. Mais je vais dire les modifications qui ont relation avec Org Mode, c'est ce que j'aime trop parce que ça me facilite un peu le flux de travail avec Org Mode.

25:10 Compter les items dans l'inbox

Abdallah: Par exemple, celui-ci oĂč je compte les items que j'ai dans l'inbox.

Sacha: Parce que nos auditeurs peuvent rater les petites icĂŽnes, les petites affiches dans ta mode-line avec les boĂźtes.

Abdallah: Ah oui, je ne sais pas s'il y a une méthode pour l'afficher un peu plus grand, mais c'est juste là, à cÎté du Org Agenda. Je vais changer ici pour montrer que ça compte ces lignes.

Sacha: C'est une bonne idée. Si tu accumules beaucoup de tùches, les chiffres dans ta mode line te harcÚlent.

Abdallah: Oui, bien sĂ»r. C'est toujours lĂ  pour me rappeler en cas oĂč j'oublie de... En fait, mon Emacs actuel, il contient, je pense, environ 160. Mais attention, c'est juste parce que je viens de migrer mes notes depuis un autre logiciel. AprĂšs avoir migrĂ© vers Org Mode, j'ai utilisĂ© AnyType pour les notes. Donc je viens juste de l'Ă©migrer la semaine passĂ©e et j'ai pas eu assez de temps pour les dĂ©placer. Donc lĂ  je sais que je dois les dĂ©placer parce que sans eux c'est trop.

Sacha: Le problĂšme de tout le monde.

Abdallah: Oui bien sûr.

Sacha: J'aime aussi ta fonction pour récupérer des données, app-core-get-device-ids-by-username. Il y a aussi une autre fonction app-core-delete-items-by-store-id qui semble utile pour ton travail. Tu utilises Emacs pour automatiser des fonctions et tu utilises le shell script.

27:36 Emacs Lisp et d'autres langages

Sacha: Dans quelle situation est-ce que tu préfÚres écrire ce genre de fonction dans Emacs Lisp plutÎt que Shell ou un autre langage?

Abdallah: En fait, j'aime bien le langage Lisp en gĂ©nĂ©ral. J'aime bien Ă©crire en Lisp malgrĂ© que je n'ai pas assez de code base Ă©crit en Lisp. J'ai un peu contribuĂ© Ă , enfin, 1 ou 2 Repos. Mais pour ces fonctions-lĂ , je les gĂ©nĂ©rais avec l'intelligence artificielle parce que ça me facilite des tĂąches un peu rĂ©pĂ©titives, trop rĂ©pĂ©titives. Et pour optimiser un peu le temps et aussi pour pouvoir les appeler Ă  travers Emacs. C'est trĂšs utile parce que juste en cliquant un bouton, je peux savoir l'information que je vais utiliser. Et je pense que c'est mieux comparĂ© Ă  un shell script oĂč je dois savoir oĂč est le shell script et aller l'exĂ©cuter et un peu ouvrir un terminal. Donc lĂ , j'utilise Emacs pour programmer. Je peux appeler cette commande pour extraire une information. Il y a aussi le fait qu'il y a des prompts. Dans Emacs, donc je sais exactement ce que je dois fournir. Et ça me facilite trop le travail, surtout les tĂąches rĂ©pĂ©titives, parce que si ça ne rĂ©pĂšte pas, je ne vais pas Ă©crire une fonction. Et puisque je suis intĂ©ressĂ© juste aux rĂ©sultats, donc je les gĂ©nĂšre en utilisant l'intelligence artificielle, puisque ça me donne un bon rĂ©sultat. Je ne m'intĂ©resse pas Ă  ce que ça soit optimisĂ© ou non.

29:32 L'intelligence artificiale

Sacha: Je me demande que tu utilises quelques interfaces pour l'IA dans Emacs comme aidemacs. Ah oui, oui. Qu'est-ce que tu penses?

Abdallah: En fait, je ne sais pas si tu as remarquĂ© dans ma config, il y a pas mal de packages d'IA. J'ai dĂ» utiliser la plupart pour les comparer. Parce qu'en fait, la partie plus difficile pour moi, c'est d'observer les changements qui sont faits par l'IA. Donc, j'ai utilisĂ© tous les packages d'Emacs pour les comparer. Je pense que le meilleur package que j'ai utilisĂ© Ă©tait eca. Je ne sais pas si vous ĂȘtes au courant. Mais actuellement, j'utilise OpenCode dans Emacs. Donc, j'ouvre un terminal dans Emacs et j'utilise OpenCode. Ça me permet de mieux observer les changements et tout. Et parfois j'utilise gptel pour des petites questions sur Emacs sans avoir Ă  ouvrir le terminal. Donc les packages que vous voyez dans ma config, c'est juste pour tester les diffĂ©rentes intĂ©grations.

30:56 Les terminals

Prot: Et quel est le terminal de ton choix ?

Abdallah: Actuellement, j'ai migrĂ© vers Ghostel. Ça fait un mois, je pense. Mon problĂšme, c'Ă©tait Vterm, c'Ă©tait trĂšs bien. Il a rĂ©solu tous mes problĂšmes. Mais il restait un seul problĂšme, c'est les applications TUI. Ça ne marche pas bien sur Vterm. Donc, j'ai dĂ» migrer vers Ghostel. Et lĂ , ça marche trĂšs bien. Je peux vous montrer, par exemple, je vais ouvrir un exemple. Vim, par exemple. Bon, je ne sais pas si j'ai... Oui, mais je sais bien comment quitter, mais je ne vais pas ouvrir Vim. Bon, je ne sais pas si ça marche dans cette configuration, puisque c'est une nouvelle configuration. Peut-ĂȘtre je vais partager ma configuration originale pour juste vous montrer la diffĂ©rence.

Sacha: Je pense que tu as partagĂ© seulement la fenĂȘtre d'Emacs.

Abdallah: Oui, je vais changer vers l'autre fenĂȘtre et je vais mettre sur que je ne partage pas quelque chose de sensible. Merci. D'accord, donc lĂ ... Est-ce que vous voyez la nouvelle fenĂȘtre avec un... Oui. Parfait. D'accord, donc v-term... Ça doit compiler, d'accord. Je pense que j'ai lancĂ© une mise Ă  jour. Donc si je vais ouvrir Open Code ici. Je ne sais pas si ça va marcher bien. Ah oui, vous pouvez voir il y a un peu de dĂ©calage. Il y a ici le prompt. LĂ , c'est un peu bizarre. Oui, je vais ouvrir une autre fenĂȘtre Ghostel juste pour vous donner une idĂ©e. Donc cette fenĂȘtre est Ghostel, je vais ouvrir la mĂȘme application. Donc lĂ , vous remarquez dĂ©jĂ  les couleurs, ils sont mieux que la session Vterm. Et oui, je pense que c'est clair que l'intĂ©gration est un peu mieux dans Ghostel. C'est trĂšs intĂ©ressant. Oui, parce que j'utilise pas mal de terminal TUI dans le travail, donc j'ai toujours une terminal ouvert, mais depuis que j'ai installĂ© Ghostel, ce n'est plus le cas parce que je n'ai plus besoin d'une terminal pour ouvrir ces applications.

Sacha: Emacs assimile une autre fonction.

Abdallah: Oui.

34:14 Les jeux et les amusements

Sacha: Ça m'amuse quand je vois les jeux dans ta configuration et aussi la prĂ©sence de Nyan Cat dans ta moodline. Quel est ton jeu prĂ©fĂ©rĂ© dans Emacs?

Abdallah: En fait, mon jeu prĂ©fĂ©rĂ© c'est Tetris. Le jeu du Emacs, oui. Celui qui est built-in. Mais j'ai dĂ©couvert un truc, je ne sais pas si vous ĂȘtes au courant, mais le package Emacs de Fedora, il ne contient pas le jeu Tetris.

Prot: Oh non ! Seulement Tetris ? Les autres jeux ?

Abdallah: Oui, seulement Tetris. Parce qu'apparemment, il y a des trucs de copyright, je pense. Parce que j'ai cherché et j'ai trouvé que Ils l'ont enlevé parce qu'il y avait des trucs de droits. Mais les autres jeux que j'ai dans ma configuration, c'était juste pour les tester. Parfois, je lance Tetris, mais c'est rare.

Sacha: Le bricolage d'Emacs est un jeu aussi.

Abdallah: Oui, bien sûr. Et pour le Nyan Cat, je pense que ça fait un changement. Par exemple, quand tu es en train de travailler sur quelque chose, tu prends assez d'efforts et tout, et tu vois le Nyan Cat courir dans le Mood Line, ça te fait un peu du plaisir. Et je me rappelle que je dois relaxer. Oui.

36:07 La communauté

Sacha: Tu as des collĂšgues qui utilisent Emacs aussi ? Ou tous les collĂšgues pensent que tu es fou ?

Abdallah: Oui, je pense que c'est le deuxiĂšme cas. La communautĂ© Emacs dans l'AlgĂ©rie, ce n'est pas grande. J'ai seulement rencontrĂ© une seule personne dans ma vie qui utilise Emacs. Je n'ai mĂȘme pas son contact, donc je l'ai juste rencontrĂ© dans une formation, je pense. Parce que je l'ai vu utiliser Magit. Je pense qu'il y a deux collĂšgues qui utilisent Neovim. Je ne sais pas s'ils sont en train de nous regarder parce que j'ai partagĂ© le lien avec eux. Donc s'ils sont en train de nous regarder, je leur passe le bonjour. Et je pense que les autres utilisent VS Code. Donc seulement ces deux utilisent Neovim et les autres VS Code. C'est tout.

Prot: Je crois que c'est le mĂȘme partout.

Abdallah: Ah bon? Je pensais que c'est un peu diffĂ©rent en Europe ou bien en AmĂ©rique. Parce qu'il y a, quand je lis le feed de Sacha, je vois toujours des meets. Des Ă©vĂ©nements organisĂ©s, oui. Et Ă  chaque fois que je vois ces Ă©vĂ©nements, je me dis peut-ĂȘtre il y aura un Ă©vĂ©nement en Afrique ou bien en Afrique du Nord, mais il n'y a jamais un Ă©vĂ©nement dans ces endroits. Je pense que c'est juste en Europe ou bien en Asie ou en AmĂ©rique.

Sacha: Les événements sont ouverts à tous, donc... Je pense que je vais à l'Emacs Berlin en ligne de temps en temps. Tu veux aussi le rejoindre?

Abdallah: Bon, rejoindre un événement dans l'Europe, c'est un peu compliqué. Donc, je peux seulement rejoindre virtuellement?

Sacha: Oui, bien sûr.

Abdallah: Mais je parlais des événements présentiels. Il n'y a pas d'événement présentiel dans ces régions. C'est juste en Europe.

Sacha: Je ne peux pas aller à l'événement présentiel.

Abdallah: Ah d'accord.

Sacha: Donc je me focalise aussi sur les événements virtuels.

Abdallah: Ah d'accord. Donc c'est le cas pour tous. Je pense que Prot aussi.

Prot: Je ne [connais] personne ici. MĂȘme que Vim.

Abdallah: Bon, mais je pense que... Est-ce que tu as des contacts avec des gens qui ont un peu le... Un peu de connaissances techniques dans la rĂ©gion oĂč tu vis, parce que j'ai vu que c'est un peu Ă©loignĂ© de la ville et tout.

Prot: Oui, non, non. Ici, les gens ne sont pas techniques. Avec les ordinateurs. Ils sont plus techniques avec les autres choses. Par exemple, l'agriculture.

Abdallah: Ah oui, oui, bien sûr. Oui, donc... Ton cas est pire que moi, parce que moi je rencontre des gens, mais ils n'utilisent pas Emacs.

Sacha: Heureusement, la communauté Emacs est trÚs bien en ligne, donc je ne me sens pas isolée.

Abdallah: Oui, oui, évidemment. Je pense que si la communauté en Reddit, je trouve que c'est rare qu'il y a une communauté qui est assez, comment dire? Donc tous les gens sont bien, ils parlent d'une bonne façon, ils te donnent des conseils. Il y a rarement ces commentaires toxiques qu'on trouve généralement sur Reddit. Donc c'est ce que j'aime dans la communauté Emacs. Et tout le monde, ils sont bien.

Prot: Est-ce que tous les gens veulent t'aider? Parce que tous les gens comprennent que Emacs est trĂšs difficile.

Abdallah: Oui, c'est le cas, oui. Je peux imaginer. Parce que j'ai partagĂ© un post il y a quelques jours sur Magit et il y a plein de gens qui me donnaient des astuces parce que j'ai demandĂ© s'il y avait des astuces supplĂ©mentaires. Ils m'ont donnĂ© des astuces. Il y avait mĂȘme des paragraphes trĂšs longs. C'Ă©tait trĂšs sympa, oui.

41:44 Accent

Sacha: En lisant ta configuration, j'ai aussi des questions sur le paquet accent pour saisir. Utilises-tu, au lieu de méthode de saisie ou input method sur Emacs ou sur ton systÚme, qu'est-ce qui te plaßt dedans?

Abdallah: En fait, je n'utilise pas le français souvent. Donc, et mon clavier est anglais. Donc, quand je veux écrire quelque chose en français, je vais juste écrire en anglais et puis ajouter des accents. Par exemple, je vais dire, je ne sais pas, j'étais dans un live stream et je vais aller ici, je vais faire Accent Menu. Je vais juste changer, sans devoir changer le clavier parce que je ne me rappelle pas l'emplacement des lettres accentuées dans le clavier. Donc je veux juste le faire dans Emacs et je le copie dans un autre navigateur ou bien quelque chose comme ça. J'ai seulement le clavier anglais et le clavier arabe parce que c'est totalement une différente méthode d'écrit. Est-ce que vous utilisez ce package puisque vous apprenez le français?

Prot: Moi non, moi j'utilise l'input method.

Abdallah: Ah d'accord. C'est quelque chose intégré dans Emacs j'imagine, parce que je pense que j'ai entendu parler. Oui, c'est exactement comme ça.

Sacha: Il y a peut-ĂȘtre une vidĂ©o rĂ©cente sur les mĂ©thodes d'input, non?

Abdallah: Je pense que c'est une fonctionnalité intégrée dans Emacs. C'est vrai? Parce que je me rappelle que j'ai une fois, je l'ai activé par accident.

Sacha: C-\.

Abdallah: Oui et je pouvais écrire en arabe dans Emacs. Et je me demandais pourquoi parce que le clavier était en anglais. C'était un peu... Mais c'est une fonctionnalité trÚs intéressante. Je vais voir plus tard.

Sacha: Je pense que tu as répondu à toutes mes questions que j'ai préparées sur ta configuration, donc la conversation est libre. J'ai une question.

44:45 Tes prochaines étapes

Sacha: Quelles sont tes prochaines étapes pour apprendre Emacs? Qu'est-ce qui t'intrigue?

Abdallah: Actuellement, je veux apprendre, un peu me familiariser avec les macros parce que je les ai utilisés précédemment, comme je vous ai dit dans l'exemple. Mais je ne suis pas assez familier et je veux l'intégrer plus dans mon flux de travail pour un peu automatiser des trucs. Et je veux aussi... Je veux aussi apprendre Emacs Lisp. En fait, je connais le langage Lisp, donc c'est un peu facile, mais je trouve qu'il y a des fonctions trÚs spécifiques d'Emacs qu'il faut apprendre pour maßtriser Emacs Lisp. Oui, c'est trÚs intéressant, surtout pour pouvoir contribuer dans la communauté Emacs. J'ai voulu contribuer il y a quelques mois sur un package Emacs, mais j'ai trouvé qu'il fallait faire un peu [de] paperasse, paperwork parce que c'est en relation avec GNU. Et oui, donc c'était un peu difficile. Il fallait demander à mon employeur de signer quelques trucs et tout. Et il pense que je vais travailler ou quelque chose comme ça. Oui, donc c'était un peu difficile, mais je veux bien apprendre Emacs Lisp pour pouvoir contribuer et aussi pour faciliter des flux de travail personnels.

Sacha: Si tu veux aller contribuer à NonGNU ELPA, je pense que la paperasse n'est pas nécessaire.

Abdallah: Je pense que le package que j'allais contribuer Ă  Ă©tait sous la licence Emacs. Il s'appelle dape. C'est un debugger pour eglot. Il s'intĂšgre avec eglot. Je pense que c'est une exigence dans le package lui-mĂȘme.

Prot: C'est pour tous les paquets dans le répertoire GNU ELPA.

Abdallah: C'est le cas. Mais je pensais que c'était juste pour les packages qui sont intégrés dans Emacs. Parce que ce package, il était disponible sur GitHub, mais le maintainer, il exige que le code soit sous licence de GNU.

47:39 Org Mode et des tableaux

Abdallah: Aussi, j'aimerais bien manipuler Org Mode De plusieurs façons. Par exemple, rĂ©cemment, j'ai un peu testĂ© les tableaux. J'ai utilisĂ© les tableaux pour essayer de gĂ©rer mes dĂ©penses personnelles. Oui, c'Ă©tait trĂšs intĂ©ressant. Parce que des fois, il y a des fonctionnalitĂ©s dans Org Mode, mĂȘme dans Emacs, oĂč on ne sait pas vraiment ce qu'on va avoir comme fonctionnalitĂ©s. Si on ne va pas vers le manuel, donc si tu ne lis pas le manuel, tu ne sais pas que cette fonctionnalitĂ© existe. Par exemple, les tableaux, si on ne va pas dans le manuel et on voit que les tableaux, il permet de faire des calculs et automatiser des formules, ça ne serait pas Ă©vident de savoir cette information parce que le format Markdown, il n'offre pas cette fonctionnalitĂ©. Je peux, je pense, vous montrer un exemple. Je pense que je l'ai quelque part sur cette dĂ©monstration. LĂ , je vais dĂ©montrer comment je cherche une note. Donc, je vais chercher « budget ». J'espĂšre que je vais le trouver. D'accord, donc « monthly budget ». LĂ , j'ai changĂ© les valeurs pour la vidĂ©o. Mais vous pouvez voir par exemple lĂ , j'ai des catĂ©gories de dĂ©penses. J'ai par exemple le salaire ici et le salaire du mois. Par exemple, en cas oĂč dans un certain mois, j'ai plus ou moins de ce chiffre. Et lĂ , j'ai le nombre que je peux associer Ă  ces catĂ©gories. Et ça diminue Ă  chaque fois que je... J'associe un numĂ©ro ici. Par exemple, lĂ , si je vais changer celle-ci en 4 et je vais faire un calcul, ça change toutes les valeurs ici pour savoir combien il nous reste pour la catĂ©gorie Savings. Si je vais augmenter, ça va aussi changer, je pense. Oui, vous remarquez que ça va changer ici. Donc, je peux associer parce qu'en fait, j'utilise la mĂ©thode Budget... Non.

Sacha: You Need A Budget.

Abdallah: Enveloppe. Enveloppe, budget. Oui, oui, oui.

Sacha: Moi aussi, moi aussi.

Abdallah: Ah, d'accord.

Sacha: Je connais le livre.

Abdallah: J'utilise cette méthode et chaque mois, je vois ce tableau et je commence à diminuer pour chaque catégorie et je mets l'argent dans chaque enveloppe pour m'organiser un peu. Et cela me permet de...

50:40 Ledger, Hledger, Gnucash

Sacha: Dans Emacs, j'utilise Ledger pour faire ça. J'utilise aussi le systÚme d'enveloppe virtuelle pour gérer mes dépenses, mes économies. J'adore le systÚme d'enveloppe virtuelle. Tu connais Ledger?

Abdallah: Ledger, oui. Ledger, le mode sur Emacs.

Sacha: Et aussi un outil, un outil de lien de commande.

Abdallah: Oui, oui, bien sûr. Je pense que c'était écrit par John Wiegley.

01: Oui, oui.

Abdallah: Je l'ai utilisĂ© Ă  un certain moment, j'ai oubliĂ© de mentionner. Donc, oui, je l'ai utilisĂ© pendant, je pense, quelques annĂ©es. C'Ă©tait trĂšs intĂ©ressant. Mais mon problĂšme, c'Ă©tait la visualisation. Il y avait une intĂ©gration avec gnuplot, si je me rappelle bien. Mais c'Ă©tait un peu compliquĂ© Ă  configurer et j'avais besoin de quelque chose de simple. Donc j'ai arrĂȘtĂ© d'utiliser Ledger et j'utilise maintenant Gnucash.

Sacha: Je [connais] aussi hledger qui a beaucoup de visualisations sur l'interface web. Donc si tu veux des visualisations...

Abdallah: Il s'appelle comment?

Sacha: Hledger.

Abdallah: hledger, ok. Je vais noter.

Prot: Avec le h.

Sacha: Le format des données est assez pareil, mais il y a une interface web pour explorer des données.

Abdallah: Ah d'accord, je vois. C'est intéressant, je vais voir plus tard. Est-ce que vous avez utilisé Gnucash ?

Sacha: [J'ai utilisé Gnucash] il y a beaucoup d'années pour gérer les finances de mon entreprise, mais je préfÚre ledger parce que c'est dans Emacs.

02: Oui, oui.

Sacha: Donc, j'ai fait justement un fichier Org avec tous mes reports. Et une fois que j'ai fait les reports, je les laisse. Je les laisse pour juste reexecuter.

Abdallah: Est-ce que vous avez documenté votre méthode de gérer cela ?

Sacha: Oui, j'ai une blog post de mon systĂšme d'enveloppe virtuelle avec Ledger. Je vais t'envoyer le lien.

Abdallah: Oui, ce serait intéressant de voir.

Sacha: Tu peux utiliser ta transaction virtuelle et tu peux définir les rÚgles pour ajouter ou [soustraire] des comptes [correspondants] pour chaque transaction.

Abdallah: En fait, je faisais exactement ça. C'est de créer des comptes virtuels et puis l'utiliser pour faire des budgets comme enveloppe budgeting. Oui, j'ai fait exactement ça. Je pense que je l'ai trouvé quelque part dans Reddit.

Sacha: Donc si j'importe les données de ma banque, presque toutes les transactions sont catégorisées automatiquement.

Abdallah: Ah d'accord, c'est trÚs bien. J'ai toujours voulu avoir cette intégration mais les banques ici n'offrent pas des fonctionnalités similaires.

Sacha: J'ai téléchargé chaque [CSV] manuellement.

Abdallah: Ah, le fichier CSV?

Sacha: Oui, le fichier CSV.

Abdallah: Ah d'accord, donc vous allez manipuler le fichier CSV pour le convertir. D'accord, je vois, je vois.

Prot: Et c'est difficile de le convertir?

Sacha: Non, non, j'ai créé une fonction pour utiliser la bibliothÚque pcsv pour [extraire] les données [du] fichier CSV et aprÚs ça traduire vers le format Ledger.

Abdallah: C'est trĂšs intĂ©ressant. En fait, ça aurait Ă©tĂ© trĂšs utile dans mon cas parce que je le fais manuellement actuellement. Bon, je n'ai pas tellement de transactions sur mon compte puisque je gĂšre mon compte personnel. Mais quand mĂȘme, ça prend, je dirais, deux heures par mois. Juste pour synchroniser un peu. Les deux comptes. Le compte de Gnucash et celui de banque.

Sacha: Apparemment, une bonne excuse pour automatiser avec Emacs.

Abdallah: Oui, peut-ĂȘtre. Mais je ne sais pas comment... Est-ce qu'il y a une façon d'importer les donnĂ©es dans Gnucash? Je pense qu'il y a une maniĂšre, mais je ne l'ai pas encore explorĂ©e. Et aussi, je me demande si... Comment vous... Vous dĂ©placez les dĂ©penses dans les catĂ©gories. Comment vous le programmez?

Sacha: Par le nom du payee.

Abdallah: Ah d'accord.

Sacha: Donc si j'achÚte les aliments au supermarché, le nom du payee est No Frills. J'assigne les catégories par le nom.

Abdallah: Oui, parce que c'est la partie un peu que je trouve un peu difficile à automatiser dans mon cas parce que je dois le faire manuellement et il n'y a pas de nom de payee. Oui, parce que le CSV que je peux exporter dans la banque, il ne contient qu'un numéro que je ne sais pas le numéro de compte, je pense. Oui, les banques ici ne sont pas assez développées. C'est pour ça. Tant que tu peux extraire ton argent et que tu peux faire des transactions en ligne, tu es super.

Sacha: Je pense que je dois rĂ©veiller ma fille pour notre grande aventure au parc d'attractions. Donc, un grand merci Ă  tous les deux pour notre conversation et [ta] patience avec nos erreurs techniques. À la prochaine.

Abdallah: Ça m'a fait un grand plaisir de converser avec vous. J'espùre que c'est demain pour vous et on se voit dans la prochaine.

Prot: À la prochaine à vous aussi et à bientît. Merci. À bientît. Au revoir.

View Org source for this post

You can e-mail me at sacha@sachachua.com.

-1:-- Emacs Chat 29: Abdallah Maouche (en français) (Post Sacha Chua)--L0--C0--2026-08-23T13:53:00.000Z

James Cherti: Emacs Spell Checker: Configuring Flyspell, Ispell, and Aspell to Minimize False Positives in Source Code and Prose

The ispell package serves as the underlying interface in Emacs for communicating with external spell checking programs. Building upon this, the flyspell package is a built-in minor mode that provides on-the-fly spell checking. It highlights misspelled words as you type and offers interactive corrections. This article presents a practical configuration for Emacs that sets up ispell to use Aspell for both programming and standard text editing.

Additionally, it addresses a common issue for developers. While writing code, variables such as filepath or buffername in comments or docstrings are often flagged as errors, generating visual noise in your programming buffers. This article contains a specific configuration to reduce these false positives.

Setting the backend and dictionary

The first step is to configure Emacs to use Aspell as the spell-checking engine, set the default dictionary, and reduce unnecessary messages when checking individual words:

;; Set the ispell program name to aspell
;; (switching to aspell will generally offer better performance than ispell.)
(setq ispell-program-name "aspell")

;; Set the global default dictionary for the Ispell process.
(setq ispell-dictionary "en_US")

;; Reduce unnecessary messages when checking individual words.
(setq ispell-quietly t)

Note that this configuration explicitly targets the US English dictionary "en_US". If you write in a different language or require a different regional dialect, such as "en_GB" or "fr_FR", replace "en_US" with your preferred language code.

Configuring aspell flags

The following configures Aspell's suggestion mode to "ultra", which favors very close spelling and phonetic matches when generating suggestions:

;; Configure Aspell's suggestion mode to "ultra", which favors very close
;; spelling and phonetic matches when generating suggestions.
(setq ispell-extra-args '("--sug-mode=ultra"))

Handling compound words in source code

Comments and string literals in source code are heavily populated with compound variable names and technical terms, such as filepath, buffername, or checkbox. Aspell may flag these as errors, generating visual noise in programming buffers.

To resolve this, we pass the --run-together flag to Aspell. This flag allows Aspell to recognize words formed by combining valid dictionary words without spaces. (It only affects words in the comments and strings that Flyspell checks; it does not cause Flyspell to check arbitrary identifiers elsewhere in the source buffer.)

To apply this cleanly, we can define a custom wrapper function that we will later attach to our programming modes using add-hook. This function activates flyspell-prog-mode and then safely appends the required flags buffer-locally using add-to-list, which inherently prevents duplicate arguments if the mode is toggled off and on:

(defun my-flyspell-prog-mode (&rest _args)
  "Enable `flyspell-prog-mode' with buffer-local Aspell arguments."
  ;; The --run-together flag instructs Aspell to accept words formed by
  ;; combining two or more valid dictionary words without spaces, treating the
  ;; resulting string as valid.
  ;;
  ;; This is excellent for source code. Code is heavily populated with
  ;; compound variable names and technical terms (e.g., filepath, buffername,
  ;; checkbox).
  ;; URL: https://www.jamescherti.com/emacs-spell-checker-flyspell-ispell-aspell/
  (make-local-variable 'ispell-extra-args)
  (dolist (item '("--run-together"
                  ;; "--ignore=2"
                  ;; "--run-together-min=3"
                  ;; "--run-together-limit=4"
                  ;; "--camel-case"
                  ))
    (add-to-list 'ispell-extra-args item))
  (flyspell-prog-mode))

Here are four additional flags worth uncommenting in the my-flyspell-prog-mode function above. Each provides specific advantages for programming workflows, along with some tradeoffs to consider:

  • --run-together-limit=4: Increases the maximum number of combined words allowed in a single string from the default limit of two up to 4. (A higher limit increases the probability that a genuine typo will be silently ignored because Aspell manages to break the misspelling down into a sequence of several shorter valid words. It also adds a slight processing overhead.)
  • --run-together-min=3 sets the minimum length of the individual words that Aspell is allowed to use when recognizing a run-together compound. For example, with a value of 3, Aspell can recognize filepath as file + path, but it is less likely to accept a compound that depends on very short components such as a or in. Increasing this value can therefore reduce false positives in source code by preventing Aspell from accepting suspicious compounds made up of short, common words.
  • --ignore=2: Configures Aspell to completely skip words containing two characters or fewer. Tradeoff: You lose spell checking for actual two-letter English words (such as "is", "an", "to", "of").
  • --camel-case: Enables Aspell's camel-case word handling, allowing camelCase identifiers to be checked more appropriately. It can reduce false positives for identifiers such as myHttpRequest that appear in docstrings and comments. Tradeoff: It can occasionally mask a typo if its components are individually recognized as valid words.

Initializing the Packages

Finally, we use add-hook to bind the spell checking functions to the appropriate Emacs hooks:

;; The flyspell package is a built-in Emacs minor mode that provides on-the-fly
;; spell checking. It highlights misspelled words as you type, offering
;; interactive corrections.
;; URL: https://www.jamescherti.com/emacs-spell-checker-flyspell-ispell-aspell/
(defun my-flyspell-enable-appropriate-mode ()
  "Enable the appropriate Flyspell mode based on the current major mode."
  (if (or (derived-mode-p 'conf-mode)
          (derived-mode-p 'yaml-mode)
          (derived-mode-p 'yaml-ts-mode)
          (derived-mode-p 'ansible-mode)
          (derived-mode-p 'nxml-mode)
          (derived-mode-p 'sgml-mode))
      (my-flyspell-prog-mode)
    (flyspell-mode 1)))

(add-hook 'prog-mode-hook #'my-flyspell-prog-mode)
(add-hook 'conf-mode-hook #'my-flyspell-enable-appropriate-mode)
(add-hook 'text-mode-hook #'my-flyspell-enable-appropriate-mode)

Note: Configuration files such as YAML and Ansible playbooks can present an edge case because some source-like modes are based on text-mode rather than prog-mode. Without an explicit override, such modes may receive ordinary Flyspell behavior rather than programming-mode behavior. Since these files behave more like source code, we use a custom dispatcher function to apply my-flyspell-prog-mode to them instead.

A few additional interesting options

Several lesser-known variables can be adjusted to change Flyspell's behavior and, in some cases, reduce the amount of work it performs. These options involve tradeoffs: reducing spell-checking activity can improve responsiveness, but may also reduce the amount of text that is checked or the feedback that Flyspell provides.

  • Setting flyspell-issue-message-flag and flyspell-issue-welcome-flag to nil suppresses Flyspell's checking messages and its startup welcome message. This removes minibuffer messages generated by Flyspell without changing the actual spell-checking operation.
  • Setting flyspell-check-changes to a non-nil value causes Flyspell to check only words that have been typed or edited, instead of also checking words that point moves across. This can reduce spell-checking activity when navigating through existing text. The tradeoff is that existing misspellings are not checked because point moves across them, so they may remain undetected until the text is edited or checked explicitly.
  • Setting flyspell-delay-use-timer to a non-nil value makes Flyspell use a timer instead of sit-for when waiting after delayed commands. This allows idle timers and other Emacs code to run during the delay. The option therefore changes how Flyspell waits before performing a check rather than disabling the delay itself.
  • Setting flyspell-mark-duplications-flag to nil prevents Flyspell from reporting repeated words as errors. This eliminates duplicate-word detection, so accidental repetitions such as "the the" are no longer reported.
  • Setting ispell-silently-savep to a non-nil value causes new words added to the personal dictionary to be saved without an interactive confirmation prompt. This avoids the prompt when saving personal-dictionary additions, but also removes that opportunity to review each addition before it is saved.
  • Setting flyspell-highlight-properties to nil prevents Flyspell from highlighting incorrect words when the relevant text already has text properties. The spelling check can still identify the word as incorrect, but Flyspell will not add its normal misspelling highlight when an applicable text property is present. This can be useful when existing text properties should take precedence over Flyspell's visual indication.
  • Lowering flyspell-large-region tells Flyspell to use its faster bulk-checking method for smaller sections of text. Instead of checking each word individually, Flyspell sends the larger section to the external Ispell/Aspell program to check it in one operation. For example, changing the value from 1000 to 200 makes Flyspell use this method for regions containing 200 or more characters. This can improve performance when checking larger sections, but repeated words such as "the the" are not detected when this bulk-checking method is used.
  • Increasing flyspell-delay increases the number of seconds Flyspell waits before checking after a command classified as delayed. This can reduce how frequently spell checking is triggered during rapid editing, at the cost of delaying the appearance of misspelling highlights.
-1:-- Emacs Spell Checker: Configuring Flyspell, Ispell, and Aspell to Minimize False Positives in Source Code and Prose (Post James Cherti)--L0--C0--2026-08-23T13:00:26.000Z

Matt Maguire: AI Agents and Local LLMs

In my last article on AI agents I talked about using Claude Code AI agent. This is an easy-to-set-up commercial solution that gives excellent results. The risk is that the pricing may not remain as affordable as it is today, and as a result I wanted to find an alternative solution that insulates me from changing commercials.

There are a number of open-weight models that one may download and run locally on your home computer. Some of the benefits are:

-1:-- AI Agents and Local LLMs (Post Matt Maguire)--L0--C0--2026-08-23T00:00:00.000Z

Monadic Sheep: An Introduction to Canvas in GNU Emacs

by Divya
2026 August 23

The canvas patch has been finally merged to upstream GNU Emacs. If you have been following my Mastodon, this was a journey that took us ~8 months. But finally we have, in core Emacs, a feature that I desperately desired in early 2025 when I was starting the Emacs Reader project: a way to update and manipulate images in Emacs without choking Emacs by putting the image data in strings. I went through a series of different hacks to overcome these limitations, some more successful than others, but thanks to the magnificent Daniel (aka minad) who suggested the idea of exposing a pixel buffer from within Emacs via the dynamic module API. That idea is now a feature in Emacs, and will be part of Emacs 32 release cycle. A more detailed history about this will be written later, for now this article is an introduction to the new feature, its API and what you can do using it. I will try to keep it as self-contained as I can, so that beginners are able to understand it. But first, we need to clarify certain misconceptions that people might have about this feature.

1. Frequently Asked Questions

  1. “Is this related to HTML5 Canvas?”

    No. It has nothing to do with any web framework or technology. We call it “canvas” because it allows the user to draw arbitrarily on to a surface in an Emacs buffer. So at a high-level, both the HTML5 and Emacs canvas do the same thing, but they have zero relation in their implementation or how they work. HTML5 Canvas also has a more rich API with drawing functions and such, right now Emacs’ canvas API integrates with the existing image API and just provides access to the underlying pixel buffer via dynamic module API. See Canvas via Dynamic Modules.

  2. “How is this different from SVG that Emacs has support for since a long time?”

    Good question! So, at the level of an Emacs buffer the canvas looks and behaves just like any other Emacs image, including SVGs. Where it differs from SVG and other images is:

    • a canvas’ :data is just ARGB32 pixel data (in strings or vectors), so no specific compressed format.
    • the ability to access an Emacs Lisp image object from a lower level and be able to manipulate it
    • update the image without having to call a full redisplay
  3. “Isn’t this still limited by Emacs’ redisplay engine?”

    Not entirely. We have a function called canvas-refresh that is to be called every time you wish to update the canvas, and this function doesn’t use/call Emacs’ redisplay, it has its own redrawing path that only updates the specific glyphs in the buffer that pertain to that canvas. Thus, whereas a full redisplay would cost more because it will try to update all the glyphs that changed, canvas-refresh only updates what needs to be. Since Emacs 25 we have double-buffering so if that’s enabled in the frame you’d still need to call redisplay to flip the buffers, but that won’t slow down your canvas. If you’re not convinced, see the demos below that run smooth at 25, 30, and 60FPS without causing any lag to the rest of the Emacs session. Or, look at this where I display a 2K60 FPS and a 720p 30FPS video side by side without Emacs missing a beat!

  4. “Is this hardware-accelerated? If not, why are you not using the GPU for this?”

    Because we simply don’t need it :D! A canvas simply exposes a pixel buffer, and Emacs simply redisplays it as it changes. You can get your pixels hardware accelerated outside of Emacs if you wish to, and then put them in canvas’ pixel buffer. This will give you hardware acceleration where you need it the most, pixel calculation, texture math, etc. Ideally, an introduction of a GPU powered redisplay within Emacs itself would be great and certainly help but one can still reap much of the benefits of HW acceleration from current Emacs via canvas.

2. What Is a Canvas?

Before answering that question, we should ask: “what is an image in GNU Emacs?” Everybody has seen Emacs display dazzling images since decades, and of varying formats. How does Emacs do that? Well, at the level of an Emacs buffer we have something called Text Properties (also see Overlay Properties), as its name suggests it is a property list (aka plist) for a character position or a string within a buffer. Among the many properties a text can have, there’s a special one called the display property. This special property is responsible for how the text gets displayed. This is exactly what we use to display images! You use create-image to create an image object, which is exactly a plist, and then you set this image object (called an image specification) to be the display property of some text or overlay (which are like text properties but without the "text", it’s an object that belongs to a particular buffer with specific beginning and end and along with properties just like for usual text).

This is briefly how the API of how images work in GNU Emacs. The new canvas feature works in full compatibility with this API. In short, at the level of an Emacs buffer, a canvas is just another image type! Indeed, compiling the latest GNU Emacs from source and evaluating: (image-type-available-p 'canvas) should return a t.

The immediate question then is: if Emacs already supported images, and if canvas just follows the same API, then what was the point of it? Why have a new image type, when we have XBM, PPM, PNG, GIF, SVG, etc.? Well, the two-fold short answer is:

  1. You need to pass image data via strings to create these objects and for most of the above formats it’s expensive to do so (except SVGs)
  2. Any arbitrary external image data cannot be directly displayed without throttling Emacs strings or objects, etc. (i.e., there’s no low-level access to the image data)

With SVGs you can mostly do really good things as long as they are simple enough, the moment you lead to have complex graphics at high refresh rate, Emacs starts throttling. I wanted in 2025 was exactly a way around this, a way to access the image Emacs is displaying at pixel-level so that I can update it in-place by fiddling with the pixels without having to pass strings or Emacs Lisp objects through the garbage collector. And this is what a canvas provides.

So, there are two levels at which a canvas can be viewed and used:

  1. purely from Emacs Lisp as an image object
  2. as an ARGB32 pixel buffer via dynamic module API

This allows for full integration with the existing Image API of Emacs while not sacrificing on low-level efficiency of being able to manipulate the pixel buffer directly. Now I will demonstrate how one can use the canvas in these two ways separately and/or simultaneously.

3. Creating and Using A Canvas

3.1. Canvas via Emacs Lisp Only

Like usual Emacs images, one can create images either by using create-image or manually building the image spec plist. For a canvas, the latter approach would look like this:

(setq test-canvas `(image :type canvas
                          :id test
                          :data-width 10
                          :data-height 10
                          :data nil))

This is what a canvas image’s specification looks like, the only difference between this and other images (other than the obvious :type canvas) is that canvas images need an :id property, this is used to uniquely identify each canvas.

If you used instead: (setq test-canvas (create-image nil 'canvas t)) to create the canvas, it would look almost the same:

(image :type canvas :data nil :scale default :id g138)

Since, of course, we didn’t provide any data in either of the cases, they are not technically valid canvas images, but that is how it looks like. To make things more interesting, remember that the "data" that a canvas accepts is ARGB32 pixel arrays. This can be done via Emacs Lisp through either unibyte strings or vectors. The latter is a bit easier to showcase, so I will go continue with that.

First, let’s create our array of pixels. We use make-vector:

;; A red square of 10x0 size
(setq rect-vec (make-vector (* 10 10) #xFFFF0000))

This creates a vector of 10x10 size containing each element as #xFFFF0000 (red) 32-bit ARGB pixel. It’s literally an pixel by pixel array vector that contains the color red. This is valid data for our canvas, we can now create it manually:

(setq rect-canvas `(image :type canvas
                          :id rect
                          :data-width 10
                          :data-height 10
                          :data ,rect-vec))

Now we can simply display this:

(insert (propertize "#" 'display rect-canvas))

After evaluating the above, you’ll see a tiny red square in your buffer! Voila, we now have a small canvas!

3.1.1. Note on Canvas Refresh

This might not seem too useful, because you can make a small rectangle via SVGs much quickly. The real value of canvas arises with the use of canvas-refresh function using which you can update the canvas without requiring a full redisplay. It only touches the glyphs which cover the particular canvas and updates them immediately. This is significantly cheaper than calling a full redisplay that will try to update all the changes.

Also, canvas-refresh has an additional optional argument RELOAD-DATA which if non-nil will reload any new updated data (either via the :data property or change in :file) from the canvas. It will be demonstrated later how to use this function properly.

3.2. Canvas via Dynamic Modules

As suggested in the introduction, canvases can be used either via Emacs Lisp and/or via dynamic modules. To know more about dynamic modules in Emacs and how to use them effectively, please consult my previous article on it. Emacs 32 provides a simple API for dealing with canvases from dynamic modules. The idea is simple, once a canvas has been created you can simply call the canavs_data function on it and it will provide you with the pixel buffer associated with the canvas. Once again, this buffer is only valid for ARGB32 pixel data.

static emacs_value
Fcanvas_update(emacs_env* env, ptrdiff_t nargs, emacs_value args[], void* data) {
  emacs_value canvas = env->args[0];
  uint32_t *canvas_pixel = env->canvas_data(env, canvas); // accessing canvas' pixel buffer
  if (canvas_pixel)
    {
      // do some pixel manipulation to the canvas pixel
    }
}

env->funcall(env, env->intern(env, "canvas-refresh"), (emacs_value[])[rect_canvas, Qnil]);

The above dynamic module function, when embedded in a legitimate dynamic module, compiled and loaded via module-load, would result in updating the canvas repeatedly by changing the colors (or whatever the pixel manipulation code does). This is how we can surpass Emacs Lisp’s limitations by accessing a low-level representation of the canvas we can manipulate directly. And since canvas-refresh avoids throttling Emacs’ full redisplay we do not encounter any slowness at all! And since we don’t use :data via Emacs Lisp image spec, whenever canvas is used via dynamic modules the RELOAD-DATA argument of canvas-refresh is to be nil.

4. Demonstrations

Here we showcase a few neat graphical animations you can do with canvas, both in Emacs Lisp and dynamic modules. For our previous demonstrations, consult Tushar’s blog post where he collects all of the experiments I did with and without Emacs. Also, look at the Minad's emacs-canvas-patch which has some basic demos (including a fancy mode-line one!). The first two of the demos below would be purely in Emacs Lisp and the last one would be in C via a dynamic module.

4.1. A Bouncing Ball

Let’s start with creating and setting up our canvas. We’ll go with dark violet (#xFF8B00FF) for the background:

(setq W 250
      H 250
      BG #xFF8B00FF)
(setq ball-canvas-vec (make-vector (* W H) BG))
(setq ball-canvas `(image :type canvas
                          :id rect
                          :data-width ,W
                          :data-height ,H
                          :data ,ball-canvas-vec))
(insert (propertize "#" 'display ball-canvas))

This is exactly what we did in the previous section, just in violet! Now is the interesting part, we want this 250x250 pixel canvas to have a ball bouncing. Well, first we prepare the ball, it will have some radius, an initial position and some initial velocity (and color!):

(setq ball-radius 10)
(setq bx (/ W 2)
      by (/ H 2)) ; start from the center
(setq dx 4
      dy 3) ; initial velocity
(setq ball-color #xFFFFFF00) ; yellow

Now we need to actually draw the ball, now I will ask you to remind yourself of some analytical geometry for this! The way we draw the ball is we take a point to be the center of the circle and check if we can build a circle around a certain bounding box. If ball-radius is 15, thus the diameter is 30, so we need at least a 30x30 square to fit the circle. Now to draw the actual circle, we’ll use the equation for a circle:

x 2 + y 2 ≀ r 2 superscript đ‘„ 2 superscript 𝑩 2 superscript 𝑟 2 x^{2}+y^{2}\leq r^{2}

Putting this in Emacs Lisp, we get:

(defun draw-ball (cx cy color)
  (let ((r2 (* ball-radius ball-radius))) ; Radius squared
    ;; the nested loop checks for the circle within the square
    (dotimes (y-off (1+ (* 2 ball-radius)))
      (dotimes (x-off (1+ (* 2 ball-radius)))
        (let* ((px (+ cx (- x-off ball-radius)))
               (py (+ cy (- y-off ball-radius)))
               (dx-local (- px cx)) ; Horizontal distance from center
               (dy-local (- py cy)) ; Vertical distance from center
               (dist2 (+ (* dx-local dx-local) (* dy-local dy-local)))) ; squared distance using pythagorean theorem
          ;; check if pixel is inside the circle AND inside the canvas bounds
          (when (and (>= px 0) (< px W) (>= py 0) (< py H) (<= dist2 r2))
            (aset ball-canvas-vec (+ (* py W) px) color)))))))

The above function should make sense, while it is not the most efficient way to draw a ball, it’s relatively simple enough for the demonstration. Now we simply need to check the physics for making sure it stays within the canvas:

(defun ball-bounce ()
  (setq bx (+ bx dx) by (+ by dy))
  ;; compare with ball-radius so the *edge* of the circle bounces, not the center.
  (when (<= bx ball-radius) (setq dx (abs dx)) (setq bx ball-radius))
  (when (>= bx (- W ball-radius)) (setq dx (- (abs dx))) (setq bx (- W ball-radius)))
  (when (<= by ball-radius) (setq dy (abs dy)) (setq by ball-radius))
  (when (>= by (- H ball-radius)) (setq dy (- (abs dy))) (setq by (- H ball-radius))))

And now we simply do it in a loop!

(setq ball-frame 0)
(setq max-ball-frames 2000)
(defun ball-loop ()
  (if (>= ball-frame max-ball-frames)
      (cancel-timer ball-timer)
    (setq ball-frame (1+ ball-frame))
    (draw-ball bx by BG) ; make old ball invisible (turn it into background color)
    (ball-bounce)
    (draw-ball bx by ball-color)
    (canvas-refresh ball-canvas t)))
(setq ball-timer (run-with-timer 0 0.02 'ball-loop))

Evaluate it all and you would have a yellow ball bouncing inside a violet square. It automatically stops after 2000 frames, but you can have it go on indefinitely as well! Here’s what it will look like:

4.2. Chaotic Double Pendulum

One of the things that fascinated me when I was studying classical mechanics for the first time was the double pendulum. The math underlying this: the Euler-Lagrange equation and variational calculus in general was also very fascinating to me. If you think it’s not, I suggest you look into the history and math behind the Brachistochrone curve. It’s evident from all this, that I want to put this into visualization using canvas!

The two equations in Lagrangians which describes the chaotic system:

ξ ¹ 1 = − m 2 ⁱ cos ⁡ ( ξ 1 − ξ 2 ) ⁱ l 1 ⁱ ξ ˙ 1 2 ⁱ sin ⁡ ( ξ 1 − ξ 2 ) + m 2 ⁱ cos ⁡ ( ξ 1 − ξ 2 ) ⁱ g ⁱ sin ⁡ ( ξ 2 ) − m 2 ⁱ l 2 ⁱ ξ ˙ 2 2 ⁱ sin ⁡ ( ξ 1 − ξ 2 ) − ( m 1 + m 2 ) ⁱ g ⁱ sin ⁡ ( ξ 1 ) l 1 ⁱ ( m 1 + m 2 − m 2 ⁱ cos 2 ⁡ ( ξ 1 − ξ 2 ) ) subscript ¹ 𝜃 1 subscript 𝑚 2 subscript 𝜃 1 subscript 𝜃 2 subscript 𝑙 1 superscript subscript ˙ 𝜃 1 2 subscript 𝜃 1 subscript 𝜃 2 subscript 𝑚 2 subscript 𝜃 1 subscript 𝜃 2 𝑔 subscript 𝜃 2 subscript 𝑚 2 subscript 𝑙 2 superscript subscript ˙ 𝜃 2 2 subscript 𝜃 1 subscript 𝜃 2 subscript 𝑚 1 subscript 𝑚 2 𝑔 subscript 𝜃 1 subscript 𝑙 1 subscript 𝑚 1 subscript 𝑚 2 subscript 𝑚 2 superscript 2 subscript 𝜃 1 subscript 𝜃 2 \displaystyle\ddot{\theta}_{1}=\frac{-m_{2}\cos(\theta_{1}-\theta_{2})\,l_{1}% \dot{\theta}_{1}^{2}\sin(\theta_{1}-\theta_{2})+m_{2}\cos(\theta_{1}-\theta_{2% })\,g\sin(\theta_{2})-m_{2}l_{2}\dot{\theta}_{2}^{2}\sin(\theta_{1}-\theta_{2}% )-(m_{1}+m_{2})g\sin(\theta_{1})}{l_{1}\left(m_{1}+m_{2}-m_{2}\cos^{2}(\theta_% {1}-\theta_{2})\right)}
ξ ¹ 2 = ( m 1 + m 2 ) ⁱ ( l 1 ⁱ ξ ˙ 1 2 ⁱ sin ⁡ ( ξ 1 − ξ 2 ) + ξ ˙ 2 2 ⁱ sin ⁡ ( ξ 1 − ξ 2 ) ⁱ cos ⁡ ( ξ 1 − ξ 2 ) ⁱ m 2 ⁱ l 2 m 1 + m 2 + cos ⁡ ( ξ 1 − ξ 2 ) ⁱ g ⁱ sin ⁡ ( ξ 1 ) − g ⁱ sin ⁡ ( ξ 2 ) ) l 2 ⁱ ( m 1 + m 2 ⁱ sin 2 ⁡ ( ξ 1 − ξ 2 ) ) subscript ¹ 𝜃 2 subscript 𝑚 1 subscript 𝑚 2 subscript 𝑙 1 superscript subscript ˙ 𝜃 1 2 subscript 𝜃 1 subscript 𝜃 2 superscript subscript ˙ 𝜃 2 2 subscript 𝜃 1 subscript 𝜃 2 subscript 𝜃 1 subscript 𝜃 2 subscript 𝑚 2 subscript 𝑙 2 subscript 𝑚 1 subscript 𝑚 2 subscript 𝜃 1 subscript 𝜃 2 𝑔 subscript 𝜃 1 𝑔 subscript 𝜃 2 subscript 𝑙 2 subscript 𝑚 1 subscript 𝑚 2 superscript 2 subscript 𝜃 1 subscript 𝜃 2 \displaystyle\ddot{\theta}_{2}=\frac{(m_{1}+m_{2})\left(l_{1}\dot{\theta}_{1}^% {2}\sin(\theta_{1}-\theta_{2})+\dfrac{\dot{\theta}_{2}^{2}\sin(\theta_{1}-% \theta_{2})\cos(\theta_{1}-\theta_{2})\,m_{2}l_{2}}{m_{1}+m_{2}}+\cos(\theta_{% 1}-\theta_{2})\,g\sin(\theta_{1})-g\sin(\theta_{2})\right)}{l_{2}\left(m_{1}+m% _{2}\sin^{2}(\theta_{1}-\theta_{2})\right)}

Now let’s create the canvas as usual with black background and display it like before:

(setq W 250
      H 250
      BG #xFF101010)
(setq canvas-vec (make-vector (* W H) BG))
(setq canvas `(image :type canvas :id dp :data-width ,W :data-height ,H :data ,canvas-vec))
(insert (propertize "#" 'display canvas))

We set some constants: gravity, length of rods, and delta time:

(setq g 9.8
      L 60.0
      dt 0.05)

We set some initial conditions for the angles Ξ 1 subscript 𝜃 1 \theta_{1} and Ξ 2 subscript 𝜃 2 \theta_{2} ; angular velocities w 1 subscript đ‘€ 1 w_{1} and w 2 subscript đ‘€ 2 w_{2} , and a fixedd anchor point:

(setq th1 2.5
      th2 2.5
      w1 0.0
      w2 0.0)
(setq pivot-x 125
      pivot-y 80)

We need some helpers, we already have the ball one from before and one for drawing a line/rod and to set a pixel in the vector:

(defun set-px (x y color)
  (when (and (>= x 0) (< x W) (>= y 0) (< y H))
    (aset canvas-vec (+ (* (truncate y) W) (truncate x)) color)))

(defun draw-line (x0 y0 x1 y1 color)
  (let* ((x0 (truncate x0)) (y0 (truncate y0))
         (x1 (truncate x1)) (y1 (truncate y1))
         (dx (abs (- x1 x0))) (dy (abs (- y1 y0)))
         (sx (if (< x0 x1) 1 -1)) (sy (if (< y0 y1) 1 -1))
         (err (- dx dy)) e2)
    (while (not (and (= x0 x1) (= y0 y1)))
      (set-px x0 y0 color)
      (setq e2 (* 2 err))
      (when (> e2 (- dy))
        (setq err (- err dy))
        (setq x0 (+ x0 sx)))
      (when (< e2 dx)
        (setq err (+ err dx))
        (setq y0 (+ y0 sy))))
    (set-px x0 y0 color)))

(defun draw-circ (cx cy r color)
  (let ((r2 (* r r)))
    (dotimes (y (* 2 r)) (dotimes (x (* 2 r))
                           (let* ((px (+ (- cx r) x)) (py (+ (- cy r) y))
                                  (dx (- px cx)) (dy (- py cy)))
                             (when (<= (+ (* dx dx) (* dy dy)) r2)
                               (set-px px py color)))))))

And now the main procedure that takes care of the pendulum movment:

(defun pendulum-physics ()
  (let* ((dth (- th1 th2))
         (den (- 3.0 (cos (* 2.0 dth))))
         (num1 (+ (* (- g) 3.0 (sin th1))
                  (* (- g) (sin (- th1 (* 2.0 th2))))
                  (* (- 2.0) (sin dth) (+ (* w2 w2 L) (* w1 w1 L (cos dth))))))
         (a1 (/ num1 (* L den)))
         (num2 (* 2.0 (sin dth)
                  (+ (* 2.0 w1 w1 L)
                     (* 2.0 g (cos th1))
                     (* w2 w2 L (cos dth)))))
         (a2 (/ num2 (* L den))))
    (setq w1 (+ w1 (* a1 dt)) w2 (+ w2 (* a2 dt)))
    (setq th1 (+ th1 (* w1 dt)) th2 (+ th2 (* w2 dt)))))

The exercise is left to the reader to make sure the above code follows the math! Specifically compare the two equations we introduced initially with the above procedure. And now the main game loop:

(defun pendulum-loop ()
  (dotimes (i 12) (pendulum-physics))
  (fillarray canvas-vec BG)
  (let* ((x1 (+ pivot-x (* L (sin th1))))
         (y1 (+ pivot-y (* L (cos th1))))
         (x2 (+ x1 (* L (sin th2))))
         (y2 (+ y1 (* L (cos th2)))))
    (draw-line pivot-x pivot-y x1 y1 #xFF888888)
    (draw-line x1 y1 x2 y2 #xFF888888)
    (draw-circ x1 y1 8 #xFF00FF00)
    (draw-circ x2 y2 8 #xFFFF0000)
    (set-px pivot-x pivot-y #xFFFFFFFF))
  (canvas-refresh canvas t))
(setq my-timer (run-with-timer 0 0.05 'pendulum-loop))

And once evaluated, it will look something like this:

One can enable interactivity here by making the mouse be able to drag the pendulum and set the initial positions, this is very much doable by using Emacs’ track-mouse functionality. I am not including it here because it’ll be too complex.

4.3. Points in 3D Space

This demo is borrowed from Alexey Kutepov, aka tsoding. They built a graphics library called olive.c. The following demo is Dots3D example from olive.c. Since this is a dynamic module, all the pixel manipulation math would be happening in C and on the Emacs Lisp side we will take care of creating and displaying the canvas and just calling the dynamic module function in a timer to update the canvas. While the original demo didn’t have any mouse interactivity, we can include it in ours quite easily.

After doing the mandatory int plugin_is_GPL_compatible we define some constants in the dynamic module:

#define WIDTH 960
#define HEIGHT 720
#define BACKGROUND_COLOR 0xFF181818
#define GRID_COUNT 10
#define GRID_PAD (0.5f/GRID_COUNT)
#define GRID_SIZE ((GRID_COUNT - 1)*GRID_PAD)
#define CIRCLE_RADIUS 5
#define Z_START 0.25f

We’ve just inherited them from the original demo’s code. Now we need one helper to draw the actual circles/points in this grid/space. We can just convert into C the code we wrote earlier for drawing circles:

void draw_circle(uint32_t *pixels, int cx, int cy, int r, uint32_t color) {
  int r2 = r * r;
  for (int y = -r; y <= r; ++y) {
    for (int x = -r; x <= r; ++x) {
      if (x*x + y*y <= r2) {
        int px = cx + x; int py = cy + y;
        if (px >= 0 && px < WIDTH && py >= 0 && py < HEIGHT)
          pixels[py * WIDTH + px] = color;
      }
    }
  }
}

Now we need to write the actual module function that will be called from Emacs. Firstly, what should this function’s signature be? Since it’s a module function, it has to be like this:

static emacs_value render(emacs_env* env, ptrdiff_t nargs, emacs_value args[], void* data)

But what should be its arguments when called? Well, firstly it needs the canvas where it will render to. It will also need as arguments the angles from which to render the whole grid, because the grid must be rotating, so the angles will be changing continuously. So let’s get those arguments, and get access to the canvas’ pixel buffer as well:

static emacs_value render(emacs_env* env, ptrdiff_t nargs, emacs_value args[], void* data) {
    emacs_value canvas = args[0];
    float angle_x = env->extract_float(env, args[1]);
    float angle_y = env->extract_float(env, args[2]);

    uint32_t* pixels = env->canvas_data(env, canvas);
    if (!pixels) return Qnil;
}

Now the background must be painted, for which we’ll just loop through all the pixels and set them to BACKGROUND_COLOR. We’ll also get some float valuees for the upcoming math. We clearly need some camera math to make it move as well, and since we are in 3D we’ll need a 3-level nested for loop. So here’s what the final function looks like:

static emacs_value render(emacs_env* env, ptrdiff_t nargs, emacs_value args[], void* data) {
    emacs_value canvas = args[0];
    float angle_x = env->extract_float(env, args[1]);
    float angle_y = env->extract_float(env, args[2]);

    uint32_t* pixels = env->canvas_data(env, canvas);
    if (!pixels) return Qnil;

    for(int i = 0; i < WIDTH * HEIGHT; ++i) pixels[i] = BACKGROUND_COLOR;

    float cos_x = cosf(angle_x), sin_x = sinf(angle_x);
    float cos_y = cosf(angle_y), sin_y = sinf(angle_y);

    float camera_distance = 0.8f;  // How far the camera is from the center (Lower = closer)
    float focal_length = 800.0f;   // The "zoom" multiplier (Higher = more zoomed in)

    for (int ix = 0; ix < GRID_COUNT; ++ix) {
        for (int iy = 0; iy < GRID_COUNT; ++iy) {
            for (int iz = 0; iz < GRID_COUNT; ++iz) {
                float x = ix*GRID_PAD - GRID_SIZE/2.0f;
                float y = iy*GRID_PAD - GRID_SIZE/2.0f;
                float z = Z_START + iz*GRID_PAD;

                float px = x;
                float py = y;
                float pz = z - (Z_START + GRID_SIZE/2.0f);

                // 1. Rotate around Y axis (Yaw)
                float x1 = px * cos_y + pz * sin_y;
                float z1 = -px * sin_y + pz * cos_y;
                float y1 = py;

                // 2. Rotate around X axis (Pitch)
                float y2 = y1 * cos_x - z1 * sin_x;
                float z2 = y1 * sin_x + z1 * cos_x;
                float x2 = x1;

                // 3. Apply camera distance
                float z_cam = z2 + camera_distance;

                // 4. Perspective projection using focal length
                float screen_x = (x2 / z_cam) * focal_length + WIDTH / 2.0f;
                float screen_y = (y2 / z_cam) * focal_length + HEIGHT / 2.0f;

                uint32_t r = ix*255/GRID_COUNT;
                uint32_t g = iy*255/GRID_COUNT;
                uint32_t b = iz*255/GRID_COUNT;
                uint32_t color = 0xFF000000 | (r << 16) | (g << 8) | b;

                draw_circle(pixels, (int)screen_x, (int)screen_y, CIRCLE_RADIUS, color);
            }
        }
    }

    return Qnil;
}

After this we can just initialize the module:

int emacs_module_init(struct emacs_runtime *rt) {
    if ((size_t)rt->size < sizeof (*rt)) return 1;
    emacs_env* env = rt->get_environment(rt);
    if ((size_t)env->size < sizeof (*env)) return 2;
    Qnil = env->make_global_ref(env, env->intern(env, "nil"));

    env->funcall(env, env->intern(env, "defalias"), 2,
                 (emacs_value[]){
                     env->intern(env, "dots3d-render"),
                     env->make_function(env, 3, 3, render, "Render dots3d", 0)
                 });
    return 0;
}

This now needs to be compiled into a shared object, do not forget to add emacs-module.h wherever gcc looks for includes:

gcc -O2 -I%ssrc dots3d.c -o /tmp/dots3d.so -fPIC -shared -lm

Now we can use this from Emacs Lisp, some preliminary stuff:

(module-load "/tmp/dots3d.so")
(declare-function dots3d-render "ext:dots3d.c")

(switch-to-buffer (get-buffer-create "*dots3d*"))

(defvar dots3d-canvas)
(defvar dots3d-frame 0)
(defvar dots3d-time 0.0)
(defvar dots3d-last-time 0.0)

(defvar dots3d-angle-x 0.0)
(defvar dots3d-angle-y 0.0)
(defvar dots3d-auto-rotate t)

(setq dots3d-time (float-time))
(setq dots3d-last-time (float-time))

We setup the main canvas:

(setq dots3d-canvas '(image :type canvas
                            :data-width 960
                            :data-height 720
                            :margin (20 . 20)
                            :scale 1
                            :id dots3d))

We need to disable the mode-line and cursor:

(setq-local cursor-type nil
            mode-line-position nil
            mode-line-modified nil
            mode-line-mule-info nil
            mode-line-remote nil)

And we display the canvas:

(insert (propertize "#" 'display dots3d-canvas))

Now the main function that takes care of interacting with the space. We basically use track-mouse to update the canvas by calling the render function with new angles, every time we drag on the canvas. To be noted, we need to stop the auto-rotate while we are dragging.

(defun dots3d-start-drag (event)
  (interactive "e")
  (setq dots3d-auto-rotate nil)
  (let* ((start-pos (posn-object-x-y (event-start event)))
         (last-x (car start-pos))
         (last-y (cdr start-pos)))
    (when (and last-x last-y)
      (track-mouse
        (let (evt pos mx my)
          (while (progn
                   (setq evt (read-event))
                   (mouse-movement-p evt))
            (setq pos (posn-object-x-y (event-start evt)))
            (when (and (car pos) (cdr pos))
              (setq mx (car pos) my (cdr pos))
              (setq dots3d-angle-y (+ dots3d-angle-y (* (- mx last-x) 0.01)))
              (setq dots3d-angle-x (+ dots3d-angle-x (* (- my last-y) 0.01)))
              (setq last-x mx last-y my)
              (dots3d-render dots3d-canvas dots3d-angle-x dots3d-angle-y)
              (canvas-refresh dots3d-canvas))))))
    (setq dots3d-auto-rotate t)))
(local-set-key [down-mouse-1] 'dots3d-start-drag)

And the final render function in Emacs Lisp can be extremely simple, you just call the render function with slightly adjusted angles and we call it in a timer so that it keeps rotating:

(defun dots3d-update ()
    (let* ((time (float-time))
           (dt (- time dots3d-last-time)))
      (switch-to-buffer (get-buffer-create "*dots3d*"))
      (setq dots3d-last-time time)
      (when dots3d-auto-rotate
        (setq dots3d-angle-y (+ dots3d-angle-y (* dt 0.5))))
      (dots3d-render dots3d-canvas dots3d-angle-x dots3d-angle-y)
      (canvas-refresh dots3d-canvas)))
(run-with-timer nil (/ 1 60.0) 'dots3d-update)

And after evaluation, you should see as below and be able to move the grid by dragging it:

5. Conclusion & Further Work

As is evident, this feature has enabled a new universe of graphical capabilities within Emacs. Of such possibilities, we have only scratched the surface in a few places. We are very excited to see what kinds of applications hackers can build using this. Here are some possible applications that can be built on top of canvas in Emacs (some of them we might work on ourselves):

  • A full-fledged document reader in Emacs. (Emacs Reader, the project which started it all.)
  • A full-fledged video player
  • A drawing pad (screw M$ Paint)
  • A framework to make video games
  • Scientific plotting (integration with Calc)
  • Org babel integrations
  • Rendering LaTeX snippets directly.
  • A nice color picker like HTML’s color attribute
  • 2D & 3D Graph viewer
  • Image & Video editors
  • A visual programming environment

And more
 we’ve opened a discussion on such graphical applications within Emacs and how to make them composable, feel free to contribute there.

-1:-- An Introduction to Canvas in GNU Emacs (Post Monadic Sheep)--L0--C0--2026-08-23T00:00:00.000Z

James Cherti: Emacs startup: Why setq beats setopt, customize-set-variable, and use-package :custom?

Every millisecond counts during Emacs initialization. A potential performance cost in modern Emacs configurations is defaulting to setopt, customize-set-variable, or the use-package :custom keyword when a direct assignment with setq is sufficient.

Disclaimer: This article assumes you have a solid understanding of Emacs Lisp and know what you are doing when switching to setq. For users who prefer a hands-off experience, defaulting to setopt or the built-in customization interface is often better for convenience, even though these options are not the fastest. Using setq is mainly intended for users who want to optimize startup performance. Always check the Elisp definition of each defcustom to ensure that switching to setq does not introduce unintended side effects.

What is the difference between setq and other ways to assign values to variables?

setq (and setq-default)

The setq and setq-default functions directly assigns a value to a variable, making it the fastest way to set a variable. (FYI: The following projects prioritize using setq and setq-default: minimal-emacs.d, Doom Emacs, Protesilaos Emacs config, and Spacemacs.)

(The setq function updates a variable's value within the current scope; if the target variable is buffer-local, the assignment only affects the current active buffer while the global state remains unchanged. Conversely, setq-default explicitly modifies the global default value of a variable. Although both functions produce the same result for purely global variables, setq-default must be used to apply system-wide changes to buffer-local variables like tab-width or indent-tabs-mode.)

setopt, customize-set-variable, and use-package :custom

The setopt, customize-set-variable, and use-package :custom functions can be slower than setq. Depending on the variable, they can:

  • Call the variable's defcustom :set function.
  • Validate the value against the variable's declared type.
  • In some cases, load the library defining an autoloaded customizable variable, potentially causing a package to load earlier during startup.

Example of an expansive defcustom :set property

To understand the latency penalty, look at how a package author might write a defcustom with an expensive :set property:

(defcustom my-global-visual-indicator t
  "Toggle a heavy visual indicator across all open buffers."
  :type 'boolean
  :group 'my-ui-package
  :set (lambda (symbol value)
         ;; Update the variable's value
         (set-default symbol value)
         
         ;; The slow part: Iterate through every open buffer
         ;; and trigger a costly visual update or cache rebuild.
         (dolist (buffer (buffer-list))
           (with-current-buffer buffer
             ;; This simulated function might parse the buffer,
             ;; apply text properties, or query a language server.
             (my-heavy-visual-update-function value)))
             
         ;; Force Emacs to immediately redraw all frames
         (redraw-display)))

Using setopt, customize-set-variable, or the use-package :custom to configure my-global-visual-indicator uses executes the associated defcustom :set function during startup:

;; Example 1: SLOW: This triggers the expensive :set function during startup
(setopt my-global-visual-indicator nil)

;; Example 2: This triggers the expensive :set function
(use-package my-ui-package
  :commands (my-ai-package-cmd1 my-ai-package-cmd2) ; defer
  :custom
  (my-global-visual-indicator nil))

In many cases, the Emacs startup phase is simply not the appropriate time to execute those :set functions.

On the other hand, if the variable does not require its Custom setter to establish associated state, using setq inside a use-package :init block bypasses the Emacs Customization setter entirely:

;; Example 1: FAST: This bypasses the :set function for a faster startup
(setq my-global-visual-indicator nil)

;; Example 2: FAST: This bypasses the :set function
(use-package my-ui-package
  :commands (my-ai-package-cmd1 my-ai-package-cmd2) ; defer
  :init
  (setq my-global-visual-indicator nil))

Note: Code placed in the :init block executes before the package loads into memory. Setting variables here ensures your customized values are already established in the global environment before the package evaluates its internal definitions, bypassing the need for a defcustom setter to update its internal state.

Trade-offs when using setq

Here are the trade-offs to consider when using setq:

  • During startup, a direct assignment can avoid UI updates or other side effects performed by a Custom setter. However, setq should only be used when the package does not require that setter to establish associated state.
  • setq does not validate types. If you pass a string where a boolean is expected, Emacs will not warn you at evaluation time.

Rule of thumb

Use setopt, customize-set-variable, or use-package :custom when a variable's Custom :set setter or type validation is required, or if you notice that the option is not being applied as intended. Otherwise, use setq by default.

(To determine whether a specific variable requires a custom :set setter for correct state initialization, inspect its properties directly within Emacs. Execute M-x describe-variable or C-h v and provide the variable's name to view its documentation. In the resulting help buffer, look for a :set attribute. While the presence of a :set function indicates that the package defines initialization logic for when the variable is modified, it does not necessarily mean that using setopt or use-package :custom is required. The necessity depends on the specific implementation, as the majority of major and minor modes automatically execute these initialization functions by default when the mode is activated.)

-1:-- Emacs startup: Why setq beats setopt, customize-set-variable, and use-package :custom? (Post James Cherti)--L0--C0--2026-08-22T16:32:15.000Z

Irreal: HTML Preview For Org Mode

Randy Ridenour has an interesting post about previewing an Org file in your browser. If you’re like me your first thought is probably, “That’s easy. Just export the Org file to HTML and display it right from the Org export dispatcher.” I do exactly that to preview my blog posts. I simply type Ctrl+c Ctrl+e h o and my post appears in my browser. Of course, it doesn’t look exactly like the final product because it uses the default Org CSS instead of the WordPress theme that you see on the Irreal site.

And that’s exactly the point. Ridenour has his own opinion on how his markup should be formatted. Five years ago, I wrote about how to to add your own CSS formatting to an Org file when you export it to HTML but those methods all required adding some text to the Org file. Ridenour’s method does all that in a function that he can call to export his Org file to the browser. The process is pretty much the same as using the export dispatcher except that he gets to have his preferred CSS applied to the output.

The other advantage of his method is the he arranges for the automatic removal of the generated HTML file. The export dispatcher method requires that you delete the files manually. I do that periodically using Embark, which makes it easy but I still have to remember to do it. If you’re looking for a fast and easy way to preview an Org file in your browser, take a look at Ridenour’s post.

-1:-- HTML Preview For Org Mode (Post Irreal)--L0--C0--2026-08-22T16:02:37.000Z

Emacs APAC: Announcing Emacs Asia-Pacific (APAC) virtual meetup, Saturday, August 22, 2026

This month’s Emacs Asia-Pacific (APAC) virtual meetup is scheduled for Saturday, August 22, 2026 with BigBlueButton and #emacs on Libera Chat IRC. The timing will be 1400 to 1500 IST.

The meetup might get extended by 30 minutes if there is any talk, this page will be updated accordingly.

If you would like to give a demo or talk (maximum 20 minutes) on GNU Emacs or any variant, please contact bhavin192 on Libera Chat with your talk details:

-1:-- Announcing Emacs Asia-Pacific (APAC) virtual meetup, Saturday, August 22, 2026 (Post Emacs APAC)--L0--C0--2026-08-22T05:06:06.000Z

Matt Maguire: AI Agents Take Over Lesson Prep

It has been a while since I have written about how I use Emacs/LaTeX to generate lesson notes for my students. Quite a lot has happened during that time. One of the main game changers has been the advancements in “AI” technology. Over the next few articles I intend to describe my journey as I navigate this technology and apply it to my teaching practice.

Chat Bots

Having decided to dip my toe in the water, I signed up to two of the prevalent players in this space, being Google Gemini and Anthropic Claude. My early experiments with ChatGPT had led to some disappointing results, where it emphatically tried to convince me that the value of Euler’s constant is 1.5. Since then, the technology has advanced significantly, and I thought it worthwhile to try again.

-1:-- AI Agents Take Over Lesson Prep (Post Matt Maguire)--L0--C0--2026-08-22T00:00:00.000Z

Donovan R.: 💭 I Smell Emacs and Smalltalk on DSH (DeepSeek Harness)

Last week, Deepseek released DSH, an agent harness with very interesting approach.
The same pitch used by VS Code and Eclipse RPC (2024): “everything is a plugin” but this time it’s a bit different. Even the agent loop itself is a plugin.

In practice, you extend the tool by just prompting. (Reminds me of an old post of mine where I said that Emacs is like a modern “Magic Lamp”)
It allows maximum customizability and also opens many possibilities.
In the official article, they showcased adding snake game to the main interface. Pretty cool!

Around the same time, I spent a little time playing with the pi-coding-agent on Emacs and was thinking that now if I want, I probably could extend Emacs indefinitely by just prompting.

Weird synchronicity.

Anyway, here are some useful links if you want to know more:

-1:-- 💭 I Smell Emacs and Smalltalk on DSH (DeepSeek Harness) (Post Donovan R.)--L0--C0--2026-08-20T17:37:36.000Z

Irreal: Setup For The Casual Suite

Charles Choi has announced the release of Casual 3.0. There are now 26 modules in the Casual suite but this release is about providing an easier setup for the suite. Prior to this release, each module had to be set up separately. As Choi says, that made sense when there were just a few modules but it has become increasingly burdensome as the number of modules grew.

The new procedure is simply to call casual-init and all the module’s you want to use are initialized. The modules you’re using is determined by a set of hook functions that can configured with the Emacs Customize utility. You simple check off which modules you want to use and call casual-init. That’s all there is to it. You can learn more about this in the Casual User Manual.

If you’ve been following along on Choi’s blog—or to a lessor extent on Irreal—you know that there are a lot of different types of modules, not all of which you might want installed. The Customize interface is an easy way to specify which ones you want.

The latest version is available on MELPA and NonGNU ELPA. The nice thing about the modules is that they are all invoked with the same key sequence, Ctrl+o by default, so you don’t have to remember a bunch of different bindings. Once you invoke it, Casual brings up a Transient menu appropriate for the current major mode

Casual is a nice suite that make it easier to navigate Emacs applications that you don’t use often and for which you haven’t internalized all the bindings. Take a look at the user manual if you want to know more.

-1:-- Setup For The Casual Suite (Post Irreal)--L0--C0--2026-08-20T14:46:27.000Z

Thanos Apollo: My Emacs Agentic Workflow

Two years ago I was a skeptic about GenAI. Now Emacs has become my agent cockpit.

Harness

My favorite harness right now is hermes-agent, which is probably the “emacs of agent harnesses” with how easy it is to configure and extend, while not being “just an agent harness.” Hermes supports messaging gateways, so you have first-class support for mobile as well. All you have to do is /handoff <platform> to continue your session via a mobile messaging app.

To interact with hermes via emacs, I’m using emacs-hermes, which is available via NonGNU ELPA.

Sometimes I also use codex via emacs-codex-ide. But it’s not as token-efficient as my hermes setup, and it’s not where I am most productive. Unlike most harnesses, hermes supports codex native compaction, which is the “killer” feature of codex.

Models

I currently use gpt-5.6-sol for most tasks through my codex subscription. It is exceptionally good at long-running goals. It can easily keep going for 20h+ trying to complete its task, and I have found the quality of its work more than adequate.

TIP: to save tokens with hermes, adjust the auxiliary.approval model to a cheap open-weight one, such as deepseek-v4-flash, which is practically free on nous portal. This has cut my usage by more than 35%.

I’m also surprised by the recent leap x.ai made with Grok, which is currently the best value-for-money model by far. The benchmarks place it close to gpt-5.6-sol and fable.

Figure 1: Artificial Analysis Intelligence Index

Figure 1: Artificial Analysis Intelligence Index

It’s currently my fallback model when I reach my usage limit on codex. It’s the fastest “frontier” model I’ve tried.

I avoid using models from Anthropic. Models like opus-5 and fable are good for coding tasks, but I found them subpar for my studies even compared to grok-4.6, plus overpriced. The API prices are insane, and their subscription only supports their proprietary harness.

What about open models?

A friend and I tried to self-host our “AI Homelab”, repurposing an old ethereum mining rig plus a couple of new Nvidia GPUs. The performance was not ideal with models like GLM-5.2 compared to subscriptions by a long shot. Forget subagent workflows if you ever attempt it. Not to mention that my electricity bill was the same as I’d pay for a subscription plan.

A lot of people in the “GNU/Linux” sphere are looking for “free” models. Even if there were a 6T+ “free” model that outperforms frontiers, there is no “free hardware” that could run it, and even if there were a FOSS Nvidia alternative, would it be financially worth it for most people as an investment? Would your average hobbyist rather pay $20/month or buy the next gen “free” DGX Spark for $5,000?

Extra goodies & Skills

Memory

No matter your setup, the biggest upgrade for me has been hindsight. It’s an agent memory system that you can share across harnesses, and it helps your agents remember your preferences and workflow details.

It’s free and easy to self-host.

Skills

Skills are where my setup stops being generic. Instead of explaining the same preferences and workflow every session, I put them in SKILL.md files that Hermes loads depending on the task. I have skills for how I want elisp written and reviewed, and even for drafts like this post.

Everyone takes a different approach, from GSD/BMAD/Spec-driven workflows that automate most of the work to “real engineering” skills that want you to babysit the agent. I prefer smaller skills I can mix together instead of one framework owning my whole workflow.

If you are just starting out, browse skills.sh for anything that “looks cool”, experiment with it, then start writing your own. The two I recommend to most people are unslop and grill-me.

Other tools and environments

As of now, the only other tools I’d recommend are web tools like firecrawl and searxng.

Current state of my “AI Psychosis”

The past year the value I have gained from GenAI tools is insane. I had not dreamed, even in my wildest dreams, that I would have such tools in my lifetime.

Tasks that used to bottleneck my workflow, such as adjusting a software tool to help me study, are now cheap to solve. Software is more of a commodity now, no longer a moat. Anything you like can be built easily. You can see that even in projects like hermes, which copied the “Grok Bot” “killer-feature” in less than 24h and made it open source in their harness.

Grunt work like notes for my syllabus based on my university lecture slides used to take me weeks. Now it’s just a /goal prompt, go for a walk for 2h, and come back and they are ready in the same exact format the rest of my notes already have. All that is left is for me to study them.

We live in exciting times.

-1:-- My Emacs Agentic Workflow (Post Thanos Apollo)--L0--C0--2026-08-20T00:00:00.000Z

Irreal: Switching To A Window In Another Frame

Álvaro Ramírez, author of Journelly and many other useful applications—a lot of them Emacs based—has a new post about a basic Emacs operation: changing focus to another window. Ramírez and I basically agree about things like this although he stubbornly refuses to admit the superiority of my method of indicating the window that has focus.

Now he has a slightly different problem. For technical reasons that you can read about in his post, he wants to be able to switch to a window in another frame. The other-window command, bound to Ctrl+x o by default, doesn’t consider windows in other frames but, of course, there’s a command for that: next-window-any-frame. That turned out to be exactly what Ramírez needed and he simply bound it to Meta+o, which is where he had other-window bound.

Like Ramírez, I seldom use more than one frame so I didn’t know about this command. If you often have more than one frame going, you may find it useful if you’re not already using it. If you’re an ace-window user, it will consider all frames by default but you can adjust this with aw-scope.

-1:-- Switching To A Window In Another Frame (Post Irreal)--L0--C0--2026-08-19T16:11:32.000Z

Charles Choi: Simplifying Setup for Casual

Since its inception, Casual required individual setup for each module it supported. This seemed fine at the start when the module count was low. Now weighing in at 26 modules, there is admittedly a lot of setup.

Not anymore. Starting with the latest Casual v3.0 update, there is now a convenience command to setup all Casual modules: casual-init.

Use the following Elisp to replace all the previous setup code:

1
2
(require 'casual)
(casual-init)

You can configure which modules to setup via the customizable hook variable casual-init-hook, a list of hook functions each associated with a module. Having a module hook function present in casual-init-hook tells casual-init to setup that module.

Editing casual-init-hook is straightforward using the customize checkbox interface shown below.

img

More information about installation and setup of Casual can be found in the Casual User Guide.

Bumping to 3.0

Because the recommended setup has significantly changed, I’ve decided to bump the major version to 3.0.

That said, there are no functional changes to Casual and all the prior individual setup still should work without change. For existing users of Casual, I highly recommend migrating to casual-init as it allows for removing individual setup code.

A significant amount of manual testing was required to verify lazy-loading behavior in casual-init. While best effort was made to avoid unpleasant surprise, if something shows up, please file an issue to let me know.

Casual v3.0 update is now available on MELPA and NonGNU ELPA.

It costs money to make, enhance, and maintain Casual as ideologically free software. If you enjoy using Casual, please consider buying me a coffee to help support its development and maintenance. Thank you!

-1:-- Simplifying Setup for Casual (Post Charles Choi)--L0--C0--2026-08-18T16:20:00.000Z

Irreal: Emacs 31.1 RC1

Great news! Sean Whitton writes to announce the release of Emacs 31.1 RC1. This is the final step before the formal release of the Emacs 31 series. Whitton remarks that the developers are hoping that RC1 will be the only release candidate. I take that as a measure of their confidence in this trial release. Unless any breaking problems pop up, we can expect the official release of 31.1 imminently.

As always, we should offer up a word of thanks for the hard work of the maintainers and all the developers who have toiled so hard to bring us this latest release. If we find ourselves in the same bar as one of them, the drinks are on us.

-1:-- Emacs 31.1 RC1 (Post Irreal)--L0--C0--2026-08-18T15:05:43.000Z

Randy Ridenour: HTML Preview for Org Files

I’ve long been a fan of Brett Terpstra’s Marked for easily reading Markdown files. Over the years, he’s added more and more features. He’s recently released Marked 3 which, if you need those features, is well worth the purchase price of $2.99/month subscription or a one-time purchase of $69.99. There are only two small things keeping me from upgrading:

  1. I don’t need any of those features — in fact, since everything I write begins as an Org mode file, I don’t really need a Markdown previewer.
  2. I teach philosophy at a small liberal arts college in a rapidly collapsing market.

So, I don’t need a Markdown previewer, but an Org mode previewer would be nice. I guess I could have Claude code a Mac app like Marked for Org files, but since Org exports to HTML, it shouldn’t be that difficult to export the HTML to a temporary file, style it with some CSS, open it in a browser, then have it automatically deleted when no longer needed.

Here is the export function and the CSS for styling the output.

(defun rlr/org-export-html-to-browser ()
  "Export the current Org buffer to a temporary HTML file in the system temp directory, open it, and then delete it when Emacs is killed."
  (interactive)
  (unless (derived-mode-p 'org-mode)
    (user-error "Not in an Org buffer"))
  (let* ((tmp (make-temp-file "org-preview-" nil ".html"))
         (org-export-in-background nil)
         (org-html-head-include-default-style nil)
         (org-html-head-include-scripts nil)
         (org-html-head rlr/org-preview-css)
         (org-html-validation-link nil))
    (org-export-to-file 'html tmp)
    (add-hook 'kill-emacs-hook
              (lambda () (ignore-errors (delete-file tmp))))
    (browse-url-of-file tmp)))
(defvar rlr/org-preview-css "
<style>
  :root { color-scheme: light dark; }
  body {
    max-width: 40rem;
    margin: 3rem auto;
    padding: 0 1.25rem;
    font-family: -apple-system, BlinkMacSystemFont, 'Iowan Old Style','Palatino Linotype', Georgia, serif;
    font-size: 1.125rem;
    line-height: 1.65;
    color: #24292f;
    background: #fdfdfc;
    text-rendering: optimizeLegibility;
    hyphens: auto;
  }
  h1, h2, h3, h4 {
    font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif;
    line-height: 1.25;
    margin-top: 2.25em;
    color: #14181c;
  }
  h1 { font-size: 1.9rem; margin-bottom: 0.2em; }
  h2 { font-size: 1.45rem; }
  h3 { font-size: 1.2rem; }
  .title { text-align: left; margin-bottom: 2rem; }
  #table-of-contents { font-size: 0.95rem; }
  a { color: #0b5cad; text-underline-offset: 2px; }
  blockquote {
    margin: 1.5em 0; padding: 0.25em 0 0.25em 1.25em;
    border-left: 3px solid #d0d7de; color: #4a5058; font-style: italic;
  }
  pre, code { font-family: 'SF Mono', Menlo, Consolas, monospace; font-size: 0.9rem; }
  pre {
    background: #f4f4f2; padding: 0.9em 1.1em;
    border-radius: 6px; overflow-x: auto; line-height: 1.45;
  }
  code { background: #f0f0ee; padding: 0.1em 0.3em; border-radius: 3px; }
  pre code { background: none; padding: 0; }
  table { border-collapse: collapse; margin: 1.5em 0; font-size: 0.95rem; }
  th, td { border: 1px solid #d0d7de; padding: 0.4em 0.7em; text-align: left; }
  th { background: #f4f4f2; }
  hr { border: none; border-top: 1px solid #d8d8d4; margin: 2.5em 0; }
  .footpara { display: inline; }
  #postamble { margin-top: 3rem; font-size: 0.85rem; color: #6a707a; }
  @media (prefers-color-scheme: dark) {
    body { color: #d6d3cd; background: #16181a; }
    h1, h2, h3, h4 { color: #f0ede8; }
    a { color: #6cb3f5; }
    blockquote { border-left-color: #3a3f45; color: #a8a49d; }
    pre { background: #1e2124; }
    code { background: #24282c; }
    th, td { border-color: #3a3f45; }
    th { background: #1e2124; }
    hr { border-top-color: #2e3236; }
  }
</style>"
  "Stylesheet injected into throwaway HTML previews of Org buffers.")

Tagged: Emacs Org

-1:-- HTML Preview for Org Files (Post Randy Ridenour)--L0--C0--2026-08-17T23:49:00.000Z

Sacha Chua: 2026-08-17 Emacs news

The canvas patch has been merged upstream, very cool! I'm looking forward to seeing the kinds of experiments and tools this enables. Also, there's a release candidate for the next version of Emacs, please go try it out!

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

View Org source for this post

You can e-mail me at sacha@sachachua.com.

-1:-- 2026-08-17 Emacs news (Post Sacha Chua)--L0--C0--2026-08-17T20:41:53.000Z

TAONAW - Emacs and Org Mode: RSI and Emacs: When Your Pinky Says Enough is Enough

I’ve been doing so much Emacs writing lately, so I guess it’s time I get my Emacs pinky (which is a form of RSI). I made a quick reference to it three days ago and got a lot of responses.

What happened?

I started to feel a strain last week, but at first I didn’t think much about it. Sometimes my pinky rests awkwardly on the keyboard because I’m used to using the Caps Lock key, but it’s usually a hand-position issue. After I readjust and let my pinky rest, it goes away.

This time the strain didn’t go away, and my pinky kept rebelling.

My keyboard, Kinesis Freestyle Edge (the one here is the RGB+, the original RGB is discontinued), has a prominent Caps Lock key that just screams “use me as control!” For years, that’s what I did. As you can see in the picture, with my pinky resting on it.

A hand is positioned on the left side of a  Kinesis Gaming mechanical keyboard with blue backlit keys.

The last week has been busy. I didn’t leave my small Manhattan apartment for two days, working straight from morning to evening, then gaming and sleeping. This isn’t normal for me and has taken a toll on my hands, and the RSI got worse.

I think my new screen also exacerbated the problem somehow. It’s much bigger and has higher resolution, which I’m still adjusting to by how I sit and how I move my head and neck. I also have a standing desk I usually use in its standing position in the morning, when I write these posts or work on projects — these are easily my longest writing sessions, sometimes going for two hours straight without a break, especially in the last couple of weeks, and that’s all Emacs and pinky work.

When I thought about Emacs pinky in the past, I mostly thought of it with a smirk. I mean, it’s your pinky. How can you not read a comment about someone having their little finger hurt and not chuckle a bit? But folks, let me tell you, once you have it, you realize how much you depend on that one small finger, especially as an Emacs user.

My work, my organization, even my gaming in the evening (as it turns out), it’s all going through that little finger. Sure, it’s my “fault” for using Emacs, but if you’ve been reading this blog for a while you’d know that I owe many things to Emacs (including, among other things, my current job).

What am I doing about it?

For now, I’ve started using the keyboard’s dedicated control key again and changed how I use my hands. I try to use my right thumb, all the way to the side from the home row (my keyboard is huge, I will get to that in a minute) for when I need to use the left side of the keyboard, and then fold my pinky and use my knuckle to press it when I need the left control, for when I use the right side of the keyboard, as you can see in this picture:

A hand is resting the keys X and A with the little finger curled over the Ctrl key on a backlit keyboard with blue lighting.

This adjustment is the easiest, since I know where these Control keys are by muscle memory. I turned off my Caps as control on my Mac to make sure I don’t get tempted to go back to my old habit. It’s been working OK, and that’s what I’m doing right now for writing this post.

Other Emacs users have suggested several things, and I’ve been considering and trying a few of them.

god-mode came up a couple of times. It creates a chord (two keys pressed at once) key binding that triggers a control press. For example, sd, and then xs (separately) to save a file in Emacs — same as C-xC-s. This can be useful, but it has two problems.

The obvious one is that this shortcut works only in Emacs. When I don’t use Emacs, which happens more often than I’d like for work, I have to use the OS shortcuts. Caps is so ingrained in my fingers’ memory that I just use it all the time, but now I need to start thinking of other options. I could probably program the keyboard’s firmware to do something similar, but that’s a topic in itself — and if I do that, I don’t need god-mode anyway. Another odd issue is that it doesn’t work on my Mac. It throws a GCC error, which from what I gather has to do with Emacs internals and how it works (or doesn’t work in this case) with its internal compiling mechanisms. Yes, I tried the fix, which doesn’t seem to work well with Homebrew, and I gave up on it.

There are other packages like god-mode, but I think the next one I’m going to try is Xah Fly Keys, because what he’s after seems to make sense to me. Which brings me to the next point: a new keyboard.

New Keyboards

I’ve been considering a new keyboard for a while, and the RSI has sped up the process. I’ve looked at a couple reviews and YouTube videos in the last couple of days. Kinesis, my current keyboard brand, is a known name in the ergonomic keyboard world, but after checking out their Advantage 360 I’m not sure I’m sold. It’s an excellent premium-looking keyboard, but it’s having a huge and heavy keyboard again. My hands are small, and I’m not sure I want to commit to another expensive giant, especially when I already struggle with room for the mouse, especially when the two halves are spread apart for the shoulders. I already compromise on this, and my chest is closer than it should be. I also think that for the price of these keyboards (over $400), I might want to take them with me when I go to the office, and with a Kinesis keyboard, this is not going to happen. Another thing I’m considering is going wireless, because of my USB switch setup. It can take my Linux Desktop or the Mac up to 30 seconds to detect the keyboard when I switch, which is small, but still annoying. The mouse, connected by a Bluetooth dongle, takes a second to switch over.

The above reasons, and Xah’s reviews, landed me on Glove80, which is even a weirder-looking keyboard. This keyboard takes ergonomics further (or just looks crazier, if you’re not into those). It’s wireless and lighter, which means taking it with me makes more sense, and will hopefully fix the USB lag issue. It has keywells for the fingers, which is basically a “dip” or a bowl (hence the name) that curves downwards so your fingers, which are naturally curled on a keyboard, are resting downwards. It has its own super-light switches, which, while I don’t think I need them, are another example of thinking of RSI and finger pain.

Going further with light keyboards, there’s the Voyager, which is tiny, lighter, and minimal. It’s not wireless, but it’s meant to be mobile, and it’s probably the best one for small hands. However, the Voyager only has 52 keys. It’s meant to be used with layers — meaning, there’s a modifier key that creates another “layer” of keys, which makes your Alt act as Ctrl, for example. I’m not sure I’m sold on this idea, especially when we’re talking Emacs. If I’m already challenged by my lack of Caps Lock, I can only imagine how much time I will have to sink into this keyboard with its layers and grid layout (which the Glove80 has too, by the way) to become efficient with it. I also think the Keywell is something I could use, so I have a rough idea of what I want to try next.

The problem with the Glove80 is that you can’t really try it out. They have a 30-day return policy, shipping not included, and they’re in New Zealand. A keyboard like that takes time to get used to, and 30 days for a $ 400-plus keyboard requires a level of confidence in the product I don’t have yet.

My conclusion so far

All in all, I think what I’m looking at is a combination of adjustments rather than one solution. I need to get used to the idea of different key bindings, be it something like god-mode or Xah Fly Keys, which will make it easier to use with a new keyboard and would also be beneficial now, until I get it. The idea of layering is a bit daunting, but I can see the wisdom in it, especially after I got an email back from BSAG, who was kind enough to share her experience with me (she’s the one who told me about the Voyager keyboard).

It will be a while before I buy a new keyboard, then it will take some time to get here, and then it will take me even longer to get used to one. I’m not looking forward to having my typing hindered even further, especially because I’m so dependent on writing notes all the time, but it’s also because of it that I have to do it. This is one thing I know I need to do.

-1:-- RSI and Emacs: When Your Pinky Says Enough is Enough (Post TAONAW - Emacs and Org Mode)--L0--C0--2026-08-17T01:54:19.000Z

Corwin Brust: Emacs Card Games on MELPA

First melpa release evah \o/

Thanks to many for patience and coaching, and card-games.el 1.0.92 (pre-release #2, MELPA) is available from package-install for users of MELPA. Release announcement after the cut..

https://code.bru.st/corwin/card-game.el/media/branch/master/doc/images/klondike.png

Grown from experimentation within Dungeon, thank you to contribs there, and for review comments from ricsy putting toward MELPA got rid of some advices forcing very interesting better ways. The hooks approach makes the registered card-games-bid-submit a generic semaphore, for example to interlink with a process, CRDT via an adaptor, or 
 in present v1.0.92 (unstable): https://melpa.org/#/card-games

-1:-- Emacs Card Games on MELPA (Post Corwin Brust)--L0--C0--2026-08-16T23:09:44.000Z

Amit Patel: Emacs Tree-sitter custom highlighting, part 3

In the part 2 I described how, for a particular project with certain naming conventions, I wanted to use emacs tree-sitter font-locking to highlight variables that might be used in the wrong context:

Screenshot showing two subscript expressions, one that seems suspect because it's an array indexed by t values that has an index that is named r

I found a way to make it work for simple expressions but it was relying on regular expression matching rather than tree-sitter parse trees. I set a goal of handling these types of expressions:

elevation_t[t]           // good
elevation_t[r]           // bug
elevation_t[t_from_r[r]] // good
elevation_t[r_from_t[t]] // bug
elevation_t[obj.t]       // good
elevation_t[obj.r]       // bug
elevation_t[t_from_r(r)] // good
elevation_t[r_from_t(t)] // bug
elevation_t[x.t_fn(x)]   // good
elevation_t[x.r_fn(x)]   // bug
elevation_t[x.t_arr[i]]  // good
elevation_t[x.r_arr[i]]  // bug
elevation_t[(t)]         // good
elevation_t[(r)]         // bug

What are my options for using tree-sitter to handle more cases? I decided to try the :pred feature of tree-sitter font lock. Using M-x treesit-explore-mode, I learned that the subscript expression looks like:

(subscript_expression 
   object: (_) 
   [
   index: (_)
   ]
)

That means I can match it with a rule that captures both the array and index as separate nodes:

(defun amitp-check-subscript (array index)
  (message "debug: %S %S" array index))

(defvar amitp/treesit-font-lock-typescript-indexing
  (treesit-font-lock-rules
   :language 'typescript
   :feature 'semantic-identifier
   :override t
   `(((subscript_expression
       object: (identifier) @array 
       index: (expression_statement) @index 
       (:pred amitp-check-subscript @array @index))))))

Those two arguments are treesit parse nodes. What can I do with them? The emacs manual has a page about how to navigate the parse tree and another page about querying the parse tree. In the docs found a function treesit-query-capture that can pattern match on a node and extract the parts.

I used that function to look for the identifier that most likely tells me what geometry type is being returned (r for region, s for side, t for triangle). Examples:

  • in t_foo_r(r), the identifier t_foo_r tells me this is likely a t (triangle)
  • in t_obj[i].r_elevation, the identifier r_elevation tells me this is likely an r (region)
  • in map[i].s_inner_t(t_queue[0]), the identifier s_inner_t tells me this is likely an s (side)

I think this is where tree-sitter can do better than regular expression based matching. I wrote a function to find the key identifier:

(defun amitp/treesit-top-typescript-node (node)
  "Try to find the identifier that represents a node's return value"
  (let ((results
         (treesit-query-capture
          node
          '(((identifier) @identifier)
            ((member_expression property: (property_identifier) @property))
            ((subscript_expression object: (_) @expr))
            ((call_expression function: (_) @expr))
            ((parenthesized_expression (_) @expr))))))
    (if (assoc 'expr results)
        (amitp/treesit-top-typescript-node (cdr (assoc 'expr results)))
      ;; prefer the property if tree-sitter matches both
      (or (cdr (assoc 'property results)) (cdr (assoc 'identifier results))))))

I wrote a function detect a mismatch:

(defun amitp-check-subscript-mismatch (array index)
  ;; note: my usual naming convention is amitp/foo but tree-sitter won't accept that
  (let* ((array-name (buffer-substring (treesit-node-start array) (treesit-node-end array)))
         (top (amitp/treesit-top-typescript-node index))
         (index-name (buffer-substring (treesit-node-start top) (treesit-node-end top))))
    (and
     (or (= (length array-name) 1) (equal "_" (substring array-name -2 -1)))
     (or (= (length index-name) 1) (equal "_" (substring index-name 1 2)))
     (not (equal (substring array-name -1) (substring index-name 0 1))))))

Then I used the tree-sitter :pred feature to only highlight if there's a mismatch:

(defface array-error)
(defface index-error)

(defvar amitp/treesit-font-lock-typescript-indexing
  (treesit-font-lock-rules
   :language 'typescript
   :feature 'semantic-identifier
   :override t
   '(((subscript_expression
       object: (_) @array-error
       index: (_) @index-error
       (:pred amitp-check-subscript-mismatch @array-error @index-error))))))
     ;; todo: add call_expression

Does it work? Yes!

Screenshot showing subscript expressions flagged if they seem like they might have a bug

But there's something a little weird here. Why do r+1 and 1+r get flagged even though I never actually handle binary operators in my amitp/treesit-top-typescript-node function? Something to investigate later.

The next feature I wanted was to highlight only the "type" character instead of the entire identifier. I couldn't figure out how, so I asked on Reddit, where /u/eleven_cupfuls gave me a solution: the @face-name can be a @function-name instead. Thank you!

Instead of using :pred to tell font-lock whether to highlight something, I need to highlight it myself. I changed the treesit font lock rules to call my function instead of applying a face:

(defvar amitp/treesit-font-lock-typescript-indexing
  (treesit-font-lock-rules
   :language 'typescript
   :feature 'semantic-identifier
   :override t
   '(((subscript_expression) @amitp-highlight-subscript-mismatch)
     ((call_expression) @amitp-highlight-subscript-mismatch))))

My function now receives the entire expression instead of receiving the array and index parts separately like the :pred version. So I need to find a way to extract the right information. I can use treesit-query-capture like before, right?

It was at this point that I learned treesit-query-capture pattern matches against the entire tree and not only the top node. That explains why it was finding r+1 and 1+r. It was looking for any identifier anywhere in the subtree. Oops.

I rewrote amitp/treesit-top-typescript-node to use treesit-node-type instead of treesit-query-capture:

(defun amitp/treesit-top-typescript-node (node)
  "Try to find the identifier that represents a node's return value"
  (pcase (treesit-node-type node)
    ("identifier" node)
    ("member_expression" (treesit-node-child-by-field-name node "property"))
    ("subscript_expression" (amitp/treesit-top-typescript-node (treesit-node-child-by-field-name node "object")))
    ("call_expression" (amitp/treesit-top-typescript-node (treesit-node-child-by-field-name node "function")))
    ("parenthesized_expression" (amitp/treesit-top-typescript-node (treesit-node-child node 1)))
    (_ nil)))

It would've been cleaner if pcase could pattern match against these nodes, but it's not too bad.

And then I wrote a new amitp-highlight-subscript-mismatch function to analyze subscript nodes (and function calls too) to make sure the names match:

(defun amitp-highlight-subscript-mismatch (node override start end &rest _)
  "Highlight mismatching variable names in amitp's geometry mesh code"
  (let* ((parts (pcase (treesit-node-type node)
                  ("subscript_expression" (list (treesit-node-child-by-field-name node "object")
                                               (treesit-node-child-by-field-name node "index")))
                  ("call_expression" (list (treesit-node-child-by-field-name node "function")
                                           (treesit-node-child
                                            (treesit-node-child-by-field-name node "arguments")
                                            1))))))
    (when parts
      (let* ((array (nth 0 parts))
             (index (nth 1 parts))
             (array-name (treesit-node-text array))
             (top (amitp/treesit-top-typescript-node index))
             (index-name (or (treesit-node-text array) ""))
             (face (if (and ;; todo also check (>= (length index-name) 1)
                        (or (= (length array-name) 1) (equal "_" (substring array-name -2 -1)))
                        (or (= (length index-name) 1) (s-match "[_0-9]" (substring index-name 1 2))))
                       (if (equal (substring array-name -1) (substring index-name 0 1))
                           'index-good
                         'index-bad)
                     nil)))
        (when face
          (treesit-fontify-with-override (- (treesit-node-end array) 2) (treesit-node-end array) face override)
          (treesit-fontify-with-override (treesit-node-start top) (min (treesit-node-end top) (+ 2 (treesit-node-start top))) face override))))))

It's a little bit long but it seems to work:

Screenshot showing subscript expressions in green or red depending on whether the names match

However, there are things I haven't figured out, like why certain expressions don't trigger the (subscript_expression) font lock rule:

r => mesh.foo_r[r]);     // matches
(r => mesh.foo_r[r]);    // matches
foo(mesh.foo_r[r]);      // matches
foo(r => mesh.foo_r[r]); // doesn't match

That's a problem for another day. I also want to:

  1. apply these rules only to files in which I use that naming convention
  2. tighten the matching to apply only to the letters r, s, t
  3. remove the green "it's ok" color and only leave the red "warning" color

This was a fun adventure. I'm not sure this is the best approach but it was a nice excuse to play with emacs tree-sitter.

-1:-- Emacs Tree-sitter custom highlighting, part 3 (Post Amit Patel)--L0--C0--2026-08-16T21:53:52.812Z

Amit Patel: Emacs Tree-sitter custom highlighting, part 2

In the part 1 I described my initial attempt at customizing font-lock using Emacs 29 tree-sitter. Regular font locking uses regular expression matching. Tree sitter allows combining regular expression matching with parse tree matching, like "variable declaration" or "function call". The ideas I came up with were mostly things that I could have implemented with regular expressions alone, but I wanted to experiment with the tree sitter approach. Here's the effect of conventional highlighting, using color for keywords and other syntax:

Screenshot showing no use of color Screenshot showing color for syntax
Screenshots showing the use of color for syntax highlighting

I wanted to instead mark variable names. In some projects I'm working with geometry, and I sometimes make mistakes putting the wrong variables together. I decided to mark horizontal and vertical variables differently. First, I defined a regular expression to guess that a variable represents something "horizontal":

(defvar amitp/regexp-horizontal-identifiers
  (rx
   (seq
    string-start
    (or "x" "dx" "width" "left" "right" "q" "col" "cols" "columns")
    (zero-or-more digit)
    string-end)))

(defvar amitp/regexp-vertical-identifiers
  (rx
   (seq
    string-start
    (or "y" "dy" "height" "top" "bottom" "up" "down" "r" "row" "rows")
    (zero-or-more digit)
    string-end)))

I had started out with a string regexp but switched to the rx syntax. Over time I added more patterns to this, including all caps and camelcase patterns.

Unlike regular expression font locking, I can limit these regular expressions to match within certain types of parse tree nodes:

(defvar amitp/treesit-font-lock-typescript-axis-variables
  (treesit-font-lock-rules
   :language 'typescript
   :feature 'semantic-identifier
   :override t
   `(
     ([(identifier) (property_identifier) (shorthand_property_identifier_pattern)]
      @variable-horizontal-face
      (:match ,amitp/regexp-horizontal-identifiers @variable-horizontal-face))
     ([(identifier) (property_identifier) (shorthand_property_identifier_pattern)]
      @variable-vertical-face
      (:match ,amitp/regexp-vertical-identifiers @variable-vertical-face))
     )))

(add-hook
 'typescript-ts-mode-hook
 (lambda ()
   (setq-local treesit-font-lock-settings
               (append treesit-font-lock-settings
                       amitp/treesit-font-lock-typescript-axis-variables))))

I think the restriction doesn't help much in this case, but it was fun learning how to use treesit-font-lock-rules for this. Here's the result:

Screenshot showing color for variables

Do you notice the bug?

This was a fun experiment. But it's a bit too noisy, especially if I have both variable and syntax highlighting. I think it'd be better if I could highlight only the problematic subexpression (the y2-dx). I decided to try it with a simpler problem.

In some of my projects, I use geometry meshes that have integer IDs for regions (r), sides (s), and triangles (t). I want to avoid bug where an array is indexed by a triangle ID, but I accidentally index it by a triangle ID. Some languages have a feature called "newtype" to make these separate types, but I don't have them in Javascript. Instead, I use a naming convention to help me catch these:

  • x_foo is a value storing type x
  • x_foo_y is a function or array indexed by type y and stores values of type x

I was hoping to use tree-sitter to find subscript expressions, and then check if the array name ends with the same type _x as the index expression x_ starts with. I implemented it for simple cases:

(defvar amitp/treesit-font-lock-typescript-indexing
  (treesit-font-lock-rules
   :language 'typescript
   :feature 'semantic-identifier
   :override t
   `(((subscript_expression) @error
      (:match "_r\\[_?[st]\\|_s\\[_?[rt]\\|_t\\[_?[sr]" @error)))
   ))

And it does work:

Screenshot
Highlighting potential indexing error

However, tree-sitter plays almost no role in this. The real work is done by the regular expression. It doesn't catch these cases:

elevation_t[obj.t]       // good
elevation_t[obj.r]       // bug
elevation_t[t_from_r(r)] // good
elevation_t[r_from_t(t)] // bug
elevation_t[x.t_fn(x)]   // good
elevation_t[x.r_fn(x)]   // bug
elevation_t[x.t_arr[i]]  // good
elevation_t[x.r_arr[i]]  // bug

How would I also handle these cases? I attempted it, but it took more time than I had originally planned for, so that'll be in part 3.

-1:-- Emacs Tree-sitter custom highlighting, part 2 (Post Amit Patel)--L0--C0--2026-08-16T21:53:41.759Z

Amit Patel: Emacs Tree-sitter custom highlighting, part 1

A few years ago I blogged about custom tree-sitter-based syntax highlighting in emacs. I started out with highlighting certain keywords in red. I wanted to show the keywords that interrupt control flow:

Screenshot showing syntax highlighting of keywords in black or red

I highlight regular keywords (while, if) in bold. I highlight control flow interrupting keywords (return, continue) in red.

That's syntax highlighting.

But I also wanted to highlight names based on their meaning.

In some projects I'm working with horizontal and vertical measurements, and I thought it would be nice to highlight those variable names differently. In some projects I'm working with Delaunay+Voronoi meshes, and I wanted to highlight the triangles, edges, and polygons differently. In some projects I'm working with hexagonal grids, and I wanted to highlight the three axes differently, like I do in my guide to hexagonal grids. In some projects I am working with html+js mixed code, and I wanted to highlight the html vs js differently, like I do in my tutorial on how I write interactive tutorials.

In that blog post from a few years ago, I had used emacs tree-sitter mode to implement this type of highlighting. Since then, emacs has incorporated its own tree-sitter mode, which works differently from the third party package. I decided to update my code to work with the new tree-sitter functions.

Let's start with highlighting keywords. This doesn't really need tree sitter mode, but I'm using it because I want to highlight more in part 2 of this post. The treesit-font-lock-rules function compiles tree sitter patterns into some internal format. Since the set of keywords is different per language, I'm going to make one variable per language. Here's the one for Python:

(defface amitp-control-keyword-face)
(defvar amitp/python-treesit-settings
  (treesit-font-lock-rules
   :language 'python
   :feature 'keyword
   :override t
   '(
     (["assert" "await" "break" "continue" "finally" "raise" "return" "yield"]
      @amitp-control-keyword-face)
     )
   ))

This will highlight those keywords in my amitp-control-keyword-face instead of the default font-lock-keyword-face.

A minor note — I had tried reusing my previous tree-sitter face names, which were named with periods (variable.horizontal) and colons (amitp:keyword), but I ran into some errors, so I switched to only using dashes (variable-horizontal) for the new tree sitter modes.

I then have to install these highlighting rules into treesit-font-lock-settings, which is a buffer-local variable. So I install it from a hook:

(add-hook 'python-ts-mode-hook
          (lambda ()
            (setq-local treesit-font-lock-settings
                        (append treesit-font-lock-settings
                                amitp/python-treesit-settings))))

That's all it took to get my keywords highlighting the way I wanted. But wait, there's more!

I had noticed that type aliases didn't highlight the way I wanted. Here's the screenshot:

Screenshot shows that the word 'type' is not highlighted as a keyword

The keyword type isn't being highlighted here. I could add it as a keyword like the others:

(defvar amitp/python-treesit-settings
  (treesit-font-lock-rules
   :language 'python
   :feature 'keyword
   :override t
   '(
     (["assert" "await" "break" "continue" "finally" "raise" "return" "yield"]
      @amitp-control-keyword-face)
     (["type"] @font-lock-keyword-face) ;; <-- added
     )
   ))

Alternatively, I could modify python.el's python--treesit-keywords to include "type".

However, unlike other keywords, type is a soft keyword in Python, not a full keyword. That means it can be used in other contexts such a function call:

Screenshot shows that the word 'type' can occur as both a keyword and an identifier

I don't want to highlight those uses. I used M-x treesit-inspect-mode to learn the tree-sitter parse tree context. And then I made the rules highlight the word type only when it occurred in the context of defining a type alias:

(defvar amitp/python-treesit-settings
  (treesit-font-lock-rules
   :language 'python
   :feature 'keyword
   :override t
   '(
     (["assert" "await" "break" "continue" "finally" "raise" "return" "yield"]
      @amitp-control-keyword-face)
     ((type_alias_statement "type" @font-lock-keyword-face)) ;; <-- added
     )
   ))

Here's the result:

Screenshot shows that the word 'type' when used as a keyword is highlighted differently than when used as a function
"type" keyword vs "type" function

This is one of the nice things I get by using tree-sitter highlighting instead of regex highlighting.

I also changed import statements to be highlighted the way I want, by marking import NAME and from name import NAME in a different face:

  (treesit-font-lock-rules
   :language 'python
   :feature 'definition
   :override t
   '(
     ((import_statement name: (dotted_name (identifier) @font-lock-variable-name-face)))
     ((import_from_statement module_name: (dotted_name (identifier))
                             name: (dotted_name (identifier) @font-lock-variable-name-face)))
     )

I'm still experimenting to figure out what rules I want to have. I try things and then delete some of them. I've found M-x treesit-inspect-mode to be quite useful.

In part 2 I'll explore heuristics to highlight based on variable names I commonly use.

-1:-- Emacs Tree-sitter custom highlighting, part 1 (Post Amit Patel)--L0--C0--2026-08-16T21:53:12.412Z

Irreal: Emacs Backups

Raymond Zeitler over at Ray on Emacs doesn’t use version control for his “non-public” files so when he had a problem with his .emacs file, he didn’t have any backups except for the single last version of a file that Emacs will make automatically if you have backups enabled. That, apparently, was enough to recover but Zeitler resolved to do better.

What he did was to replace .emacs~ with .emacs.~1~, which causes Emacs to create a numbered sequence of backups rather than simply overwriting the last one. Reading his post reminded me of my backup scheme, which I haven’t thought about in years because all my files—except autogenerated ones—go into Git.

My backup scheme is a little different from Zeitler’s because I’ve set mine to keep only the last two backups. That way, my file system doesn’t fill up with long forgotten and unneeded backups. Even so, there were a lot of backups—some for files that don’t even exist anymore—taking up a lot of space. I deleted them all and started over.

I don’t claim that 2 is the right number for everyone or even for me. It just seemed like a safe number in case something happened with Git. Happily, I’ve never had to use the backups, which is why I’d mostly forgotten about them.

One thing for sure, you need to be religious about either saving your files to a version control system or configuring the Emacs backup system to automatically save some number of backups for you.

-1:-- Emacs Backups (Post Irreal)--L0--C0--2026-08-16T15:04:42.000Z

Alvaro Ramirez: Focus windows across Emacs frames

Historically, I've only ever known two great ways of focusing Emacs windows: the built-in other-window command, which cycles through all the available windows, and the ace-window package, offering random window access. It's not that there aren't more or better ways, I just didn't look any further as these were enough for my needs.

While I really wanted to make ace-window my default choice, for whatever reason, it never stuck. other-window with a custom M-o binding always felt like the smoother fit for my limited needs. You see, I hardly ever have more than two visible windows, so whenever ace-window kicked into action (for 3 or more windows), it often took me by surprise. The one area other-window didn't fit my needs revolved around focus requiring visual feedback, but I eventually solved that with winpulse (a little package I wrote).

As you can see, I'm a simple man using few Emacs windows, and when it comes to frames, I almost never use more than one. That is until somewhat recently, when I built ytr, a tiny YouTube radio player that sits in the corner of your frame. While it all feels fairly integrated into your frame, under the hood, ytr renders in a separate frame. This broke my trusty other-window flow. I couldn't just focus my radio window using my well-internalized M-o binding.

Turns out, other-window actually caters for focusing windows across frames, but only when invoked programmatically. Sure, I can wrap it with my own custom command, but Emacs already had me covered. I found the built-in next-window-any-frame command. All I had to do was bind it to M-o and Bob's your uncle.

I can now switch between my current window and my YouTube radio, with my dear M-o binding. Balance restored.

-1:-- Focus windows across Emacs frames (Post Alvaro Ramirez)--L0--C0--2026-08-16T00:00:00.000Z

Irreal: Emacs Users

Chris Maiorana has a lovely blog post in which he makes the case that Emacs users are a different breed. He starts with the quotidian observation that you can be pretty sure that a Vim or VSCode user is either a software developer or in a closely related such field such as System Administration or DevOps. Emacs users, on the other hand, could come from almost anywhere.

This is a point that Irreal is fond of making. I’m fascinated by people from outside the software field who use Emacs as an essential part of their workflow. Their stories always illustrate the power and flexibility of Emacs to solve problems even if they aren’t about software and engineering.

What distinguishes Emacs users, Maiorana claims, is their love of tinkering and the blurring of work and play when they’re using Emacs. As he puts it

Emacs could be more than a text editor. It could be an organizer, a scratchpad, a dictionary, a canvas, a database, a calendar, an email client, and much more. What should have been work started to feel like fun.

Maiorana says that Emacs users don’t care primarily about the usefulness or user friendliness of Emacs; it’s the opportunity for tinkering that attracts them. I think he has this part backwards. Emacs users surely do love to tinker but the attractive thing about Emacs is that your tinkering can turn it into the exact tool that you need. That’s true even if the tool you need has nothing to do with software or its siblings.

Emacs is, in fact, the perfect piece of software. Not only does it indulge our urge to tinker and have fun but also allows us to turn that tinkering into the precise tool we need. Not just once but as often as we desire as our needs change. With Emacs, the fun never ends.

-1:-- Emacs Users (Post Irreal)--L0--C0--2026-08-15T15:29:25.000Z

Andros Fenollosa: Elpher: my tweaks for better Gemini browsing in Emacs

Elpher is the Gemini browser for Emacs. It works wonderfully and its integration with GNU's editor is sublime. You don't just move between capsules: you can also write your own scripts or build automated flows. It's Emacs, after all. Everything bends to your will.

If you still don't know what Gemini is, it's a minimalist protocol, halfway between the web and Gopher, with no JavaScript and no trackers. I cover it in depth in Introduction to the Gemini protocol.

Today I want to share a minimal configuration I use to browse more comfortably. Nothing exotic, just a few shortcuts and a couple of tweaks that make a difference day to day.

Elpher is on MELPA. I declare it with use-package, so everything stays in a single block: :ensure t installs it the first time, :config defines the function and sets the protocol, and :bind keeps the shortcuts encapsulated. Paste this into your init.el.

;; ===
;; elpher - Gemini and Gopher client
;; https://thelambdalab.xyz/elpher/
;; ===
(use-package elpher
  :ensure t
  :config
  ;; Smart link following that handles emojis
  (defun elpher-smart-follow ()
    "Smart link following that handles emojis."
    (interactive)
    (let ((current-pos (point)))
      (beginning-of-line)
      (if (re-search-forward "=>" (line-end-position) t)
          (elpher-follow-current-link)
        (goto-char current-pos)
        (elpher-follow-current-link))))

  ;; Gemini as the default protocol instead of Gopher
  (setq elpher-default-url-type "gemini")

  :bind
  (:map elpher-mode-map
        ("RET" . elpher-smart-follow)      ; follow link, emoji-aware
        ("l" . elpher-back)                ; back, same as eww
        ("n" . elpher-next-link)           ; next link
        ("p" . elpher-prev-link)           ; previous link
        ("b" . elpher-bookmark-current)))  ; bookmark, same as eww

What does all this give you? A few improvements you notice right away:

  • n to jump to the next link.
  • p to go back to the previous one.
  • l to return to the previous page, just like in eww.
  • b to save the current page as a bookmark, just like in eww.
  • It widens the clickable area of each link, so you can click on the emojis and not only on the text.
  • It sets Gemini as the default protocol instead of Gopher.

Watch out with elpher-smart-follow: it reassigns RET. If you already had that key mapped to something else inside elpher-mode, this overrides it.

That last note about emojis seems trivial, but it changes the experience a lot. Many capsules decorate their links with an emoji in front, and if you try to follow them from there, Elpher just sits still. With elpher-smart-follow it no longer matters where the cursor is on the line: if there is a =>, it takes you there.

And this is only the beginning. Elpher is Lisp, and Lisp bends to whatever you need: today it is four shortcuts, tomorrow a flow that opens your favorite capsules when Emacs starts. Tinker with it, break it, make it yours.


Help me keep writing Every coffee gives me a push toward the next article. Sure, it's on me!

Send an email to comment+article-b18ae804@andros.dev to leave a comment. The subject will be ignored.

-1:-- Elpher: my tweaks for better Gemini browsing in Emacs (Post Andros Fenollosa)--L0--C0--2026-08-15T14:07:40.000Z

Mike Hostetler: No Graph Needed

This month's Emacs Carnival theme is The Search For Knowledge, which is kinda something near and dear to my heart. I wrote an Obsidian to Emacs post from a little over a year ago and one of the things that I mentioned is that Obsidian has a Knowledge Graph that I didn't use. denote-explore does have one and I set graphing up once but I don't use it at all. I work just fine without a Knowledge Graph. Using Denote, ripgrep, and consult, things work great for me. It's smooth and not a lot of fuss.

I literally do all my knowledge management now within Emacs in a Denote system. I dump all of my notes into one folder in org format. Denote puts a time stamp on the file name as well as the title and the tags. Searching by tag is really just a filename search. See examples from my own denote folder:

20260714T130657--org-agenda-views__orgmode.org
20260714T100032--filtering-org-ql-results__orgmode_productivity.org
20250605T091129--escape-character-in-orgmode__orgmode.org

For me the file name is the index. It's simple. I don't need a SQLite database or anything else on the backend. As soon as I create a file I know that I can search by the file name or search its contents. This keeps things simple and, for me, simple is good. When I want to search for all of my notes about orgmode, I just run my mh/consult-denote-find-files , type orgmode and all of those files appear. If I want to add something else to that search, I just type that in. Here is my simple function that does that, using consult-find

(defun mh/consult-denote-find-files ()
  "Find files in the Denote notes directory by name via `consult-find'."
  (interactive)
  (consult-find (car (denote-directories))))

So that brings up the files using the wonderful consult-find format. I just keep typing to narrow down my search. Easy and effective.

And inside the files, the denote tags are the same as org mode tags when the document is open since it uses the TAGS property on the top of the file. I generally use just ripgrep for that – see the function I use below:

  (defun mh/rg-org-wiki ()
  "Search ~/org-wiki with ripgrep via `consult-ripgrep'.
Follows symlinks and ignores JSON files."
  (interactive)
  (let ((wiki-dir (expand-file-name "~/org-wiki")))
    (if (file-directory-p wiki-dir)
        (let ((consult-ripgrep-args
               (concat consult-ripgrep-args " --follow --glob=!*.json")))
          (consult-ripgrep wiki-dir))
      (message "Directory ~/org-wiki does not exist!"))))

(yes, my denote files are in a folder called org-wiki. That actually dates back to a pre-Obsidian setup I had once. This function actually dates back to that but it's easy to reuse here. Again, another win for Emacs and simplicity)

I do have denote-explore installed and I do use denote-explore-random-note to, well, explore my notes. I find things that I forgot, and find things to update. I use that generally when I just want to work in my notes for a while.

But that is the exception and not the rule. I set up links to explore my notes. Using the example files above, I use denote-link to link to a top-level OrgMode note (since they're all about OrgMode). That note is kind of a Map Of Content about OrgMode. That is how I want to discover notes
 the individual notes link to top-level notes and that note is linked to all else about OrgMode. Well that is the theory anyway
 I set it up but I don't do it near as much as I thought I would. This goes back to how I used Obsidian. I tried really hard to use a very rigid and disciplined link structure and at the end I find I didn't use it. It took more time than writing a lot of my notes. So really my flow is – I find and read notes that my past self used and update them to what I currently think but I don't necessarily follow a web of links. While a graph is useful for a lot of people, it just doesn't seem as useful for me. The filename is a good enough index for me.

-1:-- No Graph Needed (Post Mike Hostetler)--L0--C0--2026-08-15T05:00:00.000Z

Alvaro Ramirez: agent-shell 0.73 updates

Another month, another agent-shell update. If you missed the last one, have a look at the 0.63 update. While this post showcases the latest highlights, the full list of changes is far chunkier than what we'll cover.

What's agent-shell?

agent-shell is a native Emacs mode to interact with AI agents powered by ACP (Agent Client Protocol).

agent-shell enters the chat

Since inception in September last year (yikes nearly a year), agent-shell has featured a shell-like experience, powered by comint mode. There's also viewport mode (via agent-shell-prefer-viewport-interaction), if you prefer a more focused experience, and now we have chat mode.

Chat mode fuses comint mode with a more traditional chat-like labelling experience.

We're living on the edge here, so chat mode is now enabled by default. Ok not really that edgy, it's fairly safe (powered overlays) and can be disabled entirely via (setq agent-shell-chat-mode-enabled nil). Chat mode itself is a minor mode, so you can always toggle it on and off via M-x agent-shell-chat-mode.

A little more chatter

In the last post, we talked about making agent-shell less chatty with more grouping for the likes of tool calls and agent thinking, all collapsed by default. If that's far too quiet, you can expand by default via (setq agent-shell-activity-group-expand-by-default t). If you found these two settings either too quiet or too chatty, we now have a third alternative via (setq agent-shell-activity-group-expand-by-default 'latest). When set, only the latest grouped activity is expanded by default, and automatically collapsed when the agent moves on to something else.

I've become quite fond of this feature (thank you @nhojb for the PR), so yes. It's also enabled by default.

Prompt queueing

Queuing received some improvements. The related commands have been consolidated under agent-shell-prompt-queue.el. You can queue prompts while the agent is busy, then view, resume, or drop pending prompts via M-x agent-shell-prompt-queue, M-x agent-shell-prompt-queue-resume, and M-x agent-shell-prompt-queue-remove. The pending queue is now shown after each new submission.

Compose from anywhere

M-x agent-shell-prompt-compose opens a dedicated buffer for crafting a prompt, and it's now more independent of the shell. You can invoke it from any buffer (it resolves to the right shell), C-c C-c sends and returns you to whatever you were doing (fire and forget), and C-u C-c C-c submits the prompt and immediately lets you craft another queued prompt.

Start typing sooner

Shell initialization may take a second or two, depending on what agent you're using, which meant you had to wait for initialization before you could start typing into your new shell. Unnecessary, so that's no longer the case. Shell prompts are now offered as soon as possible, so one can get typing.

Markdown lists

While Markdown lists are easily digestible without special rendering, we can do better than that, so we now give them a better treatment with normalized padding, indentation, and of course, civilized bullets.

TAB navigation improvements + hints

TAB navigation made it into agent-shell fairly early on. I love being able to TAB my way into any section in the buffer and press RET to toggle folding. That's great and all, but we can make the navigation experience richer by welcoming the likes of Markdown source blocks, links, and images to the navigation party. Why these in particular? They are all actionable by RET too, of course.

While you'd rightly assume RET opens links to local text files in Emacs and delegates to browsers when needed, Markdown source blocks and images get a similar treatment. While their respective RET actions may not be as obvious, we can certainly make them much more discoverable, so we now add hints. Landing point on an actionable item now echoes a hint of what you can do and which key does it (say, "Press RET to copy" on a source block, "Press + to enlarge" on an image, and so on). Hints are also shown on mouse over events.

Image size hints

While agent-shell offers agent-shell-markdown-image-max-width for customizing image sizes, it's fairly restrictive. Not all images are the same, so why force them all to fit within the same constraint? agent-shell-markdown-image-max-width continues to offer a preferred default, but you can now scale images differently by getting the agent to annotate Markdown images with Pandoc-style link attributes.

The attribute block goes right after the image, taking a width and/or height in pixels or percentages:

![alt](image.png){width=300}
![alt](image.png){width=50% height=200}

Auto resize

I may be sweating the small stuff here, but this was really grinding my gears. We have lovely Markdown table rendering, which I'm glad we do as LLMs aren't always great at producing perfectly aligned tables. In the best of cases, the LLMs align the table perfectly, but it's just too wide for our Emacs window. Luckily, our lovely rendering also wraps cells to make them fit into our window. The thing is, all that lovely rendering goes out the door the moment you either resize your Emacs frame or merely split your window, resulting in a monstrosity like this:

I know. I'm sweating the small stuff here, but hey we don't have to live like this. Emacs has all the hooks in the world, so let's track window changes and rejoin the civilized world.

On a much smaller scale, I also wanted auto resize for images, so here you have it


Manual resize

While we can influence image size at render time, this can still generate undesirable image dimensions, so we can now rescale all images in buffer on demand.

Sometimes a hammer really isn't the right tool, so we can now also rescale an image at point.

Open file links as you please

Opening a local file (from a link, image, or @file mention) now routes through agent-shell-file-display-action, a standard display-buffer action you can customize. The default reuses a window already showing the file, or takes over the current one.

If you'd like a different window arrangement, you could do something like:

(setq agent-shell-file-display-action
      '(display-buffer-pop-up-window))

Xref support

Following a local file link now pushes your origin onto xref's marker stack, so xref-go-back (M-,) brings you right back to where you were in your agent-shell, just like other Emacs jumps.

Use your favorite folding key binding

Foldable fragments now use agent-shell-ui-fragment-map and bind agent-shell-ui-toggle-fragment. If you're not a fan of the RET binding to toggle folding, you can now use your preferred binding.

If TAB is your jam, you can do something like:

(with-eval-after-load 'agent-shell-ui
  (define-key agent-shell-ui-fragment-map
              (kbd "TAB") #'agent-shell-ui-toggle-fragment))

Styling thoughts

If you prefer styling agent thoughts differently, a new agent-shell-thought-body face lets you do just that.

Streaming performance

Streaming performance also received some love. Thanks to @suhail-singh for the profiling and improvements, and to @Scott-Guest and @claytharrison for the trace analysis and benchmarking in #757.

Richer display of options

The "Available config options" section now displays possible values.

Public functions

Dynamic agent configs

agent-shell-agent-configs can now be set to a function, letting you compute the available agent configurations dynamically rather than hard-coding a static list. The function is called on every access, so it stays current across code reloads.

Maybe you'd like to list only available agents. Here's a rough snippet.

(setq agent-shell-agent-configs
      (lambda ()
        (seq-filter
         (lambda (maker)
           (when-let* ((client (ignore-errors
                                 (funcall (map-elt (funcall maker) :client-maker)
                                          (current-buffer))))
                       (command (map-elt client :command)))
             (executable-find command)))
         (agent-shell-default-agent-config-makers))))

Subscribe to agent message chunks

agent-shell-subscribe-to now broadcasts an agent-message-chunk event, handy for external integrations that want to observe streamed output.

Remote agents

executable-find now works correctly on remote hosts (#742 by @CeleritasCelery), smoothing out TRAMP-driven remote agents.

New third-party packages

agent-shell-hq joins the family, offering an interface for managing multiple agent-shell sessions.

Maintenance and housekeeping

If you peeked at the commit logs, you'll notice I've been working daily on agent-shell, keeping up with project inflow. Since last month, 27 issues have been closed and 13 pull requests merged. As of today, the backlog sits at 11 open issues and 5 open PRs (versus 13 and 4 last time around). If there's something you'd like me to prioritize, feel free to ping.

agent-shell needs your support

Vendor-neutral tooling matters more than ever, and there are a couple of ways to help keep agent-shell going. Some cost money, others just a click. All are appreciated ;)

Sponsorships for agent-shell longevity

agent-shell is just me, an indie dev, while the tools it competes with have well-funded teams behind them. Time spent on agent-shell is time away from work that pays the bills, so if it's useful to you, please consider sponsoring the project. And if your employer benefits from your agent-shell use, nudge them to chip in too, they can typically contribute at a scale individuals can't.

GitHub stars for exposure

GitHub stars help with exposure, attracting new users and potential sponsors. Starring agent-shell costs nothing and can potentially help bring in more funding, so if you don't mind a couple of clicks, the project can really use another GitHub star.

Pull requests

Thank you to all contributors for these improvements!

Make it sustainable, sponsor the agent-shell

Liking agent-shell? Would like to see it evolve? Consider sponsoring the effort.

-1:-- agent-shell 0.73 updates (Post Alvaro Ramirez)--L0--C0--2026-08-15T00:00:00.000Z

Irreal: đŸ„© Red Meat Friday: The Perils Of Rewriting

The Emacs Cat, of whom I’ve written many times, has a post on a broken change in Ubuntu that affected Emacs. The TL;DR is that the Cat upgraded to Ubuntu 26.4 but when he tried to use Emacs he discovered a problem with Dired. At first he thought the problem was with Emacs 30.2, which is the default Emacs with that version of Ubuntu. But the actual problem turned out to be with Ubuntu’s implementation of ls. The Cat solved his problem by using the “old” version of ls from GNU coreutils. Ubuntu had decided that it was a good idea to rewrite the core Unix utilities—such as ls​—in Rust.

Maybe I’m just old and grumpy but this seems to me to be a terrible idea. Utilities like ls have been around and working more or less flawlessly for close to 50 years. What, really, was the reasoning behind the decision to rewrite ls? Twenty six years ago, Joe Spolsky answered that question and explained why the rewriting is almost always a bad idea.

It may have been written a quarter of century ago but Spolsky’s explanation remains true. There really is no reason—other than fashion—to rewrite core utilities that have been working perfectly well for longer than many of us have been alive. When you do, you can expect exactly the sort of problem that the Cat stumbled on.

The problem with the new ls was very specific involving an obscure parameter but it reached out to disable a major Emacs functionality and probably affected other applications as well. The annoying thing is that there really wasn’t any reason for this to happen: just someone who decided it would be a good idea to rewrite an application that was working fine.

-1:-- đŸ„© Red Meat Friday: The Perils Of Rewriting (Post Irreal)--L0--C0--2026-08-14T15:32:50.000Z

Chris Maiorana: The Emacs pastoral tradition

You’ve probably noticed, Emacs users tend to be a different breed than other computer users. While you can be reasonably sure that anyone who uses vim or VSCode is probably a developer, or someone who works in software engineering, devops, or systems administration, Emacs users can be from virtually anywhere else on the map.

While conventional software has the basics—like users, functions, documentation, and best practices—Emacs has a tradition.

In this article, we’ll look at:

Table of Contents

Let’s get nutty.

The Emacs pastoral tradition

Emacs users may come from career backgrounds or intellectual disciplines that have almost nothing to do with software or computers in general, but they have simply found their way into the “cult” of Emacs from pursuits like:

  • Academics
  • Writing and literature
  • Civics
  • Philosophy
  • Law
  • Medicine
  • Music

(And more, I’m sure.)

I’ll use myself as an example. In my technical writing career I was searching for a more optimized text editor. I started learning vim, but switched to Emacs because, as it turns out, I wasn’t looking for a text editor so much as an “operating system” for writing.

Emacs could be more than a text editor. It could be an organizer, a scratchpad, a dictionary, a canvas, a database, a calendar, an email client, and much more. What should have been work started to feel like fun.

This is what I’ve started calling the Emacs pastoral tradition.

Pastoral themes in literature evoke images of shepherds ostensibly tending to a flock, but really engaging in more leisurely pursuits like writing poetry and music. The point being that it’s the leisurely pursuits that uplift the soul and are thus more important than mere toil, even if toil is useful and productive.

Emacs is a kind of playground where we can bring our unique ideas and favorite hobbyhorses and have fun building systems around them.

The pastoral also applies to the church: the pastor as the shepherd, and the ones being sermonized to as the flock. Emacs is already jokingly referred to as a cult, so I need not belabor this point.

Doing “useless” work can be useful

What we do in Emacs can look a lot like work, and sometimes it is, but often it’s leisure disguised as work.

This might include:

  • Writing a blog post in Org Mode (as I’m doing now).
  • Tweaking your init.el file to approach that ever-illusive perfect configuration.
  • Auditioning new packages out of curiosity.
  • Trying to play Doom or surf the web in Emacs.

I’d go so far as to say that there are some entire projects out there, such as EXWM, that can be described as leisure.

Does anyone really need an Emacs window manager? No, but that’s the point. It’s an aesthetic choice rather than a purely rational one. An Emacs window manager is no more useful than any other.

I’ll paraphrase Roger Scruton in saying:

There’s nothing quite as useful as the truly useless.

But even useless pursuits can be useful, even if the only useful aspect is the fun of leisurely non-productivity.

The tool doesn’t always matter

Emacs pursuits can be productive endeavors, but they need not be in order to justify our time.

There are lots of productivity tools out there that are probably better than Org Mode. Or, at the very least, an intelligent critic could make an impassioned case for why Org Mode is lame, with multiple bullet points and peer-reviewed references; but it would not be persuasive at all to a sufficiently dedicated Org user.

For Emacs users, the user-friendliness or usefulness of the tool is the not the key point. The promise of endless tinkering is more tantalizing than a tool that simply works all the time. That’s why we’re always tweaking but never really landing on a static configuration. Emacs itself is a running Lisp image you can manipulate on the fly. At this level of interaction, the margins between work, experimentation, and play become invisible, or at least quite porous.

Some people believe we may on the cusp on a new era of non-production. You know as well as I do that there’s more hype than substance around AI at the moment. However, in the brave new world, there’s a non-zero chance that the machines will take over all the toil and leave us free to indulge in a new age of pastoral leisure. Sitting under shady trees and composing romantic sonnets in iambic pentameter sounds pretty good to me.

For people who derive value and validation from productivity, this is a threat. But for Emacs users it might be what we’ve always wanted.


If you enjoyed this ramble, here are some next steps for your consideration:

Thanks for reading. See you next time!

The post The Emacs pastoral tradition appeared first on Chris Maiorana.

-1:-- The Emacs pastoral tradition (Post Chris Maiorana)--L0--C0--2026-08-14T04:00:24.000Z

Sacha Chua: Emacs Chat 28: Fabrice Niessen (en français)

Nous avons parlé en direct avec Fabrice Niessen (Formation Emacs Boost) d'Emacs, de ses flux de travail, de sa configuration, et d'autres sujets en français.

J'ai essayé de corriger la transcription, mais je crois qu'il y a encore beaucoup d'erreurs, donc n'hésitez pas à m'envoyer un message.

  • 0:00 Introduction
  • 1:46 Le parcours
  • 4:15 Emacs sur Windows : Cygwin et WSL
  • 8:26 Les automatisations
  • 12:15 Emacs Boost
  • 21:15 Emacsclient
  • 28:49 org-html-themes
  • 35:02 Review
  • 37:25 PDF
  • 40:03 Key-chord
  • 45:41 Copier sans sĂ©lectionner
  • 48:29 fill-paragraph
  • 50:02 Les macros
  • 51:02 Volatile highlight
  • 52:47 Les tableaux Org
  • 55:43 Les formations et les cours particuliers

La transcription un peu corrigée

Details

0:00 Introduction

Sacha: Ça marche!

Prot: Maintenant, nous sommes en direct.

Sacha: SuccĂšs! Bonjour Ă  toutes et Ă  tous et bienvenue dans le deuxiĂšme Ă©pisode d'Emacs Chat en français. C'est le 28e Ă©pisode de la sĂ©rie. J'espĂšre que j'ai lancĂ© la diffusion correctement cette fois. Aujourd'hui, notre invitĂ© est Fabrice Niessen, un consultant informatique qui habite en Belgique et qui a beaucoup de dĂ©pĂŽts dĂ©diĂ©s Ă  Emacs et Ă  Org Mode, tels que le kit de configuration Leuven et le thĂšme de publication d'Org Mode org-html-themes. Il propose aussi des formations Ă  Emacs en prĂ©sentiel Ă  Paris, Ă  Rotterdam et dans d'autres villes. Merci d'ĂȘtre lĂ , Fabrice.

Fabrice: Merci pour l'invitation. C'est trĂšs sympa de votre part.

Sacha: Merci aussi Ă  Prot de m'aider pour la conversation. En fait, je pense que mon niveau de français est un peu trop faible pour ce genre de conversation, mĂȘme avec le sous-titrage automatique de mon navigateur. Donc, je vais Ă©tudier le sous-titre aprĂšs la conversation pour mieux en tirer parti. Nous faisons de notre mieux ensemble.

Prot: Merci. Je t'en prie, je t'en prie. Non, de fait, trĂšs bien, trĂšs bien.

Sacha: C'est parti. Fabrice, tu utilises Emacs depuis trĂšs longtemps. 1999, c'est plus logique en Belgique.

Fabrice: Absolument. La Suisse est encore mieux avec le 8 ans.

Sacha: Oui.

1:46 Le parcours

Sacha: Comment t'as-tu découvert Emacs?

Fabrice: En fait, j'ai dĂ©couvert Emacs en 95, lors de mon stage de fin d'Ă©tudes. J'ai Ă©tĂ© dans une entreprise pendant six mois et lĂ , ils utilisaient Linux. Enfin, Unix plutĂŽt, un serveur Unix. Et donc, j'ai dĂ» apprendre Ă  dĂ©velopper sous Emacs. Alors, j'ai appris les keybindings, j'avais la refcard imprimĂ©e avec un stabilo pour souligner tous les trucs que j'apprenais au fur et Ă  mesure. Ça m'a beaucoup aidĂ© d'ailleurs, d'en apprendre comme ça, C-x C-s, et puis tous les suivants. Et aprĂšs les six mois, c'Ă©tait donc en 95, c'est l'Ă©poque de la sortie de Windows 3.11, je pense. J'avais un ordinateur avec Windows 3.11. AprĂšs, j'ai repris les habitudes point and click, donc des interfaces graphiques. Jusqu'en 99, Ă  peu prĂšs 99, oĂč un de mes collĂšgues, Dominique, utilise Emacs. Et en fait, Ă  l'Ă©poque, on avait dĂ©jĂ  du versionning au boulot et c'Ă©tait PCL CVS, c'Ă©tait CVS le versionneur, donc un ancĂȘtre de Subversion et un ancĂȘtre de Git. Et il y avait un mode PCL CVS d'utiliser dans Emacs. Et quand j'ai vu ça une fois, c'est en fait un peu ça qui m'a fait sauter le pas de me dire il faut que je retourne parce que retourne regarder Emacs. Parce que c'Ă©tait beaucoup plus simple pour lui, il avait modifiĂ© ses fichiers dans Emacs, il avait son petit truc oĂč il sĂ©lectionnait les quelques fichiers qu'il voulait committer, il les sĂ©lectionnait, il faisait je ne sais plus quel key binding, et puis il mettait son message de commit, C-c C-c, et voilĂ , c'Ă©tait fait. Et donc quand j'ai vu ça, alors que moi, il fallait que je passe dans le shell, il fallait que je fasse sans doute des CVS add ou des choses comme ça, des quelques fichiers, c'Ă©tait beaucoup plus long. Beaucoup plus compliquĂ©. Donc, j'ai recommencĂ© Ă  me rĂ©intĂ©resser Ă  Emacs. J'ai utilisĂ© son .emacs et puis j'ai retirĂ© un petit peu de trucs et j'en ai rajoutĂ© Ă©normĂ©ment. Et c'est comme ça que ça a commencĂ©. Et voilĂ , c'est fantastique. Évidemment, une fois qu'on rentre dans Emacs, c’est-Ă -dire le C-s, rien que ça, la puissance du C-s, l’incrĂ©mental search, c’est dingue. Donc, c'est comme ça que j'ai commencĂ© et puis je n'ai pas pu m'arrĂȘter.

4:15 Emacs sur Windows : Cygwin et WSL

Sacha: Tu utilises Emacs sur Windows.

Fabrice: Exactement. Donc, effectivement, Ă  l'Ă©poque, j'ai fait plusieurs tentatives. J'ai utilisĂ© Cygwin, donc j'ai utilisĂ© Emacs ou Cygwin. Je vais aussi utiliser le Emacs natif. Ce qui Ă©tait compliquĂ©, parce que j'Ă©crivais quand mĂȘme aussi beaucoup de shell scripts, etc. Donc lĂ , c'Ă©tait dans mon Cygwin. Avec les Emacs natifs, il y avait les montages, le /mnt, etc. Donc c'Ă©tait surtout le Emacs Cygwin que j'utilisais. Et depuis maintenant 3-4 ans, 4-5 ans, Windows a eu la gĂ©niale idĂ©e de mettre le WSL, donc Windows Subsystem Linux, et donc dans mon Windows, j'ai un vrai Linux, encore mieux, qui est plus puissant que Cygwin encore. Et tout est prĂ©vu, tout est interconnectĂ©. Donc je peux passer, je peux ĂȘtre dans le monde Linux et voir mes fichiers qui sont sur le c: et vice versa, ce qui Ă©tait plus compliquĂ© avant. Donc voilĂ , c'est fantastique et donc j'utilise maintenant WSL. Je n'ai plus que ça et ça marche extrĂȘmement bien. Et c'est mon environnement de travail.

Prot: A-t-il des difficultés d'utiliser Emacs sur Cygwin ? Ou c'est comme WSL ?

Fabrice: C'Ă©tait un Ă©mulateur. Il y avait Ă  peu prĂšs tout ce que je voulais quand mĂȘme. Mais il n'y avait pas tous les outils. Peut-ĂȘtre des outils style... Non, et encore, je dirais des bĂȘtises. Top, je pensais Ă  top, etc. Mais je pense que non, ça, c'Ă©tait aussi implĂ©mentĂ©. Mais bon, il y avait un certain souci. Il y avait un certain nombre de commandes implĂ©mentĂ©es. Ici, c'est un vrai Linux. Et alors avec sudo apt install, tandis que sous Cygwin, il fallait installer des packages sĂ©parĂ©ment. Donc on a une petite interface, on sĂ©lectionne les packages. Ici, je recopie ce que je peux trouver sur le web. Si je veux installer R, j'utilise le langage R, sudo apt install R et des choses comme ça. Donc c'est beaucoup plus facile. C'est vraiment typiquement tout ce que les gens font qui n'ont que Linux. C'est exactement les mĂȘmes commandes. Je trouve ça beaucoup mieux, c'est beaucoup plus intĂ©grĂ©.

Sacha: Je pense que c'est aussi plus vite qu'utiliser Emacs sur Windows directement parce que Magit, par exemple, Magit est trop lent, je pense, si tu utilises Magit sur Windows directement.

Fabrice: Ça c'est un truc, c'Ă©tait dĂ©jĂ  pareil avant avec Cygwin aussi. Au dĂ©but, j'avais installĂ© mon WSL Linux, donc il est montĂ©, et j'avais mis Emacs, je pense... Attends, comment ça allait ? Non, je mettais mes repos Ă  l'extĂ©rieur, dans le C:. Et en fait, lĂ , il y a quand mĂȘme un passage entre les deux mondes qui est extrĂȘmement lent. Rien que pour lancer Emacs, mon Emacs se lance en une Ă  deux secondes. LĂ , dans ce cas-lĂ , ça se lançait en une ou deux minutes. Donc, il y a une diffĂ©rence Ă©norme. Donc, en fait, il faut vraiment tout mettre, vivre uniquement dans le monde WSL, dans le monde Linux. Donc, mes repos sont dans Linux. Mon Emacs, il est dans mon Linux. Mais en fait, les fenĂȘtres sont partagĂ©es. C'est ça aussi qui est bien. Je peux avoir une fenĂȘtre, ma fenĂȘtre Emacs, elle est Ă  cĂŽtĂ© d'une fenĂȘtre Outlook, par exemple. Et les presse-papiers, etc. sont complĂštement interconnectĂ©s aussi. Donc, c'est vraiment comme si c'Ă©tait une application Windows. Sauf que rĂ©ellement, elle est dans le sous-systĂšme Linux. Mais il faut vraiment mettre tout dans le sous-systĂšme Linux pour que les performances soient potables. Sinon, il y a quand mĂȘme des lenteurs pour les fichiers qui sont lus d'un domaine Ă  l'autre, d'un espace Ă  l'autre. Les shared files, quoi. Tout ce qui passe par le point de montage est quand mĂȘme beaucoup plus long.

8:26 Les automatisations

Sacha: Maintenant, tu as un environnement plus pratique pour tes automatisations. Parle-moi de tes automatisations préférées pour les tùches répétitives.

Fabrice: Volontiers. En fait, j'ai préparé un fichier Org, évidemment, pour préparer cet entretien. Et donc, vous voyez mon écran ou je dois partager ? Oui, vous le voyez, c'est ça ? Toujours ?

Prot: Oui, oui, oui, oui, je peux voir ton écran.

Fabrice: Donc, voilĂ . VoilĂ , ici, j'ai mon Emacs en grand. Et vous voyez dans le titre ici ? Le titre, je le... Oui, toutes les 20 secondes, il y a un petit truc comme ça. Toutes les 20 minutes, il faut poser les yeux pendant 20 secondes. Toutes les 20 minutes, j'ai un programme qui m'interrompt pendant 20 secondes. Ça va encore arriver deux fois tout Ă  l'heure. Et donc ici, dans le titre, vous voyez GNU Linux Emacs. Donc, on voit vraiment que c'est l'Emacs du GNU Linux. Et avec ici, j'ai la date dans laquelle il avait Ă©tĂ© compilĂ©. Et puis j'ai le PID aussi. Parfois, dans le temps, j'avais plusieurs Emacs, donc j'ai le PID qui est aussi indiquĂ©. Comme ça, quand je devais faire un kill, ça arrivait parfois de devoir faire des kills, je savais au moins lequel il fallait que je kill, parce que j'avais l'information directement dans le titre. Donc ici, j'ai une petite prĂ©sentation Org. Avec un agenda. On a parlĂ© de mon parcours, de comment j'ai fait la connaissance d'Emacs. Tu posais des questions sur diffĂ©rents repositories que j'ai rendus publics sur YouTube. Il y a le fameux thĂšme de couleur Leuven. Leuven, pour information, c'est la ville oĂč j'habite.

Prot: Une belle ville.

Fabrice: TrĂšs belle ville, absolument. Et voilĂ , j'ai un peu copiĂ© Till Tantau, je pense, le crĂ©ateur de Beamer, qui avait donnĂ© des noms pour ses thĂšmes, Madrid, etc., Hanover, lĂ  oĂč il Ă©tait passĂ© faire des sĂ©minaires. Donc c'est comme ça que j'ai pris le vƓu au dĂ©but. D'ailleurs, ici, vous voyez ce thĂšme Leuven Theme qui est appliquĂ© avec des titres plus gros pour les sections de premier niveau et puis des couleurs un peu diffĂ©rentes pour les to-do keywords, pour les titres, pour les sections, pour les tags, etc. Il y a des morceaux qui sont dans Leuven theme, il y a des morceaux qui sont dans ma configuration Emacs. Le thĂšme de couleurs s'appelle Leuven Theme et la configuration Emacs. Pour l'instant, le repository sur GitHub s'appelle Emacs Leuven. J'ai mis ici un lien. D'ailleurs, pour information, cette note que je partage, c'est aussi sur GitHub. Vous pouvez trĂšs bien la chercher sur https://github.com. Et alors ici, donc, Emacs Chat Sacha Chua. Donc, vous pourrez le trouver lĂ . Donc ici, il n'y a qu'un fichier, il y a juste l'heure et demie.

Prot: Oui, bonne idée de...

12:15 Emacs Boost

Prot: Et c'est quoi Emacs Boost ?

Fabrice: Donc en fait Emacs Boost, je fais des formations Ă  Emacs depuis 2024 et j'ai appelĂ© ça Emacs Boost, l'idĂ©e Ă©tant de booster l'utilisation d'Emacs, d'ĂȘtre plus efficace, d'ĂȘtre plus rapide, d'avoir des automatisations qui soient incluses dedans. Mon projet s'appelle Emacs Leuven depuis longtemps, mais pour ĂȘtre cohĂ©rent entre la formation que j'ai appelĂ©e Emacs Boost et mes configurations qui s'appellent pour l'instant Emacs Leuven, je suis en train de renommer tout doucement des fonctions, des variables, etc. vers Emacs Boost. L'idĂ©e, ce sera que tout mon fichier de configuration, que je peux vous montrer, Emacs Leuven, Je fais du literate programming. Donc ça c'est mon fichier de configuration d'Emacs. C'est le fichier Ă  partir duquel est anglĂ© ma configuration Emacs. Donc ici si je vais montrer de l'autre cĂŽtĂ© le fichier liste. Donc, ça, c'est mon fichier de configuration avec toutes mes configs Ă  moi. Il fait, je pense, 7500 lignes. Je pensais qu'on Ă©tait Ă  10 000 dĂ©jĂ . C'est un petit peu de nettoyage. Bon, alors Ă©videmment, c'est facile parce que j'ai quasiment toujours, devant cette queue, j'ai toujours un commentaire. Donc, si on regarde ici, si je remonte un petit peu, voilĂ , tous mes sets de pubs. Je vais agrandir un peu. J'ai systĂ©matiquement une ligne de commentaire au-dessus. VoilĂ , define. Donc, j'ai une ligne blanche, une ligne de commentaire et puis une ligne de commande. Donc, en fait, si on retirait tous les blancs ou tous les commentaires, je serais Ă  2500 lignes. C'est dĂ©jĂ  pas mal. VoilĂ , mais donc ça, c'est le fichier que j'ai commencĂ© en 99. Et donc qui est aussi public avec toutes les configurations que j'ai et tous les packages que j'utilise. J'utilise un certain nombre de packages. Donc si je regarde, par exemple, un dont je vais vous parler tout Ă  l'heure, je vais retourner au dĂ©but du fichier. Je vais retomber sur ma liste, voilĂ . Donc ça c'est les packages que je charge ou en tout cas que je customise. Donc voilĂ , ça c'est toute la liste. Je crois qu'il y en a Ă  peu prĂšs 70. Oui, un petit peu moins peut-ĂȘtre. Deux packages que j'utilise. Alors certains, je les ai commentĂ©s parce que je n'utilise plus tellement. Et sinon, ça c'est les packages que j'utilise couramment et pour lesquels il y a des configurations. Et donc d'ailleurs, le but d'Emacs Leven, c'Ă©tait de dire c'est un fichier de configuration qui est pour moi, il y a mes configurations, la façon dont je veux travailler, mes automatisations, mes settings par dĂ©faut, ou des fonctions supplĂ©mentaires, des commandes supplĂ©mentaires. Mais l'idĂ©e, c'est que n'importe qui pourrait faire require Emacs Leven, donc Ă©videmment cloner le repo, faire require de ce fichier-lĂ , et ça ne devrait pas perturber son systĂšme. Ça devrait rajouter toutes les configs que j'ai faites, mais sans perturber, sans imposer, par exemple, je ne sais pas, fancy narrow. Donc, en fait, au dĂ©marrage, ça regarde, est-ce que ça va demander, ça va regarder les packages qui sont installĂ©s dans, si je prends l'exemple de Sacha, si Sacha faisait ça, ça regarderait les packages qu'elle a installĂ©s, qui sont appelables, qui sont loadables, qu'elle a installĂ©s dans son systĂšme. Et ça dirait, tiens, voilĂ , il manque tel et tel package, voulez-vous les installer ? Donc, ça va proposer d'installer tous ces packages-lĂ . Mais on peut dire non. Et dans tous les cas, toutes mes configurations, je vĂ©rifie chaque fois si je modifie web-mode. Par exemple, je vĂ©rifie, est-ce que web-mode est installĂ© ? Si oui, alors voilĂ  les configs. S'il n'est pas installĂ©, il n'y a aucune config qui se passe. Donc, j'ai toujours un... Si je vais regarder web-mode, par exemple, pour cet exemple-ci. with-eval-after-load, voilĂ . Donc ici j'ai toujours with-eval-after-load de web-mode. Et donc ma configuration de web-mode ne sera active que si web-mode a Ă©tĂ© trouvĂ© et est utilisĂ©. Donc je ne vais pas, en thĂ©orie, je ne vais pas rajouter des raccourcis clavier, etc. pour des modes que vous n'auriez pas. Et en thĂ©orie, le fichier, il n'y a pas de truc qui soit vraiment privĂ©. Donc il n'y a pas mon nom, mon prĂ©nom, mon username, par exemple, il n'est pas dans ce fichier lĂ . Mon adresse email n'est pas dans ce fichier-lĂ . Donc j'ai un petit fichier init-local.el qui est vraiment spĂ©cifique Ă  moi. Tout ce qui est lĂ , en thĂ©orie, c'est relativement gĂ©nĂ©rique et universel. TrĂšs bien. Donc maintenant, si je repasse sur la... C'est ce que je considĂšre ĂȘtre les meilleurs packages, ceux que j'ai testĂ©s. J'ai lu des centaines, si pas des milliers, sur une vingtaine d'annĂ©es, si pas des milliers de fichiers de config. J'ai vu beaucoup de choses chez plein de gens et c'est comme ça que j'ai trouvĂ© beaucoup de packages au fur et Ă  mesure. Et quand c'Ă©tait des choses qui me plaisaient, j'ai rajoutĂ© des configurations de ces packages. C'est pour ça que c'est aussi gros, mais ça fait beaucoup de choses. Et malgrĂ© tout, ça se charge quasiment instantanĂ©ment. Donc aussi, les packages un peu plus longs, je fais des idle require. Donc j'attends qu'il y ait 5 secondes d'inactivitĂ© dans la session Emacs pour aller charger certains packages. Mon adresse email n'est pas dans ce fichier-lĂ . Donc j'ai un petit fichier init local.el qui est vraiment spĂ©cifique Ă  moi. Tout ce qui est lĂ , en thĂ©orie, c'est relativement gĂ©nĂ©rique et universel.

Prot: TrĂšs bien.

Fabrice: Donc maintenant, si je repasse sur...

Sacha: MĂȘme si ta configuration inclut beaucoup de package, parce que tu dĂ©lais le dĂ©marrage du package, c'est vite, c'est rapide.

Fabrice: Exactement. Je dis que c'est une Ă  deux secondes maximum. Et en fait, l'idĂ©e, et on va en parler encore tout Ă  l'heure, on verra, j'ai beaucoup de choses que j'aimerais bien dire, on va voir le temps qu'on a. Mais l'idĂ©e, c'est qu'en plus, une fois qu'il est dĂ©marrĂ©, il passe en mode serveur. Et donc, je peux lancer, ouvrir un fichier directement dans Emacs sans lancer une nouvelle instance d'Emacs. Donc je n'ai qu'une seule instance d'Emacs et c'est celle-lĂ  qui est utilisĂ©e tout le temps. Donc aprĂšs, chaque fois que j'appelle Emacs pour Ă©crire un message de commit ou des choses comme ça, c'est instantanĂ©. Il n'y a vraiment mĂȘme plus de temps d'attente du tout. Il n'y a mĂȘme pas les une Ă  deux secondes de dĂ©marrage. Et par exemple, un petit truc que je voulais montrer Ă©ventuellement, deux petites automatisations que je trouve extrĂȘmement utiles, qui font le pont. En fait, j'ai trois applications que j'utilise tout le temps chez Emacs. J'ai le Shell, ici, le terminal. Donc j'utilise pas, j'ai pas l'habitude, et ça c'est plutĂŽt d'ailleurs Ă  cause, entre guillemets, Ă  cause de l'Ă©poque Cygwin, oĂč le shell dans Cygwin, un shell dans Cygwin, dans l'Emacs Cygwin, ça marchait pas bien, il y avait des problĂšmes de C-m qui apparaissaient, etc. Donc j'ai pas pris l'habitude d'utiliser le shell dans Emacs. Donc j'ai toujours, voilĂ , mon petit shell sur le cĂŽtĂ©, j'ai Emacs et j'ai le browser. En gros c'est mes trois, et puis alors... Pour le boulot, Ă©videmment, Outlook. Mais sinon, c'est les trois applications que j'utilise vraiment le plus. C'est Emacs, le Shell et le Browser. Et j'ai envie d'avoir des ponts entre ces deux mondes. Donc ici, mon Shell, mon terminal et Emacs. Du style, je veux donc voir si je vais ouvrir un fichier.

21:15 Emacsclient

Fabrice: Par exemple, je vais ouvrir le fichier Emacs... Leuven, tout doux. Donc ici, j’ai un petit alias e qui en fait va faire emacsclient. Quand je fais ça, si tout se passe bien, voilĂ . Donc ici, il m'a ouvert ce fichier tout doux dans Emacs. Donc ça, c'est pour ouvrir un fichier. Je peux aussi ouvrir Le rĂ©pertoire dans lequel je suis, ça ouvre le rĂ©pertoire et je passe en direct. Par exemple, si j'Ă©tais dans ce repository Emacs Leuven, imaginons que je veux faire un guide status. J'ai aussi des alias Ă©videmment pour... Si je veux faire un Git status, je vois qu'il y a ces fichiers-lĂ . Je pourrais Ă©ventuellement vouloir garder les modifications. Je vais les faire avec Emacs. Je pourrais faire simplement up point pour me retrouver directement ici, dans le bon rĂ©pertoire. Et comme ça, lĂ , je vais faire, chez moi, je vais faire C-<f9>, qui va me lancer vc-dir sans me poser la question de savoir oĂč je veux lancer vc-dir. Donc, mon C-<f9> ne prompte pas, il lance vc-dir sur le rĂ©pertoire ou le fichier dans lequel je suis. Et donc, c'est avec ça que je commencerai. Ici, je regarderai les diffĂ©rences avec un Ă©gal. Je vais voir si c'est bon ou pas, puis je vais committer. Donc ça, c'est pour passer du shell vers Emacs. J’ai le petit e. Beaucoup de gens connaissent emacsclient, donc ce n'est pas trĂšs nouveau. J'ai juste abrĂ©viĂ© en e. Deux choses qui sont plus marrantes ou plus utiles. Il y en a une qui est marrante et une qui est utile. Celle qui est marrante, c'est cate, qui est une petite fonction dans le terminal, un petit shell script, et qui fait un cate.

Sacha: Cat plus Emacs.

Fabrice: cat de ce qu'il y a dans Emacs. Donc, si je fais cate, il va en fait m'afficher le contenu du buffer working-dir. C'est gĂ©nial. Si j'ai ce todo-lĂ , par exemple, je peux faire ici si je fais cate, ou si j'en avais plusieurs, on peut imaginer que j'ai plusieurs fenĂȘtres. Donc, si je vais ici, voilĂ . Pour l'instant, la fenĂȘtre active, c'est celle-lĂ . Donc, si je fais Cat, Cat, Ça va me remontrer ça. Si je mets mon curseur lĂ  et que je refais cate, ça va m'afficher l'autre fichier. Alors ça c'est plus... Et alors c'est emacsclient aussi. Alors ça c'est plus marrant, entre guillemets, parce que ça n'a pas beaucoup d'intĂ©rĂȘt, sauf Ă©ventuellement, parfois. Imaginons que je ne sache pas faire un grep. Il y a des opĂ©rations que je connaĂźtrais mieux dans le terminal. Par exemple, compter le nombre de lignes. J'ai un petit... Je suis sous et j’ai un global w qui va me faire le compte. Donc ici, je vais savoir directement, voilĂ , j'ai 1397 lignes dans ce fichier-lĂ . Ce qui revient Ă  faire, juste pour expliquer, ça revient Ă  faire pipe wc -l. Donc voilĂ , je pourrais faire ici aussi un grep, Ă©ventuellement, grep, je ne sais pas, test, je ne sais pas si j'ai... VoilĂ . Donc il y a toutes les lignes qui sont dans ce buffer lĂ  avec le mot test. Donc ça c'est plus marrant entre guillemets, c'est pas tout le temps utile. Par contre un qui est extrĂȘmement utile, c'est imaginons que je sois, je vais ici me redĂ©placer dans mon route, dans mon home directory. Je suis dans le home directory et en fait ici dans Emacs, j'ai ouvert ce fichier lĂ . Et j'aimerais bien faire le status, par exemple, le guide status, dans ce repo-lĂ . Mais donc, il faut que j'aille dans le repo. Donc ici, si je fais C-x C-k, avec direct plus, je sais passer Ă  la vue directe et je vois que le chemin d'accĂšs, donc le chemin, c'est ~/.dotfiles/emacs-leuven. Donc, je pourrais faire ici cd slash... En fait, ça, ce qui est juste au-dessus. Mais si je veux l'automatiser, en fait, je peux faire cde. Donc cde, comme cate, c’était cat de ce qu’il y a dans Emacs, cde, c’est cd de ce qu’il y a dans Emacs. Donc cde va me conduire lĂ  oĂč je suis dans Emacs. Donc dans le mĂȘme rĂ©pertoire, voilĂ .

Sacha: Je vais voler toutes les idées.

Fabrice: C'est le but, parce que j'ai trouvĂ© quelque part cette idĂ©e, et je l'ai peut-ĂȘtre un peu modifiĂ©e, mais c'est gĂ©nial. Donc ici, maintenant, si je vais sur n'importe quel fichier, je ne sais pas si je prends de nouveau notre exemple ici, Readme. Est-ce que vous voyez assez grand ? Est-ce que je peux augmenter la taille aussi ? De la fonte. Donc ici, je suis dans le fichier que j'ai prĂ©parĂ© pour cette prĂ©sentation. C'est un autre repository. Imaginons que dans mon shell, je veuille retourner dans ce repository, cde. Et je suis dans le repository Emacs, Sacha Chua. Je peux faire mon guide status. Je suis directement bien placĂ©. Je n'ai pas besoin de devoir taper des chemins d'accĂšs.

Sacha: Tu as aussi un script pour récupérer ou retrouver le nombre de fichiers qui est courant en Emacs dans la command line?

Fabrice: Je ne l'ai pas, mais ce serait facile Ă  faire puisqu'en fait, il y a ici ce que je... Imaginons que je veuille le faire. Je vais prendre quelques touches en plus. Je vais faire C-x C-j. Et puis... Je ne sais pas si vous savez, w, le petit w, ça me copie. Donc, le contrĂŽler, il me place dans direct sur le fichier en question. Et w, dans le presse-papier, le nom du fichier. Je pourrais faire cat. C'est facile d'implementer. Ce serait facile, parce qu'ici, je l'ai rĂ©cupĂ©rĂ©. cat, ceci. Donc j'ai directement le fichier comme ça. Donc ce serait extrĂȘmement facile. Plus dur, ce sera de lui trouver un nom. À cette commande.

Sacha: Génial, génial.

28:49 org-html-themes

Fabrice: Non, vas-y, j'allais éventuellement, maintenant on était dans, j'allais éventuellement parler d'export HTML, etc.

Sacha: Oui, oui, oui.

Fabrice: Donc vous voyez ici, on est dans le buffer Emacs, alors c'est dĂ©jĂ  agrĂ©able Ă  lire parce qu'il y a un certain nombre de couleurs, etc. J'aimerais bien d'ailleurs ici que les notes soient colorisĂ©es avec une certaine couleur, mais bon, ce sera pour une prochaine fois. Mais il y a Ă©videmment un moyen de lire qui est quand mĂȘme beaucoup plus agrĂ©able, c'est de lire ce mĂȘme document. qui est assez long, ce serait de le lire en HTML ou en PDF. Et donc, en rajoutant un setup file, donc ici simplement une ligne comme ça. Donc, il faut avoir clonĂ© le repository Ă  org-html-themes. Et alors, aprĂšs, il faut taper le chemin d'accĂšs vers le HTML thĂšme readtheorg.setup. readtheorg est un des deux thĂšmes HTML que j'ai Ă©crit. Et donc, simplement en mettant une ligne comme ça, quand on va faire C-c C-e h h, ça va gĂ©nĂ©rer. VoilĂ , c'est dĂ©jĂ  fait. On voit ici dans l'Ă©cho area, ça va gĂ©nĂ©rer la version HTML. Et donc, oui, pour aller l'ouvrir, il y aurait moyen d'aller encore plus vite, etc. Ce que je fais encore toujours pour l'instant, je fais C-x C-j, je passe dans Dired. Et dans Dired, j'ai un petit raccourci qui est extrĂȘmement pratique, que j'ai nommĂ© pour l’instant o pour Open. Et en fait, ça ouvre le fichier sur lequel je suis en utilisant l'application standard Windows. Donc, c'est ça aussi l'intĂ©rĂȘt ici. Donc, ça montre bien qu'avec WSL, je suis intĂ©grĂ© avec mon Windows. Donc, en faisant O sur ce fichier-lĂ , Ça va m'ouvrir Edge. Si je fais O sur le fichier PDF, ça va m'ouvrir sous Matra PDF. Parce que j'ai configurĂ© dans Windows que mon lecteur, c'est sous Sumatra PDF. Et donc ici, je vais faire O. Et ça doit m'ouvrir ici. Alors voilĂ , ça m'ouvre un fichier. Vous voyez ? Et donc ça m'ouvre le fichier avec... Le fichier Org, mais avec une prĂ©sentation beaucoup plus sympathique, beaucoup plus jolie, beaucoup plus agrĂ©able Ă  lire.

Sacha: Je l'adore.

Fabrice: Elle est gĂ©niale, elle est magnifique. Et en fait, il y en a une deuxiĂšme, et je vais vous montrer des petits trucs de la deuxiĂšme, parce que je n'ai pas encore tout mis dans celle-ci. J'aurais bien portĂ© des choses que j'avais créées dans l'autre, mais que je n'ai pas encore portĂ©es, donc j'aurais bien les partager dans tous les thĂšmes. Si je retourne ici dans mon fichier Org et que je mets Big Blow Ă  la place, c'est le nom de l'autre thĂšme. Je vais refaire un export. Cette fois-ci, je n'ai plus qu'Ă  faire F5 ici pour faire un refresh. On a une autre vue oĂč lĂ , chaque section devient un tab. On a tous des tabs. Donc ça peut ĂȘtre aussi intĂ©ressant en fonction de la taille du fichier. Ça peut ĂȘtre aussi intĂ©ressant de voir une vue plus monolithique oĂč on a tout en une seule page ou une vue avec des tables ici oĂč on voit section par section ou chapitre par chapitre. Mais alors cette vue-ci, elle a des trucs un peu spĂ©ciaux. Donc j'ai notamment la possibilitĂ© de faire un collapse. On va aller Ă  un endroit oĂč j'ai ici... Quelques astuces, voilĂ . Donc j'ai collapse all ou extend all. Donc je peux collapser toutes mes sections. En fait, j'ai voulu faire comme dans Org. Et j'ai mes petites flĂšches ici. Donc je peux, en cliquant sur n'importe quel item, je peux l'expander ou le collapser. Donc si j'expand ou je collapse.

Sacha: Pardon, je vais apporter des tissus Ă  ma fille.

Prot: C'est effectif comme ça, oui. Et tu as d'autres thÚmes aussi ?

Fabrice: Non, pas encore publics. J'en ai en deux, mais ils ne sont pas encore publics. Et donc voilĂ , ça peut ĂȘtre intĂ©ressant quand on a beaucoup de tĂąches, etc., pour pouvoir de nouveau, un peu comme dans le fichier Org, de collapser tout sauf un certain truc. Et alors, j'avais mis exprĂšs ici, donc lĂ  j'ai deux tĂąches, j'ai fait deux tĂąches, en todo et en startit, avec des petites tags. Et en fait, j'ai mĂȘme... Ici, un petit dashboard sur le cĂŽtĂ©. Il y a un petit truc dashboard qui s'est mis quand on l'ouvre. On voit le nombre de tĂąches en todo, le nombre de tĂąches en started. Donc, dynamiquement, ça va questionner, ça va afficher tous les statuts qui existent. Si j'en ai que deux dans tout mon fichier, c'Ă©tait pour un petit exemple. LĂ , pareil, j'ai tous les tags et on peut mĂȘme highlighter carrĂ©ment tous les trucs qui sont en todo. C'est des flags, en fait. On peut les highlighter. Je peux highlighter et ça prend la couleur du todo. Comme ça, si je veux scanner tous les trucs qui seraient en todo, je peux les mettre en todo et je peux scanner, quand j'ai un long fichier, je peux scanner tout ce qu'il y a dedans. Et ça marche aussi, si je me souviens bien, avec les petits tags. Je peux highlighter toutes les tĂąches qui ont le tag personnel ou toutes les tĂąches qui ont le tag work d'un coup. Ça me permet de scanner, de regarder ce qui est important. C'est un peu un fonctionnement Ă  la Org, mais dans le browser.

35:02 Review

Prot: Qu’est-ce que ça veut dire r ? r, or click here to review. Exactement.

Fabrice: C'est ce que je voulais te montrer. J'ai un petit truc en plus pour faire un vrai review. Si je clique sur r, ça va faire comme un narrowing. Tout le reste de l'Ă©cran va devenir foncer. Sauf la tĂąche, une tĂąche Ă  la fois. Et donc ça va aller chaque fois sur la next tĂąche. Donc ici, quand je vais cliquer sur R, il va passer Ă  la tĂąche suivante, qui est active, c'est-Ă -dire ici la tĂąche qui sera en start-it. Et donc ça collapse tout le reste. Et si je refais un r, ici c’est bloquĂ©, donc je peux faire q pour quitter. Donc r, il va Ă  la premiĂšre tĂąche, et donc je vois cette premiĂšre tĂąche ouverte, et puis il va Ă  la deuxiĂšme. Donc on pourrait passer, scanner une par une toutes les tĂąches. Et ça va chaque fois mettre en Ă©vidence uniquement cette tĂąche-lĂ . Et donc, je peux faire grand R, donc c’est pour faire le reverse, donc aller en arriĂšre. Donc r, ça va vers le suivant, la tĂąche suivante. Si j'en ai que deux, on pourrait imaginer que j'en ai 100 dans un fichier. Et donc, je peux faire mon review comme ça et relire le truc et puis Ă©ventuellement changer. Ce qui serait gĂ©nial, c'est de pouvoir changer le statut ici, etc. aussi. Et que ce soit rĂ©percutĂ© vers le fichier Org, mais ce n'est pas le cas. C'est un petit ensemble de Org ici. J'ai aussi lĂ  un petit mini-talk, comme la mini-talk dans les chapitres LaTeX. Et donc vous voyez que simplement en mettant une ligne ici, BigBlow ou ReadTheDocs, On a accĂšs Ă  l'une ou l'autre. Je gĂ©nĂšre l'autre version. F5, voilĂ . Ici, il y a un petit truc de recherche en plus que je n'ai pas dans l'autre. J'ai des petites diffĂ©rences dans les deux thĂšmes. Et donc, ça, c'est l'export vers HTML. L'idĂ©e, c'est d'avoir... J'ai mis une rĂ©fĂ©rence ici, vers le repo. Et l'idĂ©e, c'est d'avoir 0 CSS Ă  Ă©crire. Une navigation confortable, une lisibilitĂ© plus grande, un rendu professionnel.

37:25 PDF

Fabrice: On peut carrĂ©ment imprimer ça. VoilĂ , une personnalisation qui est assez simple. J'ai fait aussi un petit repository pour l'export vers la tech. Donc ici, si j'exporte, je l'ai dĂ©jĂ  fait, mais je peux directement l'ouvrir. Oh, donc je vais faire de nouveau... Oh, et ça va ouvrir l'application dans Windows qui est liĂ©e pour les PDF. Chez moi, c'est sous Sumatra PDF. VoilĂ . Et donc, ça m'ouvre ici mon fichier. Cette fois-ci, comme un PDF, avec une table des contenus, une table des matiĂšres. Et voilĂ , j'ai essayĂ© de nouveau de faire les notes, les types, etc. Donc, de les faire un rendu qui soit assez joli. Tout Ă  l'heure, on Ă©tait ici dans Emacs Leuven Boost. Et donc ça, c'est aussi public. C'est une classe qui s'appelle mcreport. Je l'ai appelĂ© mcreport pour l'instant. Donc, il faut juste charger la premiĂšre fois. Il faut charger, donc la premiĂšre fois que je lance Emacs, je devrais le faire automatiquement, mais pour l'instant je ne l'ai pas encore fait. Je dois le faire une fois Ă  chaque session. Je vais Ă©valuer ce buffer-ci dans lequel j'ai les dĂ©finitions de mes diffĂ©rents... de mcarticles, de mcreports, de mcbooks, etc. Donc je dois juste Ă©valuer une fois C'est un package. C'est des accords sur une guitare oĂč on peut gratter plusieurs cordes sur une guitare. Ici, l'idĂ©e, c'est qu'on peut appeler une commande avec deux touches simultanĂ©es. Donc, par exemple, eval-buffer. Donc ici, quand je dois Ă©valuer ceci, je ferais, si je faisais comme avant, je ferais M-x, eval-buffer, Enter.

40:03 Key-chord

Fabrice: Mais en fait, avec key-chord, je peux simplement faire vb en mĂȘme temps. Donc, si je fais vb, il y a genre 100 ou 200 millisecondes. Donc ça doit ĂȘtre fait quasiment simultanĂ©ment. Et le b peut ĂȘtre avant le v ou le v avant le b. Et donc quand je fais les deux en mĂȘme temps, vb, ça fait eval-buffer. Si vous voulez, alors ici on voit rien, c'est pas trĂšs visuel. Si vous voulez voir un truc, hh par exemple, quand je tape hh rapidement, ça fait la sĂ©lection d'abord du mot. Si je retape rapidement, ça grandit chaque fois la rĂ©gion. Donc ça fait un expand region. Et grand H, ça fait l’inverse, je retourne vers du plus petit au plus petit. xk par exemple, si je tape xk, ça va faire kill-buffer. Donc c'est chaque fois juste, si vous voulez voir dans ma configuration, oĂč ça se trouve, Emacs Levenel, XK par exemple, voilĂ . Donc c’est lĂ  que j’ai mis other window, ww, xj, ça fait le C-x C-k, donc le direct jump. Et le eval-buffer dont j’ai parlĂ©, oĂč est-ce qu'il est ? On va mettre un peu
 Donc eval-buffer, voilĂ , vb, ça fait eval-buffer. Donc pratique ça pour Ă©crire en français. Quand j'Ă©cris plus petit, plus petit, deux fois rapidement, donc il faut que ce soit chaque fois rapidement, ça va faire un insert de caractĂšres guillemets français. Donc ici, je vais faire si je le fais lentement, il ne se passe rien. Si je le fais vite, il insĂšre les guillemets. D'accord. Tant que j'y suis, vous avez vu aussi, si je fais plus petit, plus petit ici, et puis je fais un save, vous voyez que j'ai Ă  ce moment-lĂ  ici une ligne bleue dans le gutter, dans le fringe. Donc j'utilise git-gutter, si je me souviens bien, de Dmitry, j'ai oubliĂ© son nom de famille. C'est diff-hl, de Dmitry Gutov.

Sacha: Moi aussi.

Fabrice: Ouais, ça doit ĂȘtre ça. Et donc ça me met en fonction, si j'efface ici des lignes, ça va mettre du rouge. Et si je fais une insertion, et si je fais des insertions, ça va mettre du vert. Donc je vois directement dans mon buffer, systĂ©matiquement, oĂč est-ce que j'ai des choses qui ne sont pas encore commitĂ©es. Et alors pour, ça c'est la premiĂšre customisation que j'ai indiquĂ©e, si je retourne dans mon... Je peux en mĂȘme temps parler de F5 et F6. J'ai quelques trucs assez sympathiques. F5, ça va me faire... Si j'ai un seul window, quand je fais F5, ça me crĂ©e un deuxiĂšme window. Ça fait l’équivalent de C-x 2 ou de C-x 3. En fonction de la place que j'ai. Donc si j'ai assez de place pour avoir 80 caractĂšres prĂ©sentĂ©s, il va passer Ă  un mode vertical, cĂŽte Ă  cĂŽte. Sinon, si j'ai moins d'espace, il va passer sur un mode dessus-dessous. Et si je suis Ă  l'inverse, dans une situation avec plusieurs buffers, avec plusieurs windows, donc ici j'en avais encore une troisiĂšme, quand je fais F5, en fait, c'est l'Ă©quivalent de faire C-x 1, donc ça va faire Ça va garder uniquement la fenĂȘtre qui Ă©tait active. Et donc souvent, quand je dois passer d'un buffer Ă  l'autre, quand je veux ouvrir autre chose, je vais faire F5, ça me crĂ©e un deuxiĂšme buffer. Et puis je vais mettre le contenu ici d'autres choses. Je vais retourner sur ma prĂ©sentation. Et je voulais parler, par exemple, du C-w et du M-w sans sĂ©lectionner de rĂ©gion. Ça, pour moi, c'est un des trucs les plus... Les plus importants, que j'utilise tout le temps, tout le temps, tout le temps. Donc, imaginons que je veuille... Si je veux copier cette rĂ©gion-lĂ , je dois commencer Ă  sĂ©lectionner une rĂ©gion, et puis je vais pouvoir faire un copie. Si je veux, en fait, si je me rends compte... Imaginons que je veuille faire une ligne qui ressemble Ă  celle-lĂ , celle-ci, avec le kk. Imaginons que je veuille aussi faire qq, que qq ait la mĂȘme... Appel la mĂȘme commande. Donc je pourrais dupliquer la ligne. J'ai aussi une petite commande pour dupliquer les lignes. Et sinon, en fait, il faut que je copie cette ligne-lĂ . Dans un Emacs standard, pour copier la ligne, je dois aller au dĂ©but de la ligne, faire la sĂ©lection, aller Ă  la fin de la ligne et puis faire M-w. Et Ă  ce moment-lĂ , je peux la copier. En fait, c'est trop long pour moi. Donc j'Ă©tais lĂ  au milieu et donc je veux copier la ligne. Simplement, je vais prendre un autre exemple, je vais prendre celle-lĂ . Je fais M-w. Et M-w, donc j’ai un advice au-dessus de M-w. M-w, s’il n’y a pas de rĂ©gion, il considĂšre que la rĂ©gion, c'est la ligne courante. Donc ici, si j’insĂšre, je fais C-y, voilĂ . J'ai la ligne que j'avais mise dans le presse-papier.

45:41 Copier sans sélectionner

Fabrice: Et ça, ça sauve en temps fou de pouvoir... C'est quand je veux copier une ligne ou Ă  l'inverse couper une ligne parfois. C'est le mĂȘme principe. Donc le C-w, donc le cut, si on parle en Windows. Donc si je veux faire un cut de cette ligne-lĂ , de nouveau, si je n'ai pas cette customisation-lĂ , je vais devoir, au dĂ©but de la ligne, sĂ©lectionner la rĂ©gion et puis je peux faire C-w. Mais avec la petite customisation, avec l'advice que j'ai, sur n'importe quelle ligne, je suis au milieu, C-w, ça fait le cut. Et donc ça m'arrive trĂšs souvent, si je veux supprimer, par exemple, ces trois lignes-lĂ , alors je pourrais me remettre au dĂ©but, puis sĂ©lectionner ma rĂ©gion, faire C-w. Ça ne me dĂ©range pas que ça aille dans le presse-papier. Ou encore plus rapidement, je suis lĂ -dessus et ces trois lignes qui ne m'intĂ©ressent plus, je fais C-w C-w C-w. Et les trois lignes ont disparu. C'est plus facile. C'est beaucoup plus rapide, c'est beaucoup plus facile. Le fait de pouvoir faire un copier ou un couper sans sĂ©lection. De nouveau, je reviens ici, je fais un M-w, et puis je vais lĂ , C-y. Heureusement que j'ai Git, parce que lĂ , je suis en train de casser mon fichier. Ce n'est pas grave parce que je vais faire un revert aprĂšs. Mais bon, voilĂ , ça, c'est un truc extrĂȘmement utile pour moi, ce C-w, M-w. Donc, j’ai mis un lien. Ceux qui veulent aller chercher les notes de cette prĂ©sentation, j'ai mis un lien directement vers L'endroit, ça se trouve dans mon fichier de config Emacs qui est sur GitHub. Ça va ouvrir directement la section deletion and killing. Vous pourrez avoir le code. Ici, voilĂ  le code qu'il faut copier si vous le voulez. Ça, c'est pour le cut. C'est un slick cut, ce que ça s'appelle. Et puis ici, pareil pour le copier. Donc le M-w, il faut copier ça. Et donc ça regarde, en fait, est-ce qu'il y a une rĂ©gion ou pas. Et s'il n'y a pas de rĂ©gion active, ça sĂ©lectionne la ligne courante.

Sacha: J'adore ce genre de fonction, do what I mean.

Fabrice: Oui, j'en ai plusieurs autres, mais exactement.

48:29 fill-paragraph

Fabrice: Par exemple, un autre do what I mean, je l'ai pour le M-q. Imaginons ici que je vais rajouter... Tiens, j'ai des trucs dans le presse-papiers, je peux les mettre lĂ . J'avais upcase, la region. Donc maintenant, si je veux remettre en forme le paragraphe, je vais faire metacue. Et en fait, imaginons par exemple que je veuille... En fait, ça, c'Ă©tait un paragraphe qui pourrait ĂȘtre beaucoup plus long. Je vais faire loremipsum. J'ai un yasnippet qui va me faire un message un peu plus long. Et imaginons que je veuille remettre ça en une seule ligne, parce que je dois copier ça, par exemple, dans Outlook. Et dans Outlook, ce sera moche si j'ai des retours dans la ligne physique Ă  chaque fois. Ou si je veux le copier dans WhatsApp, un message. Donc, ce que je vais faire, en fait, je vais faire M-q. Il va faire l'inverse, il va toggler. Donc, mon M-q Ă  moi, il tog
 Donc, la premiĂšre fois, il fait un M-q normal et la deuxiĂšme fois, il passe Ă  la version sur une seule ligne. Quand je refais M-q, je repasse en version multiligne. Une seule ligne, plusieurs lignes, une seule ligne, plusieurs lignes.

Sacha: Donc, ça fait un toggle.

Fabrice: Donc, ça regarde la derniĂšre commande. Si la derniĂšre commande est dĂ©jĂ  le M-q, alors je refais le M-q, mais dans l’autre sens.

50:02 Les macros

Fabrice: J'ai la mĂȘme chose pour les macros. Maintenant, il y a F3 et F4 qui, depuis une dizaine d'annĂ©es, ont Ă©tĂ© bindĂ©s pour les macros. J’utilisais Shift F8 pour dĂ©finir une macro et F8 pour l'exĂ©cuter. Donc Shift F8. Imaginons que je veuille supprimer les deux premiers caractĂšres de la ligne. Ça n'a aucun sens, mais c'est pour l'exemple. Je vais faire Shift F8. VoilĂ . Non, je me suis trompĂ©. Shift F8. Et donc on voit Define Keyboard Macro. Je vais supprimer les deux premiers caractĂšres. Je passe Ă  une suivante. Et je refais Shift F8. Et parce que j'Ă©tais en mode dĂ©finition de macro, il sait qu'il doit arrĂȘter la macro. Et puis pour l'exĂ©cution, ce sera juste F8 tout seul.

51:02 Volatile highlight

Fabrice: Un truc aussi extrĂȘmement utile, je trouve, visuellement, c'est si je vais faire un undo, maintenant, les deux premiĂšres lignes de chaque phrase vont se remettre, de chaque ligne vont se remettre. Mais en fait, j'utilise Volatile Highlight qui va me montrer ce qui vient de changer. Quand je ne fais pas de mouvement dans le buffer, je vois qu'il vient de rajouter gr. Et si je refais undo, undo, undo, undo. Je vois chaque fois la modification qu'il a faite dans le buffer. Donc quand on fait des yanks, des copies, etc., des insertions ou des replays, on va voir, volatile-highlight va montrer juste comment j'ai dĂ©placĂ© mon caractĂšre. Et ça, c'est trĂšs utile pour voir un peu visuellement qu'est-ce qui s'est passĂ©. C'Ă©tait la mĂȘme chose avec mon metacue. Si je fais un undo, si je refais le metacue ici et que je fais un undo, voilĂ . On voit les insertions d'un retour en ligne Ă  chaque fin de ligne. Il ne reste que quelques minutes. Je n'aurais pas le temps de montrer tout ce que j'ai imaginĂ©. Je ne sais pas si vous avez des questions. Sinon, j'ai encore un truc que je voudrais montrer qui est assez utile. Et puis le reste vous pourrez lire.

Prot: Oui, je crois que c'est OK. Pardon ? Oui, je crois que c'est OK pour toi de démontrer. Parce que si on a des questions, on peut voir le Elisp que tu as écrit.

Fabrice: Tout Ă  fait.

Prot: Tout est public.

52:47 Les tableaux Org

Fabrice: Un truc qui m'intĂ©resse dans les fichiers Org, j'utilise beaucoup les tableaux. Par exemple, mes factures que je fais chaque mois. Je l'ai fait avec Org. J'ai un fichier qui ressemble Ă  ça. Ici, j'ai mis dans le fichier de note de cette prĂ©sentation, j'ai mis un petit exemple avec des personnes, des prix. Ici, on a un total de 2250 euros. Et ça, c'est un prix hors TVA. Ici, j'ai une autre table qui va appliquer la TVA. Elle reprend. Ici, j'ai un remote. Donc je vais chercher dans la carte participant, je vais chercher ce montant lĂ . Et donc il vient se copier lĂ  tout seul. Et sur ce montant lĂ , j'applique une TVA Ă  21%. Donc j'ai le montant, toutes charges comprises, donc avec la TVA. Et donc le truc que je trouve embĂȘtant, par dĂ©faut, enfin qui m'embĂȘte moi, c'est que potentiellement, je vais dire en fait non, et si c'Ă©tait pas 500, c'Ă©tait 1500. Et je vais sauver mon buffer. Et en fait, c'est totalement incohĂ©rent maintenant, puisque la somme n'est plus correcte. Et en plus, un tableau qui se trouve ici juste derriĂšre, mais qui pourrait se trouver totalement ailleurs dans le document, n'est plus du tout Ă  jour non plus. Et si je fais un export, je vais publier un document qui sera totalement avec des fautes, en fait. Et donc, j'ai surchargĂ© le Save Buffer dans le mode Org. Et dans le mode Org, quand ça fait Save Buffer, ça fait aussi le recalcul de toutes les tables dynamiques, le recalcul de tous les blocs dynamiques, pardon, le recalcul de toutes les tables et aussi le rĂ©alignement des tags. Par exemple, ici, je vais mettre un tag. Tout ça, vous verrez en mĂȘme temps. Mon tag. Les tags, par dĂ©faut, sont alignĂ©s Ă  80 caractĂšres. À la fin, ici, il y a une petite ligne grise. Je ne sais pas si vous la voyez bien. Elle est assez fine. Maintenant, quand je vais faire simplement save, chez moi, c'est F2, ça va calculer les montants. On va voir que ce montant-lĂ  va ĂȘtre changĂ©, ce montant-lĂ , celui-lĂ  et celui-lĂ . Je vais faire save ici. Et mon tag a Ă©tĂ© rĂ©alignĂ© aussi. 80 caractĂšres, sauf qu'ici j'ai une prĂ©sentation avec les deux chiffres, c'est pour ça qu'il y a un petit dĂ©calage. Donc de nouveau, si je mets 11500, C-x C-s, je suis sĂ»r que mon document est correct. Je n'ai pas d'information incorrecte de table avec des erreurs, simplement parce que j'aurais oubliĂ©. Vous m'entendez encore ?

Prot: Sacha, je ne peux pas t'écouter.

Sacha: Je suis muette.

55:43 Les formations et les cours particuliers

Sacha: Ma fille s'est réveillée, donc je veux consacrer du temps pour la publicité de ta formation avant qu'elle devenue grincheuse. Si c'est acceptable, dites-nous sur les formations en présentiel.

Fabrice: Je fais effectivement des formations sur Emacs parce que je suis tellement passionnĂ© par cet outil et que je le trouve magnifique, je le trouve extrĂȘmement puissant. que j'ai envie d'aider des gens Ă  pouvoir s'en servir de la mĂȘme maniĂšre que moi. Donc l'idĂ©e, c'est de montrer tout ce que j'utilise, un peu comme je le fais maintenant, mais ici, c'est avec quelques petits Ă©lĂ©ments Ă  gauche, Ă  droite. Et donc, j'aimerais bien en faire tout le temps, mais bon, j'ai fait pour l'instant dĂ©jĂ  trois formations, deux Ă  Paris, une Ă  Rotterdam, et je compte en refaire une soit en Q4 d'RGSI, J'avais quelques notes, voilĂ . Soit en Q4 2026 ou alors probablement plutĂŽt le Q1 2027, en refaire une autre Ă  Paris, oĂč l'idĂ©e, c'est en fait d'apprendre aux gens Ă  utiliser Emacs et de montrer, comme je le fais un petit peu ici, montrer les packages que j'utilise et qu'est-ce qu'ils apportent, donc qu'est-ce qu'ils font de plus, qu'est-ce qu'ils permettent d'apporter plus, comme donc le keychords, le volatile-highlight. Les petites fonctions que j'ai Ă©crites sur le recalcul des tables, par exemple sur Org, le M-w, C-w qui sĂ©lectionne la ligne d’office s'il n'y a pas de rĂ©gion, tout ça en fait, le but c'est de le montrer, de l'expliquer et que les gens puissent ou bien utiliser mon Emacs Leven tel quel et puis connaĂźtre tout ce qu'il y a dedans ou bien aller piquer des choses intĂ©ressantes qui sont dans ce fichier-lĂ .

Sacha: Est-ce que tu es aussi disponible pour les cours particuliers comme le coaching de Prot ? Tu offres aussi les cours particuliers ?

Fabrice: Absolument, absolument. Je suis passionnĂ© par Emacs et donc effectivement, des cours particuliers, je suis aussi tout Ă  fait disponible Ă  le faire. C'est le mĂȘme enthousiasme pour... En français et en anglais. En français, en anglais, en irlandais et en espagnol.

Sacha: C'est trĂšs cool. Tu as un autre rendez-vous. J'ai un fil. Merci beaucoup Ă  Fabrice et Ă  Prot.

Prot: Merci, merci Sacha et Fabrice.

Fabrice: Merci à vous. Merci beaucoup pour cette invitation. J'espÚre que ça donnera quelques idées. Je suis disponible pour... Pour toute question supplémentaire ou pour toute info supplémentaire, voilà, un grand plaisir.

Sacha: À la prochaine, je pense que nous devons à une autre conversation.

Fabrice: Avec grand plaisir. À la prochaine. Merci beaucoup.

Prot: Au revoir, au revoir, Ă  bientĂŽt.

Fabrice: Merci, au revoir.

View Org source for this post

You can e-mail me at sacha@sachachua.com.

-1:-- Emacs Chat 28: Fabrice Niessen (en français) (Post Sacha Chua)--L0--C0--2026-08-14T00:13:37.000Z

Magnus: Emacs salmagundi, 2026-08-13

Here's a few minor tweaks I've made to my Emacs setup recently.

Limiting enabled tree-sitter modes

After reading Rahul's post Emacs 31 Is Around the Corner: The Changes I'm Already Daily Driving I simplified my setup and turned on all the tree-sitter modes

(treesit-enabled-modes t)

I regretted that as soon as I opened a YAML file though. It turns out the yaml-ts-mode is rather unusable.1 I switched back to yaml-mode and adjusted the value of treesit-enabled-modes to include what I have use for (the change):

(treesit-enabled-modes '(bash-ts-mode
                         c++-ts-mode
                         c-or-c++-ts-mode
                         c-ts-mode
                         dockerfile-ts-mode
                         go-mod-ts-mode
                         go-ts-mode
                         go-work-ts-mode
                         json-ts-mode
                         python-ts-mode
                         rust-ts-mode
                         typescript-ts-mode))

The customization of treesit-enabled-modes contains a list of all the supported modes.

I hope someone will take pity on yaml-ts-mode and make it usable soon.

Tree-sitter grammar for Cabal files

When I began tinkering with my own treesitter-based mode for Haskell I failed to find a tree-sitter grammar for Cabal files so I wrote a rather rough one myself. My hope was that it could serve as a base, or motivation, for someone else to write a proper one. It took more than 2 years, but now it's finally happened!

I've switched my mode to use Curtis's grammar and I'll be archiving mine. (The change.)

Hello editable xref, good bye wgrep

Rahul's post also mentions that editable xref buffers. I had the same itch to have editable xref buffers, and I'd added a function to turn them into grep buffers to be able to edit them. I've now switched to using the built-in support, and removed the function.

At the same time I also decided to remove wgrep and instead use the builtin function for editing grep buffers.

Footnotes:

1

A mode for an indentation-sensitive language really ought to at least handle indentation!

-1:-- Emacs salmagundi, 2026-08-13 (Post Magnus)--L0--C0--2026-08-13T19:44:00.000Z

Sacha Chua: 27 août: Emacs Chat: Fabrice Niessen (en français, partie 2)

Nous allons à nouveau parler en direct avec Fabrice Niessen d'Emacs, de ses flux de travail, de sa configuration et d'autres sujets en français. Voici ses notes.

dans le fuseau horaire America/Toronto, soit 0730H EDT / 0630H CDT / 0530H MDT / 0430H PDT / 1130H UTC / 1330H CEST / 1430H EEST / 1700H IST / 1930H +08 / 2030H JST.

Voici notre conversation prĂ©cĂ©dente :

View Org source for this post

You can e-mail me at sacha@sachachua.com.

-1:-- 27 août: Emacs Chat: Fabrice Niessen (en français, partie 2) (Post Sacha Chua)--L0--C0--2026-08-13T15:58:31.000Z

Raymond Zeitler: One Hundred Times the Highest Priority

Please note that org‑get‑cust‑priority has been updated. The code that was published on 2026-07-10 causes org‑tags‑list to crash.

In the previous post,1 I was disappointed to learn that valid priority characters are limited to 0–9 and A–Z. I thought it would be neat if the exclamation mark ("!") could represent the ultimate priority, a sort of "drop everything and do this immediately" kind of priority.

It turns out that you can use "!" for this purpose, regardless of which priority characters you've configured. The trick is to write a function and assign it to org-priority-get-priority-function.

The function (which I named org-get-cust-priority) simply looks for "!" in the priority cookie and returns one hundred times the value that would be returned by the highest valid priority. If it doesn't find "!" it simply calls a modified version of the usual org-get-property function,2 which I named org-get-std-priority. The two functions are shown below.3

(defun org-get-cust-priority (s)
  "Return 100 times the highest priority when S contains
a priority cookie with `!'.  Otherwise call the usual
`org-get-property' function. Intended to be the function
`org-priority-get-priority-function' is set to."
  (interactive)
  (if (not (functionp org-priority-get-priority-function))
      (org-get-std-priority s)
    (string-match ".*?\\(\\[#\\(!\\)\\] ?\\)" s)
    (if (and (string-match ".*?\\(\\[#\\(!\\)\\] ?\\)" s) (string-equal (match-string 2 s) "!"))
        (* 100000 (abs (- org-priority-lowest org-priority-highest)))
      (org-get-std-priority s))))

(defun org-get-std-priority (s)
  "Find priority cookie and return priority.
S is a string against which you can match `org-priority-regexp'.
Same function as `org-get-priority' sans test for a custom
function in `org-priority-get-priority-function'."
  (save-match-data
    (if (not (string-match org-priority-regexp s))
        (* 1000 (- org-priority-lowest org-priority-default))
      (* 1000 (- org-priority-lowest
                 (org-priority-to-value (match-string 2 s)))))))

The number that Org uses to sort on priority depends on the values of org-priority-lowest and org-priority-highest,4 which are "C" and "A" by default. In the default configuration, the highest priority has a numeric value of 2000, while the lowest has a value of 0. If the highest and lowest priority characters are 1 and 9, respectively, the highest priority has a value of 8000.

You know you need this if you have about a dozen items on your agenda and half of them are prioritized A.


2026-08-13 Update org-get-cust-priority. Previous version breaks org-tags-list.

1 https://ray-on-emacs.blogspot.com/2026/07/numeric-priorities-in-org-mode.html
2 Modified just to prevent recursion.
3 I know this code isn't particuarly elegant. I could have made custom variables for "!" and the priority multiplier, for example. I'd like to use it for a while before deciding whether it's worth investing any more effort. I do welcome suggestions on how to improve.
4 Or rather, the difference between the highest and lowest priorities.

-1:-- One Hundred Times the Highest Priority (Post Raymond Zeitler)--L0--C0--2026-08-13T15:34:56.336Z

Irreal: Some Thursday Humor

I don’t see anything in my feed worth writing about so I offer this bit of humor to help you get through the day. If you’ve ever wondered why an S-expressions is called an sexp, here’s your answer.

Like all humor, there’s a bit of truth here. Not the part about sexp, of course, but the notion that many of us come to Emacs for some particular feature, venture too close to the init.el event horizon, and never escape.

-1:-- Some Thursday Humor (Post Irreal)--L0--C0--2026-08-13T15:05:22.000Z

The Emacs Cat: Emacs, Dired, and Ubuntu 26.04

Just recently I upgraded one of my Dell laptops to the latest Ubuntu 26.04 LTS with a clean install. The installation process was smooth and enjoyable.

Ubuntu 26.04 ships with Emacs 30.2, which made me happy, so I installed it immediately—more precisely, the emacs-pgtk Wayland-optimized package, since Wayland is the default windowing system on Ubuntu 26.04.

The Problem

I started Emacs and, after a short time, ran into a problem: Dired (C-x d) did not work properly. It displayed a strange mess of unsorted, chaotic entries, as shown below. It was a big surprise.

Click or tap to view the full-size picture.

I’ve been using the same .emacs multi-platform config file for Linux, macOS, and Windows for years and have never had any problems with Dired on different platforms, let alone across versions of the same OS.

My Dired configuration is quite simple:

;;; `DIRED'
(require 'dired)

;; Dired options for Windows and non-Windows (Linux, macOS) systems.
;; http://oremacs.com/2015/01/13/dired-options/
(if (string-equal system-type "windows-nt")
    (setq dired-listing-switches "/A:H /O:NG")
  (setq dired-listing-switches "-laGgh1v --group-directories-first --time-style=long-iso"))

;; `dired-subtree' (since 2022-10-18)
;; https://xenodium.com/drill-down-emacs-dired-with-dired-subtree/
(use-package dired-subtree
  :ensure t
  :after dired
  :config
  (bind-key "<tab>" #'dired-subtree-toggle dired-mode-map)
  (bind-key "<backtab>" #'dired-subtree-cycle dired-mode-map))

;; By default, Dired buffers will not update themselves
;; unless you explicitly tell them to.
(setq dired-auto-revert-buffer t)

The dired-listing-switches variable defines the switches passed to dir or ls for Dired on Windows and Linux/macOS systems, respectively. I’ve been using this configuration for years.

On Ubuntu 24.04 everything looks fine. So, what’s wrong with Emacs 30.2?

I decided to test the ls switches in the console. Oops—the same result as in Dired under Emacs 30.2!

Click or tap to view the full-size picture.

This means the problem is not with the new version of Emacs. After a short search, the answer was found.

The Bug in Ubuntu 26.04

It turns out the bug in Ubuntu 26.04 is real, documented, and already tracked in Ubuntu’s Rust-based coreutils for Ubuntu 26.04. The issue specifically affects ls when using --group-directories-first and is tied to missing locale collation support in the Rust rewrite.

Launchpad Bug 2154042 — Incorrect alphabetical sorting with --group-directories-first

This is the main bug report affecting Ubuntu 26.04 users after the Rust coreutils transition. It explicitly states that:

  1. ls from the Rust-based coreutils does not sort alphabetically when using --group-directories-first.
  2. The bug appears both when LC_ALL is set and when it is unset.
  3. GNU ls (gnuls) sorts correctly; Rust ls does not.

Conclusion

Since Dired—and possibly other Emacs packages—uses ls to list directories and files, Dired will not work properly on Ubuntu 26.04 regardless of the Emacs version.

Solution

Luckily, the Ubuntu team has kept the old version of coreutils (GNU coreutils), written in good old C. The GNU version of ls is gnuls and is installed by default on Ubuntu 26.04. This means we can use gnuls—which works properly—for Dired instead of ls when running Ubuntu 26.04.

To do this, we will use the insert-directory-program variable, which holds the absolute or relative name of the ls-like program. This variable is used by insert-directory and dired-insert-directory (and therefore by Dired). Its default value on Linux and macOS is ls.

First we need to determine the name and version of the OS. The custom ubuntu-version function helps with that:

(defun ubuntu-version ()
  "Return (MAJOR MINOR) as integers if running on Ubuntu, else nil."
  (when (and (eq system-type 'gnu/linux)
             (file-readable-p "/etc/os-release"))
    (with-temp-buffer
      (insert-file-contents "/etc/os-release")
      (let* ((os-release (buffer-string))
             (is-ubuntu (string-match-p "^ID=ubuntu" os-release))
             (version-id (and is-ubuntu
                              (when (string-match "VERSION_ID=\"\\([0-9]+\\)\\.\\([0-9]+\\)\"" os-release)
                                (list (string-to-number (match-string 1 os-release))
                                      (string-to-number (match-string 2 os-release)))))))
        version-id))))

On my machine ubuntu-version returns (26 4).

Now we can replace ls with gnuls if we are on Ubuntu 26.x:

(when (eq (car (ubuntu-version)) 26)
  (setq insert-directory-program "gnuls")
  (message "`ls' is replaced with `gnuls' on Ubuntu 26.x."))

After that, Dired works as before.

Click or tap to view the full-size picture.

Happy Emacsing on Ubuntu 26.04!

– The Emacs Cat.

-1:-- Emacs, Dired, and Ubuntu 26.04 (Post The Emacs Cat)--L0--C0--2026-08-13T09:55:47.000Z

Meta Redux: Projectile 3.4

Projectile 3.4 is out! After the four releases that preceded it this summer, this one is polish rather than ambition.

Most of it came out of an annoyance I’d been living with for years without ever quite naming it: a lot of what I work on isn’t one directory. CIDER is really CIDER plus cider-nrepl plus orchard plus clj-refactor plus a handful of others. RuboCop is RuboCop plus rubocop-ast plus rubocop-rails and the other extension gems plus the style guide. I’ve been maintaining families of repositories for the better part of a decade.

Lately I’ve also been reaching for git worktrees far more than I used to, so the same repository is now checked out two or three times on my disk at any given moment.

Projectile saw all of that as a pile of unrelated projects, which is exactly what it looks like from the outside.

Somewhere else in the same project

projectile-switch-worktree (s-p W) offers the other checkouts of the repository you’re in, each labelled with whatever tells it apart - the branch for git, the workspace name for Jujutsu:

Switch to worktree:
~/src/myapp-main/     (main)
~/src/myapp-hotfix/   (hotfix/crash-on-open)

Git worktrees are the obvious case. Projectile asks git about those, so a worktree you’ve never opened in Emacs shows up anyway. Jujutsu workspaces work the same way.

The case I care about more is the one without any plumbing: a second git clone of the same upstream. Same workflow, done by hand. I looked at my own ~/projects while writing this and found four separate clones of CIDER sitting in it. Nothing records those anywhere, so Projectile can’t ask git to list them - it matches them among your known projects by their remote instead.

Somewhere else in the same effort

The other half is projectile-switch-sibling-project (s-p n), which offers the projects related to the one you’re in rather than every project on the machine.

The interesting question was what “related” should mean. My first instinct was to compare directory names and look for a shared prefix. Before writing anything I tried the candidates out on my actual project directory, all ninety-odd repositories of it.

Comparing names does find rubocop, rubocop-ast and rubocop-rails. But grouping by the owner of the upstream remote finds this:

The sibling project prompt in CIDER, listing sixteen related projects including orchard, haystack, port and sayid

Half of those names have nothing in common with cider, and no amount of staring at directory names would ever have related them. haystack parses stack traces, port is a printer registry, sayid is a tracing debugger. Nothing in those names says “CIDER”.

What they do have in common is an org: they all live under clojure-emacs on GitHub. That’s not incidental, it’s how the project is actually organised - the org is the boundary of the effort, and the individual repositories are just where the pieces ended up when they got big enough to split out. Same story for the rubocop org, and for nrepl. Once I saw that, keying on the owner of the remote was obviously right, because it’s the same fact GitHub is already recording for me.

It needs a bound, though, and the same experiment showed why: about 40% of my checkouts are under my own GitHub account, and “we’re both under bbatsov” doesn’t relate anything. So an inferred group covering more than a quarter of your known projects gets dropped and the next signal takes over.1 If you’d rather just say what belongs together, projectile-project-groups is there and is never second-guessed.

Switching is only the obvious thing to do with this. Now that Projectile can tell which projects belong together, the same grouping could back a find-file or a search across the whole family - one prompt that reaches every repository in the org rather than just the one you happen to be sitting in. I go looking for “which of these fifteen repos defines this var” often enough that I suspect it’s the more useful half. Nothing built yet, but that’s where I expect this to go.

The commands come with you

Once Projectile knows two directories are the same repository, the command history can follow you between them.

Press M-p at the compile or test prompt in a worktree you made this morning and you get the commands the project is actually built with, instead of an empty history. That’s an issue from 2022 that a stale bot had helpfully closed for me at some point.

What doesn’t follow you is anything that runs without asking - what a prompt is pre-filled with, and what projectile-repeat-last-command replays. I had those shared too in the first draft, and then watched a repeat in one worktree rebuild the tree next door. A remembered command can carry absolute paths back to where it was typed.

More languages, fewer surprises

projectile-run-test-at-point (s-p c .) arrived in 3.1 knowing Python, Go and JS/TS. It now also knows Ruby (both RSpec and Minitest), Rust, Elixir, Java, Erlang and F#.

Ruby is written the same way whichever framework you use, so there the project type picks the runner rather than the syntax; Java’s picks between Maven and Gradle. Elixir tests get addressed as FILE:LINE, because ExUnit can’t select a test by name from the command line. OCaml deliberately gets nothing - its tests are ordinary values you register with Alcotest or OUnit, so there’s no syntax to recognize.

projectile-find-file-of-kind (s-p j) and projectile-toggle-related-file (s-p J) learned Phoenix, Laravel and Next.js. Rails and Django had been the only frameworks with file-kinds tables out of the box, which felt like a strange place to have stopped.

The reports answer back

The dashboard and the doctor from 3.3 both got a pass. They’re no longer plain text - section headers, field labels and findings are faced by meaning, with findings colored by severity and sorted so anything wanting your attention comes first. The faces only inherit from standard ones, so your theme styles them without knowing Projectile exists.

The doctor’s findings now come with a button that acts on them:

The doctor's findings section, with a warning about projectile-mode carrying an enable button, above four ok findings

[enable] for a projectile-mode you forgot to turn on, [enable caching] on a big uncached project, [open dirconfig], [edit .dir-locals.el]. Pressing one regenerates the report. Findings Projectile can’t act on stay plain advice.

And since a doctor report usually ends up pasted into an issue, w copies the buffer as plain text, without the faces and buttons.

Odds and ends

  • projectile-find-changed-file (s-p C) completes over what git reports as staged, unstaged or untracked - or, with a prefix argument, everything that differs from a revision you pick.
  • projectile-run-task discovers rake tasks now, read out of your Rakefile and .rake files rather than by running rake -T, which would load the whole application.
  • projectile-ignored-project-patterns is the regexp-matching sibling of projectile-ignored-projects, so keeping a whole area of your machine out of the known projects no longer needs a lambda.
  • Messages Projectile emits on its own initiative are prefixed with [Projectile] now, and the ones answering a command you just invoked aren’t. There were five different conventions in there before, which I’d somehow never noticed.
  • The known projects file, the frecency store and the session directory are resolved with locate-user-emacs-file, so they land in the right place if your configuration lives in ~/.config/emacs. Nothing moves if it doesn’t.
  • Elixir’s implementation/test toggle offers foo_test.exs rather than foo_test.ex, a script ExUnit will actually run. Project types can declare their test file extension now.

Upgrade notes

Nothing here should break a working setup, but two things are worth knowing.

A batch of options were renamed or folded together. The six projectile-<cmd>-use-comint-mode options became one projectile-use-comint-mode; projectile-per-project-compilation-buffer and projectile-per-command-compilation-buffer became projectile-compilation-buffer-scope; and a handful of options that had broken their own naming schemes were renamed to match their siblings.2 Every one of them is still honored under its old name, so your config keeps working - you’ll just see an obsolescence notice.

Two options are gone: projectile-tags-file-name and projectile-go-project-test-function were only ever read as Projectile loaded, which means setting them from your init file afterwards did precisely nothing.

And the command history is now the repository’s rather than the directory’s. Set projectile-command-history-scope to project if you’d rather have it per directory. Histories you already have are adopted, not dropped.

The full changelog is here, and the manual is at docs.projectile.mx. The cross-repository features have a page of their own, limitations included.

Five releases in six weeks is not a pace I intend to keep up, and this is the natural place for the burst to land. The big pieces from 3.0 through 3.3 have had their corners sanded down, and what’s left on my list is smaller and less interesting to write about. Which is roughly where a fifteen-year-old package ought to be.

That’s all I have for you today. Keep hacking!

  1. Which is why projectile itself comes back with no siblings on my machine. The cap is working; the answer is a configured group. â†©

  2. projectile-global-ignore-file-patterns, projectile-cmd-hist-ignoredups, projectile-related-files-fn-function, projectile-auto-discover, and the three reviewable-search options that were named after replace. â†©

-1:-- Projectile 3.4 (Post Meta Redux)--L0--C0--2026-08-13T07:30:00.000Z

Protesilaos: About Emacs themes, Solarized, and wallpapers

JTR from the blog The Art Of Not Asking Why sent me a few questions regarding Emacs themes, my opinion on the Solarized colour scheme, and even my choice of wallpaper.

JTR asks about Solarized because I consider Ethan Schoonover’s Solarized an excellent project, given what Ethan was trying to achieve. I asked JTR if I could publish my thoughts and they agreed. JTR has also written the article Back to Solarized.

Note that throughout this article I am only commenting on Ethan Schoonover’s Solarized (the original Solarized). There are many Solarized-like themes out there with lots of additions on top, including higher colour contrast, which are separate themes as far as I am concerned.

I should also point out something I always mention: themes are a personal choice and there are many factors that go into what a person wants. Keep in mind that readability is also a matter of font size, ambient light, and the screen brightness you are using.

To give a simple example, modus-operandi in a very dark room with maximum screen brightness at a fairly large font size will most probably harm you and is thus a terrible choice. But the same setup on a sunny Cyprus day in my well-light house is how you can read the screen without distractions.

Generally though, people forget to adjust their screen brightness, so always do that and rely on environmental light to not tire out your eyes.

Be mindful of the context

Have your thoughts changed about Solarized since the creation of Modus themes? Later, did they change after you created the ef-themes?

In short, no as the technological realities for my case and for the original Solarized remain the same. Ethan Schoonover wanted to have a colour scheme that would work reasonably well with terminal emulators and, by extension, Vim. Most terminal emulators let you define 16 colour values. Then applications running inside the terminal can map those to the objects they display.

Applications running inside the terminal cannot change the 16 colour values of the terminal. This means that Ethan’s Vim configuration necessarily had to have a light and a dark theme that worked with the same 16 colours. Ethan could have developed two distinct colour schemes for the terminal, which would then underpin two separate Vim themes, but I understand how this is a more complex arrangement.

My older Tempus themes project, from my pre-Emacs days, is for terminals, Vim, and friends. Same idea for its predecessor, Prot16.

I think both of those older projects are fundamentally flawed because the technological constraints they are designed to work in result in all sorts of little compromises. To give an example that many Linux users may have noticed: if you use a light theme in a terminal and do apt update && apt upgrade you will get the progress bar with a green background and a black foreground. Colour schemes cannot change that combination, so they make that text virtually unreadable (in a terminal, colour combinations should always be ACCENT + BACKGROUND, not ACCENT + BLACK/WHITE, as the accents are always designed to be visible against the background).

There cannot be one Solarized

Even though the original Solarized was designed to be a single theme, in practice it is not and cannot become as much. There is Solarized Light and Solarized Dark, sharing those same 16 colours. Solarized light has a background that is closer to yellow, while its dark counterpart’s background is closer to blue. This means that accent values which are picked to work with both eventually do not work equally well as they do not evoke the same feeling.

Without even considering the specific values of the Solarized palette, a combination of blue/cyan, magenta, and purple as foreground colours will be on the opposite side of the spectrum to the yellow background and on the same side as the blue background. This means that if we pick blue/cyan, magenta, and purple as our main colours we have a light theme (yellow) with a completely different feel than the dark theme (blue) even though they are supposed to be the “same Solarized”.

Let me outline this for you:

Foreground colours Solarized Light background Solarized Dark background
green, yellow, red/orange Same side Other side
blue/cyan, magenta, purple Other side Same side

So if we force the same mapping of foreground colours, then we have two themes with contrasting harmonies and necessarily distinct characters. In this context we begin to appreciate the importance of judgement: sometimes you achieve likeness through difference. For the table above, inter-theme likeness exists on the diagonals, so either have both themes with “same side” combinations or both themes with “other side” combinations.

This concerns the predominant colours. A theme that uses all accents in equal proportion will look very busy and thus run into other problems.

The designer can, of course, pick a colour that does not follow the overall pattern to produce an emphasis of some sort. Though they cannot start with emphasis as their baseline.

One set of values leads to compromises

Because Solarized Light and Solarized Dark are designed to share the same palette, individual values are picked as a matter of compromise, even though they are “equally good” on paper in terms of distance as measured by various luminance metrics. The blue used for the light theme, for example, must also be good enough with the dark theme. But “good enough” for both may not be optimal for either.

Concretely, the Solarized blue stands out more in the light version than in the dark version (because of the complementarity between yellow and blue). If the light effect is supposed to evoke sunlight on a surface, and if that is the reason green and yellow are desaturated, then the blue should not be as saturated for the light theme. It would have to be a considerably more washed out colour. Though the blue has to be fairly saturated to be readable on the Solarized Dark background and still look like a blue, hence the compromise.

Same idea for the green and yellow colours which are consistent with the light background but would require tweaks to be harmonious with the dark background.

The magenta does not fit any background: it stands out as an exaggeration. To be consistent with the light background, it has to be less saturated. To fit the dark background, it needs to have greater contribution from the blue channel of light (i.e. move closer to purple) or become more pink. But moving in either direction compromises the opposite theme


There is no “bright green” (terminal colour 10), “bright yellow” (terminal colour 11), “bright blue” (terminal colour 12), and “bright cyan” (terminal colour 14). I believe this is because they would not fit both Solarized Light and Solarized Dark.

My themes are designed for Emacs

As noted, Ethan’s Solarized was developed with a specific technology in mind. The same is true for my case: I develop all my themes to work with Emacs. This typically means that I can rely on more than 16 colours and each theme can have its own colour values.

For example, if I choose to apply a green background colour to the “current match” in a search, its value is not the same green I would use for, say, a “DONE” keyword in Org. Whereas in a terminal with 16 colours what you normally do is invert background and foreground colour combinations, which results in things standing out more than they need to (and becomes overwhelming when you want to use many such elements).

Each of my themes relies on its own palette. If I tweak one theme’s colour values, I am only affecting that one theme. Because each theme is its own design, I can make things similar by making them different. For example, modus-operandi-tinted is a “yellow-red theme” (simplifying greatly now) but its counterpart modus-vivendi-tinted is a “blue-purple” theme. On paper, this description alone makes them very different. But in terms of character, they both communicate a similar feel, owning to considerations that follow from what I mentioned above.

Additionally, each theme has its own colour mappings. This means that I do not have to make every theme use, for example, blue for links. In Emacs, I have greater control over what each “application” does, by configuring the relevant faces. There still are constraints, as I cannot for example decide which faces are applied to what portion of text, but this is generally not a problem.

Differences between my theme projects

What’s the difference between prot16 and your ef-themes? They seem to cover similar ideas, but it doesn’t look like prot16 has been updated.

My older projects are for terminal emulators. It is why they were inspired by Solarized. Terminals only have colour schemes though, not themes. The designer cannot control how colours are mapped to objects, so they have no means of enforcing thematic consistency.

A colour scheme sets the 16 values, but otherwise has no mappings for things such as “here is an error message” or “this is a progress bar and things are going well”, or even to specify “I only want green and blue here, but never magenta”.

If a colour-coded message gets a style, it will typically be hardcoded to red, green, or yellow. And if the colour scheme wants to support users with blue-yellow colour deficiency, for example, it will have to add a yellow value where the nominal red is (terminal colour 1 and terminal colour 9). But doing so leads to weird issues because, fundamentally, we are conflating colour names with colour mappings (there may be an application that wants to use yellow text on a red background, but now that actually becomes yellow on yellow, so you get the idea).

In Emacs, a theme designer has more control. They can specify colour values and write exactly how they are used by the relevant faces. For example, we do not only define “blue” but also say “I want keywords in programming modes to be blue”.

The constraint in Emacs is which faces do packages define and how exactly they apply them to elements of the display. A theme cannot, for instance, dictate how many separate faces a programming mode will add to its text. Furthermore, a theme cannot govern how generic faces, like font-lock-variable-use-face are set by modes to do things they are not supposed to do (for example, a mode specifies that face because they like its green colour in the one theme the author is using, but the thing being styled is not a variable and that specific face does not produce an optimal result in its context).

Additionally, themes cannot determine the surrounding text. For example, the Org export dispatcher has keys wrapped in square brackets (and I sent a patch that was merged a long time ago to have decent colours there by default when no theme is used). The bracketing is something the theme cannot change, so it has to do strange things to produce readable text because in an [i] indicator it is hard to discern the i due to how the square brackets limit the perceived boundaries—and you need to apply a highlight so that the user knows which exporter they are picking. An [ i ] is somewhat easier in this regard and a variation of it without the square brackets is even more flexible as then the theme can apply all the colour and typography attributes it needs.

Another constraint with Emacs is that we do not have real padding and margins, the way those are understood in CSS. I think this is fine for the majority of cases, but can be a problem sometimes such as when we need to use a coloured background that spans a small portion of text (e.g. with the Org dispatcher or with the style of rendered keys like <home>).

The gist is that Emacs gives the theme designer more control and in a more integrated way than a terminal+TUIs arrangement. This results in a coherent experience. Terminal-centric environments cannot have that to the same level because of disparate applications trying to form one computing environment, each doing its own thing in the process. I tried inter-application consistency in the terminal, discovered its limitations, and is the single most important reason why I switched to Emacs.

Contrast and readability

My understanding is that the contrast for Solarized wasn’t good enough for you. I don’t know if you have my issue specifically (https://taonaw.com/2023/07/05/halatoion-and-accessiblity.html), but I’m curious: don’t you get halation as well? Doesn’t it bother you, especially if you switch from a dark theme to something lighter? (For example, you read something with a dark background and light text, then switch to a page that has a light background and dark text)?

We measure contrast between colours to have an imperfect approximation of what might work in the average scenario. Though readability is influenced by many more factors beyond the applicable colours. Consider the environmental light and the brightness of the monitor. In addition, check the font size and even the font family you are using. Many font families can produce a halo effect at different sizes, regardless of colour combinations.

Have an extra source of light beside the monitor’s. If you do not, the eye focuses on one spot, which is more tiresome. And always adjust the screen brightness to not be too bright or too dim relative to the environment.

As for fonts, you may benefit from a design that has more open shapes. The exact size is also a factor.

For example, I have a font called Aporetic that is derived from Iosevka. What motivated me to do my own thing is the original monospaced m of Iosevka (maybe it is changed now), which blurred into a box at small point sizes and was thus not nice to read.

The Hack font I used to use (and which I had also modified), had similar issues. The Ubuntu Mono font that Ubuntu and derivative Linux distributions rely on, can have a g with a very short descender at certain point sizes, as well as a dotted zero where the dot is off centre.

I can go on to great details but you get the idea: you have to check all the variables and find what works for you. I am confident in stating that this is never a theme-specific issue.

As for switching between applications, these days most desktop environments have a concept of a light/dark preference, which many websites also follow. For example, GNOME has a menu from where you can toggle the “Dark Style” on/off.

The specific problem you will encounter with Solarized is that practically no other application or website uses it or similar colours. So either you will have to set Solarized everywhere you can, or you will be switching from the yellowish/blueish background to white/black, with whatever context-switching friction that may have.

Thoughts on wallpapers

This is an extra one I’m curious about: what are your thoughts about wallpapers? I noticed, the few times I worked with you and on video, that usually you have a natural scene. Have you ever switched to something else (like a drawing, perhaps an artwork, or maybe even a technical scheme)?

I have used wallpapers of all sorts and find that pictures I take of landscapes or flowers are the best for me. The reason is that they avoid exaggerations. Many pictures that are used for wallpapers are designed to show something in a more pronounced way and are thus calling for your attention. That is fine when the object of attention is the art piece. But when I am doing work on the computer, I do not want other elements to be competing for attention with whatever it is I am working on.

“Avoiding exaggerations” is a principle that informs my lifestyle. It also is why I prefer to live in the mountains and to spend plenty of time outdoors. The mountains do not have the intense stimuli a city has. In a city there are bright lights everywhere, a faster pace to motions, and sharper noises all around. Whereas the mountains are subtle, even though they are full of life.

People who go on a retreat say “I went to this holy place and found peace”, when in reality what likely happened is that they reduced their exposure to exaggerated stimuli and were thus able to lower their baseline intensity for a little while.

Because we are also conditioned by our habits, we do not realise just how much we have gotten used to things in our milieu, even when those are not benign to us.

People want magical solutions when the basics will suffice. We are heavily influenced by our environment and the elements we are exposed to. A stressful place will increase our stress levels. A harmonious place will make us more harmonious. Inconsiderate food choices will disturb us long-term, a poor work-life balance will harm us, and so on.

People think they can brute force their way into calmness or focus by means of “truly believing” in their project. This is a fool’s errand. Changing the scenery and relevant lifestyle will be far more effective than whatever willpower one thinks they can muster on demand.

-1:-- About Emacs themes, Solarized, and wallpapers (Post Protesilaos)--L0--C0--2026-08-13T00:00:00.000Z

Raymond Zeitler: Emacs Numbered Backup Strategy

Emacs produces a backup of each file by default. It does this by appending a tilde to the file's name. For example, Myfile.txt is backed up to Myfile.txt~. It's a reasonable strategy, assuming that a Version Control system is in place.

But in the absence of version control, you're limited to restoring just the previous version; older versions need to be restored from backup.

I use git for coding projects that I share to the public. But I didn't create a repository for my home directory. So when C-c a m TAG1 generated an error instead of a list of TAGged items, I hoped fervently that the latest init file change was to blame.

After finding and fixing the problem quickly, (and realizing I was lucky this time), I resolved to be more diligent about disaster recovery. I looked into what backup schemes Emacs already provides.

Emacs can create unique backups for a file rather than overwriting the previous backup. When Emacs sees a numbered backup for a given file, it will make a new, sequentially-named file for each backup of that file, while continuing to use the "append tilde" method for other files.

Once I renamed (or copied) .emacs~ to .emacs.~1~, Emacs created backups named .emacs.~2~, .emacs.~3~ (and so on). I didn't even need to change a variable -- when Emacs detected .emacs.~1~, it knew that backups of .emacs needed to be numeric, so it acted accordingly. Emacs continues to use bills.org~ as the backup for bills.org. Until I tell it otherwise.

A list of file names in light grey text on a blue background: .emacs; .emacs~1~; .emacs~2~; .emacs~3~;

Read the Emacs Manual to learn more about numbered backups.


1 C-c a m invokes org-tags-view

The default values for the pertinent backup variables are:

  • make-backup-files is t
  • backup-by-copying is nil
  • version-control is nil

-1:-- Emacs Numbered Backup Strategy (Post Raymond Zeitler)--L0--C0--2026-08-12T23:01:35.675Z

Irreal: Emacs Development: Ongoing And Good

One of the things that we Emacers hear with tiring frequency is that Emacs is moribund, old technology loved only by graybeards stuck in the last century. In fact, as actual users know, Emacs is undergoing constant development and is often the first to offer new features that the other editors copy. The most salient examples are Org mode and Magit but there are plenty of more recent examples.

But don’t take my word for it. Over at the Emacs subreddit, gonstrider has a short post relating his experience with all of this. He’s been using an unnamed editor—you’ll have no problem figuring out which one—that keeps having weekly updates that seem to make things worse each time. So he jumped in the deep end and downloaded and built Master (Emacs 32) fully expecting to find lots of broken things.

It didn’t turn out that way. He was, in fact, surprised to discover that almost everything worked well. The only problem he had was evil on Windows and he solved that by simply upgrading evil. As for the rest, he found that Emacs was super-responsive and all the functionality that seemed laggy in that other editor was snappy in Emacs.

There are two lessons here. The first is that Emacs, far from being moribund, is undergoing continuing development and introducing new features. The second point is that Emacs is, unlike some of its competitors, continuing to produce upgrades that simply work and don’t make their users crazy.

Very shortly, we should see the release of Emacs 31 but work on Emacs 32 is already ongoing and amazingly stable for what is, after all, alpha software. The next time someone tells you that Emacs is old and moribund, refer them to gonstrider’s post.

-1:-- Emacs Development: Ongoing And Good (Post Irreal)--L0--C0--2026-08-12T14:38:54.000Z

TAONAW - Emacs and Org Mode: Back to Solarized

Years ago, when I started the old blog1, I discovered Solarized.

I liked it. The colors were beautiful and worked for me, but eventually I switched to the Gotham theme in Emacs, which, if you take a look, is not much different — just a bit darker. I stayed with this theme for a long time, tweaking it a bit after a trip to New Mexico.

When I received my Mac and started working on it at home, I enjoyed its automatic switching between dark and light themes. At the same time, Prot started coming out with more of his excellent Ef Themes, and I adopted ef-reverie for my light theme and ef-night for my dark one (once again, if you look at the images, you’d see they’re not far off from Solarized, especially my choice of a light theme).

This has been the state of affairs until the beginning of this week, when I “found” Solarized again. I discovered Prot himself was heavily influenced by Solarized, and he discussed it several times on his blog. Here’s part of his background story for his project Prot16:

My starting point for developing a colour scheme was Ethan Schoonover’s Solarized. A true masterpiece. I thus settled on a method of using a 16-colour palette that consists of 8 base values and 8 accents. As with Solarized, the themes would have to be able to transition from ’light’ to ‘dark’ environments with minor alteration to the mapping of colours (though not the values themselves).

Prot’s quest is an interesting one. Like me, he enjoyed Solarized, but eventually decided the colors were not contrasting enough for his taste. His reaction was to create his well-known Modus themes, which, along with his Ef-themes and Prot16, set new theming standards for Emacs.

Meanwhile, I have had the other issue since childhood:

I’ve never liked harsh contrast for this reason. As a matter of fact, a more “gentle” contrasting theme is one of the reasons I like working in Emacs. My website and my wiki are a result of these preferences. The black text on the beige background is comfortable on my eyes. Enough contrast to read, but not enough to burn text into my vision, as many dark modes out there do.

While Prot’s ef-themes offer softer contrast (I didn’t check, but it does seem so visually) which I find excellent, I decided to try out bbatsov’s Solarized for Emacs, which I didn’t know about in the past when I first picked up this theme for Emacs — the official Emacs port is not as good in my opinion.

I’ve learned a couple of things I wasn’t aware of. For starters, you can have many variations of this theme, even though the main 16 colors are still preserved. It makes sense when you think about it: one person can choose Solarized’s red (#dc322f in hex) as an emphasis, while another can select its yellow (#b58900) or orange (#cb4b16), for example. Webpages and Emacs take it a step further, as certain texts can be bold or italicized, with solid underlines or dashed ones, etc. All of those changes take place without touching the colors themselves.

Solarized also helps me with a certain itch I’ve had for a couple of months: a dark theme for my blog. I know most of you read my posts in your RSS reader, hopefully with a theme you enjoy, so this won’t matter much. For me, how my site looks and is styled is important, and getting there is fun.

A minimalist (solarized) skyline of a city with a prominent tall building, set against a dark background with waves in the foreground and a red moon in the sky.

  1. This post is here, on the new blog, because I migrated it from the old. The date on it is accurate however, and it’s the first post I have saved in my old archive. The same is true about the New Mexico post. â†©ïžŽ

-1:-- Back to Solarized (Post TAONAW - Emacs and Org Mode)--L0--C0--2026-08-12T14:09:15.000Z

Ashish Panigrahi: Reviewing finished tasks from last week in org-agenda and emacs

Being a PhD student within a research group entails some structure to tracking your progress as you proceed with research. Within the current team that I'm in, we have weekly group meetings where each member presents the tasks that they've completed during the previous week and discusses any issues they might've faced (if any), to our supervisor.

Usually before the meeting, I jot down things that I've finished with pen and paper. But since I'm in the spirit of integrating my workflow with emacs and orgmode, I thought why don't I try implementing this part of my routine also into orgmode.

Enter org-agenda

All my todo items are tracked with org-agenda where I capture every task I need to finish with org-capture1.

I have a custom org-agenda view just for viewing my daily and weekly TODO items, events/meetings scheduled at a certain time and tasks that have a deadline associated with them. Suffice to say, this workflow has replaced all my needs for having a calendar over the cloud, be it Google, Outlook, what have you2. The only downside that I haven't figured out (yet) is to incorporate calendar invites that I receive via email, into my org-agenda automatically instead of manually capturing an event into the agenda view.

With this custom agenda view, as I finish tasks and mark them DONE, they disappear from the current view. This is by design. I figured I could just review my completed tasks by visiting the relevant files (tasks.org, meetings.org, etc. in my case). But who wants to sift through all the completed tasks and filter them manually to only note down the tasks completed during the previous week? Why not automate this with the power of emacs?

Elisp snippet that implements this weekly review

The idea is fairly simple. I'd like to look at the tasks that I've completed since the last group meeting. This assumes I review these tasks on the day of the group meeting (which I do currently3).

We simply then instruct orgmode to filter out the DONE items from the last 7 days. The org-agenda-custom-commands variable is what we'll use to define this custom org-agenda view.

(setq org-agenda-custom-commands
      '(("w" "Finished tasks from last week" tags "CLOSED>=\"<-7d>\""
         ((org-agenda-overriding-header "Finished tasks from last week")
         (org-agenda-archives-mode t)
         (org-agenda-tag-filter-preset '("-emacs" "-personal" "-email"))
         (org-agenda-prefix-format '((tags . " ")))
         (org-agenda-remove-tags nil)))))

Let's go over the various config options one by one:

  • The first line defines the keybinding in the primary org-agenda dashboard and the associated title for the keybinding.
  • tags "CLOSED>=\"<-7d>\"" is pretty self-explanatory. We filter for items that were completed in the last 7 days.
  • org-agenda-overriding-header: Simply defines the title of the agenda view.
  • org-agenda-archives-mode: This is set to t (True) meaning to also look at the archived entries from last week. I typically archive my entries frequently so this is helpful.
  • org-agenda-tag-filter-preset: I don't want personal tasks appearing in this view, so I remove any items that have the relevant tags.
  • org-agenda-prefix-format: By default, the agenda view contains information about which file the entries belong to (in my case, it's tasks.org). This appears as tasks: per entry in the agenda which unnecessarily clutters the view.
  • org-agenda-remove-tags: I don't want to remove the tags of the various entries (they are useful to me) that appear in the right margin. Hence this variable is set to nil (False).

To invoke the view, we can simply run M-x org-agenda and hit w to get into the view. My org-agenda dashboard looks like this:

My org-agenda dashboard

All the above keys are present in the default view, except my custom views (depicted by j and w keys).

Invoking custom org-agenda views (slightly) faster

For my workflow, I mostly deal with just the two org-agenda views (represented by j and w). It's a good idea to have a global keybinding to quickly invoke these views. Let's define the function and keybinding for it.

(defun pani/done-items-prev-week-org-agenda ()
  "Custom function to immediately jump to my custom org-agenda view."
  (interactive)
  (org-agenda nil "w"))

To invoke the function, we define the keybinding like via a use-package macro:

(use-package org-agenda
  :ensure nil
  ;; Don't need to go through org-agenda template for custom agenda
  :bind (:map global-map
	      ("C-c w" . pani/done-items-prev-week-org-agenda)))

I can then just hit CTRL c w to bring up this new agenda view.

That's it! This should make things easier for me during our group's weekly meetings.

Special thanks to Marci and my sister, Alaka Panigrahi for pointing out grammatical errors and typos.
  1. More on this in a future blog-post. ↩

  2. This is fully offline. Between my personal laptop and office PC, I simply use syncthing to sync all my orgmode files via peer-to-peer. ↩

  3. This is perhaps not the best approach but is a good enough starting point for me. ↩

-1:-- Reviewing finished tasks from last week in org-agenda and emacs (Post Ashish Panigrahi)--L0--C0--2026-08-12T00:00:00.000Z

Raymond Zeitler: How Much Management Does Knowledge Need?

The topic of this month's Emacs Carnival interests me, even though I "dump ... notes into a directory and simply grep for information ...."0 Org does not work the way my brain works,1 or rather, my brain doesn't work the way Org works.

I do use Org. At first I created a few Org files (work.org and home.org, and maybe todo.org) thinking I could rigidly dedicate all my topics to them.

Over time I had about dozen files, one for nearly every major facet of my life until I gave up. Why? Because it really doesn't matter where I put Org TODOs and their notes. When I need to locate something, Org provides a way to search entries for a regular expression, which is what grep does, sans all that Unixy CLI stuff.

So faced with the task of paying one of my wife's medical bills, I could agonize over whether to put the task into wife.org or bills.org or medical.org.2 Or I could just:

  • establish the task somewhere,
  • act on it
  • link to a receipt
  • mark it done3

I created a special template for org-capture, which places these one-off tasks into todo.org. And it clocks into the task, too.

A topic with even greater complexity is "education." I'm a lifelong learner; my edu.org file has (only) 29 headings, all of which have at least a few subheadings. If I learn about an Emacs feature, I might want to write about it, so I'll put a task into blog.org that links to it.

Occasionally a post will span multiple topics in edu.org, topics such as Emacs, Vivaldi, LibreOffice, HTML or LaTeX. In a year from now, which file should I look for it in? The keystroke C-c a s comes to the rescue.4 I might search for "Sacha" because all I remember is that I discovered an Emacs feature on Sacha's website.5 Incidentally, I get 13 hits when I search for "Sacha," one of which was the one I had in mind.

The question I think all Emacs users face is, "At what point does tweaking Emacs (and self-quantifying tools in general) tip the scales from aiding our progress to impeding it?" I dread going down a Knowledge Management rabbit hole,6 but I'm sure I'll enjoy the leap.

0 Charlie Holland's Search for Knowledge

1 A tree works like your brain.

2 Don't even get me started on insurance.

3 All of this occurs while clocked in to the task.

4 In this case, C-c a is bound to org-agenda.

5 Sacha Chua's blog

6 Can one gain knowledge of Knowledge Management Systems without having a Knowledge Management System in which to record that knowledge? That may be a Zen koan for someone.

-1:-- How Much Management Does Knowledge Need? (Post Raymond Zeitler)--L0--C0--2026-08-10T22:19:40.478Z

Marcin Borkowski: Emphasizing a region in Org mode

One of the most basic features of Org mode is its markup. It is basically the Markdown of the Emacs world (although some people, like Karl Voit, claim that it’s superior to Markdown), and it lets do basic things like italic, bold or verbatim very easily – at least if you’re writing text. I, on the other hand, happen to edit text pretty often. This is very different from writing. When I write, typing a slash before and after something I want to emphasize is easy. When I edit and just want to make some text bold, putting stars around it is much less convenient.
-1:-- Emphasizing a region in Org mode (Post Marcin Borkowski)--L0--C0--2026-08-10T20:35:30.000Z

Sacha Chua: 2026-08-10 Emacs news

The Navigation category has a couple of interesting window configuration tips this week in case you prefer to work with just one window or have multiple windows with some of them dedicated to specific buffers.

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

View Org source for this post

You can e-mail me at sacha@sachachua.com.

-1:-- 2026-08-10 Emacs news (Post Sacha Chua)--L0--C0--2026-08-10T13:49:50.000Z

Andros Fenollosa: Web Design for Terminal Browsers

This is a purely-for-fun exercise: a guide to web design for terminal browsers. Starting from the text browsers people actually use, I am going to pull out a set of rules in case you ever decide to build a site meant to read and look its best in a terminal. It is not an alternative to Gemini, Gopher or a smallweb attempt, but a set of best practices so your site reads well in a terminal browser, and even so you can build Web Apps for the terminal.

Five engines, one test

The first step is to install the five text engines people actually use and feed them the same test pages, isolating one feature at a time.

  • w3m (0.5.6): the most capable with tables, and it even draws images in terminals that allow it.
  • lynx (2.9.3): the patriarch, the lowest common denominator since 1992.
  • links (2.30): fast, with a little bit of CSS.
  • elinks (0.20.0): the cousin with bigger ambitions, it even has an optional CSS engine.
  • EWW: the Emacs one.

Now we look for the compatibility crossovers. You test HTML and CSS features and see how each engine interprets them.

The results:

Feature EWW w3m lynx links elinks
JavaScript No No No No No
<style> / <link> sheets No No No No Partial
display:none inline Hides Shows Shows Hides Shows
display:none by class Shows Shows Shows Shows Shows
inline color Yes (with contrast) Depends on the terminal Depends on the terminal Depends on the terminal Depends on the terminal
text-align No No No No Yes
Data tables Yes Yes Yes (no borders) Yes Yes
colspan / rowspan Yes Yes Flattens Yes Yes
Graphical images Yes In the terminal No No No
alt of a broken image Yes Yes Yes Yes Yes
srcset Yes (picks resolution) alt/src alt/src alt/src alt/src
data: URI in an image Yes alt alt alt alt
GET/POST forms Yes Yes Yes Yes Yes
Collapsible <details> No No No No No
Spacing of <article>, <section>... No No No No No
<base href> Yes Yes Yes Yes Yes
<title>, <pre>, <hr>, lists Yes Yes Yes Yes Yes

From this we draw some general conclusions:

  • JavaScript does not exist. In any of them. It is not slow or partial. There simply is no interpreter.
  • CSS is almost a mirage. Stylesheets, whether <style> or <link>, are ignored in all of them except elinks, which applies a few things like text-align. Four out of five engines do not read your CSS. Design as if it were not there.
  • display:none is a trap. If you hide something with a class (class="hidden" and .hidden{display:none} in your sheet), all five engines show it. Every one. Because they do not read the sheet: do not hide anything important with CSS. If it should not be seen, do not put it in the HTML.
  • Images are text. Only EWW (and w3m in some terminals) actually draws the image. In the rest, the image is its alt attribute. All five fall back to alt when the image is broken, and an image with no alt leaves you a [hero] with the file name, or nothing at all. alt is not accessibility for others, it is your content.
  • <details> does not fold. None of the five make it interactive. The summary and the body always show, one after the other.
  • HTML5 semantic tags are invisible. article, section, nav, header, footer, main, aside: they are transparent containers. They do not add a single line break. Their value is semantic, not visual.

With that, we can lay down a few design lines and best practices.

8 rules for publishing to the terminal

1. DOM order rules

There is no float, no flex, no grid, no order. Whatever you put first in the HTML comes first on screen. So place the content right after opening the <body> and send the long navigation and the footer to the end. A reader who opens your article does not want to tab through thirty menu links before reaching the first sentence.

2. Mark structure with tags, not styles

Real headings <h1>..<h6> for the hierarchy, never a <div class="big-title">. Lists with <ul>/<ol>, definitions with <dl>. Quotes with <blockquote>, which all of them indent. Code with <pre> and <code>. Each engine gives them its own treatment: use them for what they mean.

3. Your page must read with CSS turned off

This is the touchstone. If you disable CSS and your page becomes unreadable, it is not the terminal browser's problem, it is your HTML's problem. Spacing (margin, padding, line-height) does not exist: the separation comes from paragraphs. Structure with real <p>, not with loose <br>.

4. Do not convey information with color alone

A "required field in red" or a "green = correct" evaporate. Inline color is the most fragile thing in the table: it depends on the terminal and its configuration, and in many cases it does not even show. Always pair color with text or a symbol. An "Error:" in front, an asterisk, anything.

5. Tables for data only, never for layout

EWW and w3m draw a surprisingly good ASCII grid, colspan and rowspan included. But a layout table produces an absurd, unreadable grid. Watch the width: if the columns add up to more than the terminal's, the experience degrades. Fewer columns and short cells win.

6. Images with a descriptive alt and srcset

alt is what you see in four out of five engines. Make it a sentence, not an image1.png. And if the image is pure decoration, give it an explicit alt="": that way the reader ignores it instead of reading you the file name. A missing alt and an empty one are not the same thing. Offer srcset with several resolutions, and the engine that can show images will pick the right one. And do not rely on an image to communicate anything critical, because in most engines they do not load at all.

7. Real forms

No JavaScript submissions. A <form> with its action and its method (GET or POST), and an <input type="submit"> or a <button>. Put a name on every field: the engines collect by name, and a field without one is lost. Associate a <label> with each one.

8. Headers that do count

<title> always, descriptive: lynx and links center it at the top, and all of them use it to identify the page. <meta charset> in UTF-8 as an encoding fallback. <base> if you use relative links, which all of them respect. And serve over HTTPS, since several engines flag the certificate status.

TerminalSpeed Insights

A guide you cannot run is a list of good intentions. So I wrote a prototype. It is a Python script that reads an HTML file, or a URL, and gives you back a readability score with the specific warnings. The number is a heuristic, not a science. I hand out the points by eye, an error weighs more than a warning and that is that. What really matters is the list of warnings, not the scoreboard.

You can download it from its repository, TerminalSpeed Insights, and run it against any page:

python3 terminalspeed.py https://your-site.dev/

Running it over a few popular sites, the ones leaning more on content than design, I get some numbers:

Site Score
andros.dev (this blog) 100
text.npr.org (NPR's text-only version) 100
motherfuckingwebsite.com 100
emacswiki.org 94
suckless.org 94
gnu.org 88
man pages on man7.org 88
Wikipedia (an article) 46
Hacker News 55

At the top are the wikis, the docs, the text sites and the ones that fly the minimalism flag. At the bottom, curiously, two of the sites most loved by people who read in the terminal. It is no coincidence: the ones that score high serve the content first and lean on the tags, not on CSS. EmacsWiki, for instance, is almost plain HTML, and that is why it reads on anything that can show text.

Still, these are numbers not every browser shares. Open Hacker News in w3m and you will see the front page perfectly readable, with its numbered list of headlines, despite that 55. w3m draws tables so well that it survives. The validator is not wrong to penalize it, it flags real friction, but friction is not always a death sentence. Take the score as a guide, not a verdict: a 100 almost guarantees it reads well, a low number tells you where to look.

Not just documents: a Terminal Web App

So far I have talked about documents: articles, cards, docs pages. We still have to squeeze the forms: GET and POST work in all five engines. And a form that works is, no more and no less, an application.

Let me show you with a coffee shop:

<form action="/order" method="post">
  <p><label>Your name <input type="text" name="name"></label></p>
  <p><label>Quantity <input type="number" name="qty" value="1"></label></p>
  <p><label><input type="checkbox" name="no_milk" value="yes"> No milk</label></p>
  <p><button type="submit">Order coffee</button></p>
</form>

And this is how it looks in EWW, the Emacs browser. The fields are edited with the keyboard; here I have already filled in the name, the quantity and checked "No milk":

Coffee order form in EWW, the Emacs browser, with the name "Bob", a quantity of 2 and the "No milk" checkbox ticked, and an "Order coffee" button

You hit submit and the <form> does a POST. The server responds with a redirect (the good old Post/Redirect/Get pattern) and the browser paints the confirmation, all without leaving the keyboard or touching the mouse:

Order confirmation in EWW, the Emacs browser: "Order confirmed. Coming right up: 2 coffees without milk for Bob. Total: 2.40 EUR" with a link to place another order

Look at the detail: heading in bold, the link colored, the quantity and the no-milk option collected correctly. It is a stateful app, interactive, with no JavaScript and without a single kilobyte of framework. And yes, it scores 100 with the validator.

It is a real application: it lives in the text browser, but it looks just as good in Chrome or Firefox, because underneath there is only HTML. You get a terminal interface without touching ncurses or any TUI library. Your toolkit is HTML, your renderer is the browser and your logic lives on the server. The same app, a language you already know, and it works everywhere.

Conclusion

It has been a fun investigation. Almost nobody designs their sites for the terminal, it is a minority and, even so, it is an audience that exists. And there is no shortage of reasons: these are visitors who want accessibility or speed, who are on low resolutions or who work with pipes. More than once I have found myself doing quick searches in EWW, or reading an article there for the visual comfort.

You can take it as a curious article or as another approach to web design. I, at least, will start looking at my pages with different eyes.

I hope you enjoyed this other point of view on web design.


Help me keep writing Every coffee gives me a push toward the next article. Sure, it's on me!

Send an email to comment+article-70d817e8@andros.dev to leave a comment. The subject will be ignored.

-1:-- Web Design for Terminal Browsers (Post Andros Fenollosa)--L0--C0--2026-08-10T13:47:15.000Z

James Cherti: single-window.el - Always Open Emacs Buffers in the Current Active Window

Build Status
License

The single-window package forces Emacs to open buffers in the current active window.

It keeps your carefully arranged layouts intact, reduces visual clutter, and provides a much more predictable workflow.

To ensure it does not break standard Emacs functionality, the package is built to handle the following edge cases and integrations out of the box:

  • Transient and Magit: Excludes Transient buffers by default, which ensures that Magit popup menus render correctly and manage their own window placement.
  • Ediff control panel: Excludes the Ediff control interface so it can maintain its specific layout requirements without breaking.
  • Temporary and utility buffers: Ignores the minibuffer, asynchronous Emacs warnings, Org capture popups, and built-in *Completions* buffers so they do not hijack your active workspace.
  • Dedicated windows: Safely handles dedicated windows in the background (e.g., grep-mode or embark-export), temporarily un-dedicating them to load the buffer without throwing errors or breaking the layout.
  • Org-mode integrations: Configures Org-mode to open source blocks, the agenda, and indirect buffers directly in the active window
  • Manual overrides: Allows you to temporarily bypass the single-window enforcement by passing a prefix argument (e.g., C-u) before running a command.

The package also provides the following customization options:

  • Custom window rules: Provides a setting (single-window-respect-display-buffer-alist) that lets you prioritize your own custom display rules for specific buffers, while falling back to the single-window behavior for everything else.
  • Customizable exclusions: Allows you to define additional exclusions via the single-window-exclude-regexps variable, which accepts a list of regular expressions to match ignored buffer names.
  • Popper integration: Provides single-window-exclude-popper (disabled by default) to allow popper to bypass the single-window package enforcement for its popups.

If this project helps your workflow, please consider supporting it by ⭐ starring single-window on GitHub and sharing it on your website, blog, Mastodon, Reddit, X, LinkedIn, or other social media platforms so other Emacs users can discover its benefits.

Installation and Usage

Emacs: use-package and straight (Emacs version < 30)

To install single-window with straight.el:

Step 1: It if hasn't already been done, add the straight.el bootstrap code to your init file.

Step 2: Add the following code to the Emacs init file:

(use-package single-window
  :straight (single-window
             :type git
             :host github
             :repo "jamescherti/single-window.el")
  :config
  (single-window-mode 1))

Alternative installation: use-package and :vc (Built-in feature in Emacs version >= 30)

To install single-window with use-package and :vc (Emacs >= 30):

(use-package single-window
  :vc (:url "https://github.com/jamescherti/single-window.el"
       :rev :newest)
  :config
  (single-window-mode 1))

Alternative installation: Doom Emacs

Here is how to install single-window on Doom Emacs:

Step 1: Add to the ~/.doom.d/packages.el file:

(package! single-window
  :recipe
  (:host github :repo "jamescherti/single-window.el"))

Step 2: Add to ~/.doom.d/config.el:

(after! single-window
  (single-window-mode 1))

Step 3: Run the doom sync command:

doom sync

Frequently Asked Questions

What does this provide over display-buffer-alist?

The single-window package provides a minor mode that handles edge cases.

While similar behavior can be replicated by adding a catch-all rule to display-buffer-alist, this often disrupts standard Emacs functionality.

For instance, forcing all buffers into the active window via display-buffer-alist interferes with packages that rely on specific user interface layouts, such as Ediff, Transient (which includes Magit), Org Agenda, and many others. This package resolves this by providing default configurations that natively exclude these specific edge cases.

Additionally, the package manages dedicated windows. For example, if a user attempts to open a file directly from a dedicated grep-mode or embark-export search results buffer, standard display rules will often cause Emacs to split the frame. The single-window package intercepts this action, temporarily removes the dedicated flag so the buffer can load in the active window, and then restores the original state.

It also includes a fallback mechanism. If Emacs is forbidden from using the current window (e.g., you invoke a command while inside the minibuffer), Emacs usually splits the frame. This package catches that edge case and safely uses another existing window instead.

Finally, the package allows bypassing the strict window enforcement for individual commands by supplying a prefix argument (C-u).

What is the difference with popper?

The single-window and popper packages are not mutually exclusive and can be used simultaneously.

The popper package displays temporary buffers in a dedicated popup area, typically at the bottom of the screen, which allows for quick dismissal.

In contrast, single-window prevents automated window splitting. It also provides a mechanism for exceptions, allowing specific packages, such as Transient, and popper itself, to bypass the strict single-window enforcement and split the frame as originally intended.

Author and License

The single-window Emacs package has been written by James Cherti and is distributed under terms of the GNU General Public License version 3, or, at your choice, any later version.

Copyright (C) 2026 James Cherti

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program.

See also

Links

Other Emacs packages by the same author:

  • compile-angel.el: Speed up Emacs! This package guarantees that all .el files are both byte-compiled and native-compiled, which significantly speeds up Emacs.
  • outline-indent.el: An Emacs package that provides a minor mode that enables code folding and outlining based on indentation levels for various indentation-based text files, such as YAML, Python, and other indented text files.
  • easysession.el: Easysession is lightweight Emacs session manager that can persist and restore file editing buffers, indirect buffers/clones, Dired buffers, the tab-bar, and the Emacs frames (with or without the Emacs frames size, width, and height).
  • vim-tab-bar.el: Make the Emacs tab-bar Look Like Vim's Tab Bar.
  • elispcomp: A command line tool that allows compiling Elisp code directly from the terminal or from a shell script. It facilitates the generation of optimized .elc (byte-compiled) and .eln (native-compiled) files.
  • tomorrow-night-deepblue-theme.el: The Tomorrow Night Deepblue Emacs theme is a beautiful deep blue variant of the Tomorrow Night theme, which is renowned for its elegant color palette that is pleasing to the eyes. It features a deep blue background color that creates a calming atmosphere. The theme is also a great choice for those who miss the blue themes that were trendy a few years ago.
  • Ultyas: A command-line tool designed to simplify the process of converting code snippets from UltiSnips to YASnippet format.
  • dir-config.el: Automatically find and evaluate .dir-config.el Elisp files to configure directory-specific settings.
  • flymake-bashate.el: A package that provides a Flymake backend for the bashate Bash script style checker.
  • flymake-ansible-lint.el: An Emacs package that offers a Flymake backend for ansible-lint.
  • inhibit-mouse.el: A package that disables mouse input in Emacs, offering a simpler and faster alternative to the disable-mouse package.
  • quick-sdcv.el: This package enables Emacs to function as an offline dictionary by using the sdcv command-line tool directly within Emacs.
  • enhanced-evil-paredit.el: An Emacs package that prevents parenthesis imbalance when using evil-mode with paredit. It intercepts evil-mode commands such as delete, change, and paste, blocking their execution if they would break the parenthetical structure.
  • stripspace.el: Ensure Emacs Automatically removes trailing whitespace before saving a buffer, with an option to preserve the cursor column.
  • persist-text-scale.el: Ensure that all adjustments made with text-scale-increase and text-scale-decrease are persisted and restored across sessions.
  • pathaction.el: Execute the pathaction command-line tool from Emacs. The pathaction command-line tool enables the execution of specific commands on targeted files or directories. Its key advantage lies in its flexibility, allowing users to handle various types of files simply by passing the file or directory as an argument to the pathaction tool. The tool uses a .pathaction.yaml rule-set file to determine which command to execute. Additionally, Jinja2 templating can be employed in the rule-set file to further customize the commands.
  • kirigami.el: The kirigami Emacs package offers a unified interface for opening and closing folds across a diverse set of major and minor modes in Emacs, including outline-mode, outline-minor-mode, outline-indent-minor-mode, org-mode, markdown-mode, vdiff-mode, vdiff-3way-mode, hs-minor-mode, hide-ifdef-mode, origami-mode, yafolding-mode, folding-mode, and treesit-fold-mode. With Kirigami, folding key bindings only need to be configured once. After that, the same keys work consistently across all supported major and minor modes, providing a unified and predictable folding experience.
  • buffer-guardian.el: Automatically saves Emacs buffers without requiring manual intervention. By default, it triggers a save when the user switches to another buffer, switches to another window or frame, Emacs loses focus, or the minibuffer is opened. Beyond standard file buffers, buffer-guardian also manages specialized editing buffers such as org-src and edit-indirect. Additional features, disabled by default, include periodic or idle-time saving of all buffers, automatic exclusion of remote, nonexistent, or large files, and support for custom exclusion rules via regular expressions or predicate functions.
-1:-- single-window.el - Always Open Emacs Buffers in the Current Active Window (Post James Cherti)--L0--C0--2026-08-10T12:17:11.000Z

TAONAW - Emacs and Org Mode: Emacs Config Gems - Part 4

Part four of Emacs Config Gems is about basic file operations and save functions. I realized as I was working on this that I have the frame configuration here, which is the wrong place for it. I will change it after posting. Alright, here we go.


When deleting a file in Emacs, we should use the system’s trash (or recycle bin) instead of deleting the file permanently, giving us a chance to restore the file in case we’ve made a mistake.

In addition, since we have this turned on, we can also tell Emacs to always delete non-empty directories (because they’ll go to the trash) instead of prompting. It’s just faster.

(setq delete-by-moving-to-trash t)
(setq dired-recursive-deletes 'always)

Let’s set the frame Emacs starts with to be a bit bigger. In Emacs, these measurements are in lines and columns (character width), so we’re basically saying: “make the default frame 100 characters wide and 55 lines high.”1

(setq default-frame-alist
   '((width . 100)
     (height . 55))
    )

We might want to move Emacs’ backup files and temp files (those are the ones with ~ and #)2 to a different dedicated folder (emacs-bks) instead of the main Emacs folder. Since Emacs won’t just create the directory for us, let’s ask it to do so3. It won’t throw an error if we already have it.

(make-directory "~/emacs-bks" t)
(setq backup-directory-alist
      `((".*" . "~/emacs-bks")))
(setq auto-save-file-name-transforms
      `((".*" "~/emacs-bks" t)))

Now, let’s turn on the option to automatically save files we are working on inside Emacs, and set how long Emacs needs to be idle before auto-save kicks in. In my case, 300 seconds (= 5 minutes) is good enough for idle time. Importantly, I want to emphasize again, idle time. It will not auto-save the file as long as you type, so if you type non-stop for an hour and suddenly lose power, this will not save you, since Emacs didn’t get the chance to be idle for 5 minutes (or even 30 seconds, which is the default). For this, we have auto-save-interval, which auto-saves every 300 characters (globally in Emacs) by default. In my case, I’m lowering it here to 100, since I write plenty of quick notes (using org-add-note, C-c C-z), and it’s common for me to keep using Emacs between those without hitting the 300 mark. Both of those are described in the manual under auto-saving.

(auto-save-visited-mode t)
(setq auto-save-visited-interval 300)
(setq auto-save-interval 100)

Auto-revert checks the buffer we’re visiting against the saved file itself. If the buffer has no changes, but the saved file does have changes, it loads those changes into the buffer silently. However, if the buffer has changes (meaning, we’re writing something into the file and we haven’t saved yet), it won’t do a thing and won’t say anything. Since my particular setup means that all of my org files are inside a folder synced by Syncthing, this is ideal: if I save changes to a file on my Mac, when I switch to my Linux desktop I’ll have the file and the buffer visiting it refreshed and ready to go, provided of course that Syncthing is working and the sync is complete4.

(global-auto-revert-mode t)

Activate recentf mode, so Emacs has a list of the recent files we worked on last time, up to 30 by my config. 10 is too little; 50 starts to feel more like “everything I opened this month,” which defeats the purpose. Useful when we don’t want to navigate to the same file each time, and we don’t want to have a bookmark. Note: since I’m using consult, recent files are integrated and show under the file header in the consult minibuffer, and can be quickly viewed with f while visiting the consult minibuffer.

(recentf-mode t)
(setq recentf-max-saved-items 30)

  1. There are a couple of more interesting parameters (options) to choose from. For example, alpha is a thing, if you like your windows to be translucent (and you can set it so that the frame in focus has a different degree of alpha than those out of focus). I played with it a bit, but eventually found it to be too distracting. â†©ïžŽ

  2. But what are those anyway, and why do we need them? Let’s talk about auto-save first. It’s been there since the beginning. In the early 80s (and even before), when the idea of computers always being on and connected to some virtual cloud was science fiction, people lost their data quite often, either by hardware failure, power failures, and/or user errors, like hanging up the phone in the middle of a data transfer (these were the days of dial-up modems). Auto-saving a file was a feature that was a solution to a very common problem, and we can see it in computer magazines from the era (you can see those in the internet archive, look at page 70 in this magazine for example). As for backups, it’s a similar idea, but the purpose is slightly different. Think of backup literally means “back up.” The idea here is to restore the file from before you saved, backing up (as in going back) to what it was before you touched it. This idea is more closely associated with file-versioning, which Emacs also has, but that’s a slightly different story and it’s turned off by default. To differentiate the auto-save from backup files: In Emacs, auto-saved files are the ones marked with hashtags and the backup files are the ones with a trailing tilde: #autosave-file# and last-save-file~ â†©ïžŽ

  3. There are a bunch of these helpful OS-file level functions available to us from inside Emacs. They are scattered in the Lisp manual in the file section, depending what you’re looking for. If you want a quick cheat-sheet of sorts with examples, Xah Lee has something a bit old, but still relevant↩

  4. There are other functions around revert, particularly auto-revert-avoid-polling, which is off by default. it’s a bit confusing: auto-revert-avoid-polling means, avoid automatically polling for reverting. By default, this is nil (false) which means polling is on: Emacs does its own polling to check if a file was changed every 5 seconds by default (this is determined by auto-revert-interval). If this is flipped to true, Emacs does not check the file, and instead relies on the OS file notification system (inotify in Linux, FSEvents on macOS). This is good if we want to save battery and CPU cycles: our system checks if files were changed all the time, so Emacs just relies on that. In my case, with Syncthing, this is a bad idea because the OS may or may not catch changes done by Syncthing. My Desktop is always connected to power and my Mac is usually docked so leaving it as default makes sense. Check out the manual for auto-saving↩

-1:-- Emacs Config Gems - Part 4 (Post TAONAW - Emacs and Org Mode)--L0--C0--2026-08-10T00:01:32.000Z

Raymond Zeitler: Vakana -- Sneak a Peek

I installed Donovan Ratefison's brand-new Vakana app1 on my Android device this morning. After following the tutorial, I exported the thread as an Org-mode-compatible package in a ZIP and emailed it to myself in order to transfer it to my desktop.

After opening the Org file that Vakana generated, I exported the entire buffer to HTML. The result is shown below. The only change I wanted to make was to scale the bead of each heading 25%.2

It's fortuitous that Donovan Ratefison released Vakana in a month whose Emacs Carnival theme is "The Search for Knowledge," because Vakana provides a unique way to manage one's knowledge. I'll need to become proficient in it before adding it to my workflow. So my eventual Emacs Carnival submission will be more pedestrian.


1 Learn about Vakana here or download here.

2 The images used for beads are SVG files. However this blogging platform does not support that file type, so I converted them to GIF for this post. This should explain why they don't look awesome.

Your first thread

Your first thread
A 90-second tour. Walk it bead by bead, then make it yours — delete it, or reload it anytime from Settings → Pro Tips.

1. The thread

2. Start here · a 90-second tour

[2026-08-09 Sun]

First Bead, Marked 0
Welcome — and if this is all new, that’s perfectly fine. We’ll go gently.

Vakana is the Malagasy word for beads — there’s no other word for them in the language, and it’s where this app takes its name. The idea is old and simple: you turn a moment into a small bead — a little coloured token — and beads you keep together form a thread you can walk back along, like beads on a string. (These six notes are already a thread — “Your first thread.”)

That’s the whole shape of it. What a bead means is always yours — the app never decides that for you.

This is a short, hands-on tour: five steps, each one a thing you try yourself.

Next → 1/5 · Tap the bead.

3. 1/5 · Tap the bead

[2026-08-08 Sat]

Second Bead, Marked 1
,*Tap the bead* at the top of this note — the composer opens. That’s where you pick a bead’s shape, pattern, mood-colour and a tiny glyph (this one’s is the number 1ïžâƒŁ).

Eight shapes, eight patterns, eight moods — small enough to learn, wide enough for anything. There’s no legend: a circle isn’t “an encounter” unless you decide it is.

How does the user navigate the beads? Now I see "< Prev" and "Next >" so it's obvious.

Next → 2/5 · Open the thread.

4. 2/5 · Open the thread

[2026-08-07 Fri]

Third Bead, Marked 2
You’re on a thread right now — a chain of beads strung together on purpose. Open “Your first thread” (tap its chip here, or find it in the Threads view) and you’ll see all six beads in a row you can walk, bead by bead.

The app also keeps a quiet thread for every day you write, under Days in the Threads view — built for you, no effort.

Next → 3/5 · Comment.

5. 3/5 · Comment — swipe me right

[2026-08-06 Thu]

Some thoughts arrive after the moment. Instead of rewriting a note, leave a comment in its margin.

Try it: tap the 💬 at the top of this note — or, back in your list, swipe this row to the right. Either opens the margin, where you can jot a thought or even set it to remind you later.

(The next bead, 4ïžâƒŁ, already has a comment — spot the 💬 on its row.)

Next → 4/5 · Long-press the bead.

5.1. Comments

5.1.1. Comment — [2026-08-09 Sun 18:50]

The tutorial shows an icon for comment as a white-filled rectangular balloon with three dots. However the actual icon is a balloon that's an empty circle

6. 4/5 · Long-press a bead in the list

[2026-08-05 Wed]

Fifth Bead, Marked 4
Beads repeat, and you’ll want to find a bead’s kin. This is a thing you do from your list, not from inside a note — so head back to the list first.

There, press and hold any bead: the list instantly filters to every bead like it. (Long-press is the move most people miss.)

Want finer control? Tap the funnel at the top of the list to filter by shape, mood, pattern or glyph, then gather the results into a brand-new thread.

Next → 5/5 · Make it yours.

6.1. Comments

6.1.1. Comment — [2026-08-09 Sun 18:22]

I keep coming back to this one — long-pressing a bead is the move most people miss.

6.1.2. Comment — [2026-08-09 Sun 19:14]

The alarm clock icon next to the note field is for a reminder, not a timestamp. It puts it on the next business day at 9:00 a.m., perhaps. I pressed it at 3:00 p.m. on Sunday August 9th, and then entered the reminder for the next day at 9:00 a.m..

7. 5/5 · Close it, then make it yours

[2026-08-04 Tue]

Sixth Bead, Marked 5
This thread is closed — notice the clasp. Closing marks a loop that finished; you can reopen it anytime.

A finished thread can leave the app: open it and Export to turn it into a web page or a slide deck.

That’s the whole grammar — bead, thread, comment, close, export. Now make it yours:

‱ Delete this tour — swipe any note left, or remove it in one tap from Settings → Pro Tips → Interactive tutorial. ‱ Want it back later? Reload from the same place.

Now write your first real moment, and bead it however you like.

Created: 2026-08-09 Sun 16:42

Validate

-1:-- Vakana -- Sneak a Peek (Post Raymond Zeitler)--L0--C0--2026-08-09T23:56:55.107Z

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