Patch 9.0.0253
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)
Files:      runtime/doc/builtin.txt, runtime/doc/repeat.txt, src/structs.h,
            src/eval.c, src/evalvars.c, src/vim9script.c,
            src/proto/vim9script.pro, src/vim9compile.c, src/scriptfile.c,
            src/testdir/test_vim9_import.vim


*** ../vim-9.0.0252/runtime/doc/builtin.txt     2022-08-22 13:14:31.892769316 
+0100
--- runtime/doc/builtin.txt     2022-08-24 14:27:57.354222156 +0100
***************
*** 253,259 ****
                                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
  gettabinfo([{expr}])          List    list of tab pages
  gettabvar({nr}, {varname} [, {def}])
                                any     variable {varname} in tab {nr} or {def}
--- 253,259 ----
                                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
  gettabinfo([{expr}])          List    list of tab pages
  gettabvar({nr}, {varname} [, {def}])
                                any     variable {varname} in tab {nr} or {def}
***************
*** 4083,4099 ****
                Can also be used as a |method|: >
                        GetRegname()->getregtype()
  
! getscriptinfo()                                       *getscriptinfo()*
                Returns a |List| with information about all the sourced Vim
!               scripts in the order they were sourced. (|:scriptinfo|)
  
                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.
                    name        vim script file name.
                    sid         script ID |<SID>|.
  
  gettabinfo([{tabnr}])                                 *gettabinfo()*
                If {tabnr} is not specified, then information about all the
--- 4089,4108 ----
                Can also be used as a |method|: >
                        GetRegname()->getregtype()
  
! getscriptinfo()                                               
*getscriptinfo()*
                Returns a |List| with information about all the sourced Vim
!               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     if this script is an alias this is the script
+                               ID of the actually sourced script, otherwise 
zero
  
  gettabinfo([{tabnr}])                                 *gettabinfo()*
                If {tabnr} is not specified, then information about all the
*** ../vim-9.0.0252/runtime/doc/repeat.txt      2022-06-28 11:21:06.000000000 
+0100
--- runtime/doc/repeat.txt      2022-08-24 16:27:02.026973284 +0100
***************
*** 417,422 ****
--- 417,426 ----
                        For a script that was used with `import autoload` but
                        was not actually sourced yet an "A" is shown after the
                        script ID.
+                       For a script that was referred to by one name but
+                       after resolving symbolic links got sourced with
+                       another name the other script is after "->".  E.g.
+                       "20->22" means script 20 was sourced as script 22.
                        {not available when compiled without the |+eval|
                        feature}
  
