From f9d2115a359c7846056bb2eb31f9eae818c6d510 Mon Sep 17 00:00:00 2001 From: bfredl Date: Fri, 29 Aug 2025 12:22:25 +0200 Subject: perf(highlight): allow decoration providers to skip ranges without data Continuing the work of #31400 That PR allowed the provider to be invoked multiple times per line. We want only to do that when there actually is more data later on the line. Additionally, we want to skip over lines which contain no new highlight items. The TS query cursor already tells us what the next position with more data is, so there is no need to reinvoke the range callback before that. NB: this removes the double buffering introduced in #32619 which is funtamentally incompatible with this (nvim core is supposed to keep track of long ranges by itself, without requiring a callback reinvoke blitz). Need to adjust the priorities some other way to fix the same issue. --- runtime/lua/vim/_meta/api.lua | 7 +++ runtime/lua/vim/treesitter/highlighter.lua | 86 ++++++------------------------ 2 files changed, 23 insertions(+), 70 deletions(-) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua index 61e4a28704..abda4dead0 100644 --- a/runtime/lua/vim/_meta/api.lua +++ b/runtime/lua/vim/_meta/api.lua @@ -2175,6 +2175,13 @@ function vim.api.nvim_set_current_win(window) end --- ``` --- ["range", winid, bufnr, begin_row, begin_col, end_row, end_col] --- ``` +--- +--- In addition to returning a boolean, it is also allowed to +--- return a `skip_row, skip_col` pair of integers. This implies +--- that this function does not need to be called until a range +--- which continues beyond the skipped position. A single integer +--- return value `skip_row` is short for `skip_row, 0` +--- --- - on_end: called at the end of a redraw cycle --- ``` --- ["end", tick] diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua index da13f7fe47..9165550132 100644 --- a/runtime/lua/vim/treesitter/highlighter.lua +++ b/runtime/lua/vim/treesitter/highlighter.lua @@ -53,15 +53,12 @@ function TSHighlighterQuery:query() return self._query end ----@alias MarkInfo { start_line: integer, start_col: integer, opts: vim.api.keyset.set_extmark } - ---@class (private) vim.treesitter.highlighter.State ---@field tstree TSTree ---@field next_row integer ---@field next_col integer ---@field iter vim.treesitter.highlighter.Iter? ---@field highlighter_query vim.treesitter.highlighter.Query ----@field prev_marks MarkInfo[] ---@nodoc ---@class vim.treesitter.highlighter @@ -238,7 +235,6 @@ function TSHighlighter:prepare_highlight_states(win, srow, erow) next_col = 0, iter = nil, highlighter_query = hl_query, - prev_marks = {}, }) end) end @@ -330,44 +326,6 @@ local function get_spell(capture_name) return nil, 0 end ----Adds the mark to the buffer, clipped by the line. ----Queues the remainder if the mark continues after the line. ----@param m MarkInfo ----@param buf integer ----@param range_start_row integer ----@param range_start_col integer ----@param range_end_row integer ----@param range_end_col integer ----@param next_marks MarkInfo[] -local function add_mark( - m, - buf, - range_start_row, - range_start_col, - range_end_row, - range_end_col, - next_marks -) - local cur_start_l = m.start_line - local cur_start_c = m.start_col - if cmp_lt(cur_start_l, cur_start_c, range_start_row, range_start_col) then - cur_start_l = range_start_row - cur_start_c = range_start_col - end - - local cur_opts = m.opts - if cmp_lt(range_end_row, range_end_col, cur_opts.end_line, cur_opts.end_col) then - cur_opts = vim.deepcopy(cur_opts, true) - cur_opts.end_line = range_end_row - cur_opts.end_col = range_end_col - table.insert(next_marks, m) - end - - if cmp_lt(cur_start_l, cur_start_c, cur_opts.end_line, cur_opts.end_col) then - api.nvim_buf_set_extmark(buf, ns, cur_start_l, cur_start_c, cur_opts) - end -end - ---@param self vim.treesitter.highlighter ---@param win integer ---@param buf integer @@ -398,6 +356,10 @@ local function on_range_impl( for i = range_start_row, range_end_row - 1 do self._conceal_checked[i] = self._conceal_line or nil end + + local MAX_ROW = 2147483647 -- sentinel for skipping to the end of file + local skip_until_row = MAX_ROW + local skip_until_col = 0 self:for_each_highlight_state(win, function(state) local root_node = state.tstree:root() ---@type { [1]: integer, [2]: integer, [3]: integer, [4]: integer } @@ -409,25 +371,15 @@ local function on_range_impl( { range_start_row, range_start_col, range_end_row, range_end_col } ) then + if cmp_lt(root_range[1], root_range[2], skip_until_row, skip_until_col) then + skip_until_row = root_range[1] + skip_until_col = root_range[2] + end return end local tree_region = state.tstree:included_ranges(true) - local next_marks = {} - - for _, mark in ipairs(state.prev_marks) do - add_mark( - mark, - buf, - range_start_row, - range_start_col, - range_end_row, - range_end_col, - next_marks - ) - end - local next_row = state.next_row local next_col = state.next_col @@ -488,7 +440,7 @@ local function on_range_impl( local url = get_url(match, buf, capture, metadata) if hl and not on_conceal and (not on_spell or spell ~= nil) then - local opts = { + api.nvim_buf_set_extmark(buf, ns, start_row, start_col, { end_line = end_row, end_col = end_col, hl_group = hl, @@ -497,17 +449,7 @@ local function on_range_impl( conceal = conceal, spell = spell, url = url, - } - local mark = { start_line = start_row, start_col = start_col, opts = opts } - add_mark( - mark, - buf, - range_start_row, - range_start_col, - range_end_row, - range_end_col, - next_marks - ) + }) end if @@ -525,8 +467,12 @@ local function on_range_impl( state.next_row = next_row state.next_col = next_col - state.prev_marks = next_marks + if cmp_lt(next_row, next_col, skip_until_row, skip_until_col) then + skip_until_row = next_row + skip_until_col = next_col + end end) + return skip_until_row, skip_until_col end ---@private @@ -542,7 +488,7 @@ function TSHighlighter._on_range(_, win, buf, br, bc, er, ec, _) return end - on_range_impl(self, win, buf, br, bc, er, ec, false, false) + return on_range_impl(self, win, buf, br, bc, er, ec, false, false) end ---@private -- cgit From 5119c03be7a9d440ae1ab027c3879d2ad7e71bba Mon Sep 17 00:00:00 2001 From: bfredl Date: Wed, 3 Sep 2025 13:11:39 +0200 Subject: fix(treesitter): use subpriorities for tree ordering This partially reverts 0b8a72b73934d33a05e20c255298e88cd921df32, that is unreverts 15e77a56b711102fdc123e15b3f37d49bc0b1df1 "priority" is an internal neovim concept which does not occur in shared queries. Ideally a single priority space should eventually be enough for our needs. But as we don't want to poke at the usages of priorities right now in the wider ecosystem, introduce the "subpriorities" so that treesitter code can distinguish highlights of the same priorities with different tree nesting depth. This mainly affects `injection.combined` as parent-tree nodes might appear in the middle of child-tree nodes which otherwise is not possible. --- runtime/lua/vim/_meta/api_keysets.lua | 1 + runtime/lua/vim/treesitter/highlighter.lua | 4 ++++ 2 files changed, 5 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/api_keysets.lua b/runtime/lua/vim/_meta/api_keysets.lua index a3c6b52409..a09b0dbf65 100644 --- a/runtime/lua/vim/_meta/api_keysets.lua +++ b/runtime/lua/vim/_meta/api_keysets.lua @@ -423,6 +423,7 @@ error('Cannot require a meta file') --- @field undo_restore? boolean --- @field url? string --- @field scoped? boolean +--- @field _subpriority? integer --- @class vim.api.keyset.user_command --- @field addr? any diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua index 9165550132..c0398bbe18 100644 --- a/runtime/lua/vim/treesitter/highlighter.lua +++ b/runtime/lua/vim/treesitter/highlighter.lua @@ -360,7 +360,10 @@ local function on_range_impl( local MAX_ROW = 2147483647 -- sentinel for skipping to the end of file local skip_until_row = MAX_ROW local skip_until_col = 0 + + local subtree_counter = 0 self:for_each_highlight_state(win, function(state) + subtree_counter = subtree_counter + 1 local root_node = state.tstree:root() ---@type { [1]: integer, [2]: integer, [3]: integer, [4]: integer } local root_range = { root_node:range() } @@ -449,6 +452,7 @@ local function on_range_impl( conceal = conceal, spell = spell, url = url, + _subpriority = subtree_counter, }) end -- cgit