WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Commit 711508f

Browse files
committed
fix(packages): Perform superscript folding on ordinal numbers in bibliographies
We had the "superfolding" on terms implemented in the CSL engine class, but not on ordinal numbers (whose terms where left unprocessed). Code refactored to move that logic in the CSL locale class earlier, and this applied to all term values.
1 parent 5188da2 commit 711508f

File tree

2 files changed

+53
-52
lines changed

2 files changed

+53
-52
lines changed

packages/bibtex/csl/engine.lua

Lines changed: 23 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333

3434
local CslLocale = require("packages.bibtex.csl.locale")
3535

36-
local superfolding = require("packages.bibtex.csl.utils.superfolding")
3736
local endash = luautf8.char(0x2013)
3837
local emdash = luautf8.char(0x2014)
3938

@@ -70,18 +69,18 @@ function CslEngine:_init (style, locale, extras)
7069

7170
-- Cache for some small string operations (e.g. XML escaping)
7271
-- to avoid repeated processing.
73-
self.cache = {}
72+
self._cache = {}
7473

7574
-- Early lookups for often used localized punctuation marks
7675
self.punctuation = {
77-
open_quote = self:_render_term("open-quote") or luautf8.char(0x201C), -- 0x201C curly left quote
78-
close_quote = self:_render_term("close-quote") or luautf8.char(0x201D), -- 0x201D curly right quote
79-
open_inner_quote = self:_render_term("open-inner-quote") or luautf8.char(0x2018), -- 0x2018 curly left single quote
80-
close_inner_quote = self:_render_term("close-inner-quote") or luautf8.char(0x2019), -- 0x2019 curly right single quote
81-
page_range_delimiter = self:_render_term("page-range-delimiter") or endash,
82-
[","] = self:_render_term("comma") or ",",
83-
[";"] = self:_render_term("semicolon") or ";",
84-
[":"] = self:_render_term("colon") or ":",
76+
open_quote = self.locale:term("open-quote") or luautf8.char(0x201C), -- 0x201C curly left quote
77+
close_quote = self.locale:term("close-quote") or luautf8.char(0x201D), -- 0x201D curly right quote
78+
open_inner_quote = self.locale:term("open-inner-quote") or luautf8.char(0x2018), -- 0x2018 curly left single quote
79+
close_inner_quote = self.locale:term("close-inner-quote") or luautf8.char(0x2019), -- 0x2019 curly right single quote
80+
page_range_delimiter = self.locale:term("page-range-delimiter") or endash,
81+
[","] = self.locale:term("comma") or ",",
82+
[";"] = self.locale:term("semicolon") or ";",
83+
[":"] = self.locale:term("colon") or ":",
8584
}
8685

8786
-- Small utility for page ranges, see text processing for <text variable="page">
@@ -268,24 +267,6 @@ end
268267

269268
-- INTERNAL HELPERS
270269

271-
function CslEngine:_render_term (name, form, plural)
272-
local t = self.locale:term(name, form, plural)
273-
if t then
274-
if self.cache[t] then
275-
return self.cache[t]
276-
end
277-
t = self:_xmlEscape(t)
278-
-- The CSL specification states, regarding terms:
279-
-- "Superscripted Unicode characters can be used for superscripting."
280-
-- We replace the latter with their normal form, wrapped in a command.
281-
-- The result is cached in the term object to avoid repeated processing.
282-
-- (Done after XML escaping as superfolding may add commands.)
283-
t = superfolding(t)
284-
self.cache[t] = t
285-
end
286-
return t
287-
end
288-
289270
function CslEngine:_render_text_specials (value)
290271
-- Extensions for italic and math...
291272
-- CAVEAT: the implementation is fairly naive.
@@ -323,8 +304,8 @@ function CslEngine:_xmlEscape (t)
323304
end
324305

325306
function CslEngine:_punctuation_extra (t)
326-
if self.cache[t] then
327-
return self.cache[t]
307+
if self._cache[t] then
308+
return self._cache[t]
328309
end
329310
if self.extras.localizedPunctuation then
330311
-- non-standard: localized punctuation
@@ -333,7 +314,7 @@ function CslEngine:_punctuation_extra (t)
333314
end)
334315
end
335316
t = self:_xmlEscape(t)
336-
self.cache[t] = t
317+
self._cache[t] = t
337318
return t
338319
end
339320

