From 294adeceeb5def42f53744efb4b87a801b6593bc Mon Sep 17 00:00:00 2001 From: glepnir Date: Sun, 29 Sep 2024 11:35:37 +0800 Subject: [PATCH] udpate --- lua/minpm/async.lua | 39 ++++++++++++ lua/minpm/event.lua | 6 ++ lua/minpm/init.lua | 146 ++++++++++++++------------------------------ lua/minpm/task.lua | 32 ++++++++++ lua/minpm/win.lua | 67 ++++++++++++++++++++ 5 files changed, 190 insertions(+), 100 deletions(-) create mode 100644 lua/minpm/async.lua create mode 100644 lua/minpm/event.lua create mode 100644 lua/minpm/task.lua create mode 100644 lua/minpm/win.lua diff --git a/lua/minpm/async.lua b/lua/minpm/async.lua new file mode 100644 index 0000000..e8efdcc --- /dev/null +++ b/lua/minpm/async.lua @@ -0,0 +1,39 @@ +local function wrap_async(func) + return function(...) + local args = { ... } + return function(callback) + table.insert(args, callback) + func(unpack(args)) + end + end +end + +local function await(promise) + local co = coroutine.running() + promise(function(...) + local args = { ... } + vim.schedule(function() + assert(coroutine.resume(co, unpack(args))) + end) + end) + return coroutine.yield() +end + +local function async(func) + return function(...) + local co = coroutine.create(func) + local function step(...) + local ok, err = coroutine.resume(co, ...) + if not ok then + error(err) + end + end + step(...) + end +end + +return { + async_fs_fstat = wrap_async(vim.uv.fs_fstat), + async = async, + await = await, +} diff --git a/lua/minpm/event.lua b/lua/minpm/event.lua new file mode 100644 index 0000000..2cbba84 --- /dev/null +++ b/lua/minpm/event.lua @@ -0,0 +1,6 @@ +local api = vim.api +local au = api.nvim_create_autocmd + +local M = {} + +return M diff --git a/lua/minpm/init.lua b/lua/minpm/init.lua index 3d343c3..b9f85f2 100644 --- a/lua/minpm/init.lua +++ b/lua/minpm/init.lua @@ -1,6 +1,6 @@ -local api, stdpath, uv = vim.api, vim.fn.stdpath, vim.uv +local api, stdpath, uv, if_nil = vim.api, vim.fn.stdpath, vim.uv, vim.F.if_nil local repos, INSTALL = {}, 0 -local buf_set_lines, create_autocmd = api.nvim_buf_set_lines, api.nvim_create_autocmd +local create_autocmd = api.nvim_create_autocmd local packadd = vim.cmd.packadd local exec_autocmds = api.nvim_exec_autocmds local data_dir = stdpath('data') @@ -10,7 +10,7 @@ local STARTDIR = vim.fs.joinpath(data_dir, 'site', 'pack', 'minpm', 'start') local OPTDIR = vim.fs.joinpath(data_dir, 'site', 'pack', 'minpm', 'opt') ---@diagnostic disable-next-line: param-type-mismatch vim.opt.packpath:prepend(vim.fs.joinpath(data_dir, 'site')) -local if_nil = vim.F.if_nil +local window, TaskQueue = require('minpm.win'), require('minpm.task') local function as_table(data) return type(data) ~= 'table' and { data } or data @@ -19,132 +19,77 @@ end local use_meta = {} use_meta.__index = use_meta -function use_meta:when(e) - self.event = as_table(e) - self.islazy = true - local id - id = create_autocmd(e, { +function use_meta:handle_event() + self.auid = create_autocmd(self.event, { + pattern = self.ft or nil, callback = function(args) - if self.remote then - api.nvim_del_autocmd(id) - packadd(self.tail) - exec_autocmds(e, { - modeline = false, - data = args.data, - }) - if self.setup_config then - local module = self.tail:gsub('%.nvim', ''):gsub('-nvim', '') - require(module).setup(self.setup_config) - end + if not self.remote then + return + end + + api.nvim_del_autocmd(self.auid) + packadd(self.tail) + local module = self.tail:gsub('%.nvim$', ''):gsub('-nvim$', ''):gsub('^nvim%-', '') + local m_setup = vim.tbl_get(require(module), 'setup') + if type(m_setup) == 'function' then + m_setup(self.setup_config) + end + + if self.after_config then + self.after_config() end + + exec_autocmds(self.event, { + modeline = false, + data = args.data, + }) end, }) +end + +function use_meta:when(e) + self.event = as_table(e) + self.islazy = true + self:handle_event() return self end function use_meta:lang(ft) self.ft = as_table(ft) + self.event = 'FileType' + self:handle_event() self.islazy = true return self end -function use_meta:setup(config) - self.setup_config = config +function use_meta:dev() + self.isdev = true + self.remote = false return self end -function use_meta:config(config) - assert(type(config) == 'function') - self.config = config +function use_meta:setup(setup_config) + self.setup_config = setup_config return self end -local window = {} -function window:new() - local o = {} - setmetatable(o, self) - self.__index = self - self.content = {} - self.last_row = -1 - return o -end - -function window:create_window() - self.bufnr = api.nvim_create_buf(false, false) - self.winid = api.nvim_open_win(self.bufnr, true, { - relative = 'editor', - height = math.floor(vim.o.lines * 0.5), - width = math.floor(vim.o.columns * 0.8), - row = 3, - col = 10, - border = 'rounded', - noautocmd = true, - style = 'minimal', - }) - vim.wo[self.winid].wrap = false - vim.bo[self.bufnr].buftype = 'nofile' - vim.bo[self.bufnr].bufhidden = 'wipe' - vim.keymap.set('n', 'q', function() - if self.winid and api.nvim_win_is_valid(self.winid) then - api.nvim_win_close(self.winid, true) - self.bufnr, self.winid = nil, nil - end - end, { buffer = self.bufnr, desc = 'quit window' }) -end - -function window:get_row(repo_name) - if not vim.list_contains(self.content, repo_name) then - self.content[#self.content + 1] = repo_name - return #self.content - end - for k, v in ipairs(self.content) do - if v == repo_name then - return k - end - end -end - -function window:write_output(name, data) - local row = self:get_row(name) - 1 - vim.schedule(function() - if not self.bufnr then - self:create_window() - end - vim.bo[self.bufnr].modifiable = true - buf_set_lines(self.bufnr, row, row + 1, false, { ('%s: %s'):format(name, data) }) - vim.bo[self.bufnr].modifiable = false - end) +function use_meta:config(config) + assert(type(config) == 'function') + self.after_config = config + return self end local MAX_CONCURRENT_TASKS = if_nil(vim.g.minpm_max_concurrent_tasks, 2) -local active_tasks = 0 -local task_queue = {} - -local function process_queue() - while active_tasks < MAX_CONCURRENT_TASKS and #task_queue > 0 do - local task = table.remove(task_queue, 1) - active_tasks = active_tasks + 1 - task(function() - active_tasks = active_tasks - 1 - process_queue() - end) - end -end - -local function queue_task(fn) - table.insert(task_queue, fn) - process_queue() -end +local tsq = TaskQueue:new(MAX_CONCURRENT_TASKS) function use_meta:do_action(action, winobj) - queue_task(function(task_done) + tsq:queue_task(function(task_done) local path = vim.fs.joinpath(self.islazy and OPTDIR or STARTDIR, self.tail) local url = ('https://github.com/%s'):format(self.name) local cmd = action == INSTALL and { 'git', 'clone', '--progress', url, path } or { 'git', '-C', path, 'pull', '--progress' } uv.fs_stat(path, function(_, stat) if stat and stat.type == 'directory' then - winobj:write_output(self.name, 'Directory already exists skipped') task_done() return end @@ -173,10 +118,11 @@ function use_meta:do_action(action, winobj) end local function action_wrapper(act) + act = act or INSTALL return function() local winobj = window:new() vim.iter(repos):map(function(repo) - if not repo.remote then + if not repo.remote or repo.isdev then return end repo:do_action(act, winobj) diff --git a/lua/minpm/task.lua b/lua/minpm/task.lua new file mode 100644 index 0000000..1cd6489 --- /dev/null +++ b/lua/minpm/task.lua @@ -0,0 +1,32 @@ +local TaskQueue = {} +TaskQueue.__index = TaskQueue + +function TaskQueue:new(max_concurrent) + return setmetatable({ + active_tasks = 0, + max_concurrent_tasks = max_concurrent or 2, + task_queue = {}, + }, TaskQueue) +end + +function TaskQueue:process_queue() + while self.active_tasks < self.max_concurrent_tasks and #self.task_queue > 0 do + local task = table.remove(self.task_queue, 1) + self.active_tasks = self.active_tasks + 1 + + task(function() + self.active_tasks = self.active_tasks - 1 + self:process_queue() -- Continue processing the queue + end) + end +end + +--- @class ActionFn function +--- +--- @param fn ActionFn +function TaskQueue:queue_task(fn) + table.insert(self.task_queue, fn) + self:process_queue() +end + +return TaskQueue diff --git a/lua/minpm/win.lua b/lua/minpm/win.lua new file mode 100644 index 0000000..b0212cb --- /dev/null +++ b/lua/minpm/win.lua @@ -0,0 +1,67 @@ +local api = vim.api + +local window = {} +function window:new() + local o = {} + setmetatable(o, self) + self.__index = self + self.content = {} + self.last_row = -1 + return o +end +function window:create_buffer() + self.bufnr = api.nvim_create_buf(false, false) + vim.bo[self.bufnr].buftype = 'nofile' + vim.bo[self.bufnr].bufhidden = 'wipe' +end + +function window:set_mappings() + vim.keymap.set('n', 'q', function() + if self.winid and api.nvim_win_is_valid(self.winid) then + api.nvim_win_close(self.winid, true) + self.bufnr, self.winid = nil, nil + end + end, { buffer = self.bufnr, desc = 'quit window' }) +end + +function window:create_window() + self:create_buffer() + self.winid = api.nvim_open_win(self.bufnr, true, { + relative = 'editor', + height = math.floor(vim.o.lines * 0.5), + width = math.floor(vim.o.columns * 0.8), + row = 3, + col = 10, + border = 'rounded', + noautocmd = true, + style = 'minimal', + }) + vim.wo[self.winid].wrap = false + self:set_mappings() +end + +function window:get_row(repo_name) + if not vim.list_contains(self.content, repo_name) then + self.content[#self.content + 1] = repo_name + return #self.content + end + for k, v in ipairs(self.content) do + if v == repo_name then + return k + end + end +end + +function window:write_output(name, data) + local row = self:get_row(name) - 1 + vim.schedule(function() + if not self.bufnr then + self:create_window() + end + vim.bo[self.bufnr].modifiable = true + api.nvim_buf_set_lines(self.bufnr, row, row + 1, false, { ('%s: %s'):format(name, data) }) + vim.bo[self.bufnr].modifiable = false + end) +end + +return window