neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

iter.lua (30074B)


      1 --- @brief
      2 ---
      3 --- [vim.iter()]() is an interface for [iterable]s: it wraps a table or function argument into an
      4 --- [Iter]() object with methods (such as [Iter:filter()] and [Iter:map()]) that transform the
      5 --- underlying source data. These methods can be chained to create iterator "pipelines": the output
      6 --- of each pipeline stage is input to the next stage. The first stage depends on the type passed to
      7 --- `vim.iter()`:
      8 ---
      9 --- - Lists or arrays (|lua-list|) yield only the value of each element.
     10 ---   - Holes (nil values) are allowed (but discarded).
     11 ---   - Use pairs() to treat array/list tables as dicts (preserve holes and non-contiguous integer
     12 ---     keys): `vim.iter(pairs(…))`.
     13 ---   - Use |Iter:enumerate()| to also pass the index to the next stage.
     14 ---     - Or initialize with ipairs(): `vim.iter(ipairs(…))`.
     15 --- - Non-list tables (|lua-dict|) yield both the key and value of each element.
     16 --- - Function |iterator|s yield all values returned by the underlying function.
     17 --- - Tables with a |__call()| metamethod are treated as function iterators.
     18 ---
     19 --- The iterator pipeline terminates when the underlying |iterable| is exhausted (for function
     20 --- iterators this means it returned nil).
     21 ---
     22 --- Note: `vim.iter()` scans table input to decide if it is a list or a dict; to avoid this cost you
     23 --- can wrap the table with an iterator e.g. `vim.iter(ipairs({…}))`, but that precludes the use of
     24 --- |list-iterator| operations such as |Iter:rev()|).
     25 ---
     26 --- Examples:
     27 ---
     28 --- ```lua
     29 --- local it = vim.iter({ 1, 2, 3, 4, 5 })
     30 --- it:map(function(v)
     31 ---   return v * 3
     32 --- end)
     33 --- it:rev()
     34 --- it:skip(2)
     35 --- it:totable()
     36 --- -- { 9, 6, 3 }
     37 ---
     38 --- -- ipairs() is a function iterator which returns both the index (i) and the value (v)
     39 --- vim.iter(ipairs({ 1, 2, 3, 4, 5 })):map(function(i, v)
     40 ---   if i > 2 then return v end
     41 --- end):totable()
     42 --- -- { 3, 4, 5 }
     43 ---
     44 --- local it = vim.iter(vim.gsplit('1,2,3,4,5', ','))
     45 --- it:map(function(s) return tonumber(s) end)
     46 --- for i, d in it:enumerate() do
     47 ---   print(string.format("Column %d is %d", i, d))
     48 --- end
     49 --- -- Column 1 is 1
     50 --- -- Column 2 is 2
     51 --- -- Column 3 is 3
     52 --- -- Column 4 is 4
     53 --- -- Column 5 is 5
     54 ---
     55 --- vim.iter({ a = 1, b = 2, c = 3, z = 26 }):any(function(k, v)
     56 ---   return k == 'z'
     57 --- end)
     58 --- -- true
     59 ---
     60 --- local rb = vim.ringbuf(3)
     61 --- rb:push("a")
     62 --- rb:push("b")
     63 --- vim.iter(rb):totable()
     64 --- -- { "a", "b" }
     65 --- ```
     66 
     67 --- LuaLS is bad at generics which this module mostly deals with
     68 --- @diagnostic disable:no-unknown
     69 
     70 ---@nodoc
     71 ---@class IterMod
     72 ---@operator call:Iter
     73 
     74 local M = {}
     75 
     76 ---@nodoc
     77 ---@class Iter
     78 ---@field _peeked any
     79 ---@field _next fun():... The underlying function that returns the next value(s) from the source.
     80 local Iter = {}
     81 Iter.__index = Iter
     82 Iter.__call = function(self)
     83  return self:next()
     84 end
     85 
     86 --- Special case implementations for iterators on list tables.
     87 ---@nodoc
     88 ---@class ArrayIter : Iter
     89 ---@field _table table Underlying table data
     90 ---@field _head number Index to the front of a table iterator
     91 ---@field _tail number Index to the end of a table iterator (exclusive)
     92 local ArrayIter = {}
     93 ArrayIter.__index = setmetatable(ArrayIter, Iter)
     94 ArrayIter.__call = function(self)
     95  return self:next()
     96 end
     97 
     98 --- Packed tables use this as their metatable
     99 local packedmt = {}
    100 
    101 local function unpack(t)
    102  if type(t) == 'table' and getmetatable(t) == packedmt then
    103    return _G.unpack(t, 1, t.n)
    104  end
    105  return t
    106 end
    107 
    108 local function pack(...)
    109  local n = select('#', ...)
    110  if n > 1 then
    111    return setmetatable({ n = n, ... }, packedmt)
    112  end
    113  return ...
    114 end
    115 
    116 local function sanitize(t)
    117  if type(t) == 'table' and getmetatable(t) == packedmt then
    118    -- Remove length tag and metatable
    119    t.n = nil
    120    setmetatable(t, nil)
    121  end
    122  return t
    123 end
    124 
    125 --- Flattens a single array-like table. Errors if it attempts to flatten a
    126 --- dict-like table
    127 ---@param t table table which should be flattened
    128 ---@param max_depth number depth to which the table should be flattened
    129 ---@param depth number current iteration depth
    130 ---@param result table output table that contains flattened result
    131 ---@return table|nil flattened table if it can be flattened, otherwise nil
    132 local function flatten(t, max_depth, depth, result)
    133  if depth < max_depth and type(t) == 'table' then
    134    for k, v in pairs(t) do
    135      if type(k) ~= 'number' or k <= 0 or math.floor(k) ~= k then
    136        -- short-circuit: this is not a list like table
    137        return nil
    138      end
    139 
    140      if flatten(v, max_depth, depth + 1, result) == nil then
    141        return nil
    142      end
    143    end
    144  elseif t ~= nil then
    145    result[#result + 1] = t
    146  end
    147 
    148  return result
    149 end
    150 
    151 --- Determine if the current iterator stage should continue.
    152 ---
    153 --- If any arguments are passed to this function, then return those arguments
    154 --- and stop the current iterator stage. Otherwise, return true to signal that
    155 --- the current stage should continue.
    156 ---
    157 ---@param ... any Function arguments.
    158 ---@return boolean True if the iterator stage should continue, false otherwise
    159 ---@return any Function arguments.
    160 local function continue(...)
    161  if select(1, ...) ~= nil then
    162    return false, ...
    163  end
    164  return true
    165 end
    166 
    167 --- If no input arguments are given return false, indicating the current
    168 --- iterator stage should stop. Otherwise, apply the arguments to the function
    169 --- f. If that function returns no values, the current iterator stage continues.
    170 --- Otherwise, those values are returned.
    171 ---
    172 ---@param f function Function to call with the given arguments
    173 ---@param ... any Arguments to apply to f
    174 ---@return boolean True if the iterator pipeline should continue, false otherwise
    175 ---@return any Return values of f
    176 local function apply(f, ...)
    177  if select(1, ...) ~= nil then
    178    return continue(f(...))
    179  end
    180  return false
    181 end
    182 
    183 --- Filters an iterator pipeline.
    184 ---
    185 --- Example:
    186 ---
    187 --- ```lua
    188 --- local bufs = vim.iter(vim.api.nvim_list_bufs()):filter(vim.api.nvim_buf_is_loaded)
    189 --- ```
    190 ---
    191 ---@param f fun(...):boolean Takes all values returned from the previous stage
    192 ---                       in the pipeline and returns false or nil if the
    193 ---                       current iterator element should be removed.
    194 ---@return Iter
    195 function Iter:filter(f)
    196  return self:map(function(...)
    197    if f(...) then
    198      return ...
    199    end
    200  end)
    201 end
    202 
    203 ---@private
    204 function ArrayIter:filter(f)
    205  local inc = self._head < self._tail and 1 or -1
    206  local n = self._head
    207  for i = self._head, self._tail - inc, inc do
    208    local v = self._table[i]
    209    if f(unpack(v)) then
    210      self._table[n] = v
    211      n = n + inc
    212    end
    213  end
    214  self._tail = n
    215  return self
    216 end
    217 
    218 --- Removes duplicate values from an iterator pipeline.
    219 ---
    220 --- Only the first occurrence of each value is kept.
    221 ---
    222 --- Accepts an optional `key` argument, which if provided is called for each
    223 --- value in the iterator to compute a hash key for uniqueness comparison. This is
    224 --- useful for deduplicating table values or complex objects.
    225 --- If `key` returns `nil` for a value, that value will be considered unique,
    226 --- even if multiple values return `nil`.
    227 ---
    228 --- If a function-based iterator returns multiple arguments, uniqueness is
    229 --- checked based on the first return value. To change this behavior, specify
    230 --- `key`.
    231 ---
    232 --- Examples:
    233 ---
    234 --- ```lua
    235 --- vim.iter({ 1, 2, 2, 3, 2 }):unique():totable()
    236 --- -- { 1, 2, 3 }
    237 ---
    238 --- vim.iter({ {id=1}, {id=2}, {id=1} })
    239 ---   :unique(function(x)
    240 ---     return x.id
    241 ---   end)
    242 ---   :totable()
    243 --- -- { {id=1}, {id=2} }
    244 --- ```
    245 ---
    246 ---@param key? fun(...):any Optional hash function to determine uniqueness of values.
    247 ---@return Iter
    248 ---@see |vim.list.unique()|
    249 function Iter:unique(key)
    250  local seen = {} --- @type table<any,boolean>
    251 
    252  key = key or function(a)
    253    return a
    254  end
    255 
    256  return self:filter(function(...)
    257    local hash = key(...)
    258    if hash == nil then
    259      return true
    260    elseif not seen[hash] then
    261      seen[hash] = true
    262      return true
    263    else
    264      return false
    265    end
    266  end)
    267 end
    268 
    269 --- Flattens a |list-iterator|, un-nesting nested values up to the given {depth}.
    270 --- Errors if it attempts to flatten a dict-like value.
    271 ---
    272 --- Examples:
    273 ---
    274 --- ```lua
    275 --- vim.iter({ 1, { 2 }, { { 3 } } }):flatten():totable()
    276 --- -- { 1, 2, { 3 } }
    277 ---
    278 --- vim.iter({1, { { a = 2 } }, { 3 } }):flatten():totable()
    279 --- -- { 1, { a = 2 }, 3 }
    280 ---
    281 --- vim.iter({ 1, { { a = 2 } }, { 3 } }):flatten(math.huge):totable()
    282 --- -- error: attempt to flatten a dict-like table
    283 --- ```
    284 ---
    285 ---@param depth? number Depth to which |list-iterator| should be flattened
    286 ---                        (defaults to 1)
    287 ---@return Iter
    288 ---@diagnostic disable-next-line:unused-local
    289 function Iter:flatten(depth) -- luacheck: no unused args
    290  error('flatten() requires an array-like table')
    291 end
    292 
    293 ---@private
    294 function ArrayIter:flatten(depth)
    295  depth = depth or 1
    296  local inc = self._head < self._tail and 1 or -1
    297  local target = {}
    298 
    299  for i = self._head, self._tail - inc, inc do
    300    local flattened = flatten(self._table[i], depth, 0, {})
    301 
    302    -- exit early if we try to flatten a dict-like table
    303    if flattened == nil then
    304      error('flatten() requires an array-like table')
    305    end
    306 
    307    for _, v in pairs(flattened) do
    308      target[#target + 1] = v
    309    end
    310  end
    311 
    312  self._head = 1
    313  self._tail = #target + 1
    314  self._table = target
    315  return self
    316 end
    317 
    318 --- Maps the items of an iterator pipeline to the values returned by `f`.
    319 ---
    320 --- If the map function returns nil, the value is filtered from the iterator.
    321 ---
    322 --- Example:
    323 ---
    324 --- ```lua
    325 --- local it = vim.iter({ 1, 2, 3, 4 }):map(function(v)
    326 ---   if v % 2 == 0 then
    327 ---     return v * 3
    328 ---   end
    329 --- end)
    330 --- it:totable()
    331 --- -- { 6, 12 }
    332 --- ```
    333 ---
    334 ---@param f fun(...):...:any Mapping function. Takes all values returned from
    335 ---                      the previous stage in the pipeline as arguments
    336 ---                      and returns one or more new values, which are used
    337 ---                      in the next pipeline stage. Nil return values
    338 ---                      are filtered from the output.
    339 ---@return Iter
    340 function Iter:map(f)
    341  -- Implementation note: the reader may be forgiven for observing that this
    342  -- function appears excessively convoluted. The problem to solve is that each
    343  -- stage of the iterator pipeline can return any number of values, and the
    344  -- number of values could even change per iteration. And the return values
    345  -- must be checked to determine if the pipeline has ended, so we cannot
    346  -- naively forward them along to the next stage.
    347  --
    348  -- A simple approach is to pack all of the return values into a table, check
    349  -- for nil, then unpack the table for the next stage. However, packing and
    350  -- unpacking tables is quite slow. There is no other way in Lua to handle an
    351  -- unknown number of function return values than to simply forward those
    352  -- values along to another function. Hence the intricate function passing you
    353  -- see here.
    354 
    355  local next = self.next
    356 
    357  --- Drain values from the upstream iterator source until a value can be
    358  --- returned.
    359  ---
    360  --- This is a recursive function. The base case is when the first argument is
    361  --- false, which indicates that the rest of the arguments should be returned
    362  --- as the values for the current iteration stage.
    363  ---
    364  ---@param cont boolean If true, the current iterator stage should continue to
    365  ---                    pull values from its upstream pipeline stage.
    366  ---                    Otherwise, this stage is complete and returns the
    367  ---                    values passed.
    368  ---@param ... any Values to return if cont is false.
    369  ---@return any
    370  local function fn(cont, ...)
    371    if cont then
    372      return fn(apply(f, next(self)))
    373    end
    374    return ...
    375  end
    376 
    377  self.next = function()
    378    return fn(apply(f, next(self)))
    379  end
    380  return self
    381 end
    382 
    383 ---@private
    384 function ArrayIter:map(f)
    385  local inc = self._head < self._tail and 1 or -1
    386  local n = self._head
    387  for i = self._head, self._tail - inc, inc do
    388    local v = pack(f(unpack(self._table[i])))
    389    if v ~= nil then
    390      self._table[n] = v
    391      n = n + inc
    392    end
    393  end
    394  self._tail = n
    395  return self
    396 end
    397 
    398 --- Calls a function once for each item in the pipeline, draining the iterator.
    399 ---
    400 --- For functions with side effects. To modify the values in the iterator, use |Iter:map()|.
    401 ---
    402 ---@param f fun(...) Function to execute for each item in the pipeline.
    403 ---                  Takes all of the values returned by the previous stage
    404 ---                  in the pipeline as arguments.
    405 function Iter:each(f)
    406  local function fn(...)
    407    if select(1, ...) ~= nil then
    408      f(...)
    409      return true
    410    end
    411  end
    412  while fn(self:next()) do
    413  end
    414 end
    415 
    416 ---@private
    417 function ArrayIter:each(f)
    418  local inc = self._head < self._tail and 1 or -1
    419  for i = self._head, self._tail - inc, inc do
    420    f(unpack(self._table[i]))
    421  end
    422  self._head = self._tail
    423 end
    424 
    425 --- Collect the iterator into a table.
    426 ---
    427 --- The resulting table depends on the initial source in the iterator pipeline.
    428 --- Array-like tables and function iterators will be collected into an array-like
    429 --- table. If multiple values are returned from the final stage in the iterator
    430 --- pipeline, each value will be included in a table.
    431 ---
    432 --- Examples:
    433 ---
    434 --- ```lua
    435 --- vim.iter(string.gmatch('100 20 50', '%d+')):map(tonumber):totable()
    436 --- -- { 100, 20, 50 }
    437 ---
    438 --- vim.iter({ 1, 2, 3 }):map(function(v) return v, 2 * v end):totable()
    439 --- -- { { 1, 2 }, { 2, 4 }, { 3, 6 } }
    440 ---
    441 --- vim.iter({ a = 1, b = 2, c = 3 }):filter(function(k, v) return v % 2 ~= 0 end):totable()
    442 --- -- { { 'a', 1 }, { 'c', 3 } }
    443 --- ```
    444 ---
    445 --- The generated table is an array-like table with consecutive, numeric indices.
    446 --- To create a map-like table with arbitrary keys, use |Iter:fold()|.
    447 ---
    448 ---
    449 ---@return table
    450 function Iter:totable()
    451  local t = {}
    452 
    453  while true do
    454    local args = pack(self:next())
    455    if args == nil then
    456      break
    457    end
    458 
    459    t[#t + 1] = sanitize(args)
    460  end
    461  return t
    462 end
    463 
    464 ---@private
    465 function ArrayIter:totable()
    466  if self.next ~= ArrayIter.next or self._head >= self._tail then
    467    return Iter.totable(self)
    468  end
    469 
    470  local needs_sanitize = getmetatable(self._table[self._head]) == packedmt
    471 
    472  -- Reindex and sanitize.
    473  local len = self._tail - self._head
    474 
    475  if needs_sanitize then
    476    for i = 1, len do
    477      self._table[i] = sanitize(self._table[self._head - 1 + i])
    478    end
    479  else
    480    for i = 1, len do
    481      self._table[i] = self._table[self._head - 1 + i]
    482    end
    483  end
    484 
    485  for i = len + 1, table.maxn(self._table) do
    486    self._table[i] = nil
    487  end
    488 
    489  self._head = 1
    490  self._tail = len + 1
    491 
    492  return self._table
    493 end
    494 
    495 --- Collect the iterator into a delimited string.
    496 ---
    497 --- Each element in the iterator is joined into a string separated by {delim}.
    498 ---
    499 --- Consumes the iterator.
    500 ---
    501 --- @param delim string Delimiter
    502 --- @return string
    503 function Iter:join(delim)
    504  return table.concat(self:totable(), delim)
    505 end
    506 
    507 --- Folds ("reduces") an iterator into a single value. [Iter:reduce()]()
    508 ---
    509 --- Examples:
    510 ---
    511 --- ```lua
    512 --- -- Create a new table with only even values
    513 --- vim.iter({ a = 1, b = 2, c = 3, d = 4 })
    514 ---   :filter(function(k, v) return v % 2 == 0 end)
    515 ---   :fold({}, function(acc, k, v)
    516 ---     acc[k] = v
    517 ---     return acc
    518 ---   end) --> { b = 2, d = 4 }
    519 ---
    520 --- -- Get the "maximum" item of an iterable.
    521 --- vim.iter({ -99, -4, 3, 42, 0, 0, 7 })
    522 ---   :fold({}, function(acc, v)
    523 ---     acc.max = math.max(v, acc.max or v)
    524 ---     return acc
    525 ---   end) --> { max = 42 }
    526 --- ```
    527 ---
    528 ---@generic A
    529 ---
    530 ---@param init A Initial value of the accumulator.
    531 ---@param f fun(acc:A, ...):A Accumulation function.
    532 ---@return A
    533 function Iter:fold(init, f)
    534  local acc = init
    535 
    536  --- Use a closure to handle var args returned from iterator
    537  local function fn(...)
    538    if select(1, ...) ~= nil then
    539      acc = f(acc, ...)
    540      return true
    541    end
    542  end
    543 
    544  while fn(self:next()) do
    545  end
    546  return acc
    547 end
    548 
    549 ---@private
    550 function ArrayIter:fold(init, f)
    551  local acc = init
    552  local inc = self._head < self._tail and 1 or -1
    553  for i = self._head, self._tail - inc, inc do
    554    acc = f(acc, unpack(self._table[i]))
    555  end
    556  return acc
    557 end
    558 
    559 --- Gets the next value from the iterator.
    560 ---
    561 --- Example:
    562 ---
    563 --- ```lua
    564 ---
    565 --- local it = vim.iter(string.gmatch('1 2 3', '%d+')):map(tonumber)
    566 --- it:next()
    567 --- -- 1
    568 --- it:next()
    569 --- -- 2
    570 --- it:next()
    571 --- -- 3
    572 ---
    573 --- ```
    574 ---
    575 ---@return any
    576 function Iter:next()
    577  if self._peeked then
    578    local v = self._peeked
    579    self._peeked = nil
    580 
    581    return unpack(v)
    582  end
    583 
    584  return self._next()
    585 end
    586 
    587 ---@private
    588 function ArrayIter:next()
    589  if self._head ~= self._tail then
    590    local v = self._table[self._head]
    591    local inc = self._head < self._tail and 1 or -1
    592    self._head = self._head + inc
    593    return unpack(v)
    594  end
    595 end
    596 
    597 --- Reverses a |list-iterator| pipeline.
    598 ---
    599 --- Example:
    600 ---
    601 --- ```lua
    602 ---
    603 --- local it = vim.iter({ 3, 6, 9, 12 }):rev()
    604 --- it:totable()
    605 --- -- { 12, 9, 6, 3 }
    606 ---
    607 --- ```
    608 ---
    609 ---@return Iter
    610 function Iter:rev()
    611  error('rev() requires an array-like table')
    612 end
    613 
    614 ---@private
    615 function ArrayIter:rev()
    616  local inc = self._head < self._tail and 1 or -1
    617  self._head, self._tail = self._tail - inc, self._head - inc
    618  return self
    619 end
    620 
    621 --- Gets the next value from the iterator without consuming it.
    622 ---
    623 --- The value returned by |Iter:peek()| will be returned again by the next call
    624 --- to |Iter:next()|.
    625 ---
    626 --- Example:
    627 ---
    628 --- ```lua
    629 ---
    630 --- local it = vim.iter({ 3, 6, 9, 12 })
    631 --- it:peek()
    632 --- -- 3
    633 --- it:peek()
    634 --- -- 3
    635 --- it:next()
    636 --- -- 3
    637 ---
    638 --- ```
    639 ---
    640 ---@return any
    641 function Iter:peek()
    642  if not self._peeked then
    643    self._peeked = pack(self:next())
    644  end
    645 
    646  return unpack(self._peeked)
    647 end
    648 
    649 ---@private
    650 function ArrayIter:peek()
    651  if self._head ~= self._tail then
    652    return self._table[self._head]
    653  end
    654 end
    655 
    656 --- Find the first value in the iterator that satisfies the given predicate.
    657 ---
    658 --- Advances the iterator. Returns nil and drains the iterator if no value is found.
    659 ---
    660 --- Examples:
    661 ---
    662 --- ```lua
    663 ---
    664 --- local it = vim.iter({ 3, 6, 9, 12 })
    665 --- it:find(12)
    666 --- -- 12
    667 ---
    668 --- local it = vim.iter({ 3, 6, 9, 12 })
    669 --- it:find(20)
    670 --- -- nil
    671 ---
    672 --- local it = vim.iter({ 3, 6, 9, 12 })
    673 --- it:find(function(v) return v % 4 == 0 end)
    674 --- -- 12
    675 ---
    676 --- ```
    677 ---@param f any
    678 ---@return any
    679 function Iter:find(f)
    680  if type(f) ~= 'function' then
    681    local val = f
    682    f = function(v)
    683      return v == val
    684    end
    685  end
    686 
    687  local result = nil
    688 
    689  --- Use a closure to handle var args returned from iterator
    690  local function fn(...)
    691    if select(1, ...) ~= nil then
    692      if f(...) then
    693        result = pack(...)
    694      else
    695        return true
    696      end
    697    end
    698  end
    699 
    700  while fn(self:next()) do
    701  end
    702  return unpack(result)
    703 end
    704 
    705 --- Gets the first value satisfying a predicate, from the end of a |list-iterator|.
    706 ---
    707 --- Advances the iterator. Returns nil and drains the iterator if no value is found.
    708 ---
    709 --- Examples:
    710 ---
    711 --- ```lua
    712 ---
    713 --- local it = vim.iter({ 1, 2, 3, 2, 1 }):enumerate()
    714 --- it:rfind(1)
    715 --- -- 5	1
    716 --- it:rfind(1)
    717 --- -- 1	1
    718 ---
    719 --- ```
    720 ---
    721 ---@see |Iter:find()|
    722 ---
    723 ---@param f any
    724 ---@return any
    725 ---@diagnostic disable-next-line: unused-local
    726 function Iter:rfind(f) -- luacheck: no unused args
    727  error('rfind() requires an array-like table')
    728 end
    729 
    730 ---@private
    731 function ArrayIter:rfind(f)
    732  if type(f) ~= 'function' then
    733    local val = f
    734    f = function(v)
    735      return v == val
    736    end
    737  end
    738 
    739  local inc = self._head < self._tail and 1 or -1
    740  for i = self._tail - inc, self._head, -inc do
    741    local v = self._table[i]
    742    if f(unpack(v)) then
    743      self._tail = i
    744      return unpack(v)
    745    end
    746  end
    747  self._head = self._tail
    748 end
    749 
    750 --- Transforms an iterator to yield only the first n values, or all values
    751 --- satisfying a predicate.
    752 ---
    753 --- Example:
    754 ---
    755 --- ```lua
    756 --- local it = vim.iter({ 1, 2, 3, 4 }):take(2)
    757 --- it:next()
    758 --- -- 1
    759 --- it:next()
    760 --- -- 2
    761 --- it:next()
    762 --- -- nil
    763 ---
    764 --- local function pred(x) return x < 2 end
    765 --- local it2 = vim.iter({ 1, 2, 3, 4 }):take(pred)
    766 --- it2:next()
    767 --- -- 1
    768 --- it2:next()
    769 --- -- nil
    770 --- ```
    771 ---
    772 ---@param n integer|fun(...):boolean Number of values to take or a predicate.
    773 ---@return Iter
    774 function Iter:take(n)
    775  local i = 0
    776  local f = n
    777  if type(n) ~= 'function' then
    778    f = function()
    779      return i < n
    780    end
    781  end
    782 
    783  local stop = false
    784  local function fn(...)
    785    if not stop and select(1, ...) ~= nil and f(...) then
    786      i = i + 1
    787      return ...
    788    else
    789      stop = true
    790    end
    791  end
    792 
    793  local next = self.next
    794  self.next = function()
    795    return fn(next(self))
    796  end
    797  return self
    798 end
    799 
    800 ---@private
    801 function ArrayIter:take(n)
    802  if type(n) == 'function' then
    803    local inc = self._head < self._tail and 1 or -1
    804    for i = self._head, self._tail, inc do
    805      if not n(unpack(self._table[i])) then
    806        self._tail = i
    807        break
    808      end
    809    end
    810    return self
    811  end
    812 
    813  local inc = self._head < self._tail and n or -n
    814  local cmp = self._head < self._tail and math.min or math.max
    815  self._tail = cmp(self._tail, self._head + inc)
    816  return self
    817 end
    818 
    819 --- "Pops" a value from a |list-iterator| (gets the last value and decrements the tail).
    820 ---
    821 --- Example:
    822 ---
    823 --- ```lua
    824 --- local it = vim.iter({1, 2, 3, 4})
    825 --- it:pop()
    826 --- -- 4
    827 --- it:pop()
    828 --- -- 3
    829 --- ```
    830 ---
    831 ---@return any
    832 function Iter:pop()
    833  error('pop() requires an array-like table')
    834 end
    835 
    836 --- @nodoc
    837 function ArrayIter:pop()
    838  if self._head ~= self._tail then
    839    local inc = self._head < self._tail and 1 or -1
    840    self._tail = self._tail - inc
    841    return self._table[self._tail]
    842  end
    843 end
    844 
    845 --- Gets the last value of a |list-iterator| without consuming it.
    846 ---
    847 --- Example:
    848 ---
    849 --- ```lua
    850 --- local it = vim.iter({1, 2, 3, 4})
    851 --- it:rpeek()
    852 --- -- 4
    853 --- it:rpeek()
    854 --- -- 4
    855 --- it:pop()
    856 --- -- 4
    857 --- ```
    858 ---
    859 ---@see |Iter:last()|
    860 ---
    861 ---@return any
    862 function Iter:rpeek()
    863  error('rpeek() requires an array-like table')
    864 end
    865 
    866 ---@nodoc
    867 function ArrayIter:rpeek()
    868  if self._head ~= self._tail then
    869    local inc = self._head < self._tail and 1 or -1
    870    return self._table[self._tail - inc]
    871  end
    872 end
    873 
    874 --- Skips `n` values of an iterator pipeline, or skips values while a predicate returns |lua-truthy|.
    875 ---
    876 --- When a predicate is used, skipping stops at the first value for which the
    877 --- predicate returns non-truthy. That value is not consumed and will be returned
    878 --- by the next call to |Iter:next()|
    879 ---
    880 --- Example:
    881 ---
    882 --- ```lua
    883 ---
    884 --- local it = vim.iter({ 3, 6, 9, 12 }):skip(2)
    885 --- it:next()
    886 --- -- 9
    887 ---
    888 --- local function pred(x) return x < 10 end
    889 --- local it2 = vim.iter({ 3, 6, 9, 12 }):skip(pred)
    890 --- it2:next()
    891 --- -- 12
    892 --- ```
    893 ---
    894 ---@param n integer|fun(...):boolean Number of values to skip or a predicate.
    895 ---@return Iter
    896 function Iter:skip(n)
    897  if type(n) == 'number' then
    898    for _ = 1, n do
    899      self._peeked = nil
    900      local _ = self:next()
    901    end
    902  elseif type(n) == 'function' then
    903    local next = self.next
    904 
    905    self.next = function()
    906      while true do
    907        local peeked = self._peeked or pack(next(self))
    908 
    909        if not peeked then
    910          return nil
    911        end
    912 
    913        if not n(unpack(peeked)) then
    914          self._peeked = nil
    915          return unpack(peeked)
    916        end
    917 
    918        self._peeked = nil
    919      end
    920    end
    921  end
    922  return self
    923 end
    924 
    925 ---@private
    926 function ArrayIter:skip(n)
    927  if type(n) == 'function' then
    928    while self._head ~= self._tail do
    929      local v = self._table[self._head]
    930      if not n(unpack(v)) then
    931        break
    932      end
    933 
    934      self._head = self._head + (self._head < self._tail and 1 or -1)
    935    end
    936    return self
    937  end
    938 
    939  local inc = self._head < self._tail and n or -n
    940  self._head = self._head + inc
    941  if (inc > 0 and self._head > self._tail) or (inc < 0 and self._head < self._tail) then
    942    self._head = self._tail
    943  end
    944  return self
    945 end
    946 
    947 --- Discards `n` values from the end of a |list-iterator| pipeline.
    948 ---
    949 --- Example:
    950 ---
    951 --- ```lua
    952 --- local it = vim.iter({ 1, 2, 3, 4, 5 }):rskip(2)
    953 --- it:next()
    954 --- -- 1
    955 --- it:pop()
    956 --- -- 3
    957 --- ```
    958 ---
    959 ---@param n number Number of values to skip.
    960 ---@return Iter
    961 ---@diagnostic disable-next-line: unused-local
    962 function Iter:rskip(n) -- luacheck: no unused args
    963  error('rskip() requires an array-like table')
    964 end
    965 
    966 ---@private
    967 function ArrayIter:rskip(n)
    968  local inc = self._head < self._tail and n or -n
    969  self._tail = self._tail - inc
    970  if (inc > 0 and self._head > self._tail) or (inc < 0 and self._head < self._tail) then
    971    self._head = self._tail
    972  end
    973  return self
    974 end
    975 
    976 --- Gets the nth value of an iterator (and advances to it).
    977 ---
    978 --- If `n` is negative, offsets from the end of a |list-iterator|.
    979 ---
    980 --- Example:
    981 ---
    982 --- ```lua
    983 --- local it = vim.iter({ 3, 6, 9, 12 })
    984 --- it:nth(2)
    985 --- -- 6
    986 --- it:nth(2)
    987 --- -- 12
    988 ---
    989 --- local it2 = vim.iter({ 3, 6, 9, 12 })
    990 --- it2:nth(-2)
    991 --- -- 9
    992 --- it2:nth(-2)
    993 --- -- 3
    994 --- ```
    995 ---
    996 ---@param n number Index of the value to return. May be negative if the source is a |list-iterator|.
    997 ---@return any
    998 function Iter:nth(n)
    999  if n > 0 then
   1000    return self:skip(n - 1):next()
   1001  elseif n < 0 then
   1002    return self:rskip(math.abs(n) - 1):pop()
   1003  end
   1004 end
   1005 
   1006 --- Sets the start and end of a |list-iterator| pipeline.
   1007 ---
   1008 --- Equivalent to `:skip(first - 1):rskip(len - last + 1)`.
   1009 ---
   1010 ---@param first number
   1011 ---@param last number
   1012 ---@return Iter
   1013 ---@diagnostic disable-next-line: unused-local
   1014 function Iter:slice(first, last) -- luacheck: no unused args
   1015  error('slice() requires an array-like table')
   1016 end
   1017 
   1018 ---@private
   1019 function ArrayIter:slice(first, last)
   1020  return self:skip(math.max(0, first - 1)):rskip(math.max(0, self._tail - last - 1))
   1021 end
   1022 
   1023 --- Returns true if any of the items in the iterator match the given predicate.
   1024 ---
   1025 ---@param pred fun(...):boolean Predicate function. Takes all values returned from the previous
   1026 ---                          stage in the pipeline as arguments and returns true if the
   1027 ---                          predicate matches.
   1028 function Iter:any(pred)
   1029  local any = false
   1030 
   1031  --- Use a closure to handle var args returned from iterator
   1032  local function fn(...)
   1033    if select(1, ...) ~= nil then
   1034      if pred(...) then
   1035        any = true
   1036      else
   1037        return true
   1038      end
   1039    end
   1040  end
   1041 
   1042  while fn(self:next()) do
   1043  end
   1044  return any
   1045 end
   1046 
   1047 --- Returns true if all items in the iterator match the given predicate.
   1048 ---
   1049 ---@param pred fun(...):boolean Predicate function. Takes all values returned from the previous
   1050 ---                          stage in the pipeline as arguments and returns true if the
   1051 ---                          predicate matches.
   1052 function Iter:all(pred)
   1053  local all = true
   1054 
   1055  local function fn(...)
   1056    if select(1, ...) ~= nil then
   1057      if not pred(...) then
   1058        all = false
   1059      else
   1060        return true
   1061      end
   1062    end
   1063  end
   1064 
   1065  while fn(self:next()) do
   1066  end
   1067  return all
   1068 end
   1069 
   1070 --- Drains the iterator and returns the last item.
   1071 ---
   1072 --- Example:
   1073 ---
   1074 --- ```lua
   1075 ---
   1076 --- local it = vim.iter(vim.gsplit('abcdefg', ''))
   1077 --- it:last()
   1078 --- -- 'g'
   1079 ---
   1080 --- local it = vim.iter({ 3, 6, 9, 12, 15 })
   1081 --- it:last()
   1082 --- -- 15
   1083 ---
   1084 --- ```
   1085 ---
   1086 ---@see |Iter:rpeek()|
   1087 ---
   1088 ---@return any
   1089 function Iter:last()
   1090  local last = self:next()
   1091  local cur = self:next()
   1092  while cur do
   1093    last = cur
   1094    cur = self:next()
   1095  end
   1096  return last
   1097 end
   1098 
   1099 ---@private
   1100 function ArrayIter:last()
   1101  if self._head >= self._tail then
   1102    return nil
   1103  end
   1104  local inc = self._head < self._tail and 1 or -1
   1105  local v = self._table[self._tail - inc]
   1106  self._head = self._tail
   1107  return v
   1108 end
   1109 
   1110 --- Yields the item index (count) and value for each item of an iterator pipeline.
   1111 ---
   1112 --- For list tables, this is more efficient:
   1113 ---
   1114 --- ```lua
   1115 --- vim.iter(ipairs(t))
   1116 --- ```
   1117 ---
   1118 --- instead of:
   1119 ---
   1120 --- ```lua
   1121 --- vim.iter(t):enumerate()
   1122 --- ```
   1123 ---
   1124 --- Example:
   1125 ---
   1126 --- ```lua
   1127 ---
   1128 --- local it = vim.iter(vim.gsplit('abc', '')):enumerate()
   1129 --- it:next()
   1130 --- -- 1	'a'
   1131 --- it:next()
   1132 --- -- 2	'b'
   1133 --- it:next()
   1134 --- -- 3	'c'
   1135 ---
   1136 --- ```
   1137 ---
   1138 ---@return Iter
   1139 function Iter:enumerate()
   1140  local i = 0
   1141  return self:map(function(...)
   1142    i = i + 1
   1143    return i, ...
   1144  end)
   1145 end
   1146 
   1147 ---@private
   1148 function ArrayIter:enumerate()
   1149  local inc = self._head < self._tail and 1 or -1
   1150  for i = self._head, self._tail - inc, inc do
   1151    local v = self._table[i]
   1152    self._table[i] = pack(i, v)
   1153  end
   1154  return self
   1155 end
   1156 
   1157 --- Creates a new Iter object from a table or other |iterable|.
   1158 ---
   1159 ---@param src table|function Table or iterator to drain values from
   1160 ---@return Iter
   1161 ---@private
   1162 function Iter.new(src, ...)
   1163  local it = {}
   1164  if type(src) == 'table' then
   1165    local mt = getmetatable(src)
   1166    if mt and type(mt.__call) == 'function' then
   1167      ---@private
   1168      it._next = function()
   1169        return src()
   1170      end
   1171 
   1172      setmetatable(it, Iter)
   1173      return it
   1174    end
   1175 
   1176    local t = {}
   1177 
   1178    -- O(n): scan the source table to decide if it is an array (only positive integer indices).
   1179    for k, v in pairs(src) do
   1180      if type(k) ~= 'number' or k <= 0 or math.floor(k) ~= k then
   1181        return Iter.new(pairs(src))
   1182      end
   1183      t[#t + 1] = v -- Coerce to list-like table.
   1184    end
   1185    return ArrayIter.new(t)
   1186  end
   1187 
   1188  if type(src) == 'function' then
   1189    local s, var = ...
   1190 
   1191    --- Use a closure to handle var args returned from iterator
   1192    local function fn(...)
   1193      -- Per the Lua 5.1 reference manual, an iterator is complete when the first returned value is
   1194      -- nil (even if there are other, non-nil return values). See |for-in|.
   1195      if select(1, ...) ~= nil then
   1196        var = select(1, ...)
   1197        return ...
   1198      end
   1199    end
   1200 
   1201    ---@private
   1202    it._next = function()
   1203      return fn(src(s, var))
   1204    end
   1205 
   1206    setmetatable(it, Iter)
   1207  else
   1208    error('src must be a table or function')
   1209  end
   1210  return it
   1211 end
   1212 
   1213 --- Create a new ArrayIter
   1214 ---
   1215 ---@param t table Array-like table. Caller guarantees that this table is a valid array. Can have
   1216 ---               holes (nil values).
   1217 ---@return Iter
   1218 ---@private
   1219 function ArrayIter.new(t)
   1220  local it = {}
   1221  it._table = t
   1222  it._head = 1
   1223  it._tail = #t + 1
   1224  setmetatable(it, ArrayIter)
   1225  return it
   1226 end
   1227 
   1228 return setmetatable(M, {
   1229  __call = function(_, ...)
   1230    return Iter.new(...)
   1231  end,
   1232 }) --[[@as IterMod]]