Can I edit an svg file in a text mode (say, nxml-mode) while seeing live updates to the image in another window?

I can see SVG as images, or as text by using image-toggle-display, but it always toggles both buffers, even if I do clone-indirect-buffer.

I'm using a strange build of emacs for Windows... I've lost track of where I even got it:

GNU Emacs 24.4.1 (x86_64-w64-mingw32) of 2014-10-20 on KAEL

1 Answer 1


After various experiments, here's what I came up with.

This works so far. But I am no elisp master, so I welcome any improvements.

(defun gpc/mirror-buffer (buffer-name &optional more-after-change)
  "Create a buffer whose contents will follow the current one's
and returns the new buffer.  Runs `more-after-change' after each
change if provided.

This differs from `clone-indirect-buffer' in that the new buffer
is not visiting a file.  It's really just a kludge to support
`gpc/mirror-image', which see."
  (interactive (list 
                (let ((default (concat (buffer-name) "<mirror>")))
                  (read-string "Buffer name: " default
                               nil nil default))))
  (make-local-variable 'after-change-functions)
  (make-local-variable 'kill-buffer-hook)
      ((target-buffer (generate-new-buffer buffer-name))
       ;; Give lexical scope to arg
       (after-change more-after-change)
        #'(lambda(start end old-len)
            (let ((inhibit-read-only t))
              ;; Quick and dirty: may not be suitable for large buffers.
              (copy-to-buffer target-buffer (point-min) (point-max))
              (when (functionp after-change)
                (funcall after-change target-buffer))))))

    ;; Initialize the target buffer with the source text.
    (copy-to-buffer target-buffer (point-min) (point-max))

    (add-hook 'after-change-functions copy-change t t)

    ;; Cleanup hooks.

    ;; Kill the other buffer if the source buffer is closed.
    (add-hook 'kill-buffer-hook
              #'(lambda () (kill-buffer target-buffer)) t t)

    ;; Destroy the change hook if the other buffer is killed.
    (with-current-buffer target-buffer
      (make-local-variable 'kill-buffer-hook)
      (add-hook 'kill-buffer-hook
                #'(lambda ()
                    (remove-hook 'after-change-functions copy-change t))
                t t))))

(defun gpc/mirror-image ()
  "Open an `image-mode' buffer that tracks the content of the
current buffer.  Intended for use with svg files."
  (let* ((buffer-name (concat (buffer-name) "<image>"))
         ;; An `image-mode' buffer will switch back to text when its contents
         ;; are replaced.  Besides, the image is not updated in-place when the
         ;; content changes, so you'd have to toggle back to image-mode anyway.
         (after-change '(lambda (buffer)
                          (with-current-buffer buffer (image-mode))))
         (mirror (gpc/mirror-buffer buffer-name after-change)))
    (other-window 1)
    (switch-to-buffer buffer-name)
    (other-window 1)))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.