Skip to content

Commit 0857782

Browse files
Allow ::: without preceding empty line to be parsed as closing div (#875)
* allow ::: without preceding empty line to be parsed as closing div * Add some tests for code cell detection * Update CHANGELOG --------- Co-authored-by: Julia Silge <julia.silge@gmail.com>
1 parent 35cc2e7 commit 0857782

4 files changed

Lines changed: 227 additions & 1 deletion

File tree

apps/vscode/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## 1.128.0 (Unreleased)
44

5+
- Fixed a bug where code blocks inside complex div structures (e.g., many `::: {.notes}` divs without preceding blank lines) were not detected as executable cells (<https://github.com/quarto-dev/quarto/pull/875>).
6+
57
## 1.127.0 (Release on 2025-12-17)
68

79
- By default, headers from markdown files _in R projects_ (projects with a `DESCRIPTION` file such as R package) are no longer exported as workspace symbols. They remain exported as usual in other projects. This behaviour can be controlled manually with the new `quarto.symbols.exportToWorkspace` setting.
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import * as vscode from "vscode";
2+
import * as assert from "assert";
3+
import { WORKSPACE_PATH, examplesOutUri, openAndShowTextDocument } from "./test-utils";
4+
import { isExecutableLanguageBlock, languageNameFromBlock } from "quarto-core";
5+
import { MarkdownEngine } from "../markdown/engine";
6+
7+
suite("Code block detection", function () {
8+
const engine = new MarkdownEngine();
9+
10+
suiteSetup(async function () {
11+
await vscode.workspace.fs.delete(examplesOutUri(), { recursive: true });
12+
await vscode.workspace.fs.copy(vscode.Uri.file(WORKSPACE_PATH), examplesOutUri());
13+
});
14+
15+
// Test for issue #521 / PR #875:
16+
// Code blocks inside divs should be detected even when the closing :::
17+
// doesn't have a preceding blank line. This real-world example has many
18+
// .notes divs which caused later code blocks to not be detected.
19+
test("Detects code blocks in document with many divs (issue #521)", async function () {
20+
const { doc } = await openAndShowTextDocument("div-code-blocks.qmd");
21+
22+
const tokens = engine.parse(doc);
23+
const executableBlocks = tokens.filter(isExecutableLanguageBlock);
24+
25+
// Count R code blocks and math blocks separately
26+
const rBlocks = executableBlocks.filter(b => languageNameFromBlock(b) === "r");
27+
const mathBlocks = executableBlocks.filter(b => languageNameFromBlock(b) === "tex");
28+
29+
// Should find all 3 R code blocks
30+
assert.strictEqual(
31+
rBlocks.length,
32+
3,
33+
`Expected 3 R code blocks, found ${rBlocks.length}`
34+
);
35+
36+
// Should find all 3 math blocks (displayed as tex)
37+
assert.strictEqual(
38+
mathBlocks.length,
39+
3,
40+
`Expected 3 math blocks, found ${mathBlocks.length}`
41+
);
42+
43+
// Total should be 6 executable blocks (3 R + 3 math)
44+
assert.strictEqual(
45+
executableBlocks.length,
46+
6,
47+
`Expected 6 executable blocks total, found ${executableBlocks.length}`
48+
);
49+
});
50+
51+
test("Detects code block in simple document", async function () {
52+
const { doc } = await openAndShowTextDocument("hello.qmd");
53+
54+
const tokens = engine.parse(doc);
55+
const executableBlocks = tokens.filter(isExecutableLanguageBlock);
56+
57+
assert.strictEqual(
58+
executableBlocks.length,
59+
1,
60+
`Expected 1 executable block in hello.qmd, found ${executableBlocks.length}`
61+
);
62+
63+
const language = languageNameFromBlock(executableBlocks[0]);
64+
assert.strictEqual(language, "python", `Expected python, got ${language}`);
65+
});
66+
});
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
---
2+
title: "Div Code Blocks"
3+
format: html
4+
---
5+
6+
## Code blocks in divs without blank lines
7+
8+
This tests the fix for issue #521 where closing `:::` without a preceding
9+
blank line would cause code blocks to not be detected.
10+
11+
## A slide with code
12+
13+
```{r}
14+
library(tidyverse)
15+
head(mtcars)
16+
```
17+
18+
::: {.notes}
19+
- test notes
20+
:::
21+
22+
## A slide with math
23+
24+
$$
25+
\mathbf{y} = \mathbf{X}^\top\beta + \varepsilon
26+
$$
27+
28+
::: {.notes}
29+
- test notes
30+
:::
31+
32+
## A slide with code
33+
34+
::: {.notes}
35+
- test notes
36+
:::
37+
38+
## A slide with math
39+
40+
::: {.notes}
41+
- test notes
42+
:::
43+
44+
## A slide with code
45+
46+
::: {.notes}
47+
- test notes
48+
:::
49+
50+
## A slide with math
51+
52+
::: {.notes}
53+
- test notes
54+
:::
55+
56+
## A slide with code
57+
58+
::: {.notes}
59+
- test notes
60+
:::
61+
62+
## A slide with math
63+
64+
::: {.notes}
65+
- test notes
66+
:::
67+
68+
## A slide with code
69+
70+
::: {.notes}
71+
- test notes
72+
:::
73+
74+
## A slide with math
75+
76+
::: {.notes}
77+
- test notes
78+
:::
79+
80+
## A slide with code
81+
82+
::: {.notes}
83+
- test notes
84+
:::
85+
86+
## A slide with math
87+
88+
::: {.notes}
89+
- test notes
90+
:::
91+
92+
## A slide with code
93+
94+
::: {.notes}
95+
- test notes
96+
:::
97+
98+
## A slide with math
99+
100+
::: {.notes}
101+
- test notes
102+
:::
103+
104+
## A slide with code
105+
106+
::: {.notes}
107+
- test notes
108+
:::
109+
110+
## A slide with math
111+
112+
::: {.notes}
113+
- test notes
114+
:::
115+
116+
## A slide with code
117+
118+
```{r}
119+
head(mtcars)
120+
```
121+
122+
::: {.notes}
123+
- test notes
124+
:::
125+
126+
## A slide with math
127+
128+
$$
129+
\mathbf{y} = \mathbf{X}^\top\beta + \varepsilon
130+
$$
131+
132+
::: {.notes}
133+
- test notes
134+
:::
135+
136+
## A slide with code
137+
138+
Did not previously display properly:
139+
140+
```{r}
141+
head(mtcars)
142+
```
143+
144+
::: {.notes}
145+
- test notes
146+
:::
147+
148+
## A slide with math
149+
150+
Did not previously display properly:
151+
152+
$$
153+
\mathbf{y} = \mathbf{X}^\top\beta + \varepsilon
154+
$$
155+
156+
::: {.notes}
157+
- test notes
158+
:::

packages/core/src/markdownit/divs.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ export const divPlugin = (md: MarkdownIt) => {
139139
return false;
140140
}
141141
},
142-
{ alt: [] }
142+
{ alt: ["paragraph"] }
143143
)
144144

145145
md.renderer.rules[kTokDivOpen] = renderStartDiv

0 commit comments

Comments
 (0)