]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.2.0703: session file does not store relative Vim9 autoload imports v9.2.0703
authorMiguel Barro <miguel.barro@live.com>
Mon, 22 Jun 2026 19:19:24 +0000 (19:19 +0000)
committerChristian Brabandt <cb@256bit.org>
Mon, 22 Jun 2026 19:19:24 +0000 (19:19 +0000)
Problem:  mksession misses relative or absolute imports in the session,
          homonymous autoload scripts imported from different scripts
          cause errors (after v9.2.0579).
Solution: Correctly write scripts imported via :import autoload, skip any
          that are no longer readable, and comment out imports whose autoload
          prefix or file name tail would conflict with an earlier one
          (Miguel Barro).

fixes:  #12641
closes: #20564

Signed-off-by: Miguel Barro <miguel.barro@live.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
src/session.c
src/testdir/test_mksession.vim
src/version.c

index 090448937fec6bd2797fa13741a71b09c42167e0..0fa03e556ded9bb0f31d8d121c574185b69ed5eb 100644 (file)
@@ -1333,10 +1333,42 @@ ex_mkrc(exarg_T *eap)
            for (sid = 1; sid <= script_items.ga_len; ++sid)
            {
                si = SCRIPT_ITEM(sid);
-               if (si->sn_autoload_prefix &&
-                   (fprintf(fd, "import autoload '%s'", si->sn_name) < 0 ||
-                       put_eol(fd) == FAIL))
+
+               // Autoload script paths may be absolute, relative to the
+               // current script or relative to a 'runtimepath' directory
+               // Ignore if missing
+               if ((si->sn_autoload_prefix || si->sn_import_autoload)
+                       && file_is_readable(si->sn_name))
+               {
+                   // Check if conflicts with a previous import
+                   int b_sid = sid - 1;
+                   char_u *name = gettail(si->sn_name);
+
+                   for (; b_sid; --b_sid)
+                   {
+                       scriptitem_T *b_si = SCRIPT_ITEM(b_sid);
+
+                       // Only autoload may conflict. Ignore if missing
+                       if ((!b_si->sn_autoload_prefix && !b_si->sn_import_autoload)
+                               || !file_is_readable(b_si->sn_name))
+                           continue;
+
+                       // compare prefixes if available
+                       if (si->sn_autoload_prefix != NULL && b_si->sn_autoload_prefix != NULL
+                               && (STRCMP(si->sn_autoload_prefix, b_si->sn_autoload_prefix) == 0))
+                           break;
+
+                       // otherwise compare tails
+                       char_u *b_name = gettail(b_si->sn_name);
+                       if (STRCMP(name, b_name) == 0)
+                           break;
+                   }
+
+                   // import the auto script if there are no conflicts
+                   if (fprintf(fd, "%simport autoload '%s'", b_sid ? "# " : "", si->sn_name) < 0 ||
+                       put_eol(fd) == FAIL)
                    failed = TRUE;
+               }
            }
 #endif
        }
index 4dec8815a43525f7107a29fab741371fe102a68a..0bccad1c6662a126718b2eeb06fb0578544f6c5c 100644 (file)
@@ -1598,4 +1598,128 @@ func Test_mksession_localmappings()
 
 endfunc
 
