feat(image): proper inline rendering of math expressions. Closes #1318. Closes #1454

This commit is contained in:
Folke Lemaitre 2025-03-01 08:01:11 +01:00
parent 1bbd47973d
commit 6ea4fa72dc
No known key found for this signature in database
GPG key ID: 41F8B1FBACAE2040
7 changed files with 807 additions and 365 deletions

View file

@ -6,10 +6,11 @@
---@field buf snacks.image.buf
---@field doc snacks.image.doc
---@field convert snacks.image.convert
---@field inline snacks.image.inline
local M = setmetatable({}, {
---@param M snacks.image
__index = function(M, k)
if vim.tbl_contains({ "terminal", "image", "placement", "util", "doc", "buf", "convert" }, k) then
if vim.tbl_contains({ "terminal", "image", "placement", "util", "doc", "buf", "convert", "inline" }, k) then
M[k] = require("snacks.image." .. k)
end
return rawget(M, k)
@ -24,6 +25,7 @@ M.meta = {
---@alias snacks.image.Size {width: number, height: number}
---@alias snacks.image.Pos {[1]: number, [2]: number}
---@alias snacks.image.Loc snacks.image.Pos|snacks.image.Size|{zindex?: number}
---@alias snacks.image.Type "image"|"math"|"chart"
---@class snacks.image.Env
---@field name string
@ -78,7 +80,13 @@ local defaults = {
max_width = 80,
max_height = 40,
-- Set to `true`, to conceal the image text when rendering inline.
conceal = false, -- (experimental)
-- (experimental)
---@param lang string tree-sitter language
---@param type snacks.image.Type image type
conceal = function(lang, type)
-- only conceal math expressions
return type == "math"
end,
},
img_dirs = { "img", "images", "assets", "static", "public", "media", "attachments" },
-- window options applied to windows displaying image buffers
@ -101,6 +109,13 @@ local defaults = {
placement = false,
},
env = {},
-- icons used to show where an inline image is located that is
-- rendered below the text.
icons = {
math = "󰪚 ",
chart = "󰄧 ",
image = "",
},
---@class snacks.image.convert.Config
convert = {
notify = true, -- show a notification on error
@ -137,7 +152,7 @@ local defaults = {
-- but you can add more packages here. Useful for markdown documents.
packages = { "amsmath", "amssymb", "amsfonts", "amscd", "mathtools" },
tpl = [[
\documentclass[preview,border=2pt,varwidth,12pt]{standalone}
\documentclass[preview,border=0pt,varwidth,12pt]{standalone}
\usepackage{${packages}}
\begin{document}
${header}
@ -162,6 +177,7 @@ Snacks.config.style("snacks_image", {
Snacks.util.set_hl({
Spinner = "Special",
Anchor = "Special",
Loading = "NonText",
Math = { fg = Snacks.util.color({ "@markup.math.latex", "Special", "Normal" }) },
}, { prefix = "SnacksImage", default = true })
@ -169,6 +185,7 @@ Snacks.util.set_hl({
---@class snacks.image.Opts
---@field pos? snacks.image.Pos (row, col) (1,0)-indexed. defaults to the top-left corner
---@field range? Range4
---@field conceal? boolean
---@field inline? boolean render the image inline in the buffer
---@field width? number
---@field min_width? number
@ -178,6 +195,8 @@ Snacks.util.set_hl({
---@field max_height? number
---@field on_update? fun(placement: snacks.image.Placement)
---@field on_update_pre? fun(placement: snacks.image.Placement)
---@field type? snacks.image.Type
---@field auto_resize? boolean
local did_setup = false
@ -220,6 +239,22 @@ function M.setup(ev)
did_setup = true
local group = vim.api.nvim_create_augroup("snacks.image", { clear = true })
vim.api.nvim_create_autocmd({ "BufWipeout", "BufDelete" }, {
group = group,
callback = function(e)
vim.schedule(function()
Snacks.image.placement.clean(e.buf)
end)
end,
})
vim.api.nvim_create_autocmd({ "ExitPre" }, {
group = group,
once = true,
callback = function()
Snacks.image.placement.clean()
end,
})
if M.config.formats and #M.config.formats > 0 then
vim.api.nvim_create_autocmd("BufReadCmd", {
pattern = "*." .. table.concat(M.config.formats, ",*."),