refactor(treesitter): typing for Query, TSQuery, and TSQueryInfo

- `TSQuery`: userdata object for parsed query.

- `vim.treesitter.Query`: renamed from `Query`.
  - Add a new field `lang`.

- `TSQueryInfo`:
  - Move to `vim/treesitter/_meta.lua`, because C code owns it.
  - Correct typing for `patterns`, should be a map from `integer`
    (pattern_id) to `(integer|string)[][]` (list of predicates or
    directives).

- `vim.treesitter.QueryInfo` is added.
  - This currently has the same structure as `TSQueryInfo` (exported
    from C code).
  - Document the fields (see `TSQuery:inspect`).

- Add typing for `vim._ts_parse_query()`.
This commit is contained in:
Jongwook Choi 2024-01-25 13:27:48 -05:00 committed by Lewis Russell
parent 8b21fe83da
commit 800134ea5e
5 changed files with 83 additions and 32 deletions

View File

@ -976,7 +976,8 @@ get({lang}, {query_name}) *vim.treesitter.query.get()*
• {query_name} (`string`) Name of the query (e.g. "highlights") • {query_name} (`string`) Name of the query (e.g. "highlights")
Return: ~ Return: ~
(`Query?`) Parsed query (`vim.treesitter.Query?`) Parsed query. `nil` if no query files are
found.
*vim.treesitter.query.get_files()* *vim.treesitter.query.get_files()*
get_files({lang}, {query_name}, {is_included}) get_files({lang}, {query_name}, {is_included})
@ -1040,12 +1041,12 @@ parse({lang}, {query}) *vim.treesitter.query.parse()*
read the contents into a string before calling). read the contents into a string before calling).
Returns a `Query` (see |lua-treesitter-query|) object which can be used to Returns a `Query` (see |lua-treesitter-query|) object which can be used to
search nodes in the syntax tree for the patterns defined in {query} using `iter_*` methods search nodes in the syntax tree for the patterns defined in {query} using
below. the `iter_captures` and `iter_matches` methods.
Exposes `info` and `captures` with additional context about {query}. Exposes `info` and `captures` with additional context about {query}.
• `captures` contains the list of unique capture names defined in {query}. • `captures` contains the list of unique capture names defined in {query}.
-`info.captures` also points to `captures`. `info.captures` also points to `captures`.
• `info.patterns` contains information about predicates. • `info.patterns` contains information about predicates.
Parameters: ~ Parameters: ~
@ -1053,7 +1054,10 @@ parse({lang}, {query}) *vim.treesitter.query.parse()*
• {query} (`string`) Query in s-expr syntax • {query} (`string`) Query in s-expr syntax
Return: ~ Return: ~
(`Query`) Parsed query (`vim.treesitter.Query`) Parsed query
See also: ~
• |vim.treesitter.query.get()|
*Query:iter_captures()* *Query:iter_captures()*
Query:iter_captures({node}, {source}, {start}, {stop}) Query:iter_captures({node}, {source}, {start}, {stop})

View File

