bat 的 Markdown 高亮回归测试体系:从 example.md 源码样本到 ANSI 快照对比
bat 仓库为每种受支持的语法都维护了一对“源码样本 + 高亮快照”测试文件,tests/syntax-tests/ 下的 Markdown 目录是其中最具代表性的一个:它以一份覆盖 Markdown 全部常见语法要素的 example.md 为样本,验证 bat 基于 syntect 语法定义对 Markdown 的解析、作用域映射与 24 位色 ANSI 输出是否稳定。读完本文,你将理解 bat 如何用“快照比对”方式守护语法高亮的正确性,并能复现整套 Markdown 高亮测试流程。
测试样本的组织方式:source 与 highlighted 成对出现
Markdown 相关测试文件位于两处目录,一一对应:
- 源码样本(纯文本):tests/syntax-tests/source/Markdown/example.md,同目录还有 typescript.md 用于验证 Markdown 内嵌 TypeScript 代码围栏的场景;
- 高亮快照(ANSI 输出):tests/syntax-tests/highlighted/Markdown/example.md 与 typescript.md。
每个样本按“语言目录/文件名”组织,这一约定由对比脚本固化下来:compare_highlighted_versions.py 中的 strip_root 函数只取 dirname/filename 两级路径作为比较键(见 tests/syntax-tests/compare_highlighted_versions.py 中的 strip_root 定义),也就是说 Markdown 的快照必须存放在以语言名命名的目录中才能被正确发现。
example.md 样本:一份覆盖 Markdown 语法全集的“考题”
example.md 全文仅 39 行,但每一行都在考核 Markdown 语法定义文件中的一类规则:
| 行号 | 样本内容 | 考核的语法要素 |
|---|---|---|
| 1–6 | # H1 到 ###### H6 |
ATX 六级标题 |
| 8–9 | **bold** *italic* ~~strike~~ ~~***link***~~ 及 __bold__ _italic_ |
粗体、斜体、删除线、链接,含星号/下划线两种写法与嵌套强调 |
| 11–17 | * Unordered 缩进列表、1. Ordered 有序列表 |
无序/有序列表及缩进层级 |
| 19 | Markdown Logo |
图片语法 |
| 21–22 | > quotes 多行引用 |
块引用 |
| 24 | `fn inline_code() ...` |
行内代码 |
| 26–30 | ```rust 围栏代码块(内含 Rust 函数) |
围栏代码块的语言名识别与内嵌 Rust 高亮 |
| 32–34 | - [x] Task 等 |
GFM 任务列表 |
| 36–39 | 两列表格 | GFM 表格 |
值得注意的两个细节:
- 第 26 行的围栏语言写的是
rust。bat 的 Markdown 补丁(后文详述)已把匹配规则放宽为rust|rs,但样本仍用最常用的rust写法,保证快照反映主流用法; - 代码块内容是合法的 Rust 代码
fn syntax_highlighted<T: AsRef<&str>>(thing: T)。Markdown 语法文件通过embed机制把代码围栏内部交给 Rust 语法处理,因此这段内容的高亮快照同时考验了“Markdown → 内嵌 Rust”的双层语法协作,是快照里彩色 token 最密集的区域。
高亮快照是怎么生成的:固定的 bat 选项与环境隔离
生成脚本 tests/syntax-tests/create_highlighted_versions.py 定义了所有语言(包括 Markdown)统一的高亮基准:
# Avoid 'default' theme because it can choose a different theme based on
# the appearance settings on macOS.
BAT_OPTIONS = [
"--no-config", # 忽略用户 bat 配置
"--style=plain", # 只输出纯内容,不带行号/文件头
"--color=always", # 强制输出 ANSI 色码
"--theme=Monokai Extended", # 固定主题,保证色彩可复现
"--italic-text=always", # 固定斜体行为
]
每个选项都有明确目的:--no-config 排除个人配置干扰;固定 Monokai Extended 主题(主题文件位于 assets/themes/sublime-monokai-extended/)避免“default”主题随 macOS 明暗外观切换导致快照漂移;--style=plain 让快照只包含内容本身的着色,不含行号等 UI 装饰。
脚本还会主动清除一批环境变量:BAT_CACHE_PATH、BAT_CONFIG_PATH、BAT_OPTS、BAT_THEME、NO_COLOR、PAGER 等,并显式设置 COLORTERM=truecolor。这一步解释了快照文件的“长相”——highlighted/Markdown/example.md 中充满了 ESC[38;2;R;G;Bm 形式的 24 位真彩色码,例如标题行:
ESC[38;2;253;151;31m# ESC[0m... ← H1 的 "H1" 用 RGB(253,151,31) 高亮
逐段观察该快照,可以读出 syntect 作用域到主题的映射结果:
ESC[38;2;253;151;31m(橙红):markup.heading标题文本,出现 7 次,对应 H1–H6 共六行及标题样式;ESC[1;38;2;249;38;114m(粗体红):markup.bold粗体文本,如bold与嵌套强调中的粗体部分;ESC[3;38;2;228;46;112m(斜体蓝绿):markup.italic,对应italic、链接文本中的斜体段;ESC[3;38;2;102;217;239m(斜体天蓝):块引用的> quotes;ESC[38;2;236;53;51m(品红):行内代码`fn inline_code()...`;ESC[38;2;190;132;255m(紫色关键字)与ESC[38;2;230;219;116m(黄色字符串):rust围栏内的 Rust 关键字与字符串字面量,证明内嵌语法确实生效;ESC[4;38;2;166;226;46m(下划线绿色):链接/图片 URL。
这些色码不是手写的,而是“Markdown 语法定义 → 作用域 → 主题”链条的产物,因此快照漂移即意味着某个环节发生了变化。
Markdown 语法定义上的两个补丁:任务列表之外还藏了什么
bat 不直接使用上游 Sublime 的 Markdown 语法文件,而是通过补丁注入。assets/patches/Markdown.sublime-syntax.patch 在构建资源时由 assets/create.sh 应用(脚本遍历 patches/*.patch 执行 patch --strip=0),其中与测试直接相关的改动有两处:
- 围栏语言别名放宽:
rust的匹配从((?i:rust))改为((?i:rust|rs)),即```rs围栏同样触发 Rust 内嵌高亮; - 新增 TypeScript 嵌入:补丁为
typescript|ts增加了embed: scope:source.ts的围栏规则,这正是 source/Markdown/typescript.md 样本存在的原因——它验证 Markdown 内嵌 TypeScript 代码块能正确高亮。
补丁中还有一组较大的结构性调整:移除了原语法中 heading1/heading2 的 Setext 分支点(heading2-branch、setext_escape 变量等),把 Setext 标题识别简化为段落上下文中直接匹配 ===+ / ---+ 的写法,并顺带清理了粗斜体上下文里的 setext_escape 逃逸。这类改动的效果会在 example.md 快照中体现为标题/段落着色边界的变化,因此补丁更新后必须重新生成快照。
测试闭环:update.sh 与 regression_test.sh
整个 Markdown(以及其他语言)高亮测试由两个脚本驱动:
- tests/syntax-tests/update.sh:调用
create_highlighted_versions.py -O highlighted重新生成highlighted/目录下全部快照。在语法文件、补丁或主题变更后用此脚本刷新基线; - tests/syntax-tests/regression_test.sh:回归验证入口。它先
mktemp -d建临时目录,用当前 bat 重新高亮全部source/样本,再调用compare_highlighted_versions.py与仓库中存储的highlighted/快照做逐文件 unified diff:
output_directory=$(mktemp -d)
"$script_directory"/create_highlighted_versions.py --output="$output_directory"
"$script_directory"/compare_highlighted_versions.py \
"$script_directory/highlighted" \
"$output_directory"
比对脚本对每个文件输出 diff;只要任一对“旧快照 vs 新输出”存在差异,或新出现了没有快照的语言目录(输出 “No fixture for this language, run update.sh”),脚本即以退出码 1 失败。对 Markdown 而言,这意味着 highlighted/Markdown/example.md 中任何一个 ANSI 色码的变动都会使 CI 变红——这正是把“高亮效果”当作可回归资产的核心思想。
语法映射:.md 与 .mkd 如何被识别为 Markdown
样本文件名为 example.md,bat 对扩展名 .md 的识别来自随语法定义内置的扩展名列表;仓库另在 src/syntax_mapping/builtins/common/50-markdown.toml 中补充了一条常见别名映射:
[mappings]
"Markdown" = ["*.mkd"]
即 .mkd 扩展名同样映射到 Markdown 语法。这类 NN-name.toml 映射文件位于 src/syntax_mapping/builtins/common/ 等目录,bat 在运行时按文件名/扩展名将输入路由到对应的 syntect 语法,测试样本正是通过这条路由链才走到 Markdown 语法定义的。
小结
- Markdown 在 bat 测试体系中的代表是 source/Markdown/example.md,它以 39 行覆盖标题、强调、链接、列表、引用、代码块、任务列表与表格;
- 快照文件 highlighted/Markdown/example.md 是固定选项(
--theme=Monokai Extended、--style=plain、--color=always、COLORTERM=truecolor)下 bat 的 24 位色 ANSI 输出,逐色码即可审计“语法作用域 → 主题”的映射结果; - 语法行为由 assets/patches/Markdown.sublime-syntax.patch 定制(
rust|rs别名、TypeScript 嵌入、Setext 简化),由 assets/create.sh 在构建时应用; - 变更闭环为:改语法/补丁/主题 →
update.sh刷新快照 →regression_test.sh保证后续每次构建的高亮输出与快照逐字节一致。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00