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".
This commit is contained in:
Justin M. Keyes
2023-01-01 03:14:13 +01:00
parent 599e1d019a
commit 45549f031e
7 changed files with 74 additions and 53 deletions

View File

@@ -32,7 +32,7 @@ filename One or more file names. The first one will be the current
an option, precede the arglist with "--", e.g.: > an option, precede the arglist with "--", e.g.: >
nvim -- -filename nvim -- -filename
< All arguments after "--" are interpreted as file names, no < All arguments after "--" are interpreted as file names, no
other options or "+command" argument can follow. other options or "+command" arguments can follow.
*--* *--*
`-` Alias for stdin (standard input). `-` Alias for stdin (standard input).
@@ -143,9 +143,9 @@ argument.
these commands, independently from "-c" commands. these commands, independently from "-c" commands.
*-S* *-S*
-S [file] Vimscript or Lua (".lua") [file] will be |:source|d after the -S [file] Executes Vimscript or Lua (".lua") [file] after the first file
first file has been read or "Session.vim" if [file] is not has been read. See also |:source|. If [file] is not given,
given. Equivalent to: > defaults to "Session.vim". Equivalent to: >
-c "source {file}" -c "source {file}"
< Can be repeated like "-c", subject to the same limit of 10 < Can be repeated like "-c", subject to the same limit of 10
"-c" arguments. {file} cannot start with a "-". "-c" arguments. {file} cannot start with a "-".
@@ -190,8 +190,9 @@ argument.
-E reads stdin as text (into buffer 1). -E reads stdin as text (into buffer 1).
-es *-es* *-Es* *-s-ex* *silent-mode* -es *-es* *-Es* *-s-ex* *silent-mode*
-Es Silent mode (no UI), for scripting. Unrelated to |-s|. -Es Script mode, aka "silent mode", aka "batch mode". No UI,
Disables most prompts, messages, warnings and errors. disables most prompts and messages. Unrelated to |-s|.
See also |-S| to run script files.
-es reads/executes stdin as Ex commands. > -es reads/executes stdin as Ex commands. >
printf "put ='foo'\n%%print\n" | nvim -es printf "put ='foo'\n%%print\n" | nvim -es
@@ -215,16 +216,22 @@ argument.
*-l* *-l*
-l {script} [args] -l {script} [args]
Executes Lua {script} file and exits. All [args] (up to "--" Executes Lua {script} non-interactively (no UI) with optional
|---|) are treated as {script} args, not Nvim args: by Lua [args] after processing any preceding Nvim |cli-arguments|,
convention they are set in the `_G.arg` global table. *lua-args* then exits. See |-S| to run multiple Lua scripts without args,
On {script} error, Nvim exits with code 1. or in an interactive session.
*lua-args*
All [args] are treated as {script} arguments and passed
literally to Lua (in the conventional `_G.arg` global table),
thus "-l" ends processing of Nvim arguments.
Exits with code 1 on Lua error.
Sets 'verbose' to 1 (like "-V1"), so Lua `print()` writes to Sets 'verbose' to 1 (like "-V1"), so Lua `print()` writes to
output. output.
Any |cli-arguments| before "-l" are processed before executing Arguments before "-l" are processed before executing {script}.
{script}. For example this quits before executing "foo.lua": > This example quits before executing "foo.lua": >
nvim +q -l foo.lua nvim +q -l foo.lua
< This loads Lua module "bar" before executing "foo.lua": > < This loads Lua module "bar" before executing "foo.lua": >
nvim +"lua require('bar')" -l foo.lua nvim +"lua require('bar')" -l foo.lua
@@ -256,7 +263,7 @@ argument.
-V[N]{file} -V[N]{file}
Like -V and sets 'verbosefile' to {file} (must not start with Like -V and sets 'verbosefile' to {file} (must not start with
a digit). Messages are not displayed; instead they are a digit). Messages are not displayed, instead they are
written to {file}. written to {file}.
Example: > Example: >
nvim -V20vimlog nvim -V20vimlog

View File

