Subject: Re: bug#39927: 12.2; preview-latex gs treatment off by one

From: [email-masked]
Date: Thu, 12 Mar 2020 09:51:14 +0100 (CET)

Hi Ikumi

I spent some time digging into the internals of preview-latex, even though I do not speak elisp wery well...

The crucial part happens inside preview-gs-transact, which gets called each time GS has written something to the standard output (usually  GS>  or  GS<1>)

As I wrote in a previous message, adding some debug prints to the beginning of preview-gs-transact shows that you need to count the number of GS<1> prompts in order to guess when a new file is ready.
Current code just counts all prompts, skipping one, so with two GS> prompts at the beginning it is off by one.

The attached patch, based on that observation, solves the problem entirely.

Again, as I said in all my previous messages, I DO NOT UNDERSTAND ghostscript, I am just making empiric observations regarding the correlation between what GS writes to stdout and what seems to happen.
These observations seem to be consistent regardless of the color compatibility option you choose or the number of files you feed into GS at a time (but only tested with 9.50)

Cheers,
Itaï

--- auctex-12.2.orig/preview.el.in
+++ auctex-12.2/preview.el.in
@@ -1318,88 +1318,89 @@ This routine is the action routine calle
 The Ghostscript process buffer of PROCESS will already be selected, and
 and the standard output of Ghostscript up to the next prompt will be
 given as ANSWER."
