Merge remote-tracking branch 'vim/master'

This commit is contained in:
ichizok
2020-07-10 16:27:38 +09:00
27 changed files with 497 additions and 137 deletions
+3 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+1
View File
@@ -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);
+2 -2
View File
@@ -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);
+3
View File
@@ -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);
+10
View File
@@ -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
View File
@@ -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
View File
@@ -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().
+2
View File
@@ -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)
+55 -2
View File
@@ -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
+17
View File
@@ -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'
+18
View File
@@ -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
+24 -1
View File
@@ -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:')
+12
View File
@@ -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
+32 -3
View File
@@ -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
View File
@@ -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)
+30
View File
@@ -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,
/**/
+8
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
/*