From 20ff82d9fcdeafe2cdf4824b8af1bdeeffb513b6 Mon Sep 17 00:00:00 2001 From: Milad Rashidikhah Date: Tue, 28 Jul 2026 08:25:32 +0000 Subject: [PATCH] fix(treesitter): clamp unbounded fold ranges Problem: Tree-sitter uses UINT32_MAX for full-document ranges, which becomes -1 on 32-bit platforms and reaches _foldupdate as an invalid end row. Solution: Treat negative changed-range end rows as unbounded and clamp them to the buffer line count. Add a regression test that simulates the 32-bit sentinel. AI-assisted: Codex --- runtime/lua/vim/treesitter/_fold.lua | 5 ++-- test/functional/treesitter/fold_spec.lua | 38 ++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/runtime/lua/vim/treesitter/_fold.lua b/runtime/lua/vim/treesitter/_fold.lua index 99739753e8..2d5f3da1ba 100644 --- a/runtime/lua/vim/treesitter/_fold.lua +++ b/runtime/lua/vim/treesitter/_fold.lua @@ -291,9 +291,10 @@ local function on_changedtree(bufnr, tree_changes) local srow, _, erow, ecol = Range.unpack4(change) -- If a parser doesn't have any ranges explicitly set, treesitter will -- return a range with end_row and end_bytes with a value of UINT32_MAX, - -- so clip end_row to the max buffer line. + -- which is represented as -1 on 32-bit platforms, so clip end_row to + -- the max buffer line. -- TODO(lewis6991): Handle this generally - if erow > max_erow then + if erow > max_erow or erow < 0 then erow = max_erow elseif ecol > 0 then erow = erow + 1 diff --git a/test/functional/treesitter/fold_spec.lua b/test/functional/treesitter/fold_spec.lua index 5cd028b3ac..c61bc851d3 100644 --- a/test/functional/treesitter/fold_spec.lua +++ b/test/functional/treesitter/fold_spec.lua @@ -824,4 +824,42 @@ t2]]) | ]]) end) + + it('clamps unbounded changed ranges on 32-bit platforms', function() + insert([[ +one +two]]) + + exec_lua(function() + local parser = {} + + function parser:parse(_, callback) + if callback then + callback(nil, {}) + end + end + + function parser:for_each_tree() end + + function parser:register_cbs(callbacks) + self.callbacks = callbacks + end + + vim.treesitter.get_parser = function() + return parser + end + + vim.treesitter.foldexpr() + vim.wo.foldmethod = 'expr' + vim._foldupdate = function(_, first, last) + _G.foldupdate_range = { first, last } + end + + -- UINT32_MAX is represented as -1 by Lua integers on 32-bit platforms. + parser.callbacks.on_changedtree({ { 0, 0, -1, -1 } }) + end) + poke_eventloop() + + eq({ 0, 2 }, exec_lua('return _G.foldupdate_range')) + end) end)