提交 b3d2c603 编写于 作者: A Andre Weinand

support EH based DAs

上级 abee50e7
......@@ -491,11 +491,14 @@ declare module 'vscode' {
readonly cwd?: string;
/**
* Create a new debug adapter specification.
* Create a description for a debug adapter based on an executable program.
*/
constructor(command: string, args?: string[], env?: { [key: string]: string }, cwd?: string);
}
/**
* Represents a debug adapter running as a socket based server.
*/
export class DebugAdapterServer {
readonly type: 'server';
......@@ -511,12 +514,28 @@ declare module 'vscode' {
readonly host?: string;
/**
* Create a new debug adapter specification.
* Create a description for a debug adapter running as a socket based server.
*/
constructor(port: number, host?: string);
}
export type DebugAdapterDescriptor = DebugAdapterExecutable | DebugAdapterServer;
/**
* Represents a debug adapter that is implemented in the extension.
*/
export class DebugAdapterImplementation {
readonly type: 'implementation';
readonly implementation: any;
/**
* Create a description for a debug adapter directly implemented in the extension.
* The implementation's "type": TBD
*/
constructor(implementation: any);
}
export type DebugAdapterDescriptor = DebugAdapterExecutable | DebugAdapterServer | DebugAdapterImplementation;
export interface DebugConfigurationProvider {
/**
......
......@@ -726,6 +726,7 @@ export function createApiFactory(
ConfigurationTarget: extHostTypes.ConfigurationTarget,
DebugAdapterExecutable: extHostTypes.DebugAdapterExecutable,
DebugAdapterServer: extHostTypes.DebugAdapterServer,
DebugAdapterImplementation: extHostTypes.DebugAdapterImplementation,
DecorationRangeBehavior: extHostTypes.DecorationRangeBehavior,
Diagnostic: extHostTypes.Diagnostic,
DiagnosticRelatedInformation: extHostTypes.DiagnosticRelatedInformation,
......
......@@ -18,7 +18,7 @@ import {
import * as vscode from 'vscode';
import { Disposable, Position, Location, SourceBreakpoint, FunctionBreakpoint, DebugAdapterServer, DebugAdapterExecutable } from 'vs/workbench/api/node/extHostTypes';
import { generateUuid } from 'vs/base/common/uuid';
import { DebugAdapter, SocketDebugAdapter } from 'vs/workbench/parts/debug/node/debugAdapter';
import { ExecutableDebugAdapter, SocketDebugAdapter, AbstractDebugAdapter } from 'vs/workbench/parts/debug/node/debugAdapter';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors';
......@@ -107,7 +107,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
this._breakpoints = new Map<string, vscode.Breakpoint>();
this._breakpointEventsActive = false;
this._debugAdapters = new Map<number, DebugAdapter>();
this._debugAdapters = new Map();
// register all debug extensions
const debugTypes: string[] = [];
......@@ -336,40 +336,20 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
return this.getAdapterDescriptor(this._providerByType.get(config.type), sessionDto, folderUri, config).then(adapter => {
let da: IDebugAdapter = undefined;
let da: AbstractDebugAdapter = undefined;
switch (adapter.type) {
case 'server':
da = new class extends SocketDebugAdapter {
// DA -> VS Code
public acceptMessage(message: DebugProtocol.ProtocolMessage) {
convertToVSCPaths(message, source => {
if (paths.isAbsolute(source.path)) {
(<any>source).path = URI.file(source.path);
}
});
mythis._debugServiceProxy.$acceptDAMessage(handle, message);
}
}(adapter);
da = new SocketDebugAdapter(adapter);
break;
case 'executable':
da = new class extends DebugAdapter {
// DA -> VS Code
public acceptMessage(message: DebugProtocol.ProtocolMessage) {
convertToVSCPaths(message, source => {
if (paths.isAbsolute(source.path)) {
(<any>source).path = URI.file(source.path);
}
});
mythis._debugServiceProxy.$acceptDAMessage(handle, message);
}
da = new ExecutableDebugAdapter(adapter, config.type);
break;
}(adapter, config.type);
case 'implementation':
da = new DirectDebugAdapter(adapter.implementation);
break;
default:
......@@ -378,6 +358,15 @@ 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);
}
});
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));
return da.startSession();
......@@ -541,7 +530,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
if (debugConfigProvider) {
// try the proposed "provideDebugAdapter" API
if (debugConfigProvider.provideDebugAdapter) {
const adapterExecutable = DebugAdapter.platformAdapterExecutable(this._extensionService.getAllExtensionDescriptions(), config.type);
const adapterExecutable = ExecutableDebugAdapter.platformAdapterExecutable(this._extensionService.getAllExtensionDescriptions(), config.type);
return asThenable(() => debugConfigProvider.provideDebugAdapter(this.getSession(sessionDto), this.getFolder(folderUri), adapterExecutable, config, CancellationToken.None));
}
// try the deprecated "debugAdapterExecutable" API
......@@ -560,7 +549,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
}
// fallback: use executable information from package.json
return TPromise.wrap(DebugAdapter.platformAdapterExecutable(this._extensionService.getAllExtensionDescriptions(), config.type));
return TPromise.wrap(ExecutableDebugAdapter.platformAdapterExecutable(this._extensionService.getAllExtensionDescriptions(), config.type));
}
private startBreakpoints() {
......@@ -698,3 +687,64 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ
});
}
}
interface IDapTransport {
start(cb: (msg: DebugProtocol.ProtocolMessage) => void, errorcb: (event: DebugProtocol.Event) => void);
send(message: DebugProtocol.ProtocolMessage);
stop(): void;
}
class DirectTransport implements IDapTransport {
private _sendUp: (msg: DebugProtocol.ProtocolMessage) => void;
constructor(private da: DirectDebugAdapter) {
}
start(cb: (msg: DebugProtocol.ProtocolMessage) => void, errorcb: (event: DebugProtocol.Event) => void) {
this._sendUp = cb;
}
sendUp(message: DebugProtocol.ProtocolMessage) {
this._sendUp(message);
}
// DA -> VSCode
send(message: DebugProtocol.ProtocolMessage) {
this.da.acceptMessage(message);
}
stop(): void {
throw new Error('Method not implemented.');
}
}
class DirectDebugAdapter extends AbstractDebugAdapter {
readonly onError: Event<Error>;
readonly onExit: Event<number>;
private transport: DirectTransport;
constructor(implementation: any) {
super();
if (implementation.__setTransport) {
this.transport = new DirectTransport(this);
implementation.__setTransport(this.transport);
}
}
startSession(): TPromise<void> {
return TPromise.wrap(void 0);
}
// VSCode -> DA
sendMessage(message: DebugProtocol.ProtocolMessage): void {
this.transport.sendUp(message);
}
stopSession(): TPromise<void> {
this.transport.stop();
return TPromise.wrap(void 0);
}
}
......@@ -1914,6 +1914,15 @@ export class DebugAdapterServer implements vscode.DebugAdapterServer {
}
}
export class DebugAdapterImplementation implements vscode.DebugAdapterImplementation {
readonly type = 'implementation';
readonly implementation: any;
constructor(transport: any) {
this.implementation = transport;
}
}
export enum LogLevel {
Trace = 1,
Debug = 2,
......
......@@ -459,7 +459,7 @@ export interface IDebugAdapterProvider extends ITerminalLauncher {
substituteVariables(folder: IWorkspaceFolder, config: IConfig): TPromise<IConfig>;
}
export interface IAdapterExecutable {
export interface IDebugAdapterExecutable {
readonly type: 'executable';
readonly command: string;
readonly args: string[];
......@@ -467,13 +467,18 @@ export interface IAdapterExecutable {
readonly env?: { [key: string]: string };
}
export interface IAdapterServer {
export interface IDebugAdapterServer {
readonly type: 'server';
readonly port: number;
readonly host?: string;
}
export type IAdapterDescriptor = IAdapterExecutable | IAdapterServer;
export interface IDebugAdapterImplementation {
readonly type: 'implementation';
readonly implementation: any;
}
export type IAdapterDescriptor = IDebugAdapterExecutable | IDebugAdapterServer | IDebugAdapterImplementation;
export interface IPlatformSpecificAdapterContribution {
program?: string;
......
......@@ -17,7 +17,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
import { ExtensionsChannelId } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { IOutputService } from 'vs/workbench/parts/output/common/output';
import { IDebugAdapter, IAdapterExecutable, IDebuggerContribution, IPlatformSpecificAdapterContribution, IAdapterServer } from 'vs/workbench/parts/debug/common/debug';
import { IDebugAdapter, IDebugAdapterExecutable, IDebuggerContribution, IPlatformSpecificAdapterContribution, IDebugAdapterServer } from 'vs/workbench/parts/debug/common/debug';
/**
* Abstract implementation of the low level API for a debug adapter.
......@@ -29,6 +29,7 @@ export abstract class AbstractDebugAdapter implements IDebugAdapter {
private pendingRequests: Map<number, (e: DebugProtocol.Response) => void>;
private requestCallback: (request: DebugProtocol.Request) => void;
private eventCallback: (request: DebugProtocol.Event) => void;
private messageCallback: (message: DebugProtocol.ProtocolMessage) => void;
protected readonly _onError: Emitter<Error>;
protected readonly _onExit: Emitter<number>;
......@@ -57,6 +58,13 @@ export abstract class AbstractDebugAdapter implements IDebugAdapter {
return this._onExit.event;
}
public onMessage(callback: (message: DebugProtocol.ProtocolMessage) => void): void {
if (this.eventCallback) {
this._onError.fire(new Error(`attempt to set more than one 'Message' callback`));
}
this.messageCallback = callback;
}
public onEvent(callback: (event: DebugProtocol.Event) => void): void {
if (this.eventCallback) {
this._onError.fire(new Error(`attempt to set more than one 'Event' callback`));
......@@ -116,25 +124,29 @@ export abstract class AbstractDebugAdapter implements IDebugAdapter {
}
public acceptMessage(message: DebugProtocol.ProtocolMessage): void {
switch (message.type) {
case 'event':
if (this.eventCallback) {
this.eventCallback(<DebugProtocol.Event>message);
}
break;
case 'request':
if (this.requestCallback) {
this.requestCallback(<DebugProtocol.Request>message);
}
break;
case 'response':
const response = <DebugProtocol.Response>message;
const clb = this.pendingRequests.get(response.request_seq);
if (clb) {
this.pendingRequests.delete(response.request_seq);
clb(response);
}
break;
if (this.messageCallback) {
this.messageCallback(message);
} else {
switch (message.type) {
case 'event':
if (this.eventCallback) {
this.eventCallback(<DebugProtocol.Event>message);
}
break;
case 'request':
if (this.requestCallback) {
this.requestCallback(<DebugProtocol.Request>message);
}
break;
case 'response':
const response = <DebugProtocol.Response>message;
const clb = this.pendingRequests.get(response.request_seq);
if (clb) {
this.pendingRequests.delete(response.request_seq);
clb(response);
}
break;
}
}
}
......@@ -245,7 +257,7 @@ export class SocketDebugAdapter extends StreamDebugAdapter {
private socket: net.Socket;
constructor(private adapterServer: IAdapterServer) {
constructor(private adapterServer: IDebugAdapterServer) {
super();
}
......@@ -290,11 +302,11 @@ export class SocketDebugAdapter extends StreamDebugAdapter {
/**
* An implementation that launches the debug adapter as a separate process and communicates via stdin/stdout.
*/
export class DebugAdapter extends StreamDebugAdapter {
export class ExecutableDebugAdapter extends StreamDebugAdapter {
private serverProcess: cp.ChildProcess;
constructor(private adapterExecutable: IAdapterExecutable, private debugType: string, private outputService?: IOutputService) {
constructor(private adapterExecutable: IDebugAdapterExecutable, private debugType: string, private outputService?: IOutputService) {
super();
}
......@@ -445,24 +457,24 @@ export class DebugAdapter extends StreamDebugAdapter {
}
if (contribution.win) {
result.win = DebugAdapter.extract(contribution.win, extensionFolderPath);
result.win = ExecutableDebugAdapter.extract(contribution.win, extensionFolderPath);
}
if (contribution.winx86) {
result.winx86 = DebugAdapter.extract(contribution.winx86, extensionFolderPath);
result.winx86 = ExecutableDebugAdapter.extract(contribution.winx86, extensionFolderPath);
}
if (contribution.windows) {
result.windows = DebugAdapter.extract(contribution.windows, extensionFolderPath);
result.windows = ExecutableDebugAdapter.extract(contribution.windows, extensionFolderPath);
}
if (contribution.osx) {
result.osx = DebugAdapter.extract(contribution.osx, extensionFolderPath);
result.osx = ExecutableDebugAdapter.extract(contribution.osx, extensionFolderPath);
}
if (contribution.linux) {
result.linux = DebugAdapter.extract(contribution.linux, extensionFolderPath);
result.linux = ExecutableDebugAdapter.extract(contribution.linux, extensionFolderPath);
}
return result;
}
public static platformAdapterExecutable(extensionDescriptions: IExtensionDescription[], debugType: string): IAdapterExecutable {
public static platformAdapterExecutable(extensionDescriptions: IExtensionDescription[], debugType: string): IDebugAdapterExecutable {
const result: IDebuggerContribution = Object.create(null);
debugType = debugType.toLowerCase();
......@@ -473,7 +485,7 @@ export class DebugAdapter extends StreamDebugAdapter {
if (debuggers && debuggers.length > 0) {
debuggers.filter(dbg => strings.equalsIgnoreCase(dbg.type, debugType)).forEach(dbg => {
// extract relevant attributes and make then absolute where needed
const extractedDbg = DebugAdapter.extract(dbg, ed.extensionLocation.fsPath);
const extractedDbg = ExecutableDebugAdapter.extract(dbg, ed.extensionLocation.fsPath);
// merge
objects.mixin(result, extractedDbg, ed.isBuiltin);
......
......@@ -11,12 +11,12 @@ import * as objects from 'vs/base/common/objects';
import { TelemetryAppenderClient } from 'vs/platform/telemetry/node/telemetryIpc';
import { IJSONSchema, IJSONSchemaSnippet } from 'vs/base/common/jsonSchema';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { IConfig, IDebuggerContribution, IAdapterExecutable, INTERNAL_CONSOLE_OPTIONS_SCHEMA, IConfigurationManager, IDebugAdapter, IDebugConfiguration, ITerminalSettings, IDebugger, IDebugSession, IAdapterDescriptor, IAdapterServer } from 'vs/workbench/parts/debug/common/debug';
import { IConfig, IDebuggerContribution, IDebugAdapterExecutable, INTERNAL_CONSOLE_OPTIONS_SCHEMA, IConfigurationManager, IDebugAdapter, IDebugConfiguration, ITerminalSettings, IDebugger, IDebugSession, IAdapterDescriptor, IDebugAdapterServer } from 'vs/workbench/parts/debug/common/debug';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IOutputService } from 'vs/workbench/parts/output/common/output';
import { DebugAdapter, SocketDebugAdapter } from 'vs/workbench/parts/debug/node/debugAdapter';
import { ExecutableDebugAdapter, SocketDebugAdapter } from 'vs/workbench/parts/debug/node/debugAdapter';
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
......@@ -48,7 +48,9 @@ export class Debugger implements IDebugger {
case 'server':
return new SocketDebugAdapter(adapterDescriptor);
case 'executable':
return new DebugAdapter(adapterDescriptor, this.type, outputService);
return new ExecutableDebugAdapter(adapterDescriptor, this.type, outputService);
case 'implementation':
return undefined; // seeing 'direct' here is an error
default:
return undefined;
}
......@@ -60,7 +62,7 @@ export class Debugger implements IDebugger {
// a "debugServer" attribute in the launch config takes precedence
if (typeof config.debugServer === 'number') {
return TPromise.wrap(<IAdapterServer>{
return TPromise.wrap(<IDebugAdapterServer>{
type: 'server',
port: config.debugServer
});
......@@ -76,7 +78,7 @@ export class Debugger implements IDebugger {
// try deprecated command based extension API "adapterExecutableCommand" to determine the executable
if (this.debuggerContribution.adapterExecutableCommand) {
const rootFolder = root ? root.uri.toString() : undefined;
return this.commandService.executeCommand<IAdapterExecutable>(this.debuggerContribution.adapterExecutableCommand, rootFolder).then((ae: { command: string, args: string[] }) => {
return this.commandService.executeCommand<IDebugAdapterExecutable>(this.debuggerContribution.adapterExecutableCommand, rootFolder).then((ae: { command: string, args: string[] }) => {
return <IAdapterDescriptor>{
type: 'executable',
command: ae.command,
......@@ -86,7 +88,7 @@ export class Debugger implements IDebugger {
}
// fallback: use executable information from package.json
return DebugAdapter.platformAdapterExecutable(this.mergedExtensionDescriptions, this.type);
return ExecutableDebugAdapter.platformAdapterExecutable(this.mergedExtensionDescriptions, this.type);
});
}
......
......@@ -6,12 +6,12 @@
import * as assert from 'assert';
import * as paths from 'vs/base/common/paths';
import * as platform from 'vs/base/common/platform';
import { IAdapterExecutable, IConfigurationManager, IConfig, IDebugSession } from 'vs/workbench/parts/debug/common/debug';
import { IDebugAdapterExecutable, IConfigurationManager, IConfig, IDebugSession } from 'vs/workbench/parts/debug/common/debug';
import { Debugger } from 'vs/workbench/parts/debug/node/debugger';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { URI } from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { DebugAdapter } from 'vs/workbench/parts/debug/node/debugAdapter';
import { ExecutableDebugAdapter } from 'vs/workbench/parts/debug/node/debugAdapter';
suite('Debug - Debugger', () => {
......@@ -117,7 +117,7 @@ suite('Debug - Debugger', () => {
const configurationManager = <IConfigurationManager>{
provideDebugAdapter(session: IDebugSession, folderUri: URI | undefined, config: IConfig): TPromise<IAdapterExecutable | undefined> {
provideDebugAdapter(session: IDebugSession, folderUri: URI | undefined, config: IConfig): TPromise<IDebugAdapterExecutable | undefined> {
return TPromise.as(undefined);
}
};
......@@ -134,7 +134,7 @@ suite('Debug - Debugger', () => {
assert.equal(_debugger.type, debuggerContribution.type);
assert.equal(_debugger.label, debuggerContribution.label);
const ae = DebugAdapter.platformAdapterExecutable([extensionDescriptor0], 'mock');
const ae = ExecutableDebugAdapter.platformAdapterExecutable([extensionDescriptor0], 'mock');
assert.equal(ae.command, paths.join(extensionFolderPath, debuggerContribution.program));
assert.deepEqual(ae.args, debuggerContribution.args);
......@@ -155,7 +155,7 @@ suite('Debug - Debugger', () => {
});
test('merge platform specific attributes', () => {
const ae = DebugAdapter.platformAdapterExecutable([extensionDescriptor1, extensionDescriptor2], 'mock');
const ae = ExecutableDebugAdapter.platformAdapterExecutable([extensionDescriptor1, extensionDescriptor2], 'mock');
assert.equal(ae.command, platform.isLinux ? 'linuxRuntime' : (platform.isMacintosh ? 'osxRuntime' : 'winRuntime'));
const xprogram = platform.isLinux ? 'linuxProgram' : (platform.isMacintosh ? 'osxProgram' : 'winProgram');
assert.deepEqual(ae.args, ['rarg', '/e2/b/c/' + xprogram, 'parg']);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册