From:	CSBVAX::MRGATE!mcvax!corto.inria.fr!shapiro@uunet.uu.net@SMTP 14-JUL-1988 19:09
To:	ARISIA::EVERHART
Subj:	new, improved BibTeX mode for GNU Emacs


Received: from uunet.UU.NET by prep.ai.mit.edu; Thu, 14 Jul 88 12:52:37 EST
Received: from mcvax.UUCP by uunet.UU.NET (5.59/1.14) with UUCP 
	id AA17999; Thu, 14 Jul 88 04:40:37 EDT
Received: by mcvax.cwi.nl; Thu, 14 Jul 88 05:17:00 +0200 (MET)
Received: from blueberry.inria.fr (blueberry.uucp) by inria.inria.fr; Wed, 13 Jul 88 17:20:12 +0200 (MET)
Received: by blueberry.inria.fr (3.2/client.23-dec-86)
	id AA01293; Wed, 13 Jul 88 17:15:26 +0200
Date: Wed, 13 Jul 88 17:15:26 +0200
Message-Id: <8807131515.AA01293@blueberry.inria.fr>
Organization: INRIA, BP 105, 78153 Le Chesnay Cedex, France
	telephone +33(1)39-63-55-11, telex 697033 F, telecopy +33(1)39-63-53-30
From: Marc Shapiro <mcvax!inria.inria.fr!shapiro@uunet.uu.net>
Sender: mcvax!corto.inria.fr!shapiro@uunet.uu.net
To: info-gnu-emacs@prep.ai.mit.edu
Cc: langlois@chorus.fr, irisa!hleroy@uunet.uu.net, imag!krakowia@uunet.uu.net,
        Richard Stallman <rms@prep.ai.mit.edu>

steinmetz!sprite!montanaro, ubrinf!mond!bengt, gipsi!verlyck
BCC: netoperators@inria
Subject: new, improved BibTeX mode for GNU Emacs

Dear friends,

You will find below a much-improved BibTeX mode for Gnu Emacs.  The
comments in the beginning of the file explain the differences with the
older version.  Briefly, if point is currently in, say, an "author"
field, M-p will snatch the value of the previous author and put
it in the current field.  M-p n times in a row snatches the text of
the n'th previous "author" entry. C-u n M-p does the same.
M-n is similar to M-p except that it searches forward instead of
backwards.  M-n and M-p can be freely intermixed.

The old C-c C-k (delete-this-line) has been replaced by a more useful
C-c C-k (bibtex-clean-optionals), which looks at all the optional
fields of the current entry.  Those which are null are deleted, and
the others have their "OPT" part removed.

You are of course encouraged to play with this and report any bugs to
me.  Also, if there are some design choices you don't like, tell me.

Please note that this implementation insists on working on a
syntactically-correct file.  If, for instance, the current entry doesn't
have a name, you will get an error message "Can't find enclosing
bibtex reference".  If (for instance) the commas separating fields are
missing, it will complain "Can't find enclosing bibtex field".

One last comment: please look at the implementation of
bibtex-enclosing-regexp; if you can think of a better implementation,
send it to me.


						Marc Shapiro

INRIA, B.P. 105, 78153 Le Chesnay Cedex, France.  Tel.: +33 (1) 39-63-53-25
e-mail: shapiro@inria.inria.fr or: ...!mcvax!inria!shapiro


---- Here comes file ``~/bibtex-mode.el'' --------------------------
;;; BibTeX mode for GNU Emacs

