feat(compiler): add configurable error output modes (#14)
Problem: all parse errors went to vim.diagnostic with no way to silence them or route them to the quickfix list. Users wanting quickfix-style error navigation had no option. Solution: add an errors field to ProviderConfig accepting false, 'diagnostic' (default), or 'quickfix'. false suppresses error handling entirely. 'quickfix' converts parsed diagnostics to qflist items (1-indexed), calls setqflist, and opens the window. On success, 'quickfix' mode clears the qflist the same way 'diagnostic' mode clears vim.diagnostic.
This commit is contained in:
parent
7995d6422d
commit
253ca05da3
3 changed files with 133 additions and 3 deletions
|
|
@ -86,9 +86,18 @@ function M.compile(bufnr, name, provider, ctx)
|
|||
return
|
||||
end
|
||||
|
||||
local errors_mode = provider.errors
|
||||
if errors_mode == nil then
|
||||
errors_mode = 'diagnostic'
|
||||
end
|
||||
|
||||
if result.code == 0 then
|
||||
log.dbg('compilation succeeded for buffer %d', bufnr)
|
||||
diagnostic.clear(bufnr)
|
||||
if errors_mode == 'diagnostic' then
|
||||
diagnostic.clear(bufnr)
|
||||
elseif errors_mode == 'quickfix' then
|
||||
vim.fn.setqflist({}, 'r')
|
||||
end
|
||||
vim.api.nvim_exec_autocmds('User', {
|
||||
pattern = 'PreviewCompileSuccess',
|
||||
data = { bufnr = bufnr, provider = name, output = output_file },
|
||||
|
|
@ -105,9 +114,27 @@ function M.compile(bufnr, name, provider, ctx)
|
|||
end
|
||||
else
|
||||
log.dbg('compilation failed for buffer %d (exit code %d)', bufnr, result.code)
|
||||
if provider.error_parser then
|
||||
if provider.error_parser and errors_mode then
|
||||
local output = (result.stdout or '') .. (result.stderr or '')
|
||||
diagnostic.set(bufnr, name, provider.error_parser, output, ctx)
|
||||
if errors_mode == 'diagnostic' then
|
||||
diagnostic.set(bufnr, name, provider.error_parser, output, ctx)
|
||||
elseif errors_mode == 'quickfix' then
|
||||
local ok, diagnostics = pcall(provider.error_parser, output, ctx)
|
||||
if ok and diagnostics and #diagnostics > 0 then
|
||||
local items = {}
|
||||
for _, d in ipairs(diagnostics) do
|
||||
table.insert(items, {
|
||||
bufnr = bufnr,
|
||||
lnum = d.lnum + 1,
|
||||
col = d.col + 1,
|
||||
text = d.message,
|
||||
type = d.severity == vim.diagnostic.severity.WARN and 'W' or 'E',
|
||||
})
|
||||
end
|
||||
vim.fn.setqflist(items, 'r')
|
||||
vim.cmd('copen')
|
||||
end
|
||||
end
|
||||
end
|
||||
vim.api.nvim_exec_autocmds('User', {
|
||||
pattern = 'PreviewCompileFailed',
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
---@field env? table<string, string>
|
||||
---@field output? string|fun(ctx: preview.Context): string
|
||||
---@field error_parser? fun(output: string, ctx: preview.Context): preview.Diagnostic[]
|
||||
---@field errors? false|'diagnostic'|'quickfix'
|
||||
---@field clean? string[]|fun(ctx: preview.Context): string[]
|
||||
---@field open? boolean|string[]
|
||||
|
||||
|
|
|
|||
|
|
@ -132,6 +132,108 @@ describe('compiler', function()
|
|||
end)
|
||||
end)
|
||||
|
||||
describe('errors mode', function()
|
||||
it('errors = false suppresses error parser', function()
|
||||
local bufnr = helpers.create_buffer({ 'hello' }, 'text')
|
||||
vim.api.nvim_buf_set_name(bufnr, '/tmp/preview_test_errors_false.txt')
|
||||
vim.bo[bufnr].modified = false
|
||||
|
||||
local parser_called = false
|
||||
local provider = {
|
||||
cmd = { 'false' },
|
||||
errors = false,
|
||||
error_parser = function()
|
||||
parser_called = true
|
||||
return {}
|
||||
end,
|
||||
}
|
||||
local ctx = {
|
||||
bufnr = bufnr,
|
||||
file = '/tmp/preview_test_errors_false.txt',
|
||||
root = '/tmp',
|
||||
ft = 'text',
|
||||
}
|
||||
|
||||
compiler.compile(bufnr, 'falsecmd', provider, ctx)
|
||||
|
||||
vim.wait(2000, function()
|
||||
return compiler._test.active[bufnr] == nil
|
||||
end, 50)
|
||||
|
||||
assert.is_false(parser_called)
|
||||
helpers.delete_buffer(bufnr)
|
||||
end)
|
||||
|
||||
it('errors = quickfix populates quickfix list', function()
|
||||
local bufnr = helpers.create_buffer({ 'hello' }, 'text')
|
||||
vim.api.nvim_buf_set_name(bufnr, '/tmp/preview_test_errors_qf.txt')
|
||||
vim.bo[bufnr].modified = false
|
||||
|
||||
local provider = {
|
||||
cmd = { 'sh', '-c', 'echo "line 1 error" >&2; exit 1' },
|
||||
errors = 'quickfix',
|
||||
error_parser = function()
|
||||
return {
|
||||
{ lnum = 0, col = 0, message = 'test error', severity = vim.diagnostic.severity.ERROR },
|
||||
}
|
||||
end,
|
||||
}
|
||||
local ctx = {
|
||||
bufnr = bufnr,
|
||||
file = '/tmp/preview_test_errors_qf.txt',
|
||||
root = '/tmp',
|
||||
ft = 'text',
|
||||
}
|
||||
|
||||
vim.fn.setqflist({}, 'r')
|
||||
compiler.compile(bufnr, 'qfcmd', provider, ctx)
|
||||
|
||||
vim.wait(2000, function()
|
||||
return compiler._test.active[bufnr] == nil
|
||||
end, 50)
|
||||
|
||||
local qflist = vim.fn.getqflist()
|
||||
assert.are.equal(1, #qflist)
|
||||
assert.are.equal('test error', qflist[1].text)
|
||||
assert.are.equal(1, qflist[1].lnum)
|
||||
|
||||
vim.fn.setqflist({}, 'r')
|
||||
helpers.delete_buffer(bufnr)
|
||||
end)
|
||||
|
||||
it('errors = quickfix clears quickfix on success', function()
|
||||
local bufnr = helpers.create_buffer({ 'hello' }, 'text')
|
||||
vim.api.nvim_buf_set_name(bufnr, '/tmp/preview_test_errors_qf_clear.txt')
|
||||
vim.bo[bufnr].modified = false
|
||||
|
||||
vim.fn.setqflist({ { text = 'old error', lnum = 1 } }, 'r')
|
||||
assert.are.equal(1, #vim.fn.getqflist())
|
||||
|
||||
local provider = {
|
||||
cmd = { 'true' },
|
||||
errors = 'quickfix',
|
||||
error_parser = function()
|
||||
return {}
|
||||
end,
|
||||
}
|
||||
local ctx = {
|
||||
bufnr = bufnr,
|
||||
file = '/tmp/preview_test_errors_qf_clear.txt',
|
||||
root = '/tmp',
|
||||
ft = 'text',
|
||||
}
|
||||
|
||||
compiler.compile(bufnr, 'truecmd', provider, ctx)
|
||||
|
||||
vim.wait(2000, function()
|
||||
return compiler._test.active[bufnr] == nil
|
||||
end, 50)
|
||||
|
||||
assert.are.equal(0, #vim.fn.getqflist())
|
||||
helpers.delete_buffer(bufnr)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('stop', function()
|
||||
it('does nothing when no process is active', function()
|
||||
assert.has_no.errors(function()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue