fix(vim.iter): add richer generic annotations

Improve the vim.iter annotations with richer generics that track element and
tuple types through iterator pipelines, including multi-value stages and
list-specific methods.

Extend the LuaCATS parser and vimdoc generator so those richer generic classes
and overloads round-trip into the generated help. These annotations are only
supported by EmmyLua, so LuaLS still uses a broader fallback in _meta.lua.

AI-assisted: Codex
This commit is contained in:
Lewis Russell
2026-04-20 15:22:32 +01:00
committed by Lewis Russell
parent 2b7a00746d
commit d7ef55e881
7 changed files with 602 additions and 424 deletions

View File

@@ -180,7 +180,7 @@ LSP
LUA
• *vim.loop* Use |vim.uv| instead.
• *vim.tbl_add_reverse_lookup()*
• *vim.tbl_flatten()* Use |Iter:flatten()| instead.
• *vim.tbl_flatten()* Use |IterArray:flatten()| instead.
• *vim.tbl_islist()* Use |vim.islist()| instead.
OPTIONS

View File

@@ -128,7 +128,7 @@ iterator functions, tables implementing the |__call()| metamethod, and
*list-iterator*
Iterators on |lua-list| tables have a "middle" and "end", whereas iterators in
general may be logically infinite. Therefore some |vim.iter| operations (e.g.
|Iter:rev()|) make sense only on list-like tables (which are finite by
|IterArray:rev()|) make sense only on list-like tables (which are finite by
definition).
*lua-function-call*
@@ -3028,7 +3028,7 @@ The iterator pipeline terminates when the underlying |iterable| is exhausted
Note: `vim.iter()` scans table input to decide if it is a list or a dict; to
avoid this cost you can wrap the table with an iterator e.g.
`vim.iter(ipairs({…}))`, but that precludes the use of |list-iterator|
operations such as |Iter:rev()|).
operations such as |IterArray:rev()|).
Examples: >lua
local it = vim.iter({ 1, 2, 3, 4, 5 })
@@ -3077,9 +3077,9 @@ Iter:all({pred}) *Iter:all()*
Since: 0.10.0
Parameters: ~
• {pred} (`fun(...):boolean`) Predicate function. Takes all values
returned from the previous stage in the pipeline as arguments
and returns true if the predicate matches.
• {pred} (`fun(v: V1, ...: V...): boolean`) Predicate function. Takes
all values returned from the previous stage in the pipeline as
arguments and returns true if the predicate matches.
Iter:any({pred}) *Iter:any()*
Returns true if any of the items in the iterator match the given
@@ -3089,9 +3089,9 @@ Iter:any({pred}) *Iter:any()*
Since: 0.10.0
Parameters: ~
• {pred} (`fun(...):boolean`) Predicate function. Takes all values
returned from the previous stage in the pipeline as arguments
and returns true if the predicate matches.
• {pred} (`fun(v: V1, ...: V...): boolean`) Predicate function. Takes
all values returned from the previous stage in the pipeline as
arguments and returns true if the predicate matches.
Iter:each({f}) *Iter:each()*
Calls a function once for each item in the pipeline, draining the
@@ -3104,9 +3104,9 @@ Iter:each({f}) *Iter:each()*
Since: 0.10.0
Parameters: ~
• {f} (`fun(...)`) Function to execute for each item in the pipeline.
Takes all of the values returned by the previous stage in the
pipeline as arguments.
• {f} (`fun(v: V1, ...: V...)`) Function to execute for each item in
the pipeline. Takes all of the values returned by the previous
stage in the pipeline as arguments.
Iter:enumerate() *Iter:enumerate()*
Yields the item index (count) and value for each item of an iterator
@@ -3134,8 +3134,11 @@ Iter:enumerate() *Iter:enumerate()*
Attributes: ~
Since: 0.10.0
Overloads: ~
• `fun<V1, V...>(self: vim.IterArray<V1, V...>): vim.IterArray<integer, V1, V...>`
Return: ~
(`Iter`)
(`vim.Iter<integer, V1, V...>`)
Iter:filter({f}) *Iter:filter()*
Filters an iterator pipeline.
@@ -3145,12 +3148,15 @@ Iter:filter({f}) *Iter:filter()*
<
Parameters: ~
• {f} (`fun(...):boolean`) Takes all values returned from the previous
stage in the pipeline and returns false or nil if the current
iterator element should be removed.
• {f} (`fun(v: V1, ...: V...): boolean`) Takes all values returned from
the previous stage in the pipeline and returns false or nil if
the current iterator element should be removed.
Overloads: ~
• `fun<V1, V...>(self: vim.IterArray<V1, V...>, f: fun(v: V1, ...: V...): boolean): vim.IterArray<V1, V...>`
Return: ~
(`Iter`)
(`vim.Iter<V1, V...>`)
Iter:find({f}) *Iter:find()*
Find the first value in the iterator that satisfies the given predicate.
@@ -3177,35 +3183,14 @@ Iter:find({f}) *Iter:find()*
Since: 0.10.0
Parameters: ~
• {f} (`any`)
• {f} (`(fun(v: V1, ...: V...): boolean)|any`)
Return: ~
(`any`)
Overloads: ~
• `fun<V1, V...>(self: vim.IterArray<V1, V...>, f: V1|fun(v: V1, ...: V...): boolean): V1?, V...`
Iter:flatten({depth}) *Iter:flatten()*
Flattens a |list-iterator|, un-nesting nested values up to the given
{depth}. Errors if it attempts to flatten a dict-like value.
Examples: >lua
vim.iter({ 1, { 2 }, { { 3 } } }):flatten():totable()
-- { 1, 2, { 3 } }
vim.iter({1, { { a = 2 } }, { 3 } }):flatten():totable()
-- { 1, { a = 2 }, 3 }
vim.iter({ 1, { { a = 2 } }, { 3 } }):flatten(math.huge):totable()
-- error: attempt to flatten a dict-like table
<
Attributes: ~
Since: 0.10.0
Parameters: ~
• {depth} (`number?`) Depth to which |list-iterator| should be
flattened (defaults to 1)
Return: ~
(`Iter`)
Return (multiple): ~
(`V1?`)
(`V...`)
Iter:fold({init}, {f}) *Iter:fold()*
Folds ("reduces") an iterator into a single value. *Iter:reduce()*
@@ -3232,7 +3217,7 @@ Iter:fold({init}, {f}) *Iter:fold()*
Parameters: ~
• {init} (`any`) Initial value of the accumulator.
• {f} (`fun(acc:A, ...):A`) Accumulation function.
• {f} (`fun(acc: A, v: V1, ...: V...): A`) Accumulation function.
Return: ~
(`any`)
@@ -3270,11 +3255,12 @@ Iter:last() *Iter:last()*
Attributes: ~
Since: 0.10.0
Return: ~
(`any`)
Return (multiple): ~
(`V1?`)
(`V...`)
See also: ~
• |Iter:rpeek()|
• |IterArray:rpeek()|
Iter:map({f}) *Iter:map()*
Maps the items of an iterator pipeline to the values returned by `f`.
@@ -3295,13 +3281,17 @@ Iter:map({f}) *Iter:map()*
Since: 0.10.0
Parameters: ~
• {f} (`fun(...):...:any`) Mapping function. Takes all values returned
from the previous stage in the pipeline as arguments and returns
one or more new values, which are used in the next pipeline
stage. Nil return values are filtered from the output.
• {f} (`fun(v: V1, ...: V...): K2, V2...`) Mapping function. Takes all
values returned from the previous stage in the pipeline as
arguments and returns one or more new values, which are used in
the next pipeline stage. Nil return values are filtered from the
output.
Overloads: ~
• `fun<V1, V..., K1, K...>(self: vim.IterArray<V1, V...>, f: fun(v: V1, ...: V...): K1, K...): vim.IterArray<K1, K...>`
Return: ~
(`Iter`)
(`vim.Iter<K2, V2...>`)
Iter:next() *Iter:next()*
Gets the next value from the iterator.
@@ -3320,8 +3310,9 @@ Iter:next() *Iter:next()*
Attributes: ~
Since: 0.10.0
Return: ~
(`any`)
Return (multiple): ~
(`V1?`)
(`V...`)
Iter:nth({n}) *Iter:nth()*
Gets the nth value of an iterator (and advances to it).
@@ -3346,11 +3337,12 @@ Iter:nth({n}) *Iter:nth()*
Since: 0.10.0
Parameters: ~
• {n} (`number`) Index of the value to return. May be negative if the
• {n} (`integer`) Index of the value to return. May be negative if the
source is a |list-iterator|.
Return: ~
(`any`)
Return (multiple): ~
(`V1?`)
(`V...`)
Iter:peek() *Iter:peek()*
Gets the next value from the iterator without consuming it.
@@ -3372,112 +3364,9 @@ Iter:peek() *Iter:peek()*
Attributes: ~
Since: 0.10.0
Return: ~
(`any`)
Iter:pop() *Iter:pop()*
"Pops" a value from a |list-iterator| (gets the last value and decrements
the tail).
Example: >lua
local it = vim.iter({1, 2, 3, 4})
it:pop()
-- 4
it:pop()
-- 3
<
Attributes: ~
Since: 0.10.0
Return: ~
(`any`)
Iter:rev() *Iter:rev()*
Reverses a |list-iterator| pipeline.
Example: >lua
local it = vim.iter({ 3, 6, 9, 12 }):rev()
it:totable()
-- { 12, 9, 6, 3 }
<
Attributes: ~
Since: 0.10.0
Return: ~
(`Iter`)
Iter:rfind({f}) *Iter:rfind()*
Gets the first value satisfying a predicate, from the end of a
|list-iterator|.
Advances the iterator. Returns nil and drains the iterator if no value is
found.
Examples: >lua
local it = vim.iter({ 1, 2, 3, 2, 1 }):enumerate()
it:rfind(1)
-- 5 1
it:rfind(1)
-- 1 1
<
Attributes: ~
Since: 0.10.0
Parameters: ~
• {f} (`any`)
Return: ~
(`any`)
See also: ~
• |Iter:find()|
Iter:rpeek() *Iter:rpeek()*
Gets the last value of a |list-iterator| without consuming it.
Example: >lua
local it = vim.iter({1, 2, 3, 4})
it:rpeek()
-- 4
it:rpeek()
-- 4
it:pop()
-- 4
<
Attributes: ~
Since: 0.10.0
Return: ~
(`any`)
See also: ~
• |Iter:last()|
Iter:rskip({n}) *Iter:rskip()*
Discards `n` values from the end of a |list-iterator| pipeline.
Example: >lua
local it = vim.iter({ 1, 2, 3, 4, 5 }):rskip(2)
it:next()
-- 1
it:pop()
-- 3
<
Attributes: ~
Since: 0.10.0
Parameters: ~
• {n} (`number`) Number of values to skip.
Return: ~
(`Iter`)
Return (multiple): ~
(`V1?`)
(`V...`)
Iter:skip({n}) *Iter:skip()*
Skips `n` values of an iterator pipeline, or skips values while a
@@ -3503,26 +3392,14 @@ Iter:skip({n}) *Iter:skip()*
Since: 0.10.0
Parameters: ~
• {n} (`integer|fun(...):boolean`) Number of values to skip or a
predicate.
• {n} (`integer|fun(v: V1, ...: V...): boolean`) Number of values to
skip or a predicate.
Overloads: ~
• `fun<V1, V...>(self: vim.IterArray<V1, V...>, n: integer|fun(v: V1, ...: V...): boolean): vim.IterArray<V1, V...>`
Return: ~
(`Iter`)
Iter:slice({first}, {last}) *Iter:slice()*
Sets the start and end of a |list-iterator| pipeline.
Equivalent to `:skip(first - 1):rskip(len - last + 1)`.
Attributes: ~
Since: 0.10.0
Parameters: ~
• {first} (`number`)
• {last} (`number`)
Return: ~
(`Iter`)
(`vim.Iter<V1, V...>`)
Iter:take({n}) *Iter:take()*
Transforms an iterator to yield only the first n values, or all values
@@ -3549,11 +3426,14 @@ Iter:take({n}) *Iter:take()*
Since: 0.10.0
Parameters: ~
• {n} (`integer|fun(...):boolean`) Number of values to take or a
predicate.
• {n} (`integer|fun(v: V1, ...: V...): boolean`) Number of values to
take or a predicate.
Overloads: ~
• `fun<V1, V...>(self: vim.IterArray<V1, V...>, n: integer|fun(v: V1, ...: V...): boolean): vim.IterArray<V1, V...>`
Return: ~
(`Iter`)
(`vim.Iter<V1, V...>`)
Iter:totable() *Iter:totable()*
Collect the iterator into a table.
@@ -3581,8 +3461,12 @@ Iter:totable() *Iter:totable()*
Attributes: ~
Since: 0.10.0
Overloads: ~
• `fun<T>(self: vim.Iter<T>): T[]`
• `fun<V1, V2, V...>(self: vim.Iter<V1, V2, V...>): [V1, V2, V...][]`
Return: ~
(`table`)
(`any[]`)
Iter:unique({key}) *Iter:unique()*
Removes duplicate values from an iterator pipeline.
@@ -3615,15 +3499,165 @@ Iter:unique({key}) *Iter:unique()*
Since: 0.12.0
Parameters: ~
• {key} (`fun(...):any?`) Optional hash function to determine
uniqueness of values.
• {key} (`fun(v: V1, ...: V...): any?`) Optional hash function to
determine uniqueness of values.
Overloads: ~
• `fun<V1, V...>(self: vim.IterArray<V1, V...>, key: fun(v: V1, ...: V...): any?): vim.IterArray<V1, V...>`
Return: ~
(`Iter`)
(`vim.Iter<V1, V...>`)
See also: ~
• |vim.list.unique()|
IterArray:flatten({depth}) *IterArray:flatten()*
Flattens a |list-iterator|, un-nesting nested values up to the given
{depth}. Errors if it attempts to flatten a dict-like value.
Examples: >lua
vim.iter({ 1, { 2 }, { { 3 } } }):flatten():totable()
-- { 1, 2, { 3 } }
vim.iter({1, { { a = 2 } }, { 3 } }):flatten():totable()
-- { 1, { a = 2 }, 3 }
vim.iter({ 1, { { a = 2 } }, { 3 } }):flatten(math.huge):totable()
-- error: attempt to flatten a dict-like table
<
Attributes: ~
Since: 0.10.0
Parameters: ~
• {depth} (`integer?`) Depth to which |list-iterator| should be
flattened (defaults to 1)
Return: ~
(`vim.IterArray<V1, V...>`)
IterArray:pop() *IterArray:pop()*
"Pops" a value from a |list-iterator| (gets the last value and decrements
the tail).
Example: >lua
local it = vim.iter({1, 2, 3, 4})
it:pop()
-- 4
it:pop()
-- 3
<
Attributes: ~
Since: 0.10.0
Return (multiple): ~
(`V1?`)
(`V...`)
IterArray:rev() *IterArray:rev()*
Reverses a |list-iterator| pipeline.
Example: >lua
local it = vim.iter({ 3, 6, 9, 12 }):rev()
it:totable()
-- { 12, 9, 6, 3 }
<
Attributes: ~
Since: 0.10.0
Return: ~
(`vim.IterArray<V1, V...>`)
IterArray:rfind({f}) *IterArray:rfind()*
Gets the first value satisfying a predicate, from the end of a
|list-iterator|.
Advances the iterator. Returns nil and drains the iterator if no value is
found.
Examples: >lua
local it = vim.iter({ 1, 2, 3, 2, 1 }):enumerate()
it:rfind(1)
-- 5 1
it:rfind(1)
-- 1 1
<
Attributes: ~
Since: 0.10.0
Parameters: ~
• {f} (`V1|fun(v: V1, ...: V...): boolean`)
Return (multiple): ~
(`V1?`)
(`V...`)
See also: ~
• |Iter:find()|
IterArray:rpeek() *IterArray:rpeek()*
Gets the last value of a |list-iterator| without consuming it.
Example: >lua
local it = vim.iter({1, 2, 3, 4})
it:rpeek()
-- 4
it:rpeek()
-- 4
it:pop()
-- 4
<
Attributes: ~
Since: 0.10.0
Return (multiple): ~
(`V1?`)
(`V...`)
See also: ~
• |Iter:last()|
IterArray:rskip({n}) *IterArray:rskip()*
Discards `n` values from the end of a |list-iterator| pipeline.
Example: >lua
local it = vim.iter({ 1, 2, 3, 4, 5 }):rskip(2)
it:next()
-- 1
it:pop()
-- 3
<
Attributes: ~
Since: 0.10.0
Parameters: ~
• {n} (`integer`)
Return: ~
(`vim.IterArray<V1, V...>`)
IterArray:slice({first}, {last}) *IterArray:slice()*
Sets the start and end of a |list-iterator| pipeline.
Equivalent to `:skip(first - 1):rskip(len - last + 1)`.
Attributes: ~
Since: 0.10.0
Parameters: ~
• {first} (`integer`)
• {last} (`integer`)
Return: ~
(`vim.IterArray<V1, V...>`)
==============================================================================
Lua module: vim.json *vim.json*

View File

@@ -7,6 +7,59 @@ error('Cannot require a meta file')
---@type uv
vim.uv = ...
--- LuaLS fallback surface for the richer iterator annotations in `vim.iter`.
--- EmmyLua reads the precise generics from `runtime/lua/vim/iter.lua`; LuaLS uses
--- these broader shapes for downstream type-checking.
--- @class vim.Iter
--- @field filter fun(self: vim.Iter, f: fun(...): boolean): vim.Iter
--- @field unique fun(self: vim.Iter, key?: fun(...): any): vim.Iter
--- @field flatten fun(self: vim.Iter, depth?: integer): vim.IterArray
--- @field map fun(self: vim.Iter, f: fun(...): ...): vim.Iter
--- @field each fun(self: vim.Iter, f: fun(...)): nil
--- @field totable fun(self: vim.Iter): table
--- @field join fun(self: vim.Iter, delim: string): string
--- @field fold fun(self: vim.Iter, init: any, f: fun(acc: any, ...): any): any
--- @field next fun(self: vim.Iter): any
--- @field rev fun(self: vim.Iter): vim.IterArray
--- @field peek fun(self: vim.Iter): any
--- @field find fun(self: vim.Iter, f: any): any
--- @field rfind fun(self: vim.Iter, f: any): any
--- @field take fun(self: vim.Iter, n: integer|fun(...): boolean): vim.Iter
--- @field pop fun(self: vim.Iter): any
--- @field rpeek fun(self: vim.Iter): any
--- @field skip fun(self: vim.Iter, n: integer|fun(...): boolean): vim.Iter
--- @field rskip fun(self: vim.Iter, n: integer): vim.IterArray
--- @field nth fun(self: vim.Iter, n: integer): any
--- @field slice fun(self: vim.Iter, first: integer, last: integer): vim.IterArray
--- @field any fun(self: vim.Iter, pred: fun(...): boolean): boolean
--- @field all fun(self: vim.Iter, pred: fun(...): boolean): boolean
--- @field last fun(self: vim.Iter): any
--- @field enumerate fun(self: vim.Iter): vim.Iter
--- @class vim.IterArray : vim.Iter
--- @field filter fun(self: vim.IterArray, f: fun(...): boolean): vim.IterArray
--- @field unique fun(self: vim.IterArray, key?: fun(...): any): vim.IterArray
--- @field flatten fun(self: vim.IterArray, depth?: integer): vim.IterArray
--- @field map fun(self: vim.IterArray, f: fun(...): ...): vim.IterArray
--- @field totable fun(self: vim.IterArray): table
--- @field fold fun(self: vim.IterArray, init: any, f: fun(acc: any, ...): any): any
--- @field next fun(self: vim.IterArray): any
--- @field rev fun(self: vim.IterArray): vim.IterArray
--- @field peek fun(self: vim.IterArray): any
--- @field find fun(self: vim.IterArray, f: any): any
--- @field rfind fun(self: vim.IterArray, f: any): any
--- @field take fun(self: vim.IterArray, n: integer|fun(...): boolean): vim.IterArray
--- @field pop fun(self: vim.IterArray): any
--- @field rpeek fun(self: vim.IterArray): any
--- @field skip fun(self: vim.IterArray, n: integer|fun(...): boolean): vim.IterArray
--- @field rskip fun(self: vim.IterArray, n: integer): vim.IterArray
--- @field slice fun(self: vim.IterArray, first: integer, last: integer): vim.IterArray
--- @field last fun(self: vim.IterArray): any
--- @field enumerate fun(self: vim.IterArray): vim.IterArray
--- @class vim.IterModule
--- @operator call: fun(src: any, ...): vim.Iter
--- The following modules are loaded specially in _init_packages.lua
vim.F = require('vim.F')
@@ -18,7 +71,11 @@ vim.func = require('vim.func')
vim.glob = require('vim.glob')
vim.health = require('vim.health')
vim.hl = require('vim.hl')
vim.iter = require('vim.iter')
local iter = require('vim.iter')
-- `require('vim.iter')` carries the richer EmmyLua generic surface. Force
-- LuaLS onto the fallback module shape above so `make luals` stays clean.
---@cast iter vim.IterModule
vim.iter = iter
vim.keymap = require('vim.keymap')
vim.loader = require('vim.loader')
vim.lsp = require('vim.lsp')

