]> code.delx.au - gnu-emacs/blobdiff - lisp/textmodes/bibtex.el
(pr-alist-custom-set, pr-ps-utility-custom-set)
[gnu-emacs] / lisp / textmodes / bibtex.el
index fd84d749b866372280a2ca8e8b6925d2fe383716..c82f2dcf3d08c161cbf28a5a8a9ad5e9b7c65c18 100644 (file)
@@ -1,7 +1,7 @@
 ;;; bibtex.el --- BibTeX mode for GNU Emacs
 
 ;; Copyright (C) 1992, 1994, 1995, 1996, 1997, 1998, 1999, 2002, 2003,
 ;;; bibtex.el --- BibTeX mode for GNU Emacs
 
 ;; Copyright (C) 1992, 1994, 1995, 1996, 1997, 1998, 1999, 2002, 2003,
-;;   2004, 2005 Free Software Foundation, Inc.
+;;   2004, 2005, 2006 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>
@@ -87,7 +87,8 @@ If this is a function, call it to generate the initial field text."
   :type '(choice (const :tag "None" nil)
                  (string :tag "Initial text")
                  (function :tag "Initialize Function" :value fun)
   :type '(choice (const :tag "None" nil)
                  (string :tag "Initial text")
                  (function :tag "Initialize Function" :value fun)
-                 (other :tag "Default" t)))
+                 (const :tag "Default" t)))
+(put 'bibtex-include-OPTkey 'risky-local-variable t)
 
 (defcustom bibtex-user-optional-fields
   '(("annote" "Personal annotation (ignored)"))
 
 (defcustom bibtex-user-optional-fields
   '(("annote" "Personal annotation (ignored)"))
@@ -102,6 +103,7 @@ CROSSREF-OPTIONAL lists in `bibtex-entry-field-alist' (which see)."
                                        (choice :tag "Init" :value ""
                                                string
                                                function))))))
                                        (choice :tag "Init" :value ""
                                                string
                                                function))))))
+(put 'bibtex-user-optional-fields 'risky-local-variable t)
 
 (defcustom bibtex-entry-format
   '(opts-or-alts required-fields numerical-fields)
 
 (defcustom bibtex-entry-format
   '(opts-or-alts required-fields numerical-fields)
@@ -151,7 +153,7 @@ narrowed to just the entry."
 (defcustom bibtex-maintain-sorted-entries nil
   "If non-nil, BibTeX mode maintains all entries in sorted order.
 Allowed non-nil values are:
 (defcustom bibtex-maintain-sorted-entries nil
   "If non-nil, BibTeX mode maintains all entries in sorted order.
 Allowed non-nil values are:
-plain        All entries are sorted alphabetically.
+plain or t   All entries are sorted alphabetically.
 crossref     All entries are sorted alphabetically unless an entry has a
              crossref field.  These crossrefed entries are placed in
              alphabetical order immediately preceding the main entry.
 crossref     All entries are sorted alphabetically unless an entry has a
              crossref field.  These crossrefed entries are placed in
              alphabetical order immediately preceding the main entry.
@@ -163,7 +165,10 @@ See also `bibtex-sort-ignore-string-entries'."
   :type '(choice (const nil)
                  (const plain)
                  (const crossref)
   :type '(choice (const nil)
                  (const plain)
                  (const crossref)
-                 (const entry-class)))
+                 (const entry-class)
+                 (const t)))
+(put 'bibtex-maintain-sorted-entries 'safe-local-variable
+     '(lambda (a) (memq a '(nil t plain crossref entry-class))))
 
 (defcustom bibtex-sort-entry-class
   '(("String")
 
 (defcustom bibtex-sort-entry-class
   '(("String")
@@ -178,6 +183,17 @@ to all entries not explicitly mentioned."
   :type '(repeat (choice :tag "Class"
                          (const :tag "catch-all" (catch-all))
                          (repeat :tag "Entry name" string))))
   :type '(repeat (choice :tag "Class"
                          (const :tag "catch-all" (catch-all))
                          (repeat :tag "Entry name" string))))
