Merge remote-tracking branch 'vim/master'

This commit is contained in:
ichizok
2021-01-11 11:01:19 +09:00
36 changed files with 1632 additions and 627 deletions
+121 -29
View File
@@ -1,4 +1,4 @@
*eval.txt* For Vim version 8.2. Last change: 2020 Dec 29
*eval.txt* For Vim version 8.2. Last change: 2021 Jan 10
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -49,7 +49,7 @@ There are ten types of variables:
*Number* *Integer*
Number A 32 or 64 bit signed number. |expr-number|
The number of bits is available in |v:numbersize|.
Examples: -123 0x10 0177 0b1011
Examples: -123 0x10 0177 0o177 0b1011
Float A floating point number. |floating-point-format| *Float*
{only when compiled with the |+float| feature}
@@ -97,9 +97,10 @@ the Number. Examples:
Conversion from a String to a Number only happens in legacy Vim script, not in
Vim9 script. It is done by converting the first digits to a number.
Hexadecimal "0xf9", Octal "017" or "0o17", and Binary "0b10"
numbers are recognized (NOTE: when using |scriptversion-4| octal with a
leading "0" is not recognized). If the String doesn't start with digits, the
result is zero.
numbers are recognized
NOTE: when using |scriptversion-4| octal with a leading "0" is not recognized.
The 0o notation requires patch 8.2.0886.
If the String doesn't start with digits, the result is zero.
Examples:
String "456" --> Number 456 ~
String "6bar" --> Number 6 ~
@@ -1150,7 +1151,7 @@ expr7 *expr7*
For '!' |TRUE| becomes |FALSE|, |FALSE| becomes |TRUE| (one).
For '-' the sign of the number is changed.
For '+' the number is unchanged.
For '+' the number is unchanged. Note: "++" has no effect.
A String will be converted to a Number first.
@@ -1191,6 +1192,7 @@ start with one!
If the length of the String is less than the index, the result is an empty
String. A negative index always results in an empty string (reason: backward
compatibility). Use [-1:] to get the last byte or character.
In Vim9 script a negative index is used like with a list: count from the end.
If expr8 is a |List| then it results the item at index expr1. See |list-index|
for possible index values. If the index is out of range this results in an
@@ -1318,8 +1320,8 @@ When using the lambda form there must be no white space between the } and the
number
------
number number constant *expr-number*
*hex-number* *octal-number* *binary-number*
*0x* *hex-number* *0o* *octal-number* *binary-number*
Decimal, Hexadecimal (starting with 0x or 0X), Binary (starting with 0b or 0B)
and Octal (starting with 0, 0o or 0O).
@@ -1572,7 +1574,7 @@ Note how execute() is used to execute an Ex command. That's ugly though.
Lambda expressions have internal names like '<lambda>42'. If you get an error
for a lambda expression, you can find what it is with the following command: >
:function {'<lambda>42'}
:function <lambda>42
See also: |numbered-function|
==============================================================================
@@ -2487,12 +2489,13 @@ ch_status({handle} [, {options}])
changenr() Number current change number
char2nr({expr} [, {utf8}]) Number ASCII/UTF8 value of first char in {expr}
charclass({string}) Number character class of {string}
charcol({expr}) Number column number of cursor or mark
charidx({string}, {idx} [, {countcc}])
Number char index of byte {idx} in {string}
chdir({dir}) String change current working directory
cindent({lnum}) Number C indent for line {lnum}
clearmatches([{win}]) none clear all matches
col({expr}) Number column nr of cursor or mark
col({expr}) Number column byte index of cursor or mark
complete({startcol}, {matches}) none set Insert mode completion
complete_add({expr}) Number add completion match
complete_check() Number check for key typed during completion
@@ -2570,6 +2573,7 @@ getbufvar({expr}, {varname} [, {def}])
getchangelist([{expr}]) List list of change list items
getchar([expr]) Number get one character from the user
getcharmod() Number modifiers for the last typed character
getcharpos({expr}) List position of cursor, mark, etc.
getcharsearch() Dict last character search
getcmdline() String return the current command-line
getcmdpos() Number return cursor position in command-line
@@ -2578,6 +2582,7 @@ getcmdwintype() String return current command-line window type
getcompletion({pat}, {type} [, {filtered}])
List list of cmdline completion matches
getcurpos([{winnr}]) List position of the cursor
getcursorcharpos([{winnr}]) List character position of the cursor
getcwd([{winnr} [, {tabnr}]]) String get the current working directory
getenv({name}) String return environment variable
getfontname([{name}]) String name of font being used
@@ -2840,8 +2845,10 @@ setbufline({expr}, {lnum}, {text})
setbufvar({expr}, {varname}, {val})
none set {varname} in buffer {expr} to {val}
setcellwidths({list}) none set character cell width overrides
setcharpos({expr}, {list}) Number set the {expr} position to {list}
setcharsearch({dict}) Dict set character search from {dict}
setcmdpos({pos}) Number set cursor position in command-line
setcursorcharpos({list}) Number move cursor to position in {list}
setenv({name}, {val}) none set environment variable
setfperm({fname}, {mode}) Number set {fname} file permissions to {mode}
setline({lnum}, {line}) Number set line {lnum} to {line}
@@ -3525,8 +3532,8 @@ byteidxcomp({expr}, {nr}) *byteidxcomp()*
< The first and third echo result in 3 ('e' plus composing
character is 3 bytes), the second echo results in 1 ('e' is
one byte).
Only works differently from byteidx() when 'encoding' is set to
a Unicode encoding.
Only works differently from byteidx() when 'encoding' is set
to a Unicode encoding.
Can also be used as a |method|: >
GetName()->byteidxcomp(idx)
@@ -3602,6 +3609,18 @@ charclass({string}) *charclass()*
other specific Unicode class
The class is used in patterns and word motions.
*charcol()*
charcol({expr}) Same as |col()| but returns the character index of the column
position given with {expr} instead of the byte position.
Example:
With the cursor on '세' in line 5 with text "여보세요": >
charcol('.') returns 3
col('.') returns 7
< Can also be used as a |method|: >
GetPos()->col()
<
*charidx()*
charidx({string}, {idx} [, {countcc}])
Return the character index of the byte at {idx} in {string}.
@@ -3692,7 +3711,8 @@ col({expr}) The result is a Number, which is the byte index of the column
out of range then col() returns zero.
To get the line number use |line()|. To get both use
|getpos()|.
For the screen column position use |virtcol()|.
For the screen column position use |virtcol()|. For the
character position use |charcol()|.
Note that only marks in the current file can be used.
Examples: >
col(".") column of cursor
@@ -3993,6 +4013,9 @@ cursor({list})
This is like the return value of |getpos()| or |getcurpos()|,
but without the first item.
To position the cursor using the character count, use
|setcursorcharpos()|.
Does not change the jumplist.
If {lnum} is greater than the number of lines in the buffer,
the cursor will be positioned at the last line in the buffer.
@@ -5232,6 +5255,20 @@ getcharmod() *getcharmod()*
character itself are obtained. Thus Shift-a results in "A"
without a modifier.
*getcharpos()*
getcharpos({expr})
Get the position for {expr}. Same as |getpos()| but the column
number in the returned List is a character index instead of
a byte index.
Example:
With the cursor on '세' in line 5 with text "여보세요": >
getcharpos('.') returns [0, 5, 3, 0]
getpos('.') returns [0, 5, 7, 0]
<
Can also be used as a |method|: >
GetMark()->getcharpos()
getcharsearch() *getcharsearch()*
Return the current character search information as a {dict}
with the following entries:
@@ -5357,8 +5394,11 @@ getcurpos([{winid}])
includes an extra "curswant" item in the list:
[0, lnum, col, off, curswant] ~
The "curswant" number is the preferred column when moving the
cursor vertically. Also see |getpos()|.
The first "bufnum" item is always zero.
cursor vertically. Also see |getcursorcharpos()| and
|getpos()|.
The first "bufnum" item is always zero. The byte position of
the cursor is returned in 'col'. To get the character
position, use |getcursorcharpos()|.
The optional {winid} argument can specify the window. It can
be the window number or the |window-ID|. The last known
@@ -5372,7 +5412,24 @@ getcurpos([{winid}])
call setpos('.', save_cursor)
< Note that this only works within the window. See
|winrestview()| for restoring more state.
*getcwd()*
Can also be used as a |method|: >
GetWinid()->getcurpos()
< *getcursorcharpos()*
getcursorcharpos([{winid}])
Same as |getcurpos()| but the column number in the returned
List is a character index instead of a byte index.
Example:
With the cursor on '보' in line 3 with text "여보세요": >
getcursorcharpos() returns [0, 3, 2, 0, 3]
getcurpos() returns [0, 3, 4, 0, 3]
< Can also be used as a |method|: >
GetWinid()->getcursorcharpos()
< *getcwd()*
getcwd([{winnr} [, {tabnr}]])
The result is a String, which is the name of the current
working directory.
@@ -5679,16 +5736,18 @@ getpos({expr}) Get the position for {expr}. For possible values of {expr}
Note that for '< and '> Visual mode matters: when it is "V"
(visual line mode) the column of '< is zero and the column of
'> is a large number.
The column number in the returned List is the byte position
within the line. To get the character position in the line,
use |getcharpos()|
This can be used to save and restore the position of a mark: >
let save_a_mark = getpos("'a")
...
call setpos("'a", save_a_mark)
< Also see |getcurpos()| and |setpos()|.
< Also see |getcharpos()|, |getcurpos()| and |setpos()|.
Can also be used as a |method|: >
GetMark()->getpos()
getqflist([{what}]) *getqflist()*
Returns a |List| with all the current quickfix errors. Each
list item is a dictionary with these entries:
@@ -7554,8 +7613,10 @@ matchstrpos({expr}, {pat} [, {start} [, {count}]]) *matchstrpos()*
<
*max()*
max({expr}) Return the maximum value of all items in {expr}.
{expr} can be a |List| or a |Dictionary|. For a Dictionary,
max({expr}) Return the maximum value of all items in {expr}. Example: >
echo max([apples, pears, oranges])
< {expr} can be a |List| or a |Dictionary|. For a Dictionary,
it returns the maximum of all values in the Dictionary.
If {expr} is neither a List nor a Dictionary, or one of the
items in {expr} cannot be used as a Number this results in
@@ -7625,8 +7686,10 @@ menu_info({name} [, {mode}]) *menu_info()*
< *min()*
min({expr}) Return the minimum value of all items in {expr}.
{expr} can be a |List| or a |Dictionary|. For a Dictionary,
min({expr}) Return the minimum value of all items in {expr}. Example: >
echo min([apples, pears, oranges])
< {expr} can be a |List| or a |Dictionary|. For a Dictionary,
it returns the minimum of all values in the Dictionary.
If {expr} is neither a List nor a Dictionary, or one of the
items in {expr} cannot be used as a Number this results in
@@ -7643,13 +7706,13 @@ mkdir({name} [, {path} [, {prot}]])
necessary. Otherwise it must be "".
If {prot} is given it is used to set the protection bits of
the new directory. The default is 0755 (rwxr-xr-x: r/w for
the user readable for others). Use 0700 to make it unreadable
for others. This is only used for the last part of {name}.
Thus if you create /tmp/foo/bar then /tmp/foo will be created
with 0755.
the new directory. The default is 0o755 (rwxr-xr-x: r/w for
the user, readable for others). Use 0o700 to make it
unreadable for others. This is only used for the last part of
{name}. Thus if you create /tmp/foo/bar then /tmp/foo will be
created with 0o755.
Example: >
:call mkdir($HOME . "/tmp/foo/bar", "p", 0700)
:call mkdir($HOME . "/tmp/foo/bar", "p", 0o700)
< This function is not available in the |sandbox|.
@@ -9212,6 +9275,19 @@ setcellwidths({list}) *setcellwidths()*
< You can use the script $VIMRUNTIME/tools/emoji_list.vim to see
the effect for known emoji characters.
setcharpos({expr}, {list}) *setcharpos()*
Same as |setpos()| but uses the specified column number as the
character index instead of the byte index in the line.
Example:
With the text "여보세요" in line 8: >
call setcharpos('.', [0, 8, 4, 0])
< positions the cursor on the fourth character '요'. >
call setpos('.', [0, 8, 4, 0])
< positions the cursor on the second character '보'.
Can also be used as a |method|: >
GetPosition()->setcharpos('.')
setcharsearch({dict}) *setcharsearch()*
Set the current character search information to {dict},
@@ -9254,6 +9330,21 @@ setcmdpos({pos}) *setcmdpos()*
Can also be used as a |method|: >
GetPos()->setcmdpos()
setcursorcharpos({lnum}, {col} [, {off}]) *setcursorcharpos()*
setcursorcharpos({list})
Same as |cursor()| but uses the specified column number as the
character index instead of the byte index in the line.
Example:
With the text "여보세요" in line 4: >
call setcursorcharpos(4, 3)
< positions the cursor on the third character '세'. >
call cursor(4, 3)
< positions the cursor on the first character '여'.
Can also be used as a |method|: >
GetCursorPos()->setcursorcharpos()
setenv({name}, {val}) *setenv()*
Set environment variable {name} to {val}.
When {val} is |v:null| the environment variable is deleted.
@@ -9365,7 +9456,8 @@ setpos({expr}, {list})
"lnum" and "col" are the position in the buffer. The first
column is 1. Use a zero "lnum" to delete a mark. If "col" is
smaller than 1 then 1 is used.
smaller than 1 then 1 is used. To use the character count
instead of the byte count, use |setcharpos()|.
The "off" number is only used when 'virtualedit' is set. Then
it is the offset in screen columns from the start of the
@@ -9385,7 +9477,7 @@ setpos({expr}, {list})
Returns 0 when the position could be set, -1 otherwise.
An error message is given if {expr} is invalid.
Also see |getpos()| and |getcurpos()|.
Also see |setcharpos()|, |getpos()| and |getcurpos()|.
This does not restore the preferred column for moving
vertically; if you set the cursor position with this, |j| and
+5
View File
@@ -753,6 +753,11 @@ Cursor and mark position: *cursor-functions* *mark-functions*
screenchar() get character code at a screen line/row
screenchars() get character codes at a screen line/row
screenstring() get string of characters at a screen line/row
charcol() character number of the cursor or a mark
getcharpos() get character position of cursor, mark, etc.
setcharpos() set character position of cursor, mark, etc.
getcursorcharpos() get character position of the cursor
setcursorcharpos() set character position of the cursor
Working with text in the current buffer: *text-functions*
getline() get a line or list of lines from the buffer
+7 -1
View File
@@ -1594,6 +1594,12 @@ vim_isbdigit(int c)
return (c == '0' || c == '1');
}
static int
vim_isodigit(int c)
{
return (c >= '0' && c <= '7');
}
/*
* Vim's own character class functions. These exist because many library
* islower()/toupper() etc. do not work properly: they crash when used with
@@ -1831,7 +1837,7 @@ vim_str2nr(
// binary
ptr += 2;
else if ((what & STR2NR_OOCT)
&& (pre == 'O' || pre == 'o') && vim_isbdigit(ptr[2])
&& (pre == 'O' || pre == 'o') && vim_isodigit(ptr[2])
&& (maxlen == 0 || maxlen > 2))
// octal with prefix "0o"
ptr += 2;
+2
View File
@@ -341,3 +341,5 @@ EXTERN char e_mismatched_endfunction[]
INIT(= N_("E1151: Mismatched endfunction"));
EXTERN char e_mismatched_enddef[]
INIT(= N_("E1152: Mismatched enddef"));
EXTERN char e_invalid_operation_for_bool[]
INIT(= N_("E1153: Invalid operation for bool"));
+107 -106
View File
@@ -721,8 +721,10 @@ call_func_retlist(
#ifdef FEAT_FOLDING
/*
* Evaluate 'foldexpr'. Returns the foldlevel, and any character preceding
* it in "*cp". Doesn't give error messages.
* Evaluate "arg", which is 'foldexpr'.
* Note: caller must set "curwin" to match "arg".
* Returns the foldlevel, and any character preceding it in "*cp". Doesn't
* give error messages.
*/
int
eval_foldexpr(char_u *arg, int *cp)
@@ -809,6 +811,7 @@ get_lval(
int len;
hashtab_T *ht = NULL;
int quiet = flags & GLV_QUIET;
int writing;
// Clear everything in "lp".
CLEAR_POINTER(lp);
@@ -882,10 +885,10 @@ get_lval(
cc = *p;
*p = NUL;
// Only pass &ht when we would write to the variable, it prevents autoload
// as well.
v = find_var(lp->ll_name, (flags & GLV_READ_ONLY) ? NULL : &ht,
flags & GLV_NO_AUTOLOAD);
// When we would write to the variable pass &ht and prevent autoload.
writing = !(flags & GLV_READ_ONLY);
v = find_var(lp->ll_name, writing ? &ht : NULL,
(flags & GLV_NO_AUTOLOAD) || writing);
if (v == NULL && !quiet)
semsg(_(e_undefined_variable_str), lp->ll_name);
*p = cc;
@@ -1972,13 +1975,15 @@ eval_func(
int len = name_len;
partial_T *partial;
int ret = OK;
type_T *type = NULL;
if (!evaluate)
check_vars(s, len);
// If "s" is the name of a variable of type VAR_FUNC
// use its contents.
s = deref_func_name(s, &len, &partial, !evaluate);
s = deref_func_name(s, &len, &partial,
in_vim9script() ? &type : NULL, !evaluate);
// Need to make a copy, in case evaluating the arguments makes
// the name invalid.
@@ -1996,6 +2001,7 @@ eval_func(
funcexe.evaluate = evaluate;
funcexe.partial = partial;
funcexe.basetv = basetv;
funcexe.check_type = type;
ret = get_func_tv(s, len, rettv, arg, evalarg, &funcexe);
}
vim_free(s);
@@ -2649,7 +2655,7 @@ eval4(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
{
char_u *p;
int getnext;
exptype_T type = EXPR_UNKNOWN;
exprtype_T type = EXPR_UNKNOWN;
int len = 2;
int type_is = FALSE;
@@ -5047,6 +5053,61 @@ string2float(
}
#endif
/*
* Convert the specified byte index of line 'lnum' in buffer 'buf' to a
* character index. Works only for loaded buffers. Returns -1 on failure.
* The index of the first character is one.
*/
int
buf_byteidx_to_charidx(buf_T *buf, int lnum, int byteidx)
{
char_u *str;
if (buf == NULL || buf->b_ml.ml_mfp == NULL)
return -1;
if (lnum > buf->b_ml.ml_line_count)
lnum = buf->b_ml.ml_line_count;
str = ml_get_buf(buf, lnum, FALSE);
if (str == NULL)
return -1;
if (*str == NUL)
return 1;
return mb_charlen_len(str, byteidx + 1);
}
/*
* Convert the specified character index of line 'lnum' in buffer 'buf' to a
* byte index. Works only for loaded buffers. Returns -1 on failure. The index
* of the first byte and the first character is one.
*/
int
buf_charidx_to_byteidx(buf_T *buf, int lnum, int charidx)
{
char_u *str;
char_u *t;
if (buf == NULL || buf->b_ml.ml_mfp == NULL)
return -1;
if (lnum > buf->b_ml.ml_line_count)
lnum = buf->b_ml.ml_line_count;
str = ml_get_buf(buf, lnum, FALSE);
if (str == NULL)
return -1;
// Convert the character offset to a byte offset
t = str;
while (*t != NUL && --charidx > 0)
t += mb_ptr2len(t);
return t - str + 1;
}
/*
* Translate a String variable into a position.
* Returns NULL when there is an error.
@@ -5055,7 +5116,8 @@ string2float(
var2fpos(
typval_T *varp,
int dollar_lnum, // TRUE when $ is last line
int *fnum) // set to fnum for '0, 'A, etc.
int *fnum, // set to fnum for '0, 'A, etc.
int charcol) // return character column
{
char_u *name;
static pos_T pos;
@@ -5077,7 +5139,10 @@ var2fpos(
pos.lnum = list_find_nr(l, 0L, &error);
if (error || pos.lnum <= 0 || pos.lnum > curbuf->b_ml.ml_line_count)
return NULL; // invalid line number
len = (long)STRLEN(ml_get(pos.lnum));
if (charcol)
len = (long)mb_charlen(ml_get(pos.lnum));
else
len = (long)STRLEN(ml_get(pos.lnum));
// Get the column number
// We accept "$" for the column number: last column.
@@ -5112,18 +5177,29 @@ var2fpos(
if (name == NULL)
return NULL;
if (name[0] == '.') // cursor
return &curwin->w_cursor;
{
pos = curwin->w_cursor;
if (charcol)
pos.col = buf_byteidx_to_charidx(curbuf, pos.lnum, pos.col) - 1;
return &pos;
}
if (name[0] == 'v' && name[1] == NUL) // Visual start
{
if (VIsual_active)
return &VIsual;
return &curwin->w_cursor;
pos = VIsual;
else
pos = curwin->w_cursor;
if (charcol)
pos.col = buf_byteidx_to_charidx(curbuf, pos.lnum, pos.col) - 1;
return &pos;
}
if (name[0] == '\'') // mark
{
pp = getmark_buf_fnum(curbuf, name[1], FALSE, fnum);
if (pp == NULL || pp == (pos_T *)-1 || pp->lnum <= 0)
return NULL;
if (charcol)
pp->col = buf_byteidx_to_charidx(curbuf, pp->lnum, pp->col) - 1;
return pp;
}
@@ -5158,7 +5234,10 @@ var2fpos(
else
{
pos.lnum = curwin->w_cursor.lnum;
pos.col = (colnr_T)STRLEN(ml_get_curline());
if (charcol)
pos.col = (colnr_T)mb_charlen(ml_get_curline());
else
pos.col = (colnr_T)STRLEN(ml_get_curline());
}
return &pos;
}
@@ -5178,7 +5257,8 @@ list2fpos(
typval_T *arg,
pos_T *posp,
int *fnump,
colnr_T *curswantp)
colnr_T *curswantp,
int charcol)
{
list_T *l = arg->vval.v_list;
long i = 0;
@@ -5210,6 +5290,18 @@ list2fpos(
n = list_find_nr(l, i++, NULL); // col
if (n < 0)
return FAIL;
// If character position is specified, then convert to byte position
if (charcol)
{
buf_T *buf;
// Get the text for the specified line in a loaded buffer
buf = buflist_findnr(fnump == NULL ? curbuf->b_fnum : *fnump);
if (buf == NULL || buf->b_ml.ml_mfp == NULL)
return FAIL;
n = buf_charidx_to_byteidx(buf, posp->lnum, n);
}
posp->col = n;
n = list_find_nr(l, i, NULL); // off
@@ -5542,97 +5634,6 @@ eval_isdictc(int c)
return ASCII_ISALNUM(c) || c == '_';
}
/*
* Return the character "str[index]" where "index" is the character index. If
* "index" is out of range NULL is returned.
*/
char_u *
char_from_string(char_u *str, varnumber_T index)
{
size_t nbyte = 0;
varnumber_T nchar = index;
size_t slen;
if (str == NULL || index < 0)
return NULL;
slen = STRLEN(str);
while (nchar > 0 && nbyte < slen)
{
nbyte += MB_CPTR2LEN(str + nbyte);
--nchar;
}
if (nbyte >= slen)
return NULL;
return vim_strnsave(str + nbyte, MB_CPTR2LEN(str + nbyte));
}
/*
* Get the byte index for character index "idx" in string "str" with length
* "str_len".
* If going over the end return "str_len".
* If "idx" is negative count from the end, -1 is the last character.
* When going over the start return -1.
*/
static long
char_idx2byte(char_u *str, size_t str_len, varnumber_T idx)
{
varnumber_T nchar = idx;
size_t nbyte = 0;
if (nchar >= 0)
{
while (nchar > 0 && nbyte < str_len)
{
nbyte += MB_CPTR2LEN(str + nbyte);
--nchar;
}
}
else
{
nbyte = str_len;
while (nchar < 0 && nbyte > 0)
{
--nbyte;
nbyte -= mb_head_off(str, str + nbyte);
++nchar;
}
if (nchar < 0)
return -1;
}
return (long)nbyte;
}
/*
* Return the slice "str[first:last]" using character indexes.
* Return NULL when the result is empty.
*/
char_u *
string_slice(char_u *str, varnumber_T first, varnumber_T last)
{
long start_byte, end_byte;
size_t slen;
if (str == NULL)
return NULL;
slen = STRLEN(str);
start_byte = char_idx2byte(str, slen, first);
if (start_byte < 0)
start_byte = 0; // first index very negative: use zero
if (last == -1)
end_byte = (long)slen;
else
{
end_byte = char_idx2byte(str, slen, last);
if (end_byte >= 0 && end_byte < (long)slen)
// end index is inclusive
end_byte += MB_CPTR2LEN(str + end_byte);
}
if (start_byte >= (long)slen || end_byte <= start_byte)
return NULL;
return vim_strnsave(str + start_byte, end_byte - start_byte);
}
/*
* Handle:
* - expr[expr], expr[expr:expr] subscript
+269 -151
View File
@@ -47,6 +47,7 @@ static void f_ceil(typval_T *argvars, typval_T *rettv);
#endif
static void f_changenr(typval_T *argvars, typval_T *rettv);
static void f_char2nr(typval_T *argvars, typval_T *rettv);
static void f_charcol(typval_T *argvars, typval_T *rettv);
static void f_charidx(typval_T *argvars, typval_T *rettv);
static void f_col(typval_T *argvars, typval_T *rettv);
static void f_confirm(typval_T *argvars, typval_T *rettv);
@@ -87,12 +88,14 @@ static void f_function(typval_T *argvars, typval_T *rettv);
static void f_garbagecollect(typval_T *argvars, typval_T *rettv);
static void f_get(typval_T *argvars, typval_T *rettv);
static void f_getchangelist(typval_T *argvars, typval_T *rettv);
static void f_getcharpos(typval_T *argvars, typval_T *rettv);
static void f_getcharsearch(typval_T *argvars, typval_T *rettv);
static void f_getenv(typval_T *argvars, typval_T *rettv);
static void f_getfontname(typval_T *argvars, typval_T *rettv);
static void f_getjumplist(typval_T *argvars, typval_T *rettv);
static void f_getpid(typval_T *argvars, typval_T *rettv);
static void f_getcurpos(typval_T *argvars, typval_T *rettv);
static void f_getcursorcharpos(typval_T *argvars, typval_T *rettv);
static void f_getpos(typval_T *argvars, typval_T *rettv);
static void f_getreg(typval_T *argvars, typval_T *rettv);
static void f_getreginfo(typval_T *argvars, typval_T *rettv);
@@ -190,7 +193,9 @@ static void f_searchdecl(typval_T *argvars, typval_T *rettv);
static void f_searchpair(typval_T *argvars, typval_T *rettv);
static void f_searchpairpos(typval_T *argvars, typval_T *rettv);
static void f_searchpos(typval_T *argvars, typval_T *rettv);
static void f_setcharpos(typval_T *argvars, typval_T *rettv);
static void f_setcharsearch(typval_T *argvars, typval_T *rettv);
static void f_setcursorcharpos(typval_T *argvars, typval_T *rettv);
static void f_setenv(typval_T *argvars, typval_T *rettv);
static void f_setfperm(typval_T *argvars, typval_T *rettv);
static void f_setpos(typval_T *argvars, typval_T *rettv);
@@ -790,6 +795,8 @@ static funcentry_T global_functions[] =
ret_number, f_char2nr},
{"charclass", 1, 1, FEARG_1, NULL,
ret_number, f_charclass},
{"charcol", 1, 1, FEARG_1, NULL,
ret_number, f_charcol},
{"charidx", 2, 3, FEARG_1, NULL,
ret_number, f_charidx},
{"chdir", 1, 1, FEARG_1, NULL,
@@ -928,6 +935,8 @@ static funcentry_T global_functions[] =
ret_number, f_getchar},
{"getcharmod", 0, 0, 0, NULL,
ret_number, f_getcharmod},
{"getcharpos", 1, 1, FEARG_1, NULL,
ret_list_number, f_getcharpos},
{"getcharsearch", 0, 0, 0, NULL,
ret_dict_any, f_getcharsearch},
{"getcmdline", 0, 0, 0, NULL,
@@ -942,6 +951,8 @@ static funcentry_T global_functions[] =
ret_list_string, f_getcompletion},
{"getcurpos", 0, 1, FEARG_1, NULL,
ret_list_number, f_getcurpos},
{"getcursorcharpos", 0, 1, FEARG_1, NULL,
ret_list_number, f_getcursorcharpos},
{"getcwd", 0, 2, FEARG_1, NULL,
ret_string, f_getcwd},
{"getenv", 1, 1, FEARG_1, NULL,
@@ -1394,10 +1405,14 @@ static funcentry_T global_functions[] =
ret_void, f_setbufvar},
{"setcellwidths", 1, 1, FEARG_1, NULL,
ret_void, f_setcellwidths},
{"setcharpos", 2, 2, FEARG_2, NULL,
ret_number, f_setcharpos},
{"setcharsearch", 1, 1, FEARG_1, NULL,
ret_void, f_setcharsearch},
{"setcmdpos", 1, 1, FEARG_1, NULL,
ret_number, f_setcmdpos},
{"setcursorcharpos", 1, 3, FEARG_1, NULL,
ret_number, f_setcursorcharpos},
{"setenv", 2, 2, FEARG_2, NULL,
ret_void, f_setenv},
{"setfperm", 2, 2, FEARG_1, NULL,
@@ -1753,7 +1768,7 @@ static funcentry_T global_functions[] =
{"winrestview", 1, 1, FEARG_1, NULL,
ret_void, f_winrestview},
{"winsaveview", 0, 0, 0, NULL,
ret_dict_any, f_winsaveview},
ret_dict_number, f_winsaveview},
{"winwidth", 1, 1, FEARG_1, NULL,
ret_number, f_winwidth},
{"wordcount", 0, 0, 0, NULL,
@@ -1914,6 +1929,15 @@ internal_func_ret_type(int idx, int argcount, type_T **argtypes)
return global_functions[idx].f_retfunc(argcount, argtypes);
}
/*
* Return TRUE if "idx" is for the map() function.
*/
int
internal_func_is_map(int idx)
{
return global_functions[idx].f_func == f_map;
}
/*
* Check the argument count to use for internal function "idx".
* Returns -1 for failure, 0 if no method base accepted, 1 if method base is
@@ -2423,6 +2447,61 @@ f_char2nr(typval_T *argvars, typval_T *rettv)
rettv->vval.v_number = tv_get_string(&argvars[0])[0];
}
/*
* Get the current cursor column and store it in 'rettv'. If 'charcol' is TRUE,
* returns the character index of the column. Otherwise, returns the byte index
* of the column.
*/
static void
get_col(typval_T *argvars, typval_T *rettv, int charcol)
{
colnr_T col = 0;
pos_T *fp;
int fnum = curbuf->b_fnum;
fp = var2fpos(&argvars[0], FALSE, &fnum, charcol);
if (fp != NULL && fnum == curbuf->b_fnum)
{
if (fp->col == MAXCOL)
{
// '> can be MAXCOL, get the length of the line then
if (fp->lnum <= curbuf->b_ml.ml_line_count)
col = (colnr_T)STRLEN(ml_get(fp->lnum)) + 1;
else
col = MAXCOL;
}
else
{
col = fp->col + 1;
// col(".") when the cursor is on the NUL at the end of the line
// because of "coladd" can be seen as an extra column.
if (virtual_active() && fp == &curwin->w_cursor)
{
char_u *p = ml_get_cursor();
if (curwin->w_cursor.coladd >= (colnr_T)chartabsize(p,
curwin->w_virtcol - curwin->w_cursor.coladd))
{
int l;
if (*p != NUL && p[(l = (*mb_ptr2len)(p))] == NUL)
col += l;
}
}
}
}
rettv->vval.v_number = col;
}
/*
* "charcol()" function
*/
static void
f_charcol(typval_T *argvars, typval_T *rettv)
{
get_col(argvars, rettv, TRUE);
}
/*
* "charidx()" function
*/
@@ -2497,42 +2576,7 @@ get_optional_window(typval_T *argvars, int idx)
static void
f_col(typval_T *argvars, typval_T *rettv)
{
colnr_T col = 0;
pos_T *fp;
int fnum = curbuf->b_fnum;
fp = var2fpos(&argvars[0], FALSE, &fnum);
if (fp != NULL && fnum == curbuf->b_fnum)
{
if (fp->col == MAXCOL)
{
// '> can be MAXCOL, get the length of the line then
if (fp->lnum <= curbuf->b_ml.ml_line_count)
col = (colnr_T)STRLEN(ml_get(fp->lnum)) + 1;
else
col = MAXCOL;
}
else
{
col = fp->col + 1;
// col(".") when the cursor is on the NUL at the end of the line
// because of "coladd" can be seen as an extra column.
if (virtual_active() && fp == &curwin->w_cursor)
{
char_u *p = ml_get_cursor();
if (curwin->w_cursor.coladd >= (colnr_T)chartabsize(p,
curwin->w_virtcol - curwin->w_cursor.coladd))
{
int l;
if (*p != NUL && p[(l = (*mb_ptr2len)(p))] == NUL)
col += l;
}
}
}
}
rettv->vval.v_number = col;
get_col(argvars, rettv, FALSE);
}
/*
@@ -2633,26 +2677,24 @@ f_cosh(typval_T *argvars, typval_T *rettv)
#endif
/*
* "cursor(lnum, col)" function, or
* "cursor(list)"
*
* Moves the cursor to the specified line and column.
* Returns 0 when the position could be set, -1 otherwise.
* Set the cursor position.
* If 'charcol' is TRUE, then use the column number as a character offet.
* Otherwise use the column number as a byte offset.
*/
static void
f_cursor(typval_T *argvars, typval_T *rettv)
set_cursorpos(typval_T *argvars, typval_T *rettv, int charcol)
{
long line, col;
long coladd = 0;
int set_curswant = TRUE;
rettv->vval.v_number = -1;
if (argvars[1].v_type == VAR_UNKNOWN)
if (argvars[0].v_type == VAR_LIST)
{
pos_T pos;
colnr_T curswant = -1;
if (list2fpos(argvars, &pos, NULL, &curswant) == FAIL)
if (list2fpos(argvars, &pos, NULL, &curswant, charcol) == FAIL)
{
emsg(_(e_invarg));
return;
@@ -2666,15 +2708,24 @@ f_cursor(typval_T *argvars, typval_T *rettv)
set_curswant = FALSE;
}
}
else
else if ((argvars[0].v_type == VAR_NUMBER ||
argvars[0].v_type == VAR_STRING)
&& argvars[1].v_type == VAR_NUMBER)
{
line = tv_get_lnum(argvars);
if (line < 0)
semsg(_(e_invarg2), tv_get_string(&argvars[0]));
col = (long)tv_get_number_chk(&argvars[1], NULL);
if (charcol)
col = buf_charidx_to_byteidx(curbuf, line, col);
if (argvars[2].v_type != VAR_UNKNOWN)
coladd = (long)tv_get_number_chk(&argvars[2], NULL);
}
else
{
emsg(_(e_invarg));
return;
}
if (line < 0 || col < 0 || coladd < 0)
return; // type error; errmsg already given
if (line > 0)
@@ -2693,6 +2744,19 @@ f_cursor(typval_T *argvars, typval_T *rettv)
rettv->vval.v_number = 0;
}
/*
* "cursor(lnum, col)" function, or
* "cursor(list)"
*
* Moves the cursor to the specified line and column.
* Returns 0 when the position could be set, -1 otherwise.
*/
static void
f_cursor(typval_T *argvars, typval_T *rettv)
{
set_cursorpos(argvars, rettv, FALSE);
}
#ifdef MSWIN
/*
* "debugbreak()" function
@@ -3497,7 +3561,8 @@ common_function(typval_T *argvars, typval_T *rettv, int is_funcref)
{
name = s;
trans_name = trans_function_name(&name, &is_global, FALSE,
TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD | TFN_NO_DEREF, NULL, NULL);
TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD | TFN_NO_DEREF,
NULL, NULL, NULL);
if (*name != NUL)
s = NULL;
}
@@ -3886,6 +3951,88 @@ f_getchangelist(typval_T *argvars, typval_T *rettv)
#endif
}
static void
getpos_both(
typval_T *argvars,
typval_T *rettv,
int getcurpos,
int charcol)
{
pos_T *fp = NULL;
pos_T pos;
win_T *wp = curwin;
list_T *l;
int fnum = -1;
if (rettv_list_alloc(rettv) == OK)
{
l = rettv->vval.v_list;
if (getcurpos)
{
if (argvars[0].v_type != VAR_UNKNOWN)
{
wp = find_win_by_nr_or_id(&argvars[0]);
if (wp != NULL)
fp = &wp->w_cursor;
}
else
fp = &curwin->w_cursor;
if (fp != NULL && charcol)
{
pos = *fp;
pos.col = buf_byteidx_to_charidx(wp->w_buffer, pos.lnum,
pos.col) - 1;
fp = &pos;
}
}
else
fp = var2fpos(&argvars[0], TRUE, &fnum, charcol);
if (fnum != -1)
list_append_number(l, (varnumber_T)fnum);
else
list_append_number(l, (varnumber_T)0);
list_append_number(l, (fp != NULL) ? (varnumber_T)fp->lnum
: (varnumber_T)0);
list_append_number(l, (fp != NULL)
? (varnumber_T)(fp->col == MAXCOL ? MAXCOL : fp->col + 1)
: (varnumber_T)0);
list_append_number(l, (fp != NULL) ? (varnumber_T)fp->coladd :
(varnumber_T)0);
if (getcurpos)
{
int save_set_curswant = curwin->w_set_curswant;
colnr_T save_curswant = curwin->w_curswant;
colnr_T save_virtcol = curwin->w_virtcol;
if (wp == curwin)
update_curswant();
list_append_number(l, wp == NULL ? 0 : wp->w_curswant == MAXCOL
? (varnumber_T)MAXCOL : (varnumber_T)wp->w_curswant + 1);
// Do not change "curswant", as it is unexpected that a get
// function has a side effect.
if (wp == curwin && save_set_curswant)
{
curwin->w_set_curswant = save_set_curswant;
curwin->w_curswant = save_curswant;
curwin->w_virtcol = save_virtcol;
curwin->w_valid &= ~VALID_VIRTCOL;
}
}
}
else
rettv->vval.v_number = FALSE;
}
/*
* "getcharpos()" function
*/
static void
f_getcharpos(typval_T *argvars UNUSED, typval_T *rettv)
{
getpos_both(argvars, rettv, FALSE, TRUE);
}
/*
* "getcharsearch()" function
*/
@@ -4018,77 +4165,19 @@ f_getpid(typval_T *argvars UNUSED, typval_T *rettv)
rettv->vval.v_number = mch_get_pid();
}
static void
getpos_both(
typval_T *argvars,
typval_T *rettv,
int getcurpos)
{
pos_T *fp = NULL;
win_T *wp = curwin;
list_T *l;
int fnum = -1;
if (rettv_list_alloc(rettv) == OK)
{
l = rettv->vval.v_list;
if (getcurpos)
{
if (argvars[0].v_type != VAR_UNKNOWN)
{
wp = find_win_by_nr_or_id(&argvars[0]);
if (wp != NULL)
fp = &wp->w_cursor;
}
else
fp = &curwin->w_cursor;
}
else
fp = var2fpos(&argvars[0], TRUE, &fnum);
if (fnum != -1)
list_append_number(l, (varnumber_T)fnum);
else
list_append_number(l, (varnumber_T)0);
list_append_number(l, (fp != NULL) ? (varnumber_T)fp->lnum
: (varnumber_T)0);
list_append_number(l, (fp != NULL)
? (varnumber_T)(fp->col == MAXCOL ? MAXCOL : fp->col + 1)
: (varnumber_T)0);
list_append_number(l, (fp != NULL) ? (varnumber_T)fp->coladd :
(varnumber_T)0);
if (getcurpos)
{
int save_set_curswant = curwin->w_set_curswant;
colnr_T save_curswant = curwin->w_curswant;
colnr_T save_virtcol = curwin->w_virtcol;
if (wp == curwin)
update_curswant();
list_append_number(l, wp == NULL ? 0 : wp->w_curswant == MAXCOL
? (varnumber_T)MAXCOL : (varnumber_T)wp->w_curswant + 1);
// Do not change "curswant", as it is unexpected that a get
// function has a side effect.
if (wp == curwin && save_set_curswant)
{
curwin->w_set_curswant = save_set_curswant;
curwin->w_curswant = save_curswant;
curwin->w_virtcol = save_virtcol;
curwin->w_valid &= ~VALID_VIRTCOL;
}
}
}
else
rettv->vval.v_number = FALSE;
}
/*
* "getcurpos()" function
*/
static void
f_getcurpos(typval_T *argvars, typval_T *rettv)
{
getpos_both(argvars, rettv, TRUE);
getpos_both(argvars, rettv, TRUE, FALSE);
}
static void
f_getcursorcharpos(typval_T *argvars, typval_T *rettv)
{
getpos_both(argvars, rettv, TRUE, TRUE);
}
/*
@@ -4097,7 +4186,7 @@ f_getcurpos(typval_T *argvars, typval_T *rettv)
static void
f_getpos(typval_T *argvars, typval_T *rettv)
{
getpos_both(argvars, rettv, FALSE);
getpos_both(argvars, rettv, FALSE, FALSE);
}
/*
@@ -6217,14 +6306,14 @@ f_line(typval_T *argvars, typval_T *rettv)
== OK)
{
check_cursor();
fp = var2fpos(&argvars[0], TRUE, &fnum);
fp = var2fpos(&argvars[0], TRUE, &fnum, FALSE);
}
restore_win_noblock(save_curwin, save_curtab, TRUE);
}
}
else
// use current window
fp = var2fpos(&argvars[0], TRUE, &fnum);
fp = var2fpos(&argvars[0], TRUE, &fnum, FALSE);
if (fp != NULL)
lnum = fp->lnum;
@@ -8099,6 +8188,60 @@ f_searchpos(typval_T *argvars, typval_T *rettv)
list_append_number(rettv->vval.v_list, (varnumber_T)n);
}
/*
* Set the cursor or mark position.
* If 'charpos' is TRUE, then use the column number as a character offet.
* Otherwise use the column number as a byte offset.
*/
static void
set_position(typval_T *argvars, typval_T *rettv, int charpos)
{
pos_T pos;
int fnum;
char_u *name;
colnr_T curswant = -1;
rettv->vval.v_number = -1;
name = tv_get_string_chk(argvars);
if (name != NULL)
{
if (list2fpos(&argvars[1], &pos, &fnum, &curswant, charpos) == OK)
{
if (pos.col != MAXCOL && --pos.col < 0)
pos.col = 0;
if ((name[0] == '.' && name[1] == NUL))
{
// set cursor; "fnum" is ignored
curwin->w_cursor = pos;
if (curswant >= 0)
{
curwin->w_curswant = curswant - 1;
curwin->w_set_curswant = FALSE;
}
check_cursor();
rettv->vval.v_number = 0;
}
else if (name[0] == '\'' && name[1] != NUL && name[2] == NUL)
{
// set mark
if (setmark_pos(name[1], &pos, fnum) == OK)
rettv->vval.v_number = 0;
}
else
emsg(_(e_invarg));
}
}
}
/*
* "setcharpos()" function
*/
static void
f_setcharpos(typval_T *argvars, typval_T *rettv)
{
set_position(argvars, rettv, TRUE);
}
static void
f_setcharsearch(typval_T *argvars, typval_T *rettv UNUSED)
{
@@ -8140,6 +8283,15 @@ f_setcharsearch(typval_T *argvars, typval_T *rettv UNUSED)
}
}
/*
* "setcursorcharpos" function
*/
static void
f_setcursorcharpos(typval_T *argvars, typval_T *rettv UNUSED)
{
set_cursorpos(argvars, rettv, TRUE);
}
/*
* "setenv()" function
*/
@@ -8199,41 +8351,7 @@ f_setfperm(typval_T *argvars, typval_T *rettv)
static void
f_setpos(typval_T *argvars, typval_T *rettv)
{
pos_T pos;
int fnum;
char_u *name;
colnr_T curswant = -1;
rettv->vval.v_number = -1;
name = tv_get_string_chk(argvars);
if (name != NULL)
{
if (list2fpos(&argvars[1], &pos, &fnum, &curswant) == OK)
{
if (pos.col != MAXCOL && --pos.col < 0)
pos.col = 0;
if (name[0] == '.' && name[1] == NUL)
{
// set cursor; "fnum" is ignored
curwin->w_cursor = pos;
if (curswant >= 0)
{
curwin->w_curswant = curswant - 1;
curwin->w_set_curswant = FALSE;
}
check_cursor();
rettv->vval.v_number = 0;
}
else if (name[0] == '\'' && name[1] != NUL && name[2] == NUL)
{
// set mark
if (setmark_pos(name[1], &pos, fnum) == OK)
rettv->vval.v_number = 0;
}
else
emsg(_(e_invarg));
}
}
set_position(argvars, rettv, FALSE);
}
/*
@@ -9981,7 +10099,7 @@ f_virtcol(typval_T *argvars, typval_T *rettv)
int fnum = curbuf->b_fnum;
int len;
fp = var2fpos(&argvars[0], FALSE, &fnum);
fp = var2fpos(&argvars[0], FALSE, &fnum, FALSE);
if (fp != NULL && fp->lnum <= curbuf->b_ml.ml_line_count
&& fnum == curbuf->b_fnum)
{
+4 -6
View File
@@ -2641,8 +2641,7 @@ check_vars(char_u *name, int len)
* Find variable "name" in the list of variables.
* Return a pointer to it if found, NULL if not found.
* Careful: "a:0" variables don't have a name.
* When "htp" is not NULL we are writing to the variable, set "htp" to the
* hashtab_T used.
* When "htp" is not NULL set "htp" to the hashtab_T used.
*/
dictitem_T *
find_var(char_u *name, hashtab_T **htp, int no_autoload)
@@ -2656,12 +2655,12 @@ find_var(char_u *name, hashtab_T **htp, int no_autoload)
*htp = ht;
if (ht == NULL)
return NULL;
ret = find_var_in_ht(ht, *name, varname, no_autoload || htp != NULL);
ret = find_var_in_ht(ht, *name, varname, no_autoload);
if (ret != NULL)
return ret;
// Search in parent scope for lambda
ret = find_var_in_scoped_ht(name, no_autoload || htp != NULL);
ret = find_var_in_scoped_ht(name, no_autoload);
if (ret != NULL)
return ret;
@@ -2671,8 +2670,7 @@ find_var(char_u *name, hashtab_T **htp, int no_autoload)
ht = get_script_local_ht();
if (ht != NULL)
{
ret = find_var_in_ht(ht, *name, varname,
no_autoload || htp != NULL);
ret = find_var_in_ht(ht, *name, varname, no_autoload);
if (ret != NULL)
{
if (htp != NULL)
+1 -5
View File
@@ -1360,8 +1360,8 @@ do_shell(
#endif
#ifdef MSWIN
int winstart = FALSE;
int keep_termcap = FALSE;
#endif
int keep_termcap = !termcap_active;
/*
* Disallow shell commands for "rvim".
@@ -1395,9 +1395,7 @@ do_shell(
msg_putchar('\r'); // put cursor at start of line
if (!autocmd_busy)
{
#ifdef MSWIN
if (!keep_termcap)
#endif
stoptermcap();
}
#ifdef MSWIN
@@ -1488,9 +1486,7 @@ do_shell(
}
#endif // FEAT_GUI_MSWIN
#ifdef MSWIN
if (!keep_termcap) // if keep_termcap is TRUE didn't stop termcap
#endif
starttermcap(); // start termcap if not done by wait_return()
/*
+22
View File
@@ -2744,6 +2744,28 @@ parse_command_modifiers(
}
p = skip_range(eap->cmd, TRUE, NULL);
// In Vim9 script a variable can shadow a command modifier:
// verbose = 123
// verbose += 123
// silent! verbose = func()
// verbose.member = 2
// verbose[expr] = 2
// But not:
// verbose [a, b] = list
if (in_vim9script())
{
char_u *s, *n;
for (s = p; ASCII_ISALPHA(*s); ++s)
;
n = skipwhite(s);
if (vim_strchr((char_u *)".=", *n) != NULL
|| *s == '['
|| (*n != NUL && n[1] == '='))
break;
}
switch (*p)
{
// When adding an entry, also modify cmd_exists().
+2
View File
@@ -612,11 +612,13 @@ rb_check_type_stub(VALUE obj, int t)
{
dll_rb_check_type(obj, t);
}
# if VIM_SIZEOF_INT < VIM_SIZEOF_LONG // 64 bits only
unsigned long
rb_num2uint_stub(VALUE x)
{
return dll_rb_num2uint(x);
}
# endif
void
ruby_malloc_size_overflow_stub(size_t x, size_t y)
{
+8 -5
View File
@@ -924,7 +924,7 @@ list_slice_or_index(
if (!range)
{
if (verbose)
semsg(_(e_listidx), n1);
semsg(_(e_listidx), n1_arg);
return FAIL;
}
n1 = n1 < 0 ? 0 : len;
@@ -2188,10 +2188,13 @@ filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap)
int stride = l->lv_u.nonmat.lv_stride;
// List from range(): loop over the numbers
l->lv_first = NULL;
l->lv_u.mat.lv_last = NULL;
l->lv_len = 0;
l->lv_u.mat.lv_idx_item = NULL;
if (filtermap != FILTERMAP_MAPNEW)
{
l->lv_first = NULL;
l->lv_u.mat.lv_last = NULL;
l->lv_len = 0;
l->lv_u.mat.lv_idx_item = NULL;
}
for (idx = 0; idx < len; ++idx)
{
+1 -1
View File
@@ -4308,7 +4308,7 @@ mb_charlen(char_u *str)
return count;
}
#if defined(FEAT_SPELL) || defined(PROTO)
#if (defined(FEAT_SPELL) || defined(FEAT_EVAL)) || defined(PROTO)
/*
* Like mb_charlen() but for a string with specified length.
*/
+4 -4
View File
@@ -55,8 +55,10 @@ char_u *echo_string_core(typval_T *tv, char_u **tofree, char_u *numbuf, int copy
char_u *echo_string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID);
char_u *string_quote(char_u *str, int function);
int string2float(char_u *text, float_T *value);
pos_T *var2fpos(typval_T *varp, int dollar_lnum, int *fnum);
int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp);
int buf_byteidx_to_charidx(buf_T *buf, int lnum, int byteidx);
int buf_charidx_to_byteidx(buf_T *buf, int lnum, int charidx);
pos_T *var2fpos(typval_T *varp, int dollar_lnum, int *fnum, int charcol);
int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp, int char_col);
int get_env_len(char_u **arg);
int get_id_len(char_u **arg);
int get_name_len(char_u **arg, char_u **alias, int evaluate, int verbose);
@@ -64,8 +66,6 @@ char_u *find_name_end(char_u *arg, char_u **expr_start, char_u **expr_end, int f
int eval_isnamec(int c);
int eval_isnamec1(int c);
int eval_isdictc(int c);
char_u *char_from_string(char_u *str, varnumber_T index);
char_u *string_slice(char_u *str, varnumber_T first, varnumber_T last);
int handle_subscript(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int verbose);
int item_copy(typval_T *from, typval_T *to, int deep, int copyID);
void echo_one(typval_T *rettv, int with_space, int *atstart, int *needclr);
+1
View File
@@ -6,6 +6,7 @@ int has_internal_func(char_u *name);
char *internal_func_name(int idx);
int internal_func_check_arg_types(type_T **types, int idx, int argcount);
type_T *internal_func_ret_type(int idx, int argcount, type_T **argtypes);
int internal_func_is_map(int idx);
int check_internal_func(int idx, int argcount);
int call_internal_func(char_u *name, int argcount, typval_T *argvars, typval_T *rettv);
void call_internal_func_by_idx(int idx, typval_T *argvars, typval_T *rettv);
+1 -1
View File
@@ -18,7 +18,7 @@ char_u *tv_get_string_buf_chk(typval_T *varp, char_u *buf);
char_u *tv_stringify(typval_T *varp, char_u *buf);
int tv_check_lock(typval_T *tv, char_u *name, int use_gettext);
void copy_tv(typval_T *from, typval_T *to);
int typval_compare(typval_T *typ1, typval_T *typ2, exptype_T type, int ic);
int typval_compare(typval_T *typ1, typval_T *typ2, exprtype_T type, int ic);
char_u *typval_tostring(typval_T *arg);
int tv_islocked(typval_T *tv);
int tv_equal(typval_T *tv1, typval_T *tv2, int ic, int recursive);
+2 -2
View File
@@ -4,7 +4,7 @@ hashtab_T *func_tbl_get(void);
char_u *get_lambda_name(void);
char_u *register_cfunc(cfunc_T cb, cfunc_free_T cb_free, void *state);
int get_lambda_tv(char_u **arg, typval_T *rettv, int types_optional, evalarg_T *evalarg);
char_u *deref_func_name(char_u *name, int *lenp, partial_T **partialp, int no_autoload);
char_u *deref_func_name(char_u *name, int *lenp, partial_T **partialp, type_T **type, int no_autoload);
void emsg_funcname(char *ermsg, char_u *name);
int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, evalarg_T *evalarg, funcexe_T *funcexe);
char_u *fname_trans_sid(char_u *name, char_u *fname_buf, char_u **tofree, int *error);
@@ -31,7 +31,7 @@ int call_callback(callback_T *callback, int len, typval_T *rettv, int argcount,
void user_func_error(int error, char_u *name);
int call_func(char_u *funcname, int len, typval_T *rettv, int argcount_in, typval_T *argvars_in, funcexe_T *funcexe);
char_u *printable_func_name(ufunc_T *fp);
char_u *trans_function_name(char_u **pp, int *is_global, int skip, int flags, funcdict_T *fdp, partial_T **partial);
char_u *trans_function_name(char_u **pp, int *is_global, int skip, int flags, funcdict_T *fdp, partial_T **partial, type_T **type);
char_u *untrans_function_name(char_u *name);
void list_functions(regmatch_T *regmatch);
ufunc_T *define_function(exarg_T *eap, char_u *name_arg);
+2 -2
View File
@@ -1,6 +1,6 @@
/* vim9compile.c */
int check_defined(char_u *p, size_t len, cctx_T *cctx);
int check_compare_types(exptype_T type, typval_T *tv1, typval_T *tv2);
int check_compare_types(exprtype_T type, typval_T *tv1, typval_T *tv2);
int use_typecheck(type_T *actual, type_T *expected);
int get_script_item_idx(int sid, char_u *name, int check_writable, cctx_T *cctx);
imported_T *find_imported(char_u *name, size_t len, cctx_T *cctx);
@@ -9,7 +9,7 @@ char_u *peek_next_line_from_context(cctx_T *cctx);
char_u *next_line_from_context(cctx_T *cctx, int skip_comment);
char_u *to_name_end(char_u *arg, int use_namespace);
char_u *to_name_const_end(char_u *arg);
exptype_T get_compare_type(char_u *p, int *len, int *type_is);
exprtype_T get_compare_type(char_u *p, int *len, int *type_is);
void error_white_both(char_u *op, int len);
int assignment_len(char_u *p, int *heredoc);
void vim9_declare_error(char_u *name);
+2
View File
@@ -1,6 +1,8 @@
/* vim9execute.c */
void to_string_error(vartype_T vartype);
void funcstack_check_refcount(funcstack_T *funcstack);
char_u *char_from_string(char_u *str, varnumber_T index);
char_u *string_slice(char_u *str, varnumber_T first, varnumber_T last);
int fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx);
int call_def_function(ufunc_T *ufunc, int argc_arg, typval_T *argv, partial_T *partial, typval_T *rettv);
void ex_disassemble(exarg_T *eap);
+1
View File
@@ -16,6 +16,7 @@ void type_mismatch(type_T *expected, type_T *actual);
void arg_type_mismatch(type_T *expected, type_T *actual, int argidx);
int check_type(type_T *expected, type_T *actual, int give_msg, int argidx);
int check_arg_type(type_T *expected, type_T *actual, int argidx);
int check_argument_types(type_T *type, typval_T *argvars, int argcount, char_u *name);
char_u *skip_type(char_u *start, int optional);
type_T *parse_type(char_u **arg, garray_T *type_gap, int give_error);
int equal_type(type_T *type1, type_T *type2);
+15 -6
View File
@@ -1944,6 +1944,7 @@ typedef struct {
partial_T *partial; // for extra arguments
dict_T *selfdict; // Dictionary for "self"
typval_T *basetv; // base for base->method()
type_T *check_type; // type from funcref or NULL
} funcexe_T;
/*
@@ -1964,6 +1965,14 @@ typedef struct funcstack_S
int fs_copyID; // for garray_T collection
} funcstack_T;
typedef struct outer_S outer_T;
struct outer_S {
garray_T *out_stack; // stack from outer scope
int out_frame_idx; // index of stack frame in out_stack
outer_T *out_up; // outer scope of outer scope or NULL
int out_up_is_copy; // don't free out_up
};
struct partial_S
{
int pt_refcount; // reference count
@@ -1974,11 +1983,11 @@ struct partial_S
int pt_auto; // when TRUE the partial was created for using
// dict.member in handle_subscript()
// For a compiled closure: the arguments and local variables.
garray_T *pt_ectx_stack; // where to find local vars
int pt_ectx_frame; // index of function frame in uf_ectx_stack
funcstack_T *pt_funcstack; // copy of stack, used after context
// function returns
// For a compiled closure: the arguments and local variables scope
outer_T pt_outer;
funcstack_T *pt_funcstack; // copy of stack, used after context
// function returns
int pt_argc; // number of arguments
typval_T *pt_argv; // arguments in allocated array
@@ -4047,7 +4056,7 @@ typedef enum
EXPR_MULT, // *
EXPR_DIV, // /
EXPR_REM, // %
} exptype_T;
} exprtype_T;
/*
* Structure used for reading in json_decode().
+1 -1
View File
@@ -4201,7 +4201,7 @@ tagstack_push_items(win_T *wp, list_T *l)
// parse 'from' for the cursor position before the tag jump
if ((di = dict_find(itemdict, (char_u *)"from", -1)) == NULL)
continue;
if (list2fpos(&di->di_tv, &mark, &fnum, NULL) != OK)
if (list2fpos(&di->di_tv, &mark, &fnum, NULL, FALSE) != OK)
continue;
if ((tagname =
dict_get_string(itemdict, (char_u *)"tagname", TRUE)) == NULL)
+184 -1
View File
@@ -1,4 +1,4 @@
" Tests for cursor().
" Tests for cursor() and other functions that get/set the cursor position
func Test_wrong_arguments()
call assert_fails('call cursor(1. 3)', 'E474:')
@@ -123,4 +123,187 @@ func Test_screenpos_number()
bwipe!
endfunc
func SaveVisualStartCharPos()
call add(g:VisualStartPos, getcharpos('v'))
return ''
endfunc
" Test for the getcharpos() function
func Test_getcharpos()
call assert_fails('call getcharpos({})', 'E731:')
call assert_equal([0, 0, 0, 0], getcharpos(0))
new
call setline(1, ['', "01\tà4è678", 'Ⅵ', '012345678'])
" Test for '.' and '$'
normal 1G
call assert_equal([0, 1, 1, 0], getcharpos('.'))
call assert_equal([0, 4, 1, 0], getcharpos('$'))
normal 2G6l
call assert_equal([0, 2, 7, 0], getcharpos('.'))
normal 3G$
call assert_equal([0, 3, 1, 0], getcharpos('.'))
normal 4G$
call assert_equal([0, 4, 9, 0], getcharpos('.'))
" Test for a mark
normal 2G7lmmgg
call assert_equal([0, 2, 8, 0], getcharpos("'m"))
delmarks m
call assert_equal([0, 0, 0, 0], getcharpos("'m"))
" Test for the visual start column
vnoremap <expr> <F3> SaveVisualStartCharPos()
let g:VisualStartPos = []
exe "normal 2G6lv$\<F3>ohh\<F3>o\<F3>"
call assert_equal([[0, 2, 7, 0], [0, 2, 9, 0], [0, 2, 5, 0]], g:VisualStartPos)
call assert_equal([0, 2, 9, 0], getcharpos('v'))
let g:VisualStartPos = []
exe "normal 3Gv$\<F3>o\<F3>"
call assert_equal([[0, 3, 1, 0], [0, 3, 1, 0]], g:VisualStartPos)
let g:VisualStartPos = []
exe "normal 1Gv$\<F3>o\<F3>"
call assert_equal([[0, 1, 1, 0], [0, 1, 1, 0]], g:VisualStartPos)
vunmap <F3>
%bw!
endfunc
" Test for the setcharpos() function
func Test_setcharpos()
call assert_equal(-1, setcharpos('.', test_null_list()))
new
call setline(1, ['', "01\tà4è678", 'Ⅵ', '012345678'])
call setcharpos('.', [0, 1, 1, 0])
call assert_equal([1, 1], [line('.'), col('.')])
call setcharpos('.', [0, 2, 7, 0])
call assert_equal([2, 9], [line('.'), col('.')])
call setcharpos('.', [0, 3, 4, 0])
call assert_equal([3, 1], [line('.'), col('.')])
call setcharpos('.', [0, 3, 1, 0])
call assert_equal([3, 1], [line('.'), col('.')])
call setcharpos('.', [0, 4, 0, 0])
call assert_equal([4, 1], [line('.'), col('.')])
call setcharpos('.', [0, 4, 20, 0])
call assert_equal([4, 9], [line('.'), col('.')])
" Test for mark
delmarks m
call setcharpos("'m", [0, 2, 9, 0])
normal `m
call assert_equal([2, 11], [line('.'), col('.')])
%bw!
call assert_equal(-1, setcharpos('.', [10, 3, 1, 0]))
endfunc
func SaveVisualStartCharCol()
call add(g:VisualStartCol, charcol('v'))
return ''
endfunc
" Test for the charcol() function
func Test_charcol()
call assert_fails('call charcol({})', 'E731:')
call assert_equal(0, charcol(0))
new
call setline(1, ['', "01\tà4è678", 'Ⅵ', '012345678'])
" Test for '.' and '$'
normal 1G
call assert_equal(1, charcol('.'))
call assert_equal(1, charcol('$'))
normal 2G6l
call assert_equal(7, charcol('.'))
call assert_equal(10, charcol('$'))
normal 3G$
call assert_equal(1, charcol('.'))
call assert_equal(2, charcol('$'))
normal 4G$
call assert_equal(9, charcol('.'))
call assert_equal(10, charcol('$'))
" Test for [lnum, '$']
call assert_equal(1, charcol([1, '$']))
call assert_equal(10, charcol([2, '$']))
call assert_equal(2, charcol([3, '$']))
call assert_equal(0, charcol([5, '$']))
" Test for a mark
normal 2G7lmmgg
call assert_equal(8, charcol("'m"))
delmarks m
call assert_equal(0, charcol("'m"))
" Test for the visual start column
vnoremap <expr> <F3> SaveVisualStartCharCol()
let g:VisualStartCol = []
exe "normal 2G6lv$\<F3>ohh\<F3>o\<F3>"
call assert_equal([7, 9, 5], g:VisualStartCol)
call assert_equal(9, charcol('v'))
let g:VisualStartCol = []
exe "normal 3Gv$\<F3>o\<F3>"
call assert_equal([1, 1], g:VisualStartCol)
let g:VisualStartCol = []
exe "normal 1Gv$\<F3>o\<F3>"
call assert_equal([1, 1], g:VisualStartCol)
vunmap <F3>
%bw!
endfunc
" Test for getcursorcharpos()
func Test_getcursorcharpos()
call assert_equal(getcursorcharpos(), getcursorcharpos(0))
call assert_equal([0, 0, 0, 0, 0], getcursorcharpos(-1))
call assert_equal([0, 0, 0, 0, 0], getcursorcharpos(1999))
new
call setline(1, ['', "01\tà4è678", 'Ⅵ', '012345678'])
normal 1G9l
call assert_equal([0, 1, 1, 0, 1], getcursorcharpos())
normal 2G9l
call assert_equal([0, 2, 9, 0, 14], getcursorcharpos())
normal 3G9l
call assert_equal([0, 3, 1, 0, 1], getcursorcharpos())
normal 4G9l
call assert_equal([0, 4, 9, 0, 9], getcursorcharpos())
let winid = win_getid()
normal 2G5l
wincmd w
call assert_equal([0, 2, 6, 0, 11], getcursorcharpos(winid))
%bw!
endfunc
" Test for setcursorcharpos()
func Test_setcursorcharpos()
call assert_fails('call setcursorcharpos(test_null_list())', 'E474:')
call assert_fails('call setcursorcharpos([1])', 'E474:')
call assert_fails('call setcursorcharpos([1, 1, 1, 1, 1])', 'E474:')
new
call setline(1, ['', "01\tà4è678", 'Ⅵ', '012345678'])
normal G
call setcursorcharpos([1, 1])
call assert_equal([1, 1], [line('.'), col('.')])
call setcursorcharpos([2, 7, 0])
call assert_equal([2, 9], [line('.'), col('.')])
call setcursorcharpos(3, 4)
call assert_equal([3, 1], [line('.'), col('.')])
call setcursorcharpos([3, 1])
call assert_equal([3, 1], [line('.'), col('.')])
call setcursorcharpos([4, 0, 0, 0])
call assert_equal([4, 1], [line('.'), col('.')])
call setcursorcharpos([4, 20])
call assert_equal([4, 9], [line('.'), col('.')])
normal 1G
call setcursorcharpos([100, 100, 100, 100])
call assert_equal([4, 9], [line('.'), col('.')])
normal 1G
call setcursorcharpos('$', 1)
call assert_equal([4, 1], [line('.'), col('.')])
%bw!
endfunc
" vim: shiftwidth=2 sts=2 expandtab
+2
View File
@@ -218,6 +218,7 @@ func Test_vvar_scriptversion4()
call assert_equal(15, 0o17)
call assert_equal(15, 0O17)
call assert_equal(18, 018)
call assert_equal(511, 0o777)
call assert_equal(64, 0b1'00'00'00)
call assert_equal(1048576, 0x10'00'00)
call assert_equal(32768, 0o10'00'00)
@@ -233,6 +234,7 @@ func Test_vvar_scriptversion1()
call assert_equal(15, 0o17)
call assert_equal(15, 0O17)
call assert_equal(18, 018)
call assert_equal(511, 0o777)
endfunc
func Test_scriptversion_fail()
+34
View File
@@ -1091,6 +1091,13 @@ def Test_assign_lambda()
assert_equal(123, FuncRef_Any())
END
CheckScriptSuccess(lines)
lines =<< trim END
var Ref: func(number)
Ref = (j) => !j
END
CheckDefFailure(lines, 'E1012: Type mismatch; expected func(number) but got func(any): bool')
CheckScriptFailure(['vim9script'] + lines, 'E1012: Type mismatch; expected func(number) but got func(any): any')
enddef
def Test_heredoc()
@@ -1457,5 +1464,32 @@ def Test_unlet()
assert_equal('', $ENVVAR)
enddef
def Test_assign_command_modifier()
var lines =<< trim END
var verbose = 0
verbose = 1
assert_equal(1, verbose)
silent verbose = 2
assert_equal(2, verbose)
silent verbose += 2
assert_equal(4, verbose)
silent verbose -= 1
assert_equal(3, verbose)
var topleft = {one: 1}
sandbox topleft.one = 3
assert_equal({one: 3}, topleft)
leftabove topleft[' '] = 4
assert_equal({one: 3, ' ': 4}, topleft)
var x: number
var y: number
silent [x, y] = [1, 2]
assert_equal(1, x)
assert_equal(2, y)
END
CheckDefAndScriptSuccess(lines)
enddef
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
+21 -2
View File
@@ -231,7 +231,7 @@ def Test_extend_arg_types()
assert_equal({a: 1, b: 2}, extend({a: 1, b: 2}, {b: 4}, s:string_keep))
var res: list<dict<any>>
extend(res, map([1, 2], (_, v) => ({})))
extend(res, mapnew([1, 2], (_, v) => ({})))
assert_equal([{}, {}], res)
CheckDefFailure(['extend([1, 2], 3)'], 'E1013: Argument 2: type mismatch, expected list<number> but got number')
@@ -320,6 +320,15 @@ def Test_map_function_arg()
CheckDefAndScriptSuccess(lines)
enddef
def Test_map_item_type()
var lines =<< trim END
var l = ['a', 'b', 'c']
map(l, (k, v) => k .. '/' .. v )
assert_equal(['0/a', '1/b', '2/c'], l)
END
CheckDefAndScriptSuccess(lines)
enddef
def Test_filereadable()
assert_false(filereadable(""))
assert_false(filereadable(test_null_string()))
@@ -728,7 +737,7 @@ enddef
def Test_submatch()
var pat = 'A\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)'
var Rep = () => range(10)->map((_, v) => submatch(v, true))->string()
var Rep = () => range(10)->mapnew((_, v) => submatch(v, true))->string()
var actual = substitute('A123456789', pat, Rep, '')
var expected = "[['A123456789'], ['1'], ['2'], ['3'], ['4'], ['5'], ['6'], ['7'], ['8'], ['9']]"
actual->assert_equal(expected)
@@ -786,6 +795,16 @@ def Test_winrestcmd()
close
enddef
def Test_winsaveview()
var view: dict<number> = winsaveview()
var lines =<< trim END
var view: list<number> = winsaveview()
END
CheckDefAndScriptFailure(lines, 'E1012: Type mismatch; expected list<number> but got dict<number>', 1)
enddef
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
+6
View File
@@ -661,6 +661,12 @@ def Test_range_after_command_modifier()
bwipe!
enddef
def Test_silent_pattern()
new
silent! :/pat/put _
bwipe!
enddef
def Test_eval_command()
var from = 3
var to = 5
+27 -51
View File
@@ -117,8 +117,7 @@ def Test_disassemble_exec_expr()
'\d 2STRING stack\[-1\]\_s*' ..
'\d\+ PUSHS ".txt"\_s*' ..
'\d\+ EXECCONCAT 4\_s*' ..
'\d\+ PUSHNR 0\_s*' ..
'\d\+ RETURN',
'\d\+ RETURN 0',
res)
enddef
@@ -134,8 +133,7 @@ def Test_disassemble_yank_range()
'\d EXEC norm! m\[jjm\]\_s*' ..
' :''\[,''\]yank\_s*' ..
'\d EXEC :''\[,''\]yank\_s*' ..
'\d PUSHNR 0\_s*' ..
'\d RETURN',
'\d RETURN 0',
res)
enddef
@@ -149,8 +147,7 @@ def Test_disassemble_put_expr()
' :3put ="text"\_s*' ..
'\d PUSHS "text"\_s*' ..
'\d PUT = 3\_s*' ..
'\d PUSHNR 0\_s*' ..
'\d RETURN',
'\d RETURN 0',
res)
enddef
@@ -164,8 +161,7 @@ def Test_disassemble_put_range()
' :$-2put a\_s*' ..
'\d RANGE $-2\_s*' ..
'\d PUT a range\_s*' ..
'\d PUSHNR 0\_s*' ..
'\d RETURN',
'\d RETURN 0',
res)
enddef
@@ -273,8 +269,7 @@ def Test_disassemble_store_member()
'\d\+ PUSHS "a"\_s*' ..
'\d\+ LOAD $1\_s*' ..
'\d\+ STOREDICT\_s*' ..
'\d\+ PUSHNR 0\_s*' ..
'\d\+ RETURN',
'\d\+ RETURN 0',
res)
enddef
@@ -297,8 +292,7 @@ def Test_disassemble_store_index()
'\d LOAD $0\_s*' ..
'\d MEMBER dd\_s*' ..
'\d STOREINDEX\_s*' ..
'\d\+ PUSHNR 0\_s*' ..
'\d\+ RETURN',
'\d\+ RETURN 0',
res)
enddef
@@ -333,8 +327,7 @@ def Test_disassemble_list_assign()
'\d\+ STORE $1\_s*' ..
'\d\+ SLICE 2\_s*' ..
'\d\+ STORE $2\_s*' ..
'\d\+ PUSHNR 0\_s*' ..
'\d\+ RETURN',
'\d\+ RETURN 0',
res)
enddef
@@ -362,8 +355,7 @@ def Test_disassemble_list_add()
'\d\+ CHECKTYPE number stack\[-1\]\_s*' ..
'\d\+ LISTAPPEND\_s*' ..
'\d\+ DROP\_s*' ..
'\d\+ PUSHNR 0\_s*' ..
'\d\+ RETURN',
'\d\+ RETURN 0',
res)
enddef
@@ -390,8 +382,7 @@ def Test_disassemble_blob_add()
'\d\+ CHECKTYPE number stack\[-1\]\_s*' ..
'\d\+ BLOBAPPEND\_s*' ..
'\d\+ DROP\_s*' ..
'\d\+ PUSHNR 0\_s*' ..
'\d\+ RETURN',
'\d\+ RETURN 0',
res)
enddef
@@ -576,18 +567,17 @@ def Test_disassemble_closure()
var res = execute('disass g:Append')
assert_match('<lambda>\d\_s*' ..
'local ..= arg\_s*' ..
'\d LOADOUTER $0\_s*' ..
'\d LOADOUTER level 1 $0\_s*' ..
'\d LOAD arg\[-1\]\_s*' ..
'\d CONCAT\_s*' ..
'\d STOREOUTER $0\_s*' ..
'\d PUSHNR 0\_s*' ..
'\d RETURN',
'\d STOREOUTER level 1 $0\_s*' ..
'\d RETURN 0',
res)
res = execute('disass g:Get')
assert_match('<lambda>\d\_s*' ..
'return local\_s*' ..
'\d LOADOUTER $0\_s*' ..
'\d LOADOUTER level 1 $0\_s*' ..
'\d RETURN',
res)
@@ -615,8 +605,7 @@ def Test_disassemble_pcall()
'\d PCALL top (argc 1)\_s*' ..
'\d PCALL end\_s*' ..
'\d DROP\_s*' ..
'\d PUSHNR 0\_s*' ..
'\d RETURN',
'\d RETURN 0',
res)
enddef
@@ -865,8 +854,7 @@ def Test_disassemble_function()
'\d PUSHS "UserFunc"\_s*' ..
'\d BCALL funcref(argc 1)\_s*' ..
'\d STORE $2\_s*' ..
'\d PUSHNR 0\_s*' ..
'\d RETURN',
'\d RETURN 0',
instr)
enddef
@@ -893,8 +881,7 @@ def Test_disassemble_channel()
'var chan1: channel\_s*' ..
'\d PUSHCHANNEL 0\_s*' ..
'\d STORE $2\_s*' ..
'\d PUSHNR 0\_s*' ..
'\d RETURN',
'\d RETURN 0',
instr)
enddef
@@ -966,8 +953,7 @@ def Test_nested_func()
'echomsg "inner"\_s*' ..
'enddef\_s*' ..
'\d NEWFUNC <lambda>\d\+ Inner\_s*' ..
'\d PUSHNR 0\_s*' ..
'\d RETURN',
'\d RETURN 0',
instr)
enddef
@@ -989,8 +975,7 @@ def Test_nested_def_list()
'\d DEF /Info\_s*' ..
'def /Info/\_s*' ..
'\d DEF /Info/\_s*' ..
'\d PUSHNR 0\_s*' ..
'\d RETURN',
'\d RETURN 0',
instr)
enddef
@@ -1122,8 +1107,7 @@ def Test_disassemble_for_loop_unpack()
'endfor\_s*' ..
'\d\+ JUMP -> 8\_s*' ..
'\d\+ DROP\_s*' ..
'\d\+ PUSHNR 0\_s*' ..
'\d\+ RETURN',
'\d\+ RETURN 0',
instr)
enddef
@@ -1143,8 +1127,7 @@ def Test_disassemble_typecast()
'\d NEWLIST size 2\_s*' ..
'\d SETTYPE list<number>\_s*' ..
'\d STORE $0\_s*' ..
'\d PUSHNR 0\_s*' ..
'\d RETURN\_s*',
'\d RETURN 0\_s*',
instr)
enddef
@@ -1631,8 +1614,7 @@ def Test_dsassemble_falsy_op()
'echo "" ?? "empty string"\_s*' ..
'\d\+ PUSHS "empty string"\_s*' ..
'\d\+ ECHO 1\_s*' ..
'\d\+ PUSHNR 0\_s*' ..
'\d\+ RETURN',
'\d\+ RETURN 0',
res)
enddef
@@ -1659,8 +1641,7 @@ def Test_disassemble_compare_const()
'if ' .. substitute(case[0], '[[~]', '\\\0', 'g') .. '.*' ..
'\d PUSHNR 42.*' ..
'\d ECHO 1.*' ..
'\d PUSHNR 0.*' ..
'\d RETURN.*',
'\d RETURN 0',
instr)
else
# condition false, function just returns
@@ -1668,8 +1649,7 @@ def Test_disassemble_compare_const()
'if ' .. substitute(case[0], '[[~]', '\\\0', 'g') .. '[ \n]*' ..
'echo 42[ \n]*' ..
'endif[ \n]*' ..
'\s*\d PUSHNR 0.*' ..
'\d RETURN.*',
'\d RETURN 0',
instr)
endif
@@ -1707,8 +1687,7 @@ def Test_disassemble_execute()
'\d\+ LOAD $1\_s*' ..
'\d\+ CONCAT\_s*' ..
'\d\+ EXECUTE 1\_s*' ..
'\d\+ PUSHNR 0\_s*' ..
'\d\+ RETURN',
'\d\+ RETURN 0',
res)
enddef
@@ -1727,8 +1706,7 @@ def Test_disassemble_echomsg()
"echoerr 'went' .. 'wrong'\\_s*" ..
'\d PUSHS "wentwrong"\_s*' ..
'\d ECHOERR 1\_s*' ..
'\d PUSHNR 0\_s*' ..
'\d RETURN',
'\d RETURN 0',
res)
enddef
@@ -1837,8 +1815,7 @@ def Test_shuffle()
'\d SHUFFLE 2 up 1\_s*' ..
'\d BCALL append(argc 2)\_s*' ..
'\d DROP\_s*' ..
'\d PUSHNR 0\_s*' ..
'\d RETURN',
'\d RETURN 0',
res)
enddef
@@ -1861,8 +1838,7 @@ def Test_silent()
'\d PUSHS "error"\_s*' ..
'\d ECHOERR 1\_s*' ..
'\d CMDMOD_REV\_s*' ..
'\d PUSHNR 0\_s*' ..
'\d RETURN',
'\d RETURN 0',
res)
enddef
+54 -16
View File
@@ -597,6 +597,18 @@ def Test_expr4_equal()
CheckDefFailure(["var x = 'a' == "], 'E1097:', 3)
CheckDefExecFailure(['var items: any', 'eval 1', 'eval 2', 'if items == []', 'endif'], 'E691:', 4)
CheckDefExecFailure(['var x: any = "a"', 'echo x == true'], 'E1072: Cannot compare string with bool', 2)
CheckDefExecFailure(["var x: any = true", 'echo x == ""'], 'E1072: Cannot compare bool with string', 2)
CheckDefExecFailure(["var x: any = 99", 'echo x == true'], 'E1138', 2)
CheckDefExecFailure(["var x: any = 'a'", 'echo x == 99'], 'E1030:', 2)
for op in ['>', '>=', '<', '<=', '=~', '!~']
CheckDefExecFailure([
"var a: any = 'a'",
'var b: any = true',
'echo a ' .. op .. ' b'], 'E1072:', 3)
endfor
enddef
" test != comperator
@@ -1847,10 +1859,10 @@ def Test_expr7_lambda()
# line continuation inside lambda with "cond ? expr : expr" works
var ll = range(3)
map(ll, (k, v) => v % 2 ? {
var dll = mapnew(ll, (k, v) => v % 2 ? {
['111']: 111 } : {}
)
assert_equal([{}, {111: 111}, {}], ll)
assert_equal([{}, {111: 111}, {}], dll)
ll = range(3)
map(ll, (k, v) => v == 8 || v
@@ -1934,10 +1946,10 @@ def Test_expr7_new_lambda()
# line continuation inside lambda with "cond ? expr : expr" works
var ll = range(3)
map(ll, (k, v) => v % 2 ? {
var dll = mapnew(ll, (k, v) => v % 2 ? {
['111']: 111 } : {}
)
assert_equal([{}, {111: 111}, {}], ll)
assert_equal([{}, {111: 111}, {}], dll)
ll = range(3)
map(ll, (k, v) => v == 8 || v
@@ -2304,7 +2316,7 @@ def Test_expr7_any_index_slice()
# string is permissive, index out of range accepted
g:teststring = 'abcdef'
assert_equal('b', g:teststring[1])
assert_equal('', g:teststring[-1])
assert_equal('f', g:teststring[-1])
assert_equal('', g:teststring[99])
assert_equal('b', g:teststring[1 : 1])
@@ -2368,10 +2380,10 @@ def Test_expr7_any_index_slice()
CheckDefExecFailure(['echo g:testblob[-3]'], 'E979:', 1)
CheckScriptFailure(['vim9script', 'echo g:testblob[-3]'], 'E979:', 2)
CheckDefExecFailure(['echo g:testlist[4]'], 'E684:', 1)
CheckDefExecFailure(['echo g:testlist[4]'], 'E684: list index out of range: 4', 1)
CheckScriptFailure(['vim9script', 'echo g:testlist[4]'], 'E684:', 2)
CheckDefExecFailure(['echo g:testlist[-5]'], 'E684:', 1)
CheckScriptFailure(['vim9script', 'echo g:testlist[-5]'], 'E684:', 2)
CheckScriptFailure(['vim9script', 'echo g:testlist[-5]'], 'E684: list index out of range: -5', 2)
CheckDefExecFailure(['echo g:testdict["a" : "b"]'], 'E719:', 1)
CheckScriptFailure(['vim9script', 'echo g:testdict["a" : "b"]'], 'E719:', 2)
@@ -2802,15 +2814,23 @@ enddef
def Test_expr7_string_subscript()
var lines =<< trim END
var text = 'abcdef'
assert_equal('', text[-1])
assert_equal('f', text[-1])
assert_equal('a', text[0])
assert_equal('e', text[4])
assert_equal('f', text[5])
assert_equal('', text[6])
text = 'ábçdë'
assert_equal('ë', text[-1])
assert_equal('d', text[-2])
assert_equal('ç', text[-3])
assert_equal('b', text[-4])
assert_equal('á', text[-5])
assert_equal('', text[-6])
text = 'ábçdëf'
assert_equal('', text[-999])
assert_equal('', text[-1])
assert_equal('f', text[-1])
assert_equal('á', text[0])
assert_equal('b', text[1])
assert_equal('ç', text[2])
@@ -2904,12 +2924,21 @@ def Test_expr7_list_subscript()
assert_equal([], list[0 : -6])
assert_equal([], list[0 : -99])
END
CheckDefSuccess(lines)
CheckScriptSuccess(['vim9script'] + lines)
CheckDefAndScriptSuccess(lines)
lines = ['var l = [0, 1, 2]', 'echo l[g:astring : g:theone]']
CheckDefExecFailure(lines, 'E1012:')
CheckScriptFailure(['vim9script'] + lines, 'E1030:', 3)
lines =<< trim END
vim9script
var ld = []
def Func()
eval ld[0].key
enddef
defcompile
END
CheckScriptSuccess(lines)
enddef
def Test_expr7_dict_subscript()
@@ -2918,6 +2947,15 @@ def Test_expr7_dict_subscript()
var l = [{lnum: 2}, {lnum: 1}]
var res = l[0].lnum > l[1].lnum
assert_true(res)
var dd = {}
def Func1()
eval dd.key1.key2
enddef
def Func2()
eval dd['key1'].key2
enddef
defcompile
END
CheckScriptSuccess(lines)
enddef
@@ -2926,25 +2964,25 @@ def Test_expr7_subscript_linebreak()
var range = range(
3)
var l = range
->map('string(v:key)')
->mapnew('string(v:key)')
assert_equal(['0', '1', '2'], l)
l = range
->map('string(v:key)')
->mapnew('string(v:key)')
assert_equal(['0', '1', '2'], l)
l = range # comment
->map('string(v:key)')
->mapnew('string(v:key)')
assert_equal(['0', '1', '2'], l)
l = range
->map('string(v:key)')
->mapnew('string(v:key)')
assert_equal(['0', '1', '2'], l)
l = range
# comment
->map('string(v:key)')
->mapnew('string(v:key)')
assert_equal(['0', '1', '2'], l)
assert_equal('1', l[
+55 -4
View File
@@ -116,6 +116,14 @@ def Test_missing_endfunc_enddef()
CheckScriptFailure(lines, 'E126:', 2)
enddef
def Test_enddef_dict_key()
var d = {
enddef: 'x',
endfunc: 'y',
}
assert_equal({enddef: 'x', endfunc: 'y'}, d)
enddef
def ReturnString(): string
return 'string'
enddef
@@ -579,6 +587,22 @@ def Test_call_funcref_wrong_args()
CheckScriptFailure(head + ["funcMap['func']('str', 123)"] + tail, 'E119:')
CheckScriptFailure(head + ["funcMap['func']('str', 123, [1], 4)"] + tail, 'E118:')
var lines =<< trim END
vim9script
var Ref: func(number): any
Ref = (j) => !j
echo Ref(false)
END
CheckScriptFailure(lines, 'E1013: Argument 1: type mismatch, expected number but got bool', 4)
lines =<< trim END
vim9script
var Ref: func(number): any
Ref = (j) => !j
call Ref(false)
END
CheckScriptFailure(lines, 'E1013: Argument 1: type mismatch, expected number but got bool', 4)
enddef
def Test_call_lambda_args()
@@ -1492,7 +1516,7 @@ def Test_unknown_function()
'delfunc g:NotExist'], 'E700:')
enddef
def RefFunc(Ref: func(string): string): string
def RefFunc(Ref: func(any): any): string
return Ref('more')
enddef
@@ -1739,7 +1763,7 @@ enddef
def Shadowed(): list<number>
var FuncList: list<func: number> = [() => 42]
return FuncList->map((_, Shadowed) => Shadowed())
return FuncList->mapnew((_, Shadowed) => Shadowed())
enddef
def Test_lambda_arg_shadows_func()
@@ -1768,7 +1792,7 @@ enddef
def Line_continuation_in_lambda(): list<string>
var x = range(97, 100)
->map((_, v) => nr2char(v)
->mapnew((_, v) => nr2char(v)
->toupper())
->reverse()
return x
@@ -1778,6 +1802,33 @@ def Test_line_continuation_in_lambda()
Line_continuation_in_lambda()->assert_equal(['D', 'C', 'B', 'A'])
enddef
def Test_list_lambda()
timer_start(1000, (_) => 0)
var body = execute(timer_info()[0].callback
->string()
->substitute("('", ' ', '')
->substitute("')", '', '')
->substitute('function\zs', ' ', ''))
assert_match('def <lambda>\d\+(_: any, ...): number\n1 return 0\n enddef', body)
enddef
def DoFilterThis(a: string): list<string>
# closure nested inside another closure using argument
var Filter = (l) => filter(l, (_, v) => stridx(v, a) == 0)
return ['x', 'y', 'a', 'x2', 'c']->Filter()
enddef
def Test_nested_closure_using_argument()
assert_equal(['x', 'x2'], DoFilterThis('x'))
enddef
def Test_triple_nested_closure()
var what = 'x'
var Match = (val: string, cmp: string): bool => stridx(val, cmp) == 0
var Filter = (l) => filter(l, (_, v) => Match(v, what))
assert_equal(['x', 'x2'], ['x', 'y', 'a', 'x2', 'c']->Filter())
enddef
func Test_silent_echo()
CheckScreendump
@@ -1857,7 +1908,7 @@ def Test_recursive_call()
enddef
def TreeWalk(dir: string): list<any>
return readdir(dir)->map((_, val) =>
return readdir(dir)->mapnew((_, val) =>
fnamemodify(dir .. '/' .. val, ':p')->isdirectory()
? {[val]: TreeWalk(dir .. '/' .. val)}
: val
+26 -2
View File
@@ -649,7 +649,7 @@ copy_tv(typval_T *from, typval_T *to)
typval_compare(
typval_T *typ1, // first operand
typval_T *typ2, // second operand
exptype_T type, // operator
exprtype_T type, // operator
int ic) // ignore case
{
int i;
@@ -834,6 +834,30 @@ typval_compare(
default: break; // avoid gcc warning
}
}
else if (in_vim9script() && (typ1->v_type == VAR_BOOL
|| typ2->v_type == VAR_BOOL))
{
if (typ1->v_type != typ2->v_type)
{
semsg(_(e_cannot_compare_str_with_str),
vartype_name(typ1->v_type), vartype_name(typ2->v_type));
clear_tv(typ1);
return FAIL;
}
n1 = typ1->vval.v_number;
n2 = typ2->vval.v_number;
switch (type)
{
case EXPR_IS:
case EXPR_EQUAL: n1 = (n1 == n2); break;
case EXPR_ISNOT:
case EXPR_NEQUAL: n1 = (n1 != n2); break;
default:
emsg(_(e_invalid_operation_for_bool));
clear_tv(typ1);
return FAIL;
}
}
else
{
s1 = tv_get_string_buf(typ1, buf1);
@@ -1555,7 +1579,7 @@ tv_get_lnum(typval_T *argvars)
if (lnum <= 0) // no valid number, try using arg like line()
{
int fnum;
pos_T *fp = var2fpos(&argvars[0], TRUE, &fnum);
pos_T *fp = var2fpos(&argvars[0], TRUE, &fnum, FALSE);
if (fp != NULL)
lnum = fp->lnum;
+85 -40
View File
@@ -668,7 +668,7 @@ get_lambda_tv(
goto errret;
}
else
fp->uf_ret_type = &t_unknown;
fp->uf_ret_type = &t_any;
}
fp->uf_lines = newlines;
@@ -727,47 +727,68 @@ errret:
* name it contains, otherwise return "name".
* If "partialp" is not NULL, and "name" is of type VAR_PARTIAL also set
* "partialp".
* If "type" is not NULL and a Vim9 script-local variable is found look up the
* type of the variable.
*/
char_u *
deref_func_name(char_u *name, int *lenp, partial_T **partialp, int no_autoload)
deref_func_name(
char_u *name,
int *lenp,
partial_T **partialp,
type_T **type,
int no_autoload)
{
dictitem_T *v;
int cc;
char_u *s;
char_u *s = NULL;
hashtab_T *ht;
if (partialp != NULL)
*partialp = NULL;
cc = name[*lenp];
name[*lenp] = NUL;
v = find_var(name, NULL, no_autoload);
v = find_var(name, &ht, no_autoload);
name[*lenp] = cc;
if (v != NULL && v->di_tv.v_type == VAR_FUNC)
if (v != NULL)
{
if (v->di_tv.vval.v_string == NULL)
if (v->di_tv.v_type == VAR_FUNC)
{
*lenp = 0;
return (char_u *)""; // just in case
if (v->di_tv.vval.v_string == NULL)
{
*lenp = 0;
return (char_u *)""; // just in case
}
s = v->di_tv.vval.v_string;
*lenp = (int)STRLEN(s);
}
s = v->di_tv.vval.v_string;
*lenp = (int)STRLEN(s);
return s;
}
if (v != NULL && v->di_tv.v_type == VAR_PARTIAL)
{
partial_T *pt = v->di_tv.vval.v_partial;
if (pt == NULL)
if (v->di_tv.v_type == VAR_PARTIAL)
{
*lenp = 0;
return (char_u *)""; // just in case
partial_T *pt = v->di_tv.vval.v_partial;
if (pt == NULL)
{
*lenp = 0;
return (char_u *)""; // just in case
}
if (partialp != NULL)
*partialp = pt;
s = partial_name(pt);
*lenp = (int)STRLEN(s);
}
if (s != NULL)
{
if (type != NULL && ht == get_script_local_ht())
{
svar_T *sv = find_typval_in_script(&v->di_tv);
if (sv != NULL)
*type = sv->sv_type;
}
return s;
}
if (partialp != NULL)
*partialp = pt;
s = partial_name(pt);
*lenp = (int)STRLEN(s);
return s;
}
return name;
@@ -2387,6 +2408,14 @@ call_func(
}
}
if (error == FCERR_NONE && funcexe->check_type != NULL && funcexe->evaluate)
{
// Check that the argument types are OK for the types of the funcref.
if (check_argument_types(funcexe->check_type, argvars, argcount,
name) == FAIL)
error = FCERR_OTHER;
}
if (error == FCERR_NONE && funcexe->evaluate)
{
char_u *rfname = fname;
@@ -2629,7 +2658,8 @@ trans_function_name(
int skip, // only find the end, don't evaluate
int flags,
funcdict_T *fdp, // return: info about dictionary used
partial_T **partial) // return: partial of a FuncRef
partial_T **partial, // return: partial of a FuncRef
type_T **type) // return: type of funcref if not NULL
{
char_u *name = NULL;
char_u *start;
@@ -2733,7 +2763,7 @@ trans_function_name(
if (lv.ll_exp_name != NULL)
{
len = (int)STRLEN(lv.ll_exp_name);
name = deref_func_name(lv.ll_exp_name, &len, partial,
name = deref_func_name(lv.ll_exp_name, &len, partial, type,
flags & TFN_NO_AUTOLOAD);
if (name == lv.ll_exp_name)
name = NULL;
@@ -2741,7 +2771,8 @@ trans_function_name(
else if (!(flags & TFN_NO_DEREF))
{
len = (int)(end - *pp);
name = deref_func_name(*pp, &len, partial, flags & TFN_NO_AUTOLOAD);
name = deref_func_name(*pp, &len, partial, type,
flags & TFN_NO_AUTOLOAD);
if (name == *pp)
name = NULL;
}
@@ -3063,8 +3094,16 @@ define_function(exarg_T *eap, char_u *name_arg)
}
else
{
name = trans_function_name(&p, &is_global, eap->skip,
TFN_NO_AUTOLOAD, &fudi, NULL);
if (STRNCMP(p, "<lambda>", 8) == 0)
{
p += 8;
(void)getdigits(&p);
name = vim_strnsave(eap->arg, p - eap->arg);
CLEAR_FIELD(fudi);
}
else
name = trans_function_name(&p, &is_global, eap->skip,
TFN_NO_AUTOLOAD, &fudi, NULL, NULL);
paren = (vim_strchr(p, '(') != NULL);
if (name == NULL && (fudi.fd_dict == NULL || !paren) && !eap->skip)
{
@@ -3413,8 +3452,10 @@ define_function(exarg_T *eap, char_u *name_arg)
;
// Check for "endfunction" or "enddef".
// When a ":" follows it must be a dict key; "enddef: value,"
if (checkforcmd(&p, nesting_def[nesting]
? "enddef" : "endfunction", 4))
? "enddef" : "endfunction", 4)
&& *p != ':')
{
if (nesting-- == 0)
{
@@ -3453,7 +3494,7 @@ define_function(exarg_T *eap, char_u *name_arg)
// not find it.
else if (nesting_def[nesting])
{
if (checkforcmd(&p, "endfunction", 4))
if (checkforcmd(&p, "endfunction", 4) && *p != ':')
emsg(_(e_mismatched_endfunction));
}
else if (eap->cmdidx == CMD_def && checkforcmd(&p, "enddef", 4))
@@ -3479,7 +3520,8 @@ define_function(exarg_T *eap, char_u *name_arg)
if (*p == '!')
p = skipwhite(p + 1);
p += eval_fname_script(p);
vim_free(trans_function_name(&p, NULL, TRUE, 0, NULL, NULL));
vim_free(trans_function_name(&p, NULL, TRUE, 0, NULL,
NULL, NULL));
if (*skipwhite(p) == '(')
{
if (nesting == MAX_FUNC_NESTING - 1)
@@ -3616,7 +3658,7 @@ define_function(exarg_T *eap, char_u *name_arg)
{
hashtab_T *ht;
v = find_var(name, &ht, FALSE);
v = find_var(name, &ht, TRUE);
if (v != NULL && v->di_tv.v_type == VAR_FUNC)
{
emsg_funcname(N_("E707: Function name conflicts with variable: %s"),
@@ -4007,7 +4049,7 @@ function_exists(char_u *name, int no_deref)
flag = TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD;
if (no_deref)
flag |= TFN_NO_DEREF;
p = trans_function_name(&nm, &is_global, FALSE, flag, NULL, NULL);
p = trans_function_name(&nm, &is_global, FALSE, flag, NULL, NULL, NULL);
nm = skipwhite(nm);
// Only accept "funcname", "funcname ", "funcname (..." and
@@ -4027,7 +4069,7 @@ get_expanded_name(char_u *name, int check)
int is_global = FALSE;
p = trans_function_name(&nm, &is_global, FALSE,
TFN_INT|TFN_QUIET, NULL, NULL);
TFN_INT|TFN_QUIET, NULL, NULL, NULL);
if (p != NULL && *nm == NUL
&& (!check || translated_function_exists(p, is_global)))
@@ -4097,7 +4139,8 @@ ex_delfunction(exarg_T *eap)
int is_global = FALSE;
p = eap->arg;
name = trans_function_name(&p, &is_global, eap->skip, 0, &fudi, NULL);
name = trans_function_name(&p, &is_global, eap->skip, 0, &fudi,
NULL, NULL);
vim_free(fudi.fd_newkey);
if (name == NULL)
{
@@ -4328,6 +4371,7 @@ ex_call(exarg_T *eap)
funcdict_T fudi;
partial_T *partial = NULL;
evalarg_T evalarg;
type_T *type = NULL;
fill_evalarg_from_eap(&evalarg, eap, eap->skip);
if (eap->skip)
@@ -4343,8 +4387,8 @@ ex_call(exarg_T *eap)
return;
}
tofree = trans_function_name(&arg, NULL, eap->skip,
TFN_INT, &fudi, &partial);
tofree = trans_function_name(&arg, NULL, eap->skip, TFN_INT,
&fudi, &partial, in_vim9script() ? &type : NULL);
if (fudi.fd_newkey != NULL)
{
// Still need to give an error message for missing key.
@@ -4363,8 +4407,8 @@ ex_call(exarg_T *eap)
// contents. For VAR_PARTIAL get its partial, unless we already have one
// from trans_function_name().
len = (int)STRLEN(tofree);
name = deref_func_name(tofree, &len,
partial != NULL ? NULL : &partial, FALSE);
name = deref_func_name(tofree, &len, partial != NULL ? NULL : &partial,
in_vim9script() && type == NULL ? &type : NULL, FALSE);
// Skip white space to allow ":call func ()". Not good, but required for
// backward compatibility.
@@ -4416,6 +4460,7 @@ ex_call(exarg_T *eap)
funcexe.evaluate = !eap->skip;
funcexe.partial = partial;
funcexe.selfdict = fudi.fd_dict;
funcexe.check_type = type;
if (get_func_tv(name, -1, &rettv, &arg, &evalarg, &funcexe) == FAIL)
{
failed = TRUE;
+44
View File
@@ -765,6 +765,50 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
2327,
/**/
2326,
/**/
2325,
/**/
2324,
/**/
2323,
/**/
2322,
/**/
2321,
/**/
2320,
/**/
2319,
/**/
2318,
/**/
2317,
/**/
2316,
/**/
2315,
/**/
2314,
/**/
2313,
/**/
2312,
/**/
2311,
/**/
2310,
/**/
2309,
/**/
2308,
/**/
2307,
/**/
2306,
/**/
2305,
/**/
+19 -8
View File
@@ -33,7 +33,7 @@ typedef enum {
ISN_LOADWDICT, // push w: dict
ISN_LOADTDICT, // push t: dict
ISN_LOADS, // push s: variable isn_arg.loadstore
ISN_LOADOUTER, // push variable from outer scope isn_arg.number
ISN_LOADOUTER, // push variable from outer scope isn_arg.outer
ISN_LOADSCRIPT, // push script-local variable isn_arg.script.
ISN_LOADOPT, // push option isn_arg.string
ISN_LOADENV, // push environment variable isn_arg.string
@@ -47,7 +47,7 @@ typedef enum {
ISN_STOREW, // pop into window-local variable isn_arg.string
ISN_STORET, // pop into tab-local variable isn_arg.string
ISN_STORES, // pop into script variable isn_arg.loadstore
ISN_STOREOUTER, // pop variable into outer scope isn_arg.number
ISN_STOREOUTER, // pop variable into outer scope isn_arg.outer
ISN_STORESCRIPT, // pop into script variable isn_arg.script
ISN_STOREOPT, // pop into option isn_arg.string
ISN_STOREENV, // pop into environment variable isn_arg.string
@@ -84,6 +84,7 @@ typedef enum {
ISN_PCALL, // call partial, use isn_arg.pfunc
ISN_PCALL_END, // cleanup after ISN_PCALL with cpf_top set
ISN_RETURN, // return, result is on top of stack
ISN_RETURN_ZERO, // Push zero, then return
ISN_FUNCREF, // push a function ref to dfunc isn_arg.funcref
ISN_NEWFUNC, // create a global function from a lambda function
ISN_DEF, // list functions
@@ -104,12 +105,12 @@ typedef enum {
ISN_ADDLIST, // add two lists
ISN_ADDBLOB, // add two blobs
// operation with two arguments; isn_arg.op.op_type is exptype_T
// operation with two arguments; isn_arg.op.op_type is exprtype_T
ISN_OPNR,
ISN_OPFLOAT,
ISN_OPANY,
// comparative operations; isn_arg.op.op_type is exptype_T, op_ic used
// comparative operations; isn_arg.op.op_type is exprtype_T, op_ic used
ISN_COMPAREBOOL,
ISN_COMPARESPECIAL,
ISN_COMPARENR,
@@ -216,7 +217,7 @@ typedef struct {
// arguments to ISN_OPNR, ISN_OPFLOAT, etc.
typedef struct {
exptype_T op_type;
exprtype_T op_type;
int op_ic; // TRUE with '#', FALSE with '?', else MAYBE
} opexpr_T;
@@ -302,6 +303,12 @@ typedef struct {
int unp_semicolon; // last item gets list of remainder
} unpack_T;
// arguments to ISN_LOADOUTER and ISN_STOREOUTER
typedef struct {
int outer_idx; // index
int outer_depth; // nesting level, stack frames to go up
} isn_outer_T;
/*
* Instruction
*/
@@ -341,6 +348,7 @@ struct isn_S {
put_T put;
cmod_T cmdmod;
unpack_T unpack;
isn_outer_T outer;
} isn_arg;
};
@@ -367,10 +375,13 @@ struct dfunc_S {
// Number of entries used by stack frame for a function call.
// - ec_dfunc_idx: function index
// - ec_iidx: instruction index
// - ec_outer_stack: stack used for closures TODO: can we avoid this?
// - ec_outer_frame: stack frame for closures
// - ec_outer: stack used for closures
// - ec_frame_idx: previous frame index
#define STACK_FRAME_SIZE 5
#define STACK_FRAME_FUNC_OFF 0
#define STACK_FRAME_IIDX_OFF 1
#define STACK_FRAME_OUTER_OFF 2
#define STACK_FRAME_IDX_OFF 3
#define STACK_FRAME_SIZE 4
#ifdef DEFINE_VIM9_GLOBALS
+183 -99
View File
@@ -108,7 +108,7 @@ typedef struct {
char_u *lv_name;
type_T *lv_type;
int lv_idx; // index of the variable on the stack
int lv_from_outer; // when TRUE using ctx_outer scope
int lv_from_outer; // nesting level, using ctx_outer scope
int lv_const; // when TRUE cannot be assigned to
int lv_arg; // when TRUE this is an argument
} lvar_T;
@@ -149,7 +149,7 @@ static void delete_def_function_contents(dfunc_T *dfunc, int mark_deleted);
/*
* Lookup variable "name" in the local scope and return it in "lvar".
* "lvar->lv_from_outer" is set accordingly.
* "lvar->lv_from_outer" is incremented accordingly.
* If "lvar" is NULL only check if the variable can be found.
* Return FAIL if not found.
*/
@@ -172,7 +172,7 @@ lookup_local(char_u *name, size_t len, lvar_T *lvar, cctx_T *cctx)
if (lvar != NULL)
{
*lvar = *lvp;
lvar->lv_from_outer = FALSE;
lvar->lv_from_outer = 0;
}
return OK;
}
@@ -186,7 +186,7 @@ lookup_local(char_u *name, size_t len, lvar_T *lvar, cctx_T *cctx)
if (lvar != NULL)
{
cctx->ctx_outer_used = TRUE;
lvar->lv_from_outer = TRUE;
++lvar->lv_from_outer;
}
return OK;
}
@@ -258,7 +258,7 @@ arg_exists(
if (arg_exists(name, len, idxp, type, gen_load_outer, cctx->ctx_outer)
== OK)
{
*gen_load_outer = TRUE;
++*gen_load_outer;
return OK;
}
}
@@ -669,7 +669,7 @@ generate_two_op(cctx_T *cctx, char_u *op)
* Return ISN_DROP when failed.
*/
static isntype_T
get_compare_isn(exptype_T exptype, vartype_T type1, vartype_T type2)
get_compare_isn(exprtype_T exprtype, vartype_T type1, vartype_T type2)
{
isntype_T isntype = ISN_DROP;
@@ -699,22 +699,22 @@ get_compare_isn(exptype_T exptype, vartype_T type1, vartype_T type2)
&& (type2 == VAR_NUMBER || type2 ==VAR_FLOAT)))
isntype = ISN_COMPAREANY;
if ((exptype == EXPR_IS || exptype == EXPR_ISNOT)
if ((exprtype == EXPR_IS || exprtype == EXPR_ISNOT)
&& (isntype == ISN_COMPAREBOOL
|| isntype == ISN_COMPARESPECIAL
|| isntype == ISN_COMPARENR
|| isntype == ISN_COMPAREFLOAT))
{
semsg(_(e_cannot_use_str_with_str),
exptype == EXPR_IS ? "is" : "isnot" , vartype_name(type1));
exprtype == EXPR_IS ? "is" : "isnot" , vartype_name(type1));
return ISN_DROP;
}
if (isntype == ISN_DROP
|| ((exptype != EXPR_EQUAL && exptype != EXPR_NEQUAL
|| ((exprtype != EXPR_EQUAL && exprtype != EXPR_NEQUAL
&& (type1 == VAR_BOOL || type1 == VAR_SPECIAL
|| type2 == VAR_BOOL || type2 == VAR_SPECIAL)))
|| ((exptype != EXPR_EQUAL && exptype != EXPR_NEQUAL
&& exptype != EXPR_IS && exptype != EXPR_ISNOT
|| ((exprtype != EXPR_EQUAL && exprtype != EXPR_NEQUAL
&& exprtype != EXPR_IS && exprtype != EXPR_ISNOT
&& (type1 == VAR_BLOB || type2 == VAR_BLOB
|| type1 == VAR_LIST || type2 == VAR_LIST))))
{
@@ -726,7 +726,7 @@ get_compare_isn(exptype_T exptype, vartype_T type1, vartype_T type2)
}
int
check_compare_types(exptype_T type, typval_T *tv1, typval_T *tv2)
check_compare_types(exprtype_T type, typval_T *tv1, typval_T *tv2)
{
if (get_compare_isn(type, tv1->v_type, tv2->v_type) == ISN_DROP)
return FAIL;
@@ -737,7 +737,7 @@ check_compare_types(exptype_T type, typval_T *tv1, typval_T *tv2)
* Generate an ISN_COMPARE* instruction with a boolean result.
*/
static int
generate_COMPARE(cctx_T *cctx, exptype_T exptype, int ic)
generate_COMPARE(cctx_T *cctx, exprtype_T exprtype, int ic)
{
isntype_T isntype;
isn_T *isn;
@@ -752,13 +752,13 @@ generate_COMPARE(cctx_T *cctx, exptype_T exptype, int ic)
// checking.
type1 = ((type_T **)stack->ga_data)[stack->ga_len - 2]->tt_type;
type2 = ((type_T **)stack->ga_data)[stack->ga_len - 1]->tt_type;
isntype = get_compare_isn(exptype, type1, type2);
isntype = get_compare_isn(exprtype, type1, type2);
if (isntype == ISN_DROP)
return FAIL;
if ((isn = generate_instr(cctx, isntype)) == NULL)
return FAIL;
isn->isn_arg.op.op_type = exptype;
isn->isn_arg.op.op_type = exprtype;
isn->isn_arg.op.op_ic = ic;
// takes two arguments, puts one bool back
@@ -857,7 +857,9 @@ use_typecheck(type_T *actual, type_T *expected)
|| (actual->tt_type == VAR_FUNC
&& (expected->tt_type == VAR_FUNC
|| expected->tt_type == VAR_PARTIAL)
&& (actual->tt_member == &t_any || actual->tt_argcount < 0)))
&& (actual->tt_member == &t_any || actual->tt_argcount < 0)
&& ((actual->tt_member == &t_void)
== (expected->tt_member == &t_void))))
return TRUE;
if ((actual->tt_type == VAR_LIST || actual->tt_type == VAR_DICT)
&& actual->tt_type == expected->tt_type)
@@ -1173,6 +1175,23 @@ generate_STORE(cctx_T *cctx, isntype_T isn_type, int idx, char_u *name)
return OK;
}
/*
* Generate an ISN_STOREOUTER instruction.
*/
static int
generate_STOREOUTER(cctx_T *cctx, int idx, int level)
{
isn_T *isn;
RETURN_OK_IF_SKIP(cctx);
if ((isn = generate_instr_drop(cctx, ISN_STOREOUTER, 1)) == NULL)
return FAIL;
isn->isn_arg.outer.outer_idx = idx;
isn->isn_arg.outer.outer_depth = level;
return OK;
}
/*
* Generate an ISN_STORENR instruction (short for ISN_PUSHNR + ISN_STORE)
*/
@@ -1231,6 +1250,27 @@ generate_LOAD(
return OK;
}
/*
* Generate an ISN_LOADOUTER instruction
*/
static int
generate_LOADOUTER(
cctx_T *cctx,
int idx,
int nesting,
type_T *type)
{
isn_T *isn;
RETURN_OK_IF_SKIP(cctx);
if ((isn = generate_instr_type(cctx, ISN_LOADOUTER, type)) == NULL)
return FAIL;
isn->isn_arg.outer.outer_idx = idx;
isn->isn_arg.outer.outer_depth = nesting;
return OK;
}
/*
* Generate an ISN_LOADV instruction for v:var.
*/
@@ -1437,6 +1477,11 @@ generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc)
isn->isn_arg.funcref.fr_func = ufunc->uf_dfunc_idx;
cctx->ctx_has_closure = 1;
// if the referenced function is a closure, it may use items further up in
// the nested context, including this one.
if (ufunc->uf_flags & FC_CLOSURE)
cctx->ctx_ufunc->uf_flags |= FC_CLOSURE;
if (ga_grow(stack, 1) == FAIL)
return FAIL;
((type_T **)stack->ga_data)[stack->ga_len] =
@@ -1547,6 +1592,7 @@ generate_BCALL(cctx_T *cctx, int func_idx, int argcount, int method_call)
garray_T *stack = &cctx->ctx_type_stack;
int argoff;
type_T **argtypes = NULL;
type_T *maptype = NULL;
RETURN_OK_IF_SKIP(cctx);
argoff = check_internal_func(func_idx, argcount);
@@ -1567,6 +1613,8 @@ generate_BCALL(cctx_T *cctx, int func_idx, int argcount, int method_call)
argtypes = ((type_T **)stack->ga_data) + stack->ga_len - argcount;
if (internal_func_check_arg_types(argtypes, func_idx, argcount) == FAIL)
return FAIL;
if (internal_func_is_map(func_idx))
maptype = *argtypes;
}
if ((isn = generate_instr(cctx, ISN_BCALL)) == NULL)
@@ -1582,6 +1630,11 @@ generate_BCALL(cctx_T *cctx, int func_idx, int argcount, int method_call)
internal_func_ret_type(func_idx, argcount, argtypes);
++stack->ga_len;
if (maptype != NULL && maptype->tt_member != NULL
&& maptype->tt_member != &t_any)
// Check that map() didn't change the item types.
generate_TYPECHECK(cctx, maptype, -1);
return OK;
}
@@ -1790,9 +1843,9 @@ generate_PCALL(
stack->ga_len + offset];
type_T *expected;
if (varargs && i >= type->tt_min_argcount - 1)
if (varargs && i >= type->tt_argcount - 1)
expected = type->tt_args[
type->tt_min_argcount - 1]->tt_member;
type->tt_argcount - 1]->tt_member;
else
expected = type->tt_args[i];
if (need_type(actual, expected, offset,
@@ -1854,7 +1907,10 @@ generate_STRINGMEMBER(cctx_T *cctx, char_u *name, size_t len)
}
// change dict type to dict member type
if (type->tt_type == VAR_DICT)
((type_T **)stack->ga_data)[stack->ga_len - 1] = type->tt_member;
{
((type_T **)stack->ga_data)[stack->ga_len - 1] =
type->tt_member == &t_unknown ? &t_any : type->tt_member;
}
return OK;
}
@@ -2587,7 +2643,7 @@ compile_load(
size_t len = end - *arg;
int idx;
int gen_load = FALSE;
int gen_load_outer = FALSE;
int gen_load_outer = 0;
name = vim_strnsave(*arg, end - *arg);
if (name == NULL)
@@ -2595,7 +2651,7 @@ compile_load(
if (arg_exists(*arg, len, &idx, &type, &gen_load_outer, cctx) == OK)
{
if (!gen_load_outer)
if (gen_load_outer == 0)
gen_load = TRUE;
}
else
@@ -2606,8 +2662,8 @@ compile_load(
{
type = lvar.lv_type;
idx = lvar.lv_idx;
if (lvar.lv_from_outer)
gen_load_outer = TRUE;
if (lvar.lv_from_outer != 0)
gen_load_outer = lvar.lv_from_outer;
else
gen_load = TRUE;
}
@@ -2629,9 +2685,9 @@ compile_load(
}
if (gen_load)
res = generate_LOAD(cctx, ISN_LOAD, idx, NULL, type);
if (gen_load_outer)
if (gen_load_outer > 0)
{
res = generate_LOAD(cctx, ISN_LOADOUTER, idx, NULL, type);
res = generate_LOADOUTER(cctx, idx, gen_load_outer, type);
cctx->ctx_outer_used = TRUE;
}
}
@@ -3346,10 +3402,10 @@ get_vim_constant(char_u **arg, typval_T *rettv)
}
}
exptype_T
exprtype_T
get_compare_type(char_u *p, int *len, int *type_is)
{
exptype_T type = EXPR_UNKNOWN;
exprtype_T type = EXPR_UNKNOWN;
int i;
switch (p[0])
@@ -3748,7 +3804,12 @@ compile_subscript(
return FAIL;
}
if ((*typep)->tt_type == VAR_DICT)
{
*typep = (*typep)->tt_member;
if (*typep == &t_unknown)
// empty dict was used
*typep = &t_any;
}
else
{
if (need_type(*typep, &t_dict_any, -2, cctx,
@@ -3786,7 +3847,12 @@ compile_subscript(
else
{
if ((*typep)->tt_type == VAR_LIST)
{
*typep = (*typep)->tt_member;
if (*typep == &t_unknown)
// empty list was used
*typep = &t_any;
}
if (generate_instr_drop(cctx,
vtype == VAR_LIST ? ISN_LISTINDEX : ISN_ANYINDEX,
1) == FAIL)
@@ -4344,7 +4410,7 @@ compile_expr5(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
static int
compile_expr4(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
{
exptype_T type = EXPR_UNKNOWN;
exprtype_T type = EXPR_UNKNOWN;
char_u *p;
char_u *next;
int len = 2;
@@ -4812,7 +4878,8 @@ compile_return(char_u *arg, int check_return_type, cctx_T *cctx)
{
stack_type = ((type_T **)stack->ga_data)[stack->ga_len - 1];
if (check_return_type && (cctx->ctx_ufunc->uf_ret_type == NULL
|| cctx->ctx_ufunc->uf_ret_type == &t_unknown))
|| cctx->ctx_ufunc->uf_ret_type == &t_unknown
|| cctx->ctx_ufunc->uf_ret_type == &t_any))
{
cctx->ctx_ufunc->uf_ret_type = stack_type;
}
@@ -5117,9 +5184,9 @@ generate_loadvar(
generate_LOADV(cctx, name + 2, TRUE);
break;
case dest_local:
if (lvar->lv_from_outer)
generate_LOAD(cctx, ISN_LOADOUTER, lvar->lv_idx,
NULL, type);
if (lvar->lv_from_outer > 0)
generate_LOADOUTER(cctx, lvar->lv_idx, lvar->lv_from_outer,
type);
else
generate_LOAD(cctx, ISN_LOAD, lvar->lv_idx, NULL, type);
break;
@@ -6175,7 +6242,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
// optimization: turn "var = 123" from ISN_PUSHNR +
// ISN_STORE into ISN_STORENR
if (!lhs.lhs_lvar->lv_from_outer
if (lhs.lhs_lvar->lv_from_outer == 0
&& instr->ga_len == instr_count + 1
&& isn->isn_type == ISN_PUSHNR)
{
@@ -6187,9 +6254,9 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
if (stack->ga_len > 0)
--stack->ga_len;
}
else if (lhs.lhs_lvar->lv_from_outer)
generate_STORE(cctx, ISN_STOREOUTER,
lhs.lhs_lvar->lv_idx, NULL);
else if (lhs.lhs_lvar->lv_from_outer > 0)
generate_STOREOUTER(cctx, lhs.lhs_lvar->lv_idx,
lhs.lhs_lvar->lv_from_outer);
else
generate_STORE(cctx, ISN_STORE, lhs.lhs_lvar->lv_idx, NULL);
}
@@ -6213,6 +6280,77 @@ theend:
return ret;
}
/*
* Check for an assignment at "eap->cmd", compile it if found.
* Return NOTDONE if there is none, FAIL for failure, OK if done.
*/
static int
may_compile_assignment(exarg_T *eap, char_u **line, cctx_T *cctx)
{
char_u *pskip;
char_u *p;
// Assuming the command starts with a variable or function name,
// find what follows.
// Skip over "var.member", "var[idx]" and the like.
// Also "&opt = val", "$ENV = val" and "@r = val".
pskip = (*eap->cmd == '&' || *eap->cmd == '$' || *eap->cmd == '@')
? eap->cmd + 1 : eap->cmd;
p = to_name_end(pskip, TRUE);
if (p > eap->cmd && *p != NUL)
{
char_u *var_end;
int oplen;
int heredoc;
if (eap->cmd[0] == '@')
var_end = eap->cmd + 2;
else
var_end = find_name_end(pskip, NULL, NULL,
FNE_CHECK_START | FNE_INCL_BR);
oplen = assignment_len(skipwhite(var_end), &heredoc);
if (oplen > 0)
{
size_t len = p - eap->cmd;
// Recognize an assignment if we recognize the variable
// name:
// "g:var = expr"
// "local = expr" where "local" is a local var.
// "script = expr" where "script" is a script-local var.
// "import = expr" where "import" is an imported var
// "&opt = expr"
// "$ENV = expr"
// "@r = expr"
if (*eap->cmd == '&'
|| *eap->cmd == '$'
|| *eap->cmd == '@'
|| ((len) > 2 && eap->cmd[1] == ':')
|| lookup_local(eap->cmd, len, NULL, cctx) == OK
|| arg_exists(eap->cmd, len, NULL, NULL, NULL, cctx) == OK
|| script_var_exists(eap->cmd, len, FALSE, cctx) == OK
|| find_imported(eap->cmd, len, cctx) != NULL)
{
*line = compile_assignment(eap->cmd, eap, CMD_SIZE, cctx);
if (*line == NULL || *line == eap->cmd)
return FAIL;
return OK;
}
}
}
if (*eap->cmd == '[')
{
// [var, var] = expr
*line = compile_assignment(eap->cmd, eap, CMD_SIZE, cctx);
if (*line == NULL)
return FAIL;
if (*line != eap->cmd)
return OK;
}
return NOTDONE;
}
/*
* Check if "name" can be "unlet".
*/
@@ -7835,68 +7973,14 @@ compile_def_function(ufunc_T *ufunc, int check_return_type, cctx_T *outer_cctx)
if (!starts_with_colon)
{
char_u *pskip;
int assign;
// Assuming the command starts with a variable or function name,
// find what follows.
// Skip over "var.member", "var[idx]" and the like.
// Also "&opt = val", "$ENV = val" and "@r = val".
pskip = (*ea.cmd == '&' || *ea.cmd == '$' || *ea.cmd == '@')
? ea.cmd + 1 : ea.cmd;
p = to_name_end(pskip, TRUE);
if (p > ea.cmd && *p != NUL)
{
char_u *var_end;
int oplen;
int heredoc;
if (ea.cmd[0] == '@')
var_end = ea.cmd + 2;
else
var_end = find_name_end(pskip, NULL, NULL,
FNE_CHECK_START | FNE_INCL_BR);
oplen = assignment_len(skipwhite(var_end), &heredoc);
if (oplen > 0)
{
size_t len = p - ea.cmd;
// Recognize an assignment if we recognize the variable
// name:
// "g:var = expr"
// "local = expr" where "local" is a local var.
// "script = expr" where "script" is a script-local var.
// "import = expr" where "import" is an imported var
// "&opt = expr"
// "$ENV = expr"
// "@r = expr"
if (*ea.cmd == '&'
|| *ea.cmd == '$'
|| *ea.cmd == '@'
|| ((len) > 2 && ea.cmd[1] == ':')
|| lookup_local(ea.cmd, len, NULL, &cctx) == OK
|| arg_exists(ea.cmd, len, NULL, NULL,
NULL, &cctx) == OK
|| script_var_exists(ea.cmd, len,
FALSE, &cctx) == OK
|| find_imported(ea.cmd, len, &cctx) != NULL)
{
line = compile_assignment(ea.cmd, &ea, CMD_SIZE, &cctx);
if (line == NULL || line == ea.cmd)
goto erret;
goto nextline;
}
}
}
if (*ea.cmd == '[')
{
// [var, var] = expr
line = compile_assignment(ea.cmd, &ea, CMD_SIZE, &cctx);
if (line == NULL)
goto erret;
if (line != ea.cmd)
goto nextline;
}
// Check for assignment after command modifiers.
assign = may_compile_assignment(&ea, &line, &cctx);
if (assign == OK)
goto nextline;
if (assign == FAIL)
goto erret;
}
/*
@@ -8170,8 +8254,7 @@ nextline:
}
// Return zero if there is no return at the end.
generate_PUSHNR(&cctx, 0);
generate_instr(&cctx, ISN_RETURN);
generate_instr(&cctx, ISN_RETURN_ZERO);
}
{
@@ -8463,6 +8546,7 @@ delete_instr(isn_T *isn)
case ISN_PUSHSPEC:
case ISN_PUT:
case ISN_RETURN:
case ISN_RETURN_ZERO:
case ISN_SHUFFLE:
case ISN_SLICE:
case ISN_STORE:
+274 -84
View File
@@ -58,8 +58,7 @@ struct ectx_S {
garray_T ec_stack; // stack of typval_T values
int ec_frame_idx; // index in ec_stack: context of ec_dfunc_idx
garray_T *ec_outer_stack; // stack used for closures
int ec_outer_frame; // stack frame in ec_outer_stack
outer_T *ec_outer; // outer scope used for closures, allocated
garray_T ec_trystack; // stack of trycmd_T values
int ec_in_catch; // when TRUE in catch or finally block
@@ -149,6 +148,9 @@ exe_newlist(int count, ectx_T *ectx)
/*
* Call compiled function "cdf_idx" from compiled code.
* This adds a stack frame and sets the instruction pointer to the start of the
* called function.
* If "pt" is not null use "pt->pt_outer" for ec_outer.
*
* Stack has:
* - current arguments (already there)
@@ -160,7 +162,7 @@ exe_newlist(int count, ectx_T *ectx)
* - reserved space for local variables
*/
static int
call_dfunc(int cdf_idx, int argcount_arg, ectx_T *ectx)
call_dfunc(int cdf_idx, partial_T *pt, int argcount_arg, ectx_T *ectx)
{
int argcount = argcount_arg;
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) + cdf_idx;
@@ -243,11 +245,10 @@ call_dfunc(int cdf_idx, int argcount_arg, ectx_T *ectx)
ectx->ec_stack.ga_len += arg_to_add;
// Store current execution state in stack frame for ISN_RETURN.
STACK_TV_BOT(0)->vval.v_number = ectx->ec_dfunc_idx;
STACK_TV_BOT(1)->vval.v_number = ectx->ec_iidx;
STACK_TV_BOT(2)->vval.v_string = (void *)ectx->ec_outer_stack;
STACK_TV_BOT(3)->vval.v_number = ectx->ec_outer_frame;
STACK_TV_BOT(4)->vval.v_number = ectx->ec_frame_idx;
STACK_TV_BOT(STACK_FRAME_FUNC_OFF)->vval.v_number = ectx->ec_dfunc_idx;
STACK_TV_BOT(STACK_FRAME_IIDX_OFF)->vval.v_number = ectx->ec_iidx;
STACK_TV_BOT(STACK_FRAME_OUTER_OFF)->vval.v_string = (void *)ectx->ec_outer;
STACK_TV_BOT(STACK_FRAME_IDX_OFF)->vval.v_number = ectx->ec_frame_idx;
ectx->ec_frame_idx = ectx->ec_stack.ga_len;
// Initialize local variables
@@ -262,11 +263,32 @@ call_dfunc(int cdf_idx, int argcount_arg, ectx_T *ectx)
}
ectx->ec_stack.ga_len += STACK_FRAME_SIZE + varcount;
if (ufunc->uf_partial != NULL)
if (pt != NULL || ufunc->uf_partial != NULL || ufunc->uf_flags & FC_CLOSURE)
{
ectx->ec_outer_stack = ufunc->uf_partial->pt_ectx_stack;
ectx->ec_outer_frame = ufunc->uf_partial->pt_ectx_frame;
outer_T *outer = ALLOC_CLEAR_ONE(outer_T);
if (outer == NULL)
return FAIL;
if (pt != NULL)
{
*outer = pt->pt_outer;
outer->out_up_is_copy = TRUE;
}
else if (ufunc->uf_partial != NULL)
{
*outer = ufunc->uf_partial->pt_outer;
outer->out_up_is_copy = TRUE;
}
else
{
outer->out_stack = &ectx->ec_stack;
outer->out_frame_idx = ectx->ec_frame_idx;
outer->out_up = ectx->ec_outer;
}
ectx->ec_outer = outer;
}
else
ectx->ec_outer = NULL;
// Set execution state to the start of the called function.
ectx->ec_dfunc_idx = cdf_idx;
@@ -415,8 +437,9 @@ handle_closure_in_use(ectx_T *ectx, int free_arguments)
{
++funcstack->fs_refcount;
pt->pt_funcstack = funcstack;
pt->pt_ectx_stack = &funcstack->fs_ga;
pt->pt_ectx_frame = ectx->ec_frame_idx - top;
pt->pt_outer.out_stack = &funcstack->fs_ga;
pt->pt_outer.out_frame_idx = ectx->ec_frame_idx - top;
pt->pt_outer.out_up = ectx->ec_outer;
}
}
}
@@ -502,17 +525,21 @@ func_return(ectx_T *ectx)
// The return value should be on top of the stack. However, when aborting
// it may not be there and ec_frame_idx is the top of the stack.
ret_idx = ectx->ec_stack.ga_len - 1;
if (ret_idx == ectx->ec_frame_idx + 4)
if (ret_idx == ectx->ec_frame_idx + STACK_FRAME_IDX_OFF)
ret_idx = 0;
vim_free(ectx->ec_outer);
// Restore the previous frame.
ectx->ec_dfunc_idx = STACK_TV(ectx->ec_frame_idx)->vval.v_number;
ectx->ec_iidx = STACK_TV(ectx->ec_frame_idx + 1)->vval.v_number;
ectx->ec_outer_stack =
(void *)STACK_TV(ectx->ec_frame_idx + 2)->vval.v_string;
ectx->ec_outer_frame = STACK_TV(ectx->ec_frame_idx + 3)->vval.v_number;
ectx->ec_dfunc_idx = STACK_TV(ectx->ec_frame_idx
+ STACK_FRAME_FUNC_OFF)->vval.v_number;
ectx->ec_iidx = STACK_TV(ectx->ec_frame_idx
+ STACK_FRAME_IIDX_OFF)->vval.v_number;
ectx->ec_outer = (void *)STACK_TV(ectx->ec_frame_idx
+ STACK_FRAME_OUTER_OFF)->vval.v_string;
// restoring ec_frame_idx must be last
ectx->ec_frame_idx = STACK_TV(ectx->ec_frame_idx + 4)->vval.v_number;
ectx->ec_frame_idx = STACK_TV(ectx->ec_frame_idx
+ STACK_FRAME_IDX_OFF)->vval.v_number;
dfunc = ((dfunc_T *)def_functions.ga_data) + ectx->ec_dfunc_idx;
ectx->ec_instr = dfunc->df_instr;
@@ -598,10 +625,19 @@ call_bfunc(int func_idx, int argcount, ectx_T *ectx)
/*
* Execute a user defined function.
* If the function is compiled this will add a stack frame and set the
* instruction pointer at the start of the function.
* Otherwise the function is called here.
* If "pt" is not null use "pt->pt_outer" for ec_outer.
* "iptr" can be used to replace the instruction with a more efficient one.
*/
static int
call_ufunc(ufunc_T *ufunc, int argcount, ectx_T *ectx, isn_T *iptr)
call_ufunc(
ufunc_T *ufunc,
partial_T *pt,
int argcount,
ectx_T *ectx,
isn_T *iptr)
{
typval_T argvars[MAX_FUNC_ARGS];
funcexe_T funcexe;
@@ -634,7 +670,7 @@ call_ufunc(ufunc_T *ufunc, int argcount, ectx_T *ectx, isn_T *iptr)
iptr->isn_arg.dfunc.cdf_idx = ufunc->uf_dfunc_idx;
iptr->isn_arg.dfunc.cdf_argcount = argcount;
}
return call_dfunc(ufunc->uf_dfunc_idx, argcount, ectx);
return call_dfunc(ufunc->uf_dfunc_idx, pt, argcount, ectx);
}
if (call_prepare(argcount, argvars, ectx) == FAIL)
@@ -707,7 +743,7 @@ call_by_name(char_u *name, int argcount, ectx_T *ectx, isn_T *iptr)
}
if (ufunc != NULL)
return call_ufunc(ufunc, argcount, ectx, iptr);
return call_ufunc(ufunc, NULL, argcount, ectx, iptr);
return FAIL;
}
@@ -742,15 +778,8 @@ call_partial(typval_T *tv, int argcount_arg, ectx_T *ectx)
}
if (pt->pt_func != NULL)
{
int ret = call_ufunc(pt->pt_func, argcount, ectx, NULL);
return call_ufunc(pt->pt_func, pt, argcount, ectx, NULL);
// closure may need the function context where it was defined
ectx->ec_outer_stack = pt->pt_ectx_stack;
ectx->ec_outer_frame = pt->pt_ectx_frame;
return ret;
}
name = pt->pt_name;
}
else if (tv->v_type == VAR_FUNC)
@@ -863,6 +892,108 @@ allocate_if_null(typval_T *tv)
}
}
/*
* Return the character "str[index]" where "index" is the character index. If
* "index" is out of range NULL is returned.
*/
char_u *
char_from_string(char_u *str, varnumber_T index)
{
size_t nbyte = 0;
varnumber_T nchar = index;
size_t slen;
if (str == NULL)
return NULL;
slen = STRLEN(str);
// do the same as for a list: a negative index counts from the end
if (index < 0)
{
int clen = 0;
for (nbyte = 0; nbyte < slen; ++clen)
nbyte += MB_CPTR2LEN(str + nbyte);
nchar = clen + index;
if (nchar < 0)
// unlike list: index out of range results in empty string
return NULL;
}
for (nbyte = 0; nchar > 0 && nbyte < slen; --nchar)
nbyte += MB_CPTR2LEN(str + nbyte);
if (nbyte >= slen)
return NULL;
return vim_strnsave(str + nbyte, MB_CPTR2LEN(str + nbyte));
}
/*
* Get the byte index for character index "idx" in string "str" with length
* "str_len".
* If going over the end return "str_len".
* If "idx" is negative count from the end, -1 is the last character.
* When going over the start return -1.
*/
static long
char_idx2byte(char_u *str, size_t str_len, varnumber_T idx)
{
varnumber_T nchar = idx;
size_t nbyte = 0;
if (nchar >= 0)
{
while (nchar > 0 && nbyte < str_len)
{
nbyte += MB_CPTR2LEN(str + nbyte);
--nchar;
}
}
else
{
nbyte = str_len;
while (nchar < 0 && nbyte > 0)
{
--nbyte;
nbyte -= mb_head_off(str, str + nbyte);
++nchar;
}
if (nchar < 0)
return -1;
}
return (long)nbyte;
}
/*
* Return the slice "str[first:last]" using character indexes.
* Return NULL when the result is empty.
*/
char_u *
string_slice(char_u *str, varnumber_T first, varnumber_T last)
{
long start_byte, end_byte;
size_t slen;
if (str == NULL)
return NULL;
slen = STRLEN(str);
start_byte = char_idx2byte(str, slen, first);
if (start_byte < 0)
start_byte = 0; // first index very negative: use zero
if (last == -1)
end_byte = (long)slen;
else
{
end_byte = char_idx2byte(str, slen, last);
if (end_byte >= 0 && end_byte < (long)slen)
// end index is inclusive
end_byte += MB_CPTR2LEN(str + end_byte);
}
if (start_byte >= (long)slen || end_byte <= start_byte)
return NULL;
return vim_strnsave(str + start_byte, end_byte - start_byte);
}
static svar_T *
get_script_svar(scriptref_T *sref, ectx_T *ectx)
{
@@ -937,8 +1068,10 @@ fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx)
// The closure needs to find arguments and local
// variables in the current stack.
pt->pt_ectx_stack = &ectx->ec_stack;
pt->pt_ectx_frame = ectx->ec_frame_idx;
pt->pt_outer.out_stack = &ectx->ec_stack;
pt->pt_outer.out_frame_idx = ectx->ec_frame_idx;
pt->pt_outer.out_up = ectx->ec_outer;
pt->pt_outer.out_up_is_copy = TRUE;
// If this function returns and the closure is still
// being used, we need to make a copy of the context
@@ -1005,9 +1138,6 @@ call_def_function(
// Get pointer to a local variable on the stack. Negative for arguments.
#define STACK_TV_VAR(idx) (((typval_T *)ectx.ec_stack.ga_data) + ectx.ec_frame_idx + STACK_FRAME_SIZE + idx)
// Like STACK_TV_VAR but use the outer scope
#define STACK_OUT_TV_VAR(idx) (((typval_T *)ectx.ec_outer_stack->ga_data) + ectx.ec_outer_frame + STACK_FRAME_SIZE + idx)
if (ufunc->uf_def_status == UF_NOT_COMPILED
|| (ufunc->uf_def_status == UF_TO_BE_COMPILED
&& compile_def_function(ufunc, FALSE, NULL) == FAIL))
@@ -1111,24 +1241,24 @@ call_def_function(
ectx.ec_frame_idx = ectx.ec_stack.ga_len;
initial_frame_idx = ectx.ec_frame_idx;
if (partial != NULL)
if (partial != NULL || ufunc->uf_partial != NULL)
{
if (partial->pt_ectx_stack == NULL && current_ectx != NULL)
ectx.ec_outer = ALLOC_CLEAR_ONE(outer_T);
if (ectx.ec_outer == NULL)
goto failed_early;
if (partial != NULL)
{
// TODO: is this always the right way?
ectx.ec_outer_stack = &current_ectx->ec_stack;
ectx.ec_outer_frame = current_ectx->ec_frame_idx;
if (partial->pt_outer.out_stack == NULL && current_ectx != NULL)
{
if (current_ectx->ec_outer != NULL)
*ectx.ec_outer = *current_ectx->ec_outer;
}
else
*ectx.ec_outer = partial->pt_outer;
}
else
{
ectx.ec_outer_stack = partial->pt_ectx_stack;
ectx.ec_outer_frame = partial->pt_ectx_frame;
}
}
else if (ufunc->uf_partial != NULL)
{
ectx.ec_outer_stack = ufunc->uf_partial->pt_ectx_stack;
ectx.ec_outer_frame = ufunc->uf_partial->pt_ectx_frame;
*ectx.ec_outer = ufunc->uf_partial->pt_outer;
ectx.ec_outer->out_up_is_copy = TRUE;
}
// dummy frame entries
@@ -1410,15 +1540,6 @@ call_def_function(
++ectx.ec_stack.ga_len;
break;
// load variable or argument from outer scope
case ISN_LOADOUTER:
if (GA_GROW(&ectx.ec_stack, 1) == FAIL)
goto failed;
copy_tv(STACK_OUT_TV_VAR(iptr->isn_arg.number),
STACK_TV_BOT(0));
++ectx.ec_stack.ga_len;
break;
// load v: variable
case ISN_LOADV:
if (GA_GROW(&ectx.ec_stack, 1) == FAIL)
@@ -1614,14 +1735,6 @@ call_def_function(
*tv = *STACK_TV_BOT(0);
break;
// store variable or argument in outer scope
case ISN_STOREOUTER:
--ectx.ec_stack.ga_len;
tv = STACK_OUT_TV_VAR(iptr->isn_arg.number);
clear_tv(tv);
*tv = *STACK_TV_BOT(0);
break;
// store s: variable in old script
case ISN_STORES:
{
@@ -1902,6 +2015,43 @@ call_def_function(
}
break;
// load or store variable or argument from outer scope
case ISN_LOADOUTER:
case ISN_STOREOUTER:
{
int depth = iptr->isn_arg.outer.outer_depth;
outer_T *outer = ectx.ec_outer;
while (depth > 1 && outer != NULL)
{
outer = outer->out_up;
--depth;
}
if (outer == NULL)
{
SOURCING_LNUM = iptr->isn_lnum;
iemsg("LOADOUTER depth more than scope levels");
goto failed;
}
tv = ((typval_T *)outer->out_stack->ga_data)
+ outer->out_frame_idx + STACK_FRAME_SIZE
+ iptr->isn_arg.outer.outer_idx;
if (iptr->isn_type == ISN_LOADOUTER)
{
if (GA_GROW(&ectx.ec_stack, 1) == FAIL)
goto failed;
copy_tv(tv, STACK_TV_BOT(0));
++ectx.ec_stack.ga_len;
}
else
{
--ectx.ec_stack.ga_len;
clear_tv(tv);
*tv = *STACK_TV_BOT(0);
}
}
break;
// unlet item in list or dict variable
case ISN_UNLETINDEX:
{
@@ -2140,7 +2290,7 @@ call_def_function(
// call a :def function
case ISN_DCALL:
SOURCING_LNUM = iptr->isn_lnum;
if (call_dfunc(iptr->isn_arg.dfunc.cdf_idx,
if (call_dfunc(iptr->isn_arg.dfunc.cdf_idx, NULL,
iptr->isn_arg.dfunc.cdf_argcount,
&ectx) == FAIL)
goto on_error;
@@ -2205,6 +2355,16 @@ call_def_function(
break;
// return from a :def function call
case ISN_RETURN_ZERO:
if (GA_GROW(&ectx.ec_stack, 1) == FAIL)
goto failed;
tv = STACK_TV_BOT(0);
++ectx.ec_stack.ga_len;
tv->v_type = VAR_NUMBER;
tv->vval.v_number = 0;
tv->v_lock = 0;
// FALLTHROUGH
case ISN_RETURN:
{
garray_T *trystack = &ectx.ec_trystack;
@@ -2613,11 +2773,11 @@ call_def_function(
{
typval_T *tv1 = STACK_TV_BOT(-2);
typval_T *tv2 = STACK_TV_BOT(-1);
exptype_T exptype = iptr->isn_arg.op.op_type;
exprtype_T exprtype = iptr->isn_arg.op.op_type;
int ic = iptr->isn_arg.op.op_ic;
SOURCING_LNUM = iptr->isn_lnum;
typval_compare(tv1, tv2, exptype, ic);
typval_compare(tv1, tv2, exprtype, ic);
clear_tv(tv2);
--ectx.ec_stack.ga_len;
}
@@ -3148,11 +3308,13 @@ call_def_function(
goto failed;
++ectx.ec_stack.ga_len;
tv = STACK_TV_BOT(-1);
ea.line2 = 0;
ea.addr_count = 0;
ea.addr_type = ADDR_LINES;
ea.cmd = iptr->isn_arg.string;
ea.skip = FALSE;
if (parse_cmd_address(&ea, &errormsg, FALSE) == FAIL)
goto failed;
goto on_error;
if (ea.addr_count == 0)
tv->vval.v_number = curwin->w_cursor.lnum;
else
@@ -3387,6 +3549,15 @@ failed_early:
vim_free(ectx.ec_stack.ga_data);
vim_free(ectx.ec_trystack.ga_data);
while (ectx.ec_outer != NULL)
{
outer_T *up = ectx.ec_outer->out_up_is_copy
? NULL : ectx.ec_outer->out_up;
vim_free(ectx.ec_outer);
ectx.ec_outer = up;
}
// Not sure if this is necessary.
suppress_errthrow = save_suppress_errthrow;
@@ -3423,7 +3594,7 @@ ex_disassemble(exarg_T *eap)
}
else
fname = trans_function_name(&arg, &is_global, FALSE,
TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD, NULL, NULL);
TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD, NULL, NULL, NULL);
if (fname == NULL)
{
semsg(_(e_invarg2), eap->arg);
@@ -3508,17 +3679,27 @@ ex_disassemble(exarg_T *eap)
(varnumber_T)(iptr->isn_arg.number));
break;
case ISN_LOAD:
case ISN_LOADOUTER:
{
char *add = iptr->isn_type == ISN_LOAD ? "" : "OUTER";
if (iptr->isn_arg.number < 0)
smsg("%4d LOAD%s arg[%lld]", current, add,
smsg("%4d LOAD arg[%lld]", current,
(varnumber_T)(iptr->isn_arg.number
+ STACK_FRAME_SIZE));
else
smsg("%4d LOAD%s $%lld", current, add,
(varnumber_T)(iptr->isn_arg.number));
smsg("%4d LOAD $%lld", current,
(varnumber_T)(iptr->isn_arg.number));
}
break;
case ISN_LOADOUTER:
{
if (iptr->isn_arg.number < 0)
smsg("%4d LOADOUTER level %d arg[%d]", current,
iptr->isn_arg.outer.outer_depth,
iptr->isn_arg.outer.outer_idx
+ STACK_FRAME_SIZE);
else
smsg("%4d LOADOUTER level %d $%d", current,
iptr->isn_arg.outer.outer_depth,
iptr->isn_arg.outer.outer_idx);
}
break;
case ISN_LOADV:
@@ -3585,16 +3766,22 @@ ex_disassemble(exarg_T *eap)
break;
case ISN_STORE:
if (iptr->isn_arg.number < 0)
smsg("%4d STORE arg[%lld]", current,
iptr->isn_arg.number + STACK_FRAME_SIZE);
else
smsg("%4d STORE $%lld", current, iptr->isn_arg.number);
break;
case ISN_STOREOUTER:
{
char *add = iptr->isn_type == ISN_STORE ? "" : "OUTER";
if (iptr->isn_arg.number < 0)
smsg("%4d STORE%s arg[%lld]", current, add,
(varnumber_T)(iptr->isn_arg.number + STACK_FRAME_SIZE));
smsg("%4d STOREOUTEr level %d arg[%d]", current,
iptr->isn_arg.outer.outer_depth,
iptr->isn_arg.outer.outer_idx + STACK_FRAME_SIZE);
else
smsg("%4d STORE%s $%lld", current, add,
(varnumber_T)(iptr->isn_arg.number));
smsg("%4d STOREOUTER level %d $%d", current,
iptr->isn_arg.outer.outer_depth,
iptr->isn_arg.outer.outer_idx);
}
break;
case ISN_STOREV:
@@ -3802,6 +3989,9 @@ ex_disassemble(exarg_T *eap)
case ISN_RETURN:
smsg("%4d RETURN", current);
break;
case ISN_RETURN_ZERO:
smsg("%4d RETURN 0", current);
break;
case ISN_FUNCREF:
{
funcref_T *funcref = &iptr->isn_arg.funcref;
+40
View File
@@ -527,6 +527,46 @@ check_arg_type(type_T *expected, type_T *actual, int argidx)
return check_type(expected, actual, TRUE, argidx);
}
/*
* Check that the arguments of "type" match "argvars[argcount]".
* Return OK/FAIL.
*/
int
check_argument_types(type_T *type, typval_T *argvars, int argcount, char_u *name)
{
int varargs = (type->tt_flags & TTFLAG_VARARGS) ? 1 : 0;
int i;
if (type->tt_type != VAR_FUNC && type->tt_type != VAR_PARTIAL)
return OK; // just in case
if (argcount < type->tt_min_argcount - varargs)
{
semsg(_(e_toofewarg), name);
return FAIL;
}
if (!varargs && type->tt_argcount >= 0 && argcount > type->tt_argcount)
{
semsg(_(e_toomanyarg), name);
return FAIL;
}
if (type->tt_args == NULL)
return OK; // cannot check
for (i = 0; i < argcount; ++i)
{
type_T *expected;
if (varargs && i >= type->tt_argcount - 1)
expected = type->tt_args[type->tt_argcount - 1]->tt_member;
else
expected = type->tt_args[i];
if (check_typval_type(expected, &argvars[i], i + 1) == FAIL)
return FAIL;
}
return OK;
}
/*
* Skip over a type definition and return a pointer to just after it.
* When "optional" is TRUE then a leading "?" is accepted.