diff options
Diffstat (limited to 'runtime/lua/vim/treesitter')
-rw-r--r-- | runtime/lua/vim/treesitter/highlighter.lua | 84 | ||||
-rw-r--r-- | runtime/lua/vim/treesitter/query.lua | 56 |
2 files changed, 96 insertions, 44 deletions
diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua index 718088e0ad..5b964a6020 100644 --- a/runtime/lua/vim/treesitter/highlighter.lua +++ b/runtime/lua/vim/treesitter/highlighter.lua @@ -3,7 +3,8 @@ local a = vim.api -- support reload for quick experimentation local TSHighlighter = rawget(vim.treesitter, 'TSHighlighter') or {} TSHighlighter.__index = TSHighlighter -local ts_hs_ns = a.nvim_create_namespace("treesitter_hl") + +TSHighlighter.active = TSHighlighter.active or {} -- These are conventions defined by tree-sitter, though it -- needs to be user extensible also. @@ -54,13 +55,16 @@ TSHighlighter.hl_map = { } function TSHighlighter.new(query, bufnr, ft) + if bufnr == nil or bufnr == 0 then + bufnr = a.nvim_get_current_buf() + end + local self = setmetatable({}, TSHighlighter) self.parser = vim.treesitter.get_parser( bufnr, ft, { on_changedtree = function(...) self:on_changedtree(...) end, - on_bytes = function() self.parser:parse() end } ) @@ -69,8 +73,12 @@ function TSHighlighter.new(query, bufnr, ft) self.edit_count = 0 self.redraw_count = 0 self.line_count = {} + self.root = self.parser:parse():root() a.nvim_buf_set_option(self.buf, "syntax", "") + -- TODO(bfredl): can has multiple highlighters per buffer???? + TSHighlighter.active[bufnr] = self + -- Tricky: if syntax hasn't been enabled, we need to reload color scheme -- but use synload.vim rather than syntax.vim to not enable -- syntax FileType autocmds. Later on we should integrate with the @@ -100,6 +108,12 @@ function TSHighlighter:get_hl_from_capture(capture) end end +function TSHighlighter:on_changedtree(changes) + for _, ch in ipairs(changes or {}) do + a.nvim__buf_redraw_range(self.buf, ch[1], ch[3]+1) + end +end + function TSHighlighter:set_query(query) if type(query) == "string" then query = vim.treesitter.parse_query(self.parser.lang, query) @@ -123,28 +137,60 @@ function TSHighlighter:set_query(query) end }) - self:on_changedtree({{self.parser:parse():root():range()}}) + a.nvim__buf_redraw_range(self.buf, 0, a.nvim_buf_line_count(self.buf)) end -function TSHighlighter:on_changedtree(changes) - -- Get a fresh root - local root = self.parser:parse():root() +function TSHighlighter._on_line(_, _win, buf, line) + -- on_line is only called when this is non-nil + local self = TSHighlighter.active[buf] + if self.root == nil then + return -- parser bought the farm already + end - for _, ch in ipairs(changes or {}) do - a.nvim_buf_clear_namespace(self.buf, ts_hs_ns, ch[1], ch[3]+1) - - for capture, node in self.query:iter_captures(root, self.buf, ch[1], ch[3] + 1) do - local start_row, start_col, end_row, end_col = node:range() - local hl = self.hl_cache[capture] - if hl then - a.nvim_buf_set_extmark(self.buf, ts_hs_ns, start_row, start_col, { - end_col = end_col, - end_line = end_row, - hl_group = hl - }) - end + if self.iter == nil then + self.iter = self.query:iter_captures(self.root,buf,line,self.botline) + end + while line >= self.nextrow do + local capture, node = self.iter() + if capture == nil then + break + end + local start_row, start_col, end_row, end_col = node:range() + local hl = self.hl_cache[capture] + if hl and end_row >= line then + a.nvim__put_attr(start_row, start_col, { end_line = end_row, end_col = end_col, hl_group = hl }) + end + if start_row > line then + self.nextrow = start_row end end end +function TSHighlighter._on_start(_, buf, _tick) + local self = TSHighlighter.active[buf] + if self then + local tree = self.parser:parse() + self.root = (tree and tree:root()) or nil + end +end + +function TSHighlighter._on_win(_, _win, buf, _topline, botline) + local self = TSHighlighter.active[buf] + if not self then + return false + end + + self.iter = nil + self.nextrow = 0 + self.botline = botline + self.redraw_count = self.redraw_count + 1 + return true +end + +a.nvim__set_luahl { + on_start = TSHighlighter._on_start; + on_win = TSHighlighter._on_win; + on_line = TSHighlighter._on_line; +} + return TSHighlighter diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua index ca27a50c6a..494fb59fa7 100644 --- a/runtime/lua/vim/treesitter/query.lua +++ b/runtime/lua/vim/treesitter/query.lua @@ -28,21 +28,27 @@ end --- Gets the text corresponding to a given node -- @param node the node -- @param bufnr the buffer from which the node in extracted. -function M.get_node_text(node, bufnr) - local start_row, start_col, end_row, end_col = node:range() - if start_row ~= end_row then - return nil +function M.get_node_text(node, source) + local start_row, start_col, start_byte = node:start() + local end_row, end_col, end_byte = node:end_() + + if type(source) == "number" then + if start_row ~= end_row then + return nil + end + local line = a.nvim_buf_get_lines(source, start_row, start_row+1, true)[1] + return string.sub(line, start_col+1, end_col) + elseif type(source) == "string" then + return source:sub(start_byte+1, end_byte) end - local line = a.nvim_buf_get_lines(bufnr, start_row, start_row+1, true)[1] - return string.sub(line, start_col+1, end_col) end -- Predicate handler receive the following arguments -- (match, pattern, bufnr, predicate) local predicate_handlers = { - ["eq?"] = function(match, _, bufnr, predicate) + ["eq?"] = function(match, _, source, predicate) local node = match[predicate[2]] - local node_text = M.get_node_text(node, bufnr) + local node_text = M.get_node_text(node, source) local str if type(predicate[3]) == "string" then @@ -50,7 +56,7 @@ local predicate_handlers = { str = predicate[3] else -- (#eq? @aa @bb) - str = M.get_node_text(match[predicate[3]], bufnr) + str = M.get_node_text(match[predicate[3]], source) end if node_text ~= str or str == nil then @@ -60,7 +66,7 @@ local predicate_handlers = { return true end, - ["lua-match?"] = function(match, _, bufnr, predicate) + ["lua-match?"] = function(match, _, source, predicate) local node = match[predicate[2]] local regex = predicate[3] local start_row, _, end_row, _ = node:range() @@ -68,7 +74,7 @@ local predicate_handlers = { return false end - return string.find(M.get_node_text(node, bufnr), regex) + return string.find(M.get_node_text(node, source), regex) end, ["match?"] = (function() @@ -88,7 +94,7 @@ local predicate_handlers = { end }) - return function(match, _, bufnr, pred) + return function(match, _, source, pred) local node = match[pred[2]] local start_row, start_col, end_row, end_col = node:range() if start_row ~= end_row then @@ -96,13 +102,13 @@ local predicate_handlers = { end local regex = compiled_vim_regexes[pred[3]] - return regex:match_line(bufnr, start_row, start_col, end_col) + return regex:match_line(source, start_row, start_col, end_col) end end)(), - ["contains?"] = function(match, _, bufnr, predicate) + ["contains?"] = function(match, _, source, predicate) local node = match[predicate[2]] - local node_text = M.get_node_text(node, bufnr) + local node_text = M.get_node_text(node, source) for i=3,#predicate do if string.find(node_text, predicate[i], 1, true) then @@ -139,7 +145,7 @@ local function xor(x, y) return (x or y) and not (x and y) end -function Query:match_preds(match, pattern, bufnr) +function Query:match_preds(match, pattern, source) local preds = self.info.patterns[pattern] for _, pred in pairs(preds or {}) do @@ -164,7 +170,7 @@ function Query:match_preds(match, pattern, bufnr) return false end - local pred_matches = handler(match, pattern, bufnr, pred) + local pred_matches = handler(match, pattern, source, pred) if not xor(is_not, pred_matches) then return false @@ -182,15 +188,15 @@ end -- -- @returns The matching capture id -- @returns The captured node -function Query:iter_captures(node, bufnr, start, stop) - if bufnr == 0 then - bufnr = vim.api.nvim_get_current_buf() +function Query:iter_captures(node, source, start, stop) + if type(source) == "number" and source == 0 then + source = vim.api.nvim_get_current_buf() end local raw_iter = node:_rawquery(self.query, true, start, stop) local function iter() local capture, captured_node, match = raw_iter() if match ~= nil then - local active = self:match_preds(match, match.pattern, bufnr) + local active = self:match_preds(match, match.pattern, source) match.active = active if not active then return iter() -- tail call: try next match @@ -210,15 +216,15 @@ end -- -- @returns The matching pattern id -- @returns The matching match -function Query:iter_matches(node, bufnr, start, stop) - if bufnr == 0 then - bufnr = vim.api.nvim_get_current_buf() +function Query:iter_matches(node, source, start, stop) + if type(source) == "number" and source == 0 then + source = vim.api.nvim_get_current_buf() end local raw_iter = node:_rawquery(self.query, false, start, stop) local function iter() local pattern, match = raw_iter() if match ~= nil then - local active = self:match_preds(match, pattern, bufnr) + local active = self:match_preds(match, pattern, source) if not active then return iter() -- tail call: try next match end |