Compare commits

...

18 Commits

Author SHA1 Message Date
Bram Moolenaar
801ab06934 patch 8.2.1054: not so easy to pass a lua function to Vim
Problem:    Not so easy to pass a lua function to Vim.
Solution:   Convert a Lua function and closure to a Vim funcref. (Prabir
            Shrestha, closes #6246)
2020-06-25 19:27:56 +02:00
Bram Moolenaar
832adf9bb8 patch 8.2.1053: insufficient testing for 'statusline' and 'tabline'
Problem:    Insufficient testing for 'statusline' and 'tabline'.
Solution:   Add more tests. (Yegappan Lakshmanan, closes #6333)
2020-06-25 19:01:36 +02:00
Bram Moolenaar
7acde51832 patch 8.2.1052: build failure with older compilers
Problem:    Build failure with older compilers.
Solution:   Move declaration to start of block.
2020-06-24 23:02:40 +02:00
Bram Moolenaar
ca275a05d8 patch 8.2.1051: crash when changing a list while using reduce() on it
Problem:    Crash when changing a list while using reduce() on it.
Solution:   Lock the list. (closes #6330)
2020-06-24 22:07:46 +02:00
Bram Moolenaar
65a8ed37f7 patch 8.2.1050: missing change in struct
Problem:    Missing change in struct.
Solution:   Add missing change.
2020-06-24 21:00:25 +02:00
Bram Moolenaar
b171fb1790 patch 8.2.1049: Vim9: leaking memory when using continuation line
Problem:    Vim9: leaking memory when using continuation line.
Solution:   Keep a pointer to the continuation line in evalarg_T.  Centralize
            checking for a next command.
2020-06-24 20:34:03 +02:00
Bram Moolenaar
9d40c63c7d patch 8.2.1048: build failure without the eval feature
Problem:    Build failure without the eval feature.
Solution:   Add dummy typedef.
2020-06-24 19:05:29 +02:00
Bram Moolenaar
5409f5d8c9 patch 8.2.1047: Vim9: script cannot use line continuation like :def function
Problem:    Vim9: script cannot use line continuation like in a :def function.
Solution:   Pass the getline function pointer to the eval() functions.  Use it
            for addition and multiplication operators.
2020-06-24 18:37:35 +02:00
Bram Moolenaar
b7e2483655 patch 8.2.1046: insufficient tests for src/buffer.c
Problem:    Insufficient tests for src/buffer.c.
Solution:   Add more tests.  Move comments related tests to a separate file.
            (Yegappan Lakshmanan, closes #6325)
2020-06-24 13:37:35 +02:00
Bram Moolenaar
67fbdfefd2 patch 8.2.1045: Vim9: line break before operator does not work
Problem:    Vim9: line break before operator does not work.
Solution:   Peek the next line for an operator.
2020-06-23 22:26:05 +02:00
Bram Moolenaar
ef6d86c173 patch 8.2.1044: not all systemd file types are recognized
Problem:    Not all systemd file types are recognized.
Solution:   Match several more files. (Guido Cella, closes #6319)
2020-06-23 21:01:38 +02:00
Bram Moolenaar
4014e2ceb0 patch 8.2.1043: %a item in 'statusline' not tested
Problem:    %a item in 'statusline' not tested.
Solution:   Add a test. (Dominique Pellé, closes #6318)
2020-06-23 20:00:50 +02:00
Bram Moolenaar
df069eec3b patch 8.2.1042: Vim9: cannot put an operator on the next line
Problem:    Vim9: cannot put an operator on the next line.
Solution:   Require a colon before a range to see if that causes problems.
2020-06-22 23:02:51 +02:00
Bram Moolenaar
7eaafe65ee patch 8.2.1041: test summary is missing executed count
Problem:    Test summary is missing executed count.
Solution:   Adjust pattern used for counting.
2020-06-22 22:10:06 +02:00
Bram Moolenaar
bdd2c290d3 patch 8.2.1040: not enough testing for movement commands
Problem:    Not enough testing for movement commands.
Solution:   Add more tests. (Yegappan Lakshmanan, closes #6313)
2020-06-22 21:34:30 +02:00
Bram Moolenaar
25fd267287 patch 8.2.1039: cannot put NUL byte on clipboard
Problem:    Cannot put NUL byte on clipboard.
Solution:   Use the text length. (Christian Brabandt, closes #6312,
            closes #6149)
2020-06-22 20:30:27 +02:00
Bram Moolenaar
b2b218d89b patch 8.2.1038: popupwin test fails
Problem:    Popupwin test fails.
Solution:   Fix WaitForAssert() argument.
2020-06-22 20:22:19 +02:00
Bram Moolenaar
acd4c5e914 patch 8.2.1037: Vim9: crash when using line continuation inside :def
Problem:    Vim9: crash when using line continuation inside :def.
Solution:   Check for no more lines available.
2020-06-22 19:39:03 +02:00
60 changed files with 1681 additions and 543 deletions

View File

@@ -333,6 +333,14 @@ Examples:
:lua l = d.len -- assign d as 'self'
:lua print(l())
<
Lua functions and closures are automatically converted to a Vim |Funcref| and
can be accessed in Vim scripts. Example:
>
lua <<EOF
vim.fn.timer_start(1000, function(timer)
print('timer callback')
end)
EOF
==============================================================================
7. Buffer userdata *lua-buffer*

View File

@@ -1,4 +1,4 @@
*vim9.txt* For Vim version 8.2. Last change: 2020 Jun 21
*vim9.txt* For Vim version 8.2. Last change: 2020 Jun 22
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -257,27 +257,32 @@ Function call: >
arg2
)
For binary operators iin expressions not in [], {} or () a line break is
possible AFTER the operators. For example: >
let text = lead ..
middle ..
end
For binary operators in expressions not in [], {} or () a line break is
possible just before or after the operator. For example: >
let text = lead
.. middle
.. end
let total = start +
end -
correction
let result = positive ?
PosFunc(arg) :
NegFunc(arg)
let result = positive
? PosFunc(arg)
: NegFunc(arg)
A special case is "->" for function call chains, it can appear in the next
line: >
let result = GetBuilder()
->BuilderSetWidth(333)
->BuilderSetHeight(777)
->BuilderBuild()
Note that "enddef" cannot be used at the start of a continuation line, it ends
the current function.
< *E1050*
To make it possible for the operator at the start of the line to be
recognized, it is required to put a colon before a range. This will adde
"start" and print: >
let result = start
+ print
This will assign "start" and print a line: >
let result = start
:+ print
It is also possible to split a function header over multiple lines, in between
arguments: >
@@ -286,6 +291,9 @@ arguments: >
separator = '-'
): string
Note that "enddef" cannot be used at the start of a continuation line, it ends
the current function.
No curly braces expansion ~

View File

@@ -1655,8 +1655,9 @@ au BufNewFile,BufRead *.sil setf sil
au BufNewFile,BufRead */etc/sysctl.conf,*/etc/sysctl.d/*.conf setf sysctl
" Systemd unit files
au BufNewFile,BufRead */systemd/*.{automount,mount,path,service,socket,swap,target,timer} setf systemd
au BufNewFile,BufRead */systemd/*.{automount,dnssd,link,mount,netdev,network,nspawn,path,service,slice,socket,swap,target,timer} setf systemd
" Systemd overrides
au BufNewFile,BufRead */etc/systemd/*.conf.d/*.conf setf systemd
au BufNewFile,BufRead */etc/systemd/system/*.d/*.conf setf systemd
au BufNewFile,BufRead */.config/systemd/user/*.d/*.conf setf systemd
" Systemd temp files

View File

@@ -285,7 +285,7 @@ general_beval_cb(BalloonEval *beval, int state UNUSED)
++textwinlock;
vim_free(result);
result = eval_to_string(bexpr, NULL, TRUE);
result = eval_to_string(bexpr, TRUE);
// Remove one trailing newline, it is added when the result was a
// list and it's hardly ever useful. If the user really wants a

View File

@@ -4094,7 +4094,7 @@ build_stl_str_hl(
tv.vval.v_number = wp->w_id;
set_var((char_u *)"g:statusline_winid", &tv, FALSE);
usefmt = eval_to_string_safe(fmt + 2, NULL, use_sandbox);
usefmt = eval_to_string_safe(fmt + 2, use_sandbox);
if (usefmt == NULL)
usefmt = fmt;
@@ -4434,7 +4434,7 @@ build_stl_str_hl(
if (curwin != save_curwin)
VIsual_active = FALSE;
str = eval_to_string_safe(p, &t, use_sandbox);
str = eval_to_string_safe(p, use_sandbox);
curwin = save_curwin;
curbuf = save_curbuf;

View File

@@ -86,7 +86,7 @@ eval_client_expr_to_string(char_u *expr)
// to be typed. Do generate errors so that try/catch works.
++emsg_silent;
res = eval_to_string(expr, NULL, TRUE);
res = eval_to_string(expr, TRUE);
debug_break_level = save_dbl;
redir_off = save_ro;

View File

@@ -788,12 +788,14 @@ get_literal_key(char_u **arg, typval_T *tv)
/*
* Allocate a variable for a Dictionary and fill it from "*arg".
* "literal" is TRUE for #{key: val}
* "flags" can have EVAL_EVALUATE and other EVAL_ flags.
* Return OK or FAIL. Returns NOTDONE for {expr}.
*/
int
eval_dict(char_u **arg, typval_T *rettv, int flags, int literal)
{
int evaluate = flags & EVAL_EVALUATE;
evalarg_T evalarg;
dict_T *d = NULL;
typval_T tvkey;
typval_T tv;
@@ -803,6 +805,9 @@ eval_dict(char_u **arg, typval_T *rettv, int flags, int literal)
char_u buf[NUMBUFLEN];
int vim9script = current_sctx.sc_version == SCRIPT_VERSION_VIM9;
CLEAR_FIELD(evalarg);
evalarg.eval_flags = flags;
/*
* First check if it's not a curly-braces thing: {expr}.
* Must do this without evaluating, otherwise a function may be called
@@ -812,7 +817,7 @@ eval_dict(char_u **arg, typval_T *rettv, int flags, int literal)
*/
if (!vim9script && *start != '}')
{
if (eval1(&start, &tv, 0) == FAIL) // recursive!
if (eval1(&start, &tv, NULL) == FAIL) // recursive!
return FAIL;
if (*start == '}')
return NOTDONE;
@@ -832,7 +837,7 @@ eval_dict(char_u **arg, typval_T *rettv, int flags, int literal)
{
if ((literal
? get_literal_key(arg, &tvkey)
: eval1(arg, &tvkey, flags)) == FAIL) // recursive!
: eval1(arg, &tvkey, &evalarg)) == FAIL) // recursive!
goto failret;
if (**arg != ':')
@@ -854,7 +859,7 @@ eval_dict(char_u **arg, typval_T *rettv, int flags, int literal)
}
*arg = skipwhite(*arg + 1);
if (eval1(arg, &tv, flags) == FAIL) // recursive!
if (eval1(arg, &tv, &evalarg) == FAIL) // recursive!
{
if (evaluate)
clear_tv(&tvkey);

View File

@@ -45,12 +45,12 @@ typedef struct
} forinfo_T;
static int tv_op(typval_T *tv1, typval_T *tv2, char_u *op);
static int eval2(char_u **arg, typval_T *rettv, int flags);
static int eval3(char_u **arg, typval_T *rettv, int flags);
static int eval4(char_u **arg, typval_T *rettv, int flags);
static int eval5(char_u **arg, typval_T *rettv, int flags);
static int eval6(char_u **arg, typval_T *rettv, int flags, int want_string);
static int eval7(char_u **arg, typval_T *rettv, int flags, int want_string);
static int eval2(char_u **arg, typval_T *rettv, evalarg_T *evalarg);
static int eval3(char_u **arg, typval_T *rettv, evalarg_T *evalarg);
static int eval4(char_u **arg, typval_T *rettv, evalarg_T *evalarg);
static int eval5(char_u **arg, typval_T *rettv, evalarg_T *evalarg);
static int eval6(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int want_string);
static int eval7(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int want_string);
static int eval7_leader(typval_T *rettv, char_u *start_leader, char_u **end_leaderp);
static int free_unref_items(int copyID);
@@ -161,7 +161,7 @@ eval_clear(void)
eval_to_bool(
char_u *arg,
int *error,
char_u **nextcmd,
exarg_T *eap,
int skip) // only parse, don't execute
{
typval_T tv;
@@ -169,7 +169,7 @@ eval_to_bool(
if (skip)
++emsg_skip;
if (eval0(arg, &tv, nextcmd, skip ? 0 : EVAL_EVALUATE) == FAIL)
if (eval0(arg, &tv, eap, skip ? NULL : &EVALARG_EVALUATE) == FAIL)
*error = TRUE;
else
{
@@ -197,7 +197,7 @@ eval1_emsg(char_u **arg, typval_T *rettv, int evaluate)
int did_emsg_before = did_emsg;
int called_emsg_before = called_emsg;
ret = eval1(arg, rettv, evaluate ? EVAL_EVALUATE : 0);
ret = eval1(arg, rettv, evaluate ? &EVALARG_EVALUATE : NULL);
if (ret == FAIL)
{
// Report the invalid expression unless the expression evaluation has
@@ -317,7 +317,7 @@ eval_expr_to_bool(typval_T *expr, int *error)
char_u *
eval_to_string_skip(
char_u *arg,
char_u **nextcmd,
exarg_T *eap,
int skip) // only parse, don't execute
{
typval_T tv;
@@ -325,7 +325,8 @@ eval_to_string_skip(
if (skip)
++emsg_skip;
if (eval0(arg, &tv, nextcmd, skip ? 0 : EVAL_EVALUATE) == FAIL || skip)
if (eval0(arg, &tv, eap, skip ? NULL : &EVALARG_EVALUATE)
== FAIL || skip)
retval = NULL;
else
{
@@ -348,7 +349,7 @@ skip_expr(char_u **pp)
typval_T rettv;
*pp = skipwhite(*pp);
return eval1(pp, &rettv, 0);
return eval1(pp, &rettv, NULL);
}
/*
@@ -360,7 +361,6 @@ skip_expr(char_u **pp)
char_u *
eval_to_string(
char_u *arg,
char_u **nextcmd,
int convert)
{
typval_T tv;
@@ -370,7 +370,7 @@ eval_to_string(
char_u numbuf[NUMBUFLEN];
#endif
if (eval0(arg, &tv, nextcmd, EVAL_EVALUATE) == FAIL)
if (eval0(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL)
retval = NULL;
else
{
@@ -408,7 +408,6 @@ eval_to_string(
char_u *
eval_to_string_safe(
char_u *arg,
char_u **nextcmd,
int use_sandbox)
{
char_u *retval;
@@ -418,7 +417,7 @@ eval_to_string_safe(
if (use_sandbox)
++sandbox;
++textwinlock;
retval = eval_to_string(arg, nextcmd, FALSE);
retval = eval_to_string(arg, FALSE);
if (use_sandbox)
--sandbox;
--textwinlock;
@@ -440,7 +439,7 @@ eval_to_number(char_u *expr)
++emsg_off;
if (eval1(&p, &rettv, EVAL_EVALUATE) == FAIL)
if (eval1(&p, &rettv, &EVALARG_EVALUATE) == FAIL)
retval = -1;
else
{
@@ -458,12 +457,12 @@ eval_to_number(char_u *expr)
* Returns NULL when there is an error.
*/
typval_T *
eval_expr(char_u *arg, char_u **nextcmd)
eval_expr(char_u *arg, exarg_T *eap)
{
typval_T *tv;
tv = ALLOC_ONE(typval_T);
if (tv != NULL && eval0(arg, tv, nextcmd, EVAL_EVALUATE) == FAIL)
if (tv != NULL && eval0(arg, tv, eap, &EVALARG_EVALUATE) == FAIL)
VIM_CLEAR(tv);
return tv;
@@ -588,7 +587,7 @@ eval_foldexpr(char_u *arg, int *cp)
++sandbox;
++textwinlock;
*cp = NUL;
if (eval0(arg, &tv, NULL, EVAL_EVALUATE) == FAIL)
if (eval0(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL)
retval = 0;
else
{
@@ -776,7 +775,7 @@ get_lval(
else
{
empty1 = FALSE;
if (eval1(&p, &var1, EVAL_EVALUATE) == FAIL) // recursive!
if (eval1(&p, &var1, &EVALARG_EVALUATE) == FAIL) // recursive!
return NULL;
if (tv_get_string_chk(&var1) == NULL)
{
@@ -814,7 +813,7 @@ get_lval(
{
lp->ll_empty2 = FALSE;
// recursive!
if (eval1(&p, &var2, EVAL_EVALUATE) == FAIL)
if (eval1(&p, &var2, &EVALARG_EVALUATE) == FAIL)
{
clear_tv(&var1);
return NULL;
@@ -1417,14 +1416,17 @@ tv_op(typval_T *tv1, typval_T *tv2, char_u *op)
eval_for_line(
char_u *arg,
int *errp,
char_u **nextcmdp,
exarg_T *eap,
int skip)
{
forinfo_T *fi;
char_u *expr;
typval_T tv;
list_T *l;
evalarg_T evalarg;
CLEAR_FIELD(evalarg);
evalarg.eval_flags = skip ? 0 : EVAL_EVALUATE;
*errp = TRUE; // default: there is an error
fi = ALLOC_CLEAR_ONE(forinfo_T);
@@ -1444,8 +1446,7 @@ eval_for_line(
if (skip)
++emsg_skip;
if (eval0(skipwhite(expr + 2), &tv, nextcmdp, skip ? 0 : EVAL_EVALUATE)
== OK)
if (eval0(skipwhite(expr + 2), &tv, eap, &evalarg) == OK)
{
*errp = FALSE;
if (!skip)
@@ -1763,6 +1764,46 @@ eval_func(
return ret;
}
/*
* If inside Vim9 script, "arg" points to the end of a line (ignoring comments)
* and there is a next line, return the next line (skipping blanks) and set
* "getnext".
* Otherwise just return "arg" unmodified and set "getnext" to FALSE.
* "arg" must point somewhere inside a line, not at the start.
*/
static char_u *
eval_next_non_blank(char_u *arg, evalarg_T *evalarg, int *getnext)
{
*getnext = FALSE;
if (current_sctx.sc_version == SCRIPT_VERSION_VIM9
&& evalarg != NULL
&& evalarg->eval_cookie != NULL
&& (*arg == NUL || (VIM_ISWHITE(arg[-1])
&& (*arg == '"' || *arg == '#')))
&& source_nextline(evalarg->eval_cookie) != NULL)
{
char_u *p = source_nextline(evalarg->eval_cookie);
if (p != NULL)
{
*getnext = TRUE;
return skipwhite(p);
}
}
return arg;
}
/*
* To be called when eval_next_non_blank() sets "getnext" to TRUE.
*/
static char_u *
eval_next_line(evalarg_T *evalarg)
{
vim_free(evalarg->eval_tofree);
evalarg->eval_tofree = getsourceline(0, evalarg->eval_cookie, 0, TRUE);
return skipwhite(evalarg->eval_tofree);
}
/*
* The "evaluate" argument: When FALSE, the argument is only parsed but not
* executed. The function may return OK, but the rettv will be of type
@@ -1774,23 +1815,27 @@ eval_func(
* This calls eval1() and handles error message and nextcmd.
* Put the result in "rettv" when returning OK and "evaluate" is TRUE.
* Note: "rettv.v_lock" is not set.
* "flags" has EVAL_EVALUATE and similar flags.
* "evalarg" can be NULL, EVALARG_EVALUATE or a pointer.
* Return OK or FAIL.
*/
int
eval0(
char_u *arg,
typval_T *rettv,
char_u **nextcmd,
int flags)
exarg_T *eap,
evalarg_T *evalarg)
{
int ret;
char_u *p;
int did_emsg_before = did_emsg;
int called_emsg_before = called_emsg;
int flags = evalarg == NULL ? 0 : evalarg->eval_flags;
if (evalarg != NULL)
evalarg->eval_tofree = NULL;
p = skipwhite(arg);
ret = eval1(&p, rettv, flags);
ret = eval1(&p, rettv, evalarg);
if (ret == FAIL || !ends_excmd2(arg, p))
{
if (ret != FAIL)
@@ -1808,8 +1853,27 @@ eval0(
semsg(_(e_invexpr2), arg);
ret = FAIL;
}
if (nextcmd != NULL)
*nextcmd = check_nextcmd(p);
if (eap != NULL)
eap->nextcmd = check_nextcmd(p);
if (evalarg != NULL)
{
if (eap != NULL)
{
if (evalarg->eval_tofree != NULL)
{
// We may need to keep the original command line, e.g. for
// ":let" it has the variable names. But we may also need the
// new one, "nextcmd" points into it. Keep both.
vim_free(eap->cmdline_tofree);
eap->cmdline_tofree = *eap->cmdlinep;
*eap->cmdlinep = evalarg->eval_tofree;
}
}
else
vim_free(evalarg->eval_tofree);
}
return ret;
}
@@ -1826,23 +1890,36 @@ eval0(
* Return OK or FAIL.
*/
int
eval1(char_u **arg, typval_T *rettv, int flags)
eval1(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
{
int result;
typval_T var2;
/*
* Get the first variable.
*/
if (eval2(arg, rettv, flags) == FAIL)
if (eval2(arg, rettv, evalarg) == FAIL)
return FAIL;
if ((*arg)[0] == '?')
{
int evaluate = flags & EVAL_EVALUATE;
int result;
typval_T var2;
evalarg_T nested_evalarg;
int orig_flags;
int evaluate;
if (evalarg == NULL)
{
CLEAR_FIELD(nested_evalarg);
orig_flags = 0;
}
else
{
nested_evalarg = *evalarg;
orig_flags = evalarg->eval_flags;
}
evaluate = nested_evalarg.eval_flags & EVAL_EVALUATE;
result = FALSE;
if (flags & EVAL_EVALUATE)
if (evaluate)
{
int error = FALSE;
@@ -1857,7 +1934,9 @@ eval1(char_u **arg, typval_T *rettv, int flags)
* Get the second variable. Recursive!
*/
*arg = skipwhite(*arg + 1);
if (eval1(arg, rettv, result ? flags : flags & ~EVAL_EVALUATE) == FAIL)
nested_evalarg.eval_flags = result ? orig_flags
: orig_flags & ~EVAL_EVALUATE;
if (eval1(arg, rettv, &nested_evalarg) == FAIL)
return FAIL;
/*
@@ -1875,7 +1954,9 @@ eval1(char_u **arg, typval_T *rettv, int flags)
* Get the third variable. Recursive!
*/
*arg = skipwhite(*arg + 1);
if (eval1(arg, &var2, !result ? flags : flags & ~EVAL_EVALUATE) == FAIL)
nested_evalarg.eval_flags = !result ? orig_flags
: orig_flags & ~EVAL_EVALUATE;
if (eval1(arg, &var2, &nested_evalarg) == FAIL)
{
if (evaluate && result)
clear_tv(rettv);
@@ -1898,7 +1979,7 @@ eval1(char_u **arg, typval_T *rettv, int flags)
* Return OK or FAIL.
*/
static int
eval2(char_u **arg, typval_T *rettv, int flags)
eval2(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
{
typval_T var2;
long result;
@@ -1908,7 +1989,7 @@ eval2(char_u **arg, typval_T *rettv, int flags)
/*
* Get the first variable.
*/
if (eval3(arg, rettv, flags) == FAIL)
if (eval3(arg, rettv, evalarg) == FAIL)
return FAIL;
/*
@@ -1918,7 +1999,22 @@ eval2(char_u **arg, typval_T *rettv, int flags)
result = FALSE;
while ((*arg)[0] == '|' && (*arg)[1] == '|')
{
int evaluate = flags & EVAL_EVALUATE;
evalarg_T nested_evalarg;
int evaluate;
int orig_flags;
if (evalarg == NULL)
{
CLEAR_FIELD(nested_evalarg);
orig_flags = 0;
evaluate = FALSE;
}
else
{
nested_evalarg = *evalarg;
orig_flags = evalarg->eval_flags;
evaluate = orig_flags & EVAL_EVALUATE;
}
if (evaluate && first)
{
@@ -1934,8 +2030,9 @@ eval2(char_u **arg, typval_T *rettv, int flags)
* Get the second variable.
*/
*arg = skipwhite(*arg + 2);
if (eval3(arg, &var2, !result ? flags : flags & ~EVAL_EVALUATE)
== FAIL)
nested_evalarg.eval_flags = !result ? orig_flags
: orig_flags & ~EVAL_EVALUATE;
if (eval3(arg, &var2, &nested_evalarg) == FAIL)
return FAIL;
/*
@@ -1969,7 +2066,7 @@ eval2(char_u **arg, typval_T *rettv, int flags)
* Return OK or FAIL.
*/
static int
eval3(char_u **arg, typval_T *rettv, int flags)
eval3(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
{
typval_T var2;
long result;
@@ -1979,7 +2076,7 @@ eval3(char_u **arg, typval_T *rettv, int flags)
/*
* Get the first variable.
*/
if (eval4(arg, rettv, flags) == FAIL)
if (eval4(arg, rettv, evalarg) == FAIL)
return FAIL;
/*
@@ -1989,8 +2086,22 @@ eval3(char_u **arg, typval_T *rettv, int flags)
result = TRUE;
while ((*arg)[0] == '&' && (*arg)[1] == '&')
{
int evaluate = flags & EVAL_EVALUATE;
evalarg_T nested_evalarg;
int orig_flags;
int evaluate;
if (evalarg == NULL)
{
CLEAR_FIELD(nested_evalarg);
orig_flags = 0;
evaluate = FALSE;
}
else
{
nested_evalarg = *evalarg;
orig_flags = evalarg->eval_flags;
evaluate = orig_flags & EVAL_EVALUATE;
}
if (evaluate && first)
{
if (tv_get_number_chk(rettv, &error) == 0)
@@ -2005,7 +2116,9 @@ eval3(char_u **arg, typval_T *rettv, int flags)
* Get the second variable.
*/
*arg = skipwhite(*arg + 2);
if (eval4(arg, &var2, result ? flags : flags & ~EVAL_EVALUATE) == FAIL)
nested_evalarg.eval_flags = result ? orig_flags
: orig_flags & ~EVAL_EVALUATE;
if (eval4(arg, &var2, &nested_evalarg) == FAIL)
return FAIL;
/*
@@ -2048,7 +2161,7 @@ eval3(char_u **arg, typval_T *rettv, int flags)
* Return OK or FAIL.
*/
static int
eval4(char_u **arg, typval_T *rettv, int flags)
eval4(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
{
typval_T var2;
char_u *p;
@@ -2060,7 +2173,7 @@ eval4(char_u **arg, typval_T *rettv, int flags)
/*
* Get the first variable.
*/
if (eval5(arg, rettv, flags) == FAIL)
if (eval5(arg, rettv, evalarg) == FAIL)
return FAIL;
p = *arg;
@@ -2128,12 +2241,12 @@ eval4(char_u **arg, typval_T *rettv, int flags)
* Get the second variable.
*/
*arg = skipwhite(p + len);
if (eval5(arg, &var2, flags) == FAIL)
if (eval5(arg, &var2, evalarg) == FAIL)
{
clear_tv(rettv);
return FAIL;
}
if (flags & EVAL_EVALUATE)
if (evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE))
{
int ret = typval_compare(rettv, &var2, type, ic);
@@ -2195,23 +2308,14 @@ eval_addlist(typval_T *tv1, typval_T *tv2)
* Return OK or FAIL.
*/
static int
eval5(char_u **arg, typval_T *rettv, int flags)
eval5(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
{
typval_T var2;
int op;
varnumber_T n1, n2;
#ifdef FEAT_FLOAT
float_T f1 = 0, f2 = 0;
#endif
char_u *s1, *s2;
char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN];
char_u *p;
int concat;
int evaluate = evalarg == NULL ? 0 : (evalarg->eval_flags & EVAL_EVALUATE);
/*
* Get the first variable.
*/
if (eval6(arg, rettv, flags, FALSE) == FAIL)
if (eval6(arg, rettv, evalarg, FALSE) == FAIL)
return FAIL;
/*
@@ -2219,12 +2323,20 @@ eval5(char_u **arg, typval_T *rettv, int flags)
*/
for (;;)
{
int getnext;
char_u *p;
int op;
int concat;
typval_T var2;
// "." is only string concatenation when scriptversion is 1
op = **arg;
concat = op == '.'
&& (*(*arg + 1) == '.' || current_sctx.sc_version < 2);
p = eval_next_non_blank(*arg, evalarg, &getnext);
op = *p;
concat = op == '.' && (*(p + 1) == '.' || current_sctx.sc_version < 2);
if (op != '+' && op != '-' && !concat)
break;
if (getnext)
*arg = eval_next_line(evalarg);
if ((op != '+' || (rettv->v_type != VAR_LIST
&& rettv->v_type != VAR_BLOB))
@@ -2240,7 +2352,7 @@ eval5(char_u **arg, typval_T *rettv, int flags)
// we know that the first operand needs to be a string or number
// without evaluating the 2nd operand. So check before to avoid
// side effects after an error.
if ((flags & EVAL_EVALUATE) && tv_get_string_chk(rettv) == NULL)
if (evaluate && tv_get_string_chk(rettv) == NULL)
{
clear_tv(rettv);
return FAIL;
@@ -2253,21 +2365,23 @@ eval5(char_u **arg, typval_T *rettv, int flags)
if (op == '.' && *(*arg + 1) == '.') // .. string concatenation
++*arg;
*arg = skipwhite(*arg + 1);
if (eval6(arg, &var2, flags, op == '.') == FAIL)
if (eval6(arg, &var2, evalarg, op == '.') == FAIL)
{
clear_tv(rettv);
return FAIL;
}
if (flags & EVAL_EVALUATE)
if (evaluate)
{
/*
* Compute the result.
*/
if (op == '.')
{
s1 = tv_get_string_buf(rettv, buf1); // already checked
s2 = tv_get_string_buf_chk(&var2, buf2);
char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN];
char_u *s1 = tv_get_string_buf(rettv, buf1);
char_u *s2 = tv_get_string_buf_chk(&var2, buf2);
if (s2 == NULL) // type error ?
{
clear_tv(rettv);
@@ -2290,9 +2404,11 @@ eval5(char_u **arg, typval_T *rettv, int flags)
}
else
{
int error = FALSE;
int error = FALSE;
varnumber_T n1, n2;
#ifdef FEAT_FLOAT
float_T f1 = 0, f2 = 0;
if (rettv->v_type == VAR_FLOAT)
{
f1 = rettv->vval.v_float;
@@ -2381,7 +2497,7 @@ eval5(char_u **arg, typval_T *rettv, int flags)
eval6(
char_u **arg,
typval_T *rettv,
int flags,
evalarg_T *evalarg,
int want_string) // after "." operator
{
typval_T var2;
@@ -2396,7 +2512,7 @@ eval6(
/*
* Get the first variable.
*/
if (eval7(arg, rettv, flags, want_string) == FAIL)
if (eval7(arg, rettv, evalarg, want_string) == FAIL)
return FAIL;
/*
@@ -2404,11 +2520,17 @@ eval6(
*/
for (;;)
{
op = **arg;
int evaluate = evalarg == NULL ? 0
: (evalarg->eval_flags & EVAL_EVALUATE);
int getnext;
op = *eval_next_non_blank(*arg, evalarg, &getnext);
if (op != '*' && op != '/' && op != '%')
break;
if (getnext)
*arg = eval_next_line(evalarg);
if (flags & EVAL_EVALUATE)
if (evaluate)
{
#ifdef FEAT_FLOAT
if (rettv->v_type == VAR_FLOAT)
@@ -2431,10 +2553,10 @@ eval6(
* Get the second variable.
*/
*arg = skipwhite(*arg + 1);
if (eval7(arg, &var2, flags, FALSE) == FAIL)
if (eval7(arg, &var2, evalarg, FALSE) == FAIL)
return FAIL;
if (flags & EVAL_EVALUATE)
if (evaluate)
{
#ifdef FEAT_FLOAT
if (var2.v_type == VAR_FLOAT)
@@ -2551,10 +2673,12 @@ eval6(
eval7(
char_u **arg,
typval_T *rettv,
int flags,
evalarg_T *evalarg,
int want_string) // after "." operator
{
int evaluate = flags & EVAL_EVALUATE;
int flags = evalarg == NULL ? 0 : evalarg->eval_flags;
int evaluate = evalarg != NULL
&& (evalarg->eval_flags & EVAL_EVALUATE);
int len;
char_u *s;
char_u *start_leader, *end_leader;
@@ -2672,15 +2796,17 @@ eval7(
/*
* nested expression: (expression).
*/
case '(': *arg = skipwhite(*arg + 1);
ret = eval1(arg, rettv, flags); // recursive!
if (**arg == ')')
++*arg;
else if (ret == OK)
{
emsg(_(e_missing_close));
clear_tv(rettv);
ret = FAIL;
case '(': {
*arg = skipwhite(*arg + 1);
ret = eval1(arg, rettv, evalarg); // recursive!
if (**arg == ')')
++*arg;
else if (ret == OK)
{
emsg(_(e_missing_close));
clear_tv(rettv);
ret = FAIL;
}
}
break;
@@ -3030,6 +3156,11 @@ eval_index(
}
else
{
evalarg_T evalarg;
CLEAR_FIELD(evalarg);
evalarg.eval_flags = flags;
/*
* something[idx]
*
@@ -3038,7 +3169,7 @@ eval_index(
*arg = skipwhite(*arg + 1);
if (**arg == ':')
empty1 = TRUE;
else if (eval1(arg, &var1, flags) == FAIL) // recursive!
else if (eval1(arg, &var1, &evalarg) == FAIL) // recursive!
return FAIL;
else if (evaluate && tv_get_string_chk(&var1) == NULL)
{
@@ -3056,7 +3187,7 @@ eval_index(
*arg = skipwhite(*arg + 1);
if (**arg == ']')
empty2 = TRUE;
else if (eval1(arg, &var2, flags) == FAIL) // recursive!
else if (eval1(arg, &var2, &evalarg) == FAIL) // recursive!
{
if (!empty1)
clear_tv(&var1);
@@ -4634,7 +4765,6 @@ make_expanded_name(
char_u c1;
char_u *retval = NULL;
char_u *temp_result;
char_u *nextcmd = NULL;
if (expr_end == NULL || in_end == NULL)
return NULL;
@@ -4643,8 +4773,8 @@ make_expanded_name(
c1 = *in_end;
*in_end = NUL;
temp_result = eval_to_string(expr_start + 1, &nextcmd, FALSE);
if (temp_result != NULL && nextcmd == NULL)
temp_result = eval_to_string(expr_start + 1, FALSE);
if (temp_result != NULL)
{
retval = alloc(STRLEN(temp_result) + (expr_start - in_start)
+ (in_end - expr_end) + 1);
@@ -4947,6 +5077,10 @@ ex_echo(exarg_T *eap)
int atstart = TRUE;
int did_emsg_before = did_emsg;
int called_emsg_before = called_emsg;
evalarg_T evalarg;
CLEAR_FIELD(evalarg);
evalarg.eval_flags = eap->skip ? 0 : EVAL_EVALUATE;
if (eap->skip)
++emsg_skip;
@@ -4957,7 +5091,7 @@ ex_echo(exarg_T *eap)
need_clr_eos = needclr;
p = arg;
if (eval1(&arg, &rettv, eap->skip ? 0 : EVAL_EVALUATE) == FAIL)
if (eval1(&arg, &rettv, &evalarg) == FAIL)
{
/*
* Report the invalid expression unless the expression evaluation

View File

@@ -2084,7 +2084,7 @@ f_eval(typval_T *argvars, typval_T *rettv)
s = skipwhite(s);
p = s;
if (s == NULL || eval1(&s, rettv, EVAL_EVALUATE) == FAIL)
if (s == NULL || eval1(&s, rettv, &EVALARG_EVALUATE) == FAIL)
{
if (p != NULL && !aborting())
semsg(_(e_invexpr2), p);

View File

@@ -435,7 +435,7 @@ eval_spell_expr(char_u *badword, char_u *expr)
if (p_verbose == 0)
++emsg_off;
if (eval1(&p, &rettv, EVAL_EVALUATE) == OK)
if (eval1(&p, &rettv, &EVALARG_EVALUATE) == OK)
{
if (rettv.v_type != VAR_LIST)
clear_tv(&rettv);
@@ -774,7 +774,7 @@ ex_let(exarg_T *eap)
}
else
{
int eval_flags;
evalarg_T evalarg;
rettv.v_type = VAR_UNKNOWN;
i = FAIL;
@@ -797,14 +797,17 @@ ex_let(exarg_T *eap)
if (eap->skip)
++emsg_skip;
eval_flags = eap->skip ? 0 : EVAL_EVALUATE;
i = eval0(expr, &rettv, &eap->nextcmd, eval_flags);
evalarg.eval_flags = eap->skip ? 0 : EVAL_EVALUATE;
evalarg.eval_cookie = eap->getline == getsourceline
? eap->cookie : NULL;
i = eval0(expr, &rettv, eap, &evalarg);
if (eap->skip)
--emsg_skip;
}
if (eap->skip)
{
if (i != FAIL)
clear_tv(&rettv);
--emsg_skip;
}
else if (i != FAIL)
{

View File

@@ -1841,6 +1841,9 @@ struct exarg
char_u *nextcmd; // next command (NULL if none)
char_u *cmd; // the name of the command (except for :make)
char_u **cmdlinep; // pointer to pointer of allocated cmdline
#ifdef FEAT_EVAL
char_u *cmdline_tofree; // free later
#endif
cmdidx_T cmdidx; // the index for the command
long argt; // flags for the command
int skip; // don't execute the command, only parse it

View File

@@ -1729,7 +1729,14 @@ do_one_cmd(
#ifdef FEAT_EVAL
if (current_sctx.sc_version == SCRIPT_VERSION_VIM9 && !starts_with_colon)
{
if (ea.cmd > cmd)
{
emsg(_(e_colon_required));
goto doend;
}
p = find_ex_command(&ea, NULL, lookup_scriptvar, NULL);
}
else
#endif
p = find_ex_command(&ea, NULL, NULL, NULL);
@@ -2602,6 +2609,7 @@ doend:
#ifdef FEAT_EVAL
--ex_nesting_level;
vim_free(ea.cmdline_tofree);
#endif
return ea.nextcmd;
@@ -3446,7 +3454,7 @@ excmd_get_argt(cmdidx_T idx)
* Backslashed delimiters after / or ? will be skipped, and commands will
* not be expanded between /'s and ?'s or after "'".
*
* Also skip white space and ":" characters.
* Also skip white space and ":" characters after the range.
* Returns the "cmd" pointer advanced to beyond the range.
*/
char_u *
@@ -4905,7 +4913,7 @@ ex_colorscheme(exarg_T *eap)
if (expr != NULL)
{
++emsg_off;
p = eval_to_string(expr, NULL, FALSE);
p = eval_to_string(expr, FALSE);
--emsg_off;
vim_free(expr);
}

View File

@@ -895,9 +895,12 @@ report_discard_pending(int pending, void *value)
ex_eval(exarg_T *eap)
{
typval_T tv;
evalarg_T evalarg;
if (eval0(eap->arg, &tv, &eap->nextcmd, eap->skip ? 0 : EVAL_EVALUATE)
== OK)
evalarg.eval_flags = eap->skip ? 0 : EVAL_EVALUATE;
evalarg.eval_cookie = eap->getline == getsourceline ? eap->cookie : NULL;
if (eval0(eap->arg, &tv, eap, &evalarg) == OK)
clear_tv(&tv);
}
@@ -926,7 +929,7 @@ ex_if(exarg_T *eap)
skip = did_emsg || got_int || did_throw || (cstack->cs_idx > 0
&& !(cstack->cs_flags[cstack->cs_idx - 1] & CSF_ACTIVE));
result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip);
result = eval_to_bool(eap->arg, &error, eap, skip);
if (!skip && !error)
{
@@ -1038,7 +1041,7 @@ ex_else(exarg_T *eap)
if (eap->cmdidx == CMD_elseif)
{
result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip);
result = eval_to_bool(eap->arg, &error, eap, skip);
// When throwing error exceptions, we want to throw always the first
// of several errors in a row. This is what actually happens when
@@ -1100,7 +1103,7 @@ ex_while(exarg_T *eap)
/*
* ":while bool-expr"
*/
result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip);
result = eval_to_bool(eap->arg, &error, eap, skip);
}
else
{
@@ -1119,7 +1122,7 @@ ex_while(exarg_T *eap)
else
{
// Evaluate the argument and get the info in a structure.
fi = eval_for_line(eap->arg, &error, &eap->nextcmd, skip);
fi = eval_for_line(eap->arg, &error, eap, skip);
cstack->cs_forinfo[cstack->cs_idx] = fi;
}
@@ -1319,7 +1322,7 @@ ex_throw(exarg_T *eap)
char_u *value;
if (*arg != NUL && *arg != '|' && *arg != '\n')
value = eval_to_string_skip(arg, &eap->nextcmd, eap->skip);
value = eval_to_string_skip(arg, eap, eap->skip);
else
{
emsg(_(e_argreq));

View File

@@ -3083,7 +3083,7 @@ expand_backtick(
#ifdef FEAT_EVAL
if (*cmd == '=') // `={expr}`: Expand expression
buffer = eval_to_string(cmd + 1, &p, TRUE);
buffer = eval_to_string(cmd + 1, TRUE);
else
#endif
buffer = get_cmd_output(cmd, NULL,

View File

@@ -2079,7 +2079,7 @@ eval_includeexpr(char_u *ptr, int len)
char_u *res;
set_vim_var_string(VV_FNAME, ptr, len);
res = eval_to_string_safe(curbuf->b_p_inex, NULL,
res = eval_to_string_safe(curbuf->b_p_inex,
was_set_insecurely((char_u *)"includeexpr", OPT_LOCAL));
set_vim_var_string(VV_FNAME, NULL, 0);
return res;

View File

@@ -1928,7 +1928,7 @@ get_foldtext(
curbuf = wp->w_buffer;
++emsg_silent; // handle exceptions, but don't display errors
text = eval_to_string_safe(wp->w_p_fdt, NULL,
text = eval_to_string_safe(wp->w_p_fdt,
was_set_insecurely((char_u *)"foldtext", OPT_LOCAL));
--emsg_silent;

View File

@@ -1790,6 +1790,7 @@ EXTERN char e_const_req_value[] INIT(= N_("E1021: const requires a value"));
EXTERN char e_type_req[] INIT(= N_("E1022: type or initialization required"));
EXTERN char e_declare_var[] INIT(= N_("E1016: Cannot declare a %s variable: %s"));
EXTERN char e_declare_env_var[] INIT(= N_("E1016: Cannot declare an environment variable: %s"));
EXTERN char e_colon_required[] INIT(= N_("E1050: Colon required before a range"));
#endif
#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
EXTERN char e_alloc_color[] INIT(= N_("E254: Cannot allocate color %s"));
@@ -1879,6 +1880,9 @@ EXTERN char windowsVersion[20] INIT(= {0});
// Used for lv_first in a non-materialized range() list.
EXTERN listitem_T range_list_item;
// Passed to an eval() function to enable evaluation.
EXTERN evalarg_T EVALARG_EVALUATE INIT3(EVAL_EVALUATE, NULL, NULL);
#endif
#ifdef MSWIN

View File

@@ -35,6 +35,13 @@ typedef struct {
} luaV_Funcref;
typedef void (*msgfunc_T)(char_u *);
typedef struct {
int lua_funcref; // ref to a lua func
int lua_tableref; // ref to a lua table if metatable else LUA_NOREF. used
// for __call
lua_State *L;
} luaV_CFuncState;
static const char LUAVIM_DICT[] = "dict";
static const char LUAVIM_LIST[] = "list";
static const char LUAVIM_BLOB[] = "blob";
@@ -45,6 +52,8 @@ static const char LUAVIM_FREE[] = "luaV_free";
static const char LUAVIM_LUAEVAL[] = "luaV_luaeval";
static const char LUAVIM_SETREF[] = "luaV_setref";
static const char LUA___CALL[] = "__call";
// most functions are closures with a cache table as first upvalue;
// get/setudata manage references to vim userdata in cache table through
// object pointers (light userdata)
@@ -64,7 +73,7 @@ static const char LUAVIM_SETREF[] = "luaV_setref";
#define luaV_emsg(L) luaV_msgfunc((L), (msgfunc_T) emsg)
#define luaV_checktypval(L, a, v, msg) \
do { \
if (luaV_totypval(L, a, v) == FAIL) \
if (luaV_totypval(L, a, v) == FAIL) \
luaL_error(L, msg ": cannot convert value"); \
} while (0)
@@ -72,6 +81,8 @@ static luaV_List *luaV_pushlist(lua_State *L, list_T *lis);
static luaV_Dict *luaV_pushdict(lua_State *L, dict_T *dic);
static luaV_Blob *luaV_pushblob(lua_State *L, blob_T *blo);
static luaV_Funcref *luaV_pushfuncref(lua_State *L, char_u *name);
static int luaV_call_lua_func(int argcount, typval_T *argvars, typval_T *rettv, void *state);
static void luaV_call_lua_func_free(void *state);
#if LUA_VERSION_NUM <= 501
#define luaV_openlib(L, l, n) luaL_openlib(L, NULL, l, n)
@@ -591,6 +602,45 @@ luaV_totypval(lua_State *L, int pos, typval_T *tv)
tv->vval.v_number = (varnumber_T) lua_tointeger(L, pos);
#endif
break;
case LUA_TFUNCTION:
{
char_u *name;
lua_pushvalue(L, pos);
luaV_CFuncState *state = ALLOC_CLEAR_ONE(luaV_CFuncState);
state->lua_funcref = luaL_ref(L, LUA_REGISTRYINDEX);
state->L = L;
state->lua_tableref = LUA_NOREF;
name = register_cfunc(&luaV_call_lua_func,
&luaV_call_lua_func_free, state);
tv->v_type = VAR_FUNC;
tv->vval.v_string = vim_strsave(name);
break;
}
case LUA_TTABLE:
{
lua_pushvalue(L, pos);
int lua_tableref = luaL_ref(L, LUA_REGISTRYINDEX);
if (lua_getmetatable(L, pos)) {
lua_getfield(L, -1, LUA___CALL);
if (lua_isfunction(L, -1)) {
char_u *name;
int lua_funcref = luaL_ref(L, LUA_REGISTRYINDEX);
luaV_CFuncState *state = ALLOC_CLEAR_ONE(luaV_CFuncState);
state->lua_funcref = lua_funcref;
state->L = L;
state->lua_tableref = lua_tableref;
name = register_cfunc(&luaV_call_lua_func,
&luaV_call_lua_func_free, state);
tv->v_type = VAR_FUNC;
tv->vval.v_string = vim_strsave(name);
break;
}
}
tv->v_type = VAR_NUMBER;
tv->vval.v_number = 0;
status = FAIL;
break;
}
case LUA_TUSERDATA:
{
void *p = lua_touserdata(L, pos);
@@ -2415,4 +2465,53 @@ update_package_paths_in_lua()
}
}
/*
* Native C function callback
*/
static int
luaV_call_lua_func(
int argcount,
typval_T *argvars,
typval_T *rettv,
void *state)
{
int i;
int luaargcount = argcount;
luaV_CFuncState *funcstate = (luaV_CFuncState*)state;
lua_rawgeti(funcstate->L, LUA_REGISTRYINDEX, funcstate->lua_funcref);
if (funcstate->lua_tableref != LUA_NOREF)
{
// First arg for metatable __call method is a table
luaargcount += 1;
lua_rawgeti(funcstate->L, LUA_REGISTRYINDEX, funcstate->lua_tableref);
}
for (i = 0; i < argcount; ++i)
luaV_pushtypval(funcstate->L, &argvars[i]);
if (lua_pcall(funcstate->L, luaargcount, 1, 0))
{
luaV_emsg(funcstate->L);
return FCERR_OTHER;
}
luaV_checktypval(funcstate->L, -1, rettv, "get return value");
return FCERR_NONE;
}
/*
* Free up any lua references held by the func state.
*/
static void
luaV_call_lua_func_free(void *state)
{
luaV_CFuncState *funcstate = (luaV_CFuncState*)state;
luaL_unref(L, LUA_REGISTRYINDEX, funcstate->lua_funcref);
funcstate->L = NULL;
if (funcstate->lua_tableref != LUA_NOREF)
luaL_unref(L, LUA_REGISTRYINDEX, funcstate->lua_tableref);
VIM_CLEAR(funcstate);
}
#endif

View File

@@ -388,7 +388,7 @@ CVim::Eval(BSTR expr, BSTR *result)
/* Evaluate the expression */
++emsg_skip;
str = (char *)eval_to_string((char_u *)buffer, NULL, TRUE);
str = (char *)eval_to_string((char_u *)buffer, TRUE);
--emsg_skip;
vim_free(buffer);
if (str == NULL)

View File

@@ -832,7 +832,6 @@ msg_split(
char_u *
eval_to_string(
char_u *arg UNUSED,
char_u **nextcmd UNUSED,
int dolist UNUSED)
{
return NULL;
@@ -1562,7 +1561,7 @@ Eval(str)
PREINIT:
char_u *value;
PPCODE:
value = eval_to_string((char_u *)str, (char_u **)0, TRUE);
value = eval_to_string((char_u *)str, TRUE);
if (value == NULL)
{
XPUSHs(sv_2mortal(newSViv(0)));

View File

@@ -1373,7 +1373,7 @@ tclvimexpr(
#ifdef FEAT_EVAL
expr = Tcl_GetStringFromObj(objv[objn], NULL);
str = (char *)eval_to_string((char_u *)expr, NULL, TRUE);
str = (char *)eval_to_string((char_u *)expr, TRUE);
if (str == NULL)
Tcl_SetResult(interp, _("invalid expression"), TCL_STATIC);
else

View File

@@ -1165,6 +1165,10 @@ get_list_tv(char_u **arg, typval_T *rettv, int flags, int do_error)
list_T *l = NULL;
typval_T tv;
listitem_T *item;
evalarg_T evalarg;
CLEAR_FIELD(evalarg);
evalarg.eval_flags = flags;
if (evaluate)
{
@@ -1176,7 +1180,7 @@ get_list_tv(char_u **arg, typval_T *rettv, int flags, int do_error)
*arg = skipwhite(*arg + 1);
while (**arg != ']' && **arg != NUL)
{
if (eval1(arg, &tv, flags) == FAIL) // recursive!
if (eval1(arg, &tv, &evalarg) == FAIL) // recursive!
goto failret;
if (evaluate)
{
@@ -2457,6 +2461,8 @@ f_reduce(typval_T *argvars, typval_T *rettv)
list_T *l = argvars[0].vval.v_list;
listitem_T *li = NULL;
int r;
int prev_locked = l->lv_lock;
int called_emsg_start = called_emsg;
CHECK_LIST_MATERIALIZE(l);
if (argvars[2].v_type == VAR_UNKNOWN)
@@ -2476,6 +2482,7 @@ f_reduce(typval_T *argvars, typval_T *rettv)
li = l->lv_first;
}
l->lv_lock = VAR_FIXED; // disallow the list changing here
copy_tv(&initial, rettv);
for ( ; li != NULL; li = li->li_next)
{
@@ -2484,9 +2491,10 @@ f_reduce(typval_T *argvars, typval_T *rettv)
rettv->v_type = VAR_UNKNOWN;
r = call_func(func_name, -1, rettv, 2, argv, &funcexe);
clear_tv(&argv[0]);
if (r == FAIL)
return;
if (r == FAIL || called_emsg != called_emsg_start)
break;
}
l->lv_lock = prev_locked;
}
else
{

View File

@@ -1614,7 +1614,7 @@ eval_map_expr(
save_cursor = curwin->w_cursor;
save_msg_col = msg_col;
save_msg_row = msg_row;
p = eval_to_string(expr, NULL, FALSE);
p = eval_to_string(expr, FALSE);
--textwinlock;
--ex_normal_lock;
curwin->w_cursor = save_cursor;

View File

@@ -3,16 +3,16 @@ varnumber_T num_divide(varnumber_T n1, varnumber_T n2);
varnumber_T num_modulus(varnumber_T n1, varnumber_T n2);
void eval_init(void);
void eval_clear(void);
int eval_to_bool(char_u *arg, int *error, char_u **nextcmd, int skip);
int eval_to_bool(char_u *arg, int *error, exarg_T *eap, int skip);
int eval_expr_valid_arg(typval_T *tv);
int eval_expr_typval(typval_T *expr, typval_T *argv, int argc, typval_T *rettv);
int eval_expr_to_bool(typval_T *expr, int *error);
char_u *eval_to_string_skip(char_u *arg, char_u **nextcmd, int skip);
char_u *eval_to_string_skip(char_u *arg, exarg_T *eap, int skip);
int skip_expr(char_u **pp);
char_u *eval_to_string(char_u *arg, char_u **nextcmd, int convert);
char_u *eval_to_string_safe(char_u *arg, char_u **nextcmd, int use_sandbox);
char_u *eval_to_string(char_u *arg, int convert);
char_u *eval_to_string_safe(char_u *arg, int use_sandbox);
varnumber_T eval_to_number(char_u *expr);
typval_T *eval_expr(char_u *arg, char_u **nextcmd);
typval_T *eval_expr(char_u *arg, exarg_T *eap);
int call_vim_function(char_u *func, int argc, typval_T *argv, typval_T *rettv);
varnumber_T call_func_retnr(char_u *func, int argc, typval_T *argv);
void *call_func_retstr(char_u *func, int argc, typval_T *argv);
@@ -21,13 +21,13 @@ int eval_foldexpr(char_u *arg, int *cp);
char_u *get_lval(char_u *name, typval_T *rettv, lval_T *lp, int unlet, int skip, int flags, int fne_flags);
void clear_lval(lval_T *lp);
void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, int flags, char_u *op);
void *eval_for_line(char_u *arg, int *errp, char_u **nextcmdp, int skip);
void *eval_for_line(char_u *arg, int *errp, exarg_T *eap, int skip);
int next_for_item(void *fi_void, char_u *arg);
void free_for_info(void *fi_void);
void set_context_for_expression(expand_T *xp, char_u *arg, cmdidx_T cmdidx);
int pattern_match(char_u *pat, char_u *text, int ic);
int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int flags);
int eval1(char_u **arg, typval_T *rettv, int flags);
int eval0(char_u *arg, typval_T *rettv, exarg_T *eap, evalarg_T *evalarg);
int eval1(char_u **arg, typval_T *rettv, evalarg_T *evalarg);
void eval_addblob(typval_T *tv1, typval_T *tv2);
int eval_addlist(typval_T *tv1, typval_T *tv2);
char_u *partial_name(partial_T *pt);

View File

@@ -22,6 +22,7 @@ void ex_options(exarg_T *eap);
linenr_T *source_breakpoint(void *cookie);
int *source_dbg_tick(void *cookie);
int source_level(void *cookie);
char_u *source_nextline(void *cookie);
int do_source(char_u *fname, int check_other, int is_vimrc, int *ret_sid);
void ex_scriptnames(exarg_T *eap);
void scriptnames_slash_adjust(void);

View File

@@ -4,6 +4,7 @@ hashtab_T *func_tbl_get(void);
int get_function_args(char_u **argp, char_u endchar, garray_T *newargs, garray_T *argtypes, int *varargs, garray_T *default_args, int skip, exarg_T *eap, char_u **line_to_free);
char_u *get_lambda_name(void);
int get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate);
char_u *register_cfunc(cfunc_T cb, cfunc_free_T free_cb, void *state);
char_u *deref_func_name(char_u *name, int *lenp, partial_T **partialp, 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, funcexe_T *funcexe);

View File

@@ -7680,7 +7680,7 @@ ex_cexpr(exarg_T *eap)
// Evaluate the expression. When the result is a string or a list we can
// use it to fill the errorlist.
tv = eval_expr(eap->arg, &eap->nextcmd);
tv = eval_expr(eap->arg, eap);
if (tv != NULL)
{
if ((tv->v_type == VAR_STRING && tv->vval.v_string != NULL)

View File

@@ -2066,7 +2066,7 @@ vim_regsub_both(
clear_tv(&rettv);
}
else
eval_result = eval_to_string(source + 2, NULL, TRUE);
eval_result = eval_to_string(source + 2, TRUE);
if (eval_result != NULL)
{

View File

@@ -136,7 +136,7 @@ get_expr_line(void)
return expr_copy;
++nested;
rv = eval_to_string(expr_copy, NULL, TRUE);
rv = eval_to_string(expr_copy, TRUE);
--nested;
vim_free(expr_copy);
return rv;

View File

@@ -1148,7 +1148,7 @@ get_keymap_str(
curwin = wp;
STRCPY(buf, "b:keymap_name"); // must be writable
++emsg_skip;
s = p = eval_to_string(buf, NULL, FALSE);
s = p = eval_to_string(buf, FALSE);
--emsg_skip;
curbuf = old_curbuf;
curwin = old_curwin;

View File

@@ -1050,6 +1050,15 @@ source_level(void *cookie)
{
return ((struct source_cookie *)cookie)->level;
}
/*
* Return the readahead line.
*/
char_u *
source_nextline(void *cookie)
{
return ((struct source_cookie *)cookie)->nextline;
}
#endif
#if (defined(MSWIN) && defined(FEAT_CSCOPE)) || defined(HAVE_FD_CLOEXEC)

View File

@@ -1529,6 +1529,9 @@ struct blobvar_S
char bv_lock; // zero, VAR_LOCKED, VAR_FIXED
};
typedef int (*cfunc_T)(int argcount, typval_T *argvars, typval_T *rettv, void *state);
typedef void (*cfunc_free_T)(void *state);
#if defined(FEAT_EVAL) || defined(PROTO)
typedef struct funccall_S funccall_T;
@@ -1562,6 +1565,11 @@ typedef struct
char_u *uf_va_name; // name from "...name" or NULL
type_T *uf_va_type; // type from "...name: type" or NULL
type_T *uf_func_type; // type of the function, &t_func_any if unknown
# if defined(FEAT_LUA)
cfunc_T uf_cb; // callback function for cfunc
cfunc_free_T uf_cb_free; // callback function to free cfunc
void *uf_cb_state; // state of uf_cb
# endif
garray_T uf_lines; // function lines
# ifdef FEAT_PROFILE
@@ -1607,6 +1615,7 @@ typedef struct
#define FC_EXPORT 0x100 // "export def Func()"
#define FC_NOARGS 0x200 // no a: variables in lambda
#define FC_VIM9 0x400 // defined in vim9 script file
#define FC_CFUNC 0x800 // defined as Lua C func
#define MAX_FUNC_ARGS 20 // maximum number of function arguments
#define VAR_SHORT_LEN 20 // short variable name length
@@ -1746,6 +1755,22 @@ typedef struct
# endif
} scriptitem_T;
// Struct passed through eval() functions.
// See EVALARG_EVALUATE for a fixed value with eval_flags set to EVAL_EVALUATE.
typedef struct {
int eval_flags; // EVAL_ flag values below
// copied from exarg_T when "getline" is "getsourceline". Can be NULL.
void *eval_cookie; // argument for getline()
// pointer to the line obtained with getsourceline()
char_u *eval_tofree;
} evalarg_T;
// Flags for expression evaluation.
#define EVAL_EVALUATE 1 // when missing don't actually evaluate
#define EVAL_CONSTANT 2 // when not a constant return FAIL
# ifdef FEAT_PROFILE
/*
* Struct used in sn_prl_ga for every line of a script.
@@ -1781,6 +1806,10 @@ typedef struct
{
int dummy;
} scriptitem_T;
typedef struct
{
int dummy;
} evalarg_T;
#endif
// Struct passed between functions dealing with function call execution.

View File

@@ -90,6 +90,7 @@ NEW_TESTS = \
test_close_count \
test_cmdline \
test_command_count \
test_comments \
test_comparators \
test_compiler \
test_conceal \
@@ -341,6 +342,7 @@ NEW_TESTS_RES = \
test_close_count.res \
test_cmdline.res \
test_command_count.res \
test_comments.res \
test_comparators.res \
test_conceal.res \
test_const.res \

View File

@@ -28,7 +28,7 @@ if 1
" This uses the :s command to just fetch and process the output of the
" tests, it doesn't actually replace anything.
" And it uses "silent" to avoid reporting the number of matches.
silent %s/^Executed\s\+\zs\d\+\ze\s\+tests\?/\=Count(submatch(0),'executed')/egn
silent %s/Executed\s\+\zs\d\+\ze\s\+tests\?/\=Count(submatch(0),'executed')/egn
silent %s/^SKIPPED \zs.*/\=Count(submatch(0), 'skipped')/egn
silent %s/^\(\d\+\)\s\+FAILED:/\=Count(submatch(1), 'failed')/egn

View File

@@ -2585,7 +2585,7 @@ func Test_autocmd_window()
edit one.txt
tabnew two.txt
let g:blist = []
augroup aucmd_win_test
augroup aucmd_win_test1
au!
au BufEnter * call add(g:blist, [expand('<afile>'),
\ win_gettype(bufwinnr(expand('<afile>')))])
@@ -2594,10 +2594,29 @@ func Test_autocmd_window()
doautoall BufEnter
call assert_equal([['one.txt', 'autocmd'], ['two.txt', '']], g:blist)
augroup aucmd_win_test
augroup aucmd_win_test1
au!
augroup END
augroup! aucmd_win_test
augroup! aucmd_win_test1
%bw!
endfunc
" Test for trying to close the temporary window used for executing an autocmd
func Test_close_autocmd_window()
%bw!
edit one.txt
tabnew two.txt
augroup aucmd_win_test2
au!
au BufEnter * if expand('<afile>') == 'one.txt' | 1close | endif
augroup END
call assert_fails('doautoall BufEnter', 'E813:')
augroup aucmd_win_test2
au!
augroup END
augroup! aucmd_win_test2
%bw!
endfunc

View File

@@ -1,5 +1,7 @@
" Tests for Vim buffer
source check.vim
" Test for the :bunload command with an offset
func Test_bunload_with_offset()
%bwipe!
@@ -152,6 +154,24 @@ func Test_bdelete_cmd()
set nobuflisted
enew
call assert_fails('bdelete ' .. bnr, 'E516:')
" Deleting more than one buffer
new Xbuf1
new Xbuf2
exe 'bdel ' .. bufnr('Xbuf2') .. ' ' .. bufnr('Xbuf1')
call assert_equal(1, winnr('$'))
call assert_equal(0, getbufinfo('Xbuf1')[0].loaded)
call assert_equal(0, getbufinfo('Xbuf2')[0].loaded)
" Deleting more than one buffer and an invalid buffer
new Xbuf1
new Xbuf2
let cmd = "exe 'bdel ' .. bufnr('Xbuf2') .. ' xxx ' .. bufnr('Xbuf1')"
call assert_fails(cmd, 'E94:')
call assert_equal(2, winnr('$'))
call assert_equal(1, getbufinfo('Xbuf1')[0].loaded)
call assert_equal(0, getbufinfo('Xbuf2')[0].loaded)
%bwipe!
endfunc
@@ -166,4 +186,188 @@ func Test_buffer_error()
%bwipe
endfunc
" Test for the status messages displayed when unloading, deleting or wiping
" out buffers
func Test_buffer_statusmsg()
CheckEnglish
set report=1
new Xbuf1
new Xbuf2
let bnr = bufnr()
exe "normal 2\<C-G>"
call assert_match('buf ' .. bnr .. ':', v:statusmsg)
bunload Xbuf1 Xbuf2
call assert_equal('2 buffers unloaded', v:statusmsg)
bdel Xbuf1 Xbuf2
call assert_equal('2 buffers deleted', v:statusmsg)
bwipe Xbuf1 Xbuf2
call assert_equal('2 buffers wiped out', v:statusmsg)
set report&
endfunc
" Test for quitting the 'swapfile exists' dialog with the split buffer
" command.
func Test_buffer_sbuf_cleanup()
call writefile([], 'Xfile')
" first open the file in a buffer
new Xfile
let bnr = bufnr()
close
" create the swap file
call writefile([], '.Xfile.swp')
" Remove the catch-all that runtest.vim adds
au! SwapExists
augroup BufTest
au!
autocmd SwapExists Xfile let v:swapchoice='q'
augroup END
exe 'sbuf ' . bnr
call assert_equal(1, winnr('$'))
call assert_equal(0, getbufinfo('Xfile')[0].loaded)
" test for :sball
sball
call assert_equal(1, winnr('$'))
call assert_equal(0, getbufinfo('Xfile')[0].loaded)
%bw!
set shortmess+=F
let v:statusmsg = ''
edit Xfile
call assert_equal('', v:statusmsg)
call assert_equal(1, winnr('$'))
call assert_equal(0, getbufinfo('Xfile')[0].loaded)
set shortmess&
call delete('Xfile')
call delete('.Xfile.swp')
augroup BufTest
au!
augroup END
augroup! BufTest
endfunc
" Test for deleting a modified buffer with :confirm
func Test_bdel_with_confirm()
CheckUnix
CheckNotGui
CheckFeature dialog_con
new
call setline(1, 'test')
call assert_fails('bdel', 'E89:')
call feedkeys('c', 'L')
confirm bdel
call assert_equal(2, winnr('$'))
call assert_equal(1, &modified)
call feedkeys('n', 'L')
confirm bdel
call assert_equal(1, winnr('$'))
endfunc
" Test for editing another buffer from a modified buffer with :confirm
func Test_goto_buf_with_confirm()
CheckUnix
CheckNotGui
CheckFeature dialog_con
new Xfile
enew
call setline(1, 'test')
call assert_fails('b Xfile', 'E37:')
call feedkeys('c', 'L')
call assert_fails('confirm b Xfile', 'E37:')
call assert_equal(1, &modified)
call assert_equal('', @%)
call feedkeys('y', 'L')
call assert_fails('confirm b Xfile', 'E37:')
call assert_equal(1, &modified)
call assert_equal('', @%)
call feedkeys('n', 'L')
confirm b Xfile
call assert_equal('Xfile', @%)
close!
endfunc
" Test for splitting buffer with 'switchbuf'
func Test_buffer_switchbuf()
new Xfile
wincmd w
set switchbuf=useopen
sbuf Xfile
call assert_equal(1, winnr())
call assert_equal(2, winnr('$'))
set switchbuf=usetab
tabnew
sbuf Xfile
call assert_equal(1, tabpagenr())
call assert_equal(2, tabpagenr('$'))
set switchbuf&
%bw
endfunc
" Test for BufAdd autocommand wiping out the buffer
func Test_bufadd_autocmd_bwipe()
%bw!
augroup BufAdd_Wipe
au!
autocmd BufAdd Xfile %bw!
augroup END
edit Xfile
call assert_equal('', @%)
call assert_equal(0, bufexists('Xfile'))
augroup BufAdd_Wipe
au!
augroup END
augroup! BufAdd_Wipe
endfunc
" Test for trying to load a buffer with text locked
" <C-\>e in the command line is used to lock the text
func Test_load_buf_with_text_locked()
new Xfile1
edit Xfile2
let cmd = ":\<C-\>eexecute(\"normal \<C-O>\")\<CR>\<C-C>"
call assert_fails("call feedkeys(cmd, 'xt')", 'E565:')
%bw!
endfunc
" Test for using CTRL-^ to edit the alternative file keeping the cursor
" position with 'nostartofline'. Also test using the 'buf' command.
func Test_buffer_edit_altfile()
call writefile(repeat(['one two'], 50), 'Xfile1')
call writefile(repeat(['five six'], 50), 'Xfile2')
set nosol
edit Xfile1
call cursor(25, 5)
edit Xfile2
call cursor(30, 4)
exe "normal \<C-^>"
call assert_equal([0, 25, 5, 0], getpos('.'))
exe "normal \<C-^>"
call assert_equal([0, 30, 4, 0], getpos('.'))
buf Xfile1
call assert_equal([0, 25, 5, 0], getpos('.'))
buf Xfile2
call assert_equal([0, 30, 4, 0], getpos('.'))
set sol&
call delete('Xfile1')
call delete('Xfile2')
endfunc
" Test for running the :sball command with a maximum window count and a
" modified buffer
func Test_sball_with_count()
%bw!
edit Xfile1
call setline(1, ['abc'])
new Xfile2
new Xfile3
new Xfile4
2sball
call assert_equal(bufnr('Xfile4'), winbufnr(1))
call assert_equal(bufnr('Xfile1'), winbufnr(2))
call assert_equal(0, getbufinfo('Xfile2')[0].loaded)
call assert_equal(0, getbufinfo('Xfile3')[0].loaded)
%bw!
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@@ -392,6 +392,7 @@ func Test_getcompletion()
call delete('Xtags')
set tags&
call assert_fails("call getcompletion('\\\\@!\\\\@=', 'buffer')", 'E871:')
call assert_fails('call getcompletion("", "burp")', 'E475:')
call assert_fails('call getcompletion("abc", [])', 'E475:')
endfunc

View File

@@ -0,0 +1,277 @@
" Tests for the various flags in the 'comments' option
" Test for the 'n' flag in 'comments'
func Test_comment_nested()
new
setlocal comments=n:> fo+=ro
exe "normal i> B\nD\<C-C>ggOA\<C-C>joC\<C-C>Go\<BS>>>> F\nH"
exe "normal 5GOE\<C-C>6GoG"
let expected =<< trim END
> A
> B
> C
> D
>>>> E
>>>> F
>>>> G
>>>> H
END
call assert_equal(expected, getline(1, '$'))
close!
endfunc
" Test for the 'b' flag in 'comments'
func Test_comment_blank()
new
setlocal comments=b:* fo+=ro
exe "normal i* E\nF\n\<BS>G\nH\<C-C>ggOC\<C-C>O\<BS>B\<C-C>OA\<C-C>2joD"
let expected =<< trim END
A
*B
* C
* D
* E
* F
*G
H
END
call assert_equal(expected, getline(1, '$'))
close!
endfunc
" Test for the 'f' flag in 'comments' (only the first line has a comment
" string)
func Test_comment_firstline()
new
setlocal comments=f:- fo+=ro
exe "normal i- B\nD\<C-C>ggoC\<C-C>ggOA\<C-C>"
call assert_equal(['A', '- B', ' C', ' D'], getline(1, '$'))
%d
setlocal comments=:-
exe "normal i- B\nD\<C-C>ggoC\<C-C>ggOA\<C-C>"
call assert_equal(['- A', '- B', '- C', '- D'], getline(1, '$'))
close!
endfunc
" Test for the 's', 'm' and 'e' flags in 'comments'
" Test for automatically adding comment leaders in insert mode
func Test_comment_threepiece()
new
setlocal expandtab
call setline(1, ["\t/*"])
setlocal formatoptions=croql
call cursor(1, 3)
call feedkeys("A\<cr>\<cr>/", 'tnix')
call assert_equal(["\t/*", " *", " */"], getline(1, '$'))
" If a comment ends in a single line, then don't add it in the next line
%d
call setline(1, '/* line1 */')
call feedkeys("A\<CR>next line", 'xt')
call assert_equal(['/* line1 */', 'next line'], getline(1, '$'))
%d
" Copy the trailing indentation from the leader comment to a new line
setlocal autoindent noexpandtab
call feedkeys("a\t/*\tone\ntwo\n/", 'xt')
call assert_equal(["\t/*\tone", "\t *\ttwo", "\t */"], getline(1, '$'))
close!
endfunc
" Test for the 'r' flag in 'comments' (right align comment)
func Test_comment_rightalign()
new
setlocal comments=sr:/***,m:**,ex-2:******/ fo+=ro
exe "normal i=\<C-C>o\t /***\nD\n/"
exe "normal 2GOA\<C-C>joB\<C-C>jOC\<C-C>joE\<C-C>GOF\<C-C>joG"
let expected =<< trim END
=
A
/***
** B
** C
** D
** E
** F
******/
G
END
call assert_equal(expected, getline(1, '$'))
close!
endfunc
" Test for the 'O' flag in 'comments'
func Test_comment_O()
new
setlocal comments=Ob:* fo+=ro
exe "normal i* B\nD\<C-C>kOA\<C-C>joC"
let expected =<< trim END
A
* B
* C
* D
END
call assert_equal(expected, getline(1, '$'))
close!
endfunc
" Test for using a multibyte character as a comment leader
func Test_comment_multibyte_leader()
new
let t =<< trim END
{
a
a
a
}
END
call setline(1, t)
call cursor(2, 1)
set tw=2 fo=cqm comments=n:
exe "normal gqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgq"
let t =<< trim END
a
a
a
END
exe "normal o\n" . join(t, "\n")
let expected =<< trim END
{
a
a
a
a
a
a
}
END
call assert_equal(expected, getline(1, '$'))
set tw& fo& comments&
close!
endfunc
" Test for a space character in 'comments' setting
func Test_comment_space()
new
setlocal comments=b:\ > fo+=ro
exe "normal i> B\nD\<C-C>ggOA\<C-C>joC"
exe "normal Go > F\nH\<C-C>kOE\<C-C>joG"
let expected =<< trim END
A
> B
C
D
> E
> F
> G
> H
END
call assert_equal(expected, getline(1, '$'))
close!
endfunc
" Test for formatting lines with and without comments
func Test_comment_format_lines()
new
call setline(1, ['one', '/* two */', 'three'])
normal gggqG
call assert_equal(['one', '/* two */', 'three'], getline(1, '$'))
close!
endfunc
" Test for using 'a' in 'formatoptions' with comments
func Test_comment_autoformat()
new
setlocal formatoptions+=a
call feedkeys("a- one\n- two\n", 'xt')
call assert_equal(['- one', '- two', ''], getline(1, '$'))
%d
call feedkeys("a\none\n", 'xt')
call assert_equal(['', 'one', ''], getline(1, '$'))
setlocal formatoptions+=aw
%d
call feedkeys("aone \ntwo\n", 'xt')
call assert_equal(['one two', ''], getline(1, '$'))
%d
call feedkeys("aone\ntwo\n", 'xt')
call assert_equal(['one', 'two', ''], getline(1, '$'))
close!
endfunc
" Test for joining lines with comments ('j' flag in 'formatoptions')
func Test_comment_join_lines_fo_j()
new
setlocal fo+=j comments=://
call setline(1, ['i++; // comment1', ' // comment2'])
normal J
call assert_equal('i++; // comment1 comment2', getline(1))
setlocal fo-=j
call setline(1, ['i++; // comment1', ' // comment2'])
normal J
call assert_equal('i++; // comment1 // comment2', getline(1))
" Test with nested comments
setlocal fo+=j comments=n:>,n:)
call setline(1, ['i++; > ) > ) comment1', ' > ) comment2'])
normal J
call assert_equal('i++; > ) > ) comment1 comment2', getline(1))
close!
endfunc
" Test for formatting lines where only the first line has a comment.
func Test_comment_format_firstline_comment()
new
setlocal formatoptions=tcq
call setline(1, ['- one two', 'three'])
normal gggqG
call assert_equal(['- one two three'], getline(1, '$'))
%d
call setline(1, ['- one', '- two'])
normal gggqG
call assert_equal(['- one', '- two'], getline(1, '$'))
close!
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@@ -99,6 +99,7 @@ func Test_screenpos()
\ 'curscol': wincol + 9,
\ 'endcol': wincol + 9}, screenpos(winid, 2, 22))
close
call assert_equal({}, screenpos(999, 1, 1))
bwipe!
endfunc

View File

@@ -440,7 +440,7 @@ let s:filename_checks = {
\ 'swiftgyb': ['file.swift.gyb'],
\ 'sil': ['file.sil'],
\ 'sysctl': ['/etc/sysctl.conf', '/etc/sysctl.d/file.conf'],
\ 'systemd': ['any/systemd/file.automount', 'any/systemd/file.mount', 'any/systemd/file.path', 'any/systemd/file.service', 'any/systemd/file.socket', 'any/systemd/file.swap', 'any/systemd/file.target', 'any/systemd/file.timer', '/etc/systemd/system/some.d/file.conf', '/etc/systemd/system/some.d/.#file', '/etc/systemd/system/.#otherfile', '/home/user/.config/systemd/user/some.d/mine.conf', '/home/user/.config/systemd/user/some.d/.#file', '/home/user/.config/systemd/user/.#otherfile'],
\ 'systemd': ['any/systemd/file.automount', 'any/systemd/file.dnssd', 'any/systemd/file.link', 'any/systemd/file.mount', 'any/systemd/file.netdev', 'any/systemd/file.network', 'any/systemd/file.nspawn', 'any/systemd/file.path', 'any/systemd/file.service', 'any/systemd/file.slice', 'any/systemd/file.socket', 'any/systemd/file.swap', 'any/systemd/file.target', 'any/systemd/file.timer', '/etc/systemd/some.conf.d/file.conf', '/etc/systemd/system/some.d/file.conf', '/etc/systemd/system/some.d/.#file', '/etc/systemd/system/.#otherfile', '/home/user/.config/systemd/user/some.d/mine.conf', '/home/user/.config/systemd/user/some.d/.#file', '/home/user/.config/systemd/user/.#otherfile'],
\ 'systemverilog': ['file.sv', 'file.svh'],
\ 'tags': ['tags'],
\ 'tak': ['file.tak'],

View File

@@ -475,6 +475,11 @@ func Test_simplify()
call assert_equal('./file', simplify('./dir/../file'))
call assert_equal('../dir/file', simplify('dir/../../dir/file'))
call assert_equal('./file', simplify('dir/.././file'))
call assert_equal('../dir', simplify('./../dir'))
call assert_equal('..', simplify('../testdir/..'))
call mkdir('Xdir')
call assert_equal('.', simplify('Xdir/../.'))
call delete('Xdir', 'd')
call assert_fails('call simplify({->0})', 'E729:')
call assert_fails('call simplify([])', 'E730:')

View File

@@ -1,3 +1,4 @@
" Test for the gf and gF (goto file) commands
" This is a test if a URL is recognized by "gf", with the cursor before and
" after the "://". Also test ":\\".
@@ -38,6 +39,13 @@ func Test_gf_url()
call search("URL")
call assert_equal("URL://machine.name:1234?q=vim", expand("<cfile>"))
%d
call setline(1, "demo://remote_file")
wincmd f
call assert_equal('demo://remote_file', @%)
call assert_equal(2, winnr('$'))
close!
set isf&vim
enew!
endfunc
@@ -118,6 +126,11 @@ func Test_gf_visual()
norm! ttvtXgf
call assert_equal('Xtest_gf_visual', bufname('%'))
" if multiple lines are selected, then gf should fail
call setline(1, ["one", "two"])
normal VGgf
call assert_equal('Xtest_gf_visual', @%)
bwipe!
call delete('Xtest_gf_visual')
set hidden&
@@ -146,4 +159,21 @@ func Test_gf_error()
bwipe!
endfunc
" If a file is not found by 'gf', then 'includeexpr' should be used to locate
" the file.
func Test_gf_includeexpr()
new
let g:Inc_fname = ''
func IncFunc()
let g:Inc_fname = v:fname
return v:fname
endfunc
setlocal includeexpr=IncFunc()
call setline(1, 'somefile.java')
call assert_fails('normal gf', 'E447:')
call assert_equal('somefile.java', g:Inc_fname)
close!
delfunc IncFunc
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@@ -709,6 +709,15 @@ func Test_reduce()
call assert_fails("call reduce({}, { acc, val -> acc + val }, 1)", 'E897:')
call assert_fails("call reduce(0, { acc, val -> acc + val }, 1)", 'E897:')
call assert_fails("call reduce('', { acc, val -> acc + val }, 1)", 'E897:')
let g:lut = [1, 2, 3, 4]
func EvilRemove()
call remove(g:lut, 1)
return 1
endfunc
call assert_fails("call reduce(g:lut, { acc, val -> EvilRemove() }, 1)", 'E742:')
unlet g:lut
delfunc EvilRemove
endfunc
" splitting a string to a List using split()

View File

@@ -541,6 +541,35 @@ func Test_update_package_paths()
call assert_equal("hello from lua", luaeval("require('testluaplugin').hello()"))
endfunc
func Vim_func_call_lua_callback(Concat, Cb)
let l:message = a:Concat("hello", "vim")
call a:Cb(l:message)
endfunc
func Test_pass_lua_callback_to_vim_from_lua()
lua pass_lua_callback_to_vim_from_lua_result = ""
call assert_equal("", luaeval("pass_lua_callback_to_vim_from_lua_result"))
lua <<EOF
vim.funcref('Vim_func_call_lua_callback')(
function(greeting, message)
return greeting .. " " .. message
end,
function(message)
pass_lua_callback_to_vim_from_lua_result = message
end)
EOF
call assert_equal("hello vim", luaeval("pass_lua_callback_to_vim_from_lua_result"))
endfunc
func Vim_func_call_metatable_lua_callback(Greet)
return a:Greet("world")
endfunc
func Test_pass_lua_metatable_callback_to_vim_from_lua()
let result = luaeval("vim.funcref('Vim_func_call_metatable_lua_callback')(setmetatable({ space = ' '}, { __call = function(tbl, msg) return 'hello' .. tbl.space .. msg end }) )")
call assert_equal("hello world", result)
endfunc
" Test vim.line()
func Test_lua_line()
new

View File

@@ -754,8 +754,9 @@ func Test_normal17_z_scroll_hor2()
bw!
endfunc
" Test for H, M and L commands with folds
func Test_scroll_cmds()
" Test for commands that scroll the window horizontally. Test with folds.
" H, M, L, CTRL-E, CTRL-Y, CTRL-U, CTRL-D, PageUp, PageDown commands
func Test_vert_scroll_cmds()
15new
call setline(1, range(1, 100))
exe "normal! 30ggz\<CR>"
@@ -764,6 +765,8 @@ func Test_scroll_cmds()
40,43fold
46,49fold
let h = winheight(0)
" Test for H, M and L commands
" Top of the screen = 30
" Folded lines = 9
" Bottom of the screen = 30 + h + 9 - 1
@@ -771,10 +774,104 @@ func Test_scroll_cmds()
call assert_equal(35 + h, line('.'))
normal! 4H
call assert_equal(33, line('.'))
" Test for the CTRL-E and CTRL-Y commands with folds
%d
call setline(1, range(1, 10))
3,5fold
exe "normal 6G3\<C-E>"
call assert_equal(6, line('w0'))
exe "normal 2\<C-Y>"
call assert_equal(2, line('w0'))
" Test for CTRL-Y on a folded line
%d
call setline(1, range(1, 100))
exe (h + 2) .. "," .. (h + 4) .. "fold"
exe h + 5
normal z-
exe "normal \<C-Y>\<C-Y>"
call assert_equal(h + 1, line('w$'))
" Using <PageUp> and <PageDown> in an empty buffer should beep
%d
call assert_beeps('exe "normal \<PageUp>"')
call assert_beeps('exe "normal \<C-B>"')
call assert_beeps('exe "normal \<PageDown>"')
call assert_beeps('exe "normal \<C-F>"')
" Test for <C-U> and <C-D> with fold
%d
call setline(1, range(1, 100))
10,35fold
set scroll=10
exe "normal \<C-D>"
call assert_equal(36, line('.'))
exe "normal \<C-D>"
call assert_equal(46, line('.'))
exe "normal \<C-U>"
call assert_equal(36, line('.'))
exe "normal \<C-U>"
call assert_equal(10, line('.'))
exe "normal \<C-U>"
call assert_equal(1, line('.'))
set scroll&
" Test for scrolling to the top of the file with <C-U> and a fold
10
normal ztL
exe "normal \<C-U>\<C-U>"
call assert_equal(1, line('w0'))
" Test for CTRL-D on a folded line
%d
call setline(1, range(1, 100))
50,100fold
75
normal z-
exe "normal \<C-D>"
call assert_equal(50, line('.'))
call assert_equal(100, line('w$'))
normal z.
let lnum = winline()
exe "normal \<C-D>"
call assert_equal(lnum, winline())
call assert_equal(50, line('.'))
normal zt
exe "normal \<C-D>"
call assert_equal(50, line('w0'))
set foldenable&
close!
endfunc
" Test for the 'sidescroll' option
func Test_sidescroll_opt()
new
20vnew
" scroll by 2 characters horizontally
set sidescroll=2 nowrap
call setline(1, repeat('a', 40))
normal g$l
call assert_equal(19, screenpos(0, 1, 21).col)
normal l
call assert_equal(20, screenpos(0, 1, 22).col)
normal g0h
call assert_equal(2, screenpos(0, 1, 2).col)
call assert_equal(20, screenpos(0, 1, 20).col)
" when 'sidescroll' is 0, cursor positioned at the center
set sidescroll=0
normal g$l
call assert_equal(11, screenpos(0, 1, 21).col)
normal g0h
call assert_equal(10, screenpos(0, 1, 10).col)
%bw!
set wrap& sidescroll&
endfunc
" basic tests for foldopen/folddelete
func Test_normal18_z_fold()
CheckFeature folding
@@ -1170,6 +1267,7 @@ func Test_normal21_nv_hat()
edit Xfoo | %bw
call assert_fails(':buffer #', 'E86')
call assert_fails(':execute "normal! \<C-^>"', 'E23')
call assert_fails("normal i\<C-R>#", 'E23:')
" Test for the expected behavior when switching between two named buffers.
edit Xfoo | edit Xbar
@@ -2962,4 +3060,40 @@ func Test_normal_word_move()
close!
endfunc
" Test for 'scrolloff' with a long line that doesn't fit in the screen
func Test_normal_scroloff()
10new
80vnew
call setline(1, repeat('a', 1000))
set scrolloff=10
normal gg10gj
call assert_equal(8, winline())
normal 10gj
call assert_equal(10, winline())
normal 10gk
call assert_equal(3, winline())
set scrolloff&
close!
endfunc
" Test for vertical scrolling with CTRL-F and CTRL-B with a long line
func Test_normal_vert_scroll_longline()
10new
80vnew
call setline(1, range(1, 10))
call append(5, repeat('a', 1000))
exe "normal gg\<C-F>"
call assert_equal(6, line('.'))
exe "normal \<C-F>\<C-F>"
call assert_equal(11, line('.'))
call assert_equal(1, winline())
exe "normal \<C-B>"
call assert_equal(10, line('.'))
call assert_equal(3, winline())
exe "normal \<C-B>\<C-B>"
call assert_equal(5, line('.'))
call assert_equal(5, winline())
close!
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@@ -513,6 +513,7 @@ func Test_set_one_column()
endfunc
func Test_set_values()
" opt_test.vim is generated from ../optiondefs.h using gen_opt_test.vim
if filereadable('opt_test.vim')
source opt_test.vim
else
@@ -921,4 +922,35 @@ func Test_opt_boolean()
set number&
endfunc
" Test for the 'window' option
func Test_window_opt()
" Needs only one open widow
%bw!
call setline(1, range(1, 8))
set window=5
exe "normal \<C-F>"
call assert_equal(4, line('w0'))
exe "normal \<C-F>"
call assert_equal(7, line('w0'))
exe "normal \<C-F>"
call assert_equal(8, line('w0'))
exe "normal \<C-B>"
call assert_equal(5, line('w0'))
exe "normal \<C-B>"
call assert_equal(2, line('w0'))
exe "normal \<C-B>"
call assert_equal(1, line('w0'))
set window=1
exe "normal gg\<C-F>"
call assert_equal(2, line('w0'))
exe "normal \<C-F>"
call assert_equal(3, line('w0'))
exe "normal \<C-B>"
call assert_equal(2, line('w0'))
exe "normal \<C-B>"
call assert_equal(1, line('w0'))
enew!
set window&
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@@ -2454,10 +2454,9 @@ func Test_popupwin_terminal_buffer()
call term_sendkeys(termbuf2, "exit\<CR>")
" Exiting shell closes popup window
let pupwin = win_getid()
call feedkeys("exit\<CR>", 'xt')
" Wait for shell to exit
call WaitForAssert({-> assert_notequal(pupwin, win_getid())})
call WaitForAssert({-> assert_equal("dead", job_status(term_getjob(termbuf)))})
call feedkeys(":quit\<CR>", 'xt')
call assert_equal(origwin, win_getid())

View File

@@ -488,10 +488,10 @@ func Xtest_browse(cchar)
call assert_fails('Xprev', 'E553')
call assert_fails('Xpfile', 'E553')
Xnfile
call assert_equal('Xqftestfile2', bufname('%'))
call assert_equal('Xqftestfile2', @%)
call assert_equal(10, line('.'))
Xpfile
call assert_equal('Xqftestfile1', bufname('%'))
call assert_equal('Xqftestfile1', @%)
call assert_equal(6, line('.'))
5Xcc
call assert_equal(5, g:Xgetlist({'idx':0}).idx)
@@ -507,7 +507,7 @@ func Xtest_browse(cchar)
call assert_equal(6, g:Xgetlist({'idx':0}).idx)
Xlast
Xprev
call assert_equal('Xqftestfile2', bufname('%'))
call assert_equal('Xqftestfile2', @%)
call assert_equal(11, line('.'))
call assert_fails('Xnext', 'E553')
call assert_fails('Xnfile', 'E553')
@@ -520,14 +520,14 @@ func Xtest_browse(cchar)
endif
call assert_equal(6, g:Xgetlist({'idx':0}).idx)
Xrewind
call assert_equal('Xqftestfile1', bufname('%'))
call assert_equal('Xqftestfile1', @%)
call assert_equal(5, line('.'))
10Xnext
call assert_equal('Xqftestfile2', bufname('%'))
call assert_equal('Xqftestfile2', @%)
call assert_equal(11, line('.'))
10Xprev
call assert_equal('Xqftestfile1', bufname('%'))
call assert_equal('Xqftestfile1', @%)
call assert_equal(5, line('.'))
" Jumping to an error from the error window using cc command
@@ -538,7 +538,7 @@ func Xtest_browse(cchar)
Xopen
10Xcc
call assert_equal(11, line('.'))
call assert_equal('Xqftestfile2', bufname('%'))
call assert_equal('Xqftestfile2', @%)
Xopen
call cursor(2, 1)
if a:cchar == 'c'
@@ -547,14 +547,14 @@ func Xtest_browse(cchar)
.ll
endif
call assert_equal(6, line('.'))
call assert_equal('Xqftestfile1', bufname('%'))
call assert_equal('Xqftestfile1', @%)
" Jumping to an error from the error window (when only the error window is
" present)
Xopen | only
Xlast 1
call assert_equal(5, line('.'))
call assert_equal('Xqftestfile1', bufname('%'))
call assert_equal('Xqftestfile1', @%)
Xexpr ""
call assert_fails('Xnext', 'E42:')
@@ -1859,7 +1859,7 @@ func Test_switchbuf()
copen | only
cfirst
call assert_equal(1, tabpagenr())
call assert_equal('Xqftestfile1', bufname(''))
call assert_equal('Xqftestfile1', @%)
" If opening a file changes 'switchbuf', then the new value should be
" retained.
@@ -2679,7 +2679,7 @@ func Test_cwindow_jump()
wincmd b
cfirst
call assert_equal(2, winnr())
call assert_equal('F1', bufname(''))
call assert_equal('F1', @%)
enew | only
exe 'sb' bnum
exe 'botright sb' bnum
@@ -2768,7 +2768,7 @@ func XvimgrepTests(cchar)
edit +3 Xtestfile2
Xvimgrep +\cemacs+j Xtestfile1
let l = g:Xgetlist()
call assert_equal('Xtestfile2', bufname(''))
call assert_equal('Xtestfile2', @%)
call assert_equal('Editor:Emacs EmAcS', l[0].text)
" Test for unloading a buffer after vimgrep searched the buffer
@@ -3394,7 +3394,7 @@ func Xqfjump_tests(cchar)
Xopen | only
2Xnext
call assert_equal(3, g:Xgetlist({'idx' : 0}).idx)
call assert_equal('F3', bufname('%'))
call assert_equal('F3', @%)
Xnext
call assert_equal(7, col('.'))
Xnext
@@ -4043,20 +4043,20 @@ func Xjumpto_first_error_test(cchar)
" Test for cexpr/lexpr
enew
Xexpr l
call assert_equal('Xtestfile1', bufname(''))
call assert_equal('Xtestfile1', @%)
call assert_equal(2, line('.'))
" Test for cfile/lfile
enew
call writefile(l, 'Xerr')
Xfile Xerr
call assert_equal('Xtestfile1', bufname(''))
call assert_equal('Xtestfile1', @%)
call assert_equal(2, line('.'))
" Test for cbuffer/lbuffer
edit Xerr
Xbuffer
call assert_equal('Xtestfile1', bufname(''))
call assert_equal('Xtestfile1', @%)
call assert_equal(2, line('.'))
call delete('Xerr')
@@ -4081,7 +4081,7 @@ func Xautocmd_changelist(cchar)
autocmd QuickFixCmdPost * Xolder
call writefile(['Xtestfile2:4:Line4'], 'Xerr')
Xfile Xerr
call assert_equal('Xtestfile2', bufname(''))
call assert_equal('Xtestfile2', @%)
call assert_equal(4, line('.'))
autocmd! QuickFixCmdPost
@@ -4092,7 +4092,7 @@ func Xautocmd_changelist(cchar)
call writefile(['Xtestfile2:4:Line4'], 'Xerr')
edit Xerr
Xbuffer
call assert_equal('Xtestfile2', bufname(''))
call assert_equal('Xtestfile2', @%)
call assert_equal(4, line('.'))
autocmd! QuickFixCmdPost
@@ -4101,7 +4101,7 @@ func Xautocmd_changelist(cchar)
Xexpr 'Xtestfile1:2:Line2'
autocmd QuickFixCmdPost * Xolder
Xexpr 'Xtestfile2:4:Line4'
call assert_equal('Xtestfile2', bufname(''))
call assert_equal('Xtestfile2', @%)
call assert_equal(4, line('.'))
autocmd! QuickFixCmdPost
@@ -4112,7 +4112,7 @@ func Xautocmd_changelist(cchar)
Xexpr 'Xtestfile1:2:Line2'
autocmd QuickFixCmdPost * Xolder
silent Xgrep Line5 Xtestfile2
call assert_equal('Xtestfile2', bufname(''))
call assert_equal('Xtestfile2', @%)
call assert_equal(5, line('.'))
autocmd! QuickFixCmdPost
endif
@@ -4122,7 +4122,7 @@ func Xautocmd_changelist(cchar)
Xexpr 'Xtestfile1:2:Line2'
autocmd QuickFixCmdPost * Xolder
silent Xvimgrep Line5 Xtestfile2
call assert_equal('Xtestfile2', bufname(''))
call assert_equal('Xtestfile2', @%)
call assert_equal(5, line('.'))
autocmd! QuickFixCmdPost
@@ -4415,7 +4415,7 @@ func Test_winonly_autocmd()
" positioned correctly.
ll 3
call assert_equal(loclistid, getloclist(0, {'id' : 0}).id)
call assert_equal('Xtest1', bufname(''))
call assert_equal('Xtest1', @%)
call assert_equal(15, line('.'))
" Cleanup
autocmd! WinEnter
@@ -4476,51 +4476,51 @@ func Xtest_below(cchar)
Xexpr ["X1:5:3:L5", "X2:5:2:L5", "X2:10:3:L10", "X2:15:4:L15", "X3:3:5:L3"]
edit +7 X2
Xabove
call assert_equal(['X2', 5], [bufname(''), line('.')])
call assert_equal(['X2', 5], [@%, line('.')])
call assert_fails('Xabove', 'E553:')
normal 7G
Xbefore
call assert_equal(['X2', 5, 2], [bufname(''), line('.'), col('.')])
call assert_equal(['X2', 5, 2], [@%, line('.'), col('.')])
call assert_fails('Xbefore', 'E553:')
normal 2j
Xbelow
call assert_equal(['X2', 10], [bufname(''), line('.')])
call assert_equal(['X2', 10], [@%, line('.')])
normal 7G
Xafter
call assert_equal(['X2', 10, 3], [bufname(''), line('.'), col('.')])
call assert_equal(['X2', 10, 3], [@%, line('.'), col('.')])
" Last error in this file
Xbelow 99
call assert_equal(['X2', 15], [bufname(''), line('.')])
call assert_equal(['X2', 15], [@%, line('.')])
call assert_fails('Xbelow', 'E553:')
normal gg
Xafter 99
call assert_equal(['X2', 15, 4], [bufname(''), line('.'), col('.')])
call assert_equal(['X2', 15, 4], [@%, line('.'), col('.')])
call assert_fails('Xafter', 'E553:')
" First error in this file
Xabove 99
call assert_equal(['X2', 5], [bufname(''), line('.')])
call assert_equal(['X2', 5], [@%, line('.')])
call assert_fails('Xabove', 'E553:')
normal G
Xbefore 99
call assert_equal(['X2', 5, 2], [bufname(''), line('.'), col('.')])
call assert_equal(['X2', 5, 2], [@%, line('.'), col('.')])
call assert_fails('Xbefore', 'E553:')
normal gg
Xbelow 2
call assert_equal(['X2', 10], [bufname(''), line('.')])
call assert_equal(['X2', 10], [@%, line('.')])
normal gg
Xafter 2
call assert_equal(['X2', 10, 3], [bufname(''), line('.'), col('.')])
call assert_equal(['X2', 10, 3], [@%, line('.'), col('.')])
normal G
Xabove 2
call assert_equal(['X2', 10], [bufname(''), line('.')])
call assert_equal(['X2', 10], [@%, line('.')])
normal G
Xbefore 2
call assert_equal(['X2', 10, 3], [bufname(''), line('.'), col('.')])
call assert_equal(['X2', 10, 3], [@%, line('.'), col('.')])
edit X4
call assert_fails('Xabove', 'E42:')
@@ -4544,45 +4544,45 @@ func Xtest_below(cchar)
\ "X2:15:1:L15_1", "X2:15:2:L15_2", "X2:15:3:L15_3", "X3:3:L3"]
edit +1 X2
Xbelow 2
call assert_equal(['X2', 10, 1], [bufname(''), line('.'), col('.')])
call assert_equal(['X2', 10, 1], [@%, line('.'), col('.')])
normal 1G
Xafter 2
call assert_equal(['X2', 5, 2], [bufname(''), line('.'), col('.')])
call assert_equal(['X2', 5, 2], [@%, line('.'), col('.')])
normal gg
Xbelow 99
call assert_equal(['X2', 15, 1], [bufname(''), line('.'), col('.')])
call assert_equal(['X2', 15, 1], [@%, line('.'), col('.')])
normal gg
Xafter 99
call assert_equal(['X2', 15, 3], [bufname(''), line('.'), col('.')])
call assert_equal(['X2', 15, 3], [@%, line('.'), col('.')])
normal G
Xabove 2
call assert_equal(['X2', 10, 1], [bufname(''), line('.'), col('.')])
call assert_equal(['X2', 10, 1], [@%, line('.'), col('.')])
normal G
Xbefore 2
call assert_equal(['X2', 15, 2], [bufname(''), line('.'), col('.')])
call assert_equal(['X2', 15, 2], [@%, line('.'), col('.')])
normal G
Xabove 99
call assert_equal(['X2', 5, 1], [bufname(''), line('.'), col('.')])
call assert_equal(['X2', 5, 1], [@%, line('.'), col('.')])
normal G
Xbefore 99
call assert_equal(['X2', 5, 1], [bufname(''), line('.'), col('.')])
call assert_equal(['X2', 5, 1], [@%, line('.'), col('.')])
normal 10G
Xabove
call assert_equal(['X2', 5, 1], [bufname(''), line('.'), col('.')])
call assert_equal(['X2', 5, 1], [@%, line('.'), col('.')])
normal 10G$
2Xbefore
call assert_equal(['X2', 10, 2], [bufname(''), line('.'), col('.')])
call assert_equal(['X2', 10, 2], [@%, line('.'), col('.')])
normal 10G
Xbelow
call assert_equal(['X2', 15, 1], [bufname(''), line('.'), col('.')])
call assert_equal(['X2', 15, 1], [@%, line('.'), col('.')])
normal 9G
5Xafter
call assert_equal(['X2', 15, 2], [bufname(''), line('.'), col('.')])
call assert_equal(['X2', 15, 2], [@%, line('.'), col('.')])
" Invalid range
if a:cchar == 'c'

View File

@@ -641,4 +641,22 @@ func Test_execute_reg_as_ex_cmd()
call assert_equal(repeat('abcdefghijklmnopqrstuvwxyz', 312), str)
endfunc
" Test for clipboard registers with ASCII NUL
func Test_clipboard_nul()
CheckFeature clipboard_working
new
" Test for putting ASCII NUL into the clipboard
set clipboard=unnamed
call append(0, "\ntest")
normal ggyyp
call assert_equal("^@test^@", strtrans(getreg('*')))
call assert_equal(getline(1), getline(2))
let b = split(execute(":reg *"), "\n")
call assert_match('"\*\s*\^@test\^J',b[1])
set clipboard&vim
bwipe!
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@@ -1,10 +1,7 @@
" Test 'statusline'
"
" Not tested yet:
" %a
" %N
" %T
" %X
source view_util.vim
source check.vim
@@ -64,7 +61,17 @@ endfunc
func Test_statusline()
CheckFeature quickfix
new Xstatusline
" %a: Argument list ({current} of {max})
set statusline=%a
call assert_match('^\s*$', s:get_statusline())
arglocal a1 a2
rewind
call assert_match('^ (1 of 2)\s*$', s:get_statusline())
next
call assert_match('^ (2 of 2)\s*$', s:get_statusline())
e Xstatusline
call assert_match('^ ((2) of 2)\s*$', s:get_statusline())
only
set laststatus=2
set splitbelow
@@ -95,6 +102,18 @@ func Test_statusline()
set statusline=%F
call assert_match('/testdir/Xstatusline\s*$', s:get_statusline())
" Test for min and max width with %(. For some reason, if this test is moved
" after the below test for the help buffer flag, then the code to truncate
" the string is not executed.
set statusline=%015(%f%)
call assert_match('^ Xstatusline\s*$', s:get_statusline())
set statusline=%.6(%f%)
call assert_match('^<sline\s*$', s:get_statusline())
set statusline=%14f
call assert_match('^ Xstatusline\s*$', s:get_statusline())
set statusline=%.4L
call assert_match('^10>3\s*$', s:get_statusline())
" %h: Help buffer flag, text is "[help]".
" %H: Help buffer flag, text is ",HLP".
set statusline=%h,%H
@@ -413,3 +432,5 @@ func Test_statusline_removed_group()
call StopVimInTerminal(buf)
call delete('XTest_statusline')
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@@ -70,3 +70,46 @@ func Test_redrawtabline()
let &showtabline = showtabline_save
au! Bufadd
endfunc
" Test for the "%T" and "%X" flags in the 'tabline' option
func MyTabLine()
let s = ''
for i in range(tabpagenr('$'))
" set the tab page number (for mouse clicks)
let s .= '%' . (i + 1) . 'T'
" the label is made by MyTabLabel()
let s .= ' %{MyTabLabel(' . (i + 1) . ')} '
endfor
" after the last tab fill with TabLineFill and reset tab page nr
let s .= '%T'
" right-align the label to close the current tab page
if tabpagenr('$') > 1
let s .= '%=%Xclose'
endif
return s
endfunc
func MyTabLabel(n)
let buflist = tabpagebuflist(a:n)
let winnr = tabpagewinnr(a:n)
return bufname(buflist[winnr - 1])
endfunc
func Test_tabline_flags()
if has('gui')
set guioptions-=e
endif
set tabline=%!MyTabLine()
edit Xtabline1
tabnew Xtabline2
redrawtabline
call assert_match('^ Xtabline1 Xtabline2\s\+close$', Screenline(1))
set tabline=
%bw!
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@@ -784,78 +784,6 @@ func Test_tw_2_fo_tm_noai()
bwipe!
endfunc
func Test_tw_2_fo_cqm_com()
new
let t =<< trim END
{
a
a
a
}
END
call setline(1, t)
call cursor(2, 1)
set tw=2 fo=cqm comments=n:
exe "normal gqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgq"
let t =<< trim END
a
a
a
END
exe "normal o\n" . join(t, "\n")
let expected =<< trim END
{
a
a
a
a
a
a
}
END
call assert_equal(expected, getline(1, '$'))
set tw& fo& comments&
bwipe!
endfunc
func Test_tw_2_fo_tm_replace()
new
let t =<< trim END
@@ -975,140 +903,6 @@ func Test_whichwrap_multi_byte()
bwipe!
endfunc
" Test for automatically adding comment leaders in insert mode
func Test_threepiece_comment()
new
setlocal expandtab
call setline(1, ["\t/*"])
setlocal formatoptions=croql
call cursor(1, 3)
call feedkeys("A\<cr>\<cr>/", 'tnix')
call assert_equal(["\t/*", " *", " */"], getline(1, '$'))
" If a comment ends in a single line, then don't add it in the next line
%d
call setline(1, '/* line1 */')
call feedkeys("A\<CR>next line", 'xt')
call assert_equal(['/* line1 */', 'next line'], getline(1, '$'))
%d
" Copy the trailing indentation from the leader comment to a new line
setlocal autoindent noexpandtab
call feedkeys("a\t/*\tone\ntwo\n/", 'xt')
call assert_equal(["\t/*\tone", "\t *\ttwo", "\t */"], getline(1, '$'))
close!
endfunc
" Test for the 'f' flag in 'comments' (only the first line has the comment
" string)
func Test_firstline_comment()
new
setlocal comments=f:- fo+=ro
exe "normal i- B\nD\<C-C>ggoC\<C-C>ggOA\<C-C>"
call assert_equal(['A', '- B', ' C', ' D'], getline(1, '$'))
%d
setlocal comments=:-
exe "normal i- B\nD\<C-C>ggoC\<C-C>ggOA\<C-C>"
call assert_equal(['- A', '- B', '- C', '- D'], getline(1, '$'))
%bw!
endfunc
" Test for the 'r' flag in 'comments' (right align comment)
func Test_comment_rightalign()
new
setlocal comments=sr:/***,m:**,ex-2:******/ fo+=ro
exe "normal i=\<C-C>o\t /***\nD\n/"
exe "normal 2GOA\<C-C>joB\<C-C>jOC\<C-C>joE\<C-C>GOF\<C-C>joG"
let expected =<< trim END
=
A
/***
** B
** C
** D
** E
** F
******/
G
END
call assert_equal(expected, getline(1, '$'))
%bw!
endfunc
" Test for the 'b' flag in 'comments'
func Test_comment_blank()
new
setlocal comments=b:* fo+=ro
exe "normal i* E\nF\n\<BS>G\nH\<C-C>ggOC\<C-C>O\<BS>B\<C-C>OA\<C-C>2joD"
let expected =<< trim END
A
*B
* C
* D
* E
* F
*G
H
END
call assert_equal(expected, getline(1, '$'))
%bw!
endfunc
" Test for the 'n' flag in comments
func Test_comment_nested()
new
setlocal comments=n:> fo+=ro
exe "normal i> B\nD\<C-C>ggOA\<C-C>joC\<C-C>Go\<BS>>>> F\nH"
exe "normal 5GOE\<C-C>6GoG"
let expected =<< trim END
> A
> B
> C
> D
>>>> E
>>>> F
>>>> G
>>>> H
END
call assert_equal(expected, getline(1, '$'))
%bw!
endfunc
" Test for a space character in 'comments' setting
func Test_comment_space()
new
setlocal comments=b:\ > fo+=ro
exe "normal i> B\nD\<C-C>ggOA\<C-C>joC"
exe "normal Go > F\nH\<C-C>kOE\<C-C>joG"
let expected =<< trim END
A
> B
C
D
> E
> F
> G
> H
END
call assert_equal(expected, getline(1, '$'))
%bw!
endfunc
" Test for the 'O' flag in 'comments'
func Test_comment_O()
new
setlocal comments=Ob:* fo+=ro
exe "normal i* B\nD\<C-C>kOA\<C-C>joC"
let expected =<< trim END
A
* B
* C
* D
END
call assert_equal(expected, getline(1, '$'))
%bw!
endfunc
" Test for 'a' and 'w' flags in 'formatoptions'
func Test_fo_a_w()
new
@@ -1143,25 +937,6 @@ func Test_fo_a_w()
%bw!
endfunc
" Test for 'j' flag in 'formatoptions'
func Test_fo_j()
new
setlocal fo+=j comments=://
call setline(1, ['i++; // comment1', ' // comment2'])
normal J
call assert_equal('i++; // comment1 comment2', getline(1))
setlocal fo-=j
call setline(1, ['i++; // comment1', ' // comment2'])
normal J
call assert_equal('i++; // comment1 // comment2', getline(1))
" Test with nested comments
setlocal fo+=j comments=n:>,n:)
call setline(1, ['i++; > ) > ) comment1', ' > ) comment2'])
normal J
call assert_equal('i++; > ) > ) comment1 comment2', getline(1))
%bw!
endfunc
" Test for formatting lines using gq in visual mode
func Test_visual_gq_format()
new
@@ -1296,51 +1071,4 @@ func Test_fo_2()
close!
endfunc
" Test for formatting lines where only the first line has a comment.
func Test_fo_gq_with_firstline_comment()
new
setlocal formatoptions=tcq
call setline(1, ['- one two', 'three'])
normal gggqG
call assert_equal(['- one two three'], getline(1, '$'))
%d
call setline(1, ['- one', '- two'])
normal gggqG
call assert_equal(['- one', '- two'], getline(1, '$'))
close!
endfunc
" Test for trying to join a comment line with a non-comment line
func Test_join_comments()
new
call setline(1, ['one', '/* two */', 'three'])
normal gggqG
call assert_equal(['one', '/* two */', 'three'], getline(1, '$'))
close!
endfunc
" Test for using 'a' in 'formatoptions' with comments
func Test_autoformat_comments()
new
setlocal formatoptions+=a
call feedkeys("a- one\n- two\n", 'xt')
call assert_equal(['- one', '- two', ''], getline(1, '$'))
%d
call feedkeys("a\none\n", 'xt')
call assert_equal(['', 'one', ''], getline(1, '$'))
setlocal formatoptions+=aw
%d
call feedkeys("aone \ntwo\n", 'xt')
call assert_equal(['one two', ''], getline(1, '$'))
%d
call feedkeys("aone\ntwo\n", 'xt')
call assert_equal(['one', 'two', ''], getline(1, '$'))
close!
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@@ -13,6 +13,9 @@ def Test_expr1()
assert_equal('one', 0.1 ? 'one' : 'two')
endif
assert_equal('one', 'x' ? 'one' : 'two')
assert_equal('one', 'x'
? 'one'
: 'two')
assert_equal('one', 0z1234 ? 'one' : 'two')
assert_equal('one', [0] ? 'one' : 'two')
assert_equal('one', #{x: 0} ? 'one' : 'two')
@@ -70,6 +73,8 @@ def Test_expr2()
0 ||
7)
assert_equal(0, 0 || 0)
assert_equal(0, 0
|| 0)
assert_equal('', 0 || '')
g:vals = []
@@ -81,7 +86,9 @@ def Test_expr2()
assert_equal([0, 5], g:vals)
g:vals = []
assert_equal(4, Record(0) || Record(4) || Record(0))
assert_equal(4, Record(0)
|| Record(4)
|| Record(0))
assert_equal([0, 4], g:vals)
g:vals = []
@@ -104,7 +111,9 @@ def Test_expr3()
assert_equal(0, 0 &&
0 &&
7)
assert_equal(7, 2 && 3 && 7)
assert_equal(7, 2
&& 3
&& 7)
assert_equal(0, 0 && 0)
assert_equal(0, 0 && '')
assert_equal('', 8 && '')
@@ -158,7 +167,8 @@ def Test_expr4_equal()
assert_equal(true, true == true)
assert_equal(false, true ==
false)
assert_equal(true, true == trueVar)
assert_equal(true, true
== trueVar)
assert_equal(false, true == falseVar)
assert_equal(true, true == g:atrue)
assert_equal(false, g:atrue == false)
@@ -250,7 +260,8 @@ def Test_expr4_notequal()
assert_equal(false, true != true)
assert_equal(true, true !=
false)
assert_equal(false, true != trueVar)
assert_equal(false, true
!= trueVar)
assert_equal(true, true != falseVar)
assert_equal(false, true != g:atrue)
assert_equal(true, g:atrue != false)
@@ -334,7 +345,8 @@ def Test_expr4_greater()
assert_true(nr2 >
1)
assert_false(nr2 > 2)
assert_false(nr2 > 3)
assert_false(nr2
> 3)
if has('float')
let ff = 2.0
assert_true(ff > 0.0)
@@ -367,7 +379,8 @@ def Test_expr4_smaller()
assert_false(2 < 0)
assert_false(2 <
2)
assert_true(2 < 3)
assert_true(2
< 3)
let nr2 = 2
assert_false(nr2 < 0)
assert_false(nr2 < 2)
@@ -385,7 +398,8 @@ def Test_expr4_smallerequal()
assert_false(2 <= 0)
assert_false(2 <=
1)
assert_true(2 <= 2)
assert_true(2
<= 2)
assert_true(2 <= 3)
let nr2 = 2
assert_false(nr2 <= 0)
@@ -404,6 +418,8 @@ enddef
" test =~ comperator
def Test_expr4_match()
assert_equal(false, '2' =~ '0')
assert_equal(false, ''
=~ '0')
assert_equal(true, '2' =~
'[0-9]')
enddef
@@ -411,6 +427,8 @@ enddef
" test !~ comperator
def Test_expr4_nomatch()
assert_equal(true, '2' !~ '0')
assert_equal(true, ''
!~ '0')
assert_equal(false, '2' !~
'[0-9]')
enddef
@@ -424,7 +442,8 @@ def Test_expr4_is()
other)
let myblob = 0z1234
assert_false(myblob is 0z1234)
assert_false(myblob
is 0z1234)
let otherblob = myblob
assert_true(myblob is otherblob)
enddef
@@ -439,7 +458,8 @@ def Test_expr4_isnot()
other)
let myblob = 0z1234
assert_true(myblob isnot 0z1234)
assert_true(myblob
isnot 0z1234)
let otherblob = myblob
assert_false(myblob isnot otherblob)
enddef
@@ -522,30 +542,54 @@ def Test_expr5()
assert_equal(66, 60 + 6)
assert_equal(70, 60 +
g:anint)
assert_equal(9, g:alsoint + 5)
assert_equal(9, g:alsoint
+ 5)
assert_equal(14, g:alsoint + g:anint)
assert_equal([1, 2, 3, 4], [1] + g:alist)
assert_equal(54, 60 - 6)
assert_equal(50, 60 -
g:anint)
assert_equal(-1, g:alsoint - 5)
assert_equal(-1, g:alsoint
- 5)
assert_equal(-6, g:alsoint - g:anint)
assert_equal('hello', 'hel' .. 'lo')
assert_equal('hello 123', 'hello ' ..
123)
assert_equal('hello 123', 'hello ' .. 123)
assert_equal('hello 123', 'hello '
.. 123)
assert_equal('123 hello', 123 .. ' hello')
assert_equal('123456', 123 .. 456)
assert_equal([1, 2, 3, 4], [1, 2] + [3, 4])
assert_equal(0z11223344, 0z1122 + 0z3344)
assert_equal(0z112201ab, 0z1122 + g:ablob)
assert_equal(0z112201ab, 0z1122
+ g:ablob)
assert_equal(0z01ab3344, g:ablob + 0z3344)
assert_equal(0z01ab01ab, g:ablob + g:ablob)
enddef
def Test_expr5_vim9script()
" only checks line continuation
let lines =<< trim END
vim9script
let var = 11
+ 77
- 22
assert_equal(66, var)
END
CheckScriptSuccess(lines)
lines =<< trim END
vim9script
let var = 'one'
.. 'two'
assert_equal('onetwo', var)
END
CheckScriptSuccess(lines)
enddef
def Test_expr5_float()
if !has('float')
MissingFeature 'float'
@@ -554,13 +598,15 @@ def Test_expr5_float()
assert_equal(66.0, 60.0 + 6)
assert_equal(66.0, 60 +
6.0)
assert_equal(5.1, g:afloat + 5)
assert_equal(5.1, g:afloat
+ 5)
assert_equal(8.1, 8 + g:afloat)
assert_equal(10.1, g:anint + g:afloat)
assert_equal(10.1, g:afloat + g:anint)
assert_equal(54.0, 60.0 - 6.0)
assert_equal(54.0, 60.0 - 6)
assert_equal(54.0, 60.0
- 6)
assert_equal(54.0, 60 - 6.0)
assert_equal(-4.9, g:afloat - 5)
assert_equal(7.9, 8 - g:afloat)
@@ -585,12 +631,12 @@ func Test_expr5_fails()
call CheckDefFailure(["let x = '1' ..'2'"], msg)
call CheckDefFailure(["let x = '1'.. '2'"], msg)
call CheckDefFailure(["let x = 0z1122 + 33"], 'E1035')
call CheckDefFailure(["let x = 0z1122 + [3]"], 'E1035')
call CheckDefFailure(["let x = 0z1122 + 'asd'"], 'E1035')
call CheckDefFailure(["let x = 33 + 0z1122"], 'E1035')
call CheckDefFailure(["let x = [3] + 0z1122"], 'E1035')
call CheckDefFailure(["let x = 'asdf' + 0z1122"], 'E1035')
call CheckDefFailure(["let x = 0z1122 + 33"], 'E1051')
call CheckDefFailure(["let x = 0z1122 + [3]"], 'E1051')
call CheckDefFailure(["let x = 0z1122 + 'asd'"], 'E1051')
call CheckDefFailure(["let x = 33 + 0z1122"], 'E1051')
call CheckDefFailure(["let x = [3] + 0z1122"], 'E1051')
call CheckDefFailure(["let x = 'asdf' + 0z1122"], 'E1051')
call CheckDefFailure(["let x = 6 + xxx"], 'E1001')
endfunc
@@ -599,20 +645,23 @@ def Test_expr6()
assert_equal(36, 6 * 6)
assert_equal(24, 6 *
g:alsoint)
assert_equal(24, g:alsoint * 6)
assert_equal(24, g:alsoint
* 6)
assert_equal(40, g:anint * g:alsoint)
assert_equal(10, 60 / 6)
assert_equal(6, 60 /
g:anint)
assert_equal(1, g:anint / 6)
assert_equal(2, g:anint / g:alsoint)
assert_equal(2, g:anint
/ g:alsoint)
assert_equal(5, 11 % 6)
assert_equal(4, g:anint % 6)
assert_equal(3, 13 %
g:anint)
assert_equal(2, g:anint % g:alsoint)
assert_equal(2, g:anint
% g:alsoint)
assert_equal(4, 6 * 4 / 6)
@@ -623,13 +672,35 @@ def Test_expr6()
if has('float')
let xf = [2.0]
let yf = [3.0]
assert_equal(5.0, xf[0] + yf[0])
assert_equal(6.0, xf[0] * yf[0])
assert_equal(5.0, xf[0]
+ yf[0])
assert_equal(6.0, xf[0]
* yf[0])
endif
call CheckDefFailure(["let x = 6 * xxx"], 'E1001')
enddef
def Test_expr6_vim9script()
" only checks line continuation
let lines =<< trim END
vim9script
let var = 11
* 22
/ 3
assert_equal(80, var)
END
CheckScriptSuccess(lines)
lines =<< trim END
vim9script
let var = 25
% 10
assert_equal(5, var)
END
CheckScriptSuccess(lines)
enddef
def Test_expr6_float()
if !has('float')
MissingFeature 'float'

View File

@@ -837,5 +837,16 @@ def Test_sort_return_type()
res = [1, 2, 3]->sort()
enddef
def Line_continuation_in_def(dir: string = ''): string
let path: string = empty(dir)
\ ? 'empty'
\ : 'full'
return path
enddef
def Test_line_continuation_in_def()
assert_equal('full', Line_continuation_in_def('.'))
enddef
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker

View File

@@ -468,6 +468,14 @@ func Test_const()
call CheckDefFailure(['const &option'], 'E996:')
endfunc
def Test_range_no_colon()
call CheckDefFailure(['%s/a/b/'], 'E1050:')
call CheckDefFailure(['+ s/a/b/'], 'E1050:')
call CheckDefFailure(['- s/a/b/'], 'E1050:')
call CheckDefFailure(['. s/a/b/'], 'E1050:')
enddef
def Test_block()
let outer = 1
{
@@ -1279,7 +1287,7 @@ def Test_echomsg_cmd()
echomsg 'some' 'more' # comment
assert_match('^some more$', Screenline(&lines))
echo 'clear'
1messages
:1messages
assert_match('^some more$', Screenline(&lines))
call CheckDefFailure(['echomsg "xxx"# comment'], 'E488:')
@@ -1898,7 +1906,7 @@ def Test_vim9_comment_not_compiled()
'vim9script',
'new'
'call setline(1, ["# define pat", "last"])',
'$',
':$',
'dsearch /pat/ #comment',
'bwipe!',
])
@@ -1907,7 +1915,7 @@ def Test_vim9_comment_not_compiled()
'vim9script',
'new'
'call setline(1, ["# define pat", "last"])',
'$',
':$',
'dsearch /pat/#comment',
'bwipe!',
], 'E488:')

View File

@@ -239,7 +239,7 @@ get_function_args(
whitep = p;
p = skipwhite(p);
expr = p;
if (eval1(&p, &rettv, 0) != FAIL)
if (eval1(&p, &rettv, NULL) != FAIL)
{
if (ga_grow(default_args, 1) == FAIL)
goto err_ret;
@@ -341,6 +341,51 @@ get_lambda_name(void)
return name;
}
#if defined(FEAT_LUA) || defined(PROTO)
/*
* Registers a native C callback which can be called from Vim script.
* Returns the name of the Vim script function.
*/
char_u *
register_cfunc(cfunc_T cb, cfunc_free_T cb_free, void *state)
{
char_u *name = get_lambda_name();
ufunc_T *fp = NULL;
garray_T newargs;
garray_T newlines;
ga_init(&newargs);
ga_init(&newlines);
fp = alloc_clear(offsetof(ufunc_T, uf_name) + STRLEN(name) + 1);
if (fp == NULL)
goto errret;
fp->uf_dfunc_idx = UF_NOT_COMPILED;
fp->uf_refcount = 1;
fp->uf_varargs = TRUE;
fp->uf_flags = FC_CFUNC;
fp->uf_calls = 0;
fp->uf_script_ctx = current_sctx;
fp->uf_lines = newlines;
fp->uf_args = newargs;
fp->uf_cb = cb;
fp->uf_cb_free = cb_free;
fp->uf_cb_state = state;
set_ufunc_name(fp, name);
hash_add(&func_hashtab, UF2HIKEY(fp));
return name;
errret:
ga_clear_strings(&newargs);
ga_clear_strings(&newlines);
vim_free(fp);
return NULL;
}
#endif
/*
* Parse a lambda expression and get a Funcref from "*arg".
* Return OK or FAIL. Returns NOTDONE for dict or {expr}.
@@ -561,6 +606,10 @@ get_func_tv(
int ret = OK;
typval_T argvars[MAX_FUNC_ARGS + 1]; // vars for arguments
int argcount = 0; // number of arguments found
evalarg_T evalarg;
CLEAR_FIELD(evalarg);
evalarg.eval_flags = funcexe->evaluate ? EVAL_EVALUATE : 0;
/*
* Get the arguments.
@@ -572,8 +621,7 @@ get_func_tv(
argp = skipwhite(argp + 1); // skip the '(' or ','
if (*argp == ')' || *argp == ',' || *argp == NUL)
break;
if (eval1(&argp, &argvars[argcount],
funcexe->evaluate ? EVAL_EVALUATE : 0) == FAIL)
if (eval1(&argp, &argvars[argcount], &evalarg) == FAIL)
{
ret = FAIL;
break;
@@ -1024,6 +1072,17 @@ func_clear_items(ufunc_T *fp)
vim_free(((type_T **)fp->uf_type_list.ga_data)
[--fp->uf_type_list.ga_len]);
ga_clear(&fp->uf_type_list);
#ifdef FEAT_LUA
if (fp->uf_cb_free != NULL)
{
fp->uf_cb_free(fp->uf_cb_state);
fp->uf_cb_free = NULL;
}
fp->uf_cb_state = NULL;
fp->uf_cb = NULL;
#endif
#ifdef FEAT_PROFILE
VIM_CLEAR(fp->uf_tml_count);
VIM_CLEAR(fp->uf_tml_total);
@@ -1249,7 +1308,7 @@ call_user_func(
default_expr = ((char_u **)(fp->uf_def_args.ga_data))
[ai + fp->uf_def_args.ga_len];
if (eval1(&default_expr, &def_rettv, EVAL_EVALUATE) == FAIL)
if (eval1(&default_expr, &def_rettv, &EVALARG_EVALUATE) == FAIL)
{
default_arg_err = 1;
break;
@@ -1394,7 +1453,7 @@ call_user_func(
// A Lambda always has the command "return {expr}". It is much faster
// to evaluate {expr} directly.
++ex_nesting_level;
(void)eval1(&p, rettv, EVAL_EVALUATE);
(void)eval1(&p, rettv, &EVALARG_EVALUATE);
--ex_nesting_level;
}
else
@@ -1970,6 +2029,14 @@ call_func(
if (fp != NULL && (fp->uf_flags & FC_DELETED))
error = FCERR_DELETED;
#ifdef FEAT_LUA
else if (fp != NULL && (fp->uf_flags & FC_CFUNC))
{
cfunc_T cb = fp->uf_cb;
error = (*cb)(argcount, argvars, rettv, fp->uf_cb_state);
}
#endif
else if (fp != NULL)
{
if (funcexe->argv_func != NULL)
@@ -3697,6 +3764,7 @@ ex_return(exarg_T *eap)
char_u *arg = eap->arg;
typval_T rettv;
int returning = FALSE;
evalarg_T evalarg;
if (current_funccal == NULL)
{
@@ -3704,13 +3772,15 @@ ex_return(exarg_T *eap)
return;
}
CLEAR_FIELD(evalarg);
evalarg.eval_flags = eap->skip ? 0 : EVAL_EVALUATE;
if (eap->skip)
++emsg_skip;
eap->nextcmd = NULL;
if ((*arg != NUL && *arg != '|' && *arg != '\n')
&& eval0(arg, &rettv, &eap->nextcmd, eap->skip ? 0 : EVAL_EVALUATE)
!= FAIL)
&& eval0(arg, &rettv, eap, &evalarg) != FAIL)
{
if (!eap->skip)
returning = do_return(eap, FALSE, TRUE, &rettv);
@@ -3767,7 +3837,7 @@ ex_call(exarg_T *eap)
// instead to skip to any following command, e.g. for:
// :if 0 | call dict.foo().bar() | endif
++emsg_skip;
if (eval0(eap->arg, &rettv, &eap->nextcmd, 0) != FAIL)
if (eval0(eap->arg, &rettv, eap, NULL) != FAIL)
clear_tv(&rettv);
--emsg_skip;
return;

View File

@@ -754,6 +754,42 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
1054,
/**/
1053,
/**/
1052,
/**/
1051,
/**/
1050,
/**/
1049,
/**/
1048,
/**/
1047,
/**/
1046,
/**/
1045,
/**/
1044,
/**/
1043,
/**/
1042,
/**/
1041,
/**/
1040,
/**/
1039,
/**/
1038,
/**/
1037,
/**/
1036,
/**/

View File

@@ -2665,10 +2665,6 @@ long elapsed(DWORD start_tick);
#define REPTERM_SPECIAL 4
#define REPTERM_NO_SIMPLIFY 8
// Flags for expression evaluation.
#define EVAL_EVALUATE 1 // when missing don't actually evaluate
#define EVAL_CONSTANT 2 // when not a constant return FAIL
// Flags for find_special_key()
#define FSK_KEYCODE 0x01 // prefer key code, e.g. K_DEL instead of DEL
#define FSK_KEEP_X_KEY 0x02 // don't translate xHome to Home key

View File

@@ -643,7 +643,7 @@ check_number_or_float(vartype_T type1, vartype_T type2, char_u *op)
|| type2 == VAR_ANY)))
{
if (*op == '+')
emsg(_("E1035: wrong argument type for +"));
emsg(_("E1051: wrong argument type for +"));
else
semsg(_("E1036: %c requires number or float arguments"), *op);
return FAIL;
@@ -2402,14 +2402,38 @@ peek_next_line(cctx_T *cctx)
while (++lnum < cctx->ctx_ufunc->uf_lines.ga_len)
{
char_u *line = ((char_u **)cctx->ctx_ufunc->uf_lines.ga_data)[lnum];
char_u *p = skipwhite(line);
char_u *p;
if (line == NULL)
break;
p = skipwhite(line);
if (*p != NUL && !comment_start(p))
return p;
}
return NULL;
}
/*
* Called when checking for a following operator at "arg". When the rest of
* the line is empty or only a comment, peek the next line. If there is a next
* line return a pointer to it and set "nextp".
* Otherwise skip over white space.
*/
static char_u *
may_peek_next_line(cctx_T *cctx, char_u *arg, char_u **nextp)
{
char_u *p = skipwhite(arg);
*nextp = NULL;
if (*p == NUL || (VIM_ISWHITE(*arg) && comment_start(p)))
{
*nextp = peek_next_line(cctx);
if (*nextp != NULL)
return *nextp;
}
return p;
}
/*
* Get the next line of the function from "cctx".
* Skips over empty lines. Skips over comment lines if "skip_comment" is TRUE.
@@ -3944,6 +3968,7 @@ compile_expr7(
compile_expr6(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
{
char_u *op;
char_u *next;
int ppconst_used = ppconst->pp_used;
// get the first expression
@@ -3955,9 +3980,14 @@ compile_expr6(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
*/
for (;;)
{
op = skipwhite(*arg);
op = may_peek_next_line(cctx, *arg, &next);
if (*op != '*' && *op != '/' && *op != '%')
break;
if (next != NULL)
{
*arg = next_line_from_context(cctx, TRUE);
op = skipwhite(*arg);
}
if (!IS_WHITE_OR_NUL(**arg) || !IS_WHITE_OR_NUL(op[1]))
{
@@ -4015,6 +4045,7 @@ compile_expr6(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
compile_expr5(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
{
char_u *op;
char_u *next;
int oplen;
int ppconst_used = ppconst->pp_used;
@@ -4027,10 +4058,15 @@ compile_expr5(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
*/
for (;;)
{
op = skipwhite(*arg);
if (*op != '+' && *op != '-' && !(*op == '.' && (*(*arg + 1) == '.')))
op = may_peek_next_line(cctx, *arg, &next);
if (*op != '+' && *op != '-' && !(*op == '.' && *(op + 1) == '.'))
break;
oplen = (*op == '.' ? 2 : 1);
if (next != NULL)
{
*arg = next_line_from_context(cctx, TRUE);
op = skipwhite(*arg);
}
if (!IS_WHITE_OR_NUL(**arg) || !IS_WHITE_OR_NUL(op[oplen]))
{
@@ -4124,6 +4160,7 @@ compile_expr4(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
{
exptype_T type = EXPR_UNKNOWN;
char_u *p;
char_u *next;
int len = 2;
int type_is = FALSE;
int ppconst_used = ppconst->pp_used;
@@ -4132,7 +4169,7 @@ compile_expr4(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
if (compile_expr5(arg, cctx, ppconst) == FAIL)
return FAIL;
p = skipwhite(*arg);
p = may_peek_next_line(cctx, *arg, &next);
type = get_compare_type(p, &len, &type_is);
/*
@@ -4142,6 +4179,11 @@ compile_expr4(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
{
int ic = FALSE; // Default: do not ignore case
if (next != NULL)
{
*arg = next_line_from_context(cctx, TRUE);
p = skipwhite(*arg);
}
if (type_is && (p[len] == '?' || p[len] == '#'))
{
semsg(_(e_invexpr2), *arg);
@@ -4218,7 +4260,8 @@ compile_and_or(
ppconst_T *ppconst,
int ppconst_used UNUSED)
{
char_u *p = skipwhite(*arg);
char_u *next;
char_u *p = may_peek_next_line(cctx, *arg, &next);
int opchar = *op;
if (p[0] == opchar && p[1] == opchar)
@@ -4232,6 +4275,12 @@ compile_and_or(
ga_init2(&end_ga, sizeof(int), 10);
while (p[0] == opchar && p[1] == opchar)
{
if (next != NULL)
{
*arg = next_line_from_context(cctx, TRUE);
p = skipwhite(*arg);
}
if (!IS_WHITE_OR_NUL(**arg) || !IS_WHITE_OR_NUL(p[2]))
{
semsg(_(e_white_both), op);
@@ -4262,7 +4311,8 @@ compile_and_or(
ga_clear(&end_ga);
return FAIL;
}
p = skipwhite(*arg);
p = may_peek_next_line(cctx, *arg, &next);
}
generate_ppconst(cctx, ppconst);
@@ -4346,12 +4396,13 @@ compile_expr1(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
{
char_u *p;
int ppconst_used = ppconst->pp_used;
char_u *next;
// Evaluate the first expression.
if (compile_expr2(arg, cctx, ppconst) == FAIL)
return FAIL;
p = skipwhite(*arg);
p = may_peek_next_line(cctx, *arg, &next);
if (*p == '?')
{
garray_T *instr = &cctx->ctx_instr;
@@ -4365,6 +4416,12 @@ compile_expr1(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
int const_value = FALSE;
int save_skip = cctx->ctx_skip;
if (next != NULL)
{
*arg = next_line_from_context(cctx, TRUE);
p = skipwhite(*arg);
}
if (!IS_WHITE_OR_NUL(**arg) || !IS_WHITE_OR_NUL(p[1]))
{
semsg(_(e_white_both), "?");
@@ -4412,12 +4469,18 @@ compile_expr1(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
}
// Check for the ":".
p = skipwhite(*arg);
p = may_peek_next_line(cctx, *arg, &next);
if (*p != ':')
{
emsg(_(e_missing_colon));
return FAIL;
}
if (next != NULL)
{
*arg = next_line_from_context(cctx, TRUE);
p = skipwhite(*arg);
}
if (!IS_WHITE_OR_NUL(**arg) || !IS_WHITE_OR_NUL(p[1]))
{
semsg(_(e_white_both), ":");
@@ -6692,6 +6755,7 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
{
exarg_T ea;
int starts_with_colon = FALSE;
char_u *cmd;
// Bail out on the first error to avoid a flood of errors and report
// the right line number when inside try/catch.
@@ -6850,7 +6914,13 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
/*
* COMMAND after range
*/
cmd = ea.cmd;
ea.cmd = skip_range(ea.cmd, NULL);
if (ea.cmd > cmd && !starts_with_colon)
{
emsg(_(e_colon_required));
goto erret;
}
p = find_ex_command(&ea, NULL, starts_with_colon ? NULL
: (void *(*)(char_u *, size_t, cctx_T *))lookup_local,
&cctx);
@@ -7005,8 +7075,9 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
line = compile_mult_expr(p, ea.cmdidx, &cctx);
break;
// TODO: other commands with an expression argument
default:
// TODO: other commands with an expression argument
// Not recognized, execute with do_cmdline_cmd().
ea.arg = p;
line = compile_exec(line, &ea, &cctx);

View File

@@ -422,7 +422,7 @@ clip_mch_request_selection(Clipboard_T *cbd)
}
}
if (str != NULL && *str != NUL)
if (str != NULL && metadata.txtlen != 0)
{
char_u *temp_clipboard;
@@ -543,7 +543,7 @@ clip_mch_set_selection(Clipboard_T *cbd)
if (lpszMem)
{
vim_strncpy((char_u *)lpszMem, str, metadata.txtlen);
mch_memmove((char_u *)lpszMem, str, metadata.txtlen);
GlobalUnlock(hMem);
}
}