; BibTeX-mode, by David Jacobson, dmj@uw-june ; Last edited Feb. 19, 1986 ; This provides help for editing .bib files for BibTeX. ; Several functions are provided: ; ; find-key will find a specified key in a sorted .bib file or find the place ; where that key should be inserted. ; ; @article will insert a shell with all the possible fields for an article ; @ will do the same for the other legal entry-types. ; ; next-field (ESC-n) will move to the next un-filled-in field and announce ; whether it is required, optional, etc. If next-field is invoked from ; the text portion of an empty field (typically dot is inside ""), ; that field is deleted, except on the key, in which case the last value ; used in a find-key will be inserted. ; ; justify-field (ESC-j) will justify a field. ; ; forward-entry and backward-entry do the obvious. They are bound to ESC-] ; and ESC-[ ; ; If the global varible bibliography-shell-directory is defined, ; the shells will be taken from there. ; ; If the global variable extra-bib-fields-file is defined, the contents of ; the named file will be added after the last field; commas are fixed. ; ; The function describe-abbrev-in-buffer (^X-^D) describes the bibTeX ; abbreviation under the cursor. Once it has been invoked there will be a ; buffer named Bib-Abbrevs. It can be perused to see all abbreviations. ; ; The function describe-cite-in-buffer is intended to be invoked in another ; buffer. (It is bound to ^X-^D in TeX-mode buffers.) It finds the ; bibliography entry corresponding to the citation under the dot and ; displays it in another window. If the same directory as the current ; file contains a file named .bibfile, the name of the bibliography ; file is taken from there. (No shell metacharacters like ~ or .., please.) ; If no .bibfile exists, the user is prompted for a file name. ; To change the bibfile used by a buffer set the buffer specific ; variable bibliography-file to the name of the desired file. ; ; ; BUGS and LIMITATIONS: ; ; justify-field requires the field be delimited by '"' marks. ; ; find-key is based on collating sequence rather than alphabetical order. ; ; The @ functions are not undoable, neither is the first ; invocation of describe-abbrev-in-buffer or describe-cite-in-buffer. ; ; The @ functions conflict with the same ones in Scribe-mode. ; Loading both packages at once will make one of them inoperative. ; ; When saving a file after invoking any @ function, the system ; will often give a warning approximately "File has been modified, do you ; want to overwrite it?" This is because of an emacs bug. Reply yes. ; ; Describe-cite-in-buffer malfunctions when invoked in its own bibfile. ; ; The re's that describe the beginning of an entry are not all consistent. ; It's sloppiness, not a feature. ; ; NOTE TO INSTALLERS: ; ; Hard coded is the directory ~dmj/latex/bibshells/ just below. If this ; is installed on machines other than uw-june, that entire directory must be ; copied and the new name must replace that below. ; (setq-default bibshells-loaded 0) (defun (insert-bib-shell bibshellfile thistype len bibtype (setq bibtype (arg 1 "Bibliography type: ")) (if (! bibshells-loaded) (progn (setq bibshells-loaded 1) (setq bibshellfile "emacs:bibshells") (if (is-bound bibliography-shell-file) (setq bibshellfile bibliography-shell-file)) (save-excursion (temp-use-buffer "bibshells") (setq needs-checkpointing 0) (insert-file bibshellfile) (while (! (error-occured (re-search-forward "^\\(\\w\\w*\\) .* \\([0-9][0-9]*\\) *`$") (region-around-match 1) (setq thistype (region-to-string)) (region-around-match 2) (setq len (region-to-string)) (beginning-of-line) (next-line) (save-excursion (temp-use-buffer thistype) (setq needs-checkpointing 0)) (set-mark) (provide-prefix-argument len (forward-character)))) (copy-region-to-buffer thistype)) (delete-buffer "bibshells")))) (set-mark) (yank-buffer bibtype) (newline) (goto-character (mark)) (if (is-bound extra-bib-fields-file) (save-excursion (re-search-forward "[ \t\n]*)") (region-around-match 0) (exchange-dot-and-mark) (insert-character ',') (end-of-line) ; probably is already there, but make sure (forward-character) (insert-file extra-bib-fields-file) ) ) (undo-boundary) (next-field) ) (@article (insert-bib-shell "article")) (@book (insert-bib-shell "book")) (@booklet (insert-bib-shell "booklet")) (@conference (insert-bib-shell "inproceedings")) (@inbook (insert-bib-shell "inbook")) (@incollection (insert-bib-shell "incollection")) (@inproceedings (insert-bib-shell "inproceedings")) (@manual (insert-bib-shell "manual")) (@mastersthesis (insert-bib-shell "mastersthesis")) (@misc (insert-bib-shell "misc")) (@phdthesis (insert-bib-shell "phdthesis")) (@proceedings (insert-bib-shell "proceedings")) (@techreport (insert-bib-shell "techreport")) (@unpublished (insert-bib-shell "unpublished")) (next-field newdot string end-of-entry (if (! (is-bound Key-Search-String)) (progn (declare-buffer-specific Key-Search-String) (setq Key-Search-String "") ) ) (setq end-of-entry 0) (if (& (! (bobp)) (save-excursion (backward-character) (looking-at "\"\"\\|=,\\|=[ \t\n]*)")) ; "" or =, or = ) (save-excursion (if (= (following-char) '"') (forward-character)) (set-mark) (search-reverse ",") (erase-region) (setq end-of-entry (looking-at "[ \t\n]*)")) ) (& (= (following-char) ',') (save-excursion (beginning-of-line) (looking-at "@[A-Za-z]*(,[ \t]*$") ) ) (insert-string Key-Search-String) ) (if end-of-entry (message "Last field deleted") (progn (save-excursion (re-search-forward "[*?@&|+]*>>") (region-around-match 0) (setq string (region-to-string)) (erase-region) (setq newdot (dot)) ) (goto-character newdot) (if (= string ">>") (message "Required") (= string "?>>")(message "Optional") (= string "|>>")(message "Or the following") (= string "&|>>")(message "And/Or the following") (= string "@>>") (message "Alternative") (= string "*>>") (message "Required only if no author or editor is specified") (= string "+>>") (message "Required: (" Key-Search-String ")") (message string " is unknown -- see a local expert") ; local expert: if the bibshell file is ; not corrupted, send a bug report to ; dmj@uw-june or ; ...ihnp4!uw-beaver!uw-june!dmj ) ) ) ) (next-key new-dot key (save-excursion (re-search-forward "^@.*(\\(.*\\),[ \t]*") (region-around-match 1) (setq key (region-to-string)) (setq new-dot (mark)) ) (goto-character new-dot) key ) (forward-entry (if (looking-at "^@.*(.*,") (end-of-line)) (if (error-occured (next-key)) (end-of-file) (beginning-of-line) ) (novalue) ) (previous-key new-dot key (save-excursion (beginning-of-line) ; emacs rev-searching finds the pattern if ; dot is anywhere in it. This prevents that. (re-search-reverse "^@.*(\\(.*\\),[ \t]*") (region-around-match 1) (setq key (region-to-string)) (setq new-dot (mark)) ) (goto-character new-dot) key ) (backward-entry (previous-key) (beginning-of-line) ) (find-key temp-key not-found (declare-buffer-specific Key-Search-String) (setq Key-Search-String (arg 1 ": find-key Key: ")) (beginning-of-file) (if (error-occured (while (progn (setq temp-key (next-key)) (< temp-key Key-Search-String)) (novalue) ) ; key we are on is >= Key-Search-String (if (!= temp-key Key-Search-String) ; must be > (progn (error-occured (beginning-of-line) (previous-line)) ) ) ) (end-of-file) ; pattern was not found ) (novalue) ) (fast-find-key ; not same semantics as find-key (re-search-forward (concat "^@[0-9A-Za-z]*(" (quote (arg 1 "Key: ")) ",") ) ) (justify-field key-loc eq-loc c-col last-col start-col dot-col dot-char no-quote (setq dot-char (following-char)) (setq dot-col (current-column)) (save-excursion (previous-key) (setq key-loc (dot)) ) (save-excursion (if (looking-at ".*="); there is an = ahead (search-forward "=") (progn (search-reverse "=") (if (< (dot) key-loc) (error-message "Cannot find field")) (forward-character) ) ) (setq eq-loc (dot)) (if (! (looking-at "[ \t]*\"")) (error-message "Not a quoted string") ) (search-forward "\"") ; close space between '=' and '"' ; (backward-character) ; (delete-white-space) ; (forward-character) (setq start-col (current-column)) ; find end of region to justify (if (error-occured (re-search-forward "\"\\|^[ \t]*\n\\|^[ \t][ \t]*)[ \t]*$\\|^@.*(")) (progn (end-of-file) (setq no-quote 1) ) (progn (re-search-reverse "") (if (setq no-quote (! (looking-at "\""))) (previous-line) ) ) ) ; make it one big line (while (progn (beginning-of-line) (> (dot) eq-loc) ) (delete-previous-character) (delete-white-space) (insert-character ' ') ) ; reassemble into lines of the right length (setq left-margin start-col) (end-of-line) (setq c-col (current-column)) (while (progn (setq last-col c-col) (insert-character '!') (delete-previous-character) (end-of-line) (setq c-col (current-column)) (< c-col last-col)) ) (setq left-margin 1) ) ; save-excursion uses sticky pointers. We delete white space at ; end of lines and then re-insert it, which leaves the sticky ; pointer to the left of the space rather that to the right if ; that was where it was originally was. So here we fix it up. ; If there was nothing to justify, this will just be a no-op anyway. (if (& (!= dot-char (following-char)) (!= dot-char ' ') (!= dot-char '\t') (!= dot-char '\n')) (error-occured (search-forward (char-to-string dot-char)) (backward-character) ) ) ;when the dot is out in the left margin it winds up at the end of the ;previous line. We would like it to be the case that if no text ;moves, then neither does the cursor. But ;as a temporary hack we will just do the following: (if (& (eolp) (! (eobp))) (forward-character)) ;hack (if no-quote (message "Warning: no closing quote")) (novalue) ) (fetch&use-Bib-Abbrevs-buffer full-name abbrev (temp-use-buffer "Bib-Abbrevs") (setq needs-checkpointing 0) (setq case-fold-search 1) (if (= (buffer-size) 0) (progn (insert-file "/usr/lib/tex/macros/alpha.bst") (beginning-of-file) (search-forward "MACRO") (beginning-of-line) (set-mark) (beginning-of-file) (erase-region) (end-of-file) (set-mark) (search-reverse "MACRO") (next-line) (next-line) (erase-region) (beginning-of-file) (while (! (eobp)) (if (looking-at "^MACRO[ \t]*{\\([a-z]*\\)}[ \t\n]*{\\(.*\\)}.*$" ) (progn (region-around-match 1) (setq abbrev (region-to-string)) (region-around-match 2) (setq full-name (region-to-string)) (region-around-match 0) (erase-region) (insert-string abbrev) (insert-character '\t') (insert-string full-name) (beginning-of-line) (next-line) ) (progn (set-mark) (next-line) (erase-region) ) ) ) ) ) ) (describe-abbrev-in-buffer abbrev expanded (save-excursion (error-occured (forward-character)) (backward-word) (set-mark) (forward-word) (setq abbrev (region-to-string)) (fetch&use-Bib-Abbrevs-buffer) (beginning-of-file) (search-forward (concat abbrev "\t")) (set-mark) (end-of-line) (setq expanded (region-to-string)) ) (message expanded) ) (current-file-directory fname (setq fname (current-file-name)) (save-excursion (temp-use-buffer "Bibfilename Buffer") (erase-buffer) (insert-string fname) (search-reverse "/") (set-mark) (end-of-line) (erase-region) (beginning-of-line) (region-to-string) ) ) (describe-cite-in-buffer bib-key filename indirectbibfile currentdir (if (| (! (is-bound bibliography-file)) (= bibliography-file "") ) (progn (declare-buffer-specific bibliography-file) (setq currentdir (current-file-directory)) (setq indirectbibfile (concat currentdir "/.bibfile")) (if (file-exists indirectbibfile) (save-excursion (temp-use-buffer "Bibfile Name") (erase-buffer) (insert-file indirectbibfile) (beginning-of-file) (set-mark) (if (!= (following-char) '/') (progn (insert-string currentdir) (insert-character '/') ) ) (end-of-line) (delete-white-space) (setq filename (region-to-string)) ) (setq filename (get-tty-file "Bibliography file: ")) ) (setq bibliography-file "") (if (= filename "") (error-message "No file name")) (if (! (file-exists filename)) (if (!= 'y' (string-to-char (get-tty-string (concat "\"" filename "\" does not exist. Should I go on? ")))) (error-message "Aborted.") ) ) ) (setq filename bibliography-file) ) (save-excursion orig-buffer first-time (setq orig-buffer (dot)) (re-search-reverse "[{, \t\n]\\([^,} \t\n]*\\)[,} \t\n]\\|[{, \t\n]\\([^,} \t\n]*\\)\\'") (region-around-match 1) ; re-search-reverse == back up 1 char at a time, do looking-at (setq bib-key (region-to-string)) (if (= bib-key "") (progn (region-around-match 2) (setq bib-key (region-to-string))) ) (setq first-time (= bibliography-file "")) (error-occured (visit-file filename)) (if (& first-time (!= mode-string "BibTeX") ) (if (= 'y' (string-to-char (get-tty-string (concat (current-buffer-name) " not in BibTeX mode. Should I set it? ")))) (bibTeX-mode) ) ) (setq filename (current-file-name)) (save-excursion (temp-use-buffer orig-buffer) (setq bibliography-file filename) ) (beginning-of-file) (if (error-occured (fast-find-key bib-key)) (progn (find-key bib-key) (error-message "\""bib-key "\" not found") ) (line-to-top-of-window) ) ) ) (bibTeX-mode (TeX-mode) (local-bind-to-key "next-field" "\en") (local-bind-to-key "justify-field" "\ej") (local-bind-to-key "forward-entry" "\e]") (if (| (= (global-binding-of "\e[") "backward-paragraph") (= (global-binding-of "\e[") "nothing") ) (local-bind-to-key "backward-entry" "\e[")) ; vt100's and vt2xx's use ESC-[ for their special keys. ; If it gets locally bound to backward-entry real havoc ensues. (local-bind-to-key "describe-abbrev-in-buffer" "\^X\^D") (remove-local-binding '"') (setq mode-string "BibTeX") (declare-buffer-specific Key-Search-String) (setq Key-Search-String "") (novalue) ) )