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

Download history 240566/week @ 2024-05-01 236172/week @ 2024-05-08 263302/week @ 2024-05-15 272229/week @ 2024-05-22 279370/week @ 2024-05-29 289646/week @ 2024-06-05 302132/week @ 2024-06-12 276133/week @ 2024-06-19 284796/week @ 2024-06-26 284093/week @ 2024-07-03 311348/week @ 2024-07-10 309487/week @ 2024-07-17 309191/week @ 2024-07-24 296144/week @ 2024-07-31 323278/week @ 2024-08-07 331852/week @ 2024-08-14

每月下载 1,316,860
用于 1,518 Crates(直接使用 465 个)

MIT 许可证

470KB
12K SLoC

pulldown-cmark

Tests Docs Crates.io

文档

此库是 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