[XMarkdown] 流式渲染无法识别带 0-3 空格缩进的围栏代码块,流式中间态内容丢失、fence 状态卡死
- Dominant language
- TypeScript
- Stars
- 4.8k
- Forks
- 1.2k
- Avg merge
- 3d 3h
- Merged PRs (30d)
- 2
Description
### 重现步骤
在线示例链接:暂无(最小复现见下方代码,使用 `@ant-design/x-markdown` 的 streaming 模式)
步骤:
1. 以流式模式(`streaming={{ hasNextChunk: true }}`)渲染如下 markdown —— 围栏代码块(fenced code block)前缩进了 2 个空格(例如位于列表项内;CommonMark 允许 fence 前有 0-3 个空格):
````tsx
const input = ' ```js\nconst a = [1,2];\n ```';
````
2. 观察流式输出:`[1,2]` 中的 `[` 没有被当作代码块内容保护,触发了链接识别器挂起;最终输出丢失 closing fence 的末尾字符(实测输出以 ` `` ` 结尾,缺少 3 个反引号)。
3. 若 opening fence 缩进、closing fence 顶格(` ```js ... ``` `),`feedFenceState` 会把顶格的 closing fence 误认为新的 opening,`inFenced` 永久为 `true`,之后所有内容都走代码块路径。
### 当前行为
`feedFenceState`(`packages/x-markdown/src/XMarkdown/hooks/useStreaming.ts`)只在行首为列 0 时识别 opening fence(`lineFenceLen === 0 && (char === '`' || char === '~')`)。行首任意前导空格会先命中 else 分支置 `lineFenceRunEnded = true`,反引号不再累积,缩进的 fence 完全不可见。后果(影响流式中间态;最终渲染交给 `marked` 仍是正确的):
1. 缩进代码块的内容不被 `isInCodeBlock` 保护,按普通文本运行 per-char token 识别器,`[`、`<`、`$` 开头的内容被挂起为未完成 token(闪烁 / 占位符抖动);
2. 之后顶格的 closing fence 被当作新的 opening,`inFenced` latch 为 `true` 且无法退出,后续所有内容走 `commitCache` + code 路径,不再做任何 token 识别。
### 预期行为
与 CommonMark / `marked` 一致:fence 前允许 0-3 个空格(例如列表项内嵌套的代码块),流式状态下同样应正确识别 opening 与 closing fence,流式中间态与最终渲染一致。
### 上下文
缩进的围栏代码块(如列表项内的代码块)是常见 markdown 写法。当前流式模式下这类内容会闪烁、被吞,或导致后续内容渲染路径错误,与最终(非流式)渲染结果不一致。
### 版本
- `@ant-design/x-markdown` 2.9.0(npm 发布版)与 `main` 分支 `b529d8e9`(2026-07-31)均存在。
---
---
**真实流式渲染 Bug 现场对比图:**
(左侧为无缩进正常状态,右侧为带缩进触发挂起 Bug 的状态)

Contributor guide
No contributing guide indexed for this repository
Research direction
Start in `packages/x-markdown/src/XMarkdown/hooks/useStreaming.ts` and read `feedFenceState`, then reproduce the issue with the provided `XMarkdown` streaming example. Check how opening and closing fences with 0–3 leading spaces affect code-block protection and the `inFenced` state. Done means both fences are recognized correctly and streaming intermediate output preserves the code and resumes normal token recognition afterward.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- react, typescript
- Domain
- frontend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 72/100