Skip to content

Commit

Permalink
Add support for completionRequest
Browse files Browse the repository at this point in the history
  • Loading branch information
felixfbecker authored and zobo committed Jan 18, 2022
1 parent aae86d5 commit 98bd782
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ Options specific to CLI debugging:
- Stack traces, scope variables, superglobals, user defined constants
- Arrays & objects (including classname, private and static properties)
- Debug console
- Autocompletion in debug console for variables, array indexes, object properties (even nested)
- Watches
- Run as CLI
- Run without debugging
Expand Down
100 changes: 100 additions & 0 deletions src/phpDebug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -993,6 +993,106 @@ class PhpDebugSession extends vscode.DebugSession {
this.sendResponse(response)
}

protected async completionsRequest(response: VSCodeDebugProtocol.CompletionsResponse, args: VSCodeDebugProtocol.CompletionsArguments) {
try {
if (!args.frameId) {
throw new Error('No stack frame given');
}
const lineIndex: number = args.line ? args.line - 1 : 0;
const lines: string[] = args.text.split('\n');
/** The text before the cursor */
const typed: string = [...lines.slice(0, Math.max(lineIndex - 1, 0)), lines[lineIndex].substring(0, args.column)].join('\n');
let i = typed.length;
let containerName: string;
let operator: string | undefined;
let query: string;
while (true) {
const substr = typed.substring(0, i);
if (/\[$/.test(substr)) {
// Numeric array index
operator = '[';
} else if (/\['$/.test(substr)) {
// String array index
operator = `['`;
} else if (/->$/.test(substr)) {
operator = '->';
} else if (i > 0) {
i--;
continue;
}
query = typed.substr(i).toLowerCase();
containerName = typed.substring(0, operator ? i - operator.length : i);
break;
}
const frame = this._stackFrames.get(args.frameId);
const contexts = await frame.getContexts();
const targets: VSCodeDebugProtocol.CompletionItem[] = [];
if (!containerName || !operator) {
const responses = await Promise.all(contexts.map(context => context.getProperties()));
for (const properties of responses) {
for (const property of properties) {
if (property.name.toLowerCase().startsWith(query)) {
const text = property.name[0] === '$' ? property.name.substr(1) : property.name;
targets.push({label: property.name, text, type: 'variable', start: i, length: property.name.length});
}
}
}
} else {
// Search all contexts
for (const context of contexts) {
let response: xdebug.PropertyGetResponse | undefined;
try {
response = await frame.connection.sendPropertyGetCommand({context, fullName: containerName});
} catch (err) {
// ignore
}
if (response) {
for (const property of response.children) {
if (property.name.toLowerCase().startsWith(query)) {
let type: VSCodeDebugProtocol.CompletionItemType | undefined;
let text: string = property.name;
if (operator === '->') {
// Object
type = 'property';
} else if (operator[0] === '[') {
// Array
if (parseInt(property.name) + '' === property.name) {
// Numeric index
if (operator[1] === `'`) {
continue;
}
type = 'value';
text += ']';
} else {
// String index
if (operator[1] !== `'`) {
if (query) {
continue;
} else {
text = `'` + text;
}
}
type = 'text';
text += `']`;
}
}
targets.push({label: property.name, text, type, start: i, length: property.name.length });
}
}
// If we found the variable in one context (typically Locals), abort
break;
}
}
}
response.body = {targets};
} catch (err) {
this.sendErrorResponse(response, err);
return;
}
this.sendResponse(response);
}


protected async continueRequest(
response: VSCodeDebugProtocol.ContinueResponse,
args: VSCodeDebugProtocol.ContinueArguments
Expand Down
8 changes: 8 additions & 0 deletions src/test/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -678,6 +678,14 @@ describe('PHP Debug Adapter', () => {
it('should return variable references for structured results')
})

describe('completion', () => {
it('should provide completion for local variables');
it('should provide completion for superglobals');
it('should provide completion for object properties');
it('should provide completion for numeric array indexes');
it('should provide completion for string array indexes');
});

describe.skip('output events', () => {
const program = path.join(TEST_PROJECT, 'output.php')

Expand Down

0 comments on commit 98bd782

Please sign in to comment.