diff options
author | TJ DeVries <devries.timothyj@gmail.com> | 2021-05-01 05:19:48 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-05-01 08:19:48 -0400 |
commit | 27da5511a0c0b12fcabe29cf38c3f8a0f0b444b9 (patch) | |
tree | bd0c6670b2b0cca8400117842ed2abf9668dac90 | |
parent | ca6107cfbc1b9994d8a36494965f0b270dc2b77b (diff) | |
download | rneovim-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.txt | 563 | ||||
-rw-r--r-- | runtime/lua/vim/treesitter.lua | 38 | ||||
-rw-r--r-- | runtime/lua/vim/treesitter/health.lua | 4 | ||||
-rw-r--r-- | runtime/lua/vim/treesitter/highlighter.lua | 22 | ||||
-rw-r--r-- | runtime/lua/vim/treesitter/language.lua | 20 | ||||
-rw-r--r-- | runtime/lua/vim/treesitter/languagetree.lua | 160 | ||||
-rw-r--r-- | runtime/lua/vim/treesitter/query.lua | 153 | ||||
-rwxr-xr-x | scripts/gen_vimdoc.py | 76 | ||||
-rw-r--r-- | scripts/lua2dox.lua | 21 | ||||
-rw-r--r-- | src/nvim/lua/vim.lua | 16 |
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) |