fix(treesitter): restore highlighting on 32 bit systems #39091

Problem: Treesitter highlighting regressed on 32-bit builds because ranges that should cover the whole buffer were corrupted when passed into Lua.

Solution: Round-trip those range values through Lua and validate them so treesitter sees the same ranges on 32 and 64-bit builds.
(cherry picked from commit 3838a2579e)
This commit is contained in:
Barrett Ruth
2026-04-16 03:59:20 -04:00
committed by github-actions[bot]
parent efc136850d
commit 2ea9ed32e4
2 changed files with 39 additions and 12 deletions
+22 -12
View File
@@ -476,20 +476,20 @@ static void push_ranges(lua_State *L, const TSRange *ranges, const size_t length
for (size_t i = 0; i < length; i++) {
lua_createtable(L, include_bytes ? 6 : 4, 0);
int j = 1;
lua_pushinteger(L, ranges[i].start_point.row);
lua_pushnumber(L, (lua_Number)ranges[i].start_point.row);
lua_rawseti(L, -2, j++);
lua_pushinteger(L, ranges[i].start_point.column);
lua_pushnumber(L, (lua_Number)ranges[i].start_point.column);
lua_rawseti(L, -2, j++);
if (include_bytes) {
lua_pushinteger(L, ranges[i].start_byte);
lua_pushnumber(L, (lua_Number)ranges[i].start_byte);
lua_rawseti(L, -2, j++);
}
lua_pushinteger(L, ranges[i].end_point.row);
lua_pushnumber(L, (lua_Number)ranges[i].end_point.row);
lua_rawseti(L, -2, j++);
lua_pushinteger(L, ranges[i].end_point.column);
lua_pushnumber(L, (lua_Number)ranges[i].end_point.column);
lua_rawseti(L, -2, j++);
if (include_bytes) {
lua_pushinteger(L, ranges[i].end_byte);
lua_pushnumber(L, (lua_Number)ranges[i].end_byte);
lua_rawseti(L, -2, j++);
}
@@ -596,6 +596,16 @@ static void range_err(lua_State *L)
luaL_error(L, "Ranges can only be made from 6 element long tables or nodes.");
}
static uint32_t lua_checkuint32(lua_State *L, int index)
{
lua_Number value = luaL_checknumber(L, index);
uint32_t converted = (uint32_t)value;
if (value < 0 || value > (lua_Number)UINT32_MAX || (lua_Number)converted != value) {
luaL_error(L, "Range value out of bounds");
}
return converted;
}
// Use the top of the stack (without popping it) to create a TSRange, it can be
// either a lua table or a TSNode
static void range_from_lua(lua_State *L, TSRange *range)
@@ -609,27 +619,27 @@ static void range_from_lua(lua_State *L, TSRange *range)
}
lua_rawgeti(L, -1, 1); // [ range, start_row]
uint32_t start_row = (uint32_t)luaL_checkinteger(L, -1);
uint32_t start_row = lua_checkuint32(L, -1);
lua_pop(L, 1);
lua_rawgeti(L, -1, 2); // [ range, start_col]
uint32_t start_col = (uint32_t)luaL_checkinteger(L, -1);
uint32_t start_col = lua_checkuint32(L, -1);
lua_pop(L, 1);
lua_rawgeti(L, -1, 3); // [ range, start_byte]
uint32_t start_byte = (uint32_t)luaL_checkinteger(L, -1);
uint32_t start_byte = lua_checkuint32(L, -1);
lua_pop(L, 1);
lua_rawgeti(L, -1, 4); // [ range, end_row]
uint32_t end_row = (uint32_t)luaL_checkinteger(L, -1);
uint32_t end_row = lua_checkuint32(L, -1);
lua_pop(L, 1);
lua_rawgeti(L, -1, 5); // [ range, end_col]
uint32_t end_col = (uint32_t)luaL_checkinteger(L, -1);
uint32_t end_col = lua_checkuint32(L, -1);
lua_pop(L, 1);
lua_rawgeti(L, -1, 6); // [ range, end_byte]
uint32_t end_byte = (uint32_t)luaL_checkinteger(L, -1);
uint32_t end_byte = lua_checkuint32(L, -1);
lua_pop(L, 1); // [ range ]
*range = (TSRange) {
@@ -90,6 +90,23 @@ describe('treesitter parser API', function()
eq(true, exec_lua('return parser:parse()[1] == tree2'))
end)
it('preserves full-document included ranges', function()
insert([[
local _ = 1
]])
eq(
{
{ 0, 0, 0, 4294967295, 4294967295, 4294967295 },
},
exec_lua(function()
local parser = vim.treesitter.get_parser(0, 'lua')
local tree = parser:parse()[1]
return tree:included_ranges(true)
end)
)
end)
it('respects eol settings when parsing buffer', function()
insert([[
int main() {