feat(image): refactor of treesitter queries to support inline image data

This commit is contained in:
Folke Lemaitre 2025-02-17 12:49:38 +01:00
parent 48a3fed3c5
commit 0bf0c6223d
No known key found for this signature in database
GPG key ID: 41F8B1FBACAE2040
22 changed files with 393 additions and 68 deletions

View file

@ -0,0 +1,161 @@
---@class snacks.image.convert
local M = {}
-- vim.list_extend(args, {
-- -- "-density",
-- -- "4000",
-- -- "-background",
-- -- "transparent",
-- -- "-flatten",
-- -- "+repage",
-- -- -- "-adaptive-resize",
-- -- -- "800",
-- -- "-quality",
-- -- "100",
-- -- "-trim",
-- })
---@alias snacks.image.generate.on_done fun(code: number)
---@class snacks.image.generate
---@field cwd? string
---@field cmd string
---@field args string[]
---@field on_done? snacks.image.generate.on_done
local uv = vim.uv or vim.loop
local have_magick ---@type boolean
---@param src string
function M.is_url(src)
return src:find("^https?://") == 1
end
---@param src string
function M.is_uri(src)
return src:find("^%w%w+://") == 1
end
---@param src string
function M.norm(src)
if src:find("^file://") then
return vim.fs.normalize(vim.uri_to_fname(src))
end
return src
end
---@param src string
---@param ext string
function M.tmpfile(src, ext)
local root = Snacks.image.config.cache
local base = vim.fn.fnamemodify(src, ":t:r")
if M.is_uri(src) then
base = src:gsub("%?.*", ""):match("^%w%w+://(.*)$") or base
end
base = base:gsub("[^%w%.]+", "-")
vim.fn.mkdir(root, "p")
return root .. "/" .. vim.fn.sha256(src):sub(1, 8) .. "-" .. base .. "." .. ext
end
---@param file string
---@param opts snacks.image.generate
---@return (fun(): boolean) is_ready
function M.generate(file, opts)
local on_done = function(code)
if opts.on_done then
opts.on_done(code)
end
end
if vim.fn.filereadable(file) == 1 then
on_done(0)
return function()
return true
end
end
if Snacks.image.config.debug.convert then
Snacks.debug.cmd(opts)
end
local handle ---@type uv.uv_process_t
handle = uv.spawn(opts.cmd, opts, function(code)
handle:close()
on_done(code)
end)
return function()
return (not handle or handle:is_closing() or false) and vim.fn.filereadable(file) == 1
end
end
---@param src string
---@param dest string
---@param opts? {args?: (string|number)[], on_done?: snacks.image.generate.on_done}
---@return (fun(): boolean) is_ready
function M.magick(src, dest, opts)
opts = opts or {}
local args = opts.args or { src .. "[0]" } ---@type string[]
args[#args + 1] = dest
have_magick = have_magick == nil and vim.fn.executable("magick") == 1 or have_magick
return M.generate(dest, {
cmd = have_magick and "magick" or "convert",
args = args,
on_done = opts.on_done,
})
end
---@param src string
---@param dest string
---@param opts? {on_done?: snacks.image.generate.on_done}
---@return (fun(): boolean) is_ready
function M.tex2pdf(src, dest, opts)
return M.generate(dest, {
cmd = "pdflatex",
args = { "-output-directory=" .. vim.fn.fnamemodify(dest, ":h"), src },
on_done = opts and opts.on_done,
})
end
---@param src string
---@param opts? {on_done?: snacks.image.generate.on_done}
---@return string png, (fun(): boolean) is_ready
function M.convert(src, opts)
local png = M.tmpfile(src, "png")
src = M.norm(src)
if not M.is_uri(src) then
src = vim.fs.normalize(src)
png = M.tmpfile(src, "png")
local ext = vim.fn.fnamemodify(src, ":e"):lower()
if ext == "png" then
if opts and opts.on_done then
opts.on_done(0)
end
return src, function()
return true
end
elseif ext == "tex" then
local pdf = src:gsub("%.tex$", ".pdf")
local is_ready = function()
return false
end
M.tex2pdf(src, pdf, {
on_done = function(code)
if code == 0 then
opts.args = { "-density", 300, pdf, "-trim" }
is_ready = M.magick(pdf, png, opts)
end
end,
})
return png, function()
return is_ready()
end
end
end
opts.args = {
-- "-density",
-- 128,
src,
"-scale",
"200%",
}
return png, M.magick(src, png, opts)
end
return M

View file

@ -5,7 +5,8 @@
---@field sent? boolean image data is sent ---@field sent? boolean image data is sent
---@field placements table<number, snacks.image.Placement> image placements ---@field placements table<number, snacks.image.Placement> image placements
---@field augroup number ---@field augroup number
---@field _convert uv.uv_process_t? ---@field _ready fun(): boolean
---@field closed? boolean
local M = {} local M = {}
M.__index = M M.__index = M
@ -16,7 +17,6 @@ local _pid = 0
local nvim_id = 0 local nvim_id = 0
local uv = vim.uv or vim.loop local uv = vim.uv or vim.loop
local images = {} ---@type table<string, snacks.Image> local images = {} ---@type table<string, snacks.Image>
local have_magick ---@type boolean?
local terminal = Snacks.image.terminal local terminal = Snacks.image.terminal
---@param src string ---@param src string
@ -25,6 +25,7 @@ function M.new(src)
self.src = src self.src = src
self.file = self:convert() self.file = self:convert()
if images[self.file] then if images[self.file] then
self.closed = true
return images[self.file] return images[self.file]
end end
images[self.file] = self images[self.file] = self
@ -38,18 +39,15 @@ function M.new(src)
-- interleave the nvim id and the image id -- interleave the nvim id and the image id
self.id = bit.bor(bit.lshift(nvim_id, 24 - NVIM_ID_BITS), _id) self.id = bit.bor(bit.lshift(nvim_id, 24 - NVIM_ID_BITS), _id)
self.placements = {} self.placements = {}
if self:ready() then
vim.schedule(function()
self:on_ready()
end)
end
self.augroup = vim.api.nvim_create_augroup("snacks.image." .. self.id, { clear = true }) self.augroup = vim.api.nvim_create_augroup("snacks.image." .. self.id, { clear = true })
return self return self
end end
function M:on_ready() function M:on_ready()
self:send() if not self.sent and not self.closed then
self:send()
end
end end
function M:on_send() function M:on_send()
@ -59,39 +57,23 @@ function M:on_send()
end end
function M:ready() function M:ready()
return (not self._convert or self._convert:is_closing()) and self.file and vim.fn.filereadable(self.file) == 1 return self._ready() and self.file and vim.fn.filereadable(self.file) == 1
end end
function M:convert() function M:convert()
local src = self.src local png, ready = Snacks.image.convert.convert(self.src, {
if src:find("^file://") then on_done = function(code)
src = vim.uri_to_fname(src) if code == 0 then
end vim.schedule(function()
-- convert urls and non-png files to png self:on_ready()
if not src:find("^https?://") and src:lower():find("%.png$") then end)
return src else
end Snacks.notify.error("Failed to convert image to " .. self.file)
if not src:find("^%w%w+://") then end
src = vim.fs.normalize(src) end,
end })
local fin = src .. "[0]" self._ready = ready
local root = Snacks.image.config.cache return png
vim.fn.mkdir(root, "p")
src = root .. "/" .. Snacks.util.file_encode(fin) .. ".png"
if vim.fn.filereadable(src) == 1 then
return src
end
local opts = { args = { fin, src } }
have_magick = have_magick == nil and vim.fn.executable("magick") == 1 or have_magick
self._convert = uv.spawn(have_magick and "magick" or "convert", opts, function(code)
self._convert:close()
if code == 0 then
vim.schedule(function()
self:on_ready()
end)
end
end)
return src
end end
-- create the image -- create the image

View file

@ -5,10 +5,11 @@
---@field util snacks.image.util ---@field util snacks.image.util
---@field buf snacks.image.buf ---@field buf snacks.image.buf
---@field doc snacks.image.doc ---@field doc snacks.image.doc
---@field convert snacks.image.convert
local M = setmetatable({}, { local M = setmetatable({}, {
---@param M snacks.image ---@param M snacks.image
__index = function(M, k) __index = function(M, k)
if vim.tbl_contains({ "terminal", "image", "placement", "util", "doc", "buf" }, k) then if vim.tbl_contains({ "terminal", "image", "placement", "util", "doc", "buf", "convert" }, k) then
M[k] = require("snacks.image." .. k) M[k] = require("snacks.image." .. k)
end end
return rawget(M, k) return rawget(M, k)
@ -75,7 +76,11 @@ local defaults = {
statuscolumn = "", statuscolumn = "",
}, },
cache = vim.fn.stdpath("cache") .. "/snacks/image", cache = vim.fn.stdpath("cache") .. "/snacks/image",
debug = false, debug = {
request = false,
convert = false,
placement = false,
},
env = {}, env = {},
} }
M.config = Snacks.config.get("image", defaults) M.config = Snacks.config.get("image", defaults)
@ -225,6 +230,12 @@ function M.health()
end end
end end
if Snacks.health.have_tool({ "pdflatex" }) then
Snacks.health.ok("`pdflatex` is available to render math expressions in `latex` and `markdown` documents")
else
Snacks.health.warn("`pdflatex` is required to render LaTeX math equations")
end
if env.supported then if env.supported then
Snacks.health.ok("your terminal supports the kitty graphics protocol") Snacks.health.ok("your terminal supports the kitty graphics protocol")
elseif M.config.force then elseif M.config.force then

View file

@ -114,7 +114,7 @@ function M:render_grid(loc)
vim.api.nvim_set_hl(0, hl, { vim.api.nvim_set_hl(0, hl, {
fg = self.img.id, fg = self.img.id,
sp = self.id, sp = self.id,
bg = Snacks.image.config.debug and "#FF007C" or nil, bg = Snacks.image.config.debug.placement and "#FF007C" or nil,
}) })
local lines = {} ---@type string[] local lines = {} ---@type string[]
for r = 1, loc.height do for r = 1, loc.height do
@ -180,7 +180,7 @@ function M:render_fallback(state)
end end
function M:debug(...) function M:debug(...)
if not Snacks.image.config.debug then if true or not Snacks.image.config.debug then
return return
end end
Snacks.debug.inspect({ ... }, self.img.src, self.img.id, self.id) Snacks.debug.inspect({ ... }, self.img.src, self.img.id, self.id)

View file

@ -169,7 +169,7 @@ function M.request(opts)
if env.transform then if env.transform then
data = env.transform(data) data = env.transform(data)
end end
if Snacks.image.config.debug and opts.m ~= 1 then if Snacks.image.config.debug.request and opts.m ~= 1 then
Snacks.debug.inspect(opts) Snacks.debug.inspect(opts)
end end
io.stdout:write(data) io.stdout:write(data)

View file

@ -30,8 +30,8 @@ end
function M.pixels_to_cells(size) function M.pixels_to_cells(size)
local terminal = Snacks.image.terminal.size() local terminal = Snacks.image.terminal.size()
return M.norm({ return M.norm({
width = size.width / terminal.cell_width * terminal.scale, width = size.width / terminal.cell_width,
height = size.height / terminal.cell_height * terminal.scale, height = size.height / terminal.cell_height,
}) })
end end

View file

@ -2,5 +2,5 @@
(declaration (declaration
(call_expression (call_expression
(function_name) @fn (#eq? @fn "url") (function_name) @fn (#eq? @fn "url")
(arguments [(plain_value) @image (string_value (string_content) @image)])) (arguments [(plain_value) @image.src (string_value (string_content) @image.src)]))
) @anchor ) @image

View file

@ -4,14 +4,20 @@
(tag_name) @tag (#eq? @tag "img") (tag_name) @tag (#eq? @tag "img")
(attribute (attribute
(attribute_name) @attr_name (#eq? @attr_name "src") (attribute_name) @attr_name (#eq? @attr_name "src")
(quoted_attribute_value (attribute_value) @image) (quoted_attribute_value (attribute_value) @image.src)
) )
) )
) @anchor ) @image
(self_closing_tag (self_closing_tag
(tag_name) @tag (#eq? @tag "img") (tag_name) @tag (#eq? @tag "img")
(attribute (attribute
(attribute_name) @attr_name (#eq? @attr_name "src") (attribute_name) @attr_name (#eq? @attr_name "src")
(quoted_attribute_value (attribute_value) @image) (quoted_attribute_value (attribute_value) @image.src)
) )
) @anchor ) @image
(element
(start_tag (tag_name) @tag (#eq? @tag "svg"))
(#set! ext "svg")
) @image @image.content

View file

@ -4,15 +4,15 @@
(identifier) @tag (#any-of? @tag "img" "Image") (identifier) @tag (#any-of? @tag "img" "Image")
(jsx_attribute (jsx_attribute
(property_identifier) @attr_name (#eq? @attr_name "src") (property_identifier) @attr_name (#eq? @attr_name "src")
(string (string_fragment) @image) (string (string_fragment) @image.src)
) )
) )
) @anchor ) @image
(jsx_self_closing_element (jsx_self_closing_element
(identifier) @tag (#any-of? @tag "img" "Image") (identifier) @tag (#any-of? @tag "img" "Image")
(jsx_attribute (jsx_attribute
(property_identifier) @attr_name (#eq? @attr_name "src") (property_identifier) @attr_name (#eq? @attr_name "src")
(string (string_fragment) @image) (string (string_fragment) @image.src)
) )
) @anchor ) @image

9
queries/latex/images.scm Normal file
View file

@ -0,0 +1,9 @@
(inline_formula) @image.content @image
(displayed_equation) @image.content @image
(math_environment) @image.content @image
(graphics_include
(_ (path) @image.src)
) @image

View file

@ -1 +1,9 @@
; extends ; extends
(fenced_code_block
(info_string (language) @lang)
(#eq? @lang "math")
(code_fence_content) @image.content
(#set! injection.language "latex")
(#set! image.ext "tex")
) @image

View file

@ -0,0 +1,8 @@
; extends
(fenced_code_block
(info_string (language) @lang)
(#eq? @lang "math")
(code_fence_content) @injection.content
(#set! injection.language "latex")
)

View file

@ -1,9 +1,9 @@
(image (image
[ [
(link_destination) @image (link_destination) @image.src
(image_description (shortcut_link (link_text) @image)) (image_description (shortcut_link (link_text) @image.src))
(#gsub! @image "|.*" "") ; remove wikilink image options (#gsub! @image.src "|.*" "") ; remove wikilink image options
(#gsub! @image "^<" "") ; remove bracket link (#gsub! @image.src "^<" "") ; remove bracket link
(#gsub! @image "^>" "") (#gsub! @image.src "^>" "")
]) @anchor ]) @image

View file

@ -1,5 +1,5 @@
(infirm_tag (infirm_tag
(tag_name) @tag (#eq? @tag "image") (tag_name) @tag (#eq? @tag "image")
(tag_parameters (tag_param) @image) (tag_parameters (tag_param) @image.src)
) @anchor ) @image

View file

@ -2,10 +2,10 @@
(call_expression (call_expression
(function_name) @fn (#eq? @fn "url") (function_name) @fn (#eq? @fn "url")
(arguments [ (arguments [
(plain_value) @image (plain_value) @image.src
(string_value) @image (string_value) @image.src
; Remove quotes from the image URL ; Remove quotes from the image URL
(#gsub! @image "^['\"]" "") (#gsub! @image.src "^['\"]" "")
(#gsub! @image "['\"]$" "") (#gsub! @image.src "['\"]$" "")
])) ]))
) @anchor ) @image

12
tests/image/math.md Normal file
View file

@ -0,0 +1,12 @@
This sentence uses `$` delimiters to show math inline: $\sqrt{3x-1}+(1+x)^2$
This sentence uses delimiters to show math inline: $`\sqrt{3x-1}+(1+x)^2`$
**The Cauchy-Schwarz Inequality**\
$$\left( \sum_{k=1}^n a_k b_k \right)^2 \leq \left( \sum_{k=1}^n a_k^2 \right) \left( \sum_{k=1}^n b_k^2 \right)$$
**The Cauchy-Schwarz Inequality**
```math
\left( \sum_{k=1}^n a_k b_k \right)^2 \leq \left( \sum_{k=1}^n a_k^2 \right) \left( \sum_{k=1}^n b_k^2 \right)
```

BIN
tests/image/math.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

2
tests/image/test.aux Normal file
View file

@ -0,0 +1,2 @@
\relax
\gdef \@abspage@last{1}

View file

@ -1,4 +1,63 @@
<body> <body>
<a href="/" class="navbar-brand" aria-label="logo">
<svg
xmlns="http://www.w3.org/2000/svg"
role="img"
width="173"
height="50"
viewBox="0 0 742 214"
aria-label="Neovim"
>
<title>Neovim</title>
<defs>
<linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="a">
<stop stop-color="#16B0ED" stop-opacity=".8" offset="0%"></stop>
<stop stop-color="#0F59B2" stop-opacity=".837" offset="100%"></stop>
</linearGradient>
<linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="b">
<stop stop-color="#7DB643" offset="0%"></stop>
<stop stop-color="#367533" offset="100%"></stop>
</linearGradient>
<linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="c">
<stop stop-color="#88C649" stop-opacity=".8" offset="0%"></stop>
<stop stop-color="#439240" stop-opacity=".84" offset="100%"></stop>
</linearGradient>
</defs>
<g fill="none" fill-rule="evenodd">
<path
d="M.027 45.459L45.224-.173v212.171L.027 166.894V45.459z"
fill="url(#a)"
transform="translate(1 1)"
></path>
<path
d="M129.337 45.89L175.152-.149l-.928 212.146-45.197-45.104.31-121.005z"
fill="url(#b)"
transform="matrix(-1 0 0 1 305 1)"
></path>
<path
d="M45.194-.137L162.7 179.173l-32.882 32.881L12.25 33.141 45.194-.137z"
fill="url(#c)"
transform="translate(1 1)"
></path>
<path
d="M46.234 84.032l-.063 7.063-36.28-53.563 3.36-3.422 32.983 49.922z"
fill-opacity=".13"
fill="#000"
></path>
<g fill="#444">
<path
d="M227 154V64.44h4.655c1.55 0 2.445.75 2.685 2.25l.806 13.502c4.058-5.16 8.786-9.316 14.188-12.466 5.4-3.15 11.413-4.726 18.037-4.726 4.893 0 9.205.781 12.935 2.34 3.729 1.561 6.817 3.811 9.264 6.751 2.448 2.942 4.297 6.48 5.55 10.621 1.253 4.14 1.88 8.821 1.88 14.042V154h-8.504V96.754c0-8.402-1.91-14.987-5.729-19.757-3.82-4.771-9.667-7.156-17.544-7.156-5.851 0-11.28 1.516-16.292 4.545-5.013 3.032-9.489 7.187-13.427 12.467V154H227zM350.624 63c5.066 0 9.755.868 14.069 2.605 4.312 1.738 8.052 4.268 11.219 7.592s5.638 7.412 7.419 12.264C385.11 90.313 386 95.883 386 102.17c0 1.318-.195 2.216-.588 2.696-.393.48-1.01.719-1.851.719h-64.966v1.70c0 6.708.784 12.609 2.353 17.7 1.567 5.09 3.8 9.357 6.695 12.802 2.895 3.445 6.393 6.034 10.495 7.771 4.1 1.738 8.686 2.606 13.752 2.606 4.524 0 8.446-.494 11.762-1.483 3.317-.988 6.108-2.097 8.37-3.324 2.261-1.227 4.056-2.336 5.383-3.324 1.326-.988 2.292-1.482 2.895-1.482.784 0 1.388.3 1.81.898l2.352 2.875c-1.448 1.797-3.362 3.475-5.745 5.031-2.383 1.558-5.038 2.891-7.962 3.998-2.926 1.109-6.062 1.991-9.41 2.65a52.21 52.21 0 01-10.088.989c-6.152 0-11.762-1.064-16.828-3.19-5.067-2.125-9.415-5.225-13.043-9.298-3.63-4.074-6.435-9.06-8.415-14.96C310.99 121.655 310 114.9 310 107.294c0-6.408.92-12.323 2.76-17.744 1.84-5.421 4.493-10.093 7.961-14.016 3.467-3.922 7.72-6.991 12.758-9.209C338.513 64.11 344.229 63 350.624 63zm.573 6c-4.696 0-8.904.702-12.623 2.105-3.721 1.404-6.936 3.421-9.65 6.053-2.713 2.631-4.908 5.79-6.586 9.474S319.55 94.439 319 99h60c0-4.679-.672-8.874-2.013-12.588-1.343-3.712-3.232-6.856-5.67-9.43-2.44-2.571-5.367-4.545-8.782-5.92-3.413-1.374-7.192-2.062-11.338-2.062zM435.546 63c6.526 0 12.368 1.093 17.524 3.28 5.154 2.186 9.5 5.286 13.04 9.298 3.538 4.013 6.238 8.85 8.099 14.51 1.861 5.66 2.791 11.994 2.791 19.002 0 7.008-.932 13.327-2.791 18.957-1.861 5.631-4.561 10.452-8.099 14.465-3.54 4.012-7.886 7.097-13.04 9.254-5.156 2.156-10.998 3.234-17.524 3.234-6.529 0-12.369-1.078-17.525-3.234-5.155-2.157-9.517-5.242-13.085-9.254-3.57-4.013-6.285-8.836-8.145-14.465-1.861-5.63-2.791-11.95-2.791-18.957 0-7.008.93-13.342 2.791-19.002 1.861-5.66 4.576-10.496 8.145-14.51 3.568-4.012 7.93-7.112 13.085-9.299C423.177 64.094 429.017 63 435.546 63zm-.501 86c5.341 0 10.006-.918 13.997-2.757 3.99-1.838 7.32-4.474 9.992-7.909 2.67-3.435 4.664-7.576 5.986-12.428 1.317-4.85 1.98-10.288 1.98-16.316 0-5.965-.66-11.389-1.98-16.27-1.322-4.88-3.316-9.053-5.986-12.519-2.67-3.463-6-6.13-9.992-7.999-3.991-1.867-8.657-2.802-13.997-2.802s-10.008.935-13.997 2.802c-3.991 1.87-7.322 4.536-9.992 8-2.671 3.465-4.68 7.637-6.03 12.518-1.35 4.881-2.026 10.305-2.026 16.27 0 6.026.675 11.465 2.025 16.316 1.35 4.852 3.36 8.993 6.031 12.428 2.67 3.435 6 6.07 9.992 7.91 3.99 1.838 8.656 2.756 13.997 2.756z"
fill="currentColor"
></path>
<path
d="M530.57 152h-20.05L474 60h18.35c1.61 0 2.967.39 4.072 1.166 1.103.778 1.865 1.763 2.283 2.959l17.722 49.138a92.762 92.762 0 012.551 8.429c.686 2.751 1.298 5.5 1.835 8.25.537-2.75 1.148-5.499 1.835-8.25a77.713 77.713 0 012.64-8.429l18.171-49.138c.417-1.196 1.164-2.181 2.238-2.96 1.074-.776 2.356-1.165 3.849-1.165H567l-36.43 92zM572 61h23v92h-23zM610 153V60.443h13.624c2.887 0 4.78 1.354 5.682 4.06l1.443 6.856a52.7 52.7 0 015.097-4.962 32.732 32.732 0 015.683-3.879 30.731 30.731 0 016.496-2.57c2.314-.632 4.855-.948 7.624-.948 5.832 0 10.63 1.579 14.39 4.736 3.758 3.157 6.57 7.352 8.434 12.585 1.444-3.068 3.248-5.698 5.413-7.894 2.165-2.194 4.541-3.984 7.127-5.367a32.848 32.848 0 018.254-3.068 39.597 39.597 0 018.796-.992c5.111 0 9.653.783 13.622 2.345 3.97 1.565 7.307 3.849 10.014 6.857 2.706 3.007 4.766 6.675 6.18 11.005C739.29 83.537 740 88.5 740 94.092V153h-22.284V94.092c0-5.894-1.294-10.329-3.878-13.306-2.587-2.977-6.376-4.465-11.368-4.465-2.286 0-4.404.391-6.358 1.172a15.189 15.189 0 00-5.144 3.383c-1.473 1.474-2.631 3.324-3.474 5.548-.842 2.225-1.263 4.781-1.263 7.668V153h-22.37V94.092c0-6.194-1.249-10.704-3.744-13.532-2.497-2.825-6.18-4.24-11.051-4.24-3.19 0-6.18.798-8.976 2.391-2.799 1.593-5.399 3.775-7.804 6.54V153H610zM572 30h23v19h-23z"
fill="currentColor"
fill-opacity=".8"
></path>
</g>
</g>
</svg>
</a>
<a href="https://github.com/folke/lazy.nvim/releases/latest"> <a href="https://github.com/folke/lazy.nvim/releases/latest">
<img <img
alt="Latest release" alt="Latest release"

BIN
tests/image/test.pdf Normal file

Binary file not shown.

62
tests/image/test.tex Normal file
View file

@ -0,0 +1,62 @@
\documentclass{article}
\usepackage{graphicx} % For images
\usepackage{amsmath} % For math symbols
\usepackage{amssymb} % For extra math symbols
\begin{document}
\section{Image Tests}
Inline image:
\includegraphics[width=0.5\textwidth]{test.png}
\begin{figure}[h]
\centering
\includegraphics[width=0.7\textwidth]{test.png}
\caption{Test image centered in a figure environment.}
\label{fig:test_image}
\end{figure}
\newpage
\section{Math Tests}
Inline math: $E=mc^2$ and $\int_0^1 x^2 \,dx$
Displayed equation:
\[
f(x) = \sum_{n=0}^{\infty} \frac{x^n}{n!}
\]
Equation environment:
\begin{equation}
a^2 + b^2 = c^2
\label{eq:pythagoras}
\end{equation}
Aligned equations:
\begin{align}
x^2 + y^2 &= r^2 \\
\nabla \cdot \mathbf{E} &= \frac{\rho}{\varepsilon_0}
\end{align}
Multiline equations:
\begin{multline}
a + b + c + d + e + f + g + h + i + j + k = \\
l + m + n + o + p + q + r + s + t + u + v
\end{multline}
\newpage
\section{Testing Referencing}
Referencing an equation: See Eq.~\ref{eq:pythagoras}.
Referencing an image: See Fig.~\ref{fig:test_image}.
\end{document}

5
tests/image/test2.md Normal file
View file

@ -0,0 +1,5 @@
# test
## Wikilinks
!![[test.jpg]]