39个稳定版本 (5个主要版本)

5.2.0 2024年2月7日
5.1.0 2023年7月31日
5.0.0 2022年5月4日
4.7.1 2022年1月2日
0.7.0 2016年7月2日

#10 in 解析器实现

Download history 40522/week @ 2024-04-28 37120/week @ 2024-05-05 40798/week @ 2024-05-12 40480/week @ 2024-05-19 41674/week @ 2024-05-26 44189/week @ 2024-06-02 44441/week @ 2024-06-09 42755/week @ 2024-06-16 44308/week @ 2024-06-23 42455/week @ 2024-06-30 44796/week @ 2024-07-07 43501/week @ 2024-07-14 43536/week @ 2024-07-21 46352/week @ 2024-07-28 45010/week @ 2024-08-04 41447/week @ 2024-08-11

每月下载量180,846
435个crate(216个直接使用)使用

MIT许可证

1MB
7K SLoC

syntect

Crates.io Documentation Crates.io Build Status codecov

syntect是一个使用Sublime Text语法定义的Rust语法高亮库。它旨在为任何需要语法高亮的Rust项目提供良好的解决方案,包括与Rust编写的文本编辑器的深度集成。它至少被两家公司用于生产环境,并被许多开源项目使用。

如果您使用Rust编写文本编辑器(或其他需要高亮显示的东西),而这个库不符合您的需求,我认为这是一个bug,您应该提交问题或给我发邮件。我认为这个项目已经基本完成,我仍然维护它并审查PR,但它不是在积极开发中。

入门指南

syntect可在crates.io上获取。您可以通过将以下行添加到您的Cargo.toml中来安装它

syntect = "5.0"

之后,请查看文档示例

如果您已克隆此存储库,请确保运行

git submodule update --init

以获取运行测试所需的所有依赖项。

特性/目标

  • 支持多种语言(通过使用现有的语法格式实现)
  • 高速度高亮,比几乎所有文本编辑器都要快
  • 包含易于使用的API以处理基本用例
  • API允许在具有分段表和增量重新高亮的复杂文本编辑器中使用
  • 公开解析过程的内部结构,以便文本编辑器可以进行缓存解析状态和使用语义信息进行代码智能等操作
  • 高质量高亮显示,支持诸如heredocs和复杂语法(如Rust的)等特性。
  • 在库的二进制文件中包含所有默认语法定义的压缩转储,以便用户无需管理一个语法文件夹。
  • 文档齐全,我已经尝试为所有非自解释的内容添加有用的文档注释。
  • 内置彩色HTML输出到<pre>标签或24位彩色ANSI终端转义序列。
  • 几乎完全兼容Sublime Text 3,包括许多边缘情况。几乎通过所有Sublime的语法测试,见问题59
  • 加载速度快,目前大约23ms,但可能更快。

屏幕截图

目前有一个名为syncat的示例程序,它使用硬编码的主题和语法,通过许多较新终端支持的24位终端转义序列打印源文件之一。这些截图由于两个原因看起来不是很好:首先,sRGB颜色没有正确校正,其次,Rust语法定义使用了一些这些主题没有突出显示的复杂标签。

Nested languages Base 16 Ocean Dark Solarized Light InspiredGithub

示例代码

将字符串的高亮行打印到终端。请参阅easyhtml模块文档以获取更多基本用例示例。

use syntect::easy::HighlightLines;
use syntect::parsing::SyntaxSet;
use syntect::highlighting::{ThemeSet, Style};
use syntect::util::{as_24_bit_terminal_escaped, LinesWithEndings};

// Load these once at the start of your program
let ps = SyntaxSet::load_defaults_newlines();
let ts = ThemeSet::load_defaults();

let syntax = ps.find_syntax_by_extension("rs").unwrap();
let mut h = HighlightLines::new(syntax, &ts.themes["base16-ocean.dark"]);
let s = "pub struct Wow { hi: u64 }\nfn blah() -> u64 {}";
for line in LinesWithEndings::from(s) {
    let ranges: Vec<(Style, &str)> = h.highlight_line(line, &ps).unwrap();
    let escaped = as_24_bit_terminal_escaped(&ranges[..], true);
    print!("{}", escaped);
}

性能

目前syntect是较快的语法高亮引擎之一,但不是最快的。以下性能功能已完成

  • 在语言之间建立预链接引用(例如<script>标签),因此在热路径上没有树遍历字符串查找
  • 将作用域的紧凑二进制表示传递和复制
  • 使用位操作仅几条指令即可确定作用域是否是另一个作用域的前缀
  • 缓存正则表达式匹配以减少oniguruma搜索行次数
  • 加速作用域查找以减少高亮一系列作用域操作时必须进行的选择器匹配量
  • 懒惰编译正则表达式,以避免在启动时编译用于Actionscript的成千上万的正则表达式,而实际上没有人会使用它们
  • 可选使用fancy-regex crate。遗憾的是,在我们的基准测试中,这还没有比oniguruma更快,但未来可能会。

