Commit | Line | Data |
---|---|---|
c0274f38 ER |
1 | ;;; bibtex.el --- BibTeX mode for GNU Emacs |
2 | ||
3731a850 | 3 | ;; Copyright (C) 1992, 1994, 1995, 1996, 1997, 1998, 1999, 2002, 2003, |
ceb4c4d3 | 4 | ;; 2004, 2005, 2006 Free Software Foundation, Inc. |
9750e079 | 5 | |
31bc4210 | 6 | ;; Author: Stefan Schoef <schoef@offis.uni-oldenburg.de> |
7fbf4804 SM |
7 | ;; Bengt Martensson <bengt@mathematik.uni-Bremen.de> |
8 | ;; Mark Shapiro <shapiro@corto.inria.fr> | |
9 | ;; Mike Newton <newton@gumby.cs.caltech.edu> | |
10 | ;; Aaron Larson <alarson@src.honeywell.com> | |
11 | ;; Dirk Herrmann <D.Herrmann@tu-bs.de> | |
e04464bb | 12 | ;; Maintainer: Roland Winkler <roland.winkler@physik.uni-erlangen.de> |
cb4ad359 | 13 | ;; Keywords: BibTeX, LaTeX, TeX |
f961a17c | 14 | |
745bc783 JB |
15 | ;; This file is part of GNU Emacs. |
16 | ||
17 | ;; GNU Emacs is free software; you can redistribute it and/or modify | |
18 | ;; it under the terms of the GNU General Public License as published by | |
a1ddedc6 | 19 | ;; the Free Software Foundation; either version 2, or (at your option) |
745bc783 JB |
20 | ;; any later version. |
21 | ||
22 | ;; GNU Emacs is distributed in the hope that it will be useful, | |
23 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
24 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
25 | ;; GNU General Public License for more details. | |
26 | ||
27 | ;; You should have received a copy of the GNU General Public License | |
b578f267 | 28 | ;; along with GNU Emacs; see the file COPYING. If not, write to the |
4fc5845f LK |
29 | ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
30 | ;; Boston, MA 02110-1301, USA. | |
745bc783 | 31 | |
5c69dbfc | 32 | ;;; Commentary: |
b578f267 | 33 | |
cb4ad359 | 34 | ;; Major mode for editing and validating BibTeX files. |
e5167999 | 35 | |
5c69dbfc | 36 | ;; Usage: |
02c8032e SM |
37 | ;; See documentation for function bibtex-mode or type "\M-x describe-mode" |
38 | ;; when you are in BibTeX mode. | |
e5167999 | 39 | |
5c69dbfc RS |
40 | ;; Todo: |
41 | ;; Distribute texinfo file. | |
9ae11a89 | 42 | |
5c69dbfc | 43 | ;;; Code: |
b578f267 | 44 | |
e0dc0c55 SM |
45 | (require 'button) |
46 | ||
28f2ee66 | 47 | \f |
5c69dbfc | 48 | ;; User Options: |
e5167999 | 49 | |
f754fb7b | 50 | (defgroup bibtex nil |
ffe68348 | 51 | "BibTeX mode." |
f754fb7b RS |
52 | :group 'tex |
53 | :prefix "bibtex-") | |
54 | ||
55 | (defgroup bibtex-autokey nil | |
ffe68348 | 56 | "Generate automatically a key from the author/editor and the title field." |
f754fb7b | 57 | :group 'bibtex |
ab2d0cdb | 58 | :prefix "bibtex-autokey-") |
f754fb7b RS |
59 | |
60 | (defcustom bibtex-mode-hook nil | |
61 | "List of functions to call on entry to BibTeX mode." | |
62 | :group 'bibtex | |
ab2d0cdb | 63 | :type 'hook) |
f754fb7b RS |
64 | |
65 | (defcustom bibtex-field-delimiters 'braces | |
02c8032e | 66 | "Type of field delimiters. Allowed values are `braces' or `double-quotes'." |
f754fb7b RS |
67 | :group 'bibtex |
68 | :type '(choice (const braces) | |
7fbf4804 | 69 | (const double-quotes))) |
50e4b39e | 70 | |
f754fb7b | 71 | (defcustom bibtex-entry-delimiters 'braces |
02c8032e | 72 | "Type of entry delimiters. Allowed values are `braces' or `parentheses'." |
f754fb7b RS |
73 | :group 'bibtex |
74 | :type '(choice (const braces) | |
7fbf4804 | 75 | (const parentheses))) |
cb4ad359 | 76 | |
f754fb7b | 77 | (defcustom bibtex-include-OPTcrossref '("InProceedings" "InCollection") |
02c8032e | 78 | "List of BibTeX entries that get an OPTcrossref field." |
f754fb7b RS |
79 | :group 'bibtex |
80 | :type '(repeat string)) | |
e5167999 | 81 | |
f754fb7b | 82 | (defcustom bibtex-include-OPTkey t |
d10e87a2 SM |
83 | "If non-nil, all newly created entries get an OPTkey field. |
84 | If this is a string, use it as the initial field text. | |
85 | If this is a function, call it to generate the initial field text." | |
f754fb7b RS |
86 | :group 'bibtex |
87 | :type '(choice (const :tag "None" nil) | |
7fbf4804 SM |
88 | (string :tag "Initial text") |
89 | (function :tag "Initialize Function" :value fun) | |
90 | (other :tag "Default" t))) | |
f754fb7b RS |
91 | |
92 | (defcustom bibtex-user-optional-fields | |
50e4b39e | 93 | '(("annote" "Personal annotation (ignored)")) |
02c8032e | 94 | "List of optional fields the user wants to have always present. |
50e4b39e | 95 | Entries should be of the same form as the OPTIONAL and |
02c8032e | 96 | CROSSREF-OPTIONAL lists in `bibtex-entry-field-alist' (which see)." |
f754fb7b | 97 | :group 'bibtex |
7fbf4804 SM |
98 | :type '(repeat (group (string :tag "Field") |
99 | (string :tag "Comment") | |
100 | (option (group :inline t | |
101 | :extra-offset -4 | |
102 | (choice :tag "Init" :value "" | |
103 | string | |
104 | function)))))) | |
105 | ||
106 | (defcustom bibtex-entry-format | |
107 | '(opts-or-alts required-fields numerical-fields) | |
02c8032e | 108 | "Type of formatting performed by `bibtex-clean-entry'. |
f0cb6034 | 109 | It may be t, nil, or a list of symbols out of the following: |
d0388eac RS |
110 | opts-or-alts Delete empty optional and alternative fields and |
111 | remove OPT and ALT prefixes from used fields. | |
7fbf4804 | 112 | required-fields Signal an error if a required field is missing. |
d0388eac RS |
113 | numerical-fields Delete delimiters around numeral fields. |
114 | page-dashes Change double dashes in page field to single dash | |
115 | (for scribe compatibility). | |
d10e87a2 SM |
116 | inherit-booktitle If entry contains a crossref field and the booktitle |
117 | field is empty, set the booktitle field to the content | |
118 | of the title field of the crossreferenced entry. | |
d0388eac | 119 | realign Realign entries, so that field texts and perhaps equal |
50e4b39e | 120 | signs (depending on the value of |
f0cb6034 | 121 | `bibtex-align-at-equal-sign') begin in the same column. |
d0388eac RS |
122 | last-comma Add or delete comma on end of last field in entry, |
123 | according to value of `bibtex-comma-after-last-field'. | |
124 | delimiters Change delimiters according to variables | |
125 | `bibtex-field-delimiters' and `bibtex-entry-delimiters'. | |
126 | unify-case Change case of entry and field names. | |
127 | ||
128 | The value t means do all of the above formatting actions. | |
129 | The value nil means do no formatting at all." | |
f754fb7b RS |
130 | :group 'bibtex |
131 | :type '(choice (const :tag "None" nil) | |
7fbf4804 SM |
132 | (const :tag "All" t) |
133 | (set :menu-tag "Some" | |
134 | (const opts-or-alts) | |
135 | (const required-fields) | |
136 | (const numerical-fields) | |
137 | (const page-dashes) | |
138 | (const inherit-booktitle) | |
139 | (const realign) | |
140 | (const last-comma) | |
141 | (const delimiters) | |
142 | (const unify-case)))) | |
50e4b39e | 143 | |
f754fb7b | 144 | (defcustom bibtex-clean-entry-hook nil |
02c8032e | 145 | "List of functions to call when entry has been cleaned. |
d0388eac | 146 | Functions are called with point inside the cleaned entry, and the buffer |
f754fb7b RS |
147 | narrowed to just the entry." |
148 | :group 'bibtex | |
ab2d0cdb | 149 | :type 'hook) |
cb4ad359 | 150 | |
f754fb7b | 151 | (defcustom bibtex-maintain-sorted-entries nil |
d10e87a2 | 152 | "If non-nil, BibTeX mode maintains all entries in sorted order. |
d715b065 | 153 | Allowed non-nil values are: |
7fbf4804 SM |
154 | plain All entries are sorted alphabetically. |
155 | crossref All entries are sorted alphabetically unless an entry has a | |
a9d77f1f | 156 | crossref field. These crossrefed entries are placed in |
7fbf4804 SM |
157 | alphabetical order immediately preceding the main entry. |
158 | entry-class The entries are divided into classes according to their | |
a9d77f1f | 159 | entry name, see `bibtex-sort-entry-class'. Within each class |
7fbf4804 SM |
160 | the entries are sorted alphabetically. |
161 | See also `bibtex-sort-ignore-string-entries'." | |
162 | :group 'bibtex | |
163 | :type '(choice (const nil) | |
164 | (const plain) | |
165 | (const crossref) | |
166 | (const entry-class))) | |
167 | ||
02c8032e | 168 | (defcustom bibtex-sort-entry-class |
7fbf4804 | 169 | '(("String") |
d715b065 KG |
170 | (catch-all) |
171 | ("Book" "Proceedings")) | |
d10e87a2 | 172 | "List of classes of BibTeX entry names, used for sorting entries. |
d715b065 | 173 | If value of `bibtex-maintain-sorted-entries' is `entry-class' |
a9d77f1f SM |
174 | entries are ordered according to the classes they belong to. Each |
175 | class contains a list of entry names. An entry `catch-all' applies | |
31df23f5 | 176 | to all entries not explicitly mentioned." |
02c8032e SM |
177 | :group 'BibTeX |
178 | :type '(repeat (choice :tag "Class" | |
179 | (const :tag "catch-all" (catch-all)) | |
180 | (repeat :tag "Entry name" string)))) | |
7fbf4804 | 181 | |
d715b065 | 182 | (defcustom bibtex-sort-ignore-string-entries t |
d10e87a2 | 183 | "If non-nil, BibTeX @String entries are not sort-significant. |
7fbf4804 SM |
184 | That means they are ignored when determining ordering of the buffer |
185 | \(e.g., sorting, locating alphabetical position for new entries, etc.)." | |
f754fb7b RS |
186 | :group 'bibtex |
187 | :type 'boolean) | |
9ae11a89 | 188 | |
f754fb7b | 189 | (defcustom bibtex-field-kill-ring-max 20 |
d10e87a2 | 190 | "Max length of `bibtex-field-kill-ring' before discarding oldest elements." |
f754fb7b RS |
191 | :group 'bibtex |
192 | :type 'integer) | |
50e4b39e | 193 | |
d715b065 | 194 | (defcustom bibtex-entry-kill-ring-max 20 |
d10e87a2 | 195 | "Max length of `bibtex-entry-kill-ring' before discarding oldest elements." |
d715b065 KG |
196 | :group 'bibtex |
197 | :type 'integer) | |
198 | ||
f754fb7b | 199 | (defcustom bibtex-parse-keys-timeout 60 |
d10e87a2 | 200 | "Time interval in seconds for parsing BibTeX buffers during idle time. |
02c8032e | 201 | Parsing initializes `bibtex-reference-keys' and `bibtex-strings'." |
f754fb7b RS |
202 | :group 'bibtex |
203 | :type 'integer) | |
af6fb89d | 204 | |
d715b065 | 205 | (defcustom bibtex-parse-keys-fast t |
d10e87a2 | 206 | "If non-nil, use fast but simplified algorithm for parsing BibTeX keys. |
d715b065 KG |
207 | If parsing fails, try to set this variable to nil." |
208 | :group 'bibtex | |
209 | :type 'boolean) | |
210 | ||
cb4ad359 | 211 | (defvar bibtex-entry-field-alist |
02c8032e | 212 | '(("Article" |
7fbf4804 | 213 | ((("author" "Author1 [and Author2 ...] [and others]") |
d715b065 KG |
214 | ("title" "Title of the article (BibTeX converts it to lowercase)") |
215 | ("journal" "Name of the journal (use string, remove braces)") | |
216 | ("year" "Year of publication")) | |
217 | (("volume" "Volume of the journal") | |
218 | ("number" "Number of the journal (only allowed if entry contains volume)") | |
219 | ("pages" "Pages in the journal") | |
220 | ("month" "Month of the publication as a string (remove braces)") | |
221 | ("note" "Remarks to be put at the end of the \\bibitem"))) | |
222 | ((("author" "Author1 [and Author2 ...] [and others]") | |
223 | ("title" "Title of the article (BibTeX converts it to lowercase)")) | |
224 | (("pages" "Pages in the journal") | |
225 | ("journal" "Name of the journal (use string, remove braces)") | |
226 | ("year" "Year of publication") | |
227 | ("volume" "Volume of the journal") | |
228 | ("number" "Number of the journal") | |
229 | ("month" "Month of the publication as a string (remove braces)") | |
230 | ("note" "Remarks to be put at the end of the \\bibitem")))) | |
7fbf4804 | 231 | ("Book" |
02c8032e SM |
232 | ((("author" "Author1 [and Author2 ...] [and others]" nil t) |
233 | ("editor" "Editor1 [and Editor2 ...] [and others]" nil t) | |
d715b065 KG |
234 | ("title" "Title of the book") |
235 | ("publisher" "Publishing company") | |
236 | ("year" "Year of publication")) | |
237 | (("volume" "Volume of the book in the series") | |
238 | ("number" "Number of the book in a small series (overwritten by volume)") | |
239 | ("series" "Series in which the book appeared") | |
240 | ("address" "Address of the publisher") | |
241 | ("edition" "Edition of the book as a capitalized English word") | |
242 | ("month" "Month of the publication as a string (remove braces)") | |
243 | ("note" "Remarks to be put at the end of the \\bibitem"))) | |
02c8032e SM |
244 | ((("author" "Author1 [and Author2 ...] [and others]" nil t) |
245 | ("editor" "Editor1 [and Editor2 ...] [and others]" nil t) | |
d715b065 KG |
246 | ("title" "Title of the book")) |
247 | (("publisher" "Publishing company") | |
248 | ("year" "Year of publication") | |
249 | ("volume" "Volume of the book in the series") | |
250 | ("number" "Number of the book in a small series (overwritten by volume)") | |
251 | ("series" "Series in which the book appeared") | |
252 | ("address" "Address of the publisher") | |
253 | ("edition" "Edition of the book as a capitalized English word") | |
254 | ("month" "Month of the publication as a string (remove braces)") | |
255 | ("note" "Remarks to be put at the end of the \\bibitem")))) | |
7fbf4804 SM |
256 | ("Booklet" |
257 | ((("title" "Title of the booklet (BibTeX converts it to lowercase)")) | |
d715b065 KG |
258 | (("author" "Author1 [and Author2 ...] [and others]") |
259 | ("howpublished" "The way in which the booklet was published") | |
260 | ("address" "Address of the publisher") | |
261 | ("month" "Month of the publication as a string (remove braces)") | |
262 | ("year" "Year of publication") | |
263 | ("note" "Remarks to be put at the end of the \\bibitem")))) | |
7fbf4804 | 264 | ("InBook" |
02c8032e SM |
265 | ((("author" "Author1 [and Author2 ...] [and others]" nil t) |
266 | ("editor" "Editor1 [and Editor2 ...] [and others]" nil t) | |
d715b065 KG |
267 | ("title" "Title of the book") |
268 | ("chapter" "Chapter in the book") | |
269 | ("publisher" "Publishing company") | |
270 | ("year" "Year of publication")) | |
271 | (("volume" "Volume of the book in the series") | |
272 | ("number" "Number of the book in a small series (overwritten by volume)") | |
273 | ("series" "Series in which the book appeared") | |
274 | ("type" "Word to use instead of \"chapter\"") | |
275 | ("address" "Address of the publisher") | |
276 | ("edition" "Edition of the book as a capitalized English word") | |
277 | ("month" "Month of the publication as a string (remove braces)") | |
278 | ("pages" "Pages in the book") | |
279 | ("note" "Remarks to be put at the end of the \\bibitem"))) | |
02c8032e SM |
280 | ((("author" "Author1 [and Author2 ...] [and others]" nil t) |
281 | ("editor" "Editor1 [and Editor2 ...] [and others]" nil t) | |
d715b065 KG |
282 | ("title" "Title of the book") |
283 | ("chapter" "Chapter in the book")) | |
284 | (("pages" "Pages in the book") | |
285 | ("publisher" "Publishing company") | |
286 | ("year" "Year of publication") | |
287 | ("volume" "Volume of the book in the series") | |
288 | ("number" "Number of the book in a small series (overwritten by volume)") | |
289 | ("series" "Series in which the book appeared") | |
290 | ("type" "Word to use instead of \"chapter\"") | |
291 | ("address" "Address of the publisher") | |
292 | ("edition" "Edition of the book as a capitalized English word") | |
293 | ("month" "Month of the publication as a string (remove braces)") | |
294 | ("note" "Remarks to be put at the end of the \\bibitem")))) | |
7fbf4804 SM |
295 | ("InCollection" |
296 | ((("author" "Author1 [and Author2 ...] [and others]") | |
d715b065 KG |
297 | ("title" "Title of the article in book (BibTeX converts it to lowercase)") |
298 | ("booktitle" "Name of the book") | |
299 | ("publisher" "Publishing company") | |
300 | ("year" "Year of publication")) | |
301 | (("editor" "Editor1 [and Editor2 ...] [and others]") | |
302 | ("volume" "Volume of the book in the series") | |
303 | ("number" "Number of the book in a small series (overwritten by volume)") | |
304 | ("series" "Series in which the book appeared") | |
305 | ("type" "Word to use instead of \"chapter\"") | |
306 | ("chapter" "Chapter in the book") | |
307 | ("pages" "Pages in the book") | |
308 | ("address" "Address of the publisher") | |
309 | ("edition" "Edition of the book as a capitalized English word") | |
310 | ("month" "Month of the publication as a string (remove braces)") | |
311 | ("note" "Remarks to be put at the end of the \\bibitem"))) | |
312 | ((("author" "Author1 [and Author2 ...] [and others]") | |
313 | ("title" "Title of the article in book (BibTeX converts it to lowercase)") | |
314 | ("booktitle" "Name of the book")) | |
315 | (("pages" "Pages in the book") | |
316 | ("publisher" "Publishing company") | |
317 | ("year" "Year of publication") | |
318 | ("editor" "Editor1 [and Editor2 ...] [and others]") | |
319 | ("volume" "Volume of the book in the series") | |
320 | ("number" "Number of the book in a small series (overwritten by volume)") | |
321 | ("series" "Series in which the book appeared") | |
322 | ("type" "Word to use instead of \"chapter\"") | |
323 | ("chapter" "Chapter in the book") | |
324 | ("address" "Address of the publisher") | |
325 | ("edition" "Edition of the book as a capitalized English word") | |
326 | ("month" "Month of the publication as a string (remove braces)") | |
327 | ("note" "Remarks to be put at the end of the \\bibitem")))) | |
7fbf4804 SM |
328 | ("InProceedings" |
329 | ((("author" "Author1 [and Author2 ...] [and others]") | |
d715b065 KG |
330 | ("title" "Title of the article in proceedings (BibTeX converts it to lowercase)") |
331 | ("booktitle" "Name of the conference proceedings") | |
332 | ("year" "Year of publication")) | |
333 | (("editor" "Editor1 [and Editor2 ...] [and others]") | |
334 | ("volume" "Volume of the conference proceedings in the series") | |
335 | ("number" "Number of the conference proceedings in a small series (overwritten by volume)") | |
336 | ("series" "Series in which the conference proceedings appeared") | |
337 | ("pages" "Pages in the conference proceedings") | |
338 | ("address" "Location of the Proceedings") | |
339 | ("month" "Month of the publication as a string (remove braces)") | |
340 | ("organization" "Sponsoring organization of the conference") | |
341 | ("publisher" "Publishing company, its location") | |
342 | ("note" "Remarks to be put at the end of the \\bibitem"))) | |
343 | ((("author" "Author1 [and Author2 ...] [and others]") | |
344 | ("title" "Title of the article in proceedings (BibTeX converts it to lowercase)")) | |
345 | (("booktitle" "Name of the conference proceedings") | |
346 | ("pages" "Pages in the conference proceedings") | |
347 | ("year" "Year of publication") | |
348 | ("editor" "Editor1 [and Editor2 ...] [and others]") | |
349 | ("volume" "Volume of the conference proceedings in the series") | |
350 | ("number" "Number of the conference proceedings in a small series (overwritten by volume)") | |
351 | ("series" "Series in which the conference proceedings appeared") | |
352 | ("address" "Location of the Proceedings") | |
353 | ("month" "Month of the publication as a string (remove braces)") | |
354 | ("organization" "Sponsoring organization of the conference") | |
355 | ("publisher" "Publishing company, its location") | |
356 | ("note" "Remarks to be put at the end of the \\bibitem")))) | |
7fbf4804 SM |
357 | ("Manual" |
358 | ((("title" "Title of the manual")) | |
d715b065 KG |
359 | (("author" "Author1 [and Author2 ...] [and others]") |
360 | ("organization" "Publishing organization of the manual") | |
361 | ("address" "Address of the organization") | |
362 | ("edition" "Edition of the manual as a capitalized English word") | |
363 | ("month" "Month of the publication as a string (remove braces)") | |
364 | ("year" "Year of publication") | |
365 | ("note" "Remarks to be put at the end of the \\bibitem")))) | |
7fbf4804 SM |
366 | ("MastersThesis" |
367 | ((("author" "Author1 [and Author2 ...] [and others]") | |
d715b065 KG |
368 | ("title" "Title of the master\'s thesis (BibTeX converts it to lowercase)") |
369 | ("school" "School where the master\'s thesis was written") | |
370 | ("year" "Year of publication")) | |
371 | (("type" "Type of the master\'s thesis (if other than \"Master\'s thesis\")") | |
372 | ("address" "Address of the school (if not part of field \"school\") or country") | |
373 | ("month" "Month of the publication as a string (remove braces)") | |
374 | ("note" "Remarks to be put at the end of the \\bibitem")))) | |
7fbf4804 SM |
375 | ("Misc" |
376 | (() | |
d715b065 KG |
377 | (("author" "Author1 [and Author2 ...] [and others]") |
378 | ("title" "Title of the work (BibTeX converts it to lowercase)") | |
379 | ("howpublished" "The way in which the work was published") | |
380 | ("month" "Month of the publication as a string (remove braces)") | |
381 | ("year" "Year of publication") | |
382 | ("note" "Remarks to be put at the end of the \\bibitem")))) | |
7fbf4804 SM |
383 | ("PhdThesis" |
384 | ((("author" "Author1 [and Author2 ...] [and others]") | |
d715b065 KG |
385 | ("title" "Title of the PhD. thesis") |
386 | ("school" "School where the PhD. thesis was written") | |
387 | ("year" "Year of publication")) | |
388 | (("type" "Type of the PhD. thesis") | |
389 | ("address" "Address of the school (if not part of field \"school\") or country") | |
390 | ("month" "Month of the publication as a string (remove braces)") | |
391 | ("note" "Remarks to be put at the end of the \\bibitem")))) | |
7fbf4804 SM |
392 | ("Proceedings" |
393 | ((("title" "Title of the conference proceedings") | |
d715b065 KG |
394 | ("year" "Year of publication")) |
395 | (("booktitle" "Title of the proceedings for cross references") | |
396 | ("editor" "Editor1 [and Editor2 ...] [and others]") | |
397 | ("volume" "Volume of the conference proceedings in the series") | |
398 | ("number" "Number of the conference proceedings in a small series (overwritten by volume)") | |
399 | ("series" "Series in which the conference proceedings appeared") | |
400 | ("address" "Location of the Proceedings") | |
401 | ("month" "Month of the publication as a string (remove braces)") | |
402 | ("organization" "Sponsoring organization of the conference") | |
403 | ("publisher" "Publishing company, its location") | |
404 | ("note" "Remarks to be put at the end of the \\bibitem")))) | |
7fbf4804 SM |
405 | ("TechReport" |
406 | ((("author" "Author1 [and Author2 ...] [and others]") | |
d715b065 KG |
407 | ("title" "Title of the technical report (BibTeX converts it to lowercase)") |
408 | ("institution" "Sponsoring institution of the report") | |
409 | ("year" "Year of publication")) | |
410 | (("type" "Type of the report (if other than \"technical report\")") | |
411 | ("number" "Number of the technical report") | |
412 | ("address" "Address of the institution (if not part of field \"institution\") or country") | |
413 | ("month" "Month of the publication as a string (remove braces)") | |
414 | ("note" "Remarks to be put at the end of the \\bibitem")))) | |
7fbf4804 SM |
415 | ("Unpublished" |
416 | ((("author" "Author1 [and Author2 ...] [and others]") | |
d715b065 KG |
417 | ("title" "Title of the unpublished work (BibTeX converts it to lowercase)") |
418 | ("note" "Remarks to be put at the end of the \\bibitem")) | |
419 | (("month" "Month of the publication as a string (remove braces)") | |
02c8032e SM |
420 | ("year" "Year of publication"))))) |
421 | ||
422 | "List of BibTeX entry types and their associated fields. | |
423 | List elements are triples | |
424 | \(ENTRY-NAME (REQUIRED OPTIONAL) (CROSSREF-REQUIRED CROSSREF-OPTIONAL)). | |
425 | ENTRY-NAME is the name of a BibTeX entry. The remaining pairs contain | |
426 | the required and optional fields of the BibTeX entry. | |
427 | The second pair is used if a crossref field is present | |
428 | and the first pair is used if a crossref field is absent. | |
429 | If the second pair is nil, the first pair is always used. | |
f0cb6034 | 430 | REQUIRED, OPTIONAL, CROSSREF-REQUIRED and CROSSREF-OPTIONAL are lists. |
50e4b39e | 431 | Each element of these lists is a list of the form |
73cc75b5 | 432 | \(FIELD-NAME COMMENT-STRING INIT ALTERNATIVE-FLAG). |
50e4b39e | 433 | COMMENT-STRING, INIT, and ALTERNATIVE-FLAG are optional. |
02c8032e SM |
434 | FIELD-NAME is the name of the field, COMMENT-STRING is the comment that |
435 | appears in the echo area, INIT is either the initial content of the | |
50e4b39e RS |
436 | field or a function, which is called to determine the initial content |
437 | of the field, and ALTERNATIVE-FLAG (either nil or t) marks if the | |
d0388eac | 438 | field is an alternative. ALTERNATIVE-FLAG may be t only in the |
50e4b39e RS |
439 | REQUIRED or CROSSREF-REQUIRED lists.") |
440 | ||
02c8032e SM |
441 | (defcustom bibtex-comment-start "@Comment" |
442 | "String starting a BibTeX comment." | |
443 | :group 'bibtex | |
444 | :type 'string) | |
ab2d0cdb | 445 | |
f754fb7b | 446 | (defcustom bibtex-add-entry-hook nil |
02c8032e | 447 | "List of functions to call when BibTeX entry has been inserted." |
f754fb7b | 448 | :group 'bibtex |
ab2d0cdb | 449 | :type 'hook) |
50e4b39e | 450 | |
f754fb7b | 451 | (defcustom bibtex-predefined-month-strings |
7fbf4804 SM |
452 | '(("jan" . "January") |
453 | ("feb" . "February") | |
454 | ("mar" . "March") | |
455 | ("apr" . "April") | |
456 | ("may" . "May") | |
457 | ("jun" . "June") | |
458 | ("jul" . "July") | |
459 | ("aug" . "August") | |
460 | ("sep" . "September") | |
461 | ("oct" . "October") | |
462 | ("nov" . "November") | |
463 | ("dec" . "December")) | |
464 | "Alist of month string definitions used in the BibTeX style files. | |
d715b065 | 465 | Each element is a pair of strings (ABBREVIATION . EXPANSION)." |
f754fb7b | 466 | :group 'bibtex |
7fbf4804 SM |
467 | :type '(repeat (cons (string :tag "Month abbreviation") |
468 | (string :tag "Month expansion")))) | |
50e4b39e | 469 | |
f754fb7b | 470 | (defcustom bibtex-predefined-strings |
50e4b39e RS |
471 | (append |
472 | bibtex-predefined-month-strings | |
7fbf4804 SM |
473 | '(("acmcs" . "ACM Computing Surveys") |
474 | ("acta" . "Acta Informatica") | |
475 | ("cacm" . "Communications of the ACM") | |
476 | ("ibmjrd" . "IBM Journal of Research and Development") | |
477 | ("ibmsj" . "IBM Systems Journal") | |
478 | ("ieeese" . "IEEE Transactions on Software Engineering") | |
479 | ("ieeetc" . "IEEE Transactions on Computers") | |
480 | ("ieeetcad" . "IEEE Transactions on Computer-Aided Design of Integrated Circuits") | |
481 | ("ipl" . "Information Processing Letters") | |
482 | ("jacm" . "Journal of the ACM") | |
483 | ("jcss" . "Journal of Computer and System Sciences") | |
484 | ("scp" . "Science of Computer Programming") | |
485 | ("sicomp" . "SIAM Journal on Computing") | |
486 | ("tcs" . "Theoretical Computer Science") | |
487 | ("tocs" . "ACM Transactions on Computer Systems") | |
488 | ("tods" . "ACM Transactions on Database Systems") | |
489 | ("tog" . "ACM Transactions on Graphics") | |
490 | ("toms" . "ACM Transactions on Mathematical Software") | |
491 | ("toois" . "ACM Transactions on Office Information Systems") | |
492 | ("toplas" . "ACM Transactions on Programming Languages and Systems"))) | |
493 | "Alist of string definitions used in the BibTeX style files. | |
d715b065 | 494 | Each element is a pair of strings (ABBREVIATION . EXPANSION)." |
f754fb7b | 495 | :group 'bibtex |
7fbf4804 SM |
496 | :type '(repeat (cons (string :tag "String") |
497 | (string :tag "String expansion")))) | |
cb4ad359 | 498 | |
f754fb7b | 499 | (defcustom bibtex-string-files nil |
d10e87a2 | 500 | "List of BibTeX files containing string definitions. |
02c8032e SM |
501 | List elements can be absolute file names or file names relative |
502 | to the directories specified in `bibtex-string-file-path'." | |
f754fb7b RS |
503 | :group 'bibtex |
504 | :type '(repeat file)) | |
50e4b39e RS |
505 | |
506 | (defvar bibtex-string-file-path (getenv "BIBINPUTS") | |
28f2ee66 | 507 | "*Colon separated list of paths to search for `bibtex-string-files'.") |
cb4ad359 | 508 | |
e0dc0c55 | 509 | (defcustom bibtex-files nil |
02c8032e | 510 | "List of BibTeX files that are searched for entry keys. |
e0dc0c55 SM |
511 | List elements can be absolute file names or file names relative to the |
512 | directories specified in `bibtex-file-path'. If an element is a directory, | |
513 | check all BibTeX files in this directory. If an element is the symbol | |
514 | `bibtex-file-path', check all BibTeX files in `bibtex-file-path'." | |
515 | :group 'bibtex | |
516 | :type '(repeat file)) | |
517 | ||
518 | (defvar bibtex-file-path (getenv "BIBINPUTS") | |
519 | "*Colon separated list of paths to search for `bibtex-files'.") | |
520 | ||
f754fb7b | 521 | (defcustom bibtex-help-message t |
d10e87a2 | 522 | "If non-nil print help messages in the echo area on entering a new field." |
f754fb7b RS |
523 | :group 'bibtex |
524 | :type 'boolean) | |
cb4ad359 | 525 | |
f754fb7b | 526 | (defcustom bibtex-autokey-prefix-string "" |
02c8032e | 527 | "String prefix for automatically generated reference keys. |
d715b065 | 528 | See `bibtex-generate-autokey' for details." |
f754fb7b RS |
529 | :group 'bibtex-autokey |
530 | :type 'string) | |
50e4b39e | 531 | |
f754fb7b | 532 | (defcustom bibtex-autokey-names 1 |
d10e87a2 | 533 | "Number of names to use for the automatically generated reference key. |
d0388eac | 534 | Possibly more names are used according to `bibtex-autokey-names-stretch'. |
d715b065 KG |
535 | If this variable is nil, all names are used. |
536 | See `bibtex-generate-autokey' for details." | |
f754fb7b | 537 | :group 'bibtex-autokey |
ab2d0cdb | 538 | :type '(choice (const :tag "All" infty) |
7fbf4804 | 539 | integer)) |
cb4ad359 | 540 | |
f754fb7b | 541 | (defcustom bibtex-autokey-names-stretch 0 |
02c8032e | 542 | "Number of names that can additionally be used for reference keys. |
50e4b39e | 543 | These names are used only, if all names are used then. |
d715b065 | 544 | See `bibtex-generate-autokey' for details." |
f754fb7b RS |
545 | :group 'bibtex-autokey |
546 | :type 'integer) | |
50e4b39e | 547 | |
f754fb7b | 548 | (defcustom bibtex-autokey-additional-names "" |
02c8032e | 549 | "String to append to the generated key if not all names could be used. |
d715b065 | 550 | See `bibtex-generate-autokey' for details." |
f754fb7b RS |
551 | :group 'bibtex-autokey |
552 | :type 'string) | |
50e4b39e | 553 | |
cdc61d35 SM |
554 | (defcustom bibtex-autokey-expand-strings nil |
555 | "If non-nil, expand strings when extracting the content of a BibTeX field. | |
556 | See `bibtex-generate-autokey' for details." | |
557 | :group 'bibtex-autokey | |
558 | :type 'boolean) | |
559 | ||
50e4b39e | 560 | (defvar bibtex-autokey-transcriptions |
7fbf4804 SM |
561 | '(;; language specific characters |
562 | ("\\\\aa" . "a") ; \aa -> a | |
563 | ("\\\\AA" . "A") ; \AA -> A | |
564 | ("\\\"a\\|\\\\\\\"a\\|\\\\ae" . "ae") ; "a,\"a,\ae -> ae | |
565 | ("\\\"A\\|\\\\\\\"A\\|\\\\AE" . "Ae") ; "A,\"A,\AE -> Ae | |
566 | ("\\\\i" . "i") ; \i -> i | |
567 | ("\\\\j" . "j") ; \j -> j | |
568 | ("\\\\l" . "l") ; \l -> l | |
569 | ("\\\\L" . "L") ; \L -> L | |
570 | ("\\\"o\\|\\\\\\\"o\\|\\\\o\\|\\\\oe" . "oe") ; "o,\"o,\o,\oe -> oe | |
571 | ("\\\"O\\|\\\\\\\"O\\|\\\\O\\|\\\\OE" . "Oe") ; "O,\"O,\O,\OE -> Oe | |
572 | ("\\\"s\\|\\\\\\\"s\\|\\\\3" . "ss") ; "s,\"s,\3 -> ss | |
573 | ("\\\"u\\|\\\\\\\"u" . "ue") ; "u,\"u -> ue | |
574 | ("\\\"U\\|\\\\\\\"U" . "Ue") ; "U,\"U -> Ue | |
50e4b39e | 575 | ;; accents |
7fbf4804 | 576 | ("\\\\`\\|\\\\'\\|\\\\\\^\\|\\\\~\\|\\\\=\\|\\\\\\.\\|\\\\u\\|\\\\v\\|\\\\H\\|\\\\t\\|\\\\c\\|\\\\d\\|\\\\b" . "") |
d715b065 KG |
577 | ;; braces, quotes, concatenation. |
578 | ("[`'\"{}#]" . "") | |
7fbf4804 | 579 | ;; spaces |
e0dc0c55 | 580 | ("\\\\?[ \t\n]+\\|~" . " ")) |
d715b065 | 581 | "Alist of (OLD-REGEXP . NEW-STRING) pairs. |
d0388eac RS |
582 | Used by the default values of `bibtex-autokey-name-change-strings' and |
583 | `bibtex-autokey-titleword-change-strings'. Defaults to translating some | |
584 | language specific characters to their ASCII transcriptions, and | |
50e4b39e RS |
585 | removing any character accents.") |
586 | ||
f754fb7b | 587 | (defcustom bibtex-autokey-name-change-strings |
50e4b39e | 588 | bibtex-autokey-transcriptions |
d715b065 | 589 | "Alist of (OLD-REGEXP . NEW-STRING) pairs. |
02c8032e | 590 | Any part of a name matching OLD-REGEXP is replaced by NEW-STRING. |
d0388eac | 591 | Case is significant in OLD-REGEXP. All regexps are tried in the |
7fbf4804 | 592 | order in which they appear in the list. |
d715b065 | 593 | See `bibtex-generate-autokey' for details." |
f754fb7b | 594 | :group 'bibtex-autokey |
7fbf4804 SM |
595 | :type '(repeat (cons (regexp :tag "Old") |
596 | (string :tag "New")))) | |
cb4ad359 | 597 | |
ab2d0cdb | 598 | (defcustom bibtex-autokey-name-case-convert 'downcase |
d10e87a2 | 599 | "Function called for each name to perform case conversion. |
d715b065 | 600 | See `bibtex-generate-autokey' for details." |
ab2d0cdb RS |
601 | :group 'bibtex-autokey |
602 | :type '(choice (const :tag "Preserve case" identity) | |
7fbf4804 SM |
603 | (const :tag "Downcase" downcase) |
604 | (const :tag "Capitalize" capitalize) | |
605 | (const :tag "Upcase" upcase) | |
606 | (function :tag "Conversion function"))) | |
ab2d0cdb | 607 | |
f754fb7b | 608 | (defcustom bibtex-autokey-name-length 'infty |
d10e87a2 | 609 | "Number of characters from name to incorporate into key. |
cb4ad359 | 610 | If this is set to anything but a number, all characters are used. |
d715b065 | 611 | See `bibtex-generate-autokey' for details." |
f754fb7b RS |
612 | :group 'bibtex-autokey |
613 | :type '(choice (const :tag "All" infty) | |
7fbf4804 | 614 | integer)) |
cb4ad359 | 615 | |
f754fb7b | 616 | (defcustom bibtex-autokey-name-separator "" |
d10e87a2 | 617 | "String that comes between any two names in the key. |
d715b065 | 618 | See `bibtex-generate-autokey' for details." |
f754fb7b RS |
619 | :group 'bibtex-autokey |
620 | :type 'string) | |
cb4ad359 | 621 | |
f754fb7b | 622 | (defcustom bibtex-autokey-year-length 2 |
d10e87a2 | 623 | "Number of rightmost digits from the year field to incorporate into key. |
d715b065 | 624 | See `bibtex-generate-autokey' for details." |
f754fb7b RS |
625 | :group 'bibtex-autokey |
626 | :type 'integer) | |
50e4b39e | 627 | |
7fbf4804 | 628 | (defcustom bibtex-autokey-use-crossref t |
d10e87a2 | 629 | "If non-nil use fields from crossreferenced entry if necessary. |
7fbf4804 SM |
630 | If this variable is non-nil and some field has no entry, but a |
631 | valid crossref entry, the field from the crossreferenced entry is used. | |
d715b065 | 632 | See `bibtex-generate-autokey' for details." |
f754fb7b RS |
633 | :group 'bibtex-autokey |
634 | :type 'boolean) | |
cb4ad359 | 635 | |
f754fb7b | 636 | (defcustom bibtex-autokey-titlewords 5 |
d10e87a2 | 637 | "Number of title words to use for the automatically generated reference key. |
cb4ad359 | 638 | If this is set to anything but a number, all title words are used. |
50e4b39e | 639 | Possibly more words from the title are used according to |
d0388eac | 640 | `bibtex-autokey-titlewords-stretch'. |
d715b065 | 641 | See `bibtex-generate-autokey' for details." |
f754fb7b RS |
642 | :group 'bibtex-autokey |
643 | :type '(choice (const :tag "All" infty) | |
7fbf4804 | 644 | integer)) |
cb4ad359 | 645 | |
02c8032e SM |
646 | (defcustom bibtex-autokey-title-terminators "[.!?:;]\\|--" |
647 | "Regexp defining the termination of the main part of the title. | |
648 | Case of the regexps is ignored. See `bibtex-generate-autokey' for details." | |
f754fb7b | 649 | :group 'bibtex-autokey |
02c8032e | 650 | :type 'regexp) |
cb4ad359 | 651 | |
f754fb7b | 652 | (defcustom bibtex-autokey-titlewords-stretch 2 |
d10e87a2 | 653 | "Number of words that can additionally be used from the title. |
cb4ad359 | 654 | These words are used only, if a sentence from the title can be ended then. |
d715b065 | 655 | See `bibtex-generate-autokey' for details." |
f754fb7b RS |
656 | :group 'bibtex-autokey |
657 | :type 'integer) | |
cb4ad359 | 658 | |
ab2d0cdb RS |
659 | (defcustom bibtex-autokey-titleword-ignore |
660 | '("A" "An" "On" "The" "Eine?" "Der" "Die" "Das" | |
a172852f | 661 | "[^[:upper:]].*" ".*[^[:upper:]0-9].*") |
d10e87a2 | 662 | "Determines words from the title that are not to be used in the key. |
31df23f5 | 663 | Each item of the list is a regexp. If a word of the title matches a |
ab2d0cdb | 664 | regexp from that list, it is not included in the title part of the key. |
d715b065 | 665 | See `bibtex-generate-autokey' for details." |
f754fb7b RS |
666 | :group 'bibtex-autokey |
667 | :type '(repeat regexp)) | |
cb4ad359 | 668 | |
ab2d0cdb | 669 | (defcustom bibtex-autokey-titleword-case-convert 'downcase |
d10e87a2 | 670 | "Function called for each titleword to perform case conversion. |
d715b065 | 671 | See `bibtex-generate-autokey' for details." |
ab2d0cdb RS |
672 | :group 'bibtex-autokey |
673 | :type '(choice (const :tag "Preserve case" identity) | |
7fbf4804 SM |
674 | (const :tag "Downcase" downcase) |
675 | (const :tag "Capitalize" capitalize) | |
676 | (const :tag "Upcase" upcase) | |
677 | (function :tag "Conversion function"))) | |
ab2d0cdb | 678 | |
f754fb7b | 679 | (defcustom bibtex-autokey-titleword-abbrevs nil |
d10e87a2 | 680 | "Determines exceptions to the usual abbreviation mechanism. |
d715b065 | 681 | An alist of (OLD-REGEXP . NEW-STRING) pairs. Case is ignored |
d0388eac | 682 | in matching against OLD-REGEXP, and the first matching pair is used. |
d715b065 | 683 | See `bibtex-generate-autokey' for details." |
7fbf4804 SM |
684 | :group 'bibtex-autokey |
685 | :type '(repeat (cons (regexp :tag "Old") | |
686 | (string :tag "New")))) | |
cb4ad359 | 687 | |
f754fb7b | 688 | (defcustom bibtex-autokey-titleword-change-strings |
50e4b39e | 689 | bibtex-autokey-transcriptions |
d715b065 | 690 | "Alist of (OLD-REGEXP . NEW-STRING) pairs. |
d0388eac RS |
691 | Any part of title word matching a OLD-REGEXP is replaced by NEW-STRING. |
692 | Case is significant in OLD-REGEXP. All regexps are tried in the | |
7fbf4804 | 693 | order in which they appear in the list. |
d715b065 | 694 | See `bibtex-generate-autokey' for details." |
f754fb7b | 695 | :group 'bibtex-autokey |
7fbf4804 SM |
696 | :type '(repeat (cons (regexp :tag "Old") |
697 | (string :tag "New")))) | |
cb4ad359 | 698 | |
f754fb7b | 699 | (defcustom bibtex-autokey-titleword-length 5 |
d10e87a2 | 700 | "Number of characters from title words to incorporate into key. |
cb4ad359 | 701 | If this is set to anything but a number, all characters are used. |
d715b065 | 702 | See `bibtex-generate-autokey' for details." |
f754fb7b RS |
703 | :group 'bibtex-autokey |
704 | :type '(choice (const :tag "All" infty) | |
7fbf4804 | 705 | integer)) |
cb4ad359 | 706 | |
f754fb7b | 707 | (defcustom bibtex-autokey-titleword-separator "_" |
d10e87a2 | 708 | "String to be put between the title words. |
d715b065 | 709 | See `bibtex-generate-autokey' for details." |
f754fb7b RS |
710 | :group 'bibtex-autokey |
711 | :type 'string) | |
cb4ad359 | 712 | |
f754fb7b | 713 | (defcustom bibtex-autokey-name-year-separator "" |
d10e87a2 | 714 | "String to be put between name part and year part of key. |
d715b065 | 715 | See `bibtex-generate-autokey' for details." |
f754fb7b RS |
716 | :group 'bibtex-autokey |
717 | :type 'string) | |
cb4ad359 | 718 | |
f754fb7b | 719 | (defcustom bibtex-autokey-year-title-separator ":_" |
d10e87a2 | 720 | "String to be put between name part and year part of key. |
d715b065 | 721 | See `bibtex-generate-autokey' for details." |
f754fb7b RS |
722 | :group 'bibtex-autokey |
723 | :type 'string) | |
50e4b39e | 724 | |
f754fb7b | 725 | (defcustom bibtex-autokey-edit-before-use t |
d10e87a2 | 726 | "If non-nil, user is allowed to edit the generated key before it is used." |
f754fb7b RS |
727 | :group 'bibtex-autokey |
728 | :type 'boolean) | |
cb4ad359 | 729 | |
ab2d0cdb | 730 | (defcustom bibtex-autokey-before-presentation-function nil |
02c8032e SM |
731 | "If non-nil, function to call before generated key is presented. |
732 | The function must take one argument (the automatically generated key), | |
733 | and must return a string (the key to use)." | |
f754fb7b | 734 | :group 'bibtex-autokey |
b4a64de4 | 735 | :type '(choice (const nil) function)) |
50e4b39e | 736 | |
f754fb7b | 737 | (defcustom bibtex-entry-offset 0 |
d10e87a2 | 738 | "Offset for BibTeX entries. |
31df23f5 | 739 | Added to the value of all other variables which determine columns." |
f754fb7b RS |
740 | :group 'bibtex |
741 | :type 'integer) | |
50e4b39e | 742 | |
f754fb7b | 743 | (defcustom bibtex-field-indentation 2 |
d10e87a2 | 744 | "Starting column for the name part in BibTeX fields." |
f754fb7b RS |
745 | :group 'bibtex |
746 | :type 'integer) | |
50e4b39e | 747 | |
f754fb7b | 748 | (defcustom bibtex-text-indentation |
7fbf4804 SM |
749 | (+ bibtex-field-indentation |
750 | (length "organization = ")) | |
d10e87a2 | 751 | "Starting column for the text part in BibTeX fields. |
f754fb7b RS |
752 | Should be equal to the space needed for the longest name part." |
753 | :group 'bibtex | |
754 | :type 'integer) | |
50e4b39e | 755 | |
f754fb7b | 756 | (defcustom bibtex-contline-indentation |
50e4b39e | 757 | (+ bibtex-text-indentation 1) |
d10e87a2 | 758 | "Starting column for continuation lines of BibTeX fields." |
f754fb7b RS |
759 | :group 'bibtex |
760 | :type 'integer) | |
50e4b39e | 761 | |
f754fb7b | 762 | (defcustom bibtex-align-at-equal-sign nil |
d10e87a2 | 763 | "If non-nil, align fields at equal sign instead of field text. |
7fbf4804 SM |
764 | If non-nil, the column for the equal sign is the value of |
765 | `bibtex-text-indentation', minus 2." | |
f754fb7b RS |
766 | :group 'bibtex |
767 | :type 'boolean) | |
768 | ||
769 | (defcustom bibtex-comma-after-last-field nil | |
d10e87a2 | 770 | "If non-nil, a comma is put at end of last field in the entry template." |
f754fb7b RS |
771 | :group 'bibtex |
772 | :type 'boolean) | |
50e4b39e | 773 | |
d715b065 KG |
774 | (defcustom bibtex-autoadd-commas t |
775 | "If non-nil automatically add missing commas at end of BibTeX fields." | |
e0dc0c55 | 776 | :group 'bibtex |
d715b065 KG |
777 | :type 'boolean) |
778 | ||
779 | (defcustom bibtex-autofill-types '("Proceedings") | |
780 | "Automatically fill fields if possible for those BibTeX entry types." | |
e0dc0c55 | 781 | :group 'bibtex |
d715b065 KG |
782 | :type '(repeat string)) |
783 | ||
e0dc0c55 | 784 | (defcustom bibtex-summary-function 'bibtex-summary |
921a9483 SM |
785 | "Function to call for generating a summary of current BibTeX entry. |
786 | It takes no arguments. Point must be at beginning of entry. | |
02c8032e | 787 | Used by `bibtex-complete-crossref-cleanup' and `bibtex-copy-summary-as-kill'." |
e0dc0c55 SM |
788 | :group 'bibtex |
789 | :type '(choice (const :tag "Default" bibtex-summary) | |
790 | (function :tag "Personalized function"))) | |
791 | ||
d528bff7 | 792 | (defcustom bibtex-generate-url-list |
63d516ce | 793 | '((("url" . ".*:.*"))) |
d528bff7 SM |
794 | "List of schemes for generating the URL of a BibTeX entry. |
795 | These schemes are used by `bibtex-url'. | |
796 | ||
63d516ce | 797 | Each scheme should have one of these forms: |
d528bff7 | 798 | |
63d516ce SM |
799 | ((FIELD . REGEXP)) |
800 | ((FIELD . REGEXP) STEP...) | |
801 | ((FIELD . REGEXP) STRING STEP...) | |
a9d77f1f | 802 | |
63d516ce SM |
803 | FIELD is a field name as returned by `bibtex-parse-entry'. |
804 | REGEXP is matched against the text of FIELD. If the match succeeds, | |
805 | then this scheme is used. If no STRING and STEPs are specified | |
806 | the matched text is used as the URL, otherwise the URL is built | |
807 | by evaluating STEPs. If no STRING is specified the STEPs must result | |
808 | in strings which are concatenated. Otherwise the resulting objects | |
809 | are passed through `format' using STRING as format control string. | |
810 | ||
811 | A STEP is a list (FIELD REGEXP REPLACE). The text of FIELD | |
812 | is matched against REGEXP, and is replaced with REPLACE. | |
813 | REPLACE can be a string, or a number (which selects the corresponding | |
814 | submatch), or a function called with the field's text as argument | |
815 | and with the `match-data' properly set. | |
816 | ||
817 | Case is always ignored. Always remove the field delimiters. | |
cdc61d35 SM |
818 | If `bibtex-expand-strings' is non-nil, BibTeX strings are expanded |
819 | for generating the URL. | |
63d516ce SM |
820 | |
821 | The following is a complex example, see http://link.aps.org/linkfaq.html. | |
822 | ||
823 | (((\"journal\" . \"\\\\=<\\(PR[ABCDEL]?\\|RMP\\)\\\\=>\") | |
824 | \"http://link.aps.org/abstract/%s/v%s/p%s\" | |
825 | (\"journal\" \".*\" downcase) | |
826 | (\"volume\" \".*\" 0) | |
827 | (\"pages\" \"\\`[A-Z]?[0-9]+\" 0)))" | |
d528bff7 SM |
828 | :group 'bibtex |
829 | :type '(repeat | |
63d516ce | 830 | (cons :tag "Scheme" |
d528bff7 SM |
831 | (cons :tag "Matcher" :extra-offset 4 |
832 | (string :tag "BibTeX field") | |
a9d77f1f | 833 | (regexp :tag "Regexp")) |
63d516ce SM |
834 | (choice |
835 | (const :tag "Take match as is" nil) | |
836 | (cons :tag "Formatted" | |
837 | (string :tag "Format control string") | |
838 | (repeat :tag "Steps to generate URL" | |
839 | (list (string :tag "BibTeX field") | |
840 | (regexp :tag "Regexp") | |
841 | (choice (string :tag "Replacement") | |
842 | (integer :tag "Sub-match") | |
843 | (function :tag "Filter"))))) | |
844 | (repeat :tag "Concatenated" | |
d528bff7 | 845 | (list (string :tag "BibTeX field") |
a9d77f1f SM |
846 | (regexp :tag "Regexp") |
847 | (choice (string :tag "Replacement") | |
848 | (integer :tag "Sub-match") | |
849 | (function :tag "Filter")))))))) | |
7fbf4804 | 850 | |
cdc61d35 SM |
851 | (defcustom bibtex-expand-strings nil |
852 | "If non-nil, expand strings when extracting the content of a BibTeX field." | |
853 | :group 'bibtex | |
854 | :type 'boolean) | |
855 | ||
ffc1e1db | 856 | ;; `bibtex-font-lock-keywords' is a user option, too. But since the |
31bc4210 | 857 | ;; patterns used to define this variable are defined in a later |
50e4b39e | 858 | ;; section of this file, it is defined later. |
28f2ee66 GM |
859 | |
860 | \f | |
cdc61d35 | 861 | ;; Syntax Table and Keybindings |
9ae11a89 ER |
862 | (defvar bibtex-mode-syntax-table |
863 | (let ((st (make-syntax-table))) | |
50e4b39e | 864 | (modify-syntax-entry ?\" "\"" st) |
9ae11a89 ER |
865 | (modify-syntax-entry ?$ "$$ " st) |
866 | (modify-syntax-entry ?% "< " st) | |
50e4b39e RS |
867 | (modify-syntax-entry ?' "w " st) |
868 | (modify-syntax-entry ?@ "w " st) | |
9ae11a89 ER |
869 | (modify-syntax-entry ?\\ "\\" st) |
870 | (modify-syntax-entry ?\f "> " st) | |
871 | (modify-syntax-entry ?\n "> " st) | |
7fbf4804 SM |
872 | ;; Keys cannot have = in them (wrong font-lock of @string{foo=bar}). |
873 | (modify-syntax-entry ?= "." st) | |
9ae11a89 | 874 | (modify-syntax-entry ?~ " " st) |
7fbf4804 SM |
875 | st) |
876 | "Syntax table used in BibTeX mode buffers.") | |
9ae11a89 | 877 | |
9ae11a89 ER |
878 | (defvar bibtex-mode-map |
879 | (let ((km (make-sparse-keymap))) | |
28f2ee66 | 880 | ;; The Key `C-c&' is reserved for reftex.el |
9ae11a89 ER |
881 | (define-key km "\t" 'bibtex-find-text) |
882 | (define-key km "\n" 'bibtex-next-field) | |
7fbf4804 | 883 | (define-key km "\M-\t" 'bibtex-complete) |
50e4b39e RS |
884 | (define-key km "\C-c\"" 'bibtex-remove-delimiters) |
885 | (define-key km "\C-c{" 'bibtex-remove-delimiters) | |
886 | (define-key km "\C-c}" 'bibtex-remove-delimiters) | |
9ae11a89 | 887 | (define-key km "\C-c\C-c" 'bibtex-clean-entry) |
50e4b39e | 888 | (define-key km "\C-c\C-q" 'bibtex-fill-entry) |
d528bff7 | 889 | (define-key km "\C-c\C-s" 'bibtex-find-entry) |
02c8032e | 890 | (define-key km "\C-c\C-x" 'bibtex-find-crossref) |
e0dc0c55 | 891 | (define-key km "\C-c\C-t" 'bibtex-copy-summary-as-kill) |
cb4ad359 | 892 | (define-key km "\C-c?" 'bibtex-print-help-message) |
9ae11a89 ER |
893 | (define-key km "\C-c\C-p" 'bibtex-pop-previous) |
894 | (define-key km "\C-c\C-n" 'bibtex-pop-next) | |
50e4b39e RS |
895 | (define-key km "\C-c\C-k" 'bibtex-kill-field) |
896 | (define-key km "\C-c\M-k" 'bibtex-copy-field-as-kill) | |
897 | (define-key km "\C-c\C-w" 'bibtex-kill-entry) | |
898 | (define-key km "\C-c\M-w" 'bibtex-copy-entry-as-kill) | |
899 | (define-key km "\C-c\C-y" 'bibtex-yank) | |
900 | (define-key km "\C-c\M-y" 'bibtex-yank-pop) | |
9ae11a89 | 901 | (define-key km "\C-c\C-d" 'bibtex-empty-field) |
50e4b39e | 902 | (define-key km "\C-c\C-f" 'bibtex-make-field) |
8bf38a9b | 903 | (define-key km "\C-c\C-u" 'bibtex-entry-update) |
50e4b39e RS |
904 | (define-key km "\C-c$" 'bibtex-ispell-abstract) |
905 | (define-key km "\M-\C-a" 'bibtex-beginning-of-entry) | |
906 | (define-key km "\M-\C-e" 'bibtex-end-of-entry) | |
907 | (define-key km "\C-\M-l" 'bibtex-reposition-window) | |
908 | (define-key km "\C-\M-h" 'bibtex-mark-entry) | |
909 | (define-key km "\C-c\C-b" 'bibtex-entry) | |
cb4ad359 RS |
910 | (define-key km "\C-c\C-rn" 'bibtex-narrow-to-entry) |
911 | (define-key km "\C-c\C-rw" 'widen) | |
d528bff7 | 912 | (define-key km "\C-c\C-l" 'bibtex-url) |
50e4b39e | 913 | (define-key km "\C-c\C-o" 'bibtex-remove-OPT-or-ALT) |
cb4ad359 | 914 | (define-key km "\C-c\C-e\C-i" 'bibtex-InProceedings) |
9ae11a89 | 915 | (define-key km "\C-c\C-ei" 'bibtex-InCollection) |
cb4ad359 RS |
916 | (define-key km "\C-c\C-eI" 'bibtex-InBook) |
917 | (define-key km "\C-c\C-e\C-a" 'bibtex-Article) | |
918 | (define-key km "\C-c\C-e\C-b" 'bibtex-InBook) | |
919 | (define-key km "\C-c\C-eb" 'bibtex-Book) | |
920 | (define-key km "\C-c\C-eB" 'bibtex-Booklet) | |
921 | (define-key km "\C-c\C-e\C-c" 'bibtex-InCollection) | |
9ae11a89 ER |
922 | (define-key km "\C-c\C-e\C-m" 'bibtex-Manual) |
923 | (define-key km "\C-c\C-em" 'bibtex-MastersThesis) | |
924 | (define-key km "\C-c\C-eM" 'bibtex-Misc) | |
cb4ad359 | 925 | (define-key km "\C-c\C-e\C-p" 'bibtex-InProceedings) |
9ae11a89 | 926 | (define-key km "\C-c\C-ep" 'bibtex-Proceedings) |
cb4ad359 | 927 | (define-key km "\C-c\C-eP" 'bibtex-PhdThesis) |
50e4b39e RS |
928 | (define-key km "\C-c\C-e\M-p" 'bibtex-Preamble) |
929 | (define-key km "\C-c\C-e\C-s" 'bibtex-String) | |
cb4ad359 | 930 | (define-key km "\C-c\C-e\C-t" 'bibtex-TechReport) |
9ae11a89 | 931 | (define-key km "\C-c\C-e\C-u" 'bibtex-Unpublished) |
7fbf4804 SM |
932 | km) |
933 | "Keymap used in BibTeX mode.") | |
9ae11a89 | 934 | |
50e4b39e | 935 | (easy-menu-define |
7fbf4804 SM |
936 | bibtex-edit-menu bibtex-mode-map "BibTeX-Edit Menu in BibTeX mode" |
937 | '("BibTeX-Edit" | |
938 | ("Moving inside an Entry" | |
939 | ["End of Field" bibtex-find-text t] | |
940 | ["Next Field" bibtex-next-field t] | |
941 | ["Beginning of Entry" bibtex-beginning-of-entry t] | |
d10e87a2 SM |
942 | ["End of Entry" bibtex-end-of-entry t] |
943 | "--" | |
944 | ["Make Entry Visible" bibtex-reposition-window t]) | |
02c8032e | 945 | ("Moving in BibTeX Buffers" |
d715b065 KG |
946 | ["Find Entry" bibtex-find-entry t] |
947 | ["Find Crossref Entry" bibtex-find-crossref t]) | |
e0dc0c55 | 948 | "--" |
7fbf4804 | 949 | ("Operating on Current Field" |
d715b065 | 950 | ["Fill Field" fill-paragraph t] |
7fbf4804 SM |
951 | ["Remove Delimiters" bibtex-remove-delimiters t] |
952 | ["Remove OPT or ALT Prefix" bibtex-remove-OPT-or-ALT t] | |
953 | ["Clear Field" bibtex-empty-field t] | |
954 | "--" | |
955 | ["Kill Field" bibtex-kill-field t] | |
956 | ["Copy Field to Kill Ring" bibtex-copy-field-as-kill t] | |
957 | ["Paste Most Recently Killed Field" bibtex-yank t] | |
958 | ["Paste Previously Killed Field" bibtex-yank-pop t] | |
959 | "--" | |
960 | ["Make New Field" bibtex-make-field t] | |
961 | "--" | |
962 | ["Snatch from Similar Following Field" bibtex-pop-next t] | |
963 | ["Snatch from Similar Preceding Field" bibtex-pop-previous t] | |
964 | "--" | |
965 | ["String or Key Complete" bibtex-complete t] | |
966 | "--" | |
967 | ["Help about Current Field" bibtex-print-help-message t]) | |
d528bff7 SM |
968 | ("Operating on Current Entry" |
969 | ["Fill Entry" bibtex-fill-entry t] | |
970 | ["Clean Entry" bibtex-clean-entry t] | |
971 | ["Update Entry" bibtex-entry-update t] | |
972 | "--" | |
973 | ["Kill Entry" bibtex-kill-entry t] | |
974 | ["Copy Entry to Kill Ring" bibtex-copy-entry-as-kill t] | |
975 | ["Paste Most Recently Killed Entry" bibtex-yank t] | |
976 | ["Paste Previously Killed Entry" bibtex-yank-pop t] | |
977 | "--" | |
e0dc0c55 | 978 | ["Copy Summary to Kill Ring" bibtex-copy-summary-as-kill t] |
02c8032e | 979 | ["Browse URL" bibtex-url t] |
e0dc0c55 | 980 | "--" |
d528bff7 SM |
981 | ["Ispell Entry" bibtex-ispell-entry t] |
982 | ["Ispell Entry Abstract" bibtex-ispell-abstract t] | |
02c8032e | 983 | "--" |
d528bff7 | 984 | ["Narrow to Entry" bibtex-narrow-to-entry t] |
02c8032e | 985 | ["Mark Entry" bibtex-mark-entry t] |
d528bff7 SM |
986 | "--" |
987 | ["View Cite Locations (RefTeX)" reftex-view-crossref-from-bibtex | |
988 | (fboundp 'reftex-view-crossref-from-bibtex)]) | |
7fbf4804 SM |
989 | ("Operating on Buffer or Region" |
990 | ["Validate Entries" bibtex-validate t] | |
991 | ["Sort Entries" bibtex-sort-buffer t] | |
992 | ["Reformat Entries" bibtex-reformat t] | |
d528bff7 SM |
993 | ["Count Entries" bibtex-count-entries t] |
994 | "--" | |
e0dc0c55 SM |
995 | ["Convert Alien Buffer" bibtex-convert-alien t]) |
996 | ("Operating on Multiple Buffers" | |
997 | ["Validate Entries" bibtex-validate-globally t]))) | |
50e4b39e RS |
998 | |
999 | (easy-menu-define | |
7fbf4804 SM |
1000 | bibtex-entry-menu bibtex-mode-map "Entry-Types Menu in BibTeX mode" |
1001 | (list "Entry-Types" | |
1002 | ["Article in Journal" bibtex-Article t] | |
1003 | ["Article in Conference Proceedings" bibtex-InProceedings t] | |
1004 | ["Article in a Collection" bibtex-InCollection t] | |
1005 | ["Chapter or Pages in a Book" bibtex-InBook t] | |
1006 | ["Conference Proceedings" bibtex-Proceedings t] | |
1007 | ["Book" bibtex-Book t] | |
1008 | ["Booklet (Bound, but no Publisher/Institution)" bibtex-Booklet t] | |
1009 | ["PhD. Thesis" bibtex-PhdThesis t] | |
1010 | ["Master's Thesis" bibtex-MastersThesis t] | |
1011 | ["Technical Report" bibtex-TechReport t] | |
1012 | ["Technical Manual" bibtex-Manual t] | |
1013 | ["Unpublished" bibtex-Unpublished t] | |
1014 | ["Miscellaneous" bibtex-Misc t] | |
02c8032e | 1015 | "--" |
7fbf4804 SM |
1016 | ["String" bibtex-String t] |
1017 | ["Preamble" bibtex-Preamble t])) | |
9ae11a89 | 1018 | |
31bc4210 | 1019 | \f |
5c69dbfc | 1020 | ;; Internal Variables |
9ae11a89 | 1021 | |
7fbf4804 SM |
1022 | (defvar bibtex-pop-previous-search-point nil |
1023 | "Next point where `bibtex-pop-previous' starts looking for a similar entry.") | |
1024 | ||
1025 | (defvar bibtex-pop-next-search-point nil | |
1026 | "Next point where `bibtex-pop-next' starts looking for a similar entry.") | |
1027 | ||
1028 | (defvar bibtex-field-kill-ring nil | |
1029 | "Ring of least recently killed fields. | |
1030 | At most `bibtex-field-kill-ring-max' items are kept here.") | |
9ae11a89 | 1031 | |
7fbf4804 SM |
1032 | (defvar bibtex-field-kill-ring-yank-pointer nil |
1033 | "The tail of `bibtex-field-kill-ring' whose car is the last item yanked.") | |
745bc783 | 1034 | |
7fbf4804 SM |
1035 | (defvar bibtex-entry-kill-ring nil |
1036 | "Ring of least recently killed entries. | |
1037 | At most `bibtex-entry-kill-ring-max' items are kept here.") | |
50e4b39e | 1038 | |
7fbf4804 SM |
1039 | (defvar bibtex-entry-kill-ring-yank-pointer nil |
1040 | "The tail of `bibtex-entry-kill-ring' whose car is the last item yanked.") | |
50e4b39e | 1041 | |
7fbf4804 SM |
1042 | (defvar bibtex-last-kill-command nil |
1043 | "Type of the last kill command (either 'field or 'entry).") | |
50e4b39e | 1044 | |
d715b065 KG |
1045 | (defvar bibtex-strings |
1046 | (lazy-completion-table bibtex-strings | |
2784fcc9 SM |
1047 | (lambda () |
1048 | (bibtex-parse-strings (bibtex-string-files-init)))) | |
d715b065 | 1049 | "Completion table for BibTeX string keys. |
7fbf4804 | 1050 | Initialized from `bibtex-predefined-strings' and `bibtex-string-files'.") |
d715b065 | 1051 | (make-variable-buffer-local 'bibtex-strings) |
964a8b47 | 1052 | (put 'bibtex-strings 'risky-local-variable t) |
50e4b39e | 1053 | |
d715b065 | 1054 | (defvar bibtex-reference-keys |
2784fcc9 SM |
1055 | (lazy-completion-table bibtex-reference-keys |
1056 | (lambda () (bibtex-parse-keys nil t))) | |
e0dc0c55 SM |
1057 | "Completion table for BibTeX reference keys. |
1058 | The CDRs of the elements are t for header keys and nil for crossref keys.") | |
d715b065 | 1059 | (make-variable-buffer-local 'bibtex-reference-keys) |
964a8b47 | 1060 | (put 'bibtex-reference-keys 'risky-local-variable t) |
50e4b39e | 1061 | |
7fbf4804 | 1062 | (defvar bibtex-buffer-last-parsed-tick nil |
a9d77f1f | 1063 | "Value of `buffer-modified-tick' last time buffer was parsed for keys.") |
745bc783 | 1064 | |
7fbf4804 SM |
1065 | (defvar bibtex-parse-idle-timer nil |
1066 | "Stores if timer is already installed.") | |
0640d7bf | 1067 | |
7fbf4804 SM |
1068 | (defvar bibtex-progress-lastperc nil |
1069 | "Last reported percentage for the progress message.") | |
50e4b39e | 1070 | |
7fbf4804 SM |
1071 | (defvar bibtex-progress-lastmes nil |
1072 | "Last reported progress message.") | |
50e4b39e | 1073 | |
7fbf4804 SM |
1074 | (defvar bibtex-progress-interval nil |
1075 | "Interval for progress messages.") | |
50e4b39e | 1076 | |
7fbf4804 SM |
1077 | (defvar bibtex-key-history nil |
1078 | "History list for reading keys.") | |
50e4b39e | 1079 | |
7fbf4804 | 1080 | (defvar bibtex-entry-type-history nil |
d715b065 | 1081 | "History list for reading entry types.") |
50e4b39e | 1082 | |
7fbf4804 SM |
1083 | (defvar bibtex-field-history nil |
1084 | "History list for reading field names.") | |
50e4b39e | 1085 | |
7fbf4804 SM |
1086 | (defvar bibtex-reformat-previous-options nil |
1087 | "Last reformat options given.") | |
50e4b39e | 1088 | |
7fbf4804 SM |
1089 | (defvar bibtex-reformat-previous-reference-keys nil |
1090 | "Last reformat reference keys option given.") | |
50e4b39e | 1091 | |
7fbf4804 | 1092 | (defconst bibtex-field-name "[^\"#%'(),={} \t\n0-9][^\"#%'(),={} \t\n]*" |
cdc61d35 | 1093 | "Regexp matching the name of a BibTeX field.") |
50e4b39e | 1094 | |
cdc61d35 | 1095 | (defconst bibtex-name-part |
ffc1e1db | 1096 | (concat ",[ \t\n]*\\(" bibtex-field-name "\\)") |
cdc61d35 | 1097 | "Regexp matching the name part of a BibTeX field.") |
7fbf4804 | 1098 | |
a172852f | 1099 | (defconst bibtex-reference-key "[][[:alnum:].:;?!`'/*@+|()<>&_^$-]+" |
7fbf4804 SM |
1100 | "Regexp matching the reference key part of a BibTeX entry.") |
1101 | ||
a172852f | 1102 | (defconst bibtex-field-const "[][[:alnum:].:;?!`'/*@+=|<>&_^$-]+" |
7fbf4804 SM |
1103 | "Regexp matching a BibTeX field constant.") |
1104 | ||
cdc61d35 SM |
1105 | (defvar bibtex-entry-type |
1106 | (concat "@[ \t]*\\(?:" | |
1107 | (regexp-opt (mapcar 'car bibtex-entry-field-alist)) "\\)") | |
1108 | "Regexp matching the name of a BibTeX entry.") | |
1109 | ||
cdc61d35 | 1110 | (defvar bibtex-entry-head |
7fbf4804 SM |
1111 | (concat "^[ \t]*\\(" |
1112 | bibtex-entry-type | |
1113 | "\\)[ \t]*[({][ \t\n]*\\(" | |
1114 | bibtex-reference-key | |
1115 | "\\)") | |
cdc61d35 | 1116 | "Regexp matching the header line of a BibTeX entry (including key).") |
7fbf4804 | 1117 | |
cdc61d35 | 1118 | (defvar bibtex-entry-maybe-empty-head |
7fbf4804 | 1119 | (concat bibtex-entry-head "?") |
d715b065 | 1120 | "Regexp matching the header line of a BibTeX entry (possibly without key).") |
7fbf4804 | 1121 | |
cdc61d35 SM |
1122 | (defconst bibtex-any-entry-maybe-empty-head |
1123 | (concat "^[ \t]*\\(@[ \t]*" bibtex-field-name "\\)[ \t]*[({][ \t\n]*\\(" | |
1124 | bibtex-reference-key "\\)?") | |
1125 | "Regexp matching the header line of any BibTeX entry (possibly without key).") | |
1126 | ||
ffc1e1db RW |
1127 | (defvar bibtex-any-valid-entry-type |
1128 | (concat "^[ \t]*@[ \t]*\\(?:" | |
1129 | (regexp-opt (append '("String" "Preamble") | |
1130 | (mapcar 'car bibtex-entry-field-alist))) "\\)") | |
1131 | "Regexp matching any valid BibTeX entry (including String and Preamble).") | |
1132 | ||
7fbf4804 SM |
1133 | (defconst bibtex-type-in-head 1 |
1134 | "Regexp subexpression number of the type part in `bibtex-entry-head'.") | |
1135 | ||
1136 | (defconst bibtex-key-in-head 2 | |
1137 | "Regexp subexpression number of the key part in `bibtex-entry-head'.") | |
1138 | ||
cdc61d35 SM |
1139 | (defconst bibtex-string-type "^[ \t]*\\(@[ \t]*String\\)[ \t]*[({][ \t\n]*" |
1140 | "Regexp matching the name of a BibTeX String entry.") | |
7fbf4804 | 1141 | |
cdc61d35 SM |
1142 | (defconst bibtex-string-maybe-empty-head |
1143 | (concat bibtex-string-type "\\(" bibtex-reference-key "\\)?") | |
1144 | "Regexp matching the header line of a BibTeX String entry.") | |
7fbf4804 | 1145 | |
ffc1e1db RW |
1146 | (defconst bibtex-preamble-prefix |
1147 | "[ \t]*\\(@[ \t]*Preamble\\)[ \t]*[({][ \t\n]*" | |
1148 | "Regexp matching the prefix part of a BibTeX Preamble entry.") | |
7fbf4804 | 1149 | |
7fbf4804 SM |
1150 | (defconst bibtex-font-lock-syntactic-keywords |
1151 | `((,(concat "^[ \t]*\\(" (substring bibtex-comment-start 0 1) "\\)" | |
1152 | (substring bibtex-comment-start 1) "\\>") | |
1153 | 1 '(11)))) | |
1154 | ||
1155 | (defvar bibtex-font-lock-keywords | |
d528bff7 | 1156 | ;; entry type and reference key |
cdc61d35 | 1157 | `((,bibtex-any-entry-maybe-empty-head |
d528bff7 SM |
1158 | (,bibtex-type-in-head font-lock-function-name-face) |
1159 | (,bibtex-key-in-head font-lock-constant-face nil t)) | |
1160 | ;; optional field names (treated as comments) | |
1161 | (,(concat "^[ \t]*\\(OPT" bibtex-field-name "\\)[ \t]*=") | |
1162 | 1 font-lock-comment-face) | |
1163 | ;; field names | |
1164 | (,(concat "^[ \t]*\\(" bibtex-field-name "\\)[ \t]*=") | |
1165 | 1 font-lock-variable-name-face) | |
1166 | ;; url | |
e0dc0c55 | 1167 | bibtex-font-lock-url bibtex-font-lock-crossref) |
7fbf4804 SM |
1168 | "*Default expressions to highlight in BibTeX mode.") |
1169 | ||
d528bff7 | 1170 | (defvar bibtex-font-lock-url-regexp |
e0dc0c55 | 1171 | ;; Assume that field names begin at the beginning of a line. |
d10e87a2 SM |
1172 | (concat "^[ \t]*" |
1173 | (regexp-opt (delete-dups (mapcar 'caar bibtex-generate-url-list)) t) | |
e0dc0c55 | 1174 | "[ \t]*=[ \t]*") |
d528bff7 SM |
1175 | "Regexp for `bibtex-font-lock-url'.") |
1176 | ||
cdc61d35 SM |
1177 | (defvar bibtex-string-empty-key nil |
1178 | "If non-nil, `bibtex-parse-string' accepts empty key.") | |
7fbf4804 SM |
1179 | |
1180 | (defvar bibtex-sort-entry-class-alist | |
1181 | (let ((i -1) alist) | |
1182 | (dolist (class bibtex-sort-entry-class alist) | |
1183 | (setq i (1+ i)) | |
1184 | (dolist (entry class) | |
d715b065 KG |
1185 | ;; all entry names should be downcase (for ease of comparison) |
1186 | (push (cons (if (stringp entry) (downcase entry) entry) i) alist)))) | |
a9d77f1f SM |
1187 | "Alist mapping entry types to their sorting index. |
1188 | Auto-generated from `bibtex-sort-entry-class'. | |
1189 | Used when `bibtex-maintain-sorted-entries' is `entry-class'.") | |
0640d7bf | 1190 | |
cb4ad359 | 1191 | \f |
7fbf4804 SM |
1192 | ;; Support for hideshow minor mode |
1193 | (defun bibtex-hs-forward-sexp (arg) | |
a9d77f1f SM |
1194 | "Replacement for `forward-sexp' to be used by `hs-minor-mode'. |
1195 | ARG is ignored." | |
1196 | (if (looking-at "@\\S(*\\s(") | |
e0dc0c55 | 1197 | (goto-char (1- (match-end 0)))) |
a9d77f1f | 1198 | (forward-sexp 1)) |
50e4b39e | 1199 | |
7fbf4804 SM |
1200 | (add-to-list |
1201 | 'hs-special-modes-alist | |
1202 | '(bibtex-mode "@\\S(*\\s(" "\\s)" nil bibtex-hs-forward-sexp nil)) | |
1203 | ||
1204 | \f | |
f9bd4abe | 1205 | (defun bibtex-parse-association (parse-lhs parse-rhs) |
7fbf4804 | 1206 | "Parse a string of the format <left-hand-side = right-hand-side>. |
f9bd4abe GM |
1207 | The functions PARSE-LHS and PARSE-RHS are used to parse the corresponding |
1208 | substrings. These functions are expected to return nil if parsing is not | |
cdc61d35 SM |
1209 | successful. If the returned values of both functions are non-nil, |
1210 | return a cons pair of these values. Do not move point." | |
f9bd4abe | 1211 | (save-match-data |
7fbf4804 | 1212 | (save-excursion |
d715b065 KG |
1213 | (let ((left (funcall parse-lhs)) |
1214 | right) | |
1215 | (if (and left | |
7fbf4804 SM |
1216 | (looking-at "[ \t\n]*=[ \t\n]*") |
1217 | (goto-char (match-end 0)) | |
1218 | (setq right (funcall parse-rhs))) | |
1219 | (cons left right)))))) | |
f9bd4abe GM |
1220 | |
1221 | (defun bibtex-parse-field-name () | |
cdc61d35 | 1222 | "Parse the name part of a BibTeX field. |
f9bd4abe GM |
1223 | If the field name is found, return a triple consisting of the position of the |
1224 | very first character of the match, the actual starting position of the name | |
a9d77f1f | 1225 | part and end position of the match. Move point to end of field name. |
31df23f5 | 1226 | If `bibtex-autoadd-commas' is non-nil add missing comma at end of preceding |
d715b065 | 1227 | BibTeX field as necessary." |
ffc1e1db RW |
1228 | (cond ((looking-at bibtex-name-part) |
1229 | (goto-char (match-end 0)) | |
1230 | (list (match-beginning 0) (match-beginning 1) (match-end 0))) | |
d715b065 KG |
1231 | ;; Maybe add a missing comma. |
1232 | ((and bibtex-autoadd-commas | |
cdc61d35 | 1233 | (looking-at (concat "[ \t\n]*\\(?:" bibtex-field-name |
d715b065 KG |
1234 | "\\)[ \t\n]*="))) |
1235 | (skip-chars-backward " \t\n") | |
cdc61d35 SM |
1236 | ;; It can be confusing if non-editing commands try to |
1237 | ;; modify the buffer. | |
1238 | (if buffer-read-only | |
1239 | (error "Comma missing at buffer position %s" (point))) | |
d715b065 KG |
1240 | (insert ",") |
1241 | (forward-char -1) | |
1242 | ;; Now try again. | |
1243 | (bibtex-parse-field-name)))) | |
d30bfc76 | 1244 | |
8bf38a9b SM |
1245 | (defconst bibtex-braced-string-syntax-table |
1246 | (let ((st (make-syntax-table))) | |
1247 | (modify-syntax-entry ?\{ "(}" st) | |
1248 | (modify-syntax-entry ?\} "){" st) | |
1249 | (modify-syntax-entry ?\[ "." st) | |
1250 | (modify-syntax-entry ?\] "." st) | |
1251 | (modify-syntax-entry ?\( "." st) | |
1252 | (modify-syntax-entry ?\) "." st) | |
1253 | (modify-syntax-entry ?\\ "." st) | |
1254 | (modify-syntax-entry ?\" "." st) | |
1255 | st) | |
1256 | "Syntax-table to parse matched braces.") | |
1257 | ||
1258 | (defconst bibtex-quoted-string-syntax-table | |
1259 | (let ((st (make-syntax-table))) | |
1260 | (modify-syntax-entry ?\\ "\\" st) | |
1261 | (modify-syntax-entry ?\" "\"" st) | |
1262 | st) | |
1263 | "Syntax-table to parse matched quotes.") | |
1264 | ||
1265 | (defun bibtex-parse-field-string () | |
02c8032e | 1266 | "Parse a BibTeX field string enclosed by braces or quotes. |
8bf38a9b | 1267 | If a syntactically correct string is found, a pair containing the start and |
cdc61d35 SM |
1268 | end position of the field string is returned, nil otherwise. |
1269 | Do not move point." | |
8bf38a9b SM |
1270 | (let ((end-point |
1271 | (or (and (eq (following-char) ?\") | |
1272 | (save-excursion | |
1273 | (with-syntax-table bibtex-quoted-string-syntax-table | |
1274 | (forward-sexp 1)) | |
1275 | (point))) | |
1276 | (and (eq (following-char) ?\{) | |
1277 | (save-excursion | |
1278 | (with-syntax-table bibtex-braced-string-syntax-table | |
1279 | (forward-sexp 1)) | |
1280 | (point)))))) | |
1281 | (if end-point | |
1282 | (cons (point) end-point)))) | |
1283 | ||
f9bd4abe | 1284 | (defun bibtex-parse-field-text () |
7fbf4804 | 1285 | "Parse the text part of a BibTeX field. |
f9bd4abe GM |
1286 | The text part is either a string, or an empty string, or a constant followed |
1287 | by one or more <# (string|constant)> pairs. If a syntactically correct text | |
1288 | is found, a pair containing the start and end position of the text is | |
a9d77f1f | 1289 | returned, nil otherwise. Move point to end of field text." |
f9bd4abe | 1290 | (let ((starting-point (point)) |
7fbf4804 | 1291 | end-point failure boundaries) |
d715b065 | 1292 | (while (not (or end-point failure)) |
7fbf4804 SM |
1293 | (cond ((looking-at bibtex-field-const) |
1294 | (goto-char (match-end 0))) | |
1295 | ((setq boundaries (bibtex-parse-field-string)) | |
1296 | (goto-char (cdr boundaries))) | |
1297 | ((setq failure t))) | |
d528bff7 SM |
1298 | (if (looking-at "[ \t\n]*#[ \t\n]*") |
1299 | (goto-char (match-end 0)) | |
1300 | (setq end-point (point)))) | |
cdc61d35 | 1301 | (skip-chars-forward " \t\n") |
7fbf4804 SM |
1302 | (if (and (not failure) |
1303 | end-point) | |
cdc61d35 SM |
1304 | (list starting-point end-point (point))))) |
1305 | ||
1306 | (defun bibtex-parse-field () | |
1307 | "Parse the BibTeX field beginning at the position of point. | |
1308 | If a syntactically correct field is found, return a cons pair containing | |
1309 | the boundaries of the name and text parts of the field. Do not move point." | |
1310 | (bibtex-parse-association 'bibtex-parse-field-name | |
1311 | 'bibtex-parse-field-text)) | |
f9bd4abe | 1312 | |
cdc61d35 SM |
1313 | (defsubst bibtex-start-of-field (bounds) |
1314 | (nth 0 (car bounds))) | |
1315 | (defsubst bibtex-start-of-name-in-field (bounds) | |
1316 | (nth 1 (car bounds))) | |
1317 | (defsubst bibtex-end-of-name-in-field (bounds) | |
1318 | (nth 2 (car bounds))) | |
1319 | (defsubst bibtex-start-of-text-in-field (bounds) | |
1320 | (nth 1 bounds)) | |
1321 | (defsubst bibtex-end-of-text-in-field (bounds) | |
1322 | (nth 2 bounds)) | |
1323 | (defsubst bibtex-end-of-field (bounds) | |
1324 | (nth 3 bounds)) | |
f9bd4abe | 1325 | |
7fbf4804 | 1326 | (defun bibtex-search-forward-field (name &optional bound) |
02c8032e | 1327 | "Search forward to find a BibTeX field of name NAME. |
cdc61d35 SM |
1328 | If a syntactically correct field is found, return a pair containing |
1329 | the boundaries of the name and text parts of the field. The search | |
ffc1e1db RW |
1330 | is limited by optional arg BOUND. If BOUND is t the search is limited |
1331 | by the end of the current entry. Do not move point." | |
f9bd4abe | 1332 | (save-match-data |
7fbf4804 | 1333 | (save-excursion |
ffc1e1db RW |
1334 | (if (eq bound t) |
1335 | (let ((regexp (concat bibtex-name-part "[ \t\n]*=\\|" | |
1336 | bibtex-any-entry-maybe-empty-head)) | |
1337 | (case-fold-search t) bounds) | |
1338 | (catch 'done | |
1339 | (if (looking-at "[ \t]*@") (goto-char (match-end 0))) | |
1340 | (while (and (not bounds) | |
1341 | (re-search-forward regexp nil t)) | |
1342 | (if (match-beginning 2) | |
1343 | ;; We found a new entry | |
1344 | (throw 'done nil) | |
1345 | ;; We found a field | |
1346 | (goto-char (match-beginning 0)) | |
1347 | (setq bounds (bibtex-parse-field)))) | |
1348 | ;; Step through all fields so that we cannot overshoot. | |
1349 | (while bounds | |
1350 | (goto-char (bibtex-start-of-name-in-field bounds)) | |
1351 | (if (looking-at name) (throw 'done bounds)) | |
1352 | (goto-char (bibtex-end-of-field bounds)) | |
1353 | (setq bounds (bibtex-parse-field))))) | |
1354 | ;; Bounded search or bound is nil (i.e. we cannot overshoot). | |
1355 | ;; Indeed, the search is bounded when `bibtex-search-forward-field' | |
1356 | ;; is called many times. So we optimize this part of this function. | |
1357 | (let ((name-part (concat ",[ \t\n]*\\(" name "\\)[ \t\n]*=[ \t\n]*")) | |
1358 | (case-fold-search t) left right) | |
1359 | (while (and (not right) | |
1360 | (re-search-forward name-part bound t)) | |
1361 | (setq left (list (match-beginning 0) (match-beginning 1) | |
1362 | (match-end 1)) | |
1363 | ;; Don't worry that the field text could be past bound. | |
1364 | right (bibtex-parse-field-text))) | |
1365 | (if right (cons left right))))))) | |
7fbf4804 SM |
1366 | |
1367 | (defun bibtex-search-backward-field (name &optional bound) | |
02c8032e | 1368 | "Search backward to find a BibTeX field of name NAME. |
cdc61d35 SM |
1369 | If a syntactically correct field is found, return a pair containing |
1370 | the boundaries of the name and text parts of the field. The search | |
ffc1e1db | 1371 | is limited by the optional arg BOUND. If BOUND is t the search is |
cdc61d35 | 1372 | limited by the beginning of the current entry. Do not move point." |
f9bd4abe | 1373 | (save-match-data |
ffc1e1db RW |
1374 | (if (eq bound t) |
1375 | (setq bound (save-excursion (bibtex-beginning-of-entry)))) | |
1376 | (let ((name-part (concat ",[ \t\n]*\\(" name "\\)[ \t\n]*=[ \t\n]*")) | |
1377 | (case-fold-search t) left right) | |
1378 | (save-excursion | |
1379 | ;; the parsing functions are not designed for parsing backwards :-( | |
1380 | (when (search-backward "," bound t) | |
1381 | (or (save-excursion | |
1382 | (when (looking-at name-part) | |
1383 | (setq left (list (match-beginning 0) (match-beginning 1) | |
1384 | (match-end 1))) | |
1385 | (goto-char (match-end 0)) | |
1386 | (setq right (bibtex-parse-field-text)))) | |
1387 | (while (and (not right) | |
1388 | (re-search-backward name-part bound t)) | |
1389 | (setq left (list (match-beginning 0) (match-beginning 1) | |
1390 | (match-end 1))) | |
1391 | (save-excursion | |
1392 | (goto-char (match-end 0)) | |
1393 | (setq right (bibtex-parse-field-text))))) | |
1394 | (if right (cons left right))))))) | |
7fbf4804 | 1395 | |
d528bff7 SM |
1396 | (defun bibtex-name-in-field (bounds &optional remove-opt-alt) |
1397 | "Get content of name in BibTeX field defined via BOUNDS. | |
1398 | If optional arg REMOVE-OPT-ALT is non-nil remove \"OPT\" and \"ALT\"." | |
cdc61d35 SM |
1399 | (let ((name (buffer-substring-no-properties |
1400 | (bibtex-start-of-name-in-field bounds) | |
1401 | (bibtex-end-of-name-in-field bounds)))) | |
d528bff7 SM |
1402 | (if (and remove-opt-alt |
1403 | (string-match "\\`\\(OPT\\|ALT\\)" name)) | |
1404 | (substring name 3) | |
1405 | name))) | |
7fbf4804 | 1406 | |
cdc61d35 SM |
1407 | (defun bibtex-text-in-field-bounds (bounds &optional content) |
1408 | "Get text in BibTeX field defined via BOUNDS. | |
1409 | If optional arg CONTENT is non-nil extract content of field | |
1410 | by removing field delimiters and concatenating the resulting string. | |
1411 | If `bibtex-expand-strings' is non-nil, also expand BibTeX strings." | |
1412 | (if content | |
1413 | (save-excursion | |
ffc1e1db | 1414 | (goto-char (bibtex-start-of-text-in-field bounds)) |
cdc61d35 | 1415 | (let ((epoint (bibtex-end-of-text-in-field bounds)) |
ffc1e1db | 1416 | content opoint) |
cdc61d35 | 1417 | (while (< (setq opoint (point)) epoint) |
ffc1e1db RW |
1418 | (if (looking-at bibtex-field-const) |
1419 | (let ((mtch (match-string-no-properties 0))) | |
e8606202 RW |
1420 | (push (or (if bibtex-expand-strings |
1421 | (cdr (assoc-string mtch (bibtex-strings) t))) | |
1422 | mtch) content) | |
ffc1e1db RW |
1423 | (goto-char (match-end 0))) |
1424 | (let ((bounds (bibtex-parse-field-string))) | |
1425 | (push (buffer-substring-no-properties | |
1426 | (1+ (car bounds)) (1- (cdr bounds))) content) | |
1427 | (goto-char (cdr bounds)))) | |
cdc61d35 | 1428 | (re-search-forward "\\=[ \t\n]*#[ \t\n]*" nil t)) |
ffc1e1db | 1429 | (apply 'concat (nreverse content)))) |
cdc61d35 SM |
1430 | (buffer-substring-no-properties (bibtex-start-of-text-in-field bounds) |
1431 | (bibtex-end-of-text-in-field bounds)))) | |
7fbf4804 SM |
1432 | |
1433 | (defun bibtex-text-in-field (field &optional follow-crossref) | |
02c8032e SM |
1434 | "Get content of field FIELD of current BibTeX entry. |
1435 | Return nil if not found. | |
7fbf4804 SM |
1436 | If optional arg FOLLOW-CROSSREF is non-nil, follow crossref." |
1437 | (save-excursion | |
ffc1e1db RW |
1438 | (let* ((end (if follow-crossref (bibtex-end-of-entry) t)) |
1439 | (beg (bibtex-beginning-of-entry)) ; move point | |
1440 | (bounds (bibtex-search-forward-field field end))) | |
1441 | (cond (bounds (bibtex-text-in-field-bounds bounds t)) | |
1442 | ((and follow-crossref | |
1443 | (progn (goto-char beg) | |
1444 | (setq bounds (bibtex-search-forward-field | |
1445 | "\\(OPT\\)?crossref" end)))) | |
1446 | (let ((crossref-field (bibtex-text-in-field-bounds bounds t))) | |
7fbf4804 SM |
1447 | (if (bibtex-find-crossref crossref-field) |
1448 | ;; Do not pass FOLLOW-CROSSREF because we want | |
1449 | ;; to follow crossrefs only one level of recursion. | |
1450 | (bibtex-text-in-field field)))))))) | |
f9bd4abe GM |
1451 | |
1452 | (defun bibtex-parse-string-prefix () | |
7fbf4804 | 1453 | "Parse the prefix part of a BibTeX string entry, including reference key. |
f9bd4abe GM |
1454 | If the string prefix is found, return a triple consisting of the position of |
1455 | the very first character of the match, the actual starting position of the | |
cdc61d35 SM |
1456 | reference key and the end position of the match. |
1457 | If `bibtex-string-empty-key' is non-nil accept empty string key." | |
7fbf4804 | 1458 | (let ((case-fold-search t)) |
cdc61d35 | 1459 | (if (looking-at bibtex-string-type) |
7fbf4804 SM |
1460 | (let ((start (point))) |
1461 | (goto-char (match-end 0)) | |
cdc61d35 SM |
1462 | (cond ((looking-at bibtex-reference-key) |
1463 | (goto-char (match-end 0)) | |
1464 | (list start | |
1465 | (match-beginning 0) | |
1466 | (match-end 0))) | |
1467 | ((and bibtex-string-empty-key | |
1468 | (looking-at "=")) | |
1469 | (skip-chars-backward " \t\n") | |
1470 | (list start (point) (point)))))))) | |
f9bd4abe GM |
1471 | |
1472 | (defun bibtex-parse-string-postfix () | |
7fbf4804 | 1473 | "Parse the postfix part of a BibTeX string entry, including the text. |
f9bd4abe GM |
1474 | If the string postfix is found, return a triple consisting of the position of |
1475 | the actual starting and ending position of the text and the very last | |
a9d77f1f | 1476 | character of the string entry. Move point past BibTeX string entry." |
f9bd4abe | 1477 | (let* ((case-fold-search t) |
d715b065 KG |
1478 | (bounds (bibtex-parse-field-text))) |
1479 | (when bounds | |
cdc61d35 | 1480 | (goto-char (nth 1 bounds)) |
7fbf4804 SM |
1481 | (when (looking-at "[ \t\n]*[})]") |
1482 | (goto-char (match-end 0)) | |
d715b065 | 1483 | (list (car bounds) |
cdc61d35 | 1484 | (nth 1 bounds) |
7fbf4804 | 1485 | (match-end 0)))))) |
f9bd4abe | 1486 | |
ffc1e1db | 1487 | (defun bibtex-parse-string (&optional empty-key) |
cdc61d35 SM |
1488 | "Parse a BibTeX string entry beginning at the position of point. |
1489 | If a syntactically correct entry is found, return a cons pair containing | |
1490 | the boundaries of the reference key and text parts of the entry. | |
ffc1e1db RW |
1491 | If EMPTY-KEY is non-nil, key may be empty. Do not move point." |
1492 | (let ((bibtex-string-empty-key empty-key)) | |
1493 | (bibtex-parse-association 'bibtex-parse-string-prefix | |
1494 | 'bibtex-parse-string-postfix))) | |
f9bd4abe | 1495 | |
ffc1e1db | 1496 | (defun bibtex-search-forward-string (&optional empty-key) |
7fbf4804 | 1497 | "Search forward to find a BibTeX string entry. |
f9bd4abe | 1498 | If a syntactically correct entry is found, a pair containing the boundaries of |
ffc1e1db RW |
1499 | the reference key and text parts of the string is returned. |
1500 | If EMPTY-KEY is non-nil, key may be empty. Do not move point." | |
7fbf4804 SM |
1501 | (save-excursion |
1502 | (save-match-data | |
ffc1e1db RW |
1503 | (let ((case-fold-search t) bounds) |
1504 | (while (and (not bounds) | |
cdc61d35 | 1505 | (search-forward-regexp bibtex-string-type nil t)) |
ffc1e1db RW |
1506 | (save-excursion (goto-char (match-beginning 0)) |
1507 | (setq bounds (bibtex-parse-string empty-key)))) | |
1508 | bounds)))) | |
7fbf4804 SM |
1509 | |
1510 | (defun bibtex-reference-key-in-string (bounds) | |
cdc61d35 | 1511 | "Return the key part of a BibTeX string defined via BOUNDS" |
7fbf4804 SM |
1512 | (buffer-substring-no-properties (nth 1 (car bounds)) |
1513 | (nth 2 (car bounds)))) | |
1514 | ||
cdc61d35 SM |
1515 | (defun bibtex-text-in-string (bounds &optional content) |
1516 | "Get text in BibTeX string field defined via BOUNDS. | |
1517 | If optional arg CONTENT is non-nil extract content | |
1518 | by removing field delimiters and concatenating the resulting string. | |
1519 | If `bibtex-expand-strings' is non-nil, also expand BibTeX strings." | |
1520 | (bibtex-text-in-field-bounds bounds content)) | |
f9bd4abe | 1521 | |
d715b065 | 1522 | (defsubst bibtex-start-of-text-in-string (bounds) |
7fbf4804 | 1523 | (nth 0 (cdr bounds))) |
d715b065 | 1524 | (defsubst bibtex-end-of-text-in-string (bounds) |
7fbf4804 | 1525 | (nth 1 (cdr bounds))) |
d715b065 | 1526 | (defsubst bibtex-end-of-string (bounds) |
7fbf4804 | 1527 | (nth 2 (cdr bounds))) |
745bc783 | 1528 | |
d715b065 | 1529 | (defsubst bibtex-type-in-head () |
7fbf4804 SM |
1530 | "Extract BibTeX type in head." |
1531 | ;; ignore @ | |
1532 | (buffer-substring-no-properties (1+ (match-beginning bibtex-type-in-head)) | |
1533 | (match-end bibtex-type-in-head))) | |
31bc4210 | 1534 | |
e0dc0c55 | 1535 | (defsubst bibtex-key-in-head (&optional empty) |
a9d77f1f | 1536 | "Extract BibTeX key in head. Return optional arg EMPTY if key is empty." |
e0dc0c55 SM |
1537 | (or (match-string-no-properties bibtex-key-in-head) |
1538 | empty)) | |
f9bd4abe | 1539 | |
ffc1e1db RW |
1540 | (defun bibtex-parse-preamble () |
1541 | "Parse BibTeX preamble. | |
1542 | Point must be at beginning of preamble. Do not move point." | |
cdc61d35 | 1543 | (let ((case-fold-search t)) |
ffc1e1db RW |
1544 | (when (looking-at bibtex-preamble-prefix) |
1545 | (let ((start (match-beginning 0)) (pref-start (match-beginning 1)) | |
1546 | (bounds (save-excursion (goto-char (match-end 0)) | |
1547 | (bibtex-parse-string-postfix)))) | |
1548 | (if bounds (cons (list start pref-start) bounds)))))) | |
e5167999 | 1549 | |
cdc61d35 | 1550 | ;; Helper Functions |
d10e87a2 | 1551 | |
d528bff7 | 1552 | (defsubst bibtex-string= (str1 str2) |
a9d77f1f | 1553 | "Return t if STR1 and STR2 are equal, ignoring case." |
d528bff7 SM |
1554 | (eq t (compare-strings str1 0 nil str2 0 nil t))) |
1555 | ||
55fe21fc | 1556 | (defun bibtex-delete-whitespace () |
7fbf4804 | 1557 | "Delete all whitespace starting at point." |
50e4b39e RS |
1558 | (if (looking-at "[ \t\n]+") |
1559 | (delete-region (point) (match-end 0)))) | |
1560 | ||
55fe21fc | 1561 | (defun bibtex-current-line () |
7fbf4804 | 1562 | "Compute line number of point regardless whether the buffer is narrowed." |
50e4b39e | 1563 | (+ (count-lines 1 (point)) |
e0dc0c55 | 1564 | (if (bolp) 1 0))) |
50e4b39e | 1565 | |
ffc1e1db RW |
1566 | (defun bibtex-valid-entry (&optional empty-key) |
1567 | "Parse a valid BibTeX entry (maybe without key if EMPTY-KEY is t). | |
1568 | A valid entry is a syntactical correct one with type contained in | |
1569 | `bibtex-entry-field-alist'. Ignore @String and @Preamble entries. | |
1570 | Return a cons pair with buffer positions of beginning and end of entry | |
1571 | if a valid entry is found, nil otherwise. Do not move point. | |
1572 | After a call to this function `match-data' corresponds to the header | |
1573 | of the entry, see regexp `bibtex-entry-head'." | |
1574 | (let ((case-fold-search t) end) | |
1575 | (if (looking-at (if empty-key bibtex-entry-maybe-empty-head | |
1576 | bibtex-entry-head)) | |
1577 | (save-excursion | |
1578 | (save-match-data | |
1579 | (goto-char (match-end 0)) | |
1580 | (let ((entry-closer | |
1581 | (if (save-excursion | |
1582 | (goto-char (match-end bibtex-type-in-head)) | |
1583 | (looking-at "[ \t]*(")) | |
1584 | ",?[ \t\n]*)" ;; entry opened with `(' | |
1585 | ",?[ \t\n]*}")) ;; entry opened with `{' | |
1586 | bounds) | |
1587 | (skip-chars-forward " \t\n") | |
1588 | ;; loop over all BibTeX fields | |
1589 | (while (setq bounds (bibtex-parse-field)) | |
1590 | (goto-char (bibtex-end-of-field bounds))) | |
1591 | ;; This matches the infix* part. | |
1592 | (if (looking-at entry-closer) (setq end (match-end 0))))) | |
1593 | (if end (cons (match-beginning 0) end)))))) | |
1594 | ||
55fe21fc | 1595 | (defun bibtex-skip-to-valid-entry (&optional backward) |
a9d77f1f SM |
1596 | "Move point to beginning of the next valid BibTeX entry. |
1597 | Do not move if we are already at beginning of a valid BibTeX entry. | |
1598 | With optional argument BACKWARD non-nil, move backward to | |
1599 | beginning of previous valid one. A valid entry is a syntactical correct one | |
7fbf4804 SM |
1600 | with type contained in `bibtex-entry-field-alist' or, if |
1601 | `bibtex-sort-ignore-string-entries' is nil, a syntactical correct string | |
cdc61d35 | 1602 | entry. Return buffer position of beginning and end of entry if a valid |
d715b065 KG |
1603 | entry is found, nil otherwise." |
1604 | (interactive "P") | |
7fbf4804 | 1605 | (let ((case-fold-search t) |
ffc1e1db | 1606 | found bounds) |
cdc61d35 SM |
1607 | (beginning-of-line) |
1608 | ;; Loop till we look at a valid entry. | |
d715b065 | 1609 | (while (not (or found (if backward (bobp) (eobp)))) |
ffc1e1db RW |
1610 | (cond ((setq found (or (bibtex-valid-entry) |
1611 | (and (not bibtex-sort-ignore-string-entries) | |
1612 | (setq bounds (bibtex-parse-string)) | |
1613 | (cons (bibtex-start-of-field bounds) | |
1614 | (bibtex-end-of-string bounds)))))) | |
1615 | (backward (re-search-backward "^[ \t]*@" nil 'move)) | |
1616 | (t (if (re-search-forward "\n\\([ \t]*@\\)" nil 'move) | |
1617 | (goto-char (match-beginning 1)))))) | |
7fbf4804 | 1618 | found)) |
9ae11a89 | 1619 | |
55fe21fc | 1620 | (defun bibtex-map-entries (fun) |
e0dc0c55 SM |
1621 | "Call FUN for each BibTeX entry in buffer (possibly narrowed). |
1622 | FUN is called with three arguments, the key of the entry and the buffer | |
ffc1e1db RW |
1623 | positions of beginning and end of entry. Also, point is at beginning of |
1624 | entry and `match-data' corresponds to the header of the entry, | |
1625 | see regexp `bibtex-entry-head'. If `bibtex-sort-ignore-string-entries' | |
1626 | is non-nil, FUN is not called for @String entries." | |
cdc61d35 SM |
1627 | (let ((case-fold-search t) |
1628 | found) | |
e0dc0c55 SM |
1629 | (save-excursion |
1630 | (goto-char (point-min)) | |
cdc61d35 SM |
1631 | (while (setq found (bibtex-skip-to-valid-entry)) |
1632 | (looking-at bibtex-any-entry-maybe-empty-head) | |
1633 | (funcall fun (bibtex-key-in-head "") (car found) (cdr found)) | |
1634 | (goto-char (cdr found)))))) | |
50e4b39e RS |
1635 | |
1636 | (defun bibtex-progress-message (&optional flag interval) | |
7fbf4804 SM |
1637 | "Echo a message about progress of current buffer. |
1638 | If FLAG is a string, the message is initialized (in this case a | |
1639 | value for INTERVAL may be given as well (if not this is set to 5)). | |
02c8032e | 1640 | If FLAG is `done', the message is deinitialized. |
8bf38a9b SM |
1641 | If FLAG is nil, a message is echoed if point was incremented at least |
1642 | `bibtex-progress-interval' percent since last message was echoed." | |
7fbf4804 | 1643 | (cond ((stringp flag) |
02c8032e SM |
1644 | (setq bibtex-progress-lastmes flag |
1645 | bibtex-progress-interval (or interval 5) | |
7fbf4804 | 1646 | bibtex-progress-lastperc 0)) |
02c8032e | 1647 | ((eq flag 'done) |
7fbf4804 SM |
1648 | (message "%s (done)" bibtex-progress-lastmes) |
1649 | (setq bibtex-progress-lastmes nil)) | |
1650 | (t | |
1651 | (let* ((size (- (point-max) (point-min))) | |
1652 | (perc (if (= size 0) | |
1653 | 100 | |
1654 | (/ (* 100 (- (point) (point-min))) size)))) | |
1655 | (when (>= perc (+ bibtex-progress-lastperc | |
1656 | bibtex-progress-interval)) | |
1657 | (setq bibtex-progress-lastperc perc) | |
1658 | (message "%s (%d%%)" bibtex-progress-lastmes perc)))))) | |
50e4b39e RS |
1659 | |
1660 | (defun bibtex-field-left-delimiter () | |
7fbf4804 | 1661 | "Return a string dependent on `bibtex-field-delimiters'." |
02c8032e | 1662 | (if (eq bibtex-field-delimiters 'braces) |
50e4b39e RS |
1663 | "{" |
1664 | "\"")) | |
1665 | ||
1666 | (defun bibtex-field-right-delimiter () | |
7fbf4804 | 1667 | "Return a string dependent on `bibtex-field-delimiters'." |
02c8032e | 1668 | (if (eq bibtex-field-delimiters 'braces) |
50e4b39e RS |
1669 | "}" |
1670 | "\"")) | |
1671 | ||
1672 | (defun bibtex-entry-left-delimiter () | |
e0dc0c55 | 1673 | "Return a string dependent on `bibtex-entry-delimiters'." |
02c8032e | 1674 | (if (eq bibtex-entry-delimiters 'braces) |
50e4b39e RS |
1675 | "{" |
1676 | "(")) | |
1677 | ||
1678 | (defun bibtex-entry-right-delimiter () | |
e0dc0c55 | 1679 | "Return a string dependent on `bibtex-entry-delimiters'." |
02c8032e | 1680 | (if (eq bibtex-entry-delimiters 'braces) |
50e4b39e RS |
1681 | "}" |
1682 | ")")) | |
1683 | ||
ffc1e1db | 1684 | (defun bibtex-flash-head (prompt) |
7fbf4804 SM |
1685 | "Flash at BibTeX entry head before point, if exists." |
1686 | (let ((case-fold-search t) | |
ffc1e1db | 1687 | (pnt (point))) |
cdc61d35 SM |
1688 | (save-excursion |
1689 | (bibtex-beginning-of-entry) | |
1690 | (when (and (looking-at bibtex-any-entry-maybe-empty-head) | |
1691 | (< (point) pnt)) | |
1692 | (goto-char (match-beginning bibtex-type-in-head)) | |
cdc61d35 SM |
1693 | (if (pos-visible-in-window-p (point)) |
1694 | (sit-for 1) | |
ffc1e1db RW |
1695 | (message "%s%s" prompt (buffer-substring-no-properties |
1696 | (point) (match-end bibtex-key-in-head)))))))) | |
e5167999 | 1697 | |
d715b065 KG |
1698 | (defun bibtex-make-optional-field (field) |
1699 | "Make an optional field named FIELD in current BibTeX entry." | |
1700 | (if (consp field) | |
1701 | (bibtex-make-field (cons (concat "OPT" (car field)) (cdr field))) | |
1702 | (bibtex-make-field (concat "OPT" field)))) | |
50e4b39e | 1703 | |
cb4ad359 | 1704 | (defun bibtex-move-outside-of-entry () |
7fbf4804 | 1705 | "Make sure point is outside of a BibTeX entry." |
f0cb6034 | 1706 | (let ((orig-point (point))) |
28f2ee66 | 1707 | (bibtex-end-of-entry) |
0640e91a | 1708 | (when (< (point) orig-point) |
7fbf4804 SM |
1709 | ;; We moved backward, so we weren't inside an entry to begin with. |
1710 | ;; Leave point at the beginning of a line, and preferably | |
1711 | ;; at the beginning of a paragraph. | |
1712 | (goto-char orig-point) | |
1713 | (beginning-of-line 1) | |
0640e91a RS |
1714 | (unless (= ?\n (char-before (1- (point)))) |
1715 | (re-search-forward "^[ \t]*[@\n]" nil 'move) | |
1716 | (backward-char 1))) | |
f0cb6034 | 1717 | (skip-chars-forward " \t\n"))) |
50e4b39e RS |
1718 | |
1719 | (defun bibtex-beginning-of-first-entry () | |
cdc61d35 SM |
1720 | "Go to beginning of line of first BibTeX entry in buffer. |
1721 | If `bibtex-sort-ignore-string-entries' is non-nil, @String entries | |
1722 | are ignored. Return point" | |
e5167999 | 1723 | (goto-char (point-min)) |
cdc61d35 | 1724 | (bibtex-skip-to-valid-entry) |
50e4b39e | 1725 | (point)) |
e5167999 | 1726 | |
ffc1e1db | 1727 | (defun bibtex-enclosing-field (&optional comma noerr) |
d528bff7 | 1728 | "Search for BibTeX field enclosing point. |
ffc1e1db RW |
1729 | For `bibtex-mode''s internal algorithms, a field begins at the comma |
1730 | following the preceding field. Usually, this is not what the user expects. | |
1731 | Thus if COMMA is non-nil, the \"current field\" includes the terminating comma. | |
02c8032e SM |
1732 | Unless NOERR is non-nil, signal an error if no enclosing field is found. |
1733 | On success return bounds, nil otherwise. Do not move point." | |
ffc1e1db RW |
1734 | (save-excursion |
1735 | (when comma | |
1736 | (end-of-line) | |
1737 | (skip-chars-backward " \t") | |
1738 | (if (= (preceding-char) ?,) (forward-char -1))) | |
1739 | ||
1740 | (let ((bounds (bibtex-search-backward-field bibtex-field-name t))) | |
1741 | (cond ((and bounds | |
1742 | (<= (bibtex-start-of-field bounds) (point)) | |
1743 | (>= (bibtex-end-of-field bounds) (point))) | |
1744 | bounds) | |
1745 | ((not noerr) | |
1746 | (error "Can't find enclosing BibTeX field")))))) | |
1747 | ||
1748 | (defun bibtex-beginning-first-field (&optional beg) | |
1749 | "Move point to beginning of first field. | |
1750 | Optional arg BEG is beginning of entry." | |
1751 | (if beg (goto-char beg) (bibtex-beginning-of-entry)) | |
1752 | (looking-at bibtex-any-entry-maybe-empty-head) | |
1753 | (goto-char (match-end 0))) | |
1754 | ||
1755 | (defun bibtex-insert-kill (n &optional comma) | |
1756 | "Reinsert the Nth stretch of killed BibTeX text (field or entry). | |
1757 | Optional arg COMMA is as in `bibtex-enclosing-field'." | |
1758 | (unless bibtex-last-kill-command (error "BibTeX kill ring is empty")) | |
1759 | (let ((fun (lambda (kryp kr) ;; adapted from `current-kill' | |
1760 | (car (set kryp (nthcdr (mod (- n (length (eval kryp))) | |
1761 | (length kr)) kr)))))) | |
1762 | (if (eq bibtex-last-kill-command 'field) | |
1763 | (progn | |
1764 | ;; insert past the current field | |
1765 | (goto-char (bibtex-end-of-field (bibtex-enclosing-field comma))) | |
1766 | (set-mark (point)) | |
1767 | (message "Mark set") | |
1768 | (bibtex-make-field (funcall fun 'bibtex-field-kill-ring-yank-pointer | |
1769 | bibtex-field-kill-ring) t)) | |
1770 | ;; insert past the current entry | |
1771 | (bibtex-skip-to-valid-entry) | |
1772 | (set-mark (point)) | |
1773 | (message "Mark set") | |
1774 | (insert (funcall fun 'bibtex-entry-kill-ring-yank-pointer | |
1775 | bibtex-entry-kill-ring))))) | |
f9bd4abe | 1776 | |
50e4b39e | 1777 | (defun bibtex-format-entry () |
7fbf4804 SM |
1778 | "Helper function for `bibtex-clean-entry'. |
1779 | Formats current entry according to variable `bibtex-entry-format'." | |
1780 | (save-excursion | |
1781 | (save-restriction | |
1782 | (bibtex-narrow-to-entry) | |
1783 | (let ((case-fold-search t) | |
02c8032e | 1784 | (format (if (eq bibtex-entry-format t) |
7fbf4804 SM |
1785 | '(realign opts-or-alts required-fields |
1786 | numerical-fields | |
1787 | last-comma page-dashes delimiters | |
1788 | unify-case inherit-booktitle) | |
1789 | bibtex-entry-format)) | |
1790 | crossref-key bounds alternatives-there non-empty-alternative | |
d528bff7 | 1791 | entry-list req-field-list field-list) |
7fbf4804 SM |
1792 | |
1793 | ;; identify entry type | |
1794 | (goto-char (point-min)) | |
1795 | (re-search-forward bibtex-entry-type) | |
1796 | (let ((beg-type (1+ (match-beginning 0))) | |
1797 | (end-type (match-end 0))) | |
c2fb6415 RS |
1798 | (setq entry-list (assoc-string (buffer-substring-no-properties |
1799 | beg-type end-type) | |
1800 | bibtex-entry-field-alist | |
1801 | t)) | |
7fbf4804 SM |
1802 | |
1803 | ;; unify case of entry name | |
1804 | (when (memq 'unify-case format) | |
1805 | (delete-region beg-type end-type) | |
1806 | (insert (car entry-list))) | |
1807 | ||
1808 | ;; update left entry delimiter | |
1809 | (when (memq 'delimiters format) | |
1810 | (goto-char end-type) | |
1811 | (skip-chars-forward " \t\n") | |
1812 | (delete-char 1) | |
1813 | (insert (bibtex-entry-left-delimiter)))) | |
1814 | ||
1815 | ;; determine if entry has crossref field and if at least | |
1816 | ;; one alternative is non-empty | |
1817 | (goto-char (point-min)) | |
cdc61d35 | 1818 | (let* ((fields-alist (bibtex-parse-entry t)) |
d528bff7 | 1819 | (field (assoc-string "crossref" fields-alist t))) |
8bf38a9b | 1820 | (setq crossref-key (and field |
cdc61d35 SM |
1821 | (not (equal "" (cdr field))) |
1822 | (cdr field)) | |
8bf38a9b | 1823 | req-field-list (if crossref-key |
e0dc0c55 SM |
1824 | (nth 0 (nth 2 entry-list)) ; crossref part |
1825 | (nth 0 (nth 1 entry-list)))) ; required part | |
8bf38a9b SM |
1826 | |
1827 | (dolist (rfield req-field-list) | |
1828 | (when (nth 3 rfield) ; we should have an alternative | |
1829 | (setq alternatives-there t | |
d528bff7 | 1830 | field (assoc-string (car rfield) fields-alist t)) |
8bf38a9b | 1831 | (if (and field |
cdc61d35 | 1832 | (not (equal "" (cdr field)))) |
8bf38a9b SM |
1833 | (cond ((not non-empty-alternative) |
1834 | (setq non-empty-alternative t)) | |
1835 | ((memq 'required-fields format) | |
02c8032e | 1836 | (error "More than one non-empty alternative"))))))) |
8bf38a9b | 1837 | |
50e4b39e | 1838 | (if (and alternatives-there |
7fbf4804 SM |
1839 | (not non-empty-alternative) |
1840 | (memq 'required-fields format)) | |
1841 | (error "All alternatives are empty")) | |
1842 | ||
1843 | ;; process all fields | |
ffc1e1db RW |
1844 | (bibtex-beginning-first-field (point-min)) |
1845 | (while (setq bounds (bibtex-parse-field)) | |
7fbf4804 | 1846 | (let* ((beg-field (copy-marker (bibtex-start-of-field bounds))) |
02c8032e | 1847 | (end-field (copy-marker (bibtex-end-of-field bounds) t)) |
7fbf4804 SM |
1848 | (beg-name (copy-marker (bibtex-start-of-name-in-field bounds))) |
1849 | (end-name (copy-marker (bibtex-end-of-name-in-field bounds))) | |
1850 | (beg-text (copy-marker (bibtex-start-of-text-in-field bounds))) | |
02c8032e | 1851 | (end-text (copy-marker (bibtex-end-of-text-in-field bounds) t)) |
7fbf4804 | 1852 | (opt-alt (string-match "OPT\\|ALT" |
02c8032e SM |
1853 | (buffer-substring-no-properties |
1854 | beg-name (+ beg-name 3)))) | |
7fbf4804 SM |
1855 | (field-name (buffer-substring-no-properties |
1856 | (if opt-alt (+ beg-name 3) beg-name) end-name)) | |
cdc61d35 | 1857 | (empty-field (equal "" (bibtex-text-in-field-bounds bounds t))) |
7fbf4804 SM |
1858 | deleted) |
1859 | ||
1860 | ;; We have more elegant high-level functions for several | |
e0dc0c55 | 1861 | ;; tasks done by bibtex-format-entry. However, they contain |
7fbf4804 | 1862 | ;; quite some redundancy compared with what we need to do |
e0dc0c55 | 1863 | ;; anyway. So for speed-up we avoid using them. |
7fbf4804 | 1864 | |
8bf38a9b SM |
1865 | (if (memq 'opts-or-alts format) |
1866 | (cond ((and empty-field | |
1867 | (or opt-alt | |
c2fb6415 RS |
1868 | (let ((field (assoc-string |
1869 | field-name req-field-list t))) | |
8bf38a9b SM |
1870 | (or (not field) ; OPT field |
1871 | (nth 3 field))))) ; ALT field | |
e0dc0c55 SM |
1872 | ;; Either it is an empty ALT field. Then we have checked |
1873 | ;; already that we have one non-empty alternative. Or it | |
8bf38a9b SM |
1874 | ;; is an empty OPT field that we do not miss anyway. |
1875 | ;; So we can safely delete this field. | |
1876 | (delete-region beg-field end-field) | |
1877 | (setq deleted t)) | |
1878 | ;; otherwise: not empty, delete "OPT" or "ALT" | |
1879 | (opt-alt | |
1880 | (goto-char beg-name) | |
1881 | (delete-char 3)))) | |
7fbf4804 SM |
1882 | |
1883 | (unless deleted | |
1884 | (push field-name field-list) | |
1885 | ||
1886 | ;; remove delimiters from purely numerical fields | |
1887 | (when (and (memq 'numerical-fields format) | |
1888 | (progn (goto-char beg-text) | |
1889 | (looking-at "\\(\"[0-9]+\"\\)\\|\\({[0-9]+}\\)"))) | |
1890 | (goto-char end-text) | |
1891 | (delete-char -1) | |
1892 | (goto-char beg-text) | |
1893 | (delete-char 1)) | |
1894 | ||
1895 | ;; update delimiters | |
1896 | (when (memq 'delimiters format) | |
1897 | (goto-char beg-text) | |
1898 | (when (looking-at "[{\"]") | |
1899 | (delete-char 1) | |
1900 | (insert (bibtex-field-left-delimiter))) | |
1901 | (goto-char (1- (marker-position end-text))) | |
1902 | (when (looking-at "[}\"]") | |
1903 | (delete-char 1) | |
1904 | (insert (bibtex-field-right-delimiter)))) | |
1905 | ||
1906 | ;; update page dashes | |
1907 | (if (and (memq 'page-dashes format) | |
d528bff7 | 1908 | (bibtex-string= field-name "pages") |
7fbf4804 SM |
1909 | (progn (goto-char beg-text) |
1910 | (looking-at | |
1911 | "\\([\"{][0-9]+\\)[ \t\n]*--?[ \t\n]*\\([0-9]+[\"}]\\)"))) | |
1912 | (replace-match "\\1-\\2")) | |
1913 | ||
d715b065 | 1914 | ;; use book title of crossref'd entry |
7fbf4804 | 1915 | (if (and (memq 'inherit-booktitle format) |
7fbf4804 | 1916 | empty-field |
d528bff7 | 1917 | (bibtex-string= field-name "booktitle") |
7fbf4804 | 1918 | crossref-key) |
02c8032e SM |
1919 | (let ((title (save-excursion |
1920 | (save-restriction | |
1921 | (widen) | |
1922 | (if (bibtex-find-entry crossref-key t) | |
1923 | (bibtex-text-in-field "title")))))) | |
7fbf4804 SM |
1924 | (when title |
1925 | (setq empty-field nil) | |
1926 | (goto-char (1+ beg-text)) | |
1927 | (insert title)))) | |
1928 | ||
d715b065 KG |
1929 | ;; Use booktitle to set a missing title. |
1930 | (if (and empty-field | |
d528bff7 | 1931 | (bibtex-string= field-name "title")) |
d715b065 KG |
1932 | (let ((booktitle (bibtex-text-in-field "booktitle"))) |
1933 | (when booktitle | |
1934 | (setq empty-field nil) | |
1935 | (goto-char (1+ beg-text)) | |
1936 | (insert booktitle)))) | |
7fbf4804 SM |
1937 | |
1938 | ;; if empty field, complain | |
1939 | (if (and empty-field | |
1940 | (memq 'required-fields format) | |
c2fb6415 | 1941 | (assoc-string field-name req-field-list t)) |
7fbf4804 SM |
1942 | (error "Mandatory field `%s' is empty" field-name)) |
1943 | ||
1944 | ;; unify case of field name | |
1945 | (if (memq 'unify-case format) | |
c2fb6415 RS |
1946 | (let ((fname (car (assoc-string |
1947 | field-name | |
1948 | (append (nth 0 (nth 1 entry-list)) | |
1949 | (nth 1 (nth 1 entry-list)) | |
1950 | bibtex-user-optional-fields) | |
1951 | t)))) | |
7fbf4804 SM |
1952 | (if fname |
1953 | (progn | |
1954 | (delete-region beg-name end-name) | |
1955 | (goto-char beg-name) | |
1956 | (insert fname)) | |
1957 | ;; there are no rules we could follow | |
1958 | (downcase-region beg-name end-name)))) | |
1959 | ||
1960 | ;; update point | |
1961 | (goto-char end-field)))) | |
1962 | ||
1963 | ;; check whether all required fields are present | |
1964 | (if (memq 'required-fields format) | |
8bf38a9b SM |
1965 | (let ((found 0) altlist) |
1966 | (dolist (fname req-field-list) | |
7fbf4804 SM |
1967 | (if (nth 3 fname) |
1968 | (push (car fname) altlist)) | |
1969 | (unless (or (member (car fname) field-list) | |
1970 | (nth 3 fname)) | |
1971 | (error "Mandatory field `%s' is missing" (car fname)))) | |
1972 | (when altlist | |
1973 | (dolist (fname altlist) | |
1974 | (if (member fname field-list) | |
1975 | (setq found (1+ found)))) | |
1976 | (cond ((= found 0) | |
1977 | (error "Alternative mandatory field `%s' is missing" | |
1978 | altlist)) | |
1979 | ((> found 1) | |
8bf38a9b | 1980 | (error "Alternative fields `%s' are defined %s times" |
7fbf4804 SM |
1981 | altlist found)))))) |
1982 | ||
7fbf4804 SM |
1983 | ;; update comma after last field |
1984 | (if (memq 'last-comma format) | |
1985 | (cond ((and bibtex-comma-after-last-field | |
1986 | (not (looking-at ","))) | |
1987 | (insert ",")) | |
1988 | ((and (not bibtex-comma-after-last-field) | |
1989 | (looking-at ",")) | |
1990 | (delete-char 1)))) | |
1991 | ||
1992 | ;; update right entry delimiter | |
1993 | (if (looking-at ",") | |
1994 | (forward-char)) | |
1995 | (when (memq 'delimiters format) | |
1996 | (skip-chars-forward " \t\n") | |
1997 | (delete-char 1) | |
1998 | (insert (bibtex-entry-right-delimiter))) | |
1999 | ||
2000 | ;; fill entry | |
2001 | (if (memq 'realign format) | |
2002 | (bibtex-fill-entry)))))) | |
cb4ad359 | 2003 | |
7fbf4804 | 2004 | \f |
cb4ad359 | 2005 | (defun bibtex-autokey-abbrev (string len) |
7fbf4804 SM |
2006 | "Return an abbreviation of STRING with at least LEN characters. |
2007 | If LEN is positive the abbreviation is terminated only after a consonant | |
a9d77f1f SM |
2008 | or at the word end. If LEN is negative the abbreviation is strictly |
2009 | enforced using abs (LEN) characters. If LEN is not a number, STRING | |
7fbf4804 SM |
2010 | is returned unchanged." |
2011 | (cond ((or (not (numberp len)) | |
2012 | (<= (length string) (abs len))) | |
50e4b39e RS |
2013 | string) |
2014 | ((equal len 0) | |
2015 | "") | |
7fbf4804 SM |
2016 | ((< len 0) |
2017 | (substring string 0 (abs len))) | |
2018 | (t (let* ((case-fold-search t) | |
2019 | (abort-char (string-match "[^aeiou]" string (1- len)))) | |
2020 | (if abort-char | |
2021 | (substring string 0 (1+ abort-char)) | |
2022 | string))))) | |
2023 | ||
2024 | (defun bibtex-autokey-get-field (field &optional change-list) | |
a9d77f1f | 2025 | "Get content of BibTeX field FIELD. Return empty string if not found. |
7fbf4804 | 2026 | Optional arg CHANGE-LIST is a list of substitution patterns that is |
a9d77f1f | 2027 | applied to the content of FIELD. It is an alist with pairs |
7fbf4804 | 2028 | \(OLD-REGEXP . NEW-STRING\)." |
cdc61d35 SM |
2029 | (let* ((bibtex-expand-strings bibtex-autokey-expand-strings) |
2030 | (content (bibtex-text-in-field field bibtex-autokey-use-crossref)) | |
7fbf4804 SM |
2031 | case-fold-search) |
2032 | (unless content (setq content "")) | |
2033 | (dolist (pattern change-list content) | |
2034 | (setq content (replace-regexp-in-string (car pattern) | |
2035 | (cdr pattern) | |
e0dc0c55 | 2036 | content t))))) |
7fbf4804 SM |
2037 | |
2038 | (defun bibtex-autokey-get-names () | |
2039 | "Get contents of the name field of the current entry. | |
e0dc0c55 SM |
2040 | Do some modifications based on `bibtex-autokey-name-change-strings'. |
2041 | Return the names as a concatenated string obeying `bibtex-autokey-names' | |
2042 | and `bibtex-autokey-names-stretch'." | |
2043 | (let ((names (bibtex-autokey-get-field "author\\|editor" | |
d528bff7 SM |
2044 | bibtex-autokey-name-change-strings))) |
2045 | ;; Some entries do not have a name field. | |
02c8032e SM |
2046 | (if (string= "" names) |
2047 | names | |
e0dc0c55 SM |
2048 | (let* ((case-fold-search t) |
2049 | (name-list (mapcar 'bibtex-autokey-demangle-name | |
2050 | (split-string names "[ \t\n]+and[ \t\n]+"))) | |
2051 | additional-names) | |
2052 | (unless (or (not (numberp bibtex-autokey-names)) | |
2053 | (<= (length name-list) | |
2054 | (+ bibtex-autokey-names | |
2055 | bibtex-autokey-names-stretch))) | |
2056 | ;; Take bibtex-autokey-names elements from beginning of name-list | |
2057 | (setq name-list (nreverse (nthcdr (- (length name-list) | |
2058 | bibtex-autokey-names) | |
2059 | (nreverse name-list))) | |
2060 | additional-names bibtex-autokey-additional-names)) | |
2061 | (concat (mapconcat 'identity name-list | |
2062 | bibtex-autokey-name-separator) | |
2063 | additional-names))))) | |
50e4b39e RS |
2064 | |
2065 | (defun bibtex-autokey-demangle-name (fullname) | |
a9d77f1f | 2066 | "Get the last part from a well-formed FULLNAME and perform abbreviations." |
f9bd4abe | 2067 | (let* (case-fold-search |
a172852f | 2068 | (name (cond ((string-match "\\([[:upper:]][^, ]*\\)[^,]*," fullname) |
7fbf4804 SM |
2069 | ;; Name is of the form "von Last, First" or |
2070 | ;; "von Last, Jr, First" | |
2071 | ;; --> Take the first capital part before the comma | |
dd310c45 | 2072 | (match-string 1 fullname)) |
7fbf4804 SM |
2073 | ((string-match "\\([^, ]*\\)," fullname) |
2074 | ;; Strange name: we have a comma, but nothing capital | |
2075 | ;; So we accept even lowercase names | |
dd310c45 | 2076 | (match-string 1 fullname)) |
a172852f | 2077 | ((string-match "\\(\\<[[:lower:]][^ ]* +\\)+\\([[:upper:]][^ ]*\\)" |
7fbf4804 SM |
2078 | fullname) |
2079 | ;; name is of the form "First von Last", "von Last", | |
2080 | ;; "First von von Last", or "d'Last" | |
2081 | ;; --> take the first capital part after the "von" parts | |
dd310c45 SM |
2082 | (match-string 2 fullname)) |
2083 | ((string-match "\\([^ ]+\\) *\\'" fullname) | |
7fbf4804 SM |
2084 | ;; name is of the form "First Middle Last" or "Last" |
2085 | ;; --> take the last token | |
dd310c45 | 2086 | (match-string 1 fullname)) |
7fbf4804 | 2087 | (t (error "Name `%s' is incorrectly formed" fullname))))) |
02c8032e SM |
2088 | (funcall bibtex-autokey-name-case-convert |
2089 | (bibtex-autokey-abbrev name bibtex-autokey-name-length)))) | |
7fbf4804 | 2090 | |
e0dc0c55 SM |
2091 | (defun bibtex-autokey-get-year () |
2092 | "Return year field contents as a string obeying `bibtex-autokey-year-length'." | |
2093 | (let ((yearfield (bibtex-autokey-get-field "year"))) | |
2094 | (substring yearfield (max 0 (- (length yearfield) | |
2095 | bibtex-autokey-year-length))))) | |
2096 | ||
7fbf4804 | 2097 | (defun bibtex-autokey-get-title () |
e0dc0c55 SM |
2098 | "Get title field contents up to a terminator. |
2099 | Return the result as a string" | |
d528bff7 SM |
2100 | (let ((case-fold-search t) |
2101 | (titlestring | |
7fbf4804 SM |
2102 | (bibtex-autokey-get-field "title" |
2103 | bibtex-autokey-titleword-change-strings))) | |
2104 | ;; ignore everything past a terminator | |
02c8032e SM |
2105 | (if (string-match bibtex-autokey-title-terminators titlestring) |
2106 | (setq titlestring (substring titlestring 0 (match-beginning 0)))) | |
e0dc0c55 | 2107 | ;; gather words from titlestring into a list. Ignore |
7fbf4804 | 2108 | ;; specific words and use only a specific amount of words. |
8bf38a9b | 2109 | (let ((counter 0) |
e0dc0c55 | 2110 | titlewords titlewords-extra word) |
7fbf4804 SM |
2111 | (while (and (or (not (numberp bibtex-autokey-titlewords)) |
2112 | (< counter (+ bibtex-autokey-titlewords | |
2113 | bibtex-autokey-titlewords-stretch))) | |
2114 | (string-match "\\b\\w+" titlestring)) | |
e0dc0c55 SM |
2115 | (setq word (match-string 0 titlestring) |
2116 | titlestring (substring titlestring (match-end 0))) | |
2117 | ;; Ignore words matched by one of the elements of | |
2118 | ;; bibtex-autokey-titleword-ignore | |
d528bff7 SM |
2119 | (unless (let ((lst bibtex-autokey-titleword-ignore)) |
2120 | (while (and lst | |
2121 | (not (string-match (concat "\\`\\(?:" (car lst) | |
e0dc0c55 | 2122 | "\\)\\'") word))) |
d528bff7 SM |
2123 | (setq lst (cdr lst))) |
2124 | lst) | |
02c8032e | 2125 | (setq counter (1+ counter)) |
7fbf4804 | 2126 | (if (or (not (numberp bibtex-autokey-titlewords)) |
d10e87a2 | 2127 | (<= counter bibtex-autokey-titlewords)) |
e0dc0c55 SM |
2128 | (push word titlewords) |
2129 | (push word titlewords-extra)))) | |
2130 | ;; Obey bibtex-autokey-titlewords-stretch: | |
2131 | ;; If by now we have processed all words in titlestring, we include | |
2132 | ;; titlewords-extra in titlewords. Otherwise, we ignore titlewords-extra. | |
7fbf4804 | 2133 | (unless (string-match "\\b\\w+" titlestring) |
e0dc0c55 SM |
2134 | (setq titlewords (append titlewords-extra titlewords))) |
2135 | (mapconcat 'bibtex-autokey-demangle-title (nreverse titlewords) | |
2136 | bibtex-autokey-titleword-separator)))) | |
7fbf4804 SM |
2137 | |
2138 | (defun bibtex-autokey-demangle-title (titleword) | |
2139 | "Do some abbreviations on TITLEWORD. | |
2140 | The rules are defined in `bibtex-autokey-titleword-abbrevs' | |
2141 | and `bibtex-autokey-titleword-length'." | |
d528bff7 | 2142 | (let ((case-fold-search t) |
8bf38a9b SM |
2143 | (alist bibtex-autokey-titleword-abbrevs)) |
2144 | (while (and alist | |
2145 | (not (string-match (concat "\\`\\(?:" (caar alist) "\\)\\'") | |
2146 | titleword))) | |
2147 | (setq alist (cdr alist))) | |
2148 | (if alist | |
2149 | (cdar alist) | |
02c8032e SM |
2150 | (funcall bibtex-autokey-titleword-case-convert |
2151 | (bibtex-autokey-abbrev titleword bibtex-autokey-titleword-length))))) | |
cb4ad359 RS |
2152 | |
2153 | (defun bibtex-generate-autokey () | |
02c8032e SM |
2154 | "Generate automatically a key for a BibTeX entry. |
2155 | Use the author/editor, the year and the title field. | |
2156 | The algorithm works as follows. | |
2157 | ||
2158 | The name part: | |
2159 | 1. Use the author or editor field to generate the name part of the key. | |
cdc61d35 | 2160 | Expand BibTeX strings if `bibtex-autokey-expand-strings' is non-nil. |
02c8032e SM |
2161 | 2. Change the content of the name field according to |
2162 | `bibtex-autokey-name-change-strings' (see there for further detail). | |
2163 | 3. Use the first `bibtex-autokey-names' names in the name field. If there | |
2164 | are up to `bibtex-autokey-names' + `bibtex-autokey-names-stretch' names, | |
2165 | use all names. | |
2166 | 4. Use only the last names to form the name part. From these last names, | |
2167 | take at least `bibtex-autokey-name-length' characters (truncate only | |
2168 | after a consonant or at a word end). | |
2169 | 5. Convert all last names using the function | |
ab2d0cdb | 2170 | `bibtex-autokey-name-case-convert'. |
02c8032e SM |
2171 | 6. Build the name part of the key by concatenating all abbreviated last |
2172 | names with the string `bibtex-autokey-name-separator' between any two. | |
2173 | If there are more names in the name field than names used in the name | |
2174 | part, append the string `bibtex-autokey-additional-names'. | |
2175 | ||
2176 | The year part: | |
2177 | 1. Build the year part of the key by truncating the content of the year | |
2178 | field to the rightmost `bibtex-autokey-year-length' digits (useful | |
2179 | values are 2 and 4). | |
2180 | 2. If the year field (or any other field required to generate the key) | |
2181 | is absent, but the entry has a valid crossref field and | |
7fbf4804 SM |
2182 | `bibtex-autokey-use-crossref' is non-nil, use the field of the |
2183 | crossreferenced entry instead. | |
02c8032e SM |
2184 | |
2185 | The title part | |
2186 | 1. Change the content of the title field according to | |
2187 | `bibtex-autokey-titleword-change-strings' (see there for further detail). | |
2188 | 2. Truncate the title before the first match of | |
2189 | `bibtex-autokey-title-terminators' and delete those words which appear | |
2190 | in `bibtex-autokey-titleword-ignore'. Build the title part using the | |
2191 | first `bibtex-autokey-titlewords' words from this truncated title. | |
2192 | If the truncated title ends after up to `bibtex-autokey-titlewords' + | |
2193 | `bibtex-autokey-titlewords-stretch' words, use all words from the | |
2194 | truncated title. | |
2195 | 3. For every title word that appears in `bibtex-autokey-titleword-abbrevs' | |
2196 | use the corresponding abbreviation (see documentation of this variable | |
2197 | for further detail). | |
2198 | 4. From every title word not generated by an abbreviation, take at least | |
2199 | `bibtex-autokey-titleword-length' characters (truncate only after | |
2200 | a consonant or at a word end). | |
2201 | 5. Convert all title words using the function | |
ab2d0cdb | 2202 | `bibtex-autokey-titleword-case-convert'. |
02c8032e SM |
2203 | 6. Build the title part by concatenating all abbreviated title words with |
2204 | the string `bibtex-autokey-titleword-separator' between any two. | |
2205 | ||
2206 | Concatenate the key: | |
2207 | 1. Concatenate `bibtex-autokey-prefix-string', the name part, the year | |
2208 | part and the title part. If the name part and the year part are both | |
2209 | non-empty insert `bibtex-autokey-name-year-separator' between the two. | |
2210 | If the title part and the year (or name) part are non-empty, insert | |
2211 | `bibtex-autokey-year-title-separator' between the two. | |
2212 | 2. If `bibtex-autokey-before-presentation-function' is non-nil, it must be | |
2213 | a function taking one argument. Call this function with the generated | |
2214 | key as the argument. Use the return value of this function (a string) | |
2215 | as the key. | |
2216 | 3. If `bibtex-autokey-edit-before-use' is non-nil, present the key in the | |
2217 | minibuffer to the user for editing. Insert the key given by the user." | |
e0dc0c55 SM |
2218 | (let* ((names (bibtex-autokey-get-names)) |
2219 | (year (bibtex-autokey-get-year)) | |
2220 | (title (bibtex-autokey-get-title)) | |
7fbf4804 | 2221 | (autokey (concat bibtex-autokey-prefix-string |
e0dc0c55 SM |
2222 | names |
2223 | (unless (or (equal names "") | |
2224 | (equal year "")) | |
7fbf4804 | 2225 | bibtex-autokey-name-year-separator) |
e0dc0c55 SM |
2226 | year |
2227 | (unless (or (and (equal names "") | |
2228 | (equal year "")) | |
2229 | (equal title "")) | |
7fbf4804 | 2230 | bibtex-autokey-year-title-separator) |
e0dc0c55 | 2231 | title))) |
ab2d0cdb | 2232 | (if bibtex-autokey-before-presentation-function |
7fbf4804 SM |
2233 | (funcall bibtex-autokey-before-presentation-function autokey) |
2234 | autokey))) | |
e5167999 | 2235 | |
7fbf4804 | 2236 | \f |
02c8032e SM |
2237 | (defun bibtex-global-key-alist () |
2238 | "Return global key alist based on `bibtex-files'." | |
2239 | (if bibtex-files | |
2240 | (apply 'append | |
2241 | (mapcar (lambda (buf) | |
2242 | (with-current-buffer buf bibtex-reference-keys)) | |
2243 | (bibtex-files-expand t))) | |
2244 | bibtex-reference-keys)) | |
2245 | ||
2246 | (defun bibtex-read-key (prompt &optional key global) | |
2247 | "Read BibTeX key from minibuffer using PROMPT and default KEY. | |
2248 | If optional arg GLOBAL is non-nil, completion is based on the keys in | |
2249 | `bibtex-reference-keys' of `bibtex-files'," | |
2250 | (let (completion-ignore-case) | |
2251 | (completing-read prompt (if global (bibtex-global-key-alist) | |
2252 | bibtex-reference-keys) | |
2253 | nil nil key 'bibtex-key-history))) | |
2254 | ||
2255 | (defun bibtex-read-string-key (&optional key) | |
2256 | "Read BibTeX string key from minibuffer using default KEY." | |
2257 | (let ((completion-ignore-case t)) | |
2258 | (completing-read "String key: " bibtex-strings | |
2259 | nil nil key 'bibtex-key-history))) | |
e0dc0c55 SM |
2260 | |
2261 | (defun bibtex-parse-keys (&optional abortable verbose) | |
7fbf4804 | 2262 | "Set `bibtex-reference-keys' to the keys used in the whole buffer. |
02c8032e SM |
2263 | Find both entry keys and crossref entries. If ABORTABLE is non-nil abort |
2264 | on user input. If VERBOSE is non-nil give messages about progress. | |
cdc61d35 SM |
2265 | Return alist of keys if parsing was completed, `aborted' otherwise. |
2266 | If `bibtex-parse-keys-fast' is non-nil, use fast but simplified algorithm | |
2267 | for parsing BibTeX keys. If parsing fails, try to set this variable to nil." | |
e0dc0c55 | 2268 | (let (ref-keys crossref-keys) |
7fbf4804 SM |
2269 | (save-excursion |
2270 | (save-match-data | |
7fbf4804 SM |
2271 | (if verbose |
2272 | (bibtex-progress-message | |
2273 | (concat (buffer-name) ": parsing reference keys"))) | |
d715b065 KG |
2274 | (catch 'userkey |
2275 | (goto-char (point-min)) | |
2276 | (if bibtex-parse-keys-fast | |
2277 | (let ((case-fold-search t) | |
2278 | (re (concat bibtex-entry-head "\\|" | |
2279 | ",[ \t\n]*crossref[ \t\n]*=[ \t\n]*" | |
2280 | "\\(\"[^\"]*\"\\|{[^}]*}\\)[ \t\n]*[,})]"))) | |
2281 | (while (re-search-forward re nil t) | |
2282 | (if (and abortable (input-pending-p)) | |
2283 | ;; user has aborted by typing a key --> return `aborted' | |
2284 | (throw 'userkey 'aborted)) | |
e0dc0c55 SM |
2285 | (cond ((match-end 3) |
2286 | ;; This is a crossref. | |
2287 | (let ((key (buffer-substring-no-properties | |
2288 | (1+ (match-beginning 3)) (1- (match-end 3))))) | |
2289 | (unless (assoc key crossref-keys) | |
2290 | (push (list key) crossref-keys)))) | |
2291 | ;; only keys of known entries | |
2292 | ((assoc-string (bibtex-type-in-head) | |
2293 | bibtex-entry-field-alist t) | |
2294 | ;; This is an entry. | |
2295 | (let ((key (bibtex-key-in-head))) | |
2296 | (unless (assoc key ref-keys) | |
2297 | (push (cons key t) ref-keys))))))) | |
d715b065 KG |
2298 | |
2299 | (let (;; ignore @String entries because they are handled | |
2300 | ;; separately by bibtex-parse-strings | |
2301 | (bibtex-sort-ignore-string-entries t) | |
e0dc0c55 | 2302 | bounds) |
d715b065 KG |
2303 | (bibtex-map-entries |
2304 | (lambda (key beg end) | |
2305 | (if (and abortable | |
2306 | (input-pending-p)) | |
2307 | ;; user has aborted by typing a key --> return `aborted' | |
2308 | (throw 'userkey 'aborted)) | |
2309 | (if verbose (bibtex-progress-message)) | |
e0dc0c55 SM |
2310 | (unless (assoc key ref-keys) |
2311 | (push (cons key t) ref-keys)) | |
d715b065 | 2312 | (if (and (setq bounds (bibtex-search-forward-field "crossref" end)) |
e0dc0c55 SM |
2313 | (setq key (bibtex-text-in-field-bounds bounds t)) |
2314 | (not (assoc key crossref-keys))) | |
2315 | (push (list key) crossref-keys)))))) | |
d715b065 | 2316 | |
e0dc0c55 SM |
2317 | (dolist (key crossref-keys) |
2318 | (unless (assoc (car key) ref-keys) (push key ref-keys))) | |
7fbf4804 SM |
2319 | (if verbose |
2320 | (bibtex-progress-message 'done)) | |
d715b065 | 2321 | ;; successful operation --> return `bibtex-reference-keys' |
e0dc0c55 | 2322 | (setq bibtex-reference-keys ref-keys)))))) |
7fbf4804 | 2323 | |
d715b065 | 2324 | (defun bibtex-parse-strings (&optional add abortable) |
7fbf4804 | 2325 | "Set `bibtex-strings' to the string definitions in the whole buffer. |
d715b065 | 2326 | If ADD is non-nil add the new strings to `bibtex-strings' instead of |
a9d77f1f SM |
2327 | simply resetting it. If ADD is an alist of strings, also add ADD to |
2328 | `bibtex-strings'. If ABORTABLE is non-nil abort on user input. | |
d715b065 | 2329 | Return alist of strings if parsing was completed, `aborted' otherwise." |
7fbf4804 SM |
2330 | (save-excursion |
2331 | (save-match-data | |
2332 | (goto-char (point-min)) | |
d715b065 KG |
2333 | (let ((strings (if (and add |
2334 | (listp bibtex-strings)) | |
2335 | bibtex-strings)) | |
7fbf4804 | 2336 | bounds key) |
d715b065 KG |
2337 | (if (listp add) |
2338 | (dolist (string add) | |
d528bff7 | 2339 | (unless (assoc-string (car string) strings t) |
d715b065 KG |
2340 | (push string strings)))) |
2341 | (catch 'userkey | |
2342 | (while (setq bounds (bibtex-search-forward-string)) | |
2343 | (if (and abortable | |
2344 | (input-pending-p)) | |
2345 | ;; user has aborted by typing a key --> return `aborted' | |
2346 | (throw 'userkey 'aborted)) | |
2347 | (setq key (bibtex-reference-key-in-string bounds)) | |
d528bff7 SM |
2348 | (unless (assoc-string key strings t) |
2349 | (push (cons key (bibtex-text-in-string bounds t)) | |
2350 | strings)) | |
d715b065 KG |
2351 | (goto-char (bibtex-end-of-text-in-string bounds))) |
2352 | ;; successful operation --> return `bibtex-strings' | |
2353 | (setq bibtex-strings strings)))))) | |
7fbf4804 | 2354 | |
cdc61d35 SM |
2355 | (defun bibtex-strings () |
2356 | "Return `bibtex-strings'. Initialize this variable if necessary." | |
2357 | (if (listp bibtex-strings) bibtex-strings | |
2358 | (bibtex-parse-strings (bibtex-string-files-init)))) | |
2359 | ||
7fbf4804 SM |
2360 | (defun bibtex-string-files-init () |
2361 | "Return initialization for `bibtex-strings'. | |
e0dc0c55 | 2362 | Use `bibtex-predefined-strings' and BibTeX files `bibtex-string-files'." |
7fbf4804 | 2363 | (save-match-data |
e0dc0c55 | 2364 | (let ((dirlist (split-string (or bibtex-string-file-path default-directory) |
7fbf4804 SM |
2365 | ":+")) |
2366 | (case-fold-search) | |
e0dc0c55 SM |
2367 | string-files fullfilename compl bounds found) |
2368 | ;; collect absolute file names of valid string files | |
7fbf4804 | 2369 | (dolist (filename bibtex-string-files) |
dd310c45 | 2370 | (unless (string-match "\\.bib\\'" filename) |
7fbf4804 SM |
2371 | (setq filename (concat filename ".bib"))) |
2372 | ;; test filenames | |
e0dc0c55 SM |
2373 | (if (file-name-absolute-p filename) |
2374 | (if (file-readable-p filename) | |
2375 | (push filename string-files) | |
2376 | (error "BibTeX strings file %s not found" filename)) | |
7fbf4804 SM |
2377 | (dolist (dir dirlist) |
2378 | (when (file-readable-p | |
2379 | (setq fullfilename (expand-file-name filename dir))) | |
e0dc0c55 | 2380 | (push fullfilename string-files) |
7fbf4804 SM |
2381 | (setq found t))) |
2382 | (unless found | |
d715b065 | 2383 | (error "File %s not in paths defined via bibtex-string-file-path" |
7fbf4804 | 2384 | filename)))) |
e0dc0c55 SM |
2385 | ;; parse string files |
2386 | (dolist (filename string-files) | |
2387 | (with-temp-buffer | |
2388 | (insert-file-contents filename) | |
2389 | (goto-char (point-min)) | |
2390 | (while (setq bounds (bibtex-search-forward-string)) | |
2391 | (push (cons (bibtex-reference-key-in-string bounds) | |
2392 | (bibtex-text-in-string bounds t)) | |
2393 | compl) | |
2394 | (goto-char (bibtex-end-of-string bounds))))) | |
7fbf4804 | 2395 | (append bibtex-predefined-strings (nreverse compl))))) |
50e4b39e RS |
2396 | |
2397 | (defun bibtex-parse-buffers-stealthily () | |
a9d77f1f | 2398 | "Parse buffer in the background during idle time. |
e0dc0c55 | 2399 | Called by `run-with-idle-timer'. Whenever Emacs has been idle |
02c8032e SM |
2400 | for `bibtex-parse-keys-timeout' seconds, parse all BibTeX buffers |
2401 | which have been modified after last parsing. | |
2402 | Parsing initializes `bibtex-reference-keys' and `bibtex-strings'." | |
7fbf4804 SM |
2403 | (save-excursion |
2404 | (let ((buffers (buffer-list)) | |
2405 | (strings-init (bibtex-string-files-init))) | |
50e4b39e RS |
2406 | (while (and buffers (not (input-pending-p))) |
2407 | (set-buffer (car buffers)) | |
7fbf4804 SM |
2408 | (if (and (eq major-mode 'bibtex-mode) |
2409 | (not (eq (buffer-modified-tick) | |
2410 | bibtex-buffer-last-parsed-tick))) | |
2411 | (save-restriction | |
2412 | (widen) | |
2413 | ;; Output no progress messages in bibtex-parse-keys | |
2414 | ;; because when in y-or-n-p that can hide the question. | |
e0dc0c55 | 2415 | (if (and (listp (bibtex-parse-keys t)) |
7fbf4804 | 2416 | ;; update bibtex-strings |
d715b065 | 2417 | (listp (bibtex-parse-strings strings-init t))) |
7fbf4804 SM |
2418 | |
2419 | ;; remember that parsing was successful | |
2420 | (setq bibtex-buffer-last-parsed-tick (buffer-modified-tick))))) | |
2421 | (setq buffers (cdr buffers)))))) | |
2422 | ||
02c8032e | 2423 | (defun bibtex-files-expand (&optional current force) |
e0dc0c55 SM |
2424 | "Return an expanded list of BibTeX buffers based on `bibtex-files'. |
2425 | Initialize in these buffers `bibtex-reference-keys' if not yet set. | |
02c8032e SM |
2426 | List of BibTeX buffers includes current buffer if CURRENT is non-nil. |
2427 | If FORCE is non-nil, (re)initialize `bibtex-reference-keys' even if | |
2428 | already set." | |
e0dc0c55 SM |
2429 | (let ((file-path (split-string (or bibtex-file-path default-directory) ":+")) |
2430 | file-list dir-list buffer-list) | |
2431 | (dolist (file bibtex-files) | |
2432 | (cond ((eq file 'bibtex-file-path) | |
2433 | (setq dir-list (append dir-list file-path))) | |
2434 | ((file-accessible-directory-p file) | |
2435 | (push file dir-list)) | |
2436 | ((progn (unless (string-match "\\.bib\\'" file) | |
2437 | (setq file (concat file ".bib"))) | |
2438 | (file-name-absolute-p file)) | |
2439 | (push file file-list)) | |
2440 | (t | |
2441 | (let (fullfilename found) | |
2442 | (dolist (dir file-path) | |
2443 | (when (file-readable-p | |
2444 | (setq fullfilename (expand-file-name file dir))) | |
2445 | (push fullfilename file-list) | |
2446 | (setq found t))) | |
2447 | (unless found | |
2448 | (error "File %s not in paths defined via bibtex-file-path" | |
2449 | file)))))) | |
2450 | (dolist (file file-list) | |
2451 | (unless (file-readable-p file) | |
2452 | (error "BibTeX file %s not found" file))) | |
2453 | ;; expand dir-list | |
2454 | (dolist (dir dir-list) | |
2455 | (setq file-list | |
2456 | (append file-list (directory-files dir t "\\.bib\\'" t)))) | |
2457 | (delete-dups file-list) | |
2458 | (dolist (file file-list) | |
2459 | (when (file-readable-p file) | |
2460 | (push (find-file-noselect file) buffer-list) | |
2461 | (with-current-buffer (car buffer-list) | |
02c8032e | 2462 | (if (or force (not (listp bibtex-reference-keys))) |
e0dc0c55 SM |
2463 | (bibtex-parse-keys))))) |
2464 | (cond ((and current (not (memq (current-buffer) buffer-list))) | |
02c8032e SM |
2465 | (push (current-buffer) buffer-list) |
2466 | (if force (bibtex-parse-keys))) | |
e0dc0c55 SM |
2467 | ((and (not current) (memq (current-buffer) buffer-list)) |
2468 | (setq buffer-list (delq (current-buffer) buffer-list)))) | |
2469 | buffer-list)) | |
2470 | ||
7fbf4804 | 2471 | (defun bibtex-complete-internal (completions) |
a9d77f1f | 2472 | "Complete word fragment before point to longest prefix of COMPLETIONS. |
02c8032e | 2473 | COMPLETIONS is an alist of strings. If point is not after the part |
a9d77f1f | 2474 | of a word, all strings are listed. Return completion." |
5e860c24 | 2475 | ;; Return value is used by cleanup functions. |
50e4b39e | 2476 | (let* ((case-fold-search t) |
50e4b39e RS |
2477 | (beg (save-excursion |
2478 | (re-search-backward "[ \t{\"]") | |
2479 | (forward-char) | |
2480 | (point))) | |
7fbf4804 | 2481 | (end (point)) |
50e4b39e | 2482 | (part-of-word (buffer-substring-no-properties beg end)) |
7fbf4804 SM |
2483 | (completion (try-completion part-of-word completions))) |
2484 | (cond ((not completion) | |
2485 | (error "Can't find completion for `%s'" part-of-word)) | |
2486 | ((eq completion t) | |
2487 | part-of-word) | |
50e4b39e RS |
2488 | ((not (string= part-of-word completion)) |
2489 | (delete-region beg end) | |
2490 | (insert completion) | |
7fbf4804 | 2491 | completion) |
50e4b39e RS |
2492 | (t |
2493 | (message "Making completion list...") | |
7fbf4804 | 2494 | (with-output-to-temp-buffer "*Completions*" |
cdc61d35 | 2495 | (display-completion-list (all-completions part-of-word completions) |
f5fab556 | 2496 | part-of-word)) |
7fbf4804 SM |
2497 | (message "Making completion list...done") |
2498 | nil)))) | |
2499 | ||
02c8032e | 2500 | (defun bibtex-complete-string-cleanup (str compl) |
d715b065 | 2501 | "Cleanup after inserting string STR. |
02c8032e SM |
2502 | Remove enclosing field delimiters for STR. Display message with |
2503 | expansion of STR using expansion list COMPL." | |
ffc1e1db | 2504 | ;; point is at position inside field where completion was requested |
02c8032e | 2505 | (save-excursion |
cdc61d35 | 2506 | (let ((abbr (cdr (if (stringp str) |
02c8032e SM |
2507 | (assoc-string str compl t))))) |
2508 | (if abbr (message "Abbreviation for `%s'" abbr)) | |
cdc61d35 | 2509 | (bibtex-remove-delimiters)))) |
02c8032e SM |
2510 | |
2511 | (defun bibtex-complete-crossref-cleanup (key) | |
e0dc0c55 SM |
2512 | "Display summary message on entry KEY after completion of a crossref key. |
2513 | Use `bibtex-summary-function' to generate summary." | |
d528bff7 | 2514 | (save-excursion |
02c8032e SM |
2515 | (if (and (stringp key) |
2516 | (bibtex-find-entry key t)) | |
921a9483 | 2517 | (message "Ref: %s" (funcall bibtex-summary-function))))) |
02c8032e SM |
2518 | |
2519 | (defun bibtex-copy-summary-as-kill () | |
2520 | "Push summery of current BibTeX entry to kill ring. | |
e0dc0c55 | 2521 | Use `bibtex-summary-function' to generate summary." |
02c8032e | 2522 | (interactive) |
921a9483 SM |
2523 | (save-excursion |
2524 | (bibtex-beginning-of-entry) | |
2525 | (if (looking-at bibtex-entry-maybe-empty-head) | |
2526 | (kill-new (message "%s" (funcall bibtex-summary-function))) | |
2527 | (error "No entry found")))) | |
2528 | ||
2529 | (defun bibtex-summary () | |
2530 | "Return summary of current BibTeX entry. | |
e0dc0c55 SM |
2531 | Used as default value of `bibtex-summary-function'." |
2532 | ;; It would be neat to customize this function. How? | |
1fdecd0c RF |
2533 | (if (looking-at bibtex-entry-maybe-empty-head) |
2534 | (let* ((bibtex-autokey-name-case-convert 'identity) | |
2535 | (bibtex-autokey-name-length 'infty) | |
2536 | (bibtex-autokey-names 1) | |
2537 | (bibtex-autokey-names-stretch 0) | |
2538 | (bibtex-autokey-name-separator " ") | |
2539 | (bibtex-autokey-additional-names " etal") | |
2540 | (names (bibtex-autokey-get-names)) | |
2541 | (bibtex-autokey-year-length 4) | |
2542 | (year (bibtex-autokey-get-year)) | |
2543 | (bibtex-autokey-titlewords 5) | |
2544 | (bibtex-autokey-titlewords-stretch 2) | |
2545 | (bibtex-autokey-titleword-case-convert 'identity) | |
2546 | (bibtex-autokey-titleword-length 5) | |
2547 | (bibtex-autokey-titleword-separator " ") | |
2548 | (title (bibtex-autokey-get-title)) | |
2549 | (journal (bibtex-autokey-get-field | |
2550 | "journal" bibtex-autokey-transcriptions)) | |
2551 | (volume (bibtex-autokey-get-field "volume")) | |
2552 | (pages (bibtex-autokey-get-field "pages" '(("-.*\\'" . ""))))) | |
2553 | (mapconcat (lambda (arg) | |
2554 | (if (not (string= "" (cdr arg))) | |
2555 | (concat (car arg) (cdr arg)))) | |
2556 | `((" " . ,names) (" " . ,year) (": " . ,title) | |
2557 | (", " . ,journal) (" " . ,volume) (":" . ,pages)) | |
2558 | "")) | |
2559 | (error "Entry not found"))) | |
cb4ad359 | 2560 | |
50e4b39e | 2561 | (defun bibtex-pop (arg direction) |
02c8032e | 2562 | "Fill current field from the ARGth same field's text in DIRECTION. |
a9d77f1f | 2563 | Generic function used by `bibtex-pop-previous' and `bibtex-pop-next'." |
ffc1e1db RW |
2564 | ;; parse current field |
2565 | (let* ((bounds (bibtex-enclosing-field t)) | |
2566 | (start-old-field (bibtex-start-of-field bounds)) | |
2567 | (start-old-text (bibtex-start-of-text-in-field bounds)) | |
2568 | (end-old-text (bibtex-end-of-text-in-field bounds)) | |
2569 | (field-name (bibtex-name-in-field bounds t)) | |
2570 | failure) | |
2571 | (save-excursion | |
7fbf4804 SM |
2572 | ;; if executed several times in a row, start each search where |
2573 | ;; the last one was finished | |
ffc1e1db RW |
2574 | (cond ((eq last-command 'bibtex-pop) |
2575 | (goto-char (if (eq direction 'previous) | |
2576 | bibtex-pop-previous-search-point | |
2577 | bibtex-pop-next-search-point))) | |
2578 | ((eq direction 'previous) | |
2579 | (bibtex-beginning-of-entry)) | |
2580 | (t (bibtex-end-of-entry))) | |
2581 | ;; Search for arg'th previous/next similar field | |
2582 | (while (and (not failure) | |
2583 | (>= (setq arg (1- arg)) 0)) | |
2584 | ;; The search of BibTeX fields is not bounded by entry boundaries | |
2585 | (if (eq direction 'previous) | |
2586 | (if (setq bounds (bibtex-search-backward-field field-name)) | |
2587 | (goto-char (bibtex-start-of-field bounds)) | |
2588 | (setq failure t)) | |
2589 | (if (setq bounds (bibtex-search-forward-field field-name)) | |
2590 | (goto-char (bibtex-end-of-field bounds)) | |
2591 | (setq failure t)))) | |
2592 | (if failure | |
2593 | (error "No %s matching BibTeX field" | |
2594 | (if (eq direction 'previous) "previous" "next")) | |
2595 | ;; Found a matching field. Remember boundaries. | |
2596 | (let ((new-text (bibtex-text-in-field-bounds bounds)) | |
2597 | (nbeg (copy-marker (bibtex-start-of-field bounds))) | |
2598 | (nend (copy-marker (bibtex-end-of-field bounds)))) | |
2599 | (bibtex-flash-head "From: ") | |
7fbf4804 | 2600 | ;; Go back to where we started, delete old text, and pop new. |
ffc1e1db RW |
2601 | (goto-char end-old-text) |
2602 | (delete-region start-old-text end-old-text) | |
2603 | (if (= nbeg start-old-field) | |
2604 | (insert (bibtex-field-left-delimiter) | |
2605 | (bibtex-field-right-delimiter)) | |
2606 | (insert new-text)) | |
2607 | (setq bibtex-pop-previous-search-point (marker-position nbeg) | |
2608 | bibtex-pop-next-search-point (marker-position nend)))))) | |
2609 | (bibtex-find-text nil nil nil t) | |
50e4b39e RS |
2610 | (setq this-command 'bibtex-pop)) |
2611 | ||
e0dc0c55 SM |
2612 | (defun bibtex-beginning-of-field () |
2613 | "Move point backward to beginning of field. | |
2614 | This function uses a simple, fast algorithm assuming that the field | |
2615 | begins at the beginning of a line. We use this function for font-locking." | |
2616 | (let ((field-reg (concat "^[ \t]*" bibtex-field-name "[ \t]*="))) | |
2617 | (beginning-of-line) | |
2618 | (unless (looking-at field-reg) | |
2619 | (re-search-backward field-reg nil t)))) | |
2620 | ||
2621 | (defun bibtex-font-lock-url (bound) | |
02c8032e | 2622 | "Font-lock for URLs. BOUND limits the search." |
e0dc0c55 SM |
2623 | (let ((case-fold-search t) |
2624 | (pnt (point)) | |
2625 | field bounds start end found) | |
2626 | (bibtex-beginning-of-field) | |
2627 | (while (and (not found) | |
9cee7d84 | 2628 | (<= (point) bound) |
e0dc0c55 SM |
2629 | (prog1 (re-search-forward bibtex-font-lock-url-regexp bound t) |
2630 | (setq field (match-string-no-properties 1))) | |
2631 | (setq bounds (bibtex-parse-field-text)) | |
2632 | (progn | |
cdc61d35 | 2633 | (setq start (car bounds) end (nth 1 bounds)) |
e0dc0c55 SM |
2634 | ;; Always ignore field delimiters |
2635 | (if (memq (char-before end) '(?\} ?\")) | |
2636 | (setq end (1- end))) | |
2637 | (if (memq (char-after start) '(?\{ ?\")) | |
2638 | (setq start (1+ start))) | |
2639 | (>= bound start))) | |
2640 | (let ((lst bibtex-generate-url-list) url) | |
2641 | (goto-char start) | |
2642 | (while (and (not found) | |
63d516ce | 2643 | (setq url (car (pop lst)))) |
e0dc0c55 SM |
2644 | (setq found (and (bibtex-string= field (car url)) |
2645 | (re-search-forward (cdr url) end t) | |
63d516ce | 2646 | (>= (match-beginning 0) pnt))))) |
e0dc0c55 SM |
2647 | (goto-char end)) |
2648 | (if found (bibtex-button (match-beginning 0) (match-end 0) | |
2649 | 'bibtex-url (match-beginning 0))) | |
2650 | found)) | |
2651 | ||
2652 | (defun bibtex-font-lock-crossref (bound) | |
02c8032e | 2653 | "Font-lock for crossref fields. BOUND limits the search." |
e0dc0c55 SM |
2654 | (let ((case-fold-search t) |
2655 | (pnt (point)) | |
2656 | (crossref-reg (concat "^[ \t]*crossref[ \t]*=[ \t\n]*" | |
2657 | "\\(\"[^\"]*\"\\|{[^}]*}\\)[ \t\n]*[,})]")) | |
2658 | start end found) | |
2659 | (bibtex-beginning-of-field) | |
2660 | (while (and (not found) | |
2661 | (re-search-forward crossref-reg bound t)) | |
2662 | (setq start (1+ (match-beginning 1)) | |
2663 | end (1- (match-end 1)) | |
2664 | found (>= start pnt))) | |
2665 | (if found (bibtex-button start end 'bibtex-find-crossref | |
2666 | (buffer-substring-no-properties start end) | |
2667 | start t)) | |
2668 | found)) | |
2669 | ||
2670 | (defun bibtex-button-action (button) | |
2671 | "Call BUTTON's BibTeX function." | |
2672 | (apply (button-get button 'bibtex-function) | |
2673 | (button-get button 'bibtex-args))) | |
2674 | ||
2675 | (define-button-type 'bibtex-url | |
2676 | 'action 'bibtex-button-action | |
2677 | 'bibtex-function 'bibtex-url | |
2678 | 'help-echo (purecopy "mouse-2, RET: follow URL")) | |
2679 | ||
2680 | (define-button-type 'bibtex-find-crossref | |
2681 | 'action 'bibtex-button-action | |
2682 | 'bibtex-function 'bibtex-find-crossref | |
2683 | 'help-echo (purecopy "mouse-2, RET: follow crossref")) | |
2684 | ||
2685 | (defun bibtex-button (beg end type &rest args) | |
02c8032e | 2686 | "Make a BibTeX button from BEG to END of type TYPE in the current buffer." |
e0dc0c55 SM |
2687 | (make-text-button beg end 'type type 'bibtex-args args)) |
2688 | ||
50e4b39e RS |
2689 | \f |
2690 | ;; Interactive Functions: | |
2691 | ||
2692 | ;;;###autoload | |
2693 | (defun bibtex-mode () | |
2694 | "Major mode for editing BibTeX files. | |
2695 | ||
50e4b39e RS |
2696 | General information on working with BibTeX mode: |
2697 | ||
02c8032e SM |
2698 | Use commands such as \\[bibtex-Book] to get a template for a specific entry. |
2699 | Then fill in all desired fields using \\[bibtex-next-field] to jump from field | |
2700 | to field. After having filled in all desired fields in the entry, clean the | |
2701 | new entry with the command \\[bibtex-clean-entry]. | |
cb4ad359 | 2702 | |
d715b065 | 2703 | Some features of BibTeX mode are available only by setting the variable |
d10e87a2 SM |
2704 | `bibtex-maintain-sorted-entries' to non-nil. However, then BibTeX mode |
2705 | works only with buffers containing valid (syntactical correct) and sorted | |
2706 | entries. This is usually the case, if you have created a buffer completely | |
2707 | with BibTeX mode and finished every new entry with \\[bibtex-clean-entry]. | |
50e4b39e | 2708 | |
921a9483 | 2709 | For third party BibTeX files, call the command \\[bibtex-convert-alien] |
d715b065 | 2710 | to fully take advantage of all features of BibTeX mode. |
50e4b39e RS |
2711 | |
2712 | ||
2713 | Special information: | |
2714 | ||
d10e87a2 | 2715 | A command such as \\[bibtex-Book] outlines the fields for a BibTeX book entry. |
cb4ad359 | 2716 | |
02c8032e SM |
2717 | The names of optional fields start with the string OPT, and are thus ignored |
2718 | by BibTeX. The names of alternative fields from which only one is required | |
2719 | start with the string ALT. The OPT or ALT string may be removed from | |
2720 | the name of a field with \\[bibtex-remove-OPT-or-ALT]. | |
50e4b39e RS |
2721 | \\[bibtex-make-field] inserts a new field after the current one. |
2722 | \\[bibtex-kill-field] kills the current field entirely. | |
d715b065 | 2723 | \\[bibtex-yank] yanks the last recently killed field after the current field. |
50e4b39e | 2724 | \\[bibtex-remove-delimiters] removes the double-quotes or braces around the text of the current field. |
02c8032e SM |
2725 | \\[bibtex-empty-field] replaces the text of the current field with the default \"\" or {}. |
2726 | \\[bibtex-find-text] moves point to the end of the current field. | |
2727 | \\[bibtex-complete] completes word fragment before point according to context. | |
50e4b39e RS |
2728 | |
2729 | The command \\[bibtex-clean-entry] cleans the current entry, i.e. it removes OPT/ALT | |
02c8032e SM |
2730 | from the names of all non-empty optional or alternative fields, checks that |
2731 | no required fields are empty, and does some formatting dependent on the value | |
2732 | of `bibtex-entry-format'. Furthermore, it can automatically generate a key | |
2733 | for the BibTeX entry, see `bibtex-generate-autokey'. | |
f0cb6034 RS |
2734 | Note: some functions in BibTeX mode depend on entries being in a special |
2735 | format (all fields beginning on separate lines), so it is usually a bad | |
7fbf4804 | 2736 | idea to remove `realign' from `bibtex-entry-format'. |
cb4ad359 | 2737 | |
02c8032e SM |
2738 | BibTeX mode supports Imenu and hideshow minor mode (`hs-minor-mode'). |
2739 | ||
2740 | ---------------------------------------------------------- | |
2741 | Entry to BibTeX mode calls the value of `bibtex-mode-hook' | |
2742 | if that value is non-nil. | |
50e4b39e | 2743 | |
f0cb6034 | 2744 | \\{bibtex-mode-map}" |
cb4ad359 RS |
2745 | (interactive) |
2746 | (kill-all-local-variables) | |
2747 | (use-local-map bibtex-mode-map) | |
2748 | (setq major-mode 'bibtex-mode) | |
2749 | (setq mode-name "BibTeX") | |
2750 | (set-syntax-table bibtex-mode-syntax-table) | |
7fbf4804 | 2751 | (make-local-variable 'bibtex-buffer-last-parsed-tick) |
50e4b39e | 2752 | ;; Install stealthy parse function if not already installed |
7fbf4804 | 2753 | (unless bibtex-parse-idle-timer |
e0dc0c55 | 2754 | (setq bibtex-parse-idle-timer (run-with-idle-timer |
7fbf4804 SM |
2755 | bibtex-parse-keys-timeout t |
2756 | 'bibtex-parse-buffers-stealthily))) | |
2757 | (set (make-local-variable 'paragraph-start) "[ \f\n\t]*$") | |
2758 | (set (make-local-variable 'comment-start) bibtex-comment-start) | |
2759 | (set (make-local-variable 'comment-start-skip) | |
2760 | (concat (regexp-quote bibtex-comment-start) "\\>[ \t]*")) | |
2761 | (set (make-local-variable 'comment-column) 0) | |
a172852f | 2762 | (set (make-local-variable 'defun-prompt-regexp) "^[ \t]*@[[:alnum:]]+[ \t]*") |
7fbf4804 | 2763 | (set (make-local-variable 'outline-regexp) "[ \t]*@") |
d715b065 | 2764 | (set (make-local-variable 'fill-paragraph-function) 'bibtex-fill-field) |
7fbf4804 | 2765 | (set (make-local-variable 'fill-prefix) (make-string (+ bibtex-entry-offset |
e0dc0c55 | 2766 | bibtex-contline-indentation) |
ffe68348 | 2767 | ?\s)) |
7fbf4804 SM |
2768 | (set (make-local-variable 'font-lock-defaults) |
2769 | '(bibtex-font-lock-keywords | |
2770 | nil t ((?$ . "\"") | |
2771 | ;; Mathematical expressions should be fontified as strings | |
2772 | (?\" . ".") | |
2773 | ;; Quotes are field delimiters and quote-delimited | |
2774 | ;; entries should be fontified in the same way as | |
2775 | ;; brace-delimited ones | |
2776 | ) | |
2777 | nil | |
d715b065 | 2778 | (font-lock-syntactic-keywords . bibtex-font-lock-syntactic-keywords) |
e0dc0c55 | 2779 | (font-lock-extra-managed-props . (category)) |
d715b065 KG |
2780 | (font-lock-mark-block-function |
2781 | . (lambda () | |
d528bff7 | 2782 | (set-mark (bibtex-end-of-entry)) |
d715b065 | 2783 | (bibtex-beginning-of-entry))))) |
50e4b39e | 2784 | (setq imenu-generic-expression |
02c8032e SM |
2785 | (list (list nil bibtex-entry-head bibtex-key-in-head)) |
2786 | imenu-case-fold-search t) | |
7fbf4804 | 2787 | (make-local-variable 'choose-completion-string-functions) |
5e860c24 | 2788 | (make-local-variable 'completion-ignore-case) |
ab2d0cdb RS |
2789 | ;; XEmacs needs easy-menu-add, Emacs does not care |
2790 | (easy-menu-add bibtex-edit-menu) | |
2791 | (easy-menu-add bibtex-entry-menu) | |
a172852f | 2792 | (run-mode-hooks 'bibtex-mode-hook)) |
9ae11a89 | 2793 | |
8bf38a9b SM |
2794 | (defun bibtex-field-list (entry-type) |
2795 | "Return list of allowed fields for entry ENTRY-TYPE. | |
2796 | More specifically, the return value is a cons pair (REQUIRED . OPTIONAL), | |
2797 | where REQUIRED and OPTIONAL are lists of the required and optional field | |
02c8032e SM |
2798 | names for ENTRY-TYPE according to `bibtex-entry-field-alist', |
2799 | `bibtex-include-OPTkey', `bibtex-include-OPTcrossref', | |
2800 | and `bibtex-user-optional-fields'." | |
c2fb6415 | 2801 | (let ((e (assoc-string entry-type bibtex-entry-field-alist t)) |
8bf38a9b SM |
2802 | required optional) |
2803 | (unless e | |
ffc1e1db | 2804 | (error "Fields for BibTeX entry type %s not defined" entry-type)) |
8bf38a9b SM |
2805 | (if (and (member-ignore-case entry-type bibtex-include-OPTcrossref) |
2806 | (nth 2 e)) | |
2807 | (setq required (nth 0 (nth 2 e)) | |
2808 | optional (nth 1 (nth 2 e))) | |
2809 | (setq required (nth 0 (nth 1 e)) | |
2810 | optional (nth 1 (nth 1 e)))) | |
2811 | (if bibtex-include-OPTkey | |
2812 | (push (list "key" | |
2813 | "Used for reference key creation if author and editor fields are missing" | |
2814 | (if (or (stringp bibtex-include-OPTkey) | |
2815 | (fboundp bibtex-include-OPTkey)) | |
2816 | bibtex-include-OPTkey)) | |
2817 | optional)) | |
2818 | (if (member-ignore-case entry-type bibtex-include-OPTcrossref) | |
2819 | (push '("crossref" "Reference key of the cross-referenced entry") | |
2820 | optional)) | |
2821 | (setq optional (append optional bibtex-user-optional-fields)) | |
2822 | (cons required optional))) | |
2823 | ||
50e4b39e | 2824 | (defun bibtex-entry (entry-type) |
a9d77f1f | 2825 | "Insert a new BibTeX entry of type ENTRY-TYPE. |
02c8032e SM |
2826 | After insertion call the value of `bibtex-add-entry-hook' if that value |
2827 | is non-nil." | |
2828 | (interactive | |
2829 | (let ((completion-ignore-case t)) | |
2830 | (list (completing-read "Entry Type: " bibtex-entry-field-alist | |
2831 | nil t nil 'bibtex-entry-type-history)))) | |
8bf38a9b SM |
2832 | (let ((key (if bibtex-maintain-sorted-entries |
2833 | (bibtex-read-key (format "%s key: " entry-type)))) | |
2834 | (field-list (bibtex-field-list entry-type))) | |
7fbf4804 SM |
2835 | (unless (bibtex-prepare-new-entry (list key nil entry-type)) |
2836 | (error "Entry with key `%s' already exists" key)) | |
50e4b39e RS |
2837 | (indent-to-column bibtex-entry-offset) |
2838 | (insert "@" entry-type (bibtex-entry-left-delimiter)) | |
8bf38a9b | 2839 | (if key (insert key)) |
9ae11a89 | 2840 | (save-excursion |
a9d77f1f SM |
2841 | (mapc 'bibtex-make-field (car field-list)) |
2842 | (mapc 'bibtex-make-optional-field (cdr field-list)) | |
50e4b39e RS |
2843 | (if bibtex-comma-after-last-field |
2844 | (insert ",")) | |
2845 | (insert "\n") | |
2846 | (indent-to-column bibtex-entry-offset) | |
2847 | (insert (bibtex-entry-right-delimiter) "\n\n")) | |
0640d7bf | 2848 | (bibtex-next-field t) |
d715b065 KG |
2849 | (if (member-ignore-case entry-type bibtex-autofill-types) |
2850 | (bibtex-autofill-entry)) | |
9ae11a89 | 2851 | (run-hooks 'bibtex-add-entry-hook))) |
e5167999 | 2852 | |
8bf38a9b SM |
2853 | (defun bibtex-entry-update () |
2854 | "Update an existing BibTeX entry. | |
2855 | In the BibTeX entry at point, make new fields for those items that may occur | |
02c8032e | 2856 | according to `bibtex-field-list', but are not yet present." |
8bf38a9b SM |
2857 | (interactive) |
2858 | (save-excursion | |
2859 | (bibtex-beginning-of-entry) | |
2860 | ;; For inserting new fields, we use the fact that | |
ffc1e1db | 2861 | ;; `bibtex-parse-entry' moves point to the end of the last field. |
8bf38a9b SM |
2862 | (let* ((fields-alist (bibtex-parse-entry)) |
2863 | (field-list (bibtex-field-list | |
e0dc0c55 | 2864 | (cdr (assoc "=type=" fields-alist))))) |
ffc1e1db | 2865 | (skip-chars-backward " \t\n") |
8bf38a9b | 2866 | (dolist (field (car field-list)) |
d528bff7 | 2867 | (unless (assoc-string (car field) fields-alist t) |
8bf38a9b SM |
2868 | (bibtex-make-field field))) |
2869 | (dolist (field (cdr field-list)) | |
d528bff7 | 2870 | (unless (assoc-string (car field) fields-alist t) |
8bf38a9b SM |
2871 | (bibtex-make-optional-field field)))))) |
2872 | ||
cdc61d35 | 2873 | (defun bibtex-parse-entry (&optional content) |
d715b065 KG |
2874 | "Parse entry at point, return an alist. |
2875 | The alist elements have the form (FIELD . TEXT), where FIELD can also be | |
d528bff7 SM |
2876 | the special strings \"=type=\" and \"=key=\". For the FIELD \"=key=\" |
2877 | TEXT may be nil. Remove \"OPT\" and \"ALT\" from FIELD. | |
cdc61d35 SM |
2878 | Move point to the end of the last field. |
2879 | If optional arg CONTENT is non-nil extract content of text fields." | |
d715b065 | 2880 | (let (alist bounds) |
cb75af76 | 2881 | (when (looking-at bibtex-entry-maybe-empty-head) |
e0dc0c55 SM |
2882 | (push (cons "=type=" (bibtex-type-in-head)) alist) |
2883 | (push (cons "=key=" (bibtex-key-in-head)) alist) | |
cb75af76 | 2884 | (goto-char (match-end 0)) |
cdc61d35 | 2885 | (while (setq bounds (bibtex-parse-field)) |
d528bff7 | 2886 | (push (cons (bibtex-name-in-field bounds t) |
cdc61d35 | 2887 | (bibtex-text-in-field-bounds bounds content)) |
d715b065 KG |
2888 | alist) |
2889 | (goto-char (bibtex-end-of-field bounds)))) | |
2890 | alist)) | |
2891 | ||
2892 | (defun bibtex-autofill-entry () | |
02c8032e SM |
2893 | "Try to fill fields of current BibTeX entry based on neighboring entries. |
2894 | The current entry must have a key. Determine the neighboring entry | |
2895 | \(previouse or next\) whose key is more similar to the key of the current | |
2896 | entry. For all empty fields of the current entry insert the corresponding | |
2897 | field contents of the neighboring entry. Finally try to update the text | |
2898 | based on the difference between the keys of the neighboring and the current | |
2899 | entry (for example, the year parts of the keys)." | |
d715b065 KG |
2900 | (interactive) |
2901 | (undo-boundary) ;So you can easily undo it, if it didn't work right. | |
2902 | (bibtex-beginning-of-entry) | |
2903 | (when (looking-at bibtex-entry-head) | |
e0dc0c55 SM |
2904 | (let ((type (bibtex-type-in-head)) |
2905 | (key (bibtex-key-in-head)) | |
d715b065 KG |
2906 | (key-end (match-end bibtex-key-in-head)) |
2907 | (case-fold-search t) | |
ffc1e1db | 2908 | (bibtex-sort-ignore-string-entries t) |
d715b065 KG |
2909 | tmp other-key other bounds) |
2910 | ;; The fields we want to change start right after the key. | |
2911 | (goto-char key-end) | |
2912 | ;; First see whether to use the previous or the next entry | |
2913 | ;; for "inspiration". | |
2914 | (save-excursion | |
2915 | (goto-char (1- (match-beginning 0))) | |
2916 | (bibtex-beginning-of-entry) | |
02c8032e SM |
2917 | (if (and (looking-at bibtex-entry-head) |
2918 | (bibtex-string= type (bibtex-type-in-head)) | |
2919 | ;; In case we found ourselves :-( | |
2920 | (not (equal key (setq tmp (bibtex-key-in-head))))) | |
2921 | (setq other-key tmp | |
2922 | other (point)))) | |
d715b065 KG |
2923 | (save-excursion |
2924 | (bibtex-end-of-entry) | |
2925 | (bibtex-skip-to-valid-entry) | |
02c8032e SM |
2926 | (if (and (looking-at bibtex-entry-head) |
2927 | (bibtex-string= type (bibtex-type-in-head)) | |
2928 | ;; In case we found ourselves :-( | |
2929 | (not (equal key (setq tmp (bibtex-key-in-head)))) | |
2930 | (or (not other-key) | |
2931 | ;; Check which is the best match. | |
2932 | (< (length (try-completion "" (list key other-key))) | |
2933 | (length (try-completion "" (list key tmp)))))) | |
2934 | (setq other-key tmp | |
2935 | other (point)))) | |
d715b065 KG |
2936 | ;; Then fill the new entry's fields with the chosen other entry. |
2937 | (when other | |
2938 | (setq other (save-excursion (goto-char other) (bibtex-parse-entry))) | |
2939 | (setq key-end (point)) ;In case parse-entry changed the buffer. | |
cdc61d35 | 2940 | (while (setq bounds (bibtex-parse-field)) |
d528bff7 SM |
2941 | (let ((text (assoc-string (bibtex-name-in-field bounds t) |
2942 | other t))) | |
31df23f5 | 2943 | (if (not (and text |
cdc61d35 | 2944 | (equal "" (bibtex-text-in-field-bounds bounds t)))) |
d715b065 | 2945 | (goto-char (bibtex-end-of-field bounds)) |
31df23f5 | 2946 | (goto-char (bibtex-start-of-text-in-field bounds)) |
d715b065 KG |
2947 | (delete-region (point) (bibtex-end-of-text-in-field bounds)) |
2948 | (insert (cdr text))))) | |
2949 | ;; Finally try to update the text based on the difference between | |
2950 | ;; the two keys. | |
2951 | (let* ((prefix (try-completion "" (list key other-key))) | |
2952 | ;; If the keys are foo91 and foo92, don't replace 1 for 2 | |
2953 | ;; but 91 for 92 instead. | |
2954 | (_ (if (string-match "[0-9]+\\'" prefix) | |
2955 | (setq prefix (substring prefix 0 (match-beginning 0))))) | |
2956 | (suffix (substring key (length prefix))) | |
2957 | (other-suffix (substring other-key (length prefix)))) | |
2958 | (while (re-search-backward (regexp-quote other-suffix) key-end 'move) | |
2959 | (replace-match suffix))))))) | |
2960 | ||
ffc1e1db RW |
2961 | (defun bibtex-print-help-message (&optional field comma) |
2962 | "Print helpful information about current FIELD in current BibTeX entry. | |
2963 | Optional arg COMMA is as in `bibtex-enclosing-field'. It is t for | |
2964 | interactive calls." | |
2965 | (interactive (list nil t)) | |
2966 | (unless field (setq field (car (bibtex-find-text-internal nil nil comma)))) | |
2967 | (if (string-match "@" field) | |
2968 | (cond ((bibtex-string= field "@string") | |
2969 | (message "String definition")) | |
2970 | ((bibtex-string= field "@preamble") | |
2971 | (message "Preamble definition")) | |
2972 | (t (message "Entry key"))) | |
2973 | (let* ((case-fold-search t) | |
2974 | (type (save-excursion | |
2975 | (bibtex-beginning-of-entry) | |
2976 | (looking-at bibtex-entry-maybe-empty-head) | |
2977 | (bibtex-type-in-head))) | |
2978 | (field-list (bibtex-field-list type)) | |
2979 | (comment (assoc-string field (append (car field-list) | |
2980 | (cdr field-list)) t))) | |
2981 | (if comment (message "%s" (nth 1 comment)) | |
2982 | (message "No comment available"))))) | |
d30bfc76 | 2983 | |
02c8032e | 2984 | (defun bibtex-make-field (field &optional move interactive) |
d715b065 KG |
2985 | "Make a field named FIELD in current BibTeX entry. |
2986 | FIELD is either a string or a list of the form | |
2987 | \(FIELD-NAME COMMENT-STRING INIT ALTERNATIVE-FLAG) as in | |
a9d77f1f | 2988 | `bibtex-entry-field-alist'. |
02c8032e SM |
2989 | If MOVE is non-nil, move point past the present field before making |
2990 | the new field. If INTERACTIVE is non-nil, move point to the end of | |
2991 | the new field. Otherwise move point past the new field. | |
2992 | MOVE and INTERACTIVE are t when called interactively." | |
7fbf4804 | 2993 | (interactive |
8bf38a9b SM |
2994 | (list (let ((completion-ignore-case t) |
2995 | (field-list (bibtex-field-list | |
e0dc0c55 | 2996 | (save-excursion |
ffc1e1db RW |
2997 | (bibtex-beginning-of-entry) |
2998 | (looking-at bibtex-any-entry-maybe-empty-head) | |
e0dc0c55 | 2999 | (bibtex-type-in-head))))) |
8bf38a9b SM |
3000 | (completing-read "BibTeX field name: " |
3001 | (append (car field-list) (cdr field-list)) | |
3f3ed959 | 3002 | nil nil nil bibtex-field-history)) |
02c8032e | 3003 | t t)) |
d715b065 KG |
3004 | (unless (consp field) |
3005 | (setq field (list field))) | |
cdc61d35 SM |
3006 | (when move |
3007 | (bibtex-find-text) | |
3008 | (if (looking-at "[}\"]") | |
3009 | (forward-char))) | |
d715b065 KG |
3010 | (insert ",\n") |
3011 | (indent-to-column (+ bibtex-entry-offset bibtex-field-indentation)) | |
3012 | (if (nth 3 field) (insert "ALT")) | |
3013 | (insert (car field) " ") | |
3014 | (if bibtex-align-at-equal-sign | |
3015 | (indent-to-column (+ bibtex-entry-offset | |
3016 | (- bibtex-text-indentation 2)))) | |
3017 | (insert "= ") | |
d528bff7 SM |
3018 | (unless bibtex-align-at-equal-sign |
3019 | (indent-to-column (+ bibtex-entry-offset | |
3020 | bibtex-text-indentation))) | |
d715b065 | 3021 | (let ((init (nth 2 field))) |
02c8032e SM |
3022 | (insert (cond ((stringp init) init) |
3023 | ((fboundp init) (funcall init)) | |
3024 | (t (concat (bibtex-field-left-delimiter) | |
3025 | (bibtex-field-right-delimiter)))))) | |
2c10c0f0 | 3026 | (when interactive |
ffc1e1db RW |
3027 | ;; (bibtex-find-text nil nil bibtex-help-message) |
3028 | (if (memq (preceding-char) '(?} ?\")) (forward-char -1)) | |
3029 | (if bibtex-help-message (bibtex-print-help-message (car field))))) | |
9ae11a89 | 3030 | |
cb4ad359 | 3031 | (defun bibtex-beginning-of-entry () |
7fbf4804 | 3032 | "Move to beginning of BibTeX entry (beginning of line). |
cb4ad359 | 3033 | If inside an entry, move to the beginning of it, otherwise move to the |
a9d77f1f SM |
3034 | beginning of the previous entry. If point is ahead of all BibTeX entries |
3035 | move point to the beginning of buffer. Return the new location of point." | |
745bc783 | 3036 | (interactive) |
50e4b39e RS |
3037 | (skip-chars-forward " \t") |
3038 | (if (looking-at "@") | |
a9cb9b80 | 3039 | (forward-char)) |
d715b065 KG |
3040 | (re-search-backward "^[ \t]*@" nil 'move) |
3041 | (point)) | |
e5167999 | 3042 | |
cb4ad359 | 3043 | (defun bibtex-end-of-entry () |
7fbf4804 | 3044 | "Move to end of BibTeX entry (past the closing brace). |
cb4ad359 | 3045 | If inside an entry, move to the end of it, otherwise move to the end |
a9d77f1f | 3046 | of the previous entry. Do not move if ahead of first entry. |
d715b065 | 3047 | Return the new location of point." |
745bc783 | 3048 | (interactive) |
7fbf4804 | 3049 | (let ((case-fold-search t) |
ffc1e1db RW |
3050 | (pnt (point)) |
3051 | (_ (bibtex-beginning-of-entry)) | |
3052 | (bounds (bibtex-valid-entry t))) | |
3053 | (cond (bounds (goto-char (cdr bounds))) ; regular entry | |
3054 | ;; @String or @Preamble | |
3055 | ((setq bounds (or (bibtex-parse-string t) (bibtex-parse-preamble))) | |
7fbf4804 | 3056 | (goto-char (bibtex-end-of-string bounds))) |
ffc1e1db RW |
3057 | ((looking-at bibtex-any-valid-entry-type) |
3058 | ;; Parsing of entry failed | |
3059 | (error "Syntactically incorrect BibTeX entry starts here.")) | |
3060 | (t (if (interactive-p) (message "Not on a known BibTeX entry.")) | |
3061 | (goto-char pnt))) | |
3062 | (point))) | |
f0cb6034 | 3063 | |
d10e87a2 SM |
3064 | (defun bibtex-goto-line (arg) |
3065 | "Goto line ARG, counting from beginning of (narrowed) buffer." | |
3066 | ;; code adapted from `goto-line' | |
3067 | (goto-char (point-min)) | |
3068 | (if (eq selective-display t) | |
3069 | (re-search-forward "[\n\C-m]" nil 'end (1- arg)) | |
3070 | (forward-line (1- arg)))) | |
3071 | ||
3072 | (defun bibtex-reposition-window () | |
7fbf4804 | 3073 | "Make the current BibTeX entry visible. |
d10e87a2 SM |
3074 | If entry is smaller than `window-body-height', entry is centered in window. |
3075 | Otherwise display the beginning of entry." | |
3076 | (interactive) | |
3077 | (let ((pnt (point)) | |
3078 | (beg (line-number-at-pos (bibtex-beginning-of-entry))) | |
3079 | (end (line-number-at-pos (bibtex-end-of-entry)))) | |
3080 | (if (> (window-body-height) (- end beg)) | |
3081 | ;; entry fits in current window | |
3082 | (progn | |
3083 | (bibtex-goto-line (/ (+ 1 beg end) 2)) | |
3084 | (recenter) | |
3085 | (goto-char pnt)) | |
3086 | ;; entry too large for current window | |
3087 | (bibtex-goto-line beg) | |
3088 | (recenter 0) | |
3089 | (if (> (1+ (- (line-number-at-pos pnt) beg)) | |
3090 | (window-body-height)) | |
3091 | (bibtex-goto-line beg) | |
3092 | (goto-char pnt))))) | |
50e4b39e RS |
3093 | |
3094 | (defun bibtex-mark-entry () | |
3095 | "Put mark at beginning, point at end of current BibTeX entry." | |
3096 | (interactive) | |
3097 | (set-mark (bibtex-beginning-of-entry)) | |
3098 | (bibtex-end-of-entry)) | |
3099 | ||
3100 | (defun bibtex-count-entries (&optional count-string-entries) | |
3101 | "Count number of entries in current buffer or region. | |
d10e87a2 SM |
3102 | With prefix argument COUNT-STRING-ENTRIES count all entries, |
3103 | otherwise count all entries except @String entries. | |
3104 | If mark is active count entries in region, if not in whole buffer." | |
50e4b39e | 3105 | (interactive "P") |
7fbf4804 | 3106 | (let ((number 0) |
cdc61d35 SM |
3107 | (bibtex-sort-ignore-string-entries (not count-string-entries))) |
3108 | (save-restriction | |
3109 | (if mark-active (narrow-to-region (region-beginning) (region-end))) | |
3110 | (bibtex-map-entries (lambda (key beg end) (setq number (1+ number))))) | |
7fbf4804 | 3111 | (message "%s contains %d entries." |
e0dc0c55 | 3112 | (if mark-active "Region" "Buffer") |
d715b065 | 3113 | number))) |
50e4b39e | 3114 | |
cb4ad359 | 3115 | (defun bibtex-ispell-entry () |
d10e87a2 | 3116 | "Check BibTeX entry for spelling errors." |
745bc783 | 3117 | (interactive) |
d715b065 KG |
3118 | (ispell-region (save-excursion (bibtex-beginning-of-entry)) |
3119 | (save-excursion (bibtex-end-of-entry)))) | |
745bc783 | 3120 | |
cb4ad359 | 3121 | (defun bibtex-ispell-abstract () |
d10e87a2 | 3122 | "Check abstract of BibTeX entry for spelling errors." |
745bc783 | 3123 | (interactive) |
d715b065 KG |
3124 | (let ((bounds (save-excursion |
3125 | (bibtex-beginning-of-entry) | |
ffc1e1db | 3126 | (bibtex-search-forward-field "abstract" t)))) |
7fbf4804 SM |
3127 | (if bounds |
3128 | (ispell-region (bibtex-start-of-text-in-field bounds) | |
3129 | (bibtex-end-of-text-in-field bounds)) | |
3130 | (error "No abstract in entry")))) | |
745bc783 | 3131 | |
cb4ad359 RS |
3132 | (defun bibtex-narrow-to-entry () |
3133 | "Narrow buffer to current BibTeX entry." | |
745bc783 | 3134 | (interactive) |
cb4ad359 | 3135 | (save-excursion |
7fbf4804 SM |
3136 | (widen) |
3137 | (narrow-to-region (bibtex-beginning-of-entry) | |
3138 | (bibtex-end-of-entry)))) | |
3139 | ||
3140 | (defun bibtex-entry-index () | |
02c8032e | 3141 | "Return index of BibTeX entry head at or past position of point. |
7fbf4804 | 3142 | The index is a list (KEY CROSSREF-KEY ENTRY-NAME) that is used for sorting |
02c8032e SM |
3143 | the entries of the BibTeX buffer. CROSSREF-KEY is nil unless the value |
3144 | of `bibtex-maintain-sorted-entries' is `crossref'. Move point to the end | |
3145 | of the head of the entry found. Return nil if no entry found." | |
7fbf4804 SM |
3146 | (let ((case-fold-search t)) |
3147 | (if (re-search-forward bibtex-entry-maybe-empty-head nil t) | |
3148 | (let ((key (bibtex-key-in-head)) | |
d715b065 KG |
3149 | ;; all entry names should be downcase (for ease of comparison) |
3150 | (entry-name (downcase (bibtex-type-in-head)))) | |
7fbf4804 | 3151 | ;; Don't search CROSSREF-KEY if we don't need it. |
02c8032e SM |
3152 | (if (eq bibtex-maintain-sorted-entries 'crossref) |
3153 | (let ((bounds (bibtex-search-forward-field | |
ffc1e1db | 3154 | "\\(OPT\\)?crossref" t))) |
02c8032e SM |
3155 | (list key |
3156 | (if bounds (bibtex-text-in-field-bounds bounds t)) | |
3157 | entry-name)) | |
8bf38a9b | 3158 | (list key nil entry-name)))))) |
7fbf4804 SM |
3159 | |
3160 | (defun bibtex-lessp (index1 index2) | |
3161 | "Predicate for sorting BibTeX entries with indices INDEX1 and INDEX2. | |
3162 | Each index is a list (KEY CROSSREF-KEY ENTRY-NAME). | |
8bf38a9b SM |
3163 | The predicate depends on the variable `bibtex-maintain-sorted-entries'. |
3164 | If its value is nil use plain sorting." | |
7fbf4804 SM |
3165 | (cond ((not index1) (not index2)) ; indices can be nil |
3166 | ((not index2) nil) | |
02c8032e | 3167 | ((eq bibtex-maintain-sorted-entries 'crossref) |
7fbf4804 SM |
3168 | (if (nth 1 index1) |
3169 | (if (nth 1 index2) | |
3170 | (or (string-lessp (nth 1 index1) (nth 1 index2)) | |
3171 | (and (string-equal (nth 1 index1) (nth 1 index2)) | |
3172 | (string-lessp (nth 0 index1) (nth 0 index2)))) | |
3173 | (not (string-lessp (nth 0 index2) (nth 1 index1)))) | |
3174 | (if (nth 1 index2) | |
3175 | (string-lessp (nth 0 index1) (nth 1 index2)) | |
3176 | (string-lessp (nth 0 index1) (nth 0 index2))))) | |
02c8032e | 3177 | ((eq bibtex-maintain-sorted-entries 'entry-class) |
d715b065 KG |
3178 | (let ((n1 (cdr (or (assoc (nth 2 index1) bibtex-sort-entry-class-alist) |
3179 | (assoc 'catch-all bibtex-sort-entry-class-alist) | |
3180 | '(nil . 1000)))) ; if there is nothing else | |
3181 | (n2 (cdr (or (assoc (nth 2 index2) bibtex-sort-entry-class-alist) | |
3182 | (assoc 'catch-all bibtex-sort-entry-class-alist) | |
3183 | '(nil . 1000))))) ; if there is nothing else | |
7fbf4804 SM |
3184 | (or (< n1 n2) |
3185 | (and (= n1 n2) | |
3186 | (string-lessp (car index1) (car index2)))))) | |
02c8032e | 3187 | (t ; (eq bibtex-maintain-sorted-entries 'plain) |
7fbf4804 | 3188 | (string-lessp (car index1) (car index2))))) |
745bc783 | 3189 | |
50e4b39e RS |
3190 | (defun bibtex-sort-buffer () |
3191 | "Sort BibTeX buffer alphabetically by key. | |
7fbf4804 | 3192 | The predicate for sorting is defined via `bibtex-maintain-sorted-entries'. |
a9d77f1f SM |
3193 | If its value is nil use plain sorting. Text outside of BibTeX entries is not |
3194 | affected. If `bibtex-sort-ignore-string-entries' is non-nil, @String entries | |
d10e87a2 | 3195 | are ignored." |
745bc783 | 3196 | (interactive) |
cdc61d35 | 3197 | (bibtex-beginning-of-first-entry) ;; needed by `sort-subr' |
7fbf4804 SM |
3198 | (sort-subr nil |
3199 | 'bibtex-skip-to-valid-entry ; NEXTREC function | |
3200 | 'bibtex-end-of-entry ; ENDREC function | |
3201 | 'bibtex-entry-index ; STARTKEY function | |
3202 | nil ; ENDKEY function | |
cdc61d35 | 3203 | 'bibtex-lessp)) ; PREDICATE |
7fbf4804 | 3204 | |
e0dc0c55 | 3205 | (defun bibtex-find-crossref (crossref-key &optional pnt split) |
7fbf4804 | 3206 | "Move point to the beginning of BibTeX entry CROSSREF-KEY. |
02c8032e SM |
3207 | If `bibtex-files' is non-nil, search all these files. |
3208 | Otherwise the search is limited to the current buffer. | |
3209 | Return position of entry if CROSSREF-KEY is found or nil otherwise. | |
3210 | If CROSSREF-KEY is in the same buffer like current entry but before it | |
3211 | an error is signaled. Optional arg PNT is the position of the referencing | |
3212 | entry. It defaults to position of point. If optional arg SPLIT is non-nil, | |
3213 | split window so that both the referencing and the crossrefed entry are | |
3214 | displayed. | |
d715b065 | 3215 | If called interactively, CROSSREF-KEY defaults to crossref key of current |
e0dc0c55 | 3216 | entry and SPLIT is t." |
d715b065 KG |
3217 | (interactive |
3218 | (let ((crossref-key | |
3219 | (save-excursion | |
3220 | (bibtex-beginning-of-entry) | |
ffc1e1db | 3221 | (let ((bounds (bibtex-search-forward-field "crossref" t))) |
d715b065 KG |
3222 | (if bounds |
3223 | (bibtex-text-in-field-bounds bounds t)))))) | |
02c8032e SM |
3224 | (list (bibtex-read-key "Find crossref key: " crossref-key t) |
3225 | (point) t))) | |
3226 | (let (buffer pos eqb) | |
3227 | (save-excursion | |
3228 | (setq pos (bibtex-find-entry crossref-key t) | |
3229 | buffer (current-buffer))) | |
3230 | (setq eqb (eq buffer (current-buffer))) | |
e0dc0c55 | 3231 | (cond ((not pos) |
02c8032e SM |
3232 | (if split (message "Crossref key `%s' not found" crossref-key))) |
3233 | (split ; called (quasi) interactively | |
3234 | (unless pnt (setq pnt (point))) | |
e0dc0c55 | 3235 | (goto-char pnt) |
02c8032e SM |
3236 | (if eqb (select-window (split-window)) |
3237 | (pop-to-buffer buffer)) | |
e0dc0c55 | 3238 | (goto-char pos) |
d10e87a2 | 3239 | (bibtex-reposition-window) |
e0dc0c55 | 3240 | (beginning-of-line) |
02c8032e | 3241 | (if (and eqb (> pnt pos)) |
31df23f5 | 3242 | (error "The referencing entry must precede the crossrefed entry!"))) |
02c8032e SM |
3243 | ;; `bibtex-find-crossref' is called noninteractively during |
3244 | ;; clean-up of an entry. Then it is not possible to check | |
3245 | ;; whether the current entry and the crossrefed entry have | |
3246 | ;; the correct sorting order. | |
3247 | (eqb (goto-char pos)) | |
3248 | (t (set-buffer buffer) (goto-char pos))) | |
e0dc0c55 | 3249 | pos)) |
7fbf4804 | 3250 | |
02c8032e | 3251 | (defun bibtex-find-entry (key &optional global start display) |
7fbf4804 | 3252 | "Move point to the beginning of BibTeX entry named KEY. |
d528bff7 | 3253 | Return position of entry if KEY is found or nil if not found. |
02c8032e SM |
3254 | With prefix arg GLOBAL non-nil, search KEY in `bibtex-files'. |
3255 | Otherwise the search is limited to the current buffer. | |
d528bff7 SM |
3256 | Optional arg START is buffer position where the search starts. |
3257 | If it is nil, start search at beginning of buffer. | |
02c8032e SM |
3258 | If DISPLAY is non-nil, display the buffer containing KEY. |
3259 | Otherwise, use `set-buffer'. DISPLAY is t when called interactively." | |
3260 | (interactive (list (bibtex-read-key "Find key: " nil current-prefix-arg) | |
3261 | current-prefix-arg nil t)) | |
3262 | (if (and global bibtex-files) | |
3263 | (let ((buffer-list (bibtex-files-expand t)) | |
3264 | buffer found) | |
3265 | (while (and (not found) | |
3266 | (setq buffer (pop buffer-list))) | |
3267 | (with-current-buffer buffer | |
3268 | (if (cdr (assoc-string key bibtex-reference-keys)) | |
3269 | ;; `bibtex-find-entry' moves point if key found | |
3270 | (setq found (bibtex-find-entry key))))) | |
3271 | (cond ((and found display) | |
3272 | (let ((same-window-buffer-names | |
3273 | (cons (buffer-name buffer) same-window-buffer-names))) | |
d10e87a2 SM |
3274 | (pop-to-buffer buffer) |
3275 | (bibtex-reposition-window))) | |
02c8032e | 3276 | (found (set-buffer buffer)) |
d10e87a2 | 3277 | (display (message "Key `%s' not found" key))) |
02c8032e SM |
3278 | found) |
3279 | ||
3280 | (let* (case-fold-search | |
3281 | (pnt (save-excursion | |
3282 | (goto-char (or start (point-min))) | |
3283 | (if (re-search-forward (concat "^[ \t]*\\(" | |
3284 | bibtex-entry-type | |
3285 | "\\)[ \t]*[({][ \t\n]*\\(" | |
3286 | (regexp-quote key) | |
3287 | "\\)[ \t\n]*[,=]") | |
3288 | nil t) | |
3289 | (match-beginning 0))))) | |
3290 | (cond (pnt | |
d10e87a2 SM |
3291 | (goto-char pnt) |
3292 | (if display (bibtex-reposition-window))) | |
3293 | (display (message "Key `%s' not found" key))) | |
3294 | pnt))) | |
7fbf4804 SM |
3295 | |
3296 | (defun bibtex-prepare-new-entry (index) | |
3297 | "Prepare a new BibTeX entry with index INDEX. | |
3298 | INDEX is a list (KEY CROSSREF-KEY ENTRY-NAME). | |
3299 | Move point where the entry KEY should be placed. | |
3300 | If `bibtex-maintain-sorted-entries' is non-nil, perform a binary | |
d10e87a2 | 3301 | search to look for place for KEY. This requires that buffer is sorted, |
921a9483 | 3302 | see `bibtex-validate'. |
7fbf4804 SM |
3303 | Return t if preparation was successful or nil if entry KEY already exists." |
3304 | (let ((key (nth 0 index)) | |
3305 | key-exist) | |
3306 | (cond ((or (null key) | |
3307 | (and (stringp key) | |
3308 | (string-equal key "")) | |
3309 | (and (not (setq key-exist (bibtex-find-entry key))) | |
3310 | (not bibtex-maintain-sorted-entries))) | |
3311 | (bibtex-move-outside-of-entry)) | |
3312 | ;; if key-exist is non-nil due to the previous cond clause | |
3313 | ;; then point will be at beginning of entry named key. | |
3314 | (key-exist) | |
3315 | (t ; bibtex-maintain-sorted-entries is non-nil | |
3316 | (let* ((case-fold-search t) | |
cdc61d35 SM |
3317 | (left (save-excursion (bibtex-beginning-of-first-entry))) |
3318 | (bounds (save-excursion (goto-char (point-max)) | |
3319 | (bibtex-skip-to-valid-entry t))) | |
3320 | (right (if bounds (cdr bounds) (point-min))) | |
7fbf4804 SM |
3321 | (found (if (>= left right) left)) |
3322 | actual-index new) | |
3323 | (save-excursion | |
3324 | ;; Binary search | |
3325 | (while (not found) | |
3326 | (goto-char (/ (+ left right) 2)) | |
3327 | (bibtex-skip-to-valid-entry t) | |
3328 | (setq actual-index (bibtex-entry-index)) | |
3329 | (cond ((bibtex-lessp index actual-index) | |
3330 | (setq new (bibtex-beginning-of-entry)) | |
3331 | (if (equal right new) | |
3332 | (setq found right) | |
3333 | (setq right new))) | |
3334 | (t | |
3335 | (bibtex-end-of-entry) | |
3336 | (bibtex-skip-to-valid-entry) | |
3337 | (setq new (point)) | |
3338 | (if (equal left new) | |
3339 | (setq found right) | |
3340 | (setq left new)))))) | |
3341 | (goto-char found) | |
3342 | (bibtex-beginning-of-entry) | |
3343 | (setq actual-index (save-excursion (bibtex-entry-index))) | |
3344 | (when (or (not actual-index) | |
3345 | (bibtex-lessp actual-index index)) | |
3346 | ;; buffer contains no valid entries or | |
3347 | ;; greater than last entry --> append | |
3348 | (bibtex-end-of-entry) | |
d528bff7 | 3349 | (unless (bobp) (newline (forward-line 2))) |
7fbf4804 SM |
3350 | (beginning-of-line))))) |
3351 | (unless key-exist t))) | |
cb4ad359 | 3352 | |
50e4b39e RS |
3353 | (defun bibtex-validate (&optional test-thoroughly) |
3354 | "Validate if buffer or region is syntactically correct. | |
e0dc0c55 SM |
3355 | Check also for duplicate keys and correct sort order provided |
3356 | `bibtex-maintain-sorted-entries' is non-nil. | |
3357 | With optional argument TEST-THOROUGHLY non-nil check also for | |
3358 | the absence of required fields and for questionable month fields. | |
7fbf4804 | 3359 | If mark is active, validate current region, if not the whole buffer. |
e0dc0c55 SM |
3360 | Only check known entry types, so you can put comments outside of entries. |
3361 | Return t if test was successful, nil otherwise." | |
31bc4210 | 3362 | (interactive "P") |
7fbf4804 SM |
3363 | (let* ((case-fold-search t) |
3364 | error-list syntax-error) | |
3365 | (save-excursion | |
3366 | (save-restriction | |
ffc1e1db | 3367 | (if mark-active (narrow-to-region (region-beginning) (region-end))) |
7fbf4804 | 3368 | |
ffc1e1db | 3369 | ;; Check syntactical structure of entries |
7fbf4804 SM |
3370 | (goto-char (point-min)) |
3371 | (bibtex-progress-message "Checking syntactical structure") | |
ffc1e1db RW |
3372 | (let (bounds end) |
3373 | (while (setq end (re-search-forward "^[ \t]*@" nil t)) | |
7fbf4804 | 3374 | (bibtex-progress-message) |
ffc1e1db RW |
3375 | (goto-char (match-beginning 0)) |
3376 | (cond ((setq bounds (bibtex-valid-entry)) | |
3377 | (goto-char (cdr bounds))) | |
3378 | ((setq bounds (or (bibtex-parse-string) | |
3379 | (bibtex-parse-preamble))) | |
3380 | (goto-char (bibtex-end-of-string bounds))) | |
3381 | ((looking-at bibtex-any-valid-entry-type) | |
3382 | (push (cons (bibtex-current-line) | |
3383 | "Syntax error (check esp. commas, braces, and quotes)") | |
3384 | error-list) | |
3385 | (goto-char (match-end 0))) | |
3386 | (t (goto-char end))))) | |
7fbf4804 SM |
3387 | (bibtex-progress-message 'done) |
3388 | ||
3389 | (if error-list | |
ffc1e1db | 3390 | ;; Continue only if there were no syntax errors. |
7fbf4804 | 3391 | (setq syntax-error t) |
e0dc0c55 | 3392 | |
ffc1e1db | 3393 | ;; Check for duplicate keys and correct sort order |
e0dc0c55 SM |
3394 | (let (previous current key-list) |
3395 | (bibtex-progress-message "Checking for duplicate keys") | |
3396 | (bibtex-map-entries | |
3397 | (lambda (key beg end) | |
3398 | (bibtex-progress-message) | |
e0dc0c55 SM |
3399 | (setq current (bibtex-entry-index)) |
3400 | (cond ((not previous)) | |
3401 | ((member key key-list) | |
3402 | (push (cons (bibtex-current-line) | |
3403 | (format "Duplicate key `%s'" key)) | |
3404 | error-list)) | |
3405 | ((and bibtex-maintain-sorted-entries | |
3406 | (not (bibtex-lessp previous current))) | |
3407 | (push (cons (bibtex-current-line) | |
3408 | "Entries out of order") | |
3409 | error-list))) | |
3410 | (push key key-list) | |
3411 | (setq previous current))) | |
3412 | (bibtex-progress-message 'done)) | |
3413 | ||
3414 | ;; Check for duplicate keys in `bibtex-files'. | |
3415 | (bibtex-parse-keys) | |
02c8032e SM |
3416 | ;; We don't want to be fooled by outdated `bibtex-reference-keys'. |
3417 | (dolist (buffer (bibtex-files-expand nil t)) | |
3418 | (dolist (key (with-current-buffer buffer bibtex-reference-keys)) | |
e0dc0c55 SM |
3419 | (when (and (cdr key) |
3420 | (cdr (assoc-string (car key) bibtex-reference-keys))) | |
3421 | (bibtex-find-entry (car key)) | |
3422 | (push (cons (bibtex-current-line) | |
3423 | (format "Duplicate key `%s' in %s" (car key) | |
3424 | (abbreviate-file-name (buffer-file-name buffer)))) | |
3425 | error-list)))) | |
7fbf4804 SM |
3426 | |
3427 | (when test-thoroughly | |
7fbf4804 SM |
3428 | (bibtex-progress-message |
3429 | "Checking required fields and month fields") | |
d528bff7 | 3430 | (let ((bibtex-sort-ignore-string-entries t)) |
55fe21fc | 3431 | (bibtex-map-entries |
d715b065 | 3432 | (lambda (key beg end) |
50e4b39e | 3433 | (bibtex-progress-message) |
ffc1e1db RW |
3434 | (let* ((entry-list (assoc-string (bibtex-type-in-head) |
3435 | bibtex-entry-field-alist t)) | |
7fbf4804 SM |
3436 | (req (copy-sequence (elt (elt entry-list 1) 0))) |
3437 | (creq (copy-sequence (elt (elt entry-list 2) 0))) | |
e0dc0c55 | 3438 | crossref-there bounds alt-there field) |
ffc1e1db RW |
3439 | (bibtex-beginning-first-field beg) |
3440 | (while (setq bounds (bibtex-parse-field)) | |
d528bff7 SM |
3441 | (let ((field-name (bibtex-name-in-field bounds))) |
3442 | (if (and (bibtex-string= field-name "month") | |
e0dc0c55 SM |
3443 | ;; Check only abbreviated month fields. |
3444 | (let ((month (bibtex-text-in-field-bounds bounds))) | |
3445 | (not (or (string-match "\\`[\"{].+[\"}]\\'" month) | |
3446 | (assoc-string | |
3447 | month | |
3448 | bibtex-predefined-month-strings t))))) | |
3449 | (push (cons (bibtex-current-line) | |
7fbf4804 SM |
3450 | "Questionable month field") |
3451 | error-list)) | |
ffc1e1db RW |
3452 | (setq field (assoc-string field-name req t) |
3453 | req (delete field req) | |
3454 | creq (delete (assoc-string field-name creq t) creq)) | |
e0dc0c55 | 3455 | (if (nth 3 field) |
ffc1e1db RW |
3456 | (if alt-there |
3457 | (push (cons (bibtex-current-line) | |
3458 | "More than one non-empty alternative") | |
3459 | error-list) | |
e0dc0c55 | 3460 | (setq alt-there t))) |
d528bff7 | 3461 | (if (bibtex-string= field-name "crossref") |
ffc1e1db RW |
3462 | (setq crossref-there t))) |
3463 | (goto-char (bibtex-end-of-field bounds))) | |
3464 | (if crossref-there (setq req creq)) | |
e0dc0c55 SM |
3465 | (let (alt) |
3466 | (dolist (field req) | |
3467 | (if (nth 3 field) | |
3468 | (push (car field) alt) | |
3469 | (push (cons (save-excursion (goto-char beg) | |
3470 | (bibtex-current-line)) | |
3471 | (format "Required field `%s' missing" | |
3472 | (car field))) | |
3473 | error-list))) | |
3474 | ;; The following fails if there are more than two | |
3475 | ;; alternatives in a BibTeX entry, which isn't | |
3476 | ;; the case momentarily. | |
3477 | (if (cdr alt) | |
3478 | (push (cons (save-excursion (goto-char beg) | |
3479 | (bibtex-current-line)) | |
3480 | (format "Alternative fields `%s'/`%s' missing" | |
3481 | (car alt) (cadr alt))) | |
3482 | error-list))))))) | |
7fbf4804 | 3483 | (bibtex-progress-message 'done))))) |
e0dc0c55 | 3484 | |
50e4b39e | 3485 | (if error-list |
e0dc0c55 SM |
3486 | (let ((file (file-name-nondirectory (buffer-file-name))) |
3487 | (dir default-directory) | |
3488 | (err-buf "*BibTeX validation errors*")) | |
3489 | (setq error-list (sort error-list 'car-less-than-car)) | |
3490 | (with-current-buffer (get-buffer-create err-buf) | |
3491 | (setq default-directory dir) | |
3492 | (unless (eq major-mode 'compilation-mode) (compilation-mode)) | |
3493 | (toggle-read-only -1) | |
3494 | (delete-region (point-min) (point-max)) | |
3495 | (insert "BibTeX mode command `bibtex-validate'\n" | |
3496 | (if syntax-error | |
3497 | "Maybe undetected errors due to syntax errors. Correct and validate again.\n" | |
3498 | "\n")) | |
3499 | (dolist (err error-list) | |
3500 | (insert (format "%s:%d: %s\n" file (car err) (cdr err)))) | |
3501 | (set-buffer-modified-p nil) | |
3502 | (toggle-read-only 1) | |
3503 | (goto-line 3)) ; first error message | |
3504 | (display-buffer err-buf) | |
ffc1e1db | 3505 | nil) ; return `nil' (i.e., buffer is invalid) |
e0dc0c55 SM |
3506 | (message "%s is syntactically correct" |
3507 | (if mark-active "Region" "Buffer")) | |
ffc1e1db | 3508 | t))) ; return `t' (i.e., buffer is valid) |
e0dc0c55 SM |
3509 | |
3510 | (defun bibtex-validate-globally (&optional strings) | |
3511 | "Check for duplicate keys in `bibtex-files'. | |
02c8032e | 3512 | With optional prefix arg STRINGS, check for duplicate strings, too. |
e0dc0c55 SM |
3513 | Return t if test was successful, nil otherwise." |
3514 | (interactive "P") | |
3515 | (let ((buffer-list (bibtex-files-expand t)) | |
3516 | buffer-key-list current-buf current-keys error-list) | |
3517 | ;; Check for duplicate keys within BibTeX buffer | |
3518 | (dolist (buffer buffer-list) | |
3519 | (save-excursion | |
3520 | (set-buffer buffer) | |
3521 | (let (entry-type key key-list) | |
50e4b39e | 3522 | (goto-char (point-min)) |
e0dc0c55 SM |
3523 | (while (re-search-forward bibtex-entry-head nil t) |
3524 | (setq entry-type (bibtex-type-in-head) | |
3525 | key (bibtex-key-in-head)) | |
3526 | (if (or (and strings (bibtex-string= entry-type "string")) | |
3527 | (assoc-string entry-type bibtex-entry-field-alist t)) | |
3528 | (if (member key key-list) | |
3529 | (push (format "%s:%d: Duplicate key `%s'\n" | |
3530 | (buffer-file-name) | |
3531 | (bibtex-current-line) key) | |
3532 | error-list) | |
3533 | (push key key-list)))) | |
3534 | (push (cons buffer key-list) buffer-key-list)))) | |
3535 | ||
3536 | ;; Check for duplicate keys among BibTeX buffers | |
3537 | (while (setq current-buf (pop buffer-list)) | |
3538 | (setq current-keys (cdr (assq current-buf buffer-key-list))) | |
3539 | (with-current-buffer current-buf | |
3540 | (dolist (buffer buffer-list) | |
3541 | (dolist (key (cdr (assq buffer buffer-key-list))) | |
3542 | (when (assoc-string key current-keys) | |
3543 | (bibtex-find-entry key) | |
24ee740d | 3544 | (push (format "%s:%d: Duplicate key `%s' in %s\n" |
e0dc0c55 SM |
3545 | (buffer-file-name) (bibtex-current-line) key |
3546 | (abbreviate-file-name (buffer-file-name buffer))) | |
3547 | error-list)))))) | |
3548 | ||
3549 | ;; Process error list | |
3550 | (if error-list | |
3551 | (let ((err-buf "*BibTeX validation errors*")) | |
3552 | (with-current-buffer (get-buffer-create err-buf) | |
3553 | (unless (eq major-mode 'compilation-mode) (compilation-mode)) | |
3554 | (toggle-read-only -1) | |
3555 | (delete-region (point-min) (point-max)) | |
3556 | (insert "BibTeX mode command `bibtex-validate-globally'\n\n") | |
3557 | (dolist (err (sort error-list 'string-lessp)) (insert err)) | |
3558 | (set-buffer-modified-p nil) | |
3559 | (toggle-read-only 1) | |
3560 | (goto-line 3)) ; first error message | |
3561 | (display-buffer err-buf) | |
ffc1e1db | 3562 | nil) ; return `nil' (i.e., buffer is invalid) |
e0dc0c55 | 3563 | (message "No duplicate keys.") |
ffc1e1db RW |
3564 | t))) ; return `t' (i.e., buffer is valid) |
3565 | ||
3566 | (defun bibtex-next-field (begin &optional comma) | |
3567 | "Move point to end of text of next BibTeX field or entry head. | |
3568 | With prefix BEGIN non-nil, move point to its beginning. Optional arg COMMA | |
3569 | is as in `bibtex-enclosing-field'. It is t for interactive calls." | |
3570 | (interactive (list current-prefix-arg t)) | |
3571 | (let ((bounds (bibtex-find-text-internal t nil comma)) | |
3572 | end-of-entry) | |
3573 | (if (not bounds) | |
3574 | (setq end-of-entry t) | |
3575 | (goto-char (nth 3 bounds)) | |
3576 | (if (assoc-string (car bounds) '("@String" "@Preamble") t) | |
3577 | (setq end-of-entry t) | |
3578 | ;; BibTeX key or field | |
3579 | (if (looking-at ",[ \t\n]*") (goto-char (match-end 0))) | |
3580 | ;; end of entry | |
3581 | (if (looking-at "[)}][ \t\n]*") (setq end-of-entry t)))) | |
3582 | (if (and end-of-entry | |
3583 | (re-search-forward bibtex-any-entry-maybe-empty-head nil t)) | |
3584 | (goto-char (match-beginning 0))) | |
3585 | (bibtex-find-text begin nil bibtex-help-message))) | |
3586 | ||
3587 | (defun bibtex-find-text (&optional begin noerror help comma) | |
3588 | "Move point to end of text of current BibTeX field or entry head. | |
02c8032e SM |
3589 | With optional prefix BEGIN non-nil, move point to its beginning. |
3590 | Unless NOERROR is non-nil, an error is signaled if point is not | |
cdc61d35 | 3591 | on a BibTeX field. If optional arg HELP is non-nil print help message. |
ffc1e1db RW |
3592 | When called interactively, the value of HELP is `bibtex-help-message'. |
3593 | Optional arg COMMA is as in `bibtex-enclosing-field'. It is t for | |
3594 | interactive calls." | |
3595 | (interactive (list current-prefix-arg nil bibtex-help-message t)) | |
3596 | (let ((bounds (bibtex-find-text-internal t nil comma))) | |
02c8032e SM |
3597 | (cond (bounds |
3598 | (if begin | |
cdc61d35 | 3599 | (progn (goto-char (nth 1 bounds)) |
02c8032e SM |
3600 | (if (looking-at "[{\"]") |
3601 | (forward-char))) | |
cdc61d35 SM |
3602 | (goto-char (nth 2 bounds)) |
3603 | (if (memq (preceding-char) '(?} ?\")) | |
02c8032e | 3604 | (forward-char -1))) |
ffc1e1db RW |
3605 | (if help (bibtex-print-help-message (car bounds)))) |
3606 | ((not noerror) (error "Not on BibTeX field"))))) | |
50e4b39e | 3607 | |
ffc1e1db RW |
3608 | (defun bibtex-find-text-internal (&optional noerror subfield comma) |
3609 | "Find text part of current BibTeX field or entry head. | |
3610 | Return list (NAME START-TEXT END-TEXT END) with field or entry name, | |
3611 | start and end of text and end of field or entry head, or nil if not found. | |
cdc61d35 | 3612 | If optional arg NOERROR is non-nil, an error message is suppressed if text |
ffc1e1db RW |
3613 | is not found. If optional arg SUBFIELD is non-nil START-TEXT and END-TEXT |
3614 | correspond to the current subfield delimited by #. | |
3615 | Optional arg COMMA is as in `bibtex-enclosing-field'." | |
cdc61d35 SM |
3616 | (save-excursion |
3617 | (let ((pnt (point)) | |
ffc1e1db | 3618 | (bounds (bibtex-enclosing-field comma t)) |
cdc61d35 | 3619 | (case-fold-search t) |
ffc1e1db | 3620 | name start-text end-text end failure done no-sub) |
cdc61d35 SM |
3621 | (bibtex-beginning-of-entry) |
3622 | (cond (bounds | |
3623 | (setq name (bibtex-name-in-field bounds t) | |
ffc1e1db RW |
3624 | start-text (bibtex-start-of-text-in-field bounds) |
3625 | end-text (bibtex-end-of-text-in-field bounds) | |
3626 | end (bibtex-end-of-field bounds))) | |
cdc61d35 | 3627 | ;; @String |
ffc1e1db RW |
3628 | ((setq bounds (bibtex-parse-string t)) |
3629 | (if (<= pnt (bibtex-end-of-string bounds)) | |
3630 | (setq name "@String" ;; not a field name! | |
3631 | start-text (bibtex-start-of-text-in-string bounds) | |
3632 | end-text (bibtex-end-of-text-in-string bounds) | |
3633 | end (bibtex-end-of-string bounds)) | |
3634 | (setq failure t))) | |
cdc61d35 | 3635 | ;; @Preamble |
ffc1e1db RW |
3636 | ((setq bounds (bibtex-parse-preamble)) |
3637 | (if (<= pnt (bibtex-end-of-string bounds)) | |
3638 | (setq name "@Preamble" ;; not a field name! | |
3639 | start-text (bibtex-start-of-text-in-string bounds) | |
3640 | end-text (bibtex-end-of-text-in-string bounds) | |
3641 | end (bibtex-end-of-string bounds)) | |
3642 | (setq failure t))) | |
3643 | ;; BibTeX head | |
3644 | ((looking-at bibtex-entry-maybe-empty-head) | |
3645 | (goto-char (match-end 0)) | |
3646 | (if comma (save-match-data | |
3647 | (re-search-forward "\\=[ \t\n]*," nil t))) | |
3648 | (if (<= pnt (point)) | |
3649 | (setq name (match-string-no-properties bibtex-type-in-head) | |
3650 | start-text (or (match-beginning bibtex-key-in-head) | |
3651 | (match-end 0)) | |
3652 | end-text (or (match-end bibtex-key-in-head) | |
3653 | (match-end 0)) | |
3654 | end end-text | |
3655 | no-sub t) ;; subfields do not make sense | |
3656 | (setq failure t))) | |
3657 | (t (setq failure t))) | |
3658 | (when (and subfield (not failure)) | |
3659 | (setq failure no-sub) | |
3660 | (unless failure | |
3661 | (goto-char start-text) | |
cdc61d35 SM |
3662 | (while (not done) |
3663 | (if (or (prog1 (looking-at bibtex-field-const) | |
ffc1e1db | 3664 | (setq end-text (match-end 0))) |
cdc61d35 | 3665 | (prog1 (setq bounds (bibtex-parse-field-string)) |
ffc1e1db | 3666 | (setq end-text (cdr bounds)))) |
cdc61d35 | 3667 | (progn |
ffc1e1db | 3668 | (if (and (<= start-text pnt) (<= pnt end-text)) |
cdc61d35 | 3669 | (setq done t) |
ffc1e1db | 3670 | (goto-char end-text)) |
cdc61d35 | 3671 | (if (looking-at "[ \t\n]*#[ \t\n]*") |
ffc1e1db RW |
3672 | (setq start-text (goto-char (match-end 0))))) |
3673 | (setq done t failure t))))) | |
3674 | (cond ((not failure) | |
3675 | (list name start-text end-text end)) | |
3676 | ((and no-sub (not noerror)) | |
3677 | (error "Not on text part of BibTeX field")) | |
3678 | ((not noerror) (error "Not on BibTeX field")))))) | |
3679 | ||
3680 | (defun bibtex-remove-OPT-or-ALT (&optional comma) | |
7fbf4804 | 3681 | "Remove the string starting optional/alternative fields. |
ffc1e1db RW |
3682 | Align text and go thereafter to end of text. Optional arg COMMA |
3683 | is as in `bibtex-enclosing-field'. It is t for interactive calls." | |
3684 | (interactive (list t)) | |
7fbf4804 | 3685 | (let ((case-fold-search t) |
ffc1e1db | 3686 | (bounds (bibtex-enclosing-field comma))) |
50e4b39e | 3687 | (save-excursion |
f9bd4abe | 3688 | (goto-char (bibtex-start-of-name-in-field bounds)) |
7fbf4804 | 3689 | (when (looking-at "OPT\\|ALT") |
d715b065 | 3690 | (delete-region (match-beginning 0) (match-end 0)) |
7fbf4804 SM |
3691 | ;; make field non-OPT |
3692 | (search-forward "=") | |
3693 | (forward-char -1) | |
3694 | (delete-horizontal-space) | |
3695 | (if bibtex-align-at-equal-sign | |
3696 | (indent-to-column (- bibtex-text-indentation 2)) | |
3697 | (insert " ")) | |
3698 | (search-forward "=") | |
3699 | (delete-horizontal-space) | |
3700 | (if bibtex-align-at-equal-sign | |
3701 | (insert " ") | |
ffc1e1db RW |
3702 | (indent-to-column bibtex-text-indentation)))))) |
3703 | ||
3704 | (defun bibtex-remove-delimiters (&optional comma) | |
3705 | "Remove \"\" or {} around current BibTeX field text. | |
3706 | Optional arg COMMA is as in `bibtex-enclosing-field'. It is t for | |
3707 | interactive calls." | |
3708 | (interactive (list t)) | |
3709 | (let* ((bounds (bibtex-find-text-internal nil t comma)) | |
cdc61d35 SM |
3710 | (start (nth 1 bounds)) |
3711 | (end (nth 2 bounds))) | |
3712 | (if (memq (char-before end) '(?\} ?\")) | |
3713 | (delete-region (1- end) end)) | |
3714 | (if (memq (char-after start) '(?\{ ?\")) | |
3715 | (delete-region start (1+ start))))) | |
50e4b39e | 3716 | |
ffc1e1db | 3717 | (defun bibtex-kill-field (&optional copy-only comma) |
7fbf4804 SM |
3718 | "Kill the entire enclosing BibTeX field. |
3719 | With prefix arg COPY-ONLY, copy the current field to `bibtex-field-kill-ring', | |
ffc1e1db RW |
3720 | but do not actually kill it. Optional arg COMMA is as in |
3721 | `bibtex-enclosing-field'. It is t for interactive calls." | |
3722 | (interactive (list current-prefix-arg t)) | |
7fbf4804 | 3723 | (save-excursion |
7fbf4804 | 3724 | (let* ((case-fold-search t) |
ffc1e1db | 3725 | (bounds (bibtex-enclosing-field comma)) |
7fbf4804 SM |
3726 | (end (bibtex-end-of-field bounds)) |
3727 | (beg (bibtex-start-of-field bounds))) | |
3728 | (goto-char end) | |
cdc61d35 | 3729 | (skip-chars-forward ",") |
02c8032e | 3730 | (push (list (bibtex-name-in-field bounds) nil |
7fbf4804 SM |
3731 | (bibtex-text-in-field-bounds bounds)) |
3732 | bibtex-field-kill-ring) | |
50e4b39e | 3733 | (if (> (length bibtex-field-kill-ring) bibtex-field-kill-ring-max) |
7fbf4804 SM |
3734 | (setcdr (nthcdr (1- bibtex-field-kill-ring-max) |
3735 | bibtex-field-kill-ring) | |
3736 | nil)) | |
50e4b39e | 3737 | (setq bibtex-field-kill-ring-yank-pointer bibtex-field-kill-ring) |
7fbf4804 SM |
3738 | (unless copy-only |
3739 | (delete-region beg end)))) | |
50e4b39e RS |
3740 | (setq bibtex-last-kill-command 'field)) |
3741 | ||
ffc1e1db RW |
3742 | (defun bibtex-copy-field-as-kill (&optional comma) |
3743 | "Copy the BibTeX field at point to the kill ring. | |
3744 | Optional arg COMMA is as in `bibtex-enclosing-field'. It is t for | |
3745 | interactive calls." | |
3746 | (interactive (list t)) | |
3747 | (bibtex-kill-field t comma)) | |
745bc783 | 3748 | |
50e4b39e | 3749 | (defun bibtex-kill-entry (&optional copy-only) |
f9bd4abe | 3750 | "Kill the entire enclosing BibTeX entry. |
02c8032e SM |
3751 | With prefix arg COPY-ONLY, copy the current entry to `bibtex-entry-kill-ring', |
3752 | but do not actually kill it." | |
50e4b39e | 3753 | (interactive "P") |
7fbf4804 SM |
3754 | (save-excursion |
3755 | (let* ((case-fold-search t) | |
3756 | (beg (bibtex-beginning-of-entry)) | |
3757 | (end (progn (bibtex-end-of-entry) | |
3758 | (if (re-search-forward | |
ffc1e1db | 3759 | bibtex-any-entry-maybe-empty-head nil 'move) |
7fbf4804 SM |
3760 | (goto-char (match-beginning 0))) |
3761 | (point)))) | |
02c8032e | 3762 | (push (buffer-substring-no-properties beg end) |
7fbf4804 SM |
3763 | bibtex-entry-kill-ring) |
3764 | (if (> (length bibtex-entry-kill-ring) bibtex-entry-kill-ring-max) | |
3765 | (setcdr (nthcdr (1- bibtex-entry-kill-ring-max) | |
3766 | bibtex-entry-kill-ring) | |
3767 | nil)) | |
e0dc0c55 SM |
3768 | (setq bibtex-entry-kill-ring-yank-pointer bibtex-entry-kill-ring) |
3769 | (unless copy-only | |
3770 | (delete-region beg end)))) | |
50e4b39e RS |
3771 | (setq bibtex-last-kill-command 'entry)) |
3772 | ||
3773 | (defun bibtex-copy-entry-as-kill () | |
d528bff7 | 3774 | "Copy the entire enclosing BibTeX entry to `bibtex-entry-kill-ring'." |
745bc783 | 3775 | (interactive) |
50e4b39e RS |
3776 | (bibtex-kill-entry t)) |
3777 | ||
3778 | (defun bibtex-yank (&optional n) | |
3779 | "Reinsert the last BibTeX item. | |
3780 | More precisely, reinsert the field or entry killed or yanked most recently. | |
3781 | With argument N, reinsert the Nth most recently killed BibTeX item. | |
921a9483 | 3782 | See also the command \\[bibtex-yank-pop]." |
50e4b39e | 3783 | (interactive "*p") |
ffc1e1db | 3784 | (bibtex-insert-kill (1- n) t) |
f0cb6034 | 3785 | (setq this-command 'bibtex-yank)) |
50e4b39e RS |
3786 | |
3787 | (defun bibtex-yank-pop (n) | |
02c8032e | 3788 | "Replace just-yanked killed BibTeX item with a different item. |
50e4b39e | 3789 | This command is allowed only immediately after a `bibtex-yank' or a |
ffc1e1db | 3790 | `bibtex-yank-pop'. In this case, the region contains a reinserted |
02c8032e SM |
3791 | previously killed BibTeX item. `bibtex-yank-pop' deletes that item |
3792 | and inserts in its place a different killed BibTeX item. | |
50e4b39e RS |
3793 | |
3794 | With no argument, the previous kill is inserted. | |
3795 | With argument N, insert the Nth previous kill. | |
3796 | If N is negative, this is a more recent kill. | |
3797 | ||
3798 | The sequence of kills wraps around, so that after the oldest one | |
3799 | comes the newest one." | |
3800 | (interactive "*p") | |
d528bff7 SM |
3801 | (unless (eq last-command 'bibtex-yank) |
3802 | (error "Previous command was not a BibTeX yank")) | |
50e4b39e RS |
3803 | (setq this-command 'bibtex-yank) |
3804 | (let ((inhibit-read-only t)) | |
3805 | (delete-region (point) (mark t)) | |
ffc1e1db RW |
3806 | (bibtex-insert-kill n t))) |
3807 | ||
3808 | (defun bibtex-empty-field (&optional comma) | |
3809 | "Delete the text part of the current field, replace with empty text. | |
3810 | Optional arg COMMA is as in `bibtex-enclosing-field'. It is t for | |
3811 | interactive calls." | |
3812 | (interactive (list t)) | |
3813 | (let ((bounds (bibtex-enclosing-field comma))) | |
f9bd4abe GM |
3814 | (goto-char (bibtex-start-of-text-in-field bounds)) |
3815 | (delete-region (point) (bibtex-end-of-text-in-field bounds)) | |
cdc61d35 SM |
3816 | (insert (bibtex-field-left-delimiter) |
3817 | (bibtex-field-right-delimiter)) | |
3818 | (bibtex-find-text t nil bibtex-help-message))) | |
745bc783 | 3819 | |
31bc4210 | 3820 | (defun bibtex-pop-previous (arg) |
d0388eac | 3821 | "Replace text of current field with the similar field in previous entry. |
f0cb6034 | 3822 | With arg, goes up ARG entries. Repeated, goes up so many times. May be |
31bc4210 RS |
3823 | intermixed with \\[bibtex-pop-next] (bibtex-pop-next)." |
3824 | (interactive "p") | |
3825 | (bibtex-pop arg 'previous)) | |
745bc783 JB |
3826 | |
3827 | (defun bibtex-pop-next (arg) | |
3828 | "Replace text of current field with the text of similar field in next entry. | |
f0cb6034 | 3829 | With arg, goes down ARG entries. Repeated, goes down so many times. May be |
745bc783 JB |
3830 | intermixed with \\[bibtex-pop-previous] (bibtex-pop-previous)." |
3831 | (interactive "p") | |
31bc4210 | 3832 | (bibtex-pop arg 'next)) |
745bc783 | 3833 | |
7fbf4804 | 3834 | (defun bibtex-clean-entry (&optional new-key called-by-reformat) |
cb4ad359 | 3835 | "Finish editing the current BibTeX entry and clean it up. |
d715b065 | 3836 | Check that no required fields are empty and formats entry dependent |
7fbf4804 | 3837 | on the value of `bibtex-entry-format'. |
f9bd4abe | 3838 | If the reference key of the entry is empty or a prefix argument is given, |
d10e87a2 | 3839 | calculate a new reference key. (Note: this works only if fields in entry |
7fbf4804 SM |
3840 | begin on separate lines prior to calling `bibtex-clean-entry' or if |
3841 | 'realign is contained in `bibtex-entry-format'.) | |
d715b065 | 3842 | Don't call `bibtex-clean-entry' on @Preamble entries. |
50e4b39e | 3843 | At end of the cleaning process, the functions in |
7fbf4804 SM |
3844 | `bibtex-clean-entry-hook' are called with region narrowed to entry." |
3845 | ;; Opt. arg called-by-reformat is t if bibtex-clean-entry | |
3846 | ;; is called by bibtex-reformat | |
cb4ad359 | 3847 | (interactive "P") |
7fbf4804 | 3848 | (let ((case-fold-search t) |
02c8032e | 3849 | (start (bibtex-beginning-of-entry)) |
cdc61d35 | 3850 | (_ (looking-at bibtex-any-entry-maybe-empty-head)) |
02c8032e SM |
3851 | (entry-type (bibtex-type-in-head)) |
3852 | (key (bibtex-key-in-head))) | |
d715b065 | 3853 | ;; formatting |
d528bff7 | 3854 | (cond ((bibtex-string= entry-type "preamble") |
d715b065 KG |
3855 | ;; (bibtex-format-preamble) |
3856 | (error "No clean up of @Preamble entries")) | |
02c8032e SM |
3857 | ((bibtex-string= entry-type "string") |
3858 | (setq entry-type 'string)) | |
e0dc0c55 | 3859 | ;; (bibtex-format-string) |
d715b065 KG |
3860 | (t (bibtex-format-entry))) |
3861 | ;; set key | |
7fbf4804 | 3862 | (when (or new-key (not key)) |
d715b065 | 3863 | (setq key (bibtex-generate-autokey)) |
d528bff7 SM |
3864 | ;; Sometimes bibtex-generate-autokey returns an empty string |
3865 | (if (or bibtex-autokey-edit-before-use (string= "" key)) | |
02c8032e SM |
3866 | (setq key (if (eq entry-type 'string) |
3867 | (bibtex-read-string-key key) | |
3868 | (bibtex-read-key "Key to use: " key)))) | |
3869 | (save-excursion | |
3870 | (re-search-forward (if (eq entry-type 'string) | |
3871 | bibtex-string-maybe-empty-head | |
3872 | bibtex-entry-maybe-empty-head)) | |
3873 | (if (match-beginning bibtex-key-in-head) | |
3874 | (delete-region (match-beginning bibtex-key-in-head) | |
3875 | (match-end bibtex-key-in-head))) | |
3876 | (insert key))) | |
e0dc0c55 | 3877 | |
4f9ae122 | 3878 | (unless called-by-reformat |
02c8032e SM |
3879 | (let* ((end (save-excursion |
3880 | (bibtex-end-of-entry) | |
3881 | (if (re-search-forward | |
3882 | bibtex-entry-maybe-empty-head nil 'move) | |
3883 | (goto-char (match-beginning 0))) | |
3884 | (point))) | |
4f9ae122 | 3885 | (entry (buffer-substring start end)) |
e0dc0c55 SM |
3886 | ;; include the crossref key in index |
3887 | (index (let ((bibtex-maintain-sorted-entries 'crossref)) | |
02c8032e | 3888 | (bibtex-entry-index))) ; moves point to end of head |
d528bff7 | 3889 | error) |
e0dc0c55 | 3890 | ;; sorting |
4f9ae122 SM |
3891 | (if (and bibtex-maintain-sorted-entries |
3892 | (not (and bibtex-sort-ignore-string-entries | |
02c8032e | 3893 | (eq entry-type 'string)))) |
4f9ae122 SM |
3894 | (progn |
3895 | (delete-region start end) | |
02c8032e SM |
3896 | (setq error (not (bibtex-prepare-new-entry index)) |
3897 | start (point)) ; update start | |
3898 | (save-excursion (insert entry))) | |
d528bff7 SM |
3899 | (bibtex-find-entry key) |
3900 | (setq error (or (/= (point) start) | |
02c8032e | 3901 | (bibtex-find-entry key nil end)))) |
d528bff7 | 3902 | (if error |
e0dc0c55 SM |
3903 | (error "New inserted entry yields duplicate key")) |
3904 | (dolist (buffer (bibtex-files-expand)) | |
3905 | (with-current-buffer buffer | |
3906 | (if (cdr (assoc-string key bibtex-reference-keys)) | |
3907 | (error "Duplicate key in %s" (buffer-file-name))))) | |
3908 | ||
3909 | ;; Only update the list of keys if it has been built already. | |
02c8032e | 3910 | (cond ((eq entry-type 'string) |
e0dc0c55 SM |
3911 | (if (and (listp bibtex-strings) |
3912 | (not (assoc key bibtex-strings))) | |
02c8032e | 3913 | (push (cons key (bibtex-text-in-string |
ffc1e1db | 3914 | (bibtex-parse-string) t)) |
02c8032e | 3915 | bibtex-strings))) |
e0dc0c55 SM |
3916 | ;; We have a normal entry. |
3917 | ((listp bibtex-reference-keys) | |
3918 | (cond ((not (assoc key bibtex-reference-keys)) | |
3919 | (push (cons key t) bibtex-reference-keys)) | |
3920 | ((not (cdr (assoc key bibtex-reference-keys))) | |
3921 | ;; Turn a crossref key into a header key | |
3922 | (setq bibtex-reference-keys | |
3923 | (cons (cons key t) | |
3924 | (delete (list key) bibtex-reference-keys))))) | |
3925 | ;; Handle crossref key. | |
3926 | (if (and (nth 1 index) | |
3927 | (not (assoc (nth 1 index) bibtex-reference-keys))) | |
3928 | (push (list (nth 1 index)) bibtex-reference-keys))))) | |
3929 | ||
3930 | ;; final clean up | |
3931 | (if bibtex-clean-entry-hook | |
3932 | (save-excursion | |
3933 | (save-restriction | |
3934 | (bibtex-narrow-to-entry) | |
3935 | (run-hooks 'bibtex-clean-entry-hook))))))) | |
50e4b39e | 3936 | |
d715b065 KG |
3937 | (defun bibtex-fill-field-bounds (bounds justify &optional move) |
3938 | "Fill BibTeX field delimited by BOUNDS. | |
3939 | If JUSTIFY is non-nil justify as well. | |
3940 | If optional arg MOVE is non-nil move point to end of field." | |
3941 | (let ((end-field (copy-marker (bibtex-end-of-field bounds)))) | |
ffc1e1db RW |
3942 | (if (not justify) |
3943 | (goto-char (bibtex-start-of-text-in-field bounds)) | |
3944 | (goto-char (bibtex-start-of-field bounds)) | |
3945 | (forward-char) ;; leading comma | |
3946 | (bibtex-delete-whitespace) | |
3947 | (open-line 1) | |
3948 | (forward-char) | |
3949 | (indent-to-column (+ bibtex-entry-offset | |
3950 | bibtex-field-indentation)) | |
3951 | (re-search-forward "[ \t\n]*=" end-field) | |
3952 | (replace-match "=") | |
3953 | (forward-char -1) | |
3954 | (if bibtex-align-at-equal-sign | |
3955 | (indent-to-column | |
3956 | (+ bibtex-entry-offset (- bibtex-text-indentation 2))) | |
3957 | (insert " ")) | |
3958 | (forward-char) | |
3959 | (bibtex-delete-whitespace) | |
3960 | (if bibtex-align-at-equal-sign | |
3961 | (insert " ") | |
3962 | (indent-to-column bibtex-text-indentation))) | |
1fdecd0c RF |
3963 | ;; Paragraphs within fields are not preserved. Bother? |
3964 | (fill-region-as-paragraph (line-beginning-position) end-field | |
3965 | default-justification nil (point)) | |
d715b065 KG |
3966 | (if move (goto-char end-field)))) |
3967 | ||
3968 | (defun bibtex-fill-field (&optional justify) | |
3969 | "Like \\[fill-paragraph], but fill current BibTeX field. | |
ffc1e1db | 3970 | If optional prefix JUSTIFY is non-nil justify as well. |
d715b065 KG |
3971 | In BibTeX mode this function is bound to `fill-paragraph-function'." |
3972 | (interactive "*P") | |
3973 | (let ((pnt (copy-marker (point))) | |
ffc1e1db RW |
3974 | (bounds (bibtex-enclosing-field t))) |
3975 | (bibtex-fill-field-bounds bounds justify) | |
3976 | (goto-char pnt))) | |
d715b065 | 3977 | |
50e4b39e | 3978 | (defun bibtex-fill-entry () |
7fbf4804 SM |
3979 | "Fill current BibTeX entry. |
3980 | Realign entry, so that every field starts on a separate line. Field | |
d0388eac | 3981 | names appear in column `bibtex-field-indentation', field text starts in |
f0cb6034 | 3982 | column `bibtex-text-indentation' and continuation lines start here, too. |
d715b065 | 3983 | If `bibtex-align-at-equal-sign' is non-nil, align equal signs, too." |
50e4b39e RS |
3984 | (interactive "*") |
3985 | (let ((pnt (copy-marker (point))) | |
7fbf4804 | 3986 | (end (copy-marker (bibtex-end-of-entry))) |
ffc1e1db | 3987 | (beg (bibtex-beginning-of-entry)) ; move point |
7fbf4804 | 3988 | bounds) |
55fe21fc | 3989 | (bibtex-delete-whitespace) |
50e4b39e | 3990 | (indent-to-column bibtex-entry-offset) |
ffc1e1db RW |
3991 | (bibtex-beginning-first-field beg) |
3992 | (while (setq bounds (bibtex-parse-field)) | |
d715b065 | 3993 | (bibtex-fill-field-bounds bounds t t)) |
50e4b39e RS |
3994 | (if (looking-at ",") |
3995 | (forward-char)) | |
ffc1e1db | 3996 | (skip-chars-backward " \t\n") |
55fe21fc | 3997 | (bibtex-delete-whitespace) |
50e4b39e RS |
3998 | (open-line 1) |
3999 | (forward-char) | |
4000 | (indent-to-column bibtex-entry-offset) | |
4001 | (goto-char pnt))) | |
4002 | ||
4f9ae122 SM |
4003 | (defun bibtex-realign () |
4004 | "Realign BibTeX entries such that they are separated by one blank line." | |
4005 | (goto-char (point-min)) | |
1fdecd0c | 4006 | (let ((case-fold-search t) |
cdc61d35 SM |
4007 | (entry-type (concat "[ \t\n]*\\(" bibtex-entry-type "\\)"))) |
4008 | ;; No blank lines prior to the first entry if there no | |
e0dc0c55 | 4009 | ;; non-white characters in front of it. |
cdc61d35 | 4010 | (when (looking-at entry-type) |
4f9ae122 | 4011 | (replace-match "\\1")) |
cdc61d35 SM |
4012 | ;; Entries are separated by one blank line. |
4013 | (while (re-search-forward entry-type nil t) | |
e0dc0c55 | 4014 | (replace-match "\n\n\\1")) |
cdc61d35 | 4015 | ;; One blank line past the last entry if it is followed by |
e0dc0c55 SM |
4016 | ;; non-white characters, no blank line otherwise. |
4017 | (beginning-of-line) | |
cdc61d35 | 4018 | (when (re-search-forward bibtex-entry-type nil t) |
e0dc0c55 SM |
4019 | (bibtex-end-of-entry) |
4020 | (bibtex-delete-whitespace) | |
4021 | (open-line (if (eobp) 1 2))))) | |
4f9ae122 SM |
4022 | |
4023 | (defun bibtex-reformat (&optional read-options) | |
d0388eac | 4024 | "Reformat all BibTeX entries in buffer or region. |
e0dc0c55 | 4025 | Without prefix argument, reformatting is based on `bibtex-entry-format'. |
d0388eac | 4026 | With prefix argument, read options for reformatting from minibuffer. |
f0cb6034 | 4027 | With \\[universal-argument] \\[universal-argument] prefix argument, reuse previous answers (if any) again. |
4f9ae122 | 4028 | If mark is active reformat entries in region, if not in whole buffer." |
50e4b39e RS |
4029 | (interactive "*P") |
4030 | (let* ((pnt (point)) | |
4031 | (use-previous-options | |
4f9ae122 | 4032 | (and (equal (prefix-numeric-value read-options) 16) |
50e4b39e | 4033 | (or bibtex-reformat-previous-options |
f9bd4abe | 4034 | bibtex-reformat-previous-reference-keys))) |
50e4b39e | 4035 | (bibtex-entry-format |
e0dc0c55 SM |
4036 | (cond (read-options |
4037 | (if use-previous-options | |
4038 | bibtex-reformat-previous-options | |
4039 | (setq bibtex-reformat-previous-options | |
4040 | (mapcar (lambda (option) | |
4041 | (if (y-or-n-p (car option)) (cdr option))) | |
4042 | `(("Realign entries (recommended)? " . 'realign) | |
4043 | ("Remove empty optional and alternative fields? " . 'opts-or-alts) | |
4044 | ("Remove delimiters around pure numerical fields? " . 'numerical-fields) | |
4045 | (,(concat (if bibtex-comma-after-last-field "Insert" "Remove") | |
4046 | " comma at end of entry? ") . 'last-comma) | |
4047 | ("Replace double page dashes by single ones? " . 'page-dashes) | |
4048 | ("Inherit booktitle? " . 'inherit-booktitle) | |
4049 | ("Force delimiters? " . 'delimiters) | |
4050 | ("Unify case of entry types and field names? " . 'unify-case)))))) | |
4051 | ;; Do not include required-fields because `bibtex-reformat' | |
4052 | ;; cannot handle the error messages of `bibtex-format-entry'. | |
4053 | ;; Use `bibtex-validate' to check for required fields. | |
4054 | ((eq t bibtex-entry-format) | |
4055 | '(realign opts-or-alts numerical-fields delimiters | |
4056 | last-comma page-dashes unify-case inherit-booktitle)) | |
4057 | (t | |
4058 | (remove 'required-fields (push 'realign bibtex-entry-format))))) | |
4f9ae122 SM |
4059 | (reformat-reference-keys |
4060 | (if read-options | |
4061 | (if use-previous-options | |
4062 | bibtex-reformat-previous-reference-keys | |
4063 | (setq bibtex-reformat-previous-reference-keys | |
4064 | (y-or-n-p "Generate new reference keys automatically? "))))) | |
4f9ae122 SM |
4065 | (bibtex-sort-ignore-string-entries t) |
4066 | bibtex-autokey-edit-before-use) | |
4067 | ||
50e4b39e | 4068 | (save-restriction |
ffc1e1db | 4069 | (if mark-active (narrow-to-region (region-beginning) (region-end))) |
4f9ae122 | 4070 | (if (memq 'realign bibtex-entry-format) |
e0dc0c55 | 4071 | (bibtex-realign)) |
50e4b39e | 4072 | (bibtex-progress-message "Formatting" 1) |
d715b065 | 4073 | (bibtex-map-entries (lambda (key beg end) |
7fbf4804 | 4074 | (bibtex-progress-message) |
4f9ae122 | 4075 | (bibtex-clean-entry reformat-reference-keys t))) |
50e4b39e | 4076 | (bibtex-progress-message 'done)) |
e0dc0c55 | 4077 | (when reformat-reference-keys |
4f9ae122 | 4078 | (kill-local-variable 'bibtex-reference-keys) |
e0dc0c55 SM |
4079 | (when bibtex-maintain-sorted-entries |
4080 | (bibtex-progress-message "Sorting" 1) | |
4081 | (bibtex-sort-buffer) | |
4082 | (bibtex-progress-message 'done))) | |
50e4b39e RS |
4083 | (goto-char pnt))) |
4084 | ||
4f9ae122 | 4085 | (defun bibtex-convert-alien (&optional read-options) |
d10e87a2 | 4086 | "Make an alien BibTeX buffer fully usable by BibTeX mode. |
4f9ae122 | 4087 | If a file does not conform with all standards used by BibTeX mode, |
d10e87a2 | 4088 | some of the high-level features of BibTeX mode are not available. |
7fbf4804 | 4089 | This function tries to convert current buffer to conform with these standards. |
4f9ae122 SM |
4090 | With prefix argument READ-OPTIONS non-nil, read options for reformatting |
4091 | entries from minibuffer." | |
50e4b39e RS |
4092 | (interactive "*P") |
4093 | (message "Starting to validate buffer...") | |
4094 | (sit-for 1 nil t) | |
4f9ae122 | 4095 | (bibtex-realign) |
7fbf4804 | 4096 | (deactivate-mark) ; So bibtex-validate works on the whole buffer. |
ffc1e1db RW |
4097 | (if (not (let (bibtex-maintain-sorted-entries) |
4098 | (bibtex-validate))) | |
4099 | (message "Correct errors and call `bibtex-convert-alien' again") | |
7fbf4804 SM |
4100 | (message "Starting to reformat entries...") |
4101 | (sit-for 2 nil t) | |
4f9ae122 | 4102 | (bibtex-reformat read-options) |
7fbf4804 SM |
4103 | (goto-char (point-max)) |
4104 | (message "Buffer is now parsable. Please save it."))) | |
4105 | ||
4106 | (defun bibtex-complete () | |
4107 | "Complete word fragment before point according to context. | |
d715b065 | 4108 | If point is inside key or crossref field perform key completion based on |
d528bff7 SM |
4109 | `bibtex-reference-keys'. Inside a month field perform key completion |
4110 | based on `bibtex-predefined-month-strings'. Inside any other field | |
cdc61d35 SM |
4111 | \(including a String or Preamble definition) perform string completion |
4112 | based on `bibtex-strings'. | |
4113 | An error is signaled if point is outside key or BibTeX field." | |
7fbf4804 | 4114 | (interactive) |
d528bff7 SM |
4115 | (let ((pnt (point)) |
4116 | (case-fold-search t) | |
4117 | bounds name compl) | |
7fbf4804 | 4118 | (save-excursion |
ffc1e1db | 4119 | (if (and (setq bounds (bibtex-enclosing-field nil t)) |
7fbf4804 SM |
4120 | (>= pnt (bibtex-start-of-text-in-field bounds)) |
4121 | (<= pnt (bibtex-end-of-text-in-field bounds))) | |
d528bff7 SM |
4122 | (setq name (bibtex-name-in-field bounds t) |
4123 | compl (cond ((bibtex-string= name "crossref") | |
02c8032e SM |
4124 | ;; point is in crossref field |
4125 | 'crossref-key) | |
d528bff7 | 4126 | ((bibtex-string= name "month") |
02c8032e | 4127 | ;; point is in month field |
d528bff7 | 4128 | bibtex-predefined-month-strings) |
02c8032e | 4129 | ;; point is in other field |
cdc61d35 | 4130 | (t (bibtex-strings)))) |
7fbf4804 | 4131 | (bibtex-beginning-of-entry) |
ffc1e1db | 4132 | (cond ((setq bounds (bibtex-parse-string t)) |
cdc61d35 SM |
4133 | ;; point is inside a @String key |
4134 | (cond ((and (>= pnt (nth 1 (car bounds))) | |
4135 | (<= pnt (nth 2 (car bounds)))) | |
4136 | (setq compl 'string)) | |
4137 | ;; point is inside a @String field | |
4138 | ((and (>= pnt (bibtex-start-of-text-in-string bounds)) | |
4139 | (<= pnt (bibtex-end-of-text-in-string bounds))) | |
4140 | (setq compl (bibtex-strings))))) | |
4141 | ;; point is inside a @Preamble field | |
ffc1e1db RW |
4142 | ((setq bounds (bibtex-parse-preamble)) |
4143 | (if (and (>= pnt (bibtex-start-of-text-in-string bounds)) | |
4144 | (<= pnt (bibtex-end-of-text-in-string bounds))) | |
4145 | (setq compl (bibtex-strings)))) | |
02c8032e SM |
4146 | ((and (looking-at bibtex-entry-maybe-empty-head) |
4147 | ;; point is inside a key | |
4148 | (or (and (match-beginning bibtex-key-in-head) | |
4149 | (>= pnt (match-beginning bibtex-key-in-head)) | |
4150 | (<= pnt (match-end bibtex-key-in-head))) | |
4151 | ;; or point is on empty key | |
4152 | (and (not (match-beginning bibtex-key-in-head)) | |
4153 | (= pnt (match-end 0))))) | |
4154 | (setq compl 'key))))) | |
4155 | ||
4156 | (cond ((eq compl 'key) | |
4157 | ;; key completion: no cleanup needed | |
5e860c24 RW |
4158 | (setq choose-completion-string-functions nil |
4159 | completion-ignore-case nil) | |
4160 | (bibtex-complete-internal (bibtex-global-key-alist))) | |
02c8032e SM |
4161 | |
4162 | ((eq compl 'crossref-key) | |
4163 | ;; crossref key completion | |
5e860c24 RW |
4164 | ;; |
4165 | ;; If we quit the *Completions* buffer without requesting | |
4166 | ;; a completion, `choose-completion-string-functions' is still | |
4167 | ;; non-nil. Therefore, `choose-completion-string-functions' is | |
4168 | ;; always set (either to non-nil or nil) when a new completion | |
4169 | ;; is requested. | |
4170 | ;; Also, `choose-completion-delete-max-match' requires | |
4171 | ;; that we set `completion-ignore-case' (i.e., binding via `let' | |
4172 | ;; is not sufficient). | |
4173 | (setq completion-ignore-case nil | |
4174 | choose-completion-string-functions | |
4175 | (lambda (choice buffer mini-p base-size) | |
4176 | (setq choose-completion-string-functions nil) | |
4177 | (choose-completion-string choice buffer base-size) | |
4178 | (bibtex-complete-crossref-cleanup choice) | |
4179 | t)) ; needed by choose-completion-string-functions | |
4180 | ||
4181 | (bibtex-complete-crossref-cleanup (bibtex-complete-internal | |
4182 | (bibtex-global-key-alist)))) | |
02c8032e SM |
4183 | |
4184 | ((eq compl 'string) | |
4185 | ;; string key completion: no cleanup needed | |
5e860c24 RW |
4186 | (setq choose-completion-string-functions nil |
4187 | completion-ignore-case t) | |
4188 | (bibtex-complete-internal bibtex-strings)) | |
7fbf4804 | 4189 | |
d528bff7 | 4190 | (compl |
7fbf4804 | 4191 | ;; string completion |
5e860c24 RW |
4192 | (setq completion-ignore-case t |
4193 | choose-completion-string-functions | |
4194 | `(lambda (choice buffer mini-p base-size) | |
4195 | (setq choose-completion-string-functions nil) | |
4196 | (choose-completion-string choice buffer base-size) | |
4197 | (bibtex-complete-string-cleanup choice ',compl) | |
4198 | t)) ; needed by choose-completion-string-functions | |
4199 | (bibtex-complete-string-cleanup (bibtex-complete-internal compl) | |
4200 | compl)) | |
4201 | ||
4202 | (t (setq choose-completion-string-functions nil | |
4203 | completion-ignore-case nil) ; default | |
4204 | (error "Point outside key or BibTeX field"))))) | |
745bc783 | 4205 | |
cb4ad359 | 4206 | (defun bibtex-Article () |
f0cb6034 | 4207 | "Insert a new BibTeX @Article entry; see also `bibtex-entry'." |
7fbf4804 | 4208 | (interactive "*") |
cb4ad359 | 4209 | (bibtex-entry "Article")) |
2798dfd6 | 4210 | |
cb4ad359 | 4211 | (defun bibtex-Book () |
f0cb6034 | 4212 | "Insert a new BibTeX @Book entry; see also `bibtex-entry'." |
7fbf4804 | 4213 | (interactive "*") |
cb4ad359 | 4214 | (bibtex-entry "Book")) |
2798dfd6 | 4215 | |
cb4ad359 | 4216 | (defun bibtex-Booklet () |
f0cb6034 | 4217 | "Insert a new BibTeX @Booklet entry; see also `bibtex-entry'." |
7fbf4804 | 4218 | (interactive "*") |
cb4ad359 RS |
4219 | (bibtex-entry "Booklet")) |
4220 | ||
4221 | (defun bibtex-InBook () | |
f0cb6034 | 4222 | "Insert a new BibTeX @InBook entry; see also `bibtex-entry'." |
7fbf4804 | 4223 | (interactive "*") |
cb4ad359 RS |
4224 | (bibtex-entry "InBook")) |
4225 | ||
4226 | (defun bibtex-InCollection () | |
f0cb6034 | 4227 | "Insert a new BibTeX @InCollection entry; see also `bibtex-entry'." |
7fbf4804 | 4228 | (interactive "*") |
cb4ad359 RS |
4229 | (bibtex-entry "InCollection")) |
4230 | ||
4231 | (defun bibtex-InProceedings () | |
f0cb6034 | 4232 | "Insert a new BibTeX @InProceedings entry; see also `bibtex-entry'." |
7fbf4804 | 4233 | (interactive "*") |
cb4ad359 RS |
4234 | (bibtex-entry "InProceedings")) |
4235 | ||
4236 | (defun bibtex-Manual () | |
f0cb6034 | 4237 | "Insert a new BibTeX @Manual entry; see also `bibtex-entry'." |
7fbf4804 | 4238 | (interactive "*") |
cb4ad359 RS |
4239 | (bibtex-entry "Manual")) |
4240 | ||
4241 | (defun bibtex-MastersThesis () | |
f0cb6034 | 4242 | "Insert a new BibTeX @MastersThesis entry; see also `bibtex-entry'." |
7fbf4804 | 4243 | (interactive "*") |
cb4ad359 RS |
4244 | (bibtex-entry "MastersThesis")) |
4245 | ||
4246 | (defun bibtex-Misc () | |
f0cb6034 | 4247 | "Insert a new BibTeX @Misc entry; see also `bibtex-entry'." |
7fbf4804 | 4248 | (interactive "*") |
cb4ad359 RS |
4249 | (bibtex-entry "Misc")) |
4250 | ||
4251 | (defun bibtex-PhdThesis () | |
f0cb6034 | 4252 | "Insert a new BibTeX @PhdThesis entry; see also `bibtex-entry'." |
7fbf4804 | 4253 | (interactive "*") |
cb4ad359 RS |
4254 | (bibtex-entry "PhdThesis")) |
4255 | ||
4256 | (defun bibtex-Proceedings () | |
f0cb6034 | 4257 | "Insert a new BibTeX @Proceedings entry; see also `bibtex-entry'." |
7fbf4804 | 4258 | (interactive "*") |
cb4ad359 RS |
4259 | (bibtex-entry "Proceedings")) |
4260 | ||
4261 | (defun bibtex-TechReport () | |
f0cb6034 | 4262 | "Insert a new BibTeX @TechReport entry; see also `bibtex-entry'." |
7fbf4804 | 4263 | (interactive "*") |
cb4ad359 RS |
4264 | (bibtex-entry "TechReport")) |
4265 | ||
4266 | (defun bibtex-Unpublished () | |
f0cb6034 | 4267 | "Insert a new BibTeX @Unpublished entry; see also `bibtex-entry'." |
7fbf4804 | 4268 | (interactive "*") |
cb4ad359 RS |
4269 | (bibtex-entry "Unpublished")) |
4270 | ||
7fbf4804 SM |
4271 | (defun bibtex-String (&optional key) |
4272 | "Insert a new BibTeX @String entry with key KEY." | |
02c8032e | 4273 | (interactive (list (bibtex-read-string-key))) |
7fbf4804 | 4274 | (let ((bibtex-maintain-sorted-entries |
d528bff7 SM |
4275 | (unless bibtex-sort-ignore-string-entries |
4276 | bibtex-maintain-sorted-entries)) | |
7fbf4804 SM |
4277 | endpos) |
4278 | (unless (bibtex-prepare-new-entry (list key nil "String")) | |
4279 | (error "Entry with key `%s' already exists" key)) | |
4280 | (if (zerop (length key)) (setq key nil)) | |
50e4b39e | 4281 | (indent-to-column bibtex-entry-offset) |
7fbf4804 SM |
4282 | (insert "@String" |
4283 | (bibtex-entry-left-delimiter)) | |
4284 | (if key | |
4285 | (insert key) | |
4286 | (setq endpos (point))) | |
4287 | (insert " = " | |
4288 | (bibtex-field-left-delimiter)) | |
4289 | (if key | |
4290 | (setq endpos (point))) | |
4291 | (insert (bibtex-field-right-delimiter) | |
4292 | (bibtex-entry-right-delimiter) | |
4293 | "\n") | |
4294 | (goto-char endpos))) | |
50e4b39e RS |
4295 | |
4296 | (defun bibtex-Preamble () | |
f0cb6034 | 4297 | "Insert a new BibTeX @Preamble entry." |
7fbf4804 | 4298 | (interactive "*") |
cb4ad359 | 4299 | (bibtex-move-outside-of-entry) |
50e4b39e | 4300 | (indent-to-column bibtex-entry-offset) |
7fbf4804 | 4301 | (insert "@Preamble" |
c2fa1079 SM |
4302 | (bibtex-entry-left-delimiter) |
4303 | (bibtex-field-left-delimiter)) | |
d715b065 | 4304 | (let ((endpos (point))) |
cdc61d35 SM |
4305 | (insert (bibtex-field-right-delimiter) |
4306 | (bibtex-entry-right-delimiter) | |
d715b065 KG |
4307 | "\n") |
4308 | (goto-char endpos))) | |
2798dfd6 | 4309 | |
e0dc0c55 SM |
4310 | (defun bibtex-url (&optional pos) |
4311 | "Browse a URL for the BibTeX entry at point. | |
4312 | Optional POS is the location of the BibTeX entry. | |
d528bff7 | 4313 | The URL is generated using the schemes defined in `bibtex-generate-url-list' |
a9d77f1f | 4314 | \(see there\). Then the URL is passed to `browse-url'." |
e0dc0c55 | 4315 | (interactive) |
d528bff7 | 4316 | (save-excursion |
e0dc0c55 | 4317 | (if pos (goto-char pos)) |
d528bff7 | 4318 | (bibtex-beginning-of-entry) |
cdc61d35 SM |
4319 | ;; Always remove field delimiters |
4320 | (let ((fields-alist (bibtex-parse-entry t)) | |
e0dc0c55 | 4321 | ;; Always ignore case, |
d528bff7 SM |
4322 | (case-fold-search t) |
4323 | (lst bibtex-generate-url-list) | |
63d516ce | 4324 | field url scheme obj fmt) |
e0dc0c55 | 4325 | (while (setq scheme (pop lst)) |
a9d77f1f SM |
4326 | (when (and (setq field (cdr (assoc-string (caar scheme) |
4327 | fields-alist t))) | |
cdc61d35 | 4328 | (string-match (cdar scheme) field)) |
63d516ce SM |
4329 | (setq lst nil |
4330 | scheme (cdr scheme) | |
4331 | url (if (null scheme) (match-string 0 field) | |
4332 | (if (stringp (car scheme)) | |
4333 | (setq fmt (pop scheme))) | |
4334 | (dolist (step scheme) | |
cdc61d35 | 4335 | (setq field (cdr (assoc-string (car step) fields-alist t))) |
63d516ce SM |
4336 | (if (string-match (nth 1 step) field) |
4337 | (setq field (cond ((functionp (nth 2 step)) | |
4338 | (funcall (nth 2 step) field)) | |
4339 | ((numberp (nth 2 step)) | |
4340 | (match-string (nth 2 step) field)) | |
4341 | (t | |
4342 | (replace-match (nth 2 step) t nil field)))) | |
4343 | ;; If the scheme is set up correctly, | |
4344 | ;; we should never reach this point | |
4345 | (error "Match failed: %s" field)) | |
4346 | (push field obj)) | |
4347 | (if fmt (apply 'format fmt (nreverse obj)) | |
4348 | (apply 'concat (nreverse obj))))) | |
4349 | (browse-url (message "%s" url)))) | |
e0dc0c55 | 4350 | (unless url (message "No URL known."))))) |
d528bff7 | 4351 | |
745bc783 | 4352 | \f |
5c69dbfc | 4353 | ;; Make BibTeX a Feature |
cb4ad359 RS |
4354 | |
4355 | (provide 'bibtex) | |
745bc783 | 4356 | |
8bf38a9b | 4357 | ;; arch-tag: ee2be3af-caad-427f-b42a-d20fad630d04 |
9ae11a89 | 4358 | ;;; bibtex.el ends here |