diff options
author | Josh Rahm <joshuarahm@gmail.com> | 2023-11-29 22:40:31 +0000 |
---|---|---|
committer | Josh Rahm <joshuarahm@gmail.com> | 2023-11-29 22:40:31 +0000 |
commit | 339e2d15cc26fe86988ea06468d912a46c8d6f29 (patch) | |
tree | a6167fc8fcfc6ae2dc102f57b2473858eac34063 /runtime/lua/vim/treesitter/highlighter.lua | |
parent | 067dc73729267c0262438a6fdd66e586f8496946 (diff) | |
parent | 4a8bf24ac690004aedf5540fa440e788459e5e34 (diff) | |
download | rneovim-339e2d15cc26fe86988ea06468d912a46c8d6f29.tar.gz rneovim-339e2d15cc26fe86988ea06468d912a46c8d6f29.tar.bz2 rneovim-339e2d15cc26fe86988ea06468d912a46c8d6f29.zip |
Merge remote-tracking branch 'upstream/master' into fix_repeatcmdline
Diffstat (limited to 'runtime/lua/vim/treesitter/highlighter.lua')
-rw-r--r-- | runtime/lua/vim/treesitter/highlighter.lua | 183 |
1 files changed, 114 insertions, 69 deletions
diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua index d77a0d0d03..496193c6ed 100644 --- a/runtime/lua/vim/treesitter/highlighter.lua +++ b/runtime/lua/vim/treesitter/highlighter.lua @@ -1,17 +1,34 @@ -local a = vim.api -local query = require('vim.treesitter.query') +local api = vim.api +local query = vim.treesitter.query +local Range = require('vim.treesitter._range') + +---@alias TSHlIter fun(end_line: integer|nil): integer, TSNode, TSMetadata + +---@class TSHighlightState +---@field next_row integer +---@field iter TSHlIter|nil --- support reload for quick experimentation ---@class TSHighlighter +---@field active table<integer,TSHighlighter> +---@field bufnr integer +---@field orig_spelloptions string +---@field _highlight_states table<TSTree,TSHighlightState> +---@field _queries table<string,TSHighlighterQuery> +---@field tree LanguageTree +---@field redraw_count integer local TSHighlighter = rawget(vim.treesitter, 'TSHighlighter') or {} TSHighlighter.__index = TSHighlighter +--- @nodoc TSHighlighter.active = TSHighlighter.active or {} +---@class TSHighlighterQuery +---@field _query Query|nil +---@field hl_cache table<integer,integer> local TSHighlighterQuery = {} TSHighlighterQuery.__index = TSHighlighterQuery -local ns = a.nvim_create_namespace('treesitter/highlighter') +local ns = api.nvim_create_namespace('treesitter/highlighter') ---@private function TSHighlighterQuery.new(lang, query_string) @@ -22,7 +39,7 @@ function TSHighlighterQuery.new(lang, query_string) local name = self._query.captures[capture] local id = 0 if not vim.startswith(name, '_') then - id = a.nvim_get_hl_id_by_name('@' .. name .. '.' .. lang) + id = api.nvim_get_hl_id_by_name('@' .. name .. '.' .. lang) end rawset(table, capture, id) @@ -31,22 +48,24 @@ function TSHighlighterQuery.new(lang, query_string) }) if query_string then - self._query = query.parse_query(lang, query_string) + self._query = query.parse(lang, query_string) else - self._query = query.get_query(lang, 'highlights') + self._query = query.get(lang, 'highlights') end return self end ----@private +---@package function TSHighlighterQuery:query() return self._query end ---- Creates a new highlighter using @param tree +---@package +--- +--- Creates a highlighter for `tree`. --- ----@param tree LanguageTree |LanguageTree| parser object to use for highlighting +---@param tree LanguageTree parser object to use for highlighting ---@param opts (table|nil) Configuration of the highlighter: --- - queries table overwrite queries used by the highlighter ---@return TSHighlighter Created highlighter object @@ -57,27 +76,37 @@ function TSHighlighter.new(tree, opts) error('TSHighlighter can not be used with a string parser source.') end - opts = opts or {} + opts = opts or {} ---@type { queries: table<string,string> } self.tree = tree tree:register_cbs({ - on_changedtree = function(...) - self:on_changedtree(...) - end, on_bytes = function(...) self:on_bytes(...) end, - on_detach = function(...) - self:on_detach(...) + on_detach = function() + self:on_detach() end, }) - self.bufnr = tree:source() + tree:register_cbs({ + on_changedtree = function(...) + self:on_changedtree(...) + end, + on_child_removed = function(child) + child:for_each_tree(function(t) + self:on_changedtree(t:included_ranges(true)) + end) + end, + }, true) + + self.bufnr = tree:source() --[[@as integer]] self.edit_count = 0 self.redraw_count = 0 self.line_count = {} -- A map of highlight states. -- This state is kept during rendering across each line update. self._highlight_states = {} + + ---@type table<string,TSHighlighterQuery> self._queries = {} -- Queries for a specific language can be overridden by a custom @@ -103,7 +132,7 @@ function TSHighlighter.new(tree, opts) vim.cmd.runtime({ 'syntax/synload.vim', bang = true }) end - a.nvim_buf_call(self.bufnr, function() + api.nvim_buf_call(self.bufnr, function() vim.opt_local.spelloptions:append('noplainbuffer') end) @@ -112,6 +141,7 @@ function TSHighlighter.new(tree, opts) return self end +--- @nodoc --- Removes all internal references to the highlighter function TSHighlighter:destroy() if TSHighlighter.active[self.bufnr] then @@ -122,12 +152,14 @@ function TSHighlighter:destroy() vim.bo[self.bufnr].spelloptions = self.orig_spelloptions vim.b[self.bufnr].ts_highlight = nil if vim.g.syntax_on == 1 then - a.nvim_exec_autocmds('FileType', { group = 'syntaxset', buffer = self.bufnr }) + api.nvim_exec_autocmds('FileType', { group = 'syntaxset', buffer = self.bufnr }) end end end ----@private +---@package +---@param tstree TSTree +---@return TSHighlightState function TSHighlighter:get_highlight_state(tstree) if not self._highlight_states[tstree] then self._highlight_states[tstree] = { @@ -144,28 +176,31 @@ function TSHighlighter:reset_highlight_state() self._highlight_states = {} end ----@private +---@package +---@param start_row integer +---@param new_end integer function TSHighlighter:on_bytes(_, _, start_row, _, _, _, _, _, new_end) - a.nvim__buf_redraw_range(self.bufnr, start_row, start_row + new_end + 1) + api.nvim__buf_redraw_range(self.bufnr, start_row, start_row + new_end + 1) end ----@private +---@package function TSHighlighter:on_detach() self:destroy() end ----@private +---@package +---@param changes Range6[] function TSHighlighter:on_changedtree(changes) - for _, ch in ipairs(changes or {}) do - a.nvim__buf_redraw_range(self.bufnr, ch[1], ch[3] + 1) + for _, ch in ipairs(changes) do + api.nvim__buf_redraw_range(self.bufnr, ch[1], ch[4] + 1) end end --- Gets the query used for @param lang -- ----@private +---@package ---@param lang string Language used by the highlighter. ----@return Query +---@return TSHighlighterQuery function TSHighlighter:get_query(lang) if not self._queries[lang] then self._queries[lang] = TSHighlighterQuery.new(lang) @@ -174,7 +209,10 @@ function TSHighlighter:get_query(lang) return self._queries[lang] end ----@private +---@param self TSHighlighter +---@param buf integer +---@param line integer +---@param is_spell_nav boolean local function on_line_impl(self, buf, line, is_spell_nav) self.tree:for_each_tree(function(tstree, tree) if not tstree then @@ -203,45 +241,54 @@ local function on_line_impl(self, buf, line, is_spell_nav) end while line >= state.next_row do - local capture, node, metadata = state.iter() + local capture, node, metadata = state.iter(line) - if capture == nil then - break + local range = { root_end_row + 1, 0, root_end_row + 1, 0 } + if node then + range = vim.treesitter.get_range(node, buf, metadata and metadata[capture]) end - - local start_row, start_col, end_row, end_col = node:range() - local hl = highlighter_query.hl_cache[capture] - - local capture_name = highlighter_query:query().captures[capture] - local spell = nil - if capture_name == 'spell' then - spell = true - elseif capture_name == 'nospell' then - spell = false + local start_row, start_col, end_row, end_col = Range.unpack4(range) + + if capture then + local hl = highlighter_query.hl_cache[capture] + + local capture_name = highlighter_query:query().captures[capture] + local spell = nil ---@type boolean? + 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 - -- 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 - a.nvim_buf_set_extmark(buf, ns, start_row, start_col, { - end_line = end_row, - end_col = end_col, - hl_group = hl, - ephemeral = true, - priority = (tonumber(metadata.priority) or 100) + spell_pri_offset, -- Low but leaves room below - conceal = metadata.conceal, - spell = spell, - }) - end if start_row > line then state.next_row = start_row end end - end, true) + end) end ---@private +---@param _win integer +---@param buf integer +---@param line integer function TSHighlighter._on_line(_, _win, buf, line, _) local self = TSHighlighter.active[buf] if not self then @@ -252,6 +299,9 @@ function TSHighlighter._on_line(_, _win, buf, line, _) end ---@private +---@param buf integer +---@param srow integer +---@param erow integer function TSHighlighter._on_spell_nav(_, _, buf, srow, _, erow, _) local self = TSHighlighter.active[buf] if not self then @@ -266,27 +316,22 @@ function TSHighlighter._on_spell_nav(_, _, buf, srow, _, erow, _) end ---@private -function TSHighlighter._on_buf(_, buf) - local self = TSHighlighter.active[buf] - if self then - self.tree:parse() - end -end - ----@private -function TSHighlighter._on_win(_, _win, buf, _topline) +---@param _win integer +---@param buf integer +---@param topline integer +---@param botline integer +function TSHighlighter._on_win(_, _win, buf, topline, botline) local self = TSHighlighter.active[buf] if not self then return false end - + self.tree:parse({ topline, botline + 1 }) self:reset_highlight_state() self.redraw_count = self.redraw_count + 1 return true end -a.nvim_set_decoration_provider(ns, { - on_buf = TSHighlighter._on_buf, +api.nvim_set_decoration_provider(ns, { on_win = TSHighlighter._on_win, on_line = TSHighlighter._on_line, _on_spell_nav = TSHighlighter._on_spell_nav, |