local core = require "core" local process = require "process" local debugger = require "plugins.jpdebug.debugger" -- tiny helpers local function dirname(p) return p:match("^(.*)[/\\]") or "" end local function join(a, b) return (a:sub(-1) == "/" and a or (a .. "/")) .. b end -- returns absolute path to the plugin root (…/jpdebug) local function get_plugin_root() -- debug.getinfo(1, "S").source gives "@/full/path/to/this/file.lua" local src = debug.getinfo(1, "S").source if src:sub(1, 1) == "@" then src = src:sub(2) end local here = dirname(src) -- …/jpdebug/runners return dirname(here) -- …/jpdebug end -- ------------------- Runner API -------------------------------- ---@class luadebug: runner local luadebug = debugger.runner:new({ name = "luadebug", proc = nil ---@type process|nil }) function luadebug:run(target, name) if target.entry == nil then self.error("target.entry is required for "..name) return end local entry = target.entry local lua = target.lua or "lua" local cwd = target.cwd or "." local env = target.env or {} -- spawn the debugger self:spawn_mdb(lua) -- TODO error checking -- spawn the main lua process local proc = self:spawn_lua_process(entry, lua, cwd, env) if proc == nil then self.error("Failed to start "..entry) -- TODO kill mdb return end self.proc = proc self.on_state('connecting') -- output pump core.add_thread(function() while true do core.redraw = true coroutine.yield(0.016) -- 60FPS local sout = self.proc:read_stdout() local serr = self.proc:read_stderr() if sout == nil or serr == nil then -- Make sure to read stderr for the last time if serr and serr~="" then self.on_stderr(serr) end local exitcode = self.proc:wait(process.WAIT_INFINITE) self.on_exit(exitcode) break end if sout and sout~="" then self.on_stdout(sout) end if serr and serr~="" then self.on_stderr(serr) end end end) end function luadebug:wait(time) if not self.proc then return end return self.proc:wait(time) end function luadebug:read_stdout() if not self.proc then return end local sl = self.proc:read_stdout() return sl end function luadebug:read_stderr() if not self.proc then return end local sl = self.proc:read_stderr() return sl end function luadebug:kill() if not self.proc then return end self.proc:kill() end function luadebug:terminate() if not self.proc then return end self.proc:terminate() end -- --------------- Lua Process ----------------------------------- -- Main lua process spawner function luadebug:spawn_lua_process(entry, lua, cwd, env) local host = "localhost" local port = 8172 -- Resolve vendor/?.lua so "require('mobdebug')" finds the bundled file local vendor_glob = join(get_plugin_root(), "vendor/?.lua") -- Build the inline lua launcher local dbg_call = string.format([[ local ok, m = pcall(require, "mobdebug"); if ok then print("Connecting to "..%q..":"..tostring(%d)) m.connecttimeout = 0.1 local connected = m.start(%q, %d) if not connecten then m.off() end end print("Test123") ]], host, port, host, port) local launcher = string.format([[ package.path = %q .. ";" .. package.path %s dofile(%q) os.exit() ]], vendor_glob, dbg_call, entry) -- Spawn the process local cmd = {} if type(lua) == "table" then for i=1, #lua do table.insert(cmd, lua[i]) end else table.insert(cmd, lua) end table.insert(cmd, "-e") table.insert(cmd, launcher) local proc = process.start(cmd, { cwd = cwd, env = env, stdout = process.REDIRECT_PIPE, stderr = process.REDIRECT_PIPE, }) return proc end -- ---------------- Mobdebug ------------------------------------- function luadebug:spawn_mdb(lua) local host = "localhost" local port = 8172 -- Resolve vendor/?.lua so "require('mobdebug')" finds the bundled file local vendor_glob = join(get_plugin_root(), "vendor/?.lua") local cmd = {} if type(lua) == "table" then for i=1, #lua do table.insert(cmd, lua[i]) end else table.insert(cmd, lua) end table.insert(cmd, "-e") table.insert(cmd, string.format([[ package.path = %q .. ";" .. package.path require("mobdebug").listen(%s, %d) os.exit() ]], vendor_glob, host, port)) local proc = process.start(cmd, { stdout = process.REDIRECT_PIPE, stderr = process.REDIRECT_PIPE, }) if proc == nil then self.error("Failed to start debugger") return end self.mdb = proc self.mdb:write('run\n') -- output pump core.add_thread(function() while true do core.redraw = true coroutine.yield(0.016) -- 60FPS local sout = self.mdb:read_stdout() local serr = self.mdb:read_stderr() if sout == nil or serr == nil then -- Make sure to read stderr for the last time if serr and serr~="" then self.on_stderr(serr) end local exitcode = self.mdb:wait(process.WAIT_INFINITE) self.on_exit(exitcode) break end if sout and sout~="" then self.on_stdout('mdb> '..sout) end if serr and serr~="" then self.on_stderr('mdb> '..serr) end end end) end return luadebug