mirror of
https://github.com/vim/vim.git
synced 2025-07-26 11:04:33 -04:00
patch 8.2.2635: Vim9: cannot define an inline function
Problem: Vim9: cannot define an inline function. Solution: Make an inline function mostly work.
This commit is contained in:
parent
f90c855c71
commit
7a6eaa06f9
@ -377,3 +377,7 @@ EXTERN char e_import_as_name_not_supported_here[]
|
|||||||
INIT(= N_("E1169: 'import * as {name}' not supported here"));
|
INIT(= N_("E1169: 'import * as {name}' not supported here"));
|
||||||
EXTERN char e_cannot_use_hash_curly_to_start_comment[]
|
EXTERN char e_cannot_use_hash_curly_to_start_comment[]
|
||||||
INIT(= N_("E1170: Cannot use #{ to start a comment"));
|
INIT(= N_("E1170: Cannot use #{ to start a comment"));
|
||||||
|
EXTERN char e_missing_end_block[]
|
||||||
|
INIT(= N_("E1171: Missing } after inline function"));
|
||||||
|
EXTERN char e_cannot_use_default_values_in_lambda[]
|
||||||
|
INIT(= N_("E1172: Cannot use default values in a lambda"));
|
||||||
|
@ -2026,6 +2026,7 @@ ga_clear_strings(garray_T *gap)
|
|||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
if (gap->ga_data != NULL)
|
||||||
for (i = 0; i < gap->ga_len; ++i)
|
for (i = 0; i < gap->ga_len; ++i)
|
||||||
vim_free(((char_u **)(gap->ga_data))[i]);
|
vim_free(((char_u **)(gap->ga_data))[i]);
|
||||||
ga_clear(gap);
|
ga_clear(gap);
|
||||||
|
@ -14,6 +14,7 @@ char_u *to_name_end(char_u *arg, int use_namespace);
|
|||||||
char_u *to_name_const_end(char_u *arg);
|
char_u *to_name_const_end(char_u *arg);
|
||||||
exprtype_T get_compare_type(char_u *p, int *len, int *type_is);
|
exprtype_T get_compare_type(char_u *p, int *len, int *type_is);
|
||||||
void error_white_both(char_u *op, int len);
|
void error_white_both(char_u *op, int len);
|
||||||
|
void fill_exarg_from_cctx(exarg_T *eap, cctx_T *cctx);
|
||||||
int assignment_len(char_u *p, int *heredoc);
|
int assignment_len(char_u *p, int *heredoc);
|
||||||
void vim9_declare_error(char_u *name);
|
void vim9_declare_error(char_u *name);
|
||||||
int check_vim9_unlet(char_u *name);
|
int check_vim9_unlet(char_u *name);
|
||||||
|
@ -1946,6 +1946,25 @@ def Test_expr7_lambda()
|
|||||||
CheckScriptSuccess(lines)
|
CheckScriptSuccess(lines)
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
|
def Test_expr7_lambda_block()
|
||||||
|
var lines =<< trim END
|
||||||
|
var Func = (s: string): string => {
|
||||||
|
return 'hello ' .. s
|
||||||
|
}
|
||||||
|
assert_equal('hello there', Func('there'))
|
||||||
|
|
||||||
|
var ll = range(3)
|
||||||
|
var dll = mapnew(ll, (k, v): string => {
|
||||||
|
if v % 2
|
||||||
|
return 'yes'
|
||||||
|
endif
|
||||||
|
return 'no'
|
||||||
|
})
|
||||||
|
assert_equal(['no', 'yes', 'no'], dll)
|
||||||
|
END
|
||||||
|
CheckDefAndScriptSuccess(lines)
|
||||||
|
enddef
|
||||||
|
|
||||||
def NewLambdaWithComments(): func
|
def NewLambdaWithComments(): func
|
||||||
return (x) =>
|
return (x) =>
|
||||||
# some comment
|
# some comment
|
||||||
|
886
src/userfunc.c
886
src/userfunc.c
@ -397,6 +397,25 @@ parse_argument_types(ufunc_T *fp, garray_T *argtypes, int varargs)
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
parse_return_type(ufunc_T *fp, char_u *ret_type)
|
||||||
|
{
|
||||||
|
if (ret_type == NULL)
|
||||||
|
fp->uf_ret_type = &t_void;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
char_u *p = ret_type;
|
||||||
|
|
||||||
|
fp->uf_ret_type = parse_type(&p, &fp->uf_type_list, TRUE);
|
||||||
|
if (fp->uf_ret_type == NULL)
|
||||||
|
{
|
||||||
|
fp->uf_ret_type = &t_void;
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Register function "fp" as using "current_funccal" as its scope.
|
* Register function "fp" as using "current_funccal" as its scope.
|
||||||
*/
|
*/
|
||||||
@ -536,7 +555,501 @@ skip_arrow(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Parse a lambda expression and get a Funcref from "*arg".
|
* Check if "*cmd" points to a function command and if so advance "*cmd" and
|
||||||
|
* return TRUE.
|
||||||
|
* Otherwise return FALSE;
|
||||||
|
* Do not consider "function(" to be a command.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
is_function_cmd(char_u **cmd)
|
||||||
|
{
|
||||||
|
char_u *p = *cmd;
|
||||||
|
|
||||||
|
if (checkforcmd(&p, "function", 2))
|
||||||
|
{
|
||||||
|
if (*p == '(')
|
||||||
|
return FALSE;
|
||||||
|
*cmd = p;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read the body of a function, put every line in "newlines".
|
||||||
|
* "newlines" must already have been initialized.
|
||||||
|
* "eap->cmdidx" is CMD_function, CMD_def or CMD_block;
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
get_function_body(
|
||||||
|
exarg_T *eap,
|
||||||
|
garray_T *newlines,
|
||||||
|
char_u *line_arg_in,
|
||||||
|
char_u **line_to_free)
|
||||||
|
{
|
||||||
|
linenr_T sourcing_lnum_top = SOURCING_LNUM;
|
||||||
|
linenr_T sourcing_lnum_off;
|
||||||
|
int saved_wait_return = need_wait_return;
|
||||||
|
char_u *line_arg = line_arg_in;
|
||||||
|
int vim9_function = eap->cmdidx == CMD_def
|
||||||
|
|| eap->cmdidx == CMD_block;
|
||||||
|
#define MAX_FUNC_NESTING 50
|
||||||
|
char nesting_def[MAX_FUNC_NESTING];
|
||||||
|
int nesting = 0;
|
||||||
|
getline_opt_T getline_options;
|
||||||
|
int indent = 2;
|
||||||
|
char_u *skip_until = NULL;
|
||||||
|
int ret = FAIL;
|
||||||
|
int is_heredoc = FALSE;
|
||||||
|
char_u *heredoc_trimmed = NULL;
|
||||||
|
|
||||||
|
// Detect having skipped over comment lines to find the return
|
||||||
|
// type. Add NULL lines to keep the line count correct.
|
||||||
|
sourcing_lnum_off = get_sourced_lnum(eap->getline, eap->cookie);
|
||||||
|
if (SOURCING_LNUM < sourcing_lnum_off)
|
||||||
|
{
|
||||||
|
sourcing_lnum_off -= SOURCING_LNUM;
|
||||||
|
if (ga_grow(newlines, sourcing_lnum_off) == FAIL)
|
||||||
|
goto theend;
|
||||||
|
while (sourcing_lnum_off-- > 0)
|
||||||
|
((char_u **)(newlines->ga_data))[newlines->ga_len++] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
nesting_def[nesting] = vim9_function;
|
||||||
|
getline_options = vim9_function
|
||||||
|
? GETLINE_CONCAT_CONTBAR : GETLINE_CONCAT_CONT;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
char_u *theline;
|
||||||
|
char_u *p;
|
||||||
|
char_u *arg;
|
||||||
|
|
||||||
|
if (KeyTyped)
|
||||||
|
{
|
||||||
|
msg_scroll = TRUE;
|
||||||
|
saved_wait_return = FALSE;
|
||||||
|
}
|
||||||
|
need_wait_return = FALSE;
|
||||||
|
|
||||||
|
if (line_arg != NULL)
|
||||||
|
{
|
||||||
|
// Use eap->arg, split up in parts by line breaks.
|
||||||
|
theline = line_arg;
|
||||||
|
p = vim_strchr(theline, '\n');
|
||||||
|
if (p == NULL)
|
||||||
|
line_arg += STRLEN(line_arg);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*p = NUL;
|
||||||
|
line_arg = p + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
vim_free(*line_to_free);
|
||||||
|
if (eap->getline == NULL)
|
||||||
|
theline = getcmdline(':', 0L, indent, getline_options);
|
||||||
|
else
|
||||||
|
theline = eap->getline(':', eap->cookie, indent,
|
||||||
|
getline_options);
|
||||||
|
*line_to_free = theline;
|
||||||
|
}
|
||||||
|
if (KeyTyped)
|
||||||
|
lines_left = Rows - 1;
|
||||||
|
if (theline == NULL)
|
||||||
|
{
|
||||||
|
// Use the start of the function for the line number.
|
||||||
|
SOURCING_LNUM = sourcing_lnum_top;
|
||||||
|
if (skip_until != NULL)
|
||||||
|
semsg(_(e_missing_heredoc_end_marker_str), skip_until);
|
||||||
|
else if (eap->cmdidx == CMD_def)
|
||||||
|
emsg(_(e_missing_enddef));
|
||||||
|
else if (eap->cmdidx == CMD_block)
|
||||||
|
emsg(_(e_missing_end_block));
|
||||||
|
else
|
||||||
|
emsg(_("E126: Missing :endfunction"));
|
||||||
|
goto theend;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect line continuation: SOURCING_LNUM increased more than one.
|
||||||
|
sourcing_lnum_off = get_sourced_lnum(eap->getline, eap->cookie);
|
||||||
|
if (SOURCING_LNUM < sourcing_lnum_off)
|
||||||
|
sourcing_lnum_off -= SOURCING_LNUM;
|
||||||
|
else
|
||||||
|
sourcing_lnum_off = 0;
|
||||||
|
|
||||||
|
if (skip_until != NULL)
|
||||||
|
{
|
||||||
|
// Don't check for ":endfunc"/":enddef" between
|
||||||
|
// * ":append" and "."
|
||||||
|
// * ":python <<EOF" and "EOF"
|
||||||
|
// * ":let {var-name} =<< [trim] {marker}" and "{marker}"
|
||||||
|
if (heredoc_trimmed == NULL
|
||||||
|
|| (is_heredoc && skipwhite(theline) == theline)
|
||||||
|
|| STRNCMP(theline, heredoc_trimmed,
|
||||||
|
STRLEN(heredoc_trimmed)) == 0)
|
||||||
|
{
|
||||||
|
if (heredoc_trimmed == NULL)
|
||||||
|
p = theline;
|
||||||
|
else if (is_heredoc)
|
||||||
|
p = skipwhite(theline) == theline
|
||||||
|
? theline : theline + STRLEN(heredoc_trimmed);
|
||||||
|
else
|
||||||
|
p = theline + STRLEN(heredoc_trimmed);
|
||||||
|
if (STRCMP(p, skip_until) == 0)
|
||||||
|
{
|
||||||
|
VIM_CLEAR(skip_until);
|
||||||
|
VIM_CLEAR(heredoc_trimmed);
|
||||||
|
getline_options = vim9_function
|
||||||
|
? GETLINE_CONCAT_CONTBAR : GETLINE_CONCAT_CONT;
|
||||||
|
is_heredoc = FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int c;
|
||||||
|
|
||||||
|
// skip ':' and blanks
|
||||||
|
for (p = theline; VIM_ISWHITE(*p) || *p == ':'; ++p)
|
||||||
|
;
|
||||||
|
|
||||||
|
// Check for "endfunction", "enddef" or "}".
|
||||||
|
// When a ":" follows it must be a dict key; "enddef: value,"
|
||||||
|
if ((nesting == 0 && eap->cmdidx == CMD_block)
|
||||||
|
? *p == '}'
|
||||||
|
: (checkforcmd(&p, nesting_def[nesting]
|
||||||
|
? "enddef" : "endfunction", 4)
|
||||||
|
&& *p != ':'))
|
||||||
|
{
|
||||||
|
if (nesting-- == 0)
|
||||||
|
{
|
||||||
|
char_u *nextcmd = NULL;
|
||||||
|
|
||||||
|
if (*p == '|' || *p == '}')
|
||||||
|
nextcmd = p + 1;
|
||||||
|
else if (line_arg != NULL && *skipwhite(line_arg) != NUL)
|
||||||
|
nextcmd = line_arg;
|
||||||
|
else if (*p != NUL && *p != (vim9_function ? '#' : '"')
|
||||||
|
&& p_verbose > 0
|
||||||
|
&& eap->cmdidx != CMD_block)
|
||||||
|
give_warning2(eap->cmdidx == CMD_def
|
||||||
|
? (char_u *)_("W1001: Text found after :enddef: %s")
|
||||||
|
: (char_u *)_("W22: Text found after :endfunction: %s"),
|
||||||
|
p, TRUE);
|
||||||
|
if (nextcmd != NULL)
|
||||||
|
{
|
||||||
|
// Another command follows. If the line came from "eap"
|
||||||
|
// we can simply point into it, otherwise we need to
|
||||||
|
// change "eap->cmdlinep".
|
||||||
|
eap->nextcmd = nextcmd;
|
||||||
|
if (*line_to_free != NULL)
|
||||||
|
{
|
||||||
|
vim_free(*eap->cmdlinep);
|
||||||
|
*eap->cmdlinep = *line_to_free;
|
||||||
|
*line_to_free = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for mismatched "endfunc" or "enddef".
|
||||||
|
// We don't check for "def" inside "func" thus we also can't check
|
||||||
|
// for "enddef".
|
||||||
|
// We continue to find the end of the function, although we might
|
||||||
|
// not find it.
|
||||||
|
else if (nesting_def[nesting])
|
||||||
|
{
|
||||||
|
if (checkforcmd(&p, "endfunction", 4) && *p != ':')
|
||||||
|
emsg(_(e_mismatched_endfunction));
|
||||||
|
}
|
||||||
|
else if (eap->cmdidx == CMD_def && checkforcmd(&p, "enddef", 4))
|
||||||
|
emsg(_(e_mismatched_enddef));
|
||||||
|
|
||||||
|
// Increase indent inside "if", "while", "for" and "try", decrease
|
||||||
|
// at "end".
|
||||||
|
if (indent > 2 && (*p == '}' || STRNCMP(p, "end", 3) == 0))
|
||||||
|
indent -= 2;
|
||||||
|
else if (STRNCMP(p, "if", 2) == 0
|
||||||
|
|| STRNCMP(p, "wh", 2) == 0
|
||||||
|
|| STRNCMP(p, "for", 3) == 0
|
||||||
|
|| STRNCMP(p, "try", 3) == 0)
|
||||||
|
indent += 2;
|
||||||
|
|
||||||
|
// Check for defining a function inside this function.
|
||||||
|
// Only recognize "def" inside "def", not inside "function",
|
||||||
|
// For backwards compatibility, see Test_function_python().
|
||||||
|
c = *p;
|
||||||
|
if (is_function_cmd(&p)
|
||||||
|
|| (eap->cmdidx == CMD_def && checkforcmd(&p, "def", 3)))
|
||||||
|
{
|
||||||
|
if (*p == '!')
|
||||||
|
p = skipwhite(p + 1);
|
||||||
|
p += eval_fname_script(p);
|
||||||
|
vim_free(trans_function_name(&p, NULL, TRUE, 0, NULL,
|
||||||
|
NULL, NULL));
|
||||||
|
if (*skipwhite(p) == '(')
|
||||||
|
{
|
||||||
|
if (nesting == MAX_FUNC_NESTING - 1)
|
||||||
|
emsg(_(e_function_nesting_too_deep));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
++nesting;
|
||||||
|
nesting_def[nesting] = (c == 'd');
|
||||||
|
indent += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for ":append", ":change", ":insert". Not for :def.
|
||||||
|
p = skip_range(p, FALSE, NULL);
|
||||||
|
if (!vim9_function
|
||||||
|
&& ((p[0] == 'a' && (!ASCII_ISALPHA(p[1]) || p[1] == 'p'))
|
||||||
|
|| (p[0] == 'c'
|
||||||
|
&& (!ASCII_ISALPHA(p[1]) || (p[1] == 'h'
|
||||||
|
&& (!ASCII_ISALPHA(p[2]) || (p[2] == 'a'
|
||||||
|
&& (STRNCMP(&p[3], "nge", 3) != 0
|
||||||
|
|| !ASCII_ISALPHA(p[6])))))))
|
||||||
|
|| (p[0] == 'i'
|
||||||
|
&& (!ASCII_ISALPHA(p[1]) || (p[1] == 'n'
|
||||||
|
&& (!ASCII_ISALPHA(p[2])
|
||||||
|
|| (p[2] == 's'
|
||||||
|
&& (!ASCII_ISALPHA(p[3])
|
||||||
|
|| p[3] == 'e'))))))))
|
||||||
|
skip_until = vim_strsave((char_u *)".");
|
||||||
|
|
||||||
|
// Check for ":python <<EOF", ":tcl <<EOF", etc.
|
||||||
|
arg = skipwhite(skiptowhite(p));
|
||||||
|
if (arg[0] == '<' && arg[1] =='<'
|
||||||
|
&& ((p[0] == 'p' && p[1] == 'y'
|
||||||
|
&& (!ASCII_ISALNUM(p[2]) || p[2] == 't'
|
||||||
|
|| ((p[2] == '3' || p[2] == 'x')
|
||||||
|
&& !ASCII_ISALPHA(p[3]))))
|
||||||
|
|| (p[0] == 'p' && p[1] == 'e'
|
||||||
|
&& (!ASCII_ISALPHA(p[2]) || p[2] == 'r'))
|
||||||
|
|| (p[0] == 't' && p[1] == 'c'
|
||||||
|
&& (!ASCII_ISALPHA(p[2]) || p[2] == 'l'))
|
||||||
|
|| (p[0] == 'l' && p[1] == 'u' && p[2] == 'a'
|
||||||
|
&& !ASCII_ISALPHA(p[3]))
|
||||||
|
|| (p[0] == 'r' && p[1] == 'u' && p[2] == 'b'
|
||||||
|
&& (!ASCII_ISALPHA(p[3]) || p[3] == 'y'))
|
||||||
|
|| (p[0] == 'm' && p[1] == 'z'
|
||||||
|
&& (!ASCII_ISALPHA(p[2]) || p[2] == 's'))
|
||||||
|
))
|
||||||
|
{
|
||||||
|
// ":python <<" continues until a dot, like ":append"
|
||||||
|
p = skipwhite(arg + 2);
|
||||||
|
if (STRNCMP(p, "trim", 4) == 0)
|
||||||
|
{
|
||||||
|
// Ignore leading white space.
|
||||||
|
p = skipwhite(p + 4);
|
||||||
|
heredoc_trimmed = vim_strnsave(theline,
|
||||||
|
skipwhite(theline) - theline);
|
||||||
|
}
|
||||||
|
if (*p == NUL)
|
||||||
|
skip_until = vim_strsave((char_u *)".");
|
||||||
|
else
|
||||||
|
skip_until = vim_strnsave(p, skiptowhite(p) - p);
|
||||||
|
getline_options = GETLINE_NONE;
|
||||||
|
is_heredoc = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for ":cmd v =<< [trim] EOF"
|
||||||
|
// and ":cmd [a, b] =<< [trim] EOF"
|
||||||
|
// and "lines =<< [trim] EOF" for Vim9
|
||||||
|
// Where "cmd" can be "let", "var", "final" or "const".
|
||||||
|
arg = skipwhite(skiptowhite(p));
|
||||||
|
if (*arg == '[')
|
||||||
|
arg = vim_strchr(arg, ']');
|
||||||
|
if (arg != NULL)
|
||||||
|
{
|
||||||
|
int found = (eap->cmdidx == CMD_def && arg[0] == '='
|
||||||
|
&& arg[1] == '<' && arg[2] =='<');
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
// skip over the argument after "cmd"
|
||||||
|
arg = skipwhite(skiptowhite(arg));
|
||||||
|
if (found || (arg[0] == '=' && arg[1] == '<' && arg[2] =='<'
|
||||||
|
&& (checkforcmd(&p, "let", 2)
|
||||||
|
|| checkforcmd(&p, "var", 3)
|
||||||
|
|| checkforcmd(&p, "final", 5)
|
||||||
|
|| checkforcmd(&p, "const", 5))))
|
||||||
|
{
|
||||||
|
p = skipwhite(arg + 3);
|
||||||
|
if (STRNCMP(p, "trim", 4) == 0)
|
||||||
|
{
|
||||||
|
// Ignore leading white space.
|
||||||
|
p = skipwhite(p + 4);
|
||||||
|
heredoc_trimmed = vim_strnsave(theline,
|
||||||
|
skipwhite(theline) - theline);
|
||||||
|
}
|
||||||
|
skip_until = vim_strnsave(p, skiptowhite(p) - p);
|
||||||
|
getline_options = GETLINE_NONE;
|
||||||
|
is_heredoc = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the line to the function.
|
||||||
|
if (ga_grow(newlines, 1 + sourcing_lnum_off) == FAIL)
|
||||||
|
goto theend;
|
||||||
|
|
||||||
|
// Copy the line to newly allocated memory. get_one_sourceline()
|
||||||
|
// allocates 250 bytes per line, this saves 80% on average. The cost
|
||||||
|
// is an extra alloc/free.
|
||||||
|
p = vim_strsave(theline);
|
||||||
|
if (p == NULL)
|
||||||
|
goto theend;
|
||||||
|
((char_u **)(newlines->ga_data))[newlines->ga_len++] = p;
|
||||||
|
|
||||||
|
// Add NULL lines for continuation lines, so that the line count is
|
||||||
|
// equal to the index in the growarray.
|
||||||
|
while (sourcing_lnum_off-- > 0)
|
||||||
|
((char_u **)(newlines->ga_data))[newlines->ga_len++] = NULL;
|
||||||
|
|
||||||
|
// Check for end of eap->arg.
|
||||||
|
if (line_arg != NULL && *line_arg == NUL)
|
||||||
|
line_arg = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't define the function when skipping commands or when an error was
|
||||||
|
// detected.
|
||||||
|
if (!eap->skip && !did_emsg)
|
||||||
|
ret = OK;
|
||||||
|
|
||||||
|
theend:
|
||||||
|
vim_free(skip_until);
|
||||||
|
vim_free(heredoc_trimmed);
|
||||||
|
need_wait_return |= saved_wait_return;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handle the body of a lambda. *arg points to the "{", process statements
|
||||||
|
* until the matching "}".
|
||||||
|
* When successful "rettv" is set to a funcref.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
lambda_function_body(
|
||||||
|
char_u **arg,
|
||||||
|
typval_T *rettv,
|
||||||
|
evalarg_T *evalarg,
|
||||||
|
garray_T *newargs,
|
||||||
|
garray_T *argtypes,
|
||||||
|
int varargs,
|
||||||
|
garray_T *default_args,
|
||||||
|
char_u *ret_type)
|
||||||
|
{
|
||||||
|
int evaluate = evalarg != NULL
|
||||||
|
&& (evalarg->eval_flags & EVAL_EVALUATE);
|
||||||
|
ufunc_T *ufunc;
|
||||||
|
exarg_T eap;
|
||||||
|
garray_T newlines;
|
||||||
|
char_u *cmdline = NULL;
|
||||||
|
int ret = FAIL;
|
||||||
|
char_u *line_to_free = NULL;
|
||||||
|
partial_T *pt;
|
||||||
|
char_u *name;
|
||||||
|
int lnum_save = -1;
|
||||||
|
linenr_T sourcing_lnum_top = SOURCING_LNUM;
|
||||||
|
|
||||||
|
CLEAR_FIELD(eap);
|
||||||
|
eap.cmdidx = CMD_block;
|
||||||
|
eap.forceit = FALSE;
|
||||||
|
eap.arg = *arg + 1;
|
||||||
|
eap.cmdlinep = &cmdline;
|
||||||
|
eap.skip = !evaluate;
|
||||||
|
if (evalarg->eval_cctx != NULL)
|
||||||
|
fill_exarg_from_cctx(&eap, evalarg->eval_cctx);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
eap.getline = evalarg->eval_getline;
|
||||||
|
eap.cookie = evalarg->eval_cookie;
|
||||||
|
}
|
||||||
|
|
||||||
|
ga_init2(&newlines, (int)sizeof(char_u *), 10);
|
||||||
|
if (get_function_body(&eap, &newlines, NULL, &line_to_free) == FAIL)
|
||||||
|
goto erret;
|
||||||
|
if (cmdline != NULL)
|
||||||
|
{
|
||||||
|
// Something comes after the "}".
|
||||||
|
*arg = eap.nextcmd;
|
||||||
|
if (evalarg->eval_cctx == NULL)
|
||||||
|
{
|
||||||
|
// Need to keep the line and free it/ later.
|
||||||
|
vim_free(evalarg->eval_tofree_lambda);
|
||||||
|
evalarg->eval_tofree_lambda = cmdline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
*arg = (char_u *)"";
|
||||||
|
|
||||||
|
name = get_lambda_name();
|
||||||
|
ufunc = alloc_clear(offsetof(ufunc_T, uf_name) + STRLEN(name) + 1);
|
||||||
|
if (ufunc == NULL)
|
||||||
|
goto erret;
|
||||||
|
set_ufunc_name(ufunc, name);
|
||||||
|
if (hash_add(&func_hashtab, UF2HIKEY(ufunc)) == FAIL)
|
||||||
|
{
|
||||||
|
vim_free(ufunc);
|
||||||
|
goto erret;
|
||||||
|
}
|
||||||
|
ufunc->uf_refcount = 1;
|
||||||
|
ufunc->uf_args = *newargs;
|
||||||
|
newargs->ga_data = NULL;
|
||||||
|
ufunc->uf_def_args = *default_args;
|
||||||
|
default_args->ga_data = NULL;
|
||||||
|
ufunc->uf_func_type = &t_func_any;
|
||||||
|
|
||||||
|
// error messages are for the first function line
|
||||||
|
lnum_save = SOURCING_LNUM;
|
||||||
|
SOURCING_LNUM = sourcing_lnum_top;
|
||||||
|
|
||||||
|
// parse argument types
|
||||||
|
if (parse_argument_types(ufunc, argtypes, varargs) == FAIL)
|
||||||
|
{
|
||||||
|
SOURCING_LNUM = lnum_save;
|
||||||
|
goto erret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse the return type, if any
|
||||||
|
if (parse_return_type(ufunc, ret_type) == FAIL)
|
||||||
|
goto erret;
|
||||||
|
|
||||||
|
pt = ALLOC_CLEAR_ONE(partial_T);
|
||||||
|
if (pt == NULL)
|
||||||
|
goto erret;
|
||||||
|
pt->pt_func = ufunc;
|
||||||
|
pt->pt_refcount = 1;
|
||||||
|
|
||||||
|
ufunc->uf_lines = newlines;
|
||||||
|
newlines.ga_data = NULL;
|
||||||
|
if (sandbox)
|
||||||
|
ufunc->uf_flags |= FC_SANDBOX;
|
||||||
|
if (!ASCII_ISUPPER(*ufunc->uf_name))
|
||||||
|
ufunc->uf_flags |= FC_VIM9;
|
||||||
|
ufunc->uf_script_ctx = current_sctx;
|
||||||
|
ufunc->uf_script_ctx_version = current_sctx.sc_version;
|
||||||
|
ufunc->uf_script_ctx.sc_lnum += sourcing_lnum_top;
|
||||||
|
set_function_type(ufunc);
|
||||||
|
|
||||||
|
rettv->vval.v_partial = pt;
|
||||||
|
rettv->v_type = VAR_PARTIAL;
|
||||||
|
ret = OK;
|
||||||
|
|
||||||
|
erret:
|
||||||
|
if (lnum_save >= 0)
|
||||||
|
SOURCING_LNUM = lnum_save;
|
||||||
|
vim_free(line_to_free);
|
||||||
|
ga_clear_strings(&newlines);
|
||||||
|
ga_clear_strings(newargs);
|
||||||
|
ga_clear_strings(default_args);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse a lambda expression and get a Funcref from "*arg" into "rettv".
|
||||||
* "arg" points to the { in "{arg -> expr}" or the ( in "(arg) => expr"
|
* "arg" points to the { in "{arg -> expr}" or the ( in "(arg) => expr"
|
||||||
* When "types_optional" is TRUE optionally take argument types.
|
* When "types_optional" is TRUE optionally take argument types.
|
||||||
* Return OK or FAIL. Returns NOTDONE for dict or {expr}.
|
* Return OK or FAIL. Returns NOTDONE for dict or {expr}.
|
||||||
@ -554,6 +1067,7 @@ get_lambda_tv(
|
|||||||
garray_T newlines;
|
garray_T newlines;
|
||||||
garray_T *pnewargs;
|
garray_T *pnewargs;
|
||||||
garray_T argtypes;
|
garray_T argtypes;
|
||||||
|
garray_T default_args;
|
||||||
ufunc_T *fp = NULL;
|
ufunc_T *fp = NULL;
|
||||||
partial_T *pt = NULL;
|
partial_T *pt = NULL;
|
||||||
int varargs;
|
int varargs;
|
||||||
@ -596,7 +1110,8 @@ get_lambda_tv(
|
|||||||
*arg += 1;
|
*arg += 1;
|
||||||
ret = get_function_args(arg, equal_arrow ? ')' : '-', pnewargs,
|
ret = get_function_args(arg, equal_arrow ? ')' : '-', pnewargs,
|
||||||
types_optional ? &argtypes : NULL, types_optional, evalarg,
|
types_optional ? &argtypes : NULL, types_optional, evalarg,
|
||||||
&varargs, NULL, FALSE, NULL, NULL);
|
&varargs, &default_args,
|
||||||
|
FALSE, NULL, NULL);
|
||||||
if (ret == FAIL
|
if (ret == FAIL
|
||||||
|| (s = skip_arrow(*arg, equal_arrow, &ret_type,
|
|| (s = skip_arrow(*arg, equal_arrow, &ret_type,
|
||||||
equal_arrow || in_vim9script() ? &white_error : NULL)) == NULL)
|
equal_arrow || in_vim9script() ? &white_error : NULL)) == NULL)
|
||||||
@ -624,9 +1139,15 @@ get_lambda_tv(
|
|||||||
// Recognize "{" as the start of a function body.
|
// Recognize "{" as the start of a function body.
|
||||||
if (equal_arrow && **arg == '{')
|
if (equal_arrow && **arg == '{')
|
||||||
{
|
{
|
||||||
// TODO: process the function body upto the "}".
|
if (lambda_function_body(arg, rettv, evalarg, pnewargs,
|
||||||
// Return type is required then.
|
types_optional ? &argtypes : NULL, varargs,
|
||||||
emsg("Lambda function body not supported yet");
|
&default_args, ret_type) == FAIL)
|
||||||
|
goto errret;
|
||||||
|
goto theend;
|
||||||
|
}
|
||||||
|
if (default_args.ga_len > 0)
|
||||||
|
{
|
||||||
|
emsg(_(e_cannot_use_default_values_in_lambda));
|
||||||
goto errret;
|
goto errret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -732,6 +1253,7 @@ get_lambda_tv(
|
|||||||
hash_add(&func_hashtab, UF2HIKEY(fp));
|
hash_add(&func_hashtab, UF2HIKEY(fp));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
theend:
|
||||||
eval_lavars_used = old_eval_lavars;
|
eval_lavars_used = old_eval_lavars;
|
||||||
if (evalarg != NULL && evalarg->eval_tofree == NULL)
|
if (evalarg != NULL && evalarg->eval_tofree == NULL)
|
||||||
evalarg->eval_tofree = tofree1;
|
evalarg->eval_tofree = tofree1;
|
||||||
@ -745,6 +1267,7 @@ get_lambda_tv(
|
|||||||
errret:
|
errret:
|
||||||
ga_clear_strings(&newargs);
|
ga_clear_strings(&newargs);
|
||||||
ga_clear_strings(&newlines);
|
ga_clear_strings(&newlines);
|
||||||
|
ga_clear_strings(&default_args);
|
||||||
if (types_optional)
|
if (types_optional)
|
||||||
ga_clear_strings(&argtypes);
|
ga_clear_strings(&argtypes);
|
||||||
vim_free(fp);
|
vim_free(fp);
|
||||||
@ -2459,7 +2982,7 @@ call_func(
|
|||||||
{
|
{
|
||||||
// Check that the argument types are OK for the types of the funcref.
|
// Check that the argument types are OK for the types of the funcref.
|
||||||
if (check_argument_types(funcexe->check_type, argvars, argcount,
|
if (check_argument_types(funcexe->check_type, argvars, argcount,
|
||||||
name) == FAIL)
|
(name != NULL) ? name : funcname) == FAIL)
|
||||||
error = FCERR_OTHER;
|
error = FCERR_OTHER;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3005,27 +3528,6 @@ list_functions(regmatch_T *regmatch)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Check if "*cmd" points to a function command and if so advance "*cmd" and
|
|
||||||
* return TRUE.
|
|
||||||
* Otherwise return FALSE;
|
|
||||||
* Do not consider "function(" to be a command.
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
is_function_cmd(char_u **cmd)
|
|
||||||
{
|
|
||||||
char_u *p = *cmd;
|
|
||||||
|
|
||||||
if (checkforcmd(&p, "function", 2))
|
|
||||||
{
|
|
||||||
if (*p == '(')
|
|
||||||
return FALSE;
|
|
||||||
*cmd = p;
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ":function" also supporting nested ":def".
|
* ":function" also supporting nested ":def".
|
||||||
* When "name_arg" is not NULL this is a nested function, using "name_arg" for
|
* When "name_arg" is not NULL this is a nested function, using "name_arg" for
|
||||||
@ -3035,12 +3537,10 @@ is_function_cmd(char_u **cmd)
|
|||||||
ufunc_T *
|
ufunc_T *
|
||||||
define_function(exarg_T *eap, char_u *name_arg)
|
define_function(exarg_T *eap, char_u *name_arg)
|
||||||
{
|
{
|
||||||
char_u *theline;
|
|
||||||
char_u *line_to_free = NULL;
|
char_u *line_to_free = NULL;
|
||||||
int j;
|
int j;
|
||||||
int c;
|
int c;
|
||||||
int saved_did_emsg;
|
int saved_did_emsg;
|
||||||
int saved_wait_return = need_wait_return;
|
|
||||||
char_u *name = name_arg;
|
char_u *name = name_arg;
|
||||||
int is_global = FALSE;
|
int is_global = FALSE;
|
||||||
char_u *p;
|
char_u *p;
|
||||||
@ -3056,21 +3556,12 @@ define_function(exarg_T *eap, char_u *name_arg)
|
|||||||
char_u *ret_type = NULL;
|
char_u *ret_type = NULL;
|
||||||
ufunc_T *fp = NULL;
|
ufunc_T *fp = NULL;
|
||||||
int overwrite = FALSE;
|
int overwrite = FALSE;
|
||||||
int indent;
|
|
||||||
int nesting;
|
|
||||||
#define MAX_FUNC_NESTING 50
|
|
||||||
char nesting_def[MAX_FUNC_NESTING];
|
|
||||||
dictitem_T *v;
|
dictitem_T *v;
|
||||||
funcdict_T fudi;
|
funcdict_T fudi;
|
||||||
static int func_nr = 0; // number for nameless function
|
static int func_nr = 0; // number for nameless function
|
||||||
int paren;
|
int paren;
|
||||||
hashitem_T *hi;
|
hashitem_T *hi;
|
||||||
getline_opt_T getline_options;
|
|
||||||
linenr_T sourcing_lnum_off;
|
|
||||||
linenr_T sourcing_lnum_top;
|
linenr_T sourcing_lnum_top;
|
||||||
int is_heredoc = FALSE;
|
|
||||||
char_u *skip_until = NULL;
|
|
||||||
char_u *heredoc_trimmed = NULL;
|
|
||||||
int vim9script = in_vim9script();
|
int vim9script = in_vim9script();
|
||||||
imported_T *import = NULL;
|
imported_T *import = NULL;
|
||||||
|
|
||||||
@ -3263,7 +3754,7 @@ define_function(exarg_T *eap, char_u *name_arg)
|
|||||||
goto ret_free;
|
goto ret_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
ga_init2(&newlines, (int)sizeof(char_u *), 3);
|
ga_init2(&newlines, (int)sizeof(char_u *), 10);
|
||||||
|
|
||||||
if (!eap->skip && name_arg == NULL)
|
if (!eap->skip && name_arg == NULL)
|
||||||
{
|
{
|
||||||
@ -3399,309 +3890,7 @@ define_function(exarg_T *eap, char_u *name_arg)
|
|||||||
// Save the starting line number.
|
// Save the starting line number.
|
||||||
sourcing_lnum_top = SOURCING_LNUM;
|
sourcing_lnum_top = SOURCING_LNUM;
|
||||||
|
|
||||||
// Detect having skipped over comment lines to find the return
|
if (get_function_body(eap, &newlines, line_arg, &line_to_free) == FAIL)
|
||||||
// type. Add NULL lines to keep the line count correct.
|
|
||||||
sourcing_lnum_off = get_sourced_lnum(eap->getline, eap->cookie);
|
|
||||||
if (SOURCING_LNUM < sourcing_lnum_off)
|
|
||||||
{
|
|
||||||
sourcing_lnum_off -= SOURCING_LNUM;
|
|
||||||
if (ga_grow(&newlines, sourcing_lnum_off) == FAIL)
|
|
||||||
goto erret;
|
|
||||||
while (sourcing_lnum_off-- > 0)
|
|
||||||
((char_u **)(newlines.ga_data))[newlines.ga_len++] = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
indent = 2;
|
|
||||||
nesting = 0;
|
|
||||||
nesting_def[nesting] = (eap->cmdidx == CMD_def);
|
|
||||||
getline_options = eap->cmdidx == CMD_def
|
|
||||||
? GETLINE_CONCAT_CONTBAR : GETLINE_CONCAT_CONT;
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
if (KeyTyped)
|
|
||||||
{
|
|
||||||
msg_scroll = TRUE;
|
|
||||||
saved_wait_return = FALSE;
|
|
||||||
}
|
|
||||||
need_wait_return = FALSE;
|
|
||||||
|
|
||||||
if (line_arg != NULL)
|
|
||||||
{
|
|
||||||
// Use eap->arg, split up in parts by line breaks.
|
|
||||||
theline = line_arg;
|
|
||||||
p = vim_strchr(theline, '\n');
|
|
||||||
if (p == NULL)
|
|
||||||
line_arg += STRLEN(line_arg);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*p = NUL;
|
|
||||||
line_arg = p + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
vim_free(line_to_free);
|
|
||||||
if (eap->getline == NULL)
|
|
||||||
theline = getcmdline(':', 0L, indent, getline_options);
|
|
||||||
else
|
|
||||||
theline = eap->getline(':', eap->cookie, indent,
|
|
||||||
getline_options);
|
|
||||||
line_to_free = theline;
|
|
||||||
}
|
|
||||||
if (KeyTyped)
|
|
||||||
lines_left = Rows - 1;
|
|
||||||
if (theline == NULL)
|
|
||||||
{
|
|
||||||
// Use the start of the function for the line number.
|
|
||||||
SOURCING_LNUM = sourcing_lnum_top;
|
|
||||||
if (skip_until != NULL)
|
|
||||||
semsg(_(e_missing_heredoc_end_marker_str), skip_until);
|
|
||||||
else if (eap->cmdidx == CMD_def)
|
|
||||||
emsg(_(e_missing_enddef));
|
|
||||||
else
|
|
||||||
emsg(_("E126: Missing :endfunction"));
|
|
||||||
goto erret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detect line continuation: SOURCING_LNUM increased more than one.
|
|
||||||
sourcing_lnum_off = get_sourced_lnum(eap->getline, eap->cookie);
|
|
||||||
if (SOURCING_LNUM < sourcing_lnum_off)
|
|
||||||
sourcing_lnum_off -= SOURCING_LNUM;
|
|
||||||
else
|
|
||||||
sourcing_lnum_off = 0;
|
|
||||||
|
|
||||||
if (skip_until != NULL)
|
|
||||||
{
|
|
||||||
// Don't check for ":endfunc"/":enddef" between
|
|
||||||
// * ":append" and "."
|
|
||||||
// * ":python <<EOF" and "EOF"
|
|
||||||
// * ":let {var-name} =<< [trim] {marker}" and "{marker}"
|
|
||||||
if (heredoc_trimmed == NULL
|
|
||||||
|| (is_heredoc && skipwhite(theline) == theline)
|
|
||||||
|| STRNCMP(theline, heredoc_trimmed,
|
|
||||||
STRLEN(heredoc_trimmed)) == 0)
|
|
||||||
{
|
|
||||||
if (heredoc_trimmed == NULL)
|
|
||||||
p = theline;
|
|
||||||
else if (is_heredoc)
|
|
||||||
p = skipwhite(theline) == theline
|
|
||||||
? theline : theline + STRLEN(heredoc_trimmed);
|
|
||||||
else
|
|
||||||
p = theline + STRLEN(heredoc_trimmed);
|
|
||||||
if (STRCMP(p, skip_until) == 0)
|
|
||||||
{
|
|
||||||
VIM_CLEAR(skip_until);
|
|
||||||
VIM_CLEAR(heredoc_trimmed);
|
|
||||||
getline_options = eap->cmdidx == CMD_def
|
|
||||||
? GETLINE_CONCAT_CONTBAR : GETLINE_CONCAT_CONT;
|
|
||||||
is_heredoc = FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// skip ':' and blanks
|
|
||||||
for (p = theline; VIM_ISWHITE(*p) || *p == ':'; ++p)
|
|
||||||
;
|
|
||||||
|
|
||||||
// Check for "endfunction" or "enddef".
|
|
||||||
// When a ":" follows it must be a dict key; "enddef: value,"
|
|
||||||
if (checkforcmd(&p, nesting_def[nesting]
|
|
||||||
? "enddef" : "endfunction", 4)
|
|
||||||
&& *p != ':')
|
|
||||||
{
|
|
||||||
if (nesting-- == 0)
|
|
||||||
{
|
|
||||||
char_u *nextcmd = NULL;
|
|
||||||
|
|
||||||
if (*p == '|')
|
|
||||||
nextcmd = p + 1;
|
|
||||||
else if (line_arg != NULL && *skipwhite(line_arg) != NUL)
|
|
||||||
nextcmd = line_arg;
|
|
||||||
else if (*p != NUL && *p != '"' && p_verbose > 0)
|
|
||||||
give_warning2(eap->cmdidx == CMD_def
|
|
||||||
? (char_u *)_("W1001: Text found after :enddef: %s")
|
|
||||||
: (char_u *)_("W22: Text found after :endfunction: %s"),
|
|
||||||
p, TRUE);
|
|
||||||
if (nextcmd != NULL)
|
|
||||||
{
|
|
||||||
// Another command follows. If the line came from "eap"
|
|
||||||
// we can simply point into it, otherwise we need to
|
|
||||||
// change "eap->cmdlinep".
|
|
||||||
eap->nextcmd = nextcmd;
|
|
||||||
if (line_to_free != NULL)
|
|
||||||
{
|
|
||||||
vim_free(*eap->cmdlinep);
|
|
||||||
*eap->cmdlinep = line_to_free;
|
|
||||||
line_to_free = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for mismatched "endfunc" or "enddef".
|
|
||||||
// We don't check for "def" inside "func" thus we also can't check
|
|
||||||
// for "enddef".
|
|
||||||
// We continue to find the end of the function, although we might
|
|
||||||
// not find it.
|
|
||||||
else if (nesting_def[nesting])
|
|
||||||
{
|
|
||||||
if (checkforcmd(&p, "endfunction", 4) && *p != ':')
|
|
||||||
emsg(_(e_mismatched_endfunction));
|
|
||||||
}
|
|
||||||
else if (eap->cmdidx == CMD_def && checkforcmd(&p, "enddef", 4))
|
|
||||||
emsg(_(e_mismatched_enddef));
|
|
||||||
|
|
||||||
// Increase indent inside "if", "while", "for" and "try", decrease
|
|
||||||
// at "end".
|
|
||||||
if (indent > 2 && (*p == '}' || STRNCMP(p, "end", 3) == 0))
|
|
||||||
indent -= 2;
|
|
||||||
else if (STRNCMP(p, "if", 2) == 0
|
|
||||||
|| STRNCMP(p, "wh", 2) == 0
|
|
||||||
|| STRNCMP(p, "for", 3) == 0
|
|
||||||
|| STRNCMP(p, "try", 3) == 0)
|
|
||||||
indent += 2;
|
|
||||||
|
|
||||||
// Check for defining a function inside this function.
|
|
||||||
// Only recognize "def" inside "def", not inside "function",
|
|
||||||
// For backwards compatibility, see Test_function_python().
|
|
||||||
c = *p;
|
|
||||||
if (is_function_cmd(&p)
|
|
||||||
|| (eap->cmdidx == CMD_def && checkforcmd(&p, "def", 3)))
|
|
||||||
{
|
|
||||||
if (*p == '!')
|
|
||||||
p = skipwhite(p + 1);
|
|
||||||
p += eval_fname_script(p);
|
|
||||||
vim_free(trans_function_name(&p, NULL, TRUE, 0, NULL,
|
|
||||||
NULL, NULL));
|
|
||||||
if (*skipwhite(p) == '(')
|
|
||||||
{
|
|
||||||
if (nesting == MAX_FUNC_NESTING - 1)
|
|
||||||
emsg(_(e_function_nesting_too_deep));
|
|
||||||
else
|
|
||||||
{
|
|
||||||
++nesting;
|
|
||||||
nesting_def[nesting] = (c == 'd');
|
|
||||||
indent += 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for ":append", ":change", ":insert". Not for :def.
|
|
||||||
p = skip_range(p, FALSE, NULL);
|
|
||||||
if (eap->cmdidx != CMD_def
|
|
||||||
&& ((p[0] == 'a' && (!ASCII_ISALPHA(p[1]) || p[1] == 'p'))
|
|
||||||
|| (p[0] == 'c'
|
|
||||||
&& (!ASCII_ISALPHA(p[1]) || (p[1] == 'h'
|
|
||||||
&& (!ASCII_ISALPHA(p[2]) || (p[2] == 'a'
|
|
||||||
&& (STRNCMP(&p[3], "nge", 3) != 0
|
|
||||||
|| !ASCII_ISALPHA(p[6])))))))
|
|
||||||
|| (p[0] == 'i'
|
|
||||||
&& (!ASCII_ISALPHA(p[1]) || (p[1] == 'n'
|
|
||||||
&& (!ASCII_ISALPHA(p[2])
|
|
||||||
|| (p[2] == 's'
|
|
||||||
&& (!ASCII_ISALPHA(p[3])
|
|
||||||
|| p[3] == 'e'))))))))
|
|
||||||
skip_until = vim_strsave((char_u *)".");
|
|
||||||
|
|
||||||
// Check for ":python <<EOF", ":tcl <<EOF", etc.
|
|
||||||
arg = skipwhite(skiptowhite(p));
|
|
||||||
if (arg[0] == '<' && arg[1] =='<'
|
|
||||||
&& ((p[0] == 'p' && p[1] == 'y'
|
|
||||||
&& (!ASCII_ISALNUM(p[2]) || p[2] == 't'
|
|
||||||
|| ((p[2] == '3' || p[2] == 'x')
|
|
||||||
&& !ASCII_ISALPHA(p[3]))))
|
|
||||||
|| (p[0] == 'p' && p[1] == 'e'
|
|
||||||
&& (!ASCII_ISALPHA(p[2]) || p[2] == 'r'))
|
|
||||||
|| (p[0] == 't' && p[1] == 'c'
|
|
||||||
&& (!ASCII_ISALPHA(p[2]) || p[2] == 'l'))
|
|
||||||
|| (p[0] == 'l' && p[1] == 'u' && p[2] == 'a'
|
|
||||||
&& !ASCII_ISALPHA(p[3]))
|
|
||||||
|| (p[0] == 'r' && p[1] == 'u' && p[2] == 'b'
|
|
||||||
&& (!ASCII_ISALPHA(p[3]) || p[3] == 'y'))
|
|
||||||
|| (p[0] == 'm' && p[1] == 'z'
|
|
||||||
&& (!ASCII_ISALPHA(p[2]) || p[2] == 's'))
|
|
||||||
))
|
|
||||||
{
|
|
||||||
// ":python <<" continues until a dot, like ":append"
|
|
||||||
p = skipwhite(arg + 2);
|
|
||||||
if (STRNCMP(p, "trim", 4) == 0)
|
|
||||||
{
|
|
||||||
// Ignore leading white space.
|
|
||||||
p = skipwhite(p + 4);
|
|
||||||
heredoc_trimmed = vim_strnsave(theline,
|
|
||||||
skipwhite(theline) - theline);
|
|
||||||
}
|
|
||||||
if (*p == NUL)
|
|
||||||
skip_until = vim_strsave((char_u *)".");
|
|
||||||
else
|
|
||||||
skip_until = vim_strnsave(p, skiptowhite(p) - p);
|
|
||||||
getline_options = GETLINE_NONE;
|
|
||||||
is_heredoc = TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for ":cmd v =<< [trim] EOF"
|
|
||||||
// and ":cmd [a, b] =<< [trim] EOF"
|
|
||||||
// and "lines =<< [trim] EOF" for Vim9
|
|
||||||
// Where "cmd" can be "let", "var", "final" or "const".
|
|
||||||
arg = skipwhite(skiptowhite(p));
|
|
||||||
if (*arg == '[')
|
|
||||||
arg = vim_strchr(arg, ']');
|
|
||||||
if (arg != NULL)
|
|
||||||
{
|
|
||||||
int found = (eap->cmdidx == CMD_def && arg[0] == '='
|
|
||||||
&& arg[1] == '<' && arg[2] =='<');
|
|
||||||
|
|
||||||
if (!found)
|
|
||||||
// skip over the argument after "cmd"
|
|
||||||
arg = skipwhite(skiptowhite(arg));
|
|
||||||
if (found || (arg[0] == '=' && arg[1] == '<' && arg[2] =='<'
|
|
||||||
&& (checkforcmd(&p, "let", 2)
|
|
||||||
|| checkforcmd(&p, "var", 3)
|
|
||||||
|| checkforcmd(&p, "final", 5)
|
|
||||||
|| checkforcmd(&p, "const", 5))))
|
|
||||||
{
|
|
||||||
p = skipwhite(arg + 3);
|
|
||||||
if (STRNCMP(p, "trim", 4) == 0)
|
|
||||||
{
|
|
||||||
// Ignore leading white space.
|
|
||||||
p = skipwhite(p + 4);
|
|
||||||
heredoc_trimmed = vim_strnsave(theline,
|
|
||||||
skipwhite(theline) - theline);
|
|
||||||
}
|
|
||||||
skip_until = vim_strnsave(p, skiptowhite(p) - p);
|
|
||||||
getline_options = GETLINE_NONE;
|
|
||||||
is_heredoc = TRUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the line to the function.
|
|
||||||
if (ga_grow(&newlines, 1 + sourcing_lnum_off) == FAIL)
|
|
||||||
goto erret;
|
|
||||||
|
|
||||||
// Copy the line to newly allocated memory. get_one_sourceline()
|
|
||||||
// allocates 250 bytes per line, this saves 80% on average. The cost
|
|
||||||
// is an extra alloc/free.
|
|
||||||
p = vim_strsave(theline);
|
|
||||||
if (p == NULL)
|
|
||||||
goto erret;
|
|
||||||
((char_u **)(newlines.ga_data))[newlines.ga_len++] = p;
|
|
||||||
|
|
||||||
// Add NULL lines for continuation lines, so that the line count is
|
|
||||||
// equal to the index in the growarray.
|
|
||||||
while (sourcing_lnum_off-- > 0)
|
|
||||||
((char_u **)(newlines.ga_data))[newlines.ga_len++] = NULL;
|
|
||||||
|
|
||||||
// Check for end of eap->arg.
|
|
||||||
if (line_arg != NULL && *line_arg == NUL)
|
|
||||||
line_arg = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't define the function when skipping commands or when an error was
|
|
||||||
// detected.
|
|
||||||
if (eap->skip || did_emsg)
|
|
||||||
goto erret;
|
goto erret;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -3933,19 +4122,11 @@ define_function(exarg_T *eap, char_u *name_arg)
|
|||||||
varargs = FALSE;
|
varargs = FALSE;
|
||||||
|
|
||||||
// parse the return type, if any
|
// parse the return type, if any
|
||||||
if (ret_type == NULL)
|
if (parse_return_type(fp, ret_type) == FAIL)
|
||||||
fp->uf_ret_type = &t_void;
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
p = ret_type;
|
|
||||||
fp->uf_ret_type = parse_type(&p, &fp->uf_type_list, TRUE);
|
|
||||||
if (fp->uf_ret_type == NULL)
|
|
||||||
{
|
|
||||||
fp->uf_ret_type = &t_void;
|
|
||||||
SOURCING_LNUM = lnum_save;
|
SOURCING_LNUM = lnum_save;
|
||||||
goto erret;
|
goto erret;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
SOURCING_LNUM = lnum_save;
|
SOURCING_LNUM = lnum_save;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -4004,15 +4185,12 @@ errret_2:
|
|||||||
VIM_CLEAR(fp->uf_arg_types);
|
VIM_CLEAR(fp->uf_arg_types);
|
||||||
ret_free:
|
ret_free:
|
||||||
ga_clear_strings(&argtypes);
|
ga_clear_strings(&argtypes);
|
||||||
vim_free(skip_until);
|
|
||||||
vim_free(heredoc_trimmed);
|
|
||||||
vim_free(line_to_free);
|
vim_free(line_to_free);
|
||||||
vim_free(fudi.fd_newkey);
|
vim_free(fudi.fd_newkey);
|
||||||
if (name != name_arg)
|
if (name != name_arg)
|
||||||
vim_free(name);
|
vim_free(name);
|
||||||
vim_free(ret_type);
|
vim_free(ret_type);
|
||||||
did_emsg |= saved_did_emsg;
|
did_emsg |= saved_did_emsg;
|
||||||
need_wait_return |= saved_wait_return;
|
|
||||||
|
|
||||||
return fp;
|
return fp;
|
||||||
}
|
}
|
||||||
|
@ -750,6 +750,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
2635,
|
||||||
/**/
|
/**/
|
||||||
2634,
|
2634,
|
||||||
/**/
|
/**/
|
||||||
|
@ -3171,7 +3171,7 @@ compile_list(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Parse a lambda: "(arg, arg) => expr"
|
* Parse a lambda: "(arg, arg) => expr"
|
||||||
* "*arg" points to the '{'.
|
* "*arg" points to the '('.
|
||||||
* Returns OK/FAIL when a lambda is recognized, NOTDONE if it's not a lambda.
|
* Returns OK/FAIL when a lambda is recognized, NOTDONE if it's not a lambda.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
@ -5126,6 +5126,13 @@ exarg_getline(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
fill_exarg_from_cctx(exarg_T *eap, cctx_T *cctx)
|
||||||
|
{
|
||||||
|
eap->getline = exarg_getline;
|
||||||
|
eap->cookie = cctx;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Compile a nested :def command.
|
* Compile a nested :def command.
|
||||||
*/
|
*/
|
||||||
@ -5176,9 +5183,8 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx)
|
|||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
eap->arg = name_end;
|
eap->arg = name_end;
|
||||||
eap->getline = exarg_getline;
|
fill_exarg_from_cctx(eap, cctx);
|
||||||
eap->cookie = cctx;
|
|
||||||
eap->skip = cctx->ctx_skip == SKIP_YES;
|
|
||||||
eap->forceit = FALSE;
|
eap->forceit = FALSE;
|
||||||
lambda_name = vim_strsave(get_lambda_name());
|
lambda_name = vim_strsave(get_lambda_name());
|
||||||
if (lambda_name == NULL)
|
if (lambda_name == NULL)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user