bibtex.el 45.2 KB
Newer Older
Eric S. Raymond's avatar
Eric S. Raymond committed
1 2
;;; bibtex.el --- BibTeX mode for GNU Emacs

Eric S. Raymond's avatar
Eric S. Raymond committed
3 4
;; Copyright (C) 1992 Free Software Foundation, Inc.

Eric S. Raymond's avatar
Eric S. Raymond committed
5 6 7 8 9 10 11
;; Author: Bengt Martensson <ubrinf!mond!bengt>
;;	Mark Shapiro <shapiro@corto.inria.fr>
;;	Mike Newton <newton@gumby.cs.caltech.edu>
;;	Aaron Larson <alarson@src.honeywell.com>
;; Maintainer: Mark Shapiro <shapiro@corto.inria.fr>
;; Keywords: tex, bib

Jim Blandy's avatar
Jim Blandy committed
12 13 14 15
;; This file is part of GNU Emacs.

;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
Richard M. Stallman's avatar
Richard M. Stallman committed
16
;; the Free Software Foundation; either version 2, or (at your option)
Jim Blandy's avatar
Jim Blandy committed
17 18 19 20 21 22 23 24 25 26 27
;; any later version.

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING.  If not, write to
;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.

Eric S. Raymond's avatar
Eric S. Raymond committed
28
;;; Change Log:
Eric S. Raymond's avatar
Eric S. Raymond committed
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45