;;; Marc Shapiro 13-jul-88 [based on ideas by Sacha Krakowiak of IMAG]
;;;  * M-p (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 M-n 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. 
;;;  To do:
;;;  * optimize bibtex-enclosing-regexp
    
;;; 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
;;;   Original version

;;; 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!

(defvar bibtex-mode-syntax-table nil "")
(defvar bibtex-mode-abbrev-table nil "")
(define-abbrev-table 'bibtex-mode-abbrev-table ())
(defvar bibtex-mode-map (make-sparse-keymap) "")
(defvar bibtex-pop-previous-search-point nil
  "Next point where bibtex-pop-previous should start looking for a similar
entry.")
(defvar bibtex-pop-next-search-point nil
  "Next point where bibtex-pop-next should start looking for a similar
entry.")

;;; 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:

;;; fields
(defun bibtex-cfield (name 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.")

(defconst bibtex-field-name "[A-Za-z][---A-Za-z0-9:_+]*"
  "Regexp defining the name part of a bibtex field.")
(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.")
(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")

(defconst bibtex-OPT-field-name "OPT[A-Za-z][---A-Za-z0-9:_+]*"
  "Regexp defining the name part of an optional bibtex field.")
(defconst bibtex-OPT-field
  (bibtex-cfield bibtex-OPT-field-name bibtex-field-text)
  "Regexp defining the format of an optional bibtex field")

;;; references
(defconst bibtex-reference-type
  "@[A-Za-z]+"
  "Regexp defining the type part of a bibtex reference entry")
(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
(defconst bibtex-string
  (concat "^[ \t]*@[sS][tT][rR][iI][nN][gG][ \t\n]*[({][ \t\n]*\\("
	  bibtex-field-name
	  "\\)[ \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")

;;; bibtex mode:

(defun bibtex-mode () 
  "Major mode for editing bibtex files.

\\{bibtex-mode-map}

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].  The command
\\[bibtex-clean-optionals] removes OPT from all non-empty optional fields, and
removes all empty optional fields, from the current entry.

Use \\[bibtex-find-it] to position the dot at the end of the current field.
Use \\[bibtex-next-field] to move to end of the next field.

(bibtex-x-environment) binds a mode-specific X menu to control+right
mouse button.

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
    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 Techreport (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
---------------------------------------------------------
Entry to this mode calls the value of bibtex-mode-hook
if that value is non-nil."
  (interactive)
  (kill-all-local-variables)
  (if (not bibtex-mode-syntax-table)
      (setq bibtex-mode-syntax-table (copy-syntax-table)))
  (set-syntax-table bibtex-mode-syntax-table)
  (modify-syntax-entry ?\$ "$$  ")
  (modify-syntax-entry ?\% "<   ")
  (modify-syntax-entry ?\f ">   ")
  (modify-syntax-entry ?\n ">   ")
  (modify-syntax-entry ?'  "w   ")
  (modify-syntax-entry ?@  "w   ")
  (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]*$")

  (define-key bibtex-mode-map "\t" 'bibtex-find-it)
  (define-key bibtex-mode-map "\n" 'bibtex-next-field)
  (define-key bibtex-mode-map "\e[25~" 'bibtex-next-field)
  (define-key bibtex-mode-map "\C-c""" 'bibtex-remove-double-quotes)
  (define-key bibtex-mode-map "\C-c\C-k" 'bibtex-clean-optionals)
  (define-key bibtex-mode-map "\C-c\C-a" 'bibtex-Article)
  (define-key bibtex-mode-map "\C-c\C-b" 'bibtex-Book)
  (define-key bibtex-mode-map "\C-c\C-d" 'bibtex-DEAthesis)
  (define-key bibtex-mode-map "\C-c\C-c" 'bibtex-InProceedings)
  (define-key bibtex-mode-map "\C-c\C-i" 'bibtex-InBook)
  (define-key bibtex-mode-map "\C-ci" 'bibtex-InCollection)
  (define-key bibtex-mode-map "\C-cI" 'bibtex-InProceedings)
  (define-key bibtex-mode-map "\C-c\C-m" 'bibtex-Manual)
  (define-key bibtex-mode-map "\C-cm" 'bibtex-MastersThesis)
  (define-key bibtex-mode-map "\C-cM" 'bibtex-Misc)
  (define-key bibtex-mode-map "\C-c\C-o" 'bibtex-remove-OPT)
  (define-key bibtex-mode-map "\C-c\C-p" 'bibtex-PhdThesis)
  (define-key bibtex-mode-map "\C-cp" 'bibtex-Proceedings)
  (define-key bibtex-mode-map "\C-c\C-t" 'bibtex-TechReport)
  (define-key bibtex-mode-map "\C-c\C-s" 'bibtex-string)
  (define-key bibtex-mode-map "\C-c\C-u" 'bibtex-Unpublished)
  (define-key bibtex-mode-map "\C-c?" 'describe-mode)
  (define-key bibtex-mode-map "\ep" 'bibtex-pop-previous)
  (define-key bibtex-mode-map "\en" 'bibtex-pop-next)

					; nice alignements
  (auto-fill-mode 1)
  (setq left-margin 17)

  (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 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"))
  (up-list -1)
  (forward-char 1))

(defun bibtex-make-entry (str)
  (interactive "s")
  (concat "  " str " = \t"""""))

(defun bibtex-make-OPT-entry (str)
  (interactive "s")
  (concat "  OPT" str " = \t"""""))
  
(defun bibtex-Article ()
  (interactive)
  (bibtex-entry "Article" '("author" "title" "journal" "year")
		'("volume" "number" "pages" "month" "note")))

(defun bibtex-Book ()
  (interactive)
  (bibtex-entry "Book" '("author" "title" "publisher" "year")
		'("editor" "volume" "series" "address"
			   "edition" "month" "note")))

(defun bibtex-Booklet ()
  (interactive)
  (bibtex-entry "Booklet" '("title")
		'("author" "howpublished" "address" "month" "year" "note")))

;; France: Dipl\^{o}me d'Etudes Approfondies (similar to Master's)
(defun bibtex-DEAthesis ()
  (interactive)
  (bibtex-entry "DEAthesis" '("author" "title" "school" "year")
		'("address" "month" "note")))

(defun bibtex-InBook ()
  (interactive)
  (bibtex-entry "InBook" '("author" "title" "chapter" "publisher" "year")
		'("editor" "pages" "volume" "series" "address"
			   "edition" "month" "note")))

(defun bibtex-InCollection ()
  (interactive)
  (bibtex-entry "InCollection" '("author" "title" "booktitle"
					  "publisher" "year")
		'("editor" "chapter" "pages" "address" "month" "note")))


(defun bibtex-InProceedings ()
  (interactive)
  (bibtex-entry "InProceedings" '("author" "title" "booktitle" "year")
		'("editor" "pages" "organization" "publisher"
			   "address" "month" "note")))

(defun bibtex-Manual ()
  (interactive)
  (bibtex-entry "Manual" '("title")
		'("author" "organization" "address" "edition" "year"
			   "month" "note")))

(defun bibtex-MastersThesis ()
  (interactive)
  (bibtex-entry "MastersThesis" '("author" "title" "school" "year")
		'("address" "month" "note")))

(defun bibtex-Misc ()
  (interactive)
  (bibtex-entry "Misc" '()
		'("author" "title" "howpublished" "year" "month" "note")))

(defun bibtex-PhdThesis ()
  (interactive)
  (bibtex-entry "PhdThesis" '("author" "title" "school" "year")
		'("address" "month" "note")))

(defun bibtex-Proceedings ()
  (interactive)
  (bibtex-entry "Proceedings" '("title" "year")
		'("editor" "publisher" "organization"
			   "address" "month" "note")))
(defun bibtex-TechReport ()
  (interactive)
  (bibtex-entry "TechReport" '("author" "title" "institution" "year")
		'("type" "number" "address" "month" "note")))


(defun bibtex-Unpublished ()
  (interactive)
  (bibtex-entry "Unpublished" '("author" "title" "note")
		'("year" "month")))

(defun bibtex-string ()
  (interactive)
  (bibtex-move-outside-of-entry)
  (insert "@string{ = """"}\n")
  (previous-line 1)
  (forward-char 8))


(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-it ()
  (interactive)
  "Go to end of text of current field."
  (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")
	(delete-char 3)))
  (bibtex-inside-field))

(defun bibtex-inside-field ()
  "Try to avoid point being at end of a bibtex field."
  (skip-chars-backward " \t")
  (cond ((= (preceding-char) ?,)
	 (forward-char -1)))
  (cond ((= (preceding-char) ?\")
	 (forward-char -1))))

(defun bibtex-remove-double-quotes ()
  "Removes """" around string."
  (interactive)
  (save-excursion
    (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)))))

;;; X window menus for bibtex mode

(defun bibtex-x-help (arg)
  "Mouse commands for BibTeX mode"

  (let ((selection
	 (x-popup-menu
	  arg
	  '("BibTeX commands"
	    ("BibTeX entry types"
	     ("article in conference Proceedings" . bibtex-InProceedings)
	     ("Article in journal" . bibtex-Article)
	     ("Book" . bibtex-Book)
	     ("Booklet" . bibtex-Booklet)
	     ("Master's Thesis" . bibtex-MastersThesis)
	     ("DEA Thesis" . bibtex-DEAthesis)
	     ("Phd. Thesis" . bibtex-PhdThesis)
	     ("Technical Report" . bibtex-TechReport)
	     ("technical Manual" . bibtex-Manual)
	     ("conference Proceedings" . bibtex-Proceedings)
	     ("a chapter in a Book" . bibtex-InBook)
	     ("an article in a Collection" . bibtex-InCollection)
	     ("miscellaneous" . bibtex-Misc)
	     ("unpublished" . bibtex-Unpublished)
	     ("string" . bibtex-string)
	     )
	    ("Moving around and editing"
	     ("next field" . bibtex-next-field)
	     ("to end of field" . bibtex-find-it)
	     ("snatch from similar preceding field" . bibtex-pop-previous)
	     ("snatch from similar following field" . bibtex-pop-next)
	     ("remove OPT" . bibtex-remove-OPT)
	     ("remove quotes" . bibtex-remove-double-quotes)
	     ("clean up optional fields" . bibtex-clean-optionals)
	     )
	    ("help"
	     ("describe BibTeX mode" . describe-mode)
	     )))))
    (and selection (call-interactively selection))))

(defun bibtex-x-environment ()
  "Set up X menus for BibTeX mode.  Call it as bibtex-mode-hook, or interactively"
  (interactive)
  (require 'x-mouse)
  (define-key mouse-map x-button-c-right 'bibtex-x-help)
  )


(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) 3) ; 3 == strlen("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
	  (message "No previous matching BibTeX field.")
	  (ding))))))
  (setq this-command 'bibtex-pop-previous))

(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) 3) ; 3 == strlen("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
	  (message "No next matching BibTeX field.")
	  (ding))))))
  (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)))))



(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."
  (condition-case errname
      (bibtex-enclosing-regexp bibtex-field)
    (search-failed
     (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."
  (condition-case errname
      (bibtex-enclosing-regexp bibtex-reference)
    (search-failed
     (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-optionals ()
  "For all optional fields of current BibTeX entry: if empty, kill the whole field; otherwise, remove the \"OPT\" string in the name."
  (interactive)
  (bibtex-enclosing-reference)
  (save-excursion
    (save-restriction
      (goto-char (match-beginning 0))
      (narrow-to-region (point) (match-end 0))
      (while (re-search-forward bibtex-OPT-field (point-max) t 1)
	(let ((begin-field (match-beginning 0))
	      (end-field (match-end 0))
	      (begin-name (match-beginning bibtex-name-in-field)))
	  (goto-char (match-beginning bibtex-text-in-field))
	  (if (looking-at "\"\"")	; empty: delete whole field
	      (delete-region begin-field end-field)
	    ; otherwise: not empty, delete "OPT"
	    (goto-char begin-name)
	    (delete-char 3))		; 3 == strlen("OPT")
	  )))))

---- End of file ``~/bibtex-mode.el'' ------------------------------
