Mario Jason Braganza: View Only the Day, With Org Agenda

Org Mode, by default, shows me the whole week, when I pull up my agenda. That’s not how I work though. I normally look at the week on Monday mornings (or Sunday evenings) to plan out the week and then work everyday by just looking at what I ought to get done that particular day.

Like Dale Carnegie says,

Shut off the past! Let the dead past bury its dead. Shut out the yesterdays which have lighted fools the way to dusty death.

The load of tomorrow, added to that of yesterday, carried today, makes the strongest falter. Shut off the future as tightly as the past.

The future is today. There is no tomorrow. The day of man’s salvation is now.

Waste of energy, mental distress, nervous worries dog the steps of a man who is anxious about the future. Shut close, then the great fore and aft bulkheads, and prepare to cultivate the habit of life of day-tight compartments.

So I want then, my Org Mode to do show me, only the things, I have on my plate today.

And Org is nothing if not adaptable. org-agenda-span is the variable that controls what the Agends shows me. According to the docs,

This variable can be set to any number of days you want to see by default in the agenda, or to a span name, such a day, week, month or year.

So that’s what I did. This little snippet went into my init file

;; Set Agenda to show a day as the default timespan, instead of a week
(setq org-agenda-span 'day)


Et voilà!


Feedback on this post?
Mail me at feedback at this domain or continue the discourse here.

P.S. Subscribe to my mailing list!
Forward these posts and letters to your friends and get them to subscribe!
P.P.S. Feed my insatiable reading habit.


-1:-- View Only the Day, With Org Agenda (Post Mario Jason Braganza)--L0--C0--2025-01-20T03:20:40.000Z

Lars Ingebrigtsen: Logarithms, how do they work?

My algo for maps sucked because there was just too big a difference between the top country and the rest:

So my map looked like this:

See? The US is white while the rest of the countries are basically two different hues (non-visited countries and visited countries).

Then I remembered that logarithms were a thing that existed, and presto!


Look how pretty!

I amaze myself.

Anyway, that world map I used had a kinda wonky coding — it mixed using id country codes for contiguous countries and class country names for non-contiguous ones. So I’ve straightened that up and put the result on Microsoft Github as a separate package, if you should ever need a world map like that.

That is, you can say:

.US {
  fill: red;
}

This didn’t work in the original version, and you had to mangle the SVG structure itself.

Anyway.

-1:-- Logarithms, how do they work? (Post Lars Ingebrigtsen)--L0--C0--2025-01-19T20:26:39.000Z

Magnus: Reviewing GitHub PRs in Emacs

My Emacs config's todo-list has long had an item about finding some way to review GitHub PRs without having to leave Emacs and when the forge issue that I subscribe to came alive again I thought it was time to see if I can improve my config.

I found three packages for doing reviews

I've tried the first one before but at the time it didn't seem to work at all. Apparently that's improved somewhat, though there's a PR with a change that's necessary to make it work.1 The first two don't support comments on multiple lines of a PR, there are issues/discussions for both

The last one, emacs-pr-review does support commenting on multiple lines, but it lacks a nice way of opening a review from magit. What I can do is

  1. position the cursor on a PR in the magit status view, then
  2. copy the the PR's URL using forge-copy-url-at-point-as-kill, and
  3. open the PR by calling pr-review and pasting the PR's URL.

Which I did for a few days until I got tired of it and wrote a function to cut out they copy/paste part.

(defun mes/pr-review-via-forge ()
  (interactive)
  (if-let* ((target (forge--browse-target))
            (url (if (stringp target) target (forge-get-url target)))
            (rev-url (pr-review-url-parse url)))
      (pr-review url)
    (user-error "No PR to review at point")))

I've bound it to a key in magit-mode-map to make it easier.

I have to say I'm not completely happy with emacs-pr-review, so if either of the other two sort out commenting on multiple lines I'll check them out again.

My full setup for pr-review is here.

Footnotes:

1

The details can be found among the comments of the forge issue.

-1:-- Reviewing GitHub PRs in Emacs (Post Magnus)--L0--C0--2025-01-19T10:10:00.000Z

Mario Jason Braganza: More Espanso & Emacs Shenanigans


Espanso Logo


Now that I use my local machine to draft all my posts, desktop Emacs makes everything easier … with one exception. It swallows up a lot of my Espanso expansions.

My current bugbear is that it will not render a = when expanding.
I know this is entirely something that I brought on myself, and neither Emacs or Espanso in isolation will have issues.

My workaround after rummaging through Espanso’s documentation was to set the backend to use xdotool, in my config. (~/.config/espanso/config/default.yml)

x11_use_xdotool_backend: true

One downside now is that it no longer jumps back to a predefined location in the snippets where I’ve defined such a thing. No matter. I use them rarely and I’ll figure something out later. (Alongside why desktop emacs won’t render markdown expansions sometimes as well)

For now, most of my expansions work. Hurrah!


Feedback on this post?
Mail me at feedback at this domain or continue the discourse here.

P.S. Subscribe to my mailing list!
Forward these posts and letters to your friends and get them to subscribe!
P.P.S. Feed my insatiable reading habit.


-1:-- More Espanso & Emacs Shenanigans (Post Mario Jason Braganza)--L0--C0--2025-01-19T07:15:09.000Z

After yesterday’s issues, today I split my now.org into four different files: personal projects, work projects, work meetings, and blog projects.

Thinking about how to organize the projects in those, I rediscovered org-sort. I think I’m on to something.

-1:--  (Post TAONAW - Emacs and Org Mode)--L0--C0--2025-01-18T20:18:04.000Z

Irreal: Iterating In Lisp

Joe Marshall has an interesting post on iteration for all you Lisp heads. It’s mostly aimed at Common Lisp and Scheme but a lot of it applies to Elisp as well so even Emacs users will be interested. The mail problem for Elisp is that tail recursion isn’t supported.

I almost always prefer tail recursion for explicit iteration when it’s available but Marshall shows that it’s often better to use one of the built-in functions or mechanisms rather than an explicit loop. For example, the map and reduce functions are a form of implicit iteration that are concise and easy to use.

There are also the more imperative looping mechanisms such as the do, dolist, and dotimes macros that emulate the looping mechanisms in imperative languages. I use these a lot if only for their convenience and the knowledge that their implementations will probably expand into the most efficient code possible. In the absence of tail recursion, they’re a good choice.

Finally, there is the series suit. I’ve never used these so I don’t have any firsthand experience with them. See Marshall’s post for an explanation. You can think of them as a refinement of Map/Reduce that does away with the generation of intermediate lists.

Marshall also mentions the loop macro. Lispers either love it or hate. He, like me, isn’t a fan for all the usual reasons.

If you like any of the various Lisp languages, take a look at Marshall’s post. You’ll enjoy it.

-1:-- Iterating In Lisp (Post Irreal)--L0--C0--2025-01-18T16:18:58.000Z

Jeremy Friesen: Forking from a Step in a Stacked Habit

I wrote Some Entries from My Personal Journal, mentioning that I had adopted my personal journal habit for work. For a week, I had used the same capture templates, which produced the same structure.

The experiment worked, but needed refinement. I was writing engineering journal entries, but finding that I needed to carry forward blockers. I chose to sit, for a bit, without the refinement to ensure that I was sustaining the habit.

Clarifying the Functionality

, while waiting on some information to proceed, I started writing out how I envisioned using my work journal. I considered my past work, as mentioned in Adding a Function to Carry Forward an Org-Mode Agenda Item and Revisiting My Org-Mode Time Tracking and Laying out a Different Direction.

However, there was a substantive difference. At my past work, I was responsible for tracking my time to specific clients and projects. In which the accuracy of the time-sheet was of utmost importance. In any given week, I might have had 4 or 5 different clients to whom I was billing.

Contrast to my present situation; I don’t bill to a client and I work on a singular product (albeit with many components). Instead, I work towards the completion of quarterly goals.

I wrote the following as code comments before getting started:

At my current employer, I have quarterly managed business objectives (MBOs). These focal activities involve: initiation, tracking work, and completion (with supporting documentation).

Initiation :: Writing the stated objective with narrative around the goal, measurement, and artifacts.

Tracking Work :: As I work through these objectives, I’m recording and tracking tasks towards the larger MBO. Some tasks become blocked and I need to work towards their resolution by providing sustained energy up and out; to get help with my team.

Completion :: This involves providing documentation demonstrating the completion of the MBO. I can leverage the tracking information to provide the documentation.

The two capture template letters reflect what I’m using for my personal (non-work related) notes. So I’m hoping to piggy back on that muscle memory as I adopt these tracking approaches.

Reviewing Existing Structures

I already had my Management by Objectives (MbOs 📖) in an Org-Mode 📖 document. The structure looked as follows:

* TODO [0/3] 2025-Q1 :mbos:
** TODO Do the thing
** TODO Do the other thing
** TODO Veer left

The first line is heading level one for the quarterly goals (e.g. “2025-Q1”) tagged with mbos. It is in the state TODO with the [0/3] tracking the state of the sub-headings.

The next three lines are each heading level two. Each is in a TODO state followed by their terse name. They inherit the mbos tag from the parent heading.

Throughout the quarter, I’d rarely revisit those goals. Instead, I’d hold them in the back of my head, and towards quarter end revisit and begin assembling documentation supporting their completion.

Finding the Crease

I then set about thinking of my capture templates from my daily personal habit. I mapped j to kicking off the day and J for appending to the day.

I wanted to use the BLOCKED status for tasks. Meaning, I’d want a level 3 (or more) sub-heading, as that’s where I can easily track things in Org-Mode . I spent a bit of time mapping the previous weeks notes to this newly emerging structure.

It gave me a good perspective of what I was working on and what was blocked. And as I started adding time stamps, I could now start seeing things in the agenda view; that is I could see what my work week included and where I may have drifted away from tackling a blocker.

Make it So

I settled on a structure as follows:

* TODO [0/3] 2025-Q1 :mbos:
** TODO [1/2] Do the thing
*** DONE Step 1 :tasks:
*** TODO Step 2 :tasks:
** TODO [0/1] Do the other thing
*** TODO A different step 2 :tasks:
** TODO [0/2] Veer left
*** TODO First you turn :tasks:
*** TODO Then move :tasks:

Starting a Task

I wrote out my j capture template Sidenote See Capture templates (The Org Manual) for details on the structure of a capture template. , that is start a task that maps to an MbO :

(add-to-list 'org-capture-templates
  '("j" "Journal task for MBO"
     entry (file+function
             jf/work/filename-for-mbos
             jf/work/position-at-start-of-mbo)
     "TODO %^{Task} :tasks:\n:PROPERTIES:\n:ID: %(org-id-new)\n:END:\n%?"
     :empty-lines-before 1
     :empty-lines-after 1
     :clock-in t
     :clock-resume t))

And set about writing up the positioning function:

