feat(lua): vim.autocmd and vim.augroup

test(lua): `vim.autocmd` and `vim.augroup`

test: add describe blocks

Organize tests with `describe` blocks.

style: indentation
This commit is contained in:
Sandeep Dilip 2023-01-14 10:34:55 -05:00
parent 2f1fd15554
commit e04acb1459
No known key found for this signature in database
GPG Key ID: A542AE2057EF588B
3 changed files with 476 additions and 0 deletions

243
runtime/lua/vim/_auto.lua Normal file
View File

@ -0,0 +1,243 @@
---@class EventContext
---@field _group string|boolean
---@field _buffer integer|boolean
local EventContext = {}
---@class Event
---@field _ctx EventContext
---@field _event string|string[]
---@field _pattern? string|string[]
local Event = {}
---@class Augroup
---@field _ctx EventContext
---@field _group string
local Augroup = {}
local a, validate = vim.api, vim.validate
---@param group? string|boolean
---@param buffer? integer|boolean
---@return EventContext
---@private
function EventContext._new(group, buffer)
-- use non-nil values to avoid triggering the `__index` metamethod when we access fields on self.
local self = {
_group = group or false,
_buffer = buffer or false,
}
return setmetatable(self, EventContext)
end
---@param opts? table API options
---@return table opts
function EventContext:_apply(opts)
opts = opts or {}
local g, b = self._group, self._buffer
b = b == true and 0 or b
opts.buffer = b or opts.buffer
opts.group = g or opts.group
return opts
end
---@param self EventContext
---@param opts? table
---@return table
---@see |nvim_get_autocmds()|
function EventContext:get(opts)
return a.nvim_get_autocmds(self:_apply(opts))
end
---@param self EventContext
---@param opts? table
---@see |nvim_clear_autocmds()|
function EventContext:clear(opts)
a.nvim_clear_autocmds(self:_apply(opts))
end
---@param self EventContext
---@private
function EventContext:__index(k)
-- first, check methods
if EventContext[k] then
return EventContext[k]
end
-- then, check if we're trying to specify a buffer
if k == "buf" then
return self._buffer == false and EventContext._new(self._group, true) or nil
elseif type(k) == "number" then
return self._buffer == true and EventContext._new(self._group, k) or nil
end
-- nothing else to check; use k as event name
return Event._new(self, k)
end
---@param ctx EventContext
---@param event string|string[]
---@param pattern? string|string[]
---@return Event
---@private
function Event._new(ctx, event, pattern)
local self = {
_ctx = ctx,
_event = event,
_pattern = pattern or false,
}
return setmetatable(self, Event)
end
---@param self Event
---@param opts? table
---@return table[]
function Event:get(opts)
opts = self._ctx:_apply(opts)
opts.event = self._event
opts.pattern = self._pattern or opts.pattern
return a.nvim_get_autocmds(opts)
end
---@param self Event
---@param opts? table
function Event:exec(opts)
opts = self._ctx:_apply(opts)
opts.pattern = self._pattern or opts.pattern
a.nvim_exec_autocmds(self._event, opts)
end
---@param self Event
---@param opts? table
function Event:clear(opts)
opts = self._ctx:_apply(opts)
opts.event = self._event
opts.pattern = self._pattern or opts.pattern
a.nvim_clear_autocmds(opts)
end
--- Create an autocommand for this event
---@param self Event
---@param handler string|function
---@param opts? table
---@return integer
function Event:__call(handler, opts)
validate {
handler = { handler, {"s", "f"} },
opts = { opts, "t", true },
}
opts = self._ctx:_apply(opts)
opts.pattern = self._pattern or opts.pattern
if type(handler) == "string" and handler:sub(1, 1) == ":" then
opts.command = handler:sub(2)
else
opts.callback = handler
end
return a.nvim_create_autocmd(self._event, opts)
end
---@param self Event
---@return function|Event|nil
function Event:__index(k)
if Event[k] then
return Event[k]
elseif not self._pattern and not self._ctx._buffer then
return Event._new(self._ctx, self._event, k)
else
return nil
end
end
Augroup.__index = Augroup
---@param name string
---@return Augroup
function Augroup._new(name)
local self = {
_ctx = EventContext._new(name, nil),
_group = name,
}
return setmetatable(self, Augroup)
end
---@param self Augroup
---@return integer id
function Augroup:create()
return a.nvim_create_augroup(self._group, { clear = false })
end
---@param self Augroup
---@param opts? table
---@return integer? id
function Augroup:clear(opts)
if not opts then
return a.nvim_create_augroup(self._group, { clear = true })
else
self._ctx:clear(opts)
end
end
---@param self Augroup
function Augroup:del()
return a.nvim_del_augroup_by_name(self._group)
end
---@param self Augroup
---@param opts? table
---@return table[]?
function Augroup:get(opts)
if not opts then
local exists, cmds = pcall(a.nvim_get_autocmds, { group = self._group })
return exists and cmds or nil
else
return self._ctx:get(opts)
end
end
---@param self Augroup
---@param spec fun(au:EventContext):any
---@return integer id
---@return any
function Augroup:__call(spec)
local id = self:create()
local res = spec(self._ctx)
return id, res
end
--- Use `vim.autocmd` to manage autocommands. Index it by event names to create and execute them.
---
--- To create an autocommand, index `vim.autocmd` with an event name to return a callable table.
--- Then call it with a handler (a Lua function, a Vimscript function name, or Ex command) and
--- and optional table of options to pass to |nvim_create_autocmd()|.
---
--- <pre>lua
--- -- prefix Ex commands with ":" to use as an event handler
--- vim.autocmd.UIEnter(":echo 'Hello!'")
--- -- a Lua callback as an event handler
--- vim.autocmd.UIEnter(function()
--- vim.cmd.echo 'Hello!'
--- end)
--- -- passing in additional options
--- vim.autocmd.UIEnter(":echo 'Hello!'", {
--- desc = "greeting",
--- once = true,
--- })
--- -- specify multiple events
--- vim.autocmd[{ "UIEnter", "TabEnter", "TermEnter" }](":echo 'Hello!'")
--- </pre>
---
--- You may also specify a pattern by indexing the event.
---
--- <pre>lua
--- vim.autocmd.FileType[{ "qf", "help", "man", }](function()
--- vim.opt_local.number = false
--- vim.opt_local.relativenumber = false
--- end)
--- </pre>
---
vim.autocmd = EventContext._new(nil, nil)
vim.autocmd.buf = EventContext._new(nil, 0)
--- Create, delete, and clear autocommand groups with `vim.augroup`.
---
vim.augroup = setmetatable({}, {
__index = function(_, k) return Augroup._new(k) end,
})

