Skip to content

Commit

Permalink
feat: add initial implementation for kitty unicode placeholders and d…
Browse files Browse the repository at this point in the history
…ebugging
  • Loading branch information
3rd committed Jun 27, 2023
1 parent a28df8b commit e65ca7c
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 29 deletions.
53 changes: 40 additions & 13 deletions lua/image/backends/kitty/helpers.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ local utils = require("image/utils")
local codes = require("image/backends/kitty/codes")

local stdout = vim.loop.new_tty(1, false)
local is_tmux = vim.env.TMUX ~= nil

-- https://github.com/edluffy/hologram.nvim/blob/main/lua/hologram/terminal.lua#L77
local get_chunked = function(str)
Expand All @@ -13,10 +14,6 @@ local get_chunked = function(str)
return chunks
end

local write = vim.schedule_wrap(function(data)
stdout:write(data)
end)

-- https://github.com/edluffy/hologram.nvim/blob/main/lua/hologram/state.lua#L15
local get_term_size = function()
local ffi = require("ffi")
Expand Down Expand Up @@ -52,6 +49,27 @@ local get_term_size = function()
}
end

local encode = function(data)
if is_tmux then return "\x1bPtmux;" .. data:gsub("\x1b", "\x1b\x1b") .. "\x1b\\" end
return data
end

local write = vim.schedule_wrap(function(data)
if data == "" then return end
utils.debug("write:", vim.inspect(data))
stdout:write(data)
-- vim.fn.chansend(vim.v.stderr, data)
end)

local move_cursor = function(x, y, save)
if save then write("\x1b[s") end
write(("\x1b[" .. y .. ";" .. x .. "H"))
end

local restore_cursor = function()
write("\x1b[u")
end

---@param config KittyControlConfig
---@param data? string
-- https://github.com/edluffy/hologram.nvim/blob/main/lua/hologram/terminal.lua#L52
Expand All @@ -65,31 +83,39 @@ local write_graphics = function(config, data)
end
end
control_payload = control_payload:sub(0, -2)
log(control_payload)

if data then
if config.transmit_medium ~= codes.control.transmit_medium.direct then data = utils.base64.encode(data) end
local chunks = get_chunked(data)
for i = 1, #chunks do
write("\x1b_G" .. control_payload .. ";" .. chunks[i] .. "\x1b\\")
write(encode("\x1b_G" .. control_payload .. ";" .. chunks[i] .. "\x1b\\"))
if i == #chunks - 1 then
control_payload = "m=0"
else
control_payload = "m=1"
end
end
else
write("\x1b_G" .. control_payload .. "\x1b\\")
utils.debug("control:", control_payload)
write(encode("\x1b_G" .. control_payload .. "\x1b\\"))
end
end

local move_cursor = function(x, y)
write("\x1b[s")
write("\x1b[" .. y .. ":" .. x .. "H")
end
-- local rshift = function(x, by)
-- return math.floor(x / 2 ^ by)
-- end
local write_placeholder = function(image_id, x, y, rows, columns)
local foreground = "\x1b[38;5;" .. image_id .. "m"
local restore = "\x1b[39m"

local restore_cursor = function()
write("\x1b[u")
write(foreground)
for i = 0, rows - 1 do
move_cursor(x, y + i + 1)
for j = 0, columns - 1 do
write(codes.placeholder .. codes.diacritics[i + 1] .. codes.diacritics[j + 1])
end
end
write(restore)
end

