diff --git a/README.md b/README.md index 3a82f3d..718187c 100644 --- a/README.md +++ b/README.md @@ -946,6 +946,33 @@ end ``` +## Integrate null-ls +### The plugin provides `gotest` LSP diagnostic source for null-ls + +Gotest allow you run `go test ` 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 diff --git a/lua/go/gotest.lua b/lua/go/gotest.lua index 3270667..4506f0b 100644 --- a/lua/go/gotest.lua +++ b/lua/go/gotest.lua @@ -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 diff --git a/lua/go/null_ls.lua b/lua/go/null_ls.lua new file mode 100644 index 0000000..894e4ac --- /dev/null +++ b/lua/go/null_ls.lua @@ -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, +}