提交 88b85b66 编写于 作者: A Andre Weinand

add DA Tracker API; fixes #55945

上级 5db58b38
......@@ -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.
......@@ -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
......
......@@ -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) => {
......
......@@ -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>;
......
......@@ -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>();
......@@ -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;
......@@ -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>();
......@@ -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[] = [];
......@@ -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);
});
}
......@@ -375,7 +383,19 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
if (da) {
this._debugAdapters.set(handle, da);
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)) {
......@@ -384,9 +404,22 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
});
mythis._debugServiceProxy.$acceptDAMessage(handle, message);
});
da.onError(err => this._debugServiceProxy.$acceptDAError(handle, err.name, err.message, err.stack));
da.onExit(code => this._debugServiceProxy.$acceptDAExit(handle, code, null));
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);
});
if (tracker) {
tracker.startDebugAdapter();
}
return da.startSession();
});
}
return undefined;
});
......@@ -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);
......@@ -407,11 +446,21 @@ 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 {
......@@ -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) {
......@@ -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
......@@ -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;
......
......@@ -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 {
......@@ -564,6 +565,8 @@ export interface IConfigurationManager {
*/
onDidSelectConfiguration: Event<void>;
needsToRunInExtHost(debugType: string): boolean;
registerDebugConfigurationProvider(handle: number, debugConfigurationProvider: IDebugConfigurationProvider): void;
unregisterDebugConfigurationProvider(handle: number): void;
......
......@@ -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);
}
......
......@@ -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 => {
......@@ -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);
});
......@@ -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 {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册