diff --git a/.luarc.json b/.luarc.json
index d44eb7c..23646d3 100644
--- a/.luarc.json
+++ b/.luarc.json
@@ -4,6 +4,5 @@
"diagnostics.globals": ["vim", "jit"],
"workspace.library": ["$VIMRUNTIME/lua", "${3rd}/luv/library"],
"workspace.checkThirdParty": false,
- "workspace.ignoreDir": [".direnv"],
"completion.callSnippet": "Replace"
}
diff --git a/.styluaignore b/.styluaignore
deleted file mode 100644
index 9b42106..0000000
--- a/.styluaignore
+++ /dev/null
@@ -1 +0,0 @@
-.direnv/
diff --git a/README.md b/README.md
index 80a2650..1d4446b 100644
--- a/README.md
+++ b/README.md
@@ -1,19 +1,17 @@
# preview.nvim
-**Universal document previewer for Neovim**
+**Async document compilation 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
+- Built-in presets for Typst, LaTeX, Markdown, and GitHub-flavored Markdown
+- Compiler errors as native `vim.diagnostic`
+- User events for extensibility (`PreviewCompileStarted`,
+ `PreviewCompileSuccess`, `PreviewCompileFailed`)
## Requirements
@@ -21,18 +19,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,35 +37,35 @@ luarocks install preview.nvim
**Q: How do I define a custom provider?**
```lua
-vim.g.preview = {
- rst = {
- cmd = { 'rst2html' },
+require('preview').setup({
+ typst = {
+ cmd = { 'typst', 'compile' },
args = function(ctx)
- return { ctx.file, ctx.output }
+ return { ctx.file }
end,
output = function(ctx)
- return ctx.file:gsub('%.rst$', '.html')
+ return ctx.file:gsub('%.typ$', '.pdf')
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?**
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 in
-toggle/watch mode. 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
-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..c8bc708
--- /dev/null
+++ b/doc/preview.nvim.txt
@@ -0,0 +1,244 @@
+*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 zero provider defaults. Users must explicitly configure
+their compiler commands. preview.nvim is purely an orchestration framework.
+
+==============================================================================
+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. `true` uses
+ |vim.ui.open()|. A string[] is run as
+ a command with the output path appended.
+
+ *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 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.markdown` pandoc → HTML (standalone, embedded)
+ `presets.github` pandoc → HTML (GitHub-styled, `-f gfm` input)
+
+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:~
+
+ `compile` Compile the current buffer (default if omitted).
+ `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.
+ `open` Open the last compiled output without recompiling.
+ `status` Echo compilation status (idle, compiling, watching).
+
+==============================================================================
+API *preview.nvim-api*
+
+preview.compile({bufnr?}) *preview.compile()*
+ Compile the document in the given buffer (default: current).
+
+preview.stop({bufnr?}) *preview.stop()*
+ Kill the active compilation process for the buffer.
+
+preview.clean({bufnr?}) *preview.clean()*
+ Run the provider's clean command for the buffer.
+
+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.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..7413113 100644
--- a/flake.nix
+++ b/flake.nix
@@ -13,15 +13,12 @@
...
}:
let
- forEachSystem =
- f: nixpkgs.lib.genAttrs (import systems) (system: f nixpkgs.legacyPackages.${system});
+ forEachSystem = f: nixpkgs.lib.genAttrs (import systems) (system: f nixpkgs.legacyPackages.${system});
in
{
- 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 +30,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/commands.lua b/lua/preview/commands.lua
index 97a13f7..c91f4de 100644
--- a/lua/preview/commands.lua
+++ b/lua/preview/commands.lua
@@ -1,14 +1,17 @@
local M = {}
local handlers = {
- compile = function()
- require('preview').compile()
+ build = function()
+ require('preview').build()
+ end,
+ stop = function()
+ require('preview').stop()
end,
clean = function()
require('preview').clean()
end,
- toggle = function()
- require('preview').toggle()
+ watch = function()
+ require('preview').watch()
end,
open = function()
require('preview').open()
@@ -30,7 +33,7 @@ local handlers = {
---@param args string
local function dispatch(args)
- local subcmd = args ~= '' and args or 'toggle'
+ local subcmd = args ~= '' and args or 'build'
local handler = handlers[subcmd]
if handler then
handler()
@@ -55,13 +58,7 @@ function M.setup()
complete = function(lead)
return complete(lead)
end,
- desc = 'Toggle, compile, clean, open, or check status of document preview',
- })
-
- vim.api.nvim_create_autocmd('VimLeavePre', {
- callback = function()
- require('preview.compiler').stop_all()
- end,
+ desc = 'Build, stop, clean, watch, open, or check status of document preview',
})
end
diff --git a/lua/preview/compiler.lua b/lua/preview/compiler.lua
index a193ad2..36b5255 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[]
@@ -94,17 +55,7 @@ end
---@param name string
---@param provider preview.ProviderConfig
---@param ctx preview.Context
-function M.compile(bufnr, name, provider, ctx, opts)
- opts = opts or {}
-
- if vim.fn.executable(provider.cmd[1]) ~= 1 then
- vim.notify(
- '[preview.nvim]: "' .. provider.cmd[1] .. '" is not executable (run :checkhealth preview)',
- vim.log.levels.ERROR
- )
- return
- end
-
+function M.compile(bufnr, name, provider, ctx)
if vim.bo[bufnr].modified then
vim.cmd('silent! update')
end
@@ -130,10 +81,7 @@ function M.compile(bufnr, name, provider, ctx, opts)
last_output[bufnr] = output_file
end
- local reload_cmd
- if not opts.oneshot then
- reload_cmd = resolve_reload_cmd(provider, resolved_ctx)
- end
+ local reload_cmd = resolve_reload_cmd(provider, resolved_ctx)
if reload_cmd then
log.dbg(
@@ -143,47 +91,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 +130,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,64 +147,24 @@ 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 opened[bufnr] and 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 }
- vim.api.nvim_create_autocmd('BufUnload', {
+ vim.api.nvim_create_autocmd('BufWipeout', {
buffer = bufnr,
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 +209,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',
@@ -350,14 +220,14 @@ function M.compile(bufnr, name, provider, ctx, opts)
r.inject(output_file)
r.broadcast()
end
- if
- provider.open
- and not opts.oneshot
- and not opened[bufnr]
- and output_file ~= ''
- and vim.uv.fs_stat(output_file)
- then
- do_open(bufnr, output_file, provider.open)
+ if provider.open and not opened[bufnr] and 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
else
@@ -380,9 +250,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
@@ -401,14 +269,11 @@ function M.compile(bufnr, name, provider, ctx, opts)
active[bufnr] = { obj = obj, provider = name, output_file = output_file }
- vim.api.nvim_create_autocmd('BufUnload', {
+ vim.api.nvim_create_autocmd('BufWipeout', {
buffer = bufnr,
once = true,
callback = function()
M.stop(bufnr)
- if not provider.detach then
- close_viewer(bufnr)
- end
last_output[bufnr] = nil
end,
})
@@ -449,12 +314,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
@@ -505,15 +364,11 @@ function M.toggle(bufnr, name, provider, ctx_builder)
log.dbg('watching buffer %d with provider "%s"', bufnr, name)
vim.notify('[preview.nvim]: watching with "' .. name .. '"', vim.log.levels.INFO)
- vim.api.nvim_create_autocmd('BufUnload', {
+ vim.api.nvim_create_autocmd('BufWipeout', {
buffer = bufnr,
once = true,
callback = function()
M.unwatch(bufnr)
- stop_open_watcher(bufnr)
- if not provider.detach then
- close_viewer(bufnr)
- end
opened[bufnr] = nil
end,
})
@@ -543,23 +398,14 @@ end
---@param ctx preview.Context
function M.clean(bufnr, name, provider, ctx)
if not provider.clean then
- vim.notify(
- '[preview.nvim]: provider "' .. name .. '" has no clean command',
- vim.log.levels.WARN
- )
+ vim.notify('[preview.nvim] provider "' .. name .. '" has no clean command', vim.log.levels.WARN)
return
end
- local output_file = ''
- if provider.output then
- output_file = eval_string(provider.output, ctx)
- end
- local resolved_ctx = vim.tbl_extend('force', ctx, { output = output_file })
-
- local cmd = eval_list(provider.clean, resolved_ctx)
- local cwd = resolved_ctx.root
+ local cmd = eval_list(provider.clean, ctx)
+ local cwd = ctx.root
if provider.cwd then
- cwd = eval_string(provider.cwd, resolved_ctx)
+ cwd = eval_string(provider.cwd, ctx)
end
log.dbg('cleaning buffer %d with provider "%s": %s', bufnr, name, table.concat(cmd, ' '))
@@ -570,10 +416,10 @@ function M.clean(bufnr, name, provider, ctx)
vim.schedule_wrap(function(result)
if result.code == 0 then
log.dbg('clean succeeded for buffer %d', bufnr)
- vim.notify('[preview.nvim]: clean complete', vim.log.levels.INFO)
+ vim.notify('[preview.nvim] clean complete', vim.log.levels.INFO)
else
log.dbg('clean failed for buffer %d (exit code %d)', bufnr, result.code)
- vim.notify('[preview.nvim]: clean failed: ' .. (result.stderr or ''), vim.log.levels.ERROR)
+ vim.notify('[preview.nvim] clean failed: ' .. (result.stderr or ''), vim.log.levels.ERROR)
end
end)
)
@@ -581,17 +427,13 @@ end
---@param bufnr integer
---@return boolean
-function M.open(bufnr, open_config)
+function M.open(bufnr)
local output = last_output[bufnr]
if not output then
log.dbg('no last output file for buffer %d', bufnr)
return false
end
- if not vim.uv.fs_stat(output) then
- log.dbg('output file no longer exists for buffer %d: %s', bufnr, output)
- return false
- end
- do_open(bufnr, output, open_config)
+ vim.ui.open(output)
return true
end
@@ -616,8 +458,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..bbf70f1 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
@@ -40,10 +39,10 @@
---@class preview
---@field setup fun(opts?: table)
----@field compile fun(bufnr?: integer)
+---@field build fun(bufnr?: integer)
---@field stop fun(bufnr?: integer)
---@field clean fun(bufnr?: integer)
----@field toggle fun(bufnr?: integer)
+---@field watch fun(bufnr?: integer)
---@field open fun(bufnr?: integer)
---@field status fun(bufnr?: integer): preview.Status
---@field statusline fun(bufnr?: integer): 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, {
@@ -152,20 +144,16 @@ function M.build_context(bufnr)
end
---@param bufnr? integer
-function M.compile(bufnr)
+function M.build(bufnr)
bufnr = bufnr or vim.api.nvim_get_current_buf()
- if vim.api.nvim_buf_get_name(bufnr) == '' then
- vim.notify('[preview.nvim]: buffer has no file name', vim.log.levels.WARN)
- return
- end
local name = M.resolve_provider(bufnr)
if not name then
- vim.notify('[preview.nvim]: no provider configured for this filetype', vim.log.levels.WARN)
+ vim.notify('[preview.nvim] no provider configured for this filetype', vim.log.levels.WARN)
return
end
local ctx = M.build_context(bufnr)
local provider = config.providers[name]
- compiler.compile(bufnr, name, provider, ctx, { oneshot = true })
+ compiler.compile(bufnr, name, provider, ctx)
end
---@param bufnr? integer
@@ -177,13 +165,9 @@ end
---@param bufnr? integer
function M.clean(bufnr)
bufnr = bufnr or vim.api.nvim_get_current_buf()
- if vim.api.nvim_buf_get_name(bufnr) == '' then
- vim.notify('[preview.nvim]: buffer has no file name', vim.log.levels.WARN)
- return
- end
local name = M.resolve_provider(bufnr)
if not name then
- vim.notify('[preview.nvim]: no provider configured for this filetype', vim.log.levels.WARN)
+ vim.notify('[preview.nvim] no provider configured for this filetype', vim.log.levels.WARN)
return
end
local ctx = M.build_context(bufnr)
@@ -192,15 +176,11 @@ function M.clean(bufnr)
end
---@param bufnr? integer
-function M.toggle(bufnr)
+function M.watch(bufnr)
bufnr = bufnr or vim.api.nvim_get_current_buf()
- if vim.api.nvim_buf_get_name(bufnr) == '' then
- vim.notify('[preview.nvim]: buffer has no file name', vim.log.levels.WARN)
- return
- end
local name = M.resolve_provider(bufnr)
if not name then
- vim.notify('[preview.nvim]: no provider configured for this filetype', vim.log.levels.WARN)
+ vim.notify('[preview.nvim] no provider configured for this filetype', vim.log.levels.WARN)
return
end
local provider = config.providers[name]
@@ -210,14 +190,8 @@ end
---@param bufnr? integer
function M.open(bufnr)
bufnr = bufnr or vim.api.nvim_get_current_buf()
- if vim.api.nvim_buf_get_name(bufnr) == '' then
- vim.notify('[preview.nvim]: buffer has no file name', vim.log.levels.WARN)
- return
- end
- local name = M.resolve_provider(bufnr)
- local open_config = name and config.providers[name] and config.providers[name].open
- if not compiler.open(bufnr, open_config) then
- vim.notify('[preview.nvim]: no output file available for this buffer', vim.log.levels.WARN)
+ if not compiler.open(bufnr) then
+ vim.notify('[preview.nvim] no output file available for this buffer', vim.log.levels.WARN)
end
end
@@ -254,8 +228,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..fc20f89 100644
--- a/lua/preview/presets.lua
+++ b/lua/preview/presets.lua
@@ -93,28 +93,6 @@ local function parse_pandoc(output)
return diagnostics
end
----@param output string
----@return preview.Diagnostic[]
-local function parse_asciidoctor(output)
- local diagnostics = {}
- for line in output:gmatch('[^\r\n]+') do
- local severity, _, lnum, msg = line:match('^asciidoctor: (%u+): (.+): line (%d+): (.+)$')
- if lnum then
- local sev = vim.diagnostic.severity.ERROR
- if severity == 'WARNING' then
- sev = vim.diagnostic.severity.WARN
- end
- table.insert(diagnostics, {
- lnum = tonumber(lnum) - 1,
- col = 0,
- message = msg,
- severity = sev,
- })
- end
- end
- return diagnostics
-end
-
---@type preview.ProviderConfig
M.typst = {
ft = 'typst',
@@ -128,12 +106,9 @@ M.typst = {
error_parser = function(output)
return parse_typst(output)
end,
- clean = function(ctx)
- return { 'rm', '-f', (ctx.file:gsub('%.typ$', '.pdf')) }
- end,
open = true,
reload = function(ctx)
- return { 'typst', 'watch', '--diagnostic-format', 'short', ctx.file }
+ return { 'typst', 'watch', ctx.file }
end,
}
@@ -162,45 +137,6 @@ M.latex = {
open = true,
}
----@type preview.ProviderConfig
-M.pdflatex = {
- ft = 'tex',
- cmd = { 'pdflatex' },
- args = function(ctx)
- return { '-interaction=nonstopmode', '-file-line-error', '-synctex=1', ctx.file }
- end,
- output = function(ctx)
- return (ctx.file:gsub('%.tex$', '.pdf'))
- end,
- error_parser = function(output)
- return parse_latexmk(output)
- end,
- clean = function(ctx)
- local base = ctx.file:gsub('%.tex$', '')
- return { 'rm', '-f', base .. '.pdf', base .. '.aux', base .. '.log', base .. '.synctex.gz' }
- end,
- open = true,
-}
-
----@type preview.ProviderConfig
-M.tectonic = {
- ft = 'tex',
- cmd = { 'tectonic' },
- args = function(ctx)
- return { ctx.file }
- end,
- output = function(ctx)
- return (ctx.file:gsub('%.tex$', '.pdf'))
- end,
- error_parser = function(output)
- return parse_latexmk(output)
- end,
- clean = function(ctx)
- return { 'rm', '-f', (ctx.file:gsub('%.tex$', '.pdf')) }
- end,
- open = true,
-}
-
---@type preview.ProviderConfig
M.markdown = {
ft = 'markdown',
@@ -251,104 +187,4 @@ M.github = {
reload = true,
}
----@type preview.ProviderConfig
-M.asciidoctor = {
- ft = 'asciidoc',
- cmd = { 'asciidoctor' },
- args = function(ctx)
- return { '--failure-level', 'ERROR', ctx.file, '-o', ctx.output }
- end,
- output = function(ctx)
- return (ctx.file:gsub('%.adoc$', '.html'))
- end,
- error_parser = function(output)
- return parse_asciidoctor(output)
- end,
- clean = function(ctx)
- return { 'rm', '-f', (ctx.file:gsub('%.adoc$', '.html')) }
- end,
- open = true,
- 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',
- cmd = { 'quarto' },
- args = function(ctx)
- return { 'render', ctx.file, '--to', 'html', '--embed-resources' }
- end,
- output = function(ctx)
- return (ctx.file:gsub('%.qmd$', '.html'))
- end,
- clean = function(ctx)
- local base = ctx.file:gsub('%.qmd$', '')
- return { 'rm', '-rf', base .. '.html', base .. '_files' }
- end,
- open = true,
- reload = true,
-}
-
return M
diff --git a/scripts/ci.sh b/scripts/ci.sh
deleted file mode 100755
index 37b1267..0000000
--- a/scripts/ci.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/sh
-set -eu
-
-nix develop --command stylua --check .
-git ls-files '*.lua' | xargs nix develop --command selene --display-style quiet
-nix develop --command prettier --check .
-nix fmt
-git diff --exit-code -- '*.nix'
-nix develop --command lua-language-server --check lua/ --configpath "$(pwd)/.luarc.json" --checklevel=Warning
-nix develop --command busted
diff --git a/spec/commands_spec.lua b/spec/commands_spec.lua
index 4e12e5d..32da224 100644
--- a/spec/commands_spec.lua
+++ b/spec/commands_spec.lua
@@ -11,26 +11,20 @@ describe('commands', function()
local cmds = vim.api.nvim_get_commands({})
assert.is_not_nil(cmds.Preview)
end)
-
- it('registers VimLeavePre autocmd', function()
- require('preview.commands').setup()
- local aus = vim.api.nvim_get_autocmds({ event = 'VimLeavePre' })
- local found = false
- for _, au in ipairs(aus) do
- if au.callback then
- found = true
- break
- end
- end
- assert.is_true(found)
- end)
end)
describe('dispatch', function()
- it('does not error on :Preview compile with no provider', function()
+ it('does not error on :Preview with no provider', function()
require('preview.commands').setup()
assert.has_no.errors(function()
- vim.cmd('Preview compile')
+ vim.cmd('Preview build')
+ end)
+ end)
+
+ it('does not error on :Preview stop', function()
+ require('preview.commands').setup()
+ assert.has_no.errors(function()
+ vim.cmd('Preview stop')
end)
end)
@@ -48,10 +42,10 @@ describe('commands', function()
end)
end)
- it('does not error on :Preview toggle with no provider', function()
+ it('does not error on :Preview watch with no provider', function()
require('preview.commands').setup()
assert.has_no.errors(function()
- vim.cmd('Preview toggle')
+ vim.cmd('Preview watch')
end)
end)
end)
diff --git a/spec/compiler_spec.lua b/spec/compiler_spec.lua
index cd1dd9f..2189347 100644
--- a/spec/compiler_spec.lua
+++ b/spec/compiler_spec.lua
@@ -99,35 +99,6 @@ describe('compiler', function()
helpers.delete_buffer(bufnr)
end)
- it('notifies and returns when binary is not executable', function()
- local bufnr = helpers.create_buffer({ 'hello' }, 'text')
- vim.api.nvim_buf_set_name(bufnr, '/tmp/preview_test_nobin.txt')
- vim.bo[bufnr].modified = false
-
- local notified = false
- local orig = vim.notify
- vim.notify = function(msg)
- if msg:find('not executable') then
- notified = true
- end
- end
-
- local provider = { cmd = { 'totally_nonexistent_binary_xyz_preview' } }
- local ctx = {
- bufnr = bufnr,
- file = '/tmp/preview_test_nobin.txt',
- root = '/tmp',
- ft = 'text',
- }
-
- compiler.compile(bufnr, 'nobin', provider, ctx)
- vim.notify = orig
-
- assert.is_true(notified)
- assert.is_nil(compiler._test.active[bufnr])
- helpers.delete_buffer(bufnr)
- end)
-
it('fires PreviewCompileFailed on non-zero exit', function()
local bufnr = helpers.create_buffer({ 'hello' }, 'text')
vim.api.nvim_buf_set_name(bufnr, '/tmp/preview_test_fail.txt')
diff --git a/spec/init_spec.lua b/spec/init_spec.lua
index f68438c..5c49276 100644
--- a/spec/init_spec.lua
+++ b/spec/init_spec.lua
@@ -108,62 +108,4 @@ describe('preview', function()
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)
diff --git a/spec/presets_spec.lua b/spec/presets_spec.lua
index 2160dfa..8085a3c 100644
--- a/spec/presets_spec.lua
+++ b/spec/presets_spec.lua
@@ -33,10 +33,6 @@ describe('presets', function()
assert.are.equal('/tmp/document.pdf', output)
end)
- it('returns clean command', function()
- assert.are.same({ 'rm', '-f', '/tmp/document.pdf' }, presets.typst.clean(ctx))
- end)
-
it('has open enabled', function()
assert.is_true(presets.typst.open)
end)
@@ -50,9 +46,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()
@@ -163,120 +157,6 @@ describe('presets', function()
end)
end)
- describe('pdflatex', function()
- local tex_ctx = {
- bufnr = 1,
- file = '/tmp/document.tex',
- root = '/tmp',
- ft = 'tex',
- }
-
- it('has ft', function()
- assert.are.equal('tex', presets.pdflatex.ft)
- end)
-
- it('has cmd', function()
- assert.are.same({ 'pdflatex' }, presets.pdflatex.cmd)
- end)
-
- it('returns args with flags and file path', function()
- local args = presets.pdflatex.args(tex_ctx)
- assert.are.same(
- { '-interaction=nonstopmode', '-file-line-error', '-synctex=1', '/tmp/document.tex' },
- args
- )
- end)
-
- it('returns pdf output path', function()
- assert.are.equal('/tmp/document.pdf', presets.pdflatex.output(tex_ctx))
- end)
-
- it('has open enabled', function()
- assert.is_true(presets.pdflatex.open)
- end)
-
- it('returns clean command removing pdf and aux files', function()
- local clean = presets.pdflatex.clean(tex_ctx)
- assert.are.same({
- 'rm',
- '-f',
- '/tmp/document.pdf',
- '/tmp/document.aux',
- '/tmp/document.log',
- '/tmp/document.synctex.gz',
- }, clean)
- end)
-
- it('has no reload', function()
- assert.is_nil(presets.pdflatex.reload)
- end)
-
- it('parses file-line-error format', function()
- local output = './document.tex:10: Undefined control sequence.'
- local diagnostics = presets.pdflatex.error_parser(output, tex_ctx)
- assert.are.equal(1, #diagnostics)
- assert.are.equal(9, diagnostics[1].lnum)
- assert.are.equal(0, diagnostics[1].col)
- assert.are.equal('Undefined control sequence.', diagnostics[1].message)
- assert.are.equal(vim.diagnostic.severity.ERROR, diagnostics[1].severity)
- end)
-
- it('returns empty table for clean output', function()
- assert.are.same({}, presets.pdflatex.error_parser('', tex_ctx))
- end)
- end)
-
- describe('tectonic', function()
- local tex_ctx = {
- bufnr = 1,
- file = '/tmp/document.tex',
- root = '/tmp',
- ft = 'tex',
- }
-
- it('has ft', function()
- assert.are.equal('tex', presets.tectonic.ft)
- end)
-
- it('has cmd', function()
- assert.are.same({ 'tectonic' }, presets.tectonic.cmd)
- end)
-
- it('returns args with file path', function()
- assert.are.same({ '/tmp/document.tex' }, presets.tectonic.args(tex_ctx))
- end)
-
- it('returns pdf output path', function()
- assert.are.equal('/tmp/document.pdf', presets.tectonic.output(tex_ctx))
- end)
-
- it('has open enabled', function()
- assert.is_true(presets.tectonic.open)
- end)
-
- it('returns clean command removing pdf', function()
- assert.are.same({ 'rm', '-f', '/tmp/document.pdf' }, presets.tectonic.clean(tex_ctx))
- end)
-
- it('has no reload', function()
- assert.is_nil(presets.tectonic.reload)
- end)
-
- it('parses file-line-error format', function()
- local output = './document.tex:5: Missing $ inserted.'
- local diagnostics = presets.tectonic.error_parser(output, tex_ctx)
- assert.are.equal(1, #diagnostics)
- assert.are.equal(4, diagnostics[1].lnum)
- assert.are.equal(0, diagnostics[1].col)
- assert.are.equal('Missing $ inserted.', diagnostics[1].message)
- assert.are.equal(vim.diagnostic.severity.ERROR, diagnostics[1].severity)
- end)
-
- it('returns empty table for clean output', function()
- assert.are.same({}, presets.tectonic.error_parser('', tex_ctx))
- end)
- end)
-
describe('markdown', function()
local md_ctx = {
bufnr = 1,
@@ -461,116 +341,4 @@ describe('presets', function()
assert.are.same({}, diagnostics)
end)
end)
-
- describe('asciidoctor', function()
- local adoc_ctx = {
- bufnr = 1,
- file = '/tmp/document.adoc',
- root = '/tmp',
- ft = 'asciidoc',
- output = '/tmp/document.html',
- }
-
- it('has ft', function()
- assert.are.equal('asciidoc', presets.asciidoctor.ft)
- end)
-
- it('has cmd', function()
- assert.are.same({ 'asciidoctor' }, presets.asciidoctor.cmd)
- end)
-
- it('returns args with file and output', function()
- assert.are.same(
- { '--failure-level', 'ERROR', '/tmp/document.adoc', '-o', '/tmp/document.html' },
- presets.asciidoctor.args(adoc_ctx)
- )
- end)
-
- it('returns html output path', function()
- assert.are.equal('/tmp/document.html', presets.asciidoctor.output(adoc_ctx))
- end)
-
- it('returns clean command', function()
- assert.are.same({ 'rm', '-f', '/tmp/document.html' }, presets.asciidoctor.clean(adoc_ctx))
- end)
-
- it('has open enabled', function()
- assert.is_true(presets.asciidoctor.open)
- end)
-
- it('has reload enabled for SSE', function()
- assert.is_true(presets.asciidoctor.reload)
- end)
-
- it('parses error messages', function()
- local output =
- 'asciidoctor: ERROR: document.adoc: line 8: invalid part, must have at least one section'
- local diagnostics = presets.asciidoctor.error_parser(output, adoc_ctx)
- assert.are.equal(1, #diagnostics)
- assert.are.equal(7, diagnostics[1].lnum)
- assert.are.equal(0, diagnostics[1].col)
- assert.are.equal('invalid part, must have at least one section', diagnostics[1].message)
- assert.are.equal(vim.diagnostic.severity.ERROR, diagnostics[1].severity)
- end)
-
- it('parses warning messages', function()
- local output = 'asciidoctor: WARNING: document.adoc: line 52: section title out of sequence'
- local diagnostics = presets.asciidoctor.error_parser(output, adoc_ctx)
- assert.are.equal(1, #diagnostics)
- assert.are.equal(51, diagnostics[1].lnum)
- assert.are.equal(vim.diagnostic.severity.WARN, diagnostics[1].severity)
- end)
-
- it('returns empty table for clean output', function()
- assert.are.same({}, presets.asciidoctor.error_parser('', adoc_ctx))
- end)
- end)
-
- describe('quarto', function()
- local qmd_ctx = {
- bufnr = 1,
- file = '/tmp/document.qmd',
- root = '/tmp',
- ft = 'quarto',
- output = '/tmp/document.html',
- }
-
- it('has ft', function()
- assert.are.equal('quarto', presets.quarto.ft)
- end)
-
- it('has cmd', function()
- assert.are.same({ 'quarto' }, presets.quarto.cmd)
- end)
-
- it('returns args with render subcommand and html format', function()
- assert.are.same(
- { 'render', '/tmp/document.qmd', '--to', 'html', '--embed-resources' },
- presets.quarto.args(qmd_ctx)
- )
- end)
-
- it('returns html output path', function()
- assert.are.equal('/tmp/document.html', presets.quarto.output(qmd_ctx))
- end)
-
- it('returns clean command removing html and _files directory', function()
- assert.are.same(
- { 'rm', '-rf', '/tmp/document.html', '/tmp/document_files' },
- presets.quarto.clean(qmd_ctx)
- )
- end)
-
- it('has open enabled', function()
- assert.is_true(presets.quarto.open)
- end)
-
- it('has reload enabled for SSE', function()
- assert.is_true(presets.quarto.reload)
- end)
-
- it('has no error_parser', function()
- assert.is_nil(presets.quarto.error_parser)
- end)
- end)
end)
diff --git a/spec/reload_spec.lua b/spec/reload_spec.lua
index 68b2851..12b7aac 100644
--- a/spec/reload_spec.lua
+++ b/spec/reload_spec.lua
@@ -13,13 +13,13 @@ describe('reload', function()
describe('inject', function()
it('injects script before
hello