mirror of
https://github.com/macvim-dev/macvim.git
synced 2026-06-11 15:37:29 +02:00
Merge remote-tracking branch 'vim/master'
This commit is contained in:
+121
-29
@@ -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
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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()
|
||||
|
||||
/*
|
||||
|
||||
@@ -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().
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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().
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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[
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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 = ¤t_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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user