42 个版本
新版本 0.12.0 | 2024 年 8 月 17 日 |
---|---|
0.11.0 | 2024 年 5 月 15 日 |
0.10.0 | 2024 年 2 月 3 日 |
0.9.3 | 2023 年 5 月 21 日 |
0.0.3 | 2015 年 6 月 9 日 |
在 文本处理 中排名 6
每月下载 1,316,860 次
用于 1,518 个 Crates(直接使用 465 个)
470KB
12K SLoC
pulldown-cmark
此库是 CommonMark 的拉解析器,用 Rust 编写。它包含一个简单的命令行工具,可用于将内容渲染为 HTML,并且也便于作为库使用。
设计目标:
- 快速;最小化分配和复制
- 安全;纯 Rust 编写,无不安全代码块(除了可选的 SIMD 功能)
- 灵活;特别是支持源映射
- 正确;目标是 100% 符合 CommonMark 规范
此外,它可选地支持解析脚注、GitHub 风格表格、GitHub 风格任务列表 和 删除线。
构建此包需要 Rustc 1.71.1 或更高版本。
示例
示例用法
// Create parser with example Markdown text.
let markdown_input = "hello world";
let parser = pulldown_cmark::Parser::new(markdown_input);
// Write to a new String buffer.
let mut html_output = String::new();
pulldown_cmark::html::push_html(&mut html_output, parser);
assert_eq!(&html_output, "<p>hello world</p>\n");
为什么选择拉解析器?
虽然有很多 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,
_ => ()
}
}
请注意,由于解析器评估源的方式,可能会发生连续的文本事件。存在一个名为 TextMergeStream
的实用工具,可以提高迭代事件时的舒适度
use pulldown_cmark::{Event, Parser, Options};
let markdown_input = "Hello world, this is a ~~complicated~~ *very simple* example.";
let iterator = TextMergeStream::new(Parser::new(markdown_input));
for event in iterator {
match event {
Event::Text(text) => println!("{}", text),
_ => {}
}
}
在 examples
目录中,有一些使用此包的基本但功能齐全的示例。
使用Rust惯用用法
大部分内部扫描代码都编写在相当低级别(它基本上扫描字节模式以识别语法位),但外部接口设计为符合Rust惯用用法。
拉解析器本质上是一系列事件(起始和结束标签、文本以及其他片段)的迭代器。解析器数据结构直接实现了Rust的Iterator trait,而Event是一个枚举。因此,您可以充分利用Rust的迭代器基础设施的强大功能和表达性,包括for循环和 map
(如上述示例所示),将事件收集到向量中(用于记录、回放和处理)等等。
此外,Text
事件(表示文本)是一个小的copy-on-write字符串。绝大多数文本片段只是源文档的切片。对于这些,copy-on-write提供了方便的表示,无需分配或复制,但需要时可以分配字符串。因此,当将文本渲染为HTML时,大多数文本只需从源文档复制一次到HTML缓冲区。
当使用pulldown-cmark的自己的HTML渲染器时,请确保写入到缓冲目标,如 Vec<u8>
或 String
。由于它执行许多(非常)小的写入,直接写入到stdout、文件或套接字会损害性能。这种写入器可以被包装在 BufWriter
中。
构建选项
默认情况下,二进制文件也会构建。如果您不想/不需要它,那么就按照以下方式构建
> cargo build --no-default-features
或者使用 cargo add
将此包添加为您的项目的依赖项
> cargo add pulldown-cmark --no-default-features
从版本0.5开始,为x64平台提供了SIMD加速的扫描器。要启用它们,请使用simd功能构建
> cargo build --release --features simd
或者使用 cargo add
将此包添加为您的项目的依赖项,并使用功能
> cargo add pulldown-cmark --no-default-features --features=simd
为了获得更高的发布性能,您可能想在您的配置文件发布中设置此配置
lto = true
codegen-units = 1
panic = "abort"
作者
主要作者是Raph Levien。新设计(v0.3+)的实现由Marcus Klaas de Vries完成。自2023年以来,开发工作由Martín Pozo、Michael Howell、Roope Salmi和Martin Geisler推动。
许可证
本软件采用MIT许可。详细信息请见许可文件。
贡献
我们很高兴接受通过GitHub拉取请求的贡献。请参阅CONTRIBUTING.md以获取更多详细信息。
依赖项
~0.6–1MB
~15K SLoC