forked from aniani/vim
patch 8.2.1155: Vim9: cannot handle line break inside lambda
Problem: Vim9: cannot handle line break inside lambda. Solution: Pass the compilation context through. (closes #6407, closes #6409)
This commit is contained in:
86
src/eval.c
86
src/eval.c
@@ -390,11 +390,12 @@ skip_expr_concatenate(char_u **start, char_u **end, evalarg_T *evalarg)
|
|||||||
garray_T *gap = &evalarg->eval_ga;
|
garray_T *gap = &evalarg->eval_ga;
|
||||||
int save_flags = evalarg == NULL ? 0 : evalarg->eval_flags;
|
int save_flags = evalarg == NULL ? 0 : evalarg->eval_flags;
|
||||||
|
|
||||||
if (vim9script && evalarg->eval_cookie != NULL)
|
if (vim9script
|
||||||
|
&& (evalarg->eval_cookie != NULL || evalarg->eval_cctx != NULL))
|
||||||
{
|
{
|
||||||
ga_init2(gap, sizeof(char_u *), 10);
|
ga_init2(gap, sizeof(char_u *), 10);
|
||||||
if (ga_grow(gap, 1) == OK)
|
|
||||||
// leave room for "start"
|
// leave room for "start"
|
||||||
|
if (ga_grow(gap, 1) == OK)
|
||||||
++gap->ga_len;
|
++gap->ga_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -406,8 +407,16 @@ skip_expr_concatenate(char_u **start, char_u **end, evalarg_T *evalarg)
|
|||||||
if (evalarg != NULL)
|
if (evalarg != NULL)
|
||||||
evalarg->eval_flags = save_flags;
|
evalarg->eval_flags = save_flags;
|
||||||
|
|
||||||
if (vim9script && evalarg->eval_cookie != NULL
|
if (vim9script
|
||||||
&& evalarg->eval_ga.ga_len > 1)
|
&& (evalarg->eval_cookie != NULL || evalarg->eval_cctx != NULL))
|
||||||
|
{
|
||||||
|
if (evalarg->eval_ga.ga_len == 1)
|
||||||
|
{
|
||||||
|
// just one line, no need to concatenate
|
||||||
|
ga_clear(gap);
|
||||||
|
gap->ga_itemsize = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
char_u *p;
|
char_u *p;
|
||||||
size_t endoff = STRLEN(*end);
|
size_t endoff = STRLEN(*end);
|
||||||
@@ -415,8 +424,15 @@ skip_expr_concatenate(char_u **start, char_u **end, evalarg_T *evalarg)
|
|||||||
// Line breaks encountered, concatenate all the lines.
|
// Line breaks encountered, concatenate all the lines.
|
||||||
*((char_u **)gap->ga_data) = *start;
|
*((char_u **)gap->ga_data) = *start;
|
||||||
p = ga_concat_strings(gap, "");
|
p = ga_concat_strings(gap, "");
|
||||||
|
|
||||||
|
// free the lines only when using getsourceline()
|
||||||
|
if (evalarg->eval_cookie != NULL)
|
||||||
|
{
|
||||||
*((char_u **)gap->ga_data) = NULL;
|
*((char_u **)gap->ga_data) = NULL;
|
||||||
ga_clear_strings(gap);
|
ga_clear_strings(gap);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ga_clear(gap);
|
||||||
gap->ga_itemsize = 0;
|
gap->ga_itemsize = 0;
|
||||||
if (p == NULL)
|
if (p == NULL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
@@ -426,12 +442,14 @@ skip_expr_concatenate(char_u **start, char_u **end, evalarg_T *evalarg)
|
|||||||
// Compute "end" relative to the end.
|
// Compute "end" relative to the end.
|
||||||
*end = *start + STRLEN(*start) - endoff;
|
*end = *start + STRLEN(*start) - endoff;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Top level evaluation function, returning a string.
|
* Top level evaluation function, returning a string. Does not handle line
|
||||||
|
* breaks.
|
||||||
* When "convert" is TRUE convert a List into a sequence of lines and convert
|
* When "convert" is TRUE convert a List into a sequence of lines and convert
|
||||||
* a Float to a String.
|
* a Float to a String.
|
||||||
* Return pointer to allocated memory, or NULL for failure.
|
* Return pointer to allocated memory, or NULL for failure.
|
||||||
@@ -1878,11 +1896,16 @@ eval_next_non_blank(char_u *arg, evalarg_T *evalarg, int *getnext)
|
|||||||
*getnext = FALSE;
|
*getnext = FALSE;
|
||||||
if (current_sctx.sc_version == SCRIPT_VERSION_VIM9
|
if (current_sctx.sc_version == SCRIPT_VERSION_VIM9
|
||||||
&& evalarg != NULL
|
&& evalarg != NULL
|
||||||
&& evalarg->eval_cookie != NULL
|
&& (evalarg->eval_cookie != NULL || evalarg->eval_cctx != NULL)
|
||||||
&& (*arg == NUL || (VIM_ISWHITE(arg[-1])
|
&& (*arg == NUL || (VIM_ISWHITE(arg[-1])
|
||||||
&& *arg == '#' && arg[1] != '{')))
|
&& *arg == '#' && arg[1] != '{')))
|
||||||
{
|
{
|
||||||
char_u *p = getline_peek(evalarg->eval_getline, evalarg->eval_cookie);
|
char_u *p;
|
||||||
|
|
||||||
|
if (evalarg->eval_cookie != NULL)
|
||||||
|
p = getline_peek(evalarg->eval_getline, evalarg->eval_cookie);
|
||||||
|
else
|
||||||
|
p = peek_next_line_from_context(evalarg->eval_cctx);
|
||||||
|
|
||||||
if (p != NULL)
|
if (p != NULL)
|
||||||
{
|
{
|
||||||
@@ -1902,7 +1925,10 @@ eval_next_line(evalarg_T *evalarg)
|
|||||||
garray_T *gap = &evalarg->eval_ga;
|
garray_T *gap = &evalarg->eval_ga;
|
||||||
char_u *line;
|
char_u *line;
|
||||||
|
|
||||||
|
if (evalarg->eval_cookie != NULL)
|
||||||
line = evalarg->eval_getline(0, evalarg->eval_cookie, 0, TRUE);
|
line = evalarg->eval_getline(0, evalarg->eval_cookie, 0, TRUE);
|
||||||
|
else
|
||||||
|
line = next_line_from_context(evalarg->eval_cctx, TRUE);
|
||||||
++evalarg->eval_break_count;
|
++evalarg->eval_break_count;
|
||||||
if (gap->ga_itemsize > 0 && ga_grow(gap, 1) == OK)
|
if (gap->ga_itemsize > 0 && ga_grow(gap, 1) == OK)
|
||||||
{
|
{
|
||||||
@@ -5034,35 +5060,27 @@ handle_subscript(
|
|||||||
int ret = OK;
|
int ret = OK;
|
||||||
dict_T *selfdict = NULL;
|
dict_T *selfdict = NULL;
|
||||||
int check_white = TRUE;
|
int check_white = TRUE;
|
||||||
|
|
||||||
// When at the end of the line and ".name" follows in the next line then
|
|
||||||
// consume the line break. Only when rettv is a dict.
|
|
||||||
if (rettv->v_type == VAR_DICT)
|
|
||||||
{
|
|
||||||
int getnext;
|
int getnext;
|
||||||
char_u *p = eval_next_non_blank(*arg, evalarg, &getnext);
|
char_u *p;
|
||||||
|
|
||||||
if (getnext && *p == '.' && ASCII_ISALPHA(p[1]))
|
while (ret == OK)
|
||||||
|
{
|
||||||
|
// When at the end of the line and ".name" or "->{" or "->X" follows in
|
||||||
|
// the next line then consume the line break.
|
||||||
|
p = eval_next_non_blank(*arg, evalarg, &getnext);
|
||||||
|
if (getnext
|
||||||
|
&& ((rettv->v_type == VAR_DICT && *p == '.'
|
||||||
|
&& ASCII_ISALPHA(p[1]))
|
||||||
|
|| (*p == '-' && p[1] == '>'
|
||||||
|
&& (p[2] == '{' || ASCII_ISALPHA(p[2])))))
|
||||||
{
|
{
|
||||||
*arg = eval_next_line(evalarg);
|
*arg = eval_next_line(evalarg);
|
||||||
check_white = FALSE;
|
check_white = FALSE;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// "." is ".name" lookup when we found a dict or when evaluating and
|
if ((**arg == '(' && (!evaluate || rettv->v_type == VAR_FUNC
|
||||||
// scriptversion is at least 2, where string concatenation is "..".
|
|| rettv->v_type == VAR_PARTIAL))
|
||||||
while (ret == OK
|
|
||||||
&& (((**arg == '['
|
|
||||||
|| (**arg == '.' && (rettv->v_type == VAR_DICT
|
|
||||||
|| (!evaluate
|
|
||||||
&& (*arg)[1] != '.'
|
|
||||||
&& current_sctx.sc_version >= 2)))
|
|
||||||
|| (**arg == '(' && (!evaluate || rettv->v_type == VAR_FUNC
|
|
||||||
|| rettv->v_type == VAR_PARTIAL)))
|
|
||||||
&& (!check_white || !VIM_ISWHITE(*(*arg - 1))))
|
&& (!check_white || !VIM_ISWHITE(*(*arg - 1))))
|
||||||
|| (**arg == '-' && (*arg)[1] == '>')))
|
|
||||||
{
|
|
||||||
if (**arg == '(')
|
|
||||||
{
|
{
|
||||||
ret = call_func_rettv(arg, evalarg, rettv, evaluate,
|
ret = call_func_rettv(arg, evalarg, rettv, evaluate,
|
||||||
selfdict, NULL);
|
selfdict, NULL);
|
||||||
@@ -5079,7 +5097,7 @@ handle_subscript(
|
|||||||
dict_unref(selfdict);
|
dict_unref(selfdict);
|
||||||
selfdict = NULL;
|
selfdict = NULL;
|
||||||
}
|
}
|
||||||
else if (**arg == '-')
|
else if (**arg == '-' && (*arg)[1] == '>')
|
||||||
{
|
{
|
||||||
if (ret == OK)
|
if (ret == OK)
|
||||||
{
|
{
|
||||||
@@ -5091,7 +5109,13 @@ handle_subscript(
|
|||||||
ret = eval_method(arg, rettv, evalarg, verbose);
|
ret = eval_method(arg, rettv, evalarg, verbose);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else // **arg == '[' || **arg == '.'
|
// "." is ".name" lookup when we found a dict or when evaluating and
|
||||||
|
// scriptversion is at least 2, where string concatenation is "..".
|
||||||
|
else if (**arg == '['
|
||||||
|
|| (**arg == '.' && (rettv->v_type == VAR_DICT
|
||||||
|
|| (!evaluate
|
||||||
|
&& (*arg)[1] != '.'
|
||||||
|
&& current_sctx.sc_version >= 2))))
|
||||||
{
|
{
|
||||||
dict_unref(selfdict);
|
dict_unref(selfdict);
|
||||||
if (rettv->v_type == VAR_DICT)
|
if (rettv->v_type == VAR_DICT)
|
||||||
@@ -5108,6 +5132,8 @@ handle_subscript(
|
|||||||
ret = FAIL;
|
ret = FAIL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Turn "dict.Func" into a partial for "Func" bound to "dict".
|
// Turn "dict.Func" into a partial for "Func" bound to "dict".
|
||||||
|
@@ -8,6 +8,8 @@ char *vartype_name(vartype_T type);
|
|||||||
char *type_name(type_T *type, char **tofree);
|
char *type_name(type_T *type, char **tofree);
|
||||||
int get_script_item_idx(int sid, char_u *name, int check_writable);
|
int get_script_item_idx(int sid, char_u *name, int check_writable);
|
||||||
imported_T *find_imported(char_u *name, size_t len, cctx_T *cctx);
|
imported_T *find_imported(char_u *name, size_t len, cctx_T *cctx);
|
||||||
|
char_u *peek_next_line_from_context(cctx_T *cctx);
|
||||||
|
char_u *next_line_from_context(cctx_T *cctx, int skip_comment);
|
||||||
char_u *to_name_const_end(char_u *arg);
|
char_u *to_name_const_end(char_u *arg);
|
||||||
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);
|
||||||
|
@@ -1765,6 +1765,9 @@ typedef struct {
|
|||||||
char_u *(*eval_getline)(int, void *, int, int);
|
char_u *(*eval_getline)(int, void *, int, int);
|
||||||
void *eval_cookie; // argument for eval_getline()
|
void *eval_cookie; // argument for eval_getline()
|
||||||
|
|
||||||
|
// used when compiling a :def function, NULL otherwise
|
||||||
|
cctx_T *eval_cctx;
|
||||||
|
|
||||||
// Used to collect lines while parsing them, so that they can be
|
// Used to collect lines while parsing them, so that they can be
|
||||||
// concatenated later. Used when "eval_ga.ga_itemsize" is not zero.
|
// concatenated later. Used when "eval_ga.ga_itemsize" is not zero.
|
||||||
// "eval_ga.ga_data" is a list of pointers to lines.
|
// "eval_ga.ga_data" is a list of pointers to lines.
|
||||||
|
@@ -965,6 +965,18 @@ def Test_line_continuation_in_def()
|
|||||||
assert_equal('full', Line_continuation_in_def('.'))
|
assert_equal('full', Line_continuation_in_def('.'))
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
|
def Line_continuation_in_lambda(): list<number>
|
||||||
|
let x = range(97, 100)
|
||||||
|
->map({_,v -> nr2char(v)
|
||||||
|
->toupper()})
|
||||||
|
->reverse()
|
||||||
|
return x
|
||||||
|
enddef
|
||||||
|
|
||||||
|
def Test_line_continuation_in_lambda()
|
||||||
|
assert_equal(['D', 'C', 'B', 'A'], Line_continuation_in_lambda())
|
||||||
|
enddef
|
||||||
|
|
||||||
func Test_silent_echo()
|
func Test_silent_echo()
|
||||||
CheckScreendump
|
CheckScreendump
|
||||||
|
|
||||||
|
@@ -754,6 +754,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 */
|
||||||
|
/**/
|
||||||
|
1155,
|
||||||
/**/
|
/**/
|
||||||
1154,
|
1154,
|
||||||
/**/
|
/**/
|
||||||
|
@@ -2397,8 +2397,8 @@ comment_start(char_u *p)
|
|||||||
* comment. Skips over white space.
|
* comment. Skips over white space.
|
||||||
* Returns NULL if there is none.
|
* Returns NULL if there is none.
|
||||||
*/
|
*/
|
||||||
static char_u *
|
char_u *
|
||||||
peek_next_line(cctx_T *cctx)
|
peek_next_line_from_context(cctx_T *cctx)
|
||||||
{
|
{
|
||||||
int lnum = cctx->ctx_lnum;
|
int lnum = cctx->ctx_lnum;
|
||||||
|
|
||||||
@@ -2430,7 +2430,7 @@ may_peek_next_line(cctx_T *cctx, char_u *arg, char_u **nextp)
|
|||||||
*nextp = NULL;
|
*nextp = NULL;
|
||||||
if (*p == NUL || (VIM_ISWHITE(*arg) && comment_start(p)))
|
if (*p == NUL || (VIM_ISWHITE(*arg) && comment_start(p)))
|
||||||
{
|
{
|
||||||
*nextp = peek_next_line(cctx);
|
*nextp = peek_next_line_from_context(cctx);
|
||||||
if (*nextp != NULL)
|
if (*nextp != NULL)
|
||||||
return *nextp;
|
return *nextp;
|
||||||
}
|
}
|
||||||
@@ -2442,7 +2442,7 @@ may_peek_next_line(cctx_T *cctx, char_u *arg, char_u **nextp)
|
|||||||
* Skips over empty lines. Skips over comment lines if "skip_comment" is TRUE.
|
* Skips over empty lines. Skips over comment lines if "skip_comment" is TRUE.
|
||||||
* Returns NULL when at the end.
|
* Returns NULL when at the end.
|
||||||
*/
|
*/
|
||||||
static char_u *
|
char_u *
|
||||||
next_line_from_context(cctx_T *cctx, int skip_comment)
|
next_line_from_context(cctx_T *cctx, int skip_comment)
|
||||||
{
|
{
|
||||||
char_u *line;
|
char_u *line;
|
||||||
@@ -3079,9 +3079,14 @@ compile_lambda(char_u **arg, cctx_T *cctx)
|
|||||||
{
|
{
|
||||||
typval_T rettv;
|
typval_T rettv;
|
||||||
ufunc_T *ufunc;
|
ufunc_T *ufunc;
|
||||||
|
evalarg_T evalarg;
|
||||||
|
|
||||||
|
CLEAR_FIELD(evalarg);
|
||||||
|
evalarg.eval_flags = EVAL_EVALUATE;
|
||||||
|
evalarg.eval_cctx = cctx;
|
||||||
|
|
||||||
// Get the funcref in "rettv".
|
// Get the funcref in "rettv".
|
||||||
if (get_lambda_tv(arg, &rettv, &EVALARG_EVALUATE) != OK)
|
if (get_lambda_tv(arg, &rettv, &evalarg) != OK)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
|
||||||
ufunc = rettv.vval.v_partial->pt_func;
|
ufunc = rettv.vval.v_partial->pt_func;
|
||||||
@@ -3535,6 +3540,7 @@ compile_leader(cctx_T *cctx, char_u *start, char_u *end)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Compile whatever comes after "name" or "name()".
|
* Compile whatever comes after "name" or "name()".
|
||||||
|
* Advances "*arg" only when something was recognized.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
compile_subscript(
|
compile_subscript(
|
||||||
@@ -3550,7 +3556,7 @@ compile_subscript(
|
|||||||
|
|
||||||
if (*p == NUL || (VIM_ISWHITE(**arg) && comment_start(p)))
|
if (*p == NUL || (VIM_ISWHITE(**arg) && comment_start(p)))
|
||||||
{
|
{
|
||||||
char_u *next = peek_next_line(cctx);
|
char_u *next = peek_next_line_from_context(cctx);
|
||||||
|
|
||||||
// If a following line starts with "->{" or "->X" advance to that
|
// If a following line starts with "->{" or "->X" advance to that
|
||||||
// line, so that a line break before "->" is allowed.
|
// line, so that a line break before "->" is allowed.
|
||||||
@@ -3560,11 +3566,12 @@ compile_subscript(
|
|||||||
next = next_line_from_context(cctx, TRUE);
|
next = next_line_from_context(cctx, TRUE);
|
||||||
if (next == NULL)
|
if (next == NULL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
*arg = skipwhite(next);
|
*arg = next;
|
||||||
|
p = skipwhite(*arg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (**arg == '(')
|
if (*p == '(')
|
||||||
{
|
{
|
||||||
garray_T *stack = &cctx->ctx_type_stack;
|
garray_T *stack = &cctx->ctx_type_stack;
|
||||||
type_T *type;
|
type_T *type;
|
||||||
@@ -3576,13 +3583,13 @@ compile_subscript(
|
|||||||
// funcref(arg)
|
// funcref(arg)
|
||||||
type = ((type_T **)stack->ga_data)[stack->ga_len - 1];
|
type = ((type_T **)stack->ga_data)[stack->ga_len - 1];
|
||||||
|
|
||||||
*arg = skipwhite(*arg + 1);
|
*arg = skipwhite(p + 1);
|
||||||
if (compile_arguments(arg, cctx, &argcount) == FAIL)
|
if (compile_arguments(arg, cctx, &argcount) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
if (generate_PCALL(cctx, argcount, end_leader, type, TRUE) == FAIL)
|
if (generate_PCALL(cctx, argcount, end_leader, type, TRUE) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
else if (**arg == '-' && (*arg)[1] == '>')
|
else if (*p == '-' && p[1] == '>')
|
||||||
{
|
{
|
||||||
if (generate_ppconst(cctx, ppconst) == FAIL)
|
if (generate_ppconst(cctx, ppconst) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
@@ -3594,7 +3601,7 @@ compile_subscript(
|
|||||||
return FAIL;
|
return FAIL;
|
||||||
*start_leader = end_leader; // don't apply again later
|
*start_leader = end_leader; // don't apply again later
|
||||||
|
|
||||||
p = *arg + 2;
|
p += 2;
|
||||||
*arg = skipwhite(p);
|
*arg = skipwhite(p);
|
||||||
if (may_get_next_line(p, arg, cctx) == FAIL)
|
if (may_get_next_line(p, arg, cctx) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
@@ -3622,7 +3629,7 @@ compile_subscript(
|
|||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (**arg == '[')
|
else if (*p == '[')
|
||||||
{
|
{
|
||||||
garray_T *stack = &cctx->ctx_type_stack;
|
garray_T *stack = &cctx->ctx_type_stack;
|
||||||
type_T **typep;
|
type_T **typep;
|
||||||
@@ -3635,7 +3642,7 @@ compile_subscript(
|
|||||||
if (generate_ppconst(cctx, ppconst) == FAIL)
|
if (generate_ppconst(cctx, ppconst) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
|
||||||
p = *arg + 1;
|
++p;
|
||||||
*arg = skipwhite(p);
|
*arg = skipwhite(p);
|
||||||
if (may_get_next_line(p, arg, cctx) == FAIL)
|
if (may_get_next_line(p, arg, cctx) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
@@ -3671,12 +3678,12 @@ compile_subscript(
|
|||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (**arg == '.' && (*arg)[1] != '.')
|
else if (*p == '.' && p[1] != '.')
|
||||||
{
|
{
|
||||||
if (generate_ppconst(cctx, ppconst) == FAIL)
|
if (generate_ppconst(cctx, ppconst) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
|
||||||
++*arg;
|
*arg = p + 1;
|
||||||
if (may_get_next_line(*arg, arg, cctx) == FAIL)
|
if (may_get_next_line(*arg, arg, cctx) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
// dictionary member: dict.name
|
// dictionary member: dict.name
|
||||||
|
Reference in New Issue
Block a user