Compare commits

...
Sign in to create a new pull request.

3 commits

Author SHA1 Message Date
65fdc27a8e
fix(compiler): use cwindow and win_gotoid for quickfix focus management 2026-03-04 16:50:04 -05:00
f8e2d1d089
fix(compiler): open quickfix in background, retain focus on source buffer 2026-03-04 16:36:44 -05:00
2edeef478d
fix: stream stderr for long-running providers, clear errors on success
Problem: Long-running providers (e.g. `typst watch`) never exit on
compile error, so the exit callback never fired and diagnostics/quickfix
were never populated. The `typst watch` `reload` command also lacked
`--diagnostic-format short`, producing unparseable verbose output.

Solution: Add a `stderr` streaming callback to the long-running
`vim.system` call that accumulates chunks and re-parses on each new
chunk, populating diagnostics or quickfix in real time. When the
fs_event fires (successful compile), clear `stderr_acc` and the
reported errors. Add `--diagnostic-format short` to the typst `reload`
command to match the one-shot `args` format.
2026-03-04 16:33:02 -05:00
2 changed files with 54 additions and 3 deletions

View file

@ -143,12 +143,47 @@ function M.compile(bufnr, name, provider, ctx, opts)
table.concat(reload_cmd, ' ')
)
local stderr_acc = {}
local obj
obj = vim.system(
reload_cmd,
{
cwd = cwd,
env = provider.env,
stderr = vim.schedule_wrap(function(err, data)
if not data or not vim.api.nvim_buf_is_valid(bufnr) then
return
end
stderr_acc[#stderr_acc + 1] = data
local errors_mode = provider.errors
if errors_mode == nil then
errors_mode = 'diagnostic'
end
if provider.error_parser and errors_mode then
local output = table.concat(stderr_acc)
if errors_mode == 'diagnostic' then
diagnostic.set(bufnr, name, provider.error_parser, output, ctx)
elseif errors_mode == 'quickfix' then
local ok, diags = pcall(provider.error_parser, output, ctx)
if ok and diags and #diags > 0 then
local items = {}
for _, d in ipairs(diags) 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')
local win = vim.fn.win_getid()
vim.cmd.cwindow()
vim.fn.win_gotoid(win)
end
end
end
end),
},
vim.schedule_wrap(function(result)
if active[bufnr] and active[bufnr].obj == obj then
@ -182,7 +217,9 @@ function M.compile(bufnr, name, provider, ctx, opts)
})
end
vim.fn.setqflist(items, 'r')
vim.cmd('copen')
local win = vim.fn.win_getid()
vim.cmd.cwindow()
vim.fn.win_gotoid(win)
end
end
end
@ -228,6 +265,17 @@ function M.compile(bufnr, name, provider, ctx, opts)
return
end
stop_open_watcher(bufnr)
stderr_acc = {}
local errors_mode = provider.errors
if errors_mode == nil then
errors_mode = 'diagnostic'
end
if errors_mode == 'diagnostic' then
diagnostic.clear(bufnr)
elseif errors_mode == 'quickfix' then
vim.fn.setqflist({}, 'r')
vim.cmd.cwindow()
end
do_open(bufnr, output_file, provider.open)
opened[bufnr] = true
end)
@ -288,6 +336,7 @@ function M.compile(bufnr, name, provider, ctx, opts)
diagnostic.clear(bufnr)
elseif errors_mode == 'quickfix' then
vim.fn.setqflist({}, 'r')
vim.cmd.cwindow()
end
vim.api.nvim_exec_autocmds('User', {
pattern = 'PreviewCompileSuccess',
@ -329,7 +378,9 @@ function M.compile(bufnr, name, provider, ctx, opts)
})
end
vim.fn.setqflist(items, 'r')
vim.cmd('copen')
local win = vim.fn.win_getid()
vim.cmd.cwindow()
vim.fn.win_gotoid(win)
end
end
end

View file

@ -133,7 +133,7 @@ M.typst = {
end,
open = true,
reload = function(ctx)
return { 'typst', 'watch', ctx.file }
return { 'typst', 'watch', '--diagnostic-format', 'short', ctx.file }
end,
}