[Feature] integrate with null-ls. Add go.nvim as a null-ls source

Run `go test` on file save and
show diagnostic messages in LSP virtual text.
This commit is contained in:
ray-x 2023-01-15 15:46:16 +11:00
parent 793b0d1ede
commit 276ebe8654
3 changed files with 143 additions and 3 deletions

View File

@ -946,6 +946,33 @@ end
```
## Integrate null-ls
### The plugin provides `gotest` LSP diagnostic source for null-ls
Gotest allow you run `go test <package>` when you save your go file and add diagnostics to nvim
```lua
local null_ls = require("null-ls")
local sources = {
null_ls.builtins.diagnostics.golangci_lint,
null_ls.builtins.diagnostics.revive,
null_ls.builtins.formatting.golines.with({
extra_args = {
"--max-len=180",
"--base-formatter=gofumpt",
},
})
}
-- for go.nvim
local gotest = require("go.null_ls").gotest()
table.insert(sources, gotest)
null_ls.setup({ sources = sources, debounce = 1000, default_timeout = 5000 })
-- alternatively
null_ls.register(gotest)
```
## Sample vimrc

View File

@ -88,7 +88,7 @@ M.get_build_tags = function(args, tbl)
return
end
local function get_test_path()
function M.get_test_path()
local path = vim.fn.expand('%:p:h')
local relative_path = vim.fn.fnamemodify(path, ':.')
if path == relative_path then
@ -303,7 +303,7 @@ end
M.test_package = function(...)
local args = { ... }
log(args)
local fpath = get_test_path() .. sep .. '...'
local fpath = M.get_test_path() .. sep .. '...'
utils.log('fpath: ' .. fpath)
return run_test(fpath, args)
end
@ -409,7 +409,7 @@ local function run_tests_with_ts_node(args, func_node, tblcase_ns)
table.insert(cmd, [['^]] .. func_node.name .. [[$']] .. tbl_name)
end
local fpath = get_test_path()
local fpath = M.get_test_path()
table.insert(cmd, fpath)
if optarg['a'] then

113
lua/go/null_ls.lua Normal file
View File

@ -0,0 +1,113 @@
local vim, fn = vim, vim.fn
local utils = require('go.utils')
local log = utils.log
local function handler()
local severities = { error = 1, warning = 2, information = 3, hint = 4 }
return function(msg, done)
if msg == nil or msg.output == nil then
return
end
local msgs = msg.output
msgs = vim.split(msgs, '\n', true)
local diags = {}
local package, filename, line
-- the output is jsonencoded
local output = ''
for _, m in pairs(msgs) do
if vim.fn.empty(m) == 0 then
local entry = vim.fn.json_decode(m)
if entry.Action == 'run' then
package = entry.Package
output = ''
elseif entry.Action == 'output' then
log(entry)
if vim.fn.empty(entry.Output) == 0 then
local ma = vim.fn.matchlist(entry.Output, [[\v\s*(\w+.+\.go):(\d+):]])
if ma[2] then
log(ma)
filename = package .. utils.sep() .. ma[2]
if fn.filereadable(filename) == 0 then
filename = fn.fnamemodify(fn.expand('%:h'), ':~:.') .. utils.sep() .. ma[2]
end
line = ma[3]
end
output = output .. (entry.Output or '')
-- log(output or 'nil')
end
elseif entry.Action == 'pass' or entry.Action == 'skip' then
-- log(entry)
-- reset
output = ''
elseif entry.Action == 'fail' and vim.fn.empty(output) == 0 then
log(entry)
if filename:find(fn.expand('%:t')) then -- can be output from other files
table.insert(diags, {
file = filename,
row = tonumber(line),
col = 1,
message = output,
severity = severities.error,
source = 'go test',
})
end
output = ''
end
end
end
log(diags)
-- local ok, d = pcall(vim.fn.json_decode, msg)
return done(diags)
end
end
-- register with
-- null_ls.register(gotest)
return {
gotest = function()
local nulls = utils.load_plugin('null-ls', 'null-ls')
if nulls == nil then
vim.notify('failed to load null-ls', vim.lsp.log_levels.WARN)
return
end
local null_ls = require('null-ls')
local methods = require('null-ls.methods')
local DIAGNOSTICS_ON_SAVE = methods.internal.DIAGNOSTICS_ON_SAVE
return {
method = null_ls.methods.DIAGNOSTICS,
filetypes = { 'go' },
generator = null_ls.generator({
command = 'go',
args = function()
local a = { 'test', '-json' }
local pkg = require('go.gotest').get_test_path()
table.insert(a, pkg)
log(a)
return a
end,
to_stdin = false,
method = DIAGNOSTICS_ON_SAVE,
from_stderr = false,
-- choose an output format (raw, json, or line)
-- format = 'json',
format = 'raw',
check_exit_code = function(code, stderr)
local success = code <= 1
log(code, stderr)
if not success then
-- can be noisy for things that run often (e.g. diagnostics), but can
-- be useful for things that run on demand (e.g. formatting)
vim.notify('go test failed: ' .. tostring(stderr))
end
return success
end,
on_output = handler(),
}),
}
end,
}