+(put 'bibtex-sort-entry-class 'safe-local-variable
+     (lambda (x) (let ((OK t))
+              (while (consp x)
+                (let ((y (pop x)))
+                  (while (consp y)
+                    (let ((z (pop y)))
+                      (unless (or (stringp z) (eq z 'catch-all))
+                        (setq OK nil))))
+                  (unless (null y) (setq OK nil))))
+              (unless (null x) (setq OK nil))
+              OK)))
 
 (defcustom bibtex-sort-ignore-string-entries t
   "If non-nil, BibTeX @String entries are not sort-significant.
 
 (defcustom bibtex-sort-ignore-string-entries t
   "If non-nil, BibTeX @String entries are not sort-significant.
@@ -437,6 +453,7 @@ field or a function, which is called to determine the initial content
 of the field, and ALTERNATIVE-FLAG (either nil or t) marks if the
 field is an alternative.  ALTERNATIVE-FLAG may be t only in the
 REQUIRED or CROSSREF-REQUIRED lists.")
 of the field, and ALTERNATIVE-FLAG (either nil or t) marks if the
 field is an alternative.  ALTERNATIVE-FLAG may be t only in the
 REQUIRED or CROSSREF-REQUIRED lists.")
+(put 'bibtex-entry-field-alist 'risky-local-variable t)
 
 (defcustom bibtex-comment-start "@Comment"
   "String starting a BibTeX comment."
 
 (defcustom bibtex-comment-start "@Comment"
   "String starting a BibTeX comment."
@@ -595,7 +612,7 @@ See `bibtex-generate-autokey' for details."
   :type '(repeat (cons (regexp :tag "Old")
                        (string :tag "New"))))
 
   :type '(repeat (cons (regexp :tag "Old")
                        (string :tag "New"))))
 
-(defcustom bibtex-autokey-name-case-convert 'downcase
+(defcustom bibtex-autokey-name-case-convert-function 'downcase
   "Function called for each name to perform case conversion.
 See `bibtex-generate-autokey' for details."
   :group 'bibtex-autokey
   "Function called for each name to perform case conversion.
 See `bibtex-generate-autokey' for details."
   :group 'bibtex-autokey
@@ -604,6 +621,10 @@ See `bibtex-generate-autokey' for details."
                  (const :tag "Capitalize" capitalize)
                  (const :tag "Upcase" upcase)
                  (function :tag "Conversion function")))
                  (const :tag "Capitalize" capitalize)
                  (const :tag "Upcase" upcase)
                  (function :tag "Conversion function")))
+(put 'bibtex-autokey-name-case-convert-function 'safe-local-variable
+     (lambda (x) (memq x '(upcase downcase capitalize identity))))
+(defvaralias 'bibtex-autokey-name-case-convert
+  'bibtex-autokey-name-case-convert-function)
 
 (defcustom bibtex-autokey-name-length 'infty
   "Number of characters from name to incorporate into key.
 
 (defcustom bibtex-autokey-name-length 'infty
   "Number of characters from name to incorporate into key.
@@ -666,7 +687,7 @@ See `bibtex-generate-autokey' for details."
   :group 'bibtex-autokey
   :type '(repeat regexp))
 
   :group 'bibtex-autokey
   :type '(repeat regexp))
 
-(defcustom bibtex-autokey-titleword-case-convert 'downcase
+(defcustom bibtex-autokey-titleword-case-convert-function 'downcase
   "Function called for each titleword to perform case conversion.
 See `bibtex-generate-autokey' for details."
   :group 'bibtex-autokey
   "Function called for each titleword to perform case conversion.
 See `bibtex-generate-autokey' for details."
   :group 'bibtex-autokey
@@ -675,6 +696,8 @@ See `bibtex-generate-autokey' for details."
                  (const :tag "Capitalize" capitalize)
                  (const :tag "Upcase" upcase)
                  (function :tag "Conversion function")))
                  (const :tag "Capitalize" capitalize)
                  (const :tag "Upcase" upcase)
                  (function :tag "Conversion function")))
+(defvaralias 'bibtex-autokey-titleword-case-convert
+  'bibtex-autokey-titleword-case-convert-function)
 
 (defcustom bibtex-autokey-titleword-abbrevs nil
   "Determines exceptions to the usual abbreviation mechanism.
 
 (defcustom bibtex-autokey-titleword-abbrevs nil
   "Determines exceptions to the usual abbreviation mechanism.
@@ -847,6 +870,7 @@ The following is a complex example, see http://link.aps.org/linkfaq.html.
                                (choice (string :tag "Replacement")
                                       (integer :tag "Sub-match")
                                       (function :tag "Filter"))))))))
                                (choice (string :tag "Replacement")
                                       (integer :tag "Sub-match")
                                       (function :tag "Filter"))))))))
