From a1af643b9d53651e7ddc3dafbc4cc01d35c272f3 Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Tue, 15 Apr 2025 11:46:22 -0400 Subject: [PATCH 1/3] convert - handle cells with both yaml and implicit titles --- src/core/jupyter/jupyter-fixups.ts | 30 ++++++++++++++++++++++++++++- src/core/pandoc/pandoc-partition.ts | 2 +- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/core/jupyter/jupyter-fixups.ts b/src/core/jupyter/jupyter-fixups.ts index de0bbe32577..8e855d428ef 100644 --- a/src/core/jupyter/jupyter-fixups.ts +++ b/src/core/jupyter/jupyter-fixups.ts @@ -12,7 +12,7 @@ import { Metadata } from "../../config/types.ts"; import { lines } from "../lib/text.ts"; import { markdownWithExtractedHeading } from "../pandoc/pandoc-partition.ts"; import { partitionYamlFrontMatter, readYamlFromMarkdown } from "../yaml.ts"; -import { JupyterNotebook, JupyterOutput } from "./types.ts"; +import { JupyterCell, JupyterNotebook, JupyterOutput } from "./types.ts"; import { jupyterCellSrcAsLines, jupyterCellSrcAsStr, @@ -149,6 +149,34 @@ export function fixupFrontMatter(nb: JupyterNotebook): JupyterNotebook { return lns.map((line) => line.endsWith("\n") ? line : `${line}\n`); }; + // https://github.com/quarto-dev/quarto-cli/issues/12440 + // first, we need to find cells that have front matter _and_ markdown content, + // and split them into two cells. + const newCells: JupyterCell[] = []; + nb.cells.forEach((cell) => { + if (cell.cell_type === "raw" || cell.cell_type === "markdown") { + const partitioned = partitionYamlFrontMatter(jupyterCellSrcAsStr(cell)) || + undefined; + if (partitioned?.yaml.trim()) { + newCells.push({ + cell_type: "raw", + source: nbLines(partitioned.yaml.split("\n")), + metadata: {}, + }); + } + if (partitioned?.markdown.trim()) { + newCells.push({ + cell_type: "markdown", + source: nbLines(partitioned.markdown.split("\n")), + metadata: {}, + }); + } + } else { + newCells.push(cell); + } + }); + nb.cells = newCells; + // look for the first raw block that has a yaml object let partitioned: { yaml: string; markdown: string } | undefined; const frontMatterCellIndex = nb.cells.findIndex((cell) => { diff --git a/src/core/pandoc/pandoc-partition.ts b/src/core/pandoc/pandoc-partition.ts index 57459eb1535..5dbf93e9982 100644 --- a/src/core/pandoc/pandoc-partition.ts +++ b/src/core/pandoc/pandoc-partition.ts @@ -84,7 +84,7 @@ export function markdownWithExtractedHeading(markdown: string) { const parsedHeading = parsePandocTitle(line); headingText = parsedHeading.heading; headingAttr = parsedHeading.attr; - contentBeforeHeading = mdLines.length !== 0; + contentBeforeHeading = (mdLines.join("").trim()).length !== 0; } else if (line.match(/^=+\s*$/) || line.match(/^-+\s*$/)) { const prevLine = mdLines[mdLines.length - 1]; if (prevLine) { From acfddca164f1f88884190a80a0e0c27f37d5ea3f Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Tue, 15 Apr 2025 11:46:55 -0400 Subject: [PATCH 2/3] regression test --- tests/docs/convert/issue-12440.qmd | 18 ++++++++ .../smoke/convert/convert-issue-12042.test.ts | 3 +- .../smoke/convert/convert-issue-12440.test.ts | 41 +++++++++++++++++++ tests/test.ts | 10 +++++ 4 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 tests/docs/convert/issue-12440.qmd create mode 100644 tests/smoke/convert/convert-issue-12440.test.ts diff --git a/tests/docs/convert/issue-12440.qmd b/tests/docs/convert/issue-12440.qmd new file mode 100644 index 00000000000..eb50c2e5679 --- /dev/null +++ b/tests/docs/convert/issue-12440.qmd @@ -0,0 +1,18 @@ +--- +format: typst +--- + +# This is what happens when I don't set the title. + +## This is a level 2 heading + +This is some paragraph text. + +```{python} +# This is an executable Python code block. +print("This is the output of an executable Python code block.") +``` + +## This is another level 2 heading + +This is some more paragraph text. \ No newline at end of file diff --git a/tests/smoke/convert/convert-issue-12042.test.ts b/tests/smoke/convert/convert-issue-12042.test.ts index fb1af4ed976..00f374daf97 100644 --- a/tests/smoke/convert/convert-issue-12042.test.ts +++ b/tests/smoke/convert/convert-issue-12042.test.ts @@ -1,5 +1,5 @@ /* -* convert-backticks.test.ts +* convert-issue-12042.test.ts * * Copyright (C) 2020-2024 Posit Software, PBC * @@ -12,7 +12,6 @@ import { import { assert } from "testing/asserts"; (() => { - const input = "docs/convert/backticks.ipynb"; testQuartoCmd( "convert", ["docs/convert/issue-12042.ipynb"], diff --git a/tests/smoke/convert/convert-issue-12440.test.ts b/tests/smoke/convert/convert-issue-12440.test.ts new file mode 100644 index 00000000000..87aaf676650 --- /dev/null +++ b/tests/smoke/convert/convert-issue-12440.test.ts @@ -0,0 +1,41 @@ +/* +* docs/convert/issue-12440.test.ts +* +* Copyright (C) 2025 Posit Software, PBC +* +*/ +import { existsSync } from "../../../src/deno_ral/fs.ts"; +import { quarto } from "../../../src/quarto.ts"; +import { + ExecuteOutput, + testQuartoCmd, + removeFilesTeardown, +} from "../../test.ts"; +import { assert } from "testing/asserts"; + +(() => { + const input = "docs/convert/issue-12440.qmd"; + testQuartoCmd( + "convert", + ["docs/convert/issue-12440.qmd"], + [ + { + name: "convert-mixed-yaml-markdown-cell", + verify: async (outputs: ExecuteOutput[]) => { + await quarto([ + "convert", + "docs/convert/issue-12440.ipynb", + "--output", + "docs/convert/issue-12440-out.qmd", + ]); + const txt = Deno.readTextFileSync("docs/convert/issue-12440-out.qmd"); + assert(txt.includes("title: This is what happens when I don't set the title"), "Markdown text not found in output"); + } + } + ], + removeFilesTeardown([ + "docs/convert/issue-12440-out.qmd", + "docs/convert/issue-12440.ipynb" + ]), + ); +})(); diff --git a/tests/test.ts b/tests/test.ts index 72f6542d7b4..3f6dcb45a23 100644 --- a/tests/test.ts +++ b/tests/test.ts @@ -336,3 +336,13 @@ function readExecuteOutput(log: string) { return JSON.parse(line) as ExecuteOutput; }); } + +export const removeFilesTeardown = (fileList: string[]) => { + return { + teardown: async () => { + for (const file of fileList) { + safeRemoveSync(file); + } + } + }; +} \ No newline at end of file From f4fdf5d251ffd514e5ec723d2d499e3b2753a3d9 Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Tue, 15 Apr 2025 11:47:39 -0400 Subject: [PATCH 3/3] changelog --- news/changelog-1.7.md | 1 + 1 file changed, 1 insertion(+) diff --git a/news/changelog-1.7.md b/news/changelog-1.7.md index 2f5a281865c..12cf58209ba 100644 --- a/news/changelog-1.7.md +++ b/news/changelog-1.7.md @@ -57,6 +57,7 @@ All changes included in 1.7: - ([#12042](https://github.com/quarto-dev/quarto-cli/issues/12042)): Preserve Markdown content that follows YAML metadata in a `raw` .ipynb cell. - ([#12318](https://github.com/quarto-dev/quarto-cli/issues/12318)): Ensure enough line breaks between cells that might be trimmed. +- ([#12440](https://github.com/quarto-dev/quarto-cli/issues/12440)): Support conversion of cells with both YAML and implicit titles. ### `quarto inspect`