diff --git a/README.md b/README.md index 60af5b8..8750a36 100644 --- a/README.md +++ b/README.md @@ -2,51 +2,37 @@ ## ✨ Features -- Partial clones instead of shallow clones -- Async -- No need for compile -- Fast -- Correct sequencing of dependencies (deps should always be opt. Maybe make everything opt?) +- [x] Partial clones instead of shallow clones +- [x] waits till missing deps are installed (bootstrap Neovim and start using it right away) +- [x] Async +- [x] No need for compile +- [x] Fast +- [x] Correct sequencing of dependencies (deps should always be opt. Maybe make everything opt?) - [ ] Import specs from Packer -- Config in multiple files -- Patterns for local packages +- [x] Config in multiple files +- [x] Patterns for local packages +- [x] Profiling - [ ] lockfile +- [ ] check for updates - [ ] package.lua - [ ] package-lock.lua -- [ ] tag/version support `git tag --sort version:refname` -- [ ] auto-loading on completion for lazy-loaded commands -- [ ] semver https://devhints.io/semver +- [x] tag/version support `git tag --sort version:refname` +- [x] auto-loading on completion for lazy-loaded commands +- [x] semver https://devhints.io/semver https://semver.npmjs.com/ ## ✅ TODO -- [ ] show time taken for op in view +- [ ] view keybindings for update/clean/... +- [ ] add profiler to view +- [ ] add buttons for actions +- [x] show time taken for op in view - [ ] package meta index (package.lua cache for all packages) - [ ] migrate from Packer - [ ] auto lazy-loading of lua modules - [ ] use uv file watcher to check for config changes - [x] clear errors -- [ ] add support for versions `git tag --sort v:refname` +- [x] add support for versions `git tag --sort v:refname` - [ ] rename requires to deps -- [ ] move tasks etc to Plugin.state - - loaded - - installed - - updated - - changed: just installed or updated (dirty) - - is_local - https://github.blog/2020-12-21-get-up-to-speed-with-partial-clone-and-shallow-clone/ - -## 🖥️ Git Operations - -1. **install**: - - - run `git clone` with given `branch`,`--single-branch`, `filter=blob:none` - and `--no-checkout` - - run `git checkout` with correct `branch`, `tag` or `commit` - -2. **update**: - - - if branch is missing `git remote set-branches --add origin MISSING_BRANCH` - - `git switch MISSING_BRANCH` - - run `git fetch` - - run `git checkout` with correct `branch`, `tag` or `commit` +- [x] move tasks etc to Plugin.state +- [ ] allow setting up plugins through config diff --git a/lua/lazy/core/config.lua b/lua/lazy/core/config.lua index 0d2e255..3103c4e 100644 --- a/lua/lazy/core/config.lua +++ b/lua/lazy/core/config.lua @@ -13,6 +13,7 @@ M.defaults = { }, interactive = true, packpath = vim.fn.stdpath("data") .. "/site/pack/lazy", + lockfile = vim.fn.stdpath("config") .. "/lazy-lock.json", view = { icons = { start = "", @@ -23,6 +24,7 @@ M.defaults = { keys = " ", cmd = " ", ft = "", + task = "✔ ", }, }, } diff --git a/lua/lazy/core/plugin.lua b/lua/lazy/core/plugin.lua index 2daa855..2b9498d 100644 --- a/lua/lazy/core/plugin.lua +++ b/lua/lazy/core/plugin.lua @@ -18,11 +18,11 @@ M.dirty = false ---@class LazyPluginState ---@field loaded? {[string]:string, time:number} ----@field installed? boolean +---@field installed boolean ---@field tasks? LazyTask[] ---@field dirty? boolean ---@field updated? {from:string, to:string} ----@field is_local? boolean +---@field is_local boolean ---@field is_symlink? boolean ---@field cloned? boolean diff --git a/lua/lazy/manage/init.lua b/lua/lazy/manage/init.lua index a2ee213..fc7dfd1 100644 --- a/lua/lazy/manage/init.lua +++ b/lua/lazy/manage/init.lua @@ -39,6 +39,7 @@ function M.run(ropts, opts) if opts.wait then runner:wait() end + return runner end ---@param opts? ManagerOpts @@ -58,23 +59,26 @@ function M.install(opts) }, opts) end ----@param opts? ManagerOpts +---@param opts? ManagerOpts|{lockfile?:boolean} function M.update(opts) + opts = opts or {} M.run({ pipeline = { "fs.symlink", "git.branch", "git.fetch", - "git.checkout", + { "git.checkout", lockfile = opts.lockfile }, "plugin.docs", - "plugin.run", "wait", + "plugin.run", { "git.log", updated = true }, }, plugins = function(plugin) return plugin.uri and plugin._.installed end, - }, opts) + }, opts):wait(function() + require("lazy.manage.lock").update() + end) end ---@param opts? ManagerOpts @@ -97,9 +101,11 @@ function M.clean(opts) end function M.clear() + Plugin.update_state(true) for _, plugin in pairs(Config.plugins) do - -- clear updated status plugin._.updated = nil + plugin._.cloned = nil + plugin._.dirty = nil -- clear finished tasks if plugin._.tasks then ---@param task LazyTask diff --git a/lua/lazy/manage/lock.lua b/lua/lazy/manage/lock.lua new file mode 100644 index 0000000..0b03e8c --- /dev/null +++ b/lua/lazy/manage/lock.lua @@ -0,0 +1,73 @@ +local Config = require("lazy.core.config") +local Git = require("lazy.manage.git") + +local M = {} + +---@type table +M.lock = {} +M._loaded = false + +function M.update() + local f = assert(io.open(Config.options.lockfile, "w")) + f:write("{\n") + M.lock = {} + + ---@param plugin LazyPlugin + local plugins = vim.tbl_filter(function(plugin) + return not plugin._.is_local and plugin._.installed + end, Config.plugins) + + ---@param plugin LazyPlugin + ---@type string[] + local names = vim.tbl_map(function(plugin) + return plugin.name + end, plugins) + table.sort(names) + + for n, name in ipairs(names) do + local plugin = Config.plugins[name] + if not plugin._.is_local and plugin._.installed then + local info = assert(Git.info(plugin.dir)) + if not info.branch then + local branch = assert(Git.get_branch(plugin)) + info.branch = branch.branch + end + info.commit = info.commit + -- f:write(([[ [%q] = { branch = %q, commit = %q },]]):format(name, info.branch, info.commit) .. "\n") + f:write(([[ %q: { "branch": %q, "commit": %q }]]):format(name, info.branch, info.commit)) + if n ~= #names then + f:write(",\n") + end + ---@diagnostic disable-next-line: assign-type-mismatch + M.lock[plugin.name] = info + end + end + f:write("\n}") + f:close() +end + +function M.load() + M.lock = {} + M._loaded = true + local f = io.open(Config.options.lockfile, "r") + if f then + ---@type string + local data = f:read("*a") + local ok, lock = pcall(vim.json.decode, data) + if ok then + M.lock = lock + end + f:close() + end +end + +---@param plugin LazyPlugin +---@return {commit:string, branch:string} +function M.get(plugin) + if not M._loaded then + M.load() + end + return M.lock[plugin.name] +end + +return M diff --git a/lua/lazy/manage/task/git.lua b/lua/lazy/manage/task/git.lua index 496a917..b2f8ceb 100644 --- a/lua/lazy/manage/task/git.lua +++ b/lua/lazy/manage/task/git.lua @@ -1,5 +1,6 @@ local Util = require("lazy.util") local Git = require("lazy.manage.git") +local Lock = require("lazy.manage.lock") ---@type table local M = {} @@ -118,11 +119,21 @@ M.checkout = { skip = function(plugin) return not plugin._.installed or plugin._.is_local end, - run = function(self) + ---@param opts {lockfile?:boolean} + run = function(self, opts) local info = assert(Git.info(self.plugin.dir)) local target = assert(Git.get_target(self.plugin)) - if not self.plugin._.cloned and info.commit == target.commit then + local lock + if opts.lockfile then + lock = Lock.get(self.plugin) + if lock then + ---@diagnostic disable-next-line: cast-local-type + target = lock + end + end + + if not self.plugin._.cloned and info.commit == target.commit and info.branch == target.branch then return end @@ -131,7 +142,9 @@ M.checkout = { "--progress", } - if target.tag then + if lock then + table.insert(args, lock.commit) + elseif target.tag then table.insert(args, "tags/" .. target.tag) elseif self.plugin.commit then table.insert(args, self.plugin.commit) diff --git a/lua/lazy/view/commands.lua b/lua/lazy/view/commands.lua index 6279b62..7412e99 100644 --- a/lua/lazy/view/commands.lua +++ b/lua/lazy/view/commands.lua @@ -40,6 +40,9 @@ M.commands = { update = function() Manage.update({ clear = true, interactive = true }) end, + reset = function() + Manage.update({ clear = true, interactive = true, lockfile = true }) + end, } function M.setup()