Skip to content

Commit

Permalink
feat: genesis
Browse files Browse the repository at this point in the history
  • Loading branch information
3rd committed Jun 23, 2023
0 parents commit 16d34a0
Show file tree
Hide file tree
Showing 4 changed files with 291 additions and 0 deletions.
153 changes: 153 additions & 0 deletions lua/render/init.lua
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,
}
45 changes: 45 additions & 0 deletions lua/render/integrations/markdown.lua
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,
}
78 changes: 78 additions & 0 deletions lua/render/renderers/ueberzug.lua
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,
}
15 changes: 15 additions & 0 deletions lua/render/utils.lua
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,
}

0 comments on commit 16d34a0

Please sign in to comment.