flexmark-java

flexmark-java 是一个基于 [CommonMark(规范 0.28)] 的 Java 实现,采用“先解析块(blocks),再解析行内(inlines)”的 Markdown 解析架构。

其优势在于:速度快、灵活性高、基于 Markdown 源元素的 AST(抽象语法树),并且提供精确到组成元素词法单元(lexeme)中每个字符的源码位置信息,同时具有良好的可扩展性。

该 API 提供对解析过程的细粒度控制,并针对安装大量扩展的场景进行了优化。解析器和扩展提供了大量用于控制解析行为和 HTML 渲染方式的选项。最终目标是让解析器和渲染器能够高度精确地模拟其他 Markdown 解析器的行为。目前这一目标已通过实现 Markdown 处理器模拟(Markdown Processor Emulation) 部分达成。

该项目的动机是替换我在 JetBrains IDE 的 [Markdown Navigator] 插件中使用的 [pegdown] 解析器。[pegdown] 功能丰富,但整体性能不理想,并且在病态输入(pathological input)情况下可能卡死或几乎卡死。

:warning: 版本 0.60.0 由于实现类的重构、重命名、清理和优化,包含破坏性变更。详见 Version-0.60.0-Changes


最新版本

Maven Central status Javadocs


要求

  • 对于 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

<dependency>
    <groupId>com.vladsch.flexmark</groupId>
    <artifactId>flexmark-all</artifactId>
    <version>0.64.8</version>
</dependency>

示例代码来源:BasicSample.java

package com.vladsch.flexmark.samples;

import com.vladsch.flexmark.util.ast.Node;
import com.vladsch.flexmark.html.HtmlRenderer;
import com.vladsch.flexmark.parser.Parser;
import com.vladsch.flexmark.util.data.MutableDataSet;

public class BasicSample {
    public static void main(String[] args) {
        MutableDataSet options = new MutableDataSet();

        // 可选:启用扩展
        //options.set(Parser.EXTENSIONS, Arrays.asList(TablesExtension.create(), StrikethroughExtension.create()));

        // 可选:将软换行转为硬换行
        //options.set(HtmlRenderer.SOFT_BREAK, "<br />\n");

        Parser parser = Parser.builder(options).build();
        HtmlRenderer renderer = HtmlRenderer.builder(options).build();

        // parser 和 renderer 可复用
        Node document = parser.parse("This is *Sparta*");
        String html = renderer.render(document);  // "<p>This is <em>Sparta</em></p>\n"
        System.out.println(html);
    }
}

Gradle 构建

implementation 'com.vladsch.flexmark:flexmark-all:0.64.8'

Android Studio 构建

由于存在重复文件,需要额外配置:

packagingOptions {
    exclude 'META-INF/LICENSE-LGPL-2.1.txt'
    exclude 'META-INF/LICENSE-LGPL-3.txt'
    exclude 'META-INF/LICENSE-W3C-TEST'
    exclude 'META-INF/DEPENDENCIES'
}

更多信息参见文档: Wiki 首页 / 使用示例 / 扩展说明 / 扩展开发


Pegdown 迁移辅助

PegdownOptionsAdapter 可将 pegdown 的 Extensions.* 标志转换为 flexmark 的配置和扩展列表。

使用方式:

final private static DataHolder OPTIONS =
    PegdownOptionsAdapter.flexmarkOptions(Extensions.ALL);

或启用严格 HTML 解析:

PegdownOptionsAdapter.flexmarkOptions(true, Extensions.ALL);

说明: 默认的 flexmark pegdown 模拟对 HTML 块的解析较宽松,而 pegdown 仅在标签全部闭合时才中断 HTML 块。


最新功能与改进(节选)

  • 0.60.0 版本重大重构
  • Markdown 合并 API
  • Docx 渲染扩展
  • HTML → Markdown 转换模块
  • Java 9+ 模块支持
  • 宏(Macros)扩展
  • GitLab Markdown 支持
  • 媒体标签扩展(音频/视频等)
  • 翻译辅助 API
  • Admonition 提示块
  • 枚举引用(图表编号)
  • 属性解析 {key=value}
  • YouTube 嵌入转换
  • Docx 转换(基于 docx4j)
  • PDF 输出(基于 Open HTML To PDF)
  • Typographic 排版支持
  • Markdown 格式化器

扩展机制说明

API 提供大量扩展点。一般原则:

  • 简单扩展应只需几十行代码
  • 如果实现复杂,可能使用方式不正确或 API 需增强
  • 大多数扩展代码量较小(约几十行)
  • 较大扩展约 200 行(如 tables)

如果无法实现,建议直接提 issue。


Markdown 处理器模拟

CommonMark 并不是其他 Markdown 方言的超集或子集,而是定义了一个标准语法。

flexmark 默认兼容 CommonMark,但可以通过 ParserEmulationProfile 模拟其他解析器:

支持:

  • CommonMark(0.27 / 0.28)
  • Markdown.pl
  • Kramdown
  • MultiMarkdown
  • Pegdown
  • GitHub Markdown(部分)

注意: 仅调整解析行为,不会自动增加额外特性,需要手动启用对应扩展。


历史与动机

flexmark-java 是 [commonmark-java] 的一个分支,主要增强:

  • AST 完整反映源文本结构
  • 完整源码位置跟踪
  • 更适合 JetBrains PSI 构建

选择 commonmark-java 的原因:

  • 高性能
  • 易理解
  • 易扩展

目标:

  • 支持任意 Markdown 方言扩展
  • 提供统一配置 API
  • 支持禁用核心解析器

当前仍在持续演进,不保证向后兼容。


功能对比

特性 flexmark-java commonmark-java pegdown
解析性能 ✔(更快)
AST 完整性
AST 源位置 ✔(不稳定)
AST 可修改
可禁用核心解析
扩展能力 一般 较弱
配置统一性

进展(部分)

  • 排版(引号、智能符号)
  • GitHub 扩展(表格、任务列表等)
  • 发布功能(脚注、目录等)
  • HTML 抑制控制
  • Jekyll 支持
  • 列表解析优化

基准测试

总体性能(相对 flexmark):

实现 性能
commonmark-java 0.71x(更快)
flexmark-java 1.00x
intellij-markdown 12.41x
pegdown 28.48x

在病态输入下:

  • flexmark:稳定
  • pegdown:指数级退化甚至卡死

贡献

欢迎 PR / Issue / 评论:

要求:

  • 添加测试
  • 遵循代码风格(4 空格缩进等)

许可证

Copyright (c) 2015–2016 Atlassian Copyright (c) 2016–2023 Vladimir Schneider

BSD 2-Clause License(见 LICENSE.txt)

参考资料