Skip to content

Commit 0850110

Browse files
authored
Merge pull request #197 from intersystems-community/debugger
debugging improvements
2 parents 683daec + 7b726e3 commit 0850110

File tree

3 files changed

+122
-77
lines changed

3 files changed

+122
-77
lines changed

src/api/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,8 @@ export class AtelierAPI {
111111
}
112112

113113
public get active(): boolean {
114-
return !!this._config.active;
114+
const { host = "", port = 0 } = this.config;
115+
return !!this._config.active && host.length > 0 && port > 0;
115116
}
116117

117118
private get cookies(): string[] {

src/debug/debugAdapterFactory.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ export class ObjectScriptDebugAdapterDescriptorFactory
1212
): vscode.ProviderResult<vscode.DebugAdapterDescriptor> {
1313
if (!this.server) {
1414
// start listening on a random port
15+
const debugSession = new ObjectScriptDebugSession();
16+
1517
this.server = net
1618
.createServer((socket) => {
17-
const debugSession = new ObjectScriptDebugSession();
1819
debugSession.setRunAsServer(true);
1920
debugSession.start(socket as NodeJS.ReadableStream, socket);
2021
})

src/debug/debugSession.ts

Lines changed: 118 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { Subject } from "await-notify";
44
import {
55
InitializedEvent,
66
LoggingDebugSession,
7+
ErrorDestination,
78
OutputEvent,
89
StoppedEvent,
910
ThreadEvent,
@@ -17,7 +18,7 @@ import { DebugProtocol } from "vscode-debugprotocol";
1718
import WebSocket = require("ws");
1819
import { AtelierAPI } from "../api";
1920
import * as xdebug from "./xdebugConnection";
20-
import { FILESYSTEM_SCHEMA, FILESYSTEM_READONLY_SCHEMA } from "../extension";
21+
import { schemas } from "../extension";
2122
import * as url from "url";
2223
import { DocumentContentProvider } from "../providers/DocumentContentProvider";
2324
import { formatPropertyValue } from "./utils";
@@ -40,7 +41,7 @@ interface AttachRequestArguments extends DebugProtocol.AttachRequestArguments {
4041
export async function convertClientPathToDebugger(localPath: string, namespace: string): Promise<string> {
4142
const { protocol, pathname, query } = url.parse(decodeURIComponent(localPath), true, true);
4243
let fileName = localPath;
43-
if (protocol && (protocol === `${FILESYSTEM_SCHEMA}:` || protocol === `${FILESYSTEM_READONLY_SCHEMA}:`)) {
44+
if (protocol && schemas.includes(protocol.slice(0, -1))) {
4445
if (query.ns && query.ns !== "") {
4546
namespace = query.ns.toString();
4647
}
@@ -88,6 +89,9 @@ export class ObjectScriptDebugSession extends LoggingDebugSession {
8889
super();
8990

9091
const api = new AtelierAPI();
92+
if (!api.active) {
93+
throw new Error("Connection not active");
94+
}
9195
this._namespace = api.ns;
9296
this._url = api.xdebugUrl();
9397

@@ -141,19 +145,27 @@ export class ObjectScriptDebugSession extends LoggingDebugSession {
141145
protected async launchRequest(response: DebugProtocol.LaunchResponse, args: LaunchRequestArguments): Promise<void> {
142146
// this._args = args;
143147

144-
const debugTarget = `${this._namespace}:${args.program}`;
145-
await this._connection.sendFeatureSetCommand("debug_target", debugTarget);
146-
147-
this._debugTargetSet.notify();
148+
try {
149+
const debugTarget = `${this._namespace}:${args.program}`;
150+
await this._connection.sendFeatureSetCommand("debug_target", debugTarget);
148151

152+
this._debugTargetSet.notify();
153+
} catch (error) {
154+
this.sendErrorResponse(response, error);
155+
return;
156+
}
149157
this.sendResponse(response);
150158
}
151159

152160
protected async attachRequest(response: DebugProtocol.AttachResponse, args: AttachRequestArguments): Promise<void> {
153-
const debugTarget = `PID:${args.processId}`;
154-
await this._connection.sendFeatureSetCommand("debug_target", debugTarget);
155-
this._debugTargetSet.notify();
156-
161+
try {
162+
const debugTarget = `PID:${args.processId}`;
163+
await this._connection.sendFeatureSetCommand("debug_target", debugTarget);
164+
this._debugTargetSet.notify();
165+
} catch (error) {
166+
this.sendErrorResponse(response, error);
167+
return;
168+
}
157169
this.sendResponse(response);
158170
}
159171

@@ -202,74 +214,79 @@ export class ObjectScriptDebugSession extends LoggingDebugSession {
202214
response: DebugProtocol.SetBreakpointsResponse,
203215
args: DebugProtocol.SetBreakpointsArguments
204216
): Promise<void> {
205-
await this._debugTargetSet.wait(1000);
206-
207-
const filePath = args.source.path;
208-
const uri =
209-
filePath.startsWith(FILESYSTEM_SCHEMA) || filePath.startsWith(FILESYSTEM_READONLY_SCHEMA)
210-
? vscode.Uri.parse(filePath)
211-
: vscode.Uri.file(filePath);
212-
const fileUri = await convertClientPathToDebugger(args.source.path, this._namespace);
213-
const [, fileName] = fileUri.match(/\|([^|]+)$/);
214-
215-
const currentList = await this._connection.sendBreakpointListCommand();
216-
currentList.breakpoints
217-
.filter((breakpoint) => {
218-
if (breakpoint instanceof xdebug.LineBreakpoint) {
219-
return breakpoint.fileUri === fileName;
220-
}
221-
return false;
222-
})
223-
.map((breakpoint) => {
224-
this._connection.sendBreakpointRemoveCommand(breakpoint);
225-
});
226-
227-
let xdebugBreakpoints: (xdebug.ConditionalBreakpoint | xdebug.ClassLineBreakpoint | xdebug.LineBreakpoint)[] = [];
228-
xdebugBreakpoints = await Promise.all(
229-
args.breakpoints.map(async (breakpoint) => {
230-
const line = breakpoint.line;
231-
if (breakpoint.condition) {
232-
return new xdebug.ConditionalBreakpoint(breakpoint.condition, fileUri, line);
233-
} else if (fileName.endsWith("cls")) {
234-
return await vscode.workspace.openTextDocument(uri).then((document) => {
235-
const methodMatchPattern = new RegExp(`^(?:Class)?Method ([^(]+)(?=[( ])`, "i");
236-
for (let i = line; line > 0; i--) {
237-
const lineOfCode = document.lineAt(i).text;
238-
const methodMatch = lineOfCode.match(methodMatchPattern);
239-
if (methodMatch) {
240-
const [, methodName] = methodMatch;
241-
return new xdebug.ClassLineBreakpoint(fileUri, line, methodName, line - i - 2);
217+
try {
218+
await this._debugTargetSet.wait(1000);
219+
220+
const filePath = args.source.path;
221+
const { protocol } = url.parse(decodeURIComponent(filePath), true, true);
222+
const uri =
223+
protocol && schemas.includes(protocol.slice(0, -1)) ? vscode.Uri.parse(filePath) : vscode.Uri.file(filePath);
224+
const fileUri = await convertClientPathToDebugger(args.source.path, this._namespace);
225+
const [, fileName] = fileUri.match(/\|([^|]+)$/);
226+
227+
const currentList = await this._connection.sendBreakpointListCommand();
228+
currentList.breakpoints
229+
.filter((breakpoint) => {
230+
if (breakpoint instanceof xdebug.LineBreakpoint) {
231+
return breakpoint.fileUri === fileName;
232+
}
233+
return false;
234+
})
235+
.map((breakpoint) => {
236+
this._connection.sendBreakpointRemoveCommand(breakpoint);
237+
});
238+
239+
let xdebugBreakpoints: (xdebug.ConditionalBreakpoint | xdebug.ClassLineBreakpoint | xdebug.LineBreakpoint)[] = [];
240+
xdebugBreakpoints = await Promise.all(
241+
args.breakpoints.map(async (breakpoint) => {
242+
const line = breakpoint.line;
243+
if (breakpoint.condition) {
244+
return new xdebug.ConditionalBreakpoint(breakpoint.condition, fileUri, line);
245+
} else if (fileName.endsWith("cls")) {
246+
return await vscode.workspace.openTextDocument(uri).then((document) => {
247+
const methodMatchPattern = new RegExp(`^(?:Class)?Method ([^(]+)(?=[( ])`, "i");
248+
for (let i = line; line > 0; i--) {
249+
const lineOfCode = document.lineAt(i).text;
250+
const methodMatch = lineOfCode.match(methodMatchPattern);
251+
if (methodMatch) {
252+
const [, methodName] = methodMatch;
253+
return new xdebug.ClassLineBreakpoint(fileUri, line, methodName, line - i - 2);
254+
}
242255
}
243-
}
244-
});
245-
} else if (filePath.endsWith("mac") || filePath.endsWith("int")) {
246-
return new xdebug.RoutineLineBreakpoint(fileUri, line, "", line - 1);
247-
} else {
248-
return new xdebug.LineBreakpoint(fileUri, line);
249-
}
250-
})
251-
);
256+
});
257+
} else if (filePath.endsWith("mac") || filePath.endsWith("int")) {
258+
return new xdebug.RoutineLineBreakpoint(fileUri, line, "", line - 1);
259+
} else {
260+
return new xdebug.LineBreakpoint(fileUri, line);
261+
}
262+
})
263+
);
264+
265+
const vscodeBreakpoints: DebugProtocol.Breakpoint[] = [];
266+
await Promise.all(
267+
xdebugBreakpoints.map(async (breakpoint, index) => {
268+
try {
269+
await this._connection.sendBreakpointSetCommand(breakpoint);
270+
vscodeBreakpoints[index] = { verified: true, line: breakpoint.line };
271+
} catch (error) {
272+
vscodeBreakpoints[index] = {
273+
verified: false,
274+
line: breakpoint.line,
275+
message: error.message,
276+
};
277+
}
278+
})
279+
);
252280

253-
const vscodeBreakpoints: DebugProtocol.Breakpoint[] = [];
254-
await Promise.all(
255-
xdebugBreakpoints.map(async (breakpoint, index) => {
256-
try {
257-
await this._connection.sendBreakpointSetCommand(breakpoint);
258-
vscodeBreakpoints[index] = { verified: true, line: breakpoint.line };
259-
} catch (error) {
260-
vscodeBreakpoints[index] = {
261-
verified: false,
262-
line: breakpoint.line,
263-
message: error.message,
264-
};
265-
}
266-
})
267-
);
281+
// send back the actual breakpoint positions
282+
response.body = {
283+
breakpoints: vscodeBreakpoints,
284+
};
285+
} catch (error) {
286+
this.sendErrorResponse(response, error);
287+
return;
288+
}
268289

269-
// send back the actual breakpoint positions
270-
response.body = {
271-
breakpoints: vscodeBreakpoints,
272-
};
273290
this.sendResponse(response);
274291
}
275292

@@ -533,4 +550,30 @@ export class ObjectScriptDebugSession extends LoggingDebugSession {
533550
};
534551
this.sendResponse(response);
535552
}
553+
554+
protected sendErrorResponse(response: DebugProtocol.Response, error: Error, dest?: ErrorDestination): void;
555+
protected sendErrorResponse(
556+
response: DebugProtocol.Response,
557+
codeOrMessage: number | DebugProtocol.Message,
558+
format?: string,
559+
variables?: any,
560+
dest?: ErrorDestination
561+
): void;
562+
protected sendErrorResponse(response: DebugProtocol.Response, ...rest): void {
563+
if (rest[0] instanceof Error) {
564+
const error = rest[0] as Error & { code?: number | string; errno?: number };
565+
const dest = rest[1] as ErrorDestination;
566+
let code: number;
567+
if (typeof error.code === "number") {
568+
code = error.code as number;
569+
} else if (typeof error.errno === "number") {
570+
code = error.errno;
571+
} else {
572+
code = 0;
573+
}
574+
super.sendErrorResponse(response, code, error.message, dest);
575+
} else {
576+
super.sendErrorResponse(response, rest[0], rest[1], rest[2], rest[3]);
577+
}
578+
}
536579
}

0 commit comments

Comments
 (0)