十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

marked 中反引号代码段与加粗标记的优先级解析:backtick_precedence 测试深度解读

marked 中反引号代码段与加粗标记的优先级解析:backtick_precedence 测试深度解读 marked 中反引号代码段与加粗标记的优先级解析backtick_precedence 测试深度解读【免费下载链接】markedA markdown parser and compiler. Built for speed.项目地址: https://gitcode.com/gh_mirrors/ma/markedmarked 在解析行内元素时反引号backtick包裹的代码段code span与**加粗strong标记的优先级如何处理当**出现在反引号内部时它究竟会被渲染为加粗还是原样保留为代码文本本文以仓库中的 backtick_precedence 测试用例 为核心逐条拆解其预期输出并结合 行内代码正则、Tokenizer.codespan 实现 与 Renderer.codespan 实现 的源码讲清 marked 在这类歧义输入下的完整判定逻辑帮助读者理解代码段与强调标记的嵌套边界从而写出可预期的 Markdown。一、测试用例一份歧义输入的对照实验位于 test/specs/new/backtick_precedence.md 的测试文件是一份典型的new规格测试.md文件保存输入同名的 .html 文件 保存 expected 输出。它用 9 个精心构造的输入系统验证反引号代码段优先于加粗/强调解析这一规则。全部用例整理如下序号输入Markdown预期输出HTML考察点1**You might think this should be bold, but:**p**You might think this should be bold, but: code**/code/p单反引号代码段吞掉**外层**仅作字面文本2**You might think this should be bold, but: ** |You might think this should be bold, but:| 双反引号代码段同样吞掉**3**You might think this should be bold, but: ** |You might think this should be bold, but:| 三反引号代码段同样吞掉**4**You might think this should be bold, but: ** |You might think this should be bold, but:| 四反引号代码段同样吞掉**5**This should be bold** andthis should be codepstrongThis should be bold/strong and codethis should be code/code/p正常场景加粗与代码段各自独立工作6**startcontains **end**pstrongstart codecontains **/code end/strong/p代码段嵌套在加粗内部且不打断外层加粗7**This should be bold ** |This should be bold /p反引号数量不匹配时code span 不成立8**This should be bold**pstrongThis should be bold反向不匹配多余的反引号留在文本中9**start contains ** end**pstrongstart codecontains **/code end/strong/p多反引号代码段嵌入加粗加粗跨代码段延续从这张表可以提炼出三条核心结论它们共同构成了 marked 在此场景下的行为准则代码段优先于加粗解析只要**位于一对匹配的反引号之内它就被视为代码内容原样输出用例 1–4外层的**也不会形成加粗加粗可以被代码段跨越**...code**...**是合法的加粗的起始与结束标记可以分别位于代码段两侧代码段内部的**不会提前终止加粗用例 6、9反引号必须成对匹配代码段的开闭反引号数量必须一致且闭标记之后不能再紧跟反引号数量对不上时反引号退回普通文本**恢复正常加粗语义用例 7、8。二、行为背后的实现inlineCode 正则与 codespan 词法分析上述行为并非巧合而是由 src/rules.ts 中定义的行内代码正则直接决定的// src/rules.ts L285 const inlineCode /^()([^]|[^][\s\S]*?[^])\1(?!)/;逐段解读这个正则^(捕获开头的连续反引号反引号数量记为分组 1如 、、([^]|[^][\s\S]*?[^])匹配代码段正文——要么是单个非反引号字符要么是以非反引号字符开头和结尾的任意内容[\s\S]*? 为懒惰匹配可跨行\1要求结尾出现与开头相同数量的反引号(?!)负向前瞻保证闭合反引号之后**不能再紧跟反引号**避免把 ** 这种多打一个反引号的输入误判为代码段。再看该正则的消费方 Tokenizer.codespancodespan(src: string): Tokens.Codespan | undefined { const cap this.rules.inline.code.exec(src); if (cap) { let text cap[2].replace(this.rules.other.newLineCharGlobal, ); const hasNonSpaceChars this.rules.other.nonSpaceChar.test(text); const hasSpaceCharsOnBothEnds this.rules.other.startingSpaceChar.test(text) this.rules.other.endingSpaceChar.test(text); if (hasNonSpaceChars hasSpaceCharsOnBothEnds) { text text.substring(1, text.length - 1); } return { type: codespan, raw: cap[0], text, }; } }这段实现补充了两个细节换行归一化代码段正文中的换行符\n会被替换为空格newLineCharGlobal因此跨行代码段渲染为单行首尾空格修剪当正文两端各有一个空格且中间还有非空格字符时会去掉首尾各一个空格——这对应 CommonMark 中代码段内容前后各留一个空格的规则例如 a 输出codea/code而非code a /code。而标记优先级之所以代码段优先在于 Lexer 在行内扫描时按固定顺序尝试各规则codespan正则从当前位置开头就能命中反引号并完整吞掉一对代码段后续的strong/em规则便只能在剩余文本上工作。这正是用例 1–4 中外层**沦为字面文本、用例 6 与 9 中加粗标记得以跨代码段延续的根因。三、渲染端代码内容如何安全输出词法分析完成后代码段最终由 Renderer.codespan 渲染codespan({ text }: Tokens.Codespan): RendererOutput { return code${escapeHtmlEntities(text, true)}/code as RendererOutput; }可以看到代码段正文在输出前会经过escapeHtmlEntities(text, true)转义第二个参数为 true 表示同时处理实体因此、、、引号等字符会被安全编码避免把用户输入的 HTML 注入页面——这是代码段与裸文本的本质区别。配合用例 1–4 可知**在代码段内不需要转义因为它本就属于字面文本语境而之类的字符则需要转义以保证 HTML 安全。四、把预期交给测试如何在仓库中运行该用例backtick_precedence 属于new规格目录由 test/run-spec-tests.js 统一加载与断言。该运行器通过markedjs/testutils的getTests读取specs/new目录下所有.md/.html配对并对newTests使用默认 options运行// test/run-spec-tests.js L36-L39 runTests({ tests: newTests, parse, });这里的parse即new Marked(options).parse(markdown)。也就是说backtick_precedence 在 marked 的默认配置gfm、breaks、pedantic 均为默认值下即可通过。在仓库根目录执行完整的规格测试npm test或仅运行规格测试部分构建产物后执行test:specs可参考 package.json 中的test脚本组合。测试通过后test/specs/new/backtick_precedence.md的每一行输入都会在 backtick_precedence.html 中找到逐字节一致的输出。五、常见疑问速查Q1为什么**里的**不加粗因为codespan规则先于strong/em消费文本反引号对内的所有字符一律按代码内容处理**只是两个普通星号字符。Q2**abc**会怎样** 会得到stronga codeb**/code c/strong对应用例 6 的模式加粗标记跨越了代码段代码段内部的**不会破坏加粗结构。Q3**ab 输出里为什么有多余的反引号** 因为inlineCode的(?!)与\1要求闭合反引号恰好匹配且后面不能再有反引号配对失败的输入会让codespan不命中对应用例 7、8于是反引号按普通字符输出**恢复加粗语义。Q4代码段内的 HTML 标签会生效吗不会。渲染时经 Renderer.codespan 的escapeHtmlEntities转义b这类内容会以字面文本形式出现在code中。六、小结backtick_precedence 用 9 个用例把 marked 的代码段优先、加粗可跨越、反引号必须成对三项规则刻画得清晰可验证判定逻辑沉淀在 src/rules.ts 的inlineCode正则词法阶段由 src/Tokenizer.ts 的codespan完成渲染阶段由 src/Renderer.ts 安全输出最后通过 test/run-spec-tests.js 保证行为长期稳定。理解这组优先级是正确书写包含反引号与加粗标记混排文档如 README、文档站源码的前提——它决定了你的**是被渲染成加粗还是安静地躺在代码段里。【免费下载链接】markedA markdown parser and compiler. Built for speed.项目地址: https://gitcode.com/gh_mirrors/ma/marked创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表