拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Prettier 处理 Markdown Wiki 链接([[...]])行尾换行的完整机制与源码解析

Prettier 处理 Markdown Wiki 链接[[...]]行尾换行的完整机制与源码解析【免费下载链接】prettierPrettier is an opinionated code formatter.项目地址: https://gitcode.com/gh_mirrors/pr/prettier本文基于 Prettier 仓库中tests/format/markdown/wiki-link/end-of-line.md这一格式化测试用例展开深入讲解 Prettier 在 Markdown 换行proseWrap场景下如何把[[wiki link]]风格的 Wiki 链接当作不可拆分的单一实体处理。读完本文你将掌握proseWrap三种取值对含 Wiki 链接段落的具体影响、Wiki 链接在解析与打印两个阶段的底层实现正则 tokenizer 与mdast.js打印分支并能借助仓库内完整的测试矩阵快速验证与扩展行为。测试用例解读当 Wiki 链接溢出行尾关联文档 tests/format/markdown/wiki-link/end-of-line.md 全文仅两行 Markdown 输入If I have some markdown text, it should be wrapped properly at the character limit for markdown. However, if I have a link that overflows the end of line it should be [[wrapped as a single entity]] like this.这则用例要验证两件事普通散文在第一行超过printWidth默认 80时应按字符限制被正常换行wrapped properly at the character limitWiki 链接[[wrapped as a single entity]]即使溢出当前行尾也必须作为一个整体as a single entity被搬运到下一行而不是在[[与]]之间被拦腰折断。第二行的输入刻意让句子先占满接近 80 列的位置再把一个较长的 Wiki 链接推到行尾溢出——这正是真实文档中超长链接出现在段落末尾的典型场景。快照中的预期输出对应的 Jest 快照文件 tests/format/markdown/wiki-link/snapshots/format.test.js.snap 记录了四种选项组合下的格式化结果测试入口见 tests/format/markdown/wiki-link/format.test.js选项组合输出行为proseWrap: always两行散文按 80 列重新折行Wiki 链接整体换行到新行首链接内部不被拆分proseWrap: alwayssingleQuote: true与纯always完全一致singleQuote不参与 Markdown 换行决策proseWrap: never两段合并为一行全程不换行proseWrap: preserve输入保持原样不重新折行其中proseWrap: always的期望输出为If I have some markdown text, it should be wrapped properly at the character limit for markdown. However, if I have a link that overflows the end of line it should be [[wrapped as a single entity]] like this.注意观察前两行在接近 80 列处折行而第三行以should be开头随后是完整的[[wrapped as a single entity]]——链接整体被移动到新一行[[与]]之间没有任何折行点。proseWrap 选项三种模式的官方定义proseWrap是 Prettier 的通用选项定义在 src/common/common-options.evaluate.js 中Markdown 与 YAML 语言均引用它见 src/language-markdown/options.js 和 src/language-yaml/options.jsalways散文超出打印宽度时进行换行Wrap prose if it exceeds the print width.——本用例的主要验证对象never不对散文换行Do not wrap prose.所有内容保持单行preserve散文保持原样Wrap prose as-is.不重新折行。三者默认值为preserve。从快照可以确认never与preserve在本用例下都不会触碰 Wiki 链接而always是唯一会搬运整个链接换行的模式。命令行中的等价写法为# 与仓库测试等价的 CLI 用法 prettier --parser markdown --prose-wrap always end-of-line.md prettier --parser markdown --prose-wrap never end-of-line.md prettier --parser markdown --prose-wrap preserve end-of-line.md源码剖析Wiki 链接如何被解析为单一节点Wiki 链接之所以能整体换行根本原因在于它在解析阶段就被识别为一个不可分割的 AST 节点而不是一段普通文本。1. 解析插件 unified-plugins/wiki-link.jsPrettier 的 Markdown 解析基于 unified 生态Wiki 链接支持由专用插件 src/language-markdown/parse/unified-plugins/wiki-link.js 注入。核心逻辑如下const wikiLinkRegex /^\[\[(?linkContents.?)\]\]/s; // ... function tokenizer(eat, value) { const match wikiLinkRegex.exec(value); if (match) { const linkContents match.groups.linkContents.trim(); return eat(match[0])({ type: wikiLink, value: linkContents, }); } }关键点正则^\[\[(?linkContents.?)\]\]以非贪婪方式匹配[[与]]之间的所有内容/s标志使其可跨行匹配——这为多行 Wiki 链接见下文multi-line.md提供了基础插件通过methods.splice(methods.indexOf(link), 0, entityType)把自己的 tokenizer 插到标准linktokenizer 之前保证[[...]]优先被识别为wikiLink节点而非普通链接语法匹配成功后生成type: wikiLink的内联节点value为去除首尾空白后的链接内容。换行、空白都被收纳进这一个节点的 value 中后续打印阶段只需把整个节点当作一个整体输出。2. 打印阶段 mdast.js 的 wikiLink 分支AST 转文档doc的核心实现在 src/language-markdown/print/mdast.jswikiLink节点只有一个极简分支case wikiLink: { let contents; if (options.proseWrap preserve) { contents node.value; } else { contents node.value.replaceAll(/[\t\n]/g, ); } return [[[, contents, ]]]; }由此可以推断在preserve模式下链接内部原有的制表符/换行被原样保留对应快照中preserve输出与输入逐字一致在always/never模式下链接内部的多余空白\t、\n被统一压缩为单个空格但链接的[[前缀、内容、]]后缀始终拼接为一个连续字符串返回——这个字符串在 Prettier 的 doc 树中是不可断行的原子单元因此换行决策fill/line软折行只能发生在链接之前或之后永远无法发生在链接内部。这正是as a single entity的实现本质。3. 换行发生在哪里从快照看always模式下第三行输出为should be [[wrapped as a single entity]] like this.——链接被整体放到新行行首的should be与链接之间是允许折行的空白位置。这印证了换行点由周围的word单词与whitespace节点共同决定Wiki 链接作为一个整体参与fill布局一旦放不下就整体下沉到下一行。完整测试矩阵Wiki 链接行为的边界验证wiki-link目录下还有其他用例可与end-of-line.md互为补充共同勾勒 Prettier 对 Wiki 链接的处理边界simple.md单行简单链接[[A simple wiki link on a single line]]原样输出exceeds-line-length.md独立成段、自身超过 80 列的链接在always下也不被拆分整段保持单行exceeds-line-length-in-prose.md链接出现在散文句中且长度溢出时链接整体换行、内部不折exceeds-line-length-in-prose-broken.md输入中链接内部已存在手动换行时always模式下也会保留这种换行不会强行合并为一行multi-line.md覆盖 19 组多行链接边界——嵌套链接、转义反斜杠\\[[、HTML 注释与span包裹、引用块中的多行链接等extra-brackets.md 及其-leading/-trailing变体[[[与]]]这种多括号歧义输入在折行时的表现additional-spacing.md链接内部的多余空格在四种模式下均被原样保留alias/issue-19525.mdObsidian 风格的[[Foo:Bar]]、[[slug|Label]]别名写法在四种模式下保持稳定的回归用例对应 GitHub issue 19525。这些用例共享同一个 format.test.js它通过runFormatTest对每个.md输入依次执行proseWrap: always、always singleQuote、never、preserve四组格式化并比对快照。在仓库根目录运行# 只跑 wiki-link 相关用例 yarn jest tests/format/markdown/wiki-link # 或指定具体用例文件 yarn jest end-of-line.md新增行为或修改正则后可用yarn jest -u更新快照以确认影响面。实践建议写作阶段不需要手工规避长链接proseWrap: always下任何[[...]]都会自动整体换行你只需保证链接自身闭合正确尊重已有换行如果 Wiki 链接内部已经人为分行如exceeds-line-length-in-prose-broken.md所示Prettier 在always下不会强行合并这避免破坏 Obsidian 等工具对链接语义的依赖别名与嵌套需注意解析顺序[[slug|Label]]别名格式已被 alias/issue-19525.md 覆盖而[[[[...]]这类多括号场景存在歧义格式化结果以对应快照为准保留模式最稳妥对内容高度敏感、不希望任何空白被压缩的文档使用默认的preserve——此时链接内部空白与换行完全原样输出与always下的空白压缩行为形成鲜明对比见mdast.js中proseWrap preserve的分支判断。小结end-of-line.md虽只是一则两行的测试输入却精准锚定了 Prettier Markdown 格式化中一个关键设计Wiki 链接[[...]]是原子不可拆分的换行实体。解析层通过unified-plugins/wiki-link.js的正则 tokenizer 把链接收敛为单一wikiLink节点打印层通过mdast.js的拼接返回保证其不可拆分最终在fill布局中实现整链接换行。理解这条解析 → 打印 → 折行的完整链路即可准确预测任意 Wiki 链接在不同proseWrap配置下的格式化结果。【免费下载链接】prettierPrettier is an opinionated code formatter.项目地址: https://gitcode.com/gh_mirrors/pr/prettier创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门