LSP Client Configuration (nvim-lspconfig)
Install gopls, LSP server for Go.
$ go install golang.org/x/tools/gopls@latest
$ gopls version
golang.org/x/tools/gopls v0.16.2
Since Neovim has a built-in LSP client, it works by simply passing the gopls configuration from nvim-lspconfig.
Set it up to apply formatting on save.
vim.api.nvim_create_autocmd("BufWritePre", {
pattern = "*.go",
callback = function()
local params = vim.lsp.util.make_range_params()
params.context = {only = {"source.organizeImports"}}
-- buf_request_sync defaults to a 1000ms timeout. Depending on your
-- machine and codebase, you may want longer. Add an additional
-- argument after params if you find that you have to write the file
-- twice for changes to be saved.
-- E.g., vim.lsp.buf_request_sync(0, "textDocument/codeAction", params, 3000)
local result = vim.lsp.buf_request_sync(0, "textDocument/codeAction", params)
for cid, res in pairs(result or {}) do
for _, r in pairs(res.result or {}) do
if r.edit then
local enc = (vim.lsp.get_client_by_id(cid) or {}).offset_encoding or "utf-16"
vim.lsp.util.apply_workspace_edit(r.edit, enc)
end
end
end
vim.lsp.buf.format({async = false})
end
})
Set up keymaps for actions like jumping to definitions. C-o to go back and C-i to go forward.
vim.keymap.set('n', 'K', vim.lsp.buf.hover, { desc = 'Show hover' })
vim.keymap.set('n', 'gd', vim.lsp.buf.definition, { desc = 'Go to definition' })
vim.keymap.set('n', 'gi', vim.lsp.buf.implementation, { desc = 'Go to implementation'})
vim.keymap.set('n', 'gr', vim.lsp.buf.references, bufopts, { desc = 'Show references' })
Completion (nvim-cmp)
While completion is possible with LSP alone C-x C-o, using nvim-cmp enables automatic completion with godocs displayed alongside. It also supports completion from other sources such as paths.
Install the nvim plugin manager lazy.nvim and add plugins - sambaiz-net
{
{
'hrsh7th/nvim-cmp',
config = function()
local cmp = require('cmp')
cmp.setup({
mapping = cmp.mapping.preset.cmdline(),
sources = {
{ name = 'nvim_lsp' },
{ name = 'buffer' },
{ name = 'path' },
},
mapping = cmp.mapping.preset.insert({
['<C-b>'] = cmp.mapping.scroll_docs(-4),
['<C-f>'] = cmp.mapping.scroll_docs(4),
['<C-Space>'] = cmp.mapping.complete(),
['<C-e>'] = cmp.mapping.abort(),
['<CR>'] = cmp.mapping.confirm({ select = true }), -- Accept currently selected item. Set `select` to `false` to only confirm explicitly selected items.
}),
})
end
},
{
'hrsh7th/cmp-nvim-lsp',
dependencies = {
'neovim/nvim-lspconfig',
},
config = function()
local lspconfig = require("lspconfig")
local capabilities = require('cmp_nvim_lsp').default_capabilities()
lspconfig.gopls.setup({
capabilities = capabilities,
})
end
},
{
'hrsh7th/cmp-buffer',
},
{
'hrsh7th/cmp-path',
},
{
'hrsh7th/cmp-cmdline',
}
}
syntax highlight (nvim-treesitter)
Installing nvim-treesitter, which is used for tree-sitter that parses code and creates syntax trees, makes syntax highlighting complete.
{
'nvim-treesitter/nvim-treesitter', tag = 'v0.9.3',
config = function()
require('nvim-treesitter.configs').setup({
ensure_installed = { 'go', 'lua', 'typescript', 'javascript', 'json', 'yaml', 'html', 'css', 'bash', 'python', 'markdown', 'scala' },
highlight = {
enable = true,
},
})
end
}
Test Execution and Coverage Visualization (neotest, nvim-coverage)
Install neotest and the Go runner neotest-go to run tests.
{
'nvim-neotest/neotest', tag = 'v5.6.1',
dependencies = {
'nvim-neotest/nvim-nio',
'nvim-lua/plenary.nvim',
'antoinemadec/FixCursorHold.nvim',
'nvim-treesitter/nvim-treesitter',
'nvim-neotest/neotest-go',
},
config = function()
require('neotest').setup({
adapters = {
require('neotest-go')({
args = { '-coverprofile=coverage.out' },
})
},
})
end,
}
vim.keymap.set('n', '<Leader>tt', ':lua require("neotest").run.run()<CR>', { desc = 'Run test' })
While the status icons were garbled in the standard terminal, they were displayed correctly in WezTerm.
The output coverage profiles can be visualized with nvim-coverage.
{
'andythigpen/nvim-coverage',
dependencies = {
'nvim-lua/plenary.nvim',
},
config = function()
require('coverage').setup({
lang = {
go = {
coverage_file = vim.fn.getcwd() .. '/coverage.out',
}
}
})
end,
}
The color of the bars displayed with :Coverage indicates whether the code has been tested or not.