(defun jf/work/position-at-start-of-mbo ()
  "Position point at start of MBO.

For inserting entity."
  (let* ((incomplete-mbos
           (org-map-entries
             (lambda ()
               (let* ((mbo
                        (org-element-at-point))
                       (quarter
                         (car (org-element-lineage mbo))))
                 (cons
                   (concat
                     (propertize
                       (org-element-property :title quarter)
                       'face 'org-level-1)
                     (propertize
                       " > "
                       'face 'consult-separator)
                     (propertize
                       (org-element-property :title mbo)
                       'face 'org-level-2)
                     )
                   (org-element-property :begin mbo))))
             "+LEVEL=2+mbos-noexport+TODO!=\"DONE\""))
          (mbo
            (completing-read
              "MBO: " incomplete-mbos nil t)))
    (goto-char (alist-get mbo incomplete-mbos nil nil #'string=))))

A lingering problem is that this does not account for a daily entry that does not track towards one of my MbOs ; for example helping get someone else unstuck. But I can see space for how I’d do that…make another heading level 2 node that I don’t report.

Adding to a Task

Here’s the J capture template, that is append a note to a task.

(add-to-list 'org-capture-templates
  '("J" "Journal task progress for MBO"
     plain (file+function
             jf/work/filename-for-mbos
             jf/work/position-at-end-of-mbo-task-entity)
     "%T :: %?"
     :empty-lines-before 1
     :empty-lines-after 1
     :clock-in t
     :clock-resume t))

And here’s the function to find the place to insert the note:

(defvar jf/work/org-map-entries-mbo-task-filter
  "+LEVEL=3+mbos+tasks-journals+TODO!=\"DONE\""
  "The default filter for collecting MBO tasks.")

(defun jf/work/position-at-end-of-mbo-task-entity ()
  "Position point just before content end of MBO task.

For inserting plain text."
  (let* ((incomplete-mbo-tasks
           (org-map-entries
             (lambda ()
               (let* ((task
                        (org-element-at-point))
                       (mbo
                         (car (org-element-lineage task)))
                       (quarter
                         (cadr (org-element-lineage task))))
                 (cons
                   (concat
                     (propertize
                       (org-element-property :title quarter)
                       'face 'org-level-1)
                     (propertize
                       " > "
                       'face 'consult-separator)
                     (propertize
                       (org-element-property :title mbo)
                       'face 'org-level-2)
                     (propertize
                       " > "
                       'face 'consult-separator)
                     (propertize
                       (org-element-property :title task)
                       'face 'org-level-3)
                     )
                   (org-element-property :contents-end task))))
             jf/work/org-map-entries-mbo-task-filter))
          (task
            (completing-read
              "Task: " incomplete-mbo-tasks nil t)))
    ;; The contents-end of the task is the begining of the line that
    ;; contains the next heading.  Which the capture process for a
    ;; "plain" item then interprets as "add an item to the current
    ;; heading".  Not ideal, so move point backwards one character.
    (goto-char
      (1- (alist-get task incomplete-mbo-tasks nil nil #'string=)))))

The inline comments reflect a problem I encountered and how I solved it.

Junk Drawer

In reviewing my notes, I found that some entries did not map to tasks. I could make them a task, but they were working notes. So I decided to add a new level 3 headline: “Engineering Journal”. And tagged it with journals.

The following capture template mimics the J template (e.g. , append to a task):

(add-to-list 'org-capture-templates
  '("e" "Engineering Journal entry for MBO"
     plain (file+function
             jf/work/filename-for-mbos
             jf/work/position-in-mbo-journal-entry)
     "%T :: %?"
     :empty-lines-before 1
     :empty-lines-after 1
     :clock-in t
     :clock-resume t))

And the function to perform the capture leverages is:

(defun jf/work/position-in-mbo-journal-entry ()
  "Position point just before the selected journal.

Included to allow the re-use of logic for different 3rd level tasks."
  (let ((jf/work/org-map-entries-mbo-task-filter
          "+LEVEL=3+mbos+tasks+journals"))
    (jf/work/position-at-end-of-mbo-task-entity)))

I chose to separate the tasks and journal, in part to reduce the selection process. However, I might collapse these two.

Applying to the Personal

I don’t envision supplanting my personal j and J capture templates with the more detailed work templates. However, I’m going to hold space on how I might use those work steps. Maybe they can help me accomplish personal goals? Or even set them?

-1:-- Forking from a Step in a Stacked Habit (Post Jeremy Friesen)--L0--C0--2025-01-18T12:12:58.000Z

James Dyer: Streamlining Navigation in Org-Mode using an adapted org-goto

Navigating through lengthy org files can sometimes feel cumbersome. To address this I have tweaked org-goto to be a little more usable through the minibuffer completion system to jump to any org heading.

I have been aware of the org-goto command for a while now, which technically, allows for the efficient navigation through an org file. Every now and then I give it a go, but it just feels clunky and some of the keybindings don’t quite feel intuitively Emacs for some reason.

Now my org files are getting more elaborate, I would like a better way to navigate and especially through the hierarchy of headings.

What about org-imenu-depth I hear you say! Well as far as I can tell, this allows stepping through to a defined subheading level but while going through multiple menu/hierarchical selection steps. For efficiency, I would like a way to flatten the subheadings for a one-shot completion selection method. This is where org-goto can actually be adapted to satisfy this need, and I think leverages the org refile menu system.

To achieve this, some variables will require changing, firstly:

(setq org-goto-interface 'outline-path-completionp)

The org-goto-interface variable defines the interface you use to navigate when calling org-goto. By default, org goto uses a mixed interface with direct completion and an alternative outline-based navigation using buffers. I prefer the outline-path-completionp setting, which provides a breadcrumb-like structure for easier navigation through the minibuffer completion system.

By default, Org mode’s outline-path interface works in steps, which means you select one heading level at a time. I find this behaviour a bit clunky, especially for larger files. Fortunately, you can disable this step-by-step behaviour by setting org-outline-path-complete-in-steps to nil which flattens the presented org goto heading list when activated so you can see the full list of headings/subheadings in a single step and therefore be able to navigate directly to your desired location.

(setq org-outline-path-complete-in-steps nil)

Lets demonstrate how this now works with an example.

Given the following org:

* Heading 1
...
* Heading 2
...
** Subheading 2.1
...
* Subheading 3
...
** Subheading 3.1
...
*** Subheading 3.1.1

imenu would just allow navigation to leaf nodes and not the higher subdirectories.

(Note : I use fido-mode=/=icomplete):

Here is the minibuffer menu presented when imenu is activated by default:

(Heading.1 | Heading.2 | *Rescan* | Subheading.3)

For example, I can navigate to Heading 1 but what about Heading 2 or Heading 3?, when selecting these headings that have subheadings it will take me into further leaf node menus for selection.

This is where the setup above for org-goto comes in handy as it now presents the following:

{Heading 2/ | Heading 2/Subheading 2.1/ | Heading 1/ | Subheading 3/ | Subheading 3/Subheading 3.1/ | Subheading 3/Subheading 3.1/Subheading 3.1.1/}

Where of course the minibuffer completion system can complete to not just the leaf nodes but also the higher level subheadings, allowing navigation to any org heading!

-1:-- Streamlining Navigation in Org-Mode using an adapted org-goto (Post James Dyer)--L0--C0--2025-01-18T11:08:00.000Z

Lars Ingebrigtsen: Maps & WordPress Statistics

While dropping off to sleep last night, I was thinking about how much work it might be to add a world map to the WordPress Statistics for Emacs thing. Not because it’s useful, but because Jetpack has one, and I have to have feature parity!!!

So that’s what the Jetpack one looks like… Yeah, yeah, Mercator, but it does look kinda nice?

But then it occurred to me: Surely somebody has made an SVG world map with nice markup? And yes!

So I just have to read it in with libxml-parse-xml-region, adjust the colours to reflect view numbers with some dom-by-id and then let Emacs render it:

Easy peasy.

The one complication is that not all countries are contiguous, and you can’t have the same ID on several elements, so how do they solve that?

*sigh*

So non-contiguous countries don’t have IDs at all, but have a class grouping them… using the country name! So you have to have not only country codes, but a mapping to country names, and hope you’re using the same mapping that they’re using (i.e., “United States” and not “United States of America”, etc (and can class names even have spaces?)).

I should demand a refund! For that free map! The nerve of some people!

Anyway.

-1:-- Maps & WordPress Statistics (Post Lars Ingebrigtsen)--L0--C0--2025-01-18T08:50:37.000Z

TAONAW - Emacs and Org Mode: More problems with my projects and meetings in org-mode

In my capture templates, I now use prepend, which works well and puts my captured header at the top of now.org, which is what I want. The problem is that I barely use the meeting capture template I’ve made. I refile from my calendar file 95% of the time, and Refile places headers at the bottom of now.org bottom. The results: new projects are at the top, and new meetings are at the bottom. Chaos.

I know I can reverse how Refile works, but I’m not sure I want this to work globally. I can start hacking things and write functions to refile meetings the way I want, but I’m starting to think my problem is bigger than that.

See, the above is just the latest issue in the last week. When I was sorting through tasks on now.org I saw a bunch of TODOs that were tied to a project at one point on the project level; that is, TODOs are filed among the ACTIVE project level, floating out of their parent projects, and as some of those get old I have no idea what projects they were used to be a part of. Why did that happen and why, I don’t know.

On top of that, I just had the issue yesterday where some of the headers lost their “**” at the start of the header, non-headers.

I can’t trust my now.org like this. I’m frustrated, and I just want to throw every single project into its own file at this point, just because of this mess.

I need to calm down get back to the basics and define my main workflows (meetings vs projects vs quick tasks etc.)

-1:-- More problems with my projects and meetings in org-mode (Post TAONAW - Emacs and Org Mode)--L0--C0--2025-01-17T15:25:04.000Z

Charles Choi: Finding Text in Files Ergonomically - Announcing recent-rgrep

Searching for text in a directory tree of files is a commonplace task. For people who use plain text files there are a number of tools that can accomplish this. One such tool that is ubiquitous in Unix-flavored (predominantly Linux and macOS) systems is the command line utility grep. Emacs builds on this with the rgrep command, invoking grep recursively and presenting its results in a grep-mode buffer. In other editors and IDEs, this feature is named “Find in Files” or “Find in Project”.

Recently I went down the rabbit hole of wanting to get the results of rgrep but with the following twist: report the match results in order of recently modified file, descending. The implementation I ended up with is a two-pass solution that’s brute force but effective.

Announcing recent-rgrep, available on GitHub.

Features

  • Reports matches in order most recently modified files descending (that is, the most recent files are reported first).
  • Search only in non-binary files.
  • Avoids searching in common SCM directories (.git, .hg, .svn, .cvs).
  • Uses the existing grep that you have installed on your system.
  • Default search is case-insensitive.
  • Supports usage from both the command line and Emacs.

What about ripgrep?

NGL, you’re likely better off using ripgrep if you have it installed. But if not, then this modest script can achieve the desired result described above.

Closing Thoughts

If this all seems interesting, I invite you to try recent-rgrep out. In practice, I’ve found it to make my searches more effective as I’m usually more interested in the files that I last worked on. While there’s a performance hit with making two passes, I generally don’t encounter it in the places I use it. YMMV.

-1:-- Finding Text in Files Ergonomically - Announcing recent-rgrep (Post Charles Choi)--L0--C0--2025-01-17T13:30:00.000Z

Meta Redux: A Simpler Way to Deal with Java Sources in CIDER

For ages dealing with Java sources in CIDER has been quite painful.1 Admittedly, much of the problems were related to an early design decision I made to look for the Java sources only in the classpath, as I assumed that would be easiest way to implement this. Boy, was I wrong! Countless of iterations and refinements to the original solution later, working with Java sources is still not easy. enrich-classpath made things better, but it required changes to the way CIDER (nREPL) was being started and slowed down the first CIDER run in each project quite a bit, as it fetches all missing sources at startup. It’s also a bit trickier to use it with cider-connect, as you need to start nREPL together with enrich-classpath. Fortunately, my good friend and legendary Clojure hacker Oleksandr Yakushev recently proposed a different way of doing things and today I’m happy to announce that this new approach is a reality!

There’s an exciting new feature waiting for you in the latest CIDER MELPA build. After updating, try turning on the new variable cider-download-java-sources (M-x customize-variable cider-download-java-sources and then toggle to enable it). Now CIDER will download the Java sources of third-party libraries for Java classes when:

  • you request documentation for a class or a method (C-c C-d C-d)
  • you jump to some definition (M-.) within a Java class

Note that eldoc won’t trigger the auto-download of Java sources, as we felt this might be harmful to the user experience.

This feature works without enrich-classpath.2 The auto-downloading works for both tools.deps and Leiningen-based projects. In both cases, it starts a subprocess of either clojure or lein binary (this is the same approach that Clojure’s 1.12 add-lib utilizes).

And that’s it! The new approach is so seamless that it feels a bit like magic.

This approach should work well for most cases, but it’s not perfect. You might have problems downloading the sources of dependencies that are not public (i.e. they live in a private repo), and the credentials are non-global but under some specific alias/profile that you start REPL with. If this happens to you, please report it; but we suspect such cases would be rare. The download usually takes up to a few seconds, and then the downloaded artifact will be reused by all projects. If a download failed (most often, because the library didn’t publish the -sources.jar artifact to Maven), CIDER will not attempt to download it again until the REPL restarts. Try it out in any project by jumping to clojure.lang.RT/toArray or bringing up the docs for clojure.lang.PersistentArrayMap.

Our plan right now is to have this new feature be disabled by default in CIDER 1.17 (the next stable release), so we can gather some user feedback before enabling it by default in CIDER 1.18. We’d really appreciate your help in testing and polishing the new functionality and we’re looking forward to hear if it’s working well for you!

We also hope that other Clojure editors that use cider-nrepl internally (think Calva, iced-vim, etc) will enable the new functionality soon as well.

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

P.S. The State of CIDER 2024 survey is still open and it’d be great if you took a moment to fill it in!

  1. You need to have them around to be able to navigate to (definitions in) them and improved Java completion. More details here

  2. If you liked using enrich-classpath you can still continue using it going forward. 

-1:-- A Simpler Way to Deal with Java Sources in CIDER (Post Meta Redux)--L0--C0--2025-01-17T11:02:00.000Z

Matt Maguire: Exporting Org Mode Tables to LaTeX

In my previous post on using Org mode to make lesson notes , I mentioned that it is possible to export Org mode tables to \(\LaTeX\). There are some benefits to using Org mode instead of raw \(\LaTeX\) when making tables:

  • Org mode has a powerful table editor that makes it easy to draw out a table and move rows and columns around. Org mode will automatically make the all the columns line up nicely.
  • Since the cells in an Org mode table are all nicely lined up, it makes the table much easier to visualise without having to count & symbols in raw \(\LaTeX\) code.
  • Org mode allows the use of formulas in a table, somewhat like a spreadsheet – this is harder to achieve using \(\LaTeX\) directly.

The tables produced by Org mode are pretty basic by default. However, you can use the \(\LaTeX\) tabularray package to customise the look of your tables. Incidentally, if you are not already using the modern tabularray to produce your \(\LaTeX\) tables, you should definitely look into it – it is way more powerful and easy to use compared to the more traditional table packages (you can thank me later).

-1:-- Exporting Org Mode Tables to LaTeX (Post Matt Maguire)--L0--C0--2025-01-17T00:00:00.000Z

Irreal: Where Should Your Configuration Live, Redux

A couple of weeks ago, I published a Red Meat Friday article about the proper place for your Emacs configuration to reside. Like many Red Meat Friday posts, it was tongue in cheek but it got quite a bit of traction. Most, but not all, commenters were against putting it n the ~/.entconfig/emacs directory.

Bozhidar Batsov just published a post in favor of using ~/.config/emacs instead of just ~/.emacs or ~/.emacs.d. The argument is that it reduces home directory clutter by putting all the configuration files in a single place. I don’t find that a particularly compelling argument.

The thing is, ~/.emacs.d contains a lot more than just your Emacs configuration files. There’s all kinds of things in there including random packages, ELPA files, all kinds of state data, and plenty of other things. Sure, you could put that all into ~/.config but that seems wrong. The vast majority of it isn’t configuration data.

The XDG standard provides places for all that other data too, but that’s a non-starter for me. Who wants your Emacs data scattered among many different directories?

The commenters to Batsov’s post seem mainly to agree with him but we here in the Irreal bunker are going to continue putting all our Emacs stuff in ~/.emacs.d. It was hard enough giving up ~/.emacs.

Of course, as I and others pointed out in the original Red Meat Friday post, none of this matters very much because you can always use symlinks to put your configuration wherever you want it and make it seem as if it’s in the same old place. But if you do that, you’ve recluttered your home directory and added a level of indirection to your files.

Batsov says, and I agree, that ~/.emacs.d is probably never going away. He’s sorry about that.
I’m not.

-1:-- Where Should Your Configuration Live, Redux (Post Irreal)--L0--C0--2025-01-16T19:14:54.000Z

Sacha Chua: Changing planet.emacslife.com

For the longest time, planet.emacslife.com was generated with the Planet Venus aggregator, which required Python 2.7. My recent blog post about Yay Emacs 8: which-key-replacement-alist had some emojis that the parser couldn't handle, though (unichr() arg not in range(0x10000) (narrow Python build)). I decided to rewrite the aggregator using NodeJS.

Here are some differences between the current implementation and the previous ones:

  • The website shows the last two weeks of posts, since I can filter by date. It should also ignore future-dated posts.
  • The list of feeds on the right side is now sorted by last post date, so it's easier to see active blogs.
  • I can now filter a general feed by a regular expression.
  • I've removed a number of unreachable blogs.
  • The feed list is loaded from a JSON instead of an INI.

The Atom feed and the OPML file should validate, but let me know at sacha@sachachua.com if there are any hiccups (or if you have an Atom/RSS feed we can add to the aggregator =) ).

View org source for this post
-1:-- Changing planet.emacslife.com (Post Sacha Chua)--L0--C0--2025-01-16T15:50:31.000Z

Kisaragi Hiu: RIME 停用它自己的英數輸入模式

我電腦上習慣用 RIME 輸入法引擎提供的注音輸入法,但它預設(就像其他注音輸入法一樣)有自己的英數模式。因為系統的輸入法引擎(我是用 fcitx5)也
-1:-- RIME 停用它自己的英數輸入模式 (Post Kisaragi Hiu)--L0--C0--2025-01-16T14:55:32.000Z

Lars Ingebrigtsen: WordPress Statistics for Emacs

A few days ago, I posted about writing a new WordPress stats thing because the Jetpack stats layout annoyed me too much.

I’ve continued to tinker with it, and I think it should now basically be usable. I renamed it to wse (WordPress Statistics for Emacs), and it’s on Microsoft Github if anybody’s interested in trying it out.

The Emacs portion updates asynchronously in the background now.

I added one thing that doesn’t have anything to do with statistics per se — it now also displays recent comments, because that’s something one has to keep track of (if for no other reason than to delete any spam that slips through the (very good, but not perfect) Akismet anti-spam filter).

One thing that I’ve totally missed is that in 2020, Chrome changed how they do inter-site Referrers: They only send over the domain name, but leave off the path part. I thought I had a bug in my code, or that almost all domains had installed “referrer scrubbers” (like Google has always had). But nope: All you get is the domain name.

This is, of course, good for privacy. And I’m totally sure that Google did this Chrome change out of the goodness of their hearts, and not because this forces marketers to sign up for Google-based user tracking even harder than before. Good on ’em!

Speaking of browsers… I couldn’t help adding this little table. It’s not that it’s useful, but I was just kinda sorta curious about what people use these days. And the audience for this blog is just 50% Chrome users, which is unusual, of course. Such a discerning audience!

The desktop to mobile ratio surprises me, though — I thought that would be more 50/50, but nope. And, of course, tablets have stopped being a thing, but that’s not really surprising.

The sample is very small (one day), though, so drawing any conclusions here wouldn’t be a good idea.

-1:-- WordPress Statistics for Emacs (Post Lars Ingebrigtsen)--L0--C0--2025-01-16T06:52:28.000Z

Protesilaos Stavrou: Emacs: Org todo and agenda basics

Raw link: https://www.youtube.com/watch?v=L0EJeN1fCYw

In this ~45-minute video I cover the basics of managing your task list with Org mode. The idea is to write tasks in a simple file and then use the Org agenda views to display and filter the tasks you are interested in. Throughout the video I also comment on relevant points about the overall workflow. The basic configuration I show in the video is below:

;; These are the defaults we want to change.  We do so in the
;; following `use-package' declaration.
(setq org-M-RET-may-split-line '((default . t)))
(setq org-insert-heading-respect-content nil)
(setq org-log-done nil)
(setq org-log-into-drawer nil)


(use-package org
  :ensure nil ; do not try to install it as it is built-in
  :config
  (setq org-M-RET-may-split-line '((default . nil)))
  (setq org-insert-heading-respect-content t)
  (setq org-log-done 'time)
  (setq org-log-into-drawer t)

  (setq org-directory "/tmp/testing-org/")
  (setq org-agenda-files (list org-directory))

  ;; Learn about the ! and more by reading the relevant section of the
  ;; Org manual.  Evaluate: (info "(org) Tracking TODO state changes")
  (setq org-todo-keywords
        '((sequence "TODO(t)" "WAIT(w!)" "|" "CANCEL(c!)" "DONE(d!)"))))
-1:-- Emacs: Org todo and agenda basics (Post Protesilaos Stavrou)--L0--C0--2025-01-16T00:00:00.000Z

Srijan Choudhary: 2025-01-15-001

I had been facing an issue in #Emacs on my work Mac system: C-S-<tab> was somehow being translated to C-<tab>. I tried to look into key-translation-map to figure out the issue, but could not find anything.

Finally, turned out that I had bound C-<tab> to tab-line-switch-to-next-tab and C-<iso-lefttab> to tab-line-switch-to-prev-tab, but the actual C-S-<tab> was unbound. C-<iso-lefttab> only works on linux: something to do with how X11 sends the event to the application (and probably some compatibility mode due to which wayland was doing the same).

On Mac, once I explicitly bound C-S-<tab> in my Emacs config, it started working correctly.

-1:-- 2025-01-15-001 (Post Srijan Choudhary)--L0--C0--2025-01-15T02:20:00.000Z

200ok: Deep Merging YAML with Ruby Made Easy

Introduction

In the world of configuration management, YAML has become a de facto standard. However, when dealing with complex configurations, the need for better inheritance and merging capabilities becomes apparent. Today, I want to introduce you to YamlExtensions, a Ruby gem that solves this exact problem.

The Problem with Standard YAML Merging

Standard YAML offers basic merging with the << operator, but it falls short when dealing with nested structures. This limitation often leads to duplicate configurations or complex workarounds.

Enter YamlExtensions

YamlExtensions introduces a powerful feature: deep merging using the <<< operator. This simple yet effective enhancement allows for more sophisticated configuration management.

How It Works

Let's look at a practical example:

defaults: &DEFAULTS
  hello: world
  this:
    is:
      even: deeper

main:
  <<<: *DEFAULTS
  this:
    is:
     somewhat: deep

The gem intelligently merges nested structures, maintaining both the original and overridden values where appropriate.

Multiple Deep Merges

One particularly powerful feature is the ability to perform multiple deep merges by appending numbers to the <<< operator:

main:
  <<<1: *DEFAULTS
  <<<2: *OVERRIDES
  this:
    is:
     somewhat: deep

Getting Started

After installation, simply require the gem in your Ruby code:

require 'yaml_extensions'

YamlExtensions will freedom patch Ruby's YAML implementation to support the aforementioned features.


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

-1:-- Deep Merging YAML with Ruby Made Easy (Post 200ok)--L0--C0--2025-01-15T00:00:00.000Z

Irreal: Installing By Default With =use-package=

Bozhidar Batsov has a handy tip in his latest post. He, like me, organizes the package management of his init.el around use-package. It works well for him but he has a complaint. He almost always wants to install the packages by default but has to specify :ensure t for each package that he wants loaded by default. He has, he says, more than 50 packages so this is a nuisance.

There is, however, an easy solution. You can simply set use-package-always-ensure to t and all packages except those with :ensure nil will be loaded by default. Batsov notes that some people don’t like loading packages by default because of version issues but that this has not been an issue for him.

It hasn’t been an issue for me either. But neither has specifying :ensure t for each package. I have, according to a quick check with keep-lines, 70 instances of use-packages. All of them specify :ensure t and I’ve never considered this an irritant even though I know about use-package-always-ensure.

Still, I understand and support the urge to make everything as simple as possible so if you’re an use-package user and want to load all or most of your packages by default, Batsov shows a way of doing that with a little less effort.

-1:-- Installing By Default With =use-package= (Post Irreal)--L0--C0--2025-01-14T15:42:02.000Z

Yi Tang: Use Ledger-Cli to Track DIY Project Expenses

Table of Contents

  1. Personal Technical Challenge
  2. Baby Steps
  3. Why? - Effort Estimation

Personal Technical Challenge

I used ledger-cli1 before and it was a painful experience. The problem was not rooted in the tool but in how I intended to use it: I wanted to track all my expenses, from buying a cup of coffee to booking a holiday package. When I started this journey, there was a massive jump from knowing little to nothing about personal finance to doing double-entry accounting in plain text.

Though I gave up, it introduced me to the idea of owning my bank transaction data in text files on my personal computer. So over the years, I manually curated about 8 years of historical transaction data.

If you haven’t done so, I strongly recommend you go to your banks’ website and download the transaction data manually, going as far back as you can. You will notice that the banks only give access to 3-5 years of data2. It’s a shame that banks use outdated technologies but it is better than having nothing.

Since I had the data, I did some analysis and charts in Python/R. But I kept wondering what ledger-cli can offer. I occasionally saw blog posts on ledger-cli in the Emacs communities, so there must be something out there.

It also has become a personal challenge. I turned not to give up but put it aside to tackle it again after I got older.

Baby Steps

Hopefully, I had become smarter as well. This time, to ensure I can successfully adopt the tool, I am going to reduce the scope to limit to only tracking DIY project expenses.

I love DIY and I wish I had more days for DIY projects. It is usually labour-intensive and I feel hyped and extremely confident after a couple of DIY. Pairing it with learning ledger-cli, a cognitive-intensive activity, would make them a nice bundle3.

Though the usage is simple, the question it can answer is important. I want to know, during or after the DIY project, how much it exactly costs. I could use a much simpler tool, like spreadsheets or a pen/notebook, but I want it to be a stepping stone to acquire ledger-cli properly in the future.

Why? - Effort Estimation

I need an accurate answer to the actual costs so that I can use the data to train myself in cost estimation. This is an very important skill to have as a homeowner, it would put me in a much better position in negotiation with the tradesman. A lot of the people in the UK complained that they or their relatives got ripped off by tradesman.4

In general, house repairs and improvements are getting much more expensive every year, due to the shortage of labourers, inflation and Brexit etc. To give an example using my last two quotes, adding an electrical socket costs £240 and replacing a small section of water pipes costs £500.

I have a good habit of using org-mode to track time, my goal to add ledger-cil to my system to track the expenses. After that, I would know if it is really worth doing the DIY or finding a proper tradesman. The total cost itself is not the only metric that matters, but n very essential one to have.

Footnotes

1 https://ledger-cli.org/

2 Why don’t banks give access to all your transaction activity?

3 I might pick it up from Atomic Habit

4 How many of you have been ripped off by builders / tradesmen? (or know someone closely that has)

-1:-- Use Ledger-Cli to Track DIY Project Expenses (Post Yi Tang)--L0--C0--2025-01-14T00:00:00.000Z

Marcin Borkowski: A minor Org Clive improvement

It’s been a while since I touched Org Clive, but I’m still using it. (Side note: after writing about a dozen articles on my Doctor Who weblog it went a bit dormant. I’m still thinking about it – in fact, even more than thinking, I’m slowly writing it – and I really do hope to come back to publishing there with some regularity. Stay tuned, and if you are interested, keep its RSS feed in your RSS reader.)
-1:-- A minor Org Clive improvement (Post Marcin Borkowski)--L0--C0--2025-01-13T20:27:55.000Z

Srijan Choudhary: Triggering Orgzly sync on Android when Org file changes

Introduction

To use my org GTD system on the go, I use the excellent Orgzly Revived mobile app for Android. To sync the org files between my laptop and my phone, I use syncthing (specifically, the Syncthing Android Fork by Catfriend1).

This allows me to quickly capture things from my phone, get reminder notifications of time-sensitive tasks, and mark tasks complete from my phone. The widgets are also useful for quickly looking at some contexts like errands or calls.

Sync Issues

However, Orgzly works by contructing a copy of the contents in it's own database and synchronizing it against the files periodically or when some action is taken in the app. Right now, it does not support synchronizing the data when the file changes.

For me, this has sometimes lead to a conflict between the Orgzly database and the actual files in the org folder. This only happens if the org file is edited on my laptop and something is also edited in the Orgzly app before syncing.

But, the Orgzly app supports intents that can be used from Tasker, Automate, etc to trigger an event-based sync.

Tasker Profile

So, I created a tasker profile to do this. It was surprisingly easy (I've used tasker before, though not too much). It can be found here: https://taskernet.com/?public&tags=orgzly&time=AllTime called "Run Orgzly Sync When Org Files Change".

Here's the basic flow of the profile:

Profile: Run Orgzly Sync When Org Files Change
Settings: Notification: no
Variables: [ %orgfolder:has value ]
    Event: File Modified [ File:%orgfolder Event:* ]



Enter Task: Orgzly Sync
Settings: Run Both Together

A1: If [ %evtprm1 ~ *.org & %evtprm2 ~ ClosedWrite/MovedTo ]

    A2: Send Intent [
         Action: com.orgzly.intent.action.SYNC_START
         Cat: None
         Package: com.orgzlyrevived
         Class: com.orgzly.android.ActionReceiver
         Target: Broadcast Receiver ]

A3: End If

How it works

  1. When importing this profile, it will ask for your org folder in the local Android filesystem.
  2. Once selected and the profile is activated, it will start monitoring this folder for inotify events.
  3. When any file is modified (or created or moved to/from the folder or deleted), this profile is triggered. It received the parameters: full path of the affected file, and the actual event.
  4. Then, it checks if the affected file is an org file (ending in .org) AND if the event is one of ClosedWrite or MovedTo. I filtered to these events because they are usually the last event received for an edit.
  5. If yes, then Orgzly sync is triggered using an intent.

I've been using this for the last several months, and it works well as long as both devices are online when making edits. Conflicts can still happen if, for example, I make some edits on my laptop and subsequently on my phone but the phone is not online or the syncthing app is not running due to data saver or battery saver active. In those cases, eventually syncthing creates a conflicted file that I can manually resolve.

Limitations

  1. It does not support recursive files inside the folder. Tasker right now does not support recursively watching a folder, but if it's added this can be a good edition; specially because Orgzly Revived supports that.
  2. Since this watches the files in the folder, it also triggers a sync if Orgzly app was used to change the file. Not sure if this can be filtered somehow. Maybe based on foreground app? But it seems flakey.

Alternatives

  1. Orgzly Revived has a git sync backend in beta. This might work better with auto-commit & push.
  2. Using Emacs on Android instead of Orgzly is also an option, but I felt it did not work very well without an external keyboard. Also, it does not have widgets.
-1:-- Triggering Orgzly sync on Android when Org file changes (Post Srijan Choudhary)--L0--C0--2025-01-13T18:20:00.000Z

Emacs APAC: UPDATED Announcing Emacs Asia-Pacific (APAC) virtual meetup, Saturday, January 18, 2025

This month’s Emacs Asia-Pacific (APAC) virtual meetup is scheduled for Saturday, January 18, 2025 with Jitsi Meet 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:-- UPDATED Announcing Emacs Asia-Pacific (APAC) virtual meetup, Saturday, January 18, 2025 (Post Emacs APAC)--L0--C0--2025-01-13T17:30:00.000Z

Sacha Chua: 2025-01-13 Emacs news

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

View org source for this post
-1:-- 2025-01-13 Emacs news (Post Sacha Chua)--L0--C0--2025-01-13T13:04:42.000Z

Emacs Redux: Emacs and XDG sitting on a tree

Where to place my Emacs configuration? That is the question!

This fairly simple question has surprisingly many answers, as it often happens with projects as old as Emacs:

  • Historically Emacs’s user config was a file called .emacs, placed in your home directory
  • Later you could use the name init.el as an alias to .emacs
  • And then there’s the option that’s probably most common today - placing your configuration under the .emacs.d folder, where you usually have an init.el that potentially refers to other Emacs Lisp libraries

But wait, there’s more! Emacs 27.1 introduced XDG_CONFIG_HOME support, and Emacs 29.1 extended the level of XDG base directory specification support in Emacs.

XDG, which stands for X Desktop Group, is a set of standards and specifications developed by freedesktop.org to promote interoperability among different desktop environments, primarily in Unix-like operating systems. The group focuses on ensuring that applications can work seamlessly across various desktop environments by adhering to common guidelines.

Their most notable work to date is the XDG base directory specification. In a nutshell this specification outlines the environment variables that define where user-specific data files, configuration files, and cache files should be stored. For instance, it specifies directories like $XDG_CONFIG_HOME for configuration files and $XDG_DATA_HOME for data files.1

Most of the time the XDG config home would be ~/.config and there would be a subfolder there for the configuration of each application. In Emacs’s case that would be ~/.config/emacs. I hope it’s clear that the idea here is to reduce the clutter at the top-level and to make sure that each application stores its configuration in a predictable place.

I think it’s really great that Emacs is (slowly) adopting the industry standards and I believe that over the course of time the XDG config folder will become the preferred place to store your Emacs configuration. That’s why I encourage everyone to move in this direction and unclutter their home folder a bit.

Just keep in mind a couple of things:

  • You’ll have to make sure that folders like ~/.emacs.d don’t exist anymore, as they’d have precedence over the XDG config folder. The configuration resolution happens like this:

Emacs looks for your init file using the filenames ~/.emacs.el, ~/.emacs, or ~/.emacs.d/init.el in that order; you can choose to use any one of these names. (Note that only the locations directly in your home directory have a leading dot in the location’s basename.)

Emacs can also look in an XDG-compatible location for init.el, the default is the directory ~/.config/emacs. This can be overridden by setting XDG_CONFIG_HOME in your environment, its value replaces ~/.config in the name of the default XDG init file. However ~/.emacs.d, ~/.emacs, and ~/.emacs.el are always preferred if they exist, which means that you must delete or rename them in order to use the XDG location.

Note also that if neither the XDG location nor ~/.emacs.d exist, then Emacs will create ~/.emacs.d (and therefore use it during subsequent invocations).

Emacs will set user-emacs-directory to the directory it decides to use.

Not the best defaults IMO (especially falling back to creating .emacs.d), but you can’t fight tradition! Or rather - fighting tradition is pretty hard…

  • Some packages might have hardcoded the path in which they store their own configuration. In general they should be relying on user-emacs-directory, which will be auto-set to whatever directory Emacs discovered its configuration in, but there will always be some packages that probably didn’t do “the right thing”.

I’m guessing that we’re not really getting rid of ~/.emacs.d any time soon (or ever), but I’m hoping that article like this one might speed up a bit the process. Time will tell.

macOS users should keep in mind that unfortunately macOS doesn’t set the standard XDG environment variables, as it has its own notion of where things like configuration files, application cache, etc should be stored. Still, it’s fairly easy to just set the missing variables yourself (e.g. in your .zshrc):

export XDG_CONFIG_HOME = $HOME/.config
export XDG_DATA_HOME = $HOME/.local/share
export XDG_STATE_HOME = $HOME/.local/state
export XDG_CACHE_HOME = $HOME/.cache

So, has anyone moved their Emacs config to respect the XDG conventions already? How smooth was the process for you?

P.S. If you’re curious to learn more about how Emacs’s configuration discover process I’d suggest reading this chapter of the Emacs Manual. Good stuff!

  1. Check out the XDG base directory specification for more details. 

-1:-- Emacs and XDG sitting on a tree (Post Emacs Redux)--L0--C0--2025-01-12T16:34:00.000Z

Emacs Redux: Ensure all packages are installed by default with use-package

I’m quite fond of use-package and I’ve organized my personal Emacs setup around it for a while now. One thing that I don’t like very much is that by default almost I have to add :ensure t to almost every use-package block, as I want all external packages to be installed if they are not present. That’s quite handy when I’m setting up Emacs on a new computer.1 Think something like:

(use-package zenburn-theme
  :ensure t
  :config
  (load-theme 'zenburn t))

Not a big deal for a few packages, but kind of annoying if you have 50+ packages in your init.el. There’s a pretty simple solution to this problem, though. Just add the following bit of configuration to your Emacs setup:

(setq use-package-always-ensure t)

Now instead of specifying :ensure t you can specify :ensure nil for the packages you don’t want to install automatically. Note that for built-packages (e.g. dired) it doesn’t really matter if a package is using :ensure t or :ensure nil.2

Which approach do you prefer? Are you the type of person who ensures every package is installed when absent or not? Why do you prefer one approach over the other?

  1. I know that a lot of people object to this approach, as you’re not sure what versions of the packages you’d get as package.el is a bit primitive compared to something like Ruby’s Bundler or Node’s npm, but in practice I’ve rarely had issues with my approach and it has saved me a great deal of time. 

  2. package-installed-p will return t for those. 

-1:-- Ensure all packages are installed by default with use-package (Post Emacs Redux)--L0--C0--2025-01-12T16:12:00.000Z

Irreal: Class Notes In Emacs: Teachers’ Edition

I’ve written in the past on my astonishment about people who are able to take mathematics class notes in LaTeX fast enough to keep up with the instructor: 1, 2, and 3. Some people who do this use Emacs, some use Vim. In either case, it’s breathtaking to watch them.

Now Matt Maguire takes a look at it from the other side: producing class notes for students. Speed is not an issue here, of course, but getting a workable system involves more than just writing some LaTeX formulas and publishing them.

Maguire has a specific format in mind and has been producing notes in it for some time directly with LaTeX. But as an Emacs user, we wanted to leverage Org mode to simplify the production of the notes.

The nice thing about his exposition is that it’s widely applicable. You may not be a teacher or you may not want to produce notes involving mathematics but if you want to use anything other than the standard LaTeX article class, you have some work to do. Maguire’s post tells you how to set up another class and other custom settings you need for your unique environment.

All this may seem like a lot of work but you only have to do it once. After that, you just write whatever it is in Org as usual. The post demonstrates one of the (many) strengths of Org mode.

Update [2025-01-12 Sun 11:22]: Added link to Maguire’s post.

-1:-- Class Notes In Emacs: Teachers’ Edition (Post Irreal)--L0--C0--2025-01-12T16:02:27.000Z

Sacha Chua: Treemap visualization of an Org Mode file

[2025-01-12 Sun]: u/dr-timeous posted a treemap_org.py · GitHub that makes a coloured treemap that displays the body on hover. (Reddit) Also, I think librsvg doesn't support wrapped text, so that might mean manually wrapping if I want to figure out the kind of text density that webtreemap has.

One of the challenges with digital notes is that it's hard to get a sense of volume, of mass, of accumulation. Especially with Org Mode, everything gets folded away so neatly and I can jump around so readily with C-c j (org-goto) or C-u C-c C-w (org-refile) that I often don't stumble across the sorts of things I might encounter in a physical notebook.

Treemaps are a quick way to visualize hierarchical data using nested rectangles or squares, giving a sense of relative sizes. I was curious about what my main organizer.org file would look like as a treemap, so I wrote some code to transform it into the kind of data that https://github.com/danvk/webtreemap wants as input. webtreemap creates an HTML file that uses Javascript to let me click on nodes to navigate within them.

For this treemap prototype, I used org-map-entries to go over all the headings and make a report with the outline path and the size of the heading. To keep the tree visualization manageable, I excluded done/cancelled tasks and archived headings. I also wanted to exclude some headings from the visualization, like the way my Parenting subheading has lots of personal information underneath it. I added a :notree: tag to indicate that a tree should not be included.

Screencast of exploring a treemap

Reflections

2025-01-11_19-54-47.png
Figure 1: Screenshot of the treemap for my organizer.org

The video and the screenshot above show the treemap for my main Org Mode file, organizer.org. I feel like the treemap makes it easier to see projects and clusters where I'd accumulated notes, both in terms of length and quantity. (I've omitted some trees like "Parenting" which take up a fairly large chunk of space.)

To no one's surprise, Emacs takes up a large part of my notes and ideas. =)

When I look at this treemap, I notice a bunch of nodes I need to mark as DONE or CANCELLED because I forgot to update my organizer.org. That usually happens when I come up with an idea, don't remember that I'd come up with it before, put it in my inbox.org file, and do it from there or from the organizer.org location I've refiled it to without bumping into the first idea. Once in a blue moon, I go through my whole organizer.org file and clean out the cruft. Maybe a treemap like this will make it easier to quickly scan things.

Interestingly, "Explore AI" takes up a disproportionately large chunk of my "Inactive Projects" visualization, even though I spend more time and attention on other things. Large language models make it easy to generate a lot of text, but I haven't really done the work to process those. I've also collected a lot of links that I haven't done much with.

It might be neat to filter the headings by timestamp so that I can see things I've touched in the last 6 months.

Hmm, looking at this treemap reminds me that I've got "organizer.org/Areas/Ideas for things to do with focused time/Writing/", which probably should get moved to the posts.org file that I tend to use for drafts. Let's take look at the treemap for that file. (Updated: cleared it out!)

2025-01-11_20-10-18.png
Figure 2: Drafts in my posts.org

Unlike my organizer.org file, my posts.org file tends to be fairly flat in terms of hierarchy. It's just a staging ground for ideas before I put them on my blog. I usually try to keep posts short, but a few of my posts have sub-headings. Since the treemap makes it easy to see nodes that are larger or more complex, that could be a good nudge to focus on getting those out the door. Looking at this treemap reminds me that I've got a bunch of EmacsConf posts that I want to finish so that I can document more of our processes and tools.

2025-01-11_14-52-28.png
Figure 3: Treemap of my inbox

My inbox.org is pretty flat too, since it's really just captured top-level notes that I'll either mark as done or move somewhere else (usually organizer.org). Because the treemap visualization tool uses / as a path separator, the treemap groups headings that are plain URLs together, grouped by domain and path.

2025-01-12_08-30-44.png
Figure 4: Treemap of my Emacs configuration

My Emacs configuration is organized as a hierarchy. I usually embed the explanatory blog posts in it, which explains the larger nodes. I like how the treemap makes it easy to see the major components of my configuration and where I might have a lot of notes/custom code. For example, my config has a surprising amount to do with multimedia considering Emacs is a text editor, and that's mostly because I like to tinker with my workflow for sketchnotes and subtitles. This treemap would be interesting to colour based on whether something has been described in a blog post, and it would be great to link the nodes in a published SVG to the blog post URLs. That way, I can more easily spot things that might be fun to write about.

The code

This assumes https://github.com/danvk/webtreemap is installed with npm install -g webtreemap-cli.

(defvar my-org-treemap-temp-file "~/treemap.html") ; Firefox inside Snap can't access /tmp
(defvar my-org-treemap-command "treemap" "Executable to generate a treemap.")

(defun my-org-treemap-include-p (node)
  (not (or (eq (org-element-property :todo-type node) 'done)
           (member "notree" (org-element-property :tags node))
           (org-element-property-inherited :archivedp node 'with-self))))

(defun my-org-treemap-data (node &optional path)
  "Output the size of headings underneath this one."
  (let ((sub
         (apply
          'append
          (org-element-map
              (org-element-contents node)
              '(headline)
            (lambda (child)
              (if (my-org-treemap-include-p child)
                  (my-org-treemap-data
                   child
                   (append path
                           (list
                            (org-no-properties
                             (org-element-property :raw-value node)))))
                (list
                 (list
                  (-
                   (org-element-end child)
                   (org-element-begin child))
                  (string-join
                   (cdr
                    (append path
                            (list
                             (org-no-properties
                              (org-element-property :raw-value node))
                             (org-no-properties
                              (org-element-property :raw-value child)))))
                   "/")
                  nil))))
            nil nil 'headline))))
    (append
     (list
      (list
       (-
        (org-element-end node)
        (org-element-begin node)
        (apply '+ (mapcar 'car sub))
        )
       (string-join
        (cdr
         (append path
                 (list
                  (org-no-properties (org-element-property :raw-value node)))))
        "/")
       (my-org-treemap-include-p node)))
     sub)))

(defun my-org-treemap ()
  "Generate a treemap."
  (interactive)
  (save-excursion
    (goto-char (point-min))
    (let ((file (expand-file-name (expand-file-name my-org-treemap-temp-file)))
          (data (cdr (my-org-treemap-data (org-element-parse-buffer)))))
      (with-temp-file file
        (call-process-region
         (mapconcat
          (lambda (entry)
            (if (elt entry 2)
                (format "%d %s\n" (car entry)
                        (replace-regexp-in-string org-link-bracket-re "\\2" (cadr entry)))
              ""))
          data
          "")
         nil
         my-org-treemap-command nil t t))
      (browse-url (concat "file://" (expand-file-name my-org-treemap-temp-file))))))

There's another treemap visualization tool that can produce squarified treemaps as coloured SVGs, so that style might be interesting to explore too.

Next steps

I think there's some value in being able to look at and think about my outline headings with a sense of scale. I can imagine a command that shows the treemap for the current subtree and allows people to click on a node to jump to it (or maybe shift-click to mark something for bulk action), or one that shows subtrees summing up :EFFORT: estimates or maybe clock times from the logbook, or one limited by a timestamp range, or one that highlights matching entries as you type in a query, or one that visualizes s-exps or JSON or project files or test coverage.

It would probably be more helpful if the treemap were in Emacs itself, so I could quickly jump to the Org nodes and read more or mark something as done when I notice it. boxy-headings uses text to show the spatial relationships of nested headings, which is neat but probably not up to handling this kind of information density. Emacs can also display SVG images in a buffer, animate them, and handle mouse-clicks, so it could be interesting to implement a general treemap visualization which could then be used for all sorts of things like disk space usage, files in project modules, etc. SVGs would probably be a better fit for this because that allows increased text density and more layout flexibility.

It would be useful to browse the treemap within Emacs, export it as an SVG so that I can include it in a webpage or blog post, and add some Javascript for web-based navigation.

The Emacs community being what it is (which is awesome!), I wouldn't be surprised if someone's already figured it out. Since a quick search for treemap in the package archives and various places doesn't seem to turn anything up, I thought I'd share these quick experiments in case they resonate with other people. I guess I (or someone) could figure out the squarified treemapping algorithm or the ordered treemap algorithm in Emacs Lisp, and then we can see what we can do with it.

I've also thought about other visualizations that can help me see my Org files a different way. Network graphs are pretty popular among the org-roam crew because org-roam-ui makes them. Aside from a few process checklists that link to headings that go into step-by-step detail and things that are meant to graph connections between concepts, most of my Org Mode notes don't intentionally link to other Org Mode notes. (There are also a bunch of random org-capture context annotations I haven't bothered removing.) I tend to link to my public blog posts, sketches, and source code rather than to other headings, so that's a layer of indirection that I'd have to custom-code. Treemaps might be a good start, though, as they take advantage of the built-in hierarchy. Hmm…

View org source for this post
-1:-- Treemap visualization of an Org Mode file (Post Sacha Chua)--L0--C0--2025-01-12T13:39:55.000Z

Bozhidar Batsov: macOS No Longer Ships with Emacs

While I was setting up my new mac mini yesterday I noticed something interesting - Apple have stopped shipping the ancient Emacs 22.1 with macOS! As I was mostly using Windows + WSL2 in the past 5 years I had missed the exact moment when this happened, but after some digging I discovered that the change was first made in macOS 10.15 (“Catalina”), which was released in October 2019.

For me that’s a very welcome change, as the ancient version of Emacs that Apple was shipping in the past was only causing issues for Emacs users. (occasionally people would accidentally run it, instead of whatever Emacs they installed by themselves and they’d wonder what went wrong) Still, I got curious about would things:

  1. Why didn’t Apple just update Emacs?
  2. What prompted them to finally drop the ancient Emacs?

The answer to the first question turned out to be Apple being opposed to using anything licensed under GPL v3, and so it happens that Emacs 22.1 was the last Emacs version licensed under GPL v2.1 A former Apple employee shared the following in a comment on HackerNews:

When I was at Apple, they discontinued bundling the command line version of emacs, and they sent a long internal email stating why. The stated reason was that any newer emacs would suffer from license incompatibility with macOS, and they didn’t see the point of supporting a 10 year old version that the FSF doesn’t even support when, if someone really wants emacs, it’s just a brew install emacs away.

As for the timing - I guess this was just a long time coming.

One curious tidbit - Apple did provide a sort of out-of-the-box replacement for Emacs. That’s the mg editor, which is something like a “Micro Emacs” in a way. I think it’s a great alternative to the likes of vi and nano when you need to do simple edits from the command-line, so you might consider adding something like this to your shell setup:

export EDITOR=mg

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

  1. For the same reason macOS switched from Bash to Zsh in the very same macOS 10.15 (“Catalina”). And similarly to the situation with Emacs - for a very long time Apple were shipping an ancient version of Bash with macOS, the last one that was released under GPL v2. Obviously almost noone was willing to use it. 

-1:-- macOS No Longer Ships with Emacs (Post Bozhidar Batsov)--L0--C0--2025-01-12T09:03:00.000Z

Irreal: Converting Markdown to Org

Charles Choi has a handy tip on converting from Mardown to Org. My first thought was that I don’t care about this because I write everything in Org Mode and never use Markdown. But then it occurred to me that the same is doubtless true of Choi as well and that someday I’m going to want to import a Markdown document into Org.

Choi’s basic process is to cut and paste the Markdown document into Emacs and then use Pandoc to convert from Markdown to Org. But there’s a problem. Pandoc will wrap lines while Choi likes to keep paragraphs as a single line. I do that too because it’s how visual line mode works. The excellent Pandoc has us covered, of course. You need only specify --wrap=preserve to get the desired behavior.

With that bit of wisdom, Choi gives us a handy function that will convert the Markdown in the current region to Org. It’s probably worthwhile adding his short function to your init.el just so you’ll have it when the need arrives. If you don’t like cluttering up your init.el with things you’re not actively using, at least bookmark his post so you’ll know what to do when the need arises.

-1:-- Converting Markdown to Org (Post Irreal)--L0--C0--2025-01-11T16:09:50.000Z

Sacha Chua: Automatically correcting phrasing and misrecognized words in speech-to-text captions by using a script

I usually write my scripts with phrases that could be turned into the subtitles. I figured I might as well combine that information with the WhisperX transcripts which I use to cut out my false starts and oopses. To do that, I use the string-distance function, which calculates how similar strings are, based on the Levenshtein [distance] algorithm. If I take each line of the script and compare it with the list of words in the transcription, I can add one transcribed word at a time, until I find the number with the minimum distance from my current script phrase. This lets me approximately match strings despite misrecognized words. I use oopses to signal mistakes. When I detect those, I look for the previous script line that is closest to the words I restart with. I can then skip the previous lines automatically. When the script and the transcript are close, I can automatically correct the words. If not, I can use comments to easily compare them at that point. Even though I haven't optimized anything, it runs well enough for my short videos. With these subtitles as a base, I can get timestamps with subed-align and then there's just the matter of tweaking the times and adding the visuals.

Text from sketch

Matching a script with a transcript 2025-01-09-01

  • script
  • record on my phone
  • WhisperX transcript (with false starts and recognition errors)

My current implementation is totally unoptimized (n²) but it's fine for short videos.

Process:

  • While there are transcript words to process
    • Find the script line that has the minimum distance to the words left in the transcript. restart after oopses
  • Script
  • Transcript: min. distance between script phrase & transcript
  • Restarting after oops: find script phrase with minimum distance
  • Ex. script phrase: The Emacs text editor
  • Transcript: The Emax text editor is a…
  • Bar graph of distance decreasing, and then increasing again
  • Minimum distance
  • Oops?
    • N: Use transcript words, or diff > threshold?
      • Y: Add script words as comment
      • N: Correct minor errors
    • Y: Mark caption for skipping and look for the previous script line with minimum distance.

Result:

  • Untimed captions with comments
  • Aeneas
  • Timed captions for editing

This means I can edit a nicely-split, mostly-corrected file.

I've included the links to various files below so you can get a sense of how it works. Let's focus on an excerpt from the middle of my script file.

it runs well enough for my short videos.
With these subtitles as a base,
I can get timestamps with subed-align

When I call WhisperX with large-v2 as the model and --max_line_width 50 --segment_resolution chunk --max_line_count 1 as the options, it produces these captions corresponding to that part of the script.

01:25.087 --> 01:29.069
runs well enough for my short videos. With these subtitles

01:29.649 --> 01:32.431
as a base, I can get... Oops. With these subtitles as a base, I

01:33.939 --> 01:41.205
can get timestamps with subedeline, and then there's just

Running subed-word-data-use-script-file results in a VTT file containing this excerpt:

00:00:00.000 --> 00:00:00.000
it runs well enough for my short videos.

NOTE #+SKIP

00:00:00.000 --> 00:00:00.000
With these subtitles as a base,

NOTE #+SKIP

00:00:00.000 --> 00:00:00.000
I can get... Oops.

00:00:00.000 --> 00:00:00.000
With these subtitles as a base,

NOTE
#+TRANSCRIPT: I can get timestamps with subedeline,
#+DISTANCE: 0.14

00:00:00.000 --> 00:00:00.000
I can get timestamps with subed-align

There are no timestamps yet, but subed-align can add them. Because subed-align uses the Aeneas forced alignment tool to figure out timestamps by lining up waveforms for speech-synthesized text with the recorded audio, it's important to keep the false starts in the subtitle file. Once subed-align has filled in the timestamps and I've tweaked the timestamps by using the waveforms, I can use subed-record to create an audio file that omits the subtitles that have #+SKIP comments.

The code is available as subed-word-data-use-script-file in subed-word-data.el. I haven't released a new version of subed.el yet, but you can get it from the repository.

In addition to making my editing workflow a little more convenient, I think it might also come in handy for applying the segmentation from tools like sub-seg or lachesis to captions that might already have been edited by volunteers. (I got sub-seg working on my system, but I haven't figured out lachesis.) If I call subed-word-data-use-script-file with the universal prefix arg C-u, it should set keep-transcript-words to true and keep any corrections we've already made to the caption text while still approximately matching and using the other file's segments. Neatly-segmented captions might be more pleasant to read and may require less cognitive load.

There's probably some kind of fancy Python project that already does this kind of false start identification and script reconciliation. I just did it in Emacs Lisp because that was handy and because that way, I can make it part of subed. If you know of a more robust or full-featured approach, please let me know!

View org source for this post
-1:-- Automatically correcting phrasing and misrecognized words in speech-to-text captions by using a script (Post Sacha Chua)--L0--C0--2025-01-11T14:11:40.000Z

Lars Ingebrigtsen: WordPress Statistics & Me

I’m using Jetpack Stats to see what’s happenin’ on mah blogs, and it’s never been whatchamacall “great”, but it’s been OK. It’s OK. It displays all the pertinent info in one pretty compact screen: You don’t have to scroll around.

But they’ve been threatening to remove the “classic”, compact stats for years, and yesterday they apparently did, because I was faced with a page like the above.

As an example, let’s look at Moving Pictures, my auxiliary movie-watching logging blog (not really meant to be read by anybody, really).

I clicked the dreaded “switch to the new stats”, and…

Yeah, OK…

OK, “click” the ad away, and…

That’s the entire screen, and it shows the data for the last seven days!? OK, perhaps you can click a single day (because that’s what I’m interested in)…

*sigh*

So if I want to see today’s info, like in the old interface, I can’t see any previous days. In addition, it’s negging me: Down 87%?!? Yeah, because the day “ticked over” only a few hours earlier at this point, so any normal day will be “down” until it’s over. Why isn’t it showing me the previous 24 hours instead of the day so far, for heaven’s sake?

(I guess if you don’t have a time-series database it’s just easier and faster to just “bin” the data per day instead of having to store all the visits and do a select count(*)... thing. And WordPress very much doesn’t use a time-series database, I think.)

But let’s look at the rest of the page.

Well, that’s nice and spacious… The thing that’s interesting is really 1) the visitor tally, and 2) the referrers (useful if you have sudden explosions in visitor tallies *cough* Hackernews *cough*), and 3) clicks (to see whether something I’ve linked got a lot of traffic).

Obviously, none of that is really of interest here, since that site has no visitors, really, but bear with me.

So scrolling down, the next page has an upsell block, and then the clicks. The “Search” box is never useful, since (virtually) every search engine wisely censors search terms, and there are no videos to track.

So scroll down more, and we get more upsells, and…

… finally some more upsells.

So that’s the new stats page: On this laptop, you have to scroll three screens to glean the “useful” information, and that’s obviously useless.

This page is mostly here to upsell, I guess? Stats is the primary reason people install Jetpack Stats, so it must be seen as the primary channel for upselling people more services from Automattic, so they put all the ads on the stats page? 🤷

But even if you’ve bought everything Automattic has to sell here, the page is still pretty useless, unless you enjoy scrolling (or has a very tall screen).

This sort of stuff pisses me off, so I’m finally doing what I should have done a decade ago: Write my own stats thing, and one that doesn’t involve looking a web page.

Here’s some design points:

  1. I want all the information available at a glance.
  2. I want “today’s” data to be on a 24 hour basis, so that I look at
    how many visits I got over the last 24 hours, and not 2 hour’s worth of data if I’m looking at it at two in the morning.

  3. I want to look at the data across all my blogs at the same time.
  4. I want it to update automatically without having to wait for a reload.

So, obviously, I have to write a plugin to stash the data. And no, one can’t just parse the Apache log, because there’s too much noise there. What Jetpack Stats does (an what almost all stats libraries do) is to trigger the data collection from Javascript, because this weeds out 99.9% of the bots. (Not 99.9% of the bot traffic, though — the major web spiders (like Googlebot) will request (some) JS-triggered requests, but these are mostly well-behaved and have User-Agent strings that are easy to filter out, and there’s only a couple dozen of these.)

So this plugin does the same: An async fetch after the page has been fully rendered, triggered from Javascript. (Yes, this loses hits from people who don’t use Javascript, but those people probably don’t want to be counted anyway.) (And an advantage of using self-hosted stats is that Adblock won’t block the stats. Probably. And no cookies!)

In addition, it’s fun to see what people click, so (like Jetpack Stats), I’m instrumenting all <a> links, so when people click on something, that’s registered, too.

And while I’m at it, I’m also instrumenting <video> elements, and also Lyte/Youtube embeds.

And that’s all the plugin does — it’s a few dozen lines of JS/PHP code. It’s really quite easy and nice working inside the WordPress framework…

Then I just have an API that dumps out this collected data, and the rest happens… in Emacs, of course.

Tada! All the data available in one window, without any scrolling.

But after using the new interface for a couple days, I’m starting to wonder whether the reason they’re using the view-per-date as the most prominent indicator in interfaces like this not because of technical laziness but because of psychology. Let me bloviate:

When the date flips, you’ve confronted with the above: 100% down from the previous day! *gasp* So it’s negging you and making you disappointed. But every time afterwards you reload the page, the number goes up and everybody loves it when the number goes up, right? So doing it this way may be addicting people to the stats: First it gives you a negative shock, and then it rewards you for every reload. Dopamine, man! A 24 hour rolling view doesn’t give you any of that: The number will stay mostly the same, decreasing or increasing a bit each time, and that’s no fun now is it?

IT”S ALL A CONSPIRACY!

Anyway, this took me two days to do, and I ended up with a solution that is (for my use case) far superior to what WordPress has ever had. Your mileage will vary, of course — everybody’s got different preferences. But the reason most people install Jetpack is because of the stats functionality, so you’d think Automattic would be leery at annoying people too much. The layout changes (and attendant slow page load times) were what pushed me over the edge and make me implement my own solution.

When it’s this easy to implement something like this, Automattic is, in my opinion, shooting themselves in the foot by forcing people into changes they don’t want. I mean, absolutely nobody except me is going to use what I hacked together, but when it’s this easy, surely somebody else will write a plugin called Just The Stats And Nothing More or something?

(Yes, there are a lot of other plugins that say they’re good for stats, but from what I can tell by a cursory look-through, most of them seem to be aimed at scum. Sorry, I mean marketers, and many of them seem sleazy and filled with upsells and nobody wants that.)

I’ve got one blog hosted at WordPress.com, but this sort of stuff obviously won’t work there (they don’t allow people installing oddball plugins willy nilly, wisely enough), so I guess I’ll have to move it to self hosting, too. The only reason I haven’t done so before is that I thought it would be nice to continue to pay Automattic some money since they’re doing a lot of good work, but… (The recent meltdowns at Automattic don’t help here.)

Having all this information available in a local SQLite database means that I can add more commands that Jetpack Stats doesn’t have. For instance, there’s three Hacker News referrers? What’s that about, then?

It’s nice to have the data.

And, er, I couldn’t come up with a name when I started typing away, so it ended up being called “bang”, named after Gilbert Hernandez’s Bang & Inez. It’s on Microsoft Github, and I’m already regretting the name. Sorry!

[Edit: I renamed it to “wse”, as in “WordPress Statistics for Emacs”. I ended up with buffer names like “*Bang Date*” which was just too hilarious!]

While I’m bloviating away…

Those Unicode people are pretty smart. I guess everybody knows that those flags are composed by two Unicode characters, so the US flag is 🇺 and then 🇸 and when you put them next to each other, the rendering engine renders it as 🇺🇸 instead? That’s already really clever, but I’ve never looked into what code points like 🇺 are. That is, how they’re arranged, and whether you need some kind of table or something to make the mapping. But no!

You just add 0x1f1a5 to the ASCII code and Bob’s your sister’s brother, possibly.

… well, I thought it was neat. What! Ever!

-1:-- WordPress Statistics & Me (Post Lars Ingebrigtsen)--L0--C0--2025-01-11T09:28:41.000Z

Protesilaos Stavrou: Emacs: denote-sequence.el will also support alphanumeric sequences

In a video from 2025-01-03 (sequence notes with Denote (denote-sequence.el)), I introduced the new optional extension for Denote, which streamlines the creation of file names with an inherent hierarchical relationship. This is expressed as a numeric sequence, where each level of depth is delimited by the equals sign. So 1=1=2 refers to the second child of the first child of the parent note 1.

A popular sequencing scheme is to combine numbers with letters (Luhmann-style folgezettel). This alphanumeric expression is more compact, though it might also be a bit harder to reason about. For example, 1a2 is the same as 1=1=2, which looks clean, but it gets tricky once you reach 1za5zx which is equivalent to 1=27=5=50.

Unlike the numeric scheme where there is an explicit depth delimiter (the =), the alphanumeric scheme establishes depth by alternating between numbers and letters. Thus, 1a2 and 1=1=2 are both three levels of depth.

I am in the process of giving users the option. They will be able to pick their sequencing scheme and then produce notes, re-parent them, and so on, with Denote taking care to use/generate the correct sequence each time. In principle, the two schemes are interoperable and there will be relevant commands to switch between them.

[ UPDATE 2025-01-12 11:25 +0200: Now merged into main and deleted the feature branch. ]

Below is the commit I worked on today. For now it lives in the alphanumeric-sequence-extension branch: https://github.com/protesilaos/denote/tree/alphanumeric-sequence-extension. I will merge it into main as soon as I am done with the user-facing parts, which I will probably do tomorrow or early next week. If you are interested to try out what is now available, please check the source code and let me know your thoughts.

commit 43bd30e6ebd9e948a390d11bc3ec84cf80e74576
Author: Protesilaos Stavrou <info@protesilaos.com>
Date:   Sat Jan 11 19:37:56 2025 +0200

  Make the groundwork for alphanumeric (Luhmann) sequences in denote-sequence.el

  The idea is to support both numeric and alphanumeric sequencing
  schemes, as documented in the new user option 'denote-sequence-scheme'.

  We now have the tools to correctly split, join, increment, and convert
  input accordingly, such that we can, for example, accurately produce a
  child of sequence "1a2" (we could already do that for numeric
  sequences).

  What is covered herein is just the groundwork. I still need to extend
  the helper functions which directly support the creation of new
  parent, child, or sibling sequences. While this still is a lot of
  work, the hard part is now done.

 denote-sequence.el   | 338 +++++++++++++++++++++++++++++++++++++++------------
 tests/denote-test.el |  34 +++++-
 2 files changed, 291 insertions(+), 81 deletions(-)

About Denote

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

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

-1:-- Emacs: denote-sequence.el will also support alphanumeric sequences (Post Protesilaos Stavrou)--L0--C0--2025-01-11T00:00:00.000Z

Charles Choi: Converting a Markdown Region to Org Revisited

On more than one occasion I’ve found myself needing to convert Markdown formatted text to Org using this workflow:

  1. Copy Markdown formatted text (usually from a web browser) into the kill ring.
  2. Paste above text into an Org buffer.
  3. Manually reformat said text to Org.

Folks who have pandoc installed can automate step 3 above with the Elisp function quoted below from the Stack Exchange post “Convert region/subtree from Markdown to org”.

1
2
3
4
(defun my-md-to-org-region (start end)
  "Convert region from markdown to org"
  (interactive "r")
  (shell-command-on-region start end "pandoc -f markdown -t org" t t))

So just copy the above function into your config and call it a day, no?

Not quite. If left unspecified, pandoc will automatically wrap long lines 😞. I prefer keeping a paragraph as a single long line in Org.

To keep a paragraph as a long line, use the pandoc argument --wrap=preserve.

Listed below is an amended version of the above routine.

1
2
3
4
5
6
7
8
9
(defun cc/markdown-to-org-region (start end)
  "Convert Markdown formatted text in region (START, END) to Org.

This command requires that pandoc (man page `pandoc(1)') be
installed."
  (interactive "r")
  (shell-command-on-region
   start end
   "pandoc -f markdown -t org --wrap=preserve" t t))

Regardless of your preference, if this is news to you and seems useful, give it a try.

References

-1:-- Converting a Markdown Region to Org Revisited (Post Charles Choi)--L0--C0--2025-01-10T21:00:00.000Z

Sacha Chua: Org Babel, Mermaid JS, and fixing "Failed to launch the browser process" on Ubuntu 24

Mermaid makes pretty diagrams from text. It's Javascript-based, so the command-line tool (mmdc) uses Puppeteer to get the results of evaluating the diagram in the browser. I was running into some errors trying to get it to work from Org Mode over ob-mermaid on Ubuntu 24, since apparently AppArmor restricts Puppeteer. (Error: Failed to launch the browser process! · Issue #730 · mermaid-js/mermaid-cli).

I put together a pull request to modify ob-mermaid-cli-path so that it doesn't get quoted and can therefore have the aa-exec command needed to work around that. With that modified org-babel-execute:mermaid, I can then configure ob-mermaid like this:

(use-package ob-mermaid
  :load-path "~/vendor/ob-mermaid")
;; I need to override this so that the executable isn't quoted
(setq ob-mermaid-cli-path "aa-exec --profile chrome mmdc -c ~/.config/mermaid/config.json")

I also ran into a problem where the library that Emacs uses to display SVGs could not handle the foreignObject elements used for the labels. mermaid missing text in svg · Issue #112 · mermaid-js/mermaid-cli . Using the following ~/.config/mermaid/config.json fixed it, and I put the option in the ob-mermaid-cli-path above so that it always gets loaded.

{
  "flowchart": {
    "useMaxWidth": false,
    "htmlLabels": false
  }
}

Here's sample Mermaid markup and the file it creates:

mindmap
  root((test))
    Node 1
      Node 1A
      Node 1B
    Node 2
    Node 3
testNode 1Node 2Node 3Node 1ANode 1B

Now I can see the labeled diagrams inside Emacs, too.

This is part of my Emacs configuration.
View org source for this post
-1:-- Org Babel, Mermaid JS, and fixing "Failed to launch the browser process" on Ubuntu 24 (Post Sacha Chua)--L0--C0--2025-01-10T19:16:20.000Z

Irreal: 🥩 Red Meat Friday: Notepad Vs. Emacs

Of all the pretenders to the Emacs crown, notepad strikes me as the most ludicrous. I am not, apparently, the only one. This cartoon captures my feelings exactly.

The Emacs haters were, of course, out in force trying to deflect your attention from the essential truth but we keepers of the faith were not deceived.

-1:-- 🥩 Red Meat Friday: Notepad Vs. Emacs (Post Irreal)--L0--C0--2025-01-10T15:36:20.000Z

Listful Andrew: Sparkly Stats — Stats and sparks from daily data (Emacs package)

Give Sparkly Stats a date range and the values for each date and it gives you statistics and sparks.
-1:-- Sparkly Stats — Stats and sparks from daily data (Emacs package) (Post Listful Andrew)--L0--C0--2025-01-10T10:30:00.000Z

Irreal: Using Ediff On Regions

One of the Emacs powerhouses is Ediff. Every time I think I’ve found a better way of comparing files, it turns out that I just didn’t understand Ediff well enough. It can seem intimidating but as Prot informed me it really pretty simple if you ignore all the complications. I took his advice and can only agree with his assessment.

Now James Dyer has his own take on Ediff. His particular use case is comparing regions rather than whole files. It turns out that there are two functions, ediff-regions-linewise and ediff-regions-wordwise that will use Ediff to compare two regions in the usual Ediff way.

The documentation recommends that you use ediff-regions-wordwise for small regions and ediff-regions-linewise for larger regions. In either case, once you set up the regions to be compared you use Ediff in the usual way. Take a look at Dyer’s post to see how to set up the regions. It’s straightforward and easy to remember once you use it a couple of times.

It’s a nice post and well worth reading even if you don’t need to compare regions right now. You probably will in the future and even if you don’t remember the details, you’ll know what to look for when you do.

-1:-- Using Ediff On Regions (Post Irreal)--L0--C0--2025-01-09T16:01:49.000Z

Sacha Chua: #YayEmacs 9: Trimming/adding silences to get to a target; subed-record-sum-time

New in this video: subed-record-sum-time, #+PAD_LEFT and #+PAD_RIGHT

I like the constraints of a one-minute video, so I added a subed-record-sum-time command. That way, when I edit the video using Emacs, I can check how long the result will be. First, I split the subtitles, align it with the audio to fix the timestamps, and double check the times. Then I can skip my oopses. Sometimes WhisperX doesn't catch them, so I also look at waveforms and characters per second. I already talk quickly, so I'm not going to speed that up but I can trim the pauses in between phrases which is easy to do with waveforms. Sometimes, after reviewing a draft, I realize I need a little more time. If the original audio has some silence, I can just copy and paste it. If not, I can pad left or pad right to add some silence. I can try the flow of some sections and compile the video when I'm ready. Emacs can do almost anything. Yay Emacs!

You can watch this on YouTube, download the video, or download the audio.

Play by play:

  • I like the constraints of a one-minute video, so I added a subed-record-sum-time command. That way, when I edit the video using Emacs, I can check how long the result will be.
    • subed-record uses subtitles and directives in comments in a VTT subtitle file to edit audio and video. subed-record-sum-time calculates the resulting duration and displays it in the minibuffer.
  • First, I split the subtitles, align it with the audio to fix the timestamps, and double check the times.
    • I'm experimenting with an algorithmic way to combine the breaks from my script with the text from the transcript. subed-align calls the aeneas forced alignment tool to match up the text with the timestamps. I use subed-waveform-show-all to show all the waveforms.
  • Then I can skip my oopses.
    • Adding a NOTE #+SKIP comment before a subtitle makes subed-record-compile-video and subed-record-compile-flow skip that part of the audio.
  • Sometimes WhisperX doesn't catch them,
    • WhisperX sometimes doesn't transcribe my false starts if I repeat things quickly.
  • so I also look at waveforms
    • subed-waveform-show-all adds waveforms for all the subtitles. If I notice there's a pause or a repeated shape in the waveform, or if I listen and notice the repetition, I can confirm by middle-clicking on the waveform to sample part of it.
  • and characters per second.
    • Low characters per second is sometimes a sign that the timestamps are incorrect or there's a repetition that wasn't transcribed.
  • I already talk quickly, so I'm not going to speed that up
    • Also, I already sound like a chipmunk; mechanically speeding up my recording to fit in a certain time will make that worse =)
  • but I can trim the pauses in between phrases which is easy to do with waveforms.
    • left-click to set the start, right-click to set the stop. If I want to adjust the previous/next one at the same time, I would use shift-left-click or shift-right-click, but here I want to skip the gaps between phrases, so I adjust the current subtitle without making the previous/next one longer.
  • Sometimes, after reviewing a draft, I realize I need a little more time.
    • I can specify visuals like a video, animated GIF, or an image by adding a [[file:...]] link in the comment for a subtitle. That visual will be used until the next visual is specified in a comment on a different subtitle. subed-record-compile-video can automatically speed up video clips to fit in the time for the current audio segment, which is the set of subtitles before the next visual is defined. After I compile and review the video, sometimes I notice that something goes by too quickly.
  • If the original audio has some silence, I can just copy and paste it.
    • This can sometimes feel more natural than adding in complete silence.
  • If not, I can pad left or pad right to add some silence.
    • I added a new feature so that I could specify something like #+PAD_RIGHT: 1.5 in a comment to add 1.5 seconds of silence after the audio specified by that subtitle.
  • I can try the flow of some sections
    • I can select a region and then use M-x subed-record-compile-try-flow to play the audio or C-u M-x subed-record-compile-try-flow to play the audio+video for that region.
  • and compile the video when I'm ready.
    • subed-record-compile-video compiles the video to the file specified in #+OUTPUT: filename. ffmpeg is very arcane, so I'm glad I can simplify my use of it with Emacs Lisp.
  • Emacs can do almost anything. Yay Emacs!
    • Non-linear audio and video editing is actually pretty fun in a text editor, especially when I can just use M-x vundo to navigate my undo history.

Links:

Related:

View org source for this post
-1:-- #YayEmacs 9: Trimming/adding silences to get to a target; subed-record-sum-time (Post Sacha Chua)--L0--C0--2025-01-09T15:24:29.000Z

Listful Andrew: Sparkly — Create multiline sparks ▁▂▆▄▃▇▅ (Emacs package)

With Sparkly you can transform numbers into multiline sparks, which can help you quickly visualize data. You have control over the number of lines to use.
-1:-- Sparkly — Create multiline sparks ▁▂▆▄▃▇▅ (Emacs package) (Post Listful Andrew)--L0--C0--2025-01-09T08:00:00.000Z

Protesilaos Stavrou: Emacs: custom Oxford University calendar weeks

Over at Oxford University they use their own calendaring scheme to label the terms and weeks of their school year. Why? Because they can! And why would some guy in the mountains of Cyprus care? Because it’s fun to write Emacs Lisp!

The academic year is divided into three periods of teaching and three of vacation. The latter have no special names, while the former are called “Michaelmas”, “Hilary”, and “Trinity”. Each teaching term consists of a few weeks whose numbering starts from 1.

With some custom code, we can configure the Emacs M-x calendar to tell us (i) which term we are in for the current month of the Oxford academic year, if any, (ii) which term and week number we are in, if any, and (iii) which is the standard week number.

The generic ‘M-x calendar’ 😴

Here is how it looks without any customisations. Serviceable, but not conducive to the Oxford culture.

Generic Emacs calendar

The Oxford-friendly ‘M-x calendar’ 🎓

It is busier, for sure, though this is what you get for being at Oxford. Notice that when there is nothing Oxford-related, we just show the regular calendar information.

Oxford Emacs calendar

The code

UPDATE 2025-01-09 18:36 +0200: I revised a few lines of code to (i) work with either Sunday or Monday as the first day of the week, (ii) not show any Oxford week beyond the years specified in prot-oxford-dates.


I wrote this over the course of a few hours. It may be refined here and there, but I think it is already good enough. The only major improvement would be to implement the formula that Oxford uses to derive their dates. As I do not know it, the prot-oxford-dates have to be updated manually each year.

;; NOTE 2025-01-09: Perhaps there is some formula to always get the
;; dates, but I am not aware of it.  As such, these dates need to be
;; updated at the start of each school year.
;;
;; Source: <https://www.ox.ac.uk/about/facts-and-figures/dates-of-term>.
(defvar prot-oxford-dates
  '((michaelmas  (10 13 2024)  (12 7 2024))
    (hilary      (1  19 2025)  (3 15 2025))
    (trinity     (4  27 2025)  (6 21 2025)))
  "Alist of Oxford calendar terms with start and end date.
Each element is of the form (NAME START END), where NAME is the name of
the term, as a symbol, START is the start date and END is the end date.
START and END each are of the form (month day year), where each element
is a number.")

(defun prot-oxford--get-iso-week (date)
  "Get the ISO week of DATE.
DATE is a list of the form (month day year)."
  (unless (calendar-date-is-valid-p date)
    (error "The date `%s' does not conform with `calendar-date-is-valid-p'" date))
  (car
   (calendar-iso-from-absolute
    (calendar-absolute-from-gregorian date))))

(defun prot-oxford--get-term-week (term-start-week term-end-week iso-week prefix)
  "Return the number of the Oxford term week or nil.
Determine the week given TERM-START-WEEK and TERM-END-WEEK as Gregorian
week numbers.  Compare ISO-WEEK to them.

If `calendar-week-start-day' is a Monday, then start counting weeks from
0, because Oxford weeks start from Sunday (otherwise, Week 1 includes 6
days before the first Sunday).

When returning the number, concatenate it with PREFIX.  PREFIX is a
single letter string.  A longer PREFIX is trimmed accordingly."
  (when (and term-start-week term-end-week iso-week)
    (when-let* ((offset (pcase calendar-week-start-day
                          (0 1)
                          (1 0)))
                (number (cond
                         ((> iso-week term-end-week)
                          nil)
                         ((= term-start-week iso-week)
                          offset)
                         ((< term-start-week iso-week)
                          (+ (- iso-week term-start-week) offset))))
                (pre (if (> (length prefix) 1)
                         (substring prefix 0 1)
                       prefix)))
      (concat pre (number-to-string number)))))

(defun prot-oxford--get-term-weeks (term year)
  "Return Oxford TERM start and end week numbers as a list.
Check YEAR to determine if the date is out of bonds of the
`prot-oxford-dates'."
  (pcase-let* ((`(,beg-date ,end-date) (alist-get term prot-oxford-dates))
               (`(,_ ,_ ,term-year) beg-date)
               (beg-week (prot-oxford--get-iso-week beg-date))
               (end-week (prot-oxford--get-iso-week end-date)))
    (when (= term-year year)
      (list beg-week end-week))))

(defface prot-oxford-term-indicator
  '((((class color) (min-colors 88) (background light))
     :foreground "#224499")
    (((class color) (min-colors 88) (background dark))
     :foreground "#afc9f3")
    (t :inherit shadow))
  "Face to style the Oxford term indicator.")

(defface prot-oxford-regular-week
  '((t :inherit shadow))
  "Face to style the regular week.")

(defun prot-oxford-week (month day year)
  "Use MONTH DAY YEAR to determine current week.
Derive the Oxford term week based on the `prot-oxford-dates'."
  (pcase-let* ((`(,m-w-beg ,m-w-end) (prot-oxford--get-term-weeks 'michaelmas year))
               (`(,h-w-beg ,h-w-end) (prot-oxford--get-term-weeks 'hilary year))
               (`(,t-w-beg ,t-w-end) (prot-oxford--get-term-weeks 'trinity year))
               (gregorian-week (prot-oxford--get-iso-week (list month day year)))
               (oxford-week (or (prot-oxford--get-term-week m-w-beg m-w-end gregorian-week "M")
                                (prot-oxford--get-term-week h-w-beg h-w-end gregorian-week "H")
                                (prot-oxford--get-term-week t-w-beg t-w-end gregorian-week "T")
                                "")))
    (format " %2s  %2s  "
            (propertize oxford-week 'face 'prot-oxford-term-indicator)
            (propertize (format "%2s" gregorian-week) 'face 'prot-oxford-regular-week))))

(defun prot-oxford--get-term-month (term-name term-start-month term-end-month month)
  "Return the TERM-NAME of the term month or nil.
Determine the name given TERM-START-MONTH and TERM-END-MONTH as month
numbers.  Compare MONTH to them."
  (when-let* ((number (cond
                       ((> month term-end-month)
                        nil)
                       ((= term-start-month month)
                        1)
                       ((< term-start-month month)
                        (+ (- month term-start-month) 1)))))
    term-name))

(defun prot-oxford--get-months (term)
  "Get start and end months of `prot-oxford-dates' TERM as a list."
  (mapcar #'car (alist-get term prot-oxford-dates)))

(defun prot-oxford-month (year month)
  "Return abbreviated name of MONTH for YEAR.
Append the Oxford term name based on the `prot-oxford-dates'."
  (pcase-let* ((`(,m-beg ,m-end) (prot-oxford--get-months 'michaelmas))
               (`(,h-beg ,h-end) (prot-oxford--get-months 'hilary))
               (`(,t-beg ,t-end) (prot-oxford--get-months 'trinity))
               (oxford-term-name (or (prot-oxford--get-term-month "Michael" m-beg m-end month)
                                     (prot-oxford--get-term-month "Hilary" h-beg h-end month)
                                     (prot-oxford--get-term-month "Trinity" t-beg t-end month)
                                     "")))
    (format "%s %s %s"
            (propertize (calendar-month-name month :abbreviate) 'face 'calendar-month-header)
            (propertize (format "%s" year) 'face 'calendar-month-header)
            (propertize oxford-term-name 'face 'prot-oxford-term-indicator))))

(defun prot-oxford-intermonth-header ()
  "Return string for `calendar-intermonth-header'."
  (format "%s %s"
          (propertize "OX" 'face 'prot-oxford-term-indicator)
          (propertize "Week" 'face 'shadow)))

(setopt calendar-left-margin 10
        ;; Oxford assumes Sunday starts the week, but we want to work
        ;; with the ISO commercial dates, so Monday (1) is the first
        ;; day of the week.  But Sunday (0) will still work.
        calendar-week-start-day 1
        calendar-intermonth-spacing 12
        calendar-intermonth-header (prot-oxford-intermonth-header)
        calendar-intermonth-text '(prot-oxford-week month day year)
        calendar-month-header '(prot-oxford-month year month))
-1:-- Emacs: custom Oxford University calendar weeks (Post Protesilaos Stavrou)--L0--C0--2025-01-09T00:00:00.000Z

James Dyer: Ediff Comparing Regions

ediff is one of my favourite tools in Emacs and I have recently found myself not only using ediff-buffers and vc-ediff often but also now having the need to compare two regions.

For example, when developing new Elisp functions, I generally modify the bottom of my tangled init file and then evaluate/edit as necessary, or sometimes through the scratch buffer.

However, for something more complex, for example, at the moment, I am trying to generate an RSS feed from my Hugo-based blog’s Org file through org-publish. In this case, I prefer creating a separate directory where I can experiment with example files, generated files, and temporarily manage everything under version control.

At the end of development, I could simply paste my function(s) back into my main configuration, however, as a software engineer, I am accustomed to using merging tools, so what better way to handle this than with ediff

Given my development functions and my init file, I can’t compare entire files to copy/merge the changes across, and VC in this case is not helpful. So, how do I compare two regions? In fact, is it even possible in Emacs?

Well, yes, yes it is! (of course)

ediff-regions-linewise

Run Ediff on a pair of regions in specified buffers. BUFFER-A and BUFFER-B are the buffers to be compared. Regions (i.e., point and mark) can be set in advance or marked interactively. Each region is enlarged to contain full lines.

and my keybinding is:

(global-set-key (kbd "M-s +") #'ediff-regions-linewise)

Note: at the moment I only ever run ediff-regions-linewise but the documentation recommends using ediff-regions-wordwise for smaller regions. For this example I shall be using ediff-regions-linewise only.

Documentation for ediff-regions-wordwise

The ediff-regions-linewise function is effective for large regions, over 100-200 lines.

For small regions, use ‘ediff-regions-wordwise’.

Initially, I prefer to have a horizontal split with the two files containing the regions I would like to compare, and then place the point in the left-hand window.

When ediff-regions-linewise is then activated, that is the order the regions are set up, from left to right.

Note: According to the documentation, it seems possible to set up the two regions using marks before the call, after which you will go directly into an ediff comparison rather than the interactive region described below. However, I have not yet been able to get the region comparison to work this way.

Ok, lets do a region comparison!


  1. Run

    • Open both buffers side-by-side, Buffer A : Buffer B
    • Run M-x ediff-regions-linewise
    • Select each side-by-side buffer, by <RET>, <RET>
  2. Mark Buffer A’s Region

    • When prompted for “Region A:”, Buffer A will be automatically switched to
    • Now mark the region for comparison
    • Once the region is marked, hit C-M-c
  3. Mark Buffer B’s Region

    • Similarly, when prompted for “Region B:”, Buffer B will be automatically switched to
    • Now mark the region for comparison
    • Once the region is marked, hit C-M-c

Now the usual familiar ediff comparison begins.


Here is a little visual example set up with a simple paragraph difference:

In this case Buffer A and Buffer B are set up side-by-side and with the process for a region comparison as described above on 2. Mark Buffer A’s Region that I have deliberately made slightly different for Buffer B.

Here is the comparison result:


I have something that seems to work for me at the moment, but I would really like to fully understand the difference between a linewise and a wordwise comparison. I understand that wordwise comparison could be computationally expensive on large blocks, but what benefits do I gain from using a smaller wordwise comparison compared to a linewise comparison?

Also, although the interactive region selection works well, I really want to figure out how to set up the regions initially.

If anyone knows answers to these questions, just drop in a comment!

-1:-- Ediff Comparing Regions (Post James Dyer)--L0--C0--2025-01-08T15:30:00.000Z

Irreal: Extracting Email Addresses

Mike Zamansky has a late Christmas present for us but, as they say, better late than never. This particular present is another video in his Using Emacs series. In this video he considers the problem of extracting all email address from an Emacs buffer.

The original problem was that Zamansky had several documents, including spreadsheets, that contained email address and he wanted to get a list of all those addresses for further processing. Being an Emacser, his first thought was to extract all the text into an Emacs buffer and work from there.

That extraction amounted to cut and pasting, which, while not particularly satisfying, is probably optimal considering the many types of documents involved and the fact that is was a one-off. Once he has all the text into an Emacs buffer, the rest is relatively easy. Zamansky’s solution was to put together a bit of Elisp to do the job. The value of the video is his walking us through the process of doing that.

One of the things I really like about Zamansky’s videos is that he doesn’t clean them up. You see his mistakes and fat fingering and that helps you understand the process much better than just viewing a polished video of the final result.

Accomplished_Will_31, in a reddit comment, notes that occur can do a lot of this more easily. That’s probably true but the real value of the video is seeing how Zamansky puts together his Elisp solution. His process is applicable to a wide range of problems whether or not occur offers a shorter solution.

The video is 17 minutes, 56 seconds so plan accordingly. Like all of his videos, this one is well worth spending the time on.

-1:-- Extracting Email Addresses (Post Irreal)--L0--C0--2025-01-08T15:20:01.000Z

Protesilaos Stavrou: Emacs: the next Fontaine version will use a custom theme

As part of the current development target of my fontaine package, the way I implement changes to fonts is done via a custom theme.

commit 69e80d4a93b28804f3b9d8a0b4328952c2f0d568
Author: Protesilaos Stavrou <info@protesilaos.com>
Date:   Wed Jan 8 10:42:35 2025 +0200

  BREAKING use a custom theme instead of 'set-face-attribute' internals

 fontaine.el | 179 +++++++++++++++++++++---------------------------------------
 1 file changed, 63 insertions(+), 116 deletions(-)

Before, I was relying on the internals of set-face-attribute which worked decently for the most part but required manual intervention to persist the face attributes between theme changes. Whereas the custom theme shall remain in effect no matter what, thus reducing complexity.

Furthermore, the custom theme allows me to declare the display specification in which the given face attributes (i.e. the font styles) apply. I can thus specify that these are for a graphical Emacs frame only.

A potential advantage is the ability to modify any face, even if it is not initialised, whereas set-face-attribute requires the face be defined, else it produces an error. This potential is not realised for the time being because there is no face of the sort that Fontaine affects. All the faces it modifies are loaded eagerly by Emacs. If I need to cover more faces though, it will be straightforward.

For users, the only obvious effect of this transition is the discontinuation of the option to set a Fontaine preset per frame. All face attributes are now always applied to all frames. I am doing this because the old design did not work reliably in all cases and was a niche feature anyway.

I am not aware of any regressions, though I continue to test the package. If you do try it before the new version is out, please let me know of any possible bugs or other improvements we can make.

About Fontaine

Fontaine allows the user to define detailed font configurations and set them on demand. For example, one can have a regular-editing preset and another for presentation-mode (these are arbitrary, user-defined symbols): the former uses small fonts which are optimised for writing, while the latter applies typefaces that are pleasant to read at comfortable point sizes.

-1:-- Emacs: the next Fontaine version will use a custom theme (Post Protesilaos Stavrou)--L0--C0--2025-01-08T00:00:00.000Z

Stefan van der Walt: Pomodoros with org-timer

A New Year! Create the 2025.org journal file, channel all that post-break energy, and get ready to be Super Productive™! 💪 Oh, what’s this… a post on how to track Pomodoros using org mode. 👀 Of course, I didn’t bite. Of course not.

Productivity people sure spend a lot of time writing a lot about little; and the Pomodoro technique is no exception. It can be summarized as:

Set a timer, get to work, take a break. Repeat, take a break.

There’s way too much productivity literature out there, and much of it comes down to: find a way to get your butt in the seat and start [writing/typing/reading/editing/…]. The new wave of productivity literature, or “anti-productivity” literature, tells us to face the facts: you’re never going to finish your TODO list, so give up already, be in the present, and focus on what’s most important (or, whatever, just be present).

Sometimes, I get in the flow easily, and don’t need any tricks. But other times, especially with those slightly-mundane-tasks-that-still-need-to-get-done, I need a little nudge not get started and not get distracted (by something more interesting which, in this case, may well be everything).

For me, the Pomodoro method is helpful for that purpose. And, because distraction abounds, it’s helpful to have the timer as well as the topic you’re supposed to be focused on displayed somewhere visible.

When I saw Charl’s post, I was delighted because:

  1. It relies on org-mode, which I already use;
  2. it does everything I used org-pomodoro for, but more simply;
  3. it provides an easy path towards integration with waybar (he uses xbar for macOS, but same idea); and,
  4. as observed by Charl, it does not interfere with other org clocks.

So, what follows here is a slight modification of Charl’s method, which in turn combines previous approaches by David Wilson (System Crafters) and Xiang Ji.

Emacs configuration: basic #

(require 'org-timer)

;; If you want sound when the pomodoro is over
(setq org-clock-sound (expand-file-name "~/sounds/bell.wav"))

;; And if you'd like to have a keyboard shortcut for starting a Pomodoro
(defun org-timer-start-pomodoro ()
  (interactive)
  (setq current-prefix-arg 25)
  (call-interactively 'org-timer-set-timer))
(global-set-key (kbd "C-c P") 'org-timer-start-pomodoro)

Now, when you press C-c P, a new pomodoro timer is started (you’ll see it in the mode line). You can also start it with org-timer-set-timer. After 25 minutes, when the Pomodoro is done, a system notification is displayed. Tada!

Display pomodoro status on waybar #

To display org-timer’s status on waybar, we’ll query emacs (via emacsclient1) for its current value.

Here’s the function, org-timer-waybar-repr, which generates the status text:

(defun html-escape-string (string)
  "Escape special characters in STRING for HTML."
  (let ((result ""))
    (dolist (char (string-to-list string) result)
      (setq result
            (concat result
                    (pcase char
                      (?\& "&amp;")
                      (?\< "&lt;")
                      (?\> "&gt;")
                      (?\" "&quot;")
                      (?\' "&#39;")
                      (_ (char-to-string char))))))))

(defun org-timer-minutes-to-string ()
  "Remaining org-timer minutes, rounded to nearest minute, as string."
  (let* ((time-string (org-timer-value-string))
         (parts (split-string time-string ":"))
         (hours (string-to-number (nth 0 parts)))
         (minutes (string-to-number (nth 1 parts)))
         (seconds (string-to-number (nth 2 parts)))
         (total-minutes (+ (* hours 60) minutes (/ seconds 60.0))))
    (number-to-string (round total-minutes))))

(defun org-timer-waybar-repr ()
  "Format org-timer status for waybar"
  (if (or (not (boundp 'org-timer-countdown-timer)) (not org-timer-countdown-timer))
    "🤗"
    (html-escape-string
      (concat
        "🍅 "(org-timer-minutes-to-string)
        "  🎯 " (org-link-display-format
                  (substring-no-properties org-timer-countdown-timer-title))))))

Next, we need a script, say org-timer-remaining, to access this text:

#/bin/sh
emacsclient --eval '(org-timer-waybar-repr)' | sed 's/"//g'

(That sed bit is to strip the surrounding quotes from the resulting string.)

To display it in waybar, we need to tell waybar to call the script at a regular interval. In ~/.config/waybar/config:

{
  ...,
  "modules-left": [..., "custom/org_timer"],
  ...,
  "custom/org_timer": {
     "exec": "~/scripts/org-timer-remaining",
     "interval": 30,
     "signal": 8
  }
}

Now, after starting a timer, you should see something like:

Waybar displaying org-timer status

One little trick 🪄: do you notice the "signal": 8 above? You don’t need it, but it’s a mechanism provided by waybar for externally refreshing a widget. In this case, if we send a specific kill signal, the org-timer display will reload:

pkill -RTMIN+8 waybar

You can therefore add the following to your emacs configuration to refresh waybar the moment a pomodoro is created:

(add-hook 'org-timer-set-hook
  (lambda ()
    (start-process "waybar-timer-trigger" nil "pkill" "-RTMIN+8" "waybar")))

Things that didn’t work #

I’d have liked a tomato-themed notification for when my pomodoro expired. I can use notify-send to make that happen:

(defun pomo-notify (MSG &optional TIMEOUT)
  """Display pomodoro notification with notify-send"""
  (apply
    'start-process
    "notify-send" nil "notify-send"
    `(,@(when TIMEOUT (list (format "--expire-time=%d" (* 1000 TIMEOUT))))
       ,MSG)))

(add-hook 'org-timer-done-hook (lambda () (pomo-notify "🍅 org-timer done!")))

However, now I have two notifications popping up: mine, and the org-timer one. I tried to silence the org-timer notification using advice:

(defun suppress-org-notify (orig-fun &rest args)
  (cl-letf (
             ((symbol-function 'org-show-notification) (lambda (&rest _) (ignore)))
           )
    (apply orig-fun args)))
(advice-add 'org-timer--run-countdown-timer :around #'suppress-org-notify)

Alas, my advice fell on deaf ears. Do you perhaps know how to make it work? Let me know!

The end #

En dit is dit. Geniet die tamaties!


Waybar update #

I didn’t like that the monitoring script talked to emacs every 30s, regardless of whether the timer is running. So, here is a modified version that triggers waybar updates only when the timer is started, and pauses them when the timer ends:

org-timer-remaining:

#/bin/sh

function print_status() {
  REPR=`emacsclient --eval '(org-timer-waybar-repr)' | sed 's/"//g'`
  echo $REPR
}

print_status

MONITORING="no"

trap 'MONITORING="yes"' USR1
trap 'MONITORING="no"; print_status' USR2

while true; do
  if [ $MONITORING == "yes" ]; then
    print_status
  fi
  sleep 30 &
  wait $!
done

Because the script now does the polling internally, you invoke it slightly differently from waybar:

    "custom/org_timer": {
        "exec": "~/scripts/org-timer-remaining",
    }

And, because the script handles the signals (instead of waybar), we need to update our hooks too:

(add-hook 'org-timer-set-hook
  (lambda ()
    (start-process
    "waybar-monitor-start" nil "pkill" "-USR1" "-f" "sh .*org-timer-remaining")))

(add-hook 'org-timer-done-hook
  (lambda ()
    (start-process
    "waybar-monitor-pause" nil "pkill" "-USR2" "-f" "sh .*org-timer-remaining")))

  1. Since Emacs 29, the emacsclient server is started automatically! 🙌 ↩︎

-1:-- Pomodoros with org-timer (Post Stefan van der Walt)--L0--C0--2025-01-08T00:00:00.000Z

Irreal: Shopping Lists With Emacs

Kris Carta has a nice post on a problem we all have: handling grocery shopping lists. If you’re single and a geek, this is not a problem. All sorts of solutions suggest themselves. The problem comes when you have a significant other that is not a geek but still likes to organize things on the computer.

If one or the other of you does all the meal planning and shopping it’s not too big a problem but in most households, both partners are making suggestions and adding things to the shopping list. If, in addition, the technical partner is an Emacs geek, the problem becomes how to import the non-technical partner’s suggestion into Emacs and how to export a final list to a smartphone application that either or both can use while shopping.

I wrote about this eight years ago and not a lot has changed. Carta is an Apple user and wanted to produce a shopping list in the iOS Reminders app. That’s what we do at the Irreal bunker too and it works well. All of us here, geek or not, is perfectly capable of using Reminders and marking off items as we buy them.

Carta has a separate wrinkle. He and his wife have always planned their meals in Notion and they wanted to export the items on their meal lists to their shopping list. Even though Carta is new to Emacs, he was able, with help, to figure out how to get the Notion data into Emacs and from there to isolate the food items for the shopping list and export them to Reminders. This leverages Applescript, which is definitely a pain, but provides functionality not available on other platforms.

Here at the bunker, we just enter the list into Reminders directly. That’s easier but not as pleasing to our geek sensibilities.

-1:-- Shopping Lists With Emacs (Post Irreal)--L0--C0--2025-01-07T17:48:48.000Z

Mike Zamansky: Extracting Emails (with Emacs)

Yesterday I found myself in a situation where I had a text document interspersed with a bunch of email addresses and I wanted to extract those email addresses.

Specifically, I had to copy email addresses from a couple of spreadsheets and other sources and I thought it would be faster to copy lines form the sheets instead of email cells as well as just grabbing text from the other sources and then isolating the email addresses in Emacs.

Now, writing a little "extract emails" script to operate on a text file is easy enough. You can search for an email address using a regular expression. Regular expressions are a text pattern matching language that most programming language support. I used something like the following regular expression to identify an email address:

[a-zA-Z0-9\.]+@[a-zA-Z0-9]+\.[a-zA-Z]{3}

The first [a-zA-Z0-9\.]+ matches a sequence of one or more characters that are letters, numbers or a period (the slash before the period is to escape it - prevent using the . for its special regular expression meaning). That's followed by the @ symbol, a second set of characters, this time without the period, then a dot and then three more characters.

This will match most email addresses.

In Clojure, I'd use the function re-seq which would take a string (presumably my text file) and return a list of all the matches. That is, all the email addresses. I could do the same in Python with re.findall.

In an editor, at least in Emacs, it's a little trickier. You can search for a regular expression but that just finds the next email address. I'd then have to manually copy it and then repeat the process.

I guess a keyboard macro could do the trick but easier would be to just write an elisp function and extend Emacs.

The video shows the whole walkthrough but the core of the routine is using the search-forward-regexp function which searches for the next occurrence of an email match. I then add that email address to a list. Putting it all in a while loop, you get:

(while (search-forward-regexp regexp nil t 1)
 (push (match-string-no-properties 0) matches))

The match-string-no-properties grabs the actual text of the match.

There's a little more detail but basically I wrote an equivalent of re-seq for an Emacs buffer - it grabs everything that matches a regular expression and returns all the matches in a list.

Then, it was a simple matter of looping over that list and inserting the results. I ultimately named the routine extract-emails. So now, I can be in any Emacs buffer, copy over all my source email material, run the command, and I'll have all the emails together one per line ready to be pasted into an email client.

I'm thinking that I should probably give the routine an additional optional parameter - have it extract emails by default but allow the user to pass in any regular expression.

Just another reason why Emacs is such a powerful editor.

Here's the video, enjoy:

-1:-- Extracting Emails (with Emacs) (Post Mike Zamansky)--L0--C0--2025-01-07T13:25:00.000Z

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