mirror of
https://git.savannah.gnu.org/git/emacs.git
synced 2024-11-21 06:55:39 +00:00
484 lines
19 KiB
EmacsLisp
484 lines
19 KiB
EmacsLisp
;;; reftex-dcr.el --- viewing cross references and citations with RefTeX -*- lexical-binding: t; -*-
|
|
|
|
;; Copyright (C) 1997-2024 Free Software Foundation, Inc.
|
|
|
|
;; Author: Carsten Dominik <dominik@science.uva.nl>
|
|
;; Maintainer: auctex-devel@gnu.org
|
|
|
|
;; This file is part of GNU Emacs.
|
|
|
|
;; GNU Emacs 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.
|
|
|
|
;; GNU Emacs 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. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
;;; Commentary:
|
|
|
|
;;; Code:
|
|
|
|
(eval-when-compile (require 'cl-lib))
|
|
|
|
(declare-function bibtex-beginning-of-entry "bibtex" ())
|
|
|
|
(require 'reftex)
|
|
|
|
;;;###autoload
|
|
(defun reftex-view-crossref (&optional arg auto-how fail-quietly)
|
|
"View cross reference of macro at point.
|
|
|
|
Point must be on the KEY argument. When at a `\\ref' macro, show
|
|
corresponding `\\label' definition, also in external
|
|
documents (`xr'). When on a label, show a locations where KEY is
|
|
referenced. Subsequent calls find additional locations. When on
|
|
a `\\cite', show the associated `\\bibitem' macro or the BibTeX
|
|
database entry. When on a `\\bibitem', show a `\\cite' macro
|
|
which uses this KEY. When on an `\\index', show other locations
|
|
marked by the same index entry.
|
|
|
|
To define additional cross referencing items, use the option
|
|
`reftex-view-crossref-extra'. See also `reftex-view-crossref-from-bibtex'.
|
|
With one or two \\[universal-argument] prefixes, enforce rescanning of the document.
|
|
With argument 2, select the window showing the cross reference.
|
|
AUTO-HOW is only for the automatic crossref display and is handed through
|
|
to the functions `reftex-view-cr-cite' and `reftex-view-cr-ref'."
|
|
|
|
(interactive "P")
|
|
;; See where we are.
|
|
(let* ((macro (car (reftex-what-macro-safe 1)))
|
|
(key (reftex-this-word "^{}%\n\r, \t"))
|
|
dw)
|
|
|
|
(if (or (null macro) (reftex-in-comment))
|
|
(or fail-quietly
|
|
(error "Not on a crossref macro argument"))
|
|
|
|
(setq reftex-call-back-to-this-buffer (current-buffer))
|
|
|
|
(cond
|
|
((string-match "\\`\\\\cite\\|cite\\([s*]\\|texts?\\)?\\'\\|bibentry" macro)
|
|
;; A citation macro: search for bibitems or BibTeX entries.
|
|
;; Match also commands from biblatex ending with `s'
|
|
;; (\parencites) or `*' (\parencite*) and `texts?'
|
|
;; (\footcitetext and \footcitetexts).
|
|
(setq dw (reftex-view-cr-cite arg key auto-how)))
|
|
((string-match "\\`\\\\ref\\|ref\\(range\\|s\\)?\\*?\\'" macro)
|
|
;; A reference macro: search for labels.
|
|
;; Match also commands from cleveref ending with `s' (\namecrefs).
|
|
(setq dw (reftex-view-cr-ref arg key auto-how)))
|
|
(auto-how nil) ;; No further action for automatic display (speed)
|
|
((or (equal macro "\\label")
|
|
(member macro reftex-macros-with-labels))
|
|
;; A label macro: search for reference macros
|
|
(reftex-access-scan-info arg)
|
|
(setq dw (reftex-view-regexp-match
|
|
(format reftex-find-reference-format (regexp-quote key))
|
|
4 nil nil)))
|
|
((equal macro "\\bibitem")
|
|
;; A bibitem macro: search for citations
|
|
(reftex-access-scan-info arg)
|
|
(setq dw (reftex-view-regexp-match
|
|
(format reftex-find-citation-regexp-format (regexp-quote key))
|
|
4 nil nil)))
|
|
((member macro reftex-macros-with-index)
|
|
(reftex-access-scan-info arg)
|
|
(setq dw (reftex-view-regexp-match
|
|
(format reftex-find-index-entry-regexp-format
|
|
(regexp-quote key))
|
|
3 nil nil)))
|
|
(t
|
|
(reftex-access-scan-info arg)
|
|
(catch 'exit
|
|
(let ((list reftex-view-crossref-extra)
|
|
entry mre action group)
|
|
(while (setq entry (pop list))
|
|
(setq mre (car entry)
|
|
action (nth 1 entry)
|
|
group (nth 2 entry))
|
|
(when (string-match mre macro)
|
|
(setq dw (reftex-view-regexp-match
|
|
(format action key) group nil nil))
|
|
(throw 'exit t))))
|
|
(error "Not on a crossref macro argument"))))
|
|
(if (and (eq arg 2) (windowp dw)) (select-window dw)))))
|
|
|
|
(defun reftex-view-cr-cite (arg key how)
|
|
;; View cross-reference of a ref cite. HOW can have the values
|
|
;; nil: Show in another window.
|
|
;; echo: Show one-line info in echo area.
|
|
;; tmp-window: Show in small window and arrange for window to disappear.
|
|
|
|
;; Ensure access to scanning info
|
|
(reftex-access-scan-info (or arg current-prefix-arg))
|
|
|
|
(if (eq how 'tmp-window)
|
|
;; Remember the window configuration
|
|
(put 'reftex-auto-view-crossref 'last-window-conf
|
|
(current-window-configuration)))
|
|
|
|
(let (files size item (pos (point)) (win (selected-window)) pop-win
|
|
(bibtype (reftex-bib-or-thebib)))
|
|
;; Find the citation mode and the file list
|
|
(cond
|
|
; ((assq 'bib (symbol-value reftex-docstruct-symbol))
|
|
((eq bibtype 'bib)
|
|
(setq item nil
|
|
files (reftex-get-bibfile-list)))
|
|
; ((assq 'thebib (symbol-value reftex-docstruct-symbol))
|
|
((eq bibtype 'thebib)
|
|
(setq item t
|
|
files (reftex-uniquify
|
|
(mapcar #'cdr
|
|
(reftex-all-assq
|
|
'thebib (symbol-value reftex-docstruct-symbol))))))
|
|
(reftex-default-bibliography
|
|
(setq item nil
|
|
files (reftex-default-bibliography)))
|
|
(how) ;; don't throw for special display
|
|
(t (error "Cannot display crossref")))
|
|
|
|
(if (eq how 'echo)
|
|
;; Display in Echo area
|
|
(reftex-echo-cite key files item)
|
|
;; Display in a window
|
|
(if (not (eq how 'tmp-window))
|
|
;; Normal display
|
|
(reftex-pop-to-bibtex-entry key files nil t item)
|
|
;; A temporary window
|
|
(condition-case nil
|
|
(reftex-pop-to-bibtex-entry key files nil t item)
|
|
(error (goto-char pos)
|
|
(message "cite: no such citation key %s" key)
|
|
(error "")))
|
|
;; Resize the window
|
|
(setq size (max 1 (count-lines (point)
|
|
(reftex-end-of-bib-entry item))))
|
|
(let ((window-min-height 2))
|
|
(shrink-window (1- (- (window-height) size)))
|
|
(recenter 0))
|
|
;; Arrange restoration
|
|
(add-hook 'pre-command-hook #'reftex-restore-window-conf))
|
|
|
|
;; Normal display in other window
|
|
(add-hook 'pre-command-hook #'reftex-highlight-shall-die)
|
|
(setq pop-win (selected-window))
|
|
(select-window win)
|
|
(goto-char pos)
|
|
(when (equal arg 2)
|
|
(select-window pop-win)))))
|
|
|
|
(defun reftex-view-cr-ref (arg label how)
|
|
;; View cross-reference of a ref macro. HOW can have the values
|
|
;; nil: Show in another window.
|
|
;; echo: Show one-line info in echo area.
|
|
;; tmp-window: Show in small window and arrange for window to disappear.
|
|
|
|
;; Ensure access to scanning info
|
|
(reftex-access-scan-info (or arg current-prefix-arg))
|
|
|
|
(if (eq how 'tmp-window)
|
|
;; Remember the window configuration
|
|
(put 'reftex-auto-view-crossref 'last-window-conf
|
|
(current-window-configuration)))
|
|
|
|
(let* ((xr-data (assoc 'xr (symbol-value reftex-docstruct-symbol)))
|
|
(xr-re (nth 2 xr-data))
|
|
(entry (assoc label (symbol-value reftex-docstruct-symbol)))
|
|
(win (selected-window)) pop-win (pos (point)))
|
|
|
|
(if (and (not entry) (stringp label) xr-re (string-match xr-re label))
|
|
;; Label is defined in external document
|
|
(save-excursion
|
|
(save-match-data
|
|
(set-buffer
|
|
(or (reftex-get-file-buffer-force
|
|
(cdr (assoc (match-string 1 label) (nth 1
|
|
xr-data))))
|
|
(error "Problem with external label %s" label))))
|
|
(setq label (substring label (match-end 1)))
|
|
(reftex-access-scan-info)
|
|
(setq entry
|
|
(assoc label (symbol-value reftex-docstruct-symbol)))))
|
|
(if (eq how 'echo)
|
|
;; Display in echo area
|
|
(reftex-echo-ref label entry (symbol-value reftex-docstruct-symbol))
|
|
(let ((window-conf (current-window-configuration)))
|
|
(condition-case nil
|
|
(reftex-show-label-location entry t nil t t)
|
|
(error (set-window-configuration window-conf)
|
|
(message "ref: Label %s not found" label)
|
|
(error "ref: Label %s not found" label)))) ;; 2nd is line OK
|
|
(add-hook 'pre-command-hook #'reftex-highlight-shall-die)
|
|
|
|
(when (eq how 'tmp-window)
|
|
;; Resize window and arrange restoration
|
|
(shrink-window (1- (- (window-height) 9)))
|
|
(recenter '(4))
|
|
(add-hook 'pre-command-hook #'reftex-restore-window-conf))
|
|
(setq pop-win (selected-window))
|
|
(select-window win)
|
|
(goto-char pos)
|
|
(when (equal arg 2)
|
|
(select-window pop-win)))))
|
|
|
|
;;;###autoload
|
|
(defun reftex-mouse-view-crossref (ev)
|
|
"View cross reference of \\ref or \\cite macro where you click.
|
|
If the macro at point is a \\ref, show the corresponding label definition.
|
|
If it is a \\cite, show the BibTeX database entry.
|
|
If there is no such macro at point, search forward to find one.
|
|
With argument, actually select the window showing the cross reference."
|
|
(interactive "e")
|
|
;; Make sure the referencing macro stays visible in the original window.
|
|
(mouse-set-point ev)
|
|
(reftex-view-crossref current-prefix-arg))
|
|
|
|
(defun reftex-view-crossref-when-idle ()
|
|
"Display info about crossref at point in echo area or a window.
|
|
This function is designed to work with an idle timer and returns quickly
|
|
if the call is useless."
|
|
(and reftex-mode
|
|
;; Make sure message area is free if we need it.
|
|
(or (eq reftex-auto-view-crossref 'window) (not (current-message)))
|
|
;; Make sure we are not already displaying this one
|
|
(not (memq last-command '(reftex-view-crossref
|
|
reftex-mouse-view-crossref)))
|
|
;; Quick precheck if this might be a relevant spot
|
|
;; `reftex-view-crossref' will do a more thorough check.
|
|
(save-excursion
|
|
(search-backward "\\" nil t)
|
|
(looking-at "\\\\[a-zA-Z]*\\(cite\\|ref\\|bibentry\\)"))
|
|
;; Also check if point is inside a mandatory argument where the
|
|
;; cite/ref key usually resides: (bug#38258)
|
|
(save-excursion
|
|
(condition-case nil
|
|
(let ((forward-sexp-function nil))
|
|
(up-list -1)
|
|
(= (following-char) ?\{))
|
|
(error nil)))
|
|
;; Finally, call `reftex-view-crossref':
|
|
(condition-case nil
|
|
(let ((current-prefix-arg nil))
|
|
(cond
|
|
((eq reftex-auto-view-crossref t)
|
|
(reftex-view-crossref -1 'echo 'quiet))
|
|
((eq reftex-auto-view-crossref 'window)
|
|
(reftex-view-crossref -1 'tmp-window 'quiet))
|
|
(t nil)))
|
|
(error nil))))
|
|
|
|
(defun reftex-restore-window-conf ()
|
|
(set-window-configuration (get 'reftex-auto-view-crossref 'last-window-conf))
|
|
(put 'reftex-auto-view-crossref 'last-window-conf nil)
|
|
(remove-hook 'pre-command-hook #'reftex-restore-window-conf))
|
|
|
|
(defun reftex-echo-ref (label entry docstruct)
|
|
;; Display crossref info in echo area.
|
|
(cond
|
|
((null docstruct)
|
|
(message "%s"
|
|
(substitute-command-keys (format reftex-no-info-message "ref"))))
|
|
((null entry)
|
|
(message "ref: unknown label: %s" label))
|
|
(t
|
|
(when (stringp (nth 2 entry))
|
|
(message "ref(%s): %s" (nth 1 entry) (nth 2 entry)))
|
|
(let ((buf (get-buffer " *Echo Area*")))
|
|
(when buf
|
|
(with-current-buffer buf
|
|
(run-hooks 'reftex-display-copied-context-hook)))))))
|
|
|
|
(defun reftex-echo-cite (key files item)
|
|
;; Display citation info in echo area.
|
|
(let* ((cache (assq 'bibview-cache (symbol-value reftex-docstruct-symbol)))
|
|
(cache-entry (assoc key (cdr cache)))
|
|
entry string buf (all-files files))
|
|
|
|
(if (and reftex-cache-cite-echo cache-entry)
|
|
;; We can just use the cache
|
|
(setq string (cdr cache-entry))
|
|
|
|
;; Need to look in the database
|
|
(unless reftex-revisit-to-echo
|
|
(setq files (reftex-visited-files files)))
|
|
|
|
(setq entry
|
|
(condition-case nil
|
|
(save-excursion
|
|
(reftex-pop-to-bibtex-entry key files nil nil item t))
|
|
(error
|
|
(if (and files (= (length all-files) (length files)))
|
|
(message "cite: no such database entry: %s" key)
|
|
(message "%s" (substitute-command-keys
|
|
(format reftex-no-info-message "cite"))))
|
|
nil)))
|
|
(when entry
|
|
(if item
|
|
(setq string (reftex-nicify-text entry))
|
|
(setq string (reftex-make-cite-echo-string
|
|
(reftex-parse-bibtex-entry entry)
|
|
reftex-docstruct-symbol)))))
|
|
(unless (or (null string) (equal string ""))
|
|
(message "cite: %s" string))
|
|
(when (setq buf (get-buffer " *Echo Area*"))
|
|
(with-current-buffer buf
|
|
(run-hooks 'reftex-display-copied-context-hook)))))
|
|
|
|
;;;###autoload
|
|
(defun reftex-toggle-auto-view-crossref ()
|
|
"Toggle the automatic display of crossref information in the echo area.
|
|
When active, leaving point idle in the argument of a \\ref or \\cite macro
|
|
will display info in the echo area."
|
|
(interactive)
|
|
(if reftex-auto-view-crossref-timer
|
|
(progn
|
|
(cancel-timer reftex-auto-view-crossref-timer)
|
|
(setq reftex-auto-view-crossref-timer nil)
|
|
(message "Automatic display of crossref information was turned off"))
|
|
(setq reftex-auto-view-crossref-timer
|
|
(run-with-idle-timer
|
|
reftex-idle-time t #'reftex-view-crossref-when-idle))
|
|
(unless reftex-auto-view-crossref
|
|
(setq reftex-auto-view-crossref t))
|
|
(message "Automatic display of crossref information was turned on")))
|
|
|
|
;;;###autoload
|
|
(defun reftex-view-crossref-from-bibtex (&optional arg)
|
|
"View location in a LaTeX document which cites the BibTeX entry at point.
|
|
Since BibTeX files can be used by many LaTeX documents, this function
|
|
prompts upon first use for a buffer in RefTeX mode. To reset this
|
|
link to a document, call the function with a prefix arg.
|
|
Calling this function several times find successive citation locations."
|
|
(interactive "P")
|
|
(when arg
|
|
;; Break connection to reference buffer
|
|
(put 'reftex-bibtex-view-cite-locations :ref-buffer nil))
|
|
(let ((ref-buffer (get 'reftex-bibtex-view-cite-locations :ref-buffer)))
|
|
;; Establish connection to reference buffer
|
|
(unless ref-buffer
|
|
(setq ref-buffer
|
|
(save-current-buffer
|
|
(completing-read
|
|
"Reference buffer: "
|
|
(delq nil
|
|
(mapcar
|
|
(lambda (b)
|
|
(set-buffer b)
|
|
(if reftex-mode (list (buffer-name b)) nil))
|
|
(buffer-list)))
|
|
nil t)))
|
|
(put 'reftex-bibtex-view-cite-locations :ref-buffer ref-buffer))
|
|
;; Search for citations
|
|
(bibtex-beginning-of-entry)
|
|
(if (looking-at
|
|
"@[a-zA-Z]+[ \t\n\r]*[{(][ \t\n\r]*\\([^, \t\r\n}]+\\)")
|
|
(progn
|
|
(goto-char (match-beginning 1))
|
|
(reftex-view-regexp-match
|
|
(format reftex-find-citation-regexp-format
|
|
(regexp-quote (match-string 1)))
|
|
4 arg ref-buffer))
|
|
(error "Cannot find citation key in BibTeX entry"))))
|
|
|
|
(defun reftex-view-regexp-match (re &optional highlight-group new ref-buffer)
|
|
;; Search for RE in current document or in the document of REF-BUFFER.
|
|
;; Continue the search, if the same re was searched last.
|
|
;; Highlight the group HIGHLIGHT-GROUP of the match.
|
|
;; When NEW is non-nil, start a new search regardless.
|
|
;; Match point is displayed in another window.
|
|
;; Upon success, returns the window which displays the match.
|
|
|
|
;;; Decide if new search or continued search
|
|
(let* ((oldprop (get 'reftex-view-regexp-match :props))
|
|
(newprop (list (current-buffer) re))
|
|
(cont (and (not new) (equal oldprop newprop)))
|
|
(cnt (if cont (get 'reftex-view-regexp-match :cnt) 0))
|
|
(current-window (selected-window))
|
|
(window-conf (current-window-configuration))
|
|
match pop-window)
|
|
(switch-to-buffer-other-window (or ref-buffer (current-buffer)))
|
|
;; Search
|
|
(condition-case nil
|
|
(if cont
|
|
(setq match (reftex-global-search-continue))
|
|
(reftex-access-scan-info)
|
|
(setq match (reftex-global-search re (reftex-all-document-files))))
|
|
(error nil))
|
|
;; Evaluate the match.
|
|
(if match
|
|
(progn
|
|
(put 'reftex-view-regexp-match :props newprop)
|
|
(put 'reftex-view-regexp-match :cnt (cl-incf cnt))
|
|
(reftex-highlight 0 (match-beginning highlight-group)
|
|
(match-end highlight-group))
|
|
(add-hook 'pre-command-hook #'reftex-highlight-shall-die)
|
|
(setq pop-window (selected-window)))
|
|
(put 'reftex-view-regexp-match :props nil)
|
|
(or cont (set-window-configuration window-conf)))
|
|
(select-window current-window)
|
|
(if match
|
|
(progn
|
|
(message "Match Nr. %s" cnt)
|
|
pop-window)
|
|
(if cont
|
|
(error "No further matches (total number of matches: %d)" cnt)
|
|
(error "No matches")))))
|
|
|
|
(defvar reftex-global-search-marker (make-marker))
|
|
(defun reftex-global-search (regexp file-list)
|
|
;; Start a search for REGEXP in all files of FILE-LIST
|
|
(put 'reftex-global-search :file-list file-list)
|
|
(put 'reftex-global-search :regexp regexp)
|
|
(move-marker reftex-global-search-marker nil)
|
|
(reftex-global-search-continue))
|
|
|
|
(defun reftex-global-search-continue ()
|
|
;; Continue a global search started with `reftex-global-search'
|
|
(unless (get 'reftex-global-search :file-list)
|
|
(error "No global search to continue"))
|
|
(let* ((file-list (get 'reftex-global-search :file-list))
|
|
(regexp (get 'reftex-global-search :regexp))
|
|
(buf (or (marker-buffer reftex-global-search-marker)
|
|
(reftex-get-file-buffer-force (car file-list))))
|
|
(pos (or (marker-position reftex-global-search-marker) 1))
|
|
file)
|
|
;; Take up starting position
|
|
(unless buf (error "No such buffer %s" buf))
|
|
(switch-to-buffer buf)
|
|
(widen)
|
|
(goto-char pos)
|
|
;; Search and switch file if necessary
|
|
(if (catch 'exit
|
|
(while t
|
|
(when (re-search-forward regexp nil t)
|
|
(move-marker reftex-global-search-marker (point))
|
|
(throw 'exit t))
|
|
;; No match - goto next file
|
|
(pop file-list)
|
|
(or file-list (throw 'exit nil))
|
|
(setq file (car file-list)
|
|
buf (reftex-get-file-buffer-force file))
|
|
(unless buf (error "Cannot access file %s" file))
|
|
(put 'reftex-global-search :file-list file-list)
|
|
(switch-to-buffer buf)
|
|
(widen)
|
|
(goto-char 1)))
|
|
t
|
|
(move-marker reftex-global-search-marker nil)
|
|
(error "All files processed"))))
|
|
|
|
(provide 'reftex-dcr)
|
|
|
|
;;; reftex-dcr.el ends here
|
|
|
|
;; Local Variables:
|
|
;; generated-autoload-file: "reftex-loaddefs.el"
|
|
;; End:
|