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),仅供参考

相关新闻

WordPress子比主题超级插件:AI驱动的高效功能整合方案

WordPress子比主题超级插件:AI驱动的高效功能整合方案

1. 项目概述:子比主题超级插件的核心价值子比主题超级插件是一款专为WordPress网站设计的全能型功能扩展集合。作为一名在WordPress生态深耕多年的开发者,我亲测过市面上绝大多数主题插件,但像这样将AI功能与十余种实用模块深度整合的解决方案…

2026/9/19 7:57:34 阅读更多 →
Blender+Unity构建工业数字孪生产线:全流程实操与避坑指南

Blender+Unity构建工业数字孪生产线:全流程实操与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/19 7:56:33 阅读更多 →
基于Jetson Nano与YOLOv5s的无人机道路抛洒物实时检测系统

基于Jetson Nano与YOLOv5s的无人机道路抛洒物实时检测系统

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/19 7:56:33 阅读更多 →

最新新闻

Python+Vue开发宠物领养寄养商城系统实战

Python+Vue开发宠物领养寄养商城系统实战

1. 项目概述宠物之家领养寄养救助商城管理系统是一个基于Python后端与Vue前端开发的综合性宠物服务平台。这个系统将传统的宠物服务与电子商务功能相结合,为宠物爱好者、救助机构和商家提供了一个完整的数字化解决方案。我在实际开发这类系统时发现,宠物…

2026/9/19 8:53:00 阅读更多 →
代码审查自动化实践:从“走流程”到“有据可依”

代码审查自动化实践:从“走流程”到“有据可依”

1. 先聊聊我为什么对“走流程”的代码审查越来越不耐烦入行头几年,我一直觉得 code review 是件挺神圣的事。那时候组里人少,每次提 MR 之前都会自己反复看两三遍,review 的人也真会逐行读,揪出来的问题从逻辑漏洞到命名风格都有。…

2026/9/19 8:53:00 阅读更多 →
智能Agent记忆压缩机制设计与实现

智能Agent记忆压缩机制设计与实现

1. 项目概述最近在准备腾讯技术岗面试时,被问到了一个很有意思的问题:"如何设计一个高效的Agent记忆压缩机制?"这个问题让我意识到,虽然很多开发者都在使用各种Agent框架,但对于其核心的记忆管理机制却知之甚…

2026/9/19 8:53:00 阅读更多 →
Python+Django+Vue智慧农场系统开发实战

Python+Django+Vue智慧农场系统开发实战

1. 项目概述智慧农场管理系统是当前农业数字化转型的核心解决方案。作为一名深耕农业科技领域多年的开发者,我见证了传统农场如何通过技术手段实现质的飞跃。这个基于PythonDjangoVueMySQL的智慧农场项目,正是我在实际农业生产场景中反复验证过的成熟方案…

2026/9/19 8:53:00 阅读更多 →
CANN Runtime API Hook 接口解析:基于 aclrtApiInjectionSetFunc / aclrtApiInjectionGetFunc 的 Profiling 拦截机制

CANN Runtime API Hook 接口解析:基于 aclrtApiInjectionSetFunc / aclrtApiInjectionGetFunc 的 Profiling 拦截机制

CANN Runtime API Hook 接口解析:基于 aclrtApiInjectionSetFunc / aclrtApiInjectionGetFunc 的 Profiling 拦截机制 【免费下载链接】runtime 本项目提供CANN运行时组件和维测功能组件。 项目地址: https://gitcode.com/cann/runtime API Hook 是 CANN Run…

2026/9/19 8:53:00 阅读更多 →
基于残差信号与混合诊断的PMSM匝间短路故障识别

基于残差信号与混合诊断的PMSM匝间短路故障识别

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/19 8:52:00 阅读更多 →

日新闻

BP神经网络时序预测:滑窗长度与多窗口平均策略

BP神经网络时序预测:滑窗长度与多窗口平均策略

简介:面向机器学习、深度学习与数据建模学习者的一份完整研究文献,聚焦BP神经网络在农业产量预测中的应用。文档以1980—2018年全国棉花产量为样本,系统讲解数据归一化处理、激活函数原理、多层神经网络结构搭建及训练流程,展示敏…

2026/9/19 0:00:30 阅读更多 →
Transformer训练实时监控实战:基于MindSpore的损失曲线可视化方案

Transformer训练实时监控实战:基于MindSpore的损失曲线可视化方案

上个月调一个Deformable DETR模型,在单卡上要跑将近两天。第二天早上我下意识打开终端翻日志,发现loss从凌晨两点就开始往上爬,一路从0.8涨到1.35,整整六个小时没人发现。那六个小时的训练不仅白跑,还霸占着卡——等于…

2026/9/19 0:00:30 阅读更多 →
OpenCloud 中的 Go 类型安全转换库 spf13/cast:从零值回退到泛型 API 的完整实战指南

OpenCloud 中的 Go 类型安全转换库 spf13/cast:从零值回退到泛型 API 的完整实战指南

OpenCloud 中的 Go 类型安全转换库 spf13/cast:从零值回退到泛型 API 的完整实战指南 【免费下载链接】opencloud 🌤️ OpenCloud is the open source platform for file management, sharing and collaboration. Simple and sovereign. 项目地址: htt…

2026/9/19 0:00:30 阅读更多 →

周新闻

AI SDK Harness 依赖更新指南:掌握 harness 包 SDK 依赖的升级、桥接同步与一致性校验

AI SDK Harness 依赖更新指南:掌握 harness 包 SDK 依赖的升级、桥接同步与一致性校验

AI SDK Harness 依赖更新指南:掌握 harness 包 SDK 依赖的升级、桥接同步与一致性校验 【免费下载链接】ai The AI Toolkit for TypeScript. From the creators of Next.js, the AI SDK is a free open-source library for building AI-powered applications and ag…

2026/9/19 3:59:36 阅读更多 →
Refine v5 Ant Design NumberField 组件实战:基于 Intl 的本地化数字格式化

Refine v5 Ant Design NumberField 组件实战:基于 Intl 的本地化数字格式化

Refine v5 Ant Design NumberField 组件实战:基于 Intl 的本地化数字格式化 【免费下载链接】refine A React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility. 项目地址: https://gitcode.com/GitH…

2026/9/19 3:53:08 阅读更多 →
Flutter应用改名全指南:从Android到iOS的配置与工具实践

Flutter应用改名全指南:从Android到iOS的配置与工具实践

刚接一个外包项目时,甲方要求把工程里临时用的应用名改成正式产品名。我本来觉得“改名”这种小事,打开配置文件改一行不就完了?结果真动手才发现,Flutter项目里“应用名称”根本不是一处配置,而是一整套散落在 Androi…

2026/9/19 4:02:43 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/16 22:31:27 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/15 21:39:18 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/16 22:32:59 阅读更多 →