try to clean it up (not working yet)

This commit is contained in:
2025-04-22 17:31:35 -05:00
parent 1d0b486e08
commit 8bcc8bc0b1
4 changed files with 681 additions and 682 deletions

View File

@ -1,5 +1,6 @@
local logger = require('dep.log')
local proc = require('dep.proc')
local git = require('dep.git')
local packager = require('dep.package')
---all functions for convenience
---@type table
@ -8,22 +9,10 @@ local M = {}
---@type boolean
local initialized
---root package
---@type table
local root
---performance logging
---@type table
local perf = {}
---table of every package where their id is the index
---@type table
local packages = {}
---path to root of where plugins are downloaded
---@type string
local base_dir
--- get execution time of a function
---@param name string name of performance output
---@param code function function to run
@ -34,305 +23,35 @@ function M.benchmark(name, code, ...)
perf[name] = os.clock() - start
end
--- get name of package
---@param id string id of the package
---@return string id
---@nodiscard
function M.getpkgname(id)
local name = id:match("^[%w-_.]+/([%w-_.]+)$")
if name then
return name
else
error(string.format(
'invalid name "%s"; must be in the format "user/package"', id))
end
end
--- tell the parent it has a child and the child it has a parent
---@param parent table parent package
---@param child table child package
function M.link_dependency(parent, child)
if not parent.dependents[child.id] then
parent.dependents[child.id] = child
parent.dependents[#parent.dependents + 1] = child
end
if not child.dependencies[parent.id] then
child.dependencies[parent.id] = parent
child.dependencies[#child.dependencies + 1] = parent
end
end
--- register a new package according to the spec
---@param spec table|string the package spec to register
---@param overrides table|nil a package spec that is used to override options
---@return table package a table containing the package spec
---@nodiscard
function M.registerpkg(spec, overrides)
overrides = overrides or {}
if type(spec) ~= "table" then
spec = { spec }
end
local id = spec[1]
local package = packages[id]
-- if package hasn't been registered already, get the inital spec regisitered
if not package then
package = {
id = id, -- id of the package
enabled = true, -- whether it's going to be used
exists = false, -- if the package exists on the filesystem
lazy = false, -- if the package is lazy loaded in any way
added = false, -- if the package has been added in vim
configured = false, -- if the package has been configured
loaded = false, -- if a package has been loaded
subtree_loaded = false,
on_config = {}, -- table of functions to run on config
on_setup = {}, -- table of function to run on setup
on_load = {}, -- table of functions to run on load
lazy_load = {}, -- table of functions to run which will tell the package
-- when to load
dependencies = {}, -- this package's requirements
dependents = {}, -- packages that require this package
perf = {}
}
packages[id] = package
end
-- register the rest of the package spec
package.name = spec.as or package.name or M.getpkgname(id)
package.url = spec.url or package.url or ("https://github.com/"..id..".git")
package.branch = spec.branch or package.branch
package.dir = base_dir..package.name
package.commit = spec.commit
package.pin = overrides.pin or spec.pin or package.pin
package.enabled = not overrides.disable and not spec.disable and package.enabled
package.lazy = spec.lazy or package.lazy
-- make sure that the package exists
package.exists = vim.fn.isdirectory(package.dir) ~= 0
package.configured = package.exists
-- register all the callback functions
table.insert(package.on_config, spec.config)
table.insert(package.on_load, spec.load)
table.insert(package.lazy_load, spec.lazy)
-- if the current package isn't the root package then it depends on the root
-- package
if root and package ~= root then
M.link_dependency(root, package)
end
-- link the dependencies
if spec.requires then
if type(spec.requires) == "string" then
spec.requires = { spec.requires }
end
for _, v in pairs(spec.requires) do
M.link_dependency(M.registerpkg(v), package)
end
end
-- and link the dependents
if spec.deps then
if type(spec.deps) == "string" then
spec.deps = { spec.deps }
end
for _, v in pairs(spec.deps) do
local p = M.registerpkg(v)
M.link_dependency(package, p)
-- 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()
M.loadtree(package, true)
end)
table.insert(p.lazy_load, function(_) end)
end
end
end
return package
end
--- recurse over all packages and register them
---@param speclist table table of specs
---@param overrides table|nil a package spec that is used to override options
---@param speclist spec[] table of specs
---@param overrides spec? a package spec that is used to override options
function M.registertree(speclist, overrides)
overrides = overrides or {}
-- make sure the overrides override and take into account the packages spec
overrides = {
pin = overrides.pin or speclist.pin,
disable = overrides.disable or speclist.disable
}
-- recurse the packages
for i = 1, #speclist do
local ok, err = pcall(M.registerpkg, speclist[i], 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
}
local ok = packager:new(spec, overrides)
-- if erroring print out the spec and the error
if not ok then
error(string.format("%s (spec=%s)", err, vim.inspect(speclist[i])))
error(string.format("%s (spec=%s)", err, vim.inspect(spec)))
end
end
end
--- run specified hooks on specified package
---@param package table package spec
---@param type string which hook to run
---@return boolean, string|nil
function M.runhooks(package, type)
local hooks = package[type]
if #hooks == 0 then
return true
end
local start = os.clock()
-- chdir into the package directory to make running external commands
-- from hooks easier.
local last_cwd = vim.fn.getcwd()
vim.fn.chdir(package.dir)
for i = 1, #hooks do
local ok, err = pcall(hooks[i])
if not ok then
vim.fn.chdir(last_cwd)
package.error = true
return false, err
end
end
vim.fn.chdir(last_cwd)
package.perf[type] = os.clock() - start
logger:log("hook", "triggered %d %s %s for %s", #hooks, type,
#hooks == 1 and "hook" or "hooks", package.id)
return true
end
--- make sure a package has been loaded
---@param package table package
---@param force boolean? force lazy packages to load
---@return boolean|table return true or false if loaded or package spec if lazy loaded
function M.ensureadded(package, force)
-- print("adding ~ "..package.id)
local function loadpkg(pkg)
-- make sure to load the dependencies first
for _, p in pairs(pkg.dependencies) do
if not p.loaded then
M.ensureadded(p, true)
end
end
-- now start loading our plugin
local start = os.clock()
-- trigger the packadd for the plugin
local ok, err = pcall(vim.cmd, "packadd " .. pkg.name)
if not ok then
pkg.error = true
return false, err
end
pkg.added = true
pkg.perf.pack = os.clock() - start
logger:log("vim", "packadd completed for %s", pkg.id)
-- set the package to loaded
package.loaded = true
logger:log("load", "loaded %s", package.id)
-- trigger the on_load hooks
ok, err = M.runhooks(package, "on_load")
if not ok then
logger:log("error", "failed to load %s; reason: %s", package.id, err)
return
end
end
if not package.added and not package.lazy or force then
loadpkg(package)
elseif not package.added and package.lazy then
logger:log("lazy", "registering %d lazy hooks for %s", #package.lazy_load,
package.id)
for _, load_cond in pairs(package.lazy_load) do
-- configure the lazy loader for the user
local l = require('lazy.utils'):new()
if l == true then
logger:log("lazy", "failed to get lazy utils")
return false
end
l:set_load(function()
logger:log("lazy", "triggered %d lazy hooks for %s", #package.lazy_load,
package.id)
loadpkg(package)
end)
-- run it
load_cond(l)
end
return package
end
return true
end
--- load all packages in package tree
---@param package table package spec table
---@param force boolean? force lazy packages to load
---@return boolean boolean if tree was successfully loaded
function M.loadtree(package, force)
if not package.exists or not package.enabled or package.error then
return false
end
if package.subtree_loaded then
return true
end
if not package.lazy then
for i = 1, #package.dependencies do
if not package.dependencies[i].loaded then
return false
end
end
end
if not package.loaded then
local ok, err = M.ensureadded(package, force)
if not ok then
logger:log("error", "failed to load %s; reason: %s", package.id, err)
return false
end
end
package.subtree_loaded = true
for i = 1, #package.dependents do
package.subtree_loaded = M.loadtree(package.dependents[i], force) and package.subtree_loaded
end
return package.subtree_loaded
end
--- reload all packages in package table spec
---@param force boolean|nil force all packages to load
function M.reload(force)
-- cleanup any previous errors in the package table
for i in pairs(packages) do
packages[i].error = false
end
local reloaded = M.loadtree(root, force)
local reloaded = packager.get_root():loadtree(force)
if reloaded then
local ok, err
@ -400,9 +119,7 @@ function M.findcycle()
end
end
for i = 1, #packages do
local package = packages[i]
for _, package in pairs(packager.get_packages()) do
if not indexes[package.id] then
local cycle = connect(package)
if cycle then
@ -412,147 +129,8 @@ function M.findcycle()
end
end
--- unconfigure a packages tree
---@param package table package to unconfigure
function M.unconfiguretree(package)
-- unconfigure dependencies
for i = 1, #package.dependencies do
package.dependencies[i].subtree_loaded = false
end
-- unconfigure dependents
for i = 1, #package.dependents do
package.dependents[i].loaded = false
package.dependents[i].added = false
package.dependents[i].configured = false
package.dependents[i].subtree_loaded = false
end
end
--- update/download a package
---@param package table package spec
---@param cb function callback
function M.sync(package, cb)
if not package.enabled then
cb()
return
end
local function log_err(err)
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)
M.runhooks(package, "on_config")
logger:log("config", "package: %s configured", pkg.id)
package.configured = true
end
if package.exists then
if package.pin then
cb()
return
end
local function logerr(err)
logger:log("error", "failed to update %s; reason: %s", package.id, err)
end
proc.git_rev_parse(package.dir, "HEAD", function(err, before)
if err then
logerr(before)
cb(err)
else
if package.commit then
proc.git_checkout(package.dir, package.branch, package.commit, function(err, message)
if err then
log_err(message)
cb(err)
else
proc.git_rev_parse(package.dir, package.commit, function(err, after)
if err then
log_err(after)
cb(err)
elseif before == after then
logger:log("skip", "skipped %s", package.id)
cb(err)
else
M.unconfiguretree(package)
configurepkg(package)
logger:log("update", "updated %s; %s -> %s", package.id, before, after)
end
end)
end
end)
else
proc.git_fetch(package.dir, "origin", package.branch or "HEAD", function(err, message)
if err then
log_err(message)
cb(err)
else
proc.git_rev_parse(package.dir, "FETCH_HEAD", function(err, after)
if err then
log_err(after)
cb(err)
elseif before == after then
logger:log("skip", "skipped %s", package.id)
cb(err)
else
proc.git_reset(package.dir, after, function(err, message)
if err then
log_err(message)
else
M.unconfiguretree(package)
configurepkg(package)
logger:log("update", "updated %s; %s -> %s", package.id, before, after)
end
cb(err)
end)
end
end)
end
end)
end
end
end)
else
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",
package.id, message)
else
if package.commit then
proc.git_checkout(package.dir, package.branch, package.commit, function(err, message)
if err then
logger:log("error", "failed to checkout %s; reason: %s", package.id, message)
else
package.exists = true
M.unconfiguretree(package)
configurepkg(package)
logger:log("install", "installed %s", package.id)
end
end)
else
package.exists = true
M.unconfiguretree(package)
configurepkg(package)
logger:log("install", "installed %s", package.id)
end
end
cb(err)
end)
end
end
--- sync a tree of plugins
---@param tree table tree of plugins
---@param tree package[] tree of plugins
---@param cb function? callback
function M.synctree(tree, cb)
local progress = 0
@ -579,234 +157,11 @@ function M.synctree(tree, cb)
end
end
for i in pairs(tree) do
M.sync(tree[i], done)
for _, package in pairs(tree) do
git.sync(package, done)
end
end
local function print_list(cb)
local function get_commits(cb)
local results = {}
local done = 0
for i = 1, #packages do
local package = packages[i]
if package.exists then
proc.git_rev_parse(package.dir, "HEAD", function(err, commit)
if not err then
results[package.id] = commit
end
done = done + 1
if done == #packages then
cb(results)
end
end)
else
done = done + 1
end
end
end
get_commits(function(commits)
local buffer = vim.api.nvim_create_buf(true, true)
local line, indent = 0, 0
local function print(chunks)
local concat = {}
local column = 0
for _ = 1, indent do
concat[#concat + 1] = " "
column = column + 2
end
if not chunks then
chunks = {}
elseif type(chunks) == "string" then
chunks = { { chunks } }
end
for i = 1, #chunks do
local chunk = chunks[i]
concat[#concat + 1] = chunk[1]
chunk.offset, column = column, column + #chunk[1]
end
vim.api.nvim_buf_set_lines(buffer, line, -1, false, { table.concat(concat) })
for i = 1, #chunks do
local chunk = chunks[i]
if chunk[2] then
vim.api.nvim_buf_add_highlight(buffer, -1, chunk[2], line, chunk.offset, chunk.offset + #chunk[1])
end
end
line = line + 1
end
print(string.format("Installed packages (%s):", #packages))
indent = 1
local loaded = {}
local function dry_load(package)
if loaded[package.id] then
return
end
for i = 1, #package.dependencies do
if not loaded[package.dependencies[i].id] then
return
end
end
loaded[package.id], loaded[#loaded + 1] = true, package
local chunk = {
{ string.format("[%s] ", commits[package.id] or " "), "Comment" },
{ package.id, "Underlined" },
}
if not package.exists then
chunk[#chunk + 1] = { " *not installed", "Comment" }
end
if not package.loaded then
chunk[#chunk + 1] = { " *not loaded", "Comment" }
end
if not package.enabled then
chunk[#chunk + 1] = { " *disabled", "Comment" }
end
if package.pin then
chunk[#chunk + 1] = { " *pinned", "Comment" }
end
print(chunk)
for i = 1, #package.dependents do
dry_load(package.dependents[i])
end
end
dry_load(root)
indent = 0
print()
print("Load time (μs):")
indent = 1
local profiles = {}
for i = 1, #packages do
local package = packages[i]
local profile = {
package = package,
total = 0,
setup = package.perf.on_setup or 0,
load = package.perf.on_load or 0,
pack = package.perf.pack or 0,
"total",
"setup",
"pack",
"load",
}
if package == root then
for k, v in pairs(perf) do
if profile[k] then
profile[k] = profile[k] + v
end
end
end
for j = 1, #profile do
profile.total = profile.total + profile[profile[j]]
end
profiles[#profiles + 1] = profile
end
table.sort(profiles, function(a, b)
return a.total > b.total
end)
for i = 1, #profiles do
local profile = profiles[i]
local chunk = {
{ "- ", "Comment" },
{ profile.package.id, "Underlined" },
{ string.rep(" ", 40 - #profile.package.id) },
}
for j = 1, #profile do
local key, value = profile[j], profile[profile[j]]
chunk[#chunk + 1] = { string.format(" %5s ", key), "Comment" }
chunk[#chunk + 1] = { string.format("%4d", value * 1000000) }
end
print(chunk)
end
indent = 0
print()
print("Dependency graph:")
local function walk_graph(package)
local chunk = {
{ "| ", "Comment" },
{ package.id, "Underlined" },
}
local function add_edges(p)
for i = 1, #p.dependencies do
local dependency = p.dependencies[i]
if dependency ~= root and not chunk[dependency.id] then -- don't convolute the list
chunk[#chunk + 1] = { " " .. dependency.id, "Comment" }
chunk[dependency.id] = true
add_edges(dependency)
end
end
end
add_edges(package)
print(chunk)
for i = 1, #package.dependents do
indent = indent + 1
walk_graph(package.dependents[i])
indent = indent - 1
end
end
walk_graph(root)
-- print()
-- print("Debug information:")
-- local debug = {}
-- for l in vim.inspect(packages):gmatch("[^\n]+") do
-- debug[#debug + 1] = l
-- end
-- vim.api.nvim_buf_set_lines(buffer, line, -1, false, debug)
-- vim.api.nvim_buf_set_name(buffer, "packages.dep")
-- vim.api.nvim_buf_set_option(buffer, "bufhidden", "wipe")
-- vim.api.nvim_buf_set_option(buffer, "modifiable", false)
vim.cmd("vsp")
vim.api.nvim_win_set_buf(0, buffer)
if cb then
cb()
end
end)
end
-- basically the main function of our program
return function(opts)
logger.pipe = logger:setup()
@ -823,19 +178,24 @@ return function(opts)
end
initialized, err = pcall(function()
base_dir = opts.base_dir or vim.fn.stdpath("data").."/site/pack/deps/opt/"
packager.set_base_dir(opts.base_dir or vim.fn.stdpath("data").."/site/pack/deps/opt/")
M.benchmark("load", function()
-- register all packages
root = M.registerpkg("squibid/dep")
local root = packager:new({
"squibid/dep",
url = "https://git.squi.bid/dep",
pin = true
})
if not root then
logger:log("error", "couldn't register root package")
return
end
M.registertree(opts)
-- sort packages
table.sort(packages, comp)
-- sort package dependencies
for i = 1, #packages do
table.sort(packages[i].dependencies, comp)
table.sort(packages[i].dependents, comp)
for _, package in pairs(root.get_packages()) do
table.sort(package.requirements, comp)
table.sort(package.dependents, comp)
end
-- make sure there arent any circular dependencies
@ -858,9 +218,9 @@ return function(opts)
-- get all package that need syncing
local targets = {}
for i in pairs(packages) do
if shouldsync(packages[i]) then
targets[i] = packages[i]
for i, package in pairs(packager.get_packages()) do
if shouldsync(package) then
targets[i] = package
end
end
@ -891,16 +251,12 @@ return function(opts)
end, {})
vim.api.nvim_create_user_command("DepSync", function()
M.synctree(packages)
M.synctree(packager.get_packages())
end, {})
vim.api.nvim_create_user_command("DepReload", function()
M.reload()
end, {})
vim.api.nvim_create_user_command("DepList", function()
print_list()
end, {})
logger:cleanup()
end