* fix(commands): register VimLeavePre to call stop_all Problem: spawned compiler processes and watching autocmds were never cleaned up when Neovim exited, leaving orphaned processes running. Solution: register a VimLeavePre autocmd in commands setup that calls compiler.stop_all(), which kills active processes, unwatches all buffers, and stops the reload server. * fix(compiler): replace BufWipeout with BufUnload Problem: cleanup autocmds used BufWipeout, which only fires for :bwipeout. The common :bdelete path (used by most buffer managers and nvim_buf_delete) fires BufUnload but not BufWipeout, so processes and watches leaked on normal buffer deletion. Solution: switch all three cleanup autocmds from BufWipeout to BufUnload, which fires for both :bdelete and :bwipeout. * fix(init): guard against unnamed buffer in public API Problem: calling compile/toggle/clean/open on an unsaved scratch buffer passed an empty string as ctx.file, producing nonsensical output paths like ".pdf" and silently passing empty strings to compiler binaries. Solution: add an early return with a WARN notification in compile, toggle, clean, and open when the buffer has no file name. * fix(compiler): add fs_stat check to one-shot open path Problem: the long-running process path already guarded opens with vim.uv.fs_stat(), but the one-shot compile path and M.open() did not. Compilation can exit 0 and produce no output, and output files can be externally deleted between compile and open. Solution: add the same fs_stat guard to the one-shot open branch and to M.open() before attempting to launch the viewer. * fix(compiler): check executable before spawning process Problem: if a configured binary was missing or not in PATH, vim.system would fail silently or with a cryptic OS error. The user had no actionable feedback without running :checkhealth. Solution: check vim.fn.executable() at the start of M.compile() and notify with an ERROR-level message pointing to :checkhealth preview if the binary is not found. * fix(compiler): reformat one-shot open condition for line length Problem: the added fs_stat condition exceeded stylua's line length limit on the one-shot open guard. Solution: split the boolean condition across multiple lines to match the project's stylua formatting rules.
169 lines
5 KiB
Lua
169 lines
5 KiB
Lua
local helpers = require('spec.helpers')
|
|
|
|
describe('preview', function()
|
|
local preview
|
|
|
|
before_each(function()
|
|
helpers.reset_config()
|
|
preview = require('preview')
|
|
end)
|
|
|
|
describe('config', function()
|
|
it('returns defaults before setup is called', function()
|
|
local config = preview.get_config()
|
|
assert.is_false(config.debug)
|
|
assert.are.same({}, config.providers)
|
|
end)
|
|
|
|
it('merges user config with defaults', function()
|
|
helpers.reset_config({ debug = true })
|
|
local config = require('preview').get_config()
|
|
assert.is_true(config.debug)
|
|
assert.are.same({}, config.providers)
|
|
end)
|
|
|
|
it('merges override table with matching preset', function()
|
|
helpers.reset_config({
|
|
typst = {
|
|
cmd = { 'typst', 'compile' },
|
|
args = { '%s' },
|
|
},
|
|
})
|
|
local config = require('preview').get_config()
|
|
assert.is_not_nil(config.providers.typst)
|
|
end)
|
|
|
|
it('resolves preset = true to provider config', function()
|
|
helpers.reset_config({ typst = true, markdown = true })
|
|
local config = require('preview').get_config()
|
|
local presets = require('preview.presets')
|
|
assert.are.same(presets.typst, config.providers.typst)
|
|
assert.are.same(presets.markdown, config.providers.markdown)
|
|
end)
|
|
|
|
it('resolves latex preset under tex filetype', function()
|
|
helpers.reset_config({ latex = true })
|
|
local config = require('preview').get_config()
|
|
local presets = require('preview.presets')
|
|
assert.are.same(presets.latex, config.providers.tex)
|
|
end)
|
|
|
|
it('resolves github preset under markdown filetype', function()
|
|
helpers.reset_config({ github = true })
|
|
local config = require('preview').get_config()
|
|
local presets = require('preview.presets')
|
|
assert.are.same(presets.github, config.providers.markdown)
|
|
end)
|
|
end)
|
|
|
|
describe('resolve_provider', function()
|
|
before_each(function()
|
|
helpers.reset_config({
|
|
typst = true,
|
|
})
|
|
preview = require('preview')
|
|
end)
|
|
|
|
it('returns filetype when provider exists', function()
|
|
local bufnr = helpers.create_buffer({}, 'typst')
|
|
local name = preview.resolve_provider(bufnr)
|
|
assert.are.equal('typst', name)
|
|
helpers.delete_buffer(bufnr)
|
|
end)
|
|
|
|
it('returns nil for unconfigured filetype', function()
|
|
local bufnr = helpers.create_buffer({}, 'lua')
|
|
local name = preview.resolve_provider(bufnr)
|
|
assert.is_nil(name)
|
|
helpers.delete_buffer(bufnr)
|
|
end)
|
|
end)
|
|
|
|
describe('build_context', function()
|
|
it('builds context from buffer', function()
|
|
local bufnr = helpers.create_buffer({}, 'typst')
|
|
local ctx = preview.build_context(bufnr)
|
|
assert.are.equal(bufnr, ctx.bufnr)
|
|
assert.are.equal('typst', ctx.ft)
|
|
assert.is_string(ctx.file)
|
|
assert.is_string(ctx.root)
|
|
helpers.delete_buffer(bufnr)
|
|
end)
|
|
end)
|
|
|
|
describe('status', function()
|
|
it('returns idle when nothing is compiling', function()
|
|
local bufnr = helpers.create_buffer({})
|
|
local s = preview.status(bufnr)
|
|
assert.is_false(s.compiling)
|
|
assert.is_nil(s.provider)
|
|
helpers.delete_buffer(bufnr)
|
|
end)
|
|
end)
|
|
|
|
describe('statusline', function()
|
|
it('returns empty string when idle', function()
|
|
local bufnr = helpers.create_buffer({})
|
|
assert.are.equal('', preview.statusline(bufnr))
|
|
helpers.delete_buffer(bufnr)
|
|
end)
|
|
end)
|
|
|
|
describe('unnamed buffer guard', function()
|
|
before_each(function()
|
|
helpers.reset_config({ typst = true })
|
|
preview = require('preview')
|
|
end)
|
|
|
|
local function capture_notify(fn)
|
|
local msg = nil
|
|
local orig = vim.notify
|
|
vim.notify = function(m)
|
|
msg = m
|
|
end
|
|
fn()
|
|
vim.notify = orig
|
|
return msg
|
|
end
|
|
|
|
it('compile warns on unnamed buffer', function()
|
|
local bufnr = helpers.create_buffer({}, 'typst')
|
|
local msg = capture_notify(function()
|
|
preview.compile(bufnr)
|
|
end)
|
|
assert.is_not_nil(msg)
|
|
assert.is_truthy(msg:find('no file name'))
|
|
helpers.delete_buffer(bufnr)
|
|
end)
|
|
|
|
it('toggle warns on unnamed buffer', function()
|
|
local bufnr = helpers.create_buffer({}, 'typst')
|
|
local msg = capture_notify(function()
|
|
preview.toggle(bufnr)
|
|
end)
|
|
assert.is_not_nil(msg)
|
|
assert.is_truthy(msg:find('no file name'))
|
|
helpers.delete_buffer(bufnr)
|
|
end)
|
|
|
|
it('clean warns on unnamed buffer', function()
|
|
local bufnr = helpers.create_buffer({}, 'typst')
|
|
local msg = capture_notify(function()
|
|
preview.clean(bufnr)
|
|
end)
|
|
assert.is_not_nil(msg)
|
|
assert.is_truthy(msg:find('no file name'))
|
|
helpers.delete_buffer(bufnr)
|
|
end)
|
|
|
|
it('open warns on unnamed buffer', function()
|
|
local bufnr = helpers.create_buffer({}, 'typst')
|
|
local msg = capture_notify(function()
|
|
preview.open(bufnr)
|
|
end)
|
|
assert.is_not_nil(msg)
|
|
assert.is_truthy(msg:find('no file name'))
|
|
helpers.delete_buffer(bufnr)
|
|
end)
|
|
end)
|
|
end)
|