In my org-mode setup, initially I used to preview the whole buffer all the time. However, as my files grew bigger the waiting time for this has become prohibitively expensive. Since then I have set it up to continuously recompile the file in the background each time the file is saved. It works pretty well since instead of spawning a new async process on each save, I have a single background process which recomiles in sync and thus needs not reload all the emacs on each recompile. Overall, it takes a couple of seconds to see the updated pdf after each save.
Recently however, I updated to org-mode 9.3 and I noticed that the image creation has gotten significantly faster, and the caching has gotten better - to the point of loading instantly the images from previous sessions. This has led me searching again for a way to toggle the images whenever the cursor falls on them, like in auctex. I wanted this feature for a long while now, bug never bothered to write it myself.
This time however, I found the solution premade by John Kitchin. It essentially worked quite well but had a few bugs that irritated me. I took it upon me to fix them, and attached below is the version with the fixes, as well as a bit of refactoring.
The major points:
- Before deleting an overlay, make sure that there is still a latex fragment at the current location.
- Don’t move the point (cursor) when entering a fragment.
- Refactoring of the latex fragment test and surroungins (I know there is also org-inside-LaTeX-fragment-p, but it doesn’t detect when located on the first character of a fragment, and this one does).
To use, add the following to your org-mode-hook
(add-hook 'post-command-hook 'kk/org-latex-fragment-toggle t)
And add the following code somewhere in init.el
.
(defvar kk/org-latex-fragment-last nil
"Holds last fragment/environment you were on.")
(defun kk/org-in-latex-fragment-p ()
"Return the point where the latex fragment begins, if inside
a latex fragment. Else return false"
(let* ((el (org-element-context))
(el-type (car el)))
(and (or (eq 'latex-fragment el-type) (eq 'latex-environment el-type))
(org-element-property :begin el))))
(defun kk/org-latex-fragment-toggle ()
"Toggle a latex fragment image "
(and (eq 'org-mode major-mode)
(let ((begin (kk/org-in-latex-fragment-p)))
(cond
;; were on a fragment and now on a new fragment
((and
;; fragment we were on
kk/org-latex-fragment-last
;; and are on a fragment now
begin
;; but not on the last one this is a little tricky. as you edit the
;; fragment, it is not equal to the last one. We use the begin
;; property which is less likely to change for the comparison.
(not (and kk/org-latex-fragment-last
(= begin
kk/org-latex-fragment-last))))
;; go back to last one and put image back, provided there is still a fragment there
(save-excursion
(goto-char kk/org-latex-fragment-last)
(when (kk/org-in-latex-fragment-p) (org-preview-latex-fragment))
;; now remove current image
(goto-char begin)
(let ((ov (loop for ov in (org--list-latex-overlays)
if
(and
(<= (overlay-start ov) (point))
(>= (overlay-end ov) (point)))
return ov)))
(when ov
(delete-overlay ov)))
;; and save new fragment
(setq kk/org-latex-fragment-last begin)))
;; were on a fragment and now are not on a fragment
((and
;; not on a fragment now
(not begin)
;; but we were on one
kk/org-latex-fragment-last)
;; put image back on, provided that there is still a fragment here.
(save-excursion
(goto-char kk/org-latex-fragment-last)
(when (kk/org-in-latex-fragment-p) (org-preview-latex-fragment)))
;; unset last fragment
(setq kk/org-latex-fragment-last nil))
;; were not on a fragment, and now are
((and
;; we were not one one
(not kk/org-latex-fragment-last)
;; but now we are
begin)
;; remove image
(save-excursion
(goto-char begin)
(let ((ov (loop for ov in (org--list-latex-overlays)
if
(and
(<= (overlay-start ov) (point))
(>= (overlay-end ov) (point)))
return ov)))
(when ov
(delete-overlay ov))))
(setq kk/org-latex-fragment-last begin))))))