This repository has been archived by the owner on Feb 4, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlatex-refs.el
119 lines (103 loc) · 6.78 KB
/
latex-refs.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
;;; latex-refs.el --- Description Latex references: Utilities for completion -*- lexical-binding: t; -*-
;;
;;
;; Author: rahguzar <[email protected]>
;; Maintainer: rahguzar <[email protected]>
;; Created: May 31, 2022
;; Modified: May 31, 2022
;; Version: 0.0.1
;; Keywords: data files tex
;; Homepage: https://github.com/aikrahguzar/latex-refs
;; Package-Requires: ((emacs "28.1"))
;;
;; This file is not part of GNU Emacs.
;;
;;; Commentary:
;; This package provides the ability to match the labels in the TeX file with those in the pdf,
;; so that one can use the label in pdf to insert the one in TeX file. This is done with the help
;; of aux file and this package provides function to parse it. The code assumes that the hyperref
;; package is in use.
;;
;; In addition it provides a simple `completing-read' based interface for entering references which
;; can be entered using the label as it appears in the pdf (or more accurately the aux file.)
;; TO DO: Improve the context provided to the labels.
;; TO DO: Implement a synctex based preview for the labels. This might not be possible to do well
;; since synctex does seem to always generate correct locations and it might have unacceptably
;; high latency. It will be really nice to have so worth investigating.
;;
;;; Code:
(require 'tex)
(require 'tex-fold)
(defvar-local latex-refs--label-contexts (make-hash-table :test #'equal))
(defvar latex-refs-history nil)
(defvar latex-refs-macro-function #'latex-refs-default-macro-function "A function which returns a LaTeX macro given the type of reference as argument.")
(defun latex-refs--get-arg () "Return the string enclosed by the brace at point."
(buffer-substring (1+ (point)) (1- (progn (forward-sexp) (point)))))
(defun latex-refs--parse-newlabel ()
"Parse a newlabel macro at point which must be before the first argument.
The result is a list (PDF-LABEL TEX-LABEL PAGE SECTION TYPE)"
(let ((tex (progn (search-forward "{") (backward-char) (latex-refs--get-arg))))
(list (progn (forward-char) (latex-refs--get-arg)) tex (latex-refs--get-arg) (latex-refs--get-arg)
(buffer-substring (1+ (point)) (1- (search-forward "."))))))
(defun latex-refs--aux-file () "Get the aux file for the current LaTeX document."
(file-name-with-extension (TeX-master-file) "aux"))
(defun latex-refs-parse-file (&optional file)
"Parse the labels from aux FILE generated by LaTeX."
(let ((file (or file (latex-refs--aux-file)))
(syntax-table (make-char-table 'syntax-table)))
(modify-syntax-entry ?{ "(}" syntax-table) (modify-syntax-entry ?} "){" syntax-table)
(with-temp-buffer (insert-file-contents-literally file) (set-syntax-table syntax-table) (keep-lines (rx bol "\\newlabel")) (goto-char (point-min))
(let ((labels))
(while (not (eobp))
(push (latex-refs--parse-newlabel) labels)
(forward-line))
labels))))
(defun latex-refs--label-context (label type sec) "Get one line of text around LABEL which is of TYPE in SEC."
(or (gethash label latex-refs--label-contexts)
(puthash label (or (ignore-errors
(pcase type
((or "section" "subsection" "subsubsection") sec)
(_ (save-excursion (goto-char (point-min)) (search-forward (concat "\\label{" label "}")) (forward-line)
(string-trim (substring-no-properties (TeX-fold-buffer-substring (point) (progn (end-of-line) (point)))))))))
"")
latex-refs--label-contexts)))
(defun latex-refs--make-candidate (cand) "Make a searchable string for CAND."
(propertize (concat (propertize (truncate-string-to-width (car cand) 10 nil ?\s) 'face 'font-latex-bold-face)
(propertize (truncate-string-to-width (nth 1 cand) 40 nil ?\s) 'face 'font-latex-sedate-face)
(propertize (truncate-string-to-width (latex-refs--label-context (nth 1 cand) (nth 4 cand) (nth 3 cand)) (- (frame-width) 60) nil ?\s)
'face 'font-latex-math-face))
'tex-label (nth 1 cand) 'pdf-label (car cand) 'page (nth 2 cand) 'type (nth 4 cand) 'section (nth 3 cand)))
(defun latex-refs--make-candidates (&optional rescan filter)
"Return the decorated list of labels for the current buffer.
If RESCAN is non-nill, re-scan the document for context.
FILTER is for retruning a subset of candidates."
(when rescan (setq latex-refs--label-contexts (make-hash-table :test #'equal)))
(mapcar #'latex-refs--make-candidate (if filter (seq-filter filter (latex-refs-parse-file)) (latex-refs-parse-file))))
(defun latex-refs--completion-table (references) "Return a completion table for parsed REFERENCES."
(lambda (string predicate action)
(if (eq action 'metadata)
`(metadata (category . 'latex-reference)
(group-function . (lambda (cand transform) (if transform cand (get-text-property 0 'type cand)))))
(complete-with-action action references string predicate))))
(defun latex-refs-select (&optional rescan filter)
"Select a reference. If RESCAN is non-nil recompute the contexts.
FILTER is for retruning a subset of candidates."
(let* ((candidates (latex-refs--make-candidates rescan filter))
(selection (completing-read "Select a reference: " (latex-refs--completion-table candidates) nil nil nil 'latex-refs-history)))
(car (member selection candidates))))
(defun latex-refs-insert-one-arg-macro (macro arg) "Insert a LaTeX MACRO using ARG as its argument."
(TeX-parse-macro macro '(t)) (insert arg) (skip-chars-forward "^}") (forward-char 1))
(defun latex-refs-default-macro-function (type) "The default macro function. TYPE is the type of the reference."
(pcase type ("equation" "eqref") (_ "autoref")))
(defun latex-refs--is-equation (label) "Return non-nil if LABEL is for an equation."
(equal (nth 4 label) "equation"))
;;;###autoload
(defun latex-refs-insert (ref) "Insert a reference REF with completion. With non-nil prefix rescan for context."
(interactive (list (latex-refs-select current-prefix-arg)))
(latex-refs-insert-one-arg-macro (funcall latex-refs-macro-function (get-text-property 0 'type ref)) (get-text-property 0 'tex-label ref)))
;;;###autoload
(defun latex-refs-insert-eqref (ref) "Insert a reference REF with completion. With non-nil prefix rescan for context."
(interactive (list (latex-refs-select current-prefix-arg #'latex-refs--is-equation)))
(latex-refs-insert-one-arg-macro "eqref" (get-text-property 0 'tex-label ref)))
(provide 'latex-refs)
;;; latex-refs.el ends here