首页
/ Zed 中的 Go 语言支持:gopls 配置、Inlay Hints、Code Lens 与 Delve 调试实战

Zed 中的 Go 语言支持:gopls 配置、Inlay Hints、Code Lens 与 Delve 调试实战

2026-09-07 09:10:36作者:何将鹤

Zed 内置了完整的 Go 语言支持,将解析(Tree-sitter)、静态分析(gopls)与调试(Delve)三大能力开箱即用地集成进编辑器。本文基于官方文档(见 docs/src/languages/go.md),结合仓库源码(crates/languages/src/go.rscrates/dap_adapters/src/go.rs 等)逐层拆解 Go 环境的初始化、内联提示与代码镜头开关、基于 Delve 的四种调试模式,以及 Tailwind CSS 语言服务器在 Templ 中的联动用法。读完你可以按自己的项目配置一套完整的"编辑—提示—测试—调试"Go 工作流。

Go 支持全景:解析器、语言服务器与调试适配器

文档开头明确了 Zed 对 Go 的三层技术底座:

  • 语法解析(Tree-sitter):使用 tree-sitter-go.go 文件提供语法树与高亮;
  • 语言服务器(Language Server):使用 Go 官方工具链中的 gopls,负责补全、诊断、重构、语义高亮等;
  • 调试适配器(Debug Adapter):使用 delvedlv)作为 Go 程序的 DAP 调试后端。

该结论在仓库源码中得到印证。语言注册表 crates/languages/src/lib.rs 中,gogomodgowork 三种语言均挂载了 go_lsp_adapter 与共享的 go_context_provider,而 go 语言还额外注册了语义 Token 规则(go::semantic_token_rules())。语法层面,crates/grammars/src/grammars.rsgogomodgowork 分别映射到 tree-sitter-gotree-sitter-go-modtree-sitter-gowork;对应的高亮与可运行项查询位于 crates/grammars/src/go/highlights.scmcrates/grammars/src/go/runnables.scmcrates/grammars/src/gomod/highlights.scmcrates/grammars/src/gowork/highlights.scm

Setup:以官方 Go 模块工具安装 gopls

文档给出一个明确建议:用 Go 自身的包管理工具安装 gopls,而不要用 Homebrew 或 Linux 发行版的包管理器,因为 go install 能保证 gopls 与你的 Go 工具链版本匹配、始终可更新到最新版,避免发行版滞后导致的协议不兼容。

  1. 先彻底卸载系统包管理器安装的任何 gopls 版本:
# MacOS homebrew
brew remove gopls
# Ubuntu
sudo apt-get remove gopls
sudo snap remove gopls
# Arch
sudo pacman -R gopls
  1. 使用 Go 模块工具安装/更新到最新版:
go install golang.org/x/tools/gopls@latest
  1. 确认 gopls 位于 PATH 中:
which gopls
gopls version

如果 gopls 找不到,通常需要把 Go bin 目录加入 shell 配置,例如在 .zshrc / .bash_profile 中添加:

export PATH="$PATH:$HOME/go/bin"

源码层面:Zed 如何发现并兜底安装 gopls

从源码看,GoLspAdapter 实现了完整的"优先用户安装、失败自动安装"逻辑(见 crates/languages/src/go.rs):

  • 发现用户安装的二进制check_if_user_installed 通过 which("gopls") 定位 PATH 中的可执行文件,并附加统一启动参数 -mode=stdio(见 crates/languages/src/go.rs),即走 LSP 标准的 stdio 通道;
  • 自动安装兜底:当 gopls 不在 PATH 中时,Zed 会先探测 go 命令是否可用——若 go 都不存在,则会弹出通知并中止安装;否则执行 go install golang.org/x/tools/gopls@latest(源码中显式设置 GO111MODULE=onGOBIN 指向 Zed 的托管目录);
  • 版本化缓存:安装完成后,二进制会被重命名为形如 gopls_{gopls版本}_go_{go版本} 的文件进行缓存与去重(gopls_{version}_go_{go_version}),再次启动时可直接复用已有缓存,避免反复重装。因此即使你手动安装失败,只要系统中有 go,Zed 也能自行完成 gopls 的拉取与维护。

值得一提的是,Zed 启动 gopls 时还会注入一组默认初始化选项,默认配置中 usePlaceholdersfalse,并且内联提示、test 代码镜头与语义 Token 均按下面两节所述默认开启(见 crates/languages/src/go.rs)。

Inlay Hints(内联提示)默认值与覆盖方式

