diff options
author | Norbert Preining <norbert@preining.info> | 2022-08-19 03:00:56 +0000 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2022-08-19 03:00:56 +0000 |
commit | 1772be123f1cfa713b25548f6fec135e7ab339a3 (patch) | |
tree | 1ba6586dc52536e962cb28380567183f6f90bbc1 /biblio/citation-style-language/citeproc-node-sort.lua | |
parent | 982d5e88b736a798b356bf5cabe5e6c9b115f182 (diff) |
CTAN sync 202208190300
Diffstat (limited to 'biblio/citation-style-language/citeproc-node-sort.lua')
-rw-r--r-- | biblio/citation-style-language/citeproc-node-sort.lua | 213 |
1 files changed, 129 insertions, 84 deletions
diff --git a/biblio/citation-style-language/citeproc-node-sort.lua b/biblio/citation-style-language/citeproc-node-sort.lua index 1b82d21b55..876c3257da 100644 --- a/biblio/citation-style-language/citeproc-node-sort.lua +++ b/biblio/citation-style-language/citeproc-node-sort.lua @@ -8,57 +8,80 @@ local sort = {} local unicode = require("unicode") -local element = require("citeproc-element") +local Element = require("citeproc-element").Element +local Date = require("citeproc-node-date").Date local names = require("citeproc-node-names") -local date = require("citeproc-node-date") +local InlineElement = require("citeproc-output").InlineElement local util = require("citeproc-util") -local Sort = element.Element:new() +-- [Sorting](https://docs.citationstyles.org/en/stable/specification.html#sorting) +local Sort = Element:derive("sort") -function Sort:sort (items, context) +function Sort:from_node(node) + local o = Sort:new() + o.children = {} + + o:process_children_nodes(node) + o.sort_directions = {} + for i, key in ipairs(o.children) do + o.sort_directions[i] = (key.sort ~= "descending") + end + table.insert(o.sort_directions, true) + + return o +end + +function Sort:sort(items, state, context) -- key_map = { -- id1 = {key1, key2, ...}, -- id2 = {key1, key2, ...}, -- ... -- } - context.variable_attempt = {} - local key_map = {} - local sort_directions = {} + local sort_directions = self.sort_directions -- true: ascending -- false: descending - if not Sort.collator_obj then - local lang = context.style.lang + if not Sort.collator then + local lang = context.engine.lang local language = string.sub(lang, 1, 2) -- It's 6 seconds slower to run the whole test-suite if these package -- loading statements are put in the header. - local ducet = require("lua-uca.lua-uca-ducet") - local collator = require("lua-uca.lua-uca-collator") - local languages = require("lua-uca.lua-uca-languages") - local collator_obj = collator.new(ducet) - if languages[language] then - Sort.collator_obj = languages[language](collator_obj) - else - util.warning(string.format('Lcoale "%s" is not supported.', lang)) + local uca_ducet = require("lua-uca.lua-uca-ducet") + local uca_collator = require("lua-uca.lua-uca-collator") + Sort.collator = uca_collator.new(uca_ducet) + if language ~= "en" then + local uca_languages = require("lua-uca.lua-uca-languages") + if uca_languages[language] then + Sort.collator = uca_languages[language](Sort.collator) + else + util.warning(string.format('Locale "%s" is not provided by lua-uca. The sorting order may be incorrect.', lang)) + end end end - for _, item in ipairs(items) do - if not key_map[item.id] then - key_map[item.id] = {} - - context.item = item - for i, key in ipairs(self:query_selector("key")) do - if sort_directions[i] == nil then - local direction = (key:get_attribute("sort") ~= "descending") - sort_directions[i] = direction - end - local value = key:render(item, context) - table.insert(key_map[item.id], value) + -- TODO: optimize: use cached values for fixed keys + for i, item in ipairs(items) do + key_map[item.id] = {} + + context.id = item.id + context.cite = item + context.reference = context.engine.registry.registry[item.id] + + for j, key in ipairs(self.children) do + if context.reference then + context.sort_key = key + local key_str = key:eval(context.engine, state, context) + key_map[item.id][j] = key_str + else + -- The entry is missing + key_map[item.id][j] = false end end + -- To preserve the original order of items with same sort keys + -- sort_NameImplicitSortOrderAndForm.txt + table.insert(key_map[item.id], i) end -- util.debug(key_map) @@ -66,6 +89,7 @@ function Sort:sort (items, context) local function compare_entry(item1, item2) return self.compare_entry(key_map, sort_directions, item1, item2) end + -- util.debug(items) table.sort(items, compare_entry) return items @@ -80,8 +104,8 @@ function Sort.compare(value1, value2) end function Sort.compare_strings(str1, str2) - if Sort.collator_obj then - return Sort.collator_obj:compare_strings(str1, str2) + if Sort.collator then + return Sort.collator:compare_strings(str1, str2) else return str1 < str2 end @@ -109,77 +133,98 @@ function Sort.compare_entry(key_map, sort_directions, item1, item2) end end -local Key = element.Element:new() - -function Key:render (item, context) - context = self:process_context(context) - context.options["name-as-sort-order"] = "all" - context.sorting = true - local variable = self:get_attribute("variable") - local res = nil - if variable then - context.variable = variable - local variable_type = util.variable_types[variable] +local Key = Element:derive("key") + +function Key:new() + local o = Element.new(self) + Key.sort = "ascending" + return o +end + +function Key:from_node(node) + local o = Key:new() + o:set_attribute(node, "sort") + o:set_attribute(node, "variable") + o:set_attribute(node, "macro") + o:set_number_attribute(node, "names-min") + o:set_number_attribute(node, "names-use-first") + o:set_bool_attribute(node, "names-use-last") + return o +end + +function Key:eval(engine, state, context) + local res + if self.variable then + local variable_type = util.variable_types[self.variable] if variable_type == "name" then - res = self:_render_name(item, context) + res = self:eval_name(engine, state, context) elseif variable_type == "date" then - res = self:_render_date(item, context) + res = self:eval_date(context) elseif variable_type == "number" then - res = item[variable] + local value = context:get_variable(self.variable) + if type(value) == "string" and string.match(value, "%s+") then + value = tonumber(value) + end + res = value else - res = item[variable] + res = context:get_variable(self.variable) + if type(res) == "string" then + local inlines = InlineElement:parse(res, context) + res = context.format:output(inlines) + end end - else - local macro = self:get_attribute("macro") - if macro then - res = self:get_macro(macro):render(item, context) + elseif self.macro then + 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.name_count then + return ir.name_count + elseif ir.sort_key ~= nil then + return ir.sort_key end + local output_format = context.format + local inlines = ir:flatten(output_format) + -- util.debug(inlines) + local str = output_format:output(inlines) + return str end if res == nil then + -- make table.insert(_, nil) work res = false - elseif type(res) == "table" and res._type == "RichText" then - res = res:render(nil, context) - end - if type(res) == "string" then - res = self._normalize_string(res) end return res end -function Key:_render_name (item, context) - if not self.names then - self.names = self:create_element("names", {}, self) - names.Names:set_base_class(self.names) - self.names:set_attribute("variable", context.options["variable"]) - self.names:set_attribute("form", "long") +function Key:eval_name(engine, state, context) + if not self.name_inheritance then + self.name_inheritance = util.clone(context.name_inheritance) end - local res = self.names:render(item, context) - return res + local name = context:get_variable(self.variable) + if not name then + return false + end + local ir = self.name_inheritance:build_ir(self.variable, nil, nil, engine, state, context) + if ir.name_count then + -- name count + return ir.name_count + end + local output_format = context.format + local inlines = ir:flatten(output_format) + + local str = output_format:output(inlines) + + return str end -function Key:_render_date (item, context) +function Key:eval_date(context) if not self.date then - self.date = self:create_element("date", {}, self) - date.Date:set_base_class(self.date) - self.date:set_attribute("variable", context.options["variable"]) - self.date:set_attribute("form", "numeric") + self.date = Date:new() + self.date.variable = self.variable + self.date.form = "numeric" + self.date.date_parts = "year-month-day" end - local res = self.date:render(item, context) - return res -end -function Key._normalize_string(str) - str = unicode.utf8.lower(str) - str = string.gsub(str, "[%[%]]", "") - local words = {} - for _, word in ipairs(util.split(str, " ")) do - -- TODO: strip leading prepositions - -- remove leading apostrophe on name particle - word = string.gsub(word, "^" .. util.unicode["apostrophe"], "") - table.insert(words, word) - end - str = table.concat(words, " ") - str = string.gsub(str, util.unicode["apostrophe"], "'") - return str + return self.date:render_sort_key(context.engine, nil, context) end |