@@ -532,7 +513,7 @@ function CslEngine:_text (options, content, entry)
532513
SU.error("CSL macro " .. options.macro .. " not found")
533514
end
534515
elseif options.term then
535-
t = self:_render_term(options.term, options.form, SU.boolean(options.plural, false))
516+
t = self.locale:term(options.term, options.form, SU.boolean(options.plural, false))
536517
elseif options.variable then
537518
variable = options.variable
538519
t = entry[variable]
@@ -609,7 +590,7 @@ function CslEngine:_a_day (options, day, month) -- month needed to get gender fo
609590
local genderForm
610591
if month then
611592
local monthKey = ("month-%02d"):format(month)
612-
local _, gender = self:_render_term(monthKey)
593+
local _, gender = self.locale:term(monthKey)
613594
genderForm = gender or "neuter"
614595
end
615596
if SU.boolean(self.locale.styleOptions["limit-day-ordinals-to-day-1"], false) then
@@ -632,7 +613,7 @@ function CslEngine:_a_month (options, month)
632613
t = ("%02d"):format(month)
633614
else -- short or long (default)
634615
local monthKey = ("month-%02d"):format(month)
635-
t = self:_render_term(monthKey, form or "long")
616+
t = self.locale:term(monthKey, form or "long")
636617
end
637618
t = self:_render_stripPeriods(t, options)
638619
return t
@@ -647,7 +628,7 @@ function CslEngine:_a_season (options, season)
647628
SU.warn("CSL season formatting as a number is ignored")
648629
else
649630
local seasonKey = ("season-%02d"):format(season)
650-
t = self:_render_term(seasonKey, form or "long")
631+
t = self.locale:term(seasonKey, form or "long")
651632
end
652633
t = self:_render_stripPeriods(t, options)
653634
return t
@@ -802,7 +783,7 @@ function CslEngine:_number (options, content, entry)
802783
value = value and value.value
803784
end
804785
if value then
805-
local _, gender = self:_render_term(variable)
786+
local _, gender = self.locale:term(variable)
806787
local genderForm = gender or "neuter"
807788

808789
-- FIXME TODO: Some complex stuff about name ranges, commas, etc. in the spec.
@@ -876,7 +857,7 @@ function CslEngine:_substitute (options, content, entry)
876857
end
877858

878859
function CslEngine:_name_et_al (options)
879-
local t = self:_render_term(options.term or "et-al")
860+
local t = self.locale:term(options.term or "et-al")
880861
t = self:_render_formatting(t, options)
881862
return t
882863
end
@@ -1230,25 +1211,25 @@ function CslEngine:_names (options, content, entry)
12301211
if and_opt == "symbol" then
12311212
and_word = "&amp;"
12321213
elseif and_opt == "text" then
1233-
and_word = self:_render_term("and")
1214+
and_word = self.locale:term("and")
12341215
end
12351216
local name_delimiter = name_node.options.delimiter or inherited_opts["names-delimiter"] or ", "
12361217
-- local delimiter_precedes_et_al = name_node.options["delimiter-precedes-et-al"] -- FIXME NOT IMPLEMENTED
12371218
local delimiter_precedes_last = name_node.options["delimiter-precedes-last"]
12381219
or inherited_opts["delimiter-precedes-last"]
12391220
or "contextual"
12401221

1241-
if name_delimiter and not self.cache[name_delimiter] then
1222+
if name_delimiter and not self._cache[name_delimiter] then
12421223
name_delimiter = self:_xmlEscape(name_delimiter)
1243-
self.cache[name_delimiter] = name_delimiter
1224+
self._cache[name_delimiter] = name_delimiter
12441225
end
12451226

