Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions lua/md-render/preview.lua
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,18 @@ local function usable_win_width(win)
return math.max(1, total - textoff)
end

--- The `max_width` to render at for a window. ContentBuilder wraps the text
--- at `max_width` and then prepends `indent`, so the indent has to come off
--- the window width; otherwise lines run up to its width past the edge and
--- 'wrap' folds their last characters onto a row of their own.
---@param win integer
---@param indent? string defaults to ContentBuilder's `" "`
---@return integer
local function content_width(win, indent)
local indent_w = vim.api.nvim_strwidth(indent or " ")
return math.max(1, math.min(usable_win_width(win) - indent_w, DEFAULT_MAX_WIDTH))
end

--- Parse simple YAML frontmatter lines into key-value pairs
---@param fm_lines string[]
---@return {key: string, value: string}[]
Expand Down Expand Up @@ -503,7 +515,7 @@ end
function Session:bind_window(win)
self.win = win
if not self._explicit_max_width then
local win_width = math.min(usable_win_width(win), DEFAULT_MAX_WIDTH)
local win_width = content_width(win, self.opts.indent)
if win_width ~= (self.opts.max_width or DEFAULT_MAX_WIDTH) then
self.opts.max_width = win_width
self:rebuild()
Expand Down Expand Up @@ -1752,7 +1764,7 @@ local function install_win_resize_handler(session)

local win = render_wins[1]
if not vim.api.nvim_win_is_valid(win) then return end
local win_width = math.min(usable_win_width(win), DEFAULT_MAX_WIDTH)
local win_width = content_width(win, session.opts.indent)
if win_width == (session.opts.max_width or DEFAULT_MAX_WIDTH) then return end

session.opts.max_width = win_width
Expand Down
97 changes: 97 additions & 0 deletions tests/content_width_test.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
-- Test that rendered lines fit the window they are shown in. ContentBuilder
-- wraps at `max_width` and prepends the indent afterwards, so a window-sized
-- `max_width` let lines run past the edge by the indent's width, and 'wrap'
-- put their last characters on a screen row of their own at column 0.
-- Run: nvim --headless -u NONE --noplugin -l tests/content_width_test.lua

package.path = vim.fn.getcwd() .. "/lua/?.lua;" .. vim.fn.getcwd() .. "/lua/?/init.lua;" .. package.path

local preview = require "md-render.preview"

local pass_count = 0
local fail_count = 0

local function assert_eq(actual, expected, msg)
if vim.deep_equal(actual, expected) then
pass_count = pass_count + 1
else
fail_count = fail_count + 1
print("FAIL: " .. msg)
print(" expected: " .. vim.inspect(expected))
print(" actual: " .. vim.inspect(actual))
end
end

local function test(name, fn)
local ok, err = pcall(fn)
if not ok then
fail_count = fail_count + 1
print("ERROR: " .. name .. ": " .. tostring(err))
end
end

-- Mixed CJK and ASCII so that the wrap points land at odd and even columns.
local DOC = {
"# Title",
"",
"- [x] " .. string.rep("あいうえお abc ", 8),
" - " .. string.rep("かきくけこ `x = 1` ", 8),
"",
string.rep("さしすせそ def ", 10),
}

local function setup_md_buffer()
vim.cmd "silent! only"
local buf = vim.api.nvim_create_buf(false, false)
vim.bo[buf].filetype = "markdown"
vim.api.nvim_buf_set_name(buf, "/tmp/md-render-content-width-test-" .. buf .. ".md")
vim.api.nvim_buf_set_lines(buf, 0, -1, false, DOC)
vim.api.nvim_win_set_buf(0, buf)
return buf
end

--- Lines of the window's buffer wider than its text area.
local function overflowing(win)
local width = vim.api.nvim_win_get_width(win) - vim.fn.getwininfo(win)[1].textoff
local out = {}
for _, line in ipairs(vim.api.nvim_buf_get_lines(vim.api.nvim_win_get_buf(win), 0, -1, false)) do
if vim.api.nvim_strwidth(line) > width then table.insert(out, line) end
end
return out
end

local function render_win()
for _, w in ipairs(vim.api.nvim_tabpage_list_wins(0)) do
if vim.bo[vim.api.nvim_win_get_buf(w)].filetype == "md-render" then return w end
end
end

for _, columns in ipairs { 40, 51, 60, 73 } do
test("toggle at " .. columns .. " columns", function()
vim.o.columns = columns
local source = setup_md_buffer()
preview.toggle()
assert_eq(overflowing(vim.api.nvim_get_current_win()), {}, "toggle lines fit at " .. columns .. " columns")
preview.toggle()
pcall(vim.api.nvim_buf_delete, source, { force = true })
end)

test("split at " .. columns .. " columns", function()
vim.o.columns = columns
local source = setup_md_buffer()
preview.split { mods = { vertical = false } }
assert_eq(overflowing(render_win()), {}, "split lines fit at " .. columns .. " columns")
pcall(vim.api.nvim_buf_delete, source, { force = true })
end)

test("pager at " .. columns .. " columns", function()
vim.o.columns = columns
local source = setup_md_buffer()
preview.show_pager()
assert_eq(overflowing(render_win()), {}, "pager lines fit at " .. columns .. " columns")
pcall(vim.api.nvim_buf_delete, source, { force = true })
end)
end

print(string.format("content_width_test: %d passed, %d failed", pass_count, fail_count))
if fail_count > 0 then os.exit(1) end
Loading