Skip to content

Commit

Permalink
Allow debug session to share repl with its parent; fixes #62419
Browse files Browse the repository at this point in the history
Introduced DebugSessionOptions passed to debug.startDebugging which
encapsulated DebugConsoleMode to control this behavior.
  • Loading branch information
dgozman committed Sep 18, 2019
1 parent bbb56db commit e4d4b43
Show file tree
Hide file tree
Showing 16 changed files with 219 additions and 59 deletions.
50 changes: 50 additions & 0 deletions src/vs/vscode.proposed.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,56 @@ declare module 'vscode' {
debugAdapterExecutable?(folder: WorkspaceFolder | undefined, token?: CancellationToken): ProviderResult<DebugAdapterExecutable>;
}

/**
* Debug console mode used by debug session, see [options](#DebugSessionOptions).
*/
export enum DebugConsoleMode {
/**
* Debug session should have a separate debug console.
*/
Separate = 0,

/**
* Debug session should share debug console with its parent session.
* This value has no effect for sessions which do not have a parent session.
*/
MergeWithParent = 1
}

/**
* Options for [starting a debug session](#debug.startDebugging).
*/
export interface DebugSessionOptions {

/**
* When specified the newly created debug session is registered as a "child" session of this
* "parent" debug session.
*/
parentSession?: DebugSession;

/**
* Controls whether this session should have a separate debug console or share it
* with the parent session. Has no effect for sessions which do not have a parent session.
* Defaults to Separate.
*/
consoleMode?: DebugConsoleMode;
}

export namespace debug {
/**
* Start debugging by using either a named launch or named compound configuration,
* or by directly passing a [DebugConfiguration](#DebugConfiguration).
* The named configurations are looked up in '.vscode/launch.json' found in the given folder.
* Before debugging starts, all unsaved files are saved and the launch configurations are brought up-to-date.
* Folder specific variables used in the configuration (e.g. '${workspaceFolder}') are resolved against the given folder.
* @param folder The [workspace folder](#WorkspaceFolder) for looking up named configurations and resolving variables or `undefined` for a non-folder setup.
* @param nameOrConfiguration Either the name of a debug or compound configuration or a [DebugConfiguration](#DebugConfiguration) object.
* @param parentSessionOrOptions Debug sesison options. When passed a parent [debug session](#DebugSession), assumes options with just this parent session.
* @return A thenable that resolves when debugging could be successfully started.
*/
export function startDebugging(folder: WorkspaceFolder | undefined, nameOrConfiguration: string | DebugConfiguration, parentSessionOrOptions?: DebugSession | DebugSessionOptions): Thenable<boolean>;
}

//#endregion

//#region Rob, Matt: logging
Expand Down
15 changes: 10 additions & 5 deletions src/vs/workbench/api/browser/mainThreadDebugService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@

import { DisposableStore } from 'vs/base/common/lifecycle';
import { URI as uri } from 'vs/base/common/uri';
import { IDebugService, IConfig, IDebugConfigurationProvider, IBreakpoint, IFunctionBreakpoint, IBreakpointData, IDebugAdapter, IDebugAdapterDescriptorFactory, IDebugSession, IDebugAdapterFactory, IDataBreakpoint } from 'vs/workbench/contrib/debug/common/debug';
import { IDebugService, IConfig, IDebugConfigurationProvider, IBreakpoint, IFunctionBreakpoint, IBreakpointData, IDebugAdapter, IDebugAdapterDescriptorFactory, IDebugSession, IDebugAdapterFactory, IDataBreakpoint, IDebugSessionOptions } from 'vs/workbench/contrib/debug/common/debug';
import {
ExtHostContext, ExtHostDebugServiceShape, MainThreadDebugServiceShape, DebugSessionUUID, MainContext,
IExtHostContext, IBreakpointsDeltaDto, ISourceMultiBreakpointDto, ISourceBreakpointDto, IFunctionBreakpointDto, IDebugSessionDto, IDataBreakpointDto
IExtHostContext, IBreakpointsDeltaDto, ISourceMultiBreakpointDto, ISourceBreakpointDto, IFunctionBreakpointDto, IDebugSessionDto, IDataBreakpointDto, IStartDebuggingOptions
} from 'vs/workbench/api/common/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import severity from 'vs/base/common/severity';
Expand Down Expand Up @@ -218,10 +218,15 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
return undefined;
}

