diff --git a/README.md b/README.md index dcdf689..da5f3cf 100644 --- a/README.md +++ b/README.md @@ -55,24 +55,19 @@ require('preview').setup({ **Q: How do I override a preset?** ```lua -local presets = require('preview.presets') require('preview').setup({ - typst = vim.tbl_deep_extend('force', presets.typst, { - env = { TYPST_FONT_PATHS = '/usr/share/fonts' }, - }), + typst = { env = { TYPST_FONT_PATHS = '/usr/share/fonts' } }, }) ``` **Q: How do I automatically open the output file?** Set `open = true` on your provider (all built-in presets have this enabled) to -open the output with `vim.ui.open()` after the first successful compilation. -For a specific application, pass a command table: +open the output with `vim.ui.open()` after the first successful compilation. For +a specific application, pass a command table: ```lua -typst = vim.tbl_deep_extend('force', presets.typst, { - open = { 'sioyek', '--new-instance' }, +require('preview').setup({ + typst = { open = { 'sioyek', '--new-instance' } }, }) ``` - -See `:h preview.nvim` for more information. diff --git a/doc/preview.nvim.txt b/doc/preview.nvim.txt index 3ed587a..0b44a91 100644 --- a/doc/preview.nvim.txt +++ b/doc/preview.nvim.txt @@ -1,6 +1,6 @@ *preview.nvim.txt* Async document compilation for Neovim -Author: Raphael +Author: Barrett Ruth License: MIT ============================================================================== @@ -20,19 +20,14 @@ REQUIREMENTS *preview.nvim-requirements - A compiler binary for each configured provider (e.g. `typst`, `latexmk`) ============================================================================== -INSTALLATION *preview.nvim-installation* +SETUP *preview.nvim-setup* -With luarocks (recommended): -> - :Rocks install preview.nvim -< - -With lazy.nvim: ->lua +Load preview.nvim with your package manager. For example, with lazy.nvim: >lua { 'barrettruth/preview.nvim', } < +Call |preview.setup()| to configure providers before use. ============================================================================== CONFIGURATION *preview.nvim-configuration* @@ -42,9 +37,18 @@ Configure via `require('preview').setup()`. *preview.setup()* setup({opts?}) - `opts` is a mixed table. Array entries are preset names (see - |preview.nvim-presets|). Hash entries with table values are custom - provider configs keyed by filetype. + `opts` is a table where keys are preset names or filetypes. For each + key `k` with value `v` (excluding `debug`): + + - If `k` is a preset name and `v` is `true`, the preset is registered + as-is under its filetype. + - If `k` is a preset name and `v` is a table, it is deep-merged with + the preset and registered under the preset's filetype. + - If `k` is not a preset name and `v` is a table, it is registered + directly as a custom provider keyed by filetype `k`. + - If `v` is `false`, the entry is skipped (no-op). + + See |preview.nvim-presets| for available preset names. Fields:~ @@ -91,33 +95,28 @@ Context fields:~ `root` string Project root (git root or file directory). `ft` string Filetype. -Example using preset names:~ +Example enabling presets:~ >lua - require('preview').setup({ 'typst', 'latex', 'markdown' }) + require('preview').setup({ typst = true, latex = true, github = true }) < -Example with a custom provider:~ +Example overriding a preset field:~ >lua require('preview').setup({ - typst = { - cmd = { 'typst', 'compile' }, + typst = { open = { 'sioyek', '--new-instance' } }, + }) +< + +Example with a fully custom provider (key is not a preset name):~ +>lua + require('preview').setup({ + rst = { + cmd = { 'rst2html' }, args = function(ctx) return { ctx.file } end, output = function(ctx) - return ctx.file:gsub('%.typ$', '.pdf') - end, - error_parser = function(stderr, ctx) - local diagnostics = {} - for line, col, msg in stderr:gmatch('error:.-(%d+):(%d+):%s*(.-)%\n') do - table.insert(diagnostics, { - lnum = tonumber(line) - 1, - col = tonumber(col) - 1, - message = msg, - severity = vim.diagnostic.severity.ERROR, - }) - end - return diagnostics + return ctx.file:gsub('%.rst$', '.html') end, }, }) @@ -132,20 +131,17 @@ Import them from `preview.presets`: `presets.typst` typst compile → PDF `presets.latex` latexmk -pdf → PDF (with clean support) `presets.markdown` pandoc → HTML (standalone, embedded) - `presets.github` pandoc → HTML (GitHub-styled) + `presets.github` pandoc → HTML (GitHub-styled, `-f gfm` input) -Pass preset names as array entries to `setup()`: +Enable presets with `preset_name = true`: >lua - require('preview').setup({ 'typst', 'latex', 'markdown' }) + require('preview').setup({ typst = true, latex = true, github = true }) < -Override individual fields using `vim.tbl_deep_extend`: +Override individual fields by passing a table instead of `true`: >lua - local presets = require('preview.presets') require('preview').setup({ - typst = vim.tbl_deep_extend('force', presets.typst, { - env = { TYPST_FONT_PATHS = '/usr/share/fonts' }, - }), + typst = { env = { TYPST_FONT_PATHS = '/usr/share/fonts' } }, }) < @@ -160,7 +156,7 @@ COMMANDS *preview.nvim-commands* `stop` Kill active compilation for the current buffer. `clean` Run the provider's clean command. `toggle` Toggle auto-compile on save for the current buffer. - `status` Echo compilation status (idle, compiling, watching). + `status` Echo compilation status (idle, compiling, toggled). ============================================================================== API *preview.nvim-api* @@ -175,9 +171,9 @@ preview.clean({bufnr?}) *preview.clean()* Run the provider's clean command for the buffer. preview.toggle({bufnr?}) *preview.toggle()* - Toggle watch mode for the buffer. When enabled, the buffer is + Toggle auto-compile for the buffer. When enabled, the buffer is immediately compiled and automatically recompiled on each save - (`BufWritePost`). Call again to stop watching. + (`BufWritePost`). Call again to stop. preview.status({bufnr?}) *preview.status()* Returns a |preview.Status| table. @@ -186,7 +182,7 @@ preview.status({bufnr?}) *preview.status()* Status fields:~ `compiling` boolean Whether compilation is active. - `watching` boolean Whether watch mode is active. + `watching` boolean Whether auto-compile is active. `provider` string? Name of the active provider. `output_file` string? Path to the output file. @@ -207,12 +203,6 @@ preview.nvim fires User autocmds with structured data: `PreviewCompileFailed` Compilation failed (non-zero exit). data: `{ bufnr, provider, code, stderr }` -`PreviewWatchStarted` Watch mode enabled for a buffer. - data: `{ bufnr, provider }` - -`PreviewWatchStopped` Watch mode disabled for a buffer. - data: `{ bufnr }` - Example:~ >lua vim.api.nvim_create_autocmd('User', { diff --git a/lua/preview/compiler.lua b/lua/preview/compiler.lua index 6907524..b0c7402 100644 --- a/lua/preview/compiler.lua +++ b/lua/preview/compiler.lua @@ -188,11 +188,6 @@ function M.toggle(bufnr, name, provider, ctx_builder) end, }) - vim.api.nvim_exec_autocmds('User', { - pattern = 'PreviewWatchStarted', - data = { bufnr = bufnr, provider = name }, - }) - M.compile(bufnr, name, provider, ctx_builder(bufnr)) end @@ -205,11 +200,6 @@ function M.unwatch(bufnr) vim.api.nvim_del_autocmd(au_id) watching[bufnr] = nil log.dbg('unwatched buffer %d', bufnr) - - vim.api.nvim_exec_autocmds('User', { - pattern = 'PreviewWatchStopped', - data = { bufnr = bufnr }, - }) end ---@param bufnr integer diff --git a/lua/preview/init.lua b/lua/preview/init.lua index 968b201..e2cf794 100644 --- a/lua/preview/init.lua +++ b/lua/preview/init.lua @@ -68,15 +68,17 @@ function M.setup(opts) if k == 'debug' then vim.validate('preview.setup opts.debug', v, { 'boolean', 'string' }) debug = v - elseif type(k) == 'number' then - vim.validate('preview.setup preset name', v, 'string') - local preset = presets[v] + elseif type(k) ~= 'number' then + local preset = presets[k] if preset then - providers[preset.ft] = preset + if v == true then + providers[preset.ft] = preset + elseif type(v) == 'table' then + providers[preset.ft] = vim.tbl_deep_extend('force', preset, v) + end + elseif type(v) == 'table' then + providers[k] = v end - else - vim.validate('preview.setup provider config', v, 'table') - providers[k] = v end end diff --git a/lua/preview/presets.lua b/lua/preview/presets.lua index 3579a8d..8a414e8 100644 --- a/lua/preview/presets.lua +++ b/lua/preview/presets.lua @@ -8,7 +8,7 @@ M.typst = { return { ctx.file } end, output = function(ctx) - return ctx.file:gsub('%.typ$', '.pdf') + return (ctx.file:gsub('%.typ$', '.pdf')) end, open = { 'xdg-open' }, } @@ -21,7 +21,7 @@ M.latex = { return { '-pdf', '-interaction=nonstopmode', ctx.file } end, output = function(ctx) - return ctx.file:gsub('%.tex$', '.pdf') + return (ctx.file:gsub('%.tex$', '.pdf')) end, clean = function(ctx) return { 'latexmk', '-c', ctx.file } @@ -38,7 +38,7 @@ M.markdown = { return { ctx.file, '-s', '--embed-resources', '-o', output } end, output = function(ctx) - return ctx.file:gsub('%.md$', '.html') + return (ctx.file:gsub('%.md$', '.html')) end, clean = function(ctx) return { 'rm', '-f', (ctx.file:gsub('%.md$', '.html')) } @@ -53,17 +53,19 @@ M.github = { args = function(ctx) local output = ctx.file:gsub('%.md$', '.html') return { + '-f', + 'gfm', ctx.file, '-s', '--embed-resources', '--css', - 'https://cdn.jsdelivr.net/gh/pixelbrackets/gfm-stylesheet@master/github.css', + 'https://cdn.jsdelivr.net/gh/pixelbrackets/gfm-stylesheet@master/dist/gfm.css', '-o', output, } end, output = function(ctx) - return ctx.file:gsub('%.md$', '.html') + return (ctx.file:gsub('%.md$', '.html')) end, clean = function(ctx) return { 'rm', '-f', (ctx.file:gsub('%.md$', '.html')) } diff --git a/spec/compiler_spec.lua b/spec/compiler_spec.lua index 7360ceb..6802dee 100644 --- a/spec/compiler_spec.lua +++ b/spec/compiler_spec.lua @@ -190,31 +190,6 @@ describe('compiler', function() helpers.delete_buffer(bufnr) end) - it('fires PreviewWatchStarted event', function() - local bufnr = helpers.create_buffer({ 'hello' }, 'text') - vim.api.nvim_buf_set_name(bufnr, '/tmp/preview_test_watch_event.txt') - - local fired = false - vim.api.nvim_create_autocmd('User', { - pattern = 'PreviewWatchStarted', - once = true, - callback = function() - fired = true - end, - }) - - local provider = { cmd = { 'echo', 'ok' } } - local ctx_builder = function(b) - return { bufnr = b, file = '/tmp/preview_test_watch_event.txt', root = '/tmp', ft = 'text' } - end - - compiler.toggle(bufnr, 'echo', provider, ctx_builder) - assert.is_true(fired) - - compiler.unwatch(bufnr) - helpers.delete_buffer(bufnr) - end) - it('toggles off when called again', function() local bufnr = helpers.create_buffer({ 'hello' }, 'text') vim.api.nvim_buf_set_name(bufnr, '/tmp/preview_test_watch_toggle.txt') @@ -233,32 +208,6 @@ describe('compiler', function() helpers.delete_buffer(bufnr) end) - it('fires PreviewWatchStopped on unwatch', function() - local bufnr = helpers.create_buffer({ 'hello' }, 'text') - vim.api.nvim_buf_set_name(bufnr, '/tmp/preview_test_watch_stop.txt') - - local stopped = false - vim.api.nvim_create_autocmd('User', { - pattern = 'PreviewWatchStopped', - once = true, - callback = function() - stopped = true - end, - }) - - local provider = { cmd = { 'echo', 'ok' } } - local ctx_builder = function(b) - return { bufnr = b, file = '/tmp/preview_test_watch_stop.txt', root = '/tmp', ft = 'text' } - end - - compiler.toggle(bufnr, 'echo', provider, ctx_builder) - compiler.unwatch(bufnr) - assert.is_true(stopped) - assert.is_nil(compiler._test.watching[bufnr]) - - helpers.delete_buffer(bufnr) - end) - it('stop_all clears watches', function() local bufnr = helpers.create_buffer({ 'hello' }, 'text') vim.api.nvim_buf_set_name(bufnr, '/tmp/preview_test_watch_stopall.txt') diff --git a/spec/init_spec.lua b/spec/init_spec.lua index 7921aeb..5b97c8a 100644 --- a/spec/init_spec.lua +++ b/spec/init_spec.lua @@ -22,7 +22,7 @@ describe('preview', function() assert.are.same({}, config.providers) end) - it('accepts full provider config via hash entry', function() + it('merges override table with matching preset', function() helpers.reset_config({ typst = { cmd = { 'typst', 'compile' }, @@ -33,8 +33,8 @@ describe('preview', function() assert.is_not_nil(config.providers.typst) end) - it('resolves array preset names to provider configs', function() - helpers.reset_config({ 'typst', 'markdown' }) + 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) @@ -42,14 +42,14 @@ describe('preview', function() end) it('resolves latex preset under tex filetype', function() - helpers.reset_config({ 'latex' }) + 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' }) + helpers.reset_config({ github = true }) local config = require('preview').get_config() local presets = require('preview.presets') assert.are.same(presets.github, config.providers.markdown) @@ -59,7 +59,7 @@ describe('preview', function() describe('resolve_provider', function() before_each(function() helpers.reset_config({ - typst = { cmd = { 'typst', 'compile' } }, + typst = true, }) preview = require('preview') end) diff --git a/spec/presets_spec.lua b/spec/presets_spec.lua index 2eba98f..b8572c5 100644 --- a/spec/presets_spec.lua +++ b/spec/presets_spec.lua @@ -139,16 +139,31 @@ describe('presets', function() local args = presets.github.args(md_ctx) assert.is_table(args) assert.are.same({ + '-f', + 'gfm', '/tmp/document.md', '-s', '--embed-resources', '--css', - 'https://cdn.jsdelivr.net/gh/pixelbrackets/gfm-stylesheet@master/github.css', + 'https://cdn.jsdelivr.net/gh/pixelbrackets/gfm-stylesheet@master/dist/gfm.css', '-o', '/tmp/document.html', }, args) end) + it('args include -f and gfm flags', function() + local args = presets.github.args(md_ctx) + local idx = nil + for i, v in ipairs(args) do + if v == '-f' then + idx = i + break + end + end + assert.is_not_nil(idx) + assert.are.equal('gfm', args[idx + 1]) + end) + it('returns html output path', function() local output = presets.github.output(md_ctx) assert.is_string(output)