Skip to content

Commit

Permalink
add DA Tracker API; fixes microsoft#55945
Browse files Browse the repository at this point in the history
  • Loading branch information
weinand committed Sep 20, 2018
1 parent 5db58b3 commit 88b85b6
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 26 deletions.
24 changes: 24 additions & 0 deletions src/vs/vscode.proposed.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,21 @@ declare module 'vscode' {

export type DebugAdapterDescriptor = DebugAdapterExecutable | DebugAdapterServer | DebugAdapterImplementation;

/**
* A Debug Adapter Tracker is a means to track the communication between VS Code and a Debug Adapter.
*/
export interface IDebugAdapterTracker {
// VS Code -> Debug Adapter
startDebugAdapter?(): void;
toDebugAdapter?(message: any): void;
stopDebugAdapter?(): void;

// Debug Adapter -> VS Code
fromDebugAdapter?(message: any): void;
debugAdapterError?(error: Error): void;
debugAdapterExit?(code?: number, signal?: string): void;
}

export interface DebugConfigurationProvider {
/**
* The optional method 'provideDebugAdapter' is called at the start of a debug session to provide details about the debug adapter to use.
Expand All @@ -542,6 +557,15 @@ declare module 'vscode' {
*/
provideDebugAdapter?(session: DebugSession, folder: WorkspaceFolder | undefined, executable: DebugAdapterExecutable | undefined, config: DebugConfiguration, token?: CancellationToken): ProviderResult<DebugAdapterDescriptor>;

/**
* The optional method 'provideDebugAdapterTracker' is called at the start of a debug session to provide a tracker that gives access to the communication between VS Code and a Debug Adapter.
* @param session The [debug session](#DebugSession) for which the tracker will be used.
* @param folder The workspace folder from which the configuration originates from or undefined for a folderless setup.
* @param config The resolved debug configuration.
* @param token A cancellation token.
*/
provideDebugAdapterTracker?(session: DebugSession, folder: WorkspaceFolder | undefined, config: DebugConfiguration, token?: CancellationToken): ProviderResult<IDebugAdapterTracker>;

/**
* Deprecated, use DebugConfigurationProvider.provideDebugAdapter instead.
* @deprecated Use DebugConfigurationProvider.provideDebugAdapter instead
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,10 +149,11 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
}


public $registerDebugConfigurationProvider(debugType: string, hasProvide: boolean, hasResolve: boolean, hasProvideDebugAdapter: boolean, handle: number): Thenable<void> {
public $registerDebugConfigurationProvider(debugType: string, hasProvide: boolean, hasResolve: boolean, hasProvideDebugAdapter: boolean, hasTracker: boolean, handle: number): Thenable<void> {

const provider = <IDebugConfigurationProvider>{
type: debugType
type: debugType,
hasTracker: hasTracker
};
if (hasProvide) {
provider.provideDebugConfigurations = (folder) => {
Expand Down
2 changes: 1 addition & 1 deletion src/vs/workbench/api/node/extHost.protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -577,7 +577,7 @@ export interface MainThreadDebugServiceShape extends IDisposable {
$acceptDAMessage(handle: number, message: DebugProtocol.ProtocolMessage): void;
$acceptDAError(handle: number, name: string, message: string, stack: string): void;
$acceptDAExit(handle: number, code: number, signal: string): void;
$registerDebugConfigurationProvider(type: string, hasProvideMethod: boolean, hasResolveMethod: boolean, hasDebugAdapterExecutable: boolean, handle: number): Thenable<void>;
$registerDebugConfigurationProvider(type: string, hasProvideMethod: boolean, hasResolveMethod: boolean, hasProvideDaMethod: boolean, hasProvideTrackerMethod: boolean, handle: number): Thenable<void>;
$unregisterDebugConfigurationProvider(handle: number): Thenable<void>;
$startDebugging(folder: UriComponents | undefined, nameOrConfig: string | vscode.DebugConfiguration): Thenable<boolean>;
$customDebugAdapterRequest(id: DebugSessionUUID, command: string, args: any): Thenable<any>;
Expand Down
137 changes: 119 additions & 18 deletions src/vs/workbench/api/node/extHostDebugService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,10 @@ import { IExtensionDescription } from 'vs/workbench/services/extensions/common/e

export class ExtHostDebugService implements ExtHostDebugServiceShape {

private _handleCounter: number;
private _providerHandleCounter: number;
private _providerByHandle: Map<number, vscode.DebugConfigurationProvider>;
private _providerByType: Map<string, vscode.DebugConfigurationProvider>;
private _providers: TypeProviderPair[];

private _debugServiceProxy: MainThreadDebugServiceShape;
private _debugSessions: Map<DebugSessionUUID, ExtHostDebugSession> = new Map<DebugSessionUUID, ExtHostDebugSession>();
Expand Down Expand Up @@ -70,6 +71,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {

private _aexCommands: Map<string, string>;
private _debugAdapters: Map<number, IDebugAdapter>;
private _debugAdaptersTrackers: Map<number, vscode.IDebugAdapterTracker>;

private _variableResolver: IConfigurationResolverService;

Expand All @@ -85,10 +87,14 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
private _terminalService: ExtHostTerminalService,
private _commandService: ExtHostCommands
) {
this._aexCommands = new Map();
this._handleCounter = 0;
this._providerHandleCounter = 0;
this._providerByHandle = new Map();
this._providerByType = new Map();
this._providers = [];

this._aexCommands = new Map();
this._debugAdapters = new Map();
this._debugAdaptersTrackers = new Map();

this._onDidStartDebugSession = new Emitter<vscode.DebugSession>();
this._onDidTerminateDebugSession = new Emitter<vscode.DebugSession>();
Expand All @@ -108,7 +114,6 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
this._breakpoints = new Map<string, vscode.Breakpoint>();
this._breakpointEventsActive = false;

this._debugAdapters = new Map();

// register all debug extensions
const debugTypes: string[] = [];
Expand Down Expand Up @@ -262,17 +267,20 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
}
}

let handle = this._handleCounter++;
let handle = this._providerHandleCounter++;
this._providerByHandle.set(handle, provider);
this._providers.push({ type, provider });

this._debugServiceProxy.$registerDebugConfigurationProvider(type,
!!provider.provideDebugConfigurations,
!!provider.resolveDebugConfiguration,
!!provider.debugAdapterExecutable || !!provider.provideDebugAdapter, handle);
!!provider.debugAdapterExecutable || !!provider.provideDebugAdapter,
!!provider.provideDebugAdapterTracker, handle);

return new Disposable(() => {
this._providerByHandle.delete(handle);
this._providerByType.delete(type);
this._providers = this._providers.filter(p => p.provider !== provider); // remove
this._debugServiceProxy.$unregisterDebugConfigurationProvider(handle);
});
}
Expand Down Expand Up @@ -375,18 +383,43 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {

if (da) {
this._debugAdapters.set(handle, da);
da.onMessage(message => {
// DA -> VS Code
convertToVSCPaths(message, source => {
if (paths.isAbsolute(source.path)) {
(<any>source).path = URI.file(source.path);

return this.getDebugAdapterTrackers(sessionDto, folderUri, config).then(tracker => {

if (tracker) {
this._debugAdaptersTrackers.set(handle, tracker);
}

da.onMessage(message => {

if (tracker) {
tracker.fromDebugAdapter(message);
}

// DA -> VS Code
convertToVSCPaths(message, source => {
if (paths.isAbsolute(source.path)) {
(<any>source).path = URI.file(source.path);
}
});
mythis._debugServiceProxy.$acceptDAMessage(handle, message);
});
da.onError(err => {
tracker.debugAdapterError(err);
this._debugServiceProxy.$acceptDAError(handle, err.name, err.message, err.stack);
});
da.onExit(code => {
tracker.debugAdapterExit(code, null);
this._debugServiceProxy.$acceptDAExit(handle, code, null);
});
mythis._debugServiceProxy.$acceptDAMessage(handle, message);

if (tracker) {
tracker.startDebugAdapter();
}

return da.startSession();
});
da.onError(err => this._debugServiceProxy.$acceptDAError(handle, err.name, err.message, err.stack));
da.onExit(code => this._debugServiceProxy.$acceptDAExit(handle, code, null));
return da.startSession();

}
return undefined;
});
Expand All @@ -399,6 +432,12 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
source.path = URI.revive(source.path).fsPath;
}
});

const tracker = this._debugAdaptersTrackers.get(handle);
if (tracker) {
tracker.toDebugAdapter(message);
}

const da = this._debugAdapters.get(handle);
if (da) {
da.sendMessage(message);
Expand All @@ -407,12 +446,22 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
}

public $stopDASession(handle: number): TPromise<void> {

const tracker = this._debugAdaptersTrackers.get(handle);
this._debugAdaptersTrackers.delete(handle);
if (tracker) {
tracker.stopDebugAdapter();
}

const da = this._debugAdapters.get(handle);
this._debugAdapters.delete(handle);
return da ? da.stopSession() : void 0;
if (da) {
return da.stopSession();
} else {
return void 0;
}
}


public $acceptBreakpointsDelta(delta: IBreakpointsDeltaDto): void {

let a: vscode.Breakpoint[] = [];
Expand Down Expand Up @@ -474,7 +523,6 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
this.fireBreakpointChanges(a, r, c);
}


public $provideDebugConfigurations(handle: number, folderUri: UriComponents | undefined): Thenable<vscode.DebugConfiguration[]> {
let provider = this._providerByHandle.get(handle);
if (!provider) {
Expand Down Expand Up @@ -554,6 +602,24 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
return false;
}

private getDebugAdapterTrackers(sessionDto: IDebugSessionDto, folderUri: UriComponents | undefined, config: vscode.DebugConfiguration): TPromise<vscode.IDebugAdapterTracker> {

const session = this.getSession(sessionDto);
const folder = this.getFolder(folderUri);

const type = config.type;
const promises = this._providers
.filter(pair => pair.provider.provideDebugAdapterTracker && (pair.type === type || pair.type === '*'))
.map(pair => pair.provider.provideDebugAdapterTracker(session, folder, config, CancellationToken.None));

return TPromise.join(promises).then(trackers => {
if (trackers.length > 0) {
return new MultiTracker(trackers);
}
return undefined;
});
}

private getAdapterDescriptor(debugConfigProvider, sessionDto: IDebugSessionDto, folderUri: UriComponents | undefined, config: vscode.DebugConfiguration): Thenable<vscode.DebugAdapterDescriptor> {

// a "debugServer" attribute in the launch config takes precedence
Expand Down Expand Up @@ -722,12 +788,47 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ
}
}

interface TypeProviderPair {
type: string;
provider: vscode.DebugConfigurationProvider;
}

interface IDapTransport {
start(cb: (msg: DebugProtocol.ProtocolMessage) => void, errorcb: (event: DebugProtocol.Event) => void);
send(message: DebugProtocol.ProtocolMessage);
stop(): void;
}

class MultiTracker implements vscode.IDebugAdapterTracker {

constructor(private trackers: vscode.IDebugAdapterTracker[]) {
}

startDebugAdapter(): void {
this.trackers.forEach(t => t.startDebugAdapter ? t.startDebugAdapter() : void 0);
}

toDebugAdapter(message: any): void {
this.trackers.forEach(t => t.toDebugAdapter ? t.toDebugAdapter(message) : void 0);
}

fromDebugAdapter(message: any): void {
this.trackers.forEach(t => t.fromDebugAdapter ? t.fromDebugAdapter(message) : void 0);
}

debugAdapterError(error: Error): void {
this.trackers.forEach(t => t.debugAdapterError ? t.debugAdapterError(error) : void 0);
}

debugAdapterExit(code: number, signal: string): void {
this.trackers.forEach(t => t.debugAdapterExit ? t.debugAdapterExit(code, signal) : void 0);
}

stopDebugAdapter(): void {
this.trackers.forEach(t => t.stopDebugAdapter ? t.stopDebugAdapter() : void 0);
}
}

class DirectTransport implements IDapTransport {

private _sendUp: (msg: DebugProtocol.ProtocolMessage) => void;
Expand Down
3 changes: 3 additions & 0 deletions src/vs/workbench/parts/debug/common/debug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,7 @@ export interface IDebugConfigurationProvider {
resolveDebugConfiguration?(folderUri: uri | undefined, debugConfiguration: IConfig): TPromise<IConfig>;
provideDebugConfigurations?(folderUri: uri | undefined): TPromise<IConfig[]>;
provideDebugAdapter?(session: IDebugSession, folderUri: uri | undefined, config: IConfig): TPromise<IAdapterDescriptor>;
hasTracker: boolean;
}

export interface ITerminalLauncher {
Expand Down Expand Up @@ -564,6 +565,8 @@ export interface IConfigurationManager {
*/
onDidSelectConfiguration: Event<void>;

needsToRunInExtHost(debugType: string): boolean;

registerDebugConfigurationProvider(handle: number, debugConfigurationProvider: IDebugConfigurationProvider): void;
unregisterDebugConfigurationProvider(handle: number): void;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@ export class ConfigurationManager implements IConfigurationManager {
}
}

public needsToRunInExtHost(debugType: string): boolean {
// if the given debugType matches any registered provider that has a provideTracker method, we need to run the DA in the EH
const providers = this.providers.filter(p => p.hasTracker && (p.type === debugType || p.type === '*'));
return providers.length > 0;
}

public unregisterDebugConfigurationProvider(handle: number): void {
this.providers = this.providers.filter(p => p.handle !== handle);
}
Expand Down
10 changes: 5 additions & 5 deletions src/vs/workbench/parts/debug/node/debugger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export class Debugger implements IDebugger {
public hasConfigurationProvider = false;

public createDebugAdapter(session: IDebugSession, root: IWorkspaceFolder, config: IConfig, outputService: IOutputService): TPromise<IDebugAdapter> {
if (this.inEH()) {
if (this.inExtHost()) {
return TPromise.as(this.configurationManager.createDebugAdapter(session, root, config));
} else {
return this.getAdapterDescriptor(session, root, config).then(adapterDescriptor => {
Expand Down Expand Up @@ -91,7 +91,7 @@ export class Debugger implements IDebugger {
}

public substituteVariables(folder: IWorkspaceFolder, config: IConfig): TPromise<IConfig> {
if (this.inEH()) {
if (this.inExtHost()) {
return this.configurationManager.substituteVariables(this.type, folder, config).then(config => {
return this.configurationResolverService.resolveWithCommands(folder, config, this.variables);
});
Expand All @@ -102,12 +102,12 @@ export class Debugger implements IDebugger {

public runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments): TPromise<void> {
const config = this.configurationService.getValue<ITerminalSettings>('terminal');
return this.configurationManager.runInTerminal(this.inEH() ? this.type : '*', args, config);
return this.configurationManager.runInTerminal(this.inExtHost() ? this.type : '*', args, config);
}

private inEH(): boolean {
private inExtHost(): boolean {
const debugConfigs = this.configurationService.getValue<IDebugConfiguration>('debug');
return debugConfigs.extensionHostDebugAdapter || this.extensionDescription.extensionLocation.scheme !== 'file';
return debugConfigs.extensionHostDebugAdapter || this.configurationManager.needsToRunInExtHost(this.type) || this.extensionDescription.extensionLocation.scheme !== 'file';
}

public get label(): string {
Expand Down

0 comments on commit 88b85b6

Please sign in to comment.