+(put 'bibtex-generate-url-list 'risky-local-variable t)
 
 (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."
@@ -1049,6 +1073,7 @@ At most `bibtex-entry-kill-ring-max' items are kept here.")
   "Completion table for BibTeX string keys.
 Initialized from `bibtex-predefined-strings' and `bibtex-string-files'.")
 (make-variable-buffer-local 'bibtex-strings)
   "Completion table for BibTeX string keys.
 Initialized from `bibtex-predefined-strings' and `bibtex-string-files'.")
 (make-variable-buffer-local 'bibtex-strings)
+(put 'bibtex-strings 'risky-local-variable t)
 
 (defvar bibtex-reference-keys
   (lazy-completion-table bibtex-reference-keys
 
 (defvar bibtex-reference-keys
   (lazy-completion-table bibtex-reference-keys
@@ -1056,6 +1081,7 @@ Initialized from `bibtex-predefined-strings' and `bibtex-string-files'.")
   "Completion table for BibTeX reference keys.
 The CDRs of the elements are t for header keys and nil for crossref keys.")
 (make-variable-buffer-local 'bibtex-reference-keys)
   "Completion table for BibTeX reference keys.
 The CDRs of the elements are t for header keys and nil for crossref keys.")
 (make-variable-buffer-local 'bibtex-reference-keys)
+(put 'bibtex-reference-keys 'risky-local-variable t)
 
 (defvar bibtex-buffer-last-parsed-tick nil
   "Value of `buffer-modified-tick' last time buffer was parsed for keys.")
 
 (defvar bibtex-buffer-last-parsed-tick nil
   "Value of `buffer-modified-tick' last time buffer was parsed for keys.")
@@ -1162,7 +1188,7 @@ The CDRs of the elements are t for header keys and nil for crossref keys.")
     (,(concat "^[ \t]*\\(" bibtex-field-name "\\)[ \t]*=")
      1 font-lock-variable-name-face)
     ;; url
     (,(concat "^[ \t]*\\(" bibtex-field-name "\\)[ \t]*=")
      1 font-lock-variable-name-face)
     ;; url
-    bibtex-font-lock-url bibtex-font-lock-crossref)
+    (bibtex-font-lock-url) (bibtex-font-lock-crossref))
   "*Default expressions to highlight in BibTeX mode.")
 
 (defvar bibtex-font-lock-url-regexp
   "*Default expressions to highlight in BibTeX mode.")
 
 (defvar bibtex-font-lock-url-regexp
@@ -1175,13 +1201,7 @@ The CDRs of the elements are t for header keys and nil for crossref keys.")
 (defvar bibtex-string-empty-key nil
   "If non-nil, `bibtex-parse-string' accepts empty key.")
 
 (defvar bibtex-string-empty-key nil
   "If non-nil, `bibtex-parse-string' accepts empty key.")
 
-(defvar bibtex-sort-entry-class-alist
-  (let ((i -1) alist)
-    (dolist (class bibtex-sort-entry-class alist)
-      (setq i (1+ i))
-      (dolist (entry class)
-        ;; all entry names should be downcase (for ease of comparison)
-        (push (cons (if (stringp entry) (downcase entry) entry) i) alist))))
+(defvar bibtex-sort-entry-class-alist nil
   "Alist mapping entry types to their sorting index.
 Auto-generated from `bibtex-sort-entry-class'.
 Used when `bibtex-maintain-sorted-entries' is `entry-class'.")
   "Alist mapping entry types to their sorting index.
 Auto-generated from `bibtex-sort-entry-class'.
 Used when `bibtex-maintain-sorted-entries' is `entry-class'.")
@@ -1790,7 +1810,8 @@ Formats current entry according to variable `bibtex-entry-format'."
 
         ;; identify entry type
         (goto-char (point-min))
 
         ;; identify entry type
         (goto-char (point-min))
-        (re-search-forward bibtex-entry-type)
+        (or (re-search-forward bibtex-entry-type nil t)
+            (error "Not inside a BibTeX entry"))
         (let ((beg-type (1+ (match-beginning 0)))
               (end-type (match-end 0)))
           (setq entry-list (assoc-string (buffer-substring-no-properties
         (let ((beg-type (1+ (match-beginning 0)))
               (end-type (match-end 0)))
           (setq entry-list (assoc-string (buffer-substring-no-properties
@@ -2083,7 +2104,7 @@ and `bibtex-autokey-names-stretch'."
                       ;; --> take the last token
                       (match-string 1 fullname))
                      (t (error "Name `%s' is incorrectly formed" fullname)))))
                       ;; --> take the last token
                       (match-string 1 fullname))
                      (t (error "Name `%s' is incorrectly formed" fullname)))))
-    (funcall bibtex-autokey-name-case-convert
+    (funcall bibtex-autokey-name-case-convert-function
              (bibtex-autokey-abbrev name bibtex-autokey-name-length))))
 
 (defun bibtex-autokey-get-year ()
              (bibtex-autokey-abbrev name bibtex-autokey-name-length))))
 
 (defun bibtex-autokey-get-year ()
@@ -2145,7 +2166,7 @@ and `bibtex-autokey-titleword-length'."
       (setq alist (cdr alist)))
     (if alist
         (cdar alist)
       (setq alist (cdr alist)))
     (if alist
         (cdar alist)
-      (funcall bibtex-autokey-titleword-case-convert
+      (funcall bibtex-autokey-titleword-case-convert-function
                (bibtex-autokey-abbrev titleword bibtex-autokey-titleword-length)))))
 
 (defun bibtex-generate-autokey ()
                (bibtex-autokey-abbrev titleword bibtex-autokey-titleword-length)))))
 
 (defun bibtex-generate-autokey ()
@@ -2165,7 +2186,7 @@ The name part:
     take at least `bibtex-autokey-name-length' characters (truncate only
     after a consonant or at a word end).
  5. Convert all last names using the function
     take at least `bibtex-autokey-name-length' characters (truncate only
     after a consonant or at a word end).
  5. Convert all last names using the function
-    `bibtex-autokey-name-case-convert'.
+    `bibtex-autokey-name-case-convert-function'.
  6. Build the name part of the key by concatenating all abbreviated last
     names with the string `bibtex-autokey-name-separator' between any two.
     If there are more names in the name field than names used in the name
  6. Build the name part of the key by concatenating all abbreviated last
     names with the string `bibtex-autokey-name-separator' between any two.
     If there are more names in the name field than names used in the name
@@ -2197,7 +2218,7 @@ The title part
     `bibtex-autokey-titleword-length' characters (truncate only after
     a consonant or at a word end).
  5. Convert all title words using the function
     `bibtex-autokey-titleword-length' characters (truncate only after
     a consonant or at a word end).
  5. Convert all title words using the function
-    `bibtex-autokey-titleword-case-convert'.
+    `bibtex-autokey-titleword-case-convert-function'.
  6. Build the title part by concatenating all abbreviated title words with
     the string `bibtex-autokey-titleword-separator' between any two.
 
  6. Build the title part by concatenating all abbreviated title words with
     the string `bibtex-autokey-titleword-separator' between any two.
 
@@ -2470,6 +2491,7 @@ already set."
   "Complete word fragment before point to longest prefix of COMPLETIONS.
 COMPLETIONS is an alist of strings.  If point is not after the part
 of a word, all strings are listed.  Return completion."
   "Complete word fragment before point to longest prefix of COMPLETIONS.
 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.
   (let* ((case-fold-search t)
          (beg (save-excursion
                 (re-search-backward "[ \t{\"]")
   (let* ((case-fold-search t)
          (beg (save-excursion
                 (re-search-backward "[ \t{\"]")
@@ -2492,7 +2514,6 @@ of a word, all strings are listed.  Return completion."
              (display-completion-list (all-completions part-of-word completions)
                                      part-of-word))
            (message "Making completion list...done")
              (display-completion-list (all-completions part-of-word completions)
                                      part-of-word))
            (message "Making completion list...done")
-           ;; return value is handled by choose-completion-string-functions
            nil))))
 
 (defun bibtex-complete-string-cleanup (str compl)
            nil))))
 
 (defun bibtex-complete-string-cleanup (str compl)
@@ -2529,7 +2550,7 @@ Use `bibtex-summary-function' to generate summary."
 Used as default value of `bibtex-summary-function'."
   ;; It would be neat to customize this function.  How?
   (if (looking-at bibtex-entry-maybe-empty-head)
 Used as default value of `bibtex-summary-function'."
   ;; It would be neat to customize this function.  How?
   (if (looking-at bibtex-entry-maybe-empty-head)
-      (let* ((bibtex-autokey-name-case-convert 'identity)
+      (let* ((bibtex-autokey-name-case-convert-function 'identity)
              (bibtex-autokey-name-length 'infty)
              (bibtex-autokey-names 1)
              (bibtex-autokey-names-stretch 0)
              (bibtex-autokey-name-length 'infty)
              (bibtex-autokey-names 1)
              (bibtex-autokey-names-stretch 0)
@@ -2540,7 +2561,7 @@ Used as default value of `bibtex-summary-function'."
              (year (bibtex-autokey-get-year))
              (bibtex-autokey-titlewords 5)
              (bibtex-autokey-titlewords-stretch 2)
              (year (bibtex-autokey-get-year))
              (bibtex-autokey-titlewords 5)
              (bibtex-autokey-titlewords-stretch 2)
-             (bibtex-autokey-titleword-case-convert 'identity)
+             (bibtex-autokey-titleword-case-convert-function 'identity)
              (bibtex-autokey-titleword-length 5)
              (bibtex-autokey-titleword-separator " ")
              (title (bibtex-autokey-get-title))
              (bibtex-autokey-titleword-length 5)
              (bibtex-autokey-titleword-separator " ")
              (title (bibtex-autokey-get-title))
@@ -2847,25 +2868,45 @@ is non-nil."
        (bibtex-autofill-entry))
     (run-hooks 'bibtex-add-entry-hook)))
 
        (bibtex-autofill-entry))
     (run-hooks 'bibtex-add-entry-hook)))
 
-(defun bibtex-entry-update ()
+(defun bibtex-entry-update (&optional entry-type)
   "Update an existing BibTeX entry.
 In the BibTeX entry at point, make new fields for those items that may occur
   "Update an existing BibTeX entry.
 In the BibTeX entry at point, make new fields for those items that may occur
-according to `bibtex-field-list', but are not yet present."
-  (interactive)
+according to `bibtex-field-list', but are not yet present.
+Also, add field delimiters to numerical fields if they are not present.
+If ENTRY-TYPE is non-nil, change first the entry type to ENTRY-TYPE.
+When called interactively with a prefix arg, query for a value of ENTRY-TYPE."
+  (interactive
+   (list (if current-prefix-arg
+             (let ((completion-ignore-case t))
+               (completing-read "New entry type: " bibtex-entry-field-alist
+                                nil t nil 'bibtex-entry-type-history)))))
   (save-excursion
     (bibtex-beginning-of-entry)
   (save-excursion
     (bibtex-beginning-of-entry)
-    ;; For inserting new fields, we use the fact that
-    ;; `bibtex-parse-entry' moves point to the end of the last field.
-    (let* ((fields-alist (bibtex-parse-entry))
-           (field-list (bibtex-field-list
-                        (cdr (assoc "=type=" fields-alist)))))
-      (skip-chars-backward " \t\n")
-      (dolist (field (car field-list))
-        (unless (assoc-string (car field) fields-alist t)
-          (bibtex-make-field field)))
-      (dolist (field (cdr field-list))
-        (unless (assoc-string (car field) fields-alist t)
-          (bibtex-make-optional-field field))))))
+    (when (looking-at bibtex-entry-maybe-empty-head)
+      (goto-char (match-end 0))
+      (if entry-type
+          (save-excursion
+            (replace-match (concat "@" entry-type) nil nil nil 1))
+        (setq entry-type (bibtex-type-in-head)))
+      (let* ((field-list (bibtex-field-list entry-type))
+             (required (copy-tree (car field-list)))
+             (optional (copy-tree (cdr field-list)))
+             bounds)
+        (while (setq bounds (bibtex-parse-field))
+          (let ((fname (bibtex-name-in-field bounds t))
+                (end (copy-marker (bibtex-end-of-field bounds) t)))
+            (setq required (delete (assoc-string fname required t) required)
+                  optional (delete (assoc-string fname optional t) optional))
+            (when (string-match "\\`[0-9]+\\'"
+                                (bibtex-text-in-field-bounds bounds))
+              (goto-char (bibtex-end-of-text-in-field bounds))
+              (insert (bibtex-field-right-delimiter))
+              (goto-char (bibtex-start-of-text-in-field bounds))
+              (insert (bibtex-field-left-delimiter)))
+            (goto-char end)))
+        (skip-chars-backward " \t\n")
+        (dolist (field required) (bibtex-make-field field))
+        (dolist (field optional) (bibtex-make-optional-field field))))))
 
 (defun bibtex-parse-entry (&optional content)
   "Parse entry at point, return an alist.
 
 (defun bibtex-parse-entry (&optional content)
   "Parse entry at point, return an alist.
@@ -3154,6 +3195,17 @@ of the head of the entry found.  Return nil if no entry found."
                       entry-name))
             (list key nil entry-name))))))
 
                       entry-name))
             (list key nil entry-name))))))
 
