diff --git a/csl/core/engine.lua b/csl/core/engine.lua
index 745e04348..da26aa24e 100644
--- a/csl/core/engine.lua
+++ b/csl/core/engine.lua
@@ -13,6 +13,7 @@
--
-- THINGS NOT DONE
-- - disambiguation logic (not done at all)
+-- - collapse logic in citations (not done at all)
-- - other FIXME/TODOs in the code on specific features
--
-- luacheck: no unused args
@@ -875,7 +876,7 @@ function CslEngine:_names (options, content, entry)
local name_delimiter = name_node.options.delimiter or inherited_opts["names-delimiter"]
-- local delimiter_precedes_et_al = name_node.options["delimiter-precedes-et-al"] -- TODO NOT IMPLEMENTED
- if not self.cache[name_delimiter] then
+ if name_delimiter and not self.cache[name_delimiter] then
name_delimiter = self:_xmlEscape(name_delimiter)
self.cache[name_delimiter] = name_delimiter
end
@@ -885,7 +886,7 @@ function CslEngine:_names (options, content, entry)
et_al_min = et_al_min,
et_al_use_first = et_al_use_first,
and_word = and_word,
- name_delimiter = self.cache[name_delimiter],
+ name_delimiter = name_delimiter and self.cache[name_delimiter],
is_label_first = is_label_first,
label_opts = label_opts,
et_al_opts = et_al_opts,
@@ -1083,9 +1084,10 @@ function CslEngine:_key (options, content, entry)
end
-- FIXME: A bit ugly: When implementing SU.collatedSort, I didn't consider
--- sorting structured tables, so I need to go low level here.
+-- sorting structured tables, so we need to go low level here.
-- Moreover, I made icu.compare return a boolean, so we have to pay twice
-- the comparison cost to check equality...
+-- See PR #2105
local icu = require("justenoughicu")
function CslEngine:_sort (options, content, entries)
@@ -1116,14 +1118,32 @@ function CslEngine:_sort (options, content, entries)
local lang = self.locale.lang
local collator = icu.collation_create(lang, {})
table.sort(entries, function (a, b)
+ if (a["citation-key"] == b["citation-key"]) then
+ -- Lua can invoke the comparison function with the same entry.
+ -- Really! Due to the way it handles it pivot on partitioning.
+ -- Shortcut the inner keys comparison in that case.
+ return false
+ end
+ -- NOT IMPLEMENTED (not bothering for now):
+ -- "Items with an empty sort key value are placed at the end of the sort,
+ -- both for ascending and descending sorts."
local ak = a._keys
local bk = b._keys
for i = 1, math.min(#ak, #bk) do
- if ak[i] ~= bk[i] then -- See comment, ugly inequality check)
- return icu.compare(collator, ak[i], bk[i])
+ if ak[i] ~= bk[i] then -- HACK: See comment above, ugly inequality check
+ local cmp = icu.compare(collator, ak[i], bk[i])
+ if type(cmp) == "number" then
+ return cmp < 0 -- To keep on working whenever PR #2105 lands
+ end
+ return cmp
end
end
- return false
+ -- If we reach this point, the keys are equal.
+ -- Probably unlikely in real life, and not mentioned in the CSL spec
+ -- unless I missed it. Let's fallback to the citation order, so at
+ -- least cited entries are ordered predictably.
+ SU.warn("CSL sort keys are equal for " .. a["citation-key"] .. " and " .. b["citation-key"])
+ return a["citation-number"] < b["citation-number"]
end)
icu.collation_destroy(collator)
end
@@ -1212,6 +1232,28 @@ function CslEngine:_process (entries, mode)
self.sorting = true
self:_sort(sort.options, sort, entries)
self.sorting = false
+ else
+ -- The CSL specification says:
+ -- "In the absence of cs:sort, cites and bibliographic entries appear in
+ -- the order in which they are cited."
+ -- The wording is ambiguous!
+ -- We tracked the first citation number in 'citation-number', so for
+ -- the bibliography, using it makes sense.
+ -- For citations, we use the exact order of the input. Consider a cite
+ -- (work1, work2) and a subsequent cite (work2, work1). The order of
+ -- the bibliography should be (work1, work2), but the order of the cites
+ -- should be (work1, work2) and (work2, work1) respectively.
+ -- It seeems to be the case: Some styles (ex. American Chemical Society)
+ -- have an explicit sort by 'citation-number' in the citations section,
+ -- which would be useless if that order was impplied.
+ if mode == "bibliography" then
+ table.sort(entries, function (e1, e2)
+ if not e1["citation-number"] or not e2["citation-number"] then
+ return false; -- Safeguard?
+ end
+ return e1["citation-number"] < e2["citation-number"]
+ end)
+ end
end
local res = self:_render_children(ast, entries)
diff --git a/packages/bibtex/init.lua b/packages/bibtex/init.lua
index 64cae50ed..a494d7ccb 100644
--- a/packages/bibtex/init.lua
+++ b/packages/bibtex/init.lua
@@ -195,7 +195,7 @@ local function parseBibtex (fn, biblio)
for i = 1, #t do
if t[i].id == "entry" then
local ent = t[i][1]
- local entry = { type = ent.type, attributes = ent[1] }
+ local entry = { type = ent.type, label = ent.label, attributes = ent[1] }
if biblio[ent.label] then
SU.warn("Duplicate entry key '" .. ent.label .. "', picking the last one")
end
@@ -301,7 +301,7 @@ end
function package:_init ()
base._init(self)
- SILE.scratch.bibtex = { bib = {} }
+ SILE.scratch.bibtex = { bib = {}, cited = { keys = {}, citnums = {} } }
Bibliography = require("packages.bibtex.bibliography")
-- For DOI, PMID, PMCID and URL support.
self:loadPackage("url")
@@ -457,8 +457,6 @@ function package:registerCommands ()
-- - multiple citation keys
if not SILE.scratch.bibtex.engine then
SILE.call("bibliographystyle", { lang = "en-US", style = "chicago-author-date" })
- -- SILE.call("bibliographystyle", { lang = "en-US", style = "chicago-fullnote-bibliography" })
- -- SILE.call("bibliographystyle", { lang = "en-US", style = "apa" })
end
local engine = SILE.scratch.bibtex.engine
if not options.key then
@@ -469,7 +467,12 @@ function package:registerCommands ()
return
end
- local csljson = bib2csl(entry)
+ -- Keep track of cited entries
+ table.insert(SILE.scratch.bibtex.cited.keys, options.key)
+ local citnum = #SILE.scratch.bibtex.cited.keys
+ SILE.scratch.bibtex.cited.citnums[options.key] = citnum
+
+ local csljson = bib2csl(entry, citnum)
-- csljson.locator = { -- EXPERIMENTAL
-- label = "page",
-- value = "123-125"
@@ -482,8 +485,6 @@ function package:registerCommands ()
self:registerCommand("csl:reference", function (options, content)
if not SILE.scratch.bibtex.engine then
SILE.call("bibliographystyle", { lang = "en-US", style = "chicago-author-date" })
- -- SILE.call("bibliographystyle", { lang = "en-US", style = "chicago-fullnote-bibliography" })
- -- SILE.call("bibliographystyle", { lang = "en-US", style = "apa" })
end
local engine = SILE.scratch.bibtex.engine
if not options.key then
@@ -494,27 +495,56 @@ function package:registerCommands ()
return
end
- local cslentry = bib2csl(entry)
+ local citnum = SILE.scratch.bibtex.cited.citnums[options.key]
+ if not citnum then
+ SU.warn("Reference to a non-cited entry " .. options.key)
+ -- Make it cited
+ table.insert(SILE.scratch.bibtex.cited.keys, options.key)
+ citnum = #SILE.scratch.bibtex.cited.keys
+ SILE.scratch.bibtex.cited.citnums[options.key] = citnum
+ end
+ local cslentry = bib2csl(entry, citnum)
local cite = engine:reference(cslentry)
SILE.processString(("%s"):format(cite), "xml")
end)
- self:registerCommand("printbibliography", function (_, _)
+ self:registerCommand("printbibliography", function (options, _)
if not SILE.scratch.bibtex.engine then
SILE.call("bibliographystyle", { lang = "en-US", style = "chicago-author-date" })
- -- SILE.call("bibliographystyle", { lang = "en-US", style = "chicago-fullnote-bibliography" })
- -- SILE.call("bibliographystyle", { lang = "en-US", style = "apa" })
end
local engine = SILE.scratch.bibtex.engine
- local bib = SILE.scratch.bibtex.bib
+ local bib
+ if SU.boolean(options.cited, true) then
+ bib = {}
+ for _, key in ipairs(SILE.scratch.bibtex.cited.keys) do
+ bib[key] = SILE.scratch.bibtex.bib[key]
+ end
+ else
+ bib = SILE.scratch.bibtex.bib
+ end
+
local entries = {}
- for _, entry in pairs(bib) do
+ local ncites = #SILE.scratch.bibtex.cited.keys
+ for key, entry in pairs(bib) do
if entry.type ~= "xdata" then
crossrefAndXDataResolve(bib, entry)
if entry then
- local cslentry = bib2csl(entry)
+ local citnum = SILE.scratch.bibtex.cited.citnums[key]
+ if not citnum then
+ -- This is just to make happy CSL styles that require a citation number
+ -- However, table order is not guaranteed in Lua so the output may be
+ -- inconsistent across runs with styles that use this number for sorting.
+ -- This may only happen for non-cited entries in the bibliography, and it
+ -- would be a bad practive to use such a style to print the full bibliography,
+ -- so I don't see a strong need to fix this at the expense of performance.
+ -- (and we can't really, some styles might have several sorting criteria
+ -- leading to impredictable order anyway).
+ ncites = ncites + 1
+ citnum = ncites
+ end
+ local cslentry = bib2csl(entry, citnum)
table.insert(entries, cslentry)
end
end
@@ -522,6 +552,8 @@ function package:registerCommands ()
print("")
local cite = engine:reference(entries)
SILE.processString(("%s"):format(cite), "xml")
+
+ SILE.scratch.bibtex.cited = { keys = {}, citnums = {} }
end)
end
@@ -531,7 +563,6 @@ BibTeX is a citation management system.
It was originally designed for TeX but has since been integrated into a variety of situations.
This experimental package allows SILE to read and process BibTeX \code{.bib} files and output citations and full text references.
-(It doesn’t currently produce full bibliography listings.)
To load a BibTeX file, issue the command \autodoc:command{\loadbibliography[file=]}
@@ -544,9 +575,9 @@ To load a BibTeX file, issue the command \autodoc:command{\loadbibliography[file
To produce an inline citation, call \autodoc:command{\cite{}}, which will typeset something like “Jones 1982”.
If you want to cite a particular page number, use \autodoc:command{\cite[page=22]{}}.
-To produce a full reference, use \autodoc:command{\reference{}}.
+To produce a bibliographic reference, use \autodoc:command{\reference{}}.
-Currently, the only supported bibliography style is Chicago referencing.
+This implementation doesn’t currently produce full bibliography listings, and the only supported bibliography style is Chicago referencing.
\smallskip
\noindent
@@ -556,7 +587,7 @@ Currently, the only supported bibliography style is Chicago referencing.
\indent
While an experimental work-in-progress, the CSL (Citation Style Language) implementation is more powerful and flexible than the legacy commands.
-You must first invoke \autodoc:command{\bibliographystyle[style=