Skip to content

Commit c5e4e87

Browse files
authored
Don't crash on cross reference when format: ipynb (#13965)
* test, ipynb - add a test for cross ref in ipynb Also add a verify function for Ipynb cell content * Fix ipynb crash with inline figure labels Wrap float.content with pandoc.Blocks() in the ipynb renderer to handle content that arrives as a list instead of a typed Blocks element. Fixes #13956 * Add to changelog
1 parent 7a505ec commit c5e4e87

5 files changed

Lines changed: 66 additions & 4 deletions

File tree

news/changelog-1.9.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ All changes included in 1.9:
7474

7575
- ([#13722](https://github.com/quarto-dev/quarto-cli/issues/13722)): Fix `light-content` / `dark-content` SCSS rules not included in Reveal.js format. (author: @mcanouil)
7676

77+
### `ipynb`
78+
79+
- ([#13956](https://github.com/quarto-dev/quarto-cli/issues/13956)): Fix crash when rendering to `ipynb` format with figure labels (`#| label: fig-*`) on cells for cross references.
80+
7781
## Projects
7882

7983
- ([#13892](https://github.com/quarto-dev/quarto-cli/issues/13892)): Fix `output-dir: ./` deleting entire project directory. `output-dir` must be a subdirectory of the project directory and check is now better to avoid deleting the project itself when it revolves to the same path.

src/resources/filters/customnodes/floatreftarget.lua

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -933,10 +933,9 @@ end, function(float)
933933
float.identifier)
934934
end
935935

936-
return pandoc.Div({
937-
float.content,
938-
pandoc.Para(quarto.utils.as_inlines(float.caption_long) or {}),
939-
});
936+
local blocks = pandoc.Blocks(float.content)
937+
blocks:insert(pandoc.Para(quarto.utils.as_inlines(float.caption_long) or {}))
938+
return pandoc.Div(blocks)
940939
end)
941940

942941
-- this should really be "_quarto.format.isEmbedIpynb()" or something like that..
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
title: "Issue 13956"
3+
format: ipynb
4+
_quarto:
5+
tests:
6+
ipynb:
7+
ensureIpynbCellMatches:
8+
cellType: markdown
9+
matches:
10+
- 'Figure\s1'
11+
- '#fig-test'
12+
- 'quarto-xref'
13+
- 'This should render without crashing'
14+
---
15+
16+
Testing crash when using inline `#| label: fig-*` syntax with format: ipynb.
17+
18+
```{python}
19+
#| label: fig-test
20+
print("This should render without crashing")
21+
```
22+
23+
See @fig-test for the output.

tests/smoke/smoke-all.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
ensureDocxXpath,
2424
ensureFileRegexMatches,
2525
ensureHtmlElements,
26+
ensureIpynbCellMatches,
2627
ensurePdfRegexMatches,
2728
ensurePdfTextPositions,
2829
ensurePdfMetadata,
@@ -181,6 +182,7 @@ function resolveTestSpecs(
181182
ensureHtmlElementContents,
182183
ensureHtmlElementCount,
183184
ensureFileRegexMatches,
185+
ensureIpynbCellMatches,
184186
ensureLatexFileRegexMatches,
185187
ensureTypstFileRegexMatches,
186188
ensureDocxRegexMatches,

tests/verify.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,40 @@ export const validJsonWithFields = (file: string, fields: Record<string, unknown
302302
}
303303
}
304304

305+
export const ensureIpynbCellMatches = (
306+
file: string,
307+
options: {
308+
cellType: "code" | "markdown";
309+
matches?: (string | RegExp)[];
310+
noMatches?: (string | RegExp)[];
311+
}
312+
): Verify => {
313+
const { cellType, matches = [], noMatches = [] } = options;
314+
return {
315+
name: `IPYNB ${file} has ${cellType} cells matching patterns`,
316+
verify: async (_output: ExecuteOutput[]) => {
317+
const jsonStr = Deno.readTextFileSync(file);
318+
const notebook = JSON.parse(jsonStr);
319+
// deno-lint-ignore no-explicit-any
320+
const cells = notebook.cells.filter((c: any) => c.cell_type === cellType);
321+
// deno-lint-ignore no-explicit-any
322+
const content = cells.map((c: any) =>
323+
Array.isArray(c.source) ? c.source.join("") : c.source
324+
).join("\n");
325+
326+
for (const m of matches) {
327+
const regex = typeof m === "string" ? new RegExp(m) : m;
328+
assert(regex.test(content), `Pattern ${m} not found in ${cellType} cells of ${file}`);
329+
}
330+
for (const m of noMatches) {
331+
const regex = typeof m === "string" ? new RegExp(m) : m;
332+
assert(!regex.test(content), `Pattern ${m} should not be in ${cellType} cells of ${file}`);
333+
}
334+
return Promise.resolve();
335+
}
336+
};
337+
};
338+
305339
export const outputCreated = (
306340
input: string,
307341
to: string,

0 commit comments

Comments
 (0)