Skip to content

fix(user-input-fold): 修复嵌套与波浪号代码块的定界符解析,防止折叠预览泄漏未闭合 fence - #336

Open
wswsadadbaba123 wants to merge 2 commits into
openpi-dev:mainfrom
wswsadadbaba123:main
Open

fix(user-input-fold): 修复嵌套与波浪号代码块的定界符解析,防止折叠预览泄漏未闭合 fence#336
wswsadadbaba123 wants to merge 2 commits into
openpi-dev:mainfrom
wswsadadbaba123:main

Conversation

@wswsadadbaba123

Copy link
Copy Markdown

Problem

extensions/user-input-foldparseSegments() 使用硬编码正则匹配代码块定界符:

  • FENCE_CLOSE 接受任意 ≥3 个反引号的行,违反 CommonMark §4.5(N 个反引号开起的代码块只能由 ≥N 个反引号闭合)。粘贴包含内部 ``` 代码块的 ```` 模板(如 prompt 模板)时,外层代码块被提前闭合,后续内容误判为 Prose,折叠输出泄漏未闭合的 ````markdown,导致 TUI 中折叠标记 `… folded` 之后的所有终端消息排版被吞噬进未闭合代码块。
  • FENCE_OPEN 仅匹配反引号,波浪号(~~~)代码块完全不被识别,被整体当作普通段落截取,折叠时同样泄漏未闭合代码块。

受影响对象:粘贴长 Markdown(嵌套 fence 或波浪号 fence)并触发折叠的 TUI 用户。模型可见 Context 与 Session 原始记录不受影响(本扩展仅改渲染层)。

Value

  • 修复两类粘贴场景下 TUI 渲染流崩溃:嵌套反引号代码块与波浪号代码块是粘贴 prompt 模板、日志的常见形态;
  • 折叠预览在所有路径下保证输出配对 Markdown(包括未闭合 fence 截断到消息末尾的情况),消除"一个未闭合 fence 吞掉后续整个终端界面"的级联故障;
  • 与 CommonMark 规范对齐,降低后续维护中围绕定界符语义的歧义。

Approach

extensions/user-input-fold/index.ts 中:

  1. 定界符按字符种类 + 长度匹配:新增 openFence() 解析开起 fence 的字符(`~)与长度 N(并按规范拒绝 info string 含反引号的反引号 fence);新增 isCloseFence() 要求闭合 fence 与开起 fence 字符相同且长度 ≥ N。内部 ``` 不再提前闭合 ```` 块,反引号与波浪号互不闭合。
  2. 未闭合 fence 改为代码块 + 合成闭合 fence:旧逻辑将未闭合 fence 的整条消息回退为纯文本,反而把未闭合 fence 泄漏进折叠预览;新逻辑按 CommonMark 将其视为延续到消息末尾的代码块,并在折叠时补齐匹配的闭合 fence,保证预览永远是配对 Markdown。
  3. 保持纯函数与无副作用foldUserMessage 不变式不变——输入不可变、确定性输出、模型接收完整原文。

测试更新:原有一个测试固化了"未闭合 fence 泄漏"的 bug 行为,改为断言合成闭合 fence;新增 4 个回归测试(嵌套 fence、更长闭合 fence、波浪号 fence、反引号/波浪号互不闭合)。

Validation

本机未安装 bun(bun 不在 PATH),以下均以 package.json 中对应脚本的底层 node 命令等价执行:

  • node --test tests/extensions/user-input-fold/index.test.ts:25/25 通过(含 4 个新增回归测试);
  • tsc --noEmit(typecheck):通过;
  • biome lint .:通过;biome format(两个改动文件):通过;
  • node scripts/check-config-contract.mjs / node scripts/check-discipline-ledger.mjs:通过;
  • 用户报告的最小复现脚本(嵌套 + 内部 ``` 场景):修复前泄漏未闭合 `markdown 且内容误判为 Prose;修复后正确输出闭合的 ```` ```` ```` 配对与… folded 18 lines` 标记;
  • 全量 node scripts/run-tests.mjs:1117 个 node 测试中 27 个失败,全部为预先存在的 Windows 环境问题(git-info 测试用 #!/bin/sh stub + : PATH 分隔符导致挂起、symlink 权限、taskkill 时序抖动),已通过 stash 对照确认在干净 HEAD 上同样失败,与本 PR 无关;vitest(file-search)28 过 2 败,同样经 stash 对照确认与 HEAD 一致。

未运行:bun run check 原样(bun 未安装);建议 CI(Linux)完整跑一遍作为最终仲裁。

Impact

  • user-visible behavior:嵌套/波浪号/未闭合 fence 的长消息折叠预览不再破坏 TUI 渲染;短消息(低于折叠阈值)行为完全不变(既有 round-trip 测试覆盖)。
  • model-visible context/tools:None。registerMarkdownTransformer 仅改渲染层,模型接收的原始文本不变。
  • runtime/lifecycle:None。纯函数解析逻辑,无新增状态、IO 或依赖。
  • persisted config/data:None。
  • compatibility or risk:低。唯一行为变更是"未闭合 fence 的长消息"从纯文本截断改为带合成闭合 fence 的代码块预览——旧渲染本身就是 bug(泄漏未闭合 fence)。既有测试中仅一个断言旧 bug 行为的用例被更新。

本次提交修复了问题 #334 感谢@hasak21

@github-actions github-actions Bot added the area:ui Terminal UI, rendering, themes, or visual assets label Sep 1, 2026

@somewan820 somewan820 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

复审 exact head 5fa8628d6c86210a642f6ec95ad7dcbc732f4cda

未发现确认的 P0–P3 问题,也没有值得单独提出的简化切口。openFence() / isCloseFence() 分别承担 opening/closing fence 语义,字符类型与长度匹配正确;未闭合 fence 的合成闭合符能避免折叠预览破坏后续 TUI Markdown。改动仍局限于显示层,不影响模型上下文、Session 持久化或生命周期。

验证:

  • node --test --experimental-strip-types tests/extensions/user-input-fold/index.test.ts:25/25 通过;
  • bun run check:通过;
  • bun run test:Node 1118 通过、1 个平台跳过;Vitest 30/30 通过;
  • git diff --check:通过。

Approve。当前 CI 全绿;GitHub 状态仍需以最新页面为准,且分支相对 main 处于 BEHIND,同步主线后应重新运行最终检查。

@wswsadadbaba123

Copy link
Copy Markdown
Author

出错位置tests/web/cli.test.ts
出错代码:
const [exitCode] = (await once(child, "close")) as [number | null];
assert.equal(exitCode, 1);
是的,这是测试代码的问题。

根本原因:

在 Node.js 中,当进程被信号(如 SIGTERM)终止时,close 事件返回的是 [exitCode, signal],其中:

如果进程通过信号被杀死,exitCode 是 null,signal 是信号名(例如 "SIGTERM")
如果进程正常退出,exitCode 是数字,signal 是 null

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:ui Terminal UI, rendering, themes, or visual assets

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants