-- Neovim preview for Luma files. -- -- Set LUMA_MARKDOWN_BIN to the path of the generated executable, or make -- luma_markdown available on PATH: -- export LUMA_MARKDOWN_BIN="$PWD/luma_markdown" local preview_buf = nil local preview_win = nil local active_job = nil local update_id = 0 local function ensure_preview_buffer() if preview_buf and vim.api.nvim_buf_is_valid(preview_buf) then return end preview_buf = vim.api.nvim_create_buf(false, true) vim.api.nvim_buf_set_option(preview_buf, "buftype", "nofile") vim.api.nvim_buf_set_option(preview_buf, "bufhidden", "hide") vim.api.nvim_buf_set_option(preview_buf, "swapfile", false) vim.api.nvim_buf_set_option(preview_buf, "filetype", "markdown") end local function show_preview(src_buf, output) ensure_preview_buffer() vim.api.nvim_buf_set_lines(preview_buf, 0, -1, false, output) if not preview_win or not vim.api.nvim_win_is_valid(preview_win) then local src_win = vim.fn.bufwinid(src_buf) if src_win == -1 then return end vim.api.nvim_set_current_win(src_win) vim.cmd "rightbelow vsplit" preview_win = vim.api.nvim_get_current_win() vim.api.nvim_win_set_buf(preview_win, preview_buf) vim.api.nvim_set_current_win(src_win) end end local function update_luma_preview() local src_buf = vim.api.nvim_get_current_buf() local lines = vim.api.nvim_buf_get_lines(src_buf, 0, -1, false) local changedtick = vim.api.nvim_buf_get_changedtick(src_buf) local cmd = vim.env.LUMA_MARKDOWN_BIN or "luma_markdown" update_id = update_id + 1 local this_update = update_id if active_job then vim.fn.jobstop(active_job) active_job = nil end local output = {} local errors = {} local job job = vim.fn.jobstart({ cmd }, { stdin = "pipe", stdout_buffered = true, stderr_buffered = true, on_stdout = function(_, data) if data then vim.list_extend(output, data) end end, on_stderr = function(_, data) if data then vim.list_extend(errors, data) end end, on_exit = function(_, exit_code) if active_job == job then active_job = nil end if this_update ~= update_id then return end if not vim.api.nvim_buf_is_valid(src_buf) then return end if vim.api.nvim_buf_get_changedtick(src_buf) ~= changedtick then return end if exit_code ~= 0 then local message = table.concat(errors, "\n") if message ~= "" then vim.notify("Luma preview failed: " .. message, vim.log.levels.ERROR) end return end -- io.println adds a final newline; systemlist used to remove it. if output[#output] == "" then table.remove(output) end show_preview(src_buf, output) end, }) if job <= 0 then vim.notify("Could not start Luma preview: " .. cmd, vim.log.levels.ERROR) return end active_job = job vim.fn.chansend(job, table.concat(lines, "\n")) vim.fn.chanclose(job, "stdin") end vim.api.nvim_create_autocmd("BufWritePost", { pattern = "*.luma", callback = update_luma_preview, }) vim.api.nvim_create_user_command("LumaPreview", update_luma_preview, {}) local timer = nil local function debounced_update() if timer then timer:stop() end timer = vim.defer_fn(update_luma_preview, 150) end vim.api.nvim_create_autocmd({ "TextChanged", "TextChangedI" }, { pattern = "*.luma", callback = debounced_update, })