+(defun bibtex-init-sort-entry-class-alist ()
+  (unless (local-variable-p 'bibtex-sort-entry-class-alist)
+    (set (make-local-variable 'bibtex-sort-entry-class-alist)
+         (let ((i -1) alist)
+           (dolist (class bibtex-sort-entry-class alist)
+             (setq i (1+ i))
+             (dolist (entry class)
+               ;; All entry names should be downcase (for ease of comparison).
+               (push (cons (if (stringp entry) (downcase entry) entry) i)
+                     alist)))))))
+
 (defun bibtex-lessp (index1 index2)
   "Predicate for sorting BibTeX entries with indices INDEX1 and INDEX2.
 Each index is a list (KEY CROSSREF-KEY ENTRY-NAME).
 (defun bibtex-lessp (index1 index2)
   "Predicate for sorting BibTeX entries with indices INDEX1 and INDEX2.
 Each index is a list (KEY CROSSREF-KEY ENTRY-NAME).
@@ -3191,13 +3243,14 @@ If its value is nil use plain sorting.  Text outside of BibTeX entries is not
 affected.  If `bibtex-sort-ignore-string-entries' is non-nil, @String entries
 are ignored."
   (interactive)
 affected.  If `bibtex-sort-ignore-string-entries' is non-nil, @String entries
 are ignored."
   (interactive)
-    (bibtex-beginning-of-first-entry) ;; needed by `sort-subr'
-    (sort-subr nil
-               'bibtex-skip-to-valid-entry ; NEXTREC function
-               'bibtex-end-of-entry        ; ENDREC function
-               'bibtex-entry-index         ; STARTKEY function
-               nil                         ; ENDKEY function
-               'bibtex-lessp))             ; PREDICATE
+  (bibtex-beginning-of-first-entry)     ; Needed by `sort-subr'
+  (bibtex-init-sort-entry-class-alist)  ; Needed by `bibtex-lessp'.
+  (sort-subr nil
+             'bibtex-skip-to-valid-entry   ; NEXTREC function
+             'bibtex-end-of-entry          ; ENDREC function
+             'bibtex-entry-index           ; STARTKEY function
+             nil                           ; ENDKEY function
+             'bibtex-lessp))               ; PREDICATE
 
 (defun bibtex-find-crossref (crossref-key &optional pnt split)
   "Move point to the beginning of BibTeX entry CROSSREF-KEY.
 
 (defun bibtex-find-crossref (crossref-key &optional pnt split)
   "Move point to the beginning of BibTeX entry CROSSREF-KEY.
@@ -3274,7 +3327,7 @@ Otherwise, use `set-buffer'.  DISPLAY is t when called interactively."
               (display (message "Key `%s' not found" key)))
         found)
 
               (display (message "Key `%s' not found" key)))
         found)
 
