Problem
When an image is rendered with both with_virtual_padding = true and render_offset_top > 0, the virtual padding reservation does not include the offset. The image visually shifts down by render_offset_top cells (per lua/image/renderer.lua:200), but lua/image/image.lua:125 reserves only height virtual lines instead of height + render_offset_top. The image therefore overlaps the next render_offset_top lines of real buffer content immediately below the reserved area.
This is most visible with diagram.nvim, which always sets render_offset_top = 1 — every rendered diagram covers the first line of buffer content following the diagram block.
Steps to reproduce
/tmp/repro-init.lua:
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not vim.uv.fs_stat(lazypath) then
vim.fn.system({ "git", "clone", "--filter=blob:none",
"https://github.com/folke/lazy.nvim.git", "--branch=stable", lazypath })
end
vim.opt.rtp:prepend(lazypath)
require("lazy").setup({
{ "3rd/image.nvim",
dependencies = { "nvim-lua/plenary.nvim" },
opts = {
backend = "kitty",
kitty_method = "normal",
max_width_window_percentage = 100,
max_height_window_percentage = 100,
},
},
})
vim.defer_fn(function()
local image = require("image")
local img = image.from_file(vim.env.IMG_PATH, {
buffer = vim.api.nvim_get_current_buf(),
window = vim.api.nvim_get_current_win(),
with_virtual_padding = true,
inline = true,
x = 0,
y = 0,
render_offset_top = 1,
})
img:render()
end, 200)
/tmp/repro.txt:
ANCHOR (image renders here, render_offset_top = 1)
THIS LINE IS OVERLAPPED BY THE BOTTOM ROW OF THE IMAGE
Run in a Kitty-graphics-capable terminal (Kitty, Ghostty, etc.):
IMG_PATH=/path/to/any.png nvim --clean -u /tmp/repro-init.lua /tmp/repro.txt
The second line is partially or fully covered by the bottom row of the image. With render_offset_top = 0 (or by patching total_lines per the suggested fix below), it stays visible.
Expected behavior
When render_offset_top = N, the reservation should cover image_rows + N lines so the rendered image (occupying extmark_row + N + 1 through extmark_row + N + image_rows) fits inside the reserved area without overlapping subsequent buffer lines.
Suggested fix
lua/image/image.lua:111 already computes total_height = height + (self.render_offset_top or 0) for the cache-invalidation key. Use the same value for the actual reservation on line 125:
if self.with_virtual_padding then
-- only reserve real height for the extmark, padding is applied during rendering
- local total_lines = height
+ local total_lines = total_height
for _ = 0, total_lines - 1 do
filler[#filler + 1] = { { " ", "" } }
end
extmark_opts.virt_lines = filler
end
Environment
- image.nvim: v1.5.1 (commit
da2be65)
- Surfaced via diagram.nvim's mermaid integration on Ghostty
Problem
When an image is rendered with both
with_virtual_padding = trueandrender_offset_top > 0, the virtual padding reservation does not include the offset. The image visually shifts down byrender_offset_topcells (perlua/image/renderer.lua:200), butlua/image/image.lua:125reserves onlyheightvirtual lines instead ofheight + render_offset_top. The image therefore overlaps the nextrender_offset_toplines of real buffer content immediately below the reserved area.This is most visible with
diagram.nvim, which always setsrender_offset_top = 1— every rendered diagram covers the first line of buffer content following the diagram block.Steps to reproduce
/tmp/repro-init.lua:/tmp/repro.txt:Run in a Kitty-graphics-capable terminal (Kitty, Ghostty, etc.):
The second line is partially or fully covered by the bottom row of the image. With
render_offset_top = 0(or by patchingtotal_linesper the suggested fix below), it stays visible.Expected behavior
When
render_offset_top = N, the reservation should coverimage_rows + Nlines so the rendered image (occupyingextmark_row + N + 1throughextmark_row + N + image_rows) fits inside the reserved area without overlapping subsequent buffer lines.Suggested fix
lua/image/image.lua:111already computestotal_height = height + (self.render_offset_top or 0)for the cache-invalidation key. Use the same value for the actual reservation on line 125:Environment
da2be65)