public $startDebugging(_folderUri: uri | undefined, nameOrConfiguration: string | IConfig, parentSessionID: DebugSessionUUID | undefined): Promise<boolean> {
const folderUri = _folderUri ? uri.revive(_folderUri) : undefined;
public $startDebugging(options: IStartDebuggingOptions): Promise<boolean> {
const folderUri = options.folder ? uri.revive(options.folder) : undefined;
const launch = this.debugService.getConfigurationManager().getLaunch(folderUri);
return this.debugService.startDebugging(launch, nameOrConfiguration, false, this.getSession(parentSessionID)).then(success => {
const debugOptions: IDebugSessionOptions = {
noDebug: false,
parentSession: this.getSession(options.parentSessionID),
repl: options.repl
};
return this.debugService.startDebugging(launch, options.nameOrConfig, debugOptions).then(success => {
return success;
}, err => {
return Promise.reject(new Error(err && err.message ? err.message : 'cannot start debugging'));
Expand Down
9 changes: 7 additions & 2 deletions src/vs/workbench/api/common/extHost.api.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -754,8 +754,12 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
registerDebugAdapterTrackerFactory(debugType: string, factory: vscode.DebugAdapterTrackerFactory) {
return extHostDebugService.registerDebugAdapterTrackerFactory(debugType, factory);
},
startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration, parentSession?: vscode.DebugSession) {
return extHostDebugService.startDebugging(folder, nameOrConfig, parentSession);
startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration, parentSessionOrOptions?: vscode.DebugSession | vscode.DebugSessionOptions) {
if (!parentSessionOrOptions || (typeof parentSessionOrOptions === 'object' && 'configuration' in parentSessionOrOptions)) {
return extHostDebugService.startDebugging(folder, nameOrConfig, { parentSession: parentSessionOrOptions });
}
checkProposedApiEnabled(extension);
return extHostDebugService.startDebugging(folder, nameOrConfig, parentSessionOrOptions || {});
},
addBreakpoints(breakpoints: vscode.Breakpoint[]) {
return extHostDebugService.addBreakpoints(breakpoints);
Expand Down Expand Up @@ -900,6 +904,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
CallHierarchyOutgoingCall: extHostTypes.CallHierarchyOutgoingCall,
CallHierarchyIncomingCall: extHostTypes.CallHierarchyIncomingCall,
CallHierarchyItem: extHostTypes.CallHierarchyItem,
DebugConsoleMode: extHostTypes.DebugConsoleMode,
Decoration: extHostTypes.Decoration,
WebviewEditorState: extHostTypes.WebviewEditorState,
UIKind: UIKind
Expand Down
11 changes: 9 additions & 2 deletions src/vs/workbench/api/common/extHost.protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import { ThemeColor } from 'vs/platform/theme/common/themeService';
import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor';
import * as tasks from 'vs/workbench/api/common/shared/tasks';
import { IRevealOptions, ITreeItem } from 'vs/workbench/common/views';
import { IAdapterDescriptor, IConfig } from 'vs/workbench/contrib/debug/common/debug';
import { IAdapterDescriptor, IConfig, IDebugSessionReplMode } from 'vs/workbench/contrib/debug/common/debug';
import { ITextQueryBuilderOptions } from 'vs/workbench/contrib/search/common/queryBuilder';
import { ITerminalDimensions, IShellLaunchConfig } from 'vs/workbench/contrib/terminal/common/terminal';
import { ExtensionActivationError } from 'vs/workbench/services/extensions/common/extensions';
Expand Down Expand Up @@ -712,6 +712,13 @@ export interface IDebugConfiguration {
[key: string]: any;
}

export interface IStartDebuggingOptions {
folder: UriComponents | undefined;
nameOrConfig: string | IDebugConfiguration;
parentSessionID?: DebugSessionUUID;
repl?: IDebugSessionReplMode;
}

export interface MainThreadDebugServiceShape extends IDisposable {
$registerDebugTypes(debugTypes: string[]): void;
$sessionCached(sessionID: string): void;
Expand All @@ -722,7 +729,7 @@ export interface MainThreadDebugServiceShape extends IDisposable {
$registerDebugAdapterDescriptorFactory(type: string, handle: number): Promise<void>;
$unregisterDebugConfigurationProvider(handle: number): void;
$unregisterDebugAdapterDescriptorFactory(handle: number): void;
$startDebugging(folder: UriComponents | undefined, nameOrConfig: string | IDebugConfiguration, parentSessionID: string | undefined): Promise<boolean>;
$startDebugging(options: IStartDebuggingOptions): Promise<boolean>;
$setDebugSessionName(id: DebugSessionUUID, name: string): void;
$customDebugAdapterRequest(id: DebugSessionUUID, command: string, args: any): Promise<any>;
$appendDebugConsole(value: string): void;
Expand Down
2 changes: 1 addition & 1 deletion src/vs/workbench/api/common/extHostDebugService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export interface IExtHostDebugService extends ExtHostDebugServiceShape {

addBreakpoints(breakpoints0: vscode.Breakpoint[]): Promise<void>;
removeBreakpoints(breakpoints0: vscode.Breakpoint[]): Promise<void>;
startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration, parentSession?: vscode.DebugSession): Promise<boolean>;
startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration, options: vscode.DebugSessionOptions): Promise<boolean>;
registerDebugConfigurationProvider(type: string, provider: vscode.DebugConfigurationProvider): vscode.Disposable;
registerDebugAdapterDescriptorFactory(extension: IExtensionDescription, type: string, factory: vscode.DebugAdapterDescriptorFactory): vscode.Disposable;
registerDebugAdapterTrackerFactory(type: string, factory: vscode.DebugAdapterTrackerFactory): vscode.Disposable;
Expand Down
16 changes: 16 additions & 0 deletions src/vs/workbench/api/common/extHostTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2352,6 +2352,22 @@ export enum CommentMode {

//#endregion

//#region debug
export enum DebugConsoleMode {
/**
* Debug session should have a separate debug console.
*/
Separate = 0,

/**
* Debug session should share debug console with its parent session.
* This value has no effect for sessions which do not have a parent session.
*/
MergeWithParent = 1
}

//#endregion

@es5ClassCompat
export class QuickInputButtons {

Expand Down
11 changes: 8 additions & 3 deletions src/vs/workbench/api/node/extHostDebugService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
IBreakpointsDeltaDto, ISourceMultiBreakpointDto, IFunctionBreakpointDto, IDebugSessionDto
} from 'vs/workbench/api/common/extHost.protocol';
import * as vscode from 'vscode';
import { Disposable, Position, Location, SourceBreakpoint, FunctionBreakpoint, DebugAdapterServer, DebugAdapterExecutable, DataBreakpoint } from 'vs/workbench/api/common/extHostTypes';
import { Disposable, Position, Location, SourceBreakpoint, FunctionBreakpoint, DebugAdapterServer, DebugAdapterExecutable, DataBreakpoint, DebugConsoleMode } from 'vs/workbench/api/common/extHostTypes';
import { ExecutableDebugAdapter, SocketDebugAdapter } from 'vs/workbench/contrib/debug/node/debugAdapter';
import { AbstractDebugAdapter } from 'vs/workbench/contrib/debug/common/abstractDebugAdapter';
import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
Expand Down Expand Up @@ -252,8 +252,13 @@ export class ExtHostDebugService implements IExtHostDebugService, ExtHostDebugSe
return this._debugServiceProxy.$unregisterBreakpoints(ids, fids, dids);
}

public startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration, parentSession?: vscode.DebugSession): Promise<boolean> {
return this._debugServiceProxy.$startDebugging(folder ? folder.uri : undefined, nameOrConfig, parentSession ? parentSession.id : undefined);
public startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration, options: vscode.DebugSessionOptions): Promise<boolean> {
return this._debugServiceProxy.$startDebugging({
folder: folder ? folder.uri : undefined,
nameOrConfig,
parentSessionID: options.parentSession ? options.parentSession.id : undefined,
repl: options.consoleMode === DebugConsoleMode.MergeWithParent ? 'mergeWithParent' : 'separate'
});
}

public registerDebugConfigurationProvider(type: string, provider: vscode.DebugConfigurationProvider): vscode.Disposable {
Expand Down
19 changes: 16 additions & 3 deletions src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ export class FocusSessionActionViewItem extends SelectActionViewItem {
this._register(attachSelectBoxStyler(this.selectBox, themeService));

this._register(this.debugService.getViewModel().onDidFocusSession(() => {
const session = this.debugService.getViewModel().focusedSession;
const session = this.getSelectedSession();
if (session) {
const index = this.getSessions().indexOf(session);
this.select(index);
Expand All @@ -222,11 +222,11 @@ export class FocusSessionActionViewItem extends SelectActionViewItem {
}

protected getActionContext(_: string, index: number): any {
return this.debugService.getModel().getSessions()[index];
return this.getSessions()[index];
}

private update() {
const session = this.debugService.getViewModel().focusedSession;
const session = this.getSelectedSession();
const sessions = this.getSessions();
const names = sessions.map(s => {
const label = s.getLabel();
Expand All @@ -240,10 +240,23 @@ export class FocusSessionActionViewItem extends SelectActionViewItem {
this.setOptions(names.map(data => <ISelectOptionItem>{ text: data }), session ? sessions.indexOf(session) : undefined);
}

private getSelectedSession(): IDebugSession | undefined {
const session = this.debugService.getViewModel().focusedSession;
return session ? this.mapFocusedSessionToSelected(session) : undefined;
}

protected getSessions(): ReadonlyArray<IDebugSession> {
const showSubSessions = this.configurationService.getValue<IDebugConfiguration>('debug').showSubSessionsInToolBar;
const sessions = this.debugService.getModel().getSessions();

return showSubSessions ? sessions : sessions.filter(s => !s.parentSession);
}

protected mapFocusedSessionToSelected(focusedSession: IDebugSession): IDebugSession {
const showSubSessions = this.configurationService.getValue<IDebugConfiguration>('debug').showSubSessionsInToolBar;
while (focusedSession.parentSession && !showSubSessions) {
focusedSession = focusedSession.parentSession;
}
return focusedSession;
}
}
18 changes: 9 additions & 9 deletions src/vs/workbench/contrib/debug/browser/debugService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ import { IAction } from 'vs/base/common/actions';
import { deepClone, equals } from 'vs/base/common/objects';
import { DebugSession } from 'vs/workbench/contrib/debug/browser/debugSession';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { IDebugService, State, IDebugSession, CONTEXT_DEBUG_TYPE, CONTEXT_DEBUG_STATE, CONTEXT_IN_DEBUG_MODE, IThread, IDebugConfiguration, VIEWLET_ID, REPL_ID, IConfig, ILaunch, IViewModel, IConfigurationManager, IDebugModel, IEnablement, IBreakpoint, IBreakpointData, ICompound, IGlobalConfig, IStackFrame, AdapterEndEvent, getStateLabel } from 'vs/workbench/contrib/debug/common/debug';
import { IDebugService, State, IDebugSession, CONTEXT_DEBUG_TYPE, CONTEXT_DEBUG_STATE, CONTEXT_IN_DEBUG_MODE, IThread, IDebugConfiguration, VIEWLET_ID, REPL_ID, IConfig, ILaunch, IViewModel, IConfigurationManager, IDebugModel, IEnablement, IBreakpoint, IBreakpointData, ICompound, IGlobalConfig, IStackFrame, AdapterEndEvent, getStateLabel, IDebugSessionOptions } from 'vs/workbench/contrib/debug/common/debug';
import { isExtensionHostDebugging } from 'vs/workbench/contrib/debug/common/debugUtils';
import { isErrorWithActions, createErrorWithActions } from 'vs/base/common/errorsWithActions';
import { RunOnceScheduler } from 'vs/base/common/async';
Expand Down Expand Up @@ -255,7 +255,7 @@ export class DebugService implements IDebugService {
* main entry point
* properly manages compounds, checks for errors and handles the initializing state.
*/
startDebugging(launch: ILaunch | undefined, configOrName?: IConfig | string, noDebug = false, parentSession?: IDebugSession): Promise<boolean> {
startDebugging(launch: ILaunch | undefined, configOrName?: IConfig | string, options?: IDebugSessionOptions): Promise<boolean> {

this.startInitializingState();
// make sure to save all files and that the configuration is up to date
Expand Down Expand Up @@ -318,7 +318,7 @@ export class DebugService implements IDebugService {
}
}

return this.createSession(launchForName, launchForName!.getConfiguration(name), noDebug, parentSession);
return this.createSession(launchForName, launchForName!.getConfiguration(name), options);
})).then(values => values.every(success => !!success)); // Compound launch is a success only if each configuration launched successfully
}

Expand All @@ -328,7 +328,7 @@ export class DebugService implements IDebugService {
return Promise.reject(new Error(message));
}

return this.createSession(launch, config, noDebug, parentSession);
return this.createSession(launch, config, options);
});
}));
}).then(success => {
Expand All @@ -344,7 +344,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, parentSession?: IDebugSession): Promise<boolean> {
private createSession(launch: ILaunch | undefined, config: IConfig | undefined, options?: IDebugSessionOptions): 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;
Expand All @@ -356,7 +356,7 @@ export class DebugService implements IDebugService {
}
const unresolvedConfig = deepClone(config);

if (noDebug) {
if (options && options.noDebug) {
config!.noDebug = true;
}

Expand Down Expand Up @@ -390,7 +390,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 }, parentSession);
return this.doCreateSession(workspace, { resolved: resolvedConfig, unresolved: unresolvedConfig }, options);
}
return false;
});
Expand Down Expand Up @@ -419,9 +419,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 }, parentSession?: IDebugSession): Promise<boolean> {
private doCreateSession(root: IWorkspaceFolder | undefined, configuration: { resolved: IConfig, unresolved: IConfig | undefined }, options?: IDebugSessionOptions): Promise<boolean> {

const session = this.instantiationService.createInstance(DebugSession, configuration, root, this.model, parentSession);
const session = this.instantiationService.createInstance(DebugSession, configuration, root, this.model, options);
this.model.addSession(session);
// register listeners as the very first thing!
this.registerSessionListeners(session);
Expand Down
Loading

0 comments on commit e4d4b43

Please sign in to comment.