flexmark-java是CommonMark(规范 0.28)解析器的 Java 实现,使用块优先、内联后 Markdown 解析架构。
它的优点是速度、灵活性、基于 Markdown 源元素的 AST,其中源位置的详细信息可以细化到构成元素的词位的各个字符以及可扩展性。
该 API 允许对解析过程进行精细控制,并针对解析大量已安装的扩展进行了优化。解析器和扩展为解析器行为和 HTML 渲染变化提供了大量选项。最终目标是让解析器和渲染器能够非常准确地模仿其他解析器。通过 Markdown 处理器仿真的实现,现在已部分完成
该项目的动机是需要替换 JetBrains IDE 的 Markdown Navigator 插件中的 pegdown 解析器。 pegdown 有一个很棒的功能集,但它的速度总体上不太理想,并且对于病态输入,在解析过程中要么挂起,要么几乎挂起。
适用于版本 0.62.2 或更低、Java 8 或更高版本、Java 9+ 兼容。对于版本 0.64.0 或更高版本、Java 11 或更高版本。
该项目位于 Maven 上: com.vladsch.flexmark
除了org.jetbrains:annotations:24.0.1
之外,核心没有任何依赖项。对于扩展,请参阅下面的扩展说明。
API 仍在不断发展以适应新的扩展和功能。
对于 Maven,将flexmark-all
添加为依赖项,其中包括以下示例的核心和所有模块:
<依赖关系> <groupId>com.vladsch.flexmark</groupId> <artifactId>flexmark-all</artifactId> <版本>0.64.8</版本> </依赖>
来源:BasicSample.java
包 com.vladsch.flexmark.samples;导入 com.vladsch.flexmark.util.ast.Node;导入 com.vladsch.flexmark.html.HtmlRenderer;导入 com.vladsch.flexmark.parser.Parser;导入 com.vladsch.flexmark .util.data.MutableDataSet;public class BasicSample { public static void main(String[] args) { MutableDataSet options = new可变数据集(); // 取消注释以设置可选扩展 //options.set(Parser.EXTENSIONS, Arrays.asList(TablesExtension.create(), StrikethroughExtension.create())); // 取消注释以将软中断转换为硬中断 //options.set(HtmlRenderer.SOFT_BREAK, "<br />n"); Parser parser = Parser.builder(options).build(); HtmlRenderer 渲染器 = HtmlRenderer.builder(options).build(); // 您可以重用解析器和渲染器实例 Node document = parser.parse("This is *Sparta*"); String html = renderer.render(document); // "<p>这是<em>斯巴达</em></p>n" System.out.println(html); } }
实施 'com.vladsch.flexmark:flexmark-all:0.64.8'
由于重复文件而导致的附加设置:
包装选项{ 排除“META-INF/LICENSE-LGPL-2.1.txt” 排除“META-INF/LICENSE-LGPL-3.txt” 排除“META-INF/LICENSE-W3C-TEST” 排除“META-INF/DEPENDENCIES”}
更多信息可以在文档中找到:
Wiki Home 使用示例 扩展详细信息 编写扩展
PegdownOptionsAdapter
类将 pegdown Extensions.*
标志转换为 Flexmark 选项和扩展列表。包含 Pegdown Extensions.java
是为了方便和 pegdown 1.6.0 中未找到的新选项。这些位于flexmark-profile-pegdown
模块中,但您可以从此存储库中获取源代码:PegdownOptionsAdapter.java、Extensions.java 并制作您自己的版本,并根据您的项目需求进行修改。
您可以将扩展标志传递给静态PegdownOptionsAdapter.flexmarkOptions(int)
,也可以实例化PegdownOptionsAdapter
并使用便捷方法来设置、添加和删除扩展标志。 PegdownOptionsAdapter.getFlexmarkOptions()
每次都会返回DataHolder
的新副本,其中的选项反映 pegdown 扩展标志。
导入 com.vladsch.flexmark.html.HtmlRenderer;导入 com.vladsch.flexmark.parser.Parser;导入 com.vladsch.flexmark.profile.pegdown.Extensions;导入 com.vladsch.flexmark.profile.pegdown.PegdownOptionsAdapter;导入 com .vladsch.flexmark.util.data.DataHolder;公共类PegdownOptions {最终私有静态DataHolder选项 = PegdownOptionsAdapter.flexmarkOptions( 扩展.ALL ); 静态最终解析器 PARSER = Parser.builder(OPTIONS).build(); 静态最终 HtmlRenderer RENDERER = HtmlRenderer.builder(OPTIONS).build(); // 使用 PARSER 进行解析并使用 RENDERER 进行渲染,并具有 pegdown 兼容性}
默认的 flexmark-java pegdown 模拟使用不太严格的 HTML 块解析,它会中断空行上的 HTML 块。如果 HTML 块中的所有标记都已关闭,则 Pegdown 仅会在空行上中断 HTML 块。
为了更接近原始的 pegdown HTML 块解析行为,请使用采用boolean strictHtml
参数的方法:
导入 com.vladsch.flexmark.html.HtmlRenderer;导入 com.vladsch.flexmark.parser.Parser;导入 com.vladsch.flexmark.profile.pegdown.Extensions;导入 com.vladsch.flexmark.profile.pegdown.PegdownOptionsAdapter;导入 com .vladsch.flexmark.util.data.DataHolder;公共类PegdownOptions {最终私有静态DataHolder选项 = PegdownOptionsAdapter.flexmarkOptions(true, Extensions.ALL ); 静态最终解析器 PARSER = Parser.builder(OPTIONS).build(); 静态最终 HtmlRenderer RENDERER = HtmlRenderer.builder(OPTIONS).build(); // 使用 PARSER 进行解析并使用 RENDERER 进行渲染,并具有 pegdown 兼容性}
还提供了带有自定义链接解析器的示例,其中包括用于更改 URL 或链接属性的链接解析器以及自定义节点呈现器(如果您需要覆盖生成的链接 HTML)。
除了 pegdown 1.6.0 中提供的扩展之外,flexmark-java 还比 pegdown 更多的扩展和配置选项。通过 PegdownOptionsAdapter 提供的可用扩展
版本 0.60.0 中实现的主要重组和代码清理,请参阅 Version-0.60.0-Changes,感谢 Alex Karezin 的出色工作,您可以概述模块依赖关系,并能够深入到包和类。
合并 API 将多个 Markdown 文档合并为一个文档。
Docx 渲染器扩展:有限属性节点处理
可扩展的 HTML 到 Markdown 转换器模块:flexmark-html2md-converter。示例:HtmlToMarkdownCustomizedSample.java
Java9+模块兼容性
复合枚举引用 枚举引用扩展,用于为元素和标题创建合法编号。
宏扩展允许将任意 Markdown 内容作为块或内联元素插入,从而允许在语法仅允许内联元素的情况下使用块元素。
GitLab Flavored Markdown 用于解析和渲染 GitLab markdown 扩展。
OSGi 模块由 Dan Klco (GitHub @klcodanr) 提供
媒体标签 媒体链接转换器扩展由 Cornelia Schultz (GitHub @CorneliaXaos) 提供,使用自定义前缀将链接转换为音频、嵌入、图片和视频 HTML5 标签。
Translation Helper API 使翻译 Markdown 文档变得更加容易。
警告 创建块样式的侧面内容。有关完整文档,请参阅 Admonition Extension、MkDocs 文档材料。
枚举引用为图形、表格和其他 Markdown 元素创建枚举引用。
用于解析{name name=value name='value' name="value" #id .class-name}
属性形式的属性的属性。
YouTube 嵌入式链接转换器感谢 Vyacheslav N. Boyko (GitHub @bvn13) 将 YouTube 视频的简单链接转换为嵌入式视频 iframe HTML。
使用 docx4j 库的 Docx 转换器。如何使用:DocxConverter Sample,如何定制:自定义Docx渲染
该模块的开发由 Johner Institut GmbH 赞助。
将库更新为兼容 CommonMark(规范 0.28)并添加ParserEmulationProfile.COMMONMARK_0_27
和ParserEmulationProfile.COMMONMARK_0_28
以允许选择特定规范版本选项。
自定义节点渲染 API 能够为被覆盖的节点调用标准渲染,允许自定义节点渲染仅处理特殊情况,并让其余部分照常渲染。自定义链接解析器
Gfm-Issues 和 Gfm-Users 扩展分别用于解析和渲染#123
和@user-name
。
深度 HTML 块解析选项,可以更好地处理其他标签之后的原始文本标签,并实现 pegdown HTML 块解析兼容性。
flexmark-all
模块包括:核心、所有扩展、格式化程序、JIRA 和 YouTrack 转换器、pegdown 配置文件模块以及 HTML 到 Markdown 转换。
PDF 输出模块 使用 Open HTML To PDF 进行 PDF 输出
印刷实施
XWiki 宏扩展
杰基尔标签
Html 转 Markdown
Maven Markdown 页面生成器插件
Markdown Formatter 模块将 AST 输出为带有格式选项的 Markdown。
Markdown 格式化程序的表格,具有列宽和 Markdown 表格对齐方式:
输入 | 输出 |
---|---|
day|time|spent :---|:---:|--: nov. 2. tue|10:00|4h 40m nov. 3. thu|11:00|4h nov. 7. mon|10:20|4h 20m total:|| 13h | | day | time | spent | |:------------|:-----:|--------:| | nov. 2. tue | 10:00 | 4h 40m | | nov. 3. thu | 11:00 | 4h | | nov. 7. mon | 10:20 | 4h 20m | | total: || 13h | |
我使用 flexmark-java 作为 JetBrains IDE 的 Markdown Navigator 插件的解析器。我倾向于使用最新的、未发布的版本来修复错误或获得改进。因此,如果您发现一个对您的项目造成阻碍的错误,或者在 github 问题页面中看到一个标记fixed for next release
错误正在影响您的项目,那么请告诉我,我也许能够及时发布新版本来解决你的问题。否则,我会让错误修复和增强功能不断积累,认为没有人会受到已修复内容的影响。
API 中有许多扩展选项及其预期用途。一个很好的软启动是flexmark-java-samples
模块,它具有用于要求扩展的简单示例。下一个最佳位置是现有扩展的源,该扩展的语法与您要添加的扩展类似。
如果您的扩展程序与正确的 API 相匹配,那么任务通常会非常简短且顺利。如果您的扩展程序以非预期的方式使用 API 或者不遵循预期的内务管理协议,您可能会发现这是一场艰苦的战斗,与 if/else 条件处理和修复一个错误只会导致创建另一个错误的老鼠巢。
一般来说,如果添加一个简单的扩展需要超过几十行,那么要么是你的操作错误,要么是 API 缺少扩展点。如果您查看所有已实现的扩展,您会发现大多数都是 API 规定的样板代码之外的几行代码。这就是这个库的目标:提供一个可扩展的核心,使编写扩展变得轻而易举。
较大的扩展是flexmark-ext-tables
和flexmark-ext-spec-example
,两者的核心都是大约 200 行代码。您可以将它们用作估计扩展尺寸的指南柱。
我自己添加扩展的经验表明,有时新型扩展最好通过 API 增强来解决,以使其实现无缝,或者通过修复在扩展以正确的方式强调 API 之前不可见的错误。您想要的扩展可能正是需要这种方法的扩展。
结论是:如果您想实现扩展或功能,请随时提出问题,我将为您提供最佳方法的指导。在您投入大量徒劳的努力之前,让我改进 API 来满足您的扩展需求,可能会为您节省大量时间。
我确实要求你意识到我是这个项目的首席厨师和洗瓶工,没有一点瓦肯心灵融合技能。我确实要求您描述您想要实现的内容,因为我无法读懂您的想法。请围绕源代码和文档做一些背景调查工作,因为如果没有您的自愿努力,我无法将我所知道的内容转移给您。
如果您有商业应用程序,并且不想自己编写扩展,或者希望减少实现扩展和集成 flexmark-java 的时间和精力,请随时与我联系。我可以提供咨询/承包服务。
尽管有它的名字,commonmark 既不是其他 Markdown 风格的超集也不是子集。相反,它为原始“核心”Markdown 提出了一个标准的、明确的语法规范,从而有效地引入了另一种风格。虽然 Flexmark 默认情况下兼容 CommonMark,但它的解析器可以通过各种方式进行调整。模拟最常用的 Markdown 解析器所需的调整集可在 Flexmark 中作为ParserEmulationProfiles
获得。
正如名称ParserEmulationProfile
所暗示的那样,只有解析器会根据特定的 Markdown 风格进行调整。应用配置文件不会添加 commonmark 中可用的功能之外的功能。如果您想使用 Flexmark 完全模拟另一个 Markdown 处理器的行为,则必须调整解析器并配置 Flexmark 扩展,以提供您想要模拟的解析器中可用的附加功能。
根据 Markdown 处理器仿真,重写列表解析器以更好地控制其他 Markdown 处理器的仿真已完成。添加处理器预设来模拟这些解析器的特定 Markdown 处理行为是一个简短的待办事项列表。
一些模拟家族在模拟目标方面比其他家族做得更好。大部分工作都是针对模拟这些处理器如何解析标准 Markdown 并具体列出相关解析。对于扩展原始 Markdown 的处理器,您需要将那些已在 flexmark-java 中实现的扩展添加到解析器/渲染器构建器选项中。
如果该处理器实现了等效的扩展,则扩展将被修改为包括其自己针对特定处理器模拟的预设。
如果您发现差异,请提出问题以便解决。
主要处理器系列已实现,一些系列成员还:
杰基尔
CommonMark 用于最新实施的规范,目前为 CommonMark(规范 0.28)
联盟/CommonMark
CommonMark(规范 0.27)用于特定版本兼容性
CommonMark(规范 0.28)用于特定版本兼容性
GitHub 评论
Markdown.pl
PHP Markdown 额外
GitHub Docs(旧的 GitHub markdown 解析器)
克拉姆当
固定缩进
多重降价
钉住
0.11.0 中添加了用于封装系列内变体的配置详细信息的配置文件:
CommonMark(系列默认): ParserEmulationProfile.COMMONMARK
固定缩进(系列默认值): ParserEmulationProfile.FIXED_INDENT
GitHub 评论(仅 CommonMark): ParserEmulationProfile.COMMONMARK
旧 GitHub 文档: ParserEmulationProfile.GITHUB_DOC
Kramdown(系列默认值): ParserEmulationProfile.KRAMDOWN
Markdown.pl(系列默认): ParserEmulationProfile.MARKDOWN
MultiMarkdown: ParserEmulationProfile.MULTI_MARKDOWN
Pegdown,带有 pegdown 扩展,在flexmark-profile-pegdown
中使用PegdownOptionsAdapter
Pegdown,不带 pegdown 扩展ParserEmulationProfile.PEGDOWN
Pegdown HTML 块解析规则,不带 pegdown 扩展ParserEmulationProfile.PEGDOWN_STRICT
flexmark-java是 commonmark-java 项目的一个分支,经过修改可生成反映原始源中所有元素的 AST、对 AST 中所有元素的完整源位置跟踪以及更轻松的 JetBrains Open API PsiTree 生成。
API 进行了更改,以允许对解析过程进行更精细的控制,并针对大量已安装的扩展进行解析进行了优化。解析器和扩展附带了许多针对解析器行为和 HTML 渲染变化的调整选项。最终目标是让解析器和渲染器能够非常准确地模仿其他解析器。
这样做的动机是需要替换 Markdown Navigator 插件中的 pegdown 解析器。 pegdown 有一个很棒的功能集,但它的速度总体上不太理想,并且对于病态输入,在解析过程中要么挂起,要么几乎挂起。
commonmark-java拥有优秀的解析架构,易于理解和扩展。目标是确保在 AST 中添加源位置跟踪不会超过绝对必要地改变解析和生成 AST 的简易性。
选择commonmark-java作为解析器的原因是:速度、易于理解、易于扩展和速度。现在我已经重新设计了核心并添加了一些扩展,我对我的选择非常满意。
另一个目标是提高扩展修改解析器行为的能力,以便任何 Markdown 方言都可以通过扩展机制实现。添加了可扩展选项 API,以允许在一个位置设置所有选项。解析器、渲染器和扩展使用这些选项进行配置,包括禁用一些核心块解析器。
这是一项正在进行的工作,有许多 API 更改。在功能集基本完成之前,不会尝试保持对原始项目的向后 API 兼容性,甚至不会对该项目的早期版本保持向后兼容性。
特征 | Flexmark-java | commonmark-java | 挂钩 |
---|---|---|---|
相对解析时间(越少越好) | 1x(1) | 0.6x 至 0.7x (2) | 25 倍平均值,20,000 倍至 ∞ 病理输入 (3) |
AST 中的所有源元素 | |||
具有源位置的 AST 元素 | 有一些错误和特质 | ||
AST 很容易操纵 | AST后处理是一种扩展机制 | AST后处理是一种扩展机制 | 不是一个选择。没有节点的父节点信息,子节点为List<>。 |
AST 元素具有所有部分的详细源位置 | 仅节点开始/结束 | ||
可以禁用核心解析功能 | |||
通过扩展 API 实现的核心解析器 | 特定块解析器和节点类的instanceOf 测试 | 核心暴露了很少的扩展点 | |
易于理解和修改解析器实现 | 一个具有复杂交互作用的 PEG 解析器 (3) | ||
块元素的解析是相互独立的 | 一切尽在一个 PEG 语法中 | ||
跨解析器、渲染器和所有扩展的统一配置 | 扩展列表之外没有任何内容 | 核心的int 位标志,扩展没有 | |
解析性能针对扩展使用进行了优化 | 解析核心性能,扩展尽其所能 | 性能不是一个特征 | |
功能丰富,有许多开箱即用的配置选项和扩展 | 扩展有限,没有选项 | ||
处理器的依赖性定义以保证正确的处理顺序 | 由扩展列表排序指定的顺序,容易出错 | 不适用,核心定义了添加扩展处理的位置 |
flexmark-java 病态输入 100,000 个[
在 68 毫秒内解析,100,000 个]
在 57 毫秒内解析,100,000 个嵌套[
]
在 55 毫秒内解析
commonmark-java 病态输入 100,000 个[
在 30 毫秒内解析,100,000 个]
在 30 毫秒内解析,100,000 个嵌套[
]
在 43 毫秒内解析
pegdown 病理输入 17 [
在 650ms 内解析,18 [
在 1300ms 内解析
解析器选项,标记为任务项的项目将被实现,其余部分已完成:
手动松散清单
编号列表始终以 1 开头。
修复列表项缩进,项目必须缩进至少 4 个空格
宽松的列表开始选项,允许列表在前面没有空行时开始。
杰基尔前线问题
Jekyll 标签元素,支持{% include file %}
,包含 Markdown 和 HTML 文件内容
GitBook 链接 URL 编码。不适用
HTML 注释节点:块和内联
多行图像 URL
规格示例元素
内联 HTML:全部、非注释、注释
HTML 块:全部、非注释、注释
缩写
脚注
定义
目录
删除线
任务列表
无 Atx 接头空间
没有标题缩进
硬包裹(通过将 SOFT_BREAK 选项更改为"<br />"
来实现)
宽松的人力资源规则选项
维基链接
围栏代码块
具有自动 ID 生成功能的标头锚链接
表跨度选项用于表扩展
带有 GitHub 和 Creole 语法的 Wiki 链接
使用 GitHub 表情符号 URL 选项的表情符号快捷方式
引号
聪明人
排版
GitHub 扩展
GitHub 语法
出版
压制
处理器扩展
Commonmark 语法抑制
我很高兴决定为我自己的项目切换到基于 commonmark-java 的解析器。尽管我必须对其内部进行重大手术才能获得完整的源位置跟踪和匹配源元素的 AST,但使用它很愉快,现在扩展也很愉快。如果您不需要源级元素 AST 或 Flexmark-java 添加的其余内容,并且 CommonMark 是您的目标 Markdown 解析器,那么我鼓励您使用 commonmark-java,因为它是满足您需求的绝佳选择,并且其性能不会受到影响用于您不会使用的功能的开销。
最新,2017 年 1 月 28 日 flexmark-java 0.13.1,来自 CE EAP 2017 的 intellij-markdown,commonmark-java 0.8.0:
文件 | commonmark-java | Flexmark-java | intellij-markdown | 挂钩 |
---|---|---|---|---|
自述文件慢 | 0.420毫秒 | 0.812毫秒 | 2.027毫秒 | 15.483毫秒 |
版本 | 0.743毫秒 | 1.425毫秒 | 4.057毫秒 | 42.936毫秒 |
通用标记规范 | 31.025毫秒 | 44.465毫秒 | 600.654毫秒 | 575.131毫秒 |
降价示例 | 8.490毫秒 | 10.502毫秒 | 223.593毫秒 | 983.640毫秒 |
规格 | 4.719毫秒 | 6.249毫秒 | 35.883毫秒 | 307.176毫秒 |
桌子 | 0.229毫秒 | 0.623毫秒 | 0.800毫秒 | 3.642毫秒 |
表格格式 | 1.385毫秒 | 2.881毫秒 | 4.150毫秒 | 23.592毫秒 |
裹 | 3.804毫秒 | 4.589毫秒 | 16.609毫秒 | 86.383毫秒 |
上述比例:
文件 | commonmark-java | Flexmark-java | intellij-markdown | 挂钩 |
---|---|---|---|---|
自述文件慢 | 1.00 | 1.93 | 4.83 | 36.88 |
版本 | 1.00 | 1.92 | 5.46 | 57.78 |
通用标记规范 | 1.00 | 1.43 | 19.36 | 18.54 |
降价示例 | 1.00 | 1.24 | 26.34 | 115.86 |
规格 | 1.00 | 1.32 | 7.60 | 65.09 |
桌子 | 1.00 | 2.72 | 3.49 | 15.90 |
表格格式 | 1.00 | 2.08 | 3.00 | 17.03 |
裹 | 1.00 | 1.21 | 4.37 | 22.71 |
全面的 | 1.00 | 1.41 | 17.47 | 40.11 |
文件 | commonmark-java | Flexmark-java | intellij-markdown | 挂钩 |
---|---|---|---|---|
自述文件慢 | 0.52 | 1.00 | 2.50 | 19.07 |
版本 | 0.52 | 1.00 | 2.85 | 30.12 |
通用标记规格 | 0.70 | 1.00 | 13.51 | 12.93 |
降价示例 | 0.81 | 1.00 | 21.29 | 93.66 |
规格 | 0.76 | 1.00 | 5.74 | 49.15 |
桌子 | 0.37 | 1.00 | 1.28 | 5.85 |
表格格式 | 0.48 | 1.00 | 1.44 | 8.19 |
裹 | 0.83 | 1.00 | 3.62 | 18.83 |
全面的 | 0.71 | 1.00 | 12.41 | 28.48 |
因为这两个文件代表了 pegdown 的病态输入,所以我不再将它们作为基准测试的一部分来运行,以防止结果出现偏差。结果供后代参考。
文件 | commonmark-java | Flexmark-java | intellij-markdown | 挂钩 |
---|---|---|---|---|
悬挂挂钩 | 0.082毫秒 | 0.326毫秒 | 0.342毫秒 | 659.138毫秒 |
悬挂挂钩2 | 0.048毫秒 | 0.235毫秒 | 0.198毫秒 | 1312.944毫秒 |
上述比例:
文件 | commonmark-java | Flexmark-java | intellij-markdown | 挂钩 |
---|---|---|---|---|
悬挂挂钩 | 1.00 | 3.98 | 4.17 | 8048.38 |
悬挂挂钩2 | 1.00 | 4.86 | 4.10 | 27207.32 |
全面的 | 1.00 | 4.30 | 4.15 | 15151.91 |
文件 | commonmark-java | Flexmark-java | intellij-markdown | 挂钩 |
---|---|---|---|---|
悬挂挂钩 | 0.25 | 1.00 | 1.05 | 2024.27 |
悬挂挂钩2 | 0.21 | 1.00 | 0.84 | 5594.73 |
全面的 | 0.23 | 1.00 | 0.96 | 3519.73 |
VERSION.md 是我用于 Markdown Navigator 的版本日志文件
commonMarkSpec.md 是 intellij-markdown 测试套件中用于性能评估的 33k 行文件。
commonmark-java 项目中的 spec.txt commonmark 规范 markdown 文件
hang-pegdown.md 是一个包含单行 17 个字符的文件[[[[[[[[[[[[[[[[[
这会导致 pegdown 进入超指数解析时间。
hang-pegdown2.md 一个包含单行 18 个字符的文件[[[[[[[[[[[[[[[[[[
这会导致 pegdown 进入超指数解析时间。
wrap.md 是我用来测试打字换行性能的文件,结果发现当 pegdown 花费 0.1 秒来解析文件时,它与打字代码换行无关。在插件中,解析可能会发生多次:语法荧光笔传递、psi 树构建传递、外部注释器。
markdown_example.md 一个包含 10,000 多行、包含 500kB 以上文本的文件。
欢迎请求请求、问题和评论。对于拉取请求:
添加新功能和错误修复的测试,最好采用 ast_spec.md 格式
遵循现有样式,尽可能使合并更容易:缩进 4 个空格,修剪尾随空格。
版权所有 (c) 2015-2016 Atlassian 等。
版权所有 (c) 2016-2023,弗拉基米尔·施耐德,
BSD(2-clause)许可,请参阅 LICENSE.txt 文件。