前言
距离 Time to Press the Stop Button 已经将近两年,这一波学习热潮总算是接近尾声。 从 My Cli Setup 开始陆陆续续尝试了很多新工具,也重构了不少项目,这些统一在 2025 年度总结 - 生活篇#后记 提到的开发篇再介绍,今天就简单讲讲从 VSCodium(下称 VSC)到 Zed 的迁移过程。
介绍
Zed 算是近两年比较新的编辑器,虽然看上去类似 VSCode,实际上因为基于 tree-sitter 和 LSP,可能还是更贴近 neovim。最早作为闭源 macOS only 编辑器没多少热度,开源并支持 Linux/Windows 后才逐渐起色。
虽然我最早知道 Zed 还是因为 zedless,但后者开发过于缓慢,还不发布二进制,作为单纯的 fork 的意义不大。不像 VSCode -> VSCodium,后者间接推动了 Open VSX Registry 的发展,除了 Eclipse 也有一大堆 VSC fork 在用,避免了 Visual Studio Marketplace 的 vendor lock-in。
迁移
设置
- 虽然 Zed 官网也有 How to Migrate from VS Code to Zed,但写得太简单,只跟着这里走完立马使用只会浑身难受,建议过一遍自定义、设置项的文档再上手。
- 内置的协作工具我觉得没什么用就直接关了,登录也是不可能登录的,直接隐藏按钮。
- OpenCode ACP 简单试了下,效果不是很行。LLM Provider API Key 也是明文储存,非常难绷。虽然可以通过配置收紧默认权限,申请权限也可以拒绝,但拒绝了就停了,权限控制堪称笑话……
file_scan_exclusions对应 VSCfiles.{exclude,watcherExclude},类似的还有file_scan_inclusions,Zed 默认值要合理一点点,但自定义必须补全默认值,略冗长- file_types 逻辑和 VSC files.associations 反着来,更加合理
更新
为了防止供应链攻击 1,我从来不用自动更新,而是通过其他方式管理。
因为 Void 对 fork/Electron 极端排斥,只能下载 .deb 然后用 xdeb 转换成 xbps 再安装。由于 VSC 不支持 xdg-base-dir,安装后还要手动 patch dataFolderName:
sudo sed -i 's|"dataFolderName": ".*"|"dataFolderName": ".local/share/VSCodium"|g' "/usr/share/codium/resources/app/product.json"
Zed 这点做得很好,开箱支持。由于支持架构少了几十种,也不需要在一百多个 GitHub release assets 中翻找 2,更新脚本写起来很简单,动态更新直接 rsync -a --update --delete --links 即可。
备份
Zed 没有 workspace 的概念,打开一个文件夹即是一个 project。Project history 保存在 ~/.local/share/zed/db/*/db.sqlite 的 workspaces 表 3,直接备份数据库即可,避免陷入重装要手动拖几十个文件夹的惨剧。
插件和 language server 列表当然也可以备份 ~/.local/share/zed/{extensions,languages},但都可以在 auto_install_extensions 设置项配置,没必要。
Linter
Zed 统一使用 LSP 对接,也可以调用外部 formatter/linter 处理 stdin,设计很合理。
好处说完了,该开喷了:
- 启动 Zed 会自动下载 Node.js,哪怕系统已安装并且已经添加到 PATH 也如此。妥妥的流氓行径
- 初次打开的文件类型,Zed 会自动下载对应 language server,这挺好,就是偶尔打开一次的文件还要装几百兆依赖有点蠢,好在被骂多了现在添加了提醒。但是,为了方便默认使用万恶的 prettier……
有点跑题了,回到 linter,简单列个表,算作 mise-as-linter#TLDR 的更新。 不过 VSC 用了这么多年,接触过的语言有点多,只能写一下目前已经配置的。
注意
- Linter 和 Language Server 分开列,Zed 内置支持则用括号表示。
- 和 mise-as-linter 类似,这里的 linter 同时指代: linter, formatter, language server, extension。
- 严格来说 LSP 指代 Language Server Protocol,即工具与 Language Server 交互的协议。而且 Zed 插件除了 language server,也有(为 tree-sitter 不支持的格式提供)syntax highlighter。为了简化逻辑,下文刻意混用,统一称作 LSP。
- Zed 文档和插件命名很混乱,为了减少歧义,按以下优先级降序
- settings.json auto_install_extensions 的扩展名
~/.local/share/zed/{languages,extensions/installed}的文件夹名- GitHub zed-extensions 的 repo 名
- Zed 文档中的命名 4
列表
| Language | Linter | LSP |
|---|---|---|
| Bash | shellcheck, shfmt | basher |
| Zsh | shellcheck | / |
| C/C++ | clangd | (clangd) |
| JavaScript | biome | biome |
| TypeScript | biome | vtsls |
| CSS | biome | vscode-css-languageservice |
| HTML | biome | html |
| JSON/JSONC | biome | json-language-server |
| Lua | stylua | lua |
| Markdown | markdownlint-cli2 | / |
| Nginx | / | nginx |
| Nix | / | nix |
| Nushell | nu-lint | nu+nu-lint |
| Python | ruff, ty | ruff+ty |
| R | lintr, styler | r |
| SQL | sqlfluff | sql |
| SystemD | / | ini |
| TOML | tombi | toml+tombi |
| XML | / | xml |
| YAML | / | yaml |
解释
- Zsh
- tree-sitter-bash 对于 zsh 非常排斥,可以看作 wontfix。虽然有新的 tree-sitter-zsh,但还没发布到 zed。
- Linter/Formatter 方面倒是没太大问题,shellcheck 能用,只是有很多误报,shfmt-3.13.0 已经初步支持 zsh,未来可期。
- C/C++: 之前在 VSC 还装了 CodeLLDB,但这么多年我 GDB 都只用过一两回,LLDB 装了也没用,迁移时直接卸载了……
- Lua: 和 C/C++ 类似,从没用过 VSC 的 Lua Debug 插件,这里就不装了,配置 StyLua 为 external formatter
- Markdown
- 默认使用战五渣 prettier,配置 markdownlint-cli2 为 external formatter
- 类似 LaTeX,相比于 VSC 的 Markdown All in One,Zed 的 Markdown 支持只能说够用,但远远不算上好用
- Nginx: 很少需要写 Nginx 配置,gixy 自然用得少,暂时没配置
- Nix: nixfmt 强制一行一个软件包并且不支持使用
nolint:on,nolint:off动态开关,我显然不可能用,只安装插件提供基本的高亮 - Nushell: 因为 nu-lint 不支持 XDG,而且 Zed 解析 settings.json 不支持
~和$HOME这样的环境变量,也没有类似 VSC${userHome}的变量,只能在 nu-lint LSP 配置丑陋的绝对路径
{
"lsp": {
"nu-lint": { "binary": { "arguments": [ "--config", "/home/aaron/.config/nu-lint/config.toml", "--lsp" ] } }
}
}
- Python
- LSP: Zed 虽然支持 ty,但为了兼容性默认会安装 ty, basedpyright, pyright, pylsp 四个 language servers,我直呼有病病?在配置里禁用 ty 之外的 LSP 并禁止自动安装对应插件 5
- Formatter: 默认 ruff(总算是有个正常人),额外配置
source.organizeImports.ruff和source.fixAll.ruff两个 code_actions_on_format
- R
- 除了 Zed 方面需要安装 R 插件,也要安装 languageserver 和 lintr 两个 R 包。
- styler 是 languageserver 自带的 formatter,配合 lintr 够用。很少写 R,暂时没兴趣尝试 Posit(RStudio 开发公司)做的 air。
- SQL: 配置 sqlfluff 为 external formatter
- SystemD: 不是 typo,除了 ini 本体,zed-ini 真的支持 EditorConfig, SystemD Unit , Podman Quadlet 等一堆格式。和 Nginx 类似,很少需要写 SystemD 配置,systemdlint 也很少用,暂时没配置。
- TOML: 之前用的 taplo 很少维护,插件直接被 Zed 移除了,我无语……好在测试下来 tombi 确实更实用,直接迁移
- XML: Formatter 默认使用 prettier,用得很少,懒得和 prettier 斗智斗勇
- YAML
- 虽然装了 yamllint,但因为我很讨厌 YAML 又不用 Docker,基本只用于 GitHub Action,而 Zed 并不支持 zizmor/actionlint,这是少数停留在 VSC 的格式。
- Formatter 默认使用 prettier,好在 yaml-language-server 也能格式化。
Prettier
单独一节专门 fuck prettier🖕biome 也有点 opinionated,但态度比 prettier 好太多,而且可以通过配置关闭。不过现在 oxc (oxfmt/oxlint) 似乎也能替代 biome,有空试试。
- JavaScript: zed 默认用杀千刀的 prettier,必须禁用,这里将 biome 作为 LSP 使用,配置起来更简单。
{
"languages": {
"JavaScript": {
"formatter": "language_server",
"language_servers": [ "biome" ],
"code_actions_on_format": {
"source.fixAll.biome": true,
"source.organizeImports.biome": true
}
},
…
}
}
- TypeScript/TSX: 和 JavaScript 类似,但保留默认 language server
- CSS/HTML: biome 虽然支持 LSP,一样保留默认 language server,只当作 linter/formatter,配置起来也简单。
{
"languages": {
"CSS": { "formatter": { "language_server": { "name": "biome" } } }
}
}
- JSON/JSONC: 配置非常蛋疼,只有将 biome 配置为 external formatter 才能彻底覆盖掉人人喊打的 prettier,补充几行 argument 确保和 biome 全局配置一致,并且不影响 format on save
"JSON": {
"formatter": {
"external": {
"command": "biome",
"arguments": [
"format",
"--trailing-newline=false",
"--files-ignore-unknown=true",
"--stdin-file-path",
"{buffer_path}",
"-"
]
}
},
"prettier": {
"allowed": false
},
"language_servers": [
"json-language-server"
],
"ensure_final_newline_on_save": false
}
其他
该部分统一使用扩展名作为命名。
- ahk: 只支持 v1 格式,不支持 v2,而且只支持 Windows,我就不能 Linux 写 + Windows 测试吗……
- git-firefly: .gitignore 支持
- rainbow-csv: 维护 VNDB-Calendar 和 game-stats 必备
- log: 看似很有用其实没卵用,看日志一般 SSH 过去直接 less/moor,根本没机会用 GUI,更别提还要特地装个 Zed……
- make: 已迁移到 mise,直接不安装
- powershell: 很少写,只装了高亮,凑合用
- go/rust/zig: 自带 formatter,没啥好写的
- graphiviz/mermaid: 用到再安装
- docker-compose, dockerfile, basedpyright, pyright, pylsp, eslint: 禁止安装,防止污染环境,可惜还有很多语言没配置,只能暂且饶 prettier 一条狗命
TODO
- Code Runner: VSC 插件,支持近百种语言的一键运行。Zed 最近的几次更新在 shebang 和 code block 添加了运行按钮,能用,但非常凑合,后面还是得参考 Code Runner 配置 Tasks。
- Code Snippet: Zed 文档写着支持,格式也和 VSC 几乎一样,实测并不能使用,由于 snippet 以别扭的 JSON 格式储存,直接复制也没卵用,只能手动管理……
- LaTeX Workshop: 最近几年很少需要写 LaTeX,TinyTex 都没安装。未来如果有需要,可能尝试 Typst,之前扫过几眼官方 Tutorial、Guide for LaTeX Users 和小蓝书,不过当时顾虑生态,没深入了解,现在应该好上不少。
- zizmor: zizmor LSP 支持还比较早期,就 VSC 做得还行,理论上配合 github-actions 可以凑合用,没搞定。这个都不行,gitleaks/trufflehog/trivy 就更不指望了。
- mise: VSC Mise 插件还挺有用,不过功能很基础,后面可能手搓一个 Zed 版本
软件内置的更新程序是劫持重灾区,就连火绒的更新程序也曾被劫持,好在当年我因为手动配置代理而幸免于难…… ↩︎
为了避免这种情况,我写过 GitHub Release Highlight,预设关键词,在指定 repo 自动高亮对应 asset。不过现在基本都是 mise/scoop/appman/更新脚本,很少用到。 ↩︎
这命名其实有点讽刺,说没有 workspace 但是数据库里储存 project 的 table 就叫 workspace…… ↩︎
看到我最后才考虑破文档中的命名,想必你也能稍稍理解这难以忍受的 inconsistency…… ↩︎
别以为这样就结束了,Zed 随时可能添加新的 language server 并静默安装。因为默认禁用,不手动检查
~/.local/share/zed/languages或者进行磁盘检测你甚至根本发现不了……PyLSP 就是某次更新后自动下载安装的…… ↩︎
