]> code.delx.au - gnu-emacs/blobdiff - lisp/textmodes/reftex-cite.el
Add new maintainer (deego).
[gnu-emacs] / lisp / textmodes / reftex-cite.el
index afe000785e346bee56348a7b5b7dbf16a79ffa37..d4d5b508eefbd421b2f6a863ee1d9727837e6759 100644 (file)
@@ -1,8 +1,31 @@
-;;; 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'.
@@ -529,13 +621,13 @@ While entering the regexp, completion on knows citation keys is possible.
 
   ;; 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)
@@ -606,7 +698,7 @@ While entering the regexp, completion on knows citation keys is possible.
     ;; 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)))
@@ -636,37 +728,54 @@ While entering the regexp, completion on knows citation keys is possible.
              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)))
@@ -791,6 +900,7 @@ While entering the regexp, completion on knows citation keys is possible.
 
 (defun reftex-format-names (namelist n)
   (let (last (len (length namelist)))
+    (if (= n 0) (setq n len))
     (cond
      ((< len 1) "")
      ((= 1 len) (car namelist))
@@ -900,20 +1010,27 @@ While entering the regexp, completion on knows citation keys is possible.
   ;; 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)))