mirror of
https://github.com/macvim-dev/macvim.git
synced 2026-06-11 15:37:29 +02:00
Merge remote-tracking branch 'vim/master'
This commit is contained in:
+37
-11
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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), <v, FALSE, flags,
|
||||
(char_u *)"]", op, var_idx);
|
||||
arg = ex_let_one(skipwhite(arg + 1), <v, FALSE,
|
||||
flags | ASSIGN_UNPACK, (char_u *)"]", op, var_idx);
|
||||
clear_tv(<v);
|
||||
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
@@ -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();
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 : */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:'])
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
/**/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user