• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

sile-typesetter / sile / 14335207249

08 Apr 2025 01:46PM UTC coverage: 60.462% (+0.9%) from 59.535%
14335207249

push

github

web-flow
Merge pull request #2257 from alerque/lintery

98 of 147 new or added lines in 46 files covered. (66.67%)

1 existing line in 1 file now uncovered.

13061 of 21602 relevant lines covered (60.46%)

4611.17 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/packages/bibtex/init.lua
1
local base = require("packages.base")
×
2

3
local loadkit = require("loadkit")
×
4
local cslStyleLoader = loadkit.make_loader("csl")
×
5
local cslLocaleLoader = loadkit.make_loader("xml")
×
6

7
local CslLocale = require("packages.bibtex.csl.locale")
×
8
local CslStyle = require("packages.bibtex.csl.style")
×
9
local CslEngine = require("packages.bibtex.csl.engine")
×
10

11
local bibparser = require("packages.bibtex.support.bibparser")
×
12
local parseBibtex, crossrefAndXDataResolve = bibparser.parseBibtex, bibparser.crossrefAndXDataResolve
×
13

14
local bib2csl = require("packages.bibtex.support.bib2csl")
×
15
local locators = require("packages.bibtex.support.locators")
×
16

17
local Bibliography = require("packages.bibtex.bibliography") -- Legacy
×
18

19
local function loadCslLocale (name)
20
   local filename = SILE.resolveFile("packages/bibtex/csl/locales/locales-" .. name .. ".xml")
×
21
      or cslLocaleLoader("packages.bibtex.csl.locales.locales-" .. name)
×
22
   if not filename then
×
23
      SU.error("Could not find CSL locale '" .. name .. "'")
×
24
   end
25
   local locale, err = CslLocale.read(filename)
×
26
   if not locale then
×
27
      SU.error("Could not open CSL locale '" .. name .. "'': " .. err)
×
28
      return
×
29
   end
30
   return locale
×
31
end
32
local function loadCslStyle (name)
33
   local filename = SILE.resolveFile("packages/bibtex/csl/styles/" .. name .. ".csl")
×
34
      or cslStyleLoader("packages.bibtex.csl.styles." .. name)
×
35
   if not filename then
×
36
      SU.error("Could not find CSL style '" .. name .. "'")
×
37
   end
38
   local style, err = CslStyle.read(filename)
×
39
   if not style then
×
40
      SU.error("Could not open CSL style '" .. name .. "'': " .. err)
×
41
      return
×
42
   end
43
   return style
×
44
end
45

46
local package = pl.class(base)
×
47
package._name = "bibtex"
×
48

49
local function resolveEntry (bib, key)
50
   local entry = bib[key]
×
51
   if not entry then
×
52
      SU.warn("Unknown citation key " .. key)
×
53
      return
×
54
   end
55
   if entry.type == "xdata" then
×
56
      SU.warn("Skipped citation of @xdata entry " .. key)
×
57
      return
×
58
   end
59
   crossrefAndXDataResolve(bib, entry)
×
60
   return entry
×
61
end
62

63
function package:loadOptPackage (pack)
×
64
   local ok, _ = pcall(function ()
×
65
      self:loadPackage(pack)
×
66
      return true
×
67
   end)
68
   SU.debug("bibtex", "Optional package " .. pack .. (ok and " loaded" or " not loaded"))
×
69
   return ok
×
70
end
71

72
function package:_init ()
×
73
   base._init(self)
×
74
   SILE.scratch.bibtex = { bib = {}, cited = { keys = {}, citnums = {} } }
×
75
   -- For DOI, PMID, PMCID and URL support.
76
   self:loadPackage("url")
×
77
   -- For underline styling support
78
   self:loadPackage("rules")
×
79
   -- For TeX-like math support (extension)
80
   self:loadPackage("math")
×
81
   -- For superscripting support in number formatting
82
   -- Play fair: try to load 3rd-party optional textsubsuper package.
83
   -- If not available, fallback to raiselower to implement textsuperscript
84
   if not self:loadOptPackage("textsubsuper") then
×
85
      self:loadPackage("raiselower")
×
86
      self:registerCommand("textsuperscript", function (_, content)
×
87
         -- Fake more or less ad hoc superscripting
88
         SILE.call("raise", { height = "0.7ex" }, function ()
×
89
            SILE.call("font", { size = "1.5ex" }, content)
×
90
         end)
91
      end)