@ -1,4 +1,5 @@
---@meta ---@meta
error('Cannot require a meta file')
---@class TSNode: userdata ---@class TSNode: userdata
---@field id fun(self: TSNode): string ---@field id fun(self: TSNode): string
@ -33,7 +34,7 @@
---@field byte_length fun(self: TSNode): integer ---@field byte_length fun(self: TSNode): integer
local TSNode = {} local TSNode = {}
---@param query userdata ---@param query TSQuery
---@param captures true ---@param captures true
---@param start? integer ---@param start? integer
---@param end_? integer ---@param end_? integer
@ -41,17 +42,17 @@ local TSNode = {}
---@return fun(): integer, TSNode, any ---@return fun(): integer, TSNode, any
function TSNode:_rawquery(query, captures, start, end_, opts) end function TSNode:_rawquery(query, captures, start, end_, opts) end
---@param query userdata ---@param query TSQuery
---@param captures false ---@param captures false
---@param start? integer ---@param start? integer
---@param end_? integer ---@param end_? integer
---@param opts? table ---@param opts? table
---@return fun(): string, any ---@return fun(): integer, any
function TSNode:_rawquery(query, captures, start, end_, opts) end function TSNode:_rawquery(query, captures, start, end_, opts) end
---@alias TSLoggerCallback fun(logtype: 'parse'|'lex', msg: string) ---@alias TSLoggerCallback fun(logtype: 'parse'|'lex', msg: string)
---@class TSParser ---@class TSParser: userdata
---@field parse fun(self: TSParser, tree: TSTree?, source: integer|string, include_bytes: true): TSTree, Range6[] ---@field parse fun(self: TSParser, tree: TSTree?, source: integer|string, include_bytes: true): TSTree, Range6[]
---@field parse fun(self: TSParser, tree: TSTree?, source: integer|string, include_bytes: false|nil): TSTree, Range4[] ---@field parse fun(self: TSParser, tree: TSTree?, source: integer|string, include_bytes: false|nil): TSTree, Range4[]
---@field reset fun(self: TSParser) ---@field reset fun(self: TSParser)
@ -62,19 +63,31 @@ function TSNode:_rawquery(query, captures, start, end_, opts) end
---@field _set_logger fun(self: TSParser, lex: boolean, parse: boolean, cb: TSLoggerCallback) ---@field _set_logger fun(self: TSParser, lex: boolean, parse: boolean, cb: TSLoggerCallback)
---@field _logger fun(self: TSParser): TSLoggerCallback ---@field _logger fun(self: TSParser): TSLoggerCallback
---@class TSTree ---@class TSTree: userdata
---@field root fun(self: TSTree): TSNode ---@field root fun(self: TSTree): TSNode
---@field edit fun(self: TSTree, _: integer, _: integer, _: integer, _: integer, _: integer, _: integer, _: integer, _: integer, _:integer) ---@field edit fun(self: TSTree, _: integer, _: integer, _: integer, _: integer, _: integer, _: integer, _: integer, _: integer, _:integer)
---@field copy fun(self: TSTree): TSTree ---@field copy fun(self: TSTree): TSTree
---@field included_ranges fun(self: TSTree, include_bytes: true): Range6[] ---@field included_ranges fun(self: TSTree, include_bytes: true): Range6[]
---@field included_ranges fun(self: TSTree, include_bytes: false): Range4[] ---@field included_ranges fun(self: TSTree, include_bytes: false): Range4[]
---@class TSQuery: userdata
---@field inspect fun(self: TSQuery): TSQueryInfo
---@class (exact) TSQueryInfo
---@field captures string[]
---@field patterns table<integer, (integer|string)[][]>
---@return integer ---@return integer
vim._ts_get_language_version = function() end vim._ts_get_language_version = function() end
---@return integer ---@return integer
vim._ts_get_minimum_language_version = function() end vim._ts_get_minimum_language_version = function() end
---@param lang string Language to use for the query
---@param query string Query string in s-expr syntax
---@return TSQuery
vim._ts_parse_query = function(lang, query) end
---@param lang string ---@param lang string
---@return TSParser ---@return TSParser
vim._create_ts_parser = function(lang) end vim._create_ts_parser = function(lang) end

View File

@ -7,7 +7,7 @@ local ns = api.nvim_create_namespace('treesitter/highlighter')
---@alias vim.treesitter.highlighter.Iter fun(end_line: integer|nil): integer, TSNode, TSMetadata ---@alias vim.treesitter.highlighter.Iter fun(end_line: integer|nil): integer, TSNode, TSMetadata
---@class vim.treesitter.highlighter.Query ---@class vim.treesitter.highlighter.Query
---@field private _query Query? ---@field private _query vim.treesitter.query.Query?
---@field private lang string ---@field private lang string
---@field private hl_cache table<integer,integer> ---@field private hl_cache table<integer,integer>
local TSHighlighterQuery = {} local TSHighlighterQuery = {}

View File

