From 3da3cfc864e89a2dca6917183915683373c85af8 Mon Sep 17 00:00:00 2001 From: kylo252 <59826753+kylo252@users.noreply.github.com> Date: Thu, 9 Jun 2022 15:18:56 +0200 Subject: [PATCH] feat(autocmds): retrieve lua callback (#18642) add a new `callback` field to `nvim_get_autocmds` --- runtime/doc/api.txt | 6 +++- src/nvim/api/autocmd.c | 38 ++++++++++++++++---- src/nvim/autocmd.c | 28 +-------------- src/nvim/eval/typval.c | 24 +++++++++++++ test/functional/api/autocmd_spec.lua | 54 ++++++++++++++++++++++++++-- 5 files changed, 112 insertions(+), 38 deletions(-) diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index 2c75acbe1f..e47c5cbd6f 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -3610,7 +3610,11 @@ nvim_get_autocmds({*opts}) *nvim_get_autocmds()* • group_name (string): the autocommand group name. • desc (string): the autocommand description. • event (string): the autocommand event. - • command (string): the autocommand command. + • command (string): the autocommand command. Note: this + will be empty if a callback is set. + • callback (function|string|nil): Lua function or name of + a Vim script function which is executed when this + autocommand is triggered. • once (boolean): whether the autocommand is only run once. • pattern (string): the autocommand pattern. If the diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index 19b4119344..3a35e49dc8 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -66,7 +66,9 @@ static int64_t next_autocmd_id = 1; /// - group_name (string): the autocommand group name. /// - desc (string): the autocommand description. /// - event (string): the autocommand event. -/// - command (string): the autocommand command. +/// - command (string): the autocommand command. Note: this will be empty if a callback is set. +/// - callback (function|string|nil): Lua function or name of a Vim script function +/// which is executed when this autocommand is triggered. /// - once (boolean): whether the autocommand is only run once. /// - pattern (string): the autocommand pattern. /// If the autocommand is buffer local |autocmd-buffer-local|: @@ -280,9 +282,28 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) PUT(autocmd_info, "desc", CSTR_TO_OBJ(ac->desc)); } - PUT(autocmd_info, - "command", - STRING_OBJ(cstr_as_string(aucmd_exec_to_string(ac, ac->exec)))); + if (ac->exec.type == CALLABLE_CB) { + PUT(autocmd_info, "command", STRING_OBJ(STRING_INIT)); + + Callback *cb = &ac->exec.callable.cb; + switch (cb->type) { + case kCallbackLua: + if (nlua_ref_is_function(cb->data.luaref)) { + PUT(autocmd_info, "callback", LUAREF_OBJ(api_new_luaref(cb->data.luaref))); + } + break; + case kCallbackFuncref: + case kCallbackPartial: + PUT(autocmd_info, "callback", STRING_OBJ(cstr_as_string(callback_to_string(cb)))); + break; + default: + abort(); + } + } else { + PUT(autocmd_info, + "command", + STRING_OBJ(cstr_as_string(xstrdup(ac->exec.callable.cmd)))); + } PUT(autocmd_info, "pattern", @@ -442,7 +463,8 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc // not do that. Object *callback = &opts->callback; - if (callback->type == kObjectTypeLuaRef) { + switch (callback->type) { + case kObjectTypeLuaRef: if (callback->data.luaref == LUA_NOREF) { api_set_error(err, kErrorTypeValidation, @@ -459,10 +481,12 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc cb.type = kCallbackLua; cb.data.luaref = api_new_luaref(callback->data.luaref); - } else if (callback->type == kObjectTypeString) { + break; + case kObjectTypeString: cb.type = kCallbackFuncref; cb.data.funcref = string_to_cstr(callback->data.string); - } else { + break; + default: api_set_error(err, kErrorTypeException, "'callback' must be a lua function or name of vim function"); diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 31efce13f6..69f01afcb6 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -2477,32 +2477,6 @@ bool autocmd_delete_id(int64_t id) // AucmdExecutable Functions // =========================================================================== -/// Generate a string description of a callback -static char *aucmd_callback_to_string(Callback cb) -{ - // NOTE: this function probably belongs in a helper - - size_t msglen = 100; - char *msg = (char *)xmallocz(msglen); - - switch (cb.type) { - case kCallbackLua: - snprintf(msg, msglen, "", cb.data.luaref); - break; - case kCallbackFuncref: - // TODO(tjdevries): Is this enough space for this? - snprintf(msg, msglen, "", cb.data.funcref); - break; - case kCallbackPartial: - snprintf(msg, msglen, "", cb.data.partial->pt_name); - break; - default: - snprintf(msg, msglen, "%s", ""); - break; - } - return msg; -} - /// Generate a string description for the command/callback of an autocmd char *aucmd_exec_to_string(AutoCmd *ac, AucmdExecutable acc) FUNC_ATTR_PURE @@ -2511,7 +2485,7 @@ char *aucmd_exec_to_string(AutoCmd *ac, AucmdExecutable acc) case CALLABLE_EX: return xstrdup(acc.callable.cmd); case CALLABLE_CB: - return aucmd_callback_to_string(acc.callable.cb); + return callback_to_string(&acc.callable.cb); case CALLABLE_NONE: return "This is not possible"; } diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 2c76741891..97726da5f4 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1205,6 +1205,30 @@ void callback_copy(Callback *dest, Callback *src) } } +/// Generate a string description of a callback +char *callback_to_string(Callback *cb) +{ + size_t msglen = 100; + char *msg = (char *)xmallocz(msglen); + + switch (cb->type) { + case kCallbackLua: + snprintf(msg, msglen, "", cb->data.luaref); + break; + case kCallbackFuncref: + // TODO(tjdevries): Is this enough space for this? + snprintf(msg, msglen, "", cb->data.funcref); + break; + case kCallbackPartial: + snprintf(msg, msglen, "", cb->data.partial->pt_name); + break; + default: + snprintf(msg, msglen, "%s", ""); + break; + } + return msg; +} + /// Remove watcher from a dictionary /// /// @param dict Dictionary to remove watcher from. diff --git a/test/functional/api/autocmd_spec.lua b/test/functional/api/autocmd_spec.lua index 491dac9f35..5dfcd09438 100644 --- a/test/functional/api/autocmd_spec.lua +++ b/test/functional/api/autocmd_spec.lua @@ -135,17 +135,22 @@ describe('autocmd api', function() local desc = 'Can show description' meths.set_var('desc', desc) - exec_lua([[ + local result = exec_lua([[ local callback = function() print 'Should Not Have Errored' end vim.api.nvim_create_autocmd("BufReadPost", { pattern = "*.py", callback = callback, desc = vim.g.desc, }) + local aus = vim.api.nvim_get_autocmds({ event = 'BufReadPost' }) + local first = aus[1] + return { + desc = first.desc, + cbtype = type(first.callback) + } ]]) - eq(desc, meths.get_autocmds({ event = 'BufReadPost' })[1].desc) - matches('', meths.get_autocmds({ event = 'BufReadPost' })[1].command) + eq({ desc = desc, cbtype = 'function' }, result) end) it('will not add a description unless it was provided', function() @@ -465,6 +470,49 @@ describe('autocmd api', function() -- 3-7 for the 5 we make in the autocmd eq({1, 2, 3, 4, 5, 6, 7}, bufs) end) + + it('can retrieve a callback from an autocmd', function() + local content = 'I Am A Callback' + meths.set_var('content', content) + + local result = exec_lua([[ + local cb = function() return vim.g.content end + vim.api.nvim_create_autocmd("User", { + pattern = "TestTrigger", + desc = "A test autocommand with a callback", + callback = cb, + }) + local aus = vim.api.nvim_get_autocmds({ event = 'User', pattern = 'TestTrigger'}) + local first = aus[1] + return { + cb = { + type = type(first.callback), + can_retrieve = first.callback() == vim.g.content + } + } + ]]) + + eq("function", result.cb.type) + eq(true, result.cb.can_retrieve) + end) + + it('will return an empty string as the command for an autocmd that uses a callback', function() + local result = exec_lua([[ + local callback = function() print 'I Am A Callback' end + vim.api.nvim_create_autocmd("BufWritePost", { + pattern = "*.py", + callback = callback, + }) + local aus = vim.api.nvim_get_autocmds({ event = 'BufWritePost' }) + local first = aus[1] + return { + command = first.command, + cbtype = type(first.callback) + } + ]]) + + eq({ command = "", cbtype = 'function' }, result) + end) end) describe('groups', function()