Files
mini-debugger/adapter/debugAdapter.js

252 lines
6.7 KiB
JavaScript

const {
LoggingDebugSession,
InitializedEvent,
TerminatedEvent,
OutputEvent,
StoppedEvent,
Thread
} = require('vscode-debugadapter');
const { spawn } = require('child_process');
class MyLuaDebugSession extends LoggingDebugSession {
constructor() {
super("myLuaDebug.txt");
console.log("ADAPTER INICIADO");
this._stackFrames = [];
this.setDebuggerLinesStartAt1(true);
this.setDebuggerColumnsStartAt1(true);
}
initializeRequest(response, args) {
this.sendEvent(new InitializedEvent());
response.body = {
supportsConfigurationDoneRequest: true
};
this.sendResponse(response);
}
pendingStackTrace(frames) {
this._stackFrames = frames;
}
handleLine(line) {
if (line.startsWith("@@DEBUG@@")) {
const json = JSON.parse(line.substring(9));
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 === "stackTrace") {
this.pendingStackTrace(json.payload.frames);
return;
}
return;
}
// salida normal
this.sendEvent(new OutputEvent(line + "\n", "stdout"));
}
launchRequest(response, args) {
const program = args.program;
const cwd = args.cwd || process.cwd();
this.process = spawn(program, [], {
cwd: cwd,
stdio: ['pipe', 'pipe', 'pipe']
});
this.stdoutBuffer = "";
this.process.stdout.on('data', data => {
this.stdoutBuffer += data.toString();
let idx;
while ((idx = this.stdoutBuffer.indexOf("\n")) >= 0) {
const line = this.stdoutBuffer.slice(0, idx).trim();
this.stdoutBuffer = this.stdoutBuffer.slice(idx + 1);
this.handleLine(line);
}
});
this.process.stderr.on('data', data => {
this.sendEvent(new OutputEvent(data.toString(), "stderr"));
});
this.process.on('exit', () => {
this.sendEvent(new TerminatedEvent());
});
this.sendResponse(response);
}
sendDebugCommand(obj) {
if (!this.process || !this.process.stdin) return;
console.log("ENVIANDO CMD:", obj);
const msg = "@@DEBUGCMD@@" + JSON.stringify(obj) + "\n";
this.process.stdin.write(msg);
}
nextRequest(response, args) {
this.sendDebugCommand({ cmd: "stepOver" });
this.sendResponse(response);
}
stepInRequest(response, args) {
this.sendDebugCommand({ cmd: "stepInto" });
this.sendResponse(response);
}
stepOutRequest(response, args) {
this.sendDebugCommand({ cmd: "stepOut" });
this.sendResponse(response);
}
setBreakPointsRequest(response, args) {
const path = args.source.path; // archivo donde se han puesto breakpoints
const clientBreakpoints = args.breakpoints || [];
// Extraemos solo las líneas
const lines = clientBreakpoints.map(bp => bp.line);
// Guardamos internamente los breakpoints
if (!this.breakpoints)
this.breakpoints = {};
this.breakpoints[path] = lines;
// Enviamos los breakpoints a tu aplicación por stdin
const msg = {
cmd: "setBreakpoints",
file: path,
lines: lines
};
this.sendDebugCommand(msg);
//this.process.stdin.write(
// "@@DEBUGCMD@@" + JSON.stringify(msg) + "\n"
//);
// VSCode necesita una respuesta con los breakpoints "verificados"
response.body = {
breakpoints: lines.map(line => ({
verified: true,
line: line
}))
};
this.sendResponse(response);
}
continueRequest(response, args) {
const msg = {
cmd: "continue"
};
this.sendDebugCommand(msg);
//this.process.stdin.write(
// "@@DEBUGCMD@@" + JSON.stringify(msg) + "\n"
//);
this.sendResponse(response);
}
pauseRequest(response, args) {
this.sendDebugCommand({ cmd: "pause" });
this.sendResponse(response);
}
stackTraceRequest(response, args) {
// Pedimos al motor el stack trace
this.sendDebugCommand({ cmd: "stackTrace" });
// IMPORTANTE: el motor responde async, así que esperamos
// a que llegue el mensaje @@DEBUG@@ con type=stackTrace
const check = () => {
if (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();
}
disconnectRequest(response, args) {
const msg = { cmd: "continue" }; // por si estaba pausado
this.sendDebugCommand(msg);
//this.process.stdin.write("@@DEBUGCMD@@" + JSON.stringify(msg) + "\n");
this.process.kill();
this.sendResponse(response);
}
threadsRequest(response) {
// VSCode exige al menos un thread
response.body = {
threads: [
new Thread(1, "Main Thread")
]
};
this.sendResponse(response);
}
stackTraceRequest(response, args) {
response.body = {
stackFrames: [
{
id: 1,
name: "Lua",
source: { path: this.currentFile },
line: this.currentLine,
column: 1
}
],
totalFrames: 1
};
this.sendResponse(response);
}
configurationDoneRequest(response, args) {
this.sendResponse(response);
}
}
new MyLuaDebugSession().start(process.stdin, process.stdout);