-- local function parse_stderr(data) -- local message = table.concat(data, "\n") -- local level = "info" -- -- -- Check each line for error or warning -- for line in message:gmatch("[^\r\n]+") do -- local lower_line = line:lower() -- if lower_line:match("^%s*error:") then -- level = "error" -- break -- elseif lower_line:match("^%s*warning:") then -- level = "warn" -- -- Don't break here, in case an error appears later -- end -- end -- -- return message, level -- end local function parse_stderr(data, filter_level) local messages = {} local current_message = {} local current_level = "info" -- Split data into blocks for _, line in ipairs(data) do if line:match("^%s*$") then -- Empty line, end of block if #current_message > 0 then table.insert(messages, { message = table.concat(current_message, "\n"), level = current_level }) current_message = {} current_level = "info" end else -- Check for error/warning/info at the beginning of the block local lower_line = line:lower() if lower_line:match("^%s*error:") then current_level = "error" elseif lower_line:match("^%s*warning:") then current_level = "warn" elseif lower_line:match("^%s*info:") then current_level = "info" end table.insert(current_message, line) end end -- Add last block if exists if #current_message > 0 then table.insert(messages, { message = table.concat(current_message, "\n"), level = current_level }) end -- Filter messages based on filter_level local filtered_messages = {} for _, msg in ipairs(messages) do if not filter_level or msg.level == filter_level then table.insert(filtered_messages, msg) end end return filtered_messages end local function typst_build_and_open_sioyek() local bufname = vim.fn.expand("%:p") local pdf_path = vim.fn.fnamemodify(bufname, ":r") .. ".pdf" local cmd = string.format('typst compile "%s"', bufname) vim.fn.jobstart(cmd, { on_exit = function(_, exit_code) if exit_code == 0 then vim.notify("Typst build completed successfully", vim.log.levels.INFO, { title = "Typst Build", timeout = 3000, }) -- Now open or reload Sioyek local sioyek_running = vim.fn.system("pgrep sioyek"):match("%d+") if sioyek_running then vim.fn.system("sioyek --execute-command reload_no_flicker") vim.notify("Sioyek reloaded", vim.log.levels.INFO, { title = "Sioyek", timeout = 2000, }) else vim.fn.jobstart('sioyek "' .. pdf_path .. '"', { on_exit = function(_, sioyek_exit_code) if sioyek_exit_code == 0 then vim.notify("Sioyek opened", vim.log.levels.INFO, { title = "Sioyek", timeout = 2000, }) else vim.notify("Failed to open Sioyek", vim.log.levels.ERROR, { title = "Sioyek", timeout = 3000, }) end end, }) end else vim.notify("Typst build failed", vim.log.levels.ERROR, { title = "Typst Build", timeout = 5000, }) end end, stderr_buffered = true, on_stderr = function(_, data) if data and #data > 0 then local messages = parse_stderr(data) local error_count = 0 local warning_count = 0 local info_count = 0 for _, msg in ipairs(messages) do if msg.level == "error" then error_count = error_count + 1 elseif msg.level == "warn" then warning_count = warning_count + 1 elseif msg.level == "info" then info_count = info_count + 1 end end local notify = require("notify") local original_opts = vim.deepcopy(notify.options) notify.setup({ stages = "static", -- Add any other temporary options here }) for _, msg in ipairs(messages) do if msg.level ~= "error" then -- Route warnings and info to notify without displaying vim.notify(msg.message, vim.log.levels[msg.level:upper()], { title = "Typst Build " .. msg.level, timeout = 0, silent = true, log = true, }) end end notify.setup(original_opts) for _, msg in ipairs(messages) do if msg.level == "error" then vim.notify(msg.message, vim.log.levels.ERROR, { title = "Typst Build Error", timeout = 5000, }) end end if warning_count > 0 then vim.notify(string.format("%d warning(s)", warning_count), vim.log.levels.WARN, { title = "Typst Build Summary", timeout = 3000, }) end if info_count > 0 then vim.notify(string.format("%d info message(s)", info_count), vim.log.levels.INFO, { title = "Typst Build Summary", timeout = 2000, }) end end end, }) end vim.api.nvim_create_user_command("TypstBuild", typst_build_and_open_sioyek, {}) -- Create an autocommand group local typst_group = vim.api.nvim_create_augroup("TypstBuildGroup", { clear = true }) -- Create a command to toggle the autocommand local typst_auto_build_enabled = false -- Function to create the autocommand local function create_typst_autocmd() vim.api.nvim_create_autocmd("BufWritePost", { group = typst_group, pattern = "*.typ", callback = function() vim.cmd("TypstBuild") end, }) end -- Create a command to toggle the autocommand vim.api.nvim_create_user_command("TypstAutoBuild", function() typst_auto_build_enabled = not typst_auto_build_enabled if typst_auto_build_enabled then create_typst_autocmd() vim.notify("Typst auto-build enabled", vim.log.levels.INFO) else vim.api.nvim_clear_autocmds({ group = typst_group }) vim.notify("Typst auto-build disabled", vim.log.levels.INFO) end end, {}) return { -- { "kaarmu/typst.vim", ft = "typst", lazy = false }, -- { -- "chomosuke/typst-preview.nvim", -- lazy = false, -- or ft = 'typst' -- version = "0.3.*", -- config = function() -- require("typst-preview").setup({ -- -- Setting this true will enable printing debug information with print() -- debug = false, -- -- -- Custom format string to open the output link provided with %s -- -- Example: open_cmd = 'firefox %s -P typst-preview --class typst-preview' -- open_cmd = 'sioyek --execute-command reload_no_flicker %s', -- -- -- Setting this to 'always' will invert black and white in the preview -- -- Setting this to 'auto' will invert depending if the browser has enable -- -- dark mode -- invert_colors = "never", -- -- -- Whether the preview will follow the cursor in the source file -- follow_cursor = true, -- -- -- Provide the path to binaries for dependencies. -- -- Setting this will skip the download of the binary by the plugin. -- -- Warning: Be aware that your version might be older than the one -- -- required. -- dependencies_bin = { -- -- if you are using tinymist, just set ['typst-preview'] = "tinymist". -- ["typst-preview"] = nil, -- ["websocat"] = nil, -- }, -- -- -- This function will be called to determine the root of the typst project -- get_root = function(path_of_main_file) -- return vim.fn.fnamemodify(path_of_main_file, ":p:h") -- end, -- -- -- This function will be called to determine the main file of the typst -- -- project. -- get_main_file = function(path_of_buffer) -- return path_of_buffer -- end, -- }) -- end, -- build = function() -- require("typst-preview").update() -- end, -- }, }