当前的性能数字如下。如果实现上述更多功能,这些数字可能会更好,但它们比许多其他文本编辑器都要好。所有测量都是在2012年中期的15英寸视网膜Macbook Pro上进行的,我的新2019 Macbook大约需要这些时间的70%。

  • 高亮9200行/247kb的jQuery 2.1需要600ms。比较一下
    • Textmate 2、Spacemacs和Visual Studio Code都大约需要2秒左右(用秒表手动测量,因此是近似的)。
    • Atom需要6秒
    • Sublime Text 3开发构建需要98ms(仅高亮,点击到像素需要~200ms),尽管它有一个非常复杂的javascript语法定义。
    • Vim是瞬间的,但这个比较并不公平,因为vim的高亮显示比其他编辑器要简单得多。比较vim的语法Sublime的
    • 这些比较并不完全公平,除了与Sublime Text的比较之外,因为Sublime Text使用的是相同的主题和相同的复杂的ES6语法定义。
  • 简单的语法更快,JavaScript 是最复杂的之一。高亮一个 1700 行 62kb 的 XML 文件只需要 34ms,或者每秒 50,000 行。
  • ~138ms 可以加载并链接默认的 Sublime 软件包集中所有的语法定义。
    • 但仅需要 ~23ms 就可以加载并链接来自内部预制的二进制转储的所有语法定义,并且使用懒惰正则表达式编译。
  • ~1.9ms 解析并高亮 30 行 791 字符的 testdata/highlight_test.erb 文件。这相当于每秒大约 16,000 行或每秒 422 千字节。
  • ~250mssyncat 开始,加载定义,高亮测试文件并关闭。大部分时间都花在加载上了。

功能标志

Syntect 严重依赖 cargo features,以支持仅需要功能子集的用户。特别是,可以在不使用解析器的情况下使用 syntect 的语法高亮组件(例如,当为特定语言手动编写更高性能的解析器时),通过将 default-features = false 添加到您的 Cargo.toml 中的 syntect 条目来实现。

有关可用功能的更多信息,请参阅 Cargo.toml 中的功能部分。

纯 Rust fancy-regex 模式,无 onig

从 4.0 版本开始,syntect 提供了一个基于 fancy-regex 引擎的替代纯 Rust 正则表达式引擎,该引擎扩展了 regex crate,以支持 Sublime 语法所需的高级正则表达式功能,如前瞻。

fancy-regex 的优势在于它不需要 onig crate,该 crate 需要构建和链接 Oniguruma C 库。许多用户在构建 onig crate 时遇到困难,尤其是在 Windows 和 Webassembly 上。

根据我们的测试,这个新引擎同样正确,但在生产环境中还没有得到充分的测试。它目前似乎大约是默认 Oniguruma 引擎速度的一半,但进一步的测试和优化(也许是你!)最终可能会使其超越 Oniguruma 的速度并成为默认。

要使用 fancy-regex 引擎与 syntect 一起,将其添加到您的 Cargo.toml 中,如下所示

syntect = { version = "4.2", default-features = false, features = ["default-fancy"]}

如果您想使用 fancy-regex 引擎运行示例,可以使用如下命令行

cargo run --features default-fancy --no-default-features --release --example syncat testdata/highlight_test.erb

由于 Cargo 功能的工作方式,如果任何依赖的 crate 没有启用 fancy-regex 而依赖于 syntect,则您将获得默认的 onig 模式。

注意: fancy-regex 引擎在调试模式下 非常慢,因为正则表达式引擎(高亮的主要热点)现在是在 Rust 中而不是 C 中,总是带有优化构建。考虑使用发布模式或 onig 进行测试。

缓存

由于 syntect 的 API 公开了内部可缓存的数据结构,因此文本编辑器可以使用一种缓存策略,允许在初始高亮后更改后立即重新渲染屏幕上的文本,无论文件大小。

基本上,在初始解析过程中,每解析大约1000行时,将解析状态复制到该行的边缓冲区中。当文本发生变化时,由于Sublime Text语法(以及语言一般)的工作方式,只有变化后的高亮受影响。因此,当文本发生变化时,在解析状态缓存中回溯寻找编辑前的最后一个状态,然后启动一个后台任务从该处开始重新高亮。一旦后台任务高亮到当前编辑视图的末尾,渲染新变化并继续在后台重新高亮文件的其他部分。

这种方式,从编辑发生到新着色渲染的时间,在最坏的情况下只需要重新高亮999+视口长度行。考虑到syntect的速度,即使对于长文件和最复杂的语法和主题,这也应该在100毫秒以内完成。这对于世界上最快的打字员在最坏情况下每按一个键重新高亮也是足够的。而且,通过更频繁地缓存解析状态,可以将这个时间复杂度降至视口长度,但代价是更多的内存。

每次文件发生变化时,都会找到最新的缓存状态,然后在该点清除缓存,并启动一个后台作业。任何已运行的作业都会停止,因为它们正在处理旧状态。这样,你可以只有一个专门用于高亮显示的线程,它始终在进行最新的工作或睡眠。

