Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions lazy.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
return {
'chomosuke/typst-preview.nvim',
lazy = true,
cmd = {
"TypstPreview",
"TypstPreviewStop",
"TypstPreviewToggle",
"TypstPreviewFollowCursor",
"TypstPreviewNoFollowCursor",
"TypstPreviewFollowCursorToggle",
"TypstPreviewSyncCursor",
}
}

125 changes: 54 additions & 71 deletions lua/typst-preview/commands.lua
Original file line number Diff line number Diff line change
@@ -1,80 +1,60 @@
local config = require 'typst-preview.config'
local events = require 'typst-preview.events'
local fetch = require 'typst-preview.fetch'
local manager = require 'typst-preview.manager'
local utils = require 'typst-preview.utils'
local config = require 'typst-preview.config'
local servers = require 'typst-preview.servers'

local M = {}

---Scroll all preview to cursor position.
function M.sync_with_cursor()
for _, ser in pairs(servers.get_all()) do
servers.sync_with_cursor(ser)
end
end

---Create user commands
function M.create_commands()
local function preview_off()
local path = utils.get_buf_path(0)

if path ~= '' and servers.remove(config.opts.get_main_file(path)) then
---@param path string?
local function preview_off(path)
if path and manager.remove(
{path = path},
'user request'
) then
utils.print 'Preview stopped'
else
utils.print 'Preview not running'
end
end

local function get_path()
local path = utils.get_buf_path(0)
if path == '' then
utils.notify('Can not preview an unsaved buffer.', vim.log.levels.ERROR)
return nil
else
return config.opts.get_main_file(path)
end
end

---@param path string
---@param mode mode?
local function preview_on(mode)
-- check if binaries are available and tell them to fetch first
for _, bin in pairs(fetch.bins_to_fetch()) do
if
not config.opts.dependencies_bin[bin.name] and not fetch.up_to_date(bin)
then
utils.notify(
bin.name
.. ' not found or out of date\nPlease run :TypstPreviewUpdate first!',
vim.log.levels.ERROR
)
return
end
end

local path = get_path()
if path == nil then
return
end

local function preview_on(path, mode)
assert(path)
mode = mode or 'document'

local ser = servers.get(path)
if ser == nil or ser[mode] == nil then
servers.init(path, mode, function(s)
events.listen(s)
end)
else
local s = ser[mode]
print 'Opening another frontend'
utils.visit(s.link)
end
events.ensure_registered()

manager.get_or_init(
path,
mode,
function(task, is_new)
if is_new then
print 'Preview started'
else
print 'Opening another frontend'
end
utils.visit(task.link)
end
)
end

