A minimal implementation of Golang development plugin written in Lua for Neovim.
Neovim (from v0.5) embeds a built-in Language Server Protocol (LSP) client. And Debug Adapter Protocol (DAP) are also brought into Neovim as a general debugger. With the power of them, we are able to easily turn Neovim into a powerful editor.
nvim-go
is designed to collaborate with them, provides sufficient features, and leverages community toolchains to get Golang development done.
- Auto format with
:GoFormat
(viagoimports
,gofmt
,gofumpt
andlsp
) when saving. - Run linters with
:GoLint
(viarevive
) automatically. - Quickly test with
:GoTest
,:GoTestFunc
,:GoTestFile
and:GoTestAll
. Generate test with:GoAddTest
. - Import packages with
:GoGet
and:GoImport
. - Modify struct tags with
:GoAddTags
,:GoRemoveTags
,:GoClearTags
,:GoAddTagOptions
,:GoRemoveTagOptions
and:GoClearTagOptions
. - Generates JSON models with
:GoQuickType
(viaquicktype
). - Generate if err based on function return values with
:GoIfErr
(viaiferr
).
This section can be regarded as a guide or common practice to develop with nvim-go
, LSP (gopls
) and DAP.
If you are familiar with these tools or other equivalent, you may skip this chapter.
<Show/Hide the guide>
Language server provides vital language features to make Golang development easy.
We highly recommend you to use LSP client together with nvim-go
.
- Setup
gopls
with neovim/nvim-lspconfig. - Setup your favorite completion engine such as nvim-cmp.
- Setup and map the following methods based on what you need:
- Declaration:
vim.lsp.buf.declaration()
- Definition:
vim.lsp.buf.definition()
andvim.lsp.buf.type_definition()
- Implementation:
vim.lsp.buf.implementation()
- Hover:
vim.lsp.buf.hover()
- Signature:
vim.lsp.buf.signature_help()
- References:
vim.lsp.buf.reference()
- Symbols:
vim.lsp.buf.document_symbol()
andvim.lsp.buf.workspace_symbol()
- Rename:
vim.lsp.buf.rename()
- Format:
vim.lsp.buf.format()
, also works withGoFormat
. - Diagnostic:
vim.diagnostic
will also show lint issues with Virtual Text, which runsgo/analysis
. You may disableauto_lint
if this works well with your project.
For details of gopls
, please refer to https://github.com/golang/tools/blob/master/gopls/doc/design/design.md#features.
Prerequisites:
- Neovim (>= 0.7)
- npm (for quicktype)
Install with your favorite package manager:
" dependencies
use('nvim-lua/plenary.nvim')
" nvim-go
use('crispgm/nvim-go')
" (optional) if you enable nvim-notify
use('rcarriga/nvim-notify')
" (recommend) LSP config
use('neovim/nvim-lspconfig')
Finally, run :GoInstallBinaries
after plugin installed.
Install
quicktype
withyarn
orpnpm
:
nvim-go
install quicktype
with npm
by default, you may replace it with yarn
or pnpm
.
require('go').config.update_tool('quicktype', function(tool)
tool.pkg_mgr = 'yarn'
end)
-- setup nvim-go
require('go').setup({})
-- setup lsp client
require('lspconfig').gopls.setup({})
require('go').setup({
-- notify: use nvim-notify
notify = false,
-- auto commands
auto_format = true,
auto_lint = true,
-- linters: revive, errcheck, staticcheck, golangci-lint
linter = 'revive',
-- linter_flags: e.g., {revive = {'-config', '/path/to/config.yml'}}
linter_flags = {},
-- lint_prompt_style: qf (quickfix), vt (virtual text)
lint_prompt_style = 'qf',
-- formatter: goimports, gofmt, gofumpt, lsp
formatter = 'goimports',
-- maintain cursor position after formatting loaded buffer
maintain_cursor_pos = false,
-- test flags: -count=1 will disable cache
test_flags = {'-v'},
test_timeout = '30s',
test_env = {},
-- show test result with popup window
test_popup = true,
test_popup_auto_leave = false,
test_popup_width = 80,
test_popup_height = 10,
-- test open
test_open_cmd = 'edit',
-- struct tags
tags_name = 'json',
tags_options = {'json=omitempty'},
tags_transform = 'snakecase',
tags_flags = {'-skip-unexported'},
-- quick type
quick_type_flags = {'--just-types'},
})
Display within Neovim with:
:help nvim-go
function! LintIssuesCount()
if exists('g:nvim_go#lint_issues_count')
return g:nvim_go#lint_issues_count
endif
endfunction
call airline#parts#define_function('nvim_go', 'LintIssuesCount')
call airline#parts#define_condition('nvim_go', '&filetype == "go"')
let g:airline_section_warning = airline#section#create_right(['nvim_go'])
function! LintIssuesCount()
if exists('g:nvim_go#lint_issues_count') && &filetype == 'go'
return g:nvim_go#lint_issues_count
endif
endfunction
let g:lightline = {
\ 'colorscheme': 'wombat',
\ 'active': {
\ 'left': [ [ 'mode', 'paste' ],
\ [ 'readonly', 'filename', 'modified', 'lintcount' ] ]
\ },
\ 'component_function': {
\ 'lintcount': 'LintIssuesCount'
\ },
\ }
require('hardline').setup({
-- ...
sections = {
{
class = 'error',
item = function()
if
vim.bo.filetype == 'go'
and vim.g['nvim_go#lint_issues_count'] ~= nil
then
return vim.g['nvim_go#lint_issues_count']
else
return ''
end
end,
},
-- ...
}
augroup NvimGo
autocmd!
autocmd User NvimGoLintPopupPost wincmd p
augroup END
Or equivalently:
local NvimGo = vim.api.nvim_create_augroup("NvimGo", {
clear = true,
})
vim.api.nvim_create_autocmd({ "User" }, {
pattern = "NvimGoLintPopupPost",
group = NvimGo,
command = "wincmd p",
})