Automatic latex image toggling when cursor is on a fragment

| categories: orgmode | tags:

There was a recent suggestion on the org-mode mailing list to make it possible to toggle individual equations in org-mode when the cursor is on them, and have them toggle back when the mouse is off. Presumably, this would let you edit the equation and see the result very easily.

The strategy to enable this is to use add a function to the post-command function hook. The function will store the last fragment/environment you were on, and compare it to where you are now. If they are different the function will put the overlay back on the previous point, and do something appropriate at the current point, e.g. nothing if you are not on a fragment, or remove the overlay of the fragment you are on. The function will get run after every command, so we make sure we are in org-mode first!

Here are some example equations.

Here is a sentence with an equation \(f^{2x}=3\) and another form \(e^x = 20\) in it.

Here is a standalone equation environment.

\begin{equation} a + b \sqrt{5o} \end{equation}

And now, \[15 + 7 = 12\],

It is easiest to see this in a video here: https://www.youtube.com/watch?v=E0s3PDBqsEc

(defvar org-latex-fragment-last nil
  "Holds last fragment/environment you were on.")

(defun org-latex-fragment-toggle ()
  "Toggle a latex fragment image "
  (and (eq 'org-mode major-mode)
       (let* ((el (org-element-context))
              (el-type (car el)))
         (cond
          ;; were on a fragment and now on a new fragment
          ((and
            ;; fragment we were on
            org-latex-fragment-last
            ;; and are on a fragment now
            (or
             (eq 'latex-fragment el-type)
             (eq 'latex-environment el-type))
            ;; 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 (= (org-element-property :begin el)
                    (org-element-property :begin org-latex-fragment-last))))
           ;; go back to last one and put image back
           (save-excursion
             (goto-char (org-element-property :begin org-latex-fragment-last))
             (org-preview-latex-fragment))
           ;; now remove current image
           (goto-char (org-element-property :begin el))
           (let ((ov (loop for ov in org-latex-fragment-image-overlays
                           if
                           (and
                            (<= (overlay-start ov) (point))
                            (>= (overlay-end ov) (point)))
                           return ov)))
             (when ov
               (delete-overlay ov)))
           ;; and save new fragment
           (setq org-latex-fragment-last el))

          ;; were on a fragment and now are not on a fragment
          ((and
            ;; not on a fragment now
            (not (or
                  (eq 'latex-fragment el-type)
                  (eq 'latex-environment el-type)))
            ;; but we were on one
            org-latex-fragment-last)
           ;; put image back on
           (save-excursion
             (goto-char (org-element-property :begin org-latex-fragment-last))
             (org-preview-latex-fragment))
           ;; unset last fragment
           (setq org-latex-fragment-last nil))

          ;; were not on a fragment, and now are
          ((and
            ;; we were not one one
            (not org-latex-fragment-last)
            ;; but now we are
            (or
             (eq 'latex-fragment el-type)
             (eq 'latex-environment el-type)))
           (goto-char (org-element-property :begin el))
           ;; remove image
           (let ((ov (loop for ov in org-latex-fragment-image-overlays
                           if
                           (and
                            (<= (overlay-start ov) (point))
                            (>= (overlay-end ov) (point)))
                           return ov)))
             (when ov
               (delete-overlay ov)))
           (setq org-latex-fragment-last el))))))


(add-hook 'post-command-hook 'org-latex-fragment-toggle)
org-latex-fragment-toggle matlab-start-block-highlight-timer eldoc-schedule-timer

I think there could be another way to do this with text properties, e.g. point-left and point-entered, but that would require those properties to be set on the fragments. I might try that approach another day.

Copyright (C) 2015 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.10

Discuss on Twitter