command_spec.lua 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606
  1. local helpers = require('test.functional.helpers')(after_each)
  2. local NIL = helpers.NIL
  3. local clear = helpers.clear
  4. local command = helpers.command
  5. local curbufmeths = helpers.curbufmeths
  6. local eq = helpers.eq
  7. local meths = helpers.meths
  8. local bufmeths = helpers.bufmeths
  9. local matches = helpers.matches
  10. local source = helpers.source
  11. local pcall_err = helpers.pcall_err
  12. local exec_lua = helpers.exec_lua
  13. local assert_alive = helpers.assert_alive
  14. local feed = helpers.feed
  15. local funcs = helpers.funcs
  16. describe('nvim_get_commands', function()
  17. local cmd_dict = { addr=NIL, bang=false, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='echo "Hello World"', name='Hello', nargs='1', preview=false, range=NIL, register=false, keepscript=false, script_id=0, }
  18. local cmd_dict2 = { addr=NIL, bang=false, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='pwd', name='Pwd', nargs='?', preview=false, range=NIL, register=false, keepscript=false, script_id=0, }
  19. before_each(clear)
  20. it('gets empty list if no commands were defined', function()
  21. eq({}, meths.get_commands({builtin=false}))
  22. end)
  23. it('validates input', function()
  24. eq('builtin=true not implemented', pcall_err(meths.get_commands,
  25. {builtin=true}))
  26. eq("Invalid key: 'foo'", pcall_err(meths.get_commands,
  27. {foo='blah'}))
  28. end)
  29. it('gets global user-defined commands', function()
  30. -- Define a command.
  31. command('command -nargs=1 Hello echo "Hello World"')
  32. eq({Hello=cmd_dict}, meths.get_commands({builtin=false}))
  33. -- Define another command.
  34. command('command -nargs=? Pwd pwd');
  35. eq({Hello=cmd_dict, Pwd=cmd_dict2}, meths.get_commands({builtin=false}))
  36. -- Delete a command.
  37. command('delcommand Pwd')
  38. eq({Hello=cmd_dict}, meths.get_commands({builtin=false}))
  39. end)
  40. it('gets buffer-local user-defined commands', function()
  41. -- Define a buffer-local command.
  42. command('command -buffer -nargs=1 Hello echo "Hello World"')
  43. eq({Hello=cmd_dict}, curbufmeths.get_commands({builtin=false}))
  44. -- Define another buffer-local command.
  45. command('command -buffer -nargs=? Pwd pwd')
  46. eq({Hello=cmd_dict, Pwd=cmd_dict2}, curbufmeths.get_commands({builtin=false}))
  47. -- Delete a command.
  48. command('delcommand Pwd')
  49. eq({Hello=cmd_dict}, curbufmeths.get_commands({builtin=false}))
  50. -- {builtin=true} always returns empty for buffer-local case.
  51. eq({}, curbufmeths.get_commands({builtin=true}))
  52. end)
  53. it('gets various command attributes', function()
  54. local cmd0 = { addr='arguments', bang=false, bar=false, complete='dir', complete_arg=NIL, count='10', definition='pwd <args>', name='TestCmd', nargs='1', preview=false, range='10', register=false, keepscript=false, script_id=0, }
  55. local cmd1 = { addr=NIL, bang=false, bar=false, complete='custom', complete_arg='ListUsers', count=NIL, definition='!finger <args>', name='Finger', nargs='+', preview=false, range=NIL, register=false, keepscript=false, script_id=1, }
  56. local cmd2 = { addr=NIL, bang=true, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253R2_foo(<q-args>)', name='Cmd2', nargs='*', preview=false, range=NIL, register=false, keepscript=false, script_id=2, }
  57. local cmd3 = { addr=NIL, bang=false, bar=true, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253R3_ohyeah()', name='Cmd3', nargs='0', preview=false, range=NIL, register=false, keepscript=false, script_id=3, }
  58. local cmd4 = { addr=NIL, bang=false, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253R4_just_great()', name='Cmd4', nargs='0', preview=false, range=NIL, register=true, keepscript=false, script_id=4, }
  59. source([[
  60. let s:foo = 1
  61. command -complete=custom,ListUsers -nargs=+ Finger !finger <args>
  62. ]])
  63. eq({Finger=cmd1}, meths.get_commands({builtin=false}))
  64. command('command -nargs=1 -complete=dir -addr=arguments -count=10 TestCmd pwd <args>')
  65. eq({Finger=cmd1, TestCmd=cmd0}, meths.get_commands({builtin=false}))
  66. source([[
  67. function! s:foo() abort
  68. endfunction
  69. command -bang -nargs=* Cmd2 call <SID>foo(<q-args>)
  70. ]])
  71. source([[
  72. function! s:ohyeah() abort
  73. endfunction
  74. command -bar -nargs=0 Cmd3 call <SID>ohyeah()
  75. ]])
  76. source([[
  77. function! s:just_great() abort
  78. endfunction
  79. command -register Cmd4 call <SID>just_great()
  80. ]])
  81. -- TODO(justinmk): Order is stable but undefined. Sort before return?
  82. eq({Cmd2=cmd2, Cmd3=cmd3, Cmd4=cmd4, Finger=cmd1, TestCmd=cmd0}, meths.get_commands({builtin=false}))
  83. end)
  84. end)
  85. describe('nvim_create_user_command', function()
  86. before_each(clear)
  87. it('works with strings', function()
  88. meths.create_user_command('SomeCommand', 'let g:command_fired = <args>', {nargs = 1})
  89. meths.command('SomeCommand 42')
  90. eq(42, meths.eval('g:command_fired'))
  91. end)
  92. it('works with Lua functions', function()
  93. exec_lua [[
  94. result = {}
  95. vim.api.nvim_create_user_command('CommandWithLuaCallback', function(opts)
  96. result = opts
  97. end, {
  98. nargs = "*",
  99. bang = true,
  100. count = 2,
  101. })
  102. ]]
  103. eq({
  104. args = [[this\ is a\ test]],
  105. fargs = {"this ", "is", "a test"},
  106. bang = false,
  107. line1 = 1,
  108. line2 = 1,
  109. mods = "",
  110. smods = {
  111. browse = false,
  112. confirm = false,
  113. emsg_silent = false,
  114. hide = false,
  115. horizontal = false,
  116. keepalt = false,
  117. keepjumps = false,
  118. keepmarks = false,
  119. keeppatterns = false,
  120. lockmarks = false,
  121. noautocmd = false,
  122. noswapfile = false,
  123. sandbox = false,
  124. silent = false,
  125. split = "",
  126. tab = -1,
  127. unsilent = false,
  128. verbose = -1,
  129. vertical = false,
  130. },
  131. range = 0,
  132. count = 2,
  133. reg = "",
  134. }, exec_lua [=[
  135. vim.api.nvim_command([[CommandWithLuaCallback this\ is a\ test]])
  136. return result
  137. ]=])
  138. eq({
  139. args = [[this includes\ a backslash: \\]],
  140. fargs = {"this", "includes a", "backslash:", "\\"},
  141. bang = false,
  142. line1 = 1,
  143. line2 = 1,
  144. mods = "",
  145. smods = {
  146. browse = false,
  147. confirm = false,
  148. emsg_silent = false,
  149. hide = false,
  150. horizontal = false,
  151. keepalt = false,
  152. keepjumps = false,
  153. keepmarks = false,
  154. keeppatterns = false,
  155. lockmarks = false,
  156. noautocmd = false,
  157. noswapfile = false,
  158. sandbox = false,
  159. silent = false,
  160. split = "",
  161. tab = -1,
  162. unsilent = false,
  163. verbose = -1,
  164. vertical = false,
  165. },
  166. range = 0,
  167. count = 2,
  168. reg = "",
  169. }, exec_lua [=[
  170. vim.api.nvim_command([[CommandWithLuaCallback this includes\ a backslash: \\]])
  171. return result
  172. ]=])
  173. eq({
  174. args = "a\\b",
  175. fargs = {"a\\b"},
  176. bang = false,
  177. line1 = 1,
  178. line2 = 1,
  179. mods = "",
  180. smods = {
  181. browse = false,
  182. confirm = false,
  183. emsg_silent = false,
  184. hide = false,
  185. horizontal = false,
  186. keepalt = false,
  187. keepjumps = false,
  188. keepmarks = false,
  189. keeppatterns = false,
  190. lockmarks = false,
  191. noautocmd = false,
  192. noswapfile = false,
  193. sandbox = false,
  194. silent = false,
  195. split = "",
  196. tab = -1,
  197. unsilent = false,
  198. verbose = -1,
  199. vertical = false,
  200. },
  201. range = 0,
  202. count = 2,
  203. reg = "",
  204. }, exec_lua [=[
  205. vim.api.nvim_command('CommandWithLuaCallback a\\b')
  206. return result
  207. ]=])
  208. eq({
  209. args = 'h\tey ',
  210. fargs = {[[h]], [[ey]]},
  211. bang = true,
  212. line1 = 10,
  213. line2 = 10,
  214. mods = "confirm unsilent botright horizontal",
  215. smods = {
  216. browse = false,
  217. confirm = true,
  218. emsg_silent = false,
  219. hide = false,
  220. horizontal = true,
  221. keepalt = false,
  222. keepjumps = false,
  223. keepmarks = false,
  224. keeppatterns = false,
  225. lockmarks = false,
  226. noautocmd = false,
  227. noswapfile = false,
  228. sandbox = false,
  229. silent = false,
  230. split = "botright",
  231. tab = -1,
  232. unsilent = true,
  233. verbose = -1,
  234. vertical = false,
  235. },
  236. range = 1,
  237. count = 10,
  238. reg = "",
  239. }, exec_lua [=[
  240. vim.api.nvim_command('unsilent horizontal botright confirm 10CommandWithLuaCallback! h\tey ')
  241. return result
  242. ]=])
  243. eq({
  244. args = "h",
  245. fargs = {"h"},
  246. bang = false,
  247. line1 = 1,
  248. line2 = 42,
  249. mods = "",
  250. smods = {
  251. browse = false,
  252. confirm = false,
  253. emsg_silent = false,
  254. hide = false,
  255. horizontal = false,
  256. keepalt = false,
  257. keepjumps = false,
  258. keepmarks = false,
  259. keeppatterns = false,
  260. lockmarks = false,
  261. noautocmd = false,
  262. noswapfile = false,
  263. sandbox = false,
  264. silent = false,
  265. split = "",
  266. tab = -1,
  267. unsilent = false,
  268. verbose = -1,
  269. vertical = false,
  270. },
  271. range = 1,
  272. count = 42,
  273. reg = "",
  274. }, exec_lua [[
  275. vim.api.nvim_command('CommandWithLuaCallback 42 h')
  276. return result
  277. ]])
  278. eq({
  279. args = "",
  280. fargs = {}, -- fargs works without args
  281. bang = false,
  282. line1 = 1,
  283. line2 = 1,
  284. mods = "",
  285. smods = {
  286. browse = false,
  287. confirm = false,
  288. emsg_silent = false,
  289. hide = false,
  290. horizontal = false,
  291. keepalt = false,
  292. keepjumps = false,
  293. keepmarks = false,
  294. keeppatterns = false,
  295. lockmarks = false,
  296. noautocmd = false,
  297. noswapfile = false,
  298. sandbox = false,
  299. silent = false,
  300. split = "",
  301. tab = -1,
  302. unsilent = false,
  303. verbose = -1,
  304. vertical = false,
  305. },
  306. range = 0,
  307. count = 2,
  308. reg = "",
  309. }, exec_lua [[
  310. vim.api.nvim_command('CommandWithLuaCallback')
  311. return result
  312. ]])
  313. -- f-args doesn't split when command nargs is 1 or "?"
  314. exec_lua [[
  315. result = {}
  316. vim.api.nvim_create_user_command('CommandWithOneOrNoArg', function(opts)
  317. result = opts
  318. end, {
  319. nargs = "?",
  320. bang = true,
  321. count = 2,
  322. })
  323. ]]
  324. eq({
  325. args = "hello I'm one argument",
  326. fargs = {"hello I'm one argument"}, -- Doesn't split args
  327. bang = false,
  328. line1 = 1,
  329. line2 = 1,
  330. mods = "",
  331. smods = {
  332. browse = false,
  333. confirm = false,
  334. emsg_silent = false,
  335. hide = false,
  336. horizontal = false,
  337. keepalt = false,
  338. keepjumps = false,
  339. keepmarks = false,
  340. keeppatterns = false,
  341. lockmarks = false,
  342. noautocmd = false,
  343. noswapfile = false,
  344. sandbox = false,
  345. silent = false,
  346. split = "",
  347. tab = -1,
  348. unsilent = false,
  349. verbose = -1,
  350. vertical = false,
  351. },
  352. range = 0,
  353. count = 2,
  354. reg = "",
  355. }, exec_lua [[
  356. vim.api.nvim_command('CommandWithOneOrNoArg hello I\'m one argument')
  357. return result
  358. ]])
  359. -- f-args is an empty table if no args were passed
  360. eq({
  361. args = "",
  362. fargs = {},
  363. bang = false,
  364. line1 = 1,
  365. line2 = 1,
  366. mods = "",
  367. smods = {
  368. browse = false,
  369. confirm = false,
  370. emsg_silent = false,
  371. hide = false,
  372. horizontal = false,
  373. keepalt = false,
  374. keepjumps = false,
  375. keepmarks = false,
  376. keeppatterns = false,
  377. lockmarks = false,
  378. noautocmd = false,
  379. noswapfile = false,
  380. sandbox = false,
  381. silent = false,
  382. split = "",
  383. tab = -1,
  384. unsilent = false,
  385. verbose = -1,
  386. vertical = false,
  387. },
  388. range = 0,
  389. count = 2,
  390. reg = "",
  391. }, exec_lua [[
  392. vim.api.nvim_command('CommandWithOneOrNoArg')
  393. return result
  394. ]])
  395. -- f-args is an empty table when the command nargs=0
  396. exec_lua [[
  397. result = {}
  398. vim.api.nvim_create_user_command('CommandWithNoArgs', function(opts)
  399. result = opts
  400. end, {
  401. nargs = 0,
  402. bang = true,
  403. count = 2,
  404. register = true,
  405. })
  406. ]]
  407. eq({
  408. args = "",
  409. fargs = {},
  410. bang = false,
  411. line1 = 1,
  412. line2 = 1,
  413. mods = "",
  414. smods = {
  415. browse = false,
  416. confirm = false,
  417. emsg_silent = false,
  418. hide = false,
  419. horizontal = false,
  420. keepalt = false,
  421. keepjumps = false,
  422. keepmarks = false,
  423. keeppatterns = false,
  424. lockmarks = false,
  425. noautocmd = false,
  426. noswapfile = false,
  427. sandbox = false,
  428. silent = false,
  429. split = "",
  430. tab = -1,
  431. unsilent = false,
  432. verbose = -1,
  433. vertical = false,
  434. },
  435. range = 0,
  436. count = 2,
  437. reg = "",
  438. }, exec_lua [[
  439. vim.cmd('CommandWithNoArgs')
  440. return result
  441. ]])
  442. -- register can be specified
  443. eq({
  444. args = "",
  445. fargs = {},
  446. bang = false,
  447. line1 = 1,
  448. line2 = 1,
  449. mods = "",
  450. smods = {
  451. browse = false,
  452. confirm = false,
  453. emsg_silent = false,
  454. hide = false,
  455. horizontal = false,
  456. keepalt = false,
  457. keepjumps = false,
  458. keepmarks = false,
  459. keeppatterns = false,
  460. lockmarks = false,
  461. noautocmd = false,
  462. noswapfile = false,
  463. sandbox = false,
  464. silent = false,
  465. split = "",
  466. tab = -1,
  467. unsilent = false,
  468. verbose = -1,
  469. vertical = false,
  470. },
  471. range = 0,
  472. count = 2,
  473. reg = "+",
  474. }, exec_lua [[
  475. vim.cmd('CommandWithNoArgs +')
  476. return result
  477. ]])
  478. end)
  479. it('can define buffer-local commands', function()
  480. local bufnr = meths.create_buf(false, false)
  481. bufmeths.create_user_command(bufnr, "Hello", "", {})
  482. matches("Not an editor command: Hello", pcall_err(meths.command, "Hello"))
  483. meths.set_current_buf(bufnr)
  484. meths.command("Hello")
  485. assert_alive()
  486. end)
  487. it('can use a Lua complete function', function()
  488. exec_lua [[
  489. vim.api.nvim_create_user_command('Test', '', {
  490. nargs = "*",
  491. complete = function(arg, cmdline, pos)
  492. local options = {"aaa", "bbb", "ccc"}
  493. local t = {}
  494. for _, v in ipairs(options) do
  495. if string.find(v, "^" .. arg) then
  496. table.insert(t, v)
  497. end
  498. end
  499. return t
  500. end,
  501. })
  502. ]]
  503. feed(':Test a<Tab>')
  504. eq('Test aaa', funcs.getcmdline())
  505. feed('<C-U>Test b<Tab>')
  506. eq('Test bbb', funcs.getcmdline())
  507. end)
  508. it('does not allow invalid command names', function()
  509. matches("'name' must begin with an uppercase letter", pcall_err(exec_lua, [[
  510. vim.api.nvim_create_user_command('test', 'echo "hi"', {})
  511. ]]))
  512. matches('Invalid command name', pcall_err(exec_lua, [[
  513. vim.api.nvim_create_user_command('t@', 'echo "hi"', {})
  514. ]]))
  515. matches('Invalid command name', pcall_err(exec_lua, [[
  516. vim.api.nvim_create_user_command('T@st', 'echo "hi"', {})
  517. ]]))
  518. matches('Invalid command name', pcall_err(exec_lua, [[
  519. vim.api.nvim_create_user_command('Test!', 'echo "hi"', {})
  520. ]]))
  521. matches('Invalid command name', pcall_err(exec_lua, [[
  522. vim.api.nvim_create_user_command('💩', 'echo "hi"', {})
  523. ]]))
  524. end)
  525. it('smods can be used with nvim_cmd', function()
  526. exec_lua[[
  527. vim.api.nvim_create_user_command('MyEcho', function(opts)
  528. vim.api.nvim_cmd({ cmd = 'echo', args = { '&verbose' }, mods = opts.smods }, {})
  529. end, {})
  530. ]]
  531. eq("3", meths.cmd({ cmd = 'MyEcho', mods = { verbose = 3 } }, { output = true }))
  532. eq(1, #meths.list_tabpages())
  533. exec_lua[[
  534. vim.api.nvim_create_user_command('MySplit', function(opts)
  535. vim.api.nvim_cmd({ cmd = 'split', mods = opts.smods }, {})
  536. end, {})
  537. ]]
  538. meths.cmd({ cmd = 'MySplit' }, {})
  539. eq(1, #meths.list_tabpages())
  540. eq(2, #meths.list_wins())
  541. meths.cmd({ cmd = 'MySplit', mods = { tab = 1 } }, {})
  542. eq(2, #meths.list_tabpages())
  543. eq(2, funcs.tabpagenr())
  544. meths.cmd({ cmd = 'MySplit', mods = { tab = 1 } }, {})
  545. eq(3, #meths.list_tabpages())
  546. eq(2, funcs.tabpagenr())
  547. meths.cmd({ cmd = 'MySplit', mods = { tab = 3 } }, {})
  548. eq(4, #meths.list_tabpages())
  549. eq(4, funcs.tabpagenr())
  550. meths.cmd({ cmd = 'MySplit', mods = { tab = 0 } }, {})
  551. eq(5, #meths.list_tabpages())
  552. eq(1, funcs.tabpagenr())
  553. end)
  554. end)
  555. describe('nvim_del_user_command', function()
  556. before_each(clear)
  557. it('can delete global commands', function()
  558. meths.create_user_command('Hello', 'echo "Hi"', {})
  559. meths.command('Hello')
  560. meths.del_user_command('Hello')
  561. matches("Not an editor command: Hello", pcall_err(meths.command, "Hello"))
  562. end)
  563. it('can delete buffer-local commands', function()
  564. bufmeths.create_user_command(0, 'Hello', 'echo "Hi"', {})
  565. meths.command('Hello')
  566. bufmeths.del_user_command(0, 'Hello')
  567. matches("Not an editor command: Hello", pcall_err(meths.command, "Hello"))
  568. end)
  569. end)