Skip to content

Commit c0f1448

Browse files
committed
fix: improve semantic structure by linking code and annotation
1 parent 78a54f8 commit c0f1448

3 files changed

Lines changed: 47 additions & 25 deletions

File tree

src/format/typst/format-typst.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ function skylightingPostProcessor(brandBgColor?: string) {
207207

208208
// Annotation markers emitted by the Lua filter as Typst comments
209209
const annotationMarkerRe =
210-
/\/\/ quarto-code-annotations: (\([^)]*\))\n(\s*(?:#block\[\s*)*(?:#quarto-code-filename\([^\n]*\)\[\s*)?)#Skylighting\(/g;
210+
/\/\/ quarto-code-annotations: ([\w-]*) (\([^)]*\))\n(\s*(?:#block\[\s*)*(?:#quarto-code-filename\([^\n]*\)\[\s*)?)#Skylighting\(/g;
211211

212212
return async (output: string) => {
213213
let content = Deno.readTextFileSync(output);
@@ -231,10 +231,10 @@ function skylightingPostProcessor(brandBgColor?: string) {
231231
);
232232
}
233233

234-
// Add annotations parameter to function signature
234+
// Add cell-id and annotations parameters to function signature
235235
fn = fn.replace(
236236
"start: 1, sourcelines)",
237-
"start: 1, annotations: (:), sourcelines)",
237+
"start: 1, cell-id: \"\", annotations: (:), sourcelines)",
238238
);
239239

240240
// Move lnum increment outside if-number block (always track position)
@@ -248,7 +248,12 @@ function skylightingPostProcessor(brandBgColor?: string) {
248248
"blocks = blocks + ln + EndLine()",
249249
`let annote-num = annotations.at(str(lnum), default: none)
250250
if annote-num != none {
251-
blocks = blocks + box(width: 100%)[#ln #h(1fr) #quarto-circled-number(annote-num, color: quarto-annote-color(bgcolor))] + EndLine()
251+
if cell-id != "" {
252+
let lbl = cell-id + "-annote-" + str(annote-num)
253+
blocks = blocks + box(width: 100%)[#ln #h(1fr) #link(label(lbl))[#quarto-circled-number(annote-num, color: quarto-annote-color(bgcolor))] #label(lbl + "-back")] + EndLine()
254+
} else {
255+
blocks = blocks + box(width: 100%)[#ln #h(1fr) #quarto-circled-number(annote-num, color: quarto-annote-color(bgcolor))] + EndLine()
256+
}
252257
} else {
253258
blocks = blocks + ln + EndLine()
254259
}`,
@@ -264,7 +269,7 @@ function skylightingPostProcessor(brandBgColor?: string) {
264269
// optional #block[ wrappers and #quarto-code-filename(...)[ wrappers.
265270
const merged = content.replace(
266271
annotationMarkerRe,
267-
"$2#Skylighting(annotations: $1, ",
272+
"$3#Skylighting(cell-id: \"$1\", annotations: $2, ",
268273
);
269274
if (merged !== content) {
270275
content = merged;

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

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -108,14 +108,14 @@ end
108108

109109
-- Skylighting mode: emit a Typst comment that the TS post-processor
110110
-- will merge into the Skylighting call site.
111-
local function typstAnnotationMarker(annotations)
111+
local function typstAnnotationMarker(annotations, cellId)
112112
local dict = typstAnnotationsDict(annotations)
113-
return pandoc.RawBlock("typst", "// quarto-code-annotations: " .. dict)
113+
return pandoc.RawBlock("typst", "// quarto-code-annotations: " .. (cellId or "") .. " " .. dict)
114114
end
115115

116116
-- Native/none mode: wrap a CodeBlock in #quarto-code-annotation(annotations)[...].
117117
-- raw.line numbers always start at 1 regardless of startFrom, so adjust keys.
118-
local function wrapTypstAnnotatedCode(codeBlock, annotations)
118+
local function wrapTypstAnnotatedCode(codeBlock, annotations, cellId)
119119
local startFrom = tonumber(codeBlock.attr.attributes['startFrom']) or 1
120120
local adjustedAnnotations = {}
121121
for annoteId, lineNumbers in pairs(annotations) do
@@ -126,14 +126,18 @@ local function wrapTypstAnnotatedCode(codeBlock, annotations)
126126
adjustedAnnotations[annoteId] = adjusted
127127
end
128128
local dict = typstAnnotationsDict(adjustedAnnotations)
129+
local cellIdParam = ""
130+
if cellId and cellId ~= "" then
131+
cellIdParam = ", cell-id: \"" .. cellId .. "\""
132+
end
129133
local lang = codeBlock.attr.classes[1] or ""
130134
local code = codeBlock.text
131135
local maxBackticks = 2
132136
for seq in code:gmatch("`+") do
133137
maxBackticks = math.max(maxBackticks, #seq)
134138
end
135139
local fence = string.rep("`", maxBackticks + 1)
136-
local raw = "#quarto-code-annotation(" .. dict .. ")[" .. fence .. lang .. "\n" .. code .. "\n" .. fence .. "]"
140+
local raw = "#quarto-code-annotation(" .. dict .. cellIdParam .. ")[" .. fence .. lang .. "\n" .. code .. "\n" .. fence .. "]"
137141
return pandoc.RawBlock("typst", raw)
138142
end
139143

@@ -497,9 +501,9 @@ function code_annotations()
497501
and pendingAnnotations and next(pendingAnnotations) ~= nil then
498502
if param(constants.kSyntaxHighlighting, true) then
499503
block.content[1].content[1] = codeCell
500-
block.content[1].content:insert(1, typstAnnotationMarker(pendingAnnotations))
504+
block.content[1].content:insert(1, typstAnnotationMarker(pendingAnnotations, pendingCellId))
501505
else
502-
block.content[1].content[1] = wrapTypstAnnotatedCode(codeCell, pendingAnnotations)
506+
block.content[1].content[1] = wrapTypstAnnotatedCode(codeCell, pendingAnnotations, pendingCellId)
503507
end
504508
else
505509
block.content[1].content[1] = codeCell
@@ -535,10 +539,10 @@ function code_annotations()
535539
and codeAnnotations ~= constants.kCodeAnnotationStyleNone
536540
and pendingAnnotations and next(pendingAnnotations) ~= nil then
537541
if param(constants.kSyntaxHighlighting, true) then
538-
outputBlock(typstAnnotationMarker(pendingAnnotations))
542+
outputBlock(typstAnnotationMarker(pendingAnnotations, pendingCellId))
539543
outputBlock(codeCell)
540544
else
541-
outputBlock(wrapTypstAnnotatedCode(codeCell, pendingAnnotations))
545+
outputBlock(wrapTypstAnnotatedCode(codeCell, pendingAnnotations, pendingCellId))
542546
end
543547
else
544548
outputBlock(codeCell)
@@ -560,7 +564,7 @@ function code_annotations()
560564
if pendingAnnotations[annoteId] then
561565
local content = pandoc.write(pandoc.Pandoc({v[1]}), "typst")
562566
annotationBlocks:insert(pandoc.RawBlock("typst",
563-
"#quarto-annotation-item(" .. tostring(annotationNumber) .. ", [" .. content .. "])"))
567+
"#quarto-annotation-item(\"" .. (pendingCellId or "") .. "\", " .. tostring(annotationNumber) .. ", [" .. content .. "])"))
564568
end
565569
end
566570

@@ -574,7 +578,7 @@ function code_annotations()
574578
if useSkylighting then
575579
return nil
576580
else
577-
return wrapTypstAnnotatedCode(el, pendingAnnotations)
581+
return wrapTypstAnnotatedCode(el, pendingAnnotations, pendingCellId)
578582
end
579583
end
580584
end
@@ -584,12 +588,12 @@ function code_annotations()
584588
if is_custom_node(resolvedCell) then
585589
local custom = _quarto.ast.resolve_custom_data(resolvedCell) or pandoc.Div({})
586590
if useSkylighting then
587-
custom.content:insert(1, typstAnnotationMarker(pendingAnnotations))
591+
custom.content:insert(1, typstAnnotationMarker(pendingAnnotations, pendingCellId))
588592
end
589593
custom.content:insert(dlDiv)
590594
else
591595
if useSkylighting then
592-
resolvedCell.content:insert(1, typstAnnotationMarker(pendingAnnotations))
596+
resolvedCell.content:insert(1, typstAnnotationMarker(pendingAnnotations, pendingCellId))
593597
end
594598
resolvedCell.content:insert(dlDiv)
595599
end

src/resources/formats/typst/pandoc/quarto/definitions.typ

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,25 +59,38 @@
5959
]
6060
}
6161

62-
#let quarto-code-annotation(annotations, color: luma(60), body) = {
62+
#let quarto-code-annotation(annotations, cell-id: "", color: luma(60), body) = {
6363
show raw.where(block: true): it => it
6464
show raw.line: it => {
6565
let annote-num = annotations.at(str(it.number), default: none)
6666
if annote-num != none {
67-
box(width: 100%)[#it #h(1fr) #quarto-circled-number(annote-num, color: color)]
67+
if cell-id != "" {
68+
let lbl = cell-id + "-annote-" + str(annote-num)
69+
box(width: 100%)[#it #h(1fr) #link(label(lbl))[#quarto-circled-number(annote-num, color: color)] #label(lbl + "-back")]
70+
} else {
71+
box(width: 100%)[#it #h(1fr) #quarto-circled-number(annote-num, color: color)]
72+
}
6873
} else {
6974
it
7075
}
7176
}
7277
body
7378
}
7479

75-
#let quarto-annotation-item(n, content) = {
76-
block(above: 0.4em, below: 0.4em)[
77-
#quarto-circled-number(n)
78-
#h(0.4em)
79-
#content
80-
]
80+
#let quarto-annotation-item(cell-id, n, content) = {
81+
if cell-id != "" {
82+
[#block(above: 0.4em, below: 0.4em)[
83+
#link(label(cell-id + "-annote-" + str(n) + "-back"))[#quarto-circled-number(n)]
84+
#h(0.4em)
85+
#content
86+
] #label(cell-id + "-annote-" + str(n))]
87+
} else {
88+
block(above: 0.4em, below: 0.4em)[
89+
#quarto-circled-number(n)
90+
#h(0.4em)
91+
#content
92+
]
93+
}
8194
}
8295

8396
// Style native raw code blocks with default inset, radius, and stroke

0 commit comments

Comments
 (0)