Content AND Presentation

2025-07-07 Mass resetting Org mode checkboxes

I’m a big fan of checklists. There are many cases where I need to follow some procedure containing many steps, and I really do not want to forget about any of them. This is especially challenging when it is something I need to do not very often, say, once a month, since it is very easy to forget about one or more steps, and properly memorizing the procedure would take many months. One example is preparing and sending invoices, which I need to do at the beginning of every month. Another (slightly less typical) example is packing for vacations – it’s very easy to forget to take something, and very important not to. In fact, I prepared a generic list of things to take with me on vacation more than fifteen years ago, and I use it several times a year, sometimes adding some new item, sometimes removing some no longer needed item.

Me being me, I keep these checklists in Org mode, and they consist of one or more headings with a bunch of checkboxed lists.

One thing which was always a nuisance for me was resetting those checkboxes back to “not checked” state. I usually just query-replace​d the string [X] to [ ], but then I had to manually reset the checkbox cookies on each headline. Finally, I decided that there must be a better method – why not write a simple piece of Elisp to reset all checkboxes for me? Of course, I started with doing a bit of research to make sure I don’t reinvent the wheel. Lo and behold, Org mode already has a feature like that! There is a command called org-reset-checkbox-state-subtree, which resets all checkboxes in the current subtree. Now, the only thing left to automate is to call this function for every subtree in the buffer or region.

(defun org-reset-checkbox-state-buffer ()
  "Reset all checkboxes in the (narrowed portion of) the buffer."
  (interactive "*")
  (org-map-entries #'org-reset-checkbox-state-subtree
                   t nil
                   'archive 'comment))

