fix(pack): install missing plugins asynchronously without startup updates

This commit is contained in:
2026-05-30 16:18:17 +08:00
parent fb057c44f0
commit 8b1b23ff8f
2 changed files with 100 additions and 28 deletions
+32 -25
View File
@@ -1,16 +1,39 @@
local M = {} local M = {}
function M.setup() function M.setup()
local ok_configs, ts_configs = pcall(require, "nvim-treesitter.configs") local ok_ts, ts = pcall(require, "nvim-treesitter")
if not ok_configs then if not ok_ts then
vim.schedule(function() vim.schedule(function()
vim.notify("nvim-treesitter is not available yet. Run :lua vim.pack.update() and restart.", vim.notify("nvim-treesitter is not ready yet. Wait for background plugin install and restart Neovim.",
vim.log.levels.WARN) vim.log.levels.WARN)
end) end)
return return
end end
ts_configs.setup({ local move_config = {
goto_next_start = {
["]f"] = "@function.outer",
["]c"] = "@class.outer",
["]a"] = "@parameter.inner",
},
goto_next_end = {
["]F"] = "@function.outer",
["]C"] = "@class.outer",
["]A"] = "@parameter.inner",
},
goto_previous_start = {
["[f"] = "@function.outer",
["[c"] = "@class.outer",
["[a"] = "@parameter.inner",
},
goto_previous_end = {
["[F"] = "@function.outer",
["[C"] = "@class.outer",
["[A"] = "@parameter.inner",
},
}
ts.setup({
ensure_installed = { ensure_installed = {
"bash", "bash",
"c", "c",
@@ -51,26 +74,10 @@ function M.setup()
textobjects = { textobjects = {
move = { move = {
enable = true, enable = true,
goto_next_start = { goto_next_start = move_config.goto_next_start,
["]f"] = "@function.outer", goto_next_end = move_config.goto_next_end,
["]c"] = "@class.outer", goto_previous_start = move_config.goto_previous_start,
["]a"] = "@parameter.inner", goto_previous_end = move_config.goto_previous_end,
},
goto_next_end = {
["]F"] = "@function.outer",
["]C"] = "@class.outer",
["]A"] = "@parameter.inner",
},
goto_previous_start = {
["[f"] = "@function.outer",
["[c"] = "@class.outer",
["[a"] = "@parameter.inner",
},
goto_previous_end = {
["[F"] = "@function.outer",
["[C"] = "@class.outer",
["[A"] = "@parameter.inner",
},
}, },
}, },
}) })
@@ -84,7 +91,7 @@ function M.setup()
if name:find("goto") == 1 then if name:find("goto") == 1 then
move[name] = function(query, ...) move[name] = function(query, ...)
if vim.wo.diff then if vim.wo.diff then
local config = ts_configs.get_module("textobjects.move")[name] local config = move_config[name]
for key, textobject in pairs(config or {}) do for key, textobject in pairs(config or {}) do
if query == textobject and key:find("[%]%[][cC]") then if query == textobject and key:find("[%]%[][cC]") then
vim.cmd("normal! " .. key) vim.cmd("normal! " .. key)
+68 -3
View File
@@ -1,4 +1,5 @@
local M = {} local M = {}
local async_install_started = false
local function has_valid_head(path) local function has_valid_head(path)
if vim.fn.isdirectory(path .. "/.git") == 0 then if vim.fn.isdirectory(path .. "/.git") == 0 then
@@ -31,6 +32,45 @@ local function to_src(value)
return "https://github.com/" .. value return "https://github.com/" .. value
end end
local function plugin_name(spec)
if type(spec) == "table" and type(spec.name) == "string" and spec.name ~= "" then
return spec.name
end
local src = type(spec) == "string" and spec or spec.src
local name = src:gsub("/$", ""):match("([^/]+)$") or src
return name:gsub("%.git$", "")
end
local function plugin_installed(spec)
local root = vim.fn.stdpath("data") .. "/site/pack/core/opt"
local path = root .. "/" .. plugin_name(spec)
return vim.fn.isdirectory(path) == 1
end
local function install_missing_async(specs)
if async_install_started or #specs == 0 then
return
end
async_install_started = true
local specs_json = vim.json.encode(specs)
local lua_expr = string.format(
"local s=vim.json.decode(%q); vim.pack.add(s,{confirm=false,load=false})",
specs_json
)
vim.system({
vim.v.progpath,
"--headless",
"-u",
"NONE",
"+lua " .. lua_expr,
"+qall",
}, { detach = true })
end
function M.add(specs) function M.add(specs)
local resolved = {} local resolved = {}
@@ -44,17 +84,42 @@ function M.add(specs)
end end
end end
local ok, err = pcall(vim.pack.add, resolved, { confirm = false, load = true }) local installed = {}
local missing = {}
for _, spec in ipairs(resolved) do
if plugin_installed(spec) then
table.insert(installed, spec)
else
table.insert(missing, spec)
end
end
if #missing > 0 then
install_missing_async(missing)
vim.schedule(function()
vim.notify(
"Missing plugins detected. Started background install; restart Neovim when it finishes.",
vim.log.levels.WARN
)
end)
end
if #installed == 0 then
return false
end
local ok, err = pcall(vim.pack.add, installed, { confirm = false, load = true })
if not ok and tostring(err):find("ambiguous argument 'HEAD'", 1, true) then if not ok and tostring(err):find("ambiguous argument 'HEAD'", 1, true) then
prune_broken_pack_repos() prune_broken_pack_repos()
vim.fn.delete(vim.fn.stdpath("config") .. "/nvim-pack-lock.json") vim.fn.delete(vim.fn.stdpath("config") .. "/nvim-pack-lock.json")
ok, err = pcall(vim.pack.add, resolved, { confirm = false, load = true }) ok, err = pcall(vim.pack.add, installed, { confirm = false, load = true })
end end
if not ok then if not ok then
vim.schedule(function() vim.schedule(function()
local msg = "vim.pack failed to install/load plugins. Check network and run :lua vim.pack.update()." local msg = "vim.pack failed to load installed plugins."
vim.notify(msg .. "\n" .. tostring(err), vim.log.levels.WARN) vim.notify(msg .. "\n" .. tostring(err), vim.log.levels.WARN)
end) end)
return false return false