6

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
harpo
  • 181
  • 5

1 Answers1

0

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)
  (lexical-let*
      ((target-buffer (generate-new-buffer buffer-name))
       ;; Give lexical scope to arg
       (after-change more-after-change)
       (copy-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."
  (interactive)
  (image-mode-as-text)
  (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)))
    (split-window)
    (other-window 1)
    (switch-to-buffer buffer-name)
    (image-mode)
    (other-window 1)))
harpo
  • 181
  • 5