From 7c94bcd2d77e2e54b8836ab8325460a367b79eae Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 20 Sep 2021 19:00:50 -0700 Subject: feat(lua)!: execute Lua with "nvim -l" Problem: Nvim has Lua but the "nvim" CLI can't easily be used to execute Lua scripts, especially scripts that take arguments or produce output. Solution: - support "nvim -l [args...]" for running scripts. closes #15749 - exit without +q - remove lua2dox_filter - remove Doxyfile. This wasn't used anyway, because the doxygen config is inlined in gen_vimdoc.py (`Doxyfile` variable). - use "nvim -l" in docs-gen CI job Examples: $ nvim -l scripts/lua2dox.lua --help Lua2DoX (0.2 20130128) ... $ echo "print(vim.inspect(_G.arg))" | nvim -l - --arg1 --arg2 $ echo 'print(vim.inspect(vim.api.nvim_buf_get_text(1,0,0,-1,-1,{})))' | nvim +"put ='text'" -l - TODO? -e executes Lua code -l loads a module -i enters REPL _after running the other arguments_. --- src/nvim/lua/executor.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'src/nvim/lua/executor.c') diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 34b572f884..1bedb70efb 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -323,6 +323,34 @@ static int nlua_thr_api_nvim__get_runtime(lua_State *lstate) return 1; } +/// Copies all args into the Lua `arg` global. +/// +/// Example: +/// nvim -l foo.lua -- -e "sin=math.sin" script a b +/// +/// @note `lua` CLI sets _negative_ `arg` indices to the arguments upto "-e". +/// +/// @see https://www.lua.org/pil/1.4.html +/// @see https://github.com/premake/premake-core/blob/1c1304637f4f5e50ba8c57aae8d1d80ec3b7aaf2/src/host/premake.c#L563-L594 +/// +/// @returns number of args (stops at "--") +int nlua_set_argv(char **argv, int argc) +{ + lua_State *const L = global_lstate; + lua_newtable(L); + int i = 0; + for (; i < argc; i++) { + if (strequal("--", argv[i])) { + i--; + break; + } + lua_pushstring(L, argv[i]); + lua_rawseti(L, -2, i + 1); + } + lua_setglobal(L, "arg"); + return i + 1; +} + static void nlua_schedule_event(void **argv) { LuaRef cb = (LuaRef)(ptrdiff_t)argv[0]; -- cgit From 45549f031ee52a01601c33acc411f3111cfc4e95 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 1 Jan 2023 03:14:13 +0100 Subject: feat(lua): send "--" literally to Lua "-l" script Problem: When "-l" is followed by "--", we stop sending args to the Lua script and treat "--" in the usual way. This was for flexibility but didn't have a strong use-case, and has these problems: - prevents Lua "-l" scripts from handling "--" in their own way. - complicates the startup logic (must call nlua_init before command_line_scan) Solution: Don't treat "--" specially if it follows "-l". --- src/nvim/lua/executor.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) (limited to 'src/nvim/lua/executor.c') diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 1bedb70efb..20c901004a 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -323,32 +323,28 @@ static int nlua_thr_api_nvim__get_runtime(lua_State *lstate) return 1; } -/// Copies all args into the Lua `arg` global. +/// Copies args starting at `lua_arg0` into the Lua `arg` global. /// /// Example: -/// nvim -l foo.lua -- -e "sin=math.sin" script a b +/// nvim -l foo.lua --arg1 --arg2 /// /// @note `lua` CLI sets _negative_ `arg` indices to the arguments upto "-e". /// /// @see https://www.lua.org/pil/1.4.html /// @see https://github.com/premake/premake-core/blob/1c1304637f4f5e50ba8c57aae8d1d80ec3b7aaf2/src/host/premake.c#L563-L594 /// -/// @returns number of args (stops at "--") -int nlua_set_argv(char **argv, int argc) +/// @returns number of args +int nlua_set_argv(char **argv, int argc, int lua_arg0) { lua_State *const L = global_lstate; - lua_newtable(L); + lua_newtable(L); // _G.arg int i = 0; - for (; i < argc; i++) { - if (strequal("--", argv[i])) { - i--; - break; - } - lua_pushstring(L, argv[i]); - lua_rawseti(L, -2, i + 1); + for (; lua_arg0 >= 0 && i + lua_arg0 < argc; i++) { + lua_pushstring(L, argv[i + lua_arg0]); + lua_rawseti(L, -2, i + 1); // _G.arg[i+1] = "arg1" } lua_setglobal(L, "arg"); - return i + 1; + return i; } static void nlua_schedule_event(void **argv) -- cgit From adef308a5925a3b967af3bd7c598074e5b6cae18 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 2 Jan 2023 15:34:14 +0100 Subject: feat(lua): exit 1 on Lua "-l" script error --- src/nvim/lua/executor.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'src/nvim/lua/executor.c') diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 20c901004a..4b08603dd0 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -325,10 +325,11 @@ static int nlua_thr_api_nvim__get_runtime(lua_State *lstate) /// Copies args starting at `lua_arg0` into the Lua `arg` global. /// -/// Example: +/// Example (`lua_arg0` points to "--arg1"): /// nvim -l foo.lua --arg1 --arg2 /// -/// @note `lua` CLI sets _negative_ `arg` indices to the arguments upto "-e". +/// @note Lua CLI sets arguments upto "-e" as _negative_ `_G.arg` indices, but we currently don't +/// follow that convention. /// /// @see https://www.lua.org/pil/1.4.html /// @see https://github.com/premake/premake-core/blob/1c1304637f4f5e50ba8c57aae8d1d80ec3b7aaf2/src/host/premake.c#L563-L594 @@ -1710,10 +1711,10 @@ void ex_luafile(exarg_T *const eap) nlua_exec_file((const char *)eap->arg); } -/// execute lua code from a file. +/// Executes Lua code from a file. /// -/// Note: we call the lua global loadfile as opposed to calling luaL_loadfile -/// in case loadfile has been overridden in the users environment. +/// Note: we call the Lua global loadfile as opposed to calling luaL_loadfile +/// in case loadfile was overridden in the user's environment. /// /// @param path path of the file /// -- cgit From f43de742e881e54a3602e00c8c247cecca65a266 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 3 Jan 2023 02:54:53 +0100 Subject: feat(lua): execute stdin ("-") as Lua --- src/nvim/lua/executor.c | 57 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 8 deletions(-) (limited to 'src/nvim/lua/executor.c') diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 4b08603dd0..b7844363c5 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -335,7 +335,7 @@ static int nlua_thr_api_nvim__get_runtime(lua_State *lstate) /// @see https://github.com/premake/premake-core/blob/1c1304637f4f5e50ba8c57aae8d1d80ec3b7aaf2/src/host/premake.c#L563-L594 /// /// @returns number of args -int nlua_set_argv(char **argv, int argc, int lua_arg0) +int nlua_init_argv(char **argv, int argc, int lua_arg0) { lua_State *const L = global_lstate; lua_newtable(L); // _G.arg @@ -1711,21 +1711,62 @@ void ex_luafile(exarg_T *const eap) nlua_exec_file((const char *)eap->arg); } -/// Executes Lua code from a file. +/// Executes Lua code from a file or "-" (stdin). /// -/// Note: we call the Lua global loadfile as opposed to calling luaL_loadfile -/// in case loadfile was overridden in the user's environment. +/// Calls the Lua `loadfile` global as opposed to `luaL_loadfile` in case `loadfile` was overridden +/// in the user environment. /// -/// @param path path of the file +/// @param path Path to the file, may be "-" (stdin) during startup. /// -/// @return true if everything ok, false if there was an error (echoed) +/// @return true on success, false on error (echoed) or user canceled (CTRL-c) while reading "-" +/// (stdin). bool nlua_exec_file(const char *path) FUNC_ATTR_NONNULL_ALL { lua_State *const lstate = global_lstate; + if (!strequal(path, "-")) { + lua_getglobal(lstate, "loadfile"); + lua_pushstring(lstate, path); + } else { + int error; + int stdin_dup_fd; + if (stdin_fd > 0) { + stdin_dup_fd = stdin_fd; + } else { + stdin_dup_fd = os_dup(STDIN_FILENO); +#ifdef MSWIN + // Replace the original stdin with the console input handle. + os_replace_stdin_to_conin(); +#endif + } + FileDescriptor *const stdin_dup = file_open_fd_new(&error, stdin_dup_fd, + kFileReadOnly|kFileNonBlocking); + assert(stdin_dup != NULL); + + StringBuilder sb = KV_INITIAL_VALUE; + kv_resize(sb, 64); + ptrdiff_t read_size = -1; + // Read all input from stdin, unless interrupted (ctrl-c). + while (true) { + if (got_int) { // User canceled. + return false; + } + read_size = file_read(stdin_dup, IObuff, 64); + if (read_size < 0) { // Error. + return false; + } + kv_concat_len(sb, IObuff, (size_t)read_size); + if (read_size < 64) { // EOF. + break; + } + } + kv_push(sb, NUL); + file_free(stdin_dup, false); - lua_getglobal(lstate, "loadfile"); - lua_pushstring(lstate, path); + lua_getglobal(lstate, "loadstring"); + lua_pushstring(lstate, sb.items); + kv_destroy(sb); + } if (nlua_pcall(lstate, 1, 2)) { nlua_error(lstate, _("E5111: Error calling lua: %.*s")); -- cgit From 7089f331447bf335696276e969649fb6ee360e07 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Thu, 5 Jan 2023 09:25:19 +0100 Subject: refactor(lua): move _G.arg init to nlua_init() --- src/nvim/lua/executor.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'src/nvim/lua/executor.c') diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index b7844363c5..c756242817 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -335,9 +335,8 @@ static int nlua_thr_api_nvim__get_runtime(lua_State *lstate) /// @see https://github.com/premake/premake-core/blob/1c1304637f4f5e50ba8c57aae8d1d80ec3b7aaf2/src/host/premake.c#L563-L594 /// /// @returns number of args -int nlua_init_argv(char **argv, int argc, int lua_arg0) +static int nlua_init_argv(lua_State *const L, char **argv, int argc, int lua_arg0) { - lua_State *const L = global_lstate; lua_newtable(L); // _G.arg int i = 0; for (; lua_arg0 >= 0 && i + lua_arg0 < argc; i++) { @@ -790,10 +789,8 @@ static bool nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL return true; } -/// Initialize global lua interpreter -/// -/// Crashes Nvim if initialization fails. -void nlua_init(void) +/// Initializes global Lua interpreter, or exits Nvim on failure. +void nlua_init(char **argv, int argc, int lua_arg0) { #ifdef NLUA_TRACK_REFS const char *env = os_getenv("NVIM_LUA_NOTRACK"); @@ -814,10 +811,9 @@ void nlua_init(void) } luv_set_thread_cb(nlua_thread_acquire_vm, nlua_common_free_all_mem); - global_lstate = lstate; - main_thread = uv_thread_self(); + nlua_init_argv(lstate, argv, argc, lua_arg0); } static lua_State *nlua_thread_acquire_vm(void) -- cgit From 628b717022815a431ea0b980444d6115c644f8c8 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Thu, 5 Jan 2023 17:39:03 +0100 Subject: refactor: extract code to open stdin for reading --- src/nvim/lua/executor.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) (limited to 'src/nvim/lua/executor.c') diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index c756242817..307b34a55c 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -1724,20 +1724,7 @@ bool nlua_exec_file(const char *path) lua_getglobal(lstate, "loadfile"); lua_pushstring(lstate, path); } else { - int error; - int stdin_dup_fd; - if (stdin_fd > 0) { - stdin_dup_fd = stdin_fd; - } else { - stdin_dup_fd = os_dup(STDIN_FILENO); -#ifdef MSWIN - // Replace the original stdin with the console input handle. - os_replace_stdin_to_conin(); -#endif - } - FileDescriptor *const stdin_dup = file_open_fd_new(&error, stdin_dup_fd, - kFileReadOnly|kFileNonBlocking); - assert(stdin_dup != NULL); + FileDescriptor *stdin_dup = file_open_stdin(); StringBuilder sb = KV_INITIAL_VALUE; kv_resize(sb, 64); -- cgit