Zed 在启动 gopls 时会下发以下 hints 初始化选项,让语言服务器在编辑器设置中启用了内联提示时返回对应提示:

"hints": {
    "assignVariableTypes": true,
    "compositeLiteralFields": true,
    "compositeLiteralTypes": true,
    "constantValues": true,
    "functionTypeParameters": true,
    "parameterNames": true,
    "rangeVariableTypes": true
}

各选项含义简述:

选项 作用
assignVariableTypes :=/= 赋值处内联显示推导出的变量类型
compositeLiteralFields 在复合字面量中内联显示字段名
compositeLiteralTypes 在复合字面量中内联显示类型名
constantValues 内联显示常量值
functionTypeParameters 内联显示函数调用的类型参数(泛型)
parameterNames 在调用处内联显示形参名(用于字面量参数)
rangeVariableTypes range 循环变量处内联显示类型

若希望针对项目做定制,可在项目 .zed/settings.json 或全局用户设置中通过 lspgoplsinitialization_options 覆盖默认值:

"lsp": {
    "gopls": {
        "initialization_options": {
            "hints": {
                // 只需列出要覆盖的项,未列出的保持 Zed 默认值
                "parameterNames": false
            }
        }
    }
}

覆盖并非"整体替换":源码中 Zed 先把上述默认配置构造为 JSON,再通过 merge_json_value_into 将用户在 initialization_options 中提供的覆盖项递归合并进默认配置(见 crates/languages/src/go.rs),因此你只需书写想要改动的键。文档中也说明,更多选项细节可查阅 gopls 官方文档的 inlayHints 章节。

Code Lens(代码镜头):默认开启 test,运行测试/基准测试

Zed 默认只为 gopls 开启 test 代码镜头(源码中默认配置为 "codelenses": { "test": true },见 crates/languages/src/go.rs)。开启后,*_test.go 文件中的 TestBenchmark 函数上方会出现 "run test" 与 "run benchmark" 之类的可点击入口。

要看到这些入口,需要先在设置中启用代码镜头能力:

{
  "code_lens": "on"
}

然后可在 settings.json 中按需覆盖默认的代码镜头集合(除 test 外,还可启用 generatetidyvendor 等针对 go.mod 的维护动作):

{
  "lsp": {
    "gopls": {
      "initialization_options": {
        "codelenses": {
          "test": true,
          "generate": true,
          "regenerate_cgo": true,
          "tidy": true,
          "upgrade_dependency": true,
          "vendor": true
        }
      }
    }
  }
}

关于各 codelenses 行为的完整说明可参考 gopls 官方文档中的 code lenses 章节。

代码镜头背后:点击后发生了什么

当你在代码镜头上点击运行时,gopls 会通过 LSP 的 workspace/executeCommand 发送 gopls.run_tests 命令。Zed 的 client_command 专门拦截该命令,并将其转换为一条 go test 任务调度(见 crates/languages/src/go.rs)。任务模板在 crates/languages/src/go.rs 中构造:

  • 测试模式会附加 -test.fullpath=true、默认 -timeout 30s,并通过 -run 的正则精确匹配函数名(单个测试用 ^TestXxx$,多个用 ^(A|B)$);
  • 基准测试模式会追加 -benchmem-run=^$ 与对应的 -bench 正则;
  • 工作目录取自触发代码镜头所在文件的 URI 的父目录。

这也解释了为什么 Zed 中 gopls 的 test code lens 能"一键"跑起单个用例,而无须离开编辑器。

Debugging:基于 Delve 的零配置调试

