好的编程语言搭配好的开发工具,那必定是如虎添翼。Gopher 们应该都知晓 GoLand,这是 IDEA 专门为 Go 语言开发的集成开发环境(IDE)。此前 IDEA 对 Go 的支持是通过插件的,后来开发独立的 IDE,可见 IDEA 看到了 Go 的发展和前景。
今天这篇文章,主要给大家介绍如何将 VSCode 打造成为一个强大的 Go 开发工具。
vscode-go 插件
打开 VSCode,切换到扩展搜索界面,输入 go 搜索;或者打开一个 Go 源文件,VSCode 会建议你安装 vscode-go 插件。
之所以叫 vscode-go,是因为在 GitHub 的项目名是这个,而在 VSCode 中,插件的名称是 Go。该插件最初是微软维护的,目前已经交给 Go Team 维护。
安装完后,该插件会提示你安装它的一些依赖。如果没有提示,可以点击 Analysis Tools Missing。最后点击 Install 安装。
在 Output 窗口会看到类似如下的输出:
Tools environment: GOPATH=/Users/xuxinhua/go Installing 13 tools at /Users/xuxinhua/go/bin in module mode. gocode gopkgs go-outline go-symbols guru gorename gotests dlv gocode-gomod godef goimports golint goplsInstalling github.com/mdempsky/gocode (/Users/xuxinhua/go/bin/gocode) SUCCEEDED Installing github.com/uudashr/gopkgs/v2/cmd/gopkgs (/Users/xuxinhua/go/bin/gopkgs) SUCCEEDED Installing github.com/ramya-rao-a/go-outline (/Users/xuxinhua/go/bin/go-outline) SUCCEEDED Installing github.com/acroca/go-symbols (/Users/xuxinhua/go/bin/go-symbols) SUCCEEDED Installing golang.org/x/tools/cmd/guru (/Users/xuxinhua/go/bin/guru) SUCCEEDED Installing golang.org/x/tools/cmd/gorename (/Users/xuxinhua/go/bin/gorename) SUCCEEDED Installing github.com/cweill/gotests/... (/Users/xuxinhua/go/bin/gotests) SUCCEEDED Installing github.com/go-delve/delve/cmd/dlv (/Users/xuxinhua/go/bin/dlv) SUCCEEDED Installing github.com/stamblerre/gocode (/Users/xuxinhua/go/bin/gocode-gomod) SUCCEEDED Installing github.com/rogpeppe/godef (/Users/xuxinhua/go/bin/godef) SUCCEEDED Installing golang.org/x/tools/cmd/goimports (/Users/xuxinhua/go/bin/goimports) SUCCEEDED Installing golang.org/x/lint/golint (/Users/xuxinhua/go/bin/golint) SUCCEEDED Installing golang.org/x/tools/gopls (/Users/xuxinhua/go/bin/gopls) SUCCEEDED
目前因为 gopls 还属于 Beta 阶段,默认情况下未启用。因此你的输出应该没有 gopls 的安装。一旦启用了 gopls,VSCode 会提示你安装 gopls,确认安装即可。
注意:因为以上工具有些需要科学上网才能下载,因此请务必做了如下的配置,启用 GOPROXY:
go env -w GOPROXY=https://goproxy.cn,direct
同时建议 Go 版本 1.13+
还有一个小提示:自从有了 Module,GOPATH 渐渐淡出视野。然而,目前 go get 安装 binary 会安装到默认的 GOPATH (即 $HOME/go),为了让上面那些工具方便使用,建议将$HOME/go/bin 加入 PATH 环境变量中。(你可以通过 VSCode 的配置:go.toolsGopath 修改工具的安装位置)。
此外,可以通过 Command Palette 命令窗口,搜索 Go: Install/Update Tools 来更新或安装上面的工具。
这些工具的作用
上面安装了一堆的工具,正是因为类似的工具,让 VSCode 这样的文本编辑器可以更好地开发 Go 语言项目。
gocode 和 gocode-gomod
在早期,gocode 对于使用 Sublime Text 之类开发 Go 语言项目的小伙伴来说,功不可没。最早的项目是 https://github.com/nsf/gocode,之后不维护,mdempsky fork 了一份,继续维护 https://github.com/mdempsky/gocode。然而,Go 1.11 开始,由于 Module 的出现,gocode 不再好使,因为它只支持 GOPATH 项目,于是又出现了另一个 fork:https://github.com/stamblerre/gocode,这就是 gocode-gomod。
然而,随着 gopls 的出现,以上三个项目都建议直接使用过 Go Language Server,即 gopls。因此对于 gocode,你可以忽略。
gopkgs
这是 go list all 命令的替代者,用于列出可用的 Go 包,速度比 go list all 更快。
go-outline
将 Go 源码中的声明提取为 JSON 的工具。
go-symbols
用于从 go 源代码树中提取包符号的 JSON 表示。
guru
为编辑器提供 Go 代码导航功能的工具。Go 官方提供。用户手册见:http://golang.org/s/using-guru。由于有了 gopls,这个不需要了。
gorename
从名称就知道是干嘛的。
gotests
从源代码自动生成 Go 测试样板文件。
delve
不用介绍吧,这是专为 Go 的调试器。
godef
查找源码中的符号(symbols)信息。
goimports
自动导入缺失或移除多余的 import。同时还兼带有 gofmt 的功能。
golint
官方的 Go 源码 linter。实际中大家更喜欢 golangci-lint,它更快,支持并行,而且可以使用缓存,支持 yaml 配置等。VSCode 的配置中支持修改 Linter Tool,默认使用的 golint。当你修改为其他的,而系统没有安装对应的工具时,VSCode 会提示你安装。另外,从 revive 的项目中看到,使用它的也不少。
小结
随着你修改 VSCode 的配置,可能还会安装其他的工具,这里不一一介绍。你遇到了,看一下它的 GitHub 首页,就大概知道它的用途了。你也可以在这里查看到 vscode-go 插件使用的所有工具列表:https://github.com/golang/vscode-go/blob/master/docs/tools.md,将依赖的工具大概分成了 4 大类:工具链、文档类、格式化类和诊断类。
另外值得一提的是,当你使用 gopls 时,大部分的工具是不需要的。
配置 vscode-go 插件
vscode-go 插件几乎是开箱即用的。但由于目前 gopls 默认未启用,需要做一些简单的配置。先针对 go 和 go.mod 进行如下配置:(与是否启用 gopls 无关)
"[go]": { "editor.formatOnSave": true, "editor.codeActionsOnSave": { "source.organizeImports": true, }, // Optional: Disable snippets, as they conflict with completion ranking. "editor.snippetSuggestions": "none", },"[go.mod]": { "editor.formatOnSave": true, "editor.codeActionsOnSave": { "source.organizeImports": true, },},
其他配置的核心围绕着 gopls 进行,官方建议,如果你使用了 Module,你应该启用 gopls。不过启用 gopls 之前你需要确保:
你的 Go 版本 >= 1.12;
你的项目使用了 Module;
如果你还在使用低版本或使用 GOPATH,建议你该升级了。Module 是未来,使用 VSCode,gopls 也是未来。
打开 VSCode 的配置,找到 Extensions 中的 Go,发现配置项不少。大部分都是针对上面那一堆工具的配置。可见这个扩展的功能最初是通过使用上面一系列的命令行工具实现的。这引入了复杂性,因为每个特性都是由不同的工具提供的。在上篇介绍 LSP 的文章中提到,Language Server 使所有编辑器支持所有编程语言,而不需要这些个性化的工具。它们还提供了速度改进,因为它们可以缓存和重用结果。
如果你就是不想使用 gopls,这里列出了该插件支持的所有配置 https://github.com/golang/vscode-go/blob/master/docs/settings.md#detailed-list,在众多配置中,如果某个配置有这样的语句:Not applicable when using the language server ,表示 gopls 模式下该配置无效。
因此,我们不纠结之前的那些,只关注 gopls 相关的配置。(可以通过打开 Command Palette,搜索 Open Settings,直接打开配置文件)
启用 gopls
打开 VSCode 配置界面,定位到 Extensions -> Go 中,找到 Use Language Server,勾选上。
对应的配置是:"Go.useLanguageServer": true。如果你本地没有安装 gopls,会提示安装。如果没有提示,可以运行 Go: Install/Update Tools 命令并选择 gopls 进行安装。当 gopls 有更新时,VSCode 会自动更新。
配置 gopls
针对 gopls 有三项配置:
go.languageServerExperimentalFeatures:允许你禁用某些功能,一些实验性的特性;支持 diagnostics 和 documentLink,分别表示禁用诊断警告和禁用文档链接;一般不需要配置;
go.languageServerFlags:允许将 flags 传递给 gopls 进程;这个需要先了解下 gopls 命令的 flags;
gopls:目前 VSCode 不认,但起作用;
关于第 3 个配置 gopls,支持的配置列表参考:https://github.com/golang/tools/blob/master/gopls/doc/settings.md,比如:
"gopls": { "usePlaceholders": true, "completeUnimported": true }
关于第 2 个配置,在后面专门介绍。一般我们只需要设置如下配置即可,vscode-go 的配置就算完成。
"go.useLanguageServer": true, "[go]": { "editor.formatOnSave": true, "editor.codeActionsOnSave": { "source.organizeImports": true, }, // Optional: Disable snippets, as they conflict with completion ranking. "editor.snippetSuggestions": "none", },"[go.mod]": { "editor.formatOnSave": true, "editor.codeActionsOnSave": { "source.organizeImports": true, },},"go.trace.server": "verbose", "gopls": { // Add parameter placeholders when completing a function. "usePlaceholders": false, // If true, enable additional analyses with staticcheck. // Warning: This will significantly increase memory usage. "staticcheck": false, },"go.languageServerFlags": [ "-remote=auto", "-logfile=auto", "-debug=:0", "-rpc.trace", ]
再谈 gopls
gopls 涉及到的内容很多,这里主要聊聊和 VSCode 编辑器相关的部分。
当在 VSCode 中启用 Use Language Server 时,它会启动一个 gopls 进程,即它就是 LSP 的实现,VSCode 通过 vscode-go 和 gopls 通讯。
看看 gopls 命令提供了哪些功能:
$ gopls -h The Go Language source tools.Usage: gopls [flags] <command> [command-flags] [command-args]gopls is a Go language server. It is typically used with an editor to provide language features. When no command is specified, gopls will default to the 'serve' command. The language features can also be accessed via the gopls command-line interface.Available commands are:main: serve : run a server for Go code using the Language Server Protocol version : print the gopls version information bug : report a bug in gopls features: check : show diagnostic results for the specified file definition : show declaration of selected identifier folding_ranges : display selected file's folding ranges format : format the code according to the go standard highlight : display selected identifier's highlights implementation : display selected identifier's implementation imports : updates import statements inspect : inspect server state (daemon mode only) links : list links in a file prepare_rename : test validity of a rename operation at location references : display selected identifier's references rename : rename selected identifier signature : display selected identifier's signature fix : apply suggested fixes symbols : display selected file's symbols workspace_symbol : search symbols in workspace gopls flags are: -debug string serve debug information on the supplied address -listen string address on which to listen for remote connections. If prefixed by 'unix;', the subsequent address is assumed to be a unix domain socket. Otherwise, TCP is used. -listen.timeout duration when used with -listen, shut down the server when there are no connected clients for this duration -logfile string filename to log to. if value is "auto", then logging to a default output file is enabled -mode string no effect -ocagent string the address of the ocagent (e.g. http://localhost:55678), or off (default "off") -port int port on which to run gopls for debugging purposes -profile.cpu string write CPU profile to this file -profile.mem string write memory profile to this file -profile.trace string write trace log to this file -remote string forward all commands to a remote lsp specified by this flag. With no special prefix, this is assumed to be a TCP address. If prefixed by 'unix;', the subsequent address is assumed to be a unix domain socket. If 'auto', or prefixed by 'auto;', the remote address is automatically resolved based on the executing environment. -remote.debug string when used with -remote=auto, the -debug value used to start the daemon -remote.listen.timeout duration when used with -remote=auto, the -listen.timeout value used to start the daemon (default 1m0s) -remote.logfile string when used with -remote=auto, the -logfile value used to start the daemon -rpc.trace print the full rpc trace in lsp inspector format -v verbose output -vv very verbose output
相关的子命令和 flags 不少。
默认情况下,每次启动一个 VSCode 窗口,gopls 进程就会多一个。因为 gopls 需要维护大量的缓存,方便对编辑的源代码进行分析。因此,这种工作模式会导致 gopls 占用太多资源。
为了解决此类问题,gopls 支持一种新的模式,即启动一个单一的、持久的、共享的 gopls “守护进程” 负责管理所有 gopls 会话。在这种模式下,编辑器的每一个窗口依然会启动一个新的 gopls,不过这个 gopls 只是充当转发器,负责将 LSP 转发到那个共享的 gopls 实例,并记录相关指标、日志和 rpc 跟踪,因此这个 gopls 占用资源很少。
要使用共享 gopls 实例,必须有一个守护进程。你可以手动启动,不过更方便的是让 gopls 转发器进程根据需要启动共享守护进程。具体来说是使用 -remote=true 这个 flag:
gopls -remote=auto -logfile=auto -debug=:0 -remote.debug=:0 -rpc.trace
对于 VSCode 来说就是上文看到的如下配置:
"go.languageServerFlags": [ "-remote=auto", "-logfile=auto", "-debug=:0", "-rpc.trace", ]
这将导致该进程在需要时自动启动 gopls 守护进程,连接到它并转发 LSP。
注意,在没有连接客户端的情况下,共享 gopls 进程将在一分钟后自动关闭。
关于共享 gopls 更多的内容,可以查看 https://github.com/golang/tools/blob/master/gopls/doc/daemon.md 文档。
另外上面配置中还有一个未提到:
"go.trace.server": "verbose",
这用于在 Output 中输出客户端和 gopls Server 的通讯,方便调试,根据需要开启。
此外,你应该想到了,gopls 还支持远程开发。(上篇文章有人问,如果没有网络怎么办。gopls 默认是在本地启动服务的,所以不需要有网络,但这个远程开发就需要有网络了)对远程开发感兴趣的,可以查看文档:https://github.com/golang/tools/blob/master/gopls/doc/vscode.md#vscode-remote-development-with-gopls。
体验下强大的 VSCode
到这里,一个强大的 Go 语言开发环境就搞定了。来体验一下吧。
以 studygolang 源码为例,下载源码:
$ git clone https://github.com/studygolang/studygolang
打开 VSCode,选择 File -> Open… 打开 studygolang 文件夹。打开 main.go 文件,分别尝试如下功能:
Code completion:输入 fmt.Println 试试,看是否能正确提示;
Hover:光标悬停在某个 symbol 上,看是否能正确出现文档提示;
Jump to definition:按住 Command(MacOS)或 CTRL(Linux 或 Windows) 点击某个 symbol,能否正确跳转到定义;
Find references:在某个 symbol 上按 Shift + F12,能否正确显示引用处;
。。。
不出意外,以上功能都应该正常。
另外就是调试,在玩转 VSCode 系列教程第一篇已经简单介绍了调试功能,这里不重复。
聊聊 Lint
Lint 是一个很有用的工具,各语言都会有。Go 语言也不例外,官方有一个工具 golint。然而,大家更喜欢第三方的 lint 工具,因为无论在性能、功能还是可定制性方面都更强大。VSCode 目前默认使用 golint,但还支持另外三种 lint 工具:
golangci-lint
revive
staticcheck
这三个工具都不错,其中 staticcheck 还受到了 Google 和 Go 的赞助,因此有人建议废弃官方的 golint,同时将 staticcheck 设为默认,当然也有建议 revive 的,相关 issue[1]。不过已经确认的是 golint 会冻结、废弃:issue 38968[2]。
这三个工具,每一个都涉及到不少内容。不过基本都是开箱即用,另外可以根据自己的需要进行定制。我目前没有切换,还是用的 golint,原因有 2:
golint 目前基本够用,没有过多折腾这块;
golint 的输出在 VSCode 的 PROBLEMS 窗口,而其他三个都在 OUTPUT 窗口。不喜欢;
期待哪天它们三个中的某个转正吧。
和 GoLand 还有差距?
之前文章有人提到,VSCode 是否可以做到和 GoLand 类似的,将第三方依赖在 Explorer 显示。好比 GoLand 的 External Libraries。
研究了一下,可以这么实现。
打开 VSCode,将某个项目加入,例如上面 studygolang;
File -> Save Workspace As… 保存 Workspace,比如命名为 studygolang;
在 Explorer 中单击右键,选择 Add Folder to Workspace…,找到 module 第三方库的路径,一般是 $HOME/go/pkg/mod;
打开 studygolang.code-workspace,folders 改为:
"folders": [ { "name": "studygolang", "path": "." }, { "name": "External Libraries", "path": "../../../go/pkg/mod" }]
注意两个 name 的值。保存后,Explorer 变成这样:
当浏览代码导航到依赖的库时,左边 Explorer 也会定位到相应的目录。整体和 GoLand 还是类似的。
最后建议一个 Go 项目一个 VSCode 窗口,这样不会乱。
总结
讲了这么多,VSCode 打造为 Go 的开发环境,你还满意吗?看看我的 VSCode 界面:
注:左边 Explorer 漂亮的文件和文件夹图标使用的是 vscode-icons 插件。