Compare commits
9 commits
build/nix-
...
docs/synct
| Author | SHA1 | Date | |
|---|---|---|---|
| 87f2ed4aa2 | |||
| 23bce48d27 | |||
| 5c4fb10596 | |||
| 1e3d0fa577 | |||
| f6199af390 | |||
| 8dd8b533cc | |||
|
|
1fbc307bad | ||
|
|
00caad18bf | ||
|
|
8c9847e321 |
4 changed files with 173 additions and 21 deletions
|
|
@ -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.
|
||||||
|
|
@ -81,3 +81,8 @@ vim.g.preview = {
|
||||||
typst = { open = { 'sioyek', '--new-instance' } },
|
typst = { open = { 'sioyek', '--new-instance' } },
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Q: How do I set up SyncTeX (forward/inverse search)?**
|
||||||
|
|
||||||
|
See `:help preview-synctex` for full recipes covering Zathura, Sioyek, and
|
||||||
|
Okular.
|
||||||
|
|
|
||||||
112
doc/preview.txt
112
doc/preview.txt
|
|
@ -26,6 +26,7 @@ CONTENTS *preview-contents*
|
||||||
7. Lua API ................................................... |preview-api|
|
7. Lua API ................................................... |preview-api|
|
||||||
8. Events ............................................... |preview-events|
|
8. Events ............................................... |preview-events|
|
||||||
9. Health ............................................... |preview-health|
|
9. Health ............................................... |preview-health|
|
||||||
|
10. SyncTeX ............................................. |preview-synctex|
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
REQUIREMENTS *preview-requirements*
|
REQUIREMENTS *preview-requirements*
|
||||||
|
|
@ -272,5 +273,116 @@ Checks: ~
|
||||||
- Each configured provider's binary is executable
|
- Each configured provider's binary is executable
|
||||||
- Each configured provider's opener binary (if any) is executable
|
- Each configured provider's opener binary (if any) is executable
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
SYNCTEX *preview-synctex*
|
||||||
|
|
||||||
|
SyncTeX enables bidirectional navigation between LaTeX source and the
|
||||||
|
compiled PDF. The `latex` preset compiles with `-synctex=1` by default.
|
||||||
|
|
||||||
|
Forward search (editor -> viewer) requires caching the output path.
|
||||||
|
Inverse search (viewer -> editor) requires a fixed Neovim server socket.
|
||||||
|
|
||||||
|
The following configs leverage the below basic setup: ~
|
||||||
|
|
||||||
|
>lua
|
||||||
|
vim.fn.serverstart('/tmp/nvim-preview.sock')
|
||||||
|
|
||||||
|
local synctex_pdf = {}
|
||||||
|
vim.api.nvim_create_autocmd('User', {
|
||||||
|
pattern = 'PreviewCompileSuccess',
|
||||||
|
callback = function(args)
|
||||||
|
synctex_pdf[args.data.bufnr] = args.data.output
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
<
|
||||||
|
|
||||||
|
The recipes below bind `<leader>s` for forward search. To scroll the PDF
|
||||||
|
automatically on cursor movement, call the forward search function from a
|
||||||
|
|CursorMoved| or |CursorHold| autocmd instead.
|
||||||
|
|
||||||
|
Viewer-specific recipes: ~
|
||||||
|
|
||||||
|
*preview-synctex-zathura*
|
||||||
|
Zathura ~
|
||||||
|
|
||||||
|
Inverse search: Ctrl+click.
|
||||||
|
|
||||||
|
>lua
|
||||||
|
vim.keymap.set('n', '<leader>s', function()
|
||||||
|
local pdf = synctex_pdf[vim.api.nvim_get_current_buf()]
|
||||||
|
if pdf then
|
||||||
|
vim.fn.jobstart({
|
||||||
|
'zathura', '--synctex-forward',
|
||||||
|
vim.fn.line('.') .. ':0:' .. vim.fn.expand('%:p'), pdf,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
vim.g.preview = {
|
||||||
|
latex = {
|
||||||
|
open = {
|
||||||
|
'zathura',
|
||||||
|
'--synctex-editor-command',
|
||||||
|
'nvim --server /tmp/nvim-preview.sock'
|
||||||
|
.. [[ --remote-expr "execute('b +%{line} %{input}')"]],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
<
|
||||||
|
|
||||||
|
*preview-synctex-sioyek*
|
||||||
|
Sioyek ~
|
||||||
|
|
||||||
|
Inverse search: right-click with synctex mode active.
|
||||||
|
|
||||||
|
Add to `~/.config/sioyek/prefs_user.config`: >
|
||||||
|
inverse_search_command nvim --server /tmp/nvim-preview.sock --remote-expr "execute('b +%2 %1')"
|
||||||
|
<
|
||||||
|
|
||||||
|
>lua
|
||||||
|
vim.keymap.set('n', '<leader>s', function()
|
||||||
|
local pdf = synctex_pdf[vim.api.nvim_get_current_buf()]
|
||||||
|
if pdf then
|
||||||
|
vim.fn.jobstart({
|
||||||
|
'sioyek',
|
||||||
|
'--instance-name', 'preview',
|
||||||
|
'--forward-search-file', vim.fn.expand('%:p'),
|
||||||
|
'--forward-search-line', tostring(vim.fn.line('.')),
|
||||||
|
pdf,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
vim.g.preview = {
|
||||||
|
latex = {
|
||||||
|
open = { 'sioyek', '--instance-name', 'preview' },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
<
|
||||||
|
|
||||||
|
*preview-synctex-okular*
|
||||||
|
Okular ~
|
||||||
|
|
||||||
|
Inverse search (Shift+click): one-time GUI setup via
|
||||||
|
Settings -> Configure Okular -> Editor -> Custom Text Editor: >
|
||||||
|
nvim --server /tmp/nvim-preview.sock --remote-expr "execute('b +%l %f')"
|
||||||
|
<
|
||||||
|
|
||||||
|
>lua
|
||||||
|
vim.keymap.set('n', '<leader>s', function()
|
||||||
|
local pdf = synctex_pdf[vim.api.nvim_get_current_buf()]
|
||||||
|
if pdf then
|
||||||
|
vim.fn.jobstart({
|
||||||
|
'okular', '--unique',
|
||||||
|
('%s#src:%d:%s'):format(pdf, vim.fn.line('.'), vim.fn.expand('%:p')),
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
vim.g.preview = {
|
||||||
|
latex = { open = { 'okular', '--unique' } },
|
||||||
|
}
|
||||||
|
<
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
vim:tw=78:ts=8:ft=help:norl:
|
vim:tw=78:ts=8:ft=help:norl:
|
||||||
|
|
|
||||||
37
flake.nix
37
flake.nix
|
|
@ -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,38 @@
|
||||||
pkgs.stylua
|
pkgs.stylua
|
||||||
pkgs.selene
|
pkgs.selene
|
||||||
pkgs.lua-language-server
|
pkgs.lua-language-server
|
||||||
|
];
|
||||||
|
okular-wrapped = pkgs.symlinkJoin {
|
||||||
|
name = "okular";
|
||||||
|
paths = [ pkgs.kdePackages.okular ];
|
||||||
|
nativeBuildInputs = [ pkgs.makeWrapper ];
|
||||||
|
postBuild = ''
|
||||||
|
wrapProgram $out/bin/okular \
|
||||||
|
--prefix XDG_DATA_DIRS : "${pkgs.gsettings-desktop-schemas}/share/gsettings-schemas/${pkgs.gsettings-desktop-schemas.name}" \
|
||||||
|
--prefix XDG_DATA_DIRS : "${pkgs.gtk3}/share/gsettings-schemas/${pkgs.gtk3.name}"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
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.plantuml
|
||||||
pkgs.mermaid-cli
|
pkgs.mermaid-cli
|
||||||
|
pkgs.zathura
|
||||||
|
pkgs.sioyek
|
||||||
|
okular-wrapped
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
});
|
}
|
||||||
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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')) }
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue