From 5119c03be7a9d440ae1ab027c3879d2ad7e71bba Mon Sep 17 00:00:00 2001 From: bfredl Date: Wed, 3 Sep 2025 13:11:39 +0200 Subject: [PATCH] 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 ++++ src/nvim/api/extmark.c | 12 +++++++++++- src/nvim/api/keysets_defs.h | 2 ++ src/nvim/decoration.c | 16 +++++++++------- src/nvim/decoration.h | 2 +- src/nvim/decoration_defs.h | 1 + test/functional/treesitter/highlight_spec.lua | 2 +- 8 files changed, 30 insertions(+), 10 deletions(-) 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 diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 9d6ed35e12..a14cf426f4 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -836,6 +836,15 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer col2 = c; } + DecorPriority subpriority = 0; + if (HAS_KEY(opts, set_extmark, _subpriority)) { + VALIDATE_RANGE((opts->_subpriority >= 0 && opts->_subpriority <= UINT16_MAX), + "_subpriority", { + goto error; + }); + subpriority = (DecorPriority)opts->_subpriority; + } + if (kv_size(virt_text.data.virt_text)) { decor_range_add_virt(&decor_state, r, c, line2, col2, decor_put_vt(virt_text, NULL), true); } @@ -845,7 +854,8 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer if (has_hl) { DecorSignHighlight sh = decor_sh_from_inline(hl); sh.url = url; - decor_range_add_sh(&decor_state, r, c, line2, col2, &sh, true, (uint32_t)ns_id, id); + decor_range_add_sh(&decor_state, r, c, line2, col2, &sh, true, (uint32_t)ns_id, id, + subpriority); } } else { if (opts->ephemeral) { diff --git a/src/nvim/api/keysets_defs.h b/src/nvim/api/keysets_defs.h index e9a7637427..cb68d09186 100644 --- a/src/nvim/api/keysets_defs.h +++ b/src/nvim/api/keysets_defs.h @@ -62,6 +62,8 @@ typedef struct { Boolean undo_restore; String url; Boolean scoped; + + Integer _subpriority; } Dict(set_extmark); typedef struct { diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c index 7776adf185..2c64935b64 100644 --- a/src/nvim/decoration.c +++ b/src/nvim/decoration.c @@ -553,12 +553,12 @@ static void decor_range_add_from_inline(DecorState *state, int start_row, int st uint32_t idx = decor.data.ext.sh_idx; while (idx != DECOR_ID_INVALID) { DecorSignHighlight *sh = &kv_A(decor_items, idx); - decor_range_add_sh(state, start_row, start_col, end_row, end_col, sh, owned, ns, mark_id); + decor_range_add_sh(state, start_row, start_col, end_row, end_col, sh, owned, ns, mark_id, 0); idx = sh->next; } } else { DecorSignHighlight sh = decor_sh_from_inline(decor.data.hl); - decor_range_add_sh(state, start_row, start_col, end_row, end_col, &sh, owned, ns, mark_id); + decor_range_add_sh(state, start_row, start_col, end_row, end_col, &sh, owned, ns, mark_id, 0); } } @@ -619,14 +619,15 @@ void decor_range_add_virt(DecorState *state, int start_row, int start_col, int e .data.vt = vt, .attr_id = 0, .owned = owned, - .priority = vt->priority, + .priority_internal = ((DecorPriorityInternal)vt->priority << 16), .draw_col = -10, }; decor_range_insert(state, &range); } void decor_range_add_sh(DecorState *state, int start_row, int start_col, int end_row, int end_col, - DecorSignHighlight *sh, bool owned, uint32_t ns, uint32_t mark_id) + DecorSignHighlight *sh, bool owned, uint32_t ns, uint32_t mark_id, + DecorPriority subpriority) { if (sh->flags & kSHIsSign) { return; @@ -638,7 +639,7 @@ void decor_range_add_sh(DecorState *state, int start_row, int start_col, int end .data.sh = *sh, .attr_id = 0, .owned = owned, - .priority = sh->priority, + .priority_internal = ((DecorPriorityInternal)sh->priority << 16) + subpriority, .draw_col = -10, }; @@ -731,7 +732,7 @@ next_mark: break; } int const ordering = r->ordering; - DecorPriority const priority = r->priority; + DecorPriorityInternal const priority = r->priority_internal; int begin = 0; int end = cur_end; @@ -739,7 +740,8 @@ next_mark: int mid = begin + ((end - begin) >> 1); int mi = indices[mid]; DecorRange *mr = &slots[mi].range; - if (mr->priority < priority || (mr->priority == priority && mr->ordering < ordering)) { + if (mr->priority_internal < priority + || (mr->priority_internal == priority && mr->ordering < ordering)) { begin = mid + 1; } else { end = mid; diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h index a1b0df21c2..08586eb238 100644 --- a/src/nvim/decoration.h +++ b/src/nvim/decoration.h @@ -37,7 +37,7 @@ typedef struct { int end_row; int end_col; int ordering; ///< range insertion order - DecorPriority priority; + DecorPriorityInternal priority_internal; bool owned; ///< ephemeral decoration, free memory immediately DecorRangeKind kind; // next pointers MUST NOT be used, these are separate ranges diff --git a/src/nvim/decoration_defs.h b/src/nvim/decoration_defs.h index 33670d1e3c..c392880818 100644 --- a/src/nvim/decoration_defs.h +++ b/src/nvim/decoration_defs.h @@ -36,6 +36,7 @@ enum { typedef kvec_t(struct virt_line { VirtText line; int flags; }) VirtLines; typedef uint16_t DecorPriority; +typedef uint32_t DecorPriorityInternal; #define DECOR_PRIORITY_BASE 0x1000 /// Keep in sync with hl_mode_str[] in decoration.h diff --git a/test/functional/treesitter/highlight_spec.lua b/test/functional/treesitter/highlight_spec.lua index 2f13793686..40d528a379 100644 --- a/test/functional/treesitter/highlight_spec.lua +++ b/test/functional/treesitter/highlight_spec.lua @@ -548,7 +548,7 @@ describe('treesitter highlighting (C)', function() lua = [[ ; query (string) @string - ((comment) @comment (#set! priority 90)) + (comment) @comment (function_call (identifier) @function.call) [ "(" ")" ] @punctuation.bracket ]],