diff options
author | Justin M. Keyes <justinkz@gmail.com> | 2023-01-05 13:06:21 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-01-05 13:06:21 -0500 |
commit | 08ebf8d3a80c65b01d493ca84ad2ab7304a669f9 (patch) | |
tree | 77f73c6e88134d702ca275e43b45f5316ba5b47e /src/nvim | |
parent | 39d70fcafd6efa9d01b88bb90cab81c393040453 (diff) | |
parent | 628b717022815a431ea0b980444d6115c644f8c8 (diff) | |
download | rneovim-08ebf8d3a80c65b01d493ca84ad2ab7304a669f9.tar.gz rneovim-08ebf8d3a80c65b01d493ca84ad2ab7304a669f9.tar.bz2 rneovim-08ebf8d3a80c65b01d493ca84ad2ab7304a669f9.zip |
Merge #18706 execute Lua with "nvim -l"
Diffstat (limited to 'src/nvim')
-rw-r--r-- | src/nvim/README.md | 6 | ||||
-rw-r--r-- | src/nvim/globals.h | 4 | ||||
-rw-r--r-- | src/nvim/lua/executor.c | 75 | ||||
-rw-r--r-- | src/nvim/main.c | 96 | ||||
-rw-r--r-- | src/nvim/main.h | 5 | ||||
-rw-r--r-- | src/nvim/message.c | 33 | ||||
-rw-r--r-- | src/nvim/os/fileio.c | 24 | ||||
-rw-r--r-- | src/nvim/testdir/test_startup.vim | 1 | ||||
-rw-r--r-- | src/nvim/vim.h | 6 |
9 files changed, 169 insertions, 81 deletions
diff --git a/src/nvim/README.md b/src/nvim/README.md index e710c3ef58..6876227e48 100644 --- a/src/nvim/README.md +++ b/src/nvim/README.md @@ -18,6 +18,12 @@ The source files use extensions to hint about their purpose. - `*.h.generated.h` - exported functions’ declarations. - `*.c.generated.h` - static functions’ declarations. +Common structures +----------------- + +- StringBuilder +- kvec or garray.c for dynamic lists / vectors (use StringBuilder for strings) + Logs ---- diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 9c4df59819..bb7c519b7c 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. @@ -1042,7 +1042,7 @@ EXTERN int vim_ignored; // stdio is an RPC channel (--embed). EXTERN bool embedded_mode INIT(= false); -// Do not start a UI nor read/write to stdio (unless embedding). +// Do not start UI (--headless, -l) nor read/write to stdio (unless embedding). EXTERN bool headless_mode INIT(= false); // uncrustify:on diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 34b572f884..307b34a55c 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -323,6 +323,30 @@ static int nlua_thr_api_nvim__get_runtime(lua_State *lstate) return 1; } +/// Copies args starting at `lua_arg0` into the Lua `arg` global. +/// +/// Example (`lua_arg0` points to "--arg1"): +/// nvim -l foo.lua --arg1 --arg2 +/// +/// @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 +/// +/// @returns number of args +static int nlua_init_argv(lua_State *const L, char **argv, int argc, int lua_arg0) +{ + lua_newtable(L); // _G.arg + int i = 0; + 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; +} + static void nlua_schedule_event(void **argv) { LuaRef cb = (LuaRef)(ptrdiff_t)argv[0]; @@ -765,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"); @@ -789,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) @@ -1686,21 +1707,49 @@ 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 or "-" (stdin). /// -/// Note: we call the lua global loadfile as opposed to calling luaL_loadfile -/// in case loadfile has been overridden in the users 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 { + FileDescriptor *stdin_dup = file_open_stdin(); + + 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")); diff --git a/src/nvim/main.c b/src/nvim/main.c index b0da7f4500..9bb9eea4f6 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -279,8 +279,7 @@ int main(int argc, char **argv) // argument list "global_alist". command_line_scan(¶ms); - nlua_init(); - + nlua_init(argv, argc, params.lua_arg0); TIME_MSG("init lua interpreter"); if (embedded_mode) { @@ -363,8 +362,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 +407,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 +606,12 @@ int main(int argc, char **argv) (void)eval_has_provider("clipboard"); } + if (params.luaf != NULL) { + bool lua_ok = nlua_exec_file(params.luaf); + TIME_MSG("executing Lua -l script"); + getout(lua_ok ? 0 : 1); + } + TIME_MSG("before starting main loop"); ILOG("starting main loop"); @@ -657,9 +659,8 @@ void getout(int exitval) { exiting = true; - // When running in Ex mode an error causes us to exit with a non-zero exit - // code. POSIX requires this, although it's not 100% clear from the - // standard. + // On error during Ex mode, exit with a non-zero code. + // POSIX requires this, although it's not 100% clear from the standard. if (exmode_active) { exitval += ex_exitval; } @@ -750,6 +751,7 @@ void getout(int exitval) if (did_emsg) { // give the user a chance to read the (error) message no_wait_return = false; + // TODO(justinmk): this may call getout(0), clobbering exitval... wait_return(false); } @@ -773,10 +775,9 @@ void getout(int exitval) os_exit(exitval); } -/// Preserve files and exit. -/// @note IObuff must contain a message. -/// @note This may be called from deadly_signal() in a signal handler, avoid -/// unsafe functions, such as allocating memory. +/// Preserve files, print contents of `IObuff`, and exit 1. +/// +/// May be called from deadly_signal(). void preserve_exit(void) FUNC_ATTR_NORETURN { @@ -962,7 +963,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 +994,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 +1006,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 +1014,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 +1110,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 +1122,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 +1226,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 +1307,23 @@ 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". + headless_mode = true; + 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--; + if (argc > 0) { // Lua args after "-l <file>". + parmp->lua_arg0 = parmp->argc - argc; + argc = 0; + } + break; + case 's': // "-s {scriptin}" read from script file if (parmp->scriptin != NULL) { scripterror: @@ -1354,9 +1367,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; @@ -1392,8 +1403,8 @@ scripterror: } } - if (embedded_mode && silent_mode) { - mainerr(_("--embed conflicts with -es/-Es"), NULL); + if (embedded_mode && (silent_mode || parmp->luaf)) { + mainerr(_("--embed conflicts with -es/-Es/-l"), NULL); } // If there is a "+123" or "-c" command, set v:swapcommand to the first one. @@ -1421,6 +1432,8 @@ 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; + paramp->lua_arg0 = -1; } /// Initialize global startuptime file if "--startuptime" passed as an argument. @@ -1554,19 +1567,7 @@ static void open_script_files(mparm_T *parmp) if (parmp->scriptin) { int error; if (strequal(parmp->scriptin, "-")) { - 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(); scriptin[0] = stdin_dup; } else { scriptin[0] = file_open_new(&error, parmp->scriptin, @@ -2133,7 +2134,7 @@ static void mainerr(const char *errstr, const char *str) static void version(void) { // TODO(bfred): not like this? - nlua_init(); + nlua_init(NULL, 0, -1); info_message = true; // use os_msg(), not os_errmsg() list_version(); msg_putchar('\n'); @@ -2154,6 +2155,7 @@ static void usage(void) os_msg(_(" + Start at end of file\n")); os_msg(_(" --cmd <cmd> Execute <cmd> before any config\n")); os_msg(_(" +<cmd>, -c <cmd> Execute <cmd> after config and first file\n")); + os_msg(_(" -l <script> [args...] Execute Lua <script> (with optional args)\n")); os_msg("\n"); os_msg(_(" -b Binary mode\n")); os_msg(_(" -d Diff mode\n")); diff --git a/src/nvim/main.h b/src/nvim/main.h index c39fc1ed4a..46d7217364 100644 --- a/src/nvim/main.h +++ b/src/nvim/main.h @@ -23,15 +23,18 @@ typedef struct { char cmds_tofree[MAX_ARG_CMDS]; // commands that need free() int n_pre_commands; // no. of commands from --cmd char *pre_commands[MAX_ARG_CMDS]; // commands from --cmd argument + char *luaf; // Lua script filename from "-l" + int lua_arg0; // Lua script args start index. int edit_type; // type of editing to do char *tagname; // tag from -t argument char *use_ef; // 'errorfile' from -q argument bool input_isatty; // stdin is a terminal + bool input_istext; // stdin is text, not executable (-E/-Es) bool output_isatty; // stdout is a terminal bool err_isatty; // stderr is a terminal - bool input_neverscript; // never treat stdin as script (-E/-Es) + int no_swap_file; // "-n" argument used int use_debug_break_level; int window_count; // number of windows to use diff --git a/src/nvim/message.c b/src/nvim/message.c index 041e5ed6c3..118d144617 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -3003,31 +3003,36 @@ static int do_more_prompt(int typed_char) } #if defined(MSWIN) -void os_errmsg(char *str) +/// Headless (no UI) error message handler. +static void do_msg(char *str, bool errmsg) { + static bool did_err = false; assert(str != NULL); wchar_t *utf16str; int r = utf8_to_utf16(str, -1, &utf16str); - if (r != 0) { + if (r != 0 && !did_err) { + did_err = true; fprintf(stderr, "utf8_to_utf16 failed: %d", r); - } else { - fwprintf(stderr, L"%ls", utf16str); + ELOG("utf8_to_utf16 failed: %d", r); + } else if (r == 0) { + if (errmsg) { + fwprintf(stderr, L"%ls", utf16str); + } else { + wprintf(L"%ls", utf16str); + } xfree(utf16str); } } -/// Give a message. To be used when the UI is not initialized yet. +void os_errmsg(char *str) +{ + do_msg(str, true); +} + +/// Headless (no UI) message handler. void os_msg(char *str) { - assert(str != NULL); - wchar_t *utf16str; - int r = utf8_to_utf16(str, -1, &utf16str); - if (r != 0) { - fprintf(stderr, "utf8_to_utf16 failed: %d", r); - } else { - wprintf(L"%ls", utf16str); - xfree(utf16str); - } + do_msg(str, false); } #endif // MSWIN diff --git a/src/nvim/os/fileio.c b/src/nvim/os/fileio.c index bdea82f1ff..e93e1febcb 100644 --- a/src/nvim/os/fileio.c +++ b/src/nvim/os/fileio.c @@ -167,6 +167,30 @@ FileDescriptor *file_open_fd_new(int *const error, const int fd, const int flags return fp; } +/// Opens standard input as a FileDescriptor. +FileDescriptor *file_open_stdin(void) + FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT +{ + 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); + if (error != 0) { + ELOG("failed to open stdin: %s", os_strerror(error)); + } + return stdin_dup; +} + /// Close file and free its buffer /// /// @param[in,out] fp File to close. diff --git a/src/nvim/testdir/test_startup.vim b/src/nvim/testdir/test_startup.vim index ee0445c6b4..54b7636f69 100644 --- a/src/nvim/testdir/test_startup.vim +++ b/src/nvim/testdir/test_startup.vim @@ -962,6 +962,7 @@ endfunc " Test for enabling the lisp mode on startup func Test_l_arg() + throw 'Skipped: Nvim -l arg differs from Vim' let after =<< trim [CODE] let s = 'lisp=' .. &lisp .. ', showmatch=' .. &showmatch call writefile([s], 'Xtestout') diff --git a/src/nvim/vim.h b/src/nvim/vim.h index bfe75ef9f7..df24e423cf 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -245,12 +245,10 @@ enum { FOLD_TEXT_LEN = 51, }; //!< buffer size for get_foldtext() /// plus six following composing characters of three bytes each. #define MB_MAXBYTES 21 -// This has to go after the include of proto.h, as proto/gui.pro declares -// functions of these names. The declarations would break if the defines had -// been seen at that stage. But it must be before globals.h, where error_ga -// is declared. #ifndef MSWIN +/// Headless (no UI) error message handler. # define os_errmsg(str) fprintf(stderr, "%s", (str)) +/// Headless (no UI) message handler. # define os_msg(str) printf("%s", (str)) #endif |