diff options
author | Björn Linse <bjorn.linse@gmail.com> | 2019-09-28 14:27:20 +0200 |
---|---|---|
committer | Björn Linse <bjorn.linse@gmail.com> | 2019-12-22 12:51:46 +0100 |
commit | 440695c29696f261337227e5c419aa1cf313c2dd (patch) | |
tree | 0baea84a9ea41db8a13de86758ccc3afe8d95793 /runtime/lua/vim/treesitter.lua | |
parent | c21511b2f48685461bf2655b28eff4434c91d449 (diff) | |
download | rneovim-440695c29696f261337227e5c419aa1cf313c2dd.tar.gz rneovim-440695c29696f261337227e5c419aa1cf313c2dd.tar.bz2 rneovim-440695c29696f261337227e5c419aa1cf313c2dd.zip |
tree-sitter: implement query functionality and highlighting prototype [skip.lint]
Diffstat (limited to 'runtime/lua/vim/treesitter.lua')
-rw-r--r-- | runtime/lua/vim/treesitter.lua | 120 |
1 files changed, 112 insertions, 8 deletions
diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index e0202927bb..aa8b8fcdd1 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -12,9 +12,13 @@ function Parser:parse() if self.valid then return self.tree end - self.tree = self._parser:parse_buf(self.bufnr) + local changes + self.tree, changes = self._parser:parse_buf(self.bufnr) self.valid = true - return self.tree + for _, cb in ipairs(self.change_cbs) do + cb(changes) + end + return self.tree, changes end function Parser:_on_lines(bufnr, _, start_row, old_stop_row, stop_row, old_byte_size) @@ -26,17 +30,28 @@ function Parser:_on_lines(bufnr, _, start_row, old_stop_row, stop_row, old_byte_ self.valid = false end -local module = { +local M = { add_language=vim._ts_add_language, inspect_language=vim._ts_inspect_language, + parse_query = vim._ts_parse_query, } -function module.create_parser(bufnr, ft, id) +setmetatable(M, { + __index = function (t, k) + if k == "TSHighlighter" then + t[k] = require'vim.tshighlighter' + return t[k] + end + end + }) + +function M.create_parser(bufnr, ft, id) if bufnr == 0 then bufnr = a.nvim_get_current_buf() end - local self = setmetatable({bufnr=bufnr, valid=false}, Parser) + local self = setmetatable({bufnr=bufnr, lang=ft, valid=false}, Parser) self._parser = vim._create_ts_parser(ft) + self.change_cbs = {} self:parse() -- TODO(bfredl): use weakref to self, so that the parser is free'd is no plugin is -- using it. @@ -55,7 +70,7 @@ function module.create_parser(bufnr, ft, id) return self end -function module.get_parser(bufnr, ft) +function M.get_parser(bufnr, ft, cb) if bufnr == nil or bufnr == 0 then bufnr = a.nvim_get_current_buf() end @@ -65,9 +80,98 @@ function module.get_parser(bufnr, ft) local id = tostring(bufnr)..'_'..ft if parsers[id] == nil then - parsers[id] = module.create_parser(bufnr, ft, id) + parsers[id] = M.create_parser(bufnr, ft, id) + end + if cb ~= nil then + table.insert(parsers[id].change_cbs, cb) end return parsers[id] end -return module +-- query: pattern matching on trees +-- predicate matching is implemented in lua +local Query = {} +Query.__index = Query + +function M.parse_query(lang, query) + local self = setmetatable({}, Query) + self.query = vim._ts_parse_query(lang, query) + self.info = self.query:inspect() + self.captures = self.info.captures + return self +end + +local function get_node_text(node, bufnr) + local start_row, start_col, end_row, end_col = node:range() + if start_row ~= end_row then + return nil + 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 + +local function match_preds(match, preds, bufnr) + for _, pred in pairs(preds) do + if pred[1] == "eq?" then + local node = match[pred[2]] + local node_text = get_node_text(node, bufnr) + + local str + if type(pred[3]) == "string" then + -- (eq? @aa "foo") + str = pred[3] + else + -- (eq? @aa @bb) + str = get_node_text(match[pred[3]], bufnr) + end + + if node_text ~= str or str == nil then + return false + end + else + return false + end + end + return true +end + +function Query:iter_captures(node, bufnr, start, stop) + if bufnr == 0 then + bufnr = 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 preds = self.info.patterns[match.pattern] + local active = match_preds(match, preds, bufnr) + match.active = active + if not active then + return iter() -- tail call: try next match + end + end + return capture, captured_node + end + return iter +end + +function Query:iter_matches(node, bufnr, start, stop) + if bufnr == 0 then + bufnr = 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 preds = self.info.patterns[pattern] + local active = (not preds) or match_preds(match, preds, bufnr) + if not active then + return iter() -- tail call: try next match + end + end + return pattern, match + end + return iter +end + +return M |