;;; bibtex.el --- BibTeX mode for GNU Emacs
-;; Copyright (C) 1992 Free Software Foundation, Inc.
+;; Copyright (C) 1992, 1994, 1995, 1996, 1997 Free Software Foundation, Inc.
-;; Author: Bengt Martensson <ubrinf!mond!bengt>
+;; Author: Stefan Schoef <schoef@offis.uni-oldenburg.de>
+;; Bengt Martensson <bengt@mathematik.uni-Bremen.de>
;; Mark Shapiro <shapiro@corto.inria.fr>
;; Mike Newton <newton@gumby.cs.caltech.edu>
;; Aaron Larson <alarson@src.honeywell.com>
-;; Version: 1.3.1
-;; Maintainer:Aaron Larson <alarson@src.honeywell.com>
-;; Adapted-By: ESR
-;; Keywords: tex, bib
+;; Maintainer: Dirk Herrmann <D.Herrmann@tu-bs.de>
+;; Keywords: BibTeX, LaTeX, TeX
;; This file is part of GNU Emacs.
;; 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, 675 Mass Ave, Cambridge, MA 02139, USA.
-
-;;; TODO distribute texinfo file.
-
-;;; LCD Archive Entry:
-;;; bibtex-mode|Bengt Martensson, Marc Shapiro, Aaron Larson|
-;;; alarson@src.honeywell.com|
-;;; Support for maintaining BibTeX format bibliography databases|
-;;; 93-03-29|version 1.3|~/modes/bibtex-mode.el.Z|
+;; 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:
-;;; BUGS:
-;;; 1. using regular expressions to match the entire bibtex entry dies
-;;; on long bibtex entires (e.g. those containing abstracts) since
-;;; the length of regular expression matches is fairly limited.
-;;; 2. When inserting a string (with \C-C\C-E\s) hitting a TAB results
-;;; in the error message "Can't find enclosing Bibtex field" instead
-;;; of moving to the empty string. [reported by gernot@cs.unsw.oz.au]
-;;; 3. Function string-equalp should be in a library file, not in this
-;;; file.
-
-;;; (current keeper: alarson@src.honeywell.com
-;;; previous: shapiro@corto.inria.fr)
-
-;;; Change Log:
-
-;; Mon Mar 29 14:06:06 1993 Aaron Larson (alarson at gendibal)
-;;
-;; * bibtex.el: V1.3 released Mar 30, 1993
-;; (bibtex-field-name): Fix to match definition if latex manual,
-;; specifically letters, digits, and punctuation other than comma.
-;; Underscore is retained for historical reasons.
-;; (bibtex-make-field): Fix to work around bug in Lucid prin1-to-string
-;; function as reported by Martin Sjolin <marsj@ida.liu.se>.
-;; (bibtex-entry): minor code cleanup.
-;; (bibtex-mode-map): Remove key binding (C-c n) for
-;; narrow-to-bibtex-entry, previous binding violated emacs policy of
-;; reserving C-c followed by a letter for user customization.
-;; revise modification history to better conform to FSF changelog
-;; standards.
-;; (bibtex-refile-entry): Removed. Would need disclaimer papers to
-;; incorporate it into official sources, and unable to contact author.
-;; Fix minor "syntax" errors in documentation strings and such found
-;; by new byte compiler. Funs bibtex-mode, bibtex-remove-double-quotes
-;;
-;;
-;; Fri Jan 15 14:06:06 1993 Aaron Larson (alarson at gendibal)
-;;
-;; * bibtex.el: V1.2 released Feb 15 1993
-;; (find-bibtex-entry-location bibtex-make-field): Fixed placement of
-;; "interactive specification". [Bug report from
-;; mernst@theory.lcs.mit.edu]
-;; Fixed problem where bibtex-entry would fail if user typed entry
-;; name in wrong case.
-;; (bibtex-inside-field) Position the cursor _before_ the last comma
-;; on a line (the comma is not necessarily "inside" the field); this
-;; does not seem to break any existing code. ref sct@dcs.edinburgh.ac.uk
-;; (bibtex-enclosing-field, bibtex-enclosing-reference): leave
-;; point unmoved if no enclosing field/reference is found. As a
-;; result of changes (3) and (4) bibtex-next-field works properly,
-;; even when called from the entry key position.
-;; (bibtex-remove-OPT): realign the '=' after removing the 'opt'.
-;; (bibtex-clean-entry): always remove any trailing comma from the
-;; end of a bibtex entry (these commas get stripped automatically when
-;; optional fields are killed by bibtex-kill-optional-field, but can be
-;; left if optional fields are removed by other means).
-;; (bibtex-x-help) Replace tab with spaces in X menu as noted by
-;; khera@cs.duke.edu
-;; (bibtex-refile-entry): Added (from brannon@jove.cs.caltech.edu)
-;; (bibtex-sort-ignore-string-entries sort-bibtex-entries,
-;; map-bibtex-entries): Added variable as requested by
-;; gernot@cs.unsw.oz.au, required changes to funs.
-;; (bibtex-current-entry-label): Added at request of
-;; yasuro@maekawa.is.uec.ac.jp
-;; (bibtex-DEAthesis:) Deleted along with corresponding entry from
-;; bibtex-x-help per shapiro@corto.inria.fr
-;; Moved narrow-to-bibtex-entry from C-c C-n to C-c n (the previous
-;; binding was in conflict with the binding for bibtex-pop-next.
-;; bug report from [shapiro@corto.inria.fr]
-;;
-
-;;;
-;;; alarson@src.honeywell.com 92-Feb-13
-;;; 1. Made bibtex-entry user callable, now prompts for entry type (e.g.
-;;; Article), with completion, and bound it to a key. This is now my
-;;; preferred way to add most entries.
-;;; 2. Made fields of a bibtex entry derived from the alist bibtex-entry-
-;;; field-alist.
-;;; 3. Fixed handling of escaped double quotes, e.g. "Schr{\"o}dinger".
-;;; 4. Fixed bug where unhiding bibtex entries moved point.
-;;; 5. Made "field name" specs permit (name . value) for defaulting. E.g.
-;;; (setq bibtex-mode-user-optional-fields '(("library" . "alarson")))
-;;; will generate the field:
-;;; library = "alarson",
-;;; 6. Added binding for narrow-to-bibtex-entry
-;;; 7. Adding a bibtex entry now runs hook: bibtex-add-entry-hook
-;;; 8. Made bibtex-clean-entry fixup text alignment, and eliminated the
-;;; dependency on bibtex-enclosing-reference which has a problem with
-;;; long entries (e.g. those containing abstracts).
-;;;
-;;; alarson@src.honeywell.com 92-Jan-31
-;;; Added support for: ispell, beginning/end of entry movement, a simple
-;;; outline like mode (hide the bodies of bibtex entries), support for
-;;; sorting bibtex entries, and maintaining them in sorted order, and
-;;; simple buffer validation.
-;;; User visible functions added:
-;;; ispell-{abstract,bibtex-entry}, {beginning,end}-of-bibtex-entry
-;;; hide-bibtex-entry-bodies, sort-bibtex-entries, validate-bibtex-
-;;; buffer, find-bibtex-duplicates
-;;; user visible variables added:
-;;; bibtex-maintain-sorted-entries
-;;; new local keybindings:
-;;; " tex-insert-quote
-;;; C-c$ ispell-bibtex-entry
-;;; M-C-a beginning-of-bibtex-entry
-;;; M-C-e end-of-bibtex-entry
-;;; Mike Newton (newton@gumby.cs.caltech.edu) 90.11.17
-;;; * Handle items like
-;;; title = poft # "Fifth Tri-quarterly" # random-conf,
-;;; and title = {This title is inside curlies}
-;;; * added user settable, always present, optional fields
-;;; * fixed 'bibtex-find-it's doc string's location
-;;; * bibtex-field-text made more general (it wouldn't handle the # construct)
-;;; and it now handles a small subset of the {} cases
-
-;;; Bengt Martensson, March 6
-;;; Adapted to Bibtex 0.99 by updating the optional fields according
-;;; to the document BibTeXing, Oren Patashnik, dated January 31, 1988.
-;;; Updated documentation strings accordingly. Added (provide 'bibtex).
-;;; If bibtex-include-OPT-crossref is non-nil, every entry will have
-;;; an OPTcrossref field, analogously for bibtex-include-OPTkey and
-;;; bibtex-include-OPTannote. Added bibtex-preamble, bound to ^C^EP,
-;;; and also found in X- and sun-menus. Cleaned up the sun-menu
-;;; stuff, and made it more uniform with the X-menu stuff. Marc: I
-;;; strongly suspect that I broke your parsing... (Or, more
-;;; correctly, BibTeX 0.99 broke it.)
-;;; Added bibtex-clean-entry-zap-empty-opts, defvar'd to t. If it
-;;; is nil, bibtex-clean-entry will leave empty optional fields alone.
-
-;;; Marc Shapiro 1-feb-89: integrated changes by Bengt Martensson 88-05-06:
-;;; Added Sun menu support. Locally bound to right mouse button in
-;;; bibtex-mode. Emacs 18.49 allows local mouse bindings!!
-;;; Commented out DEAthesis.
-
-;;; Marc Shapiro 6-oct-88
-;;; * skip-whitespace replaced by skip-chars-forward
-;;; * use indent-to-column instead of inserting tabs (changes to
-;;; bibtex-entry, bibtex-make-entry, bibtex-make-OPT-entry, renamed to
-;;; bibtex-make-optional-entry)
-;;; * C-c C-k deletes the current OPT entry entirely
-;;; * C-c C-d replaces text of field with ""
-;;; * renamed bibtex-find-it to bibtex-find-text. With arg, now goes to
-;;; start of text. Fixed bugs in it.
-
-;;; Marc Shapiro 23-sep-88
-;;; * bibtex-clean-entry moves past end of entry.
-;;; * bibtex-clean-entry signals mandatory fields left empty.
-
-;;; Marc Shapiro 18-jul-88
-;;; * Fixed bug in bibtex-flash-entry
-;;; * Moved all the entry type keystrokes to "C-c C-e something" (instead of
-;;; "C-c something" previously) to make room for more. C-c C-e is
-;;; supposed to stand for "entry" [idea taken from mail-mode]. Moved
-;;; bibtex-pop-previous to C-c C-p and bibtex-pop-next to C-c C-n.
-;;; * removed binding for "\e[25~"
-;;; * replaced bibtex-clean-optionals by bibtex-clean-entry, bound to
-;;; C-c C-c
-
-;;; Marc Shapiro 13-jul-88 [based on ideas by Sacha Krakowiak of IMAG]
-;;; * bibtex-pop-previous replaces current field with value of
-;;; similar field in previous entry. May be called n times in a row
-;;; (or with arg n) to pop similar field of n'th previous entry.
-;;; There is also a bibtex-pop-next to get similar field of next
-;;; entry.
-;;; * C-c C-k now kills all empty optional fields of current entry, and
-;;; removes "OPT" for those optional fields which have text.
-
-;;; Marc Shapiro 14-dec-87
-;;; Cosmetic fixes. Fixed small bug in bibtex-move-outside-of-entry.
-;;; Skip Montanaro <steinmetz!sprite!montanaro> 7-dec-87, Shapiro 10-dec-87
-;;; before inserting an entry, make sure we are outside of a bib entry
-;;; Marc Shapiro 3-nov-87
-;;; addition for France: DEAthesis
-;;; Marc Shapiro 19-oct-1987
-;;; add X window menu option; bug fixes. TAB, LFD, C-c " and C-c C-o now
-;;; behave consistently; deletion never occurs blindly.
-;;; Marc Shapiro <shapiro@inria.inria.fr> 15-oct-1986
-;;; align long lines nicely; C-c C-o checks for the "OPT" string;
-;;; TAB goes to the end of the string; use lower case; use
-;;; run-hooks
-
-;;; Bengt Martensson <ubrinf!mond!bengt> 87-06-28
-;;; Bengt Martensson <bengt@mathematik.uni-Bremen.de> 87-06-28
-;;; Original version
-
-;;; Code:
-
-;;; NOTE by Marc Shapiro, 14-dec-87:
-;;; (bibtex-x-environment) binds an X menu for bibtex mode to x-button-c-right.
-;;; Trouble is, in Emacs 18.44 you can't have a mode-specific mouse binding,
-;;; so it will remain active in all windows. Yuck!
-
-(provide 'bibtex)
-
-;;; these guys typically don't have autoloads...[alarson:19920131.1548CST]
-;;; Check for fboundp first so that if user autoloads them from non standard
-;;; places, the users bindings will take precedence.
-(if (not (fboundp 'tex-insert-quote))
- (autoload 'tex-insert-quote "tex-mode"))
-(if (not (fboundp 'sort-subr))
- (autoload 'sort-subr "sort"))
-
-;;; These should be in a more generally accessible location.
-
-(defun string-equalp (s1 s2)
- "Like string= except differences in case are ignored."
- (let ((ss1 (if (symbolp s1) (symbol-name s1) s1))
- (ss2 (if (symbolp s2) (symbol-name s2) s2)))
- (and (= (length ss1) (length ss2))
- (string-equal (upcase ss1) (upcase ss2)))))
-
-;;; This should be moved into simple.el, and the functions there modified
-;;; to call it rather than doing it themselves.
-(defun put-string-on-kill-ring (string)
- "Make STRING be the first element of the kill ring."
- (setq kill-ring (cons string kill-ring))
- (if (> (length kill-ring) kill-ring-max)
- (setcdr (nthcdr (1- kill-ring-max) kill-ring) nil))
- (setq kill-ring-yank-pointer kill-ring))
+;; Major mode for editing and validating BibTeX files.
+;; Usage:
+;; See documentation for function bibtex-mode (or type "\M-x describe-mode"
+;; when you are in BibTeX mode).
+;; Todo:
+;; Distribute texinfo file.
+;;; Code:
-(defvar bibtex-clean-entry-zap-empty-opts t
- "*If non-nil, bibtex-clean-entry will delete all empty optional fields.")
-
-(defvar bibtex-include-OPTcrossref t
- "*If non-nil, all entries will have an OPTcrossref field.")
-(defvar bibtex-include-OPTkey t
- "*If non-nil, all entries will have an OPTkey field.")
-(defvar bibtex-include-OPTannote t
- "*If non-nil, all entries will have an OPTannote field.")
+(eval-when-compile
+ (require 'compile))
+
+;; User Options:
+
+(defgroup bibtex nil
+ "BibTeX mode."
+ :group 'tex
+ :prefix "bibtex-")
+
+(defgroup bibtex-autokey nil
+ "Generates automatically a key from the author/editor and the title field"
+ :group 'bibtex
+ :prefix "bibtex-autokey-")
+
+(defcustom bibtex-mode-hook nil
+ "List of functions to call on entry to BibTeX mode."
+ :group 'bibtex
+ :type 'hook)
+
+(defcustom bibtex-field-delimiters 'braces
+ "*Controls type of field delimiters used.
+Set this to `braces' or `double-quotes' according to your personal
+preferences. This variable is buffer-local."
+ :group 'bibtex
+ :type '(choice (const braces)
+ (const double-quotes)))
+(make-variable-buffer-local 'bibtex-field-delimiters)
+
+(defcustom bibtex-entry-delimiters 'braces
+ "*Controls type of entry delimiters used.
+Set this to `braces' or `parentheses' according to your personal
+preferences. This variable is buffer-local."
+ :group 'bibtex
+ :type '(choice (const braces)
+ (const parentheses)))
+(make-variable-buffer-local 'bibtex-entry-delimiters)
+
+(defcustom bibtex-include-OPTcrossref '("InProceedings" "InCollection")
+ "*All entries listed here will have an OPTcrossref field."
+ :group 'bibtex
+ :type '(repeat string))
+
+(defcustom bibtex-include-OPTkey t
+ "*If non-nil, all entries will have an OPTkey field.
+If this is a string, it will be used as the initial field text.
+If this is a function, it will be called to generate the initial field text."
+ :group 'bibtex
+ :type '(choice (const :tag "None" nil)
+ (const :tag "Default" t)
+ (string :tag "Initial text")
+ (function :tag "Initialize Function" :value fun)))
+
+(defcustom bibtex-user-optional-fields
+ '(("annote" "Personal annotation (ignored)"))
+ "*List of optional fields the user wants to have always present.
+Entries should be of the same form as the OPTIONAL and
+CROSSREF-OPTIONAL lists in `bibtex-entry-field-alist' (see documentation
+of this variable for details)."
+ :group 'bibtex
+ :type '(repeat
+ (group (string :tag "Field")
+ (string :tag "Comment")
+ (option (group :inline t
+ :extra-offset -4
+ (choice :tag "Init" :value ""
+ string
+ function))))))
+
+(defcustom bibtex-entry-format '(opts-or-alts numerical-fields)
+ "*Controls type of formatting performed by `bibtex-clean-entry'.
+It may be t, nil, or a list of symbols out of the following:
+opts-or-alts Delete empty optional and alternative fields and
+ remove OPT and ALT prefixes from used fields.
+numerical-fields Delete delimiters around numeral fields.
+page-dashes Change double dashes in page field to single dash
+ (for scribe compatibility).
+inherit-booktitle If entry contains a crossref field and booktitle
+ field is empty, it is set to the contents of the
+ title field of the crossreferenced entry.
+ Caution: this will work only if buffer is
+ correctly sorted.
+realign Realign entries, so that field texts and perhaps equal
+ signs (depending on the value of
+ bibtex-align-at-equal-sign) begin in the same column.
+last-comma Add or delete comma on end of last field in entry,
+ according to value of `bibtex-comma-after-last-field'.
+delimiters Change delimiters according to variables
+ `bibtex-field-delimiters' and `bibtex-entry-delimiters'.
+unify-case Change case of entry and field names.
+
+The value t means do all of the above formatting actions.
+The value nil means do no formatting at all."
+ :group 'bibtex
+ :type '(choice (const :tag "None" nil)
+ (const :tag "All" t)
+ (set :menu-tag "Some"
+ (const opts-or-alts)
+ (const numerical-fields)
+ (const page-dashes)
+ (const inherit-booktitle)
+ (const realign)
+ (const last-comma)
+ (const delimiters)
+ (const unify-case))))
+
+(defcustom bibtex-clean-entry-hook nil
+ "*List of functions to call when entry has been cleaned.
+Functions are called with point inside the cleaned entry, and the buffer
+narrowed to just the entry."
+ :group 'bibtex
+ :type 'hook)
+
+(defcustom bibtex-sort-ignore-string-entries t
+ "*If non-nil, BibTeX @String entries are not sort-significant.
+That means they are ignored when determining ordering of the buffer
+(e.g. sorting, locating alphabetical position for new entries, etc.).
+This variable is buffer-local."
+ :group 'bibtex
+ :type 'boolean)
+(make-variable-buffer-local 'bibtex-sort-ignore-string-entries)
+
+(defcustom bibtex-maintain-sorted-entries nil
+ "*If non-nil, BibTeX mode maintains all BibTeX entries in sorted order.
+Setting this variable to nil will strip off some comfort (e.g. TAB
+completion for reference keys in minibuffer, automatic detection of
+duplicates) from BibTeX mode. See also `bibtex-sort-ignore-string-entries'.
+This variable is buffer-local."
+ :group 'bibtex
+ :type 'boolean)
+(make-variable-buffer-local 'bibtex-maintain-sorted-entries)
+
+(defcustom bibtex-field-kill-ring-max 20
+ "*Max length of `bibtex-field-kill-ring' before discarding oldest elements."
+ :group 'bibtex
+ :type 'integer)
+
+(defcustom bibtex-entry-kill-ring-max 20
+ "*Max length of `bibtex-entry-kill-ring' before discarding oldest elements."
+ :group 'bibtex
+ :type 'integer)
+
+(defcustom bibtex-parse-keys-timeout 60
+ "*Specifies interval for parsing buffers.
+All BibTeX buffers in emacs are parsed if emacs has been idle
+`bibtex-parse-keys-timeout' seconds. Only buffers which were modified
+after last parsing and which are maintained in sorted order are parsed."
+ :group 'bibtex
+ :type 'integer)
-;; note: the user should be allowed to have their own list of always
-;; available optional fields. exs: "keywords" "categories"
+(defvar bibtex-entry-field-alist
+ '(
+ ("Article" . (((("author" "Author1 [and Author2 ...] [and others]")
+ ("title" "Title of the article (BibTeX converts it to lowercase)")
+ ("journal" "Name of the journal (use string, remove braces)")
+ ("year" "Year of publication"))
+ (("volume" "Volume of the journal")
+ ("number" "Number of the journal (only allowed if entry contains volume)")
+ ("pages" "Pages in the journal")
+ ("month" "Month of the publication as a string (remove braces)")
+ ("note" "Remarks to be put at the end of the \\bibitem")))
+ ((("author" "Author1 [and Author2 ...] [and others]")
+ ("title" "Title of the article (BibTeX converts it to lowercase)"))
+ (("pages" "Pages in the journal")
+ ("journal" "Name of the journal (use string, remove braces)")
+ ("year" "Year of publication")
+ ("volume" "Volume of the journal")
+ ("number" "Number of the journal")
+ ("month" "Month of the publication as a string (remove braces)")
+ ("note" "Remarks to be put at the end of the \\bibitem")))))
+ ("Book" . (((("author" "Author1 [and Author2 ...] [and others]" "" t)
+ ("editor" "Editor1 [and Editor2 ...] [and others]" "" t)
+ ("title" "Title of the book")
+ ("publisher" "Publishing company")
+ ("year" "Year of publication"))
+ (("volume" "Volume of the book in the series")
+ ("number" "Number of the book in a small series (overwritten by volume)")
+ ("series" "Series in which the book appeared")
+ ("address" "Address of the publisher")
+ ("edition" "Edition of the book as a capitalized English word")
+ ("month" "Month of the publication as a string (remove braces)")
+ ("note" "Remarks to be put at the end of the \\bibitem")))
+ ((("author" "Author1 [and Author2 ...] [and others]" "" t)
+ ("editor" "Editor1 [and Editor2 ...] [and others]" "" t)
+ ("title" "Title of the book"))
+ (("publisher" "Publishing company")
+ ("year" "Year of publication")
+ ("volume" "Volume of the book in the series")
+ ("number" "Number of the book in a small series (overwritten by volume)")
+ ("series" "Series in which the book appeared")
+ ("address" "Address of the publisher")
+ ("edition" "Edition of the book as a capitalized English word")
+ ("month" "Month of the publication as a string (remove braces)")
+ ("note" "Remarks to be put at the end of the \\bibitem")))))
+ ("Booklet" . (((("title" "Title of the booklet (BibTeX converts it to lowercase)"))
+ (("author" "Author1 [and Author2 ...] [and others]")
+ ("howpublished" "The way in which the booklet was published")
+ ("address" "Address of the publisher")
+ ("month" "Month of the publication as a string (remove braces)")
+ ("year" "Year of publication")
+ ("note" "Remarks to be put at the end of the \\bibitem")))))
+ ("InBook" . (((("author" "Author1 [and Author2 ...] [and others]" "" t)
+ ("editor" "Editor1 [and Editor2 ...] [and others]" "" t)
+ ("title" "Title of the book")
+ ("chapter" "Chapter in the book")
+ ("publisher" "Publishing company")
+ ("year" "Year of publication"))
+ (("volume" "Volume of the book in the series")
+ ("number" "Number of the book in a small series (overwritten by volume)")
+ ("series" "Series in which the book appeared")
+ ("type" "Word to use instead of \"chapter\"")
+ ("address" "Address of the publisher")
+ ("edition" "Edition of the book as a capitalized English word")
+ ("month" "Month of the publication as a string (remove braces)")
+ ("pages" "Pages in the book")
+ ("note" "Remarks to be put at the end of the \\bibitem")))
+ ((("author" "Author1 [and Author2 ...] [and others]" "" t)
+ ("editor" "Editor1 [and Editor2 ...] [and others]" "" t)
+ ("title" "Title of the book")
+ ("chapter" "Chapter in the book"))
+ (("pages" "Pages in the book")
+ ("publisher" "Publishing company")
+ ("year" "Year of publication")
+ ("volume" "Volume of the book in the series")
+ ("number" "Number of the book in a small series (overwritten by volume)")
+ ("series" "Series in which the book appeared")
+ ("type" "Word to use instead of \"chapter\"")
+ ("address" "Address of the publisher")
+ ("edition" "Edition of the book as a capitalized English word")
+ ("month" "Month of the publication as a string (remove braces)")
+ ("note" "Remarks to be put at the end of the \\bibitem")))))
+ ("InCollection" . (((("author" "Author1 [and Author2 ...] [and others]")
+ ("title" "Title of the article in book (BibTeX converts it to lowercase)")
+ ("booktitle" "Name of the book")
+ ("publisher" "Publishing company")
+ ("year" "Year of publication"))
+ (("editor" "Editor1 [and Editor2 ...] [and others]")
+ ("volume" "Volume of the book in the series")
+ ("number" "Number of the book in a small series (overwritten by volume)")
+ ("series" "Series in which the book appeared")
+ ("type" "Word to use instead of \"chapter\"")
+ ("chapter" "Chapter in the book")
+ ("pages" "Pages in the book")
+ ("address" "Address of the publisher")
+ ("edition" "Edition of the book as a capitalized English word")
+ ("month" "Month of the publication as a string (remove braces)")
+ ("note" "Remarks to be put at the end of the \\bibitem")))
+ ((("author" "Author1 [and Author2 ...] [and others]")
+ ("title" "Title of the article in book (BibTeX converts it to lowercase)")
+ ("booktitle" "Name of the book"))
+ (("pages" "Pages in the book")
+ ("publisher" "Publishing company")
+ ("year" "Year of publication")
+ ("editor" "Editor1 [and Editor2 ...] [and others]")
+ ("volume" "Volume of the book in the series")
+ ("number" "Number of the book in a small series (overwritten by volume)")
+ ("series" "Series in which the book appeared")
+ ("type" "Word to use instead of \"chapter\"")
+ ("chapter" "Chapter in the book")
+ ("address" "Address of the publisher")
+ ("edition" "Edition of the book as a capitalized English word")
+ ("month" "Month of the publication as a string (remove braces)")
+ ("note" "Remarks to be put at the end of the \\bibitem")))))
+ ("InProceedings" . (((("author" "Author1 [and Author2 ...] [and others]")
+ ("title" "Title of the article in proceedings (BibTeX converts it to lowercase)")
+ ("booktitle" "Name of the conference proceedings")
+ ("year" "Year of publication"))
+ (("editor" "Editor1 [and Editor2 ...] [and others]")
+ ("volume" "Volume of the conference proceedings in the series")
+ ("number" "Number of the conference proceedings in a small series (overwritten by volume)")
+ ("series" "Series in which the conference proceedings appeared")
+ ("pages" "Pages in the conference proceedings")
+ ("address" "Location of the Proceedings")
+ ("month" "Month of the publication as a string (remove braces)")
+ ("organization" "Sponsoring organization of the conference")
+ ("publisher" "Publishing company, its location")
+ ("note" "Remarks to be put at the end of the \\bibitem")))
+ ((("author" "Author1 [and Author2 ...] [and others]")
+ ("title" "Title of the article in proceedings (BibTeX converts it to lowercase)")
+ ("booktitle" "Name of the conference proceedings"))
+ (("pages" "Pages in the conference proceedings")
+ ("year" "Year of publication")
+ ("editor" "Editor1 [and Editor2 ...] [and others]")
+ ("volume" "Volume of the conference proceedings in the series")
+ ("number" "Number of the conference proceedings in a small series (overwritten by volume)")
+ ("series" "Series in which the conference proceedings appeared")
+ ("address" "Location of the Proceedings")
+ ("month" "Month of the publication as a string (remove braces)")
+ ("organization" "Sponsoring organization of the conference")
+ ("publisher" "Publishing company, its location")
+ ("note" "Remarks to be put at the end of the \\bibitem")))))
+ ("Manual" . (((("title" "Title of the manual"))
+ (("author" "Author1 [and Author2 ...] [and others]")
+ ("organization" "Publishing organization of the manual")
+ ("address" "Address of the organization")
+ ("edition" "Edition of the manual as a capitalized English word")
+ ("month" "Month of the publication as a string (remove braces)")
+ ("year" "Year of publication")
+ ("note" "Remarks to be put at the end of the \\bibitem")))))
+ ("MastersThesis" . (((("author" "Author1 [and Author2 ...] [and others]")
+ ("title" "Title of the master\'s thesis (BibTeX converts it to lowercase)")
+ ("school" "School where the master\'s thesis was written")
+ ("year" "Year of publication"))
+ (("type" "Type of the master\'s thesis (if other than \"Master\'s thesis\")")
+ ("address" "Address of the school (if not part of field \"school\") or country")
+ ("month" "Month of the publication as a string (remove braces)")
+ ("note" "Remarks to be put at the end of the \\bibitem")))))
+ ("Misc" . ((()
+ (("author" "Author1 [and Author2 ...] [and others]")
+ ("title" "Title of the reference (BibTeX converts it to lowercase)")
+ ("howpublished" "The way in which the reference was published")
+ ("month" "Month of the publication as a string (remove braces)")
+ ("year" "Year of publication")
+ ("note" "Remarks to be put at the end of the \\bibitem")))))
+ ("PhdThesis" . (((("author" "Author1 [and Author2 ...] [and others]")
+ ("title" "Title of the PhD. thesis")
+ ("school" "School where the PhD. thesis was written")
+ ("year" "Year of publication"))
+ (("type" "Type of the PhD. thesis")
+ ("address" "Address of the school (if not part of field \"school\") or country")
+ ("month" "Month of the publication as a string (remove braces)")
+ ("note" "Remarks to be put at the end of the \\bibitem")))))
+ ("Proceedings" . (((("title" "Title of the conference proceedings")
+ ("year" "Year of publication"))
+ (("editor" "Editor1 [and Editor2 ...] [and others]")
+ ("volume" "Volume of the conference proceedings in the series")
+ ("number" "Number of the conference proceedings in a small series (overwritten by volume)")
+ ("series" "Series in which the conference proceedings appeared")
+ ("address" "Location of the Proceedings")
+ ("month" "Month of the publication as a string (remove braces)")
+ ("organization" "Sponsoring organization of the conference")
+ ("publisher" "Publishing company, its location")
+ ("note" "Remarks to be put at the end of the \\bibitem")))))
+ ("TechReport" . (((("author" "Author1 [and Author2 ...] [and others]")
+ ("title" "Title of the technical report (BibTeX converts it to lowercase)")
+ ("institution" "Sponsoring institution of the report")
+ ("year" "Year of publication"))
+ (("type" "Type of the report (if other than \"technical report\")")
+ ("number" "Number of the technical report")
+ ("address" "Address of the institution (if not part of field \"institution\") or country")
+ ("month" "Month of the publication as a string (remove braces)")
+ ("note" "Remarks to be put at the end of the \\bibitem")))))
+ ("Unpublished" . (((("author" "Author1 [and Author2 ...] [and others]")
+ ("title" "Title of the unpublished reference (BibTeX converts it to lowercase)")
+ ("note" "Remarks to be put at the end of the \\bibitem"))
+ (("month" "Month of the publication as a string (remove braces)")
+ ("year" "Year of publication")))))
+ )
-(defvar bibtex-mode-user-optional-fields nil ;no default value
- "*List of optional fields that user want to have as always present
-when making a bibtex entry. One possibility is for ``keywords''.
-Entries can be either strings or conses, in which case the car should be
-string and the cdr the value to be inserted.")
+ "Defines reference types and their associated fields.
+List of
+(ENTRY-NAME (REQUIRED OPTIONAL) (CROSSREF-REQUIRED CROSSREF-OPTIONAL))
+triples.
+If the third element is nil, the first pair is always used.
+If not, the second pair is used in the case of presence of a crossref
+field and the third in the case of absence.
+REQUIRED, OPTIONAL, CROSSREF-REQUIRED and CROSSREF-OPTIONAL are lists.
+Each element of these lists is a list of the form
+(FIELD-NAME COMMENT-STRING INIT ALTERNATIVE-FLAG).
+COMMENT-STRING, INIT, and ALTERNATIVE-FLAG are optional.
+FIELD-NAME is the name of the field, COMMENT-STRING the comment to
+appear in the echo area, INIT is either the initial content of the
+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.")
+
+(defvar bibtex-comment-start "@Comment ")
+
+(defcustom bibtex-add-entry-hook nil
+ "List of functions to call when entry has been inserted."
+ :group 'bibtex
+ :type 'hook)
+
+(defcustom bibtex-predefined-month-strings
+ '(
+ ("jan") ("feb") ("mar") ("apr") ("may") ("jun")
+ ("jul") ("aug") ("sep") ("oct") ("nov") ("dec")
+ )
+ "Alist of month string definitions.
+Should contain all strings used for months in the BibTeX style files.
+Each element is a list with just one element: the string."
+ :group 'bibtex
+ :type '(repeat
+ (list string)))
+
+(defcustom bibtex-predefined-strings
+ (append
+ bibtex-predefined-month-strings
+ '(
+ ("acmcs") ("acta") ("cacm") ("ibmjrd") ("ibmsj") ("ieeese")
+ ("ieeetc") ("ieeetcad") ("ipl") ("jacm") ("jcss") ("scp")
+ ("sicomp") ("tcs") ("tocs") ("tods") ("tog") ("toms") ("toois")
+ ("toplas")
+ ))
+ "Alist of string definitions.
+Should contain the strings defined in the BibTeX style files. Each
+element is a list with just one element: the string."
+ :group 'bibtex
+ :type '(repeat
+ (list string)))
+
+(defcustom bibtex-string-files nil
+ "*List of BibTeX files containing string definitions.
+Those files must be specified using pathnames relative to the
+directories specified in `bibtex-string-file-path'. This variable is only
+evaluated when BibTeX mode is entered (i. e. when loading the BibTeX
+file)."
+ :group 'bibtex
+ :type '(repeat file))
+
+(defvar bibtex-string-file-path (getenv "BIBINPUTS")
+ "*Colon separated list of pathes to search for `bibtex-string-files'.")
+
+(defcustom bibtex-help-message t
+ "*If not nil print help messages in the echo area on entering a new field."
+ :group 'bibtex
+ :type 'boolean)
+
+(defcustom bibtex-autokey-prefix-string ""
+ "*String to use as a prefix for all generated keys.
+See the documentation of function `bibtex-generate-autokey' for more detail."
+ :group 'bibtex-autokey
+ :type 'string)
+
+(defcustom bibtex-autokey-names 1
+ "*Number of names to use for the automatically generated reference key.
+If this is variable is nil, all names are used.
+Possibly more names are used according to `bibtex-autokey-names-stretch'.
+See the documentation of function `bibtex-generate-autokey' for more detail."
+ :group 'bibtex-autokey
+ :type '(choice (const :tag "All" infty)
+ integer))
+
+(defcustom bibtex-autokey-names-stretch 0
+ "*Number of names that can additionally be used.
+These names are used only, if all names are used then.
+See the documentation of function `bibtex-generate-autokey' for details."
+ :group 'bibtex-autokey
+ :type 'integer)
+
+(defcustom bibtex-autokey-additional-names ""
+ "*String to prepend to the generated key if not all names could be used.
+See the documentation of function `bibtex-generate-autokey' for details."
+ :group 'bibtex-autokey
+ :type 'string)
+
+(defvar bibtex-autokey-transcriptions
+ '(
+ ;; language specific characters
+ ("\\\\aa" "a")
+ ("\\\\AA" "A")
+ ("\\\"a\\|\\\\\\\"a\\|\\\\ae" "ae")
+ ("\\\"A\\|\\\\\\\"A\\|\\\\AE" "Ae")
+ ("\\\\i" "i")
+ ("\\\\j" "j")
+ ("\\\\l" "l")
+ ("\\\\L" "L")
+ ("\\\"o\\|\\\\\\\"o\\|\\\\o\\|\\\\oe" "oe")
+ ("\\\"O\\|\\\\\\\"O\\|\\\\O\\|\\\\OE" "Oe")
+ ("\\\"s\\|\\\\\\\"s" "ss")
+ ("\\\"u\\|\\\\\\\"u" "ue")
+ ("\\\"U\\|\\\\\\\"U" "Ue")
+ ;; accents
+ ("\\\\`\\|\\\\'\\|\\\\\\^\\|\\\\~\\|\\\\=\\|\\\\\\.\\|\\\\u\\|\\\\v\\|\\\\H\\|\\\\t\\|\\\\c\\|\\\\d\\|\\\\b" "")
+ ;; braces
+ ("{" "") ("}" ""))
+ "Alist of (old-regexp new-string) pairs.
+Used by the default values of `bibtex-autokey-name-change-strings' and
+`bibtex-autokey-titleword-change-strings'. Defaults to translating some
+language specific characters to their ASCII transcriptions, and
+removing any character accents.")
+
+(defcustom bibtex-autokey-name-change-strings
+ bibtex-autokey-transcriptions
+ "Alist of (OLD-REGEXP NEW-STRING) pairs.
+Any part of name matching a OLD-REGEXP is replaced by NEW-STRING.
+Case is significant in OLD-REGEXP. All regexps are tried in the
+order in which they appear in the list, so be sure to avoid inifinite
+loops here.
+See the documentation of function `bibtex-generate-autokey' for details."
+ :group 'bibtex-autokey
+ :type '(repeat
+ (list (regexp :tag "Old")
+ (string :tag "New"))))
+
+(defcustom bibtex-autokey-name-case-convert 'downcase
+ "*Function called for each name to perform case conversion.
+See the documentation of function `bibtex-generate-autokey' for more detail."
+ :group 'bibtex-autokey
+ :type '(choice (const :tag "Preserve case" identity)
+ (const :tag "Downcase" downcase)
+ (const :tag "Capitalize" capitalize)
+ (const :tag "Upcase" upcase)
+ (function :tag "Conversion function")))
+
+(defcustom bibtex-autokey-name-length 'infty
+ "*Number of characters from name to incorporate into key.
+If this is set to anything but a number, all characters are used.
+See the documentation of function `bibtex-generate-autokey' for details."
+ :group 'bibtex-autokey
+ :type '(choice (const :tag "All" infty)
+ integer))
+
+(defcustom bibtex-autokey-name-separator ""
+ "*String that comes between any two names in the key.
+See the documentation of function `bibtex-generate-autokey' for details."
+ :group 'bibtex-autokey
+ :type 'string)
+
+(defcustom bibtex-autokey-year-length 2
+ "*Number of rightmost digits from the year field to incorporate into key.
+See the documentation of function `bibtex-generate-autokey' for details."
+ :group 'bibtex-autokey
+ :type 'integer)
+
+(defcustom bibtex-autokey-year-use-crossref-entry t
+ "*If non-nil use year field from crossreferenced entry if necessary.
+If this variable is non-nil and the current entry has no year, but a
+valid crossref entry, the year field from the crossreferenced entry is
+used.
+See the documentation of function `bibtex-generate-autokey' for details."
+ :group 'bibtex-autokey
+ :type 'boolean)
+
+(defcustom bibtex-autokey-titlewords 5
+ "*Number of title words to use for the automatically generated reference key.
+If this is set to anything but a number, all title words are used.
+Possibly more words from the title are used according to
+`bibtex-autokey-titlewords-stretch'.
+See the documentation of function `bibtex-generate-autokey' for details."
+ :group 'bibtex-autokey
+ :type '(choice (const :tag "All" infty)
+ integer))
+
+(defcustom bibtex-autokey-title-terminators
+ '("\\." "!" "\\?" ":" ";" "--")
+ "*Regexp list defining the termination of the main part of the title.
+Case of the regexps is ignored.
+See the documentation of function `bibtex-generate-autokey' for details."
+ :group 'bibtex-autokey
+ :type '(repeat regexp))
+
+(defcustom bibtex-autokey-titlewords-stretch 2
+ "*Number of words that can additionally be used from the title.
+These words are used only, if a sentence from the title can be ended then.
+See the documentation of function `bibtex-generate-autokey' for details."
+ :group 'bibtex-autokey
+ :type 'integer)
+
+(defcustom bibtex-autokey-titleword-ignore
+ '("A" "An" "On" "The" "Eine?" "Der" "Die" "Das"
+ "[^A-Z].*" ".*[^a-zA-Z0-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 matchs a
+regexp from that list, it is not included in the title part of the key.
+See the documentation of function `bibtex-generate-autokey' for details."
+ :group 'bibtex-autokey
+ :type '(repeat regexp))
+
+(defcustom bibtex-autokey-titleword-case-convert 'downcase
+ "*Function called for each titleword to perform case conversion.
+See the documentation of function `bibtex-generate-autokey' for more detail."
+ :group 'bibtex-autokey
+ :type '(choice (const :tag "Preserve case" identity)
+ (const :tag "Downcase" downcase)
+ (const :tag "Capitalize" capitalize)
+ (const :tag "Upcase" upcase)
+ (function :tag "Conversion function")))
+
+(defcustom bibtex-autokey-titleword-abbrevs nil
+ "*Determines exceptions to the usual abbreviation mechanism.
+An alist of (OLD-REGEXP NEW-STRING) pairs. Case is ignored
+in matching against OLD-REGEXP, and the first matching pair is used.
+See the documentation of function `bibtex-generate-autokey' for details.")
+
+(defcustom bibtex-autokey-titleword-change-strings
+ bibtex-autokey-transcriptions
+ "Alist of (OLD-REGEXP NEW-STRING) pairs.
+Any part of title word matching a OLD-REGEXP is replaced by NEW-STRING.
+Case is significant in OLD-REGEXP. All regexps are tried in the
+order in which they appear in the list, so be sure to avoid inifinite
+loops here.
+See the documentation of function `bibtex-generate-autokey' for details."
+ :group 'bibtex-autokey
+ :type '(repeat
+ (list (regexp :tag "Old")
+ (string :tag "New"))))
+
+(defcustom bibtex-autokey-titleword-length 5
+ "*Number of characters from title words to incorporate into key.
+If this is set to anything but a number, all characters are used.
+See the documentation of function `bibtex-generate-autokey' for details."
+ :group 'bibtex-autokey
+ :type '(choice (const :tag "All" infty)
+ integer))
+
+(defcustom bibtex-autokey-titleword-separator "_"
+ "*String to be put between the title words.
+See the documentation of function `bibtex-generate-autokey' for details."
+ :group 'bibtex-autokey
+ :type 'string)
+
+(defcustom bibtex-autokey-name-year-separator ""
+ "*String to be put between name part and year part of key.
+See the documentation of function `bibtex-generate-autokey' for details."
+ :group 'bibtex-autokey
+ :type 'string)
+
+(defcustom bibtex-autokey-year-title-separator ":_"
+ "*String to be put between name part and year part of key.
+See the documentation of function `bibtex-generate-autokey' for details."
+ :group 'bibtex-autokey
+ :type 'string)
+
+(defcustom bibtex-autokey-edit-before-use t
+ "*If non-nil, user is allowed to edit the generated key before it is used."
+ :group 'bibtex-autokey
+ :type 'boolean)
+
+(defcustom bibtex-autokey-before-presentation-function nil
+ "Function to call before the generated key is presented.
+If non-nil this should be a single function, which is called before
+the generated key is presented (in entry or, if
+`bibtex-autokey-edit-before-use' is t, in minibuffer). This function
+must take one argument (the automatically generated key), and must
+return with a string (the key to use)."
+ :group 'bibtex-autokey
+ :type 'function)
+
+(defcustom bibtex-entry-offset 0
+ "*Offset for BibTeX entries.
+Added to the value of all other variables which determine colums."
+ :group 'bibtex
+ :type 'integer)
+
+(defcustom bibtex-field-indentation 2
+ "*Starting column for the name part in BibTeX fields."
+ :group 'bibtex
+ :type 'integer)
+
+(defcustom bibtex-text-indentation
+ (+
+ bibtex-field-indentation
+ (length "organization = "))
+ "*Starting column for the text part in BibTeX fields.
+Should be equal to the space needed for the longest name part."
+ :group 'bibtex
+ :type 'integer)
+
+(defcustom bibtex-contline-indentation
+ (+ bibtex-text-indentation 1)
+ "*Starting column for continuation lines of BibTeX fields."
+ :group 'bibtex
+ :type 'integer)
+
+(defcustom bibtex-align-at-equal-sign nil
+ "*If non-nil, align fields at equal sign instead of field text.
+If non-nil, the column for the equal sign is
+the value of `bibtex-text-indentation', minus 2."
+ :group 'bibtex
+ :type 'boolean)
+
+(defcustom bibtex-comma-after-last-field nil
+ "*If non-nil, a comma is put at end of last field in the entry template."
+ :group 'bibtex
+ :type 'boolean)
+
+;; bibtex-font-lock-keywords is a user option as well, but since the
+;; patterns used to define this variable are defined in a later
+;; section of this file, it is defined later.
+\f
+;; Special support taking care of variants
+(if (boundp 'mark-active)
+ (defun bibtex-mark-active ()
+ ;; In Emacs mark-active indicates if mark is active.
+ mark-active)
+ (defun bibtex-mark-active ()
+ ;; In XEmacs (mark) returns nil when not active.
+ (if zmacs-regions (mark) (mark t))))
+
+(if (fboundp 'run-with-idle-timer)
+ ;; timer.el is distributed with Emacs
+ (fset 'bibtex-run-with-idle-timer 'run-with-idle-timer)
+ ;; timer.el is not distributed with XEmacs
+ ;; Notice that this does not (yet) pass the arguments, but they
+ ;; are not used (yet) in bibtex.el. Fix if needed.
+ (defun bibtex-run-with-idle-timer (secs repeat function &rest args)
+ (start-itimer "bibtex" function secs (if repeat secs nil) t)))
+\f
+;; Syntax Table, Keybindings and BibTeX Entry List
(defvar bibtex-mode-syntax-table
(let ((st (make-syntax-table)))
- ;; [alarson:19920214.1004CST] make double quote a string quote
(modify-syntax-entry ?\" "\"" st)
(modify-syntax-entry ?$ "$$ " st)
(modify-syntax-entry ?% "< " st)
- (modify-syntax-entry ?' "w " st)
- (modify-syntax-entry ?@ "w " st)
+ (modify-syntax-entry ?' "w " st)
+ (modify-syntax-entry ?@ "w " st)
(modify-syntax-entry ?\\ "\\" st)
(modify-syntax-entry ?\f "> " st)
(modify-syntax-entry ?\n "> " st)
(modify-syntax-entry ?~ " " st)
st))
-(defvar bibtex-mode-abbrev-table nil "")
-(define-abbrev-table 'bibtex-mode-abbrev-table ())
(defvar bibtex-mode-map
(let ((km (make-sparse-keymap)))
-
(define-key km "\t" 'bibtex-find-text)
(define-key km "\n" 'bibtex-next-field)
- (define-key km "\C-c\"" 'bibtex-remove-double-quotes)
+ (define-key km "\M-\t" 'bibtex-complete-string)
+ (define-key km [(meta tab)] 'bibtex-complete-key)
+ (define-key km "\C-c\"" 'bibtex-remove-delimiters)
+ (define-key km "\C-c{" 'bibtex-remove-delimiters)
+ (define-key km "\C-c}" 'bibtex-remove-delimiters)
(define-key km "\C-c\C-c" 'bibtex-clean-entry)
- (define-key km "\C-c?" 'describe-mode)
+ (define-key km "\C-c\C-q" 'bibtex-fill-entry)
+ (define-key km "\C-c?" 'bibtex-print-help-message)
(define-key km "\C-c\C-p" 'bibtex-pop-previous)
(define-key km "\C-c\C-n" 'bibtex-pop-next)
- (define-key km "\C-c\C-k" 'bibtex-kill-optional-field)
+ (define-key km "\C-c\C-k" 'bibtex-kill-field)
+ (define-key km "\C-c\M-k" 'bibtex-copy-field-as-kill)
+ (define-key km "\C-c\C-w" 'bibtex-kill-entry)
+ (define-key km "\C-c\M-w" 'bibtex-copy-entry-as-kill)
+ (define-key km "\C-c\C-y" 'bibtex-yank)
+ (define-key km "\C-c\M-y" 'bibtex-yank-pop)
(define-key km "\C-c\C-d" 'bibtex-empty-field)
-
- ;; [alarson:19920131.1543CST]
- (define-key km "\"" 'tex-insert-quote)
- (define-key km "\C-c$" 'ispell-bibtex-entry)
- (define-key km "\M-\C-a" 'beginning-of-bibtex-entry)
- (define-key km "\M-\C-e" 'end-of-bibtex-entry)
- (define-key km "\C-c\C-b" 'bibtex-entry)
-; (define-key km "\C-cn" 'narrow-to-bibtex-entry)
-
- (define-key km "\C-c\C-e\C-a" 'bibtex-Article)
- (define-key km "\C-c\C-e\C-b" 'bibtex-Book)
-; (define-key km "\C-c\C-e\C-d" 'bibtex-DEAthesis)
- (define-key km "\C-c\C-e\C-c" 'bibtex-InProceedings)
- (define-key km "\C-c\C-e\C-i" 'bibtex-InBook)
+ (define-key km "\C-c\C-f" 'bibtex-make-field)
+ (define-key km "\C-c$" 'bibtex-ispell-abstract)
+ (define-key km "\M-\C-a" 'bibtex-beginning-of-entry)
+ (define-key km "\M-\C-e" 'bibtex-end-of-entry)
+ (define-key km "\C-\M-l" 'bibtex-reposition-window)
+ (define-key km "\C-\M-h" 'bibtex-mark-entry)
+ (define-key km "\C-c\C-b" 'bibtex-entry)
+ (define-key km "\C-c\C-t" 'bibtex-hide-entry-bodies)
+ (define-key km "\C-c\C-rn" 'bibtex-narrow-to-entry)
+ (define-key km "\C-c\C-rw" 'widen)
+ (define-key km "\C-c\C-o" 'bibtex-remove-OPT-or-ALT)
+ (define-key km "\C-c\C-e\C-i" 'bibtex-InProceedings)
(define-key km "\C-c\C-ei" 'bibtex-InCollection)
- (define-key km "\C-c\C-eI" 'bibtex-InProceedings)
+ (define-key km "\C-c\C-eI" 'bibtex-InBook)
+ (define-key km "\C-c\C-e\C-a" 'bibtex-Article)
+ (define-key km "\C-c\C-e\C-b" 'bibtex-InBook)
+ (define-key km "\C-c\C-eb" 'bibtex-Book)
+ (define-key km "\C-c\C-eB" 'bibtex-Booklet)
+ (define-key km "\C-c\C-e\C-c" 'bibtex-InCollection)
(define-key km "\C-c\C-e\C-m" 'bibtex-Manual)
(define-key km "\C-c\C-em" 'bibtex-MastersThesis)
(define-key km "\C-c\C-eM" 'bibtex-Misc)
- (define-key km "\C-c\C-o" 'bibtex-remove-OPT)
- (define-key km "\C-c\C-e\C-p" 'bibtex-PhdThesis)
+ (define-key km "\C-c\C-e\C-p" 'bibtex-InProceedings)
(define-key km "\C-c\C-ep" 'bibtex-Proceedings)
- (define-key km "\C-c\C-eP" 'bibtex-preamble)
+ (define-key km "\C-c\C-eP" 'bibtex-PhdThesis)
+ (define-key km "\C-c\C-e\M-p" 'bibtex-Preamble)
+ (define-key km "\C-c\C-e\C-s" 'bibtex-String)
(define-key km "\C-c\C-e\C-t" 'bibtex-TechReport)
- (define-key km "\C-c\C-e\C-s" 'bibtex-string)
(define-key km "\C-c\C-e\C-u" 'bibtex-Unpublished)
km))
-(defvar bibtex-pop-previous-search-point nil
- "Next point where bibtex-pop-previous should start looking for a similar
-entry.")
+(easy-menu-define
+ bibtex-edit-menu bibtex-mode-map "BibTeX-Edit Menu in BibTeX mode"
+ '("BibTeX-Edit"
+ ("Moving inside an Entry"
+ ["End of Field" bibtex-find-text t]
+ ["Next Field" bibtex-next-field t]
+ ["Beginning of Entry" bibtex-beginning-of-entry t]
+ ["End of Entry" bibtex-end-of-entry t])
+ ("Operating on Current Entry"
+ ["Fill Entry" bibtex-fill-entry t]
+ ["Clean Entry" bibtex-clean-entry t]
+ "--"
+ ["Kill Entry" bibtex-kill-entry t]
+ ["Copy Entry to Kill Ring" bibtex-copy-entry-as-kill t]
+ ["Paste Most Recently Killed Entry" bibtex-yank t]
+ ["Paste Previously Killed Entry" bibtex-yank-pop t]
+ "--"
+ ["Ispell Entry" bibtex-ispell-entry t]
+ ["Ispell Entry Abstract" bibtex-ispell-abstract t]
+ ["Narrow to Entry" bibtex-narrow-to-entry t])
+ ("Operating on Current Field"
+ ["Remove Delimiters" bibtex-remove-delimiters t]
+ ["Remove OPT or ALT Prefix" bibtex-remove-OPT-or-ALT t]
+ ["Clear Field" bibtex-empty-field t]
+ "--"
+ ["Kill Field" bibtex-kill-field t]
+ ["Copy Field to Kill Ring" bibtex-copy-field-as-kill t]
+ ["Paste Most Recently Killed Field" bibtex-yank t]
+ ["Paste Previously Killed Field" bibtex-yank-pop t]
+ "--"
+ ["Make New Field" bibtex-make-field t]
+ "--"
+ ["Snatch from Similar Following Field" bibtex-pop-next t]
+ ["Snatch from Similar Preceding Field" bibtex-pop-previous t]
+ "--"
+ ["String Complete" bibtex-complete-string t]
+ ["Key Complete" bibtex-complete-key t]
+ "--"
+ ["Help about Current Field" bibtex-print-help-message t])
+ ("Operating on Buffer or Region"
+ ["Validate Entries" bibtex-validate t]
+ ["Sort Entries" bibtex-sort-buffer t]
+ ["Reformat Entries" bibtex-reformat t]
+ ["Hide Entry Bodies" bibtex-hide-entry-bodies t]
+ ["Count Entries" bibtex-count-entries t])
+ ("Miscellaneous"
+ ["Convert Alien Buffer" bibtex-convert-alien t]
+ ["Submit Bug Report" bibtex-submit-bug-report t])))
+
+(easy-menu-define
+ bibtex-entry-menu bibtex-mode-map "Entry-Types Menu in BibTeX mode"
+ (list "Entry-Types"
+ ["Article in Journal" bibtex-Article t]
+ ["Article in Conference Proceedings" bibtex-InProceedings t]
+ ["Article in a Collection" bibtex-InCollection t]
+ ["Chapter or Pages in a Book" bibtex-InBook t]
+ ["Conference Proceedings" bibtex-Proceedings t]
+ ["Book" bibtex-Book t]
+ ["Booklet (Bound, but no Publisher/Institution)" bibtex-Booklet t]
+ ["PhD. Thesis" bibtex-PhdThesis t]
+ ["Master's Thesis" bibtex-MastersThesis t]
+ ["Technical Report" bibtex-TechReport t]
+ ["Technical Manual" bibtex-Manual t]
+ ["Unpublished" bibtex-Unpublished t]
+ ["Miscellaneous" bibtex-Misc t]
+ ["String" bibtex-String t]
+ ["Preamble" bibtex-Preamble t]))
-(defvar bibtex-pop-next-search-point nil
- "Next point where bibtex-pop-next should start looking for a similar
-entry.")
+\f
+;; Bug Reporting
-(defvar bibtex-entry-field-alist
- '(
- ("Article" . ((("author" "title" "journal" "year")
- ("volume" "number" "pages" "month" "note"))
- (("author" "title")
- ("journal" "year" "volume" "number" "pages"
- "month" "note"))))
- ("Book" . ((("author" "title" "publisher" "year")
- ("editor" "volume" "number" "series" "address"
- "edition" "month" "note"))))
- ("Booklet" . ((("title")
- ("author" "howpublished" "address" "month" "year" "note"))))
-
- ;; France: Dipl\^{o}me d'Etudes Approfondies (similar to Master's)
-; ("DEAthesis" . ((("author" "title" "school" "year")
-; ("address" "month" "note"))))
-
- ("InBook" . ((("author" "title" "chapter" "publisher" "year")
- ("editor" "pages" "volume" "number" "series" "address"
- "edition" "month" "type" "note"))
- (("author" "title" "chapter")
- ("publisher" "year" "editor" "pages" "volume" "number"
- "series" "address" "edition" "month" "type" "note"))))
-
-
- ("InCollection" . ((("author" "title"
- "booktitle" "publisher" "year")
- ("editor" "volume" "number" "series" "type" "chapter"
- "pages" "address" "edition" "month" "note"))
- (("author" "title")
- ("booktitle" "publisher" "year"
- "editor" "volume" "number" "series" "type" "chapter"
- "pages" "address" "edition" "month" "note"))))
-
-
- ("InProceedings" . ((("author" "title" "booktitle" "year")
- ("editor" "volume" "number" "series" "pages"
- "organization" "publisher" "address" "month" "note"))
- (("author" "title")
- ("editor" "volume" "number" "series" "pages"
- "booktitle" "year"
- "organization" "publisher" "address" "month" "note"))))
-
-
- ("Manual" . ((("title")
- ("author" "organization" "address" "edition" "year"
- "month" "note"))))
-
- ("MastersThesis" . ((("author" "title" "school" "year")
- ("address" "month" "note" "type"))))
+(defconst
+ bibtex-maintainer-address "Dirk Herrmann <D.Herrmann@tu-bs.de>")
+;; current maintainer
- ("Misc" . ((()
- ("author" "title" "howpublished" "year" "month" "note"))))
+\f
+;; Internal Variables
- ("PhdThesis" . ((("author" "title" "school" "year")
- ("address" "month" "type" "note"))))
+(defvar bibtex-pop-previous-search-point nil)
+;; Next point where bibtex-pop-previous starts looking for a similar
+;; entry.
- ("Proceedings" . ((("title" "year")
- ("editor" "volume" "number" "series" "publisher"
- "organization" "address" "month" "note"))))
+(defvar bibtex-pop-next-search-point nil)
+;; Next point where bibtex-pop-next starts looking for a similar entry.
- ("TechReport" . ((("author" "title" "institution" "year")
- ("type" "number" "address" "month" "note"))))
+(defvar bibtex-field-kill-ring nil)
+;; Ring of least recently killed fields. At most
+;; bibtex-field-kill-ring-max items are kept here.
- ("Unpublished" . ((("author" "title" "note")
- ("year" "month"))))
- )
+(defvar bibtex-field-kill-ring-yank-pointer nil)
+;; The tail of bibtex-field-kill-ring whose car is the last item yanked.
+
+(defvar bibtex-entry-kill-ring nil)
+;; Ring of least recently killed entries. At most
+;; bibtex-entry-kill-ring-max items are kept here.
+
+(defvar bibtex-entry-kill-ring-yank-pointer nil)
+;; The tail of bibtex-entry-kill-ring whose car is the last item yanked.
+
+(defvar bibtex-last-kill-command nil)
+;; Holds the type of the last kill command (either 'field or 'entry)
+
+(defvar bibtex-strings nil)
+;; Candidates for bibtex-complete-string. Initialized from
+;; bibtex-predefined-strings and bibtex-string-files. This variable is
+;; buffer-local.
+(make-variable-buffer-local 'bibtex-strings)
+
+(defvar bibtex-keys nil)
+;; Candidates for TAB completion when entering a reference key using
+;; the minibuffer. Also used for bibtex-complete-key. Initialized in
+;; bibtex-mode and updated for each new entry. This variable is
+;; buffer-local.
+(make-variable-buffer-local 'bibtex-keys)
+
+(defvar bibtex-buffer-last-parsed-tick nil)
+;; Remembers the value returned by buffer-modified-tick when buffer
+;; was parsed for keys the last time.
+(make-variable-buffer-local 'bibtex-buffer-last-parsed-tick)
+
+(defvar bibtex-parse-idle-timer nil)
+;; Stores if timer is already installed
+
+(defvar bibtex-progress-lastperc nil)
+;; Holds the last reported percentage for the progress message
+
+(defvar bibtex-progress-lastmes nil)
+;; Holds the last reported progress message
+
+(defvar bibtex-progress-interval nil)
+;; Holds the chosen interval
+
+(defvar bibtex-key-history nil)
+;; Used to store the history list for reading keys
- "List of (entry-name (required optional) (crossref-required crossref-optional))
-tripples. If the third element is nil, then the first pair can be used. Required
-and optional are lists of strings. All entry creation functions use this variable
-to generate entries, and bibtex-entry ensures the entry type is valid. This
-variable can be used for example to make bibtex manipulate a different set of entry
-types, e.g. a crossreference document of organization types.")
+(defvar bibtex-entry-type-history nil)
+;; Used to store the history list for reading entry types
+(defvar bibtex-field-history nil)
+;; Used to store the history list for reading field names
-;;; A bibtex file is a sequence of entries, either string definitions
-;;; or reference entries. A reference entry has a type part, a
-;;; key part, and a comma-separated sequence of fields. A string
-;;; entry has a single field. A field has a left and right part,
-;;; separated by a '='. The left part is the name, the right part is
-;;; the text. Here come the definitions allowing to create and/or parse
-;;; entries and fields:
+(defvar bibtex-reformat-previous-options nil)
+;; Used to store the last reformat options given
+
+(defvar bibtex-reformat-previous-labels nil)
+;; Used to store the last reformat label option given
+
+\f
+;; Functions to Parse the BibTeX Entries
-;;; fields
(defun bibtex-cfield (name text)
- "Create a regexp for a bibtex field of name NAME and text TEXT"
+ ;; Create a regexp for a BibTeX field of name NAME and text TEXT.
(concat ",[ \t\n]*\\("
name
"\\)[ \t\n]*=[ \t\n]*\\("
text
"\\)"))
-(defconst bibtex-name-in-cfield 1
- "The regexp subexpression number of the name part in bibtex-cfield.")
-(defconst bibtex-text-in-cfield 2
- "The regexp subexpression number of the text part in bibtex-cfield.")
-
-;;; KAWATA Yasuro <yasuro@qqqq.maekawa.is.uec.ac.jp> reported bug that "/"
-;;; was not premitted in field names. The old value of this var was:
-;;; "[A-Za-z][---A-Za-z0-9:_+]*"
-;;; According to the LaTeX manual, page 71, the legal values are letters,
-;;; digits, and punctuation other than comma. Section 2.1 defines
-;;; punctuation as:
-;;; .:;,?!`'()[]-/*@
-;;; and says that += can be used in normal text. Specifically #$%&~_^\{}
-;;; are called out as special chars. Some experimentation with LaTeX
-;;; indicates that # and ~ definitely don't work, but that the following
-;;; citation does! \cite{a0.:;?!`'()[]-/*@_&$^+=|<>}. I chose here to
-;;; permit _ since it was previously allowed, but otherwise to only handle
-;;; punc and +=
-;;; Amendment: I couldn't get a regexp with both "[]"'s and hyphen to
-;;; work. It looks like you need them both to be the first entries in a
-;;; regexp pattern. [alarson:19930315.0900CST]
-
-(defconst bibtex-field-name "[A-Za-z][---A-Za-z0-9.:;?!`'()/*@_+=]*"
- "Regexp defining the name part of a bibtex field.")
-
-;; bibtex-field-text must be able to handle
-;; title = "Proc. Fifteenth Annual" # STOC,
-;; month = "10~" # jan,
-;; year = "{\noopsort{1973c}}1981",
-;; month = apr # "-" # may,
-;; key = {Volume-2},
-;; note = "Volume~2 is listed under Knuth \cite{book-full}"
-;; i have added a few of these, but not all! -- MON
-
-(defconst bibtex-field-const
- "[0-9A-Za-z][---A-Za-z0-9:_+]*"
- "Format of a bibtex field constant.")
+(defconst bibtex-name-in-cfield 1)
+;; The regexp subexpression number of the name part in bibtex-cfield.
+
+(defconst bibtex-text-in-cfield 2)
+;; The regexp subexpression number of the text part in bibtex-cfield.
+
+(defconst bibtex-reference-type "@[^\"#%'(),={} \t\n0-9][^\"#%'(),={} \t\n]*")
+;; Regexp defining the type part of a BibTeX reference entry (almost
+;; the same as bibtex-field-name)
+
+(defconst bibtex-reference-key "[][a-z0-9.:;?!`'/*@+=|()<>&_^$-]+")
+;; Regexp defining the label part of a BibTeX reference entry
+
+(defconst bibtex-field-name "[^\"#%'(),={} \t\n0-9][^\"#%'(),={} \t\n]*")
+;; Regexp defining the name part of a BibTeX field (almost the same as
+;; bibtex-reference-type)
+
+(defconst bibtex-field-const "[][a-z0-9.:;?!`'/*@+=|<>&_^$-]+")
+;; Regexp defining a bibtex field constant
+
+(defconst bibtex-field-string-part-not-braced
+ "[^{}]")
+;; Match field string part without braces
+
+(defconst bibtex-field-string-part-no-inner-braces
+ (concat
+ "{"
+ bibtex-field-string-part-not-braced "*"
+ "}"))
+;; Match field string part with no inner braces
+
+(defconst bibtex-field-string-part-1-inner-brace
+ (concat
+ "{"
+ "\\("
+ bibtex-field-string-part-not-braced
+ "\\|"
+ "\\(" bibtex-field-string-part-no-inner-braces "\\)"
+ "\\)*"
+ "}"))
+;; Match field string part with at most 1 inner brace
+
+(defconst bibtex-field-string-part-2-inner-braces
+ (concat
+ "{"
+ "\\("
+ bibtex-field-string-part-not-braced
+ "\\|"
+ "\\(" bibtex-field-string-part-1-inner-brace "\\)"
+ "\\)*"
+ "}"))
+;; Match field string part with at most 2 inner braces
+
+(defconst bibtex-field-string-part-3-inner-braces
+ (concat
+ "{"
+ "\\("
+ bibtex-field-string-part-not-braced
+ "\\|"
+ "\\(" bibtex-field-string-part-2-inner-braces "\\)"
+ "\\)*"
+ "}"))
+;; Match field string part with at most 3 inner braces
+
+(defconst bibtex-field-string-braced
+ bibtex-field-string-part-3-inner-braces)
+;; Match braced field string with inner nesting level of braces at most 3
+
+(defconst bibtex-field-string-quoted
+ (concat
+ "\""
+ "\\("
+ "[^\"\\]" ;; every character except quote or backslash
+ "\\|"
+;; "\\(" "\"[a-z-]" "\\)" ;; a quote followed by a letter or dash
+;; "\\|"
+;; last two lines commented out until lines like
+;; author = "Stefan Sch"of"
+;; are supported by BibTeX
+ "\\(" "\\\\\\(.\\|\n\\)" "\\)" ;; a backslash followed by any character
+ "\\)*"
+ "\""))
+;; Match quoted field string
(defconst bibtex-field-string
(concat
- "\"[^\"]*[^\\\\]\"\\|\"\"")
- "Match either a string or an empty string.")
+ "\\(" bibtex-field-string-braced "\\)"
+ "\\|"
+ "\\(" bibtex-field-string-quoted "\\)"))
+;; Match a braced or quoted string
(defconst bibtex-field-string-or-const
- (concat bibtex-field-const "\\|" bibtex-field-string)
- "Match either bibtex-field-string or bibtex-field-const.")
+ (concat bibtex-field-const "\\|" bibtex-field-string))
+;; Match either bibtex-field-string or bibtex-field-const.
(defconst bibtex-field-text
(concat
"\\(" bibtex-field-string-or-const "\\)"
- "\\([ \t\n]+#[ \t\n]+\\(" bibtex-field-string-or-const "\\)\\)*\\|"
- "{[^{}]*[^\\\\]}")
- "Regexp defining the text part of a bibtex field: either a string, or
-an empty string, or a constant followed by one or more # / constant pairs.
-Also matches simple {...} patterns.")
-
-;(defconst bibtex-field-text
-; "\"[^\"]*[^\\\\]\"\\|\"\"\\|[0-9A-Za-z][---A-Za-z0-9:_+]*"
-; "Regexp defining the text part of a bibtex field: either a string, or an empty string, or a constant.")
+ "\\([ \t\n]+#[ \t\n]+\\(" bibtex-field-string-or-const "\\)\\)*"))
+;; Regexp defining the text part of a BibTeX field: either a string,
+;; or an empty string, or a constant followed by one or more # /
+;; constant pairs.
(defconst bibtex-field
- (bibtex-cfield bibtex-field-name bibtex-field-text)
- "Regexp defining the format of a bibtex field")
-
-(defconst bibtex-name-in-field bibtex-name-in-cfield
- "The regexp subexpression number of the name part in bibtex-field")
-(defconst bibtex-text-in-field bibtex-text-in-cfield
- "The regexp subexpression number of the text part in bibtex-field")
-
-;;; references
-(defconst bibtex-reference-type
- "@[A-Za-z]+"
- "Regexp defining the type part of a bibtex reference entry")
+ (bibtex-cfield bibtex-field-name bibtex-field-text))
+;; Regexp defining the format of a BibTeX field.
+
+(defconst bibtex-name-in-field bibtex-name-in-cfield)
+;; The regexp subexpression number of the name part in BibTeX-field.
+
+(defconst bibtex-text-in-field bibtex-text-in-cfield)
+;; The regexp subexpression number of the text part in BibTeX-field.
+
(defconst bibtex-reference-head
(concat "^[ \t]*\\("
bibtex-reference-type
- "\\)[ \t]*[({]\\("
- bibtex-field-name
- "\\)")
- "Regexp defining format of the header line of a bibtex reference entry")
-(defconst bibtex-type-in-head 1
- "The regexp subexpression number of the type part in bibtex-reference-head")
-(defconst bibtex-key-in-head 2
- "The regexp subexpression number of the key part in
-bibtex-reference-head")
-
-(defconst bibtex-reference
- (concat bibtex-reference-head
- "\\([ \t\n]*" bibtex-field "\\)*"
- "[ \t\n]*[})]")
- "Regexp defining the format of a bibtex reference entry")
-(defconst bibtex-type-in-reference bibtex-type-in-head
- "The regexp subexpression number of the type part in bibtex-reference")
-(defconst bibtex-key-in-reference bibtex-key-in-head
- "The regexp subexpression number of the key part in
-bibtex-reference")
-
-;;; strings
+ "\\)[ \t]*[({][ \t]*\\("
+ bibtex-reference-key
+ "\\)"))
+;; Regexp defining format of the header line of a BibTeX reference
+;; entry.
+
+(defconst bibtex-reference-maybe-empty-head
+ (concat bibtex-reference-head "?"))
+;; Regexp defining format of the header line of a maybe empty
+;; BibTeX reference entry (without reference key).
+
+(defconst bibtex-type-in-head 1)
+;; The regexp subexpression number of the type part in
+;; bibtex-reference-head.
+
+(defconst bibtex-key-in-head 2)
+;; The regexp subexpression number of the key part in
+;; bibtex-reference-head.
+
+(defconst bibtex-reference-infix (concat "[ \t\n]*" bibtex-field))
+;; Regexp defining the (repeatable) infix of a bibtex reference
+
+(defconst bibtex-reference-postfix "[ \t\n]*,?[ \t\n]*[})]")
+;; Regexp defining the postfix of a bibtex reference
+
+(defconst bibtex-key-in-reference bibtex-key-in-head)
+;; The regexp subexpression number of the key part in
+;; bibtex-reference.
+
(defconst bibtex-string
- (concat "^[ \t]*@[sS][tT][rR][iI][nN][gG][ \t\n]*[({][ \t\n]*\\("
- bibtex-field-name
+ (concat "^[ \t]*@string[ \t\n]*[({][ \t\n]*\\("
+ bibtex-reference-key
"\\)[ \t\n]*=[ \t\n]*\\("
bibtex-field-text
- "\\)[ \t\n]*[})]")
- "Regexp defining the format of a bibtex string entry")
-(defconst bibtex-name-in-string 1
- "The regexp subexpression of the name part in bibtex-string")
-(defconst bibtex-text-in-string 2
- "The regexp subexpression of the text part in bibtex-string")
-
-(defconst bibtex-name-alignment 2
- "Alignment for the name part in BibTeX fields.
-Chosen on aesthetic grounds only.")
-
-(defconst bibtex-text-alignment (length " organization = ")
- "Alignment for the text part in BibTeX fields.
-Equal to the space needed for the longest name part.")
-
-(defun bibtex-current-entry-label (&optional include-cite kill)
- "Return the label of the bibtex entry containing, or preceding point.
-Optional argument INCLUDE-CITE, if true means put a '\\cite{}' around the
-returned value. Second optional argument KILL, if true, means place the
-returned value in the kill buffer. Interactively; providing prefix
-argument makes INCLUDE-CITE true, and kill is true by default.
-
-Rationale:
-The intention is that someone will write a function that can be bound to
-a mouse key so that people entering TeX can just mouse on the bibtex entry
-and have the citation key inserted at the current point (which will almost
-certainly be in some other buffer). In the interim this function is
-marginally useful for keyboard binding and is not bound by default.
-Suggested binding is ^C-k."
- (interactive (list current-prefix-arg t))
+ "\\)[ \t\n]*[})]"))
+;; Regexp defining the format of a BibTeX string entry.
+
+(defconst bibtex-key-in-string 1)
+;; The regexp subexpression of the name part in bibtex-string.
+
+(defconst bibtex-text-in-string 2)
+;; The regexp subexpression of the text part in bibtex-string.
+
+(defvar bibtex-font-lock-keywords
+ (list
+ ;; reference type and reference label
+ (list bibtex-reference-maybe-empty-head
+ (list bibtex-type-in-head 'font-lock-function-name-face)
+ (list bibtex-key-in-head 'font-lock-constant-face nil t))
+ ;; comments
+ (list
+ (concat "^\\([ \t]*" bibtex-comment-start ".*\\)$")
+ 1 'font-lock-comment-face)
+ ;; optional field names (treated as comments)
+ (list
+ (concat "^[ \t]*\\(OPT" bibtex-field-name "\\)[ \t]*=")
+ 1 'font-lock-comment-face)
+ ;; field names
+ (list (concat "^[ \t]*\\(" bibtex-field-name "\\)[ \t]*=")
+ 1 'font-lock-variable-name-face)
+ "*Default expressions to highlight in BibTeX mode."))
+;; now all needed patterns are defined
+
+\f
+;; Helper Functions
+
+(defun bibtex-delete-whitespace ()
+ ;; Delete all whitespace starting at point
+ (if (looking-at "[ \t\n]+")
+ (delete-region (point) (match-end 0))))
+
+(defun bibtex-current-line ()
+ ;; this computes line number of point regardless whether the buffer
+ ;; is narrowed
+ (+ (count-lines 1 (point))
+ (if (equal (current-column) 0) 1 0)))
+
+(defun bibtex-member-of-regexp (string list)
+ ;; Return non-nil if STRING is exactly matched by an element of
+ ;; LIST. The value is actually the tail of LIST whose
+ ;; car matches STRING.
+ (let (case-fold-search)
+ (while
+ (and list (not (string-match (concat "^" (car list) "$") string)))
+ (setq list (cdr list)))
+ list))
+
+(defun bibtex-assoc-of-regexp (string alist)
+ ;; Return non-nil if STRING is exactly matched by the car of an
+ ;; element of LIST (case ignored). The value is actually the element
+ ;; of LIST whose car matches STRING.
+ (let ((case-fold-search t))
+ (while
+ (and alist
+ (not (string-match (concat "^" (car (car alist)) "$") string)))
+ (setq alist (cdr alist)))
+ (car alist)))
+
+(defun bibtex-skip-to-valid-entry (&optional backward)
+ ;; If not at beginning of valid BibTeX entry, move to beginning of
+ ;; the next valid one. With argument backward non-nil, move backward
+ ;; to beginning of previous valid one. A valid entry is a
+ ;; syntactical correct one with type contained in
+ ;; bibtex-entry-field-alist or, if bibtex-sort-ignore-string-entries
+ ;; is nil, a syntactical correct string entry.
+ (let ((case-fold-search t)
+ (valid-bibtex-entry
+ (concat
+ "@[ \t]*\\("
+ (mapconcat
+ (lambda (type)
+ (concat "\\(" (car type) "\\)"))
+ bibtex-entry-field-alist
+ "\\|")
+ "\\)"))
+ found)
+ (while (and (not found)
+ (not (if backward
+ (bobp)
+ (eobp))))
+ (let ((pnt (point)))
+ (cond
+ ((looking-at valid-bibtex-entry)
+ (if (and
+ (bibtex-search-reference nil nil t)
+ (equal (match-beginning 0) pnt))
+ (setq found t)))
+ ((and (not bibtex-sort-ignore-string-entries)
+ (looking-at bibtex-string))
+ (setq found t)))
+ (if found
+ (goto-char pnt)
+ (if backward
+ (progn
+ (goto-char (1- pnt))
+ (if (re-search-backward "^[ \t]*\\(@\\)" nil 'move)
+ (goto-char (match-beginning 1))))
+ (goto-char (1+ pnt))
+ (if (re-search-forward "^[ \t]*@" nil 'move)
+ (forward-char -1))))))))
+
+(defun bibtex-map-entries (fun)
+ ;; Call FUN for each BibTeX entry starting with the current. Do this
+ ;; to the end of the file. FUN is called with one argument, the key
+ ;; of the entry, and with point inside the entry. If
+ ;; bibtex-sort-ignore-string-entries is non-nil, FUN will not be
+ ;; called for @String entries.
+ (let ((case-fold-search t))
+ (bibtex-beginning-of-entry)
+ (while (re-search-forward bibtex-reference-maybe-empty-head nil t)
+ (let ((pnt (point))
+ (reference-type
+ (downcase (buffer-substring-no-properties
+ (1+ (match-beginning bibtex-type-in-head))
+ (match-end bibtex-type-in-head))))
+ (reference-key
+ (if (match-beginning bibtex-key-in-head)
+ (buffer-substring-no-properties
+ (match-beginning bibtex-key-in-head)
+ (match-end bibtex-key-in-head))
+ "")))
+ (if (or
+ (and
+ (not bibtex-sort-ignore-string-entries)
+ (string-equal "string" (downcase reference-type)))
+ (assoc-ignore-case reference-type bibtex-entry-field-alist))
+ (funcall fun reference-key))
+ (goto-char pnt)
+ (bibtex-end-of-entry)))))
+
+(defun bibtex-progress-message (&optional flag interval)
+ ;; echos a message about progress of current buffer
+ ;; if flag is a string, the message is initialized (in this case a
+ ;; value for INTERVAL may be given as well (if not this is set to 5))
+ ;; if flag is done, the message is deinitialized
+ ;; if flag is absent, a message is echoed if point was incremented
+ ;; at least INTERVAL percent since last message was echoed
+ (let* ((size (- (point-max) (point-min)))
+ (perc (if (= size 0)
+ 100
+ (/ (* 100 (- (point) (point-min))) size))))
+ (if (or (and (not flag)
+ (>= perc
+ (+ bibtex-progress-interval bibtex-progress-lastperc)))
+ (stringp flag))
+ (progn
+ (if (stringp flag)
+ (progn
+ (setq bibtex-progress-lastmes flag)
+ (if interval
+ (setq bibtex-progress-interval interval)
+ (setq bibtex-progress-interval 5))))
+ (setq bibtex-progress-lastperc perc)
+ (message (concat bibtex-progress-lastmes " (%d%%)") perc))
+ (if (equal flag 'done)
+ (progn
+ (message (concat bibtex-progress-lastmes " (done)"))
+ (setq bibtex-progress-lastmes nil))))))
+
+
+(defun bibtex-field-left-delimiter ()
+ ;; returns a string dependent on bibtex-field-delimiters
+ (if (equal bibtex-field-delimiters 'braces)
+ "{"
+ "\""))
+
+(defun bibtex-field-right-delimiter ()
+ ;; returns a string dependent on bibtex-field-delimiters
+ (if (equal bibtex-field-delimiters 'braces)
+ "}"
+ "\""))
+
+(defun bibtex-entry-left-delimiter ()
+ ;; returns a string dependent on bibtex-field-delimiters
+ (if (equal bibtex-entry-delimiters 'braces)
+ "{"
+ "("))
+
+(defun bibtex-entry-right-delimiter ()
+ ;; returns a string dependent on bibtex-field-delimiters
+ (if (equal bibtex-entry-delimiters 'braces)
+ "}"
+ ")"))
+
+(defun bibtex-search-reference
+ (empty-head &optional bound noerror backward)
+ ;; A helper function necessary since the failure stack size limit for
+ ;; regexps was reduced in emacs 19.32.
+ ;; It searches for a BibTeX reference (maybe with an empty head if
+ ;; EMPTY-HEAD is t).
+ ;; BOUND and NOERROR are exactly as in re-search-forward. If
+ ;; BACKWARD is non-nil, search is done in reverse direction. After
+ ;; call to this function MATCH-BEGINNING and MATCH-END functions are
+ ;; defined, but only for the head part of the reference (especially
+ ;; (match-end 0) just gives the end of the head part).
+ (let ((pnt (point))
+ (prefix (if empty-head
+ bibtex-reference-maybe-empty-head
+ bibtex-reference-head))
+ (infix bibtex-reference-infix)
+ (postfix bibtex-reference-postfix))
+ (if backward
+ (let (found)
+ (while (and (not found)
+ (re-search-backward prefix bound noerror))
+ (setq found (bibtex-search-reference empty-head pnt t)))
+ (if found
+ (goto-char (match-beginning 0))
+ (if (equal noerror nil)
+ ;; yell
+ (error "Search of BibTeX reference failed."))
+ (if (equal noerror t)
+ ;; don't move
+ (goto-char pnt))
+ nil))
+ (let ((limit (if bound bound (point-max)))
+ md
+ found)
+ (while (and (not found)
+ (re-search-forward prefix bound noerror))
+ (setq md (match-data))
+ ;; save match-data of prefix regexp
+ (let ((entry-closer
+ (if (save-excursion
+ (goto-char (match-end bibtex-type-in-head))
+ (looking-at "[ \t]*("))
+ ;; entry opened with parenthesis
+ ")"
+ "}")))
+ (while (and
+ (looking-at infix)
+ (<= (match-end 0) limit))
+ (goto-char (match-end 0)))
+ ;; This matches the infix* part. The AND construction assures
+ ;; that BOUND is respected.
+ (if (and (looking-at postfix)
+ (string-equal
+ (buffer-substring-no-properties
+ (1- (match-end 0)) (match-end 0))
+ entry-closer)
+ (<= (match-end 0) limit))
+ (progn
+ (re-search-forward postfix)
+ (setq found t)))))
+ (if found
+ (progn
+ (store-match-data md)
+ ;; to set match-beginning/end again
+ (point))
+ (if (equal noerror nil)
+ ;; yell
+ (error "Search of BibTeX reference failed."))
+ (if (equal noerror t)
+ ;; don't move
+ (goto-char pnt))
+ nil)))))
+
+(defun bibtex-flash-head ()
+ ;; Flash at BibTeX reference head before point, if exists.
+ (let ((case-fold-search t)
+ flash)
+ (cond ((re-search-backward bibtex-reference-head nil t)
+ (goto-char (match-beginning bibtex-type-in-head))
+ (setq flash (match-end bibtex-key-in-reference)))
+ (t
+ (end-of-line)
+ (skip-chars-backward " \t")
+ (setq flash (point))
+ (beginning-of-line)
+ (skip-chars-forward " \t")))
+ (if (pos-visible-in-window-p (point))
+ (sit-for 1)
+ (message "From: %s"
+ (buffer-substring (point) flash)))))
+
+(defun bibtex-make-optional-field (e-t)
+ "Makes an optional field named E-T in current BibTeX entry."
+ (if (consp e-t)
+ (bibtex-make-field (cons (concat "OPT" (car e-t)) (cdr e-t)))
+ (bibtex-make-field (concat "OPT" e-t))))
+
+(defun bibtex-move-outside-of-entry ()
+ ;; Make sure we are outside of a BibTeX entry.
+ (bibtex-end-of-entry)
+ (skip-chars-forward " \t\n"))
+
+(defun bibtex-beginning-of-first-entry ()
+ ;; Go to the beginning of the first BibTeX entry in buffer. Return
+ ;; point.
+ (goto-char (point-min))
+ (if (re-search-forward "^[ \t]*@" nil 'move)
+ (beginning-of-line))
+ (point))
+
+(defun bibtex-beginning-of-last-entry ()
+ ;; Go to the beginning of the last BibTeX entry in buffer.
+ (goto-char (point-max))
+ (if (re-search-backward "^[ \t]*@" nil 'move)
+ (beginning-of-line))
+ (point))
+
+(defun bibtex-inside-field ()
+ ;; Try to avoid point being at end of a BibTeX field.
+ (end-of-line)
+ (skip-chars-backward " \t")
+ (cond ((= (preceding-char) ?,)
+ (forward-char -2)))
+ (cond ((or
+ (= (preceding-char) ?})
+ (= (preceding-char) ?\"))
+ (forward-char -1))))
+
+(defun bibtex-enclosing-field (&optional noerr)
+ ;; Search for BibTeX field enclosing point. Point moves to end of
+ ;; field. Use match-beginning and match-end to parse the field. If
+ ;; NOERR is non-nil, no error is signalled. In this case, t is
+ ;; returned on success, nil otherwise.
+ (let ((case-fold-search t)
+ (old-point (point))
+ (boe (bibtex-beginning-of-entry))
+ (success t))
+ (goto-char old-point)
+ (if (not (re-search-backward
+ (bibtex-cfield bibtex-field-name "")
+ boe t))
+ ;; Search possible beginning of field
+ (progn
+ (goto-char old-point)
+ (if noerr
+ (setq success nil)
+ (error "Can't find enclosing BibTeX field.")))
+ (if (or (not (re-search-forward bibtex-field nil t))
+ (< (match-end 0) old-point)
+ (> (match-beginning 0) old-point))
+ (progn
+ (goto-char old-point)
+ (if noerr
+ (setq success nil)
+ (error "Can't find enclosing BibTeX field.")))))
+ success))
+
+(defun bibtex-enclosing-reference-maybe-empty-head ()
+ ;; Search for BibTeX reference enclosing point. Point moves to
+ ;; end of reference. Beginning (but not end) of reference is given
+ ;; by (match-beginning 0).
+ (let ((case-fold-search t)
+ (old-point (point)))
+ (if (not
+ (re-search-backward
+ bibtex-reference-maybe-empty-head nil t))
+ (progn
+ (error "Can't find enclosing BibTeX reference.")
+ (goto-char old-point)))
+ (goto-char (match-beginning bibtex-type-in-head))
+ (if (not
+ (bibtex-search-reference t nil t))
+ (progn
+ (error "Can't find enclosing BibTeX reference.")
+ (goto-char old-point)))))
+
+(defun bibtex-insert-current-kill (n)
+ (if (not bibtex-last-kill-command)
+ (error "BibTeX kill ring is empty.")
+ (let* ((kr (if (equal bibtex-last-kill-command 'field)
+ 'bibtex-field-kill-ring
+ 'bibtex-entry-kill-ring))
+ (kryp (if (equal bibtex-last-kill-command 'field)
+ 'bibtex-field-kill-ring-yank-pointer
+ 'bibtex-entry-kill-ring-yank-pointer))
+ (ARGth-kill-element
+ (nthcdr
+ (mod (- n (length (eval kryp))) (length (eval kr)))
+ (eval kr)))
+ (current (car (set kryp ARGth-kill-element))))
+ (cond
+ ((equal bibtex-last-kill-command 'field)
+ (let (bibtex-help-message)
+ (bibtex-find-text nil t)
+ (if (looking-at "[}\"]")
+ (forward-char)))
+ (set-mark (point))
+ (message "Mark set")
+ (bibtex-make-field (list (elt current 1) nil (elt current 2)) t))
+ ((equal bibtex-last-kill-command 'entry)
+ (if (not (eobp))
+ (bibtex-beginning-of-entry))
+ (set-mark (point))
+ (message "Mark set")
+ (insert (elt current 1)))
+ (t
+ (error
+ "Unknown tag field: %s. Please submit a bug report."
+ bibtex-last-kill-command))))))
+
+(defun bibtex-format-entry ()
+ ;; Helper function for bibtex-clean-entry. Formats current entry
+ ;; according to variable bibtex-entry-format.
+ (let ((case-fold-search t)
+ (beg (point))
+ (start (bibtex-beginning-of-entry))
+ crossref-there
+ alternatives-there
+ non-empty-alternative)
+ (let ((end (copy-marker (bibtex-end-of-entry))))
+ (if (equal start (marker-position end))
+ (error "Not on a known BibTeX entry.")
+ (goto-char start)
+ (while (re-search-forward bibtex-field end t)
+ ;; determine if reference has crossref entry and if at least
+ ;; one alternative is non-empty
+ (let ((begin-name (match-beginning bibtex-name-in-field))
+ (end-name (match-end bibtex-name-in-field))
+ (begin-text (match-beginning bibtex-text-in-field))
+ (end-text (match-end bibtex-text-in-field)))
+ (goto-char begin-name)
+ (if (looking-at "ALT")
+ (progn
+ (setq alternatives-there t)
+ (goto-char begin-text)
+ (if (not (looking-at "\\(\"\"\\)\\|\\({}\\)"))
+ (setq non-empty-alternative t))))
+ (if (string-match
+ "\\(OPT\\)?crossref"
+ (buffer-substring-no-properties begin-name end-name))
+ (progn
+ (setq
+ crossref-there
+ (buffer-substring-no-properties
+ (1+ begin-text) (1- end-text)))
+ (if (equal crossref-there "")
+ (setq crossref-there nil))))))
+ (if (and alternatives-there
+ (not non-empty-alternative))
+ (progn
+ (goto-char beg)
+ (error "All alternatives are empty.")))
+ (goto-char start)
+ (re-search-forward bibtex-reference-type end)
+ (let* ((begin-type (1+ (match-beginning 0)))
+ (end-type (match-end 0))
+ (reference-type
+ (downcase
+ (buffer-substring-no-properties begin-type end-type)))
+ (entry-list
+ (assoc-ignore-case reference-type bibtex-entry-field-alist))
+ (req (elt (elt entry-list 1) 0))
+ (creq (elt (elt entry-list 2) 0))
+ (format (if (equal bibtex-entry-format t)
+ '(realign opts-or-alts numerical-fields
+ last-comma page-dashes delimiters
+ unify-case inherit-booktitle)
+ bibtex-entry-format))
+ field-done)
+ (if (memq 'unify-case format)
+ (progn
+ (delete-region begin-type end-type)
+ (insert (car entry-list))))
+ (if (memq 'delimiters format)
+ (progn
+ (goto-char end-type)
+ (skip-chars-forward " \t\n")
+ (delete-char 1)
+ (insert (bibtex-entry-left-delimiter))))
+ (goto-char start)
+ (while (re-search-forward bibtex-field end t)
+ (let* ((begin-field
+ (copy-marker (match-beginning 0)))
+ (end-field
+ (copy-marker (match-end 0)))
+ (begin-name
+ (copy-marker (match-beginning bibtex-name-in-field)))
+ (end-name
+ (copy-marker (match-end bibtex-name-in-field)))
+ (begin-text
+ (copy-marker (match-beginning bibtex-text-in-field)))
+ (end-text
+ (copy-marker (match-end bibtex-text-in-field)))
+ (field-name
+ (buffer-substring-no-properties
+ (if (string-match
+ "^OPT\\|ALT$"
+ (buffer-substring-no-properties
+ begin-name (+ begin-name 3)))
+ (+ begin-name 3)
+ begin-name)
+ end-name)))
+ (cond
+ ((and
+ (memq 'opts-or-alts format)
+ (progn (goto-char begin-name)
+ (looking-at "OPT\\|ALT")))
+ (goto-char begin-text)
+ (if (looking-at "\\(\"\"\\)\\|\\({}\\)")
+ ;; empty: delete whole field if really optional
+ ;; (missing crossref handled) or complain
+ (if (and
+ (progn
+ (goto-char begin-name)
+ (looking-at "OPT"))
+ (not crossref-there)
+ (assoc-ignore-case field-name req))
+ ;; field is not really optional
+ (progn
+ (goto-char begin-name)
+ (bibtex-remove-OPT-or-ALT)
+ (error
+ "Mandatory field ``%s'' is empty." field-name))
+ ;; field is optional
+ (delete-region begin-field end-field))
+ ;; otherwise: not empty, delete "OPT"
+ (goto-char begin-name)
+ (bibtex-remove-OPT-or-ALT)))
+ ((and
+ (memq 'numerical-fields format)
+ (progn
+ (goto-char begin-text)
+ (looking-at "\\(\"[0-9]+\"\\)\\|\\({[0-9]+}\\)")))
+ (goto-char end-text)
+ (delete-char -1)
+ (goto-char begin-text)
+ (delete-char 1))
+ (t
+ (if (memq 'delimiters format)
+ (progn
+ (goto-char begin-text)
+ (while (and
+ (<= (point) end-text)
+ (re-search-forward
+ bibtex-field-string-or-const end-text t))
+ (let ((end (point)))
+ (goto-char (match-beginning 0))
+ (if (or
+ (and
+ (equal bibtex-field-delimiters 'double-quotes)
+ (looking-at bibtex-field-string-braced))
+ (and
+ (equal bibtex-field-delimiters 'braces)
+ (looking-at bibtex-field-string-quoted)))
+ (progn
+ (goto-char (match-end 0))
+ (delete-backward-char 1)
+ (insert (bibtex-field-right-delimiter))
+ (goto-char (match-beginning 0))
+ (delete-char 1)
+ (insert (bibtex-field-left-delimiter))))
+ (goto-char end)))))
+ (if (and
+ (memq 'page-dashes format)
+ (string-match "^\\(OPT\\)?pages$" (downcase field-name))
+ (progn
+ (goto-char begin-text)
+ (looking-at
+ "\\([\"{][0-9]+\\)[ \t\n]*--?[ \t\n]*\\([0-9]+[\"}]\\)")))
+ (replace-match "\\1-\\2"))
+ (if (and
+ (memq 'inherit-booktitle format)
+ (equal (downcase field-name) "booktitle")
+ (progn
+ (goto-char begin-text)
+ (looking-at "\\(\"\"\\)\\|\\({}\\)"))
+ crossref-there
+ (not (bibtex-find-entry-location crossref-there t)))
+ ;; booktitle field empty and crossref entry found
+ ;; --> insert title field of crossreferenced entry if there
+ (let ((end-of-crefd-entry (bibtex-end-of-entry)))
+ (bibtex-beginning-of-entry)
+ (if (re-search-forward
+ (bibtex-cfield "title" bibtex-field-text)
+ end-of-crefd-entry t)
+ (progn
+ (goto-char begin-text)
+ (forward-char)
+ (insert
+ (buffer-substring-no-properties
+ (1+ (match-beginning bibtex-text-in-field))
+ (1- (match-end bibtex-text-in-field))))))))
+ (if (progn
+ (goto-char begin-text)
+ (looking-at "\\(\"\"\\)\\|\\({}\\)"))
+ ;; if empty field, complain
+ (progn
+ (forward-char)
+ (if (or (and
+ crossref-there
+ (assoc-ignore-case
+ field-name creq))
+ (and
+ (not crossref-there)
+ (assoc-ignore-case
+ field-name req)))
+ (error
+ "Mandatory field ``%s'' is empty." field-name))))
+ (if (memq 'unify-case format)
+ (let* ((fl
+ (car (cdr (assoc-ignore-case
+ reference-type
+ bibtex-entry-field-alist))))
+ (field-list
+ (append
+ (elt fl 0)
+ (elt fl 1)
+ bibtex-user-optional-fields))
+ (new-field-name
+ (car
+ (assoc-ignore-case field-name field-list))))
+ (goto-char begin-name)
+ (if new-field-name
+ (progn
+ (delete-region begin-name end-name)
+ (insert new-field-name))
+ (downcase-region begin-name end-name))))
+ (setq field-done t)))
+ (if (not field-done)
+ (goto-char begin-field)
+ (setq field-done nil)
+ (goto-char end-field))))
+ (if (looking-at (bibtex-field-right-delimiter))
+ (forward-char))
+ (if (memq 'last-comma format)
+ (cond ((and
+ bibtex-comma-after-last-field
+ (not (looking-at ",")))
+ (insert ","))
+ ((and
+ (not bibtex-comma-after-last-field)
+ (looking-at ","))
+ (delete-char 1))))
+ (if (looking-at ",")
+ (forward-char))
+ (if (memq 'delimiters format)
+ (progn
+ (skip-chars-forward " \t\n")
+ (delete-char 1)
+ (insert (bibtex-entry-right-delimiter))))
+ (if (memq 'realign format)
+ (bibtex-fill-entry)))))))
+
+(defun bibtex-autokey-change (string change-list)
+ ;; Returns a string where some regexps are changed according to
+ ;; change-list. Every item of change-list is an (old-regexp
+ ;; new-string) pair.
+ (let (case-fold-search
+ (return-string string)
+ (index 0)
+ (len (length change-list))
+ change-item)
+ (while (< index len)
+ (setq change-item (elt change-list index))
+ (while (string-match (car change-item) return-string)
+ (setq
+ return-string
+ (concat (substring return-string 0 (match-beginning 0))
+ (elt change-item 1)
+ (substring return-string (match-end 0)))))
+ (setq index (1+ index)))
+ return-string))
+
+(defun bibtex-autokey-abbrev (string len)
+ ;; Returns an abbreviation of string with at least len
+ ;; characters. String is aborted only after a consonant or at the
+ ;; word end. If len is not a number, string is returned unchanged.
+ (cond ((or
+ (not (numberp len))
+ (<= (length string) len))
+ string)
+ ((equal len 0)
+ "")
+ (t
+ (let* ((case-fold-search t)
+ (abort-char
+ (string-match "[^aeiou]" string (1- len))))
+ (if abort-char
+ (substring string 0 (1+ abort-char))
+ string)))))
+
+(defun bibtex-autokey-get-namefield (min max)
+ ;; returns the contents of the name field of the current entry
+ ;; does some modifications based on `bibtex-autokey-name-change-strings'
+ ;; and removes newlines unconditionally
+ (goto-char min)
+ (let ((case-fold-search t))
+ (if (re-search-forward
+ (bibtex-cfield "\\(author\\)\\|\\(editor\\)" bibtex-field-text)
+ max t)
+ (bibtex-autokey-change
+ (buffer-substring-no-properties
+ (1+ (match-beginning (+ bibtex-text-in-cfield 2)))
+ (1- (match-end (+ bibtex-text-in-cfield 2))))
+ (append bibtex-autokey-name-change-strings '(("\n" " "))))
+ "")))
+
+(defun bibtex-autokey-get-names (namefield)
+ ;; gathers all names in namefield into a list
+ (let ((case-fold-search t)
+ names)
+ (while (not (equal namefield ""))
+ (let (name)
+ (if (string-match "[ \t\n]and[ \t\n]" namefield)
+ (setq name (substring namefield 0 (match-beginning 0))
+ namefield (substring namefield (match-end 0)))
+ (setq name namefield
+ namefield ""))
+ (setq names (append names (list name)))))
+ names))
+
+(defun bibtex-autokey-demangle-name (fullname)
+ ;; gets the `last' part from a well-formed name
+ (let* (case-fold-search
+ (lastname
+ (if (string-match "," fullname)
+ ;; name is of the form "von Last, First" or
+ ;; "von Last, Jr, First"
+ ;; --> take only the part before the comma
+ (let ((von-last
+ (substring fullname 0 (match-beginning 0))))
+ (if (string-match "^[a-z]" von-last)
+ ;; von-last has a "von" part --> take the "last" part
+ (if (string-match "[ \t][A-Z]" von-last)
+ (substring von-last (1+ (match-beginning 0)))
+ (error
+ "Name %s is incorrectly formed" fullname))
+ ;; von-last has no "von" part --> take all
+ von-last))
+ ;; name is of the form "First von Last"
+ (if (string-match "[ \t]" fullname)
+ ;; more than one token
+ (if (string-match "[ \t][a-z]" fullname)
+ ;; there is a "von" part
+ ;; --> take everything after that
+ (if (string-match
+ "[ \t][A-Z]" fullname (match-end 0))
+ (substring fullname (1+ (match-beginning 0)))
+ (error
+ "Name %s is incorrectly formed" fullname))
+ ;; there is no "von" part --> take only the last token
+ (if (string-match " [^ ]*$" fullname)
+ (substring fullname (1+ (match-beginning 0)))
+ (error "Name %s is incorrectly formed" fullname)))
+ ;; only one token --> take it
+ fullname)))
+ (usename
+ (if (string-match "[ \t]+" lastname)
+ ;; lastname consists of two or more tokens
+ ;; --> take only the first one
+ (substring lastname 0 (match-beginning 0))
+ lastname)))
+ (funcall bibtex-autokey-name-case-convert usename)))
+
+(defun bibtex-autokey-get-namelist (namefield)
+ ;; gets namefield, performs abbreviations on the last parts, and
+ ;; return results as a list
+ (mapcar
+ (lambda (fullname)
+ (setq
+ fullname (substring fullname (string-match "[^ \t]" fullname)))
+ (bibtex-autokey-abbrev
+ (bibtex-autokey-demangle-name fullname)
+ bibtex-autokey-name-length))
+ (bibtex-autokey-get-names namefield)))
+
+(defun bibtex-autokey-get-yearfield (min max)
+ ;; get year field from current or maybe crossreferenced entry
+ (let ((case-fold-search t))
+ (goto-char min)
+ (if (re-search-forward
+ (bibtex-cfield "year" bibtex-field-text) max t)
+ (let ((year (buffer-substring-no-properties
+ (match-beginning bibtex-text-in-cfield)
+ (match-end bibtex-text-in-cfield))))
+ (string-match "[^0-9]*\\([0-9]+\\)" year)
+ (substring year (match-beginning 1) (match-end 1)))
+ (if bibtex-autokey-year-use-crossref-entry
+ (let ((crossref-field
+ (progn
+ (goto-char min)
+ (if (re-search-forward
+ (bibtex-cfield
+ "\\(OPT\\)?crossref" bibtex-field-text)
+ max t)
+ (buffer-substring-no-properties
+ (1+
+ (match-beginning (+ bibtex-text-in-cfield 1)))
+ (1-
+ (match-end (+ bibtex-text-in-cfield 1))))))))
+ (if (not (bibtex-find-entry-location crossref-field t))
+ (let ((end-of-crefd-entry (bibtex-end-of-entry)))
+ (bibtex-beginning-of-entry)
+ (if (re-search-forward
+ (bibtex-cfield "year" "[0-9]+")
+ end-of-crefd-entry t)
+ (buffer-substring-no-properties
+ (match-beginning bibtex-text-in-cfield)
+ (match-end bibtex-text-in-cfield))
+ ""))
+ ""))
+ ""))))
+
+(defun bibtex-autokey-get-titlestring (min max)
+ ;; get title field contents up to a terminator
+ (let ((case-fold-search t))
+ (let ((titlefield
+ (progn
+ (goto-char min)
+ (if (re-search-forward
+ (bibtex-cfield "title" bibtex-field-text) max t)
+ (bibtex-autokey-change
+ (buffer-substring-no-properties
+ (match-beginning bibtex-text-in-cfield)
+ (match-end bibtex-text-in-cfield))
+ bibtex-autokey-titleword-change-strings)
+ "")))
+ (index 0)
+ (numberofitems
+ (length bibtex-autokey-title-terminators)))
+ (while (< index numberofitems)
+ (if (string-match
+ (elt bibtex-autokey-title-terminators index) titlefield)
+ (setq
+ titlefield (substring titlefield 0 (match-beginning 0))))
+ (setq index (1+ index)))
+ titlefield)))
+
+(defun bibtex-autokey-get-titles (titlestring)
+ ;; gathers words from titlestring into a list. Ignores
+ ;; specific words and uses only a specific amount of words.
+ (let (case-fold-search
+ titlewords
+ titlewords-extra
+ (counter 0))
+ (while (and
+ (not (equal titlestring ""))
+ (or
+ (not (numberp bibtex-autokey-titlewords))
+ (< counter
+ (+ bibtex-autokey-titlewords
+ bibtex-autokey-titlewords-stretch))))
+ (if (string-match "\\b\\w+" titlestring)
+ (let* ((end-match (match-end 0))
+ (titleword
+ (substring titlestring (match-beginning 0) end-match)))
+ (if (bibtex-member-of-regexp
+ titleword
+ bibtex-autokey-titleword-ignore)
+ (setq counter (1- counter))
+ (setq
+ titleword
+ (funcall bibtex-autokey-titleword-case-convert titleword))
+ (if (or (not (numberp bibtex-autokey-titlewords))
+ (< counter bibtex-autokey-titlewords))
+ (setq titlewords (append titlewords (list titleword)))
+ (setq titlewords-extra
+ (append titlewords-extra (list titleword)))))
+ (setq
+ titlestring (substring titlestring end-match)))
+ (setq titlestring ""))
+ (setq counter (1+ counter)))
+ (if (string-match "\\b\\w+" titlestring)
+ titlewords
+ (append titlewords titlewords-extra))))
+
+(defun bibtex-autokey-get-titlelist (titlestring)
+ ;; returns all words in titlestring as a list
+ ;; does some abbreviation on the found words
+ (mapcar
+ (lambda (titleword)
+ (let ((abbrev
+ (bibtex-assoc-of-regexp
+ titleword bibtex-autokey-titleword-abbrevs)))
+ (if abbrev
+ (elt abbrev 1)
+ (bibtex-autokey-abbrev
+ titleword
+ bibtex-autokey-titleword-length))))
+ (bibtex-autokey-get-titles titlestring)))
+
+(defun bibtex-generate-autokey ()
+ "Generates automatically a key from the author/editor and the title field.
+This will only work for entries where each field begins on a separate line.
+The generation algorithm works as follows:
+ 1. Use the value of `bibtex-autokey-prefix-string' as a prefix.
+ 2. If there is a non-empty author (preferred) or editor field,
+ use it as the name part of the key.
+ 3. Change any substring found in
+ `bibtex-autokey-name-change-strings' to the corresponding new
+ one (see documentation of this variable for further detail).
+ 4. For every of at least first `bibtex-autokey-names' names in
+ the name field, determine the last name. If there are maximal
+ `bibtex-autokey-names' + `bibtex-autokey-names-stretch'
+ names, all names are used.
+ 5. From every last name, take at least
+ `bibtex-autokey-name-length' characters (abort only after a
+ consonant or at a word end).
+ 6. Convert all last names according to the conversion function
+ `bibtex-autokey-name-case-convert'.
+ 7. 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 than are used in the name part, prepend the string
+ contained in `bibtex-autokey-additional-names'.
+ 8. Build the year part of the key by truncating the contents of
+ the year field to the rightmost `bibtex-autokey-year-length'
+ digits (useful values are 2 and 4). If the year field is
+ absent, but the entry has a valid crossref field and the
+ variable `bibtex-autokey-year-use-crossref-entry' is non-nil,
+ use the year field of the crossreferenced entry instead.
+ 9. For the title part of the key change the contents of the
+ title field of the reference according to
+ `bibtex-autokey-titleword-change-strings' to the
+ corresponding new one (see documentation of this variable for
+ further detail).
+10. Abbreviate the result to the string up to (but not including)
+ the first occurrence of a regexp matched by the items of
+ `bibtex-autokey-title-terminators' and delete those words which
+ appear in `bibtex-autokey-titleword-ignore'.
+ Build the title part of the key by using at least the first
+ `bibtex-autokey-titlewords' words from this
+ abbreviated title. If the abbreviated title ends after
+ maximal `bibtex-autokey-titlewords' +
+ `bibtex-autokey-titlewords-stretch' words, all
+ words from the abbreviated title are used.
+11. Convert all used titlewords according to the conversion function
+ `bibtex-autokey-titleword-case-convert'.
+12. For every used title word that appears in
+ `bibtex-autokey-titleword-abbrevs' use the corresponding
+ abbreviation (see documentation of this variable for further
+ detail).
+13. From every title word not generated by an abbreviation, take
+ at least `bibtex-autokey-titleword-length' characters (abort
+ only after a consonant or at a word end).
+14. Build the title part of the key by concatenating all
+ abbreviated title words with the string
+ `bibtex-autokey-titleword-separator' between any two.
+15. At least, to get the key, concatenate
+ `bibtex-autokey-prefix-string', the name part, the year part
+ and the title part with `bibtex-autokey-name-year-separator'
+ between the name part and the year part if both are non-empty
+ and `bibtex-autokey-year-title-separator' between the year
+ part and the title part if both are non-empty. If the year
+ part is empty, but not the other two parts,
+ `bibtex-autokey-year-title-separator' is used as well.
+16. If the value of `bibtex-autokey-before-presentation-function'
+ is non-nil, it must be a function taking one argument. This
+ function is then called with the generated key as the
+ argument. The return value of this function (a string) is
+ used as the key.
+17. If the value of `bibtex-autokey-edit-before-use' is non-nil,
+ the key is then presented in the minibuffer to the user,
+ where it can be edited. The key given by the user is then
+ used."
+ (let* ((pnt (point))
+ (min (bibtex-beginning-of-entry))
+ (max (bibtex-end-of-entry))
+ (namefield (bibtex-autokey-get-namefield min max))
+ (name-etal "")
+ (namelist
+ (let ((nl (bibtex-autokey-get-namelist namefield)))
+ (if (or (not (numberp bibtex-autokey-names))
+ (<= (length nl)
+ (+ bibtex-autokey-names
+ bibtex-autokey-names-stretch)))
+ nl
+ (setq name-etal bibtex-autokey-additional-names)
+ (let (nnl)
+ (while (< (length nnl) bibtex-autokey-names)
+ (setq nnl (append nnl (list (car nl)))
+ nl (cdr nl)))
+ nnl))))
+ (namepart
+ (concat
+ (mapconcat (lambda (name) name)
+ namelist
+ bibtex-autokey-name-separator)
+ name-etal))
+ (yearfield (bibtex-autokey-get-yearfield min max))
+ (yearpart
+ (if (equal yearfield "")
+ ""
+ (substring
+ yearfield
+ (- (length yearfield) bibtex-autokey-year-length))))
+ (titlestring (bibtex-autokey-get-titlestring min max))
+ (titlelist (bibtex-autokey-get-titlelist titlestring))
+ (titlepart
+ (mapconcat
+ (lambda (name) name)
+ titlelist
+ bibtex-autokey-titleword-separator))
+ (autokey
+ (concat
+ bibtex-autokey-prefix-string
+ namepart
+ (if (not
+ (or
+ (equal namepart "")
+ (equal yearpart "")))
+ bibtex-autokey-name-year-separator)
+ yearpart
+ (if (not
+ (or
+ (and
+ (equal namepart "")
+ (equal yearpart ""))
+ (equal titlepart "")))
+ bibtex-autokey-year-title-separator)
+ titlepart)))
+ (if bibtex-autokey-before-presentation-function
+ (setq
+ autokey
+ (funcall bibtex-autokey-before-presentation-function autokey)))
+ (goto-char pnt)
+ autokey))
+
+(defun bibtex-parse-keys (add verbose &optional abortable)
+ ;; Sets bibtex-keys to the keys used in the whole (possibly
+ ;; restricted) buffer (either as entry keys or as crossref entries).
+ ;; If ADD is non-nil adds the new keys to bibtex-keys instead of
+ ;; simply resetting it. If VERBOSE is non-nil gives messages about
+ ;; progress. If ABORTABLE is non-nil abort on user input.
+ (if bibtex-maintain-sorted-entries
+ (let ((case-fold-search t)
+ (crossref-field
+ (bibtex-cfield
+ "crossref" (concat "[{\"]" bibtex-reference-key "[}\"]")))
+ (labels (if add
+ bibtex-keys)))
+ (save-excursion
+ (goto-char (point-min))
+ (if verbose
+ (bibtex-progress-message
+ (concat (buffer-name) ": parsing reference keys")))
+ (if (catch 'userkey
+ (bibtex-skip-to-valid-entry)
+ (while (not (eobp))
+ (if (and
+ abortable
+ (input-pending-p))
+ (throw 'userkey t))
+ (if verbose
+ (bibtex-progress-message))
+ (let (label
+ label2)
+ (cond
+ ((looking-at bibtex-reference-head)
+ (setq
+ label
+ (buffer-substring-no-properties
+ (match-beginning bibtex-key-in-head)
+ (match-end bibtex-key-in-head)))
+ (let ((p (point))
+ (m (bibtex-end-of-entry)))
+ (goto-char p)
+ (if (re-search-forward crossref-field m t)
+ (setq
+ label2
+ (buffer-substring-no-properties
+ (1+ (match-beginning bibtex-text-in-cfield))
+ (1- (match-end bibtex-text-in-cfield)))))
+ (goto-char p)))
+ ((looking-at bibtex-string)
+ (setq
+ label
+ (buffer-substring-no-properties
+ (match-beginning bibtex-key-in-string)
+ (match-end bibtex-key-in-string)))))
+ (forward-char)
+ (bibtex-skip-to-valid-entry)
+ (if (not (assoc label labels))
+ (setq labels
+ (cons (list label) labels)))
+ (if (and label2
+ (not (assoc label2 labels)))
+ (setq labels
+ (cons (list label2) labels))))))
+ ;; user has aborted by typing a key --> return nil
+ nil
+ ;; successful operation --> return t
+ (setq
+ bibtex-buffer-last-parsed-tick (buffer-modified-tick)
+ bibtex-keys labels)
+ (if verbose
+ (bibtex-progress-message 'done))
+ t)))))
+
+(defun bibtex-parse-buffers-stealthily ()
+ ;; Called by bibtex-run-with-idle-timer. Whenever emacs has been idle
+ ;; for bibtex-parse-keys-timeout seconds, all BibTeX buffers (starting
+ ;; with the current) are parsed.
+ (let ((buffers (buffer-list)))
+ (save-excursion
+ (while (and buffers (not (input-pending-p)))
+ (set-buffer (car buffers))
+ (save-restriction
+ (widen)
+ (if (and
+ (eq major-mode 'bibtex-mode)
+ bibtex-maintain-sorted-entries
+ (not
+ (eq (buffer-modified-tick)
+ bibtex-buffer-last-parsed-tick)))
+ (if (bibtex-parse-keys nil t t)
+ ;; successful operation --> remove buffer from list
+ (setq buffers (cdr buffers)))
+ ;; buffer is no BibTeX buffer or needs no parsing
+ (setq buffers (cdr buffers))))))))
+
+(defun bibtex-complete (string-list &optional complete-strings)
+ ;; Complete word fragment before point to longest prefix of one
+ ;; string defined in STRING-LIST. If point is not after the part of
+ ;; a word, all strings are listed. If COMPLETE-STRINGS is non-nil,
+ ;; add the strings defined in this buffer before cursor to
+ ;; STRING-LIST and remove surrounding delimiters if complete string
+ ;; could be expanded.
+ (let* ((case-fold-search t)
+ (end (point))
+ (beg (save-excursion
+ (re-search-backward "[ \t{\"]")
+ (forward-char)
+ (point)))
+ (part-of-word (buffer-substring-no-properties beg end))
+ (completions (copy-sequence string-list))
+ (completion (save-excursion
+ (if complete-strings
+ (while (re-search-backward
+ bibtex-string nil t)
+ (setq completions
+ (cons
+ (list
+ (buffer-substring-no-properties
+ (match-beginning bibtex-key-in-string)
+ (match-end bibtex-key-in-string)))
+ completions))))
+ (setq completions
+ (sort completions
+ (lambda(x y)
+ (string-lessp
+ (car x)
+ (car y)))))
+ (try-completion part-of-word completions))))
+ (cond ((eq completion t)
+ (if complete-strings
+ ;; remove double-quotes or braces if field is no concatenation
+ (save-excursion
+ (bibtex-inside-field)
+ (bibtex-enclosing-field)
+ (let ((end (match-end bibtex-text-in-field)))
+ (goto-char (match-beginning bibtex-text-in-field))
+ (if (and
+ (looking-at bibtex-field-string)
+ (equal (match-end 0) end))
+ (bibtex-remove-delimiters))))))
+ ((not completion)
+ (error "Can't find completion for \"%s\"." part-of-word))
+ ((not (string= part-of-word completion))
+ (delete-region beg end)
+ (insert completion)
+ (if (and (assoc completion completions)
+ complete-strings)
+ ;; remove double-quotes or braces if field is no concatenation
+ (save-excursion
+ (bibtex-inside-field)
+ (bibtex-enclosing-field)
+ (let ((end (match-end bibtex-text-in-field)))
+ (goto-char (match-beginning bibtex-text-in-field))
+ (if (and
+ (looking-at bibtex-field-string)
+ (equal (match-end 0) end))
+ (bibtex-remove-delimiters))))))
+ (t
+ (message "Making completion list...")
+ (let ((list (all-completions part-of-word completions)))
+ (with-output-to-temp-buffer "*Completions*"
+ (display-completion-list list)))
+ (message "Making completion list...done")))))
+
+(defun bibtex-do-auto-fill ()
+ (let ((fill-prefix
+ (make-string
+ (+ bibtex-entry-offset bibtex-contline-indentation) ? )))
+ (do-auto-fill)))
+
+(defun bibtex-pop (arg direction)
+ ;; generic function to be used by bibtex-pop-previous and bibtex-pop-next
+ (let (bibtex-help-message)
+ (bibtex-find-text nil))
(save-excursion
- (beginning-of-bibtex-entry)
- (re-search-forward bibtex-reference-head (save-excursion (end-of-bibtex-entry) (point)))
- (let* ((key (buffer-substring (match-beginning bibtex-key-in-head)
- (match-end bibtex-key-in-head)))
- (val (if include-cite
- (format "\\cite{%s}" key)
- key)))
- (if kill
- (put-string-on-kill-ring val))
- val)))
-
-;;; bibtex mode:
+ ;; parse current field
+ (bibtex-inside-field)
+ (bibtex-enclosing-field)
+ (let ((case-fold-search t)
+ (start-old-text (match-beginning bibtex-text-in-field))
+ (stop-old-text (match-end bibtex-text-in-field))
+ (start-name (match-beginning bibtex-name-in-field))
+ (stop-name (match-end bibtex-name-in-field))
+ (new-text))
+ (goto-char start-name)
+ ;; construct regexp for field with same name as this one,
+ ;; ignoring possible OPT's or ALT's
+ (let ((matching-entry
+ (bibtex-cfield
+ (buffer-substring-no-properties
+ (if (looking-at "OPT\\|ALT")
+ (+ (point) (length "OPT"))
+ (point))
+ stop-name)
+ bibtex-field-text)))
+ ;; if executed several times in a row, start each search where
+ ;; the last one was finished
+ (cond ((eq last-command 'bibtex-pop)
+ t
+ )
+ (t
+ (bibtex-enclosing-reference-maybe-empty-head)
+ (setq
+ bibtex-pop-previous-search-point (match-beginning 0)
+ bibtex-pop-next-search-point (point))))
+ (if (eq direction 'previous)
+ (goto-char bibtex-pop-previous-search-point)
+ (goto-char bibtex-pop-next-search-point))
+ ;; Now search for arg'th previous/next similar field
+ (cond
+ ((if (eq direction 'previous)
+ (re-search-backward matching-entry nil t arg)
+ (re-search-forward matching-entry nil t arg))
+ ;; Found a matching field. Remember boundaries.
+ (setq bibtex-pop-previous-search-point (match-beginning 0))
+ (setq bibtex-pop-next-search-point (match-end 0))
+ (setq new-text
+ (buffer-substring-no-properties
+ (match-beginning bibtex-text-in-field)
+ (match-end bibtex-text-in-field)))
+ ;; change delimiters, if any changes needed
+ (let ((start 0)
+ old-open
+ new-open
+ old-close
+ new-close)
+ (if (equal bibtex-field-delimiters 'braces)
+ (setq old-open ?\"
+ new-open ?\{
+ old-close ?\"
+ new-close ?\})
+ (setq old-open ?\{
+ new-open ?\"
+ old-close ?\}
+ new-close ?\"))
+ (while (string-match bibtex-field-string new-text start)
+ (let ((beg (match-beginning 0))
+ (end (1- (match-end 0))))
+ (if (and
+ (eq (aref new-text beg) old-open)
+ (eq (aref new-text end) old-close))
+ (progn
+ (aset new-text beg new-open)
+ (aset new-text end new-close))))
+ (setq start (match-end 0))))
+ (bibtex-flash-head)
+ ;; Go back to where we started, delete old text, and pop new.
+ (goto-char stop-old-text)
+ (delete-region start-old-text stop-old-text)
+ (insert new-text))
+ (t
+ ;; search failed
+ (error (concat "No "
+ (if (eq direction 'previous)
+ "previous"
+ "next")
+ " matching BibTeX field.")))))))
+ (let (bibtex-help-message)
+ (bibtex-find-text nil))
+ (setq this-command 'bibtex-pop))
+
+\f
+;; Interactive Functions:
;;;###autoload
-(defun bibtex-mode ()
- "Major mode for editing bibtex files.
+(defun bibtex-mode ()
+ "Major mode for editing BibTeX files.
-\\{bibtex-mode-map}
+To submit a problem report, enter \\[bibtex-submit-bug-report] from a
+BibTeX mode buffer. This automatically sets up a mail buffer with
+version information already added. You just need to add a description
+of the problem, including a reproducable test case and send the
+message.
-A command such as \\[bibtex-Book] will outline the fields for a BibTeX book entry.
-The optional fields start with the string OPT, and thus ignored by BibTeX.
-The OPT string may be removed from a field with \\[bibtex-remove-OPT].
-\\[bibtex-kill-optional-field] kills the current optional field entirely.
-\\[bibtex-remove-double-quotes] removes the double-quotes around the text of
-the current field. \\[bibtex-empty-field] replaces the text of the current
-field with the default \"\".
+General information on working with BibTeX mode:
-The command \\[bibtex-clean-entry] cleans the current entry, i.e. (i) removes
-double-quotes from entirely numerical fields, (ii) removes OPT from all
-non-empty optional fields, (iii) removes all empty optional fields, and (iv)
-checks that no non-optional fields are empty.
+You should use commands as \\[bibtex-Book] to get a template for a
+specific entry. You should 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, you should clean the new entry
+with command \\[bibtex-clean-entry].
-Use \\[bibtex-find-text] to position the dot at the end of the current field.
+Some features of BibTeX mode are available only by setting variable
+bibtex-maintain-sorted-entries to t. However, then BibTeX mode will
+work with buffer containing only valid (syntactical correct) entries
+and with entries being sorted. This is usually the case, if you have
+created a buffer completely with BibTeX mode and finished every new
+entry with \\[bibtex-clean-entry].
+
+For third party BibTeX buffers, please call the function
+`bibtex-convert-alien' to fully take advantage of all features of
+BibTeX mode.
+
+
+Special information:
+
+A command such as \\[bibtex-Book] will outline the fields for a BibTeX book entry.
+
+The optional fields start with the string OPT, and are thus ignored by BibTeX.
+Alternatives from which only one is required start with the string ALT.
+The OPT or ALT string may be removed from a field with \\[bibtex-remove-OPT-or-ALT].
+\\[bibtex-make-field] inserts a new field after the current one.
+\\[bibtex-kill-field] kills the current field entirely.
+\\[bibtex-yank] will yank the last recently killed field after the
+current field.
+\\[bibtex-remove-delimiters] removes the double-quotes or braces around the text of the current field.
+ \\[bibtex-empty-field] replaces the text of the current field with the default \"\" or {}.
+
+The command \\[bibtex-clean-entry] cleans the current entry, i.e. it removes OPT/ALT
+from all non-empty optional or alternative fields, checks that no required
+fields are empty, and does some formatting dependent on the value of
+bibtex-entry-format.
+Note: some functions in BibTeX mode depend on entries being in a special
+format (all fields beginning on separate lines), so it is usually a bad
+idea to remove `realign' from bibtex-entry-format.
+
+Use \\[bibtex-find-text] to position the cursor at the end of the current field.
Use \\[bibtex-next-field] to move to end of the next field.
The following may be of interest as well:
Functions:
- find-bibtex-duplicates
- find-bibtex-entry-location
- hide-bibtex-entry-bodies
- sort-bibtex-entries
- validate-bibtex-buffer
+ bibtex-entry
+ bibtex-kill-entry
+ bibtex-yank-pop
+ bibtex-pop-previous
+ bibtex-pop-next
+ bibtex-complete-string
+ bibtex-complete-key
+ bibtex-print-help-message
+ bibtex-generate-autokey
+ bibtex-beginning-of-entry
+ bibtex-end-of-entry
+ bibtex-reposition-window
+ bibtex-mark-entry
+ bibtex-ispell-abstract
+ bibtex-ispell-entry
+ bibtex-narrow-to-entry
+ bibtex-hide-entry-bodies
+ bibtex-sort-buffer
+ bibtex-validate
+ bibtex-count
+ bibtex-fill-entry
+ bibtex-reformat
+ bibtex-convert-alien
Variables:
- bibtex-clean-entry-zap-empty-opts
- bibtex-entry-field-alist
- bibtex-include-OPTannote
+ bibtex-field-delimiters
bibtex-include-OPTcrossref
bibtex-include-OPTkey
+ bibtex-user-optional-fields
+ bibtex-entry-format
+ bibtex-sort-ignore-string-entries
bibtex-maintain-sorted-entries
- bibtex-mode-user-optional-fields
-
-Fields:
- address
- Publisher's address
- annote
- Long annotation used for annotated bibliographies (begins sentence)
- author
- Name(s) of author(s), in BibTeX name format
- booktitle
- Book title when the thing being referenced isn't the whole book.
- For book entries, the title field should be used instead.
- chapter
- Chapter number
- crossref
- The database key of the entry being cross referenced.
- edition
- Edition of a book (e.g., \"second\")
- editor
- Name(s) of editor(s), in BibTeX name format.
- If there is also an author field, then the editor field should be
- for the book or collection that the work appears in
- howpublished
- How something strange has been published (begins sentence)
- institution
- Sponsoring institution
- journal
- Journal name (macros are provided for many)
- key
- Alphabetizing and labeling key (needed when no author or editor)
- month
- Month (macros are provided)
- note
- To help the reader find a reference (begins sentence)
- number
- Number of a journal or technical report
- organization
- Organization (sponsoring a conference)
- pages
- Page number or numbers (use `--' to separate a range)
- publisher
- Publisher name
- school
- School name (for theses)
- series
- The name of a series or set of books.
- An individual book will will also have it's own title
- title
- The title of the thing being referenced
- type
- Type of a technical report (e.g., \"Research Note\") to be used
- instead of the default \"Technical Report\"
- volume
- Volume of a journal or multivolume work
- year
- Year---should contain only numerals
+ bibtex-entry-field-alist
+ bibtex-predefined-strings
+ bibtex-string-files
+
---------------------------------------------------------
-Entry to this mode calls the value of bibtex-mode-hook if that value is
-non-nil."
+Entry to BibTeX mode calls the value of `bibtex-mode-hook' if that value is
+non-nil.
+
+\\{bibtex-mode-map}
+"
(interactive)
(kill-all-local-variables)
- (set-syntax-table bibtex-mode-syntax-table)
(use-local-map bibtex-mode-map)
(setq major-mode 'bibtex-mode)
(setq mode-name "BibTeX")
(set-syntax-table bibtex-mode-syntax-table)
- (setq local-abbrev-table bibtex-mode-abbrev-table)
- (make-local-variable 'paragraph-start)
- (setq paragraph-start "^[ \f\n\t]*$")
- (auto-fill-mode 1) ; nice alignments
- (setq left-margin (+ bibtex-text-alignment 1))
-
+ (setq bibtex-strings bibtex-predefined-strings)
+ (mapcar
+ (lambda (filename)
+ ;; collect pathnames
+ (let* ((path (if bibtex-string-file-path
+ bibtex-string-file-path
+ "."))
+ (dirs
+ (mapcar
+ (lambda (dirname) ;; strips off trailing slashes
+ (let ((len (length dirname)))
+ (if (equal (elt dirname (1- len)) "/")
+ (substring dirname 0 (1- (1- len)))
+ dirname)))
+ (let (actdirs)
+ (while (string-match ":" path)
+ (setq actdirs
+ (append actdirs
+ (list (substring path 0 (1- (match-end 0)))))
+ path (substring path (match-end 0))))
+ (append actdirs (list path)))))
+ (filename (if (string-match "\.bib$" filename)
+ filename
+ (concat filename ".bib")))
+ fullfilename
+ (item 0)
+ (size (length dirs)))
+ ;; test filenames
+ (while (and
+ (< item size)
+ (not (file-readable-p
+ (setq fullfilename
+ (concat (elt dirs item) "/" filename)))))
+ (setq item (1+ item)))
+ (if (< item size)
+ ;; file was found
+ (let ((case-fold-search t)
+ (curbuf (current-buffer))
+ (bufname (make-temp-name ""))
+ (compl bibtex-strings))
+ (create-file-buffer bufname)
+ (set-buffer bufname)
+ (insert-file-contents fullfilename)
+ (goto-char (point-min))
+ (while (re-search-forward bibtex-string nil t)
+ (setq compl
+ (append compl
+ (list
+ (list (buffer-substring-no-properties
+ (match-beginning bibtex-key-in-string)
+ (match-end bibtex-key-in-string)))))))
+ (kill-buffer bufname)
+ (set-buffer curbuf)
+ (setq bibtex-strings compl))
+ (error
+ "File %s not in paths defined by bibtex-string-file-path variable."
+ filename))))
+ bibtex-string-files)
+ (if bibtex-maintain-sorted-entries
+ (bibtex-run-with-idle-timer
+ 1 nil
+ (lambda ()
+ (bibtex-parse-keys nil t t))))
+ ;; to get buffer parsed once if everything else (including things
+ ;; installed in bibtex-mode-hook) has done its work
+ (if (not bibtex-parse-idle-timer)
+ (setq bibtex-parse-idle-timer
+ (bibtex-run-with-idle-timer
+ bibtex-parse-keys-timeout t
+ 'bibtex-parse-buffers-stealthily)))
+ ;; Install stealthy parse function if not already installed
+ (set (make-local-variable 'paragraph-start) "[ \f\n\t]*$")
+ (set (make-local-variable 'comment-start) bibtex-comment-start)
+ (set (make-local-variable 'comment-start-skip) bibtex-comment-start)
+ (set (make-local-variable 'comment-column) 0)
+ (set (make-local-variable 'normal-auto-fill-function)
+ 'bibtex-do-auto-fill)
+ (set (make-local-variable 'font-lock-defaults)
+ '(bibtex-font-lock-keywords
+ nil t ((?$ . "\"")
+ ;; Mathematical expressions should be fontified as strings
+ (?\" . ".")
+ ;; Quotes are field delimiters and quote-delimited
+ ;; entries should be fontified in the same way as
+ ;; brace-delimited ones
+ )))
+ (setq font-lock-mark-block-function
+ (lambda ()
+ (set-mark (bibtex-end-of-entry))
+ (bibtex-beginning-of-entry)))
+ (setq imenu-generic-expression
+ (list (list nil bibtex-reference-head bibtex-key-in-head)))
+ (setq imenu-case-fold-search t)
+ ;; XEmacs needs easy-menu-add, Emacs does not care
+ (easy-menu-add bibtex-edit-menu)
+ (easy-menu-add bibtex-entry-menu)
(run-hooks 'bibtex-mode-hook))
-(defun bibtex-move-outside-of-entry ()
- "Make sure we are outside of a bib entry"
- (cond ((or
- (= (point) (point-max))
- (= (point) (point-min))
- (looking-at "[ \n]*@")
- )
- t)
- (t
- (backward-paragraph)
- (forward-paragraph)))
- (re-search-forward "[ \t\n]*" (point-max) t))
-
-(defun ispell-abstract ()
+(defun bibtex-submit-bug-report ()
+ "Submit via mail a bug report on bibtex.el."
(interactive)
- (beginning-of-bibtex-entry)
- (re-search-forward "^[ \t]*[OPT]*abstract[ \t]*=")
- (ispell-region (point)
- (save-excursion (forward-sexp) (point))))
+ (if (y-or-n-p "Do you want to submit a bug report on BibTeX mode? ")
+ (progn
+ (require 'reporter)
+ (let ((reporter-prompt-for-summary-p t))
+ (reporter-submit-bug-report
+ bibtex-maintainer-address
+ (concat "bibtex.el " "(emacs 19.35)")
+ (list
+ 'system-configuration
+ 'system-configuration-options
+ 'bibtex-mode-hook
+ 'bibtex-parse-keys-timeout
+ ;; possible general errors
+ 'bibtex-sort-ignore-string-entries
+ 'bibtex-maintain-sorted-entries
+ 'bibtex-entry-delimiters
+ 'bibtex-field-delimiters
+ 'bibtex-comma-after-last-field
+ 'bibtex-entry-offset
+ 'bibtex-field-indentation
+ 'bibtex-text-indentation
+ 'bibtex-contline-indentation
+ 'bibtex-align-at-equal-sign
+ ;; possible sorting and parsing bugs
+ 'bibtex-entry-format
+ 'bibtex-add-entry-hook
+ 'bibtex-clean-entry-hook
+ ;; possible cleaning error
+ 'bibtex-user-optional-fields
+ ;; possible format error
+ 'bibtex-predefined-month-strings
+ 'bibtex-predefined-strings
+ 'bibtex-string-files
+ 'bibtex-string-file-path
+ ;; possible format error
+ 'bibtex-font-lock-keywords
+ ;; possible bugs regarding fontlocking
+ 'bibtex-autokey-prefix-string
+ 'bibtex-autokey-names
+ 'bibtex-autokey-names-stretch
+ 'bibtex-autokey-additional-names
+ 'bibtex-autokey-transcriptions
+ 'bibtex-autokey-name-change-strings
+ 'bibtex-autokey-name-case-convert
+ 'bibtex-autokey-name-length
+ 'bibtex-autokey-name-separator
+ 'bibtex-autokey-year-length
+ 'bibtex-autokey-year-use-crossref-entry
+ 'bibtex-autokey-titlewords
+ 'bibtex-autokey-title-terminators
+ 'bibtex-autokey-titlewords-stretch
+ 'bibtex-autokey-titleword-ignore
+ 'bibtex-autokey-titleword-case-convert
+ 'bibtex-autokey-titleword-abbrevs
+ 'bibtex-autokey-titleword-change-strings
+ 'bibtex-autokey-titleword-length
+ 'bibtex-autokey-titleword-separator
+ 'bibtex-autokey-name-year-separator
+ 'bibtex-autokey-year-title-separator
+ 'bibtex-autokey-edit-before-use
+ 'bibtex-autokey-before-presentation-function
+ ;; possible bugs regarding automatic labels
+ 'bibtex-entry-field-alist
+ ;; possible format error
+ 'bibtex-help-message
+ 'bibtex-include-OPTcrossref
+ 'bibtex-include-OPTkey
+ 'bibtex-field-kill-ring-max
+ 'bibtex-entry-kill-ring-max
+ ;; user variables which shouldn't cause any errors
+ )
+ nil nil
+ (concat "To the bibtex.el maintainer:
+
+I want to report a bug on Emacs BibTeX mode.
+I've read the `Bugs' section in the `Emacs' info page, so I know how
+to make a clear and unambiguous report. I have started a fresh Emacs
+via `"invocation-name " --no-init-file --no-site-file', thereafter (in
+case I'm reporting on a version of `bibtex.el' which is not part of
+the standard emacs distribution) I loaded the questionable version
+of `bibtex.el' with `M-x load-file', and then, to produce the buggy
+behaviour, I did the following:")))
+ (message nil))))
+
+(defun bibtex-entry (entry-type)
+ "Inserts a new BibTeX entry.
+After insertion it calls the functions in `bibtex-add-entry-hook'."
+ (interactive (let* ((completion-ignore-case t)
+ (e-t (completing-read
+ "Entry Type: "
+ bibtex-entry-field-alist
+ nil t nil 'bibtex-entry-type-history)))
+ (list e-t)))
+ (if (not bibtex-keys)
+ (bibtex-parse-keys nil t))
+ (let* (required
+ optional
+ (key
+ (if bibtex-maintain-sorted-entries
+ (completing-read
+ (format "%s key: " entry-type)
+ bibtex-keys nil nil nil 'bibtex-key-history)))
+ (e (assoc-ignore-case entry-type bibtex-entry-field-alist))
+ (r-n-o (elt e 1))
+ (c-ref (elt e 2)))
+ (if (not e)
+ (error "Bibtex entry type %s not defined." entry-type))
+ (if (and
+ (member entry-type bibtex-include-OPTcrossref)
+ c-ref)
+ (setq required (elt c-ref 0)
+ optional (elt c-ref 1))
+ (setq required (elt r-n-o 0)
+ optional (elt r-n-o 1)))
+ (if bibtex-maintain-sorted-entries
+ (bibtex-find-entry-location key)
+ (bibtex-move-outside-of-entry))
+ (indent-to-column bibtex-entry-offset)
+ (insert "@" entry-type (bibtex-entry-left-delimiter))
+ (if key
+ (insert key))
+ (save-excursion
+ (mapcar 'bibtex-make-field required)
+ (if (member entry-type bibtex-include-OPTcrossref)
+ (bibtex-make-optional-field '("crossref")))
+ (if bibtex-include-OPTkey
+ (if (or
+ (stringp bibtex-include-OPTkey)
+ (fboundp bibtex-include-OPTkey))
+ (bibtex-make-optional-field
+ (list "key" nil bibtex-include-OPTkey))
+ (bibtex-make-optional-field '("key"))))
+ (mapcar 'bibtex-make-optional-field optional)
+ (mapcar 'bibtex-make-optional-field bibtex-user-optional-fields)
+ (if bibtex-comma-after-last-field
+ (insert ","))
+ (insert "\n")
+ (indent-to-column bibtex-entry-offset)
+ (insert (bibtex-entry-right-delimiter) "\n\n"))
+ (bibtex-next-field t)
+ (run-hooks 'bibtex-add-entry-hook)))
-(defun beginning-of-bibtex-entry ()
+(defun bibtex-print-help-message ()
+ "Prints helpful information about current field in current BibTeX entry."
+ (interactive)
+ (let* ((case-fold-search t)
+ (pnt (point))
+ (field-name
+ (progn
+ (condition-case errname
+ (bibtex-enclosing-field)
+ (search-failed
+ (goto-char pnt)
+ (error "Not on BibTeX field.")))
+ (let ((mb (match-beginning bibtex-name-in-field))
+ (me (match-end bibtex-name-in-field)))
+ (goto-char mb)
+ (buffer-substring-no-properties
+ (if (looking-at "OPT\\|ALT")
+ (+ 3 mb)
+ mb)
+ me))))
+ (reference-type
+ (progn
+ (re-search-backward
+ bibtex-reference-maybe-empty-head nil t)
+ (buffer-substring-no-properties
+ (1+ (match-beginning bibtex-type-in-head))
+ (match-end bibtex-type-in-head))))
+ (entry-list
+ (assoc-ignore-case reference-type
+ bibtex-entry-field-alist))
+ (c-r-list (elt entry-list 2))
+ (req-opt-list
+ (if (and
+ (member reference-type bibtex-include-OPTcrossref)
+ c-r-list)
+ c-r-list
+ (elt entry-list 1)))
+ (list-of-entries (append
+ (elt req-opt-list 0)
+ (elt req-opt-list 1)
+ bibtex-user-optional-fields
+ (if (member
+ reference-type
+ bibtex-include-OPTcrossref)
+ '(("crossref"
+ "Label of the crossreferenced entry")))
+ (if bibtex-include-OPTkey
+ '(("key"
+ "Key used for label creation if author and editor fields are missing"))))))
+ (goto-char pnt)
+ (let ((comment (assoc-ignore-case field-name list-of-entries)))
+ (if comment
+ (message (elt comment 1))
+ (message "NO COMMENT AVAILABLE")))))
+
+(defun bibtex-make-field (e-t &optional called-by-yank)
+ "Makes a field named E-T in current BibTeX entry.
+This function is for interactive and non-interactive purposes. To call
+it interactively, just give it no arguments and enter the field name
+using the minibuffer."
+ (interactive "*P")
+ (if (not e-t)
+ (setq
+ e-t
+ (let* ((reference-type
+ (save-excursion
+ (bibtex-enclosing-reference-maybe-empty-head)
+ (buffer-substring-no-properties
+ (1+ (match-beginning bibtex-type-in-head))
+ (match-end bibtex-type-in-head))))
+ (fl
+ (car (cdr (assoc-ignore-case
+ reference-type bibtex-entry-field-alist))))
+ (field-list
+ (append
+ (elt fl 0) (elt fl 1) bibtex-user-optional-fields
+ (if bibtex-include-OPTcrossref '(("crossref" nil)))
+ (if bibtex-include-OPTkey '(("key" nil)))))
+ (completion-ignore-case t))
+ (completing-read
+ "BibTeX field name: " field-list
+ nil nil nil bibtex-field-history))))
+ (if (not (consp e-t))
+ (setq e-t (list e-t)))
+ (if (equal (length e-t) 1)
+ (setq e-t (append e-t (list ""))))
+ (if (equal (length e-t) 2)
+ (setq e-t (append e-t (list ""))))
+ (let ((name (if (elt e-t 3)
+ (concat "ALT" (car e-t))
+ (car e-t))))
+ (if (or (interactive-p) called-by-yank)
+ (let (bibtex-help-message)
+ (bibtex-find-text nil t)
+ (if (looking-at "[}\"]")
+ (forward-char))))
+ (insert ",\n")
+ (indent-to-column
+ (+ bibtex-entry-offset bibtex-field-indentation))
+ (insert name " ")
+ (if bibtex-align-at-equal-sign
+ (indent-to-column
+ (+ bibtex-entry-offset (- bibtex-text-indentation 2))))
+ (insert "= ")
+ (if (not bibtex-align-at-equal-sign)
+ (indent-to-column
+ (+ bibtex-entry-offset bibtex-text-indentation)))
+ (insert (if called-by-yank
+ ""
+ (bibtex-field-left-delimiter))
+ (let ((init (elt e-t 2)))
+ (cond
+ ((stringp init)
+ init)
+ ((fboundp init)
+ (funcall init))
+ (t
+ (error "%s is neither a string nor a function." init))))
+ (if called-by-yank
+ ""
+ (bibtex-field-right-delimiter)))
+ (if (interactive-p)
+ (forward-char -1))))
+
+(defun bibtex-beginning-of-entry ()
+ "Move to beginning of BibTeX entry.
+If inside an entry, move to the beginning of it, otherwise move to the
+beginning of the previous entry.
+If called from a program, this function returns the new location of point."
(interactive)
- (re-search-backward "^@" nil 'move))
-
-(defun skip-whitespace-and-comments ()
- ;; It might be a good idea to have forward-sexp with argument 0 do what
- ;; this function tries to do, namely skip whitespace and comments.
- ;; Maybe a better name for this would be skip-to-next-sexp.
- ;; alternative implementation:
- ;; (let ((parse-sexp-ignore-comments t))
- ;; (forward-sexp 1)
- ;; (forward-sexp -1))
- ;; but I've had problems with this not getting the parse of comments
- ;; right going backward if they contain unbalanced expressions or string
- ;; quotes. [alarson:19920217.1021CST]
- (let ((md (match-data)))
- (unwind-protect
- (while (cond ((looking-at "\\s>+\\|\\s +")
- ;; was whitespace
- ;; NOTE: also checked end-comment. In latex and
- ;; lisp modes, newline is an end comment, but it
- ;; should also be a whitespace char.
- (goto-char (match-end 0)))
- ;; If looking at beginning of comment, skip to end.
- ((looking-at "\\s<")
- (re-search-forward "\\s>"))))
- (store-match-data md))))
-
-;;; [alarson:19920214.1007CST]
-(defun end-of-bibtex-entry ()
- "If inside an entry, move to the end of it, otherwise move to the end
-of the next entry."
+ (skip-chars-forward " \t")
+ (if (looking-at "@")
+ (forward-char))
+ (re-search-backward "^[ \t]*@" nil 'move))
+
+(defun bibtex-end-of-entry ()
+ "Move to end of BibTeX entry.
+If inside an entry, move to the end of it, otherwise move to the end
+of the previous entry.
+If called from a program, this function returns the new location of point."
(interactive)
- ;; if point was previously at the end of an entry, this puts us
- ;; inside the next entry, otherwise we remain in the current one.
- (progn
- (skip-whitespace-and-comments)
-;;; (skip-chars-forward " \t\n")
- (end-of-line))
- (beginning-of-bibtex-entry)
- (let ((parse-sexp-ignore-comments t))
- (forward-sexp) ; skip entry type
- (forward-sexp) ; skip entry body
- ))
-;(defun end-of-bibtex-entry ()
-; (interactive)
-; (re-search-forward "}$" nil 'move))
+ (let ((case-fold-search t)
+ (valid-entry-head
+ (concat "[ \t]*@[ \t]*\\("
+ (mapconcat
+ (lambda (type)
+ (concat "\\(" (car type) "\\)"))
+ bibtex-entry-field-alist
+ "\\|")
+ "\\)"))
+ (org (point))
+ (pnt (bibtex-beginning-of-entry))
+ err)
+ (cond
+ ((looking-at "[ \t]*@[ \t]*string[ \t\n]*[({]")
+ (if (not (and
+ (re-search-forward bibtex-string nil t)
+ (equal (match-beginning 0) pnt)))
+ (setq err t)))
+ ((looking-at "[ \t]*@[ \t]*preamble[ \t\n]*")
+ (goto-char (match-end 0))
+ (cond
+ ((looking-at "(")
+ (if (not (re-search-forward ")[ \t]*\n\n" nil 'move))
+ (setq err t)))
+ ((looking-at "{")
+ (if (not (re-search-forward "}[ \t]*\n\n" nil 'move))
+ (setq err t)))
+ (t
+ (setq err t)))
+ (if (not err)
+ (progn
+ (goto-char (match-beginning 0))
+ (forward-char))))
+ ((looking-at valid-entry-head)
+ (bibtex-search-reference t nil t)
+ (if (not (equal (match-beginning 0) pnt))
+ (setq err t)))
+ (t
+ (if (interactive-p)
+ (message "Not on a known BibTeX entry."))
+ (goto-char org)))
+ (if err
+ (progn
+ (goto-char pnt)
+ (error "Syntactical incorrect entry starts here."))))
+ (point))
-(defun ispell-bibtex-entry ()
+(defun bibtex-reposition-window (arg)
+ "Make the current BibTeX entry visible."
+ (interactive "P")
+ (save-excursion
+ (goto-char
+ (/ (+ (bibtex-beginning-of-entry) (bibtex-end-of-entry)) 2))
+ (recenter arg)))
+
+(defun bibtex-mark-entry ()
+ "Put mark at beginning, point at end of current BibTeX entry."
(interactive)
- (ispell-region (progn (beginning-of-bibtex-entry) (point))
- (progn (end-of-bibtex-entry) (point))))
+ (set-mark (bibtex-beginning-of-entry))
+ (bibtex-end-of-entry))
+
+(defun bibtex-count-entries (&optional count-string-entries)
+ "Count number of entries in current buffer or region.
+With prefix argument it counts all entries, otherwise it counts all
+except Strings.
+If mark is active it counts entries in region, if not in whole buffer."
+ (interactive "P")
+ (let ((pnt (point))
+ (start-point
+ (if (bibtex-mark-active)
+ (region-beginning)
+ (bibtex-beginning-of-first-entry)))
+ (end-point
+ (if (bibtex-mark-active)
+ (region-end)
+ (point-max)))
+ (number 0)
+ (bibtex-sort-ignore-string-entries
+ (not count-string-entries)))
+ (save-restriction
+ (narrow-to-region start-point end-point)
+ (goto-char start-point)
+ (bibtex-map-entries
+ (lambda (current)
+ (setq number (1+ number)))))
+ (message (concat (if (bibtex-mark-active) "Region" "Buffer")
+ " contains %d entries.") number)
+ (goto-char pnt)))
+
+(defun bibtex-ispell-entry ()
+ "Spell whole BibTeX entry."
+ (interactive)
+ (ispell-region (bibtex-beginning-of-entry) (bibtex-end-of-entry)))
-(defun narrow-to-bibtex-entry ()
+(defun bibtex-ispell-abstract ()
+ "Spell abstract of BibTeX entry."
+ (interactive)
+ (let ((case-fold-search t)
+ (pnt (bibtex-end-of-entry)))
+ (bibtex-beginning-of-entry)
+ (if (not
+ (re-search-forward
+ (bibtex-cfield "abstract" bibtex-field-text) pnt t))
+ (error "No abstract in entry.")))
+ (ispell-region (match-beginning bibtex-text-in-cfield)
+ (match-end bibtex-text-in-cfield)))
+
+(defun bibtex-narrow-to-entry ()
+ "Narrow buffer to current BibTeX entry."
(interactive)
(save-excursion
- (narrow-to-region (progn (beginning-of-bibtex-entry) (point))
- (progn (end-of-bibtex-entry) (point)))))
-
+ (narrow-to-region
+ (bibtex-beginning-of-entry) (bibtex-end-of-entry))))
-(defun beginning-of-first-bibtex-entry ()
- (goto-char (point-min))
- (cond
- ((re-search-forward "^@" nil 'move)
- (beginning-of-line))
- ((and (bobp) (eobp))
- nil)
- (t
- (message "Warning: No bibtex entries found!"))))
-
-(defun hide-bibtex-entry-bodies (&optional arg)
- "Hide all lines between first and last bibtex entries not beginning with @.
+(defun bibtex-hide-entry-bodies (&optional arg)
+ "Hide all lines between first and last BibTeX entries not beginning with @.
With argument, show all text."
(interactive "P")
(save-excursion
- (beginning-of-first-bibtex-entry)
- ;; subst-char-in-region modifies the buffer, despite what the
- ;; documentation says...
- (let ((modifiedp (buffer-modified-p))
- (buffer-read-only nil))
+ (bibtex-beginning-of-first-entry)
+ (let ((buffer-read-only nil))
(if arg
(subst-char-in-region (point) (point-max) ?\r ?\n t)
- (while (save-excursion (re-search-forward "\n[^@]" (point-max) t))
- ;; (save-excursion (replace-regexp "\n\\([^@]\\)" "\r\\1"))
- (save-excursion
- (while (re-search-forward "\n\\([^@]\\)" nil t)
- (replace-match "\r\\1" nil nil)))))
- (setq selective-display (not arg))
- (set-buffer-modified-p modifiedp))))
-
-(defvar bibtex-sort-ignore-string-entries nil
- "*If true, bibtex @STRING entries are ignored when determining ordering
-of the buffer (e.g. sorting, locating alphabetical position for new entries,
-etc.)")
-
-(defun sort-bibtex-entries ()
- "Sort bibtex entries alphabetically by key.
-Text before the first bibtex entry, and following the last is not affected.
-If bibtex-sort-ignore-string-entries is true, @string entries will be ignored.
-
-Bugs:
- 1. Text between the closing brace ending one bibtex entry, and the @ starting
- the next, is considered part of the PRECEDING entry. Perhaps it should be
- part of the following entry."
+ (while (not (eobp))
+ (subst-char-in-region
+ (point)
+ (progn
+ (re-search-forward "[\n\r]@" nil t)
+ (forward-line -1)
+ (point))
+ ?\n ?\r t)
+ (forward-line 1)))
+ (setq selective-display (not arg)))))
+
+(defun bibtex-sort-buffer ()
+ "Sort BibTeX buffer alphabetically by key.
+Text outside of BibTeX entries is not affected. If
+`bibtex-sort-ignore-string-entries' is non-nil, @String entries will be
+ignored."
(interactive)
(save-restriction
- (beginning-of-first-bibtex-entry)
- (narrow-to-region (point)
- (save-excursion
- (goto-char (point-max))
- (beginning-of-bibtex-entry)
- (end-of-bibtex-entry)
- (point)))
- (sort-subr nil ; reversep
- ;; beginning of record function
- 'forward-line
- ;; end of record function
- (function (lambda () (and (re-search-forward "}\\s-*\n[\n \t]*@" nil 'move)
- (forward-char -2))))
- ;; start of key function
- (if bibtex-sort-ignore-string-entries
- (function (lambda ()
- (while (and (re-search-forward "^\\s-*\\([@a-zA-Z]*\\)\\s-*{\\s-*")
- (string-equalp "@string"
- (buffer-substring (match-beginning 1)
- (match-end 1)))))
- nil))
- (function (lambda () (re-search-forward "{\\s-*") nil)))
- ;; end of key function
- (function (lambda () (search-forward ",")))
- )))
-
-(defun map-bibtex-entries (fun)
- "Call FUN for each bibtex entry starting with the current, to the end of the file.
-FUN is called with one argument, the key of the entry, and with point inside the entry.
-If bibtex-sort-ignore-string-entries is true, FUN will not be called for @string entries."
- (beginning-of-bibtex-entry)
- (while (re-search-forward "^@[^{]*{[ \t]*\\([^, ]*\\)" nil t)
- (if (and bibtex-sort-ignore-string-entries
- (string-equalp "@string{"
- (buffer-substring (match-beginning 0)
- (match-beginning 1))))
- nil ; ignore the @string entry.
- (funcall fun (buffer-substring (match-beginning 1) (match-end 1))))))
+ (narrow-to-region
+ (bibtex-beginning-of-first-entry)
+ (save-excursion
+ (goto-char (point-max))
+ (bibtex-end-of-entry)))
+ (bibtex-skip-to-valid-entry)
+ (sort-subr
+ nil
+ ;; NEXTREC function
+ 'bibtex-skip-to-valid-entry
+ ;; ENDREC function
+ 'bibtex-end-of-entry
+ ;; STARTKEY function
+ (lambda ()
+ (let ((case-fold-search t))
+ (re-search-forward bibtex-reference-head)
+ (buffer-substring-no-properties
+ (match-beginning bibtex-key-in-head)
+ (match-end bibtex-key-in-head)))))))
-(defun find-bibtex-entry-location (entry-name)
- "Searches from beginning of current buffer looking for place to put the
-bibtex entry named ENTRY-NAME. Buffer is assumed to be in sorted order,
-without duplicates (see \\[sort-bibtex-entries]), if it is not, an error will
-be signalled."
- (interactive "sBibtex entry key: ")
- (let ((previous nil)
- point)
- (beginning-of-first-bibtex-entry)
- (or (catch 'done
- (map-bibtex-entries (function (lambda (current)
- (cond
- ((string-equal entry-name current)
- (error "Entry duplicates existing!"))
- ((or (null previous)
- (string< previous current))
- (setq previous current
- point (point))
- (if (string< entry-name current)
- (progn
- (beginning-of-bibtex-entry)
- ;; Many schemes append strings to
- ;; existing entries to resolve them,
- ;; so initial substring matches may
- ;; indicate a duplicate entry.
- (let ((idx (string-match (regexp-quote entry-name) current)))
- (if (and (integerp idx)
- (zerop idx))
- (progn
- (message "Warning: Entry %s may be a duplicate of %s!"
- entry-name current)
- (ding t))))
- (throw 'done t))))
- ((string-equal previous current)
- (error "Duplicate here with previous!"))
- (t (error "Entries out of order here!")))))))
- (end-of-bibtex-entry))))
-
-(defun validate-bibtex-buffer ()
- "Find some typical errors in bibtex files.
- 1. At signs (@) not as first char of a line.
- 2. Double quotes (\") inside strings.
- 3. Closing braces (}) not the last character of a line."
- (interactive)
- (let ((point (point)))
- (while (re-search-forward ".@" nil t)
- (let* ((foo (parse-partial-sexp (save-excursion (beginning-of-bibtex-entry)
- (point))
- (point)))
- (in-a-string (nth 3 foo)))
- (if (not in-a-string)
- (error "At sign (@) out of place!"))))
- (goto-char point)
- (while (search-forward "\"" nil t)
- (or (looking-at "[,}][ \t]*$")
- (char-equal (preceding-char) ?\")
- ;; some versions put closing brace on separate line.
- (looking-at "[ \t]*\n}")
- (save-excursion
- (save-restriction
- (narrow-to-region (point)
- (progn (beginning-of-line) (point)))
- (looking-at "^[ \t]*[a-zA-Z]+[ \t]*=[ \t]*\"$")))
- (error "Quote out of place, or missing \",\" or \"}\"!")))
- (goto-char point)
- ;; This is only approximate, should actually search for close braces,
- ;; then see if they are inside a string, or at the end of a line.
- ;; This just gets the typical case of whitespace after a closing brace.
- (while (search-forward "}[ \t]+$" nil t)
- (error "Brace not last char of line!"))
- (goto-char point)
- (message "Bibtex buffer appears o.k.")))
-
-(defun find-bibtex-duplicates ()
- "Searches forward in current buffer looking for duplicate bibtex entries.
-Buffer is assumed to be sorted, see \\[sort-bibtex-entries]"
- (interactive)
- (let ((point (point)))
- ;; errors if things are not right...
- (find-bibtex-entry-location (make-string 10 255))
- (goto-char point)
- (message "No duplicates found!")))
-
-
-;;; assoc doesn't ignore case, so we need an assoc that does...
-(defun assoc-string-equalp (thing alist)
- (or (assoc thing alist)
- (while (and alist
- (not (string-equalp thing (car (car alist)))))
- (setq alist (cdr alist)))
- (car alist)))
-
-(defvar bibtex-maintain-sorted-entries nil
- "*If true, bibtex-mode will attempt to maintain all bibtex entries in
-sorted order.
+(defun bibtex-find-entry-location (entry-name &optional ignore-dups)
+ "Looking for place to put the BibTeX entry named ENTRY-NAME.
+Performs a binary search (therefore, buffer is assumed to be in sorted
+order, without duplicates (see \\[bibtex-validate]), if it is
+not, bibtex-find-entry-location will fail). If entry-name is already
+used as a reference key, an error is signaled. However, if optional
+variable IGNORE-DUPS is non-nil, no error messages about duplicate
+entries are signaled, but the error handling is assumed to be made in
+the calling function.
+The value is nil if an duplicate entry error occurred,
+and t in all other cases."
+ (let* ((case-fold-search t)
+ (left
+ (progn
+ (bibtex-beginning-of-first-entry)
+ (bibtex-skip-to-valid-entry)
+ (bibtex-end-of-entry)))
+ (right
+ (progn
+ (bibtex-beginning-of-last-entry)
+ (bibtex-skip-to-valid-entry t)
+ (point)))
+ actual-point
+ actual-key
+ (done (>= left right))
+ new
+ dup)
+ (while (not done)
+ (setq actual-point (/ (+ left right) 2))
+ (goto-char actual-point)
+ (bibtex-skip-to-valid-entry t)
+ (setq actual-key
+ (progn
+ (re-search-forward bibtex-reference-head)
+ (buffer-substring-no-properties
+ (match-beginning bibtex-key-in-head)
+ (match-end bibtex-key-in-head))))
+ (cond
+ ((string-lessp entry-name actual-key)
+ (setq new (bibtex-beginning-of-entry))
+ (if (equal right new)
+ (setq done t)
+ (setq right new)))
+ ((string-lessp actual-key entry-name)
+ (setq new (bibtex-end-of-entry))
+ (if (equal left new)
+ (setq done t)
+ (setq left new)))
+ ((string-equal actual-key entry-name)
+ (setq dup t
+ done t)
+ (if (not ignore-dups)
+ (progn
+ (bibtex-beginning-of-entry)
+ (error "Entry with key `%s' already exists." entry-name))))))
+ (if dup
+ (progn
+ (bibtex-beginning-of-entry)
+ nil)
+ (goto-char right)
+ (setq actual-key
+ (if (looking-at bibtex-reference-head)
+ (buffer-substring-no-properties
+ (match-beginning bibtex-key-in-reference)
+ (match-end bibtex-key-in-reference))))
+ (if (or
+ (not actual-key)
+ (string-lessp actual-key entry-name))
+ ;; buffer contains no valid entries or
+ ;; greater than last entry --> append
+ (progn
+ (bibtex-end-of-entry)
+ (if (not (bobp))
+ (newline (forward-line 2)))
+ (beginning-of-line))
+ (goto-char right))
+ t)))
+
+(defun bibtex-validate (&optional test-thoroughly)
+ "Validate if buffer or region is syntactically correct.
+Only known reference types are checked, so you can put comments
+outside of entries.
+With optional argument TEST-THOROUGHLY non-nil it checks for absence of
+required fields and questionable month fields as well.
+If mark is active, it validates current region, if not whole buffer.
+Returns t if test was successful, nil otherwise."
+ (interactive "P")
+ (let (error-list
+ syntax-error
+ (case-fold-search t)
+ (valid-bibtex-entry
+ (concat
+ "@[ \t]*\\(\\(string\\)\\|"
+ (mapconcat
+ (lambda (type)
+ (concat "\\(" (car type) "\\)"))
+ bibtex-entry-field-alist
+ "\\|")
+ "\\)"))
+ (pnt (point))
+ (start-point
+ (if (bibtex-mark-active)
+ (region-beginning)
+ (bibtex-beginning-of-first-entry)))
+ (end-point
+ (if (bibtex-mark-active)
+ (region-end)
+ (point-max))))
+ (save-restriction
+ (narrow-to-region start-point end-point)
+ ;; looking if entries fit syntactical structure
+ (goto-char start-point)
+ (bibtex-progress-message "Checking syntactical structure")
+ (while (re-search-forward "^[ \t]*@" nil t)
+ (bibtex-progress-message)
+ (forward-char -1)
+ (let ((p (point))
+ (must-match
+ (looking-at valid-bibtex-entry)))
+ (if (not must-match)
+ (forward-char)
+ (let (bibtex-sort-ignore-string-entries)
+ (bibtex-skip-to-valid-entry))
+ (if (equal (point) p)
+ (forward-char)
+ (goto-char p)
+ (setq
+ error-list
+ (cons (list
+ (bibtex-current-line)
+ "Syntax error (check esp. commas, braces, and quotes)")
+ error-list))
+ (forward-char)))))
+ (bibtex-progress-message 'done)
+ (if error-list
+ (setq syntax-error t)
+ ;; looking for correct sort order and duplicates (only if
+ ;; there were no syntax errors)
+ (if bibtex-maintain-sorted-entries
+ (let (previous)
+ (goto-char start-point)
+ (bibtex-progress-message "Checking correct sort order")
+ (bibtex-map-entries
+ (lambda (current)
+ (bibtex-progress-message)
+ (cond ((or (not previous)
+ (string< previous current))
+ (setq previous current))
+ ((string-equal previous current)
+ (setq
+ error-list
+ (cons (list (bibtex-current-line)
+ "Duplicate key with previous")
+ error-list)))
+ (t
+ (setq previous current
+ error-list
+ (cons (list (bibtex-current-line)
+ "Entries out of order")
+ error-list))))))
+ (bibtex-progress-message 'done)))
+ (if test-thoroughly
+ (progn
+ (goto-char start-point)
+ (bibtex-progress-message
+ "Checking required fields and month fields")
+ (let ((bibtex-sort-ignore-string-entries t)
+ (questionable-month
+ (concat
+ "[{\"]\\("
+ (mapconcat
+ (lambda (mon)
+ (concat "\\(" (car mon) "\\)"))
+ bibtex-predefined-month-strings
+ "\\|")
+ "\\)[}\"]")))
+ (bibtex-map-entries
+ (lambda (current)
+ (bibtex-progress-message)
+ (let* ((beg (bibtex-beginning-of-entry))
+ (end (bibtex-end-of-entry))
+ (entry-list
+ (progn
+ (goto-char beg)
+ (bibtex-search-reference nil end)
+ (assoc-ignore-case
+ (buffer-substring-no-properties
+ (1+ (match-beginning bibtex-type-in-head))
+ (match-end bibtex-type-in-head))
+ bibtex-entry-field-alist)))
+ (req (copy-sequence (elt (elt entry-list 1) 0)))
+ (creq (copy-sequence (elt (elt entry-list 2) 0)))
+ crossref-there)
+ (goto-char beg)
+ (while (re-search-forward bibtex-field end t)
+ (let ((field-name
+ (buffer-substring-no-properties
+ (match-beginning bibtex-name-in-field)
+ (match-end bibtex-name-in-field))))
+ (if (and (equal (downcase field-name) "month")
+ (string-match
+ questionable-month
+ (buffer-substring-no-properties
+ (match-beginning bibtex-text-in-field)
+ (match-end bibtex-text-in-field))))
+ (setq
+ error-list
+ (cons
+ (list
+ (bibtex-current-line)
+ "Questionable month field (delimited string)")
+ error-list)))
+ (setq
+ req
+ (delete (assoc-ignore-case field-name req) req)
+ creq
+ (delete (assoc-ignore-case field-name creq) creq))
+ (if (equal (downcase field-name) "crossref")
+ (setq crossref-there t))))
+ (if crossref-there
+ (setq req creq))
+ (if (or (> (length req) 1)
+ (and (= (length req) 1)
+ (not (elt (car req) 3))))
+ ;; two (or more) fields missed or one field
+ ;; missed and this isn't flagged alternative
+ ;; (notice that this fails if there are more
+ ;; than two alternatives in a BibTeX entry,
+ ;; which isn't the case momentarily)
+ (setq
+ error-list
+ (cons
+ (list (save-excursion
+ (bibtex-beginning-of-entry)
+ (bibtex-current-line))
+ (concat
+ "Required field \""
+ (car (car req))
+ "\" missing"))
+ error-list)))))))
+ (bibtex-progress-message 'done)))))
+ (goto-char pnt)
+ (if error-list
+ (let ((bufnam (buffer-name))
+ (dir default-directory))
+ (setq error-list
+ (sort error-list
+ (lambda (a b)
+ (< (car a) (car b)))))
+ (let ((pop-up-windows t))
+ (pop-to-buffer nil t))
+ (switch-to-buffer
+ (get-buffer-create "*BibTeX validation errors*") t)
+ ;; don't use switch-to-buffer-other-window, since this
+ ;; doesn't allow the second parameter NORECORD
+ (setq default-directory dir)
+ (toggle-read-only -1)
+ (compilation-mode)
+ (delete-region (point-min) (point-max))
+ (goto-char (point-min))
+ (insert
+ "BibTeX mode command `bibtex-validate'\n"
+ (if syntax-error
+ "Maybe undetected errors due to syntax errors. Correct and validate again."
+ "")
+ "\n")
+ (while error-list
+ (insert
+ bufnam ":" (number-to-string (elt (car error-list) 0))
+ ": " (elt (car error-list) 1) "\n")
+ (setq error-list (cdr error-list)))
+ (compilation-parse-errors nil nil)
+ (setq compilation-old-error-list compilation-error-list)
+ ;; this is necessary to avoid reparsing of buffer if you
+ ;; switch to compilation buffer and enter
+ ;; `compile-goto-error'
+ (set-buffer-modified-p nil)
+ (toggle-read-only 1)
+ (goto-char (point-min))
+ (other-window -1)
+ ;; return nil
+ nil)
+ (if (bibtex-mark-active)
+ (message "Region is syntactically correct")
+ (message "Buffer is syntactically correct"))
+ t)))
-Note that this is more a property of a file than a personal preference and
-as such should normally be set via a file local variable entry.")
+(defun bibtex-next-field (arg)
+ "Finds end of text of next BibTeX field; with arg, to its beginning."
+ (interactive "P")
+ (bibtex-inside-field)
+ (let ((start (point)))
+ (condition-case ()
+ (progn
+ (bibtex-enclosing-field)
+ (goto-char (match-end 0))
+ (forward-char 2))
+ (error
+ (goto-char start)
+ (end-of-line)
+ (forward-char))))
+ (bibtex-find-text arg t))
-(defun bibtex-entry (entry-type &optional required optional)
- (interactive (let* ((completion-ignore-case t)
- (e-t (completing-read "Entry Type: " bibtex-entry-field-alist
- nil t)))
- (list e-t)))
- (if (and (null required) (null optional))
- (let* ((e (assoc-string-equalp entry-type bibtex-entry-field-alist))
- (r-n-o (elt e 1))
- (c-ref (elt e 2)))
- (if (null e)
- (error "Bibtex entry type %s not defined!"))
- (if (and bibtex-include-OPTcrossref c-ref)
- (setq required (elt c-ref 0)
- optional (elt c-ref 1))
- (setq required (elt r-n-o 0)
- optional (elt r-n-o 1)))))
- (let ((key (if bibtex-maintain-sorted-entries
- (read-string (format "%s key: " entry-type)))))
- (if key
- (find-bibtex-entry-location key))
- (bibtex-move-outside-of-entry)
- (insert "@" entry-type "{")
- (if key
- (insert key))
+(defun bibtex-find-text (arg &optional as-if-interactive silent)
+ "Go to end of text of current field; with ARG, go to beginning."
+ (interactive "P")
+ (bibtex-inside-field)
+ (if (bibtex-enclosing-field (or (interactive-p) as-if-interactive))
+ (progn
+ (if arg
+ (progn
+ (goto-char (match-beginning bibtex-text-in-field))
+ (if (looking-at "[{\"]")
+ (forward-char)))
+ (goto-char (match-end bibtex-text-in-field))
+ (if (or
+ (= (preceding-char) ?})
+ (= (preceding-char) ?\"))
+ (forward-char -1)))
+ (if bibtex-help-message
+ (bibtex-print-help-message)))
+ (beginning-of-line)
+ (cond
+ ((looking-at bibtex-string)
+ (goto-char
+ (if arg
+ (match-beginning bibtex-text-in-string)
+ (match-end bibtex-text-in-string))))
+ ((looking-at bibtex-reference-maybe-empty-head)
+ (goto-char
+ (if arg
+ (match-beginning bibtex-key-in-head)
+ (match-end 0))))
+ (t
+ (if (not silent)
+ (error "Not on BibTeX field."))))))
+
+(defun bibtex-remove-OPT-or-ALT ()
+ "Removes the string starting optional/alternative fields.
+Aligns text and goes thereafter to end of text."
+ (interactive)
+ (let ((case-fold-search t))
+ (bibtex-inside-field)
+ (bibtex-enclosing-field)
(save-excursion
- (mapcar 'bibtex-make-field required)
- (if bibtex-include-OPTcrossref
- (bibtex-make-optional-field "crossref"))
- (if bibtex-include-OPTkey
- (bibtex-make-optional-field "key"))
- (mapcar 'bibtex-make-optional-field optional)
- (mapcar 'bibtex-make-optional-field
- bibtex-mode-user-optional-fields)
- (if bibtex-include-OPTannote
- (bibtex-make-optional-field "annote"))
- (insert "\n}\n\n"))
- (if key
- (bibtex-next-field t))
- (run-hooks 'bibtex-add-entry-hook)))
+ (goto-char (match-beginning bibtex-name-in-field))
+ (if (looking-at "OPT\\|ALT")
+ (progn
+ (delete-char (length "OPT"))
+ ;; make field non-OPT
+ (search-forward "=")
+ (forward-char -1)
+ (delete-horizontal-space)
+ (if bibtex-align-at-equal-sign
+ (indent-to-column (- bibtex-text-indentation 2))
+ (insert " "))
+ (search-forward "=")
+ (delete-horizontal-space)
+ (if bibtex-align-at-equal-sign
+ (insert " ")
+ (indent-to-column bibtex-text-indentation)))))
+ (bibtex-inside-field)))
+
+(defun bibtex-remove-delimiters ()
+ "Removes \"\" or {} around string."
+ (interactive)
+ (let ((case-fold-search t))
+ (save-excursion
+ (bibtex-inside-field)
+ (bibtex-enclosing-field)
+ (let ((start (match-beginning bibtex-text-in-field))
+ (stop (match-end bibtex-text-in-field)))
+ (goto-char start)
+ (while (re-search-forward bibtex-field-string stop t)
+ (let ((beg (copy-marker (match-beginning 0)))
+ (end (copy-marker (match-end 0))))
+ (goto-char beg)
+ (if (looking-at "[{\"]")
+ (delete-char 1))
+ (goto-char end)
+ (forward-char -1)
+ (if (looking-at "[}\"]")
+ (delete-char 1))))))))
+
+(defun bibtex-kill-field (&optional copy-only)
+ "Kills the entire enclosing BibTeX field.
+With prefix arg, copy the current field to `bibtex-field-kill-ring,'
+but do not actually kill it."
+ (interactive "P")
+ (let ((pnt (point))
+ (case-fold-search t))
+ (bibtex-inside-field)
+ (bibtex-enclosing-field)
+ (let ((the-end (match-end 0))
+ (the-beginning (match-beginning 0)))
+ (goto-char the-end)
+ (skip-chars-forward " \t\n,")
+ (setq
+ bibtex-field-kill-ring
+ (cons
+ (list
+ 'field
+ (buffer-substring-no-properties
+ (match-beginning bibtex-name-in-field)
+ (match-end bibtex-name-in-field))
+ (buffer-substring-no-properties
+ (match-beginning bibtex-text-in-field)
+ (match-end bibtex-text-in-field)))
+ bibtex-field-kill-ring))
+ (if (> (length bibtex-field-kill-ring) bibtex-field-kill-ring-max)
+ (setcdr
+ (nthcdr (1- bibtex-field-kill-ring-max) bibtex-field-kill-ring)
+ nil))
+ (setq bibtex-field-kill-ring-yank-pointer bibtex-field-kill-ring)
+ (if copy-only
+ (goto-char pnt)
+ (delete-region the-beginning the-end)
+ (let (bibtex-help-message)
+ (bibtex-find-text nil t t)))))
+ (setq bibtex-last-kill-command 'field))
+
+(defun bibtex-copy-field-as-kill ()
+ (interactive)
+ (bibtex-kill-field t))
-;; (defun bibtex-entry (entry-type required optional)
-;; (bibtex-move-outside-of-entry)
-;; (insert (concat "@" entry-type "{,\n\n}\n\n"))
-;; (previous-line 3)
-;; (insert (mapconcat 'bibtex-make-entry required ",\n"))
-;; (if required
-;; (if optional
-;; (insert ",\n")))
-;; (insert (mapconcat 'bibtex-make-OPT-entry optional ",\n"))
-;; (if bibtex-mode-user-optional-fields ;MON...
-;; (progn
-;; (if optional
-;; (insert ",\n"))
-;; (insert (mapconcat 'bibtex-make-OPT-entry
-;; bibtex-mode-user-optional-fields
-;; ",\n")))) ;MON
-;; (up-list -1)
-;; (forward-char 1))
-
-
-(defun bibtex-make-field (e-t)
- (interactive "sBibTeX entry type: ")
- (let ((name (if (consp e-t) (car e-t) e-t))
- (value (if (consp e-t) (cdr e-t) "")))
- (insert ",\n")
- (indent-to-column bibtex-name-alignment)
- (insert name " = ")
- (indent-to-column bibtex-text-alignment)
- ;; lucid emacs prin1-to-string breaks the undo chain. When they fix
- ;; that, the hack can be removed. [alarson:19930316.0805CST]
-; (insert (prin1-to-string value))
- ;; begin hack
- (insert (format (if (stringp value) "\"%s\"" "%s")
- value))
- ;; end hack
- nil))
+(defun bibtex-kill-entry (&optional copy-only)
+ "Kill the entire enclosing BibTeX reference entry.
+With prefix arg copy the current reference entry to
+`bibtex-entry-kill-ring', but do not actually kill it."
+ (interactive "P")
+ (let ((pnt (point))
+ (case-fold-search t)
+ (beg (bibtex-beginning-of-entry))
+ (end
+ (progn
+ (bibtex-end-of-entry)
+ (if (re-search-forward
+ bibtex-reference-maybe-empty-head nil 'move)
+ (goto-char (match-beginning 0)))
+ (point))))
+ (setq
+ bibtex-entry-kill-ring
+ (cons
+ (list 'entry (buffer-substring-no-properties beg end))
+ bibtex-entry-kill-ring))
+ (if (> (length bibtex-entry-kill-ring) bibtex-entry-kill-ring-max)
+ (setcdr
+ (nthcdr (1- bibtex-entry-kill-ring-max) bibtex-entry-kill-ring)
+ nil))
+ (setq bibtex-entry-kill-ring-yank-pointer bibtex-entry-kill-ring)
+ (if copy-only
+ (goto-char pnt)
+ (delete-region beg end)))
+ (setq bibtex-last-kill-command 'entry))
+
+(defun bibtex-copy-entry-as-kill ()
+ (interactive)
+ (bibtex-kill-entry t))
+
+(defun bibtex-yank (&optional n)
+ "Reinsert the last BibTeX item.
+More precisely, reinsert the field or entry killed or yanked most recently.
+With argument N, reinsert the Nth most recently killed BibTeX item.
+See also the command \\[bibtex-yank-pop]]."
+ (interactive "*p")
+ (bibtex-insert-current-kill (1- n))
+ (setq this-command 'bibtex-yank))
+
+(defun bibtex-yank-pop (n)
+ "Replace just-yanked killed BibTeX item with a different.
+This command is allowed only immediately after a `bibtex-yank' or a
+`bibtex-yank-pop'.
+At such a time, the region contains a reinserted previously killed
+BibTeX item. `bibtex-yank-pop' deletes that item and inserts in its
+place a different killed BibTeX item.
+
+With no argument, the previous kill is inserted.
+With argument N, insert the Nth previous kill.
+If N is negative, this is a more recent kill.
+
+The sequence of kills wraps around, so that after the oldest one
+comes the newest one."
+ (interactive "*p")
+ (if (not (eq last-command 'bibtex-yank))
+ (error "Previous command was not a BibTeX yank"))
+ (setq this-command 'bibtex-yank)
+ (let ((inhibit-read-only t))
+ (delete-region (point) (mark t))
+ (bibtex-insert-current-kill n)))
-(defun bibtex-make-optional-field (e-t)
- (interactive "sOptional BibTeX entry type: ")
- (if (consp e-t)
- (setq e-t (cons (concat "OPT" (car e-t)) (cdr e-t)))
- (setq e-t (concat "OPT" e-t)))
- (bibtex-make-field e-t))
+(defun bibtex-empty-field ()
+ "Delete the text part of the current field, replace with empty text."
+ (interactive)
+ (bibtex-inside-field)
+ (bibtex-enclosing-field)
+ (goto-char (match-beginning bibtex-text-in-field))
+ (delete-region (point) (match-end bibtex-text-in-field))
+ (insert (concat (bibtex-field-left-delimiter)
+ (bibtex-field-right-delimiter)) )
+ (bibtex-find-text t))
+
+(defun bibtex-pop-previous (arg)
+ "Replace text of current field with the similar field in previous entry.
+With arg, goes up ARG entries. Repeated, goes up so many times. May be
+intermixed with \\[bibtex-pop-next] (bibtex-pop-next)."
+ (interactive "p")
+ (bibtex-pop arg 'previous))
-;; What to do about crossref? if present, journal and year are
-;; both optional. Due to this, i move all of them into optional. -- MON
+(defun bibtex-pop-next (arg)
+ "Replace text of current field with the text of similar field in next entry.
+With arg, goes down ARG entries. Repeated, goes down so many times. May be
+intermixed with \\[bibtex-pop-previous] (bibtex-pop-previous)."
+ (interactive "p")
+ (bibtex-pop arg 'next))
+
+(defun bibtex-clean-entry (&optional new-label called-by-reformat)
+ "Finish editing the current BibTeX entry and clean it up.
+Checks that no required fields are empty and formats entry dependent
+on the value of bibtex-entry-format.
+If label of entry is empty or a prefix argument is given, calculate a
+new entry label (note: this only will work if fields in entry begin on
+separate lines prior to calling bibtex-clean-entry or if 'realign is
+contained in bibtex-entry-format).
+Don't call this on `string' or `preamble' entries.
+At end of the cleaning process, the functions in
+bibtex-clean-entry-hook are called with region narrowed to entry."
+ (interactive "P")
+ (bibtex-format-entry)
+ (let* ((case-fold-search t)
+ (eob (bibtex-end-of-entry))
+ (key (progn
+ (bibtex-beginning-of-entry)
+ (if (re-search-forward
+ bibtex-reference-head eob t)
+ (buffer-substring-no-properties
+ (match-beginning bibtex-key-in-head)
+ (match-end bibtex-key-in-head))))))
+ (if (or
+ new-label
+ (not key))
+ (progn
+ (let ((autokey
+ (if bibtex-autokey-edit-before-use
+ (read-from-minibuffer
+ "Key to use: " (bibtex-generate-autokey) nil nil
+ 'bibtex-key-history)
+ (bibtex-generate-autokey))))
+ (bibtex-beginning-of-entry)
+ (re-search-forward bibtex-reference-maybe-empty-head)
+ (if (match-beginning bibtex-key-in-head)
+ (delete-region (match-beginning bibtex-key-in-head)
+ (match-end bibtex-key-in-head)))
+ (insert autokey)
+ (let* ((start (bibtex-beginning-of-entry))
+ (end (progn
+ (bibtex-end-of-entry)
+ (if (re-search-forward
+ bibtex-reference-maybe-empty-head nil 'move)
+ (goto-char (match-beginning 0)))
+ (point)))
+ (entry (buffer-substring start end)))
+ (delete-region start end)
+ (let ((success
+ (or
+ called-by-reformat
+ (not bibtex-maintain-sorted-entries)
+ (bibtex-find-entry-location autokey t))))
+ (insert entry)
+ (forward-char -1)
+ (bibtex-beginning-of-entry)
+ (re-search-forward bibtex-reference-head)
+ (if (not success)
+ (error
+ "New inserted reference yields duplicate key."))))))))
+ (if (not called-by-reformat)
+ (save-excursion
+ (save-restriction
+ (narrow-to-region
+ (bibtex-beginning-of-entry) (bibtex-end-of-entry))
+ (bibtex-parse-keys t nil)
+ (run-hooks 'bibtex-clean-entry-hook)))))
+
+(defun bibtex-fill-entry ()
+ "Fill current entry.
+Realigns entry, so that every field starts on a separate line. Field
+names appear in column `bibtex-field-indentation', field text starts in
+column bibtex-text-indentation and continuation lines start here, too.
+If `bibtex-align-at-equal-sign' is non-nil, align equal signs also."
+ (interactive "*")
+ (let ((pnt (copy-marker (point)))
+ (end (copy-marker (bibtex-end-of-entry))))
+ (bibtex-beginning-of-entry)
+ (bibtex-delete-whitespace)
+ (indent-to-column bibtex-entry-offset)
+ (while (re-search-forward bibtex-field end t)
+ (let* ((begin-field
+ (copy-marker (match-beginning 0)))
+ (end-field
+ (copy-marker (match-end 0)))
+ (begin-name
+ (copy-marker (match-beginning bibtex-name-in-field)))
+ (end-name
+ (copy-marker (match-end bibtex-name-in-field))))
+ (goto-char begin-field)
+ (forward-char)
+ (bibtex-delete-whitespace)
+ (open-line 1)
+ (forward-char)
+ (indent-to-column
+ (+ bibtex-entry-offset bibtex-field-indentation))
+ (re-search-forward "[ \t\n]*=" end)
+ (replace-match "=")
+ (forward-char -1)
+ (if bibtex-align-at-equal-sign
+ (indent-to-column
+ (+ bibtex-entry-offset (- bibtex-text-indentation 2)))
+ (insert " "))
+ (forward-char)
+ (bibtex-delete-whitespace)
+ (if bibtex-align-at-equal-sign
+ (insert " ")
+ (indent-to-column bibtex-text-indentation))
+ (while (re-search-forward "[ \t\n]+" end-field 'move)
+ (replace-match " "))
+ (bibtex-do-auto-fill)))
+ (if (looking-at ",")
+ (forward-char))
+ (bibtex-delete-whitespace)
+ (open-line 1)
+ (forward-char)
+ (indent-to-column bibtex-entry-offset)
+ (goto-char pnt)))
+
+(defun bibtex-reformat (&optional additional-options called-by-convert-alien)
+ "Reformat all BibTeX entries in buffer or region.
+With prefix argument, read options for reformatting from minibuffer.
+With C-u C-u prefix argument, reuse previous answers (if any) again.
+If mark is active it reformats entries in region, if not in whole buffer."
+ (interactive "*P")
+ (let* ((pnt (point))
+ (use-previous-options
+ (and (equal (prefix-numeric-value additional-options) 16)
+ (or bibtex-reformat-previous-options
+ bibtex-reformat-previous-labels)))
+ (bibtex-entry-format
+ (if additional-options
+ (if use-previous-options
+ bibtex-reformat-previous-options
+ (setq
+ bibtex-reformat-previous-options
+ (delq
+ nil
+ (list
+ (if (or
+ called-by-convert-alien
+ (y-or-n-p
+ "Realign entries (recommended for files not created by BibTeX mode)? "))
+ 'realign)
+ (if (y-or-n-p
+ "Remove empty optional and alternative fields? ")
+ 'opts-or-alts)
+ (if (y-or-n-p
+ "Remove delimiters around pure numerical fields? ")
+ 'numerical-fields)
+ (if (y-or-n-p (concat
+ (if bibtex-comma-after-last-field
+ "Insert"
+ "Remove")
+ " comma at end of entry? "))
+ 'last-comma)
+ (if (y-or-n-p
+ "Replace double page dashes by single ones? ")
+ 'page-dashes)
+ (if (y-or-n-p
+ "Force delimiters? ")
+ 'delimiters)
+ (if (y-or-n-p
+ "Unify case of entry types and field names? ")
+ 'unify-case)))))
+ '(realign)))
+ (labels
+ (if additional-options
+ (if use-previous-options
+ bibtex-reformat-previous-labels
+ (setq
+ bibtex-reformat-previous-labels
+ (y-or-n-p "Generate automatically new reference labels? ")))))
+ bibtex-autokey-edit-before-use
+ (bibtex-sort-ignore-string-entries t)
+ (start-point
+ (if (bibtex-mark-active)
+ (region-beginning)
+ (progn
+ (bibtex-beginning-of-first-entry)
+ (bibtex-skip-to-valid-entry)
+ (point))))
+ (end-point
+ (if (bibtex-mark-active)
+ (region-end)
+ (point-max)))
+ (valid-bibtex-entry
+ (concat
+ "[ \t\n]+\\(@[ \t]*\\("
+ (mapconcat
+ (lambda (type)
+ (concat "\\(" (car type) "\\)"))
+ bibtex-entry-field-alist
+ "\\|")
+ "\\)\\)")))
+ (save-restriction
+ (narrow-to-region start-point end-point)
+ (if (memq 'realign bibtex-entry-format)
+ (progn
+ (goto-char (point-min))
+ (while (re-search-forward valid-bibtex-entry nil t)
+ (replace-match "\n\\1"))))
+ (goto-char start-point)
+ (bibtex-progress-message "Formatting" 1)
+ (bibtex-map-entries
+ (lambda (current)
+ (bibtex-progress-message)
+ (bibtex-clean-entry labels labels)
+ (if (memq 'realign bibtex-entry-format)
+ (progn
+ (bibtex-end-of-entry)
+ (bibtex-delete-whitespace)
+ (open-line 2)))))
+ (bibtex-progress-message 'done))
+ (if (and
+ labels
+ bibtex-maintain-sorted-entries
+ (not called-by-convert-alien))
+ (progn
+ (bibtex-sort-buffer)
+ (setq bibtex-keys nil)
+ (bibtex-parse-keys nil t t)))
+ (goto-char pnt)))
+
+(defun bibtex-convert-alien (&optional do-additional-reformatting)
+ "Converts an alien BibTeX buffer to be fully usable by BibTeX mode.
+If a file doesn't confirm with some standards used by BibTeX mode,
+some of the high-level features of BibTeX mode won't be available.
+This function tries to convert current buffer to confirm with these standards.
+With prefix argument DO-ADDITIONAL-REFORMATTING
+non-nil, read options for reformatting entries from minibuffer."
+ (interactive "*P")
+ (message "Starting to validate buffer...")
+ (sit-for 1 nil t)
+ (goto-char (point-min))
+ (while (re-search-forward "[ \t\n]+@" nil t)
+ (replace-match "\n@"))
+ (message
+ "If errors occur, correct them and call `bibtex-convert-alien' again")
+ (sit-for 5 nil t)
+ (if (let ((bibtex-mark-active)
+ bibtex-maintain-sorted-entries)
+ (bibtex-validate))
+ (progn
+ (message "Starting to reformat entries...")
+ (sit-for 2 nil t)
+ (bibtex-reformat do-additional-reformatting t)
+ (if bibtex-maintain-sorted-entries
+ (progn
+ (message "Starting to sort buffer...")
+ (bibtex-sort-buffer)))
+ (goto-char (point-max))
+ (message "Buffer is now parsable. Please save it."))))
+
+(defun bibtex-complete-string ()
+ "Complete word fragment before point to longest prefix of a defined string.
+If point is not after the part of a word, all strings are listed.
+Remove surrounding delimiters if complete string could be expanded."
+ (interactive "*")
+ (bibtex-complete bibtex-strings t))
+
+(defun bibtex-complete-key ()
+ "Complete word fragment before point to longest prefix of a defined key.
+If point is not after the part of a word, all keys are listed. This
+function is most useful in completing crossref entries."
+ (interactive "*")
+ (if (not bibtex-keys)
+ (bibtex-parse-keys nil t))
+ (bibtex-complete bibtex-keys))
(defun bibtex-Article ()
(interactive)
(interactive)
(bibtex-entry "Booklet"))
-;(defun bibtex-DEAthesis ()
-; (interactive)
-; (bibtex-entry "DEAthesis"))
-
(defun bibtex-InBook ()
(interactive)
(bibtex-entry "InBook"))
(interactive)
(bibtex-entry "Unpublished"))
-(defun bibtex-string ()
+(defun bibtex-String ()
(interactive)
- (bibtex-move-outside-of-entry)
- (insert "@string{ = \"\"}\n")
- (previous-line 1)
- (forward-char 8))
-
-(defun bibtex-preamble ()
+ (if (not bibtex-keys)
+ (bibtex-parse-keys nil t))
+ (let ((key
+ (if (and
+ bibtex-maintain-sorted-entries
+ (not bibtex-sort-ignore-string-entries))
+ (completing-read
+ "String key: " bibtex-keys nil nil nil 'bibtex-key-history))))
+ (if (and
+ bibtex-maintain-sorted-entries
+ (not bibtex-sort-ignore-string-entries))
+ (bibtex-find-entry-location key)
+ (bibtex-move-outside-of-entry))
+ (indent-to-column bibtex-entry-offset)
+ (insert
+ (concat
+ "@String"
+ (bibtex-entry-left-delimiter)
+ (if (and
+ bibtex-maintain-sorted-entries
+ (not bibtex-sort-ignore-string-entries))
+ key)
+ " = "
+ (bibtex-field-left-delimiter)
+ (bibtex-field-right-delimiter)
+ (bibtex-entry-right-delimiter)
+ "\n"))
+ (forward-line -1)
+ (forward-char
+ (if (and
+ bibtex-maintain-sorted-entries
+ (not bibtex-sort-ignore-string-entries))
+ (+ (length "@String{") (length key) (length " = {"))
+ (length "@String{")))))
+
+(defun bibtex-Preamble ()
(interactive)
(bibtex-move-outside-of-entry)
- (insert "@Preamble{}\n")
- (previous-line 1)
+ (indent-to-column bibtex-entry-offset)
+ (insert
+ "@Preamble"
+ (bibtex-entry-left-delimiter)
+ (bibtex-entry-right-delimiter)
+ "\n")
+ (forward-line -1)
(forward-char 10))
-\f
-(defun bibtex-next-field (arg)
- "Finds end of text of next BibTeX field; with arg, to its beginning"
- (interactive "P")
- (bibtex-inside-field)
- (let ((start (point)))
- (condition-case ()
- (progn
- (bibtex-enclosing-field)
- (goto-char (match-end 0))
- (forward-char 2))
- (error
- (goto-char start)
- (end-of-line)
- (forward-char 1))))
- (bibtex-find-text arg))
-
-;; (defun bibtex-next-field ()
-;; "Finds end of text of next field."
-;; (interactive)
-;; (condition-case ()
-;; (progn
-;; (bibtex-inside-field)
-;; (re-search-forward ",[ \t\n]*" (point-max) 1)
-;; (bibtex-enclosing-field)
-;; (bibtex-inside-field))
-;; (error nil)))
-
-(defun bibtex-find-text (arg)
- "Go to end of text of current field; with arg, go to beginning."
- (interactive "P")
- (bibtex-inside-field)
- (bibtex-enclosing-field)
- (if arg
- (progn
- (goto-char (match-beginning bibtex-text-in-field))
- (if (looking-at "\"")
- (forward-char 1)))
- (goto-char (match-end bibtex-text-in-field))
- (if (= (preceding-char) ?\")
- (forward-char -1))))
-
-;; (defun bibtex-find-text ()
-;; "Go to end of text of current field."
-;; (interactive)
-;; (condition-case ()
-;; (progn
-;; (bibtex-inside-field)
-;; (bibtex-enclosing-field)
-;; (goto-char (match-end bibtex-text-in-field))
-;; (bibtex-inside-field))
-;; (error nil)))
-
-(defun bibtex-remove-OPT ()
- "Removes the 'OPT' starting optional arguments and goes to end of text"
- (interactive)
- (bibtex-inside-field)
- (bibtex-enclosing-field)
- (save-excursion
- (goto-char (match-beginning bibtex-name-in-field))
- (if (looking-at "OPT")
- ;; sct@dcs.edinburgh.ac.uk
- (progn
- (delete-char (length "OPT"))
- (search-forward "=")
- (delete-horizontal-space)
- (indent-to-column bibtex-text-alignment))))
- (bibtex-inside-field))
-
-(defun bibtex-inside-field ()
- "Try to avoid point being at end of a bibtex field."
- (interactive)
- (end-of-line)
- (skip-chars-backward " \t") ;MON - maybe delete these chars?
- (cond ((= (preceding-char) ?,)
- (forward-char -2))) ; -1 --> -2 sct@dcs.edinburgh.ac.uk
- (cond ((= (preceding-char) ?\")
- (forward-char -1)))) ;MON - only go back if quote
-
-(defun bibtex-remove-double-quotes ()
- "Removes \"\" around string."
- (interactive)
- (save-excursion
- (bibtex-inside-field)
- (bibtex-enclosing-field)
- (let ((start (match-beginning bibtex-text-in-field))
- (stop (match-end bibtex-text-in-field)))
- (goto-char stop)
- (forward-char -1)
- (if (looking-at "\"")
- (delete-char 1))
- (goto-char start)
- (if (looking-at "\"")
- (delete-char 1)))))
-
-(defun bibtex-kill-optional-field ()
- "Kill the entire enclosing optional BibTeX field"
- (interactive)
- (bibtex-inside-field)
- (bibtex-enclosing-field)
- (goto-char (match-beginning bibtex-name-in-field))
- (let ((the-end (match-end 0))
- (the-beginning (match-beginning 0)))
- (if (looking-at "OPT")
- (progn
- (goto-char the-end)
- (skip-chars-forward " \t\n,")
- (kill-region the-beginning the-end))
- (error "Mandatory fields can't be killed"))))
-
-(defun bibtex-empty-field ()
- "Delete the text part of the current field, replace with empty text"
- (interactive)
- (bibtex-inside-field)
- (bibtex-enclosing-field)
- (goto-char (match-beginning bibtex-text-in-field))
- (kill-region (point) (match-end bibtex-text-in-field))
- (insert "\"\"")
- (bibtex-find-text t))
\f
-(defun bibtex-pop-previous (arg)
- "Replace text of current field with the text of similar field in previous entry.
-With arg, go up ARG entries. Repeated, goes up so many times. May be
-intermixed with \\[bibtex-pop-next] (bibtex-pop-next)."
- (interactive "p")
- (bibtex-inside-field)
- (save-excursion
- ; parse current field
- (bibtex-enclosing-field)
- (let ((start-old-text (match-beginning bibtex-text-in-field))
- (stop-old-text (match-end bibtex-text-in-field))
- (start-name (match-beginning bibtex-name-in-field))
- (stop-name (match-end bibtex-name-in-field))
- (new-text))
- (goto-char start-name)
- ; construct regexp for previous field with same name as this one
- (let ((matching-entry
- (bibtex-cfield
- (buffer-substring (if (looking-at "OPT")
- (+ (point) (length "OPT"))
- (point))
- stop-name)
- bibtex-field-text)))
-
- ; if executed several times in a row, start each search where the
- ; last one finished
- (cond ((or (eq last-command 'bibtex-pop-previous)
- (eq last-command 'bibtex-pop-next))
- t
- )
- (t
- (bibtex-enclosing-reference)
- (setq bibtex-pop-previous-search-point (match-beginning 0))
- (setq bibtex-pop-next-search-point (match-end 0))))
- (goto-char bibtex-pop-previous-search-point)
-
- ; Now search for arg'th previous similar field
- (cond
- ((re-search-backward matching-entry (point-min) t arg)
- (setq new-text
- (buffer-substring (match-beginning bibtex-text-in-cfield)
- (match-end bibtex-text-in-cfield)))
- ; Found a matching field. Remember boundaries.
- (setq bibtex-pop-next-search-point (match-end 0))
- (setq bibtex-pop-previous-search-point (match-beginning 0))
- (bibtex-flash-head)
- ; Go back to where we started, delete old text, and pop new.
- (goto-char stop-old-text)
- (delete-region start-old-text stop-old-text)
- (insert new-text))
- (t ; search failed
- (error "No previous matching BibTeX field."))))))
- (setq this-command 'bibtex-pop-previous))
+;; Make BibTeX a Feature
-(defun bibtex-pop-next (arg)
- "Replace text of current field with the text of similar field in next entry.
-With arg, go up ARG entries. Repeated, goes up so many times. May be
-intermixed with \\[bibtex-pop-previous] (bibtex-pop-previous)."
- (interactive "p")
- (bibtex-inside-field)
- (save-excursion
- ; parse current field
- (bibtex-enclosing-field)
- (let ((start-old-text (match-beginning bibtex-text-in-field))
- (stop-old-text (match-end bibtex-text-in-field))
- (start-name (match-beginning bibtex-name-in-field))
- (stop-name (match-end bibtex-name-in-field))
- (new-text))
- (goto-char start-name)
- ; construct regexp for next field with same name as this one,
- ; ignoring possible OPT's
- (let ((matching-entry
- (bibtex-cfield
- (buffer-substring (if (looking-at "OPT")
- (+ (point) (length "OPT"))
- (point))
- stop-name)
- bibtex-field-text)))
-
- ; if executed several times in a row, start each search where the
- ; last one finished
- (cond ((or (eq last-command 'bibtex-pop-next)
- (eq last-command 'bibtex-pop-previous))
- t
- )
- (t
- (bibtex-enclosing-reference)
- (setq bibtex-pop-previous-search-point (match-beginning 0))
- (setq bibtex-pop-next-search-point (match-end 0))))
- (goto-char bibtex-pop-next-search-point)
-
- ; Now search for arg'th next similar field
- (cond
- ((re-search-forward matching-entry (point-max) t arg)
- (setq new-text
- (buffer-substring (match-beginning bibtex-text-in-cfield)
- (match-end bibtex-text-in-cfield)))
- ; Found a matching field. Remember boundaries.
- (setq bibtex-pop-next-search-point (match-end 0))
- (setq bibtex-pop-previous-search-point (match-beginning 0))
- (bibtex-flash-head)
- ; Go back to where we started, delete old text, and pop new.
- (goto-char stop-old-text)
- (delete-region start-old-text stop-old-text)
- (insert new-text))
- (t ; search failed
- (error "No next matching BibTeX field."))))))
- (setq this-command 'bibtex-pop-next))
-
-(defun bibtex-flash-head ()
- "Flash at BibTeX reference head before point, if exists. (Moves point)."
- (let ((flash))
- (cond ((re-search-backward bibtex-reference-head (point-min) t)
- (goto-char (match-beginning bibtex-type-in-head))
- (setq flash (match-end bibtex-key-in-reference)))
- (t
- (end-of-line)
- (skip-chars-backward " \t")
- (setq flash (point))
- (beginning-of-line)
- (skip-chars-forward " \t")))
- (if (pos-visible-in-window-p (point))
- (sit-for 1)
- (message "From: %s"
- (buffer-substring (point) flash)))))
-
-
-\f
-(defun bibtex-enclosing-field ()
- "Search for BibTeX field enclosing point.
-Point moves to end of field; also, use match-beginning and match-end
-to parse the field."
- ;; sct@dcs.edinburgh.ac.uk
- (let ((old-point (point)))
- (condition-case errname
- (bibtex-enclosing-regexp bibtex-field)
- (search-failed
- (goto-char old-point)
- (error "Can't find enclosing BibTeX field.")))))
-
-(defun bibtex-enclosing-reference ()
- "Search for BibTeX reference enclosing point.
-Point moves to end of reference; also, use match-beginning and match-end
-to parse the reference."
- ;; sct@dcs.edinburgh.ac.uk
- (let ((old-point (point)))
- (condition-case errname
- (bibtex-enclosing-regexp bibtex-reference)
- (search-failed
- (goto-char old-point)
- (error "Can't find enclosing BibTeX reference.")))))
-
-(defun bibtex-enclosing-regexp (regexp)
- "Search for REGEXP enclosing point.
-Point moves to end of REGEXP. See also match-beginning and match-end.
-If an enclosing REGEXP is not found, signals search-failed; point is left in
-an undefined location.
-
-[Doesn't something like this exist already?]"
-
- (interactive "sRegexp: ")
- ; compute reasonable limits for the loop
- (let* ((initial (point))
- (right (if (re-search-forward regexp (point-max) t)
- (match-end 0)
- (point-max)))
- (left
- (progn
- (goto-char initial)
- (if (re-search-backward regexp (point-min) t)
- (match-beginning 0)
- (point-min)))))
- ; within the prescribed limits, loop until a match is found
- (goto-char left)
- (re-search-forward regexp right nil 1)
- (if (> (match-beginning 0) initial)
- (signal 'search-failed (list regexp)))
- (while (<= (match-end 0) initial)
- (re-search-forward regexp right nil 1)
- (if (> (match-beginning 0) initial)
- (signal 'search-failed (list regexp))))
- ))
-
-(defun bibtex-clean-entry ()
- "For all optional fields of current BibTeX entry: if empty, kill the whole field; otherwise, remove the \"OPT\" string in the name; if text numerical, remove double-quotes. For all mandatory fields: if empty, signal error."
- (interactive)
- (beginning-of-bibtex-entry)
- (let ((start (point)))
- (save-restriction
- (narrow-to-region start (save-excursion (end-of-bibtex-entry) (point)))
- (while (re-search-forward bibtex-field (point-max) t 1)
- (let ((begin-field (match-beginning 0))
- (end-field (match-end 0))
- (begin-name (match-beginning bibtex-name-in-field))
- (end-name (match-end bibtex-name-in-field))
- (begin-text (match-beginning bibtex-text-in-field))
- (end-text (match-end bibtex-text-in-field))
- )
- (goto-char begin-name)
- (cond ((and
- (looking-at "OPT")
- bibtex-clean-entry-zap-empty-opts)
- (goto-char begin-text)
- (if (looking-at "\"\"") ; empty: delete whole field
- (delete-region begin-field end-field)
- ; otherwise: not empty, delete "OPT"
- (goto-char begin-name)
- (delete-char (length "OPT"))
- (progn
- ;; fixup alignment. [alarson:19920309.2047CST]
- (search-forward "=")
- (delete-horizontal-space)
- (indent-to-column bibtex-text-alignment))
- (goto-char begin-field) ; and loop to go through next test
- ))
- (t
- (goto-char begin-text)
- (cond ((looking-at "\"[0-9]+\"") ; if numerical,
- (goto-char end-text)
- (delete-char -1) ; delete enclosing double-quotes
- (goto-char begin-text)
- (delete-char 1)
- (goto-char end-field) ; go to end for next search
- (forward-char -2) ; to compensate for the 2 quotes deleted
- )
- ((looking-at "\"\"") ; if empty quotes, complain
- (forward-char 1)
- (if (not (or (equal (buffer-substring
- begin-name
- (+ begin-name 3))
- "OPT")
- (equal (buffer-substring
- begin-name
- (+ begin-name 3))
- "opt")))
- (error "Mandatory field ``%s'' is empty"
- (buffer-substring begin-name end-name))))
- (t
- (goto-char end-field))))))))
- (goto-char start)
- (end-of-bibtex-entry)
- ;; sct@dcs.edinburgh.ac.uk
- (save-excursion
- (previous-line 1)
- (end-of-line)
- (if (eq (preceding-char) ?,)
- (backward-delete-char 1)))
- (skip-whitespace-and-comments)))
-
-\f
-;;; Menus for bibtex mode
-
-(define-key bibtex-mode-map [menu-bar entry-types]
- (cons "Entry Types" (make-sparse-keymap "Entry Types")))
-
-(define-key bibtex-mode-map [menu-bar entry-types bibtex-InProceedings]
- '(" article in conference Proceedings " . bibtex-InProceedings))
-(define-key bibtex-mode-map [menu-bar entry-types bibtex-Article]
- '(" Article in journal " . bibtex-Article))
-(define-key bibtex-mode-map [menu-bar entry-types bibtex-Book]
- '(" Book " . bibtex-Book))
-(define-key bibtex-mode-map [menu-bar entry-types bibtex-Booklet]
- '(" Booklet " . bibtex-Booklet))
-(define-key bibtex-mode-map [menu-bar entry-types bibtex-InProceedings]
- '(" Conference " . bibtex-InProceedings))
-(define-key bibtex-mode-map [menu-bar entry-types bibtex-MastersThesis]
- '(" Master's Thesis " . bibtex-MastersThesis))
-;define-key bibtex-mode-map [menu-bar entry-types bibtex-DEAthesis]
-;'((" DEA Thesis " . bibtex-DEAthesis))
-(define-key bibtex-mode-map [menu-bar entry-types bibtex-PhdThesis]
- '(" Phd. Thesis " . bibtex-PhdThesis))
-(define-key bibtex-mode-map [menu-bar entry-types bibtex-TechReport]
- '(" Technical Report " . bibtex-TechReport))
-(define-key bibtex-mode-map [menu-bar entry-types bibtex-Manual]
- '(" technical Manual " . bibtex-Manual))
-(define-key bibtex-mode-map [menu-bar entry-types bibtex-Proceedings]
- '(" conference Proceedings " . bibtex-Proceedings))
-(define-key bibtex-mode-map [menu-bar entry-types bibtex-InBook]
- '(" a chapter in a Book " . bibtex-InBook))
-(define-key bibtex-mode-map [menu-bar entry-types bibtex-InCollection]
- '(" an article in a Collection " . bibtex-InCollection))
-(define-key bibtex-mode-map [menu-bar entry-types bibtex-Misc]
- '(" miscellaneous " . bibtex-Misc))
-(define-key bibtex-mode-map [menu-bar entry-types bibtex-Unpublished]
- '(" unpublished " . bibtex-Unpublished))
-(define-key bibtex-mode-map [menu-bar entry-types bibtex-string]
- '(" string " . bibtex-string))
-(define-key bibtex-mode-map [menu-bar entry-types bibtex-preamble]
- '(" preamble " . bibtex-preamble))
-
-(define-key bibtex-mode-map [menu-bar move/edit]
- (cons "Bibtex Edit" (make-sparse-keymap "Bibtex Edit")))
-
-(define-key bibtex-mode-map [menu-bar move/edit bibtex-next-field]
- '(" next field " . bibtex-next-field))
-(define-key bibtex-mode-map [menu-bar move/edit bibtex-find-text]
- '(" to end of field " . bibtex-find-text))
-(define-key bibtex-mode-map [menu-bar move/edit bibtex-pop-previous]
- '("snatch from similar preceding field" . bibtex-pop-previous))
-(define-key bibtex-mode-map [menu-bar move/edit bibtex-pop-next]
- '("snatch from similar following field" . bibtex-pop-next))
-(define-key bibtex-mode-map [menu-bar move/edit bibtex-remove-OPT]
- '(" remove OPT " . bibtex-remove-OPT))
-(define-key bibtex-mode-map [menu-bar move/edit bibtex-remove-double-quotes]
- '(" remove quotes " . bibtex-remove-double-quotes))
-(define-key bibtex-mode-map [menu-bar move/edit bibtex-clean-entry]
- '(" clean up entry " . bibtex-clean-entry))
-(define-key bibtex-mode-map [menu-bar move/edit find-bibtex-duplicates]
- '(" find duplicates " . find-bibtex-duplicates))
-(define-key bibtex-mode-map [menu-bar move/edit sort-bibtex-entries]
- '(" sort entries " . sort-bibtex-entries))
-(define-key bibtex-mode-map [menu-bar move/edit validate-bibtex-buffer]
- '(" validate entries " . validate-bibtex-buffer))
-\f
-
-;; Please don't send anything to bug-gnu-emacs about these Sunwindows functions
-;; since we aren't interested. See etc/SUN-SUPPORT for the reasons why
-;; we consider this nothing but a distraction from our work.
-
-;(defmacro eval-in-menu-window (&rest l)
-; "Evaluates its argument in the window in which the mouse button was pressed."
-; (list 'eval-in-window '*menu-window* l))
-
-;(defmenu bibtex-sun-entry-menu
-; ("Article In Conf. Proc." eval-in-menu-window bibtex-InProceedings)
-; ("Article In Journal" eval-in-menu-window bibtex-Article)
-; ("Book" eval-in-menu-window bibtex-Book)
-; ("Booklet" eval-in-menu-window bibtex-Booklet)
-; ("Master's Thesis" eval-in-menu-window bibtex-MastersThesis)
-; ("PhD. Thesis" eval-in-menu-window bibtex-PhdThesis)
-; ("Technical Report" eval-in-menu-window bibtex-TechReport)
-; ("Technical Manual" eval-in-menu-window bibtex-Manual)
-; ("Conference Proceedings" eval-in-menu-window bibtex-Proceedings)
-; ("In A Book" eval-in-menu-window bibtex-InBook)
-; ("In A Collection" eval-in-menu-window bibtex-InCollection)
-; ("Miscellaneous" eval-in-menu-window bibtex-Misc)
-; ("Unpublished" eval-in-menu-window bibtex-Unpublished)
-; ("string" eval-in-menu-window bibtex-string)
-; ("preamble" eval-in-menu-window bibtex-preamble))
-;
-;(defmenu bibtex-sun-menu
-; ("BibTeX menu")
-; ("add entry" . bibtex-sun-entry-menu)
-; ("next field" eval-in-menu-window bibtex-next-field nil)
-; ("to end of field" eval-in-menu-window bibtex-find-text nil)
-; ("snatch similar preceding field" eval-in-menu-window bibtex-pop-previous 1)
-; ("snatch similar following field" eval-in-menu-window bibtex-pop-next 1)
-; ("remove OPT" eval-in-menu-window bibtex-remove-OPT)
-; ("remove quotes" eval-in-menu-window bibtex-remove-double-quotes)
-; ("clean entry" eval-in-menu-window bibtex-clean-entry)
-; ("describe BibTeX mode" eval-in-menu-window describe-mode)
-; ("Main Emacs menu" . emacs-menu))
-
-;(defun bibtex-sun-menu-eval (window x y)
-; "Pop-up menu of BibTeX commands."
-; (sun-menu-evaluate window (1+ x) (1- y) 'bibtex-sun-menu))
-;
-;(defun bibtex-sun-environment ()
-; "Set up sun menus for BibTeX mode. Call it as bibtex-mode-hook, or
-;interactively"
-; (interactive)
-; (local-set-mouse '(text right) 'bibtex-sun-menu-eval))
-;
+(provide 'bibtex)
;;; bibtex.el ends here