mirror of
https://github.com/Microsoft/vscode
synced 2024-08-27 13:00:58 +00:00
Fixes #14500: wait for extension points
* Use the extension descriptions available in the UI thread * Don't wait for handling extension points until extension host is running * Create the extension host process only after extension points have been handled * Send the initial configuration as part of the initData to the extension host
This commit is contained in:
parent
d801542057
commit
6dd7a04bbd
|
@ -78,6 +78,12 @@ export abstract class AbstractExtensionService<T extends ActivatedExtension> imp
|
|||
});
|
||||
}
|
||||
|
||||
public readExtensions(): TPromise<IExtensionDescription[]> {
|
||||
return this.onReady().then(() => {
|
||||
return this._registry.getAllExtensionDescriptions();
|
||||
});
|
||||
}
|
||||
|
||||
public getExtensionsStatus(): { [id: string]: IExtensionsStatus } {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ import * as vscode from 'vscode';
|
|||
import * as paths from 'vs/base/common/paths';
|
||||
import { realpathSync } from 'fs';
|
||||
import { ITelemetryService, ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { MainContext, ExtHostContext, InstanceCollection } from './extHost.protocol';
|
||||
import { MainContext, ExtHostContext, InstanceCollection, IInitConfiguration } from './extHost.protocol';
|
||||
|
||||
|
||||
export interface IExtensionApiFactory {
|
||||
|
@ -53,7 +53,7 @@ export interface IExtensionApiFactory {
|
|||
/**
|
||||
* This method instantiates and returns the extension API surface
|
||||
*/
|
||||
export function createApiFactory(threadService: IThreadService, extensionService: ExtHostExtensionService, contextService: IWorkspaceContextService, telemetryService: ITelemetryService): IExtensionApiFactory {
|
||||
export function createApiFactory(initDataConfiguration: IInitConfiguration, threadService: IThreadService, extensionService: ExtHostExtensionService, contextService: IWorkspaceContextService, telemetryService: ITelemetryService): IExtensionApiFactory {
|
||||
|
||||
|
||||
|
||||
|
@ -64,7 +64,7 @@ export function createApiFactory(threadService: IThreadService, extensionService
|
|||
const extHostDocumentSaveParticipant = col.define(ExtHostContext.ExtHostDocumentSaveParticipant).set<ExtHostDocumentSaveParticipant>(new ExtHostDocumentSaveParticipant(extHostDocuments, threadService.get(MainContext.MainThreadWorkspace)));
|
||||
const extHostEditors = col.define(ExtHostContext.ExtHostEditors).set<ExtHostEditors>(new ExtHostEditors(threadService, extHostDocuments));
|
||||
const extHostCommands = col.define(ExtHostContext.ExtHostCommands).set<ExtHostCommands>(new ExtHostCommands(threadService, extHostEditors, extHostHeapService));
|
||||
const extHostConfiguration = col.define(ExtHostContext.ExtHostConfiguration).set<ExtHostConfiguration>(new ExtHostConfiguration(threadService.get(MainContext.MainThreadConfiguration)));
|
||||
const extHostConfiguration = col.define(ExtHostContext.ExtHostConfiguration).set<ExtHostConfiguration>(new ExtHostConfiguration(threadService.get(MainContext.MainThreadConfiguration), initDataConfiguration));
|
||||
const extHostDiagnostics = col.define(ExtHostContext.ExtHostDiagnostics).set<ExtHostDiagnostics>(new ExtHostDiagnostics(threadService));
|
||||
const languageFeatures = col.define(ExtHostContext.ExtHostLanguageFeatures).set<ExtHostLanguageFeatures>(new ExtHostLanguageFeatures(threadService, extHostDocuments, extHostCommands, extHostHeapService, extHostDiagnostics));
|
||||
const extHostFileSystemEvent = col.define(ExtHostContext.ExtHostFileSystemEventService).set<ExtHostFileSystemEventService>(new ExtHostFileSystemEventService());
|
||||
|
|
|
@ -43,6 +43,10 @@ export interface IEnvironment {
|
|||
extensionTestsPath: string;
|
||||
}
|
||||
|
||||
export interface IInitConfiguration {
|
||||
_initConfigurationBrand: void;
|
||||
}
|
||||
|
||||
export interface IInitData {
|
||||
parentPid: number;
|
||||
environment: IEnvironment;
|
||||
|
@ -50,6 +54,7 @@ export interface IInitData {
|
|||
workspace: IWorkspace;
|
||||
};
|
||||
extensions: IExtensionDescription[];
|
||||
configuration: IInitConfiguration;
|
||||
}
|
||||
|
||||
export interface InstanceSetter<T> {
|
||||
|
@ -213,7 +218,6 @@ export abstract class MainThreadWorkspaceShape {
|
|||
}
|
||||
|
||||
export abstract class MainProcessExtensionServiceShape {
|
||||
$onExtensionHostReady(extensionDescriptions: IExtensionDescription[]): TPromise<void> { throw ni(); }
|
||||
$localShowMessage(severity: Severity, msg: string): void { throw ni(); }
|
||||
$onExtensionActivated(extensionId: string): void { throw ni(); }
|
||||
$onExtensionActivationFailed(extensionId: string): void { throw ni(); }
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
'use strict';
|
||||
|
||||
import { mixin } from 'vs/base/common/objects';
|
||||
import { illegalState } from 'vs/base/common/errors';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { WorkspaceConfiguration } from 'vscode';
|
||||
import { ExtHostConfigurationShape, MainThreadConfigurationShape } from './extHost.protocol';
|
||||
|
@ -14,13 +13,13 @@ import { ConfigurationTarget } from 'vs/workbench/services/configuration/common/
|
|||
export class ExtHostConfiguration extends ExtHostConfigurationShape {
|
||||
|
||||
private _proxy: MainThreadConfigurationShape;
|
||||
private _hasConfig: boolean;
|
||||
private _config: any;
|
||||
private _onDidChangeConfiguration = new Emitter<void>();
|
||||
|
||||
constructor(proxy: MainThreadConfigurationShape) {
|
||||
constructor(proxy: MainThreadConfigurationShape, configuration: any) {
|
||||
super();
|
||||
this._proxy = proxy;
|
||||
this._config = configuration;
|
||||
}
|
||||
|
||||
get onDidChangeConfiguration(): Event<void> {
|
||||
|
@ -29,14 +28,10 @@ export class ExtHostConfiguration extends ExtHostConfigurationShape {
|
|||
|
||||
public $acceptConfigurationChanged(config: any) {
|
||||
this._config = config;
|
||||
this._hasConfig = true;
|
||||
this._onDidChangeConfiguration.fire(undefined);
|
||||
}
|
||||
|
||||
public getConfiguration(section?: string): WorkspaceConfiguration {
|
||||
if (!this._hasConfig) {
|
||||
throw illegalState('missing config');
|
||||
}
|
||||
|
||||
const config = section
|
||||
? ExtHostConfiguration._lookUp(section, this._config)
|
||||
|
|
|
@ -119,7 +119,7 @@ export class ExtHostExtensionService extends AbstractExtensionService<ExtHostExt
|
|||
* This class is constructed manually because it is a service, so it doesn't use any ctor injection
|
||||
*/
|
||||
constructor(availableExtensions: IExtensionDescription[], threadService: IThreadService, telemetryService: ITelemetryService, args: { _serviceBrand: any; workspaceStoragePath: string; }) {
|
||||
super(false);
|
||||
super(true);
|
||||
this._registry.registerExtensions(availableExtensions);
|
||||
this._threadService = threadService;
|
||||
this._storage = new ExtHostStorage(threadService);
|
||||
|
@ -186,14 +186,6 @@ export class ExtHostExtensionService extends AbstractExtensionService<ExtHostExt
|
|||
return result;
|
||||
}
|
||||
|
||||
public registrationDone(): void {
|
||||
this._proxy.$onExtensionHostReady(this._registry.getAllExtensionDescriptions()).then(() => {
|
||||
// Wait for the main process to acknowledge its receival of the extensions descriptions
|
||||
// before allowing extensions to be activated
|
||||
this._triggerOnReady();
|
||||
});
|
||||
}
|
||||
|
||||
// -- overwriting AbstractExtensionService
|
||||
|
||||
protected _showMessage(severity: Severity, msg: string): void {
|
||||
|
|
|
@ -25,7 +25,6 @@ export class MainThreadConfiguration extends MainThreadConfigurationShape {
|
|||
this._configurationEditingService = configurationEditingService;
|
||||
const proxy = threadService.get(ExtHostContext.ExtHostConfiguration);
|
||||
this._toDispose = configurationService.onDidUpdateConfiguration(event => proxy.$acceptConfigurationChanged(event.config));
|
||||
proxy.$acceptConfigurationChanged(configurationService.getConfiguration());
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
import Severity from 'vs/base/common/severity';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { AbstractExtensionService, ActivatedExtension } from 'vs/platform/extensions/common/abstractExtensionService';
|
||||
import { IMessage, IExtensionDescription, IExtensionsStatus } from 'vs/platform/extensions/common/extensions';
|
||||
import { IExtensionsRuntimeService, IMessage, IExtensionDescription, IExtensionsStatus } from 'vs/platform/extensions/common/extensions';
|
||||
import { ExtensionsRegistry, ExtensionPoint, IExtensionPointUser, ExtensionMessageCollector } from 'vs/platform/extensions/common/extensionsRegistry';
|
||||
import { IMessageService } from 'vs/platform/message/common/message';
|
||||
import { IThreadService } from 'vs/workbench/services/thread/common/threadService';
|
||||
|
@ -53,7 +53,8 @@ export class MainProcessExtensionService extends AbstractExtensionService<Activa
|
|||
constructor(
|
||||
@IThreadService threadService: IThreadService,
|
||||
@IMessageService messageService: IMessageService,
|
||||
@IEnvironmentService private environmentService: IEnvironmentService
|
||||
@IEnvironmentService private environmentService: IEnvironmentService,
|
||||
@IExtensionsRuntimeService extensionsRuntimeService: IExtensionsRuntimeService
|
||||
) {
|
||||
super(false);
|
||||
this._isDev = !environmentService.isBuilt || !!environmentService.extensionDevelopmentPath;
|
||||
|
@ -62,6 +63,8 @@ export class MainProcessExtensionService extends AbstractExtensionService<Activa
|
|||
this._threadService = threadService;
|
||||
this._proxy = this._threadService.get(ExtHostContext.ExtHostExtensionService);
|
||||
this._extensionsStatus = {};
|
||||
|
||||
extensionsRuntimeService.getExtensions().then((extensionDescriptions) => this._onExtensionDescriptions(extensionDescriptions));
|
||||
}
|
||||
|
||||
private _handleMessage(msg: IMessage) {
|
||||
|
@ -124,7 +127,7 @@ export class MainProcessExtensionService extends AbstractExtensionService<Activa
|
|||
|
||||
// -- called by extension host
|
||||
|
||||
public $onExtensionHostReady(extensionDescriptions: IExtensionDescription[]): TPromise<void> {
|
||||
private _onExtensionDescriptions(extensionDescriptions: IExtensionDescription[]): void {
|
||||
this._registry.registerExtensions(extensionDescriptions);
|
||||
|
||||
let availableExtensions = this._registry.getAllExtensionDescriptions();
|
||||
|
@ -135,7 +138,6 @@ export class MainProcessExtensionService extends AbstractExtensionService<Activa
|
|||
}
|
||||
|
||||
this._triggerOnReady();
|
||||
return;
|
||||
}
|
||||
|
||||
private _handleExtensionPoint<T>(extensionPoint: ExtensionPoint<T>, availableExtensions: IExtensionDescription[]): void {
|
||||
|
|
|
@ -296,12 +296,13 @@ export class WorkbenchShell {
|
|||
serviceCollection.set(IExtensionsRuntimeService, extensionsRuntimeService);
|
||||
disposables.add(extensionsRuntimeService);
|
||||
|
||||
const extensionHostProcessWorker = this.startExtensionHost(instantiationService);
|
||||
const extensionHostProcessWorker = instantiationService.createInstance(ExtensionHostProcessWorker);
|
||||
this.threadService = instantiationService.createInstance(MainThreadService, extensionHostProcessWorker.messagingProtocol);
|
||||
serviceCollection.set(IThreadService, this.threadService);
|
||||
|
||||
const extensionService = instantiationService.createInstance(MainProcessExtensionService);
|
||||
serviceCollection.set(IExtensionService, extensionService);
|
||||
extensionHostProcessWorker.start(extensionService);
|
||||
|
||||
serviceCollection.set(ICommandService, new CommandService(instantiationService, extensionService));
|
||||
|
||||
|
@ -454,12 +455,6 @@ export class WorkbenchShell {
|
|||
this.workbench.layout();
|
||||
}
|
||||
|
||||
private startExtensionHost(instantiationService: InstantiationService): ExtensionHostProcessWorker {
|
||||
const extensionHostProcessWorker: ExtensionHostProcessWorker = <ExtensionHostProcessWorker>instantiationService.createInstance(ExtensionHostProcessWorker);
|
||||
extensionHostProcessWorker.start();
|
||||
return extensionHostProcessWorker;
|
||||
}
|
||||
|
||||
public joinCreation(): TPromise<boolean> {
|
||||
return this.workbench.joinCreation();
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ export class ExtensionHostMain {
|
|||
this._extensionService = new ExtHostExtensionService(initData.extensions, threadService, telemetryService, { _serviceBrand: 'optionalArgs', workspaceStoragePath });
|
||||
|
||||
// Create the ext host API
|
||||
const factory = createApiFactory(threadService, this._extensionService, this._contextService, telemetryService);
|
||||
const factory = createApiFactory(initData.configuration, threadService, this._extensionService, this._contextService, telemetryService);
|
||||
defineAPI(factory, this._extensionService);
|
||||
}
|
||||
|
||||
|
@ -107,7 +107,7 @@ export class ExtensionHostMain {
|
|||
}
|
||||
|
||||
public start(): TPromise<void> {
|
||||
return this.registerExtensions();
|
||||
return this.handleEagerExtensions().then(() => this.handleExtensionTests());
|
||||
}
|
||||
|
||||
public terminate(): void {
|
||||
|
@ -142,11 +142,6 @@ export class ExtensionHostMain {
|
|||
}, 1000);
|
||||
}
|
||||
|
||||
private registerExtensions(): TPromise<void> {
|
||||
this._extensionService.registrationDone();
|
||||
return this.handleEagerExtensions().then(() => this.handleExtensionTests());
|
||||
}
|
||||
|
||||
// Handle "eager" activation extensions
|
||||
private handleEagerExtensions(): TPromise<void> {
|
||||
this._extensionService.activateByEvent('*').then(null, (err) => {
|
||||
|
|
|
@ -22,11 +22,12 @@ import { ipcRenderer as ipc } from 'electron';
|
|||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { ReloadWindowAction } from 'vs/workbench/electron-browser/actions';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IExtensionsRuntimeService } from 'vs/platform/extensions/common/extensions';
|
||||
import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { createQueuedSender, IQueuedSender } from 'vs/base/node/processes';
|
||||
import { IInitData } from 'vs/workbench/api/node/extHost.protocol';
|
||||
import { IInitData, IInitConfiguration } from 'vs/workbench/api/node/extHost.protocol';
|
||||
import { MainProcessExtensionService } from 'vs/workbench/api/node/mainThreadExtensionService';
|
||||
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
|
||||
|
||||
export const EXTENSION_LOG_BROADCAST_CHANNEL = 'vscode:extensionLog';
|
||||
export const EXTENSION_ATTACH_BROADCAST_CHANNEL = 'vscode:extensionAttach';
|
||||
|
@ -58,6 +59,8 @@ export class ExtensionHostProcessWorker {
|
|||
return this._onMessage.event;
|
||||
}
|
||||
|
||||
private extensionService: MainProcessExtensionService;
|
||||
|
||||
constructor(
|
||||
@IWorkspaceContextService private contextService: IWorkspaceContextService,
|
||||
@IMessageService private messageService: IMessageService,
|
||||
|
@ -65,7 +68,7 @@ export class ExtensionHostProcessWorker {
|
|||
@ILifecycleService lifecycleService: ILifecycleService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IEnvironmentService private environmentService: IEnvironmentService,
|
||||
@IExtensionsRuntimeService private extensionsRuntimeService: IExtensionsRuntimeService
|
||||
@IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService
|
||||
) {
|
||||
// handle extension host lifecycle a bit special when we know we are developing an extension that runs inside
|
||||
this.isExtensionDevelopmentHost = !!environmentService.extensionDevelopmentPath;
|
||||
|
@ -78,7 +81,8 @@ export class ExtensionHostProcessWorker {
|
|||
lifecycleService.onShutdown(() => this.terminate());
|
||||
}
|
||||
|
||||
public start(): void {
|
||||
public start(extensionService: MainProcessExtensionService): void {
|
||||
this.extensionService = extensionService;
|
||||
let opts: any = {
|
||||
env: objects.mixin(objects.clone(process.env), {
|
||||
AMD_ENTRYPOINT: 'vs/workbench/node/extensionHostProcess',
|
||||
|
@ -200,7 +204,7 @@ export class ExtensionHostProcessWorker {
|
|||
if (this.initializeTimer) {
|
||||
window.clearTimeout(this.initializeTimer);
|
||||
}
|
||||
this.extensionsRuntimeService.getExtensions().then(extensionDescriptors => {
|
||||
this.extensionService.readExtensions().then((extensionDescriptions) => {
|
||||
let initData: IInitData = {
|
||||
parentPid: process.pid,
|
||||
environment: {
|
||||
|
@ -213,7 +217,8 @@ export class ExtensionHostProcessWorker {
|
|||
contextService: {
|
||||
workspace: this.contextService.getWorkspace()
|
||||
},
|
||||
extensions: extensionDescriptors
|
||||
extensions: extensionDescriptions,
|
||||
configuration: this.configurationService.getConfiguration<IInitConfiguration>()
|
||||
};
|
||||
this.extensionHostProcessQueuedSender.send(stringify(initData));
|
||||
});
|
||||
|
|
|
@ -25,15 +25,9 @@ suite('ExtHostConfiguration', function () {
|
|||
if (!shape) {
|
||||
shape = new class extends MainThreadConfigurationShape { };
|
||||
}
|
||||
const result = new ExtHostConfiguration(shape);
|
||||
result.$acceptConfigurationChanged(data);
|
||||
return result;
|
||||
return new ExtHostConfiguration(shape, data);
|
||||
}
|
||||
|
||||
test('check illegal state', function () {
|
||||
assert.throws(() => new ExtHostConfiguration(new class extends MainThreadConfigurationShape { }).getConfiguration('foo'));
|
||||
});
|
||||
|
||||
test('udate / section to key', function () {
|
||||
|
||||
const shape = new RecordingShape();
|
||||
|
|
Loading…
Reference in a new issue