我一直没有使用 Vim 插件管理插件。我没数过,但是三四个实现肯定是有的。我不喜欢它们的原因,一是迁移成本,二是依赖太巨大:我在一个新环境上配置 Vim 时,我不希望必须访问 GitHub,而且是几十个 clone,这得多慢啊……而且这还意味着它需要有 git,能够联网,DNS 解析正常,等等。这些在各种奇怪的环境里都不容易实现啊。
后来,Vim 8 出来了,内建 packages 管理功能。不愧是官方出品,没有奇怪的依赖。插件来源用户随意,把文件放对位置就可以了。
然后,我又遇见了 git-subrepo,给了 submodule 以外的选择。简单来说它有点 vendoring 的感觉,把第三方仓库放自己里边了。如果目标没有安装 git-subrepo,也没有关系,所有文件都在这儿了,只是没有与第三方仓库同步的能力了而已。
而且,git-subrepo 和 git-notes 类似,它使用额外的 ref 来存储第三方仓库的数据。但与 notes 不同的是,subrepo 已经存在于第三方仓库了,所以我不必把 subrepo 的 ref 推得到处都是,还在每个地方 fetch 下来浪费空间。万一第三方仓库没了或者搬家了,关系也不大,反正我用的代码还在我的仓库里呀。
git-subrepo 是很棒的「pay as you go」设计呢。
有了这两个东西,我就可以尝试一下新的 Vim 插件管理方案了。刚好我需要安装 vim-go,所以就开始尝试啦~
于是开始安装:
cd ~/.vim git subrepo clone git@github.com:fatih/vim-go.git pack/go/start/vim-go
Vim 8 会自动加载pack/*/start/*
下的东西(它下边的目录跟原来的 ~/.vim 布局和用法一样),:packadd
命令会加载pack/*/opt/*
下的东西。
然后记得设置环境变量。首先是 GOPATH,这是放所有 Go 的源码和编译出来的库和二进制文件的地方。Go 1.8 开始默认 ~/go,但是也需要设置 GOPATH,不然 vim-go 不认。然后得把 $GOPATH/bin 加到 PATH 里去。
确认环境变量设置好之后,打开一个新的 Vim,执行:GoInstallBinaries
命令安装依赖。如果中途有神秘失败的,可以把地址拷出来,在命令行里手动调用 go install xxx 安装。部分软件需要访问 Google 的服务器,国内用户注意自行配置一下国际互联网的访问。
安装好之后就有补全、自动格式化、自动加 imports、重命名什么的啦。具体看 vim-go 的文档吧。不过有几点我稍微调整了一下。
我使用 syntastic 来进行代码检查。它默认不启用针对 Go 的检查,需要配置一下:
let g:syntastic_go_checkers = ['go', 'golint']
syntastic 的显示效果比 :GoMetaLinter 命令使用 quickfix 窗口的要好很多哦。
另外不要使用 :GoLint 这个命令,它连非常简单的未定义变量都检查不出来。
而且 vim-go 好 chatty,连跳转到定义都要用显眼的颜色来报告一下成功了……安静真的很珍贵的说。
:GoDoc 我还不会用,因为它报错了:
vim-go: gogetdoc: couldn't get package for .../t.go: can't find package containing .../t.go^@
写 Go 的时候,特别是调试的时候,加句fmt.Printf
得记得修改 import,不需要了还得再去删掉才能通过编译……Go 编译是快了,但是处理未使用变量比等待编译结束更令人心烦啊。
有了 goimports 这个命令之后,不需要在开发过程中反复手动修改导入的包列表了。它可以自动添加和删除 import。不过 vim-go 并没有提供在保存时自动调用的支持,所以我就自己动手了。
首先禁用掉它自己的自动格式化:
let g:go_fmt_autosave = 0
然后,是我自己设置的自动命令:
if !exists('#GoImports') && executable("goimports") augroup GoImports au! autocmd BufWrite *.go silent call s:GoImports() augroup END endif function! s:GoImports() let pos = getpos('.') let na = line('$') %!goimports if v:shell_error undo endif let nb = line('$') let pos[1] = pos[1] + nb - na call setpos('.', pos) endfunction
还有待提高(比如 undo 的处理),但至少能用。