92
   end
93
end
94

NEW
95
function package:declareSettings ()
×
96
   SILE.settings:declare({
×
97
      parameter = "bibtex.style",
98
      type = "string",
99
      default = "csl",
100
      help = "BibTeX style",
101
   })
102

103
   -- For CSL hanging-indent or second-field-align
104
   SILE.settings:declare({
×
105
      parameter = "bibliography.indent",
106
      type = "measurement",
107
      default = SILE.types.measurement("3em"),
108
      help = "Left indentation for bibliography entries when the citation style requires it.",
109
   })
110
end
111

112
--- Retrieve the CSL engine, creating it if necessary.
113
-- @treturn CslEngine CSL engine instance
NEW
114
function package:getCslEngine ()
×
115
   if not SILE.scratch.bibtex.engine then
×
116
      SILE.call("bibliographystyle", { lang = "en-US", style = "chicago-author-date" })
×
117
   end
118
   return SILE.scratch.bibtex.engine
×
119
end
120

121
--- Retrieve an entry and mark it as cited if it is not already.
122
-- The citation key is taken from the options, or from the content if not provided.
123
-- @tparam table options Options
124
-- @tparam table content Content
125
-- @tparam boolean warn_uncited Warn if the entry is not cited yet
126
-- @treturn table Bibliography entry
NEW
127
function package:getEntryForCite (options, content, warn_uncited)
×
128
   if not options.key then
×
129
      options.key = SU.ast.contentToString(content)
×
130
   end
131
   local entry = resolveEntry(SILE.scratch.bibtex.bib, options.key)
×
132
   if not entry then
×
133
      return
×
134
   end
135
   -- Keep track of cited entries
136
   local citnum = SILE.scratch.bibtex.cited.citnums[options.key]
×
137
   if not citnum then
×
138
      if warn_uncited then
×
139
         SU.warn("Reference to a non-cited entry " .. options.key)
×
140
      end
141
      -- Make it cited
142
      table.insert(SILE.scratch.bibtex.cited.keys, options.key)
×
143
      citnum = #SILE.scratch.bibtex.cited.keys
×
144
      SILE.scratch.bibtex.cited.citnums[options.key] = citnum
×
145
   end
146
   return entry, citnum
×
147
end
148

149
--- Retrieve a locator from the options.
150
-- @tparam table options Options
151
-- @treturn table Locator
NEW
152
function package:getLocator (options)
×
153
   local locator
154
   for k, v in pairs(options) do
×
155
      if k ~= "key" then
×
156
         if not locators[k] then
×
157
            SU.warn("Unknown option '" .. k .. "' in \\cite")
×
158
         else
159
            if not locator then
×
160
               local label = locators[k]
×
161
               locator = { label = label, value = v }
×
162
            else
163
               SU.warn("Multiple locators in \\cite, using the first one")
×
164
            end
165
         end
166
      end
167
   end
168
   return locator
×
169
end
170

171
function package:registerCommands ()
×
172
   self:registerCommand("loadbibliography", function (options, _)
×
173
      local file = SU.required(options, "file", "loadbibliography")
×
174
      parseBibtex(file, SILE.scratch.bibtex.bib)
×
175
   end)
176

177
   self:registerCommand("nocite", function (options, content)
×
178
      self:getEntryForCite(options, content, false)
×
179
   end, "Mark an entry as cited without actually producing a citation.")
×
180

181
   -- LEGACY COMMANDS
182

183
   self:registerCommand("bibstyle", function (_, _)
×
184
      SU.deprecated("\\bibstyle", "\\set[parameter=bibtex.style]", "0.13.2", "0.14.0")
×
185
   end)
186

187
   self:registerCommand("cite", function (options, content)
×
188
      local style = SILE.settings:get("bibtex.style")
×
189
      if style == "csl" then
×
190
         SILE.call("csl:cite", options, content)
×
191
         return -- done via CSL
×
192
      end
193
      if not self._deprecated_legacy_warning then
×
194
         self._deprecated_legacy_warning = true
×
195
         SU.warn("Legacy bibtex.style is deprecated, consider enabling the CSL implementation.")
×
196
      end
197
      local entry = self:getEntryForCite(options, content, false)
×
198
      if entry then
×
199
         local bibstyle = require("packages.bibtex.styles." .. style)
×
200
         local cite = Bibliography.produceCitation(options, SILE.scratch.bibtex.bib, bibstyle)
×
201
         SILE.processString(("<sile>%s</sile>"):format(cite), "xml")
×
202
      end
203
   end, "Produce a single citation.")
