branch: externals/auctex-label-numbers
commit 462f9bf626d3f6c951905d37c1f8aaddf50cbc3b
Author: Paul Nelson <63298781+ultron...@users.noreply.github.com>
Commit: GitHub <nore...@github.com>

    Add files via upload
---
 tex-numbers.el | 224 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 224 insertions(+)

diff --git a/tex-numbers.el b/tex-numbers.el
new file mode 100644
index 0000000000..4eea0dcfa9
--- /dev/null
+++ b/tex-numbers.el
@@ -0,0 +1,224 @@
+;;; tex-numbers.el --- numbering for LaTeX previews and folds  -*- 
lexical-binding: t; -*-
+
+;; Copyright (C) 2024  Paul D. Nelson
+
+;; Author: Paul D. Nelson <nelson.paul.da...@gmail.com>
+;; Version: 0.0
+;; URL: https://github.com/ultronozm/czm-preview.el
+;; Package-Requires: ((emacs "26.1") (auctex))
+;; Keywords: tex
+
+;; This program 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 of the License, or
+;; (at your option) any later version.
+
+;; This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This package augments the preview and folding features of AUCTeX:
+;; previews of labeled equations are numbered as in the compiled
+;; document, and the the macros \\ref, \\eqref, and \\label are folded
+;; with the corresponding numbers.
+;;
+;; Install via TODO.
+;;
+;; TEMPORARY NOTE: this package currently only works with the master
+;; branch of AUCTeX.  The more elaborate package
+;; https://github.com/ultronozm/czm-tex-fold.el works with released
+;; versions, but relies on advice, etc.
+;;
+;; Activate via M-x tex-numbers-mode, or by adding to your init file:
+;;
+;; (use-package tex-numbers
+;;   :hook
+;;   (LaTeX-mode . tex-numbers-mode))
+;;
+;; The package provides an interface for retrieving label numbers in
+;; LaTeX documents.  This interface is used to implement a global
+;; minor mode, `tex-numbers-mode', which enables the noted features.
+;;
+;; The label numbers are retrieved from the aux file of the compiled
+;; document.  To update them, one should compile the document,
+;; regenerate the previews and refresh the folds.
+;;
+;; By customizing the variable `tex-numbers-label-to-number-function', one
+;; can specify a different way to retrieve label numbers, e.g., by
+;; querying an LSP server.
+
+;;; Code:
+
+(require 'tex)
+(require 'latex)
+(require 'tex-fold)
+(require 'preview)
+
+(defgroup tex-numbers nil
+  "Numbering for LaTeX previews and folds."
+  :group 'AUCTeX)
+
+(defvar tex-numbers-cache (make-hash-table :test 'equal)
+  "Cache of label numbers from aux files.
+The keys are aux file names.  The values are hash tables, mapping label
+strings to label number strings.")
+
+(defun tex-numbers-update-cache (aux-file)
+  "Update the cache for AUX-FILE.
+Return the updated cache, or nil if the aux file does not exist."
+  (when (file-exists-p aux-file)
+    (with-temp-buffer
+      (insert-file-contents aux-file)
+      (let ((cache (make-hash-table :test 'equal))
+            (pattern "\\newlabel{\\([^}]+\\)}{{\\([^}]+\\)}"))
+        (save-excursion
+          (goto-char (point-min))
+          (while (re-search-forward pattern nil t)
+            (let ((label (match-string 1))
+                  (number (match-string 2)))
+              (puthash label number cache))))
+        (puthash 'timestamp (current-time) cache)
+        (puthash aux-file cache tex-numbers-cache)
+        cache))))
+
+(defun tex-numbers-label-to-number-helper (label aux-file)
+  "Get the number of LABEL from the aux file AUX-FILE.
+Check the cache first, and update it if the aux file has changed.
+Return the label number as a string, or nil if the label cannot be
+found."
+  (let ((cache (gethash aux-file tex-numbers-cache)))
+    (if (or (not cache)
+            (time-less-p (gethash 'timestamp cache)
+                         (nth 5 (file-attributes aux-file))))
+        (setq cache (tex-numbers-update-cache aux-file)))
+    (when cache
+      (gethash label cache))))
+
+(defcustom tex-numbers-label-to-number-function nil
+  "Function to retrieve label numbers.
+If non-nil, `tex-numbers-label-to-number' delegates to this function.
+The function should take a label string as its argument and return the
+corresponding label number as a string, or nil if that number cannot be
+retrieved."
+  :type '(choice (const :tag "Default" nil) function)
+  :group 'tex-numbers)
+
+(defun tex-numbers-label-to-number (label)
+  "Get number of LABEL for current tex buffer.
+If the buffer does not point to a file, or if the corresponding
+aux file does not exist, or if the label cannot be found, then
+return nil.  Otherwise, return the label number as a string.  If
+the label is found in an external document, prefix the string
+with \"X\"."
+  (if tex-numbers-label-to-number-function
+      (funcall tex-numbers-label-to-number-function label)
+    (or
+     (when-let* ((aux-file (TeX-master-file "aux")))
+       (tex-numbers-label-to-number-helper label aux-file))
+     ;; If we can't retrieve the label from the main file, then we look
+     ;; at any external documents.
+     (save-excursion
+       (save-restriction
+         (widen)
+         (goto-char (point-min))
+         (let (found)
+           (while (and (null found)
+                       (re-search-forward "\\\\externaldocument{\\([^}]+\\)}" 
nil t))
+             (let* ((filename (concat (match-string 1) ".aux")))
+               (setq found (tex-numbers-label-to-number-helper label 
filename))))
+           (when found
+             (concat "X" found))))))))
+
+(defun tex-numbers-preview-preprocessor (str)
+  "Preprocess STR for preview by adding tags to labels.
+Uses `tex-numbers-label-to-number-function' to retrieve label numbers."
+  (let ((buf (current-buffer)))
+    (with-temp-buffer
+      (insert str)
+      (goto-char (point-min))
+      (while (re-search-forward "\\\\label{\\([^}]+\\)}" nil t)
+        (let ((label (match-string 1)))
+          (when-let ((number
+                      (with-current-buffer buf
+                        (tex-numbers-label-to-number label))))
+            (when (let ((comment-start-skip
+                         "\\(\\(^\\|[^\\
+]\\)\\(\\\\\\\\\\)*\\)\\(%+[   ]*\\)"))
+                    ;; HACK: texmathp expects to be run in LaTeX-mode,
+                    ;; but here we are in a temporary buffer.
+                    (texmathp))
+              (insert (format "\\tag{%s}" number))))))
+      (buffer-substring-no-properties (point-min) (point-max)))))
+
+(defun tex-numbers-ref-helper (label default)
+  "Helper function for `tex-numbers-ref-display'.
+Returns a fold display string for LABEL (retrieved via
+`tex-numbers-label-to-number-function'), or DEFAULT if the label number cannot
+be retrieved."
+  (format "[%s]" (or (tex-numbers-label-to-number label) default)))
+
+(defun tex-numbers-ref-display (label &rest _args)
+  "Fold display for a \\ref{LABEL} macro."
+  (tex-numbers-ref-helper label "r"))
+
+(defun tex-numbers-eqref-display (label &rest _args)
+  "Fold display for a \\eqref{LABEL} macro."
+  (tex-numbers-ref-helper label "e"))
+
+(defun tex-numbers-label-display (label &rest _args)
+  "Fold display for a \\label{LABEL} macro."
+  (tex-numbers-ref-helper label "l"))
+
+(defvar tex-numbers--saved-spec-list nil
+  "Saved values from `TeX-fold-macro-spec-list'.")
+
+(defcustom tex-numbers-macro-list '("ref" "eqref" "label")
+  "List of macros to fold with theorem or equation numbers.
+Each element describes a LaTeX macro that takes a label as its argument.
+There should be a corresponding function `tex-numbers-MACRO-display'
+that returns a fold display string for that macro."
+  :type '(repeat string)
+  :group 'tex-numbers)
+
+;;;###autoload
+(define-minor-mode tex-numbers-mode
+  "Toggle `tex-numbers' mode."
+  :global t
+  :lighter nil
+  (cond
+   (tex-numbers-mode
+    (setq preview-preprocess-function #'tex-numbers-preview-preprocessor)
+    (require 'tex-fold)
+    (dolist (macro tex-numbers-macro-list)
+      (let ((func (intern (format "tex-numbers-%s-display" macro))))
+        (dolist (spec TeX-fold-macro-spec-list)
+          (when (and (member macro (cadr spec))
+                     (not (eq (car spec) func)))
+            (push (cons macro (car spec)) tex-numbers--saved-spec-list)
+            (setcdr spec (list (cl-remove macro (cadr spec) :test 'equal)))))
+        (add-to-list 'TeX-fold-macro-spec-list (list func (list macro)))))
+    (when TeX-fold-mode
+      (TeX-fold-mode 1)))
+   (t
+    (setq preview-preprocess-function nil)
+    (dolist (macro tex-numbers-macro-list)
+      (let ((func (intern (format "tex-numbers-%s-display" macro))))
+        (setq TeX-fold-macro-spec-list
+              (cl-remove-if (lambda (elem) (eq (car elem) func))
+                            TeX-fold-macro-spec-list)))
+      (when-let ((saved (assoc macro tex-numbers--saved-spec-list)))
+        (dolist (spec TeX-fold-macro-spec-list)
+          (when (eq (car spec) (cdr saved))
+            (push macro (cadr spec))))))
+    (setq tex-numbers--saved-spec-list nil)
+    (when TeX-fold-mode
+      (TeX-fold-mode 1)))))
+
+(provide 'tex-numbers)
+;;; tex-numbers.el ends here

Reply via email to