Replace Lisp calls to delete-backward-char by delete-char.
[bpt/emacs.git] / lisp / textmodes / bibtex.el
index 362aea1..e17cd9e 100644 (file)
@@ -1,7 +1,7 @@
 ;;; bibtex.el --- BibTeX mode for GNU Emacs
 
 ;; Copyright (C) 1992, 1994, 1995, 1996, 1997, 1998, 1999, 2001, 2002,
 ;;; bibtex.el --- BibTeX mode for GNU Emacs
 
 ;; Copyright (C) 1992, 1994, 1995, 1996, 1997, 1998, 1999, 2001, 2002,
-;;   2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+;;   2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
 
 ;; Author: Stefan Schoef <schoef@offis.uni-oldenburg.de>
 ;;      Bengt Martensson <bengt@mathematik.uni-Bremen.de>
 
 ;; Author: Stefan Schoef <schoef@offis.uni-oldenburg.de>
 ;;      Bengt Martensson <bengt@mathematik.uni-Bremen.de>
@@ -146,12 +146,24 @@ The value nil means do no formatting at all."
                       (const unify-case)
                       (const braces)
                       (const strings))))
                       (const unify-case)
                       (const braces)
                       (const strings))))
+(put 'bibtex-entry-format 'safe-local-variable
+     (lambda (x)
+       (or (eq x t)
+           (let ((OK t))
+             (while (consp x)
+               (unless (memq (pop x)
+                             '(opts-or-alts required-fields numerical-fields
+                               page-dashes whitespace inherit-booktitle realign
+                               last-comma delimiters unify-case braces strings))
+                 (setq OK nil)))
+             (unless (null x) (setq OK nil))
+             OK))))
 
 (defcustom bibtex-field-braces-alist nil
  "Alist of field regexps that \\[bibtex-clean-entry] encloses by braces.
 Each element has the form (FIELDS REGEXP), where FIELDS is a list
 of BibTeX field names and REGEXP is a regexp.
 
 (defcustom bibtex-field-braces-alist nil
  "Alist of field regexps that \\[bibtex-clean-entry] encloses by braces.
 Each element has the form (FIELDS REGEXP), where FIELDS is a list
 of BibTeX field names and REGEXP is a regexp.
-Whitespace in REGEXP will be replaced by \"[ \\t\\n]+\"."
+Space characters in REGEXP will be replaced by \"[ \\t\\n]+\"."
   :group 'bibtex
   :type '(repeat (list (repeat (string :tag "field name"))
                        (choice (regexp :tag "regexp")
   :group 'bibtex
   :type '(repeat (list (repeat (string :tag "field name"))
                        (choice (regexp :tag "regexp")
@@ -162,7 +174,7 @@ Whitespace in REGEXP will be replaced by \"[ \\t\\n]+\"."
 Each element has the form (FIELDS REGEXP TO-STR), where FIELDS is a list
 of BibTeX field names.  In FIELDS search for REGEXP, which are replaced
 by the BibTeX string constant TO-STR.
 Each element has the form (FIELDS REGEXP TO-STR), where FIELDS is a list
 of BibTeX field names.  In FIELDS search for REGEXP, which are replaced
 by the BibTeX string constant TO-STR.
-Whitespace in REGEXP will be replaced by \"[ \\t\\n]+\"."
+Space characters in REGEXP will be replaced by \"[ \\t\\n]+\"."
   :group 'bibtex
   :type '(repeat (list (repeat (string :tag "field name"))
                        (regexp :tag "From regexp")
   :group 'bibtex
   :type '(repeat (list (repeat (string :tag "field name"))
                        (regexp :tag "From regexp")
@@ -204,7 +216,7 @@ If value of `bibtex-maintain-sorted-entries' is `entry-class'
 entries are ordered according to the classes they belong to.  Each
 class contains a list of entry types.  An entry `catch-all' applies
 to all entries not explicitly mentioned."
 entries are ordered according to the classes they belong to.  Each
 class contains a list of entry types.  An entry `catch-all' applies
 to all entries not explicitly mentioned."
-  :group 'BibTeX
+  :group 'bibtex
   :type '(repeat (choice :tag "Class"
                          (const :tag "catch-all" (catch-all))
                          (repeat :tag "Entry type" string))))
   :type '(repeat (choice :tag "Class"
                          (const :tag "catch-all" (catch-all))
                          (repeat :tag "Entry type" string))))