×
204

205
   self:registerCommand("reference", function (options, content)
×
206
      local style = SILE.settings:get("bibtex.style")
×
207
      if style == "csl" then
×
208
         SILE.call("csl:reference", options, content)
×
209
         return -- done via CSL
×
210
      end
211
      if not self._deprecated_legacy_warning then
×
212
         self._deprecated_legacy_warning = true
×
213
         SU.warn("Legacy bibtex.style is deprecated, consider enabling the CSL implementation.")
×
214
      end
215
      local entry = self:getEntryForCite(options, content, true)
×
216
      if entry then
×
217
         local bibstyle = require("packages.bibtex.styles." .. style)
×
218
         local cite, err = Bibliography.produceReference(options, SILE.scratch.bibtex.bib, bibstyle)
×
219
         if cite == Bibliography.Errors.UNKNOWN_TYPE then
×
220
            SU.warn("Unknown type @" .. err .. " in citation for reference " .. options.key)
×
221
            return
×
222
         end
223
         SILE.processString(("<sile>%s</sile>"):format(cite), "xml")
×
224
      end
225
   end, "Produce a single bibliographic reference.")
×
226

227
   -- CSL IMPLEMENTATION COMMANDS
228

229
   -- Hooks for CSL processing
230

231
   self:registerCommand("bibSmallCaps", function (_, content)
×
232
      -- To avoid attributes in the CSL-processed content
233
      SILE.call("font", { features = "+smcp" }, content)
×
234
   end)
235

236
   self:registerCommand("bibSuperScript", function (_, content)
×
237
      -- Superscripted content from CSL may contain characters that are not
238
      -- available in the font even with +sups.
239
      -- E.g. ACS style uses superscripted numbers for references, but also
240
      -- comma-separated lists of numbers, or ranges with an en-dash.
241
      -- We want to be consistent between all these cases, so we always
242
      -- use fake superscripts.
243
      SILE.call("textsuperscript", { fake = true }, content)
×
244
   end)
245

246
   -- CSL 1.0.2 appendix VI
247
   -- "If the bibliography entry for an item renders any of the following
248
   -- identifiers, the identifier should be anchored as a link, with the
249
   -- target of the link as follows:
250
   --   url: output as is
251
   --   doi: prepend with “https://doi.org/”
252
   --   pmid: prepend with “https://www.ncbi.nlm.nih.gov/pubmed/”
253
   --   pmcid: prepend with “https://www.ncbi.nlm.nih.gov/pmc/articles/”
254
   -- NOT IMPLEMENTED:
255
   --   "Citation processors should include an option flag for calling
256
   --   applications to disable bibliography linking behavior."
257
   -- (But users can redefine these commands to their liking...)
258
   self:registerCommand("bibLink", function (options, content)
×
259
      SILE.call("href", { src = options.src }, {
×
260
         SU.ast.createCommand("url", {}, { content[1] }),
×
261
      })
262
   end)
263
   self:registerCommand("bibURL", function (_, content)
×
264
      local link = content[1]
×
265
      if not link:match("^https?://") then
×
266
         -- Play safe
267
         link = "https://" .. link
×
268
      end
269
      SILE.call("bibLink", { src = link }, content)
×
270
   end)
271
   self:registerCommand("bibDOI", function (_, content)
×
272
      local link = content[1]
×
273
      if not link:match("^https?://") then
×
274
         link = "https://doi.org/" .. link
×
275
      end
276
      SILE.call("bibLink", { src = link }, content)
×
277
   end)
278
   self:registerCommand("bibPMID", function (_, content)
×
279
      local link = content[1]
×
280
      if not link:match("^https?://") then
×
281
         link = "https://www.ncbi.nlm.nih.gov/pubmed/" .. link
×
282
      end
283
      SILE.call("bibLink", { src = link }, content)
×
284
   end)
