Background.

On Windows, I run Emacs on WSL 1 whilst displaying to Windows via the X410 X-Server.

This is currently the best compromise if one wants to interoperate with the NTFS side, at least until the WSL 2 developers manage to fix up its current cross-OS IO performance issues.

As part of this setup, I have written a small xdg-open replacement in Python which does path translation. This enables me to open any links from Emacs, running on WSL 1, using the relevant Windows file handler, no matter whether the file finds itself on the Windows or the WSL side of the world.

For example, if I’ve linked to a PPTX from my orgmode notes, pressing C-c C-o opens up that file using the true-blue Microsoft PowerPoint application on Windows. The same goes for any attachments that I open using mu4e, my favourite Emacs mail client.

This gives me the best of both worlds between Emacs on the WSL-Linux side, and all of the Windows applications on the other.

100% CPU blocking bug.

Recently however, I ran into a bug where only orgmode links would result in the Windows cmd.exe or explorer.exe process (which I use as the catch-all URL filetype opener) blocking while eating up 100% CPU, but with the handler never actually appearing.

Opening up non-orgmode links, for example with Emacs browse-url, would work perfectly.

Analysis.

Once I found time to dig into the difference above (discovering the difference took weeks, as I only ever spent a few minutes at a time before working around the problem. Fortunately I took good notes in Orgmode! ;) I discovered that browse-url used call-process and org-open-file used start-process-shell-command.

When I temporarily changed the code of the fairly long org-open-file function to use call-process-shell-command, it worked perfectly!

The fix.

Because this is Emacs Lisp, we have a number of great ways to fix the function.

In this case, I chose to advise the 150 line long org-open-file function to use call-process-shell-command instead of start-process-shell-command.

I added the following code to my init.el:

1
2
3
4
5
6
7
8
9
;; fix org-open-file for wsl by temporarily replacing start-process-shell-command with call-process-shell-command
;; if we don't do this, emacs on WSL will block forever trying to open exported file with windows handler
(defun wsl-fix-org-open-file (orig-org-open-file &rest args)
  ;; temporarily replace function,
  ;; see https://endlessparentheses.com/understanding-letf-and-how-it-replaces-flet.html
  (cl-letf (((symbol-function 'start-process-shell-command) #'call-process-shell-command))
    (apply orig-org-open-file args)))

(advice-add #'org-open-file :around #'wsl-fix-org-open-file)
  1. With advice-add and :around, we ask that Emacs call our special wsl function instead of org-open-file, whenever the latter is invoked.
  2. Our replacement function receives the original “unfixed” org-open-file and its arguments.
  3. With cl-letf, we temporarily replace start-process-shell-command with call-process-shell-command, only while we invoke the original org-open-file.

Conclusion.

The elegance and precision with which it is possible to modify the run-time behaviour of Emacs, is one of the many reasons that this 40-year-old editor is still the king.

Tangential Post Scriptum.

Emacs is always running on my computers.

However, I will also use VSCode and any of the JetBrains IDEs when the relevant tool suits the job better.

That being said, I am still waiting for any other editor to come close to the same level of integration between extension code and runtime behaviour that Emacs offers.

In Emacs, I can write and interact with code to modify just about any aspect of the editor at runtime. VSCode, for example, offers only limited possibilities via its extension API.

Leave a comment if you would like to discuss!