vim-go
is a wonderful tool that makes working with Go tooling a breeze. Though I’ve been a long time user, I only recently finished reading the entire vim-go-tutorial, and I’d thought I’d capture some of the functionality that I have found either most useful or most used (well, both, actually).
GoBuild
GoCoverage
GoDef
GoDoc
GoFiles
GoImpl
GoImplements
GoInfo
GoMetaLinter
GoPlay
GoRename
GoRun
GoSameIds
GoTest
- Snippets
- Configs
- Summary
- References
GoBuild
This does exactly what you think. Simply call :GoBuild
. Under the covers, it does the following (from the docs):
- No binaries are created; you can call
:GoBuild
multiple times without polluting your workspace. - It automatically cds into the source package’s directory.
- It parses any errors and shows them inside a quickfix list.
- It automatically detects the GOPATH and modifies it if needed (detects projects such as
gb
,Godeps
, etc..). - Runs async if used within Vim 8.0.xxx or NeoVim.
The tutorial suggests to use a nice Vim function. I’ve copied it to my vim.functions
file and have it mapped in my ftplugin/go.mod
file (see below):
function! Build_go_files()
let l:file = expand('%')
if l:file =~# '^\f\+_test\.go$'
call go#test#Test(0, 1)
elseif l:file =~# '^\f\+\.go$'
call go#cmd#Build(0)
endif
endfunction
Depending on the name of the file, this will either compile the test cases or build the program.
See Build it in the vim-go-tutorial.
GoCoverage
This calls go test -coverprofile tempfile
under the hood. It will visually change the syntax of the source code to reflect which code paths have coverage. Very cool.
Call :GoCoverageClear
to clear the syntax, or better yet, use :GoCoverageToggle
.
Want to load the results in the browser? Of course, you do. Call :GoCoverageBrowser
and vim-go
will open a browser window the the results.
See Cover it in the vim-go-tutorial.
GoDef
Easily jump to declarations by putting the cursor over the symbol and calling :GoDef
(and, use the shortcut gd
or Ctrl-]
). This will create a stack that is unwound by the shortcut Ctrl-t
.
Note that the stack consists only of the declarations that you’ve jumped to, not the other cursor positions that you’ve navigated to in the course of exploring the codebase. So, when unwinding the stack with Ctrl-t
, each item that’s popped off the stack is a declaration that you’ve visited, with the interleaving cursor navigation positions ignored (unlike Ctrl-o
, which will visit each cursor location - this is often not what you want).
Incidentally, call :GoDefStack
to view the stack.
See Go to definition in the vim-go-tutorial.
GoDoc
Simply place the cursor over any identifier and call :GoDoc
. Any documentation for that particular symbol will open in a scratch window.
There’s also a handy shortcut, the letter K
. This is overrides the normal shortcut which usually opens a man
page (C
programmers will be familiar with this).
See Documentation lookup in the vim-go-tutorial.
GoFiles
To understand all of the files that make up a particular package, call :GoFiles
in any file, and it will list all of the files that make up the package named in the package
clause at the top.
Note that it does not include test files.
The result will look something like this:
['/home/btoll/projects/validator/validators/configmap.go', '/home/btoll/projects/validator/validators/deployment.go', '/home/btoll/projects/validator/validators/document.go', '/home/btoll/projects/validator/validators/ingress.go', '/home/btoll/projects/validator/validators/kubeclient.go', '/home/btoll/projects/validator/validators/service.go']
See Dependencies and files in the vim-go-tutorial.
GoImpl
GoImpl
uses the tool impl
.
:GoImpl
will dynamically add methods to a struct
type that will satisfy a given interface
.
For example:
package main
type Animal interface {
Talk(string)
Walk() error
}
type A struct{}
Put the cursor on the A
of the struct type and enter the following in ed
:
:GoImpl Animal
This will create the following methods:
func (a *A) Talk(_ string) {
panic("not implemented") // TODO: Implement
}
func (a *A) Walk() error {
panic("not implemented") // TODO: Implement
}
Or, put the cursor on the A
and enter:
:GoImpl
Followed by:
vim-go: generating method stubs for interface: Animal
Lastly, you could put your cursor anywhere, it doesn’t have to on top of the A
:
:GoImpl a *A Animal
The results are all the same.
Note that it’s possible to do this for any interface
type, including ones from the standard library, third-party packages or your own code.
See Method stubs implementing an interface in the vim-go-tutorial.
GoImplements
This is one of my favorites, and I use it heavily. Want to know which interface
s type implements? Wonder no more!
Using the example from GoImpl
, we can place the cursor over any A
and type:
:GoImplements
The quickfix list will open with the following:
1 main.go|5 col 6| type Animal interface {
That was easy, and we already knew that.
Let’s add the following methods:
func (a *A) String() string {
panic("string")
}
func (a *A) Write(b []byte) (int, error) {
panic("write")
}
Now, if we place the cursor over any A
and type :GoImplements
we get:
1 /home/btoll/projects/go/interfaces/impl/main.go|5 col 6| type Animal interface {
1 /usr/local/go/src/fmt/print.go|63 col 6| type Stringer interface {
2 /usr/local/go/src/internal/bisect/bisect.go|473 col 6| type Writer interface {
3 /usr/local/go/src/io/io.go|99 col 6| type Writer interface {
4 /usr/local/go/src/runtime/error.go|210 col 6| type stringer interface {
That’s pretty darn cool.
Wondering about the odd numbering in my quickfix list? That’s the
relativenumber
setting (from.vimrc
):" Set the number of lines to jump forwards or backwards relative to the current cursor position. set relativenumber
GoInfo
This is great to quickly see the function signature (prototype). Lots of times it is incredibly handy to get a quick snapshot of the inputs and outputs of a function or method.
For instance, if you’re writing code that calls the fmt.Fprintf
method, you can quickly see its signature by calling :GoInfo
with the cursor over Fprintf
:
vim-go: func Fprintf(w io.Writer, format string, a ...any) (n int, err error)
See Identifier resolution in the vim-go-tutorial.
GoMetaLinter
The GoMetaLinter
function calls the gometalinter
program. It is a tool that calls go vet
, golint
and errcheck
by default. In addition, it can be configured to run these checkers anytime a file is saved.
For example:
let g:go_metalinter_enabled = ['vet', 'golint', 'errcheck']
Moreover, the checkers that are automatically called when a file is saved can be different from those that are called when :GoMetaLinter
is called explicitly. This can be nice when a full run of the checkers isn’t wanted anytime the file is saved.
Check this out:
let g:go_metalinter_autosave_enabled = ['vet', 'golint']
This says to call vet
and golint
, but not errcheck
, when the file is saved (but run all three when it’s called explicity, see the variable setting directly above).
See Check it in the vim-go-tutorial.
GoPlay
The GoPlay
command is great when you want to quickly share code on the Internet. Open a file, and enter the following command anywhere in the file:
:GoPlay
This will open the Go Playground in a browser tab with your file as the contents. It will also copy the URL to your clipboard. This also works for ranges.
Weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
See Share it in the vim-go-tutorial.
GoRename
Everyone knows that the compiler is your friend when refactoring. vim-go
also assists in this, and GoRename
will rename all of the symbols in one fell swoop.
This is AST-aware. That means that it will only rename symbols in the tree and not just same-named words. Observe:
package main
import "fmt"
type Server struct {
name string
}
func main() {
s := Server{name: "Alper"}
fmt.Println(s.name) // print the server name
}
func name() string {
return "Zeynep"
}
Now, change name
to bar
in the Server
struct
. Put the cursor over either the name
field in Server
or over name
in the line that defines the variable s
:
:GoRename bar
package main
import "fmt"
type Server struct {
bar string
}
func main() {
s := Server{bar: "Alper"}
fmt.Println(s.bar) // print the server name
}
func name() string {
return "Zeynep"
}
Note that it only changes the references for the struct
and not the name of the name
function nor the word name
in the comment.
This is taken directly from the vim-go-tutorial in the Rename identifiers section.
GoRun
To only run the current file, call :GoRun %
(it calls go run
under the hood). To run the entire package, call :GoRun
.
No more need to drop to the command line and call:
$ go run .
See Run it in the vim-go-tutorial.
GoSameIds
Another handy tool is GoSameIds
which allows you to quickly see all of the same-named identifiers. I’ll use it to see at a glance all of the err
variables in a function, for example. Just place the cursor over the identifier and call :GoSameIds
.
Easy peasy.
See Identifier highlighting in the vim-go-tutorial.
Call
:GoSameIdsClear
or:GoSameIdsToggle
to clear the highlighting.
GoTest
This calls go test
under the hood and can be called either from the test file itself or its brother (i.e., either from main_test.go
or main.go
).
Any errors will be listed in the quickfix list.
To only test a single function, place the cursor above the function in question and call :GoTestFunc
. This is useful for many different occasions, especially when running the entire test suite is time-consuming.
Finally, to make sure that the tests compile, simply call :GoTestCompile
, and any errors will be in the quickfix list. Importantly, it doesn’t run the tests themselves.
See Test it in the vim-go-tutorial.
Snippets
I’m not going to cover snippets here, other than to say that vim-go
supports them. I use Ultisnips
(even though I tend not to use them), which is supported by vim-go
, and it provides many builtin examples that are ready to use.
I have many abbreviations in my Vim config files that I use quite often, which are similar to snippets, so perhaps I’ll become a huge fan of them and then subsequently expand this section.
See Snippets in the vim-go-tutorial.
Configs
.vimrc
This is a snippet of my .vim.plugins
configuration, just one of the files that is sourced from my .vimrc
.
" vim: set ft=vim:
call plug#begin('~/.vim/plugged')
Plug 'fatih/vim-go', { 'do': ':GoInstallBinaries' }
"let g:go_auto_sameids = 1 " Highlight same identifiers in file.
let g:go_auto_type_info = 1 " Automatically show function signature of symbol under cursor.
"set updatetime=800 // Default time to show function signature in status line (800ms).
let g:go_decls_includes = 'func,type'
let g:go_def_mode = 'gopls'
let g:go_fmt_command = 'goimports'
let g:go_fmt_fail_silently = 1 " Don't show errors in quickfix window when file is saved (not working).
let g:go_highlight_types = 1
let g:go_highlight_fields = 1
let g:go_highlight_functions = 1
let g:go_highlight_methods = 1
let g:go_highlight_operators = 1
let g:go_highlight_extra_types = 1
let g:go_highlight_build_constraints = 1
let g:go_highlight_generate_tags = 1
let g:go_info_mode = 'gopls'
let g:go_list_type = 'quickfix' " ONLY have quickfix lists (no location lists).
let g:go_metalinter_autosave = 1 " Calls `GoMetaLinter` when the file is saved.
let g:go_metalinter_autosave_enabled = ['vet', 'golint'] " Don't call the whole list when saving to make it faster.
let g:go_metalinter_enabled = ['vet', 'golint', 'errcheck']
let g:go_metalinter_deadline = '5s'
"let g:go_play_browser_command = 'chrome' " When xdg-open misdetecting the browser.
let g:go_play_open_browser = 1
let g:go_test_timeout = '10s'
let g:go_textobj_include_function_doc = 1
let g:go_version_warning = 0
call plug#end()
If you are curious as to why some of my dotfiles are prefaced with dot-
, see the stow
package. I use it to install all of my dotfiles.
ftplugin/go.mod
Here is a snippet of my ftplugin/go.vim
configuration. I’ve only included the bits relevant to vim-go
:
nnoremap <leader>b :<C-u>call Build_go_files()<cr>
nnoremap <Leader>co <Plug>(go-coverage-toggle)
nnoremap <Leader>cob <Plug>(go-coverage-browser)
nnoremap <Leader>i <Plug>(go-info)
nnoremap <leader>r :!clear<cr><Plug>(go-run)
nnoremap <leader>t <Plug>(go-test)
nnoremap <leader>tc <Plug>(go-test-compile) " Compiles but does not test!
nnoremap <leader>tf <Plug>(go-test-func) " Only test the function under the cursor.
Summary
There are some commands that I haven’t covered, such as GoImports
, GoInstallBinaries
, GoLint
, et al. The reason for this is because they are called automatically by variable settings that are defined in my configuration (see the .vimrc
snippet above).
Just set it and forget it, my child.