diff options
Diffstat (limited to 'biblio/citation-style-language/citeproc-node-citation.lua')
-rw-r--r-- | biblio/citation-style-language/citeproc-node-citation.lua | 171 |
1 files changed, 124 insertions, 47 deletions
diff --git a/biblio/citation-style-language/citeproc-node-citation.lua b/biblio/citation-style-language/citeproc-node-citation.lua index 6c7f082746..aca01a7c93 100644 --- a/biblio/citation-style-language/citeproc-node-citation.lua +++ b/biblio/citation-style-language/citeproc-node-citation.lua @@ -1,6 +1,6 @@ -- --- Copyright (c) 2021-2022 Zeping Lee +-- Copyright (c) 2021-2023 Zeping Lee -- Released under the MIT license. -- Repository: https://github.com/zepinglee/citeproc-lua -- @@ -20,11 +20,13 @@ local Micro = require("citeproc-output").Micro local Formatted = require("citeproc-output").Formatted local PlainText = require("citeproc-output").PlainText local InlineElement = require("citeproc-output").InlineElement +local CiteInline = require("citeproc-output").CiteInline local DisamStringFormat = require("citeproc-output").DisamStringFormat local SortStringFormat = require("citeproc-output").SortStringFormat local util = require("citeproc-util") +---@class Citation: Element local Citation = Element:derive("citation", { givenname_disambiguation_rule = "by-cite", -- https://github.com/citation-style-language/schema/issues/338 @@ -37,6 +39,8 @@ function Citation:from_node(node, style) local o = self:new() o.children = {} + o.layout = nil + o.layouts_by_language = {} o:process_children_nodes(node) @@ -45,7 +49,13 @@ function Citation:from_node(node, style) for _, child in ipairs(o.children) do local element_name = child.element_name if element_name == "layout" then - o.layout = child + if child.locale then + for _, lang in ipairs(util.split(util.strip(child.locale))) do + o.layouts_by_language[lang] = child + end + else + o.layout = child + end elseif element_name == "sort" then o.sort = child end @@ -121,6 +131,16 @@ local function remove_name_formatting(ir) end end +---@class CitationItem +---@field id string | number +---@field prefix string? +---@field suffix string? + +---comment +---@param citation_items CitationItem[] +---@param engine CiteProc +---@param properties table +---@return string function Citation:build_cluster(citation_items, engine, properties) properties = properties or {} local output_format = engine.output_format @@ -173,7 +193,6 @@ function Citation:build_cluster(citation_items, engine, properties) -- util.debug(irs) - local citation_delimiter = self.layout.delimiter local citation_stream = {} local context = Context:new() @@ -190,9 +209,9 @@ function Citation:build_cluster(citation_items, engine, properties) local cite_prefix = citation_items[i].prefix local cite_suffix = citation_items[i].suffix if not ir.collapse_suppressed then - local ir_inlines = ir:flatten(output_format) - if #ir_inlines > 0 then - -- Make sure ir_inlines has outputs contents. + local cite_inlines = ir:flatten(output_format) + if #cite_inlines > 0 then + -- Make sure cite_inlines has outputs contents. -- collapse_AuthorCollapseNoDateSorted.txt if previous_ir then if previous_ir.own_delimiter then @@ -202,8 +221,8 @@ function Citation:build_cluster(citation_items, engine, properties) if cite_prefix then left_most_str = cite_prefix[1]:get_left_most_string() end - if citation_delimiter and not (cite_prefix and util.startswith(left_most_str, ",")) then - table.insert(citation_stream, PlainText:new(citation_delimiter)) + if previous_ir.cite_delimiter and not (cite_prefix and util.startswith(left_most_str, ",")) then + table.insert(citation_stream, PlainText:new(previous_ir.cite_delimiter)) end end end @@ -212,8 +231,12 @@ function Citation:build_cluster(citation_items, engine, properties) table.insert(citation_stream, Micro:new(cite_prefix)) end - -- util.debug(ir) - util.extend(citation_stream, ir_inlines) + -- util.debug(ir.cite_item.id) + -- util.debug(cite_inlines) + -- if context.engine.opt.citation_link then + cite_inlines = {CiteInline:new(cite_inlines, ir.cite_item)} + -- end + util.extend(citation_stream, cite_inlines) previous_ir = ir if cite_suffix then @@ -223,6 +246,8 @@ function Citation:build_cluster(citation_items, engine, properties) end end + -- util.debug(citation_stream) + local has_printed_form = true if #citation_items == 0 then -- bugreports_AuthorOnlyFail.txt @@ -232,19 +257,19 @@ function Citation:build_cluster(citation_items, engine, properties) -- date_DateNoDateNoTest.txt has_printed_form = false citation_stream = {PlainText:new("[CSL STYLE ERROR: reference with no printed form.]")} - elseif #citation_stream == 1 and citation_stream[1].value == "[NO_PRINTED_FORM]" then + elseif #citation_stream == 1 and citation_stream[1]._type == "CiteInline" and + #citation_stream[1].inlines == 1 and citation_stream[1].inlines[1].value == "[NO_PRINTED_FORM]" then has_printed_form = false end local author_only_mode = (properties.mode == "author-only" or (#citation_items >= 1 and citation_items[1]["author-only"])) if has_printed_form and context.area.layout.affixes and not author_only_mode then - local affixes = context.area.layout.affixes - if affixes.prefix then - table.insert(citation_stream, 1, PlainText:new(affixes.prefix)) + if irs[1].cite_prefix then + table.insert(citation_stream, 1, PlainText:new(irs[1].cite_prefix)) end - if affixes.suffix then - table.insert(citation_stream, PlainText:new(affixes.suffix)) + if irs[#irs].cite_suffix then + table.insert(citation_stream, PlainText:new(irs[#irs].cite_suffix)) end end @@ -314,24 +339,53 @@ function Citation:sorted_citation_items(items, engine) return items end + +---@class CiteIr: IrNode +---@field cite_item CitationItem +---@field reference table +---@field ir_index any +---@field is_ambiguous boolean +---@field disam_level integer +---@field cite_delimiter string? +---@field cite_prefix string? +---@field cite_suffix string? + +---@param cite_item CitationItem +---@param output_format OutputFormat +---@param engine CiteProc +---@param properties table +---@return CiteIr function Citation:build_fully_disambiguated_ir(cite_item, output_format, engine, properties) local cite_ir = self:build_ambiguous_ir(cite_item, output_format, engine) -- util.debug(cite_ir) + -- if cite_item.id == "ITEM-3" then + -- util.debug(cite_item.id) + -- util.debug(cite_ir) + -- end cite_ir = self:apply_disambiguate_add_givenname(cite_ir, engine) cite_ir = self:apply_disambiguate_add_names(cite_ir, engine) cite_ir = self:apply_disambiguate_conditionals(cite_ir, engine) cite_ir = self:apply_disambiguate_add_year_suffix(cite_ir, engine) + -- if cite_item.id == "ITEM-3" then + -- util.debug(cite_item.id) + -- util.debug(cite_ir) + -- end return cite_ir end +---comment +---@param cite_item CitationItem +---@param output_format OutputFormat +---@param engine CiteProc +---@return CiteIr function Citation:build_ambiguous_ir(cite_item, output_format, engine) local state = IrState:new(engine.style) local context = Context:new() context.engine = engine context.style = engine.style context.area = self - context.locale = engine:get_locale(engine.lang) + -- context.locale = engine:get_locale(engine.lang) context.name_inheritance = self.name_inheritance context.format = output_format context.id = cite_item.id @@ -339,9 +393,12 @@ function Citation:build_ambiguous_ir(cite_item, output_format, engine) -- context.reference = self:get_item(cite_item.id) context.reference = engine.registry.registry[cite_item.id] + local active_layout, context_lang = util.get_layout_by_language(self, engine, context.reference) + context.locale = engine:get_locale(context_lang) + local ir if context.reference then - ir = self:build_ir(engine, state, context) + ir = self:build_ir(engine, state, context, active_layout) else ir = Rendered:new({Formatted:new({PlainText:new(cite_item.id)}, {["font-weight"] = "bold"})}, self) end @@ -353,6 +410,12 @@ function Citation:build_ambiguous_ir(cite_item, output_format, engine) ir.is_ambiguous = false ir.disam_level = 0 + ir.cite_delimiter = active_layout.delimiter + if active_layout.affixes then + ir.cite_prefix = active_layout.affixes.prefix + ir.cite_suffix = active_layout.affixes.suffix + end + -- Formattings like font-style are ignored for disambiguation. local disam_format = DisamStringFormat:new() local inlines = ir:flatten(disam_format) @@ -374,11 +437,11 @@ function Citation:build_ambiguous_ir(cite_item, output_format, engine) return ir end -function Citation:build_ir(engine, state, context) - if not self.layout then +function Citation:build_ir(engine, state, context, active_layout) + if not active_layout then util.error("Missing citation layout.") end - return self.layout:build_ir(engine, state, context) + return active_layout:build_ir(engine, state, context) end function Citation:apply_disambiguate_add_givenname(cite_ir, engine) @@ -875,14 +938,25 @@ function Citation:check_ambiguity(cite_ir, engine) return false end -function Citation:get_same_output_irs(cite_ir, engine) - local ambiguous_same_output_irs = {} - for _, ir_ in pairs(engine.cite_irs_by_output[cite_ir.disam_str]) do - if ir_.disam_str == cite_ir.disam_str then - table.insert(ambiguous_same_output_irs, ir_) +function Citation:get_ambiguous_cite_irs(cite_ir, engine) + local res = {} + for _, ir in pairs(engine.cite_irs_by_output[cite_ir.disam_str]) do + if ir.cite_item.id ~= cite_ir.cite_item.id then + table.insert(res, ir) end end - return ambiguous_same_output_irs + return res +end + +function Citation:get_ambiguous_same_output_cite_irs(cite_ir, engine) + -- This includes the cite_ir itself. + local res = {} + for _, ir in pairs(engine.cite_irs_by_output[cite_ir.disam_str]) do + if ir.disam_str == cite_ir.disam_str then + table.insert(res, ir) + end + end + return res end function Citation:apply_disambiguate_add_year_suffix(cite_ir, engine) @@ -890,60 +964,63 @@ function Citation:apply_disambiguate_add_year_suffix(cite_ir, engine) return cite_ir end - local same_output_irs = self:get_same_output_irs(cite_ir, engine) + local ambiguous_same_output_irs = self:get_ambiguous_same_output_cite_irs(cite_ir, engine) - table.sort(same_output_irs, function (a, b) + table.sort(ambiguous_same_output_irs, function (a, b) -- return a.ir_index < b.ir_index return a.reference["citation-number"] < b.reference["citation-number"] end) - local year_suffix_number = 0 - -- util.debug(cite_ir) - local disam_format = DisamStringFormat:new() - for _, ir_ in ipairs(same_output_irs) do + for _, ir_ in ipairs(ambiguous_same_output_irs) do ir_.reference.year_suffix_number = nil end - for _, ir_ in ipairs(same_output_irs) do - -- print(ir_.cite_item.id) - -- print(ir_.reference) + -- TODO: clear year-suffix after updateItems + local year_suffix_number = 0 + for _, ir_ in ipairs(ambiguous_same_output_irs) do if not ir_.reference.year_suffix_number then year_suffix_number = year_suffix_number + 1 ir_.reference.year_suffix_number = year_suffix_number ir_.reference["year-suffix"] = self:render_year_suffix(year_suffix_number) end + -- util.debug(string.format('%s: %s "%s"', ir_.cite_item.id, tostring(ir_.reference.year_suffix_number), ir_.reference["year-suffix"])) if not ir_.year_suffix_irs then ir_.year_suffix_irs = ir_:collect_year_suffix_irs() if #ir_.year_suffix_irs == 0 then - -- By default, the year-suffix is appended the first year rendered through cs:date + -- The style does not have a "year-suffix" variable. + -- Then the year-suffix is appended the first year rendered through cs:date local year_ir = ir_:find_first_year_ir() -- util.debug(year_ir) if year_ir then local year_suffix_ir = YearSuffix:new({}, self) table.insert(year_ir.children, year_suffix_ir) table.insert(ir_.year_suffix_irs, year_suffix_ir) + else + util.warning("No date variable for year-suffix") end end end + -- Render all the year-suffix irs. for _, year_suffix_ir in ipairs(ir_.year_suffix_irs) do - year_suffix_ir.inlines = {PlainText:new(ir_.reference["year-suffix"])} + -- util.debug(ir_.reference["year-suffix"]) + year_suffix_ir.inlines = { PlainText:new(ir_.reference["year-suffix"]) } year_suffix_ir.year_suffix_number = ir_.reference.year_suffix_number year_suffix_ir.group_var = "important" end - 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] = {} - end - engine.cite_irs_by_output[disam_str][ir_.ir_index] = ir_ - + -- DisamStringFormat doesn't render YearSuffix and this can be skipped. + -- local inlines = ir_:flatten(disam_format) + -- local disam_str = disam_format:output(inlines, nil) + -- util.debug(string.format('update %s: "%s" to "%s"', ir_.cite_item.id, ir_.disam_str, disam_str)) + -- ir_.disam_str = disam_str + -- if not engine.cite_irs_by_output[disam_str] then + -- engine.cite_irs_by_output[disam_str] = {} + -- end + -- engine.cite_irs_by_output[disam_str][ir_.ir_index] = ir_ end cite_ir.is_ambiguous = false |