285
   self:registerCommand("bibPMCID", function (_, content)
×
286
      local link = content[1]
×
287
      if not link:match("^https?://") then
×
288
         link = "https://www.ncbi.nlm.nih.gov/pmc/articles/" .. link
×
289
      end
290
      SILE.call("bibLink", { src = link }, content)
×
291
   end)
292

293
   self:registerCommand("bibRule", function (_, content)
×
294
      local n = content[1] and tonumber(content[1]) or 3
×
295
      local width = n .. "em"
×
296
      SILE.call("raise", { height = "0.4ex" }, function ()
×
297
         SILE.call("hrule", { height = "0.4pt", width = width })
×
298
      end)
299
   end)
300

301
   self:registerCommand("bibBoxForIndent", function (_, content)
×
302
      local hbox = SILE.typesetter:makeHbox(content)
×
303
      local margin = SILE.types.length(SILE.settings:get("bibliography.indent"):absolute())
×
304
      if hbox.width > margin then
×
305
         SILE.typesetter:pushHbox(hbox)
×
306
         SILE.typesetter:typeset(" ")
×
307
      else
308
         hbox.width = margin
×
309
         SILE.typesetter:pushHbox(hbox)
×
310
      end
311
   end)
312

313
   -- Style and locale loading
314

315
   self:registerCommand("bibliographystyle", function (options, _)
×
316
      local sty = SU.required(options, "style", "bibliographystyle")
×
317
      local style = loadCslStyle(sty)
×
318
      -- FIXME: lang is mandatory until we can map document.lang to a resolved
319
      -- BCP47 with region always present, as this is what CSL locales require.
320
      if not options.lang then
×
321
         -- Pick the default locale from the style, if any
322
         options.lang = style.globalOptions["default-locale"]
×
323
      end
324
      local lang = SU.required(options, "lang", "bibliographystyle")
×
325
      local locale = loadCslLocale(lang)
×
326
      SILE.scratch.bibtex.engine = CslEngine(style, locale, {
×
327
         localizedPunctuation = SU.boolean(options.localizedPunctuation, false),
328
         italicExtension = SU.boolean(options.italicExtension, true),
329
         mathExtension = SU.boolean(options.mathExtension, true),
330
      })
331
   end)
332

333
   self:registerCommand("csl:cite", function (options, content)
×
334
      local entry, citnum = self:getEntryForCite(options, content, false)
×
335
      if entry then
×
336
         local engine = self:getCslEngine()
×
337
         local locator = self:getLocator(options)
×
338

339
         local cslentry = bib2csl(entry, citnum)
×
340
         cslentry.locator = locator
×
341
         local cite = engine:cite(cslentry)
×
342

343
         SILE.processString(("<sile>%s</sile>"):format(cite), "xml")
×
344
      end
345
   end, "Produce a single citation.")
×
346

