local Env = require "cave.env" local Meta = require "cave.meta" local Path = require "cave.path" local Project = require "cave.project" local Util = require "cave.util" local ClassLogger = require "cave.log.class_logger" local TemplateProvider = require "cave.template_provider" local workspaces = require "workspaces" local overseer = require "overseer" local Str = Meta.String local validate = Meta.validate local Optional = Meta.Optional ---@class cave.Manager ---@field project? cave.Project ---@field env cave.Env ---@field template_provider cave.TemplateProvider local Manager = Meta.derive "Manager" Manager.log = ClassLogger.new(Manager) function Manager:init() self.env = Env:new() ---@return overseer.TemplateDefinition[] local function project_templates() return self.project and self.project.templates or {} end self.template_provider = TemplateProvider.new("cave.nvim", project_templates) overseer.register_template(self.template_provider) ---@param task_defn overseer.TaskDefinition ---@param _ overseer.TaskUtil local function on_template_run(task_defn, _) local name = task_defn.name if name == nil or self.project == nil then return end self.project:on_template_run(name) end overseer.add_template_hook(nil, on_template_run) end ---@return cave.Manager function Manager.new() local manager = setmetatable({}, Manager) manager:init() return manager end ---@return table function Manager:get_projects() local projects = {} if self.project ~= nil then projects[self.project.name] = self.project end for _, ws in pairs(workspaces.get()) do local project = Project.new(Path.new(ws.path), ws.name, ws.custom) if projects[project.name] == nil then projects[project.name] = project end end return projects end ---@param lead string ---@return string[] function Manager:project_name_complete(lead) validate { lead = { lead, Str } } local projects = self:get_projects() local project_names = {} for project_name, _ in pairs(projects) do if vim.startswith(project_name, lead) then table.insert(project_names, project_name) end end return project_names end ---@param lead string ---@return string[] function Manager:project_dir_complete(lead) validate { lead = { lead, Str } } local projects = self:get_projects() local project_dirs = {} for _, project in pairs(projects) do local project_dir = tostring(project.dir) if vim.startswith(project_dir, lead) then table.insert(project_dirs, project_dir) end end return project_dirs end ---@param dir string? ---@param name string? function Manager:add_project(dir, name) validate { dir = { dir, Optional(Str) }, name = { name, Optional(Str) } } local log = Manager.log:call("dir=%q, name=%s", dir, name and ("%q"):format(name) or "nil") local project_dir = Path.new(dir or Path.cwd()) assert(project_dir:is_absolute()) local project_name = name or project_dir:basename() local project_uuid = Util.generate_uuid() if not project_dir:is_dir() then return log:err("%q is not a directory", project_dir) end local projects = self:get_projects() local project_exists = projects[project_name] ~= nil if project_exists then return log:err("project with name %q already exists", project_name) end local project = Project.new(project_dir, project_name, project_uuid) workspaces.add(tostring(project.dir), project.name) workspaces.set_custom(project.name, project.uuid) log:ok() end ---@param name string function Manager:remove_project(name) validate { name = { name, Str } } local log = Manager.log:call("name=%q", name) local projects = self:get_projects() local project = projects[name] if project == nil then return log:err "projectdoesn't exist" end if project == self.project then self:close_project() end workspaces.remove(name) log:ok() end ---@param old_name string ---@param new_name string function Manager:rename_project(old_name, new_name) validate { new_name = { new_name, Str }, old_name = { old_name, Str } } local log = Manager.log:call("old_name=%q, new_name=%q", old_name, new_name) if new_name == old_name then return log:ok() end local projects = self:get_projects() local project = projects[old_name] if project == nil then return log:err "project with old name doesn't exist" end if projects[new_name] ~= nil then return log:err "project with new name already exists" end project:rename(new_name) if project == self.project then self.env:update() end workspaces.rename(old_name, new_name) log:ok() end ---@param name string function Manager:open_project(name) validate { name = { name, Str } } local log = Manager.log:call("name=%q", name) if self.project ~= nil and self.project.name == name then return log:ok() end local projects = self:get_projects() local project = projects[name] if project == nil then return log:err "project doesn't exist" end self:close_project() self.project = project self.env:update(self.project) project:open() log:ok() end function Manager:open_project_config() local log = Manager.log:call() if self.project == nil then return log:ok() end vim.cmd.edit(tostring(self.project.config_script)) log:ok() end function Manager:close_project() local log = Manager.log:call() if self.project == nil then return log:ok() end self.project:close() self.project = nil self.env:update() log:ok() end return Manager