12461227
local resolved = {
12471228
variable = SU.required(name_node.options, "variable", "CSL names"),
12481229
et_al_min = et_al_min,
12491230
et_al_use_first = et_al_use_first,
12501231
and_word = and_word,
1251-
name_delimiter = name_delimiter and self.cache[name_delimiter],
1232+
name_delimiter = name_delimiter and self._cache[name_delimiter],
12521233
is_label_first = is_label_first,
12531234
label_opts = label_opts,
12541235
et_al_opts = et_al_opts,
@@ -1292,7 +1273,7 @@ function CslEngine:_label (options, content, entry)
12921273
end
12931274
end
12941275
end
1295-
value = self:_render_term(variable, options.form or "long", plural)
1276+
value = self.locale:term(variable, options.form or "long", plural)
12961277
value = self:_render_stripPeriods(value, options)
12971278
value = self:_render_textCase(value, options)
12981279
value = self:_render_formatting(value, options)

packages/bibtex/csl/locale.lua

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
local casing = require("packages.bibtex.csl.utils.casing")
1616
local xmlparser = require("packages.bibtex.csl.utils.xmlparser")
17+
local superfolding = require("packages.bibtex.csl.utils.superfolding")
1718

1819
local parse = xmlparser.parse
1920
local rules = {
@@ -31,6 +32,10 @@ function CslLocale:_init (tree)
3132
self.dates = {}
3233
self.styleOptions = {}
3334
self:_preprocess(tree)
35+
36+
-- Cache for some small string operations to avoid repeated processing
37+
-- (XML escaping and superfolding)
38+
self._cache = {}
3439
end
3540

3641
-- Store items from the syntax tree in more convenient structures and maps
@@ -70,8 +75,21 @@ function CslLocale:_preprocess (tree)
7075
end
7176
end
7277

73-
function CslLocale:_termvalue (term) -- luacheck: no unused args
74-
return term[1]
78+
function CslLocale:_termvalue (term)
79+
local t = term[1]
80+
if t then
81+
if self._cache[t] then
82+
return self._cache[t]
83+
end
84+
-- XML escape the term value
85+
t = t:gsub("&", "&amp;"):gsub("<", "&lt;"):gsub(">", "&gt;")
86+
-- The CSL specification states, regarding terms:
87+
-- "Superscripted Unicode characters can be used for superscripting."
88+
-- We replace the latter with their normal form, wrapped in a command.
89+
t = superfolding(t)
90+
self._cache[t] = t
91+
end
92+
return t
7593
end
7694

7795
function CslLocale:_lookupTerm (name, form, genderf)
@@ -158,11 +176,13 @@ function CslLocale:date (form)
158176
end
159177

160178
--- Lookup a term in the locale.
161-
-- Reserved for non-ordinal terms.
179+
--
180+
-- This method is intended to be used for non-ordinal terms.
162181
-- @tparam string name The name of the term
163182
-- @tparam string form The form of the term (default: "long")
164183
-- @tparam boolean plural Whether to return the plural form (default: false)
165-
-- @treturn string,string The term (or empty string), and the gender or the term (or nil)
184+
-- @treturn string|nil The term
185+
-- @treturn string|nil The gender of the term when applicable
166186
function CslLocale:term (name, form, plural)
167187
local term = self:_lookupTerm(name, form)
168188
if not term then
@@ -173,18 +193,18 @@ function CslLocale:term (name, form, plural)
173193
end
174194
local sgpl = SU.ast.findInTree(term, plural and "cs:multiple" or "cs:single")
175195
if not sgpl then
176-
pl.pretty.dump(term)
177196
return SU.error("CSL term error for singular/multiple: " .. name)
178197
end
179198
return self:_termvalue(sgpl), term.options.gender
180199
end
181200

182-
--- Lookup an ordinal term in the locale.
183-
-- Reserved for ordinal terms.
201+
202+
--- Render an ordinal number in the locale, using the appropriate term.
184203
-- @tparam number number The numeric value to be formatted
185-
-- @tparam string form The form of the term (default: "short")
186-
-- @tparam string genderf The gender-form of the term (default: "neuter")
187-
-- @tparam boolean plural Whether to return the plural form (default: false)
204+
-- @tparam string|nil form The form of the term (default: "short")
205+
-- @tparam string|nil genderf The gender-form of the term (default: "neuter")
206+
-- @tparam boolean|nil plural Whether to return the plural form (default: false)
207+
-- @treturn string The formatted ordinal term
188208
function CslLocale:ordinal (number, form, genderf, plural)
189209
if form == "long" then
190210
-- TODO FIXME: Not sure this is widely used, not bothering for now

0 commit comments

Comments
 (0)