*** ../vim-9.0.0252/src/structs.h       2022-08-23 18:39:14.756797669 +0100
--- src/structs.h       2022-08-24 13:42:39.564173558 +0100
***************
*** 1850,1862 ****
  #define IMP_FLAGS_AUTOLOAD    4   // script still needs to be loaded
  
  /*
!  * Info about an already sourced scripts.
   */
  typedef struct
  {
      char_u    *sn_name;           // full path of script file
      int               sn_script_seq;      // latest sctx_T sc_seq value
  
      // "sn_vars" stores the s: variables currently valid.  When leaving a 
block
      // variables local to that block are removed.
      scriptvar_T       *sn_vars;
--- 1850,1868 ----
  #define IMP_FLAGS_AUTOLOAD    4   // script still needs to be loaded
  
  /*
!  * Info about an encoutered script.
!  * When sn_state has the SN_STATE_NOT_LOADED is has not been sourced yet.
   */
  typedef struct
  {
      char_u    *sn_name;           // full path of script file
      int               sn_script_seq;      // latest sctx_T sc_seq value
  
+     // When non-zero the script ID of the actually sourced script.  Used if a
+     // script is used by a name which has a symlink, we list both names, but
+     // only the linked-to script is actually sourced.
+     int               sn_sourced_sid;
+ 
      // "sn_vars" stores the s: variables currently valid.  When leaving a 
block
      // variables local to that block are removed.
      scriptvar_T       *sn_vars;
*** ../vim-9.0.0252/src/eval.c  2022-08-18 13:28:27.720128098 +0100
--- src/eval.c  2022-08-24 13:54:43.834196244 +0100
***************
*** 1039,1044 ****
--- 1039,1045 ----
            ufunc_T *ufunc;
            type_T *type;
  
+           import_check_sourced_sid(&import->imp_sid);
            lp->ll_sid = import->imp_sid;
            lp->ll_name = skipwhite(p + 1);
            p = find_name_end(lp->ll_name, NULL, NULL, fne_flags);
*** ../vim-9.0.0252/src/evalvars.c      2022-08-14 14:16:07.987582278 +0100
--- src/evalvars.c      2022-08-24 15:01:11.324428918 +0100
***************
*** 2953,2958 ****
--- 2953,2959 ----
            {
                if (rettv != NULL)
                {
+                   // special value that is used in handle_subscript()
                    rettv->v_type = VAR_ANY;
                    rettv->vval.v_number = sid != 0 ? sid : import->imp_sid;
                }
*** ../vim-9.0.0252/src/vim9script.c    2022-08-06 22:12:59.994474165 +0100
--- src/vim9script.c    2022-08-24 14:00:27.856809188 +0100
***************
*** 697,702 ****
--- 697,716 ----
  }
  
  /*
+  * When a script is a symlink it may be imported with one name and sourced
+  * under another name.  Adjust the import script ID if needed.
+  * "*sid" must be a valid script ID.
+  */
+     void
+ import_check_sourced_sid(int *sid)
+ {
+     scriptitem_T *script = SCRIPT_ITEM(*sid);
+ 
+     if (script->sn_sourced_sid > 0)
+       *sid = script->sn_sourced_sid;
+ }
+ 
+ /*
   * Find an exported item in "sid" matching "name".
   * Either "cctx" or "cstack" is NULL.
   * When it is a variable return the index.
*** ../vim-9.0.0252/src/proto/vim9script.pro    2022-06-27 23:15:30.000000000 
+0100
--- src/proto/vim9script.pro    2022-08-24 14:24:27.406358201 +0100
***************
*** 12,17 ****
--- 12,18 ----
  void free_imports_and_script_vars(int sid);
  void mark_imports_for_reload(int sid);
  void ex_import(exarg_T *eap);
+ void import_check_sourced_sid(int *sid);
  int find_exported(int sid, char_u *name, ufunc_T **ufunc, type_T **type, 
cctx_T *cctx, cstack_T *cstack, int verbose);
  char_u *vim9_declare_scriptvar(exarg_T *eap, char_u *arg);
  void update_vim9_script_var(int create, dictitem_T *di, char_u *name, int 
flags, typval_T *tv, type_T **type, int do_member);
*** ../vim-9.0.0252/src/vim9compile.c   2022-08-17 15:55:47.864544955 +0100
--- src/vim9compile.c   2022-08-24 15:13:46.113786819 +0100
***************
*** 610,616 ****
      ret = find_imported_in_script(name, len, current_sctx.sc_sid);
      if (ret != NULL && load && (ret->imp_flags & IMP_FLAGS_AUTOLOAD))
      {
!       scid_T  dummy;
        int     save_emsg_off = emsg_off;
  
        // "emsg_off" will be set when evaluating an expression silently, but
--- 610,616 ----
      ret = find_imported_in_script(name, len, current_sctx.sc_sid);
      if (ret != NULL && load && (ret->imp_flags & IMP_FLAGS_AUTOLOAD))
      {
!       scid_T  actual_sid = 0;
        int     save_emsg_off = emsg_off;
  
        // "emsg_off" will be set when evaluating an expression silently, but
***************
*** 621,627 ****
        // script found before but not loaded yet
        ret->imp_flags &= ~IMP_FLAGS_AUTOLOAD;
        (void)do_source(SCRIPT_ITEM(ret->imp_sid)->sn_name, FALSE,
!                                                           DOSO_NONE, &dummy);
  
        emsg_off = save_emsg_off;
      }
--- 621,631 ----
        // script found before but not loaded yet
        ret->imp_flags &= ~IMP_FLAGS_AUTOLOAD;
        (void)do_source(SCRIPT_ITEM(ret->imp_sid)->sn_name, FALSE,
!                                                      DOSO_NONE, &actual_sid);
!       // If the script is a symlink it may be sourced with another name, may
!       // need to adjust the script ID for that.
!       if (actual_sid != 0)
!           ret->imp_sid = actual_sid;
  
        emsg_off = save_emsg_off;
      }
*** ../vim-9.0.0252/src/scriptfile.c    2022-08-23 19:26:00.954972237 +0100
--- src/scriptfile.c    2022-08-24 16:20:33.251259001 +0100
***************
*** 1357,1362 ****
--- 1357,1363 ----
  {
      source_cookie_T       cookie;
      char_u                *p;
+     char_u                *fname_not_fixed = NULL;
      char_u                *fname_exp;
      char_u                *firstline = NULL;
      int                           retval = FAIL;
***************
*** 1389,1401 ****
      }
      else
      {
!       p = expand_env_save(fname);
!       if (p == NULL)
!           return retval;
!       fname_exp = fix_fname(p);
!       vim_free(p);
        if (fname_exp == NULL)
!           return retval;
        if (mch_isdir(fname_exp))
        {
            smsg(_("Cannot source a directory: \"%s\""), fname);
--- 1390,1401 ----
      }
      else
      {
!       fname_not_fixed = expand_env_save(fname);
!       if (fname_not_fixed == NULL)
!           goto theend;
!       fname_exp = fix_fname(fname_not_fixed);
        if (fname_exp == NULL)
!           goto theend;
        if (mch_isdir(fname_exp))
        {
            smsg(_("Cannot source a directory: \"%s\""), fname);
***************
*** 1602,1615 ****
        int error = OK;
  
        // It's new, generate a new SID and initialize the scriptitem.
!       current_sctx.sc_sid = get_new_scriptitem(&error);
        if (error == FAIL)
            goto almosttheend;
!       si = SCRIPT_ITEM(current_sctx.sc_sid);
        si->sn_name = fname_exp;
        fname_exp = vim_strsave(si->sn_name);  // used for autocmd
        if (ret_sid != NULL)
!           *ret_sid = current_sctx.sc_sid;
  
        // Remember the "is_vimrc" flag for when the file is sourced again.
        si->sn_is_vimrc = is_vimrc;
--- 1602,1616 ----
        int error = OK;
  
        // It's new, generate a new SID and initialize the scriptitem.
!       sid = get_new_scriptitem(&error);
!       current_sctx.sc_sid = sid;
        if (error == FAIL)
            goto almosttheend;
!       si = SCRIPT_ITEM(sid);
        si->sn_name = fname_exp;
        fname_exp = vim_strsave(si->sn_name);  // used for autocmd
        if (ret_sid != NULL)
!           *ret_sid = sid;
  
        // Remember the "is_vimrc" flag for when the file is sourced again.
        si->sn_is_vimrc = is_vimrc;
***************
*** 1668,1674 ****
      if (do_profiling == PROF_YES)
      {
        // Get "si" again, "script_items" may have been reallocated.
!       si = SCRIPT_ITEM(current_sctx.sc_sid);
        if (si->sn_prof_on)
        {
            profile_end(&si->sn_pr_start);
--- 1669,1675 ----
      if (do_profiling == PROF_YES)
      {
        // Get "si" again, "script_items" may have been reallocated.
!       si = SCRIPT_ITEM(sid);
        if (si->sn_prof_on)
        {
            profile_end(&si->sn_pr_start);
***************
*** 1719,1725 ****
      // If "sn_save_cpo" is set that means we encountered "vim9script": restore
      // 'cpoptions', unless in the main .vimrc file.
      // Get "si" again, "script_items" may have been reallocated.
!     si = SCRIPT_ITEM(current_sctx.sc_sid);
      if (si->sn_save_cpo != NULL && si->sn_is_vimrc == DOSO_NONE)
      {
        if (STRCMP(p_cpo, CPO_VIM) != 0)
--- 1720,1726 ----
      // If "sn_save_cpo" is set that means we encountered "vim9script": restore
      // 'cpoptions', unless in the main .vimrc file.
      // Get "si" again, "script_items" may have been reallocated.
!     si = SCRIPT_ITEM(sid);
      if (si->sn_save_cpo != NULL && si->sn_is_vimrc == DOSO_NONE)
      {
        if (STRCMP(p_cpo, CPO_VIM) != 0)
***************
*** 1774,1779 ****
--- 1775,1793 ----
        apply_autocmds(EVENT_SOURCEPOST, fname_exp, fname_exp, FALSE, curbuf);
  
  theend:
+     if (sid > 0 && ret_sid != NULL
+           && fname_not_fixed != NULL && fname_exp != NULL)
+     {
+       int not_fixed_sid = find_script_by_name(fname_not_fixed);
+ 
+       // If "fname_not_fixed" is a symlink then we source the linked file.
+       // If the original name is in the script list we add the ID of the
+       // script that was actually sourced.
+       if (SCRIPT_ID_VALID(not_fixed_sid) && not_fixed_sid != sid)
+           SCRIPT_ITEM(not_fixed_sid)->sn_sourced_sid = sid;
+     }
+ 
+     vim_free(fname_not_fixed);
      vim_free(fname_exp);
      sticky_cmdmod_flags = save_sticky_cmdmod_flags;
  #ifdef FEAT_EVAL
***************
*** 1787,1793 ****
      char_u    *fname,
      int               check_other,        // check for .vimrc and _vimrc
      int               is_vimrc,           // DOSO_ value
!     int               *ret_sid UNUSED)
  {
      return do_source_ext(fname, check_other, is_vimrc, ret_sid, NULL, FALSE);
  }
--- 1801,1807 ----
      char_u    *fname,
      int               check_other,        // check for .vimrc and _vimrc
      int               is_vimrc,           // DOSO_ value
!     int               *ret_sid)
  {
      return do_source_ext(fname, check_other, is_vimrc, ret_sid, NULL, FALSE);
  }
***************
*** 1828,1836 ****
  
        if (si->sn_name != NULL)
        {
            home_replace(NULL, si->sn_name, NameBuff, MAXPATHL, TRUE);
!           vim_snprintf((char *)IObuff, IOSIZE, "%3d%s: %s",
                    i,
                    si->sn_state == SN_STATE_NOT_LOADED ? " A" : "",
                    NameBuff);
            if (!message_filtered(IObuff))
--- 1842,1857 ----
  
        if (si->sn_name != NULL)
        {
+           char sourced_buf[20];
+ 
            home_replace(NULL, si->sn_name, NameBuff, MAXPATHL, TRUE);
!           if (si->sn_sourced_sid > 0)
!               vim_snprintf(sourced_buf, 20, "->%d", si->sn_sourced_sid);
!           else
!               sourced_buf[0] = NUL;
!           vim_snprintf((char *)IObuff, IOSIZE, "%3d%s%s: %s",
                    i,
+                   sourced_buf,
                    si->sn_state == SN_STATE_NOT_LOADED ? " A" : "",
                    NameBuff);
            if (!message_filtered(IObuff))
***************
*** 1946,1951 ****
--- 1967,1973 ----
                || list_append_dict(l, d) == FAIL
                || dict_add_string(d, "name", si->sn_name) == FAIL
                || dict_add_number(d, "sid", i) == FAIL
+               || dict_add_number(d, "sourced", si->sn_sourced_sid) == FAIL
                || dict_add_bool(d, "autoload",
                                si->sn_state == SN_STATE_NOT_LOADED) == FAIL)
            return;
*** ../vim-9.0.0252/src/testdir/test_vim9_import.vim    2022-08-22 
13:14:31.896769307 +0100
--- src/testdir/test_vim9_import.vim    2022-08-24 16:22:52.723181428 +0100
***************
*** 2906,2910 ****
--- 2906,2955 ----
    v9.CheckScriptFailure(lines, 'E461: Illegal variable name: foo#bar', 2)
  enddef
  
+ def Test_vim9_import_symlink()
+   if !has('unix')
+     CheckUnix
+   else
+     mkdir('Xto/plugin', 'p')
+     var lines =<< trim END
+         vim9script
+         import autoload 'bar.vim'
+         g:resultFunc = bar.Func()
+         g:resultValue = bar.value
+     END
+     writefile(lines, 'Xto/plugin/foo.vim')
+ 
+     mkdir('Xto/autoload', 'p')
+     lines =<< trim END
+         vim9script
+         export def Func(): string
+           return 'func'
+         enddef
+         export var value = 'val'
+     END
+     writefile(lines, 'Xto/autoload/bar.vim')
+ 
+     var save_rtp = &rtp
+     &rtp = getcwd() .. '/Xfrom'
+     system('ln -s ' .. getcwd() .. '/Xto Xfrom')
+ 
+     source Xfrom/plugin/foo.vim
+     assert_equal('func', g:resultFunc)
+     assert_equal('val', g:resultValue)
+ 
+     var infoTo = getscriptinfo()->filter((_, v) => v.name =~ 
'Xto/autoload/bar')
+     var infoFrom = getscriptinfo()->filter((_, v) => v.name =~ 
'Xfrom/autoload/bar')
+     assert_equal(1, len(infoTo))
+     assert_equal(1, len(infoFrom))
+     assert_equal(infoTo[0].sid, infoFrom[0].sourced)
+ 
+     unlet g:resultFunc
+     unlet g:resultValue
+     &rtp = save_rtp
+     delete('Xto', 'rf')
+     delete('Xfrom', 'rf')
+   endif
+ enddef
+ 
  
  " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
*** ../vim-9.0.0252/src/version.c       2022-08-24 12:24:04.299387716 +0100
--- src/version.c       2022-08-24 13:38:00.432376921 +0100
***************
*** 733,734 ****
--- 733,736 ----
  {   /* Add new patch number below this line */
+ /**/
+     253,
  /**/

-- 
BEDEVERE:  Oooooh!
LAUNCELOT: No "Aaaaarrrrrrggghhh ... " at the back of the throat.
BEDEVERE:  No!  "Oooooh!" in surprise and alarm!
                 "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/20220824153122.F35EA1C0D30%40moolenaar.net.

Raspunde prin e-mail lui