aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/lua.txt223
-rw-r--r--runtime/lua/vim/_init_packages.lua1
-rw-r--r--runtime/lua/vim/iter.lua112
-rw-r--r--runtime/lua/vim/shared.lua105
-rwxr-xr-xscripts/gen_vimdoc.py3
-rw-r--r--test/functional/lua/iter_spec.lua425
-rw-r--r--test/functional/lua/vim_spec.lua421
7 files changed, 639 insertions, 651 deletions
diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt
index c1c00df28e..9d4272c906 100644
--- a/runtime/doc/lua.txt
+++ b/runtime/doc/lua.txt
@@ -1670,26 +1670,6 @@ endswith({s}, {suffix}) *vim.endswith()*
Return: ~
(boolean) `true` if `suffix` is a suffix of `s`
-filter({f}, {src}, {...}) *vim.filter()*
- Filter a table or iterator.
-
- This is a convenience function that performs: >lua
-
- vim.iter(src):filter(f):totable()
-<
-
- Parameters: ~
- • {f} function(...):bool Filter function. Accepts the current
- iterator or table values as arguments and returns true if those
- values should be kept in the final table
- • {src} table|function Table or iterator function to filter
-
- Return: ~
- (table)
-
- See also: ~
- • |Iter:filter()|
-
gsplit({s}, {sep}, {opts}) *vim.gsplit()*
Splits a string at each instance of a separator.
@@ -1735,64 +1715,6 @@ is_callable({f}) *vim.is_callable()*
Return: ~
(boolean) `true` if `f` is callable, else `false`
-iter({src}, {...}) *vim.iter()*
- Create an Iter |lua-iter| object from a table or iterator.
-
- The input value can be a table or a function iterator (see |luaref-in|).
-
- This function wraps the input value into an interface which allows
- chaining multiple pipeline stages in an efficient manner. Each pipeline
- stage receives as input the output values from the prior stage. The values
- used in the first stage of the pipeline depend on the type passed to this
- function:
-
- • List tables pass only the value of each element
- • Non-list tables pass both the key and value of each element
- • Function iterators pass all of the values returned by their respective
- function
-
- Examples: >lua
-
- local it = vim.iter({ 1, 2, 3, 4, 5 })
- it:map(function(v)
- return v * 3
- end)
- it:rev()
- it:skip(2)
- it:totable()
- -- { 9, 6, 3 }
-
- vim.iter(ipairs({ 1, 2, 3, 4, 5 })):map(function(i, v)
- if i > 2 then return v end
- end):totable()
- -- { 3, 4, 5 }
-
- local it = vim.iter(vim.gsplit('1,2,3,4,5', ','))
- it:map(function(s) return tonumber(s) end)
- for i, d in it:enumerate() do
- print(string.format("Column %d is %d", i, d))
- end
- -- Column 1 is 1
- -- Column 2 is 2
- -- Column 3 is 3
- -- Column 4 is 4
- -- Column 5 is 5
-
- vim.iter({ a = 1, b = 2, c = 3, z = 26 }):any(function(k, v)
- return k == 'z'
- end)
- -- true
-<
-
- Parameters: ~
- • {src} table|function Table or iterator.
-
- Return: ~
- Iter |lua-iter|
-
- See also: ~
- • |lua-iter|
-
list_contains({t}, {value}) *vim.list_contains()*
Checks if a list-like table (integer keys without gaps) contains `value`.
@@ -1835,26 +1757,6 @@ list_slice({list}, {start}, {finish}) *vim.list_slice()*
Return: ~
(list) Copy of table sliced from start to finish (inclusive)
-map({f}, {src}, {...}) *vim.map()*
- Map and filter a table or iterator.
-
- This is a convenience function that performs: >lua
-
- vim.iter(src):map(f):totable()
-<
-
- Parameters: ~
- • {f} function(...):?any Map function. Accepts the current iterator
- or table values as arguments and returns one or more new
- values. Nil values are removed from the final table.
- • {src} table|function Table or iterator function to filter
-
- Return: ~
- (table)
-
- See also: ~
- • |Iter:map()|
-
pesc({s}) *vim.pesc()*
Escapes magic chars in |lua-patterns|.
@@ -2116,20 +2018,6 @@ tbl_values({t}) *vim.tbl_values()*
Return: ~
(list) List of values
-totable({f}, {...}) *vim.totable()*
- Collect an iterator into a table.
-
- This is a convenience function that performs: >lua
-
- vim.iter(f):totable()
-<
-
- Parameters: ~
- • {f} (function) Iterator function
-
- Return: ~
- (table)
-
trim({s}) *vim.trim()*
Trim whitespace (Lua pattern "%s") from both sides of a string.
@@ -2950,6 +2838,79 @@ range({spec}) *vim.version.range()*
==============================================================================
Lua module: iter *lua-iter*
+
+The *vim.iter* module provides a generic "iterator" interface over tables
+and iterator functions.
+
+*vim.iter()* wraps its table or function argument into an *Iter* object
+with methods (such as |Iter:filter()| and |Iter:map()|) that transform the
+underlying source data. These methods can be chained together to create
+iterator "pipelines". Each pipeline stage receives as input the output
+values from the prior stage. The values used in the first stage of the
+pipeline depend on the type passed to this function:
+
+• List tables pass only the value of each element
+• Non-list tables pass both the key and value of each element
+• Function iterators pass all of the values returned by their respective
+ function
+
+Examples: >lua
+
+ local it = vim.iter({ 1, 2, 3, 4, 5 })
+ it:map(function(v)
+ return v * 3
+ end)
+ it:rev()
+ it:skip(2)
+ it:totable()
+ -- { 9, 6, 3 }
+
+ vim.iter(ipairs({ 1, 2, 3, 4, 5 })):map(function(i, v)
+ if i > 2 then return v end
+ end):totable()
+ -- { 3, 4, 5 }
+
+ local it = vim.iter(vim.gsplit('1,2,3,4,5', ','))
+ it:map(function(s) return tonumber(s) end)
+ for i, d in it:enumerate() do
+ print(string.format("Column %d is %d", i, d))
+ end
+ -- Column 1 is 1
+ -- Column 2 is 2
+ -- Column 3 is 3
+ -- Column 4 is 4
+ -- Column 5 is 5
+
+ vim.iter({ a = 1, b = 2, c = 3, z = 26 }):any(function(k, v)
+ return k == 'z'
+ end)
+ -- true
+
+<
+
+In addition to the |vim.iter()| function, the |vim.iter| module provides
+convenience functions like |vim.iter.filter()| and |vim.iter.totable()|.
+
+filter({f}, {src}, {...}) *vim.iter.filter()*
+ Filter a table or iterator.
+
+ This is a convenience function that performs: >lua
+
+ vim.iter(src):filter(f):totable()
+<
+
+ Parameters: ~
+ • {f} function(...):bool Filter function. Accepts the current
+ iterator or table values as arguments and returns true if those
+ values should be kept in the final table
+ • {src} table|function Table or iterator function to filter
+
+ Return: ~
+ (table)
+
+ See also: ~
+ • |Iter:filter()|
+
Iter:all({self}, {pred}) *Iter:all()*
Return true if all of the items in the iterator match the given predicate.
@@ -2988,9 +2949,7 @@ Iter:enumerate({self}) *Iter:enumerate()*
vim.iter(ipairs(t))
<
- over
-
- >lua
+ over >lua
vim.iter(t):enumerate()
<
@@ -3348,16 +3307,38 @@ Iter:totable({self}) *Iter:totable()*
Return: ~
(table)
-new({src}, {...}) *new()*
- Create a new Iter object from a table or iterator.
+map({f}, {src}, {...}) *vim.iter.map()*
+ Map and filter a table or iterator.
+
+ This is a convenience function that performs: >lua
+
+ vim.iter(src):map(f):totable()
+<
Parameters: ~
- • {src} table|function Table or iterator to drain values from
+ • {f} function(...):?any Map function. Accepts the current iterator
+ or table values as arguments and returns one or more new
+ values. Nil values are removed from the final table.
+ • {src} table|function Table or iterator function to filter
Return: ~
- Iter
+ (table)
-next() *next()*
- TODO: Documentation
+ See also: ~
+ • |Iter:map()|
+
+totable({f}, {...}) *vim.iter.totable()*
+ Collect an iterator into a table.
+
+ This is a convenience function that performs: >lua
+
+ vim.iter(f):totable()
+<
+
+ Parameters: ~
+ • {f} (function) Iterator function
+
+ Return: ~
+ (table)
vim:tw=78:ts=8:sw=4:sts=4:et:ft=help:norl:
diff --git a/runtime/lua/vim/_init_packages.lua b/runtime/lua/vim/_init_packages.lua
index 2cf2e91a8c..5db258c011 100644
--- a/runtime/lua/vim/_init_packages.lua
+++ b/runtime/lua/vim/_init_packages.lua
@@ -55,6 +55,7 @@ vim._submodules = {
inspect = true,
version = true,
fs = true,
+ iter = true,
}
-- These are for loading runtime modules in the vim namespace lazily.
diff --git a/runtime/lua/vim/iter.lua b/runtime/lua/vim/iter.lua
index 2545853b41..c2e2c5bd9f 100644
--- a/runtime/lua/vim/iter.lua
+++ b/runtime/lua/vim/iter.lua
@@ -1,4 +1,56 @@
---- Iterator implementation.
+---@defgroup lua-iter
+---
+--- The \*vim.iter\* module provides a generic "iterator" interface over tables and iterator
+--- functions.
+---
+--- \*vim.iter()\* wraps its table or function argument into an \*Iter\* object with methods (such
+--- as |Iter:filter()| and |Iter:map()|) that transform the underlying source data. These methods
+--- can be chained together to create iterator "pipelines". Each pipeline stage receives as input
+--- the output values from the prior stage. The values used in the first stage of the pipeline
+--- depend on the type passed to this function:
+---
+--- - List tables pass only the value of each element
+--- - Non-list tables pass both the key and value of each element
+--- - Function iterators pass all of the values returned by their respective
+--- function
+---
+--- Examples:
+--- <pre>lua
+--- local it = vim.iter({ 1, 2, 3, 4, 5 })
+--- it:map(function(v)
+--- return v * 3
+--- end)
+--- it:rev()
+--- it:skip(2)
+--- it:totable()
+--- -- { 9, 6, 3 }
+---
+--- vim.iter(ipairs({ 1, 2, 3, 4, 5 })):map(function(i, v)
+--- if i > 2 then return v end
+--- end):totable()
+--- -- { 3, 4, 5 }
+---
+--- local it = vim.iter(vim.gsplit('1,2,3,4,5', ','))
+--- it:map(function(s) return tonumber(s) end)
+--- for i, d in it:enumerate() do
+--- print(string.format("Column %d is %d", i, d))
+--- end
+--- -- Column 1 is 1
+--- -- Column 2 is 2
+--- -- Column 3 is 3
+--- -- Column 4 is 4
+--- -- Column 5 is 5
+---
+--- vim.iter({ a = 1, b = 2, c = 3, z = 26 }):any(function(k, v)
+--- return k == 'z'
+--- end)
+--- -- true
+--- </pre>
+---
+--- In addition to the |vim.iter()| function, the |vim.iter| module provides convenience functions
+--- like |vim.iter.filter()| and |vim.iter.totable()|.
+
+local M = {}
---@class Iter
local Iter = {}
@@ -733,7 +785,6 @@ end
--- </pre>
---
--- over
----
--- <pre>lua
--- vim.iter(t):enumerate()
--- </pre>
@@ -776,6 +827,7 @@ end
---
---@param src table|function Table or iterator to drain values from
---@return Iter
+---@private
function Iter.new(src, ...)
local it = {}
if type(src) == 'table' then
@@ -807,6 +859,7 @@ function Iter.new(src, ...)
end
end
+ ---@private
function it.next()
return fn(src(s, var))
end
@@ -832,4 +885,57 @@ function ListIter.new(t)
return it
end
-return Iter
+--- Collect an iterator into a table.
+---
+--- This is a convenience function that performs:
+--- <pre>lua
+--- vim.iter(f):totable()
+--- </pre>
+---
+---@param f function Iterator function
+---@return table
+function M.totable(f, ...)
+ return Iter.new(f, ...):totable()
+end
+
+--- Filter a table or iterator.
+---
+--- This is a convenience function that performs:
+--- <pre>lua
+--- vim.iter(src):filter(f):totable()
+--- </pre>
+---
+---@see |Iter:filter()|
+---
+---@param f function(...):bool Filter function. Accepts the current iterator or table values as
+--- arguments and returns true if those values should be kept in the
+--- final table
+---@param src table|function Table or iterator function to filter
+---@return table
+function M.filter(f, src, ...)
+ return Iter.new(src, ...):filter(f):totable()
+end
+
+--- Map and filter a table or iterator.
+---
+--- This is a convenience function that performs:
+--- <pre>lua
+--- vim.iter(src):map(f):totable()
+--- </pre>
+---
+---@see |Iter:map()|
+---
+---@param f function(...):?any Map function. Accepts the current iterator or table values as
+--- arguments and returns one or more new values. Nil values are removed
+--- from the final table.
+---@param src table|function Table or iterator function to filter
+---@return table
+function M.map(f, src, ...)
+ return Iter.new(src, ...):map(f):totable()
+end
+
+return setmetatable(M, {
+ __call = function(_, ...)
+ return Iter.new(...)
+ end,
+})
diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua
index 5f4ea8822b..a55deb1415 100644
--- a/runtime/lua/vim/shared.lua
+++ b/runtime/lua/vim/shared.lua
@@ -881,109 +881,4 @@ function vim.defaulttable(create)
})
end
---- Create an Iter |lua-iter| object from a table or iterator.
----
---- The input value can be a table or a function iterator (see |luaref-in|).
----
---- This function wraps the input value into an interface which allows chaining
---- multiple pipeline stages in an efficient manner. Each pipeline stage
---- receives as input the output values from the prior stage. The values used in
---- the first stage of the pipeline depend on the type passed to this function:
----
---- - List tables pass only the value of each element
---- - Non-list tables pass both the key and value of each element
---- - Function iterators pass all of the values returned by their respective
---- function
----
---- Examples:
---- <pre>lua
---- local it = vim.iter({ 1, 2, 3, 4, 5 })
---- it:map(function(v)
---- return v * 3
---- end)
---- it:rev()
---- it:skip(2)
---- it:totable()
---- -- { 9, 6, 3 }
----
---- vim.iter(ipairs({ 1, 2, 3, 4, 5 })):map(function(i, v)
---- if i > 2 then return v end
---- end):totable()
---- -- { 3, 4, 5 }
----
---- local it = vim.iter(vim.gsplit('1,2,3,4,5', ','))
---- it:map(function(s) return tonumber(s) end)
---- for i, d in it:enumerate() do
---- print(string.format("Column %d is %d", i, d))
---- end
---- -- Column 1 is 1
---- -- Column 2 is 2
---- -- Column 3 is 3
---- -- Column 4 is 4
---- -- Column 5 is 5
----
---- vim.iter({ a = 1, b = 2, c = 3, z = 26 }):any(function(k, v)
---- return k == 'z'
---- end)
---- -- true
---- </pre>
----
----@see |lua-iter|
----
----@param src table|function Table or iterator.
----@return Iter @|lua-iter|
-function vim.iter(src, ...)
- local Iter = require('vim.iter')
- return Iter.new(src, ...)
-end
-
---- Collect an iterator into a table.
----
---- This is a convenience function that performs:
---- <pre>lua
---- vim.iter(f):totable()
---- </pre>
----
----@param f function Iterator function
----@return table
-function vim.totable(f, ...)
- return vim.iter(f, ...):totable()
-end
-
---- Filter a table or iterator.
----
---- This is a convenience function that performs:
---- <pre>lua
---- vim.iter(src):filter(f):totable()
---- </pre>
----
----@see |Iter:filter()|
----
----@param f function(...):bool Filter function. Accepts the current iterator or table values as
---- arguments and returns true if those values should be kept in the
---- final table
----@param src table|function Table or iterator function to filter
----@return table
-function vim.filter(f, src, ...)
- return vim.iter(src, ...):filter(f):totable()
-end
-
---- Map and filter a table or iterator.
----
---- This is a convenience function that performs:
---- <pre>lua
---- vim.iter(src):map(f):totable()
---- </pre>
----
----@see |Iter:map()|
----
----@param f function(...):?any Map function. Accepts the current iterator or table values as
---- arguments and returns one or more new values. Nil values are removed
---- from the final table.
----@param src table|function Table or iterator function to filter
----@return table
-function vim.map(f, src, ...)
- return vim.iter(src, ...):map(f):totable()
-end
-
return vim
diff --git a/scripts/gen_vimdoc.py b/scripts/gen_vimdoc.py
index 52d03d9746..d0686e92bc 100755
--- a/scripts/gen_vimdoc.py
+++ b/scripts/gen_vimdoc.py
@@ -188,7 +188,7 @@ CONFIG = {
f'*vim.{name}()*'
if fstem.lower() == '_editor'
else f'*{name}()*'
- if fstem in ('iter.lua')
+ if name[0].isupper()
else f'*{fstem}.{name}()*'),
'module_override': {
# `shared` functions are exposed on the `vim` module.
@@ -202,6 +202,7 @@ CONFIG = {
'fs': 'vim.fs',
'secure': 'vim.secure',
'version': 'vim.version',
+ 'iter': 'vim.iter',
},
'append_only': [
'shared.lua',
diff --git a/test/functional/lua/iter_spec.lua b/test/functional/lua/iter_spec.lua
new file mode 100644
index 0000000000..6e1ecc2f7e
--- /dev/null
+++ b/test/functional/lua/iter_spec.lua
@@ -0,0 +1,425 @@
+local helpers = require('test.functional.helpers')(after_each)
+local eq = helpers.eq
+local matches = helpers.matches
+local pcall_err = helpers.pcall_err
+
+describe('vim.iter', function()
+ it('filter()', function()
+ local function odd(v)
+ return v % 2 ~= 0
+ end
+
+ local t = { 1, 2, 3, 4, 5 }
+ eq({ 1, 3, 5 }, vim.iter(t):filter(odd):totable())
+ eq({ 2, 4 }, vim.iter(t):filter(function(v) return not odd(v) end):totable())
+ eq({}, vim.iter(t):filter(function(v) return v > 5 end):totable())
+
+ do
+ local it = vim.iter(ipairs(t))
+ it:filter(function(i, v) return i > 1 and v < 5 end)
+ it:map(function(_, v) return v * 2 end)
+ eq({ 4, 6, 8 }, it:totable())
+ end
+
+ local it = vim.iter(string.gmatch('the quick brown fox', '%w+'))
+ eq({'the', 'fox'}, it:filter(function(s) return #s <= 3 end):totable())
+ end)
+
+ it('map()', function()
+ local t = { 1, 2, 3, 4, 5 }
+ eq(
+ { 2, 4, 6, 8, 10 },
+ vim
+ .iter(t)
+ :map(function(v)
+ return 2 * v
+ end)
+ :totable()
+ )
+
+ local it = vim.gsplit(
+ [[
+ Line 1
+ Line 2
+ Line 3
+ Line 4
+ ]],
+ '\n'
+ )
+
+ eq(
+ { 'Lion 2', 'Lion 4' },
+ vim
+ .iter(it)
+ :map(function(s)
+ local lnum = s:match('(%d+)')
+ if lnum and tonumber(lnum) % 2 == 0 then
+ return vim.trim(s:gsub('Line', 'Lion'))
+ end
+ end)
+ :totable()
+ )
+ end)
+
+ it('for loops', function()
+ local t = {1, 2, 3, 4, 5}
+ local acc = 0
+ for v in vim.iter(t):map(function(v) return v * 3 end) do
+ acc = acc + v
+ end
+ eq(45, acc)
+ end)
+
+ it('totable()', function()
+ do
+ local it = vim.iter({1, 2, 3}):map(function(v) return v, v*v end)
+ eq({{1, 1}, {2, 4}, {3, 9}}, it:totable())
+ end
+
+ do
+ local it = vim.iter(string.gmatch('1,4,lol,17,blah,2,9,3', '%d+')):map(tonumber)
+ eq({1, 4, 17, 2, 9, 3}, it:totable())
+ end
+ end)
+
+ it('next()', function()
+ local it = vim.iter({1, 2, 3}):map(function(v) return 2 * v end)
+ eq(2, it:next())
+ eq(4, it:next())
+ eq(6, it:next())
+ eq(nil, it:next())
+ end)
+
+ it('rev()', function()
+ eq({3, 2, 1}, vim.iter({1, 2, 3}):rev():totable())
+
+ local it = vim.iter(string.gmatch("abc", "%w"))
+ matches('rev%(%) requires a list%-like table', pcall_err(it.rev, it))
+ end)
+
+ it('skip()', function()
+ do
+ local t = {4, 3, 2, 1}
+ eq(t, vim.iter(t):skip(0):totable())
+ eq({3, 2, 1}, vim.iter(t):skip(1):totable())
+ eq({2, 1}, vim.iter(t):skip(2):totable())
+ eq({1}, vim.iter(t):skip(#t - 1):totable())
+ eq({}, vim.iter(t):skip(#t):totable())
+ eq({}, vim.iter(t):skip(#t + 1):totable())
+ end
+
+ do
+ local function skip(n)
+ return vim.iter(vim.gsplit('a|b|c|d', '|')):skip(n):totable()
+ end
+ eq({'a', 'b', 'c', 'd'}, skip(0))
+ eq({'b', 'c', 'd'}, skip(1))
+ eq({'c', 'd'}, skip(2))
+ eq({'d'}, skip(3))
+ eq({}, skip(4))
+ eq({}, skip(5))
+ end
+ end)
+
+ it('skipback()', function()
+ do
+ local t = {4, 3, 2, 1}
+ eq(t, vim.iter(t):skipback(0):totable())
+ eq({4, 3, 2}, vim.iter(t):skipback(1):totable())
+ eq({4, 3}, vim.iter(t):skipback(2):totable())
+ eq({4}, vim.iter(t):skipback(#t - 1):totable())
+ eq({}, vim.iter(t):skipback(#t):totable())
+ eq({}, vim.iter(t):skipback(#t + 1):totable())
+ end
+
+ local it = vim.iter(vim.gsplit('a|b|c|d', '|'))
+ matches('skipback%(%) requires a list%-like table', pcall_err(it.skipback, it, 0))
+ end)
+
+ it('slice()', function()
+ local t = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
+ eq({3, 4, 5, 6, 7}, vim.iter(t):slice(3, 7):totable())
+ eq({}, vim.iter(t):slice(6, 5):totable())
+ eq({}, vim.iter(t):slice(0, 0):totable())
+ eq({1}, vim.iter(t):slice(1, 1):totable())
+ eq({1, 2}, vim.iter(t):slice(1, 2):totable())
+ eq({10}, vim.iter(t):slice(10, 10):totable())
+ eq({8, 9, 10}, vim.iter(t):slice(8, 11):totable())
+ end)
+
+ it('nth()', function()
+ do
+ local t = {4, 3, 2, 1}
+ eq(nil, vim.iter(t):nth(0))
+ eq(4, vim.iter(t):nth(1))
+ eq(3, vim.iter(t):nth(2))
+ eq(2, vim.iter(t):nth(3))
+ eq(1, vim.iter(t):nth(4))
+ eq(nil, vim.iter(t):nth(5))
+ end
+
+ do
+ local function nth(n)
+ return vim.iter(vim.gsplit('a|b|c|d', '|')):nth(n)
+ end
+ eq(nil, nth(0))
+ eq('a', nth(1))
+ eq('b', nth(2))
+ eq('c', nth(3))
+ eq('d', nth(4))
+ eq(nil, nth(5))
+ end
+ end)
+
+ it('nthback()', function()
+ do
+ local t = {4, 3, 2, 1}
+ eq(nil, vim.iter(t):nthback(0))
+ eq(1, vim.iter(t):nthback(1))
+ eq(2, vim.iter(t):nthback(2))
+ eq(3, vim.iter(t):nthback(3))
+ eq(4, vim.iter(t):nthback(4))
+ eq(nil, vim.iter(t):nthback(5))
+ end
+
+ local it = vim.iter(vim.gsplit('a|b|c|d', '|'))
+ matches('skipback%(%) requires a list%-like table', pcall_err(it.nthback, it, 1))
+ end)
+
+ it('any()', function()
+ local function odd(v)
+ return v % 2 ~= 0
+ end
+
+ do
+ local t = { 4, 8, 9, 10 }
+ eq(true, vim.iter(t):any(odd))
+ end
+
+ do
+ local t = { 4, 8, 10 }
+ eq(false, vim.iter(t):any(odd))
+ end
+
+ do
+ eq(true, vim.iter(vim.gsplit('a|b|c|d', '|')):any(function(s) return s == 'd' end))
+ eq(false, vim.iter(vim.gsplit('a|b|c|d', '|')):any(function(s) return s == 'e' end))
+ end
+ end)
+
+ it('all()', function()
+ local function odd(v)
+ return v % 2 ~= 0
+ end
+
+ do
+ local t = { 3, 5, 7, 9 }
+ eq(true, vim.iter(t):all(odd))
+ end
+
+ do
+ local t = { 3, 5, 7, 10 }
+ eq(false, vim.iter(t):all(odd))
+ end
+
+ do
+ eq(true, vim.iter(vim.gsplit('a|a|a|a', '|')):all(function(s) return s == 'a' end))
+ eq(false, vim.iter(vim.gsplit('a|a|a|b', '|')):all(function(s) return s == 'a' end))
+ end
+ end)
+
+ it('last()', function()
+ local s = 'abcdefghijklmnopqrstuvwxyz'
+ eq('z', vim.iter(vim.split(s, '')):last())
+ eq('z', vim.iter(vim.gsplit(s, '')):last())
+ end)
+
+ it('enumerate()', function()
+ local it = vim.iter(vim.gsplit('abc', '')):enumerate()
+ eq({1, 'a'}, {it:next()})
+ eq({2, 'b'}, {it:next()})
+ eq({3, 'c'}, {it:next()})
+ eq({}, {it:next()})
+ end)
+
+ it('peek()', function()
+ do
+ local it = vim.iter({ 3, 6, 9, 12 })
+ eq(3, it:peek())
+ eq(3, it:peek())
+ eq(3, it:next())
+ end
+
+ do
+ local it = vim.iter(vim.gsplit('hi', ''))
+ matches('peek%(%) requires a list%-like table', pcall_err(it.peek, it))
+ end
+ end)
+
+ it('find()', function()
+ local t = {3, 6, 9, 12}
+ eq(12, vim.iter(t):find(12))
+ eq(nil, vim.iter(t):find(15))
+ eq(12, vim.iter(t):find(function(v) return v % 4 == 0 end))
+
+ do
+ local it = vim.iter(t)
+ local pred = function(v) return v % 3 == 0 end
+ eq(3, it:find(pred))
+ eq(6, it:find(pred))
+ eq(9, it:find(pred))
+ eq(12, it:find(pred))
+ eq(nil, it:find(pred))
+ end
+
+ do
+ local it = vim.iter(vim.gsplit('AbCdE', ''))
+ local pred = function(s) return s:match('[A-Z]') end
+ eq('A', it:find(pred))
+ eq('C', it:find(pred))
+ eq('E', it:find(pred))
+ eq(nil, it:find(pred))
+ end
+ end)
+
+ it('rfind()', function()
+ local t = {1, 2, 3, 2, 1}
+ do
+ local it = vim.iter(t)
+ eq(1, it:rfind(1))
+ eq(1, it:rfind(1))
+ eq(nil, it:rfind(1))
+ end
+
+ do
+ local it = vim.iter(t):enumerate()
+ local pred = function(i) return i % 2 ~= 0 end
+ eq({5, 1}, {it:rfind(pred)})
+ eq({3, 3}, {it:rfind(pred)})
+ eq({1, 1}, {it:rfind(pred)})
+ eq(nil, it:rfind(pred))
+ end
+
+ do
+ local it = vim.iter(vim.gsplit('AbCdE', ''))
+ matches('rfind%(%) requires a list%-like table', pcall_err(it.rfind, it, 'E'))
+ end
+ end)
+
+ it('nextback()', function()
+ do
+ local it = vim.iter({ 1, 2, 3, 4 })
+ eq(4, it:nextback())
+ eq(3, it:nextback())
+ eq(2, it:nextback())
+ eq(1, it:nextback())
+ eq(nil, it:nextback())
+ eq(nil, it:nextback())
+ end
+
+ do
+ local it = vim.iter(vim.gsplit('hi', ''))
+ matches('nextback%(%) requires a list%-like table', pcall_err(it.nextback, it))
+ end
+ end)
+
+ it('peekback()', function()
+ do
+ local it = vim.iter({ 1, 2, 3, 4 })
+ eq(4, it:peekback())
+ eq(4, it:peekback())
+ eq(4, it:nextback())
+ end
+
+ do
+ local it = vim.iter(vim.gsplit('hi', ''))
+ matches('peekback%(%) requires a list%-like table', pcall_err(it.peekback, it))
+ end
+ end)
+
+ it('fold()', function()
+ local t = {1, 2, 3, 4, 5}
+ eq(115, vim.iter(t):fold(100, function(acc, v) return acc + v end))
+ eq({5, 4, 3, 2, 1}, vim.iter(t):fold({}, function(acc, v)
+ table.insert(acc, 1, v)
+ return acc
+ end))
+ end)
+
+ it('handles map-like tables', function()
+ local it = vim.iter({ a = 1, b = 2, c = 3 }):map(function(k, v)
+ if v % 2 ~= 0 then
+ return k:upper(), v * 2
+ end
+ end)
+
+ local t = it:fold({}, function(t, k, v)
+ t[k] = v
+ return t
+ end)
+ eq({ A = 2, C = 6 }, t)
+ end)
+
+ it('handles table values mid-pipeline', function()
+ local map = {
+ item = {
+ file = 'test',
+ },
+ item_2 = {
+ file = 'test',
+ },
+ item_3 = {
+ file = 'test',
+ },
+ }
+
+ local output = vim.iter(map):map(function(key, value)
+ return { [key] = value.file }
+ end):totable()
+
+ table.sort(output, function(a, b)
+ return next(a) < next(b)
+ end)
+
+ eq({
+ { item = 'test' },
+ { item_2 = 'test' },
+ { item_3 = 'test' },
+ }, output)
+ end)
+
+ it('handles nil values', function()
+ local t = {1, 2, 3, 4, 5}
+ do
+ local it = vim.iter(t):enumerate():map(function(i, v)
+ if i % 2 == 0 then
+ return nil, v*v
+ end
+ return v, nil
+ end)
+ eq({
+ { [1] = 1 },
+ { [2] = 4 },
+ { [1] = 3 },
+ { [2] = 16 },
+ { [1] = 5 },
+ }, it:totable())
+ end
+
+ do
+ local it = vim.iter(ipairs(t)):map(function(i, v)
+ if i % 2 == 0 then
+ return nil, v*v
+ end
+ return v, nil
+ end)
+ eq({
+ { [1] = 1 },
+ { [2] = 4 },
+ { [1] = 3 },
+ { [2] = 16 },
+ { [1] = 5 },
+ }, it:totable())
+ end
+ end)
+end)
diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua
index 42927f7e1b..53c21fd668 100644
--- a/test/functional/lua/vim_spec.lua
+++ b/test/functional/lua/vim_spec.lua
@@ -3031,427 +3031,6 @@ describe('lua stdlib', function()
eq(false, if_nil(d, c))
eq(NIL, if_nil(a))
end)
-
- describe('vim.iter', function()
- it('filter()', function()
- local function odd(v)
- return v % 2 ~= 0
- end
-
- local t = { 1, 2, 3, 4, 5 }
- eq({ 1, 3, 5 }, vim.iter(t):filter(odd):totable())
- eq({ 2, 4 }, vim.iter(t):filter(function(v) return not odd(v) end):totable())
- eq({}, vim.iter(t):filter(function(v) if v > 5 then return v end end):totable())
-
- do
- local it = vim.iter(ipairs(t))
- it:filter(function(i, v) return i > 1 and v < 5 end)
- it:map(function(_, v) return v * 2 end)
- eq({ 4, 6, 8 }, it:totable())
- end
-
- local it = vim.iter(string.gmatch('the quick brown fox', '%w+'))
- eq({'the', 'fox'}, it:filter(function(s) return #s <= 3 end):totable())
- end)
-
- it('map()', function()
- local t = { 1, 2, 3, 4, 5 }
- eq(
- { 2, 4, 6, 8, 10 },
- vim
- .iter(t)
- :map(function(v)
- return 2 * v
- end)
- :totable()
- )
-
- local it = vim.gsplit(
- [[
- Line 1
- Line 2
- Line 3
- Line 4
- ]],
- '\n'
- )
-
- eq(
- { 'Lion 2', 'Lion 4' },
- vim
- .iter(it)
- :map(function(s)
- local lnum = s:match('(%d+)')
- if lnum and tonumber(lnum) % 2 == 0 then
- return vim.trim(s:gsub('Line', 'Lion'))
- end
- end)
- :totable()
- )
- end)
-
- it('for loops', function()
- local t = {1, 2, 3, 4, 5}
- local acc = 0
- for v in vim.iter(t):map(function(v) return v * 3 end) do
- acc = acc + v
- end
- eq(45, acc)
- end)
-
- it('totable()', function()
- do
- local it = vim.iter({1, 2, 3}):map(function(v) return v, v*v end)
- eq({{1, 1}, {2, 4}, {3, 9}}, it:totable())
- end
-
- do
- local it = vim.iter(string.gmatch('1,4,lol,17,blah,2,9,3', '%d+')):map(tonumber)
- eq({1, 4, 17, 2, 9, 3}, it:totable())
- end
- end)
-
- it('next()', function()
- local it = vim.iter({1, 2, 3}):map(function(v) return 2 * v end)
- eq(2, it:next())
- eq(4, it:next())
- eq(6, it:next())
- eq(nil, it:next())
- end)
-
- it('rev()', function()
- eq({3, 2, 1}, vim.iter({1, 2, 3}):rev():totable())
-
- local it = vim.iter(string.gmatch("abc", "%w"))
- matches('rev%(%) requires a list%-like table', pcall_err(it.rev, it))
- end)
-
- it('skip()', function()
- do
- local t = {4, 3, 2, 1}
- eq(t, vim.iter(t):skip(0):totable())
- eq({3, 2, 1}, vim.iter(t):skip(1):totable())
- eq({2, 1}, vim.iter(t):skip(2):totable())
- eq({1}, vim.iter(t):skip(#t - 1):totable())
- eq({}, vim.iter(t):skip(#t):totable())
- eq({}, vim.iter(t):skip(#t + 1):totable())
- end
-
- do
- local function skip(n)
- return vim.iter(vim.gsplit('a|b|c|d', '|')):skip(n):totable()
- end
- eq({'a', 'b', 'c', 'd'}, skip(0))
- eq({'b', 'c', 'd'}, skip(1))
- eq({'c', 'd'}, skip(2))
- eq({'d'}, skip(3))
- eq({}, skip(4))
- eq({}, skip(5))
- end
- end)
-
- it('skipback()', function()
- do
- local t = {4, 3, 2, 1}
- eq(t, vim.iter(t):skipback(0):totable())
- eq({4, 3, 2}, vim.iter(t):skipback(1):totable())
- eq({4, 3}, vim.iter(t):skipback(2):totable())
- eq({4}, vim.iter(t):skipback(#t - 1):totable())
- eq({}, vim.iter(t):skipback(#t):totable())
- eq({}, vim.iter(t):skipback(#t + 1):totable())
- end
-
- local it = vim.iter(vim.gsplit('a|b|c|d', '|'))
- matches('skipback%(%) requires a list%-like table', pcall_err(it.skipback, it, 0))
- end)
-
- it('slice()', function()
- local t = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
- eq({3, 4, 5, 6, 7}, vim.iter(t):slice(3, 7):totable())
- eq({}, vim.iter(t):slice(6, 5):totable())
- eq({}, vim.iter(t):slice(0, 0):totable())
- eq({1}, vim.iter(t):slice(1, 1):totable())
- eq({1, 2}, vim.iter(t):slice(1, 2):totable())
- eq({10}, vim.iter(t):slice(10, 10):totable())
- eq({8, 9, 10}, vim.iter(t):slice(8, 11):totable())
- end)
-
- it('nth()', function()
- do
- local t = {4, 3, 2, 1}
- eq(nil, vim.iter(t):nth(0))
- eq(4, vim.iter(t):nth(1))
- eq(3, vim.iter(t):nth(2))
- eq(2, vim.iter(t):nth(3))
- eq(1, vim.iter(t):nth(4))
- eq(nil, vim.iter(t):nth(5))
- end
-
- do
- local function nth(n)
- return vim.iter(vim.gsplit('a|b|c|d', '|')):nth(n)
- end
- eq(nil, nth(0))
- eq('a', nth(1))
- eq('b', nth(2))
- eq('c', nth(3))
- eq('d', nth(4))
- eq(nil, nth(5))
- end
- end)
-
- it('nthback()', function()
- do
- local t = {4, 3, 2, 1}
- eq(nil, vim.iter(t):nthback(0))
- eq(1, vim.iter(t):nthback(1))
- eq(2, vim.iter(t):nthback(2))
- eq(3, vim.iter(t):nthback(3))
- eq(4, vim.iter(t):nthback(4))
- eq(nil, vim.iter(t):nthback(5))
- end
-
- local it = vim.iter(vim.gsplit('a|b|c|d', '|'))
- matches('skipback%(%) requires a list%-like table', pcall_err(it.nthback, it, 1))
- end)
-
- it('any()', function()
- local function odd(v)
- return v % 2 ~= 0
- end
-
- do
- local t = { 4, 8, 9, 10 }
- eq(true, vim.iter(t):any(odd))
- end
-
- do
- local t = { 4, 8, 10 }
- eq(false, vim.iter(t):any(odd))
- end
-
- do
- eq(true, vim.iter(vim.gsplit('a|b|c|d', '|')):any(function(s) return s == 'd' end))
- eq(false, vim.iter(vim.gsplit('a|b|c|d', '|')):any(function(s) return s == 'e' end))
- end
- end)
-
- it('all()', function()
- local function odd(v)
- return v % 2 ~= 0
- end
-
- do
- local t = { 3, 5, 7, 9 }
- eq(true, vim.iter(t):all(odd))
- end
-
- do
- local t = { 3, 5, 7, 10 }
- eq(false, vim.iter(t):all(odd))
- end
-
- do
- eq(true, vim.iter(vim.gsplit('a|a|a|a', '|')):all(function(s) return s == 'a' end))
- eq(false, vim.iter(vim.gsplit('a|a|a|b', '|')):all(function(s) return s == 'a' end))
- end
- end)
-
- it('last()', function()
- local s = 'abcdefghijklmnopqrstuvwxyz'
- eq('z', vim.iter(vim.split(s, '')):last())
- eq('z', vim.iter(vim.gsplit(s, '')):last())
- end)
-
- it('enumerate()', function()
- local it = vim.iter(vim.gsplit('abc', '')):enumerate()
- eq({1, 'a'}, {it:next()})
- eq({2, 'b'}, {it:next()})
- eq({3, 'c'}, {it:next()})
- eq({}, {it:next()})
- end)
-
- it('peek()', function()
- do
- local it = vim.iter({ 3, 6, 9, 12 })
- eq(3, it:peek())
- eq(3, it:peek())
- eq(3, it:next())
- end
-
- do
- local it = vim.iter(vim.gsplit('hi', ''))
- matches('peek%(%) requires a list%-like table', pcall_err(it.peek, it))
- end
- end)
-
- it('find()', function()
- local t = {3, 6, 9, 12}
- eq(12, vim.iter(t):find(12))
- eq(nil, vim.iter(t):find(15))
- eq(12, vim.iter(t):find(function(v) return v % 4 == 0 end))
-
- do
- local it = vim.iter(t)
- local pred = function(v) return v % 3 == 0 end
- eq(3, it:find(pred))
- eq(6, it:find(pred))
- eq(9, it:find(pred))
- eq(12, it:find(pred))
- eq(nil, it:find(pred))
- end
-
- do
- local it = vim.iter(vim.gsplit('AbCdE', ''))
- local pred = function(s) return s:match('[A-Z]') end
- eq('A', it:find(pred))
- eq('C', it:find(pred))
- eq('E', it:find(pred))
- eq(nil, it:find(pred))
- end
- end)
-
- it('rfind()', function()
- local t = {1, 2, 3, 2, 1}
- do
- local it = vim.iter(t)
- eq(1, it:rfind(1))
- eq(1, it:rfind(1))
- eq(nil, it:rfind(1))
- end
-
- do
- local it = vim.iter(t):enumerate()
- local pred = function(i) return i % 2 ~= 0 end
- eq({5, 1}, {it:rfind(pred)})
- eq({3, 3}, {it:rfind(pred)})
- eq({1, 1}, {it:rfind(pred)})
- eq(nil, it:rfind(pred))
- end
-
- do
- local it = vim.iter(vim.gsplit('AbCdE', ''))
- matches('rfind%(%) requires a list%-like table', pcall_err(it.rfind, it, 'E'))
- end
- end)
-
- it('nextback()', function()
- do
- local it = vim.iter({ 1, 2, 3, 4 })
- eq(4, it:nextback())
- eq(3, it:nextback())
- eq(2, it:nextback())
- eq(1, it:nextback())
- eq(nil, it:nextback())
- eq(nil, it:nextback())
- end
-
- do
- local it = vim.iter(vim.gsplit('hi', ''))
- matches('nextback%(%) requires a list%-like table', pcall_err(it.nextback, it))
- end
- end)
-
- it('peekback()', function()
- do
- local it = vim.iter({ 1, 2, 3, 4 })
- eq(4, it:peekback())
- eq(4, it:peekback())
- eq(4, it:peekback())
- end
-
- do
- local it = vim.iter(vim.gsplit('hi', ''))
- matches('peekback%(%) requires a list%-like table', pcall_err(it.peekback, it))
- end
- end)
-
- it('fold()', function()
- local t = {1, 2, 3, 4, 5}
- eq(115, vim.iter(t):fold(100, function(acc, v) return acc + v end))
- eq({5, 4, 3, 2, 1}, vim.iter(t):fold({}, function(acc, v)
- table.insert(acc, 1, v)
- return acc
- end))
- end)
-
- it('handles map-like tables', function()
- local it = vim.iter({ a = 1, b = 2, c = 3 }):map(function(k, v)
- if v % 2 ~= 0 then
- return k:upper(), v * 2
- end
- end)
-
- local t = it:fold({}, function(t, k, v)
- t[k] = v
- return t
- end)
- eq({ A = 2, C = 6 }, t)
- end)
-
- it('handles table values mid-pipeline', function()
- local map = {
- item = {
- file = 'test',
- },
- item_2 = {
- file = 'test',
- },
- item_3 = {
- file = 'test',
- },
- }
-
- local output = vim.iter(map):map(function(key, value)
- return { [key] = value.file }
- end):totable()
-
- table.sort(output, function(a, b)
- return next(a) < next(b)
- end)
-
- eq({
- { item = 'test' },
- { item_2 = 'test' },
- { item_3 = 'test' },
- }, output)
- end)
-
- it('handles nil values', function()
- local t = {1, 2, 3, 4, 5}
- do
- local it = vim.iter(t):enumerate():map(function(i, v)
- if i % 2 == 0 then
- return nil, v*v
- end
- return v, nil
- end)
- eq({
- { [1] = 1 },
- { [2] = 4 },
- { [1] = 3 },
- { [2] = 16 },
- { [1] = 5 },
- }, it:totable())
- end
-
- do
- local it = vim.iter(ipairs(t)):map(function(i, v)
- if i % 2 == 0 then
- return nil, v*v
- end
- return v, nil
- end)
- eq({
- { [1] = 1 },
- { [2] = 4 },
- { [1] = 3 },
- { [2] = 16 },
- { [1] = 5 },
- }, it:totable())
- end
- end)
- end)
end)
describe('lua: builtin modules', function()