commands_spec.lua 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. -- Test suite for checking :lua* commands
  2. local t = require('test.testutil')
  3. local n = require('test.functional.testnvim')()
  4. local Screen = require('test.functional.ui.screen')
  5. local eq = t.eq
  6. local NIL = vim.NIL
  7. local eval = n.eval
  8. local feed = n.feed
  9. local clear = n.clear
  10. local matches = t.matches
  11. local api = n.api
  12. local exec_lua = n.exec_lua
  13. local exec_capture = n.exec_capture
  14. local fn = n.fn
  15. local source = n.source
  16. local dedent = t.dedent
  17. local command = n.command
  18. local exc_exec = n.exc_exec
  19. local pcall_err = t.pcall_err
  20. local write_file = t.write_file
  21. local remove_trace = t.remove_trace
  22. before_each(clear)
  23. describe(':lua', function()
  24. it('works', function()
  25. eq('', exec_capture('lua vim.api.nvim_buf_set_lines(1, 1, 2, false, {"TEST"})'))
  26. eq({ '', 'TEST' }, api.nvim_buf_get_lines(0, 0, 100, false))
  27. source([[
  28. lua << EOF
  29. vim.api.nvim_buf_set_lines(1, 1, 2, false, {"TSET"})
  30. EOF]])
  31. eq({ '', 'TSET' }, api.nvim_buf_get_lines(0, 0, 100, false))
  32. source([[
  33. lua << EOF
  34. vim.api.nvim_buf_set_lines(1, 1, 2, false, {"SETT"})]])
  35. eq({ '', 'SETT' }, api.nvim_buf_get_lines(0, 0, 100, false))
  36. source([[
  37. lua << EOF
  38. vim.api.nvim_buf_set_lines(1, 1, 2, false, {"ETTS"})
  39. vim.api.nvim_buf_set_lines(1, 2, 3, false, {"TTSE"})
  40. vim.api.nvim_buf_set_lines(1, 3, 4, false, {"STTE"})
  41. EOF]])
  42. eq({ '', 'ETTS', 'TTSE', 'STTE' }, api.nvim_buf_get_lines(0, 0, 100, false))
  43. matches(
  44. '.*Vim%(lua%):E15: Invalid expression: .*',
  45. pcall_err(
  46. source,
  47. [[
  48. lua << eval EOF
  49. {}
  50. EOF
  51. ]]
  52. )
  53. )
  54. end)
  55. it('throws catchable errors', function()
  56. eq('Vim(lua):E471: Argument required', pcall_err(command, 'lua'))
  57. eq(
  58. [[Vim(lua):E5107: Lua: [string ":lua"]:0: unexpected symbol near ')']],
  59. pcall_err(command, 'lua ()')
  60. )
  61. eq(
  62. [[Vim(lua):E5108: Lua: [string ":lua"]:1: TEST]],
  63. remove_trace(exc_exec('lua error("TEST")'))
  64. )
  65. eq(
  66. [[Vim(lua):E5108: Lua: [string ":lua"]:1: Invalid buffer id: -10]],
  67. remove_trace(exc_exec('lua vim.api.nvim_buf_set_lines(-10, 1, 1, false, {"TEST"})'))
  68. )
  69. eq({ '' }, api.nvim_buf_get_lines(0, 0, 100, false))
  70. end)
  71. it('works with NULL errors', function()
  72. eq([=[Vim(lua):E5108: Lua: [NULL]]=], exc_exec('lua error(nil)'))
  73. end)
  74. it('accepts embedded NLs without heredoc', function()
  75. -- Such code is usually used for `:execute 'lua' {generated_string}`:
  76. -- heredocs do not work in this case.
  77. command([[
  78. lua
  79. vim.api.nvim_buf_set_lines(1, 1, 2, false, {"ETTS"})
  80. vim.api.nvim_buf_set_lines(1, 2, 3, false, {"TTSE"})
  81. vim.api.nvim_buf_set_lines(1, 3, 4, false, {"STTE"})
  82. ]])
  83. eq({ '', 'ETTS', 'TTSE', 'STTE' }, api.nvim_buf_get_lines(0, 0, 100, false))
  84. end)
  85. it('preserves global and not preserves local variables', function()
  86. eq('', exec_capture('lua gvar = 42'))
  87. eq('', exec_capture('lua local lvar = 100500'))
  88. eq(NIL, fn.luaeval('lvar'))
  89. eq(42, fn.luaeval('gvar'))
  90. end)
  91. it('works with long strings', function()
  92. local s = ('x'):rep(100500)
  93. eq(
  94. 'Vim(lua):E5107: Lua: [string ":lua"]:0: unfinished string near \'<eof>\'',
  95. pcall_err(command, ('lua vim.api.nvim_buf_set_lines(1, 1, 2, false, {"%s})'):format(s))
  96. )
  97. eq({ '' }, api.nvim_buf_get_lines(0, 0, -1, false))
  98. eq('', exec_capture(('lua vim.api.nvim_buf_set_lines(1, 1, 2, false, {"%s"})'):format(s)))
  99. eq({ '', s }, api.nvim_buf_get_lines(0, 0, -1, false))
  100. end)
  101. it('can show multiline error messages', function()
  102. local screen = Screen.new(40, 10)
  103. screen:set_default_attr_ids({
  104. [1] = { bold = true, foreground = Screen.colors.Blue1 },
  105. [2] = { bold = true, reverse = true },
  106. [3] = { foreground = Screen.colors.Grey100, background = Screen.colors.Red },
  107. [4] = { bold = true, foreground = Screen.colors.SeaGreen4 },
  108. })
  109. feed(':lua error("fail\\nmuch error\\nsuch details")<cr>')
  110. screen:expect([[
  111. |
  112. {2: }|
  113. {3:E5108: Lua: [string ":lua"]:1: fail} |
  114. {3:much error} |
  115. {3:such details} |
  116. {3:stack traceback:} |
  117. {3: [C]: in function 'error'} |
  118. {3: [string ":lua"]:1: in main chunk}|
  119. |
  120. {4:Press ENTER or type command to continue}^ |
  121. ]])
  122. feed('<cr>')
  123. screen:expect {
  124. grid = [[
  125. ^ |
  126. {1:~ }|*8
  127. |
  128. ]],
  129. }
  130. eq(
  131. 'E5108: Lua: [string ":lua"]:1: fail\nmuch error\nsuch details',
  132. remove_trace(eval('v:errmsg'))
  133. )
  134. local status, err = pcall(command, 'lua error("some error\\nin a\\nAPI command")')
  135. local expected = 'Vim(lua):E5108: Lua: [string ":lua"]:1: some error\nin a\nAPI command'
  136. eq(false, status)
  137. eq(expected, string.sub(remove_trace(err), -string.len(expected)))
  138. feed(':messages<cr>')
  139. screen:expect([[
  140. |
  141. {2: }|
  142. {3:E5108: Lua: [string ":lua"]:1: fail} |
  143. {3:much error} |
  144. {3:such details} |
  145. {3:stack traceback:} |
  146. {3: [C]: in function 'error'} |
  147. {3: [string ":lua"]:1: in main chunk}|
  148. |
  149. {4:Press ENTER or type command to continue}^ |
  150. ]])
  151. end)
  152. it('prints result of =expr', function()
  153. exec_lua('x = 5')
  154. eq('5', exec_capture(':lua =x'))
  155. eq('5', exec_capture('=x'))
  156. exec_lua('x = "5"')
  157. eq('"5"', exec_capture(':lua =x'))
  158. eq('"5"', exec_capture('=x'))
  159. exec_lua("function x() return 'hello' end")
  160. eq('"hello"', exec_capture(':lua = x()'))
  161. exec_lua("function x() return 'hello ' end")
  162. eq('"hello "', exec_capture(':lua = x()'))
  163. exec_lua('x = {a = 1, b = 2}')
  164. eq('{\n a = 1,\n b = 2\n}', exec_capture(':lua =x'))
  165. exec_lua(function()
  166. function _G.x(success)
  167. if success then
  168. return true, 'Return value'
  169. else
  170. return false, nil, 'Error message'
  171. end
  172. end
  173. end)
  174. eq(
  175. dedent [[
  176. true
  177. "Return value"]],
  178. exec_capture(':lua =x(true)')
  179. )
  180. eq(
  181. dedent [[
  182. false
  183. nil
  184. "Error message"]],
  185. exec_capture('=x(false)')
  186. )
  187. end)
  188. it('with range', function()
  189. local screen = Screen.new(40, 10)
  190. api.nvim_buf_set_lines(0, 0, 0, 0, { 'nonsense', 'function x() print "hello" end', 'x()' })
  191. -- ":{range}lua" fails on invalid Lua code.
  192. eq(
  193. [[:{range}lua buffer=1: Vim(lua):E5107: Lua: ]]
  194. .. [[[string ":{range}lua buffer=1"]:0: '=' expected near '<eof>']],
  195. pcall_err(command, '1lua')
  196. )
  197. -- ":{range}lua" executes valid Lua code.
  198. feed(':2,3lua<CR>')
  199. screen:expect {
  200. grid = [[
  201. nonsense |
  202. function x() print "hello" end |
  203. x() |
  204. ^ |
  205. {1:~ }|*5
  206. hello |
  207. ]],
  208. attr_ids = {
  209. [1] = { foreground = Screen.colors.Blue, bold = true },
  210. },
  211. }
  212. -- ":{range}lua {code}" executes {code}, ignoring {range}
  213. eq('', exec_capture('1lua gvar = 42'))
  214. eq(42, fn.luaeval('gvar'))
  215. end)
  216. end)
  217. describe(':luado command', function()
  218. it('works', function()
  219. api.nvim_buf_set_lines(0, 0, 1, false, { 'ABC', 'def', 'gHi' })
  220. eq('', exec_capture('luado lines = (lines or {}) lines[#lines + 1] = {linenr, line}'))
  221. eq({ 'ABC', 'def', 'gHi' }, api.nvim_buf_get_lines(0, 0, -1, false))
  222. eq({ { 1, 'ABC' }, { 2, 'def' }, { 3, 'gHi' } }, fn.luaeval('lines'))
  223. -- Automatic transformation of numbers
  224. eq('', exec_capture('luado return linenr'))
  225. eq({ '1', '2', '3' }, api.nvim_buf_get_lines(0, 0, -1, false))
  226. eq('', exec_capture('luado return ("<%02x>"):format(line:byte())'))
  227. eq({ '<31>', '<32>', '<33>' }, api.nvim_buf_get_lines(0, 0, -1, false))
  228. end)
  229. it('stops processing lines when suddenly out of lines', function()
  230. api.nvim_buf_set_lines(0, 0, 1, false, { 'ABC', 'def', 'gHi' })
  231. eq('', exec_capture('2,$luado runs = ((runs or 0) + 1) vim.api.nvim_command("%d")'))
  232. eq({ '' }, api.nvim_buf_get_lines(0, 0, -1, false))
  233. eq(1, fn.luaeval('runs'))
  234. api.nvim_buf_set_lines(0, 0, -1, false, { 'one', 'two', 'three' })
  235. eq('', exec_capture('luado vim.api.nvim_command("%d")'))
  236. eq({ '' }, api.nvim_buf_get_lines(0, 0, -1, false))
  237. api.nvim_buf_set_lines(0, 0, -1, false, { 'one', 'two', 'three' })
  238. eq('', exec_capture('luado vim.api.nvim_command("1,2d")'))
  239. eq({ 'three' }, api.nvim_buf_get_lines(0, 0, -1, false))
  240. api.nvim_buf_set_lines(0, 0, -1, false, { 'one', 'two', 'three' })
  241. eq('', exec_capture('luado vim.api.nvim_command("2,3d"); return "REPLACED"'))
  242. eq({ 'REPLACED' }, api.nvim_buf_get_lines(0, 0, -1, false))
  243. api.nvim_buf_set_lines(0, 0, -1, false, { 'one', 'two', 'three' })
  244. eq('', exec_capture('2,3luado vim.api.nvim_command("1,2d"); return "REPLACED"'))
  245. eq({ 'three' }, api.nvim_buf_get_lines(0, 0, -1, false))
  246. end)
  247. it('fails on errors', function()
  248. eq(
  249. [[Vim(luado):E5109: Lua: [string ":luado"]:0: unexpected symbol near ')']],
  250. pcall_err(command, 'luado ()')
  251. )
  252. eq(
  253. [[Vim(luado):E5111: Lua: [string ":luado"]:0: attempt to perform arithmetic on global 'liness' (a nil value)]],
  254. pcall_err(command, 'luado return liness + 1')
  255. )
  256. end)
  257. it('works with NULL errors', function()
  258. eq([=[Vim(luado):E5111: Lua: [NULL]]=], exc_exec('luado error(nil)'))
  259. end)
  260. it('fails in sandbox when needed', function()
  261. api.nvim_buf_set_lines(0, 0, 1, false, { 'ABC', 'def', 'gHi' })
  262. eq(
  263. 'Vim(luado):E48: Not allowed in sandbox: sandbox luado runs = (runs or 0) + 1',
  264. pcall_err(command, 'sandbox luado runs = (runs or 0) + 1')
  265. )
  266. eq(NIL, fn.luaeval('runs'))
  267. end)
  268. it('works with long strings', function()
  269. local s = ('x'):rep(100500)
  270. eq(
  271. 'Vim(luado):E5109: Lua: [string ":luado"]:0: unfinished string near \'<eof>\'',
  272. pcall_err(command, ('luado return "%s'):format(s))
  273. )
  274. eq({ '' }, api.nvim_buf_get_lines(0, 0, -1, false))
  275. eq('', exec_capture(('luado return "%s"'):format(s)))
  276. eq({ s }, api.nvim_buf_get_lines(0, 0, -1, false))
  277. end)
  278. end)
  279. describe(':luafile', function()
  280. local fname = 'Xtest-functional-lua-commands-luafile'
  281. after_each(function()
  282. os.remove(fname)
  283. end)
  284. it('works', function()
  285. write_file(
  286. fname,
  287. [[
  288. vim.api.nvim_buf_set_lines(1, 1, 2, false, {"ETTS"})
  289. vim.api.nvim_buf_set_lines(1, 2, 3, false, {"TTSE"})
  290. vim.api.nvim_buf_set_lines(1, 3, 4, false, {"STTE"})
  291. ]]
  292. )
  293. eq('', exec_capture('luafile ' .. fname))
  294. eq({ '', 'ETTS', 'TTSE', 'STTE' }, api.nvim_buf_get_lines(0, 0, 100, false))
  295. end)
  296. it('correctly errors out', function()
  297. write_file(fname, '()')
  298. eq(
  299. ("Vim(luafile):E5112: Lua chunk: %s:1: unexpected symbol near ')'"):format(fname),
  300. exc_exec('luafile ' .. fname)
  301. )
  302. write_file(fname, 'vimm.api.nvim_buf_set_lines(1, 1, 2, false, {"ETTS"})')
  303. eq(
  304. ("Vim(luafile):E5113: Lua chunk: %s:1: attempt to index global 'vimm' (a nil value)"):format(
  305. fname
  306. ),
  307. remove_trace(exc_exec('luafile ' .. fname))
  308. )
  309. end)
  310. it('works with NULL errors', function()
  311. write_file(fname, 'error(nil)')
  312. eq([=[Vim(luafile):E5113: Lua chunk: [NULL]]=], exc_exec('luafile ' .. fname))
  313. end)
  314. end)