;;; 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
Jim Blandy's avatar
Jim Blandy committed
46 47 48 49 50 51 52 53 54 55 56 57
;;; Mike Newton (newton@gumby.cs.caltech.edu) 90.11.17
;;;  * Handle items like
;;;          title = poft # "Fifth Tri-quaterly" # 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 wouldnt 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.
Eric S. Raymond's avatar
Eric S. Raymond committed
58
;;;   Updated documentation strings accordingly.  Added (provide 'bibtex).
Jim Blandy's avatar
Jim Blandy committed
59 60 61 62 63 64 65 66 67
;;;   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.
Eric S. Raymond's avatar
Eric S. Raymond committed
68

Jim Blandy's avatar
Jim Blandy committed
69 70 71 72 73 74
;;; 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
Eric S. Raymond's avatar
Eric S. Raymond committed
75
;;;  * skip-whitespace replaced by skip-chars-forward
Jim Blandy's avatar
Jim Blandy committed
76 77 78 79 80 81 82 83 84 85 86 87 88
;;;  * 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
Eric S. Raymond's avatar
Eric S. Raymond committed
89
;;;  * Fixed bug in bibtex-flash-entry
Jim Blandy's avatar
Jim Blandy committed
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
;;;  * 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
Eric S. Raymond's avatar
Eric S. Raymond committed
122
;;; Bengt Martensson <bengt@mathematik.uni-Bremen.de> 87-06-28
Jim Blandy's avatar
Jim Blandy committed
123 124 125 126 127 128 129
;;;   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!

Eric S. Raymond's avatar
Eric S. Raymond committed
130 131
;;; Code:

Eric S. Raymond's avatar
Eric S. Raymond committed
132 133 134 135 136 137 138 139 140 141
(provide 'bibtex)

;;; these guys typically don't have autoloads...[alarson:19920131.1548CST]
(if (not (fboundp 'TeX-insert-quote))
    (autoload 'TeX-insert-quote "tex-mode"))
(if (not (fboundp 'sort-subr))
    (autoload 'sort-subr "sort"))



Jim Blandy's avatar
Jim Blandy committed
142 143 144 145
(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) "")
Eric S. Raymond's avatar
Eric S. Raymond committed
146

Jim Blandy's avatar
Jim Blandy committed
147
(defvar bibtex-pop-previous-search-point nil
Jim Blandy's avatar
Jim Blandy committed
148
  "Next point where bibtex-pop-previous should start looking for a similar
Jim Blandy's avatar
Jim Blandy committed
149
entry.")
Eric S. Raymond's avatar
Eric S. Raymond committed
150

Jim Blandy's avatar
Jim Blandy committed
151
(defvar bibtex-pop-next-search-point nil
Jim Blandy's avatar
Jim Blandy committed
152
  "Next point where bibtex-pop-next should start looking for a similar
Jim Blandy's avatar
Jim Blandy committed
153 154 155
entry.")

(defvar bibtex-clean-entry-zap-empty-opts t
Jim Blandy's avatar
Jim Blandy committed
156
  "*If non-nil, bibtex-clean-entry will delete all empty optional fields.")
Eric S. Raymond's avatar
Eric S. Raymond committed
157

Jim Blandy's avatar
Jim Blandy committed
158
(defvar bibtex-include-OPTcrossref t
Jim Blandy's avatar
Jim Blandy committed
159
  "*If non-nil, all entries will have an OPTcrossref field.")
Jim Blandy's avatar
Jim Blandy committed
160
(defvar bibtex-include-OPTkey t
Jim Blandy's avatar
Jim Blandy committed
161
  "*If non-nil, all entries will have an OPTkey field.")
Jim Blandy's avatar
Jim Blandy committed
162
(defvar bibtex-include-OPTannote t
Jim Blandy's avatar
Jim Blandy committed
163
  "*If non-nil, all entries will have an OPTannote field.")
Jim Blandy's avatar
Jim Blandy committed
164 165 166

;; note: the user should be allowed to have their own list of always
;;       available optional fields.  exs: "keywords" "categories"
Eric S. Raymond's avatar
Eric S. Raymond committed
167

Jim Blandy's avatar
Jim Blandy committed
168
(defvar bibtex-mode-user-optional-fields nil		;no default value
Jim Blandy's avatar
Jim Blandy committed
169 170
  "*List of optional fields that user want to have as always present 
when making a bibtex entry.  One possibility is for ``keywords''")
Jim Blandy's avatar
Jim Blandy committed
171 172 173 174 175 176 177 178 179 180 181 182


;;; 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)
Jim Blandy's avatar
Jim Blandy committed
183
  "Create a regexp for a bibtex field of name NAME and text TEXT"
Jim Blandy's avatar
Jim Blandy committed
184 185 186 187 188 189
  (concat ",[ \t\n]*\\("
	  name
	  "\\)[ \t\n]*=[ \t\n]*\\("
	  text
	  "\\)"))
(defconst bibtex-name-in-cfield 1
Jim Blandy's avatar
Jim Blandy committed
190
  "The regexp subexpression number of the name part in bibtex-cfield.")
Jim Blandy's avatar
Jim Blandy committed
191
(defconst bibtex-text-in-cfield 2
Jim Blandy's avatar
Jim Blandy committed
192
  "The regexp subexpression number of the text part in bibtex-cfield.")
Jim Blandy's avatar
Jim Blandy committed
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208

(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.")
Jim Blandy's avatar
Jim Blandy committed
209

Eric S. Raymond's avatar
Eric S. Raymond committed
210
(defconst bibtex-field-string
Jim Blandy's avatar
Jim Blandy committed
211
  (concat
Eric S. Raymond's avatar
Eric S. Raymond committed
212 213 214
    "\"[^\"]*[^\\\\]\"\\|\"\"")
  "Match either a string or an empty string.")

Jim Blandy's avatar
Jim Blandy committed
215 216
(defconst bibtex-field-string-or-const
  (concat bibtex-field-const "\\|" bibtex-field-string)
Jim Blandy's avatar
Jim Blandy committed
217
  "Match either bibtex-field-string or bibtex-field-const.")
Jim Blandy's avatar
Jim Blandy committed
218 219 220 221 222

(defconst bibtex-field-text
  (concat
    "\\(" bibtex-field-string-or-const "\\)"
        "\\([ \t\n]+#[ \t\n]+\\(" bibtex-field-string-or-const "\\)\\)*\\|"
Eric S. Raymond's avatar
Eric S. Raymond committed
223
    "{[^{}]*[^\\\\]}")
Jim Blandy's avatar
Jim Blandy committed
224 225
  "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.
Eric S. Raymond's avatar
Eric S. Raymond committed
226 227 228 229 230
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.")
Jim Blandy's avatar
Jim Blandy committed
231 232 233 234 235 236

(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
Jim Blandy's avatar
Jim Blandy committed
237
  "The regexp subexpression number of the name part in bibtex-field")
Jim Blandy's avatar
Jim Blandy committed
238
(defconst bibtex-text-in-field bibtex-text-in-cfield
Jim Blandy's avatar
Jim Blandy committed
239
  "The regexp subexpression number of the text part in bibtex-field")
Jim Blandy's avatar
Jim Blandy committed
240 241 242 243

;;; references
(defconst bibtex-reference-type
  "@[A-Za-z]+"
Jim Blandy's avatar
Jim Blandy committed
244
  "Regexp defining the type part of a bibtex reference entry")
Jim Blandy's avatar
Jim Blandy committed
245 246 247 248 249 250
(defconst bibtex-reference-head
  (concat "^[ \t]*\\("
	  bibtex-reference-type
	  "\\)[ \t]*[({]\\("
	  bibtex-field-name
	  "\\)")
Jim Blandy's avatar
Jim Blandy committed
251
  "Regexp defining format of the header line of a bibtex reference entry")
Jim Blandy's avatar
Jim Blandy committed
252
(defconst bibtex-type-in-head 1
Jim Blandy's avatar
Jim Blandy committed
253
  "The regexp subexpression number of the type part in bibtex-reference-head")
Jim Blandy's avatar
Jim Blandy committed
254
(defconst bibtex-key-in-head 2
Jim Blandy's avatar
Jim Blandy committed
255 256
  "The regexp subexpression number of the key part in
bibtex-reference-head")
Jim Blandy's avatar
Jim Blandy committed
257 258 259 260 261

(defconst bibtex-reference
  (concat bibtex-reference-head
	  "\\([ \t\n]*" bibtex-field "\\)*"
	  "[ \t\n]*[})]")
Jim Blandy's avatar
Jim Blandy committed
262
  "Regexp defining the format of a bibtex reference entry")
Jim Blandy's avatar
Jim Blandy committed
263
(defconst bibtex-type-in-reference bibtex-type-in-head
Jim Blandy's avatar
Jim Blandy committed
264
  "The regexp subexpression number of the type part in bibtex-reference")
Jim Blandy's avatar
Jim Blandy committed
265
(defconst bibtex-key-in-reference bibtex-key-in-head
Jim Blandy's avatar
Jim Blandy committed
266 267
  "The regexp subexpression number of the key part in
bibtex-reference")
Jim Blandy's avatar
Jim Blandy committed
268

Eric S. Raymond's avatar
Eric S. Raymond committed
269
;;; strings
Jim Blandy's avatar
Jim Blandy committed
270 271 272 273 274 275
(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]*[})]")
Jim Blandy's avatar
Jim Blandy committed
276
  "Regexp defining the format of a bibtex string entry")
Jim Blandy's avatar
Jim Blandy committed
277
(defconst bibtex-name-in-string 1
Jim Blandy's avatar
Jim Blandy committed
278
  "The regexp subexpression of the name part in bibtex-string")
Jim Blandy's avatar
Jim Blandy committed
279
(defconst bibtex-text-in-string 2
Jim Blandy's avatar
Jim Blandy committed
280
  "The regexp subexpression of the text part in bibtex-string")
Jim Blandy's avatar
Jim Blandy committed
281

Eric S. Raymond's avatar
Eric S. Raymond committed
282
(defconst bibtex-name-alignement 2
Jim Blandy's avatar
Jim Blandy committed
283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
  "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.")

;;; 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].
\\[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 \"\".

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.

Use \\[bibtex-find-text] 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.
\\[bibtex-sun-environment] binds a mode-specific Sun menu to right
mouse button.

Fields:
    address
Eric S. Raymond's avatar
Eric S. Raymond committed
321
           Publisher's address
Jim Blandy's avatar
Jim Blandy committed
322 323 324 325 326 327 328 329
    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
Eric S. Raymond's avatar
Eric S. Raymond committed
330
           Chapter number
Jim Blandy's avatar
Jim Blandy committed
331 332 333
    crossref
	   The database key of the entry being cross referenced.
    edition
Eric S. Raymond's avatar
Eric S. Raymond committed
334
           Edition of a book (e.g., ""second"")
Jim Blandy's avatar
Jim Blandy committed
335 336 337 338 339 340 341 342 343 344 345
    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
Eric S. Raymond's avatar
Eric S. Raymond committed
346
           Alphabetizing and labeling key (needed when no author or editor)
Jim Blandy's avatar
Jim Blandy committed
347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
    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
Eric S. Raymond's avatar
Eric S. Raymond committed
367 368
           Type of a technical report (e.g., ""Research Note"") to be used
           instead of the default ""Technical Report""
Jim Blandy's avatar
Jim Blandy committed
369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410
    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 bibtex-mode-syntax-table
      (set-syntax-table bibtex-mode-syntax-table)
     (setq bibtex-mode-syntax-table (make-syntax-table))
     (set-syntax-table bibtex-mode-syntax-table)
     (modify-syntax-entry ?\" ".")
     (modify-syntax-entry ?$ "$$  ")
     (modify-syntax-entry ?% "<   ")
     (modify-syntax-entry ?'  "w   ")
     (modify-syntax-entry ?@  "w   ")
     (modify-syntax-entry ?\\ "\\")
     (modify-syntax-entry ?\f ">   ")
     (modify-syntax-entry ?\n ">   ")
     (modify-syntax-entry ?~ " "))
  (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-text)
  (define-key bibtex-mode-map "\n" 'bibtex-next-field)
  (define-key bibtex-mode-map "\C-c\"" 'bibtex-remove-double-quotes)
  (define-key bibtex-mode-map "\C-c\C-c" 'bibtex-clean-entry)
  (define-key bibtex-mode-map "\C-c?" 'describe-mode)
  (define-key bibtex-mode-map "\C-c\C-p" 'bibtex-pop-previous)
  (define-key bibtex-mode-map "\C-c\C-n" 'bibtex-pop-next)
  (define-key bibtex-mode-map "\C-c\C-k" 'bibtex-kill-optional-field)
  (define-key bibtex-mode-map "\C-c\C-d" 'bibtex-empty-field)

Eric S. Raymond's avatar
Eric S. Raymond committed
411 412 413 414 415 416
  ;; [alarson:19920131.1543CST]
  (define-key bibtex-mode-map "\""   'TeX-insert-quote)
  (define-key bibtex-mode-map "\C-c$"   'ispell-bibtex-entry)
  (define-key bibtex-mode-map "\M-\C-a"   'beginning-of-bibtex-entry)
  (define-key bibtex-mode-map "\M-\C-e"   'end-of-bibtex-entry)

Jim Blandy's avatar
Jim Blandy committed
417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450
  (define-key bibtex-mode-map "\C-c\C-e\C-a" 'bibtex-Article)
  (define-key bibtex-mode-map "\C-c\C-e\C-b" 'bibtex-Book)
  (define-key bibtex-mode-map "\C-c\C-e\C-d" 'bibtex-DEAthesis)
  (define-key bibtex-mode-map "\C-c\C-e\C-c" 'bibtex-InProceedings)
  (define-key bibtex-mode-map "\C-c\C-e\C-i" 'bibtex-InBook)
  (define-key bibtex-mode-map "\C-c\C-ei" 'bibtex-InCollection)
  (define-key bibtex-mode-map "\C-c\C-eI" 'bibtex-InProceedings)
  (define-key bibtex-mode-map "\C-c\C-e\C-m" 'bibtex-Manual)
  (define-key bibtex-mode-map "\C-c\C-em" 'bibtex-MastersThesis)
  (define-key bibtex-mode-map "\C-c\C-eM" 'bibtex-Misc)
  (define-key bibtex-mode-map "\C-c\C-o" 'bibtex-remove-OPT)
  (define-key bibtex-mode-map "\C-c\C-e\C-p" 'bibtex-PhdThesis)
  (define-key bibtex-mode-map "\C-c\C-ep" 'bibtex-Proceedings)
  (define-key bibtex-mode-map "\C-c\C-eP" 'bibtex-preamble)
  (define-key bibtex-mode-map "\C-c\C-e\C-t" 'bibtex-TechReport)
  (define-key bibtex-mode-map "\C-c\C-e\C-s" 'bibtex-string)
  (define-key bibtex-mode-map "\C-c\C-e\C-u" 'bibtex-Unpublished)

  (auto-fill-mode 1)			; nice alignements
  (setq left-margin (+ bibtex-text-alignment 1))

  (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)))
Eric S. Raymond's avatar
Eric S. Raymond committed
451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626
  (re-search-forward "[ \t\n]*" (point-max) t))

(defun ispell-abstract ()
  (interactive)
  (beginning-of-bibtex-entry)
  (re-search-forward "^[ \t]*[OPT]*abstract[ \t]*=")
  (ispell-region (point)
		 (save-excursion (forward-sexp) (point))))

(defun beginning-of-bibtex-entry ()
  (interactive)
  (re-search-backward "^@" nil 'move))

(defun end-of-bibtex-entry ()
  (interactive)
  (re-search-forward "}$" nil 'move))
  
(defun ispell-bibtex-entry ()
  (interactive)
  (ispell-region (progn (beginning-of-bibtex-entry) (point))
		 (progn (end-of-bibtex-entry) (point))))

(defun narrow-to-bibtex-entry ()
  (interactive)
  (save-excursion
    (narrow-to-region (progn (beginning-of-bibtex-entry) (point))
		      (progn (end-of-bibtex-entry) (point)))))


(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 @.
With argument, show all text."
  (interactive "P")
  (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))
    (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"))))
    (setq selective-display (not arg))
    (set-buffer-modified-p modifiedp)))

(defun sort-bibtex-entries ()
  "Sort bibtex entries alphabetically by key.
Text before the first bibtex entry, and following the last is not effected.
Bugs:
  1. Text between the closing brace ending one bibtex entry, and the @ starting 
     the next, is considered part of the PRECEEDING entry.  Perhaps it should be
     part of the following entry."
  (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
	       ;; begining of record function
	       'forward-line
	       ;; end of record function
	       (function (lambda () (and (re-search-forward "}[ \t]*\n[\n \t]*@" nil 'move)
					 (forward-char -2))))
	       ;; start of key function
	       (function (lambda () (re-search-forward "{[ \t]*") 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."
  (beginning-of-bibtex-entry)
  (while (re-search-forward "^@[^{]*{[ \t]*\\([^,]*\\)" nil t)
    (funcall fun (buffer-substring (match-beginning 1) (match-end 1)))))
  
(defun find-bibtex-entry-location (entry-name)
  (interactive "sBibtex entry key: ")
  "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."
  (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]*$")
	  ;; 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!")))


(defvar bibtex-maintain-sorted-entries nil
  "*If true, bibtex-mode will attempt to maintain all bibtex entries in 
sorted order.")
Jim Blandy's avatar
Jim Blandy committed
627 628 629 630 631 632 633 634

;;
;; note: this should really take lists of strings OR of lists.  in the
;;       second case, one can use either list.  (ie:
;;                "name" (("crossref") ("journal" "year"))     )
;;

(defun bibtex-entry (entry-type required optional)
Eric S. Raymond's avatar
Eric S. Raymond committed
635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680
  (let (key)
    (if bibtex-maintain-sorted-entries
	(progn
	  (setq key (read-string (format "%s key: " entry-type)))
	  (find-bibtex-entry-location key)))
    (bibtex-move-outside-of-entry)
    (insert "@" entry-type "{")
    (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)
    (if bibtex-mode-user-optional-fields ;MON...
	(mapcar 'bibtex-make-optional-field 
		bibtex-mode-user-optional-fields))
    (if bibtex-include-OPTannote
	(bibtex-make-optional-field "annote"))
    (insert "\n}\n\n")
    (forward-char -3)
    (up-list -1)
    (forward-char 1)
    (if key
	(progn
	  (insert key)
	  (bibtex-next-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))

Jim Blandy's avatar
Jim Blandy committed
681

Jim Blandy's avatar
Jim Blandy committed
682 683 684
(defun bibtex-make-field (str)
  (interactive "sBibTeX entry type: ")
  (insert ",\n")
Eric S. Raymond's avatar
Eric S. Raymond committed
685
  (indent-to-column bibtex-name-alignement)
Jim Blandy's avatar
Jim Blandy committed
686 687
  (insert str " = ")
  (indent-to-column bibtex-text-alignment)
Eric S. Raymond's avatar
Eric S. Raymond committed
688
  (insert "\"\"")
Jim Blandy's avatar
Jim Blandy committed
689 690 691 692 693
  nil)

(defun bibtex-make-optional-field (str)
  (interactive "sOptional BibTeX entry type: ")
  (insert ",\n")
Eric S. Raymond's avatar
Eric S. Raymond committed
694 695 696 697
  (indent-to-column bibtex-name-alignement)
  (insert "OPT" str " = ")
  (indent-to-column bibtex-text-alignment)
  (insert "\"\"")
Jim Blandy's avatar
Jim Blandy committed
698 699 700 701 702 703 704 705 706 707 708 709 710 711
  nil)

;; 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-Article ()
  (interactive)
  (if bibtex-include-OPTcrossref
      (bibtex-entry "Article" '("author" "title")
		    '("journal" "year" "volume" "number" "pages"
		      "month" "note"))
    (bibtex-entry "Article" '("author" "title" "journal" "year")
		  '("volume" "number" "pages" "month" "note"))))

Eric S. Raymond's avatar
Eric S. Raymond committed
712

Jim Blandy's avatar
Jim Blandy committed
713 714 715
(defun bibtex-Book ()
  (interactive)
  (bibtex-entry "Book" '("author" "title" "publisher" "year")
Eric S. Raymond's avatar
Eric S. Raymond committed
716 717
		'("editor" "volume" "number" "series" "address"
			   "edition" "month" "note")))
Jim Blandy's avatar
Jim Blandy committed
718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743

(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)
  (if bibtex-include-OPTcrossref
      (bibtex-entry "InBook" '("author" "title" "chapter")
		    '("publisher" "year" "editor" "pages" "volume" "number"
		      "series" "address" "edition" "month" "type" "note"))
    (bibtex-entry "InBook" '("author" "title" "chapter" "publisher" "year")
		  '("editor" "pages" "volume" "number" "series" "address"
		    "edition" "month" "type" "note"))))

(defun bibtex-InCollection ()
  (interactive)
  (if bibtex-include-OPTcrossref
      (bibtex-entry "InCollection" '("author" "title")
Eric S. Raymond's avatar
Eric S. Raymond committed
744
		    '("booktitle" "publisher" "year"
Jim Blandy's avatar
Jim Blandy committed
745
		      "editor" "volume" "number" "series" "type" "chapter"
Eric S. Raymond's avatar
Eric S. Raymond committed
746
		      "pages" "address" "edition" "month" "note"))
Jim Blandy's avatar
Jim Blandy committed
747 748 749 750 751
    (bibtex-entry "InCollection" '("author" "title"
				   "booktitle" "publisher" "year")
		  '("editor" "volume" "number" "series" "type" "chapter"
		    "pages" "address" "edition" "month" "note"))))

Eric S. Raymond's avatar
Eric S. Raymond committed
752

Jim Blandy's avatar
Jim Blandy committed
753 754 755 756
(defun bibtex-InProceedings ()
  (interactive)
  (if bibtex-include-OPTcrossref
      (bibtex-entry "InProceedings" '("author" "title")
Eric S. Raymond's avatar
Eric S. Raymond committed
757 758 759
		    '("editor" "volume" "number" "series" "pages"
		      "booktitle" "year"
		      "organization" "publisher" "address" "month" "note"))
Jim Blandy's avatar
Jim Blandy committed
760 761 762 763
    (bibtex-entry "InProceedings" '("author" "title" "booktitle" "year")
		  '("editor" "volume" "number" "series" "pages"
		    "organization" "publisher" "address" "month" "note"))))

Eric S. Raymond's avatar
Eric S. Raymond committed
764

Jim Blandy's avatar
Jim Blandy committed
765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831
(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" "type")))

(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" "type" "note")))

(defun bibtex-Proceedings ()
  (interactive)
  (bibtex-entry "Proceedings" '("title" "year")
		'("editor" "volume" "number" "series" "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-preamble ()
  (interactive)
  (bibtex-move-outside-of-entry)
  (insert "@Preamble{}\n")
  (previous-line 1)
  (forward-char 10))

(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))

Eric S. Raymond's avatar
Eric S. Raymond committed
832 833 834 835 836 837 838 839 840 841 842
;; (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)))

Jim Blandy's avatar
Jim Blandy committed
843 844 845 846 847 848 849 850
(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))
Eric S. Raymond's avatar
Eric S. Raymond committed
851
	(if (looking-at "\"")
Jim Blandy's avatar
Jim Blandy committed
852 853
	    (forward-char 1)))
    (goto-char (match-end bibtex-text-in-field))
Eric S. Raymond's avatar
Eric S. Raymond committed
854
    (if (= (preceding-char) ?\")
Jim Blandy's avatar
Jim Blandy committed
855 856
	(forward-char -1))))

Eric S. Raymond's avatar
Eric S. Raymond committed
857 858 859 860 861 862 863 864 865 866 867
;; (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)))

Jim Blandy's avatar
Jim Blandy committed
868 869 870 871 872 873 874 875 876 877 878 879 880 881 882
(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 (length "OPT"))))
  (bibtex-inside-field))

(defun bibtex-inside-field ()
  "Try to avoid point being at end of a bibtex field."
  (interactive)
  (end-of-line)
Eric S. Raymond's avatar
Eric S. Raymond committed
883
  (skip-chars-backward " \t")		;MON - maybe delete these chars?
Jim Blandy's avatar
Jim Blandy committed
884 885
  (cond ((= (preceding-char) ?,)
	 (forward-char -1)))
Eric S. Raymond's avatar
Eric S. Raymond committed
886 887
  (cond ((= (preceding-char) ?\")
	 (forward-char -1))))		;MON - only go back if quote
Jim Blandy's avatar
Jim Blandy committed
888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101

(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))


(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))

(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)))))



(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)))	  
Eric S. Raymond's avatar
Eric S. Raymond committed
1102
    (while (<= (match-end 0) initial)
Jim Blandy's avatar
Jim Blandy committed
1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128
      (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)
  (bibtex-enclosing-reference)
  (goto-char (match-beginning 0))
  (let ((start (point)))
    (save-restriction
      (narrow-to-region start (match-end 0))
      (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)
Eric S. Raymond's avatar
Eric S. Raymond committed
1129
		 (if (looking-at "\"\"") ; empty: delete whole field
Jim Blandy's avatar
Jim Blandy committed
1130
		     (delete-region begin-field end-field)
Eric S. Raymond's avatar
Eric S. Raymond committed
1131
		   ; otherwise: not empty, delete "OPT"
Jim Blandy's avatar
Jim Blandy committed
1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154
		   (goto-char begin-name)
		   (delete-char (length "OPT"))
		   (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))
Eric S. Raymond's avatar
Eric S. Raymond committed
1155
					    "opt")))
Jim Blandy's avatar
Jim Blandy committed
1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223
			    (error "Mandatory field ``%s'' is empty"
				   (buffer-substring begin-name end-name))))
		       (t
			(goto-char end-field))))))))
    (goto-char start)
    (skip-chars-forward "@a-zA-Z")
    (bibtex-enclosing-reference)
    (goto-char (match-end 0))
    (skip-chars-forward " \t\n")))



;;; 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)
	     ("            Conference	          " . bibtex-InProceedings)
	     ("         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)
	     ("             preamble              " . bibtex-preamble)
	     )
	    ("Moving around and editing"
	     ("            next field             " . bibtex-next-field)
	     ("          to end of field          " . bibtex-find-text)
	     ("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 entry           " . bibtex-clean-entry)
	     )
	    ("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)
  )



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

Eric S. Raymond's avatar
Eric S. Raymond committed
1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256
(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))
Jim Blandy's avatar
Jim Blandy committed
1257 1258 1259 1260 1261 1262
 
(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 ()
Eric S. Raymond's avatar
Eric S. Raymond committed
1263 1264
  "Set up sun menus for BibTeX mode.  Call it as bibtex-mode-hook, or
interactively"
Jim Blandy's avatar
Jim Blandy committed
1265 1266 1267
  (interactive)
  (local-set-mouse  '(text right) 'bibtex-sun-menu-eval))

Eric S. Raymond's avatar
Eric S. Raymond committed
1268
;;; bibtex-mode.el ends here