diff options
Diffstat (limited to 'biblio/citation-style-language/citeproc-node-text.lua')
-rw-r--r-- | biblio/citation-style-language/citeproc-node-text.lua | 330 |
1 files changed, 151 insertions, 179 deletions
diff --git a/biblio/citation-style-language/citeproc-node-text.lua b/biblio/citation-style-language/citeproc-node-text.lua index e26a480438..fae3535013 100644 --- a/biblio/citation-style-language/citeproc-node-text.lua +++ b/biblio/citation-style-language/citeproc-node-text.lua @@ -4,224 +4,196 @@ -- Repository: https://github.com/zepinglee/citeproc-lua -- -local text = {} +local text_module = {} -local element = require("citeproc-element") -local richtext = require("citeproc-richtext") +local Element = require("citeproc-element").Element +local Rendered = require("citeproc-ir-node").Rendered +local YearSuffix = require("citeproc-ir-node").YearSuffix +local PlainText = require("citeproc-output").PlainText +local Linked = require("citeproc-output").Linked local util = require("citeproc-util") -local Text = element.Element:new() - -function Text:render (item, context) - self:debug_info(context) - context = self:process_context(context) +-- [Text](https://docs.citationstyles.org/en/stable/specification.html#text) +local Text = Element:derive("text", { + -- Default attributes + variable = nil, + form = "long", + macro = nil, + term = nil, + plural = false, + value = nil, + -- Style behavior + formatting = nil, + affixes = nil, + delimiter = nil, + display = nil, + quotes = false, + strip_periods = false, + text_case = nil, +}) + +function Text:from_node(node) + local o = Text:new() + o:set_attribute(node, "variable") + o:set_attribute(node, "form") + o:set_attribute(node, "macro") + o:set_attribute(node, "term") + o:set_bool_attribute(node, "plural") + o:set_attribute(node, "value") + + o:set_formatting_attributes(node) + o:set_affixes_attributes(node) + o:set_display_attribute(node) + o:set_quotes_attribute(node) + o:set_strip_periods_attribute(node) + o:set_text_case_attribute(node) + return o +end - local res = nil +function Text:build_ir(engine, state, context) + local ir = nil + if self.variable then + ir = self:build_variable_ir(engine, state, context) + elseif self.macro then + ir = self:build_macro_ir(engine, state, context) + elseif self.term then + ir = self:build_term_ir(engine, state, context) + elseif self.value then + ir = self:build_value_ir(engine, state, context) + end + return ir +end - local variable = nil - local variable_name = self:get_attribute("variable") - if variable_name then - local form = self:get_attribute("form") - if form == "short" then - variable = self:get_variable(item, variable_name .. "-" .. form, context) - end - if not variable then - variable = self:get_variable(item, variable_name, context) - end - if variable then - res = variable - if type(res) == "number" then - res = tostring(res) - end - if variable_name == "page" or variable_name == "locator" then - res = util.lstrip(res) - res = self:_format_page(res, context) - end - end +function Text:build_variable_ir(engine, state, context) + local variable = self.variable + local text - table.insert(context.variable_attempt, res ~= nil) + if variable == "year-suffix" then + return self:build_year_suffix_ir(engine, state, context) end - local macro_name = self:get_attribute("macro") - if macro_name then - local macro = self:get_macro(macro_name) - res = macro:render(item, context) + if not state.suppressed[variable] then + text = context:get_variable(variable, self.form) end - local term_name = self:get_attribute("term") - if term_name then - local form = self:get_attribute("form") - - local term = self:get_term(term_name, form) - if term then - res = term:render(context) - end + if not text then + local ir = Rendered:new({}, self) + ir.group_var = "missing" + return ir end - - local value = self:get_attribute("value") - if value then - res = value - res = self:escape(res) + if type(text) == "number" then + text = tostring(text) end - - if type(res) == "string" and res ~= "" then - res = richtext.new(res) + if util.variable_types[variable] == "number" then + text = self:format_number(text, variable, "numeric", context) end - if res and variable_name == "URL" then - res:add_format("URL", "true") + local inlines + if (variable == "URL" and engine.opt.url_link) or + (variable == "DOI" and engine.opt.doi_link) or + (variable == "PMID" and engine.opt.doi_link) or + (variable == "PMID" and engine.opt.doi_link) then + inlines = self:render_linked(engine, state, context, variable, text) + else + inlines = self:render_text_inlines(text, context) end - res = self:strip_periods(res, context) - res = self:case(res, context) - res = self:format(res, context) - res = self:quote(res, context) - res = self:wrap(res, context) - res = self:display(res, context) + local ir = Rendered:new(inlines, self) + ir.group_var = "important" - if variable_name == "citation-number" then - res = self:_process_citation_number(variable, res, context) + if variable == "citation-number" then + ir.citation_number = context.reference["citation-number"] end - return res -end - - -function Text:_process_citation_number(citation_number, res, context) - if context.mode == "citation" and not context.sorting and context.options["collapse"] == "citation-number" then - context.build.item_citation_numbers[context.item.id] = citation_number - if type(res) == "string" then - res = richtext.new(res) - end - table.insert(context.build.item_citation_number_text, res) + -- Suppress substituted name variable + if state.name_override and not context.sort_key then + state.suppressed[variable] = true end - return res -end - -function Text:_format_page (page, context) - local res = nil - - local page_range_delimiter = self:get_term("page-range-delimiter"):render(context) or util.unicode["en dash"] - local page_range_format = context.options["page-range-format"] - if page_range_format == "chicago" then - if self:get_style():get_version() >= "1.1" then - page_range_format = "chicago-16" - else - page_range_format = "chicago-15" - end - end + return ir +end - local last_position = 1 - local page_parts = {} - local punct_list = {} - for part, punct, pos in string.gmatch(page, "(.-)%s*([,&])%s*()") do - table.insert(page_parts, part) - table.insert(punct_list, punct) - last_position = pos - end - table.insert(page_parts, string.sub(page, last_position)) - - res = "" - for i, part in ipairs(page_parts) do - res = res .. self:_format_range(part, page_range_format, page_range_delimiter) - local punct = punct_list[i] - if punct then - if punct == "&" then - res = res .. " " .. punct .. " " - else - res = res .. punct .. " " - end +function Text:render_linked(engine, state, context, variable, text) + local href + if variable == "URL" then + href = text + elseif self.affixes and self.affixes.prefix then + if string.match(self.affixes.prefix, "https?://") then + text = self.affixes.prefix .. text + self.affixes.prefix = nil + href = text end - end - res = self:escape(res) - return res + elseif variable == "DOI" then + href = "https://doi.org/" .. text + elseif variable == "PMID" then + href = "https://www.ncbi.nlm.nih.gov/pubmed/" .. text + elseif variable == "PMCID" then + href = "https://www.ncbi.nlm.nih.gov/pmc/articles/" .. text + end + local inlines = {Linked:new(text, href)} + local output_format = context.format + local localized_quotes = nil + if self.quotes then + localized_quotes = context:get_localized_quotes() + end + inlines = output_format:with_format(inlines, self.formatting) + inlines = output_format:affixed_quoted(inlines, self.affixes, localized_quotes) + return output_format:with_display(inlines, self.display) end -function Text:_format_range (str, format, range_delimiter) - local start, delimiter, stop = string.match(str, "(%w+)%s*(%-+)%s*(%S*)") - if not stop or stop == "" then - return string.gsub(str, "\\%-", "-") - end - - - local start_prefix, start_num = string.match(start, "(.-)(%d*)$") - local stop_prefix, stop_num = string.match(stop, "(.-)(%d*)$") - - if start_prefix ~= stop_prefix then - -- Not valid range: "n11564-1568" -> "n11564-1568" - -- 110-N6 - -- N110-P5 - return start .. delimiter .. stop - end - - if format == "chicago-16" then - stop = self:_format_range_chicago_16(start_num, stop_num) - elseif format == "chicago-15" then - stop = self:_format_range_chicago_15(start_num, stop_num) - elseif format == "expanded" then - stop = stop_prefix .. self:_format_range_expanded(start_num, stop_num) - elseif format == "minimal" then - stop = self:_format_range_minimal(start_num, stop_num) - elseif format == "minimal-two" then - stop = self:_format_range_minimal(start_num, stop_num, 2) +function Text:build_year_suffix_ir(engine, state, context) + local text = context:get_variable(self.variable, self.form) + local group_var + if text then + group_var = "important" + else + text = "" + group_var = "missing" end - return start .. range_delimiter .. stop -end - -function Text:_format_range_chicago_16(start, stop) - if #start < 3 or string.sub(start, -2) == "00" then - return self:_format_range_expanded(start, stop) - elseif string.sub(start, -2, -2) == "0" then - return self:_format_range_minimal(start, stop) - else - return self:_format_range_minimal(start, stop, 2) + local ir = YearSuffix:new({PlainText:new(text)}, self) + ir.group_var = group_var + ir.affixes = util.clone(self.affixes) + ir.display = self.display + ir.formatting = util.clone(self.formatting) + if self.quotes then + ir.quotes = context:get_localized_quotes() end - return stop + return ir end -function Text:_format_range_chicago_15(start, stop) - if #start < 3 or string.sub(start, -2) == "00" then - return self:_format_range_expanded(start, stop) - else - local changed_digits = self:_format_range_minimal(start, stop) - if string.sub(start, -2, -2) == "0" then - return changed_digits - elseif #start == 4 and #changed_digits == 3 then - return self:_format_range_expanded(start, stop) - else - return self:_format_range_minimal(start, stop, 2) +function Text:build_macro_ir(engine, state, context) + local macro = context:get_macro(self.macro) + state:push_macro(self.macro) + local ir = macro:build_ir(engine, state, context) + state:pop_macro(self.macro) + if ir then + ir.affixes = util.clone(self.affixes) + ir.display = self.display + ir.formatting = util.clone(self.formatting) + if self.quotes then + ir.quotes = context:get_localized_quotes() end end - return stop + return ir end -function Text:_format_range_expanded(start, stop) - -- Expand "1234–56" -> "1234–1256" - if #start <= #stop then - return stop +function Text:build_term_ir(engine, state, context) + local str = context:get_simple_term(self.term, self.form, self.plural) + if not str then + return nil end - return string.sub(start, 1, #start - #stop) .. stop + local inlines = self:render_text_inlines(str, context) + return Rendered:new(inlines, self) end -function Text:_format_range_minimal(start, stop, threshold) - threshold = threshold or 1 - if #start < #stop then - return stop - end - local offset = #start - #stop - for i = 1, #stop - threshold do - local j = i + offset - if string.sub(stop, i, i) ~= string.sub(start, j, j) then - return string.sub(stop, i) - end - end - return string.sub(stop, -threshold) +function Text:build_value_ir(engine, state, context) + local inlines = self:render_text_inlines(self.value, context) + return Rendered:new(inlines, self) end -text.Text = Text +text_module.Text = Text -return text +return text_module |