-  (let ((ov (pop preview-gs-outstanding))
-	(have-error (not
-		     (string-match "\\`GS\\(<[0-9]+\\)?>\\'" answer ))))
-    (when (and ov (overlay-buffer ov))
-      (let ((queued (overlay-get ov 'queued)))
-	(when queued
-	  (let* ((bbox (aref queued 0))
-		 (filenames (overlay-get ov 'filenames))
-		 (oldfile (nth 0 filenames))
-		 (newfile (nth 1 filenames)))
-	    (if have-error
-		(preview-gs-flag-error ov answer)
-	      (condition-case nil
-		  (preview-delete-file oldfile)
-		(file-error nil))
-	      (overlay-put ov 'filenames (cdr filenames))
-	      (preview-replace-active-icon
-	       ov
-	       (preview-create-icon (car newfile)
-				    preview-gs-image-type
-				    (preview-ascent-from-bb
-				     bbox)
-				    (aref preview-colors 2))))
-	    (overlay-put ov 'queued nil)))))
-    (while (and (< (length preview-gs-outstanding)
-		   preview-gs-outstanding-limit)
-		(setq ov (pop preview-gs-queue)))
-      (let ((queued (overlay-get ov 'queued)))
-	(when (and queued
-		   (not (memq ov preview-gs-outstanding))
-		   (overlay-buffer ov))
-	  (let* ((filenames (overlay-get ov 'filenames))
-		 (oldfile (car (nth 0
-				    (nconc filenames
-					   (list
-					    (preview-make-filename
-					     (format "pr%d-%d.%s"
-						     (car preview-gs-sequence)
-						     (cdr preview-gs-sequence)
-						     preview-gs-image-type)
-					     TeX-active-tempdir))))))
-		 (bbox (aset queued 0
-			     (or (and preview-prefer-TeX-bb
-				      (aref queued 0))
-				 (and (stringp oldfile)
-				      (preview-extract-bb
-				       oldfile))
-				 (aref queued 0)
-				 (error "No bounding box"))))
-		 (snippet (aref queued 2))
-		 (gs-line
-		  (format
-		   "%s<<%s>>preview-do\n"
-		   (if preview-ps-file
-		       (concat "dup "
-			       (preview-gs-dsc-cvx
-				snippet
-				preview-gs-dsc))
-		     (format "%s(r)file cvx"
-			     (preview-ps-quote-filename
-			      (if (listp oldfile)
-				  (car (last oldfile))
-				oldfile))))
-		   (if preview-parsed-tightpage
-		       ""
-		     (format "/PageSize[%g %g]/PageOffset[%g \
+  (while (and (< (length preview-gs-outstanding)
+		 preview-gs-outstanding-limit)
+	      (setq ov (pop preview-gs-queue)))
+    (let ((queued (overlay-get ov 'queued)))
+      (when (and queued
+		 (not (memq ov preview-gs-outstanding))
+		 (overlay-buffer ov))
+	(let* ((filenames (overlay-get ov 'filenames))
+	       (oldfile (car (nth 0
+				  (nconc filenames
+					 (list
+					  (preview-make-filename
+					   (format "pr%d-%d.%s"
+						   (car preview-gs-sequence)
+						   (cdr preview-gs-sequence)
+						   preview-gs-image-type)
+					   TeX-active-tempdir))))))
+	       (bbox (aset queued 0
+			   (or (and preview-prefer-TeX-bb
+				    (aref queued 0))
+			       (and (stringp oldfile)
+				    (preview-extract-bb
+				     oldfile))
+			       (aref queued 0)
+			       (error "No bounding box"))))
+	       (snippet (aref queued 2))
+	       (gs-line
+		(format
+		 "%s<<%s>>preview-do\n"
+		 (if preview-ps-file
+		     (concat "dup "
+			     (preview-gs-dsc-cvx
+			      snippet
+			      preview-gs-dsc))
+		   (format "%s(r)file cvx"
+			   (preview-ps-quote-filename
+			    (if (listp oldfile)
+				(car (last oldfile))
+			      oldfile))))
+		 (if preview-parsed-tightpage
+		     ""
+		   (format "/PageSize[%g %g]/PageOffset[%g \
 %g[1 1 dtransform exch]{0 ge{neg}if exch}forall]"
-			     (- (aref bbox 2) (aref bbox 0))
-			     (- (aref bbox 3) (aref bbox 1))
-			     (aref bbox 0) (aref bbox 1))))))
-	    (setcdr preview-gs-sequence (1+ (cdr preview-gs-sequence)))
-	    (setq preview-gs-outstanding
-		  (nconc preview-gs-outstanding
-			 (list ov)))
-	    (aset queued 1 gs-line)
-	    ;; ignore errors because of dying processes: they will get
-	    ;; caught by the sentinel, anyway.
-	    (condition-case nil
-		(process-send-string
-		 process
-		 gs-line)
-	      (error nil))))))
+			   (- (aref bbox 2) (aref bbox 0))
+			   (- (aref bbox 3) (aref bbox 1))
+			   (aref bbox 0) (aref bbox 1))))))
+	  (setcdr preview-gs-sequence (1+ (cdr preview-gs-sequence)))
+	  (setq preview-gs-outstanding
+		(nconc preview-gs-outstanding
+		       (list ov)))
+	  (aset queued 1 gs-line)
+	  ;; ignore errors because of dying processes: they will get
+	  ;; caught by the sentinel, anyway.
+	  (condition-case nil
+	      (process-send-string
+	       process
+	       gs-line)
+	    (error nil))))))
+  (when (not (string-match "\\`GS>\\'" answer))
+    (let ((ov (pop preview-gs-outstanding))
+	  (have-error (not
+		       (string-match "\\`GS\\(<[0-9]+\\)?>\\'" answer))))
+      (when (and ov (overlay-buffer ov))
+	(let ((queued (overlay-get ov 'queued)))
+	  (when queued
+	    (let* ((bbox (aref queued 0))
+		   (filenames (overlay-get ov 'filenames))
+		   (oldfile (nth 0 filenames))
+		   (newfile (nth 1 filenames)))
+	      (if have-error
+		  (preview-gs-flag-error ov answer)
+		(condition-case nil
+		    (preview-delete-file oldfile)
+		  (file-error nil))
+		(overlay-put ov 'filenames (cdr filenames))
+		(preview-replace-active-icon
+		 ov
+		 (preview-create-icon (car newfile)
+				      preview-gs-image-type
+				      (preview-ascent-from-bb
+				       bbox)
+				      (aref preview-colors 2))))
+	      (overlay-put ov 'queued nil))))))
     (unless preview-gs-outstanding
       (condition-case nil
 	  (process-send-eof process)