diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 85e2d258d494f..2faeac7e3ee75 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -460,4 +460,67 @@ declare module 'vscode' { */ onData(callback: (data: string) => any): void; } + + /** + * Namespace for dealing with debug sessions. + */ + export namespace debug { + + /** + * An [event](#Event) which fires when a debug session has terminated. + */ + export const onDidTerminateDebugSession: Event; + + /** + * Create a new debug session based on the given launchConfig. + * @param launchConfig + */ + export function createDebugSession(launchConfig: DebugConfiguration): Thenable; + } + + /** + * Configuration for a debug session. + */ + export interface DebugConfiguration { + /** + * The type for the debug session. + */ + type: string; + + /** + * An optional name for the debug session. + */ + name?: string; + + /** + * The request type of the debug session. + */ + request: string; + + /** + * Additional debug type specific properties. + */ + [key: string]: any; + } + + /** + * A debug session. + */ + export interface DebugSession { + + /** + * The debug session's type from the debug configuration. + */ + readonly type: string; + + /** + * The debug session's name from the debug configuration. + */ + readonly name: string; + + /** + * Send a custom request to the debug adapter. + */ + customRequest(command: string, args?: any): Thenable; + } } diff --git a/src/vs/workbench/api/electron-browser/extensionHost.contribution.ts b/src/vs/workbench/api/electron-browser/extensionHost.contribution.ts index 2de4cdfee1a21..0ea7d2b799feb 100644 --- a/src/vs/workbench/api/electron-browser/extensionHost.contribution.ts +++ b/src/vs/workbench/api/electron-browser/extensionHost.contribution.ts @@ -15,6 +15,7 @@ import { IExtensionService } from 'vs/platform/extensions/common/extensions'; // --- addressable import { MainThreadCommands } from './mainThreadCommands'; import { MainThreadConfiguration } from './mainThreadConfiguration'; +import { MainThreadDebugService } from './mainThreadDebugService'; import { MainThreadDiagnostics } from './mainThreadDiagnostics'; import { MainThreadDocuments } from './mainThreadDocuments'; import { MainThreadEditors } from './mainThreadEditors'; @@ -70,6 +71,7 @@ export class ExtHostContribution implements IWorkbenchContribution { const col = new InstanceCollection(); col.define(MainContext.MainThreadCommands).set(create(MainThreadCommands)); col.define(MainContext.MainThreadConfiguration).set(create(MainThreadConfiguration)); + col.define(MainContext.MainThreadDebugService).set(create(MainThreadDebugService)); col.define(MainContext.MainThreadDiagnostics).set(create(MainThreadDiagnostics)); col.define(MainContext.MainThreadDocuments).set(this.instantiationService.createInstance(MainThreadDocuments, documentsAndEditors)); col.define(MainContext.MainThreadEditors).set(this.instantiationService.createInstance(MainThreadEditors, documentsAndEditors)); diff --git a/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts b/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts new file mode 100644 index 0000000000000..15b4a1ec64947 --- /dev/null +++ b/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts @@ -0,0 +1,68 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDebugService, IProcess, IConfig } from 'vs/workbench/parts/debug/common/debug'; +import { IThreadService } from 'vs/workbench/services/thread/common/threadService'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { ExtHostContext, ExtHostDebugServiceShape, MainThreadDebugServiceShape, DebugSessionUUID } from '../node/extHost.protocol'; + +export class MainThreadDebugService extends MainThreadDebugServiceShape { + + private _proxy: ExtHostDebugServiceShape; + private _toDispose: IDisposable[]; + + constructor( + @IThreadService threadService: IThreadService, + @IDebugService private debugService: IDebugService + ) { + super(); + this._proxy = threadService.get(ExtHostContext.ExtHostDebugService); + this._toDispose = []; + this._toDispose.push(debugService.onDidEndProcess(proc => this._proxy.$acceptDebugSessionTerminated(proc.getId(), proc.configuration.type, proc.name))); + } + + public dispose(): void { + this._toDispose = dispose(this._toDispose); + } + + public $createDebugSession(configuration: IConfig): TPromise { + if (configuration.request !== 'launch' && configuration.request !== 'attach') { + return TPromise.wrapError(`only 'launch' or 'attach' allowed for 'request' attribute`); + } + return this.debugService.createProcess(configuration).then(process => { + if (process) { + return process.getId(); + } + return TPromise.wrapError('cannot create debug session'); + }, err => { + return TPromise.wrapError(err && err.message ? err.message : 'cannot create debug session'); + }); + } + + public $customDebugAdapterRequest(sessionId: DebugSessionUUID, request: string, args: any): TPromise { + const process = this._findProcessByUUID(sessionId); + if (process) { + return process.session.custom(request, args).then(response => { + if (response.success) { + return response.body; + } else { + return TPromise.wrapError(response.message); + } + }); + } + return TPromise.wrapError('debug session not found'); + } + + private _findProcessByUUID(processId: DebugSessionUUID): IProcess | null { + const processes = this.debugService.getModel().getProcesses(); + const result = processes.filter(process => process.getId() === processId); + if (result.length > 0) { + return processes[0]; // there can only be one + } + return null; + } +} diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index 022f623202940..c32ae55014a79 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -34,6 +34,7 @@ import { ExtHostLanguages } from 'vs/workbench/api/node/extHostLanguages'; import { ExtHostLanguageFeatures } from 'vs/workbench/api/node/extHostLanguageFeatures'; import { ExtHostApiCommands } from 'vs/workbench/api/node/extHostApiCommands'; import { ExtHostTask } from 'vs/workbench/api/node/extHostTask'; +import { ExtHostDebugService } from 'vs/workbench/api/node/extHostDebugService'; import * as extHostTypes from 'vs/workbench/api/node/extHostTypes'; import URI from 'vs/base/common/uri'; import Severity from 'vs/base/common/severity'; @@ -83,6 +84,7 @@ export function createApiFactory( // Addressable instances const col = new InstanceCollection(); const extHostHeapService = col.define(ExtHostContext.ExtHostHeapService).set(new ExtHostHeapService()); + const extHostDebugService = col.define(ExtHostContext.ExtHostDebugService).set(new ExtHostDebugService(threadService)); const extHostDocumentsAndEditors = col.define(ExtHostContext.ExtHostDocumentsAndEditors).set(new ExtHostDocumentsAndEditors(threadService)); const extHostDocuments = col.define(ExtHostContext.ExtHostDocuments).set(new ExtHostDocuments(threadService, extHostDocumentsAndEditors)); const extHostDocumentSaveParticipant = col.define(ExtHostContext.ExtHostDocumentSaveParticipant).set(new ExtHostDocumentSaveParticipant(extHostDocuments, threadService.get(MainContext.MainThreadWorkspace))); @@ -448,6 +450,17 @@ export function createApiFactory( } }; + // namespace: debug + const debug: typeof vscode.debug = { + createDebugSession: proposedApiFunction(extension, (config: vscode.DebugConfiguration) => { + return extHostDebugService.createDebugSession(config); + }), + onDidTerminateDebugSession: proposedApiFunction(extension, (listener, thisArg?, disposables?) => { + return extHostDebugService.onDidTerminateDebugSession(listener, thisArg, disposables); + }) + }; + + return { version: pkg.version, // namespaces @@ -458,6 +471,7 @@ export function createApiFactory( window, workspace, scm, + debug, // types CancellationTokenSource: CancellationTokenSource, CodeLens: extHostTypes.CodeLens, diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 2b30bf1c2ae2f..1bcd2bea96c15 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -342,6 +342,13 @@ export abstract class MainThreadSCMShape { $setInputBoxValue(value: string): void { throw ni(); } } +export type DebugSessionUUID = string; + +export abstract class MainThreadDebugServiceShape { + $createDebugSession(config: vscode.DebugConfiguration): TPromise { throw ni(); } + $customDebugAdapterRequest(id: DebugSessionUUID, command: string, args: any): TPromise { throw ni(); } +} + // -- extension host export abstract class ExtHostCommandsShape { @@ -492,11 +499,16 @@ export abstract class ExtHostTaskShape { $provideTasks(handle: number): TPromise { throw ni(); } } +export abstract class ExtHostDebugServiceShape { + $acceptDebugSessionTerminated(id: DebugSessionUUID, type: string, name: string): void { throw ni(); } +} + // --- proxy identifiers export const MainContext = { MainThreadCommands: createMainId('MainThreadCommands', MainThreadCommandsShape), MainThreadConfiguration: createMainId('MainThreadConfiguration', MainThreadConfigurationShape), + MainThreadDebugService: createMainId('MainThreadDebugService', MainThreadDebugServiceShape), MainThreadDiagnostics: createMainId('MainThreadDiagnostics', MainThreadDiagnosticsShape), MainThreadDocuments: createMainId('MainThreadDocuments', MainThreadDocumentsShape), MainThreadEditors: createMainId('MainThreadEditors', MainThreadEditorsShape), @@ -522,6 +534,7 @@ export const ExtHostContext = { ExtHostCommands: createExtId('ExtHostCommands', ExtHostCommandsShape), ExtHostConfiguration: createExtId('ExtHostConfiguration', ExtHostConfigurationShape), ExtHostDiagnostics: createExtId('ExtHostDiagnostics', ExtHostDiagnosticsShape), + ExtHostDebugService: createExtId('ExtHostDebugService', ExtHostDebugServiceShape), ExtHostDocumentsAndEditors: createExtId('ExtHostDocumentsAndEditors', ExtHostDocumentsAndEditorsShape), ExtHostDocuments: createExtId('ExtHostDocuments', ExtHostDocumentsShape), ExtHostDocumentSaveParticipant: createExtId('ExtHostDocumentSaveParticipant', ExtHostDocumentSaveParticipantShape), diff --git a/src/vs/workbench/api/node/extHostDebugService.ts b/src/vs/workbench/api/node/extHostDebugService.ts new file mode 100644 index 0000000000000..3b1a496cd6faf --- /dev/null +++ b/src/vs/workbench/api/node/extHostDebugService.ts @@ -0,0 +1,80 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import Event, { Emitter } from 'vs/base/common/event'; + +import { IThreadService } from 'vs/workbench/services/thread/common/threadService'; +import { MainContext, MainThreadDebugServiceShape, ExtHostDebugServiceShape, DebugSessionUUID } from 'vs/workbench/api/node/extHost.protocol'; + +import * as vscode from 'vscode'; + + +export class ExtHostDebugService extends ExtHostDebugServiceShape { + + private _debugServiceProxy: MainThreadDebugServiceShape; + private _debugSessions: Map = new Map(); + + private _onDidTerminateDebugSession: Emitter; + get onDidTerminateDebugSession(): Event { return this._onDidTerminateDebugSession.event; } + + + constructor(threadService: IThreadService) { + super(); + + this._onDidTerminateDebugSession = new Emitter(); + this._debugServiceProxy = threadService.get(MainContext.MainThreadDebugService); + } + + public createDebugSession(config: vscode.DebugConfiguration): TPromise { + + return this._debugServiceProxy.$createDebugSession(config).then((id: DebugSessionUUID) => { + const debugSession = new ExtHostDebugSession(this._debugServiceProxy, id, config.type, config.name); + this._debugSessions.set(id, debugSession); + return debugSession; + }); + } + + public $acceptDebugSessionTerminated(id: DebugSessionUUID, type: string, name: string): void { + + let debugSession = this._debugSessions.get(id); + if (!debugSession) { + debugSession = new ExtHostDebugSession(this._debugServiceProxy, id, type, name); + } + this._onDidTerminateDebugSession.fire(debugSession); + this._debugSessions.delete(id); + } +} + +export class ExtHostDebugSession implements vscode.DebugSession { + + private _debugServiceProxy: MainThreadDebugServiceShape; + + private _id: DebugSessionUUID; + + private _type: string; + private _name: string; + + + constructor(proxy: MainThreadDebugServiceShape, id: DebugSessionUUID, type: string, name: string) { + this._debugServiceProxy = proxy; + this._id = id; + this._type = type; + this._name = name; + }; + + public get type(): string { + return this._type; + } + + public get name(): string { + return this._name; + } + + public customRequest(command: string, args: any): Thenable { + return this._debugServiceProxy.$customDebugAdapterRequest(this._id, command, args); + } +}