Gopls:基于 Web 的功能

LSP 的 window.showDocument 请求允许服务器指示客户端在编辑器中打开文件或在浏览器中打开网页。这是 gopls 通过 Web 界面报告程序信息的多个功能的依据。

我们认识到,Web 界面并非适合所有人:一些用户偏好全屏编辑器布局,不喜欢切换窗口;另一些用户可能只在没有窗口系统的纯文本终端中工作,或许是通过远程 SSH 或在 Linux 控制台上。不幸的是,LSP 缺乏几种自然的扩展能力,包括服务器定义功能的权限。

  • 可以 泛化 References 查询 并使用相似 UI 元素显示结果的查询;
  • 可以 生成文本流(类似于典型的 shell 命令或编译器),客户端可以将其重定向到编辑器常用的类终端 UI 元素中的命令;或
  • 像 Rename 一样,需要 提示用户 获取额外信息的重构操作。

在 LSP 提供实现这些功能的标准方法之前,基于 Web 的 UI 可以帮助弥补这些不足。

Gopls 的 Web 服务器监听 localhost 端口。为了安全起见,其所有端点都包含一个随机字符串,用作身份验证令牌。客户端在获得服务器提供的身份验证 URL 后,将能够访问您的源代码,但您机器上运行的任意进程将无法访问。重新启动 gopls 进程会导致此密钥更改,使所有现有旧 URL 无效;现有页面将显示一个横幅,表明它们已断开连接。

待办:合并 Web 服务器和调试服务器;请参阅 https://golang.ac.cn/issue/68229

Gopls 支持 Web 浏览器和客户端编辑器之间的双向通信。所有基于 Web 的报告都包含指向您源代码中声明的链接。单击其中一个链接会导致 gopls 向您的编辑器发送一个 showDocument 请求,以在相应行打开相关的源文件。即使您的源代码已被修改但未保存,此功能也有效。(VS Code 用户:如果您希望编辑器在处理此事件时将其窗口置顶,请点赞 microsoft/vscode#208093。)

source.doc:浏览程序包文档

在任何 Go 源文件中,代码操作请求都会返回一个“浏览程序包文档”的命令。此命令会打开一个浏览器窗口,显示当前 Go 程序包的文档,其设计与 https://pkg.go.dev 类似。

这使您可以预览程序包的文档,即使是内部但可能未公开的程序包。重新加载页面会更新文档以反映您的更改。无需保存已修改的 Go 源文件。

单击程序包级别符号或方法的链接(在 pkg.go.dev 中通常会带您进入源代码查看器,例如 GitHub 或 Google Code Search)会导致您的编辑器导航到相关的源文件和行。

客户端支持 (Client support)

  • VS Code:使用“Source Action… > Browse documentation for package P”菜单。
  • Emacs + eglot:在 go-mode 中使用 M-x go-browse-doc
  • Vim + coc.nvim: ??

source.freesymbols:浏览自由符号

在研究代码时,无论是为了理解代码还是评估不同的组织或重构方式,通常都需要了解给定代码块的“输入”,因为您可能正在考虑将其提取到一个独立的函数中,并想知道它需要什么参数,或者只是为了理解长函数中的一个部分如何与前面的部分相关联。

如果您选择一段代码并调用“Browse free symbols” 代码操作,您的编辑器将打开一个浏览器,显示所选内容的自由符号报告。如果一个符号在选择范围内被引用但定义在范围之外,则该符号是“自由”的。本质上,这些是所选代码块的输入。

报告将符号分为导入、本地和程序包级别符号。导入的符号按程序包分组,并链接到该程序包的文档,如上所述。其余每个符号都显示为一个链接,该链接会导致您的编辑器导航到其声明。

待办:解释点路径。

客户端支持 (Client support)

  • VS Code:使用“Source Action… > Browse free symbols”菜单。
  • Emacs + eglot:在 go-mode 中使用 M-x go-browse-freesymbols
  • Vim + coc.nvim: ??

source.assembly:浏览汇编

当您优化代码性能或调查意外崩溃时,有时检查编译器为给定 Go 函数生成的汇编代码会很有帮助。

如果您将光标或选区放在函数 f 内,gopls 会提供“Browse assembly for f” 代码操作。这将打开一个基于 Web 的列表,显示该函数以及其中嵌套的任何函数的汇编代码。

每次编辑源代码并重新加载页面时,当前程序包都会重新编译并更新列表。无需保存已修改的文件。

编译器的目标架构与 gopls 分析文件时使用的架构相同:通常是您机器的 GOARCH,但当查看带有构建标签(例如名为 foo_amd64.go 或包含注释 //go:build amd64 的文件)的文件时,标签会决定架构。

每条指令都会显示一个链接,该链接会根据调试信息,使您的编辑器导航到负责该指令的源代码行。

上图显示了 time.NewTimer 的 arm64 汇编列表。请注意,指示的指令链接到 syncTimer 函数内部的源代码位置,因为编译器内联了从 NewTimersyncTimer 的调用。

目前不支持为泛型函数、程序包初始化器(func init)或测试程序包中的函数浏览汇编。(欢迎贡献!)

客户端支持 (Client support)

  • VS Code:使用“Source Action… > Browse GOARCH assembly for f”菜单。
  • Emacs + eglot:在 go-mode 中使用 M-x go-browse-assembly
  • Vim + coc.nvim: ??

source.splitPackage:拆分程序包为组件

基于 Web 的“Split package”工具可以帮助您将一个复杂的程序包拆分成两个或多个组件,确保这些组件之间的依赖关系是无环的。

按照页面上的说明选择一组命名组件,将每个声明分配给最合适的组件,然后可视化由一个符号到另一个符号的引用所产生的组件之间的依赖关系。

下图显示了该工具在 fmt 程序包上的操作,该程序包(原则上)可以拆分成三个子程序包:一个用于格式化(Printf 及其相关函数),一个用于扫描(Scanf),以及一个用于它们之间的共同依赖。

(尝试在这个程序包上玩这个工具:这是一个有益的练习。下图显示了解决方案。)

该工具目前不执行代码转换(将声明移到新程序包,重命名符号以根据需要导出它们),但我们希望在未来的版本中添加此功能。

客户端支持 (Client support)

  • VS Code:使用“Source Action… > Split package P”菜单。

本文档的源代码可以在 golang.org/x/tools/gopls/doc 下找到。