neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

commit b337c6e0fc0a5c0c2e8125babded24ffcd544796
parent e3913c0fc22f76b116de8f0c6d7a40395755f1d2
Author: Evgeni Chasnovski <evgeni.chasnovski@gmail.com>
Date:   Sat,  9 Aug 2025 15:33:33 +0300

fix(pack): update `add()` to have default `load=false` during startup

Problem: the `load=true` in `vim.pack.add()` means that `:packadd` is
  executed even during startup. This leads to force source of 'plugin/',
  which breaks the intended loading order (`:h load-plugins`) and
  results into sourcing them twice. This also makes it ignore
  `--noplugin` argument.

  Using `:packadd!` during startup is more appropriate, while `:packadd`
  afterwards is still more favorable to actually force 'plugin/' source
  (as there is no pre-defined mechanism that will load them later).

Solution: have `load=false` default during startup, `true` - afterwards.

Diffstat:
Mruntime/doc/pack.txt | 2+-
Mruntime/lua/vim/pack.lua | 6++++--
Mtest/functional/plugin/pack_spec.lua | 41+++++++++++++++++++++++++++++++++++++++++
3 files changed, 46 insertions(+), 3 deletions(-)

diff --git a/runtime/doc/pack.txt b/runtime/doc/pack.txt @@ -336,7 +336,7 @@ add({specs}, {opts}) *vim.pack.add()* • {opts} (`table?`) A table with the following fields: • {load}? (`boolean`) Load `plugin/` files and `ftdetect/` scripts. If `false`, works like `:packadd!`. Default - `true`. + `false` during startup and `true` afterwards. del({names}) *vim.pack.del()* Remove plugins from disk diff --git a/runtime/lua/vim/pack.lua b/runtime/lua/vim/pack.lua @@ -669,7 +669,9 @@ end --- @class vim.pack.keyset.add --- @inlinedoc ---- @field load? boolean Load `plugin/` files and `ftdetect/` scripts. If `false`, works like `:packadd!`. Default `true`. +--- Load `plugin/` files and `ftdetect/` scripts. If `false`, works like `:packadd!`. +--- Default `false` during startup and `true` afterwards. +--- @field load? boolean --- Add plugin to current session --- @@ -693,7 +695,7 @@ end --- @param opts? vim.pack.keyset.add function M.add(specs, opts) vim.validate('specs', specs, vim.islist, false, 'list') - opts = vim.tbl_extend('force', { load = true }, opts or {}) + opts = vim.tbl_extend('force', { load = vim.v.vim_did_enter == 1 }, opts or {}) vim.validate('opts', opts, 'table') --- @type vim.pack.Plug[] diff --git a/test/functional/plugin/pack_spec.lua b/test/functional/plugin/pack_spec.lua @@ -137,10 +137,12 @@ function repos_setup.plugindirs() repo_write_file('plugindirs', 'lua/plugindirs.lua', 'return "plugindirs main"') repo_write_file('plugindirs', 'plugin/dirs.lua', 'vim.g._plugin = true') + repo_write_file('plugindirs', 'plugin/dirs_log.lua', '_G.DL = _G.DL or {}; DL[#DL+1] = "p"') repo_write_file('plugindirs', 'plugin/dirs.vim', 'let g:_plugin_vim=v:true') repo_write_file('plugindirs', 'plugin/sub/dirs.lua', 'vim.g._plugin_sub = true') repo_write_file('plugindirs', 'plugin/bad % name.lua', 'vim.g._plugin_bad = true') repo_write_file('plugindirs', 'after/plugin/dirs.lua', 'vim.g._after_plugin = true') + repo_write_file('plugindirs', 'after/plugin/dirs_log.lua', '_G.DL = _G.DL or {}; DL[#DL+1] = "a"') repo_write_file('plugindirs', 'after/plugin/dirs.vim', 'let g:_after_plugin_vim=v:true') repo_write_file('plugindirs', 'after/plugin/sub/dirs.lua', 'vim.g._after_plugin_sub = true') repo_write_file('plugindirs', 'after/plugin/bad % name.lua', 'vim.g._after_plugin_bad = true') @@ -357,6 +359,45 @@ describe('vim.pack', function() eq(true, exec_lua('return pcall(require, "lspconfig")')) end) + describe('startup', function() + local init_lua = '' + before_each(function() + init_lua = vim.fs.joinpath(fn.stdpath('config'), 'init.lua') + fn.mkdir(vim.fs.dirname(init_lua), 'p') + end) + after_each(function() + pcall(vim.fs.rm, init_lua, { force = true }) + end) + + it('works in init.lua', function() + local pack_add_cmd = ('vim.pack.add({ %s })'):format(vim.inspect(repos_src.plugindirs)) + fn.writefile({ pack_add_cmd, '_G.done = true' }, init_lua) + + local validate_loaded = function() + eq('plugindirs main', exec_lua('return require("plugindirs")')) + + -- Should source 'plugin/' and 'after/plugin/' exactly once + eq({ true, true }, n.exec_lua('return { vim.g._plugin, vim.g._after_plugin }')) + eq({ 'p', 'a' }, n.exec_lua('return _G.DL')) + end + + -- Should auto-install but wait before executing code after it + n.clear({ args_rm = { '-u' } }) + n.exec_lua('vim.wait(500, function() return _G.done end, 50)') + validate_loaded() + + -- Should only `:packadd!` already installed plugin + n.clear({ args_rm = { '-u' } }) + validate_loaded() + + -- Should not load plugins if `--noplugin`, only adjust 'runtimepath' + n.clear({ args = { '--noplugin' }, args_rm = { '-u' } }) + eq('plugindirs main', exec_lua('return require("plugindirs")')) + eq({}, n.exec_lua('return { vim.g._plugin, vim.g._after_plugin }')) + eq(vim.NIL, n.exec_lua('return _G.DL')) + end) + end) + it('shows progress report during installation', function() exec_lua(function() vim.pack.add({ repos_src.basic, repos_src.defbranch })