From the department of how-obscure-can-you-get-really, I present you with this neat trick to open WSL2 files in their native Windows handlers via Emacs TRAMP connection from WSL1 to WSL2.

Motivation

My use case is this:

  • On Windows, I use Emacs primarily from WSL1 to manage everything on Windows, on WSL1 and on the WSL2 distros I use for development.
  • I’m often connected to WSL2 via TRAMP, for managing files with dired, manipulating the results of simulations, and using the amazing magit.
  • When I open e.g. a simulation report in HTML or some other format, I would ideally like it to open with my Windows browser.

Method

The code below is a slightly modified version of crux-open-with from the crux package, with the one major change that it uses the relevant TRAMP mechanisms to spawn a remote process to open the file.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
(defun crux-open-with-tramp (arg)
    "Open visited file in default external program.
When in dired mode, open file under the cursor.

If the file is remote via tramp, use the tramp machinery to run
xdg-open remotely. In my case, I'm often tramped into WSL2 from
the WSL1 that runs my Emacs, in which case this command will open
the remote WSL2 file in the correct Windows handler for that file
using my special xdg-open.

With a prefix ARG always prompt for command to use."

    (interactive "P")
    (let* ((current-file-name
            ;; TRAMP:
            ;; for process-file, we need to pass the filename possibly relative
            ;; to the remote destination, hence the file-local-name
            ;; file-local-name will pass through names that are already local
            (file-local-name (if (eq major-mode 'dired-mode)
                                 (dired-get-file-for-visit)
                               buffer-file-name)))
           (open (pcase system-type
                   (`darwin "open")
                   ((or `gnu `gnu/linux `gnu/kfreebsd) "xdg-open")))
           (program (if (or arg (not open))
                        (read-shell-command "Open current file with: ")
                      open)))
      ;; TRAMP:
      ;; main trick here is to use process-file instead of call-process
      ;; process-file will use remote process if default-directory is tramp destination else local
      ;; with buffer 0 you'll get "tramp-file-name-handler: Implementation does not handle immediate return"
      ;; trick is to read call-process docs as well :)
      ;; -- 0 means don't wait, so pass nil or a buffer to see output
      (process-file program nil (get-buffer-create "crux-open") nil current-file-name)))

I have my special wsl version of xdg-open, xdg-open-wsl, running on WSL2 and so triggering the function below, which I have bound to C-c o, will open the WSL2 file using its native Windows handler.

Conclusion

Just to reiterate, this gives you Emacs running on WSL1 displaying to Windows via X410, connected to WSL2 via TRAMP, opening WSL2 files using their native Windows handlers, at the press of a button.

Surprisingly, it feels quite integrated. ;)

If you prefer running Emacs on Windows natively, you will need to tweak the Lisp somewhat.

Let me know in the comments if you’re the one other person on this planet who found this useful!