-    (let* (case-fold-search
+    (let* ((case-fold-search t)
            (pnt (save-excursion
                   (goto-char (or start (point-min)))
                   (if (re-search-forward (concat "^[ \t]*\\("
            (pnt (save-excursion
                   (goto-char (or start (point-min)))
                   (if (re-search-forward (concat "^[ \t]*\\("
@@ -3298,6 +3351,7 @@ If `bibtex-maintain-sorted-entries' is non-nil, perform a binary
 search to look for place for KEY.  This requires that buffer is sorted,
 see `bibtex-validate'.
 Return t if preparation was successful or nil if entry KEY already exists."
 search to look for place for KEY.  This requires that buffer is sorted,
 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'.
   (let ((key (nth 0 index))
         key-exist)
     (cond ((or (null key)
   (let ((key (nth 0 index))
         key-exist)
     (cond ((or (null key)
@@ -3538,7 +3592,7 @@ Return t if test was successful, nil otherwise."
           (dolist (key (cdr (assq buffer buffer-key-list)))
             (when (assoc-string key current-keys)
               (bibtex-find-entry key)
           (dolist (key (cdr (assq buffer buffer-key-list)))
             (when (assoc-string key current-keys)
               (bibtex-find-entry key)
-              (push (format "%s:%d: Duplicat key `%s' in %s\n"
+              (push (format "%s:%d: Duplicate key `%s' in %s\n"
                             (buffer-file-name) (bibtex-current-line) key
                             (abbreviate-file-name (buffer-file-name buffer)))
                     error-list))))))
                             (buffer-file-name) (bibtex-current-line) key
                             (abbreviate-file-name (buffer-file-name buffer)))
                     error-list))))))
@@ -3604,17 +3658,20 @@ interactive calls."
 
 (defun bibtex-find-text-internal (&optional noerror subfield comma)
   "Find text part of current BibTeX field or entry head.
 
 (defun bibtex-find-text-internal (&optional noerror subfield comma)
   "Find text part of current BibTeX field or entry head.
-Return list (NAME START-TEXT END-TEXT END) with field or entry name,
-start and end of text and end of field or entry head, or nil if not found.
-If optional arg NOERROR is non-nil, an error message is suppressed if text
-is not found.  If optional arg SUBFIELD is non-nil START-TEXT and END-TEXT
-correspond to the current subfield delimited by #.
+Return list (NAME START-TEXT END-TEXT END STRING-CONST) with field
+or entry name, start and end of text, and end of field or entry head.
+STRING-CONST is a flag which is non-nil if current subfield delimited by #
+is a BibTeX string constant.  Return value is nil if field or entry head
+are not found.
+If optional arg NOERROR is non-nil, an error message is suppressed
+if text is not found.  If optional arg SUBFIELD is non-nil START-TEXT
+and END-TEXT correspond to the current subfield delimited by #.
 Optional arg COMMA is as in `bibtex-enclosing-field'."
   (save-excursion
     (let ((pnt (point))
           (bounds (bibtex-enclosing-field comma t))
           (case-fold-search t)
 Optional arg COMMA is as in `bibtex-enclosing-field'."
   (save-excursion
     (let ((pnt (point))
           (bounds (bibtex-enclosing-field comma t))
           (case-fold-search t)
-          name start-text end-text end failure done no-sub)
+          name start-text end-text end failure done no-sub string-const)
       (bibtex-beginning-of-entry)
       (cond (bounds
              (setq name (bibtex-name-in-field bounds t)
       (bibtex-beginning-of-entry)
       (cond (bounds
              (setq name (bibtex-name-in-field bounds t)
@@ -3658,9 +3715,11 @@ Optional arg COMMA is as in `bibtex-enclosing-field'."
           (goto-char start-text)
           (while (not done)
             (if (or (prog1 (looking-at bibtex-field-const)
           (goto-char start-text)
           (while (not done)
             (if (or (prog1 (looking-at bibtex-field-const)
-                      (setq end-text (match-end 0)))
+                      (setq end-text (match-end 0)
+                            string-const t))
                     (prog1 (setq bounds (bibtex-parse-field-string))
                     (prog1 (setq bounds (bibtex-parse-field-string))
-                      (setq end-text (cdr bounds))))
+                      (setq end-text (cdr bounds)
+                            string-const nil)))
                 (progn
                   (if (and (<= start-text pnt) (<= pnt end-text))
                       (setq done t)
                 (progn
                   (if (and (<= start-text pnt) (<= pnt end-text))
                       (setq done t)
@@ -3669,7 +3728,7 @@ Optional arg COMMA is as in `bibtex-enclosing-field'."
                       (setq start-text (goto-char (match-end 0)))))
               (setq done t failure t)))))
       (cond ((not failure)
                       (setq start-text (goto-char (match-end 0)))))
               (setq done t failure t)))))
       (cond ((not failure)
-             (list name start-text end-text end))
+             (list name start-text end-text end string-const))
             ((and no-sub (not noerror))
              (error "Not on text part of BibTeX field"))
             ((not noerror) (error "Not on BibTeX field"))))))
             ((and no-sub (not noerror))
              (error "Not on text part of BibTeX field"))
             ((not noerror) (error "Not on BibTeX field"))))))
@@ -3703,13 +3762,10 @@ is as in `bibtex-enclosing-field'.  It is t for interactive calls."
 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))
-  (let* ((bounds (bibtex-find-text-internal nil t comma))
-         (start (nth 1 bounds))
-         (end (nth 2 bounds)))
-    (if (memq (char-before end) '(?\} ?\"))
-        (delete-region (1- end) end))
-    (if (memq (char-after start) '(?\{ ?\"))
-        (delete-region start (1+ start)))))
+  (let ((bounds (bibtex-find-text-internal nil t comma)))
+    (unless (nth 4 bounds)
+      (delete-region (1- (nth 2 bounds)) (nth 2 bounds))
+      (delete-region (nth 1 bounds) (1+ (nth 1 bounds))))))
 
 (defun bibtex-kill-field (&optional copy-only comma)
   "Kill the entire enclosing BibTeX field.
 
 (defun bibtex-kill-field (&optional copy-only comma)
   "Kill the entire enclosing BibTeX field.
@@ -3844,7 +3900,8 @@ At end of the cleaning process, the functions in
   (interactive "P")
   (let ((case-fold-search t)
         (start (bibtex-beginning-of-entry))
   (interactive "P")
   (let ((case-fold-search t)
         (start (bibtex-beginning-of-entry))
-        (_ (looking-at bibtex-any-entry-maybe-empty-head))
+        (_ (or (looking-at bibtex-any-entry-maybe-empty-head)
+              (error "Not inside a BibTeX entry")))
         (entry-type (bibtex-type-in-head))
         (key (bibtex-key-in-head)))
     ;; formatting
         (entry-type (bibtex-type-in-head))
         (key (bibtex-key-in-head)))
     ;; formatting
@@ -4152,24 +4209,31 @@ An error is signaled if point is outside key or BibTeX field."
 
     (cond ((eq compl 'key)
            ;; key completion: no cleanup needed
 
     (cond ((eq compl 'key)
            ;; key completion: no cleanup needed
+           (setq choose-completion-string-functions nil)
            (let (completion-ignore-case)
              (bibtex-complete-internal (bibtex-global-key-alist))))
 
           ((eq compl 'crossref-key)
            ;; crossref key completion
            (let (completion-ignore-case)
              (bibtex-complete-internal (bibtex-global-key-alist))))
 
           ((eq compl 'crossref-key)
            ;; crossref key completion
+           ;;
+           ;; If we quit the *Completions* buffer without requesting
+           ;; a completion, `choose-completion-string-functions' is still
+           ;; non-nil. Therefore, `choose-completion-string-functions' is
+           ;; always set (either to non-nil or nil) when a new completion
+           ;; is requested.
            (let (completion-ignore-case)
              (setq choose-completion-string-functions
                    (lambda (choice buffer mini-p base-size)
            (let (completion-ignore-case)
              (setq choose-completion-string-functions
                    (lambda (choice buffer mini-p base-size)
-                     (let ((choose-completion-string-functions nil))
-                       (choose-completion-string choice buffer base-size))
+                     (setq choose-completion-string-functions nil)
+                     (choose-completion-string choice buffer base-size)
                      (bibtex-complete-crossref-cleanup choice)
                      (bibtex-complete-crossref-cleanup choice)
-                     ;; return t (needed by choose-completion-string-functions)
-                     t))
-             (bibtex-complete-crossref-cleanup (bibtex-complete-internal
-                                                (bibtex-global-key-alist)))))
+                     t)) ; needed by choose-completion-string-functions
+             (bibtex-complete-crossref-cleanup
+              (bibtex-complete-internal (bibtex-global-key-alist)))))
 
           ((eq compl 'string)
            ;; string key completion: no cleanup needed
 
           ((eq compl 'string)
            ;; string key completion: no cleanup needed
+           (setq choose-completion-string-functions nil)
            (let ((completion-ignore-case t))
              (bibtex-complete-internal bibtex-strings)))
 
            (let ((completion-ignore-case t))
              (bibtex-complete-internal bibtex-strings)))
 
@@ -4178,15 +4242,15 @@ An error is signaled if point is outside key or BibTeX field."
            (let ((completion-ignore-case t))
              (setq choose-completion-string-functions
                    `(lambda (choice buffer mini-p base-size)
            (let ((completion-ignore-case t))
              (setq choose-completion-string-functions
                    `(lambda (choice buffer mini-p base-size)
-                      (let ((choose-completion-string-functions nil))
-                        (choose-completion-string choice buffer base-size))
+                      (setq choose-completion-string-functions nil)
+                      (choose-completion-string choice buffer base-size)
                       (bibtex-complete-string-cleanup choice ',compl)
                       (bibtex-complete-string-cleanup choice ',compl)
-                      ;; return t (needed by choose-completion-string-functions)
-                      t))
+                      t)) ; needed by choose-completion-string-functions
              (bibtex-complete-string-cleanup (bibtex-complete-internal compl)
                                              compl)))
 
              (bibtex-complete-string-cleanup (bibtex-complete-internal compl)
                                              compl)))
 
-          (t (error "Point outside key or BibTeX field")))))
+          (t (setq choose-completion-string-functions nil)
+             (error "Point outside key or BibTeX field")))))
 
 (defun bibtex-Article ()
   "Insert a new BibTeX @Article entry; see also `bibtex-entry'."
 
 (defun bibtex-Article ()
   "Insert a new BibTeX @Article entry; see also `bibtex-entry'."
@@ -4292,11 +4356,12 @@ An error is signaled if point is outside key or BibTeX field."
             "\n")
     (goto-char endpos)))
 
             "\n")
     (goto-char endpos)))
 
-(defun bibtex-url (&optional pos)
+(defun bibtex-url (&optional pos no-browse)
   "Browse a URL for the BibTeX entry at point.
 Optional POS is the location of the BibTeX entry.
 The URL is generated using the schemes defined in `bibtex-generate-url-list'
   "Browse a URL for the BibTeX entry at point.
 Optional POS is the location of the BibTeX entry.
 The URL is generated using the schemes defined in `bibtex-generate-url-list'
-\(see there\).  Then the URL is passed to `browse-url'."
+\(see there\).  Then the URL is passed to `browse-url' unless NO-BROWSE is nil.
+Return the URL or nil if none can be generated."
   (interactive)
   (save-excursion
     (if pos (goto-char pos))
   (interactive)
   (save-excursion
     (if pos (goto-char pos))
@@ -4319,20 +4384,22 @@ The URL is generated using the schemes defined in `bibtex-generate-url-list'
                       (dolist (step scheme)
                         (setq field (cdr (assoc-string (car step) fields-alist t)))
                         (if (string-match (nth 1 step) field)
                       (dolist (step scheme)
                         (setq field (cdr (assoc-string (car step) fields-alist t)))
                         (if (string-match (nth 1 step) field)
-                            (setq field (cond ((functionp (nth 2 step))
-                                               (funcall (nth 2 step) field))
-                                              ((numberp (nth 2 step))
-                                               (match-string (nth 2 step) field))
-                                              (t
-                                               (replace-match (nth 2 step) t nil field))))
+                            (push (cond ((functionp (nth 2 step))
+                                         (funcall (nth 2 step) field))
+                                        ((numberp (nth 2 step))
+                                         (match-string (nth 2 step) field))
+                                        (t
+                                         (replace-match (nth 2 step) t nil field)))
+                                  obj)
                           ;; If the scheme is set up correctly,
                           ;; we should never reach this point
                           ;; If the scheme is set up correctly,
                           ;; we should never reach this point
-                          (error "Match failed: %s" field))
-                        (push field obj))
+                          (error "Match failed: %s" field)))
                       (if fmt (apply 'format fmt (nreverse obj))
                         (apply 'concat (nreverse obj)))))
                       (if fmt (apply 'format fmt (nreverse obj))
                         (apply 'concat (nreverse obj)))))
-          (browse-url (message "%s" url))))
-      (unless url (message "No URL known.")))))
+          (if (interactive-p) (message "%s" url))
+          (unless no-browse (browse-url url))))
+      (if (and (not url) (interactive-p)) (message "No URL known."))
+      url)))
 
 \f
 ;; Make BibTeX a Feature
 
 \f
 ;; Make BibTeX a Feature