提交 9af920e1 编写于 作者: E Ed Munoz

Fixes #3650: Support stop-all-threads mode debugging for multi-threaded programs

Add a new flag to the StoppedEvent that will trigger the debug adapter to be
queried for callstacks of the expanded threads in the callstack viewlet.
上级 bbe56857
......@@ -134,18 +134,18 @@ export class DebugEditorModelManager implements IWorkbenchContribution {
const result: editorcommon.IModelDeltaDecoration[] = [];
const focusedStackFrame = this.debugService.getViewModel().getFocusedStackFrame();
const allThreads = this.debugService.getModel().getThreads();
if (!focusedStackFrame || !allThreads[focusedStackFrame.threadId] || !allThreads[focusedStackFrame.threadId].callStack) {
if (!focusedStackFrame || !allThreads[focusedStackFrame.threadId] || !allThreads[focusedStackFrame.threadId].getCachedCallStack()) {
return result;
}
// only show decorations for the currently focussed thread.
const thread = allThreads[focusedStackFrame.threadId];
thread.callStack.filter(sf => sf.source.uri.toString() === modelUrlStr).forEach(sf => {
thread.getCachedCallStack().filter(sf => sf.source.uri.toString() === modelUrlStr).forEach(sf => {
const wholeLineRange = createRange(sf.lineNumber, sf.column, sf.lineNumber, Number.MAX_VALUE);
// compute how to decorate the editor. Different decorations are used if this is a top stack frame, focussed stack frame,
// an exception or a stack frame that did not change the line number (we only decorate the columns, not the whole line).
if (sf === thread.callStack[0]) {
if (sf === thread.getCachedCallStack()[0]) {
result.push({
options: DebugEditorModelManager.TOP_STACK_FRAME_MARGIN,
range: createRange(sf.lineNumber, sf.column, sf.lineNumber, sf.column + 1)
......
......@@ -189,6 +189,10 @@ export class BaseDebugController extends treedefaults.DefaultController {
export class CallStackDataSource implements tree.IDataSource {
constructor(@debug.IDebugService private debugService: debug.IDebugService) {
// noop
}
public getId(tree: tree.ITree, element: any): string {
return element.getId();
}
......@@ -199,7 +203,7 @@ export class CallStackDataSource implements tree.IDataSource {
public getChildren(tree: tree.ITree, element: any): TPromise<any> {
if (element instanceof model.Thread) {
return TPromise.as((<model.Thread> element).callStack);
return (<model.Thread> element).getCallStack(this.debugService);
}
const threads = (<model.Model> element).getThreads();
......@@ -209,7 +213,7 @@ export class CallStackDataSource implements tree.IDataSource {
});
if (threadsArray.length === 1) {
return TPromise.as(threadsArray[0].callStack);
return threadsArray[0].getCallStack(this.debugService);
} else {
return TPromise.as(threadsArray);
}
......
......@@ -93,7 +93,7 @@ class VariablesView extends viewlet.CollapsibleViewletView {
this.toDispose.push(this.debugService.addListener2(debug.ServiceEvents.STATE_CHANGED, () => {
collapseAction.enabled = this.debugService.getState() === debug.State.Running || this.debugService.getState() === debug.State.Stopped;
}));
this.toDispose.push(this.tree.addListener2(events.EventType.FOCUS, (e: tree.IFocusEvent) => {
const isMouseClick = (e.payload && e.payload.origin === 'mouse');
const isVariableType = (e.focus instanceof model.Variable);
......@@ -226,7 +226,7 @@ class CallStackView extends viewlet.CollapsibleViewletView {
this.treeContainer = renderViewTree(container);
this.tree = new treeimpl.Tree(this.treeContainer, {
dataSource: new viewer.CallStackDataSource(),
dataSource: this.instantiationService.createInstance(viewer.CallStackDataSource),
renderer: this.instantiationService.createInstance(viewer.CallStackRenderer),
accessibilityProvider: this.instantiationService.createInstance(viewer.CallstackAccessibilityProvider)
}, debugTreeOptions(nls.localize('callStackAriaLabel', "Debug Call Stack")));
......@@ -275,7 +275,7 @@ class CallStackView extends viewlet.CollapsibleViewletView {
this.toDispose.push(this.debugService.getViewModel().addListener2(debug.ViewModelEvents.FOCUSED_STACK_FRAME_UPDATED, () => {
const focussedThread = this.debugService.getModel().getThreads()[this.debugService.getViewModel().getFocusedThreadId()];
if (focussedThread && focussedThread.stoppedDetails && focussedThread.stoppedDetails.reason !== 'step') {
if (focussedThread && focussedThread.stoppedDetails && focussedThread.stoppedDetails.reason && focussedThread.stoppedDetails.reason !== 'step') {
this.pauseMessageLabel.text(nls.localize('debugStopped', "Paused on {0}", focussedThread.stoppedDetails.reason));
if (focussedThread.stoppedDetails.text) {
this.pauseMessageLabel.title(focussedThread.stoppedDetails.text);
......@@ -292,7 +292,11 @@ class CallStackView extends viewlet.CollapsibleViewletView {
if (focused) {
const threads = this.debugService.getModel().getThreads();
for (let ref in threads) {
if (threads[ref].callStack.some(sf => sf === focused)) {
// Only query for threads whose callstacks are already available
// so that we don't perform unnecessary queries to the
// debug adapter. If it's a thread we need to expand, its
// callstack would have already been populated already
if (threads[ref].getCachedCallStack() && threads[ref].getCachedCallStack().some(sf => sf === focused)) {
this.tree.expand(threads[ref]);
}
}
......
......@@ -25,6 +25,7 @@ export interface IRawModelUpdate {
thread?: DebugProtocol.Thread;
callStack?: DebugProtocol.StackFrame[];
stoppedDetails?: IRawStoppedDetails;
allThreadsStopped?: boolean;
}
export interface IRawStoppedDetails {
......@@ -53,8 +54,31 @@ export interface IExpression extends ITreeElement, IExpressionContainer {
export interface IThread extends ITreeElement {
threadId: number;
name: string;
callStack: IStackFrame[];
stoppedDetails: IRawStoppedDetails;
/**
* Queries the debug adapter for the callstack and returns a promise with
* the stack frames of the callstack.
* If the thread is not stopped, it returns a promise to an empty array.
*/
getCallStack(debugService: IDebugService): TPromise<IStackFrame[]>;
/**
* Gets the callstack if it has already been received from the debug
* adapter, otherwise it returns undefined.
*/
getCachedCallStack(): IStackFrame[];
/**
* Invalidates the callstack cache
*/
clearCallStack(): void;
/**
* Indicates whether this thread is stopped. The callstack for stopped
* threads can be retrieved from the debug adapter.
*/
stopped: boolean;
}
export interface IScope extends IExpressionContainer {
......@@ -229,6 +253,7 @@ export interface IRawDebugSession extends ee.EventEmitter {
continue(args: DebugProtocol.ContinueArguments): TPromise<DebugProtocol.ContinueResponse>;
pause(args: DebugProtocol.PauseArguments): TPromise<DebugProtocol.PauseResponse>;
stackTrace(args: DebugProtocol.StackTraceArguments): TPromise<DebugProtocol.StackTraceResponse>;
scopes(args: DebugProtocol.ScopesArguments): TPromise<DebugProtocol.ScopesResponse>;
variables(args: DebugProtocol.VariablesArguments): TPromise<DebugProtocol.VariablesResponse>;
evaluate(args: DebugProtocol.EvaluateArguments): TPromise<DebugProtocol.EvaluateResponse>;
......
......@@ -13,6 +13,7 @@ import severity from 'vs/base/common/severity';
import types = require('vs/base/common/types');
import arrays = require('vs/base/common/arrays');
import debug = require('vs/workbench/parts/debug/common/debug');
import errors = require('vs/base/common/errors');
import { Source } from 'vs/workbench/parts/debug/common/debugSource';
const MAX_REPL_LENGTH = 10000;
......@@ -92,16 +93,57 @@ export function getFullExpressionName(expression: debug.IExpression, sessionType
}
export class Thread implements debug.IThread {
private promisedCallStack: TPromise<debug.IStackFrame[]>;
private cachedCallStack: debug.IStackFrame[];
public stoppedDetails: debug.IRawStoppedDetails;
public stopped: boolean;
constructor(public name: string, public threadId, public callStack: debug.IStackFrame[]) {
constructor(public name: string, public threadId) {
this.promisedCallStack = undefined;
this.stoppedDetails = undefined;
this.cachedCallStack = undefined;
this.stopped = false;
}
public getId(): string {
return `thread:${ this.name }:${ this.threadId }`;
}
public clearCallStack(): void {
this.promisedCallStack = undefined;
this.cachedCallStack = undefined;
}
public getCachedCallStack(): debug.IStackFrame[] {
return this.cachedCallStack;
}
public getCallStack(debugService: debug.IDebugService): TPromise<debug.IStackFrame[]> {
if (!this.stopped) {
return TPromise.as([]);
}
if (!this.promisedCallStack) {
this.promisedCallStack = this.getCallStackImpl(debugService);
this.promisedCallStack.then(result => {
this.cachedCallStack = result;
}, errors.onUnexpectedError);
}
return this.promisedCallStack;
}
private getCallStackImpl(debugService: debug.IDebugService): TPromise<debug.IStackFrame[]> {
let session = debugService.getActiveSession();
return session.stackTrace({ threadId: this.threadId, levels: 20 }).then(response => {
return response.body.stackFrames.map((rsf, level) => {
if (!rsf) {
return new StackFrame(this.threadId, 0, new Source({ name: 'unknown' }), nls.localize('unknownStack', "Unknown stack location"), undefined, undefined);
}
return new StackFrame(this.threadId, rsf.id, rsf.source ? new Source(rsf.source) : new Source({ name: 'unknown' }), rsf.name, rsf.line, rsf.column);
});
});
}
}
export class OutputElement implements debug.ITreeElement {
......@@ -360,7 +402,7 @@ export class Model extends ee.EventEmitter implements debug.IModel {
if (removeThreads) {
delete this.threads[reference];
} else {
this.threads[reference].callStack = [];
this.threads[reference].clearCallStack();
this.threads[reference].stoppedDetails = undefined;
}
} else {
......@@ -370,7 +412,7 @@ export class Model extends ee.EventEmitter implements debug.IModel {
} else {
for (let ref in this.threads) {
if (this.threads.hasOwnProperty(ref)) {
this.threads[ref].callStack = [];
this.threads[ref].clearCallStack();
this.threads[ref].stoppedDetails = undefined;
}
}
......@@ -380,6 +422,16 @@ export class Model extends ee.EventEmitter implements debug.IModel {
this.emit(debug.ModelEvents.CALLSTACK_UPDATED);
}
public continueThreads(): void {
for (let ref in this.threads) {
if (this.threads.hasOwnProperty(ref)) {
this.threads[ref].stopped = false;
}
}
this.clearThreads(false);
}
public getBreakpoints(): debug.IBreakpoint[] {
return this.breakpoints;
}
......@@ -624,11 +676,13 @@ export class Model extends ee.EventEmitter implements debug.IModel {
public sourceIsUnavailable(source: Source): void {
Object.keys(this.threads).forEach(key => {
this.threads[key].callStack.forEach(stackFrame => {
if (stackFrame.source.uri.toString() === source.uri.toString()) {
stackFrame.source.available = false;
}
});
if (this.threads[key].getCachedCallStack()) {
this.threads[key].getCachedCallStack().forEach(stackFrame => {
if (stackFrame.source.uri.toString() === source.uri.toString()) {
stackFrame.source.available = false;
}
});
}
});
this.emit(debug.ModelEvents.CALLSTACK_UPDATED);
......@@ -636,21 +690,28 @@ export class Model extends ee.EventEmitter implements debug.IModel {
public rawUpdate(data: debug.IRawModelUpdate): void {
if (data.thread) {
this.threads[data.threadId] = new Thread(data.thread.name, data.thread.id, []);
this.threads[data.threadId] = new Thread(data.thread.name, data.thread.id);
}
if (data.callStack) {
// convert raw call stack into proper modelled call stack
this.threads[data.threadId].callStack = data.callStack.map(
(rsf, level) => {
if (!rsf) {
return new StackFrame(data.threadId, 0, new Source({ name: 'unknown' }), nls.localize('unknownStack', "Unknown stack location"), undefined, undefined);
if (data.stoppedDetails) {
// Set the availability of the threads' callstacks depending on
// whether the thread is stopped or not
for (let ref in this.threads) {
if (this.threads.hasOwnProperty(ref)) {
if (data.allThreadsStopped) {
// Only update the details if all the threads are stopped
// because we don't want to overwrite the details of other
// threads that have stopped for a different reason
this.threads[ref].stoppedDetails = data.stoppedDetails;
}
return new StackFrame(data.threadId, rsf.id, rsf.source ? new Source(rsf.source) : new Source({ name: 'unknown' }), rsf.name, rsf.line, rsf.column);
});
this.threads[ref].stopped = data.allThreadsStopped;
this.threads[ref].clearCallStack();
}
}
this.threads[data.threadId].stoppedDetails = data.stoppedDetails;
this.threads[data.threadId].stopped = true;
}
this.emit(debug.ModelEvents.CALLSTACK_UPDATED);
......
......@@ -40,8 +40,8 @@ export class Source {
// first try to find the raw source amongst the stack frames - since that represenation has more data (source reference),
const threads = model.getThreads();
for (let threadId in threads) {
if (threads.hasOwnProperty(threadId) && threads[threadId].callStack) {
const found = threads[threadId].callStack.filter(sf => sf.source.uri.toString() === uri.toString()).pop();
if (threads.hasOwnProperty(threadId) && threads[threadId].getCachedCallStack()) {
const found = threads[threadId].getCachedCallStack().filter(sf => sf.source.uri.toString() === uri.toString()).pop();
if (found) {
return found.source.raw;
}
......
......@@ -240,13 +240,17 @@ export class DebugService extends ee.EventEmitter implements debug.IDebugService
this.setStateAndEmit(debug.State.Stopped);
const threadId = event.body.threadId;
this.getThreadData(threadId).then(() => {
this.session.stackTrace({ threadId: threadId, levels: 20 }).done((result) => {
this.getThreadData(threadId).done(() => {
let thread = this.model.getThreads()[threadId];
this.model.rawUpdate({ threadId: threadId, callStack: result.body.stackFrames, stoppedDetails: event.body });
this.windowService.getWindow().focus();
const callStack = this.model.getThreads()[threadId].callStack;
this.model.rawUpdate({
threadId: threadId,
stoppedDetails: event.body,
allThreadsStopped: event.body.allThreadsStopped
});
thread.getCallStack(this).then(callStack => {
this.windowService.getWindow().focus();
if (callStack.length > 0) {
// focus first stack frame from top that has source location
const stackFrameToFocus = arrays.first(callStack, sf => !!sf.source, callStack[0]);
......@@ -263,7 +267,7 @@ export class DebugService extends ee.EventEmitter implements debug.IDebugService
this.toDisposeOnSessionEnd.push(this.session.addListener2(debug.SessionEvents.CONTINUED, () => {
aria.status(nls.localize('debuggingContinued', "Debugging continued."));
this.model.clearThreads(false);
this.model.continueThreads();
this.setFocusedStackFrameAndEvaluate(null);
this.setStateAndEmit(this.configurationManager.getConfiguration().noDebug ? debug.State.RunningNoDebug : debug.State.Running);
}));
......
......@@ -7,6 +7,11 @@ import assert = require('assert');
import uri from 'vs/base/common/uri';
import severity from 'vs/base/common/severity';
import debugmodel = require('vs/workbench/parts/debug/common/debugModel');
import debug = require('vs/workbench/parts/debug/common/debug');
import { TPromise } from 'vs/base/common/winjs.base';
import { DebugService } from 'vs/workbench/parts/debug/electron-browser/debugService';
import * as sinon from 'sinon';
import { MockDebugService } from 'vs/workbench/parts/debug/test/common/mockDebugService';
suite('Debug - Model', () => {
var model: debugmodel.Model;
......@@ -91,6 +96,183 @@ suite('Debug - Model', () => {
assert.equal(model.getThreads[threadId], null);
});
test('threads multiple wtih allThreadsStopped', () => {
const mockDebugService = new MockDebugService();
const sessionStub = sinon.spy(mockDebugService.getActiveSession(), 'stackTrace');
const threadId1 = 1;
const threadName1 = "firstThread";
const threadId2 = 2;
const threadName2 = "secondThread";
const stoppedReason = "breakpoint";
// Add the threads
model.rawUpdate({
threadId: threadId1,
thread: {
id: threadId1,
name: threadName1
}
});
model.rawUpdate({
threadId: threadId2,
thread: {
id: threadId2,
name: threadName2
}
});
// Stopped event with all threads stopped
model.rawUpdate({
threadId: threadId1,
stoppedDetails: {
reason: stoppedReason,
threadId: 1
},
allThreadsStopped: true
});
const thread1 = model.getThreads()[threadId1];
const thread2 = model.getThreads()[threadId2];
// at the beginning, callstacks are obtainable but not available
assert.equal(thread1.name, threadName1);
assert.equal(thread1.stopped, true);
assert.equal(thread1.getCachedCallStack(), undefined);
assert.equal(thread1.stoppedDetails.reason, stoppedReason);
assert.equal(thread2.name, threadName2);
assert.equal(thread2.stopped, true);
assert.equal(thread2.getCachedCallStack(), undefined);
assert.equal(thread2.stoppedDetails.reason, stoppedReason);
// after calling getCallStack, the callstack becomes available
// and results in a request for the callstack in the debug adapter
thread1.getCallStack(mockDebugService).then(() => {
assert.notEqual(thread1.getCachedCallStack(), undefined);
assert.equal(thread2.getCachedCallStack(), undefined);
assert.equal(sessionStub.callCount, 1);
});
thread2.getCallStack(mockDebugService).then(() => {
assert.notEqual(thread1.getCachedCallStack(), undefined);
assert.notEqual(thread2.getCachedCallStack(), undefined);
assert.equal(sessionStub.callCount, 2);
});
// calling multiple times getCallStack doesn't result in multiple calls
// to the debug adapter
thread1.getCallStack(mockDebugService).then(() => {
return thread2.getCallStack(mockDebugService);
}).then(() => {
assert.equal(sessionStub.callCount, 2);
});
// clearing the callstack results in the callstack not being available
thread1.clearCallStack();
assert.equal(thread1.stopped, true);
assert.equal(thread1.getCachedCallStack(), undefined);
thread2.clearCallStack();
assert.equal(thread2.stopped, true);
assert.equal(thread2.getCachedCallStack(), undefined);
model.continueThreads();
assert.equal(thread1.stopped, false);
assert.equal(thread2.stopped, false);
model.clearThreads(true);
assert.equal(model.getThreads[threadId1], null);
assert.equal(model.getThreads[threadId2], null);
});
test('threads mutltiple without allThreadsStopped', () => {
const mockDebugService = new MockDebugService();
const sessionStub = sinon.spy(mockDebugService.getActiveSession(), 'stackTrace');
const stoppedThreadId = 1;
const stoppedThreadName = "stoppedThread";
const runningThreadId = 2;
const runningThreadName = "runningThread";
const stoppedReason = "breakpoint";
// Add the threads
model.rawUpdate({
threadId: stoppedThreadId,
thread: {
id: stoppedThreadId,
name: stoppedThreadName
}
});
model.rawUpdate({
threadId: runningThreadId,
thread: {
id: runningThreadId,
name: runningThreadName
}
});
// Stopped event with only one thread stopped
model.rawUpdate({
threadId: stoppedThreadId,
stoppedDetails: {
reason: stoppedReason,
threadId: 1
},
allThreadsStopped: false
});
const stoppedThread = model.getThreads()[stoppedThreadId];
const runningThread = model.getThreads()[runningThreadId];
// the callstack for the stopped thread is obtainable but not available
// the callstack for the running thread is not obtainable nor available
assert.equal(stoppedThread.name, stoppedThreadName);
assert.equal(stoppedThread.stopped, true);
assert.equal(stoppedThread.getCachedCallStack(), undefined);
assert.equal(stoppedThread.stoppedDetails.reason, stoppedReason);
assert.equal(runningThread.name, runningThreadName);
assert.equal(runningThread.stopped, false);
assert.equal(runningThread.getCachedCallStack(), undefined);
assert.equal(runningThread.stoppedDetails, undefined);
// after calling getCallStack, the callstack becomes available
// and results in a request for the callstack in the debug adapter
stoppedThread.getCallStack(mockDebugService).then(() => {
assert.notEqual(stoppedThread.getCachedCallStack(), undefined);
assert.equal(runningThread.getCachedCallStack(), undefined);
assert.equal(sessionStub.callCount, 1);
});
// calling getCallStack on the running thread returns empty array
// and does not return in a request for the callstack in the debug
// adapter
runningThread.getCallStack(mockDebugService).then(callStack => {
assert.deepEqual(callStack, []);
assert.equal(sessionStub.callCount, 1);
});
// calling multiple times getCallStack doesn't result in multiple calls
// to the debug adapter
stoppedThread.getCallStack(mockDebugService).then(() => {
assert.equal(sessionStub.callCount, 1);
});
// clearing the callstack results in the callstack not being available
stoppedThread.clearCallStack();
assert.equal(stoppedThread.stopped, true);
assert.equal(stoppedThread.getCachedCallStack(), undefined);
model.continueThreads();
assert.equal(runningThread.stopped, false);
assert.equal(stoppedThread.stopped, false);
model.clearThreads(true);
assert.equal(model.getThreads[stoppedThreadId], null);
assert.equal(model.getThreads[runningThreadId], null);
});
// Expressions
function assertWatchExpressions(watchExpressions: debugmodel.Expression[], expectedName: string) {
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import debug = require('vs/workbench/parts/debug/common/debug');
import editor = require('vs/editor/common/editorCommon');
import ee = require('vs/base/common/eventEmitter');
import uri from 'vs/base/common/uri';
import editorbrowser = require('vs/editor/browser/editorBrowser');
import severity from 'vs/base/common/severity';
import { TPromise } from 'vs/base/common/winjs.base';
import { Source } from 'vs/workbench/parts/debug/common/debugSource';
export class MockDebugService extends ee.EventEmitter implements debug.IDebugService {
private session: MockRawSession;
public serviceId = debug.IDebugService;
constructor() {
super();
this.session = new MockRawSession();
}
public getState(): debug.State {
return null;
}
public canSetBreakpointsIn(model: editor.IModel): boolean {
return false;
}
public getConfigurationName(): string {
return null;
}
public setConfiguration(name: string): TPromise<void> {
return TPromise.as(null);
}
public openConfigFile(sideBySide: boolean): TPromise<boolean> {
return TPromise.as(false);
}
public loadLaunchConfig(): TPromise<debug.IGlobalConfig> {
return TPromise.as(null);
}
public setFocusedStackFrameAndEvaluate(focusedStackFrame: debug.IStackFrame): void {}
public setBreakpointsForModel(modelUri: uri, rawData: debug.IRawBreakpoint[]): void {}
public toggleBreakpoint(IRawBreakpoint): TPromise<void> {
return TPromise.as(null);
}
public enableOrDisableAllBreakpoints(enabled: boolean): TPromise<void> {
return TPromise.as(null);
}
public toggleEnablement(element: debug.IEnablement): TPromise<void> {
return TPromise.as(null);
}
public toggleBreakpointsActivated(): TPromise<void> {
return TPromise.as(null);
}
public removeAllBreakpoints(): TPromise<any> {
return TPromise.as(null);
}
public sendAllBreakpoints(): TPromise<any> {
return TPromise.as(null);
}
public editBreakpoint(editor: editorbrowser.ICodeEditor, lineNumber: number): TPromise<void> {
return TPromise.as(null);
}
public addFunctionBreakpoint(): void {}
public renameFunctionBreakpoint(id: string, newFunctionName: string): TPromise<void> {
return TPromise.as(null);
}
public removeFunctionBreakpoints(id?: string): TPromise<void> {
return TPromise.as(null);
}
public addReplExpression(name: string): TPromise<void> {
return TPromise.as(null);
}
public clearReplExpressions(): void {}
public logToRepl(value: string, severity?: severity): void;
public logToRepl(value: { [key: string]: any }, severity?: severity): void;
public logToRepl(value: any, severity?: severity): void {}
public appendReplOutput(value: string, severity?: severity): void {}
public addWatchExpression(name?: string): TPromise<void> {
return TPromise.as(null);
}
public renameWatchExpression(id: string, newName: string): TPromise<void> {
return TPromise.as(null);
}
public clearWatchExpressions(id?: string): void {}
public createSession(noDebug: boolean): TPromise<any> {
return TPromise.as(null);
}
public restartSession(): TPromise<any> {
return TPromise.as(null);
}
public getActiveSession(): debug.IRawDebugSession {
return this.session;
}
public getModel(): debug.IModel {
return null;
}
public getViewModel(): debug.IViewModel {
return null
}
public openOrRevealEditor(source: Source, lineNumber: number, preserveFocus: boolean, sideBySide: boolean): TPromise<any> {
return TPromise.as(null);
}
public revealRepl(focus?: boolean): TPromise<void> {
return TPromise.as(null);
}
}
class MockRawSession extends ee.EventEmitter implements debug.IRawDebugSession {
public isAttach: boolean = false;
public capabilities: DebugProtocol.Capabilites;
public getType(): string {
return null;
}
public disconnect(restart?: boolean, force?: boolean): TPromise<DebugProtocol.DisconnectResponse> {
return TPromise.as(null);
}
public next(args: DebugProtocol.NextArguments): TPromise<DebugProtocol.NextResponse> {
return TPromise.as(null);
}
public stepIn(args: DebugProtocol.StepInArguments): TPromise<DebugProtocol.StepInResponse> {
return TPromise.as(null);
}
public stepOut(args: DebugProtocol.StepOutArguments): TPromise<DebugProtocol.StepOutResponse> {
return TPromise.as(null);
}
public continue(args: DebugProtocol.ContinueArguments): TPromise<DebugProtocol.ContinueResponse> {
return TPromise.as(null);
}
public pause(args: DebugProtocol.PauseArguments): TPromise<DebugProtocol.PauseResponse> {
return TPromise.as(null);
}
public stackTrace(args: DebugProtocol.StackTraceArguments): TPromise<DebugProtocol.StackTraceResponse> {
return TPromise.as({
body: {
stackFrames: []
}
});
}
public scopes(args: DebugProtocol.ScopesArguments): TPromise<DebugProtocol.ScopesResponse> {
return TPromise.as(null);
}
public variables(args: DebugProtocol.VariablesArguments): TPromise<DebugProtocol.VariablesResponse> {
return TPromise.as(null);
}
evaluate(args: DebugProtocol.EvaluateArguments): TPromise<DebugProtocol.EvaluateResponse> {
return TPromise.as(null);
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册