-;;; reftex-cite.el - Creating citations with RefTeX
-;;; Version: 4.5
-;;;
-;;; See main file reftex.el for licensing information
+;;; reftex-cite.el --- creating citations with RefTeX
+;; Copyright (c) 1997, 1998, 1999, 2000 Free Software Foundation, Inc.
+
+;; Author: Carsten Dominik <dominik@science.uva.nl>
+;; Version: 4.18
+
+;; 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 2, 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; see the file COPYING. If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;;; Commentary:
+
+;;; Code:
+
+(eval-when-compile (require 'cl))
(provide 'reftex-cite)
(require 'reftex)
;;;
;; Find bibtex files
+
+(defmacro reftex-with-special-syntax-for-bib (&rest body)
+ `(let ((saved-syntax (syntax-table)))
+ (unwind-protect
+ (progn
+ (set-syntax-table reftex-syntax-table-for-bib)
+ ,@body)
+ (set-syntax-table saved-syntax))))
+
(defun reftex-default-bibliography ()
;; Return the expanded value of `reftex-default-bibliography'.
;; The expanded value is cached.
reftex-default-bibliography))
(get 'reftex-default-bibliography :reftex-expanded))
+(defun reftex-bib-or-thebib ()
+ ;; Tests if BibTeX or \begin{tehbibliography} should be used for the
+ ;; citation
+ ;; Find the bof of the current file
+ (let* ((docstruct (symbol-value reftex-docstruct-symbol))
+ (rest (or (member (list 'bof (buffer-file-name)) docstruct)
+ docstruct))
+ (bib (assq 'bib rest))
+ (thebib (assq 'thebib rest))
+ (bibmem (memq bib rest))
+ (thebibmem (memq thebib rest)))
+ (when (not (or thebib bib))
+ (setq bib (assq 'bib docstruct)
+ thebib (assq 'thebib docstruct)
+ bibmem (memq bib docstruct)
+ thebibmem (memq thebib docstruct)))
+ (if (> (length bibmem) (length thebibmem))
+ (if bib 'bib nil)
+ (if thebib 'thebib nil))))
+
(defun reftex-get-bibfile-list ()
;; Return list of bibfiles for current document.
;; When using the chapterbib or bibunits package you should either
(concat "@[a-zA-Z]+[ \t\n\r]*[{(][ \t\n\r]*" (regexp-quote key)
"[, \t\r\n}]")))
(buffer-conf (current-buffer))
- file buf)
+ file buf pos)
(catch 'exit
(while file-list
(goto-char (point-min))
(when (re-search-forward re nil t)
(goto-char (match-beginning 0))
+ (setq pos (point))
(when return
;; Just return the relevant entry
(if item (goto-char (match-end 0)))
(set-buffer buffer-conf)
(throw 'exit return))
(switch-to-buffer-other-window buf)
+ (goto-char pos)
(recenter 0)
(if highlight
(reftex-highlight 0 (match-beginning 0) (match-end 0)))
(let* (re-list first-re rest-re
(buffer-list (if (listp buffers) buffers (list buffers)))
found-list entry buffer1 buffer alist
- key-point start-point end-point)
+ key-point start-point end-point default)
;; Read a regexp, completing on known citation keys.
+ (setq default (regexp-quote (reftex-get-bibkey-default)))
(setq re-list
(split-string
(completing-read
- "RegExp [ && RegExp...]: "
+ (concat
+ "Regex { && Regex...}: "
+ "[" default "]: ")
(if reftex-mode
(if (fboundp 'LaTeX-bibitem-list)
(LaTeX-bibitem-list)
nil nil nil 'reftex-cite-regexp-hist)
"[ \t]*&&[ \t]*"))
+ (if (or (null re-list ) (equal re-list '("")))
+ (setq re-list (list default)))
+
(setq first-re (car re-list) ; We'll use the first re to find things,
rest-re (cdr re-list)) ; the others to narrow down.
(if (string-match "\\`[ \t]*\\'" (or first-re ""))
(message "Scanning bibliography database %s" buffer1))
(set-buffer buffer1)
- (save-excursion
- (goto-char (point-min))
- (while (re-search-forward first-re nil t)
- (catch 'search-again
- (setq key-point (point))
- (unless (re-search-backward
- "\\(\\`\\|[\n\r]\\)[ \t]*@\\([a-zA-Z]+\\)[ \t\n\r]*[{(]" nil t)
- (throw 'search-again nil))
- (setq start-point (point))
- (goto-char (match-end 0))
- (condition-case nil
- (up-list 1)
- (error (goto-char key-point)
+ (reftex-with-special-syntax-for-bib
+ (save-excursion
+ (goto-char (point-min))
+ (while (re-search-forward first-re nil t)
+ (catch 'search-again
+ (setq key-point (point))
+ (unless (re-search-backward
+ "\\(\\`\\|[\n\r]\\)[ \t]*@\\([a-zA-Z]+\\)[ \t\n\r]*[{(]" nil t)
+ (throw 'search-again nil))
+ (setq start-point (point))
+ (goto-char (match-end 0))
+ (condition-case nil
+ (up-list 1)
+ (error (goto-char key-point)
(throw 'search-again nil)))
- (setq end-point (point))
-
- ;; Ignore @string, @comment and @c entries or things
- ;; outside entries
- (when (or (string= (downcase (match-string 2)) "string")
- (string= (downcase (match-string 2)) "comment")
- (string= (downcase (match-string 2)) "c")
- (< (point) key-point)) ; this means match not in {}
- (goto-char key-point)
- (throw 'search-again nil))
-
- ;; Well, we have got a match
- (setq entry (concat
- (buffer-substring start-point (point)) "\n"))
-
- ;; Check if other regexp match as well
- (setq re-list rest-re)
- (while re-list
- (unless (string-match (car re-list) entry)
- ;; nope - move on
- (throw 'search-again nil))
- (pop re-list))
-
- (setq alist (reftex-parse-bibtex-entry
- nil start-point end-point))
- (push (cons "&entry" entry) alist)
-
- ;; check for crossref entries
- (if (assoc "crossref" alist)
- (setq alist
- (append
- alist (reftex-get-crossref-alist alist))))
-
- ;; format the entry
- (push (cons "&formatted" (reftex-format-bib-entry alist))
- alist)
-
- ;; make key the first element
- (push (reftex-get-bib-field "&key" alist) alist)
-
- ;; add it to the list
- (push alist found-list))))
- (reftex-kill-temporary-buffers))))
+ (setq end-point (point))
+
+ ;; Ignore @string, @comment and @c entries or things
+ ;; outside entries
+ (when (or (string= (downcase (match-string 2)) "string")
+ (string= (downcase (match-string 2)) "comment")
+ (string= (downcase (match-string 2)) "c")
+ (< (point) key-point)) ; this means match not in {}
+ (goto-char key-point)
+ (throw 'search-again nil))
+
+ ;; Well, we have got a match
+ (setq entry (concat
+ (buffer-substring start-point (point)) "\n"))
+
+ ;; Check if other regexp match as well
+ (setq re-list rest-re)
+ (while re-list
+ (unless (string-match (car re-list) entry)
+ ;; nope - move on
+ (throw 'search-again nil))
+ (pop re-list))
+
+ (setq alist (reftex-parse-bibtex-entry
+ nil start-point end-point))
+ (push (cons "&entry" entry) alist)
+
+ ;; check for crossref entries
+ (if (assoc "crossref" alist)
+ (setq alist
+ (append
+ alist (reftex-get-crossref-alist alist))))
+
+ ;; format the entry
+ (push (cons "&formatted" (reftex-format-bib-entry alist))
+ alist)
+
+ ;; make key the first element
+ (push (reftex-get-bib-field "&key" alist) alist)
+
+ ;; add it to the list
+ (push alist found-list)))))
+ (reftex-kill-temporary-buffers))))
(setq found-list (nreverse found-list))
;; Sorting
(reftex-parse-bibtex-entry nil start (point)))
nil)))))
-;; Parse the thebibliography environment
-(defun reftex-extract-bib-entries-from-thebibliography (file)
+;; Parse the bibliography environment
+(defun reftex-extract-bib-entries-from-thebibliography (files)
;; Extract bib-entries from the \begin{thebibliography} environment.
;; Parsing is not as good as for the BibTeX database stuff.
;; The environment should be located in file FILE.
- (let* (start end buf entries re re-list)
- (unless file
+ (let* (start end buf entries re re-list file default)
+ (unless files
(error "Need file name to find thebibliography environment"))
- (setq buf (reftex-get-file-buffer-force
- file (not reftex-keep-temporary-buffers)))
- (unless buf
- (error "No such file %s" file))
- (message "Scanning thebibliography environment in %s" file)
+ (while (setq file (pop files))
+ (setq buf (reftex-get-file-buffer-force
+ file (not reftex-keep-temporary-buffers)))
+ (unless buf
+ (error "No such file %s" file))
+ (message "Scanning thebibliography environment in %s" file)
- (save-excursion
- (set-buffer buf)
- (save-restriction
- (widen)
- (goto-char (point-min))
- (if (re-search-forward
- "\\(\\`\\|[\n\r]\\)[ \t]*\\\\begin{thebibliography}" nil t)
- (progn
- (beginning-of-line 2)
- (setq start (point))))
- (if (re-search-forward
- "\\(\\`\\|[\n\r]\\)[ \t]*\\\\end{thebibliography}" nil t)
- (progn
- (beginning-of-line 1)
- (setq end (point))))
- (when (and start end)
- (setq entries
- (mapcar 'reftex-parse-bibitem
- (delete ""
- (split-string
- (buffer-substring-no-properties start end)
- "[ \t\n\r]*\\\\bibitem\\(\\[[^]]*]\\)*")))))))
+ (save-excursion
+ (set-buffer buf)
+ (save-restriction
+ (widen)
+ (goto-char (point-min))
+ (while (re-search-forward
+ "\\(\\`\\|[\n\r]\\)[ \t]*\\\\begin{thebibliography}" nil t)
+ (beginning-of-line 2)
+ (setq start (point))
+ (if (re-search-forward
+ "\\(\\`\\|[\n\r]\\)[ \t]*\\\\end{thebibliography}" nil t)
+ (progn
+ (beginning-of-line 1)
+ (setq end (point))))
+ (when (and start end)
+ (setq entries
+ (append entries
+ (mapcar 'reftex-parse-bibitem
+ (delete ""
+ (split-string
+ (buffer-substring-no-properties start end)
+ "[ \t\n\r]*\\\\bibitem\\(\\[[^]]*]\\)*"))))))
+ (goto-char end)))))
(unless entries
(error "No bibitems found"))
- (setq re-list (split-string
- (read-string "RegExp [ && RegExp...]: "
- nil 'reftex-cite-regexp-hist)
- "[ \t]*&&[ \t]*"))
+ ;; Read a regexp, completing on known citation keys.
+ (setq default (regexp-quote (reftex-get-bibkey-default)))
+ (setq re-list
+ (split-string
+ (completing-read
+ (concat
+ "Regex { && Regex...}: "
+ "[" default "]: ")
+ (if reftex-mode
+ (if (fboundp 'LaTeX-bibitem-list)
+ (LaTeX-bibitem-list)
+ (cdr (assoc 'bibview-cache
+ (symbol-value reftex-docstruct-symbol))))
+ nil)
+ nil nil nil 'reftex-cite-regexp-hist)
+ "[ \t]*&&[ \t]*"))
+
+ (if (or (null re-list ) (equal re-list '("")))
+ (setq re-list (list default)))
+
(if (string-match "\\`[ \t]*\\'" (car re-list))
(error "Empty regular expression"))
entries))
+(defun reftex-get-bibkey-default ()
+ ;; Return the word before the cursor. If the cursor is in a
+ ;; citation macro, return the word before the macro.
+ (let* ((macro (reftex-what-macro 1)))
+ (save-excursion
+ (if (and macro (string-match "cite" (car macro)))
+ (goto-char (cdr macro)))
+ (skip-chars-backward "^a-zA-Z0-9")
+ (reftex-this-word))))
+
;; Parse and format individual entries
(defun reftex-get-bib-names (field entry)
(progn
(set-buffer (get-buffer-create " *RefTeX-scratch*"))
(fundamental-mode)
+ (set-syntax-table reftex-syntax-table-for-bib)
(erase-buffer)
(insert entry))
(widen)
(defun reftex-parse-bibitem (item)
;; Parse a \bibitem entry
(let ((key "") (text ""))
- (when (string-match "\\`{\\([^}]+\\)}\\([\001-\255]*\\)" item)
+ (when (string-match "\\`{\\([^}]+\\)}\\([^\000]*\\)" item)
(setq key (match-string 1 item)
text (match-string 2 item)))
;; Clean up the text a little bit
;; Make a citation
;;;###autoload
-(defun reftex-citation (&optional no-insert)
+(defun reftex-citation (&optional no-insert format-key)
"Make a citation using BibTeX database files.
After prompting for a regular expression, scans the buffers with
bibtex entries (taken from the \\bibliography command) and offers the
-matching entries for selection. The selected entry is formated according
+matching entries for selection. The selected entry is formatted according
to `reftex-cite-format' and inserted into the buffer.
If NO-INSERT is non-nil, nothing is inserted, only the selected key returned.
+FORAT-KEY can be used to pre-select a citation format.
+
When called with one or two `C-u' prefixes, first rescans the document.
When called with a numeric prefix, make that many citations. When
-called with point inside the braces of a `\cite' command, it will
+called with point inside the braces of a `\\cite' command, it will
add another key, ignoring the value of `reftex-cite-format'.
The regular expression uses an expanded syntax: && is interpreted as `and'.
;; Call reftex-do-citation, but protected
(unwind-protect
- (reftex-do-citation current-prefix-arg no-insert)
+ (reftex-do-citation current-prefix-arg no-insert format-key)
(reftex-kill-temporary-buffers)))
-(defun reftex-do-citation (&optional arg no-insert)
+(defun reftex-do-citation (&optional arg no-insert format-key)
;; This really does the work of reftex-citation.
- (let* ((format (reftex-figure-out-cite-format arg no-insert))
+ (let* ((format (reftex-figure-out-cite-format arg no-insert format-key))
(docstruct-symbol reftex-docstruct-symbol)
(selected-entries (reftex-offer-bib-menu))
(insert-entries selected-entries)
;; Return the citation key
(car (car selected-entries))))
-(defun reftex-figure-out-cite-format (arg no-insert)
+(defun reftex-figure-out-cite-format (arg &optional no-insert format-key)
;; Check if there is already a cite command at point and change cite format
;; in order to only add another reference in the same cite command.
(let ((macro (car (reftex-what-macro 1)))
cite-format-value))
(when (listp format)
(setq key
- (reftex-select-with-char
- "" (concat "SELECT A CITATION FORMAT\n\n"
- (mapconcat
- (lambda (x)
- (format "[%c] %s %s" (car x)
- (if (> (car x) 31) " " "")
- (cdr x)))
- format "\n"))))
+ (or format-key
+ (reftex-select-with-char
+ "" (concat "SELECT A CITATION FORMAT\n\n"
+ (mapconcat
+ (lambda (x)
+ (format "[%c] %s %s" (car x)
+ (if (> (car x) 31) " " "")
+ (cdr x)))
+ format "\n")))))
(if (assq key format)
(setq format (cdr (assq key format)))
(error "No citation format associated with key `%c'" key)))))
format))
+(defun reftex-citep ()
+ "Call `reftex-citation' with a format selector `?p'."
+ (interactive)
+ (reftex-citation nil ?p))
+
+(defun reftex-citet ()
+ "Call `reftex-citation' with a format selector `?t'."
+ (interactive)
+ (reftex-citation nil ?t))
+
(defvar reftex-select-bib-map)
(defun reftex-offer-bib-menu ()
;; Offer bib menu and return list of selected items
- (let (found-list rtn key data selected-entries)
+ (let ((bibtype (reftex-bib-or-thebib))
+ found-list rtn key data selected-entries)
(while
(not
(catch 'done
;; Scan bibtex files
(setq found-list
(cond
- ((assq 'bib (symbol-value reftex-docstruct-symbol))
+ ((eq bibtype 'bib)
+; ((assq 'bib (symbol-value reftex-docstruct-symbol))
;; using BibTeX database files.
(reftex-extract-bib-entries (reftex-get-bibfile-list)))
- ((assq 'thebib (symbol-value reftex-docstruct-symbol))
+ ((eq bibtype 'thebib)
+; ((assq 'thebib (symbol-value reftex-docstruct-symbol))
;; using thebibliography environment.
(reftex-extract-bib-entries-from-thebibliography
- (cdr (assq 'thebib (symbol-value reftex-docstruct-symbol)))))
+ (reftex-uniquify
+ (mapcar 'cdr
+ (reftex-all-assq
+ 'thebib (symbol-value reftex-docstruct-symbol))))))
(reftex-default-bibliography
(message "Using default bibliography")
(reftex-extract-bib-entries (reftex-default-bibliography)))
(defun reftex-format-names (namelist n)
(let (last (len (length namelist)))
+ (if (= n 0) (setq n len))
(cond
((< len 1) "")
((= 1 len) (car namelist))
;; recommended for follow mode. It works OK for individual lookups.
(let ((win (selected-window))
(key (reftex-get-bib-field "&key" data))
- bibfile-list item tmp)
+ bibfile-list item bibtype)
(catch 'exit
(save-excursion
(set-buffer reftex-call-back-to-this-buffer)
+ (setq bibtype (reftex-bib-or-thebib))
(cond
- ((assq 'bib (symbol-value reftex-docstruct-symbol))
+ ((eq bibtype 'bib)
+; ((assq 'bib (symbol-value reftex-docstruct-symbol))
(setq bibfile-list (reftex-get-bibfile-list)))
- ((setq tmp (assq 'thebib (symbol-value reftex-docstruct-symbol)))
- (setq bibfile-list (list (cdr tmp))
+ ((eq bibtype 'thebib)
+; ((assq 'thebib (symbol-value reftex-docstruct-symbol))
+ (setq bibfile-list
+ (reftex-uniquify
+ (mapcar 'cdr
+ (reftex-all-assq
+ 'thebib (symbol-value reftex-docstruct-symbol))))
item t))
(reftex-default-bibliography
(setq bibfile-list (reftex-default-bibliography)))
- (t (ding) (throw 'exit))))
+ (t (ding) (throw 'exit nil))))
(when no-revisit
(setq bibfile-list (reftex-visited-files bibfile-list)))