This is another follow-up bug-report/patch from:
https://lists.gnu.org/archive/html/auctex-devel/2025-08/msg00026.html

and continuing the discussion from bug#79708 (Thanks Paul, Arash and
Ikumi for your help, support and comments on this feature-set).

This patch implements an extensible automatic preview mode based on
ideas by Paul in preview-auto. I wasn't sure what to call it, since I
didn't want it to clash with Paul's package. I thought of
preview-automatic or preview-live and went with the first one for now.
Let me know your thoughts.

Out of the box, enabling preview-automatic-mode would cause the previews
to be auto-updated when modified. Setting `preview-automatic-function`
to #'texmathp would further cause newly written math expressions to be
previewed as well. preview-automatic-delay can be customized to reduce
the frequency of previewing through debouncing. I am using the new
timeout package when available, and implementing the debouncing of
previewing manually otherwise.

Any comments on the design, approach or lisp-fu or welcome.

One potential point for discussion is when to call the previewing
function. I am currently doing that on modification rather than
post-command-hook to reduce unnecessary calls. This does mean that if
the user continuously types on a preview, then quickly (before the
debouncing delay passes) moves to another preview and continues typing
there before updating the first one. The first preview is not
auto-updated. I find this a good compromise to reduce the overhead, but
I am happy to change it back to post-command-hook if there is
disagreement (I do think the overhead is minimal).

On 31/03/2026, Arash Esbati wrote:
> Can you please also make a suggestion for NEWS.org? I'd then release
> AUCTeX 14.2.0. TIA.

@Arash, can we hold off releasing 14.2.0 until this patch is considered?
It would complete the basic feature set of the previewing workflow that
I am currently using and I think many people would find useful.

There is one more improvement I am planning to propose (changing the
preview face when disabled -- mostly relevant for svg previews), but it
can be postponed after releasing 14.2.0 if needed.

-- Al
>From 6052b60bfb95d0c2f734ad2070060cf11adec440 Mon Sep 17 00:00:00 2001
From: Al Haji-Ali <[email protected]>
Date: Tue, 31 Mar 2026 14:13:50 +0100
Subject: [PATCH] New feature: preview-automatic

Code derived from preview-auto.el (Copyright 2024 FSF, by Paul
D. Nelson), adapted and integrated here.

* doc/preview-latex.texi: Add docs for preview-automatic
* preview.el (preview-silent-errors): New local variable.
(preview-log-error, preview-reraise-error): Silence errors
conditionally.
(preview-automatic-function, preview-automatic-delay): New user options
(preview-handle-modification): Update previews automatically.
(preview-automatic-mode): New mode.
(preview-automatic--update-1, preview-automatic-update,
preview-automatic--after-change): New function.
---
 doc/preview-latex.texi |   3 +
 preview.el             | 129 +++++++++++++++++++++++++++++++++++++++--
 2 files changed, 128 insertions(+), 4 deletions(-)

diff --git a/doc/preview-latex.texi b/doc/preview-latex.texi
index 0f3c63b1..a38d030d 100644
--- a/doc/preview-latex.texi
+++ b/doc/preview-latex.texi
@@ -481,6 +481,9 @@ math (@code{$@dots{}$}), or if your usage of @code{$} conflicts with
 @samp{Preview Latex} group, remove @code{textmath} from
 @code{preview-default-option-list} by customizing this variable.
 
+See also @code{preview-automatic-mode} to enable automatic updating or
+generation of previews.
+
 @item Customize where previews are shown
 
 Set @code{preview-visibility-style} to control when previews appear:
diff --git a/preview.el b/preview.el
index 37da8f1d..43ad9a6e 100644
--- a/preview.el
+++ b/preview.el
@@ -372,6 +372,10 @@ See also `preview-gs-command'."
   "List of overlays to convert using gs.
 Buffer-local to the appropriate TeX process buffer.")
 
+(defvar-local preview-silent-errors nil
+  "When non-nil, do not signal preview errors nor display output buffer.
+This variable should be set in the process buffer.")
+
 (defvar-local preview-gs-outstanding nil
   "Overlays currently processed.")
 
@@ -699,7 +703,8 @@ is to be used."
         (insert-before-markers
          (format "%s: %s\n"
                  context (error-message-string err)))
-        (display-buffer (current-buffer)))))
+        (unless preview-silent-errors
+          (display-buffer (current-buffer))))))
   (setq preview-error-condition err))
 
 (defun preview-reraise-error (&optional process)
@@ -708,7 +713,11 @@ Makes sure that PROCESS is removed from the \"Compilation\"
 tag in the mode line."
   (when preview-error-condition
     (unwind-protect
-        (signal (car preview-error-condition) (cdr preview-error-condition))
+        (unless (buffer-local-value 'preview-silent-errors
+                                    (or (process-buffer process)
+                                        (current-buffer)))
+          (signal (car preview-error-condition)
+                  (cdr preview-error-condition)))
       (setq preview-error-condition nil
             compilation-in-progress (delq process compilation-in-progress)))))
 
@@ -2173,11 +2182,15 @@ for definition of OV, AFTER-CHANGE, BEG, END and LENGTH."
     (preview-register-change ov)))
 
 (defun preview-handle-modification
-  (ov after-change _beg _end &optional _length)
+    (ov after-change _beg _end &optional _length)
   "Hook function for `modification-hooks' property.
 See info node `(elisp) Overlay Properties' for
 definition of OV, AFTER-CHANGE, BEG, END and LENGTH."
-  (unless after-change
+  (if after-change
+      (when (buffer-local-value 'preview-automatic-mode
+                                (overlay-buffer ov))
+        (preview-automatic-update (overlay-buffer ov)
+                                  (overlay-start ov)))
     (preview-register-change ov)))
 
 (defun preview--string (ov use-icon helpstring &optional click1)
@@ -4675,6 +4688,114 @@ See `preview-at-point-placement'."))))
         (set-frame-parameter old-frame 'auctex-preview nil)
         (buframe-disable old-frame)))))
 
