ctx_functions_spec.lua 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. local t = require('test.testutil')
  2. local n = require('test.functional.testnvim')()
  3. local call = n.call
  4. local clear = n.clear
  5. local command = n.command
  6. local eq = t.eq
  7. local eval = n.eval
  8. local feed = n.feed
  9. local map = vim.tbl_map
  10. local api = n.api
  11. local parse_context = n.parse_context
  12. local exec_capture = n.exec_capture
  13. local source = n.source
  14. local trim = vim.trim
  15. local write_file = t.write_file
  16. local pcall_err = t.pcall_err
  17. describe('context functions', function()
  18. local fname1 = 'Xtest-functional-eval-ctx1'
  19. local fname2 = 'Xtest-functional-eval-ctx2'
  20. local outofbounds = 'Vim:E475: Invalid value for argument index: out of bounds'
  21. before_each(function()
  22. clear()
  23. write_file(fname1, '1\n2\n3')
  24. write_file(fname2, 'a\nb\nc')
  25. end)
  26. after_each(function()
  27. os.remove(fname1)
  28. os.remove(fname2)
  29. end)
  30. describe('ctxpush/ctxpop', function()
  31. it('saves and restores registers properly', function()
  32. local regs = { '1', '2', '3', 'a' }
  33. local vals = { '1', '2', '3', 'hjkl' }
  34. feed('i1<cr>2<cr>3<c-[>ddddddqahjklq')
  35. eq(
  36. vals,
  37. map(function(r)
  38. return trim(call('getreg', r))
  39. end, regs)
  40. )
  41. call('ctxpush')
  42. call('ctxpush', { 'regs' })
  43. map(function(r)
  44. call('setreg', r, {})
  45. end, regs)
  46. eq(
  47. { '', '', '', '' },
  48. map(function(r)
  49. return trim(call('getreg', r))
  50. end, regs)
  51. )
  52. call('ctxpop')
  53. eq(
  54. vals,
  55. map(function(r)
  56. return trim(call('getreg', r))
  57. end, regs)
  58. )
  59. map(function(r)
  60. call('setreg', r, {})
  61. end, regs)
  62. eq(
  63. { '', '', '', '' },
  64. map(function(r)
  65. return trim(call('getreg', r))
  66. end, regs)
  67. )
  68. call('ctxpop')
  69. eq(
  70. vals,
  71. map(function(r)
  72. return trim(call('getreg', r))
  73. end, regs)
  74. )
  75. end)
  76. it('saves and restores jumplist properly', function()
  77. command('edit ' .. fname1)
  78. feed('G')
  79. feed('gg')
  80. command('edit ' .. fname2)
  81. local jumplist = call('getjumplist')
  82. call('ctxpush')
  83. call('ctxpush', { 'jumps' })
  84. command('clearjumps')
  85. eq({ {}, 0 }, call('getjumplist'))
  86. call('ctxpop')
  87. eq(jumplist, call('getjumplist'))
  88. command('clearjumps')
  89. eq({ {}, 0 }, call('getjumplist'))
  90. call('ctxpop')
  91. eq(jumplist, call('getjumplist'))
  92. end)
  93. it('saves and restores buffer list properly', function()
  94. command('edit ' .. fname1)
  95. command('edit ' .. fname2)
  96. command('edit TEST')
  97. local bufs = call('map', call('getbufinfo'), 'v:val.name')
  98. call('ctxpush')
  99. call('ctxpush', { 'bufs' })
  100. command('%bwipeout')
  101. eq({ '' }, call('map', call('getbufinfo'), 'v:val.name'))
  102. call('ctxpop')
  103. eq({ '', unpack(bufs) }, call('map', call('getbufinfo'), 'v:val.name'))
  104. command('%bwipeout')
  105. eq({ '' }, call('map', call('getbufinfo'), 'v:val.name'))
  106. call('ctxpop')
  107. eq({ '', unpack(bufs) }, call('map', call('getbufinfo'), 'v:val.name'))
  108. end)
  109. it('saves and restores global variables properly', function()
  110. api.nvim_set_var('one', 1)
  111. api.nvim_set_var('Two', 2)
  112. api.nvim_set_var('THREE', 3)
  113. eq({ 1, 2, 3 }, eval('[g:one, g:Two, g:THREE]'))
  114. call('ctxpush')
  115. call('ctxpush', { 'gvars' })
  116. api.nvim_del_var('one')
  117. api.nvim_del_var('Two')
  118. api.nvim_del_var('THREE')
  119. eq('Vim:E121: Undefined variable: g:one', pcall_err(eval, 'g:one'))
  120. eq('Vim:E121: Undefined variable: g:Two', pcall_err(eval, 'g:Two'))
  121. eq('Vim:E121: Undefined variable: g:THREE', pcall_err(eval, 'g:THREE'))
  122. call('ctxpop')
  123. eq({ 1, 2, 3 }, eval('[g:one, g:Two, g:THREE]'))
  124. api.nvim_del_var('one')
  125. api.nvim_del_var('Two')
  126. api.nvim_del_var('THREE')
  127. eq('Vim:E121: Undefined variable: g:one', pcall_err(eval, 'g:one'))
  128. eq('Vim:E121: Undefined variable: g:Two', pcall_err(eval, 'g:Two'))
  129. eq('Vim:E121: Undefined variable: g:THREE', pcall_err(eval, 'g:THREE'))
  130. call('ctxpop')
  131. eq({ 1, 2, 3 }, eval('[g:one, g:Two, g:THREE]'))
  132. end)
  133. it('saves and restores script functions properly', function()
  134. source([[
  135. function s:greet(name)
  136. echom 'Hello, '.a:name.'!'
  137. endfunction
  138. function s:greet_all(name, ...)
  139. echom 'Hello, '.a:name.'!'
  140. for more in a:000
  141. echom 'Hello, '.more.'!'
  142. endfor
  143. endfunction
  144. function Greet(name)
  145. call call('s:greet', [a:name])
  146. endfunction
  147. function GreetAll(name, ...)
  148. call call('s:greet_all', extend([a:name], a:000))
  149. endfunction
  150. function SaveSFuncs()
  151. call ctxpush(['sfuncs'])
  152. endfunction
  153. function DeleteSFuncs()
  154. delfunction s:greet
  155. delfunction s:greet_all
  156. endfunction
  157. function RestoreFuncs()
  158. call ctxpop()
  159. endfunction
  160. ]])
  161. eq('Hello, World!', exec_capture([[call Greet('World')]]))
  162. eq(
  163. 'Hello, World!' .. '\nHello, One!' .. '\nHello, Two!' .. '\nHello, Three!',
  164. exec_capture([[call GreetAll('World', 'One', 'Two', 'Three')]])
  165. )
  166. call('SaveSFuncs')
  167. call('DeleteSFuncs')
  168. eq(
  169. 'function Greet, line 1: Vim(call):E117: Unknown function: s:greet',
  170. pcall_err(command, [[call Greet('World')]])
  171. )
  172. eq(
  173. 'function GreetAll, line 1: Vim(call):E117: Unknown function: s:greet_all',
  174. pcall_err(command, [[call GreetAll('World', 'One', 'Two', 'Three')]])
  175. )
  176. call('RestoreFuncs')
  177. eq('Hello, World!', exec_capture([[call Greet('World')]]))
  178. eq(
  179. 'Hello, World!' .. '\nHello, One!' .. '\nHello, Two!' .. '\nHello, Three!',
  180. exec_capture([[call GreetAll('World', 'One', 'Two', 'Three')]])
  181. )
  182. end)
  183. it('saves and restores functions properly', function()
  184. source([[
  185. function Greet(name)
  186. echom 'Hello, '.a:name.'!'
  187. endfunction
  188. function GreetAll(name, ...)
  189. echom 'Hello, '.a:name.'!'
  190. for more in a:000
  191. echom 'Hello, '.more.'!'
  192. endfor
  193. endfunction
  194. ]])
  195. eq('Hello, World!', exec_capture([[call Greet('World')]]))
  196. eq(
  197. 'Hello, World!' .. '\nHello, One!' .. '\nHello, Two!' .. '\nHello, Three!',
  198. exec_capture([[call GreetAll('World', 'One', 'Two', 'Three')]])
  199. )
  200. call('ctxpush', { 'funcs' })
  201. command('delfunction Greet')
  202. command('delfunction GreetAll')
  203. eq('Vim:E117: Unknown function: Greet', pcall_err(call, 'Greet', 'World'))
  204. eq(
  205. 'Vim:E117: Unknown function: GreetAll',
  206. pcall_err(call, 'GreetAll', 'World', 'One', 'Two', 'Three')
  207. )
  208. call('ctxpop')
  209. eq('Hello, World!', exec_capture([[call Greet('World')]]))
  210. eq(
  211. 'Hello, World!' .. '\nHello, One!' .. '\nHello, Two!' .. '\nHello, Three!',
  212. exec_capture([[call GreetAll('World', 'One', 'Two', 'Three')]])
  213. )
  214. end)
  215. it('errors out when context stack is empty', function()
  216. local err = 'Vim:Context stack is empty'
  217. eq(err, pcall_err(call, 'ctxpop'))
  218. eq(err, pcall_err(call, 'ctxpop'))
  219. call('ctxpush')
  220. call('ctxpush')
  221. call('ctxpop')
  222. call('ctxpop')
  223. eq(err, pcall_err(call, 'ctxpop'))
  224. end)
  225. end)
  226. describe('ctxsize()', function()
  227. it('returns context stack size', function()
  228. eq(0, call('ctxsize'))
  229. call('ctxpush')
  230. eq(1, call('ctxsize'))
  231. call('ctxpush')
  232. eq(2, call('ctxsize'))
  233. call('ctxpush')
  234. eq(3, call('ctxsize'))
  235. call('ctxpop')
  236. eq(2, call('ctxsize'))
  237. call('ctxpop')
  238. eq(1, call('ctxsize'))
  239. call('ctxpop')
  240. eq(0, call('ctxsize'))
  241. end)
  242. end)
  243. describe('ctxget()', function()
  244. it('errors out when index is out of bounds', function()
  245. eq(outofbounds, pcall_err(call, 'ctxget'))
  246. call('ctxpush')
  247. eq(outofbounds, pcall_err(call, 'ctxget', 1))
  248. call('ctxpop')
  249. eq(outofbounds, pcall_err(call, 'ctxget', 0))
  250. end)
  251. it('returns context dict at index in context stack', function()
  252. feed('i1<cr>2<cr>3<c-[>ddddddqahjklq')
  253. command('edit! ' .. fname1)
  254. feed('G')
  255. feed('gg')
  256. command('edit ' .. fname2)
  257. api.nvim_set_var('one', 1)
  258. api.nvim_set_var('Two', 2)
  259. api.nvim_set_var('THREE', 3)
  260. local with_regs = {
  261. ['regs'] = {
  262. { ['rt'] = 1, ['rc'] = { '1' }, ['n'] = 49, ['ru'] = true },
  263. { ['rt'] = 1, ['rc'] = { '2' }, ['n'] = 50 },
  264. { ['rt'] = 1, ['rc'] = { '3' }, ['n'] = 51 },
  265. { ['rc'] = { 'hjkl' }, ['n'] = 97 },
  266. },
  267. }
  268. local with_jumps = {
  269. ['jumps'] = eval((([[
  270. filter(map(add(
  271. getjumplist()[0], { 'bufnr': bufnr('%'), 'lnum': getcurpos()[1] }),
  272. 'filter(
  273. { "f": expand("#".v:val.bufnr.":p"), "l": v:val.lnum },
  274. { k, v -> k != "l" || v != 1 })'), '!empty(v:val.f)')
  275. ]]):gsub('\n', ''))),
  276. }
  277. local with_bufs = {
  278. ['bufs'] = eval([[
  279. filter(map(getbufinfo(), '{ "f": v:val.name }'), '!empty(v:val.f)')
  280. ]]),
  281. }
  282. local with_gvars = {
  283. ['gvars'] = { { 'one', 1 }, { 'Two', 2 }, { 'THREE', 3 } },
  284. }
  285. local with_all = {
  286. ['regs'] = with_regs['regs'],
  287. ['jumps'] = with_jumps['jumps'],
  288. ['bufs'] = with_bufs['bufs'],
  289. ['gvars'] = with_gvars['gvars'],
  290. }
  291. call('ctxpush')
  292. eq(with_all, parse_context(call('ctxget')))
  293. eq(with_all, parse_context(call('ctxget', 0)))
  294. call('ctxpush', { 'gvars' })
  295. eq(with_gvars, parse_context(call('ctxget')))
  296. eq(with_gvars, parse_context(call('ctxget', 0)))
  297. eq(with_all, parse_context(call('ctxget', 1)))
  298. call('ctxpush', { 'bufs' })
  299. eq(with_bufs, parse_context(call('ctxget')))
  300. eq(with_bufs, parse_context(call('ctxget', 0)))
  301. eq(with_gvars, parse_context(call('ctxget', 1)))
  302. eq(with_all, parse_context(call('ctxget', 2)))
  303. call('ctxpush', { 'jumps' })
  304. eq(with_jumps, parse_context(call('ctxget')))
  305. eq(with_jumps, parse_context(call('ctxget', 0)))
  306. eq(with_bufs, parse_context(call('ctxget', 1)))
  307. eq(with_gvars, parse_context(call('ctxget', 2)))
  308. eq(with_all, parse_context(call('ctxget', 3)))
  309. call('ctxpush', { 'regs' })
  310. eq(with_regs, parse_context(call('ctxget')))
  311. eq(with_regs, parse_context(call('ctxget', 0)))
  312. eq(with_jumps, parse_context(call('ctxget', 1)))
  313. eq(with_bufs, parse_context(call('ctxget', 2)))
  314. eq(with_gvars, parse_context(call('ctxget', 3)))
  315. eq(with_all, parse_context(call('ctxget', 4)))
  316. call('ctxpop')
  317. eq(with_jumps, parse_context(call('ctxget')))
  318. eq(with_jumps, parse_context(call('ctxget', 0)))
  319. eq(with_bufs, parse_context(call('ctxget', 1)))
  320. eq(with_gvars, parse_context(call('ctxget', 2)))
  321. eq(with_all, parse_context(call('ctxget', 3)))
  322. call('ctxpop')
  323. eq(with_bufs, parse_context(call('ctxget')))
  324. eq(with_bufs, parse_context(call('ctxget', 0)))
  325. eq(with_gvars, parse_context(call('ctxget', 1)))
  326. eq(with_all, parse_context(call('ctxget', 2)))
  327. call('ctxpop')
  328. eq(with_gvars, parse_context(call('ctxget')))
  329. eq(with_gvars, parse_context(call('ctxget', 0)))
  330. eq(with_all, parse_context(call('ctxget', 1)))
  331. call('ctxpop')
  332. eq(with_all, parse_context(call('ctxget')))
  333. eq(with_all, parse_context(call('ctxget', 0)))
  334. end)
  335. end)
  336. describe('ctxset()', function()
  337. it('errors out when index is out of bounds', function()
  338. eq(outofbounds, pcall_err(call, 'ctxset', { dummy = 1 }))
  339. call('ctxpush')
  340. eq(outofbounds, pcall_err(call, 'ctxset', { dummy = 1 }, 1))
  341. call('ctxpop')
  342. eq(outofbounds, pcall_err(call, 'ctxset', { dummy = 1 }, 0))
  343. end)
  344. it('errors when context dict is invalid', function()
  345. call('ctxpush')
  346. eq(
  347. 'Vim:E474: Failed to convert list to msgpack string buffer',
  348. pcall_err(call, 'ctxset', { regs = { {} }, jumps = { {} } })
  349. )
  350. end)
  351. it('sets context dict at index in context stack', function()
  352. api.nvim_set_var('one', 1)
  353. api.nvim_set_var('Two', 2)
  354. api.nvim_set_var('THREE', 3)
  355. call('ctxpush')
  356. local ctx1 = call('ctxget')
  357. api.nvim_set_var('one', 'a')
  358. api.nvim_set_var('Two', 'b')
  359. api.nvim_set_var('THREE', 'c')
  360. call('ctxpush')
  361. call('ctxpush')
  362. local ctx2 = call('ctxget')
  363. eq({ 'a', 'b', 'c' }, eval('[g:one, g:Two, g:THREE]'))
  364. call('ctxset', ctx1)
  365. call('ctxset', ctx2, 2)
  366. call('ctxpop')
  367. eq({ 1, 2, 3 }, eval('[g:one, g:Two, g:THREE]'))
  368. call('ctxpop')
  369. eq({ 'a', 'b', 'c' }, eval('[g:one, g:Two, g:THREE]'))
  370. api.nvim_set_var('one', 1.5)
  371. eq({ 1.5, 'b', 'c' }, eval('[g:one, g:Two, g:THREE]'))
  372. call('ctxpop')
  373. eq({ 'a', 'b', 'c' }, eval('[g:one, g:Two, g:THREE]'))
  374. end)
  375. end)
  376. end)