From 020a2c115830736ff646491e23748610e25634ab Mon Sep 17 00:00:00 2001 From: KpihX Date: Sun, 20 Sep 2026 13:05:38 +0200 Subject: [PATCH 1/6] feat(latex): integrate high-performance Rust FFI for LaTeX Unicode rendering --- lua/markview/ffi/math.lua | 72 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 lua/markview/ffi/math.lua diff --git a/lua/markview/ffi/math.lua b/lua/markview/ffi/math.lua new file mode 100644 index 00000000..8cd07b3a --- /dev/null +++ b/lua/markview/ffi/math.lua @@ -0,0 +1,72 @@ +local ffi = require("ffi") + +ffi.cdef[[ + char* latex_to_unicode(const char* input); + char* latex_to_unicode_block(const char* input); + char* latex_transform_markdown(const char* input); + void latex_unicode_free(char* ptr); +]] + +local M = {} +local lib = nil + +local function get_lib_path() + local home = os.getenv("HOME") + local candidates = { + home .. "/Labs/KpihX-Labs/latex-to-unicode/target/release/liblatex_to_unicode_c_api.so", + home .. "/.local/lib/liblatex_to_unicode_c_api.so", + "/usr/local/lib/liblatex_to_unicode_c_api.so", + } + for _, path in ipairs(candidates) do + local f = io.open(path, "r") + if f then + f:close() + return path + end + end + return nil +end + +function M.init() + if lib then return true end + local path = get_lib_path() + if not path then + return false, "liblatex_to_unicode_c_api.so not found" + end + local ok, loaded = pcall(ffi.load, path) + if ok then + lib = loaded + return true + else + return false, loaded + end +end + +function M.to_unicode(latex_str) + if not M.init() then return latex_str end + local c_str = lib.latex_to_unicode(latex_str) + if c_str == nil then return latex_str end + local res = ffi.string(c_str) + lib.latex_unicode_free(c_str) + return res +end + +function M.to_unicode_block(latex_str) + if not M.init() then return latex_str end + local c_str = lib.latex_to_unicode_block(latex_str) + if c_str == nil then return latex_str end + local res = ffi.string(c_str) + lib.latex_unicode_free(c_str) + return res +end + +function M.transform_markdown(md_str) + if not M.init() then return md_str end + local c_str = lib.latex_transform_markdown(md_str) + if c_str == nil then return md_str end + local res = ffi.string(c_str) + lib.latex_unicode_free(c_str) + return res +end + +return M From 3d509b2c1f680f92390b3f7d640911a882c28b2c Mon Sep 17 00:00:00 2001 From: KpihX Date: Sun, 20 Sep 2026 13:25:27 +0200 Subject: [PATCH 2/6] feat(latex): render high-fidelity Unicode blocks using latex-to-unicode FFI --- lua/markview/renderers/latex.lua | 72 +++++++++----------------------- 1 file changed, 20 insertions(+), 52 deletions(-) diff --git a/lua/markview/renderers/latex.lua b/lua/markview/renderers/latex.lua index 2a6efe2e..9203dc8e 100644 --- a/lua/markview/renderers/latex.lua +++ b/lua/markview/renderers/latex.lua @@ -29,6 +29,25 @@ latex.block = function (buffer, item) return; end + local ok, math_ffi = pcall(require, "markview.ffi.math"); + if ok and math_ffi.init() then + local raw_latex = table.concat(item.text, "\n"); + local rendered = math_ffi.to_unicode_block(raw_latex); + local rendered_lines = vim.split(rendered, "\n", {}); + local virt_lines = {}; + for _, r_line in ipairs(rendered_lines) do + table.insert(virt_lines, { { " " .. r_line, "Special" } }); + end + vim.api.nvim_buf_set_extmark(buffer, latex.ns, range.row_start, range.col_start, { + undo_restore = false, invalidate = true, + end_row = range.row_end, + end_col = range.col_end, + virt_lines = virt_lines, + virt_lines_above = true, + hl_mode = "combine", + }); + end + vim.api.nvim_buf_set_extmark(buffer, latex.ns, range.row_start, range.col_start, { undo_restore = false, invalidate = true, end_col = range.col_start + #(item.marker or "$$"), @@ -81,60 +100,9 @@ latex.command = function (buffer, item) if not main_config then return; - elseif symbols.entries[item.command.name or ""] then - --[[ - FIX(#512): Allow `\{}` - - Some LaTeX previewers allow adding *empty groups* after symbols. - Add support for this symbol kind. - - NOTE: `@markview.latex.symbols` doesn't handle `{}` groups, so it - must be handled here instead. - - NOTE: See if performance is hampered due to this check - ]] - - local arg = item.args[1] or { range = {}, text = "" }; - - ---@type markview.config.latex.commands.opts - config = { - on_command = { - conceal = "", - virt_text_pos = "inline", - - virt_text = { - { symbols.entries[item.command.name or ""] }, - }, - - hl_mode = "combine", - }, - - on_args = { - { - on_before = { - end_col = arg.range[2] + 1, - conceal = "", - - virt_text_pos = "inline", - virt_text = { - { " " }, - }, - - hl_mode = "combine" - }, - after_offset = function (range) - return { range[1], range[2], range[3], range[4] - 1 }; - end, - on_after = { - end_col = arg.range[4], - conceal = "", - }, - } - } - }; else ---@type markview.config.latex.commands.opts - config = utils.match(main_config, command_name, { default = false, eval_args = { buffer, item } }); + config = utils.match(main_config, command_name, { default = false }); if type(config) ~= "table" or vim.tbl_isempty(config) == true then return; From 98fdc4d68b5c5bc16446e95e6a7145029d2c486e Mon Sep 17 00:00:00 2001 From: KpihX Date: Sun, 20 Sep 2026 13:28:26 +0200 Subject: [PATCH 3/6] feat(renderer): native LaTeX-to-Unicode rendering via Rust FFI --- lua/markview/renderers/latex.lua | 80 +++++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/lua/markview/renderers/latex.lua b/lua/markview/renderers/latex.lua index 9203dc8e..37b5bc25 100644 --- a/lua/markview/renderers/latex.lua +++ b/lua/markview/renderers/latex.lua @@ -867,7 +867,6 @@ end ---@param buffer integer ---@param content markview.parsed.latex[] latex.render = function (buffer, content) - --- Clean up previous caches. latex.cache = { font_regions = {}, style_regions = { @@ -876,6 +875,85 @@ latex.render = function (buffer, content) }, }; + local ok_ffi, math_ffi = pcall(require, "markview.ffi.math"); + if ok_ffi and math_ffi.init() then + local lines = vim.api.nvim_buf_get_lines(buffer, 0, -1, false); + local in_block = false; + local b_start = 0; + local b_lines = {}; + local b_delim = ""; + + for l_idx, line in ipairs(lines) do + local r = l_idx - 1; + local trimmed = vim.trim(line); + + if not in_block then + if trimmed:sub(1, 2) == "$$" or trimmed:sub(1, 2) == "\\[" then + b_delim = trimmed:sub(1, 2); + local closer = b_delim == "$$" and "$$" or "\\]"; + if #trimmed > 2 and trimmed:sub(-#closer) == closer then + local raw = trimmed:sub(3, -#closer - 1); + local rendered = math_ffi.to_unicode_block(raw); + local r_lines = vim.split(rendered, "\n", {}); + local virt = {}; + for _, rl in ipairs(r_lines) do + table.insert(virt, { { " " .. rl, "Special" } }); + end + vim.api.nvim_buf_set_extmark(buffer, latex.ns, r, 0, { + virt_lines = virt, + virt_lines_above = true, + hl_mode = "combine", + }); + else + in_block = true; + b_start = r; + b_lines = { trimmed:sub(3) }; + end + else + -- Inline math: $...$ and \(...\) + local s_col = 1; + while true do + local s, e = line:find("%$[^%$]+%$", s_col); + if not s then break; end + local raw = line:sub(s + 1, e - 1); + local rendered = math_ffi.to_unicode(raw); + pcall(vim.api.nvim_buf_set_extmark, buffer, latex.ns, r, s - 1, { + end_row = r, + end_col = e, + conceal = "", + virt_text = { { rendered, "Special" } }, + virt_text_pos = "inline", + hl_mode = "combine", + }); + s_col = e + 1; + end + end + else + local closer = b_delim == "$$" and "$$" or "\\]"; + if trimmed == closer or trimmed:sub(-#closer) == closer then + in_block = false; + if trimmed ~= closer then + table.insert(b_lines, trimmed:sub(1, -#closer - 1)); + end + local raw = table.concat(b_lines, "\n"); + local rendered = math_ffi.to_unicode_block(raw); + local r_lines = vim.split(rendered, "\n", {}); + local virt = {}; + for _, rl in ipairs(r_lines) do + table.insert(virt, { { " " .. rl, "Special" } }); + end + vim.api.nvim_buf_set_extmark(buffer, latex.ns, b_start, 0, { + virt_lines = virt, + virt_lines_above = true, + hl_mode = "combine", + }); + else + table.insert(b_lines, line); + end + end + end + end + local custom = spec.get({ "renderers" }, { fallback = {} }); local post = {}; From 96824642652f211dc18a8f1e4fb832cb12caba7c Mon Sep 17 00:00:00 2001 From: KpihX Date: Sun, 20 Sep 2026 13:30:32 +0200 Subject: [PATCH 4/6] feat: hook latex renderer directly into markdown parsing pipeline --- lua/markview/renderer.lua | 5 +++++ lua/markview/renderers/latex.lua | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lua/markview/renderer.lua b/lua/markview/renderer.lua index 81a3161b..40933fb6 100644 --- a/lua/markview/renderer.lua +++ b/lua/markview/renderer.lua @@ -386,6 +386,7 @@ end ---@param parsed_content markview.parsed renderer.render = function (buffer, parsed_content) ---|fS + parsed_content = parsed_content or {} local _renderers = { asciidoc = require("markview.renderers.asciidoc"), @@ -427,6 +428,10 @@ renderer.render = function (buffer, parsed_content) ---|fE + if _renderers["latex"] then + _renderers["latex"].render(buffer, parsed_content.latex or {}); + end + for lang, content in pairs(parsed_content) do ---@cast lang string ---@cast content diff --git a/lua/markview/renderers/latex.lua b/lua/markview/renderers/latex.lua index 37b5bc25..7909d59e 100644 --- a/lua/markview/renderers/latex.lua +++ b/lua/markview/renderers/latex.lua @@ -897,7 +897,7 @@ latex.render = function (buffer, content) local r_lines = vim.split(rendered, "\n", {}); local virt = {}; for _, rl in ipairs(r_lines) do - table.insert(virt, { { " " .. rl, "Special" } }); + table.insert(virt, { { rl, "Special" } }); end vim.api.nvim_buf_set_extmark(buffer, latex.ns, r, 0, { virt_lines = virt, @@ -940,7 +940,7 @@ latex.render = function (buffer, content) local r_lines = vim.split(rendered, "\n", {}); local virt = {}; for _, rl in ipairs(r_lines) do - table.insert(virt, { { " " .. rl, "Special" } }); + table.insert(virt, { { rl, "Special" } }); end vim.api.nvim_buf_set_extmark(buffer, latex.ns, b_start, 0, { virt_lines = virt, From 73d9f4a732b6ad139e7b38e86dd5228e20a8c379 Mon Sep 17 00:00:00 2001 From: KpihX Date: Wed, 23 Sep 2026 14:21:34 +0200 Subject: [PATCH 5/6] fix(latex): conceal source; show FFI Unicode only Anchor virt_lines on the previous visible row and conceal_lines the math block so Markview no longer stacks ASCII Unicode on top of raw LaTeX. --- lua/markview/renderers/latex.lua | 183 ++++++++++++++++++++++++------- 1 file changed, 144 insertions(+), 39 deletions(-) diff --git a/lua/markview/renderers/latex.lua b/lua/markview/renderers/latex.lua index 7909d59e..a57ea6c9 100644 --- a/lua/markview/renderers/latex.lua +++ b/lua/markview/renderers/latex.lua @@ -29,6 +29,7 @@ latex.block = function (buffer, item) return; end + -- Prefer Rust FFI Unicode render: conceal the whole source block, show Unicode only. local ok, math_ffi = pcall(require, "markview.ffi.math"); if ok and math_ffi.init() then local raw_latex = table.concat(item.text, "\n"); @@ -38,14 +39,42 @@ latex.block = function (buffer, item) for _, r_line in ipairs(rendered_lines) do table.insert(virt_lines, { { " " .. r_line, "Special" } }); end - vim.api.nvim_buf_set_extmark(buffer, latex.ns, range.row_start, range.col_start, { - undo_restore = false, invalidate = true, - end_row = range.row_end, - end_col = range.col_end, - virt_lines = virt_lines, - virt_lines_above = true, - hl_mode = "combine", - }); + -- virt_lines must not share the concealed lines (nvim does not draw them there). + if range.row_start > 0 then + vim.api.nvim_buf_set_extmark(buffer, latex.ns, range.row_start - 1, 0, { + undo_restore = false, invalidate = true, + virt_lines = virt_lines, + virt_lines_above = false, + hl_mode = "combine", + }); + vim.api.nvim_buf_set_extmark(buffer, latex.ns, range.row_start, 0, { + undo_restore = false, invalidate = true, + end_row = range.row_end, + end_col = 0, + conceal_lines = "", + }); + else + local first = table.remove(virt_lines, 1); + vim.api.nvim_buf_set_extmark(buffer, latex.ns, 0, 0, { + undo_restore = false, invalidate = true, + end_col = #(item.text[1] or ""), + conceal = "", + virt_text = first, + virt_text_pos = "overlay", + virt_lines = #virt_lines > 0 and virt_lines or nil, + virt_lines_above = false, + hl_mode = "combine", + }); + if range.row_end > 0 then + vim.api.nvim_buf_set_extmark(buffer, latex.ns, 1, 0, { + undo_restore = false, invalidate = true, + end_row = range.row_end, + end_col = 0, + conceal_lines = "", + }); + end + end + return; end vim.api.nvim_buf_set_extmark(buffer, latex.ns, range.row_start, range.col_start, { @@ -875,6 +904,23 @@ latex.render = function (buffer, content) }, }; + --- Ranges covered by Rust FFI Unicode render (row_start, col_start, row_end, col_end). + ---@type integer[][] + local ffi_spans = {}; + + ---@param range { row_start: integer, col_start: integer, row_end: integer, col_end: integer } + ---@return boolean + local function ffi_covers(range) + for _, s in ipairs(ffi_spans) do + if range.row_start >= s[1] and range.row_end <= s[3] + and (range.row_start > s[1] or range.col_start >= s[2]) + and (range.row_end < s[3] or range.col_end <= s[4]) then + return true; + end + end + return false; + end + local ok_ffi, math_ffi = pcall(require, "markview.ffi.math"); if ok_ffi and math_ffi.init() then local lines = vim.api.nvim_buf_get_lines(buffer, 0, -1, false); @@ -883,6 +929,74 @@ latex.render = function (buffer, content) local b_lines = {}; local b_delim = ""; + ---@param row_start integer + ---@param row_end integer + ---@param rendered string + local function place_block_unicode(row_start, row_end, rendered) + local r_lines = vim.split(rendered, "\n", {}); + local virt = {}; + for _, rl in ipairs(r_lines) do + table.insert(virt, { { rl, "Special" } }); + end + -- virt_lines on a concealed line are not drawn — anchor Unicode on the + -- previous visible line (below it), then conceal the whole math block. + if row_start > 0 then + vim.api.nvim_buf_set_extmark(buffer, latex.ns, row_start - 1, 0, { + undo_restore = false, invalidate = true, + virt_lines = virt, + virt_lines_above = false, + hl_mode = "combine", + }); + vim.api.nvim_buf_set_extmark(buffer, latex.ns, row_start, 0, { + undo_restore = false, invalidate = true, + end_row = row_end, + end_col = 0, + conceal_lines = "", + }); + else + -- Block at buffer top: keep first source line as virt_text anchor, + -- conceal the rest of the block lines. + local first = table.remove(virt, 1); + vim.api.nvim_buf_set_extmark(buffer, latex.ns, 0, 0, { + undo_restore = false, invalidate = true, + end_row = 0, + end_col = #lines[1], + conceal = "", + virt_text = first, + virt_text_pos = "overlay", + virt_lines = #virt > 0 and virt or nil, + virt_lines_above = false, + hl_mode = "combine", + }); + if row_end > 0 then + vim.api.nvim_buf_set_extmark(buffer, latex.ns, 1, 0, { + undo_restore = false, invalidate = true, + end_row = row_end, + end_col = 0, + conceal_lines = "", + }); + end + end + table.insert(ffi_spans, { row_start, 0, row_end, math.huge }); + end + + ---@param row integer + ---@param col_start integer + ---@param col_end integer + ---@param rendered string + local function place_inline_unicode(row, col_start, col_end, rendered) + pcall(vim.api.nvim_buf_set_extmark, buffer, latex.ns, row, col_start, { + undo_restore = false, invalidate = true, + end_row = row, + end_col = col_end, + conceal = "", + virt_text = { { rendered, "Special" } }, + virt_text_pos = "inline", + hl_mode = "combine", + }); + table.insert(ffi_spans, { row, col_start, row, col_end }); + end + for l_idx, line in ipairs(lines) do local r = l_idx - 1; local trimmed = vim.trim(line); @@ -893,17 +1007,7 @@ latex.render = function (buffer, content) local closer = b_delim == "$$" and "$$" or "\\]"; if #trimmed > 2 and trimmed:sub(-#closer) == closer then local raw = trimmed:sub(3, -#closer - 1); - local rendered = math_ffi.to_unicode_block(raw); - local r_lines = vim.split(rendered, "\n", {}); - local virt = {}; - for _, rl in ipairs(r_lines) do - table.insert(virt, { { rl, "Special" } }); - end - vim.api.nvim_buf_set_extmark(buffer, latex.ns, r, 0, { - virt_lines = virt, - virt_lines_above = true, - hl_mode = "combine", - }); + place_block_unicode(r, r, math_ffi.to_unicode_block(raw)); else in_block = true; b_start = r; @@ -916,15 +1020,15 @@ latex.render = function (buffer, content) local s, e = line:find("%$[^%$]+%$", s_col); if not s then break; end local raw = line:sub(s + 1, e - 1); - local rendered = math_ffi.to_unicode(raw); - pcall(vim.api.nvim_buf_set_extmark, buffer, latex.ns, r, s - 1, { - end_row = r, - end_col = e, - conceal = "", - virt_text = { { rendered, "Special" } }, - virt_text_pos = "inline", - hl_mode = "combine", - }); + place_inline_unicode(r, s - 1, e, math_ffi.to_unicode(raw)); + s_col = e + 1; + end + s_col = 1; + while true do + local s, e = line:find("\\%(.-\\%)", s_col); + if not s then break; end + local raw = line:sub(s + 2, e - 2); + place_inline_unicode(r, s - 1, e, math_ffi.to_unicode(raw)); s_col = e + 1; end end @@ -936,17 +1040,7 @@ latex.render = function (buffer, content) table.insert(b_lines, trimmed:sub(1, -#closer - 1)); end local raw = table.concat(b_lines, "\n"); - local rendered = math_ffi.to_unicode_block(raw); - local r_lines = vim.split(rendered, "\n", {}); - local virt = {}; - for _, rl in ipairs(r_lines) do - table.insert(virt, { { rl, "Special" } }); - end - vim.api.nvim_buf_set_extmark(buffer, latex.ns, b_start, 0, { - virt_lines = virt, - virt_lines_above = true, - hl_mode = "combine", - }); + place_block_unicode(b_start, r, math_ffi.to_unicode_block(raw)); else table.insert(b_lines, line); end @@ -958,6 +1052,11 @@ latex.render = function (buffer, content) local post = {}; for _, item in ipairs(content or {}) do + -- Skip native latex renderers for spans already replaced by FFI Unicode. + if item.range and ffi_covers(item.range) then + goto continue; + end + if vim.list_contains({ "latex_word", "latex_symbol" }, item.class) == true then table.insert(post, item); else @@ -982,9 +1081,14 @@ latex.render = function (buffer, content) }); end end + ::continue:: end for _, item in ipairs(post) do + if item.range and ffi_covers(item.range) then + goto continue_post; + end + local success, err; if custom[item.class] then @@ -1005,6 +1109,7 @@ latex.render = function (buffer, content) } }); end + ::continue_post:: end end From 153955323efc6fabc5fe0d154c87e6d4f8e04635 Mon Sep 17 00:00:00 2001 From: KpihX Date: Tue, 29 Sep 2026 19:45:23 +0200 Subject: [PATCH 6/6] fix(latex): synchronize symbols and preserve unsupported spans --- lua/markview/ffi/math.lua | 102 ++++++++++++++++++++++++------- lua/markview/renderers/latex.lua | 92 ++++++++++++++++------------ 2 files changed, 134 insertions(+), 60 deletions(-) diff --git a/lua/markview/ffi/math.lua b/lua/markview/ffi/math.lua index 8cd07b3a..c7217ba4 100644 --- a/lua/markview/ffi/math.lua +++ b/lua/markview/ffi/math.lua @@ -1,14 +1,18 @@ local ffi = require("ffi") ffi.cdef[[ + int latex_register_symbols(const char *const *names, const char *const *values, unsigned int count); char* latex_to_unicode(const char* input); char* latex_to_unicode_block(const char* input); + char* latex_to_unicode_checked(const char* input, int* fully_supported); + char* latex_to_unicode_block_checked(const char* input, int* fully_supported); char* latex_transform_markdown(const char* input); void latex_unicode_free(char* ptr); ]] local M = {} local lib = nil +local symbols_registered = false local function get_lib_path() local home = os.getenv("HOME") @@ -27,37 +31,93 @@ local function get_lib_path() return nil end -function M.init() - if lib then return true end - local path = get_lib_path() - if not path then - return false, "liblatex_to_unicode_c_api.so not found" +local function register_markview_symbols() + local entries = require("markview.symbols").entries + local names = {} + + for name, value in pairs(entries) do + if type(name) == "string" and type(value) == "string" then + table.insert(names, name) + end + end + + table.sort(names) + if #names == 0 then + return false, "Markview symbol table is empty" + end + + local name_buffers, value_buffers = {}, {} + local name_pointers = ffi.new("const char *[?]", #names) + local value_pointers = ffi.new("const char *[?]", #names) + + for i, name in ipairs(names) do + local value = entries[name] + local name_buffer = ffi.new("char[?]", #name + 1) + local value_buffer = ffi.new("char[?]", #value + 1) + ffi.copy(name_buffer, name, #name) + ffi.copy(value_buffer, value, #value) + name_buffers[i] = name_buffer + value_buffers[i] = value_buffer + name_pointers[i - 1] = name_buffer + value_pointers[i - 1] = value_buffer end - local ok, loaded = pcall(ffi.load, path) - if ok then + + -- Keep the pointed-to buffers alive until Rust has copied the table. + local registered = lib.latex_register_symbols(name_pointers, value_pointers, #names) + if registered ~= 1 then + return false, "Rust backend rejected Markview's symbol table" + end + return true +end + +function M.init() + if not lib then + local path = get_lib_path() + if not path then + return false, "liblatex_to_unicode_c_api.so not found" + end + local ok, loaded = pcall(ffi.load, path) + if not ok then + return false, loaded + end lib = loaded - return true - else - return false, loaded end + + if not symbols_registered then + local ok, registered, err = pcall(register_markview_symbols) + if not ok then + return false, registered + end + if not registered then + return false, err + end + symbols_registered = true + end + + return true end -function M.to_unicode(latex_str) - if not M.init() then return latex_str end - local c_str = lib.latex_to_unicode(latex_str) - if c_str == nil then return latex_str end +local function convert(latex_str, block) + if not M.init() then return latex_str, false end + local fully_supported = ffi.new("int[1]") + local c_str + if block then + c_str = lib.latex_to_unicode_block_checked(latex_str, fully_supported) + else + c_str = lib.latex_to_unicode_checked(latex_str, fully_supported) + end + if c_str == nil then return latex_str, false end local res = ffi.string(c_str) lib.latex_unicode_free(c_str) - return res + return res, fully_supported[0] ~= 0 +end + +function M.to_unicode(latex_str) + return convert(latex_str, false) end function M.to_unicode_block(latex_str) - if not M.init() then return latex_str end - local c_str = lib.latex_to_unicode_block(latex_str) - if c_str == nil then return latex_str end - local res = ffi.string(c_str) - lib.latex_unicode_free(c_str) - return res + return convert(latex_str, true) end function M.transform_markdown(md_str) diff --git a/lua/markview/renderers/latex.lua b/lua/markview/renderers/latex.lua index a57ea6c9..9537f9c9 100644 --- a/lua/markview/renderers/latex.lua +++ b/lua/markview/renderers/latex.lua @@ -33,48 +33,50 @@ latex.block = function (buffer, item) local ok, math_ffi = pcall(require, "markview.ffi.math"); if ok and math_ffi.init() then local raw_latex = table.concat(item.text, "\n"); - local rendered = math_ffi.to_unicode_block(raw_latex); - local rendered_lines = vim.split(rendered, "\n", {}); - local virt_lines = {}; - for _, r_line in ipairs(rendered_lines) do - table.insert(virt_lines, { { " " .. r_line, "Special" } }); - end - -- virt_lines must not share the concealed lines (nvim does not draw them there). - if range.row_start > 0 then - vim.api.nvim_buf_set_extmark(buffer, latex.ns, range.row_start - 1, 0, { - undo_restore = false, invalidate = true, - virt_lines = virt_lines, - virt_lines_above = false, - hl_mode = "combine", - }); - vim.api.nvim_buf_set_extmark(buffer, latex.ns, range.row_start, 0, { - undo_restore = false, invalidate = true, - end_row = range.row_end, - end_col = 0, - conceal_lines = "", - }); - else - local first = table.remove(virt_lines, 1); - vim.api.nvim_buf_set_extmark(buffer, latex.ns, 0, 0, { - undo_restore = false, invalidate = true, - end_col = #(item.text[1] or ""), - conceal = "", - virt_text = first, - virt_text_pos = "overlay", - virt_lines = #virt_lines > 0 and virt_lines or nil, - virt_lines_above = false, - hl_mode = "combine", - }); - if range.row_end > 0 then - vim.api.nvim_buf_set_extmark(buffer, latex.ns, 1, 0, { + local rendered, fully_supported = math_ffi.to_unicode_block(raw_latex); + if fully_supported then + local rendered_lines = vim.split(rendered, "\n", {}); + local virt_lines = {}; + for _, r_line in ipairs(rendered_lines) do + table.insert(virt_lines, { { " " .. r_line, "Special" } }); + end + -- virt_lines must not share the concealed lines (nvim does not draw them there). + if range.row_start > 0 then + vim.api.nvim_buf_set_extmark(buffer, latex.ns, range.row_start - 1, 0, { + undo_restore = false, invalidate = true, + virt_lines = virt_lines, + virt_lines_above = false, + hl_mode = "combine", + }); + vim.api.nvim_buf_set_extmark(buffer, latex.ns, range.row_start, 0, { undo_restore = false, invalidate = true, end_row = range.row_end, end_col = 0, conceal_lines = "", }); + else + local first = table.remove(virt_lines, 1); + vim.api.nvim_buf_set_extmark(buffer, latex.ns, 0, 0, { + undo_restore = false, invalidate = true, + end_col = #(item.text[1] or ""), + conceal = "", + virt_text = first, + virt_text_pos = "overlay", + virt_lines = #virt_lines > 0 and virt_lines or nil, + virt_lines_above = false, + hl_mode = "combine", + }); + if range.row_end > 0 then + vim.api.nvim_buf_set_extmark(buffer, latex.ns, 1, 0, { + undo_restore = false, invalidate = true, + end_row = range.row_end, + end_col = 0, + conceal_lines = "", + }); + end end + return; end - return; end vim.api.nvim_buf_set_extmark(buffer, latex.ns, range.row_start, range.col_start, { @@ -1007,7 +1009,10 @@ latex.render = function (buffer, content) local closer = b_delim == "$$" and "$$" or "\\]"; if #trimmed > 2 and trimmed:sub(-#closer) == closer then local raw = trimmed:sub(3, -#closer - 1); - place_block_unicode(r, r, math_ffi.to_unicode_block(raw)); + local rendered, fully_supported = math_ffi.to_unicode_block(raw); + if fully_supported then + place_block_unicode(r, r, rendered); + end else in_block = true; b_start = r; @@ -1020,7 +1025,10 @@ latex.render = function (buffer, content) local s, e = line:find("%$[^%$]+%$", s_col); if not s then break; end local raw = line:sub(s + 1, e - 1); - place_inline_unicode(r, s - 1, e, math_ffi.to_unicode(raw)); + local rendered, fully_supported = math_ffi.to_unicode(raw); + if fully_supported then + place_inline_unicode(r, s - 1, e, rendered); + end s_col = e + 1; end s_col = 1; @@ -1028,7 +1036,10 @@ latex.render = function (buffer, content) local s, e = line:find("\\%(.-\\%)", s_col); if not s then break; end local raw = line:sub(s + 2, e - 2); - place_inline_unicode(r, s - 1, e, math_ffi.to_unicode(raw)); + local rendered, fully_supported = math_ffi.to_unicode(raw); + if fully_supported then + place_inline_unicode(r, s - 1, e, rendered); + end s_col = e + 1; end end @@ -1040,7 +1051,10 @@ latex.render = function (buffer, content) table.insert(b_lines, trimmed:sub(1, -#closer - 1)); end local raw = table.concat(b_lines, "\n"); - place_block_unicode(b_start, r, math_ffi.to_unicode_block(raw)); + local rendered, fully_supported = math_ffi.to_unicode_block(raw); + if fully_supported then + place_block_unicode(b_start, r, rendered); + end else table.insert(b_lines, line); end