Skip to content

Commit 2b36c32

Browse files
committed
Add support for completionRequest
1 parent 799e62d commit 2b36c32

File tree

2 files changed

+102
-2
lines changed

2 files changed

+102
-2
lines changed

src/phpDebug.ts

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ class PhpDebugSession extends vscode.DebugSession {
143143
supportsEvaluateForHovers: false,
144144
supportsConditionalBreakpoints: true,
145145
supportsFunctionBreakpoints: true,
146+
supportsCompletionsRequest: true,
146147
exceptionBreakpointFilters: [
147148
{
148149
filter: 'Notice',
@@ -706,6 +707,105 @@ class PhpDebugSession extends vscode.DebugSession {
706707
this.sendResponse(response);
707708
}
708709

710+
protected async completionsRequest(response: VSCodeDebugProtocol.CompletionsResponse, args: VSCodeDebugProtocol.CompletionsArguments) {
711+
try {
712+
if (!args.frameId) {
713+
throw new Error('No stack frame given');
714+
}
715+
const text: string = args.text;
716+
const lineIndex: number = args.line ? args.line - 1 : 0;
717+
const lines: string[] = text.split('\n');
718+
/** The text before the cursor */
719+
const typed: string = [...lines.slice(0, Math.max(lineIndex - 1, 0)), lines[lineIndex].substring(0, args.column)].join('\n');
720+
let i = typed.length;
721+
let containerName: string;
722+
let operator: string | undefined;
723+
let query: string;
724+
while (true) {
725+
const substr = typed.substring(0, i);
726+
if (/\[$/.test(substr)) {
727+
// Numeric array index
728+
operator = '[';
729+
} else if (/\['$/.test(substr)) {
730+
// String array index
731+
operator = `['`;
732+
} else if (/->$/.test(substr)) {
733+
operator = '->';
734+
} else if (i > 0) {
735+
i--;
736+
continue;
737+
}
738+
query = typed.substr(i);
739+
containerName = typed.substring(0, i);
740+
break;
741+
}
742+
const frame = this._stackFrames.get(args.frameId);
743+
const contexts = await frame.getContexts();
744+
const targets: VSCodeDebugProtocol.CompletionItem[] = [];
745+
if (!containerName || !operator) {
746+
const responses = await Promise.all(contexts.map(context => context.getProperties()));
747+
for (const properties of responses) {
748+
for (const property of properties) {
749+
if (property.name.toLowerCase().startsWith(query)) {
750+
targets.push({label: property.name, type: 'variable', start: i, length: property.name.length});
751+
}
752+
}
753+
}
754+
} else {
755+
// Search all contexts
756+
for (const context of contexts) {
757+
let response: xdebug.PropertyGetResponse | undefined;
758+
try {
759+
response = await frame.connection.sendPropertyGetCommand({context, fullName: containerName});
760+
} catch (err) {
761+
// ignore
762+
}
763+
if (response) {
764+
for (const property of response.children) {
765+
if (property.name.toLowerCase().startsWith(query)) {
766+
let type: VSCodeDebugProtocol.CompletionItemType | undefined;
767+
let text: string = property.name;
768+
if (operator === '->') {
769+
// Object
770+
type = 'property';
771+
} else if (operator[0] === '[') {
772+
// Array
773+
if (parseInt(property.name) + '' === property.name) {
774+
// Numeric index
775+
if (operator[1] === `'`) {
776+
continue;
777+
}
778+
type = 'value';
779+
text += ']';
780+
} else {
781+
// String index
782+
if (operator[1] !== `'`) {
783+
if (query) {
784+
continue;
785+
} else {
786+
text = `'` + text;
787+
}
788+
}
789+
type = 'text';
790+
text += `']`;
791+
}
792+
}
793+
targets.push({label: property.name, text, type, start: i, length: property.name.length });
794+
}
795+
}
796+
// If we found the variable in one context (typically Locals), abort
797+
break;
798+
}
799+
}
800+
}
801+
response.body = {targets};
802+
} catch (err) {
803+
this.sendErrorResponse(response, err);
804+
return;
805+
}
806+
this.sendResponse(response);
807+
}
808+
709809
protected async continueRequest(response: VSCodeDebugProtocol.ContinueResponse, args: VSCodeDebugProtocol.ContinueArguments) {
710810
let xdebugResponse: xdebug.StatusResponse | undefined;
711811
try {

src/xdebugConnection.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,7 @@ export class PropertyGetResponse extends Response {
476476
* @param {XMLDocument} document
477477
* @param {Property} property
478478
*/
479-
constructor(document: XMLDocument, property: Property) {
479+
constructor(document: XMLDocument, property: {context: Context}) {
480480
super(document, property.context.stackFrame.connection);
481481
this.children = Array.from(document.documentElement.firstChild.childNodes).map((propertyNode: Element) => new Property(propertyNode, property.context));
482482
}
@@ -782,7 +782,7 @@ export class Connection extends DbgpConnection {
782782
}
783783

784784
/** Sends a property_get command */
785-
public async sendPropertyGetCommand(property: Property): Promise<PropertyGetResponse> {
785+
public async sendPropertyGetCommand(property: {context: Context, fullName: string}): Promise<PropertyGetResponse> {
786786
const escapedFullName = '"' + property.fullName.replace(/("|\\)/g, '\\$1') + '"';
787787
return new PropertyGetResponse(await this._enqueueCommand('property_get', `-d ${property.context.stackFrame.level} -c ${property.context.id} -n ${escapedFullName}`), property);
788788
}

0 commit comments

Comments
 (0)