summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLemonBoy <thatlemon@gmail.com>2024-07-09 18:24:59 +0200
committerChristian Brabandt <cb@256bit.org>2024-07-09 18:31:12 +0200
commit48b7d05a4f88c4326bd5d7a73a523f2d953b3e51 (patch)
tree241c33ce0660a03e6705448665be9dbb133de211
parent03acd4761be1c2766d3ec17534ea63cdf8dd565d (diff)
patch 9.1.0547: No way to get the arity of a Vim functionv9.1.0547
Problem: No way to get the arity of a Vim function (Austin Ziegler) Solution: Enhance get() Vim script function to return the function argument info using get(func, "arity") (LemonBoy) fixes: #15097 closes: #15109 Signed-off-by: LemonBoy <thatlemon@gmail.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
-rw-r--r--runtime/doc/builtin.txt36
-rw-r--r--runtime/doc/tags4
-rw-r--r--runtime/doc/version9.txt4
-rw-r--r--src/evalfunc.c30
-rw-r--r--src/proto/userfunc.pro1
-rw-r--r--src/testdir/test_getvar.vim8
-rw-r--r--src/testdir/test_partial.vim22
-rw-r--r--src/userfunc.c41
-rw-r--r--src/version.c2
9 files changed, 136 insertions, 12 deletions
diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt
index 238b7e43d2..635b7b2218 100644
--- a/runtime/doc/builtin.txt
+++ b/runtime/doc/builtin.txt
@@ -1,4 +1,4 @@
-*builtin.txt* For Vim version 9.1. Last change: 2024 Jun 23
+*builtin.txt* For Vim version 9.1. Last change: 2024 Jul 09
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -3574,7 +3574,7 @@ garbagecollect([{atexit}]) *garbagecollect()*
Return type: |String|
-get({list}, {idx} [, {default}]) *get()*
+get({list}, {idx} [, {default}]) *get()* *get()-list*
Get item {idx} from |List| {list}. When this item is not
available return {default}. Return zero when {default} is
omitted.
@@ -3583,7 +3583,7 @@ get({list}, {idx} [, {default}]) *get()*
<
Return type: any, depending on {list}
-get({blob}, {idx} [, {default}])
+get({blob}, {idx} [, {default}]) *get()-blob*
Get byte {idx} from |Blob| {blob}. When this byte is not
available return {default}. Return -1 when {default} is
omitted.
@@ -3592,7 +3592,7 @@ get({blob}, {idx} [, {default}])
<
Return type: |Number|
-get({dict}, {key} [, {default}])
+get({dict}, {key} [, {default}]) *get()-dict*
Get item with key {key} from |Dictionary| {dict}. When this
item is not available return {default}. Return zero when
{default} is omitted. Useful example: >
@@ -3604,18 +3604,32 @@ get({dict}, {key} [, {default}])
<
Return type: any, depending on {dict}
-get({func}, {what})
- Get item {what} from Funcref {func}. Possible values for
+get({func}, {what}) *get()-func*
+ Get item {what} from |Funcref| {func}. Possible values for
{what} are:
- "name" The function name
- "func" The function
- "dict" The dictionary
- "args" The list with arguments
+ "name" The function name
+ "func" The function
+ "dict" The dictionary
+ "args" The list with arguments
+ "arity" A dictionary with information about the number of
+ arguments accepted by the function (minus the
+ {arglist}) with the following fields:
+ required the number of positional arguments
+ optional the number of optional arguments,
+ in addition to the required ones
+ varargs |TRUE| if the function accepts a
+ variable number of arguments |...|
+
+ Note: There is no error, if the {arglist} of
+ the Funcref contains more arguments than the
+ Funcref expects, it's not validated.
+
Returns zero on error.
+
Preferably used as a |method|: >
myfunc->get(what)
<
- Return type: any, depending on {func}
+ Return type: any, depending on {func} and {what}
*getbufinfo()*
getbufinfo([{buf}])
diff --git a/runtime/doc/tags b/runtime/doc/tags
index e72f73927d..be71710bc4 100644
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -7783,6 +7783,10 @@ gdb-version terminal.txt /*gdb-version*
ge motion.txt /*ge*
gender-neutral helphelp.txt /*gender-neutral*
get() builtin.txt /*get()*
+get()-blob builtin.txt /*get()-blob*
+get()-dict builtin.txt /*get()-dict*
+get()-func builtin.txt /*get()-func*
+get()-list builtin.txt /*get()-list*
get-ms-debuggers debug.txt /*get-ms-debuggers*
getbufinfo() builtin.txt /*getbufinfo()*
getbufline() builtin.txt /*getbufline()*
diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt
index 92792b9cd5..43de2692fa 100644
--- a/runtime/doc/version9.txt
+++ b/runtime/doc/version9.txt
@@ -1,4 +1,4 @@
-*version9.txt* For Vim version 9.1. Last change: 2024 Jul 06
+*version9.txt* For Vim version 9.1. Last change: 2024 Jul 08
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -41577,6 +41577,8 @@ Changed~
- 'nrformat' accepts the new "blank" suboption, to determine a signed or
unsigned number based on whitespace in front of a minus sign.
- allow to specify a priority when defining a new sign |:sign-define|
+- provide information about function arguments using the get(func, "arity")
+ function |get()-func|
*added-9.2*
Added ~
diff --git a/src/evalfunc.c b/src/evalfunc.c
index c9480f9f64..5e3122dd97 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -5134,6 +5134,36 @@ f_get(typval_T *argvars, typval_T *rettv)
list_append_tv(rettv->vval.v_list, &pt->pt_argv[i]);
}
}
+ else if (STRCMP(what, "arity") == 0)
+ {
+ int required = 0, optional = 0, varargs = FALSE;
+ char_u *name = partial_name(pt);
+
+ get_func_arity(name, &required, &optional, &varargs);
+
+ rettv->v_type = VAR_DICT;
+ if (rettv_dict_alloc(rettv) == OK)
+ {
+ dict_T *dict = rettv->vval.v_dict;
+
+ // Take into account the arguments of the partial, if any.
+ // Note that it is possible to supply more arguments than the function
+ // accepts.
+ if (pt->pt_argc >= required + optional)
+ required = optional = 0;
+ else if (pt->pt_argc > required)
+ {
+ optional -= pt->pt_argc - required;
+ required = 0;
+ }
+ else
+ required -= pt->pt_argc;
+
+ dict_add_number(dict, "required", required);
+ dict_add_number(dict, "optional", optional);
+ dict_add_bool(dict, "varargs", varargs);
+ }
+ }
else
semsg(_(e_invalid_argument_str), what);
diff --git a/src/proto/userfunc.pro b/src/proto/userfunc.pro
index 9bb461663e..ce5d257caf 100644
--- a/src/proto/userfunc.pro
+++ b/src/proto/userfunc.pro
@@ -95,4 +95,5 @@ int set_ref_in_call_stack(int copyID);
int set_ref_in_functions(int copyID);
int set_ref_in_func_args(int copyID);
int set_ref_in_func(char_u *name, ufunc_T *fp_in, int copyID);
+int get_func_arity(char_u *name, int *required, int *optional, int *varargs);
/* vim: set ft=c : */
diff --git a/src/testdir/test_getvar.vim b/src/testdir/test_getvar.vim
index 2065186a5a..6efb192ebc 100644
--- a/src/testdir/test_getvar.vim
+++ b/src/testdir/test_getvar.vim
@@ -142,20 +142,28 @@ func Test_get_func()
let l:F = function('tr')
call assert_equal('tr', get(l:F, 'name'))
call assert_equal(l:F, get(l:F, 'func'))
+ call assert_equal({'required': 3, 'optional': 0, 'varargs': v:false},
+ \ get(l:F, 'arity'))
let Fb_func = function('s:FooBar')
call assert_match('<SNR>\d\+_FooBar', get(Fb_func, 'name'))
+ call assert_equal({'required': 0, 'optional': 0, 'varargs': v:false},
+ \ get(Fb_func, 'arity'))
let Fb_ref = funcref('s:FooBar')
call assert_match('<SNR>\d\+_FooBar', get(Fb_ref, 'name'))
+ call assert_equal({'required': 0, 'optional': 0, 'varargs': v:false},
+ \ get(Fb_ref, 'arity'))
call assert_equal({'func has': 'no dict'}, get(l:F, 'dict', {'func has': 'no dict'}))
call assert_equal(0, get(l:F, 'dict'))
call assert_equal([], get(l:F, 'args'))
+
let NF = test_null_function()
call assert_equal('', get(NF, 'name'))
call assert_equal(NF, get(NF, 'func'))
call assert_equal(0, get(NF, 'dict'))
call assert_equal([], get(NF, 'args'))
+ call assert_equal({'required': 0, 'optional': 0, 'varargs': v:false}, get(NF, 'arity'))
endfunc
" get({partial}, {what} [, {default}]) - in test_partial.vim
diff --git a/src/testdir/test_partial.vim b/src/testdir/test_partial.vim
index b5a58f6e59..acc8b73c81 100644
--- a/src/testdir/test_partial.vim
+++ b/src/testdir/test_partial.vim
@@ -311,6 +311,11 @@ func Test_auto_partial_rebind()
endfunc
func Test_get_partial_items()
+ func s:Qux(x, y, z=3, w=1, ...)
+ endfunc
+ func s:Qux1(x, y)
+ endfunc
+
let dict = {'name': 'hello'}
let args = ["foo", "bar"]
let Func = function('MyDictFunc')
@@ -331,6 +336,23 @@ func Test_get_partial_items()
let dict = {'partial has': 'no dict'}
call assert_equal(dict, get(P, 'dict', dict))
call assert_equal(0, get(l:P, 'dict'))
+
+ call assert_equal({'required': 2, 'optional': 2, 'varargs': v:true},
+ \ get(funcref('s:Qux', []), 'arity'))
+ call assert_equal({'required': 1, 'optional': 2, 'varargs': v:true},
+ \ get(funcref('s:Qux', [1]), 'arity'))
+ call assert_equal({'required': 0, 'optional': 2, 'varargs': v:true},
+ \ get(funcref('s:Qux', [1, 2]), 'arity'))
+ call assert_equal({'required': 0, 'optional': 1, 'varargs': v:true},
+ \ get(funcref('s:Qux', [1, 2, 3]), 'arity'))
+ call assert_equal({'required': 0, 'optional': 0, 'varargs': v:true},
+ \ get(funcref('s:Qux', [1, 2, 3, 4]), 'arity'))
+ " More args than expected is not an error
+ call assert_equal({'required': 0, 'optional': 0, 'varargs': v:false},
+ \ get(funcref('s:Qux1', [1, 2, 3, 4]), 'arity'))
+
+ delfunc s:Qux
+ delfunc s:Qux1
endfunc
func Test_compare_partials()
diff --git a/src/userfunc.c b/src/userfunc.c
index 7536234b82..e44397d81b 100644
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -5503,6 +5503,47 @@ ex_function(exarg_T *eap)
ga_clear_strings(&lines_to_free);
}
+ int
+get_func_arity(char_u *name, int *required, int *optional, int *varargs)
+{
+ ufunc_T *ufunc = NULL;
+ int argcount = 0;
+ int min_argcount = 0;
+ int idx;
+
+ idx = find_internal_func(name);
+ if (idx >= 0)
+ {
+ internal_func_get_argcount(idx, &argcount, &min_argcount);
+ *varargs = FALSE;
+ }
+ else
+ {
+ char_u fname_buf[FLEN_FIXED + 1];
+ char_u *tofree = NULL;
+ funcerror_T error = FCERR_NONE;
+ char_u *fname;
+
+ // May need to translate <SNR>123_ to K_SNR.
+ fname = fname_trans_sid(name, fname_buf, &tofree, &error);
+ if (error == FCERR_NONE)
+ ufunc = find_func(fname, FALSE);
+ vim_free(tofree);
+
+ if (ufunc == NULL)
+ return FAIL;
+
+ argcount = ufunc->uf_args.ga_len;
+ min_argcount = ufunc->uf_args.ga_len - ufunc->uf_def_args.ga_len;
+ *varargs = has_varargs(ufunc);
+ }
+
+ *required = min_argcount;
+ *optional = argcount - min_argcount;
+
+ return OK;
+}
+
/*
* Find a function by name, including "<lambda>123".
* Check for "profile" and "debug" arguments and set"compile_type".
diff --git a/src/version.c b/src/version.c
index 53bafd37d5..ce6b8d60ab 100644
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 547,
+/**/
546,
/**/
545,