mirror of
https://github.com/neovim/neovim.git
synced 2025-09-06 03:18:16 +00:00
perf(treesitter): do not scan past given line for predicate match
Problem --- If a highlighter query returns a significant number of predicate non-matches, the highlighter will scan well past the end of the window. Solution --- In the iterator returned from `iter_captures`, accept an optional parameter `end_line`. If no parameter provided, the behavior is unchanged, hence this is a non-invasive tweak. Fixes: #25113 nvim-treesitter/nvim-treesitter#5057
This commit is contained in:
@@ -976,8 +976,8 @@ Query:iter_captures({node}, {source}, {start}, {stop})
|
|||||||
• {stop} (integer) Stopping line for the search (end-exclusive)
|
• {stop} (integer) Stopping line for the search (end-exclusive)
|
||||||
|
|
||||||
Return: ~
|
Return: ~
|
||||||
(fun(): integer, TSNode, TSMetadata): capture id, capture node,
|
(fun(end_line: integer|nil): integer, TSNode, TSMetadata): capture id,
|
||||||
metadata
|
capture node, metadata
|
||||||
|
|
||||||
*Query:iter_matches()*
|
*Query:iter_matches()*
|
||||||
Query:iter_matches({node}, {source}, {start}, {stop}, {opts})
|
Query:iter_matches({node}, {source}, {start}, {stop}, {opts})
|
||||||
|
@@ -2,7 +2,7 @@ local api = vim.api
|
|||||||
local query = vim.treesitter.query
|
local query = vim.treesitter.query
|
||||||
local Range = require('vim.treesitter._range')
|
local Range = require('vim.treesitter._range')
|
||||||
|
|
||||||
---@alias TSHlIter fun(): integer, TSNode, TSMetadata
|
---@alias TSHlIter fun(end_line: integer|nil): integer, TSNode, TSMetadata
|
||||||
|
|
||||||
---@class TSHighlightState
|
---@class TSHighlightState
|
||||||
---@field next_row integer
|
---@field next_row integer
|
||||||
@@ -241,40 +241,43 @@ local function on_line_impl(self, buf, line, is_spell_nav)
|
|||||||
end
|
end
|
||||||
|
|
||||||
while line >= state.next_row do
|
while line >= state.next_row do
|
||||||
local capture, node, metadata = state.iter()
|
local capture, node, metadata = state.iter(line)
|
||||||
|
|
||||||
if capture == nil then
|
local range = { root_end_row + 1, 0, root_end_row + 1, 0 }
|
||||||
break
|
if node then
|
||||||
|
range = vim.treesitter.get_range(node, buf, metadata and metadata[capture])
|
||||||
end
|
end
|
||||||
|
|
||||||
local range = vim.treesitter.get_range(node, buf, metadata[capture])
|
|
||||||
local start_row, start_col, end_row, end_col = Range.unpack4(range)
|
local start_row, start_col, end_row, end_col = Range.unpack4(range)
|
||||||
local hl = highlighter_query.hl_cache[capture]
|
|
||||||
|
|
||||||
local capture_name = highlighter_query:query().captures[capture]
|
if capture then
|
||||||
local spell = nil ---@type boolean?
|
local hl = highlighter_query.hl_cache[capture]
|
||||||
if capture_name == 'spell' then
|
|
||||||
spell = true
|
local capture_name = highlighter_query:query().captures[capture]
|
||||||
elseif capture_name == 'nospell' then
|
local spell = nil ---@type boolean?
|
||||||
spell = false
|
if capture_name == 'spell' then
|
||||||
|
spell = true
|
||||||
|
elseif capture_name == 'nospell' then
|
||||||
|
spell = false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Give nospell a higher priority so it always overrides spell captures.
|
||||||
|
local spell_pri_offset = capture_name == 'nospell' and 1 or 0
|
||||||
|
|
||||||
|
if hl and end_row >= line and (not is_spell_nav or spell ~= nil) then
|
||||||
|
local priority = (tonumber(metadata.priority) or vim.highlight.priorities.treesitter)
|
||||||
|
+ spell_pri_offset
|
||||||
|
api.nvim_buf_set_extmark(buf, ns, start_row, start_col, {
|
||||||
|
end_line = end_row,
|
||||||
|
end_col = end_col,
|
||||||
|
hl_group = hl,
|
||||||
|
ephemeral = true,
|
||||||
|
priority = priority,
|
||||||
|
conceal = metadata.conceal,
|
||||||
|
spell = spell,
|
||||||
|
})
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Give nospell a higher priority so it always overrides spell captures.
|
|
||||||
local spell_pri_offset = capture_name == 'nospell' and 1 or 0
|
|
||||||
|
|
||||||
if hl and end_row >= line and (not is_spell_nav or spell ~= nil) then
|
|
||||||
local priority = (tonumber(metadata.priority) or vim.highlight.priorities.treesitter)
|
|
||||||
+ spell_pri_offset
|
|
||||||
api.nvim_buf_set_extmark(buf, ns, start_row, start_col, {
|
|
||||||
end_line = end_row,
|
|
||||||
end_col = end_col,
|
|
||||||
hl_group = hl,
|
|
||||||
ephemeral = true,
|
|
||||||
priority = priority,
|
|
||||||
conceal = metadata.conceal,
|
|
||||||
spell = spell,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
if start_row > line then
|
if start_row > line then
|
||||||
state.next_row = start_row
|
state.next_row = start_row
|
||||||
end
|
end
|
||||||
|
@@ -708,7 +708,8 @@ end
|
|||||||
---@param start integer Starting line for the search
|
---@param start integer Starting line for the search
|
||||||
---@param stop integer Stopping line for the search (end-exclusive)
|
---@param stop integer Stopping line for the search (end-exclusive)
|
||||||
---
|
---
|
||||||
---@return (fun(): integer, TSNode, TSMetadata): capture id, capture node, metadata
|
---@return (fun(end_line: integer|nil): integer, TSNode, TSMetadata):
|
||||||
|
--- capture id, capture node, metadata
|
||||||
function Query:iter_captures(node, source, start, stop)
|
function Query:iter_captures(node, source, start, stop)
|
||||||
if type(source) == 'number' and source == 0 then
|
if type(source) == 'number' and source == 0 then
|
||||||
source = api.nvim_get_current_buf()
|
source = api.nvim_get_current_buf()
|
||||||
@@ -717,7 +718,7 @@ function Query:iter_captures(node, source, start, stop)
|
|||||||
start, stop = value_or_node_range(start, stop, node)
|
start, stop = value_or_node_range(start, stop, node)
|
||||||
|
|
||||||
local raw_iter = node:_rawquery(self.query, true, start, stop)
|
local raw_iter = node:_rawquery(self.query, true, start, stop)
|
||||||
local function iter()
|
local function iter(end_line)
|
||||||
local capture, captured_node, match = raw_iter()
|
local capture, captured_node, match = raw_iter()
|
||||||
local metadata = {}
|
local metadata = {}
|
||||||
|
|
||||||
@@ -725,7 +726,10 @@ function Query:iter_captures(node, source, start, stop)
|
|||||||
local active = self:match_preds(match, match.pattern, source)
|
local active = self:match_preds(match, match.pattern, source)
|
||||||
match.active = active
|
match.active = active
|
||||||
if not active then
|
if not active then
|
||||||
return iter() -- tail call: try next match
|
if end_line and captured_node:range() > end_line then
|
||||||
|
return nil, captured_node, nil
|
||||||
|
end
|
||||||
|
return iter(end_line) -- tail call: try next match
|
||||||
end
|
end
|
||||||
|
|
||||||
self:apply_directives(match, match.pattern, source, metadata)
|
self:apply_directives(match, match.pattern, source, metadata)
|
||||||
|
Reference in New Issue
Block a user