style: fill in missing code docs wherever applicable

This commit is contained in:
Amaan Qureshi 2023-02-24 03:07:52 -05:00 committed by Stephan Seitz
parent fceb4ed0ec
commit 853b1ab39a
17 changed files with 431 additions and 191 deletions

View file

@ -2,20 +2,26 @@ local api = vim.api
local M = {}
--- Creates a cache table for buffers keyed by a type name.
--- Cache entries attach to the buffer and cleanup entries
--- as buffers are detached.
-- Creates a cache table for buffers keyed by a type name.
-- Cache entries attach to the buffer and cleanup entries
-- as buffers are detached.
function M.create_buffer_cache()
local cache = {}
---@type table<string, table<string, any>>
local items = setmetatable({}, {
__index = function(tbl, key)
rawset(tbl, key, {})
return rawget(tbl, key)
end,
})
---@type table<integer|string, boolean>
local loaded_buffers = {}
---@param type_name string
---@param bufnr integer
---@param value any
function cache.set(type_name, bufnr, value)
if not loaded_buffers[bufnr] then
loaded_buffers[bufnr] = true
@ -34,18 +40,27 @@ function M.create_buffer_cache()
items[tostring(bufnr)][type_name] = value
end
---@param type_name string
---@param bufnr integer
---@return any
function cache.get(type_name, bufnr)
return items[tostring(bufnr)][type_name]
end
---@param type_name string
---@param bufnr integer
---@return boolean
function cache.has(type_name, bufnr)
return cache.get(type_name, bufnr) ~= nil
end
---@param type_name string
---@param bufnr integer
function cache.remove(type_name, bufnr)
items[tostring(bufnr)][type_name] = nil
end
---@param bufnr integer
function cache.clear_buffer(bufnr)
items[tostring(bufnr)] = nil
end

View file