+" Test vim9 script relative auto imports (issue #12641)
+func Test_mksession_vim9_relative_auto_import()
+
+  " Dummy vim9 script
+  let script_sources =<< trim END
+    vim9script
+    import autoload './XAuto.vim'
+    nnoremap dummy-test <ScriptCmd>XAuto.Test()<CR>
+  END
+  call writefile(script_sources, 'XScript.vim', 'D')
+
+  let auto_sources =<< trim END
+    vim9script
+    const ref_txt = 'Hello from vim9 dummy relative auto script!'
+    export def Test()
+      if !has("gui_running")
+        exe $"echomsg '{ref_txt}'"
+      endif
+      writefile([ref_txt], 'XDummyOutput')
+    enddef
+  END
+  call writefile(auto_sources, 'XAuto.vim', 'D')
+
+  " Source the script
+  const ref_txt = 'Hello from vim9 dummy relative auto script!'
+  source XScript.vim
+
+  " Execute mapping
+  normal dummy-test
+
+  if !has('gui_running')
+    call assert_match(ref_txt, execute('messages'), 'No vim9 auto script XAuto.Test() execution')
+  endif
+  call assert_true(filereadable('XDummyOutput'), 'Output file was not created by Vim9 auto script')
+  call assert_equal([ref_txt], readfile('XDummyOutput'))
+  call delete('XDummyOutput')
+
+  " Create a session file
+  mksession! XDummySession.vim
+  defer delete('XDummySession.vim')
+  call assert_true(filereadable('XDummySession.vim'), 'Session file was not created')
+
+  " Check the session file mappings are operational
+  let test_sources =<< trim END
+    " Load session
+    source XDummySession.vim
+    " Execute mapping
+    normal dummy-test
+    " on my way
+    cq
+  END
+  call writefile(test_sources, 'XTest.vim', 'D')
+  " spawn a new Vim instance to load the session and execute the mapping
+  call system(GetVimCommand('XTest.vim'))
+  call assert_true(filereadable('XDummyOutput'),
+        \ 'Expected output file was not created by Vim9 auto script mapping')
+  defer delete('XDummyOutput')
+  call assert_equal([ref_txt], readfile('XDummyOutput'))
+
+endfunc
+
+" Test vim9 script avoid homonimous auto imports
+func Test_mksession_vim9_duplicate_import()
+
+  " Auto script
+  let auto_sources =<< trim END
+    vim9script
+    const ref_txt = 'Hello from a duplicated vim9 script!'
+    export def Test()
+      writefile([ref_txt], 'XDummyOutput')
+    enddef
+  END
+
+  " Dummy vim9 script
+  let script_sources =<< trim END
+    vim9script
+    import autoload './XAuto.vim'
+    nnoremap dummy-test <ScriptCmd>XAuto.Test()<CR>
+  END
+
+  for i in range(1, 5)
+    let dir = $'XDir{i}'
+    call mkdir(dir, 'p')
+    defer delete(dir, 'rf')
+
+    let autofile = dir . '/XAuto.vim'
+    call writefile(auto_sources, autofile, 'D')
+
+    let scriptfile = dir . '/XScript.vim'
+    call writefile(script_sources, scriptfile, 'D')
+    exe "source " . scriptfile
+  endfor
+
+  " Create a session file
+  mksession! XDummySession.vim
+  defer delete('XDummySession.vim')
+  call assert_true(filereadable('XDummySession.vim'), 'Session file was not created')
+
+  " Check there are commented imports in the session file
+  let commented_imports = filter(readfile('XDummySession.vim'),
+  \ {_, v -> v =~ '^# import autoload'})
+  call assert_equal(4, len(commented_imports),
+  \ 'Session file does not contain the expected number of commented imports')
+
+  " Check the session file mappings are operational
+  const ref_txt = 'Hello from a duplicated vim9 script!'
+  let test_sources =<< trim END
+    " Load session
+    source XDummySession.vim
+    " Execute mapping
+    normal dummy-test
+    " on my way
+    cq
+  END
+  call writefile(test_sources, 'XTest.vim', 'D')
+  " spawn a new Vim instance to load the session and execute the mapping
+  call system(GetVimCommand('XTest.vim'))
+  call assert_true(filereadable('XDummyOutput'),
+        \ 'Expected output file was not created by Vim9 auto script mapping')
+  defer delete('XDummyOutput')
+  call assert_equal([ref_txt], readfile('XDummyOutput'))
+
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
index 67a0fa650882ff7eeb2291a949429fa84bb400bd..45b222e7ad8867620f0926c8b9a1125b6df0e689 100644 (file)
@@ -759,6 +759,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    703,
 /**/
     702,
 /**/