From 98bd7823160e7fa39b92b2c72fa8ba503ffafbeb Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Thu, 13 Oct 2016 11:38:50 +0200 Subject: [PATCH] Add support for completionRequest --- README.md | 1 + src/phpDebug.ts | 100 ++++++++++++++++++++++++++++++++++++++++++++ src/test/adapter.ts | 8 ++++ 3 files changed, 109 insertions(+) diff --git a/README.md b/README.md index 4ee056c7..05bfee35 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/src/phpDebug.ts b/src/phpDebug.ts index ef4894f3..9a3ea104 100644 --- a/src/phpDebug.ts +++ b/src/phpDebug.ts @@ -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 diff --git a/src/test/adapter.ts b/src/test/adapter.ts index ad05147f..1df7c226 100644 --- a/src/test/adapter.ts +++ b/src/test/adapter.ts @@ -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')