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_.
This commit is contained in:
Justin M. Keyes 2021-09-20 19:00:50 -07:00
parent 39d70fcafd
commit 7c94bcd2d7
16 changed files with 311 additions and 2784 deletions

View File

@ -205,6 +205,8 @@ jobs:
env:
CC: ${{ matrix.cc }}
CI_OS_NAME: ${{ matrix.os }}
# TEST_FILE: test/functional/core/startup_spec.lua
# TEST_FILTER: foo
steps:
- uses: actions/checkout@v3
@ -281,6 +283,8 @@ jobs:
DEPS_BUILD_DIR: ${{ github.workspace }}/nvim-deps
CACHE_NVIM_DEPS_DIR: ${{ github.workspace }}/nvim-deps
DEPS_PREFIX: ${{ github.workspace }}/nvim-deps/usr
# TEST_FILE: test/functional/core/startup_spec.lua
# TEST_FILTER: foo
name: windows (MSVC_64)
steps:
- uses: actions/checkout@v3

View File

@ -116,10 +116,7 @@ Each pull request must pass the automated builds on [Cirrus CI] and [GitHub Acti
- CI builds are compiled with [`-Werror`][gcc-warnings], so compiler warnings
will fail the build.
- If any tests fail, the build will fail.
See [test/README.md#running-tests][run-tests] to run tests locally.
Passing locally doesn't guarantee passing the CI build, because of the
different compilers and platforms tested against.
- If any tests fail, the build will fail. See [test/README.md#running-tests][run-tests] to run tests locally.
- CI runs [ASan] and other analyzers.
- To run valgrind locally: `VALGRIND=1 make test`
- To run Clang ASan/UBSan locally: `CC=clang make CMAKE_FLAGS="-DCLANG_ASAN_UBSAN=ON"`
@ -127,6 +124,8 @@ Each pull request must pass the automated builds on [Cirrus CI] and [GitHub Acti
neighbors_, to encourage incrementally updating the legacy style to meet our
[style](#style). (See [#3174][3174] for background.)
- CI for FreeBSD runs on [Cirrus CI].
- To see CI results faster in your PR, you can temporarily set `TEST_FILE` in
[ci.yml](https://github.com/neovim/neovim/blob/e35b9020b16985eee26e942f9a3f6b045bc3809b/.github/workflows/ci.yml#L205).
### Clang scan-build

View File

@ -21,15 +21,19 @@ Nvim includes a "standard library" |lua-stdlib| for Lua. It complements the
which can be used from Lua code (|lua-vimscript| |vim.api|). Together these
"namespaces" form the Nvim programming interface.
See the |lua-guide| for an introduction to using Lua in Neovim.
Lua plugins and user config are automatically discovered and loaded, just like
Vimscript. See |lua-guide| for practical guidance.
You can also run Lua scripts from your shell using the |-l| argument: >
nvim -l foo.lua [args...]
<
*lua-compat*
Lua 5.1 is the permanent interface for Nvim Lua. Plugins need only consider
Lua 5.1, not worry about forward-compatibility with future Lua versions. If
Nvim ever ships with Lua 5.4+, a Lua 5.1 compatibility shim will be provided
so that old plugins continue to work transparently.
------------------------------------------------------------------------------
==============================================================================
LUA CONCEPTS AND IDIOMS *lua-concepts*
Lua is very simple: this means that, while there are some quirks, once you
@ -73,25 +77,24 @@ In Lua, any missing arguments are passed as `nil`. Example: >lua
Furthermore it is not an error if extra parameters are passed, they are just
discarded.
It is also allowed to omit the parentheses (only) if the function takes
exactly one string (`"foo"`) or table literal (`{1,2,3}`). The latter is often
used to approximate the "named parameters" feature of languages like Python
("kwargs" or "keyword args"). Example: >lua
*kwargs*
When calling a function, you can omit the parentheses if the function takes
exactly one string literal (`"foo"`) or table literal (`{1,2,3}`). The latter
is often used to approximate "named parameters" ("kwargs" or "keyword args")
as in languages like Python and C#. Example: >lua
local func_with_opts = function(opts)
local will_do_foo = opts.foo
local filename = opts.filename
...
end
func_with_opts { foo = true, filename = "hello.world" }
<
There is nothing special going on here except that parentheses are treated as
There's nothing special going on here except that parentheses are treated as
whitespace. But visually, this small bit of sugar gets reasonably close to
a "keyword args" interface.
It is of course also valid to call the function with parentheses: >lua
func_with_opts({ foo = true, filename = "hello.world" })
<
Nvim tends to prefer the keyword args style.
@ -100,25 +103,20 @@ Nvim tends to prefer the keyword args style.
LUA PATTERNS *lua-patterns*
Lua intentionally does not support regular expressions, instead it has limited
"patterns" which avoid the performance pitfalls of extended regex.
|luaref-patterns|
"patterns" |luaref-patterns| which avoid the performance pitfalls of extended
regex. Lua scripts can also use Vim regex via |vim.regex()|.
Examples using |string.match()|: >lua
These examples use |string.match()| to demonstrate Lua patterns: >lua
print(string.match("foo123bar123", "%d+"))
-- 123
print(string.match("foo123bar123", "[^%d]+"))
-- foo
print(string.match("foo123bar123", "[abc]+"))
-- ba
print(string.match("foo.bar", "%.bar"))
-- .bar
For more complex matching you can use Vim regex from Lua via |vim.regex()|.
==============================================================================
IMPORTING LUA MODULES *require()* *lua-require*
@ -389,7 +387,7 @@ For example consider the following Lua omnifunc handler: >lua
vim.api.nvim_buf_set_option(0, 'omnifunc', 'v:lua.mymod.omnifunc')
Note: The module ("mymod" in the above example) must either be a Lua global,
or use the require syntax as specified above to access it from a package.
or use require() as shown above to access it from a package.
Note: `v:lua` without a call is not allowed in a Vimscript expression:
|Funcref|s cannot represent Lua functions. The following are errors: >vim

View File

@ -57,6 +57,11 @@ The following new APIs or features were added.
<
(or the Vimscript equivalent) to their |config| file.
• Run Lua scripts from your shell using |-l|. >
nvim -l foo.lua --arg1 --arg2
< Also works with stdin: >
echo "print(42)" | nvim -l -
• Added a |vim.lsp.codelens.clear()| function to clear codelenses.
• |vim.inspect_pos()|, |vim.show_pos()| and |:Inspect| allow a user to get or show items

View File

@ -9,7 +9,7 @@ Starting Vim *starting*
Type |gO| to see the table of contents.
==============================================================================
Nvim arguments *vim-arguments*
Nvim arguments *cli-arguments*
Most often, Nvim is started to edit a single file with the command: >
@ -31,8 +31,8 @@ filename One or more file names. The first one will be the current
To avoid a file name starting with a '-' being interpreted as
an option, precede the arglist with "--", e.g.: >
nvim -- -filename
< All arguments after the "--" will be interpreted as file names,
no other options or "+command" argument can follow.
< All arguments after "--" are interpreted as file names, no
other options or "+command" argument can follow.
*--*
`-` Alias for stdin (standard input).
@ -143,15 +143,13 @@ argument.
these commands, independently from "-c" commands.
*-S*
-S {file} Vimscript or Lua (".lua") {file} will be |:source|d after the
first file has been read. Equivalent to: >
-S [file] Vimscript or Lua (".lua") [file] will be |:source|d after the
first file has been read or "Session.vim" if [file] is not
given. Equivalent to: >
-c "source {file}"
< Can be repeated like "-c", subject to the same limit of 10
"-c" arguments. {file} cannot start with a "-".
-S Works like "-S Session.vim". Only when used as the last
argument or when another "-" option follows.
-L *-L* *-r*
-r Recovery mode. Without a file name argument, a list of
existing swap files is given. With a file name, a swap file
@ -211,10 +209,30 @@ argument.
nvim -es +":verbose echo 'foo'"
nvim -V1 -es +foo
< User |config| is skipped (unless given with |-u|).
< User |config| is skipped unless |-u| was given.
Swap file is skipped (like |-n|).
User |shada| is loaded (unless "-i NONE" is given).
*-l*
-l {script} [args]
Executes Lua {script} file and exits. All [args] (up to "--"
|---|) are treated as {script} args, not Nvim args: by Lua
convention they are set in the `_G.arg` global table. *lua-args*
On {script} error, Nvim exits with code 1.
Sets 'verbose' to 1 (like "-V1"), so Lua `print()` writes to
output.
Any |cli-arguments| before "-l" are processed before executing
{script}. For example this quits before executing "foo.lua": >
nvim +q -l foo.lua
< This loads Lua module "bar" before executing "foo.lua": >
nvim +"lua require('bar')" -l foo.lua
<
User |config| is skipped unless |-u| was given.
User |shada| is skipped unless |-i| was given.
Swap file is skipped (like |-n|).
*-b*
-b Binary mode. File I/O will only recognize <NL> to separate
lines. The 'expandtab' option will be reset. The 'textwidth'
@ -222,9 +240,6 @@ argument.
is set. This is done after reading the |vimrc| but before
reading any file in the arglist. See also |edit-binary|.
*-l*
-l Lisp mode. Sets the 'lisp' and 'showmatch' options on.
*-A*
-A Arabic mode. Sets the 'arabic' option on.
@ -239,10 +254,10 @@ argument.
Example: >
nvim -V8
-V[N]{filename}
Like -V and set 'verbosefile' to {filename}. Messages are not
displayed; instead they are written to the file {filename}.
{filename} must not start with a digit.
-V[N]{file}
Like -V and sets 'verbosefile' to {file} (must not start with
a digit). Messages are not displayed; instead they are
written to {file}.
Example: >
nvim -V20vimlog
<

View File

@ -55,11 +55,19 @@ if sys.version_info < MIN_PYTHON_VERSION:
doxygen_version = tuple((int(i) for i in subprocess.check_output(["doxygen", "-v"],
universal_newlines=True).split()[0].split('.')))
# Until 0.9 is released, need this hacky way to check that "nvim -l foo.lua" works.
nvim_version = list(line for line in subprocess.check_output(['nvim', '-h'], universal_newlines=True).split('\n')
if '-l ' in line)
if doxygen_version < MIN_DOXYGEN_VERSION:
print("\nRequires doxygen {}.{}.{}+".format(*MIN_DOXYGEN_VERSION))
print("Your doxygen version is {}.{}.{}\n".format(*doxygen_version))
sys.exit(1)
if len(nvim_version) == 0:
print("\nRequires 'nvim -l' feature, see https://github.com/neovim/neovim/pull/18706")
sys.exit(1)
# DEBUG = ('DEBUG' in os.environ)
INCLUDE_C_DECL = ('INCLUDE_C_DECL' in os.environ)
INCLUDE_DEPRECATED = ('INCLUDE_DEPRECATED' in os.environ)
@ -79,7 +87,7 @@ base_dir = os.path.dirname(os.path.dirname(script_path))
out_dir = os.path.join(base_dir, 'tmp-{target}-doc')
filter_cmd = '%s %s' % (sys.executable, script_path)
msgs = [] # Messages to show on exit.
lua2dox_filter = os.path.join(base_dir, 'scripts', 'lua2dox_filter')
lua2dox = os.path.join(base_dir, 'scripts', 'lua2dox.lua')
CONFIG = {
'api': {
@ -993,7 +1001,7 @@ def delete_lines_below(filename, tokenstr):
fp.writelines(lines[0:i])
def main(config, args):
def main(doxygen_config, args):
"""Generates:
1. Vim :help docs
@ -1021,7 +1029,7 @@ def main(config, args):
# runtime/lua/vim/lsp.lua:209: warning: argument 'foo' not found
stderr=(subprocess.STDOUT if debug else subprocess.DEVNULL))
p.communicate(
config.format(
doxygen_config.format(
input=' '.join(
[f'"{file}"' for file in CONFIG[target]['files']]),
output=output_dir,
@ -1108,11 +1116,7 @@ def main(config, args):
fn_map_full.update(fn_map)
if len(sections) == 0:
if target == 'lua':
fail(f'no sections for target: {target} (this usually means'
+ ' "luajit" was not found by scripts/lua2dox_filter)')
else:
fail(f'no sections for target: {target}')
fail(f'no sections for target: {target} (look for errors near "Preprocessing" log lines above)')
if len(sections) > len(CONFIG[target]['section_order']):
raise RuntimeError(
'found new modules "{}"; update the "section_order" map'.format(
@ -1159,7 +1163,7 @@ def main(config, args):
def filter_source(filename):
name, extension = os.path.splitext(filename)
if extension == '.lua':
p = subprocess.run([lua2dox_filter, filename], stdout=subprocess.PIPE)
p = subprocess.run(['nvim', '-l', lua2dox, filename], stdout=subprocess.PIPE)
op = ('?' if 0 != p.returncode else p.stdout.decode('utf-8'))
print(op)
else:

View File

@ -27,14 +27,13 @@ http://search.cpan.org/~alec/Doxygen-Lua-0.02/lib/Doxygen/Lua.pm
Running
-------
This file "lua2dox.lua" gets called by "lua2dox_filter" (bash).
This script "lua2dox.lua" gets called by "gen_vimdoc.py".
Doxygen must be on your system. You can experiment like so:
- Run "doxygen -g" to create a default Doxyfile.
- Then alter it to let it recognise lua. Add the two following lines:
- Then alter it to let it recognise lua. Add the following line:
FILE_PATTERNS = *.lua
FILTER_PATTERNS = *.lua=lua2dox_filter
- Then run "doxygen".
The core function reads the input file (filename or stdin) and outputs some pseudo C-ish language.
@ -117,26 +116,6 @@ local function string_split(Str, Pattern)
return splitStr
end
--! \class TCore_Commandline
--! \brief reads/parses commandline
local TCore_Commandline = class()
--! \brief constructor
function TCore_Commandline.init(this)
this.argv = arg
this.parsed = {}
this.params = {}
end
--! \brief get value
function TCore_Commandline.getRaw(this, Key, Default)
local val = this.argv[Key]
if not val then
val = Default
end
return val
end
-------------------------------
--! \brief file buffer
--!
@ -147,7 +126,7 @@ local TStream_Read = class()
--!
--! \param Filename name of file to read (or nil == stdin)
function TStream_Read.getContents(this, Filename)
assert(Filename)
assert(Filename, ('invalid file: %s'):format(Filename))
-- get lines from file
-- syphon lines to our table
local filecontents = {}
@ -548,15 +527,14 @@ end
local This_app = TApp()
--main
local cl = TCore_Commandline()
local argv1 = cl:getRaw(2)
local argv1 = arg[1]
if argv1 == '--help' then
TCore_IO_writeln(This_app:getVersion())
TCore_IO_writeln(This_app:getCopyright())
TCore_IO_writeln([[
run as:
lua2dox_filter <param>
nvim -l scripts/lua2dox.lua <param>
--------------
Param:
<filename> : interprets filename

View File

@ -1,90 +0,0 @@
#!/usr/bin/env bash
###########################################################################
# Copyright (C) 2012 by Simon Dales #
# simon@purrsoft.co.uk #
# #
# This program is free software; you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation; either version 2 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program; if not, write to the #
# Free Software Foundation, Inc., #
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #
###########################################################################
LANG=""
##! \brief test executable to see if it exists
test_executable() {
P_EXE="$1"
#########
WHICH=$(which "$P_EXE")
if test -z "${WHICH}"; then
echo "not found \"${P_EXE}\""
else
EXE="${P_EXE}"
fi
}
##! \brief sets the lua interpreter
set_lua() {
if test -z "${EXE}"; then
test_executable '.deps/usr/bin/luajit'
fi
if test -z "${EXE}"; then
test_executable 'luajit'
fi
if test -z "${EXE}"; then
test_executable 'lua'
fi
}
##! \brief makes canonical name of file
##!
##! Note that "readlink -f" doesn't work in MacOSX
##!
do_readlink() {
pushd . > /dev/null
TARGET_FILE=$1
cd "$(dirname $TARGET_FILE)"
TARGET_FILE=$(basename "$TARGET_FILE")
# Iterate down a (possible) chain of symlinks
while [ -L "$TARGET_FILE" ]; do
TARGET_FILE=$(readlink "$TARGET_FILE")
cd $(dirname "$TARGET_FILE")
TARGET_FILE=$(basename "$TARGET_FILE")
done
PHYS_DIR=$(pwd -P)
RESULT=$PHYS_DIR
popd > /dev/null
}
##main
set_lua
if test -z "${EXE}"; then
echo "no lua interpreter found"
exit 1
else
BASENAME=$(basename "$0")
do_readlink "$0"
DIRNAME="${RESULT}"
LUASCRIPT="${DIRNAME}/lua2dox.lua ${BASENAME}"
#echo "lua[${LUASCRIPT}]"
${EXE} ${LUASCRIPT} $@
fi
##eof

File diff suppressed because it is too large Load Diff

View File

@ -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.

View File

@ -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];

View File

@ -275,14 +275,14 @@ int main(int argc, char **argv)
// Check if we have an interactive window.
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
// argument list "global_alist".
command_line_scan(&params);
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(&params);
@ -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 <file>" (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 <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"));

View File

@ -23,15 +23,17 @@ 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 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

View File

@ -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')

View File

@ -26,6 +26,7 @@ local write_file = helpers.write_file
local meths = helpers.meths
local alter_slashes = helpers.alter_slashes
local is_os = helpers.is_os
local dedent = helpers.dedent
local testfile = 'Xtest_startuptime'
after_each(function()
@ -47,6 +48,34 @@ describe('startup', function()
assert_log("require%('vim%._editor'%)", testfile, 100)
end)
end)
it('-D does not hang #12647', function()
clear()
local screen
screen = Screen.new(60, 7)
screen:attach()
command([[let g:id = termopen('"]]..nvim_prog..
[[" -u NONE -i NONE --cmd "set noruler" -D')]])
screen:expect([[
^ |
|
Entering Debug mode. Type "cont" to continue. |
nvim_exec() |
cmd: aunmenu * |
> |
|
]])
command([[call chansend(g:id, "cont\n")]])
screen:expect([[
^ |
~ |
~ |
~ |
[No Name] |
|
|
]])
end)
end)
describe('startup', function()
@ -58,6 +87,94 @@ describe('startup', function()
os.remove('Xtest_startup_ttyout')
end)
describe('-l Lua', function()
local function assert_l_out(expected, args_before, args_after)
local args = { nvim_prog, '--clean' }
vim.list_extend(args, args_before or {})
vim.list_extend(args, { '-l', 'test/functional/fixtures/startup.lua' })
vim.list_extend(args, args_after or {})
local out = funcs.system(args):gsub('\r\n', '\n')
return eq(dedent(expected), out)
end
it('failure modes', function()
-- nvim -l <missing file>
matches('nvim: Argument missing after: "%-l"', funcs.system({ nvim_prog, '-l' }))
eq(1, eval('v:shell_error'))
end)
it('os.exit() sets Nvim exitcode', function()
-- nvim -l foo.lua -arg1 -- a b c
assert_l_out([[
bufs:
args: { "-arg1", "--exitcode", "73", "--arg2" }]],
{},
{ '-arg1', "--exitcode", "73", '--arg2' }
)
eq(73, eval('v:shell_error'))
end)
it('sets _G.arg', function()
-- nvim -l foo.lua -arg1 -- a b c
assert_l_out([[
bufs:
args: { "-arg1", "--arg2", "arg3" }]],
{},
{ '-arg1', '--arg2', 'arg3' }
)
eq(0, eval('v:shell_error'))
-- nvim -l foo.lua --
assert_l_out([[
bufs:
args: {}]],
{},
{ '--' }
)
eq(0, eval('v:shell_error'))
-- nvim file1 file2 -l foo.lua -arg1 -- file3 file4
assert_l_out([[
bufs: file1 file2 file3 file4
args: { "-arg1", "arg 2" }]],
{ 'file1', 'file2', },
{ '-arg1', 'arg 2', '--', 'file3', 'file4' }
)
eq(0, eval('v:shell_error'))
-- nvim file1 file2 -l foo.lua -arg1 --
assert_l_out([[
bufs: file1 file2
args: { "-arg1" }]],
{ 'file1', 'file2', },
{ '-arg1', '--' }
)
eq(0, eval('v:shell_error'))
-- nvim -l foo.lua <vim args>
assert_l_out([[
bufs:
args: { "-c", "set wrap?" }]],
{},
{ '-c', 'set wrap?' }
)
eq(0, eval('v:shell_error'))
-- nvim <vim args> -l foo.lua <vim args>
assert_l_out(
-- luacheck: ignore 611 (Line contains only whitespaces)
[[
wrap
bufs:
args: { "-c", "set wrap?" }]],
{ '-c', 'set wrap?' },
{ '-c', 'set wrap?' }
)
eq(0, eval('v:shell_error'))
end)
end)
it('pipe at both ends: has("ttyin")==0 has("ttyout")==0', function()
-- system() puts a pipe at both ends.
local out = funcs.system({ nvim_prog, '-u', 'NONE', '-i', 'NONE', '--headless',
@ -66,6 +183,7 @@ describe('startup', function()
'+q' })
eq('0 0', out)
end)
it('with --embed: has("ttyin")==0 has("ttyout")==0', function()
local screen = Screen.new(25, 3)
-- Remote UI connected by --embed.
@ -77,6 +195,7 @@ describe('startup', function()
0 0 |
]])
end)
it('in a TTY: has("ttyin")==1 has("ttyout")==1', function()
local screen = Screen.new(25, 4)
screen:attach()
@ -95,6 +214,7 @@ describe('startup', function()
|
]])
end)
it('output to pipe: has("ttyin")==1 has("ttyout")==0', function()
if is_os('win') then
command([[set shellcmdflag=/s\ /c shellxquote=\"]])
@ -111,6 +231,7 @@ describe('startup', function()
read_file('Xtest_startup_ttyout'))
end)
end)
it('input from pipe: has("ttyin")==0 has("ttyout")==1', function()
if is_os('win') then
command([[set shellcmdflag=/s\ /c shellxquote=\"]])
@ -128,6 +249,7 @@ describe('startup', function()
read_file('Xtest_startup_ttyout'))
end)
end)
it('input from pipe (implicit) #7679', function()
local screen = Screen.new(25, 4)
screen:attach()
@ -147,6 +269,7 @@ describe('startup', function()
|
]])
end)
it('input from pipe + file args #7679', function()
eq('ohyeah\r\n0 0 bufs=3',
funcs.system({nvim_prog, '-n', '-u', 'NONE', '-i', 'NONE', '--headless',
@ -532,32 +655,6 @@ describe('sysinit', function()
eval('printf("loaded %d xdg %d vim %d", g:loaded, get(g:, "xdg", 0), get(g:, "vim", 0))'))
end)
it('fixed hang issue with -D (#12647)', function()
local screen
screen = Screen.new(60, 7)
screen:attach()
command([[let g:id = termopen('"]]..nvim_prog..
[[" -u NONE -i NONE --cmd "set noruler" -D')]])
screen:expect([[
^ |
Entering Debug mode. Type "cont" to continue. |
nvim_exec() |
cmd: aunmenu * |
> |
<" -u NONE -i NONE --cmd "set noruler" -D 1,0-1 All|
|
]])
command([[call chansend(g:id, "cont\n")]])
screen:expect([[
^ |
~ |
~ |
[No Name] |
|
<" -u NONE -i NONE --cmd "set noruler" -D 1,0-1 All|
|
]])
end)
end)
describe('user config init', function()

View File

@ -0,0 +1,34 @@
-- Test "nvim -l foo.lua …"
local function printbufs()
local bufs = ''
for _, v in ipairs(vim.api.nvim_list_bufs()) do
local b = vim.fn.bufname(v)
if b:len() > 0 then
bufs = ('%s %s'):format(bufs, b)
end
end
print(('bufs:%s'):format(bufs))
end
local function parseargs(args)
local exitcode = nil
for i = 1, #args do
if args[i] == '--exitcode' then
exitcode = tonumber(args[i + 1])
end
end
return exitcode
end
local function main()
printbufs()
print('args:', vim.inspect(_G.arg))
local exitcode = parseargs(_G.arg)
if type(exitcode) == 'number' then
os.exit(exitcode)
end
end
main()