View File

@@ -21,7 +21,7 @@
---
--- Note: `vim.iter()` scans table input to decide if it is a list or a dict; to avoid this cost you
--- can wrap the table with an iterator e.g. `vim.iter(ipairs({…}))`, but that precludes the use of
--- |list-iterator| operations such as |Iter:rev()|).
--- |list-iterator| operations such as |IterArray:rev()|).
---
--- Examples:
---
@@ -64,36 +64,38 @@
--- -- { "a", "b" }
--- ```
--- LuaLS is bad at generics which this module mostly deals with
--- @diagnostic disable:no-unknown
---@nodoc
---@class IterMod
---@operator call:Iter
-- LuaLS cannot model the variadic EmmyLua generics used by this module.
---@diagnostic disable: no-unknown, undefined-doc-name, luadoc-miss-symbol, missing-return, missing-return-value, param-type-mismatch, return-type-mismatch, redundant-return-value, undefined-field
--- @nodoc
--- @class vim.IterModule
--- @operator call: vim.Iter<any, any...>
--- @overload fun<T>(src: T[]): vim.IterArray<T>
--- @overload fun<K, V>(src: table<K, V>): vim.Iter<K, V>
--- @overload fun(src: table, ...): vim.Iter<any, any...>
--- @overload fun(src: function, ...): vim.Iter<any, any...>
local M = {}
---@nodoc
---@class Iter
---@field _peeked any
---@field _next fun():... The underlying function that returns the next value(s) from the source.
--- @nodoc
--- @class vim.Iter<V1, V...>
--- @field private _peeked V1|[V1, V...]? Cached peek value, if any. Nil if no value is peeked.
--- @field private _next fun(): V1, V... The underlying function that returns the next packed value from the source.
--- @overload fun(self: vim.Iter<V1, V...>): V1?, V...
local Iter = {}
Iter.__index = Iter
Iter.__call = function(self)
return self:next()
end
--- Special case implementations for iterators on list tables.
---@nodoc
---@class ArrayIter : Iter
---@field _table table Underlying table data
---@field _head number Index to the front of a table iterator
---@field _tail number Index to the end of a table iterator (exclusive)
local ArrayIter = {}
ArrayIter.__index = setmetatable(ArrayIter, Iter)
ArrayIter.__call = function(self)
return self:next()
end
-- Special case implementations for iterators on list tables.
--- @nodoc
--- @class vim.IterArray<V1, V...> : vim.Iter<V1, V...>
--- @field private _table (V1|[V1, V...])[] Underlying table data. Items may be packed tuples after transforms.
--- @field private _head integer Index to the front of a table iterator
--- @field private _tail integer Index to the end of a table iterator (exclusive)
local IterArray = setmetatable({}, Iter)
IterArray.__index = IterArray
IterArray.__call = Iter.__call
--- Packed tables use this as their metatable
local packedmt = {}
@@ -105,6 +107,9 @@ local function unpack(t)
return t
end
--- @generic T...
--- @param ... T... Values to pack
--- @return T|[T...]
local function pack(...)
local n = select('#', ...)
if n > 1 then
@@ -124,11 +129,11 @@ end
--- Flattens a single array-like table. Errors if it attempts to flatten a
--- dict-like table
---@param t table table which should be flattened
---@param max_depth number depth to which the table should be flattened
---@param depth number current iteration depth
---@param result table output table that contains flattened result
---@return table|nil flattened table if it can be flattened, otherwise nil
--- @param t table table which should be flattened
--- @param max_depth integer depth to which the table should be flattened
--- @param depth integer current iteration depth
--- @param result table output table that contains flattened result
--- @return table? flattened table if it can be flattened, otherwise nil
local function flatten(t, max_depth, depth, result)
if depth < max_depth and type(t) == 'table' then
for k, v in pairs(t) do
@@ -154,9 +159,9 @@ end
--- and stop the current iterator stage. Otherwise, return true to signal that
--- the current stage should continue.
---
---@param ... any Function arguments.
---@return boolean True if the iterator stage should continue, false otherwise
---@return any Function arguments.
--- @param ... any Function arguments.
--- @return boolean True if the iterator stage should continue, false otherwise
--- @return any Function arguments.
local function continue(...)
if select(1, ...) ~= nil then
return false, ...
@@ -169,10 +174,11 @@ end
--- f. If that function returns no values, the current iterator stage continues.
--- Otherwise, those values are returned.
---
---@param f function Function to call with the given arguments
---@param ... any Arguments to apply to f
---@return boolean True if the iterator pipeline should continue, false otherwise
---@return any Return values of f
--- @generic T, R
--- @param f fun(...:T...): R... Function to call with the given arguments
--- @param ... T... Arguments to apply to f
--- @return boolean : True if the iterator pipeline should continue, false otherwise
--- @return R... : Return values of f
local function apply(f, ...)
if select(1, ...) ~= nil then
return continue(f(...))
@@ -188,10 +194,11 @@ end
--- local bufs = vim.iter(vim.api.nvim_list_bufs()):filter(vim.api.nvim_buf_is_loaded)
--- ```
---
---@param f fun(...):boolean Takes all values returned from the previous stage
--- @param f fun(v: V1, ...: V...): boolean Takes all values returned from the previous stage
--- in the pipeline and returns false or nil if the
--- current iterator element should be removed.
---@return Iter
--- @return vim.Iter<V1, V...>
--- @overload fun<V1, V...>(self: vim.IterArray<V1, V...>, f: fun(v: V1, ...: V...): boolean): vim.IterArray<V1, V...>
function Iter:filter(f)
return self:map(function(...)
if f(...) then
@@ -200,8 +207,10 @@ function Iter:filter(f)
end)
end
---@private
function ArrayIter:filter(f)
--- @nodoc
--- @param f fun(v: V1, ...: V...): boolean
--- @return vim.IterArray<V1, V...>
function IterArray:filter(f)
local inc = self._head < self._tail and 1 or -1
local n = self._head
for i = self._head, self._tail - inc, inc do
@@ -243,10 +252,11 @@ end
--- -- { {id=1}, {id=2} }
--- ```
---
---@since 14
---@param key? fun(...):any Optional hash function to determine uniqueness of values.
---@return Iter
---@see |vim.list.unique()|
--- @since 14
--- @param key? fun(v: V1, ...: V...): any Optional hash function to determine uniqueness of values.
--- @return vim.Iter<V1, V...>
--- @overload fun<V1, V...>(self: vim.IterArray<V1, V...>, key: fun(v: V1, ...: V...): any?): vim.IterArray<V1, V...>
--- @see |vim.list.unique()|
function Iter:unique(key)
local seen = {} --- @type table<any,boolean>
@@ -267,6 +277,12 @@ function Iter:unique(key)
end)
end
--- @nodoc
--- @diagnostic disable-next-line:unused-local
function Iter:flatten(depth)
error('flatten() requires an array-like table')
end
--- Flattens a |list-iterator|, un-nesting nested values up to the given {depth}.
--- Errors if it attempts to flatten a dict-like value.
---
@@ -283,17 +299,11 @@ end
--- -- error: attempt to flatten a dict-like table
--- ```
---
---@since 12
---@param depth? number Depth to which |list-iterator| should be flattened
--- @since 12
--- @param depth? integer Depth to which |list-iterator| should be flattened
--- (defaults to 1)
---@return Iter
---@diagnostic disable-next-line:unused-local
function Iter:flatten(depth) -- luacheck: no unused args
error('flatten() requires an array-like table')
end
---@private
function ArrayIter:flatten(depth)
--- @return vim.IterArray<V1, V...>
function IterArray:flatten(depth)
depth = depth or 1
local inc = self._head < self._tail and 1 or -1
local target = {}
@@ -333,13 +343,15 @@ end
--- -- { 6, 12 }
--- ```
---
---@since 12
---@param f fun(...):...:any Mapping function. Takes all values returned from
--- @since 12
--- @generic K2, V2...
--- @param f fun(v: V1, ...: V...): K2, V2... Mapping function. Takes all values returned from
--- the previous stage in the pipeline as arguments
--- and returns one or more new values, which are used
--- in the next pipeline stage. Nil return values
--- are filtered from the output.
---@return Iter
--- @return vim.Iter<K2, V2...>
--- @overload fun<V1, V..., K1, K...>(self: vim.IterArray<V1, V...>, f: fun(v: V1, ...: V...): K1, K...): vim.IterArray<K1, K...>
function Iter:map(f)
-- Implementation note: the reader may be forgiven for observing that this
-- function appears excessively convoluted. The problem to solve is that each
@@ -364,12 +376,12 @@ function Iter:map(f)
--- false, which indicates that the rest of the arguments should be returned
--- as the values for the current iteration stage.
---
---@param cont boolean If true, the current iterator stage should continue to
--- @param cont boolean If true, the current iterator stage should continue to
--- pull values from its upstream pipeline stage.
--- Otherwise, this stage is complete and returns the
--- values passed.
---@param ... any Values to return if cont is false.
---@return any
--- @param ... any Values to return if cont is false.
--- @return any
local function fn(cont, ...)
if cont then
return fn(apply(f, next(self)))
@@ -377,14 +389,18 @@ function Iter:map(f)
return ...
end
--- @diagnostic disable-next-line: duplicate-set-field
self.next = function()
return fn(apply(f, next(self)))
end
return self
end
---@private
function ArrayIter:map(f)
--- @nodoc
--- @generic K1, K...
--- @param f fun(v: V1, ...: V...): K1, K...
--- @return vim.IterArray<K1, K...>
function IterArray:map(f)
local inc = self._head < self._tail and 1 or -1
local n = self._head
for i = self._head, self._tail - inc, inc do
@@ -402,8 +418,8 @@ end
---
--- For functions with side effects. To modify the values in the iterator, use |Iter:map()|.
---
---@since 12
---@param f fun(...) Function to execute for each item in the pipeline.
--- @since 12
--- @param f fun(v: V1, ...: V...) Function to execute for each item in the pipeline.
--- Takes all of the values returned by the previous stage
--- in the pipeline as arguments.
function Iter:each(f)
@@ -417,8 +433,9 @@ function Iter:each(f)
end
end
---@private
function ArrayIter:each(f)
--- @private
--- @param f fun(v: V1, ...: V...)
function IterArray:each(f)
local inc = self._head < self._tail and 1 or -1
for i = self._head, self._tail - inc, inc do
f(unpack(self._table[i]))
@@ -450,8 +467,10 @@ end
--- To create a map-like table with arbitrary keys, use |Iter:fold()|.
---
---
---@since 12
---@return table
--- @since 12
--- @overload fun<T>(self: vim.Iter<T>): T[]
--- @overload fun<V1, V2, V...>(self: vim.Iter<V1, V2, V...>): [V1, V2, V...][]
--- @return any[]
function Iter:totable()
local t = {}
@@ -466,9 +485,12 @@ function Iter:totable()
return t
end
---@private
function ArrayIter:totable()
if self.next ~= ArrayIter.next or self._head >= self._tail then
--- @nodoc
--- @overload fun<T>(self: vim.IterArray<T>): T[]
--- @overload fun<V1, V2, V...>(self: vim.IterArray<V1, V2, V...>): [V1, V2, V...][]
--- @return any[]
function IterArray:totable()
if self.next ~= IterArray.next or self._head >= self._tail then
return Iter.totable(self)
end
@@ -531,12 +553,12 @@ end
--- end) --> { max = 42 }
--- ```
---
---@generic A
---
---@since 12
---@param init A Initial value of the accumulator.
---@param f fun(acc:A, ...):A Accumulation function.
---@return A
--- @since 12
--- @generic A
--- @param init A Initial value of the accumulator.
--- @param f fun(acc: A, v: V1, ...: V...): A Accumulation function.
--- @return A
function Iter:fold(init, f)
local acc = init
@@ -553,8 +575,12 @@ function Iter:fold(init, f)
return acc
end
---@private
function ArrayIter:fold(init, f)
--- @nodoc
--- @generic A
--- @param init A
--- @param f fun(acc: A, v: V1, ...: V...): A
--- @return A
function IterArray:fold(init, f)
local acc = init
local inc = self._head < self._tail and 1 or -1
for i = self._head, self._tail - inc, inc do
@@ -563,6 +589,7 @@ function ArrayIter:fold(init, f)
return acc
end
--- @diagnostic disable-next-line: duplicate-set-field
--- Gets the next value from the iterator.
---
--- Example:
@@ -579,8 +606,8 @@ end
---
--- ```
---
---@since 12
---@return any
--- @since 12
--- @return V1?, V...
function Iter:next()
if self._peeked then
local v = self._peeked
@@ -592,8 +619,9 @@ function Iter:next()
return self._next()
end
---@private
function ArrayIter:next()
--- @package
--- @return V1?, V...
function IterArray:next()
if self._head ~= self._tail then
local v = self._table[self._head]
local inc = self._head < self._tail and 1 or -1
@@ -602,6 +630,12 @@ function ArrayIter:next()
end
end
--- @nodoc
--- @diagnostic disable-next-line: unused-local
function Iter:rev()
error('rev() requires an array-like table')
end
--- Reverses a |list-iterator| pipeline.
---
--- Example:
@@ -614,14 +648,9 @@ end
---
--- ```
---
---@since 12
---@return Iter
function Iter:rev()
error('rev() requires an array-like table')
end
---@private
function ArrayIter:rev()
--- @since 12
--- @return vim.IterArray<V1, V...>
function IterArray:rev()
local inc = self._head < self._tail and 1 or -1
self._head, self._tail = self._tail - inc, self._head - inc
return self
@@ -646,8 +675,8 @@ end
---
--- ```
---
---@since 12
---@return any
--- @since 12
--- @return V1?, V...
function Iter:peek()
if not self._peeked then
self._peeked = pack(self:next())
@@ -656,10 +685,11 @@ function Iter:peek()
return unpack(self._peeked)
end
---@private
function ArrayIter:peek()
--- @private
--- @return V1?, V...
function IterArray:peek()
if self._head ~= self._tail then
return self._table[self._head]
return unpack(self._table[self._head])
end
end
@@ -684,9 +714,10 @@ end
--- -- 12
---
--- ```
---@since 12
---@param f any
---@return any
--- @since 12
--- @param f (fun(v: V1, ...: V...): boolean)|any
--- @overload fun<V1, V...>(self: vim.IterArray<V1, V...>, f: V1|fun(v: V1, ...: V...): boolean): V1?, V...
--- @return V1?, V...
function Iter:find(f)
if type(f) ~= 'function' then
local val = f
@@ -713,6 +744,12 @@ function Iter:find(f)
return unpack(result)
end
--- @nodoc
--- @diagnostic disable-next-line:unused-local
function Iter:rfind(f)
error('rfind() requires an array-like table')
end
--- Gets the first value satisfying a predicate, from the end of a |list-iterator|.
---
--- Advances the iterator. Returns nil and drains the iterator if no value is found.
@@ -729,18 +766,12 @@ end
---
--- ```
---
---@see |Iter:find()|
--- @see |Iter:find()|
---
---@since 12
---@param f any
---@return any
---@diagnostic disable-next-line: unused-local
function Iter:rfind(f) -- luacheck: no unused args
error('rfind() requires an array-like table')
end
---@private
function ArrayIter:rfind(f)
--- @since 12
--- @param f V1|fun(v: V1, ...: V...): boolean
--- @return V1?, V...
function IterArray:rfind(f)
if type(f) ~= 'function' then
local val = f
f = function(v)
@@ -781,21 +812,25 @@ end
--- -- nil
--- ```
---
---@since 12
---@param n integer|fun(...):boolean Number of values to take or a predicate.
---@return Iter
--- @since 12
--- @param n integer|fun(v: V1, ...: V...): boolean Number of values to take or a predicate.
--- @return vim.Iter<V1, V...>
--- @overload fun<V1, V...>(self: vim.IterArray<V1, V...>, n: integer|fun(v: V1, ...: V...): boolean): vim.IterArray<V1, V...>
function Iter:take(n)
local i = 0
local f = n
if type(n) ~= 'function' then
f = function()
return i < n
local pred --- @type fun(...): boolean
if type(n) == 'function' then
pred = n
else
local limit = n
pred = function()
return i < limit
end
end
local stop = false
local function fn(...)
if not stop and select(1, ...) ~= nil and f(...) then
if not stop and select(1, ...) ~= nil and pred(...) then
i = i + 1
return ...
else
@@ -804,14 +839,17 @@ function Iter:take(n)
end
local next = self.next
--- @diagnostic disable-next-line:duplicate-set-field
self.next = function()
return fn(next(self))
end
return self
end
---@private
function ArrayIter:take(n)
--- @private
--- @param n integer|fun(v: V1, ...: V...): boolean
--- @return vim.IterArray<V1, V...>
function IterArray:take(n)
if type(n) == 'function' then
local inc = self._head < self._tail and 1 or -1
for i = self._head, self._tail, inc do
@@ -829,6 +867,12 @@ function ArrayIter:take(n)
return self
end
--- @nodoc
--- @diagnostic disable-next-line: unused-local
function Iter:pop()
error('pop() requires an array-like table')
end
--- "Pops" a value from a |list-iterator| (gets the last value and decrements the tail).
---
--- Example:
@@ -841,21 +885,22 @@ end
--- -- 3
--- ```
---
---@since 12
---@return any
function Iter:pop()
error('pop() requires an array-like table')
end
--- @nodoc
function ArrayIter:pop()
--- @since 12
--- @return V1?, V...
function IterArray:pop()
if self._head ~= self._tail then
local inc = self._head < self._tail and 1 or -1
self._tail = self._tail - inc
return self._table[self._tail]
return unpack(self._table[self._tail])
end
end
--- @nodoc
--- @diagnostic disable-next-line: unused-local
function Iter:rpeek()
error('rpeek() requires an array-like table')
end
--- Gets the last value of a |list-iterator| without consuming it.
---
--- Example:
@@ -870,19 +915,14 @@ end
--- -- 4
--- ```
---
---@see |Iter:last()|
--- @see |Iter:last()|
---
---@since 12
---@return any
function Iter:rpeek()
error('rpeek() requires an array-like table')
end
---@nodoc
function ArrayIter:rpeek()
--- @since 12
--- @return V1?, V...
function IterArray:rpeek()
if self._head ~= self._tail then
local inc = self._head < self._tail and 1 or -1
return self._table[self._tail - inc]
return unpack(self._table[self._tail - inc])
end
end
@@ -906,9 +946,10 @@ end
--- -- 12
--- ```
---
---@since 12
---@param n integer|fun(...):boolean Number of values to skip or a predicate.
---@return Iter
--- @since 12
--- @param n integer|fun(v: V1, ...: V...): boolean Number of values to skip or a predicate.
--- @return vim.Iter<V1, V...>
--- @overload fun<V1, V...>(self: vim.IterArray<V1, V...>, n: integer|fun(v: V1, ...: V...): boolean): vim.IterArray<V1, V...>
function Iter:skip(n)
if type(n) == 'number' then
for _ = 1, n do
@@ -918,6 +959,7 @@ function Iter:skip(n)
elseif type(n) == 'function' then
local next = self.next
--- @diagnostic disable-next-line:duplicate-set-field
self.next = function()
while true do
local peeked = self._peeked or pack(next(self))
@@ -938,8 +980,10 @@ function Iter:skip(n)
return self
end
---@private
function ArrayIter:skip(n)
--- @private
--- @param n integer|fun(v: V1, ...: V...): boolean
--- @return vim.IterArray<V1, V...>
function IterArray:skip(n)
if type(n) == 'function' then
while self._head ~= self._tail do
local v = self._table[self._head]
@@ -960,6 +1004,12 @@ function ArrayIter:skip(n)
return self
end
--- @nodoc
--- @diagnostic disable-next-line:unused-local
function Iter:rskip(n)
error('rskip() requires an array-like table')
end
--- Discards `n` values from the end of a |list-iterator| pipeline.
---
--- Example:
@@ -972,18 +1022,12 @@ end
--- -- 3
--- ```
---
---@since 12
---@param n number Number of values to skip.
---@return Iter
---@diagnostic disable-next-line: unused-local
function Iter:rskip(n) -- luacheck: no unused args
error('rskip() requires an array-like table')
end
---@private
function ArrayIter:rskip(n)
local inc = self._head < self._tail and n or -n
self._tail = self._tail - inc
--- @since 12
--- @param n integer
--- @return vim.IterArray<V1, V...>
function IterArray:rskip(n)
local inc = self._head < self._tail and n or vim._assert_integer(-n)
self._tail = vim._assert_integer(self._tail - inc)
if (inc > 0 and self._head > self._tail) or (inc < 0 and self._head < self._tail) then
self._head = self._tail
end
@@ -1010,9 +1054,9 @@ end
--- -- 3
--- ```
---
---@since 12
---@param n number Index of the value to return. May be negative if the source is a |list-iterator|.
---@return any
--- @since 12
--- @param n integer Index of the value to return. May be negative if the source is a |list-iterator|.
--- @return V1?, V...
function Iter:nth(n)
if n > 0 then
return self:skip(n - 1):next()
@@ -1021,28 +1065,30 @@ function Iter:nth(n)
end
end
--- @nodoc
--- @diagnostic disable-next-line:unused-local
function Iter:slice(first, last)
error('slice() requires an array-like table')
end
--- Sets the start and end of a |list-iterator| pipeline.
---
--- Equivalent to `:skip(first - 1):rskip(len - last + 1)`.
---
---@since 12
---@param first number
---@param last number
---@return Iter
---@diagnostic disable-next-line: unused-local
function Iter:slice(first, last) -- luacheck: no unused args
error('slice() requires an array-like table')
end
---@private
function ArrayIter:slice(first, last)
return self:skip(math.max(0, first - 1)):rskip(math.max(0, self._tail - last - 1))
--- @since 12
--- @param first integer
--- @param last integer
--- @return vim.IterArray<V1, V...>
function IterArray:slice(first, last)
local left = vim._assert_integer(math.max(0, first - 1))
local right = vim._assert_integer(math.max(0, self._tail - last - 1))
return self:skip(left):rskip(right)
end
--- Returns true if any of the items in the iterator match the given predicate.
---
---@since 12
---@param pred fun(...):boolean Predicate function. Takes all values returned from the previous
--- @since 12
--- @param pred fun(v: V1, ...: V...): boolean Predicate function. Takes all values returned from the previous
--- stage in the pipeline as arguments and returns true if the
--- predicate matches.
function Iter:any(pred)
@@ -1066,8 +1112,8 @@ end
--- Returns true if all items in the iterator match the given predicate.
---
---@since 12
---@param pred fun(...):boolean Predicate function. Takes all values returned from the previous
--- @since 12
--- @param pred fun(v: V1, ...: V...): boolean Predicate function. Takes all values returned from the previous
--- stage in the pipeline as arguments and returns true if the
--- predicate matches.
function Iter:all(pred)
@@ -1104,10 +1150,9 @@ end
---
--- ```
---
---@since 12
---@see |Iter:rpeek()|
---
---@return any
--- @since 12
--- @see |IterArray:rpeek()|
--- @return V1?, V...
function Iter:last()
local last = self:next()
local cur = self:next()
@@ -1118,15 +1163,16 @@ function Iter:last()
return last
end
---@private
function ArrayIter:last()
if self._head >= self._tail then
--- @private
--- @return V1?, V...
function IterArray:last()
if self._head == self._tail then
return nil
end
local inc = self._head < self._tail and 1 or -1
local v = self._table[self._tail - inc]
self._head = self._tail
return v
return unpack(v)
end
--- Yields the item index (count) and value for each item of an iterator pipeline.
@@ -1157,8 +1203,9 @@ end
---
--- ```
---
---@since 12
---@return Iter
--- @since 12
--- @overload fun<V1, V...>(self: vim.IterArray<V1, V...>): vim.IterArray<integer, V1, V...>
--- @return vim.Iter<integer, V1, V...>
function Iter:enumerate()
local i = 0
return self:map(function(...)
@@ -1167,33 +1214,38 @@ function Iter:enumerate()
end)
end
---@private
function ArrayIter:enumerate()
--- @private
--- @return vim.IterArray<integer, V1, V...>
function IterArray:enumerate()
local inc = self._head < self._tail and 1 or -1
local n = 0
for i = self._head, self._tail - inc, inc do
n = n + 1
local v = self._table[i]
self._table[i] = pack(i, v)
self._table[i] = pack(n, unpack(v))
end
return self
end
--- Creates a new Iter object from a table or other |iterable|.
---
---@param src table|function Table or iterator to drain values from
---@return Iter
---@private
--- @generic R1, R...
--- @param src table<R1, R>|fun(s: table, v: any): R1, R... Table or iterator to drain values from
--- @return vim.Iter<R1, R...>
--- @overload fun<T>(src: T[]): vim.IterArray<T>
--- @overload fun<K, V>(src: table<K, V>): vim.Iter<K, V>
--- @private
function Iter.new(src, ...)
local it = {}
if type(src) == 'table' then
local mt = getmetatable(src)
if mt and type(mt.__call) == 'function' then
---@private
--- @private
it._next = function()
return src()
return mt.__call(src)
end
setmetatable(it, Iter)
return it
return setmetatable(it, Iter)
end
local t = {}
@@ -1205,51 +1257,48 @@ function Iter.new(src, ...)
end
t[#t + 1] = v -- Coerce to list-like table.
end
return ArrayIter.new(t)
end
if type(src) == 'function' then
local s, var = ...
--- Use a closure to handle var args returned from iterator
local function fn(...)
-- Per the Lua 5.1 reference manual, an iterator is complete when the first returned value is
-- nil (even if there are other, non-nil return values). See |for-in|.
if select(1, ...) ~= nil then
var = select(1, ...)
return ...
end
end
---@private
it._next = function()
return fn(src(s, var))
end
setmetatable(it, Iter)
else
return IterArray.new(t)
elseif type(src) ~= 'function' then
error('src must be a table or function')
end
return it
local s, var = ...
--- Use a closure to handle var args returned from iterator
local function fn(...)
-- Per the Lua 5.1 reference manual, an iterator is complete when the first returned value is
-- nil (even if there are other, non-nil return values). See |for-in|.
if select(1, ...) ~= nil then
var = select(1, ...)
return ...
end
end
--- @private
it._next = function()
return fn(src(s, var))
end
return setmetatable(it, Iter)
end
--- Create a new ArrayIter
--- Create a new IterArray
---
---@param t table Array-like table. Caller guarantees that this table is a valid array. Can have
--- holes (nil values).
---@return Iter
---@private
function ArrayIter.new(t)
local it = {}
it._table = t
it._head = 1
it._tail = #t + 1
setmetatable(it, ArrayIter)
return it
--- @generic V1, V...
--- @param t (V1|[V1, V...])[] List-like table. Caller guarantees that this table is a valid
--- internal list buffer.
--- @return vim.IterArray<V1, V...>
--- @private
function IterArray.new(t)
return setmetatable({
_table = t,
_head = 1,
_tail = #t + 1,
}, IterArray)
end
return setmetatable(M, {
__call = function(_, ...)
return Iter.new(...)
end,
}) --[[@as IterMod]]
})

View File

@@ -88,7 +88,9 @@ local v = setmetatable({}, {
--- @class nvim.luacats.Class
--- @field kind 'class'
--- @field name string
--- @field generics? string[]
--- @field parent? string
--- @field parent_generics? string[]
--- @field access? 'private'|'protected'|'package'
--- @class nvim.luacats.Field
@@ -149,7 +151,7 @@ local typedef = P({
+ (v.types * opt_postfix)
+ (P(ty_ident) * P('...')) -- Generic vararg
+ v.types,
types = v.generics + v.kv_table + v.tuple + v.dict + v.table_literal + v.fun + ty_prims,
types = v.fun + v.generics + v.kv_table + v.tuple + v.dict + v.table_literal + ty_prims,
tuple = Pf('[') * comma1(v.type) * Plf(']'),
dict = Pf('{') * comma1(Pf('[') * v.type * Pf(']') * colon * v.type) * Plf('}'),
@@ -157,10 +159,18 @@ local typedef = P({
table_literal = Pf('{') * comma1(opt_ident * Pf(':') * v.type) * Plf('}'),
fun_param = (opt_ident + ellipsis) * opt(colon * v.type),
fun_ret = v.type + (ellipsis * opt(colon * v.type)),
fun = opt(Pf('async')) * Pf('fun') * paren(comma(v.fun_param)) * opt(Pf(':') * comma1(v.fun_ret)),
generic_decl_list = Pf('<') * comma1(ty_ident * opt(ellipsis) * opt(colon * v.type)) * Plf('>'),
fun = opt(Pf('async') + Pf('sync')) * Pf('fun') * opt(v.generic_decl_list) * paren(
comma(v.fun_param)
) * opt(Pf(':') * comma1(v.fun_ret)),
generics = P(ty_ident) * Pf('<') * comma1(v.type) * Plf('>'),
}) / function(match)
return vim.trim(match):gsub('^%((.*)%)$', '%1'):gsub('%?+', '?')
return (vim.trim(match):gsub('^%((.*)%)$', '%1'):gsub('%?+', '?'))
end
--- @param name string
local function generic_opt(name)
return (Pf('<') * Cg(Ct(comma1(typedef)), name) * Plf('>')) + -Plf('<')
end
local access = P('private') + P('protected') + P('package')
@@ -170,7 +180,7 @@ local desc_delim = Sf '#:' + ws
local desc = Cg(rep(any), 'desc')
local opt_desc = opt(desc_delim * desc)
local ty_name = Cg(ty_ident, 'name')
local opt_parent = opt(colon * Cg(ty_ident, 'parent'))
local opt_parent = opt(colon * Cg(ty_ident, 'parent') * generic_opt('parent_generics'))
local lname = (ident + ellipsis) * opt(P('?'))
-- stylua: ignore
@@ -182,7 +192,7 @@ local grammar = P {
+ annot('type', comma1(Ct(v.ctype)) * opt_desc)
+ annot('cast', ty_name * ws * opt(Sf('+-')) * v.ctype)
+ annot('generic', ty_name * opt(colon * v.ctype))
+ annot('class', opt(paren(cattr)) * fill * ty_name * opt_parent)
+ annot('class', opt(paren(cattr)) * fill * ty_name * generic_opt('generics') * opt_parent)
+ annot('field', opt(caccess * ws) * v.field_name * ws * v.ctype * opt_desc)
+ annot('operator', ty_name * opt(paren(Cg(v.ctype, 'argtype'))) * colon * v.ctype)
+ annot(access)

View File

@@ -143,7 +143,9 @@ local function process_doc_line(line, state)
--- @cast parsed nvim.luacats.Class
cur_obj.kind = 'class'
cur_obj.name = parsed.name
cur_obj.generics = parsed.generics
cur_obj.parent = parsed.parent
cur_obj.parent_generics = parsed.parent_generics
cur_obj.access = parsed.access
cur_obj.desc = state.doc_lines and table.concat(state.doc_lines, '\n') or nil
state.doc_lines = nil

View File

@@ -178,6 +178,32 @@ describe('luacats grammar', function()
parent = 'vim.diagnostic.GetOpts',
})
test('@class vim.Iter<V1, V...>', {
kind = 'class',
name = 'vim.Iter',
generics = { 'V1', 'V...' },
})
test('@class vim.IterArray<T> : vim.Iter<T, never>', {
kind = 'class',
name = 'vim.IterArray',
generics = { 'T' },
parent = 'vim.Iter',
parent_generics = { 'T', 'never' },
})
test('@class vim.lsp.Client.Progress: vim.Ringbuf<{token: integer|string, value: any}>', {
kind = 'class',
name = 'vim.lsp.Client.Progress',
parent = 'vim.Ringbuf',
parent_generics = { '{token: integer|string, value: any}' },
})
test('@overload fun<V1, V2, V...>(self: vim.Iter<V1, V2, V...>): [V1, V2, V...][]', {
kind = 'overload',
type = 'fun<V1, V2, V...>(self: vim.Iter<V1, V2, V...>): [V1, V2, V...][]',
})
test('@param opt? { cmd?: string[] } Options', {
kind = 'param',
name = 'opt?',