summaryrefslogtreecommitdiff
path: root/biblio/citation-style-language/citeproc-node-text.lua
diff options
context:
space:
mode:
Diffstat (limited to 'biblio/citation-style-language/citeproc-node-text.lua')
-rw-r--r--biblio/citation-style-language/citeproc-node-text.lua330
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