diff --git a/README.md b/README.md index 80a2650..c7cc7f3 100644 --- a/README.md +++ b/README.md @@ -2,18 +2,17 @@ **Universal document previewer for Neovim** -An extensible framework for compiling and previewing _any_ documents (LaTeX, -Typst, Markdown, etc.)—diagnostics included. - - +An extensible framework for compiling documents (LaTeX, Typst, Markdown, etc.) +asynchronously with error diagnostics. ## Features - Async compilation via `vim.system()` - Built-in presets for Typst, LaTeX (latexmk, pdflatex, tectonic), Markdown, - GitHub-flavored Markdown, AsciiDoc, PlantUML, Mermaid, and Quarto -- Compiler errors via `vim.diagnostic` or quickfix -- Previewer auto-close on buffer deletion + GitHub-flavored Markdown, AsciiDoc, and Quarto +- Compiler errors as native `vim.diagnostic` +- User events for extensibility (`PreviewCompileStarted`, + `PreviewCompileSuccess`, `PreviewCompileFailed`) ## Requirements @@ -21,18 +20,8 @@ Typst, Markdown, etc.)—diagnostics included. ## Installation -With lazy.nvim: - -```lua -{ - 'barrettruth/preview.nvim', - init = function() - vim.g.preview = { typst = true, latex = true } - end, -} -``` - -Or via [luarocks](https://luarocks.org/modules/barrettruth/preview.nvim): +Install with your package manager of choice or via +[luarocks](https://luarocks.org/modules/barrettruth/preview.nvim): ``` luarocks install preview.nvim @@ -49,7 +38,7 @@ luarocks install preview.nvim **Q: How do I define a custom provider?** ```lua -vim.g.preview = { +require('preview').setup({ rst = { cmd = { 'rst2html' }, args = function(ctx) @@ -59,15 +48,15 @@ vim.g.preview = { return ctx.file:gsub('%.rst$', '.html') end, }, -} +}) ``` **Q: How do I override a preset?** ```lua -vim.g.preview = { +require('preview').setup({ typst = { env = { TYPST_FONT_PATHS = '/usr/share/fonts' } }, -} +}) ``` **Q: How do I automatically open the output file?** @@ -77,7 +66,7 @@ open the output with `vim.ui.open()` after the first successful compilation in toggle/watch mode. For a specific application, pass a command table: ```lua -vim.g.preview = { +require('preview').setup({ typst = { open = { 'sioyek', '--new-instance' } }, -} +}) ``` diff --git a/doc/preview.nvim.txt b/doc/preview.nvim.txt new file mode 100644 index 0000000..6cba09a --- /dev/null +++ b/doc/preview.nvim.txt @@ -0,0 +1,269 @@ +*preview.nvim.txt* Async document compilation for Neovim + +Author: Barrett Ruth +License: MIT + +============================================================================== +INTRODUCTION *preview.nvim* + +preview.nvim is an extensible framework for compiling documents asynchronously +in Neovim. It provides a unified interface for any compilation workflow — +LaTeX, Typst, Markdown, or anything else with a CLI compiler. + +The plugin ships with opt-in presets for common tools (Typst, LaTeX, Pandoc, +AsciiDoc, Quarto) and supports fully custom providers. +See |preview.nvim-presets|. + +============================================================================== +REQUIREMENTS *preview.nvim-requirements* + +- Neovim >= 0.11.0 +- A compiler binary for each configured provider (e.g. `typst`, `latexmk`) + +============================================================================== +SETUP *preview.nvim-setup* + +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* + +Configure via `require('preview').setup()`. + + *preview.setup()* +setup({opts?}) + + `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:~ + + `debug` boolean|string Enable debug logging. A string value + is treated as a log file path. + Default: `false` + + *preview.ProviderConfig* +Provider fields:~ + + `cmd` string[] The compiler command (required). + + `args` string[]|function Additional arguments. If a function, + receives a |preview.Context| and returns + a string[]. + + `cwd` string|function Working directory. If a function, + receives a |preview.Context|. Default: + git root or file directory. + + `env` table Environment variables. + + `output` string|function Output file path. If a function, + receives a |preview.Context|. + + `error_parser` function Receives (output, |preview.Context|) + and returns vim.Diagnostic[]. + + `errors` false|'diagnostic'|'quickfix' + How parse errors are reported. + `false` suppresses error handling. + `'quickfix'` populates the quickfix + list and opens it. Default: + `'diagnostic'`. + + `clean` string[]|function Command to remove build artifacts. + If a function, receives a + |preview.Context|. + + `open` boolean|string[] Open the output file after the first + successful compilation in toggle/watch + mode. `true` uses |vim.ui.open()|. A + string[] is run as a command with the + output path appended. + + `reload` boolean|string[]|function + Reload the output after recompilation. + `true` uses a built-in SSE server for + HTML files. A string[] is run as a + command. If a function, receives a + |preview.Context| and returns a + string[]. + + *preview.Context* +Context fields:~ + + `bufnr` integer Buffer number. + `file` string Absolute file path. + `root` string Project root (git root or file directory). + `ft` string Filetype. + `output` string? Resolved output file path (set after `output` + is evaluated, available to `args` functions). + +Example enabling presets:~ +>lua + require('preview').setup({ typst = true, latex = true, github = true }) +< + +Example overriding a preset field:~ +>lua + require('preview').setup({ + typst = { open = { 'sioyek', '--new-instance' } }, + }) +< + +Example overriding the output path (e.g. latexmk `$out_dir`):~ +>lua + require('preview').setup({ + latex = { + output = function(ctx) + return 'build/' .. vim.fn.fnamemodify(ctx.file, ':t:r') .. '.pdf' + end, + }, + }) +< + +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('%.rst$', '.html') + end, + }, + }) +< + +============================================================================== +PRESETS *preview.nvim-presets* + +preview.nvim ships with pre-built provider configurations for common tools. +Import them from `preview.presets`: + + `presets.typst` typst compile → PDF + `presets.latex` latexmk -pdf → PDF (with clean support) + `presets.pdflatex` pdflatex → PDF (single pass, no latexmk) + `presets.tectonic` tectonic → PDF (Rust-based LaTeX engine) + `presets.markdown` pandoc → HTML (standalone, embedded) + `presets.github` pandoc → HTML (GitHub-styled, `-f gfm` input) + `presets.asciidoctor` asciidoctor → HTML (AsciiDoc with SSE reload) + `presets.quarto` quarto render → HTML (scientific publishing) + +Enable presets with `preset_name = true`: +>lua + require('preview').setup({ typst = true, latex = true, github = true }) +< + +Override individual fields by passing a table instead of `true`: +>lua + require('preview').setup({ + typst = { env = { TYPST_FONT_PATHS = '/usr/share/fonts' } }, + }) +< + +============================================================================== +COMMANDS *preview.nvim-commands* + +:Preview [subcommand] *:Preview* + + Subcommands:~ + + `toggle` Toggle auto-compile on save (default if omitted). + `compile` One-shot compile of the current buffer. + `clean` Run the provider's clean command. + `open` Open the last compiled output without recompiling. + `status` Echo compilation status (idle, compiling, watching). + +============================================================================== +API *preview.nvim-api* + +preview.toggle({bufnr?}) *preview.toggle()* + Toggle auto-compile for the buffer. When enabled, the buffer is + immediately compiled and automatically recompiled on each save + (`BufWritePost`). Call again to stop. + +preview.compile({bufnr?}) *preview.compile()* + One-shot compile the document in the given buffer (default: current). + +preview.stop({bufnr?}) *preview.stop()* + Kill the active compilation process for the buffer. Programmatic + escape hatch — not exposed as a subcommand. + +preview.clean({bufnr?}) *preview.clean()* + Run the provider's clean command for the buffer. + +preview.open({bufnr?}) *preview.open()* + Open the last compiled output for the buffer without recompiling. + +preview.status({bufnr?}) *preview.status()* + Returns a |preview.Status| table. + +preview.statusline({bufnr?}) *preview.statusline()* + Returns a short status string for statusline integration: + `'compiling'`, `'watching'`, or `''` (idle). + + *preview.Status* +Status fields:~ + + `compiling` boolean Whether compilation is active. + `watching` boolean Whether auto-compile is active. + `provider` string? Name of the active provider. + `output_file` string? Path to the output file. + +preview.get_config() *preview.get_config()* + Returns the resolved |preview.Config|. + +============================================================================== +EVENTS *preview.nvim-events* + +preview.nvim fires User autocmds with structured data: + +`PreviewCompileStarted` Compilation began. + data: `{ bufnr, provider }` + +`PreviewCompileSuccess` Compilation succeeded (exit code 0). + data: `{ bufnr, provider, output }` + +`PreviewCompileFailed` Compilation failed (non-zero exit). + data: `{ bufnr, provider, code, stderr }` + +Example:~ +>lua + vim.api.nvim_create_autocmd('User', { + pattern = 'PreviewCompileSuccess', + callback = function(args) + local data = args.data + vim.notify('Compiled ' .. data.output .. ' with ' .. data.provider) + end, + }) +< + +============================================================================== +HEALTH *preview.nvim-health* + +Run `:checkhealth preview` to verify: + +- Neovim version >= 0.11.0 +- Each configured provider's binary is executable +- Each configured provider's opener binary (if any) is executable +- Each configured provider's filetype mapping is valid + +============================================================================== + vim:tw=78:ts=8:ft=help:norl: diff --git a/doc/preview.txt b/doc/preview.txt deleted file mode 100644 index 383a8f5..0000000 --- a/doc/preview.txt +++ /dev/null @@ -1,276 +0,0 @@ -*preview.txt* Async document compilation for Neovim - -Author: Barrett Ruth -License: MIT - -============================================================================== -INTRODUCTION *preview.nvim* - -preview.nvim is an extensible framework for compiling documents asynchronously -in Neovim. It provides a unified interface for any compilation workflow — -LaTeX, Typst, Markdown, or anything else with a CLI compiler. - -The plugin ships with opt-in presets for common tools (Typst, LaTeX, Pandoc, -AsciiDoc, PlantUML, Mermaid, Quarto) and supports fully custom providers. -See |preview-presets|. - -============================================================================== -CONTENTS *preview-contents* - - 1. Introduction ............................................. |preview.nvim| - 2. Requirements ..................................... |preview-requirements| - 3. Install ............................................... |preview-install| - 4. Configuration ........................................... |preview-config| - 5. Presets ............................................... |preview-presets| - 6. Commands ............................................. |preview-commands| - 7. Lua API ................................................... |preview-api| - 8. Events ............................................... |preview-events| - 9. Health ............................................... |preview-health| - -============================================================================== -REQUIREMENTS *preview-requirements* - -- Neovim >= 0.11.0 -- A compiler binary for each configured provider (e.g. `typst`, `latexmk`) - -============================================================================== -INSTALL *preview-install* - -Install with lazy.nvim: >lua - { 'barrettruth/preview.nvim' } -< - -No `setup()` call is needed. The plugin loads automatically when -|vim.g.preview| is set. See |preview-config|. - -============================================================================== -CONFIGURATION *preview-config* - -Configure by setting |vim.g.preview| to 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-presets| for available preset names. - - *preview.ProviderConfig* -Provider fields: ~ - - {cmd} (string[]) Compiler command (required). - - {args} (string[]|function) Additional arguments. If a function, - receives a |preview.Context| and - returns a string[]. - - {cwd} (string|function) Working directory. If a function, - receives a |preview.Context|. - Default: git root or file directory. - - {env} (table) Environment variables. - - {output} (string|function) Output file path. If a function, - receives a |preview.Context|. - - {error_parser} (function) Receives (output, |preview.Context|) - and returns vim.Diagnostic[]. - - {errors} (false|'diagnostic'|'quickfix') - How parse errors are reported. - `false` suppresses error handling. - `'quickfix'` populates the quickfix - list and opens it. - Default: `'diagnostic'`. - - {clean} (string[]|function) Command to remove build artifacts. - If a function, receives a - |preview.Context|. - - {open} (boolean|string[]) Open the output file after the first - successful compilation in toggle/watch - mode. `true` uses |vim.ui.open()|. A - string[] is run as a command with the - output path appended. When a string[] - is used the viewer process is tracked - and sent SIGTERM when the buffer is - deleted. `true` and single-instance - apps (e.g. Chrome) do not support - auto-close. - - {reload} (boolean|string[]|function) - Reload the output after recompilation. - `true` uses a built-in SSE server for - HTML files. A string[] is run as a - command. If a function, receives a - |preview.Context| and returns a - string[]. - - {detach} (boolean) When `true`, the viewer process opened - via a string[] `open` command is not - sent SIGTERM when the buffer is - deleted. Has no effect when `open` is - `true`. Default: `false`. - - *preview.Context* -Context fields: ~ - - {bufnr} (integer) Buffer number. - {file} (string) Absolute file path. - {root} (string) Project root (git root or file directory). - {ft} (string) Filetype. - {output} (string?) Resolved output file path (set after `output` - is evaluated, available to `args` functions). - -Global options: ~ - - {debug} (boolean|string) Enable debug logging. A string value is treated - as a log file path. Default: `false`. - -Example enabling presets: >lua - vim.g.preview = { typst = true, latex = true, github = true } -< - -Example overriding a preset field: >lua - vim.g.preview = { - typst = { open = { 'sioyek', '--new-instance' } }, - } -< - -Example overriding the output path (e.g. latexmk `$out_dir`): >lua - vim.g.preview = { - latex = { - output = function(ctx) - return 'build/' .. vim.fn.fnamemodify(ctx.file, ':t:r') .. '.pdf' - end, - }, - } -< - -Example with a fully custom provider (key is not a preset name): >lua - vim.g.preview = { - rst = { - cmd = { 'rst2html' }, - args = function(ctx) - return { ctx.file } - end, - output = function(ctx) - return ctx.file:gsub('%.rst$', '.html') - end, - }, - } -< - -============================================================================== -PRESETS *preview-presets* - -Built-in provider configurations. Enable with `preset_name = true` or -override individual fields by passing a table instead: >lua - vim.g.preview = { typst = true, latex = true, github = true } -< - - `typst` typst compile → PDF - `latex` latexmk -pdf → PDF (with clean support) - `pdflatex` pdflatex → PDF (single pass, no latexmk) - `tectonic` tectonic → PDF (Rust-based LaTeX engine) - `markdown` pandoc → HTML (standalone, embedded) - `github` pandoc → HTML (GitHub-styled, `-f gfm` input) - `asciidoctor` asciidoctor → HTML (AsciiDoc with SSE reload) - `plantuml` plantuml → SVG (UML diagrams, `.puml`) - `mermaid` mmdc → SVG (Mermaid diagrams, `.mmd`) - `quarto` quarto render → HTML (scientific publishing) - -============================================================================== -COMMANDS *preview-commands* - -:Preview [subcommand] *:Preview* - - Subcommands: ~ - - `toggle` Toggle auto-compile on save (default if omitted). - `compile` One-shot compile of the current buffer. - `clean` Run the provider's clean command. - `open` Open the last compiled output without recompiling. - `status` Echo compilation status (idle, compiling, watching). - -============================================================================== -LUA API *preview-api* - -preview.toggle({bufnr?}) *preview.toggle()* - Toggle auto-compile for the buffer. When enabled, the buffer is - immediately compiled and automatically recompiled on each save - (`BufWritePost`). Call again to stop. - -preview.compile({bufnr?}) *preview.compile()* - One-shot compile the document in the given buffer (default: current). - -preview.stop({bufnr?}) *preview.stop()* - Kill the active compilation process for the buffer. Programmatic - escape hatch — not exposed as a subcommand. - -preview.clean({bufnr?}) *preview.clean()* - Run the provider's clean command for the buffer. - -preview.open({bufnr?}) *preview.open()* - Open the last compiled output for the buffer without recompiling. - -preview.status({bufnr?}) *preview.status()* - Returns a |preview.Status| table. - -preview.statusline({bufnr?}) *preview.statusline()* - Returns a short status string for statusline integration: - `'compiling'`, `'watching'`, or `''` (idle). - -preview.get_config() *preview.get_config()* - Returns the resolved |preview.Config|. - - *preview.Status* -Status fields: ~ - - {compiling} (boolean) Whether compilation is active. - {watching} (boolean) Whether auto-compile is active. - {provider} (string?) Name of the active provider. - {output_file} (string?) Path to the output file. - -============================================================================== -EVENTS *preview-events* - -preview.nvim fires User autocmds with structured data: - -`PreviewCompileStarted` Compilation began. - data: `{ bufnr, provider }` - -`PreviewCompileSuccess` Compilation succeeded (exit code 0). - data: `{ bufnr, provider, output }` - -`PreviewCompileFailed` Compilation failed (non-zero exit). - data: `{ bufnr, provider, code, stderr }` - -Example: >lua - vim.api.nvim_create_autocmd('User', { - pattern = 'PreviewCompileSuccess', - callback = function(args) - local data = args.data - vim.notify('Compiled ' .. data.output .. ' with ' .. data.provider) - end, - }) -< - -============================================================================== -HEALTH *preview-health* - -Run |:checkhealth| preview to verify your setup: >vim - :checkhealth preview -< - -Checks: ~ -- Neovim version >= 0.11.0 -- Each configured provider's binary is executable -- Each configured provider's opener binary (if any) is executable - -============================================================================== - vim:tw=78:ts=8:ft=help:norl: diff --git a/flake.nix b/flake.nix index 636f4d0..91a0ab2 100644 --- a/flake.nix +++ b/flake.nix @@ -19,9 +19,9 @@ { formatter = forEachSystem (pkgs: pkgs.nixfmt-tree); - devShells = forEachSystem (pkgs: - let - devTools = [ + devShells = forEachSystem (pkgs: { + default = pkgs.mkShell { + packages = [ (pkgs.luajit.withPackages ( ps: with ps; [ busted @@ -33,23 +33,7 @@ pkgs.selene pkgs.lua-language-server ]; - in - { - default = pkgs.mkShell { - packages = devTools; - }; - presets = pkgs.mkShell { - packages = devTools ++ [ - pkgs.typst - pkgs.texliveMedium - pkgs.tectonic - pkgs.pandoc - pkgs.asciidoctor - pkgs.quarto - pkgs.plantuml - pkgs.mermaid-cli - ]; - }; - }); + }; + }); }; } diff --git a/lua/preview/compiler.lua b/lua/preview/compiler.lua index a193ad2..38a048e 100644 --- a/lua/preview/compiler.lua +++ b/lua/preview/compiler.lua @@ -15,49 +15,10 @@ local opened = {} ---@type table local last_output = {} ----@type table -local viewer_procs = {} - ----@type table -local open_watchers = {} - local debounce_timers = {} local DEBOUNCE_MS = 500 ----@param bufnr integer -local function stop_open_watcher(bufnr) - local w = open_watchers[bufnr] - if w then - w:stop() - w:close() - open_watchers[bufnr] = nil - end -end - ----@param bufnr integer -local function close_viewer(bufnr) - local obj = viewer_procs[bufnr] - if obj then - local kill = obj.kill - kill(obj, 'sigterm') - viewer_procs[bufnr] = nil - end -end - ----@param bufnr integer ----@param output_file string ----@param open_config boolean|string[] -local function do_open(bufnr, output_file, open_config) - if open_config == true then - vim.ui.open(output_file) - elseif type(open_config) == 'table' then - local open_cmd = vim.list_extend({}, open_config) - table.insert(open_cmd, output_file) - viewer_procs[bufnr] = vim.system(open_cmd) - end -end - ---@param val string[]|fun(ctx: preview.Context): string[] ---@param ctx preview.Context ---@return string[] @@ -143,47 +104,12 @@ 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 @@ -217,9 +143,7 @@ function M.compile(bufnr, name, provider, ctx, opts) }) end vim.fn.setqflist(items, 'r') - local win = vim.fn.win_getid() - vim.cmd.cwindow() - vim.fn.win_gotoid(win) + vim.cmd('copen') end end end @@ -236,51 +160,21 @@ function M.compile(bufnr, name, provider, ctx, opts) end) ) - if provider.open and not opts.oneshot and not opened[bufnr] and output_file ~= '' then - local pre_stat = vim.uv.fs_stat(output_file) - local pre_mtime = pre_stat and pre_stat.mtime.sec or 0 - local out_dir = vim.fn.fnamemodify(output_file, ':h') - local out_name = vim.fn.fnamemodify(output_file, ':t') - stop_open_watcher(bufnr) - local watcher = vim.uv.new_fs_event() - if watcher then - open_watchers[bufnr] = watcher - watcher:start( - out_dir, - {}, - vim.schedule_wrap(function(err, filename, _events) - if err or vim.fn.fnamemodify(filename or '', ':t') ~= out_name then - return - end - if opened[bufnr] then - stop_open_watcher(bufnr) - return - end - if not vim.api.nvim_buf_is_valid(bufnr) then - stop_open_watcher(bufnr) - return - end - local new_stat = vim.uv.fs_stat(output_file) - if not (new_stat and new_stat.mtime.sec > pre_mtime) then - 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) - ) + if + provider.open + and not opts.oneshot + and not opened[bufnr] + and output_file ~= '' + and vim.uv.fs_stat(output_file) + then + if provider.open == true then + vim.ui.open(output_file) + elseif type(provider.open) == 'table' then + local open_cmd = vim.list_extend({}, provider.open) + table.insert(open_cmd, output_file) + vim.system(open_cmd) end + opened[bufnr] = true end active[bufnr] = { obj = obj, provider = name, output_file = output_file, is_reload = true } @@ -290,10 +184,6 @@ function M.compile(bufnr, name, provider, ctx, opts) once = true, callback = function() M.stop(bufnr) - stop_open_watcher(bufnr) - if not provider.detach then - close_viewer(bufnr) - end last_output[bufnr] = nil end, }) @@ -338,7 +228,6 @@ 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', @@ -357,7 +246,13 @@ function M.compile(bufnr, name, provider, ctx, opts) and output_file ~= '' and vim.uv.fs_stat(output_file) then - do_open(bufnr, output_file, provider.open) + if provider.open == true then + vim.ui.open(output_file) + elseif type(provider.open) == 'table' then + local open_cmd = vim.list_extend({}, provider.open) + table.insert(open_cmd, output_file) + vim.system(open_cmd) + end opened[bufnr] = true end else @@ -380,9 +275,7 @@ function M.compile(bufnr, name, provider, ctx, opts) }) end vim.fn.setqflist(items, 'r') - local win = vim.fn.win_getid() - vim.cmd.cwindow() - vim.fn.win_gotoid(win) + vim.cmd('copen') end end end @@ -406,9 +299,6 @@ function M.compile(bufnr, name, provider, ctx, opts) once = true, callback = function() M.stop(bufnr) - if not provider.detach then - close_viewer(bufnr) - end last_output[bufnr] = nil end, }) @@ -449,12 +339,6 @@ function M.stop_all() for bufnr, _ in pairs(watching) do M.unwatch(bufnr) end - for bufnr, _ in pairs(open_watchers) do - stop_open_watcher(bufnr) - end - for bufnr, _ in pairs(viewer_procs) do - close_viewer(bufnr) - end require('preview.reload').stop() end @@ -510,10 +394,6 @@ function M.toggle(bufnr, name, provider, ctx_builder) once = true, callback = function() M.unwatch(bufnr) - stop_open_watcher(bufnr) - if not provider.detach then - close_viewer(bufnr) - end opened[bufnr] = nil end, }) @@ -591,7 +471,13 @@ function M.open(bufnr, open_config) log.dbg('output file no longer exists for buffer %d: %s', bufnr, output) return false end - do_open(bufnr, output, open_config) + if type(open_config) == 'table' then + local open_cmd = vim.list_extend({}, open_config) + table.insert(open_cmd, output) + vim.system(open_cmd) + else + vim.ui.open(output) + end return true end @@ -616,8 +502,6 @@ M._test = { opened = opened, last_output = last_output, debounce_timers = debounce_timers, - viewer_procs = viewer_procs, - open_watchers = open_watchers, } return M diff --git a/lua/preview/init.lua b/lua/preview/init.lua index 7cb982b..fd54d71 100644 --- a/lua/preview/init.lua +++ b/lua/preview/init.lua @@ -10,7 +10,6 @@ ---@field clean? string[]|fun(ctx: preview.Context): string[] ---@field open? boolean|string[] ---@field reload? boolean|string[]|fun(ctx: preview.Context): string[] ----@field detach? boolean ---@class preview.Config ---@field debug boolean|string @@ -102,13 +101,6 @@ function M.setup(opts) end, 'false, "diagnostic", or "quickfix"') vim.validate(prefix .. '.open', provider.open, { 'boolean', 'table' }, true) vim.validate(prefix .. '.reload', provider.reload, { 'boolean', 'table', 'function' }, true) - vim.validate(prefix .. '.detach', provider.detach, 'boolean', true) - end - - if providers['plantuml'] then - vim.filetype.add({ - extension = { puml = 'plantuml', pu = 'plantuml' }, - }) end config = vim.tbl_deep_extend('force', default_config, { @@ -254,8 +246,4 @@ M._test = { end, } -if vim.g.preview then - M.setup(vim.g.preview) -end - return M diff --git a/lua/preview/presets.lua b/lua/preview/presets.lua index 1b5333e..f189e98 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', '--diagnostic-format', 'short', ctx.file } + return { 'typst', 'watch', ctx.file } end, } @@ -271,68 +271,6 @@ M.asciidoctor = { reload = true, } ----@type preview.ProviderConfig -M.plantuml = { - ft = 'plantuml', - cmd = { 'plantuml' }, - args = function(ctx) - return { '-tsvg', ctx.file } - end, - output = function(ctx) - return (ctx.file:gsub('%.puml$', '.svg')) - end, - error_parser = function(output) - local diagnostics = {} - for line in output:gmatch('[^\r\n]+') do - local lnum = line:match('^Error line (%d+) in file:') - if lnum then - table.insert(diagnostics, { - lnum = tonumber(lnum) - 1, - col = 0, - message = line, - severity = vim.diagnostic.severity.ERROR, - }) - end - end - return diagnostics - end, - clean = function(ctx) - return { 'rm', '-f', (ctx.file:gsub('%.puml$', '.svg')) } - end, - open = true, -} - ----@type preview.ProviderConfig -M.mermaid = { - ft = 'mermaid', - cmd = { 'mmdc' }, - args = function(ctx) - return { '-i', ctx.file, '-o', ctx.output } - end, - output = function(ctx) - return (ctx.file:gsub('%.mmd$', '.svg')) - end, - error_parser = function(output) - local diagnostics = {} - for line in output:gmatch('[^\r\n]+') do - local lnum = line:match('^%s*Parse error on line (%d+)') - if lnum then - table.insert(diagnostics, { - lnum = tonumber(lnum) - 1, - col = 0, - message = line, - severity = vim.diagnostic.severity.ERROR, - }) - end - end - return diagnostics - end, - clean = function(ctx) - return { 'rm', '-f', (ctx.file:gsub('%.mmd$', '.svg')) } - end, - open = true, -} - ---@type preview.ProviderConfig M.quarto = { ft = 'quarto', diff --git a/spec/presets_spec.lua b/spec/presets_spec.lua index 2160dfa..2a63d18 100644 --- a/spec/presets_spec.lua +++ b/spec/presets_spec.lua @@ -50,9 +50,7 @@ describe('presets', function() assert.is_table(result) assert.are.equal('typst', result[1]) assert.are.equal('watch', result[2]) - assert.are.equal('--diagnostic-format', result[3]) - assert.are.equal('short', result[4]) - assert.are.equal(ctx.file, result[5]) + assert.are.equal(ctx.file, result[3]) end) it('parses errors from stderr', function()