提交 09f672c9 编写于 作者: I isidor

debug: Add support for sub-sessions

fixes #70695
上级 ae58c25e
......@@ -97,7 +97,7 @@ export class CallStackView extends ViewletPanel {
dom.addClass(container, 'debug-call-stack');
const treeContainer = renderViewTree(container);
this.dataSource = new CallStackDataSource();
this.dataSource = new CallStackDataSource(this.debugService);
this.tree = this.instantiationService.createInstance(WorkbenchAsyncDataTree, treeContainer, new CallStackDelegate(), [
new SessionsRenderer(),
new ThreadsRenderer(),
......@@ -562,6 +562,8 @@ function isDeemphasized(frame: IStackFrame): boolean {
class CallStackDataSource implements IAsyncDataSource<IDebugModel, CallStackItem> {
deemphasizedStackFramesToShow: IStackFrame[];
constructor(private debugService: IDebugService) { }
hasChildren(element: IDebugModel | CallStackItem): boolean {
return isDebugModel(element) || isDebugSession(element) || (element instanceof Thread && element.stopped);
}
......@@ -573,13 +575,18 @@ class CallStackDataSource implements IAsyncDataSource<IDebugModel, CallStackItem
return Promise.resolve([]);
}
if (sessions.length > 1) {
return Promise.resolve(sessions);
return Promise.resolve(sessions.filter(s => !s.parentSession));
}
const threads = sessions[0].getAllThreads();
// Only show the threads in the call stack if there is more than 1 thread.
return threads.length === 1 ? this.getThreadChildren(<Thread>threads[0]) : Promise.resolve(threads);
} else if (isDebugSession(element)) {
const childSessions = this.debugService.getModel().getSessions().filter(s => s.parentSession === element);
if (childSessions.length) {
return Promise.resolve(childSessions);
}
return Promise.resolve(element.getAllThreads());
} else {
return this.getThreadChildren(<Thread>element);
......
......@@ -217,7 +217,15 @@ export class FocusSessionActionItem extends SelectActionItem {
private update() {
const session = this.debugService.getViewModel().focusedSession;
const sessions = this.getSessions();
const names = sessions.map(s => s.getLabel());
const names = sessions.map(s => {
const label = s.getLabel();
if (s.parentSession) {
// Indent child sessions so they look like children
return `\u00A0\u00A0${label}`;
}
return label;
});
this.setOptions(names.map(data => <ISelectOptionItem>{ text: data }), session ? sessions.indexOf(session) : undefined);
}
......
......@@ -149,6 +149,7 @@ export interface IDebugSession extends ITreeElement {
readonly unresolvedConfiguration: IConfig | undefined;
readonly state: State;
readonly root: IWorkspaceFolder;
readonly parentSession: IDebugSession | undefined;
getLabel(): string;
......
......@@ -12,7 +12,7 @@ import { generateUuid } from 'vs/base/common/uuid';
import { RunOnceScheduler } from 'vs/base/common/async';
import severity from 'vs/base/common/severity';
import { isObject, isString, isUndefinedOrNull } from 'vs/base/common/types';
import { distinct } from 'vs/base/common/arrays';
import { distinct, lastIndex } from 'vs/base/common/arrays';
import { Range, IRange } from 'vs/editor/common/core/range';
import {
ITreeElement, IExpression, IExpressionContainer, IDebugSession, IStackFrame, IExceptionBreakpoint, IBreakpoint, IFunctionBreakpoint, IDebugModel, IReplElementSource,
......@@ -797,7 +797,17 @@ export class DebugModel implements IDebugModel {
return true;
});
let index = -1;
if (session.parentSession) {
// Make sure that child sessions are placed after the parent session
index = lastIndex(this.sessions, s => s.parentSession === session.parentSession || s === session.parentSession);
}
if (index >= 0) {
this.sessions.splice(index + 1, 0, session);
} else {
this.sessions.push(session);
}
this._onDidChangeCallStack.fire(undefined);
}
......
......@@ -315,7 +315,7 @@ export class DebugService implements IDebugService {
}
}
return this.createSession(launchForName, launchForName!.getConfiguration(name), noDebug);
return this.createSession(launchForName, launchForName!.getConfiguration(name), noDebug, parentSession);
})).then(values => values.every(success => !!success)); // Compound launch is a success only if each configuration launched successfully
}
......@@ -325,7 +325,7 @@ export class DebugService implements IDebugService {
return Promise.reject(new Error(message));
}
return this.createSession(launch, config, noDebug);
return this.createSession(launch, config, noDebug, parentSession);
});
}));
}).then(success => {
......@@ -341,7 +341,7 @@ export class DebugService implements IDebugService {
/**
* gets the debugger for the type, resolves configurations by providers, substitutes variables and runs prelaunch tasks
*/
private createSession(launch: ILaunch | undefined, config: IConfig | undefined, noDebug: boolean): Promise<boolean> {
private createSession(launch: ILaunch | undefined, config: IConfig | undefined, noDebug: boolean, parentSession?: IDebugSession): Promise<boolean> {
// We keep the debug type in a separate variable 'type' so that a no-folder config has no attributes.
// Storing the type in the config would break extensions that assume that the no-folder case is indicated by an empty config.
let type: string | undefined;
......@@ -386,7 +386,7 @@ export class DebugService implements IDebugService {
const workspace = launch ? launch.workspace : undefined;
return this.runTaskAndCheckErrors(workspace, resolvedConfig.preLaunchTask).then(result => {
if (result === TaskRunResult.Success) {
return this.doCreateSession(workspace, { resolved: resolvedConfig, unresolved: unresolvedConfig });
return this.doCreateSession(workspace, { resolved: resolvedConfig, unresolved: unresolvedConfig }, parentSession);
}
return false;
});
......@@ -415,9 +415,9 @@ export class DebugService implements IDebugService {
/**
* instantiates the new session, initializes the session, registers session listeners and reports telemetry
*/
private doCreateSession(root: IWorkspaceFolder | undefined, configuration: { resolved: IConfig, unresolved: IConfig | undefined }): Promise<boolean> {
private doCreateSession(root: IWorkspaceFolder | undefined, configuration: { resolved: IConfig, unresolved: IConfig | undefined }, parentSession?: IDebugSession): Promise<boolean> {
const session = this.instantiationService.createInstance(DebugSession, configuration, root, this.model);
const session = this.instantiationService.createInstance(DebugSession, configuration, root, this.model, parentSession);
this.model.addSession(session);
// register listeners as the very first thing!
this.registerSessionListeners(session);
......
......@@ -57,6 +57,7 @@ export class DebugSession implements IDebugSession {
private _configuration: { resolved: IConfig, unresolved: IConfig | undefined },
public root: IWorkspaceFolder,
private model: DebugModel,
private _parentSession: IDebugSession | undefined,
@IDebugService private readonly debugService: IDebugService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IOutputService private readonly outputService: IOutputService,
......@@ -83,6 +84,10 @@ export class DebugSession implements IDebugSession {
return this._configuration.unresolved;
}
get parentSession(): IDebugSession | undefined {
return this._parentSession;
}
setConfiguration(configuration: { resolved: IConfig, unresolved: IConfig | undefined }) {
this._configuration = configuration;
}
......
......@@ -123,6 +123,11 @@ export class MockDebugService implements IDebugService {
}
export class MockSession implements IDebugSession {
get parentSession(): IDebugSession | undefined {
return undefined;
}
getReplElements(): IReplElement[] {
return [];
}
......
......@@ -13,6 +13,10 @@ import { Source } from 'vs/workbench/contrib/debug/common/debugSource';
import { DebugSession } from 'vs/workbench/contrib/debug/electron-browser/debugSession';
import { ReplModel } from 'vs/workbench/contrib/debug/common/replModel';
function createMockSession(model: DebugModel, name = 'mockSession', parentSession?: DebugSession | undefined): DebugSession {
return new DebugSession({ resolved: { name, type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, parentSession, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!);
}
suite('Debug - Model', () => {
let model: DebugModel;
let rawSession: MockRawSession;
......@@ -109,7 +113,7 @@ suite('Debug - Model', () => {
test('threads simple', () => {
const threadId = 1;
const threadName = 'firstThread';
const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!);
const session = createMockSession(model);
model.addSession(session);
assert.equal(model.getSessions(true).length, 1);
......@@ -136,7 +140,7 @@ suite('Debug - Model', () => {
const stoppedReason = 'breakpoint';
// Add the threads
const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!);
const session = createMockSession(model);
model.addSession(session);
session['raw'] = <any>rawSession;
......@@ -224,7 +228,7 @@ suite('Debug - Model', () => {
const runningThreadId = 2;
const runningThreadName = 'runningThread';
const stoppedReason = 'breakpoint';
const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!);
const session = createMockSession(model);
model.addSession(session);
session['raw'] = <any>rawSession;
......@@ -338,7 +342,7 @@ suite('Debug - Model', () => {
});
test('repl expressions', () => {
const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!);
const session = createMockSession(model);
assert.equal(session.getReplElements().length, 0);
model.addSession(session);
......@@ -362,7 +366,7 @@ suite('Debug - Model', () => {
});
test('stack frame get specific source name', () => {
const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!);
const session = createMockSession(model);
model.addSession(session);
let firstStackFrame: StackFrame;
......@@ -390,10 +394,33 @@ suite('Debug - Model', () => {
assert.equal(secondStackFrame.getSpecificSourceName(), '.../x/c/d/internalModule.js');
});
test('debug child sessions are added in correct order', () => {
const session = createMockSession(model);
model.addSession(session);
const secondSession = createMockSession(model, 'mockSession2');
model.addSession(secondSession);
const firstChild = createMockSession(model, 'firstChild', session);
model.addSession(firstChild);
const secondChild = createMockSession(model, 'secondChild', session);
model.addSession(secondChild);
const thirdSession = createMockSession(model, 'mockSession3');
model.addSession(thirdSession);
const anotherChild = createMockSession(model, 'secondChild', secondSession);
model.addSession(anotherChild);
const sessions = model.getSessions();
assert.equal(sessions[0].getId(), session.getId());
assert.equal(sessions[1].getId(), firstChild.getId());
assert.equal(sessions[2].getId(), secondChild.getId());
assert.equal(sessions[3].getId(), secondSession.getId());
assert.equal(sessions[4].getId(), anotherChild.getId());
assert.equal(sessions[5].getId(), thirdSession.getId());
});
// Repl output
test('repl output', () => {
const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!);
const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, undefined, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!);
const repl = new ReplModel(session);
repl.appendToRepl('first line\n', severity.Error);
repl.appendToRepl('second line', severity.Error);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册