From 068e259c4eca382029af8db2792889cb550c9869 Mon Sep 17 00:00:00 2001 From: Raimon Zamora Date: Mon, 30 Mar 2026 23:05:38 +0200 Subject: [PATCH] =?UTF-8?q?-=20[NEW]=20pause,=20resume=20-=20[NEW]=20stack?= =?UTF-8?q?Trace=20-=20[NEW]=20stepIn,=20stepOut,=20stepOver=20-=20[NEW]?= =?UTF-8?q?=20variables=20locals,=20upvalues=20i=20globals=20-=20[NEW]=20E?= =?UTF-8?q?xpansi=C3=B3=20de=20tables?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- adapter/debugAdapter.js | 280 +++++++++++++++++++++++++++++++++++----- 1 file changed, 245 insertions(+), 35 deletions(-) diff --git a/adapter/debugAdapter.js b/adapter/debugAdapter.js index ce10d63..36d214b 100644 --- a/adapter/debugAdapter.js +++ b/adapter/debugAdapter.js @@ -4,26 +4,72 @@ const { TerminatedEvent, OutputEvent, StoppedEvent, - Thread -} = require('vscode-debugadapter'); + ThreadEvent, + Thread, + Scope, + Variable + } = require('vscode-debugadapter'); const { spawn } = require('child_process'); class MyLuaDebugSession extends LoggingDebugSession { - constructor() { - super("myLuaDebug.txt"); - console.log("ADAPTER INICIADO"); +constructor() { + super(); + + // Redirigir console.log a la Debug Console + console.log = (...args) => { this.sendEvent(new OutputEvent(args.join(" ") + "\n")); }; + console.log("Debug Adapter iniciado"); this._stackFrames = []; this.setDebuggerLinesStartAt1(true); this.setDebuggerColumnsStartAt1(true); + this._pendingLocals = null; + this._pendingUpvalues = null; + this._pendingGlobals = null; + this._refTable = new Map(); // ref → { luaRef } + this._nextRef = 1000; // IDs únicos para tablas } + //initializeRequest(response, args) { + // this.sendEvent(new InitializedEvent()); +// + // response.body = { + // supportsConfigurationDoneRequest: true + // }; +// + // this.sendResponse(response); + //} + initializeRequest(response, args) { + console.log("[ADAPTER] initializeRequest"); this.sendEvent(new InitializedEvent()); response.body = { - supportsConfigurationDoneRequest: true + supportsConfigurationDoneRequest: true, + supportsEvaluateForHovers: true, + supportsStepBack: false, + supportsSetVariable: false, + supportsRestartFrame: false, + supportsGotoTargetsRequest: false, + supportsStepInTargetsRequest: false, + supportsCompletionsRequest: false, + supportsRestartRequest: false, + supportsExceptionInfoRequest: false, + supportsDelayedStackTraceLoading: false, + supportsLoadedSourcesRequest: false, + supportsLogPoints: false, + supportsTerminateRequest: true, + supportsTerminateDebuggee: true, + supportsFunctionBreakpoints: false, + supportsConditionalBreakpoints: false, + supportsHitConditionalBreakpoints: false, + supportsConfigurationDoneRequest: true, + supportsSetExpression: false, + supportsClipboardContext: false, + supportsValueFormattingOptions: false, + supportsExceptionOptions: false, + supportsExceptionFilterOptions: false, + supportsSingleThreadExecutionRequests: true }; this.sendResponse(response); @@ -34,30 +80,116 @@ class MyLuaDebugSession extends LoggingDebugSession { } handleLine(line) { - if (line.startsWith("@@DEBUG@@")) { - const json = JSON.parse(line.substring(9)); + console.log("[ADAPTER] RAW LINE:", JSON.stringify(line)); - if (json.type === "luaError") { - const e = new OutputEvent(json.message + "\n", "stderr"); - e.body.source = { path: json.file }; - e.body.line = json.line; - this.sendEvent(e); + if (!line.startsWith("@@DEBUG@@")) { + console.log("[ADAPTER] Ignorando línea no-debug"); + return; + } + + const jsonStr = line.substring(9); + console.log("[ADAPTER] JSON A PARSEAR:", jsonStr); + + let json; + try { + json = JSON.parse(jsonStr); + } catch (e) { + console.log("[ADAPTER] ERROR PARSEANDO JSON:", e.message); + return; + } + + console.log("[ADAPTER] JSON OK:", json); + console.log("[ADAPTER] TYPE:", json.type); + + if (json.type === "luaError") { + const e = new OutputEvent(json.message + "\n", "stderr"); + e.body.source = { path: json.file }; + e.body.line = json.line; + this.sendEvent(e); + return; + } + + if (json.type === "break") { + this.currentFile = json.file; + this.currentLine = json.line; + + this.sendEvent(new StoppedEvent("breakpoint", 1)); + return; + } + if (json.type === "step") { + this.sendEvent(new StoppedEvent("step", 1)); + return; + } + + if (json.type === "pause") { + this.sendEvent(new StoppedEvent("pause", 1)); + return; + } + + if (json.type === "stackTrace") { + console.log("[ADAPTER] STACKTRACE PAYLOAD:", JSON.stringify(json.payload)); + console.log("[ADAPTER] BEFORE HANDLING STACKTRACE"); + this.pendingStackTrace(json.payload.frames); + console.log("[ADAPTER] AFTER HANDLING STACKTRACE"); + return; + } + + if (json.type === "variables") { + console.log("[ADAPTER] VARIABLES PAYLOAD:", JSON.stringify(json.payload)); + + // 1. Convertimos las variables del motor a formato DAP + const vars = json.payload.variables.map(v => { + if (v.type === "table" && v.ref > 0) { + const id = this._nextRef++; + this._refTable.set(id, v.ref); + return { + name: v.name, + value: v.value, + type: v.type, + variablesReference: id + }; + } + + return { + name: v.name, + value: v.value, + type: v.type, + variablesReference: 0 + }; + }); + + // 2. Seleccionamos el pendingResponse correcto según el kind + let response = null; + + if (json.payload.kind === "locals") { + response = this._pendingLocals; + this._pendingLocals = null; + + } else if (json.payload.kind === "upvalues") { + response = this._pendingUpvalues; + this._pendingUpvalues = null; + + } else if (json.payload.kind === "globals") { + response = this._pendingGlobals; + this._pendingGlobals = null; + + } else if (json.payload.kind === "expand") { + response = this._pendingExpand; + this._pendingExpand = null; + + } else { + console.log("[ADAPTER] VARIABLES: kind desconocido:", json.payload.kind); return; } - if (json.type === "break") { - this.currentFile = json.file; - this.currentLine = json.line; - - this.sendEvent(new StoppedEvent("breakpoint", 1)); - return; - } - - if (json.type === "stackTrace") { - this.pendingStackTrace(json.payload.frames); + if (!response) { + console.log("[ADAPTER] VARIABLES: no pending response para kind", json.payload.kind); return; } + // 3. Enviamos la respuesta a VSCode + response.body = { variables: vars }; + this.sendResponse(response); return; } @@ -66,6 +198,8 @@ class MyLuaDebugSession extends LoggingDebugSession { } launchRequest(response, args) { + console.log("[ADAPTER] launchRequest"); + const program = args.program; const cwd = args.cwd || process.cwd(); @@ -74,6 +208,8 @@ class MyLuaDebugSession extends LoggingDebugSession { stdio: ['pipe', 'pipe', 'pipe'] }); + this.sendEvent(new ThreadEvent("started", 1)); + this.stdoutBuffer = ""; this.process.stdout.on('data', data => { @@ -101,8 +237,8 @@ class MyLuaDebugSession extends LoggingDebugSession { sendDebugCommand(obj) { if (!this.process || !this.process.stdin) return; - console.log("ENVIANDO CMD:", obj); const msg = "@@DEBUGCMD@@" + JSON.stringify(obj) + "\n"; + console.log("ENVIANDO CMD:", msg); this.process.stdin.write(msg); } @@ -227,22 +363,96 @@ class MyLuaDebugSession extends LoggingDebugSession { } stackTraceRequest(response, args) { - response.body = { - stackFrames: [ - { - id: 1, - name: "Lua", - source: { path: this.currentFile }, - line: this.currentLine, - column: 1 - } - ], - totalFrames: 1 + console.log("[ADAPTER] stackTraceRequest"); + + // Pedimos el stack trace al motor + this.sendDebugCommand({ cmd: "stackTrace" }); + + // Esperamos la respuesta del motor + const check = () => { + if (this._stackFrames && this._stackFrames.length > 0) { + const frames = this._stackFrames; + this._stackFrames = []; + + response.body = { + stackFrames: frames.map((f, i) => ({ + id: i + 1, + name: f.name, + source: { path: f.file }, + line: f.line, + column: 1 + })), + totalFrames: frames.length + }; + + this.sendResponse(response); + } else { + setTimeout(check, 5); + } }; + + check(); + } + + // Aún no tiene en cuenta el frame + scopesRequest(response, args) { + console.log("[ADAPTER] scopesRequest, frameId =", args.frameId); + + response.body = { + scopes: [ + new Scope("Locals", 1, false), + new Scope("Upvalues", 2, false), + new Scope("Globals", 3, true) + ] + }; + + this.sendResponse(response); + } + + variablesRequest(response, args) { + console.log("[ADAPTER] variablesRequest ref =", args.variablesReference); + + if (args.variablesReference === 1) { + this._pendingLocals = response; + this.sendDebugCommand({ cmd: "locals" }); + return; + } + + if (args.variablesReference === 2) { + this._pendingUpvalues = response; + this.sendDebugCommand({ cmd: "upvalues" }); + return; + } + + if (args.variablesReference === 3) { + this._pendingGlobals = response; + this.sendDebugCommand({ cmd: "globals" }); + return; + } + + // Expansión de tablas + if (args.variablesReference >= 1000) { + const luaRef = this._refTable.get(args.variablesReference); + + if (!luaRef) { + console.log("[ADAPTER] No existe luaRef para", args.variablesReference); + response.body = { variables: [] }; + this.sendResponse(response); + return; + } + + this._pendingExpand = response; + this.sendDebugCommand({ cmd: "expand", ref: luaRef }); + return; + } + + // de momento, resto vacío + response.body = { variables: [] }; this.sendResponse(response); } configurationDoneRequest(response, args) { + console.log("[ADAPTER] configurationDoneRequest"); this.sendResponse(response); }