Language Server Support for Go in Vim
Last month I found a reddit thread about how to setup Vim for Go development.
The author uses coc.nvim, a heavy-weight plugin with external dependencies. coc.nvim (“Conqueror of Completion”) offers powerful features, but you’ll need to install Node.js.
If you prefer a more lightweight option that also work with plain Vim, this blog post might be for you.
Today I’m going to share my setup:
- works with Vim and NeoVim
- minimal external dependencies (gopls)
- auto-completion support
Prerequisites
You should have a working installation of Go on your computer.
Install gopls, the official language server:
GO111MODULE=on go get golang.org/x/tools/gopls@latest
Vim Setup
Vim needs a plugin for the Language Server Protocol.
I use prabirshrestha/vim-lsp, an asynchronous implementation that works both in Vim 8 and NeoVim. The plugin uses VimL and thus has no external dependencies.
Install with native package support or a plugin manager of your choice. Example:
cd ~/vim/pack
git submodule init
git submodule add https://github.com/prabirshrestha/vim-lsp.git
git add .gitmodules vim/pack/prabirshrestha/vim-lsp
git commit
(I’m happy with vim-packager, but configuring it requires a bit more effort and time. The native package manager works well since Vim 8, but is a bit clunky.)
Let’s wire up the language server with Go.
Create a new file in your Vim folder (~/vim/plugin/lsp.vim
) with the following content:
func! s:setup_ls(...) abort
let l:servers = lsp#get_allowed_servers()
# key mappings
for l:server in l:servers
let l:cap = lsp#get_server_capabilities(l:server)
if has_key(l:cap, 'completionProvider')
setlocal completefunc=lsp#complete
endif
if has_key(l:cap, 'hoverProvider')
setlocal keywordprg=:LspHover
endif
if has_key(l:cap, 'codeActionProvider')
nmap <silent><buffer>ga <plug>(lsp-code-action)
endif
if has_key(l:cap, 'definitionProvider')
nmap <silent><buffer>gd <plug>(lsp-definition)
nmap <silent><buffer>gk <plug>(lsp-peek-definition)
endif
endfor
endfunc
# register language server
augroup LSC
autocmd!
autocmd User lsp_setup call lsp#register_server({
\ 'name': 'gopls',
\ 'cmd': {_->['gopls']},
\ 'allowlist': ['go']
\})
autocmd User lsp_server_init call <SID>setup_ls()
autocmd BufEnter * call <SID>setup_ls()
augroup END
# disable diagnostics etc.
let g:lsp_diagnostics_enabled = 0
let g:lsp_diagnostics_signs_enabled = 0
let g:lsp_diagnostics_virtual_text_enabled = 0
let g:lsp_diagnostics_highlights_enabled = 0
let g:lsp_document_code_action_signs_enabled = 0
The first function creates key bindings if the language server runs. For example, hitting ga
will give you “code actions”. The most useful code action is auto-importing the necessary definitions. Code Actions are a feature I use a lot.
You can find a list of supported commands on the vim-lsp GitHub page.
Auto-completion?
Vim already has auto-completion out of the box.
Here is an excerpt from some extra key mappings in my ~/.vimrc
(“stolen” from bluz71:
"-----------------------------
" completion mappings
"-----------------------------
" t - user defined completion (via completefunc setting)
inoremap <C-t> <C-x><C-u>
Now, when I want the language server to kick in to complete my Go code, I smash CTRL+T
on my keyboard.
How to Format And Lint
The easiest way is to use the gofmt tool from the command-line. For example,
gofmt -w .
The same goes for linting.
Install golint:
go install golang.org/x/lint/golint@latest
Usage:
golint ./...
Alternatively, you can install ALE. Here is an example setup which you can adjust for Go.
Conclusion
I’ve showed you a lightweight setup for Go and Vim. The above plugins and settings have served me well.
There are languages where I’d recommend a dedicated IDE (Java), but for Go I haven’t felt the need yet.