Skip to content
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

### Bug Fixes

- fix: Keep inline code in a Typst document as a code element, so Pandoc writes the syntax highlighting definitions for it. A document with inline code and no code block failed to compile before. (#65)
- fix: Remove the code-window attributes from a block that sets code-window-enabled="false", and from a block that sets code-window-lines when lines-label is off. They reached the HTML output as data-code-window-* attributes before. (#63)
- fix: Remove the extension's own label attribute when the output format gets no window chrome, so a writer that keeps attributes no longer prints it. (#63)
- fix: Check every code block attribute in the code-window group against the schema, so a value nothing validated before, such as code-window-enabled, is now reported when it is wrong. The extension's own duplicate warnings for an invalid code-window-style or code-window-collapse are removed, since the schema already names the same mistake. (#60)
Expand Down
57 changes: 25 additions & 32 deletions _extensions/code-window/_modules/hotfix/skylighting-typst-fix.lua
Original file line number Diff line number Diff line change
Expand Up @@ -155,44 +155,35 @@ local function build_raw_line_annotation_rule()
]==], circled, circled)
end

--- Shape of the box that holds inline code, after its fill.
local _inline_box_style = 'inset: (x: 3pt, y: 0pt), outset: (y: 3pt), '
.. 'radius: 2pt, stroke: none)['

--- Process inline Code for Typst format.
--- Renders the Code element through Pandoc's Typst writer to get syntax-
--- highlighted output, then wraps it in a box with the theme background colour.
--- Puts the Code element in a box that carries the theme background colour.
--- The element itself stays in the document, so Pandoc highlights it and
--- writes the token definitions for it.
--- @param el pandoc.Code Inline code element
--- @return pandoc.RawInline|pandoc.Code Transformed or original element
--- @return pandoc.Inlines The boxed code
local function process_typst_inline(el)
local hm = PANDOC_WRITER_OPTIONS and PANDOC_WRITER_OPTIONS.highlight_method
local bg_fill = nil
local write_opts = nil

if hm then
local bg = hm['background-color']
if bg and type(bg) == 'string' then
bg_fill = string.format('rgb("%s")', bg)
end
write_opts = pandoc.WriterOptions({
highlight_method = hm,
})
end

local rendered = pandoc.write(pandoc.Pandoc({ pandoc.Plain({ el }) }), 'typst', write_opts)
rendered = rendered:gsub('%s+$', '')
if rendered == '' then return el end
local bg = hm and hm['background-color']
local opening, closing

local typst_code
if bg_fill then
typst_code = string.format(
'#box(fill: %s, inset: (x: 3pt, y: 0pt), outset: (y: 3pt), radius: 2pt, stroke: none)[%s]',
bg_fill, rendered)
if type(bg) == 'string' then
opening = string.format('#box(fill: rgb("%s"), ', bg) .. _inline_box_style
closing = ']'
else
typst_code = string.format(
'#context { let _bg = _cw-page-bg(); let _f = _cw-fg(_bg); '
.. 'box(fill: color.mix((_f, 10%%), (_bg, 90%%)), '
.. 'inset: (x: 3pt, y: 0pt), outset: (y: 3pt), radius: 2pt, stroke: none)[%s] }',
rendered)
opening = '#context { let _bg = _cw-page-bg(); let _f = _cw-fg(_bg); '
.. 'box(fill: color.mix((_f, 10%), (_bg, 90%)), ' .. _inline_box_style
closing = '] }'
end

return pandoc.RawInline('typst', typst_code)
return pandoc.Inlines({
pandoc.RawInline('typst', opening),
el,
pandoc.RawInline('typst', closing),
})
end

--- Inject Skylighting override at the start of the document.
Expand Down Expand Up @@ -245,8 +236,10 @@ local function is_title_scaffold(div)
return true
end

--- Walk the document tree and convert inline Code to RawInline with
--- background styling. Code in title scaffolds is converted to plain
--- Walk the document tree and box inline Code with background styling.
--- The extension contributes the pass once, so it sees each Code element
--- once and boxes it once.
--- Code in title scaffolds is converted to plain
--- Typst backtick code to avoid Skylighting tokens with inner quotes
--- that would break the string parameter Quarto generates.
--- The typst-title-fix post-quarto filter then evaluates the string
Expand Down
17 changes: 17 additions & 0 deletions tests/fixtures/inline-code-no-theme.qmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
title: "Inline code, no theme"
filters:
- code-window
highlight-style: none
format:
typst:
keep-typ: true
---

The document below turns syntax highlighting off, so the theme gives no
background colour.

The filter then boxes the inline code with a colour it mixes from the page,
and that box has to compile too.

A paragraph with `inline code` in it.
15 changes: 15 additions & 0 deletions tests/fixtures/inline-code-only.qmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
title: "Inline code, no block"
filters:
- code-window
format:
typst:
keep-typ: true
---

The document below has inline code and no code block.

The filter boxes the inline code and leaves the element in place, so Pandoc
writes the token definitions for it.

A paragraph with `inline code` in it.
19 changes: 19 additions & 0 deletions tests/fixtures/inline-code-with-block.qmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
title: "Inline code and a block"
filters:
- code-window
format:
typst:
keep-typ: true
---

The document below has inline code and a code block.

The boxed inline code and the block that goes through the Skylighting
override share one document, and that document has to compile.

A paragraph with `inline code` in it.

```python
x = 1
```
44 changes: 44 additions & 0 deletions tests/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,50 @@ for fixture in cell-output-bare cell-output-classed; do
fi
done

# ============================================================================
# Inline code keeps the token definitions in a document with no code block
# ============================================================================

# The filter boxes inline code and leaves the element itself in place, so
# Pandoc highlights it and writes the token definitions. The render fails to
# compile when they are missing, and the grep says why.
render inline-code-only typst
if grep -q '^#let NormalTok(' "${work_dir}/inline-code-only.typ"; then
report pass "inline-code-only: the token definitions reach the document"
else
report fail "inline-code-only: the token definitions reach the document" \
"a #let NormalTok( definition in inline-code-only.typ"
fi

# A theme with a background colour takes the box that names the colour.
if grep -q 'box(fill: rgb(' "${work_dir}/inline-code-only.typ"; then
report pass "inline-code-only: the box takes the colour of the theme"
else
report fail "inline-code-only: the box takes the colour of the theme" \
"a box(fill: rgb( call in inline-code-only.typ"
fi

# A code block goes through the Skylighting override, and the inline code
# keeps its own box in the same document.
render inline-code-with-block typst
if grep -q '^#Skylighting(' "${work_dir}/inline-code-with-block.typ" &&
grep -q 'box(fill: rgb(' "${work_dir}/inline-code-with-block.typ"; then
report pass "inline-code-with-block: the block and the inline code share the document"
else
report fail "inline-code-with-block: the block and the inline code share the document" \
"a #Skylighting( call and a box(fill: rgb( call in inline-code-with-block.typ"
fi

# A theme that gives no background colour takes the other box, which mixes its
# colour from the page. The render compiles that box.
render inline-code-no-theme typst
if grep -q 'box(fill: color.mix' "${work_dir}/inline-code-no-theme.typ"; then
report pass "inline-code-no-theme: the box takes its colour from the page"
else
report fail "inline-code-no-theme: the box takes its colour from the page" \
"a box(fill: color.mix call in inline-code-no-theme.typ"
fi

# ============================================================================
# A per-block style override reaches the output
# ============================================================================
Expand Down
Loading