Skip to content

Commit 753885b

Browse files
committed
patch 9.0.0253: a symlink to an autoload script results in two entries
Problem: A symlink to an autoload script results in two entries in the list of scripts, items expected in one are actually in the other. Solution: Have one script item refer to the actually sourced one. (closes #10960)
1 parent f5240b9 commit 753885b

File tree

11 files changed

+124
-21
lines changed

11 files changed

+124
-21
lines changed

runtime/doc/builtin.txt

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ getreg([{regname} [, 1 [, {list}]]])
253253
String or List contents of a register
254254
getreginfo([{regname}]) Dict information about a register
255255
getregtype([{regname}]) String type of a register
256-
getscriptinfo() List list of sourced scripts
256+
getscriptinfo() List list of sourced scripts
257257
gettabinfo([{expr}]) List list of tab pages
258258
gettabvar({nr}, {varname} [, {def}])
259259
any variable {varname} in tab {nr} or {def}
@@ -4089,17 +4089,20 @@ getregtype([{regname}]) *getregtype()*
40894089
Can also be used as a |method|: >
40904090
GetRegname()->getregtype()
40914091
4092-
getscriptinfo() *getscriptinfo()*
4092+
getscriptinfo() *getscriptinfo()*
40934093
Returns a |List| with information about all the sourced Vim
4094-
scripts in the order they were sourced. (|:scriptinfo|)
4094+
scripts in the order they were sourced, like what
4095+
`:scriptnames` shows.
40954096

40964097
Each item in the returned List is a |Dict| with the following
40974098
items:
40984099
autoload set to TRUE for a script that was used with
4099-
|import autoload| but was not actually sourced
4100-
yet.
4100+
`import autoload` but was not actually sourced
4101+
yet (see |import-autoload|).
41014102
name vim script file name.
41024103
sid script ID |<SID>|.
4104+
sourced if this script is an alias this is the script
4105+
ID of the actually sourced script, otherwise zero
41034106

41044107
gettabinfo([{tabnr}]) *gettabinfo()*
41054108
If {tabnr} is not specified, then information about all the

runtime/doc/repeat.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,10 @@ For writing a Vim script, see chapter 41 of the user manual |usr_41.txt|.
417417
For a script that was used with `import autoload` but
418418
was not actually sourced yet an "A" is shown after the
419419
script ID.
420+
For a script that was referred to by one name but
421+
after resolving symbolic links got sourced with
422+
another name the other script is after "->". E.g.
423+
"20->22" means script 20 was sourced as script 22.
420424
{not available when compiled without the |+eval|
421425
feature}
422426

src/eval.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1039,6 +1039,7 @@ get_lval(
10391039
ufunc_T *ufunc;
10401040
type_T *type;
10411041

1042+
import_check_sourced_sid(&import->imp_sid);
10421043
lp->ll_sid = import->imp_sid;
10431044
lp->ll_name = skipwhite(p + 1);
10441045
p = find_name_end(lp->ll_name, NULL, NULL, fne_flags);

src/evalvars.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2953,6 +2953,7 @@ eval_variable(
29532953
{
29542954
if (rettv != NULL)
29552955
{
2956+
// special value that is used in handle_subscript()
29562957
rettv->v_type = VAR_ANY;
29572958
rettv->vval.v_number = sid != 0 ? sid : import->imp_sid;
29582959
}

src/proto/vim9script.pro

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ void ex_export(exarg_T *eap);
1212
void free_imports_and_script_vars(int sid);
1313
void mark_imports_for_reload(int sid);
1414
void ex_import(exarg_T *eap);
15+
void import_check_sourced_sid(int *sid);
1516
int find_exported(int sid, char_u *name, ufunc_T **ufunc, type_T **type, cctx_T *cctx, cstack_T *cstack, int verbose);
1617
char_u *vim9_declare_scriptvar(exarg_T *eap, char_u *arg);
1718
void update_vim9_script_var(int create, dictitem_T *di, char_u *name, int flags, typval_T *tv, type_T **type, int do_member);

src/scriptfile.c

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1357,6 +1357,7 @@ do_source_ext(
13571357
{
13581358
source_cookie_T cookie;
13591359
char_u *p;
1360+
char_u *fname_not_fixed = NULL;
13601361
char_u *fname_exp;
13611362
char_u *firstline = NULL;
13621363
int retval = FAIL;
@@ -1389,13 +1390,12 @@ do_source_ext(
13891390
}
13901391
else
13911392
{
1392-
p = expand_env_save(fname);
1393-
if (p == NULL)
1394-
return retval;
1395-
fname_exp = fix_fname(p);
1396-
vim_free(p);
1393+
fname_not_fixed = expand_env_save(fname);
1394+
if (fname_not_fixed == NULL)
1395+
goto theend;
1396+
fname_exp = fix_fname(fname_not_fixed);
13971397
if (fname_exp == NULL)
1398-
return retval;
1398+
goto theend;
13991399
if (mch_isdir(fname_exp))
14001400
{
14011401
smsg(_("Cannot source a directory: \"%s\""), fname);
@@ -1602,14 +1602,15 @@ do_source_ext(
16021602
int error = OK;
16031603

16041604
// It's new, generate a new SID and initialize the scriptitem.
1605-
current_sctx.sc_sid = get_new_scriptitem(&error);
1605+
sid = get_new_scriptitem(&error);
1606+
current_sctx.sc_sid = sid;
16061607
if (error == FAIL)
16071608
goto almosttheend;
1608-
si = SCRIPT_ITEM(current_sctx.sc_sid);
1609+
si = SCRIPT_ITEM(sid);
16091610
si->sn_name = fname_exp;
16101611
fname_exp = vim_strsave(si->sn_name); // used for autocmd
16111612
if (ret_sid != NULL)
1612-
*ret_sid = current_sctx.sc_sid;
1613+
*ret_sid = sid;
16131614

16141615
// Remember the "is_vimrc" flag for when the file is sourced again.
16151616
si->sn_is_vimrc = is_vimrc;
@@ -1668,7 +1669,7 @@ do_source_ext(
16681669
if (do_profiling == PROF_YES)
16691670
{
16701671
// Get "si" again, "script_items" may have been reallocated.
1671-
si = SCRIPT_ITEM(current_sctx.sc_sid);
1672+
si = SCRIPT_ITEM(sid);
16721673
if (si->sn_prof_on)
16731674
{
16741675
profile_end(&si->sn_pr_start);
@@ -1719,7 +1720,7 @@ do_source_ext(
17191720
// If "sn_save_cpo" is set that means we encountered "vim9script": restore
17201721
// 'cpoptions', unless in the main .vimrc file.
17211722
// Get "si" again, "script_items" may have been reallocated.
1722-
si = SCRIPT_ITEM(current_sctx.sc_sid);
1723+
si = SCRIPT_ITEM(sid);
17231724
if (si->sn_save_cpo != NULL && si->sn_is_vimrc == DOSO_NONE)
17241725
{
17251726
if (STRCMP(p_cpo, CPO_VIM) != 0)
@@ -1774,6 +1775,19 @@ do_source_ext(
17741775
apply_autocmds(EVENT_SOURCEPOST, fname_exp, fname_exp, FALSE, curbuf);
17751776

17761777
theend:
1778+
if (sid > 0 && ret_sid != NULL
1779+
&& fname_not_fixed != NULL && fname_exp != NULL)
1780+
{
1781+
int not_fixed_sid = find_script_by_name(fname_not_fixed);
1782+
1783+
// If "fname_not_fixed" is a symlink then we source the linked file.
1784+
// If the original name is in the script list we add the ID of the
1785+
// script that was actually sourced.
1786+
if (SCRIPT_ID_VALID(not_fixed_sid) && not_fixed_sid != sid)
1787+
SCRIPT_ITEM(not_fixed_sid)->sn_sourced_sid = sid;
1788+
}
1789+
1790+
vim_free(fname_not_fixed);
17771791
vim_free(fname_exp);
17781792
sticky_cmdmod_flags = save_sticky_cmdmod_flags;
17791793
#ifdef FEAT_EVAL
@@ -1787,7 +1801,7 @@ do_source(
17871801
char_u *fname,
17881802
int check_other, // check for .vimrc and _vimrc
17891803
int is_vimrc, // DOSO_ value
1790-
int *ret_sid UNUSED)
1804+
int *ret_sid)
17911805
{
17921806
return do_source_ext(fname, check_other, is_vimrc, ret_sid, NULL, FALSE);
17931807
}
@@ -1828,9 +1842,16 @@ ex_scriptnames(exarg_T *eap)
18281842

18291843
if (si->sn_name != NULL)
18301844
{
1845+
char sourced_buf[20];
1846+
18311847
home_replace(NULL, si->sn_name, NameBuff, MAXPATHL, TRUE);
1832-
vim_snprintf((char *)IObuff, IOSIZE, "%3d%s: %s",
1848+
if (si->sn_sourced_sid > 0)
1849+
vim_snprintf(sourced_buf, 20, "->%d", si->sn_sourced_sid);
1850+
else
1851+
sourced_buf[0] = NUL;
1852+
vim_snprintf((char *)IObuff, IOSIZE, "%3d%s%s: %s",
18331853
i,
1854+
sourced_buf,
18341855
si->sn_state == SN_STATE_NOT_LOADED ? " A" : "",
18351856
NameBuff);
18361857
if (!message_filtered(IObuff))
@@ -1946,6 +1967,7 @@ f_getscriptinfo(typval_T *argvars UNUSED, typval_T *rettv)
19461967
|| list_append_dict(l, d) == FAIL
19471968
|| dict_add_string(d, "name", si->sn_name) == FAIL
19481969
|| dict_add_number(d, "sid", i) == FAIL
1970+
|| dict_add_number(d, "sourced", si->sn_sourced_sid) == FAIL
19491971
|| dict_add_bool(d, "autoload",
19501972
si->sn_state == SN_STATE_NOT_LOADED) == FAIL)
19511973
return;

src/structs.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1850,13 +1850,19 @@ typedef struct {
18501850
#define IMP_FLAGS_AUTOLOAD 4 // script still needs to be loaded
18511851

18521852
/*
1853-
* Info about an already sourced scripts.
1853+
* Info about an encoutered script.
1854+
* When sn_state has the SN_STATE_NOT_LOADED is has not been sourced yet.
18541855
*/
18551856
typedef struct
18561857
{
18571858
char_u *sn_name; // full path of script file
18581859
int sn_script_seq; // latest sctx_T sc_seq value
18591860

1861+
// When non-zero the script ID of the actually sourced script. Used if a
1862+
// script is used by a name which has a symlink, we list both names, but
1863+
// only the linked-to script is actually sourced.
1864+
int sn_sourced_sid;
1865+
18601866
// "sn_vars" stores the s: variables currently valid. When leaving a block
18611867
// variables local to that block are removed.
18621868
scriptvar_T *sn_vars;

src/testdir/test_vim9_import.vim

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2906,5 +2906,50 @@ def Test_vim9_autoload_error()
29062906
v9.CheckScriptFailure(lines, 'E461: Illegal variable name: foo#bar', 2)
29072907
enddef
29082908

2909+
def Test_vim9_import_symlink()
2910+
if !has('unix')
2911+
CheckUnix
2912+
else
2913+
mkdir('Xto/plugin', 'p')
2914+
var lines =<< trim END
2915+
vim9script
2916+
import autoload 'bar.vim'
2917+
g:resultFunc = bar.Func()
2918+
g:resultValue = bar.value
2919+
END
2920+
writefile(lines, 'Xto/plugin/foo.vim')
2921+
2922+
mkdir('Xto/autoload', 'p')
2923+
lines =<< trim END
2924+
vim9script
2925+
export def Func(): string
2926+
return 'func'
2927+
enddef
2928+
export var value = 'val'
2929+
END
2930+
writefile(lines, 'Xto/autoload/bar.vim')
2931+
2932+
var save_rtp = &rtp
2933+
&rtp = getcwd() .. '/Xfrom'
2934+
system('ln -s ' .. getcwd() .. '/Xto Xfrom')
2935+
2936+
source Xfrom/plugin/foo.vim
2937+
assert_equal('func', g:resultFunc)
2938+
assert_equal('val', g:resultValue)
2939+
2940+
var infoTo = getscriptinfo()->filter((_, v) => v.name =~ 'Xto/autoload/bar')
2941+
var infoFrom = getscriptinfo()->filter((_, v) => v.name =~ 'Xfrom/autoload/bar')
2942+
assert_equal(1, len(infoTo))
2943+
assert_equal(1, len(infoFrom))
2944+
assert_equal(infoTo[0].sid, infoFrom[0].sourced)
2945+
2946+
unlet g:resultFunc
2947+
unlet g:resultValue
2948+
&rtp = save_rtp
2949+
delete('Xto', 'rf')
2950+
delete('Xfrom', 'rf')
2951+
endif
2952+
enddef
2953+
29092954

29102955
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,8 @@ static char *(features[]) =
731731

732732
static int included_patches[] =
733733
{ /* Add new patch number below this line */
734+
/**/
735+
253,
734736
/**/
735737
252,
736738
/**/

src/vim9compile.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -610,7 +610,7 @@ find_imported(char_u *name, size_t len, int load)
610610
ret = find_imported_in_script(name, len, current_sctx.sc_sid);
611611
if (ret != NULL && load && (ret->imp_flags & IMP_FLAGS_AUTOLOAD))
612612
{
613-
scid_T dummy;
613+
scid_T actual_sid = 0;
614614
int save_emsg_off = emsg_off;
615615

616616
// "emsg_off" will be set when evaluating an expression silently, but
@@ -621,7 +621,11 @@ find_imported(char_u *name, size_t len, int load)
621621
// script found before but not loaded yet
622622
ret->imp_flags &= ~IMP_FLAGS_AUTOLOAD;
623623
(void)do_source(SCRIPT_ITEM(ret->imp_sid)->sn_name, FALSE,
624-
DOSO_NONE, &dummy);
624+
DOSO_NONE, &actual_sid);
625+
// If the script is a symlink it may be sourced with another name, may
626+
// need to adjust the script ID for that.
627+
if (actual_sid != 0)
628+
ret->imp_sid = actual_sid;
625629

626630
emsg_off = save_emsg_off;
627631
}

0 commit comments

Comments
 (0)