feat: minus sign and number formatting
This commit is contained in:
@@ -1,6 +1,45 @@
|
||||
local NBSP = "\u{00A0}"
|
||||
local function nbsp() return pandoc.Str(NBSP) end
|
||||
|
||||
-- The true minus sign, U+2212 -- NOT the ASCII hyphen-minus (U+002D) that
|
||||
-- string.format emits by default, and NOT an en dash (U+2013, reserved for
|
||||
-- ranges like "10-20 ns"). U+2212 is metrically designed to match the
|
||||
-- width and vertical centering of "+" (U+002B), so signed values align
|
||||
-- visually the way they would in typeset mathematics. This is applied
|
||||
-- only at the final display step -- internal parsing below still matches
|
||||
-- against the plain ASCII "-" that Lua's own %g/%e formatting produces.
|
||||
local MINUS = "\u{2212}"
|
||||
local function displayMinus(s)
|
||||
return (s:gsub("^%-", MINUS))
|
||||
end
|
||||
|
||||
-- Non-breaking DIGIT-GROUPING separator, per the SI Brochure / NIST SP811 /
|
||||
-- IUPAC Green Book convention: group digits in threes using a thin space
|
||||
-- (never a comma or period, since those mean different things -- decimal
|
||||
-- vs. thousands separator -- depending on locale). U+202F NARROW NO-BREAK
|
||||
-- SPACE is used rather than U+2009 THIN SPACE because the latter is an
|
||||
-- ordinary breakable space and could split a number across a line wrap.
|
||||
local THIN_NBSP = "\u{202F}"
|
||||
|
||||
-- group a string of digits (no sign, no decimal point) into threes,
|
||||
-- e.g. "12345" -> "12" THIN_NBSP "345". Per NIST SP811, grouping is
|
||||
-- optional for exactly four digits and left ungrouped here; it is
|
||||
-- effectively mandatory from five digits up, since that's the point at
|
||||
-- which an ungrouped run of digits becomes hard to parse at a glance.
|
||||
local function groupDigits(digits)
|
||||
local len = #digits
|
||||
if len <= 4 then return digits end
|
||||
local firstLen = len % 3
|
||||
if firstLen == 0 then firstLen = 3 end
|
||||
local groups = { digits:sub(1, firstLen) }
|
||||
local i = firstLen + 1
|
||||
while i <= len do
|
||||
table.insert(groups, digits:sub(i, i + 2))
|
||||
i = i + 3
|
||||
end
|
||||
return table.concat(groups, THIN_NBSP)
|
||||
end
|
||||
|
||||
-- render a value already known to be in scientific-notation range
|
||||
-- returns a list of inlines: mantissa <NBSP> × <NBSP> 10^exp^
|
||||
local function formatSci(value)
|
||||
@@ -14,13 +53,13 @@ local function formatSci(value)
|
||||
mantissa = mantissa:gsub("0+$", ""):gsub("%.$", "")
|
||||
local expNum = tonumber(sign .. exp) -- keeps sign, drops leading zeros (e.g. "+23" -> 23, "-06" -> -6)
|
||||
|
||||
local out = { pandoc.Str(mantissa) }
|
||||
local out = { pandoc.Str(displayMinus(mantissa)) }
|
||||
if expNum ~= 0 then
|
||||
table.insert(out, nbsp())
|
||||
table.insert(out, pandoc.Str("×"))
|
||||
table.insert(out, nbsp())
|
||||
table.insert(out, pandoc.Str("10"))
|
||||
table.insert(out, pandoc.Superscript({ pandoc.Str(tostring(expNum)) }))
|
||||
table.insert(out, pandoc.Superscript({ pandoc.Str(displayMinus(tostring(expNum))) }))
|
||||
end
|
||||
return out
|
||||
end
|
||||
@@ -38,7 +77,14 @@ local function formatPlain(value)
|
||||
if s:find("[eE]") then
|
||||
return formatSci(value)
|
||||
end
|
||||
return { pandoc.Str(s) }
|
||||
-- split into optional sign, integer part, and remainder (decimal point
|
||||
-- + fractional digits, if any) so grouping applies only to the
|
||||
-- integer part -- fractional digits are never grouped
|
||||
local sign, intPart, rest = s:match("^(%-?)(%d+)(.*)$")
|
||||
if intPart then
|
||||
s = sign .. groupDigits(intPart) .. rest
|
||||
end
|
||||
return { pandoc.Str(displayMinus(s)) }
|
||||
end
|
||||
|
||||
-- decide plain vs. scientific, unless overridden.
|
||||
@@ -163,6 +209,22 @@ local function sci(args)
|
||||
return formatSci(value)
|
||||
end
|
||||
|
||||
-- {{< number 12345 >}} -> 12 345 (auto plain/sci, grouped)
|
||||
-- {{< number 12345 notation=plain >}} -> 12 345 (forced plain, still grouped)
|
||||
-- {{< number 12345 notation=sci >}} -> 1.23 × 10⁴
|
||||
-- Bare-number equivalent of the unit shortcodes above, for cases where a
|
||||
-- value has no associated unit but should still receive the same digit
|
||||
-- grouping and notation handling.
|
||||
local function number(args, kwargs)
|
||||
local value = tonumber(pandoc.utils.stringify(args[1]))
|
||||
local mode = kwargs and kwargs["notation"] and pandoc.utils.stringify(kwargs["notation"]) or nil
|
||||
if not value then
|
||||
io.stderr:write("[units] bad number() arg: '" .. pandoc.utils.stringify(args[1]) .. "'\n")
|
||||
return pandoc.Str("[number error]")
|
||||
end
|
||||
return formatNumber(value, mode)
|
||||
end
|
||||
|
||||
-- generic converter, parameterized by which table to use.
|
||||
-- optional kwargs.notation = "sci" | "plain" forces that format
|
||||
-- for both the source value and (if present) the converted value.
|
||||
@@ -282,6 +344,7 @@ end
|
||||
return {
|
||||
["unit"] = unit,
|
||||
["sci"] = sci,
|
||||
["number"] = number,
|
||||
["energy"] = makeQuantityShortcode(ENERGY_UNITS, "energy"),
|
||||
["molarenergy"] = makeQuantityShortcode(MOLAR_ENERGY_UNITS, "molarenergy"),
|
||||
["distance"] = makeQuantityShortcode(DISTANCE_UNITS, "distance"),
|
||||
|
||||
Reference in New Issue
Block a user