提交 17243e76 编写于 作者: A Alex Dima

Simplify extension point handling

上级 746dcead
......@@ -12,7 +12,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
import mime = require('vs/base/common/mime');
import { IFilesConfiguration } from 'vs/platform/files/common/files';
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
import { IExtensionPointUser, IExtensionMessageCollector, ExtensionsRegistry } from 'vs/platform/extensions/common/extensionsRegistry';
import { IExtensionPoint, IExtensionPointUser, IExtensionMessageCollector, ExtensionsRegistry } from 'vs/platform/extensions/common/extensionsRegistry';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import * as modes from 'vs/editor/common/modes';
import { FrankensteinMode } from 'vs/editor/common/modes/abstractMode';
......@@ -24,7 +24,7 @@ import { AbstractState } from 'vs/editor/common/modes/abstractState';
import { Token } from 'vs/editor/common/core/token';
import { ModeTransition } from 'vs/editor/common/core/modeTransition';
let languagesExtPoint = ExtensionsRegistry.registerExtensionPoint<ILanguageExtensionPoint[]>('languages', {
export const languagesExtPoint: IExtensionPoint<ILanguageExtensionPoint[]> = ExtensionsRegistry.registerExtensionPoint<ILanguageExtensionPoint[]>('languages', [], {
description: nls.localize('vscode.extension.contributes.languages', 'Contributes language declarations.'),
type: 'array',
items: {
......
......@@ -13,13 +13,14 @@ import { IExtensionMessageCollector, ExtensionsRegistry } from 'vs/platform/exte
import { ISnippetsRegistry, Extensions, ISnippet } from 'vs/editor/common/modes/snippetsRegistry';
import { IModeService } from 'vs/editor/common/services/modeService';
import platform = require('vs/platform/platform');
import { languagesExtPoint } from 'vs/editor/common/services/modeServiceImpl';
export interface ISnippetsExtensionPoint {
language: string;
path: string;
}
let snippetsExtensionPoint = ExtensionsRegistry.registerExtensionPoint<ISnippetsExtensionPoint[]>('snippets', {
let snippetsExtensionPoint = ExtensionsRegistry.registerExtensionPoint<ISnippetsExtensionPoint[]>('snippets', [languagesExtPoint], {
description: nls.localize('vscode.extension.contributes.snippets', 'Contributes snippets.'),
type: 'array',
defaultSnippets: [{ body: [{ language: '', path: '' }] }],
......@@ -56,7 +57,7 @@ export class MainProcessTextMateSnippet {
}
private _withSnippetContribution(extensionName: string, extensionFolderPath: string, snippet: ISnippetsExtensionPoint, collector: IExtensionMessageCollector): void {
if (!snippet.language || (typeof snippet.language !== 'string')) {
if (!snippet.language || (typeof snippet.language !== 'string') || !this._modeService.isRegisteredMode(snippet.language)) {
collector.error(nls.localize('invalid.language', "Unknown language in `contributes.{0}.language`. Provided value: {1}", snippetsExtensionPoint.name, String(snippet.language)));
return;
}
......
......@@ -17,6 +17,7 @@ import { IModeService } from 'vs/editor/common/services/modeService';
import { IGrammar, Registry, StackElement, IToken } from 'vscode-textmate';
import { ModeTransition } from 'vs/editor/common/core/modeTransition';
import { Token } from 'vs/editor/common/core/token';
import { languagesExtPoint } from 'vs/editor/common/services/modeServiceImpl';
export interface ITMSyntaxExtensionPoint {
language: string;
......@@ -26,7 +27,7 @@ export interface ITMSyntaxExtensionPoint {
}
// TODO@Martin TS(2.0.2) - Type IJsonSchema has no defined property require. Keeping semantic using any cast
export const grammarsExtPoint: IExtensionPoint<ITMSyntaxExtensionPoint[]> = ExtensionsRegistry.registerExtensionPoint<ITMSyntaxExtensionPoint[]>('grammars', <any>{
export const grammarsExtPoint: IExtensionPoint<ITMSyntaxExtensionPoint[]> = ExtensionsRegistry.registerExtensionPoint<ITMSyntaxExtensionPoint[]>('grammars', [languagesExtPoint], <any>{
description: nls.localize('vscode.extension.contributes.grammars', 'Contributes textmate tokenizers.'),
type: 'array',
defaultSnippets: [{ body: [{ language: '{{id}}', scopeName: 'source.{{id}}', path: './syntaxes/{{id}}.tmLanguage.' }] }],
......
......@@ -208,7 +208,7 @@ namespace schema {
};
}
ExtensionsRegistry.registerExtensionPoint<schema.IUserFriendlyCommand | schema.IUserFriendlyCommand[]>('commands', schema.commandsContribution).setHandler(extensions => {
ExtensionsRegistry.registerExtensionPoint<schema.IUserFriendlyCommand | schema.IUserFriendlyCommand[]>('commands', [], schema.commandsContribution).setHandler(extensions => {
const ids = new IdGenerator('contrib-cmd-icon-');
......@@ -251,7 +251,7 @@ ExtensionsRegistry.registerExtensionPoint<schema.IUserFriendlyCommand | schema.I
});
ExtensionsRegistry.registerExtensionPoint<{ [loc: string]: schema.IUserFriendlyMenuItem[] }>('menus', schema.menusContribtion).setHandler(extensions => {
ExtensionsRegistry.registerExtensionPoint<{ [loc: string]: schema.IUserFriendlyMenuItem[] }>('menus', [], schema.menusContribtion).setHandler(extensions => {
for (let extension of extensions) {
const {value, collector} = extension;
......
......@@ -88,7 +88,7 @@ class ConfigurationRegistry implements IConfigurationRegistry {
const configurationRegistry = new ConfigurationRegistry();
Registry.add(Extensions.Configuration, configurationRegistry);
const configurationExtPoint = ExtensionsRegistry.registerExtensionPoint<IConfigurationNode>('configuration', {
const configurationExtPoint = ExtensionsRegistry.registerExtensionPoint<IConfigurationNode>('configuration', [], {
description: nls.localize('vscode.extension.contributes.configuration', 'Contributes configuration settings.'),
type: 'object',
defaultSnippets: [{ body: { title: '', properties: {} } }],
......
......@@ -29,10 +29,6 @@ export interface IActivationEventListener {
(): void;
}
export interface IPointListener {
(desc: IExtensionDescription[]): void;
}
export const IExtensionService = createDecorator<IExtensionService>('extensionService');
export interface IMessage {
......
......@@ -8,7 +8,7 @@ import * as nls from 'vs/nls';
import { onUnexpectedError } from 'vs/base/common/errors';
import { IJSONSchema } from 'vs/base/common/jsonSchema';
import Severity from 'vs/base/common/severity';
import { IActivationEventListener, IMessage, IExtensionDescription, IPointListener } from 'vs/platform/extensions/common/extensions';
import { IActivationEventListener, IMessage, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { Extensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
import { Registry } from 'vs/platform/platform';
......@@ -18,7 +18,7 @@ export interface IExtensionMessageCollector {
info(message: string): void;
}
class ExtensionMessageCollector implements IExtensionMessageCollector {
export class ExtensionMessageCollector implements IExtensionMessageCollector {
private _messageHandler: (msg: IMessage) => void;
private _source: string;
......@@ -83,52 +83,57 @@ export interface IExtensionsRegistry {
registerOneTimeActivationEventListener(activationEvent: string, listener: IActivationEventListener): void;
triggerActivationEventListeners(activationEvent: string): void;
registerExtensionPoint<T>(extensionPoint: string, jsonSchema: IJSONSchema): IExtensionPoint<T>;
handleExtensionPoints(messageHandler: (msg: IMessage) => void): void;
registerExtensionPoint<T>(extensionPoint: string, deps: IExtensionPoint<any>[], jsonSchema: IJSONSchema): IExtensionPoint<T>;
getExtensionPoints(): ExtensionPoint<any>[];
}
class ExtensionPoint<T> implements IExtensionPoint<T> {
export class ExtensionPoint<T> implements IExtensionPoint<T> {
public name: string;
private _registry: ExtensionsRegistryImpl;
public readonly name: string;
private _handler: IExtensionPointHandler<T>;
private _messageHandler: (msg: IMessage) => void;
private _users: IExtensionPointUser<T>[];
private _done: boolean;
constructor(name: string, registry: ExtensionsRegistryImpl) {
constructor(name: string) {
this.name = name;
this._registry = registry;
this._handler = null;
this._messageHandler = null;
this._users = null;
this._done = false;
}
setHandler(handler: IExtensionPointHandler<T>): void {
if (this._handler) {
if (this._handler !== null || this._done) {
throw new Error('Handler already set!');
}
this._handler = handler;
this._handle();
}
handle(messageHandler: (msg: IMessage) => void): void {
this._messageHandler = messageHandler;
acceptUsers(users: IExtensionPointUser<T>[]): void {
if (this._users !== null || this._done) {
throw new Error('Users already set!');
}
this._users = users;
this._handle();
}
private _handle(): void {
if (!this._handler || !this._messageHandler) {
if (this._handler === null || this._users === null) {
return;
}
this._done = true;
this._registry.registerPointListener(this.name, (descriptions: IExtensionDescription[]) => {
let users = descriptions.map((desc) => {
return {
description: desc,
value: desc.contributes[this.name],
collector: new ExtensionMessageCollector(this._messageHandler, desc.extensionFolderPath)
};
});
this._handler(users);
});
let handler = this._handler;
this._handler = null;
let users = this._users;
this._users = null;
try {
handler(users);
} catch (err) {
onUnexpectedError(err);
}
}
}
......@@ -252,17 +257,11 @@ const schema: IJSONSchema = {
}
};
interface IPointListenerEntry {
extensionPoint: string;
listener: IPointListener;
}
class ExtensionsRegistryImpl implements IExtensionsRegistry {
private _extensionsMap: IExtensionDescriptionMap;
private _extensionsArr: IExtensionDescription[];
private _activationMap: { [activationEvent: string]: IExtensionDescription[]; };
private _pointListeners: IPointListenerEntry[];
private _oneTimeActivationEventListeners: { [activationEvent: string]: IActivationEventListener[]; };
private _extensionPoints: { [extPoint: string]: ExtensionPoint<any>; };
......@@ -270,25 +269,15 @@ class ExtensionsRegistryImpl implements IExtensionsRegistry {
this._extensionsMap = {};
this._extensionsArr = [];
this._activationMap = {};
this._pointListeners = [];
this._extensionPoints = {};
this._oneTimeActivationEventListeners = {};
}
public registerPointListener(point: string, handler: IPointListener): void {
let entry = {
extensionPoint: point,
listener: handler
};
this._pointListeners.push(entry);
this._triggerPointListener(entry, ExtensionsRegistryImpl._filterWithExtPoint(this.getAllExtensionDescriptions(), point));
}
public registerExtensionPoint<T>(extensionPoint: string, jsonSchema: IJSONSchema): IExtensionPoint<T> {
public registerExtensionPoint<T>(extensionPoint: string, deps: IExtensionPoint<any>[], jsonSchema: IJSONSchema): IExtensionPoint<T> {
if (hasOwnProperty.call(this._extensionPoints, extensionPoint)) {
throw new Error('Duplicate extension point: ' + extensionPoint);
}
let result = new ExtensionPoint<T>(extensionPoint, this);
let result = new ExtensionPoint<T>(extensionPoint);
this._extensionPoints[extensionPoint] = result;
schema.properties['contributes'].properties[extensionPoint] = jsonSchema;
......@@ -297,22 +286,8 @@ class ExtensionsRegistryImpl implements IExtensionsRegistry {
return result;
}
public handleExtensionPoints(messageHandler: (msg: IMessage) => void): void {
Object.keys(this._extensionPoints).forEach((extensionPointName) => {
this._extensionPoints[extensionPointName].handle(messageHandler);
});
}
private _triggerPointListener(handler: IPointListenerEntry, desc: IExtensionDescription[]): void {
// console.log('_triggerPointListeners: ' + desc.length + ' OF ' + handler.extensionPoint);
if (!desc || desc.length === 0) {
return;
}
try {
handler.listener(desc);
} catch (e) {
onUnexpectedError(e);
}
public getExtensionPoints(): ExtensionPoint<any>[] {
return Object.keys(this._extensionPoints).map(point => this._extensionPoints[point]);
}
public registerExtensions(extensionDescriptions: IExtensionDescription[]): void {
......@@ -336,18 +311,6 @@ class ExtensionsRegistryImpl implements IExtensionsRegistry {
}
}
}
for (let i = 0, len = this._pointListeners.length; i < len; i++) {
let listenerEntry = this._pointListeners[i];
let descriptions = ExtensionsRegistryImpl._filterWithExtPoint(extensionDescriptions, listenerEntry.extensionPoint);
this._triggerPointListener(listenerEntry, descriptions);
}
}
private static _filterWithExtPoint(input: IExtensionDescription[], point: string): IExtensionDescription[] {
return input.filter((desc) => {
return (desc.contributes && hasOwnProperty.call(desc.contributes, point));
});
}
public getExtensionDescriptionsForActivationEvent(activationEvent: string): IExtensionDescription[] {
......
......@@ -15,7 +15,7 @@ interface IJSONValidationExtensionPoint {
url: string;
}
let configurationExtPoint = ExtensionsRegistry.registerExtensionPoint<IJSONValidationExtensionPoint[]>('jsonValidation', {
let configurationExtPoint = ExtensionsRegistry.registerExtensionPoint<IJSONValidationExtensionPoint[]>('jsonValidation', [], {
description: nls.localize('contributes.jsonValidation', 'Contributes json schema configuration.'),
type: 'array',
defaultSnippets: [{ body: [{ fileMatch: '{{file.json}}', url: '{{url}}' }] }],
......
......@@ -18,7 +18,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
import { IMarkerData } from 'vs/platform/markers/common/markers';
import { Position as EditorPosition } from 'vs/platform/editor/common/editor';
import { IMessage, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { StatusbarAlignment as MainThreadStatusBarAlignment } from 'vs/platform/statusbar/common/statusbar';
import { ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry';
import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
......@@ -195,7 +195,7 @@ export abstract class MainThreadWorkspaceShape {
}
export abstract class MainProcessExtensionServiceShape {
$onExtensionHostReady(extensionDescriptions: IExtensionDescription[], messages: IMessage[]): TPromise<void> { throw ni(); }
$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(); }
......
......@@ -10,7 +10,7 @@ import * as paths from 'vs/base/common/paths';
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 } from 'vs/platform/extensions/common/extensions';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { ExtensionsRegistry } from 'vs/platform/extensions/common/extensionsRegistry';
import { ExtHostStorage } from 'vs/workbench/api/node/extHostStorage';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
......@@ -178,8 +178,8 @@ export class ExtHostExtensionService extends AbstractExtensionService<ExtHostExt
return result;
}
public registrationDone(messages: IMessage[]): void {
this._proxy.$onExtensionHostReady(ExtensionsRegistry.getAllExtensionDescriptions(), messages).then(() => {
public registrationDone(): void {
this._proxy.$onExtensionHostReady(ExtensionsRegistry.getAllExtensionDescriptions()).then(() => {
// Wait for the main process to acknowledge its receival of the extensions descriptions
// before allowing extensions to be activated
this._triggerOnReady();
......
......@@ -8,7 +8,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 { ExtensionsRegistry } from 'vs/platform/extensions/common/extensionsRegistry';
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';
import { ExtHostContext, ExtHostExtensionServiceShape } from './extHost.protocol';
......@@ -37,6 +37,8 @@ function messageWithSource(msg: IMessage): string {
return (msg.source ? '[' + msg.source + ']: ' : '') + msg.message;
}
const hasOwnProperty = Object.hasOwnProperty;
export class MainProcessExtensionService extends AbstractExtensionService<ActivatedExtension> {
private _threadService: IThreadService;
......@@ -60,8 +62,6 @@ export class MainProcessExtensionService extends AbstractExtensionService<Activa
this._threadService = threadService;
this._proxy = this._threadService.get(ExtHostContext.ExtHostExtensionService);
this._extensionsStatus = {};
ExtensionsRegistry.handleExtensionPoints((msg) => this._handleMessage(msg));
}
private _handleMessage(msg: IMessage) {
......@@ -124,13 +124,39 @@ export class MainProcessExtensionService extends AbstractExtensionService<Activa
// -- called by extension host
public $onExtensionHostReady(extensionDescriptions: IExtensionDescription[], messages: IMessage[]): TPromise<void> {
public $onExtensionHostReady(extensionDescriptions: IExtensionDescription[]): TPromise<void> {
ExtensionsRegistry.registerExtensions(extensionDescriptions);
messages.forEach((entry) => this._handleMessage(entry));
let availableExtensions = ExtensionsRegistry.getAllExtensionDescriptions();
let extensionPoints = ExtensionsRegistry.getExtensionPoints();
for (let i = 0, len = extensionPoints.length; i < len; i++) {
this._handleExtensionPoint(extensionPoints[i], availableExtensions);
}
this._triggerOnReady();
return;
}
private _handleExtensionPoint<T>(extensionPoint: ExtensionPoint<T>, availableExtensions: IExtensionDescription[]): void {
let messageHandler = (msg: IMessage) => this._handleMessage(msg);
let users: IExtensionPointUser<T>[] = [], usersLen = 0;
for (let i = 0, len = availableExtensions.length; i < len; i++) {
let desc = availableExtensions[i];
if (desc.contributes && hasOwnProperty.call(desc.contributes, extensionPoint.name)) {
users[usersLen++] = {
description: desc,
value: desc.contributes[extensionPoint.name],
collector: new ExtensionMessageCollector(messageHandler, desc.extensionFolderPath)
};
}
}
extensionPoint.acceptUsers(users);
}
public $onExtensionActivated(extensionId: string): void {
this._activatedExtensions[extensionId] = new MainProcessSuccessExtension();
}
......
......@@ -165,7 +165,7 @@ export class ExtensionHostMain {
private registerExtensions(): TPromise<void> {
ExtensionsRegistry.registerExtensions(this._extensions);
this._extensionService.registrationDone([]);
this._extensionService.registrationDone();
return this.handleEagerExtensions().then(() => this.handleExtensionTests());
}
......
......@@ -30,7 +30,7 @@ import { IQuickOpenService } from 'vs/workbench/services/quickopen/common/quickO
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
// debuggers extension point
export const debuggersExtPoint = extensionsRegistry.ExtensionsRegistry.registerExtensionPoint<debug.IRawAdapter[]>('debuggers', {
export const debuggersExtPoint = extensionsRegistry.ExtensionsRegistry.registerExtensionPoint<debug.IRawAdapter[]>('debuggers', [], {
description: nls.localize('vscode.extension.contributes.debuggers', 'Contributes debug adapters.'),
type: 'array',
defaultSnippets: [{ body: [{ type: '', extensions: [] }] }],
......@@ -122,7 +122,7 @@ export const debuggersExtPoint = extensionsRegistry.ExtensionsRegistry.registerE
});
// breakpoints extension point #9037
export const breakpointsExtPoint = extensionsRegistry.ExtensionsRegistry.registerExtensionPoint<debug.IRawBreakpointContribution[]>('breakpoints', {
export const breakpointsExtPoint = extensionsRegistry.ExtensionsRegistry.registerExtensionPoint<debug.IRawBreakpointContribution[]>('breakpoints', [], {
description: nls.localize('vscode.extension.contributes.breakpoints', 'Contributes breakpoints.'),
type: 'array',
defaultSnippets: [{ body: [{ language: '' }] }],
......
......@@ -102,7 +102,7 @@ let keybindingType: IJSONSchema = {
}
};
let keybindingsExtPoint = ExtensionsRegistry.registerExtensionPoint<ContributedKeyBinding | ContributedKeyBinding[]>('keybindings', {
let keybindingsExtPoint = ExtensionsRegistry.registerExtensionPoint<ContributedKeyBinding | ContributedKeyBinding[]>('keybindings', [], {
description: nls.localize('vscode.extension.contributes.keybindings', "Contributes keybindings."),
oneOf: [
keybindingType,
......
......@@ -53,7 +53,7 @@ function validateThemeId(theme: string): string {
return theme;
}
let themesExtPoint = ExtensionsRegistry.registerExtensionPoint<IThemeExtensionPoint[]>('themes', {
let themesExtPoint = ExtensionsRegistry.registerExtensionPoint<IThemeExtensionPoint[]>('themes', [], {
description: nls.localize('vscode.extension.contributes.themes', 'Contributes textmate color themes.'),
type: 'array',
items: {
......@@ -77,7 +77,7 @@ let themesExtPoint = ExtensionsRegistry.registerExtensionPoint<IThemeExtensionPo
}
});
let iconThemeExtPoint = ExtensionsRegistry.registerExtensionPoint<IThemeExtensionPoint[]>('iconThemes', {
let iconThemeExtPoint = ExtensionsRegistry.registerExtensionPoint<IThemeExtensionPoint[]>('iconThemes', [], {
description: nls.localize('vscode.extension.contributes.iconThemes', 'Contributes file icon themes.'),
type: 'array',
items: {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册