mirror of
https://github.com/wolfpld/tracy.git
synced 2026-06-08 08:33:48 +00:00
154 lines
6.2 KiB
Lua
154 lines
6.2 KiB
Lua
function Link(el)
|
||
el.attributes['reference-type'] = nil
|
||
el.attributes['reference'] = nil
|
||
return el
|
||
end
|
||
|
||
-- Drop Div wrappers (e.g. table/titlepage containers), keeping their content.
|
||
function Div(el)
|
||
return el.content
|
||
end
|
||
|
||
-- ---------------------------------------------------------------------------
|
||
-- LaTeX math -> plain-text approximation.
|
||
--
|
||
-- The target Markdown renderer has no math support, so a raw "$\frac{1}{2}$"
|
||
-- would show verbatim. We turn each math node into the closest Unicode/ASCII
|
||
-- equivalent: fractions become "a/b", \times becomes "x", super/subscripts use
|
||
-- Unicode digits, and the one multi-line display equation becomes a fenced
|
||
-- code block (Markdown collapses plain newlines, a code block keeps them).
|
||
-- ---------------------------------------------------------------------------
|
||
|
||
local sup = {['0']='⁰',['1']='¹',['2']='²',['3']='³',['4']='⁴',['5']='⁵',
|
||
['6']='⁶',['7']='⁷',['8']='⁸',['9']='⁹',['+']='⁺',['-']='⁻',
|
||
['=']='⁼',['(']='⁽',[')']='⁾'}
|
||
local sub = {['0']='₀',['1']='₁',['2']='₂',['3']='₃',['4']='₄',['5']='₅',
|
||
['6']='₆',['7']='₇',['8']='₈',['9']='₉',['+']='₊',['-']='₋',
|
||
['=']='₌',['(']='₍',[')']='₎'}
|
||
|
||
-- Symbol replacements, applied as literal substitutions. Longer commands must
|
||
-- precede those that are a prefix of them (e.g. \rightarrow before \right).
|
||
local symbols = {
|
||
{'\\leftrightarrow','↔'}, {'\\rightarrow','→'}, {'\\leftarrow','←'},
|
||
{'\\Rightarrow','⇒'}, {'\\Leftarrow','⇐'}, {'\\to','→'}, {'\\mapsto','↦'},
|
||
{'\\times','×'}, {'\\cdot','·'}, {'\\div','÷'}, {'\\ast','*'}, {'\\star','*'},
|
||
{'\\leq','≤'}, {'\\geq','≥'}, {'\\neq','≠'}, {'\\approx','≈'}, {'\\equiv','≡'},
|
||
{'\\ll','«'}, {'\\gg','»'}, {'\\le','≤'}, {'\\ge','≥'},
|
||
{'\\ldots','…'}, {'\\cdots','…'}, {'\\dots','…'}, {'\\infty','∞'},
|
||
{'\\pm','±'}, {'\\mp','∓'}, {'\\propto','∝'}, {'\\sum','Σ'}, {'\\prod','Π'},
|
||
{'\\alpha','α'}, {'\\beta','β'}, {'\\gamma','γ'}, {'\\delta','δ'}, {'\\Delta','Δ'},
|
||
{'\\mu','µ'}, {'\\sigma','σ'}, {'\\pi','π'}, {'\\lambda','λ'}, {'\\theta','θ'},
|
||
{'\\left',''}, {'\\right',''},
|
||
{'\\qquad',' '}, {'\\quad',' '}, {'\\,',' '}, {'\\;',' '}, {'\\:',' '},
|
||
{'\\ ',' '}, {'\\!',''},
|
||
{'\\%','%'}, {'\\#','#'}, {'\\&','&'}, {'\\_','_'}, {'\\{','{'}, {'\\}','}'},
|
||
{'\\$','$'},
|
||
}
|
||
|
||
-- Literal (non-pattern) string replacement; avoids Lua pattern magic in keys.
|
||
local function lit_replace(s, a, b)
|
||
local out, i = {}, 1
|
||
while true do
|
||
local p = s:find(a, i, true)
|
||
if not p then out[#out + 1] = s:sub(i); break end
|
||
out[#out + 1] = s:sub(i, p - 1)
|
||
out[#out + 1] = b
|
||
i = p + #a
|
||
end
|
||
return table.concat(out)
|
||
end
|
||
|
||
-- Strip the outer braces of a "%b{}" capture.
|
||
local function grp(b) return b:sub(2, #b - 1) end
|
||
|
||
-- Map a string to Unicode super/subscript, or nil if any char is unsupported.
|
||
local function map_script(txt, map)
|
||
local res = {}
|
||
for i = 1, #txt do
|
||
local c = txt:sub(i, i)
|
||
if not map[c] then return nil end
|
||
res[#res + 1] = map[c]
|
||
end
|
||
return table.concat(res)
|
||
end
|
||
|
||
local function convert(s)
|
||
-- Text/font wrappers: keep the content, recurse to handle nesting.
|
||
for _, cmd in ipairs({'text', 'mathrm', 'mathit', 'mathbf', 'mathbb',
|
||
'mathsf', 'mathtt', 'mathcal', 'operatorname',
|
||
'textbf', 'textit', 'textrm'}) do
|
||
s = s:gsub('\\' .. cmd .. '(%b{})', function(b) return convert(grp(b)) end)
|
||
end
|
||
-- Fractions -> "num/den" (spaced when either side has spaces).
|
||
local function frac(a, b)
|
||
local n, d = convert(grp(a)), convert(grp(b))
|
||
local sep = (n:find(' ', 1, true) or d:find(' ', 1, true)) and ' / ' or '/'
|
||
return n .. sep .. d
|
||
end
|
||
s = s:gsub('\\frac(%b{})(%b{})', frac)
|
||
s = s:gsub('\\dfrac(%b{})(%b{})', frac)
|
||
s = s:gsub('\\tfrac(%b{})(%b{})', frac)
|
||
s = s:gsub('\\sfrac(%b{})(%b{})', frac)
|
||
-- Roots.
|
||
s = s:gsub('\\sqrt(%b{})', function(b) return '√(' .. convert(grp(b)) .. ')' end)
|
||
-- Single-char scripts first, so the braced fallback (e.g. "_native") below
|
||
-- is not re-scanned and mangled into Unicode subscripts.
|
||
s = s:gsub('%^([%w])', function(c) return sup[c] or ('^' .. c) end)
|
||
s = s:gsub('_([%w])', function(c) return sub[c] or ('_' .. c) end)
|
||
-- Braced scripts: Unicode when the content is all digits/signs, else keep
|
||
-- a readable "^(...)" / "_..." form.
|
||
s = s:gsub('%^(%b{})', function(b)
|
||
local inner = convert(grp(b))
|
||
return map_script(inner, sup) or ('^(' .. inner .. ')')
|
||
end)
|
||
s = s:gsub('_(%b{})', function(b)
|
||
local inner = convert(grp(b))
|
||
return map_script(inner, sub) or ('_' .. inner)
|
||
end)
|
||
-- Remaining symbols.
|
||
for _, pair in ipairs(symbols) do s = lit_replace(s, pair[1], pair[2]) end
|
||
return s
|
||
end
|
||
|
||
-- Convert a display equation, preserving its line structure for a code block.
|
||
local function convert_display(s)
|
||
s = convert(s)
|
||
for _, env in ipairs({'cases', 'aligned', 'align', 'array', 'matrix',
|
||
'gathered', 'split'}) do
|
||
s = lit_replace(s, '\\begin{' .. env .. '}', '')
|
||
s = lit_replace(s, '\\end{' .. env .. '}', '')
|
||
end
|
||
s = lit_replace(s, '\\\\', '\n') -- row break
|
||
s = s:gsub('%s*&%s*', ' ') -- column separator -> spacing
|
||
local lines = {}
|
||
for line in (s .. '\n'):gmatch('(.-)\n') do
|
||
line = line:gsub('^%s+', ''):gsub('%s+$', '')
|
||
if line ~= '' then lines[#lines + 1] = line end
|
||
end
|
||
for i = 2, #lines do lines[i] = ' ' .. lines[i] end -- indent continuations
|
||
return table.concat(lines, '\n')
|
||
end
|
||
|
||
function Math(el)
|
||
if el.mathtype == 'DisplayMath' then
|
||
return el -- handled at block level by Para, to emit a code block
|
||
end
|
||
return pandoc.Str(convert(el.text))
|
||
end
|
||
|
||
-- A paragraph that is solely a display equation becomes a fenced code block.
|
||
function Para(el)
|
||
local maths, only_math = {}, true
|
||
for _, x in ipairs(el.content) do
|
||
if x.t == 'Math' and x.mathtype == 'DisplayMath' then
|
||
maths[#maths + 1] = x
|
||
elseif x.t ~= 'Space' and x.t ~= 'SoftBreak' and x.t ~= 'LineBreak' then
|
||
only_math = false
|
||
end
|
||
end
|
||
if #maths == 0 or not only_math then return nil end
|
||
local parts = {}
|
||
for _, m in ipairs(maths) do parts[#parts + 1] = convert_display(m.text) end
|
||
return pandoc.CodeBlock(table.concat(parts, '\n\n'))
|
||
end
|