feat(pack): support file:// dev plugins via symlink
This commit is contained in:
+133
-2
@@ -79,6 +79,77 @@ local function is_url(src)
|
|||||||
return src:find("://") ~= nil
|
return src:find("://") ~= nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@param src string
|
||||||
|
---@return string?
|
||||||
|
local function file_url_to_path(src)
|
||||||
|
local path = src:match("^file://(/.+)$")
|
||||||
|
if not path then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
return vim.fs.normalize(path)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param path string
|
||||||
|
---@return string?
|
||||||
|
local function plugin_name_from_path(path)
|
||||||
|
local name = vim.fs.basename(path)
|
||||||
|
if not name or name == "" or name == "." or name == ".." then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
return name
|
||||||
|
end
|
||||||
|
|
||||||
|
local data_dir = vim.fn.stdpath("data")
|
||||||
|
if type(data_dir) == "table" then
|
||||||
|
data_dir = assert(data_dir[1])
|
||||||
|
end
|
||||||
|
local dev_opt_dir = vim.fs.joinpath(data_dir, "site", "pack", "dev", "opt")
|
||||||
|
|
||||||
|
---@param target string
|
||||||
|
---@param name string
|
||||||
|
---@return string? link
|
||||||
|
---@return boolean changed
|
||||||
|
local function ensure_dev_link(target, name)
|
||||||
|
if not vim.uv.fs_stat(target) then
|
||||||
|
log.error("pack: dev plugin path does not exist: %s", target)
|
||||||
|
return nil, false
|
||||||
|
end
|
||||||
|
|
||||||
|
local ok, info = pcall(vim.pack.get, { name })
|
||||||
|
if ok and info and #info > 0 then
|
||||||
|
pcall(vim.pack.del, { name }, { force = true })
|
||||||
|
end
|
||||||
|
|
||||||
|
vim.fn.mkdir(dev_opt_dir, "p")
|
||||||
|
local link = vim.fs.joinpath(dev_opt_dir, name)
|
||||||
|
local lstat = vim.uv.fs_lstat(link)
|
||||||
|
if lstat then
|
||||||
|
if lstat.type == "link" then
|
||||||
|
if vim.uv.fs_readlink(link) == target then
|
||||||
|
return link, false
|
||||||
|
end
|
||||||
|
local ok_unlink, err = vim.uv.fs_unlink(link)
|
||||||
|
if not ok_unlink then
|
||||||
|
log.error("pack: failed to unlink %s: %s", link, err)
|
||||||
|
return nil, false
|
||||||
|
end
|
||||||
|
else
|
||||||
|
log.error(
|
||||||
|
"pack: %s exists and is not a symlink; refusing to overwrite",
|
||||||
|
link
|
||||||
|
)
|
||||||
|
return nil, false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local ok_link, err = vim.uv.fs_symlink(target, link)
|
||||||
|
if not ok_link then
|
||||||
|
log.error("pack: failed to symlink %s -> %s: %s", link, target, err)
|
||||||
|
return nil, false
|
||||||
|
end
|
||||||
|
return link, true
|
||||||
|
end
|
||||||
|
|
||||||
---@param spec string | ow.Pack.Spec
|
---@param spec string | ow.Pack.Spec
|
||||||
---@return vim.pack.Spec
|
---@return vim.pack.Spec
|
||||||
local function to_pack_spec(spec)
|
local function to_pack_spec(spec)
|
||||||
@@ -267,13 +338,49 @@ end
|
|||||||
function M.setup(specs)
|
function M.setup(specs)
|
||||||
local pack_specs = {}
|
local pack_specs = {}
|
||||||
local order = {}
|
local order = {}
|
||||||
|
local dev_changed = {}
|
||||||
for _, spec in ipairs(specs) do
|
for _, spec in ipairs(specs) do
|
||||||
|
local spec_t = type(spec) == "table" and spec or nil
|
||||||
local src = type(spec) == "string" and spec or spec.src
|
local src = type(spec) == "string" and spec or spec.src
|
||||||
table.insert(order, src)
|
table.insert(order, src)
|
||||||
if is_url(src) then
|
local dev_path = file_url_to_path(src)
|
||||||
|
if dev_path then
|
||||||
|
local name = (spec_t and spec_t.name)
|
||||||
|
or plugin_name_from_path(dev_path)
|
||||||
|
if not name then
|
||||||
|
log.error("pack: invalid plugin name derived from %s", src)
|
||||||
|
else
|
||||||
|
local link, did_change = ensure_dev_link(dev_path, name)
|
||||||
|
if link then
|
||||||
|
local ok_add, add_err = pcall(vim.cmd.packadd, name)
|
||||||
|
if not ok_add then
|
||||||
|
log.error(
|
||||||
|
"pack: failed to packadd %s: %s",
|
||||||
|
name,
|
||||||
|
add_err
|
||||||
|
)
|
||||||
|
else
|
||||||
|
---@type ow.Pack.Plugin
|
||||||
|
M.plugins[src] = {
|
||||||
|
src = src,
|
||||||
|
name = name,
|
||||||
|
version = spec_t and spec_t.version,
|
||||||
|
build = spec_t and spec_t.build,
|
||||||
|
path = link,
|
||||||
|
}
|
||||||
|
if did_change then
|
||||||
|
dev_changed[src] = { path = link }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif is_url(src) then
|
||||||
table.insert(pack_specs, to_pack_spec(spec))
|
table.insert(pack_specs, to_pack_spec(spec))
|
||||||
else
|
else
|
||||||
vim.cmd.packadd(src)
|
local ok_add, add_err = pcall(vim.cmd.packadd, src)
|
||||||
|
if not ok_add then
|
||||||
|
log.error("pack: failed to packadd %s: %s", src, add_err)
|
||||||
|
else
|
||||||
local runtime =
|
local runtime =
|
||||||
vim.api.nvim_get_runtime_file("pack/*/opt/" .. src, false)
|
vim.api.nvim_get_runtime_file("pack/*/opt/" .. src, false)
|
||||||
---@type ow.Pack.Plugin
|
---@type ow.Pack.Plugin
|
||||||
@@ -285,6 +392,7 @@ function M.setup(specs)
|
|||||||
M.plugins[plugin.src] = plugin
|
M.plugins[plugin.src] = plugin
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local changed = M.install(pack_specs, function(data)
|
local changed = M.install(pack_specs, function(data)
|
||||||
if not data.spec.name then
|
if not data.spec.name then
|
||||||
@@ -303,6 +411,9 @@ function M.setup(specs)
|
|||||||
M.plugins[plugin.src] = plugin
|
M.plugins[plugin.src] = plugin
|
||||||
vim.cmd.packadd(plugin.name)
|
vim.cmd.packadd(plugin.name)
|
||||||
end)
|
end)
|
||||||
|
for src, data in pairs(dev_changed) do
|
||||||
|
changed[src] = data
|
||||||
|
end
|
||||||
|
|
||||||
for _, src in ipairs(order) do
|
for _, src in ipairs(order) do
|
||||||
local plugin = M.plugins[src]
|
local plugin = M.plugins[src]
|
||||||
@@ -343,6 +454,26 @@ end
|
|||||||
---@param names? string[]
|
---@param names? string[]
|
||||||
---@param opts? table
|
---@param opts? table
|
||||||
function M.update(names, opts)
|
function M.update(names, opts)
|
||||||
|
if names then
|
||||||
|
local managed = {}
|
||||||
|
for _, plugin in pairs(M.plugins) do
|
||||||
|
if not file_url_to_path(plugin.src) and is_url(plugin.src) then
|
||||||
|
managed[plugin.name] = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local filtered = {}
|
||||||
|
for _, name in ipairs(names) do
|
||||||
|
if managed[name] then
|
||||||
|
table.insert(filtered, name)
|
||||||
|
else
|
||||||
|
log.warning("pack: skipping %s (not managed by vim.pack)", name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if #filtered == 0 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
names = filtered
|
||||||
|
end
|
||||||
vim.pack.update(names, opts)
|
vim.pack.update(names, opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user