Zed 对 Go 测试与入口点(func main)支持零配置调试。直接运行 {#action debugger::Start} 动作(快捷键见你的 keymap 中 debugger::Start 绑定),即可看到 Zed 根据当前上下文(光标所在文件/测试)预生成的一批调试任务。

需要更多控制时,可在项目根目录创建 .zed/debug.json 添加自定义调试配置,所有配置项由内置 Delve 适配器 GoDebugAdapter 提供 schema 校验(其字段定义见 crates/dap_adapters/src/go.rs)。下面按文档整理四种典型场景。

说明:Delve 官方文档中有 launch / attach 配置的完整说明,本文只覆盖 Zed 中最常用的形式。

场景一:调试 Go 包(debug 模式)

调试某个具体包时,把 Delve 的 mode 设为 "debug",此时 program 应填写包名或包所在目录:

[
  {
    "label": "Go (Delve)",
    "adapter": "Delve",
    "program": "$ZED_FILE",
    "request": "launch",
    "mode": "debug"
  },
  {
    "label": "Run server",
    "adapter": "Delve",
    "request": "launch",
    "mode": "debug",
    // For Delve, the program can be a package name
    "program": "./cmd/server"
    // "args": [],
    // "buildFlags": [],
  }
]

$ZED_FILE 是 Zed 内置的上下文变量,指当前活动文件;program 也可以直接写形如 ./cmd/server 的包路径。需要为被调试程序传参时放开 args,需要定制编译标志(如构建标签)时放开 buildFlags

场景二:调试 Go 测试(test 模式)

调试某个包的测试时,把 mode 设为 "test"。此时 program 依旧是包名,可用 buildFlags 设置构建标签等编译期选项,用 args 向测试二进制传参(详见 go help testflags):

[
  {
    "label": "Run integration tests",
    "adapter": "Delve",
    "request": "launch",
    "mode": "test",
    "program": ".",
    "buildFlags": ["-tags", "integration"]
    // To filter down to just the test your cursor is in:
    // "args": ["-test.run", "$ZED_SYMBOL"]
  }
]

$ZED_SYMBOL 同样由 Zed 注入,代表光标所在的测试/符号名;配合 -test.run 即可把调试范围收敛到光标所在的单个测试上。

场景三:编译与调试分离(exec 模式)

如果项目需要以特定命令先编译出可执行文件,再对其调试,则使用 Delve 的 "exec" 模式:此时 program 指向编译产物,build 字段定义编译命令。文档给出的示例先用 go test -c 把单元测试编译成二进制,再进行调试:

[
  {
    "label": "Debug Prebuilt Unit Tests",
    "adapter": "Delve",
    "request": "launch",
    "mode": "exec",
    "program": "${ZED_WORKTREE_ROOT}/__debug_unit",
    "args": ["-test.v", "-test.run=${ZED_SYMBOL}"],
    "build": {
      "command": "go",
      "args": [
        "test",
        "-c",
        "-tags",
        "unit",
        "-gcflags\"all=-N -l\"",
        "-o",
        "__debug_unit",
        "./pkg/..."
      ]
    }
  }
]

这里 -gcflags"all=-N -l" 用于关闭优化与内联,保证断点命中的是源码行;-o __debug_unit 指定产物文件名;调试完成后该配置可让 Delve 直接运行预编译好的测试二进制,适合需要精确复现编译参数或明显加快启动速度的场景。${ZED_WORKTREE_ROOT} 是 Zed 内置的工作区根目录变量。

场景四:连接已运行的 Delve 实例(远程调试)

当需要连接一台不在本机运行的 Delve 实例(例如远程开发机或容器中已启动的 dlv dap)时,用 tcp_connection 字段来指定 Zed 连接 Delve 的主机与端口:

[
  {
    "adapter": "Delve",
    "label": "Connect to a running Delve instance",
    "program": "/Users/zed/Projects/language_repositories/golang/hello/hello",
    "cwd": "/Users/zed/Projects/language_repositories/golang/hello",
    "args": [],
    "env": {},
    "request": "launch",
    "mode": "exec",
    "stopOnEntry": false,
    "tcp_connection": { "host": "127.0.0.1", "port": 53412 }
  }
]

该场景下 Zed 不会新启动一个 Delve 进程,而是直接复用已存在的实例。由此带来的结果是:Zed 中不会出现对应的调试终端——被调试程序的 stdin/stdout 由那个外部 Delve 实例直接处理,你需要在其运行的终端里与 Delve 交互(例如查看 fmt.Println 输出)。

Delve 适配器支持哪些配置字段

除了文档示例中出现的字段,crates/dap_adapters/src/go.rs 的 schema 还定义了如下常用选项,可用在 .zed/debug.json 的 launch 配置中:

字段 类型/取值 说明
cwd string 被调试程序的工作目录,默认 ${ZED_WORKTREE_ROOT}
args array / string 传给程序的命令行参数
env / envFile object / string 程序环境变量,或从 .env 文件加载(Zed 会代读 envFile 并合并进 env,见 crates/dap_adapters/src/go.rs
buildFlags array 传给 Go 编译器的标志
mode debug / test / exec / replay / core 调试模式
stopOnEntry boolean 启动或附加后是否自动停在程序入口,默认 false
showLog boolean 是否显示 delve 的 --log 日志
dlvFlags array 透传给 dlv 的额外标志
backend default / native / lldb / rr delve 使用的调试后端
trace verbose / trace / log / info / warn / error DAP 日志级别,默认 error
stackTraceDepth number 堆栈最大深度,默认 50
substitutePath array(from/to 本地与远程路径映射,用于远程调试源码跳转
console internalConsole / integratedTerminal 调试器启动位置

另外,Zed 的 Delve 支持"自动安装":如果系统 PATH 中没有 dlv,Zed 会先确认 go 可用,然后执行 go install github.com/go-delve/delve/cmd/dlv@latest 将其装入托管目录(见 crates/dap_adapters/src/go.rs);实际通信时通过一个 delve-shim-dap 桥接程序把 DAP 转发给 dlv dap(默认监听本机随机端口),只有在指定 tcp_connection 时才直连外部实例而不启用该桥接(见 crates/dap_adapters/src/go.rs)。

Go Mod / Go Sum / Go Work:模块文件的语法支持

.go 源文件外,Zed 对 Go 模块元数据文件也提供基础的语法高亮支持,这些文件均没有独立语言服务器(Language Server: N/A),语言能力由 Tree-sitter 提供:

  • Go Mod:语法来自 camdencheek/tree-sitter-go-mod
  • Go Sum:语法来自 amaanq/tree-sitter-go-sum
  • Go Work:语法来自 tree-sitter-go-work

就当前仓库内置注册表而言,gomodgowork 均作为内置语言注册并复用 gopls 适配器与 Go 上下文(见 crates/languages/src/lib.rs),语法映射见 crates/grammars/src/grammars.rscrates/grammars/src/gomod/config.tomlcrates/grammars/src/gowork/config.toml。你可以据此推断:对 .go.mod / .go.work 这类文件,Zed 的编辑体验以高亮、括号匹配与基于 Tree-sitter 的结构能力为主,符号跳转、诊断等智能能力仍需要依赖打开其所属 Go 模块后 gopls 提供的服务。

在 Templ 文件中启用 Tailwind CSS 语言服务器

Templ 是 Go 生态中一个 HTML 模板语言。如果你希望 .templ 文件里也能获得 Tailwind CSS 的补全与检查能力(例如在 class="..." 中提示颜色、间距等工具类),需要做两件事:把 tailwindcss-language-server 加入 Templ 语言的服务器列表,并告诉 Tailwind 把 .templ 当作 HTML 解析。在 settings.json 中写入:

{
  "languages": {
    "Templ": {
      "language_servers": ["tailwindcss-language-server", "..."]
    }
  },
  "lsp": {
    "tailwindcss-language-server": {
      "settings": {
        "includeLanguages": {
          "templ": "html"
        },
        "experimental": {
          "classRegex": ["class=\"([^\"]*)\""]
        }
      }
    }
  }
}

Note: 与其他语言不同,你必须显式告诉 Tailwind 把 .templ 文件当作 HTML 处理。

配置完成后,.templ 文件中 class="..." 属性内即可获得 Tailwind CSS 的类名补全。这背后的机制与 Zed 内置的 Tailwind 适配器注册方式一致:tailwindcss-language-server 被注册为全局可用的语言服务器,可通过 languages 下的 language_servers 设置为任意语言开启(参见 crates/languages/src/lib.rsregister_available_lsp_adapter 的注册逻辑与注释说明);includeLanguages 让 Tailwind 用 HTML 规则解析 templ,而 classRegex 则指定在哪些属性中识别 class 名。

小结

  • 安装:优先用 go install golang.org/x/tools/gopls@latest 安装 gopls,并确认其在 PATH 中;Zed 也会在 go 可用时自动兜底安装与版本化缓存 gopls。
  • 提示与镜头:Zed 默认开启 7 类 gopls 内联提示与 test 代码镜头,均可通过 lsp.gopls.initialization_options 增量覆盖;运行测试的实际命令由 crates/languages/src/go.rs 中的任务模板生成。
  • 调试.zed/debug.json 支持 debug / test / exec 三种常用 launch 模式及直连现有 Delve 的 tcp_connection 方式;适配器还提供环境变量文件、路径映射、后端选择等进阶字段。
  • 生态联动.go.mod / .go.work 有内置高亮;Templ 场景下可通过 Tailwind CSS 语言服务器获得 HTML 级别的工具类补全。

如果你是 Go 开发并希望把 Zed 作为主力编辑器,建议从新建 .zed/debug.json、放上上面"调试包 / 调试测试"两段最小配置开始,配合 code lens 的 run test 入口即可覆盖绝大多数日常开发与问题排查场景。

登录后查看全文
热门项目推荐
相关项目推荐