summaryrefslogtreecommitdiff
path: root/Master/texmf-dist
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2024-09-15 19:57:10 +0000
committerKarl Berry <karl@freefriends.org>2024-09-15 19:57:10 +0000
commit53b0a5c02bb3face43d87688c11529571b8a5f7d (patch)
treefab9407e8a82cf21642859ebe9e93b0417edb881 /Master/texmf-dist
parent64d95f88768a1dde4edfda5e2b69a57251783f5b (diff)
citation-style-language (15sep24)
git-svn-id: svn://tug.org/texlive/trunk@72292 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist')
-rw-r--r--Master/texmf-dist/doc/latex/citation-style-language/CHANGELOG.md9
-rw-r--r--Master/texmf-dist/doc/latex/citation-style-language/citation-style-language-doc.pdfbin240574 -> 240563 bytes
-rw-r--r--Master/texmf-dist/doc/latex/citation-style-language/citation-style-language-doc.tex2
-rw-r--r--Master/texmf-dist/doc/man/man1/citeproc-lua.12
-rw-r--r--Master/texmf-dist/doc/man/man1/citeproc-lua.man1.pdfbin15012 -> 14936 bytes
-rw-r--r--Master/texmf-dist/scripts/citation-style-language/citeproc-bibtex-data.lua10
-rw-r--r--Master/texmf-dist/scripts/citation-style-language/citeproc-engine.lua841
-rw-r--r--Master/texmf-dist/scripts/citation-style-language/citeproc-manager.lua115
-rw-r--r--Master/texmf-dist/scripts/citation-style-language/citeproc-node-citation.lua14
-rw-r--r--Master/texmf-dist/scripts/citation-style-language/citeproc-util.lua2
-rwxr-xr-xMaster/texmf-dist/scripts/citation-style-language/citeproc.lua2
-rw-r--r--Master/texmf-dist/tex/latex/citation-style-language/citation-style-language-init.sty4
-rw-r--r--Master/texmf-dist/tex/latex/citation-style-language/citation-style-language.sty2
-rw-r--r--Master/texmf-dist/tex/latex/citation-style-language/locales/csl-locales-de-AT.xml4
-rw-r--r--Master/texmf-dist/tex/latex/citation-style-language/locales/csl-locales-de-CH.xml4
-rw-r--r--Master/texmf-dist/tex/latex/citation-style-language/locales/csl-locales-de-DE.xml4
-rw-r--r--Master/texmf-dist/tex/latex/citation-style-language/styles/apa.csl4
17 files changed, 581 insertions, 438 deletions
diff --git a/Master/texmf-dist/doc/latex/citation-style-language/CHANGELOG.md b/Master/texmf-dist/doc/latex/citation-style-language/CHANGELOG.md
index 4a08dd63029..7a21395f0fa 100644
--- a/Master/texmf-dist/doc/latex/citation-style-language/CHANGELOG.md
+++ b/Master/texmf-dist/doc/latex/citation-style-language/CHANGELOG.md
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
+## [0.6.4] - 2024-09-15
+
+### Fixed
+
+- Refactor `processCitationCluster()` to fix unexpected nil ([#77](https://github.com/zepinglee/citeproc-lua/issues/77)).
+
## [0.6.3] - 2024-08-28
### Added
@@ -241,7 +247,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Initial CTAN release.
-[Unreleased]: https://github.com/zepinglee/citeproc-lua/compare/v0.6.3...HEAD
+[Unreleased]: https://github.com/zepinglee/citeproc-lua/compare/v0.6.4...HEAD
+[0.6.4]: https://github.com/zepinglee/citeproc-lua/compare/v0.6.3...v0.6.4
[0.6.3]: https://github.com/zepinglee/citeproc-lua/compare/v0.6.2...v0.6.3
[0.6.2]: https://github.com/zepinglee/citeproc-lua/compare/v0.6.1...v0.6.2
[0.6.1]: https://github.com/zepinglee/citeproc-lua/compare/v0.6.0...v0.6.1
diff --git a/Master/texmf-dist/doc/latex/citation-style-language/citation-style-language-doc.pdf b/Master/texmf-dist/doc/latex/citation-style-language/citation-style-language-doc.pdf
index 1ca15f70276..5ec6858cc0c 100644
--- a/Master/texmf-dist/doc/latex/citation-style-language/citation-style-language-doc.pdf
+++ b/Master/texmf-dist/doc/latex/citation-style-language/citation-style-language-doc.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/latex/citation-style-language/citation-style-language-doc.tex b/Master/texmf-dist/doc/latex/citation-style-language/citation-style-language-doc.tex
index 385fef912f5..72b5a5cbaf5 100644
--- a/Master/texmf-dist/doc/latex/citation-style-language/citation-style-language-doc.tex
+++ b/Master/texmf-dist/doc/latex/citation-style-language/citation-style-language-doc.tex
@@ -51,7 +51,7 @@
}%
}
-\date{2024-08-28 v0.6.3}
+\date{2024-09-15 v0.6.4}
\maketitle
diff --git a/Master/texmf-dist/doc/man/man1/citeproc-lua.1 b/Master/texmf-dist/doc/man/man1/citeproc-lua.1
index a23c8b7228a..a411466460a 100644
--- a/Master/texmf-dist/doc/man/man1/citeproc-lua.1
+++ b/Master/texmf-dist/doc/man/man1/citeproc-lua.1
@@ -1,4 +1,4 @@
-.TH citeproc-lua 1 "0.6.3"
+.TH citeproc-lua 1 "0.6.4"
.SH NAME
citeproc-lua \- make CSL citations and bibliography for LaTeX
.SH SYNOPSIS
diff --git a/Master/texmf-dist/doc/man/man1/citeproc-lua.man1.pdf b/Master/texmf-dist/doc/man/man1/citeproc-lua.man1.pdf
index 6bbb9759bc3..df8856ea910 100644
--- a/Master/texmf-dist/doc/man/man1/citeproc-lua.man1.pdf
+++ b/Master/texmf-dist/doc/man/man1/citeproc-lua.man1.pdf
Binary files differ
diff --git a/Master/texmf-dist/scripts/citation-style-language/citeproc-bibtex-data.lua b/Master/texmf-dist/scripts/citation-style-language/citeproc-bibtex-data.lua
index d1fdc99e02d..fc1d04d4f44 100644
--- a/Master/texmf-dist/scripts/citation-style-language/citeproc-bibtex-data.lua
+++ b/Master/texmf-dist/scripts/citation-style-language/citeproc-bibtex-data.lua
@@ -1272,6 +1272,11 @@ return {
source = "apa.dbx",
type = "literal",
},
+ citations = {
+ csl = nil,
+ source = "publist.dbx",
+ type = "literal",
+ },
citedate = {
csl = nil,
source = "seuthesis.bst",
@@ -4205,11 +4210,6 @@ return {
csl = nil,
source = "adrbirthday.bst",
},
- citations = {
- csl = nil,
- source = "publist.dbx",
- type = "literal",
- },
},
macros = {
acmcs = {
diff --git a/Master/texmf-dist/scripts/citation-style-language/citeproc-engine.lua b/Master/texmf-dist/scripts/citation-style-language/citeproc-engine.lua
index 0f5e35c385d..01966fe2193 100644
--- a/Master/texmf-dist/scripts/citation-style-language/citeproc-engine.lua
+++ b/Master/texmf-dist/scripts/citation-style-language/citeproc-engine.lua
@@ -59,6 +59,17 @@ local Position = util.Position
---@field citationItems CitationItem[]
---@field properties CitationProperties
---@field citation_index integer
+---@field sorted_items CitationItem[]
+
+---@class CitationItem
+---@field id CiteId
+---@field prefix string?
+---@field suffix string?
+---@field locator string?
+---@field label string?
+---@field position_level Position?
+---@field near_note boolean?
+---@field num_citations integer?
---@class CitationProperties
---@field noteIndex NoteIndex,
@@ -114,7 +125,6 @@ local Registry = {}
---@field registry Registry
---@field cite_first_note_numbers table<ItemId, NoteIndex>
---@field cite_last_note_numbers table<ItemId, NoteIndex>
----@field note_citations_map table<NoteIndex, CitationId[]>
---@field tainted_item_ids table<ItemId, boolean>
---@field disam_irs IrNode[]
---@field cite_irs_by_output table<string, IrNode[]>
@@ -177,7 +187,6 @@ function CiteProc.new(sys, style, lang, force_lang)
cite_first_note_numbers = {},
cite_last_note_numbers = {},
- note_citations_map = {},
tainted_item_ids = {},
@@ -212,16 +221,6 @@ function CiteProc:get_independent_parent()
return self.style.info.independent_parent
end
-function CiteProc:check_valid_citation_element()
- if not self.style.citation then
- if self.style.info and self.style.info.independent_parent then
- util.error(string.format("This is a dependent style linked to '%s'.", self.style.info.independent_parent))
- else
- util.error('No <citation> in style.')
- end
- end
-end
-
---@param ids CiteId[]
function CiteProc:updateItems(ids)
-- util.debug(string.format('updateItems(%s)', table.concat(ids, ", ")))
@@ -257,7 +256,6 @@ function CiteProc:updateItems(ids)
self.registry.previous_citation = nil
self.cite_first_note_numbers = {}
self.cite_last_note_numbers = {}
- self.note_citations_map = {}
for _, item in ipairs(self.registry.registry) do
item.year_suffix_number = nil
@@ -306,106 +304,214 @@ function CiteProc:updateUncitedItems(uncited_ids)
self.registry.previous_citation = nil
self.cite_first_note_numbers = {}
self.cite_last_note_numbers = {}
- self.note_citations_map = {}
end
----@alias PreCitation [CitationId, NoteIndex, ChapterIndex]
----@alias PostCitation [CitationId, NoteIndex, ChapterIndex]
+---@alias PreCitation [CitationId, NoteIndex, ChapterIndex?]
+---@alias PostCitation [CitationId, NoteIndex, ChapterIndex?]
---@param citation CitationData
----@param citationsPre PreCitation[]
----@param citationsPost PostCitation[]
----@return (table | (integer | string | CitationId)[])[]
-function CiteProc:processCitationCluster(citation, citationsPre, citationsPost)
+---@param citations_pre PreCitation[]
+---@param citations_post PostCitation[]
+---@return [table, [integer, string, CitationId][]]
+function CiteProc:processCitationCluster(citation, citations_pre, citations_post)
-- util.debug(string.format('processCitationCluster(%s)', citation.citationID))
- self:check_valid_citation_element()
- citation = self:normalize_citation_input(citation)
- self:_check_input(citation, citationsPre, citationsPost)
-
- -- Registor citation
- self.registry.citations_by_id[citation.citationID] = citation
+ self:_check_valid_citation_element()
+ citation = self:_normalize_citation_input(citation)
+ self:_check_input(citation, citations_pre, citations_post)
- local citation_note_pairs = {}
- util.extend(citation_note_pairs, citationsPre)
- table.insert(citation_note_pairs, {citation.citationID, citation.properties.noteIndex})
- util.extend(citation_note_pairs, citationsPost)
- -- util.debug(citation_note_pairs)
-
- local citations_by_id = {}
- local citation_list = {}
- for _, pair in ipairs(citation_note_pairs) do
- local citation_id, note_number = table.unpack(pair)
- local citation_ = self.registry.citations_by_id[citation_id]
- if not citation_ then
- util.error("Citation not in registry.")
- end
- citations_by_id[citation_.citationID] = citation_
- table.insert(citation_list, citation_)
+ local citation_list, item_ids = self:_build_reconstituted_citation_list(citation, citations_pre, citations_post)
+ self:updateItems(item_ids)
+ if #citation.sorted_items > 1 and self.style.citation.sort then
+ citation.sorted_items = self.style.citation:sorted_citation_items(citation.citationItems, self)
end
- self.registry.citations_by_id = citations_by_id
- self.registry.citation_list = citation_list
- -- update self.registry.citations_by_item_id
- local item_ids = {}
- self.registry.citations_by_item_id = {}
- for _, citation_ in ipairs(citation_list) do
- for _, cite_item in ipairs(citation_.citationItems) do
- if not self.registry.citations_by_item_id[cite_item.id] then
- self.registry.citations_by_item_id[cite_item.id] = {}
- table.insert(item_ids, cite_item.id)
- end
- table.insert(self.registry.citations_by_item_id[cite_item.id], citation_)
- end
- end
- self:updateItems(item_ids)
+ local tainted_citation_ids = self:_set_positions(citation_list)
+
+ tainted_citation_ids[citation.citationID] = true
local params = {
bibchange = false,
citation_errors = {},
}
- local output = {}
+ -- TODO: evaluate params.bibchange
- local tainted_citation_ids = self:get_tainted_citation_ids(citation_note_pairs)
- tainted_citation_ids[citation.citationID] = true
+ local result = self:_rerun_changed_cites(tainted_citation_ids)
- -- Citeproc-js marks all related citations as tainted but I don't think it's
- -- necessary.
- if self.style.class == "note" and self.style.has_disambiguate then
- for _, cite_item in ipairs(citation.citationItems) do
- for _, citation_ in ipairs(self.registry.citations_by_item_id[cite_item.id]) do
- tainted_citation_ids[citation_.citationID] = true
- end
- end
+ return {params, result}
+end
+
+-- A variant of processCitationCluster() for easy use with LaTeX.
+-- It should be run after refreshing the registry (updateItems()) with all items
+---@param citation CitationData
+---@return string
+function CiteProc:process_citation(citation)
+ -- util.debug(string.format('process_citation(%s)', citation.citationID))
+ self:_check_valid_citation_element()
+ citation = self:_normalize_citation_input(citation)
+
+ local citations_pre = {}
+ for _, citation_ in ipairs(self.registry.citation_list) do
+ table.insert(citations_pre, {citation_.citationID, citation_.properties.noteIndex})
end
+ self:_check_input(citation, citations_pre, {})
- -- util.debug(tainted_citation_ids)
+ local citation_list, item_ids = self:_build_reconstituted_citation_list(citation, citations_pre, {})
+ -- self:updateItems(item_ids)
+ for i, cite_item in ipairs(citation.citationItems) do
+ self:get_item(cite_item.id)
+ end
+ if #citation.sorted_items > 1 and self.style.citation.sort then
+ citation.sorted_items = self.style.citation:sorted_citation_items(citation.citationItems, self)
+ end
- -- params.bibchange = #tainted_citation_ids > 0
- for citation_id, _ in pairs(tainted_citation_ids) do
- local citation_ = self.registry.citations_by_id[citation_id]
+ self:_set_positions(citation_list)
+ local tainted_citation_ids = {[citation.citationID] = true}
+ local result = self:_rerun_changed_cites(tainted_citation_ids)
+ return result[1][2]
+end
- local citation_index = citation_.citation_index
+function CiteProc:makeCitationCluster(citation_items)
+ local special_form = nil
+ local items = {}
- local mode = citation_.properties.mode
- if mode == "suppress-author" and self.style.class == "note" then
- mode = nil
+ for i, cite_item in ipairs(citation_items) do
+ cite_item = self:_normalize_cite_item(cite_item)
+ local item_data = self:get_item(cite_item.id)
+
+ -- Create a wrapper of the orignal item from registry so that
+ -- it may hold different `locator` or `position` values for cites.
+ local cite_item = setmetatable(cite_item, {__index = item_data})
+
+ if not special_form then
+ for _, form in ipairs({"author-only", "suppress-author", "coposite"}) do
+ if cite_item[form] then
+ special_form = form
+ end
+ end
end
- local citation_element = self.style.citation
- if mode == "author-only" and self.style.intext then
- citation_element = self.style.intext
- elseif mode == "full-cite" then
- citation_element = self.style.full_citation
+
+ -- Set "first-reference-note-number" variable when called from
+ -- processCitationCluster() > updateItems()
+ local citations = self.registry.citations_by_item_id[cite_item.id]
+ if citations and #citations > 0 then
+ cite_item["first-reference-note-number"] = citations[1].properties.noteIndex
end
- local citation_str = citation_element:build_citation_str(citation_, self)
- table.insert(output, {citation_index, citation_str, citation_id})
+ cite_item.position_level = Position.First
+ if self.cite_first_note_numbers[cite_item.id] then
+ cite_item.position_level = Position.Subsequent
+ else
+ self.cite_first_note_numbers[cite_item.id] = 0
+ end
+
+ local preceding_cite
+ if i == 1 then
+ local previous_citation = self.registry.previous_citation
+ if previous_citation then
+ if #previous_citation.citationItems == 1 and previous_citation.citationItems[1].id == cite_item.id then
+ preceding_cite = previous_citation.citationItems[1]
+ end
+ end
+ elseif citation_items[i - 1].id == cite_item.id then
+ preceding_cite = citation_items[i - 1]
+ end
+
+ if preceding_cite then
+ cite_item.position_level = self:_get_ibid_position(cite_item, preceding_cite)
+ end
+
+ table.insert(items, cite_item)
+ end
+
+ if self.registry.requires_sorting then
+ self:sort_bibliography()
+ end
+
+ self:_check_valid_citation_element()
+ local citation_element = self.style.citation
+ if special_form == "author-only" and self.style.intext then
+ citation_element = self.style.intext
end
- return {params, output}
+ local res = citation_element:build_cluster(items, self)
+
+ -- local context = {
+ -- build = {},
+ -- engine=self,
+ -- }
+ -- local res = self.style:render_citation(items, context)
+
+ self.registry.previous_citation = {
+ citationID = "pseudo-citation",
+ citationItems = items,
+ properties = {
+ noteIndex = 0,
+ }
+ }
+ return res
end
-function CiteProc:normalize_citation_input(citation)
+---@param bibsection any
+---@return [{[string]: string | number | boolean}, string[]]
+function CiteProc:makeBibliography(bibsection)
+ -- The bibsection works as a filter described in
+ -- <https://citeproc-js.readthedocs.io/en/latest/running.html#selective-output-with-makebibliography>.
+ if not self.style.bibliography then
+ return {{}, {}}
+ end
+
+ local res = {}
+
+ self.registry.widest_label = ""
+ self.registry.maxoffset = 0
+
+ local ids = self:_get_sorted_refs()
+ if bibsection then
+ ids = self:_filter_with_bibsection(ids, bibsection)
+ end
+ for _, id in ipairs(ids) do
+ local str = self.style.bibliography:build_bibliography_str(id, self)
+ table.insert(res, str)
+ end
+
+ local bib_start = self.output_format.markups["bibstart"]
+ local bib_end = self.output_format.markups["bibend"]
+ if type(bib_start) == "function" then
+ bib_start = bib_start(self)
+ end
+ if type(bib_end) == "function" then
+ bib_end = bib_end(self)
+ end
+
+ local params = {
+ hangingindent = self.style.bibliography.hanging_indent,
+ ["second-field-align"] = self.style.bibliography.second_field_align or false,
+ linespacing = self.style.bibliography.line_spacing,
+ entryspacing = self.style.bibliography.entry_spacing,
+ maxoffset = self.registry.maxoffset,
+ widest_label = self.registry.widest_label,
+ bibstart = bib_start,
+ bibend = bib_end,
+ entry_ids = util.clone(self.registry.reflist),
+ }
+
+ return {params, res}
+end
+
+function CiteProc:_check_valid_citation_element()
+ if not self.style.citation then
+ if self.style.info and self.style.info.independent_parent then
+ util.error(string.format("This is a dependent style linked to '%s'.", self.style.info.independent_parent))
+ else
+ util.error('No <citation> in style.')
+ end
+ end
+end
+
+---@param citation CitationData
+---@return CitationData
+function CiteProc:_normalize_citation_input(citation)
citation = util.deep_copy(citation)
if not citation.citationID then
@@ -416,7 +522,7 @@ function CiteProc:normalize_citation_input(citation)
citation.citationItems = {}
end
for i, cite_item in ipairs(citation.citationItems) do
- citation.citationItems[i] = self:normalize_cite_item(cite_item)
+ citation.citationItems[i] = self:_normalize_cite_item(cite_item)
end
-- Fix missing noteIndex: sort_CitationNumberPrimaryAscendingViaMacroCitation.txt
@@ -430,12 +536,14 @@ function CiteProc:normalize_citation_input(citation)
citation.properties.chapterIndex = 0
end
+ citation.sorted_items = util.clone(citation.citationItems)
+
return citation
end
---@param cite_item CitationItem
---@return CitationItem
-function CiteProc:normalize_cite_item(cite_item)
+function CiteProc:_normalize_cite_item(cite_item)
-- Shallow copy
cite_item = util.clone(cite_item)
cite_item.id = tostring(cite_item.id)
@@ -471,214 +579,237 @@ function CiteProc:normalize_cite_item(cite_item)
end
---@param citation CitationData
----@param citationsPre PreCitation[]
----@param citationsPost PostCitation[]
-function CiteProc:_check_input(citation, citationsPre, citationsPost)
- ---@type {[CitationId]: boolean}
+---@param citations_pre PreCitation[]
+---@param citations_post PostCitation[]
+function CiteProc:_check_input(citation, citations_pre, citations_post)
+ local citation_info_list = {}
+ do
+ for i, pre_citation in ipairs(citations_pre) do
+ local citation_id = pre_citation[1]
+ local note_index = pre_citation[2]
+ local chapter_number = pre_citation[3] or self.registry.citations_by_id[citation_id].properties.chapterIndex or 0
+ local name = string.format("citationsPre[%d]", i)
+ table.insert(citation_info_list, {citation_id, note_index, chapter_number, name})
+ end
+ table.insert(citation_info_list, {citation.citationID, citation.properties.noteIndex, citation.properties.chapterIndex or 0, "citation"})
+ for i, post_citation in ipairs(citations_post) do
+ local citation_id = post_citation[1]
+ local note_index = post_citation[2]
+ local chapter_number = post_citation[3] or self.registry.citations_by_id[citation_id].properties.chapterIndex or 0
+ local name = string.format("citationsPost[%d]", i)
+ table.insert(citation_info_list, {citation_id, note_index, chapter_number, name})
+ end
+ end
+
+ ---@type table<CitationId, boolean>
local citation_dict = {}
local last_note_number = 0
local last_chapter_number = 0
- for i, pre_citation in ipairs(citationsPre) do
- local citation_id = pre_citation[1]
- local note_number = pre_citation[2]
- local chapter_number = pre_citation[3]
+ for _, citation_info in ipairs(citation_info_list) do
+ local citation_id, note_index, chapter_number, name = table.unpack(citation_info)
if citation_dict[citation_id] then
- error(string.format("Previously referenced citationID '%s' encountered in citationsPre", citation_id))
+ error(string.format("Previously referenced citationID '%s' encountered at %s", name))
end
+ citation_dict[citation_id] = true
if chapter_number and chapter_number > 0 then
if chapter_number < last_chapter_number then
- util.warning(string.format("Chapter index sequence is not sane at citationsPre[%d]", i))
+ util.warning(string.format("Chapter index sequence is not sane at %s", name))
end
if chapter_number ~= last_chapter_number then
last_note_number = 0
end
last_chapter_number = chapter_number
end
- if note_number and note_number > 0 then
- if note_number < last_note_number then
- util.warning(string.format("Note index sequence is not sane at citationsPre[%d]", i))
+ if note_index > 0 then
+ if note_index < last_note_number then
+ util.warning(string.format("Note index sequence is not sane at %s", name))
end
- last_note_number = note_number
+ last_note_number = note_index
end
- citation_dict[citation_id] = true
- end
-
- do
- local citation_id = citation.citationID
- local note_number = citation.properties.noteIndex
- local chapter_number = citation.properties.chapterIndex
- if (citation_dict[citation_id]) then
- error("Citation with previously referenced citationID " .. citation_id)
- end
- if note_number and note_number > 0 then
- if note_number < last_note_number then
- util.warning("Note index sequence is not sane for citation " .. citation_id)
- end
- last_note_number = note_number
- end
- if chapter_number and chapter_number > 0 then
- if chapter_number < last_chapter_number then
- util.warning("Chapter index sequence is not sane for citation " .. citation_id)
- end
- last_chapter_number = chapter_number
- end
- citation_dict[citation_id] = true
- end
-
- for i, post_citation in ipairs(citationsPost) do
- local citation_id = post_citation[1]
- local note_number = post_citation[2]
- local chapter_number = post_citation[3]
- if citation_dict[citation_id] then
- error(string.format("Previously referenced citationID '%s' encountered in citationsPost", citation_id))
- end
- if note_number and note_number > 0 then
- if note_number < last_note_number then
- util.warning(string.format("Note index sequence is not sane at citationsPost[%d]", i))
- end
- last_note_number = note_number
- end
- if chapter_number and chapter_number > 0 then
- if chapter_number < last_chapter_number then
- util.warning(string.format("Chapter index sequence is not sane at citationsPost[%d]", i))
- end
- last_chapter_number = chapter_number
- end
- citation_dict[citation_id] = true
end
end
--- A variant of processCitationCluster() for easy use with LaTeX.
--- It should be run after refreshing the registry (updateItems()) with all items
-function CiteProc:process_citation(citation)
- -- util.debug(citation)
- -- util.debug(citationsPre)
- -- Fix missing noteIndex: sort_CitationNumberPrimaryAscendingViaMacroCitation.txt
-
- citation = self:normalize_citation_input(citation)
-
- -- Registor citation
+---@param citation CitationData
+---@param citations_pre PreCitation[]
+---@param citations_post PostCitation[]
+---@return CitationData[]
+---@return CiteId[]
+function CiteProc:_build_reconstituted_citation_list(citation, citations_pre, citations_post)
self.registry.citations_by_id[citation.citationID] = citation
- table.insert(self.registry.citation_list, citation)
-
+ ---@type [CitationId, NoteIndex][]
local citation_note_pairs = {}
- for _, citation_ in ipairs(self.registry.citation_list) do
- table.insert(citation_note_pairs, {citation_.citationID, citation_.properties.noteIndex})
- end
+ util.extend(citation_note_pairs, citations_pre)
+ table.insert(citation_note_pairs, {citation.citationID, citation.properties.noteIndex})
+ util.extend(citation_note_pairs, citations_post)
- -- update self.registry.citations_by_item_id
- for _, cite_item in ipairs(citation.citationItems) do
- if not self.registry.citations_by_item_id[cite_item.id] then
- self.registry.citations_by_item_id[cite_item.id] = {}
+ ---@type CiteId[]
+ local item_ids = {}
+ ---@type table<ItemId, boolean>
+ local item_id_dict = {}
+ ---@type CitationData[]
+ local citation_list = {}
+ ---@type table<CitationId, CitationData>
+ local citations_by_id = {}
+ -- TODO: Remove citations_by_item_id
+ ---@type table<ItemId, CitationData[]>
+ local citations_by_item_id = {}
+
+ for citation_index, pair in ipairs(citation_note_pairs) do
+ local citation_id, note_index = table.unpack(pair)
+ local citation_ = self.registry.citations_by_id[citation_id]
+ if not citation_ then
+ util.error("Citation not in registry.")
end
- table.insert(self.registry.citations_by_item_id[cite_item.id], citation)
- end
+ citation_.citation_index = citation_index
+ citation_.properties.noteIndex = note_index
- -- self:updateItems(item_ids)
- for i, cite_item in ipairs(citation.citationItems) do
- self:get_item(cite_item.id)
+ table.insert(citation_list, citation_)
+ citations_by_id[citation_.citationID] = citation_
+ for _, cite_item in ipairs(citation_.citationItems) do
+ if not item_id_dict[cite_item.id] then
+ item_id_dict[cite_item.id] = true
+ table.insert(item_ids, cite_item.id)
+ citations_by_item_id[cite_item.id] = {}
+ end
+ table.insert(citations_by_item_id[cite_item.id], citation_)
+ end
end
+ self.registry.citation_list = citation_list
+ self.registry.citations_by_id = citations_by_id
+ self.registry.citations_by_item_id = citations_by_item_id
+ return citation_list, item_ids
+end
- local tainted_citation_ids = self:get_tainted_citation_ids(citation_note_pairs)
+---@param citation_list CitationData[]
+---@return table<CitationId, boolean>
+function CiteProc:_set_positions(citation_list)
+ ---@type table<CitationId, boolean>
+ local tainted_citation_ids = {}
- local mode = citation.properties.mode
- if mode == "suppress-author" and self.style.class == "note" then
- mode = nil
+ ---@type {[integer]: CitationData[]}
+ local chapter_citations = {}
+ for _, citation in ipairs(citation_list) do
+ local chapter_number = citation.properties.chapterIndex
+ if not chapter_citations[chapter_number] then
+ chapter_citations[chapter_number] = {}
+ end
+ table.insert(chapter_citations[chapter_number], citation)
end
- local citation_element = self.style.citation
- if mode == "author-only" and self.style.intext then
- citation_element = self.style.intext
- elseif mode == "full-cite" then
- citation_element = self.style.full_citation
+
+ for _, citations in pairs(chapter_citations) do
+ self:_update_chapter_positions(citations, tainted_citation_ids)
end
- local citation_str = citation_element:build_citation_str(citation, self)
+ -- Update tainted citation ids because of citation-number's change
+ -- The self.tainted_item_ids were added in the sort_bibliography() procedure.
+ for item_id, _ in pairs(self.tainted_item_ids) do
+ if self.registry.citations_by_item_id[item_id] then
+ for _, citation in ipairs(self.registry.citations_by_item_id[item_id]) do
+ tainted_citation_ids[citation.citationID] = true
+ end
+ end
+ end
- return citation_str
+ return tainted_citation_ids
end
-
-function CiteProc:get_tainted_citation_ids(citation_note_pairs)
- local tainted_citation_ids = {}
-
- self.cite_first_note_numbers = {}
- self.cite_last_note_numbers = {}
- self.note_citations_map = {}
- -- {
- -- 1 = {"citation-1", "citation-2"},
- -- 2 = {"citation-2"},
- -- }
-
- -- Citations with noteIndex == 0 are in-text citations and they may also
- -- have position properties.
+---@param citation_list CitationData[]
+---@param tainted_citation_ids table<CitationId, boolean>
+---@return table<string, boolean>
+function CiteProc:_update_chapter_positions(citation_list, tainted_citation_ids)
+ ---@type CitationData[]
local in_text_citations = {}
-
- local last_chapter_number = 0
-
- local previous_citation
- for citation_index, pair in ipairs(citation_note_pairs) do
- local citation_id, note_number = table.unpack(pair)
- -- util.debug(citation_id)
- local citation = self.registry.citations_by_id[citation_id]
- citation.properties.noteIndex = note_number
- citation.citation_index = citation_index
-
- local chapter_number = citation.properties.chapterIndex
- if chapter_number and chapter_number ~= last_chapter_number then
- self.cite_first_note_numbers = {}
- self.cite_last_note_numbers = {}
- self.note_citations_map = {}
- previous_citation = nil
+ ---@type CitationData[]
+ local note_citations = {}
+ for _, citation in ipairs(citation_list) do
+ if citation.properties.noteIndex == 0 then
+ table.insert(in_text_citations, citation)
+ else
+ table.insert(note_citations, citation)
end
+ end
+ for _, citations in ipairs({in_text_citations, note_citations}) do
+ ---@type table<CiteId, NoteIndex>
+ local first_ref = {}
+ ---@type table<CiteId, NoteIndex>
+ local last_ref = {}
+ ---@type table<NoteIndex, CitationId[]>
+ local num_citations_in_note = {}
- local tainted = false
-
- local prev_citation = previous_citation
- if note_number == 0 then
- -- Find the previous in-text citation.
- prev_citation = in_text_citations[#in_text_citations]
- end
- local previous_cite
- for _, cite_item in ipairs(citation.citationItems) do
- tainted = self:set_cite_item_position(cite_item, note_number, previous_cite, prev_citation, citation)
-
- -- https://citeproc-js.readthedocs.io/en/latest/csl-json/markup.html#citations
- -- Citations within the main text of the document have a noteIndex of zero.
- if (self.style.class == "note" and note_number > 0) or citation.properties.mode ~= "author-only" then
- self.cite_last_note_numbers[cite_item.id] = note_number
- previous_cite = cite_item
+ for _, citation in ipairs(citations) do
+ local note_index = citation.properties.noteIndex
+ if not num_citations_in_note[note_index] then
+ num_citations_in_note[note_index] = {}
end
+ table.insert(num_citations_in_note[note_index], citation.citationID)
end
- if tainted then
- tainted_citation_ids[citation.citationID] = true
- end
+ local previous_citation
+ for _, citation in ipairs(citations) do
+ local mode = citation.properties.mode
+ local note_index = citation.properties.noteIndex
+ local previous_cite
+ for _, cite_item in ipairs(citation.sorted_items) do
+ local position_properties = {
+ position_level = cite_item.position_level,
+ ["first-reference-note-number"] = cite_item["first-reference-note-number"],
+ near_note = cite_item.near_note,
+ }
+
+ self:_set_cite_item_position(cite_item, note_index, previous_cite, previous_citation, citation, first_ref, last_ref, num_citations_in_note)
+
+ if self:_check_tainted_position_change(cite_item, position_properties) then
+ tainted_citation_ids[citation.citationID] = true
+ end
- if not self.note_citations_map[note_number] then
- self.note_citations_map[note_number] = {}
- end
- table.insert(self.note_citations_map[note_number], citation.citationID)
- if citation.properties.mode ~= "author-only" then
- if note_number == 0 then
- table.insert(in_text_citations, citation)
- else
+ -- https://citeproc-js.readthedocs.io/en/latest/csl-json/markup.html#citations
+ -- Citations within the main text of the document have a noteIndex of zero.
+ if mode ~= "author-only" and mode ~= "full-cite" then
+ if not first_ref[cite_item.id] and note_index > 0 then
+ -- note_index == 0 implied an in-text citation
+ first_ref[cite_item.id] = note_index
+ end
+ last_ref[cite_item.id] = note_index
+ previous_cite = cite_item
+ end
+ end
+
+ if mode ~= "author-only" and mode ~= "full-cite" then
previous_citation = citation
end
end
- last_chapter_number = chapter_number
- end
-
- -- Update tainted citation ids because of citation-number's change
- -- The self.tainted_item_ids were added in the sort_bibliography() procedure.
- for item_id, _ in pairs(self.tainted_item_ids) do
- if self.registry.citations_by_item_id[item_id] then
- for _, citation in ipairs(self.registry.citations_by_item_id[item_id]) do
- tainted_citation_ids[citation.citationID] = true
+ if self.style.class == "note" and self.style.has_disambiguate then
+ ---@type table<CiteId, CitationData[]>
+ local citations_by_item_id = {}
+ for _, citation in ipairs(citations) do
+ if citation.properties.mode ~= "author-only" and citation.properties.mode ~= "full-cite" then
+ for _, cite_item in ipairs(citation.sorted_items) do
+ if not citations_by_item_id[cite_item.id] then
+ citations_by_item_id[cite_item.id] = {}
+ end
+ table.insert(citations_by_item_id[cite_item.id], citation)
+ end
+ end
+ end
+ for _, citation in ipairs(citations) do
+ if citation.properties.mode ~= "author-only" and citation.properties.mode ~= "full-cite" then
+ for _, cite_item in ipairs(citation.sorted_items) do
+ assert(citations_by_item_id[cite_item.id])
+ local num_citations = #citations_by_item_id[cite_item.id]
+ if not cite_item.num_citations or (num_citations < 2) ~= (cite_item.num_citations < 2) then
+ -- self.tainted_item_ids[cite_item.id] = true
+ for _, citation_ in ipairs(citations_by_item_id[cite_item.id]) do
+ tainted_citation_ids[citation_.citationID] = true
+ end
+ end
+ cite_item.num_citations = num_citations
+ end
+ end
end
end
end
@@ -686,58 +817,41 @@ function CiteProc:get_tainted_citation_ids(citation_note_pairs)
return tainted_citation_ids
end
-function CiteProc:set_cite_item_position(cite_item, note_number, previous_cite, previous_citation, citation)
- local position = Position.First
-
+function CiteProc:_set_cite_item_position(cite_item, note_index, previous_cite, previous_citation, citation, first_ref, last_ref, num_citations_in_note)
-- https://citeproc-js.readthedocs.io/en/latest/csl-json/markup.html#citations
-- Citations within the main text of the document have a noteIndex of zero.
- if citation.properties.mode == "author-only" then
+ if citation.properties.mode == "author-only" or citation.properties.mode == "full-cite" then
-- discretionary_IbidInAuthorDateStyleWithoutIntext.txt
- cite_item.position_level = position
- return false
+ cite_item.position_level = Position.First
+ cite_item.near_note = false
+ return
end
- local first_reference_note_number = self.cite_first_note_numbers[cite_item.id]
+ local first_reference_note_number = first_ref[cite_item.id]
if first_reference_note_number then
- position = Position.Subsequent
- elseif note_number > 0 then
- -- note_number == 0 implied an in-text citation
- self.cite_first_note_numbers[cite_item.id] = note_number
+ cite_item.position_level = Position.Subsequent
+ cite_item["first-reference-note-number"] = first_reference_note_number
+ else
+ cite_item.position_level = Position.First
end
- local preceding_cite_item = self:get_preceding_cite_item(cite_item, previous_cite, previous_citation, note_number)
+ local preceding_cite_item = self:_find_preceding_ibid_item(cite_item, previous_cite, previous_citation, note_index, num_citations_in_note)
if preceding_cite_item then
- position = self:_get_cite_position(cite_item, preceding_cite_item)
+ cite_item.position_level = self:_get_ibid_position(cite_item, preceding_cite_item)
end
- local near_note = false
- local last_note_number = self.cite_last_note_numbers[cite_item.id]
+ cite_item.near_note = false
+ local last_note_number = last_ref[cite_item.id]
if last_note_number then
- local note_distance = note_number - last_note_number
- if note_distance <= self.style.citation.near_note_distance then
- near_note = true
- end
+ local note_distance = note_index - last_note_number
+ cite_item.near_note = (note_distance <= self.style.citation.near_note_distance)
end
- local tainted = false
- if cite_item.position_level ~= position then
- tainted = true
- cite_item.position_level = position
- end
- if cite_item["first-reference-note-number"] ~= first_reference_note_number then
- tainted = true
- cite_item["first-reference-note-number"] = first_reference_note_number
- end
- if cite_item.near_note ~= near_note then
- tainted = true
- cite_item.near_note = near_note
- end
- return tainted
end
-- Find the preceding cite referencing the same item
-function CiteProc:get_preceding_cite_item(cite_item, previous_cite, previous_citation, note_number)
+function CiteProc:_find_preceding_ibid_item(cite_item, previous_cite, previous_citation, note_index, num_citations_in_note)
if previous_cite then
-- a. the current cite immediately follows on another cite, within the same
-- citation, that references the same item
@@ -752,11 +866,11 @@ function CiteProc:get_preceding_cite_item(cite_item, previous_cite, previous_cit
-- b. the current cite is the first cite in the citation, and the previous
-- citation consists of a single cite referencing the same item
local previous_note_number = previous_citation.properties.noteIndex
- local num_previous_note_citations = #self.note_citations_map[previous_note_number]
- if (previous_note_number == note_number - 1 and num_previous_note_citations == 1)
- or previous_note_number == note_number then
- if #previous_citation.citationItems == 1 then
- previous_cite = previous_citation.citationItems[1]
+ local num_previous_note_citations = #num_citations_in_note[previous_note_number]
+ if (previous_note_number == note_index - 1 and num_previous_note_citations == 1)
+ or previous_note_number == note_index then
+ if #previous_citation.sorted_items == 1 then
+ previous_cite = previous_citation.sorted_items[1]
if previous_cite.id == cite_item.id then
return previous_cite
end
@@ -766,7 +880,7 @@ function CiteProc:get_preceding_cite_item(cite_item, previous_cite, previous_cit
return nil
end
-function CiteProc:_get_cite_position(item, preceding_cite)
+function CiteProc:_get_ibid_position(item, preceding_cite)
if preceding_cite.locator then
if item.locator then
if item.locator == preceding_cite.locator and item.label == preceding_cite.label then
@@ -786,142 +900,51 @@ function CiteProc:_get_cite_position(item, preceding_cite)
end
end
-function CiteProc:makeCitationCluster(citation_items)
- local special_form = nil
- local items = {}
-
- for i, cite_item in ipairs(citation_items) do
- cite_item = self:normalize_cite_item(cite_item)
- local item_data = self:get_item(cite_item.id)
-
- -- Create a wrapper of the orignal item from registry so that
- -- it may hold different `locator` or `position` values for cites.
- local cite_item = setmetatable(cite_item, {__index = item_data})
-
- if not special_form then
- for _, form in ipairs({"author-only", "suppress-author", "coposite"}) do
- if cite_item[form] then
- special_form = form
- end
- end
- end
-
- -- Set "first-reference-note-number" variable when called from
- -- processCitationCluster() > updateItems()
- local citations = self.registry.citations_by_item_id[cite_item.id]
- if citations and #citations > 0 then
- item_data["first-reference-note-number"] = citations[1].properties.noteIndex
+function CiteProc:_check_tainted_position_change(cite_item, position_properties)
+ for key, value in pairs(position_properties) do
+ if cite_item[key] ~= value then
+ return true
end
+ end
+ return false
+end
- cite_item.position_level = Position.First
- if self.cite_first_note_numbers[cite_item.id] then
- cite_item.position_level = Position.Subsequent
- else
- self.cite_first_note_numbers[cite_item.id] = 0
+---@param tainted_citation_ids table<CitationId, boolean>
+---@return [integer, string, CitationId][]
+function CiteProc:_rerun_changed_cites(tainted_citation_ids)
+ local result = {}
+ for citation_id, _ in pairs(tainted_citation_ids) do
+ local citation = self.registry.citations_by_id[citation_id]
+ local citation_index = citation.citation_index
+ local mode = citation.properties.mode
+ if mode == "suppress-author" and self.style.class == "note" then
+ mode = nil
end
-
- local preceding_cite
- if i == 1 then
- local previous_citation = self.registry.previous_citation
- if previous_citation then
- if #previous_citation.citationItems == 1 and previous_citation.citationItems[1].id == cite_item.id then
- preceding_cite = previous_citation.citationItems[1]
- end
+ local citation_element = self.style.citation
+ if mode == "author-only" and self.style.intext then
+ citation_element = self.style.intext
+ elseif mode == "full-cite" then
+ if self.style.class == "note" then
+ citation_element = self.style.citation
+ else
+ citation_element = self.style.full_citation
end
- elseif citation_items[i - 1].id == cite_item.id then
- preceding_cite = citation_items[i - 1]
end
- if preceding_cite then
- cite_item.position_level = self:_get_cite_position(cite_item, preceding_cite)
- end
-
- table.insert(items, cite_item)
- end
-
- if self.registry.requires_sorting then
- self:sort_bibliography()
- end
-
- self:check_valid_citation_element()
- local citation_element = self.style.citation
- if special_form == "author-only" and self.style.intext then
- citation_element = self.style.intext
- end
-
- local res = citation_element:build_cluster(items, self)
-
- -- local context = {
- -- build = {},
- -- engine=self,
- -- }
- -- local res = self.style:render_citation(items, context)
-
- self.registry.previous_citation = {
- citationID = "pseudo-citation",
- citationItems = items,
- properties = {
- noteIndex = 0,
- }
- }
- return res
-end
-
----@param bibsection any
----@return [{[string]: string | number | boolean}, string[]]
-function CiteProc:makeBibliography(bibsection)
- -- The bibsection works as a filter described in
- -- <https://citeproc-js.readthedocs.io/en/latest/running.html#selective-output-with-makebibliography>.
- if not self.style.bibliography then
- return {{}, {}}
- end
-
- local res = {}
-
- self.registry.widest_label = ""
- self.registry.maxoffset = 0
-
- local ids = self:get_sorted_refs()
- if bibsection then
- ids = self:filter_with_bibsection(ids, bibsection)
+ local citation_str = citation_element:build_citation_str(citation, self)
+ table.insert(result, {citation_index, citation_str, citation_id})
end
- for _, id in ipairs(ids) do
- local str = self.style.bibliography:build_bibliography_str(id, self)
- table.insert(res, str)
- end
-
- local bib_start = self.output_format.markups["bibstart"]
- local bib_end = self.output_format.markups["bibend"]
- if type(bib_start) == "function" then
- bib_start = bib_start(self)
- end
- if type(bib_end) == "function" then
- bib_end = bib_end(self)
- end
-
- local params = {
- hangingindent = self.style.bibliography.hanging_indent,
- ["second-field-align"] = self.style.bibliography.second_field_align or false,
- linespacing = self.style.bibliography.line_spacing,
- entryspacing = self.style.bibliography.entry_spacing,
- maxoffset = self.registry.maxoffset,
- widest_label = self.registry.widest_label,
- bibstart = bib_start,
- bibend = bib_end,
- entry_ids = util.clone(self.registry.reflist),
- }
-
- return {params, res}
+ return result
end
-function CiteProc:get_sorted_refs()
+function CiteProc:_get_sorted_refs()
if self.registry.requires_sorting then
self:sort_bibliography()
end
return self.registry.reflist
end
-function CiteProc:filter_with_bibsection(ids, bibsection)
+function CiteProc:_filter_with_bibsection(ids, bibsection)
if bibsection.quash then
return self:filter_quash(ids, bibsection)
elseif bibsection.select then
diff --git a/Master/texmf-dist/scripts/citation-style-language/citeproc-manager.lua b/Master/texmf-dist/scripts/citation-style-language/citeproc-manager.lua
index 4f824feab24..e361d6f6a8a 100644
--- a/Master/texmf-dist/scripts/citation-style-language/citeproc-manager.lua
+++ b/Master/texmf-dist/scripts/citation-style-language/citeproc-manager.lua
@@ -7,6 +7,7 @@
local citeproc_manager = {}
require("lualibs")
+local json_encode = utilities.json.tojson
local json_decode = utilities.json.tolua
local citeproc = require("citeproc")
@@ -14,7 +15,7 @@ local bibtex_data = require("citeproc-bibtex-data")
local bibtex_parser = require("citeproc-bibtex-parser")
local latex_parser = require("citeproc-latex-parser")
local util = citeproc.util
-local unicode = require("citeproc-unicode")
+local unicode = require("citeproc-unicode")
---@param file_name string
@@ -221,6 +222,7 @@ end
---@alias LanguageCode string
---@class RefSection
+---@field index integer
---@field initialized boolean
---@field style_id string?
---@field bib_resources FilePath[]
@@ -233,12 +235,14 @@ end
---@field citations CitationData[]
---@field citations_pre table[]
---@field bibliography_configs string[]
+---@field style string
local RefSection = {}
---@return RefSection
function RefSection:new()
---@type RefSection
local ref_section = {
+ index = 0,
initialized = false,
style_id = nil,
bib_resources = {},
@@ -251,6 +255,7 @@ function RefSection:new()
citations = {},
citations_pre = {},
bibliography_configs = {},
+ style = "",
}
setmetatable(ref_section, self)
self.__index = self
@@ -276,6 +281,9 @@ function RefSection:make_citeproc_engine()
self.lang = nil
end
+ self.style = style
+ -- self.items
+
self.engine = citeproc.new(citeproc_sys, style, self.lang, force_lang)
self:_check_dependent_style(citeproc_sys)
@@ -334,6 +342,86 @@ function RefSection:_update_uncited_items()
self.uncited_ids = uncited_ids
end
+function RefSection:record_fixture()
+ if not self.fixture then
+ self.fixture = {
+ mode = "citation",
+ description = tex.jobname,
+ result = "",
+ bibliography_result = "",
+ citations = "",
+ ["citation-items"] = "",
+ csl = self.style,
+ input = "",
+ version = "1.0",
+ citation_results = {},
+ }
+ end
+ local fixture = self.fixture
+
+ local cited_id_dict = {}
+ for _, citation in ipairs(self.citations) do
+ for _, cite_item in ipairs(citation.citationItems) do
+ cited_id_dict[cite_item.id] = true
+ end
+ end
+ local input_items = {}
+ for _, item in ipairs(self.items) do
+ if cited_id_dict[item.id] then
+ table.insert(input_items, item)
+ end
+ end
+ fixture.input = json_encode(input_items)
+
+ local citation_items = {}
+ for _, citation in ipairs(self.citations) do
+ table.insert(citation_items, util.clone(citation.citationItems))
+ end
+ fixture["citation-items"] = json_encode(citation_items)
+
+ local citations = {}
+ local citations_pre = {}
+ for _, citation in ipairs(self.citations) do
+ table.insert(citations, {citation, util.clone(citations_pre), {}})
+ table.insert(citations_pre, {citation.citationID, citation.properties.noteIndex})
+ end
+ fixture.citations = string.gsub(json_encode(citations), "{%s*}", "[]")
+
+ local result = {}
+ for i, citation_result in ipairs(fixture.citation_results) do
+ if i == #fixture.citation_results then
+ table.insert(result, string.format(">>[%d] %s", i - 1, citation_result))
+ else
+ table.insert(result, string.format("..[%d] %s", i - 1, citation_result))
+ end
+ end
+ fixture.result = table.concat(result, "\n")
+
+ local fixture_output = {}
+ local function make_block(section, str)
+ return string.format(">>===== %s =====>>\n%s\n<<===== %s =====<<\n", string.upper(section), str, string.upper(section))
+ end
+ for _, section in ipairs({"mode", "description", "result", "bibliography_result", "citations", "citation-items", "csl", "input", "version"}) do
+ if not (section == "bibliography_result" and fixture[section] == "") then
+ table.insert(fixture_output, make_block(section, fixture[section]))
+ end
+ end
+ local fixture_str = table.concat(fixture_output, "\n\n")
+
+ local file_name = string.format("%s.txt", tex.jobname)
+ if self.index > 0 then
+ file_name = string.format("%s-%d.txt", tex.jobname, self.index)
+ end
+
+ local fixture_file = io.open(file_name, "w")
+ if fixture_file then
+ fixture_file:write(fixture_str)
+ fixture_file:close()
+ else
+ error(string.format("Failed to write fixture file '%s'", file_name))
+ end
+end
+
---@class CslCitationManager
---@field global_ref_section RefSection
@@ -342,6 +430,7 @@ end
---@field ref_sections table<integer, RefSection>
---@field ref_section RefSection
---@field hyperref_loaded boolean
+---@field regression_test boolean
local CslCitationManager = {}
---@return CslCitationManager
@@ -361,6 +450,7 @@ function CslCitationManager:new()
ref_section = ref_section,
hyperref_loaded = false,
+ regression_test = false,
}
setmetatable(o, self)
@@ -388,6 +478,10 @@ function CslCitationManager:init(style_id, bib_resources_str, lang)
if self.hyperref_loaded then
global_ref_section.engine:enable_linking()
end
+ if self.regression_test then
+ self.global_ref_section:record_fixture()
+ end
+
self.ref_section_index = 0
end
@@ -414,6 +508,7 @@ function CslCitationManager:begin_ref_section(style_id, bib_resources_str, lang)
self.ref_section = self.ref_sections[self.ref_section_index]
if not self.ref_section then
self.ref_section = RefSection:new()
+ self.ref_section.index = self.ref_section_index
self.ref_sections[self.ref_section_index] = self.ref_section
end
@@ -434,6 +529,10 @@ function CslCitationManager:begin_ref_section(style_id, bib_resources_str, lang)
self.ref_section.engine:enable_linking()
end
+ if self.regression_test then
+ self.ref_section:record_fixture()
+ end
+
end
@@ -456,6 +555,7 @@ function CslCitationManager:register_citation_info(ref_section_index_str, citati
if not ref_section then
ref_section = RefSection:new()
self.ref_sections[ref_section_index] = ref_section
+ ref_section.index = ref_section_index
end
local citation = self:_make_citation(citation_info)
@@ -493,6 +593,8 @@ function CslCitationManager:cite(citation_info)
local citation = self:_make_citation(citation_info)
-- util.debug(citation)
+ table.insert(self.ref_section.citations, citation)
+
local citation_str
-- if preview_mode then
-- -- TODO: preview mode in first pass of \blockquote of csquotes
@@ -512,6 +614,11 @@ function CslCitationManager:cite(citation_info)
-- and <https://tex.stackexchange.com/questions/519954/backslashes-in-macros-defined-in-lua-code>.
tex.sprint(string.format("\\expandafter\\def\\csname l__csl_citation_tl\\endcsname{%s}", citation_str))
+ if self.regression_test then
+ table.insert(self.ref_section.fixture.citation_results, citation_str)
+ self.ref_section:record_fixture()
+ end
+
table.insert(self.ref_section.citations_pre, {citation.citationID, citation.properties.noteIndex})
end
@@ -627,6 +734,11 @@ function CslCitationManager:bibliography(filter_str)
end
local bib_lines = self:make_bibliography(filter_str)
tex.print(util.split(table.concat(bib_lines, "\n"), "\n"))
+
+ if self.regression_test then
+ self.ref_section.fixture.bibliography_result = table.concat(bib_lines, "\n")
+ self.ref_section:record_fixture()
+ end
end
---@param filter_str string
@@ -799,6 +911,7 @@ function CslCitationManager:read_aux_file(aux_content)
if not ref_section then
ref_section = RefSection:new()
self.ref_sections[ref_section_index] = ref_section
+ ref_section.index = ref_section_index
self.ref_section = ref_section
end
if command == "\\csl@aux@style" then
diff --git a/Master/texmf-dist/scripts/citation-style-language/citeproc-node-citation.lua b/Master/texmf-dist/scripts/citation-style-language/citeproc-node-citation.lua
index 839c22e1b51..183d9f68070 100644
--- a/Master/texmf-dist/scripts/citation-style-language/citeproc-node-citation.lua
+++ b/Master/texmf-dist/scripts/citation-style-language/citeproc-node-citation.lua
@@ -178,13 +178,6 @@ end
---@alias CiteId string | number
----@class CitationItem
----@field id CiteId
----@field prefix string?
----@field suffix string?
----@field locator string?
----@field label string?
-
---comment
---@param citation_items CitationItem[]
---@param engine CiteProc
@@ -195,6 +188,8 @@ function Citation:build_cluster(citation_items, engine, properties)
local output_format = engine.output_format
---@type CiteIr[]
local irs = {}
+ -- The citation_items are already sorted when evaluate the positions
+ -- To be removed
citation_items = self:sorted_citation_items(citation_items, engine)
for _, cite_item in ipairs(citation_items) do
local ir = self:build_fully_disambiguated_ir(cite_item, output_format, engine, properties)
@@ -437,7 +432,7 @@ end
---@param cite_item CitationItem
---@param output_format OutputFormat
---@param engine CiteProc
----@param properties table
+---@param properties CitationProperties
---@return CiteIr
function Citation:build_fully_disambiguated_ir(cite_item, output_format, engine, properties)
-- util.debug(cite_item.id)
@@ -451,12 +446,14 @@ function Citation:build_fully_disambiguated_ir(cite_item, output_format, engine,
-- Disambiguation should be based on the subsequent form
-- disambiguate_BasedOnEtAlSubsequent.txt
cite_item.position_level = Position.Subsequent
+ cite_item["first-reference-note-number"] = properties.noteIndex
local disam_ir = self:build_ambiguous_ir(cite_item, output_format, engine)
disam_ir = self:apply_disambiguate_add_givenname(disam_ir, engine)
disam_ir = self:apply_disambiguate_add_names(disam_ir, engine)
disam_ir = self:apply_disambiguate_conditionals(disam_ir, engine)
disam_ir = self:apply_disambiguate_add_year_suffix(disam_ir, engine)
cite_item.position_level = Position.First
+ cite_item["first-reference-note-number"] = nil
end
return cite_ir
@@ -1034,7 +1031,6 @@ function Citation:apply_disambiguate_conditionals(cite_ir, engine)
-- Update ir output
local inlines = ir_:flatten(disam_format)
local disam_str = disam_format:output(inlines, nil)
- -- util.debug("update: " .. ir_.cite_item.id .. ": " .. disam_str)
ir_.disam_str = disam_str
if not engine.cite_irs_by_output[disam_str] then
engine.cite_irs_by_output[disam_str] = {}
diff --git a/Master/texmf-dist/scripts/citation-style-language/citeproc-util.lua b/Master/texmf-dist/scripts/citation-style-language/citeproc-util.lua
index 12d2b14ee95..46a4c0aaa77 100644
--- a/Master/texmf-dist/scripts/citation-style-language/citeproc-util.lua
+++ b/Master/texmf-dist/scripts/citation-style-language/citeproc-util.lua
@@ -1063,7 +1063,7 @@ util.roman_numerals = {
-- Choose
----@enum Postion
+---@enum Position
local Position = {
First = 0,
Subsequent = 1,
diff --git a/Master/texmf-dist/scripts/citation-style-language/citeproc.lua b/Master/texmf-dist/scripts/citation-style-language/citeproc.lua
index 31b624b4d93..c71b90577ed 100755
--- a/Master/texmf-dist/scripts/citation-style-language/citeproc.lua
+++ b/Master/texmf-dist/scripts/citation-style-language/citeproc.lua
@@ -16,7 +16,7 @@ else
util = require("citeproc.util")
end
-citeproc.__VERSION__ = "0.6.3"
+citeproc.__VERSION__ = "0.6.4"
citeproc.new = engine.CiteProc.new
citeproc.util = util
diff --git a/Master/texmf-dist/tex/latex/citation-style-language/citation-style-language-init.sty b/Master/texmf-dist/tex/latex/citation-style-language/citation-style-language-init.sty
index de5b5bed4ce..9032b60fee8 100644
--- a/Master/texmf-dist/tex/latex/citation-style-language/citation-style-language-init.sty
+++ b/Master/texmf-dist/tex/latex/citation-style-language/citation-style-language-init.sty
@@ -83,6 +83,10 @@
\cs_new:Npn \__csl_initialize_lua_module:
{
+ \bool_if:NT \l__csl_regression_test_bool
+ {
+ \lua_now:n { csl_citation_manager.regression_test = true }
+ }
\tl_set:Nx \l_tmpa_tl { \clist_use:Nn \l__csl_bib_resources_clist { , } }
\lua_now:e
{
diff --git a/Master/texmf-dist/tex/latex/citation-style-language/citation-style-language.sty b/Master/texmf-dist/tex/latex/citation-style-language/citation-style-language.sty
index f6039b4008c..33ce246978a 100644
--- a/Master/texmf-dist/tex/latex/citation-style-language/citation-style-language.sty
+++ b/Master/texmf-dist/tex/latex/citation-style-language/citation-style-language.sty
@@ -9,7 +9,7 @@
\RequirePackage{expl3}
\RequirePackage{xparse}
-\ProvidesExplPackage {citation-style-language} {2024-08-28} {0.6.3}
+\ProvidesExplPackage {citation-style-language} {2024-09-15} {0.6.4}
{Citation Style Language for LaTeX}
\RequirePackage { l3keys2e }
diff --git a/Master/texmf-dist/tex/latex/citation-style-language/locales/csl-locales-de-AT.xml b/Master/texmf-dist/tex/latex/citation-style-language/locales/csl-locales-de-AT.xml
index 833ff58f768..2d27ec8635d 100644
--- a/Master/texmf-dist/tex/latex/citation-style-language/locales/csl-locales-de-AT.xml
+++ b/Master/texmf-dist/tex/latex/citation-style-language/locales/csl-locales-de-AT.xml
@@ -20,7 +20,7 @@
<name>Frank Bennett</name>
</translator>
<rights license="http://creativecommons.org/licenses/by-sa/3.0/">This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License</rights>
- <updated>2024-03-09T15:20:54-05:00</updated>
+ <updated>2024-08-27T14:30:55-04:00</updated>
</info>
<style-options punctuation-in-quote="false"/>
<date form="text">
@@ -46,7 +46,7 @@
<term name="no-publisher" form="short">n.p.</term>
<term name="on">on</term>
<term name="op-cit">op. cit.</term> <!-- like ibid., the abbreviated form is the regular form -->
- <term name="original-work-published">original work published</term>
+ <term name="original-work-published">Ursprünglich erschienen</term>
<term name="personal-communication">persönliche Kommunikation</term>
<term name="podcast">podcast</term>
<term name="podcast-episode">podcast episode</term>
diff --git a/Master/texmf-dist/tex/latex/citation-style-language/locales/csl-locales-de-CH.xml b/Master/texmf-dist/tex/latex/citation-style-language/locales/csl-locales-de-CH.xml
index 47a13f2740f..9edebba8375 100644
--- a/Master/texmf-dist/tex/latex/citation-style-language/locales/csl-locales-de-CH.xml
+++ b/Master/texmf-dist/tex/latex/citation-style-language/locales/csl-locales-de-CH.xml
@@ -14,7 +14,7 @@
<name>Sebastian Karcher</name>
</translator>
<rights license="http://creativecommons.org/licenses/by-sa/3.0/">This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License</rights>
- <updated>2024-03-09T15:20:54-05:00</updated>
+ <updated>2024-08-27T14:30:55-04:00</updated>
</info>
<style-options punctuation-in-quote="false"/>
<date form="text">
@@ -40,7 +40,7 @@
<term name="no-publisher" form="short">n.p.</term>
<term name="on">on</term>
<term name="op-cit">op. cit.</term> <!-- like ibid., the abbreviated form is the regular form -->
- <term name="original-work-published">original work published</term>
+ <term name="original-work-published">Ursprünglich erschienen</term>
<term name="personal-communication">persönliche Kommunikation</term>
<term name="podcast">podcast</term>
<term name="podcast-episode">podcast episode</term>
diff --git a/Master/texmf-dist/tex/latex/citation-style-language/locales/csl-locales-de-DE.xml b/Master/texmf-dist/tex/latex/citation-style-language/locales/csl-locales-de-DE.xml
index f7e25ad0f31..903378388a3 100644
--- a/Master/texmf-dist/tex/latex/citation-style-language/locales/csl-locales-de-DE.xml
+++ b/Master/texmf-dist/tex/latex/citation-style-language/locales/csl-locales-de-DE.xml
@@ -17,7 +17,7 @@
<name>jakov</name>
</translator>
<rights license="http://creativecommons.org/licenses/by-sa/3.0/">This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License</rights>
- <updated>2024-03-09T15:20:54-05:00</updated>
+ <updated>2024-08-27T14:30:55-04:00</updated>
</info>
<style-options punctuation-in-quote="false"/>
<date form="text">
@@ -43,7 +43,7 @@
<term name="no-publisher" form="short">n.p.</term>
<term name="on">on</term>
<term name="op-cit">op. cit.</term> <!-- like ibid., the abbreviated form is the regular form -->
- <term name="original-work-published">original work published</term>
+ <term name="original-work-published">Ursprünglich erschienen</term>
<term name="personal-communication">persönliche Kommunikation</term>
<term name="podcast">podcast</term>
<term name="podcast-episode">podcast episode</term>
diff --git a/Master/texmf-dist/tex/latex/citation-style-language/styles/apa.csl b/Master/texmf-dist/tex/latex/citation-style-language/styles/apa.csl
index a78e7f315fd..97fc1afd6cf 100644
--- a/Master/texmf-dist/tex/latex/citation-style-language/styles/apa.csl
+++ b/Master/texmf-dist/tex/latex/citation-style-language/styles/apa.csl
@@ -14,7 +14,7 @@
<category citation-format="author-date"/>
<category field="psychology"/>
<category field="generic-base"/>
- <updated>2024-08-24T12:16:51-04:00</updated>
+ <updated>2024-08-27T14:44:31-04:00</updated>
<rights license="http://creativecommons.org/licenses/by-sa/3.0/">This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License</rights>
</info>
<locale xml:lang="en">
@@ -832,7 +832,7 @@
<group delimiter=" ">
<text variable="genre" text-case="capitalize-first"/>
<group delimiter=" ">
- <text term="author" form="verb"/>
+ <text term="container-author" form="verb"/>
<names variable="interviewer">
<name and="symbol" initialize-with=". " delimiter=", "/>
</names>