aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTJ DeVries <devries.timothyj@gmail.com>2021-05-01 05:19:48 -0700
committerGitHub <noreply@github.com>2021-05-01 08:19:48 -0400
commit27da5511a0c0b12fcabe29cf38c3f8a0f0b444b9 (patch)
treebd0c6670b2b0cca8400117842ed2abf9668dac90
parentca6107cfbc1b9994d8a36494965f0b270dc2b77b (diff)
downloadrneovim-27da5511a0c0b12fcabe29cf38c3f8a0f0b444b9.tar.gz
rneovim-27da5511a0c0b12fcabe29cf38c3f8a0f0b444b9.tar.bz2
rneovim-27da5511a0c0b12fcabe29cf38c3f8a0f0b444b9.zip
docs: Treesitter (#13260)
* doc & fixes: Generate treesitter docs * fixup to treesitter-core * docs(treesitter): fix docs for most functions Co-authored-by: Thomas Vigouroux <tomvig38@gmail.com>
-rw-r--r--runtime/doc/treesitter.txt563
-rw-r--r--runtime/lua/vim/treesitter.lua38
-rw-r--r--runtime/lua/vim/treesitter/health.lua4
-rw-r--r--runtime/lua/vim/treesitter/highlighter.lua22
-rw-r--r--runtime/lua/vim/treesitter/language.lua20
-rw-r--r--runtime/lua/vim/treesitter/languagetree.lua160
-rw-r--r--runtime/lua/vim/treesitter/query.lua153
-rwxr-xr-xscripts/gen_vimdoc.py76
-rw-r--r--scripts/lua2dox.lua21
-rw-r--r--src/nvim/lua/vim.lua16
10 files changed, 775 insertions, 298 deletions
diff --git a/runtime/doc/treesitter.txt b/runtime/doc/treesitter.txt
index 510585d0dd..1f4b5d3097 100644
--- a/runtime/doc/treesitter.txt
+++ b/runtime/doc/treesitter.txt
@@ -102,14 +102,14 @@ tsnode:field({name}) *tsnode:field()*
tsnode:child_count() *tsnode:child_count()*
Get the node's number of children.
-tsnode:child({index}) *tsnode:child()*
+tsnode:child({index}) *tsnode:child()*
Get the node's child at the given {index}, where zero represents the
first child.
-tsnode:named_child_count() *tsnode:named_child_count()*
+tsnode:named_child_count() *tsnode:named_child_count()*
Get the node's number of named children.
-tsnode:named_child({index}) *tsnode:named_child()*
+tsnode:named_child({index}) *tsnode:named_child()*
Get the node's named child at the given {index}, where zero represents
the first named child.
@@ -146,7 +146,7 @@ tsnode:has_error() *tsnode:has_error()*
tsnode:sexpr() *tsnode:sexpr()*
Get an S-expression representing the node as a string.
-tsnode:id() *tsnode:id()*
+tsnode:id() *tsnode:id()*
Get an unique identier for the node inside its own tree.
No guarantees are made about this identifer's internal representation,
@@ -156,16 +156,16 @@ tsnode:id() *tsnode:id()*
NB: the id is not guaranteed to be unique for nodes from different trees.
tsnode:descendant_for_range({start_row}, {start_col}, {end_row}, {end_col})
- *tsnode:descendant_for_range()*
+ *tsnode:descendant_for_range()*
Get the smallest node within this node that spans the given range of
(row, column) positions
tsnode:named_descendant_for_range({start_row}, {start_col}, {end_row}, {end_col})
- *tsnode:named_descendant_for_range()*
+ *tsnode:named_descendant_for_range()*
Get the smallest named node within this node that spans the given
range of (row, column) positions
-Query methods *lua-treesitter-query*
+Query *lua-treesitter-query*
Tree-sitter queries are supported, with some limitations. Currently, the only
supported match predicate is `eq?` (both comparing a capture against a string
@@ -178,65 +178,6 @@ and predicates. A `capture` allows you to associate names with a specific
node in a pattern. A `predicate` adds arbitrary metadata and conditional data
to a match.
-vim.treesitter.parse_query({lang}, {query})
- *vim.treesitter.parse_query()*
- Parse {query} as a string. (If the query is in a file, the caller
- should read the contents into a string before calling).
-
- 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 below. Exposes `info` and `captures` with
- additional information about the {query}.
- - `captures` contains the list of unique capture names defined in
- {query}.
- -` info.captures` also points to `captures`.
- - `info.patterns` contains information about predicates.
-
-
-query:iter_captures({node}, {bufnr}, {start_row}, {end_row})
- *query:iter_captures()*
- Iterate over all captures from all matches inside {node}.
- {bufnr} is needed if the query contains predicates, then the caller
- must ensure to use a freshly parsed tree consistent with the current
- text of the buffer. {start_row} and {end_row} can be used to limit
- matches inside a row range (this is typically used with root node
- as the node, i e to get syntax highlight matches in the current
- viewport). When omitted the start and end row values are used from
- the given node.
-
- The iterator returns three values, a numeric id identifying the capture,
- the captured node, and metadata from any directives processing the match.
- The following example shows how to get captures by name:
->
- for id, node, metadata in query:iter_captures(tree:root(), bufnr, first, last) do
- local name = query.captures[id] -- name of the capture in the query
- -- typically useful info about the node:
- local type = node:type() -- type of the captured node
- local row1, col1, row2, col2 = node:range() -- range of the capture
- ... use the info here ...
- end
-<
-query:iter_matches({node}, {bufnr}, {start_row}, {end_row})
- *query:iter_matches()*
- Iterate over all matches within a node. The arguments are the same as
- for |query:iter_captures()| but the iterated values are different:
- an (1-based) index of the pattern in the query, a table mapping
- capture indices to nodes, and metadata from any directives processing the match.
- If the query has more than one pattern the capture table might be sparse,
- and e.g. `pairs()` method should be used over `ipairs`.
- Here an example iterating over all captures in every match:
->
- for pattern, match, metadata in cquery:iter_matches(tree:root(), bufnr, first, last) do
- for id, node in pairs(match) do
- local name = query.captures[id]
- -- `node` was captured by the `name` capture in the match
-
- local node_data = metadata[id] -- Node level metadata
-
- ... use the info here ...
- end
- end
-
Treesitter Query Predicates *lua-treesitter-predicates*
When writing queries for treesitter, one might use `predicates`, that is,
@@ -298,28 +239,6 @@ Here is a list of built-in directives:
`({capture_id}, {start_row}, {start_col}, {end_row}, {end_col}, {key?})`
The default key is "offset".
- *vim.treesitter.query.add_predicate()*
-vim.treesitter.query.add_predicate({name}, {handler})
-
-This adds a predicate with the name {name} to be used in queries.
-{handler} should be a function whose signature will be : >
- handler(match, pattern, bufnr, predicate)
-<
- *vim.treesitter.query.list_predicates()*
-vim.treesitter.query.list_predicates()
-
-This lists the currently available predicates to use in queries.
-
- *vim.treesitter.query.add_directive()*
-vim.treesitter.query.add_directive({name}, {handler})
-
-This adds a directive with the name {name} to be used in queries.
-{handler} should be a function whose signature will be : >
- handler(match, pattern, bufnr, predicate, metadata)
-Handlers can set match level data by setting directly on the metadata object `metadata.key = value`
-Handlers can set node level data by using the capture id on the metadata table
-`metadata[capture_id].key = value`
-
Treesitter syntax highlighting (WIP) *lua-treesitter-highlight*
NOTE: This is a partially implemented feature, and not usable as a default
@@ -364,92 +283,434 @@ identical identifiers, highlighting both as |hl-WarningMsg|: >
((binary_expression left: (identifier) @WarningMsg.left right: (identifier) @WarningMsg.right)
(eq? @WarningMsg.left @WarningMsg.right))
+<
-Treesitter language injection (WIP) *lua-treesitter-language-injection*
+==============================================================================
+Lua module: vim.treesitter *lua-treesitter-core*
-NOTE: This is a partially implemented feature, and not usable as a default
-solution yet. What is documented here is a temporary interface intended
-for those who want to experiment with this feature and contribute to
-its development.
+get_parser({bufnr}, {lang}, {opts}) *get_parser()*
+ Gets the parser for this bufnr / ft combination.
-Languages can have nested languages within them, for example javascript inside
-HTML. We can "inject" a treesitter parser for a child language by configuring
-injection queries. Here is an example of Javascript and CSS injected into
-HTML. >
+ If needed this will create the parser. Unconditionnally attach
+ the provided callback
- local query = [[
- (script_element (raw_text) @javascript)
- (style_element (raw_text) @css)
- ]];
+ Parameters: ~
+ {bufnr} The buffer the parser should be tied to
+ {lang} The filetype of this parser
+ {opts} Options object to pass to the created language
+ tree
- local parser = vim.treesitter.get_parser(nil, nil, {
- injections = {html = query}
- })
+ Return: ~
+ The parser
- parser:parse()
+get_string_parser({str}, {lang}, {opts}) *get_string_parser()*
+ Gets a string parser
-Any capture will be treated as the node treesitter will use for the injected
-language. The capture name will be used as the language. There are a couple
-reserved captures that do not have this behavior
+ Parameters: ~
+ {str} The string to parse
+ {lang} The language of this string
+ {opts} Options to pass to the created language tree
-`@language`
-This will use a nodes text content as the language to be injected.
-`@content`
-This will use the captured nodes content as the injected content.
+==============================================================================
+Lua module: vim.treesitter.language *treesitter-language*
-`@combined`
-This will combine all matches of a pattern as one single block of content.
-By default, each match of a pattern is treated as it's own block of content
-and parsed independent of each other.
+inspect_language({lang}) *inspect_language()*
+ Inspects the provided language.
-`@<language>`
-Any other capture name will be treated as both the language and the content.
+ Inspecting provides some useful informations on the language
+ like node names, ...
-`@_<name>`
-Any capture with a leading "_" will not be treated as a language and will have
-no special processing and is useful for capturing nodes for directives.
+ Parameters: ~
+ {lang} The language.
-Injections can be configured using `directives` instead of using capture
-names. Here is an example of a directive that resolves the language based on a
-buffer variable instead of statically in the query. >
+require_language({lang}, {path}, {silent}) *require_language()*
+ Asserts that the provided language is installed, and
+ optionally provide a path for the parser
- local query = require("vim.treesitter.query")
+ Parsers are searched in the `parser` runtime directory.
- query.add_directive("inject-preprocessor!", function(_, bufnr, _, _, data)
- local success, lang = pcall(vim.api.nvim_buf_get_var, bufnr, "css_preprocessor")
+ Parameters: ~
+ {lang} The language the parser should parse
+ {path} Optional path the parser is located at
+ {silent} Don't throw an error if language not found
- data.language = success and lang or "css"
- end)
-Here is the same HTML query using this directive. >
+==============================================================================
+Lua module: vim.treesitter.query *treesitter-query*
- local query = [[
- (script_element (raw_text) @javascript)
- (style_element
- ((raw_text) @content
- (#inject-preprocessor!)))
- ]];
+add_directive({name}, {handler}, {force}) *add_directive()*
+ Adds a new directive to be used in queries
+
+ Parameters: ~
+ {name} the name of the directive, without leading #
+ {handler} the handler function to be used signature will
+ be (match, pattern, bufnr, predicate)
+
+add_predicate({name}, {handler}, {force}) *add_predicate()*
+ Adds a new predicate to be used in queries
+
+ Parameters: ~
+ {name} the name of the predicate, without leading #
+ {handler} the handler function to be used signature will
+ be (match, pattern, bufnr, predicate)
+
+get_node_text({node}, {source}) *get_node_text()*
+ Gets the text corresponding to a given node
+
+ Parameters: ~
+ {node} the node
+ {bsource} The buffer or string from which the node is
+ extracted
+
+get_query({lang}, {query_name}) *get_query()*
+ Returns the runtime query {query_name} for {lang}.
+
+ Parameters: ~
+ {lang} The language to use for the query
+ {query_name} The name of the query (i.e. "highlights")
+
+ Return: ~
+ The corresponding query, parsed.
+
+ *get_query_files()*
+get_query_files({lang}, {query_name}, {is_included})
+ Gets the list of files used to make up a query
+
+ Parameters: ~
+ {lang} The language
+ {query_name} The name of the query to load
+ {is_included} Internal parameter, most of the time left
+ as `nil`
+
+list_predicates() *list_predicates()*
+ TODO: Documentation
+
+parse_query({lang}, {query}) *parse_query()*
+ Parse {query} as a string. (If the query is in a file, the
+ caller should read the contents into a string before calling).
+
+ 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 below.
+
+ Exposes `info` and `captures` with additional information about the {query}.
+ • `captures` contains the list of unique capture names defined
+ in {query}. - `info.captures` also points to `captures` .
+ • `info.patterns` contains information about predicates.
+
+ Parameters: ~
+ {lang} The language
+ {query} A string containing the query (s-expr syntax)
+
+ Return: ~
+ The query
+
+ *Query:iter_captures()*
+Query:iter_captures({self}, {node}, {source}, {start}, {stop})
+ Iterate over all captures from all matches inside {node}
+
+ {source} is needed if the query contains predicates, then the
+ caller must ensure to use a freshly parsed tree consistent
+ with the current text of the buffer (if relevent). {start_row}
+ and {end_row} can be used to limit matches inside a row range
+ (this is typically used with root node as the node, i e to get
+ syntax highlight matches in the current viewport). When
+ omitted the start and end row values are used from the given
+ node.
+
+ The iterator returns three values, a numeric id identifying
+ the capture, the captured node, and metadata from any
+ directives processing the match. The following example shows
+ how to get captures by name:
+>
+
+ for id, node, metadata in query:iter_captures(tree:root(), bufnr, first, last) do
+ local name = query.captures[id] -- name of the capture in the query
+ -- typically useful info about the node:
+ local type = node:type() -- type of the captured node
+ local row1, col1, row2, col2 = node:range() -- range of the capture
+ ... use the info here ...
+ end
+<
+
+ Parameters: ~
+ {node} The node under which the search will occur
+ {source} The source buffer or string to exctract text
+ from
+ {start} The starting line of the search
+ {stop} The stopping line of the search (end-exclusive)
+ {self}
+
+ Return: ~
+ The matching capture id
+ The captured node
+
+ *Query:iter_matches()*
+Query:iter_matches({self}, {node}, {source}, {start}, {stop})
+ Iterates the matches of self on a given range.
+
+ Iterate over all matches within a node. The arguments are the
+ same as for |query:iter_captures()| but the iterated values
+ are different: an (1-based) index of the pattern in the query,
+ a table mapping capture indices to nodes, and metadata from
+ any directives processing the match. If the query has more
+ than one pattern the capture table might be sparse, and e.g.
+ `pairs()` method should be used over `ipairs` . Here an
+ example iterating over all captures in every match:
+>
+
+ for pattern, match, metadata in cquery:iter_matches(tree:root(), bufnr, first, last) do
+ for id, node in pairs(match) do
+ local name = query.captures[id]
+ -- `node` was captured by the `name` capture in the match
+
+ local node_data = metadata[id] -- Node level metadata
+
+ ... use the info here ...
+ end
+ end
+<
+
+ Parameters: ~
+ {node} The node under which the search will occur
+ {source} The source buffer or string to search
+ {start} The starting line of the search
+ {stop} The stopping line of the search (end-exclusive)
+ {self}
+
+ Return: ~
+ The matching pattern id
+ The matching match
+
+set_query({lang}, {query_name}, {text}) *set_query()*
+ Sets the runtime query {query_name} for {lang}
+
+ This allows users to override any runtime files and/or
+ configuration set by plugins.
+
+ Parameters: ~
+ {lang} string: The language to use for the query
+ {query_name} string: The name of the query (i.e.
+ "highlights")
+ {text} string: The query text (unparsed).
+
+
+==============================================================================
+Lua module: vim.treesitter.highlighter *treesitter-highlighter*
+
+new({tree}, {opts}) *highlighter.new()*
+ Creates a new highlighter using
+
+ Parameters: ~
+ {tree} The language tree to use for highlighting
+ {opts} Table used to configure the highlighter
+ • queries: Table to overwrite queries used by the
+ highlighter
+
+TSHighlighter:destroy({self}) *TSHighlighter:destroy()*
+ Removes all internal references to the highlighter
+
+ Parameters: ~
+ {self}
+
+TSHighlighter:get_query({self}, {lang}) *TSHighlighter:get_query()*
+ Gets the query used for
+
+ Parameters: ~
+ {lang} A language used by the highlighter.
+ {self}
+
+
+==============================================================================
+Lua module: vim.treesitter.languagetree *treesitter-languagetree*
+
+LanguageTree:add_child({self}, {lang}) *LanguageTree:add_child()*
+ Adds a child language to this tree.
+
+ If the language already exists as a child, it will first be
+ removed.
+
+ Parameters: ~
+ {lang} The language to add.
+ {self}
+
+LanguageTree:children({self}) *LanguageTree:children()*
+ Returns a map of language to child tree.
+
+ Parameters: ~
+ {self}
+
+LanguageTree:contains({self}, {range}) *LanguageTree:contains()*
+ Determines wether This goes down the tree to recursively check childs.
+
+ Parameters: ~
+ {range} is contained in this language tree
+
+ Parameters: ~
+ {range} A range, that is a `{ start_line, start_col,
+ end_line, end_col }` table.
+ {self}
+
+LanguageTree:destroy({self}) *LanguageTree:destroy()*
+ Destroys this language tree and all its children.
+
+ Any cleanup logic should be performed here. Note, this DOES
+ NOT remove this tree from a parent. `remove_child` must be called on the parent to remove it.
+
+ Parameters: ~
+ {self}
+
+ *LanguageTree:for_each_child()*
+LanguageTree:for_each_child({self}, {fn}, {include_self})
+ Invokes the callback for each LanguageTree and it's children
+ recursively
+
+ Parameters: ~
+ {fn} The function to invoke. This is invoked
+ with arguments (tree: LanguageTree, lang:
+ string)
+ {include_self} Whether to include the invoking tree in
+ the results.
+ {self}
+
+LanguageTree:for_each_tree({self}, {fn}) *LanguageTree:for_each_tree()*
+ Invokes the callback for each treesitter trees recursively.
+
+ Note, this includes the invoking language tree's trees as
+ well.
+
+ Parameters: ~
+ {fn} The callback to invoke. The callback is invoked
+ with arguments (tree: TSTree, languageTree:
+ LanguageTree)
+ {self}
+
+LanguageTree:included_regions({self}) *LanguageTree:included_regions()*
+ Gets the set of included regions
+
+ Parameters: ~
+ {self}
+
+LanguageTree:invalidate({self}, {reload}) *LanguageTree:invalidate()*
+ Invalidates this parser and all its children
+
+ Parameters: ~
+ {self}
+
+LanguageTree:is_valid({self}) *LanguageTree:is_valid()*
+ Determines whether this tree is valid. If the tree is invalid, `parse()` must be called to get the an updated tree.
+
+ Parameters: ~
+ {self}
+
+LanguageTree:lang({self}) *LanguageTree:lang()*
+ Gets the language of this tree node.
+
+ Parameters: ~
+ {self}
+
+ *LanguageTree:language_for_range()*
+LanguageTree:language_for_range({self}, {range})
+ Gets the appropriate language that contains
+
+ Parameters: ~
+ {range} A text range, see |LanguageTree:contains|
+ {self}
+
+LanguageTree:parse({self}) *LanguageTree:parse()*
+ Parses all defined regions using a treesitter parser for the
+ language this tree represents. This will run the injection
+ query for this language to determine if any child languages
+ should be created.
+
+ Parameters: ~
+ {self}
+
+LanguageTree:register_cbs({self}, {cbs}) *LanguageTree:register_cbs()*
+ Registers callbacks for the parser
+
+ Parameters: ~
+ {cbs} An `nvim_buf_attach` -like table argument with the following keys : `on_bytes` : see `nvim_buf_attach` , but this will be called after the parsers callback. `on_changedtree` : a callback that will be called every time the
+ tree has syntactical changes. it will only be
+ passed one argument, that is a table of the ranges
+ (as node ranges) that changed. `on_child_added` : emitted when a child is added to the tree. `on_child_removed` : emitted when a child is removed from the tree.
+ {self}
+
+LanguageTree:remove_child({self}, {lang}) *LanguageTree:remove_child()*
+ Removes a child language from this tree.
+
+ Parameters: ~
+ {lang} The language to remove.
+ {self}
+
+ *LanguageTree:set_included_regions()*
+LanguageTree:set_included_regions({self}, {regions})
+ Sets the included regions that should be parsed by this
+ parser. A region is a set of nodes and/or ranges that will be
+ parsed in the same context.
+
+ For example, `{ { node1 }, { node2} }` is two separate
+ regions. This will be parsed by the parser in two different
+ contexts... thus resulting in two separate trees.
+
+ `{ { node1, node2 } }` is a single region consisting of two
+ nodes. This will be parsed by the parser in a single
+ context... thus resulting in a single tree.
+
+ This allows for embedded languages to be parsed together
+ across different nodes, which is useful for templating
+ languages like ERB and EJS.
+
+ Note, this call invalidates the tree and requires it to be
+ parsed again.
+
+ Parameters: ~
+ {regions} A list of regions this tree should manage and
+ parse.
+ {self}
+
+LanguageTree:source({self}) *LanguageTree:source()*
+ Returns the source content of the language tree (bufnr or
+ string).
+
+ Parameters: ~
+ {self}
+
+LanguageTree:trees({self}) *LanguageTree:trees()*
+ Returns all trees this language tree contains. Does not
+ include child languages.
+
+ Parameters: ~
+ {self}
+
+new({source}, {lang}, {opts}) *languagetree.new()*
+ Represents a single treesitter parser for a language. The
+ language can contain child languages with in its range, hence
+ the tree.
- local parser = vim.treesitter.get_parser(nil, nil, {
- injections = {html = query}
- })
+ Parameters: ~
+ {source} Can be a bufnr or a string of text to
+ parse
+ {lang} The language this tree represents
+ {opts} Options table
+ {opts.injections} A table of language to injection query
+ strings. This is useful for overriding
+ the built-in runtime file searching for
+ the injection language query per
+ language.
- parser:parse()
-The following properties can be attached to the metadata object provided to
-the directive.
+==============================================================================
+Lua module: vim.treesitter.health *treesitter-health*
-`language`
-Same as the language capture.
+check_health() *check_health()*
+ TODO: Documentation
-`content`
-A list of ranges or nodes to inject as content. These ranges and/or nodes will
-be treated as combined source and will be parsed within the same context. This
-differs from the `@content` capture which only captures a single node as
-content. This can also be a single number that references a captured node.
+list_parsers() *list_parsers()*
+ Lists the parsers currently installed
-`combined`
-Same as the combined capture.
+ Return: ~
+ A list of parsers
vim:tw=78:ts=8:ft=help:norl:
diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua
index f223c7b8c8..de997b2d86 100644
--- a/runtime/lua/vim/treesitter.lua
+++ b/runtime/lua/vim/treesitter.lua
@@ -25,12 +25,12 @@ setmetatable(M, {
})
--- Creates a new parser.
---
--- It is not recommended to use this, use vim.treesitter.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 language tree
+---
+--- It is not recommended to use this, use vim.treesitter.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
function M._create_parser(bufnr, lang, opts)
language.require_language(lang)
if bufnr == 0 then
@@ -41,10 +41,12 @@ function M._create_parser(bufnr, lang, opts)
local self = LanguageTree.new(bufnr, lang, opts)
+ ---@private
local function bytes_cb(_, ...)
self:_on_bytes(...)
end
+ ---@private
local function detach_cb(_, ...)
if parsers[bufnr] == self then
parsers[bufnr] = nil
@@ -52,6 +54,7 @@ function M._create_parser(bufnr, lang, opts)
self:_on_detach(...)
end
+ ---@private
local function reload_cb(_, ...)
self:_on_reload(...)
end
@@ -64,15 +67,15 @@ function M._create_parser(bufnr, lang, opts)
end
--- Gets the parser for this bufnr / ft combination.
---
--- If needed this will create the parser.
--- Unconditionnally attach the provided callback
---
--- @param bufnr The buffer the parser should be tied to
--- @param ft The filetype of this parser
--- @param opts Options object to pass to the parser
---
--- @returns The parser
+---
+--- If needed this will create the parser.
+--- Unconditionnally attach the provided callback
+---
+--- @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
+---
+--- @returns The parser
function M.get_parser(bufnr, lang, opts)
opts = opts or {}
@@ -92,6 +95,11 @@ function M.get_parser(bufnr, lang, opts)
return parsers[bufnr]
end
+--- Gets a string parser
+---
+--- @param str The string to parse
+--- @param lang The language of this string
+--- @param opts Options to pass to the created language tree
function M.get_string_parser(str, lang, opts)
vim.validate {
str = { str, 'string' },
diff --git a/runtime/lua/vim/treesitter/health.lua b/runtime/lua/vim/treesitter/health.lua
index dd0b11a6c7..e031ba1bd6 100644
--- a/runtime/lua/vim/treesitter/health.lua
+++ b/runtime/lua/vim/treesitter/health.lua
@@ -1,10 +1,14 @@
local M = {}
local ts = vim.treesitter
+--- Lists the parsers currently installed
+---
+---@return A list of parsers
function M.list_parsers()
return vim.api.nvim_get_runtime_file('parser/*', true)
end
+--- Performs a healthcheck for treesitter integration
function M.check_health()
local report_info = vim.fn['health#report_info']
local report_ok = vim.fn['health#report_ok']
diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua
index fe7e1052c9..84b6a5f135 100644
--- a/runtime/lua/vim/treesitter/highlighter.lua
+++ b/runtime/lua/vim/treesitter/highlighter.lua
@@ -70,11 +70,13 @@ TSHighlighter.hl_map = {
["include"] = "Include",
}
+---@private
local function is_highlight_name(capture_name)
local firstc = string.sub(capture_name, 1, 1)
return firstc ~= string.lower(firstc)
end
+---@private
function TSHighlighterQuery.new(lang, query_string)
local self = setmetatable({}, { __index = TSHighlighterQuery })
@@ -99,10 +101,12 @@ function TSHighlighterQuery.new(lang, query_string)
return self
end
+---@private
function TSHighlighterQuery:query()
return self._query
end
+---@private
--- Get the hl from capture.
--- Returns a tuple { highlight_name: string, is_builtin: bool }
function TSHighlighterQuery:_get_hl_from_capture(capture)
@@ -116,6 +120,11 @@ function TSHighlighterQuery:_get_hl_from_capture(capture)
end
end
+--- Creates a new highlighter using @param tree
+---
+--- @param tree The language tree to use for highlighting
+--- @param opts Table used to configure the highlighter
+--- - queries: Table to overwrite queries used by the highlighter
function TSHighlighter.new(tree, opts)
local self = setmetatable({}, TSHighlighter)
@@ -165,12 +174,14 @@ function TSHighlighter.new(tree, opts)
return self
end
+--- Removes all internal references to the highlighter
function TSHighlighter:destroy()
if TSHighlighter.active[self.bufnr] then
TSHighlighter.active[self.bufnr] = nil
end
end
+---@private
function TSHighlighter:get_highlight_state(tstree)
if not self._highlight_states[tstree] then
self._highlight_states[tstree] = {
@@ -182,24 +193,31 @@ function TSHighlighter:get_highlight_state(tstree)
return self._highlight_states[tstree]
end
+---@private
function TSHighlighter:reset_highlight_state()
self._highlight_states = {}
end
+---@private
function TSHighlighter:on_bytes(_, _, start_row, _, _, _, _, _, new_end)
a.nvim__buf_redraw_range(self.bufnr, start_row, start_row + new_end + 1)
end
+---@private
function TSHighlighter:on_detach()
self:destroy()
end
+---@private
function TSHighlighter:on_changedtree(changes)
for _, ch in ipairs(changes or {}) do
a.nvim__buf_redraw_range(self.bufnr, ch[1], ch[3]+1)
end
end
+--- Gets the query used for @param lang
+---
+--- @param lang A language used by the highlighter.
function TSHighlighter:get_query(lang)
if not self._queries[lang] then
self._queries[lang] = TSHighlighterQuery.new(lang)
@@ -208,6 +226,7 @@ function TSHighlighter:get_query(lang)
return self._queries[lang]
end
+---@private
local function on_line_impl(self, buf, line)
self.tree:for_each_tree(function(tstree, tree)
if not tstree then return end
@@ -251,6 +270,7 @@ local function on_line_impl(self, buf, line)
end, true)
end
+---@private
function TSHighlighter._on_line(_, _win, buf, line, _)
local self = TSHighlighter.active[buf]
if not self then return end
@@ -258,6 +278,7 @@ function TSHighlighter._on_line(_, _win, buf, line, _)
on_line_impl(self, buf, line)
end
+---@private
function TSHighlighter._on_buf(_, buf)
local self = TSHighlighter.active[buf]
if self then
@@ -265,6 +286,7 @@ function TSHighlighter._on_buf(_, buf)
end
end
+---@private
function TSHighlighter._on_win(_, _win, buf, _topline)
local self = TSHighlighter.active[buf]
if not self then
diff --git a/runtime/lua/vim/treesitter/language.lua b/runtime/lua/vim/treesitter/language.lua
index eed28e0e41..6dc37c7848 100644
--- a/runtime/lua/vim/treesitter/language.lua
+++ b/runtime/lua/vim/treesitter/language.lua
@@ -3,12 +3,12 @@ local a = vim.api
local M = {}
--- Asserts that the provided language is installed, and optionally provide a path for the parser
---
--- Parsers are searched in the `parser` runtime directory.
---
--- @param lang The language the parser should parse
--- @param path Optional path the parser is located at
--- @param silent Don't throw an error if language not found
+---
+--- Parsers are searched in the `parser` runtime directory.
+---
+--- @param lang The language the parser should parse
+--- @param path Optional path the parser is located at
+--- @param silent Don't throw an error if language not found
function M.require_language(lang, path, silent)
if vim._ts_has_language(lang) then
return true
@@ -37,10 +37,10 @@ function M.require_language(lang, path, silent)
end
--- Inspects the provided language.
---
--- Inspecting provides some useful informations on the language like node names, ...
---
--- @param lang The language.
+---
+--- Inspecting provides some useful informations on the language like node names, ...
+---
+--- @param lang The language.
function M.inspect_language(lang)
M.require_language(lang)
return vim._ts_inspect_language(lang)
diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua
index 2f5aeb0710..899d90e464 100644
--- a/runtime/lua/vim/treesitter/languagetree.lua
+++ b/runtime/lua/vim/treesitter/languagetree.lua
@@ -5,16 +5,16 @@ local language = require'vim.treesitter.language'
local LanguageTree = {}
LanguageTree.__index = LanguageTree
--- Represents a single treesitter parser for a language.
--- The language can contain child languages with in its range,
--- hence the tree.
---
--- @param source Can be a bufnr or a string of text to parse
--- @param lang The language this tree represents
--- @param opts Options table
--- @param opts.injections A table of language to injection query strings.
--- This is useful for overriding the built-in runtime file
--- searching for the injection language query per language.
+--- Represents a single treesitter parser for a language.
+--- The language can contain child languages with in its range,
+--- hence the tree.
+---
+--- @param source Can be a bufnr or a string of text to parse
+--- @param lang The language this tree represents
+--- @param opts Options table
+--- @param opts.injections A table of language to injection query strings.
+--- This is useful for overriding the built-in runtime file
+--- searching for the injection language query per language.
function LanguageTree.new(source, lang, opts)
language.require_language(lang)
opts = opts or {}
@@ -50,7 +50,7 @@ function LanguageTree.new(source, lang, opts)
return self
end
--- Invalidates this parser and all its children
+--- Invalidates this parser and all its children
function LanguageTree:invalidate(reload)
self._valid = false
@@ -64,38 +64,38 @@ function LanguageTree:invalidate(reload)
end
end
--- Returns all trees this language tree contains.
--- Does not include child languages.
+--- Returns all trees this language tree contains.
+--- Does not include child languages.
function LanguageTree:trees()
return self._trees
end
--- Gets the language of this tree layer.
+--- Gets the language of this tree node.
function LanguageTree:lang()
return self._lang
end
--- Determines whether this tree is valid.
--- If the tree is invalid, `parse()` must be called
--- to get the an updated tree.
+--- Determines whether this tree is valid.
+--- If the tree is invalid, `parse()` must be called
+--- to get the an updated tree.
function LanguageTree:is_valid()
return self._valid
end
--- Returns a map of language to child tree.
+--- Returns a map of language to child tree.
function LanguageTree:children()
return self._children
end
--- Returns the source content of the language tree (bufnr or string).
+--- Returns the source content of the language tree (bufnr or string).
function LanguageTree:source()
return self._source
end
--- Parses all defined regions using a treesitter parser
--- for the language this tree represents.
--- This will run the injection query for this language to
--- determine if any child languages should be created.
+--- Parses all defined regions using a treesitter parser
+--- for the language this tree represents.
+--- This will run the injection query for this language to
+--- determine if any child languages should be created.
function LanguageTree:parse()
if self._valid then
return self._trees
@@ -169,9 +169,10 @@ function LanguageTree:parse()
return self._trees, changes
end
--- Invokes the callback for each LanguageTree and it's children recursively
--- @param fn The function to invoke. This is invoked with arguments (tree: LanguageTree, lang: string)
--- @param include_self Whether to include the invoking tree in the results.
+--- Invokes the callback for each LanguageTree and it's children recursively
+---
+--- @param fn The function to invoke. This is invoked with arguments (tree: LanguageTree, lang: string)
+--- @param include_self Whether to include the invoking tree in the results.
function LanguageTree:for_each_child(fn, include_self)
if include_self then
fn(self, self._lang)
@@ -182,10 +183,12 @@ function LanguageTree:for_each_child(fn, include_self)
end
end
--- Invokes the callback for each treesitter trees recursively.
--- Note, this includes the invoking language tree's trees as well.
--- @param fn The callback to invoke. The callback is invoked with arguments
--- (tree: TSTree, languageTree: LanguageTree)
+--- Invokes the callback for each treesitter trees recursively.
+---
+--- Note, this includes the invoking language tree's trees as well.
+---
+--- @param fn The callback to invoke. The callback is invoked with arguments
+--- (tree: TSTree, languageTree: LanguageTree)
function LanguageTree:for_each_tree(fn)
for _, tree in ipairs(self._trees) do
fn(tree, self)
@@ -196,9 +199,11 @@ function LanguageTree:for_each_tree(fn)
end
end
--- Adds a child language to this tree.
--- If the language already exists as a child, it will first be removed.
--- @param lang The language to add.
+--- Adds a child language to this tree.
+---
+--- If the language already exists as a child, it will first be removed.
+---
+--- @param lang The language to add.
function LanguageTree:add_child(lang)
if self._children[lang] then
self:remove_child(lang)
@@ -212,8 +217,9 @@ function LanguageTree:add_child(lang)
return self._children[lang]
end
--- Removes a child language from this tree.
--- @param lang The language to remove.
+--- Removes a child language from this tree.
+---
+--- @param lang The language to remove.
function LanguageTree:remove_child(lang)
local child = self._children[lang]
@@ -225,10 +231,11 @@ function LanguageTree:remove_child(lang)
end
end
--- Destroys this language tree and all its children.
--- Any cleanup logic should be performed here.
--- Note, this DOES NOT remove this tree from a parent.
--- `remove_child` must be called on the parent to remove it.
+--- Destroys this language tree and all its children.
+---
+--- Any cleanup logic should be performed here.
+--- Note, this DOES NOT remove this tree from a parent.
+--- `remove_child` must be called on the parent to remove it.
function LanguageTree:destroy()
-- Cleanup here
for _, child in ipairs(self._children) do
@@ -236,23 +243,23 @@ function LanguageTree:destroy()
end
end
--- Sets the included regions that should be parsed by this parser.
--- A region is a set of nodes and/or ranges that will be parsed in the same context.
---
--- For example, `{ { node1 }, { node2} }` is two separate regions.
--- This will be parsed by the parser in two different contexts... thus resulting
--- in two separate trees.
---
--- `{ { node1, node2 } }` is a single region consisting of two nodes.
--- This will be parsed by the parser in a single context... thus resulting
--- in a single tree.
---
--- This allows for embedded languages to be parsed together across different
--- nodes, which is useful for templating languages like ERB and EJS.
---
--- Note, this call invalidates the tree and requires it to be parsed again.
---
--- @param regions A list of regions this tree should manage and parse.
+--- Sets the included regions that should be parsed by this parser.
+--- A region is a set of nodes and/or ranges that will be parsed in the same context.
+---
+--- For example, `{ { node1 }, { node2} }` is two separate regions.
+--- This will be parsed by the parser in two different contexts... thus resulting
+--- in two separate trees.
+---
+--- `{ { node1, node2 } }` is a single region consisting of two nodes.
+--- This will be parsed by the parser in a single context... thus resulting
+--- in a single tree.
+---
+--- This allows for embedded languages to be parsed together across different
+--- nodes, which is useful for templating languages like ERB and EJS.
+---
+--- Note, this call invalidates the tree and requires it to be parsed again.
+---
+--- @param regions A list of regions this tree should manage and parse.
function LanguageTree:set_included_regions(regions)
-- TODO(vigoux): I don't think string parsers are useful for now
if type(self._source) == "number" then
@@ -281,16 +288,18 @@ function LanguageTree:set_included_regions(regions)
self:invalidate()
end
--- Gets the set of included regions
+--- Gets the set of included regions
function LanguageTree:included_regions()
return self._regions
end
--- Gets language injection points by language.
--- This is where most of the injection processing occurs.
--- TODO: Allow for an offset predicate to tailor the injection range
--- instead of using the entire nodes range.
--- @private
+--- Gets language injection points by language.
+---
+--- This is where most of the injection processing occurs.
+---
+--- TODO: Allow for an offset predicate to tailor the injection range
+--- instead of using the entire nodes range.
+--- @private
function LanguageTree:_get_injections()
if not self._injection_query then return {} end
@@ -395,12 +404,14 @@ function LanguageTree:_get_injections()
return result
end
+---@private
function LanguageTree:_do_callback(cb_name, ...)
for _, cb in ipairs(self._callbacks[cb_name]) do
cb(...)
end
end
+---@private
function LanguageTree:_on_bytes(bufnr, changed_tick,
start_row, start_col, start_byte,
old_row, old_col, old_byte,
@@ -425,24 +436,26 @@ function LanguageTree:_on_bytes(bufnr, changed_tick,
new_row, new_col, new_byte)
end
+---@private
function LanguageTree:_on_reload()
self:invalidate(true)
end
+---@private
function LanguageTree:_on_detach(...)
self:invalidate(true)
self:_do_callback('detach', ...)
end
--- Registers callbacks for the parser
--- @param cbs An `nvim_buf_attach`-like table argument with the following keys :
--- `on_bytes` : see `nvim_buf_attach`, but this will be called _after_ the parsers callback.
--- `on_changedtree` : a callback that will be called every time the tree has syntactical changes.
--- it will only be passed one argument, that is a table of the ranges (as node ranges) that
--- changed.
--- `on_child_added` : emitted when a child is added to the tree.
--- `on_child_removed` : emitted when a child is removed from the tree.
+--- @param cbs An `nvim_buf_attach`-like table argument with the following keys :
+--- `on_bytes` : see `nvim_buf_attach`, but this will be called _after_ the parsers callback.
+--- `on_changedtree` : a callback that will be called every time the tree has syntactical changes.
+--- it will only be passed one argument, that is a table of the ranges (as node ranges) that
+--- changed.
+--- `on_child_added` : emitted when a child is added to the tree.
+--- `on_child_removed` : emitted when a child is removed from the tree.
function LanguageTree:register_cbs(cbs)
if not cbs then return end
@@ -467,6 +480,7 @@ function LanguageTree:register_cbs(cbs)
end
end
+---@private
local function tree_contains(tree, range)
local start_row, start_col, end_row, end_col = tree:root():range()
local start_fits = start_row < range[1] or (start_row == range[1] and start_col <= range[2])
@@ -479,6 +493,11 @@ local function tree_contains(tree, range)
return false
end
+--- Determines wether @param range is contained in this language tree
+---
+--- This goes down the tree to recursively check childs.
+---
+--- @param range A range, that is a `{ start_line, start_col, end_line, end_col }` table.
function LanguageTree:contains(range)
for _, tree in pairs(self._trees) do
if tree_contains(tree, range) then
@@ -489,6 +508,9 @@ function LanguageTree:contains(range)
return false
end
+--- Gets the appropriate language that contains @param range
+---
+--- @param range A text range, see |LanguageTree:contains|
function LanguageTree:language_for_range(range)
for _, child in pairs(self._children) do
if child:contains(range) then
diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua
index ed5146be44..db6d7e4dc0 100644
--- a/runtime/lua/vim/treesitter/query.lua
+++ b/runtime/lua/vim/treesitter/query.lua
@@ -8,6 +8,7 @@ Query.__index = Query
local M = {}
+---@private
local function dedupe_files(files)
local result = {}
local seen = {}
@@ -22,6 +23,7 @@ local function dedupe_files(files)
return result
end
+---@private
local function safe_read(filename, read_quantifier)
local file, err = io.open(filename, 'r')
if not file then
@@ -32,6 +34,11 @@ local function safe_read(filename, read_quantifier)
return content
end
+--- Gets the list of files used to make up a query
+---
+--- @param lang The language
+--- @param query_name The name of the query to load
+--- @param is_included Internal parameter, most of the time left as `nil`
function M.get_query_files(lang, query_name, is_included)
local query_path = string.format('queries/%s/%s.scm', lang, query_name)
local lang_files = dedupe_files(a.nvim_get_runtime_file(query_path, true))
@@ -79,6 +86,7 @@ function M.get_query_files(lang, query_name, is_included)
return query_files
end
+---@private
local function read_query_files(filenames)
local contents = {}
@@ -103,19 +111,20 @@ local explicit_queries = setmetatable({}, {
---
--- This allows users to override any runtime files and/or configuration
--- set by plugins.
----@param lang string: The language to use for the query
----@param query_name string: The name of the query (i.e. "highlights")
----@param text string: The query text (unparsed).
+---
+--- @param lang string: The language to use for the query
+--- @param query_name string: The name of the query (i.e. "highlights")
+--- @param text string: The query text (unparsed).
function M.set_query(lang, query_name, text)
explicit_queries[lang][query_name] = M.parse_query(lang, text)
end
--- Returns the runtime query {query_name} for {lang}.
---
--- @param lang The language to use for the query
--- @param query_name The name of the query (i.e. "highlights")
---
--- @return The corresponding query, parsed.
+---
+--- @param lang The language to use for the query
+--- @param query_name The name of the query (i.e. "highlights")
+---
+--- @return The corresponding query, parsed.
function M.get_query(lang, query_name)
if explicit_queries[lang][query_name] then
return explicit_queries[lang][query_name]
@@ -129,12 +138,23 @@ function M.get_query(lang, query_name)
end
end
---- Parses a query.
---
--- @param language The language
--- @param query A string containing the query (s-expr syntax)
---
--- @returns The query
+--- Parse {query} as a string. (If the query is in a file, the caller
+--- should read the contents into a string before calling).
+---
+--- 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 below.
+---
+--- Exposes `info` and `captures` with additional information about the {query}.
+--- - `captures` contains the list of unique capture names defined in
+--- {query}.
+--- -` info.captures` also points to `captures`.
+--- - `info.patterns` contains information about predicates.
+---
+--- @param lang The language
+--- @param query A string containing the query (s-expr syntax)
+---
+--- @returns The query
function M.parse_query(lang, query)
language.require_language(lang)
local self = setmetatable({}, Query)
@@ -147,8 +167,9 @@ end
-- TODO(vigoux): support multiline nodes too
--- Gets the text corresponding to a given node
--- @param node the node
--- @param bufnr the buffer from which the node is extracted.
+---
+--- @param node the node
+--- @param bsource The buffer or string from which the node is extracted
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_()
@@ -200,6 +221,7 @@ local predicate_handlers = {
["match?"] = (function()
local magic_prefixes = {['\\v']=true, ['\\m']=true, ['\\M']=true, ['\\V']=true}
+ ---@private
local function check_magic(str)
if string.len(str) < 2 or magic_prefixes[string.sub(str,1,2)] then
return str
@@ -282,10 +304,10 @@ local directive_handlers = {
}
--- Adds a new predicate to be used in queries
---
--- @param name the name of the predicate, without leading #
--- @param handler the handler function to be used
--- signature will be (match, pattern, bufnr, predicate)
+---
+--- @param name the name of the predicate, without leading #
+--- @param handler the handler function to be used
+--- signature will be (match, pattern, bufnr, predicate)
function M.add_predicate(name, handler, force)
if predicate_handlers[name] and not force then
error(string.format("Overriding %s", name))
@@ -295,10 +317,10 @@ function M.add_predicate(name, handler, force)
end
--- Adds a new directive to be used in queries
---
--- @param name the name of the directive, without leading #
--- @param handler the handler function to be used
--- signature will be (match, pattern, bufnr, predicate)
+---
+--- @param name the name of the directive, without leading #
+--- @param handler the handler function to be used
+--- signature will be (match, pattern, bufnr, predicate)
function M.add_directive(name, handler, force)
if directive_handlers[name] and not force then
error(string.format("Overriding %s", name))
@@ -312,14 +334,17 @@ function M.list_predicates()
return vim.tbl_keys(predicate_handlers)
end
+---@private
local function xor(x, y)
return (x or y) and not (x and y)
end
+---@private
local function is_directive(name)
return string.sub(name, -1) == "!"
end
+---@private
function Query:match_preds(match, pattern, source)
local preds = self.info.patterns[pattern]
@@ -358,7 +383,7 @@ function Query:match_preds(match, pattern, source)
return true
end
---- Applies directives against a match and pattern.
+---@private
function Query:apply_directives(match, pattern, source, metadata)
local preds = self.info.patterns[pattern]
@@ -380,6 +405,7 @@ end
--- Returns the start and stop value if set else the node's range.
-- When the node's range is used, the stop is incremented by 1
-- to make the search inclusive.
+---@private
local function value_or_node_range(start, stop, node)
if start == nil and stop == nil then
local node_start, _, node_stop, _ = node:range()
@@ -389,15 +415,36 @@ local function value_or_node_range(start, stop, node)
return start, stop
end
---- Iterates of the captures of self on a given range.
---
--- @param node The node under which the search will occur
--- @param buffer The source buffer to search
--- @param start The starting line of the search
--- @param stop The stopping line of the search (end-exclusive)
---
--- @returns The matching capture id
--- @returns The captured node
+--- Iterate over all captures from all matches inside {node}
+---
+--- {source} is needed if the query contains predicates, then the caller
+--- must ensure to use a freshly parsed tree consistent with the current
+--- text of the buffer (if relevent). {start_row} and {end_row} can be used to limit
+--- matches inside a row range (this is typically used with root node
+--- as the node, i e to get syntax highlight matches in the current
+--- viewport). When omitted the start and end row values are used from the given node.
+---
+--- The iterator returns three values, a numeric id identifying the capture,
+--- the captured node, and metadata from any directives processing the match.
+--- The following example shows how to get captures by name:
+---
+--- <pre>
+--- for id, node, metadata in query:iter_captures(tree:root(), bufnr, first, last) do
+--- local name = query.captures[id] -- name of the capture in the query
+--- -- typically useful info about the node:
+--- local type = node:type() -- type of the captured node
+--- local row1, col1, row2, col2 = node:range() -- range of the capture
+--- ... use the info here ...
+--- end
+--- </pre>
+---
+--- @param node The node under which the search will occur
+--- @param source The source buffer or string to exctract text from
+--- @param start The starting line of the search
+--- @param stop The stopping line of the search (end-exclusive)
+---
+--- @returns The matching capture id
+--- @returns The captured node
function Query:iter_captures(node, source, start, stop)
if type(source) == "number" and source == 0 then
source = vim.api.nvim_get_current_buf()
@@ -406,6 +453,7 @@ function Query:iter_captures(node, source, start, stop)
start, stop = value_or_node_range(start, stop, node)
local raw_iter = node:_rawquery(self.query, true, start, stop)
+ ---@private
local function iter()
local capture, captured_node, match = raw_iter()
local metadata = {}
@@ -425,14 +473,35 @@ function Query:iter_captures(node, source, start, stop)
end
--- Iterates the matches of self on a given range.
---
--- @param node The node under which the search will occur
--- @param buffer The source buffer to search
--- @param start The starting line of the search
--- @param stop The stopping line of the search (end-exclusive)
---
--- @returns The matching pattern id
--- @returns The matching match
+---
+--- Iterate over all matches within a node. The arguments are the same as
+--- for |query:iter_captures()| but the iterated values are different:
+--- an (1-based) index of the pattern in the query, a table mapping
+--- capture indices to nodes, and metadata from any directives processing the match.
+--- If the query has more than one pattern the capture table might be sparse,
+--- and e.g. `pairs()` method should be used over `ipairs`.
+--- Here an example iterating over all captures in every match:
+---
+--- <pre>
+--- for pattern, match, metadata in cquery:iter_matches(tree:root(), bufnr, first, last) do
+--- for id, node in pairs(match) do
+--- local name = query.captures[id]
+--- -- `node` was captured by the `name` capture in the match
+---
+--- local node_data = metadata[id] -- Node level metadata
+---
+--- ... use the info here ...
+--- end
+--- end
+--- </pre>
+---
+--- @param node The node under which the search will occur
+--- @param source The source buffer or string to search
+--- @param start The starting line of the search
+--- @param stop The stopping line of the search (end-exclusive)
+---
+--- @returns The matching pattern id
+--- @returns The matching match
function Query:iter_matches(node, source, start, stop)
if type(source) == "number" and source == 0 then
source = vim.api.nvim_get_current_buf()
diff --git a/scripts/gen_vimdoc.py b/scripts/gen_vimdoc.py
index b4d896fecc..d46306d41a 100755
--- a/scripts/gen_vimdoc.py
+++ b/scripts/gen_vimdoc.py
@@ -186,6 +186,48 @@ CONFIG = {
'module_override': {},
'append_only': [],
},
+ 'treesitter': {
+ 'mode': 'lua',
+ 'filename': 'treesitter.txt',
+ 'section_start_token': '*lua-treesitter-core*',
+ 'section_order': [
+ 'treesitter.lua',
+ 'language.lua',
+ 'query.lua',
+ 'highlighter.lua',
+ 'languagetree.lua',
+ 'health.lua',
+ ],
+ 'files': ' '.join([
+ os.path.join(base_dir, 'runtime/lua/vim/treesitter.lua'),
+ os.path.join(base_dir, 'runtime/lua/vim/treesitter/'),
+ ]),
+ 'file_patterns': '*.lua',
+ 'fn_name_prefix': '',
+ 'section_name': {},
+ 'section_fmt': lambda name: (
+ 'Lua module: vim.treesitter'
+ if name.lower() == 'treesitter'
+ else f'Lua module: vim.treesitter.{name.lower()}'),
+ 'helptag_fmt': lambda name: (
+ '*lua-treesitter-core*'
+ if name.lower() == 'treesitter'
+ else f'*treesitter-{name.lower()}*'),
+ 'fn_helptag_fmt': lambda fstem, name: (
+ f'*{name}()*'
+ if name != 'new'
+ else f'*{fstem}.{name}()*'),
+ # 'fn_helptag_fmt': lambda fstem, name: (
+ # f'*vim.treesitter.{name}()*'
+ # if fstem == 'treesitter'
+ # else (
+ # '*vim.lsp.client*'
+ # # HACK. TODO(justinmk): class/structure support in lua2dox
+ # if 'lsp.client' == f'{fstem}.{name}'
+ # else f'*vim.lsp.{fstem}.{name}()*')),
+ 'module_override': {},
+ 'append_only': [],
+ }
}
param_exclude = (
@@ -666,15 +708,6 @@ def extract_from_xml(filename, target, width):
annotations = filter(None, map(lambda x: annotation_map.get(x),
annotations.split()))
- if not fmt_vimhelp:
- pass
- else:
- fstem = '?'
- if '.' in compoundname:
- fstem = compoundname.split('.')[0]
- fstem = CONFIG[target]['module_override'].get(fstem, fstem)
- vimtag = CONFIG[target]['fn_helptag_fmt'](fstem, name)
-
params = []
type_length = 0
@@ -695,17 +728,37 @@ def extract_from_xml(filename, target, width):
if fmt_vimhelp and param_type.endswith('*'):
param_type = param_type.strip('* ')
param_name = '*' + param_name
+
type_length = max(type_length, len(param_type))
params.append((param_type, param_name))
+ # Handle Object Oriented style functions here.
+ # We make sure they have "self" in the parameters,
+ # and a parent function
+ if return_type.startswith('function') \
+ and len(return_type.split(' ')) >= 2 \
+ and any(x[1] == 'self' for x in params):
+ split_return = return_type.split(' ')
+ name = f'{split_return[1]}:{name}'
+
c_args = []
for param_type, param_name in params:
c_args.append((' ' if fmt_vimhelp else '') + (
'%s %s' % (param_type.ljust(type_length), param_name)).strip())
+ if not fmt_vimhelp:
+ pass
+ else:
+ fstem = '?'
+ if '.' in compoundname:
+ fstem = compoundname.split('.')[0]
+ fstem = CONFIG[target]['module_override'].get(fstem, fstem)
+ vimtag = CONFIG[target]['fn_helptag_fmt'](fstem, name)
+
prefix = '%s(' % name
suffix = '%s)' % ', '.join('{%s}' % a[1] for a in params
if a[0] not in ('void', 'Error'))
+
if not fmt_vimhelp:
c_decl = '%s %s(%s);' % (return_type, name, ', '.join(c_args))
signature = prefix + suffix
@@ -774,7 +827,9 @@ def extract_from_xml(filename, target, width):
xrefs.clear()
- fns = collections.OrderedDict(sorted(fns.items()))
+ fns = collections.OrderedDict(sorted(
+ fns.items(),
+ key=lambda key_item_tuple: key_item_tuple[0].lower()))
deprecated_fns = collections.OrderedDict(sorted(deprecated_fns.items()))
return (fns, deprecated_fns)
@@ -1002,6 +1057,7 @@ def main(config, args):
title, helptag, section_doc = sections.pop(filename)
except KeyError:
msg(f'warning: empty docs, skipping (target={target}): {filename}')
+ msg(f' existing docs: {sections.keys()}')
continue
i += 1
if filename not in CONFIG[target]['append_only']:
diff --git a/scripts/lua2dox.lua b/scripts/lua2dox.lua
index 1dc4c0a5a0..0b36a1e061 100644
--- a/scripts/lua2dox.lua
+++ b/scripts/lua2dox.lua
@@ -491,6 +491,27 @@ function TLua2DoX_filter.readfile(this,AppStamp,Filename)
end
end
+ -- Big hax
+ if string.find(fn, ":") then
+ -- TODO: We need to add a first parameter of "SELF" here
+ -- local colon_place = string.find(fn, ":")
+ -- local name = string.sub(fn, 1, colon_place)
+ fn = fn:gsub(":", ".", 1)
+ outStream:writeln("/// @param self")
+
+ local paren_start = string.find(fn, "(", 1, true)
+ local paren_finish = string.find(fn, ")", 1, true)
+
+ -- Nothing in between the parens
+ local comma
+ if paren_finish == paren_start + 1 then
+ comma = ""
+ else
+ comma = ", "
+ end
+ fn = string.sub(fn, 1, paren_start) .. "self" .. comma .. string.sub(fn, paren_start + 1)
+ end
+
-- add vanilla function
outStream:writeln(fn_type .. 'function ' .. fn .. '{}')
end
diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua
index 75e759094f..3994c5bc5b 100644
--- a/src/nvim/lua/vim.lua
+++ b/src/nvim/lua/vim.lua
@@ -400,7 +400,10 @@ do
wfw = true; winbl = true; winblend = true; winfixheight = true;
winfixwidth = true; winhighlight = true; winhl = true; wrap = true;
}
+
+ --@private
local function new_buf_opt_accessor(bufnr)
+ --@private
local function get(k)
if window_options[k] then
return a.nvim_err_writeln(k.." is a window option, not a buffer option")
@@ -410,23 +413,34 @@ do
end
return a.nvim_buf_get_option(bufnr or 0, k)
end
+
+ --@private
local function set(k, v)
if window_options[k] then
return a.nvim_err_writeln(k.." is a window option, not a buffer option")
end
return a.nvim_buf_set_option(bufnr or 0, k, v)
end
+
return make_meta_accessor(get, set)
end
vim.bo = new_buf_opt_accessor(nil)
+
+ --@private
local function new_win_opt_accessor(winnr)
+
+ --@private
local function get(k)
if winnr == nil and type(k) == "number" then
return new_win_opt_accessor(k)
end
return a.nvim_win_get_option(winnr or 0, k)
end
- local function set(k, v) return a.nvim_win_set_option(winnr or 0, k, v) end
+
+ --@private
+ local function set(k, v)
+ return a.nvim_win_set_option(winnr or 0, k, v)
+ end
return make_meta_accessor(get, set)
end
vim.wo = new_win_opt_accessor(nil)