5 个版本 (3 个重大更新)
0.4.1 | 2023 年 9 月 19 日 |
---|---|
0.3.0 | 2023 年 5 月 26 日 |
0.2.0 | 2023 年 5 月 24 日 |
0.1.1 | 2023 年 5 月 20 日 |
0.1.0 | 2023 年 5 月 19 日 |
#1096 in 解析器实现
每月 75 次下载
305KB
11K SLoC
syntastica
使用 tree-sitter 的现代化且易于使用的语法高亮
注意:如果在 GitHub 或 crates.io 上查看此文件,部分链接可能无法正常工作。请访问 自定义文档页面 或 docs.rs 页面,其中还包括 功能 部分。
概述
要使用 syntastica
,你可能需要依赖三个 crate
例如
syntastica = "<version>"
syntastica-parsers = { version = "<version>", features = ["some"] }
syntastica-themes = "<version>"
用例
syntastica
有三种主要的代码高亮方式,适用于三种不同的用例
- 高亮 一个 输入 一次:请参阅
highlight
和 此示例 - 高亮 一个 输入 多次(例如,使用不同的主题或渲染器):请参阅
Processor::process_once
、render
和 此示例 - 高亮 多个 不同输入 任意次数:请参阅
Processor
、render
和 此示例
子项目
除了主要的 syntastica
包之外,还开发了用于不同目的的许多其他包,并包含在仓库中。本节旨在提供良好的概述。
解析器集合
主要的 syntastica
包本身不提供树-sitter 解析器和查询。然而,该项目提供了三个不同的解析器集合,每个集合都有其优缺点。所有三个集合都依赖于 syntastica-queries
进行树-sitter 查询。选择一个,并将其作为依赖项添加到 syntastica
本身旁边。
所有三个解析器集合还提供了相同的公共 API,并为所有支持的语言提供了功能,以及三个功能组 some
、most
和 all
。请查看相应包的文档以获取更多信息。
如果您想使用这些解析器集合之外的任何语言,可以在 自定义语言示例 中看到一种方法。
syntastica-parsers
可能是最容易开始的。它使用来自 crates.io 的解析器。这主要有益于与 cargo 生态系统的良好集成。然而,许多树-sitter 解析器没有发布到 crates.io,并且那些发布的通常非常过时。因此,这个集合相对有限。syntastica-parsers-git
可能是整体上最佳的选择。它包含所有支持的语言,并且 当支持 WebAssembly 编译时,这将是用作集合。它在构建脚本中拉取固定版本的解析器 git 仓库,并链接到 C 和 C++ 解析器源。因此,它不依赖于上游解析器具有最新的 Rust 绑定。然而,以这种方式获取解析器需要git
命令在编译时可用,并且需要互联网访问,这可能不是所希望的。此外,编译可能需要非常长的时间,因为没有干净的方法来在构建之间缓存获取的仓库。syntastica-parsers-gitdep
是上述两者的混合。它使用 cargo git 依赖关系来获取解析器仓库,并依赖于远程 Rust 绑定(这就是为什么不是所有解析器都包含在内)。主要缺点是,因为这个集合依赖于不在 crates.io 上的包(即解析器),所以不能发布到 crates.io。这意味着,要使用它,您必须通过 git 依赖关系使用它,这反过来又禁止您的包发布到 crates.io。然而,与syntastica-parsers-git
不同,解析器只需由 cargo 获取一次,后续的构建将快得多。
主题集合
为了将突出显示的代码渲染给最终用户,需要一个 主题,该主题指定了用于哪个 主题键 的颜色。syntastica
项目附带一个包含几个默认主题的单独包:syntastica-themes
。
如果您想创建自己的主题,请查看 自定义主题示例 以及 theme!
宏的文档。
内部使用的箱包
《syntastica》仓库/工作空间还包含一些箱包,这些箱包不是为了外部使用,而是用于内部。以下列出了这些箱包。
注意:这些箱包的公共API没有保证!如果出于任何原因,您必须依赖其中一个,那么请使用以下方式锁定确切版本:
<crate> = "=<version>"
。
syntastica-core
定义了在多个其他箱包中使用的类型、特性、常量等。主要的syntastica
箱包将这些项目透明地重新导出,因此外部项目只需要依赖那个。然而,这些项目是在syntastica-core
中定义的,以避免在此工作空间内出现循环(开发)依赖。syntastica-macros
定义了仅在此工作空间内部使用的过程宏。这个箱包允许将语言/解析器的列表放在一个合并的languages.toml
文件中,并且不同的宏被用于需要引用此列表的不同地方。syntastica-highlight
是tree-sitter-highlight
的分支,为了在syntastica
中使用而进行了调整和精简。它包含主要的着色逻辑。syntastica-queries
是所有支持的语言的 tree-sitter 查询的集合。因为它标记为“仅限内部使用”,因为三个 解析器集合 都依赖于这个箱包,并通过其实现公开查询。然而,与列表中的前几个箱包不同,如果您仅需要查询,您可能实际上想要自己依赖这个箱包。
通用副产品
此列表包括为 syntastica
开发但与主项目没有直接关联、可以完全独立使用的箱包。
rsexpr
是一个通用的 S-表达式解析器,增加了对方括号、字符串和注释的支持。此外,解析后的 S-表达式可以格式化输出,以提供统一的格式。有关将此作为格式化器的更多信息,请参阅dprint-plugin-sexpr
。在syntastica
中,此箱包用于解析(和格式化)queries
目录中的 tree-sitter 查询。这些由cargo xtask codegen queries
处理,并产生位于generated_queries
目录中的查询,这些查询与syntastica-queries
一起捆绑。lua-pattern
是 Lua 表达式的解析器。这些表达式类似于正则表达式,但通常更有限制。该库还提供将 Lua 表达式转换为正则表达式字符串的最佳尝试转换。在syntastica
中使用它,因为许多源查询是从 nvim-treesitter 分支出来的,而 nvim-treesitter 重度使用#lua-match?
断言来匹配 Lua 表达式。然而,官方的 tree-sitter Rust 绑定不支持 Lua 表达式匹配(显然),因此在查询处理过程中(使用cargo xtask codegen queries
),所有 Lua 表达式都使用此库替换为正则表达式。syntastica-query-preprocessor
是树形查询的前处理程序,允许使用; 继承 <lang>
注释,条件性地跳过带有注释的节点,使用额外的断言如lua-match?
、contains?
和any-of?
,Neovim 的旧注入语法,以及顺序反转以翻转优先级。此库可以用于使用 Neovim 设计的查询,与官方的 tree-sitter Rust 绑定 进行最小手动更改。尽管名称中包含syntastica
,但此库可以外部使用,并且不依赖于其他任何syntastica-
库。在syntastica
中,它在codegen queries
xtask 中使用,因为许多查询是从 nvim-treesitter 分支出来的,并且为了调整来自 crates.io 的旧解析器版本的查询。
WebAssembly 支持
待办:WebAssembly 支持
示例
本节包含一些基本使用示例。更具体的示例可以在一些项的文档中找到,例如 Processor
类型或 render
函数。此外,examples
目录包含一些完整示例。
此处找到的示例列表
示例:高亮一次
此示例展示了使用 syntastica
最简单、最快的方法。请参阅有关 用例 的部分,了解何时适合使用 syntastica
的这种方式。
use syntastica::renderer::TerminalRenderer;
use syntastica_parsers::{Lang, LanguageSetImpl};
let output = syntastica::highlight(
// the code to highlight
r#"fn main() { println!("42"); }"#,
// the input's language
Lang::Rust,
// use `syntastica-parsers` language set
&LanguageSetImpl::new(),
// use the TerminalRenderer with no background color
&mut TerminalRenderer::new(None),
// use the gruvbox dark theme from `syntastica-themes`
syntastica_themes::gruvbox::dark(),
)
.unwrap_or_else(|err| panic!("highlighting failed: {err}"));
println!("{output}");
示例:多次高亮相同的输入
此示例展示了如何使用两个不同的渲染器以两种不同的主题渲染相同的输入。
use syntastica::{Processor, style::Color, renderer::*};
use syntastica_parsers::{Lang, LanguageSetImpl};
// process the input once, but store the raw highlight information
let highlights = Processor::process_once(
// the code to highlight
r#"fn main() { println!("42"); }"#,
// the input's language
Lang::Rust,
// use `syntastica-parsers` language set
&LanguageSetImpl::new(),
)
.unwrap_or_else(|err| panic!("highlighting failed: {err}"));
// render the highlights to the terminal using the
// gruvbox dark theme on a dark gray background
println!("{}", syntastica::render(
&highlights,
&mut TerminalRenderer::new(Some(Color::new(40, 40, 40))),
syntastica_themes::gruvbox::dark(),
));
// render the same input to HTML using the onelight theme
let html = syntastica::render(
&highlights,
&mut HtmlRenderer::new(),
syntastica_themes::one::light(),
);
// you could for example write that to a file called `index.html`:
// std::fs::write("index.html", html).unwrap();
示例:高亮多个不同的输入
本例展示了如果需要高亮多个不同输入,如何重复使用Processor
。
use syntastica::{Processor, style::Color, renderer::*};
use syntastica_parsers::{Lang, LanguageSetImpl};
// create a language set and a `Processor`
let language_set = LanguageSetImpl::new();
let mut processor = Processor::new(&language_set);
// Note: `language_set` has to be stored in a variable, because the processor
// is bound to the lifetime of the reference passed to `new`
// process some input
let highlights_rust = processor.process(
// the code to highlight
r#"fn main() { println!("42"); }"#,
// the input's language
Lang::Rust,
)
.unwrap_or_else(|err| panic!("highlighting failed: {err}"));
// process some other input in another language
let highlights_js = processor.process(r"console.log('42')", Lang::Javascript)
.unwrap_or_else(|err| panic!("highlighting failed: {err}"));
// render the rust code to the terminal using the
// gruvbox dark theme on a dark gray background
println!("{}", syntastica::render(
&highlights_rust,
&mut TerminalRenderer::new(Some(Color::new(40, 40, 40))),
syntastica_themes::gruvbox::dark(),
));
// render the same rust code to HTML using the onelight theme
let html = syntastica::render(
&highlights_rust,
&mut HtmlRenderer::new(),
syntastica_themes::one::light(),
);
// you could for example write that to a file called `index.html`:
// std::fs::write("index.html", html).unwrap();
// now render the javascript code to the terminal using the
// onedark theme and no background color
println!("{}", syntastica::render(
&highlights_js,
&mut TerminalRenderer::new(None),
syntastica_themes::one::dark(),
));
示例:根据文件类型检测语言
这是对第一个示例的修改,展示了如何根据文件类型检测要使用的语言。请参阅第一个示例中对其他代码的解释。
syntastica
使用tft
来检测文件类型,它提供自动检测功能。
use syntastica::{renderer::TerminalRenderer, language_set::{LanguageSet, SupportedLanguage}};
use syntastica_parsers::{Lang, LanguageSetImpl};
// detect the file type given a file's path and content.
// this requires a dependency on `tft`
let ft = tft::detect("main.rs", "");
let language_set = LanguageSetImpl::new();
let output = syntastica::highlight(
r#"fn main() { println!("42"); }"#,
// the `SupportedLanguage` trait provides a `for_file_type` function
// which returns an `Option<Lang>`
// make sure to have the trait in scope
Lang::for_file_type(ft).unwrap(),
&language_set,
&mut TerminalRenderer::new(None),
syntastica_themes::gruvbox::dark(),
)
.unwrap_or_else(|err| panic!("highlighting failed: {err}"));
println!("{output}");
示例:自定义主题
这是对第一个示例的修改,展示了如何创建简单的自定义主题。请参阅第一个示例中对其他代码的解释,并查看theme!
宏的文档以获取更多信息。
use syntastica::{renderer::TerminalRenderer, theme};
use syntastica_parsers::{Lang, LanguageSetImpl};
let theme = theme! {
// specify colors using hex literals
"purple": "#c678dd",
"blue": "#61afef",
"green": "#98c379",
// link to other keys using a `$` sign
"keyword": "$purple",
"function": "$blue",
// specify more styling options in curly braces
// (note that currently this order is required by the macro)
"string": {
color: None,
underline: false,
strikethrough: false,
italic: true,
bold: false,
link: "green",
},
};
let output = syntastica::highlight(
r#"fn main() { println!("42"); }"#,
Lang::Rust,
&LanguageSetImpl::new(),
&mut TerminalRenderer::new(None),
theme,
)
.unwrap_or_else(|err| panic!("highlighting failed: {err}"));
println!("{output}");
版本控制
此工作空间中以syntastica
开头的所有crate共享相同的版本。在所有这些的公共API中,除了列出的内部crate外,都使用典型的语义版本控制规则。其他crate在此工作空间中具有自己的单独版本。
版本指定为MAJOR.MINOR.PATCH
。只要MAJOR
版本指定器仍然是0
,对MINOR
版本的更改也可能是破坏性更改。只有当公共API完全相同,才会增加PATCH
部分。
灵感
待办事项:简要解释来源(lirstings)
待办事项
- 使用
tree-sitter-c2rust
轻松编译为WebAssembly
依赖项
~5–7MB
~133K SLoC