diff options
author | mattkae <mattkae@protonmail.com> | 2022-05-17 07:07:37 -0400 |
---|---|---|
committer | mattkae <mattkae@protonmail.com> | 2022-05-17 07:07:37 -0400 |
commit | becff06c71d277647eda4378203d03ab36e141eb (patch) | |
tree | a1f73bba3676f34e0faf76764f5de963321f5576 /elpa/auctex-13.1.3/preview.el | |
parent | 3f4a0d5370ae6c34afe180df96add3b8522f4af1 (diff) |
Evil mode and latex support
Diffstat (limited to 'elpa/auctex-13.1.3/preview.el')
-rw-r--r-- | elpa/auctex-13.1.3/preview.el | 4288 |
1 files changed, 4288 insertions, 0 deletions
diff --git a/elpa/auctex-13.1.3/preview.el b/elpa/auctex-13.1.3/preview.el new file mode 100644 index 0000000..ad48b09 --- /dev/null +++ b/elpa/auctex-13.1.3/preview.el @@ -0,0 +1,4288 @@ +;;; preview.el --- embed preview LaTeX images in source buffer -*- lexical-binding: t; -*- + +;; Copyright (C) 2001-2022 Free Software Foundation, Inc. + +;; Author: David Kastrup +;; Keywords: tex, wp, convenience + +;; This file is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 3, or (at your option) +;; any later version. + +;; This file is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs; see the file COPYING. If not, write to +;; the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +;; Boston, MA 02110-1301, USA. + +;;; Commentary: + +;; This style is for the "seamless" embedding of generated images +;; into LaTeX source code. Please see the README and INSTALL files +;; for further instruction. +;; +;; Please use the usual configure script for installation: more than +;; just Elisp files are involved: a LaTeX style, icon files, startup +;; code and so on. +;; +;; Quite a few things with regard to preview-latex's operation can be +;; configured by using +;; M-x customize-group RET preview RET +;; +;; Please report bugs with M-x preview-report-bug RET. + +;;; Code: + +(require 'tex-site) +(require 'tex) +(require 'latex) + +(eval-when-compile + (condition-case nil + (require 'desktop) + (file-error (message "Missing desktop package: +preview-latex buffers will not survive across sessions."))) + (condition-case nil + (require 'reporter) + (file-error (message "Missing reporter library, probably from the mail-lib package: +preview-latex's bug reporting commands will probably not work."))) + (require 'info)) + +(defgroup preview nil "Embed Preview images into LaTeX buffers." + :group 'AUCTeX + :prefix "preview-" + :link '(custom-manual "(preview-latex)Top") + :link '(info-link "(preview-latex)The Emacs interface") + :link '(url-link :tag "Homepage" "https://www.gnu.org/software/auctex/")) + +(defgroup preview-gs nil "Preview's Ghostscript renderer." + :group 'preview + :prefix "preview-") + +(defgroup preview-appearance nil "Preview image appearance." + :group 'preview + :prefix "preview-") + +(defconst preview-specs-type + '(repeat + (list :tag "Image spec" + ;; Use an extra :value keyword to avoid a bug in + ;; `widget-convert' of XEmacs 21.4 and Emacs 21. + ;; Analogously for the following `const' statements. + (const :format "" :value :type) + (choice :tag "Image type" + (const xpm) + (const xbm) + (symbol :tag "Other")) + (set :inline t :tag "Minimum font size" + (list :inline t :tag "" + (const :format "" :value :min) + (integer :tag "pixels"))) + (const :format "" :value :file) (string :tag "Filename") + (set :inline t :tag "Ascent ratio" + (list :inline t :tag "" + (const :format "" :value :ascent) + (integer :tag "percent of image" + :value 50)))))) + +(defun preview-specs-setter (symbol value) + "Set SYMBOL to VALUE and clear `preview-min-alist' property. +This is used in icon specs, so that customizing will +clear cached icons." + (put symbol 'preview-min-alist nil) + (set-default symbol value)) + +(defcustom preview-nonready-icon-specs + '((:type xpm :min 26 :file "prvwrk24.xpm" :ascent 90) + (:type xpm :min 22 :file "prvwrk20.xpm" :ascent 90) + (:type xpm :min 17 :file "prvwrk16.xpm" :ascent 90) + (:type xpm :min 15 :file "prvwrk14.xpm" :ascent 90) + (:type xpm :file "prvwrk12.xpm" :ascent 90) + (:type xbm :file "prvwrk24.xbm" :ascent 90)) + "The icon used for previews to be generated. +The spec must begin with `:type'. File names are relative to +`load-path' and `data-directory', a spec `:min' requires a +minimal pixel height for `preview-reference-face' before the spec +will be considered. Since evaluating the `:file' spec takes +considerable time under XEmacs, it should come after the `:min' +spec to avoid unnecessary evaluation time." + :group 'preview-appearance + :type preview-specs-type + :set #'preview-specs-setter) + +(defvar preview-nonready-icon nil + "The icon used for previews to be generated. +Suitable spec is chosen from `preview-nonready-icon-specs'.") + +(defcustom preview-error-icon-specs + '((:type xpm :min 22 :file "prverr24.xpm" :ascent 90) + (:type xpm :min 18 :file "prverr20.xpm" :ascent 90) + (:type xpm :file "prverr16.xpm" :ascent 90) + (:type xbm :file "prverr24.xbm" :ascent 90)) + "The icon used for PostScript errors. +The spec must begin with `:type'. File names are relative to +`load-path' and `data-directory', a spec `:min' requires a +minimal pixel height for `preview-reference-face' before the spec +will be considered. Since evaluating the `:file' spec takes +considerable time under XEmacs, it should come after the `:min' +spec to avoid unnecessary evaluation time." + :group 'preview-appearance + :type preview-specs-type + :set #'preview-specs-setter +) + +(defvar preview-error-icon nil + "The icon used for PostScript errors. +Suitable spec is chosen from `preview-error-icon-specs'.") + +(defcustom preview-icon-specs + '((:type xpm :min 24 :file "prvtex24.xpm" :ascent 75) + (:type xpm :min 20 :file "prvtex20.xpm" :ascent 75) + (:type xpm :min 16 :file "prvtex16.xpm" :ascent 75) + (:type xpm :file "prvtex12.xpm" :ascent 75) + (:type xbm :min 24 :file "prvtex24.xbm" :ascent 75) + (:type xbm :min 16 :file "prvtex16.xbm" :ascent 75) + (:type xbm :file "prvtex12.xbm" :ascent 75)) + "The icon used for an open preview. +The spec must begin with `:type'. File names are relative to +`load-path' and `data-directory', a spec `:min' requires a +minimal pixel height for `preview-reference-face' before the spec +will be considered. Since evaluating the `:file' spec takes +considerable time under XEmacs, it should come after the `:min' +spec to avoid unnecessary evaluation time." + :group 'preview-appearance + :type preview-specs-type + :set #'preview-specs-setter) + +(defvar preview-icon nil + "The icon used for an open preview. +Suitable spec is chosen from `preview-icon-specs'.") + +(defgroup preview-latex nil "LaTeX options for preview." + :group 'preview + :prefix "preview-") + +(defcustom preview-image-creators + '((dvipng + (open preview-gs-open preview-dvipng-process-setup) + (place preview-gs-place) + (close preview-dvipng-close)) + (png (open preview-gs-open) + (place preview-gs-place) + (close preview-gs-close)) + (jpeg (open preview-gs-open) + (place preview-gs-place) + (close preview-gs-close)) + (pnm (open preview-gs-open) + (place preview-gs-place) + (close preview-gs-close)) + (tiff (open preview-gs-open) + (place preview-gs-place) + (close preview-gs-close))) + "Define functions for generating images. +These functions get called in the process of generating inline +images of the specified type. The open function is called +at the start of a rendering pass, the place function for +placing every image, the close function at the end of +the pass. Look at the documentation of the various +functions used here for the default settings, and at +the function `preview-call-hook' through which those are +called. Additional argument lists specified in here +are passed to the functions before any additional +arguments given to `preview-call-hook'. + +Not all of these image types may be supported by your copy +of Ghostscript, or by your copy of Emacs." + :group 'preview-gs + :type '(alist :key-type (symbol :tag "Preview's image type") + :value-type + (alist :tag "Handler" :key-type (symbol :tag "Operation:") + :value-type (list :tag "Handler" + (function :tag "Handler function") + (repeat :tag "Additional \ +function args" :inline t sexp)) + :options (open place close)))) + +(defcustom preview-gs-image-type-alist + '((png png "-sDEVICE=png16m") + (dvipng png "-sDEVICE=png16m") + (jpeg jpeg "-sDEVICE=jpeg") + (pnm pbm "-sDEVICE=pnmraw") + (tiff tiff "-sDEVICE=tiff12nc")) + "Alist of image types and corresponding Ghostscript options. +The `dvipng' and `postscript' (don't use) entries really specify +a fallback device when images can't be processed by the requested +method, like when PDFTeX was used." + :group 'preview-gs + :type '(repeat (list :tag nil (symbol :tag "preview image-type") + (symbol :tag "Emacs image-type") + (repeat :inline t :tag "Ghostscript options" string)))) + +(defcustom preview-image-type 'png + "Image type to be used in images." + :group 'preview-gs + :type (append '(choice) + (mapcar (lambda (symbol) (list 'const (car symbol))) + preview-image-creators) + '((symbol :tag "Other")))) + +(defun preview-call-hook (symbol &rest rest) + "Call a function from `preview-image-creators'. +This looks up SYMBOL in the `preview-image-creators' entry +for the image type `preview-image-type' and calls the +hook function given there with the arguments specified there +followed by REST. If such a function is specified in there, +that is." + (let ((hook (cdr (assq symbol + (cdr (assq preview-image-type + preview-image-creators)))))) + (when hook + (apply (car hook) (append (cdr hook) rest))))) + + +(defvar TeX-active-tempdir nil + "List of directory name, top directory name and reference count.") +(make-variable-buffer-local 'TeX-active-tempdir) + +(defcustom preview-bb-filesize 1024 + "Size of file area scanned for bounding box information." + :group 'preview-gs :type 'integer) + +(defcustom preview-preserve-indentation t + "Whether to keep additional whitespace at the left of a line." + :group 'preview-appearance :type 'boolean) + +(defun preview-extract-bb (filename) + "Extract EPS bounding box vector from FILENAME." + (with-temp-buffer + (insert-file-contents-literally filename nil 0 preview-bb-filesize + t) + (goto-char (point-min)) + (when (search-forward-regexp "%%BoundingBox:\ + +\\([-+]?[0-9.]+\\)\ + +\\([-+]?[0-9.]+\\)\ + +\\([-+]?[0-9.]+\\)\ + +\\([-+]?[0-9.]+\\)" nil t) + (vector + (if preview-preserve-indentation + (min 72 (string-to-number (match-string 1))) + (string-to-number (match-string 1))) + (string-to-number (match-string 2)) + (string-to-number (match-string 3)) + (string-to-number (match-string 4)) + )))) + +(defcustom preview-prefer-TeX-bb nil + "Prefer TeX bounding box to EPS one if available. +If `preview-fast-conversion' is set, this option is not + consulted since the TeX bounding box has to be used anyway." + :group 'preview-gs + :type 'boolean) + +(defcustom preview-TeX-bb-border 0.5 + "Additional space in pt around Bounding Box from TeX." + :group 'preview-gs + :type 'number) + +(defvar preview-parsed-font-size nil + "Font size as parsed from the log of LaTeX run.") +(make-variable-buffer-local 'preview-parsed-font-size) +(defvar preview-parsed-magnification nil + "Magnification as parsed from the log of LaTeX run.") +(make-variable-buffer-local 'preview-parsed-magnification) +(defvar preview-parsed-pdfoutput nil + "PDFoutput as parsed from the log of LaTeX run.") +(make-variable-buffer-local 'preview-parsed-pdfoutput) +(defvar preview-parsed-counters nil + "Counters as parsed from the log of LaTeX run.") +(make-variable-buffer-local 'preview-parsed-counters) +(defvar preview-parsed-tightpage nil + "Tightpage as parsed from the log of LaTeX run.") +(make-variable-buffer-local 'preview-parsed-tightpage) + +(defun preview-get-magnification () + "Get magnification from `preview-parsed-magnification'." + (if preview-parsed-magnification + (/ preview-parsed-magnification 1000.0) 1.0)) + +(defun preview-TeX-bb (list) + "Calculate bounding box from (ht dp wd). +LIST consists of TeX dimensions in sp (1/65536 TeX point)." + (and + (consp list) + (let* ((dims (vconcat (mapcar + #'(lambda (x) + (/ x 65781.76)) + list))) + (box + (vector + (+ 72 (min 0 (aref dims 2))) + (+ 720 (min (aref dims 0) (- (aref dims 1)) 0)) + (+ 72 (max 0 (aref dims 2))) + (+ 720 (max (aref dims 0) (- (aref dims 1)) 0)))) + (border (if preview-parsed-tightpage + (vconcat (mapcar + #'(lambda(x) + (/ x 65781.76)) + preview-parsed-tightpage)) + (vector (- preview-TeX-bb-border) + (- preview-TeX-bb-border) + preview-TeX-bb-border + preview-TeX-bb-border)))) + (dotimes (i 4) + (aset box i (+ (aref box i) (aref border i)))) + box))) + +(defcustom preview-gs-command + (or ;; The GS wrapper coming with TeX Live + (executable-find "rungs") + ;; The MikTeX builtin GS + (let ((gs (executable-find "mgs"))) + ;; Check if mgs is functional for external non-MikTeX apps. + ;; See http://blog.miktex.org/post/2005/04/07/Starting-mgsexe-at-the-DOS-Prompt.aspx + (when (and gs (= 0 (shell-command (concat (shell-quote-argument gs) " -q -dNODISPLAY -c quit")))) + gs)) + ;; Windows ghostscript + (executable-find "GSWIN32C.EXE") + ;; standard GhostScript + (executable-find "gs")) + "How to call gs for conversion from EPS. See also `preview-gs-options'." + :group 'preview-gs + :type 'string) + +(defcustom preview-gs-options '("-q" "-dDELAYSAFER" "-dNOPAUSE" + "-DNOPLATFONTS" "-dPrinted" + "-dTextAlphaBits=4" + "-dGraphicsAlphaBits=4") + "Options with which to call gs for conversion from EPS. +See also `preview-gs-command'." + :group 'preview-gs + :type '(repeat string)) + +(defvar preview-gs-queue nil + "List of overlays to convert using gs. +Buffer-local to the appropriate TeX process buffer.") +(make-variable-buffer-local 'preview-gs-queue) + +(defvar preview-gs-outstanding nil + "Overlays currently processed.") +(make-variable-buffer-local 'preview-gs-outstanding) + +(defcustom preview-gs-outstanding-limit 2 + "Number of requests allowed to be outstanding. +This is the number of not-yet-completed requests we +might at any time have piped into Ghostscript. If +this number is larger, the probability of Ghostscript +working continuously is higher when Emacs is rather +busy. If this number is smaller, redisplay will +follow changes in the displayed buffer area faster." + :group 'preview-gs + :type '(restricted-sexp + :match-alternatives + ((lambda (value) (and + (integerp value) + (> value 0) + (< value 10)))) + :tag "small number")) + +(defvar preview-gs-answer nil + "Accumulated answer of Ghostscript process.") +(make-variable-buffer-local 'preview-gs-answer) + +(defvar preview-gs-image-type nil + "Image type for gs produced images.") +(make-variable-buffer-local 'preview-gs-image-type) + +(defvar preview-gs-sequence nil + "Pair of sequence numbers for gs produced images.") +(make-variable-buffer-local 'preview-gs-sequence) + +(defvar preview-scale nil + "Screen scale of images. +Magnify by this factor to make images blend with other +screen content. Buffer-local to rendering buffer.") +(make-variable-buffer-local 'preview-scale) + +(defvar preview-colors nil + "Color setup list. +An array with elements 0, 1 and 2 for background, +foreground and border colors, respectively. Each element +is a list of 3 real numbers between 0 and 1, or nil +of nothing special should be done for the color") +(make-variable-buffer-local 'preview-colors) + +(defvar preview-gs-init-string nil + "Ghostscript setup string.") +(make-variable-buffer-local 'preview-gs-init-string) + +(defvar preview-ps-file nil + "PostScript file name for fast conversion.") +(make-variable-buffer-local 'preview-ps-file) + +(defvar preview-gs-dsc nil + "Parsed DSC information.") +(make-variable-buffer-local 'preview-gs-dsc) + +(defvar preview-resolution nil + "Screen resolution where rendering started. +Cons-cell of x and y resolution, given in +dots per inch. Buffer-local to rendering buffer.") +(make-variable-buffer-local 'preview-resolution) + +(defun preview-gs-resolution (scale xres yres) + "Generate resolution argument for gs. +Calculated from real-life factor SCALE and XRES and +YRES, the screen resolution in dpi." + (format "-r%gx%g" + (/ (* scale xres) (preview-get-magnification)) + (/ (* scale yres) (preview-get-magnification)))) + +(defun preview-gs-behead-outstanding (err) + "Remove leading element of outstanding queue after error. +Return element if non-nil. ERR is the error string to +show as response of Ghostscript." + (let ((ov (pop preview-gs-outstanding))) + (when ov + (preview-gs-flag-error ov err) + (overlay-put ov 'queued nil)) + ov)) + +(defvar preview-gs-command-line nil) +(make-variable-buffer-local 'preview-gs-command-line) +(defvar preview-gs-file nil) +(make-variable-buffer-local 'preview-gs-file) + +(defcustom preview-fast-conversion t + "Set this for single-file PostScript conversion. +This will have no effect when `preview-image-type' is +set to `postscript'." + :group 'preview-latex + :type 'boolean) + +(defun preview-string-expand (arg &optional separator) + "Expand ARG as a string. +It can already be a string. Or it can be a list, then it is +recursively evaluated using SEPARATOR as separator. If a list +element is in itself a CONS cell, the CAR of the list (after symbol +dereferencing) can evaluate to either a string, in which case it is +used as a separator for the rest of the list, +or a boolean (t or nil) in which case the rest of the list is +either evaluated and concatenated or ignored, respectively. +ARG can be a symbol, and so can be the CDR +of a cell used for string concatenation." + (cond + ((stringp arg) arg) + ((consp arg) + (mapconcat + #'identity + (delq nil + (mapcar + (lambda(x) + (if (consp x) + (let ((sep (car x))) + (while (and (symbolp sep) + (not (memq sep '(t nil)))) + (setq sep (symbol-value sep))) + (if (stringp sep) + (preview-string-expand (cdr x) sep) + (and sep + (preview-string-expand (cdr x))))) + (preview-string-expand x))) + arg)) + (or separator ""))) + ((and (symbolp arg) (not (memq arg '(t nil)))) + (preview-string-expand (symbol-value arg) separator)) + (t (error "Bad string expansion")))) + +(defconst preview-expandable-string + (let ((f (lambda (x) + `(choice + string + (repeat :tag "Concatenate" + (choice + string + (cons :tag "Separated list" + (choice (string :tag "Separator") + (symbol :tag + "Indirect separator or flag")) + ,x) + (symbol :tag "Indirect variable (no separator)"))) + (symbol :tag "Indirect variable (with separator)"))))) + (funcall f (funcall f 'sexp))) + "Type to be used for `preview-string-expand'. +Just a hack until we get to learn how to do this properly. +Recursive definitions are not popular with Emacs, +so we define this type just two levels deep. This +kind of expandible string can either be just a string, or a +cons cell with a separator string in the CAR, and either +an explicit list of elements in the CDR, or a symbol to +be consulted recursively.") + +(defcustom preview-dvipng-command + "dvipng -picky -noghostscript %d -o %m/prev%%03d.png" + "Command used for converting to separate PNG images. + +You might specify options for converting to other image types, +but then you'll need to adapt `preview-dvipng-image-type'." + :group 'preview-latex + :type 'string) + +(defcustom preview-dvipng-image-type + 'png + "Image type that dvipng produces. + +You'll need to change `preview-dvipng-command' too, +if you customize this." + :group 'preview-latex + :type '(choice (const png) + (const gif) + (symbol :tag "Other" :value png))) + +(defcustom preview-dvips-command + "dvips -Pwww -i -E %d -o %m/preview.000" + "Command used for converting to separate EPS images." + :group 'preview-latex + :type 'string) + +(defcustom preview-fast-dvips-command + "dvips -Pwww %d -o %m/preview.ps" + "Command used for converting to a single PS file." + :group 'preview-latex + :type 'string) + +(defcustom preview-pdf2dsc-command + "pdf2dsc %(O?pdf) %m/preview.dsc" + "Command used for generating dsc from a PDF file." + :group 'preview-latex + :type 'string) + +(defun preview-gs-queue-empty () + "Kill off everything remaining in `preview-gs-queue'." + (mapc #'preview-delete preview-gs-outstanding) + (dolist (ov preview-gs-queue) + (if (overlay-get ov 'queued) + (preview-delete ov))) + (setq preview-gs-outstanding nil) + (setq preview-gs-queue nil)) + +(defvar preview-error-condition nil + "Last error raised and to be reported.") + +(defun preview-log-error (err context &optional process) + "Log an error message to run buffer. +ERR is the caught error syndrome, CONTEXT is where it +occured, PROCESS is the process for which the run-buffer +is to be used." + (when (or (null process) (buffer-name (process-buffer process))) + (with-current-buffer (or (and process + (process-buffer process)) + (current-buffer)) + (save-excursion + (goto-char (or (and process + (process-buffer process) + (marker-buffer (process-mark process)) + (process-mark process)) + (point-max))) + (insert-before-markers + (format "%s: %s\n" + context (error-message-string err))) + (display-buffer (current-buffer))))) + (setq preview-error-condition err)) + +(defun preview-reraise-error (&optional process) + "Raise an error that has been logged. +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)) + (setq preview-error-condition nil + compilation-in-progress (delq process compilation-in-progress))))) + +(defcustom preview-pdf-color-adjust-method t + "Method to adjust colors of images generated from PDF. +It is not consulted when the latex command produces DVI files. + +The valid values are: + +t: preview-latex transfers the foreground and background colors +of Emacs to the generated images. This option requires that +Ghostscript has working DELAYBIND feature, thus is invalid with +gs 9.27 (and possibly < 9.27). + +`compatible': preview-latex uses another mothod to transfer +colors. This option is provided for compatibility with older gs. +See the below explanation for detail. + +nil: no adjustment is done and \"black on white\" image is +generated regardless of Emacs color. This is provided for fallback for +gs 9.27 users with customized foreground color. See the below +explanation for detail. + +When the latex command produces PDF rather than DVI and Emacs has +non-trivial foreground color, the traditional method (`compatible') +makes gs >= 9.27 to stop with error. Here, \"non-trivial foreground +color\" includes customized themes. + +If you use such non-trivial foreground color and the version of +Ghostscript equals to 9.27, you have two options: + +- Choose the value `compatible' and customize +`preview-reference-face' to have default (black) foreground +color. This makes the generated image almost non-readable on +dark background, so the next option would be your only choice in +that case. +- Choose the value nil, which forces plain \"black on white\" +appearance for the generated image. You can at least read what +are written in the image although they may not match with your +Emacs color well." + :group 'preview-appearance + :type '(choice + (const :tag "Adjust to Emacs color (gs > 9.27)" t) + (const :tag "Compatibility for gs =< 9.27" compatible) + (const :tag "No adjustment (B/W, for gs 9.27)" nil))) + +(defun preview-gs-sentinel (process string) + "Sentinel function for rendering process. +Gets the default PROCESS and STRING arguments +and tries to restart Ghostscript if necessary." + (condition-case err + (let ((status (process-status process))) + (when (memq status '(exit signal)) + (setq compilation-in-progress (delq process compilation-in-progress))) + (when (buffer-name (process-buffer process)) + (with-current-buffer (process-buffer process) + (goto-char (point-max)) + (insert-before-markers "\n" mode-name " " string) + (forward-char -1) + (insert " at " + (substring (current-time-string) 0 -5)) + (forward-char 1) + (TeX-command-mode-line process) + (when (memq status '(exit signal)) + ;; process died. + ;; Throw away culprit, go on. + (let* ((err (concat preview-gs-answer "\n" + (process-name process) " " string)) + (ov (preview-gs-behead-outstanding err))) + (when (and (null ov) preview-gs-queue) + (save-excursion + (goto-char (if (marker-buffer (process-mark process)) + (process-mark process) + (point-max))) + (insert-before-markers err))) + (delete-process process) + (if (or (null ov) + (eq status 'signal)) + ;; if process was killed explicitly by signal, or if nothing + ;; was processed, we give up on the matter altogether. + (progn + (when preview-ps-file + (condition-case nil + (preview-delete-file preview-ps-file) + (file-error nil))) + (preview-gs-queue-empty)) + + ;; restart only if we made progress since last call + (let (filenames) + (dolist (ov preview-gs-outstanding) + (setq filenames (overlay-get ov 'filenames)) + (condition-case nil + (preview-delete-file (nth 1 filenames)) + (file-error nil)) + (setcdr filenames nil))) + (setq preview-gs-queue (nconc preview-gs-outstanding + preview-gs-queue)) + (setq preview-gs-outstanding nil) + (preview-gs-restart))))))) + (error (preview-log-error err "Ghostscript" process))) + (preview-reraise-error process)) + +(defun preview-gs-filter (process string) + "Filter function for processing Ghostscript output. +Gets the usual PROCESS and STRING parameters, see +`set-process-filter' for a description." + (with-current-buffer (process-buffer process) + (setq preview-gs-answer (concat preview-gs-answer string)) + (while (string-match "GS\\(<[0-9]+\\)?>" preview-gs-answer) + (let* ((pos (match-end 0)) + (answer (substring preview-gs-answer 0 pos))) + (setq preview-gs-answer (substring preview-gs-answer pos)) + (condition-case err + (preview-gs-transact process answer) + (error (preview-log-error err "Ghostscript filter" process)))))) + (preview-reraise-error)) + +(defun preview-gs-restart () + "Start a new Ghostscript conversion process." + (when preview-gs-queue + (if preview-gs-sequence + (setcar preview-gs-sequence (1+ (car preview-gs-sequence))) + (setq preview-gs-sequence (list 1))) + (setcdr preview-gs-sequence 1) + (let* ((process-connection-type nil) + (outfile (format "-sOutputFile=%s" + (file-relative-name + (format "%s/pr%d-%%d.%s" + (car TeX-active-tempdir) + (car preview-gs-sequence) + preview-gs-image-type)))) + (process + (apply #'start-process + "Preview-Ghostscript" + (current-buffer) + preview-gs-command + outfile + preview-gs-command-line))) + (goto-char (point-max)) + (insert-before-markers "Running `Preview-Ghostscript' with ``" + (mapconcat #'shell-quote-argument + (append + (list preview-gs-command + outfile) + preview-gs-command-line) + " ") "''\n") + (setq preview-gs-answer "") + (set-process-query-on-exit-flag process nil) + (set-process-sentinel process #'preview-gs-sentinel) + (set-process-filter process #'preview-gs-filter) + (process-send-string process preview-gs-init-string) + (setq mode-name "Preview-Ghostscript") + (push process compilation-in-progress) + (TeX-command-mode-line process) + (force-mode-line-update) + process))) + +(defun preview-gs-open (&optional setup) + "Start a Ghostscript conversion pass. +SETUP may contain a parser setup function." + (let ((image-info (assq preview-image-type preview-gs-image-type-alist))) + (setq preview-gs-image-type (nth 1 image-info)) + (setq preview-gs-sequence nil) + (setq preview-gs-command-line (append + preview-gs-options + (nthcdr 2 image-info)) + preview-gs-init-string + (format "{DELAYSAFER{.setsafe}if}stopped pop\ +/.preview-BP currentpagedevice/BeginPage get dup \ +null eq{pop{pop}bind}if def\ +<</BeginPage{currentpagedevice/PageSize get dup 0 get 1 ne exch 1 get 1 ne or\ +{.preview-BP %s}{pop}ifelse}bind/PageSize[1 1]>>setpagedevice\ +/preview-do{/.preview-ST[count 4 roll save]def dup length 0 eq\ +{pop}{setpagedevice}{ifelse exec}\ +stopped{handleerror quit}if \ +.preview-ST aload pop restore}bind def " + (preview-gs-color-string + preview-colors + ;; Compatibility for gs 9.27 with non-trivial + ;; foreground color and dark background. + ;; Suppress color adjustment with PDF backend + ;; when `preview-pdf-color-adjust-method' is nil. + (and (not preview-pdf-color-adjust-method) + ;; The switch `preview-parsed-pdfoutput' isn't + ;; set before parsing the latex output, so use + ;; heuristic here. + (with-current-buffer TeX-command-buffer + (and TeX-PDF-mode + (not (TeX-PDF-from-DVI)))))))) + (preview-gs-queue-empty) + (preview-parse-messages (or setup #'preview-gs-dvips-process-setup)))) + +(defun preview-gs-color-value (value) + "Return string to be used as color value for an RGB component. +Conversion from Emacs color numbers (0 to 65535) in VALUE +to Ghostscript floats." + (format "%g" (/ value 65535.0))) + +(defun preview-pdf-color-string (colors) + "Return a string that patches PDF foreground color to work properly." + (let ((fg (aref colors 1))) + (if fg + (cond ((eq preview-pdf-color-adjust-method t) + ;; New code for gs > 9.27. + ;; This assumes DELAYBIND feature, which is known to be + ;; broken in gs 9.27 (and possibly, < 9.27). + ;; <URL:https://lists.gnu.org/archive/html/auctex-devel/2019-07/msg00000.html> + ;; DELAYBIND is sometimes mentioned in association with + ;; security holes in the changelog of Ghostscript: + ;; <URL:https://www.ghostscript.com/doc/9.27/History9.htm> + ;; Thus we might have to be prepared for removal of this + ;; feature in future Ghostscript. + (concat + "/initgraphics { + //initgraphics + /RG where { + pop " + (mapconcat #'preview-gs-color-value fg " ") + " 3 copy rg RG + } if +} bind def .bindnow ")) + ((eq preview-pdf-color-adjust-method 'compatible) + ;; Traditional code for gs < 9.27. + (concat + "/GS_PDF_ProcSet GS_PDF_ProcSet dup maxlength dict copy dup begin\ +/graphicsbeginpage{//graphicsbeginpage exec " + (mapconcat #'preview-gs-color-value fg " ") + " 3 copy rg RG}bind store end readonly store ")) + (;; Do nothing otherwise. + t + ""))))) + +(defun preview-gs-color-string (colors &optional suppress-fgbg) + "Return a string setting up COLORS. +If optional argument SUPPRESS-FGBG is non-nil, behave as if FG/BG +colors were just the default value." + (let ((bg (and (not suppress-fgbg) + (aref colors 0))) + (fg (and (not suppress-fgbg) + (aref colors 1))) + (mask (aref colors 2)) + (border (aref colors 3))) + (concat + (and (or (and mask border) (and bg (not fg))) + "gsave ") + (and bg + (concat + (mapconcat #'preview-gs-color-value bg " ") + " setrgbcolor clippath fill ")) + (and mask border + (format "%s setrgbcolor false setstrokeadjust %g \ +setlinewidth clippath strokepath \ +matrix setmatrix true \ +{2 index{newpath}if round exch round exch moveto pop false}\ +{round exch round exch lineto}{curveto}{closepath}\ +pathforall pop fill " + (mapconcat #'preview-gs-color-value mask " ") + (* 2 border))) + ;; I hate antialiasing. Warp border to integral coordinates. + (and (or (and mask border) (and bg (not fg))) + "grestore ") + (and fg + (concat + (mapconcat #'preview-gs-color-value fg " ") + " setrgbcolor"))))) + +(defun preview-dvipng-color-string (colors res) + "Return color setup tokens for dvipng. +Makes a string of options suitable for passing to dvipng. +Pure borderless black-on-white will return an empty string." + (let + ((bg (aref colors 0)) + (fg (aref colors 1)) + (mask (aref colors 2)) + (border (aref colors 3))) + (concat + (and bg + (format "--bg \"rgb %s\" " + (mapconcat #'preview-gs-color-value bg " "))) + (and fg + (format "--fg \"rgb %s\" " + (mapconcat #'preview-gs-color-value fg " "))) + (and mask border + (format "--bd \"rgb %s\" " + (mapconcat #'preview-gs-color-value mask " "))) + (and border + (format "--bd %d" (max 1 (round (/ (* res border) 72.0)))))))) + +(defsubst preview-supports-image-type (imagetype) + "Check if IMAGETYPE is supported." + (image-type-available-p imagetype)) + +(defun preview-gs-dvips-process-setup () + "Set up Dvips process for conversions via gs." + (unless (preview-supports-image-type preview-gs-image-type) + (error "preview-image-type setting '%s unsupported by this Emacs" + preview-gs-image-type)) + (setq preview-gs-command-line (append + preview-gs-command-line + (list (preview-gs-resolution + (preview-hook-enquiry preview-scale) + (car preview-resolution) + (cdr preview-resolution))))) + (if preview-parsed-pdfoutput + (preview-pdf2dsc-process-setup) + (let ((process (preview-start-dvips preview-fast-conversion))) + (setq TeX-sentinel-function #'preview-gs-dvips-sentinel) + (list process (current-buffer) TeX-active-tempdir preview-ps-file + preview-gs-image-type)))) + +(defun preview-dvipng-process-setup () + "Set up dvipng process for conversion." + (setq preview-gs-command-line (append + preview-gs-command-line + (list (preview-gs-resolution + (preview-hook-enquiry preview-scale) + (car preview-resolution) + (cdr preview-resolution))))) + (if preview-parsed-pdfoutput + (if (preview-supports-image-type preview-gs-image-type) + (preview-pdf2dsc-process-setup) + (error "preview-image-type setting '%s unsupported by this Emacs" + preview-gs-image-type)) + (unless (preview-supports-image-type preview-dvipng-image-type) + (error "preview-dvipng-image-type setting '%s unsupported by this Emacs" + preview-dvipng-image-type)) + (let ((process (preview-start-dvipng))) + (setq TeX-sentinel-function #'preview-dvipng-sentinel) + (list process (current-buffer) TeX-active-tempdir t + preview-dvipng-image-type)))) + + +(defun preview-pdf2dsc-process-setup () + (let ((process (preview-start-pdf2dsc))) + (setq TeX-sentinel-function #'preview-pdf2dsc-sentinel) + (list process (current-buffer) TeX-active-tempdir preview-ps-file + preview-gs-image-type))) + +(defun preview-dvips-abort () + "Abort a Dvips run." + (preview-gs-queue-empty) + (condition-case nil + (delete-file + (let ((gsfile preview-gs-file)) + (with-current-buffer TeX-command-buffer + (funcall (car gsfile) "dvi" t)))) + (file-error nil)) + (when preview-ps-file + (condition-case nil + (preview-delete-file preview-ps-file) + (file-error nil))) + (setq TeX-sentinel-function nil)) + +(defalias 'preview-dvipng-abort #'preview-dvips-abort) +; "Abort a DviPNG run.") + +(defun preview-gs-dvips-sentinel (process _command &optional gsstart) + "Sentinel function for indirect rendering DviPS process. +The usual PROCESS and COMMAND arguments for +`TeX-sentinel-function' apply. Starts gs if GSSTART is set." + (condition-case err + (let ((status (process-status process)) + (gsfile preview-gs-file)) + (cond ((eq status 'exit) + (delete-process process) + (setq TeX-sentinel-function nil) + (condition-case nil + (delete-file + (with-current-buffer TeX-command-buffer + (funcall (car gsfile) "dvi" t))) + (file-error nil)) + (if preview-ps-file + (preview-prepare-fast-conversion)) + (when gsstart + (if preview-gs-queue + (preview-gs-restart) + (when preview-ps-file + (condition-case nil + (preview-delete-file preview-ps-file) + (file-error nil)))))) + ((eq status 'signal) + (delete-process process) + (preview-dvips-abort)))) + (error (preview-log-error err "DviPS sentinel" process))) + (preview-reraise-error process)) + +(defun preview-pdf2dsc-sentinel (process _command &optional gsstart) + "Sentinel function for indirect rendering PDF process. +The usual PROCESS and COMMAND arguments for +`TeX-sentinel-function' apply. Starts gs if GSSTART is set." + (condition-case err + (let ((status (process-status process))) + (cond ((eq status 'exit) + (delete-process process) + (setq TeX-sentinel-function nil) + ;; Add DELAYBIND option for adjustment of foreground + ;; color to work. + (if (and (eq preview-pdf-color-adjust-method t) + (aref preview-colors 1)) + (setq preview-gs-command-line (append + preview-gs-command-line + '("-dDELAYBIND")))) + (setq preview-gs-init-string + (concat preview-gs-init-string + (preview-pdf-color-string preview-colors))) + (preview-prepare-fast-conversion) + (when gsstart + (if preview-gs-queue + (preview-gs-restart) + (when preview-ps-file + (condition-case nil + (preview-delete-file preview-ps-file) + (file-error nil)))))) + ((eq status 'signal) + (delete-process process) + (preview-dvips-abort)))) + (error (preview-log-error err "PDF2DSC sentinel" process))) + (preview-reraise-error process)) + +(defun preview-gs-close (process closedata) + "Clean up after PROCESS and set up queue accumulated in CLOSEDATA." + (setq preview-gs-queue (nconc preview-gs-queue closedata)) + (if process + (if preview-gs-queue + (if TeX-process-asynchronous + (if (and (eq (process-status process) 'exit) + (null TeX-sentinel-function)) + ;; Process has already finished and run sentinel + (progn + (when preview-ps-file + (condition-case nil + (preview-delete-file preview-ps-file) + (file-error nil))) + (preview-gs-restart)) + (setq TeX-sentinel-function + (let ((fun (if preview-parsed-pdfoutput + #'preview-pdf2dsc-sentinel + #'preview-gs-dvips-sentinel))) + (lambda (process command) + (funcall fun process command t))))) + (TeX-synchronous-sentinel "Preview-DviPS" (cdr preview-gs-file) + process)) + ;; pathological case: no previews although we sure thought so. + (delete-process process) + (unless (eq (process-status process) 'signal) + (preview-dvips-abort))))) + +(defun preview-dvipng-sentinel (process _command &optional placeall) + "Sentinel function for indirect rendering DviPNG process. +The usual PROCESS and COMMAND arguments for +`TeX-sentinel-function' apply. Places all snippets if PLACEALL is set." + (condition-case err + (let ((status (process-status process))) + (cond ((eq status 'exit) + (delete-process process) + (setq TeX-sentinel-function nil) + (when placeall + (preview-dvipng-place-all))) + ((eq status 'signal) + (delete-process process) + (preview-dvipng-abort)))) + (error (preview-log-error err "DviPNG sentinel" process))) + (preview-reraise-error process)) + +(defun preview-dvipng-close (process closedata) + "Clean up after PROCESS and set up queue accumulated in CLOSEDATA." + (if preview-parsed-pdfoutput + (preview-gs-close process closedata) + (setq preview-gs-queue (nconc preview-gs-queue closedata)) + (if process + (if preview-gs-queue + (if TeX-process-asynchronous + (if (and (eq (process-status process) 'exit) + (null TeX-sentinel-function)) + ;; Process has already finished and run sentinel + (preview-dvipng-place-all) + (setq TeX-sentinel-function (lambda (process command) + (preview-dvipng-sentinel + process + command + t)))) + (TeX-synchronous-sentinel "Preview-DviPNG" (cdr preview-gs-file) + process)) + ;; pathological case: no previews although we sure thought so. + (delete-process process) + (unless (eq (process-status process) 'signal) + (preview-dvipng-abort)))))) + +(defun preview-dsc-parse (file) + "Parse DSC comments of FILE. +Return a vector with offset/length pairs corresponding to +the pages. Page 0 corresponds to the initialization section." + (with-temp-buffer + (set-buffer-multibyte nil) + (insert-file-contents-literally file) + (let ((last-pt (point-min)) + trailer + pagelist + lastbegin + pt + case-fold-search + (level 0)) + (while (search-forward-regexp "\ +%%\\(?:\\(BeginDocument:\\)\\|\ +\\(EndDocument[\n\r]\\)\\|\ +\\(Page:\\)\\|\ +\\(Trailer[\n\r]\\)\\)" nil t) + (setq pt (match-beginning 0)) + (cond ((null (memq (char-before pt) '(?\C-j ?\C-m nil)))) + (trailer (error "Premature %%%%Trailer in `%s' at offsets %d/%d" + file trailer pt)) + ((match-beginning 1) + (if (zerop level) + (setq lastbegin pt)) + (setq level (1+ level))) + ((match-beginning 2) + (if (zerop level) + (error "Unmatched %%%%EndDocument in `%s' at offset %d" + file pt) + (setq level (1- level)))) + ((> level 0)) + ((match-beginning 3) + (push (list last-pt (- pt last-pt)) pagelist) + (setq last-pt pt)) + ((match-beginning 4) + (setq trailer pt)))) + (unless (zerop level) + (error "Unmatched %%%%BeginDocument in `%s' at offset %d" + file lastbegin)) + (push (list last-pt + (- (or trailer (point-max)) last-pt)) pagelist) + (vconcat (nreverse pagelist))))) + +(defun preview-gs-dsc-cvx (page dsc) + "Generate PostScript code accessing PAGE in the DSC object. +The returned PostScript code will need the file on +top of the stack, and will replace it with an executable +object corresponding to the wanted page." + (let ((curpage (aref dsc page))) + (format "dup %d setfileposition %d()/SubFileDecode filter cvx" + (1- (car curpage)) (nth 1 curpage)))) + +(defun preview-ps-quote-filename (str &optional nonrel) + "Make a PostScript string from filename STR. +The file name is first made relative unless +NONREL is not nil." + (unless nonrel (setq str (file-relative-name str))) + (let ((index 0)) + (while (setq index (string-match "[\\()]" str index)) + (setq str (replace-match "\\\\\\&" t nil str) + index (+ 2 index))) + (concat "(" str ")"))) + +(defun preview-prepare-fast-conversion () + "This fixes up all parameters for fast conversion." + (let* ((file (if (consp (car preview-ps-file)) + (if (consp (caar preview-ps-file)) + (car (last (caar preview-ps-file))) + (caar preview-ps-file)) + (car preview-ps-file))) + (all-files (if (and (consp (car preview-ps-file)) + (consp (caar preview-ps-file))) + (caar preview-ps-file) + (list file)))) + (setq preview-gs-dsc (preview-dsc-parse file)) + (setq preview-gs-init-string + ;; Add commands for revised file access controls introduced + ;; after gs 9.27 (bug#37719) + (concat (format "systemdict /.addcontrolpath known {%s} if " + (mapconcat (lambda (f) + (format "/PermitFileReading %s .addcontrolpath" + (preview-ps-quote-filename f))) + all-files "\n")) + (format "{<</PermitFileReading[%s]>> setuserparams \ +.locksafe} stopped pop " + (mapconcat #'preview-ps-quote-filename all-files "")) + preview-gs-init-string + (format " %s(r)file /.preview-ST 1 index def %s exec .preview-ST " + (preview-ps-quote-filename file) + (preview-gs-dsc-cvx 0 preview-gs-dsc)))))) + +(defun preview-gs-urgentize (ov buff) + "Make a displayed overlay render with higher priority. +This function is used in fake conditional display properties +for reordering the conversion order to prioritize on-screen +images. OV is the overlay in question, and BUFF is the +Ghostscript process buffer where the buffer-local queue +is located." + ;; It does not matter that ov gets queued twice in that process: the + ;; first version to get rendered will clear the 'queued property. + ;; It cannot get queued more than twice since we remove the + ;; conditional display property responsible for requeuing here. + ;; We don't requeue if the overlay has been killed (its buffer made + ;; nil). Not necessary, but while we are checking... + ;; We must return t. + (preview-remove-urgentization ov) + (when (and (overlay-get ov 'queued) + (overlay-buffer ov)) + (with-current-buffer buff + (push ov preview-gs-queue))) + t) + +(defsubst preview-icon-copy (icon) + "Prepare a later call of `preview-replace-active-icon'." + + ;; This is just a GNU Emacs specific efficiency hack because it + ;; is easy to do. When porting, don't do anything complicated + ;; here, rather deliver just the unchanged icon and make + ;; `preview-replace-active-icon' do the necessary work of replacing + ;; the icon where it actually has been stored, probably + ;; in the car of the strings property of the overlay. This string + ;; might probably serve as a begin-glyph as well, in which case + ;; modifying the string in the strings property would change that + ;; glyph automatically. + + (cons 'image (cdr icon))) + +(defsubst preview-replace-active-icon (ov replacement) + "Replace the active Icon in OV by REPLACEMENT, another icon." + (let ((img (overlay-get ov 'preview-image))) + (setcdr (car img) (cdar replacement)) + (setcdr img (cdr replacement)))) + +(defun preview-gs-place (ov snippet box run-buffer tempdir ps-file _imagetype) + "Generate an image placeholder rendered over by Ghostscript. +This enters OV into all proper queues in order to make it render +this image for real later, and returns the overlay after setting +a placeholder image. SNIPPET gives the number of the +snippet in question for the file to be generated. +BOX is a bounding box if we already know one via TeX. +RUN-BUFFER is the buffer of the TeX process, +TEMPDIR is the correct copy of `TeX-active-tempdir', +PS-FILE is a copy of `preview-ps-file', IMAGETYPE is the image type +for the file extension." + (overlay-put ov 'filenames + (unless (eq ps-file t) + (list + (preview-make-filename + (or ps-file + (format "preview.%03d" snippet)) + tempdir)))) + (overlay-put ov 'queued + (vector box nil snippet)) + (overlay-put ov 'preview-image + (list (preview-icon-copy preview-nonready-icon))) + (preview-add-urgentization #'preview-gs-urgentize ov run-buffer) + (list ov)) + +(defvar view-exit-action) + +(eval-and-compile + (defvar preview-button-1 [mouse-2]) + (defvar preview-button-2 [mouse-3])) + +(defmacro preview-make-clickable (&optional map glyph helpstring click1 click2) + "Generate a clickable string or keymap. +If MAP is non-nil, it specifies a keymap to add to, otherwise +a new one is created. If GLYPH is given, the result is made +to display it wrapped in a string. In that case, +HELPSTRING is a format string with one or two %s specifiers +for preview's clicks, displayed as a help-echo. CLICK1 and CLICK2 +are functions to call on preview's clicks." + `(let ((resmap ,(or map '(make-sparse-keymap)))) + ,@(if click1 + `((define-key resmap preview-button-1 ,click1))) + ,@(if click2 + `((define-key resmap preview-button-2 ,click2))) + ,(if glyph + `(propertize + "x" + 'display ,glyph + 'mouse-face 'highlight + 'help-echo + ,(if (stringp helpstring) + (format helpstring preview-button-1 preview-button-2) + `(format ,helpstring preview-button-1 preview-button-2)) + 'keymap resmap) + 'resmap))) + +(defun preview-mouse-open-error (string) + "Display STRING in a new view buffer on click." + (let ((buff (get-buffer-create + "*Preview-Ghostscript-Error*"))) + (with-current-buffer buff + (kill-all-local-variables) + (set (make-local-variable 'view-exit-action) #'kill-buffer) + (setq buffer-undo-list t) + (erase-buffer) + (insert string) + (goto-char (point-min))) + (view-buffer-other-window buff))) + +(defun preview-mouse-open-eps (file &optional position) + "Display eps FILE in a view buffer on click. +Place point at POSITION, else beginning of file." + (let ((default-mode + ;; FIXME: Yuck! Just arrange for the file name to have the right + ;; extension instead! + (assoc-default "x.ps" auto-mode-alist #'string-match)) + (buff (get-file-buffer file))) + (save-excursion + (if buff + (pop-to-buffer buff) + (view-file-other-window file)) + (if (and (eq major-mode (default-value 'major-mode)) + default-mode) + (funcall default-mode)) + (goto-char (or position (point-min))) + (message "%s" (substitute-command-keys "\ +Try \\[ps-run-start] \\[ps-run-buffer] and \ +\\<ps-run-mode-map>\\[ps-run-mouse-goto-error] on error offset."))))) + +(defun preview-gs-flag-error (ov err) + "Make an eps error flag in overlay OV for ERR string." + (let* ((filenames (overlay-get ov 'filenames)) + (file (car (nth 0 filenames))) + ;; FIXME: This format isn't equal to actual invocation of gs + ;; command constructed in `preview-gs-restart', which + ;; contains "%d". + (outfile (format "-sOutputFile=%s" + (file-relative-name + (car (nth 1 filenames))))) + (ps-open + (let ((string + (concat + (mapconcat #'shell-quote-argument + (append (list + preview-gs-command + outfile) + preview-gs-command-line) + " ") + "\nGS>" + preview-gs-init-string + (aref (overlay-get ov 'queued) 1) + err))) + (lambda () (interactive "@") (preview-mouse-open-error string)))) + (str + (preview-make-clickable + nil + preview-error-icon + "%s views error message +%s more options" + ps-open + (let ((args + (if preview-ps-file + (list + (if (consp (car file)) (nth 1 (car file)) (car file)) + (nth 0 (aref preview-gs-dsc + (aref (overlay-get ov 'queued) 2)))) + (list file)))) + (lambda () (interactive) + (popup-menu + `("PostScript error" + ["View error" ,ps-open] + ["View source" ,(lambda () (interactive "@") + (apply #'preview-mouse-open-eps + args))]))))))) + (overlay-put ov 'strings (cons str str)) + (preview-toggle ov))) + +(defun preview-gs-transact (process answer) + "Work off Ghostscript transaction. +This routine is the action routine called via the process filter. +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 \ +%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)))))) + (unless preview-gs-outstanding + (condition-case nil + (process-send-eof process) + (error nil))))) + +(defun preview-hook-enquiry (hook) + "Gets a value from a configured hook. +HOOK is a list or single item, for which the first resolving to +non-nil counts. Entries can be a callable function, or +a symbol that is consulted, or a value. Lists are evaluated +recursively." + (cond ((functionp hook) + (funcall hook)) + ((consp hook) + (let (res) + (while (and (not res) hook) + (setq res (preview-hook-enquiry (car hook)) + hook (cdr hook))) + res)) + ((and (symbolp hook) (boundp hook)) + (symbol-value hook)) + (t hook))) + +(defun preview-inherited-face-attribute (face attribute &optional inherit) + "Fetch face attribute while adhering to inheritance. +This searches FACE for an ATTRIBUTE, using INHERIT +for resolving unspecified or relative specs. See the fourth +argument of function `face-attribute' for details." + (face-attribute face attribute nil inherit)) + +(defcustom preview-scale-function #'preview-scale-from-face + "Scale factor for included previews. +This can be either a function to calculate the scale, or +a fixed number." + :group 'preview-appearance + :type '(choice (function-item preview-scale-from-face) + (const 1.0) + (number :value 1.0) + (function :value preview-scale-from-face))) + +(defcustom preview-default-document-pt 10 + "Assumed document point size for `preview-scale-from-face'. +If the point size (such as 11pt) of the document cannot be +determined from the document options itself, assume this size. +This is for matching screen font size and previews." + :group 'preview-appearance + :type + '(choice (const :tag "10pt" 10) + (const :tag "11pt" 11) + (const :tag "12pt" 12) + (number :tag "Other" :value 11.0))) + +(defcustom preview-document-pt-list '(preview-parsed-font-size + preview-auctex-font-size + preview-default-document-pt) + "How `preview-document-pt' figures out the document size." + :group 'preview-appearance + :type + '(repeat (choice + ;; FIXME: It seems that the bug mentioned below doesn't exist + ;; at least for emacs 27.2. + ;; This is a bug: type function seems to match variables, too. + (restricted-sexp :match-alternatives (functionp) + :tag "Function" :value preview-auctex-font-size) + (variable :value preview-parsed-font-size) + (number :value 11)))) + +(defun preview-auctex-font-size () + "Calculate the default font size of document. +If packages, classes or styles were called with an option +like 10pt, size is taken from the first such option if you +had let your document be parsed by AUCTeX." + (let* ((regexp "\\`\\([0-9]+\\)pt\\'") + (option + (or + (LaTeX-match-class-option regexp) + ;; We don't have `LaTeX-match-package-option'. + (TeX-member regexp + (apply #'append + (mapcar #'cdr LaTeX-provided-package-options)) + #'string-match)))) + (if option (string-to-number (match-string 1 option))))) + +(defsubst preview-document-pt () + "Calculate the default font size of document." + (preview-hook-enquiry preview-document-pt-list)) + +(defun preview-scale-from-face () + "Calculate preview scale from `preview-reference-face'. +This calculates the scale of EPS images from a document assumed +to have a default font size given by function `preview-document-pt' +so that they match the reference face in height." + (let ((d (/ (preview-inherited-face-attribute 'preview-reference-face :height + 'default) + 10.0))) + (lambda () (/ d (preview-document-pt))))) + +(defvar preview-min-spec nil + "Value to filter out too large icons. +Icon specs with :size larger than this value is not used. +Appropriate value is determined at run time according to the +display in use.") + +(defun preview-make-image (symbol) + "Make an image from a preview spec list. +The first spec that is workable (given the current setting of +`preview-min-spec') from the given SYMBOL is used here. The +icon is cached in the property list of the SYMBOL." + (let ((alist (get symbol 'preview-min-alist))) + (cdr (or + (assq preview-min-spec alist) + (car (put symbol 'preview-min-alist + (cons + (cons preview-min-spec + (preview-filter-specs + (symbol-value symbol))) + alist))))))) + +(defun preview-filter-specs (spec-list) + "Find the first of the fitting specs and make an image." + (let (image) + (while (and spec-list + (not (setq image + (catch 'preview-filter-specs + (preview-filter-specs-1 (car spec-list)))))) + (setq spec-list (cdr spec-list))) + image)) + +(defun preview-filter-specs-1 (specs) + (and specs + (if (get 'preview-filter-specs (car specs)) + (apply (get 'preview-filter-specs (car specs)) specs) + `(,(nth 0 specs) ,(nth 1 specs) + ,@(preview-filter-specs-1 (nthcdr 2 specs)))))) + +(put 'preview-filter-specs :min + #'(lambda (_keyword value &rest args) + (if (> value preview-min-spec) + (throw 'preview-filter-specs nil) + (preview-filter-specs-1 args)))) + +(put 'preview-filter-specs :file + #'(lambda (_keyword value &rest args) + `(:file ,(expand-file-name value (expand-file-name "images" + TeX-data-directory)) + ,@(preview-filter-specs-1 args)))) + +(defun preview-ascent-from-bb (bb) + "This calculates the image ascent from its bounding box. +The bounding box BB needs to be a 4-component vector of +numbers (can be float if available)." + ;; baseline is at 1in from the top of letter paper (11in), so it is + ;; at 10in from the bottom precisely, which is 720 in PostScript + ;; coordinates. If our bounding box has its bottom not above this + ;; line, and its top above, we can calculate a useful ascent value. + ;; If not, something is amiss. We just use 100 in that case. + + (let ((bottom (aref bb 1)) + (top (aref bb 3))) + (if (and (<= bottom 720) + (> top 720)) + (round (* 100.0 (/ (- top 720.0) (- top bottom)))) + 100))) + +(defface preview-face '((((background dark)) + (:background "dark slate gray")) + (t + (:background "beige"))) + "Face to use for the preview source." + :group 'preview-appearance) + +(defface preview-reference-face '((t nil)) + "Face consulted for colors and scale of active previews. +Fallback to :inherit and 'default implemented." + :group 'preview-appearance) + +(defcustom preview-auto-reveal + '(eval (preview-arrived-via (key-binding [left]) (key-binding [right]) + #'backward-char #'forward-char)) + "Cause previews to open automatically when entered. +Possibilities are: +t autoopens, +nil doesn't, +a symbol will have its value consulted if it exists, +defaulting to nil if it doesn't. +An integer will specify a maximum cursor movement distance. +Larger movements won't open the preview. +A CONS-cell means to call a function for determining the value. +The CAR of the cell is the function to call which receives +the CDR of the CONS-cell in the rest of the arguments, while +point and current buffer point to the position in question. +All of the options show reasonable defaults." + :group 'preview-appearance + :type '(choice (const :tag "Off" nil) + (const :tag "On" t) + (symbol :tag "Indirect variable" :value reveal-mode) + (integer :tag "Maximum distance" :value 1) + (cons :tag "Function call" + :value (eval (preview-arrived-via + (key-binding [left]) + (key-binding [right]))) + function (list :tag "Argument list" + (repeat :inline t sexp))))) + +(defun preview-auto-reveal-p (mode distance) + "Decide whether to auto-reveal. +Return non-nil if region should be auto-opened. +See `preview-auto-reveal' for definitions of MODE, which gets +set to `preview-auto-reveal'. DISTANCE specifies the movement +distance with which point has been reached in case it has been +a movement starting in the current buffer." + (cond ((symbolp mode) + (and (boundp mode) + (symbol-value mode))) + ((integerp mode) + (and distance (/= 0 distance) (<= (abs distance) mode))) + ((consp mode) + (apply (car mode) (cdr mode))) + (t mode))) + +(defun preview-arrived-via (&rest list) + "Indicate auto-opening. +Return non-nil if called by one of the commands in LIST." + (memq this-command list)) + +(defcustom preview-equality-transforms '(identity + preview-canonical-spaces) +"Transformation functions for region changes. +These functions are tried in turn on the strings from the +regions of a preview to decide whether a preview is to be considered +changed. If any transform leads to equal results, the preview is +considered unchanged." + :group 'preview-appearance + :type '(repeat function)) + +(defcustom preview-transparent-color '(highlight :background) + "Color to appear transparent in previews. +Set this to something unusual when using `preview-transparent-border', +to the default background in most other cases." + :type '(radio (const :tag "None" nil) + (const :tag "Autodetect" t) + (color :tag "By name" :value "white") + (list :tag "Take from face" + :value (default :background) + (face) + (choice :tag "What to take" + (const :tag "Background" :value :background) + (const :tag "Foreground" :value :foreground)))) + :group 'preview-appearance) + +;; Note that the following default introduces a border only when +;; Emacs blinks politely when point is on an image (the tested +;; unrelated function was introduced at about the time image blinking +;; became tolerable). +(defcustom preview-transparent-border nil + "Width of transparent border for previews in pt. +Setting this to a numeric value will add a border of +`preview-transparent-color' around images, and will turn +the heuristic-mask setting of images to default to 't since +then the borders are correctly detected even in case of +palette operations. If the transparent color is something +not present otherwise in the image, the cursor display +will affect just this border. A width of 0 is interpreted +by PostScript as meaning a single pixel, other widths are +interpreted as PostScript points (1/72 of 1in)" + :group 'preview-appearance + :type '(choice (const :value nil :tag "No border") + (number :value 1.5 :tag "Border width in pt"))) + +(defun preview-get-heuristic-mask () + "Get heuristic-mask to use for previews. +Consults `preview-transparent-color'." + (cond ((stringp preview-transparent-color) + (color-values preview-transparent-color)) + ((or (not (consp preview-transparent-color)) + (integerp (car preview-transparent-color))) + preview-transparent-color) + (t (color-values (preview-inherited-face-attribute + (nth 0 preview-transparent-color) + (nth 1 preview-transparent-color) + 'default))))) + +(defsubst preview-create-icon-1 (file type ascent border) + `(image + :file ,file + :type ,type + :ascent ,ascent + ,@(and border + '(:mask (heuristic t))))) + +(defun preview-create-icon (file type ascent border) + "Create an icon from FILE, image TYPE, ASCENT and BORDER." + (list + (preview-create-icon-1 file type ascent border) + file type ascent border)) + +(put 'preview-filter-specs :type + (lambda (_keyword value &rest args) + (if (image-type-available-p value) + `(image :type ,value + ,@(preview-filter-specs-1 args)) + (throw 'preview-filter-specs nil)))) + +(defun preview-import-image (image) + "Convert the printable IMAGE rendition back to an image." + (cond ((stringp image) + (propertize image 'face 'preview-face)) + ((eq (car image) 'image) + image) + (t + (preview-create-icon-1 (nth 0 image) + (nth 1 image) + (nth 2 image) + (if (< (length image) 4) + (preview-get-heuristic-mask) + (nth 3 image)))))) + +;; No defcustom here: does not seem to make sense. + +(defvar preview-tb-icon-specs + '((:type xpm :file "prvtex24.xpm") + (:type xbm :file "prvtex24.xbm"))) + +(defvar preview-tb-icon nil) + +(defun preview-add-urgentization (fun ov &rest rest) + "Cause FUN (function call form) to be called when redisplayed. +FUN must be a form with OV as first argument, +REST as the remainder, returning T." + (let ((dispro (overlay-get ov 'display))) + (unless (eq (car dispro) 'when) + (overlay-put ov 'display `(when (,fun ,ov ,@rest) . ,dispro))))) + +(defun preview-remove-urgentization (ov) + "Undo urgentization of OV by `preview-add-urgentization'. +Return the old arguments to `preview-add-urgentization' +if there was any urgentization." + (let ((dispro (overlay-get ov 'display))) + (when (eq (car-safe dispro) 'when) + (prog1 + (car (cdr dispro)) + (overlay-put ov 'display (cdr (cdr dispro))))))) + +(defvar preview-overlay nil) + +(put 'preview-overlay + 'modification-hooks + '(preview-handle-modification)) + +(put 'preview-overlay + 'insert-in-front-hooks + '(preview-handle-insert-in-front)) + +(put 'preview-overlay + 'insert-behind-hooks + '(preview-handle-insert-behind)) + +;; We have to fake our way around atomicity. + +;; Here is the beef: for best intuitiveness, we want to have +;; insertions be carried out as expected before iconized text +;; passages, but we want to insert *into* the overlay when not +;; iconized. A preview that has become empty can not get content +;; again: we remove it. A disabled preview needs no insert-in-front +;; handler. + +(defvar preview-change-list nil + "List of tentatively changed overlays.") + +(defcustom preview-dump-threshold + "^ *\\\\begin *{document}[ %]*$" + "Regexp denoting end of preamble. +This is the location up to which preamble changes are considered +to require redumping of a format." + :group 'preview-latex + :type 'string) + +(defun preview-preamble-changed-function + (ov _after-change _beg _end &optional _length) + "Hook function for change hooks on preamble. +See info node `(elisp) Overlay Properties' for +definition of OV, AFTER-CHANGE, BEG, END and LENGTH." + (let ((format-cons (overlay-get ov 'format-cons))) + (preview-unwatch-preamble format-cons) + (preview-format-kill format-cons) + (setcdr format-cons t))) + +(defun preview-watch-preamble (file command format-cons) + "Set up a watch on master file FILE. +FILE can be an associated buffer instead of a filename. +COMMAND is the command that generated the format. +FORMAT-CONS contains the format info for the main +format dump handler." + (let ((buffer (if (bufferp file) + file + (find-buffer-visiting file))) ov) + (setcdr + format-cons + (cons command + (when buffer + (with-current-buffer buffer + (save-excursion + (save-restriction + (widen) + (goto-char (point-min)) + (unless (re-search-forward preview-dump-threshold nil t) + (error "Can't find preamble of `%s'" file)) + (setq ov (make-overlay (point-min) (point))) + (overlay-put ov 'format-cons format-cons) + (overlay-put ov 'insert-in-front-hooks + '(preview-preamble-changed-function)) + (overlay-put ov 'modification-hooks + '(preview-preamble-changed-function)) + ov)))))))) + +(defun preview-unwatch-preamble (format-cons) + "Stop watching a format on FORMAT-CONS. +The watch has been set up by `preview-watch-preamble'." + (when (consp (cdr format-cons)) + (when (cddr format-cons) + (delete-overlay (cddr format-cons))) + (setcdr (cdr format-cons) nil))) + +(defun preview-register-change (ov) + "Register not yet changed OV for verification. +This stores the old contents of the overlay in the +`preview-prechange' property and puts the overlay into +`preview-change-list' where `preview-check-changes' will +find it at some later point of time." + (unless (overlay-get ov 'preview-prechange) + (if (eq (overlay-get ov 'preview-state) 'disabled) + (overlay-put ov 'preview-prechange t) + (overlay-put ov 'preview-prechange + (save-restriction + (widen) + (buffer-substring-no-properties + (overlay-start ov) (overlay-end ov))))) + (push ov preview-change-list))) + +(defun preview-check-changes () + "Check whether the contents under the overlay have changed. +Disable it if that is the case. Ignores text properties." + (dolist (ov preview-change-list) + (condition-case nil + (with-current-buffer (overlay-buffer ov) + (let ((text (save-restriction + (widen) + (buffer-substring-no-properties + (overlay-start ov) (overlay-end ov))))) + (if (zerop (length text)) + (preview-delete ov) + (unless + (or (eq (overlay-get ov 'preview-state) 'disabled) + (preview-relaxed-string= + text (overlay-get ov 'preview-prechange))) + (overlay-put ov 'insert-in-front-hooks nil) + (overlay-put ov 'insert-behind-hooks nil) + (preview-disable ov))))) + (error nil)) + (overlay-put ov 'preview-prechange nil)) + (setq preview-change-list nil)) + +(defun preview-handle-insert-in-front + (ov after-change _beg end &optional _length) + "Hook function for `insert-in-front-hooks' property. +See info node `(elisp) Overlay Properties' for +definition of OV, AFTER-CHANGE, BEG, END and LENGTH." + (if after-change + (unless undo-in-progress + (if (eq (overlay-get ov 'preview-state) 'active) + (move-overlay ov end (overlay-end ov)))) + (preview-register-change ov))) + +(defun preview-handle-insert-behind + (ov after-change beg _end &optional _length) + "Hook function for `insert-behind-hooks' property. +This is needed in case `insert-before-markers' is used at the +end of the overlay. See info node `(elisp) Overlay Properties' +for definition of OV, AFTER-CHANGE, BEG, END and LENGTH." + (if after-change + (unless undo-in-progress + (if (eq (overlay-get ov 'preview-state) 'active) + (move-overlay ov (overlay-start ov) beg))) + (preview-register-change ov))) + +(defun preview-handle-modification + (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 + (preview-register-change ov))) + +(defun preview-toggle (ov &optional arg event) + "Toggle visibility of preview overlay OV. +ARG can be one of the following: t displays the overlay, +nil displays the underlying text, and 'toggle toggles. +If EVENT is given, it indicates the window where the event +occured, either by being a mouse event or by directly being +the window in question. This may be used for cursor restoration +purposes." + (let ((old-urgent (preview-remove-urgentization ov)) + (preview-state + (if (if (eq arg 'toggle) + (null (eq (overlay-get ov 'preview-state) 'active)) + arg) + 'active + 'inactive)) + (strings (overlay-get ov 'strings))) + (unless (eq (overlay-get ov 'preview-state) 'disabled) + (overlay-put ov 'preview-state preview-state) + (if (eq preview-state 'active) + (progn + (overlay-put ov 'category 'preview-overlay) + (if (eq (overlay-start ov) (overlay-end ov)) + (overlay-put ov 'before-string (car strings)) + (dolist (prop '(display keymap mouse-face help-echo)) + (overlay-put ov prop + (get-text-property 0 prop (car strings)))) + (overlay-put ov 'before-string nil)) + (overlay-put ov 'face nil)) + (dolist (prop '(display keymap mouse-face help-echo)) + (overlay-put ov prop nil)) + (overlay-put ov 'face 'preview-face) + (unless (cdr strings) + (setcdr strings (preview-inactive-string ov))) + (overlay-put ov 'before-string (cdr strings))) + (if old-urgent + (apply #'preview-add-urgentization old-urgent)))) + (if event + (preview-restore-position + ov + (if (windowp event) + event + (posn-window (event-start event)))))) + +(defvar preview-marker (make-marker) + "Marker for fake intangibility.") + +(defvar preview-temporary-opened nil) + +(defvar preview-last-location nil + "Restored cursor position marker for reopened previews.") +(make-variable-buffer-local 'preview-last-location) + +(defun preview-mark-point () + "Mark position for fake intangibility." + (when (eq (get-char-property (point) 'preview-state) 'active) + (unless preview-last-location + (setq preview-last-location (make-marker))) + (set-marker preview-last-location (point)) + (set-marker preview-marker (point)) + (preview-move-point)) + (set-marker preview-marker (point))) + +(defun preview-restore-position (ov window) + "Tweak position after opening/closing preview. +The treated overlay OV has been triggered in WINDOW. This function +records the original buffer position for reopening, or restores it +after reopening. Note that by using the mouse, you can open/close +overlays not in the active window." + (when (eq (overlay-buffer ov) (window-buffer window)) + (with-current-buffer (overlay-buffer ov) + (if (eq (overlay-get ov 'preview-state) 'active) + (setq preview-last-location + (set-marker (or preview-last-location (make-marker)) + (window-point window))) + (when (and + (markerp preview-last-location) + (eq (overlay-buffer ov) (marker-buffer preview-last-location)) + (< (overlay-start ov) preview-last-location) + (> (overlay-end ov) preview-last-location)) + (set-window-point window preview-last-location)))))) + +(defun preview-move-point () + "Move point out of fake-intangible areas." + (preview-check-changes) + (let* (newlist (pt (point)) (lst (overlays-at pt)) distance) + (setq preview-temporary-opened + (dolist (ov preview-temporary-opened newlist) + (and (overlay-buffer ov) + (eq (overlay-get ov 'preview-state) 'inactive) + (if (and (eq (overlay-buffer ov) (current-buffer)) + (or (<= pt (overlay-start ov)) + (>= pt (overlay-end ov)))) + (preview-toggle ov t) + (push ov newlist))))) + (when lst + (if (or disable-point-adjustment + global-disable-point-adjustment + (preview-auto-reveal-p + preview-auto-reveal + (setq distance + (and (eq (marker-buffer preview-marker) + (current-buffer)) + (- pt (marker-position preview-marker)))))) + (preview-open-overlays lst) + (while lst + (setq lst + (if (and + (eq (overlay-get (car lst) 'preview-state) 'active) + (> pt (overlay-start (car lst)))) + (overlays-at + (setq pt (if (and distance (< distance 0)) + (overlay-start (car lst)) + (overlay-end (car lst))))) + (cdr lst)))) + (goto-char pt))))) + +(defun preview-open-overlays (list &optional pos) + "Open all previews in LIST, optionally restricted to enclosing POS." + (dolist (ovr list) + (when (and (eq (overlay-get ovr 'preview-state) 'active) + (or (null pos) + (and + (> pos (overlay-start ovr)) + (< pos (overlay-end ovr))))) + (preview-toggle ovr) + (push ovr preview-temporary-opened)))) + +(defun preview--open-for-replace (beg end &rest _) + "Make `query-replace' open preview text about to be replaced." + (preview-open-overlays (overlays-in beg end))) + +(defcustom preview-query-replace-reveal t + "Make `query-replace' autoreveal previews." + :group 'preview-appearance + :type 'boolean + :require 'preview + :set (lambda (symbol value) + (set-default symbol value) + (if value + (advice-add 'replace-highlight :before + #'preview--open-for-replace) + (advice-remove 'replace-highlight + #'preview--open-for-replace))) + :initialize #'custom-initialize-reset) + +(defun preview-relaxed-string= (&rest args) +"Check for functional equality of arguments. +The arguments ARGS are checked for equality by using +`preview-equality-transforms' on them until it is exhausted +or one transform returns equality." + (let ((lst preview-equality-transforms)) + (while (and lst (not (apply #'string= (mapcar (car lst) args)))) + (setq lst (cdr lst))) + lst)) + +(defun preview-canonical-spaces (arg) + "Convert ARG into canonical form. +Removes comments and collapses white space, except for multiple newlines." + (let (pos) + (while (setq pos (string-match "\\s<.*[\n\r][ \t]*" arg pos)) + (setq arg (replace-match "" t t arg 0))) + (while (setq pos (string-match "[ \t]*\\(\\([ \t]\\)\\|[\n\r][ \t]*\\)" + arg pos)) + (setq arg (replace-match (if (match-beginning 2) " " "\n") t t arg 0) + pos (1+ pos))) + (while (setq pos (string-match "\n+" arg pos)) + (if (string= "\n" (match-string 0 arg)) + (setq arg (replace-match " " t t arg 0) + pos (1+ pos)) + (setq pos (match-end 0))))) + arg) + +(defun preview-regenerate (ovr) + "Pass the modified region in OVR again through LaTeX." + (let ((begin (overlay-start ovr)) + (end (overlay-end ovr))) + (with-current-buffer (overlay-buffer ovr) + (preview-delete ovr) + (preview-region begin end)))) + +(defcustom preview-inner-environments '("Bmatrix" "Vmatrix" "aligned" + "array" "bmatrix" "cases" + "gathered" "matrix" "pmatrix" + "smallmatrix" "split" + "subarray" "vmatrix") + "Environments not to be previewed on their own." + :group 'preview-latex + :type '(repeat string)) + + +(defun preview-next-border (backwards) + "Search for the next interesting border for `preview-at-point'. +Searches backwards if BACKWARDS is non-nil." + (let (history preview-state (pt (point))) + (catch 'exit + (while + (null + (memq + (setq preview-state + (if backwards + (if (> (setq pt + (previous-single-char-property-change + pt 'preview-state)) (point-min)) + (get-char-property (1- pt) 'preview-state) + (throw 'exit (or history (point-min)))) + (if (< (setq pt + (next-single-char-property-change + pt 'preview-state)) (point-max)) + (get-char-property pt 'preview-state) + (throw 'exit (or history (point-max)))))) + '(active inactive))) + (setq history (and (not preview-state) pt))) + (or history pt)))) + +(defun preview-at-point () + "Do the appropriate preview thing at point. +If point is positioned on or inside of an unmodified preview area, +its visibility is toggled. + +If not, the surroundings are run through preview. The +surroundings don't extend into unmodified previews or past +contiguous previews invalidated by modifications. + +Overriding any other action, if a region is +active (`transient-mark-mode'), it is run through `preview-region'." + (interactive) + (if (TeX-active-mark) + (preview-region (region-beginning) (region-end)) + (catch 'exit + (dolist (ovr (overlays-in (max (point-min) (1- (point))) + (min (point-max) (1+ (point))))) + (let ((preview-state (overlay-get ovr 'preview-state))) + (when preview-state + (unless (eq preview-state 'disabled) + (preview-toggle ovr 'toggle (selected-window)) + (throw 'exit t))))) + (preview-region (preview-next-border t) + (preview-next-border nil))))) + +(defun preview-disabled-string (ov) + "Generate a before-string for disabled preview overlay OV." + (concat (preview-make-clickable + (overlay-get ov 'preview-map) + preview-icon + "\ +%s regenerates preview +%s more options" + (lambda () (interactive) (preview-regenerate ov))) +;; icon on separate line only for stuff starting on its own line + (with-current-buffer (overlay-buffer ov) + (save-excursion + (save-restriction + (widen) + (goto-char (overlay-start ov)) + (if (bolp) "\n" "")))))) + +(defun preview-disable (ovr) + "Change overlay behaviour of OVR after source edits." + (overlay-put ovr 'queued nil) + (preview-remove-urgentization ovr) + (overlay-put ovr 'preview-image nil) + (overlay-put ovr 'timestamp nil) + (setcdr (overlay-get ovr 'strings) (preview-disabled-string ovr)) + (preview-toggle ovr) + (overlay-put ovr 'preview-state 'disabled) + (dolist (filename (overlay-get ovr 'filenames)) + (condition-case nil + (preview-delete-file filename) + (file-error nil)) + (overlay-put ovr 'filenames nil))) + +(defun preview-delete (ovr &rest _ignored) + "Delete preview overlay OVR, taking any associated file along. +IGNORED arguments are ignored, making this function usable as +a hook in some cases" + (let ((filenames (overlay-get ovr 'filenames))) + (overlay-put ovr 'filenames nil) + (delete-overlay ovr) + (dolist (filename filenames) + (condition-case nil + (preview-delete-file filename) + (file-error nil))))) + +(defun preview-clearout (&optional start end timestamp) + "Clear out all previews in the current region. +When called interactively, the current region is used. +Non-interactively, the region between START and END is +affected. Those two values default to the borders of +the entire buffer. If TIMESTAMP is non-nil, previews +with a `timestamp' property of it are kept." + (interactive "r") + (dolist (ov (overlays-in (or start (point-min)) + (or end (point-max)))) + (and (overlay-get ov 'preview-state) + (not (and timestamp + (equal timestamp (overlay-get ov 'timestamp)))) + (preview-delete ov)))) + +(defun preview-clearout-buffer (&optional buffer) + "Clearout BUFFER from previews, current buffer if nil." + (interactive) + (if buffer + (with-current-buffer buffer (preview-clearout)) + (preview-clearout))) + +(defun preview-clearout-section () + "Clearout previews from LaTeX section." + (interactive) + (save-excursion + (LaTeX-mark-section) + (preview-clearout (region-beginning) (region-end)))) + +(defun preview-clearout-at-point () + "Clearout any preview at point." + (interactive) + (preview-clearout (max (point-min) (1- (point))) + (min (point-max) (1+ (point))))) + +(defun preview-walk-document (func) + "Cycle through all buffers belonging to current document. +Each buffer having the same master file as the current file +has FUNC called with its current buffer being set to it." + (let* ((buffers (buffer-list)) + (master (expand-file-name (TeX-master-file t))) + (default-buffers (list (current-buffer) + (find-buffer-visiting master)))) + (while buffers + (with-current-buffer (pop buffers) + (when + (or (memq (current-buffer) default-buffers) + (and (memq major-mode '(plain-tex-mode latex-mode)) + (or (stringp TeX-master) + (eq TeX-master t)) + (string= (expand-file-name (TeX-master-file t)) + master))) + (funcall func)))))) + +(defun preview-clearout-document () + "Clear out all previews in current document. +The document consists of all buffers that have the same master file +as the current buffer. This makes the current document lose +all previews." + (interactive) + (preview-walk-document #'preview-clearout-buffer)) + +(defun preview-kill-buffer-cleanup (&optional buf) + "This is a cleanup function just for use in hooks. +Cleans BUF or current buffer. The difference to +`preview-clearout-buffer' is that previews +associated with the last buffer modification time are +kept." + (with-current-buffer (or buf (current-buffer)) + (save-restriction + (widen) + (preview-clearout (point-min) (point-max) (visited-file-modtime))))) + +(add-hook 'kill-buffer-hook #'preview-kill-buffer-cleanup) +(add-hook 'before-revert-hook #'preview-kill-buffer-cleanup) + +(defvar preview-last-counter nil + "Last counter information.") + +(defun preview-extract-counters (ctr) + (setq preview-last-counter + (prog1 (copy-sequence ctr) + (dolist (elt preview-last-counter) + (setq ctr (delete elt ctr))))) + (apply #'concat ctr)) + +(defun desktop-buffer-preview-misc-data (&rest _ignored) + "Hook function that extracts previews for persistent sessions." + (unless (buffer-modified-p) + (setq preview-last-counter nil) + (save-restriction + (widen) + (let (save-info (timestamp (visited-file-modtime))) + (dolist (ov (sort (overlays-in (point-min) (point-max)) + (lambda (x y) (< (overlay-start x) + (overlay-start y))))) + (when (and (memq (overlay-get ov 'preview-state) '(active inactive)) + (null (overlay-get ov 'queued)) + (cdr (overlay-get ov 'preview-image))) + (push (preview-dissect ov timestamp) save-info))) + (and save-info + (cons 'preview (cons timestamp (nreverse save-info)))))))) + +(eval-after-load "desktop" + '(add-hook + 'desktop-buffer-misc-functions + #'desktop-buffer-preview-misc-data)) + +(defvar preview-temp-dirs nil +"List of top level temporary directories in use from preview. +Any directory not in this list will be cleared out by preview +on first use.") + +(defun preview-dissect (ov timestamp) + "Extract all persistent data from OV and TIMESTAMP it." + (let ((filenames (butlast (nth 0 (overlay-get ov 'filenames))))) + (overlay-put ov 'timestamp timestamp) + (list (overlay-start ov) + (overlay-end ov) + (cdr (overlay-get ov 'preview-image)) + filenames + (let ((ctr (overlay-get ov 'preview-counters))) + (and ctr + (cons (preview-extract-counters (car ctr)) + (preview-extract-counters (cdr ctr)))))))) + +(defun preview-buffer-restore-internal (buffer-misc) + "Restore previews from BUFFER-MISC if proper. +Remove them if they have expired." + (let ((timestamp (visited-file-modtime)) tempdirlist files) + (setq preview-parsed-counters nil) + (when (eq 'preview (pop buffer-misc)) + (preview-get-geometry) + (if (equal (pop buffer-misc) timestamp) + (dolist (ovdata buffer-misc) + (setq tempdirlist + (apply #'preview-reinstate-preview tempdirlist + timestamp ovdata))) + (dolist (ovdata buffer-misc) + (setq files (nth 3 ovdata)) + (condition-case nil + (delete-file (nth 0 files)) + (file-error nil)) + (unless (member (nth 1 files) tempdirlist) + (push (nth 1 files) tempdirlist))) + (dolist (dir tempdirlist) + (condition-case nil + (delete-directory dir) + (file-error nil))))))) + + +(defun preview-buffer-restore (buffer-misc) + "At end of desktop load, reinstate previews. +This delay is so that minor modes changing buffer positions +\(like `x-symbol-mode' does) will not wreak havoc. +BUFFER-MISC is the appropriate data to be used." + (add-hook 'desktop-delay-hook + (let ((buf (current-buffer))) + (lambda () + (with-current-buffer buf + (preview-buffer-restore-internal + buffer-misc)))))) + +(defun desktop-buffer-preview (file-name _buffer-name misc) + "Hook function for restoring persistent previews into a buffer." + (when (and file-name (file-readable-p file-name)) + (let ((buf (find-file-noselect file-name))) + (if (eq (car misc) 'preview) + (with-current-buffer buf + (preview-buffer-restore misc) + buf) + buf)))) + +(eval-after-load "desktop" + '(if (boundp 'desktop-buffer-mode-handlers) + (add-to-list 'desktop-buffer-mode-handlers + '(latex-mode . desktop-buffer-preview)) + (defvar desktop-buffer-file-name) + (defvar desktop-buffer-name) + (defvar desktop-buffer-misc) + (add-hook 'desktop-buffer-handlers (lambda () + (desktop-buffer-preview + desktop-buffer-file-name + desktop-buffer-name + desktop-buffer-misc))))) + +(defcustom preview-auto-cache-preamble 'ask + "Whether to generate a preamble cache format automatically. +Possible values are nil, t, and `ask'." + :group 'preview-latex + :type '(choice (const :tag "Cache" t) + (const :tag "Don't cache" nil) + (const :tag "Ask" ask))) + +(defvar preview-dumped-alist nil + "Alist of dumped masters. +The elements are (NAME . ASSOC). NAME is the master file name +\(without extension), ASSOC is what to do with regard to this +format. Possible values: nil means no format is available +and none should be generated. t means no format is available, +it should be generated on demand. If the value is a cons cell, +the CAR of the cons cell is the command with which the format +has been generated, and the CDR is some Emacs-flavor specific +value used for maintaining a watch on possible changes of the +preamble.") + +(defun preview-cleanout-tempfiles () + "Clean out all directories and files with non-persistent data. +This is called as a hook when exiting Emacs." + (mapc #'preview-kill-buffer-cleanup (buffer-list)) + (mapc #'preview-format-kill preview-dumped-alist)) + +(defun preview-inactive-string (ov) + "Generate before-string for an inactive preview overlay OV. +This is for overlays where the source text has been clicked +visible. For efficiency reasons it is expected that the buffer +is already selected and unnarrowed." + (concat + (preview-make-clickable (overlay-get ov 'preview-map) + preview-icon + "\ +%s redisplays preview +%s more options") +;; icon on separate line only for stuff starting on its own line + (with-current-buffer (overlay-buffer ov) + (save-excursion + (save-restriction + (widen) + (goto-char (overlay-start ov)) + (if (bolp) "\n" "")))))) + +(defun preview-dvipng-place-all () + "Place all images dvipng has created, if any. +Deletes the dvi file when finished." + (let (filename queued oldfiles snippet) + (dolist (ov (prog1 preview-gs-queue (setq preview-gs-queue nil))) + (when (and (setq queued (overlay-get ov 'queued)) + (setq snippet (aref (overlay-get ov 'queued) 2)) + (setq filename (preview-make-filename + (format "prev%03d.%s" + snippet preview-dvipng-image-type) + TeX-active-tempdir))) + (if (file-exists-p (car filename)) + (progn + (overlay-put ov 'filenames (list filename)) + (preview-replace-active-icon + ov + (preview-create-icon (car filename) + preview-dvipng-image-type + (preview-ascent-from-bb + (aref queued 0)) + (aref preview-colors 2))) + (overlay-put ov 'queued nil)) + (push filename oldfiles) + (overlay-put ov 'filenames nil) + (push ov preview-gs-queue)))) + (if (setq preview-gs-queue (nreverse preview-gs-queue)) + (progn + (preview-start-dvips preview-fast-conversion) + (setq TeX-sentinel-function (lambda (process command) + (preview-gs-dvips-sentinel + process + command + t))) + (dolist (ov preview-gs-queue) + (setq snippet (aref (overlay-get ov 'queued) 2)) + (overlay-put ov 'filenames + (list + (preview-make-filename + (or preview-ps-file + (format "preview.%03d" snippet)) + TeX-active-tempdir)))) + (while (setq filename (pop oldfiles)) + (condition-case nil + (preview-delete-file filename) + (file-error nil)))) + (condition-case nil + (let ((gsfile preview-gs-file)) + (delete-file + (with-current-buffer TeX-command-buffer + (funcall (car gsfile) "dvi" t)))) + (file-error nil))))) + +(defun preview-active-string (ov) + "Generate before-string for active image overlay OV." + (preview-make-clickable + (overlay-get ov 'preview-map) + (car (overlay-get ov 'preview-image)) + "%s opens text +%s more options")) + +(defun preview-make-filename (file tempdir) + "Generate a preview filename from FILE and TEMPDIR. +Filenames consist of a CONS-cell with absolute file name as CAR +and TEMPDIR as CDR. TEMPDIR is a copy of `TeX-active-tempdir' +with the directory name, the reference count and its top directory +name elements. If FILE is already in that form, the file name itself +gets converted into a CONS-cell with a name and a reference count." + (if (consp file) + (progn + (if (consp (car file)) + (setcdr (car file) (1+ (cdr (car file)))) + (setcar file (cons (car file) 1))) + file) + (setcar (nthcdr 2 tempdir) (1+ (nth 2 tempdir))) + (cons (expand-file-name file (nth 0 tempdir)) + tempdir))) + +(defun preview-attach-filename (attached file) + "Attaches the absolute file name ATTACHED to FILE." + (if (listp (caar file)) + (setcar (car file) (cons attached (caar file))) + (setcar (car file) (list attached (caar file)))) + file) + +(defun preview-delete-file (file) + "Delete a preview FILE. +See `preview-make-filename' for a description of the data +structure. If the containing directory becomes empty, +it gets deleted as well." + (let ((filename + (if (consp (car file)) + (and (zerop + (setcdr (car file) (1- (cdr (car file))))) + (car (car file))) + (car file)))) + (if filename + (unwind-protect + (if (listp filename) + (dolist (elt filename) (delete-file elt)) + (delete-file filename)) + (let ((tempdir (cdr file))) + (when tempdir + (if (> (nth 2 tempdir) 1) + (setcar (nthcdr 2 tempdir) (1- (nth 2 tempdir))) + (setcdr file nil) + (delete-directory (nth 0 tempdir))))))))) + +(defvar preview-buffer-has-counters nil) +(make-variable-buffer-local 'preview-buffer-has-counters) + +(defun preview-place-preview (snippet start end + box counters tempdir place-opts) + "Generate and place an overlay preview image. +This generates the filename for the preview +snippet SNIPPET in the current buffer, and uses it for the +region between START and END. BOX is an optional preparsed +TeX bounding BOX passed on to the `place' hook. +COUNTERS is the info about saved counter structures. +TEMPDIR is a copy of `TeX-active-tempdir'. +PLACE-OPTS are additional arguments passed into +`preview-parse-messages'. Return +a list with additional info from the placement hook. +Those lists get concatenated together and get passed +to the close hook." + (preview-clearout start end tempdir) + (let ((ov (make-overlay start end nil nil nil))) + (overlay-put ov 'priority (TeX-overlay-prioritize start end)) + (overlay-put ov 'preview-map + (preview-make-clickable + nil nil nil + (lambda (event) (interactive "e") + (preview-toggle ov 'toggle event)) + (lambda (event) (interactive "e") + (preview-context-menu ov event)))) + (overlay-put ov 'timestamp tempdir) + (when (cdr counters) + (overlay-put ov 'preview-counters counters) + (setq preview-buffer-has-counters t)) + (prog1 (apply #'preview-call-hook 'place ov snippet box + place-opts) + (overlay-put ov 'strings + (list (preview-active-string ov))) + (preview-toggle ov t)))) + +(defun preview-counter-find (begin) + "Fetch the next preceding or next preview-counters property. +Factored out because of compatibility macros XEmacs would +not use in advice." + (or (car (get-char-property begin 'preview-counters)) + (cdr (get-char-property (max (point-min) + (1- begin)) + 'preview-counters)) + (cdr (get-char-property + (max (point-min) + (1- (previous-single-char-property-change + begin + 'preview-counters))) + 'preview-counters)) + (car (get-char-property + (next-single-char-property-change begin 'preview-counters) + 'preview-counters)))) + +(defun preview--counter-information (begin) + "Return repeated \\setcounter declaration based on point BEGIN. +If `preview-buffer-has-counters' is non-nil, return string to +insert into region tex file containing as many +\\setcounter{COUNTER}{VALUE} as possible built from +`preview-counters' property near the point BEGIN. Otherwise, +return nil." + (if preview-buffer-has-counters + (mapconcat + #'identity + (cons + "" + (preview-counter-find begin)) + "\\setcounter"))) + +(defun preview-reinstate-preview (tempdirlist timestamp start end + image filename &optional counters) + "Reinstate a single preview. +This gets passed TEMPDIRLIST, a list consisting of the kind +of entries used in `TeX-active-tempdir', and TIMESTAMP, the +time stamp under which the file got read in. It returns an augmented +list. START and END give the buffer location where the preview +is to be situated, IMAGE the image to place there, and FILENAME +the file to use: a triple consisting of filename, its temp directory +and the corresponding topdir. COUNTERS is saved counter information, +if any." + (when + (or (null filename) (file-readable-p (car filename))) + (when filename + (unless (equal (nth 1 filename) (car TeX-active-tempdir)) + (setq TeX-active-tempdir + (or (assoc (nth 1 filename) tempdirlist) + (car (push (append (cdr filename) (list 0)) + tempdirlist)))) + (setcar (cdr TeX-active-tempdir) + (car (or (member (nth 1 TeX-active-tempdir) + preview-temp-dirs) + (progn + (add-hook 'kill-emacs-hook + #'preview-cleanout-tempfiles t) + (push (nth 1 TeX-active-tempdir) + preview-temp-dirs)))))) + (setcar (nthcdr 2 TeX-active-tempdir) + (1+ (nth 2 TeX-active-tempdir))) + (setcdr filename TeX-active-tempdir) + (setq filename (list filename))) + (let ((ov (make-overlay start end nil nil nil))) + (overlay-put ov 'priority (TeX-overlay-prioritize start end)) + (overlay-put ov 'preview-map + (preview-make-clickable + nil nil nil + (lambda (event) (interactive "e") + (preview-toggle ov 'toggle event)) + (lambda (event) (interactive "e") + (preview-context-menu ov event)))) + (when counters + (overlay-put + ov 'preview-counters + (cons + (mapcar #'cdr + (if (string= (car counters) "") + preview-parsed-counters + (setq preview-parsed-counters + (preview-parse-counters (car counters))))) + (mapcar #'cdr + (if (string= (cdr counters) "") + preview-parsed-counters + (setq preview-parsed-counters + (preview-parse-counters (cdr counters))))))) + (setq preview-buffer-has-counters t)) + (overlay-put ov 'filenames filename) + (overlay-put ov 'preview-image (cons (preview-import-image image) + image)) + (overlay-put ov 'strings + (list (preview-active-string ov))) + (overlay-put ov 'timestamp timestamp) + (preview-toggle ov t))) + tempdirlist) + +(defun preview-back-command (&optional nocomplex) + "Move backward a TeX token. +If NOCOMPLEX is set, only basic tokens and no argument sequences +will be skipped over backwards." + (let ((oldpos (point)) oldpoint) + (condition-case nil + (or (search-backward-regexp "\\(\\$\\$?\ +\\|\\\\[^a-zA-Z@]\ +\\|\\\\[a-zA-Z@]+\ +\\|\\\\begin[ \t]*{[^}]+}\ +\\)\\=" (line-beginning-position) t) + nocomplex + (if (eq ?\) (char-syntax (char-before))) + (while + (progn + (setq oldpoint (point)) + (backward-sexp) + (and (not (eq oldpoint (point))) + (eq ?\( (char-syntax (char-after)))))) + (backward-char))) + (error (goto-char oldpos))))) + +(defcustom preview-required-option-list '("active" "tightpage" "auctex" + (preview-preserve-counters + "counters")) + "Specifies required options passed to the preview package. +These are passed regardless of whether there is an explicit +\\usepackage of that package present." + :group 'preview-latex + :type preview-expandable-string) + +(defcustom preview-preserve-counters nil + "Try preserving counters for partial runs if set." + :group 'preview-latex + :type 'boolean) + +(defcustom preview-default-option-list '("displaymath" "floats" + "graphics" "textmath" "sections" + "footnotes") + "Specifies default options to pass to preview package. +These options are only used when the LaTeX document in question does +not itself load the preview package, namely when you use preview +on a document not configured for preview. \"auctex\", \"active\", +\"dvips\" and \"delayed\" need not be specified here." + :group 'preview-latex + :type '(list (set :inline t :tag "Options known to work" + :format "%t:\n%v%h" :doc + "The above options are all the useful ones +at the time of the release of this package. +You should not need \"Other options\" unless you +upgraded to a fancier version of just the LaTeX style. +Please also note that `psfixbb' fails to have an effect if +`preview-fast-conversion' or `preview-prefer-TeX-bb' +are selected." + (const "displaymath") + (const "floats") + (const "graphics") + (const "textmath") + (const "sections") + (const "footnotes") + (const "showlabels") + (const "psfixbb")) + (set :tag "Expert options" :inline t + :format "%t:\n%v%h" :doc + "Expert options should not be enabled permanently." + (const "noconfig") + (const "showbox") + (const "tracingall")) + (repeat :inline t :tag "Other options" (string)))) + +(defcustom preview-default-preamble + '("\\RequirePackage[" ("," . preview-default-option-list) + "]{preview}[2004/11/05]") + "Specifies default preamble code to add to a LaTeX document. +If the document does not itself load the preview package, that is, +when you use preview on a document not configured for preview, this +list of LaTeX commands is inserted just before \\begin{document}." + :group 'preview-latex + :type preview-expandable-string) + +(defcustom preview-LaTeX-command '("%`%l \"\\nonstopmode\\nofiles\ +\\PassOptionsToPackage{" ("," . preview-required-option-list) "}{preview}\ +\\AtBeginDocument{\\ifx\\ifPreview\\undefined" +preview-default-preamble "\\fi}\"%' \"\\detokenize{\" %(t-filename-only) \"}\"") + ;; Since TeXLive 2018, the default encoding for LaTeX files has been + ;; changed to UTF-8 if used with classic TeX or pdfTeX. I.e., + ;; \usepackage[utf8]{inputenc} is enabled by default in (pdf)latex. + ;; c.f. LaTeX News issue 28 + ;; Due to this change, \detokenize is required to recognize + ;; non-ascii characters in the file name when \input is supplemented + ;; implicitly by %`-%' pair. + "Command used for starting a preview. +See description of `TeX-command-list' for details." + :group 'preview-latex + :type preview-expandable-string) + +(defun preview-goto-info-page () + "Read documentation for preview-latex in the info system." + (interactive) + (info "(preview-latex)")) + +(eval-after-load 'info '(add-to-list 'Info-file-list-for-emacs + '("preview" . "preview-latex"))) + +(defvar preview-map + (let ((map (make-sparse-keymap))) + (define-key map "\C-p" #'preview-at-point) + (define-key map "\C-r" #'preview-region) + (define-key map "\C-b" #'preview-buffer) + (define-key map "\C-d" #'preview-document) + (define-key map "\C-f" #'preview-cache-preamble) + (define-key map "\C-c\C-f" #'preview-cache-preamble-off) + (define-key map "\C-i" #'preview-goto-info-page) + ;; (define-key map "\C-q" #'preview-paragraph) + (define-key map "\C-e" #'preview-environment) + (define-key map "\C-s" #'preview-section) + (define-key map "\C-w" #'preview-copy-region-as-mml) + (define-key map "\C-c\C-p" #'preview-clearout-at-point) + (define-key map "\C-c\C-r" #'preview-clearout) + (define-key map "\C-c\C-s" #'preview-clearout-section) + (define-key map "\C-c\C-b" #'preview-clearout-buffer) + (define-key map "\C-c\C-d" #'preview-clearout-document) + map)) + +(defun preview-copy-text (ov) + "Copy the text of OV into the kill buffer." + (with-current-buffer (overlay-buffer ov) + (copy-region-as-kill (overlay-start ov) (overlay-end ov)))) + +(defun preview-copy-mml (ov) + "Copy an MML representation of OV into the kill buffer. +This can be used to send inline images in mail and news when +using MML mode." + (when (catch 'badcolor + (let ((str (car (preview-format-mml ov)))) + (if str + (if (eq last-command #'kill-region) + (kill-append str nil) + (kill-new str)) + (error "No image file available"))) + nil) + (let (preview-transparent-border) + (preview-regenerate ov)))) + +(defun preview-copy-region-as-mml (start end) + (interactive "r") + (when (catch 'badcolor + (let (str lst dont-ask) + (dolist (ov (overlays-in start end)) + (when (setq str (preview-format-mml ov dont-ask)) + (setq dont-ask (cdr str)) + (and + (>= (overlay-start ov) start) + (<= (overlay-end ov) end) + (push (list (- (overlay-start ov) start) + (- (overlay-end ov) start) + (car str)) lst)))) + (setq str (buffer-substring start end)) + (dolist (elt (nreverse (sort lst #'car-less-than-car))) + (setq str (concat (substring str 0 (nth 0 elt)) + (nth 2 elt) + (substring str (nth 1 elt))))) + (if (eq last-command #'kill-region) + (kill-append str nil) + (kill-new str))) + nil) + (let (preview-transparent-border) + (preview-region start end)))) + +(autoload 'mailcap-extension-to-mime "mailcap") + +(defun preview-format-mml (ov &optional dont-ask) + "Return an MML representation of OV as string. +This can be used to send inline images in mail and news when +using MML mode. If there is nothing current available, +nil is returned. If the image has a colored border and the +user wants it removed when asked (unless DONT-ASK is set), +`badcolor' is thrown a t. The MML is returned in the car of the +result, DONT-ASK in the cdr." + (and (memq (overlay-get ov 'preview-state) '(active inactive)) + (not (overlay-get ov 'queued)) + (let* ((text (with-current-buffer (overlay-buffer ov) + (buffer-substring (overlay-start ov) + (overlay-end ov)))) + (image (cdr (overlay-get ov 'preview-image))) + file type) + (cond ((consp image) + (and (not dont-ask) + (nth 3 image) + (if (y-or-n-p "Replace colored borders? ") + (throw 'badcolor t) + (setq dont-ask t))) + (setq file (car (car (last (overlay-get ov 'filenames)))) + type (mailcap-extension-to-mime + (file-name-extension file))) + (cons + (format "<#part %s +description=\"%s\" +filename=%s> +<#/part>" + (if type + (format "type=\"%s\" disposition=inline" type) + "disposition=attachment") + (if (string-match "[\n\"]" text) + "preview-latex image" + text) + (if (string-match "[ \n<>]" file) + (concat "\"" file "\"") + file)) + dont-ask)) + ((stringp image) + (cons image dont-ask)))))) + +(defun preview-active-contents (ov) + "Check whether we have a valid image associated with OV." + (and (memq (overlay-get ov 'preview-state) '(active inactive)) t)) + +(defun preview-context-menu (ov ev) + "Pop up a menu for OV at position EV." + (popup-menu + `("Preview" + ["Toggle" (preview-toggle ,ov 'toggle ',ev) + (preview-active-contents ,ov)] + ["Regenerate" (preview-regenerate ,ov)] + ["Remove" (preview-delete ,ov)] + ["Copy text" (preview-copy-text ,ov)] + ["Copy MIME" (preview-copy-mml ,ov) + (preview-active-contents ,ov)]) + ev)) + +(defvar preview-TeX-style-dir) + +(defun preview-TeX-style-cooked () + "Return `preview-TeX-style-dir' in cooked form. +This will be fine for prepending to a `TEXINPUTS' style +environment variable, including an initial `.' at the front." + (if (or (zerop (length preview-TeX-style-dir)) + (member (substring preview-TeX-style-dir -1) '(";" ":"))) + preview-TeX-style-dir + (let ((sep + (cond + ((stringp TeX-kpathsea-path-delimiter) + TeX-kpathsea-path-delimiter) + ((string-match + "\\`.[:]" + (if (file-name-absolute-p preview-TeX-style-dir) + preview-TeX-style-dir + (expand-file-name preview-TeX-style-dir))) + ";") + (t ":")))) + (concat "." sep preview-TeX-style-dir sep)))) + +(defun preview-set-texinputs (&optional remove) + "Add `preview-TeX-style-dir' into `TEXINPUTS' variables. +With prefix argument REMOVE, remove it again." + (interactive "P") + (let ((case-fold-search nil) + (preview-TeX-style-dir (preview-TeX-style-cooked)) + pattern) + (if remove + (progn + (setq pattern (concat "\\`\\(TEXINPUTS[^=]*\\)=\\(.*\\)" + (regexp-quote preview-TeX-style-dir))) + (dolist (env (copy-sequence process-environment)) + (if (string-match pattern env) + (setenv (match-string 1 env) + (and (or (< (match-beginning 2) (match-end 2)) + (< (match-end 0) (length env))) + (concat (match-string 2 env) + (substring env (match-end 0)))))))) + (setq pattern (regexp-quote preview-TeX-style-dir)) + (dolist (env (cons "TEXINPUTS=" (copy-sequence process-environment))) + (if (string-match "\\`\\(TEXINPUTS[^=]*\\)=" env) + (unless (save-match-data (string-match pattern env)) + (setenv (match-string 1 env) + (concat preview-TeX-style-dir + (substring env (match-end 0)))))))))) + +(defcustom preview-TeX-style-dir nil + "This variable contains the location of uninstalled TeX styles. +If this is nil, the preview styles are considered to be part of +the installed TeX system. + +Otherwise, it can either just specify an absolute directory, or +it can be a complete TEXINPUTS specification. If it is the +latter, it has to be followed by the character with which +kpathsea separates path components, either `:' on Unix-like +systems, or `;' on Windows-like systems. And it should be +preceded with .: or .; accordingly in order to have . first in +the search path. + +The `TEXINPUTS' environment type variables will get this prepended +at load time calling \\[preview-set-texinputs] to reflect this. +You can permanently install the style files using +\\[preview-install-styles]. + +Don't set this variable other than with customize so that its +changes get properly reflected in the environment." + :group 'preview-latex + :set (lambda (var value) + (and (boundp var) + (symbol-value var) + (preview-set-texinputs t)) + (set var value) + (and (symbol-value var) + (preview-set-texinputs))) + :type '(choice (const :tag "Installed" nil) + (string :tag "Style directory or TEXINPUTS path"))) + +;;;###autoload +(defun preview-install-styles (dir &optional force-overwrite + force-save) + "Installs the TeX style files into a permanent location. +This must be in the TeX search path. If FORCE-OVERWRITE is greater +than 1, files will get overwritten without query, if it is less +than 1 or nil, the operation will fail. The default of 1 for interactive +use will query. + +Similarly FORCE-SAVE can be used for saving +`preview-TeX-style-dir' to record the fact that the uninstalled +files are no longer needed in the search path." + (interactive "DPermanent location for preview TeX styles +pp") + (unless preview-TeX-style-dir + (error "Styles are already installed")) + (dolist (file (or + (condition-case nil + (directory-files + (progn + (string-match + "\\`\\(\\.[:;]\\)?\\(.*?\\)\\([:;]\\)?\\'" + preview-TeX-style-dir) + (match-string 2 preview-TeX-style-dir)) + t "\\.\\(sty\\|def\\|cfg\\)\\'") + (error nil)) + (error "Can't find files to install"))) + (copy-file file dir (cond ((eq force-overwrite 1) 1) + ((numberp force-overwrite) + (> force-overwrite 1)) + (t force-overwrite)))) + (if (cond ((eq force-save 1) + (y-or-n-p "Stop using non-installed styles permanently ")) + ((numberp force-save) + (> force-save 1)) + (t force-save)) + (customize-save-variable 'preview-TeX-style-dir nil) + (customize-set-variable 'preview-TeX-style-dir nil))) + +(defun preview-mode-setup () + "Setup proper buffer hooks and behavior for previews." + (set (make-local-variable 'desktop-save-buffer) + #'desktop-buffer-preview-misc-data) + (add-hook 'pre-command-hook #'preview-mark-point nil t) + (add-hook 'post-command-hook #'preview-move-point nil t) + (when buffer-file-name + (let* ((filename (expand-file-name buffer-file-name)) + format-cons) + (when (string-match (concat "\\." TeX-default-extension "\\'") + filename) + (setq filename (substring filename 0 (match-beginning 0)))) + (setq format-cons (assoc filename preview-dumped-alist)) + (when (consp (cdr format-cons)) + (preview-unwatch-preamble format-cons) + (preview-watch-preamble (current-buffer) + (cadr format-cons) + format-cons))))) + +;;;###autoload +(defun LaTeX-preview-setup () + "Hook function for embedding the preview package into AUCTeX. +This is called by `LaTeX-mode-hook' and changes AUCTeX variables +to add the preview functionality." + ;; This has to be done only once. + (unless (and (boundp 'LaTeX-mode-hook) + (memq #'preview-mode-setup LaTeX-mode-hook)) + (remove-hook 'LaTeX-mode-hook #'LaTeX-preview-setup) + (add-hook 'LaTeX-mode-hook #'preview-mode-setup) + (define-key LaTeX-mode-map "\C-c\C-p" preview-map) + (easy-menu-define preview-menu LaTeX-mode-map + "This is the menu for preview-latex." + '("Preview" + "Generate previews" + ["(or toggle) at point" preview-at-point] + ["for environment" preview-environment] + ["for section" preview-section] + ["for region" preview-region mark-active] + ["for buffer" preview-buffer] + ["for document" preview-document] + "---" + "Remove previews" + ["at point" preview-clearout-at-point] + ["from section" preview-clearout-section] + ["from region" preview-clearout mark-active] + ["from buffer" preview-clearout-buffer] + ["from document" preview-clearout-document] + "---" + "Turn preamble cache" + ["on" preview-cache-preamble] + ["off" preview-cache-preamble-off] + "---" + ("Customize" + ["Browse options" + (customize-group 'preview)] + ["Extend this menu" + (easy-menu-add-item + nil '("Preview") + (customize-menu-create 'preview))]) + ["Read documentation" preview-goto-info-page] + ["Report Bug" preview-report-bug])) + (if (eq major-mode 'latex-mode) + (preview-mode-setup)) + (unless preview-tb-icon + (setq preview-tb-icon (preview-filter-specs preview-tb-icon-specs))) + (when preview-tb-icon + (define-key LaTeX-mode-map [tool-bar preview] + `(menu-item "Preview at point" preview-at-point + :image ,preview-tb-icon + :help "Preview on/off at point" + :vert-only t))) + (if (boundp 'desktop-buffer-misc) + (preview-buffer-restore desktop-buffer-misc)))) + +(defun preview-clean-subdir (dir) + "Cleans out a temporary DIR with preview image files." + (condition-case err + (progn + (mapc #'delete-file + (directory-files dir t "\\`pr" t)) + (delete-directory dir)) + (error (message "Deletion of `%s' failed: %s" dir + (error-message-string err))))) + +(defun preview-clean-topdir (topdir) + "Cleans out TOPDIR from temporary directories. +This does not erase the directory itself since its permissions +might be needed for colloborative work on common files." + (mapc #'preview-clean-subdir + (condition-case nil + (directory-files topdir t "\\`tmp" t) + (file-error nil)))) + +(defun preview-create-subdirectory () + "Create a temporary subdir for the current TeX process. +If necessary, generates a fitting top +directory or cleans out an existing one (if not yet +visited in this session), then returns the name of +the created subdirectory relative to the master directory, +in shell-quoted form. `TeX-active-tempdir' is +set to the corresponding TEMPDIR descriptor as described +in `preview-make-filename'. The directory is registered +in `preview-temp-dirs' in order not to be cleaned out +later while in use." + (let ((topdir (expand-file-name (TeX-active-master "prv")))) + (if (file-directory-p topdir) + (unless (member topdir preview-temp-dirs) + ;; Cleans out the top preview directory by + ;; removing subdirs possibly left from a previous session. + (preview-clean-topdir topdir) + (push topdir preview-temp-dirs)) + (make-directory topdir) + (add-to-list 'preview-temp-dirs topdir)) + (add-hook 'kill-emacs-hook #'preview-cleanout-tempfiles t) + (setq TeX-active-tempdir + (list (make-temp-file (expand-file-name + "tmp" (file-name-as-directory topdir)) t) + topdir + 0)) + (shell-quote-argument + (concat (file-name-as-directory + ;; Don't use topdir, because %m expects the path to be + ;; relative to master + (TeX-active-master "prv" t)) + (file-name-nondirectory (nth 0 TeX-active-tempdir)))))) + +(defun preview-parse-counters (string) + "Extract counter information from STRING." + (let ((list preview-parsed-counters) (pos 0)) + (while (eq pos (string-match " *\\({\\([^{}]+\\)}{[-0-9]+}\\)" string pos)) + (setcdr (or (assoc (match-string 2 string) list) + (car (push (list (match-string 2 string)) list))) + (match-string 1 string)) + (setq pos (match-end 1))) + list)) + +(defun preview-parse-tightpage (string) + "Build tightpage vector from STRING," + (read (concat "[" string "]"))) + +(defvar preview-parse-variables + '(("Fontsize" preview-parsed-font-size + "\\` *\\([0-9.]+\\)pt\\'" 1 string-to-number) + ("Magnification" preview-parsed-magnification + "\\` *\\([0-9]+\\)\\'" 1 string-to-number) + ("PDFoutput" preview-parsed-pdfoutput + "" 0 stringp) + ("Counters" preview-parsed-counters + ".*" 0 preview-parse-counters) + ("Tightpage" preview-parsed-tightpage + "\\` *\\(-?[0-9]+ *\\)\\{4\\}\\'" 0 preview-parse-tightpage))) + +(defun preview-error-quote (string) + "Turn STRING with potential ^^ sequences into a regexp. +To preserve sanity, additional ^ prefixes are matched literally, +so the character represented by ^^^ preceding extended characters +will not get matched, usually." + (let (output case-fold-search) + ;; Some coding systems (e.g. japanese-shift-jis) use regexp meta + ;; characters on encoding. Such meta characters would be + ;; interfered with `regexp-quote' below. Thus the idea of + ;; "encoding entire string beforehand and decoding it at the last + ;; stage" does not work for such coding systems. + ;; Rather, we work consistently with decoded text. + + ;; Bytes with value from 0x80 to 0xFF represented with ^^ form are + ;; converted to byte sequence, and decoded by the file coding + ;; system. + (setq string + (preview--decode-^^ab string buffer-file-coding-system)) + + ;; Then, control characters are taken into account. + (while (string-match "\\^\\{2,\\}\\([@-_?]\\)" string) + (setq output + (concat output + (regexp-quote (substring string + 0 + (- (match-beginning 1) 2))) + (concat + "\\(?:" (regexp-quote + (substring string + (- (match-beginning 1) 2) + (match-end 0))) + "\\|" + (char-to-string + (logxor (aref string (match-beginning 1)) 64)) + "\\)")) + string (substring string (match-end 0)))) + (setq output (concat output (regexp-quote string))) + output)) + +(defun preview--decode-^^ab (string coding-system) + "Decode ^^ sequences in STRING with CODING-SYSTEM. +Sequences of control characters such as ^^I are left untouched. + +Return a new string." + ;; Since the given string can contain multibyte characters, decoding + ;; should be performed seperately on each segment made up entirely + ;; with ASCII and raw 8-bit characters. + ;; Raw 8-bit characters can arise if the latex outputs multibyte + ;; characters with partial ^^-quoting. + (let ((result "")) + ;; Here we want to collect all the ASCII and raw 8-bit bytes, + ;; excluding proper multibyte characters. The regexp + ;; [^[:multibyte:]]+ serves for that purpose. The alternative + ;; [\x00-\xFF]+ does the job as well at least for emacs 24-26, so + ;; use it instead if the former becomes invalid in future. + ;; N.B. [[:unibyte:]]+ doesn't match raw 8-bit bytes, contrary to + ;; naive expectation. + (while (string-match "[^[:multibyte:]]+" string) + (setq result + (concat result + (substring string 0 (match-beginning 0)) + (let ((text + (save-match-data + (preview--convert-^^ab + (match-string 0 string))))) + (decode-coding-string text coding-system))) + string (substring string (match-end 0)))) + (setq result (concat result string)) + result)) + +(defun preview--convert-^^ab (string) + "Convert ^^ sequences in STRING to raw 8bit. +Sequences of control characters such as ^^I are left untouched. + +Return a new string." + (let ((result "")) + (while (string-match "\\^\\^[8-9a-f][0-9a-f]" string) + (setq result + (concat result + (substring string 0 (match-beginning 0)) + (let ((byte (string-to-number + (substring string + (+ (match-beginning 0) 2) + (match-end 0)) 16))) + (byte-to-string byte))) + string (substring string (match-end 0)))) + (setq result (concat result string)) + result)) + +(defun preview-parse-messages (open-closure) + "Turn all preview snippets into overlays. +This parses the pseudo error messages from the preview +document style for LaTeX. OPEN-CLOSURE is called once +it is certain that we have a valid output file, and it has +to return in its CAR the PROCESS parameter for the CLOSE +call, and in its CDR the final stuff for the placement hook." + (with-temp-message "locating previews..." + (let (TeX-error-file TeX-error-offset snippet box counters + file line + (lsnippet 0) lstart (lfile "") lline lbuffer lpoint + lcounters + string after-string + offset + parsestate (case-fold-search nil) + (run-buffer (current-buffer)) + (run-directory default-directory) + tempdir + close-data + open-data + fast-hook + slow-hook + TeX-translate-location-file + TeX-translate-location-line + TeX-translate-location-error + TeX-translate-location-offset + TeX-translate-location-context + TeX-translate-location-string) + ;; clear parsing variables + (dolist (var preview-parse-variables) + (set (nth 1 var) nil)) + (goto-char (point-min)) + (unwind-protect + (progn + (while + (re-search-forward "\ +^\\(!\\|\\(.*?\\):[0-9]+:\\) \\|\ +\(\\(/*\ +\\(?:\\.+[^()\r\n{} /]*\\|[^()\r\n{} ./]+\ +\\(?: [^()\r\n{} ./]+\\)*\\(?:\\.[-0-9a-zA-Z_.]*\\)?\\)\ +\\(?:/+\\(?:\\.+[^()\r\n{} /]*\\|[^()\r\n{} ./]+\ +\\(?: [^()\r\n{} ./]+\\)*\\(?:\\.[-0-9a-zA-Z_.]*\\)?\\)?\\)*\\)\ +)*\\(?: \\|\r?$\\)\\|\ +\\()+\\)\\|\ + !\\(?:offset(\\([---0-9]+\\))\\|\ +name(\\([^)]+\\))\\)\\|\ +^Preview: \\([a-zA-Z]+\\) \\([^\n\r]*\\)\r?$" nil t) +;;; Ok, here is a line by line breakdown: +;;; match-alternative 1: +;;; error indicator for TeX error, either style. +;;; match-alternative 2: +;;; The same, but file-line-error-style, matching on file name. +;;; match-alternative 3: +;;; Too ugly to describe in detail. In short, we try to catch file +;;; names built from path components that don't contain spaces or +;;; other special characters once the file extension has started. +;;; +;;; Position for searching immediately after the file name so as to +;;; not miss closing parens or something. +;;; (match-string 3) is the file name. +;;; match-alternative 4: +;;; )+\( \|$\) +;;; a closing paren followed by the end of line or a space: a just +;;; closed file. +;;; match-alternative 5 (wrapped into one shy group with +;;; match-alternative 6, so that the match on first char is slightly +;;; faster): +;;; !offset(\([---0-9]+\)) +;;; an AUCTeX offset message. (match-string 5) is the offset itself +;;; !name(\([^)]+\)) +;;; an AUCTeX file name message. (match-string 6) is the file name +;;; TODO: Actually, the latter two should probably again match only +;;; after a space or newline, since that it what \message produces. +;;; disabled in prauctex.def: +;;; \(?:Ov\|Und\)erfull \\.*[0-9]*--[0-9]* +;;; \(?:.\{79\} +;;; \)*.*$\)\| +;;; This would have caught overfull box messages that consist of +;;; several lines of context all with 79 characters in length except +;;; of the last one. prauctex.def kills all such messages. + (setq file (match-string-no-properties 2)) + (cond + ((match-beginning 1) + (if (looking-at "\ +\\(?:Preview\\|Package Preview Error\\): Snippet \\([---0-9]+\\) \\(started\\|ended\\(\ +\\.? *(\\([---0-9]+\\)\\+\\([---0-9]+\\)x\\([---0-9]+\\))\\)?\\)\\.") + (progn + (when file + (unless TeX-error-file + (push nil TeX-error-file) + (push nil TeX-error-offset)) + (unless (car TeX-error-offset) + (rplaca TeX-error-file file))) + (setq snippet (string-to-number (match-string 1)) + box (unless + (string= (match-string 2) "started") + (if (match-string 4) + (mapcar #'(lambda (x) + (* (preview-get-magnification) + (string-to-number x))) + (list + (match-string 4) + (match-string 5) + (match-string 6))) + t)) + counters (mapcar #'cdr preview-parsed-counters) + + ;; And the line number to position the cursor. + line (progn + (setq lpoint (point)) + (end-of-line) +;;; variant 1: profiling seems to indicate the regexp-heavy solution +;;; to be favorable. Removing incomplete characters from the error +;;; context is an absolute nuisance. + (and (re-search-forward "\ +^l\\.\\([0-9]+\\) \\(\\.\\.\\.\\(?:\\^*\\(?:[89a-f][0-9a-f]\\|[]@-\\_?]\\)\\|\ +\[0-9a-f]?\\)\\)?\\([^\n\r]*?\\)\r? +\\([^\n\r]*?\\)\\(\\(?:\\^+[89a-f]?\\)?\\.\\.\\.\\)?\r?$" nil t) + (string-to-number (match-string 1)))) + ;; And a string of the context to search for. + string (and line (match-string 3)) + after-string (and line (buffer-substring + (+ (match-beginning 4) + (- (match-end 3) + (match-beginning 0))) + (match-end 4))) + + ;; We may use these in another buffer. + offset (or (car TeX-error-offset) 0) + file (car TeX-error-file)) + (when (and (stringp file) + (or (string= file "<none>") + (TeX-match-extension file))) + ;; if we are the first time round, check for fast hooks: + (when (null parsestate) + (setq open-data + (save-excursion (funcall open-closure)) + tempdir TeX-active-tempdir) + (dolist + (lst (if (listp TeX-translate-location-hook) + TeX-translate-location-hook + (list TeX-translate-location-hook))) + (let ((fast + (and (symbolp lst) + (get lst 'TeX-translate-via-list)))) + (if fast + (setq fast-hook + (nconc fast-hook (list fast))) + (setq slow-hook + (nconc slow-hook (list lst))))))) + ;; Functions in `TeX-translate-location-hook' + ;; may examine and modify the following variables. + (setq TeX-translate-location-file file + TeX-translate-location-line line + ;; TeX-translate-location-error error + TeX-translate-location-offset offset + ;; TeX-translate-location-context context + TeX-translate-location-string string) + (condition-case err + (save-excursion (mapc #'funcall slow-hook)) + (error (preview-log-error err "Translation hook"))) + (setq file TeX-translate-location-file + line TeX-translate-location-line + ;; error TeX-translate-location-error + offset TeX-translate-location-offset + ;; context TeX-translate-location-context + string TeX-translate-location-string) + (push (vector file (+ line offset) + string after-string + snippet box counters) + parsestate))) + ;; else normal error message + (forward-line) + (re-search-forward "^l\\.[0-9]" nil t) + (forward-line 2))) + ((match-beginning 3) + ;; New file -- Push on stack + (push (match-string-no-properties 3) TeX-error-file) + (push nil TeX-error-offset) + (goto-char (match-end 3))) + ((match-beginning 4) + ;; End of file -- Pop from stack + (when (> (length TeX-error-file) 1) + (pop TeX-error-file) + (pop TeX-error-offset)) + (goto-char (1+ (match-beginning 0)))) + ((match-beginning 5) + ;; Hook to change line numbers + (setq TeX-error-offset + (list (string-to-number (match-string 5))))) + ((match-beginning 6) + ;; Hook to change file name + (setq TeX-error-file (list (match-string-no-properties 6)))) + ((match-beginning 7) + (let ((var + (assoc (match-string-no-properties 7) + preview-parse-variables)) + (offset (- (match-beginning 0) (match-beginning 8))) + (str (match-string-no-properties 8))) + ;; paste together continuation lines: + (while (= (- (length str) offset) 79) + (search-forward-regexp "^\\([^\n\r]*\\)\r?$") + (setq offset (- (length str)) + str (concat str (match-string-no-properties 1)))) + (when (and var + (string-match (nth 2 var) str)) + (set (nth 1 var) + (funcall (nth 4 var) + (match-string-no-properties + (nth 3 var) + str)))))))) + (when (null parsestate) + (error "LaTeX found no preview images"))) + (unwind-protect + (save-excursion + (setq parsestate (nreverse parsestate)) + (condition-case err + (dolist (fun fast-hook) + (setq parsestate + (save-excursion (funcall fun parsestate)))) + (error (preview-log-error err "Fast translation hook"))) + (setq snippet 0) + (dolist (state parsestate) + (setq lsnippet snippet + file (aref state 0) + line (aref state 1) + string (aref state 2) + after-string (aref state 3) + snippet (aref state 4) + box (aref state 5) + counters (aref state 6)) + (unless (string= lfile file) + (set-buffer (if (string= file "<none>") + (with-current-buffer run-buffer + TeX-command-buffer) + (find-file-noselect + (expand-file-name file run-directory)))) + (setq lfile file)) + (save-excursion + (save-restriction + (widen) + ;; a fast hook might have positioned us already: + (if (number-or-marker-p string) + (progn + (goto-char string) + (setq lpoint + (if (number-or-marker-p after-string) + after-string + (line-beginning-position)))) + (if (and (eq (current-buffer) lbuffer) + (<= lline line)) + ;; while Emacs does the perfectly correct + ;; thing even when when the line differences + ;; get zero or negative, I don't trust this + ;; to be universally the case across other + ;; implementations. Besides, if the line + ;; number gets smaller again, we are probably + ;; rereading the file, and restarting from + ;; the beginning will probably be faster. + (progn + (goto-char lpoint) + (if (/= lline line) + (if (eq selective-display t) + (re-search-forward "[\n\C-m]" nil + 'end + (- line lline)) + (forward-line (- line lline))))) + (goto-char (point-min)) + (forward-line (1- line))) + (setq lpoint (point)) + (cond + ((search-forward (concat string after-string) + (line-end-position) t) + (backward-char (length after-string))) + ;;ok, transform ^^ sequences + ((search-forward-regexp + (concat "\\(" + (setq string + (preview-error-quote + string)) + "\\)" + (setq after-string + (preview-error-quote + after-string))) + (line-end-position) t) + (goto-char (match-end 1))) + ((search-forward-regexp + (concat "\\(" + (if (string-match + "^[^\0-\177]\\{1,6\\}" string) + (setq string + (substring string (match-end 0))) + string) + "\\)" + (if (string-match + "[^\0-\177]\\{1,6\\}$" after-string) + (setq after-string + (substring after-string + 0 (match-beginning 0))))) + (line-end-position) t) + (goto-char (match-end 1))) + (t (search-forward-regexp + string + (line-end-position) t)))) + (setq lline line + lbuffer (current-buffer)) + (if box + (progn + (if (and lstart (= snippet lsnippet)) + (setq close-data + (nconc + (preview-place-preview + snippet + (save-excursion + (preview-back-command + (= (prog1 (point) + (goto-char lstart)) + lstart)) + (point)) + (point) + (preview-TeX-bb box) + (cons lcounters counters) + tempdir + (cdr open-data)) + close-data)) + (with-current-buffer run-buffer + (preview-log-error + (list 'error + (format + "End of Preview snippet %d unexpected" + snippet)) "Parser"))) + (setq lstart nil)) + ;; else-part of if box + (setq lstart (point) lcounters counters) + ;; >= because snippets in between might have + ;; been ignored because of TeX-default-extension + (unless (>= snippet (1+ lsnippet)) + (with-current-buffer run-buffer + (preview-log-error + (list 'error + (format + "Preview snippet %d out of sequence" + snippet)) "Parser")))))))) + (preview-call-hook 'close (car open-data) close-data)))))) + +(defun preview-get-dpi () + ;; TODO: Remove false-case when required emacs version is bumped to + ;; 24.4 or newer as this is the version where + ;; `frame-monitor-attributes' has been introduced. + (if (fboundp 'frame-monitor-attributes) + (let* ((monitor-attrs (frame-monitor-attributes)) + (mm-dims (cdr (assoc 'mm-size monitor-attrs))) + (mm-width (nth 0 mm-dims)) + (mm-height (nth 1 mm-dims)) + (pixel-dims (cdddr (assoc 'geometry monitor-attrs))) + (pixel-width (nth 0 pixel-dims)) + (pixel-height (nth 1 pixel-dims))) + (cons (/ (* 25.4 pixel-width) mm-width) + (/ (* 25.4 pixel-height) mm-height))) + (cons (/ (* 25.4 (display-pixel-width)) + (display-mm-width)) + (/ (* 25.4 (display-pixel-height)) + (display-mm-height))))) + +(defun preview-get-geometry () + "Transfer display geometry parameters from current display. +Return list of scale, resolution and colors. Calculation +is done in current buffer." + (condition-case err + (let* ((geometry + (list (preview-hook-enquiry preview-scale-function) + (preview-get-dpi) + (preview-get-colors))) + (preview-min-spec + (* (cdr (nth 1 geometry)) + (/ + (preview-inherited-face-attribute + 'preview-reference-face :height 'default) + 720.0)))) + (setq preview-icon (preview-make-image 'preview-icon-specs) + preview-error-icon (preview-make-image + 'preview-error-icon-specs) + preview-nonready-icon (preview-make-image + 'preview-nonready-icon-specs)) + geometry) + (error (error "Display geometry unavailable: %s" + (error-message-string err))))) + +(defun preview-set-geometry (geometry) + "Set geometry variables from GEOMETRY. +Buffer-local `preview-scale', `preview-resolution', +and `preview-colors' are set as given." + (setq preview-scale (nth 0 geometry) + preview-resolution (nth 1 geometry) + preview-colors (nth 2 geometry))) + +(defun preview-get-colors () + "Return colors from the current display. +Fetches the current screen colors and makes a vector +of colors as numbers in the range 0..65535. +Pure borderless black-on-white will return triple nil. +The fourth value is the transparent border thickness." + (let + ((bg (color-values (preview-inherited-face-attribute + 'preview-reference-face :background 'default))) + (fg (color-values (preview-inherited-face-attribute + 'preview-reference-face :foreground 'default))) + (mask (preview-get-heuristic-mask))) + (if (equal '(65535 65535 65535) bg) + (setq bg nil)) + (if (equal '(0 0 0) fg) + (setq fg nil)) + (unless (and (numberp preview-transparent-border) + (consp mask) (integerp (car mask))) + (setq mask nil)) + (vector bg fg mask preview-transparent-border))) + +(defun preview-start-dvipng () + "Start a DviPNG process.." + (let* (;; (file preview-gs-file) + tempdir + (res (/ (* (car preview-resolution) + (preview-hook-enquiry preview-scale)) + (preview-get-magnification))) + (resolution (format " -D%d " res)) + (colors (preview-dvipng-color-string preview-colors res)) + (command (with-current-buffer TeX-command-buffer + (prog1 + (concat (TeX-command-expand preview-dvipng-command) + " " colors resolution) + (setq tempdir TeX-active-tempdir)))) + (name "Preview-DviPNG")) + (setq TeX-active-tempdir tempdir) + (goto-char (point-max)) + (insert-before-markers "Running `" name "' with ``" command "''\n") + (setq mode-name name) + (setq TeX-sentinel-function + (lambda (_process name) (message "%s: done." name))) + (if TeX-process-asynchronous + (let ((process (start-process name (current-buffer) TeX-shell + TeX-shell-command-option + command))) + (if TeX-after-start-process-function + (funcall TeX-after-start-process-function process)) + (TeX-command-mode-line process) + (set-process-filter process #'TeX-command-filter) + (set-process-sentinel process #'TeX-command-sentinel) + (set-marker (process-mark process) (point-max)) + (push process compilation-in-progress) + (sit-for 0) + process) + (setq mode-line-process ": run") + (force-mode-line-update) + (call-process TeX-shell nil (current-buffer) nil + TeX-shell-command-option + command)))) + +(defun preview-start-dvips (&optional fast) + "Start a DviPS process. +If FAST is set, do a fast conversion." + (let* (;; (file preview-gs-file) + tempdir + (command (with-current-buffer TeX-command-buffer + (prog1 + (TeX-command-expand (if fast + preview-fast-dvips-command + preview-dvips-command)) + (setq tempdir TeX-active-tempdir)))) + (name "Preview-DviPS")) + (setq TeX-active-tempdir tempdir) + (setq preview-ps-file (and fast + (preview-make-filename + (preview-make-filename + "preview.ps" tempdir) + tempdir))) + (goto-char (point-max)) + (insert-before-markers "Running `" name "' with ``" command "''\n") + (setq mode-name name) + (setq TeX-sentinel-function + (lambda (_process name) (message "%s: done." name))) + (if TeX-process-asynchronous + (let ((process (start-process name (current-buffer) TeX-shell + TeX-shell-command-option + command))) + (if TeX-after-start-process-function + (funcall TeX-after-start-process-function process)) + (TeX-command-mode-line process) + (set-process-filter process #'TeX-command-filter) + (set-process-sentinel process #'TeX-command-sentinel) + (set-marker (process-mark process) (point-max)) + (push process compilation-in-progress) + (sit-for 0) + process) + (setq mode-line-process ": run") + (force-mode-line-update) + (call-process TeX-shell nil (current-buffer) nil + TeX-shell-command-option + command)))) + +(defun preview-start-pdf2dsc () + "Start a PDF2DSC process." + (let* ((file preview-gs-file) + tempdir + pdfsource + (command (with-current-buffer TeX-command-buffer + (prog1 + (TeX-command-expand preview-pdf2dsc-command) + (setq tempdir TeX-active-tempdir + pdfsource (funcall (car file) "pdf" t))))) + (name "Preview-PDF2DSC")) + (setq TeX-active-tempdir tempdir) + (setq preview-ps-file (preview-attach-filename + pdfsource + (preview-make-filename + (preview-make-filename + "preview.dsc" tempdir) + tempdir))) + (goto-char (point-max)) + (insert-before-markers "Running `" name "' with ``" command "''\n") + (setq mode-name name) + (setq TeX-sentinel-function + (lambda (_process name) (message "%s: done." name))) + (if TeX-process-asynchronous + (let ((process (start-process name (current-buffer) TeX-shell + TeX-shell-command-option + command))) + (if TeX-after-start-process-function + (funcall TeX-after-start-process-function process)) + (TeX-command-mode-line process) + (set-process-filter process #'TeX-command-filter) + (set-process-sentinel process #'TeX-command-sentinel) + (set-marker (process-mark process) (point-max)) + (push process compilation-in-progress) + (sit-for 0) + process) + (setq mode-line-process ": run") + (force-mode-line-update) + (call-process TeX-shell nil (current-buffer) nil + TeX-shell-command-option + command)))) + +(defun preview-TeX-inline-sentinel (process _name) + "Sentinel function for preview. +See `TeX-sentinel-function' and `set-process-sentinel' +for definition of PROCESS and NAME." + (if process (TeX-format-mode-line process)) + (let ((status (process-status process))) + (if (memq status '(signal exit)) + (delete-process process)) + (when (eq status 'exit) + (save-excursion + (goto-char (point-max)) + (forward-line -1) + (if (search-forward "abnormally with code 1" nil t) + (replace-match "as expected with code 1" t t) + (if (search-forward "finished" nil t) + (insert " with nothing to show")))) + (condition-case err + (preview-call-hook 'open) + (error (preview-log-error err "LaTeX" process))) + (preview-reraise-error process)))) + +(defcustom preview-format-extensions '(".fmt" ".efmt") + "Possible extensions for format files. +Those are just needed for cleanup." + :group 'preview-latex + :type '(repeat string)) + +(defun preview-format-kill (format-cons) + "Kill a cached format. +FORMAT-CONS is intended to be an element of `preview-dumped-alist'. +Tries through `preview-format-extensions'." + (dolist (ext preview-format-extensions) + (condition-case nil + (delete-file (preview-dump-file-name (concat (car format-cons) ext))) + (file-error nil)))) + +(defun preview-dump-file-name (file) + "Make a file name suitable for dumping from FILE." + (if file + (concat (file-name-directory file) + "prv_" + (progn + (setq file (file-name-nondirectory file)) + (while (string-match " " file) + (setq file (replace-match "_" t t file))) + file)) + "prv_texput")) + +(defun preview-do-replacements (string replacements) + "Perform replacements in string. +STRING is the input string, REPLACEMENTS is a list of replacements. +A replacement is a cons-cell, where the car is the match string, +and the cdr is a list of strings or symbols. Symbols get dereferenced, +and strings get evaluated as replacement strings." + (let (rep case-fold-search) + (while replacements + (setq rep (pop replacements)) + (cond ((symbolp rep) + (setq string (preview-do-replacements + string (symbol-value rep)))) + ((string-match (car rep) string) + (setq string + (mapconcat (lambda(x) + (if (symbolp x) + (symbol-value x) + (replace-match x t nil string))) + (cdr rep) "")))))) + string) + +(defconst preview-LaTeX-disable-pdfoutput + '(("\\`\\(pdf[^ ]*\\)\ +\\(\\( +[-&]\\([^ \"]\\|\"[^\"]*\"\\)*\\|\ + +\"[-&][^\"]*\"\\)*\\)\\(.*\\)\\'" + . ("\\1\\2 \"\\\\pdfoutput=0 \" \\5"))) + "This replacement places `\"\\pdfoutput=0 \"' after the options +of any command starting with `pdf'.") + +(defcustom preview-LaTeX-command-replacements + nil + "Replacement for `preview-LaTeX-command'. +This is passed through `preview-do-replacements'." + :group 'preview-latex + :type '(repeat + (choice + (symbol :tag "Named replacement" :value preview-LaTeX-disable-pdfoutput) + (cons (string :tag "Matched string") + (repeat :tag "Concatenated elements for replacement" + (choice (symbol :tag "Variable with literal string") + (string :tag "non-literal regexp replacement"))))))) + +(defvar preview-format-name nil + "Format name when enabling preamble cache.") + +(defcustom preview-dump-replacements + '(preview-LaTeX-command-replacements + ;; If -kanji option exists, pick it up as the second match. + ;; Discard all other options. + ("\\`\\([^ ]+\\)\ +\\(?: +\\(?:\\(--?kanji[= ][^ ]+\\)\\|\\(--?output-directory[= ][^ ]+\\)\\|-\\(?:[^ \\\"]\\|\\\\.\\|\"[^\"]*\"\\)*\\)\\)*\\(.*\\)\\'" + . ("\\1 -ini \\2 \\3 -interaction=nonstopmode \"&\\1\" " preview-format-name ".ini \\4"))) + "Generate a dump command from the usual preview command." + :group 'preview-latex + :type '(repeat + (choice (symbol :tag "Named replacement") + (cons string (repeat (choice symbol string)))))) + +(defcustom preview-undump-replacements + ;; If -kanji option exists, pick it up as the second match. + ;; Discard all other options. + '(("\\`\\([^ ]+\\)\ +\\(?: +\\(?:\\(--?kanji[= ][^ ]+\\)\\|\\(--?output-directory[= ][^ ]+\\)\\|-\\(?:[^ \\\"]\\|\\\\.\\|\"[^\"]*\"\\)*\\)\\)*.*\ + \"\\\\input\" \"\\\\detokenize{\" \\(.*\\) \"}\"\\'" + . ("\\1 \\2 \\3 -interaction=nonstopmode -file-line-error " + preview-format-name " \"/AUCTEXINPUT{\" \\4 \"}\""))) + ;; See the ini file code below in `preview-cache-preamble' for the + ;; weird /AUCTEXINPUT construct. In short, it is crafted so that + ;; dumped format file can read file of non-ascii name. + "Use a dumped format for reading preamble." + :group 'preview-latex + :type '(repeat + (choice (symbol :tag "Named replacement") + (cons string (repeat (choice symbol string)))))) + + +(defun preview-cache-preamble (&optional format-cons) + "Dump a pregenerated format file. +For the rest of the session, this file is used when running +on the same master file. + +Return the process for dumping, nil if there is still a valid +format available. + +If FORMAT-CONS is non-nil, a previous format may get reused." + (interactive) + (setq TeX-current-process-region-p nil) + (let* ((dump-file + (expand-file-name (preview-dump-file-name (TeX-master-file "ini")))) + (master (TeX-master-file)) + (format-name (expand-file-name master)) + (preview-format-name (shell-quote-argument + (preview-dump-file-name (file-name-nondirectory + master)))) + (master-file (expand-file-name (TeX-master-file t))) + (command (preview-do-replacements + (TeX-command-expand + (preview-string-expand preview-LaTeX-command)) + preview-dump-replacements)) + (preview-auto-cache-preamble nil)) + (unless (and (consp (cdr format-cons)) + (string= command (cadr format-cons))) + (unless format-cons + (setq format-cons (assoc format-name preview-dumped-alist))) + (if format-cons + (preview-cache-preamble-off format-cons) + (setq format-cons (list format-name)) + (push format-cons preview-dumped-alist)) + ;; mylatex.ltx expects a file name to follow. Bad. `.tex' + ;; in the tools bundle is an empty file. + (write-region "\\let\\PREVIEWdump\\dump\\def\\dump{% +\\edef\\next{{\\ifx\\pdfoutput\\undefined\\else\ +\\pdfoutput=\\the\\pdfoutput\\relax\\fi\ +\\the\\everyjob}}\\everyjob\\next\\catcode`\\ 10 % +\\catcode`/ 0 % +\\def\\AUCTEXINPUT##1{\\catcode`/ 12\\relax\\catcode`\\ 9\\relax\\input\\detokenize{##1}\\relax}% +\\let\\dump\\PREVIEWdump\\dump}\\input mylatex.ltx \\relax%\n" nil dump-file) + (TeX-save-document #'TeX-master-file) + (prog1 (preview-generate-preview master command) + (add-hook 'kill-emacs-hook #'preview-cleanout-tempfiles t) + (setq TeX-sentinel-function + (lambda (process _status) + (condition-case err + (progn + (if (and (eq (process-status process) 'exit) + (zerop (process-exit-status process))) + (preview-watch-preamble + master-file + command + format-cons) + (preview-format-kill format-cons)) + (delete-file dump-file)) + (error (preview-log-error err "Dumping" process))) + (preview-reraise-error process))))))) + +(defun preview-cache-preamble-off (&optional old-format) + "Clear the pregenerated format file. +The use of the format file is discontinued. +OLD-FORMAT may already contain a format-cons as +stored in `preview-dumped-alist'." + (interactive) + (unless old-format + (setq old-format + (let ((master-file (expand-file-name (TeX-master-file)))) + (or (assoc master-file preview-dumped-alist) + (car (push (list master-file) preview-dumped-alist)))))) + (preview-unwatch-preamble old-format) + (preview-format-kill old-format) + (setcdr old-format nil)) + +(defun preview-region (begin end) + "Run preview on region between BEGIN and END." + (interactive "r") + (let ((TeX-region-extra + ;; Write out counter information to region. + (concat (preview--counter-information begin) + TeX-region-extra))) + (TeX-region-create (TeX-region-file TeX-default-extension) + (buffer-substring-no-properties begin end) + (if buffer-file-name + (file-name-nondirectory buffer-file-name) + "<none>") + (TeX-current-offset begin))) + (setq TeX-current-process-region-p t) + (preview-generate-preview (TeX-region-file) + (preview-do-replacements + (TeX-command-expand + (preview-string-expand preview-LaTeX-command)) + preview-LaTeX-command-replacements))) + +(defun preview-buffer () + "Run preview on current buffer." + (interactive) + (preview-region (point-min) (point-max))) + +;; We have a big problem: When we are dumping preambles, diagnostics +;; issued in later runs will not make it to the output when the +;; predumped format skips the preamble. So we have to place those +;; after \begin{document}. This we can only do if regions never +;; include the preamble. We could do this in our own functions, but +;; that would not extend to the operation of C-c C-r g RET. So we +;; make this preamble skipping business part of TeX-region-create. +;; This will fail if the region is to contain just part of the +;; preamble -- a bad idea anyhow. + +(defun preview--skip-preamble-region (region-text region-offset) + "Skip preamble for the sake of predumped formats. +Helper function of `TeX-region-create'. + +If REGION-TEXT doesn't contain preamble, it returns nil. +Otherwise, it returns cons (ALTERED-TEXT . ALTERED-OFFSET) where +ALTERED-TEXT is REGION-TEXT without the preamble part and +ALTERED-OFFSET is REGION-OFFSET increased by the number of lines +of the preamble part of REGION-TEXT." + (if (string-match TeX-header-end region-text) + (cons (substring region-text (match-end 0)) + (with-temp-buffer + (insert (substring region-text 0 (match-end 0))) + (+ region-offset (TeX-current-offset)))))) + +(defun preview-document () + "Run preview on master document." + (interactive) + (TeX-save-document #'TeX-master-file) + (setq TeX-current-process-region-p nil) + (preview-generate-preview + (TeX-master-file) + (preview-do-replacements + (TeX-command-expand + (preview-string-expand preview-LaTeX-command)) + preview-LaTeX-command-replacements))) + +(defun preview-environment (count) + "Run preview on LaTeX environment. +This avoids running environments through preview that are +indicated in `preview-inner-environments'. If you use a prefix +argument COUNT, the corresponding level of outward nested +environments is selected." + (interactive "p") + (save-excursion + (let (currenv) + (dotimes (_ (1- count)) + (setq currenv (LaTeX-current-environment)) + (if (string= currenv "document") + (error "No enclosing outer environment found")) + (LaTeX-find-matching-begin)) + (while (member (setq currenv (LaTeX-current-environment)) + preview-inner-environments) + (LaTeX-find-matching-begin)) + (if (string= currenv "document") + (error "No enclosing outer environment found")) + (preview-region + (save-excursion (LaTeX-find-matching-begin) (point)) + (save-excursion (LaTeX-find-matching-end) (point)))))) + +(defun preview-section () + "Run preview on LaTeX section." (interactive) + (save-excursion + (LaTeX-mark-section) + (preview-region (region-beginning) (region-end)))) + + +(defun preview-generate-preview (file command) + "Generate a preview. +FILE the file (without default extension), COMMAND is the command +to use. + +It returns the started process." + (let* ((geometry (preview-get-geometry)) + (commandbuff (current-buffer)) + (pr-file (cons + #'TeX-active-master + (file-name-nondirectory file))) + (master (TeX-master-file)) + (master-file (expand-file-name master)) + (dumped-cons (assoc master-file + preview-dumped-alist)) + process) + (unless dumped-cons + (push (setq dumped-cons (cons master-file + (if (eq preview-auto-cache-preamble 'ask) + (y-or-n-p "Cache preamble? ") + preview-auto-cache-preamble))) + preview-dumped-alist)) + (when (cdr dumped-cons) + (let* (TeX-current-process-region-p) + (setq process (preview-cache-preamble dumped-cons)) + (if process + ;; FIXME: Use `add-function'. + (setq TeX-sentinel-function + (let ((prev-fun TeX-sentinel-function)) + (lambda (process string) + (funcall prev-fun process string) + (TeX-inline-preview-internal + command file + pr-file commandbuff + dumped-cons + master + geometry + (buffer-string)))))))) + (or process + (TeX-inline-preview-internal command file + pr-file commandbuff + dumped-cons master + geometry)))) + +(defun TeX-inline-preview-internal (command file pr-file + commandbuff dumped-cons _master + geometry + &optional str) + "Internal stuff for previewing. +COMMAND and FILE should be explained in `TeX-command-list'. +PR-FILE is the target file name in the form for `preview-gs-file'. +COMMANDBUFF, DUMPED-CONS, MASTER, and GEOMETRY are +internal parameters, STR may be a log to insert into the current log." + (set-buffer commandbuff) + (let* + ((preview-format-name (shell-quote-argument + (concat "&" + (preview-dump-file-name + ;; Get the filename from + ;; `TeX-master-file' with prv to + ;; get the correct path but then + ;; strip the extension + (file-name-sans-extension + (TeX-master-file "prv" t)))))) + (process-environment (copy-sequence process-environment)) + (process + (progn + ;; Fix Bug#20773, Bug#27088. + ;; Make LaTeX not to insert newline in lines necessary to + ;; identify Bounding Boxes. + (setenv "max_print_line" "1000") + (TeX-run-command + "Preview-LaTeX" + (if (consp (cdr dumped-cons)) + (preview-do-replacements + command + preview-undump-replacements) + command) + file)))) + (condition-case err + (progn + (when str + (save-excursion + (goto-char (point-min)) + (insert str) + (when (= (process-mark process) (point-min)) + (set-marker (process-mark process) (point))))) + (preview-set-geometry geometry) + (setq preview-gs-file pr-file) + (setq TeX-sentinel-function #'preview-TeX-inline-sentinel) + (TeX-parse-reset) + (setq TeX-parse-function #'TeX-parse-TeX) + (if TeX-process-asynchronous + process + (TeX-synchronous-sentinel "Preview-LaTeX" file process))) + (error (preview-log-error err "Preview" process) + (delete-process process) + (preview-reraise-error process))))) + +(defconst preview-version AUCTeX-version + "Preview version. +If not a regular release, the date of the last change.") + +(defconst preview-release-date AUCTeX-date + "Preview release date using the ISO 8601 format, yyyy-mm-dd.") + +(defun preview-dump-state (buffer) + (condition-case nil + (progn + (unless (local-variable-p 'TeX-command-buffer (current-buffer)) + (setq buffer (with-current-buffer buffer (TeX-active-buffer)))) + (when (bufferp buffer) + (insert "\nRun buffer contents:\n\n") + (if (< (buffer-size buffer) 5000) + (insert-buffer-substring buffer) + (insert-buffer-substring buffer 1 2500) + (insert "...\n\n[...]\n\n\t...") + (insert-buffer-substring buffer + (- (buffer-size buffer) 2500) + (buffer-size buffer))) + (insert "\n"))) + (error nil))) + +;;;###autoload +(defun preview-report-bug () "Report a bug in the preview-latex package." + (interactive) + (let ((reporter-prompt-for-summary-p "Bug report subject: ")) + (reporter-submit-bug-report + "bug-auctex@gnu.org" + preview-version + '(AUCTeX-version + LaTeX-command-style + image-types + preview-image-type + preview-image-creators + preview-dvipng-image-type + preview-dvipng-command + preview-pdf2dsc-command + preview-gs-command + preview-gs-options + preview-gs-image-type-alist + preview-fast-conversion + preview-prefer-TeX-bb + preview-dvips-command + preview-fast-dvips-command + preview-scale-function + preview-LaTeX-command + preview-required-option-list + preview-preserve-counters + preview-default-option-list + preview-default-preamble + preview-LaTeX-command-replacements + preview-dump-replacements + preview-undump-replacements + preview-auto-cache-preamble + preview-TeX-style-dir) + (let ((buf (current-buffer))) + (lambda () (preview-dump-state buf))) + (lambda () + (insert (format "\nOutput from running `%s -h':\n" + preview-gs-command)) + (call-process preview-gs-command nil t nil "-h") + (insert "\n")) + "Remember to cover the basics. Including a minimal LaTeX example +file exhibiting the problem might help." + ))) + +(provide 'preview) +;;; preview.el ends here |