It’s not ideal – for example, it apparently unfolds the headlines in the whole buffer. Also, it calls (org-update-checkbox-count-maybe 'all), which scans the whole buffer after processing every headline – definitely not optimal. However, I’m not going to run this on Org files larger than, say, several kilobytes (and I do have Org files larger than that – in fact, the very file I’m writing this blog in is over 1.5MB!). Also, if the heading structure is nested, it is enough to run org-reset-checkbox-state-subtree on every level one headline, while my code does that on all subheadings, too – but then again, my packing list (currently the only use-case for this command over org-reset-checkbox-state-subtree) does not have any subheadings at all. If your use-case is more complex, feel free to adapt my code, for example calling org-forward-heading-same-level in a while loop.

That’s it for today, see you next time!

CategoryEnglish, CategoryBlog, CategoryEmacs

Comments on this page

2025-06-30 Interacting with an external process via stdin and stdout

For a project I’ve been recently working on, I needed Emacs to interact with an external process, sending things to it via its stdin and receiving responses via its stdout. I never did anything like that before, so I started with checking my options. I heard good things about Comint mode, but after poking around I decided that it’s not what I’m looking for:
[…] Comint mode, a general-purpose mode for communicating with interactive subprocesses.
Since I explicitly do not want the user to interact with the external process, Comint mode is not suitable for this project.

I asked on the Emacs mailing list, and got several helpful answers; again, Eli Zaretskii turned out to be a helpful hero he is.

So, let’s first code a toy example to see how this is done; in the near future, I’m going to show a real-world application. Let’s forget that Emacs has an insanely advanced scientific calculator built in and make a simple interface to bc.

We need (at the very least) to do three things: start the bc process, learn to send requests to it, and accept its responses. (To make things simpler, we will associate a buffer with the process so that all bc responses will be gathered there.) Optionally, we can also restart it when it dies.

(defvar bc-process nil
  "The `bc' process.")

(defun bc-start-process ()
  "Start `bc' in the background unless it's already started."
  (interactive)
  (unless (process-live-p bc-process)
    (setq bc-process (start-process "bc" (get-buffer-create "*bc*") "bc" "-q"))))

So far, it’s pretty clear – we define a variable to store the bc process and create a command to start it (but do nothing if it is already alive).

Let’s make a command to send user-provided input to bc. Note that we need to explicitly add a newline to the string sent.

(defun bc-send-input (bc-input)
  "Send INPUT to `bc-process'."
  (interactive "sbc input: \n")
  (if (process-live-p bc-process)
      (process-send-string bc-process (concat bc-input "\n"))
    (user-error "`bc' process is not alive")))

Note that bc-send-input has one drawback – it asks for the input first and only then makes a check for the aliveness of the bc process. This makes it simpler; a fix for that would include using a Lisp expression instead of a string in the interactive clause, probably also introducing a dedicated history variable etc. No need to do that in this simple example/proof of concept.

And now the fun part comes. So far, we can manually switch to the *bc* buffer to see bc​’s reponses – but I’d like to display them in the echo area instead. To achieve that, we need a process filter. It is a function which is called whenever a process sends something to Emacs. By default, internal-default-process-filter is used, which just inserts the process’ output to its buffer. We want to do the same and to show said output in the echo area. This gets complicated because the process filter may receive partial output. In case of bc this seems unlikely, but for processes performing more complicated tasks it is possible. (In a future article we will swap bc for something with more complex output which may indeed come in several parts, and then we’ll be able to really see this phenomenon.) Basically, we need to track two places in the process buffer. One is where we finished processing the previous output – at the same time, it marks the beginning of the new one. The other is where we finished inserting the “current” output – it marks the place we want to insert its next portion. My first idea was that the former one can be handled by the point, and the latter one by process-mark. After some experiments I discovered that the point behaves strangely when the process buffer is visible (and here’s the reason of why it does so), so I decided to use a dedicated marker variable for that in case the user actually looks at the process buffer.

Here is what I’m going to do. The process filter will first insert the received text into the process buffer at process-mark (like internal-default-process-filter), then check if the newly inserted material is “complete” (in other words, if there is a newline between bc-process--previous-mark and process-mark), and if so, it will gather it, strip the trailing newline, show it in the echo area, and finally move the “previous marker” in the process filter past said newline.

(defvar bc-process--last-mark nil
  "The end of the previously processed result.")

(defun bc-process-filter (process output)
  "Insert OUTPUT to the buffer of PROCESS.
Also, message the user."
  (let ((buffer (process-buffer process))
        (mark (process-mark process)))
    (when (buffer-live-p buffer)
      (with-current-buffer buffer
        (save-excursion
          (goto-char mark)
          (insert output)
          (set-marker mark (point)))
        (when (save-excursion (search-forward "\n" mark t))
          (message "%s" (string-trim-right
                         (buffer-substring bc-process--last-mark
                                           mark)))
          (set-marker bc-process--last-mark mark))))))

(defun bc-start-process ()
  "Start `bc' in the background unless it's already started."
  (interactive)
  (unless (process-live-p bc-process)
    (setq bc-process (start-process "bc"
                                    (get-buffer-create "*bc*")
                                    "bc" "-q"))
    (setq bc-process--last-mark (copy-marker
                                 (process-mark bc-process)))
    (set-process-filter bc-process #'bc-process-filter)))

This seems to work fine, but bc is a very simple program. What if the output from the process can be multiline (well, technically bc can also output many lines at once, but I just ignored that fact for the sake of simplicity), or arrive in parts, etc.? Well, in the next part we’ll create a simple program which accepts a line of input and outputs a (possibly multiline) JSON object, a line at a time, with some delay, and can even hang or die in the middle of producing an output (so that we can make sure that our Elisp code is capable of handling such errors). For now, that’s all!

CategoryEnglish, CategoryBlog, CategoryEmacs

Comments on this page

2025-06-23 Making functions interactive

I have to admit that I probably have a bit of OCD, and when I finish my work, I like to put my Emacs in a sort of “clean slate” state. By that I don’t mean closing it or killing all buffers (although when I leave the office, I do kill all work-related buffers). Instead, I mean going back to displaying just one window with the *GNU Emacs* buffer (the one with the logo and links to the tutorial etc.) The problem is, I sometimes accidentally kill that buffer, and then I have no place to go back to;-).

Well, some time ago it occurred to me that something in Emacs must create that buffer, and I can find that something and learn how to do that whenever I want. Grepping for a few words on that splash screen I quickly found the fancy-startup-screen function which does exactly that. I was a bit surprised that it is not an interactive function (IOW, a command), but – as I learned several years ago – this is easy to fix:

;; warning: this does not work!
(put 'fancy-startup-screen 'interactive-form '(interactive))

Sadly, the interactive-form symbol property no longer works. As Stefan Monnier pointed out in that thread, there is another (perhaps cleaner) way to make a non-interactive function into a command:

(advice-add 'fancy-startup-screen
            :before
            (lambda () (interactive) nil))

This method is a bit more verbose, but is clean, the intent is obvious and – last but not least – it works. What else could you want?

CategoryEnglish, CategoryBlog, CategoryEmacs

Comments on this page

More...

CategoryEnglish, CategoryBlog