2023-01-09 TODO stats table

Last week I showed how to compute a summary of all TODO keywords in an Org mode subtree. Today we’ll find out how to insert those data in an easy and readable way in the buffer.

What I want to achieve is something akin to clock tables. I want to be able to insert a block like this:

#+BEGIN: todotable
#+END:

and press C-c C-c, and get the stats in an Org table. (In the future, I might add support for parameters like :scope. For now, let’s keep it simple.)

The first idea I had was to add an entry to org-ctrl-c-ctrl-c-hook or org-ctrl-c-ctrl-c-final-hook (which see). During experimenting, however, I found something else. It turns out that the org-ctrl-c-ctrl-c command is basically a giant cond clause deciding what to do, and in the case of a dynamic block it calls the aptly named org-update-dblock function. This one in turn has this:

(let* ((cmd (intern (concat "org-dblock-write:" name))))
  (funcall cmd params))

(of course this is just an excerpt). This means that I just need to define an org-dblock-write:todotable function, accepting a single parameter – the plist of parameters given by the user in the #+BEGIN line (combined with some other stuff, like the previous content of the block). What’s even better, the only thing this function needs to do is to insert the necessary stuff – the previous content is emptied by the org-prepare-dblock function. (By the way, this is explained in the docstring of the org-update-dblock function, but somewhat unexpected when reading the code alone – who would guess that this part

(let* ((params (org-prepare-dblock))
       ;; ...
       )
  ;; ...
  )

would actually perform the emptying?)

So here is the first attempt at inserting the todotable block (ignoring any parameters and inserting just the data for the current subtree). I poked around in some Org mode source files and it seems there are no functions to insert a table row and realign the table – you just insert stuff like | and |-+-| manually. On the other hand, this is admittedly a good thing, since you may just insert what you need in an unaligned fashion and call org-table-align once at the end, thus not wasting cycles.

So, without further ado, here is my function.

(defun org-dblock-write:todotable (params)
  "Write a todotable."
  (insert "| Keyword | Count |\n")
  (insert "|-\n")
  (mapc (lambda (keyword-count)
          (insert (format
                   "| %s | %s |\n"
                   (car keyword-count)
                   (cdr keyword-count))))
        (org-subtree-todo-stats))
  (org-table-align))

In the future we’ll add some parameter parsing to this. Until then!

CategoryEnglish, CategoryBlog, CategoryEmacs, CategoryOrgMode