diff options
-rw-r--r-- | runtime/doc/builtin.txt | 36 | ||||
-rw-r--r-- | runtime/doc/tags | 4 | ||||
-rw-r--r-- | runtime/doc/version9.txt | 4 | ||||
-rw-r--r-- | src/evalfunc.c | 30 | ||||
-rw-r--r-- | src/proto/userfunc.pro | 1 | ||||
-rw-r--r-- | src/testdir/test_getvar.vim | 8 | ||||
-rw-r--r-- | src/testdir/test_partial.vim | 22 | ||||
-rw-r--r-- | src/userfunc.c | 41 | ||||
-rw-r--r-- | src/version.c | 2 |
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, |