Skip to content

Commit 353d37c

Browse files
committed
Implement function breakpoints
1 parent 0e93a01 commit 353d37c

File tree

3 files changed

+82
-0
lines changed

3 files changed

+82
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ What is supported?
3535
------------------
3636
- Line breakpoints
3737
- Conditional breakpoints
38+
- Function breakpoints
3839
- Step over, step in, step out
3940
- Break on entry
4041
- Breaking on uncaught exceptions and errors / warnings / notices

src/phpDebug.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ class PhpDebugSession extends vscode.DebugSession {
129129
response.body.supportsConfigurationDoneRequest = true;
130130
response.body.supportsEvaluateForHovers = false;
131131
response.body.supportsConditionalBreakpoints = true;
132+
response.body.supportsFunctionBreakpoints = true;
132133
response.body.exceptionBreakpointFilters = [
133134
{
134135
filter: 'Notice',
@@ -426,6 +427,56 @@ class PhpDebugSession extends vscode.DebugSession {
426427
});
427428
}
428429

430+
protected setFunctionBreakPointsRequest(response: VSCodeDebugProtocol.SetFunctionBreakpointsResponse, args: VSCodeDebugProtocol.SetFunctionBreakpointsArguments): void {
431+
const connections = Array.from(this._connections.values());
432+
response.body = { breakpoints: [] };
433+
// this is returned to VS Code
434+
let vscodeBreakpoints: VSCodeDebugProtocol.Breakpoint[];
435+
let breakpointsSetPromise: Promise<any>;
436+
if (connections.length === 0) {
437+
// if there are no connections yet, we cannot verify any breakpoint
438+
vscodeBreakpoints = args.breakpoints.map(breakpoint => ({verified: false, message: 'No connection'}));
439+
breakpointsSetPromise = Promise.resolve();
440+
} else {
441+
vscodeBreakpoints = [];
442+
// for all connections
443+
breakpointsSetPromise = Promise.all(connections.map((connection, connectionIndex) =>
444+
// clear breakpoints for this file
445+
connection.sendBreakpointListCommand()
446+
.then(response => Promise.all(
447+
response.breakpoints
448+
.filter(breakpoint => breakpoint instanceof xdebug.CallBreakpoint)
449+
.map(breakpoint => breakpoint.remove())
450+
))
451+
.then(() => Promise.all(
452+
args.breakpoints.map(functionBreakpoint =>
453+
connection.sendBreakpointSetCommand(new xdebug.CallBreakpoint(functionBreakpoint.name, functionBreakpoint.condition))
454+
.then(xdebugResponse => {
455+
// only capture each breakpoint once
456+
if (connectionIndex === 0) {
457+
vscodeBreakpoints.push({verified: true, id: xdebugResponse.breakpointId});
458+
}
459+
})
460+
.catch(error => {
461+
// only capture each breakpoint once
462+
if (connectionIndex === 0) {
463+
vscodeBreakpoints.push({verified: false, message: error.message});
464+
}
465+
})
466+
)
467+
))
468+
));
469+
}
470+
breakpointsSetPromise
471+
.then(() => {
472+
response.body = {breakpoints: vscodeBreakpoints};
473+
this.sendResponse(response);
474+
})
475+
.catch(error => {
476+
this.sendErrorResponse(response, error);
477+
});
478+
}
479+
429480
/** Executed after all breakpoints have been set by VS Code */
430481
protected configurationDoneRequest(response: VSCodeDebugProtocol.ConfigurationDoneResponse, args: VSCodeDebugProtocol.ConfigurationDoneArguments): void {
431482
for (const connection of Array.from(this._waitingConnections)) {

src/xdebugConnection.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ export abstract class Breakpoint {
132132
case 'exception': return new ExceptionBreakpoint(breakpointNode, connection);
133133
case 'line': return new LineBreakpoint(breakpointNode, connection);
134134
case 'conditional': return new ConditionalBreakpoint(breakpointNode, connection);
135+
case 'call': return new CallBreakpoint(breakpointNode, connection);
135136
}
136137
}
137138
/** Constructs a breakpoint object from an XML node from a XDebug response */
@@ -181,6 +182,32 @@ export class LineBreakpoint extends Breakpoint {
181182
}
182183
}
183184

185+
/** class for call breakpoints. Returned from a breakpoint_list or passed to sendBreakpointSetCommand */
186+
export class CallBreakpoint extends Breakpoint {
187+
/** the function to break on */
188+
fn: string;
189+
/** optional expression that must evaluate to true */
190+
expression: string;
191+
/** constructs a call breakpoint from an XML node */
192+
constructor(breakpointNode: Element, connection: Connection);
193+
/** contructs a call breakpoint for passing to sendSetBreakpointCommand */
194+
constructor(fn: string, expression?: string);
195+
constructor() {
196+
if (typeof arguments[0] === 'object') {
197+
const breakpointNode: Element = arguments[0];
198+
const connection: Connection = arguments[1];
199+
super(breakpointNode, connection);
200+
this.fn = breakpointNode.getAttribute('function');
201+
this.expression = breakpointNode.getAttribute('expression'); // Base64 encoded?
202+
} else {
203+
// construct from arguments
204+
this.fn = arguments[0];
205+
this.expression = arguments[1];
206+
super('call');
207+
}
208+
}
209+
}
210+
184211
/** class for exception breakpoints. Returned from a breakpoint_list or passed to sendBreakpointSetCommand */
185212
export class ExceptionBreakpoint extends Breakpoint {
186213
/** The Exception name to break on. Can also contain wildcards. */
@@ -668,6 +695,9 @@ export class Connection extends DbgpConnection {
668695
args += ` -n ${breakpoint.line}`;
669696
}
670697
data = breakpoint.expression;
698+
} else if (breakpoint instanceof CallBreakpoint) {
699+
args += ` -m ${breakpoint.fn}`;
700+
data = breakpoint.expression;
671701
}
672702
return this._enqueueCommand('breakpoint_set', args, data).then(document => new BreakpointSetResponse(document, this));
673703
}

0 commit comments

Comments
 (0)