Irreal: Red Meat Friday: Exiting Vim (Again)

One would think—I certainly did—that the “exiting Vim” meme has been exhausted by now and only invoked by the type of people who inspired the eternal September joke. But, it turns out, there is some new wine for the old wineskins. Nicola Fankhauser has this offering, which I must admit, is both new and funny.

And, of course, it offers the excellent advice to embrace Emacs instead. As Fankhauser suggests, someone is trying to start a war. It’s probably some AI that wants to get back to making paper clips. Don’t let this happen to humanity; switch to Emacs while there’s still time.

UPDATE [2023-01-27 Fri 15:14]: Read → Red

-1:-- Red Meat Friday: Exiting Vim (Again) (Post jcs)--L0--C0--January 27, 2023 07:43 PM

Sacha Chua: Adding a custom header argument to Org Mode source blocks and using that argument during export

I sometimes want to put long source blocks in a <details><summary>...</summary>...</details> block when I export to HTML, so that they're tucked away in a collapsible block. I tried using to define my own #+begin_my_details "summary text" ... #+end_my_details block, but source blocks inside my_details doesn't get fontlocked properly while in the Org file. I wanted to add a :summary attribute to the regular src blocks, and to change the HTML export to wrap the code in details if the summary was specified.

Code for adding a :summary argument and using it during export
(setq org-babel-exp-code-template "#+begin_src %lang%switches%flags :summary %summary\n%body\n#+end_src")
(defun my-org-html-src-block (src-block _contents info)
  (let* ((result (org-html-src-block src-block _contents info))
          (org-with-point-at (org-element-property :begin src-block)
         (summary (assoc-default :summary (elt block-info 2))))
    (if (member summary '("%summary" ""))
      (format "<details><summary>%s</summary>%s</details>"
(with-eval-after-load 'ox-html
   (org-export-backend-transcoders (org-export-get-backend 'html))
   'src-block 'my-org-html-src-block))

So now I can use it by specifying blocks like this:

#+begin_src emacs-lisp :summary "Code for adding a :summary argument and using it during export"
;; code goes here

It took me a bit of digging around to figure this out. When I added the :summary attribute, org-babel-get-src-block-info found it when I was in the Org file, but by the time my-org-html-src-block was called, the block had been replaced with a copy that didn't have the header argument. I dug around using edebug's d command for displaying the backtrace, stepping through various functions. I found out that in the process for exporting source code blocks, org-babel-exp-code replaces the source block with the value of org-babel-exp-code-template, substituting certain values. Adding the summary flag to that and retrieving the summary information using org-babel-get-src-block-info worked. I originally used advice-add to override org-html-src-block, but I think I'll try replacing the transcoder.

Adding custom header arguments could be useful for different export-related tweaks (someone wanted to create an argument for highlighting certain lines but hadn't figured it out in that thread). If there's a more elegant way to do this, I'd love to find out!

This is part of my Emacs configuration.
-1:-- Adding a custom header argument to Org Mode source blocks and using that argument during export (Post Sacha Chua)--L0--C0--January 27, 2023 03:11 PM

Sacha Chua: Making highlight-sexp follow modus-themes-toggle

[2023-01-27 Fri] Prot just added a modus-themes-get-color-value function. Yay! Also, it turns out that I need to update the overlay in all the buffers.

I'm experimenting with using the highlight-sexp minor mode to highlight my current s-expression, since I sometimes get confused about what I'm modifying with smartparens. The highlight-sexp background colour is hardcoded in the variable hl-sexp-background-color, and will probably look terrible if you use a light background. I wanted it to adapt when I use modus-themes-toggle. Here's how that works:

(use-package highlight-sexp
  (highlight-sexp :repo "daimrod/highlight-sexp" :fetcher github :version original)
  (emacs-lisp-mode . highlight-sexp-mode)
  (defun my-hl-sexp-update-overlay ()
    (when (overlayp hl-sexp-overlay)
         ,(if (fboundp 'modus-themes-get-color-value)
              (modus-themes-get-color-value 'bg-inactive)
  (defun my-hl-sexp-update-all-overlays ()
    (dolist (buf (buffer-list))
      (with-current-buffer buf
        (when highlight-sexp-mode
  (advice-add 'hl-sexp-create-overlay :after 'my-hl-sexp-update-overlay)
  (advice-add 'modus-themes-toggle :after 'my-hl-sexp-update-all-overlays))

This is what it looks like:

highlight-sexp.gif Figure 1: Animation of highlight-sexp toggling along with modus-themes-toggle
This is part of my Emacs configuration.
-1:-- Making highlight-sexp follow modus-themes-toggle (Post Sacha Chua)--L0--C0--January 26, 2023 03:25 PM

Tory Anderson: Emacs Personal Development Environment: accessing my notes

Intro Due to the nature of my work, I operate in a variety of environments, databases, and languages. Emacs is very good for this. As an example of this, I just found myself needing to verify if an update had worked on the PostGres database of a remote server. First I logged in to the server with better-shell-remote-open1, and then I sudoed and changed to the postgres user, then I quickly fired up my bookmarked myPostgres.
-1:-- Emacs Personal Development Environment: accessing my notes (Post)--L0--C0--January 26, 2023 12:00 AM

Irreal: Warning: You’re About To Discard Undo Information

Just a quickie today. Here’s something I didn’t know:

It’s nice to know that Emacs has our back and warns us when we’re about to lose information or state. I don’t think I ever converted to hexl so I’ve never seen this but it’s nice to know that Emacs, as always, is looking out for us.

-1:-- Warning: You’re About To Discard Undo Information (Post jcs)--L0--C0--January 25, 2023 06:03 PM

Ryan Rix: Moved my Org Site Engine-to-Fediverse cross-posting from feed2toot to Feediverse

Moved my Org Site Engine-to-Fediverse cross-posting from feed2toot to Feediverse

Lately I have been working on integrating my org-mode site engine with my Fediverse presence. Following for technical work and for creative/tea/philosophical crap should give a full view of what I am adding to my sites and keeping my main presence for 1-1 interactions with my fedi-friends.

There are Atom feeds available for headlines on various pages on the sites, and tying those to Fediverse posts should be pretty straightforward, but finding the right tool for the job is always the hard part when I am forging my own way forward.

Yesterday in the back of a rental car I added feed metadata to the org-mode document for my 2023 Hawaii Big Island Trip and wondered how I could get that on to my fedi profile – i realized that it required my laptop to run the Morph deployment to ship a new NixOS system. a bit overkill for what I want to do, especially when the data is already in at least one sqlite database!

So I modified Arcology's Router to add an endpoint which returns all the feeds on the site in a JSON document and then set to work making a script which wrapped feed2toot to orchestrate this but quickly felt that feed2toot is a bit too over-engineered for what I am asking it to do, especially when I set about adding multi-account support to it; the configuration parser is doing a lot more than I want to deal with. Feediverse is a simple single-file script which I was able to easily modify to my needs, with a bit of code-smell in the form of yaml.load-based configuration.

My fork of feediverse reads that feeds.json document and iterates over every feed looking for new posts. This lets me add new feeds to my cross-poster without deploying The Wobserver on my laptop. There is a slow pipeline that prevents me from using this to Shitpost or live-toot things, but I think that's basically okay. The idea is to use it to slowly ship out things when I make new art, or have an end-of-day log, or publish a site update, without having to think too hard about it. Most of the Arroyo Systems stuff (from adding software to my laptop or server to adding pages to my site) is managed by adding metadata keys to my org-mode documents, and this is now no different, though perhaps a bit too "roundabout" for it to be considered good engineering:

  • add ARCOLOGY_FEED page keyword
  • add ARCOLOGY_POST_VISIBILITY page keyword
  • add PUBDATE and ID heading properties to entries on the page that will be published to the feed
  • wait for syncthing to copy files to my server
  • wait for arcology inotify-watcher to reindex the site
  • wait for Feediverse to run on the quarter-hour and post the new feed entries

But my axiom in writing the Arroyo Systems stuff and The Arcology Project as a whole is that Personal Software Can Be Shitty, and this certainly is, but it means I can post new feeds from my Astro Slide so who can say whether it's good or bad.

As much as I am not a fan of Pleroma, it's really nice to just be able to fire HTML right at my server and have it appear halfway decently on the Fediverse with inline links and even images working out just fine. Just have to keep my posts somewhat hsort. This is probably too long for Fedi already, bye.

-1:-- Moved my Org Site Engine-to-Fediverse
cross-posting from feed2toot to Feediverse (Post)--L0--C0--January 25, 2023 07:23 AM

James Dyer: Using ripgrep within Projects

Given my recent forays into the world of grepping in emacs using deadgrep (and hence ripgrep) and my use of find-file-rg which feeds into my current completion system of ivy I think the next step is to try to set up a project and to see if I can gain any advantages in my workflow.

I am not yet going to dive head first into projectile but dangle a tentative pinky into the deep project pools of the built in project system, namely EDE (Emacs Development Environment).

As I have mentioned before I typically would only be working within a single directory hierarchy, hence my previous deadgrep enhancement of using a defined setq home-dir variable, but I think it might be an enhancement if I could leverage deadgreps use of projects; for example, wandering around the deadgrep code I found the following:

(defun deadgrep--project-root ()
  "Guess the project root of the given FILE-PATH."
  (let ((root default-directory)
        (project (project-current)))
    (when project
      (cond ((fboundp 'project-root)
             ;; This function was defined in Emacs 28.
             (setq root (project-root project)))
             ;; Older Emacsen.
             (-when-let (roots (project-roots project))
               (setq root (car roots))))))
    (when root
      (deadgrep--lookup-override root))))

as far as I can tell the default-directory is the current directory of the buffer which can be displayed by M-x pwd but it is the (project-current) that I am more interested in.

A quick investigation and it seems I need to turn on global-ede-mode and then run ede-new in the top level directory of my prospective project. This results in the creation of a Project.ede file containing some project information allowing the setting up of a collection of source files and instructions on how to build them. For the moment I’m not too interested in building anything, but more on how to trigger deadgreps project logic by enabling this directory hierarchy as a project.

As part of creating this project file it seems ede-project-directories has also been added to my init file and an extra menu has appeared on the menu bar called Development

Is this all I need?, well lets evaluate the following in the top level folder:


I seem to get a long list of project information, including the project top level directory. Moving down the directory hierarchy and reevaluating the same expression gives me the same directory!

So I am assuming that in my/deadgrep bespoke wrapper, if I now replace:

(deadgrep search-term home-dir)


(deadgrep search-term)

if I create a project in my former home-dir directory location then my former functionality will have been preserved but with the added flexibility of being able to define other project locations, and also apparently subdirectories! this is actually pretty neat :)

Now I have defined a rudimentary project and seems to work well with deadgrep will it work well with my other favourite ripgrep wrapper, namely find-file-rg? Well there does seem to be some code to accommodate projects too, namely:

(let* ((dir (if current-prefix-arg
              (or (let ((project (project-current)))
                    (when project
                      (if (fboundp 'project-root)
                          (project-root project)
                        (cdr project))))

and yes, it works!, so anywhere I am within the hierarchy of a project I can call up a list of files fed from ripgrep –files into ivy and complete as normal.

As a bonus find-file-rg allows the passing of a universal argument and a current-prefix-arg check enables the navigation to any directory.

In summary then, I can now define a project, I can grep throughout the project from any file / directory within that project and I can pull up a list of files within the project all leveraging the power of ripgrep through deadgrep and find-file-rg!

-1:-- Using ripgrep within Projects (Post James Dyer)--L0--C0--January 24, 2023 09:07 PM

Irreal: Remembering What You Read

Charanjit Singh says he has a hard time remembering what he reads. It’s a common enough problem and Singh has settled on a common solution: he takes notes on what he reads. There are lots of ways of doing that, of course, but this being Irreal, it’s not a hard guess that his note taking workflow revolves around Emacs.

His workflow involves three components:

  1. Emacs
  2. Denote
  3. Spookfox

Spookfox is Singh’s package for talking to Firefox.

He takes two types of notes:

Reading notes
These are notes he takes on what he reads, usually in Firefox, and
Normal note
These are notes that summarize and discuss his reading notes. They link back to the reading notes so that he can see the source.

You can get the details in his post but Singh’s workflow is pretty much like mine except that I use Org mode rather than Denote and talk to my browser (Safari) using some homegrown AppleScript and Elisp glue. It’s a great way of keeping tack of what you read, especially if you read a lot or have a hard time remembering what you read.

Using Org means that it’s easy for me to search my journal—where all these notes go—for a topic or tag. Regardless of the tools you use, taking notes on what you read is a great way of boosting your native memory.

-1:-- Remembering What You Read (Post jcs)--L0--C0--January 24, 2023 05:32 PM

Marcin Borkowski: TODO stats table with parameters

Last time I wrote about my TODO stats table I promised to make it more configurable. And here I am.
-1:-- TODO stats table with parameters (Post)--L0--C0--January 23, 2023 06:12 PM

Sacha Chua: 2023-01-23 Emacs news

[2023-01-23 Mon]: Added meetup

Links from, r/orgmode, r/spacemacs, r/planetemacs, Hacker News,,, YouTube, the Emacs NEWS file, Emacs Calendar, emacs-devel, and lemmy/c/emacs. Thanks to Andrés Ramírez for emacs-devel links. Do you have an Emacs-related link or announcement? Please e-mail me at Thank you!

-1:-- 2023-01-23 Emacs news (Post Sacha Chua)--L0--C0--January 23, 2023 02:41 PM

Jeremy Friesen: Hacking Org-Mode Export for Footnotes as Sidenotes

Yet Another Refinement to My Blogging Engine

, I made a slight modification to how I write my blog posts.

I use Org-Mode (Org-mode 📖) to write my blog posts. I use a a modified Tufte CSS theme, derived from Tufte CSS.

My theme has a concept of the side-note and the margin-note. A few years ago, I moved away from Tufte’s preferred fonts, instead letting folks’s browsers determine the font. The margin-note is for general “scribbling” in the margins. And the side-note is analogues to an inline footnote.

Up until I leveraged Ox-Hugo’s shortcode customizations to handle both. , I wrote the below function to replace Ox-Hugo 📖’s export function for footnotes.

(defun jf/org-hugo-sidenote (footnote-reference _contents info)
  "Transcode a FOOTNOTE-REFERENCE element from Org to Hugo sidenote shortcode.
CONTENTS is nil.  INFO is a plist holding contextual information."
  (let* ((element (car (org-export-get-footnote-definition footnote-reference info)))
         (beg (org-element-property :contents-begin element))
         (end (org-element-property :contents-end element)))
    (format "{{< sidenote >}}%s{{< /sidenote >}}"
            (s-trim (buffer-substring-no-properties beg end)))))

;; Over-write the custom blackfriday export for footnote links.
(advice-add #'org-blackfriday-footnote-reference
            :override #'jf/org-hugo-sidenote
            '((name . "wrapper")))

;; Don't render the section for export
(advice-add #'org-blackfriday-footnote-section
            :override (lambda (&rest rest) ())
            '((name . "wrapper")))

With the above function and advice all Org-mode exports, except to my blog, the footnotes retain their original export behavior. I definitely prefer to utilize as much of the native functionality as possible.

-1:-- Hacking Org-Mode Export for Footnotes as Sidenotes (Post Jeremy Friesen ( 22, 2023 09:04 PM

Irreal: Flexible Grepping With Deadgrep (or Ripgrep)

For those who don’t know, deadgrip is an Emacs interface to ripgrep. It displays the results in an Emacs buffer along with some ancillary information. Check the example at the deadgrip link for some example output. Ripgrep, of course, is a powerful and speedy grep utility; deadgrep provides a nice interface for Emacs users. It’s a bit like counsel-rg but provides a little more context.

James Dyer is a deadgrep user but wanted a little more flexibility. Normally, he’d like to search for whatever’s at point and he almost always wants to search his entire user directory rather than the current directory. When he’s in a Dired buffer, though, he just wants to enter the search string because the Dired buffer mostly has file and directory names.

He has an alternate form that does the same thing but uses the current directory. Between the two functions he has most of his needs covered. The amount of code he needed for this is minimal and easily copied to your init.el. If you’re not a deadgrep user, you can very easily modify it to call ripgrep directly.

I’m happy with counsel-rg but if you’d like a slightly better display and a bit more flexibility, Dyer’s post is worth a look.

-1:-- Flexible Grepping With Deadgrep (or Ripgrep) (Post jcs)--L0--C0--January 22, 2023 05:01 PM

Robert Enzmann: Getting Emacs 29 to Automatically Use Tree-sitter Modes

Recently, /u/casouri posted a guide to getting started with the new built-in tree-sitter capabilities for Emacs 29.
-1:-- Getting Emacs 29 to Automatically Use Tree-sitter Modes (Post)--L0--C0--January 22, 2023 05:00 AM

Irreal: Writing Prose With (Doom) Emacs

Over at (:doom discourse), Mediapathic has a very nice post on using (Doom) Emacs for prose writing. He’s a writer currently working on a book so he can offer firsthand experience. The unifying theme of his post is using the Org outliner to structure and control your writing.

It isn’t so much about first writing an outline—although you can do that if that method works for you—but using the outliner functionality to write your prose in any order that you like and then easily move things around to achieve a coherent whole. Much of his post discusses ways to do that moving of things around and to generate an overview of your work.

Mediapathic makes use of tags to control the export of his text and also to help him track characters and story arcs. He might, for example, include tags for the characters in a scene and also a tag identifying the story arc.

He explains how he captures and keeps notes for his writing. Capturing a note can be especially tricky because you don’t want to lose the context of what you’re working on to capture a note. The Org mode capture functionality is perfect for this: you can pop up a capture template to record your note and get back to what you were doing.

Mediapathic also discusses some ancillary functions such as word counting, writing with a “blank page” buffer, and Palimpsest, a way of removing text but storing it for later retrieval.

If you’re a writer or interested in seeing how Emacs can be used for prose writing, be sure to take a look at Mediapathic’s post. He’s a Doom Emacs user but everything he describes is easily duplicated by vanilla Emacs users.

-1:-- Writing Prose With (Doom) Emacs (Post jcs)--L0--C0--January 21, 2023 06:17 PM

Irreal: Inbox Zero With Mu4e Bookmarks

Years ago, I read and wrote about Ben Maughan’s excellent method of handling his email. It’s an inbox zero method that insists that for each message in your inbox you should

  1. Read and delete it, or
  2. Read and store it in a single folder, or
  3. Read and store it in a single folder and act on it immediately, or
  4. Read and store it in a single folder and create a TODO entry to act on it later

The important thing is that each message is acted on and removed from the inbox immediately. That makes it pretty easy to achieve inbox zero. When I first read Maughan’s post I was still using the Apple Mail app but I was immediately taken with his method and soon moved to mu4e, the mail client he used, and adopted his method.

That method works very well for me but some folks have a lot of email and require a more complicated method. Alain M. Lafon is, apparently, one such person and although he, too, uses mu4e, he uses the mu4e bookmark facility to organize his emails. You can read his post for the details.

For folks like me with a minimal email overhead, Maughan’s method is ideal. I’m inclined to think it would probably work well for those with more robust email requirements as well but I have no first hand evidence. If you think you need something stronger than Maughan’s method, take a look at Lafon’s post for another possibility.

-1:-- Inbox Zero With Mu4e Bookmarks (Post jcs)--L0--C0--January 20, 2023 07:44 PM

Eric MacAdie: 2023-01 Austin Emacs Meetup

There was another meeting two weeks ago of EmacsATX, the Austin Emacs Meetup group. For the this month we had no predetermined topic.

#1 was one of the organizers, and is active in the Austin Clojure group. He shared his screen for most of the presentation.
#2 was our professor in OKC.
#3 was a local Javascript/Clojure dev in Austin. Norman
#4 was a local dev in Austin. I think he was #5 back in June.
#5 was one of the organizers, and formerly worked for the City of Austin.

#2 previously presented to the group back in May on developing Emacs Lisp as well as using it for molecular biology. He referenced the presentation he gave for EmacsConf 2021. He also gave a talk at the recent EmacsConf in December 2022. He spoke a bit about how the conference was handled: people pre-recorded their talks, their talks were shown at the scheduled time, and then there was a chat to ask questions. He said some of the talks were very polished. RMS got to do his live. He can ramble live, or he can ramble on tape. What is the difference?

#2 was surprised at how many young people were involved with the conference.

There is an online book club going over Mastering Emacs that #2 attends. You can find out about it here. There are quite a few regular Emacs meetings listed on Sacha Chua’s Emacs new feed, although some are at a bad time for US people. A couple of people in the ATC meeting wondered how Sacha Chua makes the digest; it would be hard to automate putting posts into categories. Does she do it by hand? She has been doing this since 2015. Maybe she gave a talk about it at an EmacsConf one year; I will have to check. I still have not gotten around to watching the talks from previous years. One of the effects of Org mode is that I am not only better at tracking things on my to-do list, I realized I have a very long to-do list.

There was some discussion about GPT-3 packages, which became discussions about GPT-3. #1 gave it some interview questions for Javascript, and he thought it could pass an interview. #2 pointed out that for all the hand-wringing about GPT-3, you still have to know enough to judge the answer.

#1 said he had asked it to write a few stories, and there seems to be a pattern: a group struggles, they persevere, it works out, happy ending. Like every sports movie.

#1 asked me about Rainer Koenig’s Org-mode course. I still have to finish. I got it two years ago, and I learned enough to get stuff done with it, and haven’t gotten around to finishing. I know there is a stereotype of Emacs users who spend so much time configuring Emacs they never get much done. After I got halfway through Rainer’s course, my config has been pretty stable. Rainer is big on key chords (as are most Emacs people), but now when I learn a new mode I prefer to use M-x $FUNCTION_NAME to get stuff done. I have to pause a lot to get the function names for the new key chords he was introducing. I do not know any of the key chords for Org at all, and I see nothing wrong with that. The only criticism I have of his course is that a lot of the dates in his Org files are from a few years ago, so when you go into agenda views, all the items are from a few years ago. If the view is for a week, you will have to press the “b” key to go back dozens of times. The page on Udemy says the course has not been updated since December 2020. But other than that one point, I recommend the course and I learned a lot from it.

#1 went to Udemy to find Emacs courses, and there were more courses on Udemy than we expected.

#5 said he was getting more into macros. Someone pointed out that using the function keys makes it easier. I guess Emacs did not always use F3 and F4 to start and stop macro definitions. There was some wailing and gnashing of teeth about function keys. #1 put prompt into ChatGPT: write a rant about function keys in style of Leviticus. He read it aloud, and I said he sounded like someone who did not know the answer to a question and was just stalling for time. #5 said that is most of the Bible.

Someone said that ChatGPT was just as vacuous as ELIZA. I said, “Why do you think ChatGPT is as vacuous as ELIZA?” He took the question seriously.

#1 looked at a project on Codeberg, which is the what I am thinking about migrating to. A few people talked about getting off Github. #3 noticed people use “github” the way they use google. People do not search for something, but they google it. Since he was upset, we offered to FedEx him some Kleenex.

Then we were back to ChatGPT. There was some discussion about the legal aspects of it. Github Copilot incorporates a lot of open source material from projects with various licenses. Using it could get a company in some legal trouble. Some of the visual AI programs like DALL-E have some distortions that are speculated to be washed out artist signatures.

#1 asked ChatGPT for some stories to prove they are formulaic. He predicted if you ask it for a story about two people falling in love, they always wind up together. He did that a few times, and that is what happened. I suggested a story about Charles Bukowski finding love on Github (the same way people ask DALL-E to make a picture of an astronaut in the style of French Impressionists), but it would not do it. Although it was more polite than Bukowsky. #1 asked for one where the couple finds love on Github and breaks up. ChatGPT did it, but then they got back together again.

#1 asked ChatGPT for topics for the Emacs meetup. Then he asked for the top Emacs plugins. git-timemachine looked interesting to a few people. Searching for this package led me to the Emacsmirror Github repo and website. Then #1 prompted it for presentations about Magit and beacon mode. It gave a pretty good start. No longer will presenters be hurting for presentations.

Then #1 asked ChatGPT for a story about me getting a job using Emacs. I do not remember if he was making stories for everyone, or if this was inspired by something I said; I have stated a few times I would like a job where I could be in Emacs all day. For some reason it had me learning Python (which I know nothing about) and getting a job with that, and then I fall in love with a woman named Sarah in Antarctica. I think #1 just put in my first name and not my full name, but it seemed odd to everyone that ChatGPT inserted Python and going to Antarctica. I did not think Python is the most popular language amongst Emacs programmers; I would have guessed Clojure has more Emacs users.

At the end of the meeting, #5 mentioned he worked for a couple of summers for BP up in Prudhoe Bay. He said some of them were certified engineers who had gotten burnt out with engineering. #1 said he had a college roommate who did that, made a lot of money, and used that high number to negotiate a higher salary. Then #1 talked about neighbors shooting, and speculated they must have spent a lot of money on ammo.

Kleenex, Leviticus, art, literature, romance, oil, and guns: Emacs people can talk about more than just Emacs.

Or, as some people call it, “E-Max”.

I give people numbers since I do not know if they want their names in this write-up. Think of it as the stoner’s version of the Chatham House Rule. I figured that numbers are a little clearer than “someone said this, and someone else said that, and a third person said something else”. Plus it gives participants some deniability. People’s numbers are based on the order they are listed on the call screen, and the same person may be referred to by different numbers in different months.

I am not the official spokesperson for the group. I just got into the habit of summarizing the meetings every month, and adding my own opinions about things. The participants may remember things differently, and may disagree with opinions expressed in this post. Nothing should be construed as views held by anyone’s employers past, present or future. That said, if you like something in this post, I will take credit; if you don’t, blame somebody else.

Image from Codex aureus pultoviensis, an 11th-century manuscript housed at the National Museum in Krakow; image from National Museum in Krakow, assumed allowed under public domain.


-1:-- 2023-01 Austin Emacs Meetup (Post Eric MacAdie)--L0--C0--January 20, 2023 04:34 AM

James Dyer: More flexible grepping with deadgrep

I seem to be grepping a lot recently and I think the way I use deadgrep can be improved a little.

Currently deadgrep defaults to a recursive ripgrep from the default-directory which is generally the current directory of the buffer, but I find that by default I tend to mostly want to grep from a top level directory (yes I know, almost like a project!).

I would like to have a typical Find All References type of functionality from my grepping and not to rely on xref as I will not necessarily ever know if any xref functionality is supported for any file that I am working on and for the moment I am not using any connection to an LSP server. I would like a simple generic process that can be used across all files and I think deadgdrep can help me out with this.

I would like to bind to S-f12 to grep from the “project” top level with the search set to whatever string is under my cursor; this should enable a quick workflow involving jumping around files within the top level directory structure bouncing back and forth between the deadgrep buffer.

I have chosen the binding of S-f12 for consistency across IDEs as I am often required to use VSCode and Visual Studio for work.

In addition I would like to replace my usual local directory grepping where I use grep with deadgrep for a more unified approach.

So I created the following two functions:

(defun my/deadgrep ()
  (if (equal major-mode 'dired-mode)
      (setq search-term
            (read-from-minibuffer "Search : "))
    (setq search-term
          (read-from-minibuffer "Search : " (thing-at-point 'symbol)))
  (deadgrep search-term home-dir)

(defun my/grep ()
  (if (equal major-mode 'dired-mode)
      (setq search-term
            (read-from-minibuffer "Search : "))
    (setq search-term
          (read-from-minibuffer "Search : " (thing-at-point 'symbol)))
  (deadgrep search-term)

home-dir as you might guess is where my top level directory resides.

I came to realise that when I am in a dired buffer I don’t actually ever want to grep with the string under the cursor (which of course would most likely be a file or directory) but only when I am in a file.

I toyed around with the idea of having a single (interactive “p”) function so it would accept a prefix command and then perform the following kind of logic:

((equal current-prefix-arg nil)   ; no C-u
   (do top level grep))
((equal current-prefix-arg '(4))  ; C-u
   (do local grep))
((equal current-prefix-arg 1)     ; C-u 1
   (do some other grepping))

however this had the unintended consequence of pushing through the prefix command to deadgrep and therefore it would not start immediately but wait for user interaction. I couldn’t see a way round this so had to split my grepping into both a S-f12 and M-f12 for each function call; not much of a big deal.

As I am just running a single deadgrep instance using:

(setq deadgrep-max-buffers 1)

I also needed to add the following as I will always want to kill the current process if I am starting a new one.

(setq kill-buffer-query-functions nil)
-1:-- More flexible grepping with deadgrep (Post James Dyer)--L0--C0--January 20, 2023 12:00 AM

Irreal: Find Org Files

Howard Abrams has a nifty idea. Open an Org file by its file name, title, or the tags it contains. If, like me, the majority of the files you deal with are Org files this make a lot of sense. When you call org-find-file instead of find-file, you get a list that contains the file name, title, and tags. Since it’s mediated by completing-read you can narrow down the choices in the usual way. Once you make a choice, find-file is called with the file name from the chosen entry.

The design of the command is interesting. Rather than build something like a TAGS file for use by the command, everything happens dynamically in real time. Abrams leverages the speed of the modern grep ripgrep to build the completing-read selection list on the fly. Of course, it’s a little more complicated than that. First, ripgrep is called to gather the titles and then it is called again to gather the tags in each of the files in the target directory.

Most of Abram’s code is concerned with putting together the information and formatting each entry into a nice looking line for the user to select from. The use of ripgrep makes most of it simple. The main difficulty is gathering the tags because they can appear in an Org file in a couple of ways—see Abrams’ post for the details.

If you’re interested in playing around with his code, it’s available in his GitHub repository. It’s a nice piece of engineering and well worth studying for its ideas.

-1:-- Find Org Files (Post jcs)--L0--C0--January 19, 2023 07:24 PM

Nicolas Martyanoff: Taking code screenshots in Emacs

My friend Rémi found an Emacs package developed by Tecosaur to take screenshots of your code. I usually do it with scrot, but it always requires spending some time cropping the result with Gimp. A bit annoying.


Let us give it a try. The screenshot package is not available on MELPA due to cosmetic issues, so we have two options.

The first one would be to clone the repository, add its location to load-path, and use require to load the module. But doing so means having to explicitely install its dependencies, transient and posframe.

The second option is to rely on use-package and straight.el. With straight.el, you can load packages from various sources, including Git repositories.

(use-package screenshot
  :straight (:type git :host github :repo "tecosaur/screenshot"))

Evaluate the form with C-x C-e, and straight.el will clone the repository and load the screenshot package.


Simply run M-x screenshot to take a screenshot. If you are currently selecting a region, it will screenshot this part only. If not, it will use the entire buffer.

Since Screenshot uses the transient library, it has the same well designed interface system as Magit:

Screenshot interface

I really like the ability to adjust the font size just for the screenshot. By default, the save action will save the image in the same directory as the buffer which was selected when screenshot was executed.


Screenshot lets you change the default value of each setting. This is not obvious when reading the code, because the variables used are defined programmatically through two layers of macros: first screenshot--define-infix then transient-define-infix.

It is too bad these values are not defined as proper settings using defcustom, but we can still set them.

This is a list of available settings:

Variable Description
screenshot-line-numbers-p Whether to display line numbers or not.
screenshot-relative-line-numbers-p Whether to use relative line numbers or not.
screenshot-text-only-p Whether to show code as simple text or not.
screenshot-truncate-lines-p Whether to truncate long lines instead of wrapping them.
screenshot-font-family The name of the font to use.
screenshot-font-size The size of the font to use.
screenshot-border-width The width of the border.
screenshot-radius The corner radius of the border.
screenshot-min-width The minimal width of the screenshot as a number of characters.
screenshot-max-width The maximal width of the screenshot as a number of characters.
screenshot-shadow-radius The corner radius of the shadow.
screenshot-shadow-intensity The intensity of the shadow.
screenshot-shadow-color The hexadecimal code of the shadow.
screenshot-shadow-offset-horizontal The horizontal offset of the shadow in pixels.
screenshot-shadow-offset-vertical The vertical offset of the shadow in pixels.

Another nice feature is the presence of a hook executed in the temporary buffer used for the screenshot, allowing to execute code affecting this buffer just before the actual screenshot is being taken.

In my case, I use it to disable the fill column indicator and to remove the additional line spacing added by default by Screenshot.

This is my configuration:

(defun g-screenshot-on-buffer-creation ()
  (setq display-fill-column-indicator-column nil)
  (setq line-spacing nil))

(use-package screenshot
  :straight (:type git :host github :repo "tecosaur/screenshot")

  (setq screenshot-line-numbers-p nil)

  (setq screenshot-min-width 80)
  (setq screenshot-max-width 80)
  (setq screenshot-truncate-lines-p nil)

  (setq screenshot-text-only-p nil)

  (setq screenshot-font-family "Berkeley Mono")
  (setq screenshot-font-size 10)

  (setq screenshot-border-width 16)
  (setq screenshot-radius 0)

  (setq screenshot-shadow-radius 0)
  (setq screenshot-shadow-offset-horizontal 0)
  (setq screenshot-shadow-offset-vertical 0)

  ((screenshot-buffer-creation-hook . g-screenshot-on-buffer-creation)))

All in all a very useful package!

-1:-- Taking code screenshots in Emacs (Post Nicolas Martyanoff ( 18, 2023 06:00 PM

200ok: Schwä relaunches with alephDAM, becoming a leading news hub in the region

Schwä relaunched yesterday with alephDAM - it is a major stepping stone for the German news website. alephDAM allows Schwä to automate the delivery of news agency content, integrating news from various content providers (their print system cci, AFP, DPA, KNA, SID) into their site. This allows Schwä to automatically deliver a vast amount of articles and images every day, based on rules that they configure. This means that Schwä can manage their assets with a single point of integration and configuration, instead of dealing with many vendors. In turn, this gives room to their 160 journalists to work on quality content. See more details on their relaunch announcement.

alephDAM is part of Forward Publishing. As a technology partner and one-stop-shop, we helped Schwä's transformation into a future-proof digital newsroom. Forward Publishing's portfolio is chosen based on the best-of-breed principle, allowing Schwä to have better content with less IT.

Livingdocs is also part of Forward Publishing. With Livingdocs, Schwä's journalists and designers are able to produce digital content efficiently and aesthetically.

If you want to learn more about how to automate the delivery of news agency content, check out alephDAM. If you're interested, schedule a demo any time!

-1:-- Schwä relaunches with alephDAM, becoming a leading news hub in the region (Post Alain M. Lafon, Phil Hofmann ( 18, 2023 12:00 AM

Irreal: Why Use Emacs, Vim, and Other Nerdy Software

Derek Taylor over at DistroTube has a video that asks the question, “Why should you use Emacs, Vim, and other nerdy software?” That’s not a hard question for Irreal readers but Taylor has an unusual perspective. Even though he constantly makes videos on the technical aspects of computing, he, himself, is not a developer and does not work in the field.

His education was in music but he soon realized that in order to optimize his
use of computers he needed to be able to adapt them to his own workflow. That, he says, entails two conclusions:

  1. You should be using (very) configurable software
  2. You should be using free (in the FSF sense) software that allows you to adapt it to your needs.

The part of the video that I found most interesting was the fact that Taylor has learned a little bit of several languages—including Haskel—just so he could configure his software. He’s written several Bash scripts to automate chores and data conversions but still insists he’s not a programmer—he’s just a guy who learned what he needed to so he could adapt his computing environment to his needs.

It’s interesting that non-engineers are more than able to master enough programming to customize their software. We sometimes think that making full use of Emacs (and the others) are beyond the capabilities of normies but Taylor’s video shows otherwise.

-1:-- Why Use Emacs, Vim, and Other Nerdy Software (Post jcs)--L0--C0--January 17, 2023 06:37 PM

Troy Hinckley: Design of Emacs in Rust

This is the third post in my series about writing an Emacs core in Rust. The first post laid out my initial observations and ideas about the language runtime. The second post focused on building a safe garbage collector in Rust using the type system. I initially stated that I wanted to reach the point where I could bootstrap bytecomp.el (the elisp byte compiler). That goal is reached1, so I am providing an update on my latest learnings.
-1:-- Design of Emacs in Rust (Post)--L0--C0--January 17, 2023 12:00 AM

Irreal: Org-roam Workflow

Dominik Honnef has a nifty post that describes his Org-roam based workflow for taking notes and writing articles. It’s a very thorough and complex system that captures notes, articles that he reads on the web, articles for his blog, and miscellaneous notes as they occur to him. The system automatically maintains a bibliography and can massage a note intended for his blog to make it compatible with Hugo, which he uses to publish his blog.

His use of Org-roam is a little idiosyncratic. He doesn’t follow Luhmann’s Zettelkasten method as described, for example, by Ahrens’ How to take smart notes: one simple technique to boost writing, learning and thinking. Rather, he uses it as a sort of Wiki to save and index all his notes and the data they pertain to. Org-roam is perfect for that because it maintains forward and backward links between notes and makes it easy to maintain the relationship between notes and index them for easy retrieval.

His system is tightly integrated with Zotero to manage the papers he reads and takes notes on. He lets Zotero handle the bibliography and capturing of papers and websites. Although he provides much of his configuration in his long and detailed post, he doesn’t provide a link to his whole configuration so it may be hard to adapt his approach.

It’s a good post and well worth spending the time to read.

-1:-- Org-roam Workflow (Post jcs)--L0--C0--January 16, 2023 05:08 PM

Sacha Chua: 2023-01-16 Emacs news

Links from, r/orgmode, r/spacemacs, r/planetemacs, Hacker News,,, YouTube, the Emacs NEWS file, Emacs Calendar, emacs-devel, and lemmy/c/emacs. Thanks to Andrés Ramírez for emacs-devel links. Do you have an Emacs-related link or announcement? Please e-mail me at Thank you!

-1:-- 2023-01-16 Emacs news (Post Sacha Chua)--L0--C0--January 16, 2023 02:23 PM

Protesilaos Stavrou: Emacs: ‘substitute’ package demo

Raw link:

In this video I showcase my new package for Emacs. It streamlines certain text substitution routines I used to do with built-in commands. Quote from the project’s README:

Substitute is a set of commands that perform text replacement (i) throughout the buffer, (ii) limited to the current definition (per narrow-to-defun), (iii) from point to the end of the buffer, and (iv) from point to the beginning of the buffer.

These substitutions are meant to be as quick as possible and, as such, differ from the standard query-replace (which I still use). The provided commands prompt for substitute text and perform the substitution outright.

I will add the package to GNU ELPA. Expect it to be available shortly after this publication goes live.

-1:-- Emacs: ‘substitute’ package demo (Post)--L0--C0--January 16, 2023 12:00 AM

Irreal: A Nice Example of Using Bug-hunter from Sacha

Sacha Chua has a great post on using bug-hunter. She had a problem in her large Emacs configuration file and used bug-hunter to find it. If you’re like me—and apparently Sacha—you won’t use it very often but when you’re trying to track down an issue in your configuration, it’s just what you need. Every time I’ve used it, it found my problem quickly and easily.

For those not familiar with bug-hunter, it works by doing a binary search on your configuration: it loads the first half of your configuration and if the bug appears, it starts over loading the first quarter. If the bug is not present, it starts over loading the second half of the configuration. As with a traditional binary search, it recursively narrows down the line causing the problem until there’s only one left.

Sacha’s use case was a little unusual because the error happened asynchronously not at load time so she patched bug-hunter to capture when the offending application was being set up. Take a look at her post to see the details.

It’s a testament to the flexibility of bug-hunter that Sacha was easily able to coerce it into working for her situation. The usual use of bug-hunter is tracking down why Emacs won’t load but as Sacha’s post shows, it can be helpful in more general situations. The next time you need to figure out what part of your configuration is causing you a problem, you should give bug-hunter a try. It’s a great utility.

-1:-- A Nice Example of Using Bug-hunter from Sacha (Post jcs)--L0--C0--January 15, 2023 05:43 PM

Magnus: Composing instances using deriving via

Today I watched the very good, and short, video from Tweag on how to Avoid boilerplate instances with -XDerivingVia. It made me realise that I've read about this before, but then the topic was on reducing boilerplate with MTL-style code.

Given that I'd forgotten about it I'm writing this mostly as a note to myself.

The example from the Tweag video, slightly changed

The code for making film ratings into a Monoid, when translated to the UK, would look something like this:

{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}

module DeriveMonoid where

newtype Supremum a = MkSup a
    deriving stock (Bounded, Eq, Ord)
    deriving newtype (Show)

instance Ord a => Semigroup (Supremum a) where
    (<>) = max

instance (Bounded a, Ord a) => Monoid (Supremum a) where
    mempty = minBound

data FilmClassification
    = Universal
    | ParentalGuidance
    | Suitable12
    | Suitable15
    | Adults
    | Restricted18
    deriving stock (Bounded, Eq, Ord)
    deriving (Monoid, Semigroup) via (Supremum FilmClassification)

Composing by deriving

First let's write up a silly class for writing to stdout, a single operation will do.

class Monad m => StdoutWriter m where
    writeStdoutLn :: String -> m ()

Then we'll need a type to attach the implementation to.

newtype SimpleStdoutWriter m a = SimpleStdoutWriter (m a)
    deriving (Functor, Applicative, Monad, MonadIO)

and of course an implementation

instance MonadIO m => StdoutWriter (SimpleStdoutWriter m) where
    writeStdoutLn = liftIO . putStrLn

Now let's create an app environment based on ReaderT and use deriving via to give it an implementation of StdoutWriter via SimpleStdoutWriter.

newtype AppEnv a = AppEnv {unAppEnv :: ReaderT Int IO a}
        ( Functor
        , Applicative
        , Monad
        , MonadIO
        , MonadReader Int
    deriving (StdoutWriter) via (SimpleStdoutWriter AppEnv)

Then a quick test to show that it actually works.

λ> runReaderT (unAppEnv $ writeStdoutLn "hello, world!") 0
hello, world!
-1:-- Composing instances using <tt>deriving via</tt> (Post)--L0--C0--January 15, 2023 05:34 PM

Johnny Five is Alive: Org mode Export to PDF

Org mode Export to PDF   ATTACH

I use org-mode to document many things, but many are for my future self. Recently, a need to share my document(s) with others. The org-mode file contained a few headings with notes, some images, and a table of information. While experimenting with exporting the org-mode file to PDF I found the following helpful.

Depending on the size of the org-mode file, you may want the table of contents (toc). Add #+OPTIONS: toc:t to the top of your org-mode file. But if you do not want the table of contents add this #+OPTIONS: toc:nil to the top of your org-mode file. To learn more about this org-mode table of contents go here 1.

Learning about exporting images from an org-mode file was interesting. If you use org-insert-link 2 to link to an image file and add a description. The resulting exported file will contain a link to an image instead of displaying the image. There are two options to get an image to display in org-mode exports. The first is to use org-insert-link and insert a link to an image file without a description. The second option is to insert a link like [[./an/image-file.png/]].

Note: You will need pdf2latex program to be able to export a PDF from an org file.

Here is an example org file and here is an example pdf file exported from the example org file.

-1:-- Org mode Export to PDF (Post)--L0--C0--January 15, 2023 07:53 AM

Yuan Fu (casouri): Tree-sitter Starter Guide

This guide gives you a starting point on writing a tree-sitter major mode. Remember, don’t panic and check your manuals!

Build Emacs with tree-sitter

You can either install tree-sitter by your package manager, or from

git clone
cd tree-sitter
make install

To build and run Emacs 29:

git clone -b emacs-29
cd emacs

Require the tree-sitter package with (require 'treesit). Note that tree-sitter always appear as treesit in symbols. Now check if Emacs is successfully built with tree-sitter library by evaluating (treesit-available-p).

Tree-sitter stuff in Emacs can be categorized into two parts: the tree-sitter API itself, and integration with fontification, indentation, Imenu, etc. You can use shortdoc to glance over all the tree-sitter API functions by typing M-x shortdoc RET treesit RET. The integration are described in the rest of the post.

Install language definitions

Tree-sitter by itself doesn’t know how to parse any particular language. It needs the language grammar (a dynamic library) for a language to be able to parse it.

First, find the repository for the language grammar, eg, tree-sitter-python. Take note of the Git clone URL of it, eg, Now check where is the parser.c file in that repository, usually it’s in src.

Make sure you have Git, C and C++ compiler, and run the treesit-install-grammar command, it will prompt for the URL and the directory of parser.c, leave other prompts at default unless you know what you are doing.

You can also manually clone the repository and compile it, and put the dynamic library at a standard library location. Emacs will be able to find it. If you wish to put it somewhere else, set treesit-extra-load-path so Emacs can find it.

Tree-sitter major modes

Tree-sitter modes should be separate major modes, usually named xxx-ts-mode. I know I said tree-sitter always appear as treesit in symbols, this is the only exception.

If the tree-sitter mode and the “native” mode could share some setup code, you can create a “base mode”, which only contains the common setup. For example, there is python-base-mode (shared), and both python-mode (native), and python-ts-mode (tree-sitter) derives from it.

In the tree-sitter mode, check if we can use tree-sitter with treesit-ready-p, it will emit a warning if tree-sitter is not ready (tree-sitter not built with Emacs, can’t find the language grammar, buffer too large, etc).


Tree-sitter works like this: It parses the buffer and produces a parse tree. You provide a query made of patterns and capture names, tree-sitter finds the nodes that match these patterns, tag the corresponding capture names onto the nodes and return them to you. The query function returns a list of (capture-name . node).

For fontification, we simply use face names as capture names. And the captured node will be fontified in their capture name (the face).

The capture name could also be a function, in which case (NODE OVERRIDE START END) is passed to the function for fontification. START and END are the start and end of the region to be fontified. The function should only fontify within that region. The function should also allow more optional arguments with &rest _, for future extensibility. For OVERRIDE check out the docstring of treesit-font-lock-rules.

Query syntax

There are two types of nodes: “named nodes”, like (identifier), (function_definition), and “anonymous nodes”, like "return", "def", "(", ";". Parent-child relationship is expressed as

(parent (child) (child) (child (grand_child)))

Eg, an argument list (1, "3", 1) would be:

(argument_list "(" (number) (string) (number) ")")

Children could have field names:

(function_definition name: (identifier) type: (identifier))

To match any one in the list:

["true" "false" "none"]

Capture names can come after any node in the pattern:

(parent (child) @child) @parent

The query above captures both the parent and the child.

The query below captures all the keywords with capture name

["return" "continue" "break"] @keyword

These are the common syntax, check out the full syntax in the manual: Pattern Matching.

Query references

But how do one come up with the queries? Take python for an example, open any python source file, type M-x treesit-explore-mode RET. You should see the parse tree in a separate window, automatically updated as you select text or edit the buffer. Besides this, you can consult the grammar of the language definition. For example, Python’s grammar file is at

Neovim also has a bunch of queries to reference from.

The manual explains how to read grammar files in the bottom of Language Grammar.

Debugging queries

If your query has problems, use treesit-query-validate to debug the query. It will pop a buffer containing the query (in text format) and mark the offending part in red. Set treesit--font-lock-verbose to t if you want the font-lock function to report what it’s doing.

Set up font-lock

To enable tree-sitter font-lock, set treesit-font-lock-settings and treesit-font-lock-feature-list buffer-locally and call treesit-major-mode-setup. For example, see python--treesit-settings in python.el. Below is a snippet of it.

Note that like the current font-lock system, if the to-be-fontified region already has a face (ie, an earlier match fontified part/all of the region), the new face is discarded rather than applied. If you want later matches always override earlier matches, use the :override keyword.

Each rule should have a :feature, like function-name, string-interpolation, builtin, etc. This way users can enable/disable each feature individually.

Read the manual section Parser-based Font-Lock for more detail.

Example from python.el:

(defvar python--treesit-settings
   :feature 'comment
   :language 'python
   '((comment) @font-lock-comment-face)

   :feature 'string
   :language 'python
   '((string) @python--treesit-fontify-string)

   :feature 'string-interpolation
   :language 'python
   :override t
   '((interpolation (identifier) @font-lock-variable-name-face))


In python-ts-mode:

(treesit-parser-create 'python)
(setq-local treesit-font-lock-settings python--treesit-settings)
(setq-local treesit-font-lock-feature-list
                '(( comment definition)
                  ( keyword string type)
                  ( assignment builtin constant decorator
                    escape-sequence number property string-interpolation )
                  ( bracket delimiter function operator variable)))

Concretely, something like this:

(define-derived-mode python-ts-mode python-base-mode "Python"
  "Major mode for editing Python files, using tree-sitter library.

  :syntax-table python-mode-syntax-table
  (when (treesit-ready-p 'python)
    (treesit-parser-create 'python)
    (setq-local treesit-font-lock-feature-list
                '(( comment definition)
                  ( keyword string type)
                  ( assignment builtin constant decorator
                    escape-sequence number property string-interpolation )
                  ( bracket delimiter function operator variable)))
    (setq-local treesit-font-lock-settings python--treesit-settings)
    (setq-local imenu-create-index-function
    (setq-local treesit-defun-type-regexp (rx (or "function" "class")
    (setq-local treesit-defun-name-function

    (when python-indent-guess-indent-offset


Indentation works like this: We have a bunch of rules that look like


When the indenting a line, let NODE be the node at the beginning of the current line, we pass this node to the MATCHER of each rule, one of them will match the node (eg, “this node is a closing bracket!”). Then we pass the node to the ANCHOR, which returns a point (eg, the beginning of NODE’s parent). We find the column number of that point (eg, 4), add OFFSET to it (eg, 0), and that is the column we want to indent the current line to (4 + 0 = 4).

Matchers and anchors are functions that takes (NODE PARENT BOL &rest _). Matches return nil/non-nil for no match/match, and anchors return the anchor point. An Offset is usually a number or a variable, but it can also be a function. Below are some convenient builtin matchers and anchors.

For MATHCER we have

(parent-is TYPE) => matches if PARENT’s type matches TYPE as regexp
(node-is TYPE) => matches NODE’s type
(query QUERY) => matches if querying PARENT with QUERY
                 captures NODE.


=> checks everything. If an argument is nil, don’t match that. Eg,
(match nil TYPE) is the same as (parent-is TYPE)

For ANCHOR we have

first-sibling => start of the first sibling
parent => start of parent
parent-bol => BOL of the line parent is on.
prev-sibling => start of previous sibling
no-indent => current position (don’t indent)
prev-line => start of previous line

There is also a manual section for indent: Parser-based Indentation.

When writing indent rules, you can use treesit-check-indent to
check if your indentation is correct. To debug what went wrong, set
treesit--indent-verbose to t. Then when you indent, Emacs
tells you which rule is applied in the echo area.

Here is an example:

(defvar typescript-mode-indent-rules
  (let ((offset 'typescript-indent-offset))
       ;; This rule matches if node at point is ")", ANCHOR is the
       ;; parent node’s BOL, and offset is 0.
       ((node-is ")") parent-bol 0)
       ((node-is "]") parent-bol 0)
       ((node-is ">") parent-bol 0)
       ((node-is "\\.") parent-bol ,offset)
       ((parent-is "ternary_expression") parent-bol ,offset)
       ((parent-is "named_imports") parent-bol ,offset)
       ((parent-is "statement_block") parent-bol ,offset)
       ((parent-is "type_arguments") parent-bol ,offset)
       ((parent-is "variable_declarator") parent-bol ,offset)
       ((parent-is "arguments") parent-bol ,offset)
       ((parent-is "array") parent-bol ,offset)
       ((parent-is "formal_parameters") parent-bol ,offset)
       ((parent-is "template_substitution") parent-bol ,offset)
       ((parent-is "object_pattern") parent-bol ,offset)
       ((parent-is "object") parent-bol ,offset)
       ((parent-is "object_type") parent-bol ,offset)
       ((parent-is "enum_body") parent-bol ,offset)
       ((parent-is "arrow_function") parent-bol ,offset)
       ((parent-is "parenthesized_expression") parent-bol ,offset)

Then you set treesit-simple-indent-rules to your rules, and call treesit-major-mode-setup.


Set treesit-simple-imenu-settings and call treesit-major-mode-setup.

Set treesit-defun-type-regexp, treesit-defun-name-function, and call treesit-major-mode-setup.

C-like languages

c-ts-mode.el has some goodies for handling indenting and filling block comments.

These two rules should take care of indenting block comments.

((and (parent-is "comment") c-ts-mode--looking-at-star)
            c-ts-mode--comment-start-after-first-star -1)
((parent-is "comment") prev-adaptive-prefix 0)

Set c-ts-mode-indent-block-type-regexp and these two rules should take care of indenting statements in “{}” blocks and closing bracket “}”.

((node-is "

") point-min c-ts-mode--close-bracket-offset)
((parent-is "compound_statement")
point-min c-ts-mode--statement-offset)

c-ts-mode-comment-setup will set up comment and filling for you.

Multi-language modes

Refer to the manual: Multiple Languages.

Common Tasks

M-x shortdoc RET treesit RET will give you a complete list.

How to...

Get the buffer text corresponding to a node?

(treesit-node-text node)

Don’t confuse this with treesit-node-string.

Scan the whole tree for stuff?


Find/move to to next node that...?

(treesit-search-forward node ...)
(treesit-search-forward-goto node ...)

Get the root node?


Get the node at point?

(treesit-node-at (point))
-1:-- Tree-sitter Starter Guide (Post)--L0--C0--January 15, 2023 05:00 AM

Yuan Fu (casouri): Tree-sitter in Emacs 29 and Beyond

Emacs’ release branch is now on complete feature freeze, meaning absolutely only bug fixes can happen on it. Now is a good time to talk about the state of tree-sitter in Emacs: what do you get in Emacs 29, what you don’t, and what would happen going forward.

What’s in Emacs 29

From a pure user’s perspective, Emacs 29 just adds some new built-in major modes which look more-or-less identical to the old ones. There aren’t any flashy cool features either. That sounds disappointing, but there are a lot of new stuff under the hood, a solid base upon which exciting things can emerge.

If Emacs 29 is built with the tree-sitter library, you have access to most of the functions in its C API, including creating parsers, parsing text, retrieving nodes from the parse tree, finding the parent/child/sibling node, pattern matching nodes with a DSL, etc. You also get a bunch of convenient functions built upon the primitive functions, like searching for a particular node in the parse tree, cherry picking nodes and building a sparse tree out of the parse tree, getting the node at point, etc. You can type M-x shortdoc RET treesit RET to view a list of tree-sitter functions. And because it’s Emacs, there is comprehensive manual coverage for everything you need to know. It’s in “Section 37, Parsing Program Source” of Emacs Lisp Reference Manual.

Emacs 29 has built-in tree-sitter major modes for C, C++, C#, Java, Rust, Go, Python, Javascript, Typescript, JSON, YAML, TOML, CSS, Bash, Dockerfile, CMake file. We tried to extend existing modes with tree-sitter at first but it didn’t work out too well, so now tree-sitter lives in separate major modes. The tree-sitter modes are usually called xxx-ts-mode, like c-ts-mode and python-ts-mode. The simplest way to enable them is to use major-mode-remap-alist. For example,

(add-to-list 'major-mode-remap-alist
             '(c-mode . c-ts-mode))

The built-in tree-sitter major modes have support for font-lock (syntax highlight), indentation, Imenu, which-func, and defun navigation.

For major mode developers, Emacs 29 includes integration for these features for tree-sitter, so major modes only need to supply language-specific information, and Emacs takes care of plugging tree-sitter into font-lock, indent, Imenu, etc.


In tree-sitter major modes, fontification is categorized into “features”, like “builtin”, “function”, “variable”, “operator”, etc. You can choose what “features” to enable for a mode. If you are feeling adventurous, it is also possible to add your own fontification rules.

To add/remove features for a major mode, use treesit-font-lock-recompute-features in its mode hook. For example,

(defun c-ts-mode-setup ()
   '(function variable) '(definition)))

(add-hook 'c-ts-mode-hook #'c-ts-mode-setup)

Features are grouped into decoration levels, right now there are 4 levels and the default level is 3. If you want to program in skittles, set treesit-font-lock-level to 4 ;-)

Language grammars

Tree-sitter major modes need corresponding langauge grammar to work. These grammars come in the form of dynamic libraries. Ideally the package manager will build them when building Emacs, like with any other dynamic libraries. But they can’t cover every language grammar out there, so you probably need to build them yourself from time to time. Emacs has a command for it: treesit-install-language-grammar. It asks you for the Git repository and other stuff and builds the dynamic library. Third-party major modes can instruct their users to add the recipe for building a language grammar like this:

 '(python ""))

Then typing M-x treesit-install-language-grammar RET python builds the language grammar without user-input.

Other stuff

Things like indentation, Imenu, navigation, etc, should just work.

There is no code-folding, selection expansion, and structural navigation (except for defun) in Emacs 29. Folding and expansion should be trivial to implement in existing third-party packages. Structural navigation needs careful design and nontrivial changes to existing commands (ie, more work). So not in 29, unfortunately.

Future plans

The tree-sitter integration is far from complete. As mentioned earlier, structural navigation is still in the works. Right now Emacs allows you to define a “thing” by a regexp that matches node types, plus optionally a filter function that filters out nodes that matches the regexp but isn’t really the “thing”. Given the definition of a “thing”, Emacs has functions for finding the “things” around point (treesit--things-around), finding the “thing” at point (treesit--thing-at-point), and navigating around “things” (treesit--navigate-thing). Besides moving around, these functions should be also useful for other things like folding blocks. Beware that, as the double dash suggests, these functions are experimental and could change.

I also have an idea for “abstract list elements”. Basically an abstract list element is anything repeatable in a grammar: defun, statement, arguments in argument list, etc. These things appear at every level of the grammar and seems like a very good unit for navigation.

Context extraction

There is also potential for language-agnostic “context extraction” (for the lack of a better term) with tree-sitter. Right now we can get the name and span of the defun at point, but it doesn’t have to stop there, we can also get the parameter list, the type of the return value, the class/trait of the function, etc. Because it’s language agnostic, any tool using this feature will work on many languages all at once.

In fact, you can already extract useful things, to some degree, with the fontification queries written by major modes: using the query intended for the variable query, I can get all the variable nodes in a given range.

There are some unanswered questions though: (1) What would be the best function interface and data structure for such a feature? Should it use a plist like (:name ... :params ...), or a cl-struct? (2) If a language is different enough from the “common pattern”, how useful does this feature remains? For example, there isn’t a clear parameter list in Haskell, and there could be several defun bodies that defines the same function. (3) Is this feature genuinely useful, or is it just something that looks cool? Only time and experiments can tell, I’m looking forward to see what people will do with tree-sitter in the wild :-)

Major mode fallback

Right now there is no automatic falling back from tree-sitter major modes to “native” major modes when the tree-sitter library or language grammar is missing. Doing it right requires some change to the auto-mode facility. Hopefully we’ll see a good solution for it in Emacs 30. Right now, if you need automatic fallback, try something like this:

(define-derived-mode python-auto-mode prog-mode "Python Auto"
  "Automatically decide which Python mode to use."
  (if (treesit-ready-p 'python t)

Other stuff

Existing tree-sitter major modes are pretty basic and doesn’t have many bells and whistles, and I’m sure there are rough corners here and there. Of course, these things will improve over time.

Tree-sitter is very different and very new, and touches many parts of Emacs, so no one has experience with it and no one knows exactly how should it look like. Emacs 29 will give us valuable experience and feedback, and we can make it better and better in the future.

If you are interested, get involved! Read Contributing to Emacs for some tips in getting involved with the Emacs development. Read Tree-sitter Starter Guide if you want to write a major mode using tree-sitter. And of course, docstrings and the manual is always your friend. If you have questions, you can ask on Reddit, or comment in this post’s public inbox (see the footer).

-1:-- Tree-sitter in Emacs 29 and Beyond (Post)--L0--C0--January 15, 2023 05:00 AM

James Dyer: Using org-copy-visible in dired

Just a quick one.

Often it seems I need a copy of a list of files / directories in plain text without any gubbins such as a path, permissions, date and all those shenanigans, basically basenaming; for example:

So how can I achieve this in emacs? I would really prefer to use dired somehow rather than shell / ls (which was my first thought)

Below is my typical dired listing:

drwxr-xr-x  4 4.0K Jan 15 19:35 Backup
drwxr-xr-x  3 4.0K Jan 14 19:33 Camera
drwxr-xr-x 22 4.0K Jan 15 19:01 content
-rw-r--r--  1   65 Dec 31 16:34 .directory

Rectangle marking first came to mind but the paste seems to have a weird format and strangely inserts the text.

So I came up with the following process:

In dired, select ’(’ dired-hide-details-mode which toggles off all the details and gives:


This looks very promising and surely a simple M-w (kill-ring-save) will work?. It doesn’t work. It still copies the full details :(

However if org-copy-visible is used it does the same as in org mode in that it only copies the visible parts of the region. I had no idea that functions from one mode can be used in another!

-1:-- Using org-copy-visible in dired (Post James Dyer)--L0--C0--January 15, 2023 12:00 AM

200ok: Schedule recurring events with flexibility

We are excited to announce the release of our new JavaScript library, event-scheduler. This library allows users to schedule recurring (multi-day) events with the flexibility to provide exceptions for any conflicts. The results can be rendered in any locale to show your audience the expected date format.

It's a small library that we have created to provide the schedule for the various recurring Zen meditation retreats of Lambda Zen Temple.

We encourage others to release their code under FLOSS terms in their own projects and to check out our event-scheduler library on GitHub. The library is available under the AGPL license, has no dependencies and tests are written in Jest.

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

-1:-- Schedule recurring events with flexibility (Post Alain M. Lafon ( 15, 2023 12:00 AM

Irreal: Sudo-edit

Here’s a nifty bit of Elisp to make opening a file as root a bit easier:

When I first saw it, the idea seemed familiar and it is, in fact, very similar to some code offered by Bozhidar Batsov. I (very) slightly prefer Batsov’s version because it handles my most frequent use case by default. Most of the time when I want to edit a root file, I forget to open it with the sudo tag and have to abort the edit and reopen it. Batsov’s version handles this automatically.

-1:-- Sudo-edit (Post jcs)--L0--C0--January 14, 2023 05:36 PM

Sacha Chua: Using bug-hunter to quickly find a problem in my long Emacs configuration

I noticed that author names in my mastodon.el timeline were no longer getting highlighted. Since it behaved correctly when I used emacs -Q, I knew the problem was somewhere in my configuration. Problem: my configuration is very long. (Uh, my .el seems to have grown to be about 460KB…) I started writing some code to help me bisect it, but fortunately I remembered using a package to do that a long time ago. After a little digging around, I found bug-hunter again. I patched it to allow me to specify a header and footer:

(defvar bug-hunter-header nil "Emacs Lisp sexp to add at the beginning of the file.")
(defvar bug-hunter-footer nil "Emacs Lisp sexp to add at the end of the file.")

(defun bug-hunter--print-to-temp (sexp)
  "Print SEXP to a temp file and return the file name."
  (let ((print-length nil)
        (print-level nil)
        (file (make-temp-file "bug-hunter")))
    (with-temp-file file
      (when bug-hunter-header (print bug-hunter-header (current-buffer)))
      (print sexp (current-buffer))
      (when bug-hunter-footer (print bug-hunter-footer (current-buffer))))

That allowed me to set up this header and footer:

(setq bug-hunter-header
         (use-package quelpa)
         (use-package quelpa-use-package)
         (use-package hydra :commands defhydra)
         (use-package use-package-hydra)))
(setq bug-hunter-footer '(load-file "/tmp/mastodon-test.el"))

where mastodon-test.el was this file:

(add-to-list 'load-path "~/vendor/mastodon.el/lisp")
(require 'mastodon)
(setq mastodon-active-user "sachac"
      mastodon-instance-url "")

I wasn't sure how to use bug-hunter's support for assertions because of the asynchronous retrieval of the Mastodon timeline, but at least I could use that code to set things up.

Then I used bug-hunter-file to split up the Emacs Lisp file tangled out of my literate configuration. All I had to do was wait for the Mastodon timeline to show up, quit Emacs, and answer y if I saw the bug or n if I didn't.

Here's what the result was:

You have asked to do an interactive hunt, here's how it goes.
1) I will start a new Emacs instance, which opens a new frame.
2) You will try to reproduce your problem on the new frame.
3) When you’re done, close that frame.
4) I will ask you if you managed to reproduce the problem.
5) We will repeat steps up to 13 times, so hang tight!
Doing some initial tests...
Initial tests done. Hunting for the cause...
"~/sync/emacs/Sacha.el", line 6893 pos 0:
  The assertion returned the following value here:
  Caused by the following expression:
    (use-package highlight-defined :ensure t :custom
      (highlight-defined-face-use-itself t)
      (help-mode . highlight-defined-mode)
      (emacs-lisp-mode . highlight-defined-mode))

That's weird, since highlight-defined shouldn't have affected mastodon, but okay. I'm going to skip using highlight-defined for now.

If you're tracking down a weird change in your Emacs configuration file, you might find bug-hunter useful too. It's available on ELPA, or you can get it from Github.

View org source for this post
-1:-- Using bug-hunter to quickly find a problem in my long Emacs configuration (Post Sacha Chua)--L0--C0--January 14, 2023 03:06 PM

Marcin Borkowski: My plans for 2023

It is the first time I write a post about my plans for the upcoming year. It’s going to be a writing year!
-1:-- My plans for 2023 (Post)--L0--C0--January 14, 2023 07:03 AM

Emacs APAC: Announcing Emacs Asia-Pacific (APAC) virtual meetup, Saturday, January 28, 2023

This month’s Emacs Asia-Pacific (APAC) virtual meetup is scheduled for Saturday, January 28, 2023 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, January 28, 2023 (Post)--L0--C0--January 14, 2023 12:02 AM

Tony Zorman: Immediately Refile Notes with X.P.OrgMode

Posted on 2023-01-14 | emacs, xmonad

In a previous post I talked about using XMonad.Prompt.OrgMode to rapidly capture thoughts and ideas into an Org file. The functionality that the module provides has proven to be extremely useful to me, and really I couldn’t be happier with it. However, a user recently contacted me by email and told me that they’re missing but one feature: the ability to immediately refile notes.


If you don’t know, refiling is the act of moving an entryA headline, possibly with an attached body. below another heading; i.e., such that it becomes a subheading there. This can be useful for structuring TODOs into separate categories: one might have projects called “Work”, “Life”, “XMonad”, and so on, where all related tasks live. Quite convenient!

So far, X.P.OrgMode just dumped the created note at the end of the specified file, leaving you to pick up the pieces. This aligns with my personal workflow—while I extensively use refiling, I only do so at the end of the day after reviewing all tasks that have accumulated. However, it is conceivable that someone might want to refile certain tasks straight away when it’s pretty clear that (i) they’ll be kept, and (ii) they can be unambiguously assigned to a certain heading (e.g., an already scheduled work meeting with X).


Long story short, this is now built into X.P.OrgMode. There are two new functions:
    orgPromptRefile   :: XPConfig ->           String -> FilePath -> X ()
    orgPromptRefileTo :: XPConfig -> String -> String -> FilePath -> X ()

The former takes the same arguments as orgPrompt (which see), and is for popping up another prompt that asks for a heading. The latter refiles everything under the specified (as the second argument) heading.

The way orgPromptRefile works is that, after querying for a TODO, it always inserts the note into the file and then possibly refiles it to another heading. This way, you don’t need to worry about losing notes when you abort the refiling prompt or enter a non-existent heading.

Note: Refiling is (near) instant; the delay you are seeing above is due to auto-revert-mode.

Some Gory Details

All of the refiling is actually directly done by Emacs itself! More precisely, the EDSL that XMonad.Util.Run defines—which I’ve also written about—shells out to Emacs. This might intuitively feel horrible, but that’s just another reason to share it:
refile :: String -> FilePath -> X ()
refile (asString -> parent) (asString -> fp) =
  proc $ inEmacs
     >-> asBatch
     >-> eval (progn
                [ "find-file" <> fp
                , "end-of-buffer"
                , "org-refile nil nil"
                    <> list [ parent, fp, "nil"
                            , saveExcursion
                               ["org-find-exact-headline-in-buffer" <> parent]
                , "save-buffer"

This—as you probably guessed already—just executes the following elisp snippet in Emacs’s batch mode:
  (find-file «fp»)
  (org-refile nil nil
              (list «parent» «fp» nil
                      (org-find-exact-headline-in-buffer «parent»))))

I know this seems insane, but letting Emacs do this work is actually much less brittle than the alternative. The Org maintainers certainly know best what refiling means, and thus also what it entails—if all of this logic is already written, why not take advantage of it? Plus, I now don’t have to keep track of subtle changes in newer versions of Org.

Closing Thoughts

Writing this was actually a lot of fun, and a great opportunity to play with the EDSL that X.U.Run exposes. I reckon there are a few places in my own XMonad configuration in which I could use these kinds of “Emacs scripts” to great effect!

One other idea I’ve had is to integrate this into the language that plain old orgPrompt accepts. It could be prefixed by something like “ref:”, followed by a unique substring with which to identity a heading. This would have the disadvantage that—without the second prompt—one would not get any suggestions for headings. However, if you want to refile something you probably know where you want to put it; plus, it would not involve a possibly distracting second prompt. Actually, this sounds like a good first pull request: contributions welcome!
-1:-- Immediately Refile Notes with X.P.OrgMode (Post)--L0--C0--January 14, 2023 12:00 AM

Protesilaos Stavrou: Emacs: introduction to ‘substitute’ (my new package?)

UPDATE 2023-01-16 19:13 +0200: I did add the package. Video demo here:

I just set up the Git repositories of substitute for Emacs. Substitute is a set of commands that perform text replacement (i) throughout the buffer, (ii) limited to the current definition (per narrow-to-defun), (iii) from point to the end of the buffer, and (iv) from point to the beginning of the buffer.

These substitutions are meant to be as quick as possible and, as such, differ from the standard query-replace (which I still use). The provided commands prompt for substitute text and perform the substitution outright.

The substitution prompt mentions the target-to-be-substituted. It is possible to use the “future history” at this prompt (by typing M-n with the default key bindings for the next-history-element command). This populates the prompt with the text of the target. As such, if we want to operate on FOO to make it FOO-BAR, we use M-n and then append -BAR.

By default, the design is visually austere: the substitution prompt informs the user about the target but otherwise does not highlight anything. The post-substitution stage is also silent, with no report on how many occurences were replaced. This can be changed so that the substitution prompt highlights occurences (like isearch) and the post-substitution stage prints an informative message on what changed and where.

The substitution commands behave the same way except for their scope of application. What they have in common is the way they identify the target of the substitution: it is either the symbol at point or the text within the boundaries of the active region. The differences in scope are as follows:

  1. substitute-target-in-buffer: Substitute the target across the entire buffer.
  2. substitute-target-in-defun: Substitute the target only in the current definition (per narrow-to-defun).
  3. substitute-target-below-point: Substitute the target from point to the end of the buffer (alias substitute-target-to-end-of-buffer).
  4. substitute-target-above-point: Substitute the target from point to the beginning of the buffer (alias substitute-target-to-beginning-of-buffer).

Should this be on GNU ELPA?

I find these convenient, but I am not sure if I should formalise substitute as a package by adding it to GNU ELPA. What do others use for this case? I am aware of multiple-cursors and iedit but they are not what I am looking for. I also am still using query-replace for its interactivity (yes, I know about its ! operation) and for its ability to limit the effect to the region. I also know about combining embark with query-replace, which I also do. Still, I think there is a niche for substitute. Does anyone have the same feeling or am I deluding myself? In the latter case, there is no point in adding this to GNU ELPA.

You can contact me privately about this topic or, if you want a public discussion, use my “general issues” mailing list by sending an email to: ~protesilaos/


-1:-- Emacs: introduction to ‘substitute’ (my new package?) (Post)--L0--C0--January 14, 2023 12:00 AM

Peter Prevos: Visualise the Emacs User Survey Results with Plain Text

The Emacs User Survey was held in 2020 and 2022. It is a bit ironic that the data was neither collected nor analysed with Emacs. This article shows how to visualise the Emacs user survey results with plain text inside of Emacs. First, we download the CSV file and then parse it into an association list. With some bespoke functions and the quirky chart package that forms part of core Emacs.

Read the cleaned CSV file

Before visualising the data, let's declare some required packages and download the 2020 Emacs user survey data.

  (require 'csv)
  (require 'f)
  (require 'dash)
  (require 'chart)

   "emacs-survey.csv" 1)

Define some functions

The CSV package by Ulf jasper provides functionality to parse a CSV file buffer into a list of alists. Unfortunately, this package has no functionality to read CSV files or extract data, so I wrote a few additional functions. Thanks to u/deaddyfreddy for helping with wrangling association lists. I initially tried chatGPT to write some elisp code, but that was a disaster. Glad to see that human intelligence still trumps the silicon variant.

The csv-parse-file function parses a CSV file into a list of alists. The csv-extract-column-* functions extract the values from a column by number or header. The result of these functions is a simple list that can be visualised.

  (defun csv-parse-file (file)
    "Read CSV FILE and parse its contents."
      (insert-file-contents file)
      (csv-parse-buffer t)))

  (defun csv-extract-column-number (n csv)
    "Extract values in column N from parsed CSV file into an alist."
    (mapcar #'cdr
            (seq-map (lambda (list) (nth (- n 1) list)) csv)))

  (defun csv-extract-column-name (name csv)
    "Extract the values in a column with NAME from parsed CSV into a list."
    (mapcar #'cdr
            (seq-map (apply-partially #'assoc name) csv)))

Visualise the Emacs User Survey Results

Visualising data with in plain text is a lost art. The famous WOW signal

We are now spoilt with

Eric Ludlam's chart package can draw coloured bar charts in plain text. The chart-bar-quicky function takes several arguments, most notably the x and y variables for the chart. The n variable declares the number of bars to draw. We want the bar height to count each unique item, so we also need a function to create a ordered frequency table.

  (defun create-frequency-table (data)
    "Generate an ordered frequency table from DATA."
    (sort (-frequencies data)
          (lambda (a b) (> (cdr a) (cdr b)))))

  (defun visualise-frequency-table (table n var title)
    "Create a bar chart from a frequency TABLE with top N entries.
  VAR and TITLE used for display."
     (mapcar #'car table) var
     (mapcar #'cdr table) "Frequency" n))

Now we are ready to visualise the data. Parsing the data can take a few moments as it contains thousands of results. The bar chart will pop up in a new buffer.

  (setq emacs-survey
        (csv-parse-file "emacs-survey.csv"))

    (csv-extract-column-number 6 emacs-survey))
   "Which version of Emacs do you primarily use?")
/images/emacs/emacs-survey-2020-question-5.png Results of question six of the 2020 Emacs user Survey.


Visualising data with plain text is less aesthetic than using graphics, but it does the job of telling the story, which is the purpose of visualisation. There are other methods to create graphics inside an Emacs buffer. For example, one could use a literate programming approach and write an Org Mode file and run R or Python code to create graphics. There is also the ability to interact with GNUPlot with Emacs, but I have yet to gain experience with this method.

Let's hope the following Emacs user survey will be more Emacs-centric in its's data collection and presenting the results.

-1:-- Visualise the Emacs User Survey Results with Plain Text (Post Peter Prevos)--L0--C0--January 14, 2023 12:00 AM

Irreal: Red Meat Friday: Life Of An Emacser

As anyone who has ventured into the event horizon of their Emacs configuration knows, it can be extraordinarily difficult to get out. As the cartoon suggests, sometimes it can even suck up every available mental cycle leaving no time for other pursuits such as work.

Users of those other editors doubtlessly consider the cartoon a ding on Emacs but every Emacs user I know considers that a feature not a bug. After all, what could be more fun than hacking on your Emacs configuration? That other stuff can wait.

-1:-- Red Meat Friday: Life Of An Emacser (Post jcs)--L0--C0--January 13, 2023 05:36 PM

Please note that aggregates blogs, and blog authors might mention or link to nonfree things.