aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/nvim/eval.c32
-rw-r--r--src/nvim/globals.h1
-rw-r--r--test/functional/eval/function_spec.lua29
-rw-r--r--test/functional/helpers.lua24
-rw-r--r--test/functional/ui/screen.lua5
-rw-r--r--test/functional/viml/function_spec.lua232
-rw-r--r--test/helpers.lua7
7 files changed, 290 insertions, 40 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index baa61a26bc..18aa5bf763 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -19765,10 +19765,12 @@ void ex_function(exarg_T *eap)
/* When there is a line break use what follows for the function body.
* Makes 'exe "func Test()\n...\nendfunc"' work. */
- if (*p == '\n')
+ const char *const end = (const char *)p + STRLEN(p);
+ if (*p == '\n') {
line_arg = p + 1;
- else if (*p != NUL && *p != '"' && !eap->skip && !did_emsg)
- EMSG(_(e_trailing));
+ } else if (*p != NUL && *p != '"' && !eap->skip && !did_emsg) {
+ emsgf(_(e_trailing));
+ }
/*
* Read the body of the function, until ":endfunction" is found.
@@ -19842,8 +19844,30 @@ void ex_function(exarg_T *eap)
/* Check for "endfunction". */
if (checkforcmd(&p, "endfunction", 4) && nesting-- == 0) {
- if (line_arg == NULL)
+ if (*p == '!') {
+ p++;
+ }
+ const char *const comment_start = strchr((const char *)p, '"');
+ const char *const endfunc_end = (comment_start
+ ? strchr(comment_start, '\n')
+ : strpbrk((const char *)p, "\n|"));
+ p = (endfunc_end
+ ? (char_u *)endfunc_end
+ : p + STRLEN(p));
+ if (*p == '|') {
+ emsgf(_(e_trailing2), p);
+ if (line_arg == NULL) {
+ xfree(theline);
+ }
+ goto erret;
+ }
+ if (line_arg == NULL) {
xfree(theline);
+ } else {
+ if ((const char *)p < end) {
+ eap->nextcmd = p + 1;
+ }
+ }
break;
}
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 840be928c8..6d1bd1de12 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -1131,6 +1131,7 @@ EXTERN char_u e_longname[] INIT(= N_("E75: Name too long"));
EXTERN char_u e_toomsbra[] INIT(= N_("E76: Too many ["));
EXTERN char_u e_toomany[] INIT(= N_("E77: Too many file names"));
EXTERN char_u e_trailing[] INIT(= N_("E488: Trailing characters"));
+EXTERN char_u e_trailing2[] INIT(= N_("E488: Trailing characters: %s"));
EXTERN char_u e_umark[] INIT(= N_("E78: Unknown mark"));
EXTERN char_u e_wildexpand[] INIT(= N_("E79: Cannot expand wildcards"));
EXTERN char_u e_winheight[] INIT(= N_(
diff --git a/test/functional/eval/function_spec.lua b/test/functional/eval/function_spec.lua
new file mode 100644
index 0000000000..776e760aaf
--- /dev/null
+++ b/test/functional/eval/function_spec.lua
@@ -0,0 +1,29 @@
+local helpers = require('test.functional.helpers')(after_each)
+
+local clear = helpers.clear
+local eq = helpers.eq
+local exc_exec = helpers.exc_exec
+
+describe('Up to MAX_FUNC_ARGS arguments are handled by', function()
+ local max_func_args = 20 -- from eval.h
+ local range = helpers.funcs.range
+
+ before_each(clear)
+
+ it('printf()', function()
+ local printf = helpers.funcs.printf
+ local rep = helpers.funcs['repeat']
+ local expected = '2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,'
+ eq(expected, printf(rep('%d,', max_func_args-1), unpack(range(2, max_func_args))))
+ local ret = exc_exec('call printf("", 2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)')
+ eq('Vim(call):E740: Too many arguments for function printf', ret)
+ end)
+
+ it('rpcnotify()', function()
+ local rpcnotify = helpers.funcs.rpcnotify
+ local ret = rpcnotify(0, 'foo', unpack(range(3, max_func_args)))
+ eq(1, ret)
+ ret = exc_exec('call rpcnotify(0, "foo", 3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)')
+ eq('Vim(call):E740: Too many arguments for function rpcnotify', ret)
+ end)
+end)
diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua
index b8512c0ad6..5b641b5054 100644
--- a/test/functional/helpers.lua
+++ b/test/functional/helpers.lua
@@ -492,17 +492,6 @@ local exc_exec = function(cmd)
return ret
end
-local function redir_exec(cmd)
- nvim_command(([[
- redir => g:__output
- silent! execute "%s"
- redir END
- ]]):format(cmd:gsub('\n', '\\n'):gsub('[\\"]', '\\%0')))
- local ret = nvim_eval('get(g:, "__output", 0)')
- nvim_command('unlet! g:__output')
- return ret
-end
-
local function create_callindex(func)
local table = {}
setmetatable(table, {
@@ -562,6 +551,19 @@ local curbufmeths = create_callindex(curbuf)
local curwinmeths = create_callindex(curwin)
local curtabmeths = create_callindex(curtab)
+local function redir_exec(cmd)
+ meths.set_var('__redir_exec_cmd', cmd)
+ nvim_command([[
+ redir => g:__redir_exec_output
+ silent! execute g:__redir_exec_cmd
+ redir END
+ ]])
+ local ret = meths.get_var('__redir_exec_output')
+ meths.del_var('__redir_exec_output')
+ meths.del_var('__redir_exec_cmd')
+ return ret
+end
+
local function get_pathsep()
return funcs.fnamemodify('.', ':p'):sub(-1)
end
diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua
index 7d9cd6c026..5408e1e195 100644
--- a/test/functional/ui/screen.lua
+++ b/test/functional/ui/screen.lua
@@ -198,8 +198,9 @@ function Screen:expect(expected, attr_ids, attr_ignore, condition, any)
condition = expected
expected = nil
else
- -- Remove the last line and dedent.
- expected = dedent(expected:gsub('\n[ ]+$', ''))
+ -- Remove the last line and dedent. Note that gsub returns more then one
+ -- value.
+ expected = dedent(expected:gsub('\n[ ]+$', ''), 0)
for row in expected:gmatch('[^\n]+') do
row = row:sub(1, #row - 1) -- Last char must be the screen delimiter.
table.insert(expected_rows, row)
diff --git a/test/functional/viml/function_spec.lua b/test/functional/viml/function_spec.lua
index 776e760aaf..0cf92f7d40 100644
--- a/test/functional/viml/function_spec.lua
+++ b/test/functional/viml/function_spec.lua
@@ -1,29 +1,221 @@
local helpers = require('test.functional.helpers')(after_each)
-local clear = helpers.clear
local eq = helpers.eq
-local exc_exec = helpers.exc_exec
+local clear = helpers.clear
+local funcs = helpers.funcs
+local dedent = helpers.dedent
+local redir_exec = helpers.redir_exec
-describe('Up to MAX_FUNC_ARGS arguments are handled by', function()
- local max_func_args = 20 -- from eval.h
- local range = helpers.funcs.range
+before_each(clear)
- before_each(clear)
+local function check_nofunc(fname)
+ eq(0, funcs.exists('*' .. fname))
+end
- it('printf()', function()
- local printf = helpers.funcs.printf
- local rep = helpers.funcs['repeat']
- local expected = '2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,'
- eq(expected, printf(rep('%d,', max_func_args-1), unpack(range(2, max_func_args))))
- local ret = exc_exec('call printf("", 2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)')
- eq('Vim(call):E740: Too many arguments for function printf', ret)
- end)
+local function check_func(fname, body, indent)
+ if type(body) == 'number' then
+ body = ('return %i'):format(body)
+ end
+ eq(dedent(([[
- it('rpcnotify()', function()
- local rpcnotify = helpers.funcs.rpcnotify
- local ret = rpcnotify(0, 'foo', unpack(range(3, max_func_args)))
- eq(1, ret)
- ret = exc_exec('call rpcnotify(0, "foo", 3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)')
- eq('Vim(call):E740: Too many arguments for function rpcnotify', ret)
+ function %s()%s
+ endfunction]]
+ ), 3):format(
+ fname,
+ body and ('\n1' .. (' '):rep(2 + (indent or 8)) .. body) or ''),
+ redir_exec('function ' .. fname))
+end
+
+describe(':endfunction', function()
+ it('accepts bang', function()
+ eq('', redir_exec([[
+ function F()
+ endfunction!
+ ]]))
+ check_func('F')
+ eq('', redir_exec([[
+ function! F()
+ return 1
+ endfunction!
+ ]]))
+ check_func('F', 1)
+ end)
+ it('accepts comments', function()
+ eq('', redir_exec([[
+ function F1()
+ endfunction " Comment
+ ]]))
+ check_func('F1')
+ eq('', redir_exec([[
+ function F2()
+ endfunction " }}}
+ ]]))
+ check_func('F2')
+ eq('', redir_exec([[
+ function F3()
+ endfunction " F3
+ ]]))
+ check_func('F3')
+ eq('', redir_exec([[
+ function F4()
+ endfunction! " F4
+ ]]))
+ check_func('F4')
+ eq('', redir_exec([[
+ function! F4()
+ return 2
+ endfunction! " F4
+ ]]))
+ check_func('F4', 2)
+ end)
+ it('accepts function name', function()
+ eq('', redir_exec([[
+ function F0()
+ endfunction F0
+ ]]))
+ check_func('F0')
+ eq('', redir_exec([[
+ function F1()
+ endfunction! F1
+ ]]))
+ check_func('F1')
+ eq('', redir_exec([[
+ function! F2()
+ endfunction! F2
+ ]]))
+ check_func('F2')
+ eq('', redir_exec([[
+ function! F2()
+ return 3
+ endfunction! F2
+ ]]))
+ check_func('F2', 3)
+ end)
+ it('accepts weird characters', function()
+ eq('', redir_exec([[
+ function F1()
+ endfunction: }}}
+ ]]))
+ check_func('F1')
+ -- From accurev
+ eq('', redir_exec([[
+ function F2()
+ endfunction :}}}
+ ]]))
+ check_func('F2')
+ -- From cream-vimabbrev
+ eq('', redir_exec([[
+ function F3()
+ endfunction 1}}}
+ ]]))
+ check_func('F3')
+ -- From pyunit
+ eq('', redir_exec([[
+ function F4()
+ endfunction # }}}
+ ]]))
+ check_func('F4')
+ -- From vim-lldb
+ eq('', redir_exec([[
+ function F5()
+ endfunction()
+ ]]))
+ check_func('F5')
+ -- From vim-mail
+ eq('', redir_exec([[
+ function F6()
+ endfunction;
+ ]]))
+ check_func('F6')
+ end)
+ it('accepts commented bar', function()
+ eq('', redir_exec([[
+ function F1()
+ endfunction " F1 | echo 42
+ ]]))
+ check_func('F1')
+ eq('', redir_exec([[
+ function! F1()
+ return 42
+ endfunction! " F1 | echo 42
+ ]]))
+ check_func('F1', 42)
+ end)
+ it('errors out on an uncommented bar', function()
+ eq('\nE488: Trailing characters: | echo 42', redir_exec([[
+ function F1()
+ endfunction | echo 42
+ ]]))
+ check_nofunc('F1')
+ end)
+ it('allows running multiple commands', function()
+ eq('\n2', redir_exec([[
+ function F1()
+ echo 2
+ endfunction
+ call F1()
+ ]]))
+ check_func('F1', 'echo 2')
+ eq('\n2\n3\n4', redir_exec([[
+ function F2()
+ echo 2
+ endfunction F2
+ function F3()
+ echo 3
+ endfunction " F3
+ function! F4()
+ echo 4
+ endfunction!
+ call F2()
+ call F3()
+ call F4()
+ ]]))
+ check_func('F2', 'echo 2')
+ check_func('F3', 'echo 3')
+ check_func('F4', 'echo 4')
+ end)
+ it('allows running multiple commands with only one character in between',
+ function()
+ eq('\n3', redir_exec(dedent([[
+ function! F1()
+ echo 3
+ endfunction!
+ call F1()]])))
+ check_func('F1', 'echo 3', 2)
+ eq('\n4', redir_exec(dedent([[
+ function F5()
+ echo 4
+ endfunction
+ call F5()]])))
+ check_func('F5', 'echo 4', 2)
+ eq('\n5', redir_exec(dedent([[
+ function F6()
+ echo 5
+ endfunction " TEST
+ call F6()]])))
+ check_func('F6', 'echo 5', 2)
+ eq('\n6', redir_exec(dedent([[
+ function F7()
+ echo 6
+ endfunction F7
+ call F7()]])))
+ check_func('F7', 'echo 6', 2)
+ eq('\n2\n3\n4', redir_exec(dedent([[
+ function F2()
+ echo 2
+ endfunction F2
+ function F3()
+ echo 3
+ endfunction " F3
+ function! F4()
+ echo 4
+ endfunction!
+ call F2()
+ call F3()
+ call F4()]])))
+ check_func('F2', 'echo 2', 2)
+ check_func('F3', 'echo 3', 2)
+ check_func('F4', 'echo 4', 2)
end)
end)
+-- vim: foldmarker=▶,▲
diff --git a/test/helpers.lua b/test/helpers.lua
index 7a0e4b8c3c..260f10002e 100644
--- a/test/helpers.lua
+++ b/test/helpers.lua
@@ -284,7 +284,7 @@ local function concat_tables(...)
return ret
end
-local function dedent(str)
+local function dedent(str, leave_indent)
-- find minimum common indent across lines
local indent = nil
for line in str:gmatch('[^\n]+') do
@@ -297,12 +297,13 @@ local function dedent(str)
-- no minimum common indent
return str
end
+ local left_indent = (' '):rep(leave_indent or 0)
-- create a pattern for the indent
indent = indent:gsub('%s', '[ \t]')
-- strip it from the first line
- str = str:gsub('^'..indent, '')
+ str = str:gsub('^'..indent, left_indent)
-- strip it from the remaining lines
- str = str:gsub('[\n]'..indent, '\n')
+ str = str:gsub('[\n]'..indent, '\n' .. left_indent)
return str
end