1
0
mirror of https://git.savannah.gnu.org/git/emacs.git synced 2024-11-28 07:45:00 +00:00
emacs/lisp/textmodes/bibtex.el

1021 lines
37 KiB
EmacsLisp
Raw Normal View History

1991-05-09 21:50:45 +00:00
;;; BibTeX mode for GNU Emacs
;; Copyright (C) 1985, 1986, 1987, 1990 Free Software Foundation, Inc.
;; 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
;; the Free Software Foundation; either version 1, or (at your option)
;; 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.
;;; Mike Newton (newton@gumby.cs.caltech.edu) 91.1.20
;;; * bibtex.el/bibtex-mode : updated comments to indicate new use of
;;; address, add minor explanations and fix small omissions.
;;; * bibtex.el/bibtex-entry : fixed spelling of variable
;;; 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
;;; * put DEA thesis back in (why get rid of good code?)
;;; * merged into release 19 version code
;;; * if cross-ref'ing is on, put 'pages' near top of OPTs, as the other
;;; entries are unlikely to be used.
;;; * skip-whitespace replaced by skip-chars-forward (also done
;;; by Marc Shairo)
;;; 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
;;; * 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
;;; * 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
;;; 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)
(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.")
(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.")
;; note: the user should be allowed to have their own list of always
;; available optional fields. exs: "keywords" "categories"
(defvar bibtex-mode-user-optional-fields nil ;no default value
"*List of optional fields that user always wants present in a bibtex entry.
One possibility is for ``keywords''")
;;; 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.")
;; 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-field-string
(concat
"\"[^\"]*[^\\\\]\"\\|\"\"")
"Match either a string or an empty string.")
(defconst bibtex-field-string-or-const
(concat bibtex-field-const "\\|" bibtex-field-string)
"Match either `bibtex-field-string' or `bibtex-field-const'.")
;(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-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
(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.")
(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'.")
(defconst bibtex-name-alignement 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.")
;;; bibtex mode:
;;;###autoload
(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
Publisher's address, or for conference, location held
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 (or section or whatever).
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, labeling and cross-refing 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
---------------------------------------------------------
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)
(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)))
(re-search-forward "[ \t\n]*" (point-max) t))
;;
;; 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)
(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))
(defun bibtex-make-field (str)
(interactive "sBibTeX entry type: ")
(insert ",\n")
(indent-to-column bibtex-name-alignement)
(insert str " = ")
(indent-to-column bibtex-text-alignment)
(insert "\"\"")
nil)
(defun bibtex-make-optional-field (str)
(interactive "sOptional BibTeX entry type: ")
(insert ",\n")
(indent-to-column bibtex-name-alignement)
(insert "OPT" str " = ")
(indent-to-column bibtex-text-alignment)
(insert "\"\"")
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"))))
(defun bibtex-Book ()
(interactive)
(bibtex-entry "Book" '("author" "title" "publisher" "year")
'("editor" "volume" "number" "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)
(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"))))
;; In next 2, for crossref case, put pages near beginning of
;; optionals as it will be used most often -- MON
(defun bibtex-InCollection ()
(interactive)
(if bibtex-include-OPTcrossref
(bibtex-entry "InCollection" '("author" "title")
'("pages" "booktitle" "publisher" "year"
"editor" "volume" "number" "series" "type" "chapter"
"address" "edition" "month" "note"))
(bibtex-entry "InCollection" '("author" "title"
"booktitle" "publisher" "year")
'("editor" "volume" "number" "series" "type" "chapter"
"pages" "address" "edition" "month" "note"))))
(defun bibtex-InProceedings ()
(interactive)
(if bibtex-include-OPTcrossref
(bibtex-entry "InProceedings" '("author" "title")
'( "pages" "editor" "volume" "number" "series"
"booktitle" "year"
"organization" "publisher" "address" "month" "note"))
(bibtex-entry "InProceedings" '("author" "title" "booktitle" "year")
'("editor" "volume" "number" "series" "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" "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))
(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-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)
(skip-chars-backward " \t") ;delete these chars? -- MON
(cond ((= (preceding-char) ?,)
(forward-char -1)))
(cond ((= (preceding-char) ?\")
(forward-char -1)))) ;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))
(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)))
(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)
(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)
(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"))
(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)
(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.
(if (fboundp 'defmenu)
(progn
(defmenu bibtex-sun-entry-menu
("Article In Conf. Proc."
(lambda () (eval-in-window *menu-window* (bibtex-InProceedings))))
("Article In Journal"
(lambda () (eval-in-window *menu-window* (bibtex-Article))))
("Book"
(lambda () (eval-in-window *menu-window* (bibtex-Book))))
("Booklet"
(lambda () (eval-in-window *menu-window* (bibtex-Booklet))))
("Master's Thesis"
(lambda () (eval-in-window *menu-window* (bibtex-MastersThesis))))
;;("DEA Thesis" bibtex-DEAthesis)
("PhD. Thesis"
(lambda () (eval-in-window *menu-window* (bibtex-PhdThesis))))
("Technical Report"
(lambda () (eval-in-window *menu-window* (bibtex-TechReport))))
("Technical Manual"
(lambda () (eval-in-window *menu-window* (bibtex-Manual))))
("Conference Proceedings"
(lambda () (eval-in-window *menu-window* (bibtex-Proceedings))))
("In A Book"
(lambda () (eval-in-window *menu-window* (bibtex-InBook))))
("In A Collection"
(lambda () (eval-in-window *menu-window* (bibtex-InCollection))))
("Miscellaneous"
(lambda () (eval-in-window *menu-window* (bibtex-Misc))))
("Unpublished"
(lambda () (eval-in-window *menu-window* (bibtex-Unpublished)))))
(defmenu bibtex-sun-menu
("BibTeX menu")
("add entry" . bibtex-sun-entry-menu)
("add string"
(lambda () (eval-in-window *menu-window* (bibtex-string))))
;("next field" bibtex-next-position)
;("to end of field" bibtex-find-it)
; ("remove OPT"
; (lambda () (eval-in-window *menu-window* (bibtex-remove-opt))))
; ("remove quotes"
; (lambda () (eval-in-window *menu-window* (bibtex-remove-double-quotes))))
; ("remove this line"
; (lambda () (eval-in-window *menu-window* (kill-current-line))))
("describe BibTeX mode"
(lambda () (eval-in-window *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))
)) ; matches (if...
;;; ------------- end bibtex-mode.el -------------------------------