@ -27,7 +27,9 @@ local config = {
update_strategy = "lockfile",
parser_install_dir = nil,
}
-- List of modules that need to be setup on initialization.
---@type TSModule[][]
local queued_modules_defs = {}
-- Whether we've initialized the plugin yet.
local is_initialized = false
@ -36,10 +38,12 @@ local is_initialized = false
---@field module_path string
---@field enable boolean|string[]|function(string): boolean
---@field disable boolean|string[]|function(string): boolean
---@field keymaps table<string, string>
---@field is_supported function(string): boolean
---@field attach function(string)
---@field detach function(string)
---@field enabled_buffers table<integer, boolean>
---@field additional_vim_regex_highlighting boolean|string[]
---@type {[string]: TSModule}
local builtin_modules = {
@ -248,7 +252,7 @@ local function recurse_modules(accumulator, root, path)
end
end
---Shows current configuration of all nvim-treesitter modules
-- Shows current configuration of all nvim-treesitter modules
---@param process_function function used as the `process` parameter
--- for vim.inspect (https://github.com/kikito/inspect.lua#optionsprocess)
local function config_info(process_function)
@ -369,7 +373,7 @@ M.commands = {
---@param mod string module
---@param lang string the language of the buffer
---@param bufnr integer the bufnr
---@param bufnr integer the buffer
function M.is_enabled(mod, lang, bufnr)
if not parsers.has_parser(lang) then
return false
@ -438,8 +442,8 @@ function M.setup(user_data)
end, config.modules)
end
---Defines a table of modules that can be attached/detached to buffers
---based on language support. A module consist of the following properties:
-- Defines a table of modules that can be attached/detached to buffers
-- based on language support. A module consist of the following properties:
---* @enable Whether the modules is enabled. Can be true or false.
---* @disable A list of languages to disable the module for. Only relevant if enable is true.
---* @keymaps A list of user mappings for a given module if relevant.
@ -451,9 +455,11 @@ end
--- if a `module_path` is not specified.
---* @detach A detach function that is called for each buffer that the module is enabled for. This is required
--- if a `module_path` is not specified.
---Modules are not setup until `init` is invoked by the plugin. This allows modules to be defined in any order
---and can be loaded lazily.
---@example
--
-- Modules are not setup until `init` is invoked by the plugin. This allows modules to be defined in any order
-- and can be loaded lazily.
--
---* @example
---require"nvim-treesitter".define_modules {
--- my_cool_module = {
--- attach = function()
@ -493,7 +499,7 @@ end
---Attaches a module to a buffer
---@param mod_name string the module name
---@param bufnr integer the bufnr
---@param bufnr integer the buffer
---@param lang string the language of the buffer
function M.attach_module(mod_name, bufnr, lang)
bufnr = bufnr or api.nvim_get_current_buf()
@ -506,9 +512,9 @@ function M.attach_module(mod_name, bufnr, lang)
end
end
---Detaches a module to a buffer
-- Detaches a module to a buffer
---@param mod_name string the module name
---@param bufnr integer the bufnr
---@param bufnr integer the buffer
function M.detach_module(mod_name, bufnr)
local resolved_mod = resolve_module(mod_name)
bufnr = bufnr or api.nvim_get_current_buf()
@ -519,17 +525,18 @@ function M.detach_module(mod_name, bufnr)
end
end
---Same as attach_module, but if the module is already attached, detach it first.
-- Same as attach_module, but if the module is already attached, detach it first.
---@param mod_name string the module name
---@param bufnr integer the bufnr
---@param bufnr integer the buffer
---@param lang string the language of the buffer
function M.reattach_module(mod_name, bufnr, lang)
M.detach_module(mod_name, bufnr)
M.attach_module(mod_name, bufnr, lang)
end
---Gets available modules
-- Gets available modules
---@param root {[string]:TSModule}|nil table to find modules
---@return string[] modules list of module paths
function M.available_modules(root)
local modules = {}
@ -542,24 +549,24 @@ end
---Gets a module config by path
---@param mod_path string path to the module
---@return TSModule|nil the module or nil
---@return TSModule|nil: the module or nil
function M.get_module(mod_path)
local mod = utils.get_at_path(config.modules, mod_path)
return M.is_module(mod) and mod or nil
end
---Determines whether the provided table is a module.
---A module should contain an attach and detach function.
---@param mod table the module table
-- Determines whether the provided table is a module.
-- A module should contain an attach and detach function.
---@param mod table|nil the module table
---@return boolean
function M.is_module(mod)
return type(mod) == "table"
and ((type(mod.attach) == "function" and type(mod.detach) == "function") or type(mod.module_path) == "string")
end
---Initializes built-in modules and any queued modules
---registered by plugins or the user.
-- Initializes built-in modules and any queued modules
-- registered by plugins or the user.
function M.init()
is_initialized = true
M.define_modules(builtin_modules)
@ -569,22 +576,17 @@ function M.init()
end
end
---If parser_install_dir is not nil is used or created.
---If parser_install_dir is nil try the package dir of the nvim-treesitter
---plugin first, followed by the "site" dir from "runtimepath". "site" dir will
---be created if it doesn't exist. Using only the package dir won't work when
---the plugin is installed with Nix, since the "/nix/store" is read-only.
-- If parser_install_dir is not nil is used or created.
-- If parser_install_dir is nil try the package dir of the nvim-treesitter
-- plugin first, followed by the "site" dir from "runtimepath". "site" dir will
-- be created if it doesn't exist. Using only the package dir won't work when
-- the plugin is installed with Nix, since the "/nix/store" is read-only.
---@param folder_name string|nil
---@return string|nil, string|nil
function M.get_parser_install_dir(folder_name)
folder_name = folder_name or "parser"
local install_dir
if config.parser_install_dir then
install_dir = config.parser_install_dir
else
install_dir = utils.get_package_path()
end
local install_dir = config.parser_install_dir or utils.get_package_path()
local parser_dir = utils.join_path(install_dir, folder_name)
return utils.create_or_reuse_writable_dir(

View file

@ -31,7 +31,10 @@ local folds_levels = tsutils.memoize_by_buf_tick(function(bufnr)
end)
-- start..stop is an inclusive range
---@type table<number, number>
local start_counts = {}
---@type table<number, number>
local stop_counts = {}
local prev_start = -1
@ -40,11 +43,11 @@ local folds_levels = tsutils.memoize_by_buf_tick(function(bufnr)
local min_fold_lines = api.nvim_win_get_option(0, "foldminlines")
for _, match in ipairs(matches) do
local start, stop, stop_col
local start, stop, stop_col ---@type integer, integer, integer
if match.metadata and match.metadata.range then
start, _, stop, stop_col = unpack(match.metadata.range)
start, _, stop, stop_col = unpack(match.metadata.range) ---@type integer, integer, integer, integer
else
start, _, stop, stop_col = match.node:range()
start, _, stop, stop_col = match.node:range() ---@type integer, integer, integer, integer
end
if stop_col == 0 then
@ -65,6 +68,7 @@ local folds_levels = tsutils.memoize_by_buf_tick(function(bufnr)
end
end
---@type string[]
local levels = {}
local current_level = 0

View file

@ -2,13 +2,14 @@ local configs = require "nvim-treesitter.configs"
local M = {}
---@param config table
---@param config TSModule
---@param lang string
---@return boolean
local function should_enable_vim_regex(config, lang)
local additional_hl = config.additional_vim_regex_highlighting
local is_table = type(additional_hl) == "table"
---@diagnostic disable-next-line: param-type-mismatch
return additional_hl and (not is_table or vim.tbl_contains(additional_hl, lang))
end

View file

@ -8,6 +8,7 @@ local queries = require "nvim-treesitter.query"
local M = {}
---@type table<integer, table<TSNode|nil>>
local selections = {}
function M.init_selection()
@ -17,14 +18,15 @@ function M.init_selection()
ts_utils.update_selection(buf, node)
end
--- Get the range of the current visual selection.
-- Get the range of the current visual selection.
--
-- The range start with 1 and the ending is inclusive.
-- The range starts with 1 and the ending is inclusive.
---@return integer, integer, integer, integer
local function visual_selection_range()
local _, csrow, cscol, _ = unpack(vim.fn.getpos "'<")
local _, cerow, cecol, _ = unpack(vim.fn.getpos "'>")
local _, csrow, cscol, _ = unpack(vim.fn.getpos "'<") ---@type integer, integer, integer, integer
local _, cerow, cecol, _ = unpack(vim.fn.getpos "'>") ---@type integer, integer, integer, integer
local start_row, start_col, end_row, end_col
local start_row, start_col, end_row, end_col ---@type integer, integer, integer, integer
if csrow < cerow or (csrow == cerow and cscol <= cecol) then
start_row = csrow
@ -41,12 +43,16 @@ local function visual_selection_range()
return start_row, start_col, end_row, end_col
end
---@param node TSNode
---@return boolean
local function range_matches(node)
local csrow, cscol, cerow, cecol = visual_selection_range()
local srow, scol, erow, ecol = ts_utils.get_vim_range { node:range() }
return srow == csrow and scol == cscol and erow == cerow and ecol == cecol
end
---@param get_parent fun(node: TSNode): TSNode|nil
---@return fun():nil
local function select_incremental(get_parent)
return function()
local buf = api.nvim_get_current_buf()
@ -67,7 +73,7 @@ local function select_incremental(get_parent)
end
-- Find a node that changes the current selection.
local node = nodes[#nodes]
local node = nodes[#nodes] ---@type TSNode
while true do
local parent = get_parent(node)
if not parent or parent == node then
@ -116,7 +122,7 @@ function M.node_decremental()
end
table.remove(selections[buf])
local node = nodes[#nodes]
local node = nodes[#nodes] ---@type TSNode
ts_utils.update_selection(buf, node)
end
@ -127,14 +133,16 @@ local FUNCTION_DESCRIPTIONS = {
node_decremental = "Shrink selection to previous named node",
}
---@param bufnr integer
function M.attach(bufnr)
local config = configs.get_module "incremental_selection"
for funcname, mapping in pairs(config.keymaps) do
if mapping then
local mode
local rhs
---@type string, string|function
local mode, rhs
if funcname == "init_selection" then
mode = "n"
---@type function
rhs = M[funcname]
else
mode = "x"

View file

@ -15,16 +15,27 @@ M.comment_parsers = {
phpdoc = true,
}
---@param root TSNode
---@param lnum integer
---@return TSNode
local function get_first_node_at_line(root, lnum)
local col = vim.fn.indent(lnum)
return root:descendant_for_range(lnum - 1, col, lnum - 1, col)
end
---@param root TSNode
---@param lnum integer
---@return TSNode
local function get_last_node_at_line(root, lnum)
local col = #vim.fn.getline(lnum) - 1
return root:descendant_for_range(lnum - 1, col, lnum - 1, col)
end
---@param bufnr integer
---@param node TSNode
---@param delimiter string
---@return TSNode|nil child
---@return boolean|nil is_end
local function find_delimiter(bufnr, node, delimiter)
for child, _ in node:iter_children() do
if child:type() == delimiter then
@ -77,7 +88,7 @@ function M.get_indent(lnum)
end
-- Get language tree with smallest range around node that's not a comment parser
local root, lang_tree
local root, lang_tree ---@type TSNode, LanguageTree
parser:for_each_tree(function(tstree, tree)
if not tstree or M.comment_parsers[tree:lang()] then
return
@ -98,7 +109,7 @@ function M.get_indent(lnum)
local q = get_indents(vim.api.nvim_get_current_buf(), root, lang_tree:lang())
local is_empty_line = string.match(vim.fn.getline(lnum), "^%s*$") ~= nil
local node
local node ---@type TSNode
if is_empty_line then
local prevlnum = vim.fn.prevnonblank(lnum)
node = get_last_node_at_line(root, prevlnum)
@ -171,8 +182,9 @@ function M.get_indent(lnum)
-- do not indent for nodes that starts-and-ends on same line and starts on target line (lnum)
if q.aligned_indent[node:id()] and srow ~= erow and (srow ~= lnum - 1) then
local metadata = q.aligned_indent[node:id()]
local o_delim_node, is_last_in_line
local o_delim_node, is_last_in_line ---@type TSNode|nil, boolean|nil
if metadata.delimiter then
---@type string
local opening_delimiter = metadata.delimiter and metadata.delimiter:sub(1, 1)
o_delim_node, is_last_in_line = find_delimiter(bufnr, node, opening_delimiter)
else
@ -198,8 +210,10 @@ function M.get_indent(lnum)
return indent
end
---@type table<integer, string>
local indent_funcs = {}
---@param bufnr integer
function M.attach(bufnr)
indent_funcs[bufnr] = vim.bo.indentexpr
vim.bo.indentexpr = "nvim_treesitter#indent()"

View file

@ -31,6 +31,8 @@ end
-- {'mod1', 'mod2.sub1', 'mod2.sub2', 'mod3'}
-- ->
-- { default = {'mod1', 'mod3'}, mod2 = {'sub1', 'sub2'}}
---@param modulelist string[]
---@return table
local function namespace_modules(modulelist)
local modules = {}
for _, module in ipairs(modulelist) do
@ -50,6 +52,8 @@ local function namespace_modules(modulelist)
return modules
end
---@param list string[]
---@return integer length
local function longest_string_length(list)
local length = 0
for _, value in ipairs(list) do
@ -60,6 +64,11 @@ local function longest_string_length(list)
return length
end
---@param curbuf integer
---@param origbuf integer
---@param parserlist string[]
---@param namespace string
---@param modulelist string[]
local function append_module_table(curbuf, origbuf, parserlist, namespace, modulelist)
local maxlen_parser = longest_string_length(parserlist)
table.sort(modulelist)
@ -104,6 +113,7 @@ local function print_info_modules(parserlist, module)
modules = namespace_modules(configs.available_modules())
end
---@type string[]
local namespaces = {}
for k, _ in pairs(modules) do
table.insert(namespaces, k)
@ -150,6 +160,7 @@ local function module_info(module)
end
end
---@return string[]
function M.installed_parsers()
local installed = {}
for _, p in pairs(parsers.available_parsers()) do

View file

@ -9,6 +9,11 @@ local configs = require "nvim-treesitter.configs"
local shell = require "nvim-treesitter.shell_command_selectors"
local M = {}
---@class LockfileInfo
---@field revision string
---@type table<string, LockfileInfo>
local lockfile = {}
M.compilers = { vim.fn.getenv "CC", "cc", "gcc", "clang", "cl", "zig" }
@ -96,8 +101,8 @@ local function is_ignored_parser(lang)
return vim.tbl_contains(configs.get_ignored_parser_installs(), lang)
end
--- @param lang string
--- @return string|nil
---@param lang string
---@return string|nil
local function get_revision(lang)
if #lockfile == 0 then
load_lockfile()
@ -123,8 +128,8 @@ local function get_installed_revision(lang)
end
-- Clean path for use in a prefix comparison
-- @param input string
-- @return string
---@param input string
---@return string
local function clean_path(input)
local pth = vim.fn.fnamemodify(input, ":p")
if fn.has "win32" == 1 then
@ -133,7 +138,7 @@ local function clean_path(input)
return pth
end
---Checks if parser is installed with nvim-treesitter
-- Checks if parser is installed with nvim-treesitter
---@param lang string
---@return boolean
local function is_installed(lang)
@ -159,9 +164,9 @@ local function needs_update(lang)
return not revision or revision ~= get_installed_revision(lang)
end
---@return table
---@return string[]
local function outdated_parsers()
return vim.tbl_filter(function(lang)
return vim.tbl_filter(function(lang) ---@param lang string
return is_installed(lang) and needs_update(lang)
end, info.installed_parsers())
end
@ -252,6 +257,8 @@ function M.iter_cmd(cmd_list, i, lang, success_message)
end
end
---@param cmd Command
---@return string command
local function get_command(cmd)
local options = ""
if cmd.opts and cmd.opts.args then
@ -263,13 +270,15 @@ local function get_command(cmd)
end
end
local final = string.format("%s %s", cmd.cmd, options)
local command = string.format("%s %s", cmd.cmd, options)
if cmd.opts and cmd.opts.cwd then
final = shell.make_directory_change_for_command(cmd.opts.cwd, final)
command = shell.make_directory_change_for_command(cmd.opts.cwd, command)
end
return final
return command
end
---@param cmd_list Command[]
---@return boolean
local function iter_cmd_sync(cmd_list)
for _, cmd in ipairs(cmd_list) do
if cmd.info then
@ -311,7 +320,7 @@ local function run_install(cache_folder, install_folder, lang, repo, with_sync,
repo.url = maybe_local_path
end
-- compile_location only needed for typescript installs.
---@type string compile_location only needed for typescript installs.
local compile_location
if from_local_path then
compile_location = repo.url
@ -355,7 +364,7 @@ local function run_install(cache_folder, install_folder, lang, repo, with_sync,
local cc = shell.select_executable(M.compilers)
if not cc then
api.nvim_err_writeln('No C compiler found! "' .. table.concat(
vim.tbl_filter(function(c)
vim.tbl_filter(function(c) ---@param c string
return type(c) == "string"
end, M.compilers),
'", "'
@ -368,6 +377,17 @@ local function run_install(cache_folder, install_folder, lang, repo, with_sync,
revision = get_revision(lang)
end
---@class Command
---@field cmd string
---@field info string
---@field err string
---@field opts CmdOpts
---@class CmdOpts
---@field args string[]
---@field cwd string
---@type Command[]
local command_list = {}
if not from_local_path then
vim.list_extend(command_list, { shell.select_install_rm_cmd(cache_folder, project_name) })
@ -411,7 +431,7 @@ local function run_install(cache_folder, install_folder, lang, repo, with_sync,
shell.select_mv_cmd("parser.so", parser_lib_name, compile_location),
{
cmd = function()
vim.fn.writefile({ revision or "" }, utils.join_path(configs.get_parser_info_dir(), lang .. ".revision"))
vim.fn.writefile({ revision or "" }, utils.join_path(configs.get_parser_info_dir() or "", lang .. ".revision"))
end,
},
{ -- auto-attach modules after installation
@ -432,7 +452,7 @@ local function run_install(cache_folder, install_folder, lang, repo, with_sync,
end
---@param lang string
---@param ask_reinstall boolean
---@param ask_reinstall boolean|string
---@param cache_folder string
---@param install_folder string
---@param with_sync boolean
@ -465,6 +485,14 @@ local function install_lang(lang, ask_reinstall, cache_folder, install_folder, w
run_install(cache_folder, install_folder, lang, install_info, with_sync, generate_from_grammar)
end
---@class InstallOptions
---@field with_sync boolean
---@field ask_reinstall boolean|string
---@field generate_from_grammar boolean
---@field exclude_configured_parsers boolean
-- Install a parser
---@param options? InstallOptions
---@return function
local function install(options)
options = options or {}
@ -491,8 +519,8 @@ local function install(options)
end
assert(install_folder)
local languages
local ask
local languages ---@type string[]
local ask ---@type boolean|string
if ... == "all" then
languages = parsers.available_parsers()
ask = false
@ -533,6 +561,7 @@ function M.update(options)
M.lockfile = {}
reset_progress_counter()
if ... and ... ~= "all" then
---@type string[]
local languages = vim.tbl_flatten { ... }
local installed = 0
for _, lang in ipairs(languages) do
@ -578,6 +607,7 @@ function M.uninstall(...)
end
ensure_installed_parsers = utils.difference(ensure_installed_parsers, configs.get_ignored_parser_installs())
---@type string[]
local languages = vim.tbl_flatten { ... }
for _, lang in ipairs(languages) do
local install_dir, err = configs.get_parser_install_dir()
@ -634,7 +664,7 @@ function M.uninstall(...)
end
function M.write_lockfile(verbose, skip_langs)
local sorted_parsers = {}
local sorted_parsers = {} ---@type Parser[]
-- Load previous lockfile
load_lockfile()
skip_langs = skip_langs or {}
@ -643,6 +673,8 @@ function M.write_lockfile(verbose, skip_langs)
table.insert(sorted_parsers, { name = k, parser = v })
end
---@param a Parser
---@param b Parser
table.sort(sorted_parsers, function(a, b)
return a.name < b.name
end)
@ -650,7 +682,7 @@ function M.write_lockfile(verbose, skip_langs)
for _, v in ipairs(sorted_parsers) do
if not vim.tbl_contains(skip_langs, v.name) then
-- I'm sure this can be done in aync way with iter_cmd
local sha
local sha ---@type string
if v.parser.install_info.branch then
sha = vim.split(
vim.fn.systemlist(

View file

@ -20,15 +20,16 @@ function M.iter_locals(bufnr, root)
return queries.iter_group_results(bufnr, "locals", root)
end
---@param bufnr integer
---@return any
function M.get_locals(bufnr)
return queries.get_matches(bufnr, "locals")
end
--- Creates unique id for a node based on text and range
-- @param scope: the scope node of the definition
-- @param bufnr: the buffer
-- @param node_text: the node text to use
-- @returns a string id
-- Creates unique id for a node based on text and range
---@param scope TSNode: the scope node of the definition
---@param node_text string: the node text to use
---@return string: a string id
function M.get_definition_id(scope, node_text)
-- Add a valid starting character in case node text doesn't start with a valid one.
return table.concat({ "k", node_text or "", scope:range() }, "_")
@ -76,10 +77,13 @@ function M.get_references(bufnr)
return refs
end
--- Gets a table with all the scopes containing a node
-- Gets a table with all the scopes containing a node
-- The order is from most specific to least (bottom up)
---@param node TSNode
---@param bufnr integer
---@return TSNode[]
function M.get_scope_tree(node, bufnr)
local scopes = {}
local scopes = {} ---@type TSNode[]
for scope in M.iter_scope_tree(node, bufnr) do
table.insert(scopes, scope)
@ -88,7 +92,10 @@ function M.get_scope_tree(node, bufnr)
return scopes
end
--- Iterates over a nodes scopes moving from the bottom up
-- Iterates over a nodes scopes moving from the bottom up
---@param node TSNode
---@param bufnr integer
---@return fun(): TSNode|nil
function M.iter_scope_tree(node, bufnr)
local last_node = node
return function()
@ -105,12 +112,12 @@ function M.iter_scope_tree(node, bufnr)
end
-- Gets a table of all nodes and their 'kinds' from a locals list
-- @param local_def the local list result
-- @returns a list of node entries
---@param local_def any: the local list result
---@return table: a list of node entries
function M.get_local_nodes(local_def)
local result = {}
M.recurse_local_nodes(local_def, function(def, node, kind)
M.recurse_local_nodes(local_def, function(def, _node, kind)
table.insert(result, vim.tbl_extend("keep", { kind = kind }, def))
end)
@ -123,10 +130,10 @@ end
-- * The node
-- * The full definition match `@definition.var.something` -> 'var.something'
-- * The last definition match `@definition.var.something` -> 'something'
-- @param The locals result
-- @param The accumulator function
-- @param The full match path to append to
-- @param The last match
---@param local_def any The locals result
---@param accumulator function The accumulator function
---@param full_match? string The full match path to append to
---@param last_match? string The last match
function M.recurse_local_nodes(local_def, accumulator, full_match, last_match)
if type(local_def) ~= "table" then
return
@ -141,7 +148,7 @@ function M.recurse_local_nodes(local_def, accumulator, full_match, last_match)
end
end
--- Get a single dimension table to look definition nodes.
-- Get a single dimension table to look definition nodes.
-- Keys are generated by using the range of the containing scope and the text of the definition node.
-- This makes looking up a definition for a given scope a simple key lookup.
--
@ -152,8 +159,8 @@ end
-- Usage lookups require finding the definition of the node, so `find_definition`
-- is called very frequently, which is why this lookup must be fast as possible.
--
-- @param bufnr: the buffer
-- @returns a table for looking up definitions
---@param bufnr integer: the buffer
---@return table result: a table for looking up definitions
M.get_definitions_lookup_table = ts_utils.memoize_by_buf_tick(function(bufnr)
local definitions = M.get_definitions(bufnr)
local result = {}
@ -173,19 +180,19 @@ M.get_definitions_lookup_table = ts_utils.memoize_by_buf_tick(function(bufnr)
return result
end)
--- Gets all the scopes of a definition based on the scope type
-- Gets all the scopes of a definition based on the scope type
-- Scope types can be
--
-- "parent": Uses the parent of the containing scope, basically, skipping a scope
-- "global": Uses the top most scope
-- "local": Uses the containing scope of the definition. This is the default
--
-- @param node: the definition node
-- @param bufnr: the buffer
-- @param scope_type: the scope type
---@param node TSNode: the definition node
---@param bufnr integer: the buffer
---@param scope_type string: the scope type
function M.get_definition_scopes(node, bufnr, scope_type)
local scopes = {}
local scope_count = 1
local scope_count = 1 ---@type integer|nil
-- Definition is valid for the containing scope
-- and the containing scope of that scope
@ -209,6 +216,11 @@ function M.get_definition_scopes(node, bufnr, scope_type)
return scopes
end
---@param node TSNode
---@param bufnr integer
---@return TSNode node
---@return TSNode scope
---@return string|nil kind
function M.find_definition(node, bufnr)
local def_lookup = M.get_definitions_lookup_table(bufnr)
local node_text = ts_query.get_node_text(node, bufnr)
@ -227,11 +239,11 @@ function M.find_definition(node, bufnr)
end
-- Finds usages of a node in a given scope.
-- @param node the node to find usages for
-- @param scope_node the node to look within
-- @returns a list of nodes
---@param node TSNode the node to find usages for
---@param scope_node TSNode the node to look within
---@return TSNode[]: a list of nodes
function M.find_usages(node, scope_node, bufnr)
local bufnr = bufnr or api.nvim_get_current_buf()
bufnr = bufnr or api.nvim_get_current_buf()
local node_text = ts_query.get_node_text(node, bufnr)
if not node_text or #node_text < 1 then
@ -258,6 +270,10 @@ function M.find_usages(node, scope_node, bufnr)
return usages
end
---@param node TSNode
---@param bufnr? integer
---@param allow_scope? boolean
---@return TSNode|nil
function M.containing_scope(node, bufnr, allow_scope)
local bufnr = bufnr or api.nvim_get_current_buf()
local allow_scope = allow_scope == nil or allow_scope == true
@ -284,8 +300,8 @@ function M.nested_scope(node, cursor_pos)
return
end
local row = cursor_pos.row
local col = cursor_pos.col
local row = cursor_pos.row ---@type integer
local col = cursor_pos.col ---@type integer
local scope = M.containing_scope(node)
for _, child in ipairs(ts_utils.get_named_children(scope)) do
@ -321,6 +337,8 @@ function M.next_scope(node)
end
end
---@param node TSNode
---@return TSNode|nil
function M.previous_scope(node)
local bufnr = api.nvim_get_current_buf()

View file

@ -36,6 +36,7 @@ local filetype_to_parsername = {
---@field filetype string
---@field maintainers string[]
---@field experimental boolean|nil
---@field readme_name string|nil
---@type ParserInfo[]
local list = setmetatable({}, {
@ -1542,11 +1543,13 @@ function M.ft_to_lang(ft)
end
end
-- Get a list of all available parsers
---@return string[]
function M.available_parsers()
if vim.fn.executable "tree-sitter" == 1 and vim.fn.executable "node" == 1 then
return vim.tbl_keys(M.list)
else
return vim.tbl_filter(function(p)
return vim.tbl_filter(function(p) ---@param p string
return not M.list[p].install_info.requires_generate_from_grammar
end, vim.tbl_keys(M.list))
end
@ -1598,9 +1601,9 @@ function M.get_tree_root(bufnr)
return M.get_parser(bufnr):parse()[1]:root()
end
-- get language of given buffer
-- @param optional buffer number or current buffer
-- @returns language string of buffer
-- Gets the language of a given buffer
---@param bufnr number? or current buffer
---@return string
function M.get_buf_lang(bufnr)
bufnr = bufnr or api.nvim_get_current_buf()
return M.ft_to_lang(api.nvim_buf_get_option(bufnr, "ft"))

View file

@ -11,10 +11,10 @@ local EMPTY_ITER = function() end
M.built_in_query_groups = { "highlights", "locals", "folds", "indents", "injections" }
--- Creates a function that checks whether a given query exists
--- for a specific language.
-- Creates a function that checks whether a given query exists
-- for a specific language.
---@param query string
---@return function(string): boolean
---@return fun(string): boolean
local function get_query_guard(query)
return function(lang)
return M.has_query_files(lang, query)
@ -49,6 +49,9 @@ do
})
end
---@param bufnr integer
---@param query_group string
---@return any
function M.get_matches(bufnr, query_group)
bufnr = bufnr or api.nvim_get_current_buf()
local cached_local = query_cache.get(query_group, bufnr)
@ -94,9 +97,10 @@ do
end
-- cache will auto set the table for each lang if it is nil
---@type table<string, table<string, Query>>
local cache = setmetatable({}, mt)
--- Same as `vim.treesitter.query` except will return cached values
-- Same as `vim.treesitter.query` except will return cached values
---@param lang string
---@param query_name string
function M.get_query(lang, query_name)
@ -107,12 +111,13 @@ do
return cache[lang][query_name]
end
--- Invalidates the query file cache.
--- If lang and query_name is both present, will reload for only the lang and query_name.
--- If only lang is present, will reload all query_names for that lang
--- If none are present, will reload everything
---@param lang string
---@param query_name string
-- Invalidates the query file cache.
--
-- If lang and query_name is both present, will reload for only the lang and query_name.
-- If only lang is present, will reload all query_names for that lang
-- If none are present, will reload everything
---@param lang? string
---@param query_name? string
function M.invalidate_query_cache(lang, query_name)
if lang and query_name then
cache[lang][query_name] = nil
@ -137,7 +142,7 @@ do
end
end
--- This function is meant for an autocommand and not to be used. Only use if file is a query file.
-- This function is meant for an autocommand and not to be used. Only use if file is a query file.
---@param fname string
function M.invalidate_query_file(fname)
local fnamemodify = vim.fn.fnamemodify
@ -145,14 +150,14 @@ function M.invalidate_query_file(fname)
end
---@class QueryInfo
---@field root LanguageTree
---@field root TSNode
---@field source integer
---@field start integer
---@field stop integer
---@param bufnr integer
---@param query_name string
---@param root LanguageTree
---@param root TSNode
---@param root_lang string|nil
---@return Query|nil, QueryInfo|nil
local function prepare_query(bufnr, query_name, root, root_lang)
@ -214,15 +219,21 @@ end
---@param end_row integer
function M.iter_prepared_matches(query, qnode, bufnr, start_row, end_row)
-- A function that splits a string on '.'
local function split(string)
---@param to_split string
---@return string[]
local function split(to_split)
local t = {}
for str in string.gmatch(string, "([^.]+)") do
for str in string.gmatch(to_split, "([^.]+)") do
table.insert(t, str)
end
return t
end
-- Given a path (i.e. a List(String)) this functions inserts value at path
---@param object any
---@param path string[]
---@param value any
local function insert_to_path(object, path, value)
local curr_obj = object
@ -256,6 +267,7 @@ function M.iter_prepared_matches(query, qnode, bufnr, start_row, end_row)
end
-- Add some predicates for testing
---@type string[][] ( TODO: make pred type so this can be pred[])
local preds = query.info.patterns[pattern]
if preds then
for _, pred in pairs(preds) do
@ -279,22 +291,22 @@ function M.iter_prepared_matches(query, qnode, bufnr, start_row, end_row)
return iterator
end
--- Return all nodes corresponding to a specific capture path (like @definition.var, @reference.type)
---Works like M.get_references or M.get_scopes except you can choose the capture
---Can also be a nested capture like @definition.function to get all nodes defining a function.
---
-- Return all nodes corresponding to a specific capture path (like @definition.var, @reference.type)
-- Works like M.get_references or M.get_scopes except you can choose the capture
-- Can also be a nested capture like @definition.function to get all nodes defining a function.
--
---@param bufnr integer the buffer
---@param captures string|string[]
---@param query_group string the name of query group (highlights or injections for example)
---@param root LanguageTree|nil node from where to start the search
---@param root TSNode|nil node from where to start the search
---@param lang string|nil the language from where to get the captures.
--- Root nodes can have several languages.
--- Root nodes can have several languages.
---@return table|nil
function M.get_capture_matches(bufnr, captures, query_group, root, lang)
if type(captures) == "string" then
captures = { captures }
end
local strip_captures = {}
local strip_captures = {} ---@type string[]
for i, capture in ipairs(captures) do
if capture:sub(1, 1) ~= "@" then
error 'Captures must start with "@"'
@ -342,14 +354,21 @@ function M.iter_captures(bufnr, query_name, root, lang)
return wrapped_iter
end
---@param bufnr integer
---@param capture_string string
---@param query_group string
---@param filter_predicate fun(match: table): boolean
---@param scoring_function fun(match: table): number
---@param root TSNode
---@return table|unknown
function M.find_best_match(bufnr, capture_string, query_group, filter_predicate, scoring_function, root)
if string.sub(capture_string, 1, 1) == "@" then
--remove leading "@"
capture_string = string.sub(capture_string, 2)
end
local best
local best_score
local best ---@type table|nil
local best_score ---@type number
for maybe_match in M.iter_group_results(bufnr, query_group, root) do
local match = utils.get_at_path(maybe_match, capture_string)
@ -372,8 +391,8 @@ end
---Iterates matches from a query file.
---@param bufnr integer the buffer
---@param query_group string the query file to use
---@param root LanguageTree the root node
---@param root_lang string|nil the root node lang, if known
---@param root TSNode the root node
---@param root_lang? string the root node lang, if known
function M.iter_group_results(bufnr, query_group, root, root_lang)
local query, params = prepare_query(bufnr, query_group, root, root_lang)
if not query then
@ -396,13 +415,14 @@ end
---@alias CaptureResFn function(string, LanguageTree, LanguageTree): string, string
--- Same as get_capture_matches except this will recursively get matches for every language in the tree.
---@param bufnr integer The bufnr
-- Same as get_capture_matches except this will recursively get matches for every language in the tree.
---@param bufnr integer The buffer
---@param capture_or_fn string|CaptureResFn The capture to get. If a function is provided then that
--- function will be used to resolve both the capture and query argument.
--- The function can return `nil` to ignore that tree.
---@param query_type string The query to get the capture from. This is ignore if a function is provided
--- for the captuer argument.
--- function will be used to resolve both the capture and query argument.
--- The function can return `nil` to ignore that tree.
---@param query_type string? The query to get the capture from. This is ignored if a function is provided
--- for the capture argument.
---@return table[]
function M.get_capture_matches_recursively(bufnr, capture_or_fn, query_type)
---@type CaptureResFn
local type_fn
@ -422,7 +442,7 @@ function M.get_capture_matches_recursively(bufnr, capture_or_fn, query_type)
local capture, type_ = type_fn(lang, tree, lang_tree)
if capture then
vim.list_extend(matches, M.get_capture_matches(bufnr, capture, type_, tree:root(), lang))
vim.list_extend(matches, M.get_capture_matches(bufnr, capture, type_, tree:root(), lang) or {})
end
end)
end

View file

@ -20,12 +20,17 @@ local function valid_args(name, pred, count, strict_count)
return true
end
query.add_predicate("nth?", function(match, pattern, bufnr, pred)
---@param match (TSNode|nil)[]
---@param _pattern string
---@param _bufnr integer
---@param pred string[]
---@return boolean|nil
query.add_predicate("nth?", function(match, _pattern, _bufnr, pred)
if not valid_args("nth?", pred, 2, true) then
return
end
local node = match[pred[2]]
local node = match[pred[2]] ---@type TSNode
local n = tonumber(pred[3])
if node and node:parent() and node:parent():named_child_count() > n then
return node:parent():named_child(n) == node
@ -34,7 +39,12 @@ query.add_predicate("nth?", function(match, pattern, bufnr, pred)
return false
end)
local function has_ancestor(match, pattern, bufnr, pred)
---@param match (TSNode|nil)[]
---@param _pattern string
---@param _bufnr integer
---@param pred string[]
---@return boolean|nil
local function has_ancestor(match, _pattern, _bufnr, pred)
if not valid_args(pred[1], pred, 2) then
return
end
@ -65,7 +75,12 @@ query.add_predicate("has-ancestor?", has_ancestor)
query.add_predicate("has-parent?", has_ancestor)
query.add_predicate("is?", function(match, pattern, bufnr, pred)
---@param match (TSNode|nil)[]
---@param _pattern string
---@param bufnr integer
---@param pred string[]
---@return boolean|nil
query.add_predicate("is?", function(match, _pattern, bufnr, pred)
if not valid_args("is?", pred, 2) then
return
end
@ -84,7 +99,12 @@ query.add_predicate("is?", function(match, pattern, bufnr, pred)
return vim.tbl_contains(types, kind)
end)
query.add_predicate("has-type?", function(match, pattern, bufnr, pred)
---@param match (TSNode|nil)[]
---@param _pattern string
---@param _bufnr integer
---@param pred string[]
---@return boolean|nil
query.add_predicate("has-type?", function(match, _pattern, _bufnr, pred)
if not valid_args(pred[1], pred, 2) then
return
end
@ -102,8 +122,14 @@ end)
-- Just avoid some annoying warnings for this directive
query.add_directive("make-range!", function() end)
---@param match (TSNode|nil)[]
---@param _ string
---@param bufnr integer
---@param pred string[]
---@param metadata table
---@return boolean|nil
query.add_directive("downcase!", function(match, _, bufnr, pred, metadata)
local text, key, value
local text, key, value ---@type string|string[], string, string|integer
if #pred == 3 then
-- (#downcase! @capture "key")
@ -129,13 +155,19 @@ query.add_directive("downcase!", function(match, _, bufnr, pred, metadata)
end
end)
---@param match (TSNode|nil)[]
---@param _pattern string
---@param _bufnr integer
---@param pred string[]
---@param metadata table
---@return boolean|nil
query.add_directive("exclude_children!", function(match, _pattern, _bufnr, pred, metadata)
local capture_id = pred[2]
local node = match[capture_id]
local start_row, start_col, end_row, end_col = node:range()
local ranges = {}
for i = 0, node:named_child_count() - 1 do
local child = node:named_child(i)
local child = node:named_child(i) ---@type TSNode
local child_start_row, child_start_col, child_end_row, child_end_col = child:range()
if child_start_row > start_row or child_start_col > start_col then
table.insert(ranges, {
@ -156,6 +188,11 @@ end)
-- Trim blank lines from end of the region
-- Arguments are the captures to trim.
---@param match (TSNode|nil)[]
---@param _ string
---@param bufnr integer
---@param pred string[]
---@param metadata table
query.add_directive("trim!", function(match, _, bufnr, pred, metadata)
for _, id in ipairs { select(2, unpack(pred)) } do
local node = match[id]

View file

@ -3,6 +3,8 @@ local utils = require "nvim-treesitter.utils"
-- Convert path for cmd.exe on Windows.
-- This is needed when vim.opt.shellslash is in use.
---@param p string
---@return string
local function cmdpath(p)
if vim.opt.shellslash:get() then
local r = p:gsub("/", "\\")
@ -14,6 +16,10 @@ end
local M = {}
---@param directory string
---@param cwd string
---@param info_msg string
---@return table
function M.select_mkdir_cmd(directory, cwd, info_msg)
if fn.has "win32" == 1 then
return {
@ -38,6 +44,9 @@ function M.select_mkdir_cmd(directory, cwd, info_msg)
end
end
---@param file string
---@param info_msg string
---@return table
function M.select_rm_file_cmd(file, info_msg)
if fn.has "win32" == 1 then
return {
@ -60,13 +69,17 @@ function M.select_rm_file_cmd(file, info_msg)
end
end
---@param executables string[]
---@return string|nil
function M.select_executable(executables)
return vim.tbl_filter(function(c)
return vim.tbl_filter(function(c) ---@param c string
return c ~= vim.NIL and fn.executable(c) == 1
end, executables)[1]
end
---@param repo InstallInfo
---@param compiler string
---@return string[]
function M.select_compiler_args(repo, compiler)
if string.match(compiler, "cl$") or string.match(compiler, "cl.exe$") then
return {
@ -98,7 +111,7 @@ function M.select_compiler_args(repo, compiler)
"-Os",
}
if
#vim.tbl_filter(function(file)
#vim.tbl_filter(function(file) ---@param file string
local ext = vim.fn.fnamemodify(file, ":e")
return ext == "cc" or ext == "cpp" or ext == "cxx"
end, repo.files) > 0
@ -186,6 +199,12 @@ function M.select_mv_cmd(from, to, cwd)
end
end
---@param repo InstallInfo
---@param project_name string
---@param cache_folder string
---@param revision string|nil
---@param prefer_git boolean
---@return table
function M.select_download_commands(repo, project_name, cache_folder, revision, prefer_git)
local can_use_tar = vim.fn.executable "tar" == 1 and vim.fn.executable "curl" == 1
local is_github = repo.url:find("github.com", 1, true)
@ -277,6 +296,9 @@ function M.select_download_commands(repo, project_name, cache_folder, revision,
end
end
---@param dir string
---@param command string
---@return string command
function M.make_directory_change_for_command(dir, command)
if fn.has "win32" == 1 then
return string.format("pushd %s & %s & popd", cmdpath(dir), command)

View file

@ -34,6 +34,11 @@ local function get_node_text(node, bufnr)
end
---@private
---@param node TSNode
---@param type_patterns string[]
---@param transform_fn fun(line: string): string
---@param bufnr integer
---@return string
function M._get_line_for_node(node, type_patterns, transform_fn, bufnr)
local node_type = node:type()
local is_valid = false
@ -51,7 +56,7 @@ function M._get_line_for_node(node, type_patterns, transform_fn, bufnr)
return line:gsub("%%", "%%%%")
end
--- Gets the actual text content of a node
-- Gets the actual text content of a node
-- @deprecated Use vim.treesitter.query.get_node_text
-- @param node the node to get the text from
-- @param bufnr the buffer containing the node
@ -64,7 +69,7 @@ function M.get_node_text(node, bufnr)
return get_node_text(node, bufnr)
end
--- Determines whether a node is the parent of another
-- Determines whether a node is the parent of another
-- @param dest the possible parent
-- @param source the possible child node
function M.is_parent(dest, source)
@ -84,12 +89,12 @@ function M.is_parent(dest, source)
return false
end
--- Get next node with same parent
-- @param node node
-- @param allow_switch_parents allow switching parents if last node
-- @param allow_next_parent allow next parent if last node and next parent without children
-- Get next node with same parent
---@param node TSNode
---@param allow_switch_parents? boolean allow switching parents if last node
---@param allow_next_parent? boolean allow next parent if last node and next parent without children
function M.get_next_node(node, allow_switch_parents, allow_next_parent)
local destination_node
local destination_node ---@type TSNode
local parent = node:parent()
if not parent then
@ -116,12 +121,12 @@ function M.get_next_node(node, allow_switch_parents, allow_next_parent)
return destination_node
end
--- Get previous node with same parent
-- @param node node
-- @param allow_switch_parents allow switching parents if first node
-- @param allow_previous_parent allow previous parent if first node and previous parent without children
-- Get previous node with same parent
---@param node TSNode
---@param allow_switch_parents? boolean allow switching parents if first node
---@param allow_previous_parent? boolean allow previous parent if first node and previous parent without children
function M.get_previous_node(node, allow_switch_parents, allow_previous_parent)
local destination_node
local destination_node ---@type TSNode
local parent = node:parent()
if not parent then
return
@ -148,7 +153,7 @@ function M.get_previous_node(node, allow_switch_parents, allow_previous_parent)
end
function M.get_named_children(node)
local nodes = {}
local nodes = {} ---@type TSNode[]
for i = 0, node:named_child_count() - 1, 1 do
nodes[i + 1] = node:named_child(i)
end
@ -166,7 +171,7 @@ function M.get_node_at_cursor(winnr, ignore_injected_langs)
return
end
local root
local root ---@type TSNode|nil
if ignore_injected_langs then
for _, tree in ipairs(root_lang_tree:trees()) do
local tree_root = tree:root()
@ -209,6 +214,9 @@ function M.get_root_for_position(line, col, root_lang_tree)
return nil, nil, lang_tree
end
---comment
---@param node TSNode
---@return TSNode result
function M.get_root_for_node(node)
local parent = node
local result = node
@ -228,12 +236,17 @@ function M.highlight_node(node, buf, hl_namespace, hl_group)
M.highlight_range({ node:range() }, buf, hl_namespace, hl_group)
end
--- Get a compatible vim range (1 index based) from a TS node range.
-- Get a compatible vim range (1 index based) from a TS node range.
--
-- TS nodes start with 0 and the end col is ending exclusive.
-- They also treat a EOF/EOL char as a char ending in the first
-- col of the next row.
---comment
---@param range integer[]
---@param buf integer|nil
---@return integer, integer, integer, integer
function M.get_vim_range(range, buf)
---@type integer, integer, integer, integer
local srow, scol, erow, ecol = unpack(range)
srow = srow + 1
scol = scol + 1
@ -253,7 +266,9 @@ function M.get_vim_range(range, buf)
end
function M.highlight_range(range, buf, hl_namespace, hl_group)
---@type integer, integer, integer, integer
local start_row, start_col, end_row, end_col = unpack(range)
---@diagnostic disable-next-line: missing-parameter
vim.highlight.range(buf, hl_namespace, hl_group, { start_row, start_col }, { end_row, end_col })
end
@ -288,13 +303,15 @@ function M.update_selection(buf, node, selection_mode)
end
-- Byte length of node range
---@param node TSNode
---@return number
function M.node_length(node)
local _, _, start_byte = node:start()
local _, _, end_byte = node:end_()
return end_byte - start_byte
end
--- @deprecated Use `vim.treesitter.is_in_node_range()` instead
---@deprecated Use `vim.treesitter.is_in_node_range()` instead
function M.is_in_node_range(node, line, col)
vim.notify_once(
"nvim-treesitter.ts_utils.is_in_node_range is deprecated: use vim.treesitter.is_in_node_range",
@ -303,7 +320,7 @@ function M.is_in_node_range(node, line, col)
return ts.is_in_node_range(node, line, col)
end
--- @deprecated Use `vim.treesitter.get_node_range()` instead
---@deprecated Use `vim.treesitter.get_node_range()` instead
function M.get_node_range(node_or_range)
vim.notify_once(
"nvim-treesitter.ts_utils.get_node_range is deprecated: use vim.treesitter.get_node_range",
@ -312,6 +329,8 @@ function M.get_node_range(node_or_range)
return ts.get_node_range(node_or_range)
end
---@param node TSNode
---@return table
function M.node_to_lsp_range(node)
local start_line, start_col, end_line, end_col = ts.get_node_range(node)
local rtn = {}
@ -320,16 +339,18 @@ function M.node_to_lsp_range(node)
return rtn
end
--- Memoizes a function based on the buffer tick of the provided bufnr.
-- Memoizes a function based on the buffer tick of the provided bufnr.
-- The cache entry is cleared when the buffer is detached to avoid memory leaks.
-- @param fn: the fn to memoize, taking the bufnr as first argument
-- @param options:
-- The options argument is a table with two optional values:
-- - bufnr: extracts a bufnr from the given arguments.
-- - key: extracts the cache key from the given arguments.
-- @returns a memoized function
---@param fn function the fn to memoize, taking the buffer as first argument
---@param options? {bufnr: integer?, key: string|fun(...): string?} the memoization options
---@return function: a memoized function
function M.memoize_by_buf_tick(fn, options)
options = options or {}
---@type table<string, {result: any, last_tick: integer}>
local cache = {}
local bufnr_fn = utils.to_func(options.bufnr or utils.identity)
local key_fn = utils.to_func(options.key or utils.identity)
@ -424,6 +445,7 @@ function M.goto_node(node, goto_end, avoid_set_jump)
utils.set_jump()
end
local range = { M.get_vim_range { node:range() } }
---@type table<number>
local position
if not goto_end then
position = { range[1], range[2] }

View file

@ -5,12 +5,15 @@ local luv = vim.loop
local M = {}
-- Wrapper around vim.notify with common options set.
---@param msg string
---@param log_level number|nil
---@param opts table|nil
function M.notify(msg, log_level, opts)
local default_opts = { title = "nvim-treesitter" }
vim.notify(msg, log_level, vim.tbl_extend("force", default_opts, opts or {}))
end
-- Returns the system specific path separator.
-- Returns the system-specific path separator.
---@return string
function M.get_path_sep()
return (fn.has "win32" == 1 and not vim.opt.shellslash:get()) and "\\" or "/"
@ -18,10 +21,13 @@ end
-- Returns a function that joins the given arguments with separator. Arguments
-- can't be nil. Example:
--
--[[
print(M.generate_join(" ")("foo", "bar"))
print(M.generate_join(" ")("foo", "bar"))
--]]
-- prints "foo bar"
--prints "foo bar"
---@param separator string
---@return fun(...: string): string
function M.generate_join(separator)
return function(...)
return table.concat({ ... }, separator)
@ -32,13 +38,18 @@ M.join_path = M.generate_join(M.get_path_sep())
M.join_space = M.generate_join " "
--- Define user defined vim command which calls nvim-treesitter module function
--- - If module name is 'mod', it should be defined in hierarchy 'nvim-treesitter.mod'
--- - A table with name 'commands' should be defined in 'mod' which needs to be passed as
--- the commands param of this function
---
---@param mod string, Name of the module that resides in the hierarchy - nvim-treesitter.module
---@param commands table, Command list for the module
---@class Command
---@field run function
---@field f_args string
---@field args string
-- Define user defined vim command which calls nvim-treesitter module function
-- - If module name is 'mod', it should be defined in hierarchy 'nvim-treesitter.mod'
-- - A table with name 'commands' should be defined in 'mod' which needs to be passed as
-- the commands param of this function
--
---@param mod string Name of the module that resides in the hierarchy - nvim-treesitter.module
---@param commands table<string, Command> Command list for the module
--- - {command_name} Name of the vim user defined command, Keys:
--- - {run}: (function) callback function that needs to be executed
--- - {f_args}: (string, default <f-args>)
@ -46,7 +57,7 @@ M.join_space = M.generate_join " "
--- - {args}: (string, optional)
--- - vim command attributes
---
---Example:
---* @example
--- If module is nvim-treesitter.custom_mod
--- <pre>
--- M.commands = {
@ -136,18 +147,22 @@ function M.get_site_dir()
end
-- Gets a property at path
-- @param tbl the table to access
-- @param path the '.' separated path
-- @returns the value at path or nil
---@param tbl table the table to access
---@param path string the '.' separated path
---@return table|nil result the value at path or nil
function M.get_at_path(tbl, path)
if path == "" then
return tbl
end
local segments = vim.split(path, ".", true)
---@type table[]|table
local result = tbl
for _, segment in ipairs(segments) do
if type(result) == "table" then
---@type table
-- TODO: figure out the actual type of tbl
result = result[segment]
end
end
@ -159,17 +174,9 @@ function M.set_jump()
vim.cmd "normal! m'"
end
function M.index_of(tbl, obj)
for i, o in ipairs(tbl) do
if o == obj then
return i
end
end
end
-- Filters a list based on the given predicate
-- @param tbl The list to filter
-- @param predicate The predicate to filter with
---@param tbl any[] The list to filter
---@param predicate fun(v:any, i:number):boolean The predicate to filter with
function M.filter(tbl, predicate)
local result = {}
@ -184,8 +191,9 @@ end
-- Returns a list of all values from the first list
-- that are not present in the second list.
-- @params tbl1 The first table
-- @params tbl2 The second table
---@param tbl1 any[] The first table
---@param tbl2 any[] The second table
---@return table
function M.difference(tbl1, tbl2)
return M.filter(tbl1, function(v)
return not vim.tbl_contains(tbl2, v)
@ -196,12 +204,19 @@ function M.identity(a)
return a
end
-- Returns a function returning the given value
---@param a any
---@return fun():any
function M.constant(a)
return function()
return a
end
end
-- Returns a function that returns the given value if it is a function,
-- otherwise returns a function that returns the given value.
---@param a any
---@return fun(...):any
function M.to_func(a)
return type(a) == "function" and a or M.constant(a)
end
@ -209,7 +224,10 @@ end
---@return string|nil
function M.ts_cli_version()
if fn.executable "tree-sitter" == 1 then
local handle = io.popen "tree-sitter -V"
local handle = io.popen "tree-sitter -V"
if not handle then
return
end
local result = handle:read "*a"
handle:close()
return vim.split(result, "\n")[1]:match "[^tree%psitter ].*"

View file

@ -1,4 +1,9 @@
-- Execute as `nvim --headless -c "luafile ./scripts/update-readme.lua"`
---@class Parser
---@field name string
---@field parser ParserInfo
local parsers = require("nvim-treesitter.parsers").get_parser_configs()
local sorted_parsers = {}
@ -6,12 +11,15 @@ for k, v in pairs(parsers) do
table.insert(sorted_parsers, { name = k, parser = v })
end
---@param a Parser
---@param b Parser
table.sort(sorted_parsers, function(a, b)
return a.name < b.name
end)
local generated_text = ""
---@param v Parser
for _, v in ipairs(sorted_parsers) do
local link = "[" .. (v.parser.readme_name or v.name) .. "](" .. v.parser.install_info.url .. ")"
@ -41,7 +49,7 @@ local new_readme_text = string.gsub(
)
vim.fn.writefile(vim.fn.split(new_readme_text, "\n"), "README.md")
if string.find(readme_text, generated_text, 1, "plain") then
if string.find(readme_text, generated_text, 1, true) then
print "README.md is up-to-date!"
vim.cmd "q"
else

View file

@ -1,10 +1,15 @@
-- Execute as `nvim --headless -c "luafile ./scripts/write-lockfile.lua"`
---@type string|any[]
local skip_langs = vim.fn.getenv "SKIP_LOCKFILE_UPDATE_FOR_LANGS"
if skip_langs == vim.NIL then
skip_langs = {}
else
---@diagnostic disable-next-line: param-type-mismatch
skip_langs = vim.fn.split(skip_langs, ",")
end
print("Skipping languages: " .. vim.inspect(skip_langs))
require("nvim-treesitter.install").write_lockfile("verbose", skip_langs)
vim.cmd "q"