mirror of
https://github.com/neovim/neovim.git
synced 2025-09-06 19:38:20 +00:00
perf(treesitter): cache queries strongly
**Problem:** Query parsing uses a weak cache which is invalidated frequently **Solution:** Make the cache strong, and invalidate it manually when necessary (that is, when `rtp` is changed or `query.set()` is called) Co-authored-by: Christian Clason <c.clason@uni-graz.at>
This commit is contained in:

committed by
Christian Clason

parent
40bf23adaf
commit
3fdc430241
@@ -294,6 +294,9 @@ PERFORMANCE
|
|||||||
inflight requests). This greatly improves performance with slow LSP servers.
|
inflight requests). This greatly improves performance with slow LSP servers.
|
||||||
• 10x speedup for |vim.treesitter.foldexpr()| (when no parser exists for the
|
• 10x speedup for |vim.treesitter.foldexpr()| (when no parser exists for the
|
||||||
buffer).
|
buffer).
|
||||||
|
• Strong |treesitter-query| caching makes repeat |vim.treesitter.query.get()|
|
||||||
|
and |vim.treesitter.query.parse()| calls significantly faster for large
|
||||||
|
queries.
|
||||||
|
|
||||||
PLUGINS
|
PLUGINS
|
||||||
|
|
||||||
|
@@ -262,6 +262,7 @@ local explicit_queries = setmetatable({}, {
|
|||||||
---@param query_name string Name of the query (e.g., "highlights")
|
---@param query_name string Name of the query (e.g., "highlights")
|
||||||
---@param text string Query text (unparsed).
|
---@param text string Query text (unparsed).
|
||||||
function M.set(lang, query_name, text)
|
function M.set(lang, query_name, text)
|
||||||
|
M.get:clear(lang, query_name)
|
||||||
explicit_queries[lang][query_name] = M.parse(lang, text)
|
explicit_queries[lang][query_name] = M.parse(lang, text)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -284,7 +285,15 @@ M.get = memoize('concat-2', function(lang, query_name)
|
|||||||
end
|
end
|
||||||
|
|
||||||
return M.parse(lang, query_string)
|
return M.parse(lang, query_string)
|
||||||
end)
|
end, false)
|
||||||
|
|
||||||
|
api.nvim_create_autocmd('OptionSet', {
|
||||||
|
pattern = { 'runtimepath' },
|
||||||
|
group = api.nvim_create_augroup('ts_query_cache_reset', { clear = true }),
|
||||||
|
callback = function()
|
||||||
|
M.get:clear()
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
--- Parses a {query} string and returns a `Query` object (|lua-treesitter-query|), which can be used
|
--- Parses a {query} string and returns a `Query` object (|lua-treesitter-query|), which can be used
|
||||||
--- to search the tree for the query patterns (via |Query:iter_captures()|, |Query:iter_matches()|),
|
--- to search the tree for the query patterns (via |Query:iter_captures()|, |Query:iter_matches()|),
|
||||||
@@ -316,7 +325,7 @@ M.parse = memoize('concat-2', function(lang, query)
|
|||||||
assert(language.add(lang))
|
assert(language.add(lang))
|
||||||
local ts_query = vim._ts_parse_query(lang, query)
|
local ts_query = vim._ts_parse_query(lang, query)
|
||||||
return Query.new(lang, ts_query)
|
return Query.new(lang, ts_query)
|
||||||
end)
|
end, false)
|
||||||
|
|
||||||
--- Implementations of predicates that can optionally be prefixed with "any-".
|
--- Implementations of predicates that can optionally be prefixed with "any-".
|
||||||
---
|
---
|
||||||
|
@@ -86,7 +86,7 @@ void ui_refresh(void)
|
|||||||
local before = vim.api.nvim__stats().ts_query_parse_count
|
local before = vim.api.nvim__stats().ts_query_parse_count
|
||||||
collectgarbage('stop')
|
collectgarbage('stop')
|
||||||
for _ = 1, _n, 1 do
|
for _ = 1, _n, 1 do
|
||||||
vim.treesitter.query.parse('c', long_query, _n)
|
vim.treesitter.query.parse('c', long_query)
|
||||||
end
|
end
|
||||||
collectgarbage('restart')
|
collectgarbage('restart')
|
||||||
collectgarbage('collect')
|
collectgarbage('collect')
|
||||||
@@ -96,8 +96,39 @@ void ui_refresh(void)
|
|||||||
end
|
end
|
||||||
|
|
||||||
eq(1, q(1))
|
eq(1, q(1))
|
||||||
-- cache is cleared by garbage collection even if valid "cquery" reference is kept around
|
-- cache is retained even after garbage collection
|
||||||
eq(1, q(100))
|
eq(0, q(100))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('cache is cleared upon runtimepath changes, or setting query manually', function()
|
||||||
|
---@return number
|
||||||
|
exec_lua(function()
|
||||||
|
_G.query_parse_count = _G.query_parse_count or 0
|
||||||
|
local parse = vim.treesitter.query.parse
|
||||||
|
vim.treesitter.query.parse = function(...)
|
||||||
|
_G.query_parse_count = _G.query_parse_count + 1
|
||||||
|
return parse(...)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
local function q(_n)
|
||||||
|
return exec_lua(function()
|
||||||
|
for _ = 1, _n, 1 do
|
||||||
|
vim.treesitter.query.get('c', 'highlights')
|
||||||
|
end
|
||||||
|
return _G.query_parse_count
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
eq(1, q(10))
|
||||||
|
exec_lua(function()
|
||||||
|
vim.opt.rtp:prepend('/another/dir')
|
||||||
|
end)
|
||||||
|
eq(2, q(100))
|
||||||
|
exec_lua(function()
|
||||||
|
vim.treesitter.query.set('c', 'highlights', [[; test]])
|
||||||
|
end)
|
||||||
|
eq(3, q(100))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('supports query and iter by capture (iter_captures)', function()
|
it('supports query and iter by capture (iter_captures)', function()
|
||||||
|
Reference in New Issue
Block a user