autocmd_spec.lua 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264
  1. local helpers = require('test.functional.helpers')(after_each)
  2. local clear = helpers.clear
  3. local command = helpers.command
  4. local eq = helpers.eq
  5. local neq = helpers.neq
  6. local exec_lua = helpers.exec_lua
  7. local matches = helpers.matches
  8. local meths = helpers.meths
  9. local source = helpers.source
  10. local pcall_err = helpers.pcall_err
  11. before_each(clear)
  12. describe('autocmd api', function()
  13. describe('nvim_create_autocmd', function()
  14. it('does not allow "command" and "callback" in the same autocmd', function()
  15. local ok, _ = pcall(meths.create_autocmd, "BufReadPost", {
  16. pattern = "*.py,*.pyi",
  17. command = "echo 'Should Have Errored",
  18. callback = "not allowed",
  19. })
  20. eq(false, ok)
  21. end)
  22. it('doesnt leak when you use ++once', function()
  23. eq(1, exec_lua([[
  24. local count = 0
  25. vim.api.nvim_create_autocmd("FileType", {
  26. pattern = "*",
  27. callback = function() count = count + 1 end,
  28. once = true
  29. })
  30. vim.cmd "set filetype=txt"
  31. vim.cmd "set filetype=python"
  32. return count
  33. ]], {}))
  34. end)
  35. it('allows passing buffer by key', function()
  36. meths.set_var('called', 0)
  37. meths.create_autocmd("FileType", {
  38. command = "let g:called = g:called + 1",
  39. buffer = 0,
  40. })
  41. meths.command "set filetype=txt"
  42. eq(1, meths.get_var('called'))
  43. -- switch to a new buffer
  44. meths.command "new"
  45. meths.command "set filetype=python"
  46. eq(1, meths.get_var('called'))
  47. end)
  48. it('does not allow passing buffer and patterns', function()
  49. local ok = pcall(meths.create_autocmd, "Filetype", {
  50. command = "let g:called = g:called + 1",
  51. buffer = 0,
  52. pattern = "*.py",
  53. })
  54. eq(false, ok)
  55. end)
  56. it('does not allow passing invalid buffers', function()
  57. local ok, msg = pcall(meths.create_autocmd, "Filetype", {
  58. command = "let g:called = g:called + 1",
  59. buffer = -1,
  60. })
  61. eq(false, ok)
  62. matches('Invalid buffer id', msg)
  63. end)
  64. it('errors on non-functions for cb', function()
  65. eq(false, pcall(exec_lua, [[
  66. vim.api.nvim_create_autocmd("BufReadPost", {
  67. pattern = "*.py,*.pyi",
  68. callback = 5,
  69. })
  70. ]]))
  71. end)
  72. it('allow passing pattern and <buffer> in same pattern', function()
  73. local ok = pcall(meths.create_autocmd, "BufReadPost", {
  74. pattern = "*.py,<buffer>",
  75. command = "echo 'Should Not Error'"
  76. })
  77. eq(true, ok)
  78. end)
  79. it('should handle multiple values as comma separated list', function()
  80. meths.create_autocmd("BufReadPost", {
  81. pattern = "*.py,*.pyi",
  82. command = "echo 'Should Not Have Errored'"
  83. })
  84. -- We should have one autocmd for *.py and one for *.pyi
  85. eq(2, #meths.get_autocmds { event = "BufReadPost" })
  86. end)
  87. it('should handle multiple values as array', function()
  88. meths.create_autocmd("BufReadPost", {
  89. pattern = { "*.py", "*.pyi", },
  90. command = "echo 'Should Not Have Errored'"
  91. })
  92. -- We should have one autocmd for *.py and one for *.pyi
  93. eq(2, #meths.get_autocmds { event = "BufReadPost" })
  94. end)
  95. describe('desc', function()
  96. it('can add description to one autocmd', function()
  97. local cmd = "echo 'Should Not Have Errored'"
  98. local desc = "Can show description"
  99. meths.create_autocmd("BufReadPost", {
  100. pattern = "*.py",
  101. command = cmd,
  102. desc = desc,
  103. })
  104. eq(desc, meths.get_autocmds { event = "BufReadPost" }[1].desc)
  105. eq(cmd, meths.get_autocmds { event = "BufReadPost" }[1].command)
  106. end)
  107. it('can add description to one autocmd that uses a callback', function()
  108. local desc = 'Can show description'
  109. meths.set_var('desc', desc)
  110. local result = exec_lua([[
  111. local callback = function() print 'Should Not Have Errored' end
  112. vim.api.nvim_create_autocmd("BufReadPost", {
  113. pattern = "*.py",
  114. callback = callback,
  115. desc = vim.g.desc,
  116. })
  117. local aus = vim.api.nvim_get_autocmds({ event = 'BufReadPost' })
  118. local first = aus[1]
  119. return {
  120. desc = first.desc,
  121. cbtype = type(first.callback)
  122. }
  123. ]])
  124. eq({ desc = desc, cbtype = 'function' }, result)
  125. end)
  126. it('will not add a description unless it was provided', function()
  127. exec_lua([[
  128. local callback = function() print 'Should Not Have Errored' end
  129. vim.api.nvim_create_autocmd("BufReadPost", {
  130. pattern = "*.py",
  131. callback = callback,
  132. })
  133. ]])
  134. eq(nil, meths.get_autocmds({ event = 'BufReadPost' })[1].desc)
  135. end)
  136. it('can add description to multiple autocmd', function()
  137. meths.create_autocmd("BufReadPost", {
  138. pattern = {"*.py", "*.pyi"},
  139. command = "echo 'Should Not Have Errored'",
  140. desc = "Can show description",
  141. })
  142. local aus = meths.get_autocmds { event = "BufReadPost" }
  143. eq(2, #aus)
  144. eq("Can show description", aus[1].desc)
  145. eq("Can show description", aus[2].desc)
  146. end)
  147. end)
  148. pending('script and verbose settings', function()
  149. it('marks API client', function()
  150. meths.create_autocmd("BufReadPost", {
  151. pattern = "*.py",
  152. command = "echo 'Should Not Have Errored'",
  153. desc = "Can show description",
  154. })
  155. local aus = meths.get_autocmds { event = "BufReadPost" }
  156. eq(1, #aus, aus)
  157. end)
  158. end)
  159. it('removes an autocommand if the callback returns true', function()
  160. meths.set_var("some_condition", false)
  161. exec_lua [[
  162. vim.api.nvim_create_autocmd("User", {
  163. pattern = "Test",
  164. desc = "A test autocommand",
  165. callback = function()
  166. return vim.g.some_condition
  167. end,
  168. })
  169. ]]
  170. meths.exec_autocmds("User", {pattern = "Test"})
  171. local aus = meths.get_autocmds({ event = 'User', pattern = 'Test' })
  172. local first = aus[1]
  173. eq(first.id, 1)
  174. meths.set_var("some_condition", true)
  175. meths.exec_autocmds("User", {pattern = "Test"})
  176. eq({}, meths.get_autocmds({event = "User", pattern = "Test"}))
  177. end)
  178. it('receives an args table', function()
  179. local res = exec_lua [[
  180. local group_id = vim.api.nvim_create_augroup("TestGroup", {})
  181. local autocmd_id = vim.api.nvim_create_autocmd("User", {
  182. group = "TestGroup",
  183. pattern = "Te*",
  184. callback = function(args)
  185. vim.g.autocmd_args = args
  186. end,
  187. })
  188. return {group_id, autocmd_id}
  189. ]]
  190. meths.exec_autocmds("User", {pattern = "Test pattern"})
  191. eq({
  192. id = res[2],
  193. group = res[1],
  194. event = "User",
  195. match = "Test pattern",
  196. file = "Test pattern",
  197. buf = 1,
  198. }, meths.get_var("autocmd_args"))
  199. -- Test without a group
  200. res = exec_lua [[
  201. local autocmd_id = vim.api.nvim_create_autocmd("User", {
  202. pattern = "*",
  203. callback = function(args)
  204. vim.g.autocmd_args = args
  205. end,
  206. })
  207. return {autocmd_id}
  208. ]]
  209. meths.exec_autocmds("User", {pattern = "some_pat"})
  210. eq({
  211. id = res[1],
  212. group = nil,
  213. event = "User",
  214. match = "some_pat",
  215. file = "some_pat",
  216. buf = 1,
  217. }, meths.get_var("autocmd_args"))
  218. end)
  219. it('can receive arbitrary data', function()
  220. local function test(data)
  221. eq(data, exec_lua([[
  222. local input = ...
  223. local output
  224. vim.api.nvim_create_autocmd("User", {
  225. pattern = "Test",
  226. callback = function(args)
  227. output = args.data
  228. end,
  229. })
  230. vim.api.nvim_exec_autocmds("User", {
  231. pattern = "Test",
  232. data = input,
  233. })
  234. return output
  235. ]], data))
  236. end
  237. test("Hello")
  238. test(42)
  239. test(true)
  240. test({ "list" })
  241. test({ foo = "bar" })
  242. end)
  243. end)
  244. describe('nvim_get_autocmds', function()
  245. describe('events', function()
  246. it('should return one autocmd when there is only one for an event', function()
  247. command [[au! InsertEnter]]
  248. command [[au InsertEnter * :echo "1"]]
  249. local aus = meths.get_autocmds { event = "InsertEnter" }
  250. eq(1, #aus)
  251. end)
  252. it('should return two autocmds when there are two for an event', function()
  253. command [[au! InsertEnter]]
  254. command [[au InsertEnter * :echo "1"]]
  255. command [[au InsertEnter * :echo "2"]]
  256. local aus = meths.get_autocmds { event = "InsertEnter" }
  257. eq(2, #aus)
  258. end)
  259. it('should return the same thing if you use string or list', function()
  260. command [[au! InsertEnter]]
  261. command [[au InsertEnter * :echo "1"]]
  262. command [[au InsertEnter * :echo "2"]]
  263. local string_aus = meths.get_autocmds { event = "InsertEnter" }
  264. local array_aus = meths.get_autocmds { event = { "InsertEnter" } }
  265. eq(string_aus, array_aus)
  266. end)
  267. it('should return two autocmds when there are two for an event', function()
  268. command [[au! InsertEnter]]
  269. command [[au! InsertLeave]]
  270. command [[au InsertEnter * :echo "1"]]
  271. command [[au InsertEnter * :echo "2"]]
  272. local aus = meths.get_autocmds { event = { "InsertEnter", "InsertLeave" } }
  273. eq(2, #aus)
  274. end)
  275. it('should return different IDs for different autocmds', function()
  276. command [[au! InsertEnter]]
  277. command [[au! InsertLeave]]
  278. command [[au InsertEnter * :echo "1"]]
  279. source [[
  280. call nvim_create_autocmd("InsertLeave", #{
  281. \ command: ":echo 2",
  282. \ })
  283. ]]
  284. local aus = meths.get_autocmds { event = { "InsertEnter", "InsertLeave" } }
  285. local first = aus[1]
  286. eq(first.id, nil)
  287. -- TODO: Maybe don't have this number, just assert it's not nil
  288. local second = aus[2]
  289. neq(second.id, nil)
  290. meths.del_autocmd(second.id)
  291. local new_aus = meths.get_autocmds { event = { "InsertEnter", "InsertLeave" } }
  292. eq(1, #new_aus)
  293. eq(first, new_aus[1])
  294. end)
  295. it('should return event name', function()
  296. command [[au! InsertEnter]]
  297. command [[au InsertEnter * :echo "1"]]
  298. local aus = meths.get_autocmds { event = "InsertEnter" }
  299. eq({ { buflocal = false, command = ':echo "1"', event = "InsertEnter", once = false, pattern = "*" } }, aus)
  300. end)
  301. it('should work with buffer numbers', function()
  302. command [[new]]
  303. command [[au! InsertEnter]]
  304. command [[au InsertEnter <buffer=1> :echo "1"]]
  305. command [[au InsertEnter <buffer=2> :echo "2"]]
  306. local aus = meths.get_autocmds { event = "InsertEnter", buffer = 0 }
  307. eq({{
  308. buffer = 2,
  309. buflocal = true,
  310. command = ':echo "2"',
  311. event = 'InsertEnter',
  312. once = false,
  313. pattern = '<buffer=2>',
  314. }}, aus)
  315. aus = meths.get_autocmds { event = "InsertEnter", buffer = 1 }
  316. eq({{
  317. buffer = 1,
  318. buflocal = true,
  319. command = ':echo "1"',
  320. event = "InsertEnter",
  321. once = false,
  322. pattern = "<buffer=1>",
  323. }}, aus)
  324. aus = meths.get_autocmds { event = "InsertEnter", buffer = { 1, 2 } }
  325. eq({{
  326. buffer = 1,
  327. buflocal = true,
  328. command = ':echo "1"',
  329. event = "InsertEnter",
  330. once = false,
  331. pattern = "<buffer=1>",
  332. }, {
  333. buffer = 2,
  334. buflocal = true,
  335. command = ':echo "2"',
  336. event = "InsertEnter",
  337. once = false,
  338. pattern = "<buffer=2>",
  339. }}, aus)
  340. eq("Invalid value for 'buffer': must be an integer or array of integers", pcall_err(meths.get_autocmds, { event = "InsertEnter", buffer = "foo" }))
  341. eq("Invalid value for 'buffer': must be an integer", pcall_err(meths.get_autocmds, { event = "InsertEnter", buffer = { "foo", 42 } }))
  342. eq("Invalid buffer id: 42", pcall_err(meths.get_autocmds, { event = "InsertEnter", buffer = { 42 } }))
  343. local bufs = {}
  344. for _ = 1, 257 do
  345. table.insert(bufs, meths.create_buf(true, false))
  346. end
  347. eq("Too many buffers. Please limit yourself to 256 or fewer", pcall_err(meths.get_autocmds, { event = "InsertEnter", buffer = bufs }))
  348. end)
  349. it('should return autocmds when group is specified by id', function()
  350. local auid = meths.create_augroup("nvim_test_augroup", { clear = true })
  351. meths.create_autocmd("FileType", { group = auid, command = 'echo "1"' })
  352. meths.create_autocmd("FileType", { group = auid, command = 'echo "2"' })
  353. local aus = meths.get_autocmds { group = auid }
  354. eq(2, #aus)
  355. local aus2 = meths.get_autocmds { group = auid, event = "InsertEnter" }
  356. eq(0, #aus2)
  357. end)
  358. it('should return autocmds when group is specified by name', function()
  359. local auname = "nvim_test_augroup"
  360. meths.create_augroup(auname, { clear = true })
  361. meths.create_autocmd("FileType", { group = auname, command = 'echo "1"' })
  362. meths.create_autocmd("FileType", { group = auname, command = 'echo "2"' })
  363. local aus = meths.get_autocmds { group = auname }
  364. eq(2, #aus)
  365. local aus2 = meths.get_autocmds { group = auname, event = "InsertEnter" }
  366. eq(0, #aus2)
  367. end)
  368. it('should respect nested', function()
  369. local bufs = exec_lua [[
  370. local count = 0
  371. vim.api.nvim_create_autocmd("BufNew", {
  372. once = false,
  373. nested = true,
  374. callback = function()
  375. count = count + 1
  376. if count > 5 then
  377. return true
  378. end
  379. vim.cmd(string.format("new README_%s.md", count))
  380. end
  381. })
  382. vim.cmd "new First.md"
  383. return vim.api.nvim_list_bufs()
  384. ]]
  385. -- 1 for the first buffer
  386. -- 2 for First.md
  387. -- 3-7 for the 5 we make in the autocmd
  388. eq({1, 2, 3, 4, 5, 6, 7}, bufs)
  389. end)
  390. it('can retrieve a callback from an autocmd', function()
  391. local content = 'I Am A Callback'
  392. meths.set_var('content', content)
  393. local result = exec_lua([[
  394. local cb = function() return vim.g.content end
  395. vim.api.nvim_create_autocmd("User", {
  396. pattern = "TestTrigger",
  397. desc = "A test autocommand with a callback",
  398. callback = cb,
  399. })
  400. local aus = vim.api.nvim_get_autocmds({ event = 'User', pattern = 'TestTrigger'})
  401. local first = aus[1]
  402. return {
  403. cb = {
  404. type = type(first.callback),
  405. can_retrieve = first.callback() == vim.g.content
  406. }
  407. }
  408. ]])
  409. eq("function", result.cb.type)
  410. eq(true, result.cb.can_retrieve)
  411. end)
  412. it('will return an empty string as the command for an autocmd that uses a callback', function()
  413. local result = exec_lua([[
  414. local callback = function() print 'I Am A Callback' end
  415. vim.api.nvim_create_autocmd("BufWritePost", {
  416. pattern = "*.py",
  417. callback = callback,
  418. })
  419. local aus = vim.api.nvim_get_autocmds({ event = 'BufWritePost' })
  420. local first = aus[1]
  421. return {
  422. command = first.command,
  423. cbtype = type(first.callback)
  424. }
  425. ]])
  426. eq({ command = "", cbtype = 'function' }, result)
  427. end)
  428. end)
  429. describe('groups', function()
  430. before_each(function()
  431. command [[au! InsertEnter]]
  432. command [[au InsertEnter * :echo "No Group"]]
  433. command [[augroup GroupOne]]
  434. command [[ au InsertEnter * :echo "GroupOne:1"]]
  435. command [[augroup END]]
  436. command [[augroup GroupTwo]]
  437. command [[ au InsertEnter * :echo "GroupTwo:2"]]
  438. command [[ au InsertEnter * :echo "GroupTwo:3"]]
  439. command [[augroup END]]
  440. end)
  441. it('should return all groups if no group is specified', function()
  442. local aus = meths.get_autocmds { event = "InsertEnter" }
  443. if #aus ~= 4 then
  444. eq({}, aus)
  445. end
  446. eq(4, #aus)
  447. end)
  448. it('should return only the group specified', function()
  449. local aus = meths.get_autocmds {
  450. event = "InsertEnter",
  451. group = "GroupOne",
  452. }
  453. eq(1, #aus)
  454. eq([[:echo "GroupOne:1"]], aus[1].command)
  455. eq("GroupOne", aus[1].group_name)
  456. end)
  457. it('should return only the group specified, multiple values', function()
  458. local aus = meths.get_autocmds {
  459. event = "InsertEnter",
  460. group = "GroupTwo",
  461. }
  462. eq(2, #aus)
  463. eq([[:echo "GroupTwo:2"]], aus[1].command)
  464. eq("GroupTwo", aus[1].group_name)
  465. eq([[:echo "GroupTwo:3"]], aus[2].command)
  466. eq("GroupTwo", aus[2].group_name)
  467. end)
  468. end)
  469. describe('groups: 2', function()
  470. it('raises error for undefined augroup name', function()
  471. local success, code = unpack(meths.exec_lua([[
  472. return {pcall(function()
  473. vim.api.nvim_create_autocmd("FileType", {
  474. pattern = "*",
  475. group = "NotDefined",
  476. command = "echo 'hello'",
  477. })
  478. end)}
  479. ]], {}))
  480. eq(false, success)
  481. matches('invalid augroup: NotDefined', code)
  482. end)
  483. it('raises error for undefined augroup id', function()
  484. local success, code = unpack(meths.exec_lua([[
  485. return {pcall(function()
  486. -- Make sure the augroup is deleted
  487. vim.api.nvim_del_augroup_by_id(1)
  488. vim.api.nvim_create_autocmd("FileType", {
  489. pattern = "*",
  490. group = 1,
  491. command = "echo 'hello'",
  492. })
  493. end)}
  494. ]], {}))
  495. eq(false, success)
  496. matches('invalid augroup: 1', code)
  497. end)
  498. it('raises error for invalid group type', function()
  499. local success, code = unpack(meths.exec_lua([[
  500. return {pcall(function()
  501. vim.api.nvim_create_autocmd("FileType", {
  502. pattern = "*",
  503. group = true,
  504. command = "echo 'hello'",
  505. })
  506. end)}
  507. ]], {}))
  508. eq(false, success)
  509. matches("'group' must be a string or an integer", code)
  510. end)
  511. it('raises error for invalid pattern array', function()
  512. local success, code = unpack(meths.exec_lua([[
  513. return {pcall(function()
  514. vim.api.nvim_create_autocmd("FileType", {
  515. pattern = {{}},
  516. command = "echo 'hello'",
  517. })
  518. end)}
  519. ]], {}))
  520. eq(false, success)
  521. matches("All entries in 'pattern' must be strings", code)
  522. end)
  523. end)
  524. describe('patterns', function()
  525. before_each(function()
  526. command [[au! InsertEnter]]
  527. command [[au InsertEnter * :echo "No Group"]]
  528. command [[au InsertEnter *.one :echo "GroupOne:1"]]
  529. command [[au InsertEnter *.two :echo "GroupTwo:2"]]
  530. command [[au InsertEnter *.two :echo "GroupTwo:3"]]
  531. command [[au InsertEnter <buffer> :echo "Buffer"]]
  532. end)
  533. it('should should return for literal match', function()
  534. local aus = meths.get_autocmds {
  535. event = "InsertEnter",
  536. pattern = "*"
  537. }
  538. eq(1, #aus)
  539. eq([[:echo "No Group"]], aus[1].command)
  540. end)
  541. it('should return for multiple matches', function()
  542. -- vim.api.nvim_get_autocmds
  543. local aus = meths.get_autocmds {
  544. event = "InsertEnter",
  545. pattern = { "*.one", "*.two" },
  546. }
  547. eq(3, #aus)
  548. eq([[:echo "GroupOne:1"]], aus[1].command)
  549. eq([[:echo "GroupTwo:2"]], aus[2].command)
  550. eq([[:echo "GroupTwo:3"]], aus[3].command)
  551. end)
  552. it('should work for buffer autocmds', function()
  553. local normalized_aus = meths.get_autocmds {
  554. event = "InsertEnter",
  555. pattern = "<buffer=1>",
  556. }
  557. local raw_aus = meths.get_autocmds {
  558. event = "InsertEnter",
  559. pattern = "<buffer>",
  560. }
  561. local zero_aus = meths.get_autocmds {
  562. event = "InsertEnter",
  563. pattern = "<buffer=0>",
  564. }
  565. eq(normalized_aus, raw_aus)
  566. eq(normalized_aus, zero_aus)
  567. eq([[:echo "Buffer"]], normalized_aus[1].command)
  568. end)
  569. end)
  570. end)
  571. describe('nvim_exec_autocmds', function()
  572. it("can trigger builtin autocmds", function()
  573. meths.set_var("autocmd_executed", false)
  574. meths.create_autocmd("BufReadPost", {
  575. pattern = "*",
  576. command = "let g:autocmd_executed = v:true",
  577. })
  578. eq(false, meths.get_var("autocmd_executed"))
  579. meths.exec_autocmds("BufReadPost", {})
  580. eq(true, meths.get_var("autocmd_executed"))
  581. end)
  582. it("can trigger multiple patterns", function()
  583. meths.set_var("autocmd_executed", 0)
  584. meths.create_autocmd("BufReadPost", {
  585. pattern = "*",
  586. command = "let g:autocmd_executed += 1",
  587. })
  588. meths.exec_autocmds("BufReadPost", { pattern = { "*.lua", "*.vim" } })
  589. eq(2, meths.get_var("autocmd_executed"))
  590. meths.create_autocmd("BufReadPre", {
  591. pattern = { "bar", "foo" },
  592. command = "let g:autocmd_executed += 10",
  593. })
  594. meths.exec_autocmds("BufReadPre", { pattern = { "foo", "bar", "baz", "frederick" }})
  595. eq(22, meths.get_var("autocmd_executed"))
  596. end)
  597. it("can pass the buffer", function()
  598. meths.set_var("buffer_executed", -1)
  599. eq(-1, meths.get_var("buffer_executed"))
  600. meths.create_autocmd("BufLeave", {
  601. pattern = "*",
  602. command = 'let g:buffer_executed = +expand("<abuf>")',
  603. })
  604. -- Doesn't execute for other non-matching events
  605. meths.exec_autocmds("CursorHold", { buffer = 1 })
  606. eq(-1, meths.get_var("buffer_executed"))
  607. meths.exec_autocmds("BufLeave", { buffer = 1 })
  608. eq(1, meths.get_var("buffer_executed"))
  609. end)
  610. it("can pass the filename, pattern match", function()
  611. meths.set_var("filename_executed", 'none')
  612. eq('none', meths.get_var("filename_executed"))
  613. meths.create_autocmd("BufEnter", {
  614. pattern = "*.py",
  615. command = 'let g:filename_executed = expand("<afile>")',
  616. })
  617. -- Doesn't execute for other non-matching events
  618. meths.exec_autocmds("CursorHold", { buffer = 1 })
  619. eq('none', meths.get_var("filename_executed"))
  620. meths.command('edit __init__.py')
  621. eq('__init__.py', meths.get_var("filename_executed"))
  622. end)
  623. it('cannot pass buf and fname', function()
  624. local ok = pcall(meths.exec_autocmds, "BufReadPre", { pattern = "literally_cannot_error.rs", buffer = 1 })
  625. eq(false, ok)
  626. end)
  627. it("can pass the filename, exact match", function()
  628. meths.set_var("filename_executed", 'none')
  629. eq('none', meths.get_var("filename_executed"))
  630. meths.command('edit other_file.txt')
  631. meths.command('edit __init__.py')
  632. eq('none', meths.get_var("filename_executed"))
  633. meths.create_autocmd("CursorHoldI", {
  634. pattern = "__init__.py",
  635. command = 'let g:filename_executed = expand("<afile>")',
  636. })
  637. -- Doesn't execute for other non-matching events
  638. meths.exec_autocmds("CursorHoldI", { buffer = 1 })
  639. eq('none', meths.get_var("filename_executed"))
  640. meths.exec_autocmds("CursorHoldI", { buffer = meths.get_current_buf() })
  641. eq('__init__.py', meths.get_var("filename_executed"))
  642. -- Reset filename
  643. meths.set_var("filename_executed", 'none')
  644. meths.exec_autocmds("CursorHoldI", { pattern = '__init__.py' })
  645. eq('__init__.py', meths.get_var("filename_executed"))
  646. end)
  647. it("works with user autocmds", function()
  648. meths.set_var("matched", 'none')
  649. meths.create_autocmd("User", {
  650. pattern = "TestCommand",
  651. command = 'let g:matched = "matched"'
  652. })
  653. meths.exec_autocmds("User", { pattern = "OtherCommand" })
  654. eq('none', meths.get_var('matched'))
  655. meths.exec_autocmds("User", { pattern = "TestCommand" })
  656. eq('matched', meths.get_var('matched'))
  657. end)
  658. it('can pass group by id', function()
  659. meths.set_var("group_executed", false)
  660. local auid = meths.create_augroup("nvim_test_augroup", { clear = true })
  661. meths.create_autocmd("FileType", {
  662. group = auid,
  663. command = 'let g:group_executed = v:true',
  664. })
  665. eq(false, meths.get_var("group_executed"))
  666. meths.exec_autocmds("FileType", { group = auid })
  667. eq(true, meths.get_var("group_executed"))
  668. end)
  669. it('can pass group by name', function()
  670. meths.set_var("group_executed", false)
  671. local auname = "nvim_test_augroup"
  672. meths.create_augroup(auname, { clear = true })
  673. meths.create_autocmd("FileType", {
  674. group = auname,
  675. command = 'let g:group_executed = v:true',
  676. })
  677. eq(false, meths.get_var("group_executed"))
  678. meths.exec_autocmds("FileType", { group = auname })
  679. eq(true, meths.get_var("group_executed"))
  680. end)
  681. end)
  682. describe('nvim_create_augroup', function()
  683. before_each(function()
  684. clear()
  685. meths.set_var('executed', 0)
  686. end)
  687. local make_counting_autocmd = function(opts)
  688. opts = opts or {}
  689. local resulting = {
  690. pattern = "*",
  691. command = "let g:executed = g:executed + 1",
  692. }
  693. resulting.group = opts.group
  694. resulting.once = opts.once
  695. meths.create_autocmd("FileType", resulting)
  696. end
  697. local set_ft = function(ft)
  698. ft = ft or "txt"
  699. source(string.format("set filetype=%s", ft))
  700. end
  701. local get_executed_count = function()
  702. return meths.get_var('executed')
  703. end
  704. it('can be added in a group', function()
  705. local augroup = "TestGroup"
  706. meths.create_augroup(augroup, { clear = true })
  707. make_counting_autocmd { group = augroup }
  708. set_ft("txt")
  709. set_ft("python")
  710. eq(2, get_executed_count())
  711. end)
  712. it('works getting called multiple times', function()
  713. make_counting_autocmd()
  714. set_ft()
  715. set_ft()
  716. set_ft()
  717. eq(3, get_executed_count())
  718. end)
  719. it('handles ++once', function()
  720. make_counting_autocmd {once = true}
  721. set_ft('txt')
  722. set_ft('help')
  723. set_ft('txt')
  724. set_ft('help')
  725. eq(1, get_executed_count())
  726. end)
  727. it('errors on unexpected keys', function()
  728. local success, code = pcall(meths.create_autocmd, "FileType", {
  729. pattern = "*",
  730. not_a_valid_key = "NotDefined",
  731. })
  732. eq(false, success)
  733. matches('not_a_valid_key', code)
  734. end)
  735. it('can execute simple callback', function()
  736. exec_lua([[
  737. vim.g.executed = false
  738. vim.api.nvim_create_autocmd("FileType", {
  739. pattern = "*",
  740. callback = function() vim.g.executed = true end,
  741. })
  742. ]], {})
  743. eq(true, exec_lua([[
  744. vim.cmd "set filetype=txt"
  745. return vim.g.executed
  746. ]], {}))
  747. end)
  748. it('calls multiple lua callbacks for the same autocmd execution', function()
  749. eq(4, exec_lua([[
  750. local count = 0
  751. local counter = function()
  752. count = count + 1
  753. end
  754. vim.api.nvim_create_autocmd("FileType", {
  755. pattern = "*",
  756. callback = counter,
  757. })
  758. vim.api.nvim_create_autocmd("FileType", {
  759. pattern = "*",
  760. callback = counter,
  761. })
  762. vim.cmd "set filetype=txt"
  763. vim.cmd "set filetype=txt"
  764. return count
  765. ]], {}))
  766. end)
  767. it('properly releases functions with ++once', function()
  768. exec_lua([[
  769. WeakTable = setmetatable({}, { __mode = "k" })
  770. OnceCount = 0
  771. MyVal = {}
  772. WeakTable[MyVal] = true
  773. vim.api.nvim_create_autocmd("FileType", {
  774. pattern = "*",
  775. callback = function()
  776. OnceCount = OnceCount + 1
  777. MyVal = {}
  778. end,
  779. once = true
  780. })
  781. ]])
  782. command [[set filetype=txt]]
  783. eq(1, exec_lua([[return OnceCount]], {}))
  784. exec_lua([[collectgarbage()]], {})
  785. command [[set filetype=txt]]
  786. eq(1, exec_lua([[return OnceCount]], {}))
  787. eq(0, exec_lua([[
  788. local count = 0
  789. for _ in pairs(WeakTable) do
  790. count = count + 1
  791. end
  792. return count
  793. ]]), "Should have no keys remaining")
  794. end)
  795. it('groups can be cleared', function()
  796. local augroup = "TestGroup"
  797. meths.create_augroup(augroup, { clear = true })
  798. meths.create_autocmd("FileType", {
  799. group = augroup,
  800. command = "let g:executed = g:executed + 1"
  801. })
  802. set_ft("txt")
  803. set_ft("txt")
  804. eq(2, get_executed_count(), "should only count twice")
  805. meths.create_augroup(augroup, { clear = true })
  806. eq({}, meths.get_autocmds { group = augroup })
  807. set_ft("txt")
  808. set_ft("txt")
  809. eq(2, get_executed_count(), "No additional counts")
  810. end)
  811. it('can delete non-existent groups with pcall', function()
  812. eq(false, exec_lua[[return pcall(vim.api.nvim_del_augroup_by_name, 'noexist')]])
  813. eq('Vim:E367: No such group: "noexist"', pcall_err(meths.del_augroup_by_name, 'noexist'))
  814. eq(false, exec_lua[[return pcall(vim.api.nvim_del_augroup_by_id, -12342)]])
  815. eq('Vim:E367: No such group: "--Deleted--"', pcall_err(meths.del_augroup_by_id, -12312))
  816. end)
  817. it('groups work with once', function()
  818. local augroup = "TestGroup"
  819. meths.create_augroup(augroup, { clear = true })
  820. make_counting_autocmd { group = augroup, once = true }
  821. set_ft("txt")
  822. set_ft("python")
  823. eq(1, get_executed_count())
  824. end)
  825. it('autocmds can be registered multiple times.', function()
  826. local augroup = "TestGroup"
  827. meths.create_augroup(augroup, { clear = true })
  828. make_counting_autocmd { group = augroup, once = false }
  829. make_counting_autocmd { group = augroup, once = false }
  830. make_counting_autocmd { group = augroup, once = false }
  831. set_ft("txt")
  832. set_ft("python")
  833. eq(3 * 2, get_executed_count())
  834. end)
  835. it('can be deleted', function()
  836. local augroup = "WillBeDeleted"
  837. meths.create_augroup(augroup, { clear = true })
  838. meths.create_autocmd({"Filetype"}, {
  839. pattern = "*",
  840. command = "echo 'does not matter'",
  841. })
  842. -- Clears the augroup from before, which erases the autocmd
  843. meths.create_augroup(augroup, { clear = true })
  844. local result = #meths.get_autocmds { group = augroup }
  845. eq(0, result)
  846. end)
  847. it('can be used for buffer local autocmds', function()
  848. local augroup = "WillBeDeleted"
  849. meths.set_var("value_set", false)
  850. meths.create_augroup(augroup, { clear = true })
  851. meths.create_autocmd("Filetype", {
  852. pattern = "<buffer>",
  853. command = "let g:value_set = v:true",
  854. })
  855. command "new"
  856. command "set filetype=python"
  857. eq(false, meths.get_var("value_set"))
  858. end)
  859. it('can accept vimscript functions', function()
  860. source [[
  861. let g:vimscript_executed = 0
  862. function! MyVimscriptFunction() abort
  863. let g:vimscript_executed = g:vimscript_executed + 1
  864. endfunction
  865. call nvim_create_autocmd("FileType", #{
  866. \ pattern: ["python", "javascript"],
  867. \ callback: "MyVimscriptFunction",
  868. \ })
  869. set filetype=txt
  870. set filetype=python
  871. set filetype=txt
  872. set filetype=javascript
  873. set filetype=txt
  874. ]]
  875. eq(2, meths.get_var("vimscript_executed"))
  876. end)
  877. end)
  878. describe('augroup!', function()
  879. it('legacy: should clear and not return any autocmds for delete groups', function()
  880. command('augroup TEMP_A')
  881. command(' autocmd! BufReadPost *.py :echo "Hello"')
  882. command('augroup END')
  883. command('augroup! TEMP_A')
  884. eq(false, pcall(meths.get_autocmds, { group = 'TEMP_A' }))
  885. -- For some reason, augroup! doesn't clear the autocmds themselves, which is just wild
  886. -- but we managed to keep this behavior.
  887. eq(1, #meths.get_autocmds { event = 'BufReadPost' })
  888. end)
  889. it('legacy: remove augroups that have no autocmds', function()
  890. command('augroup TEMP_AB')
  891. command('augroup END')
  892. command('augroup! TEMP_AB')
  893. eq(false, pcall(meths.get_autocmds, { group = 'TEMP_AB' }))
  894. eq(0, #meths.get_autocmds { event = 'BufReadPost' })
  895. end)
  896. it('legacy: multiple remove and add augroup', function()
  897. command('augroup TEMP_ABC')
  898. command(' au!')
  899. command(' autocmd BufReadPost *.py echo "Hello"')
  900. command('augroup END')
  901. command('augroup! TEMP_ABC')
  902. -- Should still have one autocmd :'(
  903. local aus = meths.get_autocmds { event = 'BufReadPost' }
  904. eq(1, #aus, aus)
  905. command('augroup TEMP_ABC')
  906. command(' au!')
  907. command(' autocmd BufReadPost *.py echo "Hello"')
  908. command('augroup END')
  909. -- Should now have two autocmds :'(
  910. aus = meths.get_autocmds { event = 'BufReadPost' }
  911. eq(2, #aus, aus)
  912. command('augroup! TEMP_ABC')
  913. eq(false, pcall(meths.get_autocmds, { group = 'TEMP_ABC' }))
  914. eq(2, #meths.get_autocmds { event = 'BufReadPost' })
  915. end)
  916. it('api: should clear and not return any autocmds for delete groups by id', function()
  917. command('augroup TEMP_ABCD')
  918. command('autocmd! BufReadPost *.py :echo "Hello"')
  919. command('augroup END')
  920. local augroup_id = meths.create_augroup("TEMP_ABCD", { clear = false })
  921. meths.del_augroup_by_id(augroup_id)
  922. -- For good reason, we kill all the autocmds from del_augroup,
  923. -- so now this works as expected
  924. eq(false, pcall(meths.get_autocmds, { group = 'TEMP_ABCD' }))
  925. eq(0, #meths.get_autocmds { event = 'BufReadPost' })
  926. end)
  927. it('api: should clear and not return any autocmds for delete groups by name', function()
  928. command('augroup TEMP_ABCDE')
  929. command('autocmd! BufReadPost *.py :echo "Hello"')
  930. command('augroup END')
  931. meths.del_augroup_by_name("TEMP_ABCDE")
  932. -- For good reason, we kill all the autocmds from del_augroup,
  933. -- so now this works as expected
  934. eq(false, pcall(meths.get_autocmds, { group = 'TEMP_ABCDE' }))
  935. eq(0, #meths.get_autocmds { event = 'BufReadPost' })
  936. end)
  937. end)
  938. describe('nvim_clear_autocmds', function()
  939. it('should clear based on event + pattern', function()
  940. command('autocmd InsertEnter *.py :echo "Python can be cool sometimes"')
  941. command('autocmd InsertEnter *.txt :echo "Text Files Are Cool"')
  942. local search = { event = "InsertEnter", pattern = "*.txt" }
  943. local before_delete = meths.get_autocmds(search)
  944. eq(1, #before_delete)
  945. local before_delete_all = meths.get_autocmds { event = search.event }
  946. eq(2, #before_delete_all)
  947. meths.clear_autocmds(search)
  948. local after_delete = meths.get_autocmds(search)
  949. eq(0, #after_delete)
  950. local after_delete_all = meths.get_autocmds { event = search.event }
  951. eq(1, #after_delete_all)
  952. end)
  953. it('should clear based on event', function()
  954. command('autocmd InsertEnter *.py :echo "Python can be cool sometimes"')
  955. command('autocmd InsertEnter *.txt :echo "Text Files Are Cool"')
  956. local search = { event = "InsertEnter"}
  957. local before_delete = meths.get_autocmds(search)
  958. eq(2, #before_delete)
  959. meths.clear_autocmds(search)
  960. local after_delete = meths.get_autocmds(search)
  961. eq(0, #after_delete)
  962. end)
  963. it('should clear based on pattern', function()
  964. command('autocmd InsertEnter *.TestPat1 :echo "Enter 1"')
  965. command('autocmd InsertLeave *.TestPat1 :echo "Leave 1"')
  966. command('autocmd InsertEnter *.TestPat2 :echo "Enter 2"')
  967. command('autocmd InsertLeave *.TestPat2 :echo "Leave 2"')
  968. local search = { pattern = "*.TestPat1"}
  969. local before_delete = meths.get_autocmds(search)
  970. eq(2, #before_delete)
  971. local before_delete_events = meths.get_autocmds { event = { "InsertEnter", "InsertLeave" } }
  972. eq(4, #before_delete_events)
  973. meths.clear_autocmds(search)
  974. local after_delete = meths.get_autocmds(search)
  975. eq(0, #after_delete)
  976. local after_delete_events = meths.get_autocmds { event = { "InsertEnter", "InsertLeave" } }
  977. eq(2, #after_delete_events)
  978. end)
  979. it('should allow clearing by buffer', function()
  980. command('autocmd! InsertEnter')
  981. command('autocmd InsertEnter <buffer> :echo "Enter Buffer"')
  982. command('autocmd InsertEnter *.TestPat1 :echo "Enter Pattern"')
  983. local search = { event = "InsertEnter" }
  984. local before_delete = meths.get_autocmds(search)
  985. eq(2, #before_delete)
  986. meths.clear_autocmds { buffer = 0 }
  987. local after_delete = meths.get_autocmds(search)
  988. eq(1, #after_delete)
  989. eq("*.TestPat1", after_delete[1].pattern)
  990. end)
  991. it('should allow clearing by buffer and group', function()
  992. command('augroup TestNvimClearAutocmds')
  993. command(' au!')
  994. command(' autocmd InsertEnter <buffer> :echo "Enter Buffer"')
  995. command(' autocmd InsertEnter *.TestPat1 :echo "Enter Pattern"')
  996. command('augroup END')
  997. local search = { event = "InsertEnter", group = "TestNvimClearAutocmds" }
  998. local before_delete = meths.get_autocmds(search)
  999. eq(2, #before_delete)
  1000. -- Doesn't clear without passing group.
  1001. meths.clear_autocmds { buffer = 0 }
  1002. local without_group = meths.get_autocmds(search)
  1003. eq(2, #without_group)
  1004. -- Doest clear with passing group.
  1005. meths.clear_autocmds { buffer = 0, group = search.group }
  1006. local with_group = meths.get_autocmds(search)
  1007. eq(1, #with_group)
  1008. end)
  1009. end)
  1010. end)