mirror of
https://github.com/macvim-dev/macvim.git
synced 2026-06-11 15:37:29 +02:00
Merge remote-tracking branch 'vim/master'
This commit is contained in:
@@ -5035,8 +5035,9 @@ getchar([expr]) *getchar()*
|
||||
When the user clicks a mouse button, the mouse event will be
|
||||
returned. The position can then be found in |v:mouse_col|,
|
||||
|v:mouse_lnum|, |v:mouse_winid| and |v:mouse_win|.
|
||||
|getmousepos()| can also be used. This example positions the
|
||||
mouse as it would normally happen: >
|
||||
|getmousepos()| can also be used. Mouse move events will be
|
||||
ignored.
|
||||
This example positions the mouse as it would normally happen: >
|
||||
let c = getchar()
|
||||
if c == "\<LeftMouse>" && v:mouse_win > 0
|
||||
exe v:mouse_win . "wincmd w"
|
||||
|
||||
+111
-63
@@ -379,10 +379,17 @@ skip_expr(char_u **pp)
|
||||
* Skip over an expression at "*pp".
|
||||
* If in Vim9 script and line breaks are encountered, the lines are
|
||||
* concatenated. "evalarg->eval_tofree" will be set accordingly.
|
||||
* "arg" is advanced to just after the expression.
|
||||
* "start" is set to the start of the expression, "end" to just after the end.
|
||||
* Also when the expression is copied to allocated memory.
|
||||
* Return FAIL for an error, OK otherwise.
|
||||
*/
|
||||
int
|
||||
skip_expr_concatenate(char_u **start, char_u **end, evalarg_T *evalarg)
|
||||
skip_expr_concatenate(
|
||||
char_u **arg,
|
||||
char_u **start,
|
||||
char_u **end,
|
||||
evalarg_T *evalarg)
|
||||
{
|
||||
typval_T rettv;
|
||||
int res;
|
||||
@@ -390,48 +397,75 @@ skip_expr_concatenate(char_u **start, char_u **end, evalarg_T *evalarg)
|
||||
garray_T *gap = &evalarg->eval_ga;
|
||||
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);
|
||||
// leave room for "start"
|
||||
if (ga_grow(gap, 1) == OK)
|
||||
// leave room for "start"
|
||||
++gap->ga_len;
|
||||
}
|
||||
*start = *arg;
|
||||
|
||||
// Don't evaluate the expression.
|
||||
if (evalarg != NULL)
|
||||
evalarg->eval_flags &= ~EVAL_EVALUATE;
|
||||
*end = skipwhite(*end);
|
||||
res = eval1(end, &rettv, evalarg);
|
||||
*arg = skipwhite(*arg);
|
||||
res = eval1(arg, &rettv, evalarg);
|
||||
*end = *arg;
|
||||
if (evalarg != NULL)
|
||||
evalarg->eval_flags = save_flags;
|
||||
|
||||
if (vim9script && evalarg->eval_cookie != NULL
|
||||
&& evalarg->eval_ga.ga_len > 1)
|
||||
if (vim9script
|
||||
&& (evalarg->eval_cookie != NULL || evalarg->eval_cctx != NULL))
|
||||
{
|
||||
char_u *p;
|
||||
size_t endoff = STRLEN(*end);
|
||||
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;
|
||||
size_t endoff = STRLEN(*arg);
|
||||
|
||||
// Line breaks encountered, concatenate all the lines.
|
||||
*((char_u **)gap->ga_data) = *start;
|
||||
p = ga_concat_strings(gap, "");
|
||||
*((char_u **)gap->ga_data) = NULL;
|
||||
ga_clear_strings(gap);
|
||||
gap->ga_itemsize = 0;
|
||||
if (p == NULL)
|
||||
return FAIL;
|
||||
*start = p;
|
||||
vim_free(evalarg->eval_tofree);
|
||||
evalarg->eval_tofree = p;
|
||||
// Compute "end" relative to the end.
|
||||
*end = *start + STRLEN(*start) - endoff;
|
||||
// Line breaks encountered, concatenate all the lines.
|
||||
*((char_u **)gap->ga_data) = *start;
|
||||
p = ga_concat_strings(gap, "");
|
||||
|
||||
// free the lines only when using getsourceline()
|
||||
if (evalarg->eval_cookie != NULL)
|
||||
{
|
||||
// Do not free the first line, the caller can still use it.
|
||||
*((char_u **)gap->ga_data) = NULL;
|
||||
// Do not free the last line, "arg" points into it, free it
|
||||
// later.
|
||||
vim_free(evalarg->eval_tofree);
|
||||
evalarg->eval_tofree =
|
||||
((char_u **)gap->ga_data)[gap->ga_len - 1];
|
||||
((char_u **)gap->ga_data)[gap->ga_len - 1] = NULL;
|
||||
ga_clear_strings(gap);
|
||||
}
|
||||
else
|
||||
ga_clear(gap);
|
||||
gap->ga_itemsize = 0;
|
||||
if (p == NULL)
|
||||
return FAIL;
|
||||
*start = p;
|
||||
vim_free(evalarg->eval_tofree_lambda);
|
||||
evalarg->eval_tofree_lambda = p;
|
||||
// Compute "end" relative to the end.
|
||||
*end = *start + STRLEN(*start) - endoff;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
* a Float to a String.
|
||||
* Return pointer to allocated memory, or NULL for failure.
|
||||
@@ -1878,11 +1912,16 @@ eval_next_non_blank(char_u *arg, evalarg_T *evalarg, int *getnext)
|
||||
*getnext = FALSE;
|
||||
if (current_sctx.sc_version == SCRIPT_VERSION_VIM9
|
||||
&& evalarg != NULL
|
||||
&& evalarg->eval_cookie != NULL
|
||||
&& (evalarg->eval_cookie != NULL || evalarg->eval_cctx != NULL)
|
||||
&& (*arg == NUL || (VIM_ISWHITE(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)
|
||||
{
|
||||
@@ -1902,7 +1941,10 @@ eval_next_line(evalarg_T *evalarg)
|
||||
garray_T *gap = &evalarg->eval_ga;
|
||||
char_u *line;
|
||||
|
||||
line = evalarg->eval_getline(0, evalarg->eval_cookie, 0, TRUE);
|
||||
if (evalarg->eval_cookie != NULL)
|
||||
line = evalarg->eval_getline(0, evalarg->eval_cookie, 0, TRUE);
|
||||
else
|
||||
line = next_line_from_context(evalarg->eval_cctx, TRUE);
|
||||
++evalarg->eval_break_count;
|
||||
if (gap->ga_itemsize > 0 && ga_grow(gap, 1) == OK)
|
||||
{
|
||||
@@ -1910,7 +1952,7 @@ eval_next_line(evalarg_T *evalarg)
|
||||
((char_u **)gap->ga_data)[gap->ga_len] = line;
|
||||
++gap->ga_len;
|
||||
}
|
||||
else
|
||||
else if (evalarg->eval_cookie != NULL)
|
||||
{
|
||||
vim_free(evalarg->eval_tofree);
|
||||
evalarg->eval_tofree = line;
|
||||
@@ -1936,25 +1978,31 @@ skipwhite_and_linebreak(char_u *arg, evalarg_T *evalarg)
|
||||
}
|
||||
|
||||
/*
|
||||
* After using "evalarg" filled from "eap" free the memory.
|
||||
* After using "evalarg" filled from "eap": free the memory.
|
||||
*/
|
||||
void
|
||||
clear_evalarg(evalarg_T *evalarg, exarg_T *eap)
|
||||
{
|
||||
if (evalarg != NULL && evalarg->eval_tofree != NULL)
|
||||
if (evalarg != NULL)
|
||||
{
|
||||
if (eap != NULL)
|
||||
if (evalarg->eval_tofree != NULL)
|
||||
{
|
||||
// We may need to keep the original command line, e.g. for
|
||||
// ":let" it has the variable names. But we may also need the
|
||||
// new one, "nextcmd" points into it. Keep both.
|
||||
vim_free(eap->cmdline_tofree);
|
||||
eap->cmdline_tofree = *eap->cmdlinep;
|
||||
*eap->cmdlinep = evalarg->eval_tofree;
|
||||
if (eap != NULL)
|
||||
{
|
||||
// We may need to keep the original command line, e.g. for
|
||||
// ":let" it has the variable names. But we may also need the
|
||||
// new one, "nextcmd" points into it. Keep both.
|
||||
vim_free(eap->cmdline_tofree);
|
||||
eap->cmdline_tofree = *eap->cmdlinep;
|
||||
*eap->cmdlinep = evalarg->eval_tofree;
|
||||
}
|
||||
else
|
||||
vim_free(evalarg->eval_tofree);
|
||||
evalarg->eval_tofree = NULL;
|
||||
}
|
||||
else
|
||||
vim_free(evalarg->eval_tofree);
|
||||
evalarg->eval_tofree = NULL;
|
||||
|
||||
vim_free(evalarg->eval_tofree_lambda);
|
||||
evalarg->eval_tofree_lambda = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5034,35 +5082,27 @@ handle_subscript(
|
||||
int ret = OK;
|
||||
dict_T *selfdict = NULL;
|
||||
int check_white = TRUE;
|
||||
int getnext;
|
||||
char_u *p;
|
||||
|
||||
// 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)
|
||||
while (ret == OK)
|
||||
{
|
||||
int getnext;
|
||||
char_u *p = eval_next_non_blank(*arg, evalarg, &getnext);
|
||||
|
||||
if (getnext && *p == '.' && ASCII_ISALPHA(p[1]))
|
||||
// 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);
|
||||
check_white = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
// "." is ".name" lookup when we found a dict or when evaluating and
|
||||
// scriptversion is at least 2, where string concatenation is "..".
|
||||
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))))
|
||||
|| (**arg == '-' && (*arg)[1] == '>')))
|
||||
{
|
||||
if (**arg == '(')
|
||||
if ((**arg == '(' && (!evaluate || rettv->v_type == VAR_FUNC
|
||||
|| rettv->v_type == VAR_PARTIAL))
|
||||
&& (!check_white || !VIM_ISWHITE(*(*arg - 1))))
|
||||
{
|
||||
ret = call_func_rettv(arg, evalarg, rettv, evaluate,
|
||||
selfdict, NULL);
|
||||
@@ -5079,7 +5119,7 @@ handle_subscript(
|
||||
dict_unref(selfdict);
|
||||
selfdict = NULL;
|
||||
}
|
||||
else if (**arg == '-')
|
||||
else if (**arg == '-' && (*arg)[1] == '>')
|
||||
{
|
||||
if (ret == OK)
|
||||
{
|
||||
@@ -5091,7 +5131,13 @@ handle_subscript(
|
||||
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);
|
||||
if (rettv->v_type == VAR_DICT)
|
||||
@@ -5108,6 +5154,8 @@ handle_subscript(
|
||||
ret = FAIL;
|
||||
}
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
// Turn "dict.Func" into a partial for "Func" bound to "dict".
|
||||
|
||||
+7
-5
@@ -465,8 +465,8 @@ static funcentry_T global_functions[] =
|
||||
{"acos", 1, 1, FEARG_1, ret_float, FLOAT_FUNC(f_acos)},
|
||||
{"add", 2, 2, FEARG_1, ret_first_arg, f_add},
|
||||
{"and", 2, 2, FEARG_1, ret_number, f_and},
|
||||
{"append", 2, 2, FEARG_LAST, ret_number, f_append},
|
||||
{"appendbufline", 3, 3, FEARG_LAST, ret_number, f_appendbufline},
|
||||
{"append", 2, 2, FEARG_2, ret_number, f_append},
|
||||
{"appendbufline", 3, 3, FEARG_3, ret_number, f_appendbufline},
|
||||
{"argc", 0, 1, 0, ret_number, f_argc},
|
||||
{"argidx", 0, 0, 0, ret_number, f_argidx},
|
||||
{"arglistid", 0, 2, 0, ret_number, f_arglistid},
|
||||
@@ -1191,7 +1191,9 @@ internal_func_ret_type(int idx, int argcount, type_T **argtypes)
|
||||
|
||||
/*
|
||||
* Check the argument count to use for internal function "idx".
|
||||
* Returns OK or FAIL;
|
||||
* Returns -1 for failure, 0 if no method base accepted, 1 if method base is
|
||||
* first argument, 2 if method base is second argument, etc. 9 if method base
|
||||
* is last argument.
|
||||
*/
|
||||
int
|
||||
check_internal_func(int idx, int argcount)
|
||||
@@ -1204,14 +1206,14 @@ check_internal_func(int idx, int argcount)
|
||||
else if (argcount > global_functions[idx].f_max_argc)
|
||||
res = FCERR_TOOMANY;
|
||||
else
|
||||
return OK;
|
||||
return global_functions[idx].f_argtype;
|
||||
|
||||
name = internal_func_name(idx);
|
||||
if (res == FCERR_TOOMANY)
|
||||
semsg(_(e_toomanyarg), name);
|
||||
else
|
||||
semsg(_(e_toofewarg), name);
|
||||
return FAIL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
|
||||
+25
-9
@@ -2377,6 +2377,7 @@ eval_variable(
|
||||
{
|
||||
int ret = OK;
|
||||
typval_T *tv = NULL;
|
||||
int foundFunc = FALSE;
|
||||
dictitem_T *v;
|
||||
int cc;
|
||||
|
||||
@@ -2404,21 +2405,36 @@ eval_variable(
|
||||
// imported variable from another script
|
||||
if (import != NULL)
|
||||
{
|
||||
scriptitem_T *si = SCRIPT_ITEM(import->imp_sid);
|
||||
svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data)
|
||||
if (import->imp_funcname != NULL)
|
||||
{
|
||||
foundFunc = TRUE;
|
||||
if (rettv != NULL)
|
||||
{
|
||||
rettv->v_type = VAR_FUNC;
|
||||
rettv->vval.v_string = vim_strsave(import->imp_funcname);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
scriptitem_T *si = SCRIPT_ITEM(import->imp_sid);
|
||||
svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data)
|
||||
+ import->imp_var_vals_idx;
|
||||
tv = sv->sv_tv;
|
||||
tv = sv->sv_tv;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tv == NULL)
|
||||
if (!foundFunc)
|
||||
{
|
||||
if (rettv != NULL && verbose)
|
||||
semsg(_(e_undefvar), name);
|
||||
ret = FAIL;
|
||||
if (tv == NULL)
|
||||
{
|
||||
if (rettv != NULL && verbose)
|
||||
semsg(_(e_undefvar), name);
|
||||
ret = FAIL;
|
||||
}
|
||||
else if (rettv != NULL)
|
||||
copy_tv(tv, rettv);
|
||||
}
|
||||
else if (rettv != NULL)
|
||||
copy_tv(tv, rettv);
|
||||
|
||||
name[len] = cc;
|
||||
|
||||
|
||||
+3
-1
@@ -3235,13 +3235,15 @@ find_ex_command(
|
||||
// "varname[]" is an expression.
|
||||
// "g:varname" is an expression.
|
||||
// "varname->expr" is an expression.
|
||||
// "varname.expr" is an expression.
|
||||
// "(..." is an expression.
|
||||
// "{..." is an dict expression.
|
||||
if (*p == '('
|
||||
|| *p == '{'
|
||||
|| (*p == '[' && p > eap->cmd)
|
||||
|| p[1] == ':'
|
||||
|| (*p == '-' && p[1] == '>'))
|
||||
|| (*p == '-' && p[1] == '>')
|
||||
|| (*p == '.' && ASCII_ISALPHA(p[1])))
|
||||
{
|
||||
eap->cmdidx = CMD_eval;
|
||||
return eap->cmd;
|
||||
|
||||
+2
-2
@@ -1501,7 +1501,7 @@ openscript(
|
||||
{
|
||||
update_topline_cursor(); // update cursor position and topline
|
||||
normal_cmd(&oa, FALSE); // execute one command
|
||||
vpeekc(); // check for end of file
|
||||
(void)vpeekc(); // check for end of file
|
||||
}
|
||||
while (scriptin[oldcurscript] != NULL);
|
||||
|
||||
@@ -2045,7 +2045,7 @@ f_getchar(typval_T *argvars, typval_T *rettv)
|
||||
// getchar(0) and char avail: return char
|
||||
n = plain_vgetc();
|
||||
|
||||
if (n == K_IGNORE)
|
||||
if (n == K_IGNORE || n == K_MOUSEMOVE)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
||||
+1
-1
@@ -1890,7 +1890,7 @@ EXTERN listitem_T range_list_item;
|
||||
// Passed to an eval() function to enable evaluation.
|
||||
EXTERN evalarg_T EVALARG_EVALUATE
|
||||
# ifdef DO_INIT
|
||||
= {EVAL_EVALUATE, 0, NULL, NULL, {0, 0, 0, 0, NULL}, NULL}
|
||||
= {EVAL_EVALUATE, 0, NULL, NULL, NULL, {0, 0, 0, 0, NULL}, NULL, NULL}
|
||||
# endif
|
||||
;
|
||||
#endif
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ int eval_expr_typval(typval_T *expr, typval_T *argv, int argc, typval_T *rettv);
|
||||
int eval_expr_to_bool(typval_T *expr, int *error);
|
||||
char_u *eval_to_string_skip(char_u *arg, exarg_T *eap, int skip);
|
||||
int skip_expr(char_u **pp);
|
||||
int skip_expr_concatenate(char_u **start, char_u **end, evalarg_T *evalarg);
|
||||
int skip_expr_concatenate(char_u **arg, char_u **start, char_u **end, evalarg_T *evalarg);
|
||||
char_u *eval_to_string(char_u *arg, int convert);
|
||||
char_u *eval_to_string_safe(char_u *arg, int use_sandbox);
|
||||
varnumber_T eval_to_number(char_u *expr);
|
||||
|
||||
@@ -31,6 +31,7 @@ int screen_valid(int doclear);
|
||||
void screenalloc(int doclear);
|
||||
void free_screenlines(void);
|
||||
void screenclear(void);
|
||||
void line_was_clobbered(int screen_lnum);
|
||||
int can_clear(char_u *p);
|
||||
void screen_start(void);
|
||||
void windgoto(int row, int col);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/* scriptfile.c */
|
||||
void estack_init(void);
|
||||
estack_T *estack_push(etype_T type, char_u *name, long lnum);
|
||||
void estack_push_ufunc(ufunc_T *ufunc, long lnum);
|
||||
estack_T *estack_push_ufunc(ufunc_T *ufunc, long lnum);
|
||||
int estack_top_is_ufunc(ufunc_T *ufunc, long lnum);
|
||||
void estack_pop(void);
|
||||
estack_T *estack_pop(void);
|
||||
char_u *estack_sfile(void);
|
||||
void ex_runtime(exarg_T *eap);
|
||||
int do_in_path(char_u *path, char_u *name, int flags, void (*callback)(char_u *fname, void *ck), void *cookie);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/* vim9compile.c */
|
||||
int check_defined(char_u *p, size_t len, cctx_T *cctx);
|
||||
void clear_type_list(garray_T *gap);
|
||||
type_T *typval2type(typval_T *tv);
|
||||
int check_type(type_T *expected, type_T *actual, int give_msg);
|
||||
char_u *skip_type(char_u *start);
|
||||
@@ -8,6 +9,8 @@ char *vartype_name(vartype_T type);
|
||||
char *type_name(type_T *type, char **tofree);
|
||||
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);
|
||||
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);
|
||||
int assignment_len(char_u *p, int *heredoc);
|
||||
void vim9_declare_error(char_u *name);
|
||||
|
||||
@@ -3022,6 +3022,16 @@ lineinvalid(unsigned off, int width)
|
||||
(void)vim_memset(ScreenAttrs + off, -1, (size_t)width * sizeof(sattr_T));
|
||||
}
|
||||
|
||||
/*
|
||||
* To be called when characters were sent to the terminal directly, outputting
|
||||
* test on "screen_lnum".
|
||||
*/
|
||||
void
|
||||
line_was_clobbered(int screen_lnum)
|
||||
{
|
||||
lineinvalid(LineOffset[screen_lnum], (int)Columns);
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy part of a Screenline for vertically split window "wp".
|
||||
*/
|
||||
|
||||
+8
-5
@@ -68,7 +68,7 @@ estack_push(etype_T type, char_u *name, long lnum)
|
||||
/*
|
||||
* Add a user function to the execution stack.
|
||||
*/
|
||||
void
|
||||
estack_T *
|
||||
estack_push_ufunc(ufunc_T *ufunc, long lnum)
|
||||
{
|
||||
estack_T *entry = estack_push(ETYPE_UFUNC,
|
||||
@@ -76,6 +76,7 @@ estack_push_ufunc(ufunc_T *ufunc, long lnum)
|
||||
? ufunc->uf_name_exp : ufunc->uf_name, lnum);
|
||||
if (entry != NULL)
|
||||
entry->es_info.ufunc = ufunc;
|
||||
return entry;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -97,13 +98,15 @@ estack_top_is_ufunc(ufunc_T *ufunc, long lnum)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Take an item off of the execution stack.
|
||||
* Take an item off of the execution stack and return it.
|
||||
*/
|
||||
void
|
||||
estack_T *
|
||||
estack_pop(void)
|
||||
{
|
||||
if (exestack.ga_len > 1)
|
||||
--exestack.ga_len;
|
||||
if (exestack.ga_len == 0)
|
||||
return NULL;
|
||||
--exestack.ga_len;
|
||||
return ((estack_T *)exestack.ga_data) + exestack.ga_len;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+10
-1
@@ -1765,13 +1765,19 @@ typedef struct {
|
||||
char_u *(*eval_getline)(int, void *, int, int);
|
||||
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
|
||||
// concatenated later. Used when "eval_ga.ga_itemsize" is not zero.
|
||||
// "eval_ga.ga_data" is a list of pointers to lines.
|
||||
garray_T eval_ga;
|
||||
|
||||
// pointer to the line obtained with getsourceline()
|
||||
// pointer to the last line obtained with getsourceline()
|
||||
char_u *eval_tofree;
|
||||
|
||||
// pointer to the lines concatenated for a lambda.
|
||||
char_u *eval_tofree_lambda;
|
||||
} evalarg_T;
|
||||
|
||||
// Flags for expression evaluation.
|
||||
@@ -1905,6 +1911,9 @@ typedef struct {
|
||||
AutoPatCmd *aucmd; // autocommand info
|
||||
except_T *except; // exception info
|
||||
} es_info;
|
||||
#if defined(FEAT_EVAL)
|
||||
scid_T es_save_sid; // saved sc_sid when calling function
|
||||
#endif
|
||||
} estack_T;
|
||||
|
||||
// Information returned by get_tty_info().
|
||||
|
||||
@@ -3738,6 +3738,7 @@ check_terminal_behavior(void)
|
||||
screen_stop_highlight();
|
||||
term_windgoto(1, 0);
|
||||
out_str((char_u *)" ");
|
||||
line_was_clobbered(1);
|
||||
}
|
||||
|
||||
if (xcc_status.tr_progress == STATUS_GET)
|
||||
@@ -3767,6 +3768,7 @@ check_terminal_behavior(void)
|
||||
screen_stop_highlight();
|
||||
term_windgoto(2, 0);
|
||||
out_str((char_u *)" ");
|
||||
line_was_clobbered(2);
|
||||
}
|
||||
|
||||
if (did_send)
|
||||
|
||||
@@ -23,6 +23,13 @@ func Test_tcldo()
|
||||
call setline(1, ['one', 'two', 'three'])
|
||||
tcldo ::vim::command new
|
||||
call assert_equal(wincount + 1, winnr('$'))
|
||||
|
||||
" Try to run a command in a 'nomodifiable' buffer
|
||||
call setline(1, ['one', 'two', 'three'])
|
||||
set nomodifiable
|
||||
call assert_fails('tcldo set line "abc"', 'cannot save undo information')
|
||||
set modifiable
|
||||
|
||||
%bwipe!
|
||||
endfunc
|
||||
|
||||
@@ -91,6 +98,9 @@ func Test_vim_buffer()
|
||||
\ 'expected integer but got "x"')
|
||||
call assert_fails('tcl ::vim::buffer list x',
|
||||
\ 'wrong # args: should be "::vim::buffer list "')
|
||||
" Invalid buffer command
|
||||
call assert_fails('tcl $::vim::current(buffer) abcd',
|
||||
\ 'bad option "abcd":')
|
||||
|
||||
tcl rename eachbuf ""
|
||||
%bwipe!
|
||||
@@ -176,6 +186,8 @@ func Test_vim_window_list()
|
||||
call assert_fails('tcl ::vim::window x', 'unknown option')
|
||||
call assert_fails('tcl ::vim::window list x',
|
||||
\ 'wrong # args: should be "::vim::window option"')
|
||||
call assert_fails('tcl $::vim::current(window) abcd',
|
||||
\ 'bad option "abcd":')
|
||||
|
||||
%bwipe
|
||||
endfunc
|
||||
@@ -248,6 +260,19 @@ func Test_window_cursor()
|
||||
tcl $win cursor $here(row) $here(column)
|
||||
call assert_equal([0, 2, 3, 0], getpos('.'))
|
||||
|
||||
" Invalid values for the row and column
|
||||
tcl array set pos {1 2}
|
||||
call assert_fails('tcl $win cursor pos', "can't read \"pos(row)\":")
|
||||
tcl array set pos {row '' abc 2}
|
||||
call assert_fails('tcl $win cursor pos', "expected integer but got \"''\"")
|
||||
tcl array set pos {row 1 abc 2}
|
||||
call assert_fails('tcl $win cursor pos', "can't read \"pos(column)\":")
|
||||
tcl array set pos {row 1 column ''}
|
||||
call assert_fails('tcl $win cursor pos', "expected integer but got \"''\"")
|
||||
|
||||
call assert_fails("tcl $win cursor '' 2", "expected integer but got \"''\"")
|
||||
call assert_fails("tcl $win cursor 1 ''", "expected integer but got \"''\"")
|
||||
|
||||
call assert_fails('tcl $win cursor 1 1 1', 'wrong # args:')
|
||||
|
||||
tcl unset win here
|
||||
@@ -351,6 +376,7 @@ func Test_window_delcmd()
|
||||
call assert_equal('window deleted', TclEval('set msg'))
|
||||
|
||||
call assert_fails('tcl $::vim::current(window) delcmd', 'wrong # args')
|
||||
call assert_fails('tcl $::vim::current(window) delcmd x x', 'wrong # args')
|
||||
|
||||
tcl unset msg
|
||||
bwipe
|
||||
@@ -417,6 +443,14 @@ func Test_buffer_delete()
|
||||
|
||||
call assert_fails('tcl $::vim::current(buffer) delete', 'wrong # args:')
|
||||
call assert_fails('tcl $::vim::current(buffer) delete 1 2 3', 'wrong # args:')
|
||||
call assert_fails('tcl $::vim::current(buffer) delete 1 abc',
|
||||
\ 'expected integer but got "abc"')
|
||||
|
||||
" Try to delete lines from an 'nomodifiable' buffer
|
||||
set nomodifiable
|
||||
call assert_fails('tcl $::vim::current(buffer) delete 2 1',
|
||||
\ 'cannot save undo information')
|
||||
set modifiable
|
||||
|
||||
bwipe!
|
||||
endfunc
|
||||
@@ -458,6 +492,12 @@ func Test_buffer_append()
|
||||
call assert_fails('tcl $buf append', 'wrong # args:')
|
||||
call assert_fails('tcl $buf append 1 x x', 'wrong # args:')
|
||||
|
||||
" Try to append lines to a 'nomodifiable' buffer
|
||||
set nomodifiable
|
||||
call assert_fails('tcl $buf append 1 "first"',
|
||||
\ 'cannot save undo information')
|
||||
set modifiable
|
||||
|
||||
tcl unset buf
|
||||
bwipe!
|
||||
endfunc
|
||||
@@ -485,6 +525,18 @@ func Test_buffer_set()
|
||||
call assert_fails('tcl $::vim::current(buffer) set 6 "x"', 'line number out of range')
|
||||
|
||||
call assert_fails('tcl $::vim::current(buffer) set', 'wrong # args:')
|
||||
call assert_fails('tcl $::vim::current(buffer) set 1 2 {[list "a" "b"]}',
|
||||
\ 'list element in quotes followed by "]" instead of space')
|
||||
|
||||
" Try to modify a 'nomodifiable' buffer
|
||||
set nomodifiable
|
||||
call assert_fails('tcl $::vim::current(buffer) set 1 "x"',
|
||||
\ 'cannot save undo information')
|
||||
call assert_fails('tcl $::vim::current(buffer) set 1 {a b}',
|
||||
\ 'cannot save undo information')
|
||||
call assert_fails('tcl $::vim::current(buffer) set 1 2 {a b}',
|
||||
\ 'cannot save undo information')
|
||||
set modifiable
|
||||
bwipe!
|
||||
endfunc
|
||||
|
||||
@@ -514,6 +566,7 @@ func Test_buffer_get()
|
||||
call assert_fails('tcl $buf get 0 1', 'line number out of range')
|
||||
|
||||
call assert_fails('tcl $::vim::current(buffer) get x', 'expected integer but got "x"')
|
||||
call assert_fails('tcl $::vim::current(buffer) get 1 x', 'expected integer but got "x"')
|
||||
call assert_fails('tcl $::vim::current(buffer) get 1 1 1', 'wrong # args:')
|
||||
|
||||
tcl unset buf
|
||||
@@ -592,8 +645,8 @@ func Test_buffer_delcmd()
|
||||
q
|
||||
call assert_equal('buffer deleted', TclEval('set msg'))
|
||||
|
||||
call assert_fails('tcl $::vim::current(window) delcmd', 'wrong # args')
|
||||
call assert_fails('tcl $::vim::current(window) delcmd x x', 'wrong # args')
|
||||
call assert_fails('tcl $::vim::current(buffer) delcmd', 'wrong # args')
|
||||
call assert_fails('tcl $::vim::current(buffer) delcmd x x', 'wrong # args')
|
||||
|
||||
tcl unset msg
|
||||
%bwipe
|
||||
|
||||
@@ -208,6 +208,23 @@ def Test_method_call_linebreak()
|
||||
CheckScriptSuccess(lines)
|
||||
enddef
|
||||
|
||||
def Test_dict_member()
|
||||
let test: dict<list<number>> = {'data': [3, 1, 2]}
|
||||
test.data->sort()
|
||||
assert_equal(#{data: [1, 2, 3]}, test)
|
||||
test.data
|
||||
->reverse()
|
||||
assert_equal(#{data: [3, 2, 1]}, test)
|
||||
|
||||
let lines =<< trim END
|
||||
vim9script
|
||||
let test: dict<list<number>> = {'data': [3, 1, 2]}
|
||||
test.data->sort()
|
||||
assert_equal(#{data: [1, 2, 3]}, test)
|
||||
END
|
||||
CheckScriptSuccess(lines)
|
||||
enddef
|
||||
|
||||
def Test_bar_after_command()
|
||||
def RedrawAndEcho()
|
||||
let x = 'did redraw'
|
||||
|
||||
@@ -1278,4 +1278,22 @@ def Test_simplify_const_expr()
|
||||
res)
|
||||
enddef
|
||||
|
||||
def s:CallAppend()
|
||||
eval "some text"->append(2)
|
||||
enddef
|
||||
|
||||
def Test_shuffle()
|
||||
let res = execute('disass s:CallAppend')
|
||||
assert_match('<SNR>\d*_CallAppend\_s*' ..
|
||||
'eval "some text"->append(2)\_s*' ..
|
||||
'\d PUSHS "some text"\_s*' ..
|
||||
'\d PUSHNR 2\_s*' ..
|
||||
'\d SHUFFLE 2 up 1\_s*' ..
|
||||
'\d BCALL append(argc 2)\_s*' ..
|
||||
'\d DROP\_s*' ..
|
||||
'\d PUSHNR 0\_s*' ..
|
||||
'\d RETURN',
|
||||
res)
|
||||
enddef
|
||||
|
||||
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
|
||||
|
||||
@@ -1014,6 +1014,7 @@ def Test_expr7_list()
|
||||
|
||||
call CheckDefExecFailure(["let x = g:anint[3]"], 'E714:')
|
||||
call CheckDefFailure(["let x = g:list_mixed[xxx]"], 'E1001:')
|
||||
call CheckDefFailure(["let x = [1,2,3]"], 'E1069:')
|
||||
call CheckDefExecFailure(["let x = g:list_mixed['xx']"], 'E39:')
|
||||
call CheckDefFailure(["let x = g:list_mixed[0"], 'E111:')
|
||||
call CheckDefExecFailure(["let x = g:list_empty[3]"], 'E684:')
|
||||
@@ -1410,6 +1411,28 @@ def Test_expr7_subscript_linebreak()
|
||||
one)
|
||||
enddef
|
||||
|
||||
def Test_expr7_method_call()
|
||||
new
|
||||
setline(1, ['first', 'last'])
|
||||
eval 'second'->append(1)
|
||||
assert_equal(['first', 'second', 'last'], getline(1, '$'))
|
||||
bwipe!
|
||||
|
||||
let bufnr = bufnr()
|
||||
let loclist = [#{bufnr: bufnr, lnum: 42, col: 17, text: 'wrong'}]
|
||||
loclist->setloclist(0)
|
||||
assert_equal([#{bufnr: bufnr,
|
||||
lnum: 42,
|
||||
col: 17,
|
||||
text: 'wrong',
|
||||
pattern: '',
|
||||
valid: 1,
|
||||
vcol: 0,
|
||||
nr: 0,
|
||||
type: '',
|
||||
module: ''}
|
||||
], getloclist(0))
|
||||
enddef
|
||||
|
||||
func Test_expr7_trailing_fails()
|
||||
call CheckDefFailure(['let l = [2]', 'l->{l -> add(l, 8)}'], 'E107')
|
||||
@@ -1420,7 +1443,7 @@ func Test_expr_fails()
|
||||
call CheckDefFailure(["let x = '1'is2"], 'E488:')
|
||||
call CheckDefFailure(["let x = '1'isnot2"], 'E488:')
|
||||
|
||||
call CheckDefExecFailure(["CallMe ('yes')"], 'E492:')
|
||||
call CheckDefFailure(["CallMe ('yes')"], 'E476:')
|
||||
call CheckDefFailure(["CallMe2('yes','no')"], 'E1069:')
|
||||
call CheckDefFailure(["CallMe2('yes' , 'no')"], 'E1068:')
|
||||
|
||||
|
||||
@@ -965,6 +965,18 @@ def Test_line_continuation_in_def()
|
||||
assert_equal('full', Line_continuation_in_def('.'))
|
||||
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()
|
||||
CheckScreendump
|
||||
|
||||
|
||||
@@ -571,7 +571,7 @@ enddef
|
||||
|
||||
def Test_try_catch_fails()
|
||||
call CheckDefFailure(['catch'], 'E603:')
|
||||
call CheckDefFailure(['try', 'echo 0', 'catch','catch'], 'E1033:')
|
||||
call CheckDefFailure(['try', 'echo 0', 'catch', 'catch'], 'E1033:')
|
||||
call CheckDefFailure(['try', 'echo 0', 'catch /pat'], 'E1067:')
|
||||
call CheckDefFailure(['finally'], 'E606:')
|
||||
call CheckDefFailure(['try', 'echo 0', 'finally', 'echo 1', 'finally'], 'E607:')
|
||||
@@ -911,10 +911,10 @@ func Test_import_fails_without_script()
|
||||
CheckRunVimInTerminal
|
||||
|
||||
" call indirectly to avoid compilation error for missing functions
|
||||
call Run_Test_import_fails_without_script()
|
||||
call Run_Test_import_fails_on_command_line()
|
||||
endfunc
|
||||
|
||||
def Run_Test_import_fails_without_script()
|
||||
def Run_Test_import_fails_on_command_line()
|
||||
let export =<< trim END
|
||||
vim9script
|
||||
export def Foo(): number
|
||||
@@ -1013,6 +1013,35 @@ def Test_vim9script_funcref()
|
||||
delete('Xscript.vim')
|
||||
enddef
|
||||
|
||||
" Check that when searcing for "FilterFunc" it doesn't find the import in the
|
||||
" script where FastFilter() is called from.
|
||||
def Test_vim9script_funcref_other_script()
|
||||
let filterLines =<< trim END
|
||||
vim9script
|
||||
export def FilterFunc(idx: number, val: number): bool
|
||||
return idx % 2 == 1
|
||||
enddef
|
||||
export def FastFilter(): list<number>
|
||||
return range(10)->filter('FilterFunc')
|
||||
enddef
|
||||
END
|
||||
writefile(filterLines, 'Xfilter.vim')
|
||||
|
||||
let lines =<< trim END
|
||||
vim9script
|
||||
import {FilterFunc, FastFilter} from './Xfilter.vim'
|
||||
def Test()
|
||||
let x: list<number> = FastFilter()
|
||||
enddef
|
||||
Test()
|
||||
END
|
||||
writefile(lines, 'Ximport.vim')
|
||||
assert_fails('source Ximport.vim', 'E121:')
|
||||
|
||||
delete('Xfilter.vim')
|
||||
delete('Ximport.vim')
|
||||
enddef
|
||||
|
||||
def Test_vim9script_reload_delfunc()
|
||||
let first_lines =<< trim END
|
||||
vim9script
|
||||
|
||||
+18
-16
@@ -389,8 +389,8 @@ get_lambda_tv(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
|
||||
partial_T *pt = NULL;
|
||||
int varargs;
|
||||
int ret;
|
||||
char_u *start;
|
||||
char_u *s, *e;
|
||||
char_u *s;
|
||||
char_u *start, *end;
|
||||
int *old_eval_lavars = eval_lavars_used;
|
||||
int eval_lavars = FALSE;
|
||||
char_u *tofree = NULL;
|
||||
@@ -399,10 +399,10 @@ get_lambda_tv(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
|
||||
ga_init(&newlines);
|
||||
|
||||
// First, check if this is a lambda expression. "->" must exist.
|
||||
start = skipwhite(*arg + 1);
|
||||
ret = get_function_args(&start, '-', NULL, NULL, NULL, NULL, TRUE,
|
||||
s = skipwhite(*arg + 1);
|
||||
ret = get_function_args(&s, '-', NULL, NULL, NULL, NULL, TRUE,
|
||||
NULL, NULL);
|
||||
if (ret == FAIL || *start != '>')
|
||||
if (ret == FAIL || *s != '>')
|
||||
return NOTDONE;
|
||||
|
||||
// Parse the arguments again.
|
||||
@@ -423,8 +423,8 @@ get_lambda_tv(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
|
||||
|
||||
// Get the start and the end of the expression.
|
||||
*arg = skipwhite_and_linebreak(*arg + 1, evalarg);
|
||||
s = *arg;
|
||||
ret = skip_expr_concatenate(&s, arg, evalarg);
|
||||
start = *arg;
|
||||
ret = skip_expr_concatenate(arg, &start, &end, evalarg);
|
||||
if (ret == FAIL)
|
||||
goto errret;
|
||||
if (evalarg != NULL)
|
||||
@@ -434,7 +434,6 @@ get_lambda_tv(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
|
||||
evalarg->eval_tofree = NULL;
|
||||
}
|
||||
|
||||
e = *arg;
|
||||
*arg = skipwhite_and_linebreak(*arg, evalarg);
|
||||
if (**arg != '}')
|
||||
{
|
||||
@@ -463,13 +462,13 @@ get_lambda_tv(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
|
||||
goto errret;
|
||||
|
||||
// Add "return " before the expression.
|
||||
len = 7 + (int)(e - s) + 1;
|
||||
len = 7 + (int)(end - start) + 1;
|
||||
p = alloc(len);
|
||||
if (p == NULL)
|
||||
goto errret;
|
||||
((char_u **)(newlines.ga_data))[newlines.ga_len++] = p;
|
||||
STRCPY(p, "return ");
|
||||
vim_strncpy(p + 7, s, e - s);
|
||||
vim_strncpy(p + 7, start, end - start);
|
||||
if (strstr((char *)p + 7, "a:") == NULL)
|
||||
// No a: variables are used for sure.
|
||||
flags |= FC_NOARGS;
|
||||
@@ -509,7 +508,10 @@ get_lambda_tv(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
|
||||
}
|
||||
|
||||
eval_lavars_used = old_eval_lavars;
|
||||
vim_free(tofree);
|
||||
if (evalarg != NULL && evalarg->eval_tofree == NULL)
|
||||
evalarg->eval_tofree = tofree;
|
||||
else
|
||||
vim_free(tofree);
|
||||
return OK;
|
||||
|
||||
errret:
|
||||
@@ -517,7 +519,10 @@ errret:
|
||||
ga_clear_strings(&newlines);
|
||||
vim_free(fp);
|
||||
vim_free(pt);
|
||||
vim_free(tofree);
|
||||
if (evalarg != NULL && evalarg->eval_tofree == NULL)
|
||||
evalarg->eval_tofree = tofree;
|
||||
else
|
||||
vim_free(tofree);
|
||||
eval_lavars_used = old_eval_lavars;
|
||||
return FAIL;
|
||||
}
|
||||
@@ -1069,10 +1074,7 @@ func_clear_items(ufunc_T *fp)
|
||||
VIM_CLEAR(fp->uf_arg_types);
|
||||
VIM_CLEAR(fp->uf_def_arg_idx);
|
||||
VIM_CLEAR(fp->uf_va_name);
|
||||
while (fp->uf_type_list.ga_len > 0)
|
||||
vim_free(((type_T **)fp->uf_type_list.ga_data)
|
||||
[--fp->uf_type_list.ga_len]);
|
||||
ga_clear(&fp->uf_type_list);
|
||||
clear_type_list(&fp->uf_type_list);
|
||||
|
||||
#ifdef FEAT_LUA
|
||||
if (fp->uf_cb_free != NULL)
|
||||
|
||||
@@ -769,6 +769,36 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
1168,
|
||||
/**/
|
||||
1167,
|
||||
/**/
|
||||
1166,
|
||||
/**/
|
||||
1165,
|
||||
/**/
|
||||
1164,
|
||||
/**/
|
||||
1163,
|
||||
/**/
|
||||
1162,
|
||||
/**/
|
||||
1161,
|
||||
/**/
|
||||
1160,
|
||||
/**/
|
||||
1159,
|
||||
/**/
|
||||
1158,
|
||||
/**/
|
||||
1157,
|
||||
/**/
|
||||
1156,
|
||||
/**/
|
||||
1155,
|
||||
/**/
|
||||
1154,
|
||||
/**/
|
||||
1153,
|
||||
/**/
|
||||
|
||||
@@ -124,6 +124,7 @@ typedef enum {
|
||||
ISN_CHECKTYPE, // check value type is isn_arg.type.tc_type
|
||||
ISN_CHECKLEN, // check list length is isn_arg.checklen.cl_min_len
|
||||
|
||||
ISN_SHUFFLE, // move item on stack up or down
|
||||
ISN_DROP // pop stack and discard value
|
||||
} isntype_T;
|
||||
|
||||
@@ -237,6 +238,12 @@ typedef struct {
|
||||
int cl_more_OK; // longer is allowed
|
||||
} checklen_T;
|
||||
|
||||
// arguments to ISN_SHUFFLE
|
||||
typedef struct {
|
||||
int shfl_item; // item to move (relative to top of stack)
|
||||
int shfl_up; // places to move upwards
|
||||
} shuffle_T;
|
||||
|
||||
/*
|
||||
* Instruction
|
||||
*/
|
||||
@@ -270,6 +277,7 @@ struct isn_S {
|
||||
unlet_T unlet;
|
||||
funcref_T funcref;
|
||||
checklen_T checklen;
|
||||
shuffle_T shuffle;
|
||||
} isn_arg;
|
||||
};
|
||||
|
||||
|
||||
+58
-18
@@ -321,6 +321,14 @@ alloc_type(garray_T *type_gap)
|
||||
return type;
|
||||
}
|
||||
|
||||
void
|
||||
clear_type_list(garray_T *gap)
|
||||
{
|
||||
while (gap->ga_len > 0)
|
||||
vim_free(((type_T **)gap->ga_data)[--gap->ga_len]);
|
||||
ga_clear(gap);
|
||||
}
|
||||
|
||||
static type_T *
|
||||
get_list_type(type_T *member_type, garray_T *type_gap)
|
||||
{
|
||||
@@ -1437,20 +1445,31 @@ generate_FOR(cctx_T *cctx, int loop_idx)
|
||||
|
||||
/*
|
||||
* Generate an ISN_BCALL instruction.
|
||||
* "method_call" is TRUE for "value->method()"
|
||||
* Return FAIL if the number of arguments is wrong.
|
||||
*/
|
||||
static int
|
||||
generate_BCALL(cctx_T *cctx, int func_idx, int argcount)
|
||||
generate_BCALL(cctx_T *cctx, int func_idx, int argcount, int method_call)
|
||||
{
|
||||
isn_T *isn;
|
||||
garray_T *stack = &cctx->ctx_type_stack;
|
||||
int argoff;
|
||||
type_T *argtypes[MAX_FUNC_ARGS];
|
||||
int i;
|
||||
|
||||
RETURN_OK_IF_SKIP(cctx);
|
||||
if (check_internal_func(func_idx, argcount) == FAIL)
|
||||
argoff = check_internal_func(func_idx, argcount);
|
||||
if (argoff < 0)
|
||||
return FAIL;
|
||||
|
||||
if (method_call && argoff > 1)
|
||||
{
|
||||
if ((isn = generate_instr(cctx, ISN_SHUFFLE)) == NULL)
|
||||
return FAIL;
|
||||
isn->isn_arg.shuffle.shfl_item = argcount;
|
||||
isn->isn_arg.shuffle.shfl_up = argoff - 1;
|
||||
}
|
||||
|
||||
if ((isn = generate_instr(cctx, ISN_BCALL)) == NULL)
|
||||
return FAIL;
|
||||
isn->isn_arg.bfunc.cbf_idx = func_idx;
|
||||
@@ -2397,8 +2416,8 @@ comment_start(char_u *p)
|
||||
* comment. Skips over white space.
|
||||
* Returns NULL if there is none.
|
||||
*/
|
||||
static char_u *
|
||||
peek_next_line(cctx_T *cctx)
|
||||
char_u *
|
||||
peek_next_line_from_context(cctx_T *cctx)
|
||||
{
|
||||
int lnum = cctx->ctx_lnum;
|
||||
|
||||
@@ -2430,7 +2449,7 @@ may_peek_next_line(cctx_T *cctx, char_u *arg, char_u **nextp)
|
||||
*nextp = NULL;
|
||||
if (*p == NUL || (VIM_ISWHITE(*arg) && comment_start(p)))
|
||||
{
|
||||
*nextp = peek_next_line(cctx);
|
||||
*nextp = peek_next_line_from_context(cctx);
|
||||
if (*nextp != NULL)
|
||||
return *nextp;
|
||||
}
|
||||
@@ -2442,7 +2461,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.
|
||||
* Returns NULL when at the end.
|
||||
*/
|
||||
static char_u *
|
||||
char_u *
|
||||
next_line_from_context(cctx_T *cctx, int skip_comment)
|
||||
{
|
||||
char_u *line;
|
||||
@@ -2922,7 +2941,7 @@ compile_call(
|
||||
// builtin function
|
||||
idx = find_internal_func(name);
|
||||
if (idx >= 0)
|
||||
res = generate_BCALL(cctx, idx, argcount);
|
||||
res = generate_BCALL(cctx, idx, argcount, argcount_init == 1);
|
||||
else
|
||||
semsg(_(e_unknownfunc), namebuf);
|
||||
goto theend;
|
||||
@@ -3060,7 +3079,14 @@ compile_list(char_u **arg, cctx_T *cctx)
|
||||
break;
|
||||
++count;
|
||||
if (*p == ',')
|
||||
{
|
||||
++p;
|
||||
if (*p != ']' && !IS_WHITE_OR_NUL(*p))
|
||||
{
|
||||
semsg(_(e_white_after), ",");
|
||||
return FAIL;
|
||||
}
|
||||
}
|
||||
whitep = p;
|
||||
p = skipwhite(p);
|
||||
}
|
||||
@@ -3079,9 +3105,14 @@ compile_lambda(char_u **arg, cctx_T *cctx)
|
||||
{
|
||||
typval_T rettv;
|
||||
ufunc_T *ufunc;
|
||||
evalarg_T evalarg;
|
||||
|
||||
CLEAR_FIELD(evalarg);
|
||||
evalarg.eval_flags = EVAL_EVALUATE;
|
||||
evalarg.eval_cctx = cctx;
|
||||
|
||||
// Get the funcref in "rettv".
|
||||
if (get_lambda_tv(arg, &rettv, &EVALARG_EVALUATE) != OK)
|
||||
if (get_lambda_tv(arg, &rettv, &evalarg) != OK)
|
||||
return FAIL;
|
||||
|
||||
ufunc = rettv.vval.v_partial->pt_func;
|
||||
@@ -3093,6 +3124,8 @@ compile_lambda(char_u **arg, cctx_T *cctx)
|
||||
// Compile it into instructions.
|
||||
compile_def_function(ufunc, TRUE, cctx);
|
||||
|
||||
clear_evalarg(&evalarg, NULL);
|
||||
|
||||
if (ufunc->uf_def_status == UF_COMPILED)
|
||||
return generate_FUNCREF(cctx, ufunc->uf_dfunc_idx);
|
||||
return FAIL;
|
||||
@@ -3535,6 +3568,7 @@ compile_leader(cctx_T *cctx, char_u *start, char_u *end)
|
||||
|
||||
/*
|
||||
* Compile whatever comes after "name" or "name()".
|
||||
* Advances "*arg" only when something was recognized.
|
||||
*/
|
||||
static int
|
||||
compile_subscript(
|
||||
@@ -3550,7 +3584,7 @@ compile_subscript(
|
||||
|
||||
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
|
||||
// line, so that a line break before "->" is allowed.
|
||||
@@ -3560,11 +3594,12 @@ compile_subscript(
|
||||
next = next_line_from_context(cctx, TRUE);
|
||||
if (next == NULL)
|
||||
return FAIL;
|
||||
*arg = skipwhite(next);
|
||||
*arg = next;
|
||||
p = skipwhite(*arg);
|
||||
}
|
||||
}
|
||||
|
||||
if (**arg == '(')
|
||||
if (*p == '(')
|
||||
{
|
||||
garray_T *stack = &cctx->ctx_type_stack;
|
||||
type_T *type;
|
||||
@@ -3576,13 +3611,13 @@ compile_subscript(
|
||||
// funcref(arg)
|
||||
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)
|
||||
return FAIL;
|
||||
if (generate_PCALL(cctx, argcount, end_leader, type, TRUE) == FAIL)
|
||||
return FAIL;
|
||||
}
|
||||
else if (**arg == '-' && (*arg)[1] == '>')
|
||||
else if (*p == '-' && p[1] == '>')
|
||||
{
|
||||
if (generate_ppconst(cctx, ppconst) == FAIL)
|
||||
return FAIL;
|
||||
@@ -3594,7 +3629,7 @@ compile_subscript(
|
||||
return FAIL;
|
||||
*start_leader = end_leader; // don't apply again later
|
||||
|
||||
p = *arg + 2;
|
||||
p += 2;
|
||||
*arg = skipwhite(p);
|
||||
if (may_get_next_line(p, arg, cctx) == FAIL)
|
||||
return FAIL;
|
||||
@@ -3622,7 +3657,7 @@ compile_subscript(
|
||||
return FAIL;
|
||||
}
|
||||
}
|
||||
else if (**arg == '[')
|
||||
else if (*p == '[')
|
||||
{
|
||||
garray_T *stack = &cctx->ctx_type_stack;
|
||||
type_T **typep;
|
||||
@@ -3635,7 +3670,7 @@ compile_subscript(
|
||||
if (generate_ppconst(cctx, ppconst) == FAIL)
|
||||
return FAIL;
|
||||
|
||||
p = *arg + 1;
|
||||
++p;
|
||||
*arg = skipwhite(p);
|
||||
if (may_get_next_line(p, arg, cctx) == FAIL)
|
||||
return FAIL;
|
||||
@@ -3671,12 +3706,12 @@ compile_subscript(
|
||||
return FAIL;
|
||||
}
|
||||
}
|
||||
else if (**arg == '.' && (*arg)[1] != '.')
|
||||
else if (*p == '.' && p[1] != '.')
|
||||
{
|
||||
if (generate_ppconst(cctx, ppconst) == FAIL)
|
||||
return FAIL;
|
||||
|
||||
++*arg;
|
||||
*arg = p + 1;
|
||||
if (may_get_next_line(*arg, arg, cctx) == FAIL)
|
||||
return FAIL;
|
||||
// dictionary member: dict.name
|
||||
@@ -7135,6 +7170,10 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
|
||||
|
||||
// TODO: other commands with an expression argument
|
||||
|
||||
case CMD_SIZE:
|
||||
semsg(_("E476: Invalid command: %s"), ea.cmd);
|
||||
goto erret;
|
||||
|
||||
default:
|
||||
// Not recognized, execute with do_cmdline_cmd().
|
||||
ea.arg = p;
|
||||
@@ -7369,6 +7408,7 @@ delete_instr(isn_T *isn)
|
||||
case ISN_COMPARESTRING:
|
||||
case ISN_CONCAT:
|
||||
case ISN_DCALL:
|
||||
case ISN_SHUFFLE:
|
||||
case ISN_DROP:
|
||||
case ISN_ECHO:
|
||||
case ISN_ECHOERR:
|
||||
|
||||
+35
-4
@@ -160,6 +160,7 @@ call_dfunc(int cdf_idx, int argcount_arg, ectx_T *ectx)
|
||||
int arg_to_add;
|
||||
int vararg_count = 0;
|
||||
int idx;
|
||||
estack_T *entry;
|
||||
|
||||
if (dfunc->df_deleted)
|
||||
{
|
||||
@@ -230,7 +231,14 @@ call_dfunc(int cdf_idx, int argcount_arg, ectx_T *ectx)
|
||||
// Set execution state to the start of the called function.
|
||||
ectx->ec_dfunc_idx = cdf_idx;
|
||||
ectx->ec_instr = dfunc->df_instr;
|
||||
estack_push_ufunc(dfunc->df_ufunc, 1);
|
||||
entry = estack_push_ufunc(dfunc->df_ufunc, 1);
|
||||
if (entry != NULL)
|
||||
{
|
||||
// Set the script context to the script where the function was defined.
|
||||
// TODO: save more than the SID?
|
||||
entry->es_save_sid = current_sctx.sc_sid;
|
||||
current_sctx.sc_sid = ufunc->uf_script_ctx.sc_sid;
|
||||
}
|
||||
|
||||
// Decide where to start execution, handles optional arguments.
|
||||
init_instr_idx(ufunc, argcount, ectx);
|
||||
@@ -386,9 +394,12 @@ func_return(ectx_T *ectx)
|
||||
+ ectx->ec_dfunc_idx;
|
||||
int argcount = ufunc_argcount(dfunc->df_ufunc);
|
||||
int top = ectx->ec_frame_idx - argcount;
|
||||
estack_T *entry;
|
||||
|
||||
// execution context goes one level up
|
||||
estack_pop();
|
||||
entry = estack_pop();
|
||||
if (entry != NULL)
|
||||
current_sctx.sc_sid = entry->es_save_sid;
|
||||
|
||||
if (handle_closure_in_use(ectx, TRUE) == FAIL)
|
||||
return FAIL;
|
||||
@@ -543,7 +554,7 @@ call_by_name(char_u *name, int argcount, ectx_T *ectx, isn_T *iptr)
|
||||
|
||||
if (func_idx < 0)
|
||||
return FAIL;
|
||||
if (check_internal_func(func_idx, argcount) == FAIL)
|
||||
if (check_internal_func(func_idx, argcount) < 0)
|
||||
return FAIL;
|
||||
return call_bfunc(func_idx, argcount, ectx);
|
||||
}
|
||||
@@ -2322,6 +2333,22 @@ call_def_function(
|
||||
}
|
||||
break;
|
||||
|
||||
case ISN_SHUFFLE:
|
||||
{
|
||||
typval_T tmp_tv;
|
||||
int item = iptr->isn_arg.shuffle.shfl_item;
|
||||
int up = iptr->isn_arg.shuffle.shfl_up;
|
||||
|
||||
tmp_tv = *STACK_TV_BOT(-item);
|
||||
for ( ; up > 0 && item > 1; --up)
|
||||
{
|
||||
*STACK_TV_BOT(-item) = *STACK_TV_BOT(-item + 1);
|
||||
--item;
|
||||
}
|
||||
*STACK_TV_BOT(-item) = tmp_tv;
|
||||
}
|
||||
break;
|
||||
|
||||
case ISN_DROP:
|
||||
--ectx.ec_stack.ga_len;
|
||||
clear_tv(STACK_TV_BOT(0));
|
||||
@@ -2889,8 +2916,12 @@ ex_disassemble(exarg_T *eap)
|
||||
break;
|
||||
case ISN_2STRING: smsg("%4d 2STRING stack[%lld]", current,
|
||||
(long long)(iptr->isn_arg.number));
|
||||
break;
|
||||
break;
|
||||
|
||||
case ISN_SHUFFLE: smsg("%4d SHUFFLE %d up %d", current,
|
||||
iptr->isn_arg.shuffle.shfl_item,
|
||||
iptr->isn_arg.shuffle.shfl_up);
|
||||
break;
|
||||
case ISN_DROP: smsg("%4d DROP", current); break;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -126,7 +126,7 @@ free_imports(int sid)
|
||||
}
|
||||
ga_clear(&si->sn_imports);
|
||||
ga_clear(&si->sn_var_vals);
|
||||
ga_clear(&si->sn_type_list);
|
||||
clear_type_list(&si->sn_type_list);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user