Bram Moolenaar
unread,Aug 28, 2022, 1:55:14 PM8/28/22Sign in to reply to author
Sign in to forward
You do not have permission to delete messages in this group
Either email addresses are anonymous for this group or you need the view member email addresses permission to view the original message
to vim...@googlegroups.com
Patch 9.0.0303
Problem: It is not easy to get information about a script.
Solution: Make getscriptinf() return the version. When selecting a specific
script return functions and variables. (Yegappan Lakshmanan,
closes #10991)
Files: runtime/doc/builtin.txt, src/scriptfile.c, src/userfunc.c,
src/testdir/test_scriptnames.vim,
src/testdir/test_vim9_builtin.vim,
src/testdir/test_vim9_import.vim
*** ../vim-9.0.0302/runtime/doc/builtin.txt 2022-08-27 12:22:19.975008573 +0100
--- runtime/doc/builtin.txt 2022-08-28 18:40:05.034457308 +0100
***************
*** 4093,4116 ****
scripts in the order they were sourced, like what
`:scriptnames` shows.
Each item in the returned List is a |Dict| with the following
items:
! autoload set to TRUE for a script that was used with
`import autoload` but was not actually sourced
yet (see |import-autoload|).
! name vim script file name.
! sid script ID |<SID>|.
! sourced script ID of the actually sourced script that
this script name links to, if any, otherwise
zero
! version vimscript version (|scriptversion|)
!
! The optional Dict argument {opts} supports the following
! items:
! name script name match pattern. If specified,
! information about scripts with name
! that match the pattern "name" are returned.
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
--- 4099,4140 ----
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 Set to TRUE for a script that was used with
`import autoload` but was not actually sourced
yet (see |import-autoload|).
! 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>|.
! sourced Script ID of the actually sourced script that
this script name links to, if any, otherwise
zero
! 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 (|scriptversion|)
+ 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
*** ../vim-9.0.0302/src/scriptfile.c 2022-08-25 17:39:26.805017714 +0100
--- src/scriptfile.c 2022-08-28 18:44:32.757458421 +0100
***************
*** 1947,1952 ****
--- 1947,1999 ----
}
/*
+ * 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 *functbl;
+ hashitem_T *hi;
+ long_u todo;
+ list_T *l;
+
+ l = list_alloc();
+ if (l == NULL)
+ return NULL;
+
+ // Iterate through all the functions in the global function hash table
+ // looking for functions with script ID 'sid'.
+ functbl = func_tbl_get();
+ todo = functbl->ht_used;
+ for (hi = functbl->ht_array; todo > 0; ++hi)
+ {
+ ufunc_T *fp;
+
+ if (HASHITEM_EMPTY(hi))
+ continue;
+
+ --todo;
+ fp = HI2UF(hi);
+
+ // Add active functions with script id == 'sid'
+ if (!(fp->uf_flags & FC_DEAD) && (fp->uf_script_ctx.sc_sid == sid))
+ {
+ char_u *name;
+
+ if (fp->uf_name_exp != NULL)
+ name = fp->uf_name_exp;
+ else
+ name = fp->uf_name;
+
+ list_append_string(l, name, -1);
+ }
+ }
+
+ return l;
+ }
+
+ /*
* getscriptinfo() function
*/
void
***************
*** 1956,1961 ****
--- 2003,2010 ----
list_T *l;
char_u *pat = NULL;
regmatch_T regmatch;
+ int filterpat = FALSE;
+ scid_T sid = -1;
if (rettv_list_alloc(rettv) == FAIL)
return;
***************
*** 1970,1978 ****
if (argvars[0].v_type == VAR_DICT)
{
! pat = dict_get_string(argvars[0].vval.v_dict, "name", TRUE);
! if (pat != NULL)
! regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
}
for (i = 1; i <= script_items.ga_len; ++i)
--- 2019,2033 ----
if (argvars[0].v_type == VAR_DICT)
{
! sid = dict_get_number_def(argvars[0].vval.v_dict, "sid", -1);
! if (sid == -1)
! {
! pat = 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 (i = 1; i <= script_items.ga_len; ++i)
***************
*** 1983,1990 ****
if (si->sn_name == NULL)
continue;
! if (pat != NULL && regmatch.regprog != NULL
! && !vim_regexec(®match, si->sn_name, (colnr_T)0))
continue;
if ((d = dict_alloc()) == NULL
--- 2038,2047 ----
if (si->sn_name == NULL)
continue;
! if (filterpat && !vim_regexec(®match, si->sn_name, (colnr_T)0))
! continue;
!
! if (sid != -1 && sid != i)
continue;
if ((d = dict_alloc()) == NULL
***************
*** 1996,2001 ****
--- 2053,2074 ----
|| dict_add_bool(d, "autoload",
si->sn_state == SN_STATE_NOT_LOADED) == FAIL)
return;
+
+ // When a filter pattern is specified to return information about only
+ // specific script(s), also add the script-local variables and
+ // functions.
+ if (sid != -1)
+ {
+ dict_T *var_dict;
+
+ var_dict = dict_copy(&si->sn_vars->sv_dict, TRUE, TRUE,
+ get_copyID());
+ if (var_dict == NULL
+ || dict_add_dict(d, "variables", var_dict) == FAIL
+ || dict_add_list(d, "functions",
+ get_script_local_funcs(sid)) == FAIL)
+ return;
+ }
}
vim_regfree(regmatch.regprog);
*** ../vim-9.0.0302/src/userfunc.c 2022-08-18 13:28:27.720128098 +0100
--- src/userfunc.c 2022-08-28 18:29:33.637347610 +0100
***************
*** 40,46 ****
hash_init(&func_hashtab);
}
- #if defined(FEAT_PROFILE) || defined(PROTO)
/*
* Return the function hash table
*/
--- 40,45 ----
***************
*** 49,55 ****
{
return &func_hashtab;
}
- #endif
/*
* Get one function argument.
--- 48,53 ----
*** ../vim-9.0.0302/src/testdir/test_scriptnames.vim 2022-08-25 17:39:26.805017714 +0100
--- src/testdir/test_scriptnames.vim 2022-08-28 18:29:33.633347632 +0100
***************
*** 32,51 ****
" Test for the getscriptinfo() function
func Test_getscriptinfo()
let lines =<< trim END
let g:loaded_script_id = expand("<SID>")
let s:XscriptVar = [1, #{v: 2}]
! func s:XscriptFunc()
endfunc
END
call writefile(lines, 'X22script91')
source X22script91
let l = getscriptinfo()
call assert_match('X22script91$', l[-1].name)
call assert_equal(g:loaded_script_id, $"<SNR>{l[-1].sid}_")
! let l = getscriptinfo({'name': '22script91'})
call assert_equal(1, len(l))
call assert_match('22script91$', l[0].name)
let l = getscriptinfo({'name': 'foobar'})
call assert_equal(0, len(l))
--- 32,84 ----
" Test for the getscriptinfo() function
func Test_getscriptinfo()
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('X22script91$', l[-1].name)
call assert_equal(g:loaded_script_id, $"<SNR>{l[-1].sid}_")
+ call assert_equal(3, 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))
***************
*** 58,63 ****
--- 91,98 ----
call assert_true(len(l) > 1)
call assert_fails("echo getscriptinfo('foobar')", 'E1206:')
+ call assert_fails("echo getscriptinfo({'sid': []})", 'E745:')
+
call delete('X22script91')
endfunc
*** ../vim-9.0.0302/src/testdir/test_vim9_builtin.vim 2022-08-28 17:24:59.775549192 +0100
--- src/testdir/test_vim9_builtin.vim 2022-08-28 18:49:06.220500042 +0100
***************
*** 1898,1903 ****
--- 1898,1943 ----
def Test_getscriptinfo()
v9.CheckDefAndScriptFailure(['getscriptinfo("x")'], ['E1013: Argument 1: type mismatch, expected dict<any> but got string', 'E1206: Dictionary required for argument 1'])
+
+ var lines1 =<< trim END
+ vim9script
+ g:loaded_script_id = expand("<SID>")
+ var XscriptVar = [1, {v: 2}]
+ func XgetScriptVar()
+ return XscriptVar
+ endfunc
+ func Xscript_legacy_func1()
+ endfunc
+ def Xscript_def_func1()
+ enddef
+ func g:Xscript_legacy_func2()
+ endfunc
+ def g:Xscript_def_func2()
+ enddef
+ END
+ writefile(lines1, 'X22script92')
+
+ var lines2 =<< trim END
+ source X22script92
+ var sid = matchstr(g:loaded_script_id, '<SNR>\zs\d\+\ze_')->str2nr()
+
+ var l = getscriptinfo({sid: sid, name: 'ignored'})
+ assert_match('X22script92$', l[0].name)
+ assert_equal(g:loaded_script_id, $"<SNR>{l[0].sid}_")
+ assert_equal(999999, l[0].version)
+ assert_equal(0, l[0].sourced)
+ assert_equal({XscriptVar: [1, {v: 2}]}, l[0].variables)
+ var funcs = ['Xscript_legacy_func2',
+ $"<SNR>{sid}_Xscript_legacy_func1",
+ $"<SNR>{sid}_Xscript_def_func1",
+ 'Xscript_def_func2',
+ $"<SNR>{sid}_XgetScriptVar"]
+ for f in funcs
+ assert_true(index(l[0].functions, f) != -1)
+ endfor
+ END
+ v9.CheckDefAndScriptSuccess(lines2)
+ delete('X22script92')
enddef
def Test_gettabinfo()
*** ../vim-9.0.0302/src/testdir/test_vim9_import.vim 2022-08-25 17:39:26.805017714 +0100
--- src/testdir/test_vim9_import.vim 2022-08-28 18:29:33.633347632 +0100
***************
*** 741,746 ****
--- 741,747 ----
assert_true(len(l) == 1)
assert_match('XrelautoloadExport.vim$', l[0].name)
assert_false(l[0].autoload)
+ assert_equal(999999, l[0].version)
unlet g:result
delete('XrelautoloadExport.vim')
*** ../vim-9.0.0302/src/version.c 2022-08-28 17:59:02.544645487 +0100
--- src/version.c 2022-08-28 18:31:19.952775900 +0100
***************
*** 709,710 ****
--- 709,712 ----
{ /* Add new patch number below this line */
+ /**/
+ 303,
/**/