mirror of
https://github.com/neovim/neovim.git
synced 2026-02-20 01:09:56 +10:00
feat(iter): peek(), skip(predicate) for non-list iterators #37604
Problem: Iter:peek() only works if the iterator is a |list-iterator| (internally, an `ArrayIter`). However, it is possible to implement :peek() support for any iterator. Solution: - add `_peeked` buffer for lookahead without actually consuming values - `peek()` now works for function, pairs(), and array iterators - `skip(predicate)` stops at the first non matching element without consuming it - keep existing optimized behavior for `ArrayIter` to maintain backward compatibility - use `pack`/`unpack` to support iterators that return multiple values
This commit is contained in:
@@ -196,6 +196,26 @@ describe('vim.iter', function()
|
||||
end
|
||||
end)
|
||||
|
||||
it('skip(predicate) preserves first non-matching element', function()
|
||||
local it = vim.iter(vim.gsplit('1|2|3|4', '|'))
|
||||
|
||||
it:skip(function(x)
|
||||
return tonumber(x) < 3
|
||||
end)
|
||||
|
||||
eq('3', it:next())
|
||||
eq('4', it:next())
|
||||
end)
|
||||
|
||||
it('skip() followed by peek() works correctly', function()
|
||||
local it = vim.iter(vim.gsplit('a|b|c|d', '|'))
|
||||
|
||||
it:skip(2)
|
||||
|
||||
eq('c', it:peek())
|
||||
eq('c', it:next())
|
||||
end)
|
||||
|
||||
it('rskip()', function()
|
||||
do
|
||||
local q = { 4, 3, 2, 1 }
|
||||
@@ -434,10 +454,88 @@ describe('vim.iter', function()
|
||||
|
||||
do
|
||||
local it = vim.iter(vim.gsplit('hi', ''))
|
||||
matches('peek%(%) requires an array%-like table', pcall_err(it.peek, it))
|
||||
eq('h', it:peek())
|
||||
eq('h', it:peek())
|
||||
eq('h', it:next())
|
||||
eq('i', it:peek())
|
||||
eq('i', it:next())
|
||||
end
|
||||
end)
|
||||
|
||||
it('peek() does not consume on function iterators', function()
|
||||
local it = vim.iter(vim.gsplit('a|b|c', '|'))
|
||||
|
||||
eq('a', it:peek())
|
||||
eq('a', it:peek())
|
||||
eq('a', it:next())
|
||||
eq('b', it:next())
|
||||
end)
|
||||
|
||||
it('peek() before skip(predicate) does not break iteration', function()
|
||||
local it = vim.iter(vim.gsplit('1|2|3|4', '|'))
|
||||
|
||||
eq('1', it:peek())
|
||||
|
||||
it:skip(function(x)
|
||||
return tonumber(x) < 3
|
||||
end)
|
||||
|
||||
eq('3', it:next())
|
||||
end)
|
||||
|
||||
it('multiple peek() calls after next()', function()
|
||||
local it = vim.iter(vim.gsplit('a|b|c', '|'))
|
||||
|
||||
eq('a', it:next())
|
||||
eq('b', it:peek())
|
||||
eq('b', it:peek())
|
||||
eq('b', it:next())
|
||||
eq('c', it:next())
|
||||
end)
|
||||
|
||||
describe('peek() with multi-value returns', function()
|
||||
it('peek() preserves multiple return values from ipairs()', function()
|
||||
local it = vim.iter(ipairs({ 'a', 'b', 'c' }))
|
||||
local i1, v1 = it:peek()
|
||||
|
||||
eq(1, i1)
|
||||
eq('a', v1)
|
||||
|
||||
local i2, v2 = it:next()
|
||||
|
||||
eq(1, i2)
|
||||
eq('a', v2)
|
||||
end)
|
||||
|
||||
it('peek() works with pairs() returning multiple values', function()
|
||||
local tbl = { x = 10, y = 20 }
|
||||
local it = vim.iter(pairs(tbl))
|
||||
local k1, v1 = it:peek()
|
||||
local k2, v2 = it:peek()
|
||||
|
||||
eq(k1, k2)
|
||||
eq(v1, v2)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('peek() after transformations', function()
|
||||
it('peek() works after map() on function iterator', function()
|
||||
local it = vim.iter(vim.gsplit('1|2|3', '|')):map(tonumber)
|
||||
|
||||
eq(1, it:peek())
|
||||
eq(1, it:next())
|
||||
eq(2, it:peek())
|
||||
end)
|
||||
|
||||
it('peek() at end of iterator returns nil', function()
|
||||
local it = vim.iter({ 1 })
|
||||
|
||||
eq(1, it:next())
|
||||
eq(nil, it:peek())
|
||||
eq(nil, it:next())
|
||||
end)
|
||||
end)
|
||||
|
||||
it('find()', function()
|
||||
local q = { 3, 6, 9, 12 }
|
||||
eq(12, vim.iter(q):find(12))
|
||||
|
||||
Reference in New Issue
Block a user