1 个不稳定版本

0.5.2 2019年6月9日

1037文本处理

MIT 许可证

315KB
7.5K SLoC

pulldown-cmark

Build Status Docs Crates.io

文档

这个库是 CommonMark 的解析器,用 CommonMark 写成,用 Rust。它附带一个简单的命令行工具,用于将内容渲染为 HTML,同时也易于作为库使用。

它被设计成:

  • 快速;分配和复制量最少
  • 安全;完全用 Rust 编写,没有不安全代码块
  • 灵活;特别是支持源映射
  • 正确;目标是100%符合 CommonMark 规范

此外,它还支持可选解析脚注、GitHub 风格表格任务列表扩展删除线扩展

为什么是拉解析器?

目前有许多 Markdown 和其变体的解析器,但据我所知,没有一个是使用拉解析器的。拉解析器在 XML 中变得流行,特别是在对内存敏感的应用程序中,因为它使用的内存比构建文档树要少得多。然而,拉解析器比推解析器更容易使用,而推解析器因其需要在一系列回调中巧妙地处理状态而臭名昭著,并且容易出现错误。

在设计良好的系统中,解析和渲染阶段是清晰分开的,但为了性能和便捷性,这往往被牺牲。许多 Markdown 实现将解析和渲染混合在一起,而尝试分离它们的设计(如流行的 hoedown)则假设渲染过程可以完全表示为一个序列化的字符串。

拉取解析在某种程度上是最通用的架构。它可以驱动推送接口,同时内存消耗最小,构建抽象语法树(AST)也相当直接。另一个优点是源映射信息(解析块与源文本中的偏移量之间的映射) readily available;你可以通过调用 into_offset_iter() 来创建一个迭代器,它会产生 (Event, Range) 对,其中第二个元素是源文档中事件对应的范围。

虽然操作 AST 是转换文档最灵活的方式,但操作迭代器却出奇地简单且高效。例如,这里是将软换行符转换为硬换行符的代码

let parser = parser.map(|event| match event {
	Event::SoftBreak => Event::HardBreak,
	_ => event
});

或者在文本中展开缩写

let parser = parser.map(|event| match event {
	Event::Text(text) => Event::Text(text.replace("abbr", "abbreviation").into()),
	_ => event
});

另一个简单的例子是确定最大嵌套级别的代码

let mut max_nesting = 0;
let mut level = 0;
for event in parser {
	match event {
		Event::Start(_) => {
			level += 1;
			max_nesting = std::cmp::max(max_nesting, level);
		}
		Event::End(_) => level -= 1,
		_ => ()
	}
}

在当前仓库的 examples 目录中有一些使用该 crate 的基本但功能齐全的示例。

使用 Rust 风格

大部分内部扫描代码都写得相当底层(它几乎扫描语法位的字节模式),但外部接口被设计成符合 Rust 风格。

拉取解析器本质上是一个事件的迭代器(开始和结束标签、文本和其他片段)。解析器数据结构直接实现了 Rust 的 Iterator 特性,并且 Event 是一个枚举。因此,你可以使用 Rust 迭代器基础设施的全部功能和表达能力,包括 for 循环和 map(如上面的示例所示),将事件收集到一个向量中(用于记录、回放和处理),等等。

此外,代表文本的 Text 事件是一个小的写时复制字符串。绝大多数文本片段只是源文档的切片。对于这些,写时复制提供了一个方便的表示,无需分配或复制,但在需要时提供了分配的字符串。因此,当将文本渲染为 HTML 时,大多数文本只需从源文档复制到 HTML 缓冲区一次。

当使用 pulldown-cmark 自带的 HTML 渲染器时,确保写入到一个缓冲目标,如 Vec<u8>String。由于它执行许多(非常)小的写入操作,直接写入 stdout、文件或套接字会损害性能。这种写入器可以被包装在一个 BufWriter 中。

构建选项

默认情况下,二进制文件也会被构建。如果你不想要/需要它,那么就按照以下方式构建

> cargo build --no-default-features

或者在你的 Cargo.toml 文件中添加

pulldown-cmark = { version = "0.5", default-features = false }

从版本 0.5 开始,x64 平台提供了 SIMD 加速的扫描器。要启用它们,请使用 simd 功能构建

> cargo build --release --features simd

或者将功能添加到你的项目 Cargo.toml

pulldown-cmark = { version = "0.5", default-features = false, features = ["simd"] }

作者

主要作者是 Raph Levien。

贡献

我们欢迎通过 GitHub pull requests 提交贡献。请参阅 CONTRIBUTIONS.md 获取更多详细信息。

依赖

~0.7–1MB
~12K SLoC