preview.nvim/spec/diagnostic_spec.lua
Barrett Ruth 39406c559c
feat: compile notifications and long-running provider feedback (#55)
* feat(compiler): add compile start/complete notifications

Problem: No user-facing feedback when compilation starts or finishes.
Long-running compilers like pandoc with `--embed-resources` leave the
user staring at nothing for 15+ seconds.

Solution: Notify "compiling..." at compile start and "compilation
complete" on success. The initial `toggle` call uses a combined
"compiling with <name>..." message to avoid stacking two notifications.

* refactor(presets): use `--katex` instead of `--embed-resources --mathml`

Problem: `--embed-resources` with `--mathml` caused pandoc to inline all
assets at compile time, adding ~15s per save for KaTeX-heavy documents.

Solution: Default to `--katex`, which inserts a CDN `<script>` tag and
defers rendering to the browser. Users can opt into `--embed-resources`
or `--mathml` via `extra_args`.

* docs(presets): rewrite math rendering section for `--katex` default

* refactor(compiler): simplify notification flow, add failure notify

Problem: `compile()` used an `opts.silent` escape hatch so `toggle()`
could suppress duplicate notifications. Compilation failures had no
user-facing notification.

Solution: Remove `opts.silent` — `compile()` unconditionally notifies
on start, success, and failure. `toggle()` no longer emits its own
message.

* feat(compiler): add per-recompile notifications for long-running providers

Problem: long-running providers like `typst watch` had no per-recompile
feedback — the process stays alive and individual success/failure was
never reported.

Solution: add a persistent `output_watcher` fs_event that fires
"compilation complete" on every output mtime bump, and track
`has_errors` on `BufState` so stderr diagnostics trigger a one-shot
"compilation failed" notification. `diagnostic.set()` now returns the
diagnostic count to support this flow.

* ci: format

* chore: remove testing files
2026-03-06 14:58:10 -05:00

170 lines
5.4 KiB
Lua

local helpers = require('spec.helpers')
describe('diagnostic', function()
local diagnostic
before_each(function()
helpers.reset_config()
diagnostic = require('preview.diagnostic')
end)
describe('clear', function()
it('clears diagnostics for a buffer', function()
local bufnr = helpers.create_buffer({ 'line1', 'line2' })
local ns = diagnostic.get_namespace()
vim.diagnostic.set(ns, bufnr, {
{ lnum = 0, col = 0, message = 'test error', severity = vim.diagnostic.severity.ERROR },
})
local diags = vim.diagnostic.get(bufnr, { namespace = ns })
assert.are.equal(1, #diags)
diagnostic.clear(bufnr)
diags = vim.diagnostic.get(bufnr, { namespace = ns })
assert.are.equal(0, #diags)
helpers.delete_buffer(bufnr)
end)
end)
describe('set', function()
it('sets diagnostics from error_parser output', function()
local bufnr = helpers.create_buffer({ 'line1', 'line2' })
local ns = diagnostic.get_namespace()
local parser = function()
return {
{ lnum = 0, col = 0, message = 'syntax error', severity = vim.diagnostic.severity.ERROR },
}
end
local ctx = { bufnr = bufnr, file = '/tmp/test.typ', root = '/tmp', ft = 'typst' }
diagnostic.set(bufnr, 'typst', parser, 'error on line 1', ctx)
local diags = vim.diagnostic.get(bufnr, { namespace = ns })
assert.are.equal(1, #diags)
assert.are.equal('syntax error', diags[1].message)
assert.are.equal('typst', diags[1].source)
helpers.delete_buffer(bufnr)
end)
it('sets source to provider name when not specified', function()
local bufnr = helpers.create_buffer({ 'line1' })
local ns = diagnostic.get_namespace()
local parser = function()
return {
{ lnum = 0, col = 0, message = 'err', severity = vim.diagnostic.severity.ERROR },
}
end
local ctx = { bufnr = bufnr, file = '/tmp/test.tex', root = '/tmp', ft = 'tex' }
diagnostic.set(bufnr, 'latexmk', parser, 'error', ctx)
local diags = vim.diagnostic.get(bufnr, { namespace = ns })
assert.are.equal('latexmk', diags[1].source)
helpers.delete_buffer(bufnr)
end)
it('preserves existing source from parser', function()
local bufnr = helpers.create_buffer({ 'line1' })
local ns = diagnostic.get_namespace()
local parser = function()
return {
{
lnum = 0,
col = 0,
message = 'err',
severity = vim.diagnostic.severity.ERROR,
source = 'custom',
},
}
end
local ctx = { bufnr = bufnr, file = '/tmp/test.tex', root = '/tmp', ft = 'tex' }
diagnostic.set(bufnr, 'latexmk', parser, 'error', ctx)
local diags = vim.diagnostic.get(bufnr, { namespace = ns })
assert.are.equal('custom', diags[1].source)
helpers.delete_buffer(bufnr)
end)
it('handles parser failure gracefully', function()
local bufnr = helpers.create_buffer({ 'line1' })
local ns = diagnostic.get_namespace()
local parser = function()
error('parser exploded')
end
local ctx = { bufnr = bufnr, file = '/tmp/test.tex', root = '/tmp', ft = 'tex' }
assert.has_no.errors(function()
diagnostic.set(bufnr, 'latexmk', parser, 'error', ctx)
end)
local diags = vim.diagnostic.get(bufnr, { namespace = ns })
assert.are.equal(0, #diags)
helpers.delete_buffer(bufnr)
end)
it('does nothing when parser returns empty list', function()
local bufnr = helpers.create_buffer({ 'line1' })
local ns = diagnostic.get_namespace()
local parser = function()
return {}
end
local ctx = { bufnr = bufnr, file = '/tmp/test.tex', root = '/tmp', ft = 'tex' }
diagnostic.set(bufnr, 'latexmk', parser, 'error', ctx)
local diags = vim.diagnostic.get(bufnr, { namespace = ns })
assert.are.equal(0, #diags)
helpers.delete_buffer(bufnr)
end)
it('returns count of diagnostics set', function()
local bufnr = helpers.create_buffer({ 'line1', 'line2' })
local parser = function()
return {
{ lnum = 0, col = 0, message = 'err1', severity = vim.diagnostic.severity.ERROR },
{ lnum = 1, col = 0, message = 'err2', severity = vim.diagnostic.severity.WARN },
}
end
local ctx = { bufnr = bufnr, file = '/tmp/test.typ', root = '/tmp', ft = 'typst' }
local count = diagnostic.set(bufnr, 'typst', parser, 'errors', ctx)
assert.are.equal(2, count)
helpers.delete_buffer(bufnr)
end)
it('returns 0 on parser failure', function()
local bufnr = helpers.create_buffer({ 'line1' })
local parser = function()
error('boom')
end
local ctx = { bufnr = bufnr, file = '/tmp/test.tex', root = '/tmp', ft = 'tex' }
local count = diagnostic.set(bufnr, 'latexmk', parser, 'error', ctx)
assert.are.equal(0, count)
helpers.delete_buffer(bufnr)
end)
it('returns 0 on empty diagnostics', function()
local bufnr = helpers.create_buffer({ 'line1' })
local parser = function()
return {}
end
local ctx = { bufnr = bufnr, file = '/tmp/test.tex', root = '/tmp', ft = 'tex' }
local count = diagnostic.set(bufnr, 'latexmk', parser, 'error', ctx)
assert.are.equal(0, count)
helpers.delete_buffer(bufnr)
end)
end)
end)