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_. --- test/functional/core/startup_spec.lua | 149 ++++++++++++++++++++++++++++------ test/functional/fixtures/startup.lua | 34 ++++++++ 2 files changed, 157 insertions(+), 26 deletions(-) create mode 100644 test/functional/fixtures/startup.lua (limited to 'test') diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua index 72d8f3a103..1df8d76c97 100644 --- a/test/functional/core/startup_spec.lua +++ b/test/functional/core/startup_spec.lua @@ -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 + 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 + assert_l_out([[ + bufs: + args: { "-c", "set wrap?" }]], + {}, + { '-c', 'set wrap?' } + ) + eq(0, eval('v:shell_error')) + + -- nvim -l foo.lua + 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() diff --git a/test/functional/fixtures/startup.lua b/test/functional/fixtures/startup.lua new file mode 100644 index 0000000000..37e738c75e --- /dev/null +++ b/test/functional/fixtures/startup.lua @@ -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() -- cgit From 45549f031ee52a01601c33acc411f3111cfc4e95 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 1 Jan 2023 03:14:13 +0100 Subject: 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". --- test/functional/core/startup_spec.lua | 23 +++++++++++++++-------- test/functional/fixtures/startup.lua | 3 ++- 2 files changed, 17 insertions(+), 9 deletions(-) (limited to 'test') diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua index 1df8d76c97..dbd517995c 100644 --- a/test/functional/core/startup_spec.lua +++ b/test/functional/core/startup_spec.lua @@ -107,7 +107,8 @@ describe('startup', function() -- nvim -l foo.lua -arg1 -- a b c assert_l_out([[ bufs: - args: { "-arg1", "--exitcode", "73", "--arg2" }]], + nvim args: 8 + lua args: { "-arg1", "--exitcode", "73", "--arg2" }]], {}, { '-arg1', "--exitcode", "73", '--arg2' } ) @@ -118,7 +119,8 @@ describe('startup', function() -- nvim -l foo.lua -arg1 -- a b c assert_l_out([[ bufs: - args: { "-arg1", "--arg2", "arg3" }]], + nvim args: 7 + lua args: { "-arg1", "--arg2", "arg3" }]], {}, { '-arg1', '--arg2', 'arg3' } ) @@ -127,7 +129,8 @@ describe('startup', function() -- nvim -l foo.lua -- assert_l_out([[ bufs: - args: {}]], + nvim args: 5 + lua args: { "--" }]], {}, { '--' } ) @@ -135,8 +138,9 @@ describe('startup', function() -- nvim file1 file2 -l foo.lua -arg1 -- file3 file4 assert_l_out([[ - bufs: file1 file2 file3 file4 - args: { "-arg1", "arg 2" }]], + bufs: file1 file2 + nvim args: 11 + lua args: { "-arg1", "arg 2", "--", "file3", "file4" }]], { 'file1', 'file2', }, { '-arg1', 'arg 2', '--', 'file3', 'file4' } ) @@ -145,7 +149,8 @@ describe('startup', function() -- nvim file1 file2 -l foo.lua -arg1 -- assert_l_out([[ bufs: file1 file2 - args: { "-arg1" }]], + nvim args: 8 + lua args: { "-arg1", "--" }]], { 'file1', 'file2', }, { '-arg1', '--' } ) @@ -154,7 +159,8 @@ describe('startup', function() -- nvim -l foo.lua assert_l_out([[ bufs: - args: { "-c", "set wrap?" }]], + nvim args: 6 + lua args: { "-c", "set wrap?" }]], {}, { '-c', 'set wrap?' } ) @@ -167,7 +173,8 @@ describe('startup', function() wrap bufs: - args: { "-c", "set wrap?" }]], + nvim args: 8 + lua args: { "-c", "set wrap?" }]], { '-c', 'set wrap?' }, { '-c', 'set wrap?' } ) diff --git a/test/functional/fixtures/startup.lua b/test/functional/fixtures/startup.lua index 37e738c75e..d0e60309bd 100644 --- a/test/functional/fixtures/startup.lua +++ b/test/functional/fixtures/startup.lua @@ -23,7 +23,8 @@ end local function main() 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) if type(exitcode) == 'number' then -- cgit From adef308a5925a3b967af3bd7c598074e5b6cae18 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 2 Jan 2023 15:34:14 +0100 Subject: feat(lua): exit 1 on Lua "-l" script error --- test/functional/core/startup_spec.lua | 21 +++++++++++++++------ test/functional/fixtures/startup-fail.lua | 7 +++++++ test/functional/vimscript/system_spec.lua | 2 ++ 3 files changed, 24 insertions(+), 6 deletions(-) create mode 100644 test/functional/fixtures/startup-fail.lua (limited to 'test') diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua index dbd517995c..455de08548 100644 --- a/test/functional/core/startup_spec.lua +++ b/test/functional/core/startup_spec.lua @@ -88,11 +88,11 @@ describe('startup', function() end) describe('-l Lua', function() - local function assert_l_out(expected, args_before, args_after) + local function assert_l_out(expected, nvim_args, lua_args) local args = { nvim_prog, '--clean' } - vim.list_extend(args, args_before or {}) + vim.list_extend(args, nvim_args or {}) vim.list_extend(args, { '-l', 'test/functional/fixtures/startup.lua' }) - vim.list_extend(args, args_after or {}) + vim.list_extend(args, lua_args or {}) local out = funcs.system(args):gsub('\r\n', '\n') return eq(dedent(expected), out) end @@ -115,6 +115,13 @@ describe('startup', function() eq(73, eval('v:shell_error')) end) + it('Lua error sets Nvim exitcode', function() + eq(0, eval('v:shell_error')) + matches('E5113: .* my pearls!!', + funcs.system({ nvim_prog, '-l', 'test/functional/fixtures/startup-fail.lua' })) + eq(1, eval('v:shell_error')) + end) + it('sets _G.arg', function() -- nvim -l foo.lua -arg1 -- a b c assert_l_out([[ @@ -396,11 +403,13 @@ describe('startup', function() { 'put =mode(1)', 'print', '' })) end) - it('fails on --embed with -es/-Es', function() - matches('nvim[.exe]*: %-%-embed conflicts with %-es/%-Es', + it('fails on --embed with -es/-Es/-l', function() + matches('nvim[.exe]*: %-%-embed conflicts with %-es/%-Es/%-l', funcs.system({nvim_prog, '--embed', '-es' })) - matches('nvim[.exe]*: %-%-embed conflicts with %-es/%-Es', + matches('nvim[.exe]*: %-%-embed conflicts with %-es/%-Es/%-l', funcs.system({nvim_prog, '--embed', '-Es' })) + matches('nvim[.exe]*: %-%-embed conflicts with %-es/%-Es/%-l', + funcs.system({nvim_prog, '--embed', '-l', 'foo.lua' })) end) it('does not crash if --embed is given twice', function() diff --git a/test/functional/fixtures/startup-fail.lua b/test/functional/fixtures/startup-fail.lua new file mode 100644 index 0000000000..adcfe2a201 --- /dev/null +++ b/test/functional/fixtures/startup-fail.lua @@ -0,0 +1,7 @@ +-- Test "nvim -l foo.lua …" with a Lua error. + +local function main() + error('my pearls!!') +end + +main() diff --git a/test/functional/vimscript/system_spec.lua b/test/functional/vimscript/system_spec.lua index dbf734b51a..f099b4a36e 100644 --- a/test/functional/vimscript/system_spec.lua +++ b/test/functional/vimscript/system_spec.lua @@ -1,3 +1,5 @@ +-- Tests for system() and :! shell. + local helpers = require('test.functional.helpers')(after_each) local assert_alive = helpers.assert_alive -- cgit From f43de742e881e54a3602e00c8c247cecca65a266 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 3 Jan 2023 02:54:53 +0100 Subject: feat(lua): execute stdin ("-") as Lua --- test/functional/core/startup_spec.lua | 58 +++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 16 deletions(-) (limited to 'test') diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua index 455de08548..d7e64c6b54 100644 --- a/test/functional/core/startup_spec.lua +++ b/test/functional/core/startup_spec.lua @@ -88,18 +88,18 @@ describe('startup', function() end) describe('-l Lua', function() - local function assert_l_out(expected, nvim_args, lua_args) - local args = { nvim_prog, '--clean' } + local function assert_l_out(expected, nvim_args, lua_args, script, input) + local args = { nvim_prog } vim.list_extend(args, nvim_args or {}) - vim.list_extend(args, { '-l', 'test/functional/fixtures/startup.lua' }) + vim.list_extend(args, { '-l', (script or 'test/functional/fixtures/startup.lua') }) vim.list_extend(args, lua_args or {}) - local out = funcs.system(args):gsub('\r\n', '\n') + local out = funcs.system(args, input):gsub('\r\n', '\n') return eq(dedent(expected), out) end it('failure modes', function() - -- nvim -l - matches('nvim: Argument missing after: "%-l"', funcs.system({ nvim_prog, '-l' })) + -- nvim -l + matches('nvim%.?e?x?e?: Argument missing after: "%-l"', funcs.system({ nvim_prog, '-l' })) eq(1, eval('v:shell_error')) end) @@ -107,7 +107,7 @@ describe('startup', function() -- nvim -l foo.lua -arg1 -- a b c assert_l_out([[ bufs: - nvim args: 8 + nvim args: 7 lua args: { "-arg1", "--exitcode", "73", "--arg2" }]], {}, { '-arg1', "--exitcode", "73", '--arg2' } @@ -115,18 +115,35 @@ describe('startup', function() eq(73, eval('v:shell_error')) end) - it('Lua error sets Nvim exitcode', function() + it('Lua-error sets Nvim exitcode', function() eq(0, eval('v:shell_error')) matches('E5113: .* my pearls!!', funcs.system({ nvim_prog, '-l', 'test/functional/fixtures/startup-fail.lua' })) eq(1, eval('v:shell_error')) + matches('E5113: .* %[string "error%("whoa"%)"%]:1: whoa', + funcs.system({ nvim_prog, '-l', '-' }, 'error("whoa")')) + eq(1, eval('v:shell_error')) + end) + + it('executes stdin "-"', function() + assert_l_out('args=2 whoa', + nil, + { 'arg1', 'arg 2' }, + '-', + "print(('args=%d %s'):format(#_G.arg, 'whoa'))") + assert_l_out('biiig input: 1000042', + nil, + nil, + '-', + ('print("biiig input: "..("%s"):len())'):format(string.rep('x', (1000 * 1000) + 42))) + eq(0, eval('v:shell_error')) end) it('sets _G.arg', function() -- nvim -l foo.lua -arg1 -- a b c assert_l_out([[ bufs: - nvim args: 7 + nvim args: 6 lua args: { "-arg1", "--arg2", "arg3" }]], {}, { '-arg1', '--arg2', 'arg3' } @@ -136,7 +153,7 @@ describe('startup', function() -- nvim -l foo.lua -- assert_l_out([[ bufs: - nvim args: 5 + nvim args: 4 lua args: { "--" }]], {}, { '--' } @@ -146,7 +163,7 @@ describe('startup', function() -- nvim file1 file2 -l foo.lua -arg1 -- file3 file4 assert_l_out([[ bufs: file1 file2 - nvim args: 11 + nvim args: 10 lua args: { "-arg1", "arg 2", "--", "file3", "file4" }]], { 'file1', 'file2', }, { '-arg1', 'arg 2', '--', 'file3', 'file4' } @@ -156,7 +173,7 @@ describe('startup', function() -- nvim file1 file2 -l foo.lua -arg1 -- assert_l_out([[ bufs: file1 file2 - nvim args: 8 + nvim args: 7 lua args: { "-arg1", "--" }]], { 'file1', 'file2', }, { '-arg1', '--' } @@ -166,7 +183,7 @@ describe('startup', function() -- nvim -l foo.lua assert_l_out([[ bufs: - nvim args: 6 + nvim args: 5 lua args: { "-c", "set wrap?" }]], {}, { '-c', 'set wrap?' } @@ -180,13 +197,22 @@ describe('startup', function() wrap bufs: - nvim args: 8 + nvim args: 7 lua args: { "-c", "set wrap?" }]], { '-c', 'set wrap?' }, { '-c', 'set wrap?' } ) eq(0, eval('v:shell_error')) end) + + it('disables swapfile/shada/config/plugins', function() + assert_l_out('updatecount=0 shadafile=NONE loadplugins=false scriptnames=1', + nil, + nil, + '-', + [[print(('updatecount=%d shadafile=%s loadplugins=%s scriptnames=%d'):format( + vim.o.updatecount, vim.o.shadafile, tostring(vim.o.loadplugins), math.max(1, #vim.fn.split(vim.fn.execute('scriptnames'),'\n'))))]]) + end) end) it('pipe at both ends: has("ttyin")==0 has("ttyout")==0', function() @@ -375,11 +401,11 @@ describe('startup', function() it('-es/-Es disables swapfile, user config #8540', function() for _,arg in ipairs({'-es', '-Es'}) do local out = funcs.system({nvim_prog, arg, - '+set swapfile? updatecount? shada?', + '+set swapfile? updatecount? shadafile?', "+put =execute('scriptnames')", '+%print'}) local line1 = string.match(out, '^.-\n') -- updatecount=0 means swapfile was disabled. - eq(" swapfile updatecount=0 shada=!,'100,<50,s10,h\n", line1) + eq(" swapfile updatecount=0 shadafile=\n", line1) -- Standard plugins were loaded, but not user config. eq('health.vim', string.match(out, 'health.vim')) eq(nil, string.match(out, 'init.vim')) -- cgit