Skip to content

Commit ae392ad

Browse files
committed
refactor: extract Typst annotation helpers into require() module
Move typstAnnotationsDict, typstAnnotationMarker, and wrapTypstAnnotatedCode from code-annotation.lua into a separate modules/typst-code-annotations.lua loaded via require(). Modules loaded via require() have their own Lua scope and are not inlined into the bundled main.lua, avoiding the 200 local variable limit that the import()-inlined helpers were exceeding.
1 parent 422e053 commit ae392ad

2 files changed

Lines changed: 77 additions & 62 deletions

File tree

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
-- typst-code-annotations.lua
2+
-- Copyright (C) 2026 Posit Software, PBC
3+
4+
-- Typst annotation helpers for code blocks.
5+
-- Loaded via require() so locals stay out of the bundled main.lua scope.
6+
7+
local function _main()
8+
9+
-- Convert annotations table to a flat Typst dictionary string.
10+
-- Keys are line positions (as strings), values are annotation numbers.
11+
local function typstAnnotationsDict(annotations)
12+
local entries = {}
13+
for annoteId, lineNumbers in pairs(annotations) do
14+
local num = annoteId:match("annote%-(%d+)")
15+
if num then
16+
for _, lineNo in ipairs(lineNumbers) do
17+
table.insert(entries, {pos = lineNo, annoteNum = tonumber(num)})
18+
end
19+
end
20+
end
21+
table.sort(entries, function(a, b) return a.pos < b.pos end)
22+
local parts = {}
23+
for _, e in ipairs(entries) do
24+
table.insert(parts, '"' .. tostring(e.pos) .. '": ' .. tostring(e.annoteNum))
25+
end
26+
return '(' .. table.concat(parts, ', ') .. ')'
27+
end
28+
29+
-- Skylighting mode: emit a Typst comment that the TS post-processor
30+
-- will merge into the Skylighting call site.
31+
local function typstAnnotationMarker(annotations, cellId)
32+
local dict = typstAnnotationsDict(annotations)
33+
return pandoc.RawBlock("typst", "// quarto-code-annotations: " .. (cellId or "") .. " " .. dict)
34+
end
35+
36+
-- Native/none mode: wrap a CodeBlock in #quarto-code-annotation(annotations)[...].
37+
-- raw.line numbers always start at 1 regardless of startFrom, so adjust keys.
38+
local function wrapTypstAnnotatedCode(codeBlock, annotations, cellId)
39+
local startFrom = tonumber(codeBlock.attr.attributes['startFrom']) or 1
40+
local adjustedAnnotations = {}
41+
for annoteId, lineNumbers in pairs(annotations) do
42+
local adjusted = pandoc.List({})
43+
for _, lineNo in ipairs(lineNumbers) do
44+
adjusted:insert(lineNo - startFrom + 1)
45+
end
46+
adjustedAnnotations[annoteId] = adjusted
47+
end
48+
local dict = typstAnnotationsDict(adjustedAnnotations)
49+
local lang = codeBlock.attr.classes[1] or ""
50+
local code = codeBlock.text
51+
local maxBackticks = 2
52+
for seq in code:gmatch("`+") do
53+
maxBackticks = math.max(maxBackticks, #seq)
54+
end
55+
local fence = string.rep("`", maxBackticks + 1)
56+
local raw = "#quarto-code-annotation(" .. dict
57+
.. (cellId and cellId ~= "" and (", cell-id: \"" .. cellId .. "\"") or "")
58+
.. ")[" .. fence .. lang .. "\n" .. code .. "\n" .. fence .. "]"
59+
return pandoc.RawBlock("typst", raw)
60+
end
61+
62+
return {
63+
typstAnnotationsDict = typstAnnotationsDict,
64+
typstAnnotationMarker = typstAnnotationMarker,
65+
wrapTypstAnnotatedCode = wrapTypstAnnotatedCode,
66+
}
67+
end
68+
69+
return _main()

src/resources/filters/quarto-pre/code-annotation.lua

Lines changed: 8 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
-- Copyright (C) 2020-2022 Posit Software, PBC
33

44
local constants = require("modules/constants")
5+
local typstAnnotations = require("modules/typst-code-annotations")
56

67

78
local hasAnnotations = false
@@ -84,61 +85,6 @@ local function latexListPlaceholder(number)
8485
return '5CB6E08D-list-annote-' .. number
8586
end
8687

87-
-- Typst annotation helpers
88-
89-
-- Convert annotations table to a flat Typst dictionary string.
90-
-- Keys are line positions (as strings), values are annotation numbers.
91-
local function typstAnnotationsDict(annotations)
92-
local entries = {}
93-
for annoteId, lineNumbers in pairs(annotations) do
94-
local num = annoteId:match("annote%-(%d+)")
95-
if num then
96-
for _, lineNo in ipairs(lineNumbers) do
97-
table.insert(entries, {pos = lineNo, annoteNum = tonumber(num)})
98-
end
99-
end
100-
end
101-
table.sort(entries, function(a, b) return a.pos < b.pos end)
102-
local parts = {}
103-
for _, e in ipairs(entries) do
104-
table.insert(parts, '"' .. tostring(e.pos) .. '": ' .. tostring(e.annoteNum))
105-
end
106-
return '(' .. table.concat(parts, ', ') .. ')'
107-
end
108-
109-
-- Skylighting mode: emit a Typst comment that the TS post-processor
110-
-- will merge into the Skylighting call site.
111-
local function typstAnnotationMarker(annotations, cellId)
112-
local dict = typstAnnotationsDict(annotations)
113-
return pandoc.RawBlock("typst", "// quarto-code-annotations: " .. (cellId or "") .. " " .. dict)
114-
end
115-
116-
-- Native/none mode: wrap a CodeBlock in #quarto-code-annotation(annotations)[...].
117-
-- raw.line numbers always start at 1 regardless of startFrom, so adjust keys.
118-
local function wrapTypstAnnotatedCode(codeBlock, annotations, cellId)
119-
local startFrom = tonumber(codeBlock.attr.attributes['startFrom']) or 1
120-
local adjustedAnnotations = {}
121-
for annoteId, lineNumbers in pairs(annotations) do
122-
local adjusted = pandoc.List({})
123-
for _, lineNo in ipairs(lineNumbers) do
124-
adjusted:insert(lineNo - startFrom + 1)
125-
end
126-
adjustedAnnotations[annoteId] = adjusted
127-
end
128-
local dict = typstAnnotationsDict(adjustedAnnotations)
129-
local lang = codeBlock.attr.classes[1] or ""
130-
local code = codeBlock.text
131-
local maxBackticks = 2
132-
for seq in code:gmatch("`+") do
133-
maxBackticks = math.max(maxBackticks, #seq)
134-
end
135-
local fence = string.rep("`", maxBackticks + 1)
136-
local raw = "#quarto-code-annotation(" .. dict
137-
.. (cellId and cellId ~= "" and (", cell-id: \"" .. cellId .. "\"") or "")
138-
.. ")[" .. fence .. lang .. "\n" .. code .. "\n" .. fence .. "]"
139-
return pandoc.RawBlock("typst", raw)
140-
end
141-
14288
local function toLines(s)
14389
if s:sub(-1)~="\n" then s=s.."\n" end
14490
return s:gmatch("(.-)\n")
@@ -499,9 +445,9 @@ function code_annotations()
499445
and pendingAnnotations and next(pendingAnnotations) ~= nil then
500446
if param(constants.kSyntaxHighlighting, true) then
501447
block.content[1].content[1] = codeCell
502-
block.content[1].content:insert(1, typstAnnotationMarker(pendingAnnotations, pendingCellId))
448+
block.content[1].content:insert(1, typstAnnotations.typstAnnotationMarker(pendingAnnotations, pendingCellId))
503449
else
504-
block.content[1].content[1] = wrapTypstAnnotatedCode(codeCell, pendingAnnotations, pendingCellId)
450+
block.content[1].content[1] = typstAnnotations.wrapTypstAnnotatedCode(codeCell, pendingAnnotations, pendingCellId)
505451
end
506452
else
507453
block.content[1].content[1] = codeCell
@@ -537,10 +483,10 @@ function code_annotations()
537483
and codeAnnotations ~= constants.kCodeAnnotationStyleNone
538484
and pendingAnnotations and next(pendingAnnotations) ~= nil then
539485
if param(constants.kSyntaxHighlighting, true) then
540-
outputBlock(typstAnnotationMarker(pendingAnnotations, pendingCellId))
486+
outputBlock(typstAnnotations.typstAnnotationMarker(pendingAnnotations, pendingCellId))
541487
outputBlock(codeCell)
542488
else
543-
outputBlock(wrapTypstAnnotatedCode(codeCell, pendingAnnotations, pendingCellId))
489+
outputBlock(typstAnnotations.wrapTypstAnnotatedCode(codeCell, pendingAnnotations, pendingCellId))
544490
end
545491
else
546492
outputBlock(codeCell)
@@ -574,7 +520,7 @@ function code_annotations()
574520
if param(constants.kSyntaxHighlighting, true) then
575521
return nil
576522
else
577-
return wrapTypstAnnotatedCode(el, pendingAnnotations, pendingCellId)
523+
return typstAnnotations.wrapTypstAnnotatedCode(el, pendingAnnotations, pendingCellId)
578524
end
579525
end
580526
end
@@ -584,12 +530,12 @@ function code_annotations()
584530
if is_custom_node(resolvedCell) then
585531
local custom = _quarto.ast.resolve_custom_data(resolvedCell) or pandoc.Div({})
586532
if param(constants.kSyntaxHighlighting, true) then
587-
custom.content:insert(1, typstAnnotationMarker(pendingAnnotations, pendingCellId))
533+
custom.content:insert(1, typstAnnotations.typstAnnotationMarker(pendingAnnotations, pendingCellId))
588534
end
589535
custom.content:insert(dlDiv)
590536
else
591537
if param(constants.kSyntaxHighlighting, true) then
592-
resolvedCell.content:insert(1, typstAnnotationMarker(pendingAnnotations, pendingCellId))
538+
resolvedCell.content:insert(1, typstAnnotations.typstAnnotationMarker(pendingAnnotations, pendingCellId))
593539
end
594540
resolvedCell.content:insert(dlDiv)
595541
end

0 commit comments

Comments
 (0)