aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBjörn Linse <bjorn.linse@gmail.com>2019-08-05 13:59:40 +0200
committerGitHub <noreply@github.com>2019-08-05 13:59:40 +0200
commitf5d1e0e7b1837e8b76f1561167543c90a9942a7a (patch)
treeae871874930fc44d4269c9c01eb7827da20998bc
parent51440204e7973672f906db8e53035cfd228ce444 (diff)
parent88938634e7418ced4cfb074c48867523460dcc84 (diff)
downloadrneovim-f5d1e0e7b1837e8b76f1561167543c90a9942a7a.tar.gz
rneovim-f5d1e0e7b1837e8b76f1561167543c90a9942a7a.tar.bz2
rneovim-f5d1e0e7b1837e8b76f1561167543c90a9942a7a.zip
Merge pull request #10690 from bfredl/lua_print
lua: laundry list (crashes and additions)
-rw-r--r--runtime/doc/if_lua.txt11
-rw-r--r--src/nvim/lua/executor.c99
-rw-r--r--test/functional/lua/loop_spec.lua6
-rw-r--r--test/functional/lua/overrides_spec.lua105
4 files changed, 177 insertions, 44 deletions
diff --git a/runtime/doc/if_lua.txt b/runtime/doc/if_lua.txt
index f886fd28c5..7ddcb6cc92 100644
--- a/runtime/doc/if_lua.txt
+++ b/runtime/doc/if_lua.txt
@@ -400,7 +400,8 @@ is safe to execute API methods. >
end))
A subset of the API is available in direct luv callbacks ("fast" callbacks),
-most notably |nvim_get_mode()| and |nvim_input()|.
+most notably |nvim_get_mode()| and |nvim_input()|. It is possible to
+check whether code is running in this context using |vim.in_fast_event()|.
Example: repeating timer
@@ -462,6 +463,14 @@ vim.schedule({callback}) *vim.schedule()*
Schedules {callback} to be invoked soon by the main event-loop. Useful
to avoid |textlock| or other temporary restrictions.
+vim.in_fast_event() *vim.in_fast_event()*
+ Returns true if the code is executing as part of a "fast" event
+ handler, where most of the API is disabled. These are low-level event
+ such as luv callbacks |lua-loop-callbacks|, which can be invoked at
+ any time nvim polls for input. When this returns `false` most API
+ functions are callable, but can be subjected to other restrictions,
+ such as |textlock|.
+
vim.type_idx *vim.type_idx*
Type index for use in |lua-special-tbl|. Specifying one of the
values from |vim.types| allows typing the empty table (it is
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index 3fd1620845..4051354d65 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -223,6 +223,9 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
// schedule
lua_pushcfunction(lstate, &nlua_schedule);
lua_setfield(lstate, -2, "schedule");
+ // in_fast_event
+ lua_pushcfunction(lstate, &nlua_in_fast_event);
+ lua_setfield(lstate, -2, "in_fast_event");
// vim.loop
luv_set_loop(lstate, &main_loop.uv);
@@ -323,6 +326,42 @@ void executor_exec_lua(const String str, typval_T *const ret_tv)
nlua_pop_typval(lstate, ret_tv);
}
+static void nlua_print_event(void **argv)
+{
+ char *str = argv[0];
+ const size_t len = (size_t)(intptr_t)argv[1]-1; // exclude final NUL
+
+ for (size_t i = 0; i < len;) {
+ const size_t start = i;
+ while (i < len) {
+ switch (str[i]) {
+ case NUL: {
+ str[i] = NL;
+ i++;
+ continue;
+ }
+ case NL: {
+ // TODO(bfredl): use proper multiline msg? Probably should implement
+ // print() in lua in terms of nvim_message(), when it is available.
+ str[i] = NUL;
+ i++;
+ break;
+ }
+ default: {
+ i++;
+ continue;
+ }
+ }
+ break;
+ }
+ msg((char_u *)str + start);
+ }
+ if (len && str[len - 1] == NUL) { // Last was newline
+ msg((char_u *)"");
+ }
+ xfree(str);
+}
+
/// Print as a Vim message
///
/// @param lstate Lua interpreter state.
@@ -362,47 +401,24 @@ static int nlua_print(lua_State *const lstate)
lua_pop(lstate, 1);
}
#undef PRINT_ERROR
- lua_pop(lstate, nargs + 1);
ga_append(&msg_ga, NUL);
- {
- const size_t len = (size_t)msg_ga.ga_len - 1;
- char *const str = (char *)msg_ga.ga_data;
-
- for (size_t i = 0; i < len;) {
- const size_t start = i;
- while (i < len) {
- switch (str[i]) {
- case NUL: {
- str[i] = NL;
- i++;
- continue;
- }
- case NL: {
- str[i] = NUL;
- i++;
- break;
- }
- default: {
- i++;
- continue;
- }
- }
- break;
- }
- msg((char_u *)str + start);
- }
- if (len && str[len - 1] == NUL) { // Last was newline
- msg((char_u *)"");
- }
+
+ if (in_fast_callback) {
+ multiqueue_put(main_loop.events, nlua_print_event,
+ 2, msg_ga.ga_data, msg_ga.ga_len);
+ } else {
+ nlua_print_event((void *[]){ msg_ga.ga_data,
+ (void *)(intptr_t)msg_ga.ga_len });
}
- ga_clear(&msg_ga);
return 0;
+
nlua_print_error:
- emsgf(_("E5114: Error while converting print argument #%i: %.*s"),
- curargidx, (int)errmsg_len, errmsg);
ga_clear(&msg_ga);
- lua_pop(lstate, lua_gettop(lstate));
- return 0;
+ const char *fmt = _("E5114: Error while converting print argument #%i: %.*s");
+ size_t len = (size_t)vim_snprintf((char *)IObuff, IOSIZE, fmt, curargidx,
+ (int)errmsg_len, errmsg);
+ lua_pushlstring(lstate, (char *)IObuff, len);
+ return lua_error(lstate);
}
/// debug.debug: interaction with user while debugging.
@@ -436,15 +452,20 @@ int nlua_debug(lua_State *lstate)
if (luaL_loadbuffer(lstate, (const char *)input.vval.v_string,
STRLEN(input.vval.v_string), "=(debug command)")) {
nlua_error(lstate, _("E5115: Error while loading debug string: %.*s"));
- }
- tv_clear(&input);
- if (lua_pcall(lstate, 0, 0, 0)) {
+ } else if (lua_pcall(lstate, 0, 0, 0)) {
nlua_error(lstate, _("E5116: Error while calling debug string: %.*s"));
}
+ tv_clear(&input);
}
return 0;
}
+int nlua_in_fast_event(lua_State *lstate)
+{
+ lua_pushboolean(lstate, in_fast_callback > 0);
+ return 1;
+}
+
#ifdef WIN32
/// os.getenv: override os.getenv to maintain coherency. #9681
///
diff --git a/test/functional/lua/loop_spec.lua b/test/functional/lua/loop_spec.lua
index b96214738a..992d1666f6 100644
--- a/test/functional/lua/loop_spec.lua
+++ b/test/functional/lua/loop_spec.lua
@@ -75,6 +75,7 @@ describe('vim.loop', function()
exec_lua([[
local timer = vim.loop.new_timer()
timer:start(20, 0, function ()
+ _G.is_fast = vim.in_fast_event()
timer:close()
vim.api.nvim_set_var("valid", true)
vim.api.nvim_command("echomsg 'howdy'")
@@ -89,18 +90,20 @@ describe('vim.loop', function()
{1:~ }|
{2: }|
{3:Error executing luv callback:} |
- {3:[string "<nvim>"]:4: E5560: nvim_set_var must not }|
+ {3:[string "<nvim>"]:5: E5560: nvim_set_var must not }|
{3:be called in a lua loop callback} |
{4:Press ENTER or type command to continue}^ |
]])
feed('<cr>')
eq(false, eval("get(g:, 'valid', v:false)"))
+ eq(true, exec_lua("return _G.is_fast"))
-- callbacks can be scheduled to be executed in the main event loop
-- where the entire API is available
exec_lua([[
local timer = vim.loop.new_timer()
timer:start(20, 0, vim.schedule_wrap(function ()
+ _G.is_fast = vim.in_fast_event()
timer:close()
vim.api.nvim_set_var("valid", true)
vim.api.nvim_command("echomsg 'howdy'")
@@ -120,6 +123,7 @@ describe('vim.loop', function()
howdy |
]])
eq(true, eval("get(g:, 'valid', v:false)"))
+ eq(false, exec_lua("return _G.is_fast"))
-- fast (not deferred) API functions are allowed to be called directly
exec_lua([[
diff --git a/test/functional/lua/overrides_spec.lua b/test/functional/lua/overrides_spec.lua
index 8f318e3503..f6439001ac 100644
--- a/test/functional/lua/overrides_spec.lua
+++ b/test/functional/lua/overrides_spec.lua
@@ -14,6 +14,7 @@ local command = helpers.command
local write_file = helpers.write_file
local redir_exec = helpers.redir_exec
local alter_slashes = helpers.alter_slashes
+local exec_lua = helpers.exec_lua
local screen
@@ -53,11 +54,11 @@ describe('print', function()
v_tblout = setmetatable({}, meta_tblout)
]])
eq('', redir_exec('luafile ' .. fname))
- eq('\nE5114: Error while converting print argument #2: [NULL]',
+ eq('\nE5105: Error while calling lua chunk: E5114: Error while converting print argument #2: [NULL]',
redir_exec('lua print("foo", v_nilerr, "bar")'))
- eq('\nE5114: Error while converting print argument #2: Xtest-functional-lua-overrides-luafile:2: abc',
+ eq('\nE5105: Error while calling lua chunk: E5114: Error while converting print argument #2: Xtest-functional-lua-overrides-luafile:2: abc',
redir_exec('lua print("foo", v_abcerr, "bar")'))
- eq('\nE5114: Error while converting print argument #2: <Unknown error: lua_tolstring returned NULL for tostring result>',
+ eq('\nE5105: Error while calling lua chunk: E5114: Error while converting print argument #2: <Unknown error: lua_tolstring returned NULL for tostring result>',
redir_exec('lua print("foo", v_tblout, "bar")'))
end)
it('prints strings with NULs and NLs correctly', function()
@@ -76,6 +77,29 @@ describe('print', function()
eq('\nabc ', redir_exec('lua print("abc", "")'))
eq('\nabc def', redir_exec('lua print("abc", "", "def")'))
end)
+ it('defers printing in luv event handlers', function()
+ exec_lua([[
+ local cmd = ...
+ function test()
+ local timer = vim.loop.new_timer()
+ local done = false
+ timer:start(10, 0, function()
+ print("very fast")
+ timer:close()
+ done = true
+ end)
+ -- be kind to slow travis OS X jobs:
+ -- loop until we know for sure the callback has been executed
+ while not done do
+ os.execute(cmd)
+ vim.loop.run("nowait") -- fake os_breakcheck()
+ end
+ print("very slow")
+ vim.api.nvim_command("sleep 1m") -- force deferred event processing
+ end
+ ]], (iswin() and "timeout 1") or "sleep 0.1")
+ eq('\nvery slow\nvery fast',redir_exec('lua test()'))
+ end)
end)
describe('debug.debug', function()
@@ -183,6 +207,81 @@ describe('debug.debug', function()
{cr:Press ENTER or type command to continue}^ |
]])
end)
+
+ it("can be safely exited with 'cont'", function()
+ feed('<cr>')
+ feed(':lua debug.debug() print("x")<cr>')
+ screen:expect{grid=[[
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ lua_debug> ^ |
+ ]]}
+
+ feed("conttt<cr>") -- misspelled cont; invalid syntax
+ screen:expect{grid=[[
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ lua_debug> conttt |
+ {E:E5115: Error while loading debug string: (debug comma}|
+ {E:nd):1: '=' expected near '<eof>'} |
+ lua_debug> ^ |
+ ]]}
+
+ feed("cont<cr>") -- exactly "cont", exit now
+ screen:expect{grid=[[
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ lua_debug> conttt |
+ {E:E5115: Error while loading debug string: (debug comma}|
+ {E:nd):1: '=' expected near '<eof>'} |
+ lua_debug> cont |
+ x |
+ {cr:Press ENTER or type command to continue}^ |
+ ]]}
+
+ feed('<cr>')
+ screen:expect{grid=[[
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]]}
+ end)
end)
describe('package.path/package.cpath', function()