-
Notifications
You must be signed in to change notification settings - Fork 54
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 16d34a0
Showing
4 changed files
with
291 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
local utils = require("render/utils") | ||
|
||
local default_options = { | ||
renderer = "ueberzug", | ||
integrations = { | ||
markdown = { | ||
enabled = true, | ||
}, | ||
}, | ||
margin = { | ||
top = 0, | ||
right = 1, | ||
bottom = 1, | ||
left = 0, | ||
}, | ||
} | ||
|
||
local state = { | ||
options = default_options, | ||
integrations = {}, | ||
renderer = nil, | ||
} | ||
|
||
---@class markdown_integration_options | ||
---@field enabled boolean | ||
---@class integrations | ||
---@field markdown markdown_integration_options | ||
---@class options | ||
---@field renderer "kitty"|"ueberzug" | ||
---@field integrations integrations | ||
|
||
local get_windows = function() | ||
local windows = {} | ||
for _, window in ipairs(vim.api.nvim_tabpage_list_wins(0)) do | ||
local buffer = vim.api.nvim_win_get_buf(window) | ||
local width = vim.api.nvim_win_get_width(window) | ||
local height = vim.api.nvim_win_get_height(window) | ||
local pos = vim.api.nvim_win_get_position(window) | ||
table.insert(windows, { | ||
winnr = window, | ||
bufnr = buffer, | ||
width = width, | ||
height = height, | ||
x = pos[2], | ||
y = pos[1], | ||
}) | ||
end | ||
return windows | ||
end | ||
|
||
local render = function() | ||
local renderer = state.renderer | ||
local windows = get_windows() | ||
|
||
-- renderer.clear() | ||
|
||
local x_offset = state.options.margin.left | ||
if vim.opt.number then | ||
local width = vim.opt.numberwidth:get() | ||
x_offset = x_offset + width | ||
end | ||
if vim.opt.signcolumn ~= "no" then x_offset = x_offset + 2 end | ||
|
||
local y_offset = state.options.margin.top | ||
if vim.opt.showtabline == 2 then y_offset = y_offset + 1 end | ||
|
||
for _, window in ipairs(windows) do | ||
for _, integration in ipairs(state.integrations) do | ||
if integration.validate(window.bufnr) then | ||
local images = integration.get_buffer_images(window.bufnr) | ||
for _, image in ipairs(images) do | ||
-- log("render", image) | ||
local id = utils.nanoid() | ||
local width = vim.fn.min({ | ||
image.width or 100, | ||
window.width - state.options.margin.left - state.options.margin.right - x_offset, | ||
}) | ||
local height = vim.fn.min({ image.height or 100, window.height - state.options.margin.bottom - y_offset }) | ||
log(width) | ||
local x = window.x + image.range.start_col + x_offset | ||
local y = window.y + image.range.start_row + 2 + y_offset | ||
renderer.render(id, image.url, x, y, width, height) | ||
end | ||
end | ||
end | ||
end | ||
end | ||
|
||
local clear = function() | ||
state.renderer.clear() | ||
end | ||
|
||
local setup_autocommands = function() | ||
local events = { | ||
"BufEnter", | ||
"BufLeave", | ||
"BufWritePost", | ||
"TextChanged", | ||
"WinScrolled", | ||
"WinResized", | ||
"InsertEnter", | ||
"InsertLeave", | ||
} | ||
local group = vim.api.nvim_create_augroup("render", { clear = true }) | ||
|
||
vim.api.nvim_create_autocmd(events, { | ||
group = group, | ||
callback = function(args) | ||
if args.event == "InsertEnter" then | ||
clear() | ||
else | ||
render() | ||
end | ||
end, | ||
}) | ||
end | ||
|
||
---@param options options | ||
local setup = function(options) | ||
local opts = vim.tbl_deep_extend("force", default_options, options or {}) | ||
|
||
local ok, renderer = pcall(require, "render/renderers/" .. opts.renderer) | ||
if not ok then | ||
vim.api.nvim_err_writeln("render: failed to load " .. opts.renderer .. " renderer") | ||
return | ||
end | ||
|
||
local integrations = {} | ||
for name, integration in pairs(opts.integrations) do | ||
if integration.enabled then | ||
---@diagnostic disable-next-line: redefined-local | ||
local ok, integration_module = pcall(require, "render.integrations." .. name) | ||
if ok then | ||
table.insert(integrations, integration_module) | ||
else | ||
vim.api.nvim_err_writeln("render: failed to load" .. name .. " integration") | ||
end | ||
end | ||
end | ||
|
||
state = { | ||
options = opts, | ||
integrations = integrations, | ||
renderer = renderer, | ||
} | ||
|
||
setup_autocommands() | ||
end | ||
|
||
return { | ||
setup = setup, | ||
render = render, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
---@class Image | ||
---@field node any | ||
---@field range {start_row: number, start_col: number, end_row: number, end_col: number} | ||
---@field url string | ||
---@field width? number | ||
---@field height? number | ||
|
||
---@param buffer? number | ||
---@return Image[] | ||
local get_buffer_images = function(buffer) | ||
local bufnr = buffer or vim.api.nvim_get_current_buf() | ||
|
||
local parser = vim.treesitter.get_parser(bufnr, "markdown_inline") | ||
local root = parser:parse()[1]:root() | ||
local query = vim.treesitter.query.parse("markdown_inline", "(image (link_destination) @url) @image") | ||
|
||
local images = {} | ||
local current_image = nil | ||
|
||
for id, node in query:iter_captures(root, 0) do | ||
local key = query.captures[id] | ||
local value = vim.treesitter.get_node_text(node, bufnr) | ||
|
||
if key == "image" then | ||
local start_row, start_col, end_row, end_col = node:range() | ||
current_image = { | ||
node = node, | ||
range = { start_row = start_row, start_col = start_col, end_row = end_row, end_col = end_col }, | ||
} | ||
elseif key == "url" then | ||
current_image.url = value | ||
table.insert(images, current_image) | ||
current_image = nil | ||
end | ||
end | ||
|
||
return images | ||
end | ||
|
||
return { | ||
validate = function(bufnr) | ||
return vim.api.nvim_buf_get_option(bufnr, "filetype") == "markdown" | ||
end, | ||
get_buffer_images = get_buffer_images, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
local child = nil | ||
|
||
local spawn = function() | ||
local stdin = vim.loop.new_pipe() | ||
local stdout = vim.loop.new_pipe() | ||
local stderr = vim.loop.new_pipe() | ||
|
||
-- log("spawn") | ||
|
||
local handle, pid = vim.loop.spawn("ueberzug", { | ||
args = { "layer", "--silent" }, | ||
stdio = { stdin, stdout, stderr }, | ||
}, function(code, signal) | ||
child = nil | ||
print("code", code, "signal", signal) | ||
end) | ||
|
||
vim.loop.read_start(stdout, function(err, data) | ||
assert(not err, err) | ||
if data then log("stdout", data) end | ||
end) | ||
|
||
vim.loop.read_start(stderr, function(err, data) | ||
assert(not err, err) | ||
if data then log("stderr", data) end | ||
end) | ||
|
||
local write = function(data) | ||
local serialized = vim.fn.json_encode(data) | ||
-- log("write", serialized) | ||
vim.loop.write(stdin, serialized .. "\n") | ||
end | ||
|
||
local shutdown = function() | ||
vim.loop.shutdown(handle, function() | ||
-- log("shutdown") | ||
vim.loop.close(handle, function() | ||
-- log("close") | ||
end) | ||
end) | ||
end | ||
|
||
child = { | ||
handle = handle, | ||
pid = pid, | ||
stdin = stdin, | ||
stdout = stdout, | ||
stderr = stderr, | ||
write = write, | ||
shutdown = shutdown, | ||
} | ||
end | ||
|
||
local clear = function() | ||
if not child then spawn() end | ||
child.write({ | ||
action = "remove", | ||
identifier = "all", | ||
}) | ||
end | ||
|
||
local render = function(id, url, x, y, width, height) | ||
if not child then spawn() end | ||
child.write({ | ||
action = "add", | ||
identifier = id, | ||
path = url, | ||
x = x, | ||
y = y, | ||
max_width = width, | ||
max_height = height, | ||
}) | ||
end | ||
|
||
return { | ||
clear = clear, | ||
render = render, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
-- nanoid - https://gist.github.com/jrus/3197011 | ||
math.randomseed(tonumber(tostring(os.time()):reverse():sub(1, 9))) | ||
local nanoid = function() | ||
local template = "xxxxxxxx" | ||
return ( | ||
string.gsub(template, "[x]", function(c) | ||
local v = (c == "x") and math.random(0, 0xf) or math.random(8, 0xb) | ||
return string.format("%x", v) | ||
end) | ||
) | ||
end | ||
|
||
return { | ||
nanoid = nanoid, | ||
} |