mirror of
https://github.com/neovim/neovim.git
synced 2024-12-23 20:55:18 -07:00
fix(treesitter): revert to using iter_captures in highlighter
Fixes #27895
This commit is contained in:
parent
77a9f3395b
commit
3b29b39e6d
@ -1132,10 +1132,10 @@ Query:iter_captures({node}, {source}, {start}, {stop})
|
|||||||
i.e., to get syntax highlight matches in the current viewport). When
|
i.e., to get syntax highlight matches in the current viewport). When
|
||||||
omitted, the {start} and {stop} row values are used from the given node.
|
omitted, the {start} and {stop} row values are used from the given node.
|
||||||
|
|
||||||
The iterator returns three values: a numeric id identifying the capture,
|
The iterator returns four values: a numeric id identifying the capture,
|
||||||
the captured node, and metadata from any directives processing the match.
|
the captured node, metadata from any directives processing the match, and
|
||||||
The following example shows how to get captures by name: >lua
|
the match itself. The following example shows how to get captures by name: >lua
|
||||||
for id, node, metadata in query:iter_captures(tree:root(), bufnr, first, last) do
|
for id, node, metadata, match in query:iter_captures(tree:root(), bufnr, first, last) do
|
||||||
local name = query.captures[id] -- name of the capture in the query
|
local name = query.captures[id] -- name of the capture in the query
|
||||||
-- typically useful info about the node:
|
-- typically useful info about the node:
|
||||||
local type = node:type() -- type of the captured node
|
local type = node:type() -- type of the captured node
|
||||||
@ -1154,8 +1154,8 @@ Query:iter_captures({node}, {source}, {start}, {stop})
|
|||||||
Defaults to `node:end_()`.
|
Defaults to `node:end_()`.
|
||||||
|
|
||||||
Return: ~
|
Return: ~
|
||||||
(`fun(end_line: integer?): integer, TSNode, vim.treesitter.query.TSMetadata`)
|
(`fun(end_line: integer?): integer, TSNode, vim.treesitter.query.TSMetadata, table<integer, TSNode>`)
|
||||||
capture id, capture node, metadata
|
capture id, capture node, metadata, match
|
||||||
|
|
||||||
*Query:iter_matches()*
|
*Query:iter_matches()*
|
||||||
Query:iter_matches({node}, {source}, {start}, {stop}, {opts})
|
Query:iter_matches({node}, {source}, {start}, {stop}, {opts})
|
||||||
|
@ -4,7 +4,7 @@ local Range = require('vim.treesitter._range')
|
|||||||
|
|
||||||
local ns = api.nvim_create_namespace('treesitter/highlighter')
|
local ns = api.nvim_create_namespace('treesitter/highlighter')
|
||||||
|
|
||||||
---@alias vim.treesitter.highlighter.Iter fun(): integer, table<integer, TSNode[]>, vim.treesitter.query.TSMetadata
|
---@alias vim.treesitter.highlighter.Iter fun(end_line: integer|nil): integer, TSNode, vim.treesitter.query.TSMetadata, table<integer, TSNode[]>
|
||||||
|
|
||||||
---@class (private) vim.treesitter.highlighter.Query
|
---@class (private) vim.treesitter.highlighter.Query
|
||||||
---@field private _query vim.treesitter.Query?
|
---@field private _query vim.treesitter.Query?
|
||||||
@ -57,7 +57,6 @@ end
|
|||||||
---@field next_row integer
|
---@field next_row integer
|
||||||
---@field iter vim.treesitter.highlighter.Iter?
|
---@field iter vim.treesitter.highlighter.Iter?
|
||||||
---@field highlighter_query vim.treesitter.highlighter.Query
|
---@field highlighter_query vim.treesitter.highlighter.Query
|
||||||
---@field level integer Injection level
|
|
||||||
|
|
||||||
---@nodoc
|
---@nodoc
|
||||||
---@class vim.treesitter.highlighter
|
---@class vim.treesitter.highlighter
|
||||||
@ -193,20 +192,12 @@ function TSHighlighter:prepare_highlight_states(srow, erow)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local level = 0
|
|
||||||
local t = tree
|
|
||||||
while t do
|
|
||||||
t = t:parent()
|
|
||||||
level = level + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
-- _highlight_states should be a list so that the highlights are added in the same order as
|
-- _highlight_states should be a list so that the highlights are added in the same order as
|
||||||
-- for_each_tree traversal. This ensures that parents' highlight don't override children's.
|
-- for_each_tree traversal. This ensures that parents' highlight don't override children's.
|
||||||
table.insert(self._highlight_states, {
|
table.insert(self._highlight_states, {
|
||||||
tstree = tstree,
|
tstree = tstree,
|
||||||
next_row = 0,
|
next_row = 0,
|
||||||
iter = nil,
|
iter = nil,
|
||||||
level = level,
|
|
||||||
highlighter_query = highlighter_query,
|
highlighter_query = highlighter_query,
|
||||||
})
|
})
|
||||||
end)
|
end)
|
||||||
@ -296,9 +287,6 @@ end
|
|||||||
---@param is_spell_nav boolean
|
---@param is_spell_nav boolean
|
||||||
local function on_line_impl(self, buf, line, is_spell_nav)
|
local function on_line_impl(self, buf, line, is_spell_nav)
|
||||||
self:for_each_highlight_state(function(state)
|
self:for_each_highlight_state(function(state)
|
||||||
-- Use the injection level to offset the subpriority passed to nvim_buf_set_extmark
|
|
||||||
-- so injections always appear over base highlights.
|
|
||||||
local pattern_offset = state.level * 1000
|
|
||||||
local root_node = state.tstree:root()
|
local root_node = state.tstree:root()
|
||||||
local root_start_row, _, root_end_row, _ = root_node:range()
|
local root_start_row, _, root_end_row, _ = root_node:range()
|
||||||
|
|
||||||
@ -308,58 +296,54 @@ local function on_line_impl(self, buf, line, is_spell_nav)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if state.iter == nil or state.next_row < line then
|
if state.iter == nil or state.next_row < line then
|
||||||
state.iter = state.highlighter_query
|
state.iter =
|
||||||
:query()
|
state.highlighter_query:query():iter_captures(root_node, self.bufnr, line, root_end_row + 1)
|
||||||
:iter_matches(root_node, self.bufnr, line, root_end_row + 1, { all = true })
|
|
||||||
end
|
end
|
||||||
|
|
||||||
while line >= state.next_row do
|
while line >= state.next_row do
|
||||||
local pattern, match, metadata = state.iter()
|
local capture, node, metadata, match = state.iter(line)
|
||||||
|
|
||||||
if not match then
|
local range = { root_end_row + 1, 0, root_end_row + 1, 0 }
|
||||||
state.next_row = root_end_row + 1
|
if node then
|
||||||
|
range = vim.treesitter.get_range(node, buf, metadata and metadata[capture])
|
||||||
end
|
end
|
||||||
|
local start_row, start_col, end_row, end_col = Range.unpack4(range)
|
||||||
|
|
||||||
for capture, nodes in pairs(match or {}) do
|
if capture then
|
||||||
local capture_name = state.highlighter_query:query().captures[capture]
|
|
||||||
local spell, spell_pri_offset = get_spell(capture_name)
|
|
||||||
|
|
||||||
local hl = state.highlighter_query:get_hl_from_capture(capture)
|
local hl = state.highlighter_query:get_hl_from_capture(capture)
|
||||||
|
|
||||||
|
local capture_name = state.highlighter_query:query().captures[capture]
|
||||||
|
|
||||||
|
local spell, spell_pri_offset = get_spell(capture_name)
|
||||||
|
|
||||||
-- The "priority" attribute can be set at the pattern level or on a particular capture
|
-- The "priority" attribute can be set at the pattern level or on a particular capture
|
||||||
local priority = (
|
local priority = (
|
||||||
tonumber(metadata.priority or metadata[capture] and metadata[capture].priority)
|
tonumber(metadata.priority or metadata[capture] and metadata[capture].priority)
|
||||||
or vim.highlight.priorities.treesitter
|
or vim.highlight.priorities.treesitter
|
||||||
) + spell_pri_offset
|
) + spell_pri_offset
|
||||||
|
|
||||||
local url = get_url(match, buf, capture, metadata)
|
|
||||||
|
|
||||||
-- The "conceal" attribute can be set at the pattern level or on a particular capture
|
-- The "conceal" attribute can be set at the pattern level or on a particular capture
|
||||||
local conceal = metadata.conceal or metadata[capture] and metadata[capture].conceal
|
local conceal = metadata.conceal or metadata[capture] and metadata[capture].conceal
|
||||||
|
|
||||||
for _, node in ipairs(nodes) do
|
local url = get_url(match, buf, capture, metadata)
|
||||||
local range = vim.treesitter.get_range(node, buf, metadata[capture])
|
|
||||||
local start_row, start_col, end_row, end_col = Range.unpack4(range)
|
|
||||||
|
|
||||||
if hl and end_row >= line and (not is_spell_nav or spell ~= nil) then
|
if hl and end_row >= line and (not is_spell_nav or spell ~= nil) then
|
||||||
api.nvim_buf_set_extmark(buf, ns, start_row, start_col, {
|
api.nvim_buf_set_extmark(buf, ns, start_row, start_col, {
|
||||||
end_line = end_row,
|
end_line = end_row,
|
||||||
end_col = end_col,
|
end_col = end_col,
|
||||||
hl_group = hl,
|
hl_group = hl,
|
||||||
ephemeral = true,
|
ephemeral = true,
|
||||||
priority = priority,
|
priority = priority,
|
||||||
_subpriority = pattern_offset + pattern,
|
conceal = conceal,
|
||||||
conceal = conceal,
|
spell = spell,
|
||||||
spell = spell,
|
url = url,
|
||||||
url = url,
|
})
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
if start_row > line then
|
|
||||||
state.next_row = start_row
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if start_row > line then
|
||||||
|
state.next_row = start_row
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
@ -811,12 +811,13 @@ end
|
|||||||
--- as the {node}, i.e., to get syntax highlight matches in the current
|
--- as the {node}, i.e., to get syntax highlight matches in the current
|
||||||
--- viewport). When omitted, the {start} and {stop} row values are used from the given node.
|
--- viewport). When omitted, the {start} and {stop} row values are used from the given node.
|
||||||
---
|
---
|
||||||
--- The iterator returns three values: a numeric id identifying the capture,
|
--- The iterator returns four values: a numeric id identifying the capture,
|
||||||
--- the captured node, and metadata from any directives processing the match.
|
--- the captured node, metadata from any directives processing the match,
|
||||||
|
--- and the match itself.
|
||||||
--- The following example shows how to get captures by name:
|
--- The following example shows how to get captures by name:
|
||||||
---
|
---
|
||||||
--- ```lua
|
--- ```lua
|
||||||
--- for id, node, metadata in query:iter_captures(tree:root(), bufnr, first, last) do
|
--- for id, node, metadata, match in query:iter_captures(tree:root(), bufnr, first, last) do
|
||||||
--- local name = query.captures[id] -- name of the capture in the query
|
--- local name = query.captures[id] -- name of the capture in the query
|
||||||
--- -- typically useful info about the node:
|
--- -- typically useful info about the node:
|
||||||
--- local type = node:type() -- type of the captured node
|
--- local type = node:type() -- type of the captured node
|
||||||
@ -830,8 +831,8 @@ end
|
|||||||
---@param start? integer Starting line for the search. Defaults to `node:start()`.
|
---@param start? integer Starting line for the search. Defaults to `node:start()`.
|
||||||
---@param stop? integer Stopping line for the search (end-exclusive). Defaults to `node:end_()`.
|
---@param stop? integer Stopping line for the search (end-exclusive). Defaults to `node:end_()`.
|
||||||
---
|
---
|
||||||
---@return (fun(end_line: integer|nil): integer, TSNode, vim.treesitter.query.TSMetadata):
|
---@return (fun(end_line: integer|nil): integer, TSNode, vim.treesitter.query.TSMetadata, table<integer, TSNode>):
|
||||||
--- capture id, capture node, metadata
|
--- capture id, capture node, metadata, match
|
||||||
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()
|
||||||
@ -856,7 +857,7 @@ function Query:iter_captures(node, source, start, stop)
|
|||||||
|
|
||||||
self:apply_directives(match, match.pattern, source, metadata)
|
self:apply_directives(match, match.pattern, source, metadata)
|
||||||
end
|
end
|
||||||
return capture, captured_node, metadata
|
return capture, captured_node, metadata, match
|
||||||
end
|
end
|
||||||
return iter
|
return iter
|
||||||
end
|
end
|
||||||
|
@ -762,6 +762,32 @@ describe('treesitter highlighting (C)', function()
|
|||||||
]],
|
]],
|
||||||
}
|
}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('gives higher priority to more specific captures #27895', function()
|
||||||
|
insert([[
|
||||||
|
void foo(int *bar);
|
||||||
|
]])
|
||||||
|
|
||||||
|
local query = [[
|
||||||
|
"*" @operator
|
||||||
|
|
||||||
|
(parameter_declaration
|
||||||
|
declarator: (pointer_declarator) @variable.parameter)
|
||||||
|
]]
|
||||||
|
|
||||||
|
exec_lua([[
|
||||||
|
local query = ...
|
||||||
|
vim.treesitter.query.set('c', 'highlights', query)
|
||||||
|
vim.treesitter.highlighter.new(vim.treesitter.get_parser(0, 'c'))
|
||||||
|
]], query)
|
||||||
|
|
||||||
|
screen:expect{grid=[[
|
||||||
|
void foo(int {4:*}{11:bar}); |
|
||||||
|
^ |
|
||||||
|
{1:~ }|*15
|
||||||
|
|
|
||||||
|
]]}
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
describe('treesitter highlighting (lua)', function()
|
describe('treesitter highlighting (lua)', function()
|
||||||
|
Loading…
Reference in New Issue
Block a user