并行化

从3.0版本开始,syntect可以用于并行解析/高亮显示。SyntaxSet既是Send也是Sync,因此可以轻松地在多个线程中使用。它也可以Clone,这意味着你可以构建一个语法集,然后克隆它用于其他线程,如果你愿意的话。

与旧版本相比,没有任何东西阻止SyntaxSet的序列化。因此,你可以直接反序列化一个完全链接的SyntaxSet并开始用于解析/高亮显示。在此之前,总是需要先进行链接。

值得一提的是,正则表达式是在实际需要时才懒编译的。一旦正则表达式被编译,编译后的版本将被所有后续线程使用。请注意,这是通过内部可变性完成的,所以如果多个线程同时遇到同一个未编译的正则表达式,编译可能会多次发生。之后,将使用其中一个编译后的正则表达式。当SyntaxSet被克隆时,克隆集中的正则表达式需要重新编译。

为了将并行性添加到以前的单线程程序中,建议使用的线程池是rayon。然而,如果你在一个已经并行的环境中工作,可能存在比你想要的更多线程(例如,编写Iron请求的处理程序),建议使用rust-scoped-pool强制所有高亮显示在固定大小的线程池中完成。前者的一个例子在examples/parsyncat.rs中。

示例可用

examples文件夹中有一系列使用syntect的程序示例,以及一些代码库外的代码

  • syncat使用24位颜色ANSI转义码将高亮文件打印到终端。它演示了一个简单的文件高亮显示工作流程。
  • synhtml 打印一个HTML文件,该文件将显示高亮代码。展示了Syntect如何被Web服务器和静态站点生成器使用。
  • synstats 收集一个文件夹中代码的一组统计数据。包括基本的行数统计,也包括更高级的功能,如函数数量。展示了Syntect不仅可以用于代码高亮,还可以用于代码分析,以及如何使用API解析语义标记。
  • faiyels 是我编写的一个小型代码缩略图可视化工具,它使用 syntect 进行高亮。
  • parsyncatsyncat 类似,但接受多个文件,并并行高亮显示它们。它展示了如何使用 syntect 在多个线程中。

以下是 synstatssyntect 的代码库(不包括示例和测试数据)中提取的统计信息,截至 这个提交

################## Stats ###################
File count:                               19
Total characters:                     155504

Function count:                          165
Type count (structs, enums, classes):     64

Code lines (traditional SLOC):          2960
Total lines (w/ comments & blanks):     4011
Comment lines (comment but no code):     736
Blank lines (lines-blank-comment):       315

Lines with a documentation comment:      646
Total words written in doc comments:    4734
Total words written in all comments:    5145
Characters of comment:                 41099

使用Syntect的项目

以下是一份使用Syntect的项目列表,按它们使用 syntect 的时间顺序排列(欢迎发送PR来增加此列表)

  • bat,一个 cat(1) 克隆,使用 syntect 进行语法高亮。
  • Bolt,一个用于构建和测试API的桌面应用程序,使用 syntect 进行语法高亮。
  • catmark,一个控制台Markdown打印机,使用 syntect 进行代码块高亮。
  • Cobalt,一个使用 syntect 进行代码片段高亮的静态站点生成器。
  • crowbook,一个Markdown书籍生成器,使用 syntect 进行代码块高亮。
  • delta,一个用于Git的语法高亮分页器。
  • Docket,一个使用 syntect 进行高亮的文档站点生成器。
  • hors,通过命令行提供即时编码答案,使用 syntect 进行代码块高亮。
  • mdcat,一个控制台Markdown打印机,使用 syntect 进行代码块高亮。
  • Scribe,一个使用 syntect 进行高亮的Rust文本编辑框架。
  • syntect_server,一个用于语法高亮的HTTP服务器。
  • tokio-cassandra,Rust中的CQL外壳,使用 syntect 进行外壳着色。
  • xi-editor,一个使用 syntect 进行高亮的Rust文本编辑器。
  • Zola,一个使用 syntect 进行代码片段高亮的静态站点生成器。
  • The Way,一个用于终端的代码片段管理器,使用 syntect 进行高亮。
  • Broot,一个终端文件管理器,使用 syntect 进行文件预览。
  • Rusty Slider,一个markdown幻灯片演示应用,使用 syntect 进行代码块高亮。

许可证和致谢

感谢 Robin StockerKeith HallMartin Nordholtssyntect 做出的巨大贡献,尤其是在 v1.0 之后的重要改进。他们为 syntect 当前的发展做出了巨大贡献。例如,@robinst 实现了 fancy-regex 支持大规模重构,以启用使用竞技场的并行高亮。@keith-hall 发现并修复了许多错误,并 实现了 Sublime 语法测试支持

感谢 Textmate 2 和 @defuz 的 sublimate,我从中获取了灵感和开源代码,包括 sublimate 的 tmTheme 加载器。所有代码(包括 defuz 的 sublimate 代码)均在 MIT 许可下发布。

依赖项

~1–11MB
~120K SLoC