Compare commits

...
Sign in to create a new pull request.

5 commits

Author SHA1 Message Date
3b15ed7c6c
docs: document extra_args provider field 2026-03-05 22:07:53 -05:00
7f1f8f8d65
feat: add extra_args provider field
Problem: Overriding a single flag (e.g. `-outdir=build`) required
redefining the entire `args` function, duplicating all preset defaults.

Solution: Add `extra_args` field that appends to the resolved `args`
after evaluation. Accepts a static table or a context function.
2026-03-05 22:05:36 -05:00
Barrett Ruth
1fbc307bad
Update README to refine project description 2026-03-05 15:24:52 -05:00
Barrett Ruth
00caad18bf
refactor(presets): simplify mermaid error parser (#49)
* feat: add `mermaid` preset

Problem: no built-in support for compiling mermaid diagrams via `mmdc`.

Solution: add a `mermaid` preset that compiles `.mmd` files to SVG and
parses `Parse error on line N` diagnostics from stderr. Add
`mermaid-cli` to the nix dev shell.

* refactor(presets): simplify `mermaid` error parser

Problem: the inline `mermaid` error_parser looped over every line and
used the `Parse error on line N:` header as the message, losing the
useful `Expecting ..., got ...` token detail.

Solution: extract `parse_mermaid` alongside the other parse functions,
use a single `output:match` (mermaid's JISON parser stops at the first
error), and surface the `Expecting ..., got ...` line as the message.

* ci: format

* ci: format
2026-03-05 12:05:34 -05:00
Barrett Ruth
8c9847e321
build: split nix dev shell into default and presets (#48)
Problem: the single dev shell mixed dev tooling (linters, test runner)
with preset compiler tools, causing heavy rebuilds (e.g. Chromium for
`mermaid-cli`) for contributors who only need the dev tools.

Solution: extract dev tooling into a shared `devTools` list and expose
two shells — `default` for development and `presets` for running all
built-in preset compilers (`typst`, `texliveMedium`, `tectonic`,
`pandoc`, `asciidoctor`, `quarto`, `plantuml`, `mermaid-cli`).
2026-03-05 11:05:16 -05:00
6 changed files with 52 additions and 21 deletions

View file

@ -1,6 +1,6 @@
# preview.nvim # preview.nvim
**Universal document previewer for Neovim** **Universal previewer for Neovim**
An extensible framework for compiling and previewing _any_ documents (LaTeX, An extensible framework for compiling and previewing _any_ documents (LaTeX,
Typst, Markdown, etc.)—diagnostics included. Typst, Markdown, etc.)—diagnostics included.

View file

@ -68,6 +68,10 @@ Provider fields: ~
receives a |preview.Context| and receives a |preview.Context| and
returns a string[]. returns a string[].
{extra_args} (string[]|function) Appended to {args} after evaluation.
Useful for adding flags to a preset
without replacing its defaults.
{cwd} (string|function) Working directory. If a function, {cwd} (string|function) Working directory. If a function,
receives a |preview.Context|. receives a |preview.Context|.
Default: git root or file directory. Default: git root or file directory.

View file

@ -19,9 +19,10 @@
{ {
formatter = forEachSystem (pkgs: pkgs.nixfmt-tree); formatter = forEachSystem (pkgs: pkgs.nixfmt-tree);
devShells = forEachSystem (pkgs: { devShells = forEachSystem (
default = pkgs.mkShell { pkgs:
packages = [ let
devTools = [
(pkgs.luajit.withPackages ( (pkgs.luajit.withPackages (
ps: with ps; [ ps: with ps; [
busted busted
@ -32,10 +33,25 @@
pkgs.stylua pkgs.stylua
pkgs.selene pkgs.selene
pkgs.lua-language-server pkgs.lua-language-server
pkgs.plantuml
pkgs.mermaid-cli
]; ];
}; 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
];
};
}
);
}; };
} }

View file

@ -309,6 +309,9 @@ function M.compile(bufnr, name, provider, ctx, opts)
if provider.args then if provider.args then
vim.list_extend(cmd, eval_list(provider.args, resolved_ctx)) vim.list_extend(cmd, eval_list(provider.args, resolved_ctx))
end end
if provider.extra_args then
vim.list_extend(cmd, eval_list(provider.extra_args, resolved_ctx))
end
log.dbg('compiling buffer %d with provider "%s": %s', bufnr, name, table.concat(cmd, ' ')) log.dbg('compiling buffer %d with provider "%s": %s', bufnr, name, table.concat(cmd, ' '))

View file

@ -2,6 +2,7 @@
---@field ft? string ---@field ft? string
---@field cmd string[] ---@field cmd string[]
---@field args? string[]|fun(ctx: preview.Context): string[] ---@field args? string[]|fun(ctx: preview.Context): string[]
---@field extra_args? string[]|fun(ctx: preview.Context): string[]
---@field cwd? string|fun(ctx: preview.Context): string ---@field cwd? string|fun(ctx: preview.Context): string
---@field env? table<string, string> ---@field env? table<string, string>
---@field output? string|fun(ctx: preview.Context): string ---@field output? string|fun(ctx: preview.Context): string
@ -94,6 +95,7 @@ function M.setup(opts)
vim.validate(prefix .. '.cmd', provider.cmd, 'table') vim.validate(prefix .. '.cmd', provider.cmd, 'table')
vim.validate(prefix .. '.cmd[1]', provider.cmd[1], 'string') vim.validate(prefix .. '.cmd[1]', provider.cmd[1], 'string')
vim.validate(prefix .. '.args', provider.args, { 'table', 'function' }, true) vim.validate(prefix .. '.args', provider.args, { 'table', 'function' }, true)
vim.validate(prefix .. '.extra_args', provider.extra_args, { 'table', 'function' }, true)
vim.validate(prefix .. '.cwd', provider.cwd, { 'string', 'function' }, true) vim.validate(prefix .. '.cwd', provider.cwd, { 'string', 'function' }, true)
vim.validate(prefix .. '.output', provider.output, { 'string', 'function' }, true) vim.validate(prefix .. '.output', provider.output, { 'string', 'function' }, true)
vim.validate(prefix .. '.error_parser', provider.error_parser, 'function', true) vim.validate(prefix .. '.error_parser', provider.error_parser, 'function', true)

View file

@ -115,6 +115,24 @@ local function parse_asciidoctor(output)
return diagnostics return diagnostics
end end
---@param output string
---@return preview.Diagnostic[]
local function parse_mermaid(output)
local lnum = output:match('Parse error on line (%d+)')
if not lnum then
return {}
end
local msg = output:match('(Expecting .+)') or 'parse error'
return {
{
lnum = tonumber(lnum) - 1,
col = 0,
message = msg,
severity = vim.diagnostic.severity.ERROR,
},
}
end
---@type preview.ProviderConfig ---@type preview.ProviderConfig
M.typst = { M.typst = {
ft = 'typst', ft = 'typst',
@ -313,19 +331,7 @@ M.mermaid = {
return (ctx.file:gsub('%.mmd$', '.svg')) return (ctx.file:gsub('%.mmd$', '.svg'))
end, end,
error_parser = function(output) error_parser = function(output)
local diagnostics = {} return parse_mermaid(output)
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, end,
clean = function(ctx) clean = function(ctx)
return { 'rm', '-f', (ctx.file:gsub('%.mmd$', '.svg')) } return { 'rm', '-f', (ctx.file:gsub('%.mmd$', '.svg')) }