入门 Zed Industries 2026-09-14 17:42:19 · 0 阅读

第54章 Zed 第54章:语言服务器(Language Server)与 Tree-sitter 配置

Zed 的语言支持建立在两项技术之上:

  1. Tree-sitter:负责语法高亮和基于结构的特性,如大纲面板。
  2. 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}"]
      }
    }
  }
}

每种语言都可以自定义大量设置,包括:

这些设置可帮助你在不同语言和项目中保持一致的代码风格。

文件关联

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 简化了用户的语言服务器管理:

  1. 自动下载:当打开匹配文件类型的文件时,Zed 会自动下载相应的语言服务器。对于已知的文件类型,Zed 可能会提示你安装扩展。

  2. 存储位置:

    • macOS: ~/Library/Application Support/Zed/languages
    • Linux: $XDG_DATA_HOME/zed/languages$FLATPAK_XDG_DATA_HOME/zed/languages$HOME/.local/share/zed/languages
  3. 自动更新:Zed 会保持你的语言服务器为最新版本,确保你始终拥有最新的功能和改进。

选择语言服务器

Zed 中的某些语言提供多个语言服务器选项。你可能安装了多个捆绑了针对同一语言服务器的扩展,这可能导致功能重叠。为了确保使用你偏好的功能,Zed 允许你指定使用哪些语言服务器以及它们的优先级顺序。

你可以使用 language_servers 设置来指定你的偏好:

  "languages": {
    "PHP": {
      "language_servers": ["intelephense", "!phpactor", "!phptools", "!phpantom", "..."]
    }
  }

在此示例中:

  • intelephense 被设置为主要语言服务器。
  • phpactorphptoolsphpantom 被禁用(注意 ! 前缀)。
  • "..." 会展开为 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.jsonlsp 部分中设置这些选项:

  "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 过程中遇到问题:

  1. 查看 Zed 日志文件中的错误信息(使用命令面板:{#action zed::OpenLog})
  2. 确认外部工具(格式化工具、Lint 工具)已正确安装并添加到 PATH 中
  3. 检查 Zed 设置和语言特定配置文件(例如 .eslintrc.prettierrc)中的配置是否正确

语法高亮和主题

Zed 提供了语法高亮和主题的自定义选项,允许你调整代码的视觉外观。

自定义语法高亮

Zed 使用 Tree-sitter 语法进行语法高亮。你可以通过 theme_overrides 设置来覆盖默认的高亮规则。

以下示例将注释设置为斜体,并更改字符串的颜色:

"theme_overrides": {
  "One Dark": {
    "syntax": {
      "comment": {
        "font_style": "italic"
      },
      "string": {
        "color": "#00AA00"
      }
    }
  }
}

选择和自定义主题

更改主题:

  1. 使用主题选择器({#kb theme_selector::Toggle})
  2. 或在 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 的颜色和样式。

Semantic Tokens 文档

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

重命名符号

要在整个项目中重命名某个符号:

  1. 将光标放在该符号上
  2. 执行 {#action editor::Rename} 命令(f2|f2
  3. 输入新名称并按回车

这些功能依赖于各语言对应 language server 的能力支持。

当重命名跨越多个文件的符号时,Zed 会在多缓冲中打开预览,让你在项目范围内审查所有改动后再应用。确认重命名只需保存该多缓冲;若决定不继续,可撤销更改或在不保存的情况下关闭多缓冲。

悬停信息

使用 {#action editor::Hover} 命令可显示光标下方符号的信息,通常包括类型信息、文档以及指向相关资源的链接。

工作区符号搜索

{#action project_symbols::Toggle} 命令允许在整个项目中搜索符号(函数、类、变量),便于在大型代码库中快速导航。

代码补全

Zed 在输入时提供智能代码补全建议。你也可手动通过 {#action editor::ShowCompletions} 命令触发补全。使用 tab|tabenter|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_color 使用十六进制格式覆盖背景色。 underline 布尔值或十六进制颜色。若为 true,则使用文本颜色绘制下划线。 strikethrough 布尔值或十六进制颜色。若为 true,则使用文本颜色绘制删除线。 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 类型映射到常见主题样式。例如:

  • functionfunction 样式
  • constant 修饰符的 variableconstant 样式
  • classtype.classclasstype 样式(取首个找到的)
  • documentation 修饰符的 commentcomment.documentationcomment.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 运算符

常见的修饰符包括:declarationdefinitionreadonlystaticdeprecatedasyncdocumentationdefaultLibrary,以及语言特有的修饰符,例如 Rust 的 unsafe 或 TypeScript 的 abstract

完整规范请参阅 LSP Semantic Tokens 文档

查看 Semantic Tokens

想实时查看代码上应用的 semantic tokens,可以在命令面板中使用 {#action dev::OpenHighlightsTreeView} 命令。它会打开一个面板,展示当前 buffer 的所有高亮(包括 semantic tokens),方便你了解哪些 token 被应用了,并调试自定义规则。

故障排查

Semantic 高亮没有显示

  1. 确认该语言的 semantic_tokens 设置为 "combined""full"
  2. 确认语言服务器支持 semantic tokens(并非所有都支持)
  3. 尝试用 {#action editor::RestartLanguageServer} 重启语言服务器
  4. 查看 LSP 日志({#action dev::OpenLanguageServerLogs})中是否有错误

修改设置后颜色没有更新

修改 semantic_tokens 模式后可能需要重启语言服务器。可以在命令面板中使用 {#action editor::RestartLanguageServer}。

主题样式没有生效

请确保规则中的样式名称与主题中定义的样式一致。style 数组提供了备选项——如果第一个样式找不到,Zed 会尝试下一个。

评论 (0)