mirror of
https://github.com/neovim/neovim.git
synced 2026-07-22 00:51:33 +00:00
Merge #6914 from ZyX-I/func-def-trailing-error
Allow multiple function definitions in one :execute
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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_(
|
||||
|
||||
29
test/functional/eval/function_spec.lua
Normal file
29
test/functional/eval/function_spec.lua
Normal file
@@ -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)
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
local function check_func(fname, body, indent)
|
||||
if type(body) == 'number' then
|
||||
body = ('return %i'):format(body)
|
||||
end
|
||||
eq(dedent(([[
|
||||
|
||||
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('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)
|
||||
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=▶,▲
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user