Merge remote-tracking branch 'vim/master'

This commit is contained in:
ichizok
2021-04-15 10:20:57 +09:00
37 changed files with 1232 additions and 381 deletions
+37 -11
View File
@@ -137,18 +137,21 @@ arguments).
Vim9 functions ~
A function defined with `:def` is compiled. Execution is many times faster,
often 10x to 100x times.
often 10 to 100 times.
Many errors are already found when compiling, before the function is executed.
The syntax is strict, to enforce code that is easy to read and understand.
Compilation is done when either of these is encountered:
Compilation is done when any of these is encountered:
- the first time the function is called
- when the `:defcompile` command is encountered in the script where the
- when the `:defcompile` command is encountered in the script after the
function was defined
- `:disassemble` is used for the function.
- a function that is compiled calls the function or uses it as a function
reference
*E1091*
If compilation fails it is not tried again on the next call, instead this
error is given: "E1091: Function is not compiled: {name}".
`:def` has no options like `:function` does: "range", "abort", "dict" or
"closure". A `:def` function always aborts on an error (unless `:silent!` was
@@ -161,7 +164,7 @@ functions.
Arguments are accessed by name, without "a:", just like any other language.
There is no "a:" dictionary or "a:000" list.
*vim9-variable-arguments*
Variable arguments are defined as the last argument, with a name and have a
list type, similar to TypeScript. For example, a list of numbers: >
def MyFunc(...itemlist: list<number>)
@@ -176,6 +179,15 @@ should use its default value. Example: >
...
enddef
MyFunc(v:none, 'LAST') # first argument uses default value 'one'
<
*vim9-ignored-argument*
The argument "_" (an underscore) can be used to ignore the argument. This is
most useful in callbacks where you don't need it, but do need to give an
argument to match the call. E.g. when using map() two arguments are passed,
the key and the value, to ignore the key: >
map(myList, (_, v) => v * 2)
There is no error for using the "_" argument multiple times. No type needs to
be given.
Functions and variables are script-local by default ~
@@ -323,6 +335,18 @@ The "g:" prefix is not needed for auto-load functions.
Since `&opt = value` is now assigning a value to option "opt", ":&" cannot be
used to repeat a `:substitute` command.
For an unpack assignment the underscore can be used to ignore a list item,
similar to how a function argument can be ignored: >
[a, _, c] = theList
[a, b; _] = longList
< *E1092*
Declaring more than one variable at a time, using the unpack notation, is
currently not supported: >
var [v1, v2] = GetValues() # Error!
That is because the type needs to be inferred from the list item type, which
isn't that easy.
Constants ~
*vim9-const* *vim9-final*
@@ -356,13 +380,6 @@ The constant only applies to the value itself, not what it refers to. >
NAMES[1] = ["Emma"] # Error!
NAMES[1][0] = "Emma" # OK, now females[0] == "Emma"
< *E1092*
Declaring more than one variable at a time, using the unpack notation, is
currently not supported: >
var [v1, v2] = GetValues() # Error!
That is because the type needs to be inferred from the list item type, which
isn't that easy.
Omitting :call and :eval ~
@@ -433,6 +450,15 @@ But you can use a backslash to concatenate the lines before parsing: >
filter(list, (k,
\ v)
\ => v > 0)
< *vim9-lambda-arguments*
In legacy script a lambda could be called with any number of extra arguments,
there was no way to warn for not using them. In Vim9 script the number of
arguments must match. If you do want to accept any arguments, or any further
arguments, use "..._", which makes the function accept
|vim9-variable-arguments|. Example: >
var Callback = (..._) => 'anything'
echo Callback(1, 2, 3) # displays "anything"
< *inline-function*
Additionally, a lambda can contain statements in {}: >
var Lambda = (arg) => {
+129
View File
@@ -259,6 +259,135 @@ failed:
return NULL;
}
int
blob_slice_or_index(
blob_T *blob,
int is_range,
varnumber_T n1,
varnumber_T n2,
int exclusive,
typval_T *rettv)
{
long len = blob_len(blob);
if (is_range)
{
// The resulting variable is a sub-blob. If the indexes
// are out of range the result is empty.
if (n1 < 0)
{
n1 = len + n1;
if (n1 < 0)
n1 = 0;
}
if (n2 < 0)
n2 = len + n2;
else if (n2 >= len)
n2 = len - (exclusive ? 0 : 1);
if (exclusive)
--n2;
if (n1 >= len || n2 < 0 || n1 > n2)
{
clear_tv(rettv);
rettv->v_type = VAR_BLOB;
rettv->vval.v_blob = NULL;
}
else
{
blob_T *new_blob = blob_alloc();
long i;
if (new_blob != NULL)
{
if (ga_grow(&new_blob->bv_ga, n2 - n1 + 1) == FAIL)
{
blob_free(new_blob);
return FAIL;
}
new_blob->bv_ga.ga_len = n2 - n1 + 1;
for (i = n1; i <= n2; i++)
blob_set(new_blob, i - n1, blob_get(blob, i));
clear_tv(rettv);
rettv_blob_set(rettv, new_blob);
}
}
}
else
{
// The resulting variable is a byte value.
// If the index is too big or negative that is an error.
if (n1 < 0)
n1 = len + n1;
if (n1 < len && n1 >= 0)
{
int v = blob_get(blob, n1);
clear_tv(rettv);
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = v;
}
else
{
semsg(_(e_blobidx), n1);
return FAIL;
}
}
return OK;
}
/*
* Check if "n1"- is a valid index for a blobl with length "bloblen".
*/
int
check_blob_index(long bloblen, varnumber_T n1, int quiet)
{
if (n1 < 0 || n1 > bloblen)
{
if (!quiet)
semsg(_(e_blobidx), n1);
return FAIL;
}
return OK;
}
/*
* Check if "n1"-"n2" is a valid range for a blob with length "bloblen".
*/
int
check_blob_range(long bloblen, varnumber_T n1, varnumber_T n2, int quiet)
{
if (n2 < 0 || n2 >= bloblen || n2 < n1)
{
if (!quiet)
semsg(_(e_blobidx), n2);
return FAIL;
}
return OK;
}
/*
* Set bytes "n1" to "n2" (inclusive) in "dest" to the value of "src".
* Caller must make sure "src" is a blob.
* Returns FAIL if the number of bytes does not match.
*/
int
blob_set_range(blob_T *dest, long n1, long n2, typval_T *src)
{
int il, ir;
if (n2 - n1 + 1 != blob_len(src->vval.v_blob))
{
emsg(_("E972: Blob value does not have the right number of bytes"));
return FAIL;
}
ir = 0;
for (il = n1; il <= n2; il++)
blob_set(dest, il, blob_get(src->vval.v_blob, ir++));
return OK;
}
/*
* "remove({blob})" function
*/
+3 -2
View File
@@ -297,7 +297,9 @@ update_screen(int type_arg)
// Remove the cursor before starting to do anything, because
// scrolling may make it difficult to redraw the text under
// it.
if (gui.in_use && wp == curwin)
// Also remove the cursor if it needs to be hidden due to an
// ongoing cursor-less sleep.
if (gui.in_use && (wp == curwin || cursor_is_sleeping()))
{
gui_cursor_col = gui.cursor_col;
gui_cursor_row = gui.cursor_row;
@@ -306,7 +308,6 @@ update_screen(int type_arg)
}
}
#endif
win_update(wp);
}
+8
View File
@@ -395,3 +395,11 @@ EXTERN char e_cannot_lock_unlock_local_variable[]
INIT(= N_("E1178: Cannot lock or unlock a local variable"));
EXTERN char e_failed_to_extract_pwd_from_str_check_your_shell_config[]
INIT(= N_("E1179: Failed to extract PWD from %s, check your shell's config related to OSC 7"));
EXTERN char e_variable_arguments_type_must_be_list_str[]
INIT(= N_("E1180: Variable arguments type must be a list: %s"));
EXTERN char e_cannot_use_underscore_here[]
INIT(= N_("E1181: Cannot use an underscore here"));
EXTERN char e_blob_required[]
INIT(= N_("E1182: Blob required"));
EXTERN char e_cannot_use_range_with_assignment_operator_str[]
INIT(= N_("E1183: Cannot use a range with an assignment operator: %s"));
+22 -92
View File
@@ -1175,12 +1175,8 @@ get_lval(
lp->ll_n1 = (long)tv_get_number(&var1);
clear_tv(&var1);
if (lp->ll_n1 < 0
|| lp->ll_n1 > bloblen
|| (lp->ll_range && lp->ll_n1 == bloblen))
if (check_blob_index(bloblen, lp->ll_n1, quiet) == FAIL)
{
if (!quiet)
semsg(_(e_blobidx), lp->ll_n1);
clear_tv(&var2);
return NULL;
}
@@ -1188,14 +1184,9 @@ get_lval(
{
lp->ll_n2 = (long)tv_get_number(&var2);
clear_tv(&var2);
if (lp->ll_n2 < 0
|| lp->ll_n2 >= bloblen
|| lp->ll_n2 < lp->ll_n1)
{
if (!quiet)
semsg(_(e_blobidx), lp->ll_n2);
if (check_blob_range(bloblen, lp->ll_n1, lp->ll_n2, quiet)
== FAIL)
return NULL;
}
}
lp->ll_blob = lp->ll_tv->vval.v_blob;
lp->ll_tv = NULL;
@@ -1319,23 +1310,12 @@ set_var_lval(
if (lp->ll_range && rettv->v_type == VAR_BLOB)
{
int il, ir;
if (lp->ll_empty2)
lp->ll_n2 = blob_len(lp->ll_blob) - 1;
if (lp->ll_n2 - lp->ll_n1 + 1 != blob_len(rettv->vval.v_blob))
{
emsg(_("E972: Blob value does not have the right number of bytes"));
if (blob_set_range(lp->ll_blob, lp->ll_n1, lp->ll_n2,
rettv) == FAIL)
return;
}
if (lp->ll_empty2)
lp->ll_n2 = blob_len(lp->ll_blob);
ir = 0;
for (il = lp->ll_n1; il <= lp->ll_n2; il++)
blob_set(lp->ll_blob, il,
blob_get(rettv->vval.v_blob, ir++));
}
else
{
@@ -1362,7 +1342,8 @@ set_var_lval(
{
typval_T tv;
if (flags & (ASSIGN_CONST | ASSIGN_FINAL))
if ((flags & (ASSIGN_CONST | ASSIGN_FINAL))
&& (flags & ASSIGN_FOR_LOOP) == 0)
{
emsg(_(e_cannot_mod));
*endp = cc;
@@ -1401,7 +1382,8 @@ set_var_lval(
listitem_T *ll_li = lp->ll_li;
int ll_n1 = lp->ll_n1;
if (flags & (ASSIGN_CONST | ASSIGN_FINAL))
if ((flags & (ASSIGN_CONST | ASSIGN_FINAL))
&& (flags & ASSIGN_FOR_LOOP) == 0)
{
emsg(_("E996: Cannot lock a range"));
return;
@@ -1460,7 +1442,8 @@ set_var_lval(
/*
* Assign to a List or Dictionary item.
*/
if (flags & (ASSIGN_CONST | ASSIGN_FINAL))
if ((flags & (ASSIGN_CONST | ASSIGN_FINAL))
&& (flags & ASSIGN_FOR_LOOP) == 0)
{
emsg(_("E996: Cannot lock a list or dict"));
return;
@@ -1786,7 +1769,9 @@ next_for_item(void *fi_void, char_u *arg)
{
forinfo_T *fi = (forinfo_T *)fi_void;
int result;
int flag = in_vim9script() ? ASSIGN_DECL : 0;
int flag = ASSIGN_FOR_LOOP | (in_vim9script()
? (ASSIGN_FINAL | ASSIGN_DECL | ASSIGN_NO_MEMBER_TYPE)
: 0);
listitem_T *item;
if (fi->fi_blob != NULL)
@@ -3514,7 +3499,12 @@ eval7(
{
int flags = evalarg == NULL ? 0 : evalarg->eval_flags;
if ((in_vim9script() ? **arg : *skipwhite(*arg)) == '(')
if (evaluate && in_vim9script() && len == 1 && *s == '_')
{
emsg(_(e_cannot_use_underscore_here));
ret = FAIL;
}
else if ((in_vim9script() ? **arg : *skipwhite(*arg)) == '(')
{
// "name(..." recursive!
*arg = skipwhite(*arg);
@@ -4156,68 +4146,8 @@ eval_index_inner(
break;
case VAR_BLOB:
len = blob_len(rettv->vval.v_blob);
if (is_range)
{
// The resulting variable is a sub-blob. If the indexes
// are out of range the result is empty.
if (n1 < 0)
{
n1 = len + n1;
if (n1 < 0)
n1 = 0;
}
if (n2 < 0)
n2 = len + n2;
else if (n2 >= len)
n2 = len - (exclusive ? 0 : 1);
if (exclusive)
--n2;
if (n1 >= len || n2 < 0 || n1 > n2)
{
clear_tv(rettv);
rettv->v_type = VAR_BLOB;
rettv->vval.v_blob = NULL;
}
else
{
blob_T *blob = blob_alloc();
long i;
if (blob != NULL)
{
if (ga_grow(&blob->bv_ga, n2 - n1 + 1) == FAIL)
{
blob_free(blob);
return FAIL;
}
blob->bv_ga.ga_len = n2 - n1 + 1;
for (i = n1; i <= n2; i++)
blob_set(blob, i - n1,
blob_get(rettv->vval.v_blob, i));
clear_tv(rettv);
rettv_blob_set(rettv, blob);
}
}
}
else
{
// The resulting variable is a byte value.
// If the index is too big or negative that is an error.
if (n1 < 0)
n1 = len + n1;
if (n1 < len && n1 >= 0)
{
int v = blob_get(rettv->vval.v_blob, n1);
clear_tv(rettv);
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = v;
}
else
semsg(_(e_blobidx), n1);
}
blob_slice_or_index(rettv->vval.v_blob, is_range, n1, n2,
exclusive, rettv);
break;
case VAR_LIST:
+32 -12
View File
@@ -972,8 +972,8 @@ ex_let_vars(
{
arg = skipwhite(arg + 1);
++var_idx;
arg = ex_let_one(arg, &item->li_tv, TRUE, flags, (char_u *)",;]",
op, var_idx);
arg = ex_let_one(arg, &item->li_tv, TRUE,
flags | ASSIGN_UNPACK, (char_u *)",;]", op, var_idx);
item = item->li_next;
if (arg == NULL)
return FAIL;
@@ -998,8 +998,8 @@ ex_let_vars(
l->lv_refcount = 1;
++var_idx;
arg = ex_let_one(skipwhite(arg + 1), &ltv, FALSE, flags,
(char_u *)"]", op, var_idx);
arg = ex_let_one(skipwhite(arg + 1), &ltv, FALSE,
flags | ASSIGN_UNPACK, (char_u *)"]", op, var_idx);
clear_tv(&ltv);
if (arg == NULL)
return FAIL;
@@ -1317,7 +1317,8 @@ ex_let_one(
// ":let $VAR = expr": Set environment variable.
if (*arg == '$')
{
if (flags & (ASSIGN_CONST | ASSIGN_FINAL))
if ((flags & (ASSIGN_CONST | ASSIGN_FINAL))
&& (flags & ASSIGN_FOR_LOOP) == 0)
{
emsg(_("E996: Cannot lock an environment variable"));
return NULL;
@@ -1367,9 +1368,11 @@ ex_let_one(
// ":let &option = expr": Set option value.
// ":let &l:option = expr": Set local option value.
// ":let &g:option = expr": Set global option value.
// ":for &ts in range(8)": Set option value for for loop
else if (*arg == '&')
{
if (flags & (ASSIGN_CONST | ASSIGN_FINAL))
if ((flags & (ASSIGN_CONST | ASSIGN_FINAL))
&& (flags & ASSIGN_FOR_LOOP) == 0)
{
emsg(_(e_const_option));
return NULL;
@@ -1468,7 +1471,8 @@ ex_let_one(
// ":let @r = expr": Set register contents.
else if (*arg == '@')
{
if (flags & (ASSIGN_CONST | ASSIGN_FINAL))
if ((flags & (ASSIGN_CONST | ASSIGN_FINAL))
&& (flags & ASSIGN_FOR_LOOP) == 0)
{
emsg(_("E996: Cannot lock a register"));
return NULL;
@@ -1521,7 +1525,7 @@ ex_let_one(
else
{
set_var_lval(&lv, p, tv, copy, flags, op, var_idx);
arg_end = p;
arg_end = lv.ll_name_end;
}
}
clear_lval(&lv);
@@ -3160,7 +3164,7 @@ set_var_const(
type_T *type,
typval_T *tv_arg,
int copy, // make copy of value in "tv"
int flags, // ASSIGN_CONST, ASSIGN_FINAL, etc.
int flags_arg, // ASSIGN_CONST, ASSIGN_FINAL, etc.
int var_idx) // index for ":let [a, b] = list"
{
typval_T *tv = tv_arg;
@@ -3171,6 +3175,7 @@ set_var_const(
int is_script_local;
int vim9script = in_vim9script();
int var_in_vim9script;
int flags = flags_arg;
ht = find_var_ht(name, &varname);
if (ht == NULL || *varname == NUL)
@@ -3189,7 +3194,19 @@ set_var_const(
vim9_declare_error(name);
goto failed;
}
if ((flags & ASSIGN_FOR_LOOP) && name[1] == ':'
&& vim_strchr((char_u *)"gwbt", name[0]) != NULL)
// Do not make g:var, w:var, b:var or t:var final.
flags &= ~ASSIGN_FINAL;
var_in_vim9script = is_script_local && current_script_is_vim9();
if (var_in_vim9script && name[0] == '_' && name[1] == NUL)
{
// For "[a, _] = list" the underscore is ignored.
if ((flags & ASSIGN_UNPACK) == 0)
emsg(_(e_cannot_use_underscore_here));
goto failed;
}
di = find_var_in_ht(ht, 0, varname, TRUE);
@@ -3215,7 +3232,8 @@ set_var_const(
// Item already exists. Allowed to replace when reloading.
if ((di->di_flags & DI_FLAGS_RELOAD) == 0)
{
if (flags & (ASSIGN_CONST | ASSIGN_FINAL))
if ((flags & (ASSIGN_CONST | ASSIGN_FINAL))
&& (flags & ASSIGN_FOR_LOOP) == 0)
{
emsg(_(e_cannot_mod));
goto failed;
@@ -3250,7 +3268,8 @@ set_var_const(
// A Vim9 script-local variable is also present in sn_all_vars and
// sn_var_vals. It may set "type" from "tv".
if (var_in_vim9script)
update_vim9_script_var(FALSE, di, flags, tv, &type);
update_vim9_script_var(FALSE, di, flags, tv, &type,
(flags & ASSIGN_NO_MEMBER_TYPE) == 0);
}
// existing variable, need to clear the value
@@ -3348,7 +3367,8 @@ set_var_const(
// A Vim9 script-local variable is also added to sn_all_vars and
// sn_var_vals. It may set "type" from "tv".
if (var_in_vim9script)
update_vim9_script_var(TRUE, di, flags, tv, &type);
update_vim9_script_var(TRUE, di, flags, tv, &type,
(flags & ASSIGN_NO_MEMBER_TYPE) == 0);
}
if (copy || tv->v_type == VAR_NUMBER || tv->v_type == VAR_FLOAT)
+16 -10
View File
@@ -3435,22 +3435,25 @@ find_ex_command(
// "varname.key" is an expression.
|| (*p == '.' && ASCII_ISALPHA(p[1]))))
{
char_u *after = p;
char_u *after = eap->cmd;
// When followed by "=" or "+=" then it is an assignment.
// Skip over the whole thing, it can be:
// name.member = val
// name[a : b] = val
// name[idx] = val
// name[idx].member = val
// etc.
eap->cmdidx = CMD_eval;
++emsg_silent;
if (*after == '.')
after = skipwhite(after + 1);
if (skip_expr(&after, NULL) == OK)
{
after = skipwhite(after);
else
after = (char_u *)"";
if (*after == '=' || (*after != NUL && after[1] == '=')
if (*after == '=' || (*after != NUL && after[1] == '=')
|| (after[0] == '.' && after[1] == '.'
&& after[2] == '='))
eap->cmdidx = CMD_var;
else
eap->cmdidx = CMD_eval;
eap->cmdidx = CMD_var;
}
--emsg_silent;
return eap->cmd;
}
@@ -7378,7 +7381,7 @@ do_sleep(long msec, int hide_cursor)
# endif
if (hide_cursor)
cursor_off();
cursor_sleep();
else
cursor_on();
@@ -7430,6 +7433,9 @@ do_sleep(long msec, int hide_cursor)
// input buffer, otherwise a following call to input() fails.
if (got_int)
(void)vpeekc();
if (hide_cursor)
cursor_unsleep();
}
/*
+3
View File
@@ -230,6 +230,9 @@ EXTERN int did_endif INIT(= FALSE); // just had ":endif"
EXTERN int did_emsg; // set by emsg() when the message
// is displayed or thrown
#ifdef FEAT_EVAL
EXTERN int did_emsg_silent INIT(= 0); // incremented by emsg() when
// emsg_silent was set and did_emsg
// is not incremented
EXTERN int did_emsg_def; // set by emsg() when emsg_silent
// is set before calling a function
EXTERN int did_emsg_cumul; // cumulative did_emsg, increased
+5
View File
@@ -1139,6 +1139,11 @@ gui_update_cursor(
|| gui.row != gui.cursor_row || gui.col != gui.cursor_col)
{
gui_undraw_cursor();
// If a cursor-less sleep is ongoing, leave the cursor invisible
if (cursor_is_sleeping())
return;
if (gui.row < 0)
return;
#ifdef HAVE_INPUT_METHOD
+2 -2
View File
@@ -2059,7 +2059,7 @@ filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap)
{
// Check that map() does not change the type of the dict.
ga_init2(&type_list, sizeof(type_T *), 10);
type = typval2type(argvars, get_copyID(), &type_list);
type = typval2type(argvars, get_copyID(), &type_list, TRUE);
}
if (argvars[0].v_type == VAR_BLOB)
@@ -2565,7 +2565,7 @@ extend(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg, int is_new)
{
// Check that map() does not change the type of the dict.
ga_init2(&type_list, sizeof(type_T *), 10);
type = typval2type(argvars, get_copyID(), &type_list);
type = typval2type(argvars, get_copyID(), &type_list, TRUE);
}
if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST)
+3
View File
@@ -685,6 +685,9 @@ emsg_core(char_u *s)
*/
if (emsg_silent != 0)
{
#ifdef FEAT_EVAL
++did_emsg_silent;
#endif
if (emsg_noredir == 0)
{
msg_start();
+4
View File
@@ -13,5 +13,9 @@ int read_blob(FILE *fd, blob_T *blob);
int write_blob(FILE *fd, blob_T *blob);
char_u *blob2string(blob_T *blob, char_u **tofree, char_u *numbuf);
blob_T *string2blob(char_u *str);
int blob_slice_or_index(blob_T *blob, int is_range, varnumber_T n1, varnumber_T n2, int exclusive, typval_T *rettv);
int check_blob_index(long bloblen, varnumber_T n1, int quiet);
int check_blob_range(long bloblen, varnumber_T n1, varnumber_T n2, int quiet);
int blob_set_range(blob_T *dest, long n1, long n2, typval_T *src);
void blob_remove(typval_T *argvars, typval_T *rettv);
/* vim: set ft=c : */
+3
View File
@@ -56,6 +56,9 @@ void scroll_start(void);
void cursor_on_force(void);
void cursor_on(void);
void cursor_off(void);
int cursor_is_sleeping(void);
void cursor_sleep(void);
void cursor_unsleep(void);
void term_cursor_mode(int forced);
void term_cursor_color(char_u *color);
int blink_state_is_inverted(void);
+1 -1
View File
@@ -12,7 +12,7 @@ void ex_import(exarg_T *eap);
int find_exported(int sid, char_u *name, ufunc_T **ufunc, type_T **type, cctx_T *cctx, int verbose);
char_u *handle_import(char_u *arg_start, garray_T *gap, int import_sid, evalarg_T *evalarg, void *cctx);
char_u *vim9_declare_scriptvar(exarg_T *eap, char_u *arg);
void update_vim9_script_var(int create, dictitem_T *di, int flags, typval_T *tv, type_T **type);
void update_vim9_script_var(int create, dictitem_T *di, int flags, typval_T *tv, type_T **type, int do_member);
void hide_script_var(scriptitem_T *si, int idx, int func_defined);
void free_all_script_vars(scriptitem_T *si);
svar_T *find_typval_in_script(typval_T *dest);
+1 -1
View File
@@ -9,7 +9,7 @@ type_T *alloc_func_type(type_T *ret_type, int argcount, garray_T *type_gap);
type_T *get_func_type(type_T *ret_type, int argcount, garray_T *type_gap);
int func_type_add_arg_types(type_T *functype, int argcount, garray_T *type_gap);
int need_convert_to_bool(type_T *type, typval_T *tv);
type_T *typval2type(typval_T *tv, int copyID, garray_T *type_gap);
type_T *typval2type(typval_T *tv, int copyID, garray_T *type_gap, int do_member);
type_T *typval2type_vimvar(typval_T *tv, garray_T *type_gap);
int check_typval_arg_type(type_T *expected, typval_T *actual_tv, int arg_idx);
int check_typval_type(type_T *expected, typval_T *actual_tv, where_T where);
+5 -4
View File
@@ -1583,10 +1583,11 @@ typedef struct funccall_S funccall_T;
// values used for "uf_def_status"
typedef enum {
UF_NOT_COMPILED,
UF_TO_BE_COMPILED,
UF_COMPILING,
UF_COMPILED
UF_NOT_COMPILED, // executed with interpreter
UF_TO_BE_COMPILED, // to be compiled before execution
UF_COMPILING, // in compile_def_function()
UF_COMPILED, // successfully compiled
UF_COMPILE_ERROR // compilation error, cannot execute
} def_status_T;
/*
+9 -4
View File
@@ -5990,12 +5990,17 @@ get_id_list(
break;
}
if (name[1] == 'A')
id = SYNID_ALLBUT;
id = SYNID_ALLBUT + current_syn_inc_tag;
else if (name[1] == 'T')
id = SYNID_TOP;
{
if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
id = curwin->w_s->b_syn_topgrp;
else
id = SYNID_TOP + current_syn_inc_tag;
}
else
id = SYNID_CONTAINED;
id += current_syn_inc_tag;
id = SYNID_CONTAINED + current_syn_inc_tag;
}
else if (name[1] == '@')
{
+35 -1
View File
@@ -3938,8 +3938,12 @@ scroll_start(void)
}
}
// True if cursor is not visible
static int cursor_is_off = FALSE;
// True if cursor is not visible due to an ongoing cursor-less sleep
static int cursor_is_asleep = FALSE;
/*
* Enable the cursor without checking if it's already enabled.
*/
@@ -3948,6 +3952,7 @@ cursor_on_force(void)
{
out_str(T_VE);
cursor_is_off = FALSE;
cursor_is_asleep = FALSE;
}
/*
@@ -3956,7 +3961,7 @@ cursor_on_force(void)
void
cursor_on(void)
{
if (cursor_is_off)
if (cursor_is_off && !cursor_is_asleep)
cursor_on_force();
}
@@ -3973,6 +3978,35 @@ cursor_off(void)
}
}
/*
* Check whether the cursor is invisible due to an ongoing cursor-less sleep
*/
int
cursor_is_sleeping(void)
{
return cursor_is_asleep;
}
/*
* Disable the cursor and mark it disabled by cursor-less sleep
*/
void
cursor_sleep(void)
{
cursor_is_asleep = TRUE;
cursor_off();
}
/*
* Enable the cursor and mark it not disabled by cursor-less sleep
*/
void
cursor_unsleep(void)
{
cursor_is_asleep = FALSE;
cursor_on();
}
#if defined(CURSOR_SHAPE) || defined(PROTO)
/*
* Set cursor shape to match Insert or Replace mode.
+93 -52
View File
@@ -1,5 +1,7 @@
" Tests for the Blob types
source vim9.vim
func TearDown()
" Run garbage collection after every test
call test_garbagecollect_now()
@@ -9,73 +11,112 @@ endfunc
" Blob creation from constant
func Test_blob_create()
let b = 0zDEADBEEF
call assert_equal(v:t_blob, type(b))
call assert_equal(4, len(b))
call assert_equal(0xDE, b[0])
call assert_equal(0xAD, b[1])
call assert_equal(0xBE, b[2])
call assert_equal(0xEF, b[3])
call assert_fails('let x = b[4]')
let lines =<< trim END
VAR b = 0zDEADBEEF
call assert_equal(v:t_blob, type(b))
call assert_equal(4, len(b))
call assert_equal(0xDE, b[0])
call assert_equal(0xAD, b[1])
call assert_equal(0xBE, b[2])
call assert_equal(0xEF, b[3])
call assert_fails('VAR x = b[4]')
call assert_equal(0xDE, get(b, 0))
call assert_equal(0xEF, get(b, 3))
call assert_equal(0xDE, get(b, 0))
call assert_equal(0xEF, get(b, 3))
call assert_fails('let b = 0z1', 'E973:')
call assert_fails('let b = 0z1x', 'E973:')
call assert_fails('let b = 0z12345', 'E973:')
call assert_fails('VAR b = 0z1', 'E973:')
call assert_fails('VAR b = 0z1x', 'E973:')
call assert_fails('VAR b = 0z12345', 'E973:')
call assert_equal(0z, test_null_blob())
call assert_equal(0z, test_null_blob())
let b = 0z001122.33445566.778899.aabbcc.dd
call assert_equal(0z00112233445566778899aabbccdd, b)
call assert_fails('let b = 0z1.1')
call assert_fails('let b = 0z.')
call assert_fails('let b = 0z001122.')
call assert_fails('call get("", 1)', 'E896:')
call assert_equal(0, len(test_null_blob()))
LET b = 0z001122.33445566.778899.aabbcc.dd
call assert_equal(0z00112233445566778899aabbccdd, b)
call assert_fails('VAR b = 0z1.1')
call assert_fails('VAR b = 0z.')
call assert_fails('VAR b = 0z001122.')
call assert_fails('call get("", 1)', 'E896:')
call assert_equal(0, len(test_null_blob()))
END
call CheckLegacyAndVim9Success(lines)
endfunc
" assignment to a blob
func Test_blob_assign()
let b = 0zDEADBEEF
let b2 = b[1:2]
call assert_equal(0zADBE, b2)
let lines =<< trim END
VAR b = 0zDEADBEEF
VAR b2 = b[1 : 2]
call assert_equal(0zADBE, b2)
let bcopy = b[:]
call assert_equal(b, bcopy)
call assert_false(b is bcopy)
VAR bcopy = b[:]
call assert_equal(b, bcopy)
call assert_false(b is bcopy)
let b = 0zDEADBEEF
let b2 = b
call assert_true(b is b2)
let b[:] = 0z11223344
call assert_equal(0z11223344, b)
call assert_equal(0z11223344, b2)
call assert_true(b is b2)
LET b = 0zDEADBEEF
LET b2 = b
call assert_true(b is b2)
LET b[:] = 0z11223344
call assert_equal(0z11223344, b)
call assert_equal(0z11223344, b2)
call assert_true(b is b2)
let b = 0zDEADBEEF
let b[3:] = 0z66
call assert_equal(0zDEADBE66, b)
let b[:1] = 0z8899
call assert_equal(0z8899BE66, b)
LET b = 0zDEADBEEF
LET b[3 :] = 0z66
call assert_equal(0zDEADBE66, b)
LET b[: 1] = 0z8899
call assert_equal(0z8899BE66, b)
call assert_fails('let b[2:3] = 0z112233', 'E972:')
call assert_fails('let b[2:3] = 0z11', 'E972:')
call assert_fails('let b[3:2] = 0z', 'E979:')
LET b = 0zDEADBEEF
LET b += 0z99
call assert_equal(0zDEADBEEF99, b)
let b = 0zDEADBEEF
let b += 0z99
call assert_equal(0zDEADBEEF99, b)
VAR l = [0z12]
VAR m = deepcopy(l)
LET m[0] = 0z34 #" E742 or E741 should not occur.
END
call CheckLegacyAndVim9Success(lines)
call assert_fails('let b .= 0z33', 'E734:')
call assert_fails('let b .= "xx"', 'E734:')
call assert_fails('let b += "xx"', 'E734:')
call assert_fails('let b[1:1] .= 0z55', 'E734:')
let lines =<< trim END
VAR b = 0zDEADBEEF
LET b[2 : 3] = 0z112233
END
call CheckLegacyAndVim9Failure(lines, 'E972:')
let l = [0z12]
let m = deepcopy(l)
let m[0] = 0z34 " E742 or E741 should not occur.
let lines =<< trim END
VAR b = 0zDEADBEEF
LET b[2 : 3] = 0z11
END
call CheckLegacyAndVim9Failure(lines, 'E972:')
let lines =<< trim END
VAR b = 0zDEADBEEF
LET b[3 : 2] = 0z
END
call CheckLegacyAndVim9Failure(lines, 'E979:')
let lines =<< trim END
VAR b = 0zDEADBEEF
LET b ..= 0z33
END
call CheckLegacyAndVim9Failure(lines, ['E734:', 'E1019:', 'E734:'])
let lines =<< trim END
VAR b = 0zDEADBEEF
LET b ..= "xx"
END
call CheckLegacyAndVim9Failure(lines, ['E734:', 'E1019:', 'E734:'])
let lines =<< trim END
VAR b = 0zDEADBEEF
LET b += "xx"
END
call CheckLegacyAndVim9Failure(lines, ['E734:', 'E1012:', 'E734:'])
let lines =<< trim END
VAR b = 0zDEADBEEF
LET b[1 : 1] ..= 0z55
END
call CheckLegacyAndVim9Failure(lines, ['E734:', 'E1183:', 'E734:'])
endfunc
func Test_blob_get_range()
+9
View File
@@ -1497,6 +1497,7 @@ endfunc
func Test_balloon_show()
CheckFeature balloon_eval
" This won't do anything but must not crash either.
call balloon_show('hi!')
if !has('gui_running')
@@ -2650,4 +2651,12 @@ func Test_browsedir()
call assert_fails('call browsedir("open", [])', 'E730:')
endfunc
func HasDefault(msg = 'msg')
return a:msg
endfunc
func Test_default_arg_value()
call assert_equal('msg', HasDefault())
endfunc
" vim: shiftwidth=2 sts=2 expandtab
+17
View File
@@ -920,4 +920,21 @@ func Test_syn_contained_transparent()
bw!
endfunc
func Test_syn_include_contains_TOP()
let l:case = "TOP in included syntax means its group list name"
new
syntax include @INCLUDED syntax/c.vim
syntax region FencedCodeBlockC start=/```c/ end=/```/ contains=@INCLUDED
call setline(1, ['```c', '#if 0', 'int', '#else', 'int', '#endif', '```' ])
let l:expected = ["cCppOutIf2"]
eval AssertHighlightGroups(3, 1, l:expected, 1)
" cCppOutElse has contains=TOP
let l:expected = ["cType"]
eval AssertHighlightGroups(5, 1, l:expected, 1, l:case)
syntax clear
bw!
endfunc
" vim: shiftwidth=2 sts=2 expandtab
+8
View File
@@ -256,6 +256,14 @@ def Test_assign_unpack()
[v1, v2] = [1, 2]
assert_equal(1, v1)
assert_equal(2, v2)
[v1, _, v2, _] = [1, 99, 2, 77]
assert_equal(1, v1)
assert_equal(2, v2)
[v1, v2; _] = [1, 2, 3, 4, 5]
assert_equal(1, v1)
assert_equal(2, v2)
END
CheckDefAndScriptSuccess(lines)
+2 -2
View File
@@ -506,7 +506,7 @@ def Test_filter_wrong_dict_key_type()
enddef
def Test_filter_return_type()
var l = filter([1, 2, 3], () => 1)
var l = filter([1, 2, 3], (_, _) => 1)
var res = 0
for n in l
res += n
@@ -516,7 +516,7 @@ enddef
def Test_filter_missing_argument()
var dict = {aa: [1], ab: [2], ac: [3], de: [4]}
var res = dict->filter((k) => k =~ 'a' && k !~ 'b')
var res = dict->filter((k, _) => k =~ 'a' && k !~ 'b')
res->assert_equal({aa: [1], ac: [3]})
enddef
+43 -4
View File
@@ -386,6 +386,33 @@ def Test_disassemble_blob_add()
res)
enddef
def s:BlobIndexSlice()
var b: blob = 0z112233
echo b[1]
echo b[1 : 2]
enddef
def Test_disassemble_blob_index_slice()
var res = execute('disass s:BlobIndexSlice')
assert_match('<SNR>\d*_BlobIndexSlice\_s*' ..
'var b: blob = 0z112233\_s*' ..
'\d PUSHBLOB 0z112233\_s*' ..
'\d STORE $0\_s*' ..
'echo b\[1\]\_s*' ..
'\d LOAD $0\_s*' ..
'\d PUSHNR 1\_s*' ..
'\d BLOBINDEX\_s*' ..
'\d ECHO 1\_s*' ..
'echo b\[1 : 2\]\_s*' ..
'\d LOAD $0\_s*' ..
'\d PUSHNR 1\_s*' ..
'\d\+ PUSHNR 2\_s*' ..
'\d\+ BLOBSLICE\_s*' ..
'\d\+ ECHO 1\_s*' ..
'\d\+ RETURN 0',
res)
enddef
def s:ScriptFuncUnlet()
g:somevar = "value"
unlet g:somevar
@@ -770,7 +797,7 @@ def Test_disassemble_const_expr()
'if has("gui_running")\_s*' ..
'\d PUSHS "gui_running"\_s*' ..
'\d BCALL has(argc 1)\_s*' ..
'\d 2BOOL (!!val)\_s*' ..
'\d COND2BOOL\_s*' ..
'\d JUMP_IF_FALSE -> \d\_s*' ..
' echo "yes"\_s*' ..
'\d PUSHS "yes"\_s*' ..
@@ -1537,13 +1564,13 @@ def Test_disassemble_return_bool()
assert_match('ReturnBool\_s*' ..
'var name: bool = 1 && 0 || 1\_s*' ..
'0 PUSHNR 1\_s*' ..
'1 2BOOL (!!val)\_s*' ..
'1 COND2BOOL\_s*' ..
'2 JUMP_IF_COND_FALSE -> 5\_s*' ..
'3 PUSHNR 0\_s*' ..
'4 2BOOL (!!val)\_s*' ..
'4 COND2BOOL\_s*' ..
'5 JUMP_IF_COND_TRUE -> 8\_s*' ..
'6 PUSHNR 1\_s*' ..
'7 2BOOL (!!val)\_s*' ..
'7 COND2BOOL\_s*' ..
'\d STORE $0\_s*' ..
'return name\_s*' ..
'\d\+ LOAD $0\_s*' ..
@@ -2018,5 +2045,17 @@ def Test_profiled()
res)
enddef
def s:EchoMessages()
echohl ErrorMsg | echom v:exception | echohl NONE
enddef
def Test_disassemble_nextcmd()
# splitting commands and removing trailing blanks should not change the line
var res = execute('disass s:EchoMessages')
assert_match('<SNR>\d*_EchoMessages\_s*' ..
'echohl ErrorMsg | echom v:exception | echohl NONE',
res)
enddef
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
+37
View File
@@ -282,6 +282,20 @@ def Test_expr2()
g:vals = []
assert_equal(false, Record(0) || Record(false) || Record(0))
assert_equal([0, false, 0], g:vals)
g:vals = []
var x = 1
if x || true
g:vals = [1]
endif
assert_equal([1], g:vals)
g:vals = []
x = 3
if true || x
g:vals = [1]
endif
assert_equal([1], g:vals)
END
CheckDefAndScriptSuccess(lines)
enddef
@@ -357,6 +371,9 @@ def Test_expr2_fails()
# TODO: should fail at compile time
call CheckDefExecAndScriptFailure(["var x = 3 || 7"], 'E1023:', 1)
call CheckDefAndScriptFailure(["if 3"], 'E1023:', 1)
call CheckDefExecAndScriptFailure(['var x = 3', 'if x', 'endif'], 'E1023:', 2)
call CheckDefAndScriptFailure2(["var x = [] || false"], 'E1012: Type mismatch; expected bool but got list<unknown>', 'E745:', 1)
enddef
@@ -1605,6 +1622,26 @@ def Test_expr7_blob()
assert_equal(g:blob_empty, 0z)
assert_equal(g:blob_one, 0z01)
assert_equal(g:blob_long, 0z0102.0304)
var testblob = 0z010203
assert_equal(0x01, testblob[0])
assert_equal(0x02, testblob[1])
assert_equal(0x03, testblob[-1])
assert_equal(0x02, testblob[-2])
assert_equal(0z01, testblob[0 : 0])
assert_equal(0z0102, testblob[0 : 1])
assert_equal(0z010203, testblob[0 : 2])
assert_equal(0z010203, testblob[0 : ])
assert_equal(0z0203, testblob[1 : ])
assert_equal(0z0203, testblob[1 : 2])
assert_equal(0z0203, testblob[1 : -1])
assert_equal(0z03, testblob[-1 : -1])
assert_equal(0z02, testblob[-2 : -2])
# blob slice accepts out of range
assert_equal(0z, testblob[3 : 3])
assert_equal(0z, testblob[0 : -4])
END
CheckDefAndScriptSuccess(lines)
+131 -7
View File
@@ -74,6 +74,30 @@ def TestCompilingErrorInTry()
delete('Xdir', 'rf')
enddef
def Test_autoload_name_mismatch()
var dir = 'Xdir/autoload'
mkdir(dir, 'p')
var lines =<< trim END
vim9script
def scriptX#Function()
# comment
g:runtime = 'yes'
enddef
END
writefile(lines, dir .. '/script.vim')
var save_rtp = &rtp
exe 'set rtp=' .. getcwd() .. '/Xdir'
lines =<< trim END
call script#Function()
END
CheckScriptFailure(lines, 'E746:', 2)
&rtp = save_rtp
delete(dir, 'rf')
enddef
def CallRecursive(n: number): number
return CallRecursive(n + 1)
enddef
@@ -791,10 +815,20 @@ def Test_call_funcref_wrong_args()
enddef
def Test_call_lambda_args()
var lines =<< trim END
var Callback = (..._) => 'anything'
assert_equal('anything', Callback())
assert_equal('anything', Callback(1))
assert_equal('anything', Callback('a', 2))
assert_equal('xyz', ((a: string): string => a)('xyz'))
END
CheckDefAndScriptSuccess(lines)
CheckDefFailure(['echo ((i) => 0)()'],
'E119: Not enough arguments for function: ((i) => 0)()')
var lines =<< trim END
lines =<< trim END
var Ref = (x: number, y: number) => x + y
echo Ref(1, 'x')
END
@@ -836,6 +870,11 @@ def Test_call_lambda_args()
enddef
END
CheckDefFailure(lines, 'E1167:')
lines =<< trim END
echo ((a) => a)('aa', 'bb')
END
CheckDefAndScriptFailure(lines, 'E118:', 1)
enddef
def FilterWithCond(x: string, Cond: func(string): bool): bool
@@ -918,13 +957,22 @@ def Test_call_def_varargs()
lines =<< trim END
vim9script
def Func(...l: any)
def Func(...l: list<any>)
echo l
enddef
Func(0)
END
CheckScriptSuccess(lines)
lines =<< trim END
vim9script
def Func(...l: any)
echo l
enddef
Func(0)
END
CheckScriptFailure(lines, 'E1180:', 2)
lines =<< trim END
vim9script
def Func(..._l: list<string>)
@@ -1474,7 +1522,7 @@ def Test_redef_failure()
so Xdef
delete('Xdef')
g:Func0()->assert_equal(0)
assert_fails('g:Func0()', 'E1091:')
g:Func1()->assert_equal('Func1')
g:Func2()->assert_equal('Func2')
@@ -2078,7 +2126,7 @@ def Test_script_var_in_lambda()
var lines =<< trim END
vim9script
var script = 'test'
assert_equal(['test'], map(['one'], () => script))
assert_equal(['test'], map(['one'], (_, _) => script))
END
CheckScriptSuccess(lines)
enddef
@@ -2114,7 +2162,7 @@ def Test_list_lambda()
->substitute("('", ' ', '')
->substitute("')", '', '')
->substitute('function\zs', ' ', ''))
assert_match('def <lambda>\d\+(_: any, ...): number\n1 return 0\n enddef', body)
assert_match('def <lambda>\d\+(_: any): number\n1 return 0\n enddef', body)
enddef
def DoFilterThis(a: string): list<string>
@@ -2331,7 +2379,7 @@ def Test_block_scoped_var()
var x = ['a', 'b', 'c']
if 1
var y = 'x'
map(x, () => y)
map(x, (_, _) => y)
endif
var z = x
assert_equal(['x', 'x', 'x'], z)
@@ -2363,7 +2411,7 @@ def Test_did_emsg_reset()
vim9script
au BufWinLeave * #
def Func()
popup_menu('', {callback: () => popup_create('', {})->popup_close()})
popup_menu('', {callback: (a, b) => popup_create('', {})->popup_close()})
eval [][0]
enddef
nno <F3> <cmd>call <sid>Func()<cr>
@@ -2567,6 +2615,82 @@ def Test_check_func_arg_types()
CheckScriptFailure(lines + ['echo H(G(F2))'], 'E1013:')
enddef
def Test_compile_error()
var lines =<< trim END
def g:Broken()
echo 'a' + {}
enddef
call g:Broken()
END
# First call: compilation error
CheckScriptFailure(lines, 'E1051: Wrong argument type for +')
# Second call won't try compiling again
assert_fails('call g:Broken()', 'E1091: Function is not compiled: Broken')
delfunc g:Broken
# No error when compiling with :silent!
lines =<< trim END
def g:Broken()
echo 'a' + []
enddef
silent! defcompile
END
CheckScriptSuccess(lines)
# Calling the function won't try compiling again
assert_fails('call g:Broken()', 'E1091: Function is not compiled: Broken')
delfunc g:Broken
enddef
def Test_ignored_argument()
var lines =<< trim END
vim9script
def Ignore(_, _): string
return 'yes'
enddef
assert_equal('yes', Ignore(1, 2))
func Ok(_)
return a:_
endfunc
assert_equal('ok', Ok('ok'))
func Oktoo()
let _ = 'too'
return _
endfunc
assert_equal('too', Oktoo())
assert_equal([[1], [2], [3]], range(3)->mapnew((_, v) => [v]->map((_, w) => w + 1)))
END
CheckScriptSuccess(lines)
lines =<< trim END
def Ignore(_: string): string
return _
enddef
defcompile
END
CheckScriptFailure(lines, 'E1181:', 1)
lines =<< trim END
var _ = 1
END
CheckDefAndScriptFailure(lines, 'E1181:', 1)
enddef
def Test_too_many_arguments()
var lines =<< trim END
echo [0, 1, 2]->map(() => 123)
END
CheckDefExecAndScriptFailure(lines, 'E1106: 2 arguments too many', 1)
lines =<< trim END
echo [0, 1, 2]->map((_) => 123)
END
CheckDefExecAndScriptFailure(lines, 'E1106: One argument too many', 1)
enddef
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
+105 -75
View File
@@ -2295,70 +2295,89 @@ def Test_for_outside_of_function()
enddef
def Test_for_loop()
var result = ''
for cnt in range(7)
if cnt == 4
break
endif
if cnt == 2
continue
endif
result ..= cnt .. '_'
endfor
assert_equal('0_1_3_', result)
var lines =<< trim END
var result = ''
for cnt in range(7)
if cnt == 4
break
endif
if cnt == 2
continue
endif
result ..= cnt .. '_'
endfor
assert_equal('0_1_3_', result)
var concat = ''
for str in eval('["one", "two"]')
concat ..= str
endfor
assert_equal('onetwo', concat)
var concat = ''
for str in eval('["one", "two"]')
concat ..= str
endfor
assert_equal('onetwo', concat)
var total = 0
for nr in
[1, 2, 3]
total += nr
endfor
assert_equal(6, total)
var total = 0
for nr in
[1, 2, 3]
total += nr
endfor
assert_equal(6, total)
total = 0
for nr
in [1, 2, 3]
total += nr
endfor
assert_equal(6, total)
total = 0
for nr
in [1, 2, 3]
total += nr
endfor
assert_equal(6, total)
total = 0
for nr
in
[1, 2, 3]
total += nr
endfor
assert_equal(6, total)
total = 0
for nr
in
[1, 2, 3]
total += nr
endfor
assert_equal(6, total)
var res = ""
for [n: number, s: string] in [[1, 'a'], [2, 'b']]
res ..= n .. s
endfor
assert_equal('1a2b', res)
# with type
total = 0
for n: number in [1, 2, 3]
total += n
endfor
assert_equal(6, total)
# loop over string
res = ''
for c in 'aéc̀d'
res ..= c .. '-'
endfor
assert_equal('a-é-c̀-d-', res)
# unpack with type
var res = ''
for [n: number, s: string] in [[1, 'a'], [2, 'b']]
res ..= n .. s
endfor
assert_equal('1a2b', res)
res = ''
for c in ''
res ..= c .. '-'
endfor
assert_equal('', res)
# loop over string
res = ''
for c in 'aéc̀d'
res ..= c .. '-'
endfor
assert_equal('a-é-c̀-d-', res)
res = ''
for c in test_null_string()
res ..= c .. '-'
endfor
assert_equal('', res)
res = ''
for c in ''
res ..= c .. '-'
endfor
assert_equal('', res)
res = ''
for c in test_null_string()
res ..= c .. '-'
endfor
assert_equal('', res)
var foo: list<dict<any>> = [
{a: 'Cat'}
]
for dd in foo
dd.counter = 12
endfor
assert_equal([{a: 'Cat', counter: 12}], foo)
END
CheckDefAndScriptSuccess(lines)
enddef
def Test_for_loop_fails()
@@ -2381,6 +2400,14 @@ def Test_for_loop_fails()
g:adict = {a: 1}
CheckDefExecFailure(['for i in g:adict', 'echo 3', 'endfor'], 'E1177: For loop on dict not supported')
unlet g:adict
var lines =<< trim END
var d: list<dict<any>> = [{a: 0}]
for e in d
e = {a: 0, b: ''}
endfor
END
CheckDefAndScriptFailure2(lines, 'E1018:', 'E46:', 3)
enddef
def Test_for_loop_script_var()
@@ -2471,20 +2498,23 @@ def Test_for_loop_unpack()
enddef
def Test_for_loop_with_try_continue()
var looped = 0
var cleanup = 0
for i in range(3)
looped += 1
try
eval [][0]
catch
continue
finally
cleanup += 1
endtry
endfor
assert_equal(3, looped)
assert_equal(3, cleanup)
var lines =<< trim END
var looped = 0
var cleanup = 0
for i in range(3)
looped += 1
try
eval [][0]
catch
continue
finally
cleanup += 1
endtry
endfor
assert_equal(3, looped)
assert_equal(3, cleanup)
END
CheckDefAndScriptSuccess(lines)
enddef
def Test_while_loop()
@@ -3518,7 +3548,7 @@ func Test_no_redraw_when_restoring_cpo()
vim9script
set cpo+=M
exe 'set rtp^=' .. getcwd() .. '/Xdir'
au CmdlineEnter : ++once timer_start(0, () => script#func())
au CmdlineEnter : ++once timer_start(0, (_) => script#func())
setline(1, 'some text')
END
call writefile(lines, 'XTest_redraw_cpo')
@@ -3644,7 +3674,7 @@ enddef
def Test_catch_exception_in_callback()
var lines =<< trim END
vim9script
def Callback(...l: any)
def Callback(...l: list<any>)
try
var x: string
var y: string
@@ -3669,10 +3699,10 @@ def Test_no_unknown_error_after_error()
var lines =<< trim END
vim9script
var source: list<number>
def Out_cb(...l: any)
def Out_cb(...l: list<any>)
eval [][0]
enddef
def Exit_cb(...l: any)
def Exit_cb(...l: list<any>)
sleep 1m
source += l
enddef
@@ -3681,7 +3711,7 @@ def Test_no_unknown_error_after_error()
sleep 10m
endwhile
# wait for Exit_cb() to be called
sleep 100m
sleep 200m
END
writefile(lines, 'Xdef')
assert_fails('so Xdef', ['E684:', 'E1012:'])
+82
View File
@@ -133,3 +133,85 @@ def CheckDefExecAndScriptFailure2(
CheckDefExecFailure(lines, errorDef, lnum)
CheckScriptFailure(['vim9script'] + lines, errorScript, lnum + 1)
enddef
" Check that "lines" inside a legacy function has no error.
func CheckLegacySuccess(lines)
let cwd = getcwd()
let fname = 'XlegacySuccess' .. s:sequence
let s:sequence += 1
call writefile(['func Func()'] + a:lines + ['endfunc'], fname)
try
exe 'so ' .. fname
call Func()
finally
delfunc! Func
call chdir(cwd)
call delete(fname)
endtry
endfunc
" Check that "lines" inside a legacy function results in the expected error
func CheckLegacyFailure(lines, error)
let cwd = getcwd()
let fname = 'XlegacyFails' .. s:sequence
let s:sequence += 1
call writefile(['func Func()'] + a:lines + ['endfunc', 'call Func()'], fname)
try
call assert_fails('so ' .. fname, a:error)
finally
delfunc! Func
call chdir(cwd)
call delete(fname)
endtry
endfunc
" Execute "lines" in a legacy function, :def function and Vim9 script.
" Use 'VAR' for a declaration.
" Use 'LET' for an assignment
" Use ' #"' for a comment
def CheckLegacyAndVim9Success(lines: list<string>)
var legacylines = lines->mapnew((_, v) =>
v->substitute('\<VAR\>', 'let', 'g')
->substitute('\<LET\>', 'let', 'g')
->substitute('#"', ' "', 'g'))
CheckLegacySuccess(legacylines)
var vim9lines = lines->mapnew((_, v) =>
v->substitute('\<VAR\>', 'var', 'g')
->substitute('\<LET ', '', 'g'))
CheckDefSuccess(vim9lines)
CheckScriptSuccess(['vim9script'] + vim9lines)
enddef
" Execute "lines" in a legacy function, :def function and Vim9 script.
" Use 'VAR' for a declaration.
" Use 'LET' for an assignment
" Use ' #"' for a comment
def CheckLegacyAndVim9Failure(lines: list<string>, error: any)
var legacyError: string
var defError: string
var scriptError: string
if type(error) == type('string')
legacyError = error
defError = error
scriptError = error
else
legacyError = error[0]
defError = error[1]
scriptError = error[2]
endif
var legacylines = lines->mapnew((_, v) =>
v->substitute('\<VAR\>', 'let', 'g')
->substitute('\<LET\>', 'let', 'g')
->substitute('#"', ' "', 'g'))
CheckLegacyFailure(legacylines, legacyError)
var vim9lines = lines->mapnew((_, v) =>
v->substitute('\<VAR\>', 'var', 'g')
->substitute('\<LET ', '', 'g'))
CheckDefExecFailure(vim9lines, defError)
CheckScriptFailure(['vim9script'] + vim9lines, scriptError)
enddef
+1 -4
View File
@@ -367,10 +367,7 @@ check_for_nonempty_string_arg(typval_T *args, int idx)
return FAIL;
if (args[idx].vval.v_string == NULL || *args[idx].vval.v_string == NUL)
{
if (idx >= 0)
semsg(_(e_non_empty_string_required_for_argument_nr), idx + 1);
else
emsg(_(e_non_empty_string_required));
semsg(_(e_non_empty_string_required_for_argument_nr), idx + 1);
return FAIL;
}
return OK;
+72 -39
View File
@@ -55,7 +55,7 @@ func_tbl_get(void)
/*
* Get one function argument.
* If "argtypes" is not NULL also get the type: "arg: type".
* If "argtypes" is not NULL also get the type: "arg: type" (:def function).
* If "types_optional" is TRUE a missing type is OK, use "any".
* If "evalarg" is not NULL use it to check for an already declared name.
* Return a pointer to after the type.
@@ -68,10 +68,12 @@ one_function_arg(
garray_T *argtypes,
int types_optional,
evalarg_T *evalarg,
int is_vararg,
int skip)
{
char_u *p = arg;
char_u *arg_copy = NULL;
int is_underscore = FALSE;
while (ASCII_ISALNUM(*p) || *p == '_')
++p;
@@ -106,15 +108,16 @@ one_function_arg(
*p = c;
return arg;
}
// Check for duplicate argument name.
for (i = 0; i < newargs->ga_len; ++i)
if (STRCMP(((char_u **)(newargs->ga_data))[i], arg_copy) == 0)
{
semsg(_("E853: Duplicate argument name: %s"), arg_copy);
vim_free(arg_copy);
return arg;
}
is_underscore = arg_copy[0] == '_' && arg_copy[1] == NUL;
if (argtypes == NULL || !is_underscore)
// Check for duplicate argument name.
for (i = 0; i < newargs->ga_len; ++i)
if (STRCMP(((char_u **)(newargs->ga_data))[i], arg_copy) == 0)
{
semsg(_("E853: Duplicate argument name: %s"), arg_copy);
vim_free(arg_copy);
return arg;
}
((char_u **)(newargs->ga_data))[newargs->ga_len] = arg_copy;
newargs->ga_len++;
@@ -145,7 +148,7 @@ one_function_arg(
if (!skip)
type = vim_strnsave(type, p - type);
}
else if (*skipwhite(p) != '=' && !types_optional)
else if (*skipwhite(p) != '=' && !types_optional && !is_underscore)
{
semsg(_(e_missing_argument_type_for_str),
arg_copy == NULL ? arg : arg_copy);
@@ -155,7 +158,8 @@ one_function_arg(
{
if (type == NULL && types_optional)
// lambda arguments default to "any" type
type = vim_strsave((char_u *)"any");
type = vim_strsave((char_u *)
(is_vararg ? "list<any>" : "any"));
((char_u **)argtypes->ga_data)[argtypes->ga_len++] = type;
}
}
@@ -250,7 +254,7 @@ get_function_args(
arg = p;
p = one_function_arg(p, newargs, argtypes, types_optional,
evalarg, skip);
evalarg, TRUE, skip);
if (p == arg)
break;
if (*skipwhite(p) == '=')
@@ -264,7 +268,7 @@ get_function_args(
{
arg = p;
p = one_function_arg(p, newargs, argtypes, types_optional,
evalarg, skip);
evalarg, FALSE, skip);
if (p == arg)
break;
@@ -360,12 +364,14 @@ err_ret:
static int
parse_argument_types(ufunc_T *fp, garray_T *argtypes, int varargs)
{
int len = 0;
ga_init2(&fp->uf_type_list, sizeof(type_T *), 10);
if (argtypes->ga_len > 0)
{
// When "varargs" is set the last name/type goes into uf_va_name
// and uf_va_type.
int len = argtypes->ga_len - (varargs ? 1 : 0);
len = argtypes->ga_len - (varargs ? 1 : 0);
if (len > 0)
fp->uf_arg_types = ALLOC_CLEAR_MULT(type_T *, len);
@@ -388,25 +394,35 @@ parse_argument_types(ufunc_T *fp, garray_T *argtypes, int varargs)
fp->uf_arg_types[i] = type;
}
}
if (varargs)
{
char_u *p;
// Move the last argument "...name: type" to uf_va_name and
// uf_va_type.
fp->uf_va_name = ((char_u **)fp->uf_args.ga_data)
[fp->uf_args.ga_len - 1];
--fp->uf_args.ga_len;
p = ((char_u **)argtypes->ga_data)[len];
if (p == NULL)
// todo: get type from default value
fp->uf_va_type = &t_any;
else
fp->uf_va_type = parse_type(&p, &fp->uf_type_list, TRUE);
if (fp->uf_va_type == NULL)
return FAIL;
}
}
if (varargs)
{
char_u *p;
// Move the last argument "...name: type" to uf_va_name and
// uf_va_type.
fp->uf_va_name = ((char_u **)fp->uf_args.ga_data)
[fp->uf_args.ga_len - 1];
--fp->uf_args.ga_len;
p = ((char_u **)argtypes->ga_data)[len];
if (p == NULL)
// TODO: get type from default value
fp->uf_va_type = &t_list_any;
else
{
fp->uf_va_type = parse_type(&p, &fp->uf_type_list, TRUE);
if (fp->uf_va_type != NULL && fp->uf_va_type->tt_type != VAR_LIST)
{
semsg(_(e_variable_arguments_type_must_be_list_str),
((char_u **)argtypes->ga_data)[len]);
return FAIL;
}
}
if (fp->uf_va_type == NULL)
return FAIL;
}
return OK;
}
@@ -957,8 +973,7 @@ lambda_function_body(
garray_T *default_args,
char_u *ret_type)
{
int evaluate = evalarg != NULL
&& (evalarg->eval_flags & EVAL_EVALUATE);
int evaluate = (evalarg->eval_flags & EVAL_EVALUATE);
ufunc_T *ufunc = NULL;
exarg_T eap;
garray_T newlines;
@@ -1164,6 +1179,9 @@ get_lambda_tv(
// Recognize "{" as the start of a function body.
if (equal_arrow && **arg == '{')
{
if (evalarg == NULL)
// cannot happen?
goto theend;
if (lambda_function_body(arg, rettv, evalarg, pnewargs,
types_optional ? &argtypes : NULL, varargs,
&default_args, ret_type) == FAIL)
@@ -1236,7 +1254,8 @@ get_lambda_tv(
ga_init(&fp->uf_def_args);
if (types_optional)
{
if (parse_argument_types(fp, &argtypes, FALSE) == FAIL)
if (parse_argument_types(fp, &argtypes,
in_vim9script() && varargs) == FAIL)
goto errret;
if (ret_type != NULL)
{
@@ -1263,8 +1282,9 @@ get_lambda_tv(
#endif
if (sandbox)
flags |= FC_SANDBOX;
// can be called with more args than uf_args.ga_len
fp->uf_varargs = TRUE;
// In legacy script a lambda can be called with more args than
// uf_args.ga_len. In Vim9 script "...name" has to be used.
fp->uf_varargs = !in_vim9script() || varargs;
fp->uf_flags = flags;
fp->uf_calls = 0;
fp->uf_script_ctx = current_sctx;
@@ -2168,6 +2188,8 @@ call_user_func(
int islambda = FALSE;
char_u numbuf[NUMBUFLEN];
char_u *name;
typval_T *tv_to_free[MAX_FUNC_ARGS];
int tv_to_free_len = 0;
#ifdef FEAT_PROFILE
profinfo_T profile_info;
#endif
@@ -2313,6 +2335,7 @@ call_user_func(
if (isdefault)
{
char_u *default_expr = NULL;
def_rettv.v_type = VAR_NUMBER;
def_rettv.vval.v_number = -1;
@@ -2354,6 +2377,10 @@ call_user_func(
v->di_tv = isdefault ? def_rettv : argvars[i];
v->di_tv.v_lock = VAR_FIXED;
if (isdefault)
// Need to free this later, no matter where it's stored.
tv_to_free[tv_to_free_len++] = &v->di_tv;
if (addlocal)
{
// Named arguments should be accessed without the "a:" prefix in
@@ -2543,6 +2570,8 @@ call_user_func(
did_emsg |= save_did_emsg;
funcdepth_decrement();
for (i = 0; i < tv_to_free_len; ++i)
clear_tv(tv_to_free[i]);
cleanup_function_call(fc);
}
@@ -3189,7 +3218,7 @@ list_func_head(ufunc_T *fp, int indent)
msg_puts(", ");
msg_puts("...");
msg_puts((char *)fp->uf_va_name);
if (fp->uf_va_type)
if (fp->uf_va_type != NULL)
{
char *tofree;
@@ -4038,7 +4067,11 @@ define_function(exarg_T *eap, char_u *name_arg)
}
if (j == FAIL)
{
linenr_T save_lnum = SOURCING_LNUM;
SOURCING_LNUM = sourcing_lnum_top;
semsg(_("E746: Function name does not match script file name: %s"), name);
SOURCING_LNUM = save_lnum;
goto erret;
}
}
+58
View File
@@ -765,6 +765,64 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
2767,
/**/
2766,
/**/
2765,
/**/
2764,
/**/
2763,
/**/
2762,
/**/
2761,
/**/
2760,
/**/
2759,
/**/
2758,
/**/
2757,
/**/
2756,
/**/
2755,
/**/
2754,
/**/
2753,
/**/
2752,
/**/
2751,
/**/
2750,
/**/
2749,
/**/
2748,
/**/
2747,
/**/
2746,
/**/
2745,
/**/
2744,
/**/
2743,
/**/
2742,
/**/
2741,
/**/
2740,
/**/
2739,
/**/
2738,
/**/
+7 -4
View File
@@ -2163,10 +2163,13 @@ typedef enum {
} estack_arg_T;
// Flags for assignment functions.
#define ASSIGN_FINAL 1 // ":final"
#define ASSIGN_CONST 2 // ":const"
#define ASSIGN_NO_DECL 4 // "name = expr" without ":let"/":const"/":final"
#define ASSIGN_DECL 8 // may declare variable if it does not exist
#define ASSIGN_FINAL 0x01 // ":final"
#define ASSIGN_CONST 0x02 // ":const"
#define ASSIGN_NO_DECL 0x04 // "name = expr" without ":let"/":const"/":final"
#define ASSIGN_DECL 0x08 // may declare variable if it does not exist
#define ASSIGN_UNPACK 0x10 // using [a, b] = list
#define ASSIGN_NO_MEMBER_TYPE 0x20 // use "any" for list and dict member type
#define ASSIGN_FOR_LOOP 0x40 // assigning to loop variable
#include "ex_cmds.h" // Ex command defines
#include "spell.h" // spell checking stuff
+4
View File
@@ -57,6 +57,8 @@ typedef enum {
ISN_STORENR, // store number into local variable isn_arg.storenr.stnr_idx
ISN_STOREINDEX, // store into list or dictionary, type isn_arg.vartype,
// value/index/variable on stack
ISN_STORERANGE, // store into blob,
// value/index 1/index 2/variable on stack
ISN_UNLET, // unlet variable isn_arg.unlet.ul_name
ISN_UNLETENV, // unlet environment variable isn_arg.unlet.ul_name
@@ -133,6 +135,8 @@ typedef enum {
ISN_LISTAPPEND, // append to a list, like add()
ISN_LISTINDEX, // [expr] list index
ISN_LISTSLICE, // [expr:expr] list slice
ISN_BLOBINDEX, // [expr] blob index
ISN_BLOBSLICE, // [expr:expr] blob slice
ISN_ANYINDEX, // [expr] runtime index
ISN_ANYSLICE, // [expr:expr] runtime slice
ISN_SLICE, // drop isn_arg.number items from start of list
+116 -33
View File
@@ -422,6 +422,10 @@ check_defined(char_u *p, size_t len, cctx_T *cctx, int is_arg)
int c = p[len];
ufunc_T *ufunc = NULL;
// underscore argument is OK
if (len == 1 && *p == '_')
return OK;
if (script_var_exists(p, len, cctx) == OK)
{
if (is_arg)
@@ -986,7 +990,7 @@ bool_on_stack(cctx_T *cctx)
if (type == &t_bool)
return OK;
if (type == &t_any || type == &t_number)
if (type == &t_any || type == &t_number || type == &t_number_bool)
// Number 0 and 1 are OK to use as a bool. "any" could also be a bool.
// This requires a runtime type check.
return generate_COND2BOOL(cctx);
@@ -1791,8 +1795,9 @@ func_needs_compiling(ufunc_T *ufunc, int profile UNUSED)
{
switch (ufunc->uf_def_status)
{
case UF_NOT_COMPILED: break;
case UF_TO_BE_COMPILED: return TRUE;
case UF_TO_BE_COMPILED:
return TRUE;
case UF_COMPILED:
{
#ifdef FEAT_PROFILE
@@ -1805,7 +1810,11 @@ func_needs_compiling(ufunc_T *ufunc, int profile UNUSED)
break;
#endif
}
case UF_COMPILING: break;
case UF_NOT_COMPILED:
case UF_COMPILE_ERROR:
case UF_COMPILING:
break;
}
return FALSE;
}
@@ -1834,7 +1843,8 @@ generate_CALL(cctx_T *cctx, ufunc_T *ufunc, int pushed_argcount)
return FAIL;
}
if (ufunc->uf_def_status != UF_NOT_COMPILED)
if (ufunc->uf_def_status != UF_NOT_COMPILED
&& ufunc->uf_def_status != UF_COMPILE_ERROR)
{
int i;
@@ -1856,7 +1866,8 @@ generate_CALL(cctx_T *cctx, ufunc_T *ufunc, int pushed_argcount)
continue;
expected = ufunc->uf_arg_types[i];
}
else if (ufunc->uf_va_type == NULL || ufunc->uf_va_type == &t_any)
else if (ufunc->uf_va_type == NULL
|| ufunc->uf_va_type == &t_list_any)
// possibly a lambda or "...: any"
expected = &t_any;
else
@@ -1968,7 +1979,7 @@ generate_PCALL(
for (i = 0; i < argcount; ++i)
{
int offset = -argcount + i - 1;
int offset = -argcount + i - (at_top ? 0 : 1);
type_T *actual = ((type_T **)stack->ga_data)[
stack->ga_len + offset];
type_T *expected;
@@ -2714,8 +2725,18 @@ compile_member(int is_slice, cctx_T *cctx)
}
else if (vtype == VAR_BLOB)
{
emsg("Sorry, blob index and slice not implemented yet");
return FAIL;
if (is_slice)
{
*typep = &t_blob;
if (generate_instr_drop(cctx, ISN_BLOBSLICE, 2) == FAIL)
return FAIL;
}
else
{
*typep = &t_number;
if (generate_instr_drop(cctx, ISN_BLOBINDEX, 1) == FAIL)
return FAIL;
}
}
else if (vtype == VAR_LIST || *typep == &t_any)
{
@@ -4077,7 +4098,7 @@ compile_subscript(
// list index: list[123]
// dict member: dict[key]
// string index: text[123]
// TODO: blob index
// blob index: blob[123]
// TODO: more arguments
// TODO: recognize list or dict at runtime
if (generate_ppconst(cctx, ppconst) == FAIL)
@@ -4409,6 +4430,12 @@ compile_expr7(
// "name" or "name()"
p = to_name_end(*arg, TRUE);
if (p - *arg == (size_t)1 && **arg == '_')
{
emsg(_(e_cannot_use_underscore_here));
return FAIL;
}
if (*p == '(')
{
r = compile_call(arg, p - *arg, cctx, ppconst, 0);
@@ -6037,38 +6064,48 @@ compile_lhs(
compile_assign_index(
char_u *var_start,
lhs_T *lhs,
int is_assign,
int *range,
cctx_T *cctx)
{
size_t varlen = lhs->lhs_varlen;
char_u *p;
int r = OK;
int need_white_before = TRUE;
int empty_second;
p = var_start + varlen;
if (*p == '[')
{
p = skipwhite(p + 1);
r = compile_expr0(&p, cctx);
if (*p == ':')
{
// empty first index, push zero
r = generate_PUSHNR(cctx, 0);
need_white_before = FALSE;
}
else
r = compile_expr0(&p, cctx);
if (r == OK && *skipwhite(p) == ':')
{
// unlet var[idx : idx]
if (is_assign)
{
semsg(_(e_cannot_use_range_with_assignment_str), p);
return FAIL;
}
// blob[idx : idx] = value
*range = TRUE;
p = skipwhite(p);
if (!IS_WHITE_OR_NUL(p[-1]) || !IS_WHITE_OR_NUL(p[1]))
empty_second = *skipwhite(p + 1) == ']';
if ((need_white_before && !IS_WHITE_OR_NUL(p[-1]))
|| (!empty_second && !IS_WHITE_OR_NUL(p[1])))
{
semsg(_(e_white_space_required_before_and_after_str_at_str),
":", p);
return FAIL;
}
p = skipwhite(p + 1);
r = compile_expr0(&p, cctx);
if (*p == ']')
// empty second index, push "none"
r = generate_PUSHSPEC(cctx, VVAL_NONE);
else
r = compile_expr0(&p, cctx);
}
if (r == OK && *skipwhite(p) != ']')
@@ -6148,8 +6185,14 @@ compile_assign_unlet(
garray_T *stack = &cctx->ctx_type_stack;
int range = FALSE;
if (compile_assign_index(var_start, lhs, is_assign, &range, cctx) == FAIL)
if (compile_assign_index(var_start, lhs, &range, cctx) == FAIL)
return FAIL;
if (is_assign && range && lhs->lhs_type != &t_blob
&& lhs->lhs_type != &t_any)
{
semsg(_(e_cannot_use_range_with_assignment_str), var_start);
return FAIL;
}
if (lhs->lhs_type == &t_any)
{
@@ -6186,15 +6229,24 @@ compile_assign_unlet(
if (compile_load_lhs(lhs, var_start, rhs_type, cctx) == FAIL)
return FAIL;
if (dest_type == VAR_LIST || dest_type == VAR_DICT || dest_type == VAR_ANY)
if (dest_type == VAR_LIST || dest_type == VAR_DICT
|| dest_type == VAR_BLOB || dest_type == VAR_ANY)
{
if (is_assign)
{
isn_T *isn = generate_instr_drop(cctx, ISN_STOREINDEX, 3);
if (range)
{
if (generate_instr_drop(cctx, ISN_STORERANGE, 4) == NULL)
return FAIL;
}
else
{
isn_T *isn = generate_instr_drop(cctx, ISN_STOREINDEX, 3);
if (isn == NULL)
return FAIL;
isn->isn_arg.vartype = dest_type;
if (isn == NULL)
return FAIL;
isn->isn_arg.vartype = dest_type;
}
}
else if (range)
{
@@ -6352,6 +6404,17 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
{
int instr_count = -1;
if (var_start[0] == '_' && !eval_isnamec(var_start[1]))
{
// Ignore underscore in "[a, _, b] = list".
if (var_count > 0)
{
var_start = skipwhite(var_start + 2);
continue;
}
emsg(_(e_cannot_use_underscore_here));
goto theend;
}
vim_free(lhs.lhs_name);
/*
@@ -6405,8 +6468,14 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
// Get member from list or dict. First compile the
// index value.
if (compile_assign_index(var_start, &lhs,
TRUE, &range, cctx) == FAIL)
&range, cctx) == FAIL)
goto theend;
if (range)
{
semsg(_(e_cannot_use_range_with_assignment_operator_str),
var_start);
return FAIL;
}
// Get the member.
if (compile_member(FALSE, cctx) == FAIL)
@@ -7521,7 +7590,7 @@ compile_for(char_u *arg_start, cctx_T *cctx)
// Reserve a variable to store "var".
// TODO: check for type
var_lvar = reserve_local(cctx, arg, varlen, FALSE, &t_any);
var_lvar = reserve_local(cctx, arg, varlen, TRUE, &t_any);
if (var_lvar == NULL)
// out of memory or used as an argument
goto failed;
@@ -8417,11 +8486,13 @@ compile_def_function(
cctx_T *outer_cctx)
{
char_u *line = NULL;
char_u *line_to_free = NULL;
char_u *p;
char *errormsg = NULL; // error message
cctx_T cctx;
garray_T *instr;
int did_emsg_before = did_emsg;
int did_emsg_silent_before = did_emsg_silent;
int ret = FAIL;
sctx_T save_current_sctx = current_sctx;
int save_estack_compiling = estack_compiling;
@@ -8577,6 +8648,14 @@ compile_def_function(
#endif
break;
}
// Make a copy, splitting off nextcmd and removing trailing spaces
// may change it.
if (line != NULL)
{
line = vim_strsave(line);
vim_free(line_to_free);
line_to_free = line;
}
}
CLEAR_FIELD(ea);
@@ -8960,6 +9039,9 @@ nextline:
generate_instr(&cctx, ISN_RETURN_ZERO);
}
// When compiled with ":silent!" and there was an error don't consider the
// function compiled.
if (emsg_silent == 0 || did_emsg_silent == did_emsg_silent_before)
{
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
+ ufunc->uf_dfunc_idx;
@@ -8987,7 +9069,7 @@ nextline:
ret = OK;
erret:
if (ret == FAIL)
if (ufunc->uf_def_status == UF_COMPILING)
{
int idx;
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
@@ -9006,14 +9088,11 @@ erret:
--def_functions.ga_len;
ufunc->uf_dfunc_idx = 0;
}
ufunc->uf_def_status = UF_NOT_COMPILED;
ufunc->uf_def_status = UF_COMPILE_ERROR;
while (cctx.ctx_scope != NULL)
drop_scope(&cctx);
// Don't execute this function body.
ga_clear_strings(&ufunc->uf_lines);
if (errormsg != NULL)
emsg(errormsg);
else if (did_emsg == did_emsg_before)
@@ -9025,6 +9104,7 @@ erret:
if (do_estack_push)
estack_pop();
vim_free(line_to_free);
free_imported(&cctx);
free_locals(&cctx);
ga_clear(&cctx.ctx_type_stack);
@@ -9069,7 +9149,7 @@ set_function_type(ufunc_T *ufunc)
if (varargs)
{
ufunc->uf_func_type->tt_args[argcount] =
ufunc->uf_va_type == NULL ? &t_any : ufunc->uf_va_type;
ufunc->uf_va_type == NULL ? &t_list_any : ufunc->uf_va_type;
ufunc->uf_func_type->tt_flags = TTFLAG_VARARGS;
}
}
@@ -9212,6 +9292,8 @@ delete_instr(isn_T *isn)
case ISN_ANYSLICE:
case ISN_BCALL:
case ISN_BLOBAPPEND:
case ISN_BLOBINDEX:
case ISN_BLOBSLICE:
case ISN_CATCH:
case ISN_CHECKLEN:
case ISN_CHECKNR:
@@ -9274,6 +9356,7 @@ delete_instr(isn_T *isn)
case ISN_SLICE:
case ISN_STORE:
case ISN_STOREINDEX:
case ISN_STORERANGE:
case ISN_STORENR:
case ISN_STOREOUTER:
case ISN_STOREREG:
+107 -8
View File
@@ -1297,6 +1297,7 @@ call_def_function(
#define STACK_TV_VAR(idx) (((typval_T *)ectx.ec_stack.ga_data) + ectx.ec_frame_idx + STACK_FRAME_SIZE + idx)
if (ufunc->uf_def_status == UF_NOT_COMPILED
|| ufunc->uf_def_status == UF_COMPILE_ERROR
|| (func_needs_compiling(ufunc, PROFILING(ufunc))
&& compile_def_function(ufunc, FALSE, PROFILING(ufunc), NULL)
== FAIL))
@@ -1335,6 +1336,16 @@ call_def_function(
ga_init2(&ectx.ec_trystack, sizeof(trycmd_T), 10);
ga_init2(&ectx.ec_funcrefs, sizeof(partial_T *), 10);
idx = argc - ufunc->uf_args.ga_len;
if (idx > 0 && ufunc->uf_va_name == NULL)
{
if (idx == 1)
emsg(_(e_one_argument_too_many));
else
semsg(_(e_nr_arguments_too_many), idx);
goto failed_early;
}
// Put arguments on the stack, but no more than what the function expects.
// A lambda can be called with more arguments than it uses.
for (idx = 0; idx < argc
@@ -1374,7 +1385,7 @@ call_def_function(
// Check the type of the list items.
tv = STACK_TV_BOT(-1);
if (ufunc->uf_va_type != NULL
&& ufunc->uf_va_type != &t_any
&& ufunc->uf_va_type != &t_list_any
&& ufunc->uf_va_type->tt_member != &t_any
&& tv->vval.v_list != NULL)
{
@@ -2208,6 +2219,10 @@ call_def_function(
clear_tv(tv);
}
}
else if (status == OK && dest_type == VAR_BLOB)
{
// TODO
}
else
{
status = FAIL;
@@ -2225,6 +2240,70 @@ call_def_function(
}
break;
// store value in blob range
case ISN_STORERANGE:
{
typval_T *tv_idx1 = STACK_TV_BOT(-3);
typval_T *tv_idx2 = STACK_TV_BOT(-2);
typval_T *tv_dest = STACK_TV_BOT(-1);
int status = OK;
// Stack contains:
// -4 value to be stored
// -3 first index or "none"
// -2 second index or "none"
// -1 destination blob
tv = STACK_TV_BOT(-4);
if (tv_dest->v_type != VAR_BLOB)
{
status = FAIL;
emsg(_(e_blob_required));
}
else
{
varnumber_T n1;
varnumber_T n2;
int error = FALSE;
n1 = tv_get_number_chk(tv_idx1, &error);
if (error)
status = FAIL;
else
{
if (tv_idx2->v_type == VAR_SPECIAL
&& tv_idx2->vval.v_number == VVAL_NONE)
n2 = blob_len(tv_dest->vval.v_blob) - 1;
else
n2 = tv_get_number_chk(tv_idx2, &error);
if (error)
status = FAIL;
else
{
long bloblen = blob_len(tv_dest->vval.v_blob);
if (check_blob_index(bloblen,
n1, FALSE) == FAIL
|| check_blob_range(bloblen,
n1, n2, FALSE) == FAIL)
status = FAIL;
else
status = blob_set_range(
tv_dest->vval.v_blob, n1, n2, tv);
}
}
}
clear_tv(tv_idx1);
clear_tv(tv_idx2);
clear_tv(tv_dest);
ectx.ec_stack.ga_len -= 4;
clear_tv(tv);
if (status == FAIL)
goto on_error;
}
break;
// load or store variable or argument from outer scope
case ISN_LOADOUTER:
case ISN_STOREOUTER:
@@ -3404,16 +3483,21 @@ call_def_function(
case ISN_LISTINDEX:
case ISN_LISTSLICE:
case ISN_BLOBINDEX:
case ISN_BLOBSLICE:
{
int is_slice = iptr->isn_type == ISN_LISTSLICE;
list_T *list;
int is_slice = iptr->isn_type == ISN_LISTSLICE
|| iptr->isn_type == ISN_BLOBSLICE;
int is_blob = iptr->isn_type == ISN_BLOBINDEX
|| iptr->isn_type == ISN_BLOBSLICE;
varnumber_T n1, n2;
typval_T *val_tv;
// list index: list is at stack-2, index at stack-1
// list slice: list is at stack-3, indexes at stack-2 and
// stack-1
tv = is_slice ? STACK_TV_BOT(-3) : STACK_TV_BOT(-2);
list = tv->vval.v_list;
// Same for blob.
val_tv = is_slice ? STACK_TV_BOT(-3) : STACK_TV_BOT(-2);
tv = STACK_TV_BOT(-1);
n1 = n2 = tv->vval.v_number;
@@ -3429,9 +3513,18 @@ call_def_function(
ectx.ec_stack.ga_len -= is_slice ? 2 : 1;
tv = STACK_TV_BOT(-1);
SOURCING_LNUM = iptr->isn_lnum;
if (list_slice_or_index(list, is_slice, n1, n2, FALSE,
tv, TRUE) == FAIL)
goto on_error;
if (is_blob)
{
if (blob_slice_or_index(val_tv->vval.v_blob, is_slice,
n1, n2, FALSE, tv) == FAIL)
goto on_error;
}
else
{
if (list_slice_or_index(val_tv->vval.v_list, is_slice,
n1, n2, FALSE, tv, TRUE) == FAIL)
goto on_error;
}
}
break;
@@ -4337,6 +4430,10 @@ ex_disassemble(exarg_T *eap)
}
break;
case ISN_STORERANGE:
smsg("%4d STORERANGE", current);
break;
// constants
case ISN_PUSHNR:
smsg("%4d PUSHNR %lld", current,
@@ -4677,6 +4774,8 @@ ex_disassemble(exarg_T *eap)
case ISN_CONCAT: smsg("%4d CONCAT", current); break;
case ISN_STRINDEX: smsg("%4d STRINDEX", current); break;
case ISN_STRSLICE: smsg("%4d STRSLICE", current); break;
case ISN_BLOBINDEX: smsg("%4d BLOBINDEX", current); break;
case ISN_BLOBSLICE: smsg("%4d BLOBSLICE", current); break;
case ISN_LISTAPPEND: smsg("%4d LISTAPPEND", current); break;
case ISN_BLOBAPPEND: smsg("%4d BLOBAPPEND", current); break;
case ISN_LISTINDEX: smsg("%4d LISTINDEX", current); break;
+6 -3
View File
@@ -713,7 +713,8 @@ vim9_declare_scriptvar(exarg_T *eap, char_u *arg)
* When "create" is TRUE this is a new variable, otherwise find and update an
* existing variable.
* "flags" can have ASSIGN_FINAL or ASSIGN_CONST.
* When "*type" is NULL use "tv" for the type and update "*type".
* When "*type" is NULL use "tv" for the type and update "*type". If
* "do_member" is TRUE also use the member type, otherwise use "any".
*/
void
update_vim9_script_var(
@@ -721,7 +722,8 @@ update_vim9_script_var(
dictitem_T *di,
int flags,
typval_T *tv,
type_T **type)
type_T **type,
int do_member)
{
scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
hashitem_T *hi;
@@ -774,7 +776,8 @@ update_vim9_script_var(
if (sv != NULL)
{
if (*type == NULL)
*type = typval2type(tv, get_copyID(), &si->sn_type_list);
*type = typval2type(tv, get_copyID(), &si->sn_type_list,
do_member);
sv->sv_type = *type;
}
+16 -10
View File
@@ -252,9 +252,10 @@ func_type_add_arg_types(
/*
* Get a type_T for a typval_T.
* "type_gap" is used to temporarily create types in.
* When "do_member" is TRUE also get the member type, otherwise use "any".
*/
static type_T *
typval2type_int(typval_T *tv, int copyID, garray_T *type_gap)
typval2type_int(typval_T *tv, int copyID, garray_T *type_gap, int do_member)
{
type_T *type;
type_T *member_type = &t_any;
@@ -274,6 +275,8 @@ typval2type_int(typval_T *tv, int copyID, garray_T *type_gap)
if (l == NULL || l->lv_first == NULL)
return &t_list_empty;
if (!do_member)
return &t_list_any;
if (l->lv_first == &range_list_item)
return &t_list_number;
if (l->lv_copyID == copyID)
@@ -282,9 +285,9 @@ typval2type_int(typval_T *tv, int copyID, garray_T *type_gap)
l->lv_copyID = copyID;
// Use the common type of all members.
member_type = typval2type(&l->lv_first->li_tv, copyID, type_gap);
member_type = typval2type(&l->lv_first->li_tv, copyID, type_gap, TRUE);
for (li = l->lv_first->li_next; li != NULL; li = li->li_next)
common_type(typval2type(&li->li_tv, copyID, type_gap),
common_type(typval2type(&li->li_tv, copyID, type_gap, TRUE),
member_type, &member_type, type_gap);
return get_list_type(member_type, type_gap);
}
@@ -297,6 +300,8 @@ typval2type_int(typval_T *tv, int copyID, garray_T *type_gap)
if (d == NULL || d->dv_hashtab.ht_used == 0)
return &t_dict_empty;
if (!do_member)
return &t_dict_any;
if (d->dv_copyID == copyID)
// avoid recursion
return &t_dict_any;
@@ -305,9 +310,9 @@ typval2type_int(typval_T *tv, int copyID, garray_T *type_gap)
// Use the common type of all values.
dict_iterate_start(tv, &iter);
dict_iterate_next(&iter, &value);
member_type = typval2type(value, copyID, type_gap);
member_type = typval2type(value, copyID, type_gap, TRUE);
while (dict_iterate_next(&iter, &value) != NULL)
common_type(typval2type(value, copyID, type_gap),
common_type(typval2type(value, copyID, type_gap, TRUE),
member_type, &member_type, type_gap);
return get_dict_type(member_type, type_gap);
}
@@ -378,11 +383,12 @@ need_convert_to_bool(type_T *type, typval_T *tv)
/*
* Get a type_T for a typval_T.
* "type_list" is used to temporarily create types in.
* When "do_member" is TRUE also get the member type, otherwise use "any".
*/
type_T *
typval2type(typval_T *tv, int copyID, garray_T *type_gap)
typval2type(typval_T *tv, int copyID, garray_T *type_gap, int do_member)
{
type_T *type = typval2type_int(tv, copyID, type_gap);
type_T *type = typval2type_int(tv, copyID, type_gap, do_member);
if (type != NULL && type != &t_bool
&& (tv->v_type == VAR_NUMBER
@@ -404,7 +410,7 @@ typval2type_vimvar(typval_T *tv, garray_T *type_gap)
return &t_list_string;
if (tv->v_type == VAR_DICT) // e.g. for v:completed_item
return &t_dict_any;
return typval2type(tv, get_copyID(), type_gap);
return typval2type(tv, get_copyID(), type_gap, TRUE);
}
int
@@ -429,7 +435,7 @@ check_typval_type(type_T *expected, typval_T *actual_tv, where_T where)
int res = FAIL;
ga_init2(&type_list, sizeof(type_T *), 10);
actual_type = typval2type(actual_tv, get_copyID(), &type_list);
actual_type = typval2type(actual_tv, get_copyID(), &type_list, TRUE);
if (actual_type != NULL)
res = check_type(expected, actual_type, TRUE, where);
clear_type_list(&type_list);
@@ -1210,7 +1216,7 @@ f_typename(typval_T *argvars, typval_T *rettv)
rettv->v_type = VAR_STRING;
ga_init2(&type_list, sizeof(type_T *), 10);
type = typval2type(argvars, get_copyID(), &type_list);
type = typval2type(argvars, get_copyID(), &type_list, TRUE);
name = type_name(type, &tofree);
if (tofree != NULL)
rettv->vval.v_string = (char_u *)tofree;