347
   self:registerCommand("cites", function (_, content)
×
348
      if type(content) ~= "table" then
×
349
         SU.error("Table content expected in \\cites")
×
350
      end
351
      local cites = {}
×
352
      for i = 1, #content do
×
353
         if type(content[i]) == "table" then
×
354
            local c = content[i]
×
355
            if c.command ~= "cite" then
×
356
               SU.error("Only \\cite commands are allowed in \\cites")
×
357
            end
358
            local o = c.options
×
359
            local entry, citnum = self:getEntryForCite(o, c, false)
×
360
            if entry then
×
361
               local locator = self:getLocator(o)
×
362
               local csljson = bib2csl(entry, citnum)
×
363
               csljson.locator = locator
×
364
               cites[#cites + 1] = csljson
×
365
            end
366
         end
367
      end
368
      if #cites > 0 then
×
369
         local engine = self:getCslEngine()
×
370
         local cite = engine:cite(cites)
×
371
         SILE.processString(("<sile>%s</sile>"):format(cite), "xml")
×
372
      end
373
   end, "Produce a group of citations.")
×
374

375
   self:registerCommand("csl:reference", function (options, content)
×
376
      local entry, citnum = self:getEntryForCite(options, content, true)
×
377
      if entry then
×
378
         local engine = self:getCslEngine()
×
379

380
         local cslentry = bib2csl(entry, citnum)
×
381
         local cite = engine:reference(cslentry)
×
382

383
         SILE.processString(("<sile>%s</sile>"):format(cite), "xml")
×
384
      end
385
   end, "Produce a single bibliographic reference.")
×
386

387
   self:registerCommand("printbibliography", function (options, _)
×
388
      local bib
389
      if SU.boolean(options.cited, true) then
×
390
         bib = {}
×
391
         for _, key in ipairs(SILE.scratch.bibtex.cited.keys) do
×
392
            bib[key] = SILE.scratch.bibtex.bib[key]
×
393
         end
394
      else
395
         bib = SILE.scratch.bibtex.bib
×
396
      end
397

398
      local entries = {}
×
399
      local ncites = #SILE.scratch.bibtex.cited.keys
×
400
      for key, entry in pairs(bib) do
×
401
         if entry.type ~= "xdata" then
×
402
            crossrefAndXDataResolve(bib, entry)
×
403
            if entry then
×
404
               local citnum = SILE.scratch.bibtex.cited.citnums[key]
×
405
               if not citnum then
×
406
                  -- This is just to make happy CSL styles that require a citation number
407
                  -- However, table order is not guaranteed in Lua so the output may be
408
                  -- inconsistent across runs with styles that use this number for sorting.
409
                  -- This may only happen for non-cited entries in the bibliography, and it
410
                  -- would be a bad practice to use such a style to print the full bibliography,
411
                  -- so I don't see a strong need to fix this at the expense of performance.
412
                  -- (and we can't really, some styles might have several sorting criteria
413
                  -- leading to unpredictable order anyway).
414
                  ncites = ncites + 1
×
415
                  citnum = ncites
×
416
               end
417
               local cslentry = bib2csl(entry, citnum)
×
418
               table.insert(entries, cslentry)
×
419
            end
420
         end
421
      end
422

423
      print("<bibliography: " .. #entries .. " entries>")
×
424
      if not SILE.typesetter:vmode() then
×
425
         SILE.call("par")
×
426
      end
427
      local engine = self:getCslEngine()
×
428
      local cite = engine:reference(entries)
×
429
      SILE.settings:temporarily(function ()
×
430
         local hanging_indent = SU.boolean(engine.bibliography.options["hanging-indent"], false)
×
431
         local must_align = engine.bibliography.options["second-field-align"]
×
432
         local lskip = (SILE.settings:get("document.lskip") or SILE.types.node.glue()):absolute()
×
433
         if hanging_indent or must_align then
×
434
            -- Respective to the fixed part of the current lskip, all lines are indented
435
            -- but the first one.
436
            local indent = SILE.settings:get("bibliography.indent"):absolute()
×
437
            SILE.settings:set("document.lskip", lskip.width + indent)
×
438
            SILE.settings:set("document.parindent", -indent)
×
439
            SILE.settings:set("current.parindent", -indent)
×
440
         else
441
            -- Fixed part of the current lskip, and no paragraph indentation
442
            SILE.settings:set("document.lskip", lskip.width)
×
443
            SILE.settings:set("document.parindent", SILE.types.length())
×
444
            SILE.settings:set("current.parindent", SILE.types.length())
×
445
         end
446
         SILE.processString(("<sile>%s</sile>"):format(cite), "xml")
×
447
         SILE.call("par")
×
448
      end)
449

450
      SILE.scratch.bibtex.cited = { keys = {}, citnums = {} }
×
451
   end, "Produce a bibliography of references.")
×
452
end
453

454
package.documentation = [[
455
\begin{document}
456
BibTeX is a citation management system.
457
It was originally designed for TeX but has since been integrated into a variety of situations.
458
This experimental package allows SILE to read and process Bib(La)TeX \code{.bib} files and output citations and full text references.
459

460
\smallskip
461
\noindent
462
\em{Loading a bibliography}
463
\novbreak
464

465
\indent
466
To load a BibTeX file, issue the command \autodoc:command{\loadbibliography[file=<whatever.bib>]}.
467
You can load multiple files, and the entries will be merged into a single bibliography database.
468

469
\smallskip
470
\noindent
471
\em{Producing citations and references (CSL implementation)}
472
\novbreak
473

474
\indent
475
The CSL (Citation Style Language) implementation is more powerful and flexible than the former legacy solution available in earlier versions of this package (see below).
476

477
You should first invoke \autodoc:command{\bibliographystyle[style=<style>, lang=<lang>]}, where \autodoc:parameter{style} is the name of the CSL style file (without the \code{.csl} extension), and \autodoc:parameter{lang} is the language code of the CSL locale to use (e.g., \code{en-US}).
478

479
The command accepts a few additional options:
480

481
\begin{itemize}
482
\item{\autodoc:parameter{localizedPunctuation} (default \code{false}): whether to use localized punctuation – this is non-standard but may be useful when using a style that was not designed for the target language;}
483
\item{\autodoc:parameter{italicExtension} (default \code{true}): whether to convert \code{_text_} to italic text (“à la Markdown”);}
484
\item{\autodoc:parameter{mathExtension} (default \code{true}): whether to recognize \code{$formula$} as math formulae in (a subset of the) TeX-like syntax.}
485
\end{itemize}
486

487
The locale and styles files are searched in the \code{csl/locales} and \code{csl/styles} directories, respectively, in your project directory, or in the Lua package path.
488
For convenience and testing, SILE bundles the \code{chicago-author-date} and \code{chicago-author-date-fr} styles, and the \code{en-US} and \code{fr-FR} locales.
489
If you don’t specify a style or locale, the author-date style and the \code{en-US} locale will be used.
490

491
To produce an inline citation, call \autodoc:command{\cite{<key>}}, which will typeset something like “(Jones 1982)”.
492
If you want to cite a particular page number, use \autodoc:command{\cite[page=22]{<key>}}. Other “locator”  options are available (article, chapter, column, line, note, paragraph, section, volume, etc.) – see the CSL documentation for details.
493
Some frequent abbreviations are also supported (art, chap, col, fig…)
494

495
To mark an entry as cited without actually producing a citation, use \autodoc:command{\nocite{<key>}}.
496
This is useful when you want to include an entry in the bibliography without citing it in the text.
497

498
To generate multiple citations grouped correctly, use \autodoc:command{\cites{\cite{<key1>} \cite{<key2>}, …}}.
499
This wrapper command only accepts \autodoc:command{\cite} elements following their standard syntax.
500
Any other element triggers an error, and any text content is silently ignored.
501

502
To produce a bibliography of cited references, use \autodoc:command{\printbibliography}.
503
After printing the bibliography, the list of cited entries will be cleared. This allows you to start fresh for subsequent uses (e.g., in a different chapter).
504
If you want to include all entries in the bibliography, not just those that have been cited, set the option \autodoc:parameter{cited} to false.
505

506
To produce a bibliographic reference, use \autodoc:command{\reference{<key>}}.
507
Note that this command is not intended for actual use, but for testing purposes.
508
It may be removed in the future.
509

510
\smallskip
511
\noindent
512
\em{Producing citations and references (legacy commands)}
513
\novbreak
514

515
\indent
516
The “legacy” implementation is based on a custom rendering system.
517
The plan is to eventually deprecate and remove it, as the CSL implementation covers more use cases and is more powerful.
518

519
The \autodoc:setting[check=false]{bibtex.style} setting controls the style of the bibliography.
520
It may be set, for instance, to \code{chicago}, the only style supported out of the box.
521
(By default, it is set to \code{csl} to enforce the use of the CSL implementation.)
522

523
To produce an inline citation, call \autodoc:command{\cite{<key>}}, which will typeset something like “Jones 1982”.
524
If you want to cite a particular page number, use \autodoc:command{\cite[page=22]{<key>}}.
525

526
To produce a bibliographic reference, use \autodoc:command{\reference{<key>}}.
527

528
This implementation doesn’t currently produce full bibliography listings.
529
(Actually, you can use the \autodoc:command{\printbibliography} introduced above, but then it always uses the CSL implementation for rendering the bibliography, differing from the output of the \autodoc:command{\reference} command.)
530

531
\smallskip
532
\noindent
533
\em{Notes on the supported BibTeX syntax}
534
\novbreak
535

536
\indent
537
The BibTeX file format is a plain text format for bibliographies.
538

539
The \code{@type\{…\}} syntax is used to specify an entry, where \code{type} is the type of the entry, and is case-insensitive.
540
Any content outside entries is ignored.
541

542
The \code{@preamble} and \code{@comment} special entries are ignored.
543
The former is specific to TeX-based systems, and the latter is a comment (everything between the balanced braces is ignored).
544

545
The \code{@string\{key=value\}} special entry is used to define a string or “abbreviation,” for use in other subsequent entries.
546

547
The \code{@xdata} entry is used to define an entry that can be used as a reference in other entries.
548
Such entries are not printed in the bibliography.
549
Normally, they cannot be cited directly.
550
In this implementation, a warning is raised if they are; but as they have no known type, their formatting is not well-defined, and might not be meaningful.
551

552
Regular bibliography entries have the following syntax:
553

554
\begin[type=autodoc:codeblock]{raw}
555
@type{key,
556
  field1 = value1,
557
  field2 = value2,
558
  …
559
}
560
\end{raw}
561

562
The entry key is a unique identifier for the entry, and is case-sensitive.
563
Entries consist of fields, which are key-value pairs.
564
The field names are case-insensitive.
565
Spaces and line breaks are not important, except for readability.
566
On the contrary, commas are compulsory between any two fields of an entry.
567

568
String values shall be enclosed in either double quotes or curly braces.
569
The latter allows using quotes inside the string, while the former does not without escaping them with a backslash.
570

571
When string values are not enclosed in quotes or braces, they must not contain any whitespace characters.
572
The value is then considered to be a reference to an abbreviation previously defined in a \code{@string} entry.
573
If no such abbreviation is found, the value is considered to be a string literal.
574
(This allows a decent fallback for fields where curly braces or double quotes could historically be omitted, such as numerical values, and one-word strings.)
575

576
String values are assumed to be in the UTF-8 encoding, and shall not contain (La)TeX commands.
577
Special character sequences from TeX (such as \code{`} assumed to be an opening quote) are not supported.
578
There are exceptions to this rule.
579
Notably, the \code{~} character can be used to represent a non-breaking space (when not backslash-escaped), and the \code{\\&} sequence is accepted (though this implementation does not mandate escaping ampersands).
580
With the CSL renderer, see also the non-standard extensions above.
581

582
Values can also be composed by concatenating strings, using the \code{#} character.
583

584
Besides using string references, entries have two other \em{parent-child} inheritance mechanisms allowing to reuse fields from other entries, without repeating them: the \code{crossref} and \code{xdata} fields.
585

586
The \code{crossref} field is used to reference another entry by its key.
587
The \code{xdata} field accepts a comma-separated list of keys of entries that are to be inherited.
588

589
Some BibTeX implementations automatically include entries referenced with the \code{crossref} field in the bibliography, when a certain threshold is met.
590
This implementation does not do that.
591

592
Depending on the types of the parent and child entries, the child entry may inherit some or all fields from the parent entry, and some inherited fields may be reassigned in the child entry.
593
For instance, the \code{title} in a \code{@collection} entry is inherited as the \code{booktitle} field in a \code{@incollection} child entry.
594
Some BibTeX implementations allow configuring the data inheritance behavior, but this implementation does not.
595
It is also currently quite limited on the fields that are reassigned, and only provides a subset of the mappings defined in the BibLaTeX manual, appendix B.
596

597
Here is an example of a BibTeX file showing some of the abovementioned features:
598

599
\begin[type=autodoc:codeblock]{raw}
600
@string{JIT = "Journal of Interesting Things"}
601
...
602
This text is ignored
603
...
604
@xdata{jit-vol1-iss2,
605
  journal = JIT # { (JIT)},
606
  year    = {2020},
607
  month   = {jan},
608
  volume  = {1},
609
  number  = {2},
610
}
611
@article{my-article,
612
  author  = {Doe, John and Smith, Jane}
613
  title   = {Theories & Practices},
614
  xdata   = {jit-1-2},
615
  pages   = {100--200},
616
}
617
\end{raw}
618

619
Some fields have a special syntax.
620
The \code{author}, \code{editor} and \code{translator} fields accept a list of names, separated by the keyword \code{and}.
621
The legacy \code{month} field accepts a three-letter abbreviation for the month in English, or a number from 1 to 12.
622
The more powerful \code{date} field accepts a date-time following the ISO 8601-2 Extended Date/Time Format specification level 1 (such as \code{YYYY-MM-DD}, or a date range \code{YYYY-MM-DD/YYYY-MM-DD}, and more).
623
\end{document}
624
]]
×
625

626
return package
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc