From 2edeef478dda0d9516cad05e28eb77ef43cb3b20 Mon Sep 17 00:00:00 2001 From: Barrett Ruth Date: Wed, 4 Mar 2026 16:33:02 -0500 Subject: [PATCH] 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. --- lua/preview/compiler.lua | 43 ++++++++++++++++++++++++++++++++++++++++ lua/preview/presets.lua | 2 +- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/lua/preview/compiler.lua b/lua/preview/compiler.lua index c5a81c2..3f897ad 100644 --- a/lua/preview/compiler.lua +++ b/lua/preview/compiler.lua @@ -143,12 +143,45 @@ 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') + vim.cmd('copen') + end + end + end + end), }, vim.schedule_wrap(function(result) if active[bufnr] and active[bufnr].obj == obj then @@ -228,6 +261,16 @@ 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') + end do_open(bufnr, output_file, provider.open) opened[bufnr] = true end) diff --git a/lua/preview/presets.lua b/lua/preview/presets.lua index f189e98..3591a21 100644 --- a/lua/preview/presets.lua +++ b/lua/preview/presets.lua @@ -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, }