@ -1,18 +1,48 @@
local api = vim.api local api = vim.api
local language = require('vim.treesitter.language') local language = require('vim.treesitter.language')
---@class Query local M = {}
---@field captures string[] List of captures used in query
---@field info TSQueryInfo Contains used queries, predicates, directives ---Parsed query, see |vim.treesitter.query.parse()|
---@field query userdata Parsed query ---
---@class vim.treesitter.Query
---@field lang string name of the language for this parser
---@field captures string[] list of (unique) capture names defined in query
---@field info vim.treesitter.QueryInfo contains information used in the query (e.g. captures, predicates, directives)
---@field query TSQuery userdata query object
local Query = {} local Query = {}
Query.__index = Query Query.__index = Query
---@class TSQueryInfo ---@package
---@field captures table ---@see vim.treesitter.query.parse
---@field patterns table<string,any[][]> ---@param lang string
---@param ts_query TSQuery
---@return vim.treesitter.Query
function Query.new(lang, ts_query)
local self = setmetatable({}, Query)
local query_info = ts_query:inspect() ---@type TSQueryInfo
self.query = ts_query
self.lang = lang
self.info = {
captures = query_info.captures,
patterns = query_info.patterns,
}
self.captures = self.info.captures
return self
end
local M = {} ---Information for Query, see |vim.treesitter.query.parse()|
---@class vim.treesitter.QueryInfo
---
---List of (unique) capture names defined in query.
---@field captures string[]
---
---Contains information about predicates and directives.
---Key is pattern id, and value is list of predicates or directives defined in the pattern.
---A predicate or directive is a list of (integer|string); integer represents `capture_id`, and
---string represents (literal) arguments to predicate/directive. See |treesitter-predicates|
---and |treesitter-directives| for more details.
---@field patterns table<integer, (integer|string)[][]>
---@param files string[] ---@param files string[]
---@return string[] ---@return string[]
@ -162,7 +192,7 @@ local function read_query_files(filenames)
end end
-- The explicitly set queries from |vim.treesitter.query.set()| -- The explicitly set queries from |vim.treesitter.query.set()|
---@type table<string,table<string,Query>> ---@type table<string,table<string,vim.treesitter.Query>>
local explicit_queries = setmetatable({}, { local explicit_queries = setmetatable({}, {
__index = function(t, k) __index = function(t, k)
local lang_queries = {} local lang_queries = {}
@ -201,7 +231,7 @@ end
---@param lang string Language to use for the query ---@param lang string Language to use for the query
---@param query_name string Name of the query (e.g. "highlights") ---@param query_name string Name of the query (e.g. "highlights")
--- ---
---@return Query|nil Parsed query ---@return vim.treesitter.Query|nil -- Parsed query. `nil` if no query files are found.
M.get = vim.func._memoize('concat-2', function(lang, query_name) M.get = vim.func._memoize('concat-2', function(lang, query_name)
if explicit_queries[lang][query_name] then if explicit_queries[lang][query_name] then
return explicit_queries[lang][query_name] return explicit_queries[lang][query_name]
@ -228,26 +258,24 @@ end
--- ---
--- Returns a `Query` (see |lua-treesitter-query|) object which can be used to --- Returns a `Query` (see |lua-treesitter-query|) object which can be used to
--- search nodes in the syntax tree for the patterns defined in {query} --- search nodes in the syntax tree for the patterns defined in {query}
--- using `iter_*` methods below. --- using the `iter_captures` and `iter_matches` methods.
--- ---
--- Exposes `info` and `captures` with additional context about {query}. --- Exposes `info` and `captures` with additional context about {query}.
--- - `captures` contains the list of unique capture names defined in --- - `captures` contains the list of unique capture names defined in {query}.
--- {query}. --- - `info.captures` also points to `captures`.
--- -` info.captures` also points to `captures`.
--- - `info.patterns` contains information about predicates. --- - `info.patterns` contains information about predicates.
--- ---
---@param lang string Language to use for the query ---@param lang string Language to use for the query
---@param query string Query in s-expr syntax ---@param query string Query in s-expr syntax
--- ---
---@return Query Parsed query ---@return vim.treesitter.Query Parsed query
---
---@see |vim.treesitter.query.get()|
M.parse = vim.func._memoize('concat-2', function(lang, query) M.parse = vim.func._memoize('concat-2', function(lang, query)
language.add(lang) language.add(lang)
local self = setmetatable({}, Query) local ts_query = vim._ts_parse_query(lang, query)
self.query = vim._ts_parse_query(lang, query) return Query.new(lang, ts_query)
self.info = self.query:inspect()
self.captures = self.info.captures
return self
end) end)
---@deprecated ---@deprecated

View File

@ -56,6 +56,7 @@ typedef struct {
# include "lua/treesitter.c.generated.h" # include "lua/treesitter.c.generated.h"
#endif #endif
// TSParser
static struct luaL_Reg parser_meta[] = { static struct luaL_Reg parser_meta[] = {
{ "__gc", parser_gc }, { "__gc", parser_gc },
{ "__tostring", parser_tostring }, { "__tostring", parser_tostring },
@ -70,6 +71,7 @@ static struct luaL_Reg parser_meta[] = {
{ NULL, NULL } { NULL, NULL }
}; };
// TSTree
static struct luaL_Reg tree_meta[] = { static struct luaL_Reg tree_meta[] = {
{ "__gc", tree_gc }, { "__gc", tree_gc },
{ "__tostring", tree_tostring }, { "__tostring", tree_tostring },
@ -80,6 +82,7 @@ static struct luaL_Reg tree_meta[] = {
{ NULL, NULL } { NULL, NULL }
}; };
// TSNode
static struct luaL_Reg node_meta[] = { static struct luaL_Reg node_meta[] = {
{ "__tostring", node_tostring }, { "__tostring", node_tostring },
{ "__eq", node_eq }, { "__eq", node_eq },
@ -119,6 +122,7 @@ static struct luaL_Reg node_meta[] = {
{ NULL, NULL } { NULL, NULL }
}; };
// TSQuery
static struct luaL_Reg query_meta[] = { static struct luaL_Reg query_meta[] = {
{ "__gc", query_gc }, { "__gc", query_gc },
{ "__tostring", query_tostring }, { "__tostring", query_tostring },
@ -1649,8 +1653,10 @@ static int query_inspect(lua_State *L)
return 0; return 0;
} }
uint32_t n_pat = ts_query_pattern_count(query); // TSQueryInfo
lua_createtable(L, 0, 2); // [retval] lua_createtable(L, 0, 2); // [retval]
uint32_t n_pat = ts_query_pattern_count(query);
lua_createtable(L, (int)n_pat, 1); // [retval, patterns] lua_createtable(L, (int)n_pat, 1); // [retval, patterns]
for (size_t i = 0; i < n_pat; i++) { for (size_t i = 0; i < n_pat; i++) {
uint32_t len; uint32_t len;