Emacs Lisp: Editing one file twice at the same time

| emacs

@HaraldKi@nrw.social said:

Emacs can do everything. Except the most simple thing ever as I learned after 40 years in which I never needed it: Edit one file twice at the same time.

I can open a new Emacs "window" and re-open the file. But Emacs notices and this and shows the file's buffer in the new window, not a new buffer.

But why? Well, when editing and SVG file, you can switch between the XML and the rendered image with C-c C-c, but I would like to see the XML and the rendered next to each other.😀

You might think this is easy, just use M-x clone-indirect-buffer-other-window. But image-mode adds a wrinkle. It uses text properties to display the image, so even if you have two views of the same buffer thanks to clone-indirect-buffer, C-c C-c will toggle both of them. If we want to edit a file as both text and an SVG at the same time, we need to actually have two separate file buffers.

I started off by looking at how find-file works. From there, I went to find-file-noselect. Normally, find-file-no-select reuses any existing buffers visiting the same file. If it doesn't find any, it calls find-file-noselect-1. That lets me write this short function to jump straight to that step.

(defun my-find-file-always (filename &optional buffer-name)
  (interactive (list (read-file-name "File: ")))
  (setq buffer-name (or (create-file-buffer filename)))
  (let* ((truename (abbreviate-file-name (file-truename filename)))
         (attributes (file-attributes truename))
         (number (file-attribute-file-identifier attributes)))
    (with-current-buffer
        (find-file-noselect-1
         (get-buffer-create buffer-name)
         truename
         t nil truename number)
      (when (called-interactively-p 'any)
        (switch-to-buffer (current-buffer)))
      (current-buffer))))

(defun my-clone-file-other-window ()
  (interactive)
  (display-buffer-other-window (my-find-file-always (buffer-file-name))))

This code unconditionally opens a buffer visiting a file, so you could have multiple buffers, looking at the same file independently. With global-auto-revert-mode, editing the file in one buffer and saving it will result in changes in the other.

I sometimes play around with SVGs, and it might be helpful to be able to experiment with the source code of the SVG while seeing the changes refreshed automatically.

I really like how in Emacs, you can follow the trail of the functions to find out how they actually work.

Screencast demonstrating my-find-file-always

Transcript

00:00:00 The problem: clone-indirect-buffer-other-window and image-mode
@HaraldKi@nrw.social said, "Emacs can do everything except the most simple thing ever, as I learned after 40 years in which I never needed it: edit one file twice at the same time." You might think this is easy, just use M-x clone-indirect-buffer-other-window, but image mode adds a wrinkle. So let's show you how that works. I've got my test SVG here. We can say clone-indirect-buffer-other-window. But if I use C-c C-c, you'll notice that both of the windows change. That's because image mode uses text properties instead of some other kind of display. I mean, it's the same buffer that's being reused for the clone. So that doesn't work.
00:00:48 A quick tour of find-file
What I did was I looked at how find-file works. And then from there, I went to find-file-noselect. So this is find-file over here. If you look at the source code, you'll see how it uses find-file... It's a very short function, actually. It uses find-file-noselect. And find-file-noselect reuses a buffer if it can. Let's show you where we're looking for this. Ah, yes. So here's another buffer here. And what we want to do is we want to open a new file buffer no matter what. The way that find-file-noselect actually works is it calls this find-file-noselect1. And by taking a look at how it figured out the raw file and the true name and the number to send to it, I was able to write this short function, my-find-file-always, and a my-clone-file-other-window.
00:01:46 Demonstration of my-find-file-always
So if I say my-find-file-always, then it will always open that file, even if it's already open elsewhere.
00:01:57 Cloning it into the other window
Let's show you how it works when I clone it in the other window. All right, so if I switch this one to text mode, I can make changes to it. More stuff goes here. And as you can see, that added this over here. I have global-auto-revert mode on, so it just refreshes automatically. So yeah, that's this function.

View org source for this post