neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

commit 976a47e81be085df9aac497be0f40a7f6a4d0345
parent 981ea41abbecf6bc7a32062986ebcad13959d895
Author: Riley Bruins <ribru17@hotmail.com>
Date:   Sun, 14 Dec 2025 23:09:36 -0800

Revert "refactor(treesitter): use scratch buffer for string parser" #36964

This reverts commit 2a7cb32959b4c616bd2c76ae1933f8e068e391ad.
Diffstat:
Mruntime/doc/treesitter.txt | 4++--
Mruntime/lua/vim/treesitter/_meta/misc.lua | 2+-
Mruntime/lua/vim/treesitter/languagetree.lua | 56+++++++++++++++++++++-----------------------------------
Msrc/nvim/lua/treesitter.c | 64++++++++++++++++++++++++++++++++++++++++------------------------
Mtest/functional/treesitter/parser_spec.lua | 6------
5 files changed, 64 insertions(+), 68 deletions(-)

diff --git a/runtime/doc/treesitter.txt b/runtime/doc/treesitter.txt @@ -1471,10 +1471,10 @@ LanguageTree:register_cbs({cbs}, {recursive}) callbacks. LanguageTree:source() *LanguageTree:source()* - Returns the source bufnr of the language tree. + Returns the source content of the language tree (bufnr or string). Return: ~ - (`integer`) + (`integer|string`) *LanguageTree:tree_for_range()* LanguageTree:tree_for_range({range}, {opts}) diff --git a/runtime/lua/vim/treesitter/_meta/misc.lua b/runtime/lua/vim/treesitter/_meta/misc.lua @@ -5,7 +5,7 @@ error('Cannot require a meta file') ---@alias TSLoggerCallback fun(logtype: 'parse'|'lex', msg: string) ---@class TSParser: userdata ----@field parse fun(self: TSParser, tree: TSTree?, source: integer, include_bytes: boolean, timeout_ns: integer?): TSTree?, (Range4|Range6)[] +---@field parse fun(self: TSParser, tree: TSTree?, source: integer|string, include_bytes: boolean, timeout_ns: integer?): TSTree?, (Range4|Range6)[] ---@field reset fun(self: TSParser) ---@field included_ranges fun(self: TSParser, include_bytes: boolean?): integer[] ---@field set_included_ranges fun(self: TSParser, ranges: (Range6|TSNode)[]) diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua @@ -101,8 +101,7 @@ local TSCallbackNames = { ---taken from _trees. This is mostly a short-lived cache for included_regions() ---@field private _lang string Language name ---@field private _parent? vim.treesitter.LanguageTree Parent LanguageTree ----@field private _source integer Buffer to parse ----@field private _has_scratch_buf boolean Whether _source is a |scratch-buffer| for string parsing. +---@field private _source (integer|string) Buffer or string to parse ---@field private _trees table<integer, TSTree> Reference to parsed tree (one for each language). ---Each key is the index of region, which is synced with _regions and _valid. ---@field private _valid_regions table<integer,true> Set of valid region IDs. @@ -137,26 +136,11 @@ function LanguageTree.new(source, lang, opts) source = vim.api.nvim_get_current_buf() end - local has_scratch_buf = false - - if type(source) == 'string' then - local new_source = vim.api.nvim_create_buf(false, true) - if new_source == 0 then - error('Unable to create buffer for string parser') - end - vim.bo[new_source].fixeol = false - vim.bo[new_source].eol = false - vim.api.nvim_buf_set_lines(new_source, 0, -1, false, vim.split(source, '\n', { plain = true })) - source = new_source - has_scratch_buf = true - end - local injections = opts.injections or {} --- @class vim.treesitter.LanguageTree local self = { _source = source, - _has_scratch_buf = has_scratch_buf, _lang = lang, _children = {}, _trees = {}, @@ -192,7 +176,8 @@ end --- @private function LanguageTree:_set_logger() - local source = tostring(self:source()) + local source = self:source() + source = type(source) == 'string' and 'text' or tostring(source) local lang = self:lang() @@ -416,8 +401,8 @@ function LanguageTree:children() return self._children end ---- Returns the source bufnr of the language tree. ---- @return integer +--- Returns the source content of the language tree (bufnr or string). +--- @return integer|string function LanguageTree:source() return self._source end @@ -580,8 +565,9 @@ function LanguageTree:_async_parse(range, on_parse) end local source = self._source - local buf = vim.b[source] - local ct = buf.changedtick + local is_buffer_parser = type(source) == 'number' + local buf = is_buffer_parser and vim.b[source] or nil + local ct = is_buffer_parser and buf.changedtick or nil local total_parse_time = 0 local redrawtime = vim.o.redrawtime * 1000000 @@ -591,15 +577,19 @@ function LanguageTree:_async_parse(range, on_parse) local parse = coroutine.wrap(self._parse) local function step() - if not vim.api.nvim_buf_is_valid(source) then - return nil - end + if is_buffer_parser then + if + not vim.api.nvim_buf_is_valid(source --[[@as number]]) + then + return nil + end - -- If buffer was changed in the middle of parsing, reset parse state - if buf.changedtick ~= ct then - ct = buf.changedtick - total_parse_time = 0 - parse = coroutine.wrap(self._parse) + -- If buffer was changed in the middle of parsing, reset parse state + if buf.changedtick ~= ct then + ct = buf.changedtick + total_parse_time = 0 + parse = coroutine.wrap(self._parse) + end end thread_state.timeout = not vim.g._ts_force_sync_parsing and default_parse_timeout_ns or nil @@ -786,10 +776,6 @@ end --- `remove_child` must be called on the parent to remove it. function LanguageTree:destroy() -- Cleanup here - if self._has_scratch_buf then - self._has_scratch_buf = false - vim.api.nvim_buf_delete(self._source, {}) - end for _, child in pairs(self._children) do child:destroy() end @@ -907,7 +893,7 @@ function LanguageTree:included_regions() end ---@param node TSNode ----@param source integer +---@param source string|integer ---@param metadata vim.treesitter.query.TSMetadata ---@param include_children boolean ---@return Range6[] diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c @@ -513,34 +513,50 @@ static int parser_parse(lua_State *L) old_tree = ud ? ud->tree : NULL; } - if (lua_type(L, 3) != LUA_TNUMBER) { - return luaL_argerror(L, 3, "expected buffer handle"); - } - - handle_T bufnr = (handle_T)lua_tointeger(L, 3); - buf_T *buf = handle_get_buffer(bufnr); - - if (!buf) { + TSTree *new_tree = NULL; + size_t len; + const char *str; + handle_T bufnr; + buf_T *buf; + TSInput input; + + // This switch is necessary because of the behavior of lua_isstring, that + // consider numbers as strings... + switch (lua_type(L, 3)) { + case LUA_TSTRING: + str = lua_tolstring(L, 3, &len); + new_tree = ts_parser_parse_string(p, old_tree, str, (uint32_t)len); + break; + + case LUA_TNUMBER: + bufnr = (handle_T)lua_tointeger(L, 3); + buf = handle_get_buffer(bufnr); + + if (!buf) { #define BUFSIZE 256 - char ebuf[BUFSIZE] = { 0 }; - vim_snprintf(ebuf, BUFSIZE, "invalid buffer handle: %d", bufnr); - return luaL_argerror(L, 3, ebuf); + char ebuf[BUFSIZE] = { 0 }; + vim_snprintf(ebuf, BUFSIZE, "invalid buffer handle: %d", bufnr); + return luaL_argerror(L, 3, ebuf); #undef BUFSIZE - } + } - TSInput input = (TSInput){ (void *)buf, input_cb, TSInputEncodingUTF8, NULL }; - TSTree *new_tree = NULL; + input = (TSInput){ (void *)buf, input_cb, TSInputEncodingUTF8, NULL }; + if (!lua_isnil(L, 5)) { + uint64_t timeout_ns = (uint64_t)lua_tointeger(L, 5); + TSLuaParserCallbackPayload payload = + (TSLuaParserCallbackPayload){ .parse_start_time = os_hrtime(), + .timeout_threshold_ns = timeout_ns }; + TSParseOptions parse_options = { .payload = &payload, + .progress_callback = on_parser_progress }; + new_tree = ts_parser_parse_with_options(p, old_tree, input, parse_options); + } else { + new_tree = ts_parser_parse(p, old_tree, input); + } - if (!lua_isnil(L, 5)) { - uint64_t timeout_ns = (uint64_t)lua_tointeger(L, 5); - TSLuaParserCallbackPayload payload = - (TSLuaParserCallbackPayload){ .parse_start_time = os_hrtime(), - .timeout_threshold_ns = timeout_ns }; - TSParseOptions parse_options = { .payload = &payload, - .progress_callback = on_parser_progress }; - new_tree = ts_parser_parse_with_options(p, old_tree, input, parse_options); - } else { - new_tree = ts_parser_parse(p, old_tree, input); + break; + + default: + return luaL_argerror(L, 3, "expected either string or buffer handle"); } bool include_bytes = (lua_gettop(L) >= 4) && lua_toboolean(L, 4); diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua @@ -1264,12 +1264,6 @@ print() parser:for_each_tree(function(tstree, tree) ranges[tree:lang()] = { tstree:root():range(true) } end) - - -- Scratch buffer should get cleaned up - assert(vim.api.nvim_buf_is_loaded(parser:source())) - parser:destroy() - assert(not vim.api.nvim_buf_is_loaded(parser:source())) - return ranges end)