diff --git a/src/vs/workbench/api/electron-browser/extHostCustomers.ts b/src/vs/workbench/api/electron-browser/extHostCustomers.ts index 33b87d17da4..96394815089 100644 --- a/src/vs/workbench/api/electron-browser/extHostCustomers.ts +++ b/src/vs/workbench/api/electron-browser/extHostCustomers.ts @@ -10,9 +10,9 @@ import { ProxyIdentifier } from "vs/workbench/services/thread/common/threadServi import { IConstructorSignature1 } from "vs/platform/instantiation/common/instantiation"; import { IExtHostContext } from "vs/workbench/api/node/extHost.protocol"; -export type IExtHostNamedCustomer = [ProxyIdentifier, IExtHostCustomerCtor]; +export type IExtHostNamedCustomer = [ProxyIdentifier, IExtHostCustomerCtor]; -export type IExtHostCustomerCtor = IConstructorSignature1; +export type IExtHostCustomerCtor = IConstructorSignature1; export function extHostNamedCustomer(id: ProxyIdentifier) { return function (ctor: IExtHostCustomerCtor): void { @@ -26,11 +26,11 @@ export function extHostCustomer(ctor: IExtHostCustomerCto export namespace ExtHostCustomersRegistry { - export function getNamedCustomers(): IExtHostNamedCustomer[] { + export function getNamedCustomers(): IExtHostNamedCustomer[] { return ExtHostCustomersRegistryImpl.INSTANCE.getNamedCustomers(); } - export function getCustomers(): IExtHostCustomerCtor[] { + export function getCustomers(): IExtHostCustomerCtor[] { return ExtHostCustomersRegistryImpl.INSTANCE.getCustomers(); } } @@ -39,7 +39,7 @@ class ExtHostCustomersRegistryImpl { public static INSTANCE = new ExtHostCustomersRegistryImpl(); - private _namedCustomers: IExtHostNamedCustomer[]; + private _namedCustomers: IExtHostNamedCustomer[]; private _customers: IExtHostCustomerCtor[]; constructor() { @@ -48,14 +48,14 @@ class ExtHostCustomersRegistryImpl { } public registerNamedCustomer(id: ProxyIdentifier, ctor: IExtHostCustomerCtor): void { - const entry: IExtHostNamedCustomer = [id, ctor]; + const entry: IExtHostNamedCustomer = [id, ctor]; this._namedCustomers.push(entry); } - public getNamedCustomers(): IExtHostNamedCustomer[] { + public getNamedCustomers(): IExtHostNamedCustomer[] { return this._namedCustomers; } - public registerCustomer(ctor: IExtHostCustomerCtor): void { + public registerCustomer(ctor: IExtHostCustomerCtor): void { this._customers.push(ctor); } public getCustomers(): IExtHostCustomerCtor[] { diff --git a/src/vs/workbench/api/electron-browser/extensionHost.contribution.ts b/src/vs/workbench/api/electron-browser/extensionHost.contribution.ts index ec19b48c982..a9ddadcc6f1 100644 --- a/src/vs/workbench/api/electron-browser/extensionHost.contribution.ts +++ b/src/vs/workbench/api/electron-browser/extensionHost.contribution.ts @@ -7,9 +7,9 @@ import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { Registry } from 'vs/platform/registry/common/platform'; -import { IInstantiationService, IConstructorSignature0 } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IThreadService, ProxyIdentifier } from 'vs/workbench/services/thread/common/threadService'; -import { InstanceCollection, IExtHostContext } from '../node/extHost.protocol'; +import { MainContext, IExtHostContext } from '../node/extHost.protocol'; import { IExtensionService } from 'vs/platform/extensions/common/extensions'; import { ExtHostCustomersRegistry } from "vs/workbench/api/electron-browser/extHostCustomers"; @@ -17,10 +17,6 @@ import { ExtHostCustomersRegistry } from "vs/workbench/api/electron-browser/extH import { JSONValidationExtensionPoint } from 'vs/platform/jsonschemas/common/jsonValidationExtensionPoint'; import { ColorExtensionPoint } from 'vs/platform/theme/common/colorExtensionPoint'; import { LanguageConfigurationFileHandler } from 'vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint'; -import { SaveParticipant } from './mainThreadSaveParticipant'; - -// --- registers itself as service -import './mainThreadHeapService'; // --- mainThread participants import './mainThreadCommands'; @@ -36,7 +32,7 @@ import './mainThreadEditors'; import './mainThreadErrors'; import './mainThreadExtensionService'; import './mainThreadFileSystemEventService'; -// import './mainThreadHeapService'; +import './mainThreadHeapService'; import './mainThreadLanguageFeatures'; import './mainThreadLanguages'; import './mainThreadMessageService'; @@ -44,7 +40,7 @@ import './mainThreadOutputService'; import './mainThreadProgress'; import './mainThreadQuickOpen'; import './mainThreadSCM'; -// import './mainThreadSaveParticipant'; +import './mainThreadSaveParticipant'; import './mainThreadStatusBar'; import './mainThreadStorage'; import './mainThreadTask'; @@ -56,66 +52,57 @@ import './mainThreadWorkspace'; export class ExtHostContribution implements IWorkbenchContribution { constructor( - @IThreadService private threadService: IThreadService, - @IInstantiationService private instantiationService: IInstantiationService, - @IExtensionService private extensionService: IExtensionService + @IThreadService threadService: IThreadService, + @IInstantiationService instantiationService: IInstantiationService, + @IExtensionService extensionService: IExtensionService ) { - this.initExtensionSystem(); + + const extHostContext: IExtHostContext = threadService; + + // Named customers + const namedCustomers = ExtHostCustomersRegistry.getNamedCustomers(); + for (let i = 0, len = namedCustomers.length; i < len; i++) { + const [id, ctor] = namedCustomers[i]; + threadService.set(id, instantiationService.createInstance(ctor, extHostContext)); + } + + // Customers + const customers = ExtHostCustomersRegistry.getCustomers(); + for (let i = 0, len = customers.length; i < len; i++) { + const ctor = customers[i]; + instantiationService.createInstance(ctor, extHostContext); + } + + // Check that no named customers are missing + const expected: ProxyIdentifier[] = Object.keys(MainContext).map((key) => MainContext[key]); + threadService.assertRegistered(expected); } public getId(): string { return 'vs.api.extHost'; } +} - private initExtensionSystem(): void { - const create = (ctor: IConstructorSignature0): T => { - return this.instantiationService.createInstance(ctor); - }; - let col = new InstanceCollection(); +export class ExtensionPoints implements IWorkbenchContribution { - const extHostContext = new class implements IExtHostContext { + constructor( + @IInstantiationService private instantiationService: IInstantiationService + ) { + // Classes that handle extension points... + this.instantiationService.createInstance(JSONValidationExtensionPoint); + this.instantiationService.createInstance(ColorExtensionPoint); + this.instantiationService.createInstance(LanguageConfigurationFileHandler); + } - constructor(private readonly _threadService: IThreadService) { - } - - get(identifier: ProxyIdentifier): T { - return this._threadService.get(identifier); - } - - set(identifier: ProxyIdentifier, instance: T): void { - col.define(identifier).set(instance); - } - }(this.threadService); - - // Registered named customers - const namedCustomers = ExtHostCustomersRegistry.getNamedCustomers(); - for (let i = 0, len = namedCustomers.length; i < len; i++) { - const [id, ctor] = namedCustomers[i]; - const obj = this.instantiationService.createInstance(ctor, extHostContext); - col.define(id).set(obj); - } - - // Registered customers - const customers = ExtHostCustomersRegistry.getCustomers(); - for (let i = 0, len = customers.length; i < len; i++) { - const ctor = customers[i]; - this.instantiationService.createInstance(ctor, extHostContext); - } - - col.finish(true, this.threadService); - - col = null; - - // Other interested parties - create(JSONValidationExtensionPoint); // TODO@rehost: can survive an ext host restart - create(ColorExtensionPoint); // TODO@rehost: can survive an ext host restart - this.instantiationService.createInstance(LanguageConfigurationFileHandler); // TODO@rehost: can survive an ext host restart - create(SaveParticipant); + public getId(): string { + return 'vs.api.extensionPoints'; } } -// Register File Tracker Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution( ExtHostContribution ); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution( + ExtensionPoints +); \ No newline at end of file diff --git a/src/vs/workbench/api/electron-browser/mainThreadCommands.ts b/src/vs/workbench/api/electron-browser/mainThreadCommands.ts index 8d24bab4469..662eb5d8f57 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadCommands.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadCommands.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { IThreadService } from 'vs/workbench/services/thread/common/threadService'; import { ICommandService, CommandsRegistry, ICommandHandlerDescription } from 'vs/platform/commands/common/commands'; import { IDisposable } from 'vs/base/common/lifecycle'; import { TPromise } from 'vs/base/common/winjs.base'; @@ -15,6 +14,7 @@ import { extHostNamedCustomer } from "vs/workbench/api/electron-browser/extHostC export class MainThreadCommands implements MainThreadCommandsShape { private readonly _disposables = new Map(); + private readonly _generateCommandsDocumentationRegistration: IDisposable; private readonly _proxy: ExtHostCommandsShape; constructor( @@ -22,11 +22,35 @@ export class MainThreadCommands implements MainThreadCommandsShape { @ICommandService private readonly _commandService: ICommandService, ) { this._proxy = extHostContext.get(ExtHostContext.ExtHostCommands); + + this._generateCommandsDocumentationRegistration = CommandsRegistry.registerCommand('_generateCommandsDocumentation', () => this._generateCommandsDocumentation()); } dispose() { this._disposables.forEach(value => value.dispose()); this._disposables.clear(); + + this._generateCommandsDocumentationRegistration.dispose(); + } + + private _generateCommandsDocumentation(): TPromise { + return this._proxy.$getContributedCommandHandlerDescriptions().then(result => { + // add local commands + const commands = CommandsRegistry.getCommands(); + for (let id in commands) { + let { description } = commands[id]; + if (description) { + result[id] = description; + } + } + + // print all as markdown + const all: string[] = []; + for (let id in result) { + all.push('`' + id + '` - ' + _generateMarkdown(result[id])); + } + console.log(all.join('\n')); + }); } $registerCommand(id: string): TPromise { @@ -56,27 +80,6 @@ export class MainThreadCommands implements MainThreadCommandsShape { // --- command doc -CommandsRegistry.registerCommand('_generateCommandsDocumentation', function (accessor) { - return accessor.get(IThreadService).get(ExtHostContext.ExtHostCommands).$getContributedCommandHandlerDescriptions().then(result => { - - // add local commands - const commands = CommandsRegistry.getCommands(); - for (let id in commands) { - let { description } = commands[id]; - if (description) { - result[id] = description; - } - } - - // print all as markdown - const all: string[] = []; - for (let id in result) { - all.push('`' + id + '` - ' + _generateMarkdown(result[id])); - } - console.log(all.join('\n')); - }); -}); - function _generateMarkdown(description: string | ICommandHandlerDescription): string { if (typeof description === 'string') { return description; diff --git a/src/vs/workbench/api/electron-browser/mainThreadHeapService.ts b/src/vs/workbench/api/electron-browser/mainThreadHeapService.ts index aae6771afb5..77c5c54bad7 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadHeapService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadHeapService.ts @@ -6,17 +6,21 @@ 'use strict'; import { TPromise } from 'vs/base/common/winjs.base'; -import { IThreadService } from 'vs/workbench/services/thread/common/threadService'; -import { ExtHostContext, ObjectIdentifier } from '../node/extHost.protocol'; +import { ExtHostContext, ObjectIdentifier, IExtHostContext } from '../node/extHost.protocol'; import { consumeSignals, GCSignal } from 'gc-signals'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import Event, { Emitter } from 'vs/base/common/event'; +import { IDisposable } from "vs/base/common/lifecycle"; +import { extHostCustomer } from "vs/workbench/api/electron-browser/extHostCustomers"; export const IHeapService = createDecorator('heapService'); export interface IHeapService { _serviceBrand: any; + readonly onGarbageCollection: Event; + /** * Track gc-collection for all new objects that * have the $ident-value set. @@ -31,17 +35,18 @@ export interface IHeapService { } -export class MainThreadHeapService implements IHeapService { +export class HeapService implements IHeapService { _serviceBrand: any; + private _onGarbageCollection: Emitter = new Emitter(); + public readonly onGarbageCollection: Event = this._onGarbageCollection.event; + private _activeSignals = new WeakMap(); private _activeIds = new Set(); private _consumeHandle: number; - constructor( @IThreadService threadService: IThreadService) { - const proxy = threadService.get(ExtHostContext.ExtHostHeapService); - + constructor() { this._consumeHandle = setInterval(() => { const ids = consumeSignals(); @@ -51,8 +56,8 @@ export class MainThreadHeapService implements IHeapService { this._activeIds.delete(id); } - // send to ext host - proxy.$onGarbageCollection(ids); + // fire event + this._onGarbageCollection.fire(ids); } }, 15 * 1000); @@ -109,4 +114,26 @@ export class MainThreadHeapService implements IHeapService { } } -registerSingleton(IHeapService, MainThreadHeapService); +@extHostCustomer +export class MainThreadHeapService { + + private _toDispose: IDisposable; + + constructor( + extHostContext: IExtHostContext, + @IHeapService heapService: IHeapService, + ) { + const proxy = extHostContext.get(ExtHostContext.ExtHostHeapService); + this._toDispose = heapService.onGarbageCollection((ids) => { + // send to ext host + proxy.$onGarbageCollection(ids); + }); + } + + public dispose(): void { + this._toDispose.dispose(); + } + +} + +registerSingleton(IHeapService, HeapService); diff --git a/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts b/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts index 4aee6986e68..8ebe4914986 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts @@ -9,7 +9,6 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { sequence } from 'vs/base/common/async'; import * as strings from 'vs/base/common/strings'; import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService'; -import { IThreadService } from 'vs/workbench/services/thread/common/threadService'; import { ISaveParticipant, ITextFileEditorModel, SaveReason } from 'vs/workbench/services/textfile/common/textfiles'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -22,8 +21,9 @@ import { getDocumentFormattingEdits } from 'vs/editor/contrib/format/common/form import { EditOperationsCommand } from 'vs/editor/contrib/format/common/formatCommand'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; -import { ExtHostContext, ExtHostDocumentSaveParticipantShape } from '../node/extHost.protocol'; +import { ExtHostContext, ExtHostDocumentSaveParticipantShape, IExtHostContext } from '../node/extHost.protocol'; import { EditOperation } from 'vs/editor/common/core/editOperation'; +import { extHostCustomer } from "vs/workbench/api/electron-browser/extHostCustomers"; export interface INamedSaveParticpant extends ISaveParticipant { readonly name: string; @@ -200,8 +200,8 @@ class ExtHostSaveParticipant implements INamedSaveParticpant { readonly name = 'ExtHostSaveParticipant'; - constructor( @IThreadService threadService: IThreadService) { - this._proxy = threadService.get(ExtHostContext.ExtHostDocumentSaveParticipant); + constructor(extHostContext: IExtHostContext) { + this._proxy = extHostContext.get(ExtHostContext.ExtHostDocumentSaveParticipant); } participate(editorModel: ITextFileEditorModel, env: { reason: SaveReason }): TPromise { @@ -220,21 +220,24 @@ class ExtHostSaveParticipant implements INamedSaveParticpant { } // The save participant can change a model before its saved to support various scenarios like trimming trailing whitespace +@extHostCustomer export class SaveParticipant implements ISaveParticipant { private _saveParticipants: INamedSaveParticpant[]; constructor( - @IThreadService threadService: IThreadService, + extHostContext: IExtHostContext, @ITelemetryService private _telemetryService: ITelemetryService, - @IInstantiationService instantiationService: IInstantiationService + @IInstantiationService instantiationService: IInstantiationService, + @IConfigurationService configurationService: IConfigurationService, + @ICodeEditorService codeEditorService: ICodeEditorService ) { this._saveParticipants = [ - instantiationService.createInstance(TrimWhitespaceParticipant), - instantiationService.createInstance(FormatOnSaveParticipant), - instantiationService.createInstance(FinalNewLineParticipant), - instantiationService.createInstance(ExtHostSaveParticipant) + new TrimWhitespaceParticipant(configurationService, codeEditorService), + new FormatOnSaveParticipant(codeEditorService, configurationService), + new FinalNewLineParticipant(configurationService, codeEditorService), + new ExtHostSaveParticipant(extHostContext) ]; // Hook into model diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index 2eb89961fa6..cdd8abf31c4 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -48,10 +48,11 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation'; import * as vscode from 'vscode'; import * as paths from 'vs/base/common/paths'; import { realpath } from 'fs'; -import { MainContext, ExtHostContext, InstanceCollection, IInitData } from './extHost.protocol'; +import { MainContext, ExtHostContext, IInitData } from './extHost.protocol'; import * as languageConfiguration from 'vs/editor/common/modes/languageConfiguration'; import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions'; import { ExtHostThreadService } from "vs/workbench/services/thread/node/extHostThreadService"; +import { ProxyIdentifier } from "vs/workbench/services/thread/common/threadService"; export interface IExtensionApiFactory { (extension: IExtensionDescription): typeof vscode; @@ -78,28 +79,30 @@ export function createApiFactory( ): IExtensionApiFactory { // 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 extHostDocumentContentProviders = col.define(ExtHostContext.ExtHostDocumentContentProviders).set(new ExtHostDocumentContentProvider(threadService, extHostDocumentsAndEditors)); - const extHostDocumentSaveParticipant = col.define(ExtHostContext.ExtHostDocumentSaveParticipant).set(new ExtHostDocumentSaveParticipant(extHostDocuments, threadService.get(MainContext.MainThreadWorkspace))); - const extHostEditors = col.define(ExtHostContext.ExtHostEditors).set(new ExtHostEditors(threadService, extHostDocumentsAndEditors)); - const extHostCommands = col.define(ExtHostContext.ExtHostCommands).set(new ExtHostCommands(threadService, extHostHeapService)); - const extHostTreeViews = col.define(ExtHostContext.ExtHostTreeViews).set(new ExtHostTreeViews(threadService.get(MainContext.MainThreadTreeViews), extHostCommands)); - const extHostWorkspace = col.define(ExtHostContext.ExtHostWorkspace).set(new ExtHostWorkspace(threadService, initData.workspace)); - const extHostConfiguration = col.define(ExtHostContext.ExtHostConfiguration).set(new ExtHostConfiguration(threadService.get(MainContext.MainThreadConfiguration), extHostWorkspace, initData.configuration)); - const extHostDiagnostics = col.define(ExtHostContext.ExtHostDiagnostics).set(new ExtHostDiagnostics(threadService)); - const languageFeatures = col.define(ExtHostContext.ExtHostLanguageFeatures).set(new ExtHostLanguageFeatures(threadService, extHostDocuments, extHostCommands, extHostHeapService, extHostDiagnostics)); - const extHostFileSystemEvent = col.define(ExtHostContext.ExtHostFileSystemEventService).set(new ExtHostFileSystemEventService()); - const extHostQuickOpen = col.define(ExtHostContext.ExtHostQuickOpen).set(new ExtHostQuickOpen(threadService)); - const extHostTerminalService = col.define(ExtHostContext.ExtHostTerminalService).set(new ExtHostTerminalService(threadService)); - const extHostSCM = col.define(ExtHostContext.ExtHostSCM).set(new ExtHostSCM(threadService, extHostCommands)); - const extHostTask = col.define(ExtHostContext.ExtHostTask).set(new ExtHostTask(threadService)); - const extHostCredentials = col.define(ExtHostContext.ExtHostCredentials).set(new ExtHostCredentials(threadService)); - col.define(ExtHostContext.ExtHostExtensionService).set(extensionService); - col.finish(false, threadService); + const extHostHeapService = threadService.set(ExtHostContext.ExtHostHeapService, new ExtHostHeapService()); + const extHostDebugService = threadService.set(ExtHostContext.ExtHostDebugService, new ExtHostDebugService(threadService)); + const extHostDocumentsAndEditors = threadService.set(ExtHostContext.ExtHostDocumentsAndEditors, new ExtHostDocumentsAndEditors(threadService)); + const extHostDocuments = threadService.set(ExtHostContext.ExtHostDocuments, new ExtHostDocuments(threadService, extHostDocumentsAndEditors)); + const extHostDocumentContentProviders = threadService.set(ExtHostContext.ExtHostDocumentContentProviders, new ExtHostDocumentContentProvider(threadService, extHostDocumentsAndEditors)); + const extHostDocumentSaveParticipant = threadService.set(ExtHostContext.ExtHostDocumentSaveParticipant, new ExtHostDocumentSaveParticipant(extHostDocuments, threadService.get(MainContext.MainThreadWorkspace))); + const extHostEditors = threadService.set(ExtHostContext.ExtHostEditors, new ExtHostEditors(threadService, extHostDocumentsAndEditors)); + const extHostCommands = threadService.set(ExtHostContext.ExtHostCommands, new ExtHostCommands(threadService, extHostHeapService)); + const extHostTreeViews = threadService.set(ExtHostContext.ExtHostTreeViews, new ExtHostTreeViews(threadService.get(MainContext.MainThreadTreeViews), extHostCommands)); + const extHostWorkspace = threadService.set(ExtHostContext.ExtHostWorkspace, new ExtHostWorkspace(threadService, initData.workspace)); + const extHostConfiguration = threadService.set(ExtHostContext.ExtHostConfiguration, new ExtHostConfiguration(threadService.get(MainContext.MainThreadConfiguration), extHostWorkspace, initData.configuration)); + const extHostDiagnostics = threadService.set(ExtHostContext.ExtHostDiagnostics, new ExtHostDiagnostics(threadService)); + const languageFeatures = threadService.set(ExtHostContext.ExtHostLanguageFeatures, new ExtHostLanguageFeatures(threadService, extHostDocuments, extHostCommands, extHostHeapService, extHostDiagnostics)); + const extHostFileSystemEvent = threadService.set(ExtHostContext.ExtHostFileSystemEventService, new ExtHostFileSystemEventService()); + const extHostQuickOpen = threadService.set(ExtHostContext.ExtHostQuickOpen, new ExtHostQuickOpen(threadService)); + const extHostTerminalService = threadService.set(ExtHostContext.ExtHostTerminalService, new ExtHostTerminalService(threadService)); + const extHostSCM = threadService.set(ExtHostContext.ExtHostSCM, new ExtHostSCM(threadService, extHostCommands)); + const extHostTask = threadService.set(ExtHostContext.ExtHostTask, new ExtHostTask(threadService)); + const extHostCredentials = threadService.set(ExtHostContext.ExtHostCredentials, new ExtHostCredentials(threadService)); + threadService.set(ExtHostContext.ExtHostExtensionService, extensionService); + + // Check that no named customers are missing + const expected: ProxyIdentifier[] = Object.keys(ExtHostContext).map((key) => ExtHostContext[key]); + threadService.assertRegistered(expected); // Other instances const extHostMessageService = new ExtHostMessageService(threadService); diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index d7cdd224a5a..5bd7b384da8 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -7,7 +7,7 @@ import { createMainContextProxyIdentifier as createMainId, createExtHostContextProxyIdentifier as createExtId, - ProxyIdentifier, IThreadService + ProxyIdentifier } from 'vs/workbench/services/thread/common/threadService'; import * as vscode from 'vscode'; @@ -46,6 +46,7 @@ import { ISelection, Selection } from 'vs/editor/common/core/selection'; import { ITreeItem } from 'vs/workbench/parts/views/common/views'; import { ThemeColor } from 'vs/platform/theme/common/themeService'; +import { IDisposable } from "vs/base/common/lifecycle"; export interface IEnvironment { isExtensionDevelopmentDebug: boolean; @@ -79,7 +80,10 @@ export interface IExtHostContext { */ get(identifier: ProxyIdentifier): T; - set(identifier: ProxyIdentifier, instance: T): void; + /** + * Register manually created instance. + */ + set(identifier: ProxyIdentifier, instance: R): R; } export interface IMainContext { @@ -89,73 +93,32 @@ export interface IMainContext { get(identifier: ProxyIdentifier): T; } -export interface InstanceSetter { - set(instance: T): R; -} - -export class InstanceCollection { - private _items: { [id: string]: any; }; - - constructor() { - this._items = Object.create(null); - } - - public define(id: ProxyIdentifier): InstanceSetter { - let that = this; - return new class { - set(value: T): R { - that._set(id, value); - return value; - } - }; - } - - _set(id: ProxyIdentifier, value: T): void { - this._items[id.id] = value; - } - - public finish(isMain: boolean, threadService: IThreadService): void { - let expected = (isMain ? MainContext : ExtHostContext); - Object.keys(expected).forEach((key) => { - let id = expected[key]; - let value = this._items[id.id]; - - if (!value) { - throw new Error(`Missing actor ${key} (isMain: ${id.isMain}, id: ${id.id})`); - } - threadService.set(id, value); - }); - } -} - // --- main thread -export interface MainThreadCommandsShape { - dispose(): void; +export interface MainThreadCommandsShape extends IDisposable { $registerCommand(id: string): TPromise; $unregisterCommand(id: string): TPromise; $executeCommand(id: string, args: any[]): Thenable; $getCommands(): Thenable; } -export interface MainThreadConfigurationShape { - dispose(): void; +export interface MainThreadConfigurationShape extends IDisposable { $updateConfigurationOption(target: ConfigurationTarget, key: string, value: any, resource: URI): TPromise; $removeConfigurationOption(target: ConfigurationTarget, key: string, resource: URI): TPromise; } -export interface MainThreadDiagnosticsShape { +export interface MainThreadDiagnosticsShape extends IDisposable { $changeMany(owner: string, entries: [URI, IMarkerData[]][]): TPromise; $clear(owner: string): TPromise; } -export interface MainThreadDocumentContentProvidersShape { +export interface MainThreadDocumentContentProvidersShape extends IDisposable { $registerTextContentProvider(handle: number, scheme: string): void; $unregisterTextContentProvider(handle: number): void; $onVirtualDocumentChange(uri: URI, value: ITextSource): void; } -export interface MainThreadDocumentsShape { +export interface MainThreadDocumentsShape extends IDisposable { $tryCreateDocument(options?: { language?: string; content?: string; }): TPromise; $tryOpenDocument(uri: URI): TPromise; $trySaveDocument(uri: URI): TPromise; @@ -205,7 +168,7 @@ export interface ITextDocumentShowOptions { selection?: IRange; } -export interface MainThreadEditorsShape { +export interface MainThreadEditorsShape extends IDisposable { $tryShowTextDocument(resource: URI, options: ITextDocumentShowOptions): TPromise; $registerTextEditorDecorationType(key: string, options: editorCommon.IDecorationRenderOptions): void; $removeTextEditorDecorationType(key: string): void; @@ -220,17 +183,16 @@ export interface MainThreadEditorsShape { $getDiffInformation(id: string): TPromise; } -export interface MainThreadTreeViewsShape { +export interface MainThreadTreeViewsShape extends IDisposable { $registerView(treeViewId: string): void; $refresh(treeViewId: string, treeItemHandles: number[]): void; } -export interface MainThreadErrorsShape { +export interface MainThreadErrorsShape extends IDisposable { $onUnexpectedExtHostError(err: any): void; } -export interface MainThreadLanguageFeaturesShape { - dispose(): void; +export interface MainThreadLanguageFeaturesShape extends IDisposable { $unregister(handle: number): TPromise; $registerOutlineSupport(handle: number, selector: vscode.DocumentSelector): TPromise; $registerCodeLensSupport(handle: number, selector: vscode.DocumentSelector, eventHandle: number): TPromise; @@ -255,15 +217,15 @@ export interface MainThreadLanguageFeaturesShape { $setLanguageConfiguration(handle: number, languageId: string, configuration: vscode.LanguageConfiguration): TPromise; } -export interface MainThreadLanguagesShape { +export interface MainThreadLanguagesShape extends IDisposable { $getLanguages(): TPromise; } -export interface MainThreadMessageServiceShape { +export interface MainThreadMessageServiceShape extends IDisposable { $showMessage(severity: Severity, message: string, options: vscode.MessageOptions, commands: { title: string; isCloseAffordance: boolean; handle: number; }[]): Thenable; } -export interface MainThreadOutputServiceShape { +export interface MainThreadOutputServiceShape extends IDisposable { $append(channelId: string, label: string, value: string): TPromise; $clear(channelId: string, label: string): TPromise; $dispose(channelId: string, label: string): TPromise; @@ -271,14 +233,14 @@ export interface MainThreadOutputServiceShape { $close(channelId: string): TPromise; } -export interface MainThreadProgressShape { +export interface MainThreadProgressShape extends IDisposable { $startProgress(handle: number, options: IProgressOptions): void; $progressReport(handle: number, message: IProgressStep): void; $progressEnd(handle: number): void; } -export interface MainThreadTerminalServiceShape { +export interface MainThreadTerminalServiceShape extends IDisposable { $createTerminal(name?: string, shellPath?: string, shellArgs?: string[], waitOnExit?: boolean): TPromise; $dispose(terminalId: number): void; $hide(terminalId: number): void; @@ -289,30 +251,29 @@ export interface MainThreadTerminalServiceShape { export interface MyQuickPickItems extends IPickOpenEntry { handle: number; } -export interface MainThreadQuickOpenShape { - dispose(): void; +export interface MainThreadQuickOpenShape extends IDisposable { $show(options: IPickOptions): TPromise; $setItems(items: MyQuickPickItems[]): TPromise; $setError(error: Error): TPromise; $input(options: vscode.InputBoxOptions, validateInput: boolean): TPromise; } -export interface MainThreadStatusBarShape { +export interface MainThreadStatusBarShape extends IDisposable { $setEntry(id: number, extensionId: string, text: string, tooltip: string, command: string, color: string | ThemeColor, alignment: MainThreadStatusBarAlignment, priority: number): void; $dispose(id: number); } -export interface MainThreadStorageShape { +export interface MainThreadStorageShape extends IDisposable { $getValue(shared: boolean, key: string): TPromise; $setValue(shared: boolean, key: string, value: any): TPromise; } -export interface MainThreadTelemetryShape { +export interface MainThreadTelemetryShape extends IDisposable { $publicLog(eventName: string, data?: any): void; $getTelemetryInfo(): TPromise; } -export interface MainThreadWorkspaceShape { +export interface MainThreadWorkspaceShape extends IDisposable { $startSearch(include: string, exclude: string, maxResults: number, requestId: number): Thenable; $cancelSearch(requestId: number): Thenable; $saveAll(includeUntitled?: boolean): Thenable; @@ -321,12 +282,12 @@ export interface MainThreadWorkspaceShape { $onFileSystemChange(handle: number, resource: URI): void; } -export interface MainThreadTaskShape { +export interface MainThreadTaskShape extends IDisposable { $registerTaskProvider(handle: number): TPromise; $unregisterTaskProvider(handle: number): TPromise; } -export interface MainThreadExtensionServiceShape { +export interface MainThreadExtensionServiceShape extends IDisposable { $localShowMessage(severity: Severity, msg: string): void; $onExtensionActivated(extensionId: string): void; $onExtensionActivationFailed(extensionId: string): void; @@ -354,7 +315,7 @@ export type SCMRawResource = [ boolean /*faded*/ ]; -export interface MainThreadSCMShape { +export interface MainThreadSCMShape extends IDisposable { $registerSourceControl(handle: number, id: string, label: string): void; $updateSourceControl(handle: number, features: SCMProviderFeatures): void; $unregisterSourceControl(handle: number): void; @@ -370,15 +331,13 @@ export interface MainThreadSCMShape { export type DebugSessionUUID = string; -export interface MainThreadDebugServiceShape { - dispose(): void; +export interface MainThreadDebugServiceShape extends IDisposable { $startDebugging(folderUri: URI | undefined, nameOrConfig: string | vscode.DebugConfiguration): TPromise; $startDebugSession(folderUri: URI | undefined, config: vscode.DebugConfiguration): TPromise; $customDebugAdapterRequest(id: DebugSessionUUID, command: string, args: any): TPromise; } -export interface MainThreadCredentialsShape { - dispose(): void; +export interface MainThreadCredentialsShape extends IDisposable { $readSecret(service: string, account: string): Thenable; $writeSecret(service: string, account: string, secret: string): Thenable; $deleteSecret(service: string, account: string): Thenable; diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index a86d264b3f6..72c91b448ca 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -85,8 +85,12 @@ export class ExtensionService implements IThreadService, IExtensionService { return this._threadService.get(identifier); } - public set(identifier: ProxyIdentifier, value: T): void { - this._threadService.set(identifier, value); + public set(identifier: ProxyIdentifier, value: R): R { + return this._threadService.set(identifier, value); + } + + public assertRegistered(identifiers: ProxyIdentifier[]): void { + this._threadService.assertRegistered(identifiers); } // ---- end IThreadService diff --git a/src/vs/workbench/services/thread/common/threadService.ts b/src/vs/workbench/services/thread/common/threadService.ts index 8906d6b8821..9e694c125a7 100644 --- a/src/vs/workbench/services/thread/common/threadService.ts +++ b/src/vs/workbench/services/thread/common/threadService.ts @@ -19,7 +19,12 @@ export interface IThreadService { /** * Register instance. */ - set(identifier: ProxyIdentifier, value: T): void; + set(identifier: ProxyIdentifier, value: R): R; + + /** + * Assert these identifiers are already registered via `.set`. + */ + assertRegistered(identifiers: ProxyIdentifier[]): void; } export class ProxyIdentifier { diff --git a/src/vs/workbench/services/thread/node/abstractThreadService.ts b/src/vs/workbench/services/thread/node/abstractThreadService.ts index 04fc8ae314e..e5a073c50a8 100644 --- a/src/vs/workbench/services/thread/node/abstractThreadService.ts +++ b/src/vs/workbench/services/thread/node/abstractThreadService.ts @@ -45,6 +45,7 @@ export abstract class AbstractThreadService implements IDispatcher { } private _createProxy(proxyId: string): T { + // TODO@Alex: should all these methods be cached for this proxy ? let handler = { get: (target, name) => { return (...myArgs: any[]) => { @@ -55,11 +56,21 @@ export abstract class AbstractThreadService implements IDispatcher { return new Proxy({}, handler); } - set(identifier: ProxyIdentifier, value: T): void { + set(identifier: ProxyIdentifier, value: R): R { if (identifier.isMain !== this._isMain) { throw new Error('Mismatch in object registration!'); } this._locals[identifier.id] = value; + return value; + } + + assertRegistered(identifiers: ProxyIdentifier[]): void { + for (let i = 0, len = identifiers.length; i < len; i++) { + const identifier = identifiers[i]; + if (!this._locals[identifier.id]) { + throw new Error(`Missing actor ${identifier.id} (isMain: ${identifier.isMain})`); + } + } } private _callOnRemote(proxyId: string, methodName: string, args: any[]): TPromise { diff --git a/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts b/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts index 87937e29740..3be67dd4c9a 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts @@ -16,7 +16,6 @@ import { Model as EditorModel } from 'vs/editor/common/model/model'; import { TestThreadService } from './testThreadService'; import { MarkerService } from 'vs/platform/markers/common/markerService'; import { IMarkerService } from 'vs/platform/markers/common/markers'; -import { IThreadService } from 'vs/workbench/services/thread/common/threadService'; import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ExtHostLanguageFeatures } from 'vs/workbench/api/node/extHostLanguageFeatures'; @@ -81,7 +80,6 @@ suite('ExtHostLanguageFeatureCommands', function () { } }); instantiationService.stub(IMarkerService, new MarkerService()); - instantiationService.stub(IThreadService, threadService); instantiationService.stub(IModelService, { _serviceBrand: IModelService, getModel(): any { return model; }, diff --git a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts index ed935effd4d..be4ae19c5c1 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts @@ -17,7 +17,6 @@ import { Range as EditorRange } from 'vs/editor/common/core/range'; import { TestThreadService } from './testThreadService'; import { IMarkerService } from 'vs/platform/markers/common/markers'; import { MarkerService } from 'vs/platform/markers/common/markerService'; -import { IThreadService } from 'vs/workbench/services/thread/common/threadService'; import { ExtHostLanguageFeatures } from 'vs/workbench/api/node/extHostLanguageFeatures'; import { MainThreadLanguageFeatures } from 'vs/workbench/api/electron-browser/mainThreadLanguageFeatures'; import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands'; @@ -73,7 +72,6 @@ suite('ExtHostLanguageFeatures', function () { let inst: IInstantiationService; { let instantiationService = new TestInstantiationService(); - instantiationService.stub(IThreadService, threadService); instantiationService.stub(IMarkerService, MarkerService); instantiationService.stub(IHeapService, { _serviceBrand: undefined, diff --git a/src/vs/workbench/test/electron-browser/api/extHostTextEditor.test.ts b/src/vs/workbench/test/electron-browser/api/extHostTextEditor.test.ts index c8834439d40..59c4972297b 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostTextEditor.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostTextEditor.test.ts @@ -49,6 +49,7 @@ suite('ExtHostTextEditorOptions', () => { setup(() => { calls = []; let mockProxy: MainThreadEditorsShape = { + dispose: undefined, $trySetOptions: (id: string, options: ITextEditorConfigurationUpdate) => { assert.equal(id, '1'); calls.push(options); diff --git a/src/vs/workbench/test/electron-browser/api/extHostTreeViews.test.ts b/src/vs/workbench/test/electron-browser/api/extHostTreeViews.test.ts index 72794bd59c8..fc4e433a375 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostTreeViews.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostTreeViews.test.ts @@ -14,7 +14,6 @@ import { TreeDataProvider, TreeItem } from 'vscode'; import { TestThreadService } from './testThreadService'; import { ExtHostHeapService } from 'vs/workbench/api/node/extHostHeapService'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; -import { IThreadService } from 'vs/workbench/services/thread/common/threadService'; import { MainThreadCommands } from 'vs/workbench/api/electron-browser/mainThreadCommands'; import { IInstantiationService } from "vs/platform/instantiation/common/instantiation"; import { mock } from "vs/workbench/test/electron-browser/api/mock"; @@ -44,7 +43,6 @@ suite('ExtHostConfiguration', function () { let inst: IInstantiationService; { let instantiationService = new TestInstantiationService(); - instantiationService.stub(IThreadService, threadService); inst = instantiationService; } diff --git a/src/vs/workbench/test/electron-browser/api/testThreadService.ts b/src/vs/workbench/test/electron-browser/api/testThreadService.ts index 5ca2a4fb331..e2917bb299d 100644 --- a/src/vs/workbench/test/electron-browser/api/testThreadService.ts +++ b/src/vs/workbench/test/electron-browser/api/testThreadService.ts @@ -14,8 +14,10 @@ export function OneGetThreadService(thing: any): IThreadService { get(): T { return thing; }, - set(): void { - } + set(identifier: ProxyIdentifier, value: R): R { + return value; + }, + assertRegistered: undefined }; } @@ -63,11 +65,12 @@ export abstract class AbstractTestThreadService { return new Proxy({}, handler); } - set(identifier: ProxyIdentifier, value: T): void { + set(identifier: ProxyIdentifier, value: R): R { if (identifier.isMain !== this._isMain) { throw new Error('Mismatch in object registration!'); } this._locals[identifier.id] = value; + return value; } protected abstract _callOnRemote(proxyId: string, path: string, args: any[]): TPromise; @@ -154,4 +157,8 @@ export class TestThreadService extends AbstractTestThreadService implements IThr }); }); } + + public assertRegistered(identifiers: ProxyIdentifier[]): void { + throw new Error('Not implemented!'); + } }