Files
cave.nvim/lua/cave/project.lua
2024-10-02 14:58:20 +02:00

236 lines
6.2 KiB
Lua

local Path = require "cave.path"
local Util = require "cave.util"
local Meta = require "cave.meta"
local Context = require "cave.context"
local Config = require "cave.config"
local Log = require "cave.log"
local ClassLogger = require "cave.log.class_logger"
local resession = require "resession"
local validate = Meta.validate
local Str = Meta.String
local CONFIG_SCRIPT_TEMPLATE = [[
---@type cave.Config.Builder
local function configure(config)
end
return configure
]]
---@class cave.Project : cave.Context
---@field config_dir cave.Path
---@field config_script cave.Path
---@field config_dir_watch number
---@field config cave.Config?
---@field templates overseer.TemplateDefinition[]
---@field template_order table<string,number>
local Project = Meta.derive("Project", Context)
Project.log = ClassLogger.new(Project)
---@param dir cave.Path
---@param name string
---@param uuid string
function Project:init(dir, name, uuid)
Context.init(self, dir, name, uuid)
self.config_dir = Path.stdpath("data", "cave", "project", uuid):to_absolute() --[[@as cave.Path]]
self.config_script = self.config_dir / "config.lua"
self.templates = {}
self.template_order = {}
end
---@param dir cave.Path
---@param name string
---@param uuid string
---@return cave.Project
function Project.new(dir, name, uuid)
validate { dir = { dir, Path }, name = { name, Str }, uuid = { uuid, Str } }
local project = setmetatable({}, Project)
project:init(dir, name, uuid)
return project
end
---@return boolean
function Project:session_exists()
for _, session in pairs(resession.list()) do
if session == self.uuid then return true end
end
return false
end
---@return boolean
function Project:session_active() return resession.get_current() == self.uuid end
function Project:load_session()
local log = Project.log:call()
if self:session_exists() then
resession.load(self.uuid, { silence_errors = true, notify = false })
else
Util.close_all_buffers()
self:save_session()
end
if not self:session_active() then log:warn "project session activation failed" end
log:ok()
end
function Project:save_session()
local log = Project.log:call()
resession.save(self.uuid, { attach = true, notify = false })
if not self:session_exists() then return log:err "session wasn't created" end
log:ok()
end
function Project:delete_session()
local log = Project.log:call()
if not self:session_exists() then return log:ok() end
resession.delete(self.uuid)
if self:session_exists() then log:err "session wasn't deleted" end
log:ok()
end
function Project:init_config_dir()
local log = Project.log:call()
if not self.config_dir:is_dir() and not self.config_dir:mkdir(Path.const.o755, true) then
return log:err("config dir (%q) creation failed\n%s", self.config_dir, self.config_dir.error_msg)
end
if not self.config_script:exists() and not self.config_script:io_write(CONFIG_SCRIPT_TEMPLATE) then
return log:err("config file (%q) creation failed\n%s", self.config_script, self.config_script.error_msg)
end
log:ok()
end
function Project:load_config()
local log = Project.log:call()
self.config = nil
if not self.config_script:is_file() then
return log:err("config script (%q) missing", self.config_script)
end
local chunk, err = loadfile(self.config_script:tostring())
if chunk == nil then return log:err("config script loading failed\n%s", err) end
local chunk_ok, chunk_res = pcall(chunk)
if not chunk_ok then return log:err("config script failed\n%s", chunk_res) end
local res_type = type(chunk_res)
if res_type ~= "function" then return log:err("config script returned %s instead of function", res_type) end
local config_factory = Config.Factory.new(self)
local configure_ok, configure_res = pcall(chunk_res --[[@as cave.Config.Builder]], config_factory)
if not configure_ok then return log:err("configuring failed\n%s", configure_res) end
local build_config_ok, build_config_res = pcall(Config.Factory.build, config_factory)
if not build_config_ok then return log:err("building config failed\n%s", configure_res) end
self.config = build_config_res
log:ok()
end
function Project:load_templates()
local log = Project.log:call()
self.templates = self.config and self.config:templates() or {}
local task_order = {}
for _, template in ipairs(self.templates) do
local priority = self.template_order[template.name]
if priority ~= nil then
template.priority = priority
task_order[template.name] = priority
end
end
self.template_order = task_order
log:ok()
end
---@param name string
function Project:on_template_run(name)
validate { name = { name, Str } }
local log = Project.log:call()
local priority = -os.clock()
for _, template in ipairs(self.templates) do
if template.name == name then
template.priority = priority
self.template_order[name] = priority
break
end
end
log:ok()
end
function Project:watch_config_dir()
local log = Project.log:call()
if self.config_dir_watch == nil then
self.config_dir_watch = vim.api.nvim_create_autocmd("BufWritePost", {
callback = vim.schedule_wrap(function(event)
if self.config_dir_watch == nil then return end
if Path.new(event.match):to_absolute():is_relative_to(self.config_dir) then self:reload() end
end),
})
end
log:ok()
end
function Project:unwatch_config_dir()
local log = Project.log:call()
if self.config_dir_watch then
vim.api.nvim_del_autocmd(self.config_dir_watch)
self.config_dir_watch = nil
end
log:ok()
end
function Project:reload()
self:load_config()
self:load_templates()
end
function Project:open()
local log = Project.log:call()
self:load_session()
self:init_config_dir()
self:reload()
self:watch_config_dir()
log:ok()
end
function Project:close()
local log = Project.log:call()
self:unwatch_config_dir()
Util.save_all_buffers()
self:save_session()
log:ok()
end
---@param new_name string
function Project:rename(new_name)
validate { new_name = { new_name, Str } }
local log = Project.log:call("%q", new_name)
if self.name == new_name then return log:ok() end
self.name = new_name
if self.config ~= nil then self:reload() end
log:ok()
end
return Project