第54章 Zed 第54章:语言服务器(Language Server)与 Tree-sitter 配置
Zed 的语言支持建立在两项技术之上:
- Tree-sitter:负责语法高亮和基于结构的特性,如大纲面板。
- Language Server Protocol (LSP):提供语义相关的特性,如代码补全、诊断、跳转定义和重构。
本页将介绍语言相关的设置、文件关联、language server 配置、格式化、代码检查以及语法高亮。
支持的语言列表请参阅 Supported Languages。如需为新语言添加支持,请参阅 Language Extensions。
语言相关设置
Zed 允许你为单个语言覆盖全局设置。这些自定义配置定义在 settings.json 文件的 languages 键下。
下面是一个语言相关设置的示例:
"languages": {
"Python": {
"tab_size": 4,
"formatter": "language_server",
"format_on_save": "on"
},
"JavaScript": {
"tab_size": 2,
"formatter": {
"external": {
"command": "prettier",
"arguments": ["--stdin-filepath", "{buffer_path}"]
}
}
}
}
每种语言都可以自定义大量设置,包括:
tab_size:每级缩进的空格数formatter:用于代码格式化的工具format_on_save:保存时是否自动格式化代码enable_language_server:是否启用 language server 支持hard_tabs:使用 Tab 而非空格进行缩进preferred_line_length:推荐的最大行宽soft_wrap:长代码行的换行方式show_completions_on_input:是否在输入时显示自动补全show_completion_documentation:是否在补全菜单中显示内联及侧边文档colorize_brackets:是否使用 tree-sitter 括号查询来检测并高亮编辑器中的括号(也称“彩虹括号”)
这些设置可帮助你在不同语言和项目中保持一致的代码风格。
文件关联
Zed 会根据扩展名自动识别文件类型,但你可以根据工作流自定义这些关联。
要设置自定义文件关联,请在 settings.json 中使用 file_types 设置:
"file_types": {
"C++": ["c"],
"TOML": ["MyLockFile"],
"Dockerfile": ["Dockerfile*"]
}
此配置指示 Zed:
- 将
.c文件视为 C++ 而非 C - 将名为 "MyLockFile" 的文件识别为 TOML
- 对所有以 "Dockerfile" 开头的文件应用 Dockerfile 语法
你可以使用通配符模式进行更灵活的匹配,从而处理项目中的复杂命名约定。
语言服务器交互
语言服务器是 Zed 智能编码功能的核心,提供代码补全、跳转定义、实时错误检查等功能。
什么是语言服务器?
语言服务器实现了语言服务器协议(LSP),该协议标准化了编辑器与特定语言工具之间的通信。这使得 Zed 无需单独实现每个功能,即可为多种编程语言提供高级支持。
语言服务器提供的关键功能包括:
- 代码补全
- 错误检查与诊断
- 代码导航(跳转定义、查找引用)
- 代码操作(重命名、提取方法)
- 悬停信息
- 工作区符号搜索
管理语言服务器
Zed 简化了用户的语言服务器管理:
-
自动下载:当打开匹配文件类型的文件时,Zed 会自动下载相应的语言服务器。对于已知的文件类型,Zed 可能会提示你安装扩展。
-
存储位置:
- macOS:
~/Library/Application Support/Zed/languages - Linux:
$XDG_DATA_HOME/zed/languages、$FLATPAK_XDG_DATA_HOME/zed/languages或$HOME/.local/share/zed/languages
- macOS:
-
自动更新:Zed 会保持你的语言服务器为最新版本,确保你始终拥有最新的功能和改进。
选择语言服务器
Zed 中的某些语言提供多个语言服务器选项。你可能安装了多个捆绑了针对同一语言服务器的扩展,这可能导致功能重叠。为了确保使用你偏好的功能,Zed 允许你指定使用哪些语言服务器以及它们的优先级顺序。
你可以使用 language_servers 设置来指定你的偏好:
"languages": {
"PHP": {
"language_servers": ["intelephense", "!phpactor", "!phptools", "!phpantom", "..."]
}
}
在此示例中:
intelephense被设置为主要语言服务器。phpactor、phptools和phpantom被禁用(注意!前缀)。"..."会展开为 PHP 其余已注册但尚未列出的语言服务器。
"..." 相当于一个通配符,代表所有你没有显式提及的已注册语言服务器。你按名字列出的服务器保持原位置,"..." 则在列表中它所在的位置填入剩余的服务器。以 ! 开头的服务器会被完全排除。这意味着如果之后安装了新的语言服务器扩展,或为某个语言注册了新的服务器,"..." 会自动把它包含进来。如果你想完全掌控启用哪些服务器,就省略 "..."——这样只有你按名字列出的服务器会被启用。
示例
假设你在使用 Ruby,默认配置如下:
{
"languages": {
"Ruby": {
"language_servers": [
"solargraph",
"!ruby-lsp",
"!rubocop",
"!sorbet",
"!steep",
"!kanayago",
"!fuzzy-ruby-server",
"..."
]
}
}
}
当你在设置中覆盖 language_servers 时,你写的列表会完全替换默认配置。也就是说,像 kanayago 这类默认禁用的服务器会被 "..." 重新启用,除非你再次显式禁用它们。
| 配置 | 结果 |
|---|---|
["..."] |
solargraph, ruby-lsp, rubocop, sorbet, steep, kanayago, fuzzy-ruby-server |
["ruby-lsp", "..."] |
ruby-lsp, solargraph, rubocop, sorbet, steep, kanayago, fuzzy-ruby-server |
["ruby-lsp", "!solargraph", "!kanayago", "..."] |
ruby-lsp, rubocop, sorbet, steep, fuzzy-ruby-server |
["ruby-lsp", "solargraph"] |
ruby-lsp, solargraph |
注意:在第一个示例中,尽管
kanayago默认处于禁用状态,但"..."仍然包含它。因为该覆盖配置替换了默认列表,所以"!kanayago"条目已不存在。若要保持其禁用状态,必须在配置中显式包含"!kanayago"。
顶层语言设置
所有语言设置也可设置在 settings.json 的顶层,即位于 languages 映射之外。
顶层条目是所有语言的默认值:语言特定的值会完全**替换**顶层值,二者绝不会合并。
工具链
某些语言服务器需要配置当前的“工具链”。工具链指特定版本的编程语言编译器或/和解释器的安装环境,可能还包含项目的完整依赖集。
Zed 将 Python 的虚拟环境视为工具链的一个例子。
并非 Zed 中的所有语言都支持工具链的发现和选择,但对于支持的,您可以通过工具链选择器(通过 {#action toolchain::Select})指定工具链。要了解 Zed 中工具链的更多信息,请参阅 toolchains。
配置语言服务器
在 settings.json 中配置语言服务器时,自动补全建议包含 Zed 识别的所有可用 LSP 适配器,而不仅仅是当前已加载语言对应的活跃适配器。这有助于您在打开使用这些语言服务器的文件前,发现并配置它们。
许多语言服务器接受自定义配置选项。您可以在 settings.json 的 lsp 部分中设置这些选项:
"lsp": {
"rust-analyzer": {
"initialization_options": {
"check": {
"command": "clippy"
}
}
}
}
此示例配置 Rust Analyzer,使其在保存文件时使用 Clippy 进行额外的 Lint 检查。
嵌套对象
在 Zed 中配置语言服务器选项时,应使用嵌套对象而非点分隔的字符串,这一点在处理复杂配置时尤为重要。我们以 TypeScript 语言服务器为例,看一下实际用法:假设你想为 TypeScript 配置以下选项:
- 启用严格空值检查
- 将目标 ECMAScript 版本设为 ES2020
在 Zed 的 settings.json 中,应这样组织结构:
"lsp": {
"typescript-language-server": {
"initialization_options": {
// 以下写法不支持(VSCode 点号风格):
// "preferences.strictNullChecks": true,
// "preferences.target": "ES2020"
//
// 正确写法(嵌套表示法):
"preferences": {
"strictNullChecks": true,
"target": "ES2020"
},
}
}
}
可配置选项
语言服务器支持的配置选项因实现而异。
仅在语言服务器启动时发送一次,重新应用更改需要重启服务器。
例如,rust-analyzer 和 clangd 仅依赖这种配置方式。
"lsp": {
"rust-analyzer": {
"initialization_options": {
"checkOnSave": false
}
}
}
服务器可多次查询此配置。 大多数服务器仅依赖这种配置方式。
"lsp": {
"tailwindcss-language-server": {
"settings": {
"tailwindCSS": {
"emmetCompletions": true,
},
}
}
}
除了 LSP 相关的服务器配置选项外,Zed 中某些服务器还允许配置 Zed 启动二进制文件的方式。
语言服务器会自动下载,或在你的 PATH 中找到时直接启动。如果你想指定一个自定义的二进制文件,可以在设置中配置:
"lsp": {
"rust-analyzer": {
"binary": {
// 是从网上获取二进制文件,还是尝试使用本地版本。
"ignore_system_version": false,
"path": "/path/to/langserver/bin",
"arguments": ["--option", "value"],
"env": {
"FOO": "BAR"
}
}
}
}
启用或禁用语言服务器
你可以全局或按语言开关语言服务器支持:
"languages": {
"Markdown": {
"enable_language_server": false
}
}
上面的配置会为 Markdown 文件禁用语言服务器,在大型文档项目中有助于提升性能。你可以在 ~/.config/zed/settings.json 中全局配置,也可以在项目目录的 .zed/settings.json 中配置。
格式化与 Lint
Zed 支持代码格式化和 lint,帮助保持代码风格一致,并尽早发现潜在问题。
配置格式化工具
Zed 既支持内置格式化工具,也支持外部格式化工具。详见 formatter 文档。你可以在 settings.json 中全局或按语言配置:
"languages": {
"JavaScript": {
"formatter": {
"external": {
"command": "prettier",
"arguments": ["--stdin-filepath", "{buffer_path}"]
}
},
"format_on_save": "on"
},
"Rust": {
"formatter": "language_server",
"format_on_save": "on"
}
}
这个例子为 JavaScript 使用 Prettier,为 Rust 使用语言服务器自带的格式化工具,并都开启了保存时自动格式化。
如需为某种语言禁用格式化:
"languages": {
"Markdown": {
"format_on_save": "off"
}
}
配置 Linter
Zed 中的 Linting 通常由 language server 处理。许多 language server 允许配置 linting 规则:
"lsp": {
"eslint": {
"settings": {
"codeActionOnSave": {
"rules": ["import/order"]
}
}
}
}
此配置设置 ESLint 在保存 JavaScript 文件时自动整理 import。
要在保存时自动运行 linter 修复:
"languages": {
"JavaScript": {
"formatter": {
"code_action": "source.fixAll.eslint"
}
}
}
格式化选区
Zed 支持通过 {#action editor::FormatSelections}(快捷键 {#kb editor::FormatSelections})仅格式化选中文本。具体行为取决于配置的 formatter:
- 仅当活动 formatter 支持至少一个选中的 buffer 的范围格式化时,该操作才会显示。
- Language server:为每个选区发送 LSP 范围格式化请求。这提供最精确的仅选区格式化,且仅在配置的 language server 支持范围格式化时可用。
- Prettier:使用 Prettier 内置的范围格式化功能格式化包含所有选区的范围。位于选定范围之外的任何结果编辑将被丢弃,因此只修改选中的代码。
- 外部命令:外部命令 formatter 不支持范围格式化,在格式化选区时会被跳过。
- Code action formatter:Code action 作用于整个 buffer,因此单独无法启用
format selections。
集成格式化和 Linting
Zed 允许在保存时同时运行格式化和 linting。以下示例使用 Prettier 格式化 JavaScript 文件,使用 ESLint 进行 linting:
故障排查
如果在格式化或 Lint 过程中遇到问题:
- 查看 Zed 日志文件中的错误信息(使用命令面板:{#action zed::OpenLog})
- 确认外部工具(格式化工具、Lint 工具)已正确安装并添加到 PATH 中
- 检查 Zed 设置和语言特定配置文件(例如
.eslintrc、.prettierrc)中的配置是否正确
语法高亮和主题
Zed 提供了语法高亮和主题的自定义选项,允许你调整代码的视觉外观。
自定义语法高亮
Zed 使用 Tree-sitter 语法进行语法高亮。你可以通过 theme_overrides 设置来覆盖默认的高亮规则。
以下示例将注释设置为斜体,并更改字符串的颜色:
"theme_overrides": {
"One Dark": {
"syntax": {
"comment": {
"font_style": "italic"
},
"string": {
"color": "#00AA00"
}
}
}
}
选择和自定义主题
更改主题:
- 使用主题选择器({#kb theme_selector::Toggle})
- 或在
settings.json中设置:
"theme": {
"mode": "dark",
"dark": "One Dark",
"light": "GitHub Light"
}
在 ~/.config/zed/themes/ 目录下创建 JSON 文件即可创建自定义主题。Zed 会自动检测并提供该目录中的任何主题。
使用主题扩展
Zed 支持主题扩展。你可以在扩展面板中浏览并安装主题扩展({#kb zed::Extensions})。
如果想创建自己的主题扩展,请参考Developing Theme Extensions指南。
使用 Language Server 功能
Semantic Tokens
Semantic tokens 利用 language server 提供的类型和作用域信息,实现更丰富的语法高亮。通过 semantic_tokens 设置来启用:
"semantic_tokens": "combined"
"off"— 仅使用 Tree-sitter 高亮(默认)"combined"— 在 Tree-sitter 基础上叠加 LSP semantic tokens"full"— 完全用 LSP semantic tokens 替代 Tree-sitter
你可以在设置中通过 global_lsp_settings.semantic_token_rules 自定义 token 的颜色和样式。
Inlay Hints
Inlay hints 会在代码中内联显示附加信息,比如参数名或推断出的类型。你可以在 settings.json 中配置:
"inlay_hints": {
"enabled": true,
"show_type_hints": true,
"show_parameter_hints": true,
"show_other_hints": true
}
针对特定语言的 inlay hint 设置,请参阅各语言的文档。
Code Actions
Code actions 提供快速修复和重构选项。你可以通过 {#action editor::ToggleCodeActions} 命令调用,或者当有可用操作时,点击光标旁出现的灯泡图标。
跳转定义与查找引用
使用以下命令在代码库中导航:
- {#action editor::GoToDefinition}(f12|f12)
- {#action editor::GoToTypeDefinition}(cmd-f12|ctrl-f12)
- {#action editor::FindAllReferences}(shift-f12|shift-f12)
重命名符号
要在整个项目中重命名某个符号:
- 将光标放在该符号上
- 执行 {#action editor::Rename} 命令(f2|f2)
- 输入新名称并按回车
这些功能依赖于各语言对应 language server 的能力支持。
当重命名跨越多个文件的符号时,Zed 会在多缓冲中打开预览,让你在项目范围内审查所有改动后再应用。确认重命名只需保存该多缓冲;若决定不继续,可撤销更改或在不保存的情况下关闭多缓冲。
悬停信息
使用 {#action editor::Hover} 命令可显示光标下方符号的信息,通常包括类型信息、文档以及指向相关资源的链接。
工作区符号搜索
{#action project_symbols::Toggle} 命令允许在整个项目中搜索符号(函数、类、变量),便于在大型代码库中快速导航。
代码补全
Zed 在输入时提供智能代码补全建议。你也可手动通过 {#action editor::ShowCompletions} 命令触发补全。使用 tab|tab 或 enter|enter 接受建议。
诊断信息
Language server 会在编码过程中实时提供诊断信息(错误、警告、提示)。使用 {#action diagnostics::Deploy} 命令查看项目中所有诊断。
工具链
Zed 项目包含工具链选择器,用于为当前项目中的某种语言选择所用工具。
例如,在 Python 项目中,虚拟环境定义了依赖项和解释器路径。Language server 需要该环境才能正确分析代码。借助工具链选择器,你可以从下拉菜单中挑选合适的虚拟环境,而无需手动配置 language server 路径。
你甚至可以为 Zed 项目中的不同子项目选择不同工具链。子项目的定义因语言而异。在协作场景中,只有项目拥有者能看到并修改激活的工具链。
在 远程项目 中,你可以使用工具链选择器控制 SSH 主机上的激活工具链。当你 共享项目 时,访客无法使用工具链选择器。
为什么需要工具链?
激活的工具链用于启动 Language Server。如果工具链不正确,Language Server 可能无法解析依赖项,像「跳转到定义」或「代码补全」这类功能也就无法正常工作。
激活的工具链也影响终端面板中 Shell 的启动:部分工具链提供 Shell 的「激活脚本」,使其在 Shell 环境中可用,方便你使用。创建新终端时,Zed 会自动执行这些激活脚本。
这同样适用于 tasks。Zed 执行 task 时,相当于你打开了一个新的终端标签页并手动运行 task 命令,所以 task 的执行也受激活工具链及其激活脚本影响。
选择工具链
当前激活的工具链(如果有的话)会显示在右侧状态栏。点击它可打开工具链选择器,或者运行命令面板中的操作({#action toolchain::Select})。
Zed 会根据你正在使用的项目,自动推断一组可供选择的工具链。首次打开项目时,Zed 也会尽力为你默认选中一个。
工具链选择针对的是当前子项目,它可能是你的整个项目,也可能是其中的一部分。例如在 monorepo 中,你可以为每个子项目选择不同的工具链。
手动添加工具链
如果自动检测无法满足需求,你可以手动添加工具链。方法是点击工具链选择器中的「添加工具链」按钮。在那里你可以指定工具链的路径,并给它起一个你喜欢的名字。
Semantic Tokens 与语法高亮 - Zed
Semantic Tokens 利用来自 Language Server 的信息,提供更丰富的语法高亮。不同于基于纯语法的 tree-sitter 高亮,Semantic Tokens 能够理解代码语义——例如区分局部变量和参数,或区分类的定义和类的引用。
启用 Semantic Tokens
Semantic Tokens 由 semantic_tokens 配置项控制。默认情况下是关闭的。
{
"semantic_tokens": "combined"
}
此配置项接受三种值:
| 取值 | 说明 |
|---|---|
"off" |
不向 language server 请求语义 token,只使用 tree-sitter 语法高亮。(默认值) |
"combined" |
将 LSP 语义 token 与 tree-sitter 高亮结合使用:tree-sitter 提供基础高亮,语义 token 叠加额外信息。 |
"full" |
仅使用 LSP 语义 token。对支持语义 token 的缓冲区,tree-sitter 高亮将完全禁用。 |
你可以全局配置,也可以按语言单独配置:
{
"semantic_tokens": "off",
"languages": {
"Rust": {
"semantic_tokens": "combined"
},
"TypeScript": {
"semantic_tokens": "full"
}
}
}
注意:修改
semantic_tokens模式后可能需要重启 language server 才能生效。如果高亮没有立即更新,可以通过命令面板使用 {#action editor::RestartLanguageServer} 命令。
自定义 Token 颜色
语义 token 的样式由一组规则控制,这些规则把 LSP token 类型和修饰符映射到主题样式或自定义颜色。Zed 提供了合理的默认值,但你也可以在 settings.json 中自定义:在 global_lsp_settings.semantic_token_rules 键下添加规则即可。
规则按顺序匹配,命中第一条即生效。优先级从高到低依次为:用户自定义规则、扩展提供的语言规则、Zed 默认规则。
规则结构
每条规则可以指定:
| 属性 | 说明 |
|---|---|
token_type |
要匹配的 LSP 语义 token 类型(如 "variable"、"function"、"class")。省略则匹配所有类型。 |
token_modifiers |
必须全部满足的修饰符列表(如 ["declaration"]、["readonly", "static"])。 |
style |
要依次尝试的主题样式名称列表,使用当前主题中第一个找到的样式。 |
foreground_color"#ff0000")。background_colorunderlinetrue,则使用文本颜色绘制下划线。strikethroughtrue,则使用文本颜色绘制删除线。font_weight"normal" 或 "bold"。font_style"normal" 或 "italic"。示例:高亮未解析引用
让未解析引用更显眼:
{
"global_lsp_settings": {
"semantic_token_rules": [
{
"token_type": "unresolvedReference",
"foreground_color": "#c93f3f",
"font_weight": "bold"
}
]
}
}
示例:高亮不安全代码
高亮 Rust 中的不安全操作:
{
"global_lsp_settings": {
"semantic_token_rules": [
{
"token_type": "punctuation",
"token_modifiers": ["unsafe"],
"foreground_color": "#AA1111",
"font_weight": "bold"
}
]
}
}
示例:引用主题样式
避免硬编码颜色,直接引用主题中的样式:
{
"global_lsp_settings": {
"semantic_token_rules": [
{
"token_type": "variable",
"token_modifiers": ["mutable"],
"style": ["variable.mutable", "variable"]
}
]
}
}
系统会使用当前主题中第一个匹配的样式,后续样式作为回退方案。
示例:禁用某种 Token 类型
要禁用特定 Token 类型的高亮,添加一条匹配它的空规则即可:
{
"global_lsp_settings": {
"semantic_token_rules": [
{
"token_type": "comment"
}
]
}
}
由于用户规则具有最高优先级且首个匹配项生效,这条空规则会阻止任何样式应用于注释 token。
默认规则
Zed 的默认 semantic token 规则将标准 LSP token 类型映射到常见主题样式。例如:
function→function样式- 带
constant修饰符的variable→constant样式 class→type.class、class或type样式(取首个找到的)- 带
documentation修饰符的comment→comment.documentation或comment.doc样式
可通过 {#action zed::ShowDefaultSemanticTokenRules} 命令在 Zed 中查看完整的默认配置。
标准 Token 类型
Language server 使用标准化类型报告 token。常见类型包括:
| 类型 | 描述 |
|---|---|
namespace |
命名空间或模块名 |
type |
类型名 |
class |
类名 |
enum |
枚举类型名 |
interface |
接口名 |
struct |
结构体名 |
typeParameter |
泛型类型参数 |
parameter |
函数/方法参数 |
variable |
变量名 |
property |
对象属性或结构体字段 |
enumMember |
枚举变体 |
function |
函数名 |
method |
方法名 |
macro |
宏名称 |
keyword |
语言关键字 |
comment |
注释 |
string |
字符串字面量 |
number |
数字字面量 |
operator |
运算符 |
常见的修饰符包括:declaration、definition、readonly、static、deprecated、async、documentation、defaultLibrary,以及语言特有的修饰符,例如 Rust 的 unsafe 或 TypeScript 的 abstract。
完整规范请参阅 LSP Semantic Tokens 文档。
查看 Semantic Tokens
想实时查看代码上应用的 semantic tokens,可以在命令面板中使用 {#action dev::OpenHighlightsTreeView} 命令。它会打开一个面板,展示当前 buffer 的所有高亮(包括 semantic tokens),方便你了解哪些 token 被应用了,并调试自定义规则。
故障排查
Semantic 高亮没有显示
- 确认该语言的
semantic_tokens设置为"combined"或"full" - 确认语言服务器支持 semantic tokens(并非所有都支持)
- 尝试用 {#action editor::RestartLanguageServer} 重启语言服务器
- 查看 LSP 日志({#action dev::OpenLanguageServerLogs})中是否有错误
修改设置后颜色没有更新
修改 semantic_tokens 模式后可能需要重启语言服务器。可以在命令面板中使用 {#action editor::RestartLanguageServer}。
主题样式没有生效
请确保规则中的样式名称与主题中定义的样式一致。style 数组提供了备选项——如果第一个样式找不到,Zed 会尝试下一个。