diff options
author | Josh Rahm <rahm@google.com> | 2022-10-11 19:00:34 +0000 |
---|---|---|
committer | Josh Rahm <rahm@google.com> | 2022-10-11 19:00:34 +0000 |
commit | c367400b73d207833d51e09d663f969ffab37531 (patch) | |
tree | bc26006d942509a92b514107f9d8dca6d3911128 /runtime/lua/vim/treesitter.lua | |
parent | 4066fa85abef16fa23c30e94dc4d2bfb3b9c4545 (diff) | |
parent | 760b399f6c0c6470daa0663752bd22886997f9e6 (diff) | |
download | rneovim-c367400b73d207833d51e09d663f969ffab37531.tar.gz rneovim-c367400b73d207833d51e09d663f969ffab37531.tar.bz2 rneovim-c367400b73d207833d51e09d663f969ffab37531.zip |
Merge remote-tracking branch 'upstream/master' into colorcolchar
Diffstat (limited to 'runtime/lua/vim/treesitter.lua')
-rw-r--r-- | runtime/lua/vim/treesitter.lua | 188 |
1 files changed, 147 insertions, 41 deletions
diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 6431162799..6cd00516bf 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -3,10 +3,7 @@ local query = require('vim.treesitter.query') local language = require('vim.treesitter.language') local LanguageTree = require('vim.treesitter.languagetree') --- TODO(bfredl): currently we retain parsers for the lifetime of the buffer. --- Consider use weak references to release parser if all plugins are done with --- it. -local parsers = {} +local parsers = setmetatable({}, { __mode = 'v' }) local M = vim.tbl_extend('error', query, language) @@ -28,13 +25,15 @@ setmetatable(M, { end, }) ---- Creates a new parser. +--- Creates a new parser --- ---- It is not recommended to use this, use vim.treesitter.get_parser() instead. +--- It is not recommended to use this; use |get_parser()| instead. --- ----@param bufnr The buffer the parser will be tied to ----@param lang The language of the parser ----@param opts Options to pass to the created language tree +---@param bufnr string Buffer the parser will be tied to (0 for current buffer) +---@param lang string Language of the parser +---@param opts (table|nil) Options to pass to the created language tree +--- +---@return LanguageTree |LanguageTree| object to use for parsing function M._create_parser(bufnr, lang, opts) language.require_language(lang) if bufnr == 0 then @@ -74,16 +73,15 @@ function M._create_parser(bufnr, lang, opts) return self end ---- Gets the parser for this bufnr / ft combination. +--- Returns the parser for a specific buffer and filetype and attaches it to the buffer --- ---- If needed this will create the parser. ---- Unconditionally attach the provided callback +--- If needed, this will create the parser. --- ----@param bufnr The buffer the parser should be tied to ----@param lang The filetype of this parser ----@param opts Options object to pass to the created language tree +---@param bufnr (number|nil) Buffer the parser should be tied to (default: current buffer) +---@param lang (string|nil) Filetype of this parser (default: buffer filetype) +---@param opts (table|nil) Options to pass to the created language tree --- ----@returns The parser +---@return LanguageTree |LanguageTree| object to use for parsing function M.get_parser(bufnr, lang, opts) opts = opts or {} @@ -103,11 +101,13 @@ function M.get_parser(bufnr, lang, opts) return parsers[bufnr] end ---- Gets a string parser +--- Returns a string parser +--- +---@param str string Text to parse +---@param lang string Language of this string +---@param opts (table|nil) Options to pass to the created language tree --- ----@param str The string to parse ----@param lang The language of this string ----@param opts Options to pass to the created language tree +---@return LanguageTree |LanguageTree| object to use for parsing function M.get_string_parser(str, lang, opts) vim.validate({ str = { str, 'string' }, @@ -120,10 +120,10 @@ end --- Determines whether a node is the ancestor of another --- ----@param dest table the possible ancestor ----@param source table the possible descendant node +---@param dest userdata Possible ancestor |tsnode| +---@param source userdata Possible descendant |tsnode| --- ----@returns (boolean) True if dest is an ancestor of source +---@return boolean True if {dest} is an ancestor of {source} function M.is_ancestor(dest, source) if not (dest and source) then return false @@ -141,11 +141,11 @@ function M.is_ancestor(dest, source) return false end ---- Get the node's range or unpack a range table +--- Returns the node's range or an unpacked range table --- ----@param node_or_range table +---@param node_or_range (userdata|table) |tsnode| or table of positions --- ----@returns start_row, start_col, end_row, end_col +---@return table `{ start_row, start_col, end_row, end_col }` function M.get_node_range(node_or_range) if type(node_or_range) == 'table' then return unpack(node_or_range) @@ -154,11 +154,13 @@ function M.get_node_range(node_or_range) end end ----Determines whether (line, col) position is in node range +--- Determines whether (line, col) position is in node range +--- +---@param node userdata |tsnode| defining the range +---@param line number Line (0-based) +---@param col number Column (0-based) --- ----@param node Node defining the range ----@param line A line (0-based) ----@param col A column (0-based) +---@return boolean True if the position is in node range function M.is_in_node_range(node, line, col) local start_line, start_col, end_line, end_col = M.get_node_range(node) if line >= start_line and line <= end_line then @@ -176,11 +178,12 @@ function M.is_in_node_range(node, line, col) end end ----Determines if a node contains a range ----@param node table The node ----@param range table The range +--- Determines if a node contains a range --- ----@returns (boolean) True if the node contains the range +---@param node userdata |tsnode| +---@param range table +--- +---@return boolean True if the {node} contains the {range} function M.node_contains(node, range) local start_row, start_col, end_row, end_col = node:range() local start_fits = start_row < range[1] or (start_row == range[1] and start_col <= range[2]) @@ -189,13 +192,17 @@ function M.node_contains(node, range) return start_fits and end_fits end ----Gets a list of captures for a given cursor position ----@param bufnr number The buffer number ----@param row number The position row ----@param col number The position column +--- Returns a list of highlight captures at the given position +--- +--- Each capture is represented by a table containing the capture name as a string as +--- well as a table of metadata (`priority`, `conceal`, ...; empty if none are defined). +--- +---@param bufnr number Buffer number (0 for current buffer) +---@param row number Position row +---@param col number Position column --- ----@returns (table) A table of captures -function M.get_captures_at_position(bufnr, row, col) +---@return table[] List of captures `{ capture = "capture name", metadata = { ... } }` +function M.get_captures_at_pos(bufnr, row, col) if bufnr == 0 then bufnr = a.nvim_get_current_buf() end @@ -233,7 +240,7 @@ function M.get_captures_at_position(bufnr, row, col) if M.is_in_node_range(node, row, col) then local c = q._query.captures[capture] -- name of the capture in the query if c ~= nil then - table.insert(matches, { capture = c, priority = metadata.priority }) + table.insert(matches, { capture = c, metadata = metadata }) end end end @@ -241,4 +248,103 @@ function M.get_captures_at_position(bufnr, row, col) return matches end +--- Returns a list of highlight capture names under the cursor +--- +---@param winnr (number|nil) Window handle or 0 for current window (default) +--- +---@return string[] List of capture names +function M.get_captures_at_cursor(winnr) + winnr = winnr or 0 + local bufnr = a.nvim_win_get_buf(winnr) + local cursor = a.nvim_win_get_cursor(winnr) + + local data = M.get_captures_at_pos(bufnr, cursor[1] - 1, cursor[2]) + + local captures = {} + + for _, capture in ipairs(data) do + table.insert(captures, capture.capture) + end + + return captures +end + +--- Returns the smallest named node at the given position +--- +---@param bufnr number Buffer number (0 for current buffer) +---@param row number Position row +---@param col number Position column +---@param opts table Optional keyword arguments: +--- - ignore_injections boolean Ignore injected languages (default true) +--- +---@return userdata |tsnode| under the cursor +function M.get_node_at_pos(bufnr, row, col, opts) + if bufnr == 0 then + bufnr = a.nvim_get_current_buf() + end + local ts_range = { row, col, row, col } + + local root_lang_tree = M.get_parser(bufnr) + if not root_lang_tree then + return + end + + return root_lang_tree:named_node_for_range(ts_range, opts) +end + +--- Returns the smallest named node under the cursor +--- +---@param winnr (number|nil) Window handle or 0 for current window (default) +--- +---@return string Name of node under the cursor +function M.get_node_at_cursor(winnr) + winnr = winnr or 0 + local bufnr = a.nvim_win_get_buf(winnr) + local cursor = a.nvim_win_get_cursor(winnr) + + return M.get_node_at_pos(bufnr, cursor[1] - 1, cursor[2], { ignore_injections = false }):type() +end + +--- Starts treesitter highlighting for a buffer +--- +--- Can be used in an ftplugin or FileType autocommand. +--- +--- Note: By default, disables regex syntax highlighting, which may be required for some plugins. +--- In this case, add ``vim.bo.syntax = 'on'`` after the call to `start`. +--- +--- Example: +--- <pre> +--- vim.api.nvim_create_autocmd( 'FileType', { pattern = 'tex', +--- callback = function(args) +--- vim.treesitter.start(args.buf, 'latex') +--- vim.bo[args.buf].syntax = 'on' -- only if additional legacy syntax is needed +--- end +--- }) +--- </pre> +--- +---@param bufnr (number|nil) Buffer to be highlighted (default: current buffer) +---@param lang (string|nil) Language of the parser (default: buffer filetype) +function M.start(bufnr, lang) + bufnr = bufnr or a.nvim_get_current_buf() + + local parser = M.get_parser(bufnr, lang) + + M.highlighter.new(parser) + + vim.b[bufnr].ts_highlight = true +end + +--- Stops treesitter highlighting for a buffer +--- +---@param bufnr (number|nil) Buffer to stop highlighting (default: current buffer) +function M.stop(bufnr) + bufnr = bufnr or a.nvim_get_current_buf() + + if M.highlighter.active[bufnr] then + M.highlighter.active[bufnr]:destroy() + end + + vim.bo[bufnr].syntax = 'on' +end + return M |