aboutsummaryrefslogtreecommitdiff
path: root/src/nvim
diff options
context:
space:
mode:
authorJustin M. Keyes <justinkz@gmail.com>2023-01-05 13:06:21 -0500
committerGitHub <noreply@github.com>2023-01-05 13:06:21 -0500
commit08ebf8d3a80c65b01d493ca84ad2ab7304a669f9 (patch)
tree77f73c6e88134d702ca275e43b45f5316ba5b47e /src/nvim
parent39d70fcafd6efa9d01b88bb90cab81c393040453 (diff)
parent628b717022815a431ea0b980444d6115c644f8c8 (diff)
downloadrneovim-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.md6
-rw-r--r--src/nvim/globals.h4
-rw-r--r--src/nvim/lua/executor.c75
-rw-r--r--src/nvim/main.c96
-rw-r--r--src/nvim/main.h5
-rw-r--r--src/nvim/message.c33
-rw-r--r--src/nvim/os/fileio.c24
-rw-r--r--src/nvim/testdir/test_startup.vim1
-rw-r--r--src/nvim/vim.h6
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(&params);
- 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(&params);
@@ -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