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/globals.h | 2 +- src/nvim/lua/executor.c | 28 ++++++++++++++++ src/nvim/main.c | 70 ++++++++++++++++++++++++--------------- src/nvim/main.h | 4 ++- src/nvim/testdir/test_startup.vim | 1 + 5 files changed, 77 insertions(+), 28 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 9c4df59819..b5ffabf57c 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -515,7 +515,7 @@ EXTERN int allbuf_lock INIT(= 0); /// not allowed then. EXTERN int sandbox INIT(= 0); -/// Batch-mode: "-es" or "-Es" commandline argument was given. +/// Batch-mode: "-es", "-Es", "-l" commandline argument was given. EXTERN int silent_mode INIT(= false); /// Start position of active Visual selection. 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]; diff --git a/src/nvim/main.c b/src/nvim/main.c index b0da7f4500..77584b049a 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -275,14 +275,14 @@ int main(int argc, char **argv) // Check if we have an interactive window. check_and_set_isatty(¶ms); + // TODO: should we try to keep param scan before this? + nlua_init(); + TIME_MSG("init lua interpreter"); + // Process the command line arguments. File names are put in the global // argument list "global_alist". command_line_scan(¶ms); - nlua_init(); - - TIME_MSG("init lua interpreter"); - if (embedded_mode) { const char *err; if (!channel_from_stdio(true, CALLBACK_READER_INIT, &err)) { @@ -363,8 +363,7 @@ int main(int argc, char **argv) debug_break_level = params.use_debug_break_level; // Read ex-commands if invoked with "-es". - if (!params.input_isatty && !params.input_neverscript - && silent_mode && exmode_active) { + if (!params.input_isatty && !params.input_istext && silent_mode && exmode_active) { input_start(STDIN_FILENO); } @@ -409,14 +408,12 @@ int main(int argc, char **argv) init_default_autocmds(); TIME_MSG("init default autocommands"); - bool vimrc_none = params.use_vimrc != NULL && strequal(params.use_vimrc, "NONE"); + bool vimrc_none = strequal(params.use_vimrc, "NONE"); // Reset 'loadplugins' for "-u NONE" before "--cmd" arguments. // Allows for setting 'loadplugins' there. - if (vimrc_none) { - // When using --clean we still want to load plugins - p_lpl = params.clean; - } + // For --clean we still want to load plugins. + p_lpl = vimrc_none ? params.clean : p_lpl; // Execute --cmd arguments. exe_pre_commands(¶ms); @@ -610,6 +607,11 @@ int main(int argc, char **argv) (void)eval_has_provider("clipboard"); } + if (params.luaf != NULL) { + nlua_exec_file(params.luaf); + // return 0; + } + TIME_MSG("before starting main loop"); ILOG("starting main loop"); @@ -962,7 +964,7 @@ static bool edit_stdin(mparm_T *parmp) { bool implicit = !headless_mode && !(embedded_mode && stdin_fd <= 0) - && (!exmode_active || parmp->input_neverscript) + && (!exmode_active || parmp->input_istext) && !parmp->input_isatty && parmp->scriptin == NULL; // `-s -` was not given. return parmp->had_stdin_file || implicit; @@ -993,9 +995,9 @@ static void command_line_scan(mparm_T *parmp) } else { parmp->commands[parmp->n_commands++] = &(argv[0][1]); } - - // Optional argument. } else if (argv[0][0] == '-' && !had_minmin) { + // Optional argument. + want_argument = false; char c = argv[0][argv_idx++]; switch (c) { @@ -1005,9 +1007,7 @@ static void command_line_scan(mparm_T *parmp) silent_mode = true; parmp->no_swap_file = true; } else { - if (parmp->edit_type != EDIT_NONE - && parmp->edit_type != EDIT_FILE - && parmp->edit_type != EDIT_STDIN) { + if (parmp->edit_type > EDIT_STDIN) { mainerr(err_too_many_args, argv[0]); } parmp->had_stdin_file = true; @@ -1015,7 +1015,7 @@ static void command_line_scan(mparm_T *parmp) } argv_idx = -1; // skip to next argument break; - case '-': // "--" don't take any more option arguments + case '-': // "--" No more option arguments. // "--help" give help message // "--version" give version message // "--noplugin[s]" skip plugins @@ -1111,7 +1111,7 @@ static void command_line_scan(mparm_T *parmp) break; case 'E': // "-E" Ex mode exmode_active = true; - parmp->input_neverscript = true; + parmp->input_istext = true; break; case 'f': // "-f" GUI: run in foreground. break; @@ -1123,10 +1123,6 @@ static void command_line_scan(mparm_T *parmp) p_hkmap = true; set_option_value_give_err("rl", 1L, NULL, 0); break; - case 'l': // "-l" lisp mode, 'lisp' and 'showmatch' on. - set_option_value_give_err("lisp", 1L, NULL, 0); - p_sm = true; - break; case 'M': // "-M" no changes or writing of files reset_modifiable(); FALLTHROUGH; @@ -1231,6 +1227,7 @@ static void command_line_scan(mparm_T *parmp) FALLTHROUGH; case 'S': // "-S {file}" execute Vim script case 'i': // "-i {shada}" use for ShaDa file + case 'l': // "-l {file}" Lua mode case 'u': // "-u {vimrc}" vim inits file case 'U': // "-U {gvimrc}" gvim inits file case 'W': // "-W {scriptout}" overwrite @@ -1311,6 +1308,27 @@ static void command_line_scan(mparm_T *parmp) set_option_value_give_err("shadafile", 0L, argv[0], 0); break; + case 'l': // "-l" Lua script: args after "-l". + silent_mode = true; + p_verbose = 1; + parmp->no_swap_file = true; + parmp->use_vimrc = parmp->use_vimrc ? parmp->use_vimrc : "NONE"; + if (p_shadafile == NULL || *p_shadafile == NUL) { + set_option_value_give_err("shadafile", 0L, "NONE", 0); + } + parmp->luaf = argv[0]; + argc--; + argv++; + // Lua args after "-l " (upto "--"). + int l_argc = nlua_set_argv(argv, argc); + assert(l_argc >= 0); + argc = argc - l_argc; + if (argc > 0) { // Found "--". + argv = argv + l_argc; + had_minmin = true; + } + break; + case 's': // "-s {scriptin}" read from script file if (parmp->scriptin != NULL) { scripterror: @@ -1354,9 +1372,7 @@ scripterror: argv_idx = -1; // skip to next argument // Check for only one type of editing. - if (parmp->edit_type != EDIT_NONE - && parmp->edit_type != EDIT_FILE - && parmp->edit_type != EDIT_STDIN) { + if (parmp->edit_type > EDIT_STDIN) { mainerr(err_too_many_args, argv[0]); } parmp->edit_type = EDIT_FILE; @@ -1421,6 +1437,7 @@ static void init_params(mparm_T *paramp, int argc, char **argv) paramp->listen_addr = NULL; paramp->server_addr = NULL; paramp->remote = 0; + paramp->luaf = NULL; } /// Initialize global startuptime file if "--startuptime" passed as an argument. @@ -2154,6 +2171,7 @@ static void usage(void) os_msg(_(" + Start at end of file\n")); os_msg(_(" --cmd Execute before any config\n")); os_msg(_(" +, -c Execute after config and first file\n")); + os_msg(_(" -l