iter_spec.lua 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  1. local t = require('test.testutil')
  2. local eq = t.eq
  3. local matches = t.matches
  4. local pcall_err = t.pcall_err
  5. describe('vim.iter', function()
  6. it('new() on iterable class instance', function()
  7. local rb = vim.ringbuf(3)
  8. rb:push('a')
  9. rb:push('b')
  10. local it = vim.iter(rb)
  11. eq({ 'a', 'b' }, it:totable())
  12. end)
  13. it('filter()', function()
  14. local function odd(v)
  15. return v % 2 ~= 0
  16. end
  17. local q = { 1, 2, 3, 4, 5 }
  18. eq({ 1, 3, 5 }, vim.iter(q):filter(odd):totable())
  19. eq(
  20. { 2, 4 },
  21. vim
  22. .iter(q)
  23. :filter(function(v)
  24. return not odd(v)
  25. end)
  26. :totable()
  27. )
  28. eq(
  29. {},
  30. vim
  31. .iter(q)
  32. :filter(function(v)
  33. return v > 5
  34. end)
  35. :totable()
  36. )
  37. do
  38. local it = vim.iter(ipairs(q))
  39. it:filter(function(i, v)
  40. return i > 1 and v < 5
  41. end)
  42. it:map(function(_, v)
  43. return v * 2
  44. end)
  45. eq({ 4, 6, 8 }, it:totable())
  46. end
  47. local it = vim.iter(string.gmatch('the quick brown fox', '%w+'))
  48. eq(
  49. { 'the', 'fox' },
  50. it:filter(function(s)
  51. return #s <= 3
  52. end):totable()
  53. )
  54. end)
  55. it('map()', function()
  56. local q = { 1, 2, 3, 4, 5 }
  57. eq(
  58. { 2, 4, 6, 8, 10 },
  59. vim
  60. .iter(q)
  61. :map(function(v)
  62. return 2 * v
  63. end)
  64. :totable()
  65. )
  66. local it = vim.gsplit(
  67. [[
  68. Line 1
  69. Line 2
  70. Line 3
  71. Line 4
  72. ]],
  73. '\n'
  74. )
  75. eq(
  76. { 'Lion 2', 'Lion 4' },
  77. vim
  78. .iter(it)
  79. :map(function(s)
  80. local lnum = s:match('(%d+)')
  81. if lnum and tonumber(lnum) % 2 == 0 then
  82. return vim.trim(s:gsub('Line', 'Lion'))
  83. end
  84. end)
  85. :totable()
  86. )
  87. end)
  88. it('for loops', function()
  89. local q = { 1, 2, 3, 4, 5 }
  90. local acc = 0
  91. for v in
  92. vim.iter(q):map(function(v)
  93. return v * 3
  94. end)
  95. do
  96. acc = acc + v
  97. end
  98. eq(45, acc)
  99. end)
  100. it('totable()', function()
  101. do
  102. local it = vim.iter({ 1, 2, 3 }):map(function(v)
  103. return v, v * v
  104. end)
  105. eq({ { 1, 1 }, { 2, 4 }, { 3, 9 } }, it:totable())
  106. end
  107. -- Holes in array-like tables are removed
  108. eq({ 1, 2, 3 }, vim.iter({ 1, nil, 2, nil, 3 }):totable())
  109. do
  110. local it = vim.iter(string.gmatch('1,4,lol,17,blah,2,9,3', '%d+')):map(tonumber)
  111. eq({ 1, 4, 17, 2, 9, 3 }, it:totable())
  112. end
  113. end)
  114. it('join()', function()
  115. eq('1, 2, 3', vim.iter({ 1, 2, 3 }):join(', '))
  116. eq('a|b|c|d', vim.iter(vim.gsplit('a|b|c|d', '|')):join('|'))
  117. end)
  118. it('next()', function()
  119. local it = vim.iter({ 1, 2, 3 }):map(function(v)
  120. return 2 * v
  121. end)
  122. eq(2, it:next())
  123. eq(4, it:next())
  124. eq(6, it:next())
  125. eq(nil, it:next())
  126. end)
  127. it('rev()', function()
  128. eq({ 3, 2, 1 }, vim.iter({ 1, 2, 3 }):rev():totable())
  129. local it = vim.iter(string.gmatch('abc', '%w'))
  130. matches('rev%(%) requires an array%-like table', pcall_err(it.rev, it))
  131. end)
  132. it('skip()', function()
  133. do
  134. local q = { 4, 3, 2, 1 }
  135. eq(q, vim.iter(q):skip(0):totable())
  136. eq({ 3, 2, 1 }, vim.iter(q):skip(1):totable())
  137. eq({ 2, 1 }, vim.iter(q):skip(2):totable())
  138. eq({ 1 }, vim.iter(q):skip(#q - 1):totable())
  139. eq({}, vim.iter(q):skip(#q):totable())
  140. eq({}, vim.iter(q):skip(#q + 1):totable())
  141. end
  142. do
  143. local function skip(n)
  144. return vim.iter(vim.gsplit('a|b|c|d', '|')):skip(n):totable()
  145. end
  146. eq({ 'a', 'b', 'c', 'd' }, skip(0))
  147. eq({ 'b', 'c', 'd' }, skip(1))
  148. eq({ 'c', 'd' }, skip(2))
  149. eq({ 'd' }, skip(3))
  150. eq({}, skip(4))
  151. eq({}, skip(5))
  152. end
  153. end)
  154. it('rskip()', function()
  155. do
  156. local q = { 4, 3, 2, 1 }
  157. eq(q, vim.iter(q):rskip(0):totable())
  158. eq({ 4, 3, 2 }, vim.iter(q):rskip(1):totable())
  159. eq({ 4, 3 }, vim.iter(q):rskip(2):totable())
  160. eq({ 4 }, vim.iter(q):rskip(#q - 1):totable())
  161. eq({}, vim.iter(q):rskip(#q):totable())
  162. eq({}, vim.iter(q):rskip(#q + 1):totable())
  163. end
  164. local it = vim.iter(vim.gsplit('a|b|c|d', '|'))
  165. matches('rskip%(%) requires an array%-like table', pcall_err(it.rskip, it, 0))
  166. end)
  167. it('slice()', function()
  168. local q = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }
  169. eq({ 3, 4, 5, 6, 7 }, vim.iter(q):slice(3, 7):totable())
  170. eq({}, vim.iter(q):slice(6, 5):totable())
  171. eq({}, vim.iter(q):slice(0, 0):totable())
  172. eq({ 1 }, vim.iter(q):slice(1, 1):totable())
  173. eq({ 1, 2 }, vim.iter(q):slice(1, 2):totable())
  174. eq({ 10 }, vim.iter(q):slice(10, 10):totable())
  175. eq({ 8, 9, 10 }, vim.iter(q):slice(8, 11):totable())
  176. local it = vim.iter(vim.gsplit('a|b|c|d', '|'))
  177. matches('slice%(%) requires an array%-like table', pcall_err(it.slice, it, 1, 3))
  178. end)
  179. it('nth()', function()
  180. do
  181. local q = { 4, 3, 2, 1 }
  182. eq(nil, vim.iter(q):nth(0))
  183. eq(4, vim.iter(q):nth(1))
  184. eq(3, vim.iter(q):nth(2))
  185. eq(2, vim.iter(q):nth(3))
  186. eq(1, vim.iter(q):nth(4))
  187. eq(nil, vim.iter(q):nth(5))
  188. end
  189. do
  190. local function nth(n)
  191. return vim.iter(vim.gsplit('a|b|c|d', '|')):nth(n)
  192. end
  193. eq(nil, nth(0))
  194. eq('a', nth(1))
  195. eq('b', nth(2))
  196. eq('c', nth(3))
  197. eq('d', nth(4))
  198. eq(nil, nth(5))
  199. end
  200. end)
  201. it('nth(-x) advances in reverse order starting from end', function()
  202. do
  203. local q = { 4, 3, 2, 1 }
  204. eq(nil, vim.iter(q):nth(0))
  205. eq(1, vim.iter(q):nth(-1))
  206. eq(2, vim.iter(q):nth(-2))
  207. eq(3, vim.iter(q):nth(-3))
  208. eq(4, vim.iter(q):nth(-4))
  209. eq(nil, vim.iter(q):nth(-5))
  210. end
  211. local it = vim.iter(vim.gsplit('a|b|c|d', '|'))
  212. matches('rskip%(%) requires an array%-like table', pcall_err(it.nth, it, -1))
  213. end)
  214. it('take()', function()
  215. do
  216. local q = { 4, 3, 2, 1 }
  217. eq({}, vim.iter(q):take(0):totable())
  218. eq({ 4 }, vim.iter(q):take(1):totable())
  219. eq({ 4, 3 }, vim.iter(q):take(2):totable())
  220. eq({ 4, 3, 2 }, vim.iter(q):take(3):totable())
  221. eq({ 4, 3, 2, 1 }, vim.iter(q):take(4):totable())
  222. eq({ 4, 3, 2, 1 }, vim.iter(q):take(5):totable())
  223. end
  224. do
  225. local q = { 4, 3, 2, 1 }
  226. eq({ 1, 2, 3 }, vim.iter(q):rev():take(3):totable())
  227. eq({ 2, 3, 4 }, vim.iter(q):take(3):rev():totable())
  228. end
  229. do
  230. local q = { 4, 3, 2, 1 }
  231. local it = vim.iter(q)
  232. eq({ 4, 3 }, it:take(2):totable())
  233. -- tail is already set from the previous take()
  234. eq({ 4, 3 }, it:take(3):totable())
  235. end
  236. do
  237. local it = vim.iter(vim.gsplit('a|b|c|d', '|'))
  238. eq({ 'a', 'b' }, it:take(2):totable())
  239. -- non-array iterators are consumed by take()
  240. eq({}, it:take(2):totable())
  241. end
  242. end)
  243. it('any()', function()
  244. local function odd(v)
  245. return v % 2 ~= 0
  246. end
  247. do
  248. local q = { 4, 8, 9, 10 }
  249. eq(true, vim.iter(q):any(odd))
  250. end
  251. do
  252. local q = { 4, 8, 10 }
  253. eq(false, vim.iter(q):any(odd))
  254. end
  255. do
  256. eq(
  257. true,
  258. vim.iter(vim.gsplit('a|b|c|d', '|')):any(function(s)
  259. return s == 'd'
  260. end)
  261. )
  262. eq(
  263. false,
  264. vim.iter(vim.gsplit('a|b|c|d', '|')):any(function(s)
  265. return s == 'e'
  266. end)
  267. )
  268. end
  269. end)
  270. it('all()', function()
  271. local function odd(v)
  272. return v % 2 ~= 0
  273. end
  274. do
  275. local q = { 3, 5, 7, 9 }
  276. eq(true, vim.iter(q):all(odd))
  277. end
  278. do
  279. local q = { 3, 5, 7, 10 }
  280. eq(false, vim.iter(q):all(odd))
  281. end
  282. do
  283. eq(
  284. true,
  285. vim.iter(vim.gsplit('a|a|a|a', '|')):all(function(s)
  286. return s == 'a'
  287. end)
  288. )
  289. eq(
  290. false,
  291. vim.iter(vim.gsplit('a|a|a|b', '|')):all(function(s)
  292. return s == 'a'
  293. end)
  294. )
  295. end
  296. end)
  297. it('last()', function()
  298. local s = 'abcdefghijklmnopqrstuvwxyz'
  299. eq('z', vim.iter(vim.split(s, '')):last())
  300. eq('z', vim.iter(vim.gsplit(s, '')):last())
  301. end)
  302. it('enumerate()', function()
  303. local it = vim.iter(vim.gsplit('abc', '')):enumerate()
  304. eq({ 1, 'a' }, { it:next() })
  305. eq({ 2, 'b' }, { it:next() })
  306. eq({ 3, 'c' }, { it:next() })
  307. eq({}, { it:next() })
  308. end)
  309. it('peek()', function()
  310. do
  311. local it = vim.iter({ 3, 6, 9, 12 })
  312. eq(3, it:peek())
  313. eq(3, it:peek())
  314. eq(3, it:next())
  315. end
  316. do
  317. local it = vim.iter(vim.gsplit('hi', ''))
  318. matches('peek%(%) requires an array%-like table', pcall_err(it.peek, it))
  319. end
  320. end)
  321. it('find()', function()
  322. local q = { 3, 6, 9, 12 }
  323. eq(12, vim.iter(q):find(12))
  324. eq(nil, vim.iter(q):find(15))
  325. eq(
  326. 12,
  327. vim.iter(q):find(function(v)
  328. return v % 4 == 0
  329. end)
  330. )
  331. do
  332. local it = vim.iter(q)
  333. local pred = function(v)
  334. return v % 3 == 0
  335. end
  336. eq(3, it:find(pred))
  337. eq(6, it:find(pred))
  338. eq(9, it:find(pred))
  339. eq(12, it:find(pred))
  340. eq(nil, it:find(pred))
  341. end
  342. do
  343. local it = vim.iter(vim.gsplit('AbCdE', ''))
  344. local pred = function(s)
  345. return s:match('[A-Z]')
  346. end
  347. eq('A', it:find(pred))
  348. eq('C', it:find(pred))
  349. eq('E', it:find(pred))
  350. eq(nil, it:find(pred))
  351. end
  352. end)
  353. it('rfind()', function()
  354. local q = { 1, 2, 3, 2, 1 }
  355. do
  356. local it = vim.iter(q)
  357. eq(1, it:rfind(1))
  358. eq(1, it:rfind(1))
  359. eq(nil, it:rfind(1))
  360. end
  361. do
  362. local it = vim.iter(q):enumerate()
  363. local pred = function(i)
  364. return i % 2 ~= 0
  365. end
  366. eq({ 5, 1 }, { it:rfind(pred) })
  367. eq({ 3, 3 }, { it:rfind(pred) })
  368. eq({ 1, 1 }, { it:rfind(pred) })
  369. eq(nil, it:rfind(pred))
  370. end
  371. do
  372. local it = vim.iter(vim.gsplit('AbCdE', ''))
  373. matches('rfind%(%) requires an array%-like table', pcall_err(it.rfind, it, 'E'))
  374. end
  375. end)
  376. it('pop()', function()
  377. do
  378. local it = vim.iter({ 1, 2, 3, 4 })
  379. eq(4, it:pop())
  380. eq(3, it:pop())
  381. eq(2, it:pop())
  382. eq(1, it:pop())
  383. eq(nil, it:pop())
  384. eq(nil, it:pop())
  385. end
  386. do
  387. local it = vim.iter(vim.gsplit('hi', ''))
  388. matches('pop%(%) requires an array%-like table', pcall_err(it.pop, it))
  389. end
  390. end)
  391. it('rpeek()', function()
  392. do
  393. local it = vim.iter({ 1, 2, 3, 4 })
  394. eq(4, it:rpeek())
  395. eq(4, it:rpeek())
  396. eq(4, it:pop())
  397. end
  398. do
  399. local it = vim.iter(vim.gsplit('hi', ''))
  400. matches('rpeek%(%) requires an array%-like table', pcall_err(it.rpeek, it))
  401. end
  402. end)
  403. it('fold()', function()
  404. local q = { 1, 2, 3, 4, 5 }
  405. eq(
  406. 115,
  407. vim.iter(q):fold(100, function(acc, v)
  408. return acc + v
  409. end)
  410. )
  411. eq(
  412. { 5, 4, 3, 2, 1 },
  413. vim.iter(q):fold({}, function(acc, v)
  414. table.insert(acc, 1, v)
  415. return acc
  416. end)
  417. )
  418. end)
  419. it('flatten()', function()
  420. local q = { { 1, { 2 } }, { { { { 3 } } }, { 4 } }, { 5 } }
  421. eq(q, vim.iter(q):flatten(-1):totable())
  422. eq(q, vim.iter(q):flatten(0):totable())
  423. eq({ 1, { 2 }, { { { 3 } } }, { 4 }, 5 }, vim.iter(q):flatten():totable())
  424. eq({ 1, 2, { { 3 } }, 4, 5 }, vim.iter(q):flatten(2):totable())
  425. eq({ 1, 2, { 3 }, 4, 5 }, vim.iter(q):flatten(3):totable())
  426. eq({ 1, 2, 3, 4, 5 }, vim.iter(q):flatten(4):totable())
  427. local m = { a = 1, b = { 2, 3 }, d = { 4 } }
  428. local it = vim.iter(m)
  429. local flat_err = 'flatten%(%) requires an array%-like table'
  430. matches(flat_err, pcall_err(it.flatten, it))
  431. -- cases from the documentation
  432. local simple_example = { 1, { 2 }, { { 3 } } }
  433. eq({ 1, 2, { 3 } }, vim.iter(simple_example):flatten():totable())
  434. local not_list_like = { [2] = 2 }
  435. eq({ 2 }, vim.iter(not_list_like):flatten():totable())
  436. local also_not_list_like = { nil, 2 }
  437. eq({ 2 }, vim.iter(also_not_list_like):flatten():totable())
  438. eq({ 1, 2, 3 }, vim.iter({ nil, { 1, nil, 2 }, 3 }):flatten():totable())
  439. local nested_non_lists = vim.iter({ 1, { { a = 2 } }, { { nil } }, { 3 } })
  440. eq({ 1, { a = 2 }, { nil }, 3 }, nested_non_lists:flatten():totable())
  441. -- only error if we're going deep enough to flatten a dict-like table
  442. matches(flat_err, pcall_err(nested_non_lists.flatten, nested_non_lists, math.huge))
  443. end)
  444. it('handles map-like tables', function()
  445. local it = vim.iter({ a = 1, b = 2, c = 3 }):map(function(k, v)
  446. if v % 2 ~= 0 then
  447. return k:upper(), v * 2
  448. end
  449. end)
  450. local q = it:fold({}, function(q, k, v)
  451. q[k] = v
  452. return q
  453. end)
  454. eq({ A = 2, C = 6 }, q)
  455. end)
  456. it('handles table values mid-pipeline', function()
  457. local map = {
  458. item = {
  459. file = 'test',
  460. },
  461. item_2 = {
  462. file = 'test',
  463. },
  464. item_3 = {
  465. file = 'test',
  466. },
  467. }
  468. local output = vim
  469. .iter(map)
  470. :map(function(key, value)
  471. return { [key] = value.file }
  472. end)
  473. :totable()
  474. table.sort(output, function(a, b)
  475. return next(a) < next(b)
  476. end)
  477. eq({
  478. { item = 'test' },
  479. { item_2 = 'test' },
  480. { item_3 = 'test' },
  481. }, output)
  482. end)
  483. end)