Skip to content

Commit 36af27d

Browse files
authored
Properly map method offsets when debugging a class with multiline method arguments (#807)
1 parent bbd00b1 commit 36af27d

File tree

1 file changed

+66
-22
lines changed

1 file changed

+66
-22
lines changed

src/debug/debugSession.ts

Lines changed: 66 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -264,38 +264,62 @@ export class ObjectScriptDebugSession extends LoggingDebugSession {
264264
});
265265

266266
let xdebugBreakpoints: (xdebug.ConditionalBreakpoint | xdebug.ClassLineBreakpoint | xdebug.LineBreakpoint)[] = [];
267+
let symbols: vscode.DocumentSymbol[];
268+
if (fileName.endsWith("cls")) {
269+
// Compute DocumentSymbols for this class
270+
symbols = (
271+
await vscode.commands.executeCommand<vscode.DocumentSymbol[]>("vscode.executeDocumentSymbolProvider", uri)
272+
)[0].children;
273+
}
267274
xdebugBreakpoints = await Promise.all(
268275
args.breakpoints.map(async (breakpoint) => {
269276
const line = breakpoint.line;
270277
if (fileName.endsWith("cls")) {
271-
return await vscode.workspace.openTextDocument(uri).then((document) => {
272-
const methodMatchPattern = new RegExp(`^(?:Class)?Method ([^(]+)(?=[( ])`, "i");
273-
for (let i = line; line > 0; i--) {
274-
const lineOfCode = document.lineAt(i).text;
275-
const methodMatch = lineOfCode.match(methodMatchPattern);
276-
if (methodMatch) {
277-
const [, methodName] = methodMatch;
278+
// Find the class member that this breakpoint is in
279+
let currentSymbol: vscode.DocumentSymbol;
280+
for (const symbol of symbols) {
281+
if (symbol.range.contains(new vscode.Position(line, 0))) {
282+
currentSymbol = symbol;
283+
break;
284+
}
285+
}
286+
if (
287+
currentSymbol !== undefined &&
288+
currentSymbol.kind === vscode.SymbolKind.Method &&
289+
currentSymbol.detail.toLowerCase() !== "query"
290+
) {
291+
// This breakpoint is in a method
292+
const currentdoc = await vscode.workspace.openTextDocument(uri);
293+
for (
294+
let methodlinenum = currentSymbol.selectionRange.start.line;
295+
methodlinenum <= currentSymbol.range.end.line;
296+
methodlinenum++
297+
) {
298+
// Find the offset of this breakpoint in the method
299+
const methodlinetext: string = currentdoc.lineAt(methodlinenum).text.trim();
300+
if (methodlinetext.endsWith("{")) {
301+
// This is the last line of the method definition, so count from here
278302
if (breakpoint.condition) {
279303
return new xdebug.ClassConditionalBreakpoint(
280304
breakpoint.condition,
281305
fileUri,
282306
line,
283-
methodName,
284-
line - i - 2,
307+
currentSymbol.name,
308+
line - methodlinenum - 1,
285309
breakpoint.hitCondition
286310
);
287311
} else {
288312
return new xdebug.ClassLineBreakpoint(
289313
fileUri,
290314
line,
291-
methodName,
292-
line - i - 2,
315+
currentSymbol.name,
316+
line - methodlinenum - 1,
293317
breakpoint.hitCondition
294318
);
295319
}
296320
}
297321
}
298-
});
322+
}
299323
} else if (filePath.endsWith("mac") || filePath.endsWith("int")) {
300324
if (breakpoint.condition) {
301325
return new xdebug.RoutineConditionalBreakpoint(
@@ -450,25 +474,45 @@ export class ObjectScriptDebugSession extends LoggingDebugSession {
450474
const [, namespace, name] = decodeURI(stackFrame.fileUri).match(/^dbgp:\/\/\|([^|]+)\|(.*)$/);
451475
const routine = name;
452476
// const routine = name.includes(".") ? name : name + ".int";
453-
const fileUri = DocumentContentProvider.getUri(routine, this._workspace, namespace).toString();
454-
const source = new Source(routine, fileUri);
477+
const fileUri = DocumentContentProvider.getUri(routine, this._workspace, namespace);
478+
const source = new Source(routine, fileUri.toString());
455479
let line = stackFrame.line + 1;
456480
const place = `${stackFrame.method}+${stackFrame.methodOffset}`;
457481
const stackFrameId = this._stackFrameIdCounter++;
458482
let noSource = false;
459483
try {
460-
const document = await vscode.workspace.openTextDocument(vscode.Uri.parse(source.path));
461484
if (source.name.endsWith(".cls") && stackFrame.method !== "") {
462-
const methodMatchPattern = new RegExp(`^(Class)?Method ${stackFrame.method}(?=[( ])`, "i");
463-
for (let i = 0; i < document.lineCount; i++) {
464-
const codeLine = document.lineAt(i);
465-
466-
const methodMatch = codeLine.text.match(methodMatchPattern);
467-
if (methodMatch) {
468-
line = i + 2 + stackFrame.methodOffset;
485+
// Compute DocumentSymbols for this class
486+
const symbols: vscode.DocumentSymbol[] = (
487+
await vscode.commands.executeCommand<vscode.DocumentSymbol[]>(
488+
"vscode.executeDocumentSymbolProvider",
489+
fileUri
490+
)
491+
)[0].children;
492+
// Find the DocumentSymbol for this method
493+
let currentSymbol: vscode.DocumentSymbol;
494+
for (const symbol of symbols) {
495+
if (symbol.name === stackFrame.method && symbol.detail.toLowerCase().includes("method")) {
496+
currentSymbol = symbol;
469497
break;
470498
}
471499
}
500+
if (currentSymbol !== undefined) {
501+
const currentdoc = await vscode.workspace.openTextDocument(fileUri);
502+
for (
503+
let methodlinenum = currentSymbol.selectionRange.start.line;
504+
methodlinenum <= currentSymbol.range.end.line;
505+
methodlinenum++
506+
) {
507+
// Find the offset of this breakpoint in the method
508+
const methodlinetext: string = currentdoc.lineAt(methodlinenum).text.trim();
509+
if (methodlinetext.endsWith("{")) {
510+
// This is the last line of the method definition, so count from here
511+
line = methodlinenum + stackFrame.methodOffset + 1;
512+
break;
513+
}
514+
}
515+
}
472516
}
473517
this._stackFrames.set(stackFrameId, stackFrame);
474518
} catch (ex) {

0 commit comments

Comments
 (0)