mirror of
https://github.com/vim/vim.git
synced 2025-09-24 03:44:06 -04:00
patch 8.1.1310: named function arguments are never optional
Problem: Named function arguments are never optional. Solution: Support optional function arguments with a default value. (Andy Massimino, closes #3952)
This commit is contained in:
@@ -10920,15 +10920,58 @@ change their contents. Thus you can pass a |List| to a function and have the
|
|||||||
function add an item to it. If you want to make sure the function cannot
|
function add an item to it. If you want to make sure the function cannot
|
||||||
change a |List| or |Dictionary| use |:lockvar|.
|
change a |List| or |Dictionary| use |:lockvar|.
|
||||||
|
|
||||||
When not using "...", the number of arguments in a function call must be equal
|
|
||||||
to the number of named arguments. When using "...", the number of arguments
|
|
||||||
may be larger.
|
|
||||||
|
|
||||||
It is also possible to define a function without any arguments. You must
|
It is also possible to define a function without any arguments. You must
|
||||||
still supply the () then.
|
still supply the () then.
|
||||||
|
|
||||||
It is allowed to define another function inside a function body.
|
It is allowed to define another function inside a function body.
|
||||||
|
|
||||||
|
*optional-function-argument*
|
||||||
|
You can provide default values for positional named arguments. This makes
|
||||||
|
them optional for function calls. When a positional argument is not
|
||||||
|
specified at a call, the default expression is used to initialize it.
|
||||||
|
This only works for functions declared with |function|, not for lambda
|
||||||
|
expressions |expr-lambda|.
|
||||||
|
|
||||||
|
Example: >
|
||||||
|
function Something(key, value = 10)
|
||||||
|
echo a:key .. ": " .. value
|
||||||
|
endfunction
|
||||||
|
call Something('empty') "empty: 10"
|
||||||
|
call Something('key, 20) "key: 20"
|
||||||
|
|
||||||
|
The argument default expressions are evaluated at the time of the function
|
||||||
|
call, not definition. Thus it is possible to use an expression which is
|
||||||
|
invalid the moment the function is defined. The expressions are are also only
|
||||||
|
evaluated when arguments are not specified during a call.
|
||||||
|
|
||||||
|
You can pass |v:none| to use the default expression. Note that this means you
|
||||||
|
cannot pass v:none as an ordinary value when an argument has a default
|
||||||
|
expression.
|
||||||
|
|
||||||
|
Example: >
|
||||||
|
function Something(a = 10, b = 20, c = 30)
|
||||||
|
endfunction
|
||||||
|
call Something(1, v:none, 3) " b = 20
|
||||||
|
<
|
||||||
|
*E989*
|
||||||
|
Optional arguments with default expressions must occur after any mandatory
|
||||||
|
arguments. You can use "..." after all optional named arguments.
|
||||||
|
|
||||||
|
It is possible for later argument defaults to refer to prior arguments,
|
||||||
|
but not the other way around. They must be prefixed with "a:", as with all
|
||||||
|
arguments.
|
||||||
|
|
||||||
|
Example that works: >
|
||||||
|
:function Okay(mandatory, optional = a:mandatory)
|
||||||
|
:endfunction
|
||||||
|
Example that does NOT work: >
|
||||||
|
:function NoGood(first = a:second, second = 10)
|
||||||
|
:endfunction
|
||||||
|
<
|
||||||
|
When not using "...", the number of arguments in a function call must be equal
|
||||||
|
to the number of mandatory named arguments. When using "...", the number of
|
||||||
|
arguments may be larger.
|
||||||
|
|
||||||
*local-variables*
|
*local-variables*
|
||||||
Inside a function local variables can be used. These will disappear when the
|
Inside a function local variables can be used. These will disappear when the
|
||||||
function returns. Global variables need to be accessed with "g:".
|
function returns. Global variables need to be accessed with "g:".
|
||||||
|
@@ -1402,42 +1402,43 @@ typedef struct funccall_S funccall_T;
|
|||||||
*/
|
*/
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
int uf_varargs; /* variable nr of arguments */
|
int uf_varargs; // variable nr of arguments
|
||||||
int uf_flags;
|
int uf_flags;
|
||||||
int uf_calls; /* nr of active calls */
|
int uf_calls; // nr of active calls
|
||||||
int uf_cleared; /* func_clear() was already called */
|
int uf_cleared; // func_clear() was already called
|
||||||
garray_T uf_args; /* arguments */
|
garray_T uf_args; // arguments
|
||||||
garray_T uf_lines; /* function lines */
|
garray_T uf_def_args; // default argument expressions
|
||||||
|
garray_T uf_lines; // function lines
|
||||||
# ifdef FEAT_PROFILE
|
# ifdef FEAT_PROFILE
|
||||||
int uf_profiling; /* TRUE when func is being profiled */
|
int uf_profiling; // TRUE when func is being profiled
|
||||||
int uf_prof_initialized;
|
int uf_prof_initialized;
|
||||||
/* profiling the function as a whole */
|
// profiling the function as a whole
|
||||||
int uf_tm_count; /* nr of calls */
|
int uf_tm_count; // nr of calls
|
||||||
proftime_T uf_tm_total; /* time spent in function + children */
|
proftime_T uf_tm_total; // time spent in function + children
|
||||||
proftime_T uf_tm_self; /* time spent in function itself */
|
proftime_T uf_tm_self; // time spent in function itself
|
||||||
proftime_T uf_tm_children; /* time spent in children this call */
|
proftime_T uf_tm_children; // time spent in children this call
|
||||||
/* profiling the function per line */
|
// profiling the function per line
|
||||||
int *uf_tml_count; /* nr of times line was executed */
|
int *uf_tml_count; // nr of times line was executed
|
||||||
proftime_T *uf_tml_total; /* time spent in a line + children */
|
proftime_T *uf_tml_total; // time spent in a line + children
|
||||||
proftime_T *uf_tml_self; /* time spent in a line itself */
|
proftime_T *uf_tml_self; // time spent in a line itself
|
||||||
proftime_T uf_tml_start; /* start time for current line */
|
proftime_T uf_tml_start; // start time for current line
|
||||||
proftime_T uf_tml_children; /* time spent in children for this line */
|
proftime_T uf_tml_children; // time spent in children for this line
|
||||||
proftime_T uf_tml_wait; /* start wait time for current line */
|
proftime_T uf_tml_wait; // start wait time for current line
|
||||||
int uf_tml_idx; /* index of line being timed; -1 if none */
|
int uf_tml_idx; // index of line being timed; -1 if none
|
||||||
int uf_tml_execed; /* line being timed was executed */
|
int uf_tml_execed; // line being timed was executed
|
||||||
# endif
|
# endif
|
||||||
sctx_T uf_script_ctx; /* SCTX where function was defined,
|
sctx_T uf_script_ctx; // SCTX where function was defined,
|
||||||
used for s: variables */
|
// used for s: variables
|
||||||
int uf_refcount; /* reference count, see func_name_refcount() */
|
int uf_refcount; // reference count, see func_name_refcount()
|
||||||
funccall_T *uf_scoped; /* l: local variables for closure */
|
funccall_T *uf_scoped; // l: local variables for closure
|
||||||
char_u uf_name[1]; /* name of function (actually longer); can
|
char_u uf_name[1]; // name of function (actually longer); can
|
||||||
start with <SNR>123_ (<SNR> is K_SPECIAL
|
// start with <SNR>123_ (<SNR> is K_SPECIAL
|
||||||
KS_EXTRA KE_SNR) */
|
// KS_EXTRA KE_SNR)
|
||||||
} ufunc_T;
|
} ufunc_T;
|
||||||
|
|
||||||
#define MAX_FUNC_ARGS 20 /* maximum number of function arguments */
|
#define MAX_FUNC_ARGS 20 // maximum number of function arguments
|
||||||
#define VAR_SHORT_LEN 20 /* short variable name length */
|
#define VAR_SHORT_LEN 20 // short variable name length
|
||||||
#define FIXVAR_CNT 12 /* number of fixed variables */
|
#define FIXVAR_CNT 12 // number of fixed variables
|
||||||
|
|
||||||
/* structure to hold info for a function that is currently being executed. */
|
/* structure to hold info for a function that is currently being executed. */
|
||||||
struct funccall_S
|
struct funccall_S
|
||||||
|
@@ -94,3 +94,53 @@ func Test_user_func()
|
|||||||
unlet g:retval g:counter
|
unlet g:retval g:counter
|
||||||
enew!
|
enew!
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
func Log(val, base = 10)
|
||||||
|
return log(a:val) / log(a:base)
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Args(mandatory, optional = v:null, ...)
|
||||||
|
return deepcopy(a:)
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Args2(a = 1, b = 2, c = 3)
|
||||||
|
return deepcopy(a:)
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func MakeBadFunc()
|
||||||
|
func s:fcn(a, b=1, c)
|
||||||
|
endfunc
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_default_arg()
|
||||||
|
call assert_equal(1.0, Log(10))
|
||||||
|
call assert_equal(log(10), Log(10, exp(1)))
|
||||||
|
call assert_fails("call Log(1,2,3)", 'E118')
|
||||||
|
|
||||||
|
let res = Args(1)
|
||||||
|
call assert_equal(res.mandatory, 1)
|
||||||
|
call assert_equal(res.optional, v:null)
|
||||||
|
call assert_equal(res['0'], 0)
|
||||||
|
|
||||||
|
let res = Args(1,2)
|
||||||
|
call assert_equal(res.mandatory, 1)
|
||||||
|
call assert_equal(res.optional, 2)
|
||||||
|
call assert_equal(res['0'], 0)
|
||||||
|
|
||||||
|
let res = Args(1,2,3)
|
||||||
|
call assert_equal(res.mandatory, 1)
|
||||||
|
call assert_equal(res.optional, 2)
|
||||||
|
call assert_equal(res['0'], 1)
|
||||||
|
|
||||||
|
call assert_fails("call MakeBadFunc()", 'E989')
|
||||||
|
call assert_fails("fu F(a=1 ,) | endf", 'E475')
|
||||||
|
|
||||||
|
let d = Args2(7, v:none, 9)
|
||||||
|
call assert_equal([7, 2, 9], [d.a, d.b, d.c])
|
||||||
|
|
||||||
|
call assert_equal("\n"
|
||||||
|
\ .. " function Args2(a = 1, b = 2, c = 3)\n"
|
||||||
|
\ .. "1 return deepcopy(a:)\n"
|
||||||
|
\ .. " endfunction",
|
||||||
|
\ execute('func Args2'))
|
||||||
|
endfunc
|
||||||
|
109
src/userfunc.c
109
src/userfunc.c
@@ -75,6 +75,7 @@ get_function_args(
|
|||||||
char_u endchar,
|
char_u endchar,
|
||||||
garray_T *newargs,
|
garray_T *newargs,
|
||||||
int *varargs,
|
int *varargs,
|
||||||
|
garray_T *default_args,
|
||||||
int skip)
|
int skip)
|
||||||
{
|
{
|
||||||
int mustend = FALSE;
|
int mustend = FALSE;
|
||||||
@@ -82,9 +83,13 @@ get_function_args(
|
|||||||
char_u *p = arg;
|
char_u *p = arg;
|
||||||
int c;
|
int c;
|
||||||
int i;
|
int i;
|
||||||
|
int any_default = FALSE;
|
||||||
|
char_u *expr;
|
||||||
|
|
||||||
if (newargs != NULL)
|
if (newargs != NULL)
|
||||||
ga_init2(newargs, (int)sizeof(char_u *), 3);
|
ga_init2(newargs, (int)sizeof(char_u *), 3);
|
||||||
|
if (default_args != NULL)
|
||||||
|
ga_init2(default_args, (int)sizeof(char_u *), 3);
|
||||||
|
|
||||||
if (varargs != NULL)
|
if (varargs != NULL)
|
||||||
*varargs = FALSE;
|
*varargs = FALSE;
|
||||||
@@ -140,6 +145,43 @@ get_function_args(
|
|||||||
|
|
||||||
*p = c;
|
*p = c;
|
||||||
}
|
}
|
||||||
|
if (*skipwhite(p) == '=' && default_args != NULL)
|
||||||
|
{
|
||||||
|
typval_T rettv;
|
||||||
|
|
||||||
|
any_default = TRUE;
|
||||||
|
p = skipwhite(p) + 1;
|
||||||
|
p = skipwhite(p);
|
||||||
|
expr = p;
|
||||||
|
if (eval1(&p, &rettv, FALSE) != FAIL)
|
||||||
|
{
|
||||||
|
if (ga_grow(default_args, 1) == FAIL)
|
||||||
|
goto err_ret;
|
||||||
|
|
||||||
|
// trim trailing whitespace
|
||||||
|
while (p > expr && VIM_ISWHITE(p[-1]))
|
||||||
|
p--;
|
||||||
|
c = *p;
|
||||||
|
*p = NUL;
|
||||||
|
expr = vim_strsave(expr);
|
||||||
|
if (expr == NULL)
|
||||||
|
{
|
||||||
|
*p = c;
|
||||||
|
goto err_ret;
|
||||||
|
}
|
||||||
|
((char_u **)(default_args->ga_data))
|
||||||
|
[default_args->ga_len] = expr;
|
||||||
|
default_args->ga_len++;
|
||||||
|
*p = c;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
mustend = TRUE;
|
||||||
|
}
|
||||||
|
else if (any_default)
|
||||||
|
{
|
||||||
|
emsg(_("E989: Non-default argument follows default argument"));
|
||||||
|
mustend = TRUE;
|
||||||
|
}
|
||||||
if (*p == ',')
|
if (*p == ',')
|
||||||
++p;
|
++p;
|
||||||
else
|
else
|
||||||
@@ -163,6 +205,8 @@ get_function_args(
|
|||||||
err_ret:
|
err_ret:
|
||||||
if (newargs != NULL)
|
if (newargs != NULL)
|
||||||
ga_clear_strings(newargs);
|
ga_clear_strings(newargs);
|
||||||
|
if (default_args != NULL)
|
||||||
|
ga_clear_strings(default_args);
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,7 +254,7 @@ get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
|
|||||||
ga_init(&newlines);
|
ga_init(&newlines);
|
||||||
|
|
||||||
/* First, check if this is a lambda expression. "->" must exist. */
|
/* First, check if this is a lambda expression. "->" must exist. */
|
||||||
ret = get_function_args(&start, '-', NULL, NULL, TRUE);
|
ret = get_function_args(&start, '-', NULL, NULL, NULL, TRUE);
|
||||||
if (ret == FAIL || *start != '>')
|
if (ret == FAIL || *start != '>')
|
||||||
return NOTDONE;
|
return NOTDONE;
|
||||||
|
|
||||||
@@ -220,7 +264,7 @@ get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
|
|||||||
else
|
else
|
||||||
pnewargs = NULL;
|
pnewargs = NULL;
|
||||||
*arg = skipwhite(*arg + 1);
|
*arg = skipwhite(*arg + 1);
|
||||||
ret = get_function_args(arg, '-', pnewargs, &varargs, FALSE);
|
ret = get_function_args(arg, '-', pnewargs, &varargs, NULL, FALSE);
|
||||||
if (ret == FAIL || **arg != '>')
|
if (ret == FAIL || **arg != '>')
|
||||||
goto errret;
|
goto errret;
|
||||||
|
|
||||||
@@ -272,6 +316,7 @@ get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
|
|||||||
STRCPY(fp->uf_name, name);
|
STRCPY(fp->uf_name, name);
|
||||||
hash_add(&func_hashtab, UF2HIKEY(fp));
|
hash_add(&func_hashtab, UF2HIKEY(fp));
|
||||||
fp->uf_args = newargs;
|
fp->uf_args = newargs;
|
||||||
|
ga_init(&fp->uf_def_args);
|
||||||
fp->uf_lines = newlines;
|
fp->uf_lines = newlines;
|
||||||
if (current_funccal != NULL && eval_lavars)
|
if (current_funccal != NULL && eval_lavars)
|
||||||
{
|
{
|
||||||
@@ -729,6 +774,7 @@ call_user_func(
|
|||||||
int using_sandbox = FALSE;
|
int using_sandbox = FALSE;
|
||||||
funccall_T *fc;
|
funccall_T *fc;
|
||||||
int save_did_emsg;
|
int save_did_emsg;
|
||||||
|
int default_arg_err = FALSE;
|
||||||
static int depth = 0;
|
static int depth = 0;
|
||||||
dictitem_T *v;
|
dictitem_T *v;
|
||||||
int fixvar_idx = 0; /* index in fixvar[] */
|
int fixvar_idx = 0; /* index in fixvar[] */
|
||||||
@@ -805,12 +851,13 @@ call_user_func(
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Init a: variables.
|
* Init a: variables.
|
||||||
* Set a:0 to "argcount".
|
* Set a:0 to "argcount" less number of named arguments, if >= 0.
|
||||||
* Set a:000 to a list with room for the "..." arguments.
|
* Set a:000 to a list with room for the "..." arguments.
|
||||||
*/
|
*/
|
||||||
init_var_dict(&fc->l_avars, &fc->l_avars_var, VAR_SCOPE);
|
init_var_dict(&fc->l_avars, &fc->l_avars_var, VAR_SCOPE);
|
||||||
add_nr_var(&fc->l_avars, &fc->fixvar[fixvar_idx++].var, "0",
|
add_nr_var(&fc->l_avars, &fc->fixvar[fixvar_idx++].var, "0",
|
||||||
(varnumber_T)(argcount - fp->uf_args.ga_len));
|
(varnumber_T)(argcount >= fp->uf_args.ga_len
|
||||||
|
? argcount - fp->uf_args.ga_len : 0));
|
||||||
fc->l_avars.dv_lock = VAR_FIXED;
|
fc->l_avars.dv_lock = VAR_FIXED;
|
||||||
/* Use "name" to avoid a warning from some compiler that checks the
|
/* Use "name" to avoid a warning from some compiler that checks the
|
||||||
* destination size. */
|
* destination size. */
|
||||||
@@ -835,9 +882,11 @@ call_user_func(
|
|||||||
(varnumber_T)firstline);
|
(varnumber_T)firstline);
|
||||||
add_nr_var(&fc->l_avars, &fc->fixvar[fixvar_idx++].var, "lastline",
|
add_nr_var(&fc->l_avars, &fc->fixvar[fixvar_idx++].var, "lastline",
|
||||||
(varnumber_T)lastline);
|
(varnumber_T)lastline);
|
||||||
for (i = 0; i < argcount; ++i)
|
for (i = 0; i < argcount || i < fp->uf_args.ga_len; ++i)
|
||||||
{
|
{
|
||||||
int addlocal = FALSE;
|
int addlocal = FALSE;
|
||||||
|
typval_T def_rettv;
|
||||||
|
int isdefault = FALSE;
|
||||||
|
|
||||||
ai = i - fp->uf_args.ga_len;
|
ai = i - fp->uf_args.ga_len;
|
||||||
if (ai < 0)
|
if (ai < 0)
|
||||||
@@ -846,6 +895,25 @@ call_user_func(
|
|||||||
name = FUNCARG(fp, i);
|
name = FUNCARG(fp, i);
|
||||||
if (islambda)
|
if (islambda)
|
||||||
addlocal = TRUE;
|
addlocal = TRUE;
|
||||||
|
|
||||||
|
// evaluate named argument default expression
|
||||||
|
isdefault = ai + fp->uf_def_args.ga_len >= 0
|
||||||
|
&& (i >= argcount || (argvars[i].v_type == VAR_SPECIAL
|
||||||
|
&& argvars[i].vval.v_number == VVAL_NONE));
|
||||||
|
if (isdefault)
|
||||||
|
{
|
||||||
|
char_u *default_expr = NULL;
|
||||||
|
def_rettv.v_type = VAR_NUMBER;
|
||||||
|
def_rettv.vval.v_number = -1;
|
||||||
|
|
||||||
|
default_expr = ((char_u **)(fp->uf_def_args.ga_data))
|
||||||
|
[ai + fp->uf_def_args.ga_len];
|
||||||
|
if (eval1(&default_expr, &def_rettv, TRUE) == FAIL)
|
||||||
|
{
|
||||||
|
default_arg_err = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -867,9 +935,12 @@ call_user_func(
|
|||||||
v->di_flags |= DI_FLAGS_RO | DI_FLAGS_FIX;
|
v->di_flags |= DI_FLAGS_RO | DI_FLAGS_FIX;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Note: the values are copied directly to avoid alloc/free.
|
if (isdefault)
|
||||||
* "argvars" must have VAR_FIXED for v_lock. */
|
v->di_tv = def_rettv;
|
||||||
v->di_tv = argvars[i];
|
else
|
||||||
|
// Note: the values are copied directly to avoid alloc/free.
|
||||||
|
// "argvars" must have VAR_FIXED for v_lock.
|
||||||
|
v->di_tv = argvars[i];
|
||||||
v->di_tv.v_lock = VAR_FIXED;
|
v->di_tv.v_lock = VAR_FIXED;
|
||||||
|
|
||||||
if (addlocal)
|
if (addlocal)
|
||||||
@@ -988,8 +1059,11 @@ call_user_func(
|
|||||||
save_did_emsg = did_emsg;
|
save_did_emsg = did_emsg;
|
||||||
did_emsg = FALSE;
|
did_emsg = FALSE;
|
||||||
|
|
||||||
/* call do_cmdline() to execute the lines */
|
if (default_arg_err && (fp->uf_flags & FC_ABORT))
|
||||||
do_cmdline(NULL, get_func_line, (void *)fc,
|
did_emsg = TRUE;
|
||||||
|
else
|
||||||
|
// call do_cmdline() to execute the lines
|
||||||
|
do_cmdline(NULL, get_func_line, (void *)fc,
|
||||||
DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
|
DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
|
||||||
|
|
||||||
--RedrawingDisabled;
|
--RedrawingDisabled;
|
||||||
@@ -1145,6 +1219,7 @@ func_remove(ufunc_T *fp)
|
|||||||
func_clear_items(ufunc_T *fp)
|
func_clear_items(ufunc_T *fp)
|
||||||
{
|
{
|
||||||
ga_clear_strings(&(fp->uf_args));
|
ga_clear_strings(&(fp->uf_args));
|
||||||
|
ga_clear_strings(&(fp->uf_def_args));
|
||||||
ga_clear_strings(&(fp->uf_lines));
|
ga_clear_strings(&(fp->uf_lines));
|
||||||
#ifdef FEAT_PROFILE
|
#ifdef FEAT_PROFILE
|
||||||
vim_free(fp->uf_tml_count);
|
vim_free(fp->uf_tml_count);
|
||||||
@@ -1498,7 +1573,7 @@ call_func(
|
|||||||
|
|
||||||
if (fp->uf_flags & FC_RANGE)
|
if (fp->uf_flags & FC_RANGE)
|
||||||
*doesrange = TRUE;
|
*doesrange = TRUE;
|
||||||
if (argcount < fp->uf_args.ga_len)
|
if (argcount < fp->uf_args.ga_len - fp->uf_def_args.ga_len)
|
||||||
error = ERROR_TOOFEW;
|
error = ERROR_TOOFEW;
|
||||||
else if (!fp->uf_varargs && argcount > fp->uf_args.ga_len)
|
else if (!fp->uf_varargs && argcount > fp->uf_args.ga_len)
|
||||||
error = ERROR_TOOMANY;
|
error = ERROR_TOOMANY;
|
||||||
@@ -1624,6 +1699,12 @@ list_func_head(ufunc_T *fp, int indent)
|
|||||||
if (j)
|
if (j)
|
||||||
msg_puts(", ");
|
msg_puts(", ");
|
||||||
msg_puts((char *)FUNCARG(fp, j));
|
msg_puts((char *)FUNCARG(fp, j));
|
||||||
|
if (j >= fp->uf_args.ga_len - fp->uf_def_args.ga_len)
|
||||||
|
{
|
||||||
|
msg_puts(" = ");
|
||||||
|
msg_puts(((char **)(fp->uf_def_args.ga_data))
|
||||||
|
[j - fp->uf_args.ga_len + fp->uf_def_args.ga_len]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (fp->uf_varargs)
|
if (fp->uf_varargs)
|
||||||
{
|
{
|
||||||
@@ -1889,6 +1970,7 @@ ex_function(exarg_T *eap)
|
|||||||
char_u *arg;
|
char_u *arg;
|
||||||
char_u *line_arg = NULL;
|
char_u *line_arg = NULL;
|
||||||
garray_T newargs;
|
garray_T newargs;
|
||||||
|
garray_T default_args;
|
||||||
garray_T newlines;
|
garray_T newlines;
|
||||||
int varargs = FALSE;
|
int varargs = FALSE;
|
||||||
int flags = 0;
|
int flags = 0;
|
||||||
@@ -2103,7 +2185,8 @@ ex_function(exarg_T *eap)
|
|||||||
emsg(_("E862: Cannot use g: here"));
|
emsg(_("E862: Cannot use g: here"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get_function_args(&p, ')', &newargs, &varargs, eap->skip) == FAIL)
|
if (get_function_args(&p, ')', &newargs, &varargs,
|
||||||
|
&default_args, eap->skip) == FAIL)
|
||||||
goto errret_2;
|
goto errret_2;
|
||||||
|
|
||||||
/* find extra arguments "range", "dict", "abort" and "closure" */
|
/* find extra arguments "range", "dict", "abort" and "closure" */
|
||||||
@@ -2511,6 +2594,7 @@ ex_function(exarg_T *eap)
|
|||||||
fp->uf_refcount = 1;
|
fp->uf_refcount = 1;
|
||||||
}
|
}
|
||||||
fp->uf_args = newargs;
|
fp->uf_args = newargs;
|
||||||
|
fp->uf_def_args = default_args;
|
||||||
fp->uf_lines = newlines;
|
fp->uf_lines = newlines;
|
||||||
if ((flags & FC_CLOSURE) != 0)
|
if ((flags & FC_CLOSURE) != 0)
|
||||||
{
|
{
|
||||||
@@ -2535,6 +2619,7 @@ ex_function(exarg_T *eap)
|
|||||||
|
|
||||||
erret:
|
erret:
|
||||||
ga_clear_strings(&newargs);
|
ga_clear_strings(&newargs);
|
||||||
|
ga_clear_strings(&default_args);
|
||||||
errret_2:
|
errret_2:
|
||||||
ga_clear_strings(&newlines);
|
ga_clear_strings(&newlines);
|
||||||
ret_free:
|
ret_free:
|
||||||
|
@@ -767,6 +767,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
1310,
|
||||||
/**/
|
/**/
|
||||||
1309,
|
1309,
|
||||||
/**/
|
/**/
|
||||||
|
Reference in New Issue
Block a user