Skip to content

Commit 42ae78c

Browse files
committed
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)
1 parent 6b528fa commit 42ae78c

File tree

5 files changed

+227
-46
lines changed

5 files changed

+227
-46
lines changed

runtime/doc/eval.txt

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10920,15 +10920,58 @@ change their contents. Thus you can pass a |List| to a function and have the
1092010920
function add an item to it. If you want to make sure the function cannot
1092110921
change a |List| or |Dictionary| use |:lockvar|.
1092210922

10923-
When not using "...", the number of arguments in a function call must be equal
10924-
to the number of named arguments. When using "...", the number of arguments
10925-
may be larger.
10926-
1092710923
It is also possible to define a function without any arguments. You must
1092810924
still supply the () then.
1092910925

1093010926
It is allowed to define another function inside a function body.
1093110927

10928+
*optional-function-argument*
10929+
You can provide default values for positional named arguments. This makes
10930+
them optional for function calls. When a positional argument is not
10931+
specified at a call, the default expression is used to initialize it.
10932+
This only works for functions declared with |function|, not for lambda
10933+
expressions |expr-lambda|.
10934+
10935+
Example: >
10936+
function Something(key, value = 10)
10937+
echo a:key .. ": " .. value
10938+
endfunction
10939+
call Something('empty') "empty: 10"
10940+
call Something('key, 20) "key: 20"
10941+
10942+
The argument default expressions are evaluated at the time of the function
10943+
call, not definition. Thus it is possible to use an expression which is
10944+
invalid the moment the function is defined. The expressions are are also only
10945+
evaluated when arguments are not specified during a call.
10946+
10947+
You can pass |v:none| to use the default expression. Note that this means you
10948+
cannot pass v:none as an ordinary value when an argument has a default
10949+
expression.
10950+
10951+
Example: >
10952+
function Something(a = 10, b = 20, c = 30)
10953+
endfunction
10954+
call Something(1, v:none, 3) " b = 20
10955+
<
10956+
*E989*
10957+
Optional arguments with default expressions must occur after any mandatory
10958+
arguments. You can use "..." after all optional named arguments.
10959+
10960+
It is possible for later argument defaults to refer to prior arguments,
10961+
but not the other way around. They must be prefixed with "a:", as with all
10962+
arguments.
10963+
10964+
Example that works: >
10965+
:function Okay(mandatory, optional = a:mandatory)
10966+
:endfunction
10967+
Example that does NOT work: >
10968+
:function NoGood(first = a:second, second = 10)
10969+
:endfunction
10970+
<
10971+
When not using "...", the number of arguments in a function call must be equal
10972+
to the number of mandatory named arguments. When using "...", the number of
10973+
arguments may be larger.
10974+
1093210975
*local-variables*
1093310976
Inside a function local variables can be used. These will disappear when the
1093410977
function returns. Global variables need to be accessed with "g:".

src/structs.h

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1402,42 +1402,43 @@ typedef struct funccall_S funccall_T;
14021402
*/
14031403
typedef struct
14041404
{
1405-
int uf_varargs; /* variable nr of arguments */
1405+
int uf_varargs; // variable nr of arguments
14061406
int uf_flags;
1407-
int uf_calls; /* nr of active calls */
1408-
int uf_cleared; /* func_clear() was already called */
1409-
garray_T uf_args; /* arguments */
1410-
garray_T uf_lines; /* function lines */
1407+
int uf_calls; // nr of active calls
1408+
int uf_cleared; // func_clear() was already called
1409+
garray_T uf_args; // arguments
1410+
garray_T uf_def_args; // default argument expressions
1411+
garray_T uf_lines; // function lines
14111412
# ifdef FEAT_PROFILE
1412-
int uf_profiling; /* TRUE when func is being profiled */
1413+
int uf_profiling; // TRUE when func is being profiled
14131414
int uf_prof_initialized;
1414-
/* profiling the function as a whole */
1415-
int uf_tm_count; /* nr of calls */
1416-
proftime_T uf_tm_total; /* time spent in function + children */
1417-
proftime_T uf_tm_self; /* time spent in function itself */
1418-
proftime_T uf_tm_children; /* time spent in children this call */
1419-
/* profiling the function per line */
1420-
int *uf_tml_count; /* nr of times line was executed */
1421-
proftime_T *uf_tml_total; /* time spent in a line + children */
1422-
proftime_T *uf_tml_self; /* time spent in a line itself */
1423-
proftime_T uf_tml_start; /* start time for current line */
1424-
proftime_T uf_tml_children; /* time spent in children for this line */
1425-
proftime_T uf_tml_wait; /* start wait time for current line */
1426-
int uf_tml_idx; /* index of line being timed; -1 if none */
1427-
int uf_tml_execed; /* line being timed was executed */
1415+
// profiling the function as a whole
1416+
int uf_tm_count; // nr of calls
1417+
proftime_T uf_tm_total; // time spent in function + children
1418+
proftime_T uf_tm_self; // time spent in function itself
1419+
proftime_T uf_tm_children; // time spent in children this call
1420+
// profiling the function per line
1421+
int *uf_tml_count; // nr of times line was executed
1422+
proftime_T *uf_tml_total; // time spent in a line + children
1423+
proftime_T *uf_tml_self; // time spent in a line itself
1424+
proftime_T uf_tml_start; // start time for current line
1425+
proftime_T uf_tml_children; // time spent in children for this line
1426+
proftime_T uf_tml_wait; // start wait time for current line
1427+
int uf_tml_idx; // index of line being timed; -1 if none
1428+
int uf_tml_execed; // line being timed was executed
14281429
# endif
1429-
sctx_T uf_script_ctx; /* SCTX where function was defined,
1430-
used for s: variables */
1431-
int uf_refcount; /* reference count, see func_name_refcount() */
1432-
funccall_T *uf_scoped; /* l: local variables for closure */
1433-
char_u uf_name[1]; /* name of function (actually longer); can
1434-
start with <SNR>123_ (<SNR> is K_SPECIAL
1435-
KS_EXTRA KE_SNR) */
1430+
sctx_T uf_script_ctx; // SCTX where function was defined,
1431+
// used for s: variables
1432+
int uf_refcount; // reference count, see func_name_refcount()
1433+
funccall_T *uf_scoped; // l: local variables for closure
1434+
char_u uf_name[1]; // name of function (actually longer); can
1435+
// start with <SNR>123_ (<SNR> is K_SPECIAL
1436+
// KS_EXTRA KE_SNR)
14361437
} ufunc_T;
14371438

1438-
#define MAX_FUNC_ARGS 20 /* maximum number of function arguments */
1439-
#define VAR_SHORT_LEN 20 /* short variable name length */
1440-
#define FIXVAR_CNT 12 /* number of fixed variables */
1439+
#define MAX_FUNC_ARGS 20 // maximum number of function arguments
1440+
#define VAR_SHORT_LEN 20 // short variable name length
1441+
#define FIXVAR_CNT 12 // number of fixed variables
14411442

14421443
/* structure to hold info for a function that is currently being executed. */
14431444
struct funccall_S

src/testdir/test_user_func.vim

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,53 @@ func Test_user_func()
9494
unlet g:retval g:counter
9595
enew!
9696
endfunc
97+
98+
func Log(val, base = 10)
99+
return log(a:val) / log(a:base)
100+
endfunc
101+
102+
func Args(mandatory, optional = v:null, ...)
103+
return deepcopy(a:)
104+
endfunc
105+
106+
func Args2(a = 1, b = 2, c = 3)
107+
return deepcopy(a:)
108+
endfunc
109+
110+
func MakeBadFunc()
111+
func s:fcn(a, b=1, c)
112+
endfunc
113+
endfunc
114+
115+
func Test_default_arg()
116+
call assert_equal(1.0, Log(10))
117+
call assert_equal(log(10), Log(10, exp(1)))
118+
call assert_fails("call Log(1,2,3)", 'E118')
119+
120+
let res = Args(1)
121+
call assert_equal(res.mandatory, 1)
122+
call assert_equal(res.optional, v:null)
123+
call assert_equal(res['0'], 0)
124+
125+
let res = Args(1,2)
126+
call assert_equal(res.mandatory, 1)
127+
call assert_equal(res.optional, 2)
128+
call assert_equal(res['0'], 0)
129+
130+
let res = Args(1,2,3)
131+
call assert_equal(res.mandatory, 1)
132+
call assert_equal(res.optional, 2)
133+
call assert_equal(res['0'], 1)
134+
135+
call assert_fails("call MakeBadFunc()", 'E989')
136+
call assert_fails("fu F(a=1 ,) | endf", 'E475')
137+
138+
let d = Args2(7, v:none, 9)
139+
call assert_equal([7, 2, 9], [d.a, d.b, d.c])
140+
141+
call assert_equal("\n"
142+
\ .. " function Args2(a = 1, b = 2, c = 3)\n"
143+
\ .. "1 return deepcopy(a:)\n"
144+
\ .. " endfunction",
145+
\ execute('func Args2'))
146+
endfunc

0 commit comments

Comments
 (0)