lua: docs and tests for vim.schedule

This commit is contained in:
Björn Linse 2019-06-04 19:32:43 +02:00
parent 81e1dbca99
commit b684bd05b5
4 changed files with 58 additions and 35 deletions

View File

@ -203,6 +203,8 @@ User reloads the buffer with ":edit", emits: >
In-process lua plugins can also recieve buffer updates, in the form of lua In-process lua plugins can also recieve buffer updates, in the form of lua
callbacks. These callbacks are called frequently in various contexts, buffer callbacks. These callbacks are called frequently in various contexts, buffer
contents or window layout should not be changed inside these |textlock|. contents or window layout should not be changed inside these |textlock|.
|lua-vim.schedule| can be used to defer these operations to the main loop,
where they are allowed.
|nvim_buf_attach| will take keyword args for the callbacks. "on_lines" will |nvim_buf_attach| will take keyword args for the callbacks. "on_lines" will
receive parameters ("lines", {buf}, {changedtick}, {firstline}, {lastline}, {new_lastline}). receive parameters ("lines", {buf}, {changedtick}, {firstline}, {lastline}, {new_lastline}).

View File

@ -379,6 +379,12 @@ vim.stricmp(a, b) *lua-vim.stricmp*
string arguments and returns 0, 1 or -1 if strings are equal, a is string arguments and returns 0, 1 or -1 if strings are equal, a is
greater then b or a is lesser then b respectively. greater then b or a is lesser then b respectively.
vim.schedule(callback) *lua-vim.schedule*
Schedule `callback` to be called soon by the main event loop. This is
useful in contexts where some functionality is blocked, like an
autocommand or callback running with |textlock|. Then the scheduled
callback could invoke this functionality later when it is allowed.
vim.type_idx *lua-vim.type_idx* vim.type_idx *lua-vim.type_idx*
Type index for use in |lua-special-tbl|. Specifying one of the Type index for use in |lua-special-tbl|. Specifying one of the
values from |lua-vim.types| allows typing the empty table (it is values from |lua-vim.types| allows typing the empty table (it is

View File

@ -36,12 +36,6 @@ typedef struct {
String lua_err_str; String lua_err_str;
} LuaError; } LuaError;
/// We use this to store Lua callbacks
typedef struct {
// TODO: store more info for debugging, traceback?
int cb;
} nlua_ctx;
#ifdef INCLUDE_GENERATED_DECLARATIONS #ifdef INCLUDE_GENERATED_DECLARATIONS
# include "lua/vim_module.generated.h" # include "lua/vim_module.generated.h"
# include "lua/executor.c.generated.h" # include "lua/executor.c.generated.h"
@ -114,33 +108,32 @@ static int nlua_stricmp(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
return 1; return 1;
} }
static void nlua_schedule_cb(void **argv) static void nlua_schedule_event(void **argv)
{ {
nlua_ctx *ctx = argv[0]; LuaRef cb = (LuaRef)(ptrdiff_t)argv[0];
lua_State *const lstate = nlua_enter(); lua_State *const lstate = nlua_enter();
lua_rawgeti(lstate, LUA_REGISTRYINDEX, ctx->cb); nlua_pushref(lstate, cb);
luaL_unref(lstate, LUA_REGISTRYINDEX, ctx->cb); nlua_unref(lstate, cb);
lua_pcall(lstate, 0, 0, 0); if (lua_pcall(lstate, 0, 0, 0)) {
free(ctx); nlua_error(lstate, _("Error executing vim.schedule lua callback: %.*s"));
}
} }
/// Schedule Lua callback on main loop's event queue /// Schedule Lua callback on main loop's event queue
/// ///
/// This is used to make sure nvim API is called at the right moment.
///
/// @param lstate Lua interpreter state. /// @param lstate Lua interpreter state.
/// @param[in] msg Message base, must contain one `%s`.
static int nlua_schedule(lua_State *const lstate) static int nlua_schedule(lua_State *const lstate)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_ALL
{ {
// TODO: report error using nlua_error instead if (lua_type(lstate, 1) != LUA_TFUNCTION) {
luaL_checktype(lstate, 1, LUA_TFUNCTION); lua_pushliteral(lstate, "vim.schedule: expected function");
return lua_error(lstate);
}
nlua_ctx* ctx = (nlua_ctx*)malloc(sizeof(nlua_ctx)); LuaRef cb = nlua_ref(lstate, 1);
lua_pushvalue(lstate, 1);
ctx->cb = luaL_ref(lstate, LUA_REGISTRYINDEX);
multiqueue_put(main_loop.events, nlua_schedule_cb, 1, ctx); multiqueue_put(main_loop.events, nlua_schedule_event,
1, (void *)(ptrdiff_t)cb);
return 0; return 0;
} }

View File

