mirror of
https://github.com/neovim/neovim.git
synced 2026-08-25 00:21:55 +00:00
Merge #15293 Vimscript "method" syntax
Port VimL's method call syntax - vim-patch:8.1.{1638,1800,1803,1807,1809,1816,1820,1821,1828,1834,1835,1861,1863,1878,1879,1888,1909,1911,1912}
This commit is contained in:
@@ -608,12 +608,15 @@ static Object _call_function(String fn, Array args, dict_T *self, Error *err)
|
||||
recursive++;
|
||||
try_start();
|
||||
typval_T rettv;
|
||||
int dummy;
|
||||
funcexe_T funcexe = FUNCEXE_INIT;
|
||||
funcexe.firstline = curwin->w_cursor.lnum;
|
||||
funcexe.lastline = curwin->w_cursor.lnum;
|
||||
funcexe.evaluate = true;
|
||||
funcexe.selfdict = self;
|
||||
// call_func() retval is deceptive, ignore it. Instead we set `msg_list`
|
||||
// (see above) to capture abort-causing non-exception errors.
|
||||
(void)call_func((char_u *)fn.data, (int)fn.size, &rettv, (int)args.size,
|
||||
vim_args, NULL, curwin->w_cursor.lnum, curwin->w_cursor.lnum,
|
||||
&dummy, true, NULL, self);
|
||||
vim_args, &funcexe);
|
||||
if (!try_end(err)) {
|
||||
rv = vim_to_object(&rettv);
|
||||
}
|
||||
|
||||
531
src/nvim/eval.c
531
src/nvim/eval.c
@@ -65,6 +65,8 @@ static char *e_missbrac = N_("E111: Missing ']'");
|
||||
static char *e_dictrange = N_("E719: Cannot use [:] with a Dictionary");
|
||||
static char *e_illvar = N_("E461: Illegal variable name: %s");
|
||||
static char *e_cannot_mod = N_("E995: Cannot modify existing variable");
|
||||
static char *e_nowhitespace
|
||||
= N_("E274: No white space allowed before parenthesis");
|
||||
static char *e_invalwindow = N_("E957: Invalid window number");
|
||||
static char *e_lock_unlock = N_("E940: Cannot lock or unlock variable %s");
|
||||
|
||||
@@ -736,15 +738,15 @@ int eval_expr_typval(const typval_T *expr, typval_T *argv,
|
||||
int argc, typval_T *rettv)
|
||||
FUNC_ATTR_NONNULL_ARG(1, 2, 4)
|
||||
{
|
||||
int dummy;
|
||||
funcexe_T funcexe = FUNCEXE_INIT;
|
||||
|
||||
if (expr->v_type == VAR_FUNC) {
|
||||
const char_u *const s = expr->vval.v_string;
|
||||
if (s == NULL || *s == NUL) {
|
||||
return FAIL;
|
||||
}
|
||||
if (call_func(s, -1, rettv, argc, argv, NULL,
|
||||
0L, 0L, &dummy, true, NULL, NULL) == FAIL) {
|
||||
funcexe.evaluate = true;
|
||||
if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL) {
|
||||
return FAIL;
|
||||
}
|
||||
} else if (expr->v_type == VAR_PARTIAL) {
|
||||
@@ -753,8 +755,9 @@ int eval_expr_typval(const typval_T *expr, typval_T *argv,
|
||||
if (s == NULL || *s == NUL) {
|
||||
return FAIL;
|
||||
}
|
||||
if (call_func(s, -1, rettv, argc, argv, NULL,
|
||||
0L, 0L, &dummy, true, partial, NULL) == FAIL) {
|
||||
funcexe.evaluate = true;
|
||||
funcexe.partial = partial;
|
||||
if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL) {
|
||||
return FAIL;
|
||||
}
|
||||
} else {
|
||||
@@ -1050,7 +1053,6 @@ int call_vim_function(
|
||||
)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
int doesrange;
|
||||
int ret;
|
||||
int len = (int)STRLEN(func);
|
||||
partial_T *pt = NULL;
|
||||
@@ -1066,9 +1068,12 @@ int call_vim_function(
|
||||
}
|
||||
|
||||
rettv->v_type = VAR_UNKNOWN; // tv_clear() uses this.
|
||||
ret = call_func(func, len, rettv, argc, argv, NULL,
|
||||
curwin->w_cursor.lnum, curwin->w_cursor.lnum,
|
||||
&doesrange, true, pt, NULL);
|
||||
funcexe_T funcexe = FUNCEXE_INIT;
|
||||
funcexe.firstline = curwin->w_cursor.lnum;
|
||||
funcexe.lastline = curwin->w_cursor.lnum;
|
||||
funcexe.evaluate = true;
|
||||
funcexe.partial = pt;
|
||||
ret = call_func(func, len, rettv, argc, argv, &funcexe);
|
||||
|
||||
fail:
|
||||
if (ret == FAIL) {
|
||||
@@ -1724,7 +1729,9 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first)
|
||||
} else {
|
||||
// handle d.key, l[idx], f(expr)
|
||||
const char *const arg_subsc = arg;
|
||||
if (handle_subscript(&arg, &tv, true, true) == FAIL) {
|
||||
if (handle_subscript(&arg, &tv, true, true, (const char_u *)name,
|
||||
(const char_u **)&name)
|
||||
== FAIL) {
|
||||
error = true;
|
||||
} else {
|
||||
if (arg == arg_subsc && len == 2 && name[1] == ':') {
|
||||
@@ -3142,6 +3149,65 @@ static int pattern_match(char_u *pat, char_u *text, bool ic)
|
||||
return matches;
|
||||
}
|
||||
|
||||
/// Handle a name followed by "(". Both for just "name(arg)" and for
|
||||
/// "expr->name(arg)".
|
||||
//
|
||||
/// @param arg Points to "(", will be advanced
|
||||
/// @param basetv "expr" for "expr->name(arg)"
|
||||
//
|
||||
/// @return OK or FAIL.
|
||||
static int eval_func(char_u **const arg, char_u *const name, const int name_len,
|
||||
typval_T *const rettv, const bool evaluate,
|
||||
typval_T *const basetv)
|
||||
FUNC_ATTR_NONNULL_ARG(1, 2, 4)
|
||||
{
|
||||
char_u *s = name;
|
||||
int len = name_len;
|
||||
|
||||
if (!evaluate) {
|
||||
check_vars((const char *)s, len);
|
||||
}
|
||||
|
||||
// If "s" is the name of a variable of type VAR_FUNC
|
||||
// use its contents.
|
||||
partial_T *partial;
|
||||
s = deref_func_name((const char *)s, &len, &partial, !evaluate);
|
||||
|
||||
// Need to make a copy, in case evaluating the arguments makes
|
||||
// the name invalid.
|
||||
s = xmemdupz(s, len);
|
||||
|
||||
// Invoke the function.
|
||||
funcexe_T funcexe = FUNCEXE_INIT;
|
||||
funcexe.firstline = curwin->w_cursor.lnum;
|
||||
funcexe.lastline = curwin->w_cursor.lnum;
|
||||
funcexe.evaluate = evaluate;
|
||||
funcexe.partial = partial;
|
||||
funcexe.basetv = basetv;
|
||||
int ret = get_func_tv(s, len, rettv, arg, &funcexe);
|
||||
|
||||
xfree(s);
|
||||
|
||||
// If evaluate is false rettv->v_type was not set in
|
||||
// get_func_tv, but it's needed in handle_subscript() to parse
|
||||
// what follows. So set it here.
|
||||
if (rettv->v_type == VAR_UNKNOWN && !evaluate && **arg == '(') {
|
||||
rettv->vval.v_string = (char_u *)tv_empty_string;
|
||||
rettv->v_type = VAR_FUNC;
|
||||
}
|
||||
|
||||
// Stop the expression evaluation when immediately
|
||||
// aborting on error, or when an interrupt occurred or
|
||||
// an exception was thrown but not caught.
|
||||
if (evaluate && aborting()) {
|
||||
if (ret == OK) {
|
||||
tv_clear(rettv);
|
||||
}
|
||||
ret = FAIL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// TODO(ZyX-I): move to eval/expressions
|
||||
|
||||
/*
|
||||
@@ -3161,6 +3227,8 @@ int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate)
|
||||
{
|
||||
int ret;
|
||||
char_u *p;
|
||||
const int did_emsg_before = did_emsg;
|
||||
const int called_emsg_before = called_emsg;
|
||||
|
||||
p = skipwhite(arg);
|
||||
ret = eval1(&p, rettv, evaluate);
|
||||
@@ -3170,8 +3238,10 @@ int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate)
|
||||
}
|
||||
// Report the invalid expression unless the expression evaluation has
|
||||
// been cancelled due to an aborting error, an interrupt, or an
|
||||
// exception.
|
||||
if (!aborting()) {
|
||||
// exception, or we already gave a more specific error.
|
||||
// Also check called_emsg for when using assert_fails().
|
||||
if (!aborting() && did_emsg == did_emsg_before
|
||||
&& called_emsg == called_emsg_before) {
|
||||
emsgf(_(e_invexpr2), arg);
|
||||
}
|
||||
ret = FAIL;
|
||||
@@ -3801,6 +3871,7 @@ static int eval6(char_u **arg, typval_T *rettv, int evaluate, int want_string)
|
||||
// + in front unary plus (ignored)
|
||||
// trailing [] subscript in String or List
|
||||
// trailing .name entry in Dictionary
|
||||
// trailing ->name() method call
|
||||
//
|
||||
// "arg" must point to the first non-white of the expression.
|
||||
// "arg" is advanced to the next non-white after the recognized expression.
|
||||
@@ -3815,10 +3886,10 @@ static int eval7(
|
||||
{
|
||||
varnumber_T n;
|
||||
int len;
|
||||
char_u *s;
|
||||
char_u *start_leader, *end_leader;
|
||||
char_u *s;
|
||||
const char_u *start_leader, *end_leader;
|
||||
int ret = OK;
|
||||
char_u *alias;
|
||||
char_u *alias;
|
||||
|
||||
// Initialise variable so that tv_clear() can't mistake this for a
|
||||
// string and free a string that isn't there.
|
||||
@@ -3968,44 +4039,7 @@ static int eval7(
|
||||
ret = FAIL;
|
||||
} else {
|
||||
if (**arg == '(') { // recursive!
|
||||
partial_T *partial;
|
||||
|
||||
if (!evaluate) {
|
||||
check_vars((const char *)s, len);
|
||||
}
|
||||
|
||||
// If "s" is the name of a variable of type VAR_FUNC
|
||||
// use its contents.
|
||||
s = deref_func_name((const char *)s, &len, &partial, !evaluate);
|
||||
|
||||
// Need to make a copy, in case evaluating the arguments makes
|
||||
// the name invalid.
|
||||
s = xmemdupz(s, len);
|
||||
|
||||
// Invoke the function.
|
||||
ret = get_func_tv(s, len, rettv, arg,
|
||||
curwin->w_cursor.lnum, curwin->w_cursor.lnum,
|
||||
&len, evaluate, partial, NULL);
|
||||
|
||||
xfree(s);
|
||||
|
||||
// If evaluate is false rettv->v_type was not set in
|
||||
// get_func_tv, but it's needed in handle_subscript() to parse
|
||||
// what follows. So set it here.
|
||||
if (rettv->v_type == VAR_UNKNOWN && !evaluate && **arg == '(') {
|
||||
rettv->vval.v_string = (char_u *)tv_empty_string;
|
||||
rettv->v_type = VAR_FUNC;
|
||||
}
|
||||
|
||||
// Stop the expression evaluation when immediately
|
||||
// aborting on error, or when an interrupt occurred or
|
||||
// an exception was thrown but not caught.
|
||||
if (evaluate && aborting()) {
|
||||
if (ret == OK) {
|
||||
tv_clear(rettv);
|
||||
}
|
||||
ret = FAIL;
|
||||
}
|
||||
ret = eval_func(arg, s, len, rettv, evaluate, NULL);
|
||||
} else if (evaluate) {
|
||||
ret = get_var_tv((const char *)s, len, rettv, NULL, true, false);
|
||||
} else {
|
||||
@@ -4019,51 +4053,230 @@ static int eval7(
|
||||
*arg = skipwhite(*arg);
|
||||
|
||||
// Handle following '[', '(' and '.' for expr[expr], expr.name,
|
||||
// expr(expr).
|
||||
// expr(expr), expr->name(expr)
|
||||
if (ret == OK) {
|
||||
ret = handle_subscript((const char **)arg, rettv, evaluate, true);
|
||||
ret = handle_subscript((const char **)arg, rettv, evaluate, true,
|
||||
start_leader, &end_leader);
|
||||
}
|
||||
|
||||
// Apply logical NOT and unary '-', from right to left, ignore '+'.
|
||||
if (ret == OK && evaluate && end_leader > start_leader) {
|
||||
bool error = false;
|
||||
varnumber_T val = 0;
|
||||
float_T f = 0.0;
|
||||
ret = eval7_leader(rettv, start_leader, &end_leader);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (rettv->v_type == VAR_FLOAT) {
|
||||
f = rettv->vval.v_float;
|
||||
} else {
|
||||
val = tv_get_number_chk(rettv, &error);
|
||||
}
|
||||
if (error) {
|
||||
tv_clear(rettv);
|
||||
ret = FAIL;
|
||||
} else {
|
||||
while (end_leader > start_leader) {
|
||||
--end_leader;
|
||||
if (*end_leader == '!') {
|
||||
if (rettv->v_type == VAR_FLOAT) {
|
||||
f = !f;
|
||||
} else {
|
||||
val = !val;
|
||||
}
|
||||
} else if (*end_leader == '-') {
|
||||
if (rettv->v_type == VAR_FLOAT) {
|
||||
f = -f;
|
||||
} else {
|
||||
val = -val;
|
||||
}
|
||||
/// Apply the leading "!" and "-" before an eval7 expression to "rettv".
|
||||
/// Adjusts "end_leaderp" until it is at "start_leader".
|
||||
/// @return OK on success, FAIL on failure.
|
||||
static int eval7_leader(typval_T *const rettv, const char_u *const start_leader,
|
||||
const char_u **const end_leaderp)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
const char_u *end_leader = *end_leaderp;
|
||||
int ret = OK;
|
||||
bool error = false;
|
||||
varnumber_T val = 0;
|
||||
float_T f = 0.0;
|
||||
|
||||
if (rettv->v_type == VAR_FLOAT) {
|
||||
f = rettv->vval.v_float;
|
||||
} else {
|
||||
val = tv_get_number_chk(rettv, &error);
|
||||
}
|
||||
if (error) {
|
||||
tv_clear(rettv);
|
||||
ret = FAIL;
|
||||
} else {
|
||||
while (end_leader > start_leader) {
|
||||
end_leader--;
|
||||
if (*end_leader == '!') {
|
||||
if (rettv->v_type == VAR_FLOAT) {
|
||||
f = !f;
|
||||
} else {
|
||||
val = !val;
|
||||
}
|
||||
} else if (*end_leader == '-') {
|
||||
if (rettv->v_type == VAR_FLOAT) {
|
||||
f = -f;
|
||||
} else {
|
||||
val = -val;
|
||||
}
|
||||
}
|
||||
if (rettv->v_type == VAR_FLOAT) {
|
||||
tv_clear(rettv);
|
||||
rettv->vval.v_float = f;
|
||||
}
|
||||
if (rettv->v_type == VAR_FLOAT) {
|
||||
tv_clear(rettv);
|
||||
rettv->vval.v_float = f;
|
||||
} else {
|
||||
tv_clear(rettv);
|
||||
rettv->v_type = VAR_NUMBER;
|
||||
rettv->vval.v_number = val;
|
||||
}
|
||||
}
|
||||
|
||||
*end_leaderp = end_leader;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Call the function referred to in "rettv".
|
||||
/// @param lua_funcname If `rettv` refers to a v:lua function, this must point
|
||||
/// to the name of the Lua function to call (after the
|
||||
/// "v:lua." prefix).
|
||||
/// @return OK on success, FAIL on failure.
|
||||
static int call_func_rettv(char_u **const arg,
|
||||
typval_T *const rettv,
|
||||
const bool evaluate,
|
||||
dict_T *const selfdict,
|
||||
typval_T *const basetv,
|
||||
const char_u *const lua_funcname)
|
||||
FUNC_ATTR_NONNULL_ARG(1, 2)
|
||||
{
|
||||
partial_T *pt = NULL;
|
||||
typval_T functv;
|
||||
const char_u *funcname;
|
||||
bool is_lua = false;
|
||||
|
||||
// need to copy the funcref so that we can clear rettv
|
||||
if (evaluate) {
|
||||
functv = *rettv;
|
||||
rettv->v_type = VAR_UNKNOWN;
|
||||
|
||||
// Invoke the function. Recursive!
|
||||
if (functv.v_type == VAR_PARTIAL) {
|
||||
pt = functv.vval.v_partial;
|
||||
is_lua = is_luafunc(pt);
|
||||
funcname = is_lua ? lua_funcname : partial_name(pt);
|
||||
} else {
|
||||
funcname = functv.vval.v_string;
|
||||
}
|
||||
} else {
|
||||
funcname = (char_u *)"";
|
||||
}
|
||||
|
||||
funcexe_T funcexe = FUNCEXE_INIT;
|
||||
funcexe.firstline = curwin->w_cursor.lnum;
|
||||
funcexe.lastline = curwin->w_cursor.lnum;
|
||||
funcexe.evaluate = evaluate;
|
||||
funcexe.partial = pt;
|
||||
funcexe.selfdict = selfdict;
|
||||
funcexe.basetv = basetv;
|
||||
const int ret = get_func_tv(funcname, is_lua ? *arg - funcname : -1, rettv,
|
||||
(char_u **)arg, &funcexe);
|
||||
|
||||
// Clear the funcref afterwards, so that deleting it while
|
||||
// evaluating the arguments is possible (see test55).
|
||||
if (evaluate) {
|
||||
tv_clear(&functv);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Evaluate "->method()".
|
||||
/// @param verbose if true, give error messages.
|
||||
/// @note "*arg" points to the '-'.
|
||||
/// @return FAIL or OK. @note "*arg" is advanced to after the ')'.
|
||||
static int eval_lambda(char_u **const arg, typval_T *const rettv,
|
||||
const bool evaluate, const bool verbose)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
// Skip over the ->.
|
||||
*arg += 2;
|
||||
typval_T base = *rettv;
|
||||
rettv->v_type = VAR_UNKNOWN;
|
||||
|
||||
int ret = get_lambda_tv(arg, rettv, evaluate);
|
||||
if (ret == NOTDONE) {
|
||||
return FAIL;
|
||||
} else if (**arg != '(') {
|
||||
if (verbose) {
|
||||
if (*skipwhite(*arg) == '(') {
|
||||
EMSG(_(e_nowhitespace));
|
||||
} else {
|
||||
tv_clear(rettv);
|
||||
rettv->v_type = VAR_NUMBER;
|
||||
rettv->vval.v_number = val;
|
||||
EMSG2(_(e_missingparen), "lambda");
|
||||
}
|
||||
}
|
||||
tv_clear(rettv);
|
||||
ret = FAIL;
|
||||
} else {
|
||||
ret = call_func_rettv(arg, rettv, evaluate, NULL, &base, NULL);
|
||||
}
|
||||
|
||||
// Clear the funcref afterwards, so that deleting it while
|
||||
// evaluating the arguments is possible (see test55).
|
||||
if (evaluate) {
|
||||
tv_clear(&base);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Evaluate "->method()" or "->v:lua.method()".
|
||||
/// @note "*arg" points to the '-'.
|
||||
/// @return FAIL or OK. "*arg" is advanced to after the ')'.
|
||||
static int eval_method(char_u **const arg, typval_T *const rettv,
|
||||
const bool evaluate, const bool verbose)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
// Skip over the ->.
|
||||
*arg += 2;
|
||||
typval_T base = *rettv;
|
||||
rettv->v_type = VAR_UNKNOWN;
|
||||
|
||||
// Locate the method name.
|
||||
int len;
|
||||
char_u *name = *arg;
|
||||
char_u *lua_funcname = NULL;
|
||||
if (STRNCMP(name, "v:lua.", 6) == 0) {
|
||||
lua_funcname = name + 6;
|
||||
*arg = (char_u *)skip_luafunc_name((const char *)lua_funcname);
|
||||
*arg = skipwhite(*arg); // to detect trailing whitespace later
|
||||
len = *arg - lua_funcname;
|
||||
} else {
|
||||
char_u *alias;
|
||||
len = get_name_len((const char **)arg, (char **)&alias, evaluate, true);
|
||||
if (alias != NULL) {
|
||||
name = alias;
|
||||
}
|
||||
}
|
||||
|
||||
int ret;
|
||||
if (len <= 0) {
|
||||
if (verbose) {
|
||||
if (lua_funcname == NULL) {
|
||||
EMSG(_("E260: Missing name after ->"));
|
||||
} else {
|
||||
EMSG2(_(e_invexpr2), name);
|
||||
}
|
||||
}
|
||||
ret = FAIL;
|
||||
} else {
|
||||
if (**arg != '(') {
|
||||
if (verbose) {
|
||||
EMSG2(_(e_missingparen), name);
|
||||
}
|
||||
ret = FAIL;
|
||||
} else if (ascii_iswhite((*arg)[-1])) {
|
||||
if (verbose) {
|
||||
EMSG(_(e_nowhitespace));
|
||||
}
|
||||
ret = FAIL;
|
||||
} else if (lua_funcname != NULL) {
|
||||
if (evaluate) {
|
||||
rettv->v_type = VAR_PARTIAL;
|
||||
rettv->vval.v_partial = vvlua_partial;
|
||||
rettv->vval.v_partial->pt_refcount++;
|
||||
}
|
||||
ret = call_func_rettv(arg, rettv, evaluate, NULL, &base, lua_funcname);
|
||||
} else {
|
||||
ret = eval_func(arg, name, len, rettv, evaluate, &base);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the funcref afterwards, so that deleting it while
|
||||
// evaluating the arguments is possible (see test55).
|
||||
if (evaluate) {
|
||||
tv_clear(&base);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -7255,10 +7468,12 @@ bool callback_call(Callback *const callback, const int argcount_in,
|
||||
abort();
|
||||
}
|
||||
|
||||
int dummy;
|
||||
return call_func(name, -1, rettv, argcount_in, argvars_in,
|
||||
NULL, curwin->w_cursor.lnum, curwin->w_cursor.lnum, &dummy,
|
||||
true, partial, NULL);
|
||||
funcexe_T funcexe = FUNCEXE_INIT;
|
||||
funcexe.firstline = curwin->w_cursor.lnum;
|
||||
funcexe.lastline = curwin->w_cursor.lnum;
|
||||
funcexe.evaluate = true;
|
||||
funcexe.partial = partial;
|
||||
return call_func(name, -1, rettv, argcount_in, argvars_in, &funcexe);
|
||||
}
|
||||
|
||||
static bool set_ref_in_callback(Callback *callback, int copyID,
|
||||
@@ -8393,13 +8608,23 @@ static bool tv_is_luafunc(typval_T *tv)
|
||||
return tv->v_type == VAR_PARTIAL && is_luafunc(tv->vval.v_partial);
|
||||
}
|
||||
|
||||
/// check the function name after "v:lua."
|
||||
int check_luafunc_name(const char *str, bool paren)
|
||||
/// Skips one character past the end of the name of a v:lua function.
|
||||
/// @param p Pointer to the char AFTER the "v:lua." prefix.
|
||||
/// @return Pointer to the char one past the end of the function's name.
|
||||
const char *skip_luafunc_name(const char *p)
|
||||
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
|
||||
{
|
||||
const char *p = str;
|
||||
while (ASCII_ISALNUM(*p) || *p == '_' || *p == '.' || *p == '\'') {
|
||||
p++;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/// check the function name after "v:lua."
|
||||
int check_luafunc_name(const char *const str, const bool paren)
|
||||
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
|
||||
{
|
||||
const char *const p = skip_luafunc_name(str);
|
||||
if (*p != (paren ? '(' : NUL)) {
|
||||
return 0;
|
||||
} else {
|
||||
@@ -8407,24 +8632,26 @@ int check_luafunc_name(const char *str, bool paren)
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle expr[expr], expr[expr:expr] subscript and .name lookup.
|
||||
/// Also handle function call with Funcref variable: func(expr)
|
||||
/// Can all be combined: dict.func(expr)[idx]['func'](expr)
|
||||
/// Handle:
|
||||
/// - expr[expr], expr[expr:expr] subscript
|
||||
/// - ".name" lookup
|
||||
/// - function call with Funcref variable: func(expr)
|
||||
/// - method call: var->method()
|
||||
///
|
||||
/// Can all be combined in any order: dict.func(expr)[idx]['func'](expr)->len()
|
||||
int
|
||||
handle_subscript(
|
||||
const char **const arg,
|
||||
typval_T *rettv,
|
||||
int evaluate, // do more than finding the end
|
||||
int verbose // give error messages
|
||||
int evaluate, // do more than finding the end
|
||||
int verbose, // give error messages
|
||||
const char_u *const start_leader, // start of '!' and '-' prefixes
|
||||
const char_u **const end_leaderp // end of '!' and '-' prefixes
|
||||
)
|
||||
{
|
||||
int ret = OK;
|
||||
dict_T *selfdict = NULL;
|
||||
const char_u *s;
|
||||
int len;
|
||||
typval_T functv;
|
||||
int slen = 0;
|
||||
bool lua = false;
|
||||
dict_T *selfdict = NULL;
|
||||
const char_u *lua_funcname = NULL;
|
||||
|
||||
if (tv_is_luafunc(rettv)) {
|
||||
if (**arg != '.') {
|
||||
@@ -8433,55 +8660,28 @@ handle_subscript(
|
||||
} else {
|
||||
(*arg)++;
|
||||
|
||||
lua = true;
|
||||
s = (char_u *)(*arg);
|
||||
slen = check_luafunc_name(*arg, true);
|
||||
if (slen == 0) {
|
||||
lua_funcname = (char_u *)(*arg);
|
||||
const int len = check_luafunc_name(*arg, true);
|
||||
if (len == 0) {
|
||||
tv_clear(rettv);
|
||||
ret = FAIL;
|
||||
}
|
||||
(*arg) += slen;
|
||||
(*arg) += len;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
while (ret == OK
|
||||
&& (**arg == '['
|
||||
|| (**arg == '.' && rettv->v_type == VAR_DICT)
|
||||
|| (**arg == '(' && (!evaluate || tv_is_func(*rettv))))
|
||||
&& !ascii_iswhite(*(*arg - 1))) {
|
||||
&& (((**arg == '[' || (**arg == '.' && rettv->v_type == VAR_DICT)
|
||||
|| (**arg == '(' && (!evaluate || tv_is_func(*rettv))))
|
||||
&& !ascii_iswhite(*(*arg - 1)))
|
||||
|| (**arg == '-' && (*arg)[1] == '>'))) {
|
||||
if (**arg == '(') {
|
||||
partial_T *pt = NULL;
|
||||
// need to copy the funcref so that we can clear rettv
|
||||
if (evaluate) {
|
||||
functv = *rettv;
|
||||
rettv->v_type = VAR_UNKNOWN;
|
||||
ret = call_func_rettv((char_u **)arg, rettv, evaluate, selfdict, NULL,
|
||||
lua_funcname);
|
||||
|
||||
// Invoke the function. Recursive!
|
||||
if (functv.v_type == VAR_PARTIAL) {
|
||||
pt = functv.vval.v_partial;
|
||||
if (!lua) {
|
||||
s = partial_name(pt);
|
||||
}
|
||||
} else {
|
||||
s = functv.vval.v_string;
|
||||
}
|
||||
} else {
|
||||
s = (char_u *)"";
|
||||
}
|
||||
ret = get_func_tv(s, lua ? slen : -1, rettv, (char_u **)arg,
|
||||
curwin->w_cursor.lnum, curwin->w_cursor.lnum,
|
||||
&len, evaluate, pt, selfdict);
|
||||
|
||||
// Clear the funcref afterwards, so that deleting it while
|
||||
// evaluating the arguments is possible (see test55).
|
||||
if (evaluate) {
|
||||
tv_clear(&functv);
|
||||
}
|
||||
|
||||
/* Stop the expression evaluation when immediately aborting on
|
||||
* error, or when an interrupt occurred or an exception was thrown
|
||||
* but not caught. */
|
||||
// Stop the expression evaluation when immediately aborting on
|
||||
// error, or when an interrupt occurred or an exception was thrown
|
||||
// but not caught.
|
||||
if (aborting()) {
|
||||
if (ret == OK) {
|
||||
tv_clear(rettv);
|
||||
@@ -8490,6 +8690,21 @@ handle_subscript(
|
||||
}
|
||||
tv_dict_unref(selfdict);
|
||||
selfdict = NULL;
|
||||
} else if (**arg == '-') {
|
||||
// Expression "-1.0->method()" applies the leader "-" before
|
||||
// applying ->.
|
||||
if (evaluate && *end_leaderp > start_leader) {
|
||||
ret = eval7_leader(rettv, start_leader, end_leaderp);
|
||||
}
|
||||
if (ret == OK) {
|
||||
if ((*arg)[2] == '{') {
|
||||
// expr->{lambda}()
|
||||
ret = eval_lambda((char_u **)arg, rettv, evaluate, verbose);
|
||||
} else {
|
||||
// expr->name()
|
||||
ret = eval_method((char_u **)arg, rettv, evaluate, verbose);
|
||||
}
|
||||
}
|
||||
} else { // **arg == '[' || **arg == '.'
|
||||
tv_dict_unref(selfdict);
|
||||
if (rettv->v_type == VAR_DICT) {
|
||||
@@ -9274,6 +9489,7 @@ void ex_echo(exarg_T *eap)
|
||||
bool atstart = true;
|
||||
bool need_clear = true;
|
||||
const int did_emsg_before = did_emsg;
|
||||
const int called_emsg_before = called_emsg;
|
||||
|
||||
if (eap->skip)
|
||||
++emsg_skip;
|
||||
@@ -9288,7 +9504,8 @@ void ex_echo(exarg_T *eap)
|
||||
// Report the invalid expression unless the expression evaluation
|
||||
// has been cancelled due to an aborting error, an interrupt, or an
|
||||
// exception.
|
||||
if (!aborting() && did_emsg == did_emsg_before) {
|
||||
if (!aborting() && did_emsg == did_emsg_before
|
||||
&& called_emsg == called_emsg_before) {
|
||||
EMSG2(_(e_invexpr2), p);
|
||||
}
|
||||
need_clr_eos = false;
|
||||
@@ -10409,19 +10626,11 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments,
|
||||
typval_T rettv = { .v_type = VAR_UNKNOWN, .v_lock = VAR_UNLOCKED };
|
||||
tv_list_ref(arguments);
|
||||
|
||||
int dummy;
|
||||
(void)call_func((const char_u *)func,
|
||||
name_len,
|
||||
&rettv,
|
||||
2,
|
||||
argvars,
|
||||
NULL,
|
||||
curwin->w_cursor.lnum,
|
||||
curwin->w_cursor.lnum,
|
||||
&dummy,
|
||||
true,
|
||||
NULL,
|
||||
NULL);
|
||||
funcexe_T funcexe = FUNCEXE_INIT;
|
||||
funcexe.firstline = curwin->w_cursor.lnum;
|
||||
funcexe.lastline = curwin->w_cursor.lnum;
|
||||
funcexe.evaluate = true;
|
||||
(void)call_func((const char_u *)func, name_len, &rettv, 2, argvars, &funcexe);
|
||||
|
||||
tv_list_unref(arguments);
|
||||
// Restore caller scope information
|
||||
@@ -10779,7 +10988,9 @@ bool var_exists(const char *var)
|
||||
n = get_var_tv(name, len, &tv, NULL, false, true) == OK;
|
||||
if (n) {
|
||||
// Handle d.key, l[idx], f(expr).
|
||||
n = handle_subscript(&var, &tv, true, false) == OK;
|
||||
n = handle_subscript(&var, &tv, true, false, (const char_u *)name,
|
||||
(const char_u **)&name)
|
||||
== OK;
|
||||
if (n) {
|
||||
tv_clear(&tv);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
-- args Number of arguments, list with maximum and minimum number of arguments
|
||||
-- or list with a minimum number of arguments only. Defaults to zero
|
||||
-- arguments.
|
||||
-- base For methods: the argument to use as the base argument (1-indexed):
|
||||
-- base->method()
|
||||
-- Defaults to BASE_NONE (function cannot be used as a method).
|
||||
-- func Name of the C function which implements the VimL function. Defaults to
|
||||
-- `f_{funcname}`.
|
||||
|
||||
@@ -12,111 +15,115 @@ local varargs = function(nr)
|
||||
return {nr}
|
||||
end
|
||||
|
||||
-- Usable with the base key: use the last function argument as the method base.
|
||||
-- Value is from funcs.h file. "BASE_" prefix is omitted.
|
||||
local LAST = "BASE_LAST"
|
||||
|
||||
return {
|
||||
funcs={
|
||||
abs={args=1},
|
||||
acos={args=1, func="float_op_wrapper", data="&acos"}, -- WJMc
|
||||
add={args=2},
|
||||
['and']={args=2},
|
||||
abs={args=1, base=1},
|
||||
acos={args=1, base=1, func="float_op_wrapper", data="&acos"}, -- WJMc
|
||||
add={args=2, base=1},
|
||||
['and']={args=2, base=1},
|
||||
api_info={},
|
||||
append={args=2},
|
||||
appendbufline={args=3},
|
||||
append={args=2, base=LAST},
|
||||
appendbufline={args=3, base=LAST},
|
||||
argc={args={0, 1}},
|
||||
argidx={},
|
||||
arglistid={args={0, 2}},
|
||||
argv={args={0, 2}},
|
||||
asin={args=1, func="float_op_wrapper", data="&asin"}, -- WJMc
|
||||
assert_beeps={args={1}},
|
||||
assert_equal={args={2, 3}},
|
||||
assert_equalfile={args={2, 3}},
|
||||
asin={args=1, base=1, func="float_op_wrapper", data="&asin"}, -- WJMc
|
||||
assert_beeps={args={1}, base=1},
|
||||
assert_equal={args={2, 3}, base=2},
|
||||
assert_equalfile={args={2, 3}, base=1},
|
||||
assert_exception={args={1, 2}},
|
||||
assert_fails={args={1, 3}},
|
||||
assert_false={args={1, 2}},
|
||||
assert_inrange={args={3, 4}},
|
||||
assert_match={args={2, 3}},
|
||||
assert_fails={args={1, 3}, base=1},
|
||||
assert_false={args={1, 2}, base=1},
|
||||
assert_inrange={args={3, 4}, base=3},
|
||||
assert_match={args={2, 3}, base=2},
|
||||
assert_nobeep={args={1}},
|
||||
assert_notequal={args={2, 3}},
|
||||
assert_notmatch={args={2, 3}},
|
||||
assert_report={args=1},
|
||||
assert_true={args={1, 2}},
|
||||
atan={args=1, func="float_op_wrapper", data="&atan"},
|
||||
atan2={args=2},
|
||||
assert_notequal={args={2, 3}, base=2},
|
||||
assert_notmatch={args={2, 3}, base=2},
|
||||
assert_report={args=1, base=1},
|
||||
assert_true={args={1, 2}, base=1},
|
||||
atan={args=1, base=1, func="float_op_wrapper", data="&atan"},
|
||||
atan2={args=2, base=1},
|
||||
browse={args=4},
|
||||
browsedir={args=2},
|
||||
bufadd={args=1},
|
||||
bufexists={args=1},
|
||||
buffer_exists={args=1, func='f_bufexists'}, -- obsolete
|
||||
bufadd={args=1, base=1},
|
||||
bufexists={args=1, base=1},
|
||||
buffer_exists={args=1, base=1, func='f_bufexists'}, -- obsolete
|
||||
buffer_name={args={0, 1}, func='f_bufname'}, -- obsolete
|
||||
buffer_number={args={0, 1}, func='f_bufnr'}, -- obsolete
|
||||
buflisted={args=1},
|
||||
bufload={args=1},
|
||||
bufloaded={args=1},
|
||||
bufname={args={0, 1}},
|
||||
bufnr={args={0, 2}},
|
||||
bufwinid={args=1},
|
||||
bufwinnr={args=1},
|
||||
byte2line={args=1},
|
||||
byteidx={args=2},
|
||||
byteidxcomp={args=2},
|
||||
call={args={2, 3}},
|
||||
ceil={args=1, func="float_op_wrapper", data="&ceil"},
|
||||
buflisted={args=1, base=1},
|
||||
bufload={args=1, base=1},
|
||||
bufloaded={args=1, base=1},
|
||||
bufname={args={0, 1}, base=1},
|
||||
bufnr={args={0, 2}, base=1},
|
||||
bufwinid={args=1, base=1},
|
||||
bufwinnr={args=1, base=1},
|
||||
byte2line={args=1, base=1},
|
||||
byteidx={args=2, base=1},
|
||||
byteidxcomp={args=2, base=1},
|
||||
call={args={2, 3}, base=1},
|
||||
ceil={args=1, base=1, func="float_op_wrapper", data="&ceil"},
|
||||
changenr={},
|
||||
chanclose={args={1, 2}},
|
||||
chansend={args=2},
|
||||
char2nr={args={1, 2}},
|
||||
char2nr={args={1, 2}, base=1},
|
||||
charidx={args={2, 3}},
|
||||
cindent={args=1},
|
||||
clearmatches={args={0, 1}},
|
||||
col={args=1},
|
||||
complete={args=2},
|
||||
complete_add={args=1},
|
||||
cindent={args=1, base=1},
|
||||
clearmatches={args={0, 1}, base=1},
|
||||
col={args=1, base=1},
|
||||
complete={args=2, base=2},
|
||||
complete_add={args=1, base=1},
|
||||
complete_check={},
|
||||
complete_info={args={0, 1}},
|
||||
confirm={args={1, 4}},
|
||||
copy={args=1},
|
||||
cos={args=1, func="float_op_wrapper", data="&cos"},
|
||||
cosh={args=1, func="float_op_wrapper", data="&cosh"},
|
||||
count={args={2, 4}},
|
||||
complete_info={args={0, 1}, base=1},
|
||||
confirm={args={1, 4}, base=1},
|
||||
copy={args=1, base=1},
|
||||
cos={args=1, base=1, func="float_op_wrapper", data="&cos"},
|
||||
cosh={args=1, base=1, func="float_op_wrapper", data="&cosh"},
|
||||
count={args={2, 4}, base=1},
|
||||
cscope_connection={args={0, 3}},
|
||||
ctxget={args={0, 1}},
|
||||
ctxpop={},
|
||||
ctxpush={args={0, 1}},
|
||||
ctxset={args={1, 2}},
|
||||
ctxsize={},
|
||||
cursor={args={1, 3}},
|
||||
debugbreak={args={1, 1}},
|
||||
deepcopy={args={1, 2}},
|
||||
delete={args={1,2}},
|
||||
deletebufline={args={2,3}},
|
||||
cursor={args={1, 3}, base=1},
|
||||
debugbreak={args={1, 1}, base=1},
|
||||
deepcopy={args={1, 2}, base=1},
|
||||
delete={args={1,2}, base=1},
|
||||
deletebufline={args={2,3}, base=1},
|
||||
dictwatcheradd={args=3},
|
||||
dictwatcherdel={args=3},
|
||||
did_filetype={},
|
||||
diff_filler={args=1},
|
||||
diff_hlID={args=2},
|
||||
empty={args=1},
|
||||
diff_filler={args=1, base=1},
|
||||
diff_hlID={args=2, base=1},
|
||||
empty={args=1, base=1},
|
||||
environ={},
|
||||
escape={args=2},
|
||||
eval={args=1},
|
||||
eval={args=1, base=1},
|
||||
eventhandler={},
|
||||
executable={args=1},
|
||||
execute={args={1, 2}},
|
||||
exepath={args=1},
|
||||
exists={args=1},
|
||||
exp={args=1, func="float_op_wrapper", data="&exp"},
|
||||
exp={args=1, base=1, func="float_op_wrapper", data="&exp"},
|
||||
expand={args={1, 3}},
|
||||
expandcmd={args=1},
|
||||
extend={args={2, 3}},
|
||||
extend={args={2, 3}, base=1},
|
||||
feedkeys={args={1, 2}},
|
||||
file_readable={args=1, func='f_filereadable'}, -- obsolete
|
||||
filereadable={args=1},
|
||||
filewritable={args=1},
|
||||
filter={args=2},
|
||||
filter={args=2, base=1},
|
||||
finddir={args={1, 3}},
|
||||
findfile={args={1, 3}},
|
||||
flatten={args={1, 2}},
|
||||
float2nr={args=1},
|
||||
floor={args=1, func="float_op_wrapper", data="&floor"},
|
||||
fmod={args=2},
|
||||
float2nr={args=1, base=1},
|
||||
floor={args=1, base=1, func="float_op_wrapper", data="&floor"},
|
||||
fmod={args=2, base=1},
|
||||
fnameescape={args=1},
|
||||
fnamemodify={args=2},
|
||||
foldclosed={args=1},
|
||||
@@ -128,7 +135,7 @@ return {
|
||||
funcref={args={1, 3}},
|
||||
['function']={args={1, 3}},
|
||||
garbagecollect={args={0, 1}},
|
||||
get={args={2, 3}},
|
||||
get={args={2, 3}, base=1},
|
||||
getbufinfo={args={0, 1}},
|
||||
getbufline={args={2, 3}},
|
||||
getbufvar={args={2, 3}},
|
||||
@@ -173,7 +180,7 @@ return {
|
||||
glob2regpat={args=1},
|
||||
globpath={args={2, 5}},
|
||||
has={args=1},
|
||||
has_key={args=2},
|
||||
has_key={args=2, base=1},
|
||||
haslocaldir={args={0,2}},
|
||||
hasmapto={args={1, 3}},
|
||||
highlightID={args=1, func='f_hlID'}, -- obsolete
|
||||
@@ -187,22 +194,22 @@ return {
|
||||
hostname={},
|
||||
iconv={args=3},
|
||||
indent={args=1},
|
||||
index={args={2, 4}},
|
||||
index={args={2, 4}, base=1},
|
||||
input={args={1, 3}},
|
||||
inputdialog={args={1, 3}},
|
||||
inputlist={args=1},
|
||||
inputrestore={},
|
||||
inputsave={},
|
||||
inputsecret={args={1, 2}},
|
||||
insert={args={2, 3}},
|
||||
insert={args={2, 3}, base=1},
|
||||
interrupt={args=0},
|
||||
invert={args=1},
|
||||
invert={args=1, base=1},
|
||||
isdirectory={args=1},
|
||||
isinf={args=1},
|
||||
isinf={args=1, base=1},
|
||||
islocked={args=1},
|
||||
isnan={args=1},
|
||||
isnan={args=1, base=1},
|
||||
id={args=1},
|
||||
items={args=1},
|
||||
items={args=1, base=1},
|
||||
jobclose={args={1, 2}, func="f_chanclose"},
|
||||
jobpid={args=1},
|
||||
jobresize={args=3},
|
||||
@@ -210,12 +217,12 @@ return {
|
||||
jobstart={args={1, 2}},
|
||||
jobstop={args=1},
|
||||
jobwait={args={1, 2}},
|
||||
join={args={1, 2}},
|
||||
join={args={1, 2}, base=1},
|
||||
json_decode={args=1},
|
||||
json_encode={args=1},
|
||||
keys={args=1},
|
||||
keys={args=1, base=1},
|
||||
last_buffer_nr={}, -- obsolete
|
||||
len={args=1},
|
||||
len={args=1, base=1},
|
||||
libcall={args=3},
|
||||
libcallnr={args=3},
|
||||
line={args={1, 2}},
|
||||
@@ -223,10 +230,10 @@ return {
|
||||
lispindent={args=1},
|
||||
list2str={args={1, 2}},
|
||||
localtime={},
|
||||
log={args=1, func="float_op_wrapper", data="&log"},
|
||||
log10={args=1, func="float_op_wrapper", data="&log10"},
|
||||
log={args=1, base=1, func="float_op_wrapper", data="&log"},
|
||||
log10={args=1, base=1, func="float_op_wrapper", data="&log10"},
|
||||
luaeval={args={1, 2}},
|
||||
map={args=2},
|
||||
map={args=2, base=1},
|
||||
maparg={args={1, 4}},
|
||||
mapcheck={args={1, 3}},
|
||||
match={args={2, 4}},
|
||||
@@ -238,20 +245,20 @@ return {
|
||||
matchlist={args={2, 4}},
|
||||
matchstr={args={2, 4}},
|
||||
matchstrpos={args={2,4}},
|
||||
max={args=1},
|
||||
max={args=1, base=1},
|
||||
menu_get={args={1, 2}},
|
||||
min={args=1},
|
||||
min={args=1, base=1},
|
||||
mkdir={args={1, 3}},
|
||||
mode={args={0, 1}},
|
||||
msgpackdump={args=1},
|
||||
msgpackparse={args=1},
|
||||
nextnonblank={args=1},
|
||||
nr2char={args={1, 2}},
|
||||
['or']={args=2},
|
||||
['or']={args=2, base=1},
|
||||
pathshorten={args=1},
|
||||
pow={args=2},
|
||||
pow={args=2, base=1},
|
||||
prevnonblank={args=1},
|
||||
printf={args=varargs(1)},
|
||||
printf={args=varargs(1), base=2},
|
||||
prompt_getprompt={args=1},
|
||||
prompt_setcallback={args={2, 2}},
|
||||
prompt_setinterrupt={args={2, 2}},
|
||||
@@ -270,12 +277,12 @@ return {
|
||||
reltime={args={0, 2}},
|
||||
reltimefloat={args=1},
|
||||
reltimestr={args=1},
|
||||
remove={args={2, 3}},
|
||||
remove={args={2, 3}, base=1},
|
||||
rename={args=2},
|
||||
['repeat']={args=2},
|
||||
['repeat']={args=2, base=1},
|
||||
resolve={args=1},
|
||||
reverse={args=1},
|
||||
round={args=1, func="float_op_wrapper", data="&round"},
|
||||
reverse={args=1, base=1},
|
||||
round={args=1, base=1, func="float_op_wrapper", data="&round"},
|
||||
rpcnotify={args=varargs(2)},
|
||||
rpcrequest={args=varargs(2)},
|
||||
rpcstart={args={1, 2}},
|
||||
@@ -324,19 +331,19 @@ return {
|
||||
sign_unplace={args={1, 2}},
|
||||
sign_unplacelist={args={1}},
|
||||
simplify={args=1},
|
||||
sin={args=1, func="float_op_wrapper", data="&sin"},
|
||||
sinh={args=1, func="float_op_wrapper", data="&sinh"},
|
||||
sin={args=1, base=1, func="float_op_wrapper", data="&sin"},
|
||||
sinh={args=1, base=1, func="float_op_wrapper", data="&sinh"},
|
||||
sockconnect={args={2,3}},
|
||||
sort={args={1, 3}},
|
||||
sort={args={1, 3}, base=1},
|
||||
soundfold={args=1},
|
||||
stdioopen={args=1},
|
||||
spellbadword={args={0, 1}},
|
||||
spellsuggest={args={1, 3}},
|
||||
split={args={1, 3}},
|
||||
sqrt={args=1, func="float_op_wrapper", data="&sqrt"},
|
||||
split={args={1, 3}, base=1},
|
||||
sqrt={args=1, base=1, func="float_op_wrapper", data="&sqrt"},
|
||||
stdpath={args=1},
|
||||
str2float={args=1},
|
||||
str2list={args={1, 2}},
|
||||
str2float={args=1, base=1},
|
||||
str2list={args={1, 2}, base=1},
|
||||
str2nr={args={1, 2}},
|
||||
strcharpart={args={2, 3}},
|
||||
strchars={args={1,2}},
|
||||
@@ -344,31 +351,31 @@ return {
|
||||
strftime={args={1, 2}},
|
||||
strgetchar={args={2, 2}},
|
||||
stridx={args={2, 3}},
|
||||
string={args=1},
|
||||
strlen={args=1},
|
||||
string={args=1, base=1},
|
||||
strlen={args=1, base=1},
|
||||
strpart={args={2, 4}},
|
||||
strptime={args=2},
|
||||
strridx={args={2, 3}},
|
||||
strtrans={args=1},
|
||||
strwidth={args=1},
|
||||
strtrans={args=1, base=1},
|
||||
strwidth={args=1, base=1},
|
||||
submatch={args={1, 2}},
|
||||
substitute={args=4},
|
||||
substitute={args=4, base=1},
|
||||
swapinfo={args={1}},
|
||||
swapname={args={1}},
|
||||
synID={args=3},
|
||||
synIDattr={args={2, 3}},
|
||||
synIDtrans={args=1},
|
||||
synIDattr={args={2, 3}, base=1},
|
||||
synIDtrans={args=1, base=1},
|
||||
synconcealed={args=2},
|
||||
synstack={args=2},
|
||||
system={args={1, 2}},
|
||||
systemlist={args={1, 3}},
|
||||
system={args={1, 2}, base=1},
|
||||
systemlist={args={1, 3}, base=1},
|
||||
tabpagebuflist={args={0, 1}},
|
||||
tabpagenr={args={0, 1}},
|
||||
tabpagewinnr={args={1, 2}},
|
||||
tagfiles={},
|
||||
taglist={args={1, 2}},
|
||||
tan={args=1, func="float_op_wrapper", data="&tan"},
|
||||
tanh={args=1, func="float_op_wrapper", data="&tanh"},
|
||||
tan={args=1, base=1, func="float_op_wrapper", data="&tan"},
|
||||
tanh={args=1, base=1, func="float_op_wrapper", data="&tanh"},
|
||||
tempname={},
|
||||
termopen={args={1, 2}},
|
||||
test_garbagecollect_now={},
|
||||
@@ -382,12 +389,12 @@ return {
|
||||
toupper={args=1},
|
||||
tr={args=3},
|
||||
trim={args={1,3}},
|
||||
trunc={args=1, func="float_op_wrapper", data="&trunc"},
|
||||
type={args=1},
|
||||
trunc={args=1, base=1, func="float_op_wrapper", data="&trunc"},
|
||||
type={args=1, base=1},
|
||||
undofile={args=1},
|
||||
undotree={},
|
||||
uniq={args={1, 3}},
|
||||
values={args=1},
|
||||
uniq={args={1, 3}, base=1},
|
||||
values={args=1, base=1},
|
||||
virtcol={args=1},
|
||||
visualmode={args={0, 1}},
|
||||
wait={args={2,3}},
|
||||
@@ -401,7 +408,7 @@ return {
|
||||
win_id2win={args=1},
|
||||
win_screenpos={args=1},
|
||||
win_splitmove={args={2, 3}},
|
||||
winbufnr={args=1},
|
||||
winbufnr={args=1, base=1},
|
||||
wincol={},
|
||||
windowsversion={},
|
||||
winheight={args=1},
|
||||
@@ -414,6 +421,6 @@ return {
|
||||
winwidth={args=1},
|
||||
wordcount={},
|
||||
writefile={args={2, 3}},
|
||||
xor={args=2},
|
||||
xor={args=2, base=1},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -175,6 +175,53 @@ const VimLFuncDef *find_internal_func(const char *const name)
|
||||
return find_internal_func_gperf(name, len);
|
||||
}
|
||||
|
||||
int call_internal_func(const char_u *const fname, const int argcount,
|
||||
typval_T *const argvars, typval_T *const rettv)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
const VimLFuncDef *const fdef = find_internal_func((const char *)fname);
|
||||
if (fdef == NULL) {
|
||||
return ERROR_UNKNOWN;
|
||||
} else if (argcount < fdef->min_argc) {
|
||||
return ERROR_TOOFEW;
|
||||
} else if (argcount > fdef->max_argc) {
|
||||
return ERROR_TOOMANY;
|
||||
}
|
||||
argvars[argcount].v_type = VAR_UNKNOWN;
|
||||
fdef->func(argvars, rettv, fdef->data);
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
/// Invoke a method for base->method().
|
||||
int call_internal_method(const char_u *const fname, const int argcount,
|
||||
typval_T *const argvars, typval_T *const rettv,
|
||||
typval_T *const basetv)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
const VimLFuncDef *const fdef = find_internal_func((const char *)fname);
|
||||
if (fdef == NULL) {
|
||||
return ERROR_UNKNOWN;
|
||||
} else if (fdef->base_arg == BASE_NONE) {
|
||||
return ERROR_NOTMETHOD;
|
||||
} else if (argcount + 1 < fdef->min_argc) {
|
||||
return ERROR_TOOFEW;
|
||||
} else if (argcount + 1 > fdef->max_argc) {
|
||||
return ERROR_TOOMANY;
|
||||
}
|
||||
|
||||
typval_T argv[MAX_FUNC_ARGS + 1];
|
||||
const ptrdiff_t base_index
|
||||
= fdef->base_arg == BASE_LAST ? argcount : fdef->base_arg - 1;
|
||||
memcpy(argv, argvars, base_index * sizeof(typval_T));
|
||||
argv[base_index] = *basetv;
|
||||
memcpy(argv + base_index + 1, argvars + base_index,
|
||||
(argcount - base_index) * sizeof(typval_T));
|
||||
argv[argcount + 1].v_type = VAR_UNKNOWN;
|
||||
|
||||
fdef->func(argv, rettv, fdef->data);
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return TRUE for a non-zero Number and a non-empty String.
|
||||
*/
|
||||
@@ -9420,7 +9467,6 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero)
|
||||
int res;
|
||||
typval_T rettv;
|
||||
typval_T argv[3];
|
||||
int dummy;
|
||||
const char *func_name;
|
||||
partial_T *partial = sortinfo->item_compare_partial;
|
||||
|
||||
@@ -9444,10 +9490,11 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero)
|
||||
tv_copy(TV_LIST_ITEM_TV(si2->item), &argv[1]);
|
||||
|
||||
rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this
|
||||
res = call_func((const char_u *)func_name,
|
||||
-1,
|
||||
&rettv, 2, argv, NULL, 0L, 0L, &dummy, true,
|
||||
partial, sortinfo->item_compare_selfdict);
|
||||
funcexe_T funcexe = FUNCEXE_INIT;
|
||||
funcexe.evaluate = true;
|
||||
funcexe.partial = partial;
|
||||
funcexe.selfdict = sortinfo->item_compare_selfdict;
|
||||
res = call_func((const char_u *)func_name, -1, &rettv, 2, argv, &funcexe);
|
||||
tv_clear(&argv[0]);
|
||||
tv_clear(&argv[1]);
|
||||
|
||||
|
||||
@@ -9,11 +9,16 @@ typedef void (*FunPtr)(void);
|
||||
/// Prototype of C function that implements VimL function
|
||||
typedef void (*VimLFunc)(typval_T *args, typval_T *rvar, FunPtr data);
|
||||
|
||||
/// Special flags for base_arg @see VimLFuncDef
|
||||
#define BASE_NONE 0 ///< Not a method (no base argument).
|
||||
#define BASE_LAST UINT8_MAX ///< Use the last argument as the method base.
|
||||
|
||||
/// Structure holding VimL function definition
|
||||
typedef struct fst {
|
||||
char *name; ///< Name of the function.
|
||||
uint8_t min_argc; ///< Minimal number of arguments.
|
||||
uint8_t max_argc; ///< Maximal number of arguments.
|
||||
uint8_t base_arg; ///< Method base arg # (1-indexed), BASE_NONE or BASE_LAST.
|
||||
VimLFunc func; ///< Function implementation.
|
||||
FunPtr data; ///< Userdata for function implementation.
|
||||
} VimLFuncDef;
|
||||
|
||||
@@ -414,12 +414,7 @@ get_func_tv(
|
||||
int len, // length of "name" or -1 to use strlen()
|
||||
typval_T *rettv,
|
||||
char_u **arg, // argument, pointing to the '('
|
||||
linenr_T firstline, // first line of range
|
||||
linenr_T lastline, // last line of range
|
||||
int *doesrange, // return: function handled range
|
||||
int evaluate,
|
||||
partial_T *partial, // for extra arguments
|
||||
dict_T *selfdict // Dictionary for "self"
|
||||
funcexe_T *funcexe // various values
|
||||
)
|
||||
{
|
||||
char_u *argp;
|
||||
@@ -431,12 +426,13 @@ get_func_tv(
|
||||
* Get the arguments.
|
||||
*/
|
||||
argp = *arg;
|
||||
while (argcount < MAX_FUNC_ARGS - (partial == NULL ? 0 : partial->pt_argc)) {
|
||||
while (argcount < MAX_FUNC_ARGS
|
||||
- (funcexe->partial == NULL ? 0 : funcexe->partial->pt_argc)) {
|
||||
argp = skipwhite(argp + 1); // skip the '(' or ','
|
||||
if (*argp == ')' || *argp == ',' || *argp == NUL) {
|
||||
break;
|
||||
}
|
||||
if (eval1(&argp, &argvars[argcount], evaluate) == FAIL) {
|
||||
if (eval1(&argp, &argvars[argcount], funcexe->evaluate) == FAIL) {
|
||||
ret = FAIL;
|
||||
break;
|
||||
}
|
||||
@@ -463,9 +459,7 @@ get_func_tv(
|
||||
((typval_T **)funcargs.ga_data)[funcargs.ga_len++] = &argvars[i];
|
||||
}
|
||||
}
|
||||
ret = call_func(name, len, rettv, argcount, argvars, NULL,
|
||||
firstline, lastline, doesrange, evaluate,
|
||||
partial, selfdict);
|
||||
ret = call_func(name, len, rettv, argcount, argvars, funcexe);
|
||||
|
||||
funcargs.ga_len -= i;
|
||||
} else if (!aborting()) {
|
||||
@@ -1367,7 +1361,6 @@ int func_call(char_u *name, typval_T *args, partial_T *partial,
|
||||
{
|
||||
typval_T argv[MAX_FUNC_ARGS + 1];
|
||||
int argc = 0;
|
||||
int dummy;
|
||||
int r = 0;
|
||||
|
||||
TV_LIST_ITER(args->vval.v_list, item, {
|
||||
@@ -1380,9 +1373,13 @@ int func_call(char_u *name, typval_T *args, partial_T *partial,
|
||||
tv_copy(TV_LIST_ITEM_TV(item), &argv[argc++]);
|
||||
});
|
||||
|
||||
r = call_func(name, -1, rettv, argc, argv, NULL,
|
||||
curwin->w_cursor.lnum, curwin->w_cursor.lnum,
|
||||
&dummy, true, partial, selfdict);
|
||||
funcexe_T funcexe = FUNCEXE_INIT;
|
||||
funcexe.firstline = curwin->w_cursor.lnum;
|
||||
funcexe.lastline = curwin->w_cursor.lnum;
|
||||
funcexe.evaluate = true;
|
||||
funcexe.partial = partial;
|
||||
funcexe.selfdict = selfdict;
|
||||
r = call_func(name, -1, rettv, argc, argv, &funcexe);
|
||||
|
||||
func_call_skip_call:
|
||||
// Free the arguments.
|
||||
@@ -1402,6 +1399,9 @@ static void user_func_error(int error, const char_u *name)
|
||||
case ERROR_UNKNOWN:
|
||||
emsg_funcname(N_("E117: Unknown function: %s"), name);
|
||||
break;
|
||||
case ERROR_NOTMETHOD:
|
||||
emsg_funcname(N_("E276: Cannot use function as a method: %s"), name);
|
||||
break;
|
||||
case ERROR_DELETED:
|
||||
emsg_funcname(N_("E933: Function was deleted: %s"), name);
|
||||
break;
|
||||
@@ -1423,12 +1423,25 @@ static void user_func_error(int error, const char_u *name)
|
||||
}
|
||||
}
|
||||
|
||||
/// Used by call_func to add a method base (if any) to a function argument list
|
||||
/// as the first argument. @see call_func
|
||||
static void argv_add_base(typval_T *const basetv, typval_T **const argvars,
|
||||
int *const argcount, typval_T *const new_argvars,
|
||||
int *const argv_base)
|
||||
FUNC_ATTR_NONNULL_ARG(2, 3, 4, 5)
|
||||
{
|
||||
if (basetv != NULL) {
|
||||
// Method call: base->Method()
|
||||
memmove(&new_argvars[1], *argvars, sizeof(typval_T) * (*argcount));
|
||||
new_argvars[0] = *basetv;
|
||||
(*argcount)++;
|
||||
*argvars = new_argvars;
|
||||
*argv_base = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// Call a function with its resolved parameters
|
||||
///
|
||||
/// "argv_func", when not NULL, can be used to fill in arguments only when the
|
||||
/// invoked function uses them. It is called like this:
|
||||
/// new_argcount = argv_func(current_argcount, argv, called_func_argcount)
|
||||
///
|
||||
/// @return FAIL if function cannot be called, else OK (even if an error
|
||||
/// occurred while executing the function! Set `msg_list` to capture
|
||||
/// the error, see do_cmdline()).
|
||||
@@ -1440,15 +1453,9 @@ call_func(
|
||||
int argcount_in, // number of "argvars"
|
||||
typval_T *argvars_in, // vars for arguments, must have "argcount"
|
||||
// PLUS ONE elements!
|
||||
ArgvFunc argv_func, // function to fill in argvars
|
||||
linenr_T firstline, // first line of range
|
||||
linenr_T lastline, // last line of range
|
||||
int *doesrange, // [out] function handled range
|
||||
bool evaluate,
|
||||
partial_T *partial, // optional, can be NULL
|
||||
dict_T *selfdict_in // Dictionary for "self"
|
||||
funcexe_T *funcexe // more arguments
|
||||
)
|
||||
FUNC_ATTR_NONNULL_ARG(1, 3, 5, 9)
|
||||
FUNC_ATTR_NONNULL_ARG(1, 3, 5, 6)
|
||||
{
|
||||
int ret = FAIL;
|
||||
int error = ERROR_NONE;
|
||||
@@ -1459,9 +1466,12 @@ call_func(
|
||||
char_u *name = NULL;
|
||||
int argcount = argcount_in;
|
||||
typval_T *argvars = argvars_in;
|
||||
dict_T *selfdict = selfdict_in;
|
||||
typval_T argv[MAX_FUNC_ARGS + 1]; // used when "partial" is not NULL
|
||||
dict_T *selfdict = funcexe->selfdict;
|
||||
typval_T argv[MAX_FUNC_ARGS + 1]; // used when "partial" or
|
||||
// "funcexe->basetv" is not NULL
|
||||
int argv_clear = 0;
|
||||
int argv_base = 0;
|
||||
partial_T *partial = funcexe->partial;
|
||||
|
||||
// Initialize rettv so that it is safe for caller to invoke clear_tv(rettv)
|
||||
// even when call_func() returns FAIL.
|
||||
@@ -1480,14 +1490,15 @@ call_func(
|
||||
fname = fname_trans_sid(name, fname_buf, &tofree, &error);
|
||||
}
|
||||
|
||||
*doesrange = false;
|
||||
if (funcexe->doesrange != NULL) {
|
||||
*funcexe->doesrange = false;
|
||||
}
|
||||
|
||||
if (partial != NULL) {
|
||||
// When the function has a partial with a dict and there is a dict
|
||||
// argument, use the dict argument. That is backwards compatible.
|
||||
// When the dict was bound explicitly use the one from the partial.
|
||||
if (partial->pt_dict != NULL
|
||||
&& (selfdict_in == NULL || !partial->pt_auto)) {
|
||||
if (partial->pt_dict != NULL && (selfdict == NULL || !partial->pt_auto)) {
|
||||
selfdict = partial->pt_dict;
|
||||
}
|
||||
if (error == ERROR_NONE && partial->pt_argc > 0) {
|
||||
@@ -1506,7 +1517,7 @@ call_func(
|
||||
}
|
||||
}
|
||||
|
||||
if (error == ERROR_NONE && evaluate) {
|
||||
if (error == ERROR_NONE && funcexe->evaluate) {
|
||||
char_u *rfname = fname;
|
||||
|
||||
// Ignore "g:" before a function name.
|
||||
@@ -1521,7 +1532,12 @@ call_func(
|
||||
if (is_luafunc(partial)) {
|
||||
if (len > 0) {
|
||||
error = ERROR_NONE;
|
||||
argv_add_base(funcexe->basetv, &argvars, &argcount, argv, &argv_base);
|
||||
nlua_typval_call((const char *)funcname, len, argvars, argcount, rettv);
|
||||
} else {
|
||||
// v:lua was called directly; show its name in the emsg
|
||||
XFREE_CLEAR(name);
|
||||
funcname = (const char_u *)"v:lua";
|
||||
}
|
||||
} else if (fp != NULL || !builtin_function((const char *)rfname, -1)) {
|
||||
// User defined function.
|
||||
@@ -1549,13 +1565,16 @@ call_func(
|
||||
cfunc_T cb = fp->uf_cb;
|
||||
error = (*cb)(argcount, argvars, rettv, fp->uf_cb_state);
|
||||
} else if (fp != NULL) {
|
||||
if (argv_func != NULL) {
|
||||
if (funcexe->argv_func != NULL) {
|
||||
// postponed filling in the arguments, do it now
|
||||
argcount = argv_func(argcount, argvars, argv_clear,
|
||||
fp->uf_args.ga_len);
|
||||
argcount = funcexe->argv_func(argcount, argvars, argv_clear,
|
||||
fp->uf_args.ga_len);
|
||||
}
|
||||
if (fp->uf_flags & FC_RANGE) {
|
||||
*doesrange = true;
|
||||
|
||||
argv_add_base(funcexe->basetv, &argvars, &argcount, argv, &argv_base);
|
||||
|
||||
if (fp->uf_flags & FC_RANGE && funcexe->doesrange != NULL) {
|
||||
*funcexe->doesrange = true;
|
||||
}
|
||||
if (argcount < fp->uf_args.ga_len - fp->uf_def_args.ga_len) {
|
||||
error = ERROR_TOOFEW;
|
||||
@@ -1565,25 +1584,20 @@ call_func(
|
||||
error = ERROR_DICT;
|
||||
} else {
|
||||
// Call the user function.
|
||||
call_user_func(fp, argcount, argvars, rettv, firstline, lastline,
|
||||
call_user_func(fp, argcount, argvars, rettv, funcexe->firstline,
|
||||
funcexe->lastline,
|
||||
(fp->uf_flags & FC_DICT) ? selfdict : NULL);
|
||||
error = ERROR_NONE;
|
||||
}
|
||||
}
|
||||
} else if (funcexe->basetv != NULL) {
|
||||
// expr->method(): Find the method name in the table, call its
|
||||
// implementation with the base as one of the arguments.
|
||||
error = call_internal_method(fname, argcount, argvars, rettv,
|
||||
funcexe->basetv);
|
||||
} else {
|
||||
// Find the function name in the table, call its implementation.
|
||||
const VimLFuncDef *const fdef = find_internal_func((const char *)fname);
|
||||
if (fdef != NULL) {
|
||||
if (argcount < fdef->min_argc) {
|
||||
error = ERROR_TOOFEW;
|
||||
} else if (argcount > fdef->max_argc) {
|
||||
error = ERROR_TOOMANY;
|
||||
} else {
|
||||
argvars[argcount].v_type = VAR_UNKNOWN;
|
||||
fdef->func(argvars, rettv, fdef->data);
|
||||
error = ERROR_NONE;
|
||||
}
|
||||
}
|
||||
error = call_internal_func(fname, argcount, argvars, rettv);
|
||||
}
|
||||
/*
|
||||
* The function call (or "FuncUndefined" autocommand sequence) might
|
||||
@@ -1607,9 +1621,11 @@ theend:
|
||||
user_func_error(error, (name != NULL) ? name : funcname);
|
||||
}
|
||||
|
||||
// clear the copies made from the partial
|
||||
while (argv_clear > 0) {
|
||||
tv_clear(&argv[--argv_clear]);
|
||||
tv_clear(&argv[--argv_clear + argv_base]);
|
||||
}
|
||||
|
||||
xfree(tofree);
|
||||
xfree(name);
|
||||
|
||||
@@ -2901,7 +2917,7 @@ void ex_call(exarg_T *eap)
|
||||
int len;
|
||||
typval_T rettv;
|
||||
linenr_T lnum;
|
||||
int doesrange;
|
||||
bool doesrange;
|
||||
bool failed = false;
|
||||
funcdict_T fudi;
|
||||
partial_T *partial = NULL;
|
||||
@@ -2947,7 +2963,7 @@ void ex_call(exarg_T *eap)
|
||||
rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this.
|
||||
|
||||
if (*startarg != '(') {
|
||||
EMSG2(_("E107: Missing parentheses: %s"), eap->arg);
|
||||
EMSG2(_(e_missingparen), eap->arg);
|
||||
goto end;
|
||||
}
|
||||
|
||||
@@ -2965,15 +2981,22 @@ void ex_call(exarg_T *eap)
|
||||
curwin->w_cursor.coladd = 0;
|
||||
}
|
||||
arg = startarg;
|
||||
if (get_func_tv(name, -1, &rettv, &arg,
|
||||
eap->line1, eap->line2, &doesrange,
|
||||
true, partial, fudi.fd_dict) == FAIL) {
|
||||
|
||||
funcexe_T funcexe = FUNCEXE_INIT;
|
||||
funcexe.firstline = eap->line1;
|
||||
funcexe.lastline = eap->line2;
|
||||
funcexe.doesrange = &doesrange;
|
||||
funcexe.evaluate = true;
|
||||
funcexe.partial = partial;
|
||||
funcexe.selfdict = fudi.fd_dict;
|
||||
if (get_func_tv(name, -1, &rettv, &arg, &funcexe) == FAIL) {
|
||||
failed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Handle a function returning a Funcref, Dictionary or List.
|
||||
if (handle_subscript((const char **)&arg, &rettv, true, true)
|
||||
if (handle_subscript((const char **)&arg, &rettv, true, true,
|
||||
(const char_u *)name, (const char_u **)&name)
|
||||
== FAIL) {
|
||||
failed = true;
|
||||
break;
|
||||
|
||||
@@ -28,11 +28,37 @@ typedef enum {
|
||||
ERROR_OTHER,
|
||||
ERROR_BOTH,
|
||||
ERROR_DELETED,
|
||||
ERROR_NOTMETHOD,
|
||||
} FnameTransError;
|
||||
|
||||
/// Used in funcexe_T. Returns the new argcount.
|
||||
typedef int (*ArgvFunc)(int current_argcount, typval_T *argv, int argskip,
|
||||
int called_func_argcount);
|
||||
|
||||
/// Structure passed between functions dealing with function call execution.
|
||||
typedef struct {
|
||||
ArgvFunc argv_func; ///< when not NULL, can be used to fill in arguments only
|
||||
///< when the invoked function uses them
|
||||
linenr_T firstline; ///< first line of range
|
||||
linenr_T lastline; ///< last line of range
|
||||
bool *doesrange; ///< [out] if not NULL: function handled range
|
||||
bool evaluate; ///< actually evaluate expressions
|
||||
partial_T *partial; ///< for extra arguments
|
||||
dict_T *selfdict; ///< Dictionary for "self"
|
||||
typval_T *basetv; ///< base for base->method()
|
||||
} funcexe_T;
|
||||
|
||||
#define FUNCEXE_INIT (funcexe_T) { \
|
||||
.argv_func = NULL, \
|
||||
.firstline = 0, \
|
||||
.lastline = 0, \
|
||||
.doesrange = NULL, \
|
||||
.evaluate = false, \
|
||||
.partial = NULL, \
|
||||
.selfdict = NULL, \
|
||||
.basetv = NULL, \
|
||||
}
|
||||
|
||||
#define FUNCARG(fp, j) ((char_u **)(fp->uf_args.ga_data))[j]
|
||||
#define FUNCLINE(fp, j) ((char_u **)(fp->uf_lines.ga_data))[j]
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ gperfpipe:write([[
|
||||
%language=ANSI-C
|
||||
%global-table
|
||||
%readonly-tables
|
||||
%define initializer-suffix ,0,0,NULL,NULL
|
||||
%define initializer-suffix ,0,0,BASE_NONE,NULL,NULL
|
||||
%define word-array-name functions
|
||||
%define hash-function-name hash_internal_func_gperf
|
||||
%define lookup-function-name find_internal_func_gperf
|
||||
@@ -59,9 +59,10 @@ for name, def in pairs(funcs) do
|
||||
elseif #args == 1 then
|
||||
args[2] = 'MAX_FUNC_ARGS'
|
||||
end
|
||||
local base = def.base or "BASE_NONE"
|
||||
local func = def.func or ('f_' .. name)
|
||||
local data = def.data or "NULL"
|
||||
gperfpipe:write(('%s, %s, %s, &%s, (FunPtr)%s\n')
|
||||
:format(name, args[1], args[2], func, data))
|
||||
gperfpipe:write(('%s, %s, %s, %s, &%s, (FunPtr)%s\n')
|
||||
:format(name, args[1], args[2], base, func, data))
|
||||
end
|
||||
gperfpipe:close()
|
||||
|
||||
@@ -972,6 +972,7 @@ EXTERN char_u e_write[] INIT(= N_("E80: Error while writing"));
|
||||
EXTERN char_u e_zerocount[] INIT(= N_("E939: Positive count required"));
|
||||
EXTERN char_u e_usingsid[] INIT(= N_(
|
||||
"E81: Using <SID> not in a script context"));
|
||||
EXTERN char_u e_missingparen[] INIT(= N_("E107: Missing parentheses: %s"));
|
||||
EXTERN char_u e_maxmempat[] INIT(= N_(
|
||||
"E363: pattern uses more memory than 'maxmempattern'"));
|
||||
EXTERN char_u e_emptybuf[] INIT(= N_("E749: empty buffer"));
|
||||
|
||||
@@ -785,13 +785,13 @@ int nlua_call(lua_State *lstate)
|
||||
|
||||
try_start();
|
||||
typval_T rettv;
|
||||
int dummy;
|
||||
funcexe_T funcexe = FUNCEXE_INIT;
|
||||
funcexe.firstline = curwin->w_cursor.lnum;
|
||||
funcexe.lastline = curwin->w_cursor.lnum;
|
||||
funcexe.evaluate = true;
|
||||
// call_func() retval is deceptive, ignore it. Instead we set `msg_list`
|
||||
// (TRY_WRAP) to capture abort-causing non-exception errors.
|
||||
(void)call_func(name, (int)name_len, &rettv, nargs,
|
||||
vim_args, NULL,
|
||||
curwin->w_cursor.lnum, curwin->w_cursor.lnum,
|
||||
&dummy, true, NULL, NULL);
|
||||
(void)call_func(name, (int)name_len, &rettv, nargs, vim_args, &funcexe);
|
||||
if (!try_end(&err)) {
|
||||
nlua_push_typval(lstate, &rettv, false);
|
||||
}
|
||||
|
||||
@@ -2531,12 +2531,12 @@ do_mouse (
|
||||
}
|
||||
};
|
||||
typval_T rettv;
|
||||
int doesrange;
|
||||
(void)call_func((char_u *)tab_page_click_defs[mouse_col].func,
|
||||
-1,
|
||||
&rettv, ARRAY_SIZE(argv), argv, NULL,
|
||||
curwin->w_cursor.lnum, curwin->w_cursor.lnum,
|
||||
&doesrange, true, NULL, NULL);
|
||||
funcexe_T funcexe = FUNCEXE_INIT;
|
||||
funcexe.firstline = curwin->w_cursor.lnum;
|
||||
funcexe.lastline = curwin->w_cursor.lnum;
|
||||
funcexe.evaluate = true;
|
||||
(void)call_func((char_u *)tab_page_click_defs[mouse_col].func, -1,
|
||||
&rettv, ARRAY_SIZE(argv), argv, &funcexe);
|
||||
tv_clear(&rettv);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -6726,26 +6726,24 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest,
|
||||
|
||||
if (expr != NULL) {
|
||||
typval_T argv[2];
|
||||
int dummy;
|
||||
typval_T rettv;
|
||||
staticList10_T matchList = TV_LIST_STATIC10_INIT;
|
||||
|
||||
rettv.v_type = VAR_STRING;
|
||||
rettv.vval.v_string = NULL;
|
||||
argv[0].v_type = VAR_LIST;
|
||||
argv[0].vval.v_list = &matchList.sl_list;
|
||||
funcexe_T funcexe = FUNCEXE_INIT;
|
||||
funcexe.argv_func = fill_submatch_list;
|
||||
funcexe.evaluate = true;
|
||||
if (expr->v_type == VAR_FUNC) {
|
||||
s = expr->vval.v_string;
|
||||
call_func(s, -1, &rettv, 1, argv,
|
||||
fill_submatch_list, 0L, 0L, &dummy,
|
||||
true, NULL, NULL);
|
||||
call_func(s, -1, &rettv, 1, argv, &funcexe);
|
||||
} else if (expr->v_type == VAR_PARTIAL) {
|
||||
partial_T *partial = expr->vval.v_partial;
|
||||
|
||||
s = partial_name(partial);
|
||||
call_func(s, -1, &rettv, 1, argv,
|
||||
fill_submatch_list, 0L, 0L, &dummy,
|
||||
true, partial, NULL);
|
||||
funcexe.partial = partial;
|
||||
call_func(s, -1, &rettv, 1, argv, &funcexe);
|
||||
}
|
||||
if (tv_list_len(&matchList.sl_list) > 0) {
|
||||
// fill_submatch_list() was called.
|
||||
|
||||
@@ -5,3 +5,7 @@ let foo#bar = {}
|
||||
func foo#bar.echo()
|
||||
let g:called_foo_bar_echo += 1
|
||||
endfunc
|
||||
|
||||
func foo#addFoo(head)
|
||||
return a:head .. 'foo'
|
||||
endfunc
|
||||
|
||||
@@ -90,8 +90,8 @@ func Test_argadd_empty_curbuf()
|
||||
call assert_equal('', bufname('%'))
|
||||
call assert_equal(1, line('$'))
|
||||
rew
|
||||
call assert_notequal(curbuf, bufnr('%'))
|
||||
call assert_equal('Xargadd', bufname('%'))
|
||||
call assert_notequal(curbuf, '%'->bufnr())
|
||||
call assert_equal('Xargadd', '%'->bufname())
|
||||
call assert_equal(2, line('$'))
|
||||
|
||||
%argd
|
||||
|
||||
@@ -7,7 +7,7 @@ func Test_assert_equalfile()
|
||||
|
||||
let goodtext = ["one", "two", "three"]
|
||||
call writefile(goodtext, 'Xone')
|
||||
call assert_equal(1, assert_equalfile('Xone', 'xyzxyz'))
|
||||
call assert_equal(1, 'Xone'->assert_equalfile('xyzxyz'))
|
||||
call assert_match("E485: Can't read file xyzxyz", v:errors[0])
|
||||
call remove(v:errors, 0)
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ func Test_autoload_dict_func()
|
||||
call g:foo#bar.echo()
|
||||
call assert_equal(1, g:loaded_foo_vim)
|
||||
call assert_equal(1, g:called_foo_bar_echo)
|
||||
|
||||
eval 'bar'->g:foo#addFoo()->assert_equal('barfoo')
|
||||
endfunc
|
||||
|
||||
func Test_source_autoload()
|
||||
|
||||
@@ -102,7 +102,7 @@ func Test_deletebufline()
|
||||
call assert_equal(0, deletebufline(b, 2, 8))
|
||||
call assert_equal(['aaa'], getbufline(b, 1, 2))
|
||||
exe "bd!" b
|
||||
call assert_equal(1, deletebufline(b, 1))
|
||||
call assert_equal(1, b->deletebufline(1))
|
||||
|
||||
split Xtest
|
||||
call setline(1, ['a', 'b', 'c'])
|
||||
@@ -131,11 +131,11 @@ func Test_appendbufline_redraw()
|
||||
endif
|
||||
let lines =<< trim END
|
||||
new foo
|
||||
let winnr=bufwinnr('foo')
|
||||
let buf=bufnr('foo')
|
||||
let winnr = 'foo'->bufwinnr()
|
||||
let buf = bufnr('foo')
|
||||
wincmd p
|
||||
call appendbufline(buf, '$', range(1,200))
|
||||
exe winnr. 'wincmd w'
|
||||
exe winnr .. 'wincmd w'
|
||||
norm! G
|
||||
wincmd p
|
||||
call deletebufline(buf, 1, '$')
|
||||
|
||||
@@ -18,7 +18,7 @@ function Test_getbufwintabinfo()
|
||||
let l = getbufinfo('%')
|
||||
call assert_equal(bufnr('%'), l[0].bufnr)
|
||||
call assert_equal('vim', l[0].variables.editor)
|
||||
call assert_notequal(-1, index(l[0].windows, bufwinid('%')))
|
||||
call assert_notequal(-1, index(l[0].windows, '%'->bufwinid()))
|
||||
|
||||
" Test for getbufinfo() with 'bufmodified'
|
||||
call assert_equal(0, len(getbufinfo({'bufmodified' : 1})))
|
||||
|
||||
@@ -118,6 +118,16 @@ b = something();
|
||||
bw!
|
||||
endfunc
|
||||
|
||||
func Test_cindent_func()
|
||||
new
|
||||
setlocal cindent
|
||||
call setline(1, ['int main(void)', '{', 'return 0;', '}'])
|
||||
call assert_equal(-1, cindent(0))
|
||||
call assert_equal(&sw, 3->cindent())
|
||||
call assert_equal(-1, cindent(line('$')+1))
|
||||
bwipe!
|
||||
endfunc
|
||||
|
||||
" this was going beyond the end of the line.
|
||||
func Test_cindent_case()
|
||||
new
|
||||
|
||||
@@ -725,7 +725,7 @@ func Test_diff_filler()
|
||||
diffthis
|
||||
redraw
|
||||
|
||||
call assert_equal([0, 0, 0, 0, 0, 0, 0, 1, 0], map(range(-1, 7), 'diff_filler(v:val)'))
|
||||
call assert_equal([0, 0, 0, 0, 0, 0, 0, 1, 0], map(range(-1, 7), 'v:val->diff_filler()'))
|
||||
wincmd w
|
||||
call assert_equal([0, 0, 0, 0, 2, 0, 0, 0], map(range(-1, 6), 'diff_filler(v:val)'))
|
||||
|
||||
@@ -741,16 +741,16 @@ func Test_diff_hlID()
|
||||
diffthis
|
||||
redraw
|
||||
|
||||
call assert_equal(synIDattr(diff_hlID(-1, 1), "name"), "")
|
||||
call diff_hlID(-1, 1)->synIDattr("name")->assert_equal("")
|
||||
|
||||
call assert_equal(diff_hlID(1, 1), hlID("DiffChange"))
|
||||
call assert_equal(synIDattr(diff_hlID(1, 1), "name"), "DiffChange")
|
||||
call diff_hlID(1, 1)->synIDattr("name")->assert_equal("DiffChange")
|
||||
call assert_equal(diff_hlID(1, 2), hlID("DiffText"))
|
||||
call assert_equal(synIDattr(diff_hlID(1, 2), "name"), "DiffText")
|
||||
call assert_equal(synIDattr(diff_hlID(2, 1), "name"), "")
|
||||
call diff_hlID(1, 2)->synIDattr("name")->assert_equal("DiffText")
|
||||
call diff_hlID(2, 1)->synIDattr("name")->assert_equal("")
|
||||
call assert_equal(diff_hlID(3, 1), hlID("DiffAdd"))
|
||||
call assert_equal(synIDattr(diff_hlID(3, 1), "name"), "DiffAdd")
|
||||
call assert_equal(synIDattr(diff_hlID(4, 1), "name"), "")
|
||||
call diff_hlID(3, 1)->synIDattr("name")->assert_equal("DiffAdd")
|
||||
call diff_hlID(4, 1)->synIDattr("name")->assert_equal("")
|
||||
|
||||
wincmd w
|
||||
call assert_equal(diff_hlID(1, 1), hlID("DiffChange"))
|
||||
|
||||
@@ -7,6 +7,8 @@ end
|
||||
func Test_abs()
|
||||
call assert_equal('1.23', string(abs(1.23)))
|
||||
call assert_equal('1.23', string(abs(-1.23)))
|
||||
eval -1.23->abs()->string()->assert_equal('1.23')
|
||||
|
||||
call assert_equal('0.0', string(abs(0.0)))
|
||||
call assert_equal('0.0', string(abs(1.0/(1.0/0.0))))
|
||||
call assert_equal('0.0', string(abs(-1.0/(1.0/0.0))))
|
||||
@@ -22,6 +24,7 @@ endfunc
|
||||
func Test_sqrt()
|
||||
call assert_equal('0.0', string(sqrt(0.0)))
|
||||
call assert_equal('1.414214', string(sqrt(2.0)))
|
||||
eval 2.0->sqrt()->string()->assert_equal('1.414214')
|
||||
call assert_equal("str2float('inf')", string(sqrt(1.0/0.0)))
|
||||
call assert_equal("str2float('nan')", string(sqrt(-1.0)))
|
||||
call assert_equal("str2float('nan')", string(sqrt(0.0/0.0)))
|
||||
@@ -31,6 +34,7 @@ endfunc
|
||||
func Test_log()
|
||||
call assert_equal('0.0', string(log(1.0)))
|
||||
call assert_equal('-0.693147', string(log(0.5)))
|
||||
eval 0.5->log()->string()->assert_equal('-0.693147')
|
||||
call assert_equal("-str2float('inf')", string(log(0.0)))
|
||||
call assert_equal("str2float('nan')", string(log(-1.0)))
|
||||
call assert_equal("str2float('inf')", string(log(1.0/0.0)))
|
||||
@@ -42,6 +46,7 @@ func Test_log10()
|
||||
call assert_equal('0.0', string(log10(1.0)))
|
||||
call assert_equal('2.0', string(log10(100.0)))
|
||||
call assert_equal('2.079181', string(log10(120.0)))
|
||||
eval 120.0->log10()->string()->assert_equal('2.079181')
|
||||
call assert_equal("-str2float('inf')", string(log10(0.0)))
|
||||
call assert_equal("str2float('nan')", string(log10(-1.0)))
|
||||
call assert_equal("str2float('inf')", string(log10(1.0/0.0)))
|
||||
@@ -53,6 +58,7 @@ func Test_exp()
|
||||
call assert_equal('1.0', string(exp(0.0)))
|
||||
call assert_equal('7.389056', string(exp(2.0)))
|
||||
call assert_equal('0.367879', string(exp(-1.0)))
|
||||
eval -1.0->exp()->string()->assert_equal('0.367879')
|
||||
call assert_equal("str2float('inf')", string(exp(1.0/0.0)))
|
||||
call assert_equal('0.0', string(exp(-1.0/0.0)))
|
||||
call assert_equal("str2float('nan')", string(exp(0.0/0.0)))
|
||||
@@ -63,6 +69,7 @@ func Test_sin()
|
||||
call assert_equal('0.0', string(sin(0.0)))
|
||||
call assert_equal('0.841471', string(sin(1.0)))
|
||||
call assert_equal('-0.479426', string(sin(-0.5)))
|
||||
eval -0.5->sin()->string()->assert_equal('-0.479426')
|
||||
call assert_equal("str2float('nan')", string(sin(0.0/0.0)))
|
||||
call assert_equal("str2float('nan')", string(sin(1.0/0.0)))
|
||||
call assert_equal('0.0', string(sin(1.0/(1.0/0.0))))
|
||||
@@ -73,6 +80,8 @@ endfunc
|
||||
func Test_asin()
|
||||
call assert_equal('0.0', string(asin(0.0)))
|
||||
call assert_equal('1.570796', string(asin(1.0)))
|
||||
eval 1.0->asin()->string()->assert_equal('1.570796')
|
||||
|
||||
call assert_equal('-0.523599', string(asin(-0.5)))
|
||||
call assert_equal("str2float('nan')", string(asin(1.1)))
|
||||
call assert_equal("str2float('nan')", string(asin(1.0/0.0)))
|
||||
@@ -84,6 +93,7 @@ func Test_sinh()
|
||||
call assert_equal('0.0', string(sinh(0.0)))
|
||||
call assert_equal('0.521095', string(sinh(0.5)))
|
||||
call assert_equal('-1.026517', string(sinh(-0.9)))
|
||||
eval -0.9->sinh()->string()->assert_equal('-1.026517')
|
||||
call assert_equal("str2float('inf')", string(sinh(1.0/0.0)))
|
||||
call assert_equal("-str2float('inf')", string(sinh(-1.0/0.0)))
|
||||
call assert_equal("str2float('nan')", string(sinh(0.0/0.0)))
|
||||
@@ -94,6 +104,7 @@ func Test_cos()
|
||||
call assert_equal('1.0', string(cos(0.0)))
|
||||
call assert_equal('0.540302', string(cos(1.0)))
|
||||
call assert_equal('0.877583', string(cos(-0.5)))
|
||||
eval -0.5->cos()->string()->assert_equal('0.877583')
|
||||
call assert_equal("str2float('nan')", string(cos(0.0/0.0)))
|
||||
call assert_equal("str2float('nan')", string(cos(1.0/0.0)))
|
||||
call assert_fails('call cos("")', 'E808:')
|
||||
@@ -103,6 +114,7 @@ func Test_acos()
|
||||
call assert_equal('1.570796', string(acos(0.0)))
|
||||
call assert_equal('0.0', string(acos(1.0)))
|
||||
call assert_equal('3.141593', string(acos(-1.0)))
|
||||
eval -1.0->acos()->string()->assert_equal('3.141593')
|
||||
call assert_equal('2.094395', string(acos(-0.5)))
|
||||
call assert_equal("str2float('nan')", string(acos(1.1)))
|
||||
call assert_equal("str2float('nan')", string(acos(1.0/0.0)))
|
||||
@@ -113,6 +125,7 @@ endfunc
|
||||
func Test_cosh()
|
||||
call assert_equal('1.0', string(cosh(0.0)))
|
||||
call assert_equal('1.127626', string(cosh(0.5)))
|
||||
eval 0.5->cosh()->string()->assert_equal('1.127626')
|
||||
call assert_equal("str2float('inf')", string(cosh(1.0/0.0)))
|
||||
call assert_equal("str2float('inf')", string(cosh(-1.0/0.0)))
|
||||
call assert_equal("str2float('nan')", string(cosh(0.0/0.0)))
|
||||
@@ -123,6 +136,7 @@ func Test_tan()
|
||||
call assert_equal('0.0', string(tan(0.0)))
|
||||
call assert_equal('0.546302', string(tan(0.5)))
|
||||
call assert_equal('-0.546302', string(tan(-0.5)))
|
||||
eval -0.5->tan()->string()->assert_equal('-0.546302')
|
||||
call assert_equal("str2float('nan')", string(tan(1.0/0.0)))
|
||||
call assert_equal("str2float('nan')", string(cos(0.0/0.0)))
|
||||
call assert_equal('0.0', string(tan(1.0/(1.0/0.0))))
|
||||
@@ -134,6 +148,7 @@ func Test_atan()
|
||||
call assert_equal('0.0', string(atan(0.0)))
|
||||
call assert_equal('0.463648', string(atan(0.5)))
|
||||
call assert_equal('-0.785398', string(atan(-1.0)))
|
||||
eval -1.0->atan()->string()->assert_equal('-0.785398')
|
||||
call assert_equal('1.570796', string(atan(1.0/0.0)))
|
||||
call assert_equal('-1.570796', string(atan(-1.0/0.0)))
|
||||
call assert_equal("str2float('nan')", string(atan(0.0/0.0)))
|
||||
@@ -144,6 +159,7 @@ func Test_atan2()
|
||||
call assert_equal('-2.356194', string(atan2(-1, -1)))
|
||||
call assert_equal('2.356194', string(atan2(1, -1)))
|
||||
call assert_equal('0.0', string(atan2(1.0, 1.0/0.0)))
|
||||
eval 1.0->atan2(1.0/0.0)->string()->assert_equal('0.0')
|
||||
call assert_equal('1.570796', string(atan2(1.0/0.0, 1.0)))
|
||||
call assert_equal("str2float('nan')", string(atan2(0.0/0.0, 1.0)))
|
||||
call assert_fails('call atan2("", -1)', 'E808:')
|
||||
@@ -154,6 +170,7 @@ func Test_tanh()
|
||||
call assert_equal('0.0', string(tanh(0.0)))
|
||||
call assert_equal('0.462117', string(tanh(0.5)))
|
||||
call assert_equal('-0.761594', string(tanh(-1.0)))
|
||||
eval -1.0->tanh()->string()->assert_equal('-0.761594')
|
||||
call assert_equal('1.0', string(tanh(1.0/0.0)))
|
||||
call assert_equal('-1.0', string(tanh(-1.0/0.0)))
|
||||
call assert_equal("str2float('nan')", string(tanh(0.0/0.0)))
|
||||
@@ -164,6 +181,7 @@ func Test_fmod()
|
||||
call assert_equal('0.13', string(fmod(12.33, 1.22)))
|
||||
call assert_equal('-0.13', string(fmod(-12.33, 1.22)))
|
||||
call assert_equal("str2float('nan')", string(fmod(1.0/0.0, 1.0)))
|
||||
eval (1.0/0.0)->fmod(1.0)->string()->assert_equal("str2float('nan')")
|
||||
" On Windows we get "nan" instead of 1.0, accept both.
|
||||
let res = string(fmod(1.0, 1.0/0.0))
|
||||
if res != "str2float('nan')"
|
||||
@@ -177,6 +195,7 @@ endfunc
|
||||
func Test_pow()
|
||||
call assert_equal('1.0', string(pow(0.0, 0.0)))
|
||||
call assert_equal('8.0', string(pow(2.0, 3.0)))
|
||||
eval 2.0->pow(3.0)->string()->assert_equal('8.0')
|
||||
call assert_equal("str2float('nan')", string(pow(2.0, 0.0/0.0)))
|
||||
call assert_equal("str2float('nan')", string(pow(0.0/0.0, 3.0)))
|
||||
call assert_equal("str2float('nan')", string(pow(0.0/0.0, 3.0)))
|
||||
@@ -192,6 +211,7 @@ func Test_str2float()
|
||||
call assert_equal('1.0', string(str2float(' 1.0 ')))
|
||||
call assert_equal('1.23', string(str2float('1.23')))
|
||||
call assert_equal('1.23', string(str2float('1.23abc')))
|
||||
eval '1.23abc'->str2float()->string()->assert_equal('1.23')
|
||||
call assert_equal('1.0e40', string(str2float('1e40')))
|
||||
call assert_equal('-1.23', string(str2float('-1.23')))
|
||||
call assert_equal('1.23', string(str2float(' + 1.23 ')))
|
||||
@@ -228,6 +248,7 @@ func Test_float2nr()
|
||||
call assert_equal(1, float2nr(1.234))
|
||||
call assert_equal(123, float2nr(1.234e2))
|
||||
call assert_equal(12, float2nr(123.4e-1))
|
||||
eval 123.4e-1->float2nr()->assert_equal(12)
|
||||
let max_number = 1/0
|
||||
let min_number = -max_number
|
||||
call assert_equal(max_number/2+1, float2nr(pow(2, 62)))
|
||||
@@ -242,6 +263,7 @@ func Test_floor()
|
||||
call assert_equal('2.0', string(floor(2.0)))
|
||||
call assert_equal('2.0', string(floor(2.11)))
|
||||
call assert_equal('2.0', string(floor(2.99)))
|
||||
eval 2.99->floor()->string()->assert_equal('2.0')
|
||||
call assert_equal('-3.0', string(floor(-2.11)))
|
||||
call assert_equal('-3.0', string(floor(-2.99)))
|
||||
call assert_equal("str2float('nan')", string(floor(0.0/0.0)))
|
||||
@@ -255,6 +277,7 @@ func Test_ceil()
|
||||
call assert_equal('3.0', string(ceil(2.11)))
|
||||
call assert_equal('3.0', string(ceil(2.99)))
|
||||
call assert_equal('-2.0', string(ceil(-2.11)))
|
||||
eval -2.11->ceil()->string()->assert_equal('-2.0')
|
||||
call assert_equal('-2.0', string(ceil(-2.99)))
|
||||
call assert_equal("str2float('nan')", string(ceil(0.0/0.0)))
|
||||
call assert_equal("str2float('inf')", string(ceil(1.0/0.0)))
|
||||
@@ -266,6 +289,7 @@ func Test_round()
|
||||
call assert_equal('2.0', string(round(2.1)))
|
||||
call assert_equal('3.0', string(round(2.5)))
|
||||
call assert_equal('3.0', string(round(2.9)))
|
||||
eval 2.9->round()->string()->assert_equal('3.0')
|
||||
call assert_equal('-2.0', string(round(-2.1)))
|
||||
call assert_equal('-3.0', string(round(-2.5)))
|
||||
call assert_equal('-3.0', string(round(-2.9)))
|
||||
@@ -279,6 +303,7 @@ func Test_trunc()
|
||||
call assert_equal('2.0', string(trunc(2.1)))
|
||||
call assert_equal('2.0', string(trunc(2.5)))
|
||||
call assert_equal('2.0', string(trunc(2.9)))
|
||||
eval 2.9->trunc()->string()->assert_equal('2.0')
|
||||
call assert_equal('-2.0', string(trunc(-2.1)))
|
||||
call assert_equal('-2.0', string(trunc(-2.5)))
|
||||
call assert_equal('-2.0', string(trunc(-2.9)))
|
||||
@@ -291,6 +316,7 @@ endfunc
|
||||
func Test_isinf()
|
||||
call assert_equal(1, isinf(1.0/0.0))
|
||||
call assert_equal(-1, isinf(-1.0/0.0))
|
||||
eval (-1.0/0.0)->isinf()->assert_equal(-1)
|
||||
call assert_false(isinf(1.0))
|
||||
call assert_false(isinf(0.0/0.0))
|
||||
call assert_false(isinf('a'))
|
||||
@@ -302,6 +328,7 @@ func Test_isnan()
|
||||
call assert_true(isnan(0.0/0.0))
|
||||
call assert_false(isnan(1.0))
|
||||
call assert_false(isnan(1.0/0.0))
|
||||
eval (1.0/0.0)->isnan()->assert_false()
|
||||
call assert_false(isnan(-1.0/0.0))
|
||||
call assert_false(isnan('a'))
|
||||
call assert_false(isnan([]))
|
||||
|
||||
@@ -852,7 +852,7 @@ func Test_byte2line_line2byte()
|
||||
|
||||
set fileformat=mac
|
||||
call assert_equal([-1, -1, 1, 1, 2, 2, 2, 3, 3, -1],
|
||||
\ map(range(-1, 8), 'byte2line(v:val)'))
|
||||
\ map(range(-1, 8), 'v:val->byte2line()'))
|
||||
call assert_equal([-1, -1, 1, 3, 6, 8, -1],
|
||||
\ map(range(-1, 5), 'line2byte(v:val)'))
|
||||
|
||||
@@ -875,6 +875,34 @@ func Test_byte2line_line2byte()
|
||||
bw!
|
||||
endfunc
|
||||
|
||||
func Test_byteidx()
|
||||
let a = '.é.' " one char of two bytes
|
||||
call assert_equal(0, byteidx(a, 0))
|
||||
call assert_equal(0, byteidxcomp(a, 0))
|
||||
call assert_equal(1, byteidx(a, 1))
|
||||
call assert_equal(1, byteidxcomp(a, 1))
|
||||
call assert_equal(3, byteidx(a, 2))
|
||||
call assert_equal(3, byteidxcomp(a, 2))
|
||||
call assert_equal(4, byteidx(a, 3))
|
||||
call assert_equal(4, byteidxcomp(a, 3))
|
||||
call assert_equal(-1, byteidx(a, 4))
|
||||
call assert_equal(-1, byteidxcomp(a, 4))
|
||||
|
||||
let b = '.é.' " normal e with composing char
|
||||
call assert_equal(0, b->byteidx(0))
|
||||
call assert_equal(1, b->byteidx(1))
|
||||
call assert_equal(4, b->byteidx(2))
|
||||
call assert_equal(5, b->byteidx(3))
|
||||
call assert_equal(-1, b->byteidx(4))
|
||||
|
||||
call assert_equal(0, b->byteidxcomp(0))
|
||||
call assert_equal(1, b->byteidxcomp(1))
|
||||
call assert_equal(2, b->byteidxcomp(2))
|
||||
call assert_equal(4, b->byteidxcomp(3))
|
||||
call assert_equal(5, b->byteidxcomp(4))
|
||||
call assert_equal(-1, b->byteidxcomp(5))
|
||||
endfunc
|
||||
|
||||
" Test for charidx()
|
||||
func Test_charidx()
|
||||
let a = 'xáb́y'
|
||||
@@ -1065,7 +1093,7 @@ func Test_col()
|
||||
call assert_equal(7, col('$'))
|
||||
call assert_equal(4, col("'x"))
|
||||
call assert_equal(6, col("'Y"))
|
||||
call assert_equal(2, col([1, 2]))
|
||||
call assert_equal(2, [1, 2]->col())
|
||||
call assert_equal(7, col([1, '$']))
|
||||
|
||||
call assert_equal(0, col(''))
|
||||
@@ -1413,13 +1441,13 @@ func Test_bufadd_bufload()
|
||||
call assert_equal([''], getbufline(buf, 1, '$'))
|
||||
|
||||
let curbuf = bufnr('')
|
||||
call writefile(['some', 'text'], 'otherName')
|
||||
let buf = bufadd('otherName')
|
||||
call writefile(['some', 'text'], 'XotherName')
|
||||
let buf = 'XotherName'->bufadd()
|
||||
call assert_notequal(0, buf)
|
||||
call assert_equal(1, bufexists('otherName'))
|
||||
eval 'XotherName'->bufexists()->assert_equal(1)
|
||||
call assert_equal(0, getbufvar(buf, '&buflisted'))
|
||||
call assert_equal(0, bufloaded(buf))
|
||||
call bufload(buf)
|
||||
eval buf->bufload()
|
||||
call assert_equal(1, bufloaded(buf))
|
||||
call assert_equal(['some', 'text'], getbufline(buf, 1, '$'))
|
||||
call assert_equal(curbuf, bufnr(''))
|
||||
@@ -1439,8 +1467,9 @@ func Test_bufadd_bufload()
|
||||
call assert_equal(0, bufexists(buf2))
|
||||
|
||||
bwipe someName
|
||||
bwipe otherName
|
||||
bwipe XotherName
|
||||
call assert_equal(0, bufexists('someName'))
|
||||
call delete('XotherName')
|
||||
endfunc
|
||||
|
||||
func Test_readdir()
|
||||
@@ -1473,6 +1502,20 @@ func Test_readdir()
|
||||
call delete('Xdir', 'rf')
|
||||
endfunc
|
||||
|
||||
func Test_call()
|
||||
call assert_equal(3, call('len', [123]))
|
||||
call assert_equal(3, 'len'->call([123]))
|
||||
call assert_fails("call call('len', 123)", 'E714:')
|
||||
call assert_equal(0, call('', []))
|
||||
|
||||
function Mylen() dict
|
||||
return len(self.data)
|
||||
endfunction
|
||||
let mydict = {'data': [0, 1, 2, 3], 'len': function("Mylen")}
|
||||
eval mydict.len->call([], mydict)->assert_equal(4)
|
||||
call assert_fails("call call('Mylen', [], 0)", 'E715:')
|
||||
endfunc
|
||||
|
||||
" Test for the eval() function
|
||||
func Test_eval()
|
||||
call assert_fails("call eval('5 a')", 'E488:')
|
||||
|
||||
@@ -37,7 +37,7 @@ function Test_hide()
|
||||
" :hide as a command
|
||||
hide
|
||||
call assert_equal([orig_bname, orig_winnr], [bufname(''), winnr('$')])
|
||||
call assert_equal([1, 1], [buflisted('Xf1'), bufloaded('Xf1')])
|
||||
call assert_equal([1, 1], ['Xf1'->buflisted(), 'Xf1'->bufloaded()])
|
||||
bwipeout! Xf1
|
||||
|
||||
new Xf1
|
||||
|
||||
@@ -61,7 +61,7 @@ endfunction
|
||||
|
||||
function Test_lambda_fails()
|
||||
call assert_equal(3, {a, b -> a + b}(1, 2))
|
||||
call assert_fails('echo {a, a -> a + a}(1, 2)', 'E15:')
|
||||
call assert_fails('echo {a, a -> a + a}(1, 2)', 'E853:')
|
||||
call assert_fails('echo {a, b -> a + b)}(1, 2)', 'E15:')
|
||||
endfunc
|
||||
|
||||
|
||||
@@ -242,7 +242,7 @@ func Test_matchaddpos_otherwin()
|
||||
\]
|
||||
call assert_equal(expect, savematches)
|
||||
|
||||
call clearmatches(winid)
|
||||
eval winid->clearmatches()
|
||||
call assert_equal([], getmatches(winid))
|
||||
|
||||
call setmatches(savematches, winid)
|
||||
|
||||
159
src/nvim/testdir/test_method.vim
Normal file
159
src/nvim/testdir/test_method.vim
Normal file
@@ -0,0 +1,159 @@
|
||||
" Tests for ->method()
|
||||
|
||||
func Test_list_method()
|
||||
let l = [1, 2, 3]
|
||||
call assert_equal([1, 2, 3, 4], [1, 2, 3]->add(4))
|
||||
eval l->assert_equal(l)
|
||||
eval l->assert_equal(l, 'wrong')
|
||||
eval l->assert_notequal([3, 2, 1])
|
||||
eval l->assert_notequal([3, 2, 1], 'wrong')
|
||||
call assert_equal(l, l->copy())
|
||||
call assert_equal(l, l->deepcopy())
|
||||
call assert_equal(1, l->count(2))
|
||||
call assert_false(l->empty())
|
||||
call assert_true([]->empty())
|
||||
call assert_equal(579, ['123', '+', '456']->join()->eval())
|
||||
call assert_equal([1, 2, 3, 4, 5], [1, 2, 3]->extend([4, 5]))
|
||||
call assert_equal([1, 3], [1, 2, 3]->filter('v:val != 2'))
|
||||
call assert_equal(2, l->get(1))
|
||||
call assert_equal(1, l->index(2))
|
||||
call assert_equal([0, 1, 2, 3], [1, 2, 3]->insert(0))
|
||||
call assert_fails('eval l->items()', 'E715:')
|
||||
call assert_equal('1 2 3', l->join())
|
||||
call assert_fails('eval l->keys()', 'E715:')
|
||||
call assert_equal(3, l->len())
|
||||
call assert_equal([2, 3, 4], [1, 2, 3]->map('v:val + 1'))
|
||||
call assert_equal(3, l->max())
|
||||
call assert_equal(1, l->min())
|
||||
call assert_equal(2, [1, 2, 3]->remove(1))
|
||||
call assert_equal([1, 2, 3, 1, 2, 3], l->repeat(2))
|
||||
call assert_equal([3, 2, 1], [1, 2, 3]->reverse())
|
||||
call assert_equal([1, 2, 3, 4], [4, 2, 3, 1]->sort())
|
||||
call assert_equal('[1, 2, 3]', l->string())
|
||||
call assert_equal(v:t_list, l->type())
|
||||
call assert_equal([1, 2, 3], [1, 1, 2, 3, 3]->uniq())
|
||||
call assert_fails('eval l->values()', 'E715:')
|
||||
endfunc
|
||||
|
||||
func Test_dict_method()
|
||||
let d = #{one: 1, two: 2, three: 3}
|
||||
|
||||
call assert_equal(d, d->copy())
|
||||
call assert_equal(d, d->deepcopy())
|
||||
call assert_equal(1, d->count(2))
|
||||
call assert_false(d->empty())
|
||||
call assert_true({}->empty())
|
||||
call assert_equal(#{one: 1, two: 2, three: 3, four: 4}, d->extend(#{four: 4}))
|
||||
call assert_equal(#{one: 1, two: 2, three: 3}, d->filter('v:val != 4'))
|
||||
call assert_equal(2, d->get('two'))
|
||||
" Nvim doesn't support Blobs yet; expect a different emsg
|
||||
" call assert_fails("let x = d->index(2)", 'E897:')
|
||||
" call assert_fails("let x = d->insert(0)", 'E899:')
|
||||
call assert_fails("let x = d->index(2)", 'E714:')
|
||||
call assert_fails("let x = d->insert(0)", 'E686:')
|
||||
call assert_true(d->has_key('two'))
|
||||
call assert_equal([['one', 1], ['two', 2], ['three', 3]], d->items())
|
||||
call assert_fails("let x = d->join()", 'E714:')
|
||||
call assert_equal(['one', 'two', 'three'], d->keys())
|
||||
call assert_equal(3, d->len())
|
||||
call assert_equal(#{one: 2, two: 3, three: 4}, d->map('v:val + 1'))
|
||||
call assert_equal(#{one: 1, two: 2, three: 3}, d->map('v:val - 1'))
|
||||
call assert_equal(3, d->max())
|
||||
call assert_equal(1, d->min())
|
||||
call assert_equal(2, d->remove("two"))
|
||||
let d.two = 2
|
||||
call assert_fails('let x = d->repeat(2)', 'E731:')
|
||||
" Nvim doesn't support Blobs yet; expect a different emsg
|
||||
" call assert_fails('let x = d->reverse()', 'E899:')
|
||||
call assert_fails('let x = d->reverse()', 'E686:')
|
||||
call assert_fails('let x = d->sort()', 'E686:')
|
||||
call assert_equal("{'one': 1, 'two': 2, 'three': 3}", d->string())
|
||||
call assert_equal(v:t_dict, d->type())
|
||||
call assert_fails('let x = d->uniq()', 'E686:')
|
||||
call assert_equal([1, 2, 3], d->values())
|
||||
endfunc
|
||||
|
||||
func Test_string_method()
|
||||
eval '1 2 3'->split()->assert_equal(['1', '2', '3'])
|
||||
eval '1 2 3'->split()->map({i, v -> str2nr(v)})->assert_equal([1, 2, 3])
|
||||
eval 'ABC'->str2list()->assert_equal([65, 66, 67])
|
||||
eval 'ABC'->strlen()->assert_equal(3)
|
||||
eval "a\rb\ec"->strtrans()->assert_equal('a^Mb^[c')
|
||||
eval "aあb"->strwidth()->assert_equal(4)
|
||||
eval 'abc'->substitute('b', 'x', '')->assert_equal('axc')
|
||||
|
||||
eval 'abc'->printf('the %s arg')->assert_equal('the abc arg')
|
||||
endfunc
|
||||
|
||||
func Test_method_append()
|
||||
new
|
||||
eval ['one', 'two', 'three']->append(1)
|
||||
call assert_equal(['', 'one', 'two', 'three'], getline(1, '$'))
|
||||
|
||||
%del
|
||||
let bnr = bufnr('')
|
||||
wincmd w
|
||||
eval ['one', 'two', 'three']->appendbufline(bnr, 1)
|
||||
call assert_equal(['', 'one', 'two', 'three'], getbufline(bnr, 1, '$'))
|
||||
|
||||
exe 'bwipe! ' .. bnr
|
||||
endfunc
|
||||
|
||||
func Test_method_funcref()
|
||||
func Concat(one, two, three)
|
||||
return a:one .. a:two .. a:three
|
||||
endfunc
|
||||
let FuncRef = function('Concat')
|
||||
eval 'foo'->FuncRef('bar', 'tail')->assert_equal('foobartail')
|
||||
|
||||
" not enough arguments
|
||||
call assert_fails("eval 'foo'->FuncRef('bar')", 'E119:')
|
||||
" too many arguments
|
||||
call assert_fails("eval 'foo'->FuncRef('bar', 'tail', 'four')", 'E118:')
|
||||
|
||||
let Partial = function('Concat', ['two'])
|
||||
eval 'one'->Partial('three')->assert_equal('onetwothree')
|
||||
|
||||
" not enough arguments
|
||||
call assert_fails("eval 'one'->Partial()", 'E119:')
|
||||
" too many arguments
|
||||
call assert_fails("eval 'one'->Partial('three', 'four')", 'E118:')
|
||||
|
||||
delfunc Concat
|
||||
endfunc
|
||||
|
||||
func Test_method_float()
|
||||
eval 1.234->string()->assert_equal('1.234')
|
||||
eval -1.234->string()->assert_equal('-1.234')
|
||||
endfunc
|
||||
|
||||
func Test_method_syntax()
|
||||
eval [1, 2, 3] ->sort( )
|
||||
eval [1, 2, 3]
|
||||
\ ->sort(
|
||||
\ )
|
||||
call assert_fails('eval [1, 2, 3]-> sort()', 'E260:')
|
||||
call assert_fails('eval [1, 2, 3]->sort ()', 'E274:')
|
||||
call assert_fails('eval [1, 2, 3]-> sort ()', 'E260:')
|
||||
endfunc
|
||||
|
||||
func Test_method_lambda()
|
||||
eval "text"->{x -> x .. " extended"}()->assert_equal('text extended')
|
||||
eval "text"->{x, y -> x .. " extended " .. y}('more')->assert_equal('text extended more')
|
||||
|
||||
call assert_fails('eval "text"->{x -> x .. " extended"} ()', 'E274:')
|
||||
|
||||
" todo: lambda accepts more arguments than it consumes
|
||||
" call assert_fails('eval "text"->{x -> x .. " extended"}("more")', 'E99:')
|
||||
|
||||
" Nvim doesn't include test_refcount().
|
||||
" let l = [1, 2, 3]
|
||||
" eval l->{x -> x}()
|
||||
" call assert_equal(1, test_refcount(l))
|
||||
endfunc
|
||||
|
||||
func Test_method_not_supported()
|
||||
call assert_fails('eval 123->changenr()', 'E276:')
|
||||
endfunc
|
||||
|
||||
" vim: shiftwidth=2 sts=2 expandtab
|
||||
@@ -250,7 +250,7 @@ endfunc
|
||||
|
||||
func Test_noinsert_complete()
|
||||
func! s:complTest1() abort
|
||||
call complete(1, ['source', 'soundfold'])
|
||||
eval ['source', 'soundfold']->complete(1)
|
||||
return ''
|
||||
endfunc
|
||||
|
||||
@@ -403,7 +403,7 @@ func DummyCompleteFour(findstart, base)
|
||||
return 0
|
||||
else
|
||||
call complete_add('four1')
|
||||
call complete_add('four2')
|
||||
eval 'four2'->complete_add()
|
||||
call complete_check()
|
||||
call complete_add('four3')
|
||||
call complete_add('four4')
|
||||
@@ -989,7 +989,7 @@ func GetCompleteInfo()
|
||||
if empty(g:compl_what)
|
||||
let g:compl_info = complete_info()
|
||||
else
|
||||
let g:compl_info = complete_info(g:compl_what)
|
||||
let g:compl_info = g:compl_what->complete_info()
|
||||
endif
|
||||
return ''
|
||||
endfunc
|
||||
|
||||
@@ -546,8 +546,8 @@ func Test_synstack_synIDtrans()
|
||||
call assert_equal([], synstack(1, 1))
|
||||
|
||||
norm f/
|
||||
call assert_equal(['cComment', 'cCommentStart'], map(synstack(line("."), col(".")), 'synIDattr(v:val, "name")'))
|
||||
call assert_equal(['Comment', 'Comment'], map(synstack(line("."), col(".")), 'synIDattr(synIDtrans(v:val), "name")'))
|
||||
eval synstack(line("."), col("."))->map('synIDattr(v:val, "name")')->assert_equal(['cComment', 'cCommentStart'])
|
||||
eval synstack(line("."), col("."))->map('synIDattr(synIDtrans(v:val), "name")')->assert_equal(['Comment', 'Comment'])
|
||||
|
||||
norm fA
|
||||
call assert_equal(['cComment'], map(synstack(line("."), col(".")), 'synIDattr(v:val, "name")'))
|
||||
|
||||
@@ -7,10 +7,10 @@ func Test_System()
|
||||
if !executable('echo') || !executable('cat') || !executable('wc')
|
||||
return
|
||||
endif
|
||||
let out = system('echo 123')
|
||||
let out = 'echo 123'->system()
|
||||
call assert_equal("123\n", out)
|
||||
|
||||
let out = systemlist('echo 123')
|
||||
let out = 'echo 123'->systemlist()
|
||||
if &shell =~# 'cmd.exe$'
|
||||
call assert_equal(["123\r"], out)
|
||||
else
|
||||
|
||||
@@ -47,7 +47,7 @@ func FuncWithRef(a)
|
||||
endfunc
|
||||
|
||||
func Test_user_func()
|
||||
let g:FuncRef=function("FuncWithRef")
|
||||
let g:FuncRef = function("FuncWithRef")
|
||||
let g:counter = 0
|
||||
inoremap <expr> ( ListItem()
|
||||
inoremap <expr> [ ListReset()
|
||||
@@ -62,6 +62,14 @@ func Test_user_func()
|
||||
call assert_equal(9, g:retval)
|
||||
call assert_equal(333, g:FuncRef(333))
|
||||
|
||||
let g:retval = "nop"
|
||||
call assert_equal('xxx4asdf', "xxx"->Table(4, "asdf"))
|
||||
call assert_equal('fail', 45->Compute(0, "retval"))
|
||||
call assert_equal('nop', g:retval)
|
||||
call assert_equal('ok', 45->Compute(5, "retval"))
|
||||
call assert_equal(9, g:retval)
|
||||
" call assert_equal(333, 333->g:FuncRef())
|
||||
|
||||
enew
|
||||
|
||||
normal oXX+-XX
|
||||
@@ -150,6 +158,14 @@ func Test_default_arg()
|
||||
\ execute('func Args2'))
|
||||
endfunc
|
||||
|
||||
func s:addFoo(lead)
|
||||
return a:lead .. 'foo'
|
||||
endfunc
|
||||
|
||||
func Test_user_method()
|
||||
eval 'bar'->s:addFoo()->assert_equal('barfoo')
|
||||
endfunc
|
||||
|
||||
func Test_failed_call_in_try()
|
||||
try | call UnknownFunc() | catch | endtry
|
||||
endfunc
|
||||
|
||||
@@ -1372,6 +1372,7 @@ func Test_bitwise_functions()
|
||||
" and
|
||||
call assert_equal(127, and(127, 127))
|
||||
call assert_equal(16, and(127, 16))
|
||||
eval 127->and(16)->assert_equal(16)
|
||||
call assert_equal(0, and(127, 128))
|
||||
call assert_fails("call and(1.0, 1)", 'E805:')
|
||||
call assert_fails("call and([], 1)", 'E745:')
|
||||
@@ -1382,6 +1383,7 @@ func Test_bitwise_functions()
|
||||
" or
|
||||
call assert_equal(23, or(16, 7))
|
||||
call assert_equal(15, or(8, 7))
|
||||
eval 8->or(7)->assert_equal(15)
|
||||
call assert_equal(123, or(0, 123))
|
||||
call assert_fails("call or(1.0, 1)", 'E805:')
|
||||
call assert_fails("call or([], 1)", 'E745:')
|
||||
@@ -1392,6 +1394,7 @@ func Test_bitwise_functions()
|
||||
" xor
|
||||
call assert_equal(0, xor(127, 127))
|
||||
call assert_equal(111, xor(127, 16))
|
||||
eval 127->xor(16)->assert_equal(111)
|
||||
call assert_equal(255, xor(127, 128))
|
||||
call assert_fails("call xor(1.0, 1)", 'E805:')
|
||||
call assert_fails("call xor([], 1)", 'E745:')
|
||||
@@ -1401,6 +1404,7 @@ func Test_bitwise_functions()
|
||||
call assert_fails("call xor(1, {})", 'E728:')
|
||||
" invert
|
||||
call assert_equal(65408, and(invert(127), 65535))
|
||||
eval 127->invert()->and(65535)->assert_equal(65408)
|
||||
call assert_equal(65519, and(invert(16), 65535))
|
||||
call assert_equal(65407, and(invert(128), 65535))
|
||||
call assert_fails("call invert(1.0)", 'E805:')
|
||||
|
||||
Reference in New Issue
Block a user