return {
Expand All @@ -98,4 +124,5 @@ return {
restore_cursor = restore_cursor,
write = write,
write_graphics = write_graphics,
write_placeholder = write_placeholder,
}
79 changes: 64 additions & 15 deletions lua/image/backends/kitty/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,34 @@ local helpers = require("image/backends/kitty/helpers")

local term_size = helpers.get_term_size()

local images = {}
local last_kitty_id = 0

local is_tmux = vim.env.TMUX ~= nil
local tmux_has_passthrough = false

if is_tmux then
local ok, result = pcall(vim.fn.system, "tmux show -Apv allow-passthrough")
if ok and result == "on\n" then tmux_has_passthrough = true end
end

---@type Backend
local backend = {}

backend.setup = function()
-- TODO: check for kitty
backend.setup = function(options)
backend.options = options

if is_tmux and not tmux_has_passthrough then
utils.throw("tmux does not have allow-passthrough enabled")
return
end

vim.defer_fn(function()
-- log(get_term_size())
end, 1000)
end

local images = {}
local last_kitty_id = 0

-- extend from empty line strategy to use extmarks
backend.render = function(image_id, url, x, y, max_cols, max_rows)
if not images[image_id] then
Expand All @@ -24,25 +40,59 @@ backend.render = function(image_id, url, x, y, max_cols, max_rows)
end
local kitty_id = images[image_id]

local image_width, image_height = utils.png.get_dimensions(url)
local rows = math.floor(image_height / term_size.cell_height)
local columns = math.floor(image_width / term_size.cell_width)
-- local rows = max_rows
-- local pixel_height = math.floor(max_rows * term_size.cell_height)
-- local pixel_width = math.floor(image_width * pixel_height / image_height)
-- local columns = math.floor(pixel_width / term_size.cell_width)
-- log({
-- image_width = image_width,
-- image_height = image_height,
-- columns = columns,
-- rows = rows,
-- })

-- if true then
-- helpers.move_cursor(10, 10)
-- return
-- end

helpers.move_cursor(x, y, true)

-- transmit image
helpers.write_graphics({
action = codes.control.action.transmit,
image_id = kitty_id,
transmit_format = codes.control.transmit_format.png,
transmit_medium = codes.control.transmit_medium.file,
display_cursor_policy = codes.control.display_cursor_policy.do_not_move,
display_virtual_placeholder = is_tmux and 1 or 0,
quiet = 2,
image_id = kitty_id,
placement_id = 1,
}, url)

helpers.move_cursor(x + 1, y + 1)

local rows = max_rows
local image_width, image_height = utils.png.get_dimensions(url)
local pixel_height = math.floor(max_rows * term_size.cell_height)
local pixel_width = math.floor(image_width * pixel_height / image_height)
local columns = math.floor(pixel_width / term_size.cell_width)
-- unicode placeholders
if is_tmux then
-- create virtual image placement
helpers.write_graphics({
action = codes.control.action.display,
quiet = 2,
image_id = kitty_id,
display_rows = rows,
display_columns = columns,
display_cursor_policy = codes.control.display_cursor_policy.do_not_move,
display_virtual_placeholder = 1,
})

log({ image_width, image_height, rows, columns })
-- write placeholder
helpers.write_placeholder(kitty_id, x, y, rows, columns)
helpers.restore_cursor()
return
end

-- default display
helpers.move_cursor(x + 1, y + 1)
helpers.write_graphics({
action = codes.control.action.display,
quiet = 2,
Expand All @@ -53,7 +103,6 @@ backend.render = function(image_id, url, x, y, max_cols, max_rows)
display_zindex = -1,
display_cursor_policy = codes.control.display_cursor_policy.do_not_move,
})

helpers.restore_cursor()
end

Expand Down
2 changes: 1 addition & 1 deletion lua/image/integrations/markdown.lua
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ end

---@type fun(ctx: IntegrationContext)
local setup = function(ctx)
-- local options = ctx.options --[[@as MarkdownIntegrationOptions]]
local options = ctx.options --[[@as MarkdownIntegrationOptions]]
setup_autocommands(ctx)
render(ctx)
end
Expand Down
8 changes: 8 additions & 0 deletions lua/image/utils/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,17 @@ local throw = logger.create_logger({
output_file = "/tmp/nvim-image.txt",
})

local debug = logger.create_logger({
prefix = "[image.nvim]",
formatter = logger.default_log_formatter,
handler = nil,
output_file = "/tmp/nvim-image.txt",
})

return {
log = log,
throw = throw,
debug = debug,
base64 = base64,
png = png,
random = random,
Expand Down

0 comments on commit e65ca7c

Please sign in to comment.