View File

@ -59,6 +59,9 @@ setmetatable(vim, {
elseif key == 'inspect_pos' or key == 'show_pos' then
require('vim._inspector')
return t[key]
elseif key == 'autocmd' or key == 'augroup' then
require('vim._auto')
return t[key]
elseif vim.startswith(key, 'uri_') then
local val = require('vim.uri')[key]
if val ~= nil then

View File

@ -0,0 +1,230 @@
local helpers = require('test.functional.helpers')(after_each)
local exec_lua = helpers.exec_lua
local meths = helpers.meths
local clear = helpers.clear
local eq = helpers.eq
before_each(clear)
describe('vim.autocmd', function()
describe('vim.autocmd:get()', function()
pending('behaves like nvim_get_autocmds')
end)
describe('vim.autocmd:clear()', function()
pending('behaves like nvim_clear_autocmds')
end)
describe('vim.autocmd.buf', function()
pending('manages buflocal autocommands')
it('can create an autocommand for the current buffer', function()
exec_lua [[ vim.autocmd.buf.InsertEnter(':echo "Coding!"') ]]
eq(1, #meths.get_autocmds({ buffer = 0 }))
end)
it('can get all autocommands attached to the current buffer', function()
meths.create_autocmd('InsertEnter', {
buffer = 0,
command = 'echo "Coding!"'
})
meths.create_autocmd('InsertLeave', {
buffer = 0,
command = 'echo "Done!"'
})
local aus = exec_lua [[ return vim.autocmd.buf:get() ]]
eq(2, #aus)
end)
it('can clear all autocommands attached to the current buffer', function()
meths.create_autocmd('InsertEnter', {
buffer = 0,
command = 'echo "Coding!"'
})
meths.create_autocmd('InsertLeave', {
buffer = 0,
command = 'echo "Done!"'
})
eq(2, #meths.get_autocmds({ buffer = 0 }))
exec_lua [[ vim.autocmd.buf:clear() ]]
eq(0, #meths.get_autocmds({ buffer = 0 }))
end)
pending('can be indexed with a bufnr')
end)
it('can create an autocommand', function()
local id = exec_lua([[
return vim.autocmd.UIEnter(':echo "Hello!"')
]])
assert.number(id)
local cmds = meths.get_autocmds({ event = 'UIEnter' })
eq(1, #cmds)
eq(id, cmds[1].id)
eq('echo "Hello!"', cmds[1].command)
end)
it('can create an autocommand with options', function()
local id = exec_lua([[
return vim.autocmd.UIEnter(':echo "Hello!"', {
desc = 'greeting',
once = true,
})
]])
assert.number(id)
local cmds = meths.get_autocmds({ event = 'UIEnter' })
eq(id, cmds[1].id)
eq('greeting', cmds[1].desc)
end)
it('can create an autocommand for multiple events', function()
local id = exec_lua([[
return vim.autocmd[{ 'UIEnter', 'VimEnter', 'WinEnter' }](':echo "Hello!"')
]])
assert.number(id)
eq(id, meths.get_autocmds({ event = 'UIEnter' })[1].id)
eq(id, meths.get_autocmds({ event = 'VimEnter' })[1].id)
eq(id, meths.get_autocmds({ event = 'WinEnter' })[1].id)
end)
it('can create an autocommand for an event and pattern', function()
local id = exec_lua([[
return vim.autocmd.User.CustomEvent(':echo "Hello!"')
]])
assert.number(id)
eq(id, meths.get_autocmds({ event = 'User', pattern = 'CustomEvent' })[1].id)
end)
it('can create an autocommand and specify multiple patterns', function()
local id = exec_lua([[
return vim.autocmd.Filetype[{ 'lua', 'vim', 'sh' }](':echo "Hello!"')
]])
assert.number(id)
eq(3, #meths.get_autocmds({ event = 'Filetype' }))
end)
it('can get autocommands for an event', function()
meths.create_autocmd('UIEnter', {
command = 'echo "Hello!"',
})
local cmds = exec_lua([[
return vim.autocmd.UIEnter:get()
]])
eq(1, #cmds)
end)
it('can get autocommands for an event and pattern', function()
meths.create_autocmd('User', {
pattern = 'foo',
command = 'echo "Hello!"',
})
meths.create_autocmd('User', {
pattern = 'bar',
command = 'echo "Hello!"',
})
local cmds = exec_lua([[
return vim.autocmd.User.foo:get()
]])
eq(1, #cmds)
end)
it('can clear autocommands for an event', function()
meths.create_autocmd('UIEnter', {
command = 'echo "Hello!"',
})
exec_lua([[
vim.autocmd.UIEnter:clear()
]])
eq(0, #meths.get_autocmds({ event = 'UIEnter' }))
end)
it('can execute autocommands', function()
meths.set_var("some_condition", false)
exec_lua [[
vim.api.nvim_create_autocmd("User", {
pattern = "Test",
desc = "A test autocommand",
callback = function()
return vim.g.some_condition
end,
})
]]
exec_lua [[ vim.autocmd.User.Test:exec() ]]
local aus = meths.get_autocmds({ event = 'User', pattern = 'Test' })
local first = aus[1]
eq(first.id, 1)
meths.set_var("some_condition", true)
exec_lua [[ vim.autocmd.User.Test:exec() ]]
eq({}, meths.get_autocmds({event = "User", pattern = "Test"}))
end)
end)
describe('vim.augroup', function()
it('can delete an existing group', function()
local id = meths.create_augroup('nvim_test_augroup', { clear = true })
meths.create_autocmd('User', {
group = id,
pattern = "Test",
desc = "A test autocommand",
command = 'echo "Test!"',
})
local aus = meths.get_autocmds({ group = 'nvim_test_augroup' })
eq(1, #aus)
exec_lua [[ vim.augroup.nvim_test_augroup:del() ]]
local success = exec_lua [[
return pcall(vim.api.nvim_get_autocmds, { group = 'nvim_test_augroup' })
]]
eq(false, success)
end)
describe('Augroup:create()', function()
it('can create a group and return its id', function()
local id = exec_lua [[ return vim.augroup.nvim_test_augroup:create() ]]
meths.create_autocmd('User', {
group = id,
pattern = "Test",
desc = "A test autocommand",
command = 'echo "Test!"',
})
local aus = meths.get_autocmds({ group = id })
eq(1, #aus)
end)
pending('can return the id of an existing group')
pending('does not clear an existing group')
end)
describe('Augroup:clear()', function()
pending('clears autocommands in the group')
pending('can be called with a dictionary of autocommand options')
pending('can create the group when called without arguments')
end)
describe('Augroup:get()', function()
pending('can return a list of autocommands in the group')
pending('can be called with a dictionary of autocommand options')
pending('returns nil when called without arguments on a nonexistant group')
end)
describe('Augroup:__call()', function()
it('can create a group and define its autocommands', function()
exec_lua [[
vim.augroup.nvim_test_augroup(function(au)
au.UIEnter(":echo 'Hello!'")
au.User.Test(":echo 'Test!'")
au.InsertEnter['*'](":echo 'Test!'")
end)
]]
local aus = meths.get_autocmds({ group = 'nvim_test_augroup' })
eq(3, #aus)
end)
pending('can add autocommands to an existing group')
pending('returns the group id and any values returned from the function')
end)
end)