diff options
author | zeertzjq <zeertzjq@outlook.com> | 2023-04-15 21:27:30 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-04-15 21:27:30 +0800 |
commit | 2e2101cf7b0e4a70f5670d9d1317860a47bb8385 (patch) | |
tree | dadcd914c45ee440bb85e63e889d9e2723182fdb | |
parent | eb151a9730f0000ff46e0b3467e29bb9f02ae362 (diff) | |
parent | 9770dcf96d77d734e2b88fc693c0f4fa0a17ef74 (diff) | |
download | rneovim-2e2101cf7b0e4a70f5670d9d1317860a47bb8385.tar.gz rneovim-2e2101cf7b0e4a70f5670d9d1317860a47bb8385.tar.bz2 rneovim-2e2101cf7b0e4a70f5670d9d1317860a47bb8385.zip |
Merge pull request #23107 from zeertzjq/vim-9.0.0269
vim-patch:9.0.{0269,0303,1431}: more getscriptinfo() features
-rw-r--r-- | runtime/doc/builtin.txt | 38 | ||||
-rw-r--r-- | src/nvim/eval.lua | 2 | ||||
-rw-r--r-- | src/nvim/eval/userfunc.c | 11 | ||||
-rw-r--r-- | src/nvim/eval/userfunc.h | 6 | ||||
-rw-r--r-- | src/nvim/runtime.c | 76 | ||||
-rw-r--r-- | test/old/testdir/test_scriptnames.vim | 76 |
6 files changed, 188 insertions, 21 deletions
diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index 3c940ccfa2..cb05718ab9 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -223,7 +223,7 @@ getreg([{regname} [, 1 [, {list}]]]) String or List contents of a register getreginfo([{regname}]) Dict information about a register getregtype([{regname}]) String type of a register -getscriptinfo() List list of sourced scripts +getscriptinfo([{opts}]) List list of sourced scripts gettabinfo([{expr}]) List list of tab pages gettabvar({nr}, {varname} [, {def}]) any variable {varname} in tab {nr} or {def} @@ -3576,16 +3576,42 @@ getregtype([{regname}]) *getregtype()* Can also be used as a |method|: > GetRegname()->getregtype() -getscriptinfo() *getscriptinfo()* +getscriptinfo([{opts}]) *getscriptinfo()* Returns a |List| with information about all the sourced Vim - scripts in the order they were sourced. + scripts in the order they were sourced, like what + `:scriptnames` shows. + + The optional Dict argument {opts} supports the following + optional items: + name Script name match pattern. If specified, + and "sid" is not specified, information about + scripts with name that match the pattern + "name" are returned. + sid Script ID |<SID>|. If specified, only + information about the script with ID "sid" is + returned and "name" is ignored. Each item in the returned List is a |Dict| with the following items: - autoload always set to FALSE. - name vim script file name. - sid script ID |<SID>|. + autoload Always set to FALSE. + functions List of script-local function names defined in + the script. Present only when a particular + script is specified using the "sid" item in + {opts}. + name Vim script file name. + sid Script ID |<SID>|. + variables A dictionary with the script-local variables. + Present only when the a particular script is + specified using the "sid" item in {opts}. + Note that this is a copy, the value of + script-local variables cannot be changed using + this dictionary. + version Vimscript version, always 1 + Examples: > + :echo getscriptinfo({'name': 'myscript'}) + :echo getscriptinfo({'sid': 15}).variables +< gettabinfo([{tabnr}]) *gettabinfo()* If {tabnr} is not specified, then information about all the tab pages is returned as a |List|. Each List item is a diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 66032adbaf..7dbfac80f3 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -189,7 +189,7 @@ return { gettabinfo={args={0, 1}, base=1}, gettabvar={args={2, 3}, base=1}, gettabwinvar={args={3, 4}, base=1}, - getscriptinfo={}, + getscriptinfo={args={0, 1}}, gettagstack={args={0, 1}, base=1}, gettext={args=1, base=1}, getwininfo={args={0, 1}, base=1}, diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 67b1e53a35..9705bc93db 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -2015,12 +2015,11 @@ static void list_functions(regmatch_T *regmatch) if (!HASHITEM_EMPTY(hi)) { ufunc_T *fp = HI2UF(hi); todo--; - if ((fp->uf_flags & FC_DEAD) == 0 - && (regmatch == NULL - ? (!message_filtered(fp->uf_name) - && !func_name_refcount(fp->uf_name)) - : (!isdigit((uint8_t)(*fp->uf_name)) - && vim_regexec(regmatch, fp->uf_name, 0)))) { + if (regmatch == NULL + ? (!message_filtered(fp->uf_name) + && !func_name_refcount(fp->uf_name)) + : (!isdigit((uint8_t)(*fp->uf_name)) + && vim_regexec(regmatch, fp->uf_name, 0))) { list_func_head(fp, false, false); if (changed != func_hashtab.ht_changed) { emsg(_("E454: function list was modified")); diff --git a/src/nvim/eval/userfunc.h b/src/nvim/eval/userfunc.h index f0e1f5dca0..d0ad53c43d 100644 --- a/src/nvim/eval/userfunc.h +++ b/src/nvim/eval/userfunc.h @@ -28,10 +28,10 @@ struct funccal_entry; #define FC_DELETED 0x10 // :delfunction used while uf_refcount > 0 #define FC_REMOVED 0x20 // function redefined while uf_refcount > 0 #define FC_SANDBOX 0x40 // function defined in the sandbox -#define FC_DEAD 0x80 // function kept only for reference to dfunc -#define FC_EXPORT 0x100 // "export def Func()" +// #define FC_DEAD 0x80 // function kept only for reference to dfunc +// #define FC_EXPORT 0x100 // "export def Func()" #define FC_NOARGS 0x200 // no a: variables in lambda -#define FC_VIM9 0x400 // defined in vim9 script file +// #define FC_VIM9 0x400 // defined in vim9 script file #define FC_LUAREF 0x800 // luaref callback /// Structure used by trans_function_name() diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c index 1e39b58543..d091636e77 100644 --- a/src/nvim/runtime.c +++ b/src/nvim/runtime.c @@ -22,6 +22,7 @@ #include "nvim/cmdexpand.h" #include "nvim/debugger.h" #include "nvim/eval.h" +#include "nvim/eval/typval.h" #include "nvim/eval/userfunc.h" #include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" @@ -43,6 +44,7 @@ #include "nvim/os/stdpaths_defs.h" #include "nvim/path.h" #include "nvim/profile.h" +#include "nvim/regexp.h" #include "nvim/runtime.h" #include "nvim/strings.h" #include "nvim/usercmd.h" @@ -2356,27 +2358,99 @@ linenr_T get_sourced_lnum(LineGetter fgetline, void *cookie) : SOURCING_LNUM; } +/// Return a List of script-local functions defined in the script with id "sid". +static list_T *get_script_local_funcs(scid_T sid) +{ + hashtab_T *const functbl = func_tbl_get(); + list_T *l = tv_list_alloc((ptrdiff_t)functbl->ht_used); + + // Iterate through all the functions in the global function hash table + // looking for functions with script ID "sid". + HASHTAB_ITER(functbl, hi, { + const ufunc_T *const fp = HI2UF(hi); + // Add functions with script id == "sid" + if (fp->uf_script_ctx.sc_sid == sid) { + const char *const name = fp->uf_name_exp != NULL ? fp->uf_name_exp : fp->uf_name; + tv_list_append_string(l, name, -1); + } + }); + + return l; +} + /// "getscriptinfo()" function void f_getscriptinfo(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { tv_list_alloc_ret(rettv, script_items.ga_len); + if (tv_check_for_opt_dict_arg(argvars, 0) == FAIL) { + return; + } + list_T *l = rettv->vval.v_list; - for (int i = 1; i <= script_items.ga_len; i++) { + regmatch_T regmatch = { + .regprog = NULL, + .rm_ic = p_ic, + }; + bool filterpat = false; + varnumber_T sid = -1; + + char *pat = NULL; + if (argvars[0].v_type == VAR_DICT) { + dictitem_T *sid_di = tv_dict_find(argvars[0].vval.v_dict, S_LEN("sid")); + if (sid_di != NULL) { + bool error = false; + sid = tv_get_number_chk(&sid_di->di_tv, &error); + if (error) { + return; + } + if (sid <= 0) { + semsg(e_invargNval, "sid", tv_get_string(&sid_di->di_tv)); + return; + } + } else { + pat = tv_dict_get_string(argvars[0].vval.v_dict, "name", true); + if (pat != NULL) { + regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); + } + if (regmatch.regprog != NULL) { + filterpat = true; + } + } + } + + for (varnumber_T i = sid > 0 ? sid : 1; + (i == sid || sid <= 0) && i <= script_items.ga_len; i++) { scriptitem_T *si = SCRIPT_ITEM(i); if (si->sn_name == NULL) { continue; } + if (filterpat && !vim_regexec(®match, si->sn_name, (colnr_T)0)) { + continue; + } + dict_T *d = tv_dict_alloc(); tv_list_append_dict(l, d); tv_dict_add_str(d, S_LEN("name"), si->sn_name); tv_dict_add_nr(d, S_LEN("sid"), i); + tv_dict_add_nr(d, S_LEN("version"), 1); // Vim9 autoload script (:h vim9-autoload), not applicable to Nvim. tv_dict_add_bool(d, S_LEN("autoload"), false); + + // When a script ID is specified, return information about only the + // specified script, and add the script-local variables and functions. + if (sid > 0) { + dict_T *var_dict = tv_dict_copy(NULL, &si->sn_vars->sv_dict, true, get_copyID()); + tv_dict_add_dict(d, S_LEN("variables"), var_dict); + tv_dict_add_list(d, S_LEN("functions"), get_script_local_funcs((scid_T)sid)); + } } + + vim_regfree(regmatch.regprog); + xfree(pat); } /// Get one full line from a sourced file. diff --git a/test/old/testdir/test_scriptnames.vim b/test/old/testdir/test_scriptnames.vim index 06ae305ab7..69e5e526fd 100644 --- a/test/old/testdir/test_scriptnames.vim +++ b/test/old/testdir/test_scriptnames.vim @@ -31,12 +31,80 @@ endfunc " Test for the getscriptinfo() function func Test_getscriptinfo() - call writefile(['let loaded_script_id = expand("<SID>")'], 'Xscript') - source Xscript + let lines =<< trim END + " scriptversion 3 + let g:loaded_script_id = expand("<SID>") + let s:XscriptVar = [1, #{v: 2}] + func s:XgetScriptVar() + return s:XscriptVar + endfunc + func s:Xscript_legacy_func1() + endfunc + " def s:Xscript_def_func1() + " enddef + func Xscript_legacy_func2() + endfunc + " def Xscript_def_func2() + " enddef + END + call writefile(lines, 'X22script91') + source X22script91 let l = getscriptinfo() - call assert_match('Xscript$', l[-1].name) + call assert_match('X22script91$', l[-1].name) call assert_equal(g:loaded_script_id, $"<SNR>{l[-1].sid}_") - call delete('Xscript') + " call assert_equal(3, l[-1].version) + call assert_equal(1, l[-1].version) + call assert_equal(0, has_key(l[-1], 'variables')) + call assert_equal(0, has_key(l[-1], 'functions')) + + " Get script information using script name + let l = getscriptinfo(#{name: '22script91'}) + call assert_equal(1, len(l)) + call assert_match('22script91$', l[0].name) + let sid = l[0].sid + + " Get script information using script-ID + let l = getscriptinfo({'sid': sid}) + call assert_equal(#{XscriptVar: [1, {'v': 2}]}, l[0].variables) + let funcs = ['Xscript_legacy_func2', + \ $"<SNR>{sid}_Xscript_legacy_func1", + "\ $"<SNR>{sid}_Xscript_def_func1", + "\ 'Xscript_def_func2', + \ $"<SNR>{sid}_XgetScriptVar"] + for f in funcs + call assert_true(index(l[0].functions, f) != -1) + endfor + + " Verify that a script-local variable cannot be modified using the dict + " returned by getscriptinfo() + let l[0].variables.XscriptVar = ['n'] + let funcname = $"<SNR>{sid}_XgetScriptVar" + call assert_equal([1, {'v': 2}], call(funcname, [])) + + let l = getscriptinfo({'name': 'foobar'}) + call assert_equal(0, len(l)) + let l = getscriptinfo({'name': ''}) + call assert_true(len(l) > 1) + + call assert_fails("echo getscriptinfo({'name': []})", 'E730:') + call assert_fails("echo getscriptinfo({'name': '\\@'})", 'E866:') + let l = getscriptinfo({'name': v:_null_string}) + call assert_true(len(l) > 1) + call assert_fails("echo getscriptinfo('foobar')", 'E1206:') + + call assert_fails("echo getscriptinfo({'sid': []})", 'E745:') + call assert_fails("echo getscriptinfo({'sid': {}})", 'E728:') + call assert_fails("echo getscriptinfo({'sid': 0})", 'E475:') + call assert_fails("echo getscriptinfo({'sid': -1})", 'E475:') + call assert_fails("echo getscriptinfo({'sid': -999})", 'E475:') + + echo getscriptinfo({'sid': '1'}) + " call assert_fails("vim9cmd echo getscriptinfo({'sid': '1'})", 'E1030:') + + let max_sid = max(map(getscriptinfo(), { k, v -> v.sid })) + call assert_equal([], getscriptinfo({'sid': max_sid + 1})) + + call delete('X22script91') endfunc " vim: shiftwidth=2 sts=2 expandtab |