Skip to content

Commit

Permalink
Merge 476c28f into cb8e47c
Browse files Browse the repository at this point in the history
  • Loading branch information
ZyX-I committed Jun 21, 2017
2 parents cb8e47c + 476c28f commit 02b1f3e
Show file tree
Hide file tree
Showing 7 changed files with 290 additions and 40 deletions.
32 changes: 28 additions & 4 deletions src/nvim/eval.c
Expand Up @@ -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.
Expand Down Expand Up @@ -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;
}

Expand Down
1 change: 1 addition & 0 deletions src/nvim/globals.h
Expand Up @@ -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_(
Expand Down
29 changes: 29 additions & 0 deletions 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)
24 changes: 13 additions & 11 deletions test/functional/helpers.lua
Expand Up @@ -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, {
Expand Down Expand Up @@ -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
Expand Down
5 changes: 3 additions & 2 deletions test/functional/ui/screen.lua
Expand Up @@ -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)
Expand Down
232 changes: 212 additions & 20 deletions 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=▶,▲

0 comments on commit 02b1f3e

Please sign in to comment.