feat(buffer): track line changes via on_bytes to keep _meta aligned

Problem: `_meta` is a positional array keyed by line number. Line
insertions and deletions during editing desync it from actual buffer
content, breaking `get_fold()`, cursor-based task lookups, and extmark
re-application.

Solution: attach an `on_bytes` callback that adjusts `_meta` on line
insertions/deletions and tracks dirty rows. Remove the manual
`_meta` insert from `open_line()` since `on_bytes` now handles it.
Reset dirty rows on each full render.
This commit is contained in:
Barrett Ruth 2026-03-08 14:09:36 -04:00
parent a12e5b5763
commit db391c5715

View file

@ -26,6 +26,10 @@ local _initial_fold_loaded = false
local _filter_predicates = {}
---@type table<integer, true>
local _hidden_ids = {}
---@type table<integer, true>
local _dirty_rows = {}
---@type boolean
local _on_bytes_active = false
---@return pending.LineMeta[]
function M.meta()
@ -95,6 +99,48 @@ function M.clear_marks(b)
vim.api.nvim_buf_clear_namespace(bufnr, ns_inline, 0, -1)
end
---@return table<integer, true>
function M.dirty_rows()
return _dirty_rows
end
---@return nil
function M.clear_dirty_rows()
_dirty_rows = {}
end
---@param bufnr integer
---@return nil
function M.attach_bytes(bufnr)
if _on_bytes_active then
return
end
_on_bytes_active = true
vim.api.nvim_buf_attach(bufnr, false, {
on_bytes = function(_, buf, _, start_row, _, _, old_end_row, _, _, new_end_row, _, _)
if buf ~= task_bufnr then
_on_bytes_active = false
return true
end
local delta = new_end_row - old_end_row
if delta > 0 then
for _ = 1, delta do
table.insert(_meta, start_row + 2, { type = 'task' })
end
elseif delta < 0 then
for _ = 1, -delta do
if _meta[start_row + 2] then
table.remove(_meta, start_row + 2)
end
end
end
for r = start_row + 1, start_row + 1 + math.max(0, new_end_row) do
_dirty_rows[r] = true
end
end,
})
end
---@return nil
function M.persist_folds()
log.debug(
@ -208,7 +254,6 @@ function M.open_line(above)
local insert_row = above and (row - 1) or row
vim.bo[bufnr].modifiable = true
vim.api.nvim_buf_set_lines(bufnr, insert_row, insert_row, false, { '- [ ] ' })
table.insert(_meta, insert_row + 1, { type = 'task' })
vim.api.nvim_win_set_cursor(0, { insert_row + 1, 6 })
vim.cmd('startinsert!')
end
@ -452,6 +497,7 @@ function M.render(bufnr)
end
_meta = line_meta
_dirty_rows = {}
snapshot_folds(bufnr)
vim.bo[bufnr].modifiable = true
@ -511,6 +557,7 @@ function M.open()
if not (task_bufnr and vim.api.nvim_buf_is_valid(task_bufnr)) then
task_bufnr = vim.api.nvim_create_buf(true, false)
set_buf_options(task_bufnr)
M.attach_bytes(task_bufnr)
end
vim.cmd('botright new')