+;;; preview-automatic -- Auto-preview
+
+(defcustom preview-automatic-function nil
+  "Function to determine if a preview should be created at point.
+
+When `preview-automatic-mode' is enabled and this variable is non-nil,
+it should be a function that is is called with no arguments at (point)
+when there is not a preview already.  If the function returns a non-nil
+value, `preview-at-point' will be called.
+
+If the function returns a cons, it should be the (BEGIN . END), which
+will be used as arguments for `preview-region'."
+  :type 'symbol
+  :group 'preview-latex
+  :package-version '(auctex . "14.2.0"))
+
+;;;###autoload
+(define-minor-mode preview-automatic-mode
+  "Enable automatic refreshing and generation of previews.
+When enabled, existing previews are automatically updated when their
+text is changed.  Moreover, previews are automatically created whenever
+`preview-automatic-function' returns a non-nil value at point."
+  :group 'preview-latex
+  :init-value nil
+  (if preview-automatic-mode
+      (progn
+        (add-hook 'after-change-functions
+                  #'preview-automatic--after-change nil t)
+        (preview-automatic-update (current-buffer) (point)))
+    (remove-hook 'after-change-functions
+                 #'preview-automatic--after-change t)))
+
+(defcustom preview-automatic-delay 0.1
+  "Delay in seconds for automatic preview timer."
+  :type 'number
+  :group 'preview-latex
+  :package-version '(auctex . "14.2.0"))
+
+(defun preview-automatic--update-1 (buffer pt &optional new-only)
+  "Update preview at PT in BUFFER."
+  (when (buffer-live-p buffer)
+    (with-current-buffer buffer
+      (if-let* ((cur-process
+                 (or (get-buffer-process (TeX-process-buffer-name
+                                          (TeX-region-file)))
+                     (get-buffer-process (TeX-process-buffer-name
+                                          (TeX-master-file))))))
+          (progn
+            (when (and preview-current-region (not preview-abort-flag))
+              (progn
+                (ignore-errors (TeX-kill-job))
+                (setq preview-abort-flag t)))
+            (with-local-quit (accept-process-output cur-process))
+            ;; Assume that `preview-automatic-update' is debounced,
+            ;; otherwise this call may cause an infinite loop.
+            (preview-automatic-update buffer pt new-only))
+        (when-let* ((region
+                     (save-excursion
+                       (goto-char pt)
+                       (let* ((cur (cl-find-if
+                                    (lambda (ov)
+                                      (eq (overlay-get ov 'cateogry)
+                                          'preview-overlay))
+                                    (overlays-at (point))))
+                              (region (or
+                                       (and (not new-only) cur)
+                                       (and (not cur)
+                                            preview-automatic-function
+                                            (funcall
+                                             preview-automatic-function)))))
+                         (when region
+                           (if (consp region)
+                               region
+                             (cons (preview-next-border t)
+                                   (preview-next-border nil))))))))
+          (let ((TeX-suppress-compilation-message t)
+                (save-silently t)
+                (noninteractive t)
+                (inhibit-message t)
+                message-log-max)
+            (let* ((process (preview-region (car region) (cdr region))))
+              (with-current-buffer (process-buffer process)
+                (setq-local preview-silent-errors t)))))))))
+
+(if (require 'timeout nil t)
+    (defalias 'preview-automatic-update
+      (timeout-debounced-func 'preview-automatic--update-1
+                              'preview-automatic-delay))
+  ;; `timeout' is not available, define debounced
+  ;; `preview-automatic-update' manually.
+  (defvar preview-automatic--update-timer nil
+    "Timer used for debouncing preview-automatic-update.")
+  (defun preview-automatic-update (buffer pt &optional new-only)
+    (:documentation (documentation 'preview-automatic--update-1))
+    (when preview-automatic--update-timer
+      (cancel-timer preview-automatic--update-timer)
+      (setq preview-automatic--update-timer nil))
+
+    (setq preview-automatic--update-timer
+          (run-with-idle-timer
+           preview-automatic-delay nil
+           #'preview-automatic--update-1
+           buffer pt new-only))))
+
+(defun preview-automatic--after-change (&rest _)
+  "Ensure a preview is created after change, if needed."
+  (preview-automatic-update (current-buffer) (point) t))
+
 ;;;###autoload
 (defun preview-report-bug () "Report a bug in the preview-latex package."
        (interactive)
-- 
2.50.1 (Apple Git-155)

_______________________________________________
bug-auctex mailing list
[email protected]
https://lists.gnu.org/mailman/listinfo/bug-auctex

Reply via email to