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

237 lines
6.8 KiB
Lua

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<string, string>
---@field runnables table<string, cave.Python.Runnable>
---@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<string, string>
---@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<string, string>
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<string, string>
---@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