vim.api.nvim_create_user_command('TypstPreviewUpdate', function()
fetch.fetch(false)
vim.api.nvim_create_user_command('TypstPreviewUpdate', function(opts)
vim.notify(
'TypstPreviewUpdate is deprecated',
vim.log.levels.ERROR
)
end, {})

vim.api.nvim_create_user_command('TypstPreview', function(opts)
local path = utils.get_main_file()
if path == nil then
utils.notify('Can not preview an unsaved buffer.', vim.log.levels.ERROR)
return
end

local mode
if #opts.fargs == 1 then
mode = opts.fargs[1]
Expand All @@ -86,51 +66,54 @@ function M.create_commands()
.. ' Should be one of "document" and "slide"',
vim.log.levels.ERROR
)
return
end
else
assert(#opts.fargs == 0)
local path = get_path()
if path == nil then
return
end
local sers = servers.get(path)
if sers ~= nil then
mode = servers.get_last_mode(path)
end
mode = manager.get_last_mode(path)
end

preview_on(mode)
preview_on(path, mode)
end, {
nargs = '?',
complete = function(_, _, _)
return { 'document', 'slide' }
end,
})
vim.api.nvim_create_user_command('TypstPreviewStop', preview_off, {})

vim.api.nvim_create_user_command('TypstPreviewStop', function()
local path = utils.get_main_file()
preview_off(path)
end, {})

vim.api.nvim_create_user_command('TypstPreviewToggle', function()
local path = get_path()
local path = utils.get_main_file()
if path == nil then
utils.notify('Can not preview an unsaved buffer.', vim.log.levels.ERROR)
return
end

if servers.get(path) ~= nil then
preview_off()
if next(manager.get{path=path}) ~= nil then
preview_off(path)
else
preview_on(servers.get_last_mode(path))
preview_on(path, manager.get_last_mode(path))
end
end, {})

vim.api.nvim_create_user_command('TypstPreviewFollowCursor', function()
config.set_follow_cursor(true)
end, {})

vim.api.nvim_create_user_command('TypstPreviewNoFollowCursor', function()
config.set_follow_cursor(false)
end, {})

vim.api.nvim_create_user_command('TypstPreviewFollowCursorToggle', function()
config.set_follow_cursor(not config.get_follow_cursor())
end, {})

vim.api.nvim_create_user_command('TypstPreviewSyncCursor', function()
M.sync_with_cursor()
manager.scroll_preview()
end, {})
end

Expand Down
59 changes: 54 additions & 5 deletions lua/typst-preview/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@ local M = {
port = 0, -- tinymist will use a random port if this is 0
invert_colors = 'never',
follow_cursor = true,
dependencies_bin = {
['tinymist'] = nil,
['websocat'] = nil,
},
extra_args = nil,
get_root = function(path_of_main_file)
local root = os.getenv 'TYPST_ROOT'
if root then
Expand All @@ -23,7 +18,61 @@ local M = {
},
}

local deprecated_opts = {
'extra_args',
'dependencies_bin'
}
local all_opts = {
'debug',
'open_cmd',
'port',
'invert_colors',
'follow_cursor',
'get_root',
'get_main_file'
}

local function contains(table, value)
for _, v in pairs(table) do
if value == v then
return true
end
end
return false
end

function M.config(opts)
local deprecated = {}
local invalid = {}
for key, _ in pairs(opts) do
if not contains(all_opts, key) then
if contains(deprecated_opts, key) then
table.insert(deprecated, key)
else
table.insert(invalid, key)
end
opts[key] = nil
end
end

if next(invalid) then
vim.notify(
'typst-preview: invalid config keys: '
.. table.concat(invalid, ', ')
.. '\n',
vim.log.levels.ERROR
)
end
if next(deprecated) then
vim.notify(
'typst-preview: deprecated config keys: '
.. table.concat(deprecated, ', ')
.. '\n'
.. 'Note that the plugin has changed substantially and does not download tinymist anymore, but connects to existing language servers. Please update your configuration, cf. the documentation',
vim.log.levels.ERROR
)
end

M.opts = vim.tbl_deep_extend('force', M.opts, opts or {})
end

Expand Down
90 changes: 90 additions & 0 deletions lua/typst-preview/events.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
local config = require 'typst-preview.config'
local manager = require 'typst-preview.manager'
local utils = require 'typst-preview.utils'

---Whether lsp handlers have been registered
local lsp_handlers_registerd = false

local M = {}

---@param method string
---@param handler fun(result)
local function register_lsp_handler(method, handler)
vim.lsp.handlers[method] = function(err, result, ctx)
utils.debug(
"Received event from server: ",
ctx.method,
", err = ",
err,
", result = ",
result
)

if err ~= nil then
return
end

handler(result)
end ---@type lsp.Handler
end

function M.ensure_registered()
if lsp_handlers_registerd then
return
end

local id = vim.api.nvim_create_augroup('typst-preview-autocmds', {})
vim.api.nvim_create_autocmd('LspDetach', {
group = id,
callback = function(ev)
manager.remove(
{ client = ev.data.client },
'server detached'
)
end
})

vim.api.nvim_create_autocmd('CursorMoved', {
pattern = '*.typ',
callback = function(ev)
utils.debug("received CursorMoved in file ", ev.file)
if config.get_follow_cursor() then
manager.scroll_preview()
end
end
})

register_lsp_handler('tinymist/preview/dispose', function(result)
local task_id = result['taskId']

manager.remove(
{ task_id = task_id },
'received dispose from server'
)
end)

-- Note that tinymist does not seem to send this event: Instead, it uses
-- 'window/showDocument', which is already handled appropriately by neovim.
-- -> there is a config option, customizedShowDocument to control which
-- notification is sent
-- cf. https://github.com/Myriad-Dreamin/tinymist/pull/1450
-- -> requires tinymist v0.13.10
-- This does imply that we send a panelScrollTo in response to showDocument,
-- but that doesn't seem to result in a loop, luckily
-- register_lsp_handler('tinymist/preview/scrollSource', function(result)
-- ---@type JumpInfo
-- local jump = assert(result)
--
-- on_editor_scroll_to(jump)
-- end)

-- Don't even register the listener: This notification is sent quite often,
-- and we don't use it right now.
-- register_lsp_handler('tinymist/documentOutline', function(result)
-- -- ignore
-- end)

lsp_handlers_registerd = true
end

return M
Loading