Files
jpdebug/init.lua

209 lines
5.9 KiB
Lua

-- mod-version:3 -- lite-xl 2.1.8
local core = require "core"
local style = require "core.style"
local command = require "core.command"
local View = require "core.view"
local process = require "process"
local config = require "core.config"
core.jpdebug = core.jpdebug or {}
-- Global list of all the runners
core.jpdebug.runners = core.jpdebug.runners or {}
local runner_shell = require("plugins.jpdebug.runners.shell")
core.jpdebug.runners[runner_shell.name] = runner_shell
-- A list of created views
local active_views = {}
-- The selected target
local selected_target = nil
-- Local helper functions for debugging --------------------------
---@diagnostic disable-next-line: unused-function
local function dump(o)
if type(o) == 'table' then
local s = '{ '
for k,v in pairs(o) do
if type(k) ~= 'number' then k = '"'..k..'"' end
s = s .. '['..k..'] = ' .. dump(v) .. ','
end
return s .. '} '
else
return tostring(o)
end
end
-- ---------- JPDebugView: a simple scrollable log view ----------
---@class JPDebugView : core.view
local JPDebugView = View:extend()
function JPDebugView:new(title)
JPDebugView.super.new(self)
self.scrollable = true
self.caption = title or "JP Debug"
self.lines = { "ready.\n" }
self.max_lines = 5000 -- keep memory bounded
self.font = style.code_font
self.line_h = self.font:get_height()
end
function JPDebugView:get_name()
return self.caption
end
function JPDebugView:get_scrollable_size()
return math.max(#self.lines * self.line_h + style.padding.y * 2, self.size.y)
end
function JPDebugView:push(kind, s)
if not s or s == "" then return end
-- split on newlines; prefix stderr
for line in (s .. "\n"):gmatch("(.-)\n") do
if kind == "stderr" then
line = "[stderr] " .. line
else
line = "[stdout] " .. line
end
self.lines[#self.lines + 1] = line
if #self.lines > self.max_lines then
local drop = #self.lines - self.max_lines
for _ = 1, drop do table.remove(self.lines, 1) end
end
end
-- autoscroll to bottom
self.scroll.to.y = self:get_scrollable_size()
core.redraw = true
end
function JPDebugView:clear()
self.lines = {}
core.redraw = true
end
function JPDebugView:draw()
self:draw_background(style.background)
local ox, oy = self:get_content_offset()
local x = ox + style.padding.x
local y = oy + style.padding.y
renderer.draw_rect(self.position.x, self.position.y, self.size.x, self.size.y, style.background)
for i = 1, #self.lines do
renderer.draw_text(self.font, self.lines[i], x, y, style.text)
y = y + self.line_h
end
self:draw_scrollbar()
end
-- ---------- helper: pick a target from project module ----------
local function get_targets()
local t = (config.plugins and config.plugins.jpdebug and config.plugins.jpdebug.targets) or {}
return t
end
local function get_selected_target()
local t = selected_target or ((config.plugins and config.plugins.jdebug and config.plugins.jpdebug.default_target) or nil)
return t
end
-- ---------- run target & pipe stdout/stderr into the view ----------
local function run_target(target, name)
local title = ("JP Debug: %s"):format(name)
local view = nil
if active_views[title] then
-- If there is already a view use that one
view = active_views[title]
else
-- Otherwhise lets make one
view = JPDebugView(title)
core.root_view:get_active_node():add_view(view)
active_views[title] = view
end
-- Check if we have a runner
for runner_name,runner in pairs(core.jpdebug.runners) do
if runner_name == target.type then
-- Found a runner
local proc = runner:run(target.cmd, {
cwd = target.cwd or ".",
env = target.env or {},
stdout = process.REDIRECT_PIPE,
stderr = process.REDIRECT_PIPE
}, name)
if proc == nil then
core.error("[jpdebug] Could not run the target")
return
end
view:clear()
-- background pump (non-blocking I/O)
core.add_thread(function()
while true do
coroutine.yield(0.016) -- ~60fps
local out = proc:read_stdout()
if out == nil then
-- stdout pipe closed: try drain stderr and break when both closed
local err = proc:read_stderr()
if err ~= nil and err ~= "" then view:push("stderr", err) end
break
end
if out ~= "" then view:push("stdout", out) end
local err = proc:read_stderr()
if err ~= nil and err ~= "" then view:push("stderr", err) end
end
local code = proc:wait(process.WAIT_INFINITE)
view:push("stdout", ("\n[exit] code=%s\n"):format(tostring(code)))
end)
return view
end
end
-- No suitable runners found
core.error("[jpdebug] No suitable runners found for target %s", name)
end
---@diagnostic disable-next-line: param-type-mismatch
command.add(nil, {
-- The run command
["jpdebug:run"] = function()
local targets = get_targets()
local target = get_selected_target()
if target then
if targets[target] then
core.log('[jpdebug] Starting target %s', target)
run_target(targets[target], target)
else
core.error("[jpdebug] Selected target not existing in targets list")
return
end
else
core.error("[jpdebug] No target selected in project module")
return
end
end,
-- The set target command
["jpdebug:settarget"] = function()
core.command_view:enter("Select target", {
show_suggestions = true,
submit = function(selection)
if get_targets()[selection] then
selected_target = selection
else
core.error("[jpdebug] '%s' not a target", selection)
end
end,
suggest = function(_)
local l = {}
for name, _ in pairs(get_targets()) do
table.insert(l, name)
end
return l
end
})
end,
})