local Context = require "cave.context" local Meta = require "cave.meta" local Module = require "cave.python.module" local Path = require "cave.path" local Runnable = require "cave.python.runnable" local Script = require "cave.python.script" local Task = require "cave.task" local List = Meta.List local Map = Meta.Map local Str = Meta.String local validate = Meta.validate ---@class cave.Python.Interpreter ---@field id string ---@field path cave.Path ---@field args string[] ---@field env table ---@field runnables table ---@field context cave.Context local Interpreter = Meta.derive "Python.Interpreter" ---@return string function Interpreter:valid_path() return self.path:executable():tostring() end ---@return overseer.TemplateDefinition[] function Interpreter:templates() local templates = {} for _, runnable in pairs(self.runnables) do vim.list_extend(templates, { self:run_template(runnable), self:debug_template(runnable) }) end return templates end ---@param runnable cave.Python.Runnable ---@return overseer.TemplateDefinition function Interpreter:run_template(runnable) local name = ("python.%s.run.%s"):format(self.id, runnable.id) local script, module = runnable:concrete() local args = vim.list_extend({}, self.args) vim.list_extend( args, (script and { script:valid_file() }) or (module and { "-m", module.name }) or error "Unsupported runnable" ) vim.list_extend(args, runnable.args) local template_definition = { name = name, builder = function() ---@type overseer.TaskDefinition local task_definition = { name = name, cmd = { self:valid_path() }, args = args, env = vim.tbl_extend("keep", runnable.env, self.env), cwd = runnable:valid_cwd(), components = { { "defaults" } }, } return task_definition end, params = {}, tags = { Task.Tag.Run }, } return template_definition end ---@param runnable cave.Python.Runnable ---@return overseer.TemplateDefinition function Interpreter:debug_template(runnable) local name = ("python.%s.debug.%s"):format(self.id, runnable.id) local template_definition = {} template_definition.builder = function() local script, module = runnable:concrete() ---@type DebugpyLaunchConfig local dap_config = { name = name, type = "python", request = "launch", module = module and module.name, program = script and script:valid_file(), cwd = runnable:valid_cwd(), args = runnable.args, env = vim.tbl_extend("keep", runnable.env, self.env), python = vim.list_extend({ self:valid_path() }, self.args), } if vim.tbl_isempty(dap_config.env) then dap_config.env = nil end ---@type overseer.TaskDefinition local task_definition = { name = name, cmd = {}, components = { { "defaults" }, { "dap", config = dap_config } }, } return task_definition end template_definition.params = {} template_definition.tags = { Task.Tag.Debug } template_definition.name = name return template_definition end ---@class cave.Python.Interpreter.Factory ---@field id_ string? ---@field path_ cave.Path? ---@field args_ string[] ---@field env_ table ---@field runnables_ cave.Python.Runnable.Factory[] ---@field context_ cave.Context local Factory = Meta.derive "Python.Interpreter.Factory" ---@param context cave.Context function Factory:init(context) self.args_ = {} self.env_ = {} self.runnables_ = {} self.context_ = context end ---@param context cave.Context ---@return cave.Python.Interpreter.Factory function Factory.new(context) validate { context = { context, Context } } local factory = setmetatable({}, Factory) factory:init(context) return factory end ---@return string function Factory:get_id() return self.id_ or "python" end ---@return cave.Path function Factory:get_path() return self.path_ or Path.new "python" end ---@return string[] function Factory:get_args() return self.args_ end ---@return table function Factory:get_env() return self.env_ end ---@return cave.Python.Runnable.Factory[] function Factory:get_runnables() return self.runnables_ end ---@param id string ---@return cave.Python.Interpreter.Factory function Factory:id(id) validate { id = { id, Str } } self.id_ = id return self end ---@param path_like cave.PathLike ---@return cave.Python.Interpreter.Factory function Factory:path(path_like) validate { path_like = { path_like, Path.Like } } self.path_ = Path.like(path_like) return self end ---@param args string[] ---@return cave.Python.Interpreter.Factory function Factory:args(args) validate { args = { args, List(Str) } } vim.list_extend(self.args_, args) return self end ---@param arg string ---@return cave.Python.Interpreter.Factory function Factory:arg(arg) return self:args { arg } end ---@param env table ---@return cave.Python.Interpreter.Factory function Factory:env(env) validate { env = { env, Map(Str, Str) } } vim.tbl_extend("error", self.env_, env) return self end ---@param name string ---@return cave.Python.Module.Factory function Factory:module(name) validate { name = { name, Str } } local module_factory = Module.Factory.new(name, self.context_) table.insert(self.runnables_, module_factory) return module_factory end ---@param file_path_like cave.PathLike ---@return cave.Python.Script.Factory function Factory:script(file_path_like) validate { file = { file_path_like, Path.Like } } local file = Path.like(file_path_like) local script_factory = Script.Factory.new(file, self.context_) table.insert(self.runnables_, script_factory) return script_factory end ---@param runnables cave.Python.Runnable.Factory[] ---@return cave.Python.Interpreter.Factory function Factory:runnables(runnables) validate { runnables = { runnables, List(Runnable.Factory) } } for _, runnable in ipairs(runnables) do table.insert(self.runnables_, runnable:copy()) end return self end ---@param runnable cave.Python.Runnable.Factory ---@return cave.Python.Interpreter.Factory function Factory:runnable(runnable) return self:runnables { runnable } end ---@param interpreter cave.Python.Interpreter function Factory:init_interpreter(interpreter) interpreter.id = self:get_id() interpreter.path = self:get_path() interpreter.args = self:get_args() interpreter.env = self:get_env() interpreter.runnables = {} for _, runnable_factory in pairs(self:get_runnables()) do local runnable = runnable_factory:build() assert(interpreter.runnables[runnable.id] == nil) interpreter.runnables[runnable.id] = runnable end end ---@return cave.Python.Interpreter function Factory:build() local interpreter = setmetatable({}, Interpreter) self:init_interpreter(interpreter) return interpreter end Interpreter.Factory = Factory return Interpreter