@@ -734,11 +746,11 @@ See `bibtex-generate-autokey' for details."
 
 (defcustom bibtex-autokey-titleword-ignore
   '("A" "An" "On" "The" "Eine?" "Der" "Die" "Das"
 
 (defcustom bibtex-autokey-titleword-ignore
   '("A" "An" "On" "The" "Eine?" "Der" "Die" "Das"
-    "[^[:upper:]].*" ".*[^[:upper:]0-9].*")
+    "[^[:upper:]].*" ".*[^[:upper:][:lower:]0-9].*")
   "Determines words from the title that are not to be used in the key.
 Each item of the list is a regexp.  If a word of the title matches a
 regexp from that list, it is not included in the title part of the key.
   "Determines words from the title that are not to be used in the key.
 Each item of the list is a regexp.  If a word of the title matches a
 regexp from that list, it is not included in the title part of the key.
-See `bibtex-generate-autokey' for details."
+Case is significant.  See `bibtex-generate-autokey' for details."
   :group 'bibtex-autokey
   :type '(repeat regexp))
 
   :group 'bibtex-autokey
   :type '(repeat regexp))
 
@@ -795,7 +807,7 @@ See `bibtex-generate-autokey' for details."
   :type 'string)
 
 (defcustom bibtex-autokey-year-title-separator ":_"
   :type 'string)
 
 (defcustom bibtex-autokey-year-title-separator ":_"
-  "String to be put between name part and year part of key.
+  "String to be put between year part and title part of key.
 See `bibtex-generate-autokey' for details."
   :group 'bibtex-autokey
   :type 'string)
 See `bibtex-generate-autokey' for details."
   :group 'bibtex-autokey
   :type 'string)
@@ -895,8 +907,9 @@ and with the `match-data' properly set.
 Case is always ignored.  Always remove the field delimiters.
 If `bibtex-expand-strings' is non-nil, BibTeX strings are expanded
 for generating the URL.
 Case is always ignored.  Always remove the field delimiters.
 If `bibtex-expand-strings' is non-nil, BibTeX strings are expanded
 for generating the URL.
+Set this variable before loading BibTeX mode.
 
 
-The following is a complex example, see http://link.aps.org/linkfaq.html.
+The following is a complex example, see URL `http://link.aps.org/'.
 
    (((\"journal\" . \"\\\\=<\\(PR[ABCDEL]?\\|RMP\\)\\\\=>\")
      \"http://link.aps.org/abstract/%s/v%s/p%s\"
 
    (((\"journal\" . \"\\\\=<\\(PR[ABCDEL]?\\|RMP\\)\\\\=>\")
      \"http://link.aps.org/abstract/%s/v%s/p%s\"
@@ -933,10 +946,12 @@ The following is a complex example, see http://link.aps.org/linkfaq.html.
 Each rule should be of the form (REGEXP . SUBEXP), where SUBEXP
 specifies which parenthesized expression in REGEXP is a cited key.
 Case is significant.
 Each rule should be of the form (REGEXP . SUBEXP), where SUBEXP
 specifies which parenthesized expression in REGEXP is a cited key.
 Case is significant.
-Used by `bibtex-search-crossref' and for font-locking."
+Used by `bibtex-search-crossref' and for font-locking.
+Set this variable before loading BibTeX mode."
   :group 'bibtex
   :type '(repeat (cons (regexp :tag "Regexp")
   :group 'bibtex
   :type '(repeat (cons (regexp :tag "Regexp")
-                       (integer :tag "Number"))))
+                       (integer :tag "Number")))
+  :version "23.1")
 
 (defcustom bibtex-expand-strings nil
   "If non-nil, expand strings when extracting the content of a BibTeX field."
 
 (defcustom bibtex-expand-strings nil
   "If non-nil, expand strings when extracting the content of a BibTeX field."
@@ -1500,8 +1515,8 @@ If `bibtex-expand-strings' is non-nil, also expand BibTeX strings."
       (save-excursion
         (goto-char (bibtex-start-of-text-in-field bounds))
         (let ((epoint (bibtex-end-of-text-in-field bounds))
       (save-excursion
         (goto-char (bibtex-start-of-text-in-field bounds))
         (let ((epoint (bibtex-end-of-text-in-field bounds))
-              content opoint)
-          (while (< (setq opoint (point)) epoint)
+              content)
+          (while (< (point) epoint)
             (if (looking-at bibtex-field-const)
                 (let ((mtch (match-string-no-properties 0)))
                   (push (or (if bibtex-expand-strings
             (if (looking-at bibtex-field-const)
                 (let ((mtch (match-string-no-properties 0)))
                   (push (or (if bibtex-expand-strings
@@ -1712,13 +1727,18 @@ entry and `match-data' corresponds to the header of the entry,
 see regexp `bibtex-entry-head'.  If `bibtex-sort-ignore-string-entries'
 is non-nil, FUN is not called for @String entries."
   (let ((case-fold-search t)
 see regexp `bibtex-entry-head'.  If `bibtex-sort-ignore-string-entries'
 is non-nil, FUN is not called for @String entries."
   (let ((case-fold-search t)
+        (end-marker (make-marker))
         found)
         found)
+    ;; Use marker to keep track of the buffer position of the end of
+    ;; a BibTeX entry as this position may change during reformatting.
+    (set-marker-insertion-type end-marker t)
     (save-excursion
       (goto-char (point-min))
       (while (setq found (bibtex-skip-to-valid-entry))
     (save-excursion
       (goto-char (point-min))
       (while (setq found (bibtex-skip-to-valid-entry))
+        (set-marker end-marker (cdr found))
         (looking-at bibtex-any-entry-maybe-empty-head)
         (looking-at bibtex-any-entry-maybe-empty-head)
-        (funcall fun (bibtex-key-in-head "") (car found) (cdr found))
-        (goto-char (cdr found))))))
+        (funcall fun (bibtex-key-in-head "") (car found) end-marker)
+        (goto-char end-marker)))))
 
 (defun bibtex-progress-message (&optional flag interval)
   "Echo a message about progress of current buffer.
 
 (defun bibtex-progress-message (&optional flag interval)
   "Echo a message about progress of current buffer.
@@ -1816,13 +1836,16 @@ are ignored.  Return point"
   "Search for BibTeX field enclosing point.
 For `bibtex-mode''s internal algorithms, a field begins at the comma
 following the preceding field.  Usually, this is not what the user expects.
   "Search for BibTeX field enclosing point.
 For `bibtex-mode''s internal algorithms, a field begins at the comma
 following the preceding field.  Usually, this is not what the user expects.
-Thus if COMMA is non-nil, the \"current field\" includes the terminating comma.
+Thus if COMMA is non-nil, the \"current field\" includes the terminating comma
+as well as the entry delimiter if it appears on the same line.
 Unless NOERR is non-nil, signal an error if no enclosing field is found.
 On success return bounds, nil otherwise.  Do not move point."
   (save-excursion
     (when comma
       (end-of-line)
       (skip-chars-backward " \t")
 Unless NOERR is non-nil, signal an error if no enclosing field is found.
 On success return bounds, nil otherwise.  Do not move point."
   (save-excursion
     (when comma
       (end-of-line)
       (skip-chars-backward " \t")
+      ;; Ignore entry delimiter and comma at end of line.
+      (if (memq (preceding-char) '(?} ?\))) (forward-char -1))
       (if (= (preceding-char) ?,) (forward-char -1)))
 
     (let ((bounds (bibtex-search-backward-field bibtex-field-name t)))
       (if (= (preceding-char) ?,) (forward-char -1)))
 
     (let ((bounds (bibtex-search-backward-field bibtex-field-name t)))
@@ -1858,7 +1881,14 @@ Optional arg COMMA is as in `bibtex-enclosing-field'."
       (bibtex-skip-to-valid-entry)
       (push-mark)
       (insert (funcall fun 'bibtex-entry-kill-ring-yank-pointer
       (bibtex-skip-to-valid-entry)
       (push-mark)
       (insert (funcall fun 'bibtex-entry-kill-ring-yank-pointer
-                       bibtex-entry-kill-ring)))))
+                       bibtex-entry-kill-ring))
+      (unless (functionp bibtex-reference-keys)
+        ;; update `bibtex-reference-keys'
+        (save-excursion
+          (goto-char (mark t))
+          (looking-at bibtex-any-entry-maybe-empty-head)
+          (let ((key (bibtex-key-in-head)))
+            (if key (push (cons key t) bibtex-reference-keys))))))))
 
 (defun bibtex-format-entry ()
   "Helper function for `bibtex-clean-entry'.
 
 (defun bibtex-format-entry ()
   "Helper function for `bibtex-clean-entry'.
@@ -1879,6 +1909,7 @@ Formats current entry according to variable `bibtex-entry-format'."
                               last-comma delimiters unify-case braces
                               strings)
                   bibtex-entry-format))
                               last-comma delimiters unify-case braces
                               strings)
                   bibtex-entry-format))
+        (left-delim-re (regexp-quote (bibtex-field-left-delimiter)))
         bounds crossref-key req-field-list default-field-list field-list
         alt-fields error-field-name)
     (unwind-protect
         bounds crossref-key req-field-list default-field-list field-list
         alt-fields error-field-name)
     (unwind-protect
@@ -1918,7 +1949,8 @@ Formats current entry according to variable `bibtex-entry-format'."
 
                 ;; Do we have a crossref key?
                 (goto-char (point-min))
 
                 ;; Do we have a crossref key?
                 (goto-char (point-min))
-                (if (setq bounds (bibtex-search-forward-field "crossref"))
+                (if (setq bounds (bibtex-search-forward-field
+                                  "\\(OPT\\)?crossref"))
                     (let ((text (bibtex-text-in-field-bounds bounds t)))
                       (unless (equal "" text)
                         (setq crossref-key text))))
                     (let ((text (bibtex-text-in-field-bounds bounds t)))
                       (unless (equal "" text)
                         (setq crossref-key text))))
@@ -1973,6 +2005,17 @@ Formats current entry according to variable `bibtex-entry-format'."
                   (unless deleted
                     (push field-name field-list)
 
                   (unless deleted
                     (push field-name field-list)
 
+                    ;; Remove whitespace at beginning and end of field.
+                    ;; We do not look at individual parts of the field
+                    ;; as {foo } # bar # { baz} is a fine field.
+                    (when (memq 'whitespace format)
+                      (goto-char beg-text)
+                      (if (looking-at "\\([{\"]\\)[ \t\n]+")
+                          (replace-match "\\1"))
+                      (goto-char end-text)
+                      (if (looking-back "[ \t\n]+\\([}\"]\\)" beg-text t)
+                          (replace-match "\\1")))
+
                     ;; remove delimiters from purely numerical fields
                     (when (and (memq 'numerical-fields format)
                                (progn (goto-char beg-text)
                     ;; remove delimiters from purely numerical fields
                     (when (and (memq 'numerical-fields format)
                                (progn (goto-char beg-text)
@@ -1985,13 +2028,21 @@ Formats current entry according to variable `bibtex-entry-format'."
                     ;; update delimiters
                     (when (memq 'delimiters format)
                       (goto-char beg-text)
                     ;; update delimiters
                     (when (memq 'delimiters format)
                       (goto-char beg-text)
-                      (when (looking-at "[{\"]")
-                        (delete-char 1)
-                        (insert (bibtex-field-left-delimiter)))
-                      (goto-char (1- (marker-position end-text)))
-                      (when (looking-at "[}\"]")
-                        (delete-char 1)
-                        (insert (bibtex-field-right-delimiter))))
+                      ;; simplified from `bibtex-parse-field-text', as we
+                      ;; already checked that the field format is correct
+                      (while (< (point) end-text)
+                        (if (looking-at bibtex-field-const)
+                            (goto-char (match-end 0))
+                          (let ((boundaries (bibtex-parse-field-string)))
+                            (if (looking-at left-delim-re)
+                                (goto-char (cdr boundaries))
+                              (delete-char 1)
+                              (insert (bibtex-field-left-delimiter))
+                              (goto-char (1- (cdr boundaries)))
+                              (delete-char 1)
+                              (insert (bibtex-field-right-delimiter)))))
+                        (if (looking-at "[ \t\n]*#[ \t\n]*")
+                            (goto-char (match-end 0)))))
 
                     ;; update page dashes
                     (if (and (memq 'page-dashes format)
 
                     ;; update page dashes
                     (if (and (memq 'page-dashes format)
@@ -2001,15 +2052,6 @@ Formats current entry according to variable `bibtex-entry-format'."
                                      "\\([\"{][0-9]+\\)[ \t\n]*--?[ \t\n]*\\([0-9]+[\"}]\\)")))
                         (replace-match "\\1-\\2"))
 
                                      "\\([\"{][0-9]+\\)[ \t\n]*--?[ \t\n]*\\([0-9]+[\"}]\\)")))
                         (replace-match "\\1-\\2"))
 
-                    ;; remove whitespace at beginning and end of field
-                    (when (memq 'whitespace format)
-                      (goto-char beg-text)
-                      (if (looking-at "\\([{\"]\\)[ \t\n]+")
-                          (replace-match "\\1"))
-                      (goto-char end-text)
-                      (if (looking-back "[ \t\n]+\\([}\"]\\)" beg-text t)
-                          (replace-match "\\1")))
-
                     ;; enclose field text by braces according to
                     ;; `bibtex-field-braces-alist'.
                     (let (case-fold-search temp) ; Case-sensitive search
                     ;; enclose field text by braces according to
                     ;; `bibtex-field-braces-alist'.
                     (let (case-fold-search temp) ; Case-sensitive search
@@ -2052,7 +2094,7 @@ Formats current entry according to variable `bibtex-entry-format'."
                                 ;; if match not at left subfield boundary...
                                 (if (< (1+ (nth 1 bounds)) (match-beginning 0))
                                     (insert (bibtex-field-right-delimiter) " # ")
                                 ;; if match not at left subfield boundary...
                                 (if (< (1+ (nth 1 bounds)) (match-beginning 0))
                                     (insert (bibtex-field-right-delimiter) " # ")
-                                  (delete-backward-char 1))))))))
+                                  (delete-char -1))))))))
 
                     ;; use book title of crossref'd entry
                     (if (and (memq 'inherit-booktitle format)
 
                     ;; use book title of crossref'd entry
                     (if (and (memq 'inherit-booktitle format)
@@ -2139,7 +2181,7 @@ Formats current entry according to variable `bibtex-entry-format'."
               (if (memq 'realign format)
                   (bibtex-fill-entry)))))
 
               (if (memq 'realign format)
                   (bibtex-fill-entry)))))
 
-      ;; Unwindform: move point to location where error occured if possible
+      ;; Unwindform: move point to location where error occurred if possible
       (if error-field-name
           (let (bounds)
             (when (save-excursion
       (if error-field-name
           (let (bounds)
             (when (save-excursion
@@ -2159,7 +2201,7 @@ Return optimized value to be used by `bibtex-format-entry'."
   (setq regexp-alist
         (mapcar (lambda (e)
                   (list (car e)
   (setq regexp-alist
         (mapcar (lambda (e)
                   (list (car e)
-                        (replace-regexp-in-string "[ \t\n]+" "[ \t\n]+" (nth 1 e))
+                        (replace-regexp-in-string " +" "[ \t\n]+" (nth 1 e))
                         (nth 2 e))) ; nil for 'braces'.
                 regexp-alist))
   (let (opt-list)
                         (nth 2 e))) ; nil for 'braces'.
                 regexp-alist))
   (let (opt-list)
@@ -2283,6 +2325,10 @@ Return the result as a string"
     ;; gather words from titlestring into a list.  Ignore
     ;; specific words and use only a specific amount of words.
     (let ((counter 0)
     ;; gather words from titlestring into a list.  Ignore
     ;; specific words and use only a specific amount of words.
     (let ((counter 0)
+         (ignore-re (concat "\\`\\(?:"
+                             (mapconcat 'identity
+                                        bibtex-autokey-titleword-ignore "\\|")
+                             "\\)\\'"))
           titlewords titlewords-extra word)
       (while (and (or (not (numberp bibtex-autokey-titlewords))
                       (< counter (+ bibtex-autokey-titlewords
           titlewords titlewords-extra word)
       (while (and (or (not (numberp bibtex-autokey-titlewords))
                       (< counter (+ bibtex-autokey-titlewords
@@ -2291,13 +2337,9 @@ Return the result as a string"
         (setq word (match-string 0 titlestring)
               titlestring (substring titlestring (match-end 0)))
         ;; Ignore words matched by one of the elements of
         (setq word (match-string 0 titlestring)
               titlestring (substring titlestring (match-end 0)))
         ;; Ignore words matched by one of the elements of
-        ;; `bibtex-autokey-titleword-ignore'
-        (unless (let ((lst bibtex-autokey-titleword-ignore))
-                  (while (and lst
-                              (not (string-match (concat "\\`\\(?:" (car lst)
-                                                         "\\)\\'") word)))
-                    (setq lst (cdr lst)))
-                  lst)
+        ;; `bibtex-autokey-titleword-ignore'.  Case is significant.
+        (unless (let (case-fold-search)
+                 (string-match ignore-re word))
           (setq counter (1+ counter))
           (if (or (not (numberp bibtex-autokey-titlewords))
                   (<= counter bibtex-autokey-titlewords))
           (setq counter (1+ counter))
           (if (or (not (numberp bibtex-autokey-titlewords))
                   (<= counter bibtex-autokey-titlewords))
@@ -2416,8 +2458,10 @@ Concatenate the key:
       (apply 'append
              (mapcar (lambda (buf)
                        (with-current-buffer buf bibtex-reference-keys))
       (apply 'append
              (mapcar (lambda (buf)
                        (with-current-buffer buf bibtex-reference-keys))
-                     (bibtex-initialize t)))
-    bibtex-reference-keys))
+                     ;; include current buffer only if it uses `bibtex-mode'
+                     (bibtex-initialize (eq major-mode 'bibtex-mode))))
+    (if (eq major-mode 'bibtex-mode)
+        bibtex-reference-keys)))
 
 (defun bibtex-read-key (prompt &optional key global)
   "Read BibTeX key from minibuffer using PROMPT and default KEY.
 
 (defun bibtex-read-key (prompt &optional key global)
   "Read BibTeX key from minibuffer using PROMPT and default KEY.
@@ -2507,8 +2551,7 @@ Return alist of strings if parsing was completed, `aborted' otherwise."
   (save-excursion
     (save-match-data
       (goto-char (point-min))
   (save-excursion
     (save-match-data
       (goto-char (point-min))
-      (let ((strings (if (and add
-                              (listp bibtex-strings))
+      (let ((strings (if (and add (not (functionp bibtex-strings)))
                          bibtex-strings))
             bounds key)
         (if (listp add)
                          bibtex-strings))
             bounds key)
         (if (listp add)
@@ -2531,8 +2574,9 @@ Return alist of strings if parsing was completed, `aborted' otherwise."
 
 (defun bibtex-strings ()
   "Return `bibtex-strings'.  Initialize this variable if necessary."
 
 (defun bibtex-strings ()
   "Return `bibtex-strings'.  Initialize this variable if necessary."
-  (if (listp bibtex-strings) bibtex-strings
-    (bibtex-parse-strings (bibtex-string-files-init))))
+  (if (functionp bibtex-strings)
+      (bibtex-parse-strings (bibtex-string-files-init))
+    bibtex-strings))
 
 (defun bibtex-string-files-init ()
   "Return initialization for `bibtex-strings'.
 
 (defun bibtex-string-files-init ()
   "Return initialization for `bibtex-strings'.
@@ -2644,7 +2688,11 @@ When called interactively, FORCE is t, CURRENT is t if current buffer uses
     (dolist (file file-list)
       (if (file-readable-p file)
         (push (find-file-noselect file) buffer-list)))
     (dolist (file file-list)
       (if (file-readable-p file)
         (push (find-file-noselect file) buffer-list)))
-    ;; include current buffer iff we want it
+    ;; Include current buffer iff we want it.
+    ;; Exclude current buffer if it doesn't use `bibtex-mode'.
+    ;; Thus calling `bibtex-initialize' gives meaningful results for
+    ;; any current buffer.
+    (unless (and current (eq major-mode 'bibtex-mode)) (setq current nil))
     (cond ((and current (not (memq (current-buffer) buffer-list)))
            (push (current-buffer) buffer-list))
           ((and (not current) (memq (current-buffer) buffer-list))
     (cond ((and current (not (memq (current-buffer) buffer-list)))
            (push (current-buffer) buffer-list))
           ((and (not current) (memq (current-buffer) buffer-list))
@@ -2652,8 +2700,10 @@ When called interactively, FORCE is t, CURRENT is t if current buffer uses
     ;; parse keys
     (dolist (buffer buffer-list)
       (with-current-buffer buffer
     ;; parse keys
     (dolist (buffer buffer-list)
       (with-current-buffer buffer
-        (if (or force (nlistp bibtex-reference-keys))
-            (bibtex-parse-keys))))
+        (if (or force (functionp bibtex-reference-keys))
+            (bibtex-parse-keys))
+        (unless (functionp bibtex-strings)
+          (bibtex-parse-strings (bibtex-string-files-init)))))
     ;; select BibTeX buffer
     (if select
         (if buffer-list
     ;; select BibTeX buffer
     (if select
         (if buffer-list
@@ -2671,35 +2721,13 @@ COMPLETIONS is an alist of strings.  If point is not after the part
 of a word, all strings are listed.  Return completion."
   ;; Return value is used by cleanup functions.
   ;; Code inspired by `lisp-complete-symbol'.
 of a word, all strings are listed.  Return completion."
   ;; Return value is used by cleanup functions.
   ;; Code inspired by `lisp-complete-symbol'.
-  (let* ((case-fold-search t)
-         (beg (save-excursion
+  (let ((beg (save-excursion
                 (re-search-backward "[ \t{\"]")
                 (forward-char)
                 (point)))
                 (re-search-backward "[ \t{\"]")
                 (forward-char)
                 (point)))
-         (end (point))
-         (pattern (buffer-substring-no-properties beg end))
-         (completion (try-completion pattern completions)))
-    (cond ((not completion)
-           (error "Can't find completion for `%s'" pattern))
-          ((eq completion t)
-           pattern)
-          ((not (string= pattern completion))
-           (delete-region beg end)
-           (insert completion)
-           ;; Don't leave around a completions buffer that's out of date.
-           (let ((win (get-buffer-window "*Completions*" 0)))
-             (if win (with-selected-window win (bury-buffer))))
-           completion)
-          (t
-           (let ((minibuf-is-in-use
-                  (eq (minibuffer-window) (selected-window))))
-             (unless minibuf-is-in-use (message "Making completion list..."))
-             (with-output-to-temp-buffer "*Completions*"
-               (display-completion-list
-                (sort (all-completions pattern completions) 'string<) pattern))
-             (unless minibuf-is-in-use
-               (message "Making completion list...done")))
-           nil))))
+        (end (point)))
+    (when (completion-in-region beg end completions)
+      (buffer-substring beg (point)))))
 
 (defun bibtex-complete-string-cleanup (str compl)
   "Cleanup after inserting string STR.
 
 (defun bibtex-complete-string-cleanup (str compl)
   "Cleanup after inserting string STR.
@@ -2919,7 +2947,7 @@ BOUND limits the search."
 
 General information on working with BibTeX mode:
 
 
 General information on working with BibTeX mode:
 
-Use commands such as \\[bibtex-Book] to get a template for a specific entry.
+Use commands such as \\<bibtex-mode-map>\\[bibtex-Book] to get a template for a specific entry.
 Then fill in all desired fields using \\[bibtex-next-field] to jump from field
 to field.  After having filled in all desired fields in the entry, clean the
 new entry with the command \\[bibtex-clean-entry].
 Then fill in all desired fields using \\[bibtex-next-field] to jump from field
 to field.  After having filled in all desired fields in the entry, clean the
 new entry with the command \\[bibtex-clean-entry].
@@ -3303,7 +3331,8 @@ Return the new location of point."
           ((looking-at bibtex-any-valid-entry-type)
            ;; Parsing of entry failed
            (error "Syntactically incorrect BibTeX entry starts here"))
           ((looking-at bibtex-any-valid-entry-type)
            ;; Parsing of entry failed
            (error "Syntactically incorrect BibTeX entry starts here"))
-          (t (if (interactive-p) (message "Not on a known BibTeX entry."))
+          (t (if (called-interactively-p 'interactive)
+                (message "Not on a known BibTeX entry."))
              (goto-char pnt)))
     (point)))
 
              (goto-char pnt)))
     (point)))
 
@@ -3460,7 +3489,7 @@ are ignored."
   (bibtex-beginning-of-first-entry)     ; Needed by `sort-subr'
   (bibtex-init-sort-entry-class-alist)  ; Needed by `bibtex-lessp'.
   (if (and (eq bibtex-maintain-sorted-entries 'crossref)
   (bibtex-beginning-of-first-entry)     ; Needed by `sort-subr'
   (bibtex-init-sort-entry-class-alist)  ; Needed by `bibtex-lessp'.
   (if (and (eq bibtex-maintain-sorted-entries 'crossref)
-           (nlistp bibtex-reference-keys))
+           (functionp bibtex-reference-keys))
       (bibtex-parse-keys))              ; Needed by `bibtex-lessp'.
   (sort-subr nil
              'bibtex-skip-to-valid-entry   ; NEXTREC function
       (bibtex-parse-keys))              ; Needed by `bibtex-lessp'.
   (sort-subr nil
              'bibtex-skip-to-valid-entry   ; NEXTREC function
@@ -3491,7 +3520,7 @@ for a crossref key, t otherwise."
             (end (cdr (bibtex-valid-entry t)))
             (_ (unless end (error "Not inside valid entry")))
             (beg (match-end 0)) ; set by `bibtex-valid-entry'
             (end (cdr (bibtex-valid-entry t)))
             (_ (unless end (error "Not inside valid entry")))
             (beg (match-end 0)) ; set by `bibtex-valid-entry'
-            (bounds (bibtex-search-forward-field "crossref" end))
+            (bounds (bibtex-search-forward-field "\\(OPT\\)?crossref" end))
             case-fold-search best temp crossref-key)
        (if bounds
            (setq crossref-key (bibtex-text-in-field-bounds bounds t)
             case-fold-search best temp crossref-key)
        (if bounds
            (setq crossref-key (bibtex-text-in-field-bounds bounds t)
@@ -3567,8 +3596,7 @@ mode is not `bibtex-mode', START is nil, and DISPLAY is t."
         (while (and (not found)
                     (setq buffer (pop buffer-list)))
           (with-current-buffer buffer
         (while (and (not found)
                     (setq buffer (pop buffer-list)))
           (with-current-buffer buffer
-            (if (and (listp bibtex-reference-keys)
-                     (cdr (assoc-string key bibtex-reference-keys)))
+            (if (cdr (assoc-string key bibtex-reference-keys))
                 ;; `bibtex-search-entry' moves point if key found
                 (setq found (bibtex-search-entry key)))))
         (cond ((and found display)
                 ;; `bibtex-search-entry' moves point if key found
                 (setq found (bibtex-search-entry key)))))
         (cond ((and found display)
@@ -3608,7 +3636,7 @@ see `bibtex-validate'.
 Return t if preparation was successful or nil if entry KEY already exists."
   (bibtex-init-sort-entry-class-alist)  ; Needed by `bibtex-lessp'.
   (if (and (eq bibtex-maintain-sorted-entries 'crossref)
 Return t if preparation was successful or nil if entry KEY already exists."
   (bibtex-init-sort-entry-class-alist)  ; Needed by `bibtex-lessp'.
   (if (and (eq bibtex-maintain-sorted-entries 'crossref)
-           (nlistp bibtex-reference-keys))
+           (functionp bibtex-reference-keys))
       (bibtex-parse-keys))              ; Needed by `bibtex-lessp'.
   (let ((key (nth 0 index))
         key-exist)
       (bibtex-parse-keys))              ; Needed by `bibtex-lessp'.
   (let ((key (nth 0 index))
         key-exist)
@@ -3815,7 +3843,8 @@ Return t if test was successful, nil otherwise."
               (insert (format "%s:%d: %s\n" file (car err) (cdr err))))
             (set-buffer-modified-p nil)
             (toggle-read-only 1)
               (insert (format "%s:%d: %s\n" file (car err) (cdr err))))
             (set-buffer-modified-p nil)
             (toggle-read-only 1)
-            (goto-line 3)) ; first error message
+            (goto-char (point-min))
+            (forward-line 2)) ; first error message
           (display-buffer err-buf)
           nil) ; return `nil' (i.e., buffer is invalid)
       (message "%s is syntactically correct"
           (display-buffer err-buf)
           nil) ; return `nil' (i.e., buffer is invalid)
       (message "%s is syntactically correct"
@@ -3831,8 +3860,7 @@ Return t if test was successful, nil otherwise."
         buffer-key-list current-buf current-keys error-list)
     ;; Check for duplicate keys within BibTeX buffer
     (dolist (buffer buffer-list)
         buffer-key-list current-buf current-keys error-list)
     ;; Check for duplicate keys within BibTeX buffer
     (dolist (buffer buffer-list)
-      (save-excursion
-        (set-buffer buffer)
+      (with-current-buffer buffer
         (let (entry-type key key-list)
           (goto-char (point-min))
           (while (re-search-forward bibtex-entry-head nil t)
         (let (entry-type key key-list)
           (goto-char (point-min))
           (while (re-search-forward bibtex-entry-head nil t)
@@ -3872,7 +3900,8 @@ Return t if test was successful, nil otherwise."
             (dolist (err (sort error-list 'string-lessp)) (insert err))
             (set-buffer-modified-p nil)
             (toggle-read-only 1)
             (dolist (err (sort error-list 'string-lessp)) (insert err))
             (set-buffer-modified-p nil)
             (toggle-read-only 1)
-            (goto-line 3)) ; first error message
+            (goto-char (point-min))
+            (forward-line 2)) ; first error message
           (display-buffer err-buf)
           nil) ; return `nil' (i.e., buffer is invalid)
       (message "No duplicate keys.")
           (display-buffer err-buf)
           nil) ; return `nil' (i.e., buffer is invalid)
       (message "No duplicate keys.")
@@ -4043,7 +4072,11 @@ but do not actually kill it.  Optional arg COMMA is as in
            (end (bibtex-end-of-field bounds))
            (beg (bibtex-start-of-field bounds)))
       (goto-char end)
            (end (bibtex-end-of-field bounds))
            (beg (bibtex-start-of-field bounds)))
       (goto-char end)
-      (skip-chars-forward ",")
+      ;; Preserve white space at end of BibTeX entry
+      (if (looking-at "[ \t\n]*[)}]")
+          (progn (skip-chars-backward " \t\n")
+                 (setq end (point)))
+        (skip-chars-forward ","))
       (push (list (bibtex-name-in-field bounds) nil
                   (bibtex-text-in-field-bounds bounds))
             bibtex-field-kill-ring)
       (push (list (bibtex-name-in-field bounds) nil
                   (bibtex-text-in-field-bounds bounds))
             bibtex-field-kill-ring)
@@ -4057,7 +4090,7 @@ but do not actually kill it.  Optional arg COMMA is as in
   (setq bibtex-last-kill-command 'field))
 
 (defun bibtex-copy-field-as-kill (&optional comma)
   (setq bibtex-last-kill-command 'field))
 
 (defun bibtex-copy-field-as-kill (&optional comma)
-  "Copy the BibTeX field at point to the kill ring.
+  "Copy the BibTeX field at point to `bibtex-field-kill-ring'.
 Optional arg COMMA is as in `bibtex-enclosing-field'.  It is t for
 interactive calls."
   (interactive (list t))
 Optional arg COMMA is as in `bibtex-enclosing-field'.  It is t for
 interactive calls."
   (interactive (list t))
@@ -4071,6 +4104,8 @@ but do not actually kill it."
   (save-excursion
     (let* ((case-fold-search t)
            (beg (bibtex-beginning-of-entry))
   (save-excursion
     (let* ((case-fold-search t)
            (beg (bibtex-beginning-of-entry))
+           (key (progn (looking-at bibtex-any-entry-maybe-empty-head)
+                       (bibtex-key-in-head)))
            (end (progn (bibtex-end-of-entry)
                        (if (re-search-forward
                             bibtex-any-entry-maybe-empty-head nil 'move)
            (end (progn (bibtex-end-of-entry)
                        (if (re-search-forward
                             bibtex-any-entry-maybe-empty-head nil 'move)
@@ -4084,7 +4119,11 @@ but do not actually kill it."
                   nil))
       (setq bibtex-entry-kill-ring-yank-pointer bibtex-entry-kill-ring)
       (unless copy-only
                   nil))
       (setq bibtex-entry-kill-ring-yank-pointer bibtex-entry-kill-ring)
       (unless copy-only
-        (delete-region beg end))))
+        (delete-region beg end)
+        ;; remove key from `bibtex-reference-keys'.
+        (unless (functionp bibtex-reference-keys)
+          (setq bibtex-reference-keys
+                (delete (cons key t) bibtex-reference-keys))))))
   (setq bibtex-last-kill-command 'entry))
 
 (defun bibtex-copy-entry-as-kill ()
   (setq bibtex-last-kill-command 'entry))
 
 (defun bibtex-copy-entry-as-kill ()
@@ -4118,7 +4157,16 @@ comes the newest one."
   (unless (eq last-command 'bibtex-yank)
     (error "Previous command was not a BibTeX yank"))
   (setq this-command 'bibtex-yank)
   (unless (eq last-command 'bibtex-yank)
     (error "Previous command was not a BibTeX yank"))
   (setq this-command 'bibtex-yank)
-  (let ((inhibit-read-only t))
+  (let ((inhibit-read-only t) key)
+    ;; point is at end of yanked entry
+    (unless (functionp bibtex-reference-keys)
+      ;; remove key of yanked entry from `bibtex-reference-keys'
+      (save-excursion
+        (goto-char (mark t))
+        (if (and (looking-at bibtex-any-entry-maybe-empty-head)
+                 (setq key (bibtex-key-in-head)))
+            (setq bibtex-reference-keys
+                  (delete (cons key t) bibtex-reference-keys)))))
     (delete-region (point) (mark t))
     (bibtex-insert-kill n t)))
 
     (delete-region (point) (mark t))
     (bibtex-insert-kill n t)))
 
@@ -4150,7 +4198,7 @@ intermixed with \\[bibtex-pop-previous] (bibtex-pop-previous)."
 
 (defun bibtex-clean-entry (&optional new-key called-by-reformat)
   "Finish editing the current BibTeX entry and clean it up.
 
 (defun bibtex-clean-entry (&optional new-key called-by-reformat)
   "Finish editing the current BibTeX entry and clean it up.
-Check that no required fields are empty and formats entry dependent
+Check that no required fields are empty and format entry dependent
 on the value of `bibtex-entry-format'.
 If the reference key of the entry is empty or a prefix argument is given,
 calculate a new reference key.  (Note: this works only if fields in entry
 on the value of `bibtex-entry-format'.
 If the reference key of the entry is empty or a prefix argument is given,
 calculate a new reference key.  (Note: this works only if fields in entry
@@ -4223,23 +4271,27 @@ At end of the cleaning process, the functions in
             (if (cdr (assoc-string key bibtex-reference-keys))
                 (error "Duplicate key in %s" (buffer-file-name)))))
 
             (if (cdr (assoc-string key bibtex-reference-keys))
                 (error "Duplicate key in %s" (buffer-file-name)))))
 
-        ;; Only update the list of keys if it has been built already.
+        ;; Only update `bibtex-strings' and `bibtex-reference-keys'
+        ;; if they have been built already.
         (cond ((eq entry-type 'string)
         (cond ((eq entry-type 'string)
-               (if (and (listp bibtex-strings)
-                        (not (assoc key bibtex-strings)))
-                   (push (cons key (bibtex-text-in-string
-                                    (bibtex-parse-string) t))
-                           bibtex-strings)))
+               ;; We have a @String entry.
+               (unless (or (functionp bibtex-strings)
+                           (assoc key bibtex-strings))
+                 (push (cons key (bibtex-text-in-string
+                                  (bibtex-parse-string) t))
+                       bibtex-strings)))
               ;; We have a normal entry.
               ;; We have a normal entry.
-              ((listp bibtex-reference-keys)
-               (cond ((not (assoc key bibtex-reference-keys))
-                      (push (cons key t) bibtex-reference-keys))
-                     ((not (cdr (assoc key bibtex-reference-keys)))
-                      ;; Turn a crossref key into a header key
-                      (setq bibtex-reference-keys
-                            (cons (cons key t)
-                                  (delete (list key) bibtex-reference-keys)))))
-               ;; Handle crossref key.
+              ((not (functionp bibtex-reference-keys))
+               (let ((found (assoc key bibtex-reference-keys)))
+                 (cond ((not found)
+                        (push (cons key t) bibtex-reference-keys))
+                       ((not (cdr found))
+                        ;; Turn a crossref key into a header key
+                        (setq bibtex-reference-keys
+                              (cons (cons key t)
+                                    (delete (list key) bibtex-reference-keys))))))
+               ;; If entry has a crossref key, it goes into the list
+               ;; `bibtex-reference-keys', too.
                (if (and (nth 1 index)
                         (not (assoc (nth 1 index) bibtex-reference-keys)))
                    (push (list (nth 1 index)) bibtex-reference-keys)))))
                (if (and (nth 1 index)
                         (not (assoc (nth 1 index) bibtex-reference-keys)))
                    (push (list (nth 1 index)) bibtex-reference-keys)))))
@@ -4376,7 +4428,7 @@ If mark is active reformat entries in region, if not in whole buffer."
                            last-comma page-dashes unify-case inherit-booktitle
                            whitespace braces strings))
                 (t
                            last-comma page-dashes unify-case inherit-booktitle
                            whitespace braces strings))
                 (t
-                 (remove 'required-fields (push 'realign bibtex-entry-format)))))
+                 (cons 'realign (remove 'required-fields bibtex-entry-format)))))
          (reformat-reference-keys
           (if read-options
               (if use-previous-options
          (reformat-reference-keys
           (if read-options
               (if use-previous-options
@@ -4490,9 +4542,9 @@ An error is signaled if point is outside key or BibTeX field."
            ;; is requested.
            (let (completion-ignore-case)
              (setq choose-completion-string-functions
            ;; is requested.
            (let (completion-ignore-case)
              (setq choose-completion-string-functions
-                   (lambda (choice buffer mini-p base-size)
+                   (lambda (choice buffer base-position &rest ignored)
                      (setq choose-completion-string-functions nil)
                      (setq choose-completion-string-functions nil)
-                     (choose-completion-string choice buffer base-size)
+                     (choose-completion-string choice buffer base-position)
                      (bibtex-complete-crossref-cleanup choice)
                      t)) ; needed by choose-completion-string-functions
              (bibtex-complete-crossref-cleanup
                      (bibtex-complete-crossref-cleanup choice)
                      t)) ; needed by choose-completion-string-functions
              (bibtex-complete-crossref-cleanup
@@ -4508,9 +4560,9 @@ An error is signaled if point is outside key or BibTeX field."
            ;; string completion
            (let ((completion-ignore-case t))
              (setq choose-completion-string-functions
            ;; string completion
            (let ((completion-ignore-case t))
              (setq choose-completion-string-functions
-                   `(lambda (choice buffer mini-p base-size)
+                   `(lambda (choice buffer base-position &rest ignored)
                       (setq choose-completion-string-functions nil)
                       (setq choose-completion-string-functions nil)
-                      (choose-completion-string choice buffer base-size)
+                      (choose-completion-string choice buffer base-position)
                       (bibtex-complete-string-cleanup choice ',compl)
                       t)) ; needed by `choose-completion-string-functions'
              (bibtex-complete-string-cleanup (bibtex-complete-internal compl)
                       (bibtex-complete-string-cleanup choice ',compl)
                       t)) ; needed by `choose-completion-string-functions'
              (bibtex-complete-string-cleanup (bibtex-complete-internal compl)
@@ -4713,9 +4765,10 @@ Return the URL or nil if none can be generated."
                         (error "Match failed: %s" text)))
                     (if fmt (apply 'format fmt (nreverse obj))
                       (apply 'concat (nreverse obj)))))
                         (error "Match failed: %s" text)))
                     (if fmt (apply 'format fmt (nreverse obj))
                       (apply 'concat (nreverse obj)))))
-        (if (interactive-p) (message "%s" url))
+        (if (called-interactively-p 'interactive) (message "%s" url))
         (unless no-browse (browse-url url)))
         (unless no-browse (browse-url url)))
-      (if (and (not url) (interactive-p)) (message "No URL known."))
+      (if (and (not url) (called-interactively-p 'interactive))
+         (message "No URL known."))
       url)))
 
 \f
       url)))
 
 \f