尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

面向 AI 与编码 Agent 的 highlight.js 贡献指南:从 Grammar API 偏好到测试工作流

面向 AI 与编码 Agent 的 highlight.js 贡献指南:从 Grammar API 偏好到测试工作流 前端【免费下载链接】highlight.jsJavaScript syntax highlighter with language auto-detection and zero dependencies.项目地址https://gitcode.com/gh_mirrors/hi/highlight.js点击查看免费下载导读本指南基于 highlight.js 仓库根目录下的 AGENTS.md 编写是专门写给 AI 编码助手Agent与人类贡献者共用的协作规范。它覆盖两大部分一是AI 参与贡献的项目政策Assisted-by提交标记与docs/ai-contributions.md中的详细约定二是编写语法Grammar时必须遵循的 API 偏好——scope取代className、弃用relevance、用正则前瞻替代回调、正确区分beginScope/endScope、优先使用 multi-match。读完本文你将掌握在贡献语法文件时怎么写更规范、怎么改更现代、怎么测更高效的完整方法并能直接在src/languages/与test/markup/中落地验证。一、项目定位与本文的适用场景highlight.js 是一个零依赖的 JavaScript 语法高亮库支持语言自动检测语法定义以语言文件形式存放在 src/languages/ 下每个.js文件定义一个语法。AGENTS.md 是仓库专门为AI 辅助开发流程与语法编写规范沉淀的说明文件主要读者是计划提交 PR含 AI 辅助提交的贡献者需要修改或新增语法的开发者希望在语法质量与测试方式上对齐官方实践的维护者。文件中出现的所有规则都与源码实现一一对应下文将逐条给出可验证的源码与测试证据。二、AI 参与贡献政策dogfood2.1Assisted-by提交标记项目政策要求凡工作明显由工具辅助完成AI 助手生成、自动补全、Agent 执行等需要在 commit 或 PR 描述中附带Assisted-by尾注trailer即吃自己的狗粮eat our own dogfoodAssisted-by: model (effort)字段含义如下表字段含义示例model产品 / 模型标识Grok 4.5、Claude Sonnet 4effort推理/努力等级设置如有low、medium、high、max实际示例Assisted-by: Grok 4.5 (low) Assisted-by: Claude Opus 4 (high) Assisted-by: Copilot规则要点未知字段宁可省略也不要猜测Assisted-by: Copilot这种只有模型名的写法是合法的完全由人类完成的提交不要声称有 AI 辅助。2.2 项目级 AI 贡献政策细节Assisted-by约定只是入口完整的政策文档见 docs/ai-contributions.md。其中明确人在环内Human in the loop提交前必须逐行审查工具产出不能提交未审阅的机器输出变更自持You own the change作者必须理解自己提交的每一行能够在评审中解释和答辩拒绝凑数产出No slop未验证、低质量、批量的无效改动浪费维护者时间优先提交小而聚焦、评审成本小于价值的变更标准不降低测试、风格、范围等常规贡献规范对工具生成的代码一视同仁明确禁止未经人工逐次批准的无人值守机器人自动开 PR/issue/评论用 AI 端到端刷good first issue这类 issue 旨在让人成长全自动完成违背初衷。对维护者而言文档还给出了违规处置流程先Request changes附上复用性回复若明显偏离可评审方向则关闭 PR同一账号二次违规时升级到管理员限制其开 PR。该政策参考了 LLVM AI Tool Use Policy 与 Fedora 的 AI 辅助贡献政策。三、Grammar API 偏好现代写法规范AGENTS.md 用很大篇幅规定语法文件的现代写法这些偏好直接对应 src/lib/compiler_extensions.js 中的编译器扩展实现。3.1 优先scope弃用classNameclassName自 11.0 起被弃用docs/mode-reference.rst 中className条目明确标注deprecated:: 11.0。新代码应使用scope字符串作为 mode 的整体 CSS scope// 推荐 { match: /\bfoo\b/, scope: keyword } // 新代码中应避免 { match: /\bfoo\b/, className: keyword }从源码看className与scope的兼容是靠编译器扩展 scopeClassName 完成的编译阶段检测到className就迁移到scope并删除旧字段。这意味着旧语法仍能工作但新提交的代码不应该再引入它。3.2 触碰 mode 时移除relevancerelevance字段在 mode 级别已被弃用不要新增relevance字段。当 PR 修改touch了某个仍带relevance的 mode 时应顺手把它删掉——与className → scope相同的触碰即现代化touch it, modernize it原则。需要说明的是src/lib/compiler_extensions.js 中的compileRelevance仍会为未显式声明relevance的 mode 注入默认值1即不写relevance并不等于没有 relevance而是让编译器统一处理默认值避免语法作者手工维护。一些公共模式如 modes.js 中的NUMBER_MODE、TITLE_MODE为抑制误报会显式写relevance: 0这类既有写法属于已被接受的公共模式新语法不应再模仿其显式relevance的书写习惯。3.3 优先用 lookaround 而非on:begin/on:end回调当规则本质是匹配这个但排除某些情况时优先用纯正则尤其是负向前瞻/负向后顾而不是在on:begin/on:end回调里调用response.ignoreMatch()。理由回调在每个候选匹配上都要跑 JS难以优化前瞻/后顾由正则引擎处理性能更好。// 推荐——排除看起来像调用的语句关键字 { match: /\b(?!(?:if|for|while|switch)\b)[a-z_][A-Za-z0-9_]*(?\()/, scope: title.function } // 能用一个前瞻解决时应避免这种写法 { match: /\b[a-z_][A-Za-z0-9_]*(?\()/, scope: title.function, on:begin: (m, resp) { if (/^(?:if|for|while|switch)$/.test(m[0])) resp.ignoreMatch(); } }回调并非不可用当决策需要集合判断、多匹配状态、begin/end 配对校验或正则难以表达的逻辑时回调依然是合理选择。典型例子是 modes.js 中的END_SAME_AS_BEGIN——它在on:begin里用resp.data._beginMatch m[1]暂存起始词在on:end里比对并决定是否ignoreMatch()这正是回调擅长、纯正则难以表达的场景。3.4scope与beginScope/endScope不可混用三者作用范围完全不同详见 docs/mode-reference.rst 的beginScope/endScope条目字段作用于scopestring整个 modebegin 与 end 之间的全部内容作为一个区域beginScope仅 begin 匹配部分string 包裹整个 beginobject 按多匹配片段分别指定endScope仅 end 匹配部分形态与beginScope相同当 mode 在 begin 之后还有正文例如end: /$/、contains: […]且只想让开头词元lexeme获得 keyword/punctuation 配色、正文保持无 scope 时必须使用beginScope// begin 片段单独上色正文除 contains 外保持无 scope { begin: [/^[ \t]*/, /\bFeature\b/, /:/], beginScope: { 2: keyword, 3: punctuation }, end: /$/, contains: [VARIABLE] } // 错误这会把整个 mode/行都当作 keyword { begin: [/^[ \t]*/, /\bFeature\b/, /:/], scope: { 2: keyword, 3: punctuation }, end: /$/ }糖语法sugar仅用于match: [ … ]没有end时object 形式的scope: { 1: …, 3: … }会被编译为beginScope。这一逻辑实现在 src/lib/ext/multi_class.js 的scopeSugar函数中检测到 object 形态的scope时把它搬到beginScope并删除原字段。因此 match-only 的多类规则用 objectscope是合法的但若 mode 有真正的begin/end对应显式写beginScope让意图更清晰。另外字符串形式beginScope: keyword包裹的是整个 begin 词元不区分多匹配索引。从源码深入看 multi_class.js 的实现beginMultiClass要求begin必须是数组、beginScope必须是 object且与skip/excludeBegin/returnBegin互斥违反会抛MultiClassError随后remapScopeNames会把你写的 1-based 索引重排——因为数组里的每个正则会被拼进一个更大的正则内部捕获组会破坏索引对齐函数通过regex.countMatchGroups累加偏移量把索引重映射到正确的顶层捕获组位置见 remapScopeNames 的注释示例(a)(((b)))(c)会产生[a,b,b,b,c]因此 scope 映射必须修正为{1, 2, 5}。对应测试见 test/api/multiClassMatch.js其中验证了begin: [a,b,c]className: {1:a, 3:c}对输入abcdef输出span classhljs-aa/spanbspan classhljs-cc/spanspan classhljs-defdef/span内嵌捕获组/((func))/的 match 数组仍能按1: keyword正确取到func多类匹配可以嵌在带className的父 mode 内部正常工作。3.5 关键字 后续 token优先 multi-match当规则本质是keyword/punctuation然后空白然后标识符且各片段需要不同 scope 时优先使用 multi-match而不是beginexcludeBeginend: /\W/这类老式写法// 推荐——match-onlyobject scope 是 beginScope 的糖 { match: [/\bnew\b/, /\s/, hljs.IDENT_RE], scope: { 1: keyword, 3: type } } // 推荐——begin 之后还有正文使用 beginScope { begin: [/\bnew\b/, /\s/, hljs.IDENT_RE], beginScope: { 1: keyword, 3: type }, end: /$/, contains: [/* ... */] } // 老式写法——能放进 multi-match 时新代码应避免 { className: type, beginKeywords: new, // 或 begin: /new\s/, excludeBegin: true end: /\W/, excludeBegin: true, excludeEnd: true }multi-match 的核心特性使用begin: [ … ]或match: [ … ]连续 pattern 组成的数组每个片段的 scope 使用从 1 开始的索引指向数组元素beginScope/endScope或 match 上的 objectscope糖避免产生空 span让 keyword 与 type/title 的边界显式化。同样的思路适用于:之后的类型规则、class/enum标题等场景。现有用法可参考 java.js、scala.js、rust.js 以及测试 test/api/multiClassMatch.js。beginScope的实际使用在 fsharp.js含beginScope: { 2: meta }、gherkin.js、freedesktop.js 中都能找到。3.6 模式构建Building patterns优先使用正则字面量或模板字符串hljs 接受字符串 pattern无需new RegExp(...)拼接 lookaround 与IDENT_RE等片段时优先使用regex.concat(...)模仿相邻语法的风格例如 src/languages/lib/java.js 的数字变体用模板字符串而非new RegExp。补充说明字符串形式的正则与hljs.IDENT_RE定义见 modes.js是语法文件中非常常见的组合配合regex.concat可以像搭积木一样组合 pattern 而无需关心转义边界。四、运行测试先构建再测试AGENTS.md 强调一个关键前提markup 测试和大多数 mocha 套件加载的是build/目录的编译输出而不是直接加载src/。修改src/languages/下的语法后必须重新构建否则会得到Cannot find module ../build或陈旧结果。4.1 常用命令# 重建单个语言快然后运行它的 markup 测试 node tools/build.js -t node rust ONLY_LANGUAGESrust npm run test-markup # 完整 node 构建所有语言 node tools/build.js -t node # 完整测试套件同样需要先构建 npm test # 其他定向脚本见 package.json npm run test-markup npm run test-detect其中node tools/build.js -t node对应 package.json 中的build脚本npm test实为mocha testtest-markup为mocha test/markuptest-detect为mocha test/detect见 package.json。4.2 注意事项ONLY_LANGUAGES是空格分隔的 markup 文件夹名列表如rust、bash对应test/markup/ /目录Mocha 的--grep在这里往往匹配不佳语言套件嵌套在动态describe下markup 测试优先用ONLY_LANGUAGESmarkup 用例以配对文件形式存在test/markup/lang/name.txt与name.expect.txt。期望文件比较的是hljs.highlight(...).valuetrim 后。以 test/markup/bash/ 为例可以看到arithmetic.txt/arithmetic.expect.txt、escaped-quote.txt/escaped-quote.expect.txt等成对文件不要从本地重建提交build/产物通常由 CI 构建除非项目明确要求。4.3 源码级佐证测试代码确实从build/加载编译产物例如 test/api/multiClassMatch.js 首行就是const hljs require(../../build);——这直接印证了 AGENTS.md 关于先 build 再测试的警告。检测detect测试同理目录结构见 test/detect/rust。五、风格与卫生Style / Hygiene最后一条规范关于代码卫生忽略尾随空白以及git diff --check报告的轻微 EOF 换行问题除非你本来就在修改那一行。不要为了清理空白专门开 commit也不要用尾随空格阻塞评审宁可保留历史噪音也不要顺手制造与功能无关的空白 diff。这条规则的目的很明确——让 review 聚焦在真实变更上。六、实践清单给 AI 助手与贡献者的快速自检结合全文提交一个语法相关 PR 前请逐条核对归属标记工具辅助较多时commit/PR 加Assisted-by: model (effort)且所有产出都经人工审阅API 现代化新代码一律scope不用className触碰到的 mode 顺手移除relevance正则优先能用前瞻/后顾表达排除就不写on:begin/on:end回调回调留给真正需要状态的场景如END_SAME_AS_BEGIN作用域正确区分scope整个 mode、beginScope/endScope仅 begin/end有begin/end对时显式用beginScopematch-only 时 objectscope糖也可multi-match 优先关键字/标点 空白 标识符这类规则用begin: […]或match: […] 1-based 索引 scope不再用excludeBegin/end: /\W/的旧式组合先构建再测试node tools/build.js -t node rust后用ONLY_LANGUAGESrust npm run test-markup定向验证不提交build/产物最小 diff不顺手清理无关的尾随空白让评审聚焦真实改动。按这套规范产出的语法既能在 test/markup/ 与 test/api/multiClassMatch.js 中通过官方测试框架验证也能与 src/languages/java.js、src/languages/rust.js 等核心语法的现代风格保持一致。赞分享前端【免费下载链接】highlight.jsJavaScript syntax highlighter with language auto-detection and zero dependencies.项目地址https://gitcode.com/gh_mirrors/hi/highlight.js点击查看免费下载相关推荐InstallerX Revived 代码贡献全指南面向 AI 编码 Agent 的仓库协作规范与构建流程InstallerX Revived 代码贡献全指南面向 AI 编码 Agent 的仓库协作规范与构建流程 InstallerX Revived 是一个社区维原生移动Warp 贡献指南从代码提交、编码规范到测试与基准测试的完整工作流Warp 贡献指南从代码提交、编码规范到测试与基准测试的完整工作流 Warp 是一个面向 GPU 加速仿真、机器人与机器学习的 Python 框架其核心能力高性能计算物理引擎图形学机器人Vim 仓库开发协作指南面向 AI Coding Agent 与贡献者的贡献规范、构建测试与代码风格全解析Vim 仓库开发协作指南面向 AI Coding Agent 与贡献者的贡献规范、构建测试与代码风格全解析 本文以 Vim 官方仓库根目录的 AGENTS.m开发工具代码编辑器上一篇如何使用Context7 MCP Server构建可靠的文档检索系统完整指南下一篇告别浪费Zen Browser打印功能三大优化技巧让纸张与质量兼得创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表