@@ -122,9 +122,6 @@ modifications.
.It Fl b .It Fl b
Binary mode. Binary mode.
.Ic ":help edit-binary" .Ic ":help edit-binary"
.It Fl l
Lisp mode.
Sets the 'lisp' and 'showmatch' options.
.It Fl A .It Fl A
Arabic mode. Arabic mode.
Sets the 'arabic' option. Sets the 'arabic' option.
@@ -144,7 +141,7 @@ is specified, append messages to
instead of printing them. instead of printing them.
.Ic ":help 'verbose'" .Ic ":help 'verbose'"
.It Fl D .It Fl D
Debug mode for VimL (Vim script). Vimscript debug mode.
Started when executing the first command from a script. Started when executing the first command from a script.
:help debug-mode :help debug-mode
.It Fl n .It Fl n
@@ -268,10 +265,26 @@ but execute
before processing any vimrc. before processing any vimrc.
Up to 10 instances of these can be used independently from instances of Up to 10 instances of these can be used independently from instances of
.Fl c . .Fl c .
.It Fl l Ar script Op Ar args
Execute Lua
.Ar script
with optional
.Op Ar args
after processing any preceding Nvim startup arguments.
All
.Op Ar args
are treated as script arguments and are passed literally to Lua, that is,
.Fl l
stops processing of Nvim arguments.
.Ic ":help -l"
.It Fl S Op Ar session .It Fl S Op Ar session
Source Execute
.Ar session .Ar session
after the first file argument has been read. after the first file argument has been read. If
.Ar session
filename ends with
.Pa .lua
it is executed as Lua instead of Vimscript.
Equivalent to Equivalent to
.Cm -c \(dqsource session\(dq . .Cm -c \(dqsource session\(dq .
.Ar session .Ar session

View File

@@ -323,32 +323,28 @@ static int nlua_thr_api_nvim__get_runtime(lua_State *lstate)
return 1; return 1;
} }
/// Copies all args into the Lua `arg` global. /// Copies args starting at `lua_arg0` into the Lua `arg` global.
/// ///
/// Example: /// 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". /// @note `lua` CLI sets _negative_ `arg` indices to the arguments upto "-e".
/// ///
/// @see https://www.lua.org/pil/1.4.html /// @see https://www.lua.org/pil/1.4.html
/// @see https://github.com/premake/premake-core/blob/1c1304637f4f5e50ba8c57aae8d1d80ec3b7aaf2/src/host/premake.c#L563-L594 /// @see https://github.com/premake/premake-core/blob/1c1304637f4f5e50ba8c57aae8d1d80ec3b7aaf2/src/host/premake.c#L563-L594
/// ///
/// @returns number of args (stops at "--") /// @returns number of args
int nlua_set_argv(char **argv, int argc) int nlua_set_argv(char **argv, int argc, int lua_arg0)
{ {
lua_State *const L = global_lstate; lua_State *const L = global_lstate;
lua_newtable(L); lua_newtable(L); // _G.arg
int i = 0; int i = 0;
for (; i < argc; i++) { for (; lua_arg0 >= 0 && i + lua_arg0 < argc; i++) {
if (strequal("--", argv[i])) { lua_pushstring(L, argv[i + lua_arg0]);
i--; lua_rawseti(L, -2, i + 1); // _G.arg[i+1] = "arg1"
break;
}
lua_pushstring(L, argv[i]);
lua_rawseti(L, -2, i + 1);
} }
lua_setglobal(L, "arg"); lua_setglobal(L, "arg");
return i + 1; return i;
} }
static void nlua_schedule_event(void **argv) static void nlua_schedule_event(void **argv)

View File

@@ -275,14 +275,14 @@ int main(int argc, char **argv)
// Check if we have an interactive window. // Check if we have an interactive window.
check_and_set_isatty(&params); check_and_set_isatty(&params);
// 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 // Process the command line arguments. File names are put in the global
// argument list "global_alist". // argument list "global_alist".
command_line_scan(&params); command_line_scan(&params);
nlua_init();
nlua_set_argv(argv, argc, params.lua_arg0);
TIME_MSG("init lua interpreter");
if (embedded_mode) { if (embedded_mode) {
const char *err; const char *err;
if (!channel_from_stdio(true, CALLBACK_READER_INIT, &err)) { if (!channel_from_stdio(true, CALLBACK_READER_INIT, &err)) {
@@ -1318,14 +1318,9 @@ static void command_line_scan(mparm_T *parmp)
} }
parmp->luaf = argv[0]; parmp->luaf = argv[0];
argc--; argc--;
argv++; if (argc > 0) { // Lua args after "-l <file>".
// Lua args after "-l <file>" (upto "--"). parmp->lua_arg0 = parmp->argc - argc;
int l_argc = nlua_set_argv(argv, argc); argc = 0;
assert(l_argc >= 0);
argc = argc - l_argc;
if (argc > 0) { // Found "--".
argv = argv + l_argc;
had_minmin = true;
} }
break; break;
@@ -1438,6 +1433,7 @@ static void init_params(mparm_T *paramp, int argc, char **argv)
paramp->server_addr = NULL; paramp->server_addr = NULL;
paramp->remote = 0; paramp->remote = 0;
paramp->luaf = NULL; paramp->luaf = NULL;
paramp->lua_arg0 = -1;
} }
/// Initialize global startuptime file if "--startuptime" passed as an argument. /// Initialize global startuptime file if "--startuptime" passed as an argument.

View File

@@ -24,6 +24,7 @@ typedef struct {
int n_pre_commands; // no. of commands from --cmd int n_pre_commands; // no. of commands from --cmd
char *pre_commands[MAX_ARG_CMDS]; // commands from --cmd argument char *pre_commands[MAX_ARG_CMDS]; // commands from --cmd argument
char *luaf; // Lua script filename from "-l" char *luaf; // Lua script filename from "-l"
int lua_arg0; // Lua script args start index.
int edit_type; // type of editing to do int edit_type; // type of editing to do
char *tagname; // tag from -t argument char *tagname; // tag from -t argument

View File

@@ -107,7 +107,8 @@ describe('startup', function()
-- nvim -l foo.lua -arg1 -- a b c -- nvim -l foo.lua -arg1 -- a b c
assert_l_out([[ assert_l_out([[
bufs: bufs:
args: { "-arg1", "--exitcode", "73", "--arg2" }]], nvim args: 8
lua args: { "-arg1", "--exitcode", "73", "--arg2" }]],
{}, {},
{ '-arg1', "--exitcode", "73", '--arg2' } { '-arg1', "--exitcode", "73", '--arg2' }
) )
@@ -118,7 +119,8 @@ describe('startup', function()
-- nvim -l foo.lua -arg1 -- a b c -- nvim -l foo.lua -arg1 -- a b c
assert_l_out([[ assert_l_out([[
bufs: bufs:
args: { "-arg1", "--arg2", "arg3" }]], nvim args: 7
lua args: { "-arg1", "--arg2", "arg3" }]],
{}, {},
{ '-arg1', '--arg2', 'arg3' } { '-arg1', '--arg2', 'arg3' }
) )
@@ -127,7 +129,8 @@ describe('startup', function()
-- nvim -l foo.lua -- -- nvim -l foo.lua --
assert_l_out([[ assert_l_out([[
bufs: bufs:
args: {}]], nvim args: 5
lua args: { "--" }]],
{}, {},
{ '--' } { '--' }
) )
@@ -135,8 +138,9 @@ describe('startup', function()
-- nvim file1 file2 -l foo.lua -arg1 -- file3 file4 -- nvim file1 file2 -l foo.lua -arg1 -- file3 file4
assert_l_out([[ assert_l_out([[
bufs: file1 file2 file3 file4 bufs: file1 file2
args: { "-arg1", "arg 2" }]], nvim args: 11
lua args: { "-arg1", "arg 2", "--", "file3", "file4" }]],
{ 'file1', 'file2', }, { 'file1', 'file2', },
{ '-arg1', 'arg 2', '--', 'file3', 'file4' } { '-arg1', 'arg 2', '--', 'file3', 'file4' }
) )
@@ -145,7 +149,8 @@ describe('startup', function()
-- nvim file1 file2 -l foo.lua -arg1 -- -- nvim file1 file2 -l foo.lua -arg1 --
assert_l_out([[ assert_l_out([[
bufs: file1 file2 bufs: file1 file2
args: { "-arg1" }]], nvim args: 8
lua args: { "-arg1", "--" }]],
{ 'file1', 'file2', }, { 'file1', 'file2', },
{ '-arg1', '--' } { '-arg1', '--' }
) )
@@ -154,7 +159,8 @@ describe('startup', function()
-- nvim -l foo.lua <vim args> -- nvim -l foo.lua <vim args>
assert_l_out([[ assert_l_out([[
bufs: bufs:
args: { "-c", "set wrap?" }]], nvim args: 6
lua args: { "-c", "set wrap?" }]],
{}, {},
{ '-c', 'set wrap?' } { '-c', 'set wrap?' }
) )
@@ -167,7 +173,8 @@ describe('startup', function()
wrap wrap
bufs: bufs:
args: { "-c", "set wrap?" }]], nvim args: 8
lua args: { "-c", "set wrap?" }]],
{ '-c', 'set wrap?' }, { '-c', 'set wrap?' },
{ '-c', 'set wrap?' } { '-c', 'set wrap?' }
) )

View File

@@ -23,7 +23,8 @@ end
local function main() local function main()
printbufs() printbufs()
print('args:', vim.inspect(_G.arg)) print('nvim args:', #vim.v.argv)
print('lua args:', vim.inspect(_G.arg))
local exitcode = parseargs(_G.arg) local exitcode = parseargs(_G.arg)
if type(exitcode) == 'number' then if type(exitcode) == 'number' then