Skip to content

[XMarkdown] 流式渲染无法识别带 0-3 空格缩进的围栏代码块,流式中间态内容丢失、fence 状态卡死 #2017

Description

@Xuepoo

重现步骤

在线示例链接:暂无(最小复现见下方代码,使用 @ant-design/x-markdown 的 streaming 模式)

步骤:

  1. 以流式模式(streaming={{ hasNextChunk: true }})渲染如下 markdown —— 围栏代码块(fenced code block)前缩进了 2 个空格(例如位于列表项内;CommonMark 允许 fence 前有 0-3 个空格):

    const input = '  ```js\nconst a = [1,2];\n  ```';
    <XMarkdown input={input} streaming={{ hasNextChunk: true }} />
  2. 观察流式输出:[1,2] 中的 [ 没有被当作代码块内容保护,触发了链接识别器挂起;最终输出丢失 closing fence 的末尾字符(实测输出以 `` 结尾,缺少 3 个反引号)。

  3. 若 opening fence 缩进、closing fence 顶格( ```js ... ```),feedFenceState 会把顶格的 closing fence 误认为新的 opening,inFenced 永久为 true,之后所有内容都走代码块路径。

当前行为

feedFenceStatepackages/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 的状态)
UI Bug Visual

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions