more fixes
This commit is contained in:
153
lua/dep.lua
153
lua/dep.lua
@ -2,15 +2,10 @@ local logger = require('dep.log')
|
||||
local git = require('dep.git')
|
||||
local packager = require('dep.package')
|
||||
|
||||
---all functions for convenience
|
||||
---@type table
|
||||
-- all functions for convenience
|
||||
local M = {}
|
||||
|
||||
---@type boolean
|
||||
local initialized
|
||||
|
||||
---performance logging
|
||||
---@type table
|
||||
-- performance logging
|
||||
local perf = {}
|
||||
|
||||
--- get execution time of a function
|
||||
@ -30,13 +25,13 @@ function M.registertree(speclist, overrides)
|
||||
overrides = overrides or {}
|
||||
|
||||
-- recurse the packages
|
||||
local over = overrides
|
||||
for _, spec in pairs(speclist) do
|
||||
|
||||
-- make sure the overrides override and take into account the packages spec
|
||||
---@diagnostic disable-next-line: missing-fields
|
||||
overrides = {
|
||||
pin = overrides.pin or spec.pin,
|
||||
disable = overrides.disable or spec.disable
|
||||
over = {
|
||||
pin = over.pin or spec.pin,
|
||||
disable = over.disable or spec.disable
|
||||
}
|
||||
|
||||
local ok = packager:new(spec, overrides)
|
||||
@ -48,8 +43,48 @@ function M.registertree(speclist, overrides)
|
||||
end
|
||||
end
|
||||
|
||||
--- clean out old packages
|
||||
function M.clean()
|
||||
vim.loop.fs_scandir(
|
||||
packager.get_base_dir(),
|
||||
vim.schedule_wrap(function(err, handle)
|
||||
if err then
|
||||
logger:log("error", string.format("failed to clean; reason: %s", err))
|
||||
else
|
||||
local queue = {}
|
||||
|
||||
while handle do
|
||||
local name = vim.loop.fs_scandir_next(handle)
|
||||
if name then
|
||||
queue[name] = packager.get_base_dir()..name
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
-- keep packages that still exist
|
||||
for _, package in pairs(packager.get_packages()) do
|
||||
queue[package.name] = nil
|
||||
end
|
||||
|
||||
for name, dir in pairs(queue) do
|
||||
local co = coroutine.create(function()
|
||||
local ok = vim.fn.delete(dir, "rf")
|
||||
if ok then
|
||||
logger:log("clean", string.format("deleted %s", name))
|
||||
else
|
||||
logger:log("error", string.format("failed to delete %s", name))
|
||||
end
|
||||
end)
|
||||
coroutine.resume(co)
|
||||
end
|
||||
end
|
||||
end)
|
||||
)
|
||||
end
|
||||
|
||||
--- reload all packages in package table spec
|
||||
---@param force boolean|nil force all packages to load
|
||||
---@param force boolean? force all packages to load
|
||||
function M.reload(force)
|
||||
local reloaded = packager.get_root():loadtree(force)
|
||||
|
||||
@ -72,63 +107,6 @@ function M.reload(force)
|
||||
end
|
||||
end
|
||||
|
||||
--- check if there's a circular dependency in the package tree
|
||||
function M.findcycle()
|
||||
local index = 0
|
||||
local indexes = {}
|
||||
local lowlink = {}
|
||||
local stack = {}
|
||||
|
||||
-- use tarjan algorithm to find circular dependencies (strongly connected
|
||||
-- components)
|
||||
local function connect(package)
|
||||
indexes[package.id], lowlink[package.id] = index, index
|
||||
stack[#stack + 1], stack[package.id] = package, true
|
||||
index = index + 1
|
||||
|
||||
for i = 1, #package.dependents do
|
||||
local dependent = package.dependents[i]
|
||||
|
||||
if not indexes[dependent.id] then
|
||||
local cycle = connect(dependent)
|
||||
if cycle then
|
||||
return cycle
|
||||
else
|
||||
lowlink[package.id] = math.min(lowlink[package.id], lowlink[dependent.id])
|
||||
end
|
||||
elseif stack[dependent.id] then
|
||||
lowlink[package.id] = math.min(lowlink[package.id], indexes[dependent.id])
|
||||
end
|
||||
end
|
||||
|
||||
if lowlink[package.id] == indexes[package.id] then
|
||||
local cycle = { package }
|
||||
local node
|
||||
|
||||
repeat
|
||||
node = stack[#stack]
|
||||
stack[#stack], stack[node.id] = nil, nil
|
||||
cycle[#cycle + 1] = node
|
||||
until node == package
|
||||
|
||||
-- a node is by definition strongly connected to itself ignore single-node
|
||||
-- components unless it explicitly specified itself as a dependency
|
||||
if #cycle > 2 or package.dependents[package.id] then
|
||||
return cycle
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for _, package in pairs(packager.get_packages()) do
|
||||
if not indexes[package.id] then
|
||||
local cycle = connect(package)
|
||||
if cycle then
|
||||
return cycle
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- sync a tree of plugins
|
||||
---@param tree package[] tree of plugins
|
||||
---@param cb function? callback
|
||||
@ -141,16 +119,15 @@ function M.synctree(tree, cb)
|
||||
has_errors = has_errors or err
|
||||
|
||||
if progress == #tree then
|
||||
-- TODO: implement clean
|
||||
-- clean()
|
||||
M.reload()
|
||||
|
||||
if has_errors then
|
||||
logger:log("error", "there were errors during sync; see :messages or :DepLog for more information")
|
||||
else
|
||||
logger:log("update", "synchronized %s %s", #tree, #tree == 1 and "package" or "packages")
|
||||
end
|
||||
|
||||
M.clean()
|
||||
M.reload()
|
||||
|
||||
if cb then
|
||||
cb()
|
||||
end
|
||||
@ -158,7 +135,10 @@ function M.synctree(tree, cb)
|
||||
end
|
||||
|
||||
for _, package in pairs(tree) do
|
||||
git.sync(package, done)
|
||||
local co = coroutine.create(function()
|
||||
git.sync(package, done)
|
||||
end)
|
||||
coroutine.resume(co)
|
||||
end
|
||||
end
|
||||
|
||||
@ -167,8 +147,8 @@ return function(opts)
|
||||
logger.pipe = logger:setup()
|
||||
|
||||
--- make comparison for table.sort
|
||||
---@param a table package spec a
|
||||
---@param b table package spec b
|
||||
---@param a package package spec a
|
||||
---@param b package package spec b
|
||||
---@return boolean
|
||||
local function comp(a, b)
|
||||
-- NOTE: this doesn't have to be in any real order, it just has to be
|
||||
@ -177,7 +157,7 @@ return function(opts)
|
||||
return a.id < b.id
|
||||
end
|
||||
|
||||
initialized, err = pcall(function()
|
||||
local initialized, err = pcall(function()
|
||||
packager.set_base_dir(opts.base_dir or vim.fn.stdpath("data").."/site/pack/deps/opt/")
|
||||
M.benchmark("load", function()
|
||||
-- register all packages
|
||||
@ -199,7 +179,10 @@ return function(opts)
|
||||
end
|
||||
|
||||
-- make sure there arent any circular dependencies
|
||||
M.findcycle()
|
||||
local ok = packager.findcycle(packager.get_packages())
|
||||
if type(ok) == "table" then
|
||||
logger:log("error", "found a cycle in the package spec here: %s", vim.inspect(ok))
|
||||
end
|
||||
end)
|
||||
|
||||
-- load packages
|
||||
@ -218,12 +201,13 @@ return function(opts)
|
||||
|
||||
-- get all package that need syncing
|
||||
local targets = {}
|
||||
for i, package in pairs(packager.get_packages()) do
|
||||
for _, package in pairs(packager.get_packages()) do
|
||||
if shouldsync(package) then
|
||||
targets[i] = package
|
||||
table.insert(targets, package)
|
||||
end
|
||||
end
|
||||
|
||||
-- install all targets
|
||||
M.synctree(targets)
|
||||
end)
|
||||
|
||||
@ -231,17 +215,18 @@ return function(opts)
|
||||
logger:log("error", err)
|
||||
end
|
||||
|
||||
-- add some user commands
|
||||
vim.api.nvim_create_user_command("DepLog", function()
|
||||
vim.cmd('vsp '..logger.path)
|
||||
vim.opt_local.readonly = true
|
||||
|
||||
-- make the log auto update while it's open
|
||||
local w = vim.uv.new_fs_event()
|
||||
local function watch_file(fname)
|
||||
local fullpath = vim.api.nvim_call_function(
|
||||
'fnamemodify', { fname, ':p' })
|
||||
w:start(fullpath, {}, vim.schedule_wrap(function(...)
|
||||
vim.api.nvim_command('checktime')
|
||||
-- Debounce: stop/start.
|
||||
w:stop()
|
||||
watch_file(fname)
|
||||
end))
|
||||
@ -258,5 +243,11 @@ return function(opts)
|
||||
M.reload()
|
||||
end, {})
|
||||
|
||||
vim.api.nvim_create_user_command("DepClean", function()
|
||||
-- clean AND reload to make sure that all old packages are gone
|
||||
M.clean()
|
||||
M.reload()
|
||||
end, {})
|
||||
|
||||
logger:cleanup()
|
||||
end
|
||||
|
@ -1,3 +1,7 @@
|
||||
-- TODO: clean this up, it's a mess
|
||||
-- the nesting of all the proc calls is really annoying, and I need to find a
|
||||
-- cleaner way to do it
|
||||
|
||||
local logger = require('dep.log')
|
||||
local proc = require('dep.proc')
|
||||
|
||||
@ -7,31 +11,47 @@ local git = {}
|
||||
---@param package package package to update/install
|
||||
---@param cb function callback
|
||||
function git.sync(package, cb)
|
||||
if package.exists then
|
||||
git.update(package, cb)
|
||||
else
|
||||
git.install(package, cb)
|
||||
local function sync()
|
||||
-- update or install
|
||||
if package.exists then
|
||||
git.update(package, cb)
|
||||
else
|
||||
git.install(package, cb)
|
||||
end
|
||||
end
|
||||
|
||||
-- handle arbitrary branches here
|
||||
if package.branch then
|
||||
proc.git_resolve_branch(package.url, package.branch, function(err, message)
|
||||
if not err then
|
||||
package.branch = message
|
||||
sync()
|
||||
end
|
||||
end)
|
||||
else
|
||||
sync()
|
||||
end
|
||||
end
|
||||
|
||||
--- configure a package
|
||||
---@param package table package spec
|
||||
local function configurepkg(package)
|
||||
package:runhooks("on_config")
|
||||
|
||||
logger:log("config", "package: %s configured", package.id)
|
||||
package.configured = true
|
||||
end
|
||||
|
||||
--- install a given package
|
||||
---@param package package package to install
|
||||
---@param cb function callback
|
||||
function git.install(package, cb)
|
||||
local function configurepkg()
|
||||
package:runhooks("on_config")
|
||||
|
||||
logger:log("config", "package: %s configured", package.id)
|
||||
package.configured = true
|
||||
end
|
||||
|
||||
|
||||
if not package.enabled then
|
||||
cb()
|
||||
return
|
||||
end
|
||||
|
||||
logger:log("error", "%s: doesn't exist", package.id)
|
||||
proc.git_clone(package.dir, package.url, package.branch, function(err, message)
|
||||
if err then
|
||||
logger:log("error", "failed to install %s; reason: %s",
|
||||
@ -44,15 +64,15 @@ function git.install(package, cb)
|
||||
else
|
||||
package.exists = true
|
||||
package:unconfiguretree()
|
||||
configurepkg()
|
||||
logger:log("install", "installed %s", package.id)
|
||||
configurepkg(package)
|
||||
end
|
||||
end)
|
||||
else
|
||||
package.exists = true
|
||||
package:unconfiguretree()
|
||||
configurepkg()
|
||||
logger:log("install", "installed %s", package.id)
|
||||
configurepkg(package)
|
||||
end
|
||||
end
|
||||
|
||||
@ -74,15 +94,6 @@ function git.update(package, cb)
|
||||
logger:log("error", "failed to update %s; reason: %s", package.id, err)
|
||||
end
|
||||
|
||||
--- configure a package
|
||||
---@param pkg table package spec
|
||||
local function configurepkg(pkg)
|
||||
package:runhooks("on_config")
|
||||
|
||||
logger:log("config", "package: %s configured", pkg.id)
|
||||
package.configured = true
|
||||
end
|
||||
|
||||
if package.pin then
|
||||
cb()
|
||||
return
|
||||
@ -112,8 +123,8 @@ function git.update(package, cb)
|
||||
cb(err)
|
||||
else
|
||||
package:unconfiguretree()
|
||||
configurepkg(package)
|
||||
logger:log("update", "updated %s; %s -> %s", package.id, before, after)
|
||||
configurepkg(package)
|
||||
end
|
||||
end)
|
||||
end
|
||||
@ -124,7 +135,7 @@ function git.update(package, cb)
|
||||
log_err(message)
|
||||
cb(err)
|
||||
else
|
||||
proc.git_rev_parse(package.dir, "FETCH_HEAD", function(err, after)
|
||||
proc.git_rev_parse(package.dir, "FETCH_HEAD^{commit}", function(err, after)
|
||||
if err then
|
||||
log_err(after)
|
||||
cb(err)
|
||||
@ -137,8 +148,8 @@ function git.update(package, cb)
|
||||
log_err(message)
|
||||
else
|
||||
package:unconfiguretree()
|
||||
configurepkg(package)
|
||||
logger:log("update", "updated %s; %s -> %s", package.id, before, after)
|
||||
configurepkg(package)
|
||||
end
|
||||
|
||||
cb(err)
|
||||
|
@ -76,7 +76,7 @@ function logger:log(level, message, ...)
|
||||
|
||||
-- write to the pipe if it's open
|
||||
if logger.pipe then
|
||||
logger.pipe:write(string.format("[%s] %s:%s: %s\n", os.date("%Y/%m/%d"), source.short_src:gsub('.*%/', ''), source.currentline, message))
|
||||
logger.pipe:write(string.format("[%s] %s:%s: %s\n", os.date("%T"), source.short_src:gsub('.*%/', ''), source.currentline, message))
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
@ -23,13 +23,13 @@ local logger = require('dep.log')
|
||||
---@field lazy boolean if the package is lazy loaded in any way
|
||||
---@field added boolean if the package has been added in vim
|
||||
---@field configured boolean if the package has been configured
|
||||
---@field lazied boolean if the packages lazy loading has been set
|
||||
---@field loaded boolean if a package has been loaded
|
||||
---@field subtree_loaded boolean is the subtree has been loaded
|
||||
---@field on_config function[] table of functions to run on config
|
||||
---@field on_setup function[] table of function to run on setup
|
||||
---@field on_load function[] table of functions to run on load
|
||||
---@field lazy_load function[] table of functions to run which will tell the
|
||||
--- package when to load
|
||||
---@field lazy_load function[] table of functions to run which will tell the package when to load
|
||||
---@field requirements package[] this package's requirements
|
||||
---@field dependents package[] packages that require this package
|
||||
---@field perf table performance metrics for the package
|
||||
@ -71,7 +71,7 @@ local function check_spec(spec)
|
||||
|
||||
local name = spec[1]:match("^[%w-_.]+/([%w-_.]+)$")
|
||||
if not name then
|
||||
logger:log("spec",'invalid name "%s"; must be in the format "user/package"', spec[1])
|
||||
logger:log("spec", 'invalid name "%s"; must be in the format "user/package"', spec[1])
|
||||
return false
|
||||
end
|
||||
end
|
||||
@ -153,6 +153,7 @@ local function check_spec(spec)
|
||||
return false
|
||||
end
|
||||
|
||||
-- turn an id into a spec
|
||||
if (is == "string") then
|
||||
spec.reqs = { spec.reqs }
|
||||
end
|
||||
@ -165,6 +166,7 @@ local function check_spec(spec)
|
||||
return false
|
||||
end
|
||||
|
||||
-- turn an id into a spec
|
||||
if (is == "string") then
|
||||
spec.deps = { spec.deps }
|
||||
end
|
||||
@ -212,31 +214,33 @@ function package:new(spec, overrides)
|
||||
local id = spec[1]
|
||||
|
||||
local o = packages[id] or {}
|
||||
self.__index = self
|
||||
setmetatable(o, self)
|
||||
|
||||
-- if package hasn't been registered already, get the inital spec regisitered
|
||||
if not o.id then
|
||||
o.id = id -- id of the package
|
||||
o.enabled = true -- whether it's going to be used
|
||||
o.exists = false -- if the package exists on the filesystem
|
||||
o.lazy = false -- if the package is lazy loaded in any way
|
||||
o.added = false -- if the package has been added in vim
|
||||
o.configured = false -- if the package has been configured
|
||||
o.loaded = false -- if a package has been loaded
|
||||
o.id = id
|
||||
o.enabled = true
|
||||
o.exists = false
|
||||
o.lazy = false
|
||||
o.added = false
|
||||
o.lazied = false
|
||||
o.configured = false
|
||||
o.loaded = false
|
||||
o.subtree_loaded = false
|
||||
o.on_config = {} -- table of functions to run on config
|
||||
o.on_setup = {} -- table of function to run on setup
|
||||
o.on_load = {} -- table of functions to run on load
|
||||
o.lazy_load = {} -- table of functions to run which will tell the package
|
||||
-- when to load
|
||||
o.requirements = {} -- this package's requirements
|
||||
o.dependents = {} -- packages that require this package
|
||||
o.on_config = {}
|
||||
o.on_setup = {}
|
||||
o.on_load = {}
|
||||
o.lazy_load = {}
|
||||
|
||||
o.requirements = {}
|
||||
o.dependents = {}
|
||||
o.perf = {}
|
||||
|
||||
packages[id] = o
|
||||
end
|
||||
|
||||
o.name = spec.as or o.name or id
|
||||
o.name = spec.as or o.name or id:match("^[%w-_.]+/([%w-_.]+)$")
|
||||
o.url = spec.url or o.url or ("https://github.com/"..id..".git")
|
||||
o.branch = spec.branch or o.branch
|
||||
o.dir = base_dir..o.name
|
||||
@ -287,26 +291,32 @@ function package:new(spec, overrides)
|
||||
if spec.deps then
|
||||
---it is the correct type as asserted in check_spec()
|
||||
---@diagnostic disable-next-line: param-type-mismatch
|
||||
for _, v in pairs(spec.deps) do
|
||||
local pkg = package:new(v)
|
||||
if type(pkg) ~= "table" then
|
||||
for _, dep in pairs(spec.deps) do
|
||||
local pkg = package:new(dep)
|
||||
if not pkg then
|
||||
return false
|
||||
end
|
||||
o:link_dependency(nil, pkg)
|
||||
o:link_dependency(o, pkg)
|
||||
|
||||
-- if the child package is lazy loaded make sure the child package
|
||||
-- is only loaded when the parent package has finished loading
|
||||
if package.lazy then
|
||||
table.insert(package.on_load, function()
|
||||
o:loadtree(true)
|
||||
if o.lazy then
|
||||
table.insert(o.on_load, function()
|
||||
local ok = o:loadtree(true)
|
||||
if not ok then
|
||||
logger:log("lazy",
|
||||
"failed to run loadtree for %s, some packages may not be loaded",
|
||||
o.id)
|
||||
end
|
||||
end)
|
||||
|
||||
-- tell the dep that it's gonna be lazy
|
||||
pkg.lazy = true
|
||||
table.insert(pkg.lazy_load, function(_) end)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self.__index = self
|
||||
|
||||
return o
|
||||
end
|
||||
|
||||
@ -316,14 +326,23 @@ function package.set_base_dir(_base_dir)
|
||||
base_dir = _base_dir
|
||||
end
|
||||
|
||||
--- get the base directory for packages
|
||||
---@return string base_dir
|
||||
---@nodiscard
|
||||
function package.get_base_dir()
|
||||
return base_dir
|
||||
end
|
||||
|
||||
--- get the root directory
|
||||
---@return package root
|
||||
---@nodiscard
|
||||
function package.get_root()
|
||||
return root
|
||||
end
|
||||
|
||||
--- get the packages in dep
|
||||
---@return package root
|
||||
---@nodiscard
|
||||
function package.get_packages()
|
||||
return packages
|
||||
end
|
||||
@ -376,12 +395,12 @@ function package:ensureadded(force)
|
||||
end
|
||||
end
|
||||
|
||||
-- run setup hooks
|
||||
pkg:runhooks("on_setup")
|
||||
|
||||
-- now start loading our plugin
|
||||
local start = os.clock()
|
||||
|
||||
-- run setup hooks
|
||||
self:runhooks("on_setup")
|
||||
|
||||
-- trigger the packadd for the plugin
|
||||
---@diagnostic disable-next-line: param-type-mismatch
|
||||
local ok, err = pcall(vim.cmd, "packadd "..pkg.name)
|
||||
@ -394,23 +413,24 @@ function package:ensureadded(force)
|
||||
logger:log("vim", "packadd completed for %s", pkg.id)
|
||||
|
||||
-- set the package to loaded
|
||||
self.loaded = true
|
||||
logger:log("load", "loaded %s", self.id)
|
||||
pkg.loaded = true
|
||||
logger:log("load", "loaded %s", pkg.id)
|
||||
|
||||
-- trigger the on_load hooks
|
||||
ok, err = self:runhooks("on_load")
|
||||
ok, err = pkg:runhooks("on_load")
|
||||
if not ok then
|
||||
logger:log("error", "failed to load %s; reason: %s", self.id, err)
|
||||
logger:log("error", "failed to load %s; reason: %s", pkg.id, err)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- make sure the package is lazy loaded if need be
|
||||
if not self.added and not self.lazy or force then
|
||||
if not self.added and not self.loaded and not self.lazy or force then
|
||||
loadpkg(self)
|
||||
elseif not self.added and self.lazy then
|
||||
logger:log("lazy", "registering %d lazy hooks for %s", #self.lazy_load,
|
||||
self.id)
|
||||
self.lazied = true
|
||||
for _, load_cond in pairs(self.lazy_load) do
|
||||
-- configure the lazy loader for the user
|
||||
local l = require('lazy.utils'):new()
|
||||
@ -436,26 +456,31 @@ end
|
||||
--- load all packages in package tree
|
||||
---@param force boolean? force lazy packages to load
|
||||
---@return boolean boolean if tree was successfully loaded
|
||||
---@nodiscard
|
||||
function package:loadtree(force)
|
||||
-- if the package doesn't exist or isn't enabled then don't load it
|
||||
if not self.exists or not self.enabled then
|
||||
logger:log("load", "package %s doesn't exist or is not enabled", self.id)
|
||||
return false
|
||||
end
|
||||
|
||||
if self.subtree_loaded then
|
||||
logger:log("load", "package %s's subtree is already loaded", self.id)
|
||||
-- if the subtree is loaded then it's already loaded unless it needs forcing
|
||||
if not force and self.subtree_loaded then
|
||||
return true
|
||||
end
|
||||
|
||||
-- if the package isn't lazy check that it's requirements are loaded
|
||||
if not self.lazy then
|
||||
for _, requirement in pairs(self.requirements) do
|
||||
if not requirement.loaded then
|
||||
logger:log("load", "package %s requires %s to be loaded first", self.id, requirement.id)
|
||||
if not requirement.loaded and not requirement.lazy then
|
||||
logger:log("error", "failed to load %s; requirement: %s isn't loaded",
|
||||
self.id, requirement.id)
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- if the package isn't loaded and isn't lazy then it should probably be
|
||||
-- loaded
|
||||
if not self.loaded then
|
||||
local ok, err = self:ensureadded(force)
|
||||
if not ok then
|
||||
@ -464,8 +489,9 @@ function package:loadtree(force)
|
||||
end
|
||||
end
|
||||
|
||||
package.subtree_loaded = true
|
||||
self.subtree_loaded = true
|
||||
|
||||
-- make sure the dependants are loaded
|
||||
for _, dependant in pairs(self.dependents) do
|
||||
self.subtree_loaded = dependant:loadtree(force) and self.subtree_loaded
|
||||
end
|
||||
@ -490,4 +516,68 @@ function package:unconfiguretree()
|
||||
end
|
||||
end
|
||||
|
||||
--- check a list of packages for any cycles
|
||||
---@param pkgs package[] list of packages
|
||||
---@return package[]|false cycle the cycle that was found or false if not found
|
||||
---@nodisacard
|
||||
function package.findcycle(pkgs)
|
||||
local index = 0
|
||||
local indexes = {}
|
||||
local lowlink = {}
|
||||
local stack = {}
|
||||
|
||||
--- use tarjan algorithm to find circular dependencies (strongly connected
|
||||
--- components)
|
||||
---@param pkg package
|
||||
local function connect(pkg)
|
||||
indexes[pkg.id], lowlink[pkg.id] = index, index
|
||||
stack[#stack + 1], stack[pkg.id] = pkg, true
|
||||
index = index + 1
|
||||
|
||||
for i = 1, #pkg.dependents do
|
||||
local dependent = pkg.dependents[i]
|
||||
|
||||
if not indexes[dependent.id] then
|
||||
local cycle = connect(dependent)
|
||||
if cycle then
|
||||
return cycle
|
||||
else
|
||||
lowlink[pkg.id] = math.min(lowlink[pkg.id], lowlink[dependent.id])
|
||||
end
|
||||
elseif stack[dependent.id] then
|
||||
lowlink[pkg.id] = math.min(lowlink[pkg.id], indexes[dependent.id])
|
||||
end
|
||||
end
|
||||
|
||||
if lowlink[pkg.id] == indexes[pkg.id] then
|
||||
local cycle = { pkg }
|
||||
local node
|
||||
|
||||
repeat
|
||||
node = stack[#stack]
|
||||
stack[#stack], stack[node.id] = nil, nil
|
||||
cycle[#cycle + 1] = node
|
||||
until node == pkg
|
||||
|
||||
-- a node is by definition strongly connected to itself ignore single-node
|
||||
-- components unless it explicitly specified itself as a dependency
|
||||
if #cycle > 2 or pkg.dependents[pkg.id] then
|
||||
return cycle
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- actually check the cycle
|
||||
for _, pkg in pairs(pkgs) do
|
||||
if not indexes[package.id] then
|
||||
local cycle = connect(pkg)
|
||||
if cycle then
|
||||
return cycle
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
return package
|
||||
|
@ -1,12 +1,18 @@
|
||||
local proc = {}
|
||||
|
||||
--- execute a process
|
||||
---@param process string the program
|
||||
---@param args string[] the args
|
||||
---@param cwd string? the pwd
|
||||
---@param env table env
|
||||
---@param cb function callback
|
||||
function proc.exec(process, args, cwd, env, cb)
|
||||
local buffer = {}
|
||||
|
||||
local function cb_output(_, data, _)
|
||||
table.insert(buffer, table.concat(data))
|
||||
end
|
||||
local function cb_exit(job_id, exit_code, _)
|
||||
local function cb_exit(_, exit_code, _)
|
||||
local output = table.concat(buffer)
|
||||
cb(exit_code ~= 0, output)
|
||||
end
|
||||
@ -62,54 +68,56 @@ function proc.git_checkout(dir, branch, commit, cb)
|
||||
end
|
||||
|
||||
function proc.git_resolve_branch(url, branch, cb)
|
||||
if string.match(branch or "", "*") ~= "*" then
|
||||
-- if the branch doesn't contain a * then return the branch
|
||||
if not string.match(branch, "*") then
|
||||
cb(false, branch)
|
||||
return
|
||||
end
|
||||
local buffer = {}
|
||||
|
||||
local buffer = {}
|
||||
local function cb_output(_, data, _)
|
||||
if data[1] ~= "" then
|
||||
buffer = data
|
||||
end
|
||||
end
|
||||
|
||||
vim.fn.jobstart({ "git", "ls-remote", "--tags", "--sort", "v:refname", url, },
|
||||
vim.fn.jobstart({ "git", "ls-remote", "--tags", "--sort", "v:refname", url },
|
||||
{
|
||||
cwd = nil,
|
||||
env = { GIT_TERMINAL_PROMPT = 0 },
|
||||
env = git_env,
|
||||
stdin = nil,
|
||||
on_stdout = cb_output,
|
||||
on_stderr = cb_output,
|
||||
on_exit = function(_, exit_code, _)
|
||||
if exit_code == 0 then
|
||||
-- get a list of all versions
|
||||
local versions = {}
|
||||
for _, v in pairs(buffer) do
|
||||
local s, e = string.find(v, "refs/tags/.+")
|
||||
if not s or not e then
|
||||
goto continue
|
||||
end
|
||||
if exit_code ~= 0 then
|
||||
return
|
||||
end
|
||||
|
||||
local tag = string.sub(v, s, e)
|
||||
tag = string.gsub(tag, "refs/tags/", "")
|
||||
tag = string.gsub(tag, "%^{}", "")
|
||||
|
||||
table.insert(versions, tag)
|
||||
::continue::
|
||||
-- get a list of all versions
|
||||
local versions = {}
|
||||
for _, v in pairs(buffer) do
|
||||
local s, e = string.find(v, "refs/tags/.+")
|
||||
if not s or not e then
|
||||
goto continue
|
||||
end
|
||||
|
||||
-- match the chosen version against all versions
|
||||
for i = #versions, 1, -1 do
|
||||
if branch == "*" then
|
||||
cb(false, versions[i])
|
||||
local tag = string.sub(v, s, e)
|
||||
tag = tag:gsub("refs/tags/", ""):gsub("%^{}", "")
|
||||
|
||||
table.insert(versions, tag)
|
||||
::continue::
|
||||
end
|
||||
|
||||
-- match the chosen version against all versions
|
||||
for i = #versions, 1, -1 do
|
||||
if branch == "*" then
|
||||
cb(false, versions[i])
|
||||
return
|
||||
else
|
||||
local r = string.match(versions[i], branch)
|
||||
if r then
|
||||
cb(false, r)
|
||||
return
|
||||
else
|
||||
local r = string.match(versions[i], branch)
|
||||
if r then
|
||||
cb(false, r)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -37,7 +37,7 @@ end
|
||||
---@param opts vim.api.keyset.user_command? options
|
||||
function lazy:cmd(name, opts)
|
||||
opts = opts or {}
|
||||
vim.api.nvim_create_user_command(name, function(o)
|
||||
vim.api.nvim_create_user_command(name, opts['callback'] or function()
|
||||
self:cleanup()
|
||||
end, opts)
|
||||
|
||||
@ -49,9 +49,8 @@ end
|
||||
---@param opts vim.api.keyset.create_autocmd? options
|
||||
function lazy:auto(event, opts)
|
||||
opts = opts or {}
|
||||
opts['once'] = true
|
||||
|
||||
opts['callback'] = function()
|
||||
opts['once'] = opts['once'] or true
|
||||
opts['callback'] = opts['callback'] or function()
|
||||
self:cleanup()
|
||||
end
|
||||
|
||||
@ -64,7 +63,7 @@ end
|
||||
---@param opts vim.keymap.set.Opts? options
|
||||
function lazy:keymap(mode, bind, opts)
|
||||
opts = opts or {}
|
||||
vim.keymap.set(mode, bind, function()
|
||||
vim.keymap.set(mode, bind, opts['callback'] or function()
|
||||
self:cleanup()
|
||||
|
||||
-- register keymap unload
|
||||
|
Reference in New Issue
Block a user