Merge pull request #23107 from zeertzjq/vim-9.0.0269

vim-patch:9.0.{0269,0303,1431}: more getscriptinfo() features
This commit is contained in:
zeertzjq
2023-04-15 21:27:30 +08:00
committed by GitHub
6 changed files with 188 additions and 21 deletions

View File

@@ -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

View File

@@ -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},

View File

@@ -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"));

View File

@@ -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()

View File

@@ -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(&regmatch, 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.

View File

@@ -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