Dep now supports lazy loading
This commit is contained in:
10
README.md
10
README.md
@ -69,7 +69,7 @@ A package must be declared in the following format.
|
|||||||
"user/package",
|
"user/package",
|
||||||
|
|
||||||
-- [function] Code to run after the package is loaded into neovim.
|
-- [function] Code to run after the package is loaded into neovim.
|
||||||
function()
|
load = function()
|
||||||
require "package".setup(...)
|
require "package".setup(...)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
@ -83,6 +83,10 @@ A package must be declared in the following format.
|
|||||||
os.execute(...)
|
os.execute(...)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
-- [function] Code used to determine when the package should be loaded.
|
||||||
|
lazy = function(load)
|
||||||
|
end,
|
||||||
|
|
||||||
-- [string] Overrides the short name of the package.
|
-- [string] Overrides the short name of the package.
|
||||||
-- Defaults to a substring of the full name after '/'.
|
-- Defaults to a substring of the full name after '/'.
|
||||||
as = "custom_package",
|
as = "custom_package",
|
||||||
@ -95,6 +99,10 @@ A package must be declared in the following format.
|
|||||||
-- Defaults to whatever the remote configured as their HEAD, which is usually "master".
|
-- Defaults to whatever the remote configured as their HEAD, which is usually "master".
|
||||||
branch = "develop",
|
branch = "develop",
|
||||||
|
|
||||||
|
-- [string] Overrides the commit ref to target
|
||||||
|
-- Defaults to the latest commit on the current branch
|
||||||
|
commit = "e76cb03",
|
||||||
|
|
||||||
-- [boolean] Prevents the package from being loaded.
|
-- [boolean] Prevents the package from being loaded.
|
||||||
disable = true,
|
disable = true,
|
||||||
|
|
||||||
|
1014
lua/dep.lua
1014
lua/dep.lua
File diff suppressed because it is too large
Load Diff
165
lua/dep/log.lua
165
lua/dep/log.lua
@ -1,23 +1,28 @@
|
|||||||
--
|
local logger = {}
|
||||||
-- Copyright (c) 2022 chiya.dev
|
|
||||||
--
|
|
||||||
-- Use of this source code is governed by the MIT License
|
|
||||||
-- which can be found in the LICENSE file and at:
|
|
||||||
--
|
|
||||||
-- https://chiya.dev/licenses/mit.txt
|
|
||||||
--
|
|
||||||
local vim, setmetatable, pcall, debug, string, os, assert = vim, setmetatable, pcall, debug, string, os, assert
|
|
||||||
|
|
||||||
|
logger.stage_colors = {
|
||||||
|
skip = "Comment",
|
||||||
|
clean = "Boolean",
|
||||||
|
install = "MoreMsg",
|
||||||
|
update = "WarningMsg",
|
||||||
|
delete = "Directory",
|
||||||
|
error = "ErrorMsg",
|
||||||
|
}
|
||||||
|
|
||||||
|
--- create the default logging path
|
||||||
|
---@return string path to the logfile
|
||||||
local function default_log_path()
|
local function default_log_path()
|
||||||
-- ensure cache directory exists (#5)
|
-- create cache directory and chmod it if it doesn't already exist
|
||||||
local path = vim.fn.stdpath("cache")
|
local path = vim.fn.stdpath("cache")
|
||||||
if not vim.loop.fs_stat(path) then
|
if not vim.loop.fs_stat(path) then
|
||||||
vim.loop.fs_mkdir(path, 0x1ff) -- 0777
|
vim.loop.fs_mkdir(path, 0x1ff) -- 0777
|
||||||
end
|
end
|
||||||
|
|
||||||
return path .. "/dep.log"
|
return vim.fs.joinpath(path, "/dep.log")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- attempt to format a string
|
||||||
|
---@vararg string formating args
|
||||||
local function try_format(...)
|
local function try_format(...)
|
||||||
local ok, s = pcall(string.format, ...)
|
local ok, s = pcall(string.format, ...)
|
||||||
if ok then
|
if ok then
|
||||||
@ -25,85 +30,69 @@ local function try_format(...)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Writes logs to a file and prints pretty status messages.
|
--- setup all logging stuff
|
||||||
local Logger = setmetatable({
|
---@param path string|nil optional alternative path for the log file
|
||||||
__metatable = "Logger",
|
---@return table
|
||||||
__index = {
|
function logger:setup(path)
|
||||||
--- Prints a message associated with a stage.
|
logger.path = path or default_log_path()
|
||||||
log = function(self, stage, message, ...)
|
local pipe
|
||||||
-- calling function
|
|
||||||
local source = debug.getinfo(2, "Sl").short_src
|
|
||||||
|
|
||||||
-- format or stringify message
|
logger.handle = assert(vim.loop.fs_open(logger.path, "w", 0x1a4)) -- 0644
|
||||||
if type(message) == "string" then
|
pipe = vim.loop.new_pipe()
|
||||||
message = try_format(message, ...) or message
|
pipe:open(logger.handle)
|
||||||
else
|
|
||||||
message = vim.inspect(message)
|
return pipe
|
||||||
|
end
|
||||||
|
|
||||||
|
--- log a message
|
||||||
|
---@param level string error level
|
||||||
|
---@param message any string message to send
|
||||||
|
---@vararg any options to go into the message
|
||||||
|
function logger:log(level, message, ...)
|
||||||
|
-- make sure the message string is actually a string, and formatted
|
||||||
|
-- appropriately
|
||||||
|
if type(message) == "string" then
|
||||||
|
message = try_format(message, ...) or message
|
||||||
|
else
|
||||||
|
message = vim.inspect(message)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- get debug info about the current function
|
||||||
|
local source = debug.getinfo(2, "Sl")
|
||||||
|
|
||||||
|
-- schedule a log message to be sent to vim, and the log file
|
||||||
|
vim.schedule(function()
|
||||||
|
if not logger.silent then
|
||||||
|
if level == "error" then
|
||||||
|
vim.api.nvim_echo({ { string.format("[dep] %s", message) } }, true, { err = true })
|
||||||
|
elseif logger.stage_colors[level] then
|
||||||
|
vim.api.nvim_echo({
|
||||||
|
{ "[dep]", "Identifier" },
|
||||||
|
{ " " },
|
||||||
|
{ message, logger.stage_colors[level] },
|
||||||
|
}, true, {})
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- print and write must be done on the main event loop
|
-- write to the pipe if it's open
|
||||||
vim.schedule(function()
|
if logger.pipe then
|
||||||
if not self.silent then
|
logger.pipe:write(string.format("[%s] %s:%s: %s\n", os.date("%Y/%m/%d"), source.short_src:gsub('.*%/', ''), source.currentline, message))
|
||||||
if stage == "error" then
|
end
|
||||||
vim.api.nvim_err_writeln(string.format("[dep] %s", message))
|
end)
|
||||||
elseif self.stage_colors[stage] then
|
end
|
||||||
vim.api.nvim_echo({
|
|
||||||
{ "[dep]", "Identifier" },
|
|
||||||
{ " " },
|
|
||||||
{ message, self.stage_colors[stage] },
|
|
||||||
}, true, {})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if self.pipe then
|
--- cleanup all logging stuff
|
||||||
self.pipe:write(string.format("[%s] %s: %s\n", os.date(), source, message))
|
---@param pipe table? pipe
|
||||||
end
|
---@param handle table? handle
|
||||||
end)
|
function logger:cleanup(pipe, handle)
|
||||||
end,
|
if pipe then
|
||||||
|
pipe:close()
|
||||||
|
pipe = nil
|
||||||
|
end
|
||||||
|
if handle then
|
||||||
|
vim.loop.fs_close(logger.handle)
|
||||||
|
handle = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
--- Closes the log file handle.
|
return logger
|
||||||
close = function(self)
|
|
||||||
if self.pipe then
|
|
||||||
self.pipe:close()
|
|
||||||
self.pipe = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
if self.handle then
|
|
||||||
vim.loop.fs_close(self.handle)
|
|
||||||
self.handle = nil
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
},
|
|
||||||
}, {
|
|
||||||
--- Constructs a new `Logger`.
|
|
||||||
__call = function(mt, path)
|
|
||||||
path = path or default_log_path()
|
|
||||||
|
|
||||||
-- clear and open log file
|
|
||||||
local handle = assert(vim.loop.fs_open(path, "w", 0x1a4)) -- 0644
|
|
||||||
local pipe = vim.loop.new_pipe()
|
|
||||||
pipe:open(handle)
|
|
||||||
|
|
||||||
return setmetatable({
|
|
||||||
path = path,
|
|
||||||
handle = handle,
|
|
||||||
pipe = pipe,
|
|
||||||
silent = false,
|
|
||||||
|
|
||||||
-- TODO: This looks good for me ;) but it should have proper vim color mapping for other people.
|
|
||||||
stage_colors = {
|
|
||||||
skip = "Comment",
|
|
||||||
clean = "Boolean",
|
|
||||||
install = "MoreMsg",
|
|
||||||
update = "WarningMsg",
|
|
||||||
delete = "Directory",
|
|
||||||
error = "ErrorMsg",
|
|
||||||
},
|
|
||||||
}, mt)
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
Logger = Logger,
|
|
||||||
global = Logger(),
|
|
||||||
}
|
|
||||||
|
@ -1,273 +0,0 @@
|
|||||||
--
|
|
||||||
-- Copyright (c) 2022 chiya.dev
|
|
||||||
--
|
|
||||||
-- Use of this source code is governed by the MIT License
|
|
||||||
-- which can be found in the LICENSE file and at:
|
|
||||||
--
|
|
||||||
-- https://chiya.dev/licenses/mit.txt
|
|
||||||
--
|
|
||||||
local require, type, setmetatable, error, table, assert, math, os, debug =
|
|
||||||
require, type, setmetatable, error, table, assert, math, os, debug
|
|
||||||
local logger = require("dep.log").global
|
|
||||||
|
|
||||||
local function parse_name_from_id(id)
|
|
||||||
local name = id:match("^[%w-_.]+/([%w-_.]+)$")
|
|
||||||
if name then
|
|
||||||
return name
|
|
||||||
else
|
|
||||||
error(string.format('invalid package name "%s"; must be in the format "user/package"', id))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function is_nonempty_str(s)
|
|
||||||
return type(s) == "string" and #s ~= 0
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Package information.
|
|
||||||
local Package = setmetatable({
|
|
||||||
__metatable = "Package",
|
|
||||||
__index = {
|
|
||||||
--- Runs all registered hooks of the given type.
|
|
||||||
run_hooks = function(self, hook)
|
|
||||||
local hooks = self["on_" .. hook]
|
|
||||||
if not hooks or #hooks == 0 then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
local start = os.clock()
|
|
||||||
for i = 1, #hooks do
|
|
||||||
local ok, err = xpcall(hooks[i], debug.traceback)
|
|
||||||
if not ok then
|
|
||||||
return false, err
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local elapsed = os.clock() - start
|
|
||||||
self.perf.hooks[hook] = elapsed
|
|
||||||
|
|
||||||
logger:log(
|
|
||||||
"hook",
|
|
||||||
"triggered %d %s %s for %s in %dms",
|
|
||||||
#hooks,
|
|
||||||
hook,
|
|
||||||
#hooks == 1 and "hook" or "hooks",
|
|
||||||
self.id,
|
|
||||||
elapsed
|
|
||||||
)
|
|
||||||
|
|
||||||
return true
|
|
||||||
end,
|
|
||||||
},
|
|
||||||
}, {
|
|
||||||
--- Constructs a new `Package` with the given identifier.
|
|
||||||
__call = function(mt, id)
|
|
||||||
local name = parse_name_from_id(id)
|
|
||||||
return setmetatable({
|
|
||||||
id = id,
|
|
||||||
name = name,
|
|
||||||
url = "https://github.com/" .. id .. ".git",
|
|
||||||
enabled = true,
|
|
||||||
exists = false,
|
|
||||||
added = false,
|
|
||||||
configured = false,
|
|
||||||
loaded = false,
|
|
||||||
dependencies = {},
|
|
||||||
dependents = {},
|
|
||||||
subtree_configured = false,
|
|
||||||
subtree_loaded = false,
|
|
||||||
on_setup = {},
|
|
||||||
on_config = {},
|
|
||||||
on_load = {},
|
|
||||||
perf = { hooks = {} },
|
|
||||||
}, mt)
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
--- Manages a set of packages.
|
|
||||||
local PackageStore = setmetatable({
|
|
||||||
__metatable = "PackageStore",
|
|
||||||
__index = {
|
|
||||||
--- Links the given packages such that the parent must load before the child.
|
|
||||||
link_dependency = function(self, 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,
|
|
||||||
|
|
||||||
--- Ensures the given package spec table is valid.
|
|
||||||
validate_spec = function(self, spec)
|
|
||||||
assert(spec[1] ~= nil, "package id missing from spec")
|
|
||||||
assert(type(spec[1]) == "string", "package id must be a string")
|
|
||||||
parse_name_from_id(spec[1])
|
|
||||||
|
|
||||||
assert(spec.as == nil or is_nonempty_str(spec.as), "package name must be a string")
|
|
||||||
assert(spec.url == nil or type(spec.url) == "string", "package url must be a string") -- TODO: validate url or path
|
|
||||||
assert(spec.branch == nil or is_nonempty_str(spec.branch), "package branch must be a string")
|
|
||||||
assert(spec.pin == nil or type(spec.pin) == "boolean", "package pin must be a boolean")
|
|
||||||
assert(spec.disable == nil or type(spec.disable) == "boolean", "package disable must be a boolean")
|
|
||||||
|
|
||||||
assert(
|
|
||||||
spec.requires == nil or type(spec.requires) == "table" or type(spec.requires) == "string",
|
|
||||||
"package requires must be a string or table"
|
|
||||||
)
|
|
||||||
assert(
|
|
||||||
spec.deps == nil or type(spec.deps) == "table" or type(spec.deps) == "string",
|
|
||||||
"package deps must be a string or table"
|
|
||||||
)
|
|
||||||
|
|
||||||
assert(spec.setup == nil or type(spec.setup) == "function", "package setup must be a function")
|
|
||||||
assert(spec.config == nil or type(spec.config) == "function", "package config must be a function")
|
|
||||||
assert(spec[2] == nil or type(spec[2]) == "function", "package loader must be a function")
|
|
||||||
end,
|
|
||||||
|
|
||||||
--- Creates or updates a package from the given spec table, and returns that package.
|
|
||||||
add_spec = function(self, spec, scope)
|
|
||||||
self:validate_spec(spec)
|
|
||||||
scope = scope or {}
|
|
||||||
|
|
||||||
local id = spec[1]
|
|
||||||
local pkg = self[id]
|
|
||||||
|
|
||||||
if not pkg then
|
|
||||||
pkg = Package(id)
|
|
||||||
self[id], self[#self + 1] = pkg, pkg
|
|
||||||
end
|
|
||||||
|
|
||||||
-- blend package spec with existing package info
|
|
||||||
pkg.name = spec.as or pkg.name
|
|
||||||
pkg.url = spec.url or pkg.url
|
|
||||||
pkg.branch = spec.branch or pkg.branch
|
|
||||||
pkg.pin = scope.pin or spec.pin or pkg.pin
|
|
||||||
pkg.enabled = not scope.disable and not spec.disable and pkg.enabled
|
|
||||||
|
|
||||||
pkg.on_setup[#pkg.on_setup + 1] = spec.setup
|
|
||||||
pkg.on_config[#pkg.on_config + 1] = spec.config
|
|
||||||
pkg.on_load[#pkg.on_load + 1] = spec[2]
|
|
||||||
|
|
||||||
local requires = type(spec.requires) == "table" and spec.requires or { spec.requires }
|
|
||||||
local deps = type(spec.deps) == "table" and spec.deps or { spec.deps }
|
|
||||||
|
|
||||||
-- recursively add specs for dependencies and dependents
|
|
||||||
for i = 1, #requires do
|
|
||||||
self:link_dependency(self:add_spec(requires[i], scope), pkg)
|
|
||||||
end
|
|
||||||
|
|
||||||
for i = 1, #deps do
|
|
||||||
self:link_dependency(pkg, self:add_spec(deps[i], scope))
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
--- Adds the given list of specs.
|
|
||||||
add_specs = function(self, specs, scope)
|
|
||||||
assert(type(specs) == "table", "package list must be a table")
|
|
||||||
assert(specs.pin == nil or type(specs.pin) == "boolean", "package list pin must be a boolean")
|
|
||||||
assert(specs.disable == nil or type(specs.disable) == "boolean", "package list disable must be a boolean")
|
|
||||||
assert(specs.modules == nil or type(specs.modules) == "table", "package list module list must be a table")
|
|
||||||
|
|
||||||
scope = scope or {}
|
|
||||||
scope = {
|
|
||||||
-- outer scope takes precedence over inner list's overrides
|
|
||||||
pin = scope.pin or specs.pin,
|
|
||||||
disable = scope.disable or specs.disable,
|
|
||||||
}
|
|
||||||
|
|
||||||
-- add specs in spec list
|
|
||||||
for i = 1, #specs do
|
|
||||||
self:add_spec(specs[i], scope)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- recursively add referenced spec list modules
|
|
||||||
if specs.modules then
|
|
||||||
local prefix = specs.modules.prefix or ""
|
|
||||||
for i = 1, #specs.modules do
|
|
||||||
local name = specs.modules[i]
|
|
||||||
assert(type(name) == "string", "package list inner module name must be a string")
|
|
||||||
name = prefix .. name
|
|
||||||
|
|
||||||
local module = require(name)
|
|
||||||
assert(type(module) == "table", "package list inner module did not return a spec list table")
|
|
||||||
self:add_specs(module, scope)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
--- Ensures there are no circular dependencies in this package store.
|
|
||||||
ensure_acyclic = function(self)
|
|
||||||
-- tarjan's strongly connected components algorithm
|
|
||||||
local idx, indices, lowlink, stack = 0, {}, {}, {}
|
|
||||||
|
|
||||||
local function connect(pkg)
|
|
||||||
indices[pkg.id], lowlink[pkg.id] = idx, idx
|
|
||||||
stack[#stack + 1], stack[pkg.id] = pkg, true
|
|
||||||
idx = idx + 1
|
|
||||||
|
|
||||||
for i = 1, #pkg.dependents do
|
|
||||||
local dependent = pkg.dependents[i]
|
|
||||||
|
|
||||||
if not indices[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], indices[dependent.id])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if lowlink[pkg.id] == indices[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 the package explicitly specified itself as a dependency (i.e. the user is being weird)
|
|
||||||
if #cycle > 2 or pkg.dependents[pkg.id] then
|
|
||||||
return cycle
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
for i = 1, #self do
|
|
||||||
local pkg = self[i]
|
|
||||||
|
|
||||||
if not indices[pkg.id] then
|
|
||||||
local cycle = connect(pkg)
|
|
||||||
if cycle then
|
|
||||||
-- found dependency cycle
|
|
||||||
local names = {}
|
|
||||||
for j = 1, #cycle do
|
|
||||||
names[j] = cycle[j].id
|
|
||||||
end
|
|
||||||
error("circular dependency detected in package dependency graph: " .. table.concat(names, " -> "))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
},
|
|
||||||
}, {
|
|
||||||
--- Constructs a new `PackageStore`.
|
|
||||||
__call = function(mt)
|
|
||||||
-- hash part of store maps package ids to packages
|
|
||||||
-- array part of store is a list of packages
|
|
||||||
-- all packages in a store are unique based on their id
|
|
||||||
return setmetatable({}, mt)
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
Package = Package,
|
|
||||||
PackageStore = PackageStore,
|
|
||||||
}
|
|
@ -1,4 +1,3 @@
|
|||||||
local logger = require("dep.log").global
|
|
||||||
local proc = {}
|
local proc = {}
|
||||||
|
|
||||||
function proc.exec(process, args, cwd, env, cb)
|
function proc.exec(process, args, cwd, env, cb)
|
||||||
@ -9,15 +8,6 @@ function proc.exec(process, args, cwd, env, cb)
|
|||||||
end
|
end
|
||||||
local function cb_exit(job_id, exit_code, _)
|
local function cb_exit(job_id, exit_code, _)
|
||||||
local output = table.concat(buffer)
|
local output = table.concat(buffer)
|
||||||
logger:log(
|
|
||||||
process,
|
|
||||||
string.format(
|
|
||||||
'Job %s ["%s"] finished with exitcode %s\n%s',
|
|
||||||
job_id,
|
|
||||||
table.concat(args, '", "'),
|
|
||||||
exit_code,
|
|
||||||
output)
|
|
||||||
)
|
|
||||||
cb(exit_code ~= 0, output)
|
cb(exit_code ~= 0, output)
|
||||||
end
|
end
|
||||||
table.insert(args, 1, process)
|
table.insert(args, 1, process)
|
||||||
@ -43,7 +33,7 @@ function proc.git_clone(dir, url, branch, cb)
|
|||||||
local args = { "clone", "--depth=1", "--recurse-submodules", "--shallow-submodules", url, dir }
|
local args = { "clone", "--depth=1", "--recurse-submodules", "--shallow-submodules", url, dir }
|
||||||
|
|
||||||
if branch then
|
if branch then
|
||||||
args[#args + 1] = "--branch=" .. branch
|
args[#args + 1] = "--branch="..branch
|
||||||
end
|
end
|
||||||
|
|
||||||
proc.exec("git", args, nil, git_env, cb)
|
proc.exec("git", args, nil, git_env, cb)
|
||||||
@ -61,4 +51,70 @@ function proc.git_reset(dir, treeish, cb)
|
|||||||
proc.exec("git", args, dir, git_env, cb)
|
proc.exec("git", args, dir, git_env, cb)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function proc.git_checkout(dir, branch, commit, cb)
|
||||||
|
local args = { "fetch", "--depth=2147483647", "origin", branch }
|
||||||
|
proc.exec("git", args, dir, git_env, function(err, message)
|
||||||
|
cb(err, message)
|
||||||
|
|
||||||
|
args = { "checkout", commit }
|
||||||
|
proc.exec("git", args, dir, git_env, cb)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function proc.git_resolve_branch(url, branch, cb)
|
||||||
|
if string.match(branch or "", "*") ~= "*" then
|
||||||
|
cb(false, branch)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
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, },
|
||||||
|
{
|
||||||
|
cwd = nil,
|
||||||
|
env = { GIT_TERMINAL_PROMPT = 0 },
|
||||||
|
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
|
||||||
|
|
||||||
|
local tag = string.sub(v, s, e)
|
||||||
|
tag = string.gsub(tag, "refs/tags/", "")
|
||||||
|
tag = string.gsub(tag, "%^{}", "")
|
||||||
|
|
||||||
|
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
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
return proc
|
return proc
|
||||||
|
12
lua/dep2.lua
12
lua/dep2.lua
@ -1,12 +0,0 @@
|
|||||||
--
|
|
||||||
-- Copyright (c) 2022 chiya.dev
|
|
||||||
--
|
|
||||||
-- Use of this source code is governed by the MIT License
|
|
||||||
-- which can be found in the LICENSE file and at:
|
|
||||||
--
|
|
||||||
-- https://chiya.dev/licenses/mit.txt
|
|
||||||
--
|
|
||||||
local logger = require("dep.log").global
|
|
||||||
local store = require("dep.package").PackageStore()
|
|
||||||
|
|
||||||
-- placeholder for refactoring
|
|
97
lua/lazy/utils.lua
Normal file
97
lua/lazy/utils.lua
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
---@class lazy
|
||||||
|
---@field load function
|
||||||
|
---@field command_ids table
|
||||||
|
---@field auto_ids table
|
||||||
|
---@field keybind_ids table
|
||||||
|
local lazy = {}
|
||||||
|
|
||||||
|
--- create a new instance of lazy
|
||||||
|
---@return lazy
|
||||||
|
function lazy:new()
|
||||||
|
local o = {}
|
||||||
|
|
||||||
|
setmetatable(o, self)
|
||||||
|
|
||||||
|
o.command_ids = {}
|
||||||
|
o.auto_ids = {}
|
||||||
|
o.keybind_ids = {}
|
||||||
|
|
||||||
|
self.__index = self
|
||||||
|
|
||||||
|
return o
|
||||||
|
end
|
||||||
|
|
||||||
|
--- set the loading function
|
||||||
|
---@param load function the loading function
|
||||||
|
function lazy:set_load(load)
|
||||||
|
self.load = load
|
||||||
|
end
|
||||||
|
|
||||||
|
--- get the configured load function
|
||||||
|
---@return function load function
|
||||||
|
function lazy:get_load()
|
||||||
|
return self.load
|
||||||
|
end
|
||||||
|
|
||||||
|
--- create a usercommand which will trigger the plugin to load
|
||||||
|
---@param name string the name of the command
|
||||||
|
---@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)
|
||||||
|
self:cleanup()
|
||||||
|
end, opts)
|
||||||
|
|
||||||
|
table.insert(self.command_ids, name)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- user an auto command which will trigger the plugin to load
|
||||||
|
---@param event string the event to trigger on
|
||||||
|
---@param opts vim.api.keyset.create_autocmd? options
|
||||||
|
function lazy:auto(event, opts)
|
||||||
|
opts = opts or {}
|
||||||
|
opts['once'] = true
|
||||||
|
|
||||||
|
opts['callback'] = function()
|
||||||
|
self:cleanup()
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(self.auto_ids, vim.api.nvim_create_autocmd(event, opts))
|
||||||
|
end
|
||||||
|
|
||||||
|
--- create a keybind which will trigger the plugin to load
|
||||||
|
---@param mode string the mode to trigger in
|
||||||
|
---@param bind string the binding to use
|
||||||
|
---@param opts vim.keymap.set.Opts? options
|
||||||
|
function lazy:keymap(mode, bind, opts)
|
||||||
|
opts = opts or {}
|
||||||
|
vim.keymap.set(mode, bind, function()
|
||||||
|
self:cleanup()
|
||||||
|
|
||||||
|
-- register keymap unload
|
||||||
|
local keys = vim.api.nvim_replace_termcodes(bind, true, false, true)
|
||||||
|
vim.api.nvim_feedkeys(keys, mode, false)
|
||||||
|
end, opts)
|
||||||
|
|
||||||
|
table.insert(self.keybind_ids, { ['mode'] = mode, ['bind'] = bind })
|
||||||
|
end
|
||||||
|
|
||||||
|
--- cleanup all the callbacks, and load the plugin
|
||||||
|
function lazy:cleanup()
|
||||||
|
-- cleanup user commands
|
||||||
|
for _, v in pairs(self.command_ids) do
|
||||||
|
vim.api.nvim_del_user_command(v)
|
||||||
|
end
|
||||||
|
-- cleanup auto commands
|
||||||
|
for _, v in pairs(self.auto_ids) do
|
||||||
|
vim.api.nvim_del_autocmd(v)
|
||||||
|
end
|
||||||
|
-- cleanup keymaps
|
||||||
|
for _, v in pairs(self.keybind_ids) do
|
||||||
|
vim.keymap.del(v['mode'], v['bind'], {})
|
||||||
|
end
|
||||||
|
-- load the plugin
|
||||||
|
self:load()
|
||||||
|
end
|
||||||
|
|
||||||
|
return lazy
|
Reference in New Issue
Block a user