Skip to content
This repository has been archived by the owner on Oct 2, 2021. It is now read-only.

Commit

Permalink
Implement completionRequest - Fix #87
Browse files Browse the repository at this point in the history
  • Loading branch information
roblourens committed Oct 13, 2016
1 parent 0977f9c commit 959c44b
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 3 deletions.
78 changes: 76 additions & 2 deletions src/chrome/chromeDebugAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import {StoppedEvent, InitializedEvent, TerminatedEvent, Handles, ContinuedEvent

import {ILaunchRequestArgs, ISetBreakpointsArgs, ISetBreakpointsResponseBody, IStackTraceResponseBody,
IAttachRequestArgs, IScopesResponseBody, IVariablesResponseBody,
ISourceResponseBody, IThreadsResponseBody, IEvaluateResponseBody, ISetVariableResponseBody, IDebugAdapter} from '../debugAdapterInterfaces';
ISourceResponseBody, IThreadsResponseBody, IEvaluateResponseBody, ISetVariableResponseBody, IDebugAdapter,
ICompletionsResponseBody} from '../debugAdapterInterfaces';
import {IChromeDebugAdapterOpts, ChromeDebugSession} from './chromeDebugSession';
import {ChromeConnection} from './chromeConnection';
import * as ChromeUtils from './chromeUtils';
Expand Down Expand Up @@ -157,7 +158,8 @@ export abstract class ChromeDebugAdapter implements IDebugAdapter {
],
supportsConfigurationDoneRequest: true,
supportsSetVariable: true,
supportsConditionalBreakpoints: true
supportsConditionalBreakpoints: true,
supportsCompletionsRequest: true
};
}

Expand Down Expand Up @@ -1046,6 +1048,78 @@ export abstract class ChromeDebugAdapter implements IDebugAdapter {
}));
}

public completions(args: DebugProtocol.CompletionsArguments): Promise<ICompletionsResponseBody> {
const text = args.text;
const column = args.column;

const prefix = text.substring(0, column);

let expression: string;
const dot = prefix.lastIndexOf('.');
if (dot >= 0) {
expression = prefix.substr(0, dot);
}

if (expression) {
const getCompletionsFn = `(function(x){var a=[];for(var o=x;o;o=o.__proto__){a.push(Object.getOwnPropertyNames(o))};return a})(${expression})`;

let evalPromise: Promise<Crdp.Debugger.EvaluateOnCallFrameResponse | Crdp.Runtime.EvaluateResponse>;
if (typeof args.frameId === 'number') {
if (!this.paused || !this._currentStack[args.frameId]) {
return Promise.reject(errors.completionsStackFrameNotValid());
}

const callFrameId = this._currentStack[args.frameId].callFrameId;
evalPromise = this.chrome.Debugger.evaluateOnCallFrame({ callFrameId, expression: getCompletionsFn, silent: true, returnByValue: true });
} else {
// contextId: 1 - see https://github.com/nodejs/node/issues/8426
evalPromise = this.chrome.Runtime.evaluate({ expression: getCompletionsFn, silent: true, contextId: 1, returnByValue: true });
}

return evalPromise.then(response => {
if (response.exceptionDetails) {
return { targets: [] };
} else {
return { targets: this.getFlatAndUniqueCompletionItems(response.result.value) };
}
});
} else {
// If no expression was passed, we must be getting global completions at a breakpoint
if (typeof args.frameId !== "number" || !this.paused || !this._currentStack[args.frameId]) {
return Promise.reject(errors.completionsStackFrameNotValid());
}

const callFrame = this._currentStack[args.frameId];
const scopeExpandPs = callFrame.scopeChain
.map(scope => new ScopeContainer(callFrame.callFrameId, undefined, scope.object.objectId).expand(this));
return Promise.all(scopeExpandPs)
.then((variableArrs: DebugProtocol.Variable[][]) => {
const targets = this.getFlatAndUniqueCompletionItems(
variableArrs.map(variableArr => variableArr.map(variable => variable.name)));
return { targets };
});
}
}

private getFlatAndUniqueCompletionItems(arrays: string[][]): DebugProtocol.CompletionItem[] {
const set = new Set<string>();
const items: DebugProtocol.CompletionItem[] = [];

for (let i = 0; i < arrays.length; i++) {
for (let name of arrays[i]) {
if (!isIndexedPropName(name) && !set.has(name)) {
set.add(name);
items.push({
label: <string>name,
type: 'property'
});
}
}
}

return items;
}

private getArrayNumPropsByEval(objectId: string): Promise<IPropCount> {
const getNumPropsFn = `function() { return [this.length, Object.keys(this).length - this.length]; }`;
return this.getNumPropsByEval(objectId, getNumPropsFn);
Expand Down
1 change: 0 additions & 1 deletion src/chrome/variables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ export class ScopeContainer extends BaseVariableContainer {
}

return variables;

}).then(variables => {
if (this._returnValue) {
return this.insertRemoteObject(adapter, variables, 'Return value', this._returnValue);
Expand Down
5 changes: 5 additions & 0 deletions src/debugAdapterInterfaces.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@ export interface ISetVariableResponseBody {
value: string;
}

export interface ICompletionsResponseBody {
/** The possible completions for . */
targets: DebugProtocol.CompletionItem[];
}

declare type PromiseOrNot<T> = T | Promise<T>;

/**
Expand Down
8 changes: 8 additions & 0 deletions src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,11 @@ export function runtimeConnectionTimeout(timeoutMs: number, errMsg: string): Deb
variables: { _error: errMsg, _timeout: timeoutMs + '' }
};
}

export function completionsStackFrameNotValid(): DebugProtocol.Message {
return {
id: 2020,
format: 'stack frame not valid',
sendTelemetry: true
};
}

0 comments on commit 959c44b

Please sign in to comment.