@ -5,10 +5,13 @@ local funcs = helpers.funcs
local meths = helpers.meths local meths = helpers.meths
local clear = helpers.clear local clear = helpers.clear
local eq = helpers.eq local eq = helpers.eq
local eval = helpers.eval
local feed = helpers.feed
local meth_pcall = helpers.meth_pcall
before_each(clear) before_each(clear)
describe('vim.stricmp', function() describe('lua function', function()
-- İ: `tolower("İ")` is `i` which has length 1 while `İ` itself has -- İ: `tolower("İ")` is `i` which has length 1 while `İ` itself has
-- length 2 (in bytes). -- length 2 (in bytes).
-- Ⱥ: `tolower("Ⱥ")` is `ⱥ` which has length 2 while `Ⱥ` itself has -- Ⱥ: `tolower("Ⱥ")` is `ⱥ` which has length 2 while `Ⱥ` itself has
@ -17,7 +20,7 @@ describe('vim.stricmp', function()
-- Note: 'i' !=? 'İ' and 'ⱥ' !=? 'Ⱥ' on some systems. -- Note: 'i' !=? 'İ' and 'ⱥ' !=? 'Ⱥ' on some systems.
-- Note: Built-in Nvim comparison (on systems lacking `strcasecmp`) works -- Note: Built-in Nvim comparison (on systems lacking `strcasecmp`) works
-- only on ASCII characters. -- only on ASCII characters.
it('works', function() it('vim.stricmp', function()
eq(0, funcs.luaeval('vim.stricmp("a", "A")')) eq(0, funcs.luaeval('vim.stricmp("a", "A")'))
eq(0, funcs.luaeval('vim.stricmp("A", "a")')) eq(0, funcs.luaeval('vim.stricmp("A", "a")'))
eq(0, funcs.luaeval('vim.stricmp("a", "a")')) eq(0, funcs.luaeval('vim.stricmp("a", "a")'))
@ -106,10 +109,35 @@ describe('vim.stricmp', function()
eq(1, funcs.luaeval('vim.stricmp("\\0c\\0", "\\0b\\0")')) eq(1, funcs.luaeval('vim.stricmp("\\0c\\0", "\\0b\\0")'))
eq(1, funcs.luaeval('vim.stricmp("\\0C\\0", "\\0B\\0")')) eq(1, funcs.luaeval('vim.stricmp("\\0C\\0", "\\0B\\0")'))
end) end)
end)
describe("vim.split", function() it("vim.schedule", function()
it("works", function() meths.execute_lua([[
test_table = {}
vim.schedule(function()
table.insert(test_table, "xx")
end)
table.insert(test_table, "yy")
]], {})
eq({"yy","xx"}, meths.execute_lua("return test_table", {}))
-- type checked args
eq({false, 'Error executing lua: vim.schedule: expected function'},
meth_pcall(meths.execute_lua, "vim.schedule('stringly')", {}))
eq({false, 'Error executing lua: vim.schedule: expected function'},
meth_pcall(meths.execute_lua, "vim.schedule()", {}))
meths.execute_lua([[
vim.schedule(function()
error("big failure\nvery async")
end)
]], {})
feed("<cr>")
eq('Error executing vim.schedule lua callback: [string "<nvim>"]:2: big failure\nvery async', eval("v:errmsg"))
end)
it("vim.split", function()
local split = function(str, sep) local split = function(str, sep)
return meths.execute_lua('return vim.split(...)', {str, sep}) return meths.execute_lua('return vim.split(...)', {str, sep})
end end
@ -141,10 +169,8 @@ describe("vim.split", function()
assert(string.match(err, "Infinite loop detected")) assert(string.match(err, "Infinite loop detected"))
end end
end) end)
end)
describe("vim.trim", function() it('vim.trim', function()
it('works', function()
local trim = function(s) local trim = function(s)
return meths.execute_lua('return vim.trim(...)', { s }) return meths.execute_lua('return vim.trim(...)', { s })
end end
@ -164,10 +190,8 @@ describe("vim.trim", function()
eq(false, status) eq(false, status)
assert(string.match(err, "Only strings can be trimmed")) assert(string.match(err, "Only strings can be trimmed"))
end) end)
end)
describe("vim.inspect", function() it('vim.inspect', function()
it('works', function()
-- just make sure it basically works, it has its own test suite -- just make sure it basically works, it has its own test suite
local inspect = function(t, opts) local inspect = function(t, opts)
return meths.execute_lua('return vim.inspect(...)', { t, opts }) return meths.execute_lua('return vim.inspect(...)', { t, opts })
@ -187,10 +211,8 @@ describe("vim.inspect", function()
end}) end})
]], {})) ]], {}))
end) end)
end)
describe("vim.deepcopy", function() it("vim.deepcopy", function()
it("works", function()
local is_dc = meths.execute_lua([[ local is_dc = meths.execute_lua([[
local a = { x = { 1, 2 }, y = 5} local a = { x = { 1, 2 }, y = 5}